ZeroMQ(java)之负载均衡
我们在实际的应用中最常遇到的场景如下:
A向B发送请求,B向A返回结果。。。。
但是这种场景就会很容易变成这个样子:
很多A向B发送请求,所以B要不断的处理这些请求,所以就会很容易想到对B进行扩展,由多个B来处理这些请求,那么这里就出现了另外一个问题:
B对请求处理的速度可能不同,那么B之间他们的负载也是不同的,那么应该如何对请求进行分发就成了一个比较重要的问题。。。也就变成了负载均衡的问题了。。。
其实最好的负载均衡解决方案也很简单:
绝大多数的任务都是独立的,这里中间层可以将A发送过来的请求先缓存起来,然后B的行为就是主动的找中间层获取请求处理,然后返回,再获取。。。。也就是中间层只是做一个请求的缓存。。。由B自己来掌控合适来处理请求,也就是当B已经处理完了任务之后,自己去主动获取。。。而不是由中间层自己去主动分发。。。。
嗯,那么在ZeroMQ中应该如何实现这种模式呢,恩其实还挺简单的,如下图:
这里由两个Router来作为中间层,具体的数据流程如下:
(1)中间层启动,Worker连接Backend,向其发送Request请求(ready),这个时候中间层就能够知道哪一个worker现在是空闲的,将其保存起来(放到worker队列),可以处理请求
worker的执行流程就是send(发送ready)--->recv(获取请求),
(2)Client端向Fronted发送请求,中间层将请求缓存到一个任务队列
(3)中间层从任务队里里面取出一个任务,将其发送给worker队列中的一个worker,并将其从woker队列中移除
(4)worker处理完以后,发送执行结果,也就是send,中间层收到woker的数据 之后,将其发送给相应的client,然后在讲这个worker放到worker队列中,表示当前这个worker可用。。。。
好了,前面就基本上介绍了整个结构用ZeroMQ应该是怎么实现的,那么接下来就直接来上代码吧:
- package balance;
- import java.util.LinkedList;
- import org.zeromq.ZFrame;
- import org.zeromq.ZMQ;
- import org.zeromq.ZMsg;
- public class Balance {
- public static class Client {
- public void start() {
- new Thread(new Runnable(){
- public void run() {
- // TODO Auto-generated method stub
- ZMQ.Context context = ZMQ.context();
- ZMQ.Socket socket = context.socket(ZMQ.REQ);
- socket.connect("ipc://front"); //连接router,想起发送请求
- for (int i = ; i < ; i++) {
- socket.send("hello".getBytes(), ); //发送hello请求
- String bb = new String(socket.recv()); //获取返回的数据
- System.out.println(bb);
- }
- socket.close();
- context.term();
- }
- }).start();
- }
- }
- public static class Worker {
- public void start() {
- new Thread(new Runnable(){
- public void run() {
- // TODO Auto-generated method stub
- ZMQ.Context context = ZMQ.context();
- ZMQ.Socket socket = context.socket(ZMQ.REQ);
- socket.connect("ipc://back"); //连接,用于获取要处理的请求,并发送回去处理结果
- socket.send("ready".getBytes()); //发送ready,表示当前可用
- while (!Thread.currentThread().isInterrupted()) {
- ZMsg msg = ZMsg.recvMsg(socket); //获取需要处理的请求,其实这里msg最外面的标志frame是router对分配给client的标志frame
- ZFrame request = msg.removeLast(); //最后一个frame其实保存的就是实际的请求数据,这里将其移除,待会用新的frame代替
- ZFrame frame = new ZFrame("hello fjs".getBytes());
- msg.addLast(frame); //将刚刚创建的frame放到msg的最后,worker将会收到
- msg.send(socket); //将数据发送回去
- }
- socket.close();
- context.term();
- }
- }).start();
- }
- }
- public static class Middle {
- private LinkedList<ZFrame> workers;
- private LinkedList<ZMsg> requests;
- private ZMQ.Context context;
- private ZMQ.Poller poller;
- public Middle() {
- this.workers = new LinkedList<ZFrame>();
- this.requests = new LinkedList<ZMsg>();
- this.context = ZMQ.context();
- this.poller = new ZMQ.Poller();
- }
- public void start() {
- ZMQ.Socket fronted = this.context.socket(ZMQ.ROUTER); //创建一个router,用于接收client发送过来的请求,以及向client发送处理结果
- ZMQ.Socket backend = this.context.socket(ZMQ.ROUTER); //创建一个router,用于向后面的worker发送数据,然后接收处理的结果
- fronted.bind("ipc://front"); //监听,等待client的连接
- backend.bind("ipc://back"); //监听,等待worker连接
- //创建pollItem
- ZMQ.PollItem fitem = new ZMQ.PollItem(fronted, ZMQ.Poller.POLLIN);
- ZMQ.PollItem bitem = new ZMQ.PollItem(backend, ZMQ.Poller.POLLIN);
- this.poller.register(fitem); //注册pollItem
- this.poller.register(bitem);
- while (!Thread.currentThread().isInterrupted()) {
- this.poller.poll();
- if (fitem.isReadable()) { //表示前面有请求发过来了
- ZMsg msg = ZMsg.recvMsg(fitem.getSocket()); //获取client发送过来的请求,这里router会在实际请求上面套一个连接的标志frame
- this.requests.addLast(msg); //将其挂到请求队列
- }
- if (bitem.isReadable()) { //这里表示worker发送数据过来了
- ZMsg msg = ZMsg.recvMsg(bitem.getSocket()); //获取msg,这里也会在实际发送的数据前面包装一个连接的标志frame
- //这里需要注意,这里返回的是最外面的那个frame,另外它还会将后面的接着的空的标志frame都去掉
- ZFrame workerID = msg.unwrap(); //把外面那层包装取下来,也就是router对连接的标志frame
- this.workers.addLast(workerID); //将当前的worker的标志frame放到worker队列里面,表示这个worker可以用了
- ZFrame readyOrAddress = msg.getFirst(); //这里获取标志frame后面的数据,如果worker刚刚启动,那么应该是发送过来的ready,
- if (new String(readyOrAddress.getData()).equals("ready")) { //表示是worker刚刚启动,发过来的ready
- msg.destroy();
- } else {
- msg.send(fronted); //表示是worker处理完的返回结果,那么返回给客户端
- }
- }
- while (this.workers.size() > && this.requests.size() > ) {
- ZMsg request = this.requests.removeFirst();
- ZFrame worker = this.workers.removeFirst();
- request.wrap(worker); //在request前面包装一层,把可以用的worker的标志frame包装上,这样router就会发给相应的worker的连接
- request.send(backend); //将这个包装过的消息发送出去
- }
- }
- fronted.close();
- backend.close();
- this.context.term();
- }
- }
- public static void main(String args[]) {
- Worker worker = new Worker();
- worker.start();
- Client client = new Client();
- client.start();
- Middle middle = new Middle();
- middle.start();
- }
- }
其实根据前面已经提出来的实现原理来编写代码还是比较顺利的,中途也没有遇到什么问题。。。不过要理解这部分要比较了解ZeroMQ的数据格式才行
ZeroMQ(java)之负载均衡的更多相关文章
- Java 客户端负载均衡
客户端侧负载均衡 在下图中,负载均衡能力算法是由内容中心提供,内容中心相对于用户中心来说,是用户中心的客户端,所以又被称为客户端侧负载均衡 自定义实现Client Random负载均衡 获取所有的服务 ...
- 搭建nginx+tomcat+Java的负载均衡环境
转载 未测 供参考 另外这篇文章也不错.http://blog.csdn.net/wang379275614/article/details/47778201 一.简介: Tomcat在高并发环境下 ...
- 【转】搭建nginx+tomcat+Java的负载均衡环境
一.简介: Tomcat在高并发环境下处理动态请求时性能很低,而在处理静态页面更加脆弱.虽然Tomcat的最新版本支持epoll,但是通过Nginx来处理静态页面要比通过Tomcat处理在性能方面好很 ...
- Java实现负载均衡算法--轮询和加权轮询
1.普通轮询算法 轮询(Round Robin,RR)是依次将用户的访问请求,按循环顺序分配到web服务节点上,从1开始到最后一台服务器节点结束,然后再开始新一轮的循环.这种算法简单,但是没有考虑到每 ...
- Centos6.5生成环境配置--nginx1.9 + PHP+可多个tomcat(多个端口)+多域名+java web 负载均衡
安装n p 参考: CentOS6.5搭建LNMP http://www.cnblogs.com/xiaoit/p/3991037.html http://blog.csdn.net/keyunq/a ...
- 15套java互联网架构师、高并发、集群、负载均衡、高可用、数据库设计、缓存、性能优化、大型分布式 项目实战视频教程
* { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩 展 ...
- java架构师负载均衡、高并发、nginx优化、tomcat集群、异步性能优化、Dubbo分布式、Redis持久化、ActiveMQ中间件、Netty互联网、spring大型分布式项目实战视频教程百度网盘
15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; ...
- 高级java高并发,高性能,分布式,高可用,负载均衡,系统架构实战
java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战 视频课程包含: ...
- 几种简单的负载均衡算法及其Java代码实现
什么是负载均衡 负载均衡,英文名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助.通过某种负载分担技 ...
随机推荐
- jTemplate —— 基于jQuery的javascript前台模版引擎
reference: http://blog.csdn.net/lexinquan/article/details/6674102 http://blog.csdn.net/kuyuyingz ...
- [bzoj 2151]种树(贪心)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2151 分析:原型是bzoj 1150(CTSC 2007) 首先DP无法下手,想到贪心.想到贪 ...
- DOM(八)使用DOM控制表单
1.表单简介 表单<form>是网页中交互最多的形式之一,它通过各种形式接收用户的数据,包括下拉列表框,单选按钮,复选框和文本框,本篇主要介绍表单中常用的属性和方法 javascript中 ...
- 如何解决mysql数据库X小时无连接自动关闭
windows下打开my.ini,增加: interactive_timeout=28800000 wait_timeout=28800000 专家解答:MySQL是一个小型关系型数据库管理系统,由于 ...
- C#中File类的文件操作方法详解
File类,是一个静态类,主要是来提供一些函数库用的.静态实用类,提供了很多静态的方法,支持对文件的基本操作,包括创建,拷贝,移动,删除和打开一个文件.File类方法的参量很多时候都是路径path.F ...
- ORACLE创建表空间、创建用户、更改用户默认表空间以及授权、查看权限
Oracle创建用户.表空间.导入导出....命令 //创建临时表空间 create temporary tablespace ext_temptempfile 'D:\oracle\product\ ...
- 浅议SNMP安全、SNMP协议、网络管理学习
相关学习资料 tcp-ip详解卷1:协议.pdf(重点看25章SNMP部分) http://www.rfc-editor.org/rfc/rfc1213.txt http://www.rfc-edit ...
- TCP/IP详解 学习六
从ip层看路由表 选路策略 选路策略:决定把哪些路由放到路由表的规则. Ip执行选路机制,而路由守护程序则提供选路策略. Netstat –rn 打印路由表,如果没有-n命令会搜索配置文件,将网络地 ...
- 使用Java的嵌套循环打印出平行四边形、等腰三角形、棱形、矩形的星星图案(Java工程师面试必备)
第一遍是看了视频,听老师讲解嵌套循环的使用,然后到星星图形这一步,当时都觉得听明白了,但是自己去做,就是写不出来 第二遍看了赵老师的教程,看了好熟悉的感觉,还是自己写不出来 第三遍找网上关于图形的嵌套 ...
- 【linux基础】第九周作业
1.详细描述一次加密通讯的过程,结合图示最佳. 加密通讯:A <--> B 1)A与 B通信,首先A.B双方都应该持有对方的公钥,即证书,并验证证书的合法性. 2)加密: i. A ...