• ASP.NET 负载均衡 StateServer Session共享问题(经验记录)


      (源地址:http://www.cnblogs.com/ryhan/p/3748976.html)

      

      最近在改造公司的一个系统 支持F5硬件负载,由于系统后面还跟了个异步工具,需要将Admin上传的文件保存本地后发送到FTP上,并记录位置,异步工具根据位置进行下载。对于工具异步导出的文件,需要在Admin上进行下载,采用的方式是异步工具生成文件包,分发到Admin下的文件夹里(将Admin下的文件发布FTP)并更新数据库记录,Admin展示可下载的包,并链接下载。 到此为止 ,完成系统上的功能性改造。

      下面,另一件本来以为很容易的事情,就来了。。。也是这件事情,让我产生了 一定要写下此篇文章的动力。。。

      ----正文开始----

      现网公网端口只开80,且公网端口走F5负载,方式为轮询。既然为负载就肯定要考虑Session保存问题。

      初始设想:

        使用Memcache共享缓存,并自己开发一个索引器,在索引器写好以后,突然发现如何标识客户端是个问题,需要自己设定cookies标识,要自己拦截请求等等等。。。,方案不好搞了。。

      

      改动方案:

        ASP.NET的网站,首选肯定是微软整出来的功能、组件。 经过问度娘,得到两个比较靠谱的方式(不需要自己写代码,修改sessionState配置即可)

        1. 使用StateServer,选择其中一个Admin所在的服务器,开启ASP.NET 状态服务

        2. 使用SQLServer ,在数据库整一库,专门保存Session,然后用作业清理过期的登录信息。

        (以上配置是ASP.NET关于存储Session方式的其中两个,具体可百度。。。)    

    状态服务(运行services.msc):

      

      

    我最终选择了方案1,原因是觉得这个简单,靠谱~~ ,好吧,以下还原我的整个配置、处理过程。。。。

      

      1.首先,状态服务端口是42424。 修改web.config配置:

    <sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" cookieless="false" timeout="30" />
    

       完事后,启动调测,登录成功,表示存储OK。

      2.果断发布版本,在服务器A、B上分别部署,并将状态服务IP都指向A。然后打开登录页面。。。

    无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同。如果服务器位于远程计算机上,请检查 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesaspnet_stateParametersAllowRemoteConnection 的值,确保服务器接受远程请求。如果服务器位于本地计算机上,并且上面提到的注册表值不存在或者设置为 0,则状态服务器连接字符串必须使用“localhost”或“127.0.0.1”作为服务器名称。 

     图:

      

     额?黄页??为毛??? 好吧看来需要修改注册表AllowRemoteConnection ,把0改成1,然后重启状态服务。再次打开页面。。。哈哈。。终于显示了。。。A、B分别登录各种操作都是正常的。。。表示当时很开心。。。

    图:

      3.公司研发环境内没有F5,只能使用Nginx进行模拟负载。那就下载下来启动。。。每次启动都挂,说是神马创建日志文件失败。。。。好吧再问度娘,有人说要把CMD.EXE拷贝到那nginx-1.2.5下面,然后在把nginx.exe拖到CMD里面执行,额 果然好了可以启动。。。

    然后把CMD拷贝到根目录

      百度下,配置好复杂有点乱,那就删删删除,最后配置就几句话,测试了下,满足了我的需求(这种配置情况下,每次负载转发请求都是轮询的 就是ABABAB。。。):

    #user  nobody;
    worker_processes  1;
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    #error_log  logs/error.log  info;
    
    #pid        logs/nginx.pid;
    
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log  logs/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
    
        upstream xx.xx.xx.xx{        
             server   192.168.xx.x2:2014;
             server   192.168.xx.x1:2014;
                }
                
        server {
            listen       2014;
            server_name  xx.xx.xx.xx;
            location / {
                proxy_pass http://xx.xx.xx.xx;
               # access_log off;
                proxy_connect_timeout       10;
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
       }       
    
    }
    

      

       4.负载启动后,果断访问虚拟主机,额。。。悲剧的事情,从此处开始。。。。

       a.正确的密码帐号,登录后页面刷了下,没登录成功

       b.验证码 偶尔不正确,搞不懂了。。

       c.偶尔登录成功,各种操作也OK。

       d.在用X帐号登录第一次时,页面刷新了下,然后用了Y帐号登录,成功。 刷了下页面,嗯? 怎么编写X了,再刷一次,我擦怎么又是Y了。然后。。。发现了,这次缓存有问题了。。。。session貌似没共享?  度娘都是说这样可以共享的,但是为毛不行。。。

      ------------------------------------------------------------------------------------------------------------------------

      中间各种测试,各种分析,最终结论是要么A的session没发给B的服务器,要么就是哪地方挂了,果断把状态服务给关了,然后两个页面都挂了。看来,两个页面都用了这状态服务器,但是为啥不行呢。。。

      果断在增加一个sessiontest.aspx页面,输出当前登录的用户,图片验证码、Cookies、SessionID信息。

      打开虚拟主机页面,刷新:

      第一次:  

    sessionid=ajfjs245ywd12p45nnjwvqe5 
    username= 
    userpwd= 
    userRole= 
    randmKey=2715 
    cookies=ASP.NET_SessionId:ajfjs245ywd12p45nnjwvqe5 

      第二次: 

    sessionid=ajfjs245ywd12p45nnjwvqe5 
    username= 
    userpwd= 
    userRole= 
    randmKey=8432 
    cookies=ASP.NET_SessionId:ajfjs245ywd12p45nnjwvqe5 

      第三次:

    sessionid=ajfjs245ywd12p45nnjwvqe5 
    username= 
    userpwd= 
    userRole= 
    randmKey=2715 
    cookies=ASP.NET_SessionId:ajfjs245ywd12p45nnjwvqe5 

      什么情况? 为毛来回变换。。。。猜测,就是这个原因导致每次登录时系统反馈验证码不正确,偶尔登录成功后,X、Y用户会来回切换 也是这个。。。

    然后用Fiddler构造请求,使用 ASP.NET_SessionId:ajfjs245ywd12p45nnjwvqe5 分别对两台负载WEB进行发包,得到的结果分别与请求虚拟主机的第一次和第二次结果一致。。。说明两台负载WEB的Session没存在一起,或者存在一起了,但是是分开的,压根没共享。。。。

     

      好吧问题定位清楚,那就开始排查是什么导致这问题产生的。。。

      ------------------------------------------------------------------------------------------------------------------------

      5.拿出神器,开始抓包,发现通信正常,A的SESSION也发给B的服务器了。但是为啥还不行?  

        ------------------------------------------------------------------------------------------------------------------------

      中间各种测试,各种分析,最终 还是没结论,然后就一边分析、一边百度。。。。

      ------------------------------------------------------------------------------------------------------------------------ 

      最终,在一个网页上看到一段话,才恍然大悟。。。原来虽然我Session共享了,但是A、B存储的位置不对,每次读写都不是一个。囧

      要在 Web 场中的不同 Web 服务器间维护会话状态,Microsoft“Internet 信息服务”(IIS) 配置数据库中 Web 站点的应用程序路径(例如,LMW3SVC2)与 Web 场中所有 Web 服务器必须相同。大小写也必须相同,因为应用程序路径是区分大小写的。在一台 Web 服务器上,承载 ASP.NET 应用程序的 Web 站点的实例 ID 可能是 2(其中应用程序路径是 LMW3SVC2)。在另一台 Web 服务器上,Web 站点的实例 ID 可能是 3(其中应用程序路径是 LMW3SVC3)。因此,Web 场中的 Web 服务器之间的应用程序路径是不同的。我们必须使Web 场Web 站点的实例 ID 相同即可。你可以在IIS中把某一个WEB配置信息保存为一个文件,其他Web 服务器的IIS配置可以来自这一个文件。
    

      为验证实际情况,继续抓包,并把A、B WEB使用的状态服务改到C上,刷新虚拟主机页面:

      第一次(A <-> C):

        

      第二次(B <-> C):

      

    解码后情况:

    第一次:

    GET /LM/W3SVC/753869457/ROOT(njUDW6dgPSYnBQVKY6MYyJpdicI=)/ajfjs245ywd12p45nnjwvqe5 HTTP/1.1
    Host: 192.168.14.75
    Exclusive: acquire
    

    第二次:

    GET /LM/W3SVC/753869456/Root(tHayTJV6gf0ZP3QdeL9pR3YM4mg=)/ajfjs245ywd12p45nnjwvqe5 HTTP/1.1
    Host: 192.168.14.75
    Exclusive: acquire
    

    可以看到,第一次和第二次的请求路径不一样(红色部分),cookies ID是一样的,也就是说状态服务器上已经保存了两个负载WEB的Session,但是两个WEB存储的路径不一致。

    ------------------------------------------------------------------------------------------------------------------------

    解决方案:

    1.使用VBS脚本更改IIS站点设置(http://support.microsoft.com/default.aspx?scid=kb;zh-cn;325056)  

    使用代码修改 IIS 配置数据库。
    创建一个文本文件,然后将该文件命名为 Moveinstance.vbs。
    将下面的脚本代码添加到 Moveinstance.vbs 中,这些代码会修改 Web 站点的实例 ID,以便这些实例 ID 是相同的:
    Dim WebService
    Dim oldstr
    Dim newstr
    Dim args
    Set args = WScript.Arguments
    If args.Count < 1 Then
        Wscript.Echo "Must have original instance id and new instance id" &     chr(10) & chr(13) & _
        "usage:  moveinstance.vbs 1 5"  & chr(10) & chr(13) & _
    "Moves instance 1 to instance 5"
        WScript.Quit()
    End If
    Set WebService = GetObject("IIS://LocalHost/W3SVC")
    oldstr = args(0) 'old instance
    newstr = args(1) 'new instance
    WebService.MoveHere oldstr,newstr
    WebService.SetInfo
    Set WebService = nothing
    Set args=nothing
    WScript.echo "DONE"
    						
    保存 Moveinstance.vbs。
    在命令提示符下从您在上一步中保存该 .vbs 文件时的同一个位置运行此脚本。
    
    例如,在命令提示符下,键入 cscript moveinstance.vbs 1 5。这会在配置数据库中将 Web 站点的实例 ID 从 1 更改为 5。
    
    注意:分配给 Web 站点的新实例 ID 一定不能已经分配给其他 Web 站点。这会导致不可靠的结果。
    

    2.使用MetaEdit工具直接修改(http://support.microsoft.com/kb/240225):

    MetaEdit
    
    IIS 4.0 资源工具包介绍 MetaEdit 如下所示:
    元数据库编辑器 (MetaEdit) 是一个工具,它提供的功能类似于 Windows NT 注册表编辑器。使用 MetaEdit,可以浏览和修改配置数据库中的属性。请注意,在使用 MetaEdit,您可以进行更改,可能会损坏您的 IIS 配置。请务必认真编辑所有项。
    MetaEdit 是附带可从 Microsoft Press IIS 4.0 资源工具包实用程序之一。
    
    MetaEdit 文档 (MetaEdit.doc) 位于 IIS 资源工具包光盘上的 IIS Resource KitUtilityMetaEdit 文件夹中。
    

    3.将站点A配置导出,在B服务器使用A导出的配置进行发布(需要保证A、B服务器上的结构、目录 文件一致。)

    另外:

      若要在A、B Web上负载,还必须保证A、B的 machineKey 配置一致,不然也会黄页。

      我的配置:

    <machineKey validationKey="3FF1E929BC0534950B0920A7B59FA698BD02DFE8" decryptionKey="280450BB36319B474C996B506A95AEDF9B51211B1D2B7A77" decryption="3DES" validation="SHA1"/>
    

      

      在整个过程中,发现好多问题,最终还是解决掉! 另外,一直不用SQLServer存储Session的原因就是,那建库脚本老是执行失败。。

                                              by:MR.HAN  2014.05.23

      

  • 相关阅读:
    函数式编程:用户登录和注册
    文件操作
    shell介绍
    函数返回值
    selenium 事件介绍
    Linux添加硬盘设备
    分布式系统中幂等性、at least once 和 at most once 问题
    HBase面试考点
    简单理解:数据库的一致性与四种隔离级别(+MySQL实现)
    大数据框架Hive优化方法
  • 原文地址:https://www.cnblogs.com/ryhan/p/3748976.html
Copyright © 2020-2023  润新知