1、模式简介
he publish-subscribe pattern is used for one-to-many distribution of data from a single publisher to multiple subscribers in a fan out fashion.
The publish-subscribe pattern is formally defined by http://rfc.zeromq.org/spec:29.
ZMQ_PUB
A socket of type ZMQ_PUB is used by a publisher to distribute data. Messages sent are distributed in a fan out fashion to all connected peers. The zmq_recv(3) function is not implemented for this socket type.
When a ZMQ_PUB socket enters the mute state due to having reached the high water mark for a subscriber, then any messages that would be sent to the subscriber in question shall instead be dropped until the mute state ends. The zmq_send() function shall never block for this socket type.
Summary of ZMQ_PUB characteristics | |
---|---|
Compatible peer sockets | ZMQ_SUB, ZMQ_XSUB |
Direction | Unidirectional |
Send/receive pattern | Send only |
Incoming routing strategy | N/A |
Outgoing routing strategy | Fan out |
Action in mute state | Drop |
ZMQ_SUB
A socket of type ZMQ_SUB is used by a subscriber to subscribe to data distributed by a publisher. Initially a ZMQ_SUB socket is not subscribed to any messages, use the ZMQ_SUBSCRIBE option of zmq_setsockopt(3) to specify which messages to subscribe to. The zmq_send() function is not implemented for this socket type.
Summary of ZMQ_SUB characteristics | |
---|---|
Compatible peer sockets | ZMQ_PUB, ZMQ_XPUB |
Direction | Unidirectional |
Send/receive pattern | Receive only |
Incoming routing strategy | Fair-queued |
Outgoing routing strategy | N/A |
2、特别说明
2.1、一般情况下,ZMQ_PUB进行端口的bind,毕竟是发布端嘛,ZMQ_SUB端调用connect,但是这并不是唯一的模式。zeromq非常的灵活,反之也是可以的,ZMQ_SUB端绑定端口,ZMQ_PUB端连接;
2.2、publish的消息,一般第一帧是topic,第二帧是数据,有时候也可以省略第一帧,这个时候ZMQ_SUB订阅的时候,订阅的topic为""就可以了,如果ZMQ_SUB不订阅则收不到消息。
2.3、消息只能从ZMQ_PUB端发送到ZMQ_SUB端,反之则不可以。
3、组网图如下:
说明:
1、ZMQ_PUB调用bind绑定端口的时候,ZMQ_SUB必须调用connect连接到ZMQ_PUB
2、ZMQ_SUB调用bind绑定端口的时候,ZMQ_PUB必须调用connect连接到ZMQ_SUB
3、ZMQ_SUB可以多次调用connect连接不同的PUB端,ZMQ_PUB也可以调用多次connect连接不同的SUB端
4、ZMQ_SUB必须设置topic才能够接收到PUB的消息
4、代码如下:
pub.cpp:
/**
* @file pub.cpp
* @brief pub demo
* @author shlian
* @version 1.0
* @date 2020-11-04
*/
#include <chrono>
#include <iostream>
#include <gflags/gflags.h>
#include <zmqpp/context.hpp>
#include <zmqpp/context_options.hpp>
#include <zmqpp/loop.hpp>
#include <zmqpp/message.hpp>
#include <zmqpp/socket.hpp>
#include <zmqpp/socket_options.hpp>
#include <zmqpp/socket_types.hpp>
#include <zmqpp/zmqpp.hpp>
#include "../include/common.h"
using namespace std;
DEFINE_string(pub_endpoint,"tcp://*:12345","the endpoint binded by publish socket");
DEFINE_int32(pub_interval,1000,"the interval for publishing message");
DEFINE_int32(io_thread_count,1,"the io thread number of zeromq context");
bool publish_message(zmqpp::socket &socket);
int main(int argc, char *argv[])
{
gflags::SetUsageMessage("Usage");
gflags::ParseCommandLineFlags(&argc,&argv,true);
zmqpp::context context;
context.set(zmqpp::context_option::io_threads,FLAGS_io_thread_count);
zmqpp::socket pub_socket=zmqpp::socket(context,zmqpp::socket_type::pub);
pub_socket.bind(FLAGS_pub_endpoint);
//pub_socket.connect("tcp://127.0.0.1:12346");
zmqpp::loop looper;
looper.add(std::chrono::milliseconds(FLAGS_pub_interval),0,std::bind(publish_message,std::ref(pub_socket)));
looper.start();
return 0;
}
unsigned long long publish_count=0;
bool publish_message(zmqpp::socket &socket)
{
zmqpp::message msg;
if(publish_count++%2==0)
{
msg.add("even");//publish topic
}else{
msg.add("odd");//publish topic
}
msg.add(common::format_time());//publish data
LOG_INFO("publish:["<<msg.get(0)<<"],["<<msg.get(1)<<"]");
auto res=socket.send(msg);
return res;
}
sub.cpp:
/**
* @file sub.cpp
* @brief sub demo
* @author shlian
* @version 1.0
* @date 2020-11-04
*/
#include <iostream>
#include <gflags/gflags.h>
#include <zmqpp/context.hpp>
#include <zmqpp/context_options.hpp>
#include <zmqpp/loop.hpp>
#include <zmqpp/message.hpp>
#include <zmqpp/socket.hpp>
#include <zmqpp/socket_types.hpp>
#include <zmqpp/zmqpp.hpp>
#include "../include/common.h"
DEFINE_string(connect_endpoint,"tcp://127.0.0.1:12345","the endpoint binded by publish socket");
DEFINE_string(topic,"","the subscribe topic,there are 3 choices:odd even and empty topic is the default topic");
DEFINE_int32(io_thread_count,1,"the io thread number of zeromq context");
using namespace std;
bool handle_message(zmqpp::socket &socket);
int main(int argc, char *argv[])
{
gflags::SetUsageMessage("Usage");
gflags::ParseCommandLineFlags(&argc,&argv,true);
zmqpp::context context;
context.set(zmqpp::context_option::io_threads,FLAGS_io_thread_count);
zmqpp::socket sub_socket(context,zmqpp::socket_type::sub);
sub_socket.connect(FLAGS_connect_endpoint);
//sub_socket.bind("tcp://*:12346");
sub_socket.subscribe(FLAGS_topic);
zmqpp::loop looper;
looper.add(sub_socket,std::bind(handle_message,std::ref(sub_socket)),zmqpp::poller::poll_in|zmqpp::poller::poll_error);
looper.start();
return 0;
}
bool handle_message(zmqpp::socket &socket)
{
zmqpp::message msg;
auto res=socket.receive(msg);
LOG_INFO("recv topic:["<<msg.get(0)<<"],msg:["<<msg.get(1)<<"]from["<<FLAGS_connect_endpoint<<"]");
return res;
}
CMakeLists.txt:
cmake_minimum_required( VERSION 3.8 FATAL_ERROR)
project(pub LANGUAGES CXX)
#set dirs
set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR})
message("project dir:${PROJECT_ROOT}")
SET(BIN_DESTINATION ${PROJECT_SOURCE_DIR}/bin)
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${BIN_DESTINATION})
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${BIN_DESTINATION})
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BIN_DESTINATION})
#include cmake files
include(${PROJECT_ROOT}/../version.cmake)
#set compile flags
#add_definitions(-std=c++11 -g -rdynamic)
set(CMAKE_CXX_FLAGS "-g3 -rdynamic -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0 ")#-fsanitize=address -fno-omit-frame-pointer -fsanitize=leak")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
#include dirs
include_directories(./ ../include/
)
#link dirs
link_directories(${BIN_DESTINATION})
#execute
SET(SRC_MAIN pub.cpp ../include/common.cpp)
add_executable( ${PROJECT_NAME} ${SRC_MAIN})
set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION})
target_link_libraries(${PROJECT_NAME} pthread zmq zmqpp gflags)
add_executable(sub sub.cpp ../include/common.cpp)
set_target_properties(sub PROPERTIES VERSION ${PROJECT_VERSION})
target_link_libraries(sub pthread zmq zmqpp gflags)
5、运行截图如下: