项目组要和被收购的公司做接口对接,使用camel进行集成。在使用的过程当中,我们几个小伙伴都遇到了一个相同的问题:请求request body的内容失踪了。这里贴出有问题的路由配置:
<?xml version="1.0" encoding="UTF-8"?>
<routes xmlns="http://camel.apache.org/schema/spring"><route><from uri="rest:post:sett/pay/apply.json"/><log message="接收clivet请求, ${body}"/><setProperty name="template_code"><constant>clivet_payment_request_json</constant></setProperty><process ref="json2TemplateProcessor"/><to uri="netty-http:http://127.0.0.1:1234/****/save.json"/><!-- ?httpMethod=POST&transferExchange=true --></route>
</routes>
原因是from接口后紧随的log把HttpServletRequest的InputStream给消费掉了。导致转发的请求的springmvc读不到数据。把 ${body}去掉即可解决问题。
给出我的排查过程。
排查过程
debug CamelServlet的入口方法
- CamelServlet先把HttpServletRequest的InputStream读出来,构成成一个InputStreamCache对象,然后写入Exchange的in字段里面。调用堆栈如下:
at org.apache.camel.http.common.HttpHelper.readRequestBodyFromServletRequest(HttpHelper.java:147)at org.apache.camel.http.common.DefaultHttpBinding.parseBody(DefaultHttpBinding.java:646)at org.apache.camel.http.common.HttpMessage.createBody(HttpMessage.java:101)at org.apache.camel.support.MessageSupport.getBody(MessageSupport.java:65)at org.apache.camel.http.common.DefaultHttpBinding.readBody(DefaultHttpBinding.java:193)at org.apache.camel.http.common.DefaultHttpBinding.readRequest(DefaultHttpBinding.java:118)at org.apache.camel.http.common.HttpMessage.init(HttpMessage.java:73)at org.apache.camel.http.common.HttpMessage.<init>(HttpMessage.java:39)at org.apache.camel.http.common.CamelServlet.doExecute(CamelServlet.java:274)at org.apache.camel.http.common.CamelServlet.doService(CamelServlet.java:214)at org.apache.camel.http.common.CamelServlet.service(CamelServlet.java:130)
2. 进入 org.apache.camel.processor.LogProcessor#process 方法这里camel会消费exchange的InputStreamCahce。
读完之后,这个流就close了,你是无法再次读出来了。至此,破案。
我们组三个小伙伴都遇到了这个问题,camel官方也意识到了这个问题。官方faq :WHY IS MY MESSAGE EMPTY?
In Camel the message body can be of any types. Some types are safely readable multiple times, and therefore do not ‘suffer’ from becoming ‘empty’. So when you message body suddenly is empty, then that is often related to using a message type that is no re-readable; in other words, the message body can only be read once. On subsequent reads the body is now empty. This happens with types that are streaming based, such as java.util.InputStream, etc.
意即如果消息体是基于流式的, 如java.util.InputStream、Reader等,是不能重复读取的。
解决方案
1 官方解决方案
camel给出的解决方案 STREAM CACHING,把inputstream缓存起来,供后续重复读。
需要增加如下配置
context.getStreamCachingStrategy().setSpoolEnabled(true);
context.getStreamCachingStrategy().setSpoolDirectory("/tmp/cachedir");
context.getStreamCachingStrategy().setSpoolThreshold(64 * 1024);
context.getStreamCachingStrategy().setBufferSize(16 * 1024);context.setStreamCaching(true);
2 去掉from节点后的${body}
${body}可以在某个processor(LogProcessor除外)消费了HttpServletRequest的Inputstream后再使用,但决不能紧跟着from节点用。