Асинхронный запрос / ответ в Proto.Actor? - PullRequest
0 голосов
/ 15 сентября 2018

Я новичок в программировании на proto.actor/actor, и мне интересно, возможно ли добиться такого поведения:

Актер A запрашивает актера B с помощью асинхронной команды - он должен ждать ответа для достижения модели запрос / ответ, но с использованием задач. Актер B использует HTTP-запрос, так что это будет некоторая асинхронная операция ввода-вывода, поэтому я не хочу, чтобы она была заблокирована для других актеров в это время, поэтому, когда 10 актеров будут спрашивать его одновременно, каждый запрос будет поставлен в очередь, но пока он первый запрос ожидает обработки, второй должен получить возможность продолжить. После того, как первый запрос будет завершен, он должен иметь приоритет в очереди и получить ответ субъекту А.

Как получить этот поток?

Например, у меня есть 3 клиента, которые запрашивают у службы некоторые данные, сервисный звонок занимает 5 секунд, и большую часть этого времени служба тратит на ввод-вывод. В текущей реализации у нас есть всего 15 секунд для всех запросов, но я бы хотел, чтобы это заняло ~ 5-6 секунд

 public static class ProtoTest
    {
        public static PID Service;

        public static async Task Start()
        {
            var context = new RootContext();

            var props = Props.FromProducer(() => new ClientActor());
            var serviceProps = Props.FromProducer(() => new ServiceActor());
            Service = context.Spawn(serviceProps);

            var jobs = new List<Task>();
            for (int i = 0; i < 3; i++)
            {
                string actorName = $"Actor_{i}";
                jobs.Add(Task.Run(() =>
                {
                    var client = context.SpawnNamed(props, actorName);
                    context.Send(client, new Command());
                }));
            }

            Console.ReadLine();
        }
    }

    public class ClientActor : IActor
    {
        public virtual async Task ReceiveAsync(IContext context)
        {
            if (context.Message is Command)
            {
                Console.WriteLine($"{DateTime.Now.ToLongTimeString()} START processing by {context.Self.Id}");
                var result = await context.RequestAsync<string>(ProtoTest.Service, new Query());
                Console.WriteLine($"{DateTime.Now.ToLongTimeString()} End processing by {context.Self.Id}");
            }

            return;
        }
    }

    public class ServiceActor : IActor
    {
        public async virtual Task ReceiveAsync(IContext context)
        {
            if (context.Message is Query)
            {
                // this operation is taking long time so actor could handle others in this time
                await Task.Delay(5000);

                context.Respond("result");
            }

            return;
        }
    }

1 Ответ

0 голосов
/ 09 октября 2018

Один из основных принципов актера заключается в том, что он не выполняет несколько операций параллельно.Если я правильно понимаю вашу проблему, то вместо этого вы можете создать нового актера для каждой операции, которую вы хотите запустить параллельно (актеры дешевы, поэтому создание многих не является проблемой).Таким образом, если субъекту A необходимо отправить N команд для асинхронной обработки и получать каждый результат по мере их поступления, он может порождать N участников, B1, B2 ... Bn (по одной для каждой команды) и отправлять запрос каждому из них.Актеры B ждут результата, а затем отвечают обратно актеру.Каждый ответ затем будет отправлен в виде сообщения в почтовый ящик субъекта А. и будет обработан последовательно в порядке их завершения.

...