FreeSwitch中mod_dptools和mod_easyroute两个模块及应用场景
FreeSWITCH 中的 mod_dptools
和 mod_easyroute
是两个功能不同的模块,分别服务于呼叫控制和动态路由场景。以下是详细介绍:
- mod_dptools
功能概述
mod_dptools
(Dialplan Tools)是 FreeSWITCH 最核心的模块之一,提供了丰富的 Application(App) 和工具,用于在拨号计划(Dialplan)中实现呼叫控制逻辑14。主要功能包括:
- 基础呼叫操作:如
answer
(接听)、bridge
(桥接)、hangup
(挂断)等。 - 媒体处理:如
playback
(播放音频)、record
(录音)、set_audio_level
(调节音量)。 - DTMF 交互:通过
read
、capture
等应用捕获用户按键输入^2。 - 资源限制:通过
limit
应用限制并发呼叫数(Capacity)或每秒呼叫数(CPS)^5。 - 虚拟端点:提供
user
端点,简化呼叫字符串配置(如user/1000
映射到 SIP 地址)^1。
应用场景
- IVR 系统:结合
playback
和read
实现语音菜单交互。 - 呼叫中心:通过
bridge
转接呼叫,利用limit
控制并发量。 - 会议系统:管理多方通话的媒体流和权限。
- 资源限制:限制特定 IP 或线路的呼叫频率,防止过载^5。
- mod_easyroute
功能概述
mod_easyroute
是一个轻量级的 动态路由引擎,通过数据库查询实现基于 DID(直接拨入号码)的路由决策69。核心特性包括:
- 数据库驱动:从数据库(如 MySQL、PostgreSQL)中查询路由规则。
- 灵活路由:根据来电号码动态选择目标(如 SIP 地址、网关)。
- 简化配置:替代复杂的拨号计划逻辑,适合多租户或动态路由需求。
应用场景
- 多运营商线路:根据 DID 选择成本最优的运营商网关^9。
- 企业分机路由:将不同 DID 映射到内部用户或部门。
- 动态号码分配:结合外部系统实现按需路由(如临时号码池)。
对比与协作
- 互补性:
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。
两者结合可构建高效、可扩展的 VoIP 系统。
在高并发低延迟毫秒级场景下王者组合
在高并发、低延迟场景下,结合 mod_dptools
和 mod_easyroute
实现动态路由的核心思路是:通过 mod_easyroute
快速查询 MySQL 路由表,再通过 mod_dptools
的 bridge
和 limit
实现高效呼叫控制。以下是具体实现方法:
一、模块协作方法
mod_easyroute
:- 动态查询 MySQL 路由表,根据主叫/被叫号码、时间、地区等条件筛选最优路由。
- 支持主叫号码的完全匹配(
=
)和前缀匹配(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
字段建立复合索引。
- 对
- 缓存路由:
- 使用 Redis 缓存高频路由规则,减少 MySQL 查询次数17。
- 连接池配置:
- 在
mod_db
中启用连接池,避免频繁建立数据库连接。
- 在
- 系统参数调优:
- 调整
ulimit -n
和 UDP 缓冲区大小,提升并发处理能力13。
- 调整
五、典型场景
外呼系统动态路由:
- 主叫号码
10123456789
匹配前缀101
,被叫前缀+86
,当前时间在允许范围内。 mod_easyroute
查询到目标 IP10.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
说明:
- FreeSWITCH 配置结构:
FreeSWITCH 的配置文件默认集中在conf/
目录下,模块的配置文件(如mod_easyroute
)一般存放在conf/autoload_configs/
中^3。 - 模块加载逻辑:
FreeSWITCH 在启动时会自动加载autoload_configs/
中的模块配置,确保mod_easyroute
的路由规则生效。
验证方法:
- 通过命令行查看路径:
bash
进入 FreeSWITCH 配置目录
cd /usr/local/freeswitch/conf/autoload_configs/
检查文件是否存在
ls -l easyroute.conf.xml
如果未找到文件,可能需要手动创建该配置文件并启用模块。
实现动态控制并发
要实现动态并发限制,需结合 mod_dptools
的 limit
应用与 mod_easyroute
的路由查询功能,并通过 MySQL 动态获取限制值。以下是具体步骤和配置示例:
- 修改 MySQL 表结构
在route_rules
表中新增concurrency_limit
字段:
sql
ALTER TABLE route_rules ADD COLUMN concurrency_limit INT DEFAULT 1000;
- 调整
mod_easyroute
查询
更新easyroute.conf.xml
的 SQL 查询,返回concurrency_limit
字段:
xml
SELECT ip, port, callee_rewrite_rule, concurrency_limit
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 ‘${destination_number}’ LIKE CONCAT(callee_prefix, ‘%’)
AND TIME(NOW()) BETWEEN outbound_time_start AND outbound_time_end
ORDER BY priority ASC
LIMIT 1
-
动态设置并发限制
在拨号计划中,通过set
应用将concurrency_limit
保存为通道变量,并在limit
中引用:
xml
关键点说明
- 动态变量传递:
easyroute_result(concurrency_limit)
从路由查询结果中提取concurrency_limit
字段^2。expr()
函数用于处理空值,若数据库未返回限制值,则默认使用 1000^2。
- 高并发优化:
- 结合文献1的建议,确保系统已调整文件描述符(
ulimit -n 65535
)和 UDP 缓冲区^1。
- 结合文献1的建议,确保系统已调整文件描述符(
- 路由匹配逻辑:
- 主叫号码支持完全匹配和前缀匹配,被叫号码支持前缀规则^3。
验证方法
- 数据库写入测试值:
sql
INSERT INTO route_rules (ip, port, caller_pattern, callee_prefix, concurrency_limit)
VALUES (‘10.0.0.5’, 5060, ‘101’, ‘+86’, 500); - 查看日志:
检查 FreeSWITCH 日志,确认dynamic_limit
是否正确加载:
bash
freeswitch -nc -nonat -rp
通过上述配置,可实现 基于路由规则的动态并发控制,满足高并发场景的灵活需求23。
场景一
如果将mysql数据表缓存到redis的8号库,动态路由先查询redis,不存在,再查询mysql,依旧不存在,返回404报错,问题一:缓存到redis是键值对该如何配置?问题二:给出添加redis后的配置文件代码
问题一:Redis 键值对配置方法
在 Redis 8 号库中,键值对设计需包含路由规则的核心字段,建议采用以下格式:
- 键(Key):
route:${caller_id_number}:${destination_number}
其中${caller_id_number}
为主叫号码,${destination_number}
为被叫号码。若需支持前缀匹配,可扩展为:
plaintext
route:exact: c a l l e r i d n u m b e r : {caller_id_number}: calleridnumber:{callee_prefix} 完全匹配
route:prefix: c a l l e r p r e f i x : {caller_prefix}: callerprefix:{callee_prefix} 前缀匹配 - 值(Value):存储 JSON 格式的路由信息,包含
ip
、port
、callee_rewrite_rule
等字段,例如:
json
{
“ip”: “10.0.0.5”,
“port”: 5060,
“callee_rewrite_rule”: “^\d+$”,
“concurrency_limit”: 500
}
问题二:添加 Redis 后的配置文件代码
-
Redis 连接配置(
vars.xml
)
xml
-
拨号计划(
dialplan.xml
)
xml
- Lua 脚本示例(
redis_query.lua
)
lua
local caller = argv1
local callee = argv2
local redis = require “redis”
local client = redis.connect(os.getenv(“redis_host”), os.getenv(“redis_port”))
client:auth(os.getenv(“redis_password”))
client:select(8) – 选择 8 号库
– 尝试完全匹配
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
)
lua
local caller = argv1
local callee = argv2
local route_data = argv3
local redis = require “redis”
local client = redis.connect(os.getenv(“redis_host”), os.getenv(“redis_port”))
client:auth(os.getenv(“redis_password”))
client:select(8)
– 根据匹配类型设置键(示例仅存储完全匹配)
local key = “route:exact:” … caller … “:” … callee
client:setex(key, 3600, route_data) – 缓存 1 小时
关键点说明
- 键设计:支持完全匹配和前缀匹配,通过不同键前缀区分逻辑67。
- 性能优化:使用 Lua 脚本减少网络往返,结合
setex
设置过期时间避免内存泄漏45。 - 高并发适配:通过
mod_dptools
的limit
动态控制并发量,确保系统稳定性^2。
1456^7
场景二
还要考虑主叫号码/被叫前缀可能没限制,只限制/主叫号码/被叫号码这种情况,请重新分析
在 FreeSWITCH 中处理主叫/被叫号码的灵活限制,需结合 拨号计划动态匹配 和 数据库规则设计,以下是具体方案:
- 数据库表结构设计
在route_rules
表中新增限制标识字段,支持独立控制主叫/被叫限制:
sql
ALTER TABLE route_rules
ADD COLUMN caller_restricted BOOLEAN DEFAULT FALSE,
ADD COLUMN callee_restricted BOOLEAN DEFAULT FALSE;
caller_restricted
:TRUE
时启用主叫号码匹配,FALSE
时忽略主叫限制。callee_restricted
:TRUE
时启用被叫前缀匹配,FALSE
时忽略被叫限制13。
- 动态 SQL 查询优化
修改mod_easyroute
的查询逻辑,支持条件组合:
xml
SELECT ip, port, concurrency_limit
FROM route_rules
WHERE
(caller_restricted = FALSE
OR (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
(callee_restricted = FALSE
OR ‘${destination_number}’ LIKE CONCAT(callee_prefix, ‘%’))
AND TIME(NOW()) BETWEEN outbound_time_start AND outbound_time_end
ORDER BY priority ASC
LIMIT 1
逻辑说明:
- 当
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。 - 缓存策略:结合 Redis 缓存高频路由规则,减少数据库查询压力13。
通过上述配置,可实现 主叫/被叫独立限制 的灵活路由控制,满足复杂业务场景需求13^4。
给出缓存到redis的键值对情况
在 Redis 中缓存键值对时,需根据业务场景设计键结构和存储格式。以下是典型配置方案:
- 键(Key)设计
-
层级结构:使用冒号分隔业务标识和参数,例如:
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}
^3
- 仅主叫限制:
- 值(Value)格式
-
JSON 字符串:存储路由规则详情,例如:
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
- 操作示例
python
import redis
连接 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
减少网络往返次数^2 - 过期时间:通过
SETEX
设置 TTL 避免内存泄漏^1
通过合理设计键结构和存储格式,可高效管理路由规则缓存31。
换一种思路,以ip和端口号为redis的键,主叫号码及被叫前缀和同等规则为一组,以json缓存到redis中,这个方案可行吗
以IP和端口号为Redis键,主叫号码及被叫前缀规则为JSON值的方案可行但需优化。具体分析如下:
方案可行性分析
-
键设计合理性
- 使用
${ip}:${port}
作为键(如10.0.0.5:5060
)能唯一标识路由目标服务器14。 - 但需注意:同一IP端口可能对应多组主叫/被叫规则,此时需在值中存储规则集合而非单条规则。
- 使用
-
值存储方式
-
JSON格式:适合存储结构化规则数据,例如:
json
{
“rules”:
{“caller_prefix”: “10086”, “callee_prefix”: “+86”, “concurrency_limit”: 500},
{“caller_prefix”: “138”, “callee_prefix”: “+1”, “callee_rewrite_rule”: “^\d+$”}}
通过JSON可灵活扩展字段14。
-
-
查询效率
- 优势:直接通过IP端口定位路由目标,适合反向查询场景(已知目标服务器时获取规则)^4。
- 劣势:若需根据主叫/被叫前缀正向查询IP端口,需遍历所有键,时间复杂度为 O ( n ) O(n) O(n)27。
优化建议
-
复合键设计
结合主叫/被叫前缀生成辅助键,建立双向映射:
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。 -
缓存更新策略
- 主动更新:规则变更时同步更新Redis^2。
- 过期时间:对低频规则设置TTL(如
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端口,快速获取关联规则。
- 低频规则管理:结合TTL避免内存浪费25。
若需高频正向查询(主叫/被叫→IP端口),建议采用双向键设计或结合关系型数据库78。
在FreeSWITCH中,mod_dptools
和 mod_easyroute
是两个功能不同的模块,分别用于呼叫流程控制和路由管理。以下是它们的详细介绍和应用场景分析:
1. mod_dptools:核心工具模块
mod_dptools
是FreeSWITCH的核心模块之一,默认自动加载。它提供了大量Dialplan应用(Application)和API命令,用于控制呼叫流程、操作通道变量、播放媒体文件等基础功能。
主要功能及常用指令
-
呼叫控制:
answer
:应答呼叫(触发媒体协商)。hangup
:挂断呼叫,可指定挂机原因(如hangup NORMAL_CLEARING
)。transfer
:将呼叫转移到其他分机或拨号方案上下文。
-
媒体操作:
playback
:播放音频文件(如playback /path/to/file.wav
)。record_session
:录制通话音频(支持多种格式)。speak
:使用TTS引擎合成语音。
-
变量操作:
set
:设置通道变量(如set my_var=123
)。export
:将变量传递到后续的桥接通道。
-
逻辑控制:
bridge
:桥接两个终端(如bridge user/1001
)。park
:将通话暂存到停车场,等待后续处理。
-
信息获取:
info
:打印当前通道的详细信息(调试用途)。
应用场景
- IVR系统:通过
playback
和read
实现交互式语音菜单。 - 呼叫转接:使用
transfer
或bridge
将通话转接到其他分机。 - 录音功能:
record_session
用于合规性录音或语音信箱。 - 动态路由:结合
set
和条件判断,根据变量调整路由逻辑。 - 调试工具:
info
或log
帮助实时查看通道状态。
示例 Dialplan 配置:
<extension name="example"><condition field="destination_number" expression="^1234$"><action application="answer"/><action application="playback" data="/sounds/welcome.wav"/><action application="bridge" data="user/1001"/></condition>
</extension>
2. mod_easyroute:动态路由模块
mod_easyroute
并非FreeSWITCH的官方模块,而是一个第三方或社区贡献模块(需确认具体来源),其设计目的是简化复杂路由规则的配置,通常通过外部数据源(如数据库、API)实现动态路由决策。
核心功能
-
动态路由规则:
- 基于数据库查询、HTTP API响应或脚本结果动态选择路由目标。
- 支持按主叫号码、被叫号码、时间、地理位置等条件路由。
-
集成外部系统:
- 与CRM或业务系统对接,实现客户专属路由(如VIP客户直连专属坐席)。
-
负载均衡:
- 根据坐席忙闲状态或技能组分配呼叫。
应用场景
- 多租户呼叫中心:根据租户ID从数据库加载不同路由策略。
- 智能路由:基于时间规则路由到夜间服务或节假日语音提示。
- 故障转移:主线路繁忙时自动切换到备用网关。
- 全球化部署:根据呼叫来源国家选择本地网关降低成本。
示例配置思路(假设模块存在):
<extension name="dynamic_route"><condition field="destination_number" expression="^.+$"><action application="easyroute" data="mysql://user:pass@host/db?query=SELECT route FROM rules WHERE dst=${dst_number}"/></condition>
</extension>
模块对比与协作
特性 | mod_dptools | mod_easyroute |
---|---|---|
定位 | 基础工具集 | 高级路由扩展 |
配置方式 | 内联Dialplan XML | 外部数据源(DB/API) |
灵活性 | 依赖硬编码逻辑 | 动态实时更新规则 |
适用场景 | 简单、静态路由 | 复杂、业务耦合路由 |
协作示例:
通过mod_easyroute
从数据库获取目标号码,再使用mod_dptools
的bridge
发起呼叫,结合set
传递变量记录路由信息。
注意事项
-
mod_easyroute的可用性:
需确认模块是否存在或是否更名(如社区模块可能命名为mod_lcr
、mod_http_routing
等)。若无此模块,可通过以下替代方案实现类似功能:- mod_curl:通过HTTP API获取路由信息。
- mod_lua:编写Lua脚本实现动态逻辑。
- mod_v8:使用JavaScript定义路由规则。
-
性能考量:
频繁查询外部数据可能增加延迟,建议缓存路由结果或使用内存数据库(如Redis)。
总结
- mod_dptools 是FreeSWITCH的“瑞士军刀”,用于实现基础呼叫控制功能。
- mod_easyroute(或类似模块)适合需要业务逻辑深度集成的动态路由场景,需依赖外部数据源增强灵活性。
开发者可根据需求选择原生工具或扩展模块,或通过脚本语言(Lua/JavaScript)实现自定义路由逻辑。
在高并发,低延迟,毫秒级响应的需求下,给出这两个模块同时使用的方法,场景及配置文件的示例代码,假设路由信息存储在mysql数据表中,表结构有ip,端口号,主叫号码【完全匹配及前缀匹配动态选择】,被叫前缀,被叫前缀还写规则,外呼时间,外呼地区等基础信息,如果存在多个相同ip及端口号的不同主叫/被叫,会在数据表中存储多条记录。
高并发低延迟场景下 mod_dptools
与 mod_easyroute
协同方案
在高并发、低延迟(毫秒级响应)需求下,需通过 动态路由缓存、异步查询 和 高效SQL设计 优化性能。以下是实现方法及配置示例。
1. 数据表设计(MySQL)
CREATE TABLE route_rules (id INT AUTO_INCREMENT PRIMARY KEY,caller_match_type ENUM('exact', 'prefix') NOT NULL, -- 主叫匹配类型:完全匹配或前缀caller_pattern VARCHAR(32) NOT NULL, -- 主叫号码规则callee_prefix VARCHAR(16) NOT NULL, -- 被叫号码前缀ip VARCHAR(15) NOT NULL, -- 目标IPport INT NOT NULL, -- 目标端口priority INT DEFAULT 10, -- 优先级(数值越小优先级越高)region VARCHAR(32), -- 外呼地区(如CN/US)valid_time TIME, -- 外呼有效时间段(如09:00-18:00)created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 创建索引加速查询
CREATE INDEX idx_callee_prefix ON route_rules(callee_prefix);
CREATE INDEX idx_caller ON route_rules(caller_match_type, caller_pattern);
CREATE INDEX idx_region_time ON route_rules(region, valid_time);
2. 配置 mod_easyroute
动态路由
假设 mod_easyroute
支持从MySQL动态查询路由(若需替代方案,可用 mod_lua
或 mod_curl
)。
模块配置(easyroute.conf.xml
)
<configuration name="easyroute.conf"><routes><route name="mysql_route" enabled="true"driver="mysql"host="localhost"port="3306"database="freeswitch"user="fs_user"password="password"query="SELECT ip, port, priority FROM route_rules WHERE (caller_match_type = 'exact' AND caller_pattern = '${caller_id_number}') OR (caller_match_type = 'prefix' AND '${caller_id_number}' LIKE CONCAT(caller_pattern, '%'))AND callee_prefix = LEFT('${destination_number}', LENGTH(callee_prefix))AND region = '${region}' AND (valid_time IS NULL OR CURTIME() BETWEEN valid_time_start AND valid_time_end)ORDER BY priority ASC LIMIT 1"/></routes>
</configuration>
3. 高并发优化策略
3.1 路由缓存(Redis)
使用Redis缓存查询结果,减少数据库压力:
- 键设计:
route:${caller}:${callee}:${region}:${time}
。 - 过期时间:5秒(平衡实时性与缓存有效性)。
3.2 异步查询
通过FreeSWITCH的 异步模式 避免阻塞呼叫线程:
<action application="easyroute_async" data="mysql_route"/>
<action application="park"/>
3.3 连接池优化
- MySQL连接池:配置
mod_easyroute
使用至少20个连接。 - 查询超时:设置超时时间100ms,超时后触发备用路由。
4. 拨号计划(dialplan.xml
)
<context name="dynamic_routing"><extension name="dynamic_route"><condition field="destination_number" expression="^(\d+)$"><!-- 设置必要变量 --><action application="set" data="region=CN"/> <!-- 从IP或数据库获取实际地区 --><!-- 异步查询路由(非阻塞) --><action application="easyroute_async" data="mysql_route"/><action application="park"/><!-- 结果处理(从通道变量读取路由) --><action application="bridge" data="${if( ${easyroute_status} == success ? sofia/gateway/${easyroute_ip}:${easyroute_port}/${destination_number} : error/NO_ROUTE)}"/><!-- 失败处理 --><action application="log" data="WARNING Route failed: ${easyroute_error}"/><action application="hangup"/></condition></extension>
</context>
5. 场景示例
场景:国际外呼动态选路
- 主叫:
+8613812345678
- 被叫:
+447712345678
- 流程:
- 根据被叫前缀
+44
匹配英国路由。 - 主叫完全匹配
+8613812345678
或前缀匹配+86
。 - 根据当前时间(如14:00)筛选有效路由。
- 按优先级选择IP:Port组合(如
gw_uk1:5060
)。
- 根据被叫前缀
6. 性能保障措施
- 数据库优化:
- 使用内存表(
MEMORY
引擎)存储热点路由。 - 定期清理过期规则。
- 使用内存表(
- FreeSWITCH调优:
<!-- settings.conf.xml --> <param name="max-sessions" value="10000"/> <param name="session-per-sec" value="1000"/> <param name="enable-async-park" value="true"/>
- 监控:
- 实时监控MySQL查询延迟(Prometheus + Grafana)。
- 告警规则:路由查询超时率 > 1% 或错误率 > 0.5%。
7. 容灾与降级方案
- 本地缓存文件:当MySQL不可用时,加载静态路由规则。
<route name="fallback_route" driver="csv"file="/etc/freeswitch/fallback_routes.csv"/>
- 默认网关:无路由时转发至默认网关(如SIP中继)。
总结
通过 mod_easyroute
实现动态路由决策,结合 mod_dptools
完成最终呼叫控制,配合缓存、异步查询和数据库优化,可满足高并发低延迟需求。关键点在于减少实时数据库访问、优先内存操作,并通过合理索引提升SQL效率。
相关文章:
FreeSwitch中mod_dptools和mod_easyroute两个模块及应用场景
FreeSWITCH 中的 mod_dptools 和 mod_easyroute 是两个功能不同的模块,分别服务于呼叫控制和动态路由场景。以下是详细介绍: mod_dptools 功能概述 mod_dptools(Dialplan Tools)是 FreeSWITCH 最核心的模块之一,提供了…...
【Java】泛型与集合篇 —— Set 接口
目录 Set 接口及实现类HashSet 类特点内部实现构造方法LinkedHashSet 类基本概念特点构造方法常用方法适用场景用 Set 对象实现集合运算TreeSet 类特性构造方法常用方法注意事项对象顺序自然排序定制排序注意事项Set 接口及实现类 HashSet 类 HashSet 是 Java 集合框架中 Set…...
DeepSeek 助力 Vue 开发:打造丝滑的右键菜单(RightClickMenu)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
数据结构与算法面试专题——堆排序
完全二叉树 完全二叉树中如果每棵子树的最大值都在顶部就是大根堆 完全二叉树中如果每棵子树的最小值都在顶部就是小根堆 设计目标:完全二叉树的设计目标是高效地利用存储空间,同时便于进行层次遍历和数组存储。它的结构使得每个节点的子节点都可以通过简…...
【Mysql】索引
【Mysql】索引 一、索引的简介二、索引结构2.1 Hash2.2 二叉搜索树2.3 B树2.4 B树 三、索引分类3.1 主键索引3.2 普通索引3.3 唯一索引3.4 全文索引3.5 聚集索引3.6 非聚集索引3.7 索引覆盖 四、使用索引4.1 自动创建索引4.2 手动创建索引4.2.1 主键索引4.2.2 唯一索引4.2.3 普…...
qt的下载安装详细介绍
下载 我们可以在国内的镜像网站上下载qt安装包,按需下载: 我的需求是在windows上运行,x64的qt5.15.2,所以我下载的是qt-unified-windows-x64-4.6.0-online.exe 下载完成之后,我们来到存放该exe文件的目录,打开命令窗…...
Eclipse插件开发六:使用Web前端技术开发AI助手页面
之前的过程中,我们都不怎么熟悉Eclipse的哪些API,样式也没发怎么去修改,现在我们要修改为用html的方式来编写. 准备一个AI助手聊天页面的html.css,js代码 效果如下所示。 1.快速demo 1.1.准备前端代码 确保准备的前端代码可以在浏览器正常…...
Jackson使用
Jackson 是一个功能强大的 JSON 处理库,除了基本的序列化和反序列化功能外,它还提供了许多其他功能,以满足不同的需求。以下是一些常用的高级功能: 0.普通序列化反序列化 序列化 import com.fasterxml.jackson.databind.ObjectM…...
Maven——Maven开发经验总结(1)
摘要 本文总结了 Maven 开发中的多个关键经验,包括如何根据版本号决定推送到 releases 或 snapshots 仓库,如何在构建过程中跳过测试,父项目如何控制子项目依赖版本,父项目依赖是否能传递到子项目,如何跳过 Maven dep…...
MyBatis-Plus之通用枚举
MyBatis-Plus之通用枚举 前言 MyBatis-Plus中提供了通用枚举,简单来说就是将数据库中的某一字段的代替的含义转换成真实的含义将数据展示给用户,用户在存储时也会将真实值转换成代替的数字存入到数据库中。举个例子:用户性别在数据库中存储…...
React通用登录/注销功能实现方案(基于shadcn/ui)
React通用登录/注销功能实现方案(基于shadcn/ui) 一、功能需求分析二、通用功能封装1. 通用登录表单组件2. 认证Hook封装 三、功能使用示例1. 登录页面实现2. 用户菜单实现 四、路由保护实现五、方案优势 一、功能需求分析 需要实现以下核心功能&#x…...
AI工具篇:利用DeepSeek+Kimi 辅助生成综述汇报PPT
随着科研和学术报告需求的增加,如何高效地准备一份结构清晰、内容充实的PPT已成为许多研究者的挑战。 传统的PPT制作过程繁琐,需要大量文献收集、数据分析和设计工作,而AI工具能够帮助提升效率,减少重复劳动。 本文将介绍如何使用…...
审计级别未启用扩展模式导致查询 DBA_AUDIT_TRAIL 时 SQL_TEXT 列为空
如果查询 DBA_AUDIT_TRAIL 时发现 SQL_TEXT 列为空,但其他字段(如 OS_USERNAME、USERNAME、TIMESTAMP 等)有数据,可能是由于以下原因之一。以下是可能的原因及解决方法: 1. 审计级别未启用扩展模式 默认情况下&#x…...
HTTP 和RESTful API 基础,答疑
一文搞懂RESTful API - bigsai - 博客园 1. API 路径 开头必须 /,表示绝对路径,不支持 . 或 ..(相对路径)。API 结尾 / 通常不需要,但部分框架会自动处理 / → 无 /。 ✅ 推荐 GET /api/v1/products # 资源集合…...
手写简易RPC(实践版)
首先了解rpc rpc-远程过程调用,openFeign,Dubbo都可以算作rpc,以微服务来具体说明,就是在本地不需要去发送请求,通过rpc框架,像调用本地方法一样调用其他服务的方法,本质上还是要经过网络&…...
Day6 25/2/19 WED
【一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解(马士兵)】https://www.bilibili.com/video/BV13g41157hK?p4&v…...
【DL】浅谈深度学习中的知识蒸馏 | 输出层知识蒸馏
目录 一 核心概念与背景 二 输出层知识蒸馏 1 教师模型训练 2 软标签生成(Soft Targets) 3 学生模型训练 三 扩展 1 有效性分析 2 关键影响因素 3 变体 一 核心概念与背景 知识蒸馏(Knowledge Distillation, KD)是一种模…...
机器学习PCA和LDA
主成分分析(PCA, Principal Component Analysis)和线性判别分析(LDA, Linear Discriminant Analysis)是两种常用的降维方法,它们虽然都用于数据降维,但核心思想和应用场景不同。 PCA(主成分分析…...
tcp协议连接,和传输数据
1、连接 这个是通用的 2、传送数据 当连接建立后,客户端和服务器都可以主动发送数据,分别如下 1》客户端先发送数据 这里是单向的,服务器没有对客户端的数据内容进行应答,只是单纯的对报文应答ack 2》服务器先发送数据...
【异常错误】pycharm debug view变量的时候显示不全,中间会以...显示
异常问题: 这个是在新版的pycharm中出现的,出现的问题,点击view后不全部显示,而是以...折叠显示 在setting中这么设置一下就好了: 解决办法: https://youtrack.jetbrains.com/issue/PY-75568/Large-stri…...
Java基础——代理模式
代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。 一、代理模式的主要作用 控制访问:通…...
解锁机器学习核心算法|主成分分析(PCA):降维的魔法棒
一、引言 在机器学习的庞大算法体系中,有十种算法被广泛认为是最具代表性和实用性的,它们犹如机器学习领域的 “十大神器”,各自发挥着独特的作用。这十大算法包括线性回归、逻辑回归、决策树、随机森林、K - 近邻算法、K - 平均算法、支持向…...
sql注入漏洞
目录 一、SQL注入概述 例子背景 正常情况下的查询 SQL注入攻击 利用优先级进行攻击 二、解决SQL注入 使用PreparedStatement接口 步骤和方法 1. 创建PreparedStatement对象 2. 向占位符传入值 3. 执行SQL语句 示例 总结 SQL 注入是一种常见的网络攻击手段。通俗来…...
spring微服务+dubbo框架,某一服务启动时提示多个bean存在
在java的springboot项目中使用DubboService的注解的实现类中,在引用本模块的类时,使用的DubboRefrence注解,在启动项目时报错,提示该类需要以一个bean对象,但是存在了两个,把DubboRefrence的注解改成Autowi…...
React useState 和 useEffect 使用坑点注意总结
React Hooks 使用注意事项 Area: Hooks Date: February 10, 2025 Important: 🌟🌟🌟 React Hooks 注意事项 要点: useState 的初始化值 只在第一次渲染时计算,并且这个值不会随着组件重新渲染而更新。useEffect 可…...
使用rknn进行yolo11-pose部署
文章目录 概要生成ONNX生成RKNN实测效果概要 使用 RKNN 进行 YOLOv11 Pose 部署的必要性在于,RKNN 能将 YOLOv11 Pose 模型转化为适合 Rockchip 硬件平台(如 RV1109、RV1126)执行的格式,充分利用其 AI 加速功能,显著提高推理速度和效率。此外,RKNN 提供模型优化(如量化…...
开源语音克隆项目 OpenVoice V2 本地部署
#本机环境 WIN11 I5 GPU 4060ti 16G 内存 32G #开始 git clone https://github.com/myshell-ai/OpenVoice.git conda create -n opvenv python3.9 -y conda activate opvenv pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/…...
YOLOv12从入门到入土(含结构图)
论文链接:https://arxiv.org/abs/2502.12524 代码链接:https://github.com/sunsmarterjie/yolov12 文章摘要: 长期以来,增强YOLO框架的网络架构一直至关重要,但一直专注于基于cnn的改进,尽管注意力机制在建…...
8.【线性代数】——求解Ax=b
八 求解Axb 1. 解Axb求特解 x p x_p xp求特解 x n x_n xn所有解 2. Axb什么时候有解3. A m ∗ n A_{m * n} Am∗n不同秩的Axb解分析3.1 列满秩 rn<m3.2 行满秩 rm<n3.3 rmn3.4 r<m 且 r < n3.5 综述 1. 解Axb 求解 { x 1 2 x 2 2 x 3 2 x 4 b 1 2 x 1…...
【Quest开发】全身跟踪
软件:Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件:Meta Quest3 最终效果:能像meta的操作室沉浸场景一样根据头盔移动来推断用户姿势,实现走路、蹲下、手势匹配等功能 需要借助UnityMovement这个包 GitHub …...
Spring Boot 示例项目:从零开始构建 Web 应用
一、项目概述 本文档将指导您通过一个示例项目,了解如何使用 Spring Boot 框架构建一个简单的 Web 应用程序。该项目涵盖了从数据模型定义到控制器、服务层以及数据访问层的完整开发流程,帮助您快速掌握 Spring Boot 的基本使用方法。 二、项目结构 1. 项目模块 本示例项…...
Windows 启动 SSH 服务报错 1067
Windows 启动 SSH 服务报错 1067 一、原本安装的 Windows 自带的 SSH 服务 按 Windows 键 -> 设置 -> 系统 -> 可选功能 在 添加的功能 查看是否安装了 OpenSSH 服务 一开始 执行 net start sshd 是可以正常启动的 并且其他机器也可以通过 ssh 访问 这个电脑 但是有…...
【AI战略思考15】我对做自媒体视频博主的初步探索和一些思考
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】【AI应用】 导言 因为自己找工作可能没那么快和顺利,事实是比我之前想象的要难很多,所以这几天探索了下自己能否尝试做自媒体或者视频博主来尝试赚点钱,如果做…...
零基础学QT、C++(一)安装QT
目录 如何快速学习QT、C呢? 一、编译器、项目构建工具 1、编译器(介绍2款) 2、项目构建工具 二、安装QT 1、下载QT安装包 2、运行安装包 3、运行QT creator 4、导入开源项目 总结 闲谈 如何快速学习QT、C呢? 那就是项目驱动法&…...
Word接入DeepSeek(API的作用)
1.打开”Word”,点击“文件”。 2.点击“选项”。 3.点击“信任中心”——“信任中心设置”。 4. 勾选”启用所有宏“,点击”确定“。 5.点击“自定义功能区”,勾选上“开发工具”,点击“确定”。 6.返回“文件——开发工具“下的…...
ok113i——交叉编译音视频动态库
提示:buildroot支持ffmpeg和SDL,但博主的ffmpeg是按下面方法编译通过,SDL使用buildroot直接编译也通过; 1. 下载ffmpeg源码 下载链接:https://github.com/FFmpeg/FFmpeg/tags 根据版本需要自行下载压缩包,…...
CSS中五种定位方式(position)对比分析
在 CSS 中,定位方式(position)决定了元素如何相对于其参照物进行定位,同时影响文档流的布局。以下是五种定位方式的对比、参照物说明及代码示例: 1. position: static(默认定位) 参照物&#x…...
Jest单元测试
由于格式和图片解析问题,可前往 阅读原文 前端自动化测试在提高代码质量、减少错误、提高团队协作和加速交付流程方面发挥着重要作用。它是现代软件开发中不可或缺的一部分,可以帮助开发团队构建可靠、高质量的应用程序 单元测试(Unit Testi…...
Lineageos 22.1(Android 15) 开机向导制作
一、前言 开机向导原理其实就是将特定的category的Activity加入ComponentResolver,如下 <category android:name"android.intent.category.SETUP_WIZARD"/>然后我们开机启动的时候,FallbackHome结束,然后启动Launcher的时候…...
前沿计组知识入门
这份PDF文件是一份关于计算机体系结构的讲义,涵盖了从基础概念到高级主题的多个方面。以下是详细的总结和分析: 计算机体系结构概述 定义:计算机体系结构是计算机系统的理论组成部分,根据其属性和功能进行划分,包括计…...
力扣 最长递增子序列
动态规划,二分查找。 题目 由题,从数组中找一个最长子序列,不难想到,当这个子序列递增子序列的数越接近时是越容易拉长的。从dp上看,当遍历到这个数,会从前面的dp选一个最大的数加上当前数,注意…...
在项目中调用本地Deepseek(接入本地Deepseek)
前言 之前发表的文章已经讲了如何本地部署Deepseek模型,并且如何给Deepseek模型投喂数据、搭建本地知识库,但大部分人不知道怎么应用,让自己的项目接入AI模型。 文末有彩蛋哦!!! 要接入本地部署的deepsee…...
已解决IDEA无法输入中文问题(亲测有效)
前言 在使用IDEA的时候,比如我们想写个注释,可能不经意间,输入法就无法输入中文了,但是在其他地方打字,输入法仍然能够正常工作。这是什么原因呢,这篇文章带你解决这个问题! 快捷键 如果你的I…...
Java 语法新特性(Records、Pattern Matching、Sealed Classes)深度解析(11/17/21)✨
一、Records(Java 16) 📝 核心价值:简化不可变数据载体的定义 // 传统POJO vs Record public record User(String name, int age) {} // 自动生成:构造方法/equals()/hashCode()/toString() User user new User(&qu…...
书评与笔记:《如何有效报告Bug》
文章目录 书评笔记核心原则1. 首要目标:让程序员亲眼看到问题2. 次要目标:详细描述问题3. 保持冷静,避免误操作4. 提供额外信息5. 清晰、准确地表达 实用建议不要自作聪明地诊断问题类比:看医生时的症状描述程序员的心理 总结 原文…...
Node.js 中的 fs 模块详解
fs(File System)模块是 Node.js 的核心模块之一,用于处理文件系统的操作,包括文件的读取、写入、删除、重命名等。它提供了同步和异步两种操作方式,适用于不同的场景。 1. 前置知识 1.1 文件系统 文件系统是操作系统…...
【深度学习】如何一步步实现SGD随机梯度下降算法
如何一步步实现SGD随机梯度下降算法 文章目录 如何一步步实现SGD随机梯度下降算法SGD随机梯度下降算法的作用MNIST_SAMPLE数据集SGD算法的七大步骤Step1. 初始化模型参数Step2. 计算预测值predictionsStep3. 计算损失lossStep4. 计算梯度gradientsStep5. 更新模型参数Step6. 重…...
Android Hal AIDL 简介 (一)
Android 接口定义语言 (AIDL) 是一款可供用户用来抽象化 IPC 的工具。 以在 .aidl 文件中指定的接口为例,各种构建系统都会使用 aidl 二进制文件构造 C++ 或 Java 绑定,以便跨进程使用该接口(无论其运行时环境或位数如何)。 AIDL 可以在 Android 中的任何进程之间使用:在…...
【数据分析】2.数据分析业务全流程
业务流程方法论:3阶段6步骤 一、课程核心内容结构 1. 方法论概述 目标:系统性地解决商业中的关键问题框架:分为三个阶段,每个阶段包含两个步骤适用场景:适用于数据分析师、业务经理等需要通过数据分析支持决策的从业…...
如何使用Spark SQL进行复杂的数据查询和分析
使用Spark SQL进行复杂的数据查询和分析是一个涉及多个步骤和技术的过程。以下是如何使用Spark SQL进行复杂数据查询和分析的详细指南: 一、准备阶段 环境搭建: 确保已经安装并配置好了Apache Spark环境。准备好数据源,可以是CSV文件、JSON…...