다음을 통해 공유


Azure: Durable Task Framework – Episode III: Coded Orchestrations

Coded Orchestrations

When working in integration space, we commonly deal with terms like Orchestration, Business Process Execution etc. For this purpose we use differed powerful products like Enterprise Service Busses, Process Execution Engines, etc.

Following picture shows one BPMN2 example, which graphically represents some business process.

  http://developers.de/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/damir_5F00_dobric/clip_5F00_image008_5F00_thumb_5F00_0EF9EE9B.jpg

Business people love such diagrams. They especially like the idea of painting such diagrams, compiling them and executing them. This gives a feeling you would an artist in a role of software architect. But in reality, there is a majority of cases, when this approach does not work. I spent lot of time in projects as developer or architect making such pictures running. Realistically majority of time I spent in fixing some configuration in those nice "pictures" then on creating execution logic. If you run in such problems DTF might be the right approach. 

But first, business guys must accept (if applicable) the idea of coding of the business process. This sounds sometimes more expensive, but it doesn't have to be. Today, high programming languages like C# are so efficient, that it is truly easier to read few lines of C# than to read few lines of XML or JSON. 

If you are a developer with knowledge of .NET and C# they (you) will love DTF. With DTF you don't get monitoring engine and fancy orchestration diagrams. But you have so called coded-orchestrations. Most important component in integration system is messaging and scheduler. DTF gives you scheduler by default and used messaging system is Service Bus (Azure Service Bus or Service Bus for Windows Server). All for free of charge. An additionally, you don't need to learn anything about Service Bus, because DTF seats between your code and SB.

Typical DTF application is nothing else than representation of some workflow. For example, to express the BPMN diagram shown above in code, we would write something like:

var goods = ReceiveGoods(); 
if(IsNormalShipment(goods) 
{ 
   if(IsInsuranceRequired(goods) 
  { 
      RequestInsurance(goods); 
  } 
  FillInPostLabel(goods);  
  
} 
else if(IsSpatialShipment(goods) 
{ 
  var quotes = RequestQUotesFormCarrier(goods); 
  AssignCarrier(quotes, goods); 
  PreparePaperWork(); 
}

 

Hello DTF 

  

Let's implement now the orchestration form code shown above. Create project of any type which targets System32 platform (any Desktop kind of application). Then add the reference to NuGet package as shown at the next picture:

http://developers.de/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/damir_5F00_dobric/clip_5F00_image010_5F00_thumb_5F00_295C7263.jpg

This will install the assembly called 'DurableTask', which implements all Durable Task Framework. DTF introduces two new artefacts:

  • Task Hub Client and
  • Task Hub Worker

Task hub client is implemented in class TaskHubClient and is responsible for starting of orchestrations, getting the state of orchestration, termination of orchestration etc. Agnostically it is similar to WCF-Proxy and HttpClient. 

Task hub worker is a server part. Similarly to WCF ServiceHost, hub worker is used to register the orchestrations as service and to run listener. The worker is implemented in a class TaskHubWorker. 

First task in typical DTF project is to implement orchestration. To do this, we should implement a class which derives fromTaskOrchestration<Result,Input>.

public class  MyOrchestration : TaskOrchestration<Result, Input>
{
    public override  async Task<Result>
    RunTask(OrchestrationContext context, Input input)
    {
        var goods = await context.ScheduleTask<string>
        (typeof(ReceiveGoodsTask), goods);
        if (IsNormalShipment(goods)
        {
            if (await context.ScheduleTask<bool>(typeof(CheckInsuranceTask),
             goods);
            {
                goods = await
                 context.ScheduleTask<goods>(typeof(RequestInsuranceTask), goods);
            }
            var result context.ScheduleTask<Result>(typeof(FillPostLabelTask),
            goods);
        }
 
        else if  (IsSpatialShipment(goods)
        {
            quotes = await
             context.ScheduleTask<goods>(typeof(RequestCarierQuotesTask),
             goods);
            context.ScheduleTask<goods>(typeof(AssignCarrierTask),
             . . .);
            context.ScheduleTask<goods>(typeof(PaperWorkTask),
             . . .);
        }
    }
}
namespace DurableTaskSamples.Greetings
{
    using System;
    using System.Windows.Forms;
    using DurableTask;
 
    public sealed  class GetUserTask : TaskActivity<string, string>
    {
 
        protected override  string Execute(DurableTask.TaskContext context,
        string input)
        {
            GetUserName userNamedialog = new  GetUserName();
            Console.WriteLine("Waiting for user to enter name...");
            string user = "";
            DialogResult dialogResult = userNamedialog.ShowDialog();
            if (dialogResult == DialogResult.OK)
            {
                user = userNamedialog.UserName;
            }
            Console.WriteLine("User Name Entered: " + user);
 
            return user;
        }
    }
}
 
namespace DurableTaskSamples
{
    using System;
    using DurableTask;
 
    public sealed  class CheckInsuranceTask : TaskActivity<string, string>
    {
        protected override  bool Execute(DurableTask.TaskContext context,
        string input)
        {
            // put here the business logic 
            bool result = DoSomething(input);
 
            return result;
        }
    }
}
 
TaskHubWorker taskHub = new  TaskHubWorker(taskHubName,
                                          servicebusConnectionString,
                                          storageConnectionString);
taskHub.CreateHub();
 
taskHub.AddTaskActivities( 
new GetUserTask(), 
new SendGreetingTask(), 
new CronTask(), 
new ComputeSumTask(), 
new GoodTask(), 
new BadTask(), 
new CleanupTask(),
new EmailTask());
                        taskHub.AddTaskActivitiesFromInterface<IManagementSqlOrchestrationTasks>( 
  new ManagementSqlOrchestrationTasks()
);
 
taskHub.AddTaskActivitiesFromInterface<IMigrationTasks> 
( 
   new MigrationTasks()
);
 
taskHub.Start(); 

Under the hub

Initialization of the hub explained previously also performs the setup of required Service Bus queues and table width="100%"Storage table width="100%s. The picture below on left shows 3 required queues.

http://developers.de/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/damir_5F00_dobric/clip_5F00_image012_5F00_thumb_5F00_396B411A.png

http://developers.de/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/damir_5F00_dobric/clip_5F00_image014_5F00_thumb_5F00_2DD583DB.png

storageConnectionString is specified by creating of the hub in following code snippet:

 

TaskHubWorker taskHub = new  TaskHubWorker(taskHubName, 
                                          servicebusConnectionString, 
                                          storageConnectionString); 

If storage connection string is not specified, no tracking information will be used. In that case there are no runtime restrictions, but you will not be able to get instance history data and instance state information. For some scenarios this is anyhow not required. Daenet has extended DTF to support provider based State Persistence. The upcoming version of DTF will provide injectable width="100%"service similarly to OWIN middleware in ASP.NET.

But, as developer you don't have to necessary know all details behind under the hub. When working with DTF you will never have to directly send or receive a message from Service Bus. 

It is completely transparent to you. But it is good to know few details under the hub like queues and table width="100%s which are used. Sometimes you will have to deal with them especially during development time. Last but not least, it is also good to know that all communication with Service Bus queues uses session queues (ordered messaging) and compressed messages, which are serialized with JSON serializer. 

Recap

With DTF Microsoft provides an easy option for building robust distributed and scalable services with build-in state persistence and program execution check points. All of scenarios described on the beginning of this article are very easy to implement with DTF.

Some Azure products like Mobile services, API Management or TFS Online use Durable Task Framework for automating of complex workflows. Right now DTF does not offer dashboard and orchestration designer. Daenet has already successfully implemented DTF in some traditional integration projects. Ten years ago we had to hardly deal with different protocols and products like BizTalk ware great help. But this time is over. We deal today with web services and problem of implementation of an adapter is mostly not an issue anymore. We had to invest in a past in development of Business Activity Monitor for DTF, but now we have a mobile dashboard for all devices which shows semantically aggregated business events generated from DTF framework. As long coding is not an option in you organization DTF is not the way to go. But if you have developers in the team, DTF is definitely a valid option for many integration scenarios.

 
Episode I:
The Problem of robust execution

Episode II:
Clustered Singletones, Failover and Competing Consumer

 Episode III (this article):
Coded Orchestrations

References: