我们知道Socket和HTTP采用的是类似"信息交换"模式,即客户端发送一条信息到服务端,然后(一般来说)服务器端都会返回一定的信息以表示响应。客户端和服务端之间约定了交互信息的格式,以便双方都能够解析交互所产生的信息。但是很多独立的应用并没有采用这种模式,而是采用类似常规的函数调用的方式来完成想要的功能。
RPC就是想实现函数调用模式的网络化。客户端就像调用本地函数一样,然后客户端把这些参数打包之后通过网络传递到服务端,服务端解包到处理过程中执行,然后执行的结果反馈给客户端。
RPC(Remote Procedure Call Protocol)——远程过程调用协议,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。它假定某些传输协议的存在,如TCP或UDP,以便为通信程序之间携带信息数据。通过它可以使函数调用模式网络化。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
API、进程间通信。主要用于分布式应用间通信
本质区别就是REST是使用http协议,相比RPC的实现协议传输会传更多的内容,但是两个可以做相同的事情。
运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
Go标准包中已经提供了对RPC的支持,而且支持三个级别的RPC:TCP、HTTP、JSONRPC。但Go的RPC包是独一无二的RPC,它和传统的RPC系统不同,它只支持Go开发的服务器与客户端之间的交互,因为在内部,它们采用了Gob来编码。
Go RPC的函数只有符合下面的条件才能被远程访问,不然会被忽略,详细的要求如下:
举个例子,正确的RPC函数格式如下:
func (t *T) MethodName(argType T1, replyType *T2) error
T、T1和T2类型必须能被encoding/gob包编解码。
任何的RPC都需要通过网络来传递数据,Go RPC可以利用HTTP和TCP来传递数据,利用HTTP的好处是可以直接复用net/http里面的一些函数。详细的例子请看下面的实现
package main
import (
"log"
"net"
"net/rpc"
)
type HelloServer struct {}
func (p *HelloServer) Hello(request string, reply *string) error {
*reply = "Hello " + request
return nil
}
func main(){
// 将HelloServer注册为一个RCP服务
rpc.RegisterName("HelloServer", new(HelloServer))
listener, err := net.Listen("tcp", ":1234")
if (err != nil){
log.Fatal("ListenTCP error:" , err)
}
conn, err := listener.Accept()
if (err != nil){
log.Fatal("Accept error:" , err)
}
rpc.ServeConn(conn)
}
package main
import (
"fmt"
"log"
"net/rpc"
)
func main(){
client, err := rpc.Dial("tcp", ":1234")
if (err != nil){
log.Fatal("Dialing error:" , err)
}
var reply string
err = client.Call("HelloServer.Hello", "hello", &reply)
if (err != nil){
log.Fatal("Calling error:" , err)
}
fmt.Println(reply)
}
package api
import "net/rpc"
const HelloServiceName = "path/to/pck.HelloServer"
type HelloServiceInterface = interface {
Hello(request string, reply *string) error
}
/// 注册
func RegisterHelloServer(svc HelloServiceInterface) error {
return rpc.RegisterName(HelloServiceName, svc)
}
type HelloServiceClient struct {
*rpc.Client
}
var _ HelloServiceInterface = (*HelloServiceClient)(nil) // HelloServiceClient 必须实现HelloServiceInterface方法
func (p *HelloServiceClient) Hello(request string, reply *string) error {
return p.Client.Call(HelloServiceName+".Hello", request, reply)
}
func DialHelloService(netWork, address string)(*HelloServiceClient, error) {
c, err := rpc.Dial(netWork, address)
if(err != nil){
return nil, err
}
return &HelloServiceClient{Client:c}, nil
}
package main
import (
"fmt"
"log"
"test/api"
)
func main(){
// 拨号
client, err := api.DialHelloService("tcp", ":1234")
if (err != nil){
log.Fatal("Dialing error:" , err)
}
var reply string
err = client.Hello("hello", &reply)
if (err != nil){
log.Fatal("Calling error:" , err)
}
fmt.Println(reply)
}
package main
import (
"log"
"net"
"net/rpc"
"test/api"
)
type HelloServer struct {}
func (p *HelloServer) Hello(request string, reply *string) error {
*reply = "Hello " + request
return nil
}
func main(){
// 将HelloServer注册为一个RCP服务
api.RegisterHelloServer(new(HelloServer))
listener, err := net.Listen("tcp", ":1234")
if (err != nil){
log.Fatal("ListenTCP error:" , err)
}
for{
conn, err := listener.Accept()
if (err != nil){
log.Fatal("Accept error:" , err)
}
go rpc.ServeConn(conn)
}
}
package main
import (
"errors"
"fmt"
"log"
"net/http"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
// Arith.Multiply
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
fmt.Println(reply,"乘法执行了")
return nil
}
//Arith.Divide
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
fmt.Println(quo,"除法执行了")
return nil
}
func main(){
rpc.Register(new(Arith)) //注册一个Arith的RPC服
rpc.HandleHTTP() // 把该服务注册到了HTTP协议, 然后我们就可以利用http的方式来传递数据了
/*err := http.ListenAndServe(":1234", nil)
if err != nil {
log.Fatal(err.Error())
}
*/
lister,err:=net.Listen("tcp","127.0.0.1:1234")
if err != nil {
log.Fatal(err.Error())
}
http.Serve(lister,nil)
}
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main(){
client,err:=rpc.DialHTTP("tcp","127.0.0.1:1234")
if err!=nil{
log.Fatal("dialing:", err)
}
args := Args{17, 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)
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)
}
package main
import (
"errors"
"fmt"
"net"
"net/rpc"
"os"
)
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() {
arith := new(Arith)
rpc.Register(arith)
tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go rpc.ServeConn(conn)
}
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
package main
import (
"fmt"
"log"
"net/rpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main(){
//client,err:=rpc.DialHTTP("tcp","127.0.0.1:1234") --- http fangshi
// client, err := jsonrpc.Dial("tcp", service) ---- JSON RPC
client, err := rpc.Dial("tcp", "127.0.0.1:1234") //tcp方式
if err!=nil{
log.Fatal("dialing:", err)
}
args := Args{17, 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)
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)
}
package main
import (
"errors"
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"os"
)
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() {
arith := new(Arith)
rpc.Register(arith)
tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go jsonrpc.ServeConn(conn)
}
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
func main(){
//client,err:=rpc.DialHTTP("tcp","127.0.0.1:1234") --- http fangshi
client, err := jsonrpc.Dial("tcp", "127.0.0.1:1234") // ---- JSON RPC
//client, err := rpc.Dial("tcp", "127.0.0.1:1234") //tcp方式
if err!=nil{
log.Fatal("dialing:", err)
}
args := Args{17, 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)
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)
}