天天看點

Azure Services Bus(服務總線)中的工作流(workflow)

     在Azure Services Platform上對于工作流服務的支援,一直是我很感興趣的内容。當然也是疑問

比較多的領域。鑒于這方面的資料太少,是以今天就從AzureServicesKit中的一個DEMO出發,來大概

了解一下這方面相關内容。

     注:今天的示例位于AzureServicesKit安裝目錄\Labs\Ex02-RoutingWithXPath\end檔案夾。

     該示例場景展示的是一個定單(order)流程,如下圖:

Azure Services Bus(服務總線)中的工作流(workflow)

     注:圖中的兩個服務可能布置在1台或N台機器上。

     在上圖中,我們看出在目前場景中存在兩個服務,即:

    BillService(即定單生成)。

    ShipOrderService(定單處理:包括處理定單相關資訊等)

     其中的 BillService的代碼如下:

[ServiceContract(Name  =   " Billing " , Namespace  =   " http://Microsoft.ServicePlatformLabs " )]

public   class  BillingService

{

    [OperationContract(Name  =   " Invoice " , IsOneWay  =   true )]

     public   void  Invoice( string  orderId,  string  total)

    {

        Console.WriteLine( " Invoice for Order {0} ({1}) generated " , orderId, total);

    }

}

    注:上面的ServiceContract屬性Name="Billing"和OperationContract屬性Name = "Invoice"

會以連結方式綁定到工作流CloudServiceBusSend活動(activity)的Action屬性上,即:

    http: // Microsoft.ServicePlatformLabs/Billing/Invoice

    ShippingService的代碼如下:

    [ServiceContract(Name  =   " Shipping " , Namespace  =   " http://Microsoft.ServicePlatformLabs " )]

     public   class  ShippingService

    {

        [OperationContract(Name  =   " ProcessOrder " , IsOneWay  =   true )]

         public   void  ProcessOrder( string  orderId)

        {

            Console.WriteLine( " Processing Shipping information for Order {0} " , orderId);

        }

    }

    同理,ShippingService會以連結方式綁定到工作流CloudServiceBusSend活動(activity)的

Action屬性上,即:

http: // Microsoft.ServicePlatformLabs/Shipping/ProcessOrder     

    而這兩個服務都會被暴露到ServiceBus中以便讓使用者進行通路操作,進而完成一個客戶下訂單的

完整流程(CreateOrder).

    而如何對這兩個服務進行安排組裝,就是通過WorkFlow的進行的。如果有開發過工作流經驗的開

發者應該會很容易了解這個概念。不過這裡還是要解釋一下,就是在雲中運作的工作流與我們平時所

了解的工作流(主要是在Activities方面)還是有些差異的,當然這并不意味着雲中的工作流要更難

于了解,恰恰相反,就目前而言,還是很容易的,下面是其在VS中的設計器中的截圖:

Azure Services Bus(服務總線)中的工作流(workflow)

    有關這幾個新添加的工作流activity,可能就要幾個篇幅來介紹和說明,因為今天的目的不在于

此,是以就先略過這塊内容了。

    另外就是目前在雲中隻支援CloudSequentialWorkFlow,如下圖:

Azure Services Bus(服務總線)中的工作流(workflow)

    目前還不支援狀态機工作流,但并不確定将來就不會出現。當然将來會不會出現workflow4中的

flowchart型工作流就更不好說了。

    假設我們最終要實作的工作流程如下:

    1.接受用戶端post過來的請求,并擷取其中指定的節點資訊,本DEMO中節點路徑為:

       < root >< order >< total > 節點值 </ total ></ order ></ root >

    2.通過對該節點值進行比較判斷,當值大于1000時則将工作流程轉入一個CloudDelay活動中,以

進行延時操作(設定CloudDelay活動的TimeOut屬性)。當值小于或等于1000時則順序執行上面所介

紹的兩個服務(BillingService,ShippingService)

    将上面的流程中工作流進行表示(建立)的結果如下圖:

Azure Services Bus(服務總線)中的工作流(workflow)

     好了,現在我們有了工作流和服務,接下來就是通過編碼将服務運作起來以及将工作流釋出到

Azure上了。下面是服務的建立運作代碼:

internal   static   void  Main()

        {

            var billingServiceHost  =   new  ServiceHost( typeof (BillingService));

            Console.WriteLine( " BillingService hosted at: " );

            Console.WriteLine( "" t "  + billingServiceHost.Description.Endpoints[0].Address.Uri);

            billingServiceHost.Open();

            var shippingServiceHost  =   new  ServiceHost( typeof (ShippingService));

            Console.WriteLine( " ShippingService hosted at: " );

            Console.WriteLine( "" t "  + shippingServiceHost.Description.Endpoints[0].Address.Uri);

            shippingServiceHost.Open();

            Console.WriteLine();

            Console.WriteLine( " Press [Enter] to exit " );

            Console.ReadLine();

            billingServiceHost.Close();

            shippingServiceHost.Close();

        }

     上面代碼中的Address.Uri屬性是在app.config中進行配置的:

Azure Services Bus(服務總線)中的工作流(workflow)
Azure Services Bus(服務總線)中的工作流(workflow)

Code

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <bindings>

      <netEventRelayBinding>

        <binding name="default" />

      </netEventRelayBinding>

    </bindings>

    <services>

      <service name="Services.ShippingService">

        <host>

          <baseAddresses>

            <add baseAddress="sb://servicebus.windows.net/services/[ENTER YOUR SERVICEBUS USERNAME HERE]"/>

          </baseAddresses>

        </host>

        <endpoint address="Shipping"

                  contract="Services.ShippingService"

                  binding="netEventRelayBinding"

                  behaviorConfiguration="transportClientBehavior0"

                  bindingConfiguration="default" />

      </service>

      <service name="Services.BillingService">

        <host>

          <baseAddresses>

            <add baseAddress="sb://servicebus.windows.net/services/[ENTER YOUR SERVICEBUS USERNAME HERE]"/>

          </baseAddresses>

        </host>

        <endpoint address="Billing"

                  contract="Services.BillingService"

                  binding="netEventRelayBinding"

                  behaviorConfiguration="transportClientBehavior0"

                  bindingConfiguration="default" />

      </service>

    </services>

    <behaviors>

      <endpointBehaviors>

        <behavior name="transportClientBehavior0">

          <!--

            Specify

              - CardSpace (default)

              - UserNamePassword

              - X509Certificate

              - AutomaticRenewal (tgt)

              - FederationViaCardSpace

            for credentialType.

          -->

          <transportClientEndpointBehavior credentialType="UserNamePassword">

            <clientCredentials>

              <userNamePassword userName="[ENTER YOUR SERVICEBUS USERNAME HERE]"

                                password="[ENTER YOUR SERVICEBUS PASSWORD HERE]" />

            </clientCredentials>

          </transportClientEndpointBehavior>

        </behavior>

      </endpointBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

    注:config檔案中的[ENTER YOUR SERVICEBUS USERNAME HERE]内容就是我們在AzureService平台上

建立的solution名稱(這部分内容參見這篇文章),這裡我們繼續使用上一篇文章中建立的那個項目名

稱MSF_DataSyncExample,而PASSWORD就是我們在建立MSF_DataSyncExample之後所設定的密碼。

    上面僅是完成了服務的本地化配置。而要把工作流(檔案内容)釋出到AzureService上我們還需要

在工作流檔案的可視模式下的空白區域單擊滑鼠右鍵,從彈出菜單中選擇“Deploy WordFlow”,這裡系

統會彈出一個視窗,如下:

Azure Services Bus(服務總線)中的工作流(workflow)

    我們隻需要将剛才在config檔案中輸入的solution名稱和密碼在這裡敲進去并單擊Deploy按鈕即可。

這樣系統就會在AzureService上為我們建立這樣一個釋出了的工作流(注:您也可以通過下面的位址

http://workflow.ex.azure.microsoft.com/login.aspx?name=[solutionname]來通路AzureService

并在系統向導的提示下完成工作流的釋出)。

    工作流釋出之後,我們還需要為目前的工作流建立一個執行個體,以便于讓用戶端通路該工作流服務時

持有它,進而完成工作流程。而建立工作流執行個體的工作是在Azure平台上完成的,請在IE位址欄中敲入

位址:https://workflow.ex.azure.microsoft.com/WorkflowManagement.aspx,如下圖:

Azure Services Bus(服務總線)中的工作流(workflow)

    我們在上圖中選擇了剛才釋出(Deploy)的工作流,然後點選“Manage Instances”按鈕後,系統顯

示如下:

Azure Services Bus(服務總線)中的工作流(workflow)

    我們點選:“CreateInstance”按鈕後,如下圖:

Azure Services Bus(服務總線)中的工作流(workflow)

     我們看到,系統以 “ name_date/time stamp”方式為我們命名了一個“執行個體名稱”,這主要是為了

避免出現同名的情況,因為執行個體在系統中必須唯一。

    接着我們點選"next"按鈕,系統接下來會提示我們進行身份驗證,如下圖:

Azure Services Bus(服務總線)中的工作流(workflow)

    這樣系統就會建立該執行個體并傳回到目前工作流的執行個體管理清單:

Azure Services Bus(服務總線)中的工作流(workflow)

    當我們把工作流執行個體建立完成後,就把将目前工作流運作(通過選中相應的執行個體并單擊頁面下方的

“Start”按鈕,另外可以點選Details按鈕來獲得執行個體ID,下面會介紹)起來。

    這樣在Azure平台上,我們的工作流就算是配置運作起來了,那麼接下來,我們來看看到如何讓客戶

端通路釋出的工作流的。

    首先是用戶端的配置檔案:

<? xml version="1.0" encoding="utf-8"  ?>

< configuration >

   < appSettings >

     < add  key ="UserName"  value ="[ENTER YOUR SERVICEBUS USERNAME HERE]" />

     < add  key ="Password"  value ="[ENTER YOUR SERVICEBUS PASSWORD HERE]" />

     < add  key ="InstanceId"  value ="[ENTER THE WORKFLOW INSTANCE ID]" />

   </ appSettings >

</ configuration >

     從上面節點資訊可以看出,我們要将配置在service端的相應節點資訊(包括solution名稱和密碼

)配置到目前config節點上。當然這裡還多了一個節點就是“InstanceId”,該執行個體ID就是我們剛才在

Azure平台上建立的工作流執行個體的ID,我們可以通過單擊相應的執行個體來獲得該内容,如下:

Azure Services Bus(服務總線)中的工作流(workflow)

    這樣就完成了在client端的config配置,下面就是用戶端通路的工作流的代碼了。

Azure Services Bus(服務總線)中的工作流(workflow)
Azure Services Bus(服務總線)中的工作流(workflow)

Code

internal class Program

{

    private const string workflowTypeName = "CloudOrderWorkflow2";

    private const string activityName = "ReceiveOrder";

    internal static void Main()

    {

        Console.Title = "Client";

        var userName = ConfigurationManager.AppSettings["UserName"].ToString();

        var password = ConfigurationManager.AppSettings["Password"].ToString();

        var instanceId = ConfigurationManager.AppSettings["InstanceId"].ToString();

        Console.WriteLine("Press [enter] to send the order to the cloud workflow");

        Console.ReadLine();

        var request = CreateMessage();

        try

        {

            CallReceiveActivity(userName, password, workflowTypeName, instanceId, activityName, request);

        }

        catch (WebException ex)

        {

            Console.WriteLine("Error handled:");

            using (var sr = new StreamReader(ex.Response.GetResponseStream()))

            {

                Console.WriteLine(sr.ReadToEnd());

            }

        }

        Console.WriteLine("Press [enter] to exit");

        Console.ReadLine();

    }

    private static string CreateMessage()

    {   //注:為了測試工作流中的IFELSE流程,可在800運作之後修改為1400,這樣會運作cloudDelay流程

        return "<root><order><total>800</total></order></root>";

    }

    internal static void CallReceiveActivity(string userName, string password, string workflowName, string instanceId, string activityName, string message)

    {

        var url = String.Format(CultureInfo.InvariantCulture,

                   "http://{0}/workflowsHttp/{1}/workflows/{2}/instances/{3}/{4}",

                   WorkflowClientConfig.WorkflowHostName, userName, workflowName, instanceId, activityName);

        var token = GetWorkflowGrantingToken(userName, password);

        using (var httpRequest = new WebClient())

        {

            httpRequest.Headers.Add(HttpRequestHeader.ContentType, "text/xml");

            httpRequest.Headers.Add("X-MS-Identity-Token", token);

            var response = httpRequest.UploadString(url, message);

            Console.WriteLine("Response by the server: {0}", response);

        }

    }

    internal static string GetWorkflowGrantingToken(string userName, string password)

    {

        string tokenUrl = string.Format(CultureInfo.InvariantCulture,

                                        "https://{0}/issuetoken.aspx?u={1}&p={2}",

                                        WorkflowClientConfig.StsHostName,

                                        userName,

                                        password);

        Console.WriteLine("Requesting authentication token

Azure Services Bus(服務總線)中的工作流(workflow)

");

        HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenUrl);

        HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();

        byte[] tokenBody = new byte[500];

        int tokenBodyLength = tokenResponse.GetResponseStream().Read(tokenBody, 0, 500);

        var authenticationToken = Encoding.UTF8.GetString(tokenBody, 0, tokenBodyLength);

        Console.WriteLine("Authentication Token: {0}", authenticationToken);

        return authenticationToken;

    }

}

     現在我們在本地分别打開兩個VS2008,一個将service項目做為啟動項目中,一個以Client作為啟動

項目,分别在相應的項目上擊滑鼠右鍵,在彈出菜單上選“調試”--》“啟動新執行個體”。先運作service端如

下:

Azure Services Bus(服務總線)中的工作流(workflow)

    然後是按上面所說方式啟動Client端:

Azure Services Bus(服務總線)中的工作流(workflow)

    這裡,我們在切換回服務端運作界面,如下:

Azure Services Bus(服務總線)中的工作流(workflow)

    到這裡,我們隻是測試了工作流中的ELSE分支(代碼中Total為800),還未測試當Total>1000的分支。

下面是修改Total值之後的測試流程:

    1.首先重複上面在AZURE平台上建立工作流執行個體的步驟,以便生成新的執行個體ID供用戶端config檔案更新

節點配置。

    2.将client的運作代碼從800改為1400,然後順序啟動上面的service,client應用,這時我們還是會看到

之前運作的service端和client端的運作界面,隻是這次服務端在用戶端提送出請求之後,并未馬上傳回列印

結果,而是運作了cloudDelay活動,這時如果我們通路azure平台上的相關工作流頁面時,會看到下面的結果

azure_run_flow.gif

    即目前工作流執行個體正在運作,我們需要等待1分鐘之後,再重新整理該頁面時,會看到目前執行個體已運作完成:

azure_workflow_complate.gif

    好了,到現在為止,整個開發和運作測試流程就介紹的差不多了。

    因為手頭的資料不多,而我還有的一些疑問還是沒有最終得到解答。下面我将它們羅列出來,如果大家

感興趣或有這方面的經驗,不妨與我聯系或在回複中進行讨論。

    1.例子中的SERVICE端如果能夠被布署在多台伺服器上,當client端post請求時,目前會通過哪台機器上

的服務來處理?我猜測的一種可能應該是service bus會通過一些網絡路由算法(如最短路徑算法)來配置設定相

應的服務機來處理相應請求。但如果是這樣,是否還應該包括負載均衡方面的考慮呢? 必定每台機器的處理

能力有限,任務多了就要排隊,如果一味還是”最短路徑“的話,會讓service bus中的某一個服務結點不堪

負。

    2.目前的測試可以讓azure平台上的某一工作流在某一條件下被觸發執行(上傳中的total<=1000).但工

作流的持久化就在azure上嗎?這樣的話,如果azure平台出現問題,正在運作的執行個體可能會将執行在一半的

工作流“復原”,以免出現資料被髒讀的情況嗎?另外如果企業想将被執行失敗的工作流按“自己的方式”

進行處理又應該如何去做呢?

   3.在biztalk中其扮演的角色是ESB(企業服務總線),而Azure Service Bus目前而言應該是一個ISB(

Internet 服務總線)。如果與ISG是ESB的一種實作方式的話,那在ESB中的消息類型與ISB中的消息類型又是

否為一脈相承呢,在SDK中我看到了這樣一段代碼(出現在了AzureServicesKit\Labs\IntroServiceBus\

Ex04-RESTSample中):

       Message response = StreamMessageHelper.CreateMessage(

           OperationContext.Current.IncomingMessageVersion, "GETRESPONSE", this.WriteImage);

     而該Message類型為abstract類型(位置System.ServiceModel.Channels名空間),這個類型又與在

 BIZTALK Server上配置于資料庫中的message表中的消息資料是怎樣一個關系呢。以前曾在網上看到有篇文

 章說azure會将shartpoint逐漸在雲中加以實作,如果是這樣的話那biztalk是否将來會與AZURE平台産生

 某種更深層次的關聯嗎?

    疑問之後還是疑問,看來在研究Azure平台的過程中還有很長的路要走,走一步

看一步吧!!!   

     好了,今天的内容就先到這裡了。

     原文連結:http://www.cnblogs.com/daizhj/archive/2008/12/08/1350013.html

     作者: daizhj, 代震軍

     Tags: Azure,isb,service bus,esb,workflow,工作流,服務總線

     網址: http://daizhj.cnblogs.com/