Skip to content

服务注册中心

在cloud-consumer-order80工程的微服务中,服务提供方所在的IP地址和端口号硬编码到订单微服务中,会存在非常多的问题:

  1. 微服务的IP地址或者端口号发生了变化,则支付微服务将变得不可用。
  2. RestTemplate只负责调用,无法实现微服务的负载均衡功能。
  3. 如果后续需要增加订单服务,需要部署更多的订单微服务,硬编码订单微服务则后续的维护会变得异常复杂。

1. 服务注册中心之Eureka

目前Eureka已经停更进入维护阶段,后续不再新功能支持:
Alt text 注册中心Eureka也是作为一个微服务且需要程序员自己开发部署,没有和微服务真正的分离解耦,仅仅做到和业务开发没有关系。现在越来越多开发需要服务注册中心作为单独一个中间件。

2. 服务注册中心之Consul

2.1 Consul介绍

Consul是一套开源的分布式服务发现和配置管理系统,用Go语言开发。提供了微服务系统中的服务治理、配置中心、控制总线等功能。它具有很多优点。包括:基于raft协议,比较简洁;支持健康检查, 同时支持HTTP和DNS协议 支持跨数据中心的WAN集群提供图形界面跨平台,支持Linux、Mac、Windows。官网地址为https://www.consul.io/

2.2 功能特性

  1. 服务发现:提供Http和DNS两种发现方式。
  2. 健康检测:支持多种方式,HTTP\TCP\Docker\Shell脚本定制化监控。
  3. KV存储:Key\Value的存储
  4. 多数据中心:Consul支持多中心
  5. 可视化Web页面
    在SpringCloud中,定位主要用来作为服务发现和配置中心以及事件总线:
    Alt text

2.3 安装运行Consul

  1. 下载Consul
    访问https://developer.hashicorp.com/consul/install,下载Consul软件。
    Alt text
  2. 解压文件后查看版本号信息
cmd
E:\middleware\consul_1.20.0_windows_amd64>consul -version
Consul v1.20.0
Revision cddc6181
Build Date 2024-10-14T20:52:01Z
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
  1. 创建consul配置持久化 在consul目录下面创建data目录:
    Alt text
  2. 启动consul
cmd
consul agent -server -ui -bind=127.0.0.1 -client=0.0.0.0 -bootstrap-expect 1 -data-dir E:\middleware\consul_1.20.0_windows_amd64\data
==> Starting Consul agent...
               Version: '1.20.0'
               .....
2024-10-26T08:36:47.296+0800 [DEBUG] agent: Skipping remote check since it is managed automatically: check=serfHealth
2024-10-26T08:36:47.296+0800 [DEBUG] agent: Node info in sync

其中参数说明:

  • -bootstrap-expect: 仅适用于以服务器模式运行,后面接数字表示在集群中期望的成员数量
  • -data-dir: 用于指定Consul节点的数据存储目录

2.4 访问Consul

访问Consul控制页面:http://localhost:8500/ ,结果如下:
Alt text

3. 微服务接入Consul

前面我们有两个工程:服务提供provider8001、服务消费consume80,现在进行相关配置即可接入。

3.1 服务提供接入Consul

  1. pom.xml中添加consul依赖:
xml
<!--SpringCloud consul discovery -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
  1. application.yml配置consul
yml
####Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
  1. 在启动类加入注解,开启服务发现
java
package com.rocket.springcloud.provider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaRepositories(basePackages = {"com.rocket.springcloud.provider.dao"})
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain {

    public static void main(String[] args) {
        SpringApplication.run(PaymentMain.class, args);
    }
}
  1. 查看Consul控制台:
    Alt text

3.2 服务消费接入Consul

  1. pom.xml中添加consul依赖:
xml
<!--SpringCloud consul discovery -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
  1. application.yml配置consul
yml
####Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
  1. 在启动类加入注解,开启服务发现
yml
@SpringBootApplication
@EnableDiscoveryClient
public class OrderMain {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain.class, args);
    }
}
  1. 查看Consul控制台:
    Alt text
  2. 修改OrderController:
java
@RestController
public class OrderController{

//    public static final String PaymentSrv_URL = "http://localhost:8001";//先写死,硬编码
    public static final String PaymentSrv_URL = "http://cloud-payment-service"; //服务注册中心上的微服务名称

    @Autowired
    private RestTemplate restTemplate;

    @PostMapping("/consumer/pay/add")
    public ResultData addOrder(@RequestBody PayDTO payDTO){
        return restTemplate.postForObject(PaymentSrv_URL + "/pay/add",payDTO,ResultData.class);
    }
    @GetMapping("/consumer/pay/get/{id}")
    public ResultData getPayInfo(@PathVariable("id") Integer id){
        return restTemplate.getForObject(PaymentSrv_URL + "/pay/get/"+id, ResultData.class, id);
    }
}
  1. 通过服务名称调用方式,会报错找不到相同服务名的主机,需要配置RestTemplate加上负载均衡
java
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

4. 不同注册中心区别

常见的注册中心有Eureka、Consul、Zookeeper,另外还有redis、nacos(后续spring-cloud-alibaba说明)

组件名语言CAP服务健康检查对外暴露接口SpringCloud集成
EurekajavaAP可配支持Http已集成
ConsulgoCP支持Http/DNS已集成
ZookeeperjavaCP支持客户端已集成

CAP理论

CAP指的是:

  • C(Consistency): 一致性
  • A(Availability): 可用性
  • P(Partition tolerance): 分区容错性

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,由于分布式系统的刚需是分区容错性,所以有两大类:

  1. CP: 满足一致性,分区容忍必的系统,通常性能不是特别高。
  2. AP: 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

5. 基于Consul的配置中心

随着系统进行拆分微服务化,由于每个服务都需要必要的配置信息才能运行,上百个配置文件的维护管理就需要一套集中式的、动态的配置管理设施。

5.1 修改pom.xml

调整cloud-provider-payment8001工程的pom.xml:

xml
<!--SpringCloud consul config-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

5.2 创建bootstrap.yml

applicaiton.yml是用户级的资源配置项;bootstrap.yml是系统级的,优先级更加高;bootstrap.yml是比application.yml先加载的。默认情况下,它们不会被本地配置覆盖。
Spring Cloud设计的有一个"Bootstrap Context",作为Spring应用的Application Context的父上下文。初始化的时候,Bootstrap Context负责从外部源加载配置属性并解析配置。新增了一个bootstrap.yml文件,保证Bootstrap ContextApplication Context配置的分离。
cloud-provider-payment8001工程中,创建bootstrap.yml文件,内容如下:

yml
spring:
  application:
    name: cloud-payment-service
  #### Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
      config:
        enabled: true
        profile-separator: '-' ## 默认是','
        format: yaml
        data-key: data  # 默认是data

5.3 在Consul添加配置

  1. 在Consul页面控制台上面,选择key/value菜单:
    Alt text
  2. 创建config目录,Consul配置文件夹需要以/结尾:
    Alt text
  3. 在config目录下,创建cloud-payment-service-dev和cloud-payment-service-prod目录:
    Alt text
  4. 前面bootstrap.yml文件配置format: yml, 按照Spring-Cloud-Consul配置约定,需要配置名叫data的key,然后在cloud-payment-service-dev中将application.yml中的内容加到data里面:
    Alt text
  5. 为了区分配置cloud-payment-service-dev和cloud-payment-service-prod, 创建prod数据库为db01,再把db01库中添加t_pay表:
    Alt text
    在cloud-payment-service-prod中将application.yml中的内容加到data里面: Alt text

5.4 简化application.yml配置

cloud-payment-service8001工程中,调整application.yml:

yml
server:
  port: 8001

spring:
  profiles:
    active: prod

5.5 验证配置功能

Alt text 查询生产库db01中的数据 Alt text

6. Consul动态配置

如果我们修改了consul中的配置信息,会发现cloud-payment-service8001工程并没有生效。如果要支持动态调整consul中配置及时生效,需要配置cloud-payment-service8001工程。

  1. 修改启动类,添加@RefreshScope注解:
java
@EnableJpaRepositories(basePackages = {"com.rocket.springcloud.provider.dao"})
@SpringBootApplication
@EnableDiscoveryClient
@RefreshScope
public class PaymentMain {

    public static void main(String[] args) {
        SpringApplication.run(PaymentMain.class, args);
    }
}
  1. bootstrap.yml文件调整, 增加监视文件变更相关配置, 每隔10s检测内容变化:
yml
spring:
  application:
    name: cloud-payment-service
  #### Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
      config:
        enabled: true
        profile-separator: '-' ## 默认是','
        format: yaml
        data-key: data  # 默认是data
        watch:
          enabled: true
          wait-time: 10 # 默认55s