1. 程式人生 > >zookeeper curator學習(配合spring boot模擬leader選舉)

zookeeper curator學習(配合spring boot模擬leader選舉)

round 一段時間 .cn cti -s col tid void sco

基礎知識:http://www.cnblogs.com/LiZhiW/p/4930486.html

項目路徑:https://gitee.com/zhangjunqing/spring-boot 查找下面四個項目就可以了

zookeeper版本為zookeeper-3.4.9(需要查找合適的curator版本)

三個spring bootweb項目:技術分享

官方案例leader:技術分享

思路:新建三個spring boot web項目,在這三個web項目中定義一個過濾器來在初始化時搶奪leader權限,如果發現強奪到leader權限,則可以提供服務,如果沒有搶奪到請求則提示無法提供服務,當leader死掉時,curator會重新選舉新的leader(這裏只是提供選舉的一個使用思路,本身這個選舉也是舉個列子

)。

1 pom文件依賴如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.springboot</groupId>
  <
artifactId>springboot-zookeeper-leaderTest01</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springoot</name> <url>http://maven.apache.org</url> <!-- 使用spring boot必須有這依賴 --> <parent> <
groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.7.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- spring boot依賴jar包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 以下是 curator依賴jar包 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-test</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-x-discovery</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

2:spring boot啟動類如下:

package com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@ServletComponentScan(basePackages= {"com.springboot.filter"})//此處用於掃描過濾器
@SpringBootApplication  //註意此類的包地址應該是最大的包,這樣他就可以自動掃描其下包的所有類.否則也可以(scanBasePackages={"com.springboot.controller"})指定要掃描的包
public class App {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(App.class, args);
    }
}

3:controller層如下:

package com.springboot.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    
    //如果能提供服務,請求返回值就在此處
    @GetMapping("/say")
    public @ResponseBody String sayHello() {
        return "is servicing now";
    }
    
    
}

3:進行leader選舉根據其是否是leader身份來判斷是否提供服務

package com.springboot.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.retry.ExponentialBackoffRetry;

@WebFilter(filterName = "myFilter", urlPatterns = "/*")
public class MyFilter implements Filter {
    
    //zookeeper集群地址
    public static final String ZOOKEEPERSTRING = "192.168.99.129:2181,192.168.99.153:2181,192.168.99.171:2181";
    
    //zookeeper鏈接client
    CuratorFramework    client = null;
    
    //選舉類
    LeaderSelector leaderSelector;
    String dataPath = "/tomcat/leader";
    
    public void init(FilterConfig filterConfig) throws ServletException {
            //初始化連接
            client = CuratorFrameworkFactory.newClient(ZOOKEEPERSTRING, new ExponentialBackoffRetry(1000, 3));
            
            //啟動一個客戶端
            client.start();
            
            
            //實例化一個選舉器//特別註意LeaderListener,如果takeLeadership方法執行完畢,則會自動釋放leaders身份,故我們使用countDownLatch來阻塞此方法使其不主動放棄leaders身份
            //同時這也給我們一個思路,我們可以通過在外部修改countDownLatch的值來控制leader是否主動放棄其身份
            leaderSelector = new LeaderSelector(client, dataPath, new LeaderListener());//放棄leader權限後還可以參加選舉
            leaderSelector.autoRequeue();
            
            //開始服務
            leaderSelector.start();
            
    }
    
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        
        //判斷是否現在是leader
        if(leaderSelector.hasLeadership()) {
            chain.doFilter(request, response);
        }else {
            response.getWriter().write("is not servicing");
        }
        //chain.doFilter(request, response);
    }
    
    public void destroy() {
        // TODO Auto-generated method stub
        
    }
    
}

4:leader身份監聽器:

package com.springboot.filter;

import java.util.concurrent.CountDownLatch;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;

public class LeaderListener extends LeaderSelectorListenerAdapter{
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    //當這個方法執行完畢以後,leader會自動放棄身份,我們在這個項目當中使用countDownLatch阻塞不主動放棄身份
    //這裏給我們提供思路,我們可以在外部動態修改countDownLatch值來使其主動放棄leader身份
    public void takeLeadership(CuratorFramework client) throws Exception {
        System.out.println("一直阻塞當中,一直提供服務");
        
        /**
         * 使用countdownLath,使leader永遠不放棄自己的地位
         */
        countDownLatch.await();;
    }


}

5:進行測試

一: 在不同端口啟動項目3個此項目,發現8080現為leader提供服務

技術分享

技術分享

技術分享

二:強制關閉8080的web服務,一會後觀察 leader成功跳到8081,提供服務

技術分享

三:啟動8080,關閉8081,8082 一段時間後8080又重新拿回leader身份

技術分享

特別註意:

在zookeeper重新選擇leader的過程中需要等待一段時間,這段時間沒法對外提供服務,

zookeeper curator學習(配合spring boot模擬leader選舉)