一种新的编程语言
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);
}
static void HandleApprovalRequest(ApprovalRequest approval)
{
DocumentApproval doc = ReadFromDB(approval.DocumentId);
doc.OnApprovalReceived(approval.Approved);
PersistToDB(doc);
}
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,如需转载请自行联系原作者