第02篇:手写JavaRPC框架之设计思路

西魏陶渊明 ... 2022-8-22 Mojito 大约 8 分钟

天下代码一大抄, 抄来抄去有提高, 看你会抄不会抄!

# 一、前言

隔壁老李又在喷我了: "完犊子了,小编这绝对是为了骗粉丝,而水的一篇文章,到了第二篇竟然还没有开始写代码,又是一篇纯概念文章"。

我也想写代码,但是在没有讲清楚思路之前,一定不要上来就蛮干,不然就毫无设计可言了。小编向各位观众老爷保证,下一篇文章绝对上代码。

本篇文章非常重要,这是我们本系列文章中的重中之重,本篇文章的主要内容就是设计我们自己的通信协议及架构,可以这样说如果没有了本篇文章的内容,就不可能实现RPC。因为RPC的最基本要求就是能实现远程通信。本篇文章是讲述通信层的设计思路,下一篇就是实战的编写。(ps: 其实下一篇比较好写,因为代码我早就写完了嘻嘻,而这一篇竟然酝酿了一周还写的...不忍直视)

# 二、目标

本篇文章主要会围绕以下三方面展开叙述,希望在通读全篇后,大家都能在脑子中形成对这三个方面的认识,因为下面的三个方面是通信层搭建的主要指导思想。

  1. 设计我们自己的通信协议
  2. 确定我们的通信层的架构
  3. 确定我们的工程结构

# 2.1 为什么我们要设计自己的通信协议呢?

上一篇我们也说了,实现RPC可以基于http也可以基于tcp。他们各有各的好处,如果是基于http其实我们的挑战就相对比较小一些,因为实现http的协议已经是在太多了,我们只用通过代理进行层层封装即可,而我们之所以要自己实现通信协议就是。

作为Java程序猿还是要对底层通信协议的具体实现有点了解的。如果不了解的话也没关系,你只要知道他是二进制数据就可以了。我们一步一步通过代码编写将二进制数据转换成我们Java语言能够认识的数据就好了。如果这个过程你学会了,那么一通百通,http如何实现的其实大概也能知道猜到一点。

# 2.2 为什么要讲通信层的架构?

怎么理解架构?

作为一个有经验的开发者,都会清楚,我们写代码就如同写文章。好的代码一定是思路清晰的,思路清晰的代码耦合性一定是很少的。我们举一个例子,最近大环境不好,大厂裁员较多,很多小伙伴都要面试吧,就举一个面试的问题,通过这个例子来解释下什么是架构。

  • 面试官说: 同学做一下自我介绍吧。

首先我们不能懵啊,如果懵了就说明没有头绪了,这样就容易讲乱,没有头绪在开发过程中的体现就是代码写的杂乱。比如你在介绍家乡的时候突然穿插了一下爱好,而在讲爱好的时候,又穿插的讲了一下家乡。这样就会导致主题不分明,听者会感觉会乱。所以这里我们就需要 单一职责。首先定义清楚你的讲话的结构,然后每个结构点就一个职责。

如下我们设计的面试架构是这些点:

姓名,家乡,大学,专业,兴趣爱好,单位职称 .

下面我们只用实现每个点的内容(主题清晰),最终将他组装成完成的自我介绍回答;

// 姓名,家乡,大学,专业,兴趣爱好,单位职称 
public interface Introduce{
     // 这是一个介绍类,负责介绍自己
     public void introduce();
}
public class XiaoMing implements Introduce{

     // 将任务进行拆分,拆分的维度是逻辑顺序,然后抽离出方法,抽离的维度是单一职责。
     // 这样的好处是工能化,模块化,便于复用。
     public void introduce(){
         sout("我叫小明");
         // 主题介绍家乡
         introduceHometown();
         // 主题介绍学校
         introduceSchool();
         // 主题介绍专业
         introduceMajor();
         // 主题介绍兴趣爱好
         introduceInterest();
         sout("从业xx年,目前在公司的职称是xxx");
     }
     
    private void introduceHometown(){
         sout("我的老家是河南南阳")
         sout("我的家乡就坐落在河南南阳邓州市")
         sout("邓州市一个美丽的城市,是中国邓姓的发源地")
         sout("邓州也是河南境内人口最多的一个县级城市")
     }
     
     private void introduceSchool(){
         sout("我大学是在河南大学")
         sout("河南大学简称河大,是一所位于中国河南省开封市涵盖文、史、哲、经、管、
         法、理、工、医、农、教育、艺术等12个学科门类的省部共建型综合性公立大学。")
     }
     
     private void introduceMajor(){
         sout("我的专业是计算机与信息工程")
     }
     
     private void introduceInterest(){
         sout("我的个人爱好是写博客、打游戏、做美食、偶会也会跑跑步")
     }
}
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

有没有发现单一职责的设计,会很大程度提高我们的代码利用率呢? 我们要的就是这个效果,所以我们再开始编码之前,最好提前定义清楚我们的架构是怎么样的。

上面的例子,不知道小编有没有给大家解释清楚,但是总归我们的目的,是要在设计的时候就要明确责任划分,尽可能的单一职责。尽可能的解耦。

要想设计一个好的框架,首先一定要有一个好的架构设计。这一点我们可以直接参考dubbo的设计架构。

在这里插入图片描述 上半部分不用看,我们只用看通信层就好了。

  • serialize 序列化层,负责将二进制数据转成Java认识的数据类型
  • transport 传输层,负责发送和接受数据
  • exchange 转换成,通信层和业务逻辑层转换的地方。
  • protocol 协议层,告诉serialize用什么协议来encode和decode数据的

所以说,天下代码一大抄,抄来抄去有提高,就看你会抄不会抄了。 我们的设计就主要参考dubbo来了。

# 2.3 工程结构设计

目前市面上的框架基本上都是自己来定制通信层,而通信层基本也不会单独的提供出去。但是本系列小编希望是通信层和业务能分开。通信层可以做RPC也可以利用通信层去实现消息队列或者是web容器。所以因为这个设计,就要求我们的项目能单独的去发布。所以我们整体的项目结构是由三个部分组成的,如下。

# 三、核心知识点

# 3.1 通信层协议定义

什么是协议呢?

其实就是规则,我们按照什么样的方式将二进制数据转换成Java对象。

如下图,我们的一条数据会分为4个部分

  1. 第一部分占用一个字节是协议标记,用来标记是http协议还是自定义协议。
  2. 第二部分占用一个字节是序列化标记,用来确定我们的真实报文使用什么来进行序列化和反序列化。
  3. 第三部分占用四个字节,用来表示数据的字节长度,确定真实报文的长度。
  4. 第四部分长度不固定,是真实的传输数据。最终会通过第二部分将这些二进制数据转换成Java对象。

以上就是我们定义的数据解析协议,通过上面的规则将二进制数据,转换成Java对象。

读到这里你有没有一点收获呢?

有没有发现,其实协议的概念,其实很简单,就是一个规则或者说是约定。能让彼此都互相认识的一个约定。

在本系列中,我们会自定义一个协议,同时也会兼容支持http协议。如果感兴趣,就跟着小编一起coding吧。

# 3.2 通信层架构设计

前面说了,我们是站在巨人的肩膀上的,根据dubbo的设计思路和我们的目标,我们也来画一张图。

在这里插入图片描述 我们的最终架构如上图。

作用
serialize 序列化协议层,包含了多种序列化协议
codec 数据解码器和编码器的具体实现层
exchange API交换层,业务层API和通信层API交换数据的地方,负责将业务数据转换成二进制数据发送,也负责将二进制数据转换成业务数据返回
model 基础数据模型
business 提供给开发者用来实现业务的api
api Fluent 风格的api, 这种风格的好处是不需要记住接下来的步骤和方法

# 3.3 工程结构

为了符合前面我们定的目标,所以我们要有一个大的工程。

项目名 职责
mojito-net 底层通信模块
mojito-rpc rpc模块
mojito-spring-boot-starter springboot自动化配置

由此我们的项目诞生了。下一篇我们就开始手撸代码吧。

.
├── README.md
├── mojito-net
│   ├── pom.xml
│   └── src
│       ├── main
│       │   ├── java
│       │   └── resources
│       └── test
│           └── java
├── mojito-rpc
│   ├── pom.xml
│   └── src
│       ├── main
│       │   ├── java
│       │   └── resources
│       └── test
│           └── java
├── mojito-spring-boot-starter
│   ├── pom.xml
│   └── src
│       ├── main
│       │   ├── java
│       │   └── resources
│       └── test
│           └── java
└── pom.xml

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

本文由西魏陶渊明版权所有。如若转载,请注明出处:西魏陶渊明
上次编辑于: 2022年8月22日 09:35
贡献者: lxchinesszz