概述

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. 【Vue.js】SPA

    SPA 2019-11-13  23:20:48  by冲冲 1.概念 (1)MPA(multi-page application) 特点:每一次页面跳转的时候,后台服务器都会返回一个新的html文档 ...

  2. 关于前端项目cookie的获取

    最近项目在做切换,2.0要升级到3.0,遇到跨域问题,使用的是nginx 进行对端口拦截请求其他地址,另外有的服务还要登录权限,之前没看到有这个,后来加上获取cookie的请求 var v3_toke ...

  3. 发布项目到maven中央仓库

    https://www.xiaominfo.com/2017/04/25/swagger-bootstrap-ui-issue-maven-central/?tdsourcetag=s_pcqq_ai ...

  4. [省选联考 2021 A/B 卷] 图函数

    考虑到一件事情首先\(u -> u\)是可行的. 所以其实对于\(f(u,G')\) 只要考虑\([1,u]\)的点. 那么考虑其条件等价于\(u -> i\) 和 \(i -> u ...

  5. [CCC​2019] Tourism题解

    我们先考虑一下拿部分分: subtask1 考虑因为 \(n < 2k\) ,那么我们的划分一定是从中间某个地方裁开,且满足 \(k\) 的条件的,我们发现当划分点在 \([n\ mod\ k, ...

  6. Atcoder Regular Contest 058 D - 文字列大好きいろはちゃん / Iroha Loves Strings(单调栈+Z 函数)

    洛谷题面传送门 & Atcoder 题面传送门 神仙题. mol 一发现场(bushi)独立切掉此题的 ycx %%%%%%% 首先咱们可以想到一个非常 naive 的 DP,\(dp_{i, ...

  7. Mysql 预处理 PREPARE以及预处理的好处

    Mysql 预处理 PREPARE以及预处理的好处 Mysql手册 预处理记载: 预制语句的SQL语法在以下情况下使用:   · 在编代码前,您想要测试预制语句在您的应用程序中运行得如何.或者也许一个 ...

  8. R语言与医学统计图形-【29】地图的绘制

    R绘制地图原理: R使用一个个多边形(polygon)来表示每个区域,通过顺次连接GIS数据提供的每个区域多边形的坐标来逐点绘制这些多边形,所以理论上只要得到GIS数据就可绘制相应的地图. 地图绘制说 ...

  9. 学习资源 Docker从入门到实践 pdf ,docker基础总结导图

    学习资源 Docker从入门到实践 pdf ,docker基础总结导图 Docker从入门到实践 pdf 云盘地址:https://pan.baidu.com/s/1vYyxlW8SSFSsMuKaI ...

  10. R包 tidyverse 分列

    代码: 1 library(tidyverse) 2 separate(data = df,col=chr_pos,into=c("chr","pos"),se ...