目录
一、服务注册与发现
服务的注册与发现对于微服务来说是一个非常重要的环节。在微服务架构中,每个服务(Service)是动态变化的,难以通过静态配置的方式维护服务,需要用到服务发现框架来完成服务(Service)的自动化管理。
Eureka是Netflix开发的服务注册和服务发现组件,Spring Cloud将Eureka集成到子项目Spring-Cloud-Netflix中实现服务注册中心的功能。
(1)客户的服务请求会通过Spring Cloud路由转发给真正的服务提供者。
(2)为了找到真正的服务提供者(即发现服务),需要服务提供者先注册服务。
(3)为了实现微服务架构中的服务注册和发现,通常需要构建一个独立媒介来管理服务,这个媒介就是注册中心。
(4)服务注册中心是服务提供者和服务消费者进行交互的媒介,起着服务注册和发现的作用。注册服务的目的是为了暴露服务访问的接口,此时的注册中心要能存储和更新服务的地址信息。服务发现的目的是为了调用服务;如果服务调用超时或者失败,将需要采用集群容错机制。
(5)为了确保运行时的稳定性和可用性,还需要进行服务的监控。
(6)Spring Cloud服务发现解决方案
- Spring Cloud Eureka(黄是黄了意思,基本不用)
- Spring Cloud Consul
- Spring Cloud Zookeeper
- Spring Cloud Alibaba Nacos(红是红的意思,很流行)
二、Eureka工作原理
在Eureka架构中,微服务组件有两种:
(1)EurekaServer:服务端(服务注册表)
记录服务信息
进行服务的心跳监控(注册、续约、下线)
(2)EurekaClient:客户端
服务提供者:注册服务,提供服务
服务消费者:也可以注册服务,调用服务
Eureka Server服务注册中心
Eureka Server,也称为服务注册中心。各个服务启动后,会在Eureka Server中进行注册,这样Eureka Server的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client存在两种角色:提供者与消费者
一个服务是相对的,一个服务可以是提供者也可以说消费者
比如说,服务A调用服务B,服务B调用服务C,这个时候服务B就属于既是消费者也是提供者
Eureka的作用
服务消费者是如何调用服务提供者的?
服务提供者和服务消费者都属于Eureka Client,它们都会将自己的信息通过REST API形式提交给Eureka Server。
服务消费者注册后,还会获取一份服务注册列表,该列表包含了所有向Eureka Server注册的服务信息。
获取到服务注册信息后,服务消费者就会根据服务提供者的IP地址,通过HTTP远程调用服务提供者的服务。
服务调用出现的问题
服务消费者该如何获取服务提供者的地址信息?
- 服务提供者启动时向eureka注册自己的信息
- eureka保存这些信息
- 消费者根据服务名称向eureka拉取提供者信息
如果有多个服务提供者,消费者该如何选择?
服务消费者利用负载均衡算法,从服务列表中挑选一个
消费者如何得知服务提供者的健康状态?
- 服务提供者会每隔30秒向EurekaServer发送心跳请求,报告健康状态
- eureka会更新记录服务列表信息,心跳不正常会被剔除
- 消费者就可以拉取到最新的信息
配置心跳机制属性
eureka.server.enable-self-preservation:指定是否启用Eureka服务器的自我保护机制。所谓自我保护机制是防止在网络状况不好的情况下,Eureka服务器误删除大量的注册实例。启用自我保护机制时Eureka服务器会保护服务注册表中的信息,不再删除服务注册表中的数据。通常建议关闭自我保护机制。
默认情况下,“EurekaClient客户端”会定时(默认30秒)向“EurekaServer注册中心”发送心跳包,就是说EurekaClient会定时向EurekaServer发送一个请求,如果EurekaServer能收到,就知道当前这个EurekaClient是存活状态。
如果EurekaServer在一定时间内(一般默认是90秒),没有收到EurekaClient发送的心跳包,就会从服务列表中剔除该服务。但是如果在短时间内丢失了大量服务实例的心跳,这时候EurekaServer就会开启自我保护机制,不会剔除服务。
在什么环境下开启自我保护机制?
本地环境
建议关闭自我保护机制。因为在本地开发环境中,EurekaServer端相对来说重启频率不高,但是在EurekaClient端,可能改动代码之后需要重启,频率相对来说比较高;那么EurekaClient端重启之后就不会及时去向EurekaServer端发送心跳包,EurekaServer端就会认为是网络延迟或者其他原因,不会剔除服务,这样的话就会影响开发效率。生产环境
建议开启自我保护机制。因为生产环境不会频繁重启服务器,并且EurekaClient端与EurekaServer端存在网络延迟的几率较高,所以需要开启自我保护机制避免误删服务。
eureka.instance.eviction-interval-timer-in-ms:指定清理无效节点的时间间隔,单位为ms。默认值为60000,也就是说Eureka服务器每60s执行一次清理无效节点的操作。
eureka.instance.lease-renewal-interval-in-seconds:指定Eureka客户端需要多长时间发送心跳包给eureka服务器,单位为s。默认值为30。
eureka.instance.lease-expiration-duration-in-seconds:指定Eureka客户端如果长时间没有发送心跳包给eureka服务器,eureka服务器会将其删除,单位为s。默认值为90。
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://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://server1:7001/eureka,http://server2:7002/eureka
# 集群版
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001
#访问路径可以显示IP地址
prefer-ip-address: true
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
#lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
#lease-expiration-duration-in-seconds: 2
#保护机制
# server:
# enable-self-preservation:
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.lht.springcloud.entities # 所有Entity别名类所在包
三、Spring Cloud Eureka的应用-重、难点
搭建EurekaServer服务步骤如下:
1.创建项目,引入spring-cloud-starter-netflix-eureka-server的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.添加application.yml文件,编写下面的配置
server:
port: 7001
eureka:
instance:
hostname: server1 #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#集群指向其它eureka
#defaultZone: http://eureka7002.com:7002/eureka/
#单机就是7001自己
defaultZone: http://server2:7002/eureka/
#server:
#关闭自我保护机制,保证不可用服务被及时踢除
#enable-self-preservation: false
#eviction-interval-timer-in-ms: 2000
3.编写启动类,添加@EnableEurekaServer注解
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
启动EurekaServer
在浏览器中访问http://localhost:7000/。效果如图所示。
Eureka注册中心的主页
•System Status:显示Eureka注册中心的描述信息和状态
•DS Replicas:Eureka注册中心可以配置为服务器集群,在这里可以显示集群中的相邻Eureka服务器节点。
•General Info:Eureka注册中心的常规信息。
•Instances currently registered with Eureka:显示注册到Eureka注册中心的实例信息。
•Instance Info:当前Eureka注册中心的实例信息。
user-service(服务提供者)完成服务注册
将user-service服务注册到EurekaServer步骤如下:
1.在user-service项目引入spring-cloud-starter-netflix-eureka-client的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.在application.yml文件,编写下面的配置:
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://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://server1:7001/eureka,http://server2:7002/eureka
# 集群版
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001
#访问路径可以显示IP地址
prefer-ip-address: true
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
#lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
#lease-expiration-duration-in-seconds: 2
# server:
# enable-self-preservation:
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.lht.springcloud.entities # 所有Entity别名类所在包
user-service完成服务注册
保证Eureka Server启动的状态下,启动user-service,在浏览器中访问Eureka Server的主界面http://localhost:7001/,效果如下图所示。
order-service(服务消费者)完成服务注册
order-service虽然是消费者,但与user-service一样都是eureka的client端,同样可以实现服务注册:
1.在order-service项目引入spring-cloud-starter-netflix-eureka-client的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
1.在application.yml文件,编写下面的配置:
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: false
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机
#defaultZone: http://localhost:7001/eureka
# 集群
defaultZone: http://server1:7001/eureka,http://server2:7002/eureka # 集群版
CAP原则回顾
计算机专家Eric Brewer于2000年在ACM分布式计算原理专题讨论会(简称PODC、Principles Of Distributed Computing)中提出的分布式系统设计要考虑三个核心要素:
一致性(Consistency):同一时刻的同一请求的实例返回的结果相同,所有的数据要求具有强一致性(Strong Consistency);
可用性(Availability):所有实例的读写请求在一定时间内可以得到正确的响应;
分区容错性(Partition tolerance):在网络异常(挖断光缆、设备故障、宕机)的情况下,系统仍能提供正常的服务;
以上的三个特点就是CAP原则(又称CAP定理),但是这三个特性不可能同时满足,所以分布式系统设计要考虑的是在满足P(分区容错性)的前提下选择C(一致性)还是A(可用性),即:CP或AP。
结论:Eureka为AP设计。作为服务发现产品,可用性优先级较高,一致性的特点并不重要,宁可返回错误的数据,也比不返回结果要好得多。
搭建Eureka高可用集群
Eureka Server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。
更改系统hosts文件配置
以 Windows系统为例,如果要构建集群,需要修改 hosts 文件,为其添加主机名的映射。
打开C:\Windows\System32\drivers\etc\hosts 文件,添加以下内容:
改造Eureka Server(相互注册)
eureka-server的application.yml配置文件内容如下:
按照搭建eureka-server的方式,再搭建一个名为eureka-server-another的Eureka Server。
改造服务提供者
按照搭建user-service的方式,搭建一个名为user-service-another的服务提供者。
启动测试
依次启动两个Eureka Server、两个服务提供者、一个服务消费者。启动成功后,无论访问哪个Eureka Server,Eureka Server的注册实例都是一样的,访问server1:7000的页面效果如下图所示。
在order-service完成服务拉取
服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡
1.修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口:
2.在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解: