Spring Cloud各核心组件的功能及作用

Spring Wu 305 2021-02-03

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/namepath等。
  • 然后通过Spring封装的Ribbon客户端选择一个主机地址。根据value/namepath等值,封装成一个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这个注解中有一个fallbackfallbackFactory两个参数,两个参数都定义了当请求失败后,需要执行的类。该类可以自定义的实现一些请求失败后需要进行的操作,该类需要实现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:

功能GatewayZuul
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