基础知识:

项目路径:https://gitee.com/zhangjunqing/spring-boot  查找下面四个项目就可以了

zookeeper版本为zookeeper-3.4.9(需要查找合适的curator版本)

 

 

                            三个spring bootweb项目:

spring boot quartz 串行_大数据

                                           官方案例leader:

spring boot quartz 串行_java_02

 

 

思路:新建三个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提供服务

spring boot quartz 串行_大数据_03

 

 

spring boot quartz 串行_大数据_04

spring boot quartz 串行_apache_05

 

 

二:强制关闭8080的web服务,一会后观察 leader成功跳到8081,提供服务

spring boot quartz 串行_spring_06

三:启动8080,关闭8081,8082  一段时间后8080又重新拿回leader身份

spring boot quartz 串行_apache_07

 

 

特别注意:

     在zookeeper重新选择leader的过程中需要等待一段时间,这段时间没法对外提供服务,