准备工作
当搜索 Go gRPC 的时候肯定会搜到
官网:https://grpc.io/docs/languages/go/quickstart/,内容如下:
简单的一个 Go gRPC 的准备,耗死个人。
第一步
安装 Go 肯定大家都没问题。
第二步
安装 protoc,直接去这里https://github.com/protocolbuffers/protobuf/releases 下载吧,反正这个方法不用纠结啥。
我下载的是 osx-aarch 版本:
解压缩完之后呢,会发现有一个 bin 目录,将这个 bin 目录加到 PATH 里,
然后在终端执行:
$ protoc --version
libprotoc 3.21.0
正常输出说明 protoc 安装成功。
第三步
蛋疼的就是第三步了,执行下面两个命令:
$ 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
一直不成功,说什么 time out,who knows?
真是把我惹急了,那就来最原始的方式吧:下载代码安装。
后知后觉
之前对 Go 的 module 不是很熟悉,为啥 time out,其实是被墙了。
环境变量 GOPROXY
配置的是:https://goproxy.io,direct
,可以加上一个国内的 Go 模块代理:https://goproxy.cn
,执行命令即可:
go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct
再试一下前面那两个 go install
命令看是否好使。不行的话,老老实实按照下面的方式手动安装吧。
安装 protoc-gen-go
clone 这个代码:https://github.com/golang/protobuf 到你的 $GOPATH/src/github.com/golang/
这个目录下,然后切到 protoc-gen-go 目录下执行命令:
go install
简直如丝般顺滑,然后你发现在 $GOPATH/src/bin
目录下多了一个 protoc-gen-go
。
OK,protoc-gen-go 安装成功。
安装 protoc-gen-go-grpc
切换到目录:$GOPATH/src/google.golang.org/
,clone 代码:https://github.com/grpc/grpc-go,执行如下命令:
$ git clone git@github.com:grpc/grpc-go.git grpc
$ cd grpc/cmd/protoc-gen-go-grpc
$ go install
就这样 protoc-gen-go-grpc 也安装好了。
LAST
最后将 export PATH="$PATH:$(go env GOPATH)/bin"
加到你的 .bashrc
或 .zshrc
,然后 source .zshrc
。
至此准备工作结束。
gRPC Hello World
示例取自:https://tutorialedge.net/golang/go-grpc-beginners-tutorial/
项目结构:
➜ grpc-hello-world
├── chat
│ ├── chat.go
│ └── chat.pb.go(proto 文件生成的 go 代码)
├── chat.proto
├── cmd
│ ├── client.go
│ └── server.go
├── gen_go.sh
├── go.mod
创建目录 grpc-hello-word
,并切到 grpc-hello-word
。
使用 go mod init grpc-hello-world
,
就初始化了使用 Go modules 的项目,会多出来一个 go.mod 文件。
chat.proto
syntax = "proto3";
option go_package = "./;chat";
package chat;
message Message {
string body = 1;
string bottom = 2;
}
service ChatService {
rpc SayHello(Message) returns (Message) {}
}
proto 如何生成 Go 代码,折腾了好几个命令,这里我记录一下吧,供大家尝试。
proto -> go 方式 1
如果是使用命令安装的 proto-gen-go 和 protoc-gen-go-grpc:
$ 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
项目根目录执行命令:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=./chat --go-grpc_opt=paths=source_relative \
chat.proto
go-grpc_out 表示将生成 go 代码放到哪个目录。
proto -> go 方式 2
如果是手动方式安装 proto-gen-go 和 protoc-gen-go-grpc,插件版本比较高,可以使用命令:
protoc --go_out=plugins=grpc:./chat chat.proto
–go_out 表示生成 go 代码的地方。
或运行 gen_go.sh
gen_go.sh
#!/usr/bin/env bash
PROJECT_PATH="$PWD"
echo "PROJECT_PATH: ${PROJECT_PATH}"
protoc ${PROJECT_PATH}/*.proto --proto_path=${PROJECT_PATH} \
--go_out=plugins=grpc,paths=source_relative:${PROJECT_PATH}/chat
接口实现 chat.go
package chat
import (
"context"
"log"
)
type Server struct {
}
func (s *Server) SayHello(ctx context.Context, in *Message) (*Message, error) {
log.Printf("Receive message body from client: %s", in.Body)
return &Message{Body: "Hello From the Server!"}, nil
}
client.go
package main
import (
"context"
"google.golang.org/grpc"
"grpc-test-1/chat"
"log"
)
func main() {
var conn *grpc.ClientConn
conn, err := grpc.Dial(":9000", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %s", err)
}
defer conn.Close()
c := chat.NewChatServiceClient(conn)
response, err := c.SayHello(context.Background(), &chat.Message{Body: "Hello From Client!"})
if err != nil {
log.Fatalf("Error when calling SayHello: %s", err)
}
log.Printf("Response from server: %s", response.Body)
}
server.go
package main
import (
"fmt"
"google.golang.org/grpc"
"grpc-test-1/chat"
"log"
"net"
)
func main() {
fmt.Println("Go gRPC Beginners Tutorial!")
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 9000))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := chat.Server{}
grpcServer := grpc.NewServer()
chat.RegisterChatServiceServer(grpcServer, &s)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
大公告成
代码加好了以后,执行命令:
go mod tidy
会自动下载代码中的相关包导入并更新 go.mod
文件。
下面就去启动服务端server.go
,然后运行client.go
玩起来吧。
参考:protoc-gen-go安装的问题
go get dial tcp 172.217.163.49:443: i/o timeout