Nginx 使用反向代理时 Hessian 的 411 错误解决方案

鉴客 发布于 2011/05/19 16:23
阅读 4K+
收藏 7

问题描述:  

Hessian 实现 web service 过程中,需要创建对象时,是使用 HTTP POST 方法来传递数据的。但是在有反向代理 (nginx) 的情况下,会抛出异常 (com.caucho.hessian.client.HessianConnectionException: 411:java.io.IOException: Server returned HTTP response code: 411 for URL:http://xxxx/xxx/xxxService)

首先来看下 HTTP 411 错误的解释: Length Required 服务器不能处理请求,除非客户发送一个 Content-Length 头。( HTTP 1.1 新)这是因为 Hessian 与服务端通信默认是采取分块的方式 (chunked encoding) 发送数据,而反向代理要获得 Content-Length 这个头,才能处理请求,但是 Hessian 的请求中并没有加入这个参数。

解决方法:  

com.caucho.hessian.client.HessianProxyFactory 类中,有一个 boolean _chunckedPost 的域成员,其默认值为 true 。这个值就是规定 Hessian 是否以分块发送的方式与服务端交换数据的参数,因此在创建 com.caucho.hessian.client.HessianProxyFactory 的对象后(假设为 factory ),只要调用其上的 setChunckedPost() 方法,把这个属性设置为 false 即可。即 factory.setChunkedPost(false);

分块编码传输:

分块编码 (chunked encoding) 传输方式是 HTTP 1.1 协议中定义的 Web 用户向服务器提交数据的一种方法,当服务器收到 chunked 编码方式的数据时会分配一个缓冲区存放之,如果提交的数据大小未知,客户端会以一个协商好的分块大小向服务器提交数据。

The content can be broken up into a number of chunks; each of which is prefixed by its size in bytes. A zero size chunk indicates the end of the response message. If a server is using chunked encoding it must set the Transfer-Encoding header to "chunked".

Chunked encoding is useful when a large amount of data is being returned to the client and the total size of the response may not be known until the request has been fully processed. An example of this is generating an HTML table of results from a database query. If you wanted to use the Content-Length header you would have to buffer the whole result set before calculating the total content size. However, with chunked encoding you could just write the data one row at a time and write a zero sized chunk when the end of the query was reached.

 

如果不使用 Chunked encoding 传输方式,需要将要发送的数据缓存下来,计算出 content-length ,从而满足反向代理( Nginx )需要 content-length 的要求。

业务系统对 hessian 进行封装:

1.       根据 HessianProxyFactoryBean 实现 MyHessianProxyFactoryBean  

public class MyHessianProxyFactoryBean extends MyHessianClientInterceptor implements FactoryBean {  

    private Object serviceProxy ;  

    public void afterPropertiesSet() {

        super .afterPropertiesSet();

        this . serviceProxy = new ProxyFactory(getServiceInterface(), this )

                .getProxy(getBeanClassLoader());

    }  

    public Object getObject() {

        return this . serviceProxy ;

    }  

    public Class getObjectType() {

         return getServiceInterface();

    }  

    public boolean isSingleton() {

        return true ;

    }

}

 

2.       根据 HessianClientInterceptor 实现 MyHessianClientInterceptor  

public class MyHessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {  

    private HessianProxyFactory proxyFactory = new MyHessianProxyFactory();  

private Object hessianProxy ;  

……………………

}

3.       继承 HessianProxyFactory 实现 MyHessianProxyFactory  

public class MyHessianProxyFactory extends HessianProxyFactory {  

    @Override

    public Object create(Class api, String urlName, ClassLoader loader)

            throws MalformedURLException {

        this .setChunkedPost( false ); // 设置规定 Hessian 不以分块发送的方式与服务端交换数据,解决 hessian nginx 411 错误

 

        if (api == null ) throw new NullPointerException(

                "api must not be null for HessianProxyFactory.create()" );

        InvocationHandler handler = null ;  

        URL url = new URL(urlName);

        handler = new MyHessianProxy(url, this );  

        return Proxy.newProxyInstance (loader, new Class[] { api, HessianRemoteObject. class },

                handler);

    }  

}

4.       继承 HessianProxy 实现 MyHessianProxy

public class MyHessianProxy extends HessianProxy {

 

    public MyHessianProxy(URL url, HessianProxyFactory factory) {

        super (url, factory);

    }

 

    /** Get information from the headers of response */

    @Override

    protected void parseResponseHeaders(URLConnection conn) {  

    }  

    /** Add information to request headers */

    @Override

    protected void addRequestHeaders(URLConnection conn) {  

    }  

}

 

 

 

针对该问题的简单配置:

         xml 配置文件中添加属性:

< property name = "chunkedPost" >

           < value > false </ value >

       </ property >

 

       < property name = "readTimeout" >

           < value > 25000 </ value >

       </ property >

 

总结:

         该问题还可以通过修改 nginx 配置,使得 nginx 可以处理分块编码 (chunked encoding) 传输方式。据本人所知, nginx 还没有明确的解决方案,需要继续研究。

         Hessian 扩展性很好,它的实现预留了一些接口,允许开发人员对其进行一定的封装,从而按自己的需求对 hessian 进行设置。

加载中
返回顶部
顶部