你的位置:首页 > ASP.net教程

[ASP.net教程]说一说sateless的那些事~


前段时间 工作需要 生平第一次听到“无状态”一词 随后 了解了下 怕自己忘记 随手记录下来 

/*

尚未创建过Session:
InitializeRequest -> CreateNewStoreData

InitializeRequest -> ResetItemTimeout -> InitializeRequest -> GetItemExclusive
(一种假象:http://localhost:29566和http://localhost:29566/Default.aspx)

 

已经创建过Session:

InitializeRequest -> GetItemExclusive
但如果没查到 会 CreateUninitializedItem -> GetItemExclusive(不管经过是什么,都会进入Page_Load)

 

--------------Page_load--------------
EndRequest(尚未创建过Session)
ReleaseItemExclusive -> EndRequest(已经创建过Session,此次无Update)
SetAndReleaseItemExclusive -> EndRequest (已经创建过Session,且此次有Update)

 


HttpModule -> RedisSession(InitializeRequest -> ResetItemTimeout只会在进入HttpHandle前调用这两个方法) -> HttpHandle
不要再HttpModule、HttpHandle中操作Session
HttpModel是因为调用顺序问题,HttpHandle是因为就算更新了,也不会进行被同步到Redis
好吧,其实这两个的context.Session 都是null

 

Media资源(图片、css、script)是不会进来的

 

*/

 

SessionStateStoreProviderBase类

定义一个数据存储会话状态所需的成员

Namespace:   System.Web.SessionState
Assembly:  System.Web (in System.Web.dll)

1.初始化每个请求对象

public abstract void InitializeRequest(HttpContext context)

Redis:
/// <summary>    /// 在异步回发初始化期间提出    /// </summary>    /// <param name="context"></param>    public override void InitializeRequest(HttpContext context)    {      if (context != null && context.Request != null)      {        LogInfoFormat("InitializeRequest invoking: Cookie.ASP.NET_SessionId:{0},RequestRawUrl:{1}"          , context.Request.Cookies != null && context.Request.Cookies["ASP.NET_SessionId"] != null ? context.Request.Cookies["ASP.NET_SessionId"].Value : ""          , context.Request.RawUrl          );      }    }

 

2.创建一个新的对象用对当前的请求

public abstract SessionStateStoreData CreateNewStoreData(HttpContext context,int timeout)


Redis:
 public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) {   return new SessionStateStoreData(new SessionStateItemCollection(),staticObjectsGetter(context),timeout); }

 

3.更新此次会话的过期时间

public abstract void ResetItemTimeout(HttpContext context,string id)

Redis:

    public override void ResetItemTimeout(HttpContext context, string id)    {      //throw new NotImplementedException();    }

4.从会话状态存储中返回只读会话状态

public abstract SessionStateStoreData GetItemExclusive(HttpContext context,string id,out bool locked,out TimeSpan lockAge,out object lockId,out SessionStateActions actions)

Redis:

public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)    {      LogInfoFormat("GetItemExclusive invoking: id:{0},RequestRawUrl:{1}"        , id        , context != null && context.Request != null ? context.Request.RawUrl : ""        );      //读取redis,从redis获取session      return GetItemFromSessionStore(true, context, id, out locked, out lockAge, out lockId, out actions);    }/// <summary>/// 从Redis读取/// </summary>/// <param name="isWriteLockRequired"></param>/// <param name="context"></param>/// <param name="id"></param>/// <param name="locked"></param>/// <param name="lockAge"></param>/// <param name="lockId"></param>/// <param name="actions"></param>/// <returns></returns>private SessionStateStoreData GetItemFromSessionStore(bool isWriteLockRequired, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions){  locked = false;  lockAge = TimeSpan.Zero;  lockId = null;  actions = SessionStateActions.None;  SessionStateStoreData result = null;  var key = GetSessionIdKey(id);  var stateRaw = RedisHelper.RedisDB.HashGetAll(key);  RedisSessionState state;  if (!RedisSessionState.TryParse(stateRaw, out state))  {    locked = false; //如果在数据存储区中未找到会话项数据,GetItemExclusive 方法就会将 locked out 参数设置为 false 并返回 null    return null; //CreateUninitializedItem -> GetItemExclusive  }  actions = state.Flags;  if (state.Locked)  {    locked = true; //如果在数据存储区中找到会话项数据但是数据被锁定,GetItemExclusive 方法就会将 locked out 参数设置为 true,将 lockAge out 参数设置为当前日期和时间与项被锁定时的日期和时间(从数据存储区中检索)的差值,将 lockId out 参数设置为从数据存储区中检索的锁定标识符,并返回 null    lockId = state.LockId;    //lockAge = new TimeSpan((DateTime.UtcNow - state.LockDate).Ticks * 7);//ExecutionTimeout有点长,100s左右    lockAge = DateTime.UtcNow - state.LockDate;    return null;  }  if (isWriteLockRequired)  {    locked = state.Locked = true;    state.LockDate = DateTime.UtcNow;    lockAge = TimeSpan.Zero;    lockId = ++state.LockId;  }  state.Flags = SessionStateActions.None;  UpdateSessionState(key, state);  var items = actions == SessionStateActions.InitializeItem ? new SessionStateItemCollection() : state.Items;  result = new SessionStateStoreData(items, staticObjectsGetter(context), state.Timeout);  return result;}

5.添加一个新的会话状态项到会话存储里

public abstract void CreateUninitializedItem(HttpContext context,string id,int timeout)

Redis:

// GetItemExclusive如果没有查到,则会调用该方法(非因为Locked return null) public override void CreateUninitializedItem(HttpContext context, string id, int timeout)    {      var key = GetSessionIdKey(id);      var state = new RedisSessionState()      {        Timeout = timeout,        Flags = SessionStateActions.InitializeItem      };      UpdateSessionState(key, state);    } private void UpdateSessionState(string key, RedisSessionState state)  {    UseTransaction(transaction =>    {      transaction.HashSetAsync(key, state.ToHashEntryArr());      transaction.KeyExpireAsync(key, TimeSpan.FromMinutes(state.Timeout));    });  }

6.一个请求结束后回传

public abstract void EndRequest(HttpContext context)

Redis:

    public override void EndRequest(HttpContext context)    {      //throw new NotImplementedException();    }

7.释放会话状态存储中的锁定

public abstract void ReleaseItemExclusive(HttpContext context,string id,object lockId)

Redis:

  
///1、一次Http请求处理完之后(在Page_Load 及 aspx之后),如果没有发生了Session Update,则会调用此方法///2、Page_Load之前,如果反复执行GetItemExclusive始终是locked状态,一段时间之后系统会自行调用此方法来尝试释放.///  如果 SessionStateModule 对象在调用 GetItemExclusive 或 GetItem 方法期间遇到锁定的会话数据,它将以半秒为间隔重新请求会话数据,
/// 直到锁定被释放或者会话数据的锁定时间超过 ExecutionTimeout 属性的值。如果超过了执行超时时间,SessionStateModule 对象将调用 ReleaseItemExclusive 方法以释放会话存储区数据并随即请求会话存储区数据public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) { UnlockSessionStateIfLocked(id, (int)lockId); }     /// <summary> /// 释放会话存储区数据并随即请求会话存储区数据 /// </summary> /// <param name="id"></param> /// <param name="lockId"></param> private void UnlockSessionStateIfLocked(string id, int lockId) { var key = GetSessionIdKey(id); var stateRaw = RedisHelper.RedisDB.HashGetAll(key); RedisSessionState state; if (RedisSessionState.TryParse(stateRaw, out state) && state.Locked && state.LockId == lockId) { UnlockSessionState(key, state.Timeout); } } /// <summary> /// 不知道这样 对比 UpdateSessionState, 性能怎么样。这里虽然分成多项,但会作为一个transaction提交 /// </summary> /// <param name="client"></param> /// <param name="key"></param> /// <param name="state"></param> private void UnlockSessionState(string key, int timeout) { UseTransaction(transaction => { transaction.HashSetAsync(key, "locked", false); transaction.HashSetAsync(key, "lockId", 0); transaction.HashSetAsync(key, "lockDate", 0); //transaction.HashSetAsync(key, "timeout", timeout); //没必要 transaction.KeyExpireAsync(key, TimeSpan.FromMinutes(timeout)); }); } /// <summary> /// /// </summary> /// <param name="action"></param> private void UseTransaction(Action<ITransaction> action) { try { //Redis的更新 都会进这 var transaction = RedisHelper.RedisDB.CreateTransaction(); action(transaction); transaction.Execute(); } catch (Exception ex) { LogError(ex.Message, ex); } }

8.在更新会话状态数据存储与当前请求的值的会话项信息,并清除对数据的锁定

public abstract void SetAndReleaseItemExclusive(HttpContext context,string id,SessionStateStoreData item,object lockId,bool newItem)

Redis:

/// 一次Http请求处理完之后(在Page_Load 及 aspx之后),如果发生了Session Update,则会调用此方法public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)    {      if (newItem) //true:定义一个新的状态      {        var state = new RedisSessionState()        {          Items = (SessionStateItemCollection)item.Items,          Timeout = item.Timeout,        };        var key = GetSessionIdKey(id);        UpdateSessionState(key, state);      }      else //false 已存在的状态      {        UpdateSessionStateIfLocked(id, (int)lockId, state =>        {          state.Items = (SessionStateItemCollection)item.Items;          state.Locked = false;          state.Timeout = item.Timeout;        });      }    }     /// <summary>    ///     /// </summary>    /// <param name="id"></param>    /// <param name="lockId"></param>    /// <param name="stateAction"></param>    private void UpdateSessionStateIfLocked(string id, int lockId, Action<RedisSessionState> stateAction)    {      var key = GetSessionIdKey(id);      var stateRaw = RedisHelper.RedisDB.HashGetAll(key);      RedisSessionState state;      if (RedisSessionState.TryParse(stateRaw, out state) && state.Locked && state.LockId == lockId)      {        stateAction(state);        UpdateSessionState(key, state);      }    }     /// <summary>    ///     /// </summary>    /// <param name="client"></param>    /// <param name="key"></param>    /// <param name="state"></param>    private void UpdateSessionState(string key, RedisSessionState state)    {      UseTransaction(transaction =>      {        transaction.HashSetAsync(key, state.ToHashEntryArr());        transaction.KeyExpireAsync(key, TimeSpan.FromMinutes(state.Timeout));      });    }     /// <summary>    ///     /// </summary>    /// <param name="action"></param>    private void UseTransaction(Action<ITransaction> action)    {      try      {        //Redis的更新 都会进这        var transaction = RedisHelper.RedisDB.CreateTransaction();        action(transaction);        transaction.Execute();      }      catch (Exception ex)      {        LogError(ex.Message, ex);      }    }

 

 

 未完,待续...