Я пишу сложное распределенное приложение, пользуясь услугами WCF.
Требования
Мои требования следующие:
- На многих станциях (ПК) работает одно и то же программное обеспечение (приложение, которое мне нужно разработать).
- Каждая станция будет отправлять сообщения другим станциям (каждая станция имеет район). Станции будут маршрутизировать сообщения для достижения конечного пункта назначения для каждого сообщения (это контекст P2P, где необходима локальная маршрутизация).
- Когда сообщение доставляется станцией, эта станция должна быть уверена, что сообщение достигает пункта назначения (другой станции где-то в мире). По соображениям производительности я не могу создавать службы, которые маршрутизируют мое сообщение с использованием синхронных подходов (вызов службы будет длиться слишком долго, опрос никогда не был хорошей идеей). По этой причине рассматривается сообщение с обратной связью: как только сообщение достигает пункта назначения, пункт назначения отправляет сообщение I-Received-The-Message обратно отправителю.
- При таком подходе мне нужно, чтобы мои станции реализовывали сервисы для маршрутизации сообщений обратной связи. По сути, каждый раз, когда доставляется сообщение, таблица задач заполняется одной записью, указывающей, что доставка сообщения должна быть подтверждена. Если никакое сообщение обратной связи для этого сообщения не достигает станции отправителя, станция отправителя снова попытается отправить исходное сообщение.
Что я не могу сделать
Я знаю, что для сценариев P2P есть хорошо предоставленный тип сервиса, но я не могу использовать его по некоторым причинам (я не буду беспокоить вас по этим причинам).
Пожалуйста, примите требования, которые я перечислил выше.
Мое решение
Я принял это решение:
Два сервисных контракта определяют вызовы для отправки (маршрутизации) обычных сообщений и сообщений ответа / подтверждения доставки:
/* Routing routines */
[ServiceContract]
public interface IMessageRouting {
/* When a client receives the message, in the MyMessage type
there are some fields that helps the current station to
decide which neighbour station the received packet will
be routed to */
[OperationContract(IsOneWay = true)]
void RouteMessage(MyMessage msg);
}
/* Delivery-Confirm messaging */
[ServiceContract]
public interface IDeliveryConfirmMessageRouting {
/* When the final destination (THE FINAL DESTINATION
ONLY, not an intermediate hop station) obtains a
message, it will route back to the sender a reply message */
[OperationContract(IsOneWay = true)]
void RouteDeliveryConfirmMessage(MyDeliveryConfirmMessage dcmsg);
}
Вот реализации служб:
/* This service will be self-hosted by my application in order
to provide routing functionality to other stations */
[ServiceBehaviour(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class StationMessagingService : IMessageRouting {
/* Constructing the service */
public StationMessagingService() { ... }
// Implementation of serive operations
public void RouteMessage(MyMessage msg) {
...
}
}
И служба подтверждения доставки ...
/* This service will be self-hosted by my application in order
to provide delivery confirm message routing functionality
to other stations */
[ServiceBehaviour(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class StationDeliveryConfirmService : IDeliveryConfirmMessageRouting {
/* This service is particular, I will discuss the following lines
before the constructors in the next paragraph after first
typing all the code */
public delegate void DeliveryMessageReceivedEventHandler(
object sender, String DeliveryMessageReceivedEventArgs);
public event DeliveryMessageReceivedEventHandler DeliveryMessageReceived;
/* Constructing the service */
public StationDeliveryConfirmService() { ... }
// Implementation of serive operations
public void RouteDeliveryConfirmMessage(MyDeliveryConfirmMessage dcmsg) {
...
/* In the end fire the event only if I am the destination
of this message, otherwise I must route this message */
if (...) { /* Omitting condition for clarity */
this.DeliveryMessageReceived(this,
"A delivery confirm message has arrived with this info: " +
dcmsg.Info()); /* Info contains string info */
}
}
}
На данный момент я готов разместить свои услуги:
/* My program */
public class Program {
// Program's entry point
static void Main(string[] args) {
// Defining the delivery check table (I have a special type/class for this)
DeliveryCheckTable DCT = new DeliveryCheckTable(...);
// Creating services
StationMessagingService SMS = new StationMessagingService();
StationDeliveryConfirmService SDCS = new StationDeliveryConfirmService();
// Event handlers registration (expalinations in the next paragraph)
SDCS.DeliveryMessageReceived += Program.DeliveryMessageReceivedHandler;
// Hosting
Uri MyBaseAddress = new Uri("http://services.myapplication.com/Services/");
using (ServiceHost hostMessagingSvc = new ServiceHost(SMS, MyBaseAddress),
ServiceHost hostDeliveryConfirmSvc = new ServiceHost(SDCS,
MyBaseAddress)) {
// Info on endpoints in config file
// Running services
hostMessagingSvc.Open();
hostDeliveryConfirmSvc.Open();
// ...
// Application's other operations
// For clarity and simplicity, just consider that the code
// here is some kind of infinite loop with actions in it
// where the GUI can commununicate with the user, somewhere
// in the application's code, there is a List where all
// sent messages are inserted and, when a delivery
// confirm arrives, the corresponding item in the list is cleared.
// The list is rendered as a table by the GUI.
// ...
/*** Arriving here somehow when my application needs to be shut down. ***/
// Closing services
hostMessagingSvc.Close();
hostDeliveryConfirmSvc.Close();
}
}
/* Event handlers for the delivery confirm messages
service (please be patient, these lines of code
will be discussed in short) */
static void DeliveryMessageReceivedHandler(object sender,
string DeliveryMessageReceivedEventArgs) {
/* Here I will perform actions on the List
deleting the row containing the ID of the
message sent whose confirm has arrived */
}
} /* Program class */
Некоторые пояснения
Как вы можете видеть по коду (коду, который работает и работает правильно), мне удалось разрешить моей размещенной службе взаимодействовать с приложением хостинга через обратные вызовы.
Таким образом, типичный поток выглядит следующим образом:
- Моя соседка вызывает сервисную программу
void RouteMessage(... msg)
моего приложения, чтобы отправить мне сообщение.
- В процедуре обслуживания я проверю заголовок сообщения и поищу пункт назначения. Если пункт назначения не я, я направлю его к другому моему соседу (ближе к пункту назначения), в противном случае я буду использовать сообщение.
- Если я получу сообщение, мне придется отправить подтверждение.
- Я позвоню в соседнюю сервисную подпрограмму
void RouteDeliveryConfirmMessage(... msg)
моего соседа, чтобы позволить ей направить это сообщение о подтверждении доставки.
- Каждая станция направляет сообщения и, если станция обнаруживает, что является пунктом назначения, она потребляет сообщение. но когда сообщение является подтверждением, а станция является пунктом назначения, эта станция будет использовать подтверждение и сгенерирует событие
DeliveryMessageReceived
, вызывающее запуск процедуры обработчика и удаляющее соответствующую запись таблицы (так что отправитель получит подтверждение зная, что больше нет необходимости повторно отправлять сообщение, потому что оно было правильно получено).
Контекст приложения
Как видите, я не предоставил много подробностей о моем приложении, просто необходимых для понимания кода ... Это происходит в основном по следующим причинам:
- Я не хочу беспокоить вас своими проблемами и целями разработки приложений.
- Многое можно сказать о том, почему я выбрал некоторые подходы, но это будет зависеть от конкретного контекста, и я, вероятно, слишком углублюсь в несвязанную тему.
- Некоторые могут спросить: «Почему вам нужно заставить станцию маршрутизировать сообщение, а не предоставлять прямой обмен сообщениями от отправителя к получателю?», «Какова цель маршрутизации? Разве службы не позволяют вам напрямую вызывать станцию назначения конечная точка обслуживания? Что ж, мне нужно управлять одноранговыми узлами в сети, где одноранговые узлы немного знают всю сеть. Новые одноранговые узлы присоединяются к существующему, и у них есть только ссылки на конечные точки некоторых станций. Узлу не нужно иметь полное знание сети, у него есть соседство, и он использует это. Однако рассмотрите это как часть моих требований.
Вопрос
ОК, время для допроса. Всего один вопрос.
То, что я описал здесь, - это решение, которое мне удалось разработать, чтобы позволить службе взаимодействовать с приложением хостинга. Это проблема, для которой я не нашел правильный шаблон. Так что я нашел этот способ его кодирования ...
Это хорошо?
Это лучшая практика?
Это плохая практика?
Если это плохая практика, каков правильный способ / способ сделать это? Как решить коммуникационные проблемы между сервисом и его хостингом?
Thankyou