`

ZeroMQ:现代和快速的网络协议栈

阅读更多

Berkeley 套接字BSD)是所有网络通信共同使用的API。建立于20世纪80年代初期,它是TCP / IP协议族的原始实现,是目前任何操作系统广泛支持的必备组件。大部分人都很熟悉BSD 套接字中的对等连接,这需要显式的建立,断开,选择运输协议(TCPUDP),错误处理,等等。当这些工作完成时,你就来到了应用层协议(例如 HTTP)的世界,这时你需要额外的数据格式(frame),缓冲,以及处理逻辑。这也难怪一个高性能的网络应用程序并不容易编写。

如果我们可以将诸如不同的套接字类型,连接处理,数据格式,甚至是路由等低级别的细节抽象出来岂不是会更好?这就是 ZeroMQ ØMQ/ ZMQ 网络库产生的初衷 它提供在不同传输层传送消息的套接字: INPROCIPCTCP,多播等,你还可以使用诸如扇出(fanout),发布订阅(pubsub),任务分发(task distribution)和请求应答(request-reply)的消息模式建立多对多的套接字连接。这些都是目前很火的概念,那么让我们更详细的剖析这些概念。

面向消息 vs. 面向数据流数据报

ZeroMQ 套接字在传统套接字 API 之上提供了一个抽象层。ZeroMQ 套接字能够隐藏不得不在应用程序中反复使用的机械复杂底层处理逻辑。ZeroMQ抽象层的通信不是面向数据流(TCP面向数据报(UDP,而是面向消息的。如果一个客户端套接字发送一个150KB的消息,那么在另一端的服务端套接字将收到一个完整的同样的消息,而无需实现任何显式的缓冲区和数据格式(frame)。当然,我们仍然可以实现数据流接口,但这样做需要一个显式的应用层协议。



# create zeromq request / reply socket pair
ctx = ZMQ::Context.new
req = ctx.socket ZMQ::REQ
rep = ctx.socket ZMQ::REP

# connect sockets: notice that reply can connect first even with no server!
rep.connect('tcp://127.0.0.1:5555')
req.bind('tcp://127.0.0.1:5555')
req.send ZMQ::Message.new('hello' * (1024*1024))

msg = ZMQ::Message.new
rep.recv(msg)
msg.copy_out_string.size # => 5242880


 面向数据/数据报转换到面向消息似乎是一个微小的变化,但这一微小的变化改变了事情的本质。因为ZeroMQ处理所有的缓冲和数据格式,客户端和服务器应用程序将获得数量级上的改变:更简单,更安全,更容易编写。

与传输层无关的套接字


ZeroMQ套接字是与传输层无关的:ZeroMQ套接字对所有传输层协议定义了统一的API接口。默认支持 进程内(inproc) 进程间(IPC) 多播 TCP协议,在不同的协议之间切换只要简单的改变连接字符串的前缀。也就是说我们可以在任何时候以最小的代价从进程间的本地通信切换到分布式下的TCP通信。ZeroMQ在背后处理连接建立,断开和重连逻辑,这使得应用变得非常简单


能够感知路由和拓扑的套接字


ZeroMQ套接字能够理解路由和网络拓扑结构。如前所述,ZeroMQ管理了对等连接的状态, ZeroMQ套接字可以绑定到两个不同的端口来监听入站请求 ,或通过单个API调用将数据发送到两个不同的套接字。那么ZeroMQ如何确定监听哪个端口,或将数据发送给谁?这取决于应用程序选择哪种成对的套接字类型:请求/响应 发布/订阅 推送/拉取(pipeline) 还是对等连接 测试中)。

ctx = ZMQ::Context.new

# create publisher socket, and publish to two pipes!
pub = ctx.socket(ZMQ::PUB)
pub.bind('tcp://127.0.0.1:5000')
pub.bind('inproc://some.pipe')

# generate random message, ex: '1 9'
Thread.new { loop { pub.send [rand(2), rand(10)].join(' ') } }

# create a consumer, and listen for messages whose key is '1'
sub = ctx.socket(ZMQ::SUB)
sub.connect('inproc://some.pipe')
sub.setsockopt(ZMQ::SUBSCRIBE, '1')

loop { p sub.recv } # => "1 9" ...

在使用发布/订阅套接字对时(从发布者到订阅者的单向通信),发布者套接字会将消息复制并发送给所有连接的客户(本地的IPC客户端,远程TCP监听器等)。在使用请求/应答套接字对时(服务器与客户端的双向通信),消息会自动负载均衡并发送到某个已连接的客户端套接字。使用推送/拉取套接字组合时(管道,具有负载均衡的单向通信),推送方对消息进行负载均衡并发送给拉取方。





 ZeroMQ可以直接通过套接字接口定义网络拓扑结构,而无需定义和维护单独的路由,负载均衡,消息中间件等协作层。当然,ZeroMQ不会阻止我们使用任何这些工具,但在许多情况下,使用ZeroMQ路由能够获得更好的性能和更低的操作复杂性。


ZeroMQ背后做了什么


默认情况下,ZeroMQ的通信都是以异步方式完成的。ZeroMQ应用程序,如果要启用异步方式,需要显式的定义后台 I / O线程的数量。在大多数情况下,一个专用的I / O线程就够用了。所有线程的核心逻辑都由ZeroMQC++库处理。这意味着,应用程序至少需要两个可以调度的线程。



这种异步处理模型使得ZeroMQ能够抽象所有连接建立,中断,重连逻辑,同时也能最小化消息传递延迟:非阻塞意味着消息的分发,传递和排队(发送方或接收方)可以并行应用程序的处理过程。当然,应用也可以通过设置每个ZeroMQ套接字允许使用的内存或是交换空间的大小来控制套接字的排队行为。因此,虽然异步I/O是默认的,应用如果需要也可以模拟出阻塞I / O的行为ZeroMQ将零拷贝,框架优化,无锁数据结构结合在一起,形成了一个高性能高吞吐的,具有现代API的面向消息中间件。



Mongrel2: ZeroMQ用例


Mongrel2 web服务器是使用ZeroMQ的很有意思的一个用例:所有入站请求都通过推送插套接字自动负载均衡到已连接的处理器(handler)。这些处理器处理通过拉取套接字获取传入的请求,并进行处理,然后将请求发布到Mongrel2服务器上的发布者套接字,Mongrel2订阅发布者套接字并通过主题过滤器获取处理器的进程ID

这一做法与通常情况的在后台完成整个请求的处理过程不同,处理过程不再依赖整个请求 - 响应处理周期,而是通过管道模式将处理过程分为几个阶段,当所有阶段都处理完成时再将响应发出。




ZeroMQ的目标和其他话题

 毫无疑问ZeroMQ是一个有雄心的项目,这个简短的介绍只涉及完整功能集的一部分。ZeroMQ的既定目标是 成为标准的网络协议栈的一部分,进而成为Linux内核协议栈的一部分目标是否会达成还有待观察,但它绝对是在传统BSD套接字之上的,富有前途的非常有用的抽象层。ZeroMQ使得编写高性能网络应用,超乎想象的方便和有趣。

开始使用ZeroMQ最好的法是通过一些实际操作的例子。ZeroMQ的想法并不是很新,但还是需要一些时间才可以熟练的使用它。对于Ruby开发者,Andrew Cholakian制作了一个快速上手非常棒例子集点击dripdrop检出代码),对于其他语言可以访问ZeroMQ网站获取对应语言绑定的源码以深入了解。


 


以上翻译自http://www.igvita.com/2010/09/03/zeromq-modern-fast-networking-stack/

 

  • 大小: 24.6 KB
  • 大小: 29.7 KB
  • 大小: 25.6 KB
  • 大小: 35.7 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics