Golang-RPC(二):golang中的rpc实现之json-rpc

因为内置net/rpc包接口设计的缺陷,通过追踪 rpc.HandleHTTP() 方法,找到 ServeHTTP 方法:

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
     
	if req.Method != "CONNECT" {
     
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)
		io.WriteString(w, "405 must CONNECT\n")
		return
	}
	conn, _, err := w.(http.Hijacker).Hijack()
	if err != nil {
     
		log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
		return
	}
	io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
	server.ServeConn(conn)
}

func (server *Server) ServeConn(conn io.ReadWriteCloser) {
     
	buf := bufio.NewWriter(conn)
	srv := &gobServerCodec{
     
		rwc:    conn,
		dec:    gob.NewDecoder(conn),
		enc:    gob.NewEncoder(buf),
		encBuf: buf,
	}
	server.ServeCodec(srv)
}

根本就没有设置codec的参数,直接写死了 gob ,所以依赖于 net/rpc 包的除了 gob 之外的编码方式都只能使用 tcp 协议。

我们无法使用jsonrpc等定制的编码作为rpc.DialHTTP的底层协议,如果需要让jsonrpc支持rpc.DialHTTP函数,需要调整rpc的接口。

除了传输协议,还有可以指定一个RPC编码协议,用于编码/解码RPC调用的函数参数和返回值,RPC调用不指定编码协议时,默认采用Go语言特有的gob编码协议。

因为,, 其他语言一般都不支持Go语言的gob协议,因此如果需要跨语言RPC调用就需要采用通用的编码协议。

Go的标准库还提供了一个"net/rpc/jsonrpc"包,用于提供基于JSON编码的RPC支持。

JSON RPC采用JSON进行数据编解码,因而支持跨语言调用。但目前的jsonrpc库是基于tcp协议实现的,暂时不支持使用http进行数据传输。

定义服务

package repo

import (
	"errors"
)

type Order struct {
     }

type OrderInfo struct {
     
	Id string
	Price float64
	Status int
}

func (o *Order) GetOne(orderId string, orderInfo *OrderInfo) error {
     
	if orderId == "" {
     
		return errors.New("orderId is invalid")
	}

	*orderInfo = OrderInfo{
     
		Id: orderId,
		Price: 100.00,
		Status: 1,
	}

	return nil
}

服务端

package main

import (
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"

	"demo1/go-rpc/repo"
)

func main() {
     
	err := rpc.Register(new(repo.Order))
	if err != nil {
     
		log.Fatal(err)
	}

	l, err := net.Listen("tcp", ":8100")
	if err != nil {
     
		log.Fatal(err)
	}

	for {
     
		conn, e := l.Accept()
		if e != nil {
     
			continue
		}
		go jsonrpc.ServeConn(conn)
	}
}

客户端

package main

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

	"demo1/go-rpc/repo"
)

func main() {
     
	client, err := jsonrpc.Dial("tcp", "127.0.0.1:8100")
	if err != nil {
     
		log.Fatal("dialing:", err)
	}

	orderId := "asddddddd"
	var orderInfo repo.OrderInfo

	err = client.Call("Order.GetOne", orderId, &orderInfo)
	if err != nil {
     
		log.Fatal("Order error:", err)
	}
	fmt.Println(orderInfo)
}

jsonrpc 中的协议说明

使用tcp工具连接测试

1、请求参数列表

id		int 	编号,默认是0
method	string	要调用的方法
params			参数,数组或者是map,如果参数是单个的值应转换成数组。

例如

{
     "method":"Order.GetOne","params":["asddddddd"],"id":0}
{
     "method":"Order.GetOne","params":{
     "A":9,"B":2},"id":0}

2、响应参数

id		int		编号,默认是0
result			结果
error	string	错误信息

如果成功,返回结果:

{
     "id":0,"result":{
     "Id":"asddddddd","Price":100,"Status":1},"error":null}

如果失败,返回结果:

{
     "id":0,"result":null,"error":"rpc: can't find service Arith.Multiply"}
{
     "id":0,"result":null,"error":"json: cannot unmarshal string into Go value of type [1]interface {}"}

你可能感兴趣的:(golang)