freeswitch APR-UTIL库消息队列实现


概述
freeswitch的核心源代码是基于apr库开发的,在不同的系统上有很好的移植性。
APR库在之前的文章中已经介绍过了,APR-UTIL库是和APR并列的工具库,它们都是由APACHE开源出来的跨平台可移植库,不同点在于库中实现的功能接口有区别。
在应用的开发过程中,模块内部的消息传递是经常会碰到的需求,而消息队列就是很好的解决方案。
APR-UTIL库中就提供了一套线程安全的消息队列接口。
我对几个问题比较好奇,消息队列的数据结构是什么样的?消息队列如何插入元素?消息队列如何弹出元素?如何获取最优的效率?
下面我们对apr-util库的消息队列实现做一个介绍。
环境
centos:CentOS release 7.0 (Final)或以上版本
freeswitch:v1.8.7
GCC:4.8.5
数据结构
APR-UTIL库消息队列源代码文件:
libs\apr-util\include\apr_queue.h
libs\apr-util\misc\apr_queue.c
在apr_queue.c文件中,我们可以找到消息队列的数据结构定义
struct apr_queue_t {
void **data; //消息指针数组
unsigned int nelts; //消息个数
unsigned int in; //消息入队的数组下标
unsigned int out; //消息出队的数组下标
unsigned int bounds; //队列容量
unsigned int full_waiters; //队列满的等待者个数
unsigned int empty_waiters; //队列空的等待者个数
apr_thread_mutex_t *one_big_mutex; //多线程互斥锁
apr_thread_cond_t *not_empty; //队列空转非空的条件变量
apr_thread_cond_t *not_full; //队列满转非满的条件变量
int terminated; //队列终止标识
};
常用函数
apr_queue.h文件的接口函数:
apr_queue_create //create a FIFO queue
apr_queue_push //push/add a object to the queue, blocking if the queue is already full
apr_queue_pop //pop/get an object from the queue, blocking if the queue is already empty
apr_queue_pop_timeout //pop/get an object from the queue, blocking if the queue is already empty
apr_queue_trypush //push/add a object to the queue, returning immediatly if the queue is full
apr_queue_trypop //pop/get an object to the queue, returning immediatly if the queue is empty
apr_queue_size //returns the size of the queue.
apr_queue_interrupt_all //interrupt all the threads blocking on this queue.
apr_queue_term //terminate all queue, sending a interupt to all the blocking threads

apr_queue_create创建
APU_DECLARE(apr_status_t) apr_queue_create(apr_queue_t **queue,
unsigned int queue_capacity,
apr_pool_t *a);
接口逻辑:
- 在内存池a中分配一块大小为apr_queue_t的内存queue。
- 在内存池a中创建一个新的互斥量queue->one_big_mutex。
- 在内存池a中创建两个条件变量queue->not_empty和queue->not_full。
- 在内存池a中分配一块大小为queue_capacity * sizeof(void*)的内存queue->data。
- 变量初始化。
- 在内存池a注册一个清理回调函数。
消息队列创建后的内存模型如图

apr_queue_push入队
APU_DECLARE(apr_status_t) apr_queue_push(apr_queue_t *queue, void *data);
接口逻辑:
- 判断终止标识queue->terminated。
- 加锁queue->one_big_mutex。
- 判断queue是满的,否则继续下一步。
a) 判断终止标识queue->terminated。
b) 队列满的等待者queue->full_waiters递增。
c) 等待条件变量queue->not_full的信号,唤醒之后继续下一步。
d) 队列满的等待者queue->full_waiters递减。
e) 再次判断queue仍然是满的,则解锁退出,返回APR_EINTR。
- 消息入队,在queue->data[queue->in]保存消息指针。
- 入队下标queue->in递增,超过数组长度则从0重新开始。
- 消息个数queue->nelts递增。
- 判断有队列空的等待者queue->empty_waiters,则给条件变量queue->not_empty发送信号。
- 解锁queue->one_big_mutex,退出。
消息入队后的内存模型如图

apr_queue_pop出队
APU_DECLARE(apr_status_t) apr_queue_pop(apr_queue_t *queue, void **data);
接口逻辑:
- 判断终止标识queue->terminated。
- 加锁queue->one_big_mutex。
- 判断queue是空的,否则继续下一步。
a) 判断终止标识queue->terminated。
b) 队列空的等待者queue->empty_waiters递增。
c) 等待条件变量queue->not_empty的信号,唤醒之后继续下一步。
d) 队列空的等待者queue->empty_waiters递减。
e) 再次判断queue仍然是空的,则解锁退出,返回APR_EINTR。
- 消息出队,获取消息指针queue->data[queue->out]。
- 出队下标queue->out递增,超过数组长度则从0重新开始。
- 消息个数queue->nelts递减。
- 判断有队列满的等待者queue->full_waiters,则给条件变量queue->not_full发送信号。
- 解锁queue->one_big_mutex,退出。
消息出队后的内存模型如图,从图中我们可以看出消息出队仅仅是对out下标做了改动,消息内容都没有变动。

apr_queue_term终止
APU_DECLARE(apr_status_t) apr_queue_term(apr_queue_t *queue);
接口逻辑:
- 加锁queue->one_big_mutex。
- 设置队列终止标识queue->terminated。
- 解锁queue->one_big_mutex。
- 加锁queue->one_big_mutex。
- 给条件变量queue->not_empty发送广播,唤醒所有等待的线程。
- 给条件变量queue->not_full发送广播,唤醒所有等待的线程。
- 解锁queue->one_big_mutex。
- 退出。
总结
apr-util的消息队列在使用时,需要注意的地方。
消息入队,再次判断队列满时,是会丢消息的,需要调用者判断返回值为APR_EINTR并新入队。
消息出队,再次判断队列空时,也需要调用者判断返回值为APR_EINTR并重新获取消息,而不能简单的报错终止业务逻辑。
消息队列的接口中,没有销毁队列的接口,因为apr_queue_t是在内存池a中创建的,并且有注册内存池清理回调函数,所以消息队列会随着内存池a的清理自动销毁。
空空如常
求真得真
freeswitch APR-UTIL库消息队列实现的更多相关文章
- BOOST库 消息队列
直接贴实验代码: /******* boost 消息队列 **********/ #if 1 #include <boost/thread/thread.hpp> #include < ...
- 消息队列库——ZeroMQ
消息队列库——ZeroMQ ZeroMQ(简称ZMQ)是一个基于消息队列的多线程网络库,其对套接字类型.连接处理.帧.甚至路由的底层细节进行抽象,提供跨越多种传输协议的套接字. ZMQ是网络通信中新的 ...
- 商城08——activeMQ 使用消息队列同步索引库
1. 课程计划 1.什么是MQ 2.MQ的应用场景 3.ActiveMQ的使用方法. 4.使用消息队列实现商品同步. 2. 同步索引库分析 方案一:在taotao-manager中,添加商品的业务 ...
- freeswitch APR库
概述 freeswitch依赖库源代码基本都可以在libs目录下找到. 在freeswitch的官方手册中,可以找到freeswitch的依赖库表格,其中freeswitch的core核心代码依赖库主 ...
- JAVAEE——宜立方商城08:Zookeeper+SolrCloud集群搭建、搜索功能切换到集群版、Activemq消息队列搭建与使用
1. 学习计划 1.solr集群搭建 2.使用solrj管理solr集群 3.把搜索功能切换到集群版 4.添加商品同步索引库. a) Activemq b) 发送消息 c) 接收消息 2. 什么是So ...
- java JMS消息队列
http://blog.csdn.net/shirdrn/article/details/6362792 http://haohaoxuexi.iteye.com/blog/1893038 http: ...
- 消息队列—ActiveMQ
1. 学习计划 1.什么是MQ 2.MQ的应用场景 3.ActiveMQ的使用方法. 4.使用消息队列实现商品同步. 2. 同步索引库分析 方案一:在manager(后台)中,添加商品的业务逻 ...
- RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙
消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
随机推荐
- 【TcaplusDB知识库】如何部署TcaplusDB Local 版
[TcaplusDB知识库]部署TcaplusDB Local 版的准备操作 1. 版本介绍 TcaplusDB Local版,是为用户提供的一个满足本地开发调试的版本(基于Docker部署的可下载版 ...
- springboot项目中常遇到的问题-初学者最容易犯的错
1.在spring中有两个main方法 2.在idea中少提代码类了,或者某类中代码依赖关系没解决掉
- Cannot connect to runtime process
发生一个或多个错误. 未能启动调试适配器.可以在输出窗口中查看额外的信息. Cannot connect to runtime process, timeout after 10000 ms (rea ...
- P2066 机器分配 解析
小日记: 1.今天新学的字体颜色,尽管不熟悉,但玩的666,卡星(开心) ╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮╰( ̄▽ ̄)╮ 2.今天油腔滑调,谅解亿下 P2066 ...
- C# 将Excel转为PDF时自定义表格纸张大小
通过后端程序将Excel表格转为PDF格式时,直接转换后的PDF效果可能出现表格页面过小或者过大,导致页面内容分布不均.要改善转换后的文档效果,只需在转换前自定义表格纸张大小,即可调整转换后的PDF页 ...
- Python 3 快速入门 1 —— 数据类型与变量
本文假设你已经有一门面向对象编程语言基础,如Java等,且希望快速了解并使用Python语言.本文对重点语法和数据结构以及用法进行详细说明,同时对一些难以理解的点进行了图解,以便大家快速入门.一些较偏 ...
- a.out的由来
用过linux的都知道,在linux下编译链接程序,如果不加-o参数,生成的binary代码的名字都是默认的a.out.一不小心,a.out还会覆盖上次其他code生成的binary代码. a.out ...
- Linux三剑客之老三grep
说明: Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来.工作中我们常常用它来过滤出我们想要的数据. 格式: grep [OPTIONS] 基本参 ...
- HTTP 之 options预请求
一.HTTP一共有八种常见请求方法 get:参数在url上,浏览器长度有限制,不安全 post:参数不可见,长度不受限制 put:上传最新内容到指定位置 delete:删除请求的url所表示的资源 h ...
- STM32代码常见的坑
1 混淆换行符\和除号/造成的坑 入坑代码: GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin ...
