文章: Java Remoting远程服务(下)

标签: 文章 java remoting | 发表时间:2012-02-02 14:00 | 作者:
出处:http://pipes.yahoo.com/pipes/pipe.info?_id=10560380f804c7341f042a2b8a03e117

上篇分别介绍了Java Remoting远程服务中的RMI、EJB、Web Service等技术,下篇继续分享其他的内容。

4. Hessian

Hessian(http://hessian.caucho.com)是一种轻量级的Web Service, 采用的是二进制的RPC协议。

图五:Hessian架构图 [5]

如图五所示,Hessian可以形容是一种基于二进制协议提供RMI功能的组件。

接下来我们使用Hessian来实现本文的用例。

  1. 接口类IAnimalService.java
  2. public interface IAnimalService {
        public String getMonkeyName();
    }
  3. 实现类AnimalServiceImp.java
  4. public class AnimalServiceImp implements IAnimalService {
        @Override
        public String getMonkeyName() {
            return "I'm Jacky";
        }
    }
  5. 服务端容器Tomcat配置Web.xml(不需要单独编写Servlet代码)
  6. <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>
  7. 客户端Client.java
  8. 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的利弊:

  • 优势:使用简单,速度快;跨语言,跨平台;可以用来兼容legacy系统的功能。
  • 劣势:安全性的支持不够强,不支持两阶段事务。

通过上面的例子我们可以看出,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作为解决方案的企业也越来越多。

5. NIO(Mina/Netty)

Java NIO可以理解为我们常说的非阻塞IO(异步IO),这个概念在高并发、多线程的环境里面尤为适用。NIO的基本原理是选择器来处理IO请求,将每个请求做标识,塞入处理队列;每个客户端请求进入睡眠,等待唤醒。

图六:异步IO工作原理 [6]

图六展示了异步IO的工作原理,很显然异步IO在高并发的情况下可以节省系统很多资源(对比阻塞IO,异步IO不需要开启同等数量的服务线程)。

接下来我们使用异步IO来实现本文的用例,第三方库使用的是Netty。

  1. 接口类IAnimalService.java, Request.java
    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;
        }
    }
  2. 实现类AnimalServiceImp.java
    public class AnimalServiceImp implements IAnimalService, Serializable {
        /** 
         * 序列号
         */
        private static final long serialVersionUID = -160535222600556362L;@Override
        public String getMoneyName() {
            return "I'am Jackey";
        }
    } 
  3. 服务器端Server.java
    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)); //启动服务并绑定端口 
  4. 客户端代码Client.java
    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这种方式来做分布式应用的优劣也是非常明显的:

  1. 优点:基于TCP通信,效率上高于HTTP的方式,非阻塞IO应对高并发绰绰有余。根据具体的需要制定数据传输的格式,可扩展性强。
  2. 缺点:不能跨语言,无法穿透防火墙。

结论

对企业来讲,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

作者

李湃,上海交通大学计算机硕士毕业,5年互联网的行业经验,现就职于国内某互联网公司,喜欢开源技术,对于Java企业架构、分布式技术、高性能高可靠软件设计有极大的热情,希望能对国内社区有所贡献。博客地址:http://haperkelu2011.iteye.com/


感谢 崔康对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至 [email protected]。也欢迎大家通过新浪微博( @InfoQ)或者腾讯微博( @InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

相关 [文章 java remoting] 推荐:

文章: Java Remoting远程服务(下)

- - InfoQ cn
上篇分别介绍了Java Remoting远程服务中的RMI、EJB、Web Service等技术,下篇继续分享其他的内容. 百度技术沙龙第二十三期:一站式的前端开发框架(2月18日 周六). QCon北京2012:语言代码之美. 盛大云计算创意&开发大赛火热报名中. Hessian(http://hessian.caucho.com)是一种轻量级的Web Service, 采用的是二进制的RPC协议.

文章: MongoDB、Java及ORM

- - InfoQ cn
目前有很多互相竞争的NoSQL产品,它们使用的方式不尽相同,但都能很好地解决大数据问题. MongoDB就是其中一款非常不错的产品. MongoDB是面向文档、无Schema的存储解决方案,它用JSON风格的文档展现、查询、修改数据. MongoDB有很丰富的文档,安装和设置都很简单,而且易于扩展.

文章: Java SE1.6中的Synchronized

- - InfoQ cn
在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了Java SE1.6中为了减少获得锁和释放锁带来的性能消耗,而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程.

文章: Java虚拟机家族考

- Haides - InfoQ中文站
说起Java虚拟机,许多Java程序员都会潜意识地把它与Sun HotSpot虚拟机等同看待,也许还有一些程序员会注意到BEA JRockit和IBM J9,但大多数人对JVM的认识都仅限于此了. 从1996年初Sun发布的JDK 1.0中所包含的Sun Classic VM算起,Java虚拟机已经发展了15个年头,沧海桑田一瞬间,15年转眼而过,这期间曾经涌现、湮灭过许多或经典或优秀或有特色的虚拟机实现,在《Java虚拟机专栏》的第1篇中,我们先暂且把代码与技术放下,一起来回顾一下Java虚拟机家族的发展轨迹和历史变迁.

文章: Apache Crunch:用于简化MapReduce编程的Java库

- - InfoQ cn
Apache Crunch(孵化器项目)是基于Google的 FlumeJava库编写的Java库,用于创建MapReduce流水线. 与其他用来创建MapReduce作业的高层工具(如Apache Hive、Apache Pig和Cascading等)类似,Crunch提供了用于实现如连接数据、执行聚合和排序记录等常见任务的模式库.

文章: 深入理解Java内存模型(五)——锁

- - InfoQ cn
锁的释放-获取建立的happens before 关系. 锁是java并发编程中最重要的同步机制. 锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 深度剖析WebKit渲染机制:Chromium项目Committer确认QCon北京2013. QCon北京自动化运维专题:腾讯海量SNS社区网站高效运维探索.

文章: JAVA线程池的分析和使用

- - InfoQ cn
合理利用线程池能够带来三个好处. 通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 当任务到达时,任务可以不需要等到线程创建就能立即执行. 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控. 但是要做到合理的利用线程池,必须对其原理了如指掌.

文章: Java编码易疏忽的十个问题

- - InfoQ cn
在Java编码中,我们容易犯一些错误,也容易疏忽一些问题,因此笔者对日常编码中曾遇到的一些经典情形归纳整理成文,以共同探讨. Amazon五星敏捷图书作者、敏捷教练Amr Elssamadisy确认参加2012QCon杭州. 腾讯Web前端团队TAT(Tencent Alloy Team)负责人屈超确认参加2012QCon杭州.

文章: James Ward谈使用HTML5和Java开发客户端/服务器应用

- - InfoQ cn
谈到应用开发,不管是客户端/服务器类应用、传统的Web应用还是移动Web应用,最近的趋势是使用像流式网格布局(Fluid Grid Layout)和响应式网页设计(Responsive Web Design)这样的模式,以及像HTML5、CSS3和JavaScript(客户端和服务器都支持)这样的技术.

再次发布本人所有博客文章中涉及的代码与工具(大部分是C++和Java)

- - BlogJava_首页
    (为了能让更多人看到,再发一次旧文,望见谅).     为了更方便地管理博文中涉及的各种代码与工具资源,现在把这些资源迁移到 Google Code 中,有兴趣者可前往下载. 1、《 Portal-Basic Java Web 应用开发框架 v2.6.2 发布(源码、示例及文档)》.   资源下载地址: http://code.google.com/p/portal-basic/downloads/list.