国内网络问题,windows使用grpc过程
安装protoc
将写好的proto文件,生成pb.go文件,下载地址https://github.com/protocolbuffers/protobuf/releases,下载下来的是一个zip文件,将文件解压找到/bin/protoc.exe,将其配置到环境变量。在控制台中输入protoc --version,打印出下载protoc版本说明安装正确。
安装grpc
这里因为网络问题,需要从github上拉源码安装,
git clone https://github.com/golang/net.git %GOPATH%/src/golang.org/x/net
git clone https://github.com/golang/text.git %GOPATH%/src/golang.org/x/text
git clone https://github.com/grpc/grpc-go.git %GOPATH%/src/google.golang.org/grpc
git clone https://github.com/protocolbuffers/protobuf-go.git %GOPATH%/src/google.golang.org/protobuf
git clone https://github.com/google/go-genproto.git %GOPATH%/src/google.golang.org/genproto
这里是grpc依赖的,已经编译时依赖的全部资源
安装protoc-gen-go
go get github.com/golang/protobuf/protoc-gen-go
我这里使用的是protoc-gen-gofast(有大佬做过测试,go-fast要快4-7倍,https://studygolang.com/articles/22824)
使用源码安装
git clone https://github.com/gogo/protobuf.git
go install protoc-gen-gofast
至此准备工作已经完成,接下来进行简单的测试
Grpc测试
grpc基于http2.0,再次同时记录一下grpc流模式的使用(http2.0 :1. 多路复用 2.TCP连接 3.二进制代替文本 4.标题压缩 5.服务器推送,grpc也同样具有这些功能)
proto文件
syntax = "proto3";
package proto;
option go_package = "proto";
message HelloReq {
string name = 1;
string content = 2;
}
message HelloResp{
string content = 1;
}
message HelloListResp{
repeated string content = 1;
}
service HelloService {
// 普通调用
rpc Hello(HelloReq) returns (HelloResp){}
// 客户端流模式
rpc HelloClientStream(stream HelloReq) returns (HelloListResp){}
// 服务端流模式
rpc HelloServerStream(HelloReq) returns (stream HelloResp){}
// 双向流模式
rpc HelloStream(stream HelloReq) returns (stream HelloResp){}
}
在proto文件的目录下执行 protoc --gofast_out=plugins=grpc:. *.proto 生成 hello.pb.go 文件。
服务端代码:
package main
import (
"context"
"demo/proto"
"fmt"
"google.golang.org/grpc"
"io"
"net"
)
type HelloServer struct {
}
func (*HelloServer) Hello(ctx context.Context, in *proto.HelloReq) (*proto.HelloResp, error) {
return &proto.HelloResp{
Content: fmt.Sprintf("%v : %v", in.GetName(), in.GetContent()),
}, nil
}
func (*HelloServer) HelloClientStream(stream proto.HelloService_HelloClientStreamServer) error {
var users []string
for {
recv, err := stream.Recv()
//接收完了返回结果并关闭
if err == io.EOF {
return stream.SendAndClose(&proto.HelloListResp{
Content: users,
})
}
if err != nil {
return err
}
users = append(users, fmt.Sprintf(recv.GetName()+" : "+recv.GetContent()))
}
}
func (*HelloServer) HelloServerStream(in *proto.HelloReq, stream proto.HelloService_HelloServerStreamServer) error {
for i := 0; i < 10; i++ {
stream.Send(&proto.HelloResp{
Content: fmt.Sprintf("%v - %v : %v", i, in.GetName(), in.GetContent()),
})
}
return nil
}
func (*HelloServer) HelloStream(stream proto.HelloService_HelloStreamServer) error {
for {
recv, err := stream.Recv()
//接收完了返回
if err == io.EOF {
return nil
}
if err != nil {
return err
}
err = stream.Send(&proto.HelloResp{
Content: fmt.Sprintf("%v : %v", recv.GetName(), recv.GetContent()),
})
if err != nil {
return err
}
}
}
func main() {
listen, err := net.Listen("tcp", ":8001")
if err != nil {
panic(err)
}
s := grpc.NewServer()
proto.RegisterHelloServiceServer(s, &HelloServer{})
err = s.Serve(listen)
if err != nil {
panic(err)
}
}
客户端实现:
package main
import (
"context"
"demo/proto"
"fmt"
"google.golang.org/grpc"
"io"
"strconv"
"time"
)
func main() {
conn, err := grpc.Dial("localhost:8001", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
panic(err)
}
defer conn.Close()
cli := proto.NewHelloServiceClient(conn)
// 普通调用
hello, err := cli.Hello(context.Background(), &proto.HelloReq{
Name: "demo",
Content: "Hello World",
})
if err != nil {
panic(err)
}
fmt.Println(hello.GetContent())
// 客户端流模式测试
client, err := cli.HelloClientStream(context.Background())
if err != nil {
panic(err)
}
for i := 0; i < 5; i++ {
client.Send(&proto.HelloReq{
Name: "world" + strconv.Itoa(i),
Content: "hello",
})
}
client.CloseSend()
recv, err := client.CloseAndRecv()
if err != nil {
panic(err)
}
for i := range recv.Content {
fmt.Println(recv.GetContent()[i])
}
// 服务端流模式测试
stream, err := cli.HelloServerStream(context.Background(), &proto.HelloReq{
Name: "hello",
Content: "world",
})
for {
resp, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
} else {
panic(err)
}
}
fmt.Println(resp.Content)
}
// 双向流模式测试
helloStream, err := cli.HelloStream(context.Background())
if err != nil {
panic(err)
}
go func() {
for {
resp, err := helloStream.Recv()
if err != nil {
if err == io.EOF {
break
} else {
panic(err)
}
}
fmt.Println(resp.Content)
}
}()
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
fmt.Println("send", i)
helloStream.Send(&proto.HelloReq{
Name: "hello" + strconv.Itoa(i),
Content: "world",
})
}
helloStream.CloseSend()
time.Sleep(time.Second)
}
目录结构如下: