当前位置: 代码迷 >> Java相关 >> Nginx+IIS+Memcached集群(简)
  详细解决方案

Nginx+IIS+Memcached集群(简)

热度:679   发布时间:2016-04-22 19:51:01.0
CAS服务器集群和客户端集群环境下的单点登录和单点注销解决方案

CAS的集群环境,包括CAS的客户应用是集群环境,以及CAS服务本身是集群环境这两种情况。在集群环境下使用CAS,要解决两个问题,一是单点退出(注销)时,CAS如何将退出请求正确转发到用户session所在的具体客户应用服务器,而不是转发到其他集群服务器上,二是解决CAS服务端集群环境下各种Ticket信息的共享。

  1. CAS集群部署

    由于CAS Server是一个Web应用,因此可部署在Tomcat等容器中。直接部署CAS集群并使用负载均衡配置后,由于每次访问的CAS Server不固定,会发生通行证丢失。

    解决方法:配置TOMCAT集群及Session复制,解决CAS Server Session复制。详细配置方法见"Nginx+Tomcat+Memcached集群"。

  2. CAS的Ticket信息共享

    当用户登录后,Ticket存储在CAS Server中,由于这部分数据未保存在Session中,仅靠TOMCAT Session复制无法解决问题。默认配置下,CAS Server使用org.jasig.cas.ticket.registry.DefaultTicketRegistry把Ticket数据保存在 HashMap中,因此多台CAS Server无法共享数据。导致用户登录及退出均存在问题。因此需要将Ticket信息进行共享存储,使多台CAS Server使用相同的存储区域管理Ticket。

    Ticket信息共享处理比较简单,就是将Ticket信息从原来的内存存储变为数据库存储。见"ticketRegistry.xml"文件。处理方法有两种:1是将Ticket信息放在Redis内存数据库中,2是将Ticket信息放在memcached中,推荐使用memcached,现在DMGeoSSO已经支持这两种方式了,配置文件示例见"ticketRegistry.xml.redis"和ticketRegistry.xml.memcache。默认配置文件的内容为"ticketRegistry.xml.default"

    Redis方式源代码:RedisTicketRegistry.java

    Memcached方式源代码:MemCacheTicketRegistry.java

    这里需要注意的是:TGT和ST的超时时间最大只能设置为30天(即2592000秒),多1秒都不行。这是Memcached的特性。

  3. CAS单点注销

    根据CAS Server工作流程,当收到Logout请求后,CAS Server会删除自身存储的有关当前用户的所有Ticket票据,"问题二"的解决方法已经解决了多台CAS Server删除票据的问题。但随后从CAS Server会发起HTTP POST请求到应用服务器,该请求中具备"logoutRequest"标志,应用服务器的SingleSignOutFilter接收到该请求后在应用服务器端进行用户登出操作。该操作主要是将应用服务器端的CAS Client中保存的用户Session数据失效,达到客户端登出效果。即,对于CAS系统,必须Server端和Client均进行登出操作,用户才会真正登出。cas退出采用的是异步操作,客户端是否退出成功也不关心。

    CAS Server的这个工作流程,在应用集群部署的情况下带来一系列问题。由于应用服务器集群化,且一般会使用Session复制,当CAS Server向应用服务器发起Logout请求时,仅针对一台服务器发起请求,导致应用服务器没有全部退出,使得用户使用登出操作时,有时可以退出,有时不能退出,用户体验很差。

    解决方法:采用广播方式,将单台Tomcat收到的注销请求广播给集群环境下的所有节点,达到所有服务器都注销的效果。核心代码:DMGeoSSOClient中的CasSingleLogoutClusterFilter.java。

    配置方式,将DMGeoSSOClient工程下的lib目录下的jar包(servlet-api-2.3.jar除外)以及dist下的cas-client-core-3.1.3.jar包复制到集群环境所有Tomcat的lib目录,并修改所有tomcat的web.xml,增加过滤器的配置:

    <filter>

         <filter-name>CAS SLO Cluster Filter</filter-name>

         <filter-class>org.esco.cas.client.CasSingleLogoutClusterFilter</filter-class>

         <init-param>

         <param-name>clientHostName</param-name>

         <param-value>127.0.0.1:8080</param-value>

         </init-param>

         <init-param>

         <param-name>peersUrls</param-name>

         <param-value>http://127.0.0.1:8080,http://127.0.0.1:8081</param-value>

         </init-param>

    </filter>

    clientHostName是本Tomcat的IP和端口,peersUrls是集群中所有节点的访问地址(格式为:协议://IP:端口,多个以","分隔),注意,这些IP地址和端口需要确保集群中所有的节点都能访问到。

    另外,在集群中的所有需要做单点登录的应用中,web.xml中增加:

    <filter-mapping>

         <filter-name>CAS SLO Cluster Filter</filter-name>

         <url-pattern>/*</url-pattern>

    </filter-mapping>

    注意:这个过滤器需要放在原单点注销的过滤器之前才有效。

  4. Nginx+Tomcat+Memcached集群(简)

    nginx配置:

    nginx.conf:

    upstream cluster {

        server 127.0.0.1:8080;

        server 127.0.0.1:8081;

    }

    proxy.conf:

    server {

    listen 8888;

    server_name 127.0.0.1;

    #access_log logs/access.log main;

     

        proxy_connect_timeout 60s;

        proxy_send_timeout 300s;

        proxy_read_timeout 300s;

        proxy_buffer_size 1024k;

        proxy_buffers 4 1024k;

        proxy_busy_buffers_size 1024k;

        proxy_temp_file_write_size 1024k;

        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;

        proxy_max_temp_file_size 1024m;

     

        location ~ ^/DMGeoPortal/ {

        proxy_pass http://cluster;

             proxy_set_header Host $host:$server_port;

             proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header REMOTE-HOST $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    }

        location ~ ^/DMGeoSSO/ {

        proxy_pass http://cluster;

             proxy_set_header Host $host:$server_port;

             proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header REMOTE-HOST $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    }

    }

    Tomcat配置:

    context.xml:

    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"

            memcachedNodes="n1:172.16.254.69:11211,n2:172.16.254.70:11211"

            sticky="false"

            sessionBackupAsync="false"

            sessionBackupTimeout="18000"

            transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"

            copyCollectionsForSerialization="false"

    />

    server.xml:

    <Engine name="Catalina" defaultHost="localhost">

    注意:当sticky为false时,不需要配置jvmRoute属性,当sticky为true时,一定要配置jvmRoute属性,且集群中所有tomcat的jvmRoute属性均不一样。sticky的属性默认为true。在CAS服务器端集群和客户端集群环境下,需要将sticky配置为false,这样可以避免很多莫名其妙的session丢失问题。

    Sticky 模式:tomcat session 为 主session, memcached 为备 session。Request请求到来时, 从memcached加载备 session 到 tomcat (仅当tomcat jvmroute发生变化时,否则直接取tomcat session);Request请求结束时,将tomcat session更新至memcached,以达到主备同步之目的。下面是sticky模式时响应的流程图(图片来源网络):

     

    Non-Sticky模式:tomcat session 为 中转session, memcached1 为主 session,memcached 2 为备session。Request请求到来时,从memcached 2加载备 session 到 tomcat,(当 容器 中还是没有session 则从memcached1加载主 session 到 tomcat, 这种情况是只有一个memcached节点,或者有memcached1 出错时),Request请求结束时,将tomcat session更新至 主memcached1和备memcached2,并且清除tomcat session 。以达到主备同步之目的,如下是non-sticky模式的响应流程图:(图片来源网络)。

    requestUriIgnorePattern属性不要设置。否则在CAS服务器端集群和客户端集群环境下有很多问题(包括影响注销功能,不能完全注销),因为我们的单点登录是对所有的资源进行拦截的,不需要设置requestUriIgnorePattern(URL忽略)属性。

    集群中所有Tomcat的lib下新增的包:

    javolution-5.4.3.1.jar

    memcached-2.6.jar

    memcached-session-manager-1.5.1.jar

    memcached-session-manager-tc6-1.5.1.jar

    msm-javolution-serializer-1.5.1.jar

    msm-kryo-serializer-1.5.1.jar

    msm-serializer-benchmark-1.5.1.jar

    msm-xstream-serializer-1.5.1.jar

  5. Nginx+IIS+Memcached集群(简)

    nginx配置:

    nginx.conf:

    upstream dotnetcluster {

        server 127.0.0.1:80;

        server 127.0.0.1:81;

    }

    proxy.conf:

    server {

    listen 8888;

    server_name 127.0.0.1;

    #access_log logs/access.log main;

     

        proxy_connect_timeout 60s;

        proxy_send_timeout 300s;

        proxy_read_timeout 300s;

        proxy_buffer_size 1024k;

        proxy_buffers 4 1024k;

        proxy_busy_buffers_size 1024k;

        proxy_temp_file_write_size 1024k;

        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;

        proxy_max_temp_file_size 1024m;

     

        location ~ ^/DMGeoGlobeWeb/ {

        proxy_pass http://dotnetcluster;

             proxy_set_header Host $host:8888;

             proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header REMOTE-HOST $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    }

        location ~ ^/DMGeoMIS/ {

        proxy_pass http://dotnetcluster;

             proxy_set_header Host $host:8888;

             proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header REMOTE-HOST $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    }

    }

    这里与Java应用稍有不同,注意上述红色加粗部分端口号的配置,该端口号是你最终访问集群服务器的端口号。

    IIS配置:

    将下列dll文件放在Web应用程序的bin目录:

    Enyim.Caching.dll

    MemcachedSessionProvider.dll

    修改Web应用程序的Web.config配置:

    <?xml version="1.0" encoding="utf-8"?>

    <configuration>

    <configSections>

    <sectionGroup name="sessionManagement">

    <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" />

    </sectionGroup>

    </configSections>

     

    <sessionManagement>

    <memcached protocol="Binary">

    <servers>

    <!-- make sure you use the same ordering of nodes in every configuration you have -->

    <add address="172.16.254.69" port="11211" />

    <add address="172.16.254.70" port="11211" />

    </servers>

    <locator type="MemcachedSessionProvider.SessionNodeLocator, MemcachedSessionProvider" />

    </memcached>

    </sessionManagement>

    <system.web>

    <sessionState customProvider="Memcached" mode="Custom">

    <providers>

    <add name="Memcached" type="MemcachedSessionProvider.SessionProvider, MemcachedSessionProvider" />

    </providers>

    </sessionState>

    <machineKey validationKey="3A2122BF7DA69398B43FF26BD658CE428EC417BA" decryptionKey="5C90C7D3BE69792117E02AE72DDDAFBA853F5FDB3E57BC5C" decryption="3DES" validation="SHA1"/>

    </system.web>

    </configuration>

    上述红色加粗部分是新增的配置项。说明如下:

    sessionManagement:用来配置Memcached连接地址。

    sessionState:用将配置将Session存储在什么地方,这里配置的是自定义,即将Session存储在Memcached中。

    machineKey:这是比较关键的配置。如果我们的Web应用程序是在同一个IIS服务器上,用不同的端口来区分不同的网站应用,那么不需要配置machineKey;如果我们的Web应用程序是在不同的IIS服务器上,那么切记一定要配置machineKey。machineKey是对密钥进行配置,以便将其用于对 Forms 身份验证 Cookie 数据和视图状态数据进行加密和解密,并将其用于对进程外会话状态标识进行验证。默认情况下,Asp.Net的配置是自己动态生成,如果单台服务器当然没问题,但是如果多台服务器负载均衡,machineKey还采用动态生成的方式,每台服务器上的machinekey值不一致,就导致加密出来的结果也不一致,不能共享验证和 ViewState,所以对于多台服务器负载均衡的情况,一定要在每个站点配置相同的machineKey

    machineKey的生成算法如下:

    private static string CreateKey(int len)

    {

    byte[] bytes = new byte[len];

    new RNGCryptoServiceProvider().GetBytes(bytes);

    StringBuilder sb = new StringBuilder();

     

    for (int i = 0; i < bytes.Length; i++)

    {

    sb.Append(string.Format("{0:X2}", bytes[i]));

    }

     

    return sb.ToString();

    }

    调用:

    string validationKey = CreateKey(20);

    string decryptionKey = CreateKey(24);

    string machineKey = string.Format("<machineKey validationKey=\"{0}\" decryptionKey=\"{1}\" decryption=\"3DES\" validation=\"SHA1\"/>",validationKey, decryptionKey);