(十六)java版spring cloud微服務架構b2b2c電子商務平臺-Hystrix請求合併

一、請求合併適用的場景

在服務提供者提供了返回單個對象和多個對象的查詢接口,並且單個對象的查詢併發數很高,服務提供者負載較高的時候,我們就可以使用請求合併來降低服務提供者的負載。
實現請求合併
1、傳統方式
首先在服務提供者的GetRequestController中添加兩個接口,用於打印是哪個方法被調用

    /**     * 爲Hystrix請求合併提供的接口     */
    @GetMapping("/users/{id}")    public User getUserById(@PathVariable Long id){
        logger.info("=========getUserById方法:入參ids:"+id);        return new User("one"+id, "女", "110-"+id);
    }    @GetMapping("/users")    public List<User> getUsersByIds(@RequestParam("ids") List<Long> ids){
        List<User> userList = new ArrayList<>();
        User user;
        logger.info("=========getUsersByIds方法:入參ids:"+ids);        for(Long id : ids){
            user = new User("person"+id ,"男","123-"+id);
            userList.add(user);
        }
        System.out.println(userList);        return userList;
    }

在消費者(RibbonConsumHystrix)項目中的RibbonController中實現簡單的調用上邊的兩個接口

    /**     * 單個請求處理     * @param id     */
    @GetMapping("/users/{id}")    public User findOne(@PathVariable Long id){
        LOGGER.debug("=============/hystrix/users/{} 執行了", id);
        User user = service.findOne(id);        return user;
    }    /**     * 多個請求處理     * @param ids id串,使用逗號分隔     */
    @GetMapping("/users")    public List<User> findAll(@RequestParam List<Long> ids){
        LOGGER.debug("=============/hystrix/users?ids={} 執行了", ids);        return service.findAll(ids);
    }

擴充RibbonService,添加兩個方法,分別調用上述兩個接口,主要是爲了分層明確

    /**請求合併使用到的測試方法**/

    /**     * 查一個User對象     */
    public User findOne(Long id){
        LOGGER.info("findOne方法執行了,id= "+id);        return restTemplate.getForObject("http://eureka-service/users/{1}", User.class, id);
    }    /**     * 查多個對象     *     * 注意: 這裏用的是數組,作爲結果的接收,因爲restTemplate.getForObject方法在這裏受限     *         如果盡如《SpringCloud微服務實戰》一書中指定類型爲List.class,會返回一個List<LinkedHashMap>類型的集合     *         爲了避坑這裏我們使用數組的方式接收結果     */
    public List<User> findAll(List<Long> ids){
        LOGGER.info("findAll方法執行了,ids= "+ids);
        User[] users = restTemplate.getForObject("http://eureka-service/users?ids={1}", User[].class, StringUtils.join(ids, ","));        return Arrays.asList(users);
    }

我們還需要一個將請求合併的類,在hystrix包下創建UserCollapseCommand

package com.cnblogs.hellxz.hystrix;import com.cnblogs.hellxz.entity.User;import com.cnblogs.hellxz.servcie.RibbonService;import com.netflix.hystrix.HystrixCollapser;import com.netflix.hystrix.HystrixCollapserProperties;import com.netflix.hystrix.HystrixCommand;import java.util.ArrayList;import java.util.Collection;import java.util.List;import java.util.stream.Collectors;//注意這個asKey方法不是HystrixCommandKey.Factory.asKeyimport static com.netflix.hystrix.HystrixCollapserKey.Factory.asKey;/** * @Author : Hellxz * @Description: 繼承HystrixCollapser的請求合併器 * @Date : 2018/5/5 11:42 */public class UserCollapseCommand extends HystrixCollapser<List<User>,User,Long> {    private RibbonService service;    private Long userId;    /**     * 構造方法,主要用來設置這個合併器的時間,意爲每多少毫秒就會合並一次     * @param ribbonService 調用的服務     * @param userId 單個請求傳入的參數     */
    public UserCollapseCommand(RibbonService ribbonService, Long userId){        super(Setter.withCollapserKey(asKey("userCollapseCommand")).andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));        this.service = ribbonService;        this.userId = userId;
    }    /**     * 獲取請求中的參數     */
    @Override
    public Long getRequestArgument() {        return userId;
    }    /**     * 創建命令,執行批量操作     */
    @Override
    public HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collapsedRequests) {        //按請求數聲名UserId的集合
        List<Long> userIds = new ArrayList<>(collapsedRequests.size());        //通過請求將100毫秒中的請求參數取出來裝進集合中
        userIds.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));        //返回UserBatchCommand對象,自動執行UserBatchCommand的run方法
        return new UserBatchCommand(service, userIds);
    }    /**     * 將返回的結果匹配回請求中     * @param batchResponse 批量操作的結果     * @param collapsedRequests 合在一起的請求     */
    @Override
    protected void mapResponseToRequests(List<User> batchResponse, Collection<CollapsedRequest<User, Long>> collapsedRequests) {        int count = 0 ;        for(CollapsedRequest<User,Long> collapsedRequest : collapsedRequests){            //從批響應集合中按順序取出結果
            User user = batchResponse.get(count++);            //將結果放回原Request的響應體內
            collapsedRequest.setResponse(user);
        }
    }
}

其中將多個參數封裝成一個List,將參數交給UserBatchCommand類執行

創建測試接口:
在這裏用了類比方法,分別是同步方法和異步方法

    /**     * 合併請求測試     * 說明:這個測試本應在findOne方法中new一個UserCollapseCommand對象進行測試     *         苦於沒有好的辦法做併發實驗,這裏就放在一個Controller中了     *         我們看到,在這個方法中用了三個UserCollapseCommand對象進行模擬高併發     */
    @GetMapping("/collapse")    public List<User> collapseTest(){
        LOGGER.info("==========>collapseTest方法執行了");
        List<User> userList = new ArrayList<>();
        Future<User> queue1 = new UserCollapseCommand(service, 1L).queue();
        Future<User> queue2 = new UserCollapseCommand(service, 2L).queue();
        Future<User> queue3 = new UserCollapseCommand(service, 3L).queue();        try {
            User user1 = queue1.get();
            User user2 = queue2.get();
            User user3 = queue3.get();
            userList.add(user1);
            userList.add(user2);
            userList.add(user3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }        return userList;
    }    /**     * 同步方法測試合併請求     *     * 說明:這個方法是用來與上面的方法做類比的,通過這個實驗我們發現如果使用同步方法,     *         那麼這個請求合併的作用就沒有了,這會給findAll方法造成性能浪費     */
    @GetMapping("synccollapse")    public List<User> syncCollapseTest(){
        LOGGER.info("==========>syncCollapseTest方法執行了");
        List<User> userList = new ArrayList<>();
        User user1 = new UserCollapseCommand(service, 1L).execute();
        User user2 = new UserCollapseCommand(service, 2L).execute();
        User user3 = new UserCollapseCommand(service, 3L).execute();
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);        return userList;
    }

2、註解方式
擴充RibbonService

    /**註解方式實現請求合併**/

    /**     * 被合併請求的方法     * 注意是timerDelayInMilliseconds,注意拼寫     */
    @HystrixCollapser(batchMethod = "findAllByAnnotation",collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds",value = "100")})    public Future<User> findOneByAnnotation(Long id){        //你會發現根本不會進入這個方法體
        LOGGER.info("findOne方法執行了,ids= "+id);        return null;
    }    /**     * 真正執行的方法     */
    @HystrixCommand
    public List<User> findAllByAnnotation(List<Long> ids){
        LOGGER.info("findAll方法執行了,ids= "+ids);
        User[] users = restTemplate.getForObject("http://eureka-service/users?ids={1}", User[].class, StringUtils.join(ids, ","));        return Arrays.asList(users);
    }

擴充RibbonController調用findOneByAnnotation()

      @GetMapping("/collapsebyannotation/{id}")    public User collapseByAnnotation(@PathVariable Long id) throws ExecutionException, InterruptedException {
        Future<User> one = service.findOneByAnnotation(id);
        User user = one.get();      
          return user;
    } 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章