Architecture Design: Inter-system Communication (8) — Communication Management and RMI part 1. As mentioned earlier, the RMI framework in the JDK has undergone major changes in JDK1.1, JDK1.2, JDK1.5, and JDK1.6+. The RMI principles discussed below are based on JDK1.6+.
3. How JAVA RMI works
From the above two sets of code, we have an idea of how the RMI framework is used. Let’s take a look at the fundamentals of RMI. I read a lot of RMI materials on the Internet, basically the code is a copy (even variable names, syntax errors are the same), and there are many materials misleading readers. The following figure illustrates several core concepts of the overall RMI framework:
3-1 relationship between Registry and Stub and Skeleton
-
It is important to note that before the RMI Client performs a formal RMI call, it must locate the RMI registration information to be invoked in the RMI registry via LocateRegistry or Naming. After finding the RMI transaction registration information, the Client obtains the RMI Remote Service Stub information from the RMI registry. After this process is successful, the RMI Client can begin the formal invocation process.
-
In addition, it should be noted that in the formal call process of RMI Client, the RMI Client does not directly access Remote Service. Instead, the Stub obtained by the Client accesses the proxy Skeleton of Remote Service as the proxy of RMI Client. The order is shown above. That is, the actual request calls are made between stub-skeleton.
-
Registry is not involved in the specific stub-skeleton invocation, but is only responsible for recording which Stub is used by “which service name” and presenting it to the Remote Client when asked about it (an error is reported if it does not exist).
-
To verify the call described above, we write a simple program to verify the relationship between Registry, Stub, and Skeleton. In the following experimental program, Registry, Stub, and Skeleton are distributed on three separate JVMS. Then, when the Remote Client successfully queries the Stub of the Remote Service on Registry, the Registry JVM is terminated. Verify that the stub-skeleton interaction is not affected.
Here is a standalone Registry program (note that it is only responsible for registration, query) :
package testRMI; import java.rmi.registry.LocateRegistry; /** * is an RMI registry repository, not responsible for anything else. <br> * After the warehouse is created, lock the WAITOBJECT, * @author yinwenjie */ public class SingleRegistry {private static final Object WAITOBJECT = new Object(); public static void main(String[] args) throws Exception { LocateRegistry.createRegistry(1099); synchronized (WAITOBJECT) { WAITOBJECT.wait(); }}} 1234567891011121314151617181920Copy the code
The RMI service interface is defined as RemoteServiceInterface. The method definition in the RemoteServiceInterface is the same as in the previous section. I won’t post the code here to save space) :
package testRMI; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.List; import testRMI.entity.UserInfo; Public interface RemoteServiceInterface extends Remote {/** * public List<UserInfo> queryAllUserinfo() throws RemoteException; } 1234567891011121314Copy the code
The following code is another running JVM that provides registration for the RMI Remote Service. It is, in effect, a concrete service provider. The code is as follows:
package testRMI; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RemoteRegistryUnicastMain { public static void main(String[] args) throws Exception { /* * We through LocateRegistry the get method, for the existence of a remote JVMS RMI Registry * * / Registry Registry. = LocateRegistry getRegistry (" 192.168.61.1 ", 1099); // The following is the Stub for RMI Server to bind/rebind to the remote RMI registry. // under jVM-CLASspath of the same remote RMI registry, RemoteUnicastServiceImpl remoteService = new RemoteUnicastServiceImpl(); /* * * The registered repository exists at 192.168.61.1. * When using registry for binding or rebinding, No need to write the full RMI URL * */ registry. Bind ("queryAllUserinfo", remoteService); }} 1234567891011121314151617181920212223242526Copy the code
The last JVM is the RMI Client. There are two things that the RMI Client needs to do: query the Registry Service, obtain the Stub of the Remote Service, and then make formal requests through the Stub agent. In order to ensure the continuity of the experimental program, the Remote Service is continuously requested after the Stub is obtained.
package testRMI; import java.rmi.Naming; import java.util.List; import testRMI.entity.UserInfo; @author yinwenjie ** / public class RemoteClient {private static Object WAITOBJECT = new Object(); Public static void main(String[] args) throws Exception {// The Java name Service technology is used to search RMI interfaces. RemoteServiceInterface remoteServiceInterface = (RemoteServiceInterface) Naming. Lookup (" rmi: / / 192.168.61.1 queryAllUserinfo "); // Ensure continuous requests to see if SingleRegistry is disabled for(;) { List<UserInfo> users = remoteServiceInterface.queryAllUserinfo(); System.out.println("users.size() = " +users.size()); synchronized (RemoteClient.WAITOBJECT) { RemoteClient.WAITOBJECT.wait(1000); }}}} 123456789101112131415161718192021222324252627282930Copy the code
The following is a record of the test case, first we start SingleRegistry and RemoteRegistryUnicastMain respectively. The Remote Service is successfully registered with the Remote Registry and is in working state:
Please note that the screenshot above, RemoteRegistryUnicastMain Application is only one thread at work. This has to do with the thread management of THE RMI-service we will talk about later. Next we start the RMI Client:
Note in particular that the Client queries the RMI Registry to get the remoteServiceInterface instance, which is not a concrete implementation of the remoteServiceInterface interface, but rather the RMI Stub proxy. The endpoint points to the specific Service provision location of the RMI Service, which is provided by the Skeleton on the RMI Service as a proxy.
Then start calling continuously. In this process, we close the RMI Registry service:
Obviously SingleRegistry stopping does not affect the calls made by RemoteClient.
3-2. Remote-service Thread management
The presentation we have seen in the above RemoteRegistryUnicastMain processing request, using the thread pool. This is an improvement to the RMI framework in JDK1.5 to JDK1.6+. Previous versions, including JDK1.5, used new threads to handle requests; After JDK1.6, thread pools were switched to, and the size of thread pools can be adjusted:
-
Sun. Rmi. Transport. TCP. MaxConnectionThreads: the size of the connection pool, the default is unlimited. The unlimited size is definitely a problem, given the Maximum number of files that can be opened by a single process on Linux, the recommended value is 65535 (in production). If the number of threads in the connection pool reaches the maximum at the same time, subsequent Client requests will report an error. Whether the test/development environment sets this value is less important.
-
Sun. Rmi. Transport. TCP. ThreadKeepAliveTime: if you have any spare threads when a thread pool resources, how long will the idle thread resources be cancelled (in milliseconds), the default is 1 minute.
If you are using the Command console on Linux or Windows, you can set the parameters with statements like the following:
java -Dsun.rmi.transport.tcp.maxConnectionThreads=2 -Dsun.rmi.transport.tcp.threadKeepAliveTime=1000 testRMI.RemoteRegistryUnicastMain
If you are using the Eclipse console for execution, you can set this up using the Debug Configuration -> Arguments dialog box, as shown in the following figure:
3-3, Registry and Naming
Both Registry and Naming appear in the code listed in these two articles, and the usage methods of both are also warned in the code comments. Both Registry and Naming can bind/rebind/unbind RMI services, and both can query RMI services using the lookup method.
**Naming is actually an encapsulation of Registry. ** Use the full URL to find the registered service name. We explained the working mode of Naming through the source code of the Lookup method in the Naming class. The following is the source code of the Lookup method in the Naming class:
/** * Returns a reference, a stub, for the remote object associated * with the specified <code>name</code>. * * @param name a name in URL format (without The scheme component) * @return a reference for a remote object formatted URL * @since JDK1.1 */ public static remote lookup(String name) throws NotBoundException, java.net.MalformedURLException, RemoteException { ParsedNamingURL parsed = parseURL(name); Registry registry = getRegistry(parsed); if (parsed.name == null) return registry; return registry.lookup(parsed.name); } 12345678910111213141516171819Copy the code
In this static method, the incoming URL information is first parsed (parseURL(name) private method), and parsed, an object of the ParsedNamingURL private class, is returned. Let’s look at the definition of ParsedNamingURL:
/**
* Simple class to enable multiple URL return values.
*/
private static class ParsedNamingURL {
String host;
int port;
String name;
ParsedNamingURL(String host, int port, String name) {
this.host = host;
this.port = port;
this.name = name;
}
}1234567891011121314
Copy the code
If a URL format problem is found during parsing, an exception will be thrown. If successfully resolved, Registry’s lookup method is called. The complete URL format used by RMI Naming service is:
rmi://host:port/name
Rmi is a fixed format, and host is the address of the service where the RMI Registry is located. Port is the Registry access port. The default port is 1099. Name indicates the registration name of the RMI Remote Service. In the previous code, the RMI Remote Service registration name was queryAllUserinfo.
Naming service only provides the query of RMI service registration information, and you cannot create a registration service using Naming.
3-3 UnicastRemoteObject and Activatable
When I first explained how RMI works in the last article, I mentioned that “this is one of the ways the RMI framework works in JDK1.5”. So there are other working modes for RMI besides this one?
Yes, in JDK1.2, a new way of working with RMI was introduced, written by Ann Wollrath. That is, the real provider of the Remote Service is ported to the JVM where the RMI Registry Registry resides through RMI “activation” mode.
The Remote Service implementation that uses this working mode no longer inherits UnicastRemoteObject, but needs to inherit Activatable (the rest of the business code doesn’t need to change) :
package testRMI; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.rmi.activation.Activatable; import java.rmi.activation.ActivationException; import java.rmi.activation.ActivationID; import java.util.ArrayList; import java.util.List; import testRMI.entity.UserInfo; /** * a concrete implementation of RMI Service. Note here we inherited is Activatable parent * @ author yinwenjie * / public class RemoteActivationServiceImpl extends Activatable implements RemoteServiceInterface {/ * * * this constructor must type, otherwise the client will quote: < br > * Java lang. NoSuchMethodException: testRMI.RemoteActivationServiceImpl.<init>(java.rmi.activation.ActivationID, Java rmi. Abnormal MarshalledObject) < br > * in this method, you can call any Activatable constructor of the parent. Here we call the simplest * @throws RemoteException * @throws ActivationException */ public RemoteActivationServiceImpl(ActivationID id, MarshalledObject<? > data) throws RemoteException, ActivationException { super("file://E:\\testworkspace\\testBSocket\\target\\classes", data, false , 0); } /** * */ private static final long serialVersionUID = 6797720945876437472L; /* (non-Javadoc) * @see testRMI.RemoteServiceInterface#queryAllUserinfo() */ @Override public List<UserInfo> queryAllUserinfo() throws RemoteException { List<UserInfo> users = new ArrayList<UserInfo>(); UserInfo user1 = new UserInfo(); user1.setUserAge(21); user1.setUserDesc("userDesc1"); user1.setUserName("userName1"); user1.setUserSex(true); users.add(user1); UserInfo user2 = new UserInfo(); user2.setUserAge(21); user2.setUserDesc("userDesc2"); user2.setUserName("userName2"); user2.setUserSex(false); users.add(user2); return users; }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657Copy the code
The way to register with the RMI Registry Registry also needs to change:
package testRMI; import java.rmi.MarshalledObject; import java.rmi.RMISecurityManager; import java.rmi.activation.Activatable; import java.rmi.activation.ActivationDesc; import java.rmi.activation.ActivationGroup; import java.rmi.activation.ActivationGroupDesc; import java.rmi.activation.ActivationGroupID; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.Properties; import testRMI.entity.UserInfo; public class RemoteActivationMain { public static void main(String[] args) throws Exception { System.setSecurityManager(new RMISecurityManager()); System.setProperty("java.rmi.activation.port","1099"); // Props = new Properties(); // The most important parameter is the permission for the rmI activation service. // The configuration of the all.policy file is described later in this article. "E:/testworkspace/testBSocket/src/all.policy"); GroupID: ActivationGroupID object generated by registerGroup * className: ActivationGroupID object generated by registerGroup * className: ActivationGroupID object generated by registerGroup . Here is the testRMI RemoteActivationServiceImpl specific RemoteServiceInterface interface * location: equivalent to the classpath. This registration process didn't know how to serialize class. * actually before we establish RemoteActivationServiceImpl constructor, Actually have already written dead * here just to explain the location desc and RemoteActivationServiceImpl constructor, and conducted a parameter specifies the * * MarshalledObject: a way of serialization and deserialization, see: * http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/java/rmi/MarshalledObject.html * */ ActivationGroupDesc groupdesc = new ActivationGroupDesc(props, null); ActivationGroupID groupid = ActivationGroup.getSystem().registerGroup(groupdesc); UserInfo userinfo = new UserInfo(); ActivationDesc desc = new ActivationDesc(groupid, "testRMI.RemoteActivationServiceImpl", "file://E:\\testworkspace\\testBSocket\\target\\classes", new MarshalledObject<UserInfo>(userinfo)); UnicastRemoteObject is registered differently from UnicastRemoteObject. * This is complicated because the Remote Object will be serialized to the JVM where RMI Registry is running. * */ RemoteServiceInterface Server = (RemoteServiceInterface)Activatable.register(desc); /* * Unlike the UnicastRemoteObject registration, the JVM will exit when the RemoteObject is successfully bound * because subsequent RMI calls will have nothing to do with it. * * / / / Naming. Bind (" rmi: / / 192.168.61.1:1099 / queryAllUserinfo ", server). Registry Registry = LocateRegistry. GetRegistry (" 192.168.61.1 ", 1099); registry.bind("queryAllUserinfo", server); }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758Copy the code
You also need to make a JVM permission description file all.policy:
Grant {permission Java. Security. AllPermission “”,” “; };
Of course this is the easy description (mainly for laziness, haha). You can also use the permission description file that comes with the JDK installation directory, with some modifications of course. The file is stored in $JAVA_HOME\jre\lib\security\java.policy, which has some permissions to modify according to your actual situation.
Then we launch RMIB, a registry program that supports RMI “activated” mode:
rmid -J-Djava.security.policy=E:\testworkspace\testBSocket\src\all.policy -port 1099 -J-Djava.rmi.server.codebase=file:/E:/testworkspace/testBSocket/target/classes/
If you are running Linux, the command is as follows:
export CLASSPATH=$CLASSPATH:/root/java/classes/
Rmid – J – Djava security. The policy = / usr/jdk1.7.0 _71 / all the policy – port 1099
There is no need to change the client code or how it works. You can see the effect by running the RMI Client directly. Note the following issues here;
- With RMI’s “activated” mode of operation, the original RMI Remote Service real Service provider no longer needs to respond to the RMI Client call. So you’ll see that after RemoteActivationMain completes registration, the JVM on which RemoteActivationMain is located will exit, rather than wait for registration to complete, as RemoteUnicastMain did before. As shown below:
- In addition, the Client requests a Stub where the endPoint no longer points to the real Service provider of the Remote Service, but to the Registry registered Service provider. This is because Registry is responsible for the implementation of the real service. As shown below:
Note the endPoint point of the Stub obtained by the Client.
RMI — a special RPC service implementation
Through the explanation of these two articles, we have introduced the basic usage, working principle and working mode of RMI to you. RMI is introduced because it introduces AN important inter-system communication management framework called RPC. Starting with the next article, we’ll dive into the RPC specification and introduce the use and work of several major RPC frameworks.
Of course, the introduction of RMI technology in two articles does not cover all of RMI technology, and I will return to you later on to introduce the features of RMI technology. Thank you for continuing to follow my blog.