grpc 是一个高性能、开源和通用的 RPC 框架,面向服务端和移动端,基于 HTTP/2 设计。目前支持c、java和go,分别是grpc、grpc-java、grpc-go,目前c版本支持c、c++、node.js、ruby、python、objective-c、php和c#。grpc官网 grpc-go
ProtoBuf(全称Protocol Buffer)是数据结构序列化和反序列化框架,ProtoBuf是Google推出的一款轻量高效的数据化数据存储格式,性能比json、xml强,ProtoBuf经历了ProtoBuf2和ProtoBuf3,ProtoBuf3比ProtoBuf简化了很多,目前主流的是ProtoBuf3.
优点:
1.性能:压缩性好、序列化和反序列化快(比xml和json快2-100倍)、传输速度快
2.便捷性:使用简单(自动生成序列化和反序列化代码)、维护成本低(只支持proto文件)、向后兼容(不必破坏旧格式)、加密型号
3.跨语言:跨平台、支持各种主流语言
缺点:
1.通用性差:json可以任何语言都支持,但是protobuf需要专门的解析库
2.自解释性差:只有通过proto文件才能了解数据结构
protoBuf安装并配置环境变量官网
1.编辑环境变量文件:vim ~/.zshrc
2.添加环境变量:
export PATH="$PATH:/usr/local/go/src/GolangStudy/protoc-28.2-osx-x86_64/bin"
3.使配置生效:保存并关闭编辑器后,在终端中输入source ~/.zshrc
命令,使更改生效。
4.验证
protoc --version
安装protoBuf的go依赖包
go get github.com/golang/protobuf/protoc-gen-go
helloworld.proto
syntax = "proto3";
// 生成 proto 文件所在包路径
package protos;
// 影响go文件生成位置和包名
option go_package = "GolangStudy/Introduction/grpc/protos";
message HelloRquest{
string name=1;//1是编号不是值
}
项目目录结构
转换命令(第一种命令使用grpc,会比第二种多很多)
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld.proto
protoc --go_out=/usr/local/go/src/GolangStudy/GolangStudy --proto_path=/usr/local/go/src/GolangStudy/GolangStudy/Introduction/grpc/protos --go_opt=module=GolangStudy helloworld.proto
调用并比较跟json格式的区别
package main
import (
"GolangStudy/Introduction/grpc/protos"
"encoding/json"
"fmt"
"github.com/golang/protobuf/proto"
)
type Hello struct {
Name string `json:"name"`
}
func main() {
req := protos.HelloRquest{
Name: "bobby",
}
jsonStruct := Hello{Name: "bobby"}
jsonRsp, _ := json.Marshal(jsonStruct)
fmt.Println(len(string(jsonRsp)))
rsp, _ := proto.Marshal(&req)
fmt.Println(len(string(rsp)))
}
json长度是15,而protobuf长度是7
grpc四种数据流
简单模式(simple rpc):客户端发起一次请求,服务端响应一个数据
服务端数据流模式(server-side streaming rpc):客户端发起一次请求,服务端返回一段连续的数据流。(客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端)
客户端数据流模式(client-side streaming rpc):客户端源源不断的项向服务端发送数据流,而在发送结束后,由服务器返回一个响应(物流网终端向服务器报送数据)
双向数据流模式(bidirectional streaming rpc):客户端和服务端都可以向双方发送数据流,双方的数据可以同时互相发送,可以实现实时交互(聊天机器人)
grpc简单模式
目录结构
proto代码
syntax = "proto3";
// 生成proto文件所在包路径
package protos;
option go_package = ".;proto";
// 影响go文件生成位置和包名
service Greeter{
rpc SayHello(HelloRquest)returns(HelloReply);//hello接口
}
message HelloRquest{
string name=1;//1是编号不是值
}
message HelloReply{
string message=1;
}
生成go文件
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld.proto
使用接口删除这一行(可能是生成方式的问题)
server端
package main
import (
"GolangStudy/Introduction/grpc/example2/proto"
"context"
"net"
"google.golang.org/grpc"
)
type Server struct {
}
func (s *Server) SayHello(ctx context.Context, request *proto.HelloRquest) (*proto.HelloReply, error) {
return &proto.HelloReply{
Message: "helo" + request.Name,
}, nil
}
func main() {
g := grpc.NewServer()
proto.RegisterGreeterServer(g, &Server{})
lis, err := net.Listen("tcp", "0.0.0.0:8080")
if err != nil {
panic("failed to listen:" + err.Error())
}
err = g.Serve(lis)
if err != nil {
panic("failed to start ")
}
}
client端
package main
import (
"GolangStudy/Introduction/grpc/example2/proto"
"context"
"fmt"
"google.golang.org/grpc"
)
func main() {
conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
if err != nil {
panic(err)
}
defer conn.Close()
c := proto.NewGreeterClient(conn)
r, err := c.SayHello(context.Background(), &proto.HelloRquest{
Name: "bobby",
})
if err != nil {
panic(err)
}
fmt.Println(r.Message)
}
grpc流模式
proto
syntax = "proto3";
// 生成proto文件所在包路径
package protos;
option go_package = ".;proto";
// 影响go文件生成位置和包名
service Greeter{
rpc GetStream(StreamReqData)returns(stream SteramResData);//服务端流模式
rpc PostStream(stream StreamReqData)returns(stream SteramResData);//客户端流模式
rpc AllStream(stream StreamReqData)returns(stream SteramResData);//双向流模式
}
message StreamReqData{
string data=1;
}
message SteramResData{
string data=1;
}
生成go文件
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative stream.proto
删除
server
package main
import (
"GolangStudy/Introduction/grpc/stream_grpc_test/proto"
"fmt"
"net"
"sync"
"time"
"google.golang.org/grpc"
)
const PORT = ":50052"
type server struct {
}
func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error {
i := 0
for {
i++
_ = res.Send(&proto.SteramResData{
Data: fmt.Sprintf("%v", time.Now().Unix()),
})
time.Sleep(time.Second)
if i > 10 {
break
}
}
return nil
}
func (s *server) PostStream(cliStr proto.Greeter_PostStreamServer) error {
for {
if a, err := cliStr.Recv(); err != nil {
fmt.Println(err)
break
} else {
fmt.Println(a.Data)
}
}
return nil
}
func (s *server) AllStream(allStr proto.Greeter_AllStreamServer) error {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
for {
data, _ := allStr.Recv()
fmt.Println("收到客户端消息:" + data.Data)
}
}()
go func() {
defer wg.Done()
for {
allStr.Send(&proto.SteramResData{
Data: "我是服务器",
})
time.Sleep(time.Second)
}
}()
wg.Wait()
return nil
}
func main() {
lis, err := net.Listen("tcp", PORT)
if err != nil {
panic(err)
}
s := grpc.NewServer()
proto.RegisterGreeterServer(s, &server{})
err = s.Serve(lis)
if err != nil {
panic("failed to start ")
}
}
client
package main
import (
"GolangStudy/Introduction/grpc/stream_grpc_test/proto"
"context"
"fmt"
"sync"
"time"
"google.golang.org/grpc"
)
func main() {
conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithInsecure())
if err != nil {
panic(err)
}
defer conn.Close()
//服务端流模式
// c := proto.NewGreeterClient(conn)
// res, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "mooc"})
// for {
// a, err := res.Recv() //socket编程send recv
// if err != nil {
// fmt.Println(err)
// break
// }
// fmt.Println(a)
// }
// //客户端流模式
// c := proto.NewGreeterClient(conn)
// putS, _ := c.PostStream(context.Background())
// i := 0
// for {
// i++
// _ = putS.Send(&proto.StreamReqData{
// Data: fmt.Sprintf("mooc%d", i),
// })
// time.Sleep(time.Second)
// if i > 10 {
// break
// }
// }
//双向流模式
c := proto.NewGreeterClient(conn)
allStr, _ := c.AllStream(context.Background())
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
for {
data, _ := allStr.Recv()
fmt.Println("收到服务器消息:" + data.Data)
}
}()
go func() {
defer wg.Done()
for {
allStr.Send(&proto.StreamReqData{
Data: "我是客户端",
})
time.Sleep(time.Second)
}
}()
wg.Wait()
}