lizongbo at 618119.com 工作,生活,Android,前端,Linode,Ubuntu,nginx,java,apache,tomcat,Resin,mina,Hessian,XMPP,RPC

2009年01月11日

扩展java.net.URL支持自定义协议来优化hessian的调用

Filed under: Java — 标签:, , , , , , , — lizongbo @ 22:51

扩展java.net.URL支持自定义协议来优化hessian调用

hessian是个高性能的java RPC调用协议,但是官方默认只提供了基于http和https两种方式的远程调用。
虽然每天使用http方式调用上千万次也没出现性能问题,(有jdk一份功劳,jdk1.5及以上版本支持了http.KeepAlive,
默认设置为: .KeepAlive.remainingData=512
http.KeepAlive.queuedConnections=10)
但是如果能够改成tcp纯socket长连接池方式,性能是还可以优化的,因为把http的header头信息给省了七七八八。
由于hessian使用的URL和URLConnection来发送hessian请求和应答的,而URL的协议处理是可以扩展的,
因此可以通过扩展URL支持自定义协议来灵活切换hessian使用http或者tcp或者udp方式进行请求发送和接收应答。

查找相关资料后整理了三种扩展方法:

1.通过用户指定的package名称的最后一位作为协议名称(包名要是小写的)。
例如我自定义了三个协议,hessiantcp,hessianudp,hessiantcpudp;
则需要建立三个继承java.net.URLStreamHandler的Handler类(实现类的名字必须是Handler).
即:
com.lizongbo..protocol.hessiantcp.Handler.
com.lizongbo.hessian.protocol.hessianudp.Handler.java
com.lizongbo.hessian.protocol.hessiantcpudp.Handler.java

在运行时,还要指定系统属性java.protocol.handler.pkgs
或者在java命令行里增加启动参数:
-Djava.protocol.handler.pkgs=com.lizongbo.hessian.protocol(多个包名之间用竖线隔开,例如:
-Djava.protocol.handler.pkgs=com.lizongbo.hessian.protocola|com.lizongbo.hessian.protocolb)

或者在代码里调用创建URL之前,先执行:
[code]
System.setProperty(“java.protocol.handler.pkgs”,”com.lizongbo.hessian.protocol”);
serviceUrl = new (“hessiantcp://618119.com/blog/hessian/service”);
[/code]
这样,用户便能够通过URL对象处理hessiantcp://这样的协议了,
否则,使用hessiantcp://这样的协议会导致异常。

参考:http://www.tuscany.org.cn/index.php/Tuscany与JBoss集成中遇到的问题及排除

http://java.sun.com/developer/onlineTraining/protocolhandlers/

2.设置指定的URLStreamHandlerFactory也可以扩展自定义的协议。
[code]
package com.lizongbo.hessian.protocol;

import java.net.*;
import java.util.Hashtable;

class HessianURLStreamHandlerFactory implements URLStreamHandlerFactory {
private String packagePrefix = “com.lizongbo.hessian.protocol”;
protected static Hashtable handlers
= new Hashtable
();
private URLStreamHandlerFactory otherFactory;
static {
handler = new com.lizongbo.hessian.protocol.
hessiantcp.Handler();
handlers.put(“hessiantcp”, handler);
handler = new com.lizongbo.hessian.protocol.hessianudp.Handler();
handlers.put(“hessianudp”, handler);
handler = new com.lizongbo.hessian.protocol.hessiantcpudp.Handler();
handlers.put(“hessiantcpudp”, handler);

}

public HessianURLStreamHandlerFactory() {
this(null);
}

public HessianURLStreamHandlerFactory(URLStreamHandlerFactory otherFactory) {
this.setOtherFactory(otherFactory);
}

public void setOtherFactory(URLStreamHandlerFactory otherFactory) {
this.otherFactory = otherFactory;
}

public URLStreamHandlerFactory getOtherFactory() {
return otherFactory;
}

public URLStreamHandler createURLStreamHandler(String protocol) {
URLStreamHandler handler = handlers.get(protocol);
if (handler != null) {
return handler;
}
try {
String clsName = packagePrefix + “.” + protocol + “.Handler”;
Class cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
if (cls != null) {
handler = (URLStreamHandler) cls.newInstance();
handlers.put(protocol, handler);
return handler;
}
} catch (Exception e) {
// any number of exceptions can get thrown here
}

if (otherFactory != null) {
return otherFactory.createURLStreamHandler(protocol);
}
if (“http”.equalsIgnoreCase(protocol)) {
return null; //返回非null的URLStreamHandler还可以覆盖java默认实现协议的URLStreamHandler
}

return null;
}

}

[/code]

在代码里调用创建URL之前,先执行:
[code]
static{
try {
/**
该行代码只能执行一次,否则会抛出工厂已经定义的错误,错误信息如下:
java.lang.Error: factory already defined
at java.net.URL.setURLStreamHandlerFactory(URL.java:1074)
这样的方式还有个缺点,就是工厂一旦被其它第三方组件占用,那么使用这个方法就只能二者选一,
除非其它组件支持创建URLStreamHandlerFactory实例,
因为java.net.URL是不提供获取已经设置存在的factory的方法的。

*/
URL.setURLStreamHandlerFactory(new HessianURLStreamHandlerFactory());
} catch (Exception ex) {

}
}
URL serviceUrl = new URL(“hessiantcp://618119.com/blog/hessian/service”);
[/code]

早期java没有自带jsse的时候,想要使用到https协议就需要类似的处理,
参考: http://www.javaworld.com/javaworld/javatips/jw-javatip96.html

3.在创建URL的时候,手工识别,并实现自定义协议所需的URLStreamHandler.
代码如下:
public ProtobufRpcChannel(String url) {
try {
if (url != null && url.toLowerCase().startsWith(“hessiantcp://”)) {
serviceUrl = new URL(null, url, new URLStreamHandler() {
protected URLConnection openConnection(URL u) throws
IOException {
return null;//在这里处理
}
});
} else {
serviceUrl = new URL(url);
}
} catch (MalformedURLException ex) {
ex.printStackTrace();

}

可以参考一个jms协议的扩展例子:http://www.ibm.com/developerworks/cn/java/l-jms/index.html

有个注意事项,如果试用了非http方式的发送hessian请求,
接口调用方法的返回值不能够是java.io.InputStream,因为hessian的代码里写死了:
在返回InputStream的时候,连接类别被强行转成HttpURLConnection(但是ResultInputStream里对httpConn并没啥特别的操作,
暂时没看懂作者为何这样写的)

[code]
Object value = in.readObject(method.getReturnType());

if (value instanceof InputStream) {
value = new ResultInputStream(httpConn, is, in, (InputStream) value);
is = null;
httpConn = null;
}
[/code]

3 Comments »

  1. ~很有用的东西~ THANKS~

    Reply

    评论 by Caesar — 2009年04月7日 @ 17:47

  2. 楼主,
    com.lizongbo.hessian.protocol.hessiantcp.Handler.java
    com.lizongbo.hessian.protocol.hessianudp.Handler.java
    com.lizongbo.hessian.protocol.hessiantcpudp.Handler.java

    这三个类是否能贴上代码?

    祝好!

    Reply

    评论 by greed — 2009年12月23日 @ 16:28

  3. 也可以使用反射 对URL的 static Hashtable handlers = new Hashtable(); 属性进行赋值

    Reply

    评论 by jet — 2012年02月2日 @ 11:34

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress