RPC / JSON-RPC in Golang

与蟒唯舞 · · 804 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

RPC(Remote Procedure Call)—— 远程过程调用。通过它可以使函数调用模式网络化。客户端就像调用本地函数一样,客户端把这些参数打包之后通过网络传递到服务端,服务端解包到处理过程中执行,然后将执行的结果反馈给客户端。在 OSI 网络通信模型中,RPC 跨越了传输层和应用层。RPC 使得开发分布式应用程序更加容易。

HTTP RPC server using golang net/rpc package

Go 标准包中已经提供了对 RPC 的支持,而且支持三个级别的 RPC:TCP、HTTP、JSONRPC。但 Go 的 RPC 包是独一无二的 RPC,它和传统的 RPC 系统不同,它只支持 Go 开发的服务器与客户端之间的交互,因为在内部采用了 Gob 来编码。

Go RPC 的函数只有符合下面的条件才能被远程访问:

  • 函数必须是导出的(首字母大写)
  • 必须有两个参数,并且是导出类型或者内建类型
  • 第二个参数必须是指针类型的
  • 函数还要有一个返回值 error

举个例子,正确的 RPC 函数格式如下:

func (t *T) MethodName(argType T1, replyType *T2) error

T、T1 和 T2 类型必须能被 encoding/gob 包编解码。

接下来,定义服务对象和方法:
http-rpc/arith.go

package http_rpc

import (
    "log"
)

type Arith int

type Args struct {
    A, B int
}

func (t *Arith) Multiply(args Args, result *int) error {
    log.Printf("Multiply %d with %d\n", args.A, args.B)
    *result = args.A * args.B
    return nil
} 

任何的 RPC 都需要通过网络来传递数据,Go RPC 可以利用 HTTP 和 TCP 来传递数据,利用 HTTP 的好处是可以直接复用 net/http 里面的一些函数。
http-rpc/server/main.go

package main

import (
    "log"
    "net/http"
    "net/rpc"
    . "rpc-golang/http-rpc"
)

func main() {
    arith := new(Arith)
    rpc.Register(arith)
    // 把该服务注册到 HTTP 协议上
    rpc.HandleHTTP()
    log.Println("HTTP RPC service listen and serving on port 1234")
    if err := http.ListenAndServe(":1234", nil); err != nil {
        log.Fatalf("Error serving: %s", err)
    }
}
HTTP RPC client using golang net/rpc package

http-rpc/client/main.go

package main

import (
    "log"
    "net/rpc"
    . "rpc-golang/http-rpc"
)

func main() {
    client, err := rpc.DialHTTP("tcp", ":1234")
    if err != nil {
        log.Fatalf("Error in dialing. %s", err)
    }
    args := Args{
        A: 2,
        B: 3,
    }
    var reply int
    err = client.Call("Arith.Multiply", args, &reply)
    if err != nil {
        log.Fatal("Arith error:", err)
    }
    log.Printf("%d * %d = %d\n", args.A, args.B, reply)
}
JSON RPC server using gorilla rpc/json

使用 Gorilla 工具包来简化默认的 net/rpc/jsonrpc. Gorilla 工具包与默认包之间的微小差别在于它需要方法签名来接受 * Request 对象作为第一个参数,并将 Args 参数更改为指针 * Args

net/rpc 我们的 Multiply 方法看起来是这样的:

func (t *Arith) Multiply(args Args, result *Result) error

对于 gorilla 而言, Multiply 方法看起来是这样的:

func (t *Arith) Multiply(r *http.Request, args *Args, result *Result) error

接下来,定义服务对象和方法:
json-rpc/arith.go

package json_rpc

import (
    "log"
    "net/http"
)

type Arith int

type Args struct {
    A, B int
}

func (t *Arith) Multiply(r *http.Request, args *Args, result *int) error {
    log.Printf("Multiply %d with %d\n", args.A, args.B)
    *result = args.A * args.B
    return nil
}

json-rpc/server/main.go

package main

import (
    "github.com/gorilla/mux"
    "github.com/gorilla/rpc"
    "github.com/gorilla/rpc/json"
    "log"
    "net/http"
    . "rpc-golang/json-rpc"
)

func main() {
    server := rpc.NewServer()
    server.RegisterCodec(json.NewCodec(), "application/json")
    server.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8")
    arith := new(Arith)
    server.RegisterService(arith, "")
    r := mux.NewRouter()
    r.Handle("/rpc", server)
    log.Println("JSON RPC service listen and serving on port 1234")
    if err := http.ListenAndServe(":1234", r); err != nil {
        log.Fatalf("Error serving: %s", err)
    }
}
JSON RPC client using gorilla rpc/json

json-rpc/client/main.go

package main

import (
    "bytes"
    "github.com/gorilla/rpc/json"
    "log"
    "net/http"
    . "rpc-golang/json-rpc"
)

func checkError(err error) {
    if err != nil {
        log.Fatalf("%s", err)
    }
}

func main() {
    url := "http://localhost:1234/rpc"
    args := Args{
        A: 2,
        B: 3,
    }

    message, err := json.EncodeClientRequest("Arith.Multiply", args)
    checkError(err)

    resp, err := http.Post(url, "application/json", bytes.NewReader(message))
    defer resp.Body.Close()

    checkError(err)

    reply := new(int)
    err = json.DecodeClientResponse(resp.Body, reply)
    checkError(err)

    log.Printf("%d * %d = %d\n", args.A, args.B, *reply)
}
JSON RPC client using Python

我们可以使用 Python 去调用使用 golang + gorilla 工具包写的 JSON RPC server。

json-rpc/client/main.py

# -*- coding: utf-8 -*-
import requests

"""
JSON RPC client using Python
"""
def rpc_call():
    url = 'http://localhost:1234/rpc'
    payload = {
        'id': 1,
        'method': 'Arith.Multiply',
        'params': [{'A': 2, 'B': 3}]
    }
    r = requests.post(url, json=payload)
    print r.text


if __name__ == '__main__':
    rpc_call()
JSON-RPC 简介

In JSON-RPC all messages sent from server or client are valid JSON objects. Client must send JSON object with following keys:

  • method - Name of method/service
  • params - Array of arguments to be passed
  • id - Id is usually integer and makes it easier for client to know which request it got response to, if RPC calls are done asynchroneously.

Server may reply with JSON object with following keys:

  • result - Contains return value of method called. It’s null if error ocurred.
  • error - If error occurred, this will indicate error code or error message, otherwise it’s null
  • id - The id of the request it is responding to.

Example:

Request:

{"method": "Arith.Multiply", "params": [{A: 2, B: 3}], "id": 1}

Response:

{"result": 6, "error": null, "id": 1}

JSON-RPC v2 adds support for batch queries and notifications (calls which don’t require response).

目录结构

代码参考地址:https://github.com/happy-python/rpc-golang


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:与蟒唯舞

查看原文:RPC / JSON-RPC in Golang

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

804 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传