记录一个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

相关推荐
找方案2 小时前
从用户画像到智能定价:81页方案拆解“大数据决策与支撑平台”怎么落地
大数据·人工智能·数据分析·智慧城市·用户画像
liliangcsdn2 小时前
DeepSeek DeepInfer开源项目的功能分析和探索
大数据·人工智能
xiaogai_gai3 小时前
钉钉月结报销到金蝶付款单同步方案实战教程
大数据·数据库·钉钉
Elastic 中国社区官方博客4 小时前
Elasticsearch:安装自行管理的 Elastic Stack 集群
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
商业看点解说5 小时前
支持私有化部署的 AI 助手有哪些?把“私有化“拆成四层,排除一类常被错放的产品
大数据·人工智能
清华kenny5 小时前
安徽晓得养HIS系统适合哪类健康机构升级使用
大数据·人工智能·安徽晓得养
SelectDB5 小时前
数仓 TCO 怎么算:五项成本拆解框架速查
大数据·数据库·开源
戴西软件6 小时前
制造研发中的AI视觉与仿真:戴西工程图纸智能审图智能体、戴西3DViz_CAE AI集成与戴西3DViz_CAD AI集成
大数据·运维·人工智能·3d·自动化·制造·rpa
SelectDB6 小时前
高并发 BI 看板怎么撑住:隔离与加速配置速查
大数据·数据库·数据分析
Pin_Qing6 小时前
账单没超,请求先被拒了
大数据·开发语言·人工智能