域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

Go 开发 gRPC 服务端和客户端

Go 开发 gRPC 需要两个protoc插件和一个代码依赖。

  • 插件:protoc-gen-go:用于将 proto 生成 Go 语言的数据结构
  • 插件:protoc-gen-go-grpc:用于生成 gRPC 相关的代码
  • 代码依赖:google.golang.org/grpc

安装插件

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

生成代码

mkdir -p ./pb && \
protoc --go_out=./pb --go_opt=paths=source_relative \
--go-grpc_out=./pb --go-grpc_opt=paths=source_relative \
--proto_path=../proto helloworld.proto

由于本项目结构特殊,所以采用了特定的生成命令。更常用的命令参数,可供参考:

protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pb/helloworld.proto

由于本项目结构特殊,所以采用了特定的生成命令。更常用的命令参数,可供参考:

protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pb/helloworld.proto

安装依赖

你可以手动安装依赖:

go get -u google.golang.org/grpc

更推荐的是,使用 go mod 来自动管理依赖:

服务端

server/main.go中编写服务端代码。

首先,实现自己的 MyGreeter服务:

type MyGreeter struct {
	pb.UnimplementedGreeterServer
}

func (s *MyGreeter) SayHello(c context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "[Go] Hello " + in.Name}, nil
}
func main() {
	addr := ":9527"
	listen, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatal("Error listening", err)
		return
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &MyGreeter{})
	log.Println("[GO] Server listen at:", addr)
	if err := s.Serve(listen); err != nil {
		log.Fatal("Error Serveing", err)
		return
	}
}
package main

import (
	"context"
	"helloworld_go/pb"
	"log"
	"net"

	"google.golang.org/grpc"
)

type MyGreeter struct {
	pb.UnimplementedGreeterServer
}

func (s *MyGreeter) SayHello(c context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "[Go] Hello " + in.Name}, nil
}

func main() {
	addr := ":9527"
	listen, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatal("Error listening", err)
		return
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &MyGreeter{})
	log.Println("[GO] Server listen at:", addr)
	if err := s.Serve(listen); err != nil {
		log.Fatal("Error Serveing", err)
		return
	}
}

客户端

打开 client/go 来编写客户端代码:

package main

import (
	"context"
	"helloworld_go/pb"
	"log"
	"os"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	grpc_server_addr := os.Getenv("GRPC_SERVER")
	if grpc_server_addr == "" {
		grpc_server_addr = "127.0.0.1:9527"
	}
	conn, err := grpc.Dial(grpc_server_addr, grpc.WithTransportCredentials(insecure.NewCredentials())) // 本地测试没有 TLS, 禁用 TLS
	if err != nil {
		log.Fatal("Dial error: ", err)
	}
	defer conn.Close()

	c := pb.NewGreeterClient(conn)

	r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "张三"})
	if err != nil {
		log.Fatal("SayHello error: ", err)
	}
	log.Println("Greeting:", r.GetMessage())
}

同样的,为了连接不同的服务端,我们的客户端通过环境变量 GRPC_SERVER 来指定服务端地址。

启动客户端:

go run client/main.go

指定环境变量来启动客户端:

GRPC_SERVER=127.0.0.1:9527 go run client/main.go
要查看完整内容,请先登录