MQQueue.Get()
операция останавливается на 5 минут при получении сообщения из QueueManager с использованием SSL на .NetCore 3.1. Один и тот же код работает нормально при работе в. Net Framework (v4.8) или без SSL в обеих средах выполнения.
Подробности:
- Клиент: Управляемый клиент MQ от NuGet (IBMMQDotnetClient / v9.1.4)
- Сервер: docker из тега ibmcom / mq: 9.1.4.0-r1 с базисной c конфигурацией SSL с использованием
SSLCIPH(ANY_TLS12) SSLCAUTH(OPTIONAL)
- Воспроизведено: . Net Ядро 3.1.2 на Windows и. Net Ядро 3.1.1 на Linux
- MQ Logs: MqGet-framework48.zip , MqGet-core31.zip
Мои выводы:
- Вложенный
MqGet-core31
сообщает: - Клиент отправляет запрос на получение сообщения в 09: 15: 52.199588
- Он получает ответ с телом сообщения почти сразу в 09: 15: 52.311050
- Затем он вызывает другую
MQRcvThread.ReceiveOneTSH()
для завершения sh операции get. Он вызывает MQTCPConnection.Receive()
и останавливается на 5 минут до 09: 20: 52.412669
- Возможная причина:
MQTCPConnection
сохраняет оригинал * Экземпляр 1060 * и MQEncryptedSocket
обертывают тот же socket
через SslStream
- Метод
MQTCPConnection.Receive()
опрашивает socket
, а затем вызывает MQEncryptedSocket.Read()
- Похоже что данные из
socket
считываются SslStream
в MQEncryptedSocket
до вызова метода poll()
и poll()
ожидает своего 5-минутного тайм-аута. Или любое другое состояние гонки? - Если я поставлю точку останова перед проблемным c
poll()
вызовом и задержу его на мгновение, то оно часто проходит без задержки!
Тестовый код:
Этот код заканчивается через ~ 2 секунды. Net Framework, но для его запуска требуется 5 минут и ~ 2 секунды. . Net Core. (Win или Linux)
// import server's CA
MQConnectionPool.ImportServerCertificateAuthorityIfNotPresent(CertMqTestCa);
Hashtable properties = new Hashtable {
{MQC.HOST_NAME_PROPERTY, Mq1QmSslAnyTls12.Host},
{MQC.PORT_PROPERTY, Mq1QmSslAnyTls12.Port},
{MQC.CHANNEL_PROPERTY, Mq1QmSslAnyTls12.ChannelName},
{MQC.USER_ID_PROPERTY, "admin"},
{MQC.PASSWORD_PROPERTY, "changeit"},
{MQC.CCSID_PROPERTY, 819},
{MQC.SSL_CIPHER_SPEC_PROPERTY, "TLS_RSA_WITH_AES_256_CBC_SHA256"},
{MQC.SSL_CERT_STORE_PROPERTY, "*USER"}
};
MQQueueManager queueManager;
using (new MQCcsidSetter(properties)) {
queueManager = new MQQueueManager(null, properties);
}
string requestText = Guid.NewGuid().ToString();
string queueName = "SV.MqConnectionPoolTest";
// put message - works fine
using (MQQueue queue = queueManager.AccessQueue(queueName, MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING)) {
byte[] requestBytes = Encoding.UTF8.GetBytes(requestText);
MQMessage requestMessage = new MQMessage {Expiry = 3000, Priority = 4, CharacterSet = 1208, MessageType = MQC.MQMT_DATAGRAM};
requestMessage.Write(requestBytes, 0, requestBytes.GetLength(0));
queue.Put(requestMessage, new MQPutMessageOptions());
}
// get message back from the same queue
using (MQQueue queue = queueManager.AccessQueue(queueName, MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING)) {
while (true) {
MQMessage msg = new MQMessage();
queue.Get(msg, new MQGetMessageOptions()); // <<= !!!IT IS DELAYED HERE!!!
msg.Seek(0);
string msgContent = msg.ReadString(msg.MessageLength);
if (requestText.Equals(msgContent)) {
break;
}
}
}
Дампы потока:
- Поток получателя клиента MQ
SocketPal.Poll()
Socket.Poll()
MQTCPConnection.Receive()
MQRcvThread.ReceiveBuffer()
MQRcvThread.ReceiveOneTSH()
MQRcvThread.Run()
ThreadHelper.ThreadStart_Context()
ExecutionContext.RunInternal()
ThreadHelper.ThreadStart()
Ожидание Poll()
, но оно заканчивается 5-минутным (сердцебиение) тайм-аутом. Затем он переходит к this.network.Read()
и немедленно получает правильные данные.
this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.timeout);
if (this.socket.Poll(this.timeout * 1000, SelectMode.SelectRead))
{
length2 = this.network.Read(cBuffer, offset1, length1);
Monitor.Wait()
MQSession.ExchangeTSH()
MQProxyQueue.RequestMessages()
MQProxyQueue.FlushQueue()
MQProxyQueue.ProxyMQGET()
MQFAP.zstMQGET()
MQFAP.MQGET()
MQDestination.Get()
MQDestination.Get()
MqConnectionPoolTest.TestMqDirect()
Ожидает entry.Reply
для заполнения получающей веткой:
while (entry.Reply == null) {
Monitor.Wait((object) entry, this.rmtReqEntMaxPollTime, true);
...
Пожалуйста, кто-нибудь знает обходной путь или есть исправление?