FreeSWITCH 的 mod_distributor
模块详解及应用场景
模块概述
mod_distributor
是 FreeSWITCH 中一个用于动态分配呼叫或任务的模块,其核心功能是将传入的请求(如 SIP 呼叫、消息等)按预设策略分发到多个目标节点或坐席。它通常用于实现负载均衡、高可用性及灵活的呼叫路由。
核心功能与工作原理
-
分配策略:
- 轮询(Round Robin):依次分配请求,确保各节点均匀负载。
- 随机(Random):随机选择目标节点,避免集中压力。
- 基于权重(Weighted):根据节点处理能力分配不同权重,优先级更高的节点接收更多请求。
- 最少连接(Least Connections):将请求导向当前活跃连接最少的节点。
-
动态节点管理:
- 支持运行时动态添加或移除节点,无需重启服务。
- 提供健康检查机制,自动排除故障节点,确保请求仅分配给可用资源。
-
集成接口:
- 通过 FreeSWITCH 的 XML/HTTP API 或 Event Socket 与其他系统交互。
- 可与数据库或外部控制逻辑结合,实现复杂分发逻辑(如基于客户优先级分配)。
配置与使用
-
启用模块:
在conf/autoload_configs/modules.conf.xml
中添加:<load module="mod_distributor"/>
-
定义节点池:
在 XML 配置文件(如distributor.conf.xml
)中配置目标节点:<nodes> <node name="node1" weight="5" url="sofia/internal/sip:agent1@domain"/> <node name="node2" weight="3" url="sofia/internal/sip:agent2@domain"/> </nodes>
-
路由逻辑:
在 Dialplan 中调用分发策略:<action application="distributor" data="round-robin my_node_pool"/>
应用场景
-
呼叫中心负载均衡:
- 将大量来电均匀分配给坐席,避免单个坐席过载。结合技能组(通过权重),优先路由给高级支持团队。
-
多节点高可用架构:
- 在多个 FreeSWITCH 实例间分配 SIP 请求,提升系统容错能力。若某节点故障,自动将流量切换到健康节点。
-
云通信平台扩展:
- 横向扩展处理能力,根据实时负载动态增减节点(如 Kubernetes 弹性伸缩),应对突发流量。
-
多租户资源隔离:
- 为不同客户分配独立节点池,确保资源公平使用,防止某一租户占用过多资源。
最佳实践与注意事项
- 健康检查配置:定期检测节点状态(如 SIP OPTIONS 心跳),避免将请求分配给宕机节点。
- 性能监控:结合
mod_snmp
或外部监控工具,跟踪分发效率及节点负载。 - 算法选择:高并发场景建议使用“最少连接”,确保实时负载均衡;简单轮询适合节点性能均匀的环境。
- 安全隔离:在分发时验证请求来源,防止恶意流量占用资源。
与其他模块对比
- mod_callcenter:专为呼叫中心设计,支持队列管理、坐席状态监控等,而
mod_distributor
更轻量,适合基础分发。 - mod_hash:通过一致性哈希实现持久会话(同一用户固定到某节点),而
mod_distributor
侧重即时负载均衡。
总结
mod_distributor
是构建弹性通信系统的关键组件,通过灵活的分发策略提升资源利用率和系统可靠性。适用于需要动态扩展、故障恢复或复杂路由策略的场景,是高效管理大规模语音/消息流量的理想选择。
深入解析:动态生成基于权重的多节点高可用架构
是的,FreeSWITCH 的 mod_distributor
可以动态生成并管理基于权重的多节点高可用架构。以下是具体实现方法、技术细节及场景应用:
一、动态权重的实现机制
1. 动态权重调整
- 原生支持:
mod_distributor
的节点权重(weight
)可通过 运行时 API 动态修改,无需重启服务或重新加载模块。
例如通过 Event Socket (ESL) 或 XML/HTTP API 发送命令,直接更新节点池中某个节点的权重值:# 通过 ESL 调整节点权重 api distributor set_weight my_node_pool node1 10
- 外部集成:
结合外部监控系统(如 Prometheus)或负载均衡控制器,根据实时指标(CPU、连接数)自动调整权重。例如:- 当节点负载超过阈值时,降低权重,减少新请求分配。
- 当节点空闲时,增加权重,吸引更多流量。
2. 动态节点增删
- 运行时操作:
支持通过 API 动态添加或移除节点,适用于云环境中的弹性伸缩场景:# 添加新节点到池 api distributor add_node my_node_pool node3 sofia/internal/sip:agent3@domain weight=7 # 移除故障节点 api distributor del_node my_node_pool node2
- 自动化脚本示例:
当 Kubernetes 检测到新 Pod 启动时,自动调用 FreeSWITCH API 将节点加入分发池:# Python 示例:通过 HTTP API 添加节点 import requests response = requests.post( "http://freeswitch:8080/api/distributor/add_node", params={"pool": "cloud_nodes", "name": "pod-123", "url": "sofia/public/sip:10.0.0.5", "weight": 5} )
二、高可用架构的核心设计
1. 健康检查与故障转移
- 内置健康检查:
通过定期发送 SIP OPTIONS 请求检测节点存活状态,自动屏蔽不可达节点。 - 自定义检测逻辑:
扩展mod_distributor
的健康检查,例如检查数据库连接或 API 响应:<!-- 配置节点健康检查策略 --> <nodes> <node name="node1" url="..." health_check="custom_script.lua"/> </nodes>
2. 流量切换策略
- 平滑权重迁移:
动态调整权重时,逐步增加/减少流量分配比例,避免瞬间冲击。 - 会话保持(可选):
通过mod_hash
实现一致性哈希,确保同一会话(如通话)始终路由到同一节点,避免中途中断。
三、典型应用场景
1. 云原生弹性伸缩
- 场景:
在 AWS/Azure 上部署 FreeSWITCH 集群,根据 CPU 使用率自动扩展节点数量。 - 实现:
云监控触发 Lambda 函数 → 调用 FreeSWITCH API 动态添加节点并设置权重。
2. 多地域容灾
- 场景:
跨地域部署节点(如北京、上海、纽约),根据网络延迟动态调整权重。 - 实现:
使用mod_http_request
获取实时延迟数据 → 通过 ESL 调整权重。
3. 混合云负载均衡
- 场景:
混合云环境(私有云 + 公有云),优先将流量分配到私有云节点,仅在高峰期启用公有云节点。 - 实现:
设置私有云节点初始权重为10
,公有云为2
;当并发数超过阈值时,临时提升公有云权重至8
。
四、配置示例与代码片段
1. 动态权重调整脚本(Lua)
-- 根据节点负载动态调整权重
local load = get_node_load("node1") -- 假设从监控系统获取负载值
local new_weight = math.floor(100 / (load + 1)) -- 负载越高,权重越低
-- 通过 ESL 更新权重
local cmd = "api distributor set_weight my_pool node1 " .. new_weight
local response = freeswitch.API():executeString(cmd)
2. 自动化健康检查配置
<!-- conf/autoload_configs/distributor.conf.xml -->
<pools>
<pool name="high_availability">
<nodes>
<node name="node1" url="sofia/public/sip:10.0.0.1" weight="10" health_check="5s"/> <!-- 每5秒检查一次 -->
<node name="node2" url="sofia/public/sip:10.0.0.2" weight="5" health_check="http://10.0.0.2/health"/>
</nodes>
<strategy>weighted</strategy>
</pool>
</pools>
五、注意事项与优化建议
-
权重精度控制:
权重值为整数,需确保不同节点间的权重比例合理(如 3:2 而非 100:99)。 -
避免脑裂问题:
在双活数据中心场景中,结合mod_redis
同步节点状态,防止因网络分区导致权重冲突。 -
性能监控:
使用mod_snmp
或mod_statsd
监控分发延迟、节点健康状态。
总结
通过 mod_distributor
的动态 API 和灵活配置,可以构建 自适应权重调整、多节点高可用 的通信架构。结合云原生工具链和外部监控系统,能够实现全自动化的弹性伸缩与故障恢复,适用于大规模、高并发的企业级通信场景。
分步实现:基于 MySQL 动态节点及权重的呼叫路由
以下是针对 上百个动态节点(配置在 MySQL) 并根据被叫号码动态分配呼叫的完整实现方案:
一、数据库设计
创建 MySQL 表存储节点信息及路由规则:
CREATE TABLE `call_rules` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`called_prefix` VARCHAR(10) NOT NULL, -- 被叫号码前缀(如 8610)
`node_pool` VARCHAR(50) NOT NULL -- 关联的节点池名称
);
CREATE TABLE `distributor_nodes` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`node_pool` VARCHAR(50) NOT NULL, -- 节点池名称(与call_rules关联)
`node_name` VARCHAR(50) NOT NULL, -- 节点唯一标识
`node_url` VARCHAR(255) NOT NULL, -- SIP地址(如 sofia/external/sip:192.168.1.10:5060)
`weight` INT DEFAULT 10, -- 动态权重
`is_active` TINYINT DEFAULT 1 -- 是否启用(0=禁用)
);
二、FreeSWITCH 动态加载逻辑
1. 使用 mod_xml_curl
动态获取节点池
配置 xml_curl.conf.xml
从 MySQL 拉取节点数据:
<configuration name="xml_curl.conf">
<bindings>
<binding name="distributor_nodes">
<url>http://localhost:8080/xml_curl/distributor_nodes</url>
<method>POST</method>
</binding>
</bindings>
</configuration>
2. 编写 PHP/Python 接口查询数据库
示例 PHP 脚本 (distributor_nodes.php
):
<?php
$pdo = new PDO('mysql:host=localhost;dbname=freeswitch', 'user', 'pass');
$called_number = $_POST['Called-Number'] ?? '';
$prefix = substr($called_number, 0, 4); // 提取被叫前缀
// 1. 根据被叫前缀获取关联的节点池
$stmt = $pdo->prepare("SELECT node_pool FROM call_rules WHERE ? LIKE CONCAT(called_prefix, '%')");
$stmt->execute([$called_number]);
$node_pool = $stmt->fetchColumn();
// 2. 查询该节点池下所有激活的节点
$nodes = $pdo->query("SELECT node_name, node_url, weight FROM distributor_nodes
WHERE node_pool = '$node_pool' AND is_active = 1")->fetchAll(PDO::FETCH_ASSOC);
// 3. 生成 XML 响应
header("Content-Type: text/xml");
echo "<document type=\"freeswitch/xml\">
<section name=\"distributor\">
<pool name=\"$node_pool\">";
foreach ($nodes as $node) {
echo "<node name=\"{$node['node_name']}\" url=\"{$node['node_url']}\" weight=\"{$node['weight']}\"/>";
}
echo "</pool></section></document>";
三、FreeSWITCH Dialplan 配置
在拨号计划中触发动态分发:
<context name="dynamic_distributor">
<extension name="dynamic_route">
<condition field="destination_number" expression="^(\d+)$">
<!-- 1. 调用 XML_CURL 接口获取节点池 -->
<action application="set" value="effective_node_pool=${curl(http://localhost:8080/xml_curl/distributor_nodes?Called-Number=${destination_number})}"/>
<!-- 2. 使用 mod_distributor 分配呼叫 -->
<action application="distributor" data="weighted ${effective_node_pool}"/>
</condition>
</extension>
</context>
四、动态权重更新机制
1. 外部系统更新权重
通过 MySQL 触发器或管理后台修改权重后,调用 FreeSWITCH API 同步:
# 示例:通过 ESL 更新节点权重
fs_cli -x "api distributor set_weight $node_pool $node_name $new_weight"
2. 自动同步脚本(Python)
定时扫描数据库变化并推送更新:
import MySQLdb
import subprocess
db = MySQLdb.connect("localhost", "user", "pass", "freeswitch")
cursor = db.cursor()
# 查询最近修改过的节点
cursor.execute("""
SELECT node_pool, node_name, weight
FROM distributor_nodes
WHERE last_updated > NOW() - INTERVAL 5 MINUTE
""")
for row in cursor.fetchall():
pool, node, weight = row
cmd = f"api distributor set_weight {pool} {node} {weight}"
subprocess.run(["fs_cli", "-x", cmd])
db.close()
五、性能优化策略
-
缓存层加速:
- 使用 Redis 缓存节点池数据,减少直接查询 MySQL 的压力。
- 设置 TTL 为 30 秒,确保数据时效性。
-
批量预加载:
在 FreeSWITCH 启动时预加载常用节点池到内存:
-- scripts/init.lua
local pools = {"pool1", "pool2", "pool3"}
for _, pool in ipairs(pools) do
session:execute("distributor", "preload "..pool)
end
- 连接池管理:
在 PHP/Python 脚本中使用 MySQL 连接池(如mysqli_poll
或SQLAlchemy
)。
六、高可用保障
-
双写容灾:
- 配置 MySQL 主从复制,当主库故障时自动切换从库。
- 在 FreeSWITCH 中设置多个 XML_CURL 端点,指向不同的数据库查询接口。
-
故障降级:
若数据库不可用,使用本地缓存的最远权重数据:
<action application="distributor" data="weighted ${effective_node_pool}"/>
<action application="log" data="WARNING Database unreachable, using cached nodes"/>
<action application="set" value="hangup_after_bridge=true"/>
七、完整流程示例
场景:呼叫 861012345678
,根据前缀 8610
路由到北京节点池(包含 50 个节点)。
-
请求流程:
- FreeSWITCH 收到呼叫 → 提取被叫号码
861012345678
。 - 触发 XML_CURL 请求 → PHP 查询
call_rules
表匹配到node_pool=beijing
。 - PHP 从
distributor_nodes
表拉取所有beijing
池的活跃节点及权重。 - FreeSWITCH 动态加载这些节点到
mod_distributor
。 - 按权重分配呼叫到具体节点(如
node_beijing_23
)。
- FreeSWITCH 收到呼叫 → 提取被叫号码
-
动态调整:
- 运维人员在 MySQL 中将
node_beijing_23
权重从10
改为5
。 - Python 同步脚本检测到变更 → 通过 ESL 执行
distributor set_weight beijing node_beijing_23 5
。 - 后续呼叫自动减少向该节点的流量分配。
- 运维人员在 MySQL 中将
八、关键注意事项
- 索引优化:为
called_prefix
和node_pool
字段添加索引,加快查询速度。 - 权重归一化:确保权重总和不超过系统处理能力(如单个节点池权重总和≤1000)。
- 日志监控:记录分发详情到
mod_cdr
或mod_odbc_cdr
,便于事后分析。