1. 程式人生 > >服務容錯保護(Spring Cloud Hystrix)之請求合併

服務容錯保護(Spring Cloud Hystrix)之請求合併

最近在看springCoud微服務實戰,因此做一些看書筆記吧,都是書上例子,方便理解。

微服務架構中依賴遠端呼叫實現服務之間的通訊,所以必然會考慮到通訊消耗與連線數。所以容錯保護提供了HystrixCollapser來實現請求合併。

原始碼在這裡,有空自己看吧。

下面通過例子來幫助自己理解,記下省的以後忘了。

首先:定義一個實體類User,不多說了。

再次:service 和Service的實現類

import com.ribbon.User;

import java.util.List;

/**
 * Created by qhe on 2018/7/27.
 */
public interface UserService {

    public User find(Long id);

    public List<User> findAll(List<Long> ids);
}
package com.ribbon.user;

import com.ribbon.User;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * Created by qhe on 2018/7/27.
 */
public class UserServiceImpl implements UserService {

    @Autowired
    private RestTemplate restTemplate;


    @Override
    public User find(Long id) {
        return restTemplate.getForObject
                ("http//user-service/users/{1}",User.class,id);
    }

    @Override
    public List<User> findAll(List<Long> ids) {
        return restTemplate.getForObject
                ("http//user-service/users?ids={1}",List.class, StringUtils.join(ids,","));
    }
}


這裡定義了兩個介面,一個是單個請求,第二個是批量請求,並且用restTemplate實現了遠端呼叫。

然後第一步開始為請求合併實現一個批量請求

package com.ribbon.user;

import com.netflix.hystrix.HystrixCommand;
import com.ribbon.User;

import java.util.List;

import static com.netflix.hystrix.HystrixCommandGroupKey.Factory.asKey;

/**
 * Created by qhe on 2018/7/27.
 */
public class UserBatchCommand extends HystrixCommand<List<User>> {

    UserService userService;
    List<Long> userIds;

    public UserBatchCommand(UserService userService,List<Long> userIds){
        super(Setter.withGroupKey(asKey("userBatchCommand")));
        this.userIds = userIds;
        this.userService = userService;
    }

    @Override
    protected List<User> run() throws Exception {
        return userService.findAll(userIds);
    }
}

這裡實際上就是一個簡單的HystrixCommand實現。

第二步開始實現合併器

package com.ribbon.user;

import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCollapserKey;
import com.netflix.hystrix.HystrixCollapserProperties;
import com.netflix.hystrix.HystrixCommand;
import com.ribbon.User;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by qhe on 2018/7/27.
 */
public class UserCollapseCommand extends HystrixCollapser<List<User>,User,Long> {

    private UserService userService;

    private Long userId;

    public UserCollapseCommand(UserService userService,Long userId){
        super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userBatchCommand")).andCollapserPropertiesDefaults(
                HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)//設定時間延遲屬性
        ));
        this.userService = userService;
        this.userId = userId;
    }

    @Override
    public Long getRequestArgument() {
        return userId;//返回給定的單個請求引數
    }

    /**
     *collection引數中儲存了延遲時間窗中收集到的所有獲取單個User的請求。
     * 通過獲取這些請求的引數來組織上面我們準備的批量請求命令UserBatchCommand例項
     * @param collection
     * @return
     */
    @Override
    protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collection) {
        List<Long> userIds = new ArrayList<>(collection.size());
        userIds.addAll(collection.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));
        return new UserBatchCommand(userService,userIds);
    }

    /**
     * 在批量請求命令UserBatchCommand例項被觸發執行完成之後,該方法開始執行,
     * 其中users中儲存了creatCommand方法中組織的批量請求命令的返回結果,
     * 而collection引數則代表了每個被合併的請求,
     * 在這裡我們通過遍歷批量結果users物件,為collection中每個合併前的單個請求設定返回結果,
     * 完成批量結果到單個請求結果的轉換
     * @param users
     * @param collection
     */
    @Override
    protected void mapResponseToRequests(List<User> users, Collection<CollapsedRequest<User, Long>> collection) {
        int count = 0;
        for (CollapsedRequest<User,Long> collapsedRequest:collection){
            User user = users.get(count++);
            collapsedRequest.setResponse(user);
        }
    }
}

通過合併器,我們可以把多個相同請求合併成一個請求,而這些服務消費者並不用做什麼知道什麼。

上面是通過繼承方式來實現合併,下面通過註解形式來實現,spring的幾乎所有功能都有程式碼實現和註解實現。

package com.ribbon.user;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.ribbon.User;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * Created by qhe on 2018/7/27.
 */
@Service
public class UserServiceA {


    @Autowired
    RestTemplate restTemplate;

    @HystrixCollapser(batchMethod = "findAll",collapserProperties = {
            @HystrixProperty(name="timerDelayInMilliseconds",value = "100")})
    public User find(Long id){return null;}


    @HystrixCommand
    public List<User> findAll(List<Long> ids){
        return restTemplate.getForObject("http://user-service/user?ids={1}",List.class, StringUtils.join(ids,","));
    }