基础知识:
项目路径: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的过程中需要等待一段时间,这段时间没法对外提供服务,