手搓RPC

序列化、反序列化问题

RPC远程调用时,需要将对象进行序列化/反序列化操作。

比如RPC框架的response、request对象需要传递给服务提供方/服务消费方。所以需要对RPC的response、request对象进行序列化、反序列化操作。

实现方式:

  • 第三方序列化框架
    • 推荐。比较方便,但是该项目不需要太复杂,所以暂不使用。
  • 继承Serializable接口
    • Serializable接口本身不包含任何方法或字段,它是一个标记接口(marker interface)
    • 具体的序列化方法主要通过ObjectOutputStreamObjectInputStream这两个类来完成
    • 如果更好的控制序列化过程,还需要使用writeObject()readObject()方法
    • 每一个继承该接口,同时想要精细化控制的类都需要实现writeObject()readObject()方法
  • 自定义一个Serializer序列化器
    • 自己定义序列化、反序列化方法,将控制序列化的方法都写在序列化器中,不需要每个类自己写一遍。
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
package org.example.serializer;

import java.io.*;

public class JDKSerializer implements Serializer {

@Override
public <T> byte[] serialize(T obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}

@Override
public <T> T deserialize(byte[] bytes, Class<T> clazz) throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
try {
return (T) objectInputStream.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
objectInputStream.close();
}
}
}

注意:

  • 流的flush方法close方法
  • flush()方法的主要作用是立即将缓冲区中的所有数据写入到目标输出流中。这保证了即使在程序异常终止的情况下,已经调用过的方法所要写入的数据能够被保存下来。

  • close()方法不仅会调用flush()方法来清空缓冲区,还会关闭底层的输出流,释放与之关联的所有系统资源。一旦流被关闭,就不能再进行任何读写操作,否则会抛出IOException

使用建议

在实际编程中,推荐的做法是在完成所有写入操作后,总是调用close()方法来关闭流。由于close()方法内部会调用flush(),所以通常不需要显式地调用flush(),除非有特定的需求(例如,在多次写入之间需要立即同步数据)。

消费方发起调用的方法

rpc本质就是远程调用,比如服务提供方A,消费方B、C…,无论消费方有多少,获取提供方A的某个数据所需要的请求代码都是相同的。所以可以采用代理的方法(代理本来要用作方法的增强,在这里直接调用相同的代码逻辑)。

缺点:这样实现的接口UserService必须在common包里定义,提供方实现这个接口才可以。因为消费方也需要。