Fabric Architecture Foundation: Java RMI Detailed

RMI Introduction Java RMI, the Remote Method Invocation, a Java API for implementing Remote Procedure Call (RPC), can directly transfer serialized Java objects and distributed garbage collection. Its implementation relies on the Java Virtual Machine (JVM), so it only supports calls from one JVM to another.

这里写图片描述 rmi的实现 (1) 直接使用Registry实现rmi 服务端:

public class RegistryService {       Public static void main(String[] args) {           Try {               // Remote object registry instance on the local host, default port 1099               Registry registry = LocateRegistry.createRegistry(1099);               // Create a remote object               HelloRegistryFacade hello = new HelloRegistryFacadeImpl();               // Register the remote object on the RMI registration server and name it HelloRegistry               Registry.rebind("HelloRegistry", hello);               System.out.println("======= Starting the RMI service successfully! =======");           } catch (RemoteException e) {               e.printStackTrace();           }       } }   Interface:   Inherit the Remote interface   Public interface HelloRegistryFacade extends Remote {

String helloWorld(String name) throws RemoteException;

}   Interface implementation:   Inherited UnicastRemoteObject   Public class HelloRegistryFacadeImpl extends UnicastRemoteObject   Implements HelloRegistryFacade{

public HelloRegistryFacadeImpl() throws RemoteException {
    Super();
}

@Override
Public String helloWorld(String name) {
    Return "[Registry] Hello! " + name;
}

}   Client:   Public class RegistryClient {       Public static void main(String[] args) {           Try {               Registry registry = LocateRegistry.getRegistry(1099);               HelloRegistryFacade hello = (HelloRegistryFacade) registry.lookup("HelloRegistry");               String response = hello.helloWorld("ZhenJin");               System.out.println("========> + response + ” <=======”);           } catch (NotBoundException | RemoteException e) {               e.printStackTrace();           }       } }

图:这里写图片描述 Registry(注册表)是放置所有服务器对象的命名空间。 每次服务端创建一个对象时,它都会使用bind()或rebind()方法注册该对象。 这些是使用称为绑定名称的唯一名称注册的。

To call a remote object, the client needs a reference to the object, such as (HelloRegistryFacade). That is, the object (lookup() method) is obtained from the registry by the name of the server binding (HelloRegistry). (2) Implementing rmi using the Naming method Server:

public class NamingService {       Public static void main(String[] args) {           Try {               // An instance of the remote object registry Registry on the local host               LocateRegistry.createRegistry(1100);               // Create a remote object               HelloNamingFacade hello = new HelloNamingFacadeImpl();               // Register the remote object on the RMI registration server and name it Hello               //The standard format of the bound URL is: rmi://host:port/name               Naming.bind("rmi://localhost:1100/HelloNaming", hello);               System.out.println("======= Starting the RMI service successfully! =======");           } catch (RemoteException | MalformedURLException | AlreadyBoundException e) {               e.printStackTrace();           }       } }   The interface and interface are implemented in the same way as the Registry

客户端:

public class NamingClient {       Public static void main(String[] args) {           Try {               String remoteAddr=”rmi://localhost:1100/HelloNaming”;               HelloNamingFacade hello = (HelloNamingFacade) Naming.lookup(remoteAddr);               String response = hello.helloWorld("ZhenJin");               System.out.println("========> + response + ” <=======”);           } catch (NotBoundException | RemoteException | MalformedURLException e) {               e.printStackTrace();           }       } }   Naming part of the source code:   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); }

Naming is actually a package for Registry

Scala to implement rmi It is said that rmi is a remote call through the JVM virtual machine. We verify it by the jvm language such as Scala, kotlin, etc.

服务:

object ScalaRmiService extends App { try {       Val user:UserScalaFacade = new UserScalaFacadeImpl       LocateRegistry.createRegistry(1103)       Naming.rebind("rmi://localhost:1103/UserScala", user)       Println("======= Start the RMI service successfully! =======") } catch {       Case e: IOException => println(e) } }   Interface   Trad UserScalaFacade extends Remote {

/**       * Get user information by username       */ @throws(classOf[RemoteException]) def getByName(userName: String): User

/**       * Get user information by user gender       */ @throws(classOf[RemoteException]) def getBySex(userSex: String): List[User]

}   Interface implementation:   Class UserScalaFacadeImpl extends UnicastRemoteObject with   UserScalaFacade {

/**       * Simulate a database table       */ private lazy val userList = List(       New User("Jane", "female", 16),       New User("jack", "male", 17),       New User("ZhenJin", "Male", 18) )

override def getByName(userName: String): User = userList.filter(u => userName.equals(u.userName)).head

override def getBySex(userSex: String): List[User] = userList.filter(u => userSex.equals(u.userSex))

}   Entity class:   Entity classes must be serializable for a remote transfer   Class User(name: String, sex: String, age: Int) extends Serializable {

var userName: String = name var userSex: String = sex var userAge: Int = age override def toString = s”User(userName=userName,userSex=userName,userSex=userSex, userAge=$userAge)”

}   Scala client:   Object ScalaRmiClient extends App {

try {

val remoteAddr="rmi://localhost:1103/UserScala"
Val userFacade = Naming.lookup(remoteAddr).asInstanceOf[UserScalaFacade]

Println(userFacade.getByName("ZhenJin"))
System.out.println("--------------------------------------")
For (user <- userFacade.getBySex("male")) println(user)

} catch { case e: NotBoundException => println(e) case e: RemoteException => println(e) case e: MalformedURLException => println(e) }

}   Java client:   Public class JavaRmiClient {

public static void main(String[] args) {

    Try {
        String remoteAddr="rmi://localhost:1103/UserScala";
        UserScalaFacade userFacade = (UserScalaFacade) Naming.lookup();

        User zhenJin = userFacade.getByName("ZhenJin");
        System.out.println(zhenJin);
        System.out.println("--------------------------------------");
        List<User> userList = userFacade.getBySex("male");
        System.out.println(userList);

    } catch (NotBoundException | RemoteException | MalformedURLException e) {
        e.printStackTrace();
    }
} }

The above test can prove that Scala and Java are interoperable, and Scala itself is also an introduction to the

serialization that can directly reference Java classes. Serialization is the process of converting a data structure or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, through a network connection). Deserialization is The opposite of extracting a data structure from a series of bytes.

这里写图片描述 Kotlin实现rmi 服务端:

fun main(args: Array) {       Try {           Val hello: HelloKotlinFacade = HelloKotlinFacadeImpl()           LocateRegistry.createRegistry(1102)           Naming.rebind("rmi://localhost:1101/HelloKotlin", hello)           Println("======= Start RMI service success! =======")       } catch (e: IOException) {           e.printStackTrace()       } }   Client:   Fun main(args: Array) {       Try {           Val hello = Naming.lookup("rmi://localhost:1102/HelloKotlin") as HelloKotlinFacade           Val response = hello.helloWorld("ZhenJin")           Println("=======> $response <=======")       } catch (e: NotBoundException) {           e.printStackTrace()       } catch (e: RemoteException) {           e.printStackTrace()       } catch (e: MalformedURLException) {           e.printStackTrace()       } }   Implementation and interface omission...

SpringBoot implements rmi StringBoot can be easily implemented by configuration.

服务:

@Configuration public class RmiServiceConfig {       @Bean       Public RmiServiceExporter registerService(UserFacade userFacade) {           RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();           rmiServiceExporter.setServiceName("UserInfo");           rmiServiceExporter.setService(userFacade);           rmiServiceExporter.setServiceInterface(UserFacade.class);           rmiServiceExporter.setRegistryPort(1101);           Return rmiServiceExporter;       } }   Client:   @Configuration public class RmiClientConfig {

@Bean
public UserFacade userInfo() {
    RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
    rmiProxyFactoryBean.setServiceUrl("rmi://localhost:1101/UserInfo");
    rmiProxyFactoryBean.setServiceInterface(UserFacade.class);
    rmiProxyFactoryBean.afterPropertiesSet();
    return (UserFacade) rmiProxyFactoryBean.getObject();
}

}   Client test class:   @Autowired private UserFacade userFacade;        @Test public void userBySexTest() {       Try {           List userList = userFacade.getBySex("male");           userList.forEach(System.out::println);       } catch (RemoteException e) {           e.printStackTrace();       } }   Through the test class, it can be seen that this is no different from our usual program call internal method!

rmi call process 这里写图片描述 有两个远程服务接口可供client调用,Factory和Product接口

FactoryImpl class implements the Factory interface, ProductImpl class implements the Product interface 1. FactoryImpl is registered in the rmi-registry 2. The client side requests a reference to the Factory 3. rmi-registry returns a reference to a FactoryImpl on the client side 4. The client side calls FactoryImpl's remote method to request a remote reference to ProductImpl 5. FactoryImpl returns a ProductImpl reference to the client 6. client calls remote method via ProductImpl reference Zookeeper implements rmi Source: http://www.importnew.com/20344.html

Install Zookeeper Unzip ZooKeeper

tar -zxvf zookeeper-3.4.12.tar.gz

in the conf directory to create a new zoo.cfg

cd zookeeper-3.4.12/conf vim zoo.cfg

zoo.cfg code as follows (specify the log file directory yourself):

tickTime=2000 dataDir=/usr/local/zookeeper-3.4.12/data dataLogDir=/usr/local/zookeeper-3.4.12/log clientPort=2181

In the bin directory, start Zookeeper:

cd zookeeper-3.4.12/bin ./zkServer.sh start

:

public class RmiConsumer {

// used to wait for the current thread to continue after the SyncConnected event fires
Private CountDownLatch latch = new CountDownLatch(1);

// Define a volatile member variable to hold the latest RMI address (considering that the variable may be modified by other threads, once modified, the value of the variable will affect all threads)
Private volatile List<String> urlList = new ArrayList<>();

// constructor
Public RmiConsumer() {
    ZooKeeper zk = connectServer(); // Connect to the ZooKeeper server and get the ZooKeeper object
    If (zk != null) {
        watchNode(zk); // observe all child nodes of the /registry node and update the urlList member variable
    }
}

// Find the RMI service
Public <T extends Remote> T lookup() {
    T service = null;
    Int size = urlList.size();
    If (size > 0) {
        String url;
        If (size == 1) {
            Url = urlList.get(0); // If there is only one element in the urlList, get the element directly
            Log.debug("using only url: {}", url);
        } else {
            Url = urlList.get(ThreadLocalRandom.current().nextInt(size)); // If there are multiple elements in urlList

, get an element randomly                   Log.debug("using random url: {}", url);               }               Service = lookupService(url); // Find the RMI service from JNDI           }           Return service;       }

// Connect ZooKeeper Server
Private ZooKeeper connectServer() {
    ZooKeeper zk = null;
    Try {
        Zk = new ZooKeeper(Constant.ZK_CONNECTION_STRING, Constant.ZK_SESSION_TIMEOUT, new Watcher() {
            @Override
            Public void process(WatchedEvent event) {
                If (event.getState() == Event.KeeperState.SyncConnected) {
                    latch.countDown(); // wake up the currently executing thread
                }
            }
        });
        Latch.await(); // Make the current thread wait
    } catch (IOException | InterruptedException e) {
        Log.error("", e);
    }
    Return zk;
}

// Observe whether all child nodes under the /registry node have changed
Private void watchNode(final ZooKeeper zk) {
    Try {
        List<String> nodeList = zk.getChildren(Constant.ZK_REGISTRY_PATH, event -> {
            If (event.getType() == Watcher.Event.EventType.NodeChildrenChanged) {
                watchNode(zk); // If the child node changes, the method is called again (in order to get the data in the latest child node)
            }
        });
        List<String> dataList = new ArrayList<>(); // used to store data in /registry all child nodes
        For (String node : nodeList) {
            Byte[] data = zk.getData(Constant.ZK_REGISTRY_PATH + "/" + node, false, null); // Get the data in the child of /registry
            dataList.add(new String(data));
        }
        Log.debug("node data: {}", dataList);
        urlList = dataList; // update the latest RMI address
    } catch (KeeperException | InterruptedException e) {
        Log.error("", e);
    }
}

// Find the RMI remote service object in JNDI
@SuppressWarnings("unchecked")
Private <T> T lookupService(String url) {
    T remote = null;
    Try {
        Remote = (T) Naming.lookup(url);
    } catch (NotBoundException | MalformedURLException | RemoteException e) {
        Log.error("Remote lookup error!", e);
    }
    Return remote;
} }

生产者:   Public class RmiProvider {

/**
 * Used to wait for the current thread to continue after the SyncConnected event fires
 */
Private CountDownLatch latch = new CountDownLatch(1);

// Publish the RMI service and register the RMI address into ZooKeeper
Public void publish(Remote remote, String host, int port) {
    String url = publishService(remote, host, port); // Publish the RMI service and return the RMI address
    If (url != null) {
        ZooKeeper zk = connectServer(); // Connect to the ZooKeeper server and get the ZooKeeper object
        If (zk != null) {
            createNode(zk, url); // Create a ZNode and put the RMI address on the ZNode
        }
    }
}

 /**
  *Publish RMI service
  */
Private string publishService(Remote remote, String host, int port) {
    String url = null;
    Try {
        Url = String.format("rmi://%s:%d/%s", host, port, remote.getClass().getName());
        LocateRegistry.createRegistry(port);
        Naming.rebind(url, remote);
        Log.debug("publish rmi service (url: {})", url);
    } catch (RemoteException | MalformedURLException e) {
        Log.error("", e);
    }
    Return url;
}

// Connect to the ZooKeeper server
Private ZooKeeper connectServer() {
    ZooKeeper zk = null;
    Try {
        Zk = new ZooKeeper(Constant.ZK_CONNECTION_STRING, Constant.ZK_SESSION_TIMEOUT, new Watcher() {
            @Override
            Public void process(WatchedEvent event) {
                If (event.getState() == Event.KeeperState.SyncConnected) {
                    latch.countDown(); // wake up the currently executing thread
                }
            }
        });
        Latch.await(); // Make the current thread wait
    } catch (IOException | InterruptedException e) {
        Log.error("", e);
    }
    Return zk;
}

/**
 * Create a node
 */
Private void createNode(ZooKeeper zk, String url) {
    Try {
        Byte[] data = url.getBytes();
        String path = zk.create(Constant.ZK_PROVIDER_PATH, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); // Create a temporary and ordered ZNode
        Log.debug("create zookeeper node ({} => {})", path, url);
    } catch (KeeperException | InterruptedException e) {
        Log.error("", e);
    }
}

} Illustration:这里写图片描述