简体   繁体   English

微软奥尔良谷物通讯性能

[英]Microsoft Orleans grain communication performance

I am working on a workflow engine using Mircosoft Orleans as the base, as it offers a number of useful features such as automatically distributing the work and handling fail over. 我正在使用以Mircosoft Orleans为基础的工作流引擎,因为它提供了许多有用的功能,例如自动分配工作和处理故障转移。

I have three types of grains: 我有三种谷物:

  • Workflow - Holds information in the workflow and what order work blocks should be executed in 工作流程-包含工作流程中的信息以及应在其中执行哪些订单工作块
  • Work Block - The parts that actually do the work 工作块-实际完成工作的零件
  • Execution - A single execution of the workflow 执行-工作流程的一次执行

My problem is that when running a large number of current executions, ie > 1000 the performance really suffers. 我的问题是,当运行大量当前执行(即> 1000)时,性能确实会受到影响。 I have done a bit of profiling and narrowed this down to the communication that happens between the grains. 我做了一些分析,并将其范围缩小到谷物之间发生的交流。 Is there anyway I can improve this any more? 反正有什么我可以改善的呢?

Here is the outline of my code and how the grains interact 这是我的代码的概述以及谷物之间的相互作用

The execution grain sits in a loop, getting the next work block from the workflow and then calling execute on the workblock. 执行粒度位于循环中,从工作流中获取下一个工作块,然后在工作块上调用execute。 It is this constant calling between grains that is causing the execution time for one of my test workflows to go from 10 seconds when running a single execution to around 5 minutes when running over 1000. Can this be improved or should I re-architect the solution to remove the grain communication? 正是这种持续不断的调用导致我的一个测试工作流的执行时间从一次执行的10秒变为超过1000次的5分钟。这可以改进还是应该重新设计解决方案?删除粮食交流?

[StorageProvider(ProviderName = "WorkflowStore")]
[Reentrant]
[StatelessWorker]
public class Workflow : Grain<WorkflowState>, IWorkflow
{
    public Task<BlockRef> GetNext(Guid currentBlockId, string connectionName)
    {
         //Lookup the next work block
    }
}

[Reentrant]
[StatelessWorker]
public class WorkBlock : Grain<WorkBlock State>, IWorkBlock 
{
    public Task<string> Execute(IExecution execution)
    {
         //Do some work
    }
}


[StorageProvider(ProviderName = "ExecutionStore")]
public class Execution : Grain<ExecutionState>, IExecution, IRemindable
{
    private async Task ExecuteNext(bool skipBreakpointCheck = false)
    {            
        if (State.NextBlock == null)
        {
            await FindAndSetNext(null, null);
        }

        ...

        var outputConnection = await workblock.Execute();

        if (!string.IsNullOrEmpty(outputConnection))
        {
            await FindAndSetNext(State.NextBlock.Id, outputConnection);
            ExecuteNext().Ignore();
        }
    }

    private async Task FindAndSetNext(Guid? currentId, string outputConnection)
    {
        var next = currentId.HasValue ? await _flow.GetNextBlock(currentId.Value, outputConnection) : await _flow.GetNextBlock();
        ...
    }
}

A couple of issues here: 这里有几个问题:

1) It does not seem right that Workflow is a StatelessWorker AND uses StorageProvider. 1)Workflow是StatelessWorker并使用StorageProvider似乎并不正确。 StorageProvider means it has state that it cares too persist, StatelessWorker means it does not have any state. StorageProvider表示它具有太在意的状态,StatelessWorker表示它不具有任何状态。 Instead use regular non StatelessWorker grains. 而是使用常规的非StatelessWorker谷物。

2) lets look top down at modelling: Workflow is just the data about workflow and the code to execute, WorkBlock is one block of the multi block workflow (one step of the multi step workflow), correct? 2)让我们自上而下地进行建模:工作流只是有关工作流的数据和要执行的代码,工作块是多块工作流的一个块(多步工作流的一个步骤),对吗? In such a case none of them should be grains. 在这种情况下,它们都不应该是谷物。 They are just state. 他们只是状态。 The Execution is the only one that needs to be grain. 执行是唯一需要谷物的工具。 Execution receives the Workflow, Workflow encodes inside its data what is the next block, and Execution just executes the block. 执行接收工作流,工作流在其数据内编码下一个块是什么,而执行仅执行该块。

3) From scalability perspective you just want a lot of Execution grains. 3)从可伸缩性的角度来看,您只需要很多执行粒度。 If a Workflow has an id, then you can use an Execution grain for each Workflow id. 如果工作流具有ID,则可以为每个工作流ID使用执行粒度。 If you want to execute the same Workflow (with same id) multiple times in parallel, now it depends. 如果要并行执行相同的工作流(具有相同的ID),则现在要视情况而定。 If its not too much in parallel, maybe one Execution grain for all will be enough. 如果并行执行的次数不是太多,那么一个执行粒度就足够了。 If not, you can use a pool of X Execution grains (the id for Execution grain will be "WorkflowId-NumberBetween0AndX). 如果没有,则可以使用X执行颗粒池(执行颗粒的ID为“ WorkflowId-NumberBetween0AndX”)。

In my opinion these functions shouldn't be standalone grains, aggregating them would eliminate the expensive inter-grain communication. 我认为这些功能不应该是独立的,将它们聚合可以消除昂贵的晶粒间通信。

If you rename Work Block to Activity and Execution to WorkflowInstance , your concept gets very-very similar to Microsoft's Workflow Foundation. 如果将工作块重命名为活动 ,将执行重命名为WorkflowInstance ,则您的概念将非常类似于Microsoft的Workflow Foundation。 I've started a project on GitHub ( Orleans.Activities ) to run WF4 workflows on Orleans. 我已经在GitHub( Orleans.Activities )上启动了一个项目,以在Orleans上运行WF4工作流。 Though it is not production ready, no performance tests, but at least works. 尽管尚未准备好生产,但没有性能测试,但至少可以运行。 Maybe you should give it a try. 也许您应该尝试一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM