Go net/rpc

2021-04-10  本文已影响0人  JunChow520

Golang官方提供的net/rpc库使用encoding/gob进行编解码,支持TCP或HTTP数据传输方式,由于其它语言不支持gob编解码方式,因此使用net/rpc库实现的RPC方法是没有办法进行跨语言调用。

import "net/rpc"

Golang的RPC支持三个级别的RPC,分别是TCP、HTTP、JSONRPC。net/rpc与传统的RPC不同,它只支持Golang开发的服务器和服务端之间的交互,因为在内部采用过了encoding/gob来编解码。

本地方法

type Result struct{
    Num int
    Data int
}

type Cal int
func (c *Cal) Square(num int) *Result{
    return &Result{Num:num, Data:num*num}
}
cal := new(Cal)
result := cal.Square(10)
fmt.Printf("num = %d, square = %d\n", result.Num, result.Data)//num = 10, square = 100

RPC Service

服务端首先需要注册服务,然后让服务实例所属的方法暴露给调用者,从而提供服务(方法)。服务实例对应的方法称之为输出方法,输出方法可被远程客户端调用。在定义输出方法时,必须满足规定的规则才能被远程客户端调用,不然会被忽略。

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

T、T1、T2类型都必须能被encoding/gob包编解码,net/rpc可利用HTTP和TCP来传递数据。

RPC服务必须满足以下五种要求

项目 参数 规则
方法类型 T 方法的类型必须是可导出的(可输出的),首字母大写。
方法名 MethodName 方法本身必须是可导出的
方法参数 T1, T2 方法参数有且仅有两个,同时参数必须是可导出或内建类型的。
方法参数 replyType 方法第二个参数必须是指针类型
方法返回值 error 返回值类型必须为error

net/rpc对方法参数个数限制仅能有2个,分别为argTypereplyType

参数 类型 描述
t T 服务对象所属类型
argType T1 调用者提供的请求类型
replyType *T2 返回给调用者的响应类型

根据net/rpc规定改造本地方法

type Cal int
func (c *Cal) Square(num int, result *Result) error{
    result.Num = num
    result.Data = num * num
    return nil
}
var result Result
cal := new(Cal)
cal.Square(10, &result)
fmt.Printf("num = %d, square = %d\n", result.Num, result.Data)//num = 10, square = 100

RPC Server

RPC服务端

RPC HTTP Server

例如:实现net/rpc库实现RPC方法,使用HTTP作为RPC的载体,通过net/http包监听客户端连接请求。

$ vim server/main.go
package main

import (
    "log"
    "net/http"
    "net/rpc"
)

type Result struct{
    Num int
    Data int
}

type Cal int
func (c *Cal) Square(num int, result *Result) error{
    result.Num = num
    result.Data = num * num
    return nil
}

func main(){
    //发布满足RPC注册条件的方法
    err := rpc.Register(new(Cal))
    if err!=nil{
        log.Fatal(err)
    }
    //注册用于处理RPC消息的HTTP处理器
    rpc.HandleHTTP()
    //监听端口等待RPC请求
    log.Println("RPC Server listen on 7000")
    err = http.ListenAndServe(":7001", nil)
    if err!=nil {
        log.Fatal("ERROR:", err)
    }
}
$ go run main.go
2021/04/09 20:49:03 RPC Server listen on 7000

RPC TCP Server

type User struct{
    Id int
    Name string
}

type RpcServer struct{}
func (s *RpcServer) User(user User, message *string) error{
    *message = fmt.Sprintf("id = %d, name = %s", user.Id, user.Name)
    return nil
}
rcvr := new(RpcServer)
//注册RPC服务
err := rpc.Register(rcvr)
if err!=nil{
    log.Fatalln("TPC Server: rpc register error:", err)
}
//创建TCP服务端
addr,err := net.ResolveTCPAddr("tcp", ":7001")
if err != nil{
    log.Fatalln("TCP Resolve Addr error:", err)
}
//指定地址监听TCP网络请求
listener,err := net.ListenTCP("tcp", addr)
if err!=nil {
    log.Fatalln("TCP Listen error:", err)
}
//客户端
for{
    //监听请求获取连接对象
    conn,err := listener.Accept()
    if err!=nil {
        log.Println("connect error")
        continue
    }
    //创建一个goroutine处理连接
    go rpc.ServeConn(conn)
}
$ go run server/main.go

rpc.Register

func rpc.Register(rcvr interface{}) error

rpc.RegisterName

func rpc.RegisterName(name string, recv interface{}) error

rpc.HandleHTTP

func HandleHTTP() {
    DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
}

rpc.HandleHTTP()会调用http.Handle()在预定义路径上DefaultRPCPath注册处理器,这个处理器最终被添加到net/http包中的默认多路复用器。

func (server *Server) HandleHTTP(rpcPath, debugPath string) {
    http.Handle(rpcPath, server)
    http.Handle(debugPath, debugHTTP{server})
}

http.ListenAndServe

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

rpc.ServeConn

ServeConn在单个连接上执行Server,ServeConn会阻塞,服务该连接直到客户端挂起。

func ServeConn(conn io.ReadWriteCloser) {
    DefaultServer.ServeConn(conn)
}

调用者应另开线程调用go server.ServeConn(conn)

ServeConn在该连接使用encoding/gob包有线格式。

RPC Client

RPC客户端

RPC HTTP Client

$ vim client/main.go
package main

import (
    "log"
    "net/rpc"
)

type Result struct{
    Num int
    Data int
}

func main(){
    client,err := rpc.DialHTTP("tcp", "127.0.0.1:7001")
    if err!=nil {
        log.Fatal("RPC Client ERROR:", err)
    }

    var result Result
    err = client.Call("Cal.Square", 10, &result)
    if err!=nil {
        log.Fatal("RPC Client Call Error:", err)
    }

    log.Printf("Num = %d, Data = %d\n", result.Num, result.Data)
}
$ go run main.go
2021/04/09 20:49:47 Num = 10, Data = 100

RPC TCP Client

type User struct{
    Id int
    Name string
}
//连接RPC服务端
client,err := rpc.Dial("tcp", "127.0.0.1:7001")
if err!=nil {
    panic(err)
}
defer client.Close()
//发送请求
var reply string
user := &User{Id:1, Name:"root"}
err = client.Call("RpcServer.User", user, &reply)
if err!=nil{
    panic(err)
}
log.Println(reply)

client.Call

func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error

client.Go

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call

异步调用方式

var result Result

call := client.Go("Cal.Square", 10, &result, nil)
<-call.Done

log.Printf("Num = %d, Data = %d\n", result.Num, result.Data)
上一篇 下一篇

猜你喜欢

热点阅读