rpc的正确打开方式|读懂Go原生net/rpc包
前言
最近在阅读字节跳动开源RPC框架Kitex的源码,分析了如何借助命令行,由一个IDL文件,生成client和server的脚手架代码,也分析了Kitex的日志组件klog。当然Kitex还有许多其他组件:服务注册、发现、负载均衡、熔断、限流等等,后续我也会继续分析。
我希望借助这篇文章,用尽可能少的语言,配合分析Go原生net/rpc包的部分核心代码,帮助你贯通RPC的知识,梳理RPC的运作流程,让你对RPC有一个比较全面的认识。
以此为基础,将有助于你在阅读其他开源RPC框架源码时,对比发掘开源RPC框架具体做了哪些提高。
RPC的流程
远程过程调用 (Remote Procedure Call,RPC) 是一种计算机通信协议。允许运行在一台计算机的程序调用另一个地址空间的子程序(一般是开放网络中的一台计算机),而程序员就像调用调用本地程序一样,无需额外做交互编程。
假设你要调用一个Add(a int, b int) int方法,实现求和功能,但是这个方法部署在另一台机器上,该如何调用?
这就是一次RPC的流程,甚至和HTTP请求/响应流程很像,眼下我先侧重于介绍RPC的概念,以后会介绍其与HTTP的区别。
并且这里暂时没有涉及所谓的服务注册、发现、负载均衡、熔断、限流等字眼,这些都是一个成熟的RPC框架应该具备的功能组件,用于确保一个RPC框架的高可用,但是却不是一个RPC框架所必需的。
RPC协议本质上定义了一种通信的流程,而具体的实现技术是没有约束的,每一种RPC框架都有自己的实现方式,比如你可以规定自己的RPC请求/响应包含消息头和消息体,使用gob/json/pb/thrift来序列化/反序列化消息内容,使用socket/http2进行网络通信,只要client和server消息的发送和解析能对应即可。希望读者仔细体会——“约定”这个概念,这将贯穿始终。
分析net/rpc
先讲解一下流程图中的序列化和网络传输部分,这是RPC的核心。
消息编码/解码(序列化)
上面的RPC通信流程图,其中很重要的一环就是消息的编解码,消息只有序列化之后,才能高效地参与网络传输。通过实现上图net/rpc包定义的接口,可以指定使用的编解码方式,比如net/rpc包默认使用了gob二进制编码:
服务端负责序列化的结构gobServerCodec的实现了ServerCodec接口,服务端需要编解码消息的地方,都会调用gobServerCodec的对应方法(客户端也是类似的实现,也是一样使用gob编解码)。
消息的网络传输
消息序列化之后,是需要用于网络传输的,涉及到客户端与服务端的通信方式。
这是服务端的接受链接的逻辑,和大部分网络应用相同,server监听了一个ip:port
,然后accept一个连接之后,会开启一个go协程处理请求与响应。
这是客户端发起请求的方式,也印证了socket网络编程的通信模型。
理解了RPC的各个流程之后,就能梳理清楚RPC框架的各种组件是作用在哪个层面的,例如Kitex的网络库netpoll,虽然我未曾看过其源码实现,但是有理由猜测其是在网络通信/传输部分做了提高。
Server端的设计
这是service的结构,可以看到一个服务通过Map可以绑定多个名称的方法,提供调用,且对应service需要提前注册到服务端,这样在客户端请求达到时才能准确调用。
服务注册主要参数是serviceName和service实体。
reflect.xxx():主要的工作就是通过反射的机制,解析所绑定的服务的名称、类型等。
suitableMethods():解析一个service绑定的所有method。
serviceMap.LoadOrStore():将service注册到服务端server的Map,如下是Server的结构:
Client端的设计
这是Client的结构:
codec:编解码的具体实现。seq:RPC的序列号,每发起一个就计数增加,加入Map,且完成或失败后从Map中移除。pending:配合seq工作的Map。
这是客户端具体发起一次RPC请求的过程,当然一次具体的RPC请求可以是同步的,也可以是异步的:
client.Go()是异步的。client.Call()是同步的,且其内部就是调用了client.Go(),但是因为其调用之后,在调用完成之前,会被阻塞在chan上,因此后续的RPC请求必须等待发送。
小结
到此为止我们粗浅的分析了net/rpc的一些核心源码,借此梳理了RPC的工作流程,主要包括:
- RPC的编解码(序列化)协议选择
- RPC的网络通信/传输模型(Socket编程)
- RPC的请求发起/响应接受(同步/异步)
RPC的功能组件
一个成熟的RPC框架只实现基本的通信功能是不够的,否则它将十分的脆弱,没有任何应对服务宕机的能力,在高并发场景下也难堪重任,因此需要增加很多的功能组件来提高服务的可靠性:
- 超时控制|请求重试|负载均衡|熔断器|限流器|日志|监控|链路追踪|...
(Go原生net/rpc包也有很多提高可靠性的设计,本文没有过多展开)
结束语
这篇文章,我借助Go原生net/rpc包的部分核心源码,梳理了RPC的工作流程,试图帮助你建立RPC的全局观念,希望你明白,RPC框架是对RPC通信流程的具体实现,每一个框架为提高自身的可靠性,又延伸出了多种功能组件。
后续的文章我也将继续分析字节跳动开源RPC框架Kitex的核心组件源码,共勉。
关注公众号【程序员白泽】,我会同步分享博客文章。
rpc的正确打开方式|读懂Go原生net/rpc包的更多相关文章
- C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别
C#语法——泛型的多种应用 本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...
- C++11随机数的正确打开方式
C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...
- iOS开发小技巧--相机相册的正确打开方式
iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ...
- Xcode 的正确打开方式——Debugging(转载)
Xcode 的正确打开方式——Debugging 程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能 ...
- InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式
InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式 https://mp.weixin.qq.com/s/HGa_90XvC22anabiBF8AbQ 在这篇文章里,我将讨论在MySQL 5 ...
- Console控制台的正确打开方式
Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象 |-- assert() 如果第一个参数断言为false,则在控制台输出错误信息 ...
- 任务队列和异步接口的正确打开方式(.NET Core版本)
任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processi ...
- (一)Redis for Windows正确打开方式
目录 (一)Redis for Windows正确打开方式 (二)Redis for 阿里云公网连接 (三)Redis for StackExchange.Redis 下载地址 官网.中文网1 及 中 ...
- List的remove()方法的三种正确打开方式
转: java编程:List的remove()方法的三种正确打开方式! 2018年08月12日 16:26:13 Aries9986 阅读数 2728更多 分类专栏: leetcode刷题 版权声 ...
随机推荐
- EMS导入导出邮箱
Exchange支持EMS命令导出用户邮箱内容作为备份的功能.当重要用户的邮件误删除后,可以通过导出的邮箱恢复数据. 1.授权管理用户 Exchange默认安装完成后,内置"Mailbox ...
- 微信小程序下拉加载和上拉刷新两种实现方法
方法一:onPullDownRefresh和onReachBottom方法实现小程序下拉加载和上拉刷新 首先要在json文件里设置window属性 设置js里onPullDownRefresh和onR ...
- 32位x86处理器架构
我们看看32 位 x86 处理器的基本架构特点.这些处理器包括了 Intel IA-32 系列中的成员和所有 32 位 AMD 处理器. 操作模式 x86 处理器有三个主要的操作模式:保护模式.实地址 ...
- MySQL存储引擎、基础数据类型、约束条件
MySQL存储引擎 存储引擎 # 存储引擎可以堪称是处理数据的不同方式 # 查看存储引擎的方式 show engines; # 需要掌握的四个存储引擎 MyISAM MySQL5.5之前的默认的存储引 ...
- mount挂载命令
了解命令mount,这是一个挂载命令,挂载的意思是指的就是将设备文件中的顶级目录连接到 Linux 根目录下的某一目录(最好是空目录),访问此目录就等同于访问设备文件.当在 Linux 系统中使用硬件 ...
- 中文版Postman
作为软件开发从业者,API 调试是必不可少的一项技能,在这方面 Postman 做的非常出色.但是在整个软件开发过程中,API 调试只是其中的一部分,还有很多事情 Postman 无法完成,或者无法高 ...
- Selenium3自动化测试【27】Frame的操作
本篇文章内容摘要 " 讲解Python3+Selenium3如何处理Frame窗体" 同步视频知识与系列知识内容,可关注:[公众号]:柒哥测试:[WX]:Lee-890;[视频号] ...
- 1903021116-吉琛-Java第四周作业-程序编写
项目 内容 课程班级博客链接 19级信计班 这个作业要求链接 Java分支语句学习 https://edu.cnblogs.com/campus/pexy/19xj/homework/12563 我的 ...
- 从源码学习UEToll
从源码学习UEToll 捕获控件梳理 相对位置功能梳理 网格栅栏功能梳理 捕获代码分析 TransparentActivity public @interface Type { int TYPE_UN ...
- android软件简约记账app开发day02-收入支出明细页面绘制
android软件简约记账app开发day02-收入支出明细页面绘制 效果图 列表界面绘制 新建layout文件-item_mainlv.xml大体使用绝对布局,嵌套相对布局,嵌套文本内容实现 < ...