gRPC 使用 protobuf 通信构建微服务,本文代码:GitHub
本文目录:
微服务架构
单一的代码库
以前使用 Laravel 做 web 项目时,是根据 MVC 去划分目录结构的,即 Controller 层处理业务逻辑,Model 层处理数据库的 CURD,View 层处理数据渲染与页面交互。以及 MVP、MVVM 都是将整个项目的代码是集中在一个代码库中,进行业务处理。这种单一聚合代码的方式在前期实现业务的速度很快,但在后期会暴露很多问题:
- 开发与维护困难:随着业务复杂度的增加,代码的耦合度往往会变高,多个模块相互耦合后不易横向扩展
- 效率和可靠性低:过大的代码量将降低响应速度,应用潜在的安全问题也会累积
拆分的代码库
微服务是一种软件架构,它将一个大且聚合的业务项目拆解为多个小且独立的业务模块,模块即服务,各服务间使用高效的协议(protobuf、JSON 等)相互调用即是 RPC。这种拆分代码库的方式有以下特点:
- 每个服务应作为小规模的、独立的业务模块在运行,类似 Unix 的 Do one thing and do it well
- 每个服务应在进行自动化测试和(分布式)部署时,不影响其他服务
- 每个服务内部进行细致的错误检查和处理,提高了健壮性
二者对比
本质上,二者只是聚合与拆分代码的方式不同。
参考:微服务架构的优势与不足
构建微服务
UserInfoService 微服务
接下来创建一个处理用户信息的微服务:UserInfoService,客户端通过 name 向服务端查询用户的年龄、职位等详细信息,需先安装 gRPC 与 protoc 编译器:
1 | go get -u google.golang.org/grpc |
目录结构
1 | ├── proto |
调用流程
Protobuf 协议
每个微服务有自己独立的代码库,各自之间在通信时需要高效的协议,要遵循一定的数据结构来解析和编码要传输的数据,在微服务中常使用 protobuf 来定义。
Protobuf(protocal buffers)是谷歌推出的一种二进制数据编码格式,相比 XML 和 JSON 的文本数据编码格式更有优势:
读写更快、文件体积更小
它没有 XML 的标签名或 JSON 的字段名,更为轻量,更多参考
语言中立
只需定义一份 .proto 文件,即可使用各语言对应的 protobuf 编译器对其编译,生成的文件中有对 message 编码、解码的函数
对于 JSON
- 在 PHP 中需使用
json_encode()
和json_decode()
去编解码,在 Golang 中需使用 json 标准库的Marshal()
和Unmarshal()
… 每次解析和编码比较繁琐 - 优点:可读性好、开发成本低
- 缺点:相比 protobuf 的读写速度更慢、存储空间更多
对于 Protobuf
- *.proto 可生成 *.php 或 *.pb.go … 在项目中可直接引用该文件中编译器生成的编码、解码函数
- 优点:高效轻量、一处定义多处使用
- 缺点:可读性差、开发成本高
定义微服务的 user.proto 文件
1 | syntax = "proto3"; // 指定语法格式,注意 proto3 不再支持 proto2 的 required 和 optional |
编译 user.proto 文件
1 | protoc 编译器的 grpc 插件会处理 service 字段定义的 UserInfoService |
生成 user.pb.go
1 | package proto |
服务端实现微服务
实现流程
代码参考
1 | package main |
运行监听:
客户端调用
实现流程
代码参考
1 | package main |
运行调用成功:
总结
在上边 UserInfoService 微服务的实现过程中,会发现每个微服务都需要自己管理服务端监听端口,客户端连接后调用,当有很多个微服务时端口的管理会比较麻烦,相比 gRPC,go-micro 实现了服务发现(Service Discovery)来方便的管理微服务,下节将随服务的 Docker 化一起学习。
更多参考:Nginx 的微服务系列教程