手写通用类型负载均衡路由引擎(含随机、轮询、哈希等及其带权形式)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/alinyua/article/details/84105650

本文记录了通用类型负载均衡路由引擎(工厂)的实现过程和思路。通过路由引擎获取指定枚举类型的负载均衡器,降低了代码耦合,规范了各个负载均衡器的使用,减少出错的可能,并简化了其对应带权负载均衡的实现(提供默认实现),而无需另外编写和集成。

一 使用效果

    public static void main(String[] args){
        ServiceInfo serviceInfo1=new ServiceInfo("A",0,3);
        ServiceInfo serviceInfo2=new ServiceInfo("B",0,1);
        ServiceInfo serviceInfo3=new ServiceInfo("C",0,1);
		//原列表
        List<ServiceInfo> serviceInfoList= Lists.newArrayList(serviceInfo1,serviceInfo2,serviceInfo3);
		//通过路由引擎获取指定枚举类型负载均衡器(这里是轮询)
        RouteStrategy weightRouteStrategy= RouteEngine.queryClusterStrategy(RouteStrategyEnum.Polling);
		//使用 select 方法获取目标元素 (这里是轮询10次)
        for (int i = 0; i < 10; i++) {
            System.out.println(weightRouteStrategy.select(serviceInfoList));
        }
        System.out.println("--------------------------------");
        //注意:使用selectWithWeight的话ServiceInfo需实现 WeightGetAble 接口 (这里是带权轮询10次)
        for (int i = 0; i < 10; i++) {
            System.out.println(weightRouteStrategy.selectWithWeight(serviceInfoList));
        }
    }

结果如下,第一次使用轮询不考虑权重,则3个元素依次输出,第二次使用带权轮询,实现输出次数与权重比一致(3:1:1)
结果

二 总体结构

1 结构图

结构

2 组件介绍

  • RouteEngine(路由引擎)
    即负载均衡器工厂,通过该工厂获取指定类型负载均衡策略的实现(即负载均衡器)。
  • RouteStrategy(路由策略接口)
    即负载均衡策略的接口,定义了 select选择方法和 selectWithWeight 带权选择方法。其中selectWithWeight 提供默认实现并要求其参数列表元素需实现 WeightGetAble 接口。
  • RouteStrategyEnum(路由策略枚举类)
    即负载均衡策略枚举类,并使用数字code将其对应管理。
  • WeightGetAble(权重获取接口)
    使用带权负载均衡的对象所需实现的接口,负责提供权重因子(默认为1)。
  • WeightUtil(权重工具类)
    提供 将实现了WeightGetAble的元素列表转化为带权重列表 等方法。
  • impl(路由策略实现类)
    提供路由策略的具体实现

3 相互关系

RouteStrategy利用WeightUtil提供对WeightGetAble列表的默认路由实现,但仍依赖于impl实现类。

RouteStrategyEnum管理RouteStrategy实现类 与数字的对应关系,RouteEngine 管理 RouteStrategyEnum元素 与RouteStrategy实现类 的对应关系。

用户通过指定 数字 或 RouteStrategyEnum 向 RouteEngine 索取 RouteStrategy实现类。

三 结构实现

1 RouteStrategy

路由策略的接口。默认带权负载均衡算法是构造新列表取代原始列表。
selectWithWeight 方法会使用到 WeightUtil和WeightGetAble。

/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-14
 * @Description: 路由策略接口
 */
public interface RouteStrategy {

    /**
     * 负载策略算法
     *
     * @param primeList 原始列表
     * @return
     */
    <T> T select(List<T> primeList);

    /**
     * 带权负载策略算法
     *
     * @param primeList 原始列表
     * @return
     */
    default  <T extends WeightGetAble> T selectWithWeight(List<T> primeList){
        return select(WeightUtil.getWeightList(primeList));
    }
}

2 WeightGetAble

注意这里提供了 默认实现。

/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-15
 * @Description: 权重接口,使用权重负载均衡的列表元素必须实现此接口
 */

public interface WeightGetAble {

    default int getWeightFactors(){
        return 1;
    }

}

3 WeightUtil

使用枚举来实现工具类单例化,具体可见:
单例模式及其4种推荐写法和3类保护手段

getWeightList方法将构造新列表取代原列表,策略是将权重转为次数后添加进新列表,故权重不可过大,这里只提供了简单实现,还可以优化。

/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-15
 * @Description: TODO
 */

public enum WeightUtil {
    INSTANCE;
    public static  <T extends WeightGetAble> List<T> getWeightList(List<T> primeList){
        //存放加权后列表
        List<T> weightList= Lists.newArrayList();
        for (T prime:primeList){
            //按权重转化为次数添加进加权后列表
            //TODO:需注意权重代表列表长度,故不可过大,此处应优化
            int weight=prime.getWeightFactors();
            for(int i=0;i<weight;i++){
                weightList.add(prime);
            }
        }
        return weightList;
    }
}

4 RouteStrategyEnum

管理负载均衡算法和数字code的关系,并提供按code获取算法的功能。

扫描二维码关注公众号,回复: 4860546 查看本文章
/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-14
 * @Description: 路由策略 枚举类
 */
public enum RouteStrategyEnum {

    //随机算法
    Random(0),
    //轮询算法
    Polling(1),
    //源地址hash算法
    HashIP(2);


    private int code;

    RouteStrategyEnum(int code) {
        this.code = code;
    }

    public static RouteStrategyEnum queryByCode  (int code) {
        for (RouteStrategyEnum strategy : values()) {
            if(strategy.getCode()==code){
                return strategy;
            }
        }
        return null;
    }

    public int getCode() {
        return code;
    }
}

5 RouteEnige

提供按枚举或数字获取负载均衡器的方法。

注意:这里每次从RouteEngine获取的PollingRouteStrategyImpl都是新生产的,这是因为轮询是带有状态的(即轮询到的位置index)。需要用户自行保存PollingRouteStrategyImpl实例以维持其状态。

/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-12
 * @Description: 路由均衡引擎
 */
public class RouteEngine {
    
    private final static RouteStrategy RANDOM_ROUTE_STRATEGY_IMPL =new RandomRouteStrategyImpl();
    
    private final static RouteStrategy HASHIP_ROUTE_STRATEGY_IMPL =new HashIPRouteStrategyImpl();

    public static RouteStrategy queryClusterStrategy(int clusterStrategyCode) {
        RouteStrategyEnum clusterStrategyEnum = RouteStrategyEnum.queryByCode(clusterStrategyCode);
        return queryClusterStrategy(clusterStrategyEnum);
    }

    public static RouteStrategy queryClusterStrategy(RouteStrategyEnum routeStrategyEnum) {
        if(routeStrategyEnum==null){
            return new RandomRouteStrategyImpl();
        }
        switch (routeStrategyEnum){
            case Random:
                return RANDOM_ROUTE_STRATEGY_IMPL ;
            case Polling:
                return new PollingRouteStrategyImpl();
            case HashIP:
                return HASHIP_ROUTE_STRATEGY_IMPL ;
            default:
                return RANDOM_ROUTE_STRATEGY_IMPL ;
        }
    }

}

四 策略实现

1 随机策略

/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-14
 * @Description: 随机策略 负载均衡
 */
public class RandomRouteStrategyImpl implements RouteStrategy  {

    @Override
    public <T> T select(List<T> primeList) {
        return primeList.get(RandomUtils.nextInt(0, primeList.size()));
    }

}

2 轮询策略

这里使用了可重入锁来避免并发环境下 index 引发的错误。

/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-14
 * @Description: 轮询策略 负载均衡
 */
public class PollingRouteStrategyImpl implements RouteStrategy {

    /**
     * 计数器
     */
    private int index = 0;
    private Lock lock = new ReentrantLock();

    @Override
    public <T> T select(List<T> primeList) {
        T point=null;
        try {
            lock.tryLock(10,TimeUnit.MILLISECONDS);
            //若计数大于列表元素个数,将计数器归0
            if (index >= primeList.size()) {
                index = 0;
            }
            point=primeList.get(index++);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        //保证程序健壮性,若未取到服务,则改用随机算法
        if (point == null) {
            point = primeList.get(RandomUtils.nextInt(0, primeList.size()));
        }
        return point;
    }
    
}

3 基于IP的哈希策略

/**
 * @version V1.0
 * @author: lin_shen
 * @date: 18-11-14
 * @Description: 基于本地IP的哈希策略 负载均衡
 */
public class HashIPRouteStrategyImpl implements RouteStrategy {
    
    @Override
    public <T> T select(List<T> primeList) {
        String localIP=null;
        try {
            localIP=InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        //保证程序健壮性,若未取到域名,则采用改用随机字符串
        if(localIP==null){
            localIP= RandomUtils.nextBytes(5).toString();
        }

        //获取源地址对应的hashcode
        int hashCode = localIP.hashCode();
        //获取服务列表大小
        int size = primeList.size();

        return primeList.get(Math.abs(hashCode) % size);
    }
    
}

猜你喜欢

转载自blog.csdn.net/alinyua/article/details/84105650
今日推荐