我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Golang中tinyrpc框架怎么使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Golang中tinyrpc框架怎么使用

本篇内容介绍了“Golang中tinyrpc框架怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    tinyrpc功能

    tinyrpc基于TCP协议,支持各种压缩格式,基于protocol buffer的序列化协议。其rpc是基于golang原生的net/rpc开发而成。

    tinyrpc项目结构

    tinyrpc基于net/rpc开发而成,在此基础上集成了额外的能力。项目结构如图:

    Golang中tinyrpc框架怎么使用

    功能目录如下:

    • codec 编码模块

    • compressor 压缩模块

    • header 请求/响应头模块

    • protoc-gen-tinyrpc 代码生成插件

    • serializer 序列化模块

    tinyrpc源码解读

    客户端和服务端构建

    客户端是以net/rpcrpc.Client为基础构建,在此基础上定义了Option以配置压缩方式和序列化方式:

    type Option func(o *options)type options struct {compressType compressor.CompressTypeserializer   serializer.Serializer}

    在创建客户端的时候将配置好的压缩算法和序列化方式作为创建客户端的参数:

    func NewClient(conn io.ReadWriteCloser, opts ...Option) *Client {options := options{compressType: compressor.Raw,serializer:   serializer.Proto,}for _, option := range opts {option(&options)}return &Client{rpc.NewClientWithCodec(codec.NewClientCodec(conn, options.compressType, options.serializer))}}

    服务端是以net/rpcrpc.Server为基础构建,在此基础上扩展了Server的定义:

    type Server struct {*rpc.Serverserializer.Serializer}

    在创建客户端和开启服务时传入序列化方式:

    func NewServer(opts ...Option) *Server {options := options{serializer: serializer.Proto,}for _, option := range opts {option(&options)}return &Server{&rpc.Server{}, options.serializer}}func (s *Server) Serve(lis net.Listener) {log.Printf("tinyrpc started on: %s", lis.Addr().String())for {conn, err := lis.Accept()if err != nil {continue}go s.Server.ServeCodec(codec.NewServerCodec(conn, s.Serializer))}}

    压缩算法compressor

    压缩算法的实现中首先是定义了压缩的接口:

    type Compressor interface {Zip([]byte) ([]byte, error)Unzip([]byte) ([]byte, error)}

    压缩的接口包含压缩和解压方法。

    压缩算法使用的是uint类型,使用iota来初始化,并且使用map来进行所有压缩算法实现的管理:

    type CompressType uint16const (Raw CompressType = iotaGzipSnappyZlib)// Compressors which supported by rpcvar Compressors = map[CompressType]Compressor{Raw:    RawCompressor{},Gzip:   GzipCompressor{},Snappy: SnappyCompressor{},Zlib:   ZlibCompressor{},}

    序列化 serializer

    序列化部分代码非常简单,提供了一个接口:

    type Serializer interface {Marshal(message interface{}) ([]byte, error)Unmarshal(data []byte, message interface{}) error}

    目前只有ProtoSerializer一个实现,ProtoSerializer内部的实现是基于"google.golang.org/protobuf/proto"来实现的,并没有什么特殊的处理,因此就不花费笔墨详述了。

    请求/响应头 header

    tinyrpc定义了自己的请求头和响应头:

    // RequestHeader request header structure looks like:// +--------------+----------------+----------+------------+----------+// | CompressType |      Method    |    ID    | RequestLen | Checksum |// +--------------+----------------+----------+------------+----------+// |    uint16    | uvarint+string |  uvarint |   uvarint  |  uint32  |// +--------------+----------------+----------+------------+----------+type RequestHeader struct {sync.RWMutexCompressType compressor.CompressTypeMethod       stringID           uint64RequestLen   uint32Checksum     uint32}

    请求头由压缩类型,方法,id,请求长度和校验码组成。

    // ResponseHeader request header structure looks like:// +--------------+---------+----------------+-------------+----------+// | CompressType |    ID   |      Error     | ResponseLen | Checksum |// +--------------+---------+----------------+-------------+----------+// |    uint16    | uvarint | uvarint+string |    uvarint  |  uint32  |// +--------------+---------+----------------+-------------+----------+type ResponseHeader struct {sync.RWMutexCompressType compressor.CompressTypeID           uint64Error        stringResponseLen  uint32Checksum     uint32}

    响应头由压缩类型,id,错误信息,返回长度和校验码组成。

    为了实现头的重用,tinyrpc为头构建了缓存池:

    var (RequestPool  sync.PoolResponsePool sync.Pool)func init() {RequestPool = sync.Pool{New: func() interface{} {return &RequestHeader{}}}ResponsePool = sync.Pool{New: func() interface{} {return &ResponseHeader{}}}}

    在使用时get出来,生命周期结束后放回池子,并且在put之前需要进行重置:

        h := header.RequestPool.Get().(*header.RequestHeader)defer func() {h.ResetHeader()header.RequestPool.Put(h)}()
    // ResetHeader reset request headerfunc (r *RequestHeader) ResetHeader() {r.Lock()defer r.Unlock()r.ID = 0r.Checksum = 0r.Method = ""r.CompressType = 0r.RequestLen = 0}// ResetHeader reset response headerfunc (r *ResponseHeader) ResetHeader() {r.Lock()defer r.Unlock()r.Error = ""r.ID = 0r.CompressType = 0r.Checksum = 0r.ResponseLen = 0}

    搞清楚了头的结构以及对象池的复用逻辑,那么具体的头的编码与解码就是很简单的拆装工作,就不在此一行一行解析了,大家有兴趣可以自行去阅读。

    编码 codec

    由于tinyrpc是基于net/rpc开发,那么其codec模块自然也是依赖于net/rpcClientCodecServerCodec接口来实现的。

    客户端实现

    客户端是基于ClientCodec实现的能力:

    type ClientCodec interface {WriteRequest(*Request, any) errorReadResponseHeader(*Response) errorReadResponseBody(any) errorClose() error}

    client定义了一个clientCodec类型,并且实现了ClientCodec的接口方法:

    type clientCodec struct {r io.Readerw io.Writerc io.Closercompressor compressor.CompressType // rpc compress type(raw,gzip,snappy,zlib)serializer serializer.Serializerresponse   header.ResponseHeader // rpc response headermutex      sync.Mutex            // protect pending mappending    map[uint64]string}

    WriteRequest实现:

    // WriteRequest Write the rpc request header and body to the io streamfunc (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error {c.mutex.Lock()c.pending[r.Seq] = r.ServiceMethodc.mutex.Unlock()if _, ok := compressor.Compressors[c.compressor]; !ok {return NotFoundCompressorError}reqBody, err := c.serializer.Marshal(param)if err != nil {return err}compressedReqBody, err := compressor.Compressors[c.compressor].Zip(reqBody)if err != nil {return err}h := header.RequestPool.Get().(*header.RequestHeader)defer func() {h.ResetHeader()header.RequestPool.Put(h)}()h.ID = r.Seqh.Method = r.ServiceMethodh.RequestLen = uint32(len(compressedReqBody))h.CompressType = compressor.CompressType(c.compressor)h.Checksum = crc32.ChecksumIEEE(compressedReqBody)if err := sendFrame(c.w, h.Marshal()); err != nil {return err}if err := write(c.w, compressedReqBody); err != nil {return err}c.w.(*bufio.Writer).Flush()return nil}

    可以看到代码的实现还是比较清晰的,主要分为几个步骤:

    • 将数据进行序列化构成请求体

    • 选择相应的压缩算法进行压缩

    • 从Pool中获取请求头实例将数据全部填入其中构成最后的请求头

    • 分别通过io操作发送处理过的请求头和请求体

    ReadResponseHeader实现:

    // ReadResponseHeader read the rpc response header from the io streamfunc (c *clientCodec) ReadResponseHeader(r *rpc.Response) error {c.response.ResetHeader()data, err := recvFrame(c.r)if err != nil {return err}err = c.response.Unmarshal(data)if err != nil {return err}c.mutex.Lock()r.Seq = c.response.IDr.Error = c.response.Errorr.ServiceMethod = c.pending[r.Seq]delete(c.pending, r.Seq)c.mutex.Unlock()return nil}

    此方法作用是读取返回的响应头,并解析成具体的结构体

    ReadResponseBody实现:

    func (c *clientCodec) ReadResponseBody(param interface{}) error {if param == nil {if c.response.ResponseLen != 0 {if err := read(c.r, make([]byte, c.response.ResponseLen)); err != nil {return err}}return nil}respBody := make([]byte, c.response.ResponseLen)err := read(c.r, respBody)if err != nil {return err}if c.response.Checksum != 0 {if crc32.ChecksumIEEE(respBody) != c.response.Checksum {return UnexpectedChecksumError}}if c.response.GetCompressType() != c.compressor {return CompressorTypeMismatchError}resp, err := compressor.Compressors[c.response.GetCompressType()].Unzip(respBody)if err != nil {return err}return c.serializer.Unmarshal(resp, param)}

    此方法是用于读取返回的响应结构体,流程如下:

    • 读取流获取响应体

    • 根据响应头中的校验码来比对响应体是否完整

    • 根据压缩算法来解压具体的结构体

    • 进行反序列化

    服务端实现

    服务端是基于ServerCodec实现的能力:

    type ServerCodec interface {ReadRequestHeader(*Request) errorReadRequestBody(any) errorWriteResponse(*Response, any) error// Close can be called multiple times and must be idempotent.Close() error}

    和客户端类似,server定义了一个serverCodec类型,并且实现了ServerCodec的接口方法:

    type serverCodec struct {r io.Readerw io.Writerc io.Closerrequest    header.RequestHeaderserializer serializer.Serializermutex      sync.Mutex // protects seq, pendingseq        uint64pending    map[uint64]*reqCtx}

    ReadRequestHeader实现:

    // ReadRequestHeader read the rpc request header from the io streamfunc (s *serverCodec) ReadRequestHeader(r *rpc.Request) error {s.request.ResetHeader()data, err := recvFrame(s.r)if err != nil {return err}err = s.request.Unmarshal(data)if err != nil {return err}s.mutex.Lock()s.seq++s.pending[s.seq] = &reqCtx{s.request.ID, s.request.GetCompressType()}r.ServiceMethod = s.request.Methodr.Seq = s.seqs.mutex.Unlock()return nil}

    此方法用于读取请求头并解析成结构体

    ReadRequestBody实现:

    // ReadRequestBody read the rpc request body from the io streamfunc (s *serverCodec) ReadRequestBody(param interface{}) error {if param == nil {if s.request.RequestLen != 0 {if err := read(s.r, make([]byte, s.request.RequestLen)); err != nil {return err}}return nil}reqBody := make([]byte, s.request.RequestLen)err := read(s.r, reqBody)if err != nil {return err}if s.request.Checksum != 0 {if crc32.ChecksumIEEE(reqBody) != s.request.Checksum {return UnexpectedChecksumError}}if _, ok := compressor.Compressors[s.request.GetCompressType()]; !ok {return NotFoundCompressorError}req, err := compressor.Compressors[s.request.GetCompressType()].Unzip(reqBody)if err != nil {return err}return s.serializer.Unmarshal(req, param)}

    此方法用于读取请求体,流程和读取响应体差不多,大致如下:

    • 读取流并解析成请求体

    • 根据请求头中的校验码进行校验

    • 根据压缩算法进行解压

    • 反序列化

    WriteResponse实现:

    // WriteResponse Write the rpc response header and body to the io streamfunc (s *serverCodec) WriteResponse(r *rpc.Response, param interface{}) error {s.mutex.Lock()reqCtx, ok := s.pending[r.Seq]if !ok {s.mutex.Unlock()return InvalidSequenceError}delete(s.pending, r.Seq)s.mutex.Unlock()if r.Error != "" {param = nil}if _, ok := compressor.Compressors[reqCtx.compareType]; !ok {return NotFoundCompressorError}var respBody []bytevar err errorif param != nil {respBody, err = s.serializer.Marshal(param)if err != nil {return err}}compressedRespBody, err := compressor.Compressors[reqCtx.compareType].Zip(respBody)if err != nil {return err}h := header.ResponsePool.Get().(*header.ResponseHeader)defer func() {h.ResetHeader()header.ResponsePool.Put(h)}()h.ID = reqCtx.requestIDh.Error = r.Errorh.ResponseLen = uint32(len(compressedRespBody))h.Checksum = crc32.ChecksumIEEE(compressedRespBody)h.CompressType = reqCtx.compareTypeif err = sendFrame(s.w, h.Marshal()); err != nil {return err}if err = write(s.w, compressedRespBody); err != nil {return err}s.w.(*bufio.Writer).Flush()return nil}

    此方法用于写入响应体,大致与写入请求体差不多,流程如下:

    • 将响应体序列化

    • 使用压缩算法将响应体进行压缩

    • 使用Pool管理响应头

    • 分别发送返回头和返回体

    “Golang中tinyrpc框架怎么使用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Golang中tinyrpc框架怎么使用

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    Golang中tinyrpc框架怎么使用

    本篇内容介绍了“Golang中tinyrpc框架怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!tinyrpc功能tinyrpc基于T
    2023-07-05

    golang下grpc框架怎么使用

    今天小编给大家分享一下golang下grpc框架怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 什么是grpc和
    2023-06-30

    golang websocket框架怎么用

    使用Golang编写WebSocket应用程序可以使用一些流行的框架,例如Gorilla WebSocket或Melody。下面是使用Gorilla WebSocket框架的简单示例:首先,确保你已经安装了Golang,并且已经设置好了工作
    2023-10-27

    golang任务调度框架怎么使用

    在Go语言中,可以使用多种任务调度框架来进行任务的管理和调度,例如:1. Gin: Gin是一个高性能的Go语言Web框架,它支持路由和中间件,可以用于创建RESTful API。可以使用Gin框架来创建定时任务的API接口,并使用cron
    2023-09-13

    golang人工智能框架怎么使用

    在Golang中使用人工智能框架需要以下步骤:1. 安装相应的人工智能框架:比如TensorFlow、PyTorch等。可以使用go get命令从GitHub上下载并安装框架的Go绑定库。2. 导入所需的包:根据框架的要求,导入相应的包,比
    2023-10-18

    如何使用golang框架?

    go 框架(例如 gin)提供了构建高性能 web 应用程序的基础。在本指南中,您将学习如何:1. 选择 gin 等 go 框架;2. 安装 gin 并创建项目;3. 设置路由处理请求;4. 使用 gin 处理 get 请求并返回 json
    如何使用golang框架?
    2024-05-23

    Gin框架中bind怎么使用

    本篇内容主要讲解“Gin框架中bind怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Gin框架中bind怎么使用”吧!概述Gin框架中,有bind函数可以非常方便的将url的查询参数qu
    2023-06-22

    Java中怎么使用Nutz框架

    使用Nutz框架在Java中进行开发可以按照以下步骤进行:1. 导入Nutz框架的相关依赖:在项目的构建文件(如Maven的pom.xml)中添加Nutz框架的依赖项。你可以通过Nutz的官方网站或GitHub页面找到最新的版本和依赖信息。
    2023-08-24

    gin框架中怎么使用JWT

    这篇文章主要讲解了“gin框架中怎么使用JWT”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“gin框架中怎么使用JWT”吧!什么是JWT?JWT全称JSON Web Token是一种跨域认证
    2023-06-30

    Java中怎么使用Sinatra框架

    这篇文章主要讲解了“Java中怎么使用Sinatra框架”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中怎么使用Sinatra框架”吧!Sinatra 是一个微型的 Ruby 语言的
    2023-06-17

    Golang函数如何在Web框架中使用?

    go 函数在 web 框架中用于:处理请求和生成响应。可作为可重复使用的处理器函数。可分组在函数组中以实现功能模块化。可作为中间件执行预处理或后处理操作。Go 函数如何在 Web 框架中使用?Go 函数是强大的代码块,可用于执行各种任务。
    Golang函数如何在Web框架中使用?
    2024-04-11

    Python中的Scrapy框架怎么使用

    这篇文章主要讲解了“Python中的Scrapy框架怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python中的Scrapy框架怎么使用”吧!安装Scrapy首先,您需要在您的计算
    2023-07-05

    怎么在java中使用mybatis框架

    怎么在java中使用mybatis框架?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Java的特点有哪些Java的特点有哪些1.Java语言作为静态面向对象编程
    2023-06-14

    golang tcp框架怎么实现

    要实现一个基于TCP的框架,可以参考以下步骤:导入所需的包: net、io、bufio、sync等。创建一个TCP服务器的结构体,包括监听地址、端口、处理函数等字段。实现TCP服务器的初始化方法,用于设置监听地址和端口。实现TCP服务器的启
    golang tcp框架怎么实现
    2024-02-29

    aforge.net框架怎么使用

    要使用AForge.NET框架,你需要进行以下步骤:1. 下载AForge.NET框架:你可以从官方下载AForge.NET框架的最新版本。2. 引用AForge.NET库:在你的项目中,右键点击项目,选择“添加引用”,然后浏览到AForg
    aforge.net框架怎么使用
    2024-02-29

    Django框架中模型怎么使用

    这篇文章主要介绍“Django框架中模型怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Django框架中模型怎么使用”文章能帮助大家解决问题。Django 对各种数据库提供了很好的支持,包括
    2023-07-02

    怎么安装golang revel框架

    这篇文章主要介绍“怎么安装golang revel框架”,在日常操作中,相信很多人在怎么安装golang revel框架问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么安装golang revel框架”的疑
    2023-07-05

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录