作者

渡码,阿里巴巴码农,公众号:渡码 作者,专注大数据开发、数据分析和Python技术。

关注公众号 渡码 回复关键字 manis,可获取电子书、各章节和完整源代码,并且可加入读者群一起交流问题。

简介

19年上半年,我阅读了Hadoop RPC模块的源代码,读完后发现这个模块设计的非常好,与其他模块无耦合,完全可以独立出来当成一个独立的框架。为了总结学到的编程知识,同时也为了学习Apache顶级开源项目的代码是如何编写的,我便把它做成了电子书,共350页,从写代码到做成电子书共花了6个月的时间。本来想做成付费专栏赚点小钱,并且已经到了上架阶段了,但后来决定把它免费开放出来,让更多的人能够学习到优秀的实战项目。

当然我们这本书并不是源码分析类教程,而是强调动手能力。在这里我会带着大家按照 Hadoop RPC 源码从 0 到 1 完整敲一遍,代码量在 4600 行左右。为了让不熟悉 Hadoop 或 RPC 的朋友也能够学习,我将 Hadoop RPC 稍微做了一点改造,赋予了新的业务含义,也有自己的名字,叫 Manis。Mnias 源码相比于 Hadoop RPC源码还原度为90%。为什么不是100%呢?一方面为了突出重点,我会把不太重要、不是很核心的技术舍弃掉。另一方面为了符合新的业务定义,我会做一些改进,而不是照搬完全 Hadoop RPC。

虽然这个项目是实现 RPC 功能,但我觉得我们关注的重点不应该过多地放在 RPC 本身,而应该重点学习编写 RPC 过程中所涉及的系统设计、面向对象设计思想和原则、工程/代码规范、客户端开发、服务端开发、网络编程、多线程、并发编程、设计模式、异常处理等核心知识,可以说是麻雀虽小五脏俱全。尤其是对于刚学习 Java 还没有接触线上实战项目的朋友,这是一次很好的练兵机会。

学习开源项目的一个优势在于它是经过线上检验的,Hadoop集群规模最大达到上万台服务端,足以证明它的 RPC 模块是优秀的。另外一个好处是可以积累顶级开源项目的开发经验,大到架构设计,小到设计模式、代码规范,说不定日后就能为开源社区贡献代码了。所以,学会了 Manis 后,不但有编写实战项目的经验,同时也有能力阅读 Hadoop RPC 的源码,这也算是面试的加分项。

涉及到的核心技术

下面我们来介绍一下 Manis 中涉及的核心技术点。作为一个 RPC 框架,最关键的几个模块是客户端、网络模块和服务端

客户端

作为客户端来说,它的职责非常明确,以数据库客户端为例,它的职责就是向用户提供增删改查接口,并将相应的请求发送给服务端,接收结果并返回给用户。由于客户端职责边界是非常明确的,所以我们从设计上就要将其与网络模块、与服务端解耦,解耦的方式就要用到设计模式中的代理模式。也就说客户端只需要定义好它需要提供的功能(接口)并提供给用户,具体如何实现就交给代理,至于代理是通过网络发送给服务端还是通过其他什么方式客户端就不需要关心了,客户端只关心调用代理并拿结果。这样做的好处是客户端与其他模块解耦,提高了系统扩展性。当然,代理模式还有个容易被忽略的好处是它天然地适合用在 RPC 场景。

Manis 中支持多种序列化/反序列化方式,每种序列化方式对应一个类,它们都继承共同的基类。我们在设计时需要做到不同序列化方式之间的代码是解耦的,且序列化/反序列化模块与客户端模块、与网络模块是解耦的,这样才能做到任意地增加新的新的序列化方式以及删除老的序列化方式。为了实现客户端与序列化/反序列化模块的松耦合,我们需要用到一些设计模式,比如,用适配器模式将客户端定义的请求接口适配到不同序列化协议定义的请求接口。这样做几乎不需要修改现有的代码,符合面向对象的开闭原则

网络模块

下面再来说说网络模块。

由于客户端的请求可能来自不同的序列化协议,但的目的是相同的,都是为了通过网络模块的服务端,可以说是殊途同归。这样的话,我们就有必要在网络这一层定义一个统一的协议(接口),让不同序列化方式都遵循相同的协议(接口),那么网络模块就可以对它们“一视同仁”,编写一套代码就可以了。就好比,不管你用U盘还是硬盘,只要是 USB 接口,那都能插到电脑的同一个接口进行相同的读写逻辑。对于服务端的返回值也是采用同样的处理逻辑。

网络模块必不可少的功能就是发送网络请求,当然除了这个还有一个更核心的功能是管理网络资源。听起来有点抽象,如果用面向对象的思想来理解,其实就是创建一个类代表网络连接,比如就叫Connection类,每次创建一个网络连接其实就是创建一个Connection对象。当然,我们知道网络资源比较宝贵且创建成本较高,当系统客户端请求量非常大的时候,我们不可能为每次请求都创建一个网络连接,所以,需要建立一个网络连接池,以达到复用网络资源的目的。我们可以再定义一个类ConnectionId,每个ConnectionId对象都唯一代表Connection对象,ConnectionId的属性包含服务端地址请求网络的一些参数,所以我们可以认为客户端请求服务端的地址和参数相同的话,就可以复用同一个网络连接。当然,这里还有一个很关键的问题不容忽视,网络连接池是公共资源,为了保证线程安全,在对资源池读写时需要加锁,也是从这里开始本书加大了对并发编程的相关讲解。刚刚介绍的这部分在 Manis 中是自主实现的。

建立网络连接的过程中还会涉及发送请求头、请求上下文,装饰网络输入、输出流等功能,这些比较偏业务,这里就不再赘述了。

发送网络请求时,为了将业务代码与发送请求代码剥离,在 Manis 创建了一个建线程池,将发送发送请求的代码封装成线程,丢到线程池中等待执行。所以,这里又涉及到三部分知识

  • 使用工厂模式创建线程池,并用单例模式保证不被重复创建
  • 使用Java的ExecutorFuture,用来创建任务并等待返回
  • 对线程池的读写保证线程安全

最后,网络模块要实现的是等待服务端返回的结果。由于网络模块同一时间会接收大量客户端网络请求,所以,我们可以创建一个单独的线程,每隔一定时间轮询是否有服务端的返回。

服务端

对于服务端来说,我们最关心的是性能问题。因为大量的客户端请求最终都会汇总到服务端一个节点来处理。所以最原始的单线程+while循环的方式肯定满足不了性能要求。所以比较最容易想到的改进点是多线程,虽然在一定程度上能解决第一种方式带来的问题,但这种方式也有很大的缺点:频繁创建线程成本比较大,并且线程之间的切换也需要一定的开销,当线程数过多时显然会降低服务端的性能。目前比较常用的解决方案是Reactor模式Reactor模式也分为单线程Reactor、多线程Reactor和多Reactor。这几种的区别在书里都有具体说明,这里我就不再介绍了。Reactor模式的优势按照我自己的理解就四个字——各司其职。Manis 中使用的是多Reactor模式,设计图如下:

简单介绍一下图中几个线程的功能

  • Listener: 接收客户端的连接请求,也可以叫做 Acceptor,封装连接请求
  • Readr: 多线程并行地读取客户端请求,进行反序列化和解析操作
  • Handler: 多线程并行地读取调用请求,解析调用方法并执行调用
  • Responder: 读取响应结果,发送给客户端

够各司其职吧。那它们之间怎么联系呢?从图上可以看到是消息队列,消息队列可以很好地实现组件间的解耦。

虽然服务端的职责也比较明确、清晰,但涉及的内容一点不少,包括注册不同的序列化方式,解析并调用相应的请求。最关键的是服务端线程是最多的,并且需要线程之间需要高度协调的,所以对并发编程的要求也更高,这块书中也有重点讲解。

最后我们看看Manis中核心组件的时序图


avatar

由于 Manis 在设计上是足够优秀的,所以开发的时候这三个模块可以并行进行。有点像近几年web开发比较火的前后端分离架构,只要各个模块把协议定义好了后,开发就可以并行进行而不需要依赖彼此。至此,Manis 的核心技术就介绍完了,当然这只是冰山一角,毕竟 4600 行代码。

如何获得本书

关注公众号 渡码 回复关键字 manis,可获取电子书+源码+读者交流群。

目录

代码结构

本书特色

在讲解相册内容同时,大部分章节都加入了课外拓展,针对每一节涉及的基础知识,如:设计模式、序列化/反序列化基础、单例测试、源码分析、并发编程以及Hadoop源码分析等内容都有拓展讲解。力求让零基础的朋友也能跟上本书节奏,从0到1独立完成一个项目。

希望你学完本书后不只学会了某项技术,而是提高了设计实现整个系统的能力。

适宜人群

  1. Java工程师
  2. 想独立做一个完整项目的朋友
  3. Hadoop 初学者
  4. 大数据开发人员

欢迎加入知识星球

花6个月写的付费专栏,免费送|仿开源框架从零到一完整实现高性能、可扩展的RPC框架的更多相关文章

  1. 仿开源框架从零到一完整实现高性能、可扩展的RPC框架 | 6个月做成教程免费送

    去年年就在写一本付费小册,今年年初基本上就写完了,本来预计计划是春节上线结果由于平台的原因一直拖着没上.五一前跟平台联系给的反馈是五月份能上,结果平台又在重构,停止小册的申请和上线,最后我考虑了一下决 ...

  2. 花1个月时间准备 面试华为,薪资和定级都谈好了却被拒,HR竟说......

    说在前面,千万不要频繁跳槽. 本来华为很想去的,面试前花了一个月的时间准备,面试过程挺顺利的,也拒绝了其他的所有面试邀请,而我拒绝其他面试邀请的底气,则是之前面试过程中的良好表现,薪资和定级都谈好了. ...

  3. 手写RPC框架指北另送贴心注释代码一套

    Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的.所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架. 完整代码以及 ...

  4. 从零开始手写 dubbo rpc 框架

    rpc rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo. 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理. 前言 工作至今,接触 rpc 框架已经有很长时间. ...

  5. 带你手写基于 Spring 的可插拔式 RPC 框架(五)注册中心

    注册中心代码使用 zookeeper 实现,我们通过图片来看看我们注册中心的架构. 首先说明, zookeeper 的实现思路和代码是参考架构探险这本书上的,另外在 github 和我前面配置文件中的 ...

  6. 看了这篇你就会手写RPC框架了

    一.学习本文你能学到什么? RPC的概念及运作流程 RPC协议及RPC框架的概念 Netty的基本使用 Java序列化及反序列化技术 Zookeeper的基本使用(注册中心) 自定义注解实现特殊业务逻 ...

  7. 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍

    概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...

  8. 手写实现RPC框架(不带注册中心和带注册中心两种)

    实现自己的RPC框架如果不需要自定义协议的话那就要基于Socket+序列化. ProcessorHandler:主要是用来处理客户端的请求. package dgb.nospring.myrpc; i ...

  9. 带你手写基于 Spring 的可插拔式 RPC 框架(二)整体结构

    前言 上一篇文章中我们已经知道了什么是 RPC 框架和为什么要做一个 RPC 框架了,这一章我们来从宏观上分析,怎么来实现一个 RPC 框架,这个框架都有那些模块以及这些模块的作用. 总体设计 在我们 ...

随机推荐

  1. 运用shell脚本 执行sftp,ftp命令

    sftp文件上传(从本地上传到远程) #!/bin/bash #远程上传文件测试 if [ $# -ne 2 ] then echo "miss arguments" echo & ...

  2. 算法竞赛进阶指南--lowbit运算,找到二进制下所有是1的位

    // lowbit运算,找到二进制下所有是1的位 int H[37]; // 预处理 for (int i = 0; i < 36; i++) H[(1ll << i) % 37] ...

  3. Codeforce-Ozon Tech Challenge 2020-C. Kuroni and Impossible Calculation(鸽笼原理)

    To become the king of Codeforces, Kuroni has to solve the following problem. He is given n numbers a ...

  4. 图论--最长路--洛谷P1807 最长路_NOI导刊2010提高(07)

    题目描述 设G为有n个顶点的有向无环图,G中各顶点的编号为1到n,且当为G中的一条边时有i < j.设w(i,j)为边的长度,请设计算法,计算图G中<1,n>间的最长路径. 输入格式 ...

  5. P2480 [SDOI2010]古代猪文

    P2480 [SDOI2010]古代猪文 比较综合的一题 前置:Lucas 定理,crt 求的是: \[g^x\bmod 999911659,\text{其中}x=\sum_{d\mid n}\tbi ...

  6. 金钱货币用什么类型--(Java)

    0.前言 项目中,基本上都会涉及到金钱:那么金钱用什么数据类型存储呢? 不少新人都会认为用double,因为它是双精度类型啊,或者float, 其实,float和double都是不能用来表示精确的类型 ...

  7. 线段树 区间加 gcd 差分 小阳的贝壳

    小阳的贝壳 如果线段树要维护区间gcd 这个很简单,但是如果有了区间加,维护gcd 就比较麻烦了. 这个首先可以证明的是 gcd(x,y,z)=gcd(x,y-x,z-y)   这个可以推到 n 个 ...

  8. 王颖奇 20171010129《面向对象程序设计(java)》第十周学习总结

    实验十  泛型程序设计技术 实验时间 2018-11-1 1.实验目的与要求 (1) 理解泛型概念: (2) 掌握泛型类的定义与使用: (3) 掌握泛型方法的声明与使用: (4) 掌握泛型接口的定义与 ...

  9. Java变量相关

    1.Java是强类型语言 所有的变量必须先声明,后使用: 指定类型后只能接受类型匹配的值: 2.变量声明 变量标识符由字母.数字.下划线和$组成: 关键字和保留字不能做标识符: 长度不限制: 大小写区 ...

  10. 【大数据 Spark】利用电影观看记录数据,进行电影推荐

    利用电影观看记录数据,进行电影推荐. 目录 利用电影观看记录数据,进行电影推荐. 准备 1.任务描述: 2.数据下载 3.部分数据展示 实操 1.设置输入输出路径 2.配置spark 3.读取Rati ...