1. 概述

kamailio SIP消息处理进程主要分为三类。

类别功能数量
UDP worker进程处理以UDP传输的SIP消息port * children
TCP main进程负责tcp链接管理,生命周期维护,tcp work的消息分发1
TCP worker进程处理以TCP传输的SIP消息children

以下面的配置为例:

则SIP消息处理进程数量为:4 * 2 + 1 + 4 = 13个

children=4
listen=udp:127.0.0.1:5060
listen=udp:127.0.0.1:5080
listen=tcp:127.0.0.1:5061

在生成环境,建议children设置为CPU核数, 这样能最大化利用多核能力,并且避免太多的进程切换。

2. UDP Socket进程处理分析

UDP Socket处理主要分为三个步骤

  1. UDP端口信息收集
  2. 端口绑定
  3. 进程fork

2.1. UDP端口信息收集


2.2. 端口绑定


2.3. 进程fork阶段


2.4. UDP read 阶段

udp recvfrom() 都是读到一个静态的buffer区域中,这个静态buffer会一直循环利用,而不是每次都申请。


2.5. UDP 消息进程间负载均衡阶段

  1. 套接字共享:所有进程共享一个套接字文件描述符
  2. 竞争接收:一组相同端口的worker都从相同的套接字上等待接收消息,但是只有一个能收到消息
  3. 内核级调度: 当UDP数据包到达时
    1. 将数据包放到套接字接收缓冲区
    2. 唤醒一个正在等待的进程
    3. 被唤醒的集成调用recvfrom()获取数据

内核的调度策略,无法预测,可能INVITE在woker1上,ACK在woker2上,BYE在woker3上,CANCEL在woker4上。 在大量消息的情况下,只能做到大致均衡。

这种调度优缺点都非常明显

  • 优点
    • 高并发,充分利用多核CPU
    • 高效率,无需应用层调度
    • 低延迟,消息直接由进程处理
  • 缺点
    • 惊群效应,多个进程被唤醒,只有一个能读到消息
    • 负载不均衡,某些进程可能更活跃
    • 消息顺序,无法保证消息顺序。例如可能先处理了CANCEL, 再处理了INVITE
    • 编程复杂,始终考虑多进程的边界,稍不注意,日志打印都会引起coredump

3. TCP消息处理分析

3.1. 消息处理模型

TCP消息的处理阶段和UDP处理的类似,唯一的区别是, TCP woker进程的数量和端口无关,而只和children参数有关。

这里主要讲一下tcp_poll_method

kamailio在三种pool模型上层做了封装.

维度selectepollkqueue
平台通用LinuxBSD
可监控数~101410W+10W+
复杂度O(n)O(1)O(1)
内存拷贝每次仅注册仅注册
适合小规模高并发高并发

3.2. 负载均衡策略

  • 应用级分配
    • TCP链接由TCP Main进程负责分配
    • accept新的TCP/TLS链接
    • 维护TCP链接的生命周期

3.3. 调用链分析

flowchart LR
    tcp_main:handle_io --> tcp_main:handle_new_connect
    tcp_main:handle_io --> tcp_main:handle_tcpconn_ev
	tcp_main:handle_new_connect --> tcp_main:send2child
	tcp_main:handle_tcpconn_ev --> tcp_main:send2child
	tcp_main:send2child --> tcp_main:handle_tcp_child
	tcp_main:send2child --> tcp_main:handle_ser_child
	tcp_main:handle_io --> tcp_main:handle_tcp_child
	tcp_main:handle_tcp_child --> io_watch_add
	tcp_main:handle_io --> tcp_main:handle_ser_child
	tcp_main:handle_ser_child --> io_watch_add
	main:main_loop --> tcp_main:tcp_main_loop
	tcp_main:tcp_main_loop -- tcp listen sockets --> io_watch_add
	tcp_main:tcp_main_loop -- tls listen sockets --> io_watch_add
	tcp_main:tcp_main_loop -- process unix_sockets --> io_watch_add
	tcp_main:tcp_main_loop -- tcp child unix_sockets --> io_watch_add
	tcp_read:handle_io --> io_watch_add
	tcp_read:tcp_receive_loop --tcpmain socket--> io_watch_add
	tcp_read:tcp_receive_loop --> io_wait_loop_epoll
	
	tcp_main:tcp_main_loop --> io_wait_loop_epoll --> handle_io
	main:main_loop --> tcp_main:tcp_init_children --> tcp_read:tcp_receive_loop

4. 举例说明: UDP读TCP发