使用ZooKeeper实现软负载均衡(原理)
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,提供的功能包括配置维护、名字服务、分布式同步、组服务等。
ZooKeeper会维护一个树形的数据结构,类似于Windows资源管理器目录,其中EPHEMERAL类型的节点会随着创建它的客户端断开而被删除,利用这个特性很容易实现软负载均衡。
基本原理是,每个应用的Server启动时创建一个EPHEMERAL节点,应用客户端通过读取节点列表获得可用服务器列表,并订阅节点事件,有Server宕机断开时触发事件,客户端监测到后把该Server从可用列表中删除。
来看示例,这里用了BIO模型编写了一个接收/应答的小程序用于演示效果,优点就是简单。为了方便后面的改造,客户端每次发送消息时都会读取服务器列表并从新建立连接。后边会看到只需要几十行代码即可改造为使用ZooKeeper的软负载模式。
Server代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket; public class SimpleServer implements Runnable { public static void main(String[] args) throws IOException {
int port = 18080;
SimpleServer server = new SimpleServer(port);
Thread thread = new Thread(server);
thread.start();
} private int port; public SimpleServer(int port) {
this.port = port;
} @Override
public void run() {
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("Server started");
Socket socket = null;
while (true) {
socket = server.accept();
new Thread(new SimpleServerHandler(socket)).start();
}
} catch(IOException ex) {
ex.printStackTrace();
} finally {
if (server != null) {
try {
server.close();
} catch (IOException e) {}
}
}
}
} class SimpleServerHandler implements Runnable { private Socket socket; public SimpleServerHandler(Socket socket) {
this.socket = socket;
} @Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(
this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String body = null;
while (true) {
body = in.readLine();
if (body == null)
break;
System.out.println("Receive : " + body);
out.println("Hello, " + body);
} } catch (Exception e) {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (out != null) {
out.close();
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
客户端代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List; public class SimpleClient { private static List<String> servers = new ArrayList<>(); public static void main(String[] args) { initServerList(); SimpleClient client = new SimpleClient();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String name;
try {
name = console.readLine();
if("exit".equals(name)) {
System.exit(0);
}
client.send(name);
} catch (IOException e) {
e.printStackTrace();
}
}
} private static void initServerList() {
servers.clear();
servers.add("127.0.0.1:18080");
} public static String getServer() {
return servers.get(0);
} public SimpleClient() {
} public void send(String name) { String server = SimpleClient.getServer();
String[] cfg = server.split(":"); Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket(cfg[0], Integer.parseInt(cfg[1]));
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true); out.println(name);
while(true) {
String resp = in.readLine();
if(resp == null)
break;
else if(resp.length() > 0) {
System.out.println("Receive : " + resp);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行测试,服务器端输出截图:
客户端输出截图:
很好,一切运行正常。
接下来添加ZooKeeper部分。为了演示效果更好,修改一下ZooKeeper的配置文件,以便于服务器断开后能更快的被监测到。主要是减小Session的超时时间
zookeeper/conf/zoo.cfg
tickTime=2000
initLimit=2
syncLimit=5
dataDir=D:\\ZooKeeper\\zookeeper-3.4.8\\data
clientPort=2181
http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
minSessionTimeout=2000
maxSessionTimeout=5000
在项目中添加zkclient的maven依赖
<!-- http://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.8</version>
</dependency>
Server代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket; import org.I0Itec.zkclient.ZkClient; public class SimpleServer implements Runnable { public static void main(String[] args) throws IOException {
int port = 18080;
SimpleServer server = new SimpleServer(port);
Thread thread = new Thread(server);
thread.start();
} private int port; public SimpleServer(int port) {
this.port = port;
} private void regServer() {
//向ZooKeeper注册当前服务器
ZkClient client = new ZkClient("127.0.0.1:2181", 60000, 1000);
String path = "/test/server" + port;
if(client.exists(path))
client.delete(path);
client.createEphemeral(path, "127.0.0.1:" + port);
} @Override
public void run() {
ServerSocket server = null;
try {
server = new ServerSocket(port);
regServer();
System.out.println("Server started at " + port);
Socket socket = null;
while (true) {
socket = server.accept();
new Thread(new SimpleServerHandler(socket)).start();
}
} catch(IOException ex) {
ex.printStackTrace();
} finally {
if (server != null) {
try {
server.close();
} catch (IOException e) {}
}
} }
}
//SimpleServerHandler略
客户端代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random; import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient; public class SimpleClient { private static List<String> servers = new ArrayList<>(); public static void main(String[] args) { initServerList(); SimpleClient client = new SimpleClient();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String name;
try {
name = console.readLine();
if("exit".equals(name)) {
System.exit(0);
}
client.send(name);
} catch (IOException e) {
e.printStackTrace();
}
}
} private static void initServerList() {
//启动时从ZooKeeper读取可用服务器
String path = "/test";
ZkClient zkClient = new ZkClient("127.0.0.1:2181", 60000, 1000);
List<String> childs = zkClient.getChildren(path);
servers.clear();
for(String p : childs) {
servers.add(zkClient.readData(path + "/" + p));
}
//订阅节点变化事件
zkClient.subscribeChildChanges("/test", new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> currentChilds)
throws Exception {
System.out.println(String.format("[ZookeeperRegistry] service list change: path=%s, currentChilds=%s", parentPath, currentChilds.toString()));
servers.clear();
for(String p : currentChilds) {
servers.add(zkClient.readData(path + "/" + p));
}
System.out.println("Servers: " + servers.toString());
}
}); } public static String getServer() {
return servers.get(new Random().nextInt(servers.size()));
}
//其他无变化, 略
}
分别启动Server和Client,然后修改Server的端口号,再启动一个实例,可以看到客户端检测到了这个新服务器的存在
在客户端发送一些消息,可以看到被随机的分发到两个Server上处理
接下来关闭其中一个Server,可以看到客户端几秒钟后监测到这个事件并自动删除了该服务器。
这样,我们就实现了基于ZooKeeper的软负载均衡。
使用ZooKeeper实现软负载均衡(原理)的更多相关文章
- 【Zookeeper】实现负载均衡原理
一.思路 使用Zookeeper实现负载均衡原理,服务器端将启动的服务注册到,zk注册中心上,采用临时节点.客户端从zk节点上获取最新服务节点信息,本地使用负载均衡算法,随机分配服务器. 服务端启动的 ...
- Zookeeper实现负载均衡原理
先玩个正常的,好玩的socket编程: 服务端: 首先公共的这个Handler: package com.toov5.zkDubbo; import java.io.BufferedReader; i ...
- 使用Zookeeper实现负载均衡原理
思路 使用Zookeeper实现负载均衡原理,服务器端将启动的服务注册到,zk注册中心上,采用临时节点.客户端从zk节点上获取最新服务节点信息,本地使用负载均衡算法,随机分配服务器. 创建项目工程 M ...
- 六大Web负载均衡原理与实现
还有个姊妹篇也可以参考这个文章:LVS(Linus Virtual Server):三种负载均衡方式比较+另三种负载均衡方式, LVS 实现了负载均衡,NAT,DR,TUN zookeeper使用ZA ...
- zookeeper如何实现负载均衡的?(具体连接哪一个zookeeper服务器的选择?)阿里面试
如果想了解web 6大负载均衡算法,参考:六大Web负载均衡原理与实现 主要是三点:负载均衡算法,健康检查和会话保持 1:首先,我们要了解,我们的应用程序,比如java web程序,里面配置了10个z ...
- Nginx 负载均衡原理简介与负载均衡配置详解
Nginx负载均衡原理简介与负载均衡配置详解 by:授客 QQ:1033553122 测试环境 nginx-1.10.0 负载均衡原理 客户端向反向代理发送请求,接着反向代理根据某种负载机制 ...
- 搞懂分布式技术9:Nginx负载均衡原理与实践
搞懂分布式技术9:Nginx负载均衡原理与实践 本篇摘自<亿级流量网站架构核心技术>第二章 Nginx负载均衡与反向代理 部分内容. 当我们的应用单实例不能支撑用户请求时,此时就需要扩容, ...
- (转)使用LVS实现负载均衡原理及安装配置详解
使用LVS实现负载均衡原理及安装配置详解 原文:https://www.cnblogs.com/liwei0526vip/p/6370103.html
- LVS实现负载均衡原理及安装配置
LVS实现负载均衡原理及安装配置 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均衡设备F ...
随机推荐
- mysql5.5 物理删除binlog文件导致的故障
故障现象: 中午12点多,一套主从集群的主库因为没有配置大页内存,发布时导致OOM,MYSQL实例重启了,然后MHA发生了切换.切换过程正常.切换后需要把原master配置成新master的slave ...
- 在VBA中使用Windows API
VBA是一种强大的编程语言,可用于自定义Microsoft Office解决方案.通过使用VBA处理一个或多个Office应用程序对象模型,可以容易地修改Office应用程序的功能或者能够使两个或多个 ...
- sqlite升级--浅谈Android数据库版本升级及数据的迁移
Android开发涉及到的数据库采用的是轻量级的SQLite3,而在实际开发中,在存储一些简单的数据,使用SharedPreferences就足够了,只有在存储数据结构稍微复杂的时候,才会使用数据库来 ...
- java byte&0xFF
做串口端口通讯时,数据都是以byte类型发送的 普通的byte范围是-128-127,而java的byte范围是0-255 因此将数据的byte转成java的byte时,需要与0xff(1111111 ...
- Google Developing for Android 一 - 相关上下文介绍
前几天在G+上看到Google Developers站点,有一个Android系列的文章,分享到个人微博,周末闲来没事就学写了下,把它们简单的翻译了下,没想到一发不可收拾,六篇文章全部都翻译完了,有些 ...
- WEB页面采集器编写经验之一:静态页面采集器
严格意义来说,采集器和爬虫不是一回事:采集器是对特定结构的数据来源进行解析.结构化,将所需的数据从中提取出来:而爬虫的主要目标更多的是页面里的链接和页面的TITLE. 采集器也写过不少了,随便写一点经 ...
- Hadoop_配置_linux下编译eclipse插件
使用的hadoop版本为hadoop-1.2.1(对应的含源码的安装包为hadoop-1.2.1.tar.gz) 将hadoop和eclipse都解压在home中的用户目录下 /home/chen/h ...
- 编译安装带ssl 模块指定版本Python
出现这个或者fatal error: openssl/名单.h: No such file or directory.都是没有安装libssl-dev- libssl-dev包含libraries ...
- linux 进程的创建
1. 进程号: 每个进程在被初始化的时候,系统都会为其分配一个唯一标识的进程id,称为进程号: 进程号的类型为pid_t,通过getpid()和getppid()可以获取当前进程号和当前进程的父进程的 ...
- MVC执行过程
HttpRuntime中的PR方法1,封装HttpContext2,获取HttpApplication 主要做3件事a,执行本事件时主要调用Init将Global编译得到类型,b,确保Appstart ...