It would be easiest if all components were executed on the same heap space of the same Java virtual machine on the same computer, but in practice we often face not such a single situation. If the client is just a device that can execute Java what to do? What if for security reasons only programs on the server can access the database?
We know that in most cases, method calls occur between two objects on the same heap. What if you want to call methods on objects on different machines?
Usually, we get information from one computer to another computer through the input/output stream of the socket, open the socket connection of the other computer, and then obtain the outputStream to write the data. But if we want to call another computer On your computer, what are the methods of objects on another Java virtual machine? Of course, we can define and design the communication protocol ourselves to call, and then transmit the execution results back through Socket, and it can also be like calling methods on the local machine, that is, if we want to call remote objects (such as other heaps) ), but it should be like a normal call.
This is what RMI brings to us.
The design of remote procedure calls
There are 4 things to create: server, client, server auxiliary facilities and client auxiliary facilities.
1. Create client and server applications. The server application is a remote service, an object with methods that the client will call.
2. Create client and server side helpers. They will handle all the underlying network input/output details of the client and server, making the client and program appear to be processing local calls.
Tasks of Auxiliary Facilities Auxiliary facilities are objects that actually perform communication. They make the client feel as if it is calling a local object. The client object looks like it is calling a remote method, but in fact it is just calling a method. A proxy that handles Socket and streaming details locally. On the server side, the server's auxiliary facilities will connect the requests from the client facilities through the socket, parse the packaged information, and then call the real service, so for the service object this is After calling the auxiliary facility from the local service, it gets the return value, wraps it and sends it back (via the socket's output stream) to the client's auxiliary facility. The client's auxiliary facility unpacks the information and transmits it to the client object.
The process of calling a method
1. The client object calls doBigThing() on the auxiliary facility object
2. The client's auxiliary facilities package the call information and send it to the server's auxiliary facilities through the network.
3. The server-side auxiliary facility decodes the information from the client-side auxiliary facility and uses it to call the real service.
The diagram describing this process is as follows:
JavaRMI provides client-side and server-side helper objects
In Java, RMI has helped us create client-side and server-side auxiliary facilities. It also knows how to make the client-side auxiliary facilities look like real services. In other words, RMI knows how to provide the same methods for client calls. .
In addition, RMI provides all the infrastructure required for execution, including service query and auxiliary facilities that allow clients to find and obtain clients (real service agents).
When using RMI, there is no need to write any network or input/output programs. The client's call to a remote method is the same as a method call on the same Java virtual machine.
General calls are a little different from RMI calls. Although to the client, this method call looks local, but the client auxiliary facility will make the call through the network. This call will eventually involve sockets and streams. It starts as a local call, and the agent will convert it to a remote one. How the intermediate information is sent from the Java virtual machine to the Java virtual machine depends on the protocol used by the auxiliary facility object.
When using RMI, you must decide on the protocol: JRMP or IIOP. JRMP is the native protocol of RMI. It is designed for remote calls between Java. On the other hand, IIOP is produced for CORBA, which allows us to call Java. For objects or other types of remote methods, CORBA is usually more troublesome than RMI, because if both ends are not Java, a bunch of terrible translation and conversation operations will occur.
We only care about Java-to-Java operations, so we will use a fairly simple RMI.
In RMI, the client-side auxiliary facilities are called stubs, and the server-side auxiliary facilities are called skeletons.
How to create a remote service
1.Create Remote interface
The remote interface defines the methods that the client can call remotely. It is a polymorphic class as a service. Both the stub and the service will implement this interface.
2. Implement the Remote interface
This is the actual executing class. It implements the methods defined on the interface. It is the object that the client will call.
3. Use rmic to generate stub and skeleton
Both the client and the server have helpers. We don't need to create these classes or generate the source code of these classes. This will be automatically handled when executing the rmic tool attached to the JDK.
4. Start RMIregistry (rmiregistry)
rmiregistry is like a phone book, the user will get the proxy (the client's stub/helper object) from here
5. Start the remote service
The service object must start executing. The class that implements the service will start the service instance and register it with RMIRegistry. Only after registration can it serve the user.
Server code
Define interface
/**
*
*MyRemote.java
*
* Function: TODO
* Class name: MyRemote.java
*
* ver. Updated character holder and new content.
*────────────────────────────────────────────
* V1.00 2013-3-19 First version of module Su Ruo
*
* Copyright (c) 2013 dennisit corporation All Rights Reserved.
*
* Email:<a href="mailto:[email protected]">Send email</a>
*
*
* Remote is a marked interface, which means there are no methods. However, it has special meaning for RMI, so this rule must be followed.
* Note that extends is used here, and interfaces can inherit other interfaces.
*
*/
public interface MyRemote extends Remote{
/**
* The remote interface defines the methods that the client can call remotely. It is a polymorphic class as a service. That is to say, the client will
* Mobilize the stub that implements this interface, and because this stub will perform network and input/output work, various things may happen
* Problem, the client handles or declares exceptions to recognize this type of risk. If the method declares an exception in the interface, call the method
* All procedures must handle or redeclare this exception.
*
* The parameters and return values of remote methods must be primitive or serializable. Any parameters of remote methods will be
* The package is transmitted through the network, and when it is completed through serialization, the return value is the same. Therefore, if a custom type is used
*, it must be serialized
* @return
* @throws RemoteException
* All methods in the interface must declare RemoteException
*/
public String sayHello() throws RemoteException;
}
/**
*
*MyRemoteImpl.java
*
* Function: TODO
* Class name: MyRemoteImpl.java
*
* ver. Updated character holder and new content.
*────────────────────────────────────────────
* V1.00 2013-3-19 First version of module Su Ruo
*
* Copyright (c) 2013 dennisit corporation All Rights Reserved.
*
* Email:<a href="mailto:[email protected]">Send email</a>
*
* In order to become a remote service object, the object must have remote-related functions. The simplest method is to inherit UnicastRemoteObject
* (from java.rmi.server) to let this parent class handle the work
*
*/
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
/**
* The constructor of the parent class declares an exception, so you must write the constructor because it means that your constructor will call risky program code
*
* UnicastRemoteObject has a small problem, its constructor throws RemoteException. The only way to handle it is
* Declare a constructor for your own implementation so that there is a place to declare RemoteException. When the class is initialized, the parent class
* The constructor will definitely be called. If the constructor of the parent class throws an exception, the custom constructor we must also declare will throw an exception.
* @throws RemoteException
*/
protected MyRemoteImpl() throws RemoteException {
}
/**
* Implement all methods of the outgoing interface, but do not need to declare RemoteException
*/
@Override
public String sayHello(){
return "server says, rmi hello world!";
}
public static void main(String[] args) {
try {
/**
* We already have a remote service, and we must allow remote users to access it. This can be done by initializing it and adding it to the RMI Registry.
* (It must be running, otherwise this program will fail). When registering an object, the RMI system will add the stub to the registry,
* Because this is what the client needs. Use rebind() of java.rmi.Naming to register the service
*/
MyRemote service = new MyRemoteImpl();
/**
* Create a remote object, and then use static Naming.rebind() to create an association. The registered name will be provided for client query
*/
Naming.rebind("Remote Hello World", service);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void exec(){
try {
/**
* The client must obtain the stub object because the client must call its method. This depends on the RMI registry. The client will query the phone like
* Search the same directory to find services with matching names.
* The client queries RMIRegistry and returns the stub object
* Naming.lookup("rmi://127.0.0.1/Remote Hello World");
* Parameter description
* rmi://127.0.0.1/Remote Hello World
* 127.0.0.1 represents the host name or host IP address
* Remote Hello World must be the same as the registered name
*
*/
MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1/Remote Hello World");
String tmp = service.sayHello();
System.out.println(tmp);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new MyRemoteClient().exec();
}
}
The rmic tool that comes with the JDK will generate two classes, stub and skeleton, based on the service implementation. It will add _Stub or _Skeleton after the remote implementation name according to the naming rules. rmic has several options, including not generating skeletons, observing the source code of the generated classes, or using IIOP as the communication protocol. The generated classes will be placed in the current directory. Remember that rmic must be able to find the implemented classes. Therefore, you may need to execute rmic from the directory where the implementation is located (in practice, you may need to consider the package directory structure and complete name, for simplicity, the package is not used here)
Call the command line to start rmiregistry. Make sure you start it from a directory that can access the class. The easiest way is to run it from the directory of the class.
The running screenshot is as follows
Notice:
The client uses the interface to call the method on the stub. The client's Java virtual machine must have a stub class, but the client will not reference the stub class in the program code. The client always operates the real remote object through the interface.
The server must have stubs and skeletons, as well as service and remote interfaces. It requires the stub class because the stub will be replaced by the real service connected to RMIRegistry.
Common mistakes when using RMI:
1. Forgot to start rmiregistry before starting the remote service (rmiregistry must be started before using Naming.rebind() to register the service)
2. Forgot to make the parameters and return types serializable (it will not be detected during compilation and will only be discovered during execution)
3. Forget to hand over the stub class to the client
RMI is very suitable for writing and running remote services, but we will not use RMI alone to execute website services. For large-scale enterprise-level applications, we need more and better functions. Such as transaction management, large-scale concurrent processing, and security And database management, etc. This requires the use of EnterpriseApplicationServer.
The JavaEE server includes a Web server and an Enterprise JavaBeans (EJB) server. The EJB server acts between RMI calls and the service layer.
Application of RMI in JINI
Jini also uses RMI (although other protocols can also be used), but it has several more key functions.
1. Adaptive discovery
2. Self-healing networks
The RMI client must first obtain the address and name of the remote service. The client's query program code must contain the IP address or host name of the remote service (because RMIRegistry is on it) and the name registered by the service.
But when using JINI, users only need to know one thing, the interface implemented by the service! That's it.
Jini uses lookupservice, which is stronger and more adaptable than RMIRegistry. Because Jini will automatically advertise on the network. When the query service comes online, it will use IP multicast technology to send information to the entire network. Not only that, if The client comes online after the query service has been broadcast, and the client can also send messages to the entire network to inquire.
When the service comes online, it will dynamically explore the JINI query service on the network and apply for registration. When registering, the service will send a serialized object to the query service. This object can be the stub of the RMI remote service or the driver of the network device. , or even the service itself that can be executed on the client. And what is registered is the implemented interface. Not the name.
How adaptive exploration works
1.Jini query service starts on the network and uses IP multicast technology to promote itself
2. Another Jini service that has been started will seek to register with the newly started query service. It registers the function rather than the name, that is, the interface implemented, and then sends the serialized object to the query service.
3. Internet customers want to get something to implement ScientificCalculator, but they don’t know where to find it, so they ask the query service
4. Query service responds to query results
Operation of a self-healing network
1. A certain Jini service requires registration, and the query service will give a lease. Newly registered services must renew the lease regularly. Otherwise, the query service will assume that the service is offline. The query service will strive to present an accurate and complete available service network status.
2. The service is offline due to shutdown, so the lease is not updated and the query service is kicked off.