我试图了解Microsoft Orleans中的谷物单线程。我使用了here中的代码,并对其进行了一些修改以测试我的方案。

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

    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);
                }
            }
        }
    }


我的Grain接口和实际的Grain实现

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


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

问题:


那么,这是否违反了奥尔良单线程执行模型,还是我在这里遗漏了一些东西?
另外,当我在谷物内部使用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内理想地处理?如果谷物加工时间更长,会发生什么?

最佳答案

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

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

修改您的代码以仅选择一个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秒才能完成,谷物可以开始处理下一个请求。

关于c# - 奥尔良 Cereal 中的单线程,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51527335/

10-13 08:50