上篇分别介绍了Java Remoting远程服务中的RMI、EJB、Web Service等技术,下篇继续分享其他的内容。
Hessian(http://hessian.caucho.com)是一种轻量级的Web Service, 采用的是二进制的RPC协议。
图五:Hessian架构图[5]
如图五所示,Hessian可以形容是一种基于二进制协议提供RMI功能的组件。
接下来我们使用Hessian来实现本文的用例。
public interface IAnimalService { public String getMonkeyName(); }
public class AnimalServiceImp implements IAnimalService { @Override public String getMonkeyName() { return "I'm Jacky"; } }
<servlet> <servlet-name>AnimalService</servlet-name> <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class> <init-param> <param-name>home-class</param-name> <param-value>com.demo.AnimalServiceImp</param-value> </init-param> <init-param> <param-name>home-api</param-name> <param-value>com.demo.IAnimalService</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>AnimalService</servlet-name> <url-pattern>/service/animalService</url-pattern> </servlet-mapping> </servlet>
final String url = "http://localhost:8080/service/animalService"; HessianProxyFactory factory = new HessianProxyFactory(); IAnimalService proxy = (IAnimalService) factory.create(IAnimalService.class, url); System.out.println(proxy.getMonkeyName());
使用Hessian的利弊:
通过上面的例子我们可以看出,Hessian使用起来非常简单简单,而且性能评测结果显示Hessian高于基于XML协议的RPC技术(http://daniel.gredler.net/2008/01/07/java-remoting-protocol-benchmarks/)。笔者认为在局域网内Hessian取代WebService是可行的,谁愿意花时间去研究相对笨重的Web Service框架,而且运行相率又很一般呢。大家可能想问,Hessian到底快在哪呢?有两点,首先Hessian采用的是二进制的RPC协议,其次Hessian的序列化速度也比Java本身序列化要快。因而选择Hessian作为解决方案的企业也越来越多。
Java NIO可以理解为我们常说的非阻塞IO(异步IO),这个概念在高并发、多线程的环境里面尤为适用。NIO的基本原理是选择器来处理IO请求,将每个请求做标识,塞入处理队列;每个客户端请求进入睡眠,等待唤醒。
图六:异步IO工作原理[6]
图六展示了异步IO的工作原理,很显然异步IO在高并发的情况下可以节省系统很多资源(对比阻塞IO,异步IO不需要开启同等数量的服务线程)。
接下来我们使用异步IO来实现本文的用例,第三方库使用的是Netty。
public interface IAnimalService extends Serializable { public String getMoneyName(); } public class Request implements Serializable { /** * 序列号 */ private static final long serialVersionUID = 3701941641993894303L;@SuppressWarnings("rawtypes") private Class service; //接口类 private String method; //调用方法名称 private Object[] paras; //调用方法参数 private String version; //服务版本 /** * @return the service */@SuppressWarnings("rawtypes") public Class getService() { return service; } /** * @param service the service to set */ public void setService(Class service) { this.service = service; } /** * @return the method */ public String getMethod() { return method; } /** * @param method the method to set */ public void setMethod(String method) { this.method = method; } /** * @return the paras */ public Object[] getParas() { return paras; } /** * @param paras the paras to set */ public void setParas(Object[] paras) { this.paras = paras; } /** * @return the version */ public String getVersion() { return version; } /** * @param version the version to set */ public void setVersion(String version) { this.version = version; } }
public class AnimalServiceImp implements IAnimalService, Serializable { /** * 序列号 */ private static final long serialVersionUID = -160535222600556362L;@Override public String getMoneyName() { return "I'am Jackey"; } }
final int port = 9990; ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeLine = Channels.pipeline(new SimpleChannelUpstreamHandler() { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { //监听消息到达 Request obj = (request) e.getMessage(); if (obj.getService().equals(IAnimalService.class)) { Method targetMethod = obj.getService().getMethod(obj.getMethod(), new Class[0]); Object result = targetMethod.invoke(new AnimalServiceImp(), obj.getParas()); e.getChannel().write(result); } } }); pipeLine.addFirst("encoder", new ObjectEncoder()); //对象编码器 pipeLine.addFirst("decoder", new ObjectDecoder()); //对象解码器 return pipeLine; } }); bootstrap.bind(new InetSocketAddress(port)); //启动服务并绑定端口
ClientBootstrap client = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
client.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeLine = Channels.pipeline(new SimpleChannelUpstreamHandler() {
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
//创建连接发送请求
Request r = new Request();
r.setVersion("1.0.0"); //设置版本
r.setService(IAnimalService.class); //设置服务类型
r.setMethod("getMoneyName"); //调用服务方法名称
r.setParas(null); //参数
e.getChannel().write(r);
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception{
//监听消息到达
System.out.println(e.getMessage().toString());
}
});
pipeLine.addFirst("encoder", new ObjectEncoder()); //对象编码器
pipeLine.addFirst("decoder", new ObjectDecoder()); //对象解码器
return pipeLine;
}
});
client.setOption("tcpNoDelay", true);
client.setOption("keepAlive", true);
ChannelFuture future = client.connect(new InetSocketAddress("127.0.0.1", 9990));
future.getChannel().getCloseFuture().awaitUninterruptibly();
client.releaseExternalResources(); //释放外部资源
上述代码的实现稍有复杂,主要的结构是客户端将请求对象编码并发送管道,服务端将接受的字节流解码为对象,调用相应的方法并将结果返还至客户端。感兴趣的读者可以查看Netty官网(http://www.jboss.org/netty)来了解详情。
中国最大的互联网公司之一,淘宝,内部使用的服务框架HSF就采用了这种方式(采用的第三方NIO库是Mina)[7]。笔者认为使用NIO这种方式来做分布式应用的优劣也是非常明显的:
对企业来讲,Java Remoting采取何种方案没有一个特定的标准。根据笔者的经验,业务特点以及数据吞吐量决定了技术的选择方向。比如第三方数据接口,重点考虑的是跨平台、跨语言、支持高并发、保证安全;而局域网内的分布式服务,重点考虑的是高性能、稳定性、可伸缩性。
[5] http://safehammad.com/tag/hessian/
[6] http://onjava.com/onjava/2002/09/04/nio.html
[7] http://archive.cnblogs.com/a/1963077/
[8] http://www.salesforce.com/us/developer/docs/api/index.htm