概述

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);

接口逻辑:

  1. 在内存池a中分配一块大小为apr_queue_t的内存queue。
  2. 在内存池a中创建一个新的互斥量queue->one_big_mutex。
  3. 在内存池a中创建两个条件变量queue->not_empty和queue->not_full。
  4. 在内存池a中分配一块大小为queue_capacity * sizeof(void*)的内存queue->data。
  5. 变量初始化。
  6. 在内存池a注册一个清理回调函数。

消息队列创建后的内存模型如图

apr_queue_push入队

APU_DECLARE(apr_status_t) apr_queue_push(apr_queue_t *queue, void *data);

接口逻辑:

  1. 判断终止标识queue->terminated。
  2. 加锁queue->one_big_mutex。
  3. 判断queue是满的,否则继续下一步。

a)      判断终止标识queue->terminated。

b)      队列满的等待者queue->full_waiters递增。

c)      等待条件变量queue->not_full的信号,唤醒之后继续下一步。

d)      队列满的等待者queue->full_waiters递减。

e)      再次判断queue仍然是满的,则解锁退出,返回APR_EINTR。

  1. 消息入队,在queue->data[queue->in]保存消息指针。
  2. 入队下标queue->in递增,超过数组长度则从0重新开始。
  3. 消息个数queue->nelts递增。
  4. 判断有队列空的等待者queue->empty_waiters,则给条件变量queue->not_empty发送信号。
  5. 解锁queue->one_big_mutex,退出。

消息入队后的内存模型如图

apr_queue_pop出队

APU_DECLARE(apr_status_t) apr_queue_pop(apr_queue_t *queue, void **data);

接口逻辑:

  1. 判断终止标识queue->terminated。
  2. 加锁queue->one_big_mutex。
  3. 判断queue是空的,否则继续下一步。

a)      判断终止标识queue->terminated。

b)      队列空的等待者queue->empty_waiters递增。

c)      等待条件变量queue->not_empty的信号,唤醒之后继续下一步。

d)      队列空的等待者queue->empty_waiters递减。

e)      再次判断queue仍然是空的,则解锁退出,返回APR_EINTR。

  1. 消息出队,获取消息指针queue->data[queue->out]。
  2. 出队下标queue->out递增,超过数组长度则从0重新开始。
  3. 消息个数queue->nelts递减。
  4. 判断有队列满的等待者queue->full_waiters,则给条件变量queue->not_full发送信号。
  5. 解锁queue->one_big_mutex,退出。

消息出队后的内存模型如图,从图中我们可以看出消息出队仅仅是对out下标做了改动,消息内容都没有变动。

apr_queue_term终止

APU_DECLARE(apr_status_t) apr_queue_term(apr_queue_t *queue);

接口逻辑:

  1. 加锁queue->one_big_mutex。
  2. 设置队列终止标识queue->terminated。
  3. 解锁queue->one_big_mutex。
  4. 加锁queue->one_big_mutex。
  5. 给条件变量queue->not_empty发送广播,唤醒所有等待的线程。
  6. 给条件变量queue->not_full发送广播,唤醒所有等待的线程。
  7. 解锁queue->one_big_mutex。
  8. 退出。

总结

apr-util的消息队列在使用时,需要注意的地方。

消息入队,再次判断队列满时,是会丢消息的,需要调用者判断返回值为APR_EINTR并新入队。

消息出队,再次判断队列空时,也需要调用者判断返回值为APR_EINTR并重新获取消息,而不能简单的报错终止业务逻辑。

消息队列的接口中,没有销毁队列的接口,因为apr_queue_t是在内存池a中创建的,并且有注册内存池清理回调函数,所以消息队列会随着内存池a的清理自动销毁。


空空如常

求真得真

freeswitch APR-UTIL库消息队列实现的更多相关文章

  1. BOOST库 消息队列

    直接贴实验代码: /******* boost 消息队列 **********/ #if 1 #include <boost/thread/thread.hpp> #include < ...

  2. 消息队列库——ZeroMQ

    消息队列库——ZeroMQ ZeroMQ(简称ZMQ)是一个基于消息队列的多线程网络库,其对套接字类型.连接处理.帧.甚至路由的底层细节进行抽象,提供跨越多种传输协议的套接字. ZMQ是网络通信中新的 ...

  3. 商城08——activeMQ 使用消息队列同步索引库

    1.  课程计划 1.什么是MQ 2.MQ的应用场景 3.ActiveMQ的使用方法. 4.使用消息队列实现商品同步. 2.  同步索引库分析 方案一:在taotao-manager中,添加商品的业务 ...

  4. freeswitch APR库

    概述 freeswitch依赖库源代码基本都可以在libs目录下找到. 在freeswitch的官方手册中,可以找到freeswitch的依赖库表格,其中freeswitch的core核心代码依赖库主 ...

  5. JAVAEE——宜立方商城08:Zookeeper+SolrCloud集群搭建、搜索功能切换到集群版、Activemq消息队列搭建与使用

    1. 学习计划 1.solr集群搭建 2.使用solrj管理solr集群 3.把搜索功能切换到集群版 4.添加商品同步索引库. a) Activemq b) 发送消息 c) 接收消息 2. 什么是So ...

  6. java JMS消息队列

    http://blog.csdn.net/shirdrn/article/details/6362792 http://haohaoxuexi.iteye.com/blog/1893038 http: ...

  7. 消息队列—ActiveMQ

    1.   学习计划 1.什么是MQ 2.MQ的应用场景 3.ActiveMQ的使用方法. 4.使用消息队列实现商品同步. 2.   同步索引库分析 方案一:在manager(后台)中,添加商品的业务逻 ...

  8. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

  9. Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流

    1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...

随机推荐

  1. es的rest风格的api文档

    rest风格的api put http://127.0.0.1:9200/索引名称/类型名称/文档id (创建文档,指定文档id) post http://127.0.0.1:9200/索引名称/类型 ...

  2. linux结束进程命令

    在linux中,进程之间通过信号来通信.进程的信号就是预定义好一个消息,进程能识别它并决定忽略还是做出反应. 信号 名称 描述 1 HUP 挂起 2 INT 中断 3 QUIT 结束运行 9 KILL ...

  3. [省选联考 2020 A 卷] 组合数问题

    题意 [省选联考 2020 A 卷] 组合数问题 想法 自己在多项式和数论方面还是太差了,最近写这些题都没多少思路,看完题解才会 首先有这两个柿子 \(k*\dbinom{n}{k} = n*\dbi ...

  4. [FJ2021]D2T3题解

    考试的时候一点思路没有,最近听福州的神仙的一些做法. 想自己推一下. 题目大概是这样的 \(a_i = \frac{i\ *\ a_{i - 1} \ + \ i\ * \ (i\ -\ 1)\ * ...

  5. 洛谷 P4569 - [BJWC2011]禁忌(AC 自动机+矩阵乘法)

    题面传送门 又好久没做过 AC 自动机的题了,做道练练手罢( 首先考虑对于某个固定的字符串怎样求出它的伤害,我们考虑贪心,每碰到出现一个模式串就将其划分为一段,最终该字符串的代价就是划分的次数.具体来 ...

  6. Python函数之传参

    目录 1. 函数传参 1.1 参数的作用 1.2 形参和实参 1.3 位置参数 1.4 关键字参数 1.5 默认实参 1.6 参数总结 2. 可变参数 1. 函数传参 1.1 参数的作用 1.2 形参 ...

  7. WebRTC网页打开摄像头并录制视频

    前面我们能打开本地摄像头,并且在网页上看到摄像头的预览图像. 本文我们使用MediaRecorder来录制视频.在网页上播放录制好的视频,并能提供下载功能. html 首先创建一个html界面,放上一 ...

  8. 11 — springboot集成swagger — 更新完毕

    1.前言 理论知识滤过,自行百度百科swagger是什么 2.导入依赖 <!-- swagger所需要的依赖--> <dependency> <groupId>io ...

  9. 学习java 7.14

    学习内容: 标准输入输出流 输出语言的本质:是一个标准的输出流 字节打印流 字符打印流 对象序列化流 明天内容: 进程和线程 遇到问题: 用对象序列化流序列化一个对象后,假如我们修改了对象所属的类文件 ...

  10. adhere, adjust, adjacent

    adhere to stick,不是to here. 在古英语里,stick是twig(细树枝).fasten(想必是用twig来固定).后引申为粘住.stick还有stab, pierce的意思,想 ...