精讲分布式系统核心:实战:基于JavaRMI实现分布式对象通信
Posted king哥Java架构
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了精讲分布式系统核心:实战:基于JavaRMI实现分布式对象通信相关的知识,希望对你有一定的参考价值。
实战:基于Java RMI实现分布式对象通信
本节,我们将演示如何基于Java RMI来实现分布式对象通信。
示例概述
RMI应用程序通常包含两个单独的程序,即服务器和客户端。一个典型的服务器程序创建一些远程对象,使对这些对象的引用可访问,并等待客户端调用这些对象上的方法。典型的客户端程序获取对服务器上一个或多个远程对象的远程引用,然后在其上调用方法。RMI提供了一种机制,服务器和客户端通过该机制进行通信并来回传递信息。有时将这样的应用程序称为分布式对象应用程序。
分布式对象应用程序需要执行以下操作。
-
找到远程对象。应用程序可以使用各种机制来获取对远程对象的引用。例如,应用程序可以使用RMI的简单命名功能、RMI注册表注册其远程对象,或者应用程序可以传递和返回远程对象引用,作为其他远程调用的一部分。
-
与远程对象通信。远程对象之间的通信详细信息由RMI处理。对于程序员而言,远程通信看起来类似于常规的Java方法调用。
-
为传递的对象加载类定义。因为RMI使对象能够来回传递,所以它提供了用于加载对象的类定义以及传输对象的数据的机制。
下图描绘了使用RMI注册表获取对远程对象的引用的RMI分布式应用程序。服务器调用注册表以将名称与远程对象关联(或绑定)。客户端在服务器的注册表中通过其名称查找远程对象,然后在其上调用一个方法。该图还显示RMI系统使用现有的Web服务器在需要时从服务器到客户端以及从客户端到服务器为对象加载类定义。
在本例中,同样会有一个服务器和客户端,分别为RmiEchoServer和RmiEchoClient。顾名思义,本例用于演示Echo协议的内容。当RmiEchoServer接收到RmiEchoClient发起的请求时,RmiEchoServer会将请求的内容原样返回给RmiEchoClient。
编写RMI服务器
RMI服务器代码由一个接口Message和一个类RmiEchoServer组成。
接口定义了可以从客户端调用的方法。本质上,该接口定义了远程对象的客户端视图,该类提供了实现。
1.设计远程接口Message
Message接口提供了客户端和服务器之间的连接。代码如下。
package com.waylau.java.demo.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Message extends Remote {
/*** 发送消息
*
* @param msg 消息
* @return 响应内容
* @throws RemoteException
*/
String echoMessage(String msg) throws RemoteException;
}
Message接口只有一个方法echoMessage。当调用该方法时,需要返回响应内容。
2.实现远程接口Message
以下是实现Message接口的RmiEchoServer类。代码如下。
package com.waylau.java.demo.rmi;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class RmiEchoServer implements Message {
@Override
public String echoMessage(String msg) throws RemoteException {
System.out.println("Client -> Server: " + msg);
return msg;
}
public static void main(String args[]) {
try {
int port = ServerConstant.PORT;
RmiEchoServer obj = new RmiEchoServer();
Message stub = (Message) UnicastRemoteObject.exportObject(obj, 0);
// 绑定远程对象的stub到注册中心
Registry registry =
LocateRegistry.getRegistry(port); // 如不指定端口,默认使用1099
registry.rebind(ServerConstant.REGISTRY_NAME, stub);
System.err.println("RmiEchoServer started on port: " + port);
} catch (Exception e) {
System.err.println("RmiEchoServer exception: " + e.toString());
e.printStackTrace();
}
}
}
RmiEchoServer类的方法echoMessage来自Message接口的定义,该方法的具体实现如下。
- 在控制台输出接收到的内容msg。·而后将接收到的内容msg原样返回。
RmiEchoServer类的方法main是服务器应用的主入口。我们重点看下main方法的具体实现。
UnicastRemoteObject.exportObject静态方法用于导出提供的远程对象,以便它可以从远程客户端接收其远程方法的调用。第二个参数int指定用于侦听该对象的传入远程调用请求的TCP端口。通常使用零值,该值指定使用匿名端口。然后,实际端口将在运行时由RMI或基础操作系统选择。但是,也可以使用非零值来指定用于侦听的特定端口。成功导出exportObject调用后,RmiEchoServer远程对象已准备就绪,可以处理传入的远程调用。
exportObject方法返回导出的远程对象的存根。请注意,变量存根的类型必须是Message,而不是RmiEchoServer,因为远程对象的存根仅实现导出的远程对象实现的远程接口。
如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
[Java架构群]
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的JAVA交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
exportObject方法声明它可以引发RemoteException,这是一个经过检查的异常类型。main方法使用其try/catch块处理此异常。如果未通过这种方式处理异常,则必须在main方法的throws子句中声明RemoteException。
在客户端可以在远程对象上调用方法之前,客户端必须首先获取对该远程对象的引用。可以通过在程序中获得任何其他对象引用的方式来获得引用,例如通过将引用作为方法返回值的一部分或包含此类引用的数据结构的一部分来获取。
系统提供一种特殊类型的远程对象RMI注册表,用于查找对其他远程对象的引用。RMI注册表是一个简单的远程对象命名服务,使客户端能够通过名称获得对远程对象的引用。注册表通常仅用于查找RMI客户端需要使用的第一个远程对象,然后,第一个远程对象可能会为查找其他对象提供支持。
java.rmi.registry.Registry远程接口是用于绑定(或注册)和在注册表中查找远程对象的API。java.rmi.registry.LocateRegistry类提供了静态方法,用于合成对特定网络地址(主机和端口)上注册表的远程引用。
这些方法将创建包含指定网络地址的远程引用对象,而不执行任何远程通信。在本例中,我们通过port来指定特定的端口号。LocateRegistry还提供了用于在当前Java虚拟机中创建新注册表的静态方法,尽管本示例未使用这些方法。在本地主机上的RMI注册表中注册了远程对象后,任何主机上的客户端都可以按名称(即代码中的
ServerConstant.REGISTRY_NAME)查找远程对象,获取其引用,然后在该对象上调用远程方法。注册表可以由主机上运行的所有服务器共享,或者单个服务器进程可以创建和使用其自己的注册表。
为了方便管理引用中的常量(比如主机地址、端口号、远程名称、消息等),我们将常量定义在ServerConstant中。代码如下。
package com.waylau.java.demo.rmi;
public interface ServerConstant {
String HOST =
"localhost";
int PORT = 1099;
String REGISTRY_NAME = "Echo Server Message";
String HELLO_WORLD =
"Hello World! Welcome to waylau.com!";
}
编写RMI客户端
RMI客户端RmiEchoClient的代码如下。
package com.waylau.java.demo.rmi;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RmiEchoClient {
public static void main(String[] args) {
try {
String host = ServerConstant.HOST;
int port = ServerConstant.PORT;
Registry registry = LocateRegistry.getRegistry(host, port);
Message stub =(Message) registry.lookup(ServerConstant.REGISTRY_NAME);
String response = stub.echoMessage(ServerConstant.HELLO_WORLD);
System.out.println("Server -> Client: " + response);
} catch (Exception e) {
System.err.println("RmiEchoClient exception: " + e.toString());
e.printStackTrace();
}
}
}
客户端将使用RmiEchoServer用来绑定其远程对象的相同名称来构建用于查找Message远程对象的名称。此外,客户端使用
LocateRegistry.getRegistry API来合成对服务器上注册表的远程引用,其参数分别是服务器的名称及端口号。然后,客户端在注册表上调用lookup方法,以在服务器的注册表中按名称(即代码中的ServerConstant.REGISTRY_NAME)查找远程对象。stub即为远程对象的存根,通过调用stub的echoMessage方法,即实现调用远程对象上的方法。其中,ServerConstant.HELLO_WORLD为echoMessage方法参数,也就是传递给远程对象的消息。
运行
在启动应用之前,需要先启动RMI注册表。RMI注册表是一种简单的服务器引导程序命名工具,它使远程客户端可以获取对初始远程对象的引用。可以使用rmiregistry命令启动。在执行rmiregistry之前,确保该命令所执行的目录下包含了应用的编译文件,即.class文件。在Maven管理的应用中,编辑文件通常是在应用的“target”目录下。
以下是在Windows系统上启动执行rmiregistry命令的方式。
start rmiregistry 1099
其中,1099是RMI服务器所绑定的端口号。该命令不会产生任何输出,通常在后台运行。效果如下图所示。
当RMI注册表启动之后,就可以启动RMI服务器和客户端程序了,观察控制台输出内容。
可以看到,RMI服务器输出内容如下。
RmiEchoServer started on port: 1099
Client -> Server: Hello World! Welcome to waylau.com!
RMI客户端输出内容如下。
Server -> Client: Hello World! Welcome to waylau.com!
本节示例,可以在java-rmi项目下找到。
本章小结
本章介绍了基于对象的分布式系统架构及常用的分布式对象系统,包括微软DCOM(COM+)、CORBA、Java RMI。
同时我们也认识到分布式对象系统有其优点也有其缺点,在实际应用中,要根据实际的场景来考虑使用哪种分布式对象系统技术。比如,在特定的平台,我们可以使用与该平台所对应的分布式对象系统技术。
比如针对微软平台,我们可以使用DCOM(COM+)技术;在Java平台,我们可以使用Java RMI技术。如果平台具有多样性,或者没有办法统一到相同的技术上来,那么可以选择使用跨平台的分布式技术,比如CORBA。
写在最后
最近我整理了整套《JAVA核心知识点总结》,说实话,作为一 名 Java 程序员,不论你需不需要面试都应该好好看下这份资料。拿到手总是不亏的~我的不少粉丝也因此拿到腾讯字节快手offer,点击下面图片↓直达领取
好了,以上就是本文的全部内容了,如果觉得有收获,记得三连,我们下期再见。
以上是关于精讲分布式系统核心:实战:基于JavaRMI实现分布式对象通信的主要内容,如果未能解决你的问题,请参考以下文章
深度学习核心技术精讲100篇(八十一)-NLP预训练模型ERNIE实战应用案例
深度学习核心技术精讲100篇(五十九)-多业务融合推荐策略实战应用
深度学习核心技术精讲100篇(五十一)-Spark平台下基于LDA的k-means算法实现
第三百七十节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索结果分页