Взято отсюда: http://habrahabr.ru/post/128634/
Модель push-нотификаций является распространённой моделью для обмена сообщениями. Она подразумевает не получение информации по запросу, а немедленную её передачу отправителю при появлении этой информации на сервере.
Возможность создания push-механизма предоставляет и WCF. Этот фреймворк позволяет создать push-сервис с использованием wsDualHttpBinding контракта. Такой контракт позволяет для каждого запроса определить метод обратного вызова, который будет вызван при наступлении какого-либо события.
Если применить этот механизм к системе обмена сообщениями, то получим следующий алгоритм:
— Для каждого запроса на новые сообщения создаётся callback, который сохраняется в списке подписчиков на новые сообщения.
— При получении нового сообщения, система проходит по списку подписчиков и находит нужного нам получателя сообщения (а значит и нужный callback).
— Вызываем нужный нам callback-метод.
Ниже приведён пример использования wsDualHttpBinding для WCF сервиса:
— Создаём метод обратного вызова для запроса на новые сообщения
— Создаём сервис контракт
— Создаём сам сервис
— Конфигурируем сервис в файле web.config
Сервис готов.
Однако, эта модель работает только в том случае, когда и сервер, и подписчики являются .NET-приложениями.
Вышеописанный метод не подходит для случаев, когда подписчик, к примеру, является мобильным устройством и может использовать только запросы в формате REST.
В этом случае на помощь приходят асинхронные REST-запросы.
Итак, создадим сервис, аналогичный по функциональности предыдущему, только на основе REST.
В случае с асинхронной моделью запрос состоит из двух частей: BeginRequestName и EndRequestName.
— Определим ServiceContract для REST-сервиса
Обратите внимание: EndGetMessage не помечен аттрибутом OperationContact.
— Создадим класс для асинхронного результата, реализующий интерфейс IAsyncResult
Помимо реализации интерфейса, в этом классе также хранится Id получателя сообщения (recipientId), а также само сообщение, которое будет доставлено отправителю(result).
— Теперь реализуем сам сервис
Когда приходит запрос на получение нового сообщения, создаётся асинхронный результат, который добавляется в список подписчиков. Как только приходит сообщение для данного подписчика, свойство IsCompleted для данного IAsyncResult устанавливается в true, и вызывается метод EndGetMessage. В EndGetMessage отправляется ответ подписчику.
— Осталось сконфигурировать сервис в файле web.config
Сервис готов.
Очевидно, что при истечении времени ожидания ответа от сервиса, нужно будет переотправлять запрос на получение новых сообщений.
Таким образом можно реализовать Push-сервис для обмена сообщениями “в реальном времени”, основываясь на REST запросах. Такой сервис может использоваться с любого клиента, поддерживающиего RESTful реквесты, в том числе и из обычного браузера.
Создание Push Notification сервиса на основе WCF RESTиз песочницы
.NET*
В качестве вступления
Модель push-нотификаций является распространённой моделью для обмена сообщениями. Она подразумевает не получение информации по запросу, а немедленную её передачу отправителю при появлении этой информации на сервере.
Стандартный подход с ипользованием wsDualHttpBinding
Возможность создания push-механизма предоставляет и WCF. Этот фреймворк позволяет создать push-сервис с использованием wsDualHttpBinding контракта. Такой контракт позволяет для каждого запроса определить метод обратного вызова, который будет вызван при наступлении какого-либо события.
Если применить этот механизм к системе обмена сообщениями, то получим следующий алгоритм:
— Для каждого запроса на новые сообщения создаётся callback, который сохраняется в списке подписчиков на новые сообщения.
— При получении нового сообщения, система проходит по списку подписчиков и находит нужного нам получателя сообщения (а значит и нужный callback).
— Вызываем нужный нам callback-метод.
Ниже приведён пример использования wsDualHttpBinding для WCF сервиса:
— Создаём метод обратного вызова для запроса на новые сообщения
interface IMessageCallback
{
[OperationContract(IsOneWay = true)]
void OnMessageAdded(int senderId, string message, DateTime timestamp);
}
— Создаём сервис контракт
[ServiceContract(CallbackContract = typeof(IMessageCallback))]
public interface IMessageService
{
[OperationContract]
void AddMessage(int senderId, int recipientId, string message);
[OperationContract]
bool Subscribe();
}
— Создаём сам сервис
Public class MessageService : IMessageService
{
private static List<IMessageCallback> subscribers = new List<IMessageCallback>();
public bool Subscribe(int id)
{
try
{
IMessageCallback callback =
OperationContext.Current.GetCallbackChannel<IMessageCallback>();
callback.id = id;
if (!subscribers.Contains(callback))
subscribers.Add(callback);
return true;
}
catch
{
return false;
}
}
public void AddMessage(int senderId, int recipientId, string message)
{
subscribers.ForEach(delegate(IMessageCallback callback)
{
if ((((ICommunicationObject)callback).State == CommunicationState.Opened) && (callback.id == recipientId))
{
callback.OnMessageAdded(recipientId, message, DateTime.Now);
}
else
{
subscribers.Remove(callback);
}
});
}
}
— Конфигурируем сервис в файле web.config
<system.serviceModel>
<services>
<service name="WCFPush.MessageService" behaviorConfiguration="Default">
<endpoint address ="" binding="wsDualHttpBinding" contract="WCFPush.IMessage">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Сервис готов.
Однако, эта модель работает только в том случае, когда и сервер, и подписчики являются .NET-приложениями.
Использование RESTful подхода
Вышеописанный метод не подходит для случаев, когда подписчик, к примеру, является мобильным устройством и может использовать только запросы в формате REST.
В этом случае на помощь приходят асинхронные REST-запросы.
Итак, создадим сервис, аналогичный по функциональности предыдущему, только на основе REST.
В случае с асинхронной моделью запрос состоит из двух частей: BeginRequestName и EndRequestName.
— Определим ServiceContract для REST-сервиса
[ServiceContract]
public interface IMessageService
{
[WebGet(UriTemplate = "AddMessage?senderId={senderId}&recipientId={recipientId}&message={message}")]
[OperationContract ]
bool AddMessage(int senderId, int recipientId, string message);
[WebGet(UriTemplate = "Subscribe?id={id}")]
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetMessage(int id, AsyncCallback callback, object asyncState);
ServiceMessage EndGetMessage(IAsyncResult result);
}
Обратите внимание: EndGetMessage не помечен аттрибутом OperationContact.
— Создадим класс для асинхронного результата, реализующий интерфейс IAsyncResult
public class MessageAsyncResult : IAsyncResult
{
public AsyncCallback Callback { get; set; }
private readonly object accessLock = new object();
private bool isCompleted = false;
private ServiceMessage result;
private int recipientId;
private object asyncState;
public MessageAsyncResult(object state)
{
asyncState = state;
}
public int RecipientId
{
get
{
lock (accessLock)
{
return recipientId;
}
}
set
{
lock (accessLock)
{
recipientId = value;
}
}
}
public ServiceMessage Result
{
get
{
lock (accessLock)
{
return result;
}
}
set
{
lock (accessLock)
{
result = value;
}
}
}
public bool IsCompleted
{
get
{
lock (accessLock)
{
return isCompleted;
}
}
set
{
lock (accessLock)
{
isCompleted = value;
}
}
}
public bool CompletedSynchronously
{
get
{
return false;
}
}
public object AsyncState
{
get
{
return asyncState;
}
}
public WaitHandle AsyncWaitHandle
{
get
{
return null;
}
}
}
Помимо реализации интерфейса, в этом классе также хранится Id получателя сообщения (recipientId), а также само сообщение, которое будет доставлено отправителю(result).
— Теперь реализуем сам сервис
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.PerCall,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MessageService : IMessageService
{
private static List<MessageAsyncResult> subscribers = new List<MessageAsyncResult>();
public bool AddMessage(int senderId, int recipientId, string message)
{
subscribers.ForEach(delegate(MessageAsyncResult result)
{
if (result.RecipientId == recipientId)
{
result.Result = new ServiceMessage(senderId, recipientId, message, DateTime.Now);
result.IsCompleted = true;
result.Callback(result);
subscribers.Remove(result);
}
});
return true;
}
public IAsyncResult BeginGetMessage(int id, AsyncCallback callback, object asyncState)
{
MessageAsyncResult asyncResult = new MessageAsyncResult(asyncState);
asyncResult.Callback = callback;
asyncResult.RecipientId = id;
subscribers.Add(asyncResult);
return asyncResult;
}
public ServiceMessage EndGetMessage(IAsyncResult result)
{
return (result as MessageAsyncResult).Result;
}
}
Когда приходит запрос на получение нового сообщения, создаётся асинхронный результат, который добавляется в список подписчиков. Как только приходит сообщение для данного подписчика, свойство IsCompleted для данного IAsyncResult устанавливается в true, и вызывается метод EndGetMessage. В EndGetMessage отправляется ответ подписчику.
— Осталось сконфигурировать сервис в файле web.config
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webBinding">
</binding>
</webHttpBinding>
</bindings>
<services>
<service name=" WCFPush.MessageService" behaviorConfiguration="Default">
<endpoint address="" contract="WCFPush.IMessageService" behaviorConfiguration="web" bindingConfiguration="webBinding" binding="webHttpBinding">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Сервис готов.
Очевидно, что при истечении времени ожидания ответа от сервиса, нужно будет переотправлять запрос на получение новых сообщений.
Заключение
Таким образом можно реализовать Push-сервис для обмена сообщениями “в реальном времени”, основываясь на REST запросах. Такой сервис может использоваться с любого клиента, поддерживающиего RESTful реквесты, в том числе и из обычного браузера.
Комментариев нет:
Отправить комментарий