Подписчик WCF на Azure SB с несколькими операциями

Я пытаюсь создать службу WCF, которая одновременно публикует сообщения в теме, а также подписывается на эту тему. Идея состоит в том, чтобы мой сервис предоставлял конечные точки для управления клиентом (т. Е. CreateCustomer, EditCustomer, DeleteCustomer и т. Д.). Затем я хочу опубликовать сообщение в теме после завершения каждой операции (т. Е. OnCustomerCreated, OnCustomerChanged, OnCustomerDeleted и т. Д.).

Например, клиентское приложение получит сообщение EditCustomer на моем сервисе. Я немедленно опубликую сообщение OnCustomerChanged с предоставленным объектом customer. У моей службы (той же, что и у клиента) будет другой контракт, который принимает OnCustomerChanged и обновляет мою базу данных.

У меня вопрос: нужно ли сделать отдельную подписку на мою тему для каждого типа сообщений (например, OnCustomerChangedSubscription, OnCustomerDeletedSubscription и т. Д.), Чтобы я мог правильно направлять сообщения различных типов на правильную конечную точку?

Если это так, мне понадобится несколько контрактов с одним методом, чтобы я мог правильно настроить конечные точки wcf:

то есть:

 <service name="site.Services.Business.Managers.CustomerManager">
   <!-- endpoint that clients will hit -->
    <endpoint address="" binding="basicHttpBinding" contract="site.Services.Business.Contracts.ICustomerManager" />
   <!-- endpoint that publishes messages-->
    <endpoint address="sb://test-site.servicebus.windows.net/Managers/CustomerManager" 
              binding="netTcpRelayBinding" 
              contract="site.Services.Business.Contracts.ICustomerManager" 
              behaviorConfiguration="sbTokenProvider" />
    <!-- One Endpoint for each message type (this will get very cumbersome and the contract will only have 1 method on it) -->
    <endpoint address="sb://test-site.servicebus.windows.net/Managers/CustomerManager"
                  binding="netMessagingBinding"
                  listenUri="sb://test-site.servicebus.windows.net/Managers/CustomerManager/subscriptions/OnCustomerDeleted"
                  behaviorConfiguration ="sbTokenProvider"
                  contract="site.Services.Business.Contracts.CustomerManager.IOnCustomerDeleted" />
    <endpoint address="sb://test-site.servicebus.windows.net/Managers/CustomerManager"
                  binding="netMessagingBinding"
                  listenUri="sb://test-site.servicebus.windows.net/Managers/CustomerManager/subscriptions/OnCustomerCreated"
                  behaviorConfiguration ="sbTokenProvider"
                  contract="site.Services.Business.Contracts.CustomerManager.IOnCustomerCreated" />
…etc

  </service>

Альтернативой может быть создание одного подписчика (Allmessages), имеющего только один контракт с HandleMessage(BrokeredMessage message) операции, а затем определить внутри этого метода с методом для вызова на моем сервисе. Это не похоже, что я делаю правильные вещи там, хотя По сути, я принимаю все сообщения и определяю обработчик внутри службы.

То, что я ищу, - это способ иметь службу, которая реализует 3 контракта: ICustomerPublisher (уже есть), ICustomerManager (предоставляется клиентам через http) и ICustomerSubscriber.

ICustomerSubscriber будет выглядеть так:

[ServiceContract]
public interface ICustomerSubscriber
{
    [OperationContract(IsOneWay = true)]
    void OnCustomerCreated(ICustomerMessage message);

    [OperationContract(IsOneWay = true)]
    void OnCustomerDeleted(ICustomerMessage message);

    [OperationContract(IsOneWay = true)]
    void OnCustomerChanged(ICustomerMessage message);
}

и я смогу позвонить:

publisher.Publish<OnCustomerChanged>(new CustomerChangedMessage(customer));

и мой метод OnCustomerChanged получил это сообщение.

Любая помощь будет оценена.

2 ответа

Во-первых: Да, если служба имеет активацию по запросу, ваш SubscriptionClient не сможет работать, за исключением тех случаев, когда служба отвечает на внешний запрос, и ее нужно будет каждый раз создавать и удалять. Единственный возможный способ сохранить работоспособность SubscriptionClient - это изменить активацию на синглтон.

Но я думаю, что лучший подход состоит в том, чтобы полностью вытащить SubscriptionClient из этого сервиса и запустить его самостоятельно. Если вы хотите, чтобы он запускался предварительно, то это может быть приложение службы Windows или консоль; в облаке это может быть WebJob или рабочая роль. Опять же, я не понимаю, почему ваш метод OnMessage должен быть операцией WCF.

Второе: если подписка только одна, то каждое сообщение может быть получено и завершено только одним клиентом. Если нескольким клиентам необходимо получить копию каждого сообщения, каждому клиенту нужна своя подписка. Подписки могут использовать одни и те же условия фильтрации или нет условий.

Я не смог найти подходящий способ выполнить то, что я упомянул выше. Вместо этого я сделал следующее:

Создана подписка по умолчанию на тему моего клиента без фильтра.

В конструкторе для моей службы WCF я использую SubscriptionClient для регистрации в качестве подписчика:

    public CustomerManager()
    {
        //set up automapper and IoC
        Initializer.Initialize();
        Publisher = IoCContainer.GetContainer().Resolve<IPublisher>("CustomerManager");
        Publisher.Subscribe("AllMessages");

        var client =
            SubscriptionClient.CreateFromConnectionString(CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString"), "customertopic", "AllMessages", ReceiveMode.PeekLock);

        client.OnMessage(m =>
        {
            Console.WriteLine("Message Received.");
            HandleMessage(m);
        });
    }

Мой метод HandleMessage принимает BrokeredMessage в качестве параметра и затем определяет, какую внутреннюю операцию вызывать, основываясь на типе тела сообщения.

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void HandleMessage(BrokeredMessage message)
    {

        var customerMessage = message.GetBody<CustomerMessage>();

        switch (customerMessage.EventName)
        {
            case "OnCreated" : 
                OnCustomerCreated(customerMessage);
                break;
            case "OnDeleted" : 
                OnCustomerDeleted(customerMessage as OnDeleted);
                break;
            case "OnChanged" :
                OnCustomerChanged(customerMessage as OnChanged);
                break;
        }

        message.Complete(); //mark the message as completed

    }

У меня есть пара проблем, хотя.

Во-первых, я использую экземпляр для каждого запроса для моей службы. Это вызовет какие-либо проблемы с согласованностью сообщений? Например, если у меня есть несколько экземпляров CustomerManager, будет ли каждый пытаться обрабатывать одно и то же сообщение, или SubscriptionClient гарантирует, что только 1 CustomerManager получит сообщение?

Во-вторых, я хочу иметь возможность подписаться на другие сервисы для моей подписки AllMessages. Одним из примеров является служба уведомлений. Я могу захотеть отправить push-уведомление владельцу учетной записи при создании нового клиента (OnCustomerCreated), или я могу попросить клиента проверить информацию, которая изменилась в его учетной записи (OnCustomerChanged). Могу ли я по-прежнему подписывать другие сервисы на CustomerTopic AllMessages или пометить сообщение как завершенное (как я делаю в конце моего метода HandleMessage), чтобы очистить сообщение и для других подписчиков? Более технически, приведет ли добавление другого подписчика к подписке AllMessages к многоадресной рассылке или балансировке?

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