隔离术之使用 Hystrix 实现隔离

标签: dev | 发表时间:2017-04-02 08:00 | 作者:
出处:http://itindex.net/admin/pagedetail

本篇摘自《亿级流量网站架构核心技术》第三章 隔离术 部分内容,全文。


相关阅读

聊聊高并发之隔离术

商品详情页系统的Servlet3异步化实践


1 Hystrix简介

Hystrix是Netflix开源的一款针对分布式系统的延迟和容错库,目的是用来隔离分布式服务故障。它提供线程和信号量隔离,以减少不同服务之间资源竞争带来的相互影响;提供优雅降级机制;提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。Hystrix通过这些机制来阻止级联失败并保证系统弹性、可用。下图是一个典型的分布式服务实现。


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


使用线程隔离或信号隔离的目的是为不同的服务分配一定的资源,当自己的资源用完,直接返回失败而不是占用别人的资源。


同理,如“HTTP服务1”和“HTTP服务2”要分别访问远程的“分布式服务A”和“分布式服务B”,假设它们共享线程池,那么其中一个服务在出现问题时也会影响到另一个服务,因此,我们需要进行访问隔离,可以通过Hystrix的线程池隔离或信号量隔离来实现。


其次,“分布式服务B”依赖了“分布式服务D”和“分布式服务E”,其中“分布式服务D”是一个可降级的服务,意思是出现故障时(如超时、网络故障)可以暂时屏蔽掉或者返回缓存脏数据,如访问商品详情页时,可以暂时屏蔽掉上边的商家信息,不会影响用户下单流程。


当我们依赖的服务访问超时时,要提供降级策略。比如,返回托底数据阻止级联故障。当因为一些故障(如网络故障)使得服务可用率下降时,要能及时熔断,一是快速失败,二是可以保护远程分布式服务。


到此我们大体了解了Hystrix是用来解决什么问题的。

1.限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用,通过线程池隔离和信号量隔离实现。

2.Hystrix提供了优雅降级机制:超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。

3.Hystrix也提供了熔断器实现,当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复。

4.还提供了请求缓存、请求合并实现。

接下来,我们来看下如何使用Hystrix,本书使用的版本是Hystrix- 1.5.6。


隔离示例

以线程池隔离为示例,会为不同的服务设置不同的线程池,从而实现相互隔离。

为不同的HTTP服务设置不同的线程池,为不同的分布式服务调用设置不同的线程池。


假设我们现在要调用一个获取库存服务,通过封装一个命令GetStockServiceCommand来实现。

public class GetStockServiceCommand extends HystrixCommand<String> {

    private StockService stockService;

    public GetStockServiceCommand() {

        super(setter());

    }

    private static Setter setter() {

        //服务分组

        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory. asKey("stock");

        //服务标识

        HystrixCommandKey commandKey =HystrixCommandKey.Factory. asKey("getStock");

        //线程池名称

        HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory. asKey("stock-pool");

        //线程池配置

        HystrixThreadPoolProperties.Setter threadPoolProperties =HystrixThreadPoolProperties.Setter threadPoolProperties =HystrixThreadPoolProperties.Setter()

               .withCoreSize(10)

               .withKeepAliveTimeMinutes(5)

               .withMaxQueueSize(Integer.MAX_VALUE)

               .withQueueSizeRejectionThreshold(10000);

 

        //命令属性配置

        HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()

               .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);

        return HystrixCommand.Setter

                        .withGroupKey(groupKey)

                       .andCommandKey(commandKey)

                       .andThreadPoolKey(threadPoolKey)

                       .andThreadPoolPropertiesDefaults(threadPoolProperties)

                        .andCommandPropertiesDefaults(commandProperties);

    }

   @Override

    protectedString run() throws Exception {

        return stockService.getStock();

    }

}

几个重要组件如下。

HystrixCommandGroupKey:配置全局唯一标识服务分组的名称,比如,库存系统就是一个服务分组。当我们监控时,相同分组的服务会聚合在一起,必填选项。

HystrixCommandKey:配置全局唯一标识服务的名称,比如,库存系统有一个获取库存服务,那么就可以为这个服务起一个名字来唯一识别该服务,如果不配置,则默认是简单类名。

HystrixThreadPoolKey:配置全局唯一标识线程池的名称,相同线程池名称的线程池是同一个,如果不配置,则默认是分组名,此名字也是线程池中线程名字的前缀。

HystrixThreadPoolProperties:配置线程池参数,coreSize配置核心线程池大小和线程池最大大小,keepAliveTimeMinutes是线程池中空闲线程生存时间(如果不进行动态配置,那么是没有任何作用的),maxQueueSize配置线程池队列最大大小,queueSizeRejectionThreshold限定当前队列大小,即实际队列大小由这个参数决定,通过改变queueSizeRejectionThreshold可以实现动态队列大小调整。

HystrixCommandProperties:配置该命令的一些参数,如executionIsolationStrategy配置执行隔离策略,默认是使用线程隔离,此处我们配置为THREAD,即线程池隔离。


此处可以粗粒度实现隔离,也可以细粒度实现隔离,如下所示。

服务分组+线程池:粗粒度实现,一个服务分组/系统配置一个隔离线程池即可,不配置线程池名称或者相同分组的线程池名称配置为一样。

服务分组+服务+线程池:细粒度实现,一个服务分组中的每一个服务配置一个隔离线程池,为不同的命令实现配置不同的线程池名称即可。

混合实现:一个服务分组配置一个隔离线程池,然后对重要服务单独设置隔离线程池。


如上配置是在应用启动时就配置好了,在实际运行过程中,我们可能随时调整其中一些参数,如线程池大小、队列大小,此时,可以使用如下方式进行动态配置。

String dynamicQueueSizeRejectionThreshold = "hystrix.threadpool."+ "stock-pool" + ".queueSizeRejectionThreshold";

Configuration configuration = ConfigurationManager.getConfigInstance();

configuration.setProperty(dynamicQueueSizeRejectionThreshold,100);


如果是改变线程池配置,则是"hystrix.threadpool."+ threadPoolKey + propertyName;如果是改变命令属性配置,则是"hystrix.command." + commandKey + propertyName。


接下来就可以通过如下方式创建命令。

GetStockServiceCommand command = new GetStockServiceCommand(newStockService());


然后通过如下方式同步调用。

String result = command.execute();


或者返回Future从而实现异步调用。

Future<String> future = command.queue();


或者配合RxJava实现响应式编程。

Observable<String> observe =command.observe();

observe.asObservable().subscribe((result) -> {

    System.out.println(result);

});


在应用Hystrix时,首先需要把服务封装成HystrixCommand,即命令模式实现,然后就可以通过同步/异步/响应式模式来调用服务。


信号量隔离通过如下配置即可。

HystrixCommandProperties.Setter commandProperties= HystrixCommandProperties.Setter()          .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)

       .withExecutionIsolationSemaphoreMaxConcurrentRequests(50);

信号量隔离只是限制了总的并发数,服务使用主线程进行同步调用,即没有线程池。因此,如果只是想限制某个服务的总并发调用量或者调用的服务不涉及远程调用的话,可以使用轻量级的信号量来实现。


GetStockServiceCommand不是单例,不能重用,必须每次使用创建一个。如果觉得Hystrix太麻烦或者太重,则可以参考Hystrix思路设计自己的组件。



新书预售地址,请长按二维码预定。



相关 [隔离 hystrix 隔离] 推荐:

隔离术之使用 Hystrix 实现隔离

- - IT瘾-dev
本篇摘自《亿级流量网站架构核心技术》第三章 隔离术 部分内容,全文. 商品详情页系统的Servlet3异步化实践. Hystrix是Netflix开源的一款针对分布式系统的延迟和容错库,目的是用来隔离分布式服务故障. 它提供线程和信号量隔离,以减少不同服务之间资源竞争带来的相互影响;提供优雅降级机制;提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复.

事务隔离和传播

- - 数据库 - ITeye博客
- Spring中事务的Propagation(传播性)的取值.        -- 加入当前已有事务;只有当前没有事务才起一个新的事务.        比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务.

Docker是如何实现隔离的

- - 掘金后端
容器化技术在当前云计算、微服务等体系下大行其道,而 Docker 便是容器化技术的典型,对于容器化典型的技术,我们有必要弄懂它,所以这篇文章,我会来分析下 Docker 是如何实现隔离技术的,Docker 与虚拟机又有哪些区别呢. 接下来,我们开始逐渐揭开它的面纱. 我们开始运行一个简单的容器,这里以 busybox镜像为例,它是一个常用的Linux工具箱,可以用来执行很多Linux命令,我们以它为镜像启动容器方便来查看容器内部环境.

ClickHouse 权限控制与资源隔离

- - IT瘾-dev
使用clickhouse多半应用在实时数仓项目来支持adhoc查询,为了确保企业数据安全高效的使用,那么权限控制与资源隔离是必不可少的. clickhouse在20.4之后的版本开始支持基于RBAC的访问控制管理;主要包括的功能有:用户创建、角色创建、权限管理以及资源隔离;接下来我们将演示如何使用这些功能.

使用VitrualEnvWrapper隔离python项目的库依赖

- jeff - Jeff的妙想奇境
VirtualEnv用于在一台机器上创建多个独立的python运行环境,VirtualEnvWrapper为前者提供了一些便利的命令行上的封装. - 隔离项目之间的第三方包依赖,如A项目依赖django1.2.5,B项目依赖django1.3. - 为部署应用提供方便,把开发环境的虚拟环境打包到生产环境即可,不需要在服务器上再折腾一翻.

数据库隔离级别导致的问题

- - Java - 编程语言 - ITeye博客
java代码在开始事务后,先做了一个查询,再insert,此时会报: ERROR JDBCExceptionReporter:78 - Could not retrieve transation read-only status server. 问题解决过程: 查看mysql的事物隔离级别.                                            返回结果: REPEATABLE-READ.

MySQL数据库事务隔离级别(Transaction Isolation Level)

- - RSS - IT博客云
修改事务隔离级别的方法:. 1.全局修改,修改 mysql.ini配置文件,在最后加上. 1 #可选参数有: READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE. 这里全局默认是 REPEATABLE-READ,其实MySQL本来默认也是这个级别.

spring-cloud中zuul的两种隔离机制实验

- - ImportNew
ZuulException REJECTED_SEMAPHORE_EXECUTION 是一个最近在性能测试中经常遇到的异常. 查询资料发现是因为zuul默认每个路由直接用信号量做隔离,并且默认值是100,也就是当一个路由请求的信号量高于100那么就拒绝服务了,返回500. 既然默认值太小,那么就在gateway的配置提高各个路由的信号量再实验.

聊聊 Nacos 配置隔离和分类的使用

- - IT瘾-dev
最近在使用Nacos来作为配置中心和注册中心,在使用的过程难免会有些问题. 有的是框架问题,有的是使用方式的问题,不久前也分享了一篇 《最近使用Nacos的一些问题》,感兴趣的可以看看. 今天要聊的话题也是在使用过程中发现的,主要是前期赶进度太忙了,停下来之后才有时间去整理,去思考更优的方式. 环境隔离是最基本的一个需求,在日常开发过程中,常需要不同的环境,比如开发,测试,预发,线上环境.

MySQL 数据库事务隔离性的实现

- - 掘金 后端
​​​​摘要: 事实上在数据库引擎的实现中并不能实现完全的事务隔离,比如串行化. 本文分享自华为云社区 《【数据库事务与锁机制】- 事务隔离的实现》,原文作者:技术火炬手. 事实上在数据库引擎的实现中并不能实现完全的事务隔离,比如串行化. 这种事务隔离方式虽然是比较理想的隔离措施,但是会对并发性能产生比较大的影响,所以在 MySQL 中事务的默认隔离级别是 REPEATABLE READS(可重复读),下面我们展开讨论一下 MySQL 对数据库隔离性的实现.