1、Nginx 使用反向代理时 Hessian 的 411 错误
报错如下:
com.caucho.hessian.client.HessianConnectionException: 411:java.io.IOException: Server returned HTTP response code: 411 for URL:http://xxxx/xxx/xxxService
排查过程:
刚一开始发现报错时,以为是server端返回的报错,还跑去问server端是不是报错了。。。后来了解到server端添加了nginx代理,不再支持绑定ip直连接口的方式。通过查资料发现Hessian在Nginx反向代理的情况下存在问题。
问题原因:
首先来看下 HTTP 411 错误的解释: Length Required 服务器不能处理请求,除非客户发送一个Content-Length 头。( HTTP 1.1 新)这是因为 Hessian 与服务端通信默认是采取分块的方式 (chunked encoding) 发送数据,而反向代理要获得 Content-Length 这个头,才能处理请求,但是 Hessian 的请求中并没有加入这个参数。
解决办法:
根据以上问题出现的原因,最直接想到的办法就是,缺少一个Content-Length那么我可以传递这个参数给server端,从而避免这个报错。具体办法是需要将将要发送的数据缓存下来。计算出content-length随着请求发送给server端,以满足Nginx需要这个参数的需求。
然而这样计算比较麻烦,所以可以使用Hessian提供的一个参数进行设置,setChunkedPost(false),(可以在初始化HessianFactory对象时写在构造函数里)这个是用来设置 Hessian 是否以分块发送的方式与服务端交换数据的参数,默认为true,设置为false时,向server端发送数据时就不采用分块方式,从而避免了报错。
2、Hessian调用重载方法报错问题(expected end of call)
报错如下:
org.springframework.web.util.NestedServletException: Hessian skeleton invocation failed; nested exception is com.caucho.hessian.io.HessianProtocolException: ....... Caused by: com.caucho.hessian.io.HessianProtocolException: getList: expected end of call ('xxx') at 0x53 (S). ..... com.caucho.hessian.client.HessianRuntimeException: com.caucho.hessian.io.HessianProtocolException: .......
排查过程:
Caused by: com.caucho.hessian.io.HessianProtocolException: getList: expected end of call ('xxx') at 0x53 (S).
根据以下报错可以看出是调用getList方法时报错,将server端提供的接口反编译发现,getList存在多个,也就是存在重载的情况。
问题原因:
查找相关资料发现,Hessian的HessianFactory的isOverloadEnabled属性默认为false。这个参数如果为false,Hessian调用的时候获取接口仅根据方法名;反之,Hessian调用时决定调用哪个方法是通过方法名和参数类型一起决定。所以也就解释了,存在重载的方法时为什么会报以上错误。
解决办法:
在初始化HessianFactory对象时写在构造函数里,对factory设置 setOverloadEnabled(true) ,问题解决。
3、Hessian由于版本不兼容导致的报错(com.caucho.hessian.io.HessianProtocolException: expected string at 0x6d )
报错如下:(借用了一下别人的报错信息,自己的忘记保存了。。。)
2011-4-25 16:14:44 org.apache.catalina.core.StandardWrapperValve invoke 严重: Servlet.service() for servlet remoting threw exception com.caucho.hessian.io.HessianProtocolException: expected string at 0x6d at com.caucho.hessian.io.Hessian2Input.error(Hessian2Input.java:2882) at com.caucho.hessian.io.Hessian2Input.expect(Hessian2Input.java:2830) at com.caucho.hessian.io.Hessian2Input.readString(Hessian2Input.java:1362) at com.caucho.hessian.io.Hessian2Input.readMethod(Hessian2Input.java:272) at com.caucho.hessian.server.HessianSkeleton.invoke(HessianSkeleton.java:249) at com.caucho.hessian.server.HessianSkeleton.invoke(HessianSkeleton.java:221) at org.springframework.remoting.caucho.Hessian2SkeletonInvoker.invoke(Hessian2SkeletonInvoker.java:67) at org.springframework.remoting.caucho.HessianServiceExporter.handleRequest(HessianServiceExporter.java:147) at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:49) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:819) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:754) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:399) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:364) at javax.servlet.http.HttpServlet.service(HttpServlet.java:710) at javax.servlet.http.HttpServlet.service(HttpServlet.java:803) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447) at java.lang.Thread.run(Thread.java:619)
排查过程:
这个报错真心花了好几个小时才搞定。。。从报错信息上来看,仅仅报出了expected string at 0x6d,这个信息没什么参考价值。。。但是我看了下面com.caucho.hessian.io.Hessian2Input.error(Hessian2Input.java:2882),为什么会出现Hessian2?带着疑问我去查了一下,发现高版本和低版本Hessian的协议已经变化了,低版本的是Hessian1,而高版本的是Hessian2。但是我管理的系统中Hessian版本和server端的版本一直没有变化呀,为什么会这样报错?突然想起来我resin的版本升级了,从3.0.28升级到了3.1,经过查询resin3.1新引入了Hessian的包,而且jar包的版本是高于我当前系统的低版本的,并且resin启动时会强制加载它的hessian包。。。orz。。。所以基本上定位了,就是版本不兼容。然而我还没发让server端更改高版本,所以只能自己想办法搞定这个事情。
问题原因:
Resin3.1引入的Hessian版本是高版本,其中使用的协议为Hessian2,而server端是低版本使用Hessian1协议,协议不匹配,通信后的结果自然是你不懂我我不懂你。
解决办法:
很多文章写解决办法都是换版本,但是在实际情况下,作为接口的使用者,没有能力更没有理由让server端按照你的意思去升级版本,但是这事就无解了吗?并不是,可以设置两个参数来解决这个事情。
factory.setHessian2Reply(false); factory.setHessian2Request(false); void setHessian2Reply(boolean isHessian2) True if the proxy can read Hessian 2 responses. void setHessian2Request(boolean isHessian2) True if the proxy should send Hessian 2 requests.
根据Hessian的文档说明发现,这两个参数一个是设置用Hessian2协议发请求,一个是设置用Hessian2协议收回复,在高版本中这个参数默认是true,所以我们可以把它们设置成false,也就是通知系统强制用Hessian1协议来收发信息,问题也随之解决。(实验一段时间并未发现问题,目前看这个方案可行)
总结:
以上就是这阶段系统中Hessian出现的各种问题的一个小总结,可能有些东西叙述起来略显啰嗦,也有可能某些东西存在问题,希望大家批评指正。
对于开源的软件来说,版本冲突的问题一直都是困扰程序猿的大问题,然而作为使用者的我们如何避免入坑呢?或许官方文档才是最值得依靠和研读的,即使它是英文的。