Zookeeper实现负载均衡原理

╰+攻爆jí腚メ 2022-01-28 13:17 319阅读 0赞

Zookeeper实现负载均衡原理

先玩个正常的,好玩的socket编程:

服务端:

首先公共的这个Handler:

  1. package com.toov5.zkDubbo;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintWriter;
  6. import java.net.Socket;
  7. public class ServerHandler implements Runnable {
  8. private Socket socket;
  9. public ServerHandler(Socket socket) {
  10. this.socket = socket;
  11. }
  12. public void run() {
  13. BufferedReader in = null;
  14. PrintWriter out = null;
  15. try {
  16. in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
  17. out = new PrintWriter(this.socket.getOutputStream(), true);
  18. String body = null;
  19. while (true) {
  20. body = in.readLine();
  21. if (body == null)
  22. break;
  23. System.out.println("Receive : " + body);
  24. out.println("Hello, " + body);
  25. }
  26. } catch (Exception e) {
  27. if (in != null) {
  28. try {
  29. in.close();
  30. } catch (IOException e1) {
  31. e1.printStackTrace();
  32. }
  33. }
  34. if (out != null) {
  35. out.close();
  36. }
  37. if (this.socket != null) {
  38. try {
  39. this.socket.close();
  40. } catch (IOException e1) {
  41. e1.printStackTrace();
  42. }
  43. this.socket = null;
  44. }
  45. }
  46. }
  47. }
  48. package com.toov5.zkDubbo;
  49. import java.io.IOException;
  50. import java.net.ServerSocket;
  51. import java.net.Socket;
  52. import org.I0Itec.zkclient.ZkClient;
  53. //##ServerScoekt服务端
  54. public class ZkServerScoekt implements Runnable {
  55. private static int port = 18080; //socket 服务启动后的所使用的 端口号
  56. public static void main(String[] args) throws IOException {
  57. ZkServerScoekt server = new ZkServerScoekt(port); //构造函数传入port
  58. // regServer(); //去zk注册
  59. Thread thread = new Thread(server);
  60. thread.start();
  61. }
  62. public ZkServerScoekt(int port) {
  63. this.port = port;
  64. }
  65. // //注册服务
  66. // public static void regServer(){
  67. // //1、建立zk连接
  68. // ZkClient zkClient = new ZkClient("192.168.91.5",5000,10000);
  69. // //2.先创建父节点
  70. // String root = "/toov5";
  71. // if (!zkClient.exists(root)) {
  72. // //如果父节点不存,直接创建父节点
  73. // zkClient.createPersistent(root); //持久节点
  74. // }
  75. // //3、创建子节点
  76. // String nodeName = root+"/service_"+port;
  77. // String nodeValue="127.0.0.1:"+port;
  78. // if (zkClient.exists(nodeName)) { //如果存在 直接删除掉
  79. // zkClient.delete(nodeName);
  80. // }
  81. // zkClient.createEphemeral(nodeName); //字节的 临时节点 如果服务宕机了 找不到了就
  82. // System.out.println("服务注册成功"+nodeName);
  83. //
  84. // }
  85. //
  86. //
  87. public void run() {
  88. ServerSocket serverSocket = null;
  89. try {
  90. serverSocket = new ServerSocket(port);
  91. System.out.println("Server start port:" + port);
  92. Socket socket = null;
  93. while (true) {
  94. socket = serverSocket.accept();
  95. new Thread(new ServerHandler(socket)).start();
  96. }
  97. } catch (Exception e) {
  98. e.printStackTrace();
  99. } finally {
  100. try {
  101. if (serverSocket != null) {
  102. serverSocket.close();
  103. }
  104. } catch (Exception e2) {
  105. }
  106. }
  107. }
  108. }

客户端:

  1. package com.toov5.zkDubbo;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintWriter;
  6. import java.net.Socket;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. public class ZkServerClient {
  10. public static List<String> listServer = new ArrayList<String>();
  11. public static void main(String[] args) {
  12. initServer();
  13. ZkServerClient client= new ZkServerClient();
  14. BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
  15. while (true) {
  16. String name;
  17. try {
  18. name = console.readLine();
  19. if ("exit".equals(name)) {
  20. System.exit(0);
  21. }
  22. client.send(name);
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }
  28. // 注册所有server
  29. public static void initServer() {
  30. listServer.clear();
  31. listServer.add("127.0.0.1:18080"); //连接服务 放到list中 存放集群地址 做负载均衡用的
  32. }
  33. // 获取当前server信息
  34. public static String getServer() {
  35. return listServer.get(0);
  36. }
  37. public void send(String name) {
  38. String server = ZkServerClient.getServer();
  39. String[] cfg = server.split(":");
  40. Socket socket = null;
  41. BufferedReader in = null;
  42. PrintWriter out = null;
  43. try {
  44. socket = new Socket(cfg[0], Integer.parseInt(cfg[1]));
  45. in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  46. out = new PrintWriter(socket.getOutputStream(), true);
  47. out.println(name);
  48. while (true) {
  49. String resp = in.readLine();
  50. if (resp == null)
  51. break;
  52. else if (resp.length() > 0) {
  53. System.out.println("Receive : " + resp);
  54. break;
  55. }
  56. }
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. } finally {
  60. if (out != null) {
  61. out.close();
  62. }
  63. if (in != null) {
  64. try {
  65. in.close();
  66. } catch (IOException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. if (socket != null) {
  71. try {
  72. socket.close();
  73. } catch (IOException e) {
  74. e.printStackTrace();
  75. }
  76. }
  77. }
  78. }
  79. }

运行结果:

1179709-20181103002051183-439886477.png

引申思路

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

1179709-20181102205858298-1781058.png

1、会员服务 项目启动时候 会在zk上面创建临时节点 /toov5 对应的value是127.0.0.1

  1. 如果是集群 /toov5 下面会会以 节点形式存储 节点名字就是IP+端口 value 分别也是 IP+端口 有些是没有用到value的哦

2、订单直接去使用 /toov5 查询下面的子节点信息,获取到之后。在本地使用负载均衡算法实现调用,决定使用哪个。

  1. 127.0.0.1:8080
  2. 127.0.0.1:8081

PS: 任何服务的底层都是 socke 技术

socket客户端 ( 客户端去获取注册信息) socket服务器端

废话不多说,项目:

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.101tec</groupId>
  4. <artifactId>zkclient</artifactId>
  5. <version>0.8</version>
  6. </dependency>
  7. </dependencies>  

这个不是Apache的哦注意

服务端代码:

  1. package com.toov5.zkDubbo;
  2. import java.io.IOException;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. import org.I0Itec.zkclient.ZkClient;
  6. //##ServerScoekt服务端
  7. public class ZkServerScoekt implements Runnable {
  8. private static int port = 18081; //socket 服务启动后的所使用的 端口号
  9. public static void main(String[] args) throws IOException {
  10. ZkServerScoekt server = new ZkServerScoekt(port); //构造函数传入port
  11. regServer(); //去zk注册
  12. Thread thread = new Thread(server);
  13. thread.start();
  14. }
  15. public ZkServerScoekt(int port) {
  16. this.port = port;
  17. }
  18. //注册服务
  19. public static void regServer(){
  20. //1、建立zk连接
  21. ZkClient zkClient = new ZkClient("192.168.91.5",5000,10000);
  22. //2.先创建父节点
  23. String root = "/toov5";
  24. if (!zkClient.exists(root)) {
  25. //如果父节点不存,直接创建父节点
  26. zkClient.createPersistent(root); //持久节点
  27. }
  28. //3、创建子节点
  29. String nodeName = root+"/service_"+port;
  30. String nodeValue="127.0.0.1:"+port;
  31. if (zkClient.exists(nodeName)) { //如果存在 直接删除掉
  32. zkClient.delete(nodeName);
  33. }
  34. zkClient.createEphemeral(nodeName); //字节的 临时节点 如果服务宕机了 找不到了就
  35. System.out.println("服务注册成功"+nodeName);
  36. }
  37. public void run() {
  38. ServerSocket serverSocket = null;
  39. try {
  40. serverSocket = new ServerSocket(port);
  41. System.out.println("Server start port:" + port);
  42. Socket socket = null;
  43. while (true) {
  44. socket = serverSocket.accept();
  45. new Thread(new ServerHandler(socket)).start();
  46. }
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. } finally {
  50. try {
  51. if (serverSocket != null) {
  52. serverSocket.close();
  53. }
  54. } catch (Exception e2) {
  55. }
  56. }
  57. }
  58. }

客户端代码:

  1. package com.toov5.zkDubbo;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintWriter;
  6. import java.net.Socket;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. public class ZkServerClient {
  10. public static List<String> listServer = new ArrayList<String>();
  11. public static void main(String[] args) {
  12. initServer();
  13. ZkServerClient client= new ZkServerClient();
  14. BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
  15. while (true) {
  16. String name;
  17. try {
  18. name = console.readLine();
  19. if ("exit".equals(name)) {
  20. System.exit(0);
  21. }
  22. client.send(name);
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }
  28. // 注册所有server
  29. public static void initServer() {
  30. listServer.clear();
  31. listServer.add("127.0.0.1:18080"); //连接服务 放到list中 存放集群地址 做负载均衡用的
  32. }
  33. // 获取当前server信息
  34. public static String getServer() {
  35. return listServer.get(0);
  36. }
  37. public void send(String name) {
  38. String server = ZkServerClient.getServer();
  39. String[] cfg = server.split(":");
  40. Socket socket = null;
  41. BufferedReader in = null;
  42. PrintWriter out = null;
  43. try {
  44. socket = new Socket(cfg[0], Integer.parseInt(cfg[1]));
  45. in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  46. out = new PrintWriter(socket.getOutputStream(), true);
  47. out.println(name);
  48. while (true) {
  49. String resp = in.readLine();
  50. if (resp == null)
  51. break;
  52. else if (resp.length() > 0) {
  53. System.out.println("Receive : " + resp);
  54. break;
  55. }
  56. }
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. } finally {
  60. if (out != null) {
  61. out.close();
  62. }
  63. if (in != null) {
  64. try {
  65. in.close();
  66. } catch (IOException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. if (socket != null) {
  71. try {
  72. socket.close();
  73. } catch (IOException e) {
  74. e.printStackTrace();
  75. }
  76. }
  77. }
  78. }
  79. }

1179709-20181102233640889-670850507.png

启动服务端后的结果:

可以看到 去zk注册了信息。服务的名字 和子节点

其中子节点是 临时节点 相当于如果服务宕机了 就查不到了哦

1179709-20181103000813179-1038410821.png

启动三次服务端后:

1179709-20181103003633234-1677646649.png

然后 去zk读取节点信息:

客户端 负载均衡轮训算法

请求次数 % 服务端数 = 服务器端口号的奇数偶数

客户端:

  1. package com.toov5.zkDubbo;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintWriter;
  6. import java.net.Socket;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. import org.I0Itec.zkclient.ZkClient;
  10. public class ZkServerClient {
  11. public static List<String> listServer = new ArrayList<String>();
  12. public static void main(String[] args) {
  13. initServer();
  14. ZkServerClient client= new ZkServerClient();
  15. BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
  16. while (true) {
  17. String name;
  18. try {
  19. name = console.readLine();
  20. if ("exit".equals(name)) {
  21. System.exit(0);
  22. }
  23. client.send(name);
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }
  29. // 注册所有server
  30. public static void initServer() {
  31. listServer.clear();
  32. // listServer.add("127.0.0.1:18080"); //连接服务 放到list中 存放集群地址 做负载均衡用的
  33. //建立zk连接
  34. ZkClient zkClient = new ZkClient("192.168.91.5",5000,10000);
  35. //读取父节点信息
  36. String root = "/toov5";
  37. List<String> children=zkClient.getChildren(root);
  38. for(String pString : children){
  39. //父+子 =完整的路径
  40. String path = root+"/"+pString;
  41. String nodeValue =zkClient.readData(path);
  42. //把值放到集合中去
  43. listServer.add(nodeValue);
  44. }
  45. //服务发现成功
  46. System.out.println("服务发现:"+listServer.toString());
  47. //然后给 serverCount赋值
  48. serverCount=listServer.size();
  49. }
  50. //请求总数
  51. private static int reqCount = 1;
  52. //服务个数
  53. private static int serverCount = 0; //初始值是0
  54. // 获取当前server信息
  55. public static String getServer() {
  56. // return listServer.get(0); 不能写死 本地负载均衡轮询算法
  57. String serverName = listServer.get(reqCount%serverCount );
  58. System.out.println("客户端请求次数:"+reqCount+"对应服务器"+serverName);
  59. reqCount++;
  60. return serverName;
  61. }
  62. public void send(String name) {
  63. String server = ZkServerClient.getServer();
  64. String[] cfg = server.split(":");
  65. Socket socket = null;
  66. BufferedReader in = null;
  67. PrintWriter out = null;
  68. try {
  69. socket = new Socket(cfg[0], Integer.parseInt(cfg[1]));
  70. in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  71. out = new PrintWriter(socket.getOutputStream(), true);
  72. out.println(name);
  73. while (true) {
  74. String resp = in.readLine();
  75. if (resp == null)
  76. break;
  77. else if (resp.length() > 0) {
  78. System.out.println("Receive : " + resp);
  79. break;
  80. }
  81. }
  82. } catch (Exception e) {
  83. e.printStackTrace();
  84. } finally {
  85. if (out != null) {
  86. out.close();
  87. }
  88. if (in != null) {
  89. try {
  90. in.close();
  91. } catch (IOException e) {
  92. e.printStackTrace();
  93. }
  94. }
  95. if (socket != null) {
  96. try {
  97. socket.close();
  98. } catch (IOException e) {
  99. e.printStackTrace();
  100. }
  101. }
  102. }
  103. }
  104. }

启动后(后端服务启动三个):

1179709-20181103010756253-4521993.png

访问一次:取模算法后的结果

1179709-20181103010942606-1195397108.png

第二次、第三次:

1179709-20181103011134065-448809196.png

看到结果 太美妙了!

美妙集中在此:

1179709-20181103011310933-1708484618.png

好好欣赏下~~

发表评论

表情:
评论列表 (有 0 条评论,319人围观)

还没有评论,来说两句吧...

相关阅读

    相关 解析负载均衡原理

    不能狭义地理解为分配给所有实际服务器一样多的工作量,因为多台服务器的承载能力各不相同,这可能体现在硬件配置、网络带宽的差异,也可能因为某台服务器身兼多职,我们所说的“均衡”,也

    相关 nginx负载均衡原理

    负载均衡在服务端开发中算是一个比较重要的特性。因为Nginx除了作为常规的Web服务器外,还会被大规模的用于反向代理前端,因为Nginx的异步框架可以处理很大的并发请求,把这些