分布式应用首先要解决的是跨域的问题,解决session、frame、cookie的跨域是最基本的,然后才是负载均衡和性能优化,上面的不解决就没法往后面进行。上一博客主要是解决了frame跨域的问题,今天来解决session跨域的问题,如何让session共享。其实解决session共享的方案有好多,比如通过数据库像redis、sqlserver,或者用session服务器,今天主要是通过sql server来实现session共享。
一、什么是cookie?
大家平时在登录网页时有一个记住用户名的选项,等下次登录会填充用户名,甚至有的会自动填充用户名和密码,这样就会很方便用户。那它是怎么实现的呢?其实很简单就是通过cookie保存在浏览器中,如下图,这是163.com存在谷歌浏览器中的cookie,用户在浏览器输入url时,浏览器会将该域名下存储的cookie放在http的head中发送到服务器(浏览器不能把其他域名的cookie也发送到服务器,不然这就乱套了,这么多cookie,什么还有重名的,于是乎这就产生了cookie的跨域,关于cookie的跨域请听下文分解。)然后服务器给控件赋值,于是就达到了自动填充的效果。可能会觉得这样不安全,人家能监听到你的cookie信息,比如用户名密码这些,确实,这也是有一定的弊端,有的做的完善的会进行一些加密,但加密也是需要消耗额外的资源的,所以都是有利有弊吧。而且最重要的是浏览器每次请求都会将对应的cookie发送给服务器,服务器又会把cookie响应给客户端,cookie内容多的时候,可想而知,系统就会变慢,所以性能优化有一条就是对cookie优化,性能优化又是一个新的大陆。
二、session和cookie的关系
关于什么是session,对我来说没有十万个为什么也有一二十个。它是怎么来的又是怎么没的,HTTP本身是无状态的,客户端和服务端又是怎么个的眉来眼去的让服务器知道是同一个用户的操作呢?每个用户登录之后都会在服务器保存session["UserName"],那这么多的session["UserName"],怎么确保A用户获取的session["UserName"]就是它本人的,不是其他用户的呢?对于每次请求都会实例化一个session对象,这个session对象有一个sessionId,当用户设置session["UserName"]后,那这个session对象就会存在服务器中,如果session对象中没有那就会被销毁。保存在服务器中之后,响应给浏览器时会把这个session对象对应的sessionid放在cookie中,这样浏览器下次再请求时就会把这个sessionid带上,然后就能根据sessid找到这个session对象了,而不至于找到别的。在下图可以找到sessionID,设置的session["UserName"]放在session对象的keys熟悉中。Mode时InProc模式,就是放在IIS中。
三、Session共享设置
要想共享,多个应用用一份,那就得先把session分离出来,不然怎么做到共享。
1.启动ASP.NET State Service服务
2.在数据库服务器执行sql
sql文件在C:WindowsMicrosoft.NETFramework64v4.0.30319InstallSqlState.sql,InstallSqlState.sql是注册,UninstallSqlState.sql是取消。执行完之后只是创建了ASPState数据库,并未创建表如下图,还提示我使用aspnet_regsql.exe去install sql session state。以及启动SQLServer Agent.
3.启动SQLServerAgent
4.注册SQL Session State
原本想着直接运行aspnet_regsql.exe注册,但打开之后提示要用命令行,已经提示用此向导创建或配置成员、角色 事件的,对于回话状态用命令行。
于是乎根据提示cmd命令行 aspnet_regsql.exe -?出现下面的使用方法
由于是我在本地的数据库上所以执行的是我本地的数据库服务器的名字:aspnet_regsql.exe -S PC-201611282159 -E -ssadd -sstype p
注册完成之后ASPState数据库就存在了两张表:ASPStateTempApplications,ASPStateTempSessions
从表结构能看出来有appid和appname,这就导致了默认是一个应用一个id和name,那这样是共享不了的,怎么共享呢?其实也比较容易,只需需改下存储过程TempGetAppID中的WHERE AppName = @appName注释掉,然后重启就好了。
四、demo
这里我创建了两个应用,一个是WebForm的,一个是MVC的,在WebForm中设置session,然后在MVC中获取session。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WebForm { public partial class Index : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { Session["UserName"] = "CuiYW"; HttpCookie cookie = new HttpCookie("UserInfo"); Request.Cookies.Add(cookie); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MVC.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { if (Session["UserName"] != null) { ViewBag.UserName = Session["UserName"]; } return View(); } } }
同时要在webconfig的<system.web>中都要配置
<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="server=PC-201611282159; database=ASPState;Trusted_Connection=true;" timeout="20" >
先运行WebForm设置session,然后运行MVC,在View中显示ViewBag.UserName.结果是可以获取到,这就意味着实现了共享。
五、总结
上面主要是利用SessionStateMode的SQLServer来实现session共享,这毕竟是微软的,所以具有一定的局限行,只能是sql server。其实session共享可以用其他的数据库,比如redis,而且一般实际应用中也不会只用一台做服务器,最少有一台做备份。还有就是单点登录的方式有好几个,session共享也只是其中一个。这些都会在以后的博客中一一写到。reids实现session、SSO是我最近的重点。之前对于分布式应用可能也没多少实战,可能主要还是懒,用不到就是了解了解看看书,只有理论没有实践,通过这次将WebForm集成到MVC中,算是动手了一下,也让自己学到很多东西。最后想吐槽一下,写博客真不容易,不仅要写还要边写边操作,为了这篇搞了一上午,下午午休之后又开始搞,直接搞到现在。生活不易啊!!!