master選舉使用場景及結構
現在很多時候我們的服務需要7*24小時工作,假如一台機器掛了,我們希望能有其它機器頂替它繼續工作。此類問題現在多采用master-salve模式,也就是常說的主從模式,正常情況下主機提供服務,備機負責監聽主機狀態,當主機異常時,可以自動切換到備機繼續提供服務(這里有點兒類似於數據庫主庫跟備庫,備機正常情況下只監聽,不工作),這個切換過程中選出下一個主機的過程就是master選舉。
對於以上提到的場景,傳統的解決方式是采用一個備用節點,這個備用節點定期給當前主節點發送ping包,主節點收到ping包后會向備用節點發送應答ack,當備用節點收到應答,就認為主節點還活着,讓它繼續提供服務,否則就認為主節點掛掉了,自己將開始行使主節點職責。如圖1所示:
使用Zookeeper實現服務master選舉
在哨兵機制中 只能一個主 多個從!
使用zk可以實現之!
原理: 多個服務器在啟動時候,會在Zookeeper上創建相同的臨時節點,誰如果能夠創建成功,誰就為主!(節點保證唯一)
如果主服務器宕機,會話連接斷開。臨時節點刪除。其他節點服務選舉開始
創建項目:
pom文件: 注意springboot 整合 zk的包
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> <exclusions> <exclusion> <artifactId>slf4j-api</artifactId> <groupId>org.slf4j</groupId> </exclusion> <exclusion> <artifactId>log4j</artifactId> <groupId>log4j</groupId> </exclusion> <exclusion> <artifactId>slf4j-log4j12</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
后台controller
package com.toov5.zkMaster; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class IndexController { @Value("${server.port}") private String serverPort; @RequestMapping("getServerInfo") public String getServerInfo(){ return "serverPort"+serverPort+(ElectionMaster.isSurvival? "選舉為主" :"選舉為從"); } public static void main(String[] args) { //1,項目啟動的時候 在zk創建臨時節點 //2,誰能夠創建成功誰就是主服務器 //3,使用服務監聽節點是否被刪除,如果被刪。 重新開始創建節點 } }
啟動加載運行類
package com.toov5.zkMaster; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class MyApplicationRunner implements ApplicationRunner{ private ZkClient zkClient = new ZkClient("192.168.91.5:2181"); private String path = "/election"; @Value("${server.port}") private String serverPort; //啟動后執行的方法 public void run(ApplicationArguments args) throws Exception { //重寫這個方法 System.out.println("項目啟動成功!"); //1,項目啟動的時候 在zk創建臨時節點 createEphemeral(); //2,誰能夠創建成功誰就是主服務器 //3,使用服務監聽節點是否被刪除,如果被刪。 重新開始創建節點 zkClient.subscribeDataChanges(path, new IZkDataListener() { //返回節點如果被刪除后 返回通知 public void handleDataDeleted(String arg0) throws Exception { //重新創建(選舉) System.out.println("開始重新選舉策略"); createEphemeral(); } public void handleDataChange(String arg0, Object arg1) throws Exception { // TODO Auto-generated method stub } }); } private void createEphemeral(){ try { zkClient.createEphemeral(path); System.out.println("serverport"+serverPort+",選舉成功!"); ElectionMaster.isSurvival=true; //標志位true 單個jvm共享 } catch (Exception e) { System.out.println("該節點已經存在"); ElectionMaster.isSurvival=false; } } }
全局變量
package com.toov5.zkMaster; public class ElectionMaster { //服務器info信息 是否存活 public static boolean isSurvival; //靜態的 標志下 服務器是否還存活 }
運行類
package com.toov5.zkMaster; public class ElectionMaster { //服務器info信息 是否存活 public static boolean isSurvival; //靜態的 標志下 服務器是否還存活 }
yml:
server: port: 8088
運行,啟動兩個端口
一個主 一個從
然后關掉主,重新選舉從(需要等待一段時間,強制關閉有延遲的)