public void Test_BasicThreadDataSlotStorage() { var context = LogTrackerContext.Create("UNITTEST", LogTrackerContextStorageTypeEnum.THREAD_DATASLOT); Assert.AreEqual( context.RequestId, LogTrackerContext.Current.RequestId); Assert.AreEqual( context.RequestStartTimeUTC, LogTrackerContext.Current.RequestStartTimeUTC); }
private void StartProcessSubscribedMessage(SubscriberProcess process) { //this._stop = false; //Stopwatch totalWatch = new Stopwatch(); //totalWatch.Start(); _logger.Trace("WorkerThread({0}) - start running...", Thread.CurrentThread.ManagedThreadId); //using (var connection = MessageBusConfig.DefaultConnectionFactory.CreateConnection()) //this.connectionFactory.CreateConnection()) using (var channel = this._connection.CreateModel()) { channel.QueueDeclare( queue: this.QueueName, durable: true, exclusive: false, autoDelete: false, arguments: null); BasicGetResult result = null; while (this._stop == false) { try { result = channel.BasicGet(this.QueueName, false); if (result == null) { Task.Delay(MessageBusConfig.DefaultPullWaitTime).Wait(); continue; } _logger.Trace("WorkerThread({0}) - receive message: {1}", Thread.CurrentThread.ManagedThreadId, result.BasicProperties.MessageId); } catch (Exception ex) { if (!_connection.IsOpen || channel.IsClosed) { _logger.Trace("WorkerThread({0}) message channel is closed. Reason: {1}", Thread.CurrentThread.ManagedThreadId, _connection.IsOpen ? channel.CloseReason : _connection.CloseReason); } this._stop = true; this._is_restart = true; _logger.Warn(ex, "dequeue exception, restart subscriber..."); result = null; break; } TOutputMessage response = null; var body = result.Body; var props = result.BasicProperties; bool current_reply = (string.IsNullOrEmpty(props.ReplyTo) == false); var replyProps = channel.CreateBasicProperties(); replyProps.CorrelationId = props.CorrelationId; LogTrackerContext logtracker = null; if (props.Headers == null || props.Headers[LogTrackerContext._KEY_REQUEST_ID] == null) { // message without logtracker context info logtracker = LogTrackerContext.Create(this.GetType().FullName, LogTrackerContextStorageTypeEnum.NONE); } else { string req_id = Encoding.UTF8.GetString((byte[])props.Headers[LogTrackerContext._KEY_REQUEST_ID]); DateTime req_time = DateTime.Parse(Encoding.UTF8.GetString((byte[])props.Headers[LogTrackerContext._KEY_REQUEST_START_UTCTIME])).ToUniversalTime(); logtracker = LogTrackerContext.Init( LogTrackerContextStorageTypeEnum.NONE, req_id, req_time); } try { TInputMessage request = JsonConvert.DeserializeObject <TInputMessage>(Encoding.Unicode.GetString(body)); _logger.Trace("WorkerThread({0}) - before processing message: {1}", Thread.CurrentThread.ManagedThreadId, props.MessageId); response = process(request, logtracker); _logger.Trace("WorkerThread({0}) - message was processed: {1}", Thread.CurrentThread.ManagedThreadId, props.MessageId); } catch (Exception e) { response = new TOutputMessage() { exception = e.ToString() }; _logger.Warn(e, "WorkerThread({0}) - process message with exception: {1}, ex: {2}", Thread.CurrentThread.ManagedThreadId, props.MessageId, e); } try { if (current_reply) { byte[] responseBytes = null;//Encoding.UTF8.GetBytes(response); if (response != null) { responseBytes = Encoding.Unicode.GetBytes(JsonConvert.SerializeObject(response)); } channel.BasicPublish( exchange: "", routingKey: props.ReplyTo, basicProperties: replyProps, body: responseBytes); } channel.BasicAck( deliveryTag: result.DeliveryTag, multiple: false); } catch (Exception ex) { // connection fail while return message or ack. // just shutdown and ignore it. non-return ack will be ignore, message will retry in next time if (!_connection.IsOpen || channel.IsClosed) { _logger.Trace("WorkerThread({0}) message channel is closed. Reason: {1}", Thread.CurrentThread.ManagedThreadId, _connection.IsOpen ? channel.CloseReason : _connection.CloseReason); } this._stop = true; this._is_restart = true; _logger.Warn(ex, "dequeue exception, restart subscriber..."); result = null; break; } } } _logger.Info("WorkerThread({0}) - {1} (restarting: {2}).", Thread.CurrentThread.ManagedThreadId, this.Status, this._is_restart); //this._stop?"stopped":"restarting"); }
/// <summary> /// NOTE: /// 所有的 PublishMessageAsync( ) 系列 overloading method 裡,這個 method 是真正在處理訊息發送的部分。 /// 其餘的 overloading 目的只是做預設參數的處理 /// </summary> /// <param name="isWaitReply"></param> /// <param name="waitReplyTimeout"></param> /// <param name="messageExpirationTimeout"></param> /// <param name="routing"></param> /// <param name="messageBody"></param> /// <param name="messageHeaders"></param> /// <param name="retryCount"></param> /// <param name="retryWaitTimeout"></param> /// <param name="tracker"></param> /// <returns></returns> internal protected virtual async Task <TOutputMessage> PublishMessageAsync( bool isWaitReply, TimeSpan waitReplyTimeout, TimeSpan messageExpirationTimeout, string routing, //TInputMessage message, byte[] messageBody, Dictionary <string, object> messageHeaders, int retryCount, TimeSpan retryWaitTimeout, LogTrackerContext tracker) { bool isPublishComplete = false; ConnectionFactory connFac = MessageBusConfig.DefaultConnectionFactory; while (true) { if (retryCount <= 0) { // 已經超出最大的重新連線嘗試次數。 throw new Exception("RetryLimitException"); } try { using (var connection = MessageBusConfig.DefaultConnectionFactory.CreateConnection(connFac.HostName.Split(','), this.ConnectionName)) using (var channel = connection.CreateModel()) { string replyQueueName = null; if (this.BusType == MessageBusTypeEnum.QUEUE) { channel.QueueDeclare( //queue: routing, queue: this.QueueName, durable: true, exclusive: false, autoDelete: false, arguments: null); } else if (this.BusType == MessageBusTypeEnum.EXCHANGE) { channel.ExchangeDeclare( this.ExchangeName, this.ExchangeType, true, false, null); } else { throw new InvalidOperationException(); } if (isWaitReply) { replyQueueName = channel.QueueDeclare().QueueName; //consumer = new QueueingBasicConsumer(channel); //channel.BasicConsume( // queue: replyQueueName, // noAck: true, // consumer: consumer); } IBasicProperties props = channel.CreateBasicProperties(); props.ContentType = "application/json"; props.Expiration = messageExpirationTimeout.TotalMilliseconds.ToString(); if (tracker == null) { tracker = LogTrackerContext.Create( this.GetType().Name, LogTrackerContextStorageTypeEnum.NONE); } props.Headers = (messageHeaders == null)? new Dictionary <string, object>() : new Dictionary <string, object>(messageHeaders); props.Headers[LogTrackerContext._KEY_REQUEST_ID] = tracker.RequestId; props.Headers[LogTrackerContext._KEY_REQUEST_START_UTCTIME] = tracker.RequestStartTimeUTC_Text; if (isWaitReply) { //message.correlationId = props.CorrelationId = Guid.NewGuid().ToString("N"); props.ReplyTo = replyQueueName; } try { if (this.BusType == MessageBusTypeEnum.EXCHANGE) { channel.BasicPublish( exchange: this.ExchangeName ?? "", routingKey: routing, basicProperties: props, //body: Encoding.Unicode.GetBytes(JsonConvert.SerializeObject(message))); body: messageBody); } else if (this.BusType == MessageBusTypeEnum.QUEUE) { channel.BasicPublish( exchange: "", routingKey: this.QueueName, basicProperties: props, //body: Encoding.Unicode.GetBytes(JsonConvert.SerializeObject(message))); body: messageBody); } isPublishComplete = true; if (isWaitReply) { BasicGetResult result = null; DateTime until = DateTime.Now.Add(waitReplyTimeout); while (result == null && DateTime.Now < until) { result = channel.BasicGet(replyQueueName, true); if (result == null) { Task.Delay(MessageBusConfig.DefaultPullWaitTime).Wait(); } } if (result != null) { // done, receive response message TOutputMessage response = JsonConvert.DeserializeObject <TOutputMessage>(Encoding.Unicode.GetString(result.Body)); return(response); } else { // timeout, do not wait anymore throw new TimeoutException(string.Format( "MessageBus 沒有在訊息指定的時間內 ({0}) 收到 ResponseMessage 回覆。", waitReplyTimeout)); } } break; } finally { if (isWaitReply && string.IsNullOrEmpty(replyQueueName) == false) { channel.QueueDelete(replyQueueName); } } } } catch (Exception ex) { // 如果訊息已經成功送出,只是在等待 response 時發生連線錯誤,則不會進行 Retry, 會原封不動地把 exception 丟回給呼叫端。 // 由呼叫端決定該如何進行下一個步驟。 if (isPublishComplete) { throw; } // connection fail. _logger.Warn(ex, "Retry (left: {0}) ...", retryCount); //Console.WriteLine("Retry.."); retryCount--; await Task.Delay(retryWaitTimeout); continue; } finally { } } return(null); }
static void Main(string[] args) { string mode = (args.Length == 0) ? "ASYNC" : args[0].ToUpper(); Console.WriteLine("Start in {0} mode.", mode); BetMessageClient bmc = null; BetRpcClient brc = null; BetTimerClient btc = null; LogTrackerContext.Create("POC/BetTransactions.Client", LogTrackerContextStorageTypeEnum.THREAD_DATASLOT); _logger.Info("Init..."); switch (mode) { case "ASYNC": bmc = new BetMessageClient(); break; case "SYNC": brc = new BetRpcClient(); break; case "TIMER": btc = new BetTimerClient(); break; } Stopwatch timer = new Stopwatch(); timer.Start(); for (int i = 1; i <= 1000000; i++) { BetTransactionMessage btm = new BetTransactionMessage() { Id = string.Format("{0}-{1}", i, Guid.NewGuid()) }; switch (mode) { case "ASYNC": bmc.PublishAsync("letou", btm).Wait(); break; case "SYNC": // async sample //try //{ // OutputMessageBase omb = brc.CallAsync("letou", btm).Result; //} //catch(AggregateException ae) //{ // foreach(Exception ex in ae.InnerExceptions) // if (ex is RpcServerException) Console.WriteLine("RpcServerException.Source: {0}", (ex as RpcServerException).Source); //} // sync sample try { OutputMessageBase omb = brc.Call("letou", btm); } catch (RpcServerException rse) { Console.WriteLine("\r\nRpcServerException: {0}", rse.Source); } break; case "TIMER": btc.PublishAsync("letou", btm, TimeSpan.FromSeconds(50)).Wait(); break; } if (args.Length > 1 && args[1] == "delay") { Task.Delay(300).Wait(); } Console.Write('.'); if (i % 100 == 0) { Console.WriteLine(); Console.WriteLine("Message Published: {0}, {1:0.00} msg/sec", i, 100 * 1000 / timer.ElapsedMilliseconds); timer.Restart(); } } }