golang实现JSON-RPC 2.0协议的开发插件库jsonrpc的使用

Golang实现JSON-RPC 2.0协议的开发插件库jsonrpc的使用

关于

  • 简单、优雅、简洁
  • 符合JSON-RPC 2.0规范

安装

$ go get github.com/osamingo/jsonrpc/v2@latest

使用示例

基础示例

package main

import (
	"context"
	"log"
	"net/http"

	"github.com/goccy/go-json"
	"github.com/osamingo/jsonrpc/v2"
)

type (
	// EchoHandler 定义Echo方法处理器
	EchoHandler struct{}
	// EchoParams 定义Echo方法的参数结构
	EchoParams struct {
		Name string `json:"name"`
	}
	// EchoResult 定义Echo方法的返回结果结构
	EchoResult struct {
		Message string `json:"message"`
	}

	// PositionalHandler 定义Positional方法处理器
	PositionalHandler struct{}
	// PositionalParams 定义Positional方法的参数结构(位置参数)
	PositionalParams []int
	// PositionalResult 定义Positional方法的返回结果结构
	PositionalResult struct {
		Message []int `json:"message"`
	}
)

// ServeJSONRPC 实现Echo方法的处理逻辑
func (h EchoHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (any, *jsonrpc.Error) {
	var p EchoParams
	if err := jsonrpc.Unmarshal(params, &p); err != nil {
		return nil, err
	}

	return EchoResult{
		Message: "Hello, " + p.Name,
	}, nil
}

// ServeJSONRPC 实现Positional方法的处理逻辑
func (h PositionalHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (any, **jsonrpc.Error) {
	var p PositionalParams
	if err := jsonrpc.Unmarshal(params, &p); err != nil {
		return nil, err
	}

	return PositionalResult{
		Message: p,
	}, nil
}

func main() {
	// 创建方法仓库
	mr := jsonrpc.NewMethodRepository()

	// 注册Echo方法
	if err := mr.RegisterMethod("Main.Echo", EchoHandler{}, EchoParams{}, EchoResult{}); err != nil {
		log.Fatalln(err)
	}

	// 注册Positional方法
	if err := mr.RegisterMethod("Main.Positional", PositionalHandler{}, PositionalParams{}, PositionalResult{}); err != nil {
		log.Fatalln(err)
	}

	// 设置HTTP路由
	http.Handle("/jrpc", mr)
	http.HandleFunc("/jrpc/debug", mr.ServeDebug)

	// 启动HTTP服务
	if err := http.ListenAndServe(":8080", http.DefaultServeMux); err != nil {
		log.Fatalln(err)
	}
}

高级示例

package main

import (
	"log"
	"net/http"

	"github.com/osamingo/jsonrpc/v2"
)

type (
	// HandleParamsResulter 定义处理器接口
	HandleParamsResulter interface {
		jsonrpc.Handler
		Name() string
		Params() any
		Result() any
	}
	
	// Servicer 定义服务接口
	Servicer interface {
		MethodName(HandleParamsResulter) string
		Handlers() []HandleParamsResulter
	}
	
	// UserService 用户服务结构
	UserService struct {
		SignUpHandler HandleParamsResulter
		SignInHandler HandleParamsResulter
	}
)

// MethodName 实现服务接口的方法名生成
func (us *UserService) MethodName(h HandleParamsResulter) string {
	return "UserService." + h.Name()
}

// Handlers 返回所有处理器
func (us *UserService) Handlers() []HandleParamsResulter {
	return []HandleParamsResulter{us.SignUpHandler, us.SignInHandler}
}

// NewUserService 创建用户服务实例
func NewUserService() *UserService {
	return &UserService{
	// 初始化处理器
	}
}

func main() {
	// 创建方法仓库
	mr := jsonrpc.NewMethodRepository()

	// 注册服务方法
	for _, s := range []Servicer{NewUserService()} {
		for _, h := range s.Handlers() {
			mr.RegisterMethod(s.MethodName(h), h, h.Params(), h.Result())
		}
	}

	// 设置HTTP路由
	http.Handle("/jrpc", mr)
	http.HandleFunc("/jrpc/debug", mr.ServeDebug)

	// 启动HTTP服务
	if err := http.ListenAndServe(":8080", http.DefaultServeMux); err != nil {
		log.Fatalln(err)
	}
}

调用示例

调用Echo方法

请求:

{
  "jsonrpc": "2.0",
  "method": "Main.Echo",
  "params": {
    "name": "John Doe"
  },
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}

响应:

{
  "jsonrpc": "2.0",
  "result": {
    "message": "Hello, John Doe"
  },
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}

调用Positional方法

请求:

{
  "jsonrpc": "2.0",
  "method": "Main.Positional",
  "params": [3,1,1,3,5,3],
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}

响应:

{
  "jsonrpc": "2.0",
  "result": {
    "message": [3,1,1,3,5,3]
  },
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}

访问调试接口

响应:

[
  {
    "handler": "EchoHandler",
    "name": "Main.Echo",
    "params": {
      "$ref": "#/definitions/EchoParams",
      "$schema": "http://json-schema.org/draft-04/schema#",
      "definitions": {
        "EchoParams": {
          "additionalProperties": false,
          "properties": {
            "name": {
              "type": "string"
            }
          },
          "required": [
            "name"
          ],
          "type": "object"
        }
      }
    },
    "result": {
      "$ref": "#/definitions/EchoResult",
      "$schema": "http://json-schema.org/draft-04/schema#",
      "definitions": {
        "EchoResult": {
          "additionalProperties": false,
          "properties": {
            "message": {
              "type": "string"
            }
          },
          "required": [
            "message"
          ],
          "type": "object"
        }
      }
    }
  }
]

许可证

MIT许可证


更多关于golang实现JSON-RPC 2.0协议的开发插件库jsonrpc的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现JSON-RPC 2.0协议的开发插件库jsonrpc的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang实现JSON-RPC 2.0协议开发指南

JSON-RPC 2.0是一种轻量级的远程过程调用协议,使用JSON作为数据格式。在Golang中,标准库net/rpc/jsonrpc提供了基本的JSON-RPC支持,但功能较为基础。下面我将介绍如何使用更强大的第三方库github.com/gorilla/rpc/v2/jsonrpc来实现JSON-RPC 2.0服务。

安装依赖

首先安装必要的库:

go get github.com/gorilla/rpc/v2
go get github.com/gorilla/rpc/v2/jsonrpc

服务端实现

1. 定义服务方法

package main

import (
	"errors"
	"net/http"
	"log"

	"github.com/gorilla/rpc/v2"
	"github.com/gorilla/rpc/v2/jsonrpc"
)

type Args struct {
	A, B int
}

type Reply struct {
	Result int
}

type ArithService struct{}

func (s *ArithService) Multiply(r *http.Request, args *Args, reply *Reply) error {
	reply.Result = args.A * args.B
	return nil
}

func (s *ArithService) Divide(r *http.Request, args *Args, reply *Reply) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	reply.Result = args.A / args.B
	return nil
}

2. 创建并启动服务

func main() {
	// 创建RPC服务
	s := rpc.NewServer()
	s.RegisterCodec(jsonrpc.NewCodec(), "application/json")
	
	// 注册服务
	arith := new(ArithService)
	s.RegisterService(arith, "")
	
	// 设置HTTP路由
	http.Handle("/rpc", s)
	
	// 启动HTTP服务
	log.Println("Starting JSON-RPC server on localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

客户端实现

1. 创建客户端

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Client struct {
	url string
}

func NewClient(url string) *Client {
	return &Client{url: url}
}

func (c *Client) Call(method string, args interface{}, reply interface{}) error {
	// 构造请求体
	request := map[string]interface{}{
		"jsonrpc": "2.0",
		"method":  method,
		"params":  args,
		"id":      1,
	}
	
	// 编码为JSON
	body, err := json.Marshal(request)
	if err != nil {
		return err
	}
	
	// 发送HTTP请求
	resp, err := http.Post(c.url, "application/json", bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	
	// 读取响应
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	
	// 解析响应
	var response struct {
		Jsonrpc string      `json:"jsonrpc"`
		Result  interface{} `json:"result"`
		Error   interface{} `json:"error"`
		Id      int         `json:"id"`
	}
	
	if err := json.Unmarshal(data, &response); err != nil {
		return err
	}
	
	// 处理错误
	if response.Error != nil {
		return fmt.Errorf("RPC error: %v", response.Error)
	}
	
	// 解码结果
	resultBytes, err := json.Marshal(response.Result)
	if err != nil {
		return err
	}
	
	return json.Unmarshal(resultBytes, reply)
}

2. 使用客户端调用服务

func main() {
	client := NewClient("http://localhost:8080/rpc")
	
	// 乘法调用
	var multiplyReply Reply
	err := client.Call("ArithService.Multiply", &Args{A: 5, B: 6}, &multiplyReply)
	if err != nil {
		fmt.Println("Multiply error:", err)
	} else {
		fmt.Println("Multiply result:", multiplyReply.Result)
	}
	
	// 除法调用
	var divideReply Reply
	err = client.Call("ArithService.Divide", &Args{A: 10, B: 2}, &divideReply)
	if err != nil {
		fmt.Println("Divide error:", err)
	} else {
		fmt.Println("Divide result:", divideReply.Result)
	}
	
	// 错误调用(除零)
	err = client.Call("ArithService.Divide", &Args{A: 10, B: 0}, &divideReply)
	if err != nil {
		fmt.Println("Divide error:", err)
	} else {
		fmt.Println("Divide result:", divideReply.Result)
	}
}

高级特性

1. 批量请求支持

JSON-RPC 2.0支持批量请求,可以一次发送多个方法调用:

func (c *Client) BatchCall(requests []map[string]interface{}) ([]map[string]interface{}, error) {
	body, err := json.Marshal(requests)
	if err != nil {
		return nil, err
	}

	resp, err := http.Post(c.url, "application/json", bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var responses []map[string]interface{}
	if err := json.Unmarshal(data, &responses); err != nil {
		return nil, err
	}

	return responses, nil
}

2. 通知(Notification)支持

JSON-RPC 2.0支持通知(没有返回值的调用):

func (c *Client) Notify(method string, args interface{}) error {
	request := map[string]interface{}{
		"jsonrpc": "2.0",
		"method":  method,
		"params":  args,
	}
	
	body, err := json.Marshal(request)
	if err != nil {
		return err
	}
	
	_, err = http.Post(c.url, "application/json", bytes.NewBuffer(body))
	return err
}

错误处理

JSON-RPC 2.0定义了标准错误格式:

type RPCError struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

func (e *RPCError) Error() string {
	return fmt.Sprintf("RPC error %d: %s", e.Code, e.Message)
}

// 预定义错误
var (
	ErrParseError     = &RPCError{Code: -32700, Message: "Parse error"}
	ErrInvalidRequest = &RPCError{Code: -32600, Message: "Invalid Request"}
	ErrMethodNotFound = &RPCError{Code: -32601, Message: "Method not found"}
	ErrInvalidParams  = &RPCError{Code: -32602, Message: "Invalid params"}
	ErrInternal      = &RPCError{Code: -32603, Message: "Internal error"}
)

总结

通过gorilla/rpc/jsonrpc库,我们可以方便地实现JSON-RPC 2.0协议的服务端和客户端。主要特点包括:

  1. 支持JSON-RPC 2.0规范
  2. 易于集成到现有HTTP服务中
  3. 支持批量请求和通知
  4. 提供了标准的错误处理机制

对于更简单的需求,也可以考虑使用标准库net/rpc/jsonrpc,但它只实现了JSON-RPC 1.0规范,功能较为有限。

在实际项目中,你可能还需要考虑添加认证、限流、日志记录等中间件功能,这些都可以通过包装HTTP处理器来实现。

回到顶部