Hystrix请求合并
请求合并就是consumer反复的请求provider同一个接口,只是传的参数不一样,为了提高HTTP的通信效率,把它连着调用的很多个接口合成一个请求发出去,然后在provider上处理,处理完成后,返回一个一个结果就ok了,但是这不是所有的请求都能合并,这是有要求的,比如说,请求的时间里的特别近。服务端的接口也有要求,返回的数据要支持多个请求的,一般是list集合。
比如说,服务端有一个根据id查询书的接口,传两个id去查询同一本书,或者两个不一样的id,查询两本不一样的书,然后返回来,这时候服务端本来是getBookById() 应该返回一个Book对象,但是要做请求合并的话,就不能返回Book对象,要返回一个List集合里面放了一个Book,因为这个请求结果可能有多个,拿到List集合之后再在consumer中自动的把list集合拆开,发到几个不同的请求里面去,然后那个请求就拿到了它的响应结果了,服务端返回的是list集合,客户端拿到的是Book对象。
下面写一个简单的例子:
首先在provider中的BookController中定义如下方法:
/*一会儿在那边调用的时候,那个方法名叫,getBookById,就传一个Long类型的数据进来就行了
请求合并的话,我可以把所有请求的参数收集起来,然后拼接成一个参数,主要格式就是自己来自定义
我假设这里把参数的格式拼成: 1,2,3 ,4,5......
* */
@GetMapping("/book/{ids}")
public List<Book> getBookByIds(@PathVariable String ids){
System.out.println("ids>>>>>>>>>>"+ids);
String[] split=ids.split(",");
List<Book> books=new ArrayList<>();
for (String s : split) {
Book book = new Book();
book.setId(Long.parseLong(s));
books.add(book);
}
return books;
}
在consumer的BookService中添加如下代码:
public List<Book> getBookByIds(List<Long> ids){
Book[] books = restTemplateTwo.getForObject("http://provider/book/{1}", Book[].class, StringUtils.join(ids, ","));
return Arrays.asList(books);
}
在consumer中定义一个BookBachCommand
public class BookBatchCommand extends HystrixCommand<List<Book>> {
BookSerivce bookSerivce;
List<Long> ids;
public BookBatchCommand(BookSerivce bookSerivce,List<Long> ids) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("CollapsingGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("CollapsingKey")));
this.bookSerivce = bookSerivce;
}
@Override
protected List<Book> run() throws Exception {
return bookSerivce.getBookByIds(ids);
}
}
然后在consumer中创建一个BookCollapseCommand 来调用BookBachCommand
public class BookCollapseCommand extends HystrixCollapser<List<Book>,Book,Long> {
BookSerivce bookSerivce;
Long id;
public BookCollapseCommand(BookSerivce bookSerivce) {
super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("bookCollapseCommand")).andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));
//withTimerDelayInMilliseconds(100) 等待一百毫秒,如果有请求来就一起走,没有就自己走
this.bookSerivce = bookSerivce;
}
@Override
public Long getRequestArgument() {
return id;
}
@Override
protected HystrixCommand<List<Book>> createCommand(Collection<CollapsedRequest<Book, Long>> collection) {
//ids就是合并后的请求的参数,collection其实就是待合并的请求
List<Long> ids=new ArrayList<>((collection.size()));
for (CollapsedRequest<Book, Long> request : collection) {
//把请求的参数一个个放到ids中
ids.add(request.getArgument());
}
return new BookBatchCommand(bookSerivce,ids);
}
@Override
protected void mapResponseToRequests(List<Book> books, Collection<CollapsedRequest<Book, Long>> collection) {
//第一个参数请求之后从provider上面拿过来的结果,第二个参数就是一个个的请求
int count=0;
for (CollapsedRequest<Book, Long> request : collection) {
//把集合中的数据挨个取出来,分别设置给 request
request.setResponse(books.get(count++));
}
}
}
然后再在BookController中创建一个方法用于测试:
@GetMapping("/test6")
public void test6() throws ExecutionException, InterruptedException {
HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
BookCollapseCommand bc1 = new BookCollapseCommand(bookSerivce, 99L);
BookCollapseCommand bc2 = new BookCollapseCommand(bookSerivce, 99L);
BookCollapseCommand bc3= new BookCollapseCommand(bookSerivce, 99L);
BookCollapseCommand bc4 = new BookCollapseCommand(bookSerivce, 99L);
//注意一定要先入队等待其他请求
Future<Book> q1=bc1.queue();
Future<Book> q2=bc2.queue();
Future<Book> q3=bc3.queue();
Future<Book> q4=bc4.queue();
Book b1 = q1.get();
Book b2 = q2.get();
Book b3 = q3.get();
Book b4 = q4.get();
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);
System.out.println(b4);
ctx.close();
}
运行结果如下:
在provider中只调用了一次,而consumer中的四个请求各自拿到了他们想要的数据,这就是请求合并,他们的前提是这几个请求离得非常近。如果像如下那样就不行:
会发两个请求,意味着第四个跟不上,单独发: