一、负载均衡场景

nginx配置

upstream myupstream{
    server *.*.*.*:8080 weight=1;
    server *.*.*.*:8080 weight=1;
}
server{
    listen *.*.*.*:80
    local / {
        proxy_pass http://myupstream;
    }
}

二、session不一致产生的原因

在配置了负载均衡之后,本地发出的请求会被分发到不同的服务器上,第一次请求时,服务器1会产生一个session写到服务器内存中并将sessionid返回到本地浏览器中的cookie中,第二次请求时,本地发起请求同时会携带该sessionid到服务器2,但服务器2内存中并不存在该sessionid,因此会创建一个新的session并返回到本地浏览器中,之后的每一次请求都会循环这个过程,导致用户信息丢失。

三、基于IP_HASH的负载均衡的解决方案

实现

设置负载均衡策略为iphash

upstream backserver { 
ip_hash; 
    server *.*.*.*:8080;
    server *.*.*.*:8080;
}
原理

同一ip的请求会分发到同一服务器进行处理

优点
  • 配置简单,无需修改代码,不入侵应用。
  • 便于水平拓展。
缺点
  • 服务器出现故障或者重启会导致session丢失
  • 可能会导致单点负载过高

四、Session复制的解决方案

实现

修改tomcat配置文件server.xml,添加cluster节点

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">

<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>

<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
bind="127.0.0.1"
address="228.0.0.4"<!--保留ip,用于广播-->
port="45564"
frequency="500"
dropTime="3000"/> 
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4001"<!--如果是在同一台机器上的两个tomcat做负载,则此端口则不能重复-->
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

修改应用内web.xml,增加节点

<distributable/> 
原理

多个tomcat利用组播网络复制session.(224.0.0.0~224.0.0.255为用户可用的组播地址)

优点
  • 不入侵应用
  • 可以适应各种负载均衡策略
  • 服务器重启或者宕机不会造成session丢失
缺点
  • 性能低
  • 内存消耗大

五、RedisSession共享

实现

1、添加相关依赖

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
  <version>1.2.1.RELEASE</version>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.8.1</version>
</dependency>

2、web.xml添加过滤器

<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

3、spring配置

    <context:property-placeholder location="classpath:config/redis.properties"
        ignore-unresolvable="true" />
    <!-- 启用redis session maxInactiveIntervalInSeconds为过期时间-->
    <bean id="redisHttpSessionConfiguration"
        class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="maxInactiveIntervalInSeconds" value="600" />
    </bean>
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="maxWaitMillis" value="${redis.maxWait}" />
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
    </bean>
    <!-- redis连接工厂 -->
    <bean id="connectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="poolConfig" ref="poolConfig" />
        <property name="port" value="${redis.port}" />
        <property name="hostName" value="${redis.host}" />
        <property name="password" value="${redis.password}" />
        <property name="timeout" value="${redis.timeout}"></property>
    </bean>
优点
  • 适应多种负载均衡策略。
  • 服务器重启或者宕机不会造成session丢失。
  • 拓展能力强,适合集群数量大使用。
缺点
  • 入侵应用。
  • redis读取与存储需要序列化,消耗性能。
原理

利用过滤器,将HttpSession替换为RedisSession。

参考

https://blog.csdn.net/jiaojizu/article/details/80946994