ZeroMQ (Martin Sustrik)读后感和笔记

网上的关于ZeroMQ的文章有很多很多,大多分两类,一类是以ZeroMQ官方文档为核心,做了点翻译,教大家怎么去用ZeroMQ,适合初学者,这类文章比较多;还有一类是以ZeroMQ的源代码为核心,这些笔者对源代码作了较为深入的分析,也写过许多实例,会深入浅出的描绘流程,对代码里的“坑”会做些分析,适合中高级的读者,但这类文章比较少。感觉玩ZMQ的兄弟不多啊,O(∩_∩)O~。

前几天在网上看到一篇Martin Sustrik对Zmq的论文,感觉不错,不深不浅,从方法论以及高并发软件设计的角度讲解了Zmq,有兴趣的同学可以自行翻墙google。

以下是我阅读后的一点读后感,权当做笔记了:

ZeroMQ 被很多人称之为“消息中间件”,并广泛应用于金融、财务、游戏等领域。它的核心就是保证各个应用间的通讯可以快速、高并发、灵活简单的被使用。

ZeroMQ提供Lib服务,不是一个消息服务器,所以他没有Broker(经济学名词,代理,有兴趣的同学可以google一下broker, dealer, trader的区别)的开销,可以保证高吞吐和低延时。

ZeroMQ适用于大规模的批量化部署,它是一个分布式的架构,并不存在“central-server”的问题,其实底层他会实现适合各种部署的模式,如 IPC, Inproc, Tcp等等,去连接各个网络或进程或线程实体(endpoint)。

一个应用程序可以调用多个ZeroMQ的Lib,那么从理论上说ZeroMQ需要是可重入的,那么如果lib里面有全局变量的话需要做严格的控制。ZeroMQ采用了work around的方法,为每个ZeroMQ 的Socket实体,制定了Context,应用所获得的状态也肯定是某个Context之下的。


ZeroMQ的性能,理论上是由3个因素决定:分配的内存,系统调用的个数,并发模型;但是具体业务场景不同,性能也会不同,所以我们要关注于关键路径的优化。比如,我们要操作一个字符串,一般会分配内存,可是在ZMQ发送消息的过程中并没有分配内存的动作。如果消息不大,每256个消息分配一次内存,如果消息流是稳定的,在发送的过程中不需要分配内存。(一个memory chunk会包含所有内存)

假如ZMQ所有的上下文环境都有已经建立好并且两个endpoint的连接已经建好,那么要分配的内存只有消息本身,所以需要优化的也就是收发消息的过程中的内存分配。在高并发领域,一次消息(消息可能是大的也可能是小的)的内存分配和拷贝的成本是需要被均衡的(balance)才可以获得比较好的性能。对于大消息,ZMQ采用了分配一次内存然后传送指针的方式,也就是零拷贝用reference count去管理。对于小消息,因为没有超出memory chunk范围,没有新的内存分配,还是消息拷贝。


前面说过,系统调用次数太多会引起性能的瓶颈,所以ZMQ采取了批量化消息传送的方式。但是,批量化传送会导致高延时,所以在吞吐和延时方面要有一个好的策略。这个策略其实是一个交易( trade-off ,中文意思:两者间的平衡)。如果消息流速没有超过网络栈的带宽,且是零零星星的,批处理算法会被关闭;但是,消息流速太大,批处理算法会被打开,所有消息在被网络栈接受前需要排队等待网络栈的空闲。另外,这个batching algithm只需要打开一个就够了,因为在某些底层的协议栈也会有类似的算法。


ZerMQ  socket有点像TCP socket,但是他可以接受点到多点的连接,这点上和UDP又有点像。Socket对象存在于用户线程,其实ZMQ针对这个Socket还有工作线程,在工作线程的上下文中可以接收连接,读取数据,等等。同时会有很多对象存在于工作线程,但是他们的父对象就是用户线程的Socket。这些对象可以构成一棵树,树上所有的节点的生命周期都是一样的。在工作线程有两种异步对象,listener 和 connnecter, 前者会监听一个tcp链接并创建session/engine,后者会尝试链接一个tcp peer,成功后创建 session/engine. Session 是用来和用户线程的 socket交互的,engine用来和网络侧交互的。session的类型只有一种,里面包含了一个SocketPair;但是engine的类型有很多种,TCP engine,PGM engine,IPC engine 等等。 除此之外还有个Context对象,里面有的都是关于这个Socket的全局变量。


和许多高并发模型(actor)一样, ZMQ采用多线程实现了高并发(线程间的通信用异步消息,避免了锁的使用)。但是为了线程安全,一个tcp engine只能绑定一个work thread,也不会产生互斥。但是在一个工作线程上的对象仍然要轮流工作,所以调度器采用了事件驱动(reactor)而不是事件循环的策略,当然万万不能阻塞。这么一个异步高并发模型,要关闭是很复杂的,要考虑各种对象的各种状态,这点我还没来得及研究,太大了个屁的。

关于无锁算法,ZMQ采用类似CAS的算法,个人(文强)感觉CAS的算法如果没有科学家的官方认证,还是不要用的好,尤其是带垃圾回收的那种(非环形队列),太复杂了。每个pipe的队列都是1to1的结构,如果有1ton的情况就增加队列,生产和消费者之间用CAS. (文强认为:1对1的生产消费者模型应该用不到CAS的,类似LINUX的fifo 环形queue就可以搞定,这里为什么不用?)。还有就是批量化再次被引入了pipe队列的无锁算法,设定pre read 和 pre write,对于写端可以把消息放到pre区域,积攒到一定程度再flush到读端;对于读端可以主动从pre write把消息取到 pre read,然后再一个个的读。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值