如何使用ServiceStack RabbitMQ RPC从异常中恢复

时间:2015-01-19 22:03:03

标签: servicestack rabbitmq

在ServiceStack Web服务项目中给出以下代码:

public object Post(LeadInformation request)
{
    if (request == null) throw new ArgumentNullException("request");

    try
    {
        var msgId = Guid.NewGuid();
        var profiler = Profiler.Current;
        using (profiler.Step("Direct Api LeadInformation POST {0}".Fmt(msgId)))
        {

            var domainRequest = Mapper.Map<Leads.ServiceModel.Api.DirectApi.LeadInformationInfo>(request);

            LeadInformationResponse response;
            using (var client = base.MessageFactory.CreateMessageQueueClient())
            {
                var replyToMq = client.GetTempQueueName();
                using (profiler.Step("request message {0}".Fmt(msgId)))
                {
                    var requestMsg = new Message<Leads.ServiceModel.Api.DirectApi.LeadInformationInfo>(domainRequest)
                    {
                        ReplyTo = replyToMq,
                        Id = msgId,
                    };
                    client.Publish<Leads.ServiceModel.Api.DirectApi.LeadInformationInfo>(requestMsg);    
                }

                using (profiler.Step("response message {0}".Fmt(msgId)))
                {
                    var timeOut = TimeSpan.FromMilliseconds(2000);
                    // IMessageQueueClient.Get sleeps the thread for 100ms, lets wait for a total of x seconds

                    var responseMsg = client.Get<Leads.ServiceModel.Api.DirectApi.LeadInformationInfoResponse>(replyToMq, timeOut);

                    var domainResponse = responseMsg.GetBody();

                    if (domainResponse.ResponseStatus != null)
                    {
                        client.Nak(responseMsg, false);
                    }
                    client.Ack(responseMsg);
                    response = Mapper.Map<LeadInformationResponse>(domainResponse);
                }
            }
            return response;
        }

    }
    catch (Exception exception)
    {
        _log.Error(exception);
        throw;
    }         
}

托管ServiceStack的Windows服务(2个版本,相同结果):

版本1

在另一个进程中调用Web服务,该进程可能会死并返回null或异常

mqServer.RegisterHandler<LeadInformationInfo>(m =>
{
    try
    {
        repository.SaveMessage(m as Message);
        LeadInformationInfo response;
        using (var client = new JsonServiceClient(settingsFactory.GetMasterSetting("ProcessorApi:baseUri")))
        {
            response = client.Post<LeadInformationInfo>(m.GetBody());
        }
        return response; // will cause RabbitMQ to hang if null
    }
    catch (Exception exception)
    {
        _log.Error("RegisterHandler<LeadInformationInfo>", exception);
        throw;
    }
}, 1);

版本2

调用正在进行中的服务

mqServer.RegisterHandler<LeadInformationInfo>(m =>
{
    var db = container.Resolve<IRepository>();
    db.SaveMessage(m as Message);
    var response = ServiceController.ExecuteMessage(m);
    return response; // will cause RabbitMQ to hang if null
}, 4);

你会注意到,如果你在处理过程中返回null来模拟NullReferenceException,那么RabbitMQ临时队列就会停留在&#39;运行&#39; state vs.&#39; idle&#39;,它处于挂起状态,如此图所示。

this image

队列将无限期地保持这种状态,只有这样才能解决这种情况,即回收RabbitMQ Windows服务或托管过程,无论哪种方式在生产环境中都能很好地工作。我已尝试设置超时,但在这种情况下,这似乎并不像预期的那样有效。

如何从这个问题中可靠地恢复?

谢谢你, 斯蒂芬

1 个答案:

答案 0 :(得分:2)

响应为null时的行为,因为服务的典型响应类型未知,Request DTO is published to the .out MQ

此行为还包括客户端ReplyTo请求,但后来将其更改为in this commit以将请求DTO发布回客户端ReplyTo MQ。

虽然此更改现在应该清理客户端创建的独占临时回复MQ,但这意味着只返回请求DTO ,而不是响应DTO 客户通常会期望。这些行为在这些null Response MQ Tests中可见,如果您的处理程序返回空响应DTO,则可以避免此行为。虽然正常行为适用于异常气泡,但可以在请求DLQ中发布。

null个回复发布回ReplyMQ的更改可以从 v4.0.37 + 获得,现在是available on MyGet