奥尔良粒子中的单线程处理

3
我正在尝试理解 Microsoft Orleans 中 grains 的单线程处理。我使用了 这里 的代码,并对其进行了一些修改以测试我的场景。 我的客户端代码和 silo 构建代码
    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秒向颗粒发送请求,而我允许颗粒内的每个方法调用至少需要10秒。现在,根据颗粒的单线程执行和Orleans运行时调度的描述(参见这里),我希望请求将被排队,除非当前请求的方法完成,否则下一个请求不会被颗粒接受。然而,控制台输出并不支持这一点。控制台输出如下:
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. 那么,这是否违反了Orleans单线程执行模型,还是我理解有误?
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”。 这是否意味着每个谷粒理想情况下应该在200毫秒内处理完毕?如果谷粒处理时间更长会发生什么?

3
它们是针对唯一标识符单线程运行的。TemperatureSensorGrain(grainId: 344) 不会并行执行多个轮次,但 TemperatureSensorGrain 可以并行执行许多不同的标识符。 - Dan Wilson
1
@DanWilson 是正确的:每个颗粒实际上是单线程的,但整个筒仓/集群并非如此(因为这不可扩展)。 - Reuben Bond
哦...我不知道我怎么错过了那个。谢谢。 - AvinashK
1个回答

4

正如评论中的@DanWilson所说,您观察到这种行为是因为每次调用都是在单独的grain上进行的。

在Orleans中,每个grain实际上是单线程的,但整个silo或集群不是。这意味着许多grain可以同时执行,这也意味着向主机添加更多核心或添加更多计算机将允许您扩展服务。

修改您的代码以仅选择一个grainId(通过将其移动到循环之外),我看到了这个示例执行:

137 received temperature: 18.74616
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
137 complete
137 received temperature: 20.03226
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
Client giving another request
137 complete
137 received temperature: 21.4471

正如您所预期的那样:许多请求正在排队(每秒一个),但每个请求在谷物开始处理下一个请求之前需要花费10秒钟才能完成。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接