ZeroMQ 教程 002 : 高级技巧

上一章我们简单的介绍了一个ZMQ, 并给出了三个套路的例子: 请求-回应, 订阅-发布, 流水线(分治). 这一章, 我们将深入的探索一下ZMQ中的socket, 以及"套路"

socket API

如果熟悉linux socket编程的同学阅读完了第一章, 一定有一种说不上来的别扭感觉.因为通常情况下, 当我们讨论socket的时候, 我们一般指的是操作系统提供的网络编程接口里的那个socket概念. 而在ZMQ中, 只是借用了这个概念的名字, 在ZMQ中, 我们讨论到socket的时候, 一般指代的是调用zmq_socket()接口返回的那个socket, 具体一点: zmq socket.

zmq socket比起linux socket来说, 逻辑理解起来比较类似, 虽然两者内部完全就不是同一种东西.

socket需要被创建, 以及关闭. zmq_socket(), zmq_close()

socket有配置项. zmq_setsockopt(), zmq_getsockopt()

socket有绑定和连接两种操作. zmq_bind(), zmq_connect()

收发socket上的数据. zmq_msg_send(), zmq_msg_recv(), zmq_send(), zmq_recv()

但与linux socket不同的是, zmq socket没有listen这个逻辑概念.

需要注意的是, zmq socket是void指针, 而消息则是结构实例. 这就意味着, 在C语言的API中, 需要zmq socket的地方, 传递的一定是值, 而需要传递消息的时候, 比如使用zmq_msg_send()和zmq_msg_recv()这样的接口, 消息参数则传递其地址. 其设计哲学是: 在zmq中, socket不归程序员掌控, 所以你可能拿到一个句柄(地址), 但不能看到它长什么样(不能看到socket实例), 但消息是程序员创建的, 是受程序员掌控的.

将socket接入网络拓扑中

在两个结点上用ZMQ实现通讯, 你需要分别为两个结点创建socket, 并在其中一个结点上调用zmq_bind(), 在另一个结点上创建对应的zmq_connect(). 在ZMQ中, 请不要再以死板的"客户端", "服务端"来区分网络结点. 而要这样理解: zmq_bind()调用应该发生在网络拓扑中那些不易变的结点上, 而zmq_connect()应该发生在网络拓扑中那些易变的结点上.

ZMQ建立起的数据连接和常见的TCP连接有一些不同, 但也有一些共通之处, 如下:

TCP是TCP/IP协议栈的四层协议, 当建立一个TCP连接的时候, 双方都必须使用TCP/IP协议栈. 从这个角度看, ZMQ是四层之上的4.5层, ZMQ下面统一了很多连接协议, 对于TCP/IP协议栈来说, ZMQ下面有TCP, 除了TCP/IP, ZMQ还能通过共享内存在线程间建立连接, 在进程间建立连接(具体连接手段对上层是透明的), 使用TCP/IP协议栈的PGM协议(建立在IP层上的一种多播协议)建立连接.

在linux socket中, 一个连接就是一个socket, 但在ZMQ中, 一个socket上可以承载多个数据连接. 这里socket和connection不再是同个层次上的等价词汇, 要把socket理解为程序员访问数据连接的一个入口, 一个大门, 门推开, 可能有多个连接, 而不止一个. 有多个数据流等待吞吐.

上面说了, 用ZMQ在结点间建立连接, 程序员操作ZMQ相关API的时候, 实际上位于的是类似于TCP/IP里的第4.5层, 反过来看, 即具体的连接是如何建立, 如何保持, 如何维护的, 这ZMQ库的工作, 不应该由使用ZMQ库的人去关心. 也就是说, 自从使用了ZMQ库, 你再也不需要关心TCP是如何握手了. 并且, 对于合适的协议, 对端结点上线下线时, ZMQ库将负责优雅的处理接连中断, 重试连接等脏活.

再次重申, 如何连接, 是ZMQ库的工作, 你不应该插手. 你只需要关心数据, 套路, 拓扑.

在请求-回应套路中, 我们把比较不易变的逻辑结点称为服务端, 把易变, 也就是会经常性的退出, 或重新加入网络拓扑的结点称为客户端. 服务端向外提供服务, 必须提供一个"地址"供客户端去上门, 换句话说, 在这个套路拓扑中, 那些经常来来去去的客户端应该知道去哪找服务端. 但反过来, 服务端完全不关心去哪找客户端, 你爱来不来, 不来就滚, 不要打扰我飞升. 对于不易变的结点, 应该使用zmq_bind()函数, 对于易变的结点, 应该采用zmq_connect

在传统的linux socket编程中, 如果服务端还没有上线工作, 这个时候去启动客户端程序, 客户端程序的connect()调用会返回错误. 但在ZMQ中, 它妥善处理了这种情况. 客户端调用zmq_connect(), 不会报错, 仅会导致消息被阻塞而发不出去.

不要小看这一点设计, 它反映出ZMQ的设计思想: 在请求-应答套路中, 它不光允许客户端可以随时退出, 再回来. 甚至允许服务端去上个厕所.

另外, 一个服务端可以多次调用zmq_bind()以将自己关联到多个endpoint上.(所谓的endpoint, 就是通讯协议+通讯地址的组合, 它一般情况下指代了在这种通讯协议中的一个网络结点, 但这个结点可以是逻辑性的, 不一定只是一台机器).这就意味着, zmq socket可以同时接受来自多个不同通讯协议的多簇请求消息.

zmq_bind(socket, "tcp://*:5555"); zmq_bind(socket, "tcp://*:999"); zmq_bind(socket, "inproc://suprise_motherfucker");

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypgfs.html