plainify

互联网是如何工作的?

前言 我们每天都在使用网络,面对这个看不见也摸不着的东西,你有没有思考过他的背后发生了什么吗? 相信很多人第一次接触网络是通过一个叫「网站」的东西,那网站又究竟是什么? 影视剧里经过出现的通过 IP 地址获取定位,是真的可以实现吗? 相信你在阅读本文后就会有一个清晰的认知了。 网站?服务器?网络? 首先我们要理解的是,一个网站只是一堆保存在硬盘上的文件, 就像你的电影、 音乐或图片一样。 然而,网站的唯一的不同之处是: 网站包含一种称为 HTML 的代码。也正是这个叫 HTML 的东西让网站有了好看的皮肤(界面)。 如果你对编程不熟悉,一开始你会很难理解 HTML,因为 HTML 其实是让你的浏览器(Chrome、Safrai、FireFox、IE 等)去**“理解”**的信息,浏览器读得懂这些代码,然后会按照代码的内容展示这些文件。 就和我们对待自己的电脑文件一样,我们会把 HTML 文件存储在硬盘的某个位置, 然后通过浏览器去访问这些 HTML 文件。对于互联网,我们使用特定而功能强大的电脑,我们称之为服务器,所以其实服务器本质上也是一台电脑,它们没有屏幕、鼠标或者键盘,因为它们的主要目的是存储数据,并用它来提供服务。 这就是为什么它们被称作服务器的原因——因为他们用数据服务你。 因此如果想要存储更多的信息,我们有两种解决方案: 增加服务器的容量 增加服务器的数量 显而易见的,一台服务器可以存储的信息是有限的,哪怕是不断增加一台机器的容量也肯定是有上限的。所以,我们可以通过第二种方式以达到**「量变产生质变」**的效果。 于是,一台又一台的服务器通过“网线”连接在了一起,形成了类似下面这样的结构 众多服务器组成的这种结构被称为“互联网”,而其中每台服务器都被称作“节点”,所以「顺着网线来打你」是有理论依据的。 IP 地址?域名? 那么紧接着,问题就来了。 既然网络是用来存储文件的,对于我这个初次上网的人,我又如何找到我想要的内容呢? 不着急,我们先来设想一个场景。 闰土家里有几套房子,其中房子 A 被闰土爸妈当作仓库了,一天,妈妈让闰土去房子 A 拿一个家传的盒子 X,说是给未来儿媳妇准备的,房子 A 在 a 市 b 区 c 小区 d 号,并给了侧门钥匙,让他从侧门 C 进去拿,盒子 X 在房子 A 的 2 楼的房间 B 里的衣柜。...

plainify

想追女神?先学 Synchronized 吧

在之前的《从找对象到多线程》一文中我曾介绍了一些和多线程有关的知识,而谈到多线程,就一定离不开「锁」这个名词。在 Java 中,锁的使用主要有两种:Synchronized 关键字和 Lock 接口,本文将会换个角度来聊一聊 synchronized 中的锁。 Synchronized 用的锁是存在对象头里的,用来表明当前对象所持有的锁。在 Java SE1.6 之前,Synchronized 是作为重量锁出现的,一旦使用了 synchronized,就一定会阻塞到其他线程。而在 Java SE1.6 后,为了减少获得锁和释放锁带来的性能问题,引入了"偏向锁"和"轻量锁"的概念。由此可以得知,在新的 Java 中,锁一共有 4 种状态:无锁状态、偏向锁状态、轻量锁状态和重量锁状态。这几个状态会随着竞争不断升级且只能升级不能降级,即轻量锁只会升级到重量锁而不会降级到偏向锁。 以上的解释未免太过官方了,我们从一个小例子入手。 我们用女神来表示同步代码块,就好比女神有很多追求者,同步代码块也会被很多线程执行。有一天女神的微博状态变成了「单身」,此时她就处于无锁状态,于是这些追求者纷纷创建了一个名为**「找对象」的线程**,此时对于女神(对象)来说,还没有任何线程来访问她,所以当第一个男生小 A 试图邀请她看电影的时候**(获取锁)**,她会偏向小 A 的邀请,此时她就是处于「偏向锁」状态的。有了这次经历之后,小 A 就知道该怎么邀请女神而不用反复试探了,这就是「可重入锁」,即同一个线程可以多次访问同一代码块。 再后来女神发了一条微博,说今天和这个男生看电影很开心。这条微博被其他男生看见了,大家也都知道了女神这个对象的偏向状态了。可还是有男生小 B 想追女神,此时这两个男生各自「找对象」的线程就在女神这个对象上产生了竞争。 小 B 一直关注女神的微博动态,他心想着,只要小 A 被女神拒绝了,女神就会变成「无锁」状态,自己也就有机会被女神偏向了。女神也知道小 B 在追自己,为了找到最合适的另一半,女神也在暗中观察小 B,有两个竞争者同时竞争,这时候她就处于**「轻量锁」的状态。虽然女神明显更喜欢小 A,但在小 B 心里觉得小 A 除了比自己早点出现外根本不具有和自己竞争的能力,于是不断给女神献殷勤,保持关系,这就叫自旋,**不断的将自己的时间花费在获取锁上,逐渐成为一条舔 🐶。 虽然一开始女神也会偶尔答应小 B 的邀请,但当竞争者越来越多后,小 B 变得疯狂起来,追求逐渐变成了骚扰,女神也逐渐不耐烦起来。最终在小 A 的努力下,女神和小 A 确定了关系,并发了微博告知众人,此时她的状态就升级成为**「重量锁」状态**。这时,除了小 A,其他所有竞争者的「找对象」线程都没有办法再追求女神了。这样做的好处就是赶紧断了那些追求者的念头,让他们可以早日觅得其他良人,不要在一棵树上吊死。 从线程的角度来看,重量锁使除了拥有锁的线程外的其他所有线程都阻塞,这样可以有效防止 CPU 空转,避免造成资源的浪费。 在偏向锁和轻量锁阶段,女神还没有和任何人确定关系,只要给点甜头小 B 等其他追求者都会很开心,这是一种「乐观锁」。而一旦女神和小 A 确定了关系,自身状态升级为重量锁后,小 B 就很不开心了,对他来说这就是一种「悲观锁」。...

plainify

接口调度者—— API 网关

背景 我们知道在微服务架构风格中,一个大应用被拆分成为了多个小的服务系统提供出来,这些小的服务他们自成体系,也就是说这些小系统可以拥有自己的数据库,框架甚至语言等,这些小系统通常以提供 Rest Api 风格的接口来被 H5, Android, IOS 以及第三方应用程序调用。 在《浅入浅出消息队列》这一篇文章中,我提到了消息队列是方便服务与服务之间的通信解耦,如下图所示: 那么这时候问题来了,如果一个外部的应用(浏览器、App)要去访问这个大应用怎么办? 很简单啊,直接通过 HTTP 请求不就完了? 问题 真的这么简单吗?我们以淘宝的商品详情页为例: 如上图所示,这个页面包含了视频、库存、商品价格、商品评价等内容,这些数据都来自不同的微服务中,所以没办法像传统单体应用一样依靠数据库的 join 查询来得到最终结果,因此就需要多次调用以检索数据,如下图所示: 这就会引发几个严重的问题: 不同的客户端设备可能需要不同的数据。Web,H5,APP,需要单独写一套 API 多次客户端请求导致用户体验不佳。移动网络相较于服务于服务间的局域网,有更低的带宽和更高的延时,如果可以同时执行请求倒也还好,但如果客户端要按照顺序执行请求,就会让用户体验变得异常糟糕。 缺乏封装导致前后端不协调。过分的拆分 API,会导致客户端和服务端过度耦合,再加上移动端 APP 的新版本迭代到每个手机用户时需要很久,这样会使后端很难更改服务的 API。 这样显然是不好的设计,因此,本期的“天降猛男”就出现了——API 网关。 API 网关 在介绍 API 网关前,我们先来介绍一个设计模式——外观模式。 外观模式(Facade Pattern)它向现有的系统添加一个接口,来隐藏系统的复杂性。类图如下所示: 之所以要在说 API 网关前说一下外观模式,是因为二者的设计理念是类似的。 和外观模式类似,API 网关封装了应用程序的内部架构,并为其客户端提供 API,他还可能具有其他职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。下图展示了客户端、API 网关和服务之间的关系。 所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。其出现也是侧面贯彻了软件工程中**“高内聚,低耦合”**的思想。 核心作用 API 网关负责请求路由、API 组合和协议转换。来自外部客户端的所有 API 请求首先会先转到 API 网关,后者再将请求路由到相应的服务。API 网关使用 API 组合模式处理其他请求,调用多个服务并聚合结果。同时他还可以在客户端友好的协议(例如 HTTP)与客户端不友好的协议之间进行转换。 请求路由 当 API 网关收到请求时,随机会查询路由映射,该映射将指定请求路由到哪个服务。例如,路由映射可以将 HTTP 方法和路径映射到服务的 HTTP URL,这一点和 Nginx 提供的反向代理的功能是一样的,后面我们也会对其进行一个比较。...

plainify

你的系统可用性 5 个 9 了吗?

又是一年放榜日,众多考生满怀期待的点开招生网,结果输了信息才发现根本没办法查询——查询人数太多了,直接把系统打挂了! 这个时候,还没翻身的码农闰土被问到一个直击心灵的问题:这个系统可用性达到了多少个 9? 想要回答这个问题,我们得先有些前置知识。 可用性&可靠性 这两个词很相似,我也一直找不到一个很好的定义区分这两个词,直到后来在看分布式系统的时候,看到了一个解释: 可用性被定义为系统的一个属性,它说明系统已准备好,马上就可以使用。换句话说,高度可用的系统在任何给定的时刻都能及时地工作。 可靠性是指系统可以无故障地持续运行,是一个持续的状态。与可用性相反,可靠性是根据时间段而不是任何时刻来进行定义的。 举个例子,想要评估一个舔 🐶,可用性就是你找他的时候能不能找到,而可靠性就是你需要花钱的时候他出手大不大方。一个舔 🐶 如果随叫随到,但是花钱太抠,就是高可用、低可靠;而如果他经常找不到人,但出手很大方,就是低可用、高可靠。 类比到系统时,如果系统在每小时崩溃 1ms,那么它的可用性就超过 99.9999%,但是它还是高度不可靠。与之类似,如果一个系统从来不崩溃,但是每年要停机两星期,那么它是高度可靠的,但是可用性只有 96%。 百度百科对于系统可靠性的解释是:系统可靠性一般是指在规定的时间内和规定的工况下,系统完成规定功能的能力/概率。也就是系统的无故障运行概率。而在我们在评估一个系统的可用性和可靠性时,一般都会说三个 9,四个 9 之类的。这些一般都是说系统的**SLA(Service Level Agreement)**具体是几个「9」,以此,来表示该系统一年中具体宕机的时间。对于这几个 9 的解释,我会放到第三节来详细解释。 不过,在实际交流过程中,大多数人对这两个词的理解还是差不多的。况且咬文嚼字也并非本文的主题,接下来我们来看看可用性的计算方式。 可用性计算 通常我们用 A 表示一个系统的可用性,用以下几个指标来辅助计算: 相关指标 MTBF MTBF,即平均故障间隔时间,英文全称是“Mean Time Between Failure”。是衡量一个产品(尤其是电器产品)的可靠性指标。单位为“小时”。具体来说,是指相邻两次故障之间的平均工作时间,也称为平均故障间隔。 MTTR MTTR,全称是 Mean Time To Repair,即平均修复时间。是指可修复产品的平均修复时间,就是从出现故障到修复中间的这段时间。MTTR 越短表示易恢复性越好。 通过上述公式计算出单个组件的可用性后,我们便可以以此计算出整个系统的可用性,而系统可用性是通过将系统建模为串联和并联的组件来计算的。以下规则用于确定系统是串联的还是并联的: 如果组件的失效导致组合变得不可操作,则认为这两个部件是串联操作的 如果组件的故障导致另一部件接管故障部件的操作,则认为这两部件并行操作 串行可用性 如上图所示,两个组件 X 和 Y,如果有一个出问题导致整个组合都不可用,就认为 X 和 Y 这两个组件是串联的。只有组件 X 和组件 Y 同时可用时,整个组合才可用。由此可见,组合的可用性是这两部分的乘积,公式如下: A = Ax Ay...

2020-08-22 251 words 2 min
plainify

【译】如何创建一个可复用的网页爬虫

原文地址:How to Create a Reusable Web Scraper 网页爬虫是个非常有趣的玩具。不过不好玩的是,我们需要根据不同网页上的元素不断的调整自己的代码。这就是为什么我要着手实现一个更好的网页爬虫项目——通过该项目可以以最少的更改实现对新网页的爬取。 第一步是将网页爬虫按照逻辑分成每个独立的部分: 页面请求器 页面验证器 模板页面处理器 页面请求器 页面请求器的实现有一些技巧。下载网页时要考虑很多因素。你需要确保你可以随机的使用用户代理,并且不要过于频繁地从同一域中请求。 此外,停下手头的工作去分析为什么网页无法下载是一件出力不讨好的事。尤其是当你的爬虫已经在多个站点运行了好几个小时的情况下。因此,我们会处理一些请求,并将它们保存为文件。 将请求保存到文件中还有另外一个好处。你不必担心一个标签的消失会影响到你的爬虫。如果页面处理器是独立的,并且你已经完成了页面的下载,你还可以根据需要快速且频繁的对其进行处理。如果发现有另一个要抓取的数据元素怎么办?别担心。只需添加一个标签,然后在你已下载的页面上重新运行处理器即可。 以下是一些实际情况下的示例代码: import requests import pickle from random import randint from urllib.parse import urlparse def _random_ua(): ua_list = ["user agent1, user agent2", "user agent3"] random_num = randint(0, len(ua_list)) return ua_list[random_num] def _headers(): return { 'user-agent': _random_ua() } def _save_page(response): uri = urlparse(response.url) filename = uri.netloc + ".pickle" with open(filename, 'wb+') as pickle_file: pickle....

plainify

云计算之 Anything As a Service

这年头,不管什么应用,仿佛“上云“都成了标配,感觉拥抱了“云”,应用的逼格都上升了。那真的是这样的吗? 是的,的确是这样。 云计算是一种按照需求通过 Internet 获取计算资源的形态。这些计算资源被包装成为服务,提供给用户。而提供这些服务的主体,我们称之为云服务供应商(Cloud Service Provider)。云服务最主要的有三类,就是本文的三个主角 IaaS、PaaS、SaaS。 那这三个 aaS 到底是个啥? XaaS 看到这个标题,可能就有人产生疑惑了,那三个 aaS 都不知道是什么,怎么突然又多了一个 XaaS。不急,且听我娓娓道来。 XaaS,即一切皆服务,代表 “X as a service”、“anything as a service”或“everything as a service” 。这一缩写指越来越多地通过互联网提供的服务,而不仅仅指本地或现场服务。云计算的本质就是 XaaS。 XaaS 最常见的例子就是“软件即服务”(Software as a Service,SaaS)、“基础设施即服务”(Infrastructure as a Service,IaaS)和“平台即服务”(platform as a service,PaaS)。 详解 根据所提供服务的不同,云服务可以被分为以下三类/三层: 缩写 全写 注释 IaaS Infrastructure as a Service 基础设施即服务 PaaS Platform as a Service 平台即服务 SaaS Software as a Service 软件即服务 如下图所示:...

plainify

DO,VO,DTO 你知道吗?

作为后端最常用的编程语言之一,Java 已经有很多年的历史了,在阿里内部,Java 也是使用最广泛的一门语言。在阿里实习的这段时间,规范一词是我感受最深的。没有规矩不成方圆,今天来说一下 Java 中的各种 O(bject)。 为什么会出现这些 O? 我们知道,这些 O 不管叫什么名字,其本质都还是对象(Object),既然本质都一样,为什么非要给他们套上各种马甲? 个人认为原因有三: 第一,随着编程工业化的发展,需要有一套合理的体系出现。中国人喜欢造神,外国人喜欢造概念,于是 MVC、MVP、MVVM 等编程模型就出现了,为了搭配这些编程模型的使用,需要对 Object 的功能进行划分,于是我们便看到了这些层出不穷的 Object。当然这里并没有批评这些概念的意思。 其二,我认为在团队协作编码中,一个好的命名方式是可以节约很多时间成本的。就比如getItemById一眼看去就知道是通过 id 获取一个 item 对象,ItemVO一眼看去就知道是前端透出的 json 对应的对象。 其三,如此划分,可以让项目结构更加清楚,不至于出现东一块西一块,对象乱扔的局面。尽可能避免了在多人协作时对象混乱的情况。 总的来说,这一切都是为了让软件编程更加合理、更加规范、更加高效。 有哪些 O? 这些 O 有很多衍生出的命名,比如 VO、DO、BO,这里我们把常见的 O 列举出来,然后一一解释。 以下内容参考阿里巴巴 Java 开发手册,如果有需要可以在微信公众号「01 二进制」后台回复「Java 开发手册」获得。 DO( Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。 PO(Persistant Object):持久对象,一个 PO 的数据结构对应着库中表的结构,表中的一条记录就是一个 PO 对象 DTO( Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。 BO( Business Object):业务对象。 由 Service 层输出的封装业务逻辑的对象。 AO( Application Object):应用对象。 在 Web 层与 Service 层之间抽象的复用对象模型,极为贴近展示层,复用度不高。 VO( View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。 POJO( Plain Ordinary Java Object):POJO 专指只有 setter/getter/toString 的简单类,包括 DO/DTO/BO/VO 等。 DAO(Data Access Objects):数据访问对象,和上面那些 O 不同的是,其功能是用于进行数据操作的。通常不会用于描述数据实体。 一下子给出 8 个常见的 O,光看解释大家可能会有些迷糊,接下来我们从下面这张图入手,带大家直观的感受下,这些 O 的用处。...

plainify

浅入浅出消息队列

高中的时候,每节自习课都会有人零零散散的找老师问问题,一开始就一两人还好,后来渐渐的人多了,老师也烦了,你说我这上了一天的课难得晚上可以看自习休息会,这帮小崽子还一个个这么折腾人。 于是老师决定让同学们把需要提的问题写在纸上,下自习后交给课代表,然后老师再从课代表那取出要问的问题,然后再一个个解决。 相信在学生时代大家都遇到过上面的这种情况,如果我们将在学校上课抽象成一个系统,那这种情况就是一个很常见的消息队列的使用场景。 在上述实例中,要提的问题就是**「消息」,提问题的学生是「生产者」,回答问题的老师是「消费者」,收集问题的课代表是「消息队列」**。 看到「生产者」和「消费者」,不知道有没有想起来在 RPC 的中同样也有生产者和消费者,那么这两者之间有关联吗? 如果对 RPC 还不了解的读者可以阅读我之前写过的一篇文章 我们先来回顾一下,在 RPC 的调用关系里,我们把提供具体的调用方法的系统叫服务提供者(Provider),调用服务的系统称为服务消费者(Consumer)。 如果有 N 个不同系统相互之间都有 RPC 调用,这时候整个系统环境就是一个很大的网状结构,如下图所示,依赖关系有 N*(N-1)/2 个。任何一个系统出问题,都会影响剩下 N-1 个系统,这种强耦合肯定不是我们想要的,那应该怎么降低这种耦合呢? 基于这些问题,消息队列(Message Queue,简称 MQ)技术横空出世,所有的处理请求先作为一个消息发送到 MQ(一般我们叫做 broker),接着处理消息的系统从 MQ 拿到消息并进行处理。这样就实现了各个系统间的解耦,同时可以把失败策略、重试等作为一个机制,对各个应用透明,直接在 MQ 与各调用方的应用接口层面实现即可,如下图所示: 一般来说,我们把发送消息的系统称为消息生产者(message producer),接受处理消息的系统称为消息消费者(message consumer)。 一般来说,我们把发送消息的系统称为消息生产者(message producer),接受处理消息的系统称为消息消费者(message consumer)。 根据消息处理的特点,我们又可以总结两种消息模式: 点对点模式(Point to Point,PTP),一个生产者发送的每一个消息,都只能有一个消费者能消费,看起来消息就像从一个点传递到了另外一个点。 发布订阅模式(Publish-Subscribe,PubSub),一个生产者发送的每一个消息,都会发送到所有订阅了此队列的消费者,这样对这个消息感兴趣的系统都可以拿到这个消息。 使用 MQ 的好处 从上面的介绍中我们可以看出 MQ 的出现在一定程度上是可以将复杂的服务解耦。 那么回到一开始的例子,我们发现,课代表的出现也确实在一定程度上解决了老师和学生之间的强耦合关系。类似的例子还有快递柜,快递员将快递放到快递柜,不方便拿快递的人可以在下班之后再去拿快递。这样快递员和收快递的人就可以更加自主,只通过一个简单的“容器”——快递柜来联系。你甚至可以不知道你的快递员长什么样子,快递员也可以不知道你是谁,在他们眼里,都只有快递柜,没有对方。 毫无疑问,与一个简单的容器打交道,比与复杂的人打交道容易一万倍,他们也可以更加专注的去完成自己的事情。 那么除解耦外还有其他的优势吗?自然是有的。 再回到一开始学生提问的例子,我们设想另一个场景:如果提问的问题特别难,而且是每个人都要问这个问题,老师给每个人讲解要花很多时间,学生有压力,老师也会觉得麻烦,所以老师决定第二天上课的时候把这个问题单独拿出来说。 在上面这个事例中,就体现了 MQ 的另外两个作用——广播和削峰。 广播:老师只需要讲解一次,所有需要听这道题的同学就都可以听了。 削峰:将当晚那么多同学讲题的需求延迟到第二天上课。 当然,在实际的使用过程中,MQ 的用处也不只是这么些,这里推荐阅读阿里巴巴中间件团队的十分钟入门 RocketMQ 中的第一章 👉消息中间件需要解决哪些问题? MQ 的使用成本 如此一看,消息队列确实有很多好处,不过凡事皆有利弊,消息队列的使用也是有一定的使用成本的。...

plainify

聊一聊 RPC

随着近几年分布式、微服务架构的火热,RPC 在开发工作中使用的越来越多,也变的越来越重要。 作为一个学生,在学校接触到的大多都是 SSM 这类单体应用,但实习后发现,基本是接触不到从 0 到 1 的项目的,更多的是在为整个大系统的某个小模块添砖加瓦。因此,模块与模块之间的通信就变得异常重要。 集群、微服务、分布式 《道德经》是老子的宇宙生成论,其中“一生二,二生三”广为流传,对于一个软件系统来说,笔者认为这句话也同样适用。所谓一,便是系统的业务需求,无论何人,其编写的每行代码最后都是为了服务业务,或是实现业务功能,或是提升业务性能,最终目的均无法逃离业务。一般意义上,一个公司的业务系统发展脉络基本都是类似的:从单个应用到多个应用,从本地调用到远程调用,随着业务规模的发展,需要对远程服务进行高效的资源管理。于是分布式、集群、微服务等**“银弹”**便应运而生。 在欧洲民间传说的影响下,银弹往往被描绘成具有驱魔功效的武器。 后来也被比喻为具有极端有效性的解决方法。不过佛瑞德·布鲁克斯所发表一篇关于软件工程的论文中提到在软件工程领域是没有银弹的,复杂的软件工程问题无法靠简单的答案来解决 为了分散业务能力,出现了“微服务”;为了分散机器压力,出现了“集群”和“分布式”。那这三者有何关联?我们以一张图来说明: 某大型系统下有众多功能,如订单、视频、物流等,项目初期可能是写在一个大的工程里,部署在一台机子上,后来业务发展了,每个子功能都变得相当复杂,用 IDE 打开这个项目都要花好久,为了方便开发,开发团队将每个功能分开,并起名为“服务”。每个服务可以操作自己的数据库、缓存等,也可以在本机与其他服务通信(这时项目仍然部署在一台机子上)。再后来,一个 DB、Redis 已经没办法满足这个服务的需求,所以又将单个 DB 扩展成 DB 集群,单个 Redis 扩展成 Redis 集群以此来分担机器的压力。再后来,这些服务所在服务器的性能被压榨的一滴也没有了,没办法,只能将这些服务一个个的分在不同的机器上,这就是**“分布式”**。 由此可见,集群,是在多台机器上部署相同的程序,对于集群内部而言,每台机器是一个不同的节点。但对于集群外部(调用方)而言,集群就是一个整体,操作起来就和操作单个数据库、单个 Redis 没有任何区别。对于整个项目来说,如果集群中某个节点挂了,整个集群仍然可以正常工作,这是一种纵向的扩展。 而分布式,是指在多台机器上部署不同的模块。这些模块原本可以放在一台机器上,这叫中心化,一旦这台机器崩溃,上面所有的服务就会崩溃,整个项目也就崩溃了。因此我们可以将系统横向拆分成多个服务后部署到不同的服务器上,如果一台机器崩溃,虽然这台机器上的服务也会崩溃,但不至于导致整个系统发生崩溃,这叫去中心化。 所以随着业务的发展,微服务、集群、分布式这些名词的出现是很有必要的。 RPC 的三个问题 上面我们用了一定篇幅解释了微服务、集群、分布式这些比较火的名词,接下来我们回到本文的主角——RPC。 RPC(Remote Procedure Call),即远程过程调用。不同于本地调用,函数与函数之间同属于同一块内存空间,如需调用某个函数,只需要找到所在内存地址即可。远程调用,通俗地说,便是有两台服务器 A,B,一个应用部署在 A 服务器上,想要调用 B 服务器上应用提供的函数/方法,需要通过网络来表达调用的语义和传达调用的数据。 知道了 RPC 是什么,以及为什么需要 RPC 后,接下来我们就要看看如何实现 RPC 了。文末我会给出一个简单的用 Java 实现 RPC 的 demo,这里先从一个有趣例子出发给出需要解决的三个问题:Call ID 映射、序列化和反序列化、网络传输。 从一个有趣的例子出发 笔者之前写过一篇《从找对象到多线程》,文中以找对象这个例子出发,介绍了线程,这次就让我们开一个线程来看一下远程调用 RPC 吧。 笔者的好友在一个男生如云的工科学校,机缘巧合下,喜欢上一个隔壁学校的妹子,终于有一天他决定告白。所以,第一步就是要知道那个女生所在的学校、年级、班级、姓名等相关信息,确定到那个人,这个过程就是Call ID 映射。由于疫情的原因,虽然各自都开学了,但都被学校强制封闭性管理,无法直接见面,因此,男生就想着用**情书的方式表达自己的爱慕之意,这个过程就是序列化**。男生想着,只要女生收到情书后便能**理解自己的爱慕之情,就会和自己在一起了,这就是反序列化**。剩下的就是如何将情书**送过去了。可选的方式有很多,比如找快递小哥送、发微信、发邮件之类的,只要能将信息传送过去就可以。这个过程就是网络传输**。 Call ID 映射 不知道上面的例子有没有很好的解释Call ID 映射、序列化和反序列化、网络传输是什么东西。将上述例子类比到项目中,我们就能很好的理解为什么需要解决这些问题了。...

2020-06-13 187 words 1 min
plainify

聊一聊 2038 年问题

庚子年是中国传统的 60 甲子纪年法。擅长观测的古人很早就发现,每当年份执行到庚子这一年,自然灾害变多,突发事件频频,一些震动世界、影响安定的大事件也容易发生在这一年。而我们现在所处的 2020 年就是新一轮的庚子年,现在都 4 月了,很多网友都调侃说新的一年什么事情都没做,光在见证历史了。 当然了,作为一个技术博主,我并不是来给大家科普庚子年的,今天我们要说的是计算机中的一个比较危险的年份——2038 年。 2038 年问题 在说 2038 年问题前,我们需要先明白计算机是如何存储系统时间的。 在 Unix 或类 Unix 系统中,都是以 1970 年 1 月 1 日 0 点 0 分 0 秒作为时间的基准点,用秒数来表示系统时间,也即当前系统时间是从基准时间(1970 年 1 月 1 日 0 点 0 分 0 秒)走过多少秒之后的时间。 用公式简单表示就是这样:系统时间 = 基准时间 + 秒数 因为基准时间是确定的,所以我们唯一需要考虑的就是秒数应该如何保存。在当时那个年代,计算机硬件资源非常紧缺,用 16 位表示数据就已经是一个非常奢侈的选择了。因此当时的设计者都认为用 32 位存储秒数已经**“足够大”**了。所以从那个时候开始使用 32 位来表示秒数。 我们知道在二进制中,32 位数能表示的最大的数是$2^{32}-1$,不过为了能够让时间可以往前往后数,会用第一位作为正负的判断位,第一位如果为 0,则说明为正;若第一位是 1,则说明为负。因此,在 Unix 或类 Unix 系统中,这$2^{32}-1$个数被分成了 2 部分,分别是$+2^{31}-1$和$-2^{31}$。这样我们就可以以 1970 年 1 月 1 日 0 点 0 分 0 秒作为时间的基准点,往前往后每过一秒就加一个数字,依此来计算时间。...

2020-04-16 199 words 1 min