写点什么

golang 开发一个简单的 grpc

作者:六月的
  • 2022-10-19
    上海
  • 本文字数:2852 字

    阅读完需:约 1 分钟

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/protoc-gen-go@v1.28go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
复制代码


然后把 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 --versionlibprotoc 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 compatibilitytype 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.go2022/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)
}}
复制代码


用户头像

六月的

关注

还未添加个人签名 2019-07-23 加入

还未添加个人简介

评论

发布
暂无评论
golang开发一个简单的grpc_golang_六月的_InfoQ写作社区