3、grpc和protobuf入门

一、grpc和protobuf简介

  • 什么是grpc:gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持
  • grpc官网地址https://github.com/grpc/grpc
  • grpc-go官网地址:github搜索“grpc-go” -> All GitHub;https://github.com/grpc/grpc-go
    在这里插入图片描述
    在这里插入图片描述
  • grpc通信
    在这里插入图片描述
  • 什么是protobuf:Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存储格式,性能比 Json、XML 真的强!太!多!;protobuf经历了protobuf2和protobuf3,pb3比pb2简化了很多,目前主流的版本是pb3
  • protobuf优缺点
    在这里插入图片描述

二、grpc环境搭建


三、go使用protobuf

1 - go简单使用protobuf

  • 新建一个proto文件:helloworld.proto(注意后缀是proto)
syntax = "proto3";
package proto; //当前包名
option go_package = "./";

message HelloRequest{
  string  name = 1; // 1是编号不是值
}
  • 打开命令行
    在这里插入图片描述
  • cmd下执行命令生成go文件protoc --go_out=. *.proto
    在这里插入图片描述
  • main.go测试:注意proto生成的时候helloworld.pb.go包名是package __
package main

import (
	"encoding/json"
	"fmt"

	"github.com/golang/protobuf/proto"

	"MyTestProject/helloworld/proto"
)

type Hello struct {
	Name string `json:"name"`
}

func main() {
	req := __.HelloRequest{
		Name: "test",
	}
	rsp, _ := proto.Marshal(&req)
	fmt.Println("proto len = ", len(rsp))

	jsonStruct := Hello{Name: "test"}
	jsonRsp, _ := json.Marshal(jsonStruct)
	fmt.Println("json len = ", len(jsonRsp))
}

在这里插入图片描述

2 - 进一步对比protobuf和json

  • helloworld.proto
syntax = "proto3";
package proto; //当前包名
option go_package = "./";

message HelloRequest{
  string  name = 1; // 1是编号不是值
  int32 age = 2;
  repeated string courses = 3; //repeated代表可重复的,切片
}
  • main.go
package main

import (
	"encoding/json"
	"fmt"

	"github.com/golang/protobuf/proto"

	"MyTestProject/helloworld/proto"
)

type Hello struct {
	Name    string   `json:"name"`
	Age     int      `json:"age"`
	Courses []string `json:"courses"`
}

func main() {
	req := __.HelloRequest{
		Name:    "test",
		Age:     18,
		Courses: []string{"go", "gin", "微服务"},
	}
	rsp, _ := proto.Marshal(&req)
	fmt.Println("proto len = ", len(rsp))

	jsonStruct := Hello{
		Name:    "test",
		Age:     18,
		Courses: []string{"go", "gin", "微服务"},
	}
	jsonRsp, _ := json.Marshal(jsonStruct)
	fmt.Println("json len = ", len(jsonRsp))
}

在这里插入图片描述

3 - proto的序列化与反序列化

package main

import (
	"fmt"

	"github.com/golang/protobuf/proto"

	"MyTestProject/helloworld/proto"
)

func main() {
	req := __.HelloRequest{
		Name:    "test",
		Age:     18,
		Courses: []string{"go", "gin", "微服务"},
	}
	rsp, _ := proto.Marshal(&req)
	fmt.Println("proto len = ", len(rsp))

	// 反序列化proto
	newReq := __.HelloRequest{}
	_ = proto.Unmarshal(rsp, &newReq)
	fmt.Println(newReq.Name, newReq.Age, newReq.Courses)
}

在这里插入图片描述

4 - grpc的proto编写与生成

  • grpc的proto文件:helloworld.proto
syntax = "proto3";
package proto; //当前包名
option go_package = "./";

service Hello{
  rpc Hello(HelloRequest) returns (Response); // hello接口
}

message HelloRequest{
  string  name = 1; // 1是编号不是值
  int32 age = 2;
  repeated string courses = 3; //repeated代表可重复的,切片
}

message Response{
  string reply = 1;
}
  • grpc生成:这个与之前的非grpc的相似,但是生成的命令需要修改为protoc --go-grpc_out=. *.proto(这个就是为什么在grpc环境搭建中我们需要下载与修改protoc-gen-go-grpc.exe这个文件)
    在这里插入图片描述

四、go的grpc快速体验

  • helloworld.proto
    • 生成命令protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto
    • 注意最新版本的grpc将pb.go拆分成2份文件,分别是proto -> helloworld.pb.go,grpc -> helloworld_grpc.pb.go
      在这里插入图片描述
syntax = "proto3";
option go_package = ".;proto";

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
  • server:注意最新版本的grpc需要在Server的结构中需要带上proto.UnimplementedGreeterServer否则会报错
package main

import (
	"context"
	"net"

	"google.golang.org/grpc"

	"test_project/grpc_test/proto"
)

type Server struct{
	proto.UnimplementedGreeterServer
}

func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,
	error) {
	return &proto.HelloReply{
		Message: "hello " + request.Name,
	}, nil
}

func main() {
	g := grpc.NewServer()
	proto.RegisterGreeterServer(g, &Server{})
	lis, err := net.Listen("tcp", "0.0.0.0:8088")
	if err != nil {
		return
	}
	err = g.Serve(lis)
	if err != nil {
		return
	}
}

  • client
package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"

	"test_project/grpc_test/proto"
)

func main() {
	conn, err := grpc.Dial("127.0.0.1:8088", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		return
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "test"})
	if err != nil {
		return
	}
	fmt.Println(r.Message)
}

在这里插入图片描述


五、grpc的流模式

  • grpc有四种流模式

    • 简单模式(Simple RPC):这种模式最为传统,即客户端发起一次请求,服务端响应一个数据,这和大家平时熟悉的RPC没有什么大的区别,所以不再详细介绍
    • 服务端数据流模式(Server-side streaming RPC):这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端。
    • 客户端数据流模式(Client-side streaming RPC):与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数
      据流,而在发送结束后,由服务端返回一个响应。典型的例子是物联网终端向服务器报送数据(比如热感应装置要一直把温度发送给服务端)。
    • 双向数据流模式(Bidirectional streaming RPC): 顾名思义,这是客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互。典型的例子是聊天机器人。
  • 项目结构
    在这里插入图片描述

  • stream.proto

syntax = "proto3";

option go_package = ".;proto";

service Greeter {
  rpc GetStream(StreamReqData) returns (stream StreamResData); //服务端流模式
  rpc PutStream(stream StreamReqData) returns (StreamResData); //客户端流模式
  rpc AllStream(stream StreamReqData) returns (stream StreamResData); //双向流模式
}

message StreamReqData {
  string data = 1;
}

message StreamResData {
  string data = 1;
}
  • server.go
package main

import (
	"fmt"
	"net"
	"sync"
	"time"

	"google.golang.org/grpc"

	"test_project/stream_grpc_test/proto"
)

const PORT = ":50052"

type server struct {
	proto.UnimplementedGreeterServer
}

func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error {
	i := 0
	for {
		i++
		_ = res.Send(&proto.StreamResData{
			Data: fmt.Sprintf("%v", time.Now().Unix()),
		})
		time.Sleep(time.Second)
		if i > 10 {
			break
		}
	}

	return nil
}

func (s *server) PutStream(cliStr proto.Greeter_PutStreamServer) 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.StreamResData{Data: "我是服务器"})
			time.Sleep(time.Second)
		}
	}()

	wg.Wait()
	return nil
}

func main() {
	lis, err := net.Listen("tcp", PORT)
	if err != nil {
		return
	}
	s := grpc.NewServer()
	proto.RegisterGreeterServer(s, &server{})
	err = s.Serve(lis)
	if err != nil {
		return
	}
}

  • client.go
package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc/credentials/insecure"
	"sync"
	"time"

	"google.golang.org/grpc"

	"test_project/stream_grpc_test/proto"
)

func main() {
	conn, err := grpc.Dial("localhost:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		return
	}
	defer conn.Close()
	//服务端流模式
	c := proto.NewGreeterClient(conn)
	res, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "学习"})
	for {
		a, err := res.Recv() //如果大家懂socket编程的话就明白 send recv
		if err != nil {
			fmt.Println(err)
			break
		}
		fmt.Println(a.Data)
	}

	//客户端流模式
	putS, _ := c.PutStream(context.Background())
	i := 0
	for {
		i++
		_ = putS.Send(&proto.StreamReqData{
			Data: fmt.Sprintf("学习%d", i),
		})
		time.Sleep(time.Second)
		if i > 10 {
			break
		}
	}

	//双向流模式
	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()
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无休止符

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值