RPC包简介

rpc包实现了通过网络或者别的io连接,来远程访问对象的导出方法。对象以其类型名作为标识注册为可访问的服务。注册之后,对象的导出方法将可以远程访问。一个服务可以注册多个不同类型的对象,但是注册多个相同类型的对象是错误的。
只有满足这些标准的方法才可以被远端访问,别的方法会被忽略:

  1. 对象是导出的
  2. 方法是导出的
  3. 方法有两个参数,均是导出(或内置)类型
  4. 方法的第二个参数是一个指针
  5. 方法有一个错误返回值

实际上,方法应该看起来像这样:

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

其中T1和T2可以被encoding/gob编码。即便使用不同的编解码器,这些要求也同样适用(在将来,这些要求可能针对自定义编解码器会有所缓和)。
方法的第一个参数表示调用者的入参,第二个参数表示返回的出参。方法的返回值(如果不是nil),可以认为是由errors.New创建的。如果方法返回错误,出参将不会返回。
在创建客户端原始网络连接上,希望使用该服务的客户端会建立了一个连接,然后在这个连接上调用NewClient,现在Dial方法封装了这两步。生成的客户端对象有两个方法,Call和Go。调用时,需要指定服务和方法,然后两个指针参数,一个指针包含请求参数,另一个指针保存返回结果。
Call方法同步等待远程方法执行完成,而Go方法异步启动调用并使用Call结构体的Done管道发出完成信号。
除非显式设置编解码器,默认使用encoding/gob传输数据。

例子

这里有一个简单的例子,服务端希望导出Arith对象,客户端调用其Multiply方法

服务端

服务端基于http提供服务,暴露了Arith.Multiply和Arith.Divide两个rpc接口供客户端调用

package server

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

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

func StartServer() {
	arith := new(Arith)
	rpc.Register(arith)
	rpc.HandleHTTP()
	l, e := net.Listen("tcp", ":1234")
	if e != nil {
		log.Fatal("listen error:", e)
	}
	go http.Serve(l, nil)
	time.Sleep(time.Second)
}

客户端

客户端同步调用Arith.Multiply方法

package main

import (
	"demo/com.my/rpc/server"
	"fmt"
	"log"
	"net/rpc"
	"time"
)

func main() {
	server.StartServer()

	serverAddress := "127.0.0.1"
	client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
	if err != nil {
		log.Fatal("dialing:", err)
	}

	// Synchronous call
	args := &server.Args{A: 7, B: 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)

	time.Sleep(time.Hour)

}

连接分析

rpc.HandleHTTP

rpc在http通道上注册了两个路由,DefaultRPCPath和DefaultDebugPath,前者用于传输rpc消息,后者是一个简单的rpc调用统计页面

rpc.DialHTTP

客户端建立连接时指定路径,DialHTTPPath(network, address, DefaultRPCPath)