//internal protected virtual async Task<TOutputMessage> PublishMessageAsync(string routing, TInputMessage message, LogTrackerContext tracker)
        //{
        //    return await this.PublishMessageAsync(
        //        this.IsWaitReturn,
        //        MessageBusConfig.DefaultWaitReplyTimeOut,
        //        MessageBusConfig.DefaultMessageExpirationTimeout,
        //        routing,
        //        message,
        //        MessageBusConfig.DefaultRetryCount,
        //        MessageBusConfig.DefaultRetryWaitTime,
        //        tracker);
        //}

        //internal protected virtual async Task<TOutputMessage> PublishMessageAsync(string routing, byte[] message, LogTrackerContext tracker)
        //{
        //    return await this.PublishMessageAsync(
        //        this.IsWaitReturn,
        //        MessageBusConfig.DefaultWaitReplyTimeOut,
        //        MessageBusConfig.DefaultMessageExpirationTimeout,
        //        routing,
        //        message,
        //        null,
        //        MessageBusConfig.DefaultRetryCount,
        //        MessageBusConfig.DefaultRetryWaitTime,
        //        tracker);
        //}


        internal protected virtual async Task <TOutputMessage> PublishMessageAsync(
            bool isWaitReply,
            TimeSpan waitReplyTimeout,
            TimeSpan messageExpirationTimeout,
            string routing,
            TInputMessage message,
            int retryCount,
            TimeSpan retryWaitTimeout,
            LogTrackerContext tracker)
        {
            Dictionary <string, object> headers = new Dictionary <string, object>();

            headers["INPUT-MESSAGE-TYPE"] = typeof(TInputMessage).FullName;
            if (this.IsWaitReturn)
            {
                headers["OUTPUT-MESSAGE-TYPE"] = typeof(TOutputMessage).FullName;
            }

            return(await this.PublishMessageAsync(
                       isWaitReply,
                       waitReplyTimeout,
                       messageExpirationTimeout,
                       routing,
                       Encoding.Unicode.GetBytes(JsonConvert.SerializeObject(message)),
                       headers,
                       retryCount,
                       retryWaitTimeout,
                       tracker));
        }
예제 #2
0
        protected override void ExecuteSubscriberProcess(BetTransactionMessage message, LogTrackerContext logtracker)
        {
            LogTrackerContext.Init(LogTrackerContextStorageTypeEnum.THREAD_DATASLOT, logtracker);
            _logger.Info(message.Id);

            //Console.WriteLine(message.Id);
            return;
        }
예제 #3
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);
        }
예제 #4
0
        protected override OutputMessageBase ExecuteSubscriberProcess(BetTransactionMessage message, LogTrackerContext logtracker)
        {
            LogTrackerContext.Init(LogTrackerContextStorageTypeEnum.THREAD_DATASLOT, logtracker);
            _logger.Info(message.Id);

            //throw new Exception("TEST");

            //Console.Write("start: {0}", message.Id);
            //Task.Delay(1000 * 10).Wait();
            //Console.Write("end: {0}", message.Id);

            return(new OutputMessageBase());
        }
        protected override void ExecuteSubscriberProcess(TimerMessage message, LogTrackerContext logtracker)
        {
            _logger.Info("Add Timer Task: {0}, {1}, {2}", message.ID, message.RouteKey, message.RunAt);

            lock (schedule)
            {
                this.schedule.Add(new MessagePack()
                {
                    _message = message,
                    _context = logtracker
                });
                this.schedule.Sort((t1, t2) => { return(DateTime.Compare(t1._message.RunAt, t2._message.RunAt)); });
            }
            this.outputWait.Set();
        }
예제 #6
0
        static void Main(string[] args)
        {
            LogTrackerLogger logger = new LogTrackerLogger(LogManager.GetCurrentClassLogger());
            Guid             guid1  = Guid.NewGuid();
            DateTime         dt1    = DateTime.UtcNow;

            Console.WriteLine(String.Format("Worker: request_id1: {0} | request_start_time_utc: {1}", guid1.ToString(), dt1));
            LogTrackerContext.Init(LogTrackerContextStorageTypeEnum.THREAD_DATASLOT, guid1.ToString(), dt1);
            MockZeus.Service.Mercury.TP.GBTP gb1 = new MockZeus.Service.Mercury.TP.GBTP();
            gb1.GetBalance();

            System.Threading.Thread.Sleep(1000);

            Guid     guid2 = Guid.NewGuid();
            DateTime dt2   = DateTime.UtcNow;

            Console.WriteLine(String.Format("Worker: request_id1: {0} | request_start_time_utc: {1}", guid2.ToString(), dt2));
            LogTrackerContext.Init(LogTrackerContextStorageTypeEnum.THREAD_DATASLOT, guid2.ToString(), dt2);
            MockZeus.Service.Mercury.TP.GBTP gb2 = new MockZeus.Service.Mercury.TP.GBTP();
            gb2.GetBalance();

            Console.ReadLine();
        }
예제 #7
0
        static void Main(string[] args)
        {
            //LogTrackerContext.Clean();

            //// Logger WITHOUT logConttext - To GrayLog
            //Console.WriteLine("Logger WITHOUT logConttext - To GrayLog");
            //LogTrackerLogger loggerWOCtxGL = new LogTrackerLogger(LogManager.GetLogger("ToGrayLog"));
            //loggerWOCtxGL.Info(new LogMessage() { Message = "This log has no context information from POC.Client" });
            //Console.ReadLine();

            //// Logger WITH explicit logConttext - To Gray Log
            //string request_id = Guid.NewGuid().ToString();
            //DateTime utcNow = DateTime.UtcNow;
            //LogTrackerContext lgc = LogTrackerContext.Init(LogTrackerContextStorageTypeEnum.NONE, request_id, utcNow);
            //Console.WriteLine("Logger WITH explicit logConttext - To Gray Log");
            //LogTrackerLogger loggerWTHCtxGL = new LogTrackerLogger(LogManager.GetLogger("ToGrayLog"), lgc);
            //loggerWTHCtxGL.Info(new LogMessage() { Message = "This log has context information from POC.Client" });
            //Console.ReadLine();
            //LogTrackerContext.Clean();


            //HttpClient client = new HttpClient();
            HttpClient client = new HttpClient(new LogTrackerHandler());

            client.BaseAddress = new Uri("http://localhost:31554/");
            Console.WriteLine(client.GetAsync("/api/values").Result);
            Console.WriteLine(client.GetAsync("/api/values/123").Result);

            // 測試Init錯誤參數
            LogTrackerContext wrong_request_id = LogTrackerContext.Init(LogTrackerContextStorageTypeEnum.NONE, "", DateTime.UtcNow);
            LogTrackerContext wrong_start_time = LogTrackerContext.Init(LogTrackerContextStorageTypeEnum.NONE, Guid.NewGuid().ToString(), DateTime.MinValue);
            // 以下測試需要將LogTrackerHandler中的request_id或start_time如上改成錯誤參數進行測試
            //HttpClient wrong_http_context = new HttpClient(new LogTrackerHandler());
            //client.BaseAddress = new Uri("http://localhost:31554/");
            //Console.WriteLine(wrong_http_context.GetAsync("/api/values").Result);
            //Console.WriteLine(wrong_http_context.GetAsync("/api/values/123").Result);
        }
예제 #8
0
        public void Test_ThreadDataSlotStorage_MultiThreads()
        {
            List <Task> tasks = new List <Task>();

            for (int i = 0; i < 10; i++)
            {
                tasks.Add(Task.Run(() =>
                {
                    //LogTrackerContext context = LogTrackerContext.Create("UNITTEST", LogTrackerContextStorageTypeEnum.THREAD_DATASLOT);
                    for (int y = 0; y < 10; y++)
                    {
                        string current_request_id     = Guid.NewGuid().ToString();
                        DateTime current_request_time = DateTime.UtcNow;

                        LogTrackerContext context = LogTrackerContext.Init(
                            LogTrackerContextStorageTypeEnum.THREAD_DATASLOT,
                            current_request_id,
                            current_request_time);

                        this.TestContext.WriteLine("TID: {0}, Request-ID: {1}, Request-Time: {2}", Thread.CurrentThread.ManagedThreadId, context.RequestId, context.RequestStartTimeUTC);

                        for (int x = 0; x < 10; x++)
                        {
                            Task.Delay(100).Wait();
                            Assert.AreEqual(
                                current_request_id,
                                LogTrackerContext.Current.RequestId);
                            Assert.AreEqual(
                                current_request_time,
                                LogTrackerContext.Current.RequestStartTimeUTC);
                        }
                    }
                }));
            }

            Task.WaitAll(tasks.ToArray());
        }
예제 #9
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");
        }
예제 #10
0
 protected override ResponseMessage ExecuteSubscriberProcess(RequestMessage message, LogTrackerContext logtracker)
 {
     Interlocked.Increment(ref count);
     return(new ResponseMessage()
     {
         result_json = message.input_json
     });
 }
        /// <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);
        }
예제 #12
0
 protected abstract TOutputMessage ExecuteSubscriberProcess(TInputMessage message, LogTrackerContext logtracker);
예제 #13
0
        //private bool _stop_forwarder = false;
        //public void StopForwarder()
        //{
        //    _stop_forwarder = true;
        //    this.outputWait.Set();
        //}

        private void ForwardMessage()
        {
            //_stop_forwarder = false;
            //while (_stop_forwarder == false)
            while (this.Status != WorkerStatusEnum.STOPPED)
            {
                while (this.schedule.Count > 0)
                {
                    MessagePack       msgpack = this.schedule[0];
                    TimerMessage      msg     = msgpack._message;
                    LogTrackerContext ctx     = msgpack._context;

                    msg.RunAt = msg.RunAt.ToUniversalTime();

                    MessageClientBase <InputMessageBase> mcb = null;

                    if (string.IsNullOrEmpty(msg.QueueName))
                    {
                        mcb = new MessageClientBase <InputMessageBase>(msg.ExchangeName, msg.ExchangeType);
                    }
                    else
                    {
                        mcb = new MessageClientBase <InputMessageBase>(msg.QueueName);
                    }


                    if (msg.RunAt <= DateTime.UtcNow)
                    {
                        // do expired
                        //Console.WriteLine("Run Expired Task: {0}, {1}, {2}", msg.ID, msg.RouteKey, msg.RunAt);
                        _logger.Info("Run Expired Task: {0}, {1}, {2}", msg.ID, msg.RouteKey, msg.RunAt);

                        lock (schedule) schedule.Remove(msgpack);
                        mcb.PublishMessageAsync(
                            mcb.IsWaitReturn,
                            MessageBusConfig.DefaultWaitReplyTimeOut,
                            MessageBusConfig.DefaultMessageExpirationTimeout,
                            msg.RouteKey,
                            Encoding.Unicode.GetBytes(msg.MessageText),
                            new Dictionary <string, object>()
                        {
                            { "INPUT-MESSAGE-TYPE", msg.MessageType }
                        },
                            MessageBusConfig.DefaultRetryCount,
                            MessageBusConfig.DefaultRetryWaitTime,
                            ctx).Wait();
                    }
                    else if (outputWait.WaitOne(msg.RunAt - DateTime.UtcNow) == false)
                    {
                        // do now
                        // Console.WriteLine("Run OnTime Task: {0}, {1}, {2}", msg.ID, msg.RouteKey, msg.RunAt);
                        _logger.Info("Run OnTime Task: {0}, {1}, {2}", msg.ID, msg.RouteKey, msg.RunAt);

                        lock (schedule) schedule.Remove(msgpack);
                        //stp.PublishMessage(msg.RouteKey, msg);
                        mcb.PublishMessageAsync(
                            mcb.IsWaitReturn,
                            MessageBusConfig.DefaultWaitReplyTimeOut,
                            MessageBusConfig.DefaultMessageExpirationTimeout,
                            msg.RouteKey,
                            Encoding.Unicode.GetBytes(msg.MessageText),
                            null,
                            MessageBusConfig.DefaultRetryCount,
                            MessageBusConfig.DefaultRetryWaitTime,
                            ctx).Wait();
                    }
                    //else
                    //{
                    //    Console.WriteLine("Next Job:");
                    //    lock (schedule) foreach (MessagePack m in schedule)
                    //    {
                    //        Console.WriteLine("- [{0}][{1}], {2}", m._message.ID, m._message.RouteKey, m._message.RunAt);
                    //    }
                    //}

                    mcb.Dispose();

                    if (this.Status == WorkerStatusEnum.STOPPED)
                    {
                        // ToDo: do shutdown work
                        return;
                    }
                }

                this.outputWait.WaitOne();
            }
        }
예제 #14
0
        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();
                }
            }
        }