-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
eddie murphy
committed
Dec 22, 2024
1 parent
4bec8a1
commit 8c6cb9f
Showing
2 changed files
with
146 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,5 +18,5 @@ post: | |
hugo new posts/2024/$(name)/index.md | ||
serve: | ||
hugo serve | ||
ka: | ||
km: | ||
hugo new kamailio/$(id)/index.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
--- | ||
title: "DMQ模块源码学习笔记" | ||
date: "2024-12-22 18:07:08" | ||
draft: false | ||
type: posts | ||
tags: | ||
- all | ||
categories: | ||
- all | ||
--- | ||
|
||
# 背景 | ||
|
||
多个SIP注册服务器之间,如何同步分机的注册信息呢? | ||
|
||
简单的方案就是使用共享数据库的方式同步注册信息,这个方案实现起来简单,但是分机的注册信息本身就是个需要频繁增删改查的,数据库很可能在大量注册分机的压力下,成为性能的瓶颈。 | ||
|
||
除了数据库之外,OpenSIPS和kamailio分别提供了不同的方案。 | ||
|
||
OpenSIPS提供的方案是使用cluster模块,cluster模块在多个实例之间同步分机的注册信息,注册信息的格式是OpenSIPS自定义的格式。 | ||
Kamailio的方案是DMQ模块, DMQ听起来高大上,放佛是依赖外部的一个服务。 但它其实就是扩展SIP消息,通过SIP消息来广播分机的注册信息。 | ||
|
||
```log | ||
KDMQ sip:[email protected]:5090 SIP/2.0 | ||
Via: SIP/2.0/UDP 192.168.40.15;branch=z9hG4bK55e5.423d95110000 | ||
To: <sip:[email protected]:5090> | ||
From: <sip:[email protected]:5060>;tag=2cdb7a33a7f21abb98fd3a44968e3ffd-5b01 | ||
CSeq: 10 KDMQ | ||
Call-ID: [email protected] | ||
Content-Length: 116 | ||
User-Agent: kamailio (4.3.0 (x86_64/linneaus)) | ||
Max-Forwards: 1 | ||
Content-Type: text/plain | ||
sip:192.168.40.16:5060;status=active | ||
sip:192.168.40.15:5060;status=disabled | ||
sip:192.168.40.17:5060;status=active | ||
``` | ||
|
||
# 源码分析 | ||
|
||
该模块一共暴露了8个参数,其中7个参数都是简单类型,INT和STR,就直接取对应变量的地址就可以了。 | ||
|
||
其中notification_address参数是用来配置集群中其他节点的通信地址的,因为要配置多次,所以需要用一个函数来解析。 | ||
|
||
```c | ||
// dmq.c | ||
static param_export_t params[] = { | ||
{"num_workers", PARAM_INT, &dmq_num_workers}, | ||
{"ping_interval", PARAM_INT, &dmq_ping_interval}, | ||
{"server_address", PARAM_STR, &dmq_server_address}, | ||
{"server_socket", PARAM_STR, &dmq_server_socket}, | ||
{"notification_address", PARAM_STR|PARAM_USE_FUNC, dmq_add_notification_address}, | ||
{"notification_channel", PARAM_STR, &dmq_notification_channel}, | ||
{"multi_notify", PARAM_INT, &dmq_multi_notify}, | ||
{"worker_usleep", PARAM_INT, &dmq_worker_usleep}, | ||
{0, 0, 0} | ||
}; | ||
``` | ||
|
||
这些参数都没有加上static关键词,主要目的为了在dmq模块的其他c文件能使用。 | ||
|
||
```c | ||
// dmq.c | ||
int dmq_num_workers = DEFAULT_NUM_WORKERS; | ||
int dmq_worker_usleep = 0; | ||
str dmq_server_address = {0, 0}; | ||
str dmq_server_socket = {0, 0}; | ||
str dmq_notification_channel = str_init("notification_peer"); | ||
int dmq_multi_notify = 0; | ||
int dmq_ping_interval = 60; | ||
str_list_t *dmq_notification_address_list = NULL; | ||
``` | ||
## dmq_add_notification_address | ||
这个函数是在脚本解析阶段执行,每次配置一个notification_address,它就会被调用一次。 | ||
最终的目的就是组装dmq_notification_address_list这个链表结构,它里面存的就是集群里其他节点的SIP通信地址。 | ||
```c | ||
static int dmq_add_notification_address(modparam_t type, void *val) | ||
{ | ||
str tmp_str; | ||
int total_list = 0; /* not used */ | ||
if(val == NULL) { | ||
LM_ERR("invalid notification address parameter value\n"); | ||
return -1; | ||
} | ||
tmp_str.s = ((str *)val)->s; | ||
tmp_str.len = ((str *)val)->len; | ||
/* | ||
这个参数的格式是个SIP URL | ||
如: | ||
sip:10.0.0.21:5060 | ||
sip:10.0.0.21:5061;transport=tls | ||
所以需要做一次解析,其实也是验证数据的格式是否正确 | ||
dmq_notification_uri 是个static类型的值,可以重复使用 | ||
*/ | ||
if(parse_uri(tmp_str.s, tmp_str.len, &dmq_notification_uri) < 0) { | ||
LM_ERR("could not parse notification address\n"); | ||
return -1; | ||
} | ||
/* initial allocation */ | ||
if(dmq_notification_address_list == NULL) { | ||
/* | ||
初次分配, | ||
申请一块内容,用来存储第一个元素 | ||
*/ | ||
dmq_notification_address_list = pkg_malloc(sizeof(str_list_t)); | ||
// 申请失败 | ||
if(dmq_notification_address_list == NULL) { | ||
PKG_MEM_ERROR; | ||
return -1; | ||
} | ||
// 装载列表的第一项 | ||
dmq_tmp_list = dmq_notification_address_list; | ||
dmq_tmp_list->s = tmp_str; | ||
dmq_tmp_list->next = NULL; | ||
LM_DBG("Created list and added new notification address to the list " | ||
"%.*s\n", | ||
dmq_tmp_list->s.len, dmq_tmp_list->s.s); | ||
} else { | ||
// 思考这里传入的是双层指针,而不是指针,这是为什么? | ||
// 在函数参数中使用指向指针的指针,可以修改指针本身的值 | ||
// append_str_list 就要修改last指针的指向 | ||
// notification_address 每次被设置一次,链表就要增加一项,dmq_tmp_list就要向后移动一位 | ||
dmq_tmp_list = append_str_list( | ||
tmp_str.s, tmp_str.len, &dmq_tmp_list, &total_list); | ||
if(dmq_tmp_list == NULL) { | ||
LM_ERR("could not append to list\n"); | ||
return -1; | ||
} | ||
LM_DBG("added new notification address to the list %.*s\n", | ||
dmq_tmp_list->s.len, dmq_tmp_list->s.s); | ||
} | ||
return 0; | ||
} | ||
``` | ||
|