奥尔良谷物中的单螺纹

时间:2018-07-25 20:41:22

标签: c# .net-core actor orleans

我正在尝试了解Microsoft Orleans中的谷物单线程。我使用了http://www.informit.com/articles/article.aspx?p=2416187中的代码,并对其进行了一些修改以测试我的场景。

我的客户代码和筒仓建筑代码

    static async Task Main(string[] args)
    {
        var siloBuilder = new SiloHostBuilder()
            .UseLocalhostClustering()
            .UseDashboard(options => { })
            .Configure<ClusterOptions>(options =>
            {
                options.ClusterId = "dev";
                options.ServiceId = "Orleans2GettingStarted";
            })
            .Configure<EndpointOptions>(options =>
                options.AdvertisedIPAddress = IPAddress.Loopback)
            .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning).AddConsole());

        using (var host = siloBuilder.Build())
        {
            await host.StartAsync();

            var clientBuilder = new ClientBuilder()
                .UseLocalhostClustering()
                .Configure<ClusterOptions>(options =>
                {
                    options.ClusterId = "dev";
                    options.ServiceId = "Orleans2GettingStarted";
                })
                .ConfigureLogging(logging => logging.AddConsole());

            using (var client = clientBuilder.Build())
            {
                await client.Connect();

                var random = new Random();
                string sky = "blue";

                while (sky == "blue") // if run in Ireland, it exits loop immediately
                {
                    Console.WriteLine("Client giving another request");
                    int grainId = random.Next(0, 500);
                    double temperature = random.NextDouble() * 40;
                    var sensor = client.GetGrain<ITemperatureSensorGrain>(grainId);

                    // Not awaiting this task so that next call to grain 
                    // can be made without waiting for current call to complete
                    Task t = sensor.SubmitTemperatureAsync((float)temperature);
                    Thread.Sleep(1000);
                }
            }
        }
    }

我的谷物界面和实际的谷物实现

public interface ITemperatureSensorGrain : IGrainWithIntegerKey
{
    Task SubmitTemperatureAsync(float temperature);
}


public class TemperatureSensorGrain : Grain, ITemperatureSensorGrain
{
    public async Task SubmitTemperatureAsync(float temperature)
    {
        long grainId = this.GetPrimaryKeyLong();
        Console.WriteLine($"{grainId} received temperature: {temperature}");

        await Task.Delay(10000);
        // Thread.Sleep(10000);
        Console.WriteLine($"{grainId} complete");
        // return Task.CompletedTask;
    }
}

我基本上在做的是每1秒向Grain发送一次请求,而我允许Grain内部的每个方法调用至少花费10秒。现在,根据谷物的单线程执行和here中所述的Orleans Runtime Scheduling,我希望,除非当前请求的方法完成,否则谷物将对请求进行排队,并且下一个请求将不会被谷物处理。但是,控制台输出并不能证实这一点。控制台输出为:

Client giving another request
344 received temperature: 8.162848
Client giving another request
357 received temperature: 10.32219
Client giving another request
26 received temperature: 1.166182
Client giving another request
149 received temperature: 37.74038
Client giving another request
60 received temperature: 26.72013
Client giving another request
218 received temperature: 24.19116
Client giving another request
269 received temperature: 17.1897
Client giving another request
318 received temperature: 8.562404
Client giving another request
372 received temperature: 8.865559
Client giving another request
443 received temperature: 5.254442
Client giving another request
344 complete        <-------------- The first request completed here
97 received temperature: 19.24687

这很清楚,在当前请求完成之前,谷物正在处理下一个请求。

问题:

  1. 那么,这是否违反了奥尔良单线程执行模型,或者我在这里缺少什么?

  2. 此外,当我在谷物内部使用Thread.sleep(10000)而不是Task.Delay(10000)时,我得到的控制台输出几乎与每个请求调用的额外警告不同- Task [Id=1, Status=RanToCompletion] in WorkGroup [Activation: S127.0.0.1:11111:270246987*grn/6424EE47/00000028@cafcc6a5 #GrainType=Orleans2GettingStarted.TemperatureSensorGrain Placement=RandomPlacement State=Valid] took elapsed time 0:00:10.0019256 for execution, which is longer than 00:00:00.2000000。 这是否意味着每个谷物都应该在200ms内理想地处理?如果谷物加工时间更长,会发生什么?

1 个答案:

答案 0 :(得分:3)

正如@DanWilson在评论中所说,您正在观察此行为,因为每个调用都是在单独的谷物上进行的。

在奥尔良,每个谷物实际上都是单线程的,但不是整个筒仓或群集。这意味着可以同时执行许多任务,这意味着向主机添加更多核心或添加更多计算机将使您能够扩展服务。

修改代码以仅选择一次var srce, dest: TBitmapSurface; path: string; scan: integer; w, h1, h2: integer; begin path := 'C:\tmp\Imgs\res.bmp'; srce := TBitmapSurface.Create; TBitmapCodecManager.LoadFromFile(path, srce); dest := TBitmapSurface.Create; // first half w := srce.Width; h1 := srce.Height div 2; dest.SetSize(w, h1, TPixelFormat.RGBA); for scan := 0 to h1-1 do Move(srce.Scanline[scan]^, TBitmapSurface(dest).Scanline[scan]^, srce.Width * 4); Image1.Bitmap.Assign(dest); // second half h2 := srce.Height - h1; dest.SetSize(w, h2, TPixelFormat.RGBA); for scan := h1 to srce.Height-1 do Move(srce.Scanline[scan]^, TBitmapSurface(dest).Scanline[scan-h1]^, srce.Width * 4); Image2.Bitmap.Assign(dest); end; (通过将其移到循环外),我看到此示例执行:

grainId

您所期望的是:许多请求被排队(每秒一个),但是每个请求需要10秒才能完成,谷物可以开始处理下一个请求。