Spring Cloud

Eureka

图片说明

服务提供者

Eureka服务治理体系支持跨平台,虽然我们前文使用了Spring Boot来作为服务提供者,但是对于其他技术平台只要支持Eureka通信机制,一样也是可以作为服务提供者,换句话说,服务提供者既可以是Java写的,也可以是python写的,也可以是js写的。这些服务提供者将自己注册到Eureka上,供其它应用发现然后调用,这就是我们的服务提供者

服务注册

服务提供者在启动的时候会通过发送REST请求将自己注册到Eureka Server上,同时还携带了自身服务的一些元数据信息。Eureka Server在接收到这个REST请求之后,将元数据信息存储在一个双层结构的Map集合中,第一层的key是服务名,第二层的key是具体服务的实例名,我们在上篇文章最后展示出来的截图中,大家也可以看出一些端倪,如下:
图片说明

同时,我们在服务注册时,需要确认一下eureka.client.register-with-eureka=true配置是否正确,该值默认就为true,表示启动注册操作,如果设置为false则不会启动注册操作。

服务同步

再说服务同步之前,我先描述一个场景:首先我有两个服务注册中心,地址分别是http://localhost:1111和http://localhost:1112,然后我还有两个服务提供者,地址分别是http://localhost:8080和http://localhost:8081,然后我将8080这个服务提供者注册到1111这个注册中心上去,将8081这个服务提供者注册到1112这个注册中心上去,此时我在服务消费者中如果只向1111这个注册中心去查找服务提供者,那么是不是只能获取到8080这个服务而获取不到8081这个服务?大家看下面一张图:
图片说明
答案是服务消费者可以获取到两个服务提供者提供的服务。虽然两个服务提供者的信息分别被两个服务注册中心所维护,但是由于服务注册中心之间也互相注册为服务,当服务提供者发送请求到一个服务注册中心时,它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步,通过服务同步,两个服务提供者的服务信息我们就可以通过任意一台注册中心来获取到。

服务续约

在注册完服务之后,服务提供者会维护一个心跳来不停的告诉Eureka Server:“我还在运行”,以防止Eureka Server将该服务实例从服务列表中剔除,这个动作称之为服务续约,和服务续约相关的属性有两个,如下:

eureka.instance.lease-expiration-duration-in-seconds=90
eureka.instance.lease-renewal-interval-in-seconds=30

第一个配置用来定义服务失效时间,默认为90秒,第二个用来定义服务续约的间隔时间,默认为30秒。

服务消费者

消费者主要是从服务注册中心获取服务列表,拿到服务提供者的列表之后,服务消费者就知道去哪里调用它所需要的服务了,我们从下面几点来进一步了解下服务消费者。

获取服务

当我们启动服务消费者的时候,它会发送一个REST请求给服务注册中心来获取服务注册中心上面的服务提供者列表,而Eureka Server上则会维护一份只读的服务清单来返回给客户端,这个服务清单并不是实时数据,而是一份缓存数据,默认30秒更新一次,如果想要修改清单更新的时间间隔,可以通过eureka.client.registry-fetch-interval-seconds=30来修改,单位为秒(注意这个修改是在eureka-server上来修改)。另一方面,我们的服务消费端要确保具有获取服务提供者的能力,此时要确保eureka.client.fetch-registry=true这个配置为true。

服务调用

服务消费者从服务注册中心拿到服务提供者列表之后,通过服务名就可以获取具体提供服务的实例名和该实例的元数据信息,客户端将根据这些信息来决定调用哪个实例,我们之前采用了Ribbon,Ribbon中默认采用轮询的方式去调用服务提供者,进而实现了客户端的负载均衡。

服务下线

服务提供者在运行的过程中可能会发生关闭或者重启,当服务进行正常关闭时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心我要下线了,服务注册中心收到请求之后,将该服务状态置为DOWN,表示服务已下线,并将该事件传播出去,这样就可以避免服务消费者调用了一个已经下线的服务提供者了。

服务注册中心

服务注册中心就是Eureka提供的服务端,它提供了服务注册与发现功能。

失效剔除

上面我们说到了服务下线问题,正常的服务下线发生流程有一个前提那就是服务正常关闭,但是在实际运行中服务有可能没有正常关闭,比如系统故障、网络故障等原因导致服务提供者非正常下线,那么这个时候对于已经下线的服务Eureka采用了定时清除:Eureka Server在启动的时候会创建一个定时任务,每隔60秒就去将当前服务提供者列表中超过90秒还没续约的服务剔除出去,通过这种方式来避免服务消费者调用了一个无效的服务。

自我保护

我们在前三篇文章中给大家看的截图上,都有这样一个警告,如下图:

图片说明

这个警告实际上就是触发了Eureka Server的自我保护机制。Eureka Server在运行期间会去统计心跳失败比例在15分钟之内是否低于85%,如果低于85%,Eureka Server会将这些实例保护起来,让这些实例不会过期,但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。我们在单机测试的时候很容易满足心跳失败比例在15分钟之内低于85%,这个时候就会触发Eureka的保护机制,一旦开启了保护机制,则服务注册中心维护的服务实例就不是那么准确了,此时我们可以使用eureka.server.enable-self-preservation=false来关闭保护机制,这样可以确保注册中心中不可用的实例被及时的剔除。

负载均衡

ribbon+restTemplate

它可以在客户端配置 ribbonServerList(服务端列表),然后轮询请求以实现均衡负载。

  • ribbon 是一个客户端负载均衡器,可以简单的理解成类似于 nginx的负载均衡模块的功能,可以很好的控制http和tcp的一些行为。
  • restTemplate 服务调用的的一个模块,配合@LoadBalanced,开启负载均衡的功能,其中设置了拦截器,每次调用都会拦截器拦截通过ip轮询请求的方式实现负载均衡

服务端的负载均衡

EurekaClient端的ipv4做为随机的种子,生成一个重新排序的serverList。ip轮询达到负载均衡

故障转移

Eureka集群故障转移

1、以8761和8762两个端口号启动EurekaServer
2、启动ServiceA,配置注册中心地址为:http://localhost:8761/eureka,http://localhost:8762/eureka
3、启动成功后,关闭8761端口的EurekaServer
4、在EurekaClient端发送心跳请求的地方打上断点:AbstractJerseyEurekaHttpClient.sendHeartBeat()
5、查看断点处数据,第一次请求的EurekaServer是8761端口的服务,因为该服务已经关闭,所以返回的response是null
6、第二次会重新请求8762端口的服务,返回的response为状态为200,故障转移成功。
总结:当EurekaServer部分服务节点宕机了,向他注册的服务依然可以访问成功,因为该服务节点的故障导致的访问不成功,已经被转移到其他节点了。

Ribbon

饥饿加载

笔者从网上看到很多博客中都提到过的一种情况:在进行服务调用的时候,如果网络情况不好,第一次调用会超时。有很多大神对此提出了解决方案,比如把超时时间改长一点、禁用超时等。

Spring Cloud 目前正在高速发展中,版本更新很快,我们能发现的问题基本上在版本更新的时候就修复了,或者提供最优的解决方案。

超时的问题也是一样,Ribbon 的客户端是在第一次请求的时候初始化的,如果超时时间比较短的话,初始化 Client 的时间再加上请求接口的时间,就会导致第一次请求超时。

本教程是基于 Finchley.SR2 撰写的,这个版本已经提供了一种针对上述问题的解决方法,那就是 eager-load 方式。通过配置 eager-load 来提前初始化客户端就可以解决这个问题。

ribbon.eager-load.enabled=true
ribbon.eager-load.clients=ribbon-eureka-demo

ribbon.eager-load.enabled:开启 Ribbon 的饥饿加载模式。
ribbon.eager-load.clients:指定需要饥饿加载的服务名,也就是你需要调用的服务,若有多个则用逗号隔开。

怎么进行验证呢?网络情况确实不太好模拟,不过通过调试源码的方式即可验证,在 org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration 中找到对应的代码,代码如下所示。

@Bean
@ConditionalOnProperty(value = "ribbon.eager-load.enabled")
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
    return new RibbonApplicationContextInitializer(springClientFactory(),ribbonEagerLoadProperties.getClients());
}

在 return 这行设置一个断点,然后以调试的模式启动服务,如果能进入到这个断点的代码这里,就证明配置生效了

ribbon和Feign

两者都有负载均衡的作用,在使用@FeignClient注解的时候 是默认使用了ribbon进行客户端的负载均衡的,默认的是随机的策略,那么如果我们想要更改策略的话,需要修改消费者yml中的配置。

  • Feign是一种声明式,模板化的HTTP客户端 (仅在consumer中使用),声明式调用就像调用本地方法一样调用远程方法,无感知远程http请求。
  • ribbon是一个基于Http和TCP的客户端负载均衡工具,它解决并提供了微服务的负载均衡问题

Hystrix

Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性、容错性与局部应用的弹性,是一个实现了超时机制和断路器模式的工具类库。

Hystrix的工作原理

  • 使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行。
  • 每个依赖都维护着一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。
  • 记录请求成功,失败,超时和线程拒绝。
  • 服务错误百分比超过了阈值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。
  • 请求失败,被拒绝,超时或熔断时执行降级逻辑。
  • 近实时地监控指标和配置的修改。

解决问题:
当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)

什么是断路器:
断路器有三种状态:

  • 打开状态:一段时间内 达到一定的次数无法调用 并且多次监测没有恢复的迹象 断路器完全打开 那么下次请求就不会请求到该服务
  • 半开状态:短时间内 有恢复迹象 断路器会将部分请求发给该服务,正常调用时 断路器关闭
  • 关闭状态:当服务一直处于正常状态 能正常调用

Hystrix有四种防雪崩方式:

  • 服务降级:接口调用失败就调用本地的方法返回一个空
  • 服务熔断:接口调用失败就会进入调用接口提前定义好的一个熔断的方法,返回错误信息
  • 服务隔离:隔离服务之间相互影响
  • 服务监控:在服务发生调用时,会将每秒请求数、成功请求数等运行指标记录下来。

雪崩效应场景:

  • 雪崩效应是在大型互联网项目中,当某个服务发生宕机时,调用这个服务的其他服务也会发生宕机,大型项目的微服务之间的调用是互通的,这样就会将服务的不可用逐步扩大到各个其他服务中,从而使整个项目的服务宕机崩溃.发生雪崩效应的原因有以下几点:
    • 1.单个服务的代码存在bug.
    • 2.请求访问量激增导致服务发生崩溃(如大型商城的枪红包,秒杀功能).
    • 3.服务器的硬件故障也会导致部分服务不可用.
    • 4.网络原因

Hystrix,可以控制熔断的最大队列,以及触发熔断启动,通过概率以及触发断路器开启的阀值。将会调用我们定义好的默认方法返回

配置参数详解

// 熔断器在整个统计时间内是否开启的阀值
 @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
 // 至少有3个请求才进行熔断错误比率计算
 /**
  * 设置在一个滚动窗口中,打开断路器的最少请求数。
  比如:如果值是20,在一个窗口内(比如10秒),收到19个请求,即使这19个请求都失败了,断路器也不会打开。
  */
 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "3"),
 //当出错率超过50%后熔断器启动
 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
 // 熔断器工作时间,超过这个时间,先放一个请求进去,成功的话就关闭熔断,失败就再等一段时间
 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
@HystrixProperty(name = "coreSize", value = "30"),
 /**
  * BlockingQueue的最大队列数,当设为-1,会使用SynchronousQueue,值为正时使用LinkedBlcokingQueue。
  */
 @HystrixProperty(name = "maxQueueSize", value = "101"),
 /**
  * 设置存活时间,单位分钟。如果coreSize小于maximumSize,那么该属性控制一个线程从实用完成到被释放的时间.
  */

/**
  我们知道,线程池内核心线程数目都在忙碌,再有新的请求到达时,线程池容量可以被扩充为到最大数量。
等到线程池空闲后,多于核心数量的线程还会被回收,此值指定了线程被回收前的存活时间,默认为 2,即两分钟。
*/
 @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
 /**
  * 设置队列拒绝的阈值,即使maxQueueSize还没有达到
  */
 @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),

// 滑动统计的桶数量
 /**
  * 设置一个rolling window被划分的数量,若numBuckets=10,rolling window=10000,
  *那么一个bucket的时间即1秒。必须符合rolling window % numberBuckets == 0。默认1
  */
 @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
 // 设置滑动窗口的统计时间。熔断器使用这个时间
 /** 设置统计的时间窗口值的,毫秒值。
  circuit break 的打开会根据1个rolling window的统计来计算。
  若rolling window被设为10000毫秒,则rolling window会被分成n个buckets,
  每个bucket包含success,failure,timeout,rejection的次数的统计信息。默认10000
  **/
 @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000")


fallbackMethod:方法执行时熔断、错误、超时时会执行的回退方法,需要保持此方法与 Hystrix 方法的签名和返回值一致。

defaultFallback:默认回退方法,当配置 fallbackMethod 项时此项没有意义,另外,默认回退方法不能有参数,返回值要与 Hystrix方法的返回值相同。

Spring Cloud Gateway 与 Zuul

Zuul构建于 Servlet 2.5,兼容 3.x,使用的是阻塞式的 API,不支持长连接,比如 websockets。另外
Spring Cloud Gateway构建于 Spring 5+,基于 Spring Boot 2.x 响应式的、非阻塞式的 API。同时,它支持 websockets,和 Spring 框架紧密集成,开发体验相对来说十分不错。

Zuul 2.x 在底层上有了很大的改变,使用了异步无阻塞式的 API,性能改善明显,不过现在 Spring Cloud 也没集成 Zuul 2.x

据了解,正是因为 Zuul 2.x 的不断跳票,Spring Cloud 才釜底抽薪推出了自己的服务网关:Spring Cloud Gateway,栈长看了下,使用起来比 Zuul 更简单,配置更方便,所以说选 Spring Cloud Gateway 没错,毕竟是 Spring Cloud 亲儿子,不会始乱终弃。

Zuul

Zuul是对SpringCloud提供的成熟对的路由方案,他会根据请求的路径不同,网关会定位到指定的微服务,并代理请求到不同的微服务接口,他对外隐蔽了微服务的真正接口地址。
三个重要概念:动态路由表,路由定位,反向代理:

  • 动态路由表:Zuul支持Eureka路由,手动配置路由,这俩种都支持自动更新
  • 路由定位:根据请求路径,Zuul有自己的一套定位服务规则以及路由表达式匹配
  • 反向代理:客户端请求到路由网关,网关受理之后,在对目标发送请求,拿到响应之后在 给客户端

它可以和Eureka,Ribbon,Hystrix等组件配合使用,
Zuul的应用场景:

  • 对外暴露,权限校验,服务聚合,日志审计等
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务