WCF4.0 –- RESTful WCF Services (实例) (并发同步服务 SyncService) 【转】

http://blog.csdn.net/fangxing80/article/details/6272886

最近写自动化测试时遇到一个问题: 我们在进行一个并发测试的过程中,需要所有客户端测试代码,在某个时机同步。回想所学到的,线程同步手段很多了,同一台PC上的进程间同步也可以通过Metux实现,多PC的时候怎么办。因为最近在学习REST WCF,自然想到它,用它来做个同步服务,即可以解决多线程,多进程,多PC同步,还可以支持跨语言,真是一举多得。(类似的解决方案还有PNUNIT,它是通过.Net Remoting实现的,因为它还要写配置,还要起Lancher/Agent,有点烦)。
1. SyncService 的主要功能——Barrier(栏栅):

借用PNUNIT的概念Barrier,也就是异步过程中的同步点,进到Barrier里的所有对象都要等待其他对象进入。这些对象可以是不同的线程,进程(不同PC,不同语言实现的客户端),过程如下图:3个客户端启动之后,有快有慢,但是在Barrier处进行一次同步,先到的等待后到的。 举个实际例子: 假如我们要实现两个客户端通信的功能的测试,必须是两个客户端同时上线。那么我们可以在代码中设计一个barrier,让双方都确认上线之后,再进行通信测试。 (1) 准备Barrier

  1. var init = SyncService.Init("Barrier_Logon", "Client1", "Client2");  
  2. // 启动Client1  
  3. Process.Start("Client1.exe");  
  4. // 启动Client2  
  5. Process.Start("Client2.exe");  

(2) Client1 

 

  1. // client1登录  
  2. var client1 = Login("Client1");  
  3. // 同步,等待Client2登录  
  4. var enter = SyncService.Enter("Barrier_Logon", "Client1");  
  5. // client1 和 client2 相互通信 ...   

(3) Client2 和 Client1 类似

  1. // client2登录  
  2. var client2 = Login("Client2");  
  3. // 同步,等待Client1登录  
  4. var enter = SyncService.Enter("Barrier_Logon", "Client2");  
  5. // client1 和 client2 相互通信 ...   


2. SyncService 的消息交换功能——SetMessage/GetMessage: 我们还可以通过SyncService中的消息容器进行消息传递。如下图: 在异步的两段代码中,设置同步点,保证 GetMessage 是在 SetMessage 之后发生。这一点是并行测试中是很常见的处理。 Client1的代码:

  1. // 设置消息给client2  
  2. var set = SyncService.SetMessage("Barrier", "key", "hello client2");  
  3. // 进入Barrier, 等待client2  
  4. var enter = SyncService.Enter("Barrier", "Client1");  

Client2的代码:

  1. // 进入Barrier,等待client1  
  2. var enter = SyncService.Enter("Barrier", "Client2");  
  3. // 取得消息  
  4. var get = SyncService.GetMessage("Barrier", "key");  
  5. // 确认获得消息,是"hello client2"  
  6. Assert.AreEqual(get, "hello client2");  


3. SyncService的实现 如果上面的并行处理代码理解了的话,SyncService的实现就很好推断出来了。服务端维护一个Dictionary<string, SyncGroup>的容器,每个客户端Enter时,调用对应的ManualResetEvent.Set()解锁。然后WaitAll其他的ManualResetEvent,从而实现同步。

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.ServiceModel;  
  5. using System.ServiceModel.Activation;  
  6. using System.ServiceModel.Web;  
  7. using System.Text;  
  8. using System.Threading;  
  9. using System.Runtime.Serialization;  
  10.   
  11. namespace SyncService  
  12. {  
  13.     [ServiceContract]  
  14.     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]  
  15.     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]  
  16.     public class SyncService  
  17.     {  
  18.         private static Dictionary<string, SyncGroup> _syncPool = new Dictionary<string, SyncGroup>();  
  19.   
  20.         [WebGet(UriTemplate="Init/{barrier}/{targetnames}")]  
  21.         public string Init(string barrier, string targetnames)  
  22.         {  
  23.             var ctx = WebOperationContext.Current;  
  24.             try  
  25.             {  
  26.                 lock (_syncPool)  
  27.                 {  
  28.                     _syncPool[barrier] = new SyncGroup();  
  29.                     var syncGroup = _syncPool[barrier];  
  30.                     var targets = targetnames.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);  
  31.                     Array.ForEach(targets, t => syncGroup.ResetEventDict.Add(t, new ManualResetEvent(false)));  
  32.                 }  
  33.                 return "ok";  
  34.             }  
  35.             catch (Exception ex)  
  36.             {  
  37.                 return ex.Message;  
  38.             }  
  39.         }  
  40.   
  41.         [WebGet(UriTemplate = "Enter/{barrier}/{targetname}/{timeout=60000}")]  
  42.         public string Enter(string barrier, string targetname, string timeout)  
  43.         {  
  44.             var ctx = WebOperationContext.Current;  
  45.             try  
  46.             {  
  47.                 var syncObj = _syncPool[barrier];  
  48.                 var target = syncObj.ResetEventDict[targetname];  
  49.                 target.Set();  
  50.                 var intTimeout = int.Parse(timeout);  
  51.                 var success = WaitHandle.WaitAll(syncObj.ResetEventDict.Values.ToArray(), intTimeout);  
  52.                 if (success)  
  53.                     return "ok";  
  54.                 else  
  55.                     return "timeout";  
  56.             }  
  57.             catch (Exception ex)  
  58.             {  
  59.                 return ex.Message;  
  60.             }  
  61.         }  
  62.   
  63.         [WebGet(UriTemplate = "SetMessage/{barrier}/{key}/{message=null}")]  
  64.         public string SetMessage(string barrier, string key, string message)  
  65.         {  
  66.             var ctx = WebOperationContext.Current;  
  67.             try  
  68.             {  
  69.                 var syncObj = _syncPool[barrier];  
  70.                 lock (syncObj)  
  71.                 {  
  72.                     var query = syncObj.Messages.FirstOrDefault(m => m.Key == key);  
  73.                     syncObj.Messages.Remove(query);  
  74.                     var messageInfo = new MessageInfo  
  75.                     {  
  76.                         BarrierName = barrier,  
  77.                         Key = key,  
  78.                         Message = message,  
  79.                         UpdateDateTime = DateTime.Now  
  80.                     };  
  81.                     syncObj.Messages.Add(messageInfo);  
  82.                 }  
  83.                 return "ok";  
  84.             }  
  85.             catch (Exception ex)  
  86.             {  
  87.                 return ex.Message;  
  88.             }  
  89.         }  
  90.   
  91.         [WebGet(UriTemplate = "GetMessage/{barrier}/{key}")]  
  92.         public string GetMessage(string barrier, string key)  
  93.         {  
  94.             var ctx = WebOperationContext.Current;  
  95.             try  
  96.             {  
  97.                 var syncObj = _syncPool[barrier];  
  98.                 var query = syncObj.Messages.FirstOrDefault(m => m.Key == key);  
  99.                 return query.Message;  
  100.             }  
  101.             catch (Exception ex)  
  102.             {  
  103.                 return ex.Message;  
  104.             }  
  105.         }  
  106.   
  107.         [WebGet(UriTemplate = "ListMessages/{barrier=all}", ResponseFormat=WebMessageFormat.Xml)]  
  108.         public List<MessageInfo> ListMessages(string barrier)  
  109.         {  
  110.             var ctx = WebOperationContext.Current;  
  111.             try  
  112.             {  
  113.                 var messages = new List<MessageInfo>();  
  114.                 if (barrier == "all")  
  115.                     _syncPool.Values.ToList().ForEach(t => messages.AddRange(t.Messages));  
  116.                 else  
  117.                     messages = _syncPool[barrier].Messages;  
  118.                 return messages;  
  119.             }  
  120.             catch  
  121.             {  
  122.                 return null;  
  123.             }  
  124.         }  
  125.   
  126.         [WebGet(UriTemplate="Check", ResponseFormat=WebMessageFormat.Xml)]  
  127.         public string Check()  
  128.         {  
  129.             return "Welcome to the SyncService! " +   
  130.                 DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();  
  131.         }  
  132.               
  133.     }  
  134.   
  135.     [DataContract]  
  136.     [KnownType(typeof(MessageInfo))]  
  137.     public class SyncGroup  
  138.     {  
  139.         internal Dictionary<string, ManualResetEvent> ResetEventDict { get; set; }  
  140.         [DataMember]  
  141.         public string Name { get; set; }     
  142.         [DataMember]  
  143.         public List<MessageInfo> Messages { get; set; }  
  144.         [DataMember]  
  145.         public Dictionary<string, string> States { get; set; }  
  146.   
  147.         public SyncGroup()  
  148.         {  
  149.             Messages = new List<MessageInfo>();  
  150.             ResetEventDict = new Dictionary<string, ManualResetEvent>();  
  151.         }  
  152.     }  
  153.   
  154.     [DataContract]  
  155.     public class MessageInfo  
  156.     {  
  157.         [DataMember]  
  158.         public string BarrierName { get; set; }  
  159.         [DataMember]  
  160.         public string Key { get; set; }  
  161.         [DataMember]  
  162.         public string Message { get; set; }  
  163.         [DataMember]  
  164.         public string Identity { get; set; }  
  165.         [DataMember]  
  166.         public DateTime UpdateDateTime { get; set; }  
  167.     }  
  168. }  


默认使用JSON格式,另外为了查看当前的同步的状况和消息,可以通过 ListStates/ListMessages 查看。 (1) 初始化Barrier则发送: http://server/SyncService/Init/MyBarrier/Client1,Client2 (2) 客户端进入Barrier则发送: http://server/SyncService/Enter/MyBarrier/Client1/10000 (最后是timeout设定) (3) 设置消息则发送: http://server/SyncService/SetMessage/MyBarrier/Key/MessageContent (4) 取得消息则发送: http://server/SyncService/GetMessage/MyBarrier/Key (5) 查看所有的“锁”则发送:http://server/SyncService/ListStates(或者指定某个Barrier: /MyBarrier) (6) 查看所有的消息则发送:http://server/SyncService/ListMessages(或者指定某个Barrier: /MyBarrier) (7) 清空所有SyncGroup则发送:http://server/SyncService/Restart
是的,全部的操作全部是 HttpRequest 的"GET", 因此各种客户端都可以轻松调用,很方便。 (用WCF创建这样一个服务也非常简单全部代码一百多行,正所谓天下武功无快不破:)

posted @ 2016-02-02 16:30  J.Y  阅读(187)  评论(0编辑  收藏  举报