GrabBag/Module/ZeroMQServer/Src/CVrZeroMQServer.cpp

207 lines
5.8 KiB
C++
Raw Normal View History

2026-05-31 00:10:46 +08:00
#include "CVrZeroMQServer.h"
#include <iostream>
#include <thread>
#include <sstream>
#include <string>
#include "VrError.h"
#include "IVrUtils.h"
#include "VrStringUtils.h"
#include "VrLog.h"
bool IVrZeroMQServer::CreateObject(IVrZeroMQServer** ppMQServer)
{
if (ppMQServer == nullptr) {
return false;
}
try {
*ppMQServer = new CVrZeroMQServer();
return true;
}
catch (const std::exception& e) {
std::cerr << "Failed to create ZeroMQ server: " << e.what() << std::endl;
return false;
}
}
CVrZeroMQServer::CVrZeroMQServer()
: m_funMQEvent(nullptr)
, m_funRecvAck(nullptr)
, m_bMonitor(false)
, m_bMonitorWorking(false)
, m_bInitialized(false)
{
m_pContext = new zmq::context_t;
m_pRouter = new zmq::socket_t(*m_pContext, ZMQ_ROUTER);
m_pWorkers = new zmq::socket_t(*m_pContext, ZMQ_DEALER);
m_pSocket = new zmq::socket_t(*m_pContext, ZMQ_REP);
}
CVrZeroMQServer::~CVrZeroMQServer()
{
UnInit();
delete m_pSocket;
delete m_pWorkers;
delete m_pRouter;
delete m_pContext;
}
/// 启动服务
int CVrZeroMQServer::Init(int nPort, FunMQEvent funMQEvent, FunServerRecvAndAck funRecvAck, bool bMultitask)
{
m_funMQEvent = funMQEvent;
m_funRecvAck = funRecvAck;
std::string transferAddr = "tcp://*:";
transferAddr.append(CVrStringUtils::Int2Str(nPort));
if (!bMultitask) {
try {
m_pSocket->bind(transferAddr);
LOG_DEBUG("[ZMQ-REP] bind OK: %s\n", transferAddr.c_str());
} catch (const std::exception& e) {
LOG_ERROR("[ZMQ-REP] bind FAIL: %s\n", e.what());
return ERR_CODE(NET_ERR_CREAT_BIND);
}
m_bRecv = true;
m_recvThread = std::thread(&CVrZeroMQServer::_WorkTask, this, m_pSocket);
m_recvThread.detach();
LOG_DEBUG("[ZMQ-REP] recv thread started, port=%d\n", nPort);
}
else {
try {
// Frontend ROUTER binds to TCP port for external clients
m_pRouter->bind(transferAddr);
} catch (const std::exception& e) {
LOG_ERROR("Failed to bind ROUTER: %s", e.what());
return ERR_CODE(NET_ERR_CREAT_BIND);
}
// Backend DEALER binds to inproc for worker pool
m_pWorkers->bind("inproc://workers");
// Spawn worker tasks based on hardware concurrency (min 2)
unsigned int nWorkers = 5;
for (unsigned int i = 0; i < nWorkers; ++i) {
auto* pTask = new CVrZeroMQTask();
pTask->Init(m_pContext, funMQEvent, funRecvAck);
m_vecTask.push_back(pTask);
}
// Start proxy thread to route between ROUTER and DEALER
m_workerThread = std::thread(&CVrZeroMQServer::_WorkerThread, this);
m_workerThread.detach();
}
m_bMonitor = true;
// 启动监控线程(监控 ROUTER 套接字,单线程模式也可复用)
m_monitorThread = std::thread(&CVrZeroMQServer::_MonitorEvent, this, m_pRouter);
m_monitorThread.detach();
return SUCCESS;
}
/// 销毁服务
int CVrZeroMQServer::UnInit()
{
m_bMonitor = false;
m_bRecv = false;
// 关闭单线程模式下的 socket 以唤醒阻塞的 recv
try {
if (m_pSocket) m_pSocket->close();
} catch (...) {}
// 等待接收线程/代理线程/监控线程结束
if (m_recvThread.joinable()) m_recvThread.join();
if (m_workerThread.joinable()) m_workerThread.join();
if (m_monitorThread.joinable()) m_monitorThread.join();
for (auto& pTask : m_vecTask)
{
pTask->UnInit();
delete pTask;
}
m_vecTask.clear();
try { if (m_pWorkers) m_pWorkers->close(); } catch (...) {}
try { if (m_pRouter) m_pRouter->close(); } catch (...) {}
try { if (m_pContext) m_pContext->close(); } catch (...) {}
return SUCCESS;
}
void CVrZeroMQServer::_WorkerThread()
{
// 使用 zmq proxy 在线程中转发 ROUTER<->DEALER避免阻塞主线程
try {
zmq::proxy(*m_pRouter, *m_pWorkers);
} catch (const std::exception& e) {
LOG_DEBUG("ZeroMQ proxy terminated: %s", e.what());
}
}
void CVrZeroMQServer::on_event_handshake_succeeded(const zmq_event_t& event_, const char* addr_)
{
LOG_DEBUG("on_event_handshake_succeeded \n");
if (m_funMQEvent) {
m_funMQEvent(MQ_EVENT_NEWLINK);
}
}
void CVrZeroMQServer::on_event_disconnected(const zmq_event_t& event_, const char* addr_)
{
LOG_DEBUG("on_event_disconnected \n");
if (m_funMQEvent) {
m_funMQEvent(MQ_EVENT_DELINK);
}
}
void CVrZeroMQServer::_WorkTask(zmq::socket_t *socket)
{
if (!socket) return;
m_bRecvWorking = true;
try {
while (m_bRecv) {
zmq::message_t msg;
zmq::recv_result_t ret = socket->recv(msg, zmq::recv_flags::none);
if (!m_bRecv) break;
if (ret.has_value()) {
LOG_DEBUG("[ZMQ-REP] _WorkTask recv: %.*s\n", (int)msg.size(), static_cast<char*>(msg.data()));
if (m_funRecvAck) {
std::string strResponse = m_funRecvAck(static_cast<char*>(msg.data()), msg.size());
zmq::message_t response(strResponse.c_str(), strResponse.size());
socket->send(response, zmq::send_flags::none);
2026-06-26 17:49:32 +08:00
LOG_DEBUG("[ZMQ-REP] _WorkTask send resp bytes=%zu\n", strResponse.size());
2026-05-31 00:10:46 +08:00
} else {
socket->send(msg, zmq::send_flags::none);
}
}
}
} catch (const std::exception& e) {
LOG_DEBUG("Server recv loop terminated: %s", e.what());
}
m_bRecvWorking = false;
}
void CVrZeroMQServer::_MonitorEvent(zmq::socket_t* socket)
{
// 设置监控
try {
init(*socket, "inproc://monitor", ZMQ_EVENT_ALL);
m_bMonitorWorking = true;
while (m_bMonitor)
{
check_event(100);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
m_bMonitorWorking = false;
} catch (const std::exception& e) {
LOG_DEBUG("Monitor exception: %s", e.what());
m_bMonitorWorking = false;
}
}