Example #1
0
        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);
        }
Example #2
0
        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();
                }
            }
        }