Страницы

Поиск по вопросам

суббота, 13 октября 2018 г.

Как грамотно завершить работу self hosted wcf службы?

Допустим есть wcf служба, которая хостится в win service.
Если пользователь решит вырубить сервис, то как мне грамотно завершить работу wcf службы?
Допустим, один юзер вызвал метод по перемещению файлов, другой юзер что-то делает с базой.
Если без логики завершить работу Win Service, то это приведет к необратимым последствиям.


Ответ

Можно попробовать при остановке Win Service сигналить в WCF Service с помощью CancellationToken о том, что Win Service собирается останавливаться. WCF Service внутри себя должен проверять состояние данного CancellationToken, и, если запрошена отмена, выдавать исключение для вновь запрашиваемых операций, а уже выполняющиеся, например, прерывать. Win Service должен дождаться завершения отмены активных операций выполняемых WCF Service. Для отслеживания этого в WCF Service можно, к примеру, использовать ManualResetEvent и счётчик активных операций.
Ниже примерная реализация (с достаточным числом упрощений).
Контракт:
[ServiceContract] public interface IWCFService { [OperationContract] string Echo(string str); }
WCF Service:
public class WCFService : IWCFService { CancellationToken ctOperations;
public WCFService() { //у WinService берём CancellationToken остановки операций ctOperations = MyService.GetOperationsCancellationToken(); }
public string Echo(string str) { //не выполняем новые операции, если началась остановка if (ctOperations.IsCancellationRequested) throw new Exception("Service stopping.");
try { //увеличиваем счётчик активных операций IncActiveCnt();
//имитация деятельности int i = 0; while (i++ < 10) { ctOperations.ThrowIfCancellationRequested(); Thread.Sleep(500); }
return "Echo " + str; } catch (OperationCanceledException) { throw new Exception("Service stopping."); } catch (Exception) { throw; } finally { //уменьшаем счётчик активных операций DecActiveCnt(); } }
static object lockObj = new object(); static int activeOperationCnt = 0; static ManualResetEvent evtNoActiveOperations = new ManualResetEvent(true); //свойство для проверки в WinService static public ManualResetEvent NoActiveOperations { get { return evtNoActiveOperations; } }
private void DecActiveCnt() { lock (lockObj) { if (--activeOperationCnt == 0) //сигналим, если нет активных операций evtNoActiveOperations.Set(); } }
private void IncActiveCnt() { lock (lockObj) { activeOperationCnt++; //сбрасываем сигнал, если есть активные операции evtNoActiveOperations.Reset(); } } }
Win Service (в данном примере не внешнее приложение, а он сам себе является клиентом WCF Service):
public class MyService : ServiceBase { static CancellationTokenSource ctsOperations = null; static ServiceHost svcHost = null;
public MyService() { }
protected override void OnStart(string[] args) { //токен для остановки операций ctsOperations = new CancellationTokenSource();
svcHost = new ServiceHost(typeof(WCFService)); svcHost.Open();
//запускаем клиента WCF Service ThreadPool.QueueUserWorkItem(UseWCFService); //ждём 13 сек. Thread.Sleep(13000); //теперь остановим Win Service Stop(); }
protected override void OnStop() { //сигналим отмену операций ctsOperations.Cancel();
int msecExtend = 3000; RequestAdditionalTime(msecExtend); //ждём завершение отмены операций WCFService.NoActiveOperations.WaitOne(msecExtend);
svcHost.Close(); ctsOperations.Dispose(); }
public static CancellationToken GetOperationsCancellationToken() { return ctsOperations.Token; }
private void UseWCFService(object state) { //имитируем клиента Uri tcpUri = new Uri(@"http://localhost:8733/WCFService/"); EndpointAddress address = new EndpointAddress(tcpUri); BasicHttpBinding binding = new BasicHttpBinding(); ChannelFactory f = new ChannelFactory(binding, address); IWCFService svc = f.CreateChannel();
int i = 0; while (!ctsOperations.IsCancellationRequested) try { string echo = svc.Echo((++i).ToString()); Console.WriteLine(echo); } catch { Console.WriteLine("Error"); } } }

Комментариев нет:

Отправить комментарий