1. eureka注册中心
1.1 微服务注册中心
注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。
1.2 注册中中心的主要作用
服务注册中心(下称注册中心)是微服务架构非常重要的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:
- 服务发现:
服务注册/反注册:保存服务提供者和服务调用者的信息
服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功能
服务路由(可选):具有筛选整合服务提供者的能力。 - 服务配置:
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
配置下发:主动将配置推送给服务提供者和服务调用者
服务健康检测
检测服务提供者的健康情况
1.3 eureka基础架构
1.3.1 按角色区分
-
服务注册中心
提供服务注册和发现,多个Eureka Server之间会同步数据,做到状态一致(最终一致性),简单理解就是将每个eureka client角色的服务将服务信息交由euraka server进行管理。eureka server可以相互注册和数据同步 -
服务提供者
Service Provider服务提供方将自身服务注册到eureka server,从而使服务消费方能够找到 -
服务消费者
Service Consumer服务消费方从eureka server获取注册服务列表,从而能够消费服务
1.3.2 按架构区分
- eureka server
各个节点启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到 - eureka client
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
1.4 eureka服务注册与发现
-
服务发现
eureka server 端通过appName和instanceId来唯一区分一个服务实例,服务信息的保存源码中是使用map管理,所以对于服务消费方,就可以获取到服务方相关服务元信息进行调用,需要注意的是每个eureka server 也可能会扮演eureka client角色,它会向它配置文件中的其他Eureka Server进行拉取注册表、服务注册和发送心跳等操作。
// 第一层的key是appName,第二层的key是instanceId private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
-
服务注册
Service Provider启动时会将服务信息(InstanceInfo)发送给eureka server,eureka server接收到之后会写入registry中。服务注册默认过期时间DEFAULT_DURATION_IN_SECS = 90秒,每60秒清理超过90s未续约的peer节点信息,eurekaclient每30秒InstanceInfo写入到本地registry之后,然后同步给其他peer节点。Peer之间的状态是采用异步的方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的(AP).如果同步过程中,这时会根据异常信息做对应的处理,如果是读取超时或者网络连接异常,则稍后重试;如果其他异常则打印错误日志不再后续处理。 -
服务续约移除
当客户端启动想 eureka server注册了本身服务列表后,需要隔段时间发送一次心跳给 eureka server服务 端来证明自己还活着,当 eureka server 收到这个心跳请求后才会知道客户端还活着,才会维护该服务列表信息。一旦因为某些原因导致客户端没有按时发送心跳给 eureka server服务端, 这时候eureka server可能会认为你这个客户端已经挂了,它就有可能把该服务从服务列表中删除掉
1.5 eureka自我保护机制
- 自我保护机制引入
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。自我保护机制引入就是为了保护网络波动的情况导致的服务注册信息丢失
1.6 eureka集群模式
服务启动后向Eureka注册,Eureka Server会将注册信息向其他Eureka Server进行同步,当服务消费者要调用服务提供者,则向服务注册中心获取服务提供者地址,然后会将服务提供者地址缓存在本地,下次再调用时,则直接从本地缓存中取,完成一次调用。简单的来说,就是eureka互相注册观望,信息相互同步.
1.7 eureka项目搭建
1.7.1 spring cloud eureka 相关配置
- eureka server 相关配置
#服务端开启自我保护模式 eureka.server.enable-self-preservation=true #扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒 eureka.server.eviction-interval-timer-in-ms= 60000 #间隔多长时间,清除过期的 delta 数据 eureka.server.delta-retention-timer-interval-in-ms=0 #请求频率限制器 eureka.server.rate-limiter-burst-size=10 #是否开启请求频率限制器 eureka.server.rate-limiter-enabled=false #请求频率的平均值 eureka.server.rate-limiter-full-fetch-average-rate=100 #是否对标准的client进行频率请求限制。如果是false,则只对非标准client进行限制 eureka.server.rate-limiter-throttle-standard-clients=false #注册服务、拉去服务列表数据的请求频率的平均值 eureka.server.rate-limiter-registry-fetch-average-rate=500 #设置信任的client list eureka.server.rate-limiter-privileged-clients= #在设置的时间范围类,期望与client续约的百分比。 eureka.server.renewal-percent-threshold=0.85 #多长时间更新续约的阈值 eureka.server.renewal-threshold-update-interval-ms=0 #对于缓存的注册数据,多长时间过期 eureka.server.response-cache-auto-expiration-in-seconds=180 #多长时间更新一次缓存中的服务注册数据 eureka.server.response-cache-update-interval-ms=0 #缓存增量数据的时间,以便在检索的时候不丢失信息 eureka.server.retention-time-in-m-s-in-delta-queue=0 #当时间戳不一致的时候,是否进行同步 eureka.server.sync-when-timestamp-differs=true #是否采用只读缓存策略,只读策略对于缓存的数据不会过期。 eureka.server.use-read-only-response-cache=true ################server node 与 node 之间关联的配置#####################33 #发送复制数据是否在request中,总是压缩 eureka.server.enable-replicated-request-compression=false #指示群集节点之间的复制是否应批处理以提高网络效率。 eureka.server.batch-replication=false #允许备份到备份池的最大复制事件数量。而这个备份池负责除状态更新的其他事件。可以根据内存大小,超时和复制流量,来设置此值得大小 eureka.server.max-elements-in-peer-replication-pool=10000 #允许备份到状态备份池的最大复制事件数量 eureka.server.max-elements-in-status-replication-pool=10000 #多个服务中心相互同步信息线程的最大空闲时间 eureka.server.max-idle-thread-age-in-minutes-for-peer-replication=15 #状态同步线程的最大空闲时间 eureka.server.max-idle-thread-in-minutes-age-for-status-replication=15 #服务注册中心各个instance相互复制数据的最大线程数量 eureka.server.max-threads-for-peer-replication=20 #服务注册中心各个instance相互复制状态数据的最大线程数量 eureka.server.max-threads-for-status-replication=1 #instance之间复制数据的通信时长 eureka.server.max-time-for-replication=30000 #正常的对等服务instance最小数量。-1表示服务中心为单节点。 eureka.server.min-available-instances-for-peer-replication=-1 #instance之间相互复制开启的最小线程数量 eureka.server.min-threads-for-peer-replication=5 #instance之间用于状态复制,开启的最小线程数量 eureka.server.min-threads-for-status-replication=1 #instance之间复制数据时可以重试的次数 eureka.server.number-of-replication-retries=5 #eureka节点间间隔多长时间更新一次数据。默认10分钟。 eureka.server.peer-eureka-nodes-update-interval-ms=600000 #eureka服务状态的相互更新的时间间隔。 eureka.server.peer-eureka-status-refresh-time-interval-ms=0 #eureka对等节点间连接超时时间 eureka.server.peer-node-connect-timeout-ms=200 #eureka对等节点连接后的空闲时间 eureka.server.peer-node-connection-idle-timeout-seconds=30 #节点间的读数据连接超时时间 eureka.server.peer-node-read-timeout-ms=200 #eureka server 节点间连接的总共最大数量 eureka.server.peer-node-total-connections=1000 #eureka server 节点间连接的单机最大数量 eureka.server.peer-node-total-connections-per-host=10 #在服务节点启动时,eureka尝试获取注册信息的次数 eureka.server.registry-sync-retries= #在服务节点启动时,eureka多次尝试获取注册信息的间隔时间 eureka.server.registry-sync-retry-wait-ms= #当eureka server启动的时候,不能从对等节点获取instance注册信息的情况,应等待多长时间。 eureka.server.wait-time-in-ms-when-sync-empty=0
- eureka client 常用配置
#该客户端是否可用 eureka.client.enabled=true #实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true eureka.client.register-with-eureka=false #此客户端是否获取eureka服务器注册表上的注册信息,默认为true eureka.client.fetch-registry=false #是否过滤掉,非UP的实例。默认为true eureka.client.filter-only-up-instances=true #与Eureka注册服务中心的通信zone和url地址 eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ #client连接Eureka服务端后的空闲等待时间,默认为30 秒 eureka.client.eureka-connection-idle-timeout-seconds=30 #client连接eureka服务端的连接超时时间,默认为5秒 eureka.client.eureka-server-connect-timeout-seconds=5 #client对服务端的读超时时长 eureka.client.eureka-server-read-timeout-seconds=8 #client连接all eureka服务端的总连接数,默认200 eureka.client.eureka-server-total-connections=200 #client连接eureka服务端的单机连接数量,默认50 eureka.client.eureka-server-total-connections-per-host=50 #执行程序指数回退刷新的相关属性,是重试延迟的最大倍数值,默认为10 eureka.client.cache-refresh-executor-exponential-back-off-bound=10 #执行程序缓存刷新线程池的大小,默认为5 eureka.client.cache-refresh-executor-thread-pool-size=2 #心跳执行程序回退相关的属性,是重试延迟的最大倍数值,默认为10 eureka.client.heartbeat-executor-exponential-back-off-bound=10 #心跳执行程序线程池的大小,默认为5 eureka.client.heartbeat-executor-thread-pool-size=5 # 询问Eureka服务url信息变化的频率(s),默认为300秒 eureka.client.eureka-service-url-poll-interval-seconds=300 #最初复制实例信息到eureka服务器所需的时间(s),默认为40秒 eureka.client.initial-instance-info-replication-interval-seconds=40 #间隔多长时间再次复制实例信息到eureka服务器,默认为30秒 eureka.client.instance-info-replication-interval-seconds=30 #从eureka服务器注册表中获取注册信息的时间间隔(s),默认为30秒 eureka.client.registry-fetch-interval-seconds=30 # 获取实例所在的地区。默认为us-east-1 eureka.client.region=us-east-1 #实例是否使用同一zone里的eureka服务器,默认为true,理想状态下,eureka客户端与服务端是在同一zone下 eureka.client.prefer-same-zone-eureka=true # 获取实例所在的地区下可用性的区域列表,用逗号隔开。(AWS) eureka.client.availability-zones.china=defaultZone,defaultZone1,defaultZone2 #eureka服务注册表信息里的以逗号隔开的地区名单,如果不这样返回这些地区名单,则客户端启动将会出错。默认为null eureka.client.fetch-remote-regions-registry= #服务器是否能够重定向客户端请求到备份服务器。 如果设置为false,服务器将直接处理请求,如果设置为true,它可能发送HTTP重定向到客户端。默认为false eureka.client.allow-redirects=false #客户端数据接收 eureka.client.client-data-accept= #增量信息是否可以提供给客户端看,默认为false eureka.client.disable-delta=false #eureka服务器序列化/反序列化的信息中获取“_”符号的的替换字符串。默认为“__“ eureka.client.escape-char-replacement=__ #eureka服务器序列化/反序列化的信息中获取“$”符号的替换字符串。默认为“_-” eureka.client.dollar-replacement="_-" #当服务端支持压缩的情况下,是否支持从服务端获取的信息进行压缩。默认为true eureka.client.g-zip-content=true #是否记录eureka服务器和客户端之间在注册表的信息方面的差异,默认为false eureka.client.log-delta-diff=false # 如果设置为true,客户端的状态更新将会点播更新到远程服务器上,默认为true eureka.client.on-demand-update-status-change=true #此客户端只对一个单一的VIP注册表的信息感兴趣。默认为null eureka.client.registry-refresh-single-vip-address= #client是否在初始化阶段强行注册到服务中心,默认为false eureka.client.should-enforce-registration-at-init=false #client在shutdown的时候是否显示的注销服务从服务中心,默认为true eureka.client.should-unregister-on-shutdown=true
- eureka instanse 常用配置
#服务注册中心实例的主机名 eureka.instance.hostname=localhost #注册在Eureka服务中的应用组名 eureka.instance.app-group-name= #注册在的Eureka服务中的应用名称 eureka.instance.appname= #该实例注册到服务中心的唯一ID eureka.instance.instance-id= #该实例的IP地址 eureka.instance.ip-address= #该实例,相较于hostname是否优先使用IP eureka.instance.prefer-ip-address=false
1.7.2 项目模块详细
-
技术版本
spring cloud : Hoxton.SR6
springboot : 2.3.2.RELEASE -
项目结构
appName instanseId address role eureka-server eureka9001 eureka9001.com:9001 eureka server eureka-server eureka9002 eureka9002.com:9002 eureka server eureka-server eureka9003 eureka9003.com:9003 eureka server app-service service8001 localhost:8001 eureka client app-service service8002 localhost:8002 eureka client app-client client7001 localhost:7001 eureka client app-client client7002 localhost:7002 eureka client
1.7.3 eruka server 集群搭建
-
修改host文件模拟多主机
-
yml文件
server: port: 9001 spring: application: name: eureka-server eureka: instance: hostname: eureka9001.com instance-id: eureka9001 client: register-with-eureka: false #是否将自己注册到注册中心 fetch-registry: false # 是否从eureka中获取注册信息 #配置暴露给Eureka client的请求地址 service-url: # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ defaultZone: http://eureka9002.com:9002/eureka/,http://eureka9003.com:9003/eureka/ server: enable-self-preservation: false
-
配置启动类
@SpringBootApplication @EnableEurekaServer // 开启eureka server public class EurekaServer01Application { public static void main(String[] args) { SpringApplication.run(EurekaServer01Application.class, args); } }
-
启动服务eureka-server 下三个实例(eureka9001,eureka9002,eureka9003),分别访问eureka管理页面
从上图就可以已经看到,在eureka管理界面上已经可以看出 eureka sever三个服务已经相互守望,相互监控
1.7.4 eureka client 搭建
-
yml配置
server: port: 8001 spring: application: name: app-service eureka: client: service-url: defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9003.com:9003/eureka/ instance: prefer-ip-address: true # 使IP地址注册 instance-id: service8001
server: port: 8002 spring: application: name: app-service eureka: client: service-url: defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9003.com:9003/eureka/ instance: prefer-ip-address: true # 使IP地址注册 instance-id: service8002
-
.启动类配置
@SpringBootApplication @EnableEurekaServer public class ApplicationService01Application { public static void main(String[] args) { SpringApplication.run(ApplicationService01Application.class, args); } }
-
.启动 . app-service 下两个实例 app-service8001 app-service8002,观察euraka管理界面
以 eureka9001server为例,此时instances里面已经管理了service8001,service8002 两个的服务实例信息。
1.7.5 验证服务动态发现
- 在1.7.4 状态下被管理服务 app-service下两个实例 service8001 service8002
- client7001实例yml配置
server: port: 7001 spring: application: name: app-client eureka: client: service-url: defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/,http://eureka9003.com:9003/eureka/ instance: prefer-ip-address: true # 使IP地址注册 instance-id: client7001
- 启动client7001实例
打开管理界面可以看到 app-client下client7001已经被管理
1.7.6 验证服务的剔除
- 1.7.5下状态client7001,service8001,service8002服务信息已经被配置中心管理,euraka管理信息如下
- 此时关闭client7001实例,模拟服务down机,观察管理界面,发现其并不是立即清除client7001注册信息,在一段时间之后服务注册信息消除。
- client7001服务信息没有立即清除原因
eureka在client7001上一次收到心跳时间为准,如果在90s内没有再次收到心跳包,就会将实例注销,并且eureka默认60秒间隔会剔除无效信息。所以会产生一定时间延迟。对应参数配置:#剔除失效服务间隔(默认60s=60000ms) eureka.server.eviction-interval-timer-in-ms=60000 #Eureka服务端在收到最后一次心跳之后等待的时间上限,默认90s,单位为秒,超过则失效(客户端告诉服务端按照此规则等待自己) eureka.instance.lease-expiration-duration-in-seconds =90
1.7.7 eureka 管理界面详解
-
system status 模块
Renews threshold:每分钟最少的续约数,这个值是根据开启自我保护的阈值计算的
例如:Renews threshold=3,全部服务每分钟最少要有3个续约请求,不然就进入自我保护模式
spring cloud根据eureka.server.renewal-percent-threshold配置阈值,默认0.85
Renews (last min):最后一分钟,所有客户端续订的次数
例如:Renews (last min) = 4,代表在最后一分钟,所有客户端一共向这个eureka-server续费了4次 -
DS Replicas 模块 注册中心的副本地址
ureka-server端配置的eureka.client.service-url.defaultZone属性中的值,简单来说就是除自己以外的eureka server 地址 -
Instances currently registered with Eureka
Application:服务名称。配置的spring.application.name属性
Availability Zones:实例的数量
Status:实例的状态 + eureka.instance.instance‐id的值。实例的状态分为UP、DOWN、STARTING、OUT_OF_SERVICE、UNKNOWN. UP: 服务正常运行,特殊情况当进入自我保护模式,所有的服务依然是UP状态,所以需要做好熔断重试等容错机制应对灾难性网络出错情况 OUT_OF_SERVICE : 不再提供服务,其他的Eureka Client将调用不到该服务,一般有人为的调用接口设置的,如:强制下线。 UNKNOWN: 未知状态 STARTING : 表示服务正在启动中 DOWN: 表示服务已经宕机,无法继续提供服务
-
General Info
total-avail-memory:当前eureka-server服务被分配的总可用内存,不是宿主机的总可用内存,不知道怎么算的
environment:不知道,和左上角的一样,好像是eureka的环境,默认是test环境,不知道怎么修改
num-of-cpus:cup数量
current-memory-usage:已经使用内存+占用总内存百分比,相对于total-avail-memory
server-uptime:eureka-server当前服务运行总时间
registered-replicas:当前eureka-server主动注册的地址,注册的eureka-server副本
unavailable-replicas:不可用的副本,不可用的eureka-server实例副本如果多台正常的eureka-server相互注册在一台服务器,也就是ip地址或域名一样时或者如果域名不一样,但是一台机器,虽然eureka-server正常工作,但是也会被认为不可用副本
available-replicas:可用的副本,需要多台eureka-server相互注册,且不是在同一台服务器,才会在可用的副本里面,也就是ip地址或域名一样时或者如果域名不一样,但是一台机器,虽然eureka-server正常工作,但是也会被认为不可用副本
1.8 eureka 安全模式
1.8.1 整合 spring security 保证安全模式
其实按照我的理解,eureka的安全模式并不是其自身的性质,而是借助spring security 来进行认证的。spring security 在不配置爱定义的auth认证会默认跳转到/login ,进行登录验证。
注意点:在开启spring security之后,一定要在server 段放行心跳请求,免认证,进行配置。
2. ribbon 负载均衡
2.1 负载均衡
负载均衡是指通过负载均衡策略分配到多个执行单元上,常见的负载均衡方式有两种
- 服务器反向代理负载均衡
反向代理只值对服务器的代理,代理服务器接受请求,通过负载均衡算法,将请求转发给后端服务器,后端服务返回给代理服务器然后代理服务器返回到客户端。反向代理服务器的优点是隔离后端服务器和客户端,使用双网卡屏蔽真实服务器网络,安全性更好,相比较于DNS域名解决负载均衡,反向代理在故障处理方面更灵活,支持负载均衡算法的横向扩展。 - 客户端负载均衡
客户端负载均衡中,客户端可以知道所有服务端的详细信息,当需要调用 Server上的接口时,客户端从自身已知的Server列表中,根据提前配置的负载均衡策略,自己挑选一个服务端来调用,此时客户端知道自己调用的是哪一个 Server
2.2 ribbon客户端负载均衡架构
- 简单架构图
ribbon 是典型的客户端负载均衡实现体现,通过获取Eureak注册中心的 服务调用信息,经过负载均衡算法选择下游服务实例发起调用。
2.3 ribbon使用
-
appservice 提供两个实例 service1 service2 供 服务client1进行调用
appName port instance id app-client 7001 client1 app-service 8001 service1 app-service 8002 service2 -
appservice服务提供方调用接口模拟代码
暴露接口: http://APP-SERVICE/serviceServer/info/{times}@RestController @RequestMapping("/serviceServer") public class RibbonTestController { @Value("${spring.application.name}") private String applicationName; @Value("${server.port}") private String port; @Value("${eureka.instance.instance-id}") private String instanceId; /** * 简单模拟一下返回请求响应 区分实际调用服务 * * @param requestTime 调用次数 */ @GetMapping("/info/{requestTimes}") public Map getServerInfo(@PathVariable("requestTimes") String requestTimes) { Map<Object, Object> result = MapUtil.builder().put("target.appName", applicationName) .put("target.port", port) .put("target.instanceId", instanceId)