当前位置: 首页 > 科技观察

隔离使用Hystrix实现隔离

时间:2023-03-20 16:55:53 科技观察

一、Hystrix简介Hystrix是Netflix开源的一个分布式系统的延迟和容错库。目的是隔离分布式服务故障。提供线程和信号量隔离,减少不同服务之间因资源竞争造成的相互影响;提供优雅的降级机制;提供熔断机制,让服务可以快速失败,而不是阻塞等待服务响应,并可以快速从中恢复。Hystrix使用这些机制来防止级联故障并确保系统弹性和可用性。下图是一个典型的分布式服务实现。首先,大多数人在使用Tomcat的时候,多个HTTP服务会共享一个线程池。假设其中一个HTTP服务访问的数据库响应很慢,这会增加服务响应时间延迟,大部分线程会阻塞等待数据响应返回,导致整个Tomcat线程池被服务占用,甚至打倒整个Tomcat。因此,如果我们能够将不同的HTTP服务隔离到不同的线程池中,一个HTTP服务的满线程池不会对其他服务造成灾难性的故障。这就需要线程隔离或者信号量隔离来实现。使用线程隔离或信号隔离的目的是为不同的服务分配一定的资源。当自己的资源用完后,直接返回失败,不会占用别人的资源。同样,如果“HTTP服务1”和“HTTP服务2”分别需要访问远程的“分布式服务A”和“分布式服务B”,假设它们共享线程池,那么其中一个服务也会影响到另一个服务,所以我们需要访问隔离,可以通过Hystrix的线程池隔离或者信号量隔离来实现。其次,“分布式服务B”依赖于“分布式服务D”和“分布式服务E”,其中“分布式服务D”是一个可降级的服务,也就是说当发生故障时(比如超时,网络故障)你可以暂时阻止或返回缓存的脏数据。例如,在访问商品详情页时,可以暂时屏蔽上面的商家信息,而不影响用户的下单流程。当我们依赖的服务访问超时时,我们需要提供降级策略。例如,返回支持数据以防止级联故障。当服务因为某些故障(比如网络故障)导致可用性下降时,必须能够及时熔断,一是快速故障,二是能够保护远程分布式服务。至此我们对Hystrix用来解决什么问题有了一个大概的了解。限制调用分布式服务的资源使用。被调用服务的问题不会影响其他服务调用。它是通过线程池隔离和信号量隔离来实现的。Hystrix提供了优雅的降级机制:超时降级,资源不足(线程或信号量)时降级,降级后可以配合降级接口返回backingdata。Hystrix还提供了熔断器实现。当故障率达到阈值时,自动触发降级(如网络故障/超时故障率高),熔断器触发的快速故障会快速恢复。它还提供请求缓存和请求合并实现。接下来,我们就来看看如何使用Hystrix。本书使用的版本是Hystrix-1.5.6。2、隔离示例以线程池隔离为例,不同的服务会设置不同的线程池,实现相互隔离。为不同的HTTP服务设置不同的线程池,为不同的分布式服务调用设置不同的线程池。假设我们现在要调用一个股票服务,通过封装一个命令GetStockServiceCommand来实现。publicclassGetStockServiceCommandextendsHystrixCommand{privateStockServicestockService;publicGetStockServiceCommand(){super(setter());}privatestaticSettersetter(){//服务分组HystrixCommandGroupKeygroupKey=HystrixCommandGroupKey.Factory.asKey("stock");//服务标志HystrixCommandKeyFixstrixCommandH.KeycommandasKey("getStock");//线程池名称HystrixThreadPoolKeythreadPoolKey=HystrixThreadPoolKey.Factory.asKey("stock-pool");//线程池配置HystrixThreadPoolProperties.SetterthreadPoolProperties=HystrixThreadPoolProperties.SetterthreadPool.RereadSreadSize()0.withKeepAliveTimeMinutes(5).withMaxQueueSize(Integer.MAX_VALUE).withQueueSizeRejectionThreshold(10000);//命令属性配置HystrixCommandProperties.SettercommandProperties=HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);returnHystrixCommand.Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey).andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);}@OverrideprotectedStringrun()throwsException{returnstockService.getStock();}}几个重要的组件如下全局唯一服务标识HystrixCommandGroupKey:configuration组的名称,例如inventorysystem就是一个服务组。我们在监控的时候,会把同组的服务聚合在一起,这是必选项。HystrixCommandKey:配置全局唯一标识服务的名称。例如,如果库存系统有一个库存服务,你可以给这个服务一个名字来唯一标识这个服务。如果没有配置,默认是一个简单的类名。HystrixThreadPoolKey:配置全局唯一标识线程池的名称。具有相同线程池名称的线程池是相同的。如果未配置,则默认为组名。这个名字也是线程池中线程名的前缀。HystrixThreadPoolProperties:配置线程池参数,coreSize配置核心线程池大小和线程池最大大小,keepAliveTimeMinutes是线程池中空闲线程的生存时间(如果不动态配置则没有作用),maxQueueSize配置线程poolqueue***Size,queueSizeRejectionThreshold限制当前队列大小,即实际队列大小由该参数决定,通过改变queueSizeRejectionThreshold可以实现队列大小的动态调整。HystrixCommandProperties:配置命令的一些参数,比如executionIsolationStrategy配置执行隔离策略,默认是使用线程隔离,这里我们配置为THREAD,即线程池隔离。在这里,隔离可以在粗粒度级别或细粒度级别实现,如下所示。服务组+线程池:粗粒度实现,一个服务组/系统可以配置一个隔离的线程池,不配置线程池名称或配置同组线程池名称相同。服务组+服务+线程池:细粒度实现,一个服务组中的每个服务都配置一个隔离的线程池,不同的命令实现可以配置不同的线程池名称。混合实现:为一个服务组配置一个隔离线程池,然后为重要的服务单独设置隔离线程池。以上配置是在应用启动时配置的。在实际运行过程中,我们可能会随时调整一些参数,比如线程池的大小,队列的大小。这时候可以采用以下方法进行动态配置。StringdynamicQueueSizeRejectionThreshold="hystrix.threadpool."+"stock-pool"+".queueSizeRejectionThreshold";Configurationconfiguration=ConfigurationManager.getConfigInstance();configuration.setProperty(dynamicQueueSizeRejectionThreshold,100);如果改变了线程池配置,就是"hystrix.threadpool."+threadPoolKey+propertyName;如果是改变command属性配置,就是“hystrix.command”。+命令键+属性名。接下来,您可以创建如下命令。GetStockServiceCommandcommand=newGetStockServiceCommand(newStockService());然后如下同步调用。Stringresult=command.execute();或者返回Future实现异步调用。Futurefuture=command.queue();或者配合RxJava实现响应式编程。Observableobserve=command.observe();observe.asObservable().subscribe((result)->{System.out.println(result);});在应用Hystrix时,首先需要将服务封装成HystrixCommand,即command方式的实现,然后可以通过同步/异步/响应方式调用服务。信号量隔离可以配置如下。HystrixCommandProperties.SettercommandProperties=HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE).withExecutionIsolationSemaphoreMaxConcurrentRequests(50);信号量隔离只限制并发线程同步的总数。调用服务使用main因此,如果只是想限制服务的并发调用总数或者被调用的服务不涉及远程调用,可以使用轻量级信号量来实现。GetStockServiceCommand不是单例,不能重复使用。每次使用都必须创建一个。如果觉得Hystrix太笨重,可以参考Hystrix的思路设计自己的组件。【本文为专栏作者张凯涛原创文章,作者微信公众号:凯涛博客(kaitao-1234567)】点此阅读更多本作者好文