最近调研新项目的技术方案和旧项目的优化方案,接触到几个比较有意思的概念,一些经验之谈分享给大家。网上的资料多数集中在应用层面比较碎片化,这里从是什么怎么回事开始慢慢梳理。
文章会分为两部分,从概念,与http,restful等几个容易混淆的概念的对比,js实现,后端node实现等几个方面做说明。我接触这个东西的时间也不长,涉及的方方面面又非常广泛,如果有错误或者纰漏欢迎大家在评论区讨论。
protocol buffers
后边简称protobuf,是谷歌出的一种数据格式,用于在不同应用,不同语言之间进行通信。类比概念json,xml其实都是数据格式,只不过protobuf是二进制或者base64,更短而已,但是用起来会更加麻烦些。
官方文档:Protocol Buffers | Google Developers
js强推这篇文章(麻烦先通读这篇文章再往后看):Protocol Buffers 在前端项目中的使用 - 卢沟晓月 - 博客园 (cnblogs.com)
这里说下大概步骤
- 安装protoc编译器,作用是将xxx.proto文件编译为各种语言的实现的编译器。之前说过protobuf是为了跨语言跨平台的,所以编译工作也是重要的一环。
- 准备xxx.proto文件,这个是数据格式定义文件,类似接口文档,但是除了给人看,更重要的是给protobuf编译器看,这是为了跨语言跨平台非常重要的一步。一个非常非常简单的例子
// class.proto
syntax = "proto3";
package school;
message PBClass {
uint64 classId = 1;
string name = 2;
}
-
编译,xxx.proto不能被各种语言直接使用,所以需要编译器将xxx.proto文件编译为各种语言的能够使用的定义数据类型的类(也就是class.proto->class.java,class.php,class.js这种)。。。这里还是去查具体语言和库的资料吧,因为根据语言不同,用的插件不同编译命令也不一样。拿前端举例,如果你用的是官方google-protobuf那么命令大概是'protoc --js_out=xxx'这个样子,如果你用protobufjs这个社区库,那么库中就已经提供了一个对于原生命令的封装,如'pbjs -t json-module -w commonjs xxx'这个样子,如果你用了ts-proto这个库,那么命令又会是'protoc --ts_proto_opt=esModuleInterop=true --plugin=protoc-gen-ts_proto'这种,后边我 会在项目代码中告知具体的库和命令
-
编译完成后就会生成一个对应语言的文件,如js会生成class.js文件。例子用的是protobufjs这个前端库
/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/
"use strict";
var $protobuf = require("protobufjs/light");
var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $protobuf.Root()))
.addJSON({
school: {
nested: {
PBClass: {
fields: {
classId: {
type: "uint64",
id: 1
},
name: {
type: "string",
id: 2
}
}
}
}
}
});
module.exports = $root;
rpc
强烈推荐这篇文章:RPC简介及框架选择 - 简书 (jianshu.com)
你肯定知道restful规范,那你也很快能理解rpc。rpc(远程过程调用)。我们举个简单的例子: 有一台电脑A,上边有个web服务器,用go写了个方法getA(),返回了一个数据。我们想获得这个数据如何办到。大家肯定会说,这还不简单,发个请求给服务器,服务器调用这个方法,然后得到数据再返回给前端。对,没错。但是我们把这个过程规范化统一化些。
当当当。。。restful
- 我们规定获取数据必须用http的get方法
- 要获取的数据用url路径标识
- 通过网络将请求发送给后端
- 后端根据请求的方法,和路径判断前端到底要干什么
- 返回数据 这就是restful的简单理解 那rpc是什么呢,简单来说就是前后端的方法名保持一致,参数保持一致,然后前端调用这个方法相当于后端调了这个方法。。。没了。。。就是这么个东西。
那肯定有人会问
- 前端调用了什么方法后端怎么知道呢
- 前端怎么知道后端有哪些方法呢 问题1,发请求呗,把方法名发过去。。。。。。是不是感觉智商被侮辱了。但是这就是协议的目的,就是大家都默认的达成一致的东西,放之四海皆准的东西称作协议或者规范 问题2,前后端都用一个文件定义方法名呗。。。。。。是不是突然想到了刚才说的什么东西各中语言都能用,对的,prto文件,这就是grpc。当然我们也可以实现的稍微low一点,这个文件都不需要,直接口头协商。。。。。
最后总结一下rpc就是
- 前后端协商好方法名如getA
- 前端发送请求将方法名,参数等信息一某种数据格式发送给后端,如json字符串,或者protobuf的二进制
- 后端拿到方法名和参数调用对应方法
- 返回数据 远程(请求)过程(方法名)调用,就是这个意思
说几个网上几个常见问题的个人理解
-
rpc和http有什么关系 没关系,真没关系,不是一个层面的东西。甚至说restful跟http都没啥关系。只不过是http上有那四个method而已。。。我不用http,直接约跟后端约定我发这个字符串给你'get /class/id',空格前是请求方法,空格后是资源地址也行。。。只是不方便而已。说回到rpc,grpc底层使用的是http2协议,dubbo是自定义报文的tcp协议
-
restful,rpc与protobuf,json,xml是什么关系 简单来说,json那些是数据格式。而rpc那些是与后端协商好的公参。如{"a":1}这个是json,{"func":"getA",arg1:"xxx"}这个就是rpc规范的json,如果rpc规范配合protobuf就是grpc。当然实际用到的rpc框架实现会比这个复杂很多
-
小程序是否支持rpc(这个问题可以先看什么是grpc) 看了上边的问题大家肯定说那肯定没问题啊。。。但是,就我现在的理解(存疑)
这个问题的根源不在于什么是rpc,而在于你用的rpc框架是什么。现在大家肯定不会自定义rpc,一般都使用三方框架,比如grpc里面规定了rpc数据格式长什么样子,请求参数格式,相应参数格式等等,还封装了相关的方法,如数据添加,数据解析,发送请求等等等等。
后端用了grpc,前端也要用grpc这个框架(我不确定不同rpc框架之间是否能混用,尤其是采用同一种数据格式的rpc框架如protobuf这种数据格式),那grpc封装的方法小程序中是否能跑。。。尤其是rpc框架一般都会附带网络请求方法,啊这。。。我们项目还用到了uniapp需要编译。。。这坑就有点多了
grpc
如果通过前边的阅读你知道了protobuf和rpc,那么这两个结合就是grpc,是一个rpc框架
官网:gRPC
啰啰嗦嗦说了很多描述性的东西,希望大家能耐心看完。在下一期会贴很多代码搞定前后端的protobuf和rpc,希望不鸽。
欢迎大家友善指正和讨论。