解决硬编码提供服务地址的问题,需要一个强大的服务发现机制。服务发现组件正是微服务架构中十分关键的一个组件。
服务提供者、消费者、服务发现组件三者的关系大致如下:
综上,服务发现组件应该具备如下功能:
Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务。包含server和client两部分。Spring Cloud将它集成在子项目Spring Cloud Netflix中,实现微服务的注册与发现。
两个组件
创建Spring Boot项目,artifactId为 microservice-discovery-eureka,添加如下依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
<version>2.2.5.RELEASEversion>
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,得到如下结果
在microservice-simple-provider-user 项目的pom.xml下添加如下依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>2.2.5.RELEASEversion>
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上了,同样改造电影微服务,改造完毕后,启动所有微服务,访问注册中心,可以看到如下界面
可以看到,用户微服务、电影微服务都被注册到了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节点。如下图
只需要修改配置文件中的 Eureka Server地址(配置多个),就可以注册到集群上了。其实只配置Eureka Server集群中的某个节点,也能正常注册到集群上, 因为多个server之间会数据同步
示例如下:
eureka:
client:
service-url:
defaultZone: http://peer1:8761/eureka/, http://peer2:8762/eureka/
1.复制microservice-discovery-eureka,修改artifactId为microservice-discovery-eureka-authenticating,这里直接修改原项目
2.pom.xml中添加spring-boot-starter-security依赖,提供用户认证能力
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
<version>2.3.4.RELEASEversion>
dependency>
3.添加配置文件内容如下
security:
basic:
enable: true
user:
name: user
password: user123456
登陆账号为user,密码为user123456,再次访问eureka主页,就会要求进行登录,初始密码会在控制台打印出来
修改一下 eureka客户端配置的注册中心交互地址即可,改为
eureka:
client:
service-url:
defaultZone: http://user:user123456@localhost:8761/eureka/
Eureka元数据有两种,标准元数据和自定义元数据
标准元数据
自定义元数据
关于元数据的示例
改造用户微服务,修改配置文件
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
}
}
]
提供一些REST端点,供非JVM微服务操作Eureka。一般来说很少用原始的http请求来操作,很多语言提供一些工具包来封装了这些API。此节不展开介绍
默认情况,Eureka Server一定时间内没有收到某个微服务实例心跳,会注销该实例,但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,微服务是健康的,却被注销掉了
通过自我保护模式来解决这个问题,当Eureka Server节点短时间内丢失过多客户端时,这个节点会进入自我保护模式。之后Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据。故障恢复后,Eureka Server节点会自动退出自我保护模式。
可以使用 eureka.server.enable-self-preservation = false 禁用自我保护
如果某个服务器有 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首页,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
副作用: