Go net/rpc

JunChow520 · · 829 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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

  • net/rpc包提供通过网络或其它I/O连接对一个对象的导出方法的访问
  • 服务端注册一个对象,使其作为一个服务被暴露,服务的名字是该对象的类型名,注册后对象导出的方法就可以被远程访问。
  • 服务端可以注册多个不同类型的对象(服务),禁止注册具有相同类型的多个对象。

Golang的RPC函数需要满足以下条件才能被远程调用,不然会被忽略:

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

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

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

项目 名称 描述
方法类型 T 导出的,首字母大写。
方法名 MethodName 导出的
方法参数 - 均为导出或内置的内容
方法参数 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服务端
  • 将本地方法放在服务端,注册服务器定义的方法。
  • 服务端需要提供一个套接字服务,监听客户端发送的请求,解析获得客户端的请求参数。
  • 获取请求参数执行服务器的调用函数,将返回结果交给客户端。

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

  • Register用来向Server服务端注册RPC服务
  • 默认rpc.Register()会将方法接收者(revr,receiver)的类型名作为方法名前缀。
func rpc.Register(rcvr interface{}) error

rpc.RegisterName

  • 可调用rpc.RegisterName()方法来设置RPC方法名前缀
func rpc.RegisterName(name string, recv interface{}) error

rpc.HandleHTTP

  • rpc.HandleHTTP()采用HTTP协议作为RPC载体,用于注册HTTP路由。
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

  • http.ListenAndServe用于在指定端口上启动HTTP服务,请求RPC方法会交给RPC内部路由进行处理。
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}
  • http.ListenAndServer第二个参数传入nil时会使用默认的多路复用器

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

  • 创建HTTP服务器调用远程方法
$ 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

  • client.Call同步调用会阻塞当前程序直到结果返回
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error

client.Go

  • client.Go异步调用通过调用<-call.Done阻塞当前程序直到RPC调用结束
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)

证书鉴权


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:JunChow520

查看原文:Go net/rpc

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

829 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传