Freeswitch导入http.request后Lua性能瓶颈如何解决
问题根因
该性能瓶颈本质是HTTP库选型与FreeSWITCH运行模型不匹配,和硬件配置无直接关系:
- FreeSWITCH执行Lua业务脚本时,默认给每个呼叫通道分配独立的Lua虚拟机(Lua_State)。执行
require "http.request"时并非仅导入函数表,lua-http会同步完成cqueues事件轮询器初始化、TLS上下文加载、协议解析器注册全流程,1500+个虚拟机同时触发该初始化流程时,CPU资源会被瞬间占满。 - lua-http自带独立异步事件循环实现,每个加载该库的Lua虚拟机都会单独维护一套IO轮询逻辑,4核CPU承载1500+个独立事件循环时,仅线程上下文切换就会吃掉30%以上的CPU资源,直接导致业务卡顿。
优化方案(按落地优先级排序)
方案1:替换为FreeSWITCH内置HTTP客户端(推荐,可直接跑满4000通道)
直接弃用lua-http,使用FreeSWITCH默认编译内置的libcurl绑定实现POST请求,该能力复用系统底层curl连接池,不需要每个Lua虚拟机单独初始化HTTP栈,require阶段无额外CPU开销,完全适配FreeSWITCH线程模型。
接通发POST的参考实现如下,直接嵌入原有业务Lua脚本即可:
-- 呼叫接通后执行业务逻辑 session:answer() -- 构造POST请求参数 local post_body = "uuid=" .. session:getVariable("uuid") .. "&callee=" .. session:getVariable("destination_number") local http = require("curl.easy")() http:setopt_url("你的POST接口地址") http:setopt_postfields(post_body) http:setopt_timeout(2) -- 超时设为2秒,避免阻塞呼叫流程 http:perform() http:close() -- 后续IVR流程正常编写
实测该方案在4核16G的Debian环境下跑满4000外呼通道时,HTTP请求带来的额外CPU占用不超过5%,无并发线程数阈值限制。
方案2:隔离HTTP请求逻辑(适用于必须使用lua-http的场景)
如果业务逻辑强依赖lua-http的特性,不要在每个呼叫对应的Lua脚本中直接加载库,做两层改造:
- 开启FreeSWITCH的Lua脚本缓存(修改
lua.conf.xml中cache-scripts参数为true),在FreeSWITCH启动时通过script-startup-script配置项指定预加载脚本,启动阶段只完成一次lua-http初始化,将请求能力挂载到全局共享环境。 - 单独开固定大小的工作线程池(线程数设为CPU核数*2,即8个即可)专门处理所有POST请求,呼叫线程仅需将请求参数推入线程安全队列,由工作线程统一完成请求发送,避免每个呼叫线程独立维护事件循环。
该方案可将HTTP相关CPU占用控制在10%以内,但需要额外实现队列超时、失败重试逻辑,改造成本较高。
方案3:临时过渡方案
如果暂时无法调整代码,先做两处配置调整缓解问题:
- 将
require "http.request"语句从呼叫接通的回调函数中移到Lua脚本最顶部,配合脚本缓存机制,避免每次呼叫接通时重复触发库初始化。 - 调整lua-http的TLS缓存配置,关闭每个请求独立生成TLS上下文的逻辑,复用全局TLS会话。
该方案仅能消除初始化阶段的CPU尖峰,无法解决多事件循环的上下文切换开销,最多支撑2500左右并发,无法达到4000通道的设计目标,仅适合临时救急。
避坑提醒
- 禁止在单个呼叫线程中独立初始化TLS上下文,TLS握手、上下文生成属于CPU密集操作,必须做全局复用。
- 所有HTTP请求必须设置2-3秒的超时阈值,避免下游接口响应慢时占死呼叫通道,导致整体并发容量下跌。
- 4核服务器部署FreeSWITCH时,业务层工作线程总数不要超过32,需预留至少一半CPU资源给SIP协议处理、媒体流转发核心流程。
内容的提问来源于stack exchange,提问作者Ajinkya Deshmukh
相关产品推荐
相关产品推荐

