欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于Hessian一些报错的解决方案总结

程序员文章站 2022-04-20 21:13:19
...
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。这也正是工作时引入Hessian的一个重要原因,简单易配置。但是同样也有缺点,高版本对于低版本的向下兼容处理的并不好、一直流传一句话,“对于hessian,能不用高版本就不用高版本”,这句话充分说明了大家对于Hessian一些错误的困扰,那么下面总结一下最近遇到的Hessian相关的问题。

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出现的各种问题的一个小总结,可能有些东西叙述起来略显啰嗦,也有可能某些东西存在问题,希望大家批评指正。
对于开源的软件来说,版本冲突的问题一直都是困扰程序猿的大问题,然而作为使用者的我们如何避免入坑呢?或许官方文档才是最值得依靠和研读的,即使它是英文的。