Spring Cloud Eureka
服务发现与注册
Eureka Client
eurela client存在于每个provider和consumer中。
eureka client有以下动作:
- 将本服务的信息注册到eureka注册中心。
- 拉取注册注册中心的注册表,把各服务的注册信息缓存在本地中。
- 定时向注册中心发送心跳(默认30s)保持通信并更新注册中心的注册表到本地注册表缓存中。如果超过90s没有发送心跳,则注册中心将会从注册表中将该服务删除。
- 服务触发下线操作后,会主动请求注册中心删除该服务的信息。
如果在代码中,一个服务调用了另一个服务的接口,那么当前服务会从本地的注册表缓存中根据要调用的服务的serviceID获取到服务信息,根据服务信息通过http(Spring Cloud Feign,Spring boot2.X使用的是openfeign,在从1.X升级到2.X时注意feign包的引用)请求去调用。
Eureka Service
eureka service是服务注册中心,可以存在多个,因此可以部署eureka service的集群保证高可用。如果当一个节点挂掉,比如,此时有个服务发起renew请求,发现连接失败。eureka会自动切换到另一个节点;除非eureka service全部挂掉,那神仙也没办法。
当15分钟内,集群中超过85%的客户端,没有正常心跳,则会开启自我保护机制。此时eureka server不再剔除注册表中的客户端,当此时eureka server接收到客户端注册请求,会在当前节点上注册,但是不会同步到其他eureka server节点上。当网络恢复正常,也就是集群中的节点恢复正常心跳后。则会把注册信息同步到其他的eureka server节点。
eureka service有以下动作:
- 接收eureka client端的服务注册,当收到client端的注册请求时,会把client端的注册信息缓存到本地,并同步到其他eureka service,如果client端注册到该节点的注册中心失败时,会自动寻找下一个注册中心节点并注册。
- 提供服务注册表,每个eureka client会定时通过eureka service获取注册表缓存到本地。
- 服务管理,eureka service管理eureka client的renew、cancel等操作,保证eureka client每次能拿到集群中最新的注册表信息。
- 集群管理,每个eureka service更新服务注册表的数据后,会同步给集群中的其他eureka service。
通过eureka记录了服务信息后,还并不能调用远程服务。因为eureka只提供服务的注册与发现,不提供远程调用。
Eureka更新到2.0版本后,官方已经停止维护。
Spring Cloud Consul
consul与eureka的区别:
-
consul提供CP(一致性、分区容错性)的保证,consul使用Raft协议保证其一致性。如果一个普通节点挂了,那么整个系统不会down掉,请求会转移到活着的节点上。但是当集群leader挂了后,集群会停止服务,直到新的leader选出来之后。集群才可继续运转。并且在服务注册时,服务信息需要同步写入集群的半数以上的机器才算成功,服务注册速度相对于eureka更慢,这就是为什么能保证CP缺不能保证A(可用性)的原因。
-
eureka提供了AP(可用性、分区容错性)的保证,eureka通过弱一致性,保证了集群的AP(可用性、分区容错性),当eureka的一个普通节点挂掉以后,请求也是会往另外的节点转发过去,当eureka service集群的某个节点挂掉后,服务也可以照常运行。因为eureka service通常由3台或更多的服务器组成eureka service,eureka service的数据会互相同步。但是在同步时,不会保证每次数据同步都能完成。并且eureka的服务注册相对于consul更快,因为eureka的服务只需要在集群中的一台eureka service上注册成功就算服务注册完成,之后再去eureka service中同步服务信息,这就导致了可能在eureka service1能成功调用该新注册的服务,但是eureka service2中调用不了该新注册的服务,所以虽然eureka service中的注册信息虽不一致,但集群依然能正常运作。
Spring Cloud Feign
Spring Cloud Feign是Spring Cloud中的核心组件,如果没有了Feign,那么分布式集群的远程调用将会变的复杂无比。
使用@EnableFeignClients启用feign
@EnableFeignClients
是启用feign最基础的注解。注解中使用了@Import(FeignClientsRegistrar.class)
导入了feign组件的注册器,以下是工作原理:
- 先查看是否定义了
defaultConfiguration
,如果定义了,则生成自定义配置的bean。未定义则使用系统默认的。该配置指定了Decoder
解码器、Encoder
编码器、Contract
组件扫描构造器。 - 扫描由
basePackages
定义的包地址,如果未定义basePackges
,则扫描全包。 - 扫描项目中贴了
@FeignClient
的类。获取到@FeignClient
注解的配置的值,如value/name
、path
等。 - 然后通过Spring封装的
Ribbon
客户端选择一个主机地址。根据value/name
、path
等值,封装成一个Request,调用目标主机。 - 如果调用失败:看用户是否配置了Retryer来覆盖默认重试机制,如果未设置,默认不重试,再调用用户配置的
fallback
信息,如果fallback
未设置,该fallback
指定了用户自定义的失败回调配置类,可以通过配置hystrix
来控制服务的熔断和降级功能。
Spring Cloud Ribbon
在我们平常使用Spring Cloud服务时,是感受不到Ribbon
的存在的,因为feign封装了Ribbon
,使得调用远程服务的方式变得异常简单,只需要两个注解@EnableFeignClients
和@FeignClient
即可实现远程调用和负载均衡。
Ribbon
默认使用的策略是轮询策略,集群中的每台服务器都可“雨露均沾”
feign把相关参数都包装好后,生成的request最终通过FeignClientFactoryBean.loadBlance
方法调用Ribbon
负载均衡客户端。
Spring Cloud Ribbon
根据feign传过来的参数封装成RibbonRequest
,通过负载均衡算法,在eureka service
提供的服务注册表中选择一台最合适的主机,最终调用到一台目标主机。
在使用feign的时候如果使用注解@EnableFeignClients
时定义了url
参数,那么不会使用负载均衡。反之,则使用的是负载均衡。
Spring Cloud Hystrix
Hystrix是为了解决微服务容错性问题,在微服务架构中,底层服务经常有复杂的依赖关系,比如:订单服务依赖积分、库存等服务。如果下单时,积分服务挂了,或者积分服务超时,前面的请求还在处理,后面的请求又过来了。一直卡在积分服务这里,又会导致订单服务超时。就会导致雪崩效应,这时Hystrix提供了快速失败和降级功能。当检测到积分服务超时或报错时,接下来通往积分服务的请求都会快速失效,然后订单服务可以继续执行接下来的流程。
Hystrix主要有以下特点:
- 自定义请求超时时间,在服务调用的时候,如果请求可以长时间的存在。前面的请求没有执行完,后面不断有请求过来,导致请求越积越多,从而导致雪崩效应致使整个服务全部down掉。如果可以控制请求的超时时间,就可以当某个请求超过预定的请求时间时,请求直接失效。给后面的请求让路。
- 线程池隔离服务,线程池隔离了服务依赖的不同资源,比如:订单服务依赖的积分、库存等服务,每个服务预先分配了不同的线程池,当其中某个服务挂掉时,最多也只会影响到挂掉的服务的线程池,不会影响到其他线程池的服务。如果没有线程池来隔离服务。那么,一个服务挂掉了,导致请求积压,那么其他服务的CPU资源将被占用,间接导致其他服务挂掉。
- 请求统计,当请求失败到一定次数后,直接把服务变为不可用,接下来的请求就会直接失效。不会造成请求的积压,最终导致服务的崩溃。
在@FeignClient
这个注解中有一个fallback
和fallbackFactory
两个参数,两个参数都定义了当请求失败后,需要执行的类。该类可以自定义的实现一些请求失败后需要进行的操作,该类需要实现FeignClient
接口。
Spring Cloud Zuul
Zuul是整个Spring Cloud项目的入口,前端过来的请求都需要经过Zuul,然后Zuul根据请求找到后台对应的服务。
Zuul的特点:
- 统一的降级,对于某些特殊的业务,可以在后台服务中做该业务对应的降级,但是并不是所有的服务都是有类似的需求。所以可以在Zuul中做统一的降级来保证其他服务的可用。
- 限流,每个网站都应该有对应的限流策略,防止恶意攻击。
- 认证授权,每个前端请求都通过zuul进入到后台系统中,zuul根据过滤规则放行特定的请求并过滤无关的请求。在请求到达zuul的时候,zuul会验证请求的合法性。通过验证后,根据请求匹配到后台服务,然后将该请求转发到对应的后台服务中去。
Zuul虽然在2.X的版本使用了非阻塞的方式实现,但是Netflix官宣Zuul2.X后不再维护开源版本,且Spring Cloud暂时也没有整合的计划。可以考虑换一波Spring Cloud Gateway了。
自定义ZuulFilter
zuul的filter一共有四个生命周期:
- pre 请求在路由之前调用
- route 请求已经得到微服务路由,在请求微服务的时候调用
- post 请求在已经调用微服务之后调用
- error 请求在调用微服务后,发生错误时调用
我们可以实现ZuulFilter,然后根据不同的生命周期来做不同的事情。
路由熔断
zuul提供了ZuulFallbackProvider接口,该接口提供了getRoute和fallbackResponse两个方法。getRoute方法主要指定对哪个服务进行熔断,fallbackResponse可以指定熔断后的返回数据。
ZuulFallbackProvider是1.x的版本,2.x里使用FallbackProvider。
Spring Cloud Gateway
对于Zuul来说,Gateway使用Spring5+开发,并且官方宣称性能要比Zuul好。不过这些都不是理由,我觉得还是稳定性。Spring Cloud Gateway目前GitHub的活跃度和社区建设来说要比zuul好得多,因为毕竟open zuul是官方弃子,而且Spring Cloud Gateway是spring的官方项目并且经过生产实践,不用担心稳定性的问题。
Spring Cloud Gateway对比Zuul1.X:
功能 | Gateway | Zuul |
---|---|---|
Websockets | 支持 | 1.X不支持,使用servlet 2.5开发,2.X支持 |
非阻塞 | 支持 | 1.X不支持,2.X支持 |
Spring Cloud Gateway使用Spring 5 + Spring Boot 2开发,所以在项目中使用时。mvn包不能引用mvc的包,直接引用webflux的包即可。
其实对于网关来说,最重要的功能也就几个:路由、请求过滤、转发。所以gateway和zuul的功能性上面其实是几乎一样的。
总结
Spring Cloud请求流程如下:
Spring Cloud Gateway(或Zuul) -> consul(或eureka) -> feign -> ribbon -> hystrix