1. Overview
1.概述
When two JVMs need to communicate, Java RMI is one option we have to make that happen. In this article, we’ll bootstrap a simple example showcasing Java RMI technology.
当两个JVM需要进行通信时,Java RMI是我们实现这一目的的一个选择。在这篇文章中,我们将引导一个简单的例子,展示Java RMI技术。
2. Creating the Server
2.创建服务器
There are two steps needed to create an RMI server:
创建一个RMI服务器需要两个步骤。
- Create an interface defining the client/server contract.
- Create an implementation of that interface.
2.1. Defining the Contract
2.1.合同的定义
First of all, let’s create the interface for the remote object. This interface extends the java.rmi.Remote marker interface.
首先,让我们为远程对象创建一个接口。这个接口扩展了java.rmi.Remotemarker接口。
In addition, each method declared in the interface throws the java.rmi.RemoteException:
此外,接口中声明的每个方法都会抛出java.rmi.RemoteException。
public interface MessengerService extends Remote {
String sendMessage(String clientMessage) throws RemoteException;
}
Note, though, that RMI supports the full Java specification for method signatures, as long as the Java types implement java.io.Serializable.
不过请注意,只要Java类型实现了java.io.Serializable,RMI就支持方法签名的全部Java规范。
We’ll see in future sections, how both the client and the server will use this interface.
我们将在未来的章节中看到,客户端和服务器将如何使用这个接口。
For the server, we’ll create the implementation, often referred to as the Remote Object.
对于服务器,我们将创建实现,通常被称为Remote Object。
For the client, the RMI library will dynamically create an implementation called a Stub.
对于客户端,RMI库将动态地创建一个名为Stub的实现。。
2.2. Implementation
2.2.实施
Furthermore, let’s implement the remote interface, again called the Remote Object:
此外,让我们实现远程接口,再次称为远程对象。
public class MessengerServiceImpl implements MessengerService {
@Override
public String sendMessage(String clientMessage) {
return "Client Message".equals(clientMessage) ? "Server Message" : null;
}
public String unexposedMethod() { /* code */ }
}
Notice, that we’ve left off the throws RemoteException clause from the method definition.
请注意,我们在方法定义中删除了throws RemoteException子句。
It’d be unusual for our remote object to throw a RemoteException since this exception is typically reserved for the RMI library to raise communication errors to the client.
如果我们的远程对象抛出一个RemoteException,那是不正常的,因为这个异常通常是为RMI库保留的,用于向客户端提出通信错误。
Leaving it out also has the benefit of keeping our implementation RMI-agnostic.
忽略它还有一个好处,就是使我们的实现与RMI无关。
Also, any additional methods defined in the remote object, but not in the interface, remain invisible for the client.
此外,在远程对象中定义的任何额外方法,但不在接口中,对客户端来说仍然是不可见的。
3. Registering the Service
3.注册服务
Once we create the remote implementation, we need to bind the remote object to an RMI registry.
一旦我们创建了远程实现,我们需要将远程对象绑定到RMI注册表。
3.1. Creating a Stub
3.1.创建一个存根
First, we need to create a stub of our remote object:
首先,我们需要创建一个远程对象的存根。
MessengerService server = new MessengerServiceImpl();
MessengerService stub = (MessengerService) UnicastRemoteObject
.exportObject((MessengerService) server, 0);
We use the static UnicastRemoteObject.exportObject method to create our stub implementation. The stub is what does the magic of communicating with the server over the underlying RMI protocol.
我们使用静态的UnicastRemoteObject.exportObject方法来创建我们的存根实现。存根是通过底层RMI协议与服务器进行通信的神奇工具。
The first argument to exportObject is the remote server object.
exportObject的第一个参数是远程服务器对象。
The second argument is the port that exportObject uses for exporting the remote object to the registry.
第二个参数是exportObject用于将远程对象导出到注册表的端口。
Giving a value of zero indicates that we don’t care which port exportObject uses, which is typical and so chosen dynamically.
给出一个零的值表示我们不关心哪个端口exportObject使用,这是典型的,所以是动态选择。
Unfortunately, the exportObject() method without a port number is deprecated.
不幸的是,没有端口号的exportObject()方法已被废弃。
3.2. Creating a Registry
3.2.创建一个注册表
We can stand up a registry local to our server or as a separate stand-alone service.
我们可以在我们的服务器上建立一个本地的注册表,或者作为一个单独的独立服务。
For simplicity, we’ll create one that is local to our server:
为了简单起见,我们将创建一个本地的服务器。
Registry registry = LocateRegistry.createRegistry(1099);
This creates a registry to which stubs can be bound by servers and discovered by clients.
这创建了一个注册表,服务器可以绑定存根,客户可以发现存根。
Also, we’ve used the createRegistry method, since we are creating the registry local to the server.
另外,我们使用了createRegistry方法,因为我们是在服务器本地创建注册表。
By default, an RMI registry runs on port 1099. Rather, a different port can also be specified in the createRegistry factory method.
默认情况下,RMI注册表在端口1099上运行。相反,也可以在createRegistryfactory方法中指定一个不同的端口。
But in the stand-alone case, we’d call getRegistry, passing the hostname and port number as parameters.
但在独立的情况下,我们会调用getRegistry,将主机名和端口号作为参数传递。
3.3. Binding the Stub
3.3.绑定存根
Consequently, let’s bind our stub to the registry. An RMI registry is a naming facility like JNDI etc. We can follow a similar pattern here, binding our stub to a unique key:
因此,让我们把我们的存根绑定到注册表上。RMI注册表是一个类似于JNDI等的命名设施。在这里我们可以遵循类似的模式,将我们的存根绑定到一个唯一的键上。
registry.rebind("MessengerService", stub);
As a result, the remote object is now available to any client that can locate the registry.
因此,远程对象现在可以被任何能够找到注册表的客户端使用。
4. Creating the Client
4.创建客户
Finally, let’s write the client to invoke the remote methods.
最后,让我们编写客户端来调用远程方法。
To do this, we’ll first locate the RMI registry. In addition, we’ll look up the remote object stub using the bounded unique key.
要做到这一点,我们首先要找到RMI注册表。此外,我们将使用绑定的唯一键来查找远程对象的存根。
And finally, we’ll invoke the sendMessage method:
最后,我们将调用sendMessage方法。
Registry registry = LocateRegistry.getRegistry();
MessengerService server = (MessengerService) registry
.lookup("MessengerService");
String responseMessage = server.sendMessage("Client Message");
String expectedMessage = "Server Message";
assertEquals(expectedMessage, responseMessage);
Because we’re running the RMI registry on the local machine and default port 1099, we don’t pass any parameters to getRegistry.
因为我们在本地机器和默认端口1099上运行RMI注册表,所以我们不向getRegistry传递任何参数。
Indeed, if the registry is rather on a different host or different port, we can supply these parameters.
事实上,如果注册表是在不同的主机或不同的端口,我们可以提供这些参数。
Once we lookup the stub object using the registry, we can invoke the methods on the remote server.
一旦我们使用注册表查找存根对象,我们就可以在远程服务器上调用这些方法。
5. Conclusion
5.结论
In this tutorial, we got a brief introduction to Java RMI and how it can be the foundation for client-server applications. Stay tuned for additional posts about some of RMI’s unique features!
在本教程中,我们简单介绍了Java RMI,以及它如何成为客户端-服务器应用程序的基础。请继续关注有关RMI的一些独特功能的其他文章。
The source code of this tutorial can be found over on GitHub.
本教程的源代码可以在GitHub上找到。