我有一个问题,关于在ZMQ中从多个线程发布到单个订户的最佳方式是。我有一个用C ++编写的Web服务器,对于每个连接,都需要向订阅者发送ZMQ消息。这些服务器可以有数百到数千个并发连接,所以我很好奇写入这个用户的最有效方法是什么。我可以想到三种不同的方式。
分享连接
我不相信这种方式会起作用,但基本上只有一个连接指针,并将其传递给所有线程。我假设(当我在ZMQ网站上搜索时找不到太多信息),这将导致竞争条件,因为我不认为zmq_msg_send()是线程安全的。我可以在send上放一个互斥来解决这个问题,但我担心速度,因为我的服务器需要尽可能快。
共享队列
这类似于共享连接,但是以不同的方式,而不是在zmq_msg_send()周围放置一个互斥体,而是在一个共享的消息向量周围发送一个互斥量,并有一个写入线程来处理所有这些消息。我相信这会比之前的方法更快,因为写入向量可能比做zmq_msg_send()快得多,但是,如果可能的话,我想避免等待。
每个帖子的连接
我能想到避免互斥等待的唯一方法是每个线程打开一个zmq连接(这意味着我进入每个连接,作为每个线程一个用户连接的进程)。这可能是可行的,虽然我不知道zmq_connect是如何工作的。在建立连接之前会阻塞吗?理想情况下,我的流程会是这样的:
user_connection()
{
createZMQConnection();
doWork();
sendData();
}
但是,如果创建连接块,则最好使用共享队列。
有没有人提出类似的申请,或者有人知道推荐的模式是什么?任何见解将不胜感激。
编辑:
感谢您的回复,我只是在查看zmq文档,发现了很多你在谈论的内容,但也许你可以澄清一下。
至于我的基础设施,我有很多酒吧到单子。酒吧是连接和sub绑定。
我有多个负载均衡的盒子,每个盒子运行多个线程。总的来说,我看到大约30,000条消息/秒。每盒,我可能会看到大约2,000-4,000条消息/秒。我每次请求的通常处理时间大约是40ms,因此我经常会打开大约100-200个并发实例。我不确定是否会打开200个并发套接字对象,或者这是否是ZMQ做事的方式。根据事物的声音,我应该将zmq :: context传递给每个线程并在那里创建套接字。
这就是我想象的代码将如何流动,如果这看起来正确,请告诉我
void receiveConnection()
{
zmq::context_t context(1);
doWorkClass c(context);
c.run();
}
doWorkClass(context)
{
socket(context, ZMQ_PUB);
}
void doWorkClass::run()
{
sendString = doWork();
s_send(socket,sendString);
}
所以我为所有套接字使用一个上下文,并为每个线程创建一个套接字,完成我的工作,并发送我的消息。
答案 0 :(得分:4)
从您的想法中可以看出,您尝试优化资源并尝试避免访问它们的竞争条件。
ZeroMQ确实看起来“相似”,但工作方式略有不同。忘记指针,忘记阻塞和类似的问题。
这意味着,您可以将多个线程创建为 PUB
,并且有一个 SUB
(或几个,具有负载均衡设计) ),它消耗所有传入的事件流。
根据给出的信息集,似乎需要一个设置,其中:
SUB
-archetype, .bind()
点“并将其自己的订阅(以避免任何过滤)设置为 ""
PUB
原型和 .connect()
实例化为“中心节点”ZeroMQ不鼓励与ZeroMQ套接字共享接入点,这不是一种线程安全的方法,原则上应该避免使用。
ZeroMQ也不鼓励所有“低级别”和系统信令/阻塞手段。
如果需要,可以为线程到线程的软SIG信令创建另一层。状态的控制。
您的所有消息流都可以设计为非阻塞(确实可以轻松地降低性能)。如您所示,您的服务器需要速度。
您的MSG / msec&的峰值速率和持续速率是多少? MB /毫秒有点量化吗?
您可以在StackOverflow和ZeroMQ Web上找到ZeroMQ层的合理性能测试,以便进行比较。
答案 1 :(得分:2)
最佳(我相信单一或最正确)实现目标的方法是使用每线程连接方法。
您需要在线程体中创建新的套接字(作为客户端),建立连接,确保通过实现简单的握手程序并且仅在开始推送数据后才连接。
此处的握手非常重要,因为zmq_connect无法保证在呼叫结束后套接字就可以立即传输。
我通常使用ZMQ_PAIR套接字(当应用程序期望在单个主机上运行时),父级和子级之间共享URL,或者分布式环境使用REQ / REP。