public void SendToOneChannel(object stateInfo) { var descriptor = (ChannelDescriptor)stateInfo; _identityProvider.Identity = new Identity(descriptor.CustomerCode); var channelService = ObjectFactoryBase.Resolve <INotificationChannelService>(); var config = ConfigDictionary[descriptor.CustomerCode]; try { var key = GetKey(descriptor.ChannelName, descriptor.CustomerCode); var state = _lockers[key]; if (Monitor.TryEnter(state)) { try { if (!state.BlockState.HasValue || state.BlockState.Value.AddSeconds(config.WaitIntervalAfterErrors) <= DateTime.Now) { if (state.BlockState.HasValue) { _logger.Info( "Temporary channel lock has been released for channel {channel}, customer code {customerCode}", descriptor.ChannelName, descriptor.CustomerCode ); state.BlockState = null; //снимаем блокировку, если прошел указанные интервал и пробуем отправить снова } var service = ObjectFactoryBase.Resolve <IMessageService>(); var res = service.GetMessagesToSend(descriptor.ChannelName, config.PackageSize); if (res.IsSucceeded) { var channel = GetChannel(config, descriptor.ChannelName); var semaphore = new SemaphoreSlim(Math.Max(channel.DegreeOfParallelism, 1)); var localState = new ChannelState() { ErrorsCount = 0 }; var factoryMap = res.Result.Select(m => m.Key).Distinct() .ToDictionary(k => k, k => new TaskFactory(new OrderedTaskScheduler())); var tasks = res.Result .Select(m => SendOneMessage(descriptor.CustomerCode, descriptor.InstanceId, config, channel, service, m, semaphore, factoryMap[m.Key], localState, channelService)) .ToArray(); Task.WaitAll(tasks); if (localState.ErrorsCount >= config.ErrorCountBeforeWait) { state.BlockState = DateTime.Now; _logger.Info( "Temporary channel lock has been acquired for channel {channel}, customer code {customerCode}", channel.Name, descriptor.CustomerCode ); } } else { state.BlockState = DateTime.Now; _logger.Info( "Queue for channel {channel}, customer code {customerCode} is unavailable, temporary lock will be acquired", descriptor.ChannelName, descriptor.CustomerCode ); } } } finally { Monitor.Exit(state); } } else { _logger.Info( "Queue for channel {channel}, {customerCode} is busy", descriptor.ChannelName, descriptor.CustomerCode ); } } catch (Exception ex) { _logger.Error().Exception(ex) .Message( "An error occured while processing messages from the queue for channel {channel}, customer code {customerCode}", descriptor.ChannelName, descriptor.CustomerCode ) .Write(); } }
private async Task SendOneMessage( string customerCode, string instanceId, NotificationSenderConfig config, NotificationChannel channel, IMessageService service, Message message, SemaphoreSlim semaphore, TaskFactory factory, ChannelState state, INotificationChannelService channelService) { await factory.StartNew(() => { lock (state) { if (state.ErrorsCount >= config.ErrorCountBeforeWait) { return; } } var timer = new Stopwatch(); timer.Start(); string url = GetUrl(customerCode, instanceId, channel, message); try { semaphore.Wait(); _logger.Debug("Start processing message {messageId} ", message.Id); var request = (HttpWebRequest)WebRequest.Create(url); request.Method = message.Method.ToUpper(); request.Timeout = 1000 * config.TimeOut; var mediaType = !string.IsNullOrEmpty(channel.MediaType) ? channel.MediaType : "text/xml"; request.ContentType = $"{mediaType}; charset=utf-8"; byte[] data = Encoding.UTF8.GetBytes(message.Xml); request.ContentLength = data.Length; using (var streamWriter = request.GetRequestStream()) { streamWriter.Write(data, 0, data.Length); streamWriter.Flush(); } using (var httpResponse = (HttpWebResponse)request.GetResponse()) { timer.Stop(); _logger.Info() .Message( "Message {message} for channel {channel} has been sent on url {url}", message.Method, channel.Name, Uri.UnescapeDataString(url) ) .Property("productId", message.Key) .Property("statusCode", httpResponse.StatusCode) .Property("timeTaken", timer.ElapsedMilliseconds) .Property("messageId", message.Id) .Property("customerCode", customerCode) .Write(); channelService.UpdateNotificationChannel(customerCode, channel.Name, message.Key, message.Created, httpResponse.StatusCode.ToString()); } ; service.RemoveMessage(message.Id); } catch (WebException ex) { timer.Stop(); lock (state) { state.ErrorsCount++; } var httpResponse = ex.Response as HttpWebResponse; if (httpResponse != null) { _logger.Info() .Message( "Message {message} for channel {channel} has been sent on url {url}", message.Method, channel.Name, Uri.UnescapeDataString(url) ) .Property("productId", message.Key) .Property("statusCode", httpResponse.StatusCode) .Property("timeTaken", timer.ElapsedMilliseconds) .Property("messageId", message.Id) .Property("customerCode", customerCode) .Write(); channelService.UpdateNotificationChannel(customerCode, channel.Name, message.Key, message.Created, httpResponse.StatusCode.ToString()); } else { _logger.Info() .Message( "Message {message} for channel {channel} has not been sent on url {url}", message.Method, channel.Name, Uri.UnescapeDataString(url) ) .Property("productId", message.Key) .Property("statusCode", ex.Status) .Property("timeTaken", timer.ElapsedMilliseconds) .Property("messageId", message.Id) .Property("customerCode", customerCode) .Write(); channelService.UpdateNotificationChannel(customerCode, channel.Name, message.Key, message.Created, ex.Status.ToString()); } _logger.Error().Exception(ex) .Message( "Message {message} for channel {channel} has not been sent on url {url}", message.Method, channel.Name, Uri.UnescapeDataString(url) ) .Property("productId", message.Key) .Property("timeTaken", timer.ElapsedMilliseconds) .Property("messageId", message.Id) .Property("customerCode", customerCode) .Write(); } finally { semaphore.Release(); } }); }
private void UpdateConfiguration(string customerCode) { _identityProvider.Identity = new Identity(customerCode); var configProvider = ObjectFactoryBase.Resolve <INotificationProvider>(); try { string instanceId = _props.InstanceId; _logger.Info() .Message("start UpdateConfiguration for {customerCode}", customerCode) .Property("instanceId", instanceId) .Write(); int delay = 0; var items = _senders.Zip(_lockers.Keys, (s, k) => new { Sender = s, Key = k }); var config = ConfigDictionary.AddOrUpdate(customerCode, code => configProvider.GetConfiguration(), (code, cfg) => configProvider.GetConfiguration()); foreach (var channel in config.Channels.Where(c => c.DegreeOfParallelism > 0)) { var key = GetKey(channel.Name, customerCode); if (_lockers.ContainsKey(key)) { var sender = items.First(itm => itm.Key == key).Sender; sender.Change( new TimeSpan(0, 0, delay), new TimeSpan(0, 0, config.CheckInterval) ); _logger.Info( "Update sender for {key} whith delay {delay} and interval {interval}", key, delay, config.CheckInterval ); } else { var state = new ChannelState { BlockState = null, ErrorsCount = 0 }; var descriptor = new ChannelDescriptor { ChannelName = channel.Name, CustomerCode = customerCode, InstanceId = instanceId }; _lockers.Add(key, state); _senders.Add(new Timer((SendToOneChannel), descriptor, new TimeSpan(0, 0, delay), new TimeSpan(0, 0, config.CheckInterval))); _logger.Info( "Add sender for {key} whith delay {delay} and interval {interval}", key, delay, config.CheckInterval ); } delay++; } if (customerCode != SingleCustomerCoreProvider.Key) { var autopublishKey = GetKey(AutopublishKey, customerCode); if (_lockers.ContainsKey(autopublishKey)) { var sender = items.First(itm => itm.Key == autopublishKey).Sender; if (config.Autopublish) { sender.Change( new TimeSpan(0, 0, delay), new TimeSpan(0, 0, config.CheckInterval) ); _logger.Info( "Update autopublish for {key} whith delay {delay} and interval {interval}", autopublishKey, delay, config.CheckInterval ); } else { sender.Change( new TimeSpan(0, 0, 0, 0, -1), new TimeSpan(0, 0, config.CheckInterval) ); _logger.Info( "Stop autopublish for {key} whith delay {delay} and interval {interval}", autopublishKey, delay, config.CheckInterval ); } } else if (config.Autopublish) { var state = new ChannelState { BlockState = null, ErrorsCount = 0 }; _lockers.Add(autopublishKey, state); _senders.Add(new Timer( (Autopublish), customerCode, new TimeSpan(0, 0, delay), new TimeSpan(0, 0, config.CheckInterval) )); _logger.Info("Add autopublish for {key} whith delay {delay} and interval {interval}", autopublishKey, delay, config.CheckInterval); } delay++; var itemsToStop = items .Where(itm => itm.Key.StartsWith(GetKeyPrefix(customerCode)) && itm.Key != autopublishKey && !config.Channels.Any(c => GetKey(c.Name, customerCode) == itm.Key && c.DegreeOfParallelism > 0)); foreach (var item in itemsToStop) { item.Sender.Change( new TimeSpan(0, 0, 0, 0, -1), new TimeSpan(0, 0, config.CheckInterval) ); _logger.Info("Stop sender for {key} whith delay {delay}", item.Key, delay); } } } catch (Exception ex) { _logger.Error().Exception(ex) .Message("can not UpdateConfiguration for {customerCode}", customerCode) .Write(); } finally { _logger.Info("end UpdateConfiguration for {customerCode}", customerCode); } }