返回顶部
首页 > 资讯 > 精选 >Go的内置RPC原理是什么
  • 460
分享到

Go的内置RPC原理是什么

2023-07-05 08:07:46 460人浏览 八月长安
摘要

这篇文章主要介绍“Go的内置rpc原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go的内置RPC原理是什么”文章能帮助大家解决问题。从一个 Demo 入手为了快速进入状态,我们先搞一个 D

这篇文章主要介绍“Go的内置rpc原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go的内置RPC原理是什么”文章能帮助大家解决问题。

    从一个 Demo 入手

    为了快速进入状态,我们先搞一个 Demo,当然这个 Demo 是参考 Go 源码 src/net/rpc/server.go,做了一丢丢的修改。

    首先定义请求的入参和出参:

    package commontype Args struct {A, B int}type Quotient struct {Quo, Rem int}

    接着在定义一个对象,并给这个对象写两个方法

    type Arith struct{}func (t *Arith) Multiply(args *common.Args, reply *int) error {*reply = args.A * args.Breturn nil}func (t *Arith) Divide(args *common.Args, quo *common.Quotient) error {if args.B == 0 {return errors.New("divide by zero")}quo.Quo = args.A / args.Bquo.Rem = args.A % args.Breturn nil}

    然后起一个 RPC server:

    func main() {arith := new(Arith)rpc.ReGISter(arith)rpc.HandleHttp()l, e := net.Listen("tcp", ":9876")if e != nil {panic(e)}go http.Serve(l, nil)var wg sync.WaitGroupwg.Add(1)wg.Wait()}

    最后初始化 RPC Client,并发起调用:

    func main() {client, err := rpc.DialHTTP("tcp", "127.0.0.1:9876")if err != nil {panic(err)}args := common.Args{A: 7, B: 8}var reply int  // 同步调用err = client.Call("Arith.Multiply", &args, &reply)if err != nil {panic(err)}fmt.Printf("Call Arith: %d * %d = %d\n", args.A, args.B, reply)  // 异步调用quotient := new(common.Quotient)divCall := client.Go("Arith.Divide", args, quotient, nil)replyCall := <-divCall.Donefmt.Printf("Go Divide: %d divide %d = %+v %+v\n", args.A, args.B, replyCall.Reply, quotient)}

    如果不出意外,RPC 调用成功

    Go的内置RPC原理是什么

    这 RPC 吗

    在剖析原理之前,我们先想想什么是 RPC?

    RPC 是 Remote Procedure Call 的缩写,一般翻译为远程过程调用,不过我觉得这个翻译有点难懂,啥叫过程?如果查一下 Procedure,就能发现它就是应用程序的意思。

    所以翻译过来应该是调用远程程序,说人话就是调用的方法不在本地,不能通过内存寻址找到,只能通过远程通信来调用。

    一般来说 RPC 框架存在的意义是让你调用远程方法像调用本地方法一样方便,也就是将复杂的编解码、通信过程都封装起来,让代码写起来更简单。

    说到这里其实我想吐槽一下,网上经常有文章说,既然有 Http,为什么还要有 RPC?如果你理解 RPC,我相信你不会问出这样的问题,他们是两个维度的东西,RPC 关注的是远程调用的封装,Http 是一种协议,RPC 没有规定通信协议,RPC 也可以使用 Http,这不矛盾。这种问法就好像在问既然有了苹果手机,为什么还要有中国移动?

    扯远了,我们回头看一下上述的例子是否符合我们对 RPC 的定义。

    • 首先是远程调用,我们是开了一个 Server,监听了9876端口,然后 Client 与之通信,将这两个程序部署在两台机器上,只要网络是通的,照样可以正常工作

    • 其次它符合调用远程方法像调用本地方法一样方便,代码中没有处理编解码,也没有处理通信,只不过方法名以参数的形式传入,和一般的 RPC 稍有不同,倒是很像 dubbo 的泛化调用

    综上两点,这很 RPC。

    下面我将用两段内容分别剖析 Go 内置的 RPC Server 与 Client 的原理,来看看 Go 是如何实现一个 RPC 的。

    RPC Server 原理

    注册服务

    这里的服务指的是一个具有公开方法的对象,比如上面 Demo 中的 Arith,只需要调用 Register 就能注册

    rpc.Register(arith)

    注册完成了以下动作:

    • 利用反射获取这个对象的类型、类名、值、以及公开方法

    • 将其包装为 service 对象,并存在 server 的 serviceMap 中,serviceMap 的 key 默认为类名,比如这里是Arith,也可以调用另一个注册方法 RegisterName 来自定义名称

    注册 Http Handle

    这里你可能会问,为啥 RPC 要注册 Http Handle。没错,Go 内置的 RPC 通信是基于 Http 协议的,所以需要注册。只需要一行代码:

    rpc.HandleHTTP()

    它调用的是 Http 的 Handle 方法,也就是 HandleFunc 的底层实现,这块如果不清楚,可以看我之前的文章《一文读懂 Go Http Server 原理》。

    它注册了两个特殊的 Path:/_goRPC_ 和 /debug/rpc,其中有一个是 Debug 专用,当然也可以自定义。

    逻辑处理

    注册时传入了 RPC 的 server 对象,这个对象必须实现 Handler 的 ServeHTTP 接口,也就是 RPC 的处理逻辑入口在这个 ServeHTTP 中:

    type Handler interface {ServeHTTP(ResponseWriter, *Request)}

    我们看 RPC Server 是如何实现这个接口的:

    // ServeHTTP implements an http.Handler that answers RPC requests.func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {// ①  if req.Method != "CONNECT" {w.Header().Set("Content-Type", "text/plain; charset=utf-8")w.WriteHeader(http.StatusMethodNotAllowed)io.WriteString(w, "405 must CONNECT\n")return}  // ②conn, _, err := w.(http.Hijacker).Hijack()if err != nil {log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())return}  // ③io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")// ④server.ServeConn(conn)}

    我对这段代码标了号,逐一看:

    ①:限制了请求的 Method 必须是 CONNECT,如果不是则直接返回错误,这么做是为什么?看下 Method 字段的注释就恍然大悟:Go 的 Http Client 是发不出 CONNECT 的请求,也就是 RPC 的 Server 是没办法通过 Go 的 Http Client 访问,限制必须得使用 RPC Client

    type Request struct {// Method specifies the HTTP method (GET, POST, PUT, etc.).// For client requests, an empty string means GET.//// Go's HTTP client does not support sending a request with// the CONNECT method. See the documentation on Transport for// details.Method string}

    ②:Hijack 是劫持 Http 的连接,劫持后需要手动处理连接的关闭,这个操作是为了复用连接

    ③:先写一行响应:

    "HTTP/1.0 200 Connected to Go RPC \n\n"

    ④:开始真正的处理,这里段比较长,大致做了如下几点事情:

    准备好数据、编解码器

    在一个大循环里处理每一个请求,处理流程是:

    • 读出请求,包括要调用的service,参数等

    • 通过反射异步地调用对应的方法

    • 将执行结果编码写回连接

    说到这里,代码中有个对象池的设计挺巧妙,这里展开说说。

    高并发下,Server 端的 Request 对象和 Response 对象会频繁地创建,这里用了队列来实现了对象池。以 Request 对象池做个介绍,在 Server 对象中有一个 Request 指针,Request 中有个 next 指针

    type Server struct {...freeReq    *Request..}type Request struct {ServiceMethod string Seq           uint64next          *Request}

    在读取请求时需要这个对象,如果池中没有对象,则 new 一个出来,有的话就拿到,并将 Server 中的指针指向 next:

    func (server *Server) getRequest() *Request {server.reqLock.Lock()req := server.freeReqif req == nil {req = new(Request)} else {server.freeReq = req.next*req = Request{}}server.reqLock.Unlock()return req}

    请求处理完成时,释放这个对象,插入到链表的头部

    func (server *Server) freeRequest(req *Request) {server.reqLock.Lock()req.next = server.freeReqserver.freeReq = reqserver.reqLock.Unlock()}

    画个图整体感受下:

    Go的内置RPC原理是什么

    回到正题,Client 和 Server 之间只有一条连接,如果是异步执行,怎么保证返回的数据是正确的呢?这里先不说,如果一次性说完了,下一节的 Client 就没啥可说的了,你说是吧?

    RPC Client 原理

    Client 使用第一步是 New 一个 Client 对象,在这一步,它偷偷起了一个协程,干什么呢?用来读取 Server 端的返回,这也是 Go 惯用的伎俩。

    每一次 Client 的调用都被封装为一个 Call 对象,包含了调用的方法、参数、响应、错误、是否完成。

    同时 Client 对象有一个 pending map,key 为请求的递增序号,当 Client 发起调用时,将序号自增,并把当前的 Call 对象放到 pending map 中,然后再向连接写入请求。

    写入的请求先后分别为 Request 和参数,可以理解为 header 和 body,其中 Request 就包含了 Client 的请求自增序号。

    Server 端响应时把这个序号带回去,Client 接收响应时读出返回数据,再去 pending map 里找到对应的请求,通知给对应的阻塞协程。

    这不就能把请求和响应串到一起了吗?这一招很多 RPC 框架也是这么玩的。

    Go的内置RPC原理是什么

    Client 、Server 流程都走完,但我们忽略了编解码细节,Go RPC 默认使用 gob 编解码器,这里也稍微介绍下 gob。

    gob 编解码

    gob 是 Go 实现的一个 Go 亲和的协议,可以简单理解这个协议只能在 Go 中用。Go Client RPC 对编解码接口的定义如下:

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

    同理,Server 端也有一个定义:

    type ServerCodec interface {ReadRequestHeader(*Request) errorReadRequestBody(interface{}) errorWriteResponse(*Response, interface{}) error  Close() error}

    gob 是其一个实现,这里只看 Client:

    func (c *gobClientCodec) WriteRequest(r *Request, body interface{}) (err error) {if err = c.enc.Encode(r); err != nil {return}if err = c.enc.Encode(body); err != nil {return}return c.encBuf.Flush()}func (c *gobClientCodec) ReadResponseHeader(r *Response) error {return c.dec.Decode(r)}func (c *gobClientCodec) ReadResponseBody(body interface{}) error {return c.dec.Decode(body)}

    追踪到底层就是 Encoder 的 EncodeValue 和 DecodeValue 方法,Encode 的细节我不打算写,因为我也不想看这一块,最终结果就是把结构体编码成了二进制数据,调用 writeMessage。

    关于“Go的内置RPC原理是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网精选频道,小编每天都会为大家更新不同的知识点。

    --结束END--

    本文标题: Go的内置RPC原理是什么

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

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

    猜你喜欢
    • Go的内置RPC原理是什么
      这篇文章主要介绍“Go的内置RPC原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go的内置RPC原理是什么”文章能帮助大家解决问题。从一个 Demo 入手为了快速进入状态,我们先搞一个 D...
      99+
      2023-07-05
    • 一文吃透Go的内置RPC原理
      目录从一个 Demo 入手这 RPC 吗RPC Server 原理注册服务注册 Http Handle逻辑处理RPC Client 原理gob 编解码总结从一个 Demo 入手 为了...
      99+
      2023-03-03
      Go内置RPC原理 Go RPC原理 Go RPC
    • Golang原生rpc的原理是什么
      这篇文章主要讲解了“Golang原生rpc的原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang原生rpc的原理是什么”吧!创建rpc接口,需要几个条件方法的类型是可输出的方...
      99+
      2023-06-29
    • RPC消息协议设计原理是什么
      这篇文章主要介绍了RPC消息协议设计原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇RPC消息协议设计原理是什么文章都会有所收获,下面我们一起来看看吧。消息边界RPC 需要在一条 TCP 链接上进行多次...
      99+
      2023-06-02
    • Go定时器内部的实现原理是什么
      这篇文章主要讲解了“Go定时器内部的实现原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go定时器内部的实现原理是什么”吧!前言本节,我们重点关注系...
      99+
      2024-04-02
    • Go channel实现原理是什么
      本篇内容主要讲解“Go channel实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Go channel实现原理是什么”吧!channel单纯地将函数并发执行是...
      99+
      2023-07-05
    • C++内存管理原理是什么
      这篇文章主要讲解了“C++内存管理原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++内存管理原理是什么”吧!1.C/C++中程序内存分布C/C++中程序内存区域大致划分为:内核空...
      99+
      2023-06-25
    • go协程调度的原理是什么
      Go协程的调度原理是基于M:N的模型,其中M代表操作系统的线程,N代表Go协程。Go运行时系统会创建一定数量的操作系统线程,每个线程...
      99+
      2023-10-23
      go
    • Python中的内存管理的原理是什么?
      Python中的内存管理的原理是什么?Python是一种高级的、动态类型的编程语言,具有自动垃圾回收功能。Python内存管理的原理基于引用计数机制和垃圾回收机制。引用计数机制是Python内存管理的基础。每个对象都会有一个引用计数器,用于...
      99+
      2023-10-22
      内存管理原理 Python内存管理 内存分配策略
    • MySQL排序的内部原理是什么
      MySQL排序的内部原理是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。排序,排序,排序 我们通过explain查看My...
      99+
      2024-04-02
    • Java内存模型的原理是什么
      这篇文章将为大家详细讲解有关Java内存模型的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。所有的编程语言中都有内存模型这个概念,区别于微架构的内存模型,高级语言的内存模型包括了编...
      99+
      2023-06-17
    • golang内存分配的原理是什么
      Golang中的内存分配是通过运行时系统来管理的。以下是Golang内存分配的原理: 堆分配:Golang使用一个堆来存储动态分...
      99+
      2023-10-21
      golang
    • Spring mvc中内置编码过滤器的原理是什么
      Spring mvc中内置编码过滤器的原理是什么?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。web.xml 中 添加如下配置:<filter> ...
      99+
      2023-05-31
      springmvc 滤器
    • PHP运行内存设置的方法和原理是什么
      今天小编给大家分享一下PHP运行内存设置的方法和原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、什么是 PHP ...
      99+
      2023-07-05
    • go协程调度原理是什么
      Go协程调度的原理是基于M:N调度模型。其中,M代表操作系统的线程(Machine),N代表Go语言的协程(Goroutine)。在...
      99+
      2023-10-07
      go
    • Java内存的原型及工作原理是什么
      这篇文章给大家介绍Java内存的原型及工作原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。主要通过分析Java内存分配的栈、堆以以及常量池详细的讲解了其的工作原理。一、java虚拟机内存原型寄存器:我们在程序中...
      99+
      2023-06-17
    • Python内存管理机制的原理是什么
      今天就跟大家聊聊有关Python内存管理机制的原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。小块空间的内存池在Python中,许多时候申请的内存都是小块的内存,这些小块内存...
      99+
      2023-06-17
    • Go语言中熔断的原理是什么
      本篇内容介绍了“Go语言中熔断的原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!今天我们就来一起看...
      99+
      2024-04-02
    • Go中闭包的底层原理是什么
      这篇文章将为大家详细讲解有关Go中闭包的底层原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1. 什么是闭包一个函数内引用了外部的局部变量,这种现象,就称之为闭包。例如下面的这段代码中,adder...
      99+
      2023-06-25
    • go内存管理机制是什么
      Go语言的内存管理机制是基于垃圾回收(Garbage Collection)的。 Go语言中的内存管理是由垃圾回收器负责的,它会自动...
      99+
      2023-10-27
      go
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作