在自己大四的实习中,对于计算机知识的理解有了更大的认知,对于网络协议、数据传输这块有了一定的认知,在快过22年的春节的时候,公司在年前的工作比较少,我闲来手写一个RPC框架来学习学习。
在选取网络传输技术上层API的时候,刚开始我打算使用NIO来调用socket从而去调用传输层的TCP协议,但是鉴于我在面试过程中遇到了面试官问我Netty的知识点,并且我从来没有使用过Netty,那么我这次的手写RPC我就是用Netty来做,当然Netty其实也是NIO框架,原理是一样的,只是Netty框架简化了开发。在这个博客中我应该会加一些Netty的知识点,就当学习了。
首先来介绍一下Netty。
Netty
etty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
接着我们介绍以下RPC。
RPC
当我们的服务端从单机变为多机时,我们想要调用远程服务的方法时不能直接调用,RPC可以使我们调用远程服务的方法相当于调用本地方法那样直接调用,其实说到底也是一个网络通信的问题,我们在本地伪造一个本地方法(代理),再通过网络将自己调用的方法的信息传输到存在该服务的远程服务器,之后找到该方法,再将该方法的返回值通过网络传输到本地方法,这样操作上就是本地直接调用伪造方法。在实现技术上,通信方面就是使用Netty,调用的话使用反射(服务端)与代理(客户端)。
接着我们来进行rpc框架的搭建
搭建RPC框架
搭建简介:
服务端:自定义一个@RPCService注解,统一标识提供服务方法的类(Bean)。在服务启动的时候将提供服务方法的Bean创建好并且注册到一个容器(类似于IOC容器)中。
客户端:自定义一个@RpcReference注解,将其加到消费类的实例对象中的属性中标识想要调用的对象,调用启动后通过java proxy创建服务端对象的代理对象,并且重写调用逻辑,其实就是将调用方的信息封装,之后通过Netty将调用信息传输到服务端。服务端拿到信息通过上面的@RPCService注解信息找到对应的类调用方法返回传输。
搭建过程
首先要将我们使用的依赖,通过Maven引入到我们的项目中。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.55.Final</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>8.0.11</version>
</dependency>
接下来我们把我们需要的实体类、实现类、注解、编解码器以及服务接口编写好。
实体类:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
//服务的实体类
public class Book {
private Integer id;
private String bookName;
}
服务接口:
public interface BookService {
Book getBook(Integer id);
Book updateBook(Book book);
Integer delBook(Integer id);
Integer insertBook(Book book);
}
注解:
//客户端标识作用的注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcReference {
public String referenceName();
}
//服务端标识