Golang Jsonrpc Demo

RPC

RPC(Remote Procedure Call)远程过程调用在分布式技术中常常用到例如Hyperledger Fabric中内部节点通信就用到了RPC,还有如今流行的微服务架构在内部模块的沟通之间也常常使用RPC进行通信。

简单理解就是,本地机器调用远端服务机器上的方法,向远端机器指明要调用的方法、传入参数并拿回执行的结果。

Golang中的RPC

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

Go RPC的函数只有符合下面的条件才能被远程访问,不然会被忽略,详细的要求如下:

  • 函数必须是导出的(首字母大写)。

  • 必须有两个导出类型的参数:

    1)第一个参数是接收的参数,

    2)第二个参数是返回给客户端的参数*result,它必须是指针类型的。

  • 函数要返回一个error

  • 调用方法通常为:Service.Method

来源:

golang中的rpc包用法 - andyidea - 博客园 (cnblogs.com)

Demo

首先,假设函数func (DemoService) Div(args Args, result *float64) error {...}是我们需要调用的服务器上的函数,它的具体内容如下:

// rpcdemo/rpc.go
package rpcdemo

import "errors"

type DemoService struct {
}

// Args 由于有两个参数,所以构造一个 struct 作为参数
type Args struct {
	A, B int
}

// Div 调用 rpc 时, 使用 Service.Method,也就是 DemoService.Div
func (DemoService) Div(args Args, result *float64) error {
	if args.B == 0 {
		return errors.New("division by zero")
	}

	*result = float64(args.A) / float64(args.B)
	return nil
}

然后,在服务端注册该方法后,设置服务器监听请求的端口,连接到客户端后使用goroutine处理:

// rpcdemo/server/serverRpc.go
package main

import (
	"rpcdemo"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
)

func main() {
	// 向 rpc 注册服务
	rpc.Register(rpcdemo.DemoService{})
	// 设置服务端监听端口
	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		panic(err)
	}
	fmt.Println("Open serverRcp, listening on Port 1234...")

	for {
		// 监听端口到来的连接
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("accept error: %v", err)
		}
		fmt.Println("Get request from client...")
		go func() {
			jsonrpc.ServeConn(conn)	// 开一个 goroutine 去做事情,这样还能继续接收别的连接
		}()

	}
}

让客户端连接相应的端口,发送请求并展示结果:

// rpcdemo/client/clientRpc.go
package main

import (
	"rpcdemo"
	"fmt"
	"net"
	"net/rpc/jsonrpc"
)

func main() {
	// 首先客户端去连接端口
	conn, err := net.Dial("tcp", ":1234")
	if err != nil {
		panic(err)
	}
	// 告诉客户端使用该链接
	client := jsonrpc.NewClient(conn)
	// 调用服务器上的方法
	var result float64
	err = client.Call("DemoService.Div", rpcdemo.Args{10, 3}, &result)
	fmt.Println(result, err)

	err = client.Call("DemoService.Div", rpcdemo.Args{10, 0}, &result)
	fmt.Println(result, err)
}

依次运行服务器,客户端:

s1-> go run rpcdemo/server/serverRpc.go
----------------------------------
s2-> Open serverRcp, listening on Port 1234...
s4-> Get request from client...
==================================
s3-> go run rpcdemo/client/clientRpc.go
----------------------------------
s5-> 3.3333333333333335 <nil>
s6-> 3.3333333333333335 division by zero
Licensed under CC BY-NC-SA 4.0
自认为是幻象波普星的来客
Built with Hugo
主题 StackJimmy 设计