我想用嵌入式tomcat会话集群设置一个Spring Boot应用程序。
由于嵌入式tomcat没有server.xml文件,因此我创建了TomcatEmbeddedServletContainerFactory并以编程方式设置集群配置。代码如下:
server.xml
@Configuration public class TomcatConfig { @Bean public EmbeddedServletContainerFactory servletContainerFactory() { return new TomcatEmbeddedServletContainerFactory() { @Override protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( Tomcat tomcat) { configureCluster(tomcat); return super.getTomcatEmbeddedServletContainer(tomcat); } private void configureCluster(Tomcat tomcat) { // static membership cluster SimpleTcpCluster cluster = new SimpleTcpCluster(); cluster.setChannelStartOptions(3); { DeltaManager manager = new DeltaManager(); 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 MessageDispatch15Interceptor()); { StaticMembershipInterceptor membership = new StaticMembershipInterceptor(); String[] memberSpecs = clusterMembers.split(",", -1); for (String spec : memberSpecs) { ClusterMemberDesc memberDesc = new ClusterMemberDesc(spec); StaticMember member = new StaticMember(); member.setHost(memberDesc.address); member.setPort(memberDesc.port); member.setDomain("MyWebAppDomain"); member.setUniqueId(memberDesc.uniqueId); 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); } }; } private static class ClusterMemberDesc { public String address; public int port; public String uniqueId; public ClusterMemberDesc(String spec) throws IllegalArgumentException { String[] values = spec.split(":", -1); if (values.length != 3) throw new IllegalArgumentException("clusterMembers element " + "format must be address:port:uniqueIndex"); address = values[0]; port = Integer.parseInt(values[1]); int index = Integer.parseInt(values[2]); if ((index < 0) || (index > 255)) throw new IllegalArgumentException("invalid unique index: must be >= 0 and < 256"); uniqueId = "{"; for (int i = 0; i < 16; i++, index++) { if (i != 0) uniqueId += ','; uniqueId += index % 256; } uniqueId += '}'; } }; // This is for example. In fact these are read from application.properties private int localClusterMemberPort = 9991; private String clusterMembers = "111.222.333.444:9992:1"; }
我在以下环境中测试了代码:
由于cookie没有考虑端口,因此包含JSESSIONID的cookie在两个实例之间共享。
启动实例时,tomcat群集似乎可以正常工作,因为两个实例的请求的JSESSIONID值相同。但是,当我使用第一个实例登录后向第二个实例发出请求时,第二个实例找不到HttpSession。它记录以下消息:
w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
显然,HttpSession没有被共享。但是,当第二个实例创建新会话时,第一个实例的登录信息将被清除,并且登录无效。
这是怎么回事 会话是共享的,但是HttpSession不是共享的?
顺便说一句,我读过<distributed />必须web.xml为应用程序使用tomcat会话集群指定标记。但是我不知道如何在Spring Boot的no-xml环境中指定它。这是问题的原因吗?那么如何指定呢?
<distributed />
web.xml
我搜索并找到了一些显示使用Redis进行聚类的文档。但是目前,我不想在配置中添加其他活动部件。在我的配置中,最大3〜4个节点。
关键是使上下文可分发,并设置管理器。
当我按如下所示修改问题的代码时,会话集群起作用了。
@Configuration public class TomcatConfig { @Bean public EmbeddedServletContainerFactory servletContainerFactory() { TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory() { ... }; factory.addContextCustomizers(new TomcatContextCustomizer() { @Override public void customize(Context context) { context.setManager(new DeltaManager()); context.setDistributable(true); } }); return factory; } ... }
对于Spring Boot 1.2.4,不需要context.setManager()。但是对于Spring Boot to 1.3.0,如果未调用context.setManager(),集群将失败并显示以下日志。
2015-11-18 19:59:42.882 WARN 9764 --- [ost-startStop-1] o.a.catalina.ha.tcp.SimpleTcpCluster : Manager [org.apache.catalina.session.StandardManager[]] does not implement ClusterManager, addition to cluster has been aborted.
我对此版本依赖性有些担心。因此,我为此打开了一个问题。