Go 三件套及作用,ORM、RPC 讲解 | 青训营笔记

239 阅读8分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 3 天

Go 三件套及作用,ORM、RPC 讲解 | 青训营笔记

今天直播课讲了 Go 三件套,不过比起知道怎么使用框架,我觉得知道框架的应用场景更重要。

Gorm

Gorm 是什么?它在 Github 上的介绍是这样说的:"The fantastic ORM library for Golang",即 Go 语言的优秀 ORM 库。

那么问题又来了,什么是 ORM?

ORM 全称 "Object Relational Mapping",翻译过来就是“对象关系映射”。“对象”指的是什么?没错,就是我们经常所说的面向对象编程里的对象。关系是什么?我认为就是关系型数据库。那么 ORM 的作用就可以理解为是在代码里的对象和关系型数据库里的数据之间打通了一个通道,方便我们将它们之间互相转换。

可能我说得有点不清楚,接下来我讲一下有 ORM 和没有 ORM 的操作上的区别,可能会比较好理解。

我们知道,一般使用例如 SQLite、MySQL 之类的关系型数据库进行增删改查等操作时,不可避免的就是要编写 SQL 语句。而说到 SQL,毕竟再怎么说,它也是一种编程语言,而且还和我们平常学的高级语言不太一样。也就是说,需要一定的学习成本。

而在使用的时候,我们还需要编写一段字符串,然后把这段字符串,输入到数据库中。不知道各位有没有过这样的感觉,不过我是觉得这样以字符串输入命令的方式很虚渺,而且有时候因为没有编辑器的提示也很没底。这还是不谈一不注意就会有 SQL 注入攻击等其他问题的情况了。

这就导致了无论是在编写上使用上,直接使用 SQL 都有一定难度。而就在这时候,ORM,出现了。通过 ORM,我们只需要调用框架给出的方法,就可以不用自己编写 SQL 语句,像写平常的代码一样轻松实现增删改查等的功能。而且只要按规定使用,就可以避免许多因不注意或者根本不知道而导致的问题和漏洞。

当然,ORM 的优势不止这个。

我们知道,面向对象是属于软件工程里的范畴,而关系数据库则是从数学理论发展而来的,两者之间原本是并无关系的。因此如果我们要把一个对象转化为能储存在数据库里的数据,或是把数据库里的数据提取为对象,总是不免要做一番繁琐的转换操作,费时又费力。

而 ORM 则帮助我们解决了这个问题,只需要传入对象,ORM 就可以经过处理之后将相应的数据插入到数据库中,亦或是将查询的结果经过包装后直接返回创建好的对象给我们,方便又快捷。

下图简单说明了上面所提到的 ORM 的部分功能:

ORM.drawio.png

当然,ORM 的优势不只在于这些,不过这里就大概说这么多了。

至于 Gorm,我想如果了解了 ORM 是干什么的了话,那么上手应该也会容易吧。

Kitex

Kitex 是什么?它在 GitHub 上的介绍是这样说的:"Go RPC framework with high-performance and strong-extensibility for building micro-services",一个由于构建微服务的具有高性能和强扩展性的 Go RPC 框架。

那么问题双来了,什么是 RPC?

RPC 全称 "Remote Procedure Call",即远程过程调用。通过这个技术能够让我们调用网络另一端服务器的函数就像本地调用一样方便。

它是怎么做到这点的呢?简单来说,就是把要调用的函数的相关参数序列化成相应的格式发送给服务器另一端的被调用方,被调用方再反序列化接收到的数据,得到参数后经过函数得到结果,再把结果以同样的方式返还给被调用方。只要这个过程足够丝滑,那么就可以感觉像调用本地函数一样了。

整个过程可参考下图:

RPC

同样的,我们再讲一下 RPC 相对于本地调用的一些区别或优势。

其中一点就是 RPC 技术使得同一个工程中使用多个语言变得更加容易,因为传输参数毕竟用的是某种网络协议(这点后面会提到),由于格式统一,那么自然是可以跨语言的,就像 JSON 一样,唯一的问题可能就是得多造轮子。不过如果这样能够充分发挥到某种语言的优势或是写到自己心爱的语言,这点问题或许不大。更何况还有 RPC 框架的存在呢。

RPC 框架是什么?可以看到上图,虽说整个过程就是 RPC,但若是想实现它的话还得经过处理数据和网络通信等操作,还是有点麻烦。那么有没有一种轮子,可以让我们使用起来就像直接从第 1 步到了第 11 步,帮我们把中间的过程都包办下来了呢?答案是肯定的,这种轮子就叫做 RPC 框架,它帮助我们包办了其中第 2 步一直到第 10 步的内容,Kitex 和 gRPC 都是属于 RPC 框架的一种。

嗯,看起来好像明晰了,但真正使用起来的时候又会有其他的问题,拿 gRPC 举例(其他框架也是类似的),可能看着看着文档就又迷惑了:什么是 protobuf ?.proto 文件又是什么啊?这个 IDL 语言又是怎么回事?别急,且让我慢慢道来。

可以想象到的是,RPC 技术的难点主要就在于数据的封装与收发,我们只谈封装这部分,用 JSON 格式固然可以,但有没有更好的方法呢?或者说,能不能再造个轮子呢?Protobuf 就应运而生。

Protobuf 全称为 Protocol Buffers,它更像是一种机制或者标准。按我的理解来说,它与 JSON、XML 等格式的作用是一样的,都是用来交换数据。不过我觉得 protobuf 与它们的区别之一就在于它更偏向于在运行时交换数据使用,而不太适合储存持久化数据。为什么这么说?这就要说说它的运作机制了。

首先我们得明确 .proto 文件是什么?它是用来描述收发数据的数据结构以及要调用的函数,有了这个文件,我们就可以确定我们要调用的远程函数的函数声明,以及它所需要的一些参数和相应的数据结构的定义;而远端也可以由此确定要调用的函数以及参数的值,并确定返回值的定义。其实,也许有个更好的说法,就是接口,这个文件定义了一个或者多个接口。而编写这个文件的语言,就叫做 "IDL",即“接口描述语言”。

值得注意的是,IDL 并不是某个特定的语言,它和 HDL(硬件描述语言)一样,是一类语言的统称。不同的框架用的序列化数据的方式不同,使用的接口描述文件也不同,因此使用的 IDL 也会不同。不过也有像 Kitex 这种支持多种传输格式的框架。

最后,gRPC 框架就会根据 .proto 文件来序列化相应的信息,并传输,接收方同样使用 gRPC 框架来接收,并也通过 .proto 文件来反序列化来得到发送的信息。

相信聪明的你也注意到了,双方是都需要安装有 gRPC 框架,这样岂不是说如果跨语言还得要跨语言轮子?以及双方在本地都要有同样的 .proto 文件,这样要注意同步文件岂不是也很麻烦?我只能说,还真是。不过 gRPC 的轮子谷歌以及造了不少了,覆盖了不少语言,实在不行还有像 Kitex 这种支持多种格式的框架。再没有的话,那还是换框架或者换语言吧,虽说可以自己造,网上也有公开的原理,不过总归还是不妥。而至于同步文件的问题,gRPC 姑且还算有一些防范的机制,具体大家还是可以看看文档。

相信看下来,也能对 RPC 以及相关的术语有所了解了。总的来说,RPC 确实比较方便,但也不是万能的,仍存在着一些限制,切不可无脑使用。

Hertz

这个就不多说了,就是类似于 Python 里的 Django 和 Flask 这样子的一个框架。不过确实也有为微服务优化过,能为一组路由注册中间件感觉也挺方便的。说到这个中间件吧……我觉得说得有点玄乎,但实际上可能无意间就用到过很多了,比如我觉得其实这个鉴权操作,大概可以算是个中间件吧。