internal virtual void PrintMergeMessageLog(MergedWarpMessage mergeMessage)
 {
     if (Logger().IsEnabled(LogLevel.Debug))
     {
         Logger().LogDebug($"merge msg size:{mergeMessage.msgIds.Count}");
         foreach (AbstractMessage cm in mergeMessage.msgs)
         {
             Logger().LogDebug(cm.ToString());
         }
         var sb = new StringBuilder();
         foreach (long l in mergeMessage.msgIds)
         {
             sb.Append(MSG_ID_PREFIX).Append(l).Append(SINGLE_LOG_POSTFIX);
         }
         sb.Append('\n');
         foreach (long l in outerInstance._futures.Keys)
         {
             sb.Append(FUTURES_PREFIX).Append(l).Append(SINGLE_LOG_POSTFIX);
         }
         Logger().LogDebug(sb.ToString());
     }
 }
            public async Task Run()
            {
                while (true)
                {
                    lock (outerInstance.mergeLock)
                    {
                        try
                        {
                            Monitor.Wait(outerInstance.mergeLock, TimeSpan.FromMilliseconds(MAX_MERGE_SEND_MILLS));
                        }
                        catch (Exception)
                        {
                        }
                    }
                    outerInstance.isSending = true;

                    // send batch message is sync request, but there is no need to get the return value.
                    // Since the messageFuture has been created before the message is placed in basketMap,
                    // the return value will be obtained in ClientOnResponseProcessor.
                    // fast fail
                    foreach (var item in outerInstance.basketMap)
                    {
                        var address = item.Key;

                        if (item.Value.Count <= 0)
                        {
                            return;
                        }
                        var mergeMessage = new MergedWarpMessage();
                        while (item.Value.Count > 0)
                        {
                            RpcMessage msg = item.Value.Take();
                            mergeMessage.msgs.Add((AbstractMessage)msg.Body);
                            mergeMessage.msgIds.Add(msg.Id);
                        }
                        if (mergeMessage.msgIds.Count > 1)
                        {
                            PrintMergeMessageLog(mergeMessage);
                        }
                        IChannel sendChannel = null;
                        try
                        {
                            sendChannel = await outerInstance.clientChannelManager.AcquireChannel(address);

                            await outerInstance.SendAsyncRequest(sendChannel, mergeMessage);
                        }
                        catch (FrameworkException e)
                        {
                            if (e.Errcode == FrameworkErrorCode.ChannelIsNotWritable && sendChannel != null)
                            {
                                await outerInstance.DestroyChannel(address, sendChannel);
                            }
                            foreach (int msgId in mergeMessage.msgIds)
                            {
                                if (outerInstance._futures.TryRemove(msgId, out MessageFuture messageFuture) && messageFuture != null)
                                {
                                    messageFuture.ResultMessage = null;
                                }
                            }
                            Logger().LogError(e, $"client merge call failed: {e.Message}");
                        }
                    }

                    outerInstance.isSending = false;
                }
            }