1. 程式人生 > >springcloud系列—Hystrix—第3章-4: Hystrix 請求合併

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中寫好之後,直接呼叫就可以了.