ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,提供的功能包括配置维护、名字服务、分布式同步、组服务等。

ZooKeeper会维护一个树形的数据结构,类似于Windows资源管理器目录,其中EPHEMERAL类型的节点会随着创建它的客户端断开而被删除,利用这个特性很容易实现软负载均衡。

基本原理是,每个应用的Server启动时创建一个EPHEMERAL节点,应用客户端通过读取节点列表获得可用服务器列表,并订阅节点事件,有Server宕机断开时触发事件,客户端监测到后把该Server从可用列表中删除。

来看示例,这里用了BIO模型编写了一个接收/应答的小程序用于演示效果,优点就是简单。为了方便后面的改造,客户端每次发送消息时都会读取服务器列表并从新建立连接。后边会看到只需要几十行代码即可改造为使用ZooKeeper的软负载模式。

Server代码

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.PrintWriter;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7.  
  8. public class SimpleServer implements Runnable {
  9.  
  10. public static void main(String[] args) throws IOException {
  11. int port = 18080;
  12. SimpleServer server = new SimpleServer(port);
  13. Thread thread = new Thread(server);
  14. thread.start();
  15. }
  16.  
  17. private int port;
  18.  
  19. public SimpleServer(int port) {
  20. this.port = port;
  21. }
  22.  
  23. @Override
  24. public void run() {
  25. ServerSocket server = null;
  26. try {
  27. server = new ServerSocket(port);
  28. System.out.println("Server started");
  29. Socket socket = null;
  30. while (true) {
  31. socket = server.accept();
  32. new Thread(new SimpleServerHandler(socket)).start();
  33. }
  34. } catch(IOException ex) {
  35. ex.printStackTrace();
  36. } finally {
  37. if (server != null) {
  38. try {
  39. server.close();
  40. } catch (IOException e) {}
  41. }
  42. }
  43. }
  44. }
  45.  
  46. class SimpleServerHandler implements Runnable {
  47.  
  48. private Socket socket;
  49.  
  50. public SimpleServerHandler(Socket socket) {
  51. this.socket = socket;
  52. }
  53.  
  54. @Override
  55. public void run() {
  56. BufferedReader in = null;
  57. PrintWriter out = null;
  58. try {
  59. in = new BufferedReader(new InputStreamReader(
  60. this.socket.getInputStream()));
  61. out = new PrintWriter(this.socket.getOutputStream(), true);
  62. String body = null;
  63. while (true) {
  64. body = in.readLine();
  65. if (body == null)
  66. break;
  67. System.out.println("Receive : " + body);
  68. out.println("Hello, " + body);
  69. }
  70.  
  71. } catch (Exception e) {
  72. if (in != null) {
  73. try {
  74. in.close();
  75. } catch (IOException e1) {
  76. e1.printStackTrace();
  77. }
  78. }
  79. if (out != null) {
  80. out.close();
  81. }
  82. if (this.socket != null) {
  83. try {
  84. this.socket.close();
  85. } catch (IOException e1) {
  86. e1.printStackTrace();
  87. }
  88. this.socket = null;
  89. }
  90. }
  91. }
  92. }

客户端代码

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.PrintWriter;
  5. import java.net.Socket;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8.  
  9. public class SimpleClient {
  10.  
  11. private static List<String> servers = new ArrayList<>();
  12.  
  13. public static void main(String[] args) {
  14.  
  15. initServerList();
  16.  
  17. SimpleClient client = new SimpleClient();
  18. BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
  19. while (true) {
  20. String name;
  21. try {
  22. name = console.readLine();
  23. if("exit".equals(name)) {
  24. System.exit(0);
  25. }
  26. client.send(name);
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32.  
  33. private static void initServerList() {
  34. servers.clear();
  35. servers.add("127.0.0.1:18080");
  36. }
  37.  
  38. public static String getServer() {
  39. return servers.get(0);
  40. }
  41.  
  42. public SimpleClient() {
  43. }
  44.  
  45. public void send(String name) {
  46.  
  47. String server = SimpleClient.getServer();
  48. String[] cfg = server.split(":");
  49.  
  50. Socket socket = null;
  51. BufferedReader in = null;
  52. PrintWriter out = null;
  53. try {
  54. socket = new Socket(cfg[0], Integer.parseInt(cfg[1]));
  55. in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  56. out = new PrintWriter(socket.getOutputStream(), true);
  57.  
  58. out.println(name);
  59. while(true) {
  60. String resp = in.readLine();
  61. if(resp == null)
  62. break;
  63. else if(resp.length() > 0) {
  64. System.out.println("Receive : " + resp);
  65. break;
  66. }
  67. }
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. } finally {
  71. if (out != null) {
  72. out.close();
  73. }
  74. if (in != null) {
  75. try {
  76. in.close();
  77. } catch (IOException e) {
  78. e.printStackTrace();
  79. }
  80. }
  81. if (socket != null) {
  82. try {
  83. socket.close();
  84. } catch (IOException e) {
  85. e.printStackTrace();
  86. }
  87. }
  88. }
  89. }
  90. }

运行测试,服务器端输出截图:

客户端输出截图:

很好,一切运行正常。

接下来添加ZooKeeper部分。为了演示效果更好,修改一下ZooKeeper的配置文件,以便于服务器断开后能更快的被监测到。主要是减小Session的超时时间

zookeeper/conf/zoo.cfg

  1. tickTime=2000
  2. initLimit=2
  3. syncLimit=5
  4. dataDir=D:\\ZooKeeper\\zookeeper-3.4.8\\data
  5. clientPort=2181
  6. http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
  7. minSessionTimeout=2000
  8. maxSessionTimeout=5000

在项目中添加zkclient的maven依赖

  1. <!-- http://mvnrepository.com/artifact/com.101tec/zkclient -->
  2. <dependency>
  3. <groupId>com.101tec</groupId>
  4. <artifactId>zkclient</artifactId>
  5. <version>0.8</version>
  6. </dependency>

Server代码

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.PrintWriter;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7.  
  8. import org.I0Itec.zkclient.ZkClient;
  9.  
  10. public class SimpleServer implements Runnable {
  11.  
  12. public static void main(String[] args) throws IOException {
  13. int port = 18080;
  14. SimpleServer server = new SimpleServer(port);
  15. Thread thread = new Thread(server);
  16. thread.start();
  17. }
  18.  
  19. private int port;
  20.  
  21. public SimpleServer(int port) {
  22. this.port = port;
  23. }
  24.  
  25. private void regServer() {
  26. //向ZooKeeper注册当前服务器
  27. ZkClient client = new ZkClient("127.0.0.1:2181", 60000, 1000);
  28. String path = "/test/server" + port;
  29. if(client.exists(path))
  30. client.delete(path);
  31. client.createEphemeral(path, "127.0.0.1:" + port);
  32. }
  33.  
  34. @Override
  35. public void run() {
  36. ServerSocket server = null;
  37. try {
  38. server = new ServerSocket(port);
  39. regServer();
  40. System.out.println("Server started at " + port);
  41. Socket socket = null;
  42. while (true) {
  43. socket = server.accept();
  44. new Thread(new SimpleServerHandler(socket)).start();
  45. }
  46. } catch(IOException ex) {
  47. ex.printStackTrace();
  48. } finally {
  49. if (server != null) {
  50. try {
  51. server.close();
  52. } catch (IOException e) {}
  53. }
  54. }
  55.  
  56. }
  57. }
  58. //SimpleServerHandler略

客户端代码

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.PrintWriter;
  5. import java.net.Socket;
  6. import java.util.ArrayList;
  7. import java.util.Arrays;
  8. import java.util.List;
  9. import java.util.Random;
  10.  
  11. import org.I0Itec.zkclient.IZkChildListener;
  12. import org.I0Itec.zkclient.ZkClient;
  13.  
  14. public class SimpleClient {
  15.  
  16. private static List<String> servers = new ArrayList<>();
  17.  
  18. public static void main(String[] args) {
  19.  
  20. initServerList();
  21.  
  22. SimpleClient client = new SimpleClient();
  23. BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
  24. while (true) {
  25. String name;
  26. try {
  27. name = console.readLine();
  28. if("exit".equals(name)) {
  29. System.exit(0);
  30. }
  31. client.send(name);
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }
  37.  
  38. private static void initServerList() {
  39. //启动时从ZooKeeper读取可用服务器
  40. String path = "/test";
  41. ZkClient zkClient = new ZkClient("127.0.0.1:2181", 60000, 1000);
  42. List<String> childs = zkClient.getChildren(path);
  43. servers.clear();
  44. for(String p : childs) {
  45. servers.add(zkClient.readData(path + "/" + p));
  46. }
  47. //订阅节点变化事件
  48. zkClient.subscribeChildChanges("/test", new IZkChildListener() {
  49. @Override
  50. public void handleChildChange(String parentPath, List<String> currentChilds)
  51. throws Exception {
  52. System.out.println(String.format("[ZookeeperRegistry] service list change: path=%s, currentChilds=%s", parentPath, currentChilds.toString()));
  53. servers.clear();
  54. for(String p : currentChilds) {
  55. servers.add(zkClient.readData(path + "/" + p));
  56. }
  57. System.out.println("Servers: " + servers.toString());
  58. }
  59. });
  60.  
  61. }
  62.  
  63. public static String getServer() {
  64. return servers.get(new Random().nextInt(servers.size()));
  65. }
  66. //其他无变化, 略
  67. }

分别启动Server和Client,然后修改Server的端口号,再启动一个实例,可以看到客户端检测到了这个新服务器的存在

在客户端发送一些消息,可以看到被随机的分发到两个Server上处理

接下来关闭其中一个Server,可以看到客户端几秒钟后监测到这个事件并自动删除了该服务器。

这样,我们就实现了基于ZooKeeper的软负载均衡。

使用ZooKeeper实现软负载均衡(原理)的更多相关文章

  1. 【Zookeeper】实现负载均衡原理

    一.思路 使用Zookeeper实现负载均衡原理,服务器端将启动的服务注册到,zk注册中心上,采用临时节点.客户端从zk节点上获取最新服务节点信息,本地使用负载均衡算法,随机分配服务器. 服务端启动的 ...

  2. Zookeeper实现负载均衡原理

    先玩个正常的,好玩的socket编程: 服务端: 首先公共的这个Handler: package com.toov5.zkDubbo; import java.io.BufferedReader; i ...

  3. 使用Zookeeper实现负载均衡原理

    思路 使用Zookeeper实现负载均衡原理,服务器端将启动的服务注册到,zk注册中心上,采用临时节点.客户端从zk节点上获取最新服务节点信息,本地使用负载均衡算法,随机分配服务器. 创建项目工程 M ...

  4. 六大Web负载均衡原理与实现

    还有个姊妹篇也可以参考这个文章:LVS(Linus Virtual Server):三种负载均衡方式比较+另三种负载均衡方式, LVS 实现了负载均衡,NAT,DR,TUN zookeeper使用ZA ...

  5. zookeeper如何实现负载均衡的?(具体连接哪一个zookeeper服务器的选择?)阿里面试

    如果想了解web 6大负载均衡算法,参考:六大Web负载均衡原理与实现 主要是三点:负载均衡算法,健康检查和会话保持 1:首先,我们要了解,我们的应用程序,比如java web程序,里面配置了10个z ...

  6. Nginx 负载均衡原理简介与负载均衡配置详解

    Nginx负载均衡原理简介与负载均衡配置详解   by:授客  QQ:1033553122   测试环境 nginx-1.10.0 负载均衡原理 客户端向反向代理发送请求,接着反向代理根据某种负载机制 ...

  7. 搞懂分布式技术9:Nginx负载均衡原理与实践

    搞懂分布式技术9:Nginx负载均衡原理与实践 本篇摘自<亿级流量网站架构核心技术>第二章 Nginx负载均衡与反向代理 部分内容. 当我们的应用单实例不能支撑用户请求时,此时就需要扩容, ...

  8. (转)使用LVS实现负载均衡原理及安装配置详解

    使用LVS实现负载均衡原理及安装配置详解 原文:https://www.cnblogs.com/liwei0526vip/p/6370103.html

  9. LVS实现负载均衡原理及安装配置

    LVS实现负载均衡原理及安装配置 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均衡设备F ...

随机推荐

  1. 强势回归,Linux blk用实力证明自己并不弱!

    Flash的出现把存储的世界搅翻了天,仿佛一夜之间发现了新大陆,所有旧世界的东西都变得笨拙.NVMe驱动义无反顾地抛弃了Linux blk,开发自己的队列管理. 当第一次看到NVMe重新使用Linux ...

  2. STM32F4读写内部FLASH【使用库函数】

    STM32F4Discovery开发帮使用的STM32F407VGT6芯片,内部FLASH有1M之多.平时写的代码,烧写完之后还有大量的剩余.有效利用这剩余的FLASH能存储不少数据.因此研究了一下S ...

  3. Java借助Runtime调用外部程序阻塞的代码

    有时候在java代码中会调用一些外部程序,比如SwfTools来转换swf.ffmpeg来转换视频等.如果你的代码这样写:Runtime.getRuntime().exec(command),会发现程 ...

  4. Spark代码调优(一)

    环境极其恶劣情况下: import org.apache.spark.SparkContext import org.apache.spark.rdd.RDD import org.apache.sp ...

  5. Linux通过NAT方式配置网络

    步骤:1.将虚拟机的网卡VMware Network Adapter VMnet8改成DHCP自动获取IP.2.将Linux虚拟机的网卡自定义连接到VMware Network Adapter VMn ...

  6. TensorFlow中权重的随机初始化

    一开始没看懂stddev是什么参数,找了一下,在tensorflow/python/ops里有random_ops,其中是这么写的: def random_normal(shape, mean=0.0 ...

  7. Unity3D图片的下载及保存

    Unity3D图片的下载及保存 分类: Unity3D 2013-06-24 15:03 3609人阅读 评论(2) 收藏 举报 Unity3D图片URL 代码如下: [csharp] view pl ...

  8. Oauth2.0

    注:Access token 为第三方平台的token,在用户授权时使用Refresh Token为用户的token get/post参数时,一般采用字典排序

  9. lombok

    参考http://blog.csdn.net/mlinge/article/details/51340362

  10. hibernate配置文件hibernate.cfg.xml的详细解释

    <!--标准的XML文件的起始行,version='1.0'表明XML的版本,encoding='gb2312'表明XML文件的编码方式-->                 <?x ...