private void PrefetchRequestCallback(BrokerQueueItem queueItem, object state) { try { #if DEBUG BrokerTracing.TraceVerbose("[BrokerQueueDispatcher] .PrefetchRequestCallback: received one request. request id={0}", GetMessageId(queueItem)); #endif bool isCached = false; bool shouldDisposeQueueItem = false; Guid persistId = Guid.Empty; object asyncToken = null; int dispatcherNumber = 0; if (queueItem != null) { dispatcherNumber = queueItem.DispatchNumber; persistId = queueItem.PersistId; asyncToken = queueItem.PersistAsyncToken.AsyncToken; if (this.requestsAsyncTokenTable.Count > 0 && dispatcherNumber == 0) { // if the request is already in the cache or dispatched. lock (this.requestsAsyncTokenTable) { if (this.requestsAsyncTokenTable.ContainsKey(persistId)) { try { DispatcherAsyncTokenItem asyncTokenItem = this.requestsAsyncTokenTable[persistId]; if (asyncTokenItem.AsyncToken == null) { asyncTokenItem.AsyncToken = asyncToken; } // the duplicated request item is of no use after retrieving out its async token. so we should dispose it if possible shouldDisposeQueueItem = true; } catch (Exception e) { // the request in the table is removed by another thread. BrokerTracing.TraceInfo("[BrokerQueueDispatcher] .PrefetchRequestCallback: The request async token is removed from the table, Exception: {0}", e); } isCached = true; } } } if (!isCached) { bool needQueue = true; // if there are pending requests callback, then dispatch the request through the pending requests callback. if (this.requestCallbackQueue.Count > 0) { try { BrokerQueueCallbackItem requestCallback; if (this.requestCallbackQueue.TryDequeue(out requestCallback)) { this.RegisterReemit(queueItem); requestCallback.Callback(queueItem, requestCallback.CallbackState); needQueue = false; #if DEBUG BrokerTracing.TraceVerbose("[BrokerQueueDispatcher] .PrefetchRequestCallback: Invoked callback for request."); #endif } else { BrokerTracing.TraceInfo("[BrokerQueueDispatcher] .PrefetchRequestCallback: The requests callback queue is empty."); } } catch (InvalidOperationException e) { // the request callback queue is drained by other threads. BrokerTracing.TraceInfo("[BrokerQueueDispatcher] .PrefetchRequestCallback: The requests callback queue is empty, Exception: {0}", e); } catch (Exception e) { // request callback raise exception. BrokerTracing.TraceWarning("[BrokerQueueDispatcher] .PrefetchRequestCallback: The requests callback raise exception, Exception: {0}", e); } } if (needQueue) { // if no pending requests callback, then append the request to the cache queue. this.requestCacheQueue.Enqueue(queueItem); if (this.requestCacheQueue.Count >= this.GetCacheContainerSize()) { this.needPrefetch = false; } else { this.needPrefetch = true; } } } } BrokerQueue brokerQueue = (BrokerQueue)state; // try to get the next request. if (this.needPrefetch) { // hop to another thread to avoid recursive stack overflow for memory queue that will call the callback at the same thread. ThreadPool.QueueUserWorkItem(this.GetRequestThreadProc, state); } else { lock (this.waitBrokerQueuesForPeekRequests) { this.waitBrokerQueuesForPeekRequests.Add(brokerQueue); } if (this.requestCacheQueue.Count < this.sharedData.DispatcherCount + this.watermarkerForTriggerPrefetch) { this.TriggerPrefetch(); } } // drain the pending requests callback. this.HandlePendingRequestCallback(); // check if the response already come back. if (!this.HandleResponseWithoutAsyncToken(brokerQueue, queueItem) && shouldDisposeQueueItem) { // if its response is not back, and we've decided that the request queue item is of no use, dispose it. queueItem.Dispose(); } } catch (Exception e) { BrokerTracing.TraceInfo("[BrokerQueueDispatcher] .PrefetchRequestCallback: raised unknown exception: {0}", e); } }
public void PutResponseAsync(Message responseMsg, BrokerQueueItem requestItem, bool ignoreAsyncToken = false) { try { // Null means the request has been put back. if (responseMsg != null && this.sharedData.Config.LoadBalancing.MessageResendLimit > 0) { if (!requestItem.ReemitToken.Finish()) { TraceUtils.TraceWarning( "BrokerQueueDispatcher", "PutResponseAsync", "Drop the response {0} since no multi emission callback registered", Utility.GetMessageIdFromMessage(requestItem.Message)); return; } } BrokerQueueAsyncToken asyncToken = requestItem.PersistAsyncToken; if (asyncToken.AsyncToken != null || ignoreAsyncToken || responseMsg == null) { asyncToken.Queue.PutResponseAsync(responseMsg, requestItem); return; } DispatcherAsyncTokenItem asyncTokenItem = null; lock (this.requestsAsyncTokenTable) { this.requestsAsyncTokenTable.TryGetValue(asyncToken.PersistId, out asyncTokenItem); } if (asyncTokenItem != null) { bool hasGetAsyncToken = false; try { if (asyncTokenItem != null) { if (asyncTokenItem.AsyncToken != null) { asyncToken.AsyncToken = asyncTokenItem.AsyncToken; hasGetAsyncToken = true; lock (this.requestsAsyncTokenTable) { this.requestsAsyncTokenTable.Remove(asyncToken.PersistId); } } } } catch (Exception e) { if (hasGetAsyncToken) { BrokerTracing.TraceWarning("[BrokerQueueDispatcher] .PutResponseAsync: remove the async token from the table raised the exception, {0}", e); } else { // maybe there are duplicate responses, and the request in the async token table is removed by the previous request. BrokerTracing.TraceWarning("[BrokerQueueDispatcher] .PutResponseAsync: try to get the async token from the table raised the exception, {0}", e); } } if (hasGetAsyncToken) { asyncToken.Queue.PutResponseAsync(responseMsg, requestItem); } else { BrokerTracing.TraceInfo("[BrokerQueueDispatcher] .PutResponseAsync: Failed to get async token."); lock (this.responsesWithoutAsyncTokenTable) { this.responsesWithoutAsyncTokenTable.Add(asyncToken.PersistId, new DispatcherAsyncTokenItem(responseMsg, asyncToken)); } if (asyncTokenItem.AsyncToken != null) { bool needPutToTheQueue = false; try { lock (this.responsesWithoutAsyncTokenTable) { this.responsesWithoutAsyncTokenTable.Remove(asyncToken.PersistId); } needPutToTheQueue = true; lock (this.requestsAsyncTokenTable) { this.requestsAsyncTokenTable.Remove(asyncToken.PersistId); } } catch (Exception e) { if (needPutToTheQueue) { BrokerTracing.TraceInfo("[BrokerQueueDispatcher] .PutResponseAsync: remove the request async token from the table failed, the exception: {0}", e); } else { // in case the reponse message is persisted by another thread. BrokerTracing.TraceInfo("[BrokerQueueDispatcher] .PutResponseAsync: remove the response from the responses without async table fail, the exception: {0}", e); } } if (needPutToTheQueue) { asyncToken.AsyncToken = asyncTokenItem.AsyncToken; asyncToken.Queue.PutResponseAsync(responseMsg, requestItem); } else { BrokerTracing.TraceWarning( "[BrokerQueueDispatcher] .PutResponseAsync: Don't put response back because needPutToTheQueue=false. AsyncToken.PersistId={0}, requestItem id: {1}, response id:{2}", asyncToken.PersistId, requestItem?.Message?.Headers?.MessageId, responseMsg?.Headers?.MessageId); } } else { // the request item is processed and its response is returned, but its async token is unknown yet. At this point, as corresponding DispatcherAsyncToken item is // already put into responsesWithoutAsyncTokenTable, the request item itself is of no use now. so dispose it. BrokerTracing.TraceInfo( "[BrokerQueueDispatcher] .PutResponseAsync: Dispose requestItem id: {0}, response id:{1}", requestItem?.Message?.Headers?.MessageId, responseMsg?.Headers?.MessageId); requestItem.Dispose(); } } } else { BrokerTracing.TraceError("[BrokerQueueDispatcher] .PutResponseAsync: can not find the async token."); } } catch (Exception e) { BrokerTracing.TraceError("[BrokerQueueDispatcher] .PutResponseAsync: unkown exception, {0}", e); } }