欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Cloud与Docker微服务架构实战(三)—— 服务注册与发现

程序员文章站 2022-06-12 22:54:33
...

四、微服务注册与发现

解决硬编码提供服务地址的问题,需要一个强大的服务发现机制。服务发现组件正是微服务架构中十分关键的一个组件。

服务提供者、消费者、服务发现组件三者的关系大致如下:

  • 各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息。
  • 服务消费者可从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务提供者的接口
  • 各个微服务与服务发现组件使用一定机制(心跳)通信。服务发现组件如长时间无法与某微服务实例通信,就会注销该实例
  • 微服务网络地址发生变更(实例增减或IP端口变化),会重新注册到服务发现组件,不用人工修改

Spring Cloud与Docker微服务架构实战(三)—— 服务注册与发现

综上,服务发现组件应该具备如下功能:

  • 服务注册表:记录各个微服务信息。提供查询API和管理API,查询API用于查询可用微服务实例,管理API用于服务的注册和注销
  • 服务注册与服务发现:服务注册是指微服务在启动时,将自己的信息注册到服务发现组件上的过程。服务发现是指查询可用微服务列表及其网络地址的机制
  • 服务检查:服务发现组件使用一定机制定时检测已注册的服务,如果发现某实例长时间无法访问,就会从服务注册表中移除该实例

1.Eureka简介

Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务。包含server和client两部分。Spring Cloud将它集成在子项目Spring Cloud Netflix中,实现微服务的注册与发现。

2.Eureka原理

Spring Cloud与Docker微服务架构实战(三)—— 服务注册与发现

  • Application Service 服务提供者
  • Application Client 服务消费者
  • Make Remote Call 调用RESTful API行为

两个组件

  • Eureka Server 提供服务发现能力,各个微服务启动时,会向Eureka Server注册自己的信息,Eureka Server 进行存储
  • Eureka Client 是一个Java客户端,简化与 Eureka的交互
  • 微服务启动后,会周期性(默认30秒)地向Eureka Server发送心跳续约自己的“租期”
  • 如果Eureka Server 在一定时间内没有接收到某个微服务实例的心跳,Eureka Server会注销该实例(默认90秒)
  • 默认情况下,Eureka Server 同时也是 Eureka Client,多个Eureka Server实例,互相之间通过复制,来实现服务注册表中数据的同步
  • Eureka Client 会缓存服务注册表中的信息。优势:降低server的压力,加快查询服务的速度;及时server的所有节点都宕机,服务消费者依然可以使用缓存中信息找到服务提供者

3.编写 Eureka Server

创建Spring Boot项目,artifactId为 microservice-discovery-eureka,添加如下依赖:

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

编写启动类,在启动类上加上@EnableEurekaServer注解,声明这是一个Eureka Server

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

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

}

配置文件如下:

server:
  port: 8761

eureka:
  client:
#    默认交互地址
    service-url:
      defaultZone: http://localhost:8761/eureka/
#      不要注册自己,因为自己就是server
    register-with-eureka: false
#    不要同步注册服务列表,因为只有单节点
    fetch-registry: false

启动项目,访问 http://localhost:8761,得到如下结果

Spring Cloud与Docker微服务架构实战(三)—— 服务注册与发现

4.将微服务注册到Eureka Server上

在microservice-simple-provider-user 项目的pom.xml下添加如下依赖

    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        <version>2.2.5.RELEASE</version>
    </dependency>

配置文件如下

server:
  port: 8080

spring:
  datasource:
    #通用数据源配置
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?charset=utf8mb4&useSSL=false
    username: root
    password: 123456
    # Hikari 数据源专用配置 Hikari 是Springboot用的连接池
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
    #初始化数据


  # JPA 相关配置
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      # 慎用!
#      ddl-auto: create
#  注册到服务发现中心的名称
  application:
    name: microservice-simple-provider-user

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
#   将自己的IP注册到服务发现中心,如果为false,则注册所在操作系统的hostname
  instance:
    prefer-ip-address: true

logging:
  level:
    root: INFO
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.Extractor: TRACE

management:
  endpoints:
    web:
      base-path: /actuator		#如果修改,则监管点访问路径前缀也会改变
      exposure:
        include: "*"  #星号代表暴露全部,可以选择暴露部分,填入想暴露的即可

info:
  app:
    name:microservice-simple-provider-user


编写启动类,启动类上加上 @EnableDiscoveryClient 注解,声明这是一个Eureka Client

@EnableDiscoveryClient
@SpringBootApplication
public class MicroserviceSimpleProviderUserApplication {

    public static void main(String[] args) {
        SpringApplication.run(MicroserviceSimpleProviderUserApplication.class, args);
    }

}

@EnableDiscoveryClient 为各种服务组件提供支持,该注解是 Spring-cloud-commons项目的注解,高度抽象;

@EnableEurekaClient表明是Eureka的Client,只能与Eureka一起工作

完成上述步骤后,就可以将用户微服务注册到Eureka Server上了,同样改造电影微服务,改造完毕后,启动所有微服务,访问注册中心,可以看到如下界面

Spring Cloud与Docker微服务架构实战(三)—— 服务注册与发现

可以看到,用户微服务、电影微服务都被注册到了Eureka Server上了

5. Eureka Server的高可用

微服务十分关注高可用,作为微服务之一的注册中心也不例外。之前设置的不让注册中心注册自己,现在需要让注册中心把自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,实现服务清单的互相同步,达到高可用的目的。

在前面项目基础上,进行修改,构建一个双节点的服务注册中心集群

1.复制项目,将项目的artifactId改为microservice-discovery-eureka-ha,里面的名称相应进行修改

2.配置系统的hosts,macOS 文件路径为 /etc/hosts,添加以下内容

127.0.0.1 peer1 peer2

3.修改application.yaml ,让两个节点的Eureka互相注册

spring:
  application:
    name: microservice-discovery-eureka-ha
---
spring:
#  指定profiles 为 peer1
  profiles: peer1

server:
  port: 8761

eureka:
  instance:
#    指定 profile 为 peer1 时,hostname 为peer1
    hostname: peer1
  client:
#    注册到 peer2上去
    service-url:
      defaultZone: http://peer2:8762/eureka/

---
spring:
  #  指定profiles 为 peer2
  profiles: peer2

server:
  port: 8762

eureka:
  instance:
    #    指定 profile 为 peer2 时,hostname 为peer2
    hostname: peer2
  client:
    #    注册到 peer1上去
    service-url:
      defaultZone: http://peer1:8761/eureka/

---将配置文件分为三段

二三段为spring.properties 指定了一个值,表示它所在的那段内容应用在哪个profile里

以不同的profile启动时,会有不同的配置

  • 打包项目,使用以下两个项目启动两个Eureka Server节点,使用 mvn package打包

  • java -jar microservice-discovery-eureka-ha-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1

    java -jar microservice-discovery-eureka-ha-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2

  • 访问peer1:8761,发现registered-replicas 有peer2节点,同理访问peer2:8762,发现有peer1节点。如下图

Spring Cloud与Docker微服务架构实战(三)—— 服务注册与发现

将应用注册到Eureka集群上

只需要修改配置文件中的 Eureka Server地址(配置多个),就可以注册到集群上了。其实只配置Eureka Server集群中的某个节点,也能正常注册到集群上, 因为多个server之间会数据同步

示例如下:

eureka:
  client:
    service-url:
      defaultZone: http://peer1:8761/eureka/, http://peer2:8762/eureka/

Spring Cloud与Docker微服务架构实战(三)—— 服务注册与发现

为Eureka Server添加用户认证

1.复制microservice-discovery-eureka,修改artifactId为microservice-discovery-eureka-authenticating,这里直接修改原项目

2.pom.xml中添加spring-boot-starter-security依赖,提供用户认证能力

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.3.4.RELEASE</version>
</dependency>

3.添加配置文件内容如下

security:
  basic:
    enable: true
  user:
    name: user
    password: user123456

登陆账号为user,密码为user123456,再次访问eureka主页,就会要求进行登录,初始密码会在控制台打印出来

将微服务注册到需认证的Eureka Server

修改一下 eureka客户端配置的注册中心交互地址即可,改为

eureka:
  client:
    service-url:
      defaultZone: http://user:aaa@qq.com:8761/eureka/

Eureka的元数据

Eureka元数据有两种,标准元数据和自定义元数据

标准元数据

  • 主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用

自定义元数据

  • 可以使用 eureka.instance.metadata-map配置,这些元数据可以在远程客户端中访问,一般不会改变客户端的行为,除非客户端知道该元数据的含义

关于元数据的示例

改造用户微服务,修改配置文件

  instance:
    prefer-ip-address: true
    metadata-map:
      my-metadata: 我自定义的元数据

改造电影微服务,修改controller,添加以下方法

/*
 * 查询 microservice-simple-provider-user的实例信息
 * @return
 */
@GetMapping("/user-instance")
public List<ServiceInstance> showInfo(){
    return this.discoveryClient.getInstances("microservice-simple-provider-user");
}

启动eureka 注册中心,用户微服务和电影微服务,注意这里注册中心的地址不要弄混了

访问localhost:8010/user-instance,返回如下内容

[
{
"metadata": {
"management.port": "8080",
"my-metadata": "我自定义的元数据"
},
"serviceId": "MICROSERVICE-SIMPLE-PROVIDER-USER",
"uri": "http://172.19.173.181:8080",
"secure": false,
"scheme": "http",
"host": "172.19.173.181",
"port": 8080,
"instanceId": "172.19.173.181:microservice-simple-provider-user:8080",
"instanceInfo": {
"instanceId": "172.19.173.181:microservice-simple-provider-user:8080",
"app": "MICROSERVICE-SIMPLE-PROVIDER-USER",
"appGroupName": null,
"ipAddr": "172.19.173.181",
"sid": "na",
"homePageUrl": "http://172.19.173.181:8080/",
"statusPageUrl": "http://172.19.173.181:8080/actuator/info",
"healthCheckUrl": "http://172.19.173.181:8080/actuator/health",
"secureHealthCheckUrl": null,
"vipAddress": "microservice-simple-provider-user",
"secureVipAddress": "microservice-simple-provider-user",
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"hostName": "172.19.173.181",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1600829307368,
"lastRenewalTimestamp": 1600829456688,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1600829306779
},
"isCoordinatingDiscoveryServer": false,
"metadata": {
"management.port": "8080",
"my-metadata": "我自定义的元数据"
},
"lastUpdatedTimestamp": 1600829307368,
"lastDirtyTimestamp": 1600829306684,
"actionType": "ADDED",
"asgName": null
}
}
]

Eureka Server的REST端点

提供一些REST端点,供非JVM微服务操作Eureka。一般来说很少用原始的http请求来操作,很多语言提供一些工具包来封装了这些API。此节不展开介绍

Eureka 的自我保护模式

默认情况,Eureka Server一定时间内没有收到某个微服务实例心跳,会注销该实例,但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,微服务是健康的,却被注销掉了

通过自我保护模式来解决这个问题,当Eureka Server节点短时间内丢失过多客户端时,这个节点会进入自我保护模式。之后Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据。故障恢复后,Eureka Server节点会自动退出自我保护模式。

可以使用 eureka.server.enable-self-preservation = false 禁用自我保护

多网卡环境下的IP选择

如果某个服务器有 eth0、eth1和eth2三块网卡,但只有eth1可以被其他服务器访问,如果将eth0和eth2注册到Eureka Server上,其他微服务无法通过此IP调用该微服务接口

Spring Cloud 提供了按需选择IP的能力,避免以上问题

1.忽略指定名称网卡

spring:
	cloud:
		inetutils:
			ignored-interfaces:
				- docker0
				- veth.*
eureka:
	instance:
		prefer-ip-address: true

这样就可以忽略docker0网卡以及所有以veth开头的网卡

2.正则表达式,指定使用的网络地址

spring:
	cloud:
		inetutils:
			preferredNetworks:
				- 192.168
				- 10.0
eureka:
	instance:
		prefer-ip-address: true

3.只使用站点本地地址

spring:
	cloud:
		inetutils:
			useOnlySiteLocalInterfaces: true
eureka:
	instance:
		prefer-ip-address: true

4.手动指定IP地址

eureka:
	instance:
		prefer-ip-address: true
		ip-address: 127.0.0.1

Eureka 健康检查

一般在Eureka首页,Status栏中显示UP,则该应用程序状态正常。还有其他取值,如DOWN、OUT_OF_SERVICE、UNKNOWN等。只有UP的微服务才会被请求,默认情况下,服务端与客户端心跳保持正常,应用程序就会始终保持“UP”状态。

但以上机制不能完全反映应用程序状态。微服务与Eureka Server之间心跳正常,Eureka Server认为该微服务“UP”,实际上该微服务的数据源有问题(网络抖动连不上),根本无法正常工作

前面提到过,使用 Spring Boot Actuator可以展示应用程序健康信息,如何才能将该端点的健康状态传播到 Eureka Server呢?

为Eureka开启健康检查,配置如下,在微服务配置文件中设置

eureka:
	client:
		healthcheck:
			enabled: true

副作用:

  • 如果在 bootstrap.yml配置,可能会使注册状态为UNKNOWN
  • /pause 端点无法正常工作(BUG)