ribbon已經提供了很多負載的政策,如下圖:
這些負載政策通常已經滿足我們的日常業務需求(這些政策的具體介紹,可以點此檢視),如特殊需要,我們也需要自定義負載政策。
我的應用場景是:spring cloud zuul做為使用者請求的入口服務,zuul代理到目标服務的時候,其内部就是通過ribbon的負載政策選出并代理到一個服務執行個體,這裡我自定義的負載政策實作的功能是“同一個ip下的同一個使用者的所有請求被代理到同一個執行個體”(如果請求沒有使用者資訊,那就相當于ip_hash政策)。
建立一個類IpUserHashRule繼承自com.netflix.loadbalancer.AbstractLoadBalancerRule:
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);
}
}
關鍵在于ipUserHash方法,其将使用者的ip和使用者辨別組合所得的hashcode再與服務執行個體數量進行模運算進而得到執行個體
有了這個類過後,還需要配置使用這個自定義的負載政策,配置如下:
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/