1. 程式人生 > >Zookeeper實現Master選舉(哨兵機制)

Zookeeper實現Master選舉(哨兵機制)

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

 

執行,啟動兩個埠

一個主 一個從 

然後關掉主,重新選舉從(需要等待一段時間,強制關閉有延遲的)