Spring Cloud 服务注册与发现

一.背景

1.问题提出

我们在一个父项目下写了两个子项目,需要两个子项目之间相互调用。我们可以发送HTTP请求来获取我们想要的资源,具体实现的方法有很多,可以用HttpURLConnectionHttpClientOkhttpRestTemplate等。

举个例子,我们想要通过订单表中的商品Id来查询商品的详细信息,我们需要向商品所在的模块发送HTTP请求,通过请求获取商品的详细信息:

@Resource
RestTemplate restTemplate;

@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){
    OrderInfo orderInfo = orderMapper.selectByOrderId(orderId);
    String url="http://127.0.0.1:9090/product/"+orderInfo.getProductId();
    ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
    orderInfo.setProductInfo(productInfo);
    return orderInfo;
}

上面的实现有一个问题:如果机器发生变故,url会跟着变更。url变来变去,我们写的代码就要进行修改,因此我们要想办法解决这个问题。

2.解决思路

注册中心可以解决这个问题。

服务启动或变更的时候,向注册中心报道,注册中心就存储着应用和IP的关系。

当我们想要调用方法的时候,只需要向注册中心去获取服务方的IP就行了。

那什么是注册中心?

注册中心(Service Registry) 是分布式系统(尤其是微服务架构)中的核心组件,主要用于解决服务实例的 自动发现、动态管理和路由调度 问题。它就像一个 “电话簿”,记录着所有服务实例的网络地址(IP、端口等信息),并实时更新这些信息,确保服务之间可以快速、可靠地通信。

注册中心有三种角色:服务提供者(Server)服务消费者(Client)服务注册中心(Registry)

它们之间的关系可以通过两个概念来描述:

服务注册服务提供者在启动的时候向服务注册中心(Registry)注册自身服务,并向服务注册中心(Registry)定期发送心跳汇报存好情况。

服务发现服务消费者注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口。

3.CAP理论

Consistency,一致性:这里的一致性指的是强一致性,所有结点在同一时间具有相同的数据;

Availability,可用性:保证每个请求都有响应;

Partition Tolerance,分区容错性:分布式系统中的节点之间出现网络分区(即部分节点无法通信)时,系统仍能继续运行。

这个CAP是一个“不可能三角”,即这三个要求不能同时实现,要想实现CA就不能实现P,要实现AP就不能实现C。

在微服务系统中,P这一点是必须保证的,所有A和C只能实现一个。

二.Eureka

Eureka(尤里卡)曾是SpringCloud官方默认的注册中心,其符合CAP理论中的AP,即保证分布式系统的可用性。

1.搭建服务中心

1)创建项目;

2)pom加入Eureka的依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

3)配置文件,加入相关配置:

server:
  port: 10010
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: localhost
  client:
    fetch-registry: false # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为false
    register-with-eureka: false # 表示是否将自己注册到Eureka Server,默认为true.由于当前应用就是Eureka Server,故而设置为false.
    service-url:
      # 设置Eureka Server的地址,查询服务和注册服务都需要依赖这个地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
logging:
  pattern:
    console: '%d{MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n'

4)启动类,开启Eureka的功能:

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

此时我们启动项目,在浏览器上输入:127.0.0.1:10010,如果出现以下页面说明成功了:

2.服务注册

下面的配置都是加在服务提供方(Server)的。

1)加入Eureka依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2)修改配置信息:

配置服务的名称和eureka地址

spring:
 application:
     name: product-service
eureka:
 client:
     service-url:
         defaultZone: http://127.0.0.1:10010/eureka

3)启动程序

启动程序后出现下面的界面说明注册成功了:

注意红线圈出的部分,这个Application的名字与刚刚配置的名字是一样的:product-service。

3.服务发现

下面的配置都是加在服务消费方(Client)的。

1)加入Eureka依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2)修改配置信息:

spring:
 application:
     name: order-service
eureka:
 client:
     service-url:
         defaultZone: http://127.0.0.1:10010/eureka

3)修改远程调用的代码

修改本篇文章一开始提出的问题代码:

@Resource
RestTemplate restTemplate;

@Resource
DiscoveryClient discoveryClient;

@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){
    OrderInfo orderInfo = orderMapper.selectByOrderId(orderId);
    //从eureka中获取服务列表
    List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
    //写法一:
    EurekaServiceInstance instance=(EurekaServiceInstance)instances.get(0);
    String url=instance.getUri()+"/product/"+orderInfo.getProductId();
    //写法二:
    //String uri = instances.get(0).getUri().toString();
    //String url=uri+"/product/"+orderInfo.getProductId();
    ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
    orderInfo.setProductInfo(productInfo);
    return orderInfo;
}

两种写法都可以。 

4)启动程序

如果出现下面界面就说明成功了:

三.Consul

Consul 是HashiCorp开发的⼀款开源的组件,主要用于服务发现、配置管理和分布式系统的健康检查。官网:Spring Cloud Consul :: Spring Cloud Consul

它的主要功能包括:

服务注册/发现服务在启动时向Consul注册, 并在需要时,向Consul查询其他服务的地址和状态
健康检查Consul提供了健康检查机制,确保只有健康的服务实例对外提供服务,防止服务转发到故障的服务
配置管理Consul提供了一个Key/Value存储系统,用于存储配置数据和其他需要共享的信息
服务分段Consul支持服务分段,允许你创建隔离的环境,例如开发,测试和生产环境

1.下载与启动

下载地址:Install | Consul | HashiCorp Developer,选择对应的系统下载。

我在这里在Windows环境下展示,下载完后进行解压缩,然后到解压缩的目录,在目录下输入cmd:

Consul常见启动命令

consul agent [options]

选项:

-dev开发模式,关闭所有持久化选项,用于快速开启consul代理
-server以服务器模式启动 Consul 代理
-client以客户端模式启动 Consul 代理
-ui启用 Consul 的 Web 用户界面
-bootstrap-expect设置期望加入的服务器节点数,用于初始化集群
-data-dir指定数据存储目录

如果输出下面内容说明成功:

下载成功就可以启动了:

.\consul.exe agent -dev

开发模式( -dev )主要用于快速启动单节点 Consul 环境,适用于开发和测试,不适用于生产环境,因为它不会持久化任何状态。而服务器模式(-server)和客户端模式(-client)则更多地用于生产环境,以构建一个完整的 Consul 集群。服务器模式是 Consul 集群的核心,负责处理集群的管理和数据存储。客户端模式更轻量级,主要用于服务发现和健康检查,不参与集群的管理和数据存储。

访问Consul:127.0.0.1:8500:

2.服务注册与发现

1)添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2)添加配置

spring:
    application:
        name: product-service
    cloud:
        consul:
            host: localhost
            port: 8500
            discovery:
                service-name: product-service

3)在启动类中添加 @EnableDiscoveryClient 注解

3.服务配置和刷新

Consul 除了服务注册和发现之外,还可以作为配置中心,服务的配置信息存储在 Consul 中,服务从 Consul 获取配置信息。

Consul 提供了一个键值存储(KV Store)用于存储服务配置和其他元数据,是Spring Cloud Config的替代方案,在特殊的"bootstrap"阶段,配置被加载到 Spring 环境中。默认情况下,配置存储在 /config 文件夹中。根据优雅程序的名称和激活的配置文件,会创建多个 PropertySource 实例。当前配置在应用程序启动时读取,向/refresh发送HTTP POST请求将导致配置重新加载。Config Watch也会自动检测变化并重新加载应用程序上下文。

初始化配置

1)创建目录

2)创建应用目录

在config目录下再创建一个目录product-service

3)添加配置信息

在product-service目录下创建文件data

项目配置

Spring Cloud 会创建一个 Bootstrap Context , 作为 Spring 应用"Application Context" 的父上下文。在Spring应用启动的初始化阶段, Bootstrap Context 负责从外部源(如Consul)加载配置属性并解析配置。所以除了添加 spring-cloud-starter-consul-config 依赖之外,还需要加入依赖 spring-cloud-starter-bootstrap 来实现:

<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>

配置文件

在 Spring 启动的 bootstrap 阶段会通过 Consul 去获取 key = config/product-service/data 对应的value,对应value格式为 yaml

spring:
    application:
        name: product-service
    cloud:
        consul:
            host: 127.0.0.1
            port: 8500
            config:
                format: YAML

补充:

Bootstrap属性具有高优先级,也就是说在 bootstrap.yml 或 bootstrap.properties 中定义的配置会优先于 application.yml 或 application.properties 中的配置。

bootstrap.yml 主要用于配置应用启动时所需的外部依赖和环境,而 application.yml 用于业务逻辑相关的配置(如数据库连接等)。

动态刷新

Consul支持动态刷新配置,在配置类上添加 @RefreshScope 注解,可以标记需要动态刷新的Bean。当接收到刷新事件时,这些Bean会重新加载最新的配置。

Consul会对配置更新进行检查,跟服务器上面的配置文件的版本进行比较,如果版本不一致,则调用Spring的刷新事件,触发事件刷新,否则代表配置没有变化。

默认的检查频率为 1000 单位毫秒,可以通过 spring.cloud.consul.config.watch.delay配置。

4.配置持久化

适用下面的命令来启动会对数据进行持久化:

consul agent -server -bootstrap-expect 1 -ui -data-dir=存储路径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值