今天我们来聊聊Java远程服务的解决方案。Java分布式远程服务的解决方案,近几年在互联网应用越来越普及。我们简单分析下,形成这种格局的背景。
从无到有开发一个产品的时候,如果技术框架没有积累,那么代码的实现会比较随意,很多时候前端web层耦合了很多后端DAL层的代码。接下来,随着产品越来越多,每个产品的技术实现都会有很多重复代码。这就给后期的维护和升级带来了不便(比如针对某个服务做缓存优化或者日志处理,代价会非常高)。服务模块化呼之欲出!
服务模块化,就意味着代码的实现架构不再是Web层与DAL层的简单关系了。很多相似的业务会抽象为一个分布式服务,Java语言支持多种远程服务的实现,像EJB、 WebService、 RMI、Hessian等等。下面我们通过一个具体的例子来简述这些技术的使用以及在实践中如何权衡各种技术的适用场景。
用例:提供一个分布式的动物中心服务,提供猴子的名字。
1. RMI
RMI是Java提供的分布式应用API,远程方法调用RPC的实现。它的宗旨是,某个JVM下的对象可以调用其他JVM下的远程对象。RMI的底层实现是构建于TCP协议之上,将远程对象绑定具体的端口号,监听客户端的请求;客户端与远程对象的通信当中,依赖于预定义的接口,即RMI会生成一个本地Stub代理类,每次客户端调用远程对象的时候,Stub代理类会初始化参数、启动远程连接、将参数进行编组(marshal),通过网络传输送往服务器端,并对返回的结果进行反编组(unmarshal)。对于客户端调用方来讲,RMI隐藏了对象序列化和网络传输的实现细节。
图一:RMI的调用机制[1]
图一描述了RMI调用的大体步骤:首先RMI Server会通过请求RMIRegistry(远程对象联机注册服务)绑定一个远程对象,对象的元数据信息放在一个已有的Web Server上面;然后RMI Client会发送请求到RMIRegistry获取远程对象的地址,并远程调用该对象的方法。
下面我们使用RMI来实现之前所描述的用例。
- 接口类IAnimalService.java
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IAnimalService extends Remote {
String getMonkeyName() throws RemoteException;
}
- 实现类AnimalServiceImp.java
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
public class AnimalServiceImp implements IAnimalService {
public AnimalServiceImp() {
}
@Override
public String getMonkeyName() throws RemoteException {
return "I'm Jacky";
}
}
- 服务端AnimalServer.java
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
try {
final int port = 8009; //绑定的端口号
final String host = "127.0.0.1"; //本机作为服务host
final String serviceName = "animalService"; //服务名称
IAnimalService obj = new AnimalServiceImp();
IAnimalService stub = (IAnimalService) UnicastRemoteObject.exportObject(obj, port); //端口绑定远程对象
Registry registry = LocateRegistry.getRegistry();
registry.unbind(serviceName);
registry.bind(serviceName, stub); //注册服务地址
System.out.println("Server Start...");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
- 客户端Client.java
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
Registry registry = null;
final String host = "127.0.0.1";
final String serviceName = "animalService"; //服务名称
try {
registry = LocateRegistry.getRegistry(host); //获取远程对象联机注册
//获取动态代理类
IAnimalService stub = (IAnimalService) registry.lookup(serviceName);
//远程调用
String name = stub.getMonkeyName();
System.out.println("monkey name: " + name);
} catch (Exception e) {
e.printStackTrace();
}
- 部署RMI:编译上述代码、启动RMIRegistry、运行服务端的代码(AnimalServer.java)
- 客户端调用RMI:运行客户端代码(Client.java)
使用RMI的利弊:
- 优势:面向对象的远程服务模型;基于TCP协议上的服务,执行速度快。
- 劣势:不能跨语言;每个远程对象都要绑定端口,不易维护;不支持分布式事务JTA
早在Applet时期,Applet+RMI是Java业内广泛推崇的方式来实现分布式计算。笔者认为RMI框架对于安全性、事务、可扩展性的支持非常有限,进而限制了其发展。
2. EJB
EJB是之前Sun公司推出的基于面向对象的服务器端组件模型。它旨在成为一个可移植的、可扩展的、事务处理的、带有安全策略的分布式解决方案。
图二:EJB在J2EE解决方案中的角色[2]
EJB的核心有三个部分:会话Bean、实体Bean、消息Bean。EJB3.0对组件模型做了很多简化,降低了开发以及配置的复杂度。本节讨论的都已EJB3.0为准。
图三:EJB3.0架构图[3]
如图三描述的那样会话Bean主要负责将业务逻辑抽象出来,会话Bean分为有状态Bean和无状态Bean,有状态Bean记录客户端的信息,无状态Bean反之。实体Bean负责持久层ORMapping的工作,EJB3.0对实体Bean做了很大的调整,提供了持久化API(JPA),简化了开发和配置。消息Bean主要用来处理JMS中间件接受的客户端消息,即JMS队列的消费者,本质上它是一个异步的无状态会话Bean。
对于本文的用例来说,最适合使用无状态的会话Bean,下面我们来看下具体的实现。
- 接口类AnimalBeanLocal.java
import javax.ejb.Remote;
@Remote
public interface AnimalBeanLocal {
String getMonkeyName();
}
- 无状态会话Bean AnimalBean.java
import javax.ejb.Stateless;
/**
* Session Bean implementation class AnimalBean
*/
@Stateless
public class AnimalBean implements AnimalBeanLocal {
/**
* Default constructor.
*/
public AnimalBean() {
}
public String getMonkeyName() {
return "I'm Jacky";
}
}
- 客户端Client.java
import javax.naming.InitialContext;
//经由JNDI命明和目录服务获取EJB
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext ctx = new InitialContext(props);
AnimalBeanLocal proxy;
proxy = (AnimalBeanLocal) ctx.lookup("AnimalBean/remote");
System.out.println(helloworld.getMonkeyName());
} catch (Exception e) {
e.printStackTrace();
}
- 部署EJB:启动JBOSS,并将EJB组件注册进JNDI服务
- 客户端调用EJB:运行客户端代码
使用EJB的利弊:
- 优势:可扩展性好,安全性强,支持分布式事务处理。
- 劣势:不能跨语言;配置相对复杂,不同J2EE容器之间很难做无缝迁移。
EJB是被诟病最多的分布式解决方案,主要原因是EJB配置复杂而且不同容器迁移起来困难。尽管EJB3.0做了很多的简化,配置起来还是相对笨重。对于学习曲线如此陡峭的技术来说,并不是企业放心采用的解决方案。
3. Web Service
Web Service是一组分布式应用模型的规范, 它定义了网络传输协议和访问服务的数据格式。该模型隐藏了技术的实现细节,旨在提供松散耦合、跨平台、跨语言的服务组件。
图四:Web Service架构图[4]
图四描述了SOAP协议实现的Web Service模型(本节讨论都以SOAP协议实现为准),首先客户端通过UDDI(发现整合平台)找到对应的Web Service,下载对应WSDL文件,生成本地代理类,继而请求Web Service服务。UDDI的概念一直被弱化,因为客户端一般都知道Web Service的地址。
接下来我们使用Web Service来实现本文的用例。本节使用的Web Service第三方库是CXF(http://cxf.apache.org/),规范使用的是JAX-WS。
- 接口类IAnimalService.java
import javax.jws.WebService;
@WebService
public interface IAnimalService {
public String getMonkeyName();
}
- 实现类AnimalServiceImp.java
import javax.jws.WebService;
@WebService(endpointInterface = "IAnimalService", serviceName = "AnimalService")
public class AnimalServiceImp implements IAnimalService {
@Override
public String getMonkeyName() {
return "I'm Jacky";
}
}
- 服务端Server.java
import javax.xml.ws.Endpoint;
IAnimalService serviceInstance = new AnimalServiceImp();
final String address = "http://localhost:9000/animalService"; //服务名称
Endpoint.publish(address, serviceInstance); //绑定并发布服务
- 客户端Client.java(无需手动下载WSDL文件,动态调用Web Service)
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInInterceptors().add(new LoggingInInterceptor()); //日志输入拦截器
factory.getOutInterceptors().add(new LoggingOutInterceptor()); //日志输出拦截器
factory.setServiceClass(IAnimalService.class);
factory.setAddress("http://localhost:9000/animalService");
IAnimalService client = (IAnimalService) factory.create();
System.out.println(client.getMonkeyName());
使用Web Service的利弊:
- 优势:跨语言、跨平台,SOA思想的实现;安全性高;可以用来兼容legacy系统的功能
- 劣势:性能相对差,不支持两阶段事务
Web Service使用的范围非常广,比如SalesForces(http:// www.salesforce.com),世界上最大的在线CRM提供商, 它的产品卖给使用不同技术平台的企业(.Net, Java, Ruby),SalesForces云计算的数据接口是以Web Service的方式发布的[8];Web Service另一个适用场景是,企业很多时候会有新老系统做数据交互,而新老系统使用的技术平台不一致,Web Service是个不错的选择。
引用
[1] http://www.tcs.uj.edu.pl/~krawczyk/programowanie_w_sieci_internet/wyklad/wyklad5-rmi/rmi/slajd1.html
[2] http://www.ibm.com/developerworks/cn/websphere/library/bestpractices/enterprise_javabean.html
[3] http://publib.boulder.ibm.com/infocenter/radhelp/v7r5/topic/com.ibm.jee5.doc/topics/cejb3vejb21.html
[4] http://www.emeraldinsight.com/journals.htm?articleid=862014&show=abstract
分享到:
相关推荐
利用rmi远程调用,实现客户端和服务器端的通讯,而不是采用socket的编程方法。
在Spring框架中,注解是实现服务暴露和远程调用(Remoting)的重要方式,它极大地简化了配置,提高了代码的可读性和可维护性。本文将深入探讨如何使用注解来实现Spring中的Remoting服务。 首先,让我们理解什么是...
Direct Web Remoting (DWR) 是一个开源的Java库,它允许Web应用程序在客户端的JavaScript和服务器端的Java之间进行直接的、异步的通信,实现了Web应用中的Ajax功能。DWR通过自动化处理JavaScript和Java之间的类型...
1. 远程方法调用(RMI):RMI是Java提供的分布式对象通信模型,它允许一个虚拟机上的对象通过网络调用另一虚拟机上的对象的方法。RMI体系结构主要由客户端、RMI远程引用层和传输层组成。RMI通过序列化Java对象并在...
1. RMI(Remote Method Invocation):RMI是Java平台上的标准远程调用协议,Spring Remoting对RMI的支持非常全面。开发者可以通过实现Remote接口,创建远程服务,并通过RmiRegistry注册服务,客户端则通过...
Hessian Remoting是一种基于HTTP的远程调用技术,它利用了高效的二进制序列化协议,使得Java对象能够在网络间高效地传输。标题中的“Hessian remoting on http”指的是在HTTP协议上运行的Hessian远程调用机制,这是...
詹金斯远程处理层 Jenkins remoting 是一个可执行的 JAR,它在自动化服务器中实现通信层。 它用于主 <=> 代理和主 <=> CLI 通信。 通常,该库包含引导程序代码,用于将单独的 JVM 桥接到单个半共享空间中。 ...
- 在启动远程服务时,同时启用 Jrebel 热部署和远程 Debug 参数,如:`java -agentpath:./lib/libjrebel64.so -Drebel.remoting_plugin=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=...
1. **Remoting**: 允许Flex客户端与Java服务直接交互,实现远程方法调用(RPC)。 2. **Messaging**: 支持发布/订阅消息模式,实现事件驱动的通信。 3. **LiveCycle Data Services (LCDS)**: 提供更高级的功能,如...
尽管在现代Java企业应用中,Spring框架下的远程调用技术(如Spring Remoting)和各种微服务架构(如Spring Cloud)逐渐流行,但Java RMI因其简单和直接性,在某些场景下依然有着不可替代的作用。
Java类交互的远程服务器端Ajax开源框架DWR(Direct Web Remoting)是一个强大的工具,它允许Web应用程序在浏览器和服务器之间进行动态、实时的通信,无需刷新页面即可更新内容。DWR简化了JavaScript与Java之间的通信...
文章还提及了使用.NET Remoting技术相较于传统技术(如DCOM、CORBA和Java RMI)的优越性。如DCOM在互联网环境中的效率问题,以及.NET Remoting支持的跨平台和易于配置的优势。 最后,文章通过具体示例(图1和图2)...
此外,.NET Remoting还支持与其他平台和技术栈的无缝集成,例如可以与Java、CORBA等技术进行通信。 #### 六、性能优化与最佳实践 - **性能优化技巧**:本书提供了许多关于如何提高.NET Remoting应用性能的方法,...
总结来说,Dubbo作为一个强大的远程调用框架,不仅简化了分布式系统的开发,还提供了丰富的功能来保证服务的高可用性和高性能。通过理解并熟练掌握Dubbo的原理和使用,开发者可以更好地构建和管理大规模的分布式系统...
Jenkins 使用 Remoting 库(通常为agent.jar或remoting.jar)实现控制器与代理之间的通信,该库允许代理从控制器加载类和类加载器资源,以便从控制器发送的 Java 对象(构建步骤等)可以在代理上执行。 Jenkins ...
DWR(Direct Web Remoting)是一个web远程调用框架,利用这个框架可以让AJAX变得很简单,通过DWR可以在客户端通过JavaScript直接调用服务器的Java方法并返回值给JavaScript,整个过程就好像通过本地客户端调用一样,...
1. **定义远程接口**:在RMI中,你需要定义一个继承自`java.rmi.Remote`的接口,并在方法声明上抛出`java.rmi.RemoteException`。例如: ```java public interface MyRemoteService extends Remote { String ...
然而,由于依赖Java序列化,这可能会导致一些性能上的开销,而且不适用于跨语言通信。因此,选择HttpInvoker还是Hessian,取决于具体的应用场景和性能需求。 总的来说,Spring的HttpInvoker提供了一种灵活的远程...