阅读62 返回首页    go 阿里云 go 技术社区[云栖]


WCF后续之旅(9):通过WCF的双向通信实现Session管理[上篇]

一、Session 管理提供的具体功能

  • Start/End Session:可以调用service开始一个新的Session或者结束掉一个现有的Session。当开始一个Session的时候,service根据client端传入的client相关的信息(ClientInfo),创建一个SessionInfo对象,该对象由一个GUID类型的SessionID唯一标识,代表一个具体的Client和Service的Session。在service端,通过一个dictionary维护者一个当前所有的active session列表,key为SessionID,value是SessionInfo对象。当client调用相应的service,传入对应的SessionID,该SessionID对应的SessionInfo从该session列表中移除。
  • Session Timeout:如同ASP.NET具有一个Timeout的时间一样,我们的例子也具有timeout的机制。在client可以注册timeout事件,某个session timeout,service会通过在start session中指定的callback回调相应的操作(OnTimeout)并处罚client注册的timeout事件。session timeout后,SessionInfo对象从active session列表中移除。 比如在本例中,我们通过注册事件使得timeout后,程序在显示timeout message之后,自动退出。
  • Session Renew:session timeout判断的依据是client最后活动的时间(last activity time),而该事件反映的是最后一次鼠标操作的时间(假设我们的client是一个GUI应用)。所以从session的生命管理来讲,用户的每次鼠标操作实际上将session的时间延长到session timeout的时间。
  • Session Listing Viewing:Administrator或者某个具有相应权限的用户,可以查看当前活动的session列表和session相关的信息,比如IP地址、主机名称、用户名、session开始的时间和最后一次活动的时间,见下图。
  • Session Killing:如何发现某个用户正在做一些不该做的事情,或者发现当前的并发量太大,管理员可以强行杀掉某个正在活动的Session。同session timeout一样,client端可以注册session killed事件。当session被强行中止后,service回调client相应的方法(OnSessionKilled),触发该事件。比如在本例中,我们通过注册事件使得某个client对应的session被杀掉后,该client程序在显示message之后,自动退出。

wcf_02_09_01_thumb1

二、Session Timeout的实现原理

1、客户端驱动

image_thumb1

2、服务端驱动

System.Runtime.Remoting.Lifetime.ISponsor)引用lease对象. 当Lease Manager检测到某个remote object的lease超时,Remoting不会马上对其进行垃圾回收,而是找到该lease的Sponsor对象,通过Sponsor对象回调Renewal方法(Sponsor处于client端),返回一个Timespan对象,表明需要将remote object的lifetime延长的时间,如何该值小于或者等于零,则不需要延长,该对象将会被回收掉;否则将lifetime延长至相应的时间。同时,client的每次远程调用,都会自动实现对lifetime的Renew功能。(详细内容可以参考我的文章:[原创]我所理解的Remoting (2) :远程对象的生命周期管理-Part II)

image_thumb3

步骤一

步骤二

步骤三

步骤四

步骤五

注:可能有人会说,为什么不将LastActivityTime返回到service端,service将session的LastActivityTime设定成该值就可以了呀?实际上,这样做依赖于这样的一个假设:client端的时间和server端的时间是一致的。很显然,我们不能作出这样的假设。

三、整个应用的结构

wcf_02_09_02_thumb1

   1: namespace Artech.SessionManagement.Contract
   2: {
   3:     [DataContract]
   4:     public class SessionClientInfo
   5:     {
   6:         [DataMember]
   7:         public string IPAddress{ get; set; } 
   8:  
   9:         [DataMember]
  10:         public string HostName{ get; set; } 
  11:  
  12:         [DataMember]
  13:         public string UserName{ get; set; } 
  14:  
  15:         [DataMember]
  16:         public IDictionary<string, string> ExtendedProperties{ get; set; }
  17:     }
  18: } 

   1: namespace Artech.SessionManagement.Contract
   2: {
   3:     [DataContract]
   4:     [KnownType(typeof(SessionClientInfo))]
   5:     public class SessionInfo
   6:     {
   7:         [DataMember]
   8:         public Guid SessionID{ get; set; } 
   9:         [DataMember]
  10:         public DateTime StartTime{ get; set; } 
  11:         [DataMember]
  12:         public DateTime LastActivityTime{get;set;} 
  13:         [DataMember]
  14:         public SessionClientInfo ClientInfo{ get; set; } 
  15:         public bool IsTimeout{ get; set; }
  16:     }
  17: } 
  18:  

   1: namespace Artech.SessionManagement.Contract
   2: {    
   3:     public interface ISessionCallback
   4:     {
   5:         [OperationContract]
   6:         TimeSpan Renew(); 
   7:  
   8:         [OperationContract(IsOneWay = true)]
   9:         void OnSessionKilled(SessionInfo sessionInfo); 
  10:  
  11:         [OperationContract(IsOneWay = true)]
  12:         void OnSessionTimeout(SessionInfo sessionInfo);
  13:     }
  14: } 

   1: namespace Artech.SessionManagement.Contract
   2: {
   3:     [ServiceContract(CallbackContract = typeof(ISessionCallback))]
   4:     public interface ISessionManagement
   5:     {
   6:         [OperationContract]
   7:         Guid StartSession(SessionClientInfo clientInfo, out TimeSpan timeout); 
   8:  
   9:         [OperationContract]
  10:         void EndSession(Guid sessionID); 
  11:  
  12:         [OperationContract]
  13:         IList<SessionInfo> GetActiveSessions(); 
  14:  
  15:         [OperationContract]
  16:         void KillSessions(IList<Guid> sessionIDs);
  17:     }
  18: }

具体实现请参阅Part II.

 

 WCF后续之旅: 

WCF后续之旅(1): WCF是如何通过Binding进行通信的 
WCF后续之旅(2): 如何对Channel Layer进行扩展——创建自定义Channel 
WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher 
WCF后续之旅(4):WCF Extension Point 概览 
WCF后续之旅(5): 通过WCF Extension实现Localization 
WCF后续之旅(6): 通过WCF Extension实现Context信息的传递 
WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成 
WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成 
WCF后续之旅(9):通过WCF的双向通信实现Session管理[Part I] 
WCF后续之旅(9): 通过WCF双向通信实现Session管理[Part II] 
WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance 
WCF后续之旅(11): 关于并发、回调的线程关联性(Thread Affinity) 
WCF后续之旅(12): 线程关联性(Thread Affinity)对WCF并发访问的影响 
WCF后续之旅(13): 创建一个简单的WCF SOAP Message拦截、转发工具[上篇] 
WCF后续之旅(13):创建一个简单的SOAP Message拦截、转发工具[下篇] 
WCF后续之旅(14):TCP端口共享 
WCF后续之旅(15): 逻辑地址和物理地址 
WCF后续之旅(16): 消息是如何分发到Endpoint的--消息筛选(Message Filter) 
WCF后续之旅(17):通过tcpTracer进行消息的路由


作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及着作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文链接

最后更新:2017-10-30 17:04:42

  上一篇:go  一个通过BackgroundWorker实现WinForm异步操作的例子
  下一篇:go  WCF后续之旅(9): 通过WCF双向通信实现Session管理[下篇]