Результат вызова задачи на зерно в Орлеане
Я прошу прощения за длинный вопрос. Я экспериментировал с Орлеаном, чтобы узнать о его различных свойствах, и эти вопросы логически находятся под одним зонтиком.
Первый тест включал выполнение запроса от клиента к определенному фрагменту данных каждую 1 секунду, в то время как для выполнения запросов коэффициенту требуется 10 секунд. Код такой:
// client code
while (1)
{
Console.WriteLine("Client giving another request");
double temperature = random.NextDouble() * 40;
var sensor = client.GetGrain<ITemperatureSensorGrain>(500);
Task t = sensor.SubmitTemperatureAsync((float)temperature);
Console.WriteLine(t.Status);
Thread.Sleep(1000);
}
// grain code
public Task SubmitTemperatureAsync(float temperature)
{
long grainId = this.GetPrimaryKeyLong();
Console.WriteLine($"{grainId} outer received temperature: {temperature}");
Thread.Sleep(10000);
Console.WriteLine($"{grainId} outer complete");
return Task.CompletedTask;
}
Консольный вывод:
Client giving another request
Task Status - WaitingForActivation
500 outer received temperature: 32.29987 <------------ print statement inside grain
Client giving another request <--------------------- client continues
Task Status - WaitingForActivation <------------------- client isn't blocked
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
Client giving another request
Task Status - WaitingForActivation
500 outer complete
Поскольку зерна в Орлеане являются однопоточными, вызывается только первый запрос, а остальные запросы ставятся в очередь на стороне зерна. Мои вопросы по этой части: -
В обычном C#, когда вызывается асинхронный метод, он продолжается в основном потоке, пока не достигнет оператора await, когда он запускает ожидаемое выражение как другую задачу и возвращает эту задачу. Таким образом, вызывающий абонент блокируется до тех пор, пока не будет нажата инструкция await. Точно так же и здесь клиент должен быть заблокирован на 10 секунд, после чего первый запрос на зерно возвращает задачу. Однако этого не происходит. Клиент продолжает планировать задачи без блокировки.
- Итак, это зов зерна от клиента
FireAndForget
? - Если да, то как они возвращают объект Task?
- Существуют ли какие-либо блокировки, когда клиент выполняет вызов объекта зерна, а среда выполнения возвращает объект Task клиенту?
Второй тест включал выполнение запроса от зерна к зерну, в котором второе зерно ждет 10 секунд, прежде чем вернуться. Код такой:
// client code
while (1)
{
Console.WriteLine("Client giving another request");
double temperature = random.NextDouble() * 40;
var sensor = client.GetGrain<ITemperatureSensorGrain>(500);
Task t = sensor.SubmitTemperatureAsync((float)temperature);
Console.WriteLine("Client Task Status - "+t.Status);
// make client sleep for a long time after the first request
// because we don't want any more requests from the client
Thread.Sleep(1000000000);
}
// outer-grain (ITemperatureSensorGrain) code
public async Task SubmitTemperatureAsync(float temperature)
{
long grainId = this.GetPrimaryKeyLong();
Console.WriteLine($"{grainId} outer received temperature: {temperature}");
while(true)
{
Console.WriteLine("Grain sending another request");
ITempBGrain sensor = this.GrainFactory.GetGrain<ITempBGrain>(400);
// await sensor.SubmitTempBAsync(temperature);
Task t = sensor.SubmitTempBAsync(temperature);
Console.WriteLine("Grain Task Status - "+t.Status);
Thread.Sleep(1000);
}
}
// inner-grain (ITempBGrain) code
public Task SubmitTempBAsync(float temperature)
{
long grainId = this.GetPrimaryKeyLong();
Console.WriteLine($"{grainId} internal received temperature: {temperature}");
Thread.Sleep(10000);
Console.WriteLine($"{grainId} internal complete");
return Task.CompletedTask;
}
Консольный вывод:
Client giving another request
Client Task Status - WaitingForActivation
500 outer received temperature: 10.36764
Grain sending another request <-------------- Outer grain prints
Grain Task Status - WaitingForActivation
Grain sending another request <----------- Inner grain doesn't print
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
Grain sending another request
Grain Task Status - WaitingForActivation
warn: Orleans.Runtime.CallbackData[100157]
Response did not arrive on time in 00:00:30 for message: Request *cli/015ba7a5@d4cdc7ab->S127.0.0.1:30000:0*grn/6424EE47/000001f4 #17: . Target History is: <S127.0.0.1:30000:0:*grn/6424EE47/000001f4:>. About to break its promise.
Grain sending another request
Grain Task Status - WaitingForActivation
Здесь я вижу нечто похожее на то, что произошло с клиентом в первом эксперименте. Итак, эти вопросы все еще там. Однако здесь происходит еще одна странная вещь. Вывод на консоль внутреннего зерна нигде не появляется. Почему внутреннее зерно не выполняется? Если я включаю закомментированную строку в коде внешнего зерна и ожидаю задачу внутреннего зерна, появляется следующий вывод, который кажется действительным.
Client giving another request
Client Task Status - WaitingForActivation
500 outer received temperature: 6.332514
Grain sending another request
400 internal received temperature: 6.332514
400 internal complete
Grain sending another request
400 internal received temperature: 6.332514
1 ответ
Первая часть
Нет, при вызове зерна нет блокировки. Этот пост дополнительно разъясняет, что происходит, когда выполняется зерновой вызов.
Вторая часть
Хотя правильно, что зерна однопоточные, неправильно полагать, что каждое зерно имеет свою нить в Орлеане. Как говорит @Tseng Orleans uses the async feature of .NET Core. It will process a grain until an async operation happens. Then it returns the thread to the thread-pool. This thread can be used by another grain to process data until the async operation is complete. When its complete, it resumes. its not necessary the same thread (but its the same context)
, Первое зерно блокирует поток, не давая возможности для второго зерна исполниться.
Спасибо Tseng и Reuben Bond за разъяснения.