servicestack 如何使用ServiceStack RabbitMQ RPC从异常中恢复,servicestack,rabbitmq,servicestack,Rabbitmq" /> servicestack 如何使用ServiceStack RabbitMQ RPC从异常中恢复,servicestack,rabbitmq,servicestack,Rabbitmq" />

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

servicestack 如何使用ServiceStack RabbitMQ RPC从异常中恢复,servicestack,rabbitmq,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

在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;
    }         
}
public object Post(潜在客户信息请求)
{
if(request==null)抛出新的ArgumentNullException(“request”);
尝试
{
var msgId=Guid.NewGuid();
var profiler=profiler.Current;
使用(profiler.Step(“DirectAPI LeadInformation POST{0}”.Fmt(msgId)))
{
var domainRequest=Mapper.Map(请求);
领导信息反应;
使用(var client=base.MessageFactory.CreateMessageQueueClient())
{
var replyToMq=client.GetTempQueueName();
使用(profiler.Step(“请求消息{0}.Fmt(msgId)))
{
var requestMsg=新消息(domainRequest)
{
ReplyTo=replyToMq,
Id=msgId,
};
client.Publish(requestMsg);
}
使用(profiler.Step(“响应消息{0}.Fmt(msgId)))
{
var超时=TimeSpan.From毫秒(2000);
//IMessageQueueClient.Get使线程休眠100毫秒,让我们总共等待x秒
var responseMsg=client.Get(replyToMq,超时);
var domainResponse=responseMsg.GetBody();
if(domainResponse.ResponseStatus!=null)
{
client.Nak(responseMsg,false);
}
client.Ack(responseMsg);
response=Mapper.Map(domainResponse);
}
}
返回响应;
}
}
捕获(异常)
{
_日志错误(异常);
投掷;
}         
}
以及承载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);
mqServer.RegisterHandler(m=>
{
尝试
{
repository.SaveMessage(m作为消息);
领导信息响应;
使用(var client=newjsonserviceclient(settingsFactory.GetMasterSetting(“ProcessorApi:baseUri”))
{
response=client.Post(m.GetBody());
}
返回响应;//如果为null,将导致RabbitMQ挂起
}
捕获(异常)
{
_log.Error(“RegisterHandler”,异常);
投掷;
}
}, 1);
Version2

调用正在处理的服务

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);
mqServer.RegisterHandler(m=>
{
var db=container.Resolve();
db.SaveMessage(m作为消息);
var响应=ServiceController.ExecuteMessage(m);
返回响应;//如果为null,将导致RabbitMQ挂起
}, 4);
您会注意到,如果在处理过程中返回null以模拟行中某个位置的NullReferenceException,RabbitMQ临时队列将保持“运行”状态而不是“空闲”,它将保持此挂起状态,如下图所示

队列将无限期地保持此状态,解决此情况的唯一方法是回收RabbitMQ windows服务或托管进程,无论哪种方法都无法在生产环境中正常工作。我已经尝试设置一个超时,但在本例中,这似乎并不像预期的那样有效

如何可靠地从该问题中恢复

谢谢,,
Stephen

响应为
null
时的行为,因为服务的典型响应类型未知,因此

此行为还包括客户端
ReplyTo
请求,但后来将其更改为将请求数据发布回客户端
ReplyTo
MQ

虽然此更改现在应该清理客户端创建的独占临时应答MQ,但这意味着只返回请求DTO,而不是客户端通常期望的响应DTO。这种行为在这些文件中可见,如果处理程序返回空响应DTO,则可以避免这种行为。尽管正常行为是针对异常气泡,并在请求DLQ中发布


null
响应发布回ReplyMQ的更改可从现在的v4.0.37+获得。

您是否从带有
ReplyTo
的MQ客户端调用此服务?您还可以添加用于调用此服务的代码吗?不,为了简洁起见,我省略了这一点,是您从处理程序返回null导致挂起。挂起是什么意思?队列只是处于活动状态,还是会导致服务挂起,即工作进程是否仍能够处理消息?另外,你能告诉我你是不是在用
回复给客户打电话吗?(如果“否”表示不是,或者您不希望添加客户端代码,则不会)。使用此代码(使用MyGet软件包更新),我仍然看到相同的行为。要澄清的是,这将是具有返回Null的处理程序的服务器项目。不遵守原始超时值怎么办?@Stephen您是说?如果不返回null,可以指向特定的代码行(GitHub允许链接到行号)。否则,您可以使用pull-request更新测试项目。是的,如果您删除HelloRabbitResponse,而不是从RegisterHandler返回null,您将看到我所说的内容。我还在PR中添加了两个web项目,以模拟寿命更长的应用程序。