参考视频:https://www.bilibili.com/video/av30168877/?p=3
参考文章:https://blog.csdn.net/shan9liang/article/details/8995023
1.RPC与RMI
- RMI(remote method invocation,面向对象的远程方法调用)
- RPC(remote procedure call,远程过程调用)
RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。在RPC中,当一个请求到达RPC服务器时,这个请求就包含了一个参数集和一个文本值,通常形成“classname.methodname”的形式。这就向RPC服务器表明,被请求的方法在为 “classname”的类中,名叫“methodname”。然后RPC服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与RPC请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后返回客户方。
Java RMI 指的是远程方法调用 (Remote Method Invocation)。它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法。可以用此方法调用的任何对象必须实现该远程接口。
RPC与RMI区别于联系
- RPC 跨语言,而 RMI只支持Java。
- RMI 调用远程对象方法,允许方法返回 Java 对象以及基本数据类型,而RPC 不支持对象的概念,传送到 RPC 服务的消息由外部数据表示 (External Data Representation, XDR) 语言表示,这种语言抽象了字节序类和数据类型结构之间的差异。只有由 XDR 定义的数据类型才能被传递, 可以说 RMI 是面向对象方式的 Java RPC 。
- 在方法调用上,RMI中,远程接口使每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口上,那么这个新方法就不能被RMI客户方所调用。
代码实现:
1.Server端方法接口与实现类
HelloService.java
1 2 3 4 5
| package server;
public interface HelloService { String sayHi(String name); }
|
HelloServiceImpl.java
1 2 3 4 5 6 7 8
| package server;
public class HelloServiceImpl implements HelloService { @Override public String sayHi(String name) { return "Hi " + name; } }
|
2.远程调用的函数注册中心接口及其实现
RegisterServerCenter.java
1 2 3 4 5 6 7 8 9 10 11 12
| package server;
public interface RegisterServerCenter { void start();
void stop();
void register(Class service, Class serviceImpl); }
|
RegisterServerCenterImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| package server;
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class RegisterServerCenterImpl implements RegisterServerCenter {
private static Map<String, Class> serviceRegister = new HashMap<>();
private static int port;
private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static boolean isRunning = false;
public RegisterServerCenterImpl(int port) { this.port = port; }
@Override public void start() { ServerSocket serverSocket = null; Socket socket; try { serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port)); } catch (IOException e) { e.printStackTrace(); } isRunning = true; while (true) { try { System.out.println("---- start server ----");
socket = serverSocket.accept();
executorService.execute(new ServiceTask(socket)); } catch (IOException e) { e.printStackTrace(); } }
}
@Override public void stop() { isRunning = false;
executorService.shutdown(); }
@Override public void register(Class service, Class serviceImpl) {
serviceRegister.put(service.getName(), serviceImpl); }
private static class ServiceTask implements Runnable {
private Socket socket;
ServiceTask(Socket socket) { this.socket = socket; }
@Override public void run() { ObjectOutputStream outputStream = null; ObjectInputStream inputStream = null; try { inputStream = new ObjectInputStream(socket.getInputStream());
String serviceName = inputStream.readUTF(); String methodName = inputStream.readUTF(); Class[] paramType = (Class[]) inputStream.readObject(); Object[] args = (Object[]) inputStream.readObject();
Class serviceClass = serviceRegister.get(serviceName); Method method = serviceClass.getMethod(methodName, paramType); Object result = method.invoke(serviceClass.newInstance(), args);
outputStream = new ObjectOutputStream(socket.getOutputStream()); outputStream.writeObject(result); } catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } if (socket != null) { socket.close(); } } catch (IOException e) { e.printStackTrace(); } } } } }
|
3.Client端代码
Client.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package client;
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.Socket; import java.util.Objects;
public class Client {
public static <T> T getRemoteProxyObj(Class service, InetSocketAddress inetSocketAddress) { return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Socket socket = new Socket(); ObjectOutputStream outputStream = null; ObjectInputStream inputStream = null; try { socket.connect(inetSocketAddress); outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeUTF(service.getName()); outputStream.writeUTF(method.getName()); outputStream.writeObject(method.getParameterTypes()); outputStream.writeObject(args);
inputStream = new ObjectInputStream(socket.getInputStream()); return inputStream.readObject(); } finally { Objects.requireNonNull(inputStream).close(); Objects.requireNonNull(outputStream).close(); socket.close(); }
} }); } }
|
4.服务端启动类
RPCServerTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package test;
import server.HelloService; import server.HelloServiceImpl; import server.RegisterServerCenter; import server.RegisterServerCenterImpl;
public class RPCServerTest { public static void main(String[] args) {
new Thread(new Runnable() { @Override public void run() { RegisterServerCenter server = new RegisterServerCenterImpl(9999); server.register(HelloService.class, HelloServiceImpl.class); server.start(); } }).start(); } }
|
5.客户端启动类
ClientRPCTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package test;
import client.Client; import server.HelloService;
import java.net.InetSocketAddress;
public class ClientRPCTest { public static void main(String[] args) throws ClassNotFoundException { HelloService service = Client.getRemoteProxyObj(Class.forName("server.HelloService"), new InetSocketAddress("127.0.0.1", 9999)); System.out.println((service.sayHi("zhangsan"))); } }
|
此时启动两个启动类便可实现RPC远程方法调用