刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • rpc

    • RPC 初识
    • RPC简单实现
    • grpc初识和实现
      • grpc 和 rpc的区别
      • grpc和protobuf的区别
        • 1. grpc
        • 2. protobuf
      • python 开发 grpc和probuf
        • 1. protobuf体验
        • 2. grpc打印hello word
      • go 开发 grpc
        • 1. 下载编译工具
        • 2. 下载go的依赖包
        • 3. proto文件
        • 4. 生成go文件
        • 5. 服务端代码
        • 6. 客户端
      • python和golang 联调
      • grpc 模式
        • 1. 简单模式
        • 2. 服务端数据流模式
        • 3. 客户端数据流模式
        • 4. 双向数据流模式
      • gRPC的优势
    • protobuf进阶
    • grpc进阶
  • grpc

  • protobuf

  • rpc+grpc
  • rpc
bigox
2022-06-22
目录

grpc初识和实现

# grpc 和 rpc的区别

  • gRPC 是一款高性能、开源的 RPC 框架,产自 Google,基于 ProtoBuf 序列化协议进行开发,支持多种语言(Golang、Python、Java等)。gRPC 提供了一种简单的方法来定义服务,同时客户端可以充分利用 HTTP/2 stream 的特性,从而有助于节省带宽、降低 TCP 的连接次数、节省CPU的使用等。

  • rpc和grpc之间的关系是什么?

    1. rpc是一种协议,grpc是基于rpc协议实现的一种框架。
  • grpc的解决rpc三大问题

    1. 协议约定。gRPC 的协议是 Protocol Buffers,是一种压缩率极高的序列化协议,Google 在 2008 年开源了 Protocol Buffers,支持多种编程语言,所以 gRPC 支持客户端与服务端可以用不同语言实现。

    2. 传输协议。gRPC 的数据传输用的是 Netty Channel, Netty 是一个高效的基于异步 IO 的网络传输架构。Netty Channel 中,每个 gRPC 请求封装成 HTTP 2.0 的 Stream。

    3. 服务发现。gRPC 本身没有提供服务发现的机制,需要通过其他组件。

  • 总结

    grpc是一种实现了rpc协议的框架,并且分别通过protocol buffer、netty channel 以及服务发现组件解决rpc的协议约定、传输协议、服务发现三大问题。

# grpc和protobuf的区别

# 1. grpc

  • 多语言:语言中立,支持多种语言。
  • 轻量级、高性能:序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架。
  • 可插拔
  • IDL:基于文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub。
  • 移动端:基于标准的 HTTP2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量。
  • gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc (opens new window), grpc-java (opens new window), grpc-go (opens new window). 其中 C 版本支持 C (opens new window), C++ (opens new window), Node.js (opens new window), Python (opens new window), Ruby (opens new window), Objective-C (opens new window), PHP (opens new window) 和 C# (opens new window) 支持.
image-20211025223207088

# 2. protobuf

  • 习惯用 Json、XML 数据存储格式的你们,相信大多都没听过Protocol Buffer

  • Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存储格式,性能比 Json、XML 真的强!太!多!

  • protobuf经历了protobuf2和protobuf3,pb3比pb2简化了很多,目前主流的版本是pb3

    image-20211025223315453

# python 开发 grpc和probuf

# 1. protobuf体验

  1. 安装
python -m pip install grpcio #安装grpc
python -m pip install grpcio-tools #安装grpc tools
1
2
  1. 先体验protobuf3

protobuf3 是有自己专门的定义格式的 - 门槛

syntax = "proto3";

message HelloRequest {
  string name = 1;   // name 表示名称,name 的编号是1;
}
1
2
3
4
5
  1. 生成proto的python文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
1
  1. 查看protobuf生成的代码
from grpc_test import helloworld_pb2

request = helloworld_pb2.HelloRequest()

request.name = "bobby"
req_str = request.SerializeToString()
print(req_str)

request2 = helloworld_pb2.HelloRequest()

request2.ParseFromString(req_str)
print(request2.name)
1
2
3
4
5
6
7
8
9
10
11
12
  1. 对比一下protobuf和json生成的效果
from grpc_test import helloworld_pb2

request = helloworld_pb2.HelloRequest()

request.name = "bobby"
req_str = request.SerializeToString()
print(req_str)

import json
req_json = {
    "name":"bobby"
}
print(len(json.dumps(req_json)))
print(len(req_str))
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2. grpc打印hello word

  • https://github.com/grpc/grpc/blob/master/examples/python/helloworld

  • 客户端代码

    from __future__ import print_function
    
    import logging
    
    import grpc
    from protobuf import protobuf_pb2
    from protobuf import protobuf_pb2_grpc
    
    if __name__ == '__main__':
        with grpc.insecure_channel("127.0.0.1:50051") as channel:
            stub = protobuf_pb2_grpc.GreeterStub(channel)
            response = stub.SayHello(protobuf_pb2.HelloRequest(name="word!!!"))
        print(response.message)
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • 服务端代码

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @time : 2021/10/26
    # @desc : ...
    
    from concurrent import futures
    import logging
    import grpc
    from protobuf import protobuf_pb2
    from protobuf import protobuf_pb2_grpc
    
    
    class Greeter(protobuf_pb2_grpc.GreeterServicer):
        def SayHello(self, request, context):
            return protobuf_pb2.HelloReply(message="hello "+request.name)
    
    
    if __name__ == '__main__':
        server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
        protobuf_pb2_grpc.add_GreeterServicer_to_server(Greeter(),server)
        server.add_insecure_port("0.0.0.0:50051")
        server.start()
        server.wait_for_termination()
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

# go 开发 grpc

# 1. 下载编译工具

  • 地址 https://github.com/protocolbuffers/protobuf/releases

注意:protoc的版本需要和golang/protobuf保持一致 (尽量自己去下载最新的版本);下载完成后解压后记得将路径添加到环境变量中

# 2. 下载go的依赖包

go get -u google.golang.org/protobuf
1

# 3. proto文件

syntax = "proto3";
option go_package = ".;proto";
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4. 生成go文件

protoc -I . helloword.proto --go_out=plugins=grpc:.
1

# 5. 服务端代码

package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "grpc_demo/hello"
    "net"
)

type Server struct {
}


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

func main()  {
    g := grpc.NewServer()
    s := Server{}
    hello.RegisterGreeterServer(g,&s)
    lis, err := net.Listen("tcp", fmt.Sprintf(":8080"))
    if err != nil {
        panic("failed to listen: "+err.Error())
    }
    g.Serve(lis)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 6. 客户端

package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "grpc_demo/proto"
)

func main()  {
    conn,err := grpc.Dial("127.0.0.1:8080",grpc.WithInsecure())
    if err!=nil{
        panic(err)
    }
    defer conn.Close()
    c := hello.NewGreeterClient(conn)
    r,err := c.SayHello(context.Background(),&hello.HelloRequest{Name:"bobby"})
    if err!=nil{
        panic(err)
    }
    fmt.Println(r.Message)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# python和golang 联调

  • 要点
    • 使用同一个 *.proto文件
    • python和go的 *.proto文件中尽量不要使用package参数,除非大量的包名重复

# grpc 模式

  • gRPC主要有4种请求/响应模式

    1. 简单模式(Simple RPC)

    2. 服务端数据流模式(Server-side streaming RPC)

    3. 客户端数据流模式(Client-side streaming RPC)

    4. 双向数据流模式(Bidirectional streaming RPC)

  • 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;
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 1. 简单模式

  • 简单请求响应

# 2. 服务端数据流模式

  • 这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端。
  • 代码见双向数据流

# 3. 客户端数据流模式

  • 与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。典型的例子是物联网终端向服务器报送数据。

  • 代码见双向数据流

# 4. 双向数据流模式

  • 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;
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • server

    /*
     * @date: 2021/10/27
     * @desc: ...
     */
    
    package main
    
    import (
       "fmt"
       "google.golang.org/grpc"
       "net"
       "picturePro/grpcStreamTest/proto"
       "sync"
       "time"
    )
    
    const ADDR = ":50052"
    
    type server struct {
    }
    
    // GetStream 服务端数据流模式
    func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error {
       i := 0
       for {
          i++
          _ = res.Send(&proto.StreamResData{
             Data: fmt.Sprintf("date: %v", time.Now().Unix()),
          })
          time.Sleep(time.Second)
          if i > 10 {
             break
          }
       }
       return nil
    }
    
    // PutStream 客户端数据流模式
    func (s *server) PutStream(clientStr proto.Greeter_PutStreamServer) error {
       for {
          if a, err := clientStr.Recv(); err != nil {
             fmt.Println("er:", 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() {
       listen, err := net.Listen("tcp", ADDR)
       if err != nil {
          panic(err.Error())
       }
       s := grpc.NewServer()
       proto.RegisterGreeterServer(s, &server{})
       err = s.Serve(listen)
       if err != nil {
          return
       }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
  • client

    /*
     * @date: 2021/10/27
     * @desc: ...
     */
    
    package main
    
    import (
       "context"
       "fmt"
       "google.golang.org/grpc"
       "picturePro/grpcStreamTest/proto"
       "sync"
       "time"
    )
    
    func main() {
       conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure())
       if err != nil {
          return
    
       }
       defer conn.Close()
       c := proto.NewGreeterClient(conn)
       // 服务端流模式
       res, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "hello"})
       for {
          a, err := res.Recv()
          if err != nil {
             fmt.Println(err.Error())
             break
          }
          fmt.Println(a.Data)
       }
       // 客户端流模式
       puts, _ := c.PutStream(context.Background())
       for i := 0; i < 10; i++ {
          err = puts.Send(&proto.StreamReqData{
             Data: fmt.Sprintf("test %v", i),
          })
          if err != nil {
             fmt.Println(err.Error())
          }
          time.Sleep(time.Second)
       }
    
       //双向流模式
       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()
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67

# gRPC的优势

与HTTP(Restful API)对比,gRPC的优势

  • gRPC和restful API都提供了一套通信机制,用于server/client模型通信,而且它们都使用http作为底层的传输协议(严格地说, gRPC使用的http2.0,而restful api则不一定)。不过gRPC还是有些特有的优势,如下:
  1. gRPC可以通过protobuf来定义接口,可以有更加严格的接口约束条件,支持多种语言。
  2. protobuf可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高传输速度。
  3. gRPC可以支持streaming流式通信(http2.0),提高传输速度。
#rpc
上次更新: 2023/05/04, 15:30:48
RPC简单实现
protobuf进阶

← RPC简单实现 protobuf进阶→

最近更新
01
go与http代理
05-24
02
自制申威架构k8s-reloader
12-06
03
Docker Buildx 教程
12-01
更多文章>
Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式