所以,我正在探索0MQ作为我的集群应用程序的消息传递协议。它提供了我需要的那种异步通信。
到目前为止,我已经初步成功制作了一个原型。我可以使用几种不同类型的模式发送和接收消息。
我目前正在尝试通过 PUB/SUB
“握手”获得 REP/REQ
模式以进行初始同步。 ZeroMQ指南在“节点协调”下描述了这种方法。而且该方法效果很好。唯一的问题是,0MQ似乎占据了我信息的50%,或者至少是其中的数据。
我正在使用Qt,因此消息由QString
组成。我使用 QByteArray
将字符串字节打包为 QDataStream
。然后,我将QByteArray
传递到“电线”上并在另一侧打开包装。我经常使用这种方法通过其他协议(如tcp套接字)进行通信,效果很好。
我有4名“工作人员”通过PUB/SUB
和REQ/REP
连接到单个“经理”。在大多数运行中,至少1个,最多3个“工人”同步很好。但是,当他们不这样做时,他们会收到一个空字符串。
以下是4个客户的日志:
alicia
:
[09:18:15.337] [Info] Logging initialized
[09:18:15.337] [Debug] Initializing client
alicia
[09:18:15.337] [Debug] Attempting to receive START signal from server
[09:18:15.340] [Info] Received message:
START
[ 09:18:15.341] [Debug] Recieved a SYNC message
SYNC
[09:18:15.341] [Info] Received START signal from manager
[09:18:15.341] [Info] Received message:
2
brenda
:
[09:18:15.337] [Info] Logging initialized
[09:18:15.337] [Debug] Initializing client
brenda
[09:18:15.337] [Debug] Attempting to receive START signal from server
[09:18:15.340] [Info] Received message:
START
[ 09:18:15.340] [Debug] Recieved a SYNC message
SYNC
[09:18:15.340] [Info] Received START signal from manager
[09:18:15.340] [Info] Received message:
START
[09:18:15.340] [Debug] Sending
0
th message
carlie
:
[09:18:15.336] [Info] Logging initialized
[09:18:15.337] [Debug] Initializing client
carlie
[09:18:15.337] [Debug] Attempting to receive START signal from server
[09:18:15.340] [Info] Received message:
START
[ 09:18:15.340] [Debug] Recieved a SYNC message
[09:18:15.340] [Fatal] carlie
Caught unhandled WASError:
Assertion "SOMEPATH/zmqworker.cpp" failed at SOMEPATH/zmqworker.cpp:52:virtual void was::ZMQWorker::run(): Invalid sync message
darcie
:
[09:18:15.336] [Info] Logging initialized
[09:18:15.337] [Debug] Initializing client
darcie
[09:18:15.337] [Debug] Attempting to receive START signal from server
[09:18:15.340] [Info] Received message:
START
[ 09:18:15.341] [Debug] Recieved a SYNC message
[09:18:15.341] [Fatal] darcie
Caught unhandled WASError:
Assertion "SOMEPATH/zmqworker.cpp" failed at SOMEPATH/zmqworker.cpp:52:virtual void was::ZMQWorker::run(): Invalid sync message
以下是大部分代码:
来自 zmqtools.cpp
void sendQString( zmq::socket_t& socket, QString& str )
{
QByteArray package;
QDataStream packer( &package, QIODevice::WriteOnly );
packer << str;
zmq::message_t msg( package.data(), package.size(), NULL );
WAS_ASSERT_MSG( socket.send( msg ), "Failed to send QString" );
}
void recvQString( zmq::socket_t& socket, QString& str )
{
zmq::message_t msg;
WAS_ASSERT_MSG( socket.recv( &msg ), "Failed to receive QString" );
QByteArray package( ( char* )msg.data(), msg.size() );
QDataStream unpacker( &package, QIODevice::ReadOnly );
unpacker >> str;
}
来自 zmqworker.cpp
ZMQWorker::ZMQWorker( const QString& clientName, QObject *parent ) :
QThread( parent ),
clientName( clientName ),
context( 1 ),
inMessageSocket( context, ZMQ_PULL ),
outMessageSocket( context, ZMQ_PUSH ),
inControlSocket( context, ZMQ_SUB ),
synchronizeSocket( context, ZMQ_REQ )
{
qxtLog->debug() << "Initializing client " << clientName;
inMessageSocket.connect( "tcp://localhost:9900" );
outMessageSocket.connect( "tcp://localhost:9901" );
inControlSocket.connect( "tcp://localhost:9902" );
inControlSocket.setsockopt( ZMQ_SUBSCRIBE, "", 0 );
synchronizeSocket.connect( "tcp://localhost:9903" );
messagePoll.fd = 0;
messagePoll.events = ZMQ_POLLIN;
messagePoll.revents = 0;
messagePoll.socket = inMessageSocket;
controlPoll.fd = 0;
controlPoll.events = ZMQ_POLLIN;
controlPoll.revents = 0;
controlPoll.socket = inControlSocket;
}
void ZMQWorker::run()
{
QString message;
// Wait for start signal from server
bool started = true;
do
{
qxtLog->debug() << "Attempting to receive START signal from server";
recvQString( inControlSocket, message );
qxtLog->info() << "Received message: " << message;
started = message == "START";
} while( !started );
message = "SYNC";
sendQString( synchronizeSocket, message );
recvQString( synchronizeSocket, message );
qxtLog->debug() << "Recieved a SYNC message" << message;
WAS_ASSERT_MSG( message == "SYNC", "Invalid sync message" );
qxtLog->info() << "Received START signal from manager";
int messagesSent = 0;
forever
{
zmq::poll( &messagePoll, 1, 0 );
if( messagePoll.revents & ZMQ_POLLIN )
{
recvQString( inMessageSocket, message );
qxtLog->info() << "Received message: " << message;
}
zmq::poll( &controlPoll, 1, 0 );
if( controlPoll.revents & ZMQ_POLLIN )
{
recvQString( inControlSocket, message );
qxtLog->info() << "Received message: " << message;
if( message == "STOP" )
break;
}
if( messagesSent < 1000 )
{
qxtLog->debug() << "Sending " << messagesSent << "th message";
QString message = QString::number( messagesSent );
sendQString( outMessageSocket, message );
messagesSent++;
}
}
}
来自 zmqmanager.cpp
ZMQManager::ZMQManager( const QString& serverName, unsigned clientCount, QObject* parent ) :
QThread( parent ),
serverName( serverName ),
clientCount( clientCount ),
context( 1 ),
inMessageSocket( context, ZMQ_PULL ),
outMessageSocket( context, ZMQ_PUSH ),
outControlSocket( context, ZMQ_PUB ),
synchronizeSocket( context, ZMQ_REP )
{
qxtLog->debug() << "Initializing server " << serverName;
outMessageSocket.bind( "tcp://*:9900" );
inMessageSocket.bind( "tcp://*:9901" );
outControlSocket.bind( "tcp://*:9902" );
synchronizeSocket.bind( "tcp://*:9903" );
messagePoll.fd = 0;
messagePoll.events = ZMQ_POLLIN;
messagePoll.revents = 0;
messagePoll.socket = inMessageSocket;
synchronizePoll.fd = 0;
synchronizePoll.events = ZMQ_POLLIN;
synchronizePoll.revents = 0;
synchronizePoll.socket = synchronizeSocket;
}
void ZMQManager::run()
{
QString message;
unsigned clientsConnected = 0;
do
{
qxtLog->debug() << "Publishing START signal";
message = "START";
sendQString( outControlSocket, message );
zmq::poll( &synchronizePoll, 1, 5000 );
if( synchronizePoll.revents & ZMQ_POLLIN )
{
qxtLog->debug() << "Checking for response to START signal";
recvQString( synchronizeSocket, message );
qxtLog->debug() << "Recieved a SYNC message" << message;
WAS_ASSERT_MSG( message == "SYNC", "Invalid sync message" );
sendQString( synchronizeSocket, message );
clientsConnected++;
}
} while( clientsConnected < clientCount );
qxtLog->info() << "Started and syncrhonized with clients";
unsigned messagesSent = 0;
unsigned messagesReceived = 0;
do
{
zmq::poll( &messagePoll, 1, 0 );
if( messagePoll.revents & ZMQ_POLLIN )
{
recvQString( inMessageSocket, message );
qxtLog->info() << "Received message: " << message;
messagesReceived++;
}
if( messagesSent < clientCount * 1000 )
{
qxtLog->debug() << "Sending a message";
message = QString::number( messagesSent );
sendQString( outMessageSocket, message );
messagesSent++;
}
} while( messagesSent < clientCount * 1000 && messagesReceived < clientCount * 1000 );
message = "STOP";
sendQString( outControlSocket, message );
}
所以,最后我的问题是:
REQ/REP
同步化?我非常感谢具有ZMQ经验的人员的一些见解,特别是混合PUB/SUB
,PUSH/PULL
和REQ/REP
模式。
答案 0 :(得分:8)
问题在于,当您使用构造函数
时,ZeroMQ不会复制数据 zmq::message_t( void*, int size );
相反,ZeroMQ只使用您提供的缓冲区作为邮件的存储空间。因此,如果在实际发送消息之前释放或覆盖了该缓冲区,则会出现垃圾或段错误。
解决方案相对容易。相反,你使用这个构造函数:
zmq::message_t( int size );
它创建一个指定大小的缓冲区。然后,您只需在发送之前手动将数据手动复制到消息中。
以下是已更正的,现在正在发送的发送功能:
void sendQString( zmq::socket_t& socket, QString& str )
{
QByteArray package;
QDataStream packer( &package, QIODevice::WriteOnly );
packer << str;
qxtLog->debug() << "sending a message of " << package.size() << " bytes";
qxtLog->debug() << "the message says " << str;
zmq::message_t msg( package.size() );
memcpy( msg.data(), package.data(), package.size() );
ASSERT_MSG( socket.send( msg ), "Failed to send QString" );
}
void recvQString( zmq::socket_t& socket, QString& str )
{
zmq::message_t msg;
ASSERT_MSG( socket.recv( &msg ), "Failed to receive QString" );
qxtLog->debug() << "received a message of " << (int)msg.size() << " bytes";
QByteArray package( ( char* )msg.data(), msg.size() );
QDataStream unpacker( &package, QIODevice::ReadOnly );
unpacker >> str;
qxtLog->debug() << "the message says " << str;
}
感谢freenode上#zeromq频道上的人,我终于能够发现问题了。