servicestack 如何使用ServiceStack RabbitMQ RPC从异常中恢复
在ServiceStack web服务项目中给定以下代码: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
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项目,以模拟寿命更长的应用程序。