Подписчик 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 к многоадресной рассылке или балансировке?