跟我一起学习ZeroMQ(6):发布订阅模式(Publish-subscribe pattern)ZMQ_PUB和ZMQ_SUB

ZeroMQ发布订阅

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 socketsZMQ_SUBZMQ_XSUB
DirectionUnidirectional
Send/receive patternSend only
Incoming routing strategyN/A
Outgoing routing strategyFan out
Action in mute stateDrop

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 socketsZMQ_PUBZMQ_XPUB
DirectionUnidirectional
Send/receive patternReceive only
Incoming routing strategyFair-queued
Outgoing routing strategyN/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、运行截图如下:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ztenv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值