Однопоточность в зернах Орлеана

Я пытаюсь понять однопоточность зерна в Microsoft Orleans. Я использовал этот код и немного его изменил, чтобы проверить мои сценарии.

Мой код клиента и строительный код бункера

    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 секунд. Теперь, согласно описанному здесь однопоточному выполнению зерен и планированию времени выполнения Орлеана, я ожидаю, что запросы будут поставлены в очередь, а следующий запрос не будет обрабатываться зерном, пока не завершится метод текущего запроса. Тем не менее, вывод консоли не подтверждает это. Консольный вывод:

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, Значит ли это, что каждое зерно в идеале должно обрабатываться в течение 200 мс? Что произойдет, если зерна будут обрабатываться дольше?

1 ответ

Решение

Как говорит @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 секунд, прежде чем зерно может начать обработку следующего запроса.

Другие вопросы по тегам