分布式会话处理方式
分布式Session一般分为以下几种处理方式:
- 粘性会话(sticky session):通过使每个用户每访问的服务端实例为同一个来确保session的正常使用
- 会话拷贝(session replication):通过各个服务端实例之间的session复制,来确保每个服务端实例均具有session中的信息,以达到session的一致性
- 会话第三方管理:通过redis等第三方的工具来存储session信息,使所有服务器实例均能访问到相同的session
其中session replication
由于每个服务器实例都需要存储所有的session信息,并不适合规模较大的集群使用
会话拷贝(session replication)
tomcat
只带了session replication
的功能,只需要通过配置文件进行配置就可以使多个tomcat
实例之间相互进行session
拷贝,以下是来自tomcat
官网文档的配置:
<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"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
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.MessageDispatchInterceptor"/>
</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.ClusterSessionListener"/>
</Cluster>
但是,如果使用了springboot
中的内置tomcat
,则需要通过java config的方式进行配置,而我在网络上并没有找到tomcat
新版本的配置,于是通过官方文档及旧版配置进行了梳理,得出以下配置方法:
@Configuration
public class TomcatConfig {
@Value("${tomcat.localClusterMemberPort}")
private int localClusterMemberPort;
@Value("${tomcat.clusterMembers}")
private String clusterMembers;
@Bean
public TomcatServletWebServerFactory servletContainerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory() {
@Override
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
configureCluster(tomcat);
return super.getTomcatWebServer(tomcat);
}
};
factory.addContextCustomizers((TomcatContextCustomizer) context -> {
context.setDistributable(true);
});
return factory;
}
private void configureCluster(Tomcat tomcat) {
SimpleTcpCluster cluster = new SimpleTcpCluster();
cluster.setChannelSendOptions(8);
DeltaManager manager = new DeltaManager();
manager.setExpireSessionsOnShutdown(false);
manager.setNotifyListenersOnReplication(true);
cluster.setManagerTemplate(manager);
GroupChannel channel = new GroupChannel();
NioReceiver receiver = new NioReceiver();
receiver.setPort(localClusterMemberPort);
channel.setChannelReceiver(receiver);
ReplicationTransmitter sender = new ReplicationTransmitter();
sender.setTransport(new PooledParallelSender());
channel.setChannelSender(sender);
channel.addInterceptor(new TcpPingInterceptor());
//将消息调度到线程(线程池)以异步发送消息
channel.addInterceptor(new TcpFailureDetector());
channel.addInterceptor(new MessageDispatchInterceptor());
StaticMembershipInterceptor membership = new StaticMembershipInterceptor();
String[] memberSpecs = clusterMembers.split(",", -1);
for (int i = 0; i < memberSpecs.length; i++) {
String[] addrPart = memberSpecs[i].split(":");
StaticMember member = new StaticMember();
member.setHost(addrPart[0]);
member.setPort(Integer.parseInt(addrPart[1]));
member.setDomain("MyDomain");
member.setUniqueId(String.valueOf(i).getBytes());
membership.addStaticMember(member);
}
channel.addInterceptor(membership);
cluster.setChannel(channel);
cluster.addValve(new ReplicationValve());
cluster.addValve(new JvmRouteBinderValve());
cluster.addClusterListener(new ClusterSessionListener());
tomcat.getEngine().setCluster(cluster);
}
}