springcloud系列—Hystrix—第3章-4: Hystrix 請求合併
資料參考:《Spring Cloud 微服務實戰》
目錄
請求合併
在微服務架構中,我們將一個專案拆分成很多個獨立的模組,這些獨立的模組通過遠端呼叫來互相配合工作,但是,在高併發情況下,通訊次數的增加會導致總的通訊時間增加,同時,執行緒池的資源也是有限的,高併發環境會導致有大量的執行緒處於等待狀態,進而導致響應延遲,為了解決這些問題,我們需要來了解Hystrix的請求合併。
Hystrix中的請求合併,就是利用一個合併處理器,將對同一個服務發起的連續請求合併成一個請求進行處理(這些連續請求的時間窗預設為10ms),在這個過程中涉及到的一個核心類就是HystrixCollapser
服務提供者介面
我需在在服務提供者中提供兩個介面供服務消費者呼叫,如下:
@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; }
第一個介面是一個批處理介面,第二個介面是一個處理單個請求的介面。在批處理介面中,服務消費者傳來的ids引數格式是1,2,3,4…這種格式,正常情況下我們需要根據ids查詢到對應的資料,然後組裝成一個集合返回,我這裡為了處理方便,不管什麼樣的請求統統都返回一樣的資料集;處理單個請求的介面就比較簡單了,不再贅述。
服務消費者
服務提供者處理好之後,接下來我們來看看服務消費者要怎麼處理。
- BookService
首先在BookService中新增兩個方法用來呼叫服務提供者提供的介面,如下:
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); }
test8用來呼叫提供單個id的介面,test9用來呼叫批處理的介面,在test9中,我將test9執行時所處的執行緒打印出來,方便我們觀察執行結果,另外,在RestTemplate中,如果返回值是一個集合,我們得先用一個數組接收,然後再轉為集合
- BookBatchCommand
BookService中的方法準備好了後,我們就可以來建立一個BookBatchCommand,這是一個批處理命令,如下:
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);
}
}
繼承自HystrixCommand,用來處理合並之後的請求,在run方法中呼叫BookService中的test9方法。
BookCollapseCommand
接下來我們需要建立BookCollapseCommand繼承自HystrixCollapser來實現請求合併。如下:
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);
}
}
}
關於這個類,我說如下幾點:
1.首先在構造方法中,我們設定了請求時間窗為100ms,即請求時間間隔在100ms之內的請求會被合併為一個請求。
2.createCommand方法主要用來合併請求,在這裡獲取到各個單個請求的id,將這些單個的id放到一個集合中,然後再創建出一個BookBatchCommand物件,用該物件去發起一個批量請求。
3.mapResponseToRequests方法主要用來為每個請求設定請求結果。該方法的第一個引數batchResponse表示批處理請求的結果,第二個引數collapsedRequests則代表了每一個被合併的請求,然後我們通過遍歷batchResponse來為collapsedRequests設定請求結果。
合併請求和不合並的區別
通過註解實現請求合併
上面這種請求合併方式寫起來稍微有一點麻煩,我們可以使用註解來更優雅的實現這一功能。首先在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);
}
在test10方法上新增@HystrixCollapser註解實現請求合併,用batchMethod屬性指明請求合併後的處理方法,collapserProperties屬性指定其他屬性。
在BookService中寫好之後,直接呼叫就可以了.