ServiceStack.Redis: Настроить так, чтобы класс запроса и ответа / dto был одним и тем же классом? - PullRequest
1 голос
/ 05 мая 2020

Я использовал ServiceStack некоторое время назад, и мне хорошо известно о предпочтительном дизайне API на основе сообщений, и это то, что я использую в некоторых API на основе REST.

библиотеку Redis / MQ и, как всегда, наслаждайтесь структурой и функциональностью ServiceStack. Тем не менее, я пытаюсь заменить некоторый устаревший коммуникационный код на сервер MQ, и протестировал некоторые примеры SS , и он работает хорошо.

Однако с некоторым устаревшим кодом я работаю использует один и тот же класс для исходящего запроса и ответа, поэтому отправляется GetSomething, а ответ является экземпляром того же класса GetSomething, но со свойством, например GetSomething.Result, которое содержит ответ / результат.

Поскольку мне нужна была прямая замена текущей модели связи, я посмотрел, может ли этот сценарий поддерживаться "из коробки", но я действительно не нашел ничего, что могло бы решить эту проблему. Когда я делаю подобные вещи в Consumer, у которого есть Handler:

mqHost.RegisterHandler<GetSomething>(base.ExecuteMessage);

, и у издателя, который хочет получить ответ:

mqServer.RegisterHandler<GetSomething>(m => {...});

, происходит то, что издатель забирает запрос немедленно, и он никогда не достигает Потребителя. Если я удалю прослушиватель ответа в издателе, он достигнет потребителя, но когда потребитель затем отвечает тем же DTO, GetSomething, он застревает в вечном l oop, потому что я думаю об ответе помещается в ту же очередь MQ.

Есть ли разумный способ решить эту проблему с помощью ServiceStack?

У меня есть некоторые идеи возможных обходных путей, но я хотел бы знать, можно ли это решаться лучше и разумнее.

Ответы [ 2 ]

0 голосов
/ 06 мая 2020

Рабочий процесс ServiceStack MQ Message определен в документации:

ServiceStack MQ Message Workflow

И объясняет, что происходит, когда:

Сообщения без ответов отправляются на '.outq' Topi c

Сообщения с ответами публикуются в ответе .inq

Ответы на сообщения с ReplyTo публикуются на этот адрес

Сообщения с исключениями проверяются повторно, а затем публикуются в очереди недоставленных сообщений .dlq

Таким образом, возвращение Request DTO помещает его в INQ этого Request DTO, который будет выполняться обработчиком, зарегистрированным для его обработки, который в данном случае оказывается самим собой, то есть l oop. В Redis MQ Services, возвращая null или void, публикует его в переходный / скользящий ответ DTO Redis .outq.

0 голосов
/ 06 мая 2020

Я просто хотел поделиться одним обходным путем, который может быть не самым красивым, но, похоже, работает. Мне все еще интересно, есть ли более эффективные способы сделать это.

Издатель:

Издатель назначает RequestFilter на RedisMqServer и в этом методе изменяет .Body, заменяя оболочку фактическим запросом.

Издатель затем вызывает .RegisterHandler один раз для класса оболочки ответа, а затем для каждого фактического / реального Обработчика, как задумано. В результате будет вызван правильный обработчик службы:

    public RedisClient(string name)
    {
        Name = name;
        redisFactory = new PooledRedisClientManager("localhost:6379");
        mqServer = new RedisMqServer(redisFactory, retryCount: 2);

        mqServer.RequestFilter = RequestFilter;

        // Response wrapper, ContainerResponse implements IProtocolContainer
        mqServer.RegisterHandler<ContainerResponse>(m => 
        {
            return m;
        });

        mqServer.RegisterHandler<GetSomething>(m =>
        {
            // m.Body is here an GetSomething
            return null;
        });
        mqServer.Start();
    }

    private ServiceStack.Messaging.IMessage RequestFilter(ServiceStack.Messaging.IMessage message)
    {
        if (message.Body is IProtocolContainer protocolContainer)
        {
            message.Body = protocolContainer.TheRequest;
        }
        return message;
    }

    public void AddMessage<T>(T theRequest) where T : CoreRequest
    {
        using (var mqClient = mqServer.CreateMessageQueueClient())
        {
            mqClient.Publish(new ContainerRequest(theRequest));
        }
    }
}

Потребитель:

Тот же принцип применяется для потребителя:

    public override void Configure(Container container)
    {
        container.Register(new ConsumerInfo() { Name = ServiceName });

        var redisFactory = new PooledRedisClientManager("localhost:6379");
        container.Register<IRedisClientsManager>(redisFactory);
        var mqHost = new RedisMqServer(redisFactory, retryCount: 2);

        mqHost.RequestFilter = RequestFilter;
        mqHost.ResponseFilter = ResponseFilter;

        mqHost.RegisterHandler<ContainerRequest>(base.ExecuteMessage);
        mqHost.RegisterHandler<GetSomething>(base.ExecuteMessage);
        mqHost.Start();
    }

    private object ResponseFilter(object arg)
    {
        return new ContainerResponse(arg as CoreRequest);
    }

    private ServiceStack.Messaging.IMessage RequestFilter(ServiceStack.Messaging.IMessage message)
    {
        if (message.Body is IProtocolContainer protocolContainer)
        {
            System.Diagnostics.Debug.WriteLine($"\tReplaced Body with {protocolContainer.TheRequest.GetType().Name}");
            message.Body = protocolContainer.TheRequest;
        }
        return message;
    }
}
...