0.1、索引
https://waterflow.link/articles/1665674508275
1、什么是 grpc
在 gRPC 中,客户端应用程序能够间接调用不同机器上的服务器应用程序上的办法,就像它是本地对象一样,使您更容易创立分布式应用程序和服务。与许多 RPC 零碎一样,gRPC 基于定义服务的思维,指定能够近程调用的办法及其参数和返回类型。在服务端,服务端实现这个接口并运行一个 gRPC 服务器来解决客户端调用。在客户端,客户端有一个 stub(在某些语言中仅称为客户端),它提供与服务器雷同的办法。
所以 grpc 是跨语言的。
2、什么是 Protocol Buffers
Protocol Buffers 提供了一种语言中立、平台中立、可扩大的机制,用于以向前兼容和向后兼容的形式序列化结构化数据。它相似于 JSON,只是它更小更快,并且生成本地语言绑定。
能够通过 .proto 定义数据结构,而后就能够应用 Protocol Buffers 编译器 protoc 从. proto 定义中生成咱们喜爱的语言的数据拜访类。它们为每个字段提供简略的拜访器,如 name() 和 set_name(),以及将整个构造序列化 / 解析到原始字节 / 从原始字节中提取的办法。
3、grpc 服务端
1、首先咱们须要下载 go 对应的 protoc 插件
go install google.golang.org/protobuf/cmd/[email protected]
go install google.golang.org/grpc/cmd/[email protected]
而后把 GOPATH 放到 PATH
export PATH="$PATH:$(go env GOPATH)/bin"
接着打印下看看有没有进去
echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$GOPATH/bin:/usr/local/go/bin
接着开新窗口,执行上面命令看下 protoc 是否装置胜利
protoc --version
libprotoc 3.19.1
2、接着咱们创立一个 hello.proto
内容如下(不懂构造的可自行百度)
syntax = "proto3";
package helloservice;
option go_package = ".;helloservice"; // 指定包名
message String {string value = 1;}
service HelloService {rpc Hello(String) returns (String); // 一元办法
rpc Channel (stream String) returns (stream String); // 流式办法
}
目录构造如下
.
├── go.mod
├── go.sum
├── helloclient
│ └── main.go
├── helloservice
│ ├── hello.proto
3、接着命令行生成对应语言的类代码
cd helloservice
protoc --go_out=./ --go-grpc_out=./ hello.proto
咱们能够看下当初的目录构造(其余文件目录可疏忽,前面会创立,当初只须要关注 hello_grpc.pb.go)
.
├── go.mod
├── go.sum
├── helloclient
│ └── main.go
├── helloservice
│ ├── hello.pb.go
│ ├── hello.proto
│ ├── hello_grpc.pb.go
│ ├── hello_service.go
│ └── main
│ └── main.go
4、实现本人的 hello service
在下面生成的 hello_grpc.pb.go 中咱们能够看到这样的接口
// HelloServiceServer is the server API for HelloService service.
// All implementations must embed UnimplementedHelloServiceServer
// for forward compatibility
type HelloServiceServer interface {Hello(context.Context, *String) (*String, error)
Channel(HelloService_ChannelServer) error
mustEmbedUnimplementedHelloServiceServer()}
翻译一下就是
// HelloServiceServer 是 HelloService 服务的服务端 API。
// 所有实现都必须嵌入 UnimplementedHelloServiceServer
// 为了向前兼容
所以咱们在 helloservice 中创立一个 hello_service.go 文件,用来实现下面的接口
package helloservice
import (
"context"
"io"
"time"
)
type HelloService struct {
}
func (h HelloService) mustEmbedUnimplementedHelloServiceServer() {panic("implement me")
}
func (h HelloService) Hello(ctx context.Context, args *String) (*String, error) {time.Sleep(time.Second)
reply := &String{Value: "hello:" + args.GetValue()}
return reply, nil
}
func (h HelloService) Channel(stream HelloService_ChannelServer) error {
for {recv, err := stream.Recv()
if err != nil {
if err == io.EOF {return nil}
return err
}
reply := &String{Value: "hello:" + recv.Value}
err = stream.Send(reply)
if err != nil {return err}
}
}
下面的办法和简略,就是打印咱们自定义的字符串。
而后咱们在 main 中编写下服务启动的代码
package main
import (
"google.golang.org/grpc"
"grpcdemo/helloservice"
"log"
"net"
)
func main() {
// NewServer 创立一个 gRPC 服务器,它没有注册服务,也没有开始承受申请。grpcServer := grpc.NewServer()
// 注册服务
helloservice.RegisterHelloServiceServer(grpcServer, new(helloservice.HelloService))
// 开启一个 tcp 监听
listen, err := net.Listen("tcp", ":1234")
if err != nil {log.Fatal(err)
}
log.Println("server started...")
// 在监听器 listen 上承受传入的连贯,创立一个新的 ServerTransport 和 service goroutine。服务 goroutine 读取 gRPC 申请,而后调用注册的处理程序来回复它们。log.Fatal(grpcServer.Serve(listen))
}
而后咱们启动下看下成果
go run helloservice/main/main.go
2022/10/13 23:07:46 server started...
4、grpc 客户端
接着咱们编写客户端的代码 helloclient/main.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"grpcdemo/helloservice"
"io"
"log"
"time"
)
func main() {
// 连贯 grpc 服务端
conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
if err != nil {log.Fatal(err)
}
defer conn.Close()
// 一元 rpc
unaryRpc(conn)
// 流式 rpc
streamRpc(conn)
}
func unaryRpc(conn *grpc.ClientConn) {
// 创立 grpc 客户端
client := helloservice.NewHelloServiceClient(conn)
// 发送申请
reply, err := client.Hello(context.Background(), &helloservice.String{Value: "hello"})
if err != nil {log.Fatal(err)
}
log.Println("unaryRpc recv:", reply.Value)
}
func streamRpc(conn *grpc.ClientConn) {
// 创立 grpc 客户端
client := helloservice.NewHelloServiceClient(conn)
// 生成 ClientStream
stream, err := client.Channel(context.Background())
if err != nil {log.Fatal(err)
}
go func() {
for {
// 发送音讯
if err := stream.Send(&helloservice.String{Value: "hi"}); err != nil {log.Fatal(err)
}
time.Sleep(time.Second)
}
}()
for {
// 接管音讯
recv, err := stream.Recv()
if err != nil {
if err == io.EOF {break}
log.Fatal(err)
}
fmt.Println("streamRpc recv:", recv.Value)
}
}