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

【译】一种有关艺术风格迁移的神经网络算法

在艺术领域, 尤其是绘画创作上, 人们已经掌握了一种可以创造独一无二视觉体验的能力, 那就是通过将一张图片的内容和风格之间构成某种复杂的关系。 到目前为止, 该过程的算法基础是未知的, 并且不存在具有类似能力的人工系统。 然而, 受到一种名为深度神经网络的视觉模型的启发, 在视觉感知的其他关键领域, 例如物体和人脸识别, 仿生学的效果已经可以接近人类的表现。 这里我们将会介绍一个基于深度神经网络的人工系统, 它可以生成具有高感知品质的艺术图片。 该系统使用神经表示来分离和重组任意图像的内容和风格, 提供了一种创建艺术图像的神经算法。 而且, 按照要去表现最优的人工神经网络和生物视觉中找到相同. 我们的工作提供了人类是怎样创作和认知艺术图像的算法理解。 此外, 鉴于性能优化的人工神经网络与生物视觉之间惊人的相似性, 我们的工作为算法理解人类如何创造和感知艺术形象提供了一条前进的道路。 处理图像任务最有效的深度神经网络是卷积神经网络。 卷积神经网络由小型计算单元层组成, 以前馈方式分层处理视觉信息(图 1)。 每层单元可以理解为图像过滤器的集合(a collection of image filters), 每个图像过滤器从输入图像中提取特定特征。 因此, 一个给定层的输出包括所谓的特征映射(feature maps): 它们是对输入的图像进行不同类型的过滤得到的。 当卷积神经网络被训练用于物体识别时, 会生成一个图像的表征(representations) , 随着处理层级的上升, 物体的信息越来越明确。 因此, 随着神经网络中的层级一级一级地被处理, 输入的图像会被转换成一种表征, 与图片的像素细节相比, 这种表征会越来越关注图片的实际内容。 通过对某一层的提取出来的 feaure map 的重塑, 我们可以直接看到该层包含的图片信息。 层级越高, 那么获取的图像中物体内容就越高质量, 并且没有确切的像素值的约束(层级越高, 像素丢失越多)。 相反, 在低层级中重塑的话, 其实像素丢失地很少。 所以我们参考的是神经网络高层的特征, 用它来作为图片内容的表征。 为了获取输入图像的风格表征, 我们用一个特征空间去捕获纹理的信息。 这个特征空间建立在每层神经网络的过滤响应之上(也就是上面提到的 feature map)。 在 feature map 的空间范围上(也就是同一层上的 feature map), 过滤响应各有不同(feature map 关注的特征不同), 而这个特征空间就是由这些差异构成。 通过对每一层 featute map 两两求相关性, 我们会获得一个静态的, 多尺度的图像表征, 这也就捕获到了图像的纹理信息, 但这纹理信息并非全局的。...

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....

【译】Effective TensorFlow Chapter13——在TensorFlow中利用learn API构建神经网络框架

本文翻译自: 《Building a neural network training framework with learn API》, 如有侵权请联系删除, 仅限于学术交流, 请勿商用。 如有谬误, 请联系指出。 为了简单起见, 在之前的大多数示例中, 我们都是手动创建一个会话(session), 并不关心保存和加载检查点, 但在实践中通常不是这样做的。 在这我推荐你使用 learn API 来进行会话管理和日志记录(session management and logging)。 我们使用 TensorFlow 提供了一个简单而实用的框架来训练神经网络。 在这一节中, 我们将解释这个框架是如何工作的。 当利用神经网络训练模型进行实验时, 通常需要分割训练集和测试集。 你需要利用训练集训练你的模型, 并在测试集中计算一些指标来评估模型的好坏。 你还需要将模型参数存储为一个检查点(checkpoint), 因为你需要可以随时停止并重启训练过程。 TensorFlow 的 learn API 旨在简化这项工作, 使我们能够专注于开发实际模型。 使用 tf.learn API 的最简单的方式是直接使用 tf.Estimator 对象。 你需要定义一个模型函数, 该模型函数包含一个损失函数(loss function)、 一个训练操作(train op)、 一个或一组预测, 以及一组可选的用于评估的度量操作: import tensorflow as tf def model_fn(features, labels, mode, params): predictions = ....