48

Golang RPC实践

 4 years ago
source link: https://www.tuicool.com/articles/MvQRzq7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

摘要: 总体上来说,HTTP每次请求比较浪费资源的。虽然HTTP也是走在TCP上面的,但是HTTP请求自己添加了很多自己的信息,因此会消耗带宽资源。所以一些公司就是用RPC作为内部应用的通信协议。 原文

如果你对Go也感兴趣, 可以关注我的公众号: GoGuider

RPC

RPC(Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络细节的应用程序通信协议。RPC协议构建于TCP或UDP,或者是HTTP上。

在Go中,标准库提供的net/rpc包实现了RPC协议需要的相关细节,开发者可以很方便的使用该包编写RPC的服务端和客户端程序。

UniUVjz.png!web

从上图看, RPC本身就是一个client-server模型。

下面列举一个实例代码, 来了解RPC调用过程

server.go

package main

import (
    "fmt"
    "log"
    "net"
    "net/http"
    "net/rpc"
    "os"
    "time"
)

type Args struct {
    A, B int
}

type Math int

//计算乘积
func (t *Math) Multiply(args *Args, reply *int) error {
    time.Sleep(time.Second * 3) //睡1秒,同步调用会等待,异步会先往下执行
    *reply = args.A * args.B
    fmt.Println("Multiply")
    return nil
}

//计算和
func (t *Math) Sum(args *Args, reply *int) error {
    time.Sleep(time.Second * 3)
    *reply = args.A + args.B
    fmt.Println("Sum")
    return nil
}

func main() {
    //创建对象
    math := new(Math)
    //rpc服务注册了一个Math对象 公开方法供客户端调用
    rpc.Register(math)
    //指定rpc的传输协议 这里采用http协议作为rpc调用的载体 也可以用rpc.ServeConn处理单个连接请求
    rpc.HandleHTTP()
    l, e := net.Listen("tcp", ":1234")
    if e != nil {
        log.Fatal("listen error", e)
    }
    go http.Serve(l, nil)
    os.Stdin.Read(make([]byte, 1))
}

client.go

package main

import (
    "fmt"
    "log"
    "net/rpc"
    "time"
)

type Args struct {
    A, B int
}

func main() {
    //调用rpc服务端提供的方法之前,先与rpc服务端建立连接
    client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
    if err != nil {
        log.Fatal("dialHttp error", err)
        return
    }
    //同步调用服务端提供的方法

    args := &Args{7, 8}
    var reply int
    //可以查看源码 其实Call同步调用是用异步调用实现的。后续再详细学习
    err = client.Call("Math.Multiply", args, &reply) //这里会阻塞三秒

    if err != nil {
        log.Fatal("call Math.Multiply error", err)
    }
    fmt.Printf("Multiply:%d*%d=%d\n", args.A, args.B, reply)

    //异步调用

    var sum int

    divCall := client.Go("Math.Sum", args, ∑, nil)

    //使用select模型监听通道有数据时执行,否则执行后续程序
    for {
        select {
        case <-divCall.Done:
            fmt.Printf("%d+%d是%d, 退出执行!", args.A, args.B, sum)
            return
        default:
            fmt.Println("继续等待....")
            time.Sleep(time.Second * 1)
        }
    }
}

运行命令

go run server.go

go run client.go

运行结果

Multiply:7*8=56
继续等待....
继续等待....
继续等待....
7+8=15,出执行

调用过程解析

server端

  • rpc服务注册了一个Math对象 公开方法供客户端调用
  • 采用http协议作为rpc调用的载体, 处理请求

client端

  • 调用rpc服务端提供的方法之前,先与rpc服务端建立连接
  • 使用Call 方法调用远程方法

延伸

其实细心的朋友会注意到client.go 里面有client.Call 和 client.Go 调用;

查看源码可以看到client.Call 底层就是调用的client.Go

// 部分源码:

/ Go invokes the function asynchronously. It returns the Call structure representing
// the invocation. The done channel will signal when the call is complete by returning
// the same Call object. If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
    call := new(Call)
    call.ServiceMethod = serviceMethod
    call.Args = args
    call.Reply = reply
    if done == nil {
        done = make(chan *Call, 10) // buffered.
    } else {
        // If caller passes done != nil, it must arrange that
        // done has enough buffer for the number of simultaneous
        // RPCs that will be using that channel. If the channel
        // is totally unbuffered, it's best not to run at all.
        if cap(done) == 0 {
            log.Panic("rpc: done channel is unbuffered")
        }
    }
    call.Done = done
    client.send(call)
    return call
}

// Call invokes the named function, waits for it to complete, and returns its error status.
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error {
    call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
    return call.Error
}

参考文章


Recommend

  • 38
    • studygolang.com 5 years ago
    • Cache

    golang - gob与rpc

    今天和大家聊聊golang中怎么使用rpc,rpc数据传输会涉及到gob编码,所以先讲讲gob,别担心,就算你完全没有接触过gob与rpc,只要知道rpc的中文是远程过程调用,剩下的我都能给你讲明白(带你入门不包你精通)! 一、数据结构编...

  • 23
    • studygolang.com 5 years ago
    • Cache

    golang中的net/rpc包

    本文先介绍RPC,然后go原生对RPC的使用,之后是介绍go语言中有哪些RPC框架以及一些其他常见的框架,最后是探究go语言中rpc的源码。 (1)首先介绍下什么RPC? (2)RPC可以做什么? (3)RPC与REST风格...

  • 37
    • www.swoole.org.cn 5 years ago
    • Cache

    Tcp Rpc 踩坑实践

    最近接到需求, 目前项目满足不了, 需要通过中间件实现. 经过讨论和分析, 最后打算 使用 swoole 构建一个 Tcp Rpc 服务. 正常的Rpc 轮子遍地都是 , 但是我们的需求很独特, 需要根据 参数...

  • 33
    • studygolang.com 4 years ago
    • Cache

    golang实现RPC调用

    RPC远程调用 RPC通信过程 调用者(客户端Client)以本地调用的方式发起调用

  • 29
    • studygolang.com 3 years ago
    • Cache

    Golang之rpc框架rpcx

    前言 远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。通过该协议程序员可以实现像调取本地函数一样,调取远程服务的函数。这里介绍一个高效的rpc库(rpcx)。

  • 6
    • blog.lpflpf.cn 2 years ago
    • Cache

    Golang反射学习:手写一个RPC

    本文主要为了在对golang反射学习后做一个小练习,使用100行代码实现一个通用的RPC服务。 golang 的RPC框架还是非常丰富的,比如 gRPC,go-zero, go-dubbo 等都是使用非常普遍的rpc框架。在go语言实现的RPC客户端中,大部分RPC框架采用的是使用生成代码...

  • 0
    • studygolang.com 2 years ago
    • Cache

    golang实现RPC的几种方式

    golang实现RPC的几种方式 豆瓣奶茶 · 2018-08-21 01:34:46 · 57010 次点击 · 预计阅读时间 9 分钟 · 大约8小时之前 开始浏览     ...

  • 3

    很早之前也了解过一些rpc, 但是心理始终接受不了这种东西,觉得http 用的好好的,为什么要那么麻烦的使用rpc? 且还要定义什么proto 文件 ,后来看了一篇文章 ,既然业内都在使用并且都快成为一种行业标准了,必然有它的优势。 以下是文章原文

  • 2

    使用golang进行rpc开发之三使用grpc进行开发 2022-09-29 Go...

  • 1

    使用golang进行rpc开发之二使用json进行开发 2022-09-29 Go...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK