概述

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

空空如常

求真得真

freeswitch的事件引擎实现分析的更多相关文章

  1. freeswitch的任务引擎实现分析

    概述 freeswitch核心框架中有一个定时任务系统,在开发过程中用来做一些延时操作和异步操作很方便. 我们在VOIP的呼叫流程中,经常会有一些对实时性要求没那么高的操作,或者会有阻塞流程的操作,我 ...

  2. SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念

    本文属于 SQL Server 扩展事件(Extented Events)从入门到进阶 系列 在第一二节中,我们创建了一些简单的.类似典型SQL Trace的扩展事件会话.在此过程中,介绍了很多扩展事 ...

  3. (转载)Flash Loader加载完成不发送COMPLETE和ERROR事件的问题分析

    (转载)http://blog.dou.li/flash-loader%E5%8A%A0%E8%BD%BD%E5%AE%8C%E6%88%90%E4%B8%8D%E5%8F%91%E9%80%81co ...

  4. Python建立时间事件引擎原理剖析

    作为python小白,学习量化交易的曲线是非常陡峭的,唯一好的办法就是一点点啃代码.以下代码案例来自vnpy的引擎代码. # encoding: UTF-8 #定义时间事件 EVENT_TIMER = ...

  5. jquery get ($.get) 事件用法与分析

    jquery get ($.get) 事件用法与分析 get() 方法通过远程 HTTP GET 请求载入信息.这是一个简单的 GET 请求功能以取代复杂 $.ajax .请求成功时可调用回调函数.如 ...

  6. PHP实现事件机制实例分析

    PHP实现事件机制实例分析 内置了事件机制的语言不多,php也没有提供这种功能.事件(Event)说简单了就是一个Observer模式.实现起来非常easy.可是有所不同的是,事件的监听者谁都能够加, ...

  7. R数据分析:生存分析与有竞争事件的生存分析的做法和解释

    今天被粉丝发的文章给难住了,又偷偷去学习了一下竞争风险模型,想起之前写的关于竞争风险模型的做法,真的都是皮毛哟,大家见笑了.想着就顺便把所有的生存分析的知识和R语言的做法和论文报告方法都给大家梳理一遍 ...

  8. JQuery Sizzle引擎源代码分析

    最近在拜读艾伦在慕课网上写的JQuery课程,感觉在国内对JQuery代码分析透彻的人没几个能比得过艾伦.有没有吹牛?是不是我说大话了? 什么是Sizzle引擎? 我们经常使用JQuery的选择器查询 ...

  9. Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析

    声明:本文为原创博文,转载请注明出处. 句柄(handle)代表一种对持有资源的索引,句柄的叫法在window上较多,在unix/linux等系统上大多称之为描述符,为了抽象不同平台的差异,libuv ...

  10. Java三大主流开源工作流引擎技术分析

    首先,这个评论是我从网上,书中,搜索和整理出来的,也许有技术点上的错误点,也许理解没那么深入.但是我是秉着学习的态度加以评论,学习,希望对大家有用,进入正题! 三大主流工作流引擎:Shark,oswo ...

随机推荐

  1. Redis 学习笔记2:持久化

    目录 1 什么是持久化 1.1 aof 1.2 rdb 2 RDB持久化 2.1 RDB 是什么 2.2 手动触发 3 AOF持久化 3.1 aof 是什么 3.2 appendfile 文件说明: ...

  2. SpringBoot整合Liquibase

    1.是什么? Liquibase官网 Liquibase是一个开源的数据库管理工具,可以帮助开发人员管理和跟踪数据库变更.它可以与各种关系型数据库和NoSQL数据库一起使用,并提供多种数据库任务自动化 ...

  3. 整合SpringBoot + Dubbo + Nacos 出现 Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass

    版本 SpringBoot:2.7.3 Dubbo:3.0.4 Nacos:2.0.3 异常信息如下 Unable to make protected final java.lang.Class ja ...

  4. Celery将任务分发到不同的队列,交给不同的Worker处理

    https://docs.celeryq.dev/en/stable/userguide/routing.html#routing-tasks https://blog.csdn.net/wangle ...

  5. 公私两用U盘管理软件:保障企业数据安全

    随着信息技术的不断发展,企业对数据安全的关注程度越来越高.U盘作为一种便携式的移动存储设备,在工作中被广泛使用,但其本身也存在很多安全隐患,会面临数据泄漏和未经授权访问的风险.例如:企业研发部机密文档 ...

  6. 【C#】【DateTime】获取当前时间的前一天或者后一天方法学习

    假期懈怠了一阵子,因为工作环境没有网络,随即记录下当时的问题: 1.关于DateTime.Now(2023/10/07)的前一天的时间(2023/10/06),想要通过ToString转换成字符串后除 ...

  7. MES数据追溯常遇问题及解决方法

    MES数据追溯常遇问题及解决方法: 在实际数字化工厂MES应用过程,由于设计或使用不当,数据追溯过程中也可能会存在诸多问题,常遇问题包括:1. 数据质量问题 可能存在数据录入错误.数据缺失或不完整等情 ...

  8. Python——第一章:数据类型介绍

    数据类型: 区分不同的数据.不同的数据类型应该有不同的操作 数字: 做加减乘除+-*/ 整数,int 小数,float a= 10 #整数 b = 20 print(a + b) #加法运算 c = ...

  9. 一个Tomcat 如何部署多个项目?附多种解决方案及详细步骤!

    此文源自一次多年前面试的面试题,民工哥将它总结出来分享给大家,希望对大家有所帮助,或者今后的面试中说不定会用的上. 首先,我们了解一下常见的Java Web服务器. Tomcat:由Apache组织提 ...

  10. Java 在PDF中添加骑缝章

    骑缝章是用于往来业务合同,以确保合同真实.有效的印章加盖方法,是一种防范风险的重要方式.在Java程序中,可以通过使用工具来辅助加盖这种骑缝章. 工具:Free Spire.PDF for Java ...