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; } }
二、平滑加權輪詢算法 --- 分析