快速上手gRPC与protobuf:从入门到实战
快速上手gRPC与protobuf:从入门到实战
gRPC是一种高性能、开源和通用的RPC框架,它基于HTTP/2协议标准设计,支持多种编程语言。而protobuf是一种数据序列化协议,用于在不同系统之间传输数据。本文将从微服务架构出发,介绍gRPC和protobuf的基本概念,并通过一个具体的实例,帮助读者快速上手gRPC和protobuf的开发。
一、微服务
在微服务架构出现之前,传统的单体架构存在诸多问题:一旦某个服务宕机,可能会导致整个应用无法使用;资源伸缩性差,只能整体应用进行伸缩;代码耦合度高,可维护性差。
微服务架构通过将应用拆分为多个小型服务,每个服务独立运行,可以独立部署和扩展。所有请求通过网关进行路由,可以将公共功能集中到网关或单独的服务中,如统一认证中心。
在微服务架构中,服务之间通过网络进行调用,需要使用RPC(远程过程调用)协议。为了提高传输效率,通常会使用自定义协议进行TCP调用。同时,由于服务可能分布在多台机器上,还需要引入服务治理,包括服务发现、服务容错和链路追踪等功能。
二、gRPC介绍
gRPC是一个语言和平台无关的开源RPC框架,支持多种语言的客户端和服务端交互。它默认使用Protocol Buffers作为数据序列化协议,可以将数据结构转换为二进制格式进行传输,具有高效、紧凑的特点。
序列化与反序列化
- 序列化:将数据结构或对象转换成二进制串的过程。
- 反序列化:将在序列化过程中所产生的二进制串转换成数据结构或对象的过程。
三、protobuf
protobuf是Google开源的一种数据序列化协议,具有以下特点:
- 二进制格式:需要编码和解码,本身不具有可读性。
- 传输效率高:序列化后的数据比JSON和XML更小,适合网络传输。
- 跨平台支持:支持多种编程语言。
- 快速的序列化和反序列化:性能优于JSON和XML。
protobuf编译器安装
- 下载protobuf编译器(protoc):
- 访问GitHub仓库:https://github.com/protocolbuffers/protobuf/releases
- 下载对应版本并解压,将bin目录添加到环境变量中。
- 安装Go专用的protobuf生成器:
go install github.com/golang/protobuf/protoc-gen-go@latest
protobuf文件编写
创建一个简单的protobuf文件(user.proto):
syntax = "proto3";
option go_package = "../service";
package service;
message User {
string username = 1;
int32 age = 2;
}
使用protobuf编译器生成Go代码:
protoc --go_out=./ .\user.proto
protobuf使用示例
在Go中使用protobuf进行序列化和反序列化:
package main
import (
"fmt"
"google.golang.org/protobuf/proto"
"protobuflearn/service"
)
func main() {
user := &service.User{
Username: "zhou",
Age: 18,
}
// 序列化
marshal, err := proto.Marshal(user)
if err != nil {
panic(err)
}
// 反序列化
newUser := &service.User{}
err = proto.Unmarshal(marshal, newUser)
if err != nil {
panic(err)
}
fmt.Println(newUser.String())
}
protobuf文件结构
- message:定义消息类型,类似于C++中的class或Go中的struct。
- 字段选项:可以使用
required
(默认)、optional
和repeated
来定义字段属性。
消息嵌套
可以定义多个消息类型,并进行嵌套:
message UserInfo {
repeated User info = 1;
}
定义服务
可以在protobuf文件中定义RPC服务:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
四、gRPC实例
gRPC基于HTTP/2协议,支持流式传输和双向流式调用。下面通过一个简单的商品库存查询服务来演示gRPC的使用。
服务端实现
定义protobuf文件(product.proto):
syntax = "proto3"; option go_package = "../service"; package service; message ProductRequest { int32 prod_id = 1; } message ProductResponse { int32 prod_stock = 1; } service ProdService { rpc GetProductStock(ProductRequest) returns (ProductResponse); }
生成Go代码:
protoc --go_out=plugins=grpc:./ .\product.proto
实现服务端逻辑(product.go):
package service import "context" var ProductService = &productService{} type productService struct{} func (p *productService) GetProductStock(ctx context.Context, request *ProductRequest) (*ProductResponse, error) { stock := p.GetStockById(request.ProdId) return &ProductResponse{ProdStock: stock}, nil } func (p *productService) GetStockById(id int32) int32 { return 100 }
编写服务端主程序:
package main import ( "log" "net" "google.golang.org/grpc" "protobuflearn/service" ) func main() { rpcServer := grpc.NewServer() service.RegisterProdServiceServer(rpcServer, service.ProductService) listener, err := net.Listen("tcp", ":8002") if err != nil { log.Fatalf("net.Listen err: %v", err) } rpcServer.Serve(listener) }
客户端实现
- 编写客户端代码:
package client import ( "context" "fmt" "log" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "protobuflearn/client/service" ) func main() { conn, err := grpc.Dial(":8002", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatalf("服务端出错,连接不上: %v", err) } defer conn.Close() productServiceClient := service.NewProdServiceClient(conn) request := &service.ProductRequest{ ProdId: 123, } resp, err := productServiceClient.GetProductStock(context.Background(), request) if err != nil { log.Fatal("调用gRPC方法错误: ", err) } fmt.Println("调用gRPC方法成功,ProdStock = ", resp.ProdStock) }
通过以上步骤,你已经完成了一个简单的gRPC服务的开发。希望这篇文章能帮助你快速入门gRPC和protobuf的开发。