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