如何使用嵌入式tomcat会话集群设置Spring Boot应用程序? [英] How to setup a Spring Boot application with embedded tomcat session clustering?

查看:122
本文介绍了如何使用嵌入式tomcat会话集群设置Spring Boot应用程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想设置一个带有嵌入式tomcat会话集群的Spring Boot应用程序.

I want to setup a Spring Boot application with embedded tomcat session clustering.

由于嵌入式tomcat没有server.xml文件,因此我创建了TomcatEmbeddedServletContainerFactory并以编程方式设置集群配置.代码如下:

Since embedded tomcat does not have a server.xml file, I've created a TomcatEmbeddedServletContainerFactory and programmatically setup the cluster configuration. The code is as follows:

@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";
}

我在以下环境中测试了代码:

And I tested the code with the following environment:

  • 单Windows PC
  • 2个具有不同的localClusterMemberPort和clusterMembers的Spring Boot应用程序实例

由于cookie没有考虑端口,因此包含JSESSIONID的cookie在两个实例之间共享.

Since a cookie does not take the port in account, the cookie that contains JSESSIONID is shared between the two instances.

启动实例时,tomcat群集似乎可以正常工作,因为两个实例的请求的JSESSIONID值相同.但是,当我使用第一个实例登录后向第二个实例发出请求时,第二个实例找不到HttpSession.它记录以下消息:

When the instances are started, tomcat clustering seems to work, since the JSESSIONID value of the requests for the 2 instances are the same. But when I make a request to the second instance after I logged in using the first instance, the second instance does not find the HttpSession. It logs he following message:

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没有被共享.但是,当第二个实例创建新会话时,第一个实例的登录信息将被清除,并且登录无效.

Apparently the HttpSession is not being shared. But as the second instance creates a new session, the first instance's login information is cleared and login is invalidated.

这是怎么回事?会话已共享但HttpSession未共享?

What is happening here? Session is shared but HttpSession is not shared?

顺便说一句,我读到必须在web.xml上指定<distributed />标记,应用程序才能使用tomcat会话集群.但是我不知道如何在Spring Boot的no-xml环境中指定它.这是问题的原因吗?那怎么指定呢?

BTW, I've read that <distributed /> tag must be specified on web.xml for the applications to use tomcat session clustering. But I don't know how to specify it with Spring Boot's no-xml environment. Is this the cause of the problem? Then how can it be specified?

我已经搜索并找到了一些文档,这些文档显示了使用Redis进行聚类的情况.但是目前,我不想在配置中添加其他活动部件.在我的配置中,最多3〜4个节点.

I've searched and found a few documents that show the clustering using Redis. But currently I don't want to add another moving part to my configuration. In my configuration 3~4 nodes are the maximum.

推荐答案

关键是使上下文可分发,并设置管理器.

The key was to make the context distributable, and setting manager.

当我按如下所示修改问题代码时,会话集群起作用了.

When I modified the code of the question as follows, the session clustering worked.

@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(),则集群失败,并显示以下日志.

For Spring Boot 1.2.4, context.setManager() is not needed. But for Spring Boot to 1.3.0, if context.setManager() is not called, clustering fails and the following log is shown.

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.

我对此版本依赖性有些担心.因此,我为此打开了一个问题.

I am somewhat worried about this version dependency. So I opened an issue for this.

这篇关于如何使用嵌入式tomcat会话集群设置Spring Boot应用程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆