public async Task <Response <ServiceHostAndPort> > Lease(DownstreamContext context) { var value = context.HttpContext.Request.Cookies[_key]; if (!string.IsNullOrEmpty(value) && _stored.ContainsKey(value)) { var cached = _stored[value]; var updated = new StickySession(cached.HostAndPort, DateTime.UtcNow.AddMilliseconds(_expiryInMs)); _stored[value] = updated; return(new OkResponse <ServiceHostAndPort>(updated.HostAndPort)); } var next = await _loadBalancer.Lease(context); if (next.IsError) { return(new ErrorResponse <ServiceHostAndPort>(next.Errors)); } if (!string.IsNullOrEmpty(value) && !_stored.ContainsKey(value)) { _stored[value] = new StickySession(next.Data, DateTime.UtcNow.AddMilliseconds(_expiryInMs)); } return(new OkResponse <ServiceHostAndPort>(next.Data)); }
private void Enqueue(List <EventMessage> Events, CancellationToken cancellationToken) { var persistentConnection = _senderLoadBlancer.Lease(); try { var channel = persistentConnection.GetProducer(); var groups = Events.GroupBy(a => a.RouteKey).Select(a => a.Key); foreach (var topic in groups) { var messages = new List <Message <string, string> >(); var curEvents = Events.Where(a => a.RouteKey == topic).ToArray(); for (var eventIndex = 0; eventIndex < curEvents.Count(); eventIndex++) { using (var tracer = new Hummingbird.Extensions.Tracing.Tracer("AMQP Publish")) { tracer.SetComponent(_compomentName); tracer.SetTag("x-eventId", curEvents[eventIndex].EventId); tracer.SetTag("x-messageId", curEvents[eventIndex].MessageId); tracer.SetTag("x-traceId", curEvents[eventIndex].TraceId); _logger.LogInformation(curEvents[eventIndex].Body); var message = new Message <string, string>(); message.Key = curEvents[eventIndex].MessageId; message.Timestamp = curEvents[eventIndex].Timestamp; message.Value = curEvents[eventIndex].Body; message.Headers = new Headers(); foreach (var key in curEvents[eventIndex].Headers.Keys) { message.Headers.Add(new Header(key, UTF8Encoding.UTF8.GetBytes(curEvents[eventIndex].Headers[key] as string))); } messages.Add(message); } } channel.ProduceBatch(topic, messages, TimeSpan.FromMilliseconds(_senderConfirmTimeoutMillseconds), TimeSpan.FromMilliseconds(_senderConfirmFlushTimeoutMillseconds), cancellationToken); } } catch (Exception ex) { _logger.LogError(ex, ex.Message); throw ex; } }
public async Task <Response <ServiceHostAndPort> > Lease(DownstreamContext context) { var key = context.HttpContext.Request.Cookies[_key]; lock (_lock) { if (!string.IsNullOrEmpty(key) && _stored.ContainsKey(key)) { var cached = _stored[key]; var updated = new StickySession(cached.HostAndPort, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key); _stored[key] = updated; _bus.Publish(updated, _keyExpiryInMs); return(new OkResponse <ServiceHostAndPort>(updated.HostAndPort)); } } var next = await _loadBalancer.Lease(context); if (next.IsError) { return(new ErrorResponse <ServiceHostAndPort>(next.Errors)); } lock (_lock) { if (!string.IsNullOrEmpty(key) && !_stored.ContainsKey(key)) { var ss = new StickySession(next.Data, DateTime.UtcNow.AddMilliseconds(_keyExpiryInMs), key); _stored[key] = ss; _bus.Publish(ss, _keyExpiryInMs); } } return(new OkResponse <ServiceHostAndPort>(next.Data)); }
/// <summary> /// 订阅消息(同一类消息可以重复订阅) /// 作者:郭明 /// 日期:2017年4月3日 /// </summary> /// <typeparam name="TD"></typeparam> /// <typeparam name="TH"></typeparam> /// <param name="QueueName">消息类型名称</param> /// <param name="EventTypeName">消息类型名称</param> /// <returns></returns> public IEventBus Register <TD, TH>(string QueueName, string EventTypeName = "", CancellationToken cancellationToken = default(CancellationToken)) where TD : class where TH : IEventHandler <TD> { var persistentConnection = _receiveLoadBlancer.Lease(); var queueName = string.IsNullOrEmpty(QueueName) ? typeof(TH).FullName : QueueName; var routeKey = string.IsNullOrEmpty(EventTypeName) ? typeof(TD).FullName : EventTypeName; var eventAction = _lifetimeScope.GetService(typeof(TH)) as IEventHandler <TD>; if (eventAction == null) { eventAction = System.Activator.CreateInstance(typeof(TH)) as IEventHandler <TD>; } System.Threading.Tasks.Task.Run(async() => { IConsumer <string, string> consumer = null; try { consumer = persistentConnection.GetConsumer(); consumer.Subscribe(routeKey); while (!cancellationToken.IsCancellationRequested) { try { var ea = consumer.Consume(cancellationToken); // 消息队列空 if (ea != null && ea.IsPartitionEOF) { _logger.LogDebug("Reached end of topic {consumeResult.Topic}, partition {consumeResult.Partition}, offset {consumeResult.Offset}."); continue; } else { _logger.LogInformation($"Consumed message '{ea.Value}' at: '{ea.TopicPartitionOffset}'."); } var EventId = -1L; var MessageId = ea.Key; var TraceId = MessageId; using (var tracer = new Hummingbird.Extensions.Tracing.Tracer("AMQP Received", TraceId)) { #region 获取EventId 和 TracerId if (ea.Headers != null) { try { long.TryParse(System.Text.Encoding.UTF8.GetString(ea.Headers.GetLastBytes("x-eventId")), out EventId); } catch { } try { TraceId = System.Text.Encoding.UTF8.GetString(ea.Headers.GetLastBytes("x-traceId")); } catch { } } #endregion tracer.SetComponent(_compomentName); tracer.SetTag("queueName", queueName); tracer.SetTag("x-messageId", MessageId); tracer.SetTag("x-eventId", EventId); tracer.SetTag("x-traceId", TraceId); try { var eventResponse = new EventResponse() { EventId = EventId, MessageId = MessageId, TraceId = TraceId, Headers = new Dictionary <string, object>(), Body = default(TD), QueueName = queueName, RouteKey = routeKey, BodySource = ea.Value }; #region 格式化消息 try { #region 设置body eventResponse.Body = JsonConvert.DeserializeObject <TD>(eventResponse.BodySource); #endregion #region 设置header foreach (var key in ea.Headers) { eventResponse.Headers.Add(key.Key, Encoding.UTF8.GetString(key.GetValueBytes())); } if (!eventResponse.Headers.ContainsKey("x-topic")) { eventResponse.Headers.Add("x-topic", routeKey); } if (!eventResponse.Headers.ContainsKey("x-messageId")) { eventResponse.Headers.Add("x-messageId", MessageId); } if (!eventResponse.Headers.ContainsKey("x-eventId")) { eventResponse.Headers.Add("x-eventId", EventId); } if (!eventResponse.Headers.ContainsKey("x-traceId")) { eventResponse.Headers.Add("x-traceId", TraceId); } #endregion _logger.LogDebug(eventResponse.BodySource); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } #endregion #region 处理消息 using (var tracerExecuteAsync = new Hummingbird.Extensions.Tracing.Tracer("AMQP Execute")) { var handlerSuccess = false; var handlerException = default(Exception); tracerExecuteAsync.SetComponent(_compomentName); tracerExecuteAsync.SetTag("queueName", queueName); tracerExecuteAsync.SetTag("x-messageId", MessageId); tracerExecuteAsync.SetTag("x-eventId", EventId); tracerExecuteAsync.SetTag("x-traceId", TraceId); try { handlerSuccess = await _receiverPolicy.ExecuteAsync(async(handlerCancellationToken) => { return(await eventAction.Handle(eventResponse.Body, (Dictionary <string, object>)eventResponse.Headers, handlerCancellationToken)); }, CancellationToken.None); if (handlerSuccess) { if (_subscribeAckHandler != null) { _subscribeAckHandler(new EventResponse[] { eventResponse }); } consumer.StoreOffset(ea); _logger.LogInformation($"kafk offset store,topic={routeKey} partation={ea.TopicPartition.Partition}offset={ea.TopicPartitionOffset.Offset.Value}"); // consumer.Commit(ea); // Console.WriteLine($"kafk offset commit,topic={routeKey} partation={ea.TopicPartition.Partition}offset={ea.TopicPartitionOffset.Offset.Value}"); } else { tracerExecuteAsync.SetError(); } } catch (Exception ex) { tracerExecuteAsync.SetError(); handlerException = ex; _logger.LogError(ex, ex.Message); } finally { if (!handlerSuccess) { //重新入队,默认:是 var requeue = true; try { //执行回调,等待业务层的处理结果 if (_subscribeNackHandler != null) { requeue = await _subscribeNackHandler((new EventResponse[] { eventResponse }, handlerException)); } } catch (Exception innterEx) { _logger.LogError(innterEx, innterEx.Message); } if (!requeue) { consumer.StoreOffset(ea); _logger.LogInformation($"kafk offset store,topic={routeKey} partation={ea.TopicPartition.Partition}offset={ea.TopicPartitionOffset.Offset.Value}"); // consumer.Commit(ea); // Console.WriteLine($"kafk offset commit,topic={routeKey} partation={ea.TopicPartition.Partition}offset={ea.TopicPartitionOffset.Offset.Value}"); } else { consumer.Seek(ea.TopicPartitionOffset); //重新入队重试 _logger.LogInformation($"kafk offset seek,topic={routeKey} partation={ea.TopicPartition.Partition}offset={ea.TopicPartitionOffset.Offset.Value}"); } } } } #endregion } catch (Exception ex) { tracer.SetError(); _logger.LogError(ex.Message, ex); } } } catch (Exception ex) { _logger.LogError(ex, ex.Message); } } } catch (Exception ex) { _logger.LogError(ex, ex.Message); if (consumer != null) { consumer.Close(); } } }); return(this); }
/// <summary> /// 订阅消息(同一类消息可以重复订阅) /// 作者:郭明 /// 日期:2017年4月3日 /// </summary> /// <typeparam name="TD"></typeparam> /// <typeparam name="TH"></typeparam> /// <param name="QueueName">消息类型名称</param> /// <param name="EventTypeName">消息类型名称</param> /// <returns></returns> public IEventBus Register <TD, TH>(string QueueName, string EventTypeName = "", CancellationToken cancellationToken = default(CancellationToken)) where TD : class where TH : IEventHandler <TD> { var queueName = string.IsNullOrEmpty(QueueName) ? typeof(TH).FullName : QueueName; var routeKey = string.IsNullOrEmpty(EventTypeName) ? typeof(TD).FullName : EventTypeName; var eventAction = _lifetimeScope.GetService(typeof(TH)) as IEventHandler <TD>; if (eventAction == null) { eventAction = System.Activator.CreateInstance(typeof(TH)) as IEventHandler <TD>; } var persistentConnection = _receiverLoadBlancer.Lease(); if (!persistentConnection.IsConnected) { persistentConnection.TryConnect(); } for (int i = 0; i < _reveiverMaxDegreeOfParallelism; i++) { System.Threading.Tasks.Task.Run(() => { try { var _channel = persistentConnection.GetConsumer(); //direct fanout topic _channel.ExchangeDeclare(_exchange, _exchangeType, true, false, null); //在MQ上定义一个持久化队列,如果名称相同不会重复创建 _channel.QueueDeclare(queueName, true, false, false, null); //绑定交换器和队列 _channel.QueueBind(queueName, _exchange, routeKey); //绑定交换器和队列 _channel.QueueBind(queueName, _exchange, queueName); //输入1,那如果接收一个消息,但是没有应答,则客户端不会收到下一个消息 _channel.BasicQos(0, _preFetch, false); //在队列上定义一个消费者a EventingBasicConsumer consumer = new EventingBasicConsumer(_channel); consumer.Received += async(ch, ea) => { using (var tracer = new Hummingbird.Extensions.Tracing.Tracer("AMQP Received")) { try { #region Ensure IsConnected if (!persistentConnection.IsConnected) { persistentConnection.TryConnect(); } #endregion var EventId = -1L; var MessageId = string.IsNullOrEmpty(ea.BasicProperties.MessageId) ? Guid.NewGuid().ToString("N") : ea.BasicProperties.MessageId; var TraceId = MessageId; #region 获取 eventId if (ea.BasicProperties.Headers != null && ea.BasicProperties.Headers.ContainsKey("x-eventId")) { try { long.TryParse(System.Text.Encoding.UTF8.GetString(ea.BasicProperties.Headers["x-eventId"] as byte[]), out EventId); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } } #endregion #region 获取 eventId if (ea.BasicProperties.Headers != null && ea.BasicProperties.Headers.ContainsKey("x-traceId")) { try { TraceId = System.Text.Encoding.UTF8.GetString(ea.BasicProperties.Headers["x-traceId"] as byte[]); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } } #endregion tracer.SetComponent(_compomentName); tracer.SetTag("queueName", queueName); tracer.SetTag("x-messageId", MessageId); tracer.SetTag("x-traceId", TraceId); tracer.SetTag("x-eventId", EventId); var eventResponse = new EventResponse() { EventId = EventId, MessageId = MessageId, TraceId = TraceId, Headers = ea.BasicProperties.Headers ?? new Dictionary <string, object>(), QueueName = queueName, RouteKey = routeKey, BodySource = Encoding.UTF8.GetString(ea.Body), Body = default(TD), }; try { #region 设置body eventResponse.Body = JsonConvert.DeserializeObject <TD>(eventResponse.BodySource); #endregion #region 设置header if (!eventResponse.Headers.ContainsKey("x-exchange")) { eventResponse.Headers.Add("x-exchange", _exchange); } if (!eventResponse.Headers.ContainsKey("x-exchange-type")) { eventResponse.Headers.Add("x-exchange-type", _exchangeType); } #endregion _logger.LogInformation(eventResponse.BodySource); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } #region AMQP ExecuteAsync using (var tracerExecuteAsync = new Hummingbird.Extensions.Tracing.Tracer("AMQP Execute")) { var handlerSuccess = false; var handlerException = default(Exception); try { handlerSuccess = await _receiverPolicy.ExecuteAsync(async(handlerCancellationToken) => { return(await eventAction.Handle(eventResponse.Body, (Dictionary <string, object>)eventResponse.Headers, handlerCancellationToken)); }, cancellationToken); if (handlerSuccess) { if (_subscribeAckHandler != null) { _subscribeAckHandler(new EventResponse[] { eventResponse }); } //确认消息 _channel.BasicAck(ea.DeliveryTag, false); } else { tracerExecuteAsync.SetError(); } } catch (Exception ex) { _logger.LogError(ex, ex.Message); tracerExecuteAsync.SetError(); handlerException = ex; } finally { if (!handlerSuccess) { //重新入队,默认:是 var requeue = true; try { //执行回调,等待业务层的处理结果 if (_subscribeNackHandler != null) { requeue = await _subscribeNackHandler((new EventResponse[] { eventResponse }, handlerException)); } } catch (Exception innterEx) { _logger.LogError(innterEx, innterEx.Message); } //确认消息 _channel.BasicReject(ea.DeliveryTag, requeue); } } } #endregion } catch (Exception ex) { tracer.SetError(); _logger.LogError(ex.Message, ex); } } }; consumer.Unregistered += (ch, ea) => { _logger.LogDebug($"MQ:{queueName} Consumer_Unregistered"); }; consumer.Registered += (ch, ea) => { _logger.LogDebug($"MQ:{queueName} Consumer_Registered"); }; consumer.Shutdown += (ch, ea) => { _logger.LogDebug($"MQ:{queueName} Consumer_Shutdown.{ea.ReplyText}"); }; consumer.ConsumerCancelled += (object sender, ConsumerEventArgs e) => { _logger.LogDebug($"MQ:{queueName} ConsumerCancelled"); }; //消费队列,并设置应答模式为程序主动应答 _channel.BasicConsume(queueName, false, consumer); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } }); } return(this); }
private async Task <bool> Enqueue(List <EventMessage> Events, bool confirm, CancellationToken cancellationToken = default(CancellationToken)) { var persistentConnection = _senderLoadBlancer.Lease(); try { if (!persistentConnection.IsConnected) { persistentConnection.TryConnect(); } var channel = persistentConnection.GetProducer(); // 提交走批量通道 var batchPublish = channel.CreateBasicPublishBatch(); for (var eventIndex = 0; eventIndex < Events.Count; eventIndex++) { await _senderRetryPolicy.ExecuteAsync((ct) => { var MessageId = Events[eventIndex].MessageId; var json = Events[eventIndex].Body; var routeKey = Events[eventIndex].RouteKey; byte[] bytes = Encoding.UTF8.GetBytes(json); //设置消息持久化 IBasicProperties properties = channel.CreateBasicProperties(); properties.DeliveryMode = 2; properties.MessageId = MessageId; properties.Headers = new Dictionary <string, Object>(); properties.Headers["x-eventId"] = Events[eventIndex].EventId; properties.Headers["x-traceId"] = Events[eventIndex].TraceId; using (var tracer = new Hummingbird.Extensions.Tracing.Tracer("AMQP Publish")) { tracer.SetComponent(_compomentName); tracer.SetTag("x-messageId", MessageId); tracer.SetTag("x-eventId", Events[eventIndex].EventId); tracer.SetTag("x-traceId", Events[eventIndex].TraceId); _logger.LogInformation(json); foreach (var key in Events[eventIndex].Headers.Keys) { if (!properties.Headers.ContainsKey(key)) { properties.Headers.Add(key, Events[eventIndex].Headers[key]); } } if (Events[eventIndex].Headers.ContainsKey("x-first-death-queue")) { //延时队列或者直接写死信的情况 var newQueue = Events[eventIndex].Headers["x-first-death-queue"].ToString(); //创建一个队列 channel.QueueDeclare( queue: newQueue, durable: true, exclusive: false, autoDelete: false, arguments: Events[eventIndex].Headers); batchPublish.Add( exchange: "", mandatory: true, routingKey: newQueue, properties: properties, body: bytes); } else { //发送到正常队列 batchPublish.Add( exchange: _exchange, mandatory: true, routingKey: routeKey, properties: properties, body: bytes); } } return(Task.FromResult(true)); }, cancellationToken); } //批量提交 batchPublish.Publish(); if (confirm) { return(channel.WaitForConfirms(TimeSpan.FromMilliseconds(_senderConfirmTimeoutMillseconds))); } else { return(true); } } catch (Exception ex) { _logger.LogError(ex, ex.Message); throw ex; } }