protobuf和grpc使用

一.Protobuf

1. Protobuf是什么:
    实现数格式序列化的工具,序列化的目的之一是进行网络传输,在传输过程中数据流越小传输速度自然越快,可以整体提升系统性能。

2.文件基本使用编写:
    syntax="proto3"://指定proto版本
    package hello_grpc;//指定默认包名
    option go_package = "/hello_grpc", //指定go中的生成目录

    service 服务的名{rpc 方法}
    请求消息
    响应消息

3.proto文件编写
    syntax = "proto3"; 		// 指定proto版本
    package hello_grpc;     // 指定默认包名
    // 指定golang包名,代码生成到的包下
    option go_package = "/hello_grpc";
    //定义rpc服务
    service HelloService {
      // 定义函数
      rpc SayHello (HelloRequest) returns (HelloResponse) {}
    }
    // HelloRequest 请求内容
    message HelloRequest {
      string name = 1;
      string message = 2;
    }
    // HelloResponse 响应内容
        message HelloResponse{
      string name = 1;
      string message = 2;
    }
4.proto语法:proto中只定义接口方法
    (1)service对应方法
    (2)rpc对应结构体中的方法
    (3)message对应的结构体

5.proto数据类型

(1)基本数据类型及其对应
    .proto Type	解释	Go Type
    double		float64
    float		float32
    int32	使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代	int32
    uint32	使用变长编码	uint32
    uint64	使用变长编码	uint64
    sint32	使用变长编码,这些编码在负值时比int32高效的多	int32
    sint64	使用变长编码,有符号的整型值。编码时比通常的int64高效	int64
    fixed32	总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。	uint32
    fixed64	总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。	uint64
    sfixed32	总是4个字节	int32
    sfixed64	总是8个字节	int64
    bool		bool
    string	一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本	string
    bytes	可能包含任意顺序的字节数据	[]byte

(2)数组类型:repeated关键字 类型 变量
    message ArrayRequest {
      repeated int64 a1 = 1;
      repeated string a2 = 2;
      repeated Request request_list = 3;
    }

(3)键值对map类型:map<基本类型:类型>
	    message MapRequest {
      map<int64, string> m_i_s = 1;
      map<string, bool> m_i_b = 2;
      map<string, ArrayRequest> m_i_arr = 3;

(4)嵌套类型
	message Q1 {
  	mssage Q2{
  			string name2 = 2;
  		}
 	string name1 = 1;
   Q2 q2 = 2;
}

二.grpc

1.gRPC作用:
    微服务之间的通信,不同的服务之间会相互调用,分为客户端和服务端。

2.golang写RPC程序的3个基本条件
(1)结构体字段首字母要大写,可以别人调用
(2)函数名必须首字母大写
(3)函数第一参数是接收参数,第二个参数是返回给客户端的参数,必须是指针类型,还必须有一个返回值error

    func (r *Rect) Perimeter(p Params, ret *int) error {
        *ret = (p.Height + p.Width) * 2
        return nil
    }
3.grpc文件服务端和客户端

(1)服务端编写
    1)	编写一个结构体,用于注册服务
    2)	实现protobuf中的rpc定义的所有方法
    3)	监听端口
    4)	注册服务

(2)客户端编写
    1)	建立连接
    2)	初始化服务
    3)	调用方法

	//服务端
    package main
    import (
        "context"
        "fmt"
        "google.golang.org/grpc"
        "grpc/demo03/duo_proto"
        "net"
    )
    // 编写一个结构体
    type Vserver struct {
        duo_proto.UnimplementedVideoServiceServer  // 必须嵌入
    }
    // 实现proto中的方法
    func (Vserver) Look(ctx context.Context, req *duo_proto.Request) (res                             *duo_proto.Response, err error) {
        fmt.Println(req)//req接收客户端的请求参数
        return &duo_proto.Response{
        Name: "look",  //res响应给客户端的消息
        }, nil
    }
    // 定义一个结构体
    type Oserver struct {
        duo_proto.UnimplementedOrderServiceServer
    }
    func (Oserver) Buy(ctx context.Context, req *duo_proto.Request) (res *duo_proto.Response, err error) {
    // 响应给客户端的消息
    return &duo_proto.Response{
        Name: "buy",
    }, nil
    }
func main() {
    //监听端口
    listen, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    //注册服务
    s := grpc.NewServer()
    duo_proto.RegisterVideoServiceServer(s, &Vserver{})
    duo_proto.RegisterOrderServiceServer(s, &Oserver{})
    err = s.Serve(listen)
    fmt.Println("程序运行在8080")
    if err!=nil{
        panic(err)
    }
}


//客户端
package main

import (
    "context"
    "fmt"
    "grpc/demo03/duo_proto"

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

func main() {
    //建立连接
    conn,err :=grpc.Dial(":8080",grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err!=nil{
        panic(err)
    }
    defer conn.Close()

    //调用方法
    orderClient :=duo_proto.NewOrderServiceClient(conn)
    res,err :=orderClient.Buy(context.Background(),&duo_proto.Request{
        Name:"ff", //客户端请求的参数
    })
    fmt.Println(res,err)

    //调用方法
    videoClient :=duo_proto.NewVideoServiceClient(conn)
    res,err =videoClient.Look(context.Background(),&duo_proto.Request{
        Name:"ff",
    })
    fmt.Println(res,err)
}


4.原生rpc代码编写调用

(1)服务端
 package main

import (
    "log"
    "net/http"
    "net/rpc"
)
//  例题:golang实现RPC程序,实现求矩形面积和周长
type Params struct {
    Width, Height int
}

type Rect struct{}  //定义对象调用方法

// RPC服务端方法,求矩形面积
func (r *Rect) Area(p Params, ret *int) error {
    *ret = p.Height * p.Width
    return nil
}

// 周长
func (r *Rect) Perimeter(p Params, ret *int) error {
    *ret = (p.Height + p.Width) * 2
    return nil
}

// 主函数
func main() {
    // 1.注册一个rect对象的服务
rpc.Register(new(Rect))

    // 2.服务处理绑定到http协议上
rpc.HandleHTTP()

    // 3.监听服务
    err := http.ListenAndServe(":8000", nil)
   
}

(2)客户端:

package main

import (
    "fmt"
    "log"
    "net/rpc"
)

// 传的参数
type Params struct {
    Width, Height int
}

// 主函数
func main() {
    // 1.连接远程rpc服务
    conn, err := rpc.DialHTTP("tcp", ":8000")
 
    // 2.调用方法
    // 面积
    ret := 0
    err2 := conn.Call("Rect.Area", Params{50, 100}, &ret)
   //第一个参数是类.方法名,第二个是传递的参数对象,第三个是指针
}




4.grpc的传输方式:

核心:客户端发送请求,接收响应,服务端接收请求,发送响应

(1)一问一答
	1)Proto文件
	    syntax = "proto3";
    option go_package = "/proto";

    service Simple {
 	     rpc Fun(Request)returns(Response){}
    }
    message Request {
 	     string name = 1;
    }
    message Response {
 	     string Text = 1;
    }
	2)服务端客户端同上

(2)服务端流式传输(流式响应)
	使用场景:
        1.客户端不知道什么时候结束
        2.下载文件

    Proto文件编写
    //基础定义
    syntax="proto3";
    package proto;
    option go_package="./proto"; 

    //请求
    message Request{
        string Name =1;
    }

    //响应
    message Response{
        string Text =1;
    }

    //流式响应
    service Streamres{
        rpc StreamFunc(Request)returns(stream Response){}
    }
    Service编写
	    package main

    import (
        "grpc/demo4/stream_proto/proto"
        "net"
        "google.golang.org/grpc"
        "fmt"
    )

    // 定义服务端结构体
    type StreamresServer struct {
        proto.UnimplementedStreamresServer
    }

    // 实现服务器端流式传输方法
    func (s *StreamresServer) StreamFunc(req *proto.Request, stream         proto.Streamres_StreamFuncServer) error {
        //服务端发送数据
        for i :=0;i<10;i++{
            //发送res结构体
            stream.Send(&proto.Response{
            Text: fmt.Sprintf("第%d轮数据",i),
            })
        }    
        return nil
    }

func main() {
    //监听端口
    listen,err :=net.Listen("tcp",":8080")
    if err !=nil{
        panic(err)
    }
    //注册service服务
    s :=grpc.NewServer()
    proto.RegisterStreamresServer(s,&StreamresServer{})
    s.Serve(listen)
}




Client编写
	package main

import (
    "context"
    "fmt"
    "grpc/demo4/stream_proto/proto"

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

func main() {
    //建立连接
    conn,err :=grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err !=nil{
        panic(err)
    }
    defer conn.Close()
    
    //初始化客户端服务
    client :=proto.NewStreamresClient(conn)

    //调用方法,传输req结构体
    stream,_ :=client.StreamFunc(context.Background(),&proto.Request{
        Name:"张三",
    })
    //接收数据
    for i :=0;i<10;i++{
        res,err :=stream.Recv()
        fmt.Println(res,err)
    }

}


(3)客户端流式传输(原理和服务端流式相反)
    使用案例:上传文件
    service clientstrean{
    rpc UploadFile(stream FileRequest)returns(Response){}
    }

(4)双向流
    使用案例:聊天
    syntax = "proto3";
    option go_package = "/proto";

    message Request {
      string name = 1;
    }
    message Response {
      string Text = 1;
    }

    service BothStream{
      rpc Chat(stream Request)returns(stream Response){}
    }

//服务端
package main

import (
    "fmt"
    "grpc/double_stream/stream_proto/proto"
    "log"
    "net"

    "google.golang.org/grpc"
)

type BothStream struct{
    proto.UnsafeBothStreamServer
}

func (BothStream) Chat(stream proto.BothStream_ChatServer) error {

  for i :=0;i<10;i++{
    req,_ :=stream.Recv()
    fmt.Println("接收到的请求:",req)
    //发送响应
    stream.Send(&proto.Response{
        Text:"客户端返回的text字段",
    })
  }
  return nil
}

func main() {
  listen, err := net.Listen("tcp", ":8080")
  if err != nil {
    log.Fatal(err)
  }
  server := grpc.NewServer()
  proto.RegisterBothStreamServer(server, &BothStream{})

  server.Serve(listen)
}


//客户端
package main

import (
    "context"
    "fmt"
    "grpc/double_stream/stream_proto/proto"

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

func main() {
    //建立连接
    conn, _ := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
     // 初始化客户端
     client := proto.NewBothStreamClient(conn)
     //调用方法,返回操作对象
     stream,_:= client.Chat(context.Background())
     for i := 0; i < 10; i++ {
        
        stream.Send(&proto.Request{
          Name: fmt.Sprintf("第%d次", i),
        })

        response, err := stream.Recv()
        fmt.Println(response, err)
      }
}


三.proto(定义方法等)+grpc(微服务注册方法)结合使用实现微服务调用

1.使用步骤,proto文件的多服务
(1)生成grpc的go文件:文件生成在proto文件定义的go包路径下
	 protoc  --go_out=. --go-grpc_out=. path/to/video.proto(proto文件)

(2)编写服务端客户端文件

2.多proto文件生成与使用
(1)将方法和message进行拆分
(2)分别生成文件到同一个包下
(3)使用方式同上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

skyQAQLinux

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

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

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

打赏作者

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

抵扣说明:

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

余额充值