我想用一个在Spring Integration中设置的API请求。响应时间应作为消息头添加,以便可以在下游进行审计并与其他数据一起记录。
HTTP成功和错误响应都应该定时,例如我想知道服务器发出500响应需要多长时间。我也已经在使用Retry和CircuitBreaker建议(因为我想要这个功能)。
我已经写了一份自定义建议(来自reading the docs)并添加到我的:
public class RequestResponseTimeInterceptor extends AbstractRequestHandlerAdvice {
private static final Logger LOG = Logger.getLogger(RequestResponseTimeInterceptor.class);
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
long before = System.currentTimeMillis();
try {
Object result = callback.execute();
long after = System.currentTimeMillis();
message = MessageBuilder.fromMessage(message).setHeader("requestResponseTime", after - before).build();
return result;
}
catch (MessageHandlingException e) {
long after = System.currentTimeMillis();
Message modifiedFailedMessage = MessageBuilder.fromMessage(e.getFailedMessage()).setHeader("requestResponseTime", after - before).build();
throw new MessageHandlingException(modifiedFailedMessage, e.getMessage(), e.getCause());
}
}
<bean id="calculationRetry" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice"/>
<bean id="calculationCircuitBreaker" class="org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice"/>
<bean id="timer" class="com.integration.audit.RequestResponseTimeInterceptor"/>
<int-http:request-handler-advice-chain>
<ref bean="timer"/>
<ref bean="calculationRetry"/>
<ref bean="calculationCircuitBreaker"/>
</int-http:request-handler-advice-chain>
它通过回调执行之间的时间来工作。我遇到了一个问题,即HTTP响应失败(例如HTTP 500)被包装在MessageHandlingException中(在Spring Integration中,按设计),这会阻止其他建议完成并对响应进行计时。
我已经在代码示例中通过捕获异常,将响应时间添加为消息头,重新创建异常并抛出异常(因此它被我的错误通道拾取)解决了这个问题。这似乎工作正常 - 但感觉它不是最好的解决方案。它也是任何重试的完整持续时间(来自我的重试建议),而不是最后一次重试。
是否有更好的方法来定时HTTP请求/响应,它可以很好地与现有的重试建议和失败的HTTP响应代码一起使用?
感谢您的反馈!
修改:根据Artem的信息。
request-handler-advice-chain中的建议顺序很重要(毕竟它是一个有序的链)。所以把'计时器'#39;在链的末端意味着它只是请求/响应的次数,而不是多次重试或断路器。
我根据Artem的评论更新了建议,现在它实际上正在返回一条消息。然后,我可以阅读新的&#34; requestResponseTime&#34;根据需要在管道下游标题。
这是新代码:
public class RequestResponseTimeInterceptor extends AbstractRequestHandlerAdvice {
private static final Logger LOG = Logger.getLogger(RequestResponseTimeInterceptor.class);
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> requestMessage) throws Exception {
long before = System.currentTimeMillis();
try {
Object result = callback.execute();
long after = System.currentTimeMillis();
Message<?> responseMessage = (Message<?>) result;
return MessageBuilder.withPayload(responseMessage.getPayload())
.setHeader(MessageHeaderConstants.REQUEST_RESPONSE_TIME, after - before).build();
}
catch (MessageHandlingException e) {
//Catch HTTP errors (e.g. 500s) so we can also time the response
long after = System.currentTimeMillis();
Message modifiedFailedMessage = MessageBuilder.fromMessage(e.getFailedMessage()).setHeader(MessageHeaderConstants.REQUEST_RESPONSE_TIME, after - before).build();
//rethrow new exception for error handling
throw new MessageHandlingException(modifiedFailedMessage, e.getMessage(), e.getCause());
}
}
<int-http:request-handler-advice-chain>
<ref bean="calculationCircuitBreaker"/>
<ref bean="calculationRetry"/>
<ref bean="timer"/>
</int-http:request-handler-advice-chain>
答案 0 :(得分:1)
首先想一想你的计时器是否应该包含所有重试次数。对于打开断路器的超时计数而言,这对我来说似乎并不重要。我的意思是以不同的方式重新排序你的建议会更加现实:首先是电路,重试然后只有计时器。这样,您将只计算真实的http请求,并且不会通过打开的电路进入该部分。
关于您的例外问题。使用必需的标题重建消息肯定是可以的。为此,我们引入了RewriteEngine On
RewriteCond %{QUERY_STRING} ^(.*)$
RewriteCond %{QUERY_STRING} !^command
RewriteRule ^(.*) http://your.domain?command=$1&%1 [L]
:https://docs.spring.io/spring-integration/docs/latest-ga/reference/html/whats-new.html#_errormessagepublisher_and_errormessagestrategy
另一方面,请注意Spring Cloud Sleuth项目:https://cloud.spring.io/spring-cloud-sleuth/