下面是自定义I/O线程数量的方法:
int io_threads = 4; void * context = zmq_ctx_new(); zmq_ctx_set(context, ZMQ_IO_THREADS, io_threads); assert(zmq_ctx_get(context, ZMQ_IO_THREADS) == io_threads);回想一下你用linux socket + epoll编写服务端应用程序的套路, 一般都是一个tcp连接专门开一个线程. ZMQ不一样, ZMQ允许你在一个进程里持有上千个连接(不一定是TCP哦), 但处理这上千个连接的I/O作业, 可能只有一个, 或者几个线程而已, 并且事实也证明这样做是可行的. 可能你的进程里只有十几个线程, 但就是能处理超过上千个连接.
当你的程序只使用inproc作为通讯手段的时候, 其实是不需要线程来处理异步I/O的, 因为inproc是通过共享内存实现通讯的. 这个时候你可以手动设置I/O线程的数量为0. 这是一个小小的优化手段, 嗯, 对性能的提升基本为0.
套路, 套路, 套路ZMQ的设计是亲套路的, ZMQ的核心其实在于路由与缓存, 这也是为什么作为一个网络库, 它更多的被人从消息队列这个角度了解到的原因. 要用ZMQ实现套路, 关键在于使用正确的socket类型, 然后把拓扑中的socket组装配对起来. 所以, 要懂套路, 就需要懂zmq里的socket类型.
zmq提供了你构建如下套路的手段:
请求-应答套路. 多对多的客户端-服务端模型. 用于远程调用以及任务分发场景.
发布-订阅套路. 多对多的喇叭-村民模型. 用于数据分发场景.
流水线套路. 用于并行作业处理场景.
一夫一妻套路. 一对一的连接模型. 这一般用于在进程中两个线程进行通讯时使用.
我们在第一章中已经大致接触了套路, 除了一夫一妻没有接触到, 这章稍后些部分我们也将接触这种套路.要了解具体socket的各个类型都是干嘛用的, 可以去阅读zmq_socket()的manpage, 我建议你去阅读, 并且仔细阅读, 反复阅读.下面列出的是可以互相组合的socket类型. 双方可以替换bind与connect操作.
PUB SUB. 经典的发布-订阅套路
REQ REP. 经典的请求-应答套路
REQ ROUTER (注意, REQ发出的数据中, 以一个空帧来区分消息头与消息体)
DEALER REP(注意, REP假定收到的数据中, 有一个空帧用以区分消息头与消息体)
DEALER ROUTER
DEALER DEALER
ROUTER ROUTER
PUSH PULL. 经典的流水线套路.
PAIR PAIR. 一夫一妻套路
后续你还会看到有XPUB与XSUB两种类型的socket. 就目前来说, 只有上面的socket配对连接是有效的, 其它没列出的组合的行为是未定义的, 但就目前的版本来说, 错误的组合socket类型并不会导致连接时出错, 甚至可能会碰巧按你的预期运行, 但强烈不建议你这个瞎jb搞. 在未来的版本中, 组合非法的socket类型可能会导致API调用出错.
消息, 消息, 消息libzmq有两套收发消息的API接口, 这个之前我们已经讲过. 并且在第一章里建议你多使用zmq_send()与zmq_recv(), 建议你规避zmq_msg_send()与zmq_msg_recv(). 但zmq_recv有一个缺陷, 就是当你提供给zmq_recv()接口的接收buffer不够长时, zmq_recv()会把数据截断. 如果你无法预测你要收到的二进制数据的长度, 那么你只能使用zmq_msg_xxx()接口.
从接口名上的msg三个字母就能看出, 这个系列的接口是操纵结构体, 也就是"消息"(其实是帧, 后面会讲到), 而不是"数据", 而非缓冲区的接口, 实际上它们操纵的是zmq_msg_t类型的结构. 这个系列的接口功能更为丰富, 但使用起来也请务必万分小心.
初始化消息相关的接口: zmq_msg_init(), zmq_msg_init_size(), zmq_msg_init_data()
消息收发接口: zmq_msg_send(), zmq_msg_recv()
消息释放接口: zmq_close()
访问消息内容的接口: zmq_msg_data(), zmq_msg_size(), zmq_msg_more()
访问消息配置项的接口: zmq_msg_get(), zmq_msg_set()
复制拷贝操作接口: zmq_msg_copy(), zmq_msg_move()
消息结构中封装的数据是二进制的, 依然由程序员自己解释. 关于zmq_msg_t结构类型, 下面是你需要知道的基础知识:
去阅读上面的消息相关接口API的manpage, 你会发现传递参数都是以zmq_msg_t *. 也就是说这是一个内部实现不对外开放的类型, 创建, 传递, 都应当以指针类型进行操作.
要从socket中接收一个消息, 你需要先通过zmq_msg_init()创建一个消息对象, 然后将这个消息对象传递给zmq_msg_recv()接口
要向socket中写入消息, 你需要先通过zmq_msg_init_size()创建一个数据容量指定的消息对象, 然后把你要写入的二进制数据通过内存拷贝函数, 比如memcpy()写入消息中, 最后调用zmq_msg_send(), 看到这里你应该明白, zmq_msg_init_size()接口内部进行了内存分配.