一、负载均衡场景
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。