ios13.5.1降级_高并发系统下的降级如何实现

ab7a399091d129e8c394648f332a81a7.png

前言

服务的降级主要是指当服务负载过高或者出现故障时,将一些非核心业务(负载过高)或者故障业务进行移除或者暂不处理的措施,为其它业务空余出处理资源或者无障碍处理。一般来说在一个系统里面服务降级的流程包含服务接入层、服务逻辑处理层、数据访问层和网络传输层。由于服务接入层一般都有Nginx进行控制,例如流量桶控制,一般称为接入层的限流,这里不做介绍,重点讲一下剩下几个层的降级处理方式。

服务层降级

在大中型分布式系统中,通常系统很多依赖(HTTP,Dubbo等),如果一个应用不能对来自依赖的故障进行有效处理,那该应用本身就处在被拖垮的风险中。在一个高流量的网站中,某个单一的后端一旦发生延迟,将会在数秒内导致所有应用资源被耗尽。

例如:一个依赖30个服务的系统,每个服务99.99%可用。

此系统的失败率为1-99.99%的30次方 ≈ 0.3%

意味着一亿次请求 会有 3,000,00次失败

随着服务依赖数量的变多,服务不稳定的概率会成指数性提高.

这些服务依赖之间的稳定性保障就需要服务层的降级功能,这里介绍基于Hystrix的一个典型场景的降级使用。例如一个商品详情服务它需要调用商品、价格及商品评论三个服务,当一个服务出现故障时,它们没有使用降级服务的演变过程如下:

e0614306006b5855cc4504d883f049d3.png

商品详情需要调用三个服务,如果其中某一个服务由于异常无法响应或者响应延迟,就导致了商品详情连带雪崩。

此时就可以使用Hystrix来进行隔离或者熔断降级处理,它的隔离模式是通过线程隔离,以上面的业务流程为例:

f377f889fd85525585282438cfbcdfff.png

Hystrix通过将每个依赖服务分配独立的线程池进行资源隔离, 从而避免服务雪崩。

如上图所示, 当商品评论服务不可用时, 即使商品服务独立分配的20个线程全部处于同步等待状态,也不会影响其他依赖服务的调用。

Hystrix的熔断器模式流程如下:

b31ebe7a6998f947447485e41fa8ba84.png

熔断器模式定义了熔断器开关相互转换的逻辑:

服务的健康状况 = 请求失败数 / 请求总数.

熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的.

当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.

当熔断器开关打开时, 请求被禁止通过.

当熔断器开关处于打开状态, 经过一段时间后, 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过.

熔断器的开关能保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待. 并且熔断器能在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能.

我们基于HystrixCommand来介绍上面商品详情的一个实现逻辑:

public class CommentServiceHystrixCommand extends HystrixCommand<Response> {
  private CommentService service;
  private Request request;

  public Service1HystrixCommand(CommentService service, Request request){
    supper(
      Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ServiceGroup"))
          .andCommandKey(HystrixCommandKey.Factory.asKey("CommentServicequery"))
          .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("CommentServiceThreadPool"))
          .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
            .withCoreSize(20))//服务线程池数量
          .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
            .withCircuitBreakerErrorThresholdPercentage(60)//熔断器关闭到打开阈值
            .withCircuitBreakerSleepWindowInMilliseconds(3000)//熔断器打开到关闭的时间窗长度
      ))
      this.service = service;
      this.request = request;
    );
  }
  //业务正常访问
  @Override
  protected Response run(){
    return service.call(request);
  }
  //业务降级返回
  @Override
  protected Response getFallback(){
    return Response.dummy();
  }
}

其它几个模块类似,使用了Command模式构建了服务对象之后, 服务便拥有了熔断器和线程池的功能。整个服务的框架如下如所示:

b1e439560531048a7d099a1104eccb99.png

隔离及熔断流程如下:

1. 构建Hystrix的Command对象, 调用执行方法.

2. Hystrix检查当前服务的熔断器开关是否开启, 若开启, 则执行降级服务getFallback方法.

3. 若熔断器开关关闭, 则Hystrix检查当前服务的线程池是否能接收新的请求, 若超过线程池已满, 则执行降级服务getFallback方法.

4. 若线程池接受请求, 则Hystrix开始执行服务调用具体逻辑run方法.

5. 若服务执行失败, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.

6. 若服务执行超时, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.

7. 若服务执行成功, 返回正常结果.

8. 若服务降级方法getFallback执行成功, 则返回降级结果.

若服务降级方法getFallback执行失败, 则抛出异常.

数据层降级

数据层降级一般是指数据的读写降级,例如数据库迁移或者变更场景以及读写DB压力过大的临时处理场景都会用到此服务,我们仍然以商品详情页面来进行分析,假设商品的评论表进行迁移,在实时过程中不希望有新的数据写入,到时仍然希望保障用户的使用连贯性,对于评论仍然可以正常读取查看。

这个实现可以通过配置中心动态配置实现,逻辑处理层依据配置动态调整逻辑分支,例如降级后所有的写操作就走另外一个分支返回“系统维护中,暂无法处理”的提示,但是此模式就需要所有业务逻辑基础上添加一层降级处理,对业务侵入很大。那么怎么处理呢?这里介绍一种基于spring的热加载方案来实现动态切换,Spring的HotSwappableTargetSource,它可以实时动态的修改业务实现Bean,这样我们只需要保障之前的业务Bean不变,重新实现一个降级Bean即可,假设评论如下接口:

public interface CommentService {
  public get(String id);
  public write(String id, String comment)
}

正常评论的业务实现如下:

public CommentServiceImpl implements CommentService {
  public get(String id){
	//获取评论信息
  }
  public write(String id, String comment){
    //写入评论信息
  }
}

降级评论的业务实现如下:

public CommentServiceDegradationImpl implements CommentService {
  public get(String id){
	//获取评论信息
  }
  public write(String id, String comment){
    throw new DegradationException("系统维护中,暂无法处理");
  }
}

接下来配置HotSwappableTargetSource,如下:

 <bean id="CommentService"
		class="org.frame.base.annotation.support.EhCacheFactoryBean" init-method="init"> 
		<property name="targetSource">
			<ref bean = "targetSource"/>
		</property> 
	</bean> 
	<!-- 默认配置 -->
	<bean id="targetSource" class="org.springframework.aop.target.HotSwappableTargetSource" autowire="constructor" >
	<constructor-arg>
		<ref bean="commentServiceImpl"/>
	</constructor-arg>
	<bean id="commentServiceImpl"
		class="com.test.CommentServiceImpl" /> 
	<bean id="commentServiceDegradationImpl "
		class="com.test.CommentServiceDegradationImpl " /> 
	</bean>

当需要降级的时候,通过配置中心感知到降级指令,再如下swap修改即可切换到降级service:

HotSwappableTargetSource targetSource = (HotSwappableTargetSource) ctx
				.getBean("targetSource");
		CommentService commentService = (CommentService) ctx.getBean("commentServiceDegradationImpl");
		targetSource.swap(commentService);

网络层降级

网络层降级这里主要是指多机房间专线的流量降级,多机房间专线是核心资源,几乎业务的所有数据都有可能会通过专线进行传输,实现数据的双向或者单向的流转,那么当带宽出现波峰的时候就自然需要将某些不重要的业务进行降级或者流量控制,它的通用架构图如下:

dc76b3037c38d6e3646bbc7cf69cbb2a.png

它分为两种,一种是完全暂停服务,不再占用带宽,另外一种是通过令牌桶或者流量桶进行流控服务,降低带宽使用。

降级服务可通过配置中心实时下发指令,直接停止或者限制数据同步服务即可。

降低带宽可使用令牌桶或者流量桶,详细可参看本公众号的高并发系统下的限流方案如何实现。

d7570a51e33b920ee329d5860cc628d2.png

欢迎移步搜索关注公众号:互联网架构师之路(hlw_architector),获取最新架构材料,阅读公众号原文。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值