程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF從理論到實踐(8):事件廣播

WCF從理論到實踐(8):事件廣播

編輯:關於.NET

上文討論了WCF中三種消息交換模式,one-way,request/reply,duplex。前兩項比較簡單,無需多言,duplex相對比較復雜,上文只是實現了簡單的回調,在真正應用的時候,還有許多值得注意之處,本文就結合一個實際的應用例子來談論下duplex的具體應用和非常值得我們注意的地方。

本文的出發點

通過閱讀本文,您能理解以下知識:

如何實現一個基於duplex的事件廣播

解析在實現duplex事件廣播中的幾個問題

初步探討一下異步

本文適合的讀者

本文屬於中等難度的文章,需要有WCF消息交換和windows應用程序開發相關的基礎知識,有關WCF消息交換,請閱讀WCF從理論到實踐(7):消息交換模式

如何實現一個基於duplex的事件廣播

在討論如何實現之前,先看一下本文的范例所要實現的功能是什麼?本文的范例實現了一個簡單的分布式任務管理系統,簡單的說,它是在服務端(Server Point)執行任務(Job),並且將任務的信息呈現給客戶端。它有如下特征:

通過調用服務端的Accept(),客戶端能連接上服務端,並保持會話。

客戶端在啟動的時候,可以通過遠程調用GetJobs()來獲取當前服務端中全部的任務,並將這些任務在客戶端窗體中用列表控件呈現出來

客戶端能通過調用AddJob()向服務端添加任務,當服務端完成添加操作之後,引發添加完成的事件,並向全部的客戶端廣播該事件

當客戶端服務端發來的添加新任務事件廣播的時候,客戶端將新增任務添加到列表控件加以呈現

客戶端可以命令服務端執行具體某個任務,當任務在開始執行和執行結束後,服務端都會像全部客戶端廣播任務的執行情況,並且任務的執行和事件的廣播異步執行

客戶端收到廣播後,便可以更新任務信息。

和以前文章不同,本文先給出最後實現的效果

如何您要了解該范例得具體設計和實現,可以下載下面的文件進行分析:

范例最終實現:/Files/jillzhang/Jillzhang.Event.rar

我這裡只列出范例中項目列表

項目名稱

項目描述

Jillzhang.Event.Core

該項目用於定義WCF的契約,主要包括IServer服務契約,ICallback用於回調的服務契約,Job數據契約

Jillzhang.Event.Service

服務端的具體實現,其中Server實現了一個有廣播事件能力的服務契約

Jillzhang.Event.Host

服務的宿主程序,一個ConsoleApplication

Jillzhang.Event.Client

客戶端實現,用於消費服務端。

Jillzhang.Event.Client2

和Jillzhang.Event.Client是一個實現,但為了驗證廣播,可與Jillzhang.Event.Client同時消費服務端

解析在實現duplex事件廣播中的幾個問題

1) Duplex模式對服務行為ConcurrencyMode的要求

我們知道ConcurrencyMode是控制服務並發的,默認情況下ConCurrendMode的值為Single,它設置服務運行在單線程下,當上一個請求未完成之前,服務是不接受下一個請求的。而duplex在進行回調的時候,如果回調方法沒有被設置為 One-Way的交換模式,服務端是會等待客戶端對回調的響應的,這可不是一件好事情,因為服務端並不能保證客戶端能正常地執行回調並返回數據。更多的情況下,我們期望回調在發出後能立即返回,方法有兩個:a)將回調方法設置為One-Way交換模式 b)采用多線程。經過我的測試,當回調方法被設置了one-way模式後,將ConcurrencyMode設置為Single是可以實現duplex雙向通訊的。 要第二種方法也非常簡單,只需要將ConcurrencyMode設置為Mutiple.此時即使回調方法不是one-way模式,也是可以完成duplex的。值得說明一下的是ConcurrencyMode還有中性的屬性:ConcurrencyMode.Reentrant,說句心裡話,我不喜歡這個不倫不類的家伙,他能實現在單線程下同時接受多個請求,但有利必有弊,這個家伙不能保證請求事務的完整性,使用的時候應該謹慎。

2)InstanceContextMode = InstanceContextMode.PerSession卻為何能實現廣播?

如果將InstanceContextMode設置為PerSession,我們知道服務端對象是針對每一個會話的,也就是說每個會話會產生一個對象實例,這樣如果要實現廣播,我們必須將當前服務包含的會話信息用一個列表對象記錄下來,廣播的時候,我們遍歷會話列表,進行逐個回調。本示例中巧妙的利用了Event可包括多個委托實例的特征,一個靜態的Event對象針對每個會話創建一個委托實例便可以完成上述的要求。遍歷回調的方法便可以編寫如下:

private void BroadcastEvent(CallbackEventArg e, ServerEventHanlder temp)
    {
      if (OnStatusChanged != null)
      {
        foreach (ServerEventHanlder handler in temp.GetInvocationList())
        {
          handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
        }
      }
    }

3)困擾了我半天的問題

上篇文章中,已經對duplex有了初步的認識,本以為本文的范例實現會很順利呢,可有一個問題卻困擾了我半天,在回調的時候非常不穩定,有時能回調4.5次,有時1,2次之後,再回調卻沒了響應,開始百思不得其解,因為開始幾次可成功回調,為何會不穩定呢?經過好一番嘗試,也沒能解決,回調沒有響應,肯定是客戶端與服務端失去了連接,會話過期就會造成雙方通訊連接的中斷,經過分析,我的系統是這樣的

會不會在Accept後Do()方法前的過程中會話過期了呢?後來經過驗證,的確是此處的問題,解決方法是通過設置操作契約的IsTerminating來實現會話的維護,當一個操作契約的IsTerminating被設置為false的時候,該操作不會導致會話的中斷,將IServer設計如下便解決了我的問題 :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace Jillzhang.Event.Core
{
  [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallback))]
  public interface IServer
  {
    [OperationContract(IsOneWay = true, IsInitiating = true, IsTerminating = false)]
    void Accept();

    [OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=false)]
    void Do(string jobName);

    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void AddJob(Job job);

    [OperationContract(IsOneWay=false,IsInitiating=false,IsTerminating=false)]
    List<Job> GetJobs();
  }
}

初步探討一下異步

進程間通訊是一件很耗時的事情,如果同步執行會造成線程的阻塞,如果是在服務端,會降低服務的處理能力(這種說法可能有些問題,我會進一步求證,估計保留,經過查證多線程在服務端的好處在於提供對單個請求用多個線程處理的能力,從而防止完成一個請求之前,無法接受新的請求),如果是在客戶端,會給用戶帶來不好的體驗。下面就分別探討一下如何實現服務端和客戶端的異步。

在服務端,一個事件的異步可以通過delegate的BeginInvoke和EndInvoke來實現,具體方法可以參見示例項目Jillzhang.Event.Service中的Server對象的方法BroadcastEvent方法的實現

而在客戶端,我們可以起多個線程,當然最方便快捷的辦法就是使用BackGroundWorker後台線程來處理耗時比較長的操作了,具體實現也可以參考Jillzhang.Event.Client項目中的Form1.cs實現。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved