1 Zookeeper实现注册中心
1.1 项目准备
创建一个父工程,名称为handwritten-zookeeper-discovery,并在父工程下创建两个Spring Boot项目,分别为order-service和user-service,并在order-service中准备一个OrderController。
1 2 3 4 5 6 7 8 9
| @RestController @RequestMapping("/order") public class OrderController{ @RequestMapping("/query") public String query(){ System.out.printLn("query..."); return "query..."; } }
|
1.2 实现服务注册
以order-service项目为例,实现服务注册的功能
1.2.1 事件监听机制扩展Spring Boot源码
1 2 3 4 5 6
| public class JackApplicationListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { System.out.println("事件监听机制的回调..."); } }
|
Spring SPI: MATA-INF/spring.factories
1
| org.springframework.context.ApplicationListener=com.jack.zkorderservice.initializer.JackApplicationListener
|
1.2.2 定义服务注册接口和实现类
1 2 3 4 5 6 7 8 9 10
| public interface ServiceRegistry{ void register(); }
public class ZookeeperServiceRegistry implements ServiceRegistry{ @Override public void register(){ } }
|
1.2.3 构造函数初始化curatorFramework
(1)引入curator依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.2.1</version> </dependency>
|
(2)构造函数初始化curator
1 2 3 4 5 6 7 8 9 10 11
| private CuratorFramework curatorFramework;
public ZookeeperServiceRegistry(String zkServer){ this.curatorFramework = CUratorFrameworkFactory .build() .connectionTimeoutMs(20000) .connectString(zkServer) .retryPolicy(new ExponentialBackoffRetry(1000, 3)) .build(); curatorFramework.start(); }
|
1.2.4 配置application.properties
1 2 3 4
| server.port=9091 zk.service-name=order-service zk.server=192.168.0.8:2181 zk.ip=127.0.0.1
|
1.2.5 完善监听器回调逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class JackApplicationListener implements ApplicationListener<ContextRefreshedEvent>{ @Override public void onApplicationEvent(ContextRefreshedEvent event){ System.out.printLn("事件监听机制得回调。。。"); Environment environment = event.getApplicationContext().getEnvironment(); String serviceName = environment.getProperty("zk.service-name"); String ip = environment.getProperty("zk.ip"); String port = environment.getProperty("server.port"); String zkServer = environment.getProperty("zk.server"); ServiceRegistry zookeeperServiceRegistry = new ZookeeperServiceRegistry(zkServer); zookeeperServiceRegistry.register() } }
|
1.2.6 完善ZookeeperRegistry构造函数
1 2
| ServiceRegistry zookeeperServiceRegistry = new ZookeeperServiceRegistry(serviceName,ip,port,zkServer);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class ZookeeperServiceRegistry implements ServiceRegistry{ private CuratorFramework curatorFramework; private final String ip; private final String port; private final String serviceName; private final String basePath="/jack-registry"; public ZookeeperServiceRegistry(String serviceName, String ip, String port, String zkServer) { this.serviceName=serviceName; this.ip=ip; this.port=port; this.curatorFramework = CuratorFrameworkFactory .builder() .connectionTimeoutMs(20000) .connectString(zkServer) .retryPolicy(new ExponentialBackoffRetry(1000, 3)) .build(); curatorFramework.start(); } }
|
1.2.7 完善ZookeeperServiceRegistry#register()方法
1 2 3 4 5 6 7 8 9 10 11 12
| public void register() { String serviceNamePath=basePath+"/"+serviceName; try { if(curatorFramework.checkExists().forPath(serviceNamePath)==null) { this.curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(serviceNamePath); } String urlNode = curatorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(serviceNamePath + "/" + ip +":"+ port); System.out.println("服务 "+urlNode+" 注册成功..."); } catch (Exception e) { e.printStackTrace(); } }
|
1.3 注册多个服务实例
(1)修改application.properties文件
(2)允许多个实例的方式进行注册
(3)观察order-service节点下的实例
1.4 手写服务发现
1.4.1 定义服务发现接口和实现类
1 2 3 4
| public interface ServiceDiscovery { List<String> discovery(String serviceName); void registerWatch(String serviceNamePath); }
|
1 2 3 4 5 6 7 8 9
| public class ServiceDiscoveryImpl implements ServiceDiscovery{ @Override public List<String> discovery(String serviceName) { return null; } @Override public void registerWatch(String serviceNamePath) { } }
|
1.4.2 定义配置信息
1 2
| server.port=6666 zk.server=192.168.0.8:2181
|
1.4.3 完善ServiceDiscoveryImpl构造函数
(1)引入curator依赖
1 2 3 4 5
| <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.2.1</version> </dependency>
|
(2)完善构造函数
1 2 3 4 5 6 7 8 9 10 11
| private final CuratorFramework curatorFramework; private final String basePath="/jack-registry"; public ServiceDiscoveryImpl(String zkServer) { this.curatorFramework = CuratorFrameworkFactory .builder() .connectionTimeoutMs(20000) .connectString(zkServer) .retryPolicy(new ExponentialBackoffRetry(1000, 3)) .build(); curatorFramework.start(); }
|
1.4.4 完善ServiceDiscoveryImpl#discovery方法
1 2 3 4 5 6 7 8 9 10 11 12
| @Override public List<String> discovery(String serviceName) { String serviceNamePath=basePath + "/" + serviceName; try { if (this.curatorFramework.checkExists().forPath(serviceNamePath)!=null){ return this.curatorFramework.getChildren().forPath(serviceNamePath); } } catch (Exception e){ e.printStackTrace(); } return null; }
|
1.4.5 将ServiceDiscoveryImpl交给Spring IoC容器管理
(1)创建ZookeeperDiscoveryAutoConfiguration配置类
1 2 3 4 5 6 7 8 9
| @Configuration public class ZookeeperDiscoveryAutoConfiguration { @Resource private Environment environment; @Bean public ServiceDiscoveryImpl serviceDiscovery(){ return new ServiceDiscoveryImpl(environment.getProperty("zk.server")); } }
|
(2)通过spring spi机制管理配置类
1
| org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.jack.zkuserservice.config.ZookeeperDiscoveryAutoConfiguration
|
1.4.6 负载均衡
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface LoadBalance { String select(List<String> urls); }
public class RandomLoadBalance implements LoadBalance{ @Override public String select(List<String> urls) { int len=urls.size(); Random random=new Random(); return urls.get(random.nextInt(len)); } }
|
1.4.7 ServiceDiscoveryTest测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @SpringBootTest public class ServiceDiscoveryTest { @Resource private ServiceDiscovery serviceDiscovery; private List<String> urls; @Test public void discovery() throws IOException { urls = this.serviceDiscovery.discovery("order-service"); LoadBalance loadBalance=new RandomLoadBalance(); String url = loadBalance.select(urls); System.out.println("目标url为: "+url); String response = new RestTemplate().getForObject("http://" + url + "/order/query", String.class); System.out.println("response: "+response); this.serviceDiscovery.registerWatch("/jack-registry/order-service",urls); System.in.read(); } }
|
1.4.8 添加对节点的监听
监听order-service子节点的变化,完善ServiceDiscoveryImpl#registerWatch方法
1 2 3 4 5 6 7 8 9 10 11 12
| public void registerWatch(String serviceNamePath) { CuratorCache curatorCache = CuratorCache.build(curatorFramework, serviceNamePath); CuratorCacheListener listener = CuratorCacheListener.builder().forPathChildrenCache(serviceNamePath, curatorFramework, new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { System.out.println("最新的urls为: "+curatorFramework.getChildren().forPath(serviceNamePath)); } }).build(); curatorCache.listenable().addListener(listener); curatorCache.start(); }
|
2 Spring Cloud Zookeeper实现注册中心
(1)创建spring-cloud-zookeeper的spring boot项目,Spring Boot版本为2.7.2
(2)定义Spring Cloud的版本管理
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.3</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement>
|
(3)引入spring cloud zookeeper注册中心的依赖
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> </dependency>
|
(4)写注册中心相关的配置
1 2 3 4 5 6 7 8 9 10
| spring: cloud: zookeeper: connect-string: 192.168.0.8:2181 discovery: root: /services/registries application: name: spring-cloud-zookeeper server: port: 9091
|
(5)启动Spring Boot项目,观察Zookeeper Server上的数据
(6)服务发现代码
1 2 3 4 5 6
| @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/sc-zk-discovery") public List<ServiceInstance> serviceUrl() { return discoveryClient.getInstances("spring-cloud-zookeeper"); }
|
(7)服务注册实现核心入口
AbstractAutoServiceRegistration#onApplicationEvent