freeswitch的事件引擎实现分析
概述
freeswitch是由事件驱动的,fs内部有各种事件来标识状态的变化包括呼叫的变化、配置的变化、号码的变化等等。
而一个框架内的事件引擎需要实现哪些基本的功能呢?
让我们来看一下fs的事件引擎是如何实现的。
环境
centos:CentOS release 7.0 (Final)或以上版本
freeswitch:v1.8.7
GCC:4.8.5
数据结构
fs中event事件的实现主要在以下俩个文件。
src\include\switch_event.h
src\switch_event.c
涉及到的主要结构体如下
其中switch_event是传递事件消息的结构体,switch_event_node则是记录事件回调的结构体。
struct switch_event_header {
/*! the header name */
char *name;
/*! the header value */
char *value;
/*! array space */
char **array;
/*! array index */
int idx;
/*! hash of the header name */
unsigned long hash;
struct switch_event_header *next;
};
struct switch_event {
/*! the event id (descriptor) */
switch_event_types_t event_id;
/*! the priority of the event */
switch_priority_t priority;
/*! the owner of the event */
char *owner;
/*! the subclass of the event */
char *subclass_name;
/*! the event headers */
switch_event_header_t *headers;
/*! the event headers tail pointer */
switch_event_header_t *last_header;
/*! the body of the event */
char *body;
/*! user data from the subclass provider */
void *bind_user_data;
/*! user data from the event sender */
void *event_user_data;
/*! unique key */
unsigned long key;
struct switch_event *next;
int flags;
};
struct switch_event_node {
/*! the id of the node */
char *id;
/*! the event id enumeration to bind to */
switch_event_types_t event_id;
/*! the event subclass to bind to for custom events */
char *subclass_name;
/*! a callback function to execute when the event is triggered */
switch_event_callback_t callback;
/*! private data */
void *user_data;
struct switch_event_node *next;
};
struct switch_event_subclass {
/*! the owner of the subclass */
char *owner;
/*! the subclass name */
char *name;
/*! the subclass was reserved by a listener so it's ok for a module to reserve it still */
int bind;
};
总图
常用接口
src\include\switch_event.h 文件中,常用的接口列表
//事件引擎初始化接口
switch_event_init
switch_event_shutdown
//事件发布者接口
switch_event_create_subclass
switch_event_add_header
switch_event_destroy
switch_event_fire
//事件订阅者接口
switch_event_bind
switch_event_get_header
事件引擎是一个典型的PUB/SUB模型,发布者负责发布事件消息,事件引擎提供消息通道和转发,订阅者注册消息回调函数,由事件引擎根据事件消息调用回调函数,处理业务逻辑。
事件引擎模型图
事件引擎初始化switch_event_init
函数原型
SWITCH_DECLARE(switch_status_t) switch_event_init(switch_memory_pool_t *pool)
函数逻辑
1, 全局变量初始化。MAX_DISPATCH的大小范围是(2,(cpu核数/2)+1)。
2, 初始化事件分发的消息队列EVENT_DISPATCH_QUEUE。
3, 启动1个事件分发线程,执行线程函数“switch_event_dispatch_thread”。线程函数中监听EVENT_DISPATCH_QUEUE消息队列,并根据消息类型执行注册的回调函数。
4, 设置全局变量SYSTEM_RUNNING = 1。
事件引擎初始化后的内存模型图
事件引擎关闭switch_event_shutdown
函数原型
SWITCH_DECLARE(switch_status_t) switch_event_shutdown(void)
函数逻辑:
1, 设置全局变量SYSTEM_RUNNING = 0。
2, 向EVENT_CHANNEL_DISPATCH_QUEUE发送NULL包,通知channel分发线程结束处理。
3, 向EVENT_DISPATCH_QUEUE发送n个NULL包,n的数量由dispatch分发线程数目决定,通知dispatch分发线程结束处理。
4, 等待所有的EVENT_DISPATCH_QUEUE_THREADS线程结束退出。
5, 清空EVENT_DISPATCH_QUEUE队列,销毁所有未处理完的event。
6, 遍历CUSTOM_HASH,清理所有subclass注册。
7, 如果支持事件回收,清理事件回收队列的内存。
8, 处理结束。
创建事件switch_event_create_subclass
函数原型
SWITCH_DECLARE(switch_status_t) switch_event_create_subclass_detailed(const char *file, const char *func, int line,
switch_event_t **event, switch_event_types_t event_id, const char *subclass_name);
#define switch_event_create_subclass(_e, _eid, _sn) switch_event_create_subclass_detailed(__FILE__, (const char * )__SWITCH_FUNC__, __LINE__, _e, _eid, _sn)
函数逻辑:
1, 如果支持事件回收,从回收队列获取一个事件。不支持则重新分配一个新的事件。
2, 事件预处理,添加固定头域格式。
3, 处理“subclass_name”对应头域。
新创建的事件的内存模型如图
事件增加头域switch_event_add_header
函数原型
SWITCH_DECLARE(switch_status_t) switch_event_add_header(switch_event_t *event, switch_stack_t stack, const char *header_name, const char *fmt, ...)
函数逻辑:
1, 获取参数列表。
2, 获取一个头域结构体header。
3, header结构体赋值。
4, 将header添加到“event->headers”中。
增加头域后的事件的内存模型如图
事件销毁switch_event_destroy
函数原型
SWITCH_DECLARE(void) switch_event_destroy(switch_event_t **event)
函数逻辑:
1, 遍历”hp = event->headers”,如果“hp->array”不为空,先循环释放“hp->array[i]”内存,然后释放“hp->array”内存,再释放“hp->name”和“hp->value”和“hp”,“hp”的释放需要判断事件循环使用标识。
2, 释放“event->body”和“event->subclass_name”。
3, 根据事件循环使用标识,判断是否释放“event”。事件循环使用则把“event”发送到队列“EVENT_RECYCLE_QUEUE”。
4, 处理结束。
事件发布switch_event_fire
函数原型
SWITCH_DECLARE(switch_status_t) switch_event_fire_detailed(const char *file, const char *func, int line, switch_event_t **event, void *user_data);
#define switch_event_fire(event) switch_event_fire_detailed(__FILE__, (const char * )__SWITCH_FUNC__, __LINE__, event, NULL)
函数逻辑:
1, 参数校验
2, 检查EVENT_DISPATCH_QUEUE队列大小,如果队列满了,则对当前dispatch线程数和MAX_DISPATCH比较,并增加新的dispatch分发线程。
3, 将事件event推送到EVENT_DISPATCH_QUEUE中。
4, 处理结束。
事件较多时,事件引擎的线程模型如图
事件绑定switch_event_bind
函数原型
SWITCH_DECLARE(switch_status_t) switch_event_bind(const char *id, switch_event_types_t event, const char *subclass_name,
switch_event_callback_t callback, void *user_data)
函数逻辑:
1, 参数校验。
2, 检查subclass_name,针对不同的subclass owner,添加一个新节点到CUSTOM_HASH。
3, 分配新内存块event_node,对event_node初始化,绑定事件回调函数。
4, 将event_node插入EVENT_NODES[event]节点下的链表头部。
5, 处理结束。
事件绑定后的内存模型如图
事件获取头域switch_event_get_header
函数原型
_Ret_opt_z_ SWITCH_DECLARE(char *) switch_event_get_header_idx(switch_event_t *event, const char *header_name, int idx);
#define switch_event_get_header(_e, _h) switch_event_get_header_idx(_e, _h, -1)
函数逻辑:
1, 对header_name执行默认HASH函数(TIME33算法),得到hash值。
2, 遍历hp = event->headers,查找到满足条件“((!hp->hash || hash == hp->hash) && !strcasecmp(hp->name, header_name))”的头域,则返回”hp->value”。
3, 如果header_name的值为“_body”,则返回“event->body”。
4, 匹配失败,返回NULL。
总结
freeswitch可以通过设置全局变量“events-use-dispatch”的值,来控制事件分发的模式。
默认情况下“events-use-dispatch=1”,使用dispatch模式分发事件,dispatch线程数量大小范围为(2,(cpu/2)+1)。
当“events-use-dispatch=0”时,使用线程池模式分发事件。
fs的事件可以设置为循环使用,处理会有小的区别,主要在回收事件时会将事件的内存保留在循环队列中,方便循环使用,提高内存使用效率。
空空如常
求真得真