# 工作总结:微服务性能优化与架构 本文档汇总**三个微服务的性能优化**、**架构设计**及**性能测试基线**,便于汇报与后续复现。 --- ## 一、完成三个微服务的性能优化 ### 1. 文本向量(Embedding) **调研与选型**:对 TEI(Text Embeddings Inference)、vLLM 与原有 SentenceTransformers 方案进行对比评估。 | 方案 | 特点 | 结论 | |------|------|------| | **SentenceTransformers** | 本地 Python 进程,单机推理,无独立服务化 | 此前用于 Qwen3-Embedding-0.6B,扩展性与运维性一般 | | **vLLM** | 高吞吐推理框架,更适合生成式与重排混合场景 | 纯 embedding 场景通常不作为首选 | | **TEI** | HuggingFace 官方 embedding 专用推理服务,Docker 部署 | **当前最优选型** | **当前方案**:以 **TEI** 为文本向量后端,模型为 `Qwen/Qwen3-Embedding-0.6B`;文本 embedding 服务(端口 **6005**)将 `POST /embed/text` 请求转发至 TEI(默认端口 **8080**)。 **具体配置与脚本**: - **配置**:`config/config.yaml` → `services.embedding.backend: "tei"`,`services.embedding.backends.tei.base_url: "http://127.0.0.1:8080"`、`model_id: "Qwen/Qwen3-Embedding-0.6B"`、`timeout_sec: 20`。 - **启动**:`scripts/start_tei_service.sh`(Docker 容器);环境变量:`TEI_DEVICE=cuda`(默认)、`TEI_PORT=8080`、`TEI_MODEL_ID=Qwen/Qwen3-Embedding-0.6B`、`TEI_VERSION=1.9`、`TEI_MAX_BATCH_TOKENS=4096`、`TEI_MAX_CLIENT_BATCH_SIZE=24`、`TEI_DTYPE=float16`;T4 自动选镜像 `ghcr.io/huggingface/text-embeddings-inference:turing-1.9`。 - **编排**:`./scripts/service_ctl.sh start tei` 或 `start embedding`(text embedding 会校验 TEI `/health` 后再就绪)。 **工程化收益**: - **独立服务**:TEI 以 Docker 容器运行,与主程序 `.venv` 解耦;embedding 使用 `.venv-embedding`,便于独立扩缩容与升级。 - **协议统一**:与 `local_st`(SentenceTransformers)可插拔,通过 `config.yaml` 的 `services.embedding.backend` 或环境变量 `EMBEDDING_BACKEND` 切换。 - **T4 适配**:按 GPU 架构自动选择镜像(T4 用 `turing-*`,Ampere+ 用 `cuda-*`);`TEI_MAX_BATCH_TOKENS`、`TEI_MAX_CLIENT_BATCH_SIZE`、`TEI_DTYPE` 已按短文本场景调优。 - **健康检查与编排**:`service_ctl.sh` 对 tei 校验容器存在且 `GET http://127.0.0.1:8080/health` 成功;embedding 启动前会等待 TEI 健康。 详见:`docs/TEI_SERVICE说明文档.md`、`docs/QUICKSTART.md` §4。 --- ### 2. 重排(Reranker) **后端**:vLLM 部署 **Qwen3-Reranker-0.6B**,对外提供 `POST /rerank`(query + docs → scores),端口 **6007**。 **针对 T4 的优化(已落地配置)**: - **精度**:`dtype: "float16"`,降低显存与计算量。 - **Prefix Caching**:`enable_prefix_caching: true`,对重复前缀(如相同 query)做缓存,减少重复计算。 - **CUDA 图**:`enforce_eager: false`(默认),利用 vLLM 的 CUDA graph 降低 kernel 启动开销。 - **按文档长度分批**:`sort_by_doc_length: true`,请求内先按文档长度排序再按 `infer_batch_size` 分批推理,减少 padding 浪费。 - **参数搜索结论**:在 T4、1000-doc 口径下对 `infer_batch_size` 做了 24/32/48/64 对比;**单请求延迟(c=1)** 上 `infer_batch_size=64` 最优,故当前默认 `infer_batch_size: 64`;`max_model_len: 256` 满足 query+doc 短文本场景;`gpu_memory_utilization: 0.36` 与 T4 16GB 匹配。 **具体配置**(`config/config.yaml` → `services.rerank.backends.qwen3_vllm`): ```yaml model_name: "Qwen/Qwen3-Reranker-0.6B" engine: "vllm" max_model_len: 256 tensor_parallel_size: 1 gpu_memory_utilization: 0.36 dtype: "float16" enable_prefix_caching: true enforce_eager: false infer_batch_size: 64 sort_by_doc_length: true instruction: "Given a shopping query, rank product titles by relevance" ``` 环境变量覆盖:`RERANK_BACKEND`、`RERANKER_SERVICE_URL`、`RERANK_VLLM_INFER_BATCH_SIZE`、`RERANK_VLLM_SORT_BY_DOC_LENGTH` 等。启停:`./scripts/service_ctl.sh start reranker`,健康:`curl -sS http://127.0.0.1:6007/health`。 详见:`reranker/DEPLOYMENT_AND_TUNING.md`、`reranker/README.md`。 --- ### 3. 图片向量(Image Embedding) **方案**:**clip-as-service**(CN-CLIP,模型由配置控制),由独立服务提供图片向量化能力。 **具体内容**: - **端口**:clip-as-service 默认 **51000**(`CNCLIP_PORT`);文本走 TEI(8080),图片走 clip-as-service。 - **API**:文本 embedding 服务默认暴露 `POST /embed/text`(6005),图片 embedding 服务默认暴露 `POST /embed/image`(6008);图片请求由 `embeddings/server.py` 按配置调用实现 `ImageEncoderProtocol` 的后端(clip-as-service 或本地 CN-CLIP)。 - **环境与启停**:CN-CLIP 使用独立虚拟环境 `.venv-cnclip`;启动 `scripts/start_cnclip_service.sh`,或 `./scripts/service_ctl.sh start cnclip`;设备可通过 `CNCLIP_DEVICE=cuda`(默认)或 `cpu` 指定。 - **配置**:图片后端在 `config/config.yaml` 的 `services.embedding` 下配置(若存在 image 相关 backend);clip-as-service 默认模型由 `embeddings/config.py` 的 `CLIP_AS_SERVICE_MODEL_NAME` 控制,flow 配置在 `third-party/clip-as-service/server/torch-flow-temp.yml`。 详见:`docs/CNCLIP_SERVICE说明文档.md`、`embeddings/README.md`。 --- ### 4. 翻译(Translation) **背景**:原使用 DeepL,后迁移至 **qwen-mt**(如 `qwen-mt-flash`)。qwen-mt 云端限速约 **RPM=60(每分钟 60 请求)**,此前未做大商品量压测,未暴露问题;高并发索引或查询场景下易触限。 **当前方案**: - **统一 translator service**:业务侧统一走 6006,按 `model + scene` 选择能力,不再存在翻译 provider 分支。 - **配置入口**:`config/config.yaml` → `services.translation`,显式声明 `service_url`、`default_model`、`default_scene`、各 capability 的 `backend`、`base_url/api_url`、timeout 与本地模型运行参数。 - **内部规则收口**:scene 集合、语言码映射、LLM prompt 模板、本地模型方向约束统一放在 `translation/` 内部,不再散落在 `config/`、`query/` 等位置。 - **调用位置**:QueryParser 与 Indexer 均通过 `translation.create_translation_client()` 获取客户端,不写死 URL 或模型名。 - **缓存**:translator service 对所有 translation capability 统一接入 Redis 缓存;每个 capability 通过 `use_cache` 控制开关,key 格式固定为 `trans:{model}:{target_lang}:{source_text[:4]}{sha256}`。 - **场景支撑**:在线索引(indexer)与 query 请求(QueryParser)共用同一套 provider 配置;可按环境或租户通过修改 `config.yaml` 或环境变量切换 provider/model。 - **待配合**:**金伟侧对索引侧翻译调用做流量控制**(限流/排队/批量聚合),避免索引高峰打满 qwen 限速,影响在线 query 翻译。 --- ### 5. 内容理解字段(支撑 Suggest) **能力**:支持根据商品标题批量生成 **qanchors**(锚文本)、**enriched_attributes**、**tags**,供索引与 suggest 使用。 **具体内容**: - **接口**:`POST /indexer/enrich-content`(Indexer 服务端口 **6004**)。请求体为 `items` 数组,每项含 `spu_id`、`title`(必填)及可选多语言标题等;单次请求最多 **50 条**,建议批量调用。响应 `results` 与 `items` 一一对应,每项含 `spu_id`、`qanchors`(按语言键,如 `qanchors.zh`、`qanchors.en`,逗号分隔短语)、`enriched_attributes`、`tags`。 - **索引侧**:微服务组合方式下,调用方先拿不含 qanchors/tags 的 doc,再调用本接口补齐后写入 ES 的 `qanchors.{lang}` 等字段;索引 transformer(`indexer/document_transformer.py`、`indexer/product_enrich.py`)内也可在构建 doc 时调用内容理解逻辑,写入 `qanchors.{lang}`。 - **Suggest 侧**:`suggestion/builder.py` 从 ES 商品索引读取 `_source: ["id", "spu_id", "title", "qanchors"]`,对 `qanchors.{lang}` 用 `_split_qanchors` 拆成词条,以 `source="qanchor"` 加入候选,排序时 `qanchor` 权重大于纯 title(`add_product("qanchor", ...)`);suggest 配置中 `sources: ["query_log", "qanchor"]` 表示候选来源包含 qanchor。 - **实现与依赖**:内容理解内部使用大模型(需 `DASHSCOPE_API_KEY`),支持多语言与 Redis 缓存(如 `product_anchors`);逻辑与 `indexer/product_enrich` 一致。 **状态**:内容理解字段已接入索引与 suggest 链路;依赖内容理解(qanchors/tags)的**全量数据尚未全部完成一轮**,后续需持续跑满并校验效果。 详见:`indexer/ANCHORS_AND_SEMANTIC_ATTRIBUTES.md`、`docs/搜索API对接指南-05-索引接口(Indexer).md`(`enrich-content` 等)、`api/routes/indexer.py`(enrich-content 路由)。 --- ## 二、架构 ### 1. Translator Service 与动态选择翻译 - **设计**:翻译已从 provider 架构中独立出来,采用 **一个 translator service + 多个 capability backend**;配置单一来源为 `config/config.yaml` 的 `services.translation` 块,`service_url` / `default_model` / `default_scene` 不再接受环境变量静默覆盖。 - **翻译(具体实现)**: - **业务入口**:`translation.create_translation_client()` - **服务编排**:`translation/service.py` - **后端实现**:`translation/backends/qwen_mt.py`、`translation/backends/llm.py`、`translation/backends/deepl.py`、`translation/backends/local_seq2seq.py` - **调用方**:`query/query_parser.py`、`indexer/incremental_service.py`、`indexer/indexing_utils.py` - **效果**:仅改 `services.translation.default_model` 或启用的 capability,即可切换云端/本地翻译能力;调用方始终只连 6006。 ### 2. 服务的监控与拉起机制 - **脚本**:`scripts/service_ctl.sh` 统一负责各服务的生命周期与监控;依赖 `scripts/lib/load_env.sh` 与项目根目录 `.env`。 - **服务与端口**: - 核心:backend **6002**、indexer **6004**、frontend **6003**、eval-web **6010**(搜索评估 UI)。 - 可选:embedding(text) **6005**、embedding-image **6008**、translator **6006**、reranker **6007**、tei **8080**、cnclip **51000**。 - 端口可由环境变量覆盖:`API_PORT`、`INDEXER_PORT`、`FRONTEND_PORT`、`EVAL_WEB_PORT`、`EMBEDDING_TEXT_PORT`、`EMBEDDING_IMAGE_PORT`、`TRANSLATION_PORT`、`RERANKER_PORT`、`TEI_PORT`、`CNCLIP_PORT`。 - **命令**: - `./scripts/service_ctl.sh start [service...]` 或 `up all` / `start all`(all 含 tei、cnclip、embedding、embedding-image、translator、reranker、backend、indexer、frontend、eval-web,按依赖顺序);`stop`、`restart`、`down` 同参数;`status` 默认列出所有服务。 - 启动时:backend/indexer/frontend/embedding/translator/reranker 会写 pid 到 `logs/.pid`,并执行 `wait_for_health`(GET `http://127.0.0.1:/health`);reranker 健康重试 90 次,其余 30 次;TEI 校验 Docker 容器存在且 `/health` 成功;cnclip 无 HTTP 健康则仅校验进程/端口。 - **监控常驻**: - `./scripts/service_ctl.sh monitor-start ` 启动后台监控进程,将 targets 写入 `logs/service-monitor.targets`,pid 写入 `logs/service-monitor.pid`,日志追加到 `logs/service-monitor.log`。 - 轮询间隔 `MONITOR_INTERVAL_SEC` 默认 **10** 秒;连续 **3** 次(`MONITOR_FAIL_THRESHOLD`)健康失败则触发重启;重启冷却 `MONITOR_RESTART_COOLDOWN_SEC` 默认 **30** 秒;每小时最多重启 `MONITOR_MAX_RESTARTS_PER_HOUR` 默认 **6** 次;超限时调用 `scripts/ops/wechat_alert.py` 告警(若存在)。 - **日志**:各服务按日滚动到 `logs/-.log`,通过 `scripts/ops/daily_log_router.sh` 与 `LOG_RETENTION_DAYS`(默认 30)控制保留。 详见:`scripts/service_ctl.sh` 内注释及 `docs/Usage-Guide.md`。 ### 3. Suggest 索引与增量 - **当前**:suggest 索引由**全量脚本触发**,入口为 `main.py build-suggestions`,或封装脚本 `scripts/build_suggestions.sh [args...]`。 - **全量示例**:`./scripts/build_suggestions.sh 162 --mode full --days 30 --publish-alias`(重建建议索引,近 30 天数据,并发布别名)。 - **增量示例**:`./scripts/build_suggestions.sh 162 --mode incremental --overlap-minutes 30`(按 watermark 增量更新);脚本内部调用 `main.py build-suggestions --tenant-id ...`。 - 构建逻辑在 `suggestion/builder.py` 的 `SuggestionIndexBuilder`:从 ES 商品索引(含 `title`、`qanchors`)与查询日志等拉取数据,写入 versioned 建议索引并切换 alias。 - **尚未完成的“增量机制”**:指**自动/事件驱动的增量**(如商品变更或日志写入时自动刷新建议索引);当前 incremental 模式为“按 watermark 再跑一次构建”,仍为脚本主动触发,非持续增量流水线。 - **依赖**:suggest 候选依赖商品侧 **内容理解字段**(qanchors/tags);`sources: ["query_log", "qanchor"]` 表示候选来自查询日志与 qanchor;当前内容理解未全量跑完一轮,suggest 数据会随全量重建逐步完善。 详见:`suggestion/builder.py`、`suggestion/ARCHITECTURE_V2.md`、`main.py`(build-suggestions 子命令)。 --- ## 三、性能测试报告摘要 以下数据来自 `docs/性能测试报告.md`,测试时间 **2026-03-12**,环境:**8 vCPU**(Intel Xeon Platinum 8255C @ 2.50GHz)、**约 15Gi 可用内存**;租户 **162** 文档数约 **53**(search/search/suggestions/rerank 与文档规模相关)。压测工具:`benchmarks/perf_api_benchmark.py`,场景×并发矩阵,每档 **20s**。 **复现命令(四场景×四并发)**: ```bash cd /data/saas-search .venv/bin/python benchmarks/perf_api_benchmark.py \ --scenario backend_search,backend_suggest,embed_text,rerank \ --concurrency-list 1,5,10,20 \ --duration 20 \ --tenant-id 162 \ --backend-base http://127.0.0.1:6002 \ --embedding-base http://127.0.0.1:6005 \ --translator-base http://127.0.0.1:6006 \ --reranker-base http://127.0.0.1:6007 \ --output perf_reports/2026-03-12/perf_matrix_report.json ``` 执行前需启动服务:`./scripts/service_ctl.sh start embedding translator reranker backend`,并通过各端口 `/health` 检查。Reranker 单独 386-docs 口径复现见性能测试报告 §7.4 与 §13。 ### 3.1 向量化服务(embed_text) | 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | |-----:|-------:|-------:|----------:|--------:|--------:|--------:| | 1 | 966 | 100% | 48.27 | 20.63 | 23.41 | 49.80 | | 5 | 1796 | 100% | 89.57 | 55.55 | 69.62 | 109.84 | | 10 | 2095 | 100% | 104.42 | 95.22 | 117.66 | 152.48 | | 20 | 2393 | 100% | 118.70 | 167.37 | 212.21 | 318.70 | **结论**:随并发提升吞吐持续增长,延迟平滑上升,扩展性较好。 --- ### 3.2 Reranker(rerank) 口径:query 固定 `wireless mouse`,每次请求 **386 docs**,句长 15–40 词随机(从 1000 词池采样);配置 `rerank_window=384`。复现命令: ```bash .venv/bin/python benchmarks/perf_api_benchmark.py \ --scenario rerank --duration 20 --concurrency-list 1,5,10,20 --timeout 60 \ --rerank-dynamic-docs --rerank-doc-count 386 --rerank-vocab-size 1000 \ --rerank-sentence-min-words 15 --rerank-sentence-max-words 40 \ --rerank-query "wireless mouse" --rerank-seed 20260312 \ --reranker-base http://127.0.0.1:6007 \ --output perf_reports/2026-03-12/rerank_realistic/rerank_386docs.json ``` | 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | |-----:|-------:|-------:|----------:|--------:|--------:|--------:| | 1 | 14 | 100% | 0.67 | 1498.64 | 1799.25 | 2160.96 | | 5 | 15 | 100% | 0.62 | 8011.99 | 9725.61 | 9726.02 | | 10 | 20 | 100% | 0.61 | 16217.12| 18043.05| 18050.04| | 20 | 20 | 100% | 0.60 | 33252.35| 33456.74| 33480.14| **结论**:在 386 docs/请求 的真实口径下,吞吐约 **0.6 rps**,延迟随并发明显上升(并发 20 时 P95 约 33.5s),是当前整条链路的**最重瓶颈**。与 DashScope 云后端对比:单并发 vLLM 延迟更优;并发 5+ 时云后端吞吐更高(约 1.3x–6x),在线高并发场景可优先考虑云后端。 --- ### 3.3 搜索(backend_search) | 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | |-----:|-------:|-------:|----------:|--------:|--------:|--------:| | 1 | 160 | 100% | 7.98 | 124.89 | 228.06 | 345.49 | | 5 | 161 | 100% | 7.89 | 628.91 | 1271.49| 1441.02| | 10 | 181 | 100% | 8.78 | 1129.23| 1295.88| 1330.96| | 20 | 161 | 100% | 7.63 | 2594.00| 4706.44| 4783.05| **结论**:吞吐约 **8 rps** 平台化,延迟随并发上升明显,符合“检索 + 向量 + 重排”重链路特征。多租户补测(文档数 500–10000,见报告 §12)表明:文档数越大,RPS 下降、延迟升高;tenant 0(10000 doc)在并发 20 出现部分 ReadTimeout(成功率 59.02%),需注意 timeout 与容量规划;补测命令示例:`for t in 0 1 2 3 4; do .venv/bin/python benchmarks/perf_api_benchmark.py --scenario backend_search --concurrency-list 1,5,10,20 --duration 20 --tenant-id $t --output perf_reports/2026-03-12/search_tenant_matrix/tenant_${t}.json; done`。 --- ### 3.4 Suggest(backend_suggest) | 并发 | 请求数 | 成功率 | 吞吐(RPS) | Avg(ms) | P95(ms) | Max(ms) | |-----:|-------:|-------:|----------:|--------:|--------:|--------:| | 1 | 3502 | 100% | 175.09 | 5.68 | 8.70 | 15.98 | | 5 | 4168 | 100% | 208.10 | 23.93 | 36.93 | 59.53 | | 10 | 4152 | 100% | 207.25 | 48.05 | 59.45 | 127.20 | | 20 | 4190 | 100% | 208.99 | 95.20 | 110.74 | 181.37 | **结论**:吞吐高且稳定(**约 200+ rps**),对并发友好,与 search/rerank 相比压力较小。 --- ## 四、总结 | 维度 | 要点 | |------|------| | **Embedding** | TEI 替代 SentenceTransformers/vLLM 作为文本向量后端,兼顾性能与工程化(Docker、配置化、T4 调优);图片向量由 clip-as-service 承担。 | | **Reranker** | vLLM + Qwen3-Reranker-0.6B,针对 T4 做 float16、prefix caching、CUDA 图、按长度分批及 batch/长度参数搜索;高并发场景可选用 DashScope 云后端。 | | **翻译** | 因 qwen-mt 限速(RPM≈60),迁移至可配置的 qwen-flash 等方案,支撑在线索引与 query;需金伟侧对索引做流量控制。 | | **内容理解** | 提供 qanchors/tags 等字段生成接口,支撑 suggest 与检索增强;全量一轮尚未完全跑满。 | | **架构** | Provider 动态选择翻译;service_ctl 统一监控与拉起;suggest 目前全量脚本触发,增量待做。 | | **性能基线** | 向量化扩展性良好;reranker 为整链瓶颈(386 docs 约 0.6 rps);search 约 8 rps;suggest 约 200+ rps。 | **关键文件与复现**: - 配置:`config/config.yaml`(services、rerank、query_config)、`.env`(端口与 API Key)。 - 脚本:`scripts/service_ctl.sh`(启停与监控)、`benchmarks/perf_api_benchmark.py`(压测)、`scripts/build_suggestions.sh`(suggest 构建)。 - 完整步骤与多租户/rerank 对比见:`docs/性能测试报告.md`。