目录
服务治理
服务注册与服务发现
Euraka 的使用
单机版(单节点模式)
集群版(高可用模式)
其他配置
Spring Cloud封装了Netflix 公司开发的Eureka模块来实现服务治理在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
Eureka包含两个组件: Eureka Server和Eureka Client
Eureka Server提供服务注册服务各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问,是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
1、创建新的 module 名称为 cloud-eureka-server7001
2、修改 POM 文件,引入依赖如下
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
com.yixuan.springcloud
cloud-api-commons
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
3、写 YML 文件: resource/application.yml
server:
port: 7001 # 表示端口号
eureka:
instance:
hostname: localhost #eureka服务端的实例名字
client:
# false 表示不向注册中心注册自己
register-with-eureka: false
# false 表示不需要去检索服务, 由于当前的 整个 module 就是服务注册中心,无需检索服务
fetch-registry: false
service-url:
#设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4、主启动类:com.atyixuan.springcloud.EurekaMain7001
@EnableEurekaServer 注解启动一个服务注册中心提供给其他应用进行对话
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
}
}
5、测试
浏览器中输入链接:http://localhost:7001
现在注册中心已经创建好,以前cloud-consumer-order80 是直接通过 RestTemplate 调用 cloud-provider-payment8001 ;现在我们将 cloud-provider-payment8001 注册到辅助注册中心,让 cloud-consumer-order80 在注册中心找到 cloud-provider-payment8001 的信息,然后调用 cloud-provider-payment8001 中的功能模块完成支付任务。
a、对于cloud-provider-payment8001 服务提供者而言,要讲自己的信息注册到 服务注册中心上
1、在其 POM 文件中添加 Eureka Client 的依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2、在其 application.yml 配置文件中添加相关配置信息
eureka:
client:
# 向注册中心注册自己
register-with-eureka: true
# 检索服务
fetchRegistry: true
# 服务注册中心的地址
service-url:
defaultZone: http://localhost:7001/eureka
到目前为止,该配置文件的内容为:
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql 驱动包
url: jdbc:mysql://服务器IP地址:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 数据库密码
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atyixuan.springcloud.entities # 所有的 entity 别名类所在包
eureka:
client:
# 表示是否将自己注册到 EurekaServer 默认为 true
register-with-eureka: true
# 是否从 EurekaServer 抓取已有的注册信息,默认为 true。单节点无所谓,集群必须为 true
# 才能配置 Ribbon 使用负载均衡
fetch-registry: true
# 入驻地址
service-url:
defaultZone: http://localhost:7001/eureka
3、在其主启动类上添加注解:@EnableEurekaClient
效果如下所示:
@EnableEurekaClient
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
4、测试,先启动服务注中心 cloud-eureka-server7001,然后再启动 cloud-provider-payment8001,在浏览器中输入 http://localhost:7001/
得到结果图
在上图中可以看到 Instances currently registered with Eureka 一栏下方已经有 注册进的服务
我们采用同样的方式,将 cloud-consumer-order80 注册进 Eureka Server 中。
1、在 POM 文件中添加 依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2、添加 YML 文件中的配置信息
server:
port: 80
spring:
application:
name: cloud-order-service
# 添加 eureka 配置信息
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
3、在其主启动类上添加注解:@EnableEurekaClient
效果如下所示:
@EnableEurekaClient
@SpringBootApplication
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
4、测试:先启动 cloud-eureka-server7001 再启动 cloud-provider-payment8001 和 cloud-consumer-order80 在浏览器中输入如下内容进行测试
http://localhost/consumer/payment/get/31
http://localhost/consumer/payment/create?serial=MySQL
数据库中的插入结果如下:
注意YML 文件中的两个空格不能省略
在微服务架构这样的分布式环境中,我们需要充分考虑故障的情况,所以在充分考虑发生故障的情况,所以在生产环境中必须对各个组件进行高可用部署,对微服务如此,对服务注册中心也一样,我们需要构建高可用的服务注册中心以增强系统的可用性。
Eureka Server 的设计一开始就考虑了高可用问题,在Eureka 的服务治理设计中,所有的节点既是服务提供方,也是服务消费方,服务注册中心也不例外。只是服务注册中心的如下配置不让服务注册中心注册自己而已:
eureka:
client:
# 表示是否将自己注册到 EurekaServer 默认为 true
register-with-eureka: false
# 是否从 EurekaServer 抓取已有的注册信息,默认为 true。单节点无所谓,集群必须为 true
# 才能配置 Ribbon 使用负载均衡
fetch-registry: false
准备:修改 hosts 文件
由于只有一台机器,我们将其当成两台机器使用
hosts 文件路径: C:\Windows\System32\drivers\etc\hosts
将如下内容添加到 hosts 文件结尾并保存
######### Spring Cloud Configuration ##########
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
创建另外一个module , cloud-eureka-server7002
1、创建一个普通的 Maven 文件
2、POM 文件中引入依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
com.yixuan.springcloud
cloud-api-commons
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
3、修改 application.yml 配置文件
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com
client:
fetch-registry: false
register-with-eureka: false
service-url:
# 注册到 cloud-eureka-server7001 中去
defaultZone: http://eureka7001.com:7001/eureka/
4、创建主启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7002.class, args);
}
}
修改 cloud-eureka-server7001 的 application.yml 文件中的配置
由于要相互注册:
yml 文件要修改为:
server:
port: 7001
eureka:
instance:
# eureka 服务端的实例名称
hostname: eureka7001.com
client:
# false 表示不向注册中心注册自己。
register-with-eureka: false
# false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
# 设置 eureka server 交互的地址查询服务和注册服务都需要依赖这个地址
service-url:
# defaultzone: "http://${eureka.instance.hostname}:${server.port}/eureka/"
defaultzone: "http://euraka7002:7002/eureka/"
上述在 hosts 文件中的配置
######### Spring Cloud Configuration ##########
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
cloud-eureka-server7001 采用 eureka7001.com 作为 hostname
cloud-eureka-server7002 采用 eureka7002.com 作为 hostname
修改 cloud-provider-payment8001 和 cloud-consumer-order80 向 eureka 注册中心的注册链接
由于现在有两个注册中心,需向两个中心同时注册中心注册,如果其中一个注册中心出现故障,另外一个注册中心仍然可以使用,从而实现了服务注册中心的高可用。
在cloud-provider-payment8001 和 cloud-consumer-order80 的 yml 中修改内容如下:
cloud-provider-payment8001
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql 驱动包
url: jdbc:mysql://服务器ip 地址:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 数据库密码
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atyixuan.springcloud.entities # 所有的 entity 别名类所在包
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
######################修改内容如下###################
#######defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
cloud-consumer-order80 yml 文件修改如下
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
instance:
instance-id: payment8001
client:
register-with-eureka: true
fetch-registry: true
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
测试:
先启动 cloud-eureka-server7001 和 cloud-eureka-server7002两个注册中心
再启动服务提供者:cloud-provider-payment8001
再启动服务消费者:cloud-consumer-order80
浏览器输入链接:
http://eureka7001.com:7001/
http://eureka7002.com:7002/
http://localhost/consumer/payment/get/31
http://localhost/consumer/payment/create?serial=Linux
数据库中插入结果
支付服务提供者cloud-provider-payment8001 集群环境构建, 创建另外一个cloud-provider-payment8002
创建一个新的模块 cloud-provider-payment8002,可以完全仿照 cloud-provider-payment8001 的创建流程创建,此处不再赘述。
1、修改 order80 获取服务的链接地址
修改 cloud-consumer-order80 的 OrderController, 此时有两个订单支付服务的cloud-provider-payment8001 和 cloud-provider-payment8002,我们将服务服务查询的地址直接写成 http://CLOUD-PAYMENT-SERVICE,(来源于 Eureka7001.com:7001 网页上注册的服务名称),修改内容如下所示:
// 单机版是如下
// public static final String PAYMENT_URL = "http://localhost:8001";
// 使用微服务版
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
2、客户端 RestTemplate 负载均衡
在 cloud-consumer-order80 模块中的 config 包下的 ApplicationContextConfig类中的 RestTemplate 实例上方添加 @LoadBalanced,以此来实现负载均衡,
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced // 启动负载均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
当通过链接访问的时候,支付服务会在 cloud-provider-payment8002 和 cloud-provider-payment8001 之间来回切换,如下图中 serverPort = 8001 和 8002 所示,由于 Eureka 中已经集成了负载均衡模块 Ribbon,从而实现了两个服务之间的切换。
3、显示 IP 地址和服务名称
在 cloud-provider-payment8002 和 cloud-provider-payment8001 的配置文件中分别添加中添加
两种配置,添加了如下配置之后,当鼠标浮动在 http://eureka7001.com:7001/ 或者 http://eureka7002.com:7002/ 中的服务命名之上的时候,浏览器左下角会显式 ip地址
对于 cloud-provider-payment8001 配置文件,添加:
instance:
instance-id: payment8001 # 在 eureka 内直接显示主机名称
prefer-ip-address: true # 访问路径显示 ip 地址
eureka:
client:
# 表示是否将自己注册到 EurekaServer 默认为 true
register-with-eureka: true
# 是否从 EurekaServer 抓取已有的注册信息,默认为 true。单节点无所谓,集群必须为 true
# 才能配置 Ribbon 使用负载均衡
fetch-registry: true
# 入驻地址
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001 # 在 eureka 内直接显示主机名称
prefer-ip-address: true # 访问路径显示 ip 地址
对于 cloud-provider-payment8002 配置文件,添加:
instance:
instance-id: payment8002 # 在 eureka 内直接显示主机名称
prefer-ip-address: true # 访问路径显示 ip 地址
eureka:
client:
# 表示是否将自己注册到 EurekaServer 默认为 true
register-with-eureka: true
# 是否从 EurekaServer 抓取已有的注册信息,默认为 true。单节点无所谓,集群必须为 true
# 才能配置 Ribbon 使用负载均衡
fetch-registry: true
# 入驻地址
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
instance:
instance-id: payment8002
prefer-ip-address: true # 访问路径显示 ip 地址
结果图:
4、服务发现:对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息,对于服务提供者 cloud-provider-payment8001 和 cloud-provider-payment8002 而言,可以专门提供一个访问入口,访问其相关信息,我们在其 com.atyixuan.springcloud.controller.PaymentController 类中添加如下内容,使得 cloud-consumer-order80 可以获取到服务提供者的一些信息,
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery(){
List services = discoveryClient.getServices();
for (String service: services){
log.info("************element: " + service);
}
// http://CLOUD-PAYMENT-SERVICE 是对外暴露的微服务名称
List instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for(ServiceInstance instance: instances){
// 服务 id + 服务主机名称 + 服务端口 + 服务的 uri 地址
log.info("" + instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
}
return this.discoveryClient;
}
在浏览器中输入 http://localhost:8001/payment/discovery,
得到 Discovery 的内容
同时在控制台的输出内容如下:我们获取到了:服务 id + 服务主机名称 + 服务端口 + 服务的 uri 地址
5、自我保护机制
如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
保护机制主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
该警告就是触发了 Eureka Server 的自我保护机制,服务在注册到 Eureka Server 之后,会维持一个心跳连接,告诉Eureka Server 自己还活着。Eureka Server 在运行期间,会统计心跳失败的比例在 15 分钟之内是否低于 85%,如果出现低于的情况,Eureka Server 会将当前的实例注册信息保存起来,让这些实例不会过期,尽可能的保护这些注册信息。但是如果在保护期间内,实例出现问题,客户端很容易拿到实际已经不存在的服务实例,会出现调用失败的情况,所以客户端必须要有容错机制,比如使用请求重试,断路器等机制,后面 Hystrix 会涉及到这部分内容。
设置 yml 取消自我保护机制
在 cloud-provider-payment8001 和 cloud-provider-payment8002 的 eureka 配置下设置心跳的间隔时间和 Eureka-server 收到最后一次心跳等待的时间
对于 cloud-provider-payment8001
eureka:
client:
# 表示是否将自己注册到 EurekaServer 默认为 true
register-with-eureka: true
# 是否从 EurekaServer 抓取已有的注册信息,默认为 true。单节点无所谓,集群必须为 true
# 才能配置 Ribbon 使用负载均衡
fetch-registry: true
# 入驻地址
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001 # 在 eureka 内直接显示主机名称
prefer-ip-address: true # 访问路径显示 ip 地址
# Eureka 客户端向服务器发送心跳的时间间隔,单位为秒(默认是 30秒)
lease-renewal-interval-in-seconds: 1
# Eureka 服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是 90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
对于 cloud-provider-payment8002
eureka:
client:
# 表示是否将自己注册到 EurekaServer 默认为 true
register-with-eureka: true
# 是否从 EurekaServer 抓取已有的注册信息,默认为 true。单节点无所谓,集群必须为 true
# 才能配置 Ribbon 使用负载均衡
fetch-registry: true
# 入驻地址
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
instance:
instance-id: payment8002
prefer-ip-address: true # 访问路径显示 ip 地址
# Eureka 客户端向服务器发送心跳的时间间隔,单位为秒(默认是 30秒)
lease-renewal-interval-in-seconds: 1
# Eureka 服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是 90
lease-expiration-duration-in-seconds: 2
上述是在注册服务的方修改配置信息