freeswitch的事件引擎实现分析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了freeswitch的事件引擎实现分析相关的知识,希望对你有一定的参考价值。

参考技术A 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 */

       intidx;

       /*!hash of the header name */

       unsignedlong hash;

       structswitch_event_header *next;

;

struct switch_event

       /*!the event id (descriptor) */

       switch_event_types_tevent_id;

       /*!the priority of the event */

       switch_priority_tpriority;

       /*!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 */

       unsignedlong key;

       structswitch_event *next;

       intflags;

;

struct switch_event_node

       /*!the id of the node */

       char*id;

       /*!the event id enumeration to bind to */

       switch_event_types_tevent_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_tcallback;

       /*!private data */

       void*user_data;

       structswitch_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 itstill */

       intbind;

;

总图

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_DECLARE(switch_status_t)switch_event_init(switch_memory_pool_t *pool)

函数逻辑

[if !supportLists]1, [endif]全局变量初始化。MAX_DISPATCH的大小范围是(2,(cpu核数/2)+1)。

[if !supportLists]2, [endif]初始化事件分发的消息队列EVENT_DISPATCH_QUEUE。

[if !supportLists]3, [endif]启动1个事件分发线程,执行线程函数“switch_event_dispatch_thread”。线程函数中监听EVENT_DISPATCH_QUEUE消息队列,并根据消息类型执行注册的回调函数。

[if !supportLists]4, [endif]设置全局变量SYSTEM_RUNNING = 1。

事件引擎初始化后的内存模型图

函数原型

SWITCH_DECLARE(switch_status_t)switch_event_shutdown(void)

函数逻辑:

[if !supportLists]1, [endif]设置全局变量SYSTEM_RUNNING = 0。

[if !supportLists]2, [endif]向EVENT_CHANNEL_DISPATCH_QUEUE发送NULL包,通知channel分发线程结束处理。

[if !supportLists]3, [endif]向EVENT_DISPATCH_QUEUE发送n个NULL包,n的数量由dispatch分发线程数目决定,通知dispatch分发线程结束处理。

[if !supportLists]4, [endif]等待所有的EVENT_DISPATCH_QUEUE_THREADS线程结束退出。

[if !supportLists]5, [endif]清空EVENT_DISPATCH_QUEUE队列,销毁所有未处理完的event。

[if !supportLists]6, [endif]遍历CUSTOM_HASH,清理所有subclass注册。

[if !supportLists]7, [endif]如果支持事件回收,清理事件回收队列的内存。

[if !supportLists]8, [endif]处理结束。

函数原型

SWITCH_DECLARE(switch_status_t) switch_event_create_subclass_detailed(constchar *file, const char *func, int line,

                                                                                                                         switch_event_t **event, switch_event_types_tevent_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)

函数逻辑:

[if !supportLists]1, [endif]如果支持事件回收,从回收队列获取一个事件。不支持则重新分配一个新的事件。

[if !supportLists]2, [endif]事件预处理,添加固定头域格式。

[if !supportLists]3, [endif]处理“subclass_name”对应头域。

新创建的事件的内存模型如图

函数原型

SWITCH_DECLARE(switch_status_t) switch_event_add_header(switch_event_t*event, switch_stack_t stack, const char *header_name, const char *fmt, ...)

函数逻辑:

[if !supportLists]1, [endif]获取参数列表。

[if !supportLists]2, [endif]获取一个头域结构体header。

[if !supportLists]3, [endif]header结构体赋值。

[if !supportLists]4, [endif]将header添加到“event->headers”中。

增加头域后的事件的内存模型如图

函数原型

SWITCH_DECLARE(void)switch_event_destroy(switch_event_t **event)

函数逻辑:

[if !supportLists]1, [endif]遍历”hp = event->headers”,如果“hp->array”不为空,先循环释放“hp->array[i]”内存,然后释放“hp->array”内存,再释放“hp->name”和“hp->value”和“hp”,“hp”的释放需要判断事件循环使用标识。

[if !supportLists]2, [endif]释放“event->body”和“event->subclass_name”。

[if !supportLists]3, [endif]根据事件循环使用标识,判断是否释放“event”。事件循环使用则把“event”发送到队列“EVENT_RECYCLE_QUEUE”。

[if !supportLists]4, [endif]处理结束。

函数原型

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)

函数逻辑:

[if !supportLists]1, [endif]参数校验

[if !supportLists]2, [endif]检查EVENT_DISPATCH_QUEUE队列大小,如果队列满了,则对当前dispatch线程数和MAX_DISPATCH比较,并增加新的dispatch分发线程。

[if !supportLists]3, [endif]将事件event推送到EVENT_DISPATCH_QUEUE中。

[if !supportLists]4, [endif]处理结束。

事件较多时,事件引擎的线程模型如图

函数原型

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)

函数逻辑:

[if !supportLists]1, [endif]参数校验。

[if !supportLists]2, [endif]检查subclass_name,针对不同的subclass owner,添加一个新节点到CUSTOM_HASH。

[if !supportLists]3, [endif]分配新内存块event_node,对event_node初始化,绑定事件回调函数。

[if !supportLists]4, [endif]将event_node插入EVENT_NODES[event]节点下的链表头部。

[if !supportLists]5, [endif]处理结束。

事件绑定后的内存模型如图

函数原型

_Ret_opt_z_ SWITCH_DECLARE(char *)switch_event_get_header_idx(switch_event_t *event, const char *header_name, intidx);

#define switch_event_get_header(_e, _h)switch_event_get_header_idx(_e, _h, -1)

函数逻辑:

[if !supportLists]1, [endif]对header_name执行默认HASH函数(TIME33算法),得到hash值。

[if !supportLists]2, [endif]遍历hp = event->headers,查找到满足条件“((!hp->hash

|| hash == hp->hash) && !strcasecmp(hp->name, header_name))”的头域,则返回”hp->value”。

[if !supportLists]3, [endif]如果header_name的值为“_body”,则返回“event->body”。

[if !supportLists]4, [endif]匹配失败,返回NULL。

freeswitch可以通过设置全局变量“events-use-dispatch”的值,来控制事件分发的模式。

默认情况下“events-use-dispatch=1”,使用dispatch模式分发事件,dispatch线程数量大小范围为(2,(cpu/2)+1)。

当“events-use-dispatch=0”时,使用线程池模式分发事件。

fs的事件可以设置为循环使用,处理会有小的区别,主要在回收事件时会将事件的内存保留在循环队列中,方便循环使用,提高内存使用效率。

空空如常

求真得真

freeswitch 事件命令

1.uuid_bridge

桥接两条呼叫的腿。

Usage: uuid_bridge <uuid> <other_uuid>

uuid_bridge至少需要有一条腿是被呼通的。

2.uuid_chat

发送聊天信息

Usage: <uuid> <text>

如果和会话(session,由uuid指定)相关的终端有一个receive_event handler,该消息会被发往终端,并以及时消息的形式显示出来。

3.

以上是关于freeswitch的事件引擎实现分析的主要内容,如果未能解决你的问题,请参考以下文章

大数据计算引擎之Flink Flink CEP复杂事件编程

Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

深入node.js 3 模板引擎原理 事件 文件操作 可读流的实现原理

大事件--练习项目分享--实现前后端交互

esper 事件引擎,各种事件类型示例代码

生成freeswitch事件的几种方式