[分布式会话]zuul通过Ribbon配置负载均衡策略实现粘性会话(sticky session)

分布式Session处理方式

分布式Session一般分为以下几种处理方式:

  • 黏性会话(sticky session):通过使每个用户每访问的服务端实例为同一个来确保session的正常使用
  • 会话拷贝(session replication):通过各个服务端实例之间的session复制,来确保每个服务端实例均具有session中的信息,以达到session的一致性
  • 会话第三方管理:通过redis等第三方的工具来存储session信息,使所有服务器实例均能访问到相同的session

黏性Session

实现粘性会话的方式主要在于确保每个用户在通过Session有效期内,访问的服务器实例为同一台,以此来保证Session信息保持

在分布式系统中,通常所有用户都会通过统一的入口(即网关Gateway)来访问系统,因此我们只需要在网关处确保用户请求的转发目的地是同一个就可以完成。而这通过实现负载均衡的策略来实现。

在使用SpringCloud进行开发的过程中,如果使用的网关服务zuul使用Ribbon实现负载均衡,则可以使用如下的方式实现一个策略来保证:

/**
 * 对访问者地址进行hash计算,使同一访问者访问同一实例
 * 参考自 
 * @see  com.netflix.loadbalancer.RoundRobinRule
 * @author YWS
 * @data 2018/11/08
 */
public class StickyLoadBalancerRule extends AbstractLoadBalancerRule {

    private static final Logger logger = LoggerFactory.getLogger(StickyLoadBalancerRule.class);

    public StickyLoadBalancerRule() {}

    public StickyLoadBalancerRule(ILoadBalancer lb) {
        this();
        this.setLoadBalancer(lb);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {}

    @Override
    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            logger.warn("no load balancer");
            return null;
        } else {
            Server server = null;
            int count = 0;

            while (true) {
                if (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) {
                        int nextServerIndex = this.addrHash(serverCount, count);
                        List<Server> tArr = new ArrayList(allServers);
                        tArr.sort(Comparator.comparing(Server::getHost));
                        server = tArr.get(nextServerIndex);
                        if (server != null && server.isAlive() && server.isReadyToServe()) {
                            return server;
                        } else {
                            server = null;
                        }
                        continue;
                    }

                    logger.warn("No up servers available from load balancer: {}", lb);
                    return null;
                }

                if (count >= 10) {
                    logger.warn("No available alive servers after 10 tries from load balancer: {}", lb);
                }

                return server;
            }
        }
    }

    public int addrHash(int serverCount, int offset) {
        String remoteAddr = this.getRemoteAddr();
        return Math.abs(remoteAddr.hashCode() + offset) % serverCount;
    }

    public String getRemoteAddr() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String forwardedFor = request.getHeader("X-FORWARDED-FOR");
        String remoteAddr = StringUtils.isEmpty(forwardedFor) ? request.getRemoteAddr() : forwardedFor;
        return remoteAddr;
    }
}

扩展文章

[分布式会话]springboot中实现session replication

GitHub

github.yws179

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值