mod_distributor
模块详解及应用场景mod_distributor
是 FreeSWITCH 中一个用于动态分配呼叫或任务的模块,其核心功能是将传入的请求(如 SIP 呼叫、消息等)按预设策略分发到多个目标节点或坐席。它通常用于实现负载均衡、高可用性及灵活的呼叫路由。
分配策略:
动态节点管理:
集成接口:
启用模块:
在 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"/>
呼叫中心负载均衡:
多节点高可用架构:
云通信平台扩展:
多租户资源隔离:
mod_snmp
或外部监控工具,跟踪分发效率及节点负载。mod_distributor
更轻量,适合基础分发。mod_distributor
侧重即时负载均衡。mod_distributor
是构建弹性通信系统的关键组件,通过灵活的分发策略提升资源利用率和系统可靠性。适用于需要动态扩展、故障恢复或复杂路由策略的场景,是高效管理大规模语音/消息流量的理想选择。
是的,FreeSWITCH 的 mod_distributor
可以动态生成并管理基于权重的多节点高可用架构。以下是具体实现方法、技术细节及场景应用:
mod_distributor
的节点权重(weight
)可通过 运行时 API 动态修改,无需重启服务或重新加载模块。# 通过 ESL 调整节点权重
api distributor set_weight my_node_pool node1 10
# 添加新节点到池
api distributor add_node my_node_pool node3 sofia/internal/sip:agent3@domain weight=7
# 移除故障节点
api distributor del_node my_node_pool node2
# 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}
)
mod_distributor
的健康检查,例如检查数据库连接或 API 响应:
<nodes>
<node name="node1" url="..." health_check="custom_script.lua"/>
nodes>
mod_hash
实现一致性哈希,确保同一会话(如通话)始终路由到同一节点,避免中途中断。mod_http_request
获取实时延迟数据 → 通过 ESL 调整权重。10
,公有云为 2
;当并发数超过阈值时,临时提升公有云权重至 8
。-- 根据节点负载动态调整权重
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)
<pools>
<pool name="high_availability">
<nodes>
<node name="node1" url="sofia/public/sip:10.0.0.1" weight="10" health_check="5s"/>
<node name="node2" url="sofia/public/sip:10.0.0.2" weight="5" health_check="http://10.0.0.2/health"/>
nodes>
<strategy>weightedstrategy>
pool>
pools>
权重精度控制:
权重值为整数,需确保不同节点间的权重比例合理(如 3:2 而非 100:99)。
避免脑裂问题:
在双活数据中心场景中,结合 mod_redis
同步节点状态,防止因网络分区导致权重冲突。
性能监控:
使用 mod_snmp
或 mod_statsd
监控分发延迟、节点健康状态。
通过 mod_distributor
的动态 API 和灵活配置,可以构建 自适应权重调整、多节点高可用 的通信架构。结合云原生工具链和外部监控系统,能够实现全自动化的弹性伸缩与故障恢复,适用于大规模、高并发的企业级通信场景。
以下是针对 上百个动态节点(配置在 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=禁用)
);
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_nodesurl>
<method>POSTmethod>
binding>
bindings>
configuration>
示例 PHP 脚本 (distributor_nodes.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 "
$node_pool \">";
foreach ($nodes as $node) {
echo "{$node['node_name']} \" url=\"{$node['node_url']}\" weight=\"{$node['weight']}\"/>";
}
echo "";
在拨号计划中触发动态分发:
<context name="dynamic_distributor">
<extension name="dynamic_route">
<condition field="destination_number" expression="^(\d+)$">
<action application="set" value="effective_node_pool=${curl(http://localhost:8080/xml_curl/distributor_nodes?Called-Number=${destination_number})}"/>
<action application="distributor" data="weighted ${effective_node_pool}"/>
condition>
extension>
context>
通过 MySQL 触发器或管理后台修改权重后,调用 FreeSWITCH API 同步:
# 示例:通过 ESL 更新节点权重
fs_cli -x "api distributor set_weight $node_pool $node_name $new_weight"
定时扫描数据库变化并推送更新:
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()
缓存层加速:
批量预加载:
在 FreeSWITCH 启动时预加载常用节点池到内存:
-- scripts/init.lua
local pools = {"pool1", "pool2", "pool3"}
for _, pool in ipairs(pools) do
session:execute("distributor", "preload "..pool)
end
mysqli_poll
或 SQLAlchemy
)。双写容灾:
故障降级:
若数据库不可用,使用本地缓存的最远权重数据:
<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 个节点)。
请求流程:
861012345678
。call_rules
表匹配到 node_pool=beijing
。distributor_nodes
表拉取所有 beijing
池的活跃节点及权重。mod_distributor
。node_beijing_23
)。动态调整:
node_beijing_23
权重从 10
改为 5
。distributor set_weight beijing node_beijing_23 5
。called_prefix
和 node_pool
字段添加索引,加快查询速度。mod_cdr
或 mod_odbc_cdr
,便于事后分析。