天天看點

程式員眼中的工作流

一種新的程式設計語言

    WF4.0提供了許多功能來簡化企業應用開發、部署和管理。這篇文章中,我想從開發人員将工作流看成一門程式設計語言角度探索工作流。

    相對與其它行業主流語言如:C\C++\Java\C#\VB,WF有很多“新”功能 。作為開發人員我個人最欣賞下面這幾項功能:

      1、完全聲明式的程式設計語言

      2、支援延續(Continuation)

      3、内置持久化功能

      4、在伺服器宿主管理工作流執行個體

聲明式的程式設計 

    這一點非常容易了解:在WF中,代碼就是資料。開發人員能使用可視化的編輯器通過拖拽建立工作流的定義。 所産生的工作流定義是工作流的抽象文法樹(Abstract Syntax Tree,AST),它能被任何工具周遊和解析。

    下面,我将通過一個例子來詳述其餘的3點。

延續(Continuation)和執行個體管理

The old world :

    現在,假設我們要在不使用WF情況下設計一個程式來追蹤檔案的稽核過程。 程式從使用者接受一個檔案,發送通知給稽核人員,等待稽核人員的決策(同意或者退回),然後通知原始送出者。

    首先,讓我們使用C#風格的僞代碼寫一個簡單的控制台版本作為概念論證

         string document = Console.ReadLine();

         Console.WriteLine("Approver, please respond");

         string result = Console.ReadLine();

         Console.WriteLine("Submitter, result for your document approval is {0}", result);

                    Program 1

    這是很簡單,但是當我們将它變成一個在任何時候可以接受來自不同使用者的不同要求的服務時,該程式需要處理的幾個新問題:

1、狀态管理

    在上述簡單的代碼中,程式是有狀态,但開發人員并不需要明确追蹤他們:當第一ReadLine傳回時,程式會自動從該點恢複并發送消給稽核人員,當第二 ReadLine傳回時,程式會自動從該點恢複并傳回結果給送出人員。在這裡,程式的狀态通過調用堆棧儲存的,堆棧由編譯器和作業系統管理。但在現實世界 中,稽核人員可能需要幾天時間對請求作出回應,程式通過阻止線程來等待回應顯然是不切實際的。是以,将會沒有調用堆棧來儲存程式的狀态,開發人員必須明确 地跟蹤狀态。

2、執行個體管理

    由于系統能夠同時處理多個檔案。該服務需要分别追蹤多個審批程式執行個體及其狀态。

3、可伸縮性和可用性

    因為上述兩點,我們知道該服務是有狀态的 ,對于任何有狀态的服務,這總是一個問題:服務如何擴充來滿足不斷增長的需求,在出現不可避免的硬體/軟體故障時仍然可用。一般來說,狀态都必須被維護,因為沒有任何類似的服務程序來完成這些目标。

    下面是一個簡單的實作這種服務的僞代碼段。 我們當然可以使用重疊IO和線程池對其進行優化或使用WCF簡化一些東西,但是這并不關注的問題。

       while (true)

       {

           Socket requestSocket = socket.Accept();

           new Thread(HandleRequest).Start(requestSocket);

       }

        static void HandleRequest(object state)

        {           

            Socket socket = state as Socket;

            //Receive bytes from socket and serialize it to a Request object

            Request request = …

            if (request.Type == RequestType.Submit)

            {

               //Read and serialize more bytes from the socket to a SubmitRequest object

               SubmitRequest submit = …

                HandleSubmitRequest(submit);

            }

            else if (request.Type == RequestType.Approval)

//Read and serialize more bytes from the socket to a SubmitRequest object

                ApprovalRequest approval = …

                HandleApprovalRequest(approval);

        }      

        static void HandleSubmitRequest(SubmitRequest submit)

        {

            int documentId = CreateDocumentInstanceInDB(submit);

            SendEmailToApprover(submit.Approver);

            SetDocumentInstanceStateInDB(documentId, DocumentState.WaitForApproval);

        }

        static void HandleApprovalRequest(ApprovalRequest approval)

            SubmitRequest submit = LookupDocumentInstanceInDB(approval.DocumentId);

            SendEmailToSubmitter(submit.Submitter, approval.Approved);

            SetDocumentInstanceStateInDB(approval.DocumentId, approval.Approved ? DocumentState.Approved : DocumentState.Rejected);

                 Program 2

    正如我們可以在這裡看到,非常簡單的程式突然變得非常複雜,一旦開發人員需要清楚地跟蹤狀态,執行個體以及獨立的代碼。

    良好的面向對象的做法,對程式一個自然優化的是将文檔的稽核生命周期抽象為一個類: 

     class DocumentApproval

    {

        public int Id    {…}

        public DocumentState State   {…}

        public string Submitter   {…}

        public string Approver   {…}

        public void OnSubmitted()

            SendEmailToApprover(this.Approver);

            this.State = DocumentState.WaitForApproval;

        public void OnApprovalReceived(bool approved)

            SendEmailToSubmitter(this.Submitter, approved);

            this.State = approved ? DocumentState.Approved : DocumentState.Rejected;

}

        Program 3

    有了這個類,我們可以從socket, DB, 和執行個體管理的代碼中分離出業務邏輯 。大部分代碼與Program 2類似,2個處理請求的方法改寫為: 

       static void HandleSubmitRequest(SubmitRequest submit)

            //create a DocumentApproval object from a SubmitRequest

            DocumentApproval doc = …;

            doc.OnSubmitted();

            PersistToDB(doc);

            DocumentApproval doc = ReadFromDB(approval.DocumentId);

            doc.OnApprovalReceived(approval.Approved);

                 Program 4

    現在代碼非差的清晰了。業務邏輯被管道代碼分離出來。然而,DocumentApproval類現在有自己的邏輯分散在幾個方法中,并依賴管道代碼在适當 時候用正确的順序調用适當的方法。這與我們在Program 1中的順序編碼經驗有很多的不同,但是優化了很多。 

The new world

現在讓我們看看如何通過WF4實作同樣的服務 

              Program 5

    假設你對Workflow Foundation, Activities, Workflow Services, 和Messaging Activities有一些基本了解,我不打算詳細解釋這些。 它基本上模拟這樣一個服務,首先接受一個檔案送出的資訊。請發送電子郵件至稽核人員(使用自定義活動),接受第二個消息是檔案審批,然後發送電子郵件送出 者(使用自定義活動)。

    有趣的是,這個流程看起來像在program 1中的代碼那麼簡單。它描述為一個單一的檔案審批過程程式流,它描述為一個連續順序過程,就好像是中間沒有斷點。但是它的功能與Program 2一樣強大:等待新的消息時候,這項服務将不會阻止任何線程;它可以同時跟蹤多個文檔執行個體;在脫工作流的服務所有狀态能夠持久化 ,如果前一個機器超載,在另外一台不同的機器上可以繼續這個檔案的審批程式。開發人員不需要編寫任何代碼是有可能的,因為這是WF運作時的功能。

延續(Continuation)

    在這個工作流程,在Receive活動執行時,工作流狀态變成“idle”, 這意味着它沒事可做隻是在等待的事情的發生。這時,工作流運作時可以将這個執行個體從正在運作的線程中拿走,并開始運作其它執行個體。當這份檔案的消息到達 時,WF運作時有辦法找出相應的執行個體,從空閑點上恢複。執行個體的所有狀态都被保留,就像“調用堆棧”的線程永遠不會改變。

    延續不是一個新事物,很多語言包括Lisp, Scheme, Ruby, 和Perl都有類似的概念。不過,WF的延續,更容易使用,它來自自然,即使沒有開發者的注意:事實上,我經常給WF初學者需要解釋延續對于WF意味着什 麼是有依據的。還與持久性功能相結合,WF延續(Continuation)持久化長時間運作的異步服務,它是易于擴充的。 

内置執行個體管理 

    當這個工作流程的定義被WorkflowServiceHost 宿主時,服務宿主将自動啟動一個的要求送出的檔案的工作流程的新執行個體。每當一個工作流執行個體達到空閑點,執行個體資料可以使用持久性功能從記憶體中解除安裝。當一個文 檔審批請求到來時,服務宿主可以找出請求的是哪個現有的檔案處理流程。重新啟動該執行個體,恢複和給予新的要求給它。另一個很酷的事情是,在持久化之後,WF 的執行個體不必堅守在宿主程序中,是以在其它的不同的機器的宿主可以加載相同的執行個體,加強了可擴充性和可用性。

    WF3.5已經有通過WCF的資訊激活工作流執行個體的功能。

内置的持久化支援

    正如我們前面所看到,為了使延續和執行個體管理能持久和可擴充,所有的執行個體資料要使用持久的格式持久化,從主機程序分離。 WF運作時有一個很大的功能集來支援持久化功能。此外WF4的程式設計模型強迫中繼資料和執行個體資料分離,這使得持久化比WF3.5更加容易

部落格擴充

    這篇文章闡述了為什麼開發人員可能有興趣将Workflow Foundation作為開發業務長時間運作應用程式的新工具 。它示範了使用WF4.0隻通過拖拽如何建立一個強大的可持久化的和可擴充的服務。很快又觸及幾個WF4.0重要功能領域。如延續,新的資料模型,持久化,WCF的內建和基于内容的相關性。我們的團隊将繼續在部落格詳述每個類别。

本文轉自麒麟部落格園部落格,原文連結:http://www.cnblogs.com/zhuqil/archive/2010/03/05/a-developer-s-view-of-workflow.html,如需轉載請自行聯系原作者