Golang 学习笔记

RPC / JSON-RPC in Golang

2018-10-19  本文已影响29人  与蟒唯舞

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 的函数只有符合下面的条件才能被远程访问:

举个例子,正确的 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:

Server may reply with JSON object with following keys:

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

上一篇下一篇

猜你喜欢

热点阅读