-
Notifications
You must be signed in to change notification settings - Fork 77
Interprocess Communication
#Interprocess Communication (IPC)
##JMS JMS allows data to be queued. Key use of JMS is for handling AVL feeds such that the data can be received on a web server and then forwarded via a queue to the core prediction system. Also, JMS enables multiple systems to use the same feed, which is useful if want to setup multiple test systems running off of a single AVL feed.
##RMI Key use of RMI is for communicating data between the core prediction system and the web servers. The data is serialized in order to be transmitted. RMI is used because it is a standard part of Java and the communication fast. It works for both when the processes are on the same server or when they are on different machines.
The org.transitime.ipc.rmi package is for making it easy to have remote RMI access to objects on the server. In particular, it is intended for "command" type of objects that do not change. They are simply used for changing state of a server or reading information from it.
The RMI registry needs to be run for this to work. Because don't want to interfere with other possible uses of the RMI registry a special port 2099 is used instead of the default 1099. Since it can be a nuisance to make sure that the rmiregistry is running the registry will be started automatically if it isn't currently running. If you want to run it manually, the registry comes with the JDK, which means the executable can be found in a place such as C:/Program Files/Java/jdk1.7.0_25/bin/rmiregistry.exe . With changes to the RMI system with version 1.7_20 of Java you need to specify the codebase if you run the rmiregistry manually. It would be started using something like: /usr/lib/jvm/jre/bin/rmiregistry -J-Djava.rmi.server.codebase=file:/home/ec2-user/jars/transitime.jar 2099 &
AbstractServer configures RMI to not use port 0, basically a random port, when establishing communication (the main port 2099 is used to establish communication but then communication is handed off to a separate port). Instead of using port 0 for actually sending commands to a server, RMI is configured to use a special port via the Java property -Dtransitime.rmi.secondaryRmiPort. The default value is 2098. In this way the firewalls for the servers can be configured to be open just for the ports that are actually used, which is much safer than having to completely open up network traffic. Every server on a machine must use a different secondary port for communication. This means that if you have multiple core systems running on a server only one can use the default secondary RMI port. The other core systems will need to have a different port specified by -Dtransitime.rmi.secondaryRmiPort .
An important issue is that when running on AWS the RMI server needs to be run using Java property -Djava.rmi.server.hostname=PUBLIC_DOMAIN.amazonaws.com . Otherwise RMI server will tell the client to use an internal IP address, resulting in the client simply timing out. This was a particularly difficult issue to resolve. The key documentation on this issue was found in the "Java Remote Method Invocation" document in the section 3.5 RMI Through Firewalls Via Proxies.
The purpose of these classes is to make the system robust and easy to use. The server not only initializes the object but also will reconnect if the registry restarts for some reason. On the client side the objects are initialized and when a remote call is made an InvocationHandler is used to both instrument the call but also to handle exceptions and retry communication as appropriate. If an RMI stub needs to be recreated due to server restart etc it is automatically redone. This means that the connection to the server is robust and easy to use.
Proxy objects are used so that each RMI call is instrumented. Since each RMI object is a proxy RmiCallInvocationHandler.invoke() is called for each RMI call. In invoke() the calls are logged and timed. Also, the number of current calls to a project are limited so that the server cannot be overwhelmed. This prevents problems due to denial of service attack or the project doing stop the world garbage collecting and therefore being unavailable for a while.
To create a RMI object do the following:
-
Create an interface class that inherits from java.rmi.Remote class. Each method that should be accessible remotely should throw java.rmi.RemoteException.
-
Create a server class that extends AbstractServer and implements the interface class.
-
The server class needs a constructor that calls super(projectId, objectName)
-
The server class should implement the remote methods defined in the interface.
-
For the client create an object such as (for the "Hello" interface class in this example) Hello hello = ClientFactory.getInstance(projectId, Hello.class);
Exceptions:
-
If get a ConnectException then likely the RMI registry is not running.
-
If get NotBoundException then the server object hasn't been created