Session服务器配置指南与使用经验的深入解析


Posted in PHP onJune 17, 2013

一.摘要
所有Web程序都会使用Session保存数据. 使用独立的Session服务器可以解决负载均衡场景中的Session共享问题.本文介绍.NET平台下建立Session服务器的几种办法, 并介绍在使用Session时的各种经验和技巧.

二.关于Session,SessionID和Cookies
Session数据保存在服务器端, 但是每一个客户端都需要保存一个SessionID, SessionID保存在Cookies中, 关闭浏览器时过期.

在向服务器发送的HTTP请求中会包含SessionID, 服务器端根据SessionID获取获取此用户的Session信息.
很多初级开发人员不知道SessionID和Cookies的关系, 所以常常认为两者没有联系. 这是不正确的. 正是因为SessionID保存在Cookies中, 所以在我们保存Cookies的时候,一定要注意不要因为Cookies的大小和个数问题而导致SessionID对象. 在我们的程序中, 对SessionID的Cookies有特殊的处理:

      /// <summary>
        /// 写入cookie.
        /// </summary>
        /// <param name="day"></param>
        /// <returns></returns>
        public bool SetCookie(int day)
        {
            string CookieName = GetType().ToString();
            HttpCookie SessionCookie = null;
            //对 SessionId 进行备份.
            if (HttpContext.Current.Request.Cookies["ASP.NET_SessionId"] != null)
            {
                string SesssionId = HttpContext.Current.Request.Cookies["ASP.NET_SessionId"].Value.ToString();
                SessionCookie = new HttpCookie("ASP.NET_SessionId");
                SessionCookie.Value = SesssionId;
            }            //省略掉中间的代码部分.只保留备份SessionID和找回SessionID的逻辑            
            //如果cookie总数超过20 个, 重写ASP.NET_SessionId, 以防Session 丢失.
            if (HttpContext.Current.Request.Cookies.Count > 20 && SessionCookie != null)
            {
                if (SessionCookie.Value != string.Empty)
                {        
                    HttpContext.Current.Response.Cookies.Remove("ASP.NET_SessionId");
                    HttpContext.Current.Response.Cookies.Add(SessionCookie);
                }
            }
            return true;
        }

三.搭建Session服务器的几种方式
将Session保存在独立的服务器中可以实现在多台Web服务器之间共享Session.虽然我们也可以自己开发Session存储系统, 但是使用ASP.NET自带的存储机制将更加便捷.
.NET提供了5种保存Seission的方式:
方式名称 存储方式 性能
Off 设置为不使用Session功能
InProc 设置为将Session存储在进程内,就是ASP中的存储方式,这是默认值。 性能最高
StateServer 设置为将Session存储在独立的状态服务中。通常是aspnet_state.exe进程. 性能损失10-15%
SQLServer 设置将Session存储在SQL Server中。 性能损失10-20%
Customer 自定制的存储方案 由实现方式确定

我们可以在Web.Config中配置程序使用的Session存储方式.默认情况下是InProc, 即保存在IIS进程中. 关于Off, InProc和Customer本文不做讲解. 相关文章大家都可以在网上搜索到.
下面主要讲解 StateServer 和 SQLServer 的应用.

四.使用 StateServer 模式搭建Session服务器
(1)服务器端配置
1.启动 Asp.net State service服务.(这个服务默认的状态为手动.修改为自动并启动.)
2.修改注册表: [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\aspnet_state\Parameters]
设置 AllowRemoteConnection = 1 , 设置 Port = 42424 (十进制,默认即为42424)
Port是服务的端口号
AllowRemoteConnection 表示是否允许其他机器连接,0为仅能本机使用,1为可以供其他机器使用.

(2)客户端设置
在Web应用程序的Web.Config中, 我们需要修改 <configuration> / <system.web> 的<sessionState>节点.如果没有
没有则添加(默认使用的是InProc方式)

<sessionState 
mode="StateServer" 
stateConnectionString="tcpip=服务器ip:42424" 
cookieless="false" 
timeout="60"/>

上面的参数我们可以根据需要修改.

五.使用SqlServer模式搭建Session服务器
(1)服务器端配置
使用SqlServer模式搭建Session服务器端有两种方式. ASP.NET 1.0和1.1版本请使用方式a, 2.0即以上版本请使用方式b.

a.使用SQL文件创建Session数据库
在ASP.NET 1.0和1.1 版本中, 只能使用这种方式.对于2.0及其以上版本,请使用aspnet_regsql.exe工具.(当然此方法也通用2.0版本)
.net提供了数据库安装脚本,可以在机器的windows文件夹中找到:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlState.sql
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlStateTemplate.sql
根据ASP.NET的版本不同, 需要使用不同的SQL脚本. ASP.NET主要有1.1和2.0两个版本,可以在不同的版本文件夹找到这两个SQL.
InstallSqlState.sql 是创建默认名称的数据库"[ASPState]".此SQL可以直接运行.
InstallSqlStateTemplate.sql 可以使用自己指定的数据库保存数据.此SQL需要自己修改后运行, 打开SQL文件将其中 [DatabaseNamePlaceHolder] 替换为自己指定的数据库名称.
执行installsqlstate.sql时不需要指定数据库,可以在任意数据库上执行.此SQL会自己创建新的数据库

b. 使用aspnet_regsql.exe工具
ASP.NET 2.0版本后微软提供了aspnet_regsql.exe工具可以方便的配置Session数据库.该工具位于 Web 服务器上的"系统根目录\Microsoft.NET\Framework\版本号"文件夹中.
使用举例:
aspnet_regsql.exe -S . -U sa -P 123456 -ssadd -sstype p
-S参数:
表示数据库实例名称. 可以用"."表示本机.
-U和-P参数:
表示用户名和密码.
-E参数:
可以再-U ?P 与 -E中选择一组. ?E表示以当前系统用户通过windows身份验证登录数据库, -U -P则是使用SqlServer用户登录数据库.
-ssadd / ?ssremove 参数:
-ssadd表示是添加Session数据库, -ssremove表示移除Session数据库.
sstype 参数:

选项 说明
t 将会话数据存储到 SQL Server tempdb 数据库中。这是默认设置。如果将会话数据存储到 tempdb 数据库中,则在重新启动 SQL Server 时将丢失会话数据。
p 将会话数据存储到 ASPState 数据库中,而不是存储到 tempdb 数据库中。
c 将会话数据存储到自定义数据库中。如果指定 c 选项,则还必须使用 -d 选项包括自定义数据库的名称。

(2)Session客户端设置
此房是同样需要Web应用程序修改Web.Config中的<sessionState>节点.如果使用默认的数据库(ASPState库), 则配置如下:

 <sessionState
      mode="SQLServer"
      sqlConnectionString="server=192.168.9.151; uid=sa; pwd=123456;"
      />

如果使用了自定义的数据库名称,则还需要制定allowCustomSqlDatabase属性并在数据库连接串中指定数据库:
<sessionState
      mode="SQLServer"
      allowCustomSqlDatabase="true"
      sqlConnectionString="server=192.168.9.151; DataBase=MyAspState;uid=sa; pwd=123456;"
      />

六.使用经验与技巧总结
下面是SessionID, Session_End时间, StatServer模式 和 SqlServer模式的各种经验和技巧总结.
(1)StateServer模式:
1.在web farm中,请确认在所有的web服务器上有相同的<machineKey>
2. 要保存在Session中的对象是可序列化的。
3.为了在web farm中的不同web服务器上维护session state,IIS Metabase中的网站应用程序路径(如\LM\W3SVC\2)应该在所有的服务器上保持一致(大小写敏感).
4. ASP.NET处理Session是在Machine.Config中配置的HttpModuel模块, 在.NET的安装目录下的Config文件夹中, 查看Web.Config(1.1版本是在Machine.Config):
       <httpModules>
            ...            <add name="Session" type="System.Web.SessionState.SessionStateModule"/>
            ...        </httpModules>

确认此模块是否存在.
5.StateServer不支持负载均衡, 所以如果大并发推荐使用SqlServer模式, 可以享受到SqlServer的高性能和安全性.虽然存储效率会有下降.
6.需要让所有机器的MachineKey相同.在Machine.Config中配置:
<machineKey 
validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" 
decryptionKey="123456789012345678901234567890123456789012345678" 
validation="SHA1" 
decryption="Auto" 
/>

(2)SqlServer模式:
1. 要保存在Session中的对象是可序列化的。
2. 如果使用了默认是数据库, 则在客户端配置文件中的数据库链接字符串的用户,需要拥有ASPState和tempdb两个库的dbowner权限.
3. 在SQLServer模式下,session过期是由SQL Agent使用一个注册任务完成的,要确认SQL Agent已经运行。否则无法清理过期的Session数据, 会导致数据库数据一直增加.
4. 如果使用SqlServer模式时, 对于Web场中的各服务器的 ASP.NET 应用程序路径必须是相同的。请在 IIS 配置数据库中对 Web 场中的所有 Web 服务器进行 Web 站点的应用程序路径同步。大小写一定要相同,因为 Web 站点的应用程序路径是区分大小写的。
5.需要让所有机器的MachineKey相同.在Machine.Config中配置:
<machineKey 
validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" 
decryptionKey="123456789012345678901234567890123456789012345678" 
validation="SHA1" 
decryption="Auto" 
/>

(3)Session:
1. 不能直接通过Session服务器在ASP.NET和ASP之间共享Session. 请使用微软提供的解决方案:
http://msdn.microsoft.com/zh-cn/library/aa479313.aspx
2. 在不同的应用程序或一个网站的不同虚拟目录之间无法共享Session
3. Session的过期时间是滑动时间.
4. Session存储.NET自带的值类型性能最优. 存储对象会降低性能.
(4)SessionID:
1.SessionID 还可以保存在URL上, 设置Web.Config文件中的System.Web/sessionState节点的Cookiesless属性即可:
 <sessionState
      cookieless="UseUri"
      />

2. 一般在Session超时或删除之后,SessionID保持不变. 因为Session过期后会在服务器端清除数据, 但是SessionID保存在用户浏览器上, 所以只要浏览器不关闭则HTTP头中的SessionID保持不变.
3.关闭浏览器后再访问, SessionID会不同.
4.每打开一个IE6窗口, SessionID都不同, 在IE6中两个窗口的Session不能共享.
5.FireFox的标签页和新的FireFox窗口, SessionID都相同, 在FF的窗口和标签页上Session能共享.
6.对于包含FrameSet的页面,比如:
<frameset cols="25%,50%,25%">
  <frame src="SessionID.aspx">
  <frame src="SessionID.aspx">
  <frame src="SessionID.aspx">
</frameset>

如果后缀名是.htm并且.htm文件没有交给ASP.NET的ISAPI处理, 那么根据服务器速度在每个Frame页面生成不同的SessionID, 再刷新后相同都等于最后一个SessionID.
解决办法是将.htm后缀改成.aspx, 或者将.htm文件交给ASP.NET的ISAPI处理. 
(5)Session_End事件:
1. Session_End仅在InProc模式中可用
2. 关闭浏览器,Session_End是不会触发的。HTTP是一种无状态协议,服务器没有办法知道你的浏览器是否已经关闭。
3. 当Session因为时间过期或调用Session.Abandon时,Session_End才会触发.Session.Clear()仅仅是清除数据,但没有删除session。
4. Session_End由一个后台线程触发,使用工作者进程账号运行. 所以程序不会通知发生的错误.
5. 在Session_End访问数据库要考虑权限问题. Session_End是用运行工作者进程(aspnet_wp.exe)的帐号运行的,这个账号可以在machine.config中指定。因此,在Session_End中,如果使用integrity security连接SQL,它将使用工作者进程账号身份连接,这可能会引起登录失败.
6.因为Session_End是有独立线程出发的, 所以在Session_End中无法使用HttpContext对象(Request,Response,Server等对象都在HttpContext中),  即无法使用 Response.Redirect 和Server.Transfer等方法.

七.总结
我已经使用SqlServer模式对公司的多台服务器实现了Session共享, 服务器重启也不会导致用户预定过程重新开始(预定过程需要的Session不会丢失).  希望本文对具体的Session服务器搭建人员有所帮助. 

PHP 相关文章推荐
一个可以删除字符串中HTML标记的PHP函数
Oct 09 PHP
网站用php实现paypal整合方法
Nov 28 PHP
php.ini修改php上传文件大小限制的方法详解
Jun 17 PHP
Zend studio文件注释模板设置方法
Sep 29 PHP
浅析PHP微信支付通知的处理方式
May 25 PHP
php中$美元符号与Zen Coding冲突问题解决方法分享
May 28 PHP
PHP简单实现HTTP和HTTPS跨域共享session解决办法
May 27 PHP
php ajax实现文件上传进度条
Mar 29 PHP
Laravel框架实现发送短信验证功能代码
Jun 06 PHP
Laravel自定义 封装便捷返回Json数据格式的引用方法
Sep 29 PHP
浅谈Laravel POST,PUT,PATCH 路由的区别
Oct 15 PHP
PHP实现笛卡尔积算法的实例讲解
Dec 22 PHP
解析php中session的实现原理以及大网站应用应注意的问题
Jun 17 #PHP
CodeIgniter图像处理类的深入解析
Jun 17 #PHP
jQuery+php实现ajax文件即时上传的详解
Jun 17 #PHP
深入Apache与Nginx的优缺点比较详解
Jun 17 #PHP
php之CodeIgniter学习笔记
Jun 17 #PHP
apache配置虚拟主机的方法详解
Jun 17 #PHP
php unset全局变量运用问题的深入解析
Jun 17 #PHP
You might like
PHP cron中的批处理
2008/09/16 PHP
PHP判断文件是否存在、是否可读、目录是否存在的代码
2012/10/03 PHP
深入php多态的实现详解
2013/06/09 PHP
php 读取文件头判断文件类型的实现代码
2013/08/05 PHP
免费的ip数据库淘宝IP地址库简介和PHP调用实例
2014/04/08 PHP
php内存缓存实现方法
2015/01/24 PHP
php文件上传后端处理小技巧
2016/05/22 PHP
ThinkPHP 3.2.3实现加减乘除图片验证码
2018/12/05 PHP
在html页面上拖放移动标签
2010/01/08 Javascript
javascript 传统事件模型构造的事件监听器实现代码
2010/05/31 Javascript
javascript动态加载三
2012/08/22 Javascript
JS中的substring和substr函数的区别说明
2013/05/07 Javascript
在javascript中如何得到中英文混合字符串的长度
2014/01/17 Javascript
jquery实现更改表格行顺序示例
2014/04/30 Javascript
jQuery图片切换插件jquery.cycle.js使用示例
2014/06/16 Javascript
jQuery实现可关闭固定于底(顶)部的工具条菜单效果
2015/11/06 Javascript
Bootstrap基本组件学习笔记之分页(12)
2016/12/08 Javascript
详解封装基础的angular4的request请求方法
2018/06/05 Javascript
[04:44]显微镜下的DOTA2第二期——你所没有注意到的细节
2014/06/20 DOTA
python 实现归并排序算法
2012/06/05 Python
Python列表(list)、字典(dict)、字符串(string)基本操作小结
2014/11/28 Python
Django查找网站项目根目录和对正则表达式的支持
2015/07/15 Python
python利用urllib和urllib2访问http的GET/POST详解
2017/09/27 Python
Python基于mysql实现学生管理系统
2019/02/21 Python
Python2 Selenium元素定位的实现(8种)
2019/02/25 Python
用Python徒手撸一个股票回测框架搭建【推荐】
2019/08/05 Python
Python基于Twilio及腾讯云实现国际国内短信接口
2020/06/18 Python
利用Pycharm + Django搭建一个简单Python Web项目的步骤
2020/10/22 Python
一道输出判断型Java面试题
2014/10/01 面试题
高中军训感想300字
2014/03/04 职场文书
大学开学计划书
2014/04/30 职场文书
药剂专业求职信
2014/06/20 职场文书
制冷与空调专业毕业生推荐信
2014/07/07 职场文书
2014年党务工作总结
2014/11/25 职场文书
校本培训个人总结
2015/02/28 职场文书
手把手教你导入Go语言第三方库
2021/08/04 Golang