grpc 是 google 给出的 rpc 调用方式,它基于 google 的 protobuf 定义方式,提供了一整套数据定义和 rpc 传输的方式
它是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
https://github.com/grpc/grpc
Protobuf 是 Google 给出的一种通用的数据表示方式,通过 proto 文件定义的数据格式,可以一键式的生成 C++,Python,Java 等各种语言实现
protobuf经历了protobuf2和protobuf3,pb3比pb2简化了很多,目前主流的版本是pb3
protobuf 效率较目前主流的json 格式数据快几倍,但是可读性不如json
下载地址: http://github.com/protocolbuffers/protobuf/releases
**注意:**mac下载安装包后面使用google内置的proto会找不到,建议下载压缩文件
https://github.com/protocolbuffers/protobuf/releases/download/v3.20.1/protoc-3.20.1-osx-aarch_64.zip
然后配置环境变量
# mac 把可执行文件加入到环境变量
vim .bash_profile
# proto
PATH=$PATH:/Users/lxx/soft/protoc-21.0-rc-1-osx-x86_64/bin:
设置go proxy 代理,下载速度快
windows
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
mac or linux
export GO111MODULE=on
export GOPROXY=https://goproxy.cn
设置成功可以同过go env查看
参考代理网站: GOPROXY.IO , 七牛云
# 下载完会在 go paht
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
# 运行成功会在go的bin路径下生成两个个可执行文件
protoc-gen-go
protoc-gen-go-grpc
# mac 的话,把该可执行文件复制到/usr/local/bin/路径()
cp /Users/lxx/go/bin/protoc-gen-go /usr/local/bin/protoc-gen-go
cp /Users/lxx/go/bin/protoc-gen-go-grpc /usr/local/bin/protoc-gen-go-grpc
# Goland安装高亮Protocol 的插件
# Plugins中搜索 Protocol Buffers
syntax = "proto3";
// 一定要加go的package路径,否则生成不了,意思是把go文件生成到proto文件夹下
// ;proto 表示生成的go文件,包名为proto
option go_package = "./proto"; // windows 需要加上./ mac不需要
// 类似于go的结构体,可以定义属性
message HelloRequest {
string name = 1; // 定义了name字段,类型是string,编号是1
int32 age =2
}
// 执行命令
protoc --go_out=. --go-grpc_out=. ./helloworld.proto
// 执行完生成一个helloworld.pb.go
在这个go文件中,可以找到咱们定义的结构体
package main
import (
"encoding/json"
"fmt"
"go_test_learn/proto"
pb "github.com/golang/protobuf/proto" // 导入这个模块
)
func main() {
// proto 格式编码解码
req:=proto.HelloRequest{Name: "lxx"}
r,_:=pb.Marshal(&req)
fmt.Println(string(r))
// 解码
req2:=proto.HelloRequest{Name: "lxx"}
pb.Unmarshal(r,&req2)
fmt.Println(req2) // {{{} [] [] 0xc0001182c0} 0 [] lxx 23 []}
// json 格式编码解码
type HelloRequest struct{
Name string
}
h:=HelloRequest{Name: "lxx"}
r1,_:=json.Marshal(&h)
fmt.Println(string(r1))
h1:=HelloRequest{}
json.Unmarshal(r1,&h1)
fmt.Println(h1)
}
syntax = "proto3";
// 一定要加go的package路径,否则生成不了,意思是把go文件生成到proto文件夹下
// ;proto 表示生成的go文件,包名为proto
option go_package = "../proto;proto";
// 类似于go的结构体,可以定义属性
message HelloRequest {
string name = 1; // 1 是编号,不是值
int32 age = 2;
}
重新生成
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/helloworld.proto
package main
import (
"encoding/json"
"fmt"
"go_test_learn/proto"
pb "github.com/golang/protobuf/proto" // 导入这个模块
)
func main() {
// proto 格式编码解码
req := proto.HelloRequest{Name: "lxx", Age: 19}
r, _ := pb.Marshal(&req)
fmt.Println(string(r))
// 解码
req2 := proto.HelloRequest{}
pb.Unmarshal(r, &req2)
fmt.Println(req2)
// json 格式编码解码
type HelloRequest struct {
Name string
Age int32
Girls []string
}
h := HelloRequest{Name: "lxx",Age: 19}
r1, _ := json.Marshal(&h)
fmt.Println(string(r1))
h1 := HelloRequest{}
json.Unmarshal(r1, &h1)
fmt.Println(h1)
}
syntax = "proto3";
option go_package = ".;proto";
// 定义一个服务,gRPC自有的,它需要用grpc插件生成,也就是咱们安装的那个插件
service Hello{
// 服务内有一个函数叫Hello,接收HelloRequest类型参数,返回HelloResponse类型参数
rpc Hello(HelloRequest) returns(HelloResponse);
}
// 类似于go的结构体,可以定义属性
message HelloRequest {
string name = 1; // 1 是编号,不是值
int32 age = 2;
repeated string girls = 3;
}
// 定义一个响应的类型
message HelloResponse {
string reply =1;
}
生成go脚本
// protoc 可执行文件必须加入环境变量
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/helloworld.proto
package main
import (
"context"
"fmt"
"go_test_learn/proto"
"google.golang.org/grpc"
"net"
)
// 定义一个结构体,名字随意,只要实现Hello方法即可,相当于proto的service
type HelloServer struct {
proto.UnimplementedHelloServer
}
// 给结构体绑定Hello方法
// 请求参数必须是两个:context context.Context, request *proto.HelloRequest
// 返回参数必须是两个:*proto.HelloResponse, error
// 自动生成的go文件中结构体中的字段给转成大写字母开头,符合go导出字段规范
func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
fmt.Println(request.Name)
return &proto.HelloResponse{Reply:"ok"}, nil
}
func main() {
// 创建一个grpc对象
g := grpc.NewServer()
s := HelloServer{}
// 把s注册到g对象中
proto.RegisterHelloServer(g,&s)
// 监听端口
lis,error:=net.Listen("tcp","0.0.0.0:50052")
if error!=nil{
panic("启动服务异常")
}
g.Serve(lis)
}
package main
import (
"context"
"fmt"
"go_test_learn/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
// 连接grpc服务端,grpc.WithInsecure()跳过对服务器的验证, 弃用了
//conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithInsecure())
// WithTransportCredentials 生产一个安全连接认证,确保成功连接
// NewCredentials 返回禁用传输安全的凭据
conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
panic("连接服务异常")
}
//defer 关闭
defer conn.Close()
//创建一个客户端,传入连接对象
client := proto.NewHelloClient(conn)
request := proto.HelloRequest{
Name: "lxx",
}
//调用SayHello方法,传入两个参数
res, err := client.Hello(context.Background(), &request)
if err != nil {
panic("调用方法异常")
}
//打印出返回的数据
fmt.Println(res.Reply)
}
-I 或者 --proto_path:用于指定所编译的源码,就是我们所导入的proto文件,支持多次指定,按照顺序搜索,如果未指定,则使用当前工作目录。
--go_out:同样的也有其他语言的,例如--java_out、--csharp_out,用来指定语言的生成位置,用于生成*.pb.go 文件
--go_opt:paths=source_relative 指定--go_out生成文件是基于相对路径的
--go-grpc_out:用于生成 *_grpc.pb.go 文件
--go-grpc_opt:
paths=source_relative 指定--go_grpc_out生成文件是基于相对路径的
require_unimplemented_servers=false 默认是true,会在server类多生成一个接口
--grpc-gateway_out:是使用到了 protoc-gen-grpc-gateway.exe 插件,用于生成pb.gw.go文件
--grpc-gateway_opt:
logtostderr=true 记录log
paths=source_relative 指定--grpc-gateway_out生成文件是基于相对路径的
generate_unbound_methods=true 如果proto文件没有写api接口信息,也会默认生成
--openapiv2_out:使用到了protoc-gen-openapiv2.exe 插件,用于生成swagger.json 文件
// 新版protoc-gen-go不支持grpc服务生成,需要通过protoc-gen-go-grpc生成grpc服务接口,但是生成的Server端接口中会出现一个mustEmbedUnimplemented***方法,是为了解决前向兼容问题,如果不解决,就无法传递给RegisterXXXService方法,解决办法有两个:
1. 在grpc server实现结构体中匿名嵌入Unimplemented***Server结构体
type HelloServer struct {
proto.UnimplementedHelloServer
}
2. 使用protoc生成server代码时命令行加上关闭选项protoc --go-grpc_out=require_unimplemented_servers=false
3. 常用命令
生成 .pb.go 文件
protoc --go_out=. --go_opt=paths=source_relative proto/helloworld.proto
生产 _grpc.pb.go 文件
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false --go-grpc_opt=paths=source_relative proto/helloworld.proto
二合一
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false --go-grpc_opt=paths=source_relative proto/helloworld.proto