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; } }
二、平滑加权轮询算法 --- 分析