go 进阶 RPC相关: 一. RPC 与 Protobuf 基础问题

2023-11-05

一. 什么是RPC

  1. 参考博客: Go RPC 开发指南
  2. 什么是RPC: 全称Remote Procedure Call, 即远程过程调用,主要作用是,实现像调用本地方法一样调用远程方法,并且屏蔽底层网络编程细节

1. RPC 实现原理

  1. 首先整个RPC架构中可以分为以下几个模块
  1. 客户端服务调用方Client
  2. 客户端存根Client Stub: 存放服务端地址信息,将客户端的请求信息打包成网络消息,再通过网络传输发送给服务端
  3. 服务端存根Server Stub: 接收客户端发送过来的请求消息并进行解包,然后调用本地服务进行处理
  4. 服务端Server: 服务的真正提供者
  1. RPC调用过程: 启动服务提供方–>暴露服务–> 服务消费方启动–>服务发现–>服务引用–>服务消费方调用服务提供方暴露的方法

2. 有http为什么还要出现RPC

  1. 或者问RPC与http有什么区别
  2. 首先在服务注册,发现层面,没有什么区别,比如SpringCloud中基于http通信,基于Nacos,或Eureka等等作为注册中心,进行服务注册,服务发现,发起调用的
  3. 在细节上
  1. HTTP是一种超文本传输协议,默认情况下基于JSON格式传输数据,跨语言, 简单易用,但是效率低
  2. RPC 远程过程调用,是一种计算机通信协议,基于二进制进行数据传输,在发送与接收数据时根据指定协议进行序列化,反序列化,性能略高,但是因为序列化问题就造成了没有HTTP灵活,比如不能跨语言
  3. HTTP与RPC底层都基于socket通信,但是HTTP支持资源定位的路径支持RESTful风格,RPC不支持
  1. 参考博客

3. Protobut

  1. 参考博客
  2. 前面说过,在使用RPC时可以指定序列化协议,反序列化协议,在发送数据与接收数据时根据直接协议进行序列化反序列化
  3. 常见的序列化反序列化协议有哪些:
  1. json 序列化
  2. 比如使用Dubbo时hessian2序列化
  3. Google中提出的Protobuf等等
  1. 讲一下Protobuf序列化原理, 首先说一下Protobuf使用流程
  1. 定义 .proto 文件
  2. 使用protoc 编译器编译 .proto 文件生成一系列接口代码
  3. 调用生成的接口实现对 .proto 定义的字段的读取以及 message 对象的序列化、反序列化方法

Protobuf 编码方式

  1. Protobuf支持两种编码方式:
  1. Varint编码: 一种变长的编码方式,编码原理是用字节表示数字,值越小的数字,使用越少的字节数表示。因此,可以通过减少表示数字的字节数进行数据压缩
  2. Zigzag编码: 一种变长的编码方式,编码原理是使用无符号数来表示有符号数字,使得绝对值小的数字都可以采用较少字节来表示,特别对表示负数的数据能更好地进行数据压缩

Protobuf 数据存储方式

  1. 支持两种
  1. T-L-V数据存储方式: 即标识符-长度-字段值的存储方式,其原理是以标识符-长度-字段值表示单个数据,最终将所有数据拼接成一个字节流,从而实现数据存储的功能。其中Length可选存储,如储存Varint编码数据就不需要存储Length,此时为T-V存储方式。
  2. T-V数据存储方式: 消息字段的标识号、数据类型、字段值经过Protobuf采用Varint和Zigzag编码后,以T-V(Tag-Value)方式进行数据存储。对于Varint与Zigzag编码方式编码的数据,省略了T-L-V中的字节长度Length

Protobuf对于数据存储的三大原则

  1. Protocol Buffer将消息中的每个字段进行编码后,利用T - L - V 存储方式进行数据的存储,最终得到一个二进制字节流。
  2. Protobuf对于不同的字段类型采用不同的编码和数据存储方式对消息字段进行序列化,以确保得到高效紧凑的数据压缩对于Varint编码数据的存储,不需要存储字节长度Length,使用T-V存储方式进行存储;对于采用其它编码方式(如LENGTH_DELIMITED)的数据,使用T-L-V存储方式进行存储。
  3. ProtoBuf对于数据字段值的独特编码方式与T-L-V数据存储方式,使得 ProtoBuf序列化后数据量体积极小。

Protobuf 序列化原理

  1. Protobuf 序列化: Protobuf对于不同的字段类型采用不同的编码方式和数据存储方式进行序列化,确保高效紧凑,序列化过程
  1. 判断每个字段是否有设置值,有值才进行编码。
  2. 根据字段标识号与数据类型将字段值通过不同的编码方式进行编码。
  3. 将编码后的数据块按照字段类型采用不同的数据存储方式封装成二进制数据流。
  1. Protobuf 反序列化过程如下:
  1. 调用消息类的parseFrom(input)解析从输入流读入的二进制字节数据流。
  2. 将解析出来的数据按照指定的格式读取到Java、C++、Phyton对应的结构类型中

4. 其它问题

  1. protobuf的序列化流程: 参考序列化原理内部的序列化部分, 然后讲一下protobuf的编码方式与存储方式,另外还有存储原则
  2. go的struct通过grpc传输的时候,是一个二进制的字节流,struct变成字节流的过程?是怎么拼成字节流的?跟上面一样
  3. 用rpc请求和http请求的区别或gRPC和REST的区别是什么,和rpc的优势是什么,是什么造成rpc的性能更好: 参考"2. 有http为什么还要出现RPC"
  4. RPC接口和RESTful接口是怎么做的: 在grpc框架中提供了grpc-gateway,如果rpc接口需要支持RESTful,通过grpc-gateway实现
  5. Protocol Buffers是什么,为什么它被用于gRPC中: 一种高效的序列化,反序列化协议
  6. gRPC的流程是什么: 定义服务、生成源代码、实现服务、启动服务。首先,需要定义要实现的服务及其接口,使用Protocol Buffers编写接口定义文件。其次,使用编译器生成客户端和服务器端的源代码。然后,实现生成的接口。最后,启动服务器并将其部署在适当的位置
  7. gRPC支持哪些类型的序列化: :二进制(使用Protocol Buffers)和JSON。其中,二进制序列化在效率和性能方面比JSON序列化更优秀。但是,JSON序列化在可读性方面更好,可以方便地进行调试和测试
  8. grpc底层通信协议: gRPC底层使用的HTTP/2协议, HTTP协议本身可以通过Content-Encoding表示压缩算法,使用Contetn-length指定数据长度

二. golang 标准库与 RPC

1. net/rpc库

  1. golang标准库"net/rpc"提供了RPC支持
  2. 使用http作为RPC的载体,通过net/http包监听客户端连接请求, 如下监听本地的8095端口
package main
 
import (
    "errors"
    "fmt"
    "log"
    "net"
    "net/http"
    "net/rpc"
    "os"
)
 
// 算数运算结构体
type Arith struct {
}
 
// 算数运算请求结构体
type ArithRequest struct {
    A int
    B int
}
 
// 算数运算响应结构体
type ArithResponse struct {
    Pro int // 乘积
    Quo int // 商
    Rem int // 余数
}
 
// 乘法运算方法
func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
    res.Pro = req.A * req.B
    return nil
}
 
// 除法运算方法
func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
    if req.B == 0 {
        return errors.New("divide by zero")
    }
    res.Quo = req.A / req.B
    res.Rem = req.A % req.B
    return nil
}
 
func main() {
    rpc.Register(new(Arith)) // 注册rpc服务
    rpc.HandleHTTP()         // 采用http协议作为rpc载体
    lis, err := net.Listen("tcp", "127.0.0.1:8095")
    if err != nil {
        log.Fatalln("fatal error: ", err)
    }
    fmt.Fprintf(os.Stdout, "%s", "start connection")
    http.Serve(lis, nil)
}
  1. rpc_client 客户端示例
package main
 
import (
    "fmt"
    "log"
    "net/rpc"
)
 
// 算数运算请求结构体
type ArithRequest struct {
    A int
    B int
}
 
// 算数运算响应结构体
type ArithResponse struct {
    Pro int // 乘积
    Quo int // 商
    Rem int // 余数
}
 
func main() {
    conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095")
    if err != nil {
        log.Fatalln("dailing error: ", err)
    }
 
    req := ArithRequest{9, 2}
    var res ArithResponse
 
    err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
    if err != nil {
        log.Fatalln("arith error: ", err)
    }
    fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
 
    err = conn.Call("Arith.Divide", req, &res)
    if err != nil {
        log.Fatalln("arith error: ", err)
    }
    fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}

2. net/rpc/jsonrpc库

  1. 上面存在问题: 上面的例子演示了使用net/rpc实现RPC的过程,但是没办法在其他语言中调用上面例子实现的RPC方法。所以接下来的例子演示使用net/rpc/jsonrpc库实现RPC方法,此方式实现的RPC方法支持跨语言调用,也就是net/rpc/jsonrpc库
  2. 提供一个服务端,监听本地的8096端口,并处理客户端的tcp连接请求
package main
 
import (
    "errors"
    "fmt"
    "log"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
    "os"
)
 
// 算数运算结构体
type Arith struct {
}
 
// 算数运算请求结构体
type ArithRequest struct {
    A int
    B int
}
 
// 算数运算响应结构体
type ArithResponse struct {
    Pro int // 乘积
    Quo int // 商
    Rem int // 余数
}
 
// 乘法运算方法
func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
    res.Pro = req.A * req.B
    return nil
}
 
// 除法运算方法
func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
    if req.B == 0 {
        return errors.New("divide by zero")
    }
    res.Quo = req.A / req.B
    res.Rem = req.A % req.B
    return nil
}
 
func main() {
    rpc.Register(new(Arith)) // 注册rpc服务
    lis, err := net.Listen("tcp", "127.0.0.1:8096")
    if err != nil {
        log.Fatalln("fatal error: ", err)
    }
    fmt.Fprintf(os.Stdout, "%s", "start connection")
    for {
        conn, err := lis.Accept() // 接收客户端连接请求
        if err != nil {
            continue
        }
        go func(conn net.Conn) { // 并发处理客户端请求
            fmt.Fprintf(os.Stdout, "%s", "new client in coming\n")
            jsonrpc.ServeConn(conn)
        }(conn)
    }
}
  1. 提供一个客户端
package main
 
import (
    "fmt"
    "log"
    "net/rpc/jsonrpc"
)
 
// 算数运算请求结构体
type ArithRequest struct {
    A int
    B int
}
 
// 算数运算响应结构体
type ArithResponse struct {
    Pro int // 乘积
    Quo int // 商
    Rem int // 余数
}
 
func main() {
    conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
    if err != nil {
        log.Fatalln("dailing error: ", err)
    }
 
    req := ArithRequest{9, 2}
    var res ArithResponse
 
    err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
    if err != nil {
        log.Fatalln("arith error: ", err)
    }
    fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
 
    err = conn.Call("Arith.Divide", req, &res)
    if err != nil {
        log.Fatalln("arith error: ", err)
    }
    fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go 进阶 RPC相关: 一. RPC 与 Protobuf 基础问题 的相关文章

随机推荐

  • linux增大交换空间,Linux系统增加交换空间的方法

    Linux系统增加交换空间有两种方法 严格的说 在系统安装完后只有一种方法可以增加swap 那就是本文的第二种方法 至于第一种方法应该是安装系统时设置交换区 1 使用分区 在安装OS时划分出专门的交换分区 空间大小要事先规划好 启动系统时自
  • 【事业单位笔试】zrzyb信息中心-社会招聘-笔试记录

    写在前面的话 虽然说本人在21年校招时有北京银行 农银金科 中信银行研发 京东发 邮储银行 中软 郑州铁路局等的offer 奈何个人原因还是选择了有北京户口的现在的工作单位 虽然只有两年 但也是时候准备考虑一下今后的发展了 感谢大家支持 我
  • 电机控制进阶——PID速度控制

    之前的几篇文章 电机控制基础篇 介绍的电机编码器原理 定时器输出PWM 定时器编码器模式测速等 本篇在前几篇的基础上 继续来学习电机控制 通过PID算法 来进行电机的速度控制 并进行实验测试 PID基础 PID即 Proportional
  • 海外新冠疫情 API数据接口

    海外新冠疫情 计费模式 免费额度 点数单价 每日限制 会员免费 100次 免费 10000次 更新时间 2022 07 11 02 51 15接口状态 正常 返回海外新冠数据 请求地址 HTTPGET POST https www mait
  • 毕业设计 嵌入式 单片机智能路灯

    文章目录 1 简介 2 绪论 2 1 项目背景 2 2 需求分析 3 系统设计 3 1 功能设计 3 1 1 系统角色分析 3 1 2 开发环境 3 2 总体设计 3 3 硬件部分 3 3 1 整体架构 3 3 2 stm32部分 3 3
  • 短视频去水印小程序源码 附带详细搭建教程

    开发语言 PHP 数据库 MySQL 无需授权 文件完全开源 可以二次开发 后台可以自己添加自己的接口 压缩包里包含一个免费 接口 不敢保证能一直使用 下载地址
  • 慌的一批!妹子一个rm -rf把公司服务器数据删没了...

    来源 https www cnblogs com zhouyu629 p 3734494 html 作者 zhouyu 责编 linse 经历了两天不懈努力 终于恢复了一次误操作删除的生产服务器数据 对本次事故过程和解决办法记录在此 警醒自
  • mysql字符串函数

    mysql的字符串函数 对于针对字符串位置的操作 第一个位置被标记为1 1 ASCII str 返回字符串str的最左面字符的ASCII代码值 如果 str是空字符串 返回 0 如果 str是 NULL 返回 NULL mysql gt s
  • 解决bootstrapTable动态添加的一行不会被$(“#bootstrap-table“).bootstrapTable(‘getSelections‘)获取到

    背景 业务需要bootstrapTable表格选中某一行数据 并将按钮表示为可点击状态 当选中多行或没选中数据时设置为不可点击状态 解决 a class btn btn success i class fa fa plus i 所选行下方添
  • (23)[CS13] LSTM Generating:Generating Sequences With Recurrent Neural Networks

    计划完成深度学习入门的126篇论文第二十三篇 UT的Alex Graves等领导研究通过LSTM来生成不同风格的文本和手写体handwriting ABSTRACT INTRODUCTION 摘要 本文通过对一个数据点的预测 说明了LSTM
  • Anaconda(Miniconda) 安装(Windows下)

    下载地址 https www anaconda com download or https conda io miniconda html 下载之后直接安装 可能需要设置路径 之后在cmd中输入 conda list 查看安装内容 如需要更
  • 测试用例的设计方法(七种)详细分析

    1 需求分析法 需求分析法 按照需求 设计测试用例 其中的需求分为两种 用户需求 软件需求 1 验证需求是否正确 完整 无二义性 并且逻辑一致 2 要从 黑盒 的角度 设计出充分并且必要的测试集 以保证设计和代码都能完全符合需求 2 等价类
  • 2023华为OD机试真题【数字游戏】

    题目内容 小明玩一个游戏 系统发1 n张牌 每张牌上有一个整数 第一张给小明 后n张按照发牌顺序排成连续的一行 需要小明判断 后n张牌中 是否存在连续的若干张牌 其和可以整除小明手中牌上的数字 输入描述 输入数据有多组 每组输入数据有两行
  • vue中native的用法

    vue中native的用法 官方解释 你可能想在某个组件的根元素上监听一个原生事件 可以使用 v on 的修饰符 native 举例 比如a标签可以直接绑定原生事件 如果你通过自定义封装了button标签 命名mao button 这时候绑
  • js 取数组对象的交集内容

    一 简单数组 两数组a 1 2 3 b 2 4 5 求a b数组 var a 1 2 3 var b 2 4 5 并集 var union a concat b filter function n return a indexOf n 1
  • vue3+element Plus使用el-tabs标签页,页面刷新不会到默认页(1)

    当我们使用el tabs标签页 在页面刷新后就会回到默认的那一页 如果我们想让页面停留在当前页 可以使用localStorage存储当前页的值 1 引入el tabs
  • vue cli3 性能优化实战

    性能优化 知识追寻者搞了个人站点后 心血来潮来了一波前端性能优化实战 个人站点地址 https zszxz com index 生成分析报告 在 packge json 中引入 analyz vue cli service build mo
  • 企业微信的外部联系人回调处理技巧

    一 关于设置接收事件服务器的信息 在企业微信管理后台的 客户联系 客户 页面 点开 API 小按钮 再点击 接收事件服务器 配置 进入配置页面 要求填写URL Token EncodingAESKey三个参数 URL是企业后台接收企业微信推
  • vbs 文件用于删除符合条件的文件夹

    数据库备份后的文件夹名称为 2011 06 30 2011 07 01 2011 07 02 2011 07 03 2011 07 04 2011 07 05 文件夹内为数据库的备份文件 现考虑只保存最后两天的文件夹 因此 做了一个定时任务
  • go 进阶 RPC相关: 一. RPC 与 Protobuf 基础问题

    目录 一 什么是RPC 1 RPC 实现原理 2 有http为什么还要出现RPC 3 Protobut Protobuf 编码方式 Protobuf 数据存储方式 Protobuf对于数据存储的三大原则 Protobuf 序列化原理 4 其