从零开始手写一个RPC-01
从零开始手写一个RPC,让每个人都能看懂。
第一次手写RPC,一定会有理解的偏差与代码上的不完善,但就是这样,与新手对同样问题的疑惑,才能更好理解这样写为什么会出错,那样为什么是对的。
学习建议:一定要手写代码,边写边想。
RPC的概念
RPC是指远程过程调用,举个例子,两台服务器A,B.一个应用在A服务器上想要调用B服务器的一个应用获取信息,由于不在同一个内存空间,不能直接通信,因为不知道对方的地址和端口号,在同一个服务器的两个应用也无法直接通信,因为他们不知道对方的端口号,因此需要通过网络来表达调用的语义和传达调用的数据。(理解网络的五层模型就会好理解很多)
贴一个网上找的图,就更好理解了。
01.一个简单明了的RPC调用
所需知识:
- Java基础知识
- Java socket 编程
- 开发工具使用IDEA,项目使用Maven搭建,引入lombok包。
本节问题:
如何完成一个简单的RPC
一个简单地RPC调用就是客户端调用服务端的一个方法,服务端执行这个方法并返回数据给客户端。
过程:
1.首先创建一个Student类,在客户端和服务端都应该存在,客户端需要得到这个Student对象数据,服务端需要操作Student这个对象。
@Data//get(),set()方法
@AllArgsConstructor//有参构造
@NoArgsConstructor//无参构造
@Builder//链式编程
public class Student implements Serializable {
private Integer id;
private String name;
private boolean sex;
}
2.定义客户端需要调用的,服务端需要提供的接口
public interface StudentService {
Student getStudentByStudentId(Integer id);
}
3.服务端实现StudentService的功能
public class StudentServiceImpl implements StudentService {
@Override
public Student getStudentByStudentId(Integer id) {
//我们在代码中生成数据
System.out.println("客户端查询了"+id+"的学生");
Random random = new Random();
Student student = Student.builder()
.id(random.nextInt())
.name("zahngsan")
.sex(random.nextBoolean())
.build();
return student;
}
}
4.客户端建立Socket连接,传输Id给服务端,得到返回的Student对象
public class RPCClient {
public static void main(String[] args) {
try {
// 建立Socket连接
Socket socket = new Socket("127.0.0.1", 8890);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
// 传给服务器id
objectOutputStream.writeInt(new Random().nextInt());
objectOutputStream.flush();
// 服务器查询数据,返回对应的对象
Student student = (Student) objectInputStream.readObject();
System.out.println("服务端返回的student:"+student);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
System.out.println("客户端启动失败");
}
}
}
5.服务端以BIO的方式监听Socket,如有数据,调用对应服务的实现类执行任务,将结果返回给客户端
public class RPCServer {
public static void main(String[] args) {
StudentServiceImpl studentService = new StudentServiceImpl();
try {
ServerSocket serverSocket = new ServerSocket(8890);
System.out.println("服务端启动了");
while (true) {
Socket socket = serverSocket.accept();
new Thread(() -> {
try {
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Integer id = ois.readInt();
Student student = studentService.getStudentByStudentId(id);
oos.writeObject(student);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
System.out.println("从IO中读取数据错误");
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器启动失败");
}
}
}
总结:
这个例子以不到百行的代码,实现了客户端与服务端的一个远程过程调用,非常适合上手,当然它是及其不完善的,甚至连消息格式都没有统一,我们将在接下来的版本更新中逐渐完善它。
总结:
这个例子以不到百行的代码,实现了客户端与服务端的一个远程过程调用,非常适合上手,当然它是及其不完善的,甚至连消息格式都没有统一,我们将在接下来的版本更新中逐渐完善它。