HTTP RPC
最后更新于:2022-04-02 04:56:19
Call方法会等待远端调用完成,而Go方法异步的发送调用请求并使用返回的Call结构体类型的Done通道字段传递完成信号。
Call方法同步
Go方法异步
HTTP RPC
http的服务端代码实现如下:
~~~
package main
import (
"errors"
"fmt"
"net/http"
"net/rpc"
)
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 main() {
// 服务端会调用(用于HTTP服务)
arith := new(Arith)
/*
Register在server注册并公布rcvr的方法集中满足如下要求的方法:
- 方法是导出的
- 方法有两个参数,都是导出类型或内建类型
- 方法的第二个参数是指针
- 方法只有一个error接口类型的返回值
如果rcvr不是一个导出类型的值,或者该类型没有满足要求的方法,Register会返回错误。Register也会使用log包将错误写入日志。客户端可以使用格式为"Type.Method"的字符串访问这些方法,其中Type是rcvr的具体类型。
*/
rpc.Register(arith)
/*
HandleHTTP函数注册DefaultServer的RPC信息HTTP处理器对应到DefaultRPCPath,和DefaultServer的debug处理器对应到DefaultDebugPath。HandleHTTP函数会注册到http.DefaultServeMux。之后,仍需要调用http.Serve(),一般会另开线程:"go http.Serve(l, nil)"
*/
rpc.HandleHTTP()
/*
ListenAndServe监听srv.Addr指定的TCP地址,并且会调用Serve方法接收到的连接。如果srv.Addr为空字符串,会使用":http"。
ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。Handle和HandleFunc函数可以向DefaultServeMux添加处理器。
*/
err := http.ListenAndServe(":1234", nil)
if err != nil {
fmt.Println(err.Error())
}
}
~~~
通过上面的例子可以看到,我们注册了一个Arith的RPC服务,然后通过rpc.HandleHTTP函数把该服务注册到了HTTP协议上,然后我们就可以利用http的方式来传递数据了。
请看下面的客户端代码:
~~~
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main() {
// if len(os.Args) != 2 {
// fmt.Println("Usage: ", os.Args[0], "server")
// os.Exit(1)
// }
// serverAddress := os.Args[1]
/*
DialHTTP在指定的网络和地址与在默认HTTP RPC路径监听的HTTP RPC服务端连接。
*/
client, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")
if err != nil {
/*
Fatal等价于{log.Print(v...); os.Exit(1)}
Print调用log.Output将生成的格式化字符串输出到logger,参数用和fmt.Print相同的方法处理。
Output写入输出一次日志事件。参数s包含在Logger根据选项生成的前缀之后要打印的文本。如果s末尾没有换行会添加换行符。calldepth用于恢复PC,出于一般性而提供,但目前在所有预定义的路径上它的值都为2。
Exit让当前程序以给出的状态码code退出。一般来说,状态码0表示成功,非0表示出错。程序会立刻终止,defer的函数不会被执行。
*/
log.Fatal("dialing:", err)
}
// Synchronous call
args := Args{17, 8}
var reply int
/*
Call方法会等待远端调用完成,而Go方法异步的发送调用请求并使用返回的Call结构体类型的Done通道字段传递完成信号。
Call调用指定的方法,等待调用返回,将结果写入reply,然后返回执行的错误状态。
*/
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)
var quot Quotient
err = client.Call("Arith.Divide", args, ")
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)
}
~~~
我们把上面的服务端和客户端的代码分别编译,然后先把服务端开启,然后开启客户端,输入代码,就会输出如下信息:
~~~
Arith: 17*8=136
Arith: 17/8=2 remainder 1
~~~
通过上面的调用可以看到参数和返回值是我们定义的struct类型,在服务端我们把它们当做调用函数的参数的类型,在客户端作为client.Call的第2,3两个参数的类型。客户端最重要的就是这个Call函数,它有3个参数,第1个要调用的函数的名字,第2个是要传递的参数,第3个要返回的参数(注意是指针类型),通过上面的代码例子我们可以发现,使用Go的RPC实现相当的简单,方便。
';