Go RPC简介及简单案例(一)

298 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

进程间通信(IPC,Inter-Process Communication)

进程是计算机系统分配资源的最小单位。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进行协调工作,才有了进程间通信。进程间通信技术包括消息传递、同步、共享内存和远程过程调用。IPC分为LPC和RPC

LPC和RPC

本地过程调用(LPC)LPC用在多任务操作系统中,使得同时运行的任务能互相会话。这些任务共享内存空间使任务同步和互相发送信息。

远程过程调用(RPC)类似于LPC,只是在网上工作。同一主机中由于有函数栈的存在,调用函数时直接将现场压栈,将函数参数压栈,然后PC跳到目的地址执行函数,执行完后PC根据栈中的数据调回到原来的地方。但是进程分布在不同主机时,就不能这样做了,需要用到RPC。

RPC
在分布式计算, **远程过程调用**(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信
协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的
子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC 是一
种服务器-客户端( Client/Server )模式,经典实现是一个通过 **发送请求-接受回应** 进行信息交
互的系统。  

来源:wiki 维基百科
RPC的组成

一个完整的RPC框架,无论是genrpc、grpc还是net/rpc,都需要有这四种组件:

  1. client端:服务的调用方,一般通过client.Call函数远程调用
  2. server端:服务的提供者
  3. client stub:客户端存根,它会存放服务端的地址,client发送请求时会将请求参数和服务器地址打包成网络信息,然后发给server。
  4. server stub:服务端存根,接受client发来的消息,解包,根据参数调用server本地的方法,会将结果返回到client。
简单的Go RPC服务(调用远程向量加法器)

任务要求:客户端传入向量参数,发送给服务端,服务端计算完后返回给客户端,客户端显示结果

文件目录如下:

-RPC Calculate
        -client
            -client.go
                -go.mod
        -server
            -common_server
                    -common.go
            -server.go
            -go.mod

注意首先创建目录,然后分别在server和client文件夹下调用命令行

go mod init server   //server文件夹下调用
go mod init client   //client文件夹下调用

运行上面两行命令后会各自生成go.mod文件,这是一个包管理文件。

common.go

package common_server

import "errors"

type Args struct {
	A, B []int32
}

type Result struct {
	C []int32
}

type VectorServer struct{}

func (t *VectorServer) Add(args *Args, reply *Result) error {
	if len(args.A) != len(args.B) {
		return errors.New("Vector lengths do not match")
	}

	reply.C = make([]int32, len(args.A))
	for i := 0; i < len(args.A); i++ {
		reply.C[i] = args.A[i] + args.B[i]
	}
	return nil
}

server.go

package main

import (
	"Server/common_server"
	"fmt"
	"net/http"
	"net/rpc"
)

func main() {
	var ms = new(common_server.VectorServer) // create a new VectorServer
	var err = rpc.Register(ms)

	if err != nil {
		fmt.Println("rpc.Register failed:", err)
		return
	}

	rpc.HandleHTTP()

	// start listening for incoming connections
	fmt.Println("Listening on port 9090...")
	err = http.ListenAndServe(":9090", nil)

	if err != nil {
		fmt.Println("ListenAndServe failed:", err)
	}

	fmt.Println("Server stopped....")

}

client.go

package main

import (
	"fmt"
	"log"
	"net/rpc"
)

type Args struct {
	A, B []int32
}

type Result struct {
	C []int32
}

func main() {
	args := Args{A: []int32{1, 2, 3}, B: []int32{4, 5, 6}}
	reply := Result{}

	client, err := rpc.DialHTTP("tcp", "localhost:9090")
	if err != nil {
		log.Fatal("Dialing:", err)
	}

	err = client.Call("VectorServer.Add", &args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}

	fmt.Printf("Result: %v", reply.C)

}

在server和client下分别开一个终端 注意先运行server再运行client

  1. server下
go run .
  1. client下
go run .

server端显示:

Listening on port 9090...

client端显示:

Result: [5 7 9]