最近决心开始学习go语言,可是苦于没有实际的应用场景,学习始终停留在hello world层面,看过的教程和资料印象也不深入。因而决定从go自带的rpc实现开始切入,了解一下go语言在实际场景下是如何使用的,包括异常处理、代理和过滤、go routine的用法等等,同时也简单了解了一下其余rpc的go语言实现,好比thrift和grpc等等。一阵蜻蜓点水,稍微加深了印象,也开始慢慢体会到go语言和java语言的种种差别和共性。接下来,为了进一步巩固学习效果,也算是为了对本身目前为止的职业生涯作一次复习和汇报,决定使用go语言从零开始构建一个比较完整的RPC(或者说是微服务)框架。java
微服务框架和RPC框架git
本文中提到RPC框架,指的是提供基础的RPC调用支持的框架;而本文中提到的微服务框架,指的是包含一些服务治理相关的功能(好比服务注册发现、负载均衡、链路追踪等)的RPC框架。github
在动手开始作以前,须要先了解学习一下其余现有的产品,能够从中学习一下优秀的经验和方法,这里列举一下初步了解到的几个框架:apache
以上就是目前了解过的几个已有的框架,比较惭愧的是了解得都不够深刻,后续还要持续学习。设计模式
Pluggable Interfacesbash
值得一提的是除了thrift,其余三个称得上微服务框架的产品,其特性都包含Pluggable Interfaces,也就是能够经过插件替换部分功能。经过插件实现可替换的功能,实际上在一个微服务框架中基本是最低要求了,不然后续的功能扩展将会变得十分困难,相信我,这里是饱含血泪的经验之谈。网络
在开始着手设计甚至是编写代码之前,咱们首先分析一下咱们的需求(来自学习软件工程中的成果)。同时对于一部分可能不太熟悉RPC相关细节的同窗来讲,对咱们后面要作的事情心中也可以有一个大体的概念。这里就直接列举几个功能性需求:负载均衡
有了大体的需求,接下来就能够开始着手设计了。首先咱们将框架划分为若干层,层与层之间约定经过接口交互。这里就不要问为何须要分层了,非要问就是经验。分层做为一种经典到不能在经典的设计模式,几乎在软件开发过程当中无处不在,在RPC框架当中也十分适用,下面画出大体的层次图:框架
上面提到的各个层,除了service,实际上能够提供多种实现,因此应该都以plugin的方式实现。异步
这样一来按照咱们划分的层次,一个客户端从发出请求到收到响应的流程大概就是这样:
经过上面的层次划分能够看到,一个请求或者响应实际上会依次穿过各个层而后经过网络发送或者到达用户逻辑,因此咱们采用相似过滤器链同样的方式处理请求和响应,以此来达到对扩展开放,对修改关闭的效果。这样一来对于一些附加功能好比熔断降级和限流、身份认证等功能均可以在过滤器中实现。
接下来设计具体的消息协议,所谓消息协议大概就是两台计算机为了互相通讯而作的约定。举个例子,TCP协议约定了一个TCP数据包的具体格式,好比前2个byte表示源端口,第3和第4个byte表示目标端口,接下来是序号和确认序号等等。而在咱们的RPC框架中,也须要定义本身的协议。通常来讲,网络协议都分为head和body部分,head是一些元数据,是协议自身须要的数据,body则是上一层传递来的数据,只须要原封不动的接着传递下去就是了。
接下来咱们就试着定义本身的协议:
-------------------------------------------------------------------------------------------------
|2byte|1byte |4byte |4byte | header length |(total length - header length - 4byte)|
-------------------------------------------------------------------------------------------------
|magic|version|total length|header length| header | body |
-------------------------------------------------------------------------------------------------
复制代码
根据上面的协议,一个消息体由如下几个部分严格按照顺序组成:
协议中消息头的数据主要是RPC调用过程当中的元数据,元数据跟方法参数和响应无关,主要记录额外的信息以及实现附属功能好比链路追踪、身份认证等等;消息体的数据则是由实际的请求参数或者响应编码而来。 在实际的处理中,消息头在发送端一般是一个结构体,在发送时会被编码成二进制添加在消息头的前面,在接收端接收时又解码成一个结构体,交给程序进行处理。这里试着列举消息头包含的各个信息:
type Header struct {
Seq uint64 //序号, 用来惟一标识请求或响应
MessageType byte //消息类型,用来标识一个消息是请求仍是响应
CompressType byte //压缩类型,用来标识一个消息的压缩方式
SerializeType byte //序列化类型,用来标识消息体采用的编码方式
StatusCode byte //状态类型,用来标识一个请求是正常仍是异常
ServiceName string //服务名
MethodName string //方法名
Error string //方法调用发生的异常
MetaData map[string]string //其余元数据
}
复制代码
第一篇文章就到此为止了,主要先作一下准备,整理一下思路,若是有不正确或者不合理的部分还请你们多多指教。