引言

对于后端来说,可能都会从入门的HTTP,到后面的Server RPC阶段,在实践中GRPC确实好用,调用其他服务和调用一个似的,没什么区别,而且相对于HTTP来说,功能更强劲,扩展性更强。

当我们使用gateway作为HTTP—GRPC的转换器后,前端既可以直接通过HTTP访问后端服务的GRPC接口,不需要多开发一个HTTP Server,而且引入GRPC后好处还不止这些。

protocol buffer文件是作为GRPC的接口定义语言Interface Definition Language (IDL),以及传输消息格式定义。那在实际工程中,我们应当充分发挥pb文件的作用,其本身即可拥有三种功能:① 定义接口以及消息的格式的文件;② 可以作为文档,其实时性很高;③ 生成实际代码;

问题

当前后端通信采用RPC通信后,由于总有服务处于最顶层,gateway作为入口,一个错误从底层调用到顶层,结果就是错误不知道是那一层带入的了。这真是一个痛点,不只是在rpc服务中存在,HTTP服务中照样不可避免的有着这些问题。

那这个时候就应该从工程性的角度去解决这个问题了。

Error的分类

Error分类

以上概念大多出自dave的博客:dont-just-check-errors-handle-them-gracefully

dave不仅是pkg/errors包的开发者,也是Go小组的成员,所以在Go Error的发展进程中,都可以看到他对error处理的思想和预设的轨迹。

gRPC的Error设计

kratos和Go-Micro中有一种Error的设计,也就是将Error返回值也定义为pb文件,让其参与协议的传输。

Server——> gRPC——>Server,类似于这样的传输。

1
2
3
4
5
6
message Error {
int32 code = 1;
string reason = 2;
string message = 3;
map<string, string> metadata = 4;
};

code对应了HTTP Code以及gRPC的错误码。google.golang.org\grpc\codes\codes.go

gRPC对应HTTP

reason可以是自己server定义的error。

1
2
3
4
5
6
enum ErrorReason {
// Do not use this default value.
ERROR_REASON_UNSPECIFIED = 0;
// The request is calling a disabled service for a consumer.
SERVICE_DISABLED = 1;
}

message则是具体错误,metadata则是error传递过程中可能会夹带的一些其他数据。

通过这样的设计,就可以将错误锁在pb文件中,而且,每一层都有自己对错误翻译,这样错误有着连续性,顶层server可以直接对接前端,让前端报错。

gRPC pb文件管理

pb文件最好使用一个大仓库包起来,这样前端后端查询的时候都可以直接从里面找到文件,需要什么服务就拉取什么,查询错误码也会非常方便。

不过这里肯定会引发权限问题,这个就需要进行gitlab的扩展了。

gitlab设置

显然,其在具体的实施中会碰到一些问题,但实现之后管理pb文件将会比较方便。