C++ 为什么ZeroMQ推/拉工作,而不是发布/订阅?

C++ 为什么ZeroMQ推/拉工作,而不是发布/订阅?,c++,protocol-buffers,zeromq,publish-subscribe,distributed-computing,C++,Protocol Buffers,Zeromq,Publish Subscribe,Distributed Computing,环境:NVIDIA在Jetson开发板上用TX2i处理器安装了NVIDIA风格的Ubuntu18.01。ZMQ4.3.2,利用C++包装器对ZMQ. 我已经有很多代码使用google protocol buffers和ZeroMQ运行,它们都是PUSH/PULL,并且运行良好,除了我有一个不是点对点,而是1:3的例子。这里正确的解决方案是发布/订阅,但我无法将消息传递给我的订阅者 我将代码简化为这个简单的示例。如果我取消注释#define语句,订阅者将一无所获。注释(编译为PUSH/PULL而不

环境:NVIDIA在Jetson开发板上用TX2i处理器安装了NVIDIA风格的Ubuntu18.01。ZMQ4.3.2,利用C++包装器对ZMQ.</P> 我已经有很多代码使用google protocol buffers和ZeroMQ运行,它们都是PUSH/PULL,并且运行良好,除了我有一个不是点对点,而是1:3的例子。这里正确的解决方案是发布/订阅,但我无法将消息传递给我的订阅者

我将代码简化为这个简单的示例。如果我取消注释
#define
语句,订阅者将一无所获。注释(编译为PUSH/PULL而不是PUB/SUB),则订阅者按预期获得消息。由于()的
sleep\u次数过多,我希望订阅者在发布者执行发送之前有足够的时间进行注册

编辑:

为什么要尝试/捕获订阅服务器?我很早就遇到了一个例外,我认为这是因为出版商还没有准备好。这似乎不再是事实,所以它不是我想象的那样

// Publisher
#include "/usr/local/include/zmq.hpp"
#include "protobuf_namespace.pb.h"
#include <chrono>
#include <thread>


#define PUB_SUB

int main( void )
{
  zmq::context_t* m_pContext = new zmq::context_t( 1 );

#ifdef PUB_SUB
  zmq::socket_t*  m_pSocket  = new zmq::socket_t( *m_pContext, ZMQ_PUB );
#else
  zmq::socket_t*  m_pSocket  = new zmq::socket_t( *m_pContext, ZMQ_PUSH );
#endif

  std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
  //m_pSocket->bind( "tcp://*:53001" );       // using '*' or specific IP doesn't change result
  m_pSocket->bind( "tcp://127.0.0.1:53001" );
  std::this_thread::sleep_for( std::chrono::seconds( 1 ) );

  // Send the parameters
  protobuf_namespace::Params params;
  params.set_calibrationdata( protobuf_namespace::CalDataType::CAL_REQUESTED ); // init one value to non-zero
  std::string        params_str = params.SerializeAsString();
  zmq::message_t     zmsg( params_str.size() );

  memcpy( zmsg.data(), params_str.c_str(), params_str.size() );
  m_pSocket->send( zmsg, zmq::send_flags::none );

  std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
  m_pSocket->close();
  zmq_ctx_destroy( m_pContext );
}
//发布者
#包括“/usr/local/include/zmq.hpp”
#包括“protobuf_namespace.pb.h”
#包括
#包括
#定义PUB_SUB
内部主(空)
{
zmq::context_t*m_pContext=新的zmq::context_t(1);
#ifdef PUB_SUB
zmq::socket\u t*m\u pSocket=new zmq::socket\u t(*m\u pContext,zmq\u PUB);
#否则
zmq::socket\u t*m\u pSocket=新的zmq::socket\u t(*m\u pContext,zmq\u PUSH);
#恩迪夫
std::this_thread::sleep_for(std::chrono::seconds(1));
//m_pSocket->bind(“tcp://*:53001”);//使用“*”或特定IP不会改变结果
m_pSocket->bind(“tcp://127.0.0.1:53001" );
std::this_thread::sleep_for(std::chrono::seconds(1));
//发送参数
protobuf_名称空间::Params Params;
params.set_calibrationdata(protobuf_名称空间::CalDataType::CAL_请求);//将一个值初始化为非零
std::string params_str=params.SerializeAsString();
zmq::message_t zmsg(params_str.size());
memcpy(zmsg.data(),params_str.c_str(),params_str.size());
m_pSocket->send(zmsg,zmq::send_标志::none);
std::this_thread::sleep_for(std::chrono::seconds(1));
m_pSocket->close();
zmq_ctx_destroy(m_pContext);
}
//订户-先启动我!
#包括“/usr/local/include/zmq.hpp”
#包括“protobuf_namespace.pb.h”
#包括
#包括
#包括
#定义PUB_SUB
内部主(空)
{
zmq::context_t*m_pContext=新的zmq::context_t(1);
#ifdef PUB_SUB
zmq::socket\u t*m\u pSocket=新的zmq::socket\u t(*m\u pContext,zmq\u SUB);
m_pSocket->connect(“tcp://127.0.0.1:53001" );
int-linger=0;
zmq_setsockopt(m_pSocket,zmq_LINGER和LINGER,sizeof(LINGER));
zmq_setsockopt(m_pSocket,zmq_SUBSCRIBE,”,0);
#否则
zmq::socket_t*m_pSocket=新的zmq::socket_t(*m_pContext,zmq_PULL);
m_pSocket->connect(“tcp://127.0.0.1:53001" );
#恩迪夫
protobuf_名称空间::Params Params;
zmq::message_t zmsg;
bool retry=true;
做{
试一试{
m_pSocket->recv(zmsg,zmq::recv_标志::无);
重试=错误;
std::this_thread::sleep_for(std::chrono::seconds(1));
}捕获(…){
printf(“捕获\n”);
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}while(重试);
std::string param_str(static_cast(zmsg.data()),zmsg.size());
params.ParseFromString(param_str);
if(params.calibrationdata()==protobuf_名称空间::CalDataType::CAL_请求)
printf(“已请求校准”);
其他的
printf(“坏数据”);
std::this_thread::sleep_for(std::chrono::seconds(1));
m_pSocket->close();
zmq_ctx_destroy(m_pContext);
}
如果您从未使用过ZeroMQ,
在这里,我们可以先看一下“”
,然后再深入了解更多细节

Q为什么要尝试/抓住订阅者

因为:
a)
//订户-先启动我tcp://
传输类路径设置以接受
.bind()
中的任何第一个
.connect()
,在此之前进行大量的小睡…
std::this_thread::sleep_for(std::chrono::seconds(1))

b)try
d
m\u pSocket->recv(zmsg,zmq::recv\u flags::none)必须抛出异常,因为到目前为止没有
tcp://
传输类路径设置(因为
发布
-端尚未从睡眠中返回)

Q:为什么ZeroMQ推/拉工作,而不是发布/订阅

好吧,如果设计得当,两者都将遵守已发布的API

只需移除任何阻塞
sleep()
-s,阻止
-s加入,使
.connect()
-s能够尽快成功。此外,还可以采用非阻塞形式的
.recv()
-ops(refactor
try/catch
),这在中很常见,以更好地反映基于预防性设计的
.poll()
或反应性
.recv(…,ZMQ\u NOBLOCK)
的事件处理的性质


最后,但并非最不重要的是: ZeroMQ v4+(与v2+和v3之前的。?API相反)已切换为使用
PUB
端消息过滤,因此必须适当考虑订阅管理(定时/错误处理/弹性)

在任何怀疑的情况下,可以集成使用ZeroMQ内置的
socket\u monitor
工具,扩展
Context()
-实例,并跟踪/检查
Context()
-实例内部的每个事件,直到发布的API事件的最低细节级别

如果您从未使用过ZeroMQ,请不要犹豫,
在这里,我们可以先看一下“”
,然后再深入了解更多细节

Q// Subscriber - start me first! #include "/usr/local/include/zmq.hpp" #include "protobuf_namespace.pb.h" #include <chrono> #include <thread> #include <stdio.h> #define PUB_SUB int main( void ) { zmq::context_t* m_pContext = new zmq::context_t( 1 ); #ifdef PUB_SUB zmq::socket_t* m_pSocket = new zmq::socket_t( *m_pContext, ZMQ_SUB ); m_pSocket->connect( "tcp://127.0.0.1:53001" ); int linger = 0; zmq_setsockopt( m_pSocket, ZMQ_LINGER, &linger, sizeof( linger ) ); zmq_setsockopt( m_pSocket, ZMQ_SUBSCRIBE, "", 0 ); #else zmq::socket_t* m_pSocket = new zmq::socket_t( *m_pContext, ZMQ_PULL ); m_pSocket->connect( "tcp://127.0.0.1:53001" ); #endif protobuf_namespace::Params params; zmq::message_t zmsg; bool retry = true; do { try { m_pSocket->recv( zmsg, zmq::recv_flags::none ); retry = false; std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); } catch( ... ) { printf("caught\n"); } std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); } while( retry ); std::string param_str( static_cast<char*>( zmsg.data() ), zmsg.size() ); params.ParseFromString( param_str ); if( params.calibrationdata() == protobuf_namespace::CalDataType::CAL_REQUESTED ) printf( "CAL_REQUESTED\n" ); else printf( "bad data\n" ); std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); m_pSocket->close(); zmq_ctx_destroy( m_pContext ); }