【Spring Cloud 7,2021大厂Java面试题精选

  • 调用 observe()会返回一个Observable对象

  • 调用这个 Observable对象的subscribe()方法完成事件注册,从而获取结果

(4)toObservable()

事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。调用 toObservable() 会返回一个cold  Observable,也就是说,调用 toObservable() 不会立即触发执行run()/construct(),必须有订阅者订阅 Observable 时才会执行。

如果继承的是 HystrixComman,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是 HystrixObservableCommand ,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。

toObservable()使用方法:

  • 调用observe()会返回一个Observable对象

  • 调用这个 Observable对象的subscribe()方法完成事件注册,从而获取结果

需注意的是, HystrixCommand也支持 toObservable()和observe(), 但是即使将 HystrixCommand 转换成Observable,它也只能发射一个值对象。只有 HystrixObservableCommand才支持发射多个值对象。

3、几种方法的关系


  • execute()实际是调用了queue().get()

  • queue()实际调用了toObservable().toBlocking().toFuture()

  • observe()实际调用toObservable()获得一个cold Observable,再创建一个ReplaySubject对象订阅Observable,将源Observable转化为hot Observable。因此调用observe()会自动触发执行run()/construct()。

Hystrix 总是以Observable的形式作为相应返回,不同执行命令的方法只是进行了相应的转换。

五、 Hystrix 容错

=============

Hystrix 的容错主要是通过添加容许延迟和容错方法,帮助控制这些分布式服务之间的交互。还通过隔离服务之间的访问点,阻止它们之间的级联故障以及提供退回选项来实现这一点,从而提高系统的整体弹性。 Hystrix主要提供了一下几种容错方法:

  • 资源隔离

  • 熔断

  • 降级

资源隔离主要指对线程的隔离。 Hystrix提供了两种线程隔离的方式:线程池和信号量。

1、线程隔离-线程池


Hystrix还通过命令模式对发送请求的对象和执行请求的对象进行解耦,将不同类型的业务请求封装为对应的命令请求。如订单服务查询商品,查询商品请求->商品command;商品服务查询库存,查询库存请求->库存command。并且为每个类型的command配置一个线程池,当第一次创建command时,根据配置创建一个线程池,并放入ConcurrentHashMap,如商品command:


final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();

...

if (!threadPools.containsKey(key)) {

    threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));

}

后续查询商品的请求创建command时,将会重用已创建的线程池。线程池隔离之后的服务依赖关系:

通过发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,使得请求线程可以快速失败,从而避免依赖问题扩散。

线程池隔离优点:

  • 保护应用程序以免受来自依赖故障的影响,指定依赖线程池饱和不会影响应用程序的其余部分。

  • 当引入新客户端lib时,即使发生问题,也是在lib中,并不会影响其他内容。

  • 当依赖从故障恢复正常时,应用程序会立即恢复正常的性能。

  • 当应用程序一些配置参数错误时,线程池的运行状况会很快检测到这一点(通过增加错误、延迟、超时、拒绝等),同时可以通过动态属性进行实时纠正错误的参数配置。

  • 如果服务的性能有变化,需要实时调整,比如增加或减少超时时间,更改重试次数,可以通过线程池指标状态属性修改,而且不会影响到其它调用请求。

  • 除了隔离优势外, Hystrix 拥有专门的线程可提供内置的并发功能,使得可以在同步调用之上构建异步门面(外观模式),为异步编程提供了支持( Hystrix 引入了R小Java异步框架)。

注意:尽管线程池提供了线程隔离,我们的客户端底层代码也必须要有超时设置或响应线程中断,不能无限制的阻塞以致线程池一直饱和。

缺点:

线程池的主要缺点是增加了计算开销。每个命令的执行都在单独的线程完成,增加了排队、调度和上下文切换的开销。因此,要使用 Hystrix ,就必须接受它带来的开销,以换取它所提供的的好处。

通常情况下,线程池引入的开销足够小,不会有重大的成本和性能影响。但对于一些访问延迟极低的服务,如只依赖内存缓存,线程池引入的开销就比较明显了,这时候使用线程池隔离技术就不合适了,我们需要考虑更轻量级的方式,如信号量隔离。

2、线程隔离-信号量


上面提到了线程池隔离的缺点,当依赖延迟极低的服务时,线程池隔离技术引入的开销超过了它所带来的好处。这时候可以使用信号量隔离技术来代替,通过设置信号量来限制对任何给定依赖的并发调用量。下图说明了线程池隔离和信号量隔离的主要区别:

使用线程池时,发送请求的线程和执行依赖服务的线程不是同一个,而使用信号量时,发送请求的线程和执行依赖服务的线程时同一个, 都是发起请求的线程。

3、线程隔离总结


线程池和信号量都可以做线程隔离,但各有各的优缺点和支持的场景,对比如下:

线程切换支持异步支持超时支持熔断限流开销
信号量
线程池

线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务, Hystrix会通过响应中断的方式通知线程立即结束并返回。

六、熔断器

=====

现实生活中,可能大家都有注意到家庭电路中通常会安装一个保险盒,当负载过载时,保险盒中的保险丝会自动熔断,以保护电路及家里的各种电器,这就是熔断器的一个常见例子。Hystrix中的熔断器(Circuit Breaker)也是起类似作用,Hystrix在运行过程中会向每个commandKey对应的熔断器报告成功、失败、超时和拒绝的状态,熔断器维护并统计这些数据,并根据这些统计信息来决策熔断开关是否打开。如果打开,熔断后续请求,快速返回。隔一段时间(默认是5s)之后熔断器尝试半开,放入一部分流量请求进来,相当于对依赖服务进行一次健康检查,如果请求成功,熔断器关闭。

熔断器配置,Circuit Breaker主要包括如下6个参数:


1、circuitBreaker.enabled

是否启用熔断器,默认是TRUE。

2 、circuitBreaker.forceOpen

熔断器强制打开,始终保持打开状态,不关注熔断开关的实际状态。默认值FLASE。

3、circuitBreaker.forceClosed

熔断器强制关闭,始终保持关闭状态,不关注熔断开关的实际状态。默认值FLASE。

4、circuitBreaker.errorThresholdPercentage

错误率,默认值50%,例如一段时间(10s)内有100个请求,其中有54个超时或者异常,那么这段时间内的错误率是54%,大于了默认值50%,这种情况下会触发熔断器打开。

5、circuitBreaker.requestVolumeThreshold

默认值20。含义是一段时间内至少有20个请求才进行errorThresholdPercentage计算。比如一段时间了有19个请求,且这些请求全部失败了,错误率是100%,但熔断器不会打开,总请求数不满足20。

6、circuitBreaker.sleepWindowInMilliseconds

半开状态试探睡眠时间,默认值5000ms。如:当熔断器开启5000ms之后,会尝试放过去一部分流量进行试探,确定依赖服务是否恢复。

七、熔断器工作原理

=========

下图展示了HystrixCircuitBreaker的工作原理:

熔断器工作的详细过程如下:


第一步,调用 allowRequest() 判断是否允许将请求提交到线程池

1、允许熔断器强制打开, circuitBreaker.forceOpen为true,不允许放行,返回。

2、如果熔断器强制关闭, circuitBreaker.forceOpen为true,允许放行。 此外不必关注熔断器实际状态,也就是说熔断器仍然会维护统计数据和开关状态,只是不生效而已。

第二步,调用isOpen()判断熔断器开关是否打开

1、 如果熔断器开关打开,进入第三步,否则继续;

2、 如果一个周期内总的请求数小于circuitBreaker.requestVolumeThreshold的值,允许请求放行,否则继续;

3、 如果一个周期内错误率小于circuitBreaker.errorThresholdPercentage的值,允许请求放行。否则,打开熔断器开关,进入第三步。

第三步, 调用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复

如果熔断器打开,且距离熔断器打开的时间或上一次试探请求放行的时间超过circuitBreaker.sleepWindowInMilliseconds的值时,熔断器器进入半开状态,允许放行一个试探请求;否则,不允许放行。

此外,为了提供决策依据,每个熔断默认维护了10个bucket,每秒一个bucket,当心的bucket被创建时,最旧的bucket会被抛弃。其中每个bucket维护了请求、失败、超时、拒绝的计数器,Hystrix负责收集并统计这些计数器。

八、回退降级

======

降级,通常指事务高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。 Hystrix提供的降级主要是为了容错,保证当前服务不受依赖服务故障的影响,从而提高服务的健壮性。要支持回退或降级处理,可以重写 HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法。

1、Hystrix在以下几种情况下会走降级逻辑


  • 执行construct()或run()抛出异常

  • 熔断器打开导致命令短路

  • 命令的线程池和队列或信号量的容量超额,命令被拒绝

  • 命令执行超时

2、降级回退方式


(1)Fail Fast快速失败

快速失败是最普通的命令执行方法,命令没有重写降级逻辑。 如果命令执行发生任何类型的故障,它将直接抛出异常。

(2)Fail Fast无声失败

指在降级方法中通过返回null,空Map,空List或其他类似的响应来完成。

最后

小编精心为大家准备了一手资料

**点击这里免费领取**以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术

【附】架构书籍赠送

  1. BAT面试的20道高频数据库问题解析
  2. Java面试宝典
  3. Netty实战
  4. 算法

BATJ面试要点及Java架构师进阶资料

497325)]

[外链图片转存中…(img-4vuHdIpU-1628389497326)]

**点击这里免费领取**以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术

【附】架构书籍赠送

  1. BAT面试的20道高频数据库问题解析
  2. Java面试宝典
  3. Netty实战
  4. 算法

[外链图片转存中…(img-TJ7uzCdE-1628389497327)]

BATJ面试要点及Java架构师进阶资料

[外链图片转存中…(img-NQjmroi1-1628389497329)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值