记录一个zmq客户端的性能调优

git clone https://github.com/zeromq/cppzmq.git

cd cppzmq # 切稳定匹配版本,libzmq4.3.5 搭配 v4.10.0 / v4.11.0

git checkout v4.10.0

复制代码
./configure --enable-debug

cmake -B build -DCMAKE_INSTALL_PREFIX=`pwd`/output

git clone https://github.com/zeromq/cppzmq.git

//demo

#include <zmq.h>

#include "zmq.hpp"

#include <iostream>

#include <vector>

#include <signal.h>

#include <cstring>

int main()

{

zmq::context_t ctx(1);

std::vector<std::string> endpoints = {

"tcp://127.0.0.1:5555",

"ipc:///tmp/zmq_pub_ipc"

};

std::vector<zmq::socket_t> subs;

for (const auto& addr : endpoints)

{

zmq::socket_t sub(ctx, zmq::socket_type::sub);

sub.set(zmq::sockopt::subscribe, "");

sub.set(zmq::sockopt::reconnect_ivl, 1000); // 5秒重试间隔

sub.set(zmq::sockopt::reconnect_ivl_max, 1000); // 最大30秒

sub.connect(addr);

subs.emplace_back(std::move(sub));

}

std::vector<zmq_pollitem_t> items(subs.size());

for (size_t i = 0; i < subs.size(); i++)

{

itemsi.socket = subsi.handle();

itemsi.fd = 0;

itemsi.events = ZMQ_POLLIN;

itemsi.revents = 0;

}

while (true)

{

// 2000毫秒超时,替代ppoll

int ret = zmq_poll(items.data(), static_cast<int>(items.size()), -1);

if (ret < 0)

{

int err = zmq_errno();

std::cerr << "poll err: " << zmq_strerror(err) << "\n";

if (err == ETERM) break;

// poll被信号打断就重试

if (err == EINTR) continue;

continue;

}

if (ret == 0) continue;

for (size_t i = 0; i < items.size(); i++)

{

if (itemsi.revents & ZMQ_POLLIN)

{

zmq::message_t msg;

auto res = subsi.recv(msg);

if (!res.has_value())

continue;

std::string data(msg.data<char>(), msg.size());

std::cout << "" \<\< endpoints\[i << "] recv: " << data << "\n";

itemsi.revents = 0;

}

}

}

return 0;

}

/////////////

htop -p `pidof ser1`

1.查看到主线程有0.7的cpu

//2s超时一次的开销

int ret = zmq_poll(items.data(), static_cast<int>(items.size()), 2000);

//改成这个无任何开销 就是会一直阻塞不好退出这个程序

int ret = zmq_poll(items.data(), static_cast<int>(items.size()), -1);

2.查看ZMGbg/IO/0线程有0.4的开销

用strace -p 查看在不停的尝试连接

将尝试连接的间隔拉开

sub.set(zmq::sockopt::reconnect_ivl, 1000); // 1秒重试间隔

sub.set(zmq::sockopt::reconnect_ivl_max, 1000); // 最大1秒

import zmq

import time

def main():

初始化上下文

context = zmq.Context()

创建发布套接字

pub = context.socket(zmq.PUB)

同时绑定TCP和IPC两个通道

pub.bind("tcp://127.0.0.1:5555")

pub.bind("ipc:///tmp/zmq_pub_ipc")

print("发布服务启动成功")

print("绑定TCP: tcp://127.0.0.1:5555")

print("绑定IPC: ipc:///tmp/zmq_pub_ipc")

print("-" * 40)

num = 0

while True:

组装消息内容

msg = f"消息序号:{num} | 实时测试数据"

广播发送,TCP、IPC所有订阅者都会收到这条消息

pub.send_string(msg)

print(f"已发送:{msg}")

num += 1

time.sleep(0.1)

if name == "main":

try:

main()

except KeyboardInterrupt:

print("\n检测到退出信号,关闭服务")

finally:

释放资源

zmq.Context().destroy()

10HZ的频率发布数据.

发二进制

import zmq

import time

def sleep_ms(ms):

return time.sleep(ms / 1000.0)

def main():

ctx = zmq.Context()

三个独立发布socket

pub_tcp = ctx.socket(zmq.PUB)

pub_tcp.bind("tcp://127.0.0.1:5555")

pub_ipc = ctx.socket(zmq.PUB)

pub_ipc.bind("ipc:///tmp/zmq_pub_ipc")

cnt = 0

while True:

构造100字节填充数据

buf = bytearray(100) # 初始全0,固定长度100

可自定义填充内容:前4字节放序号,剩余填充数据

buf0:4 = cnt.to_bytes(4, byteorder='little', signed=False)

示例:中间填充自定义值

for i in range(4, 100):

bufi = (cnt + i) % 256

data_100bytes = bytes(buf) # 转为bytes,严格100长度

assert len(data_100bytes) == 100

分别发给不同通道

#pub_tcp.send(data_100bytes)

pub_ipc.send(data_100bytes)

print(f"发送第{cnt}包,长度:{len(data_100bytes)} bytes")

cnt += 1

sleep_ms(10)

if name == "main":

try:

main()

except KeyboardInterrupt:

print("\n退出")

finally:

ctx.destroy()

////////////////

查看每个fd分别是啥

LD_LIBRARY_PATH=/home/ubuntu22/lib/libzmq/build/output/lib gdb --args ./ser1

catch syscall epoll_create1

catch syscall eventfd2

这里每个调用栈eventfd2会中断2次 一次是进入中断 一次是退出中断,退出的时候

(gdb) info reg rax

rax 0x3

第一个eventfd 3是ctx本身自带的mailbox 里面 里面带了signaler 里面带了fd.

zmq::socket_base_t *zmq::ctx_t::create_socket (int type_)

->start()

->...

第二个是socket里面reaper里面带的fd4

然后这时reaper里面创建了eventpoll 也就是fd5

接下来是io_thread_t里面的eventfd fd6

然后是io_therad里面创建的eventpoll fd7

zmq::socket_base_t *zmq::ctx_t::create_socket (int type_)

->socket_base_t *s = socket_base_t::create (type_, this, slot, sid);

这次是socket_base里面的_mailbox fd8

// Socket's mailbox object.

i_mailbox *_mailbox;

这里循环有2个 zmq::socket_t sub(ctx, zmq::socket_type::sub);

所以下一个是socket_base里面的_mailbox fd9

接下来就是work线程里面的两个socket fd

/////////plug 触发epoll

相关推荐
华奥系科技1 小时前
汛期城市内涝治理:智慧水务如何重塑防汛“安全感”?
大数据·运维·人工智能
Bode_20022 小时前
智能协同与绿色数字孪生舱主要功能与关键技术
大数据·人工智能·制造·碳中和
SmartBrain2 小时前
编程助手工具自动化开发对比报告:OpenSpec、Claude Code、Cursor、PI
大数据·人工智能
小赖同学啊2 小时前
可信数据空间设计
大数据
想ai抽2 小时前
Spark Executor 因节点内存超限被杀的分析与应对
大数据·性能优化·spark
就改了3 小时前
Windows Elasticsearch 完整上手教程
大数据·windows·elasticsearch
yyuuuzz4 小时前
独立站运营的几个技术层面常见问题
大数据·运维·服务器·网络·数据库·aws
XIAOYU6720134 小时前
高中物理成绩优异,适合报考大数据哪个细分专业数学成绩偏弱,还适合填报大数据相关专业吗
大数据
2601_954971134 小时前
大数据需要掌握哪些主流大数据工具框架
大数据