zoukankan      html  css  js  c++  java
  • 服务隔离与降级

    1.什么是服务隔离
    简单来说,当系统某个服务或组件出现故障时,能隔离这些故障并且能做到优雅地服务降级。
    比如,在图片分享应用中,当出现故障时,用户可能无法上传图片,但他们依然能浏览、编辑和分享已上传的图片。

    2.为什么做服务隔离
    我们在做系统设计的时候,必须有一个清楚的认知是:任何软件系统,故障是不可避免的,并且大多数还是不可预测的,因此,我们只能在系统的设计之初就充分的考虑好应对措施,如何在故障发生时,去尽最大可能的止损和减少故障范围。 把系统分离成子服务,将子服务进行一定程度隔离的做法,能保证在有不可预测的故障发生时,缩小故障范围的最佳手段。

    上图一个典型的分布式服务实现, 当大多数人在使用Tomcat时,多个HTTP服务会共享一个线程池,假设其中一个HTTP服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个Tomcat线程池都被该服务占用,甚至拖垮整个Tomcat。因此,如果我们能把不同HTTP服务隔离到不同的线程池,则某个HTTP服务的线程池满了也不会对其他服务造成灾难性故障.

    上面只是个简单的例子,在大中型分布式系统中,通常系统很多依赖(HTTP,hession,Netty,Dubbo等),在高并发访问下,这些依赖的稳定性与否对系统的影响非常大,但是依赖有很多不可控问题:如网络连接缓慢,资源繁忙,暂时不可用,服务脱机等。
    当依赖阻塞时,大多数服务器的线程池就出现阻塞(BLOCK),影响整个线上服务的稳定性,在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败。高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险,最终导致系统崩溃,又称雪崩效应;

    解决上诉雪崩效应,一般有3种方案;
    (1)熔断模式:这种模式主要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,防止火灾。放到我们的系统中,如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

    (2)隔离模式:这种模式就像对系统请求按类型划分成一个个小岛的一样,当某个小岛被火少光了,不会影响到其他的小岛。例如可以对不同类型的请求使用线程池来资源隔离,每种类型的请求互不影响,如果一种类型的请求线程资源耗尽,则对后续的该类型请求直接返回,不再调用后续资源。这种模式使用场景非常多,例如将一个服务拆开,对于重要的服务使用单独服务器来部署,再或者公司最近推广的多中心。

    (3)限流模式:上述的熔断模式和隔离模式都属于出错后的容错处理机制,而限流模式则可以称为预防模式。限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。这种模式不能解决服务依赖的问题,只能解决系统整体资源分配问题,因为没有被限流的请求依然有可能造成雪崩效应。

    1.怎么做服务隔离

    服务隔离是一个很大的概念,具体到实现上,有方方面面;下面先谈谈隔离的维度上有哪些;

    1.线程隔离

    线程隔离主要有线程池隔离,在实际使用时我们会把请求分类,然后交给不同的线程池处理,当一种业务的请求处理发生问题时,不会将故障扩散到其他线程池,从而保证其他服务可用。

    2.进程隔离
    在公司发展初期,一般是先进行从0到1,不会一上来就进行系统的拆分,这样就会开发出一些比较大而全的系统,系统中的一个模块/功能出现问题,整个系统就不可用了。首先想到的解决方案是通过部署多个实例,然后通过负载均衡进行路由转发,但是这种情况无法避免某个模块因BUG而出现如OOM导致整个系统不可用的风险。因此此种方案只是一个过渡,较好的解决方案是通过将系统拆分为多个子系统来实现物理隔离。通过进程隔离使得某一个子系统出现问题不会影响到其他子系统。

    3.集群隔离
    随着系统的发展,单实例服务无法满足需求了,此时需要服务化技术,通过部署多个服务,形成服务集群来提升系统容量,如下图所示

    随着调用方的增多,当秒杀服务被刷会影响到其他服务的稳定性,此时应该考虑为秒杀提供单独的服务集群,即为服务分组,从而当某一个分组出现问题不会影响到其他分组,从而实现了故障隔离,如下图所示

    4.机房隔离
    随着对系统可用性的要求,会进行多机房部署,每个机房的服务都有自己的服务分组,本机房的服务应该只调用本机房服务,不进行跨机房调用;其中一个机房服务发生问题时可以通过DNS/负载均衡将请求全部切到另一个机房;或者考虑服务能自动重试其他机房的服务从而提升系统可用性。

    5.读写隔离
    通过主从模式将读和写集群分离,读服务只从从Redis集群获取数据,当主Redis集群出现问题时,从Redis集群还是可用的,从而不影响用户访问;而当从Redis集群出现问题时可以进行其他集群的重试。

    6.动静隔离
    当用户访问如结算页时,如果JS/CSS等静态资源也在结算页系统中时,很可能因为访问量太大导致带宽被打满导致出现不可用。因此应该将动态内容和静态资源分离,一般应该将静态资源放在CDN上

    方案实现,

    1.先在系统设计上,做到服务拆分, 让每个小业务成为孤岛,即便是其中一个业务挂掉,其他服务依然可以正常运行。
    2.服务隔离目前业内应用比较广泛和成熟的的方案是Hystrix,
    hystrix是netflix开源的一个容灾框架,解决当外部依赖故障时拖垮业务系统、甚至引起雪崩的问题

    Hystrix的服务隔离主要有两种,常用的是线程隔离,对热点接口建立单独的线程池避免对主程序的干扰 ,比如 Tomcat默认是一个线程池150个线程,如果单个热点接口的请求过多,就会造成其他功能没有线程可用甚至直接程序崩溃的问题

    另外一种是信号量,应用场景不是太多,两者的区别 其实就是一个增大系统的开销,一个则直接限制了线程总的并发数,开销更小一些;

    Hystrix基本使用方法,参考
    https://www.imooc.com/article/75502
    http://blog.51cto.com/developerycj/1950881

    有了服务隔离后,谈谈服务熔断

    服务熔断的三个模块,熔断请求判断算法、熔断恢复机制、熔断报警;
    对于Hystrix的实现
    (1)熔断请求判断机制算法:使用无锁循环队列计数,每个熔断器默认维护10个bucket,每1秒一个bucket,每个blucket记录请求的成功、失败、超时、拒绝的状态,默认错误超过50%且10秒内超过20个请求进行中断拦截。
    (2)熔断恢复:对于被熔断的请求,每隔5s允许部分请求通过,若请求都是健康的(RT<250ms)则对请求健康恢复。
    (3)熔断报警:对于熔断的请求打日志,异常请求超过某些设定则报警

    熔断之后,就要对服务进行降级处理了;所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。 也可以理解为兜底;
    这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强

    方案1:dubbo降级

    Dubbo开发中,可能由于服务没有启动或者网络不通,调用中会出现RpcException,也就是远程调用失败。如果是服务启动顺序的问题,可能加上check=”false”的配置可以得到很好的解决.但是,如果是服务宕掉或者并发数太高导致的RpcException该如何处理?

    查看dubbo的官方文档,可以发现有个mock的配置,mock只在出现非业务异常(比如超时,网络异常等)时执行。mock的配置支持两种,一种为boolean值,默认的为false。如果配置为true,则缺省使用mock类名,即类名+Mock后缀;另外一种则是配置”return null”,可以很简单的忽略掉异常,示例如下,当接口超时异常后,会自动返回null降级;

    <dubbo:reference id="iUser" interface="com.dubbosample.iface.IUser" timeout="10000" check="false" mock="return null">

    check=true,时, 需要自定义一个接口名+Mock的类名的降级接口,此时如果调用失败会调用Mock实现

    方案2: hystrix降级

    引入Hystrix依赖后,通过在需要降级的方法上加注解实现降级
    @HystrixCommand(
    fallbackMethod = "fallback",
    threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "20"), @HystrixProperty(name = "maxQueueSize", value = "100"), @HystrixProperty(name = "queueSizeRejectionThreshold", value = "20")},
    commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "30000"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20")

    其中
    fallbackMethod指降级后的兜底方法,特别注意fallback方法的返参和入参必须和该方法相同;

  • 相关阅读:
    利用CSS计数函数counter()实现计数
    弹跳加载动画特效Bouncing loader
    HTML页面中解决内容元素随窗口变化布局变乱问题
    CSS中列表项list样式
    框模型中设置内容区域元素占地尺寸box-sizing属性
    PHP100视频教程-->视频下载
    HTML页面中5种超酷的伪类选择器:hover效果
    HTML中获取input中单选按钮radio数据(性别例子)
    HTML中 DOM操作的Document 对象详解(收藏)
    14、输入一个链表,从尾到头打印链表每个节点的值。
  • 原文地址:https://www.cnblogs.com/canda/p/9669362.html
Copyright © 2011-2022 走看看