RPC简介与实践
本文是RPC的简介与实践,首先介绍一下RPC的概念与原理,接着介绍一下Go语言对RPC的支持,最后提供一个Go语言原生RPC的简单demo。
一、RPC是什么
RPC,全称:Remote Procedure Call,中文翻译:远程过程调用。
RPC是一种技术思想,而非规范或协议,它指的是,本地计算机程序通过网络向远程计算机程序请求服务,隐藏底层网络技术,使其看起来就像调用本地函数一样简便,服务提供方与调用方都无需了解底层网络的传输细节。
二、RPC是怎么工作的
1. RPC框架的组成
(1)客户端(Client):服务调用者。
(2)服务端(Server):服务提供者。
(3)客户端存根(Client Stub):存放服务端的地址信息,负责将客户端的请求参数打包成网络消息,通过网络远程发送给服务方。
(4)服务端存根(Server Stub):接收调用方发送过来的消息,将消息解包,并调用本地的方法。
(5)底层网络传输:可以是TCP或HTTP。
2. 一次完整的RPC调用流程
简单图示:
详细介绍:
(1)客户端通过本地调用的方式调用服务。
(2)客户端存根接收到调用请求后负责将调用的方法、参数等信息序列化成网络消息体,找到远程服务的地址,将消息通过网络发送给服务方。
(3)服务端存根接收到消息后进行解码(反序列化),根据解码结果调用本地服务。
(4)服务端调用本地方法进行业务逻辑处理,然后将处理结果返回给服务端存根。
(5)服务端存根将调用结果序列化,然后通过网络发送给调用方。
(6)客户端存根收到消息,将消息进行解码(方序列化),得到调用结果,将结果发送给客户端。
(7)客户端最终得到服务调用结果。
三、Go语言对RPC的支持
1. Go内置RPC
Go标准包“net/rpc”中已经提供了对RPC的支持,而且支持三个级别的RPC:TCP、HTTP、JSONRPC。但Go的RPC只支持Go开发的服务器与客户端之间的交互,无法跨语言调用,因为在内部,它们采用了Gob编码。
2. 什么是Gob编码
Gob是Go语言内置的编解码方式,可以支持变长类型的编解码(意味着通用)而且它的效率比json,xml更高。
3. Go的RPC函数怎么写
Go RPC函数只有符合下面的条件才能被远程访问,否则会被忽略:
(1)函数必须是导出的(函数名称首字母大写)。
(2)必须有两个导出类型的参数,第一个是接收的参数,第二个是返回给客户端的参数,第二个参数必须是指针类型的。
(3)必须有一个error类型的返回值。
4. 一个简单的demo
这里提供一个基于TCP的RPC简单demo,逻辑很简单,就是求两个整型数值的乘机与商余,代码如下。
(1)服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | /** * rpc demo tcp server - 求两数的乘积与商余 * author: JetWu * date: 2020.04.21 */ package main import ( "errors" "fmt" "log" "net" "net/rpc" "time" ) //接收参数 type Args struct { A, B int } //返回参数 type Quotient struct { Quo, Rem int } //RPC服务函数 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() { //RPC服务注册 arith := new(Arith) rpc.Register(arith) tcpAddr, err := net.ResolveTCPAddr( "tcp" , ":1234" ) if err != nil { log.Fatal( "net.ResolveTCPAddr err: " , err) } //监听网络 listener, err := net.ListenTCP( "tcp" , tcpAddr) if err != nil { log.Fatal( "net.ListenTCP err: " , err) } defer listener.Close() log.Println( "rpc demo tcp server started ..." ) for { fmt.Printf( "accepted at %v\n" , time.Now()) //等待客户端连接 conn, err := listener.Accept() if err != nil { fmt.Println( "listener.Accept err: " , err) continue } fmt.Printf( "getted at %v\n" , time.Now()) //为客户端提供服务 go rpc.ServeConn(conn) } } |
(2)客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /** * rpc demo tcp client - 求两数的乘积与商余 * author: JetWu * date: 2020.04.21 */ package main import ( "fmt" "log" "net/rpc" ) //接收参数 type Args struct { A, B int } //返回参数 type Quotient struct { Quo, Rem int } func main() { //连接服务端 client, err := rpc.Dial( "tcp" , ":1234" ) if err != nil { log.Fatal( "rpc.Dial error: " , err) } log.Println( "successfully connected to the rpc demo tcp server ..." ) //RPC服务调用 args := Args{17, 8} var reply int err = client.Call( "Arith.Multiply" , args, &reply) if err != nil { log.Fatal( "Arith.Multiply error: " , err) } fmt.Printf( "Arith.Multiply: %d * %d = %d\n" , args.A, args.B, reply) var quo Quotient err = client.Call( "Arith.Divide" , args, &quo) if err != nil { log.Fatal( "Arith.Divide error: " , err) } fmt.Printf( "Arith.Divide: %d / %d = %d remainder %d\n" , args.A, args.B, quo.Quo, quo.Rem) } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)