/**************************************************************
技术博客
http://www.cnblogs.com/itdef/
 
技术交流群
群号码:324164944
 
欢迎c c++ windows驱动爱好者 服务器程序员沟通交流
**************************************************************/
ROUTER 与 REQ通讯
#include "stdafx.h"
#include "zhelpers.hpp"
#include <thread> void worker_thread(void *arg) {
zmq::context_t context();
zmq::socket_t worker(context, ZMQ_REQ); s_set_id(worker, (intptr_t)arg);
worker.connect("tcp://localhost:5671"); // "ipc" doesn't yet work on windows. int total = ;
while () {
// Tell the broker we're ready for work
s_send(worker, "Hi Boss"); // Get workload from broker, until finished
std::string workload = s_recv(worker);
if ("Fired!" == workload) {
std::cout << "Processed: " << total << " tasks" << std::endl;
break;
}
total++; // Do some random work
s_sleep(within() + );
}
return;
} int main() {
zmq::context_t context();
zmq::socket_t broker(context, ZMQ_ROUTER); broker.bind("tcp://*:5671"); // "ipc" doesn't yet work on windows. const int NBR_WORKERS = ;
std::thread workers[NBR_WORKERS];
for (int worker_nbr = ; worker_nbr < NBR_WORKERS; worker_nbr++) {
workers[worker_nbr]= std::thread( worker_thread, (void *)(intptr_t)worker_nbr);
} // Run for five seconds and then tell workers to end
int64_t end_time = s_clock() + ;
int workers_fired = ;
while () {
// Next message gives us least recently used worker
std::string identity = s_recv(broker);
s_recv(broker); // Envelope delimiter
s_recv(broker); // Response from worker s_sendmore(broker, identity);
s_sendmore(broker, "");
// Encourage workers until it's time to fire them
if (s_clock() < end_time)
s_send(broker, "Work harder");
else {
s_send(broker, "Fired!");
if (++workers_fired == NBR_WORKERS)
break;
}
} for (int worker_nbr = ; worker_nbr < NBR_WORKERS; worker_nbr++) {
workers[worker_nbr].join();
}
return ;
}

ROUTER 与 DEALER通讯

// rtdealer_cpp.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
//
// Custom routing Router to Dealer
//
// Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com> #include "zhelpers.hpp"
#include <thread> static void *
worker_task(void *args)
{
zmq::context_t context();
zmq::socket_t worker(context, ZMQ_DEALER); #if (defined (WIN32))
s_set_id(worker, (intptr_t)args);
#else
s_set_id(worker); // Set a printable identity
#endif worker.connect("tcp://localhost:5671"); int total = ;
while () {
// Tell the broker we're ready for work
s_sendmore(worker, "");
s_send(worker, "Hi Boss"); // Get workload from broker, until finished
s_recv(worker); // Envelope delimiter
std::string workload = s_recv(worker);
// .skip
if ("Fired!" == workload) {
std::cout << "Completed: " << total << " tasks" << std::endl;
break;
}
total++; // Do some random work
s_sleep(within() + );
} return NULL;
} // .split main task
// While this example runs in a single process, that is just to make
// it easier to start and stop the example. Each thread has its own
// context and conceptually acts as a separate process.
int main() {
zmq::context_t context();
zmq::socket_t broker(context, ZMQ_ROUTER); broker.bind("tcp://*:5671");
srandom((unsigned)time(NULL)); const int NBR_WORKERS = ;
std::thread workers[NBR_WORKERS];
int worker_nbr = ;
for (; worker_nbr < NBR_WORKERS; ++worker_nbr) {
workers[worker_nbr] = std::thread( worker_task, (void *)(intptr_t)worker_nbr);
} // Run for five seconds and then tell workers to end
int64_t end_time = s_clock() + ;
int workers_fired = ;
while () {
// Next message gives us least recently used worker
std::string identity = s_recv(broker);
{
s_recv(broker); // Envelope delimiter
s_recv(broker); // Response from worker
} s_sendmore(broker, identity);
s_sendmore(broker, ""); // Encourage workers until it's time to fire them
if (s_clock() < end_time)
s_send(broker, "Work harder");
else {
s_send(broker, "Fired!");
if (++workers_fired == NBR_WORKERS)
break;
}
} for (int worker_nbr = ; worker_nbr < NBR_WORKERS; ++worker_nbr) {
workers[worker_nbr].join();
} return ;
}

DEALER REQ区别在于 REQ模式socket发送信息时候会自动在信息前添加“”空白信息作为隔离

DEALER模式socket需要手动添加 s_sendmore(socket, "");

而ROUTER 在接受信息前会受到来源socket的地址信息 而发送时会将地址信息添加到socket的信息之前 同时还要在在信息前添加“”空白信息作为隔离

s_sendmore(broker, identity);
s_sendmore(broker, "");

均衡负载 此段代码比较复杂 理解较难

我将代码设置为单线程 然后调试 纠正了一些我理解的router模式

稍后画图

// lbbroker_cpp.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include "zhelpers.hpp"
#include <thread>
#include <queue> void client_thread(void* arg) {
zmq::context_t context();
zmq::socket_t client(context, ZMQ_REQ); s_set_id(client, (intptr_t));
client.connect("tcp://localhost:5672"); s_send(client, "HELLO");
std::string reply = s_recv(client);
std::cout << "Client: " << reply << std::endl;
return;
} void worker_thread(void* arg){
zmq::context_t context();
zmq::socket_t worker(context, ZMQ_REQ); s_set_id(worker, (intptr_t));
worker.connect("tcp://localhost:5673"); s_send(worker, "READY");
while () {
std::string address = s_recv(worker);
{
std::string empty = s_recv(worker);
assert(empty.size() == );
}
std::string request = s_recv(worker);
std::cout << "Worker: " << request << std::endl; s_sendmore(worker, address);
s_sendmore(worker, "");
s_send(worker, "OK");
}
return;
} int main()
{
zmq::context_t context();
zmq::socket_t frontend(context, ZMQ_ROUTER);
zmq::socket_t backend(context, ZMQ_ROUTER); frontend.bind("tcp://*:5672");
backend.bind("tcp://*:5673"); int client_nbr;
std::thread client[];
for (client_nbr = ; client_nbr < ; client_nbr++)
{
client[client_nbr] = std::thread(client_thread,(void *)(intptr_t)client_nbr);
} int worker_nbr;
std::thread worker[];
for (worker_nbr = ; worker_nbr < ; worker_nbr++)
{
worker[worker_nbr] = std::thread(worker_thread, (void *)(intptr_t)worker_nbr);
} std::queue<std::string> worker_queue; while ()
{
zmq::pollitem_t items[] = {
{backend,,ZMQ_POLLIN,},
{frontend,,ZMQ_POLLIN,}
};
if (worker_queue.size())
zmq::poll(&items[], , -);
else
zmq::poll(&items[], , -); if (items[].revents & ZMQ_POLLIN) { // Queue worker address for LRU routing
worker_queue.push(s_recv(backend)); {
// Second frame is empty
std::string empty = s_recv(backend);
assert(empty.size() == );
} // Third frame is READY or else a client reply address
std::string client_addr = s_recv(backend); // If client reply, send rest back to frontend
if (client_addr.compare("READY") != ) { {
std::string empty = s_recv(backend);
assert(empty.size() == );
} std::string reply = s_recv(backend);
s_sendmore(frontend, client_addr);
s_sendmore(frontend, "");
s_send(frontend, reply); if (--client_nbr == )
break;
}
}
if (items[].revents & ZMQ_POLLIN) { // Now get next client request, route to LRU worker
// Client request is [address][empty][request]
std::string client_addr = s_recv(frontend); {
std::string empty = s_recv(frontend);
assert(empty.size() == );
} std::string request = s_recv(frontend); std::string worker_addr = worker_queue.front();//worker_queue [0];
worker_queue.pop(); s_sendmore(backend, worker_addr);
s_sendmore(backend, "");
s_sendmore(backend, client_addr);
s_sendmore(backend, "");
s_send(backend, request);
}
} for (client_nbr = ; client_nbr < ; client_nbr++)
{
client[client_nbr].join();
} for (worker_nbr = ; worker_nbr < ; worker_nbr++)
{
worker[worker_nbr].join();
} return ;
}

此处图仅为个人领悟 不能保证完全正确 请谨慎参考

开始看官方的图示 我以为上述代码是如官方所示

但是对于router来说

connect到router的客户端或者工作者(client worker)

与router直接send到router的信息处理上是有区别的

下图用直线和箭头来区分

我将代码设置为单线程 然后调试 得出流程如图:

由于新建线程的随机性

步骤1 2 是的先后次序是随机的

1 client 发送信息到frontend router

信息格式为

"0001"

""

"HELLO"

2 worker发送信息到backend router

信息格式为

"0009"

""

"READY"

3函数主体进入POLL循环

根据代码

if (worker_queue.size())
zmq::poll(&items[0], 2, -1);
else
zmq::poll(&items[0], 1, -1);

由于空闲worker队列中暂时无记录

所以仅仅对backend 进行poll轮询

首先接受信息记录的通讯ID 为9 并push进worker队列

worker_queue.push(s_recv(backend));

然后接受空字节分隔符 在接受发送的正文“READY”

接收信息格式如下:

"0009"

""

"READY"

由于此次接受的正文是“READY” 根据代码不进入发送流程

if (client_addr.compare("READY") != 0)

步骤3结束

4 主体代码进入第二次POLL轮询

frontend 接收信息如下

信息格式为

"0001"

""

"HELLO"

弹出之前接收空闲worker的ID

std::string worker_addr = worker_queue.front();

然后向backend发送信息格式如下:

"0009"

""

"0001"

""

"HELLO"

5 backend接收信息后直接路由到ID为0009的worker

6 worker接收到信息格式为

"0001"

""

"HELLO"

然后发送信息格式:

"0001"

""

"OK"

7 backend接收到步骤6的信息:

将步骤6的worker的id 0009 push仅worker队列

worker_queue.push(s_recv(backend));

此次接受的信息格式为

"0009"

""

"001"

""

"OK"

由于此次接受正文不是"READY"

进入发送模式 发送信息到frontend

发送信息格式为

"0001"

""

"OK"

8 frontend直接路由此信息到ID 0001的client

最后client接收到的信息格式为

"OK"

zeromq学习记录(八)负载均衡 附ZMQ_ROUTER的流程分析的更多相关文章

  1. SpringCloud全家桶学习之Feign负载均衡----Feign(四)

    一.Feign概述 (1)Feign是什么? 官网地址:https://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-f ...

  2. 架构师成长之路6.5 DNS服务器搭建(添加记录、负载均衡、DNS视图)

    点击返回架构师成长之路 架构师成长之路6.5 DNS服务器搭建(添加记录.负载均衡.DNS视图)  部署主DNS : 点击 部署从DNS : 点击 1.添加A记录.CNAME记录.MX记录.PTR记录 ...

  3. Nginx记录-nginx 负载均衡5种配置方式(转载)

    nginx 负载均衡5种配置方式 1.轮询(默认)   每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除.  2.weight 指定轮询几率,weight和访问比率成 ...

  4. zeromq学习记录(一)最初的简单示例使用ZMQ_REQ ZMQ_REP

    阅读zeromq guide的一些学习记录 zeromq官方例子 在VC下运行会有些跨平台的错误 我这里有做修改 稍后会发布出来 相关的代码与库  http://download.zeromq.org ...

  5. Dubbo -- 系统学习 笔记 -- 示例 -- 负载均衡

    Dubbo -- 系统学习 笔记 -- 目录 示例 想完整的运行起来,请参见:快速启动,这里只列出各种场景的配置方式 负载均衡 在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调 ...

  6. [记录]HAproxy负载均衡配置教程

    HAproxy负载均衡配置教程 一.简介 haproxy是一个开源的高性能负载均衡软件:支持双机热备.虚拟主机和图形化的管理界面,自带强大的对RS健康检查功能:支持TCP(四层).HTTP(七层)应用 ...

  7. Dubbo源码(八) - 负载均衡

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡.分摊到多个 ...

  8. 学习下nginx负载均衡--深入理解nginx

    作为代理服务器,一般都需要向上游服务器转发请求.这里的负载均衡是指通过一种策略尽量把请求平均的分发都上游服务器 1.upstream 语法 upstream name {} 配置快: http 栗子( ...

  9. SpringCloud学习笔记:负载均衡Ribbon(3)

    1. RestTemplate简介 RestTemplate是Spring Resource中一个访问第三方RESTful API接口的网络请求框架. RestTemplate是用来消费REST服务的 ...

随机推荐

  1. Flask--SQLAlchemy--基本查询备忘

    SQLAlchemy查询过滤器: 查询所有用户数据 User.query.all() 查询有多少个用户 User.query.count() 查询第1个用户 User.query.first() 查询 ...

  2. VS2017调试出现异常浏览器直接关闭的解决办法

    最近升级完VS2017后,出现了各种不适应. 1.F5调试时总是会打开新的浏览器,过去都是在现有窗口右侧打开新的新的浏览器标签页. 这一点就让很不爽,勉强接受吧,继续调试代码但是还有第二种情况. 2. ...

  3. VS在.NETFramework升级时遇到类库冲突如何解决

    相信大家在开发环境中随着程序的不断升级,很多时间需要升级. NETFramework版本.今天项目中遇到的问题是从. NETFramework4.0升级到4.5时提示 Entityframework. ...

  4. c# 抽象类 抽象函数 接口

    抽象类与抽象方法: 被abstract关键字修饰的类叫做抽象类 被abstract关键字修饰的方法叫做抽象方法 1.抽象方法必须放在抽象类中 2.抽象方法不可以实现代码,用空语句替代 3.抽象方法可以 ...

  5. background-size的应用情景:当给出的背景图片大于实际网页需要布局的图片大小时

    网页需求是:50*50 如果只设置  width:50px;height:50px;background-image("images/XXX.png"); 效果如下: 添加设置:b ...

  6. centos关闭邮件提醒

    解决:you have mail in /var/spool/mail/root 提示   echo "unset MAILCHECK">> /etc/profile; ...

  7. 微信小程序版本管理

    备忘录:<需要修改完善> 打开小程序开发者工具,点击版本管理,点击设置 , 通用更改码云的名字和邮箱 网络认证选择用户名和密码 把密码输入了,点击远程 添加 输入码云的https地址 和仓 ...

  8. centos svn强制要求提交代码时写注释

    1. 重命名svn主目录中hooks的pre-commit.tmpl文件为pre-commit,并添加可执行权限 mv pre-commit.tmpl pre-commit chmod u+x pre ...

  9. oracle 创建表,删除表,修改表,查询表

    1,获取当前用户下的所有表信息 =>  SELECT * FROM user_tables 1.1,查询某一张表的字段信息:SELECT  *  FROM user_tab_columns  w ...

  10. elasticsearch-权威指南笔记-基础部分

    参考这里的文档es权威指南 话说这个坑爹的文档是2.x版本的es,英文版本也是,所以就没啥好抱怨的了. 官方教程中有很多坑 例如,需要启动text上的索引. 还有就是get这个是不能带json的,所以 ...