RPC(Remote Procedure Call) 是远程过程调用的简称,是分布式系统中不同节点间流行的通信方式。在互联网时代,RPC 已经和 IPC 一样成为一个不可或缺的基础构件。因此 Go 语言的标准库也提供了一个简单的 RPC 实现:net/rpc 包。
RPC 的目的是成为网络系统的函数调用模式。客户端执行 RPC 就像调用原生函数一样,只是它们将函数参数打包并通过网络发送到服务器。然后服务器可以解包这些参数并处理请求,将结果执行回客户端。
在计算机科学中,远程过程调用 (RPC) 是一种进程间通信,它允许计算机程序使子例程或过程在另一个地址空间(通常在共享网络上的另一台计算机上)执行,而无需程序员明确编码此远程交互的详细信息。也就是说,无论子程序是执行程序的本地程序还是远程程序,程序员都编写基本相同的代码。当所讨论的软件使用面向对象的原则时,RPC 被称为远程调用或远程方法调用。
RPC 版 Hello World
由于 net/rpc
为我们提供了让一段代码通过网络与另一段代码通信的方法,一个自然的例子就是客户端-服务器架构中的“Hello World”。
代码结构:
.
├── Makefile # instructions for building and running the
| # example
|
├── client # RPC client
│ └── client.go # provides the interface which executes the
| # command remotely.
|
├── core # Shared functionality.
│ └── core.go # - implements the actual handler that gets
| # executed in the server;
| # - provides the go representation of the
| # messages exchanged between client and server.
|
├── main.go # CLI
| #
└── server # RPC server - provides the interface which
| # allows a client to communicate with it
└── server.go # over the wire.
server.go
HelloWorld 方法需要满足 GO 语言 RPC 规则,接收两个参数,第二个参数是指针类型,返回一个 Error 类型,必须是公共方法。HelloWorldService 类型对象注册到 RPC 服务 RPC.REGISTER 函数调用 将对象类型中的对象方法注册到 RPC 规则中作为 RPC 函数,所有注册的方法都放在 HelloWorldService 服务空间下建立 TCP 链接,通过 rpc.serverConn 函数在该链接上提供 RPC 服务:
package main
import (
"net"
"net/rpc"
)
type HelloWorldService struct {
}
func (s *HelloWorldService) HelloWorld(request string, response *string) error {
*response = "hello" + request
return nil
}
func main() {
_ = rpc.RegisterName("HelloWorldService", &HelloWorldService{})
listener, err := net.Listen("tcp", ":8000")
if err != nil {
panic("Monitor port failed!")
}
conn, err := listener.Accept()
if err != nil {
panic("Establish a connection failure!")
}
rpc.ServeConn(conn)
}
client.go
通过 rpc.dial 的 RPC 拨号服务通过 client.call 调用具体方法方法中的第一个参数是 RPC 服务名和方法名,第二个和第三个参数都包含在方法中。
package main
import (
"fmt"
"log"
"net/rpc"
)
func main() {
client, err := rpc.Dial("tcp", "127.0.0.1:8000")
if err != nil {
log.Fatal("dial", err)
}
var response string
err = client.Call("HelloWorldService.HelloWorld", "world", &response)
if err != nil {
log.Fatal("caller", err)
}
fmt.Println(response)
}