spring cloud ribbon自定義負載均衡策略
阿新 • • 發佈:2019-01-03
ribbon已經提供了很多負載的策略,如下圖:
這些負載策略通常已經滿足我們的日常業務需求(這些策略的具體介紹,可以點此檢視),如特殊需要,我們也需要自定義負載策略。
我的應用場景是:spring cloud zuul做為使用者請求的入口服務,zuul代理到目標服務的時候,其內部就是通過ribbon的負載策略選出並代理到一個服務例項,這裡我自定義的負載策略實現的功能是“同一個ip下的同一個使用者的所有請求被代理到同一個例項”(如果請求沒有使用者資訊,那就相當於ip_hash策略)。
新建一個類IpUserHashRule繼承自com.netflix.loadbalancer.AbstractLoadBalancerRule:
關鍵在於ipUserHash方法,其將使用者的ip和使用者標識組合所得的hashcode再與服務例項數量進行模運算從而得到例項public class IpUserHashRule extends AbstractLoadBalancerRule { private static Logger log = LoggerFactory.getLogger(IpUserHashRule.class); public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } Server server = null; int count = 0; while (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if ((upCount == 0) || (serverCount == 0)) { log.warn("No up servers available from load balancer: " + lb); return null; } int nextServerIndex = ipUserHash(serverCount); server = allServers.get(nextServerIndex); if (server == null) { /* Transient. */ Thread.yield(); continue; } if (server.isAlive() && (server.isReadyToServe())) { return (server); } // Next. server = null; } if (count >= 10) { log.warn("No available alive servers after 10 tries from load balancer: " + lb); } return server; } private int ipUserHash(int serverCount) { String userTicket = getTicketFromCookie(); String userIp = getRemoteAddr(); try { userIp = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { } int userHashCode = Math.abs((userIp+userTicket).hashCode()); return userHashCode%serverCount; } private String getRemoteAddr() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String remoteAddr = "0.0.0.0"; if (request.getHeader("X-FORWARDED-FOR") != null) { remoteAddr = request.getHeader("X-FORWARDED-FOR"); } else { remoteAddr = request.getRemoteAddr(); } return remoteAddr; } private String getTicketFromCookie() { String ticket = ""; RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); //從cookie獲取ticket Cookie cookie = CookieUtil.getCookieByName(request,CookieUtil.COOKIE_TICKET_NAME); if (cookie!=null) { ticket = cookie.getValue()!=null?cookie.getValue():""; } return ticket; } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // TODO Auto-generated method stub } public static void main(String[] args) { String ticket = ""; String localIp = "127.0.0.1"; System.out.println(Math.abs((ticket+localIp).hashCode())%5); } }
有了這個類過後,還需要配置使用這個自定義的負載策略,配置如下:
user.ribbon.NFLoadBalancerRuleClassName=com.bqjr.spring.cloud.zuul.ribbonextend.IpUserHashRule
這個配置的意思是,名叫user服務使用IpUserHashRule這個負載策略(其他服務依然使用預設的負載策略,spring cloud ribbon提供的預設負載策略是這個類com.netflix.loadbalancer.ZoneAvoidanceRule)。醬紫就可以啦。
參考文件:http://docs.springcloud.cn/user-guide/ribbon/#ribbon-api
推薦文章(Netflix 原始碼分析):http://www.idouba.net/sping-cloud-and-netflix/