RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLzElaNFTWq10dRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLmNGMmZWMmNjYiR2M2MTOhRDZhRDOiNTOxEzYiVGO4U2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
RPC是一种通信方式:既可以基于HTTP协议实现,也可以直接在TCP协议上实现。
Client:客户端
public class Client {
public static void main(String[] args) {
UserService stub = (UserService) Stub.getStub(UserServiceImpl.class);
System.out.println(stub.getUserById(1).toString());
}
}
Stub:用于封装客户端侧的网络通信
实现方式,借助动态代理
public class Stub {
public static Object getStub(Class clazz) {
Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("127.0.0.1",8000);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
String clazzName = clazz.getName();
String methodName = method.getName();
Class[] typeParameters = method.getParameterTypes();
//将这些关键信息发送给Server,其借助反射进行恢复
oos.writeUTF(clazzName);
oos.writeUTF(methodName);
oos.writeObject(typeParameters);
oos.writeObject(args);
oos.flush();
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object o1 = ois.readObject();
oos.close();
socket.close();
return o1;
}
});
return proxy;
}
}
Server:服务端
public class Server {
private static boolean running = true;
public static void main(String[] args) {
ServerSocket ss = null;
try {
ss = new ServerSocket(8000);
while (running) {
Socket s = ss.accept();
process(s);
s.close();
}
...
}
private static void process(Socket s){
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
String clazzName = ois.readUTF();
String methodName = ois.readUTF();
Class[] parameterTypes = (Class[]) ois.readObject();
Object[] args = (Object[]) ois.readObject();
//获取指定的Class对象
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> clazz = classLoader.loadClass(clazzName);
//借助反射执行方法
Method method = clazz.getMethod(methodName, parameterTypes);
Object o = method.invoke(clazz.newInstance(), args);
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
oos.writeObject(o);
oos.flush();
}
}
这里欣赏下常用反射函数
在利用反射访问private、protected成员变量和方法时必须通过setAccessible(boolean access)方法取消Java语言检查,否则将抛出IllegalAccessException。
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> clazz = loader.loadClass("com.biezhi.ioc.PrivateHero");
PrivateHero hero = (PrivateHero) clazz.newInstance();
Field name = clazz.getDeclaredField("name");
// 取消Java语言访问检查以访问private变量
name.setAccessible(true);
name.set(hero, "德玛西亚之力"); //要不这里报错
常见的序列化方式
序列化:将对象状态转换为可保持或传输的二进制。
反序列化:将二进制数据转换为对象的过程。
推荐protobuf、thrift、Hessian(PS:反正不要用JDK原生的就对了)