当前位置 博文首页 > MPolaris:三. SpringCloud服务注册与发现

    MPolaris:三. SpringCloud服务注册与发现

    作者:MPolaris 时间:2021-01-30 15:36

    1. Eureka

    1.1 Eureka理解

    什么是服务治理

    Spring Cloud封装了Netflix公司开发的Eurkeka模块来实现服务治理

    在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂。管理比较复杂服务之间的依赖关系可以实现服务调用,负载均衡,容错等,实现服务发现与注册。

    什么是服务注册与发现

    Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心,而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持 心跳链接 。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。

    在服务注册与发现中,有一个注册中心。当服务器启动的时候会把当前自己服务器的信息(比如:服务地址、通讯地址等)以别名方式注册到注册中心中。另一方(消费者/服务提供者)以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用。RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的依赖关系(服务治理概念)。在任何RPC远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))

    下图左边是Eureka系统架构,右边是Dubbo系统架构

    image-20210125231142531

    Eureka包含两个组件:Eureka Server 和 Eureka Client

    • Eureka Server 提供服务注册中心

      各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

    • Eureka Client 通过注册中心进行访问

      是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中表把这个服务节点移除(默认90秒)

    1.2 单机Eureka构建步骤

    IDEA生成EurekaServer端服务注册中心

    类似物业公司

    • 建Module cloud-eureka-server7001
    • pom.xml
    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 公共模块 -->
        <dependency>
            <groupId>com.polaris</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- boot web actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- 通用配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    

    SpringBoot 1x 和 SpringBoot 2x对比

    <!-- SpringBoot1.X对应的SpringCloud eureka,不要再用了!!! -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <!-- SpringBoot2.X对应的SpringCloud eureka -->
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    
    • yml配置文件
    server:
      port: 7001
    
    eureka:
      instance:
        hostname: localhost  # eureka服务端的实例名称
      client:
        # false表示不向注册中心注册自己
        register-with-eureka: false
        # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
        fetch-registry: false     
        service-url:
          # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
    • 主启动类
    @SpringBootApplication
    @EnableEurekaServer // 声明我是服务注册中心
    public class EurekaMain7001 {
        public static void main(String[] args) {
            SpringApplication.run(EurekaMain.class);
        }
    }
    
    • 测试

    运行该Eureka Server主启动类,访问 localhost:7001,就会看到下面的服务注册中心,可以发现目前还没有任何服务入驻进服务注册中心中,在应用中显示:No instances available

    image-20210125234159844

    EurekaClient端 服务提供者cloud-provider-payment8001修改

    • pom.xml添加依赖
    <!-- 注意:与Eureka Server一样,这里SpringBoot2x不再使用spring-cloud-starter-eureka -->
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    • yml配置文件添加配置
    spring:
      application:
        name: cloud-payment-service  # 入驻Eureka服务注册中心的服务名称
    
    eureka:
      client:
        # 表示是否将自己注册进EurekaServer默认为true。
        register-with-eureka: true
        # 是否从EurekaServer抓取已有的注册信息,默认为true。
        # 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          # 单机版
          defaultZone: http://localhost:7001/eureka # 入驻的服务注册中心地址
    
    • 主启动类添加注解
    @SpringBootApplication
    @EnableEurekaClient
    public class PaymentMain {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain.class,args);
        }
    }
    
    • 测试

    注意先要启动EurekaServer,因为有了服务注册中心具体的服务提供者才能后向其中注册自己的服务

    可以发现注册到服务注册中心的服务名(图中蓝框)即为我们在yml配置文件中设置的服务名,下面页面中出现的 红字 是Eureka的 自我保护机制

    image-20210125235021654

    EurekaClient端 服务消费者cloud-sonsumer-order80修改

    与服务提供者cloud-provider-payment8001修改差不多,不再赘述

    spring:
      application:
        name: cloud-order-service # 入驻Eureka服务注册中心的服务名称
    eureka:
      client:
        # 表示是否将自己注册进EurekaServer默认为true。
        register-with-eureka: true
        # 是否从EurekaServer抓取已有的注册信息,默认为true。
        # 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          # 单机
          defaultZone: http://localhost:7001/eureka
    

    总结

    此时再回看最开始的Eureka系统架构,在服务注册中心和服务提供者没有集群的情况下,7001端口的微服务就对应了服务注册中心,而该服务不需要向服务注册中心注册自己,8001端口的微服务作为服务提供方入住到服务注册中心,8002端口的微服务作为服务消费方也同样注册到服务注册中心

    image-20210126000301308
    1.3 集群Eureka构建步骤

    集群Eureka原理

    服务注册中心Eureka Server中分为 服务注册服务发现,服务注册过程将服务信息注册进服务注册中心,服务发现过程从服务注册中心上获取服务信息,而这个过程的实质就是:将服务名作为key存储,然后根据value取得服务的调用地址。

    整个Eureka的过程如下:

    • 先启动Eureka注册中心
    • 启动服务提供者服务
    • 服务提供者服务将自身信息(比如服务地址)以别名方式注册到Eureka注册中心
    • 消费者服务在需要调用接口时,使用服务别名到注册中心获取实际的RPC远程调用地址
    • 消费者获得调用地址后,底层实际是利用 HttpClient 技术实现远程调用
    • 消费者获得服务地址后会缓存字本地JVM内存中,默认每间隔30秒更新一次服务调用地址

    那么微服务RPC远程服务调用最核心的是什么呢?

    高可用!如果注册中心只有一个,而这个注册中心出现了故障那么整个微服务就直接GG了,整个微服务环境就不可用了,所以应该搭建Eureka注册中心集群, 实现 负载均衡 + 故障容错

    那怎么实现Eureka注册中心的集群呢?用一句话总结就是 - 互相注册,相互守望。如下图所示,服务注册中心实现相互注册让彼此都知道对方的存在,也就是注册中心集群中的每一个注册中心都知道整个集群中的其他注册中心,比如如果有三个注册服务中心7001,7002,7003,那么就将7002和7003注册给7001, 将7002和7001注册给7003, 将7003和7001注册给7002, 以此类推,而这些个注册服务中心 作为一个整体对外看做一个注册服务中心。

    image-20210126001813038

    Eureaka集群环境构建

    参考cloud-eureka-server7001新建一个服务注册中心cloud-eureka-server7002

    • 修改pom.xml

    copy复制cloud-eureka-server7001的POM文件即可

    • 修改映射配置(域名映射),用不同的端口号来映射同一个地址

    找到C:\Windows\System32\drivers\etc路径下的hosts文件,将其内容修改成如下内容:

    # learn-spring-cloud
    127.0.0.1 eureka7001.com
    127.0.0.1 eureka7002.com
    
    • yml配置文件,7001与7002都修改一下(以前是单机)

    以前是单机版配置,而现在已经有两个注册中心可以看做两台机器,显然 hostname 不能再叫localhost

    更改了服务端的实例名称后,最重要的是在defaultZone中将自己注册给其他注册中心

    server:
      port: 7001
    
    eureka:
      instance:
        hostname: eureka7001.com  # eureka服务端的实例名称
      client:
        register-with-eureka: false
        fetch-registry: true
        service-url:
          # 互相注册,相互守望
          defaultZone: http://eureka7002.com:7002/eureka/
    
    server:
      port: 7002
    
    eureka:
      instance:
        hostname: eureka7002.com
      client:
        register-with-eureka: false
        fetch-registry: true
        service-url:
          # 互相注册,相互守望
          defaultZone: http://eureka7001.com:7001/eureka/
    
    • 测试

    两个服务中心已经完成了互相注册。主页面DS Replicas下面的信息就表示是这个Eureka Server相邻节点且这些节点加上自己互为一个集群。

    • 将服务提供者8001和服务消费者80发布到2台Eureka集群配置中

    修改其配置文件即可,就是将自己的微服务注册到每一个服务注册中心里去,见配置文件中的defaultZone。

    eureka:
      client:
        # 表示是否将自己注册进EurekaServer默认为true。
        register-with-eureka: true
        # 是否从EurekaServer抓取已有的注册信息,默认为true。
        # 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          # 集群版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka 
    
    • 测试
    image-20210126005558365

    服务提供者集群环境构建

    • 参考8001服务新建8002服务

    • pom.xml

    8002和8001的POM文件一样

    • yml配置文件

    将端口改为8002,其他和8001相同,两个微服务 对外暴露的服务名相同均为cloud-payment-service 从而构成集群。

    server:
      port: 8002
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
        driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
        url: jdbc:mysql://mpolaris.top:3306/cloud-test?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
    eureka:
      client:
        # 表示是否将自己注册进EurekaServer默认为true。
        register-with-eureka: true
        # 是否从EurekaServer抓取已有的注册信息,默认为true。
        # 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          # 集群版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.polaris.springcloud.entities    # 所有Entity别名类所在包
    
    • 主启动类与业务类与8001基本一致

    主启动类名字分别为PaymentMain8001与PaymentMain8002

    • controller

    修改controller,添加端口号以区分这两个具体的微服务:读取配置文件中设置的端口号。8002的修改同8001。

    @RestController
    @Slf4j
    @RequestMapping("/payment")
    public class PaymentController {
        @Resource
        private PaymentService paymentService;
    
        @Value("${server.port}")
        private String serverPort;
    
        @PostMapping("/save")
        public CommonResult save(@RequestBody Payment payment) {
            int result = paymentService.save(payment);
            log.info("===> result: " + result);
            if(result > 0) {
                return new CommonResult(200,
                        "保存到数据库成功,端口号:" + serverPort,result);
            }
            return new CommonResult(400,"保存到数据库失败",null);
        }
    
        @GetMapping("/get/{id}")
        public CommonResult<Payment> save(@PathVariable("id") Long id) {
            Payment paymentById = paymentService.getPaymentById(id);
            log.info("===> payment: " + paymentById);
            if(paymentById != null) {
                return new CommonResult(200,
                        "查询成功,端口号:" + serverPort,paymentById);
            }
            return new CommonResult(400,"查询失败",null);
        }
    }
    
    • 测试

    如图可以看到此时服务注册中心构成集群,而相同名字的服务提供方的实际提供者已经出现了两个,分别是8001和8002,也就是说服务提供方微服务也实现了集群。

    image-20210126013058983

    负载均衡

    • 发现问题:通过服务消费者80访问,只能访问到服务提供者8001
    image-20210126013401986
    • 也就是说每次访问的具体微服务都是8001端口的CLOUD-PAYMENT-SERVICE服务,这明显是不符合业务逻辑的,原因就是在消费方代码中我们将服务访问地址写死了,没有实现负载均衡,这显然是不对的,所以我们应该让80访问服务名而不是具体的服务,即应该将其改为服务提供者 微服务名称!
    image-20210126014041933
    • 同时在配置文件中通过 @LoadBalanced 注解赋予RestTemplate负载均衡能力,该负载均衡默认为轮询方式。所以将80服务的配置文件修改如下:
    @Configuration
    public class ApplicationContextConfig {
        @Bean
        @LoadBalanced //使用该注解赋予RestTemplate负载均衡的能力
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
    • 然后重启80端口,发现每次访问 CLOUD-PAYMENT-SERVICE 服务时,具体的微服务在8001和8002之间进行轮询切换。
    Video_2021-01-26_015202
    • 当然此时负载均衡我们还没有用到Ribbon,在Ribbon和Eureka整合后,消费者可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能。

    总结

    image-20210126020536213
    1.4 actuator微服务信息完善

    主机名称:服务名称修改

    发现问题:在注册中心显示的微服务中,我们发现服务名含有主机名称,这显然不是我们希望看到的

    image-20210126225633299

    怎么能解决这个问题呢,只需要修改服务提供方(8001和8002)的配置文件,向其中的eureka部分加入instance实例即可配置该服务显示的服务名称

    instance:
      instance-id: payment8001
    

    最终的整体配置文件如下:

    server:
      port: 8001
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
        driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
        url: jdbc:mysql://mpolaris.top:3306/cloud-test?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 1234321
    
    eureka:
      client:
        # 表示是否将自己注册进EurekaServer默认为true。
        register-with-eureka: true
        # 是否从EurekaServer抓取已有的注册信息,默认为true。
        # 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          # 集群版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
          # 单机版
          # defaultZone: http://localhost:7001/eureka
      instance:
        instance-id: payment8001
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.polaris.springcloud.entities
    

    8002服务的修改同上,此时再访问注册中心,看到的服务具体名称中就没有主机名了,而是我们配置好的服务名称:

    image-20210126225527532

    访问信息有IP信息提示

    发现问题:我们在鼠标移动到具体服务时,提示的地址信息中并没有服务所在具体主机的IP地址,这在开发中不方便定位具体微服务。

    image-20210126225852166

    解决方式仍然是通过配置文件,在配置文件中向其中的eureka部分加入优先ip地址即可配置该服务访问路径可以显示IP地址:

    instance:
      prefer-ip-address: true  # 访问路径可以显示IP地址
    
    image-20210126230236440

    最终的配置文件如下:

    eureka:
      client:
        # 表示是否将自己注册进EurekaServer默认为true。
        register-with-eureka: true
        # 是否从EurekaServer抓取已有的注册信息,默认为true。
        # 单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          # 集群版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      instance:
        instance-id: payment8001
        prefer-ip-address: true #访问路径可以显示IP地址
    
    1.5 服务发现Discovery

    对于注册进Eureka服务注册中心的微服务,可以通过服务发现来获取该服务的信息

    修改微服务的Controller

    向其中注入DiscoveryClient,并编写相应Controller方法

    DiscoveryClient对象中的 getServices 方法用于获取服务列表的信息,也就是有哪些服务,如cloud-payment-service服务, getInstances 方法用于获取服务列表对应的具体服务实例,如cloud-payment-service服务对应的8001和8002服务。

    import org.springframework.cloud.client.discovery.DiscoveryClient;
    //...
    @RestController
    @Slf4j
    @RequestMapping("/payment")
    public class PaymentController {
    	//...
    
        @Resource
        private DiscoveryClient discoveryClient;
    
        @GetMapping("/discovery")
        public Object discovery() {
            //获取服务列表的信息
            List<String> services = discoveryClient.getServices();
            for (String service : services) {
                log.info("===> service:" + service);
            }
    
            //根据微服务名称获取具体服务实例
            List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
            for (ServiceInstance instance : instances) {
                log.info("===> " + instance.getServiceId()
                        + "\t" + instance.getHost()
                        + "\t" + instance.getPort()
                        + "\t" + instance.getUri());
            }
            return this.discoveryClient;
        }
    	//...
    }
    

    修改主启动类

    只需要在主启动类上添加注解@EnableDiscoveryClient,修改后的主启动类:

    @SpringBootApplication
    @EnableEurekaClient
    @EnableDiscoveryClient
    public class PaymentMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8001.class,args);
        }
    }
    

    测试
    访问地址http://localhost:8001/payment/discovery,我们可以看到获取的服务信息,即完成了服务发现:

    image-20210126234422613

    后台也对服务列表进行了日志打印:

    <img src="https://gitee.com/mp2333/blog-img/raw/master/SpringCloud/20210126234348.png" alt="image-20210126234348515" https://gitee.com/mp2333/blog-img/raw/master/SpringCloud/20210126234854.png />

    1.6 Eureka自我保护(属于CAP里面的AP分支)

    自我保护机制

    保护模式主要用于一组客户端和EurekaServer之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。换句话说就是,某时刻某一个微服务不可用了,Eureka不会立刻清理,而是依旧会对该微服务的信息进行保存。

    如果在Eureka Server的首页看到以下提示,说明Eureka进入了保护模式

    image-20210126234854836

    产生原因

    为什么会产生Eureka自我保护机制? => 为了防止 EurekaClient可以正常运行,但是与EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除。

    什么是自我保护模式? => 默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之前无法正常通信,以上行为可能变得非常危险 - 因为微服务本身是健康的,只是由于网络问题链接不到EurekaServer,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题 :当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障,网络延时),那么这个节点就会进入自我保护模式。在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例,宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

    怎么禁止自我保护

    先在EurekaServer端修改配置文件即可设置关闭自我保护机制

    eureka:
      server:
        # 关闭自我保护机制,保证不可用服务被及时剔除。默认为true开启
        enable-self-preservation: false
        # 时间间隔,单位ms
        eviction-interval-time-in-ms: 2000 
    

    然后在EurekaClient端修改配置文件

    eureka:
      instance:
        instance-id: payment8001
        # Eureka客户单向服务端发送心跳的时间间隔,默然是30秒,这里改成1秒
        lease-renewal-interval-in-seconds: 1
        # Eureka服务端在收到最后一次心跳后等待时间上限,默然为90秒,超时将剔除服务,这里改成2秒
        lease-expiration-duration-in-seconds: 2
    

    这样就会使EurekaClient客户端的微服务很快死亡。

    2. Zookeeper

    2.1 Eureka停止更新

    https://github.com/Netflix/eureka/wiki

    我们可以使用SpringCloud整合Zookeeper替代Eureka

    2.2 Zookeeper理解

    Zookeeper是一个分布式协调工具,可以实现注册中心功能

    安装Zookeeper

    # 关闭Linux服务器防火墙(关闭默认端口2181也行)
    # 2181	对Client端提供服务的端口
    # 3888	选举Leader
    # 2888	集群内的机器通讯使用。(Leader使用此端口)
    systemctl stop firewalld
    systemctl status firewalld
    
    # 我这里使用的是zookeeper-3.4.11.tar.gz,解压即可
    
    # bin目录下启动zookeeper服务器
    ./zkServer.sh start
     
    # 连接zookeeper客户端
    # 如果是连接同一台主机上的zk进程,那么直接运行bin/目录下的kCli.sh,即可连接上zk。
    # 直接执行zkCli.sh命令默认以主机号 127.0.0.1,端口号 2181 来连接zk
    # 如果要连接不同机器上的zk,可以使用 -server 参数,例如:
    ./zkCli.sh -server 192.168.0.1:2181
     
    # 启动报错?
    # grep: /usr/local/zookeeper-3.4.11/bin/../conf/zoo.cfg: No such file or directory
    # 这里的原因是因为下载下来的zoo.cfg名字是zoo_sample.cfg,只需要改名字即可
    mv zoo_sample.cfg zoo.cfg
    

    Zookeeper服务器取代Eureka服务器,zk作为服务注册中心

    2.3 服务提供者

    新建cloud-provider-payment8004

    pom.xml

    <dependencies>
    	<!-- 公共模块 -->
    	<dependency>
    		<groupId>com.polaris</groupId>
    		<artifactId>cloud-api-common</artifactId>
    		<version>${project.version}</version>
    	</dependency>
    	<!-- boot web actuator -->
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-actuator</artifactId>
    	</dependency>
        
    	<!--SpringBoot整合Zookeeper客户端-->
    	<dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
    	</dependency>
        
    	<!-- 通用配置 -->
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-devtools</artifactId>
    		<scope>runtime</scope>
    		<optional>true</optional>
    	</dependency>
    	<dependency>
    		<groupId>org.projectlombok</groupId>
    		<artifactId>lombok</artifactId>
    		<optional>true</optional>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-test</artifactId>
    		<scope>test</scope>
    	</dependency>
    </dependencies>
    

    yml配置文件

    server:  
      # 8004表示注册到zookeeper服务器的支付服务提供者端口号
      port: 8004
    spring:
      application:
        # 服务别名 - 注册zookeeper到注册中心的名称
        name: cloud-provider-payment
      cloud:
        zookeeper:
          # zookeeper访问地址
          connect-string: mpolaris.top:2181
    

    主启动类

    @SpringBootApplication
    //该注解用于向使用consul或zookeeper作为注册中心时注册服务
    @EnableDiscoveryClient
    public class PaymentMain8004 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8004.class,args);
        }
    }
    

    controller

    @RestController
    @Slf4j
    @RequestMapping("/payment")
    public class PaymentController {
        @Value("${server.port}")
        private String serverPort;
    
        @RequestMapping(value = "/zk")
        public String paymentZk() {
            return "===> SpringCloud with zookeeper:" 
                    + serverPort 
                    + "\t" 
                    + UUID.randomUUID().toString();
        }
    }
    

    启动8004注册进zookeeper

    • 启动zk: zkServer.sh start
    • 启动8004后报错
    image-20210127012232353
    • why?

      • 解决zookeeper版本jar包冲突问题
    image-20210127012945364
    • 排除zk冲突后的新pom.xml
    <!--SpringBoot整合Zookeeper客户端-->
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
    	<!-- 先排除自带的zookeeper3.5.3 -->
    	<exclusions>
    		<exclusion>
    			<groupId>org.apache.zookeeper</groupId>
    			<artifactId>zookeeper</artifactId>
    		</exclusion>
    	</exclusions>
    </dependency>
    
    <!-- 添加zookeeper3.4.11版本zookeeper -->
    <dependency>
    	<groupId>org.apache.zookeeper</groupId>
    	<artifactId>zookeeper</artifactId>
    	<version>3.4.11</version>
    </dependency>
    

    验证测试1

    image-20210127013617048

    访问 http://localhost:8004/payment/zk

    image-20210127014618342

    验证测试2

    image-20210127014115054

    获得json串后用在线工具查看如下

    image-20210127014501415

    思考

    服务节点是临时节点还是持久节点? => 临时节点

    image-20210127015507188
    2.4 服务消费者

    新建cloud-consumerzk-order80

    pom.xml