手写rpc框架1
参考视频: 自己动手实现RPC框架,本内容为视频学习笔记。
三篇内容:
理论篇:rpc核心原理、现有框架对比、相关技术
实战篇:代码实现、使用案例
总结篇
理论篇
概念讲解
RPC: Remote Procedure Call,远程过程调用。是分布式系统常见的一种通信方法,从跨进程到跨物理机已经有几十年历史。
函数和过程区别
有返回值的:函数(function)
无返回值:过程(Procedure)
交互形式
跨进程交互形式:HTTP、基于数据库做数据交换、消息队列、RPC
在RPC中:Server: Provider服务提供者,Client: Consumer、服务消费者,Stub: 存根、服务描述。RPC可以像本地方法一样调用远程方法。
stub怎么理解?
客户端存根:存放服务端的地址消息,将客户端请求参数打包成网络消息,然后通过网络远程发送给服务方。
服务端存根:接收客户端发过来的消息,将消息解包,并调用本地方法。
现有框架对比:
核心原理
server将需要暴露的服务及地址信息注册到注册中心,client订阅注册中心,如果server地址发生改变会再次注册,注册中心会通知client地址的改变;也可以没有注册中心,地址写死在rpc里面。
过程3如下:
问题:网络、序列化、接口怎么调用远程方法?
技术栈
JavaCore、Maven、反射
动态代理,生成client存根实际调用对象
序列化,Java对象和二进制数据互转
网络通信,传输序列化后的数据,jetty、URLConnection
实战篇
步骤:
- 创建工程、制定协议、通用工具方法
- 实现序列化模块
- 实现网络模块
- 实现Server模块
- 实现Client模块
- 使用案例
创建工程
模块
pom.xml配置:
<!--提取出版本号,便于管理-->
<properties>
<commons.version>2.5</commons.version>
<jetty.version>9.2.28.v20190418</jetty.version>
<fastjson.version>1.2.79</fastjson.version>
<lombok.version>1.18.8</lombok.version>
<junit.version>4.12</junit.version>
<slf4j.version>1.7.32</slf4j.version>
<logback.version>1.2.6</logback.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!--子模块可能会用的依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--公共依赖,所有模块都会依赖的-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--全称为Simple Logging Facade for Java。 是对不同日志框架提供的一个门面封装。-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!--日志框架,实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
<!--工程编译的版本-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<!--指定maven编译的jdk版本,否则会按默认的
maven3 jdk1.5, maven2 jdk1.3-->
<configuration>
<!--指定源代码使用的JDK版本-->
<source>${maven.compiler.source}</source>
<!--指定生成的class文件的编译版本-->
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
里面用到了dependencies
和dependencyManagement
,说下二者区别:
dependencyManagement
放的是子模块可能要用的依赖,如果全都用的话,直接放到总的dependencies
就行了,dependencyManagement
不会自动引入依赖,只是声明版本号,如果子模块中写了这个依赖,但是没有指定具体版本,就会从父pom文件继承,如果指定了版本号,就按指定的来,这样做是为了统一管理项目的版本号,确保每个模块的依赖和版本一致,否则每个模块一个版本,不利于管理。
lombok原理:
自从Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”规范,只要程序实现了该API,就能在javac运行的时候得到调用。
Lombok就是一个实现了"JSR 269 API"的程序。在使用javac的过程中,它产生作用的具体流程如下:
javac对源代码进行分析,生成一棵抽象语法树(AST)
javac编译过程中调用实现了JSR 269的Lombok程序
此时Lombok就对第一步骤得到的AST进行处理,找到Lombok注解所在类对应的语法树(AST),然后修改该语法树(AST),增加Lombok注解定义的相应树节点
javac使用修改后的抽象语法树(AST)生成字节码文件
协议
反射操作类
对反射进行了一层封装
/**
* 反射工具类
* @author hqingLau
**/
public class ReflectUtils {
/**
* 根据class创建对象
* @param clazz 待创建对象的类
* @param <T> 对象类型
* @return 创建好的对象
*/
public static <T> T newInstance(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
/**
* 获取某个class的公有方法
* @param clazz
* @return
*/
public static Method[] getPublicMethods(Class clazz) {
// 返回当前类所有的方法
Method[] methods = clazz.getDeclaredMethods();
List<Method> methodList =new ArrayList<>();
for(Method m:methods) {
if(Modifier.isPublic(m.getModifiers())) {
methodList.add(m);
}
}
// new Method[0]就是起一个模板的作用,指定了返回数组的类型,
// 0是为了节省空间,因为它只是为了说明返回的类型
// 数组的大小还是list的size决定的
return methodList.toArray(new Method[0]);
}
/**
* 调用指定对象的指定方法
* @param obj 被调用的对象
* @param method 被调用的方法
* @param args 方法参数
* @return 返回结果
*/
public static Object invoke(Object obj,
Method method,
Object... args) {
try {
return method.invoke(obj,args);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
(第一节完)