FreeSwitch之mod_cidlookup 和 mod_blacklist和mod_curl的抉择

FreeSWITCH 的 mod_curl 模块是一个用于通过 HTTP/HTTPS 协议与外部服务进行交互的核心模块。它允许 FreeSWITCH 在呼叫处理过程中发起 HTTP 请求(如 GET、POST 等),并将响应结果集成到呼叫流程中。以下是关于 mod_curl 的详细介绍:


1. 主要功能

  • 发起 HTTP 请求:支持 GET、POST、PUT、DELETE 等 HTTP 方法。
  • 动态数据交互:从外部服务获取动态配置(如拨号计划路由、用户鉴权信息),或推送事件数据(如 CDR 话单、通话状态)。
  • 事件绑定 :将 HTTP 请求绑定到 FreeSWITCH 的特定事件(如 CHANNEL_CREATECHANNEL_HANGUP),实现实时回调。
  • 变量注入:在请求中插入 FreeSWITCH 变量(如主叫号码、被叫号码),实现动态参数传递。
  • 响应处理:解析 HTTP 响应内容(如 JSON/XML),并根据结果控制呼叫流程(如放音、转接)。

2. 配置与使用

2.1 加载模块

在 FreeSWITCH 配置文件 conf/autoload_configs/modules.conf.xml 中启用模块:

xml 复制代码
<load module="mod_curl"/>
2.2 配置文件

模块的配置文件位于 conf/autoload_configs/curl.conf.xml,常用配置项如下:

xml 复制代码
<configuration name="curl.conf">
  <settings>
    <!-- 超时时间(毫秒) -->
    <param name="timeout" value="5000"/>
    <!-- 是否启用 SSL 验证 -->
    <param name="ssl-verify-peer" value="false"/>
    <param name="ssl-verify-host" value="false"/>
    <!-- 代理服务器 -->
    <param name="proxy" value="http://proxy.example.com:8080"/>
  </settings>
</configuration>
2.3 在拨号计划中使用

通过 curl 应用在拨号计划中发起 HTTP 请求,并根据响应控制流程:

xml 复制代码
<extension name="dynamic_route">
  <condition field="destination_number" expression="^(\d+)$">
    <action application="curl" 
            data="http://api.example.com/route?called=${destination_number}"/>
    <action application="bridge" data="${curl_response_data}"/>
  </condition>
</extension>
  • curl_response_data 变量保存 HTTP 响应内容。
  • 若响应为 JSON/XML,可配合 mod_jsonmod_xml_curl 解析。
2.4 事件订阅与回调

通过 event 命令绑定事件到 HTTP 回调:

xml 复制代码
<params>
  <param name="url" value="http://api.example.com/cdr"/>
  <param name="event-type" value="CHANNEL_HANGUP"/>
  <param name="event-data" value="Unique-ID: ${uuid}"/>
</params>

3. 核心 API 方法

  • curl :发起同步 HTTP 请求,直接返回响应。

    lua 复制代码
    -- Lua 示例
    local response = curl("http://api.example.com/auth?user=${caller_id_number}")
  • curl_async:发起异步 HTTP 请求,不阻塞当前会话。

  • curl_perform:结合会话变量动态构建请求。


4. 典型应用场景

4.1 动态路由

通过 HTTP 请求从外部服务获取路由目标(如根据主叫号码选择网关):

xml 复制代码
<action application="curl" data="http://api.example.com/routing?called=${destination_number}"/>
<action application="bridge" data="${curl_response_data}"/>
4.2 用户鉴权

验证主叫号码是否有权限发起呼叫:

xml 复制代码
<action application="curl" data="http://api.example.com/auth?user=${caller_id_number}"/>
<action application="hangup" data="NORMAL_CLEARING" if="${curl_response_code} != 200"/>
4.3 CDR 话单推送

通话结束后推送话单到外部系统:

xml 复制代码
<event type="CHANNEL_HANGUP">
  <params>
    <param name="url" value="http://api.example.com/cdr"/>
    <param name="method" value="POST"/>
    <param name="data" value="caller=${caller_id_number}&duration=${billsec}"/>
  </params>
</event>
4.4 动态 IVR 菜单

从外部服务获取 IVR 菜单配置:

lua 复制代码
local menu = curl("http://api.example.com/ivr/${destination_number}")
session:execute("playback", menu.audio_file)

5. 高级特性

  • 多请求并行 :通过 curl_async 发起多个异步请求提升性能。

  • 自定义 Headers :添加 HTTP 头:

    xml 复制代码
    <param name="http-headers" value="X-API-Key: my-secret-key"/>
  • Basic Auth :支持身份验证:

    xml 复制代码
    <param name="auth-username" value="user"/>
    <param name="auth-password" value="pass"/>
  • SSL/TLS 支持 :通过 https:// URL 启用加密通信。


6. 注意事项

  • 性能影响:同步请求会阻塞会话,高并发时建议使用异步模式。
  • 错误处理 :检查 curl_response_codecurl_response_phrase 处理异常。
  • 超时设置 :合理配置 timeout 避免长时间阻塞。
  • 安全性 :启用 SSL 验证(ssl-verify-peer)避免中间人攻击。

7. 调试与日志

  • 日志查看 :在 FreeSWITCH 日志文件(log/freeswitch.log)中搜索 mod_curl 相关条目。

  • 调试命令 :通过 CLI 命令查看请求详情:

    curl debug on
    curl http://api.example.com/test
    

8. 与其他模块对比

  • mod_xml_curl:专用于 XML 配置的动态获取(如拨号计划、用户目录)。
  • mod_http :提供 HTTP 服务器功能(如 REST API 接口),而 mod_curl 是客户端。

通过 mod_curl,FreeSWITCH 可以灵活地与 Web 服务集成,实现高度动态的通信解决方案。如需进一步优化,建议结合脚本语言(如 Lua)处理复杂的逻辑和响应解析。


在 FreeSWITCH 中,通过 mod_curl 直接发起 HTTP 请求与通过 LuaJIT(使用 Lua 脚本)发起请求,存在显著差异。以下是两者的对比分析:


1. 实现方式

mod_curl
  • 原生模块:是 FreeSWITCH 的内置模块(C 语言实现),直接集成到 FreeSWITCH 核心。
  • 同步/异步模式
    • 同步请求:通过 curl 应用发起请求,阻塞当前会话,直到获取响应。
    • 异步请求:通过 curl_async 发起请求,非阻塞,但需通过事件或回调处理响应。
  • 配置驱动:通常通过 XML 拨号计划或配置文件定义请求逻辑。
LuaJIT
  • 脚本驱动 :通过 Lua 脚本(LuaJIT 加速)发起 HTTP 请求,依赖第三方库(如 LuaSocketLuaSecLua-cURL)。
  • 灵活性:可在脚本中自由编写复杂逻辑(如条件判断、循环、数据处理)。
  • 异步支持 :需自行实现异步逻辑(例如协程或结合 event 模块),默认多为同步请求。

2. 性能对比

场景 mod_curl LuaJIT
同步请求 高性能(C 原生实现,低延迟) 较低(Lua 脚本解析 + 库开销)
高并发 更适合(异步模式可减少阻塞) 需谨慎处理(脚本阻塞易导致性能瓶颈)
资源占用 较低(直接集成,无额外依赖) 较高(Lua 运行时 + 第三方库)

3. 功能与灵活性

mod_curl
  • 核心优势
    • 简单直接:适合简单的 HTTP 请求(如鉴权、路由查询)。
    • 事件集成:可与 FreeSWITCH 事件系统绑定(如通话结束后推送 CDR)。
    • 变量注入 :直接在 URL 或请求体中插入 FreeSWITCH 变量(如 ${caller_id})。
  • 局限性
    • 逻辑简单:难以处理复杂响应(如 JSON/XML 解析需依赖其他模块)。
    • 依赖配置:动态逻辑需通过拨号计划或事件钩子实现,灵活性受限。
LuaJIT
  • 核心优势
    • 复杂逻辑处理 :可直接解析 JSON/XML(如使用 cjson 库)、动态生成请求参数。
    • 流程控制:支持条件分支、循环、错误重试等高级逻辑。
    • 与其他模块交互 :可结合 dbredis 等模块实现混合操作。
  • 局限性
    • 开发复杂度高:需编写和维护 Lua 脚本。
    • 依赖管理:需确保第三方库(如 HTTP 客户端)正确安装。

4. 代码示例对比

mod_curl(拨号计划中同步请求)
xml 复制代码
<extension name="auth">
  <condition field="destination_number" expression="^(\d+)$">
    <action application="curl" data="http://api.example.com/auth?user=${caller_id_number}"/>
    <action application="log" data="INFO Response: ${curl_response_data}"/>
    <action application="bridge" data="${curl_response_data}" if="curl_response_code == 200"/>
    <action application="hangup" data="NORMAL_CLEARING" if="curl_response_code != 200"/>
  </condition>
</extension>
LuaJIT(脚本中发起请求)
lua 复制代码
local http = require("socket.http")
local ltn12 = require("ltn12")

function auth(session)
    local url = "http://api.example.com/auth?user=" .. session:getVariable("caller_id_number")
    local response = {}
    local res, code = http.request{
        url = url,
        sink = ltn12.sink.table(response)
    }
    response = table.concat(response)
    
    if code == 200 then
        session:execute("bridge", response)
    else
        session:hangup("NORMAL_CLEARING")
    end
end

5. 适用场景

场景 推荐方案 理由
简单请求 mod_curl 快速集成,无需编码,性能高
复杂逻辑/数据处理 LuaJIT 灵活处理 JSON/XML、重试逻辑、多步骤交互
高并发异步任务 mod_curl(异步模式) 减少阻塞,提升系统吞吐量
动态路由/鉴权 mod_curl 直接通过变量注入实现,配置简单
外部系统深度集成 LuaJIT 可结合数据库、缓存、其他 API 混合操作

6. 关键差异总结

维度 mod_curl LuaJIT
开发效率 高(配置驱动) 中(需编写脚本)
执行性能 高(C 原生) 中(LuaJIT 优化,但仍依赖脚本解释)
灵活性 低(仅限 HTTP 请求) 高(支持任意 Lua 代码)
维护成本 低(集中配置) 中(需维护脚本和依赖库)
调试难度 低(日志直接查看响应) 中(需调试脚本逻辑)

7. 如何选择?

  • mod_curl 如果
    • 需求简单(如发起 HTTP 请求并获取结果)。
    • 需要高性能、低延迟。
    • 避免引入额外依赖或脚本维护。
  • LuaJIT 如果
    • 需要复杂逻辑(如解析响应、重试机制、多服务交互)。
    • 已熟悉 Lua 或需与其他 Lua 模块(如数据库)配合。
    • 接受一定的性能损耗以换取灵活性。

8. 混合使用建议

  • mod_curl 用于核心的 HTTP 交互(如鉴权、路由),确保性能。
  • LuaJIT 处理复杂业务逻辑(如动态 IVR、话单分析)。
  • 通过 event 机制将两者结合,例如用 mod_curl 推送事件,再用 Lua 脚本处理后续流程。

通过合理选择,可以充分发挥两者的优势,构建高效灵活的 FreeSWITCH 应用。


在 FreeSWITCH 中,mod_cidlookupmod_blacklist 是专门用于主叫号码归属地查询和黑白名单管理的模块,而 mod_curl 是一个通用的 HTTP 客户端模块。是否需要在已启用这两个模块的情况下额外启用 mod_curl,取决于具体业务场景的复杂性和灵活性需求。以下是详细分析:


1. 模块功能对比

模块 核心功能 适用场景
mod_cidlookup 通过本地数据库或简单 HTTP 请求查询主叫号码归属地(如区号、运营商)。 基础归属地查询、来电显示格式化。
mod_blacklist 基于本地配置文件或数据库检查主叫/被叫号码是否在黑名单中,拦截呼叫。 静态黑白名单管理、简单拦截逻辑。
mod_curl 通过 HTTP/HTTPS 与外部服务动态交互(如获取动态配置、调用复杂业务逻辑 API)。 需要外部实时数据或复杂逻辑的场景。

2. 是否需要启用 mod_curl?

场景 1:仅需基础功能
  • 不需要启用 mod_curl
    • 如果业务需求仅限于:
      • 使用 mod_cidlookup 的本地数据库或简单 HTTP 接口查询归属地。
      • 使用 mod_blacklist 的静态配置文件管理黑白名单。
    • 此时 mod_cidlookupmod_blacklist 已足够。
场景 2:需要动态扩展或复杂逻辑
  • 需要启用 mod_curl
    • 如果业务需求包含以下任意一项:
      1. 动态数据源:黑白名单或归属地数据需要从外部 API 实时获取(如对接云服务、风控系统)。
      2. 复杂逻辑处理:查询结果需结合其他条件(如时间、地理位置、用户状态)动态决策。
      3. 数据格式化:主叫/被叫号码需要根据外部规则动态格式化(如国际号码转换、掩码处理)。
      4. 统一管理:希望将归属地、黑白名单等逻辑集中到外部服务,而非分散在 FreeSWITCH 配置中。

3. 典型场景示例

案例 1:动态黑白名单
  • 需求
    黑名单需要从外部风控系统实时拉取,且拦截逻辑需结合主叫号码的归属地(如只拦截境外号码)。
  • 实现
    • 使用 mod_curl 发起 HTTP 请求到风控 API,获取动态黑名单和拦截规则。
    • 结合 mod_cidlookup 的归属地信息,执行复杂拦截逻辑(如 Lua 脚本判断)。
案例 2:主叫号码深度格式化
  • 需求
    根据外部服务的规则,将主叫号码转换为特定格式(如 +86 13800138000008613800138000)。
  • 实现
    • 通过 mod_curl 调用外部 API 获取格式化规则,动态修改主叫号码(mod_cidlookup 仅支持简单格式转换)。
案例 3:归属地扩展查询
  • 需求
    不仅需要归属地,还需查询号码的风险评分(如高频呼叫标记)。
  • 实现
    • mod_cidlookup 提供基础归属地,mod_curl 调用风控 API 补充风险数据,综合决策是否接续呼叫。

4. 性能与维护权衡

方案 优点 缺点
仅用 mod_cidlookup/mod_blacklist 高性能、低延迟(本地操作)。 灵活性低,无法适应动态需求。
结合 mod_curl 灵活对接外部系统,支持复杂逻辑。 依赖网络请求,可能增加延迟和故障点。

5. 配置建议

如果启用 mod_curl
  1. 优化 HTTP 请求
    • 使用 异步请求curl_async)避免阻塞呼叫流程。
    • 设置合理超时(如 3 秒)和重试机制。
  2. 缓存策略
    • 对频繁查询的数据(如黑白名单)在 FreeSWITCH 内存或 Redis 中缓存,减少外部调用。
  3. 安全加固
    • 启用 HTTPS 并配置 SSL 验证(ssl-verify-peer=true)。
    • 通过 HTTP Headers 传递认证信息(如 API Key)。
示例:动态黑名单检查
xml 复制代码
<!-- 拨号计划中结合 mod_curl 和 Lua 脚本 -->
<extension name="dynamic_blacklist">
  <condition field="destination_number" expression="^(\d+)$">
    <action application="lua" data="check_blacklist.lua"/>
  </condition>
</extension>

check_blacklist.lua

lua 复制代码
local curl = require "freeswitch.curl"

function check_blacklist(session)
    local caller = session:getVariable("caller_id_number")
    local url = "https://api.security.com/blacklist?number=" .. caller
    
    -- 同步请求(高并发时建议异步)
    local response = curl.get(url, {timeout=3000})
    
    if response.code == 200 and response.body == "blocked" then
        session:execute("hangup", "USER_BUSY")
    end
end

6. 结论

  • 无需启用 mod_curl
    如果业务仅依赖静态数据(如本地归属地数据库、固定黑白名单),且无需复杂逻辑扩展。
  • 建议启用 mod_curl
    如果需实现以下功能:
    • 动态数据源(外部 API、数据库)。
    • 复杂决策逻辑(组合归属地、风控、用户状态)。
    • 统一管理多系统的交互(如同时对接归属地服务、黑名单服务、CRM 系统)。

7. 替代方案

如果希望减少模块依赖,也可通过以下方式替代 mod_curl

  • mod_xml_curl:专用于动态获取 XML 配置(如拨号计划、用户目录),适合简单查询。
  • Lua 脚本 + 第三方库 :使用 Lua 的 LuaSecLua-cURL 库直接发起 HTTP 请求(灵活性高,但需编码和维护)。

最终决策需结合业务场景的实时性要求、系统架构复杂度和运维成本综合权衡。


在需要 多级回退查询 (Redis → MySQL → HTTP)的复杂场景下,FreeSWITCH 的模块选择和架构设计需兼顾 灵活性性能可维护性。以下是针对该场景的完整解决方案:


1. 核心需求分析

  • 分层查询:优先查询 Redis 缓存,未命中则查 MySQL,最后回退到 HTTP 外部 API。
  • 数据一致性:若数据最终从 MySQL 或 HTTP 获取,需回写 Redis 缓存。
  • 性能优化:减少外部 HTTP 调用频率,降低延迟。
  • 容错机制:单层查询失败时自动降级到下一层,避免服务中断。

2. 模块选择与组合

(1) 必选模块
  • Lua 脚本模块(mod_lua)
    • 核心作用:通过 LuaJIT 实现复杂逻辑控制(分层查询、错误处理、缓存回写)。
    • 优势:灵活编写多级查询逻辑,支持 Redis/MySQL/HTTP 混合操作。
  • mod_curl
    • 核心作用:在 Lua 脚本中发起最终 HTTP 请求(如未命中缓存和数据库时)。
    • 优势:原生支持异步请求,性能优于 Lua 第三方 HTTP 库。
(2) 可选模块
  • mod_redisLua Redis 客户端库
    • 用于直接连接 Redis,提升查询效率(若使用 Lua 库需安装 lua-resty-redis)。
  • mod_odbcLua MySQL 客户端库
    • 用于连接 MySQL(若使用 Lua 库需安装 luasql.mysql)。

3. 推荐架构设计

plaintext 复制代码
FreeSWITCH 呼叫流程
       │
       ▼
   Lua 脚本
       │
       ├─1. 查询 Redis
       │     │
       │     ├─ 命中 → 返回归属地
       │     ▼
       ├─2. 未命中 → 查询 MySQL
       │     │
       │     ├─ 命中 → 回写 Redis → 返回归属地
       │     ▼
       ├─3. 未命中 → 调用 HTTP API(mod_curl)
       │     │
       │     ├─ 成功 → 回写 Redis + MySQL → 返回归属地
       │     ▼
       └─4. 失败 → 返回默认值或挂断

4. 实现步骤

步骤 1:启用必要模块
  • 加载模块 :在 modules.conf.xml 中启用:

    xml 复制代码
    <load module="mod_lua"/>
    <load module="mod_curl"/>
    <!-- 若使用 mod_redis 或 mod_odbc -->
    <load module="mod_redis"/> 
    <load module="mod_odbc"/>
步骤 2:编写 Lua 脚本
lua 复制代码
-- 示例脚本:get_caller_location.lua
local redis = require "resty.redis"
local mysql = require "luasql.mysql"
local curl = freeswitch.curl()

function get_location(session)
    local caller = session:getVariable("caller_id_number")
    local location = nil

    -- 1. 查询 Redis
    local red = redis:new()
    local ok, err = red:connect("redis.example.com", 6379)
    if ok then
        location = red:get("caller_location:" .. caller)
        red:set_keepalive(10000, 100) -- 连接池复用
    end

    -- 2. Redis 未命中 → 查询 MySQL
    if not location then
        local env = mysql.mysql()
        local conn = env:connect("db_name", "db_user", "db_pass", "db_host")
        if conn then
            local cursor = conn:execute("SELECT location FROM caller_locations WHERE number = '" .. caller .. "'")
            local row = cursor:fetch({}, "a")
            if row then
                location = row.location
                -- 回写 Redis(异步避免阻塞)
                freeswitch.background_exec("lua", "redis_set.lua " .. caller .. " " .. location)
            end
            conn:close()
            env:close()
        end
    end

    -- 3. MySQL 未命中 → HTTP 请求
    if not location then
        local response = curl.get("http://api.example.com/lookup?number=" .. caller, {timeout=3000})
        if response.code == 200 then
            location = response.body
            -- 异步回写 Redis 和 MySQL
            freeswitch.background_exec("lua", "persist_location.lua " .. caller .. " " .. location)
        end
    end

    -- 4. 返回结果或默认处理
    if location then
        session:setVariable("caller_location", location)
    else
        session:execute("hangup", "NO_ROUTE_DESTINATION")
    end
end
步骤 3:辅助脚本(异步回写)
  • redis_set.lua(异步写入 Redis):

    lua 复制代码
    local caller = argv[1]
    local location = argv[2]
    local red = redis:new()
    red:connect("redis.example.com", 6379)
    red:set("caller_location:" .. caller, location)
    red:expire("caller_location:" .. caller, 3600) -- 缓存1小时
    red:close()
  • persist_location.lua(异步写入 MySQL):

    lua 复制代码
    local caller = argv[1]
    local location = argv[2]
    local env = luasql.mysql()
    local conn = env:connect("db_name", "db_user", "db_pass", "db_host")
    conn:execute("INSERT INTO caller_locations (number, location) VALUES ('" .. caller .. "', '" .. location .. "')")
    conn:close()
    env:close()
步骤 4:集成到拨号计划
xml 复制代码
<extension name="dynamic_cid_lookup">
  <condition field="destination_number" expression="^(\d+)$">
    <action application="lua" data="get_caller_location.lua"/>
    <action application="set" data="effective_caller_id_name=${caller_location}"/>
    <action application="bridge" data="user/${destination_number}"/>
  </condition>
</extension>

5. 性能优化建议

  1. 连接池管理
    • Redis/MySQL 使用长连接和连接池(如 set_keepalive)。
  2. 异步回写
    • 通过 freeswitch.background_exec 异步写入数据库,避免阻塞主线程。
  3. 缓存策略
    • 设置合理的 Redis 过期时间(如 1 小时),平衡缓存命中率和数据新鲜度。
  4. 超时控制
    • Redis/MySQL/HTTP 均设置超时(如 Redis 500ms,MySQL 1s,HTTP 3s)。
  5. 降级策略
    • 任一层次查询失败时直接跳过,记录日志并继续下一层。

6. 模块选择对比

方案 优点 缺点
纯 Lua 脚本 + 第三方库 灵活性高,统一逻辑控制 依赖外部库安装,需维护 Lua 脚本
mod_redis + mod_odbc 性能更高(C 原生模块) 配置复杂,不支持动态 HTTP 回退
混合方案(推荐) 平衡性能与灵活性 需同时管理多个模块和脚本

7. 关键决策点

  • 选择 Lua 为主
    必须使用 Lua 脚本实现多级回退逻辑,其他模块(如 mod_cidlookup)无法满足复杂分层查询需求。
  • 启用 mod_curl
    最终 HTTP 回退需依赖此模块,且其异步模式优于 Lua 的第三方 HTTP 库。
  • Redis/MySQL 连接方式
    • 若对性能要求极高,优先使用 mod_redismod_odbc(C 原生模块)。
    • 若追求部署简便,直接使用 Lua 库(需安装 resty.redisluasql.mysql)。

8. 部署注意事项

  1. 依赖安装

    • Lua 库安装示例:

      bash 复制代码
      # 安装 luarocks(Lua 包管理器)
      apt-get install luarocks
      # 安装 Redis/MySQL 客户端库
      luarocks install lua-resty-redis
      luarocks install luasql-mysql
  2. 错误日志

    • 在 Lua 脚本中记录各层查询结果和错误信息:

      lua 复制代码
      freeswitch.consoleLog("WARNING", "MySQL query failed: " .. err)
  3. 压力测试

    • 使用 sippsngrep 模拟高并发,验证分层查询的稳定性。

9. 总结

  • 必须启用模块mod_lua(核心逻辑) + mod_curl(HTTP 回退)。
  • 推荐组合 :Lua 脚本 + Lua Redis/MySQL 库 + mod_curl
  • 优势:通过脚本控制多层回退、异步回写、灵活降级,满足动态数据交互需求。


结论

是的,通过 Lua 脚本 + 多级缓存(Redis → MySQL → HTTP) + mod_curl 的方案,可以在不启用 mod_cidlookupmod_blacklist 的情况下,实现更灵活的号码格式化、归属地查询和黑白名单管理,并满足高并发、低延迟(毫秒级响应)的需求。但需注意以下关键点:


1. 功能替代可行性

模块功能 原生模块实现(mod_cidlookup/mod_blacklist) Lua 脚本 + 多级缓存方案
归属地查询 基于本地数据库或简单 HTTP 请求 支持多级回退(Redis → MySQL → HTTP)
号码格式化 内置规则(如国际号补全) 可自定义规则(如正则匹配、外部 API 动态规则)
黑白名单管理 基于静态配置文件或简单数据库查询 支持动态规则(如实时风控、多维度拦截逻辑)
性能 高(C 原生实现,低延迟) 取决于缓存命中率和网络优化(需精细调优)
灵活性 低(依赖模块配置) 高(可自由扩展逻辑、集成外部系统)

2. 高并发低延迟场景的实现关键

(1) 缓存策略优化
  • Redis 热数据缓存

    • 将高频查询的号码(如近期通话号码)预热到 Redis,缓存命中率需 ≥95%
    • 设置合理的 TTL(如 1 小时),避免缓存雪崩。
  • 本地内存缓存 (可选):

    • 在 Lua 脚本中增加内存缓存层(如 lua_shared_dict),进一步减少 Redis 访问延迟。
  • 示例缓存结构

    lua 复制代码
    -- 伪代码:多级缓存查询
    local function get_caller_info(caller)
        -- 1. 本地内存缓存
        local cache = ngx.shared.caller_cache
        local info = cache:get(caller)
        if info then return info end
    
        -- 2. Redis 缓存
        local info = redis.get("caller:" .. caller)
        if info then
            cache:set(caller, info, 60)  -- 内存缓存60秒
            return info
        end
    
        -- 3. 回退到 MySQL/HTTP
        -- ...
    end
(2) 异步与非阻塞设计
  • 异步 HTTP 请求

    • 使用 curl_async 发起 HTTP 请求,避免阻塞 FreeSWITCH 会话。
    • 通过 event 机制或协程处理异步响应。
  • 非阻塞数据库操作

    • 使用连接池(如 Redis set_keepalive、MySQL 连接池),减少连接建立开销。
  • 示例异步 HTTP 请求

    lua 复制代码
    local curl = require "freeswitch.curl"
    local caller = session:getVariable("caller_id_number")
    
    -- 异步请求(回调函数处理响应)
    curl.async_get("http://api.example.com/lookup?caller=" .. caller, {
        callback = function(response)
            if response.code == 200 then
                session:setVariable("caller_location", response.body)
            end
        end
    })
(3) 性能调优
优化方向 具体措施
减少网络延迟 - Redis/MySQL 部署在 FreeSWITCH 同机房或内网。 - HTTP API 启用 CDN 或全球加速。
批量查询 合并多个号码的查询请求(如 GET /batch_lookup?numbers=13800138000,13900139000)。
无锁化设计 避免 Lua 脚本中的全局锁,使用无状态设计。
JIT 编译优化 确保 LuaJIT 启用,关键代码段使用 jit.on()jit.off() 精细控制。

3. 功能实现对比

(1) 号码格式化
  • 原生模块(mod_cidlookup)

    xml 复制代码
    <!-- 示例:简单补全国际码 -->
    <param name="default-country" value="CN"/>
    <param name="prefix" value="+86"/>
  • Lua 脚本方案

    lua 复制代码
    local function format_number(caller)
        -- 自定义规则(如 +86 转换、去除前缀0)
        if string.match(caller, "^0") then
            return "+86" .. string.sub(caller, 2)
        end
        return caller
    end
(2) 黑白名单拦截
  • 原生模块(mod_blacklist)

    xml 复制代码
    <!-- 示例:静态黑名单 -->
    <list name="blacklist">
        <node type="allow" cidr="192.168.1.0/24"/>
        <node type="deny" cidr="10.0.0.5/32"/>
    </list>
  • Lua 脚本方案

    lua 复制代码
    local function check_blacklist(caller)
        -- 动态规则(如实时风控 API)
        local risk_score = curl.get("http://api.risk.com/check?caller=" .. caller)
        return risk_score > 80  -- 风险分高于80则拦截
    end

4. 性能基准参考

场景 mod_cidlookup/mod_blacklist Lua 脚本方案
单次查询延迟 0.1~1 ms(本地查询) 0.5~5 ms(依赖缓存命中率)
并发能力 10,000+ TPS(C 原生) 3,000~5,000 TPS(LuaJIT 优化后)
资源占用 低(独立进程) 中(Lua 虚拟机 + 网络连接)

5. 何时选择原生模块?

  • 需求简单:仅需静态归属地查询或固定黑白名单。
  • 极致性能:业务对延迟极度敏感(如金融级实时交易系统)。
  • 避免外部依赖:无可用 Redis/MySQL/HTTP 服务,或要求纯本地化部署。

6. 何时选择 Lua 脚本方案?

  • 动态规则:需实时更新归属地、黑白名单(如结合风控系统)。
  • 多数据源融合:需同时查询归属地、用户画像、历史行为等。
  • 业务扩展性:未来可能增加更多定制化逻辑(如 VIP 号码优先路由)。

7. 最终建议

  • 混合架构 (平衡性能与灵活性):
    • 核心高频功能 (如黑白名单基础拦截)使用 mod_blacklist
    • 动态扩展功能(如风控、多级归属地查询)使用 Lua 脚本 + 多级缓存。
  • 全量替代条件
    • 已具备高性能 Redis/MySQL 集群和低延迟 HTTP 服务。
    • 团队熟悉 Lua 开发和 FreeSWITCH 异步事件机制。

通过合理设计,Lua 脚本方案完全可以在高并发场景下替代 mod_cidlookupmod_blacklist,但需在性能、灵活性和维护成本之间谨慎权衡。



结论

是的,通过 Lua 脚本 + 多级缓存(Redis → MySQL → HTTP) 的方案,可以完全替代 mod_cidlookupmod_blacklist,实现更灵活、动态的号码格式化、归属地查询和黑白名单管理,彻底无需启用原生模块。但需满足以下条件:


1. 完全替代的前提

模块功能 替代方案 关键要求
归属地查询 Lua 脚本查询 Redis/MySQL/HTTP,动态返回归属地信息 Redis 缓存命中率 ≥95%,HTTP 接口响应时间 ≤50ms
号码格式化 Lua 脚本自定义规则(如正则匹配、外部 API 动态规则) 格式化逻辑需封装为可复用的 Lua 函数,避免重复编码
黑白名单管理 Lua 脚本结合动态数据源(如 Redis 实时黑名单、风控 API) 黑名单数据更新需实时同步到 Redis,拦截逻辑需毫秒级响应
性能 通过多级缓存和异步设计优化,达到与原生模块相近的延迟(≤5ms 平均响应) Redis 部署在 FreeSWITCH 同机房,HTTP 服务启用 CDN 或边缘计算

2. 原生模块 vs. Lua 脚本方案对比

维度 mod_cidlookup/mod_blacklist Lua 脚本方案
功能实现 简单、静态规则(本地数据库/配置文件) 复杂、动态规则(多级缓存、外部 API、实时风控)
性能 超高(C 原生实现,微秒级延迟) 高(依赖缓存命中率和网络优化,毫秒级延迟)
灵活性 低(配置驱动,无法动态扩展) 极高(可自由编码,支持任意逻辑)
维护成本 低(模块配置简单,无需编码) 中高(需维护 Lua 脚本、缓存策略、外部服务)
适用场景 静态数据、固定规则、极致性能需求 动态数据、复杂规则、高扩展性需求

3. 完全替代的实现方案

(1) 归属地查询与号码格式化
lua 复制代码
-- 示例:动态归属地查询 + 号码格式化
function get_formatted_caller(session)
    local caller = session:getVariable("caller_id_number")
    
    -- 1. 查询 Redis 缓存
    local location = redis.get("caller_location:" .. caller)
    if not location then
        -- 2. 查询 MySQL
        location = mysql.query("SELECT location FROM caller_locations WHERE number = ?", caller)
        if not location then
            -- 3. 回退到 HTTP API
            local response = curl.get("http://api.example.com/lookup?caller=" .. caller)
            location = response.body or "Unknown"
            -- 异步回写缓存
            freeswitch.background_exec("lua", "redis_set.lua " .. caller .. " " .. location)
        end
        redis.set("caller_location:" .. caller, location, "EX", 3600)
    end

    -- 自定义格式化(如 +86 前缀)
    local formatted_caller = "+86" .. string.gsub(caller, "^0", "")
    session:setVariable("effective_caller_id_number", formatted_caller)
    session:setVariable("caller_location", location)
end
(2) 黑白名单动态拦截
lua 复制代码
-- 示例:动态黑名单检查(支持实时风控)
function check_blacklist(session)
    local caller = session:getVariable("caller_id_number")
    local destination = session:getVariable("destination_number")

    -- 1. 检查本地缓存黑名单
    if redis.sismember("local_blacklist", caller) == 1 then
        session:hangup("USER_BUSY")
        return
    end

    -- 2. 调用风控 API(异步非阻塞)
    curl.async_get("http://api.risk.com/check?caller=" .. caller, {
        callback = function(response)
            if response.code == 200 and tonumber(response.body) > 80 then
                -- 高风险号码,加入实时黑名单并拦截
                redis.sadd("realtime_blacklist", caller)
                session:hangup("USER_BUSY")
            end
        end
    })
end

4. 性能优化关键措施

  1. Redis 热数据预热
    • 定期将高频号码的归属地、黑名单数据预加载到 Redis。
    • 使用 LFU 淘汰策略,优先保留热点数据。
  2. 批量查询与异步回写
    • 对批量呼叫请求合并查询(如 MGET 批量获取 Redis 数据)。
    • 异步回写数据库和缓存,避免阻塞主线程。
  3. LuaJIT 性能调优
    • 使用 FFI 调用 C 库加速关键代码。
    • 避免全局变量和频繁的 GC 操作。
  4. 网络架构优化
    • Redis/MySQL 部署在 FreeSWITCH 同一可用区,网络延迟 ≤1ms。
    • HTTP 服务启用 HTTP/2 和压缩,减少传输开销。

5. 何时不建议完全替代?

场景 建议
超高性能需求 需微秒级响应(如金融级实时交易系统),保留原生模块。
无运维团队支持 缺乏维护 Lua 脚本和缓存服务的能力时,使用原生模块更稳定。
纯静态规则 数据极少变动(如国际区号表),原生模块配置更简单。

6. 最终建议

  • 完全替代的条件
    • 业务需要动态、实时数据交互。
    • 团队具备 Lua 开发和缓存架构维护能力。
    • 已部署高性能 Redis/MySQL 集群和低延迟 HTTP 服务。
  • 保留原生模块的条件
    • 业务规则简单且静态。
    • 对性能要求达到极致(如每秒万级查询)。

通过合理设计,Lua 脚本方案可以完全取代 mod_cidlookupmod_blacklist,但需在性能、灵活性和运维成本之间精细权衡。

相关推荐
狂爱代码的码农2 天前
FreeSwitch 的 `mod_blacklist` 模块详解
freeswitch
狂爱代码的码农19 天前
freeswitch在centos上编译过程
freeswitch
狂爱代码的码农1 个月前
debian12.9编译freeswitch1.10.12【默认安装】
freeswitch
Mike_Zhang2 个月前
FreeSWITCH日志功能分析及apr模拟
voip·freeswitch
贾宝玉的玉宝贾2 个月前
FreeSWITCH 简单图形化界面39 - Windows安装FreeSWITCH For IPPBX(WSL环境)
windows·voip·freeswitch·ippbx·sip测试
贾宝玉的玉宝贾2 个月前
FreeSWITCH 简单图形化界面38 - 使用uniapp中使用JsSIP进行音视频呼叫
uni-app·音视频·voip·freeswitch·ippbx·jssip
代码浪人2 个月前
docker 基于Debian镜像安装FreeSwitch1.10.7
docker·容器·debian·freeswitch
new_abc3 个月前
Sofia-SIP 使用教程
freeswitch·sofia
hongkid3 个月前
docker 部署freeswitch(非编译方式)
docker·容器·freeswitch