原文地址:
https://nanomsg.org/documentation-zeromq.html
自这篇文档完成以后,Nanomsg和ZeroMQ都已经发生了很大的变化。尽管如此,本文档从ZeroMQ和Nanomsg原创作者的角度来阐述,仍有助于理解nanomsg的历史变更。
授权
Nanomsg库基于MIT授权,这就意味着Nanomsg不同于ZeroMQ,你可以修改源代码,可以作为专利产品以不同的授权重新发布等。更多关于授权许可的信息可以在
这里找到。
POSIX兼容
ZeroMQ API是基于BSD套接字API建模的,但它与POSIX API并不完全匹配。nanomsg的目标是完全符合POSIX标准。
- Sockets被标识为int而不是空指针。
- ZeroMQ中的上下文概念在nanomsg中不存在。这意味着API更简化(可以在单个步骤中创建套接字),同时增加了在单个进程中使用nanomsg库在不同模块之间进行通信的可能性(想想用不同语言实现的插件,它们彼此之间使用不同的语言)。更多的讨论可以在这里找到。
- 发送和接收函数(nn_send, nn_sendmsg, nn_recv和nn_recvmsg)完全匹配POSIX语法和语义。
实现语言
Nanomsg使用c实现而不是c++。
从用户角度来看,这意味着Nanomsg不依赖于c++运行时(libstdc++或类似的运行时),在受限和嵌入式环境中使用会很方便。对开发者来说,Nanomsg让生活变得容易了一些(应该是开发难度降低的意思)。因为使用了侵入式容器(
侵入式容器虽然较非侵入式容器使用起来复杂麻烦,但是它没有非侵入式容器的拷贝、克隆等要求,也可以保存抽象类,对内存管理的要求很低,而且允许定制数据结构,所以通常能提供更好的性能)而不是c++ STL容器,需要分配内存的数量大大减少。上述的变化也意味着内存碎片更少,cache命中率更高等。
更多的关于c和c++的比较可以在
这里和
这里看到。
插件式传输方式和协议
在ZeroMQ中,没有正式的关于新的传输方式(想想WebSockets, DCCP, SCTP)和新的协议(REQ/REP, PUB/SUB等)的插件式API。因此自2008年以来ZeroMQ没有添加新的传输,也没有实现新的协议。正式的内部传输API(参见
transport.h 和
protocol.h)旨在缓解问题,同时作为创建和试验新的传输和协议的基础。
请注意,这两类API仍然是很新的,并且在将来为了适用更多不同的场景可能会进行一些调整。
Nanomsg实现了一种新的SURVEY(调查)协议。这个想法是向多个对等点发送一条消息(“调查”)并等待所有接收点的回应。有关更多细节,请参阅
本文。也可以看
这里。
在金融服务中,使用“将任何人的信息传递给其他人”这类模式的信息是很常见的。为了应对这种应用场景,nanomsg中实现了一种新的总线(BUS)协议。点击
查看细节。
线程模型
我在ZeroMQ中犯了的重大架构错误之一是线程模型。每个独立对象只由一个单独线程管理,对于由工作线程处理的异步对象来说,这种模型运行良好。但对于由用户线程管理的对象来说这成了一个麻烦。线程允许在任意时间跨度内(例如一个小时)执行不相关的工作,在此期间,它所管理的对象被完全阻塞。随之造成了一些不良的后果:在REQ/REP协议中无法实现请求重发,在应用程序执行其他工作时,PUB/SUB订阅无法生效等。在nanomsg中,对象没有和特定线程紧密绑定,因此这些问题将不存在。
ZeroMQ中的REQ套接字不能真正用于实际环境中,因为由于服务故障或类似原因导致消息丢失,REQ套接字就会卡住。用户不得不使用XREQ来替代REQ并实现请求重新尝试。使用nanomsg, retry功能被内置到REQ套接字中。
在nanomsg中,REQ和REP都支持取消正在进行的处理。简单地发送一个新的请求而不等待回复(在使用REQ套接字时)或获取一个新的请求而不需要回复上一个请求(在使用REP套接字时)。
在ZeroMQ中,由于它的线程模型,先绑定后连接的场景不适用于进程内传输。在nanomsg中这个问题被解决了。
基于类似的原因,在ZeroMQ中,进程内传输时自动重连不起作用。这个问题在nanomsg中也解决了。
最后,Nanomsg套接字线程安全,虽然仍然不鼓励多个线程并行操作单个套接字。在使用ZeroMQ套接字时,类似场景会发生的随机失败令人头疼且难于调试。
状态机
Nanomsg库中的内部交互基于一组状态机建模。这种设计的目的是避免像ZeroMQ中所看到的难以理解的关闭机制,从而使基于Nanomsg库的开发更加容易。更多的讨论参考
这里和
这里。
支持IOCP
ZeroMQ有一个长期存在的问题是它在内部使用BSD套接字API,即使在Windows平台上也是如此。使用IOCP将需要对代码进行重大重写,因此,尽管进行了多次尝试,但从未实现。IOCP应该具有更好的性能,更重要的是,它允许使用额外的传输机制,如通过BSD套接字API无法访问的命名管道。基于这些原因,nanomsg在Windows平台上内部使用IOCP。
条件触发
对于用户来说,ZeroMQ令人困惑的地方是,它可以使用ZMQ_FD文件描述符将ZeroMQ套接字集成到外部事件循环中。造成混淆的主要原因是描述符是边触发的,也就是说,它只在之前没有消息且新消息到达时才发出信号。
nanomsg使用条件触发的文件描述符,当有信息可用时触发,不需要关注过去是否可用,这简化了信号系统。
路由优先级
nanomsg实现了输出通讯的优先级。您可以决定将消息优先路由到特定的目的地,只有在主目的地不可用时退回到替代目的地。更多讨论看
这里。
异步DNS
DNS查询(即将主机名转换为IP地址)是以异步方式进行的。在ZeroMQ中,这样的查询是同步进行的,这意味着当DNS不可用时,整个库(包括没有使用DNS的套接字)就会挂起。
零拷贝
虽然ZeroMQ提供了一个“零拷贝”API,但它并不是真正的零拷贝。相反,它是“在消息到达内核边界之前的零拷贝”。从那时起,数据就像标准TCP一样被复制。另一方面,nanomsg的目标是支持真正的零拷贝机制,比如RDMA (CPU旁路、直接内存到内存复制)和共享内存(通过共享内存实现同一台机器上进程间的传输数据)。用于零拷贝消息传递的API入口点是nn_allocmsg和nn_freemsg函数,并结合NN_MSG选项传递给send/recv函数的。
高效的订阅匹配
在ZeroMQ中,使用简单树用于存储和匹配发布/订阅。这种订阅机制能满足超过10,000个订阅用户,这种情况下,简单树工作良好。但是,当有用户需要处理多达1.5亿的订阅时,需要更有效的数据结构。因此,nanomsg使用了高效的前缀树来替代简单树。更多信息详见
这里。
统一缓存模型
ZeroMQ有一种奇怪的双缓冲行为。传出和传入数据都存储在消息队列和TCP的tx/rx缓冲区中。例如,这意味着如果您想限制传出数据量,您必须同时设置ZMQ_SNDBUF和ZMQ_SNDHWM套接字选项。考虑到两者之间没有语义差异,nanomsg仅使用TCP(或等效的)缓冲区来存储数据。
可扩展协议
最后,在哲学层面上,nanomsg的目标是实现不同的“可伸缩性协议”,而不是成为一个通用的网络库。具体地说:
不同的协议是完全分开的,你不能连接REQ套接字到SUB或类似的套接字。
每个协议都包含一套具有明确定义的先决条件的分布式算法(比如,在REQ/REP情况下,“服务必须是无状态的”)和保证机制(如果REQ套接字保持活动,请求将最终被处理)。
部分故障由协议处理,而不是由用户处理。事实上,这些对用户是透明的。
协议的规格说明在/rfc子目录中。
目标是通过IETF标准化协议。
没有类似于udp的套接字(ZMQ_ROUTER),您应该使用L4协议来实现这种功能。
-----------------------------------------------------------------------------
之前读别人翻译的文章,经常觉得拗口难读,对于翻译作者的水平也嗤之以鼻。没想到等自己翻译的时候,自己成了自己曾经鄙视的那一类人,原来翻译能做到“信达雅”真的挺难。如有理解错误之处,请读者及时指出,我及时修改。