FreeSWITCH 中的 mod_dptools
和 mod_easyroute
是两个功能不同的模块,分别服务于呼叫控制和动态路由场景。以下是详细介绍:
mod_dptools
(Dialplan Tools)是 FreeSWITCH 最核心的模块之一,提供了丰富的 Application(App) 和工具,用于在拨号计划(Dialplan)中实现呼叫控制逻辑14。主要功能包括:answer
(接听)、bridge
(桥接)、hangup
(挂断)等。playback
(播放音频)、record
(录音)、set_audio_level
(调节音量)。read
、capture
等应用捕获用户按键输入^2。limit
应用限制并发呼叫数(Capacity)或每秒呼叫数(CPS)^5。user
端点,简化呼叫字符串配置(如 user/1000
映射到 SIP 地址)^1。应用场景
playback
和 read
实现语音菜单交互。bridge
转接呼叫,利用 limit
控制并发量。mod_easyroute
是一个轻量级的 动态路由引擎,通过数据库查询实现基于 DID(直接拨入号码)的路由决策69。核心特性包括:应用场景
对比与协作
mod_dptools
提供底层呼叫控制能力,而 mod_easyroute
专注于高层路由逻辑。例如,可通过 mod_easyroute
查询数据库确定目标,再通过 mod_dptools
的 bridge
发起呼叫。mod_easyroute
查询数据库获取目标地址。mod_dptools
的 bridge
执行呼叫桥接,并通过 limit
限制并发量59。总结
mod_dptools
:是 FreeSWITCH 的“瑞士军刀”,适用于基础呼叫控制、媒体操作和资源管理12^5。mod_easyroute
:简化动态路由配置,适合需要灵活 DID 路由的场景69。在高并发、低延迟场景下,结合 mod_dptools
和 mod_easyroute
实现动态路由的核心思路是:通过 mod_easyroute
快速查询 MySQL 路由表,再通过 mod_dptools
的 bridge
和 limit
实现高效呼叫控制。以下是具体实现方法:
一、模块协作方法
mod_easyroute
:
=
)和前缀匹配(LIKE
),被叫前缀的规则匹配。mod_dptools
:
limit
控制并发呼叫量,避免过载。bridge
快速桥接呼叫,减少延迟。二、MySQL 表结构示例
sql
CREATE TABLE route_rules (
id INT PRIMARY KEY AUTO_INCREMENT,
ip VARCHAR(15) NOT NULL, – 目标服务器 IP
port INT NOT NULL, – 目标端口
caller_match_type ENUM(‘exact’, ‘prefix’), – 主叫匹配类型
caller_pattern VARCHAR(32), – 主叫号码(完全匹配或前缀)
callee_prefix VARCHAR(16), – 被叫前缀
callee_rewrite_rule VARCHAR(64), – 被叫重写规则(如 ^(\d+)$
→ 1\1
)
outbound_time_start TIME, – 外呼时间范围(起始)
outbound_time_end TIME, – 外呼时间范围(结束)
region VARCHAR(32), – 外呼地区(如 us-east
)
priority INT DEFAULT 10, – 路由优先级(数值越小优先级越高)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
三、配置文件示例
MySQL 连接配置(vars.xml
)
xml
mod_easyroute
路由查询(easyroute.conf.xml
)
xml
SELECT ip, port, callee_rewrite_rule
FROM route_rules
WHERE
(caller_match_type = ‘exact’ AND caller_pattern = ‘ c a l l e r i d n u m b e r ′ ) O R ( c a l l e r m a t c h t y p e = ′ p r e f i x ′ A N D ′ {caller_id_number}') OR (caller_match_type = 'prefix' AND ' calleridnumber′)OR(callermatchtype=′prefix′AND′{caller_id_number}’ LIKE CONCAT(caller_pattern, ‘%’))
AND ‘ d e s t i n a t i o n n u m b e r ′ L I K E C O N C A T ( c a l l e e p r e f i x , ′ A N D T I M E ( N O W ( ) ) B E T W E E N o u t b o u n d t i m e s t a r t A N D o u t b o u n d t i m e e n d A N D r e g i o n = ′ {destination_number}' LIKE CONCAT(callee_prefix, '%') AND TIME(NOW()) BETWEEN outbound_time_start AND outbound_time_end AND region = ' destinationnumber′LIKECONCAT(calleeprefix,′ANDTIME(NOW())BETWEENoutboundtimestartANDoutboundtimeendANDregion=′{region}’ – 假设地区信息通过变量传递
ORDER BY priority ASC
LIMIT 1
拨号计划(dialplan.xml
)
xml
四、优化建议
caller_pattern
、callee_prefix
、region
字段建立复合索引。mod_db
中启用连接池,避免频繁建立数据库连接。ulimit -n
和 UDP 缓冲区大小,提升并发处理能力13。五、典型场景
外呼系统动态路由:
10123456789
匹配前缀 101
,被叫前缀 +86
,当前时间在允许范围内。mod_easyroute
查询到目标 IP 10.0.0.5:5060
,并应用被叫重写规则。mod_dptools
的 limit
确保并发不超过 1000,bridge
完成低延迟呼叫。通过上述配置,可实现 毫秒级路由决策 + 高并发控制,满足严苛的实时性要求16^7。
easyroute.conf.xml
文件通常位于 FreeSWITCH 的配置目录下的 conf/autoload_configs/
子目录中^3。完整路径示例如下:
/usr/local/freeswitch/conf/autoload_configs/easyroute.conf.xml
说明:
conf/
目录下,模块的配置文件(如 mod_easyroute
)一般存放在 conf/autoload_configs/
中^3。autoload_configs/
中的模块配置,确保 mod_easyroute
的路由规则生效。验证方法:
如果未找到文件,可能需要手动创建该配置文件并启用模块。
要实现动态并发限制,需结合 mod_dptools
的 limit
应用与 mod_easyroute
的路由查询功能,并通过 MySQL 动态获取限制值。以下是具体步骤和配置示例:
route_rules
表中新增 concurrency_limit
字段:mod_easyroute
查询easyroute.conf.xml
的 SQL 查询,返回 concurrency_limit
字段:动态设置并发限制
在拨号计划中,通过 set
应用将 concurrency_limit
保存为通道变量,并在 limit
中引用:
xml
关键点说明
easyroute_result(concurrency_limit)
从路由查询结果中提取 concurrency_limit
字段^2。expr()
函数用于处理空值,若数据库未返回限制值,则默认使用 1000^2。ulimit -n 65535
)和 UDP 缓冲区^1。验证方法
dynamic_limit
是否正确加载:通过上述配置,可实现 基于路由规则的动态并发控制,满足高并发场景的灵活需求23。
场景一
如果将mysql数据表缓存到redis的8号库,动态路由先查询redis,不存在,再查询mysql,依旧不存在,返回404报错,问题一:缓存到redis是键值对该如何配置?问题二:给出添加redis后的配置文件代码
问题一:Redis 键值对配置方法
在 Redis 8 号库中,键值对设计需包含路由规则的核心字段,建议采用以下格式:
route:${caller_id_number}:${destination_number}
${caller_id_number}
为主叫号码,${destination_number}
为被叫号码。若需支持前缀匹配,可扩展为:ip
、port
、callee_rewrite_rule
等字段,例如:问题二:添加 Redis 后的配置文件代码
Redis 连接配置(vars.xml
)
xml
拨号计划(dialplan.xml
)
xml
redis_query.lua
)– 尝试完全匹配
local key_exact = “route:exact:” … caller … “:” … callee
local result = client:get(key_exact)
– 若未命中,尝试前缀匹配
if not result then
local key_prefix = “route:prefix:” … string.sub(caller, 1, 3) … “:” … callee
result = client:get(key_prefix)
end
if result then
session:setVariable(“easyroute_result”, result)
session:answer()
else
session:setVariable(“skip_easyroute”, “false”) – 继续执行后续动作
end
redis_set.lua
)– 根据匹配类型设置键(示例仅存储完全匹配)
local key = “route:exact:” … caller … “:” … callee
client:setex(key, 3600, route_data) – 缓存 1 小时
关键点说明
setex
设置过期时间避免内存泄漏45。mod_dptools
的 limit
动态控制并发量,确保系统稳定性^2。1456^7
场景二
还要考虑主叫号码/被叫前缀可能没限制,只限制/主叫号码/被叫号码这种情况,请重新分析
在 FreeSWITCH 中处理主叫/被叫号码的灵活限制,需结合 拨号计划动态匹配 和 数据库规则设计,以下是具体方案:
route_rules
表中新增限制标识字段,支持独立控制主叫/被叫限制:caller_restricted
:TRUE
时启用主叫号码匹配,FALSE
时忽略主叫限制。callee_restricted
:TRUE
时启用被叫前缀匹配,FALSE
时忽略被叫限制13。mod_easyroute
的查询逻辑,支持条件组合:caller_restricted=FALSE
时,跳过主叫匹配条件。callee_restricted=FALSE
时,跳过被叫前缀匹配14。拨号计划动态适配
在 dialplan.xml
中通过变量控制路由逻辑:
xml
caller_restricted
callee_restricted
匹配逻辑仅主叫限制 TRUE
FALSE
匹配主叫号码,忽略被叫前缀
仅被叫限制 FALSE
TRUE
匹配被叫前缀,忽略主叫号码
双重限制 TRUE
TRUE
同时匹配主叫号码和被叫前缀
无限制 FALSE
FALSE
匹配所有呼叫
caller_restricted
和 callee_restricted
建立复合索引^1。通过上述配置,可实现 主叫/被叫独立限制 的灵活路由控制,满足复杂业务场景需求13^4。
给出缓存到redis的键值对情况
在 Redis 中缓存键值对时,需根据业务场景设计键结构和存储格式。以下是典型配置方案:
层级结构:使用冒号分隔业务标识和参数,例如:
route:caller:主叫号码:被叫前缀
或 route:callee:被叫号码
^3
示例:route:exact:10086:+86137
(主叫 10086,被叫前缀 +86137)
动态组合:根据限制条件动态生成键:
route:caller:${caller_id_number}
route:callee:${destination_number}
route:exact:${caller_id_number}:${destination_number}
^3JSON 字符串:存储路由规则详情,例如:
json
{
“ip”: “10.0.0.5”,
“port”: 5060,
“concurrency_limit”: 500,
“callee_rewrite_rule”: “^\+86\d+$”
}13
哈希表(Hash):使用 HSET
存储独立字段:
bash
HSET route:exact:10086:+86137 ip 10.0.0.5 port 5060 limit 500^1
连接 Redis
r = redis.Redis(host=‘localhost’, port=6379, db=8)
设置键值对(JSON 格式)
route_data = {
“ip”: “10.0.0.5”,
“port”: 5060,
“concurrency_limit”: 500
}
r.set(“route:exact:10086:+86137”, json.dumps(route_data))
获取值
result = r.get(“route:exact:10086:+86137”)
print(json.loads(result)) 输出路由信息^1
MSET
或 Pipeline
减少网络往返次数^2SETEX
设置 TTL 避免内存泄漏^1通过合理设计键结构和存储格式,可高效管理路由规则缓存31。
换一种思路,以ip和端口号为redis的键,主叫号码及被叫前缀和同等规则为一组,以json缓存到redis中,这个方案可行吗
以IP和端口号为Redis键,主叫号码及被叫前缀规则为JSON值的方案可行但需优化。具体分析如下:
方案可行性分析
键设计合理性
${ip}:${port}
作为键(如10.0.0.5:5060
)能唯一标识路由目标服务器14。值存储方式
JSON格式:适合存储结构化规则数据,例如:
json
{
“rules”:
{“caller_prefix”: “10086”, “callee_prefix”: “+86”, “concurrency_limit”: 500},
{“caller_prefix”: “138”, “callee_prefix”: “+1”, “callee_rewrite_rule”: “^\d+$”}
}
通过JSON可灵活扩展字段14。
查询效率
优化建议
复合键设计
结合主叫/被叫前缀生成辅助键,建立双向映射:
python
主键:存储规则集合
redis.set(“10.0.0.5:5060”, json.dumps(rules))
辅助键:支持正向查询
redis.hset(“route:caller:10086”, “callee:+86”, “10.0.0.5:5060”)
通过哈希表实现 O ( 1 ) O(1) O(1)复杂度查询27。
缓存更新策略
SETEX key 3600 value
)15。配置示例
python
import redis
import json
连接Redis
r = redis.Redis(host=‘localhost’, port=6379, db=8)
存储规则
rules =
{“caller_prefix”: “10086”, “callee_prefix”: “+86”, “limit”: 500},
{“caller_prefix”: “138”, “callee_prefix”: “+1”, “rewrite_rule”: “^\d+$”}
r.set(“10.0.0.5:5060”, json.dumps(rules))
建立辅助索引
for rule in rules:
hash_key = f"route:caller:{rule’caller_prefix’}"
r.hset(hash_key, f"callee:{rule’callee_prefix’}", “10.0.0.5:5060”)
适用场景
若需高频正向查询(主叫/被叫→IP端口),建议采用双向键设计或结合关系型数据库78。