用到命令模式,将对依赖服务的请求封装成两个命令对象,实现行为请求者与消费者解耦
两种命令对象为:HystrixCommand和HystrixObservableCommand
HystrixCommand:用在依赖的服务返回单个操作结果,有两个方法execute(同步)、queue(异步)
HystrixObservableCommand:用在依赖的服务返回多个操作结果,有两个方法observe(返回hot Observable对象),toObservable(返回coldObservable对象)
Observable对象是对依赖服务调用返回结果的封装
Observable对象是Rxjava的核心,理解为被观察者-订阅者,有两种 hot和cold
hot Observable:无论被观察者是否有订阅者,对象都会在被创建后发布出去,所以订阅者可能是中途进来,只看到事件局部
cold Observable:只有有订阅者,对象才会发布出去,所以订阅者肯定看到事件全部
HystrixCircuitBreaker:断路器
定义了三个断路器抽象方法
allowRequest:每个Hystrix命令都通过他判断是否被执行
isOpen:返回当前断路器是否打开
markSuccess:用来闭合断路器
还有三个静态类
Factory:维护一个Hystrix命令和HystrixCircuitBreaker的map
NoOpcircuitBreaker定义一个什么都不做的断路器,它允许所有请求,断路器始终闭合
HystrixCircuitBreakerImpl:HystrixCircuitBreaker的实现类
定义了四个核心属性:
public static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
private final HystrixCommandProperties properties;//断路器对应HystrixCommand实例的属性对象
private final HystrixCommandMetrics metrics;//记录各类度量指标
private final AtomicReference<HystrixCircuitBreakerImpl.Status> status;
private final AtomicLong circuitOpened;//断路器是否打开标识
private final AtomicReference<Subscription> activeSubscription;
}
各个接口的实现
/***
* 先判断是否强制打开关闭,在判断是否打开
* @return
*/
public boolean isOpen() {
Boolean a = (Boolean)this.properties.circuitBreakerForceOpen().get();
Boolean b = (Boolean)this.properties.circuitBreakerForceClosed().get();
return a.booleanValue()?true:
(b.booleanValue()?false:this.circuitOpened.get() >= 0L);
}
/***
* 设置了一个断路器打开之后的休眠时间,在休眠时候到达之后,将再次允许请求尝试访问,
* 此时断路器处于半开状态,若请求继续失败,断路器又进入打开状态,并继续等待下一个休眠窗口,
* 若请求成功,则将断路器关闭
* @return
*/
public boolean allowRequest() {
return ((Boolean)this.properties.circuitBreakerForceOpen().get()).booleanValue()?false:(((Boolean)this.properties.circuitBreakerForceClosed().get()).booleanValue()?true:(this.circuitOpened.get() == -1L?true:(((HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status)this.status.get()).equals(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.HALF_OPEN)?false:this.isAfterSleepWindow())));
}
实现
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/movie/{id}")
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
@HystrixProperty(name = "execution.timeout.enabled", value = "false")},fallbackMethod = "findByIdFallback")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
/**
* fallback方法
* @param id
* @return
*/
public User findByIdFallback(Long id) {
User user = new User();
user.setId(5L);
return user;
}
}
依赖隔离
线程池隔离和信号量
异常处理
HystrixBadRequestException不会进入降级函数,也可以指定哪些异常不进入降级函数
在降级函数中可以获取具体异常
可以给命令设置命令组以及线程池组
开启缓存
请求合并
HsytrixCollapser实现了请求合并,将处于很短时间内(默认是10毫秒)对同一依赖服务的多个请求进行合并以批量方式发起请求的功能(服务提供方也需要提供相应的批量实现的接口)
/***
*
* @param <BatchReturnType> 合并后批量请求的返回类型
* @param <ResponseType> 单个请求的返回类型
* @param <RequestArgumentType> 请求参数类型
*/
public abstract class HystrixCollapser<BatchReturnType, ResponseType, RequestArgumentType> implements HystrixExecutable<ResponseType>, HystrixObservable<ResponseType> {
//定义三个抽象方法
/***
* 该函数用来定义获取请求参数的方法
* @return
*/
public abstract RequestArgumentType getRequestArgument();
/***
* 合并请求产生批量命令的具体实现方法
* @param var1
* @return
*/
protected abstract HystrixCommand<BatchReturnType> createCommand(
Collection<com.netflix.hystrix.HystrixCollapser.CollapsedRequest<ResponseType, RequestArgumentType>> var1);
/***
* 批量命令结构返回后的处理,这里需要实现将批量结果拆分并传递给合并前的各个原子请求命令的逻辑
* @param var1
* @param var2
*/
protected abstract void mapResponseToRequests(BatchReturnType var1,
Collection<com.netflix.hystrix.HystrixCollapser.CollapsedRequest<ResponseType, RequestArgumentType>> var2);
}
实现:
首先服务提供者提供两个服务:
@RequestMapping("/getbook6")
public List<Book> book6(String ids) {
System.out.println("ids>>>>>>>>>>>>>>>>>>>>>" + ids);
ArrayList<Book> books = new ArrayList<>();
books.add(new Book("《李自成》", 55, "姚雪垠", "人民文学出版社"));
books.add(new Book("中国文学简史", 33, "林庚", "清华大学出版社"));
books.add(new Book("文学改良刍议", 33, "胡适", "无"));
books.add(new Book("ids", 22, "helloworld", "haha"));
return books;
}
@RequestMapping("/getbook6/{id}")
public Book book61(@PathVariable Integer id) {
Book book = new Book("《李自成》2", 55, "姚雪垠2", "人民文学出版社2");
return book;
}
消费者:提供两个对上述两个服务的调用方法
public Book test8(Long id) {
return restTemplate.getForObject("http://HELLO-SERVICE/getbook6/{1}", Book.class, id);
}
public List<Book> test9(List<Long> ids) {
System.out.println("test9---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName());
Book[] books = restTemplate.getForObject("http://HELLO-SERVICE/getbook6?ids={1}", Book[].class, StringUtils.join(ids, ","));
return Arrays.asList(books);
}
创建批处理命令:实现了对test9的调用
public class BookBatchCommand extends HystrixCommand<List<Book>> {
private List<Long> ids;
private BookService bookService;
public BookBatchCommand(List<Long> ids, BookService bookService) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("CollapsingGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("CollapsingKey")));
this.ids = ids;
this.bookService = bookService;
}
@Override
protected List<Book> run() throws Exception {
return bookService.test9(ids);
}
}
创建批处理器
public class BookCollapseCommand extends HystrixCollapser<List<Book>, Book, Long> {
private BookService bookService;
private Long id;
public BookCollapseCommand(BookService bookService, Long id) {
super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("bookCollapseCommand")).andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));
this.bookService = bookService;
this.id = id;
}
@Override
public Long getRequestArgument() {
return id;
}
@Override
protected HystrixCommand<List<Book>> createCommand(Collection<CollapsedRequest<Book, Long>> collapsedRequests) {
List<Long> ids = new ArrayList<>(collapsedRequests.size());
ids.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));
BookBatchCommand bookBatchCommand = new BookBatchCommand(ids, bookService);
return bookBatchCommand;
}
@Override
protected void mapResponseToRequests(List<Book> batchResponse, Collection<CollapsedRequest<Book, Long>> collapsedRequests) {
System.out.println("mapResponseToRequests");
int count = 0;
for (CollapsedRequest<Book, Long> collapsedRequest : collapsedRequests) {
Book book = batchResponse.get(count++);
collapsedRequest.setResponse(book);
}
}
}
注解方式实现:在BookService中添加两个方法,如下:
@HystrixCollapser(batchMethod = "test11",collapserProperties = {@HystrixProperty(name ="timerDelayInMilliseconds",value = "100")})
public Future<Book> test10(Long id) {
return null;
}
@HystrixCommand
public List<Book> test11(List<Long> ids) {
System.out.println("test9---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName());
Book[] books = restTemplate.getForObject("http://HELLO-SERVICE/getbook6?ids={1}", Book[].class, StringUtils.join(ids, ","));
return Arrays.asList(books);
}
请求合并的额外开销:
并不是所有的请求都适合合并,1、依赖服务本身是个高延迟服务适合 2、短时间内的并发量大适合
配置详解
按照优先级低到高,配置分为四种:全局默认值、全局配置属性、实例默认值、实力配置属性
Command属性
command属性用来控制HystrixCommand命令的行为,主要分为5种类型:
execution配置、fallback配置、circuitBreaker配置、metrics配置、requestContext配置
execution配置
控制的是HystrixCommand.run()的执行
fallback配置
控制HystrixCommand.getFallback()的执行,同时适用线程池和信号量的隔离策略
circuitBreaker配置
断路器的属性配置,用来控制HystrixCircuitBreaker的行为
metrics配置
控制HystrixCommand和HystrixObservableCommand执行中捕获的指标信息
requestContext配置
对HystrixCommand使用的HystrixRequestContext的设置
Collapser属性
请求合并相关属性
threadPool属性
用来控制Hystrix命令所属线程池的配置
Hystrix仪表盘
前面介绍过HystrixCommand和HystrixObservableCommand在执行过程中会记录很多度量指标信息,这些信息除了给断路器用,还会给系统运维等使用,Hystrix仪表盘就是消费者之一。
开启仪表盘
1、新增项目
2、在启动类增加注释:@EnableHystrixDashboard
3、yml文件中指定端口
server.port=2001
4、访问:http://localhost:2001/hystrix
监控方式
支持三种监控方式:
1、默认的集群监控 2、指定的集群监控 3、单体应用的监控
单体应用监控
访问:http://hystrix-app:port/hystrix.stream
为服务实例添加/hystrix.stream端点
pom中增加依赖jar
确保开启@EnableCircuitBreaker
Turbine集群监控
端点为/turbine.stream
实现步骤
1、创建turbine项目
2、引入pom依赖jar
3、启动类开启注解:@EnableTurbine
4、yml加入相关配置: