java 平滑加权轮询算法实现与讲解

                      java 平滑加权轮询算法实现与分析

废话,可直接跳过: 有一个需求,需要在代码层面上 实现 灰度 发布,有一种很简单的办法就是取余,比如  当前时间戳(或者业务ID) % 10 对于10取余, 余1,2,3 的走 逻辑A,其他的走逻辑B,从而达到灰度发布的效果,但是我不甘于此,我想设计的复杂点,就去研究了下nginx相关的轮询算法, 我中意了一个 平滑加权轮询算法(再简单的东西,只要你愿意去思考,总能学到东西,不甘于现状)。

 

 

  • 平滑加权轮询算法  ---  直接贴代码

  • 平滑加权轮询算法 ---  分析

 

一、平滑加权轮询算法  ---  直接贴代码

public class SmoothServer {

    public SmoothServer(String ip, int weight, int curWeight) {
        this.ip = ip;
        this.weight = weight;
        this.curWeight = curWeight;
    }

    private String ip;

    private int weight;

    private int curWeight;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public int getCurWeight() {
        return curWeight;
    }

    public void setCurWeight(int curWeight) {
        this.curWeight = curWeight;
    }
}
服务器实体类
public class SmoothWeightRoundRobin {

    /**初始化所有的服务器**/
    List<SmoothServer> servers = new ArrayList<>();

    /**服务器权重总和*/
    private int weightCount;

    public void init(List<SmoothServer> servers) {
        this.servers = servers;
        this.weightCount = this.servers.stream().map(server -> server.getWeight()).reduce(0, (l, r) -> l + r);

    }

    /**获取需要执行的服务器**/
    public SmoothServer getServer() {
        SmoothServer tmpSv = null;

        for (SmoothServer sv : servers) {
            sv.setCurWeight(sv.getWeight() + sv.getCurWeight());
            if (tmpSv == null || tmpSv.getCurWeight() < sv.getCurWeight()) tmpSv = sv;
        }

        tmpSv.setCurWeight(tmpSv.getCurWeight() - weightCount);
        return tmpSv;
    }

}
平滑权衡算法
public class RobinExecute {

    /** 线程使用完不会清除该变量,会一直保留着,由于线程 池化所以不用担心内存泄漏 **/
    private ThreadLocal<SmoothWeightRoundRobin> weightRoundRobinTl = new ThreadLocal<SmoothWeightRoundRobin>();

    private ReentrantLock lock = new ReentrantLock();

    /** 为什么添加volatile,是因为 ReentrantLock 并不保证内存可见性 **/
    private volatile SmoothWeightRoundRobin smoothWeightRoundRobin;

    public static void main(String[] args) {

        RobinExecute robinExecute = new RobinExecute();

        /** ==================    TheadLocal   ========================**/
        robinExecute.acquireWeightRoundRobinOfTheadLocal().getServer();

        /** ==================    ReentrantLock 可重入锁   ========================**/
        robinExecute.getLock().lock();  //notice: 注意此锁会无休止的等待资源,如果使用此锁,无比保证资源能够被拿到
        try {
            robinExecute.acquireWeightRoundRobinOfLock();
        } catch (Exception e) {
            e.printStackTrace();
        } finally { //确保一定要释放锁
            robinExecute.getLock().unlock();
        }

    }

    /**
     * 在分布式情况,第二种使用方式  使用cas ReentrantLock 可重入锁
     * notice:
     * @return
     */
    public SmoothServer acquireWeightRoundRobinOfLock() {
        if (smoothWeightRoundRobin == null) {
            SmoothWeightRoundRobin weightRoundRobin = new SmoothWeightRoundRobin();
            List<SmoothServer> servers = new ArrayList<>();
            servers.add(new SmoothServer("191", 1, 0));
            servers.add(new SmoothServer("192", 2, 0));
            servers.add(new SmoothServer("194", 4, 0));
            weightRoundRobin.init(servers);
            smoothWeightRoundRobin = weightRoundRobin;
        }
        return smoothWeightRoundRobin.getServer();
    }

    /**
     * 在分布式情况,第一种使用方式  ThreadLock
     * notice: 只有在使用池化技术的情况才建议使用此方式,否则达不到效果,还会造成内存泄漏
     * @return
     */
    public SmoothWeightRoundRobin acquireWeightRoundRobinOfTheadLocal() {
        return Optional.ofNullable(weightRoundRobinTl.get())
            .orElseGet(() -> {
                SmoothWeightRoundRobin weightRoundRobin = new SmoothWeightRoundRobin();
                List<SmoothServer> servers = new ArrayList<>();
                servers.add(new SmoothServer("191", 1, 0));
                servers.add(new SmoothServer("192", 2, 0));
                servers.add(new SmoothServer("194", 4, 0));
                weightRoundRobin.init(servers);
                weightRoundRobinTl.set(weightRoundRobin);
                return weightRoundRobin;
            });
    }

    public ReentrantLock getLock() {
        return lock;
    }

    public ThreadLocal<SmoothWeightRoundRobin> getWeightRoundRobinTl() {
        return weightRoundRobinTl;
    }
}
两种使用方式(适用分布式环境中)

 

二、平滑加权轮询算法 ---  分析

 

 

 

posted @ 2018-12-04 13:37  无期(瑶瑶)  阅读(3670)  评论(0编辑  收藏  举报