返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中tinyrpc框架怎么使用
  • 709
分享到

Golang中tinyrpc框架怎么使用

2023-07-05 00:07:38 709人浏览 八月长安
摘要

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

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

    您可能感兴趣的文档:

    --结束END--

    本文标题: Golang中tinyrpc框架怎么使用

    本文链接: https://lsjlt.com/news/348467.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢
    • Golang中tinyrpc框架怎么使用
      本篇内容介绍了“Golang中tinyrpc框架怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!tinyrpc功能tinyrpc基于T...
      99+
      2023-07-05
    • golang下grpc框架怎么使用
      今天小编给大家分享一下golang下grpc框架怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 什么是grpc和...
      99+
      2023-06-30
    • golang websocket框架怎么用
      使用Golang编写WebSocket应用程序可以使用一些流行的框架,例如Gorilla WebSocket或Melody。下面是使...
      99+
      2023-10-27
      golang
    • Golang中GinWeb框架如何使用
      Golang中GinWeb框架如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。安装Go版本要求: Go1.12及以上1....
      99+
      2024-04-02
    • golang任务调度框架怎么使用
      在Go语言中,可以使用多种任务调度框架来进行任务的管理和调度,例如:1. Gin: Gin是一个高性能的Go语言Web框架,它支持路...
      99+
      2023-09-13
      golang
    • golang人工智能框架怎么使用
      在Golang中使用人工智能框架需要以下步骤:1. 安装相应的人工智能框架:比如TensorFlow、PyTorch等。可以使用go...
      99+
      2023-10-18
      golang 人工智能
    • 如何使用golang框架?
      go 框架(例如 gin)提供了构建高性能 web 应用程序的基础。在本指南中,您将学习如何:1. 选择 gin 等 go 框架;2. 安装 gin 并创建项目;3. 设置路由处理请求;...
      99+
      2024-05-23
      框架 golang git
    • Gin框架中bind怎么使用
      本篇内容主要讲解“Gin框架中bind怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Gin框架中bind怎么使用”吧!概述Gin框架中,有bind函数可以非常方便的将url的查询参数qu...
      99+
      2023-06-22
    • Java中怎么使用Sinatra框架
      这篇文章主要讲解了“Java中怎么使用Sinatra框架”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中怎么使用Sinatra框架”吧!Sinatra 是一个微型的 Ruby 语言的...
      99+
      2023-06-17
    • gin框架中怎么使用JWT
      这篇文章主要讲解了“gin框架中怎么使用JWT”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“gin框架中怎么使用JWT”吧!什么是JWT?JWT全称JSON Web Token是一种跨域认证...
      99+
      2023-06-30
    • Java中怎么使用Nutz框架
      使用Nutz框架在Java中进行开发可以按照以下步骤进行:1. 导入Nutz框架的相关依赖:在项目的构建文件(如Maven的pom....
      99+
      2023-08-24
      Java Nutz
    • Python中tornado框架怎么使用
      Tornado 是一个Python的web框架和异步网络库,可以用于构建高性能的web应用程序。下面是一个简单的使用Tornado框...
      99+
      2024-03-01
      Python tornado
    • golang 不用框架
      golang是一门支持并发编程的静态类型编程语言。它在处理大量的web并发请求时表现非常出色,可以轻松地扩展性能。很多人在使用golang时都会使用一些常用的web框架如Beego,Gin,Martini等,但我们可以完全不使用这些框架去编...
      99+
      2023-05-19
    • Golang爬虫框架colly的使用
      目录项目特性安装colly实例colly 的配置colly页面爬取和解析colly框架重构爬虫Golang爬虫框架 colly 简介 colly是一个采用Go语言编写的Web爬虫框架...
      99+
      2024-04-02
    • Golang中Gin框架的使用入门教程
      目录安装与简单测试常见请求与分组请求获取参数 与 参数合法性验证获得query中参数获得multipart/urlencoded form中的参数模型绑定和参数验证自定义参数验证项目...
      99+
      2024-04-02
    • Golang函数如何在Web框架中使用?
      go 函数在 web 框架中用于:处理请求和生成响应。可作为可重复使用的处理器函数。可分组在函数组中以实现功能模块化。可作为中间件执行预处理或后处理操作。 Go 函数如何在 Web 框...
      99+
      2024-04-11
      golang web框架 git
    • 怎么安装golang revel框架
      这篇文章主要介绍“怎么安装golang revel框架”,在日常操作中,相信很多人在怎么安装golang revel框架问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么安装golang revel框架”的疑...
      99+
      2023-07-05
    • golang tcp框架怎么实现
      要实现一个基于TCP的框架,可以参考以下步骤: 导入所需的包: net、io、bufio、sync等。 创建一个TCP服务器...
      99+
      2024-02-29
      golang
    • Node.js中怎么使用Express4.x框架
      这篇文章将为大家详细讲解有关Node.js中怎么使用Express4.x框架,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Web应用创建首先要做的是下载ex...
      99+
      2024-04-02
    • 怎么在php中使用swoft框架
      本篇文章为大家展示了怎么在php中使用swoft框架,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。PHP开发环境搭建工具有哪些一、phpStudy,是一个新手入门最常用的开发环境。二、WampSer...
      99+
      2023-06-14
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作