實現一個分散式排程系統-LoadBalance和Ha策略

w39發表於2021-09-09
上一篇主要介紹了基於zookeeper的服務註冊實現:

對於我們的系統還需要一些策略,比如worker機器的選擇策略,請求失敗的策略,讓我們的系統更加的可靠。

客戶端負載均衡

對於loadbalance,主要是實現幾個方法,讓我們的manager客戶端進行worker機器進行合適的選擇。比如輪訓,隨機,或者權重。

具體實現

0-工具方法
 private int getNextNonNegative() {
        return MathUtil.getNonNegative(idx.getAndIncrement());
 }
public static int getNonNegative(int originValue){
        //透過二進位制位操作將originValue轉化為非負數
        return 0x7fffffff & originValue;
}
1-隨機
  @Override
    public Endpoint doSelect(RequestPacket request) {

        List<Endpoint> endpoints = getEndpoints();

        int idx = (int) (ThreadLocalRandom.current().nextDouble() * endpoints.size());
        for (int i = 0; i < endpoints.size(); i++) {
            Endpoint ref = endpoints.get((i + idx) % endpoints.size());
            //TODO 判斷是否存活
            return ref;
        }
        return null;
    }

2-輪訓
  @Override
    public Endpoint doSelect(RequestPacket request) {
        List<Endpoint> endpoints = getEndpoints();
        int index = getNextNonNegative();
        for (int i = 0; i < endpoints.size(); i++) {
            Endpoint ref = endpoints.get((i + index) % endpoints.size());
            return ref;
        }
        return null;
    }

3-低併發度優先: referer的某時刻的call數越小優先順序越高
 低併發referer獲取策略:
 由於Referer List可能很多,比如上百臺,如果每次都要從這上百個Referer或者最低並  發的幾個,效能有些損耗,因此 random.nextInt(list.size()) 獲取一個起始index,然後獲取最多不超過MAX_REFERER_COUNT的狀態是isAvailable的referer進行判斷activeCount.
 protected Endpoint doSelect(RequestPacket request) {
        List<Endpoint> endpoints = getEndpoints();

        int refererSize = endpoints.size();
        int startIndex = ThreadLocalRandom.current().nextInt(refererSize);
        int currentCursor = 0;
        int currentAvailableCursor = 0;
        Endpoint endpoint = null;
        while (currentAvailableCursor < MAX_REFERER_COUNT && currentCursor < refererSize) {
            Endpoint temp = endpoints.get((startIndex + currentCursor) % refererSize);
            currentCursor++;
            currentAvailableCursor++;
            if (endpoint == null) {
                endpoint = temp;
            } else {
                if (compare(endpoint, temp) > 0) {
                    endpoint = temp;
                }
            }
        }

        return endpoint;
    }

4 ......更多

Ha策略

這裡就簡單實現兩種:
1- FailfastHaStrategy  快速失敗
我們透過loadBalance選擇一個worker機器地址,然後發起請求,如果出現錯誤就立刻報錯失敗。
2- FailoverHaStrategy  會帶有重試功能
我們透過loadBalance會選擇一組worker機器,如果第一個失敗,會輪訓呼叫後面的機器,直到成功,或者全部失敗

實現

1-FailfastHaStrategy  

    //快速失敗策略
    public ResponsePacket call(RequestPacket request, LoadBalance loadBalance) {
        //獲取執行的服務
        Endpoint endpoint = loadBalance.select(request);
        log.info("{}FailfastHaStrategy start to call {},request:{}", Constants.LOG_PREFIX, endpoint.getHost(), request.toString());

        //獲取nettyClient  傳送RPC請求

        return request(endpoint, request);
    }


2-FailoverHaStrategy  
public class FailoverHaStrategy extends AbstractHaStrategy {

    protected ThreadLocal<List<Endpoint>> endpointHolder = new ThreadLocal<List<Endpoint>>() {
        @Override
        protected java.util.List<Endpoint> initialValue() {
            return new ArrayList<Endpoint>();
        }
    };

    public ResponsePacket call(RequestPacket request, LoadBalance loadBalance) {

        //根據規則獲取一組endpoint
        List<Endpoint> endpointList = selectReferers(request, loadBalance);
        if (endpointList.isEmpty()) {
            throw new CommonException(999999, String.format("FailoverHaStrategy No Endpoint loadbalance:%s", loadBalance));
        }
        int tryCount = request.getRetries();//獲取使用者配置的重試次數
        // 如果有問題,則設定為不重試
        if (tryCount < 0) {
            tryCount = 0;
        }
        for (int i = 0; i <= tryCount; i++) {
            Endpoint endpoint = endpointList.get(i % endpointList.size());
            log.info("{}FailoverHaStrategy start to call ......{},tryCount:{},request:{}", Constants.LOG_PREFIX, endpoint.getHost(), (i + 1), request.toString());
            try {
                //獲取nettyClient  傳送RPC請求
                return request(endpoint, request);

            } catch (RuntimeException e) {
                // 對於業務異常,直接丟擲,不進行重試
                if (e instanceof CommonException) {
                    throw e;
                } else if (i >= tryCount) {
                    log.info("{}tryCount is over......throw e", Constants.LOG_PREFIX);
                    throw e;
                }
                log.info("{}try run ,tryCount:{}", Constants.LOG_PREFIX, (i + 1));
            }
        }
        throw new CommonException(999999, "FailoverHaStrategy.call should not come here!");
    }

    protected List<Endpoint> selectReferers(RequestPacket request, LoadBalance loadBalance) {
        List<Endpoint> endpoints = endpointHolder.get();
        endpoints.clear();
        loadBalance.selectToHolder(request, endpoints);
        return endpoints;
    }

}

總結

後續我們會繼續介紹一些核心功能,比如代理的實現和transport層的實現
完整程式碼見github: 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/854/viewspace-2820356/,如需轉載,請註明出處,否則將追究法律責任。

相關文章