1.Hystrix是什么?
在分布式系统中,每个服务都可能会调用很多其他的服务,被调用的那些服务就是依赖服务,依赖服务出现一些故障是很正常的,因为不能保障100%的可用性。hystrix可以让我们在分布式系统中对服务间的调用进行控制,加入一些调用延迟或者依赖故障的容错机制。Hystrix通过将依赖服务进行资源隔离,当某个依赖服务出现故障的时候,防止故障在整个系统的依赖服务中蔓延,同时Hystrix还提供了fallback降级机制。(Hystrix保护系统高可用主要通过一下几点:资源隔离、限流、熔断、限流、运维监控)。2.Hystrix的设计原则
1.对依赖服务调用时出现的延迟和失败进行容错保护。
2.在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延
4.提供fallback优雅的降级的支持
5.支持近实时的监控、报警一级运维操作
3.Hystrix是如何实现的?
1.通过hystrixCommand或者HystrixObservableCommand来封装对外部依赖的访问请求,这个访问请求一般会运行在独立的线程中。
2.对于超出我们设定的阈(yu)值服务调用,直接进行超时返回,不允许它长时间的阻塞。
3.对每一个依赖服务进行资源隔离。通过线程池或者是semaphore这两种方式。4.对依赖服务被调用的成功次数,失败次数,拒绝次数,超时次数进行统计。
5.如果对某一个依赖服务的调用失败次数超过了一点的阈值,Hystrix自动进行熔断,并在一段时间内对该服务的调用直接进行降级,一段时间后再自动尝试恢复
6.当对一个服务调用出现失败、被拒绝、超时、短路等异常情况时,自动调用fallback降级机制
7.对属性和配置的修改提供近实时的支持
4.在eclipse中导入Hystrix的依赖(基于Spring boot)
导入依赖及可,如果下载不了,可以去maven中央仓库下载( http://mvnrepository.com/artifact/com.netflix.hystrix/hystrix-core/1.5.12) <dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
服务接口故障高并发情况下服务资源耗尽的场景
1.假设服务部署在tomcat服务器上,线程资源有50个,上面有多个服务,假设其中一个服务故障了,10个线程去调用这个服务,阻塞在了那,或出现了很长的延迟,高并发情况下,大量请求的涌入,导致tomcat服务器将大量的线程都去调用这个服务,都阻塞在了那。这个时候,调用一些其他服务的接口,这个时候,你的缓存服务就没有足够的线程去调用了。解决方式:资源隔离
你调用服务的时候,不能在赤裸裸的直接调用了。应将服务接口调用的逻辑进行封装。Hystrix 进行资源隔离的方法,其实是提供了一个command的抽象,你将对一个依赖服务的所有调用请求,全部隔离在同一份资源池内,对这个依赖服务的所有调用请求,全部走这个资源池的资源。(有两种方式,一种是线程池,一种是信号量)。
线程池隔离技术:
对一个依赖服务,所有的调用请求,全部隔离到一个线程池内,对商品服务的每次调用请求都封装到一个command里面,每个command(每次服务调用请求)都是使用线程池内的一个线程去执行的。比如,对某个服务的请求并发量达到了100,线程池里面只有10个线程,那么最多只有这10个线程去执行。1.HystrixCommand。简单的一个demo,这时用来获取一条数据的
public String changeProduct(Long productId){
HystrixCommand<ProductInfo> productInfoCommand = new ProductInfoCommand(productId);
//当代码执行execute时,会尝试将productInfoCommand扔到ProductInfoCommandGroup(对应一个线程池)这个线程池中
//用线程池里面的一个线程去执行run方法。
ProductInfo productInfo =productInfoCommand.execute();
System.out.println(productInfo);
return "success";
}
package com.hystrix.command;
import com.alibaba.fastjson.JSONObject;
import com.hystrix.model.ProductInfo;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
public class ProductInfoCommand extends HystrixCommand<ProductInfo> {
private Long productId;
public ProductInfoCommand(Long productId){
super(HystrixCommandGroupKey.Factory.asKey("ProductInfoCommandGroup"));
this.productId=productId;
}
//这里应该根据productId去进行查询,不过为了简单,直接用JSON数据
@Override
protected ProductInfo run() throws Exception {
String productInfo = "{\"id\": " + productId + ", \"name\": \"iphone7手机\", \"price\": 5599, \"pictureList\":\"a.jpg,b.jpg\", \"specification\": \"iphone7的规格\", \"service\": \"iphone7的售后服务\", \"color\": \"红色,白色,黑色\", \"size\": \"5.5\", \"shopId\": 1, \"modifiedTime\": \"2017-01-01 12:00:00\"}";
return JSONObject.parseObject(productInfo,ProductInfo.class);F
}
}
2.HystrixObservableCommand:是设计用来获取多条数据的
@RequestMapping("/productInfoBatch")
@ResponseBody
public String productInfoBatch(String productIds){
HystrixObservableCommand<ProductInfo> productInfoBatchCommand =
new ProductInfoBatchCommand(productIds.split(","));
Observable<ProductInfo> observable=productInfoBatchCommand.observe();
observable.subscribe(new Observer<ProductInfo>(){
@Override
public void onCompleted() {
// 得到了所有要查询的商品的信息
}
@Override
public void onError(Throwable e) {
// TODO Auto-generated method stub
e.printStackTrace();
}
@Override
public void onNext(ProductInfo productInfo) {
// 接口回调
System.out.println(productInfo);
}
});
return "success";
}
package com.hystrix.command;
import com.alibaba.fastjson.JSONObject;
import com.hystrix.model.ProductInfo;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
public class ProductInfoCommand extends HystrixCommand<ProductInfo> {
private Long productId;
public ProductInfoCommand(Long productId){
super(HystrixCommandGroupKey.Factory.asKey("ProductInfoCommand"));
this.productId=productId;
}
@Override
protected ProductInfo run() throws Exception {
String productInfo = "{\"id\": " + productId + ", \"name\": \"iphone7手机\", \"price\": 5599, \"pictureList\":\"a.jpg,b.jpg\",
\"specification\": \"iphone7的规格\", \"service\": \"iphone7的售后服务\", \"color\": \"红色,白色,黑色\", \"size\": \"5.5\", \"shopId\": 1, \"modifiedTime\": \"2017-01-01 12:00:00\"}";
return JSONObject.parseObject(productInfo,ProductInfo.class);
}
}
1.command的四种调用方式
同步:new CommandCache(args).execute()注:如果你认为observable command只会返回一条数据,那么也可以调用上面的模式,去同步执行,返回一条数据
new ObservableCommandCache(args).toBlocking.toFuture().get()
异步:new CommandCache(args).queue()
对Command调用queue(),仅仅将command放入线程池的一个等待队列,就立即返回,拿到一个Future对象,后面可以执行其他的代码,然后过一段时间对future调用ger()方法获取数据,这个future就是JDK并发包里面的那个Future
Future future = ProductInfoCommand.queue();
new ObservableCommandCache(args).toBlocking.toFuture()
同理
立即执行:Observable<String> f = new CommandCache(args).observe()
立即执行construct方法去拿到所有的结果
延迟执行:Observable<String> f = new ObservableCommandCache(agrs).toObservable()
f.subscribe(new Observer<String>(){
public void onCompleted(){
}
public void onError(){
e.printStackTrace();
}
public void onNext(){
}
});
默认的线程池大小是10个,即使服务接口故障了,最多只有10个线程会hang死,而对其他的线程无影响
//下一篇再讲解信号量以及信号量和线程池实现资源隔离的区别