spring cloud ribbon自定义负载均衡策略

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/


猜你喜欢

转载自blog.csdn.net/jayjjb/article/details/71552861