12 · 并发与网络模型:LangChain 的调度机制拆解
本片目标:回答生产级三个必问题——100 个用户同时问扛得住吗?同步/异步/批量怎么选?底层发了什么 HTTP 请求?本片把 LangChain 的线程模型和网络栈彻底拆开。 新增规定性:12(执行协议:invoke/batch/stream/ainvoke 四件套 + 并发配置) 数据字典:ContextThreadPoolExecutor / run_in_executor / RunnableConfig。 进程线程模型:本片主角——线程池、事件循环、async 并发。 网络模型:httpx Client / AsyncClient、SSE 流、超时重试。
1. 上集回顾
第 11 篇模型能调用工具了——但一次工具问答 = 多次模型调用(1 工具 = 2 次)。部署到生产前必须回答:- 100 个用户同时提问,串行
invoke一个占 1~3 秒,第 100 个用户要等 5 分钟——怎么扛? - 同步、异步、批量,什么时候用哪个?为什么有
ainvoke这种东西? - 底层到底走什么 HTTP 客户端?超时、重试、连接池在哪配?
2. 数据字典:Runnable 四件套(执行协议)
对照规则:
a 前缀 = 异步版;输入列表 = 批量版。
2.1 RunnableConfig(并发控制的关键参数)
3. 数据字典:LangChain 的线程池
3.1 ContextThreadPoolExecutor(langchain_core 的专用线程池)
submit 时用 copy_context() 复制当前线程的 contextvars 上下文到工作线程。为什么?——因为 LangChain 用 contextvars 传递 run_id、追踪信息,如果线程不复制上下文,异步调用里的追踪就断了。
3.2 run_in_executor(同步↔异步的桥)
4. 实测:并发行为的真相(2026-08 本机,智谱 glm-4.7)
我用 3 个问题做了一组对照实验:
并发 ≠ 一定更快。并发是”潜力”,实际收益取决于 API 的限流策略。 生产环境要对 API 的 QPS 上限做压测,再定 max_concurrency。
另一个实测确认:RunnableParallel(第 09 篇的 {"context":..., "question":...})的同步 invoke 内部确实用 ThreadPoolExecutor——两条分支是真并行:
5. 进程线程模型(全图)
run_in_executor。同步调用会被包装成 Future 丢进线程池,事件循环等 Future 完成——这是 FastAPI(第 14 篇)里混用同步/异步的关键。
6. 网络模型:httpx 全栈
LangChain 生态的 HTTP 客户端是 httpx(不是 requests、不是 urllib)。本机实测依赖:6.1 同步栈(invoke 路径)
6.2 异步栈(ainvoke 路径)
invoke 阻塞线程 1~3 秒,100 并发需要 100 个线程;ainvoke 在事件循环里等待时不占线程,100 并发只需要 1 个事件循环线程 + N 个协程。这是 FastAPI async 路由的核心优势(第 14 篇)。
6.3 流式网络模型(SSE)
7. 关键方法速查
8. 验证:跑起来
配套代码code/12_async_bench.py:
- 对照实验:串行 invoke vs batch vs abatch(3 问 × 3 种方式计时);
- 观察
RunnableParallel内部线程名(证明线程池存在); abatch限流演示:max_concurrency=1时退化为串行;- 打印 httpx 客户端信息。
9. 边界
- batch 不一定快——API 限流时并发触发 429 重试反而更慢。先压测再定并发数。
- 线程池每次 batch 新建——高频调用建议自己管理连接复用(或直接走异步)。
- 同步阻塞代码里别混 async——
asyncio.run()在同一线程二次调用会报错。 - 本地 Chroma 的 HNSW 索引是线程安全的吗——多线程并发检索建议用同一实例(第 08 篇已提)。写入要加锁。
推荐资料(延伸阅读)
- LangChain 官方文档 · 运行时 —— invoke/batch/stream 的执行模型
- httpx 官方文档 —— LangChain 底层的 HTTP 客户端
- Python 官方文档 · asyncio —— 事件循环 / 协程权威参考
10. 未完待续
并发模型懂了。但这一篇我们反复钻进langchain_core.runnables.config、httpx 这些底层——你已经在用 langchain_core,但只用了它 36 个模块里的几个。
它到底还有多少模块?哪些是你以后的武器、哪些是必须避开的坑?——这就是第 13 篇:langchain_core 全地图。
→ 13 · langchain_core 全地图