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
plainify

你看那个区块链,像不像我借你的二百元?

最近这几天区块链又粉墨登场了,新闻媒体也一直在大量报道,宣称可能要在金融界掀起一番浪潮。甚至有人说很久之前中国就出现了区块链的产物——麻将。那么区块链到底是什么,麻将和区块链又有什么关系呢? 笔者这两天也阅读了相关文献和资料,下图是我学习的一个路径,本文也是按照自己了解区块链的路径做的一些总结,篇幅较长,写这篇文章的目的也是希望能简单地向对区块链技术不了解但是想知道区块链是什么的人介绍区块链技术,因为自己在区块链方面也是一个小白,如果有错误的地方,还望各位留言指正。 区块链 -> 比特币 首先我们得先知道,区块链和比特币之间并不等同,如果要论辈分,区块链应该是比特币的爸爸,为什么这么说呢?我们先来了解下区块链是什么,以及区块链是用来干嘛的。 什么是区块链?区块链是做什么的? 一提到区块链,相信很多人都会想到什么去中心化、分布式、不可篡改之类的名词。说实话我刚开始由于姿势水平不够看到这些名词的时候也是一头雾水,《区块链技术发展现状与展望》一文给出如下定义: 狭义来讲,区块链是一种按照时间顺序将数据区块以链条的方式组合成特定数据结构, 并以密码学方式保证的不可篡改和不可伪造的去中心化共享总账(Decentralized shared ledger),能够安全存储简单的、有先后关系的、能在系统内验证的数据。 广义的区块链技术则是利用加密链式区块结构来验证与存储数据、利用分布式节点共识算法来生成和更新数据、利用自动化脚本代码(智能合约)来编程和操作数据的一种全新的去中心化基础架构与分布式计算范式。 上面的文字毕竟太过学院派了,如果用一句话来概括,区块链其实就是一种数据结构(栈和队列也是一种数据结构),既然他是一种数据结构,那区块链的作用自然也就不言而喻了:组织并存储数据。剩下的一些定语如「去中心化」、「分布式」这些无非就是对这种数据结构的修饰罢了。比特币就是应用这种技术制作的数字货币。 区块+链=区块链? 既然他和栈一样是一种数据结构,那我们总得知道这个数据结构长什么样子吧。我们把区块链拆分成**「区块」和「链」**就会明白区块链长什么样子了。 区块 上图是一个**区块(Block)**示意图,每个区块包含两个部分: 区块头(Block Header):记录当前区块的特征值(里面存放的内容我们稍后再说) 区块体(Block Body):存放的数据 所以,区块就是一种存放数据的东西,可以近似的理解为链表中的节点。 链 把许多上图那样的区块连接在一起就形成了区块链,如下图所示: 想要形成一个链,那总得有头吧,链头的区块学名叫做创世区块(Genesis Block)。前一个区块称为后一个区块的父区块,反之则称为子区块。 所以,其实区块链就长上面那样,没什么神秘的。 这时我想肯定会有人问了,你说把区块连接在一起他们就能连接在一起了吗?他们之间是怎么连接的呢? 如何链接 这里我也不卖关子了,子区块与父区块是通过父区块的 哈希(Hash) 值建立链接的。这里又引入一个新的概念,什么是哈希呢? 所谓「哈希」就是计算机可以对任意内容,计算出一个长度相同的特征值。区块链的哈希长度是 256 位,这就是说,不管原始内容是什么,最后都会计算出一个 256 位的二进制数字。而且可以保证,**只要原始内容不同,对应的哈希一定是不同的。**因此我们可以得到两个推论: 推论 1:每个区块的哈希都是不一样的,可以通过哈希唯一标识区块。 推论 2:如果区块的内容变了,它的哈希一定会改变。 所以我们只需要在每个区块的区块头存放上个区块的哈希值即可。就是下图红线标注的地方。 至于如何计算出这个哈希值,不同的区块链有不同的计算方法,这里不多说。 下面这篇文章介绍了比特币是如何计算 Block Hash 的,有兴趣的可以阅读了解下。👇 https://www.jianshu.com/p/4187a7352769 为什么需要区块链? 看了上面的内容后相信你应该就明白什么是区块链了。但是肯定就会有人说,说到底这不就是链表吗,为什么国家还要大力发展区块链产业,甚至上升到了到战略层面呢? 的确,区块链的技术原理并不复杂,但是他的『社会意义』却是巨大的。 区块链本质上是一种解决信任问题、降低信任成本的技术方案,其目的就是为了去中心化。 好了,这里又出现了一个新的概念,去中心化?这个名词只要一提到区块链就一定会被提到,所以你一定想知道去中心化到底是什么。不急,在谈去中心化之前,我们先谈一下另一个词——信任。...

plainify

从找对象到多线程

最近遇到了一个和多线程有关的事,顺便简单复习了下相关概念,稍后举个栗子 🌰 来解释下。 进程 and 线程 进程我们都知道,就是应用程序的启动实例。比如我们运行一个游戏,打开一个软件,就是开启了一个进程。 而线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。 我们可以简单的理解:为了做一件事,我开了一个进程,为了让这件事更有效率的完成,我开了多个线程。 从找对象入手加深理解 如果这么说还是不清楚的话,我们以一个找对象的例子来加深理解: ​ 读研了,望着身边的小伙伴一个个都是成双成对,笔者有一个万年单身的好基友就跟一条酸菜鱼似的(又酸又菜又多余),想着要不也找个女朋友吧?于是就创建了一个名为**「找对象」的进程**。但是学校女生太多了,如果一个一个试的话不知道啥时候才能脱单。这时候,万能的度娘给出了建议,为什么不同时下手呢? 于是,他就创建了两个「撩妹」的线程,每个线程负责撩一个妹子。但这时就有问题出来了。 ​ 虽然他可以同时撩两个妹子,但是有些事情就是没办法同时和两个妹子做,比如上课、吃饭、散步。这个时候就需要**「数据保护」。做这些事情的时候,他一次只能跟一个妹子做,这就叫做「临界区」。他撩的两个妹子,一个是红玫瑰,一个是白玫瑰,各有各的特点,这是她们的「私有属性」。这两个妹子都可以跟他约会,此时他就是一个「共享内存」。如果某一天他跟其中一个妹子在外面约会,那另一个撩妹线程就只能「阻塞」了。但是为了防止后院起火,不让另一个妹子不爽,他就只能发条朋友圈说自己在学习,不想被打扰,这就叫「互斥锁」。同时为了不让她们影响对方的存在,他用 QQ 叫着其中一个的小名,微信却是另一个的情侣头像,这就是「消息传递」**。 ​ 上述两个线程在某种意义上就构成了**「竞态条件」(个人认为叫做「竞争状态」会更好)。如果他只有这两个线程的话,想要完成「找对象」这个进程,就只能看这两个线程谁的执行效率更高(其实就是看谁先运行完)**了。这么一看,我的好基友貌似很快就可以完成「找对象」这一进程了,因为无论是哪个线程,只要有一个「撩妹」线程结束了,整个进程也就结束了。 ​ 但是在结束前**可能会出现一些问题(注意这里是可能),如果他在排约会档期时一旦没有进行良好的调度,就很有可能会出现两个妹子约在同一天约会,基友却没办法到达的情况,这种情况就叫「死锁」,即较长时间的等待或资源竞争。亦或是如果不幸被两个妹子都发觉了对方的存在,却又为表大度,你让我我让你,最终导致「找对象」进程无法进行下去,这就叫做「活锁」。 如果此时出现了一个「工具人」妹子,主动倒追我基友,这样的话即使被加入了「找对象」的进程,也可能会因为一系列问题始终没法和他单独约会(毕竟还有两个「撩妹」线程在进行着),这种情况就叫做「饥饿」**。 这三种情况总结一下就是: 死锁:争来争去 谁也得不到 活锁:让来让去 谁都不拥有 饥饿:排多久 都轮不到自己 倘若我基友最后撩成功的女朋友并不是他最喜欢的那个人,即最终的运行结果和预期不同,这就叫**「线程不安全」。反之,如果最后撩成功的妹子是他最喜欢的,即最终的结果和预期相同,那这个就叫「线程安全」**。 这么一看,真是人生如戏。只是可悲的是,我本以为现在执行的是一个单进程,却没想到也只是别人的一个线程罢了。 如果你觉得我的文章还不错,不妨扫描下方二维码关注我,你的支持是我前进的最大动力 💪

plainify

科普系列——从网购/直播入手理解什么是CDN

前言 相信很多人在制作自己的第一个网站的时是很激动的。我们知道,在一个网站项目中,页面里经常会有许多 JavaScript 以及 CSS 的引用,如果是直接引用项目内文件的话,他们可能是这样的: 这种方式的优点是开发省力,发布省力,对服务器要求小,省钱,没有具体公网接入需求。 然而如果你的网站里面有很多图片或者视频并且需要部署到公网上时,网站的访问速度一定会让你倍感崩溃。就像下面这张图 👇 这时候肯定会有推荐你使用 CDN 来加速网站里的一些 JavaScript 和 CSS 文件,如下所示: 其实上面的图片就已经使用到 CDN 了。那到底什么是 CDN 呢? 在解释什么是 CDN 之前,我们先来看一个身边非常常见的案例—— 网购。 京东自营与淘宝的购物体验 相信现在应该没有人没用过淘宝和京东的,在说 CDN 之前我们先来说下我在淘宝和京东的购物体验。下面是我在使用这两个电商平台时的情况: 在淘宝买第三方店家商品 在京东购买自营商品 之前我在淘宝买了一个雷电 3 的扩展坞,发货地是深圳,花了三天才到南京,如果收货地是河南呢?新疆呢?我想时间就更长了。可是我在河南的同学在京东(自营)买了一个手机下午购买的第二天早晨就收到货了(并不是给京东打广告)。这是为什么呢? 我们在用京东购物的时候,如果仔细观察的话可以发现,京东自营会根据我们的收货地点,在全国范围内找离我们最近、送达最快的仓库,比如我在南京下的订单,他可能就会从上海甚至直接从南京发货;如果是在洛阳下单可能就会从郑州发货。这样做的好处就是不管我们在南京,还是乌鲁木齐,我们的收货时间会大大减少。CDN 就类似于京东建立的这种仓储系统。 从网购到 CDN 不知道上面的描述是否清楚,这里为了加深理解,我制作了下面的流程对比图: 为了让货物更快的送到买家手中,京东建立了这种仓储系统;类比到网络中,为了让用户更快地加载网页(可以理解为服务器给浏览器送页面),CDN 横空出世了。 CDN 的全称是 Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN 系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上(如下图所示)。其目的是使用户可就近取得所需内容,解决 Internet 网络拥挤的状况,提高用户访问网站的响应速度。 从“直播”理解一些和 CDN 有关的名词 从上面的描述中我们得知了 CDN 的作用以及大概原理,但是其中的细节并没有展开来说。其实 CDN 的一些细节通常会和一些名词联系上,例如负载均衡、源站之类的。同样的,我们以一个身边的例子——“直播”——来讲解这些和 CDN 有关的名词。 我们知道,视频其实是由一帧一帧的图片组成的,所以直播的时候我们收到的视频画面的流程可以近似理解为下面这样 👇 然而事实是这样的吗?当然不是!一个主播怎么可能只有一个观众,所以应该是下面这样 👇...

plainify

科普系列——如何解释什么是 AJAX?

前言 学妹这学期新开了一门课《Script 及 AJAX 开发技术》,然而临近学期末,她突然跑来问我:到底什么是 AJAX ?相信很多人(尤其是前端)在写代码的时候经常会用到 AJAX 技术,但是如果真要说出个所以然,可能还会有些困难。其实简单概括下,AJAX 就是一种利用 JavaScript 向服务端发起请求,并获得服务端响应的技术。它的特点是异步请求,局部刷新。 Tips:这里我将技术二字加粗了,是因为很多初学者会以为 AJAX 是一个库/框架,类似于JQuery/Vue之类的,因而有很多初学者会提出该怎么安装 AJAX 的问题。事实上 AJAX 是一种技术。 虽然概括起来很简单,但是 AJAX 技术的一些细节仍然值得我们思考,接下来我会详细的介绍。 AJAX 解决的问题 我们刚才说过了,AJAX 是一种发送请求的技术,那在 AJAX 被发明前,浏览器是如何请求的呢? 地址栏。用户在地址栏输入 http://baidu.com ,按回车,就向 http://baidu.com 发起了一个请求。(同时页面刷新) a 标签。用户点击页面中的 a 链接,也会发起一个请求。(同时页面刷新) img 标签。页面中如果有 img 标签,那么就会发起一个对此图片的请求(页面没有刷新,但是只能请求图片)类似的还有 link 标签、script 标签,都可以对一类文件的请求。 在这三种方式中,除了第三种,其他两种方式想要发送一个请求,就必须要刷新页面,如果页面只有展示内容的话刷新一下自然无所谓,但倘若一个页面有很多的表单内容需要填写,而你在最后填写完成提交的时候才告诉你,其中某一个地方不符合要求,要你回去重填,然后刷新一下页面,内容都消失了,怕是当时就可能会气的暴走了吧。 也正是这种极端的用户体验让微软创新地开发了一个接口 ActiveXObject(“Microsoft.XMLHTTP”),并在 IE 5.0 中开放给开发者用。通过该接口,浏览器可以向服务器发送请求并取回所需的数据,并在客户端采用 JavaScript 处理来自服务器的回应。这就是 AJAX 的前身。随后这种技术被谷歌的开发人员发现并运用在 Gmail 中,再然后就是 W3C 制定了一个标准用来规范 AJAX,至此 AJAX 算是正式成为每一个前端开发者的必备技能了。 通过 AJAX 技术,服务器和浏览器之间交换的数据大量减少,服务器回应更快了。同时,很多的处理工作可以在发出请求的客户端机器上完成,因此服务端的负荷也减少了许多。 AJAX 的原理 那 AJAX 的实现原理又是什么呢?我们先来看一下 AJAX 的定义,以下内容摘自维基百科:...

plainify

如何向女朋友解释在地址栏中输入网址后发生了什么?

前几天有个学妹问我为什么在浏览器里面输了网址就会显示出来页面,虽然这个现象很常见,但是要想解释清楚确实有些小困难,当时也只是简单的回答了她,现在想趁着这个机会好好整理下相关知识。整理完才觉得其实就和我们去一个地方找人是一个道理。所以说艺术源于生活却又高于生活,技术同样如此。 在回答这个问题前, 我们先来了解下我们平常说的那个网址到底是啥? 网址的学名叫做统一资源定位符(Uniform Resource Locator, 常缩写为URL), 我们知道现在的互联网其实就是由众多资源所构成的一张巨大的网, 如何定位那些资源就是靠的 URL, 因此我们也可以把 URL 理解为是网络上资源的“门牌号“, 我们在浏览器中输入网址, 就相当于开一辆车(浏览器)去找一个地址(URL) 1. 缓存查找 如果你要出门找一个地方, 第一想法肯定是先想这个地方你有没有去过, 你要是去过的话那就不需要问人直接过去就好了。 我们的系统也是这么想的。 当你在浏览器中输入了 URL 之后, 浏览器会先查看 浏览器缓存 中有没有这个地址, 如果没有那就再去 系统缓存, 如果系统缓存还没有, 那就去路由器缓存找, 总之只要缓存中有, 就说明有这个资源, 那浏览器直接显示出来就好了。 Tips: 这里说下 hosts 文件 , hosts 是一个没有扩展名的系统文件, 可以用记事本等工具打开, 其作用就是将一些常用的网址域名与其对应的 IP 地址建立一个关联“数据库”, 当用户在浏览器中输入一个需要登录的网址时, 系统会首先自动从 hosts 文件中寻找对应的 IP 地址,一旦找到, 系统会立即打开对应网页, 如果没有找到, 则系统会再将网址提交 DNS 域名解析服务器进行 IP 地址的解析。 需要注意的是, hosts 文件配置的映射是静态的, 如果网络上的计算机更改了请及时更新 IP 地址, 否则将不能访问。 2. DNS 解析 如果你认得去那个地址的路自然是最好, 那如果你根本就没去过那咋办? 肯定会有人说导航, 但并不是所有的地方都是导航能搜到的, 这个时候我们自然而然就会想着去问路人了。 浏览器也是这样的, 如果在本地缓存中没有找到想要的资源, 那就只能去其他网络上的机器中寻找我想要的资源了。 那你怎么知道你要的资源在那台机器上? 这时, DNS 就横空出世了。...

今天我们来种一棵树

Image by pangziai on Pixabay 今天是一年一度的植树节,说到树,我就想到了《西游记》中的古树精,今年下半年。。。好了好了,不皮了,我们直接开花。今天就趁着植树节来种一棵我们程序员的“树”吧。 什么是“树”? 在种树之前,我们先来了解下什么是树?看个例子: 不对不对,放错了,应该是下面这个: 维基百科对于树的定义是: 在计算机科学中,树(英语:tree)是一种抽象数据类型(ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由 n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 说白了,只要是形如上图的数据结构就叫树。 二叉树 自然界中有“迎客松”、“轩辕柏”这样出名的树,我们今天要种的树也是我们 IT 圈里面的扛把子——“二叉树”。 二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构之一。 二叉树的特点是每个结点最多有两个子树,左边的叫做左子树,右边的叫做右子树,二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。 下面这棵树就是一棵二叉树。 常见术语 结点(node):上图中的 A、B、C。。。就是一个结点 结点的度:一个结点含有的子树的个数称为该结点的度 树的度:一棵树中,最大的结点的度称为树的度 深度:对于任意节点 n , n 的深度为从根到 n 的唯一路径长,根的深度为 0; 高度:对于任意节点 n , n 的高度为从 n 到一片树叶的最长路径长,所有树叶的高度为 0; 种树 接下来的代码均用 Java 展现,完整代码可在公众号「01 二进制」后台回复“二叉树”查看。 现在我们就开始种一棵如上图的树吧。根据定义,我们了解到,结点是一棵二叉树最重要的元素,而作为一个结点,必须满足以下条件: 根结点 左子树和右子树 因此我们可以创建一个结点类(TreeNode): class TreeNode { String data; TreeNode left; TreeNode right; TreeNode(String data) { this....