后端好书阅读与推荐(续四)

默认分类 2018-03-05 访问: 505 次

后端好书阅读与推荐系列文章:
后端好书阅读与推荐
后端好书阅读与推荐(续)
后端好书阅读与推荐(续二)
后端好书阅读与推荐(续三)
后端好书阅读与推荐(续四)

这里依然记录一下每本书的亮点与自己读书心得和体会,分享并求拍砖。

Docker生产环境实践指南

Docker生产环境实践指南 (豆瓣) https://book.douban.com/subject/26825958/

前面docker的基本概念和一些核心原理都看的差不多了,那么现在该关注一下具体的生产环境的使用方法了。

亮点:

  • 在生产环境中运行docker与在其它环境中相比,最主要的差异是需要在其安全性与稳定性上投入更多的注意力
  • 书中提到docker 容器与宿主机是通过IPtables实现的nat转换来进行通信,这点官网有说明 Docker container networking,然后就说了不适宜网络吞吐量有很高要求的应用(但是可以禁用Docker的NAT来提升网络性能),但是docker已经做出了一些努力,效果并不差,见:1,2,3
  • 书中对于docker相关常见的概念解释得非常清晰易懂,这一部分尤其适合初学者
  • docker生产环境最好的方式是将应用程序及其依赖预先打包成一个镜像,而包含数据库凭证等铭感信息在运行时动态添加(安全起见);常见流程是开发机上打包并推送到仓库,然后服务器从仓库拉取镜像,这种用例简单但是从工作流和安全角度看并不理想,更标准的做法是使用持续集成 / 持续交付系统在应用程序代码或者dockerfile发生变化时自动从新构建镜像
  • 实现开发速度和生产环境稳定的方法之一是拥抱简单,亦即系统的每一个部分——容器——有且只有一个目标。系统的简单需要设计时遵照如下原则:倾向无状态服务,倾向静态配置,倾向静态的网络布局,区别对待有状态和无状态的服务
  • 保持镜像小巧:可以从空的文件系统开始构建,从类似于busybox、aipine这种轻量级操作系统开始构建,如果嫌包少就可以从主流Linux发行版的容器优化版开始构建,最明智的做法是标准化一个特定的镜像版本,并尽可能的用于所有容器;尽可能减少层数,也就是相关的命令尽量放到一行,而且下载的内容用完后要在同一行删除;可以使用docker-squash来减小镜像体积
  • 存储镜像:开源、公共项目建议存共有仓库;安全性和性能要求较高的存私有仓库;需要定制的使用save / load。此外,很多人可能不知道,DockerHub其实是提供了自动构建功能的,只要通过WebHook连上Github就行(亲测)
  • 一致性是扩展环境和传播知识的关键,所以应该在一个构建系统中构建所有镜像,而且要实现镜像标准化,让所有镜像(尽可能)继承于一个标准基础镜像
  • AUFS引擎的挂载速度非常快,能很快的创建新容器所以成为了Docker存储引擎的默认解决方案。其性能瓶颈主要在需要写入大文件的场景,因此使用AUFS来存放数据库文件可能不是一个好主意,同样太多的镜像层可能导致文件查找时间过长,所以不要让自己的文件有太多分层。AUFS有一个最大的问题是没有被容纳到Linux主流发行版。事实上,在生产环境选择存储引擎,一个要点是看业务与特性是否吻合,另一个要点就是选最有把握能稳定运维的工具
  • Docker网络实现包含三方面内容,IP分配、域名解析、容器或服务发现,这也是零配置网络的理论基础。零配置网络即是一组在没有人工干预的情况下自动创建和配置一个TCP/IP网络的技术。
  • Docker集群之中的服务发现,目前是consul做得最好,可以自定义健康检查机制,不像zookeeper(和TCP会话周期绑定)和etcd(和TTL值挂钩),提供了一个非常完备的服务发现解决方案

本书名副其实,对于在生产环境中使用docker有不错的方向指导意义,当然细节还得自己扣啊。
注意:由于时间原因,书中有些内容有些过时但是译者给出了注解(但是译者的注解也可能过时呀,所以一定要自己注意跟踪docker的最新变化)

The Go Programming Language

The Go Programming Language (豆瓣) https://book.douban.com/subject/26337545/

为什么要了解一下 golang 呢?最明显的原因就是,这个语言有一款杀手级应用,也就是前面说过很多次的:Docker;而且前一段时间 nodejs 的创始人不是要拥抱 go 语言了吗,作为一个编程语言的创始人转投另一门语言,还是很值得了解一下的;最后,对 go 的协程是早有耳闻,传言可以用同步的方式写出异步的代码,轻松实现高并发......

亮点:

  • 正如Rob Pike所说,“软件的复杂性是乘法级相关的”,通过增加一个部分的复杂性来修复问题通常将慢慢地增加其他部分的复杂性。通过增加功能和选项和配置是修复问题的最快的途径,但是这很容易让人忘记简洁的内涵,即使从长远来看,简洁依然是好软件的关键因素。秉承着简洁原则,golang没有内置大多数语言都有的隐式转换、宏、异常、继承等等
  • 依然是简洁原则的贯彻,golang 不允许 import 没有使用的包,也不能定义没有使用的变量,否则连编译都不能通过。还有左大括号必须和代码在同一行......感觉这些强制要求非常有用,多一些标准,少一些歧义,这应该是利于工程化的
  • break 和 continue 都可以加label来实现跳出任一循环,这个有点像 goto 语句,这种语句我们要少用,一般是在编译器生成代码过程使用的
  • 并不是所有的词法域都显式地对应到由花括弧包含的语句;还有一些隐含的规则。比如for语句创建了两个词法域:花括弧包含的是显式的部分是for的循环体部分词法域,另外一个隐式的部分则是循环的初始化部分,比如用于迭代变量i的初始化。隐式的词法域部分的作用域还包含条件测试部分和循环后的迭代部分(i++),当然也包含循环体词法域
  • 结构体中的匿名成员是一个比较有意思的特点,可以用来无缝的实现“对象组合”这一需求,组合是 go 实现面向对象编程的核心
  • 大部分语言使用固定大小的函数调用栈,常见的大小从64KB到2MB不等。固定大小栈会限制递归的深度,当你用递归处理大量数据时,需要避免栈溢出;除此之外,还会导致安全性问题。与相反,Go语言使用可变栈,栈的大小按需增加(初始时很小)。这使得我们使用递归时不必考虑溢出和安全问题
  • 在Go中,错误处理有一套独特的编码风格。检查某个子函数是否失败后,我们通常将处理失败的逻辑代码放在处理成功的代码之前。如果某个错误会导致函数返回,那么成功时的逻辑代码不应放在else语句块中,而应直接放在函数体中。Go中大部分函数的代码结构几乎相同,首先是一系列的初始检查,防止错误发生,之后是函数的实际逻辑
  • 当defer语句被执行时,跟在defer后面的函数会被延迟执行。直到包含该defer语句的函数执行完毕时,defer后的函数才会被执行,不论包含defer语句的函数是通过return正常结束,还是由于panic导致的异常结束。deferred类似于java的finally,可用于保证资源回收、锁释放等
  • 看到第八章终于知道传言的大概意思了,只需要在原来同步的调用函数的代码之前加一个 go 关键字,就能新建一个goroutine(可以类比线程,但是有不同之处),然后主线程就能继续干活而不必等待函数执行结束了
  • 线程和goroutine的区别主要有几方面:goroutine的栈不是固定大小的,一般从2KB开始动态变化,所以可以打开几百上千的goroutine,而线程是固定栈大小,不可能开很多;goroutine由Go调度器进行调度,而不是操作系统,采用m:n模型(最多n个线程执行m个goroutine),所以goroutine的切换代价较小
  • 虽然及时修改了一个源文件也要重新编译该文件对应的包及其依赖包,但是go的编译速度非常快,主要有三点原因:所有导入包开头显示声明,快速找到;禁止环装依赖,可以并发编译;编译后的包记录了包的依赖关系,编译器不需要遍历所有依赖文件只需读取直接导入包的目标文件
  • 程序的复杂性是可以控制的,其中两种技术在实践中被证明很有效:软件正式部署前的代码评审;自动化测试(写一些小的程序用来检测被测试代码的行为和预期的一样)。go test 命令是一个按照一定的约定和组织的测试代码的驱动程序,在*_test.go文件中:测试函数以Test为函数名前缀,用于测试程序的一些逻辑行为,go test命令会调用这些测试函数并报告PASS或FAIL;基准测试函数以Benchmark为函数名前缀,用于衡量函数的性能,go test命令会多次运行基准函数以计算一个平均的执行时间;示例函数以Example为函数名前缀,提供一个由编译器保证正确性的示例文档

本书名不虚传,对go的基础知识介绍的很详尽,底层原理也稍微有一些涉及,同时还有一些高级的话题比如各种并发、测试等,是一本入门go的好书,看完后对golang就有一个几乎完整的概念了。

PS:gitbook有对应中文版。

从PAXOS到ZOOKEEPER分布式一致性原理与实践

从Paxos到Zookeeper (豆瓣) https://book.douban.com/subject/26292004/

如今搞分布式的似乎都离不开Zookeeper了,Zookeeper一般是作为分布式的基础设施,还是很有必要了解一下的。本书阐释了从集中式到分布式的问题,如何解决,层层递进引入了Zookeeper,然后介绍了应用场景和技术内幕,算是一个非常全面的介绍了,而且作者的思路非常清晰,我们读起来很流畅,但是书中涉及的一些协议本身是比较复杂的,读起来有些麻烦。

亮点:

  • 集中式的系统保证事务的 ACID 相对而言是比较容易的,而且有很多成熟的解决方案。但是根据 CAP 理论:“一个分布式系统系统不可能同时满足一致性、可用性、和分区容错性,最多只能满足两样”,所以分布式系统不大可能严格满足 ACID 的强一致性,而分区容错性 P 是分布式系统本身存在的意义,所以一般设计者就在 A 和 C 之间寻求平衡了,根据 BASE 理论,一般分布式系统都实现以实现最终一致性、保证高可用性为目标。BASE理论不仅应用于分布式系统,也广泛应用于现代关系数据库
  • 数据的一致性与可用性之间的反复权衡产生了 一系列的一致性协议,最经典莫过于二阶段三阶段提交协议和Paxos算法了。这三者层层递进,分别解决了前者的一些问题,并各自在不同的领域被使用
  • Zookeeper是一个典型的分布式数据一致性解决方案(工业级产品),致力于提供一个高性能(高吞吐量)、高可用(解决单点问题)、具有严格顺序访问(主要是写)控制能力(实现复杂同步原语)的分布式协调服务。其设计目标:简单的数据模型(树型、共享、内存)、可以构建集群、顺序访问(请求有唯一递增编号)、高性能,可以保证许多一致性特性:顺序一致性、原子性、单一视图、可靠性、实时性。分布式应用程序可以基于它实现数据发布订阅、负载均衡、命名服务、分布式协调通知、集群管理、Master选举、分布式锁和分布式队列等功能
  • 作者对ZooKeeper的常见应用场景进行了详尽的描述,非常有借鉴意义,比如:client利用节点创建的API可以创建一个顺序节点,再加上其type就形成了一个全局唯一的ID了,从而实现命名服务;不同机器通过在同一节点创建临时子节点,并根据其他机器的临时子节点来判断对应的机器是否存活,从而实现心跳检测;通过强一致性的唯一节点保证来实现master选举(谁能建立特定节点谁就是master,其他机器注册watcher,一旦当前master挂了节点删除,就又开始新的选举);通过节点唯一性来实现排它锁、子节点排序来实现共享锁......总而言之,可以在ZooKeeper子节点唯一性上面做很多文章
  • 具体使用案例,Canal这款基于Mysql BinLog的增量订阅消费组件使用ZooKeeper来实现Canal Server的主备切换功能,主要原理就是临时节点和节点唯一性;Canal Client利用ZooKeeper来时刻关注Canal Server的变化,进行消费并记录消费点
  • 数据模型:由ZNode(称为节点,类型有持久、临时、顺序节点,可组合)层次化组织而构成的树,路径类似于Unix文件系统,每个ZNode可以存数据、创建子节点(临时节点无子节点);采用版本号以及CAS来实现乐观锁机制;采用ACL来实现细粒度的权限管理;采用一次性、客户端回调串行执行、推拉结合的轻量级watcher来实现时间发布订阅
  • Leader选举的原理简单说来就是谁的数据越新,那么其ZXID(事务ID)越大,就越能够保证数据的恢复,就越能成为Leader,对于相同的ZXID,那么SID较大的成为Leader,这个应该没啥特别的原因,只是作为一种确定机制。
  • Leader是集群中的核心,主要工作:事务请求的唯一调度者和处理者,保证集群事务处理的顺序性;集群内部各个服务的调度者。Follower是集群状态跟随者,主要工作:处理客户端非事务请求,转发事务请求给Leader;参与事务请求Proposal的投票;参与Leader选举。Observer观察集群最新变化状态并同步过来,工作类似于Follower,只是不参与任何投票,通常用于在不影响集群事务处理的情况下提升集群的非事务处理能力

看完全书再去看看应用案例可能会产生更好的理解。

现代操作系统(原书第4版)

现代操作系统(原书第4版) (豆瓣) https://book.douban.com/subject/27096665/

操作系统是我们一切编程的根基,不了解操作系统就只是“粘贴复制员”而不是“程序员”更别提“工程师”了。这本书既讲到了传统的重点概念:内存、进程、文件、安全等,又讲到了现在广泛应用的虚拟化、云、Linux、Windows等,无愧于“现代操作系统”的名称。

亮点:

  • 抽象是管理复杂性的一个关键,好的抽象可以把一个几乎不可能管理的任务划分为两个可管理的部分,其一是抽象的定义与实现,其二是用这些抽象解决问题。操作系统就是把硬件丑陋的接口转换为一个良好、清晰、优雅、一致的抽象。最为人熟知的抽象就是“文件”,它统一了各种格式的数据、各种IO设备对于用户的接口(操作系统3大抽象:文件,进程与线程,地址空间,分别对应磁盘,处理器,内存)。可以把操作系统理解为一个资源管理者,管理硬件资源如何分配给应用程序,让应用程序更好地使用这些资源
  • 技术的变化会导致某些思想迅速过时,但是也可能使得另一种是思想复活,当技术的变化影响了系统内部不同组件的相对性能之时就更是如此。比如早期计算机指令由硬件直接执行,微程序设计出现后采用解释执行,RISC又采用了直接执行,java又采用了解释执行,这样来回摆动主要是因为执行速度不总是关键因素,还有可能是网络延迟。再比如计算服务过时了,但是又以云计算的形式重新流行,所以对待技术要保持客观、辩证的态度,不要因为现在看起来过时就对它唾弃,说不定将来它又能流行起来发挥大的作用
  • 有了进程还要线程的原因:逻辑上,一个应用中本身可以划分为多个活动,一个活动一个线程便于理解;线程更轻量级,容易创建、撤销;若存在大量IO处理,多线程彼此重叠进行是可以提升吞吐量的(若每个线程都是CPU密集型则不能);多线程可以真正利用多核。
  • 都知道java有偏向锁、轻量级锁、重量级锁的升级,实际上操作系统也有类似锁的概念,而且Linux还采取了“Futex”这个方案来结合自旋锁和重量级锁(阻塞)的优点。一个Futex包含两部分:一个内核服务和一个用户库,简单说来就是在用户态创建一个futex同步变量,当有进程要获得锁时,对futex执行"down"操作即原子性的给futex同步变量减1,如果成功即可继续不需进入内核态,否则进入内核态阻塞,这样就大大减少了低竞争时的吞吐量
  • 通过交换技术,系统可以同时运行超过实际内存大小的多个进程;现代计算机都使用某种虚拟内存技术,每个进程地址被分为同样大小的页面(通常4、8KB),可以被放入空闲内存的任何页框内,有多种页面置换算法,实际应用中用的最多的是老化算法和工作集时钟算法;如果在执行过程中有大小变化的数据结构可以采用分段的方法,但是几乎没有主流的操作系统考虑分段算法
  • 文件系统的存储区分配方案有连续文件、链表、文件分配表和iNode。磁盘空间可以通过位图的空闲表来管理。提升文件系统性能的做法有高速缓存、预读取、以及尽可能将一个文件的块放在一起等方法
  • 操作系统除了负责提供抽象还要负责管理所有IO,IO主要有三种方式实现:程序控制IO、中断驱动IO、DMA
  • 死锁发生有四个条件:互斥条件、占有和等待条件、不可抢占条件、环路等待;解决办法有四个:鸵鸟对策、死锁检测与恢复、合理资源分配动态避免死锁、通过破坏引起死锁发生的四个必要条件之一来防止死锁发生
  • 虚拟化的好处:可以在节省硬件与电力资源的情况下实现强隔离性、使得各个应用程序很容易拥有自己的运行环境、设置检查点和虚拟机迁移比在普通操作系统中容易的多,目前虚拟化最重要的用途就是
  • 编写操作系统不容易,那么从何入手呢?从对外接口开始,三大原则:简单,不是当没什么东西可以添加而是当没什么东西可以减少时才能达到尽善尽美,或者说KISS原则;完备,完事应该简单,但是不能过于简单,必须要能完成用户所需的一切事情;效率,如果功能不能有效实现那就不值得拥有这个功能。这些原则对于我们所有的系统设计都有借鉴意义

这本书的字真是太多了,也太小了,吐槽一下印刷,此外,内容也很细很全,还是应该采取先观其大略、使用时细读的策略。

大规模分布式存储系统

大规模分布式存储系统 (豆瓣): https://book.douban.com/subject/25723658/

看了许多分布式概念性的书籍,也了解了java中间件、分布式一致性、ZooKeeper作为基础设施等等概念,是时候找个具体的方向了解分布式了,那么这本书就是分布式在存储这一块的具体实战了。本书从概念到实战,讲了分布式文件、键值系统、表格系统、数据库等等,几乎覆盖了所有涉及存储的方向,看完就能对分布式存储有一个较为完整的了解了。

亮点:

  • 分布式存储有几个特性:可扩展(容易扩展到几百几千台集群规模,且性能随数量线性增长)、低成本(构建于普通PC机之上)、高性能、易用(提供易用的对外接口);主要挑战在于数据分布均匀、数据一致性、容错性、负载均衡、事务并发与控制、易用性、压缩解压缩;数据分为非结构化数据(图、文)、结构化数据(关系数据库)、半结构化数据(HTML);不同的存储系统适合不同的数据类型,分布式文件系统主要存储Blob对象(文档、图片、视频)等非结构化数据和作为其他分布式系统的基础,分布式键值系统存储简单的半结构化数据,分布式表格存储复杂的半结构化数据,分布式数据库存储结构化数据
  • 设计网络系统的基本原则是:网络永远是不可靠的,任何一个消息只有收到对方回复后才可认为发送成功,系统设计时总是假设网络将会出现异常并采取相应处理措施(但是我们必须假设信道是可靠的,不然任何工作于其之上的任何协议都不能是可靠的)
  • 分布式系统在CAP理论的C和A之间权衡时可以参照Oracle的DataGuard复制组件的三种模式:最大保护模式(亦即强同步模式,主库先将操作日志同步到至少一个备库才能返回给客户端成功结果),应用于余额等关键记录;最大性能模式(亦即异步复制,主库完成执行就返回客户端,将重做日志以异步的方式复制到备库),应用于用户操作日志等非关键记录;最大可用性模式(上述两种模式的折中,正常情况相当于最大保护模式,当主备之间的网络出现故障切换为最大性能模式),应用于一般记录
  • 故障检测可以通过心跳检测来做,这是最原始的想法,但是机器也可能因为太忙,或者与控制节点的网络断掉而无法进行心跳,这种情况下使用时钟同步(需要考虑一个时间提前量,因为时钟并不能严格一致一般都会有小于1秒的微小误差)加租约(Lease)的形式可以较好地避免这个问题
  • 跨机房部署有三种方案:集群整体切换(实际最常见做法,两个机房保持独立,保持相同的副本数,有主备之分(Primary,Secondary),可能强同步或者异步复制),单个集群跨机房(不同数据分片的主副本位于不同机房)、Paxos选主副本(每个数据分片的多个副本构成一个Paxos复制组,自动选举主副本,降低了对总控节点的依赖但是工程复杂度很高)。
  • GFS成功的经验表明:单Master的设计是可行的,不仅简化了系统,而且能较好地实现一致性。这是一种中心化的架构,是目前互联网的主流架构,而去中心化的Gossip协议虽然借着Cassandra(Amazon的Dynamo的开源实现)大火了一把,但是由于其复杂性与一致性问题实际上分布式系统很少使用
  • OceanBase是一款可扩展的关系型数据库(支持强一致性和跨表事务),主要架构分内四部分:RootServer一主一备,主备强同步,负责集群管理,数据分布以及副本管理;UpdateServer一主一备,主备同步模式可配置,接受写操作,并且定期把新数据同步给Chunkserver;ChunkServer存储基线数据,只提供读取服务,并定期接受UpdateServer的数据同步;MergeServer解析用户Mysql兼容请求,将请求转发给对应的ChunkServer和UpdateServer,无状态,类似于网关的作用
  • OceanBase写操作强一致性的原理是:UpdateServer将redo日志发送给备机,redo日志写到本地磁盘,redo日志应用到主机内存表,返回客户端成功。这样即使主备机切换也能保证新的主机有以前所有的操作记录而不会丢失,可以通过增加备机数量来提高可用性保证。当然UpdateServer主备同步也支持异步模式,支持最终一致性,一般用来实现异地容灾。此外主备集群也可以实现错峰合并
  • OceanBase借助UpdateServer逻辑单点来实现强一致性,那么这个单点会不会成为瓶颈呢?一般来说,互联网业务读写比都比较高,所以不会成为瓶颈,即使是双十一这种,也可以通过自动冻结内存表转入SSD硬盘、定期合并与分发、旁路导入、多块网卡配置、RAID卡缓存模块、成组提交等优化方式来避免内存、网络、磁盘的瓶颈;通过主备强一致备份策略与实时同步避免单点故障,这样这个单点问题就几乎不存在了
  • 列式存储的主要目的有两个:大部分OLAP只需要读取部分列而不是全部列数据,列式存储可以避免读取无用数据;将同一列的数据在物理上存放在一起,能够极大的提高数据压缩率
  • 大表左连接是互联网的一个常见问题,一般采取引用(多表主键连接)、或者嵌套(主表冗余连表信息),但是这分别只能适应读取次数少、读写比较高的场景,如果读取次数多且读写比不高那么这两种做法就没辙了。OceanBase采用基线数据冗余连表+修改增量合并的方式解决了这个问题

看完这本书是真的很有收获,对整个分布式架构有了一个完整的了解,从大大的跨机房到一个小小的数据分片副本,从上到下非常清晰。此外最佳实践那一部分也是很赞的,强烈推荐此书。

微服务设计

微服务设计 (豆瓣): https://book.douban.com/subject/26772677/

微服务最近两年越来越火,其实这并不是什么新鲜概念,计算机体系里面早就有单内核与微内核的架构思想了,微服务也是一种类似的思想,但是是处于更高级别的应用架构层(所以计算机领域里面的许多思想是可以相互借鉴的比如抽象、缓存、LRU算法、并行处理等等)。微服务的好处是很多的,单个服务容易开发、理解和维护;容纳异构技术;部署简单、迭代速度快、变化小bug少;弹性、扩展性好等等。本书就带领我们从建模、集成、测试到部署和监控,全面了解微服务,为进一步学习指引方向。

亮点:

  • 微服务是一种分布式系统解决方案,推动细粒度服务的使用,这些小而自治的服务协同工作,都有自己的生命周期,围绕业务领域建模,根据业务的边界来确定服务的边界,因此也很容易确定某个功能代码的位置,避免了传统分层架构的许多问题。一个微服务就是一个实体,不同微服务通过网络通信互相调用。我们知道,直接进程内方法调用是很快,但是也意味着两个对象紧密耦合了,通过网络通信调用虽然慢一些,但是两个对象就松耦合了,可以随时替换掉一个而另一个完全不用知道,我觉得随着网速越来越快,网络通信代价越来越小,微服务的优势将越来越明显,直接耦合调用的方式的优势将越来越弱
  • 架构师就类似于城市规划师应该专注于大方向,只能参与少量细节,需要保证系统能满足当前的需求,还要能够应对将来的变化,以一种演化的方式来进行规划,这是一个很大的标准,从何入手呢?答案是:分区,定好服务边界与交互方式,可以使用限界上下文。演化的方式就是我们要理解,随着项目发展,服务一定会越来越大,我们要做的就是让架构增量变化,在拆分这件事变得过分昂贵之前进行拆分
  • 重用代码可能引入危险,因为我们可能引入服务之间的耦合。所以虽然我们要做到DRY原则,但是也要防止过度追求DRY原则带来的系统过度耦合,这是一个比较微妙的话题,要记住,DRY不是意味着避免重复的代码,而是意味着避免系统行为和知识的重复,在微服务内部不要违反DRY,跨服务时可以适当违反来避免服务之间耦合
  • 处理跨服务的业务流程时可以选择:编排,一个中心节点调度其他所有服务完成,这样流程很清晰,但是会导致中心节点承担过多,其它服务沦为贫血的基于CURD的服务;协同,相关服务订阅特定的事件,事件由客户端触发之后每个服务各自完成自己的处理,这样能明显消除耦合,但是就看不到明显的流程图了,而且可能需要跨服务监控,但是总体而言倾向于协同,利大于弊
  • 真正的持续集成要理解3个问题:是否每天签入代码到主线?是否有一组测试来验证修改?构件失败过后是否把修复CI当做第一要务?最好是每一个微服务都有自己的代码库和CI,只有这样才能真正实现独立部署
  • 微服务部署最好采用单主机(可以是物理主机、虚拟机、容器)单服务的方式,这样利于团队自治、服务部署、监控、故障排查、扩展性更好等,但是这个模式会引入大量主机,这就意味着重复劳动会多起来,所以我们一定要实现自动化(不管什么技术栈都要追求自动化)
  • 自动化测试类型分为单元测试、服务测试、用户测试(端到端测试)。其中单元测试是针对一个函数的、面向技术、帮助开发人员快速发现错误和增加重构信心;服务测试针对一系列函数构成的完整服务,提高测试隔离性,帮助快速定位问题;端到端测试包含整个系统,增强我们发布服务的信心。这三者的比例最好是前者比后者多一个数量级
  • 监控固然要看低层指标比如CPU利用率、空闲内存、带宽使用等等,但是这些指标往往太多而带来噪声不利于监控我们真正关心的行为,所以需要语义监控,亦即准备一些合成数据和事件来观察系统是否按照我们的期望完成任务,当然这些数据要注意不能和真实生产数据混淆
  • 任何组织在设计一套系统时,所交付的方案在结构上都与该组织的沟通结构一致。这被称为康威定律
  • 构建分布式系统需要思维的转变,那就是故障总会出现,不是所有人都需要像Google或者Netflix一样使用ChaosMonkey来模拟故障测试系统的健壮性,但是我们都需要为各种故障做好准备比如:超时(设置默认超时并记录日志)、断路器(请求失败一定次数后断路器打开,下次快速失败就不用再等待了)、舱壁(服务内外都可以使用,关注点分离,为每个下游单独准备连接池)、隔离

PS:书中提到了不少参考书,都是可以看看的。

算法(第4版)

算法(第4版) (豆瓣) https://book.douban.com/subject/19952400/

本书讲解的算法和数据结构都是我们必须掌握的,属于入门级知识,但是作者讲的很仔细,很有趣,没有那么多的数学证明,比《算法导论》读起来跟容易一些,但是作者的观点也都是立得住的,有理有据,值得通看,形成自己对于算法的初步体系。

亮点:

  • 本书提供了所有代码还有教学视频,可以辅助学习,网址。我把代码fork了一份,在
  • 本书对算法性能的分析是科学式的,亦即先对性能提出假设,建立数学模型,然后用多种实验验证它们,必要时重复这个过程
  • API的目的是将调用和实现分离(模块化编程),可以将API看做调用和实现之间的一份契约,他详细说明了每个方法的作用,实现的目标就是遵守这份契约。读到这里就有一个感想,我们程序的进步都是在努力地把人为的契约固化到代码中,减少出错概率。比如程序员之间直接约定不够可靠,那么就通过接口来强制约定;再比如生产与开发环境不容易保持一致,就通过DockerFile来强制约定
  • 作者循循善诱,为了给我们分析算法性能提供动力,讲了一个3-sum问题的逐步优化过程:暴力的3-sum是一个立方级别的算法,先从简单的情况考虑2-sum,通过线性对数级别的排序,然后用对数级别的二分查找一个元素的相反数来进行统计,这个算法就是线性对数级别的,然后自然扩展到3-sum,得到平方对数级别的快速的3-sum并查集也是类似循序渐进的教法
  • 初级算法虽然简单,但是它帮我们建立了一些基本规则,展示了一些性能基准,在某些特殊情况下也是很好的选择,也是开发更强大的算法的基础,所以仍有必要系统的学习它们
  • 把排序算法讲了一遍之后,讲到了规约(为了解决某个问题而发明的算法可以解决另一个问题),比如排序就可以用了解决很多其他问题,一般能将暴力的平方级别解法下降到非暴力的线性对数级别,比如:找出重复元素、排名、优先队列、中位数、第K个最小/大的值(模仿快速排序的partition操作就能达到线性级别)等
  • 散列表的意义是在时间与空间之间取得一个平衡,选择适当大小的数组M,既不会因为空链表造成内存浪费也不会因为链表太长而导致查找效率低
  • 书中对于图的表述中,深度优先等遍历方式都给出了详细的算法与示例的轨迹图,你想看不懂都难
  • 就解决图的连通性问题,理论上来说,深度优先比union-find(联合查找或者说并查集)更快,但是实际上union-find更快,因为它不需要对图进行预处理,是一种动态的算法(能用接近常数的时间检查两点是否相通,甚至是添加边),而对于已经是图的数据类型使用深度优先更快,因为它能利用已知信息
  • 用于子串查找的KMP算法的基本思想是,当文本出现不匹配时就能知晓一部分文本内容,可以利用这些信息避免将指针会退到所有这些已知字符之前,亦即充分利用已知信息,这也是插入排序优于选择排序的原因。KMP主要亮点是提前判断如何重新开始查找,这种判断不需要文本指针i完全回退,只需根据模式本身信息重新进行比较位置的选择
  • 许多问题都可以规约为排序问题(中位数、不同值统计)、最短路径问题(任务调度、套汇)、最大流量问题(就业安置、网络可靠性)、线性规划(最大流量)
  • NP是所有搜索问题(有解且验证解的正确性的时间不会超过输入规模的多项式的问题)的集合;P是能够在多项式时间内解决的所有搜索问题的集合

本书的java基础部分是相当的丰富,学完算法也可以顺带复习一遍java了,很划算 (* ̄︶ ̄)。
还有,本书虽厚,但是一定要看完,这样才有一个完整的体系,并且算法的细节也能掌握了,之所以不采取往常对待大部头的观其大略的方法是因为这是算法,是经常需要我们实施的领域,和代码编写直接相关(不像操作系统的知识),所以必须掌握细节。可以参见我的另外几篇专门讲算法的博客

Hadoop权威指南

Hadoop权威指南(第3版) (豆瓣): https://book.douban.com/subject/26206050/

Hadoop的出现开启了大数据的序幕,到了现在已经成为一个比较完善的生态,服务于云计算与大数据。记得大三的时候学校有免费的云服务器,我申请了5台自己搭建了一个Hadoop集群跑了一下 Hello World 级别的 Word Count,然后就开始期末考试了,没有再继续深入了解,现在是时候全面的了解一下了,这本书号称权威指南,看它没错。

亮点:

  • MapReduce相对于其他系统的优势:比RDBMS更适合一次写入多次读取的任务,适合批处理任务,是一种线性可伸缩的编程模型;因为尽量做到数据本地存储,所以比网格计算(带宽限制,只适宜CPU密集型任务)更能处理巨量数据的任务,同时处理CPU密集型任务也不弱;比志愿计算(贡献CPU而非带宽)更可靠,更易实施,更好控制
  • HDFS是Hadoop的旗舰级文件系统,但是Hadoop也能集成其它文件系统如S3,HDFS设计要点:超大文件、流式数据访问、商用硬件(零售店可买)、低延迟访问不太适用(HBASE更佳)、大量的小文件、仅支持单用户写入且不能随机写
  • 分布式文件系统的块(chunk)抽象有许多好处:一个文件可以大于任意磁盘的容量(因为无需把一个文件存在一个磁盘上);抽象块简化了存储设计(块大小固定,默认64MB磁盘性能越好越大,故每块盘能存多少块是固定的,而且元数据也可以单独管理);适合数据备份,从而提高容错能力和可用性(每一块可以复制到多个机器上,默认是3个,如果块、磁盘或机器发生故障系统会从其他机器读取副本,此过程对用户透明。而且多个副本可以提高读取性能)
  • HDFS 有两种节点,一个namenode作为管理者,管理整个文件系统的命名空间,维护整个文件系统树及其文件与目录,同时也记录着每个文件中各个块所在的数据节点信息,namenode至关重要必须正常运行,所以提供了两种容错机制,实时同步元数据至其它地方和运行辅助namenode;多个datanode作为实际工作者,存储数据块供读写并定期向namenode发送存储的块的列表
  • YARN将JobTracker划分为多个职能的实体(资源管理器和应用管理器),改善了经典的MapReduce面临的扩展性问题,实际上比MapReduce更具一般性,MapReduce只是YARN应用的一种形式,有很多其他的YARN应用如分布式shell
  • MapReduce提供了计数器(统计无效数据)、排序、连接(join操作,将两个数据集合二为一)、边数据分布(配置额外的只读数据来完成作业)、MapReduce库类等常见功能
  • 部署hadoop时典型配置是同一个机架有一些节点,不同机架通过交换机连接起来的二级网络架构。因为机架内部通信更快所以一定要把这些配置告诉Hadoop系统,这样分配MapReduce任务时会倾向于机架内节点,节省传输成本,同时HDFS的文件块的3个副本也会尽量分布在两个机架上
  • Pig是一种探索大规模数据集的脚本语言,省去MapReduce繁琐耗时的步骤(写mapper、reducer,代码编译,打包,提交作业),仅需控制台的几行pig Latin代码就能处理TB级别的数据,而且支持更丰富的数据结构
  • Hive能把SQL查询转换为一些列运行在Hadoop上运行的MapReduce作业,它把数据组织为表,通过这种方式为HDFS中的数据赋予结构。Hive与传统数据库有很多类似之处(如SQL语言的支持,尤其类似于Mysql),但是其底层依赖于HDFS和MapReduce而非传统的文件系统,Hive采用读时模式(查询时进行模式检查)而非写时模式(写入时检查模式)可以使得数据加载非常迅速,因为他只是移动数据而不需要读取、解析、序列化等。写时模式有利于提升查询性能,因为可以建立索引、数据压缩等,Hive没有考虑过这些特性(以及事务、更新、删除等),因为MapReduce下,全表扫描是常态
  • HBASE是在HDFS上开发的面向列[族]的分布式数据库,如果需要实时的访问超大规模的数据集,就可以使用HBASE。与RDBMS相比,HBASE能解决很多伸缩性问题(表可以高达几十亿行,几百万列)、能水平分区并在上千个普通节点上自动复制、线性扩展与新节点自动处理。RDBMS对于中小规模应用提供的易用性、灵活性和完整的功能性是几乎无可取代的,但是要在数据规模和并发读中任意方面向上扩展就会损失很多性能,因为RDBMS是面向行的具有ACID和事务的强一致性等特性,向上扩展意味着要打破这些特性,使得管理变得复杂
  • ZooKeeper一般用来构建分布式服务具有许多特性:简单(核心是一个精简的文件系统)、丰富(提供分布式队列、锁、领导选举等功能)、高可用(运行于一组机器,避免单点故障)、松耦合交互(参与者不必互相了解)、资源库(提供通用协调模式的开源共享库)

本书作为了解Hadoop的设计思想以及关键点的手段,我尚未进入真正的使用,很多代码以及API部分就省略没看了,所以虽然是个大部头,但是看起来并不会特别慢。当然如果真的要使用,书中的接口和代码可能就已经过时了,要搭配最新官方文档来看

HTTP权威指南

HTTP权威指南 (豆瓣): https://book.douban.com/subject/10746113/

Http协议构成了当今互联网的基石,无论是浏览网页还是使用APP都离不开这个协议,非常有必要进行全面的了解。这本书也是一个权威指南,看完就能有一个完整的概念了,而且本书还讲到了许多架构方面的经验,只赚不赔哈哈。

亮点:

  • URI 统一资源标识符,在世界范围唯一标志并定位一个信息资源,包含URL和URN;URL 统一资源定位符,描述一台特定服务器上的某特定资源的位置;URN 统一资源名称,作为特定内容的唯一名称而与资源所在地无关(所以资源可以迁移到另一处而不影响其访问,URL就不行)。现在几乎所有URI都是URL,URN由于架构的缺乏(虽然引入了PURL)和巨大的工程变动仍处于试验阶段并未广泛使用
  • HTTP延时一般都是由TCP延时造成的(除非是客户端、服务端超载或者处理复杂的动态资源),如果要编写高性能HTTP程序就要考虑许多的TCP层面的问题与解决方案:TCP握手产生的延时对于传输少量的http报文而言是巨大的开销(复用已有TCP连接);延迟确认算法不太适用于HTTP这种双峰特征的请求响应式行为(调整或禁用这个算法);慢启动影响新的Http连接的传输速度(复用连接,持久连接);Nagle算法影响小的Http报文传输(可以禁用该算法,TCP_NODELAY);TIME_WAIT累计与端口耗尽,TIME_WAIT状态会持续2MSL(如120s)的时间才会转换到CLOSE状态,然后才能创建相同IP和端口的连接(服务器端口有限,如60000个),限制了服务器的连接速率(60000/120=500次每秒),可以增加客户端负载生成器的数量或者虚拟IP
  • 减少Http连接延时的方法:并行连接(并行执行多个Http事务),管道化连接(通过共享的TCP连接发起请求),复用连接(交替传送请求与响应报文),持久连接(由于站点本地性,http事务结束后不必马上释放TCP连接,因为很有可能马上又要向该站点发起请求)。其中持久连接与并行连接搭配可能是最高效的方式
  • web代理连接的是两个使用相同协议的应用,而网关连接的是两个或多个使用不同协议的端点。所以web代理扮演的是中间人传输者的角色(改善安全性、提高性能,比如完成访问控制、防火墙、匿名者、缓存、反向代理负载均衡、内容路由器、转码器等等),网关扮演的是协议转换器的角色(比如PHP-FPM能把Nginx转发的http请求解析出来然后调用php-cgi来注入参数并执行PHP代码得到结果然后封装为http协议回复给nginx)
  • 大规模web爬虫对其访问管理(避免重复)用到了一些技术:树和散列表(加速查找)、有损的位图(url映射成数字,url无限数字有线所以可能有冲突)、检查点(已访问URL存入本地)、分类(集群爬虫通过通讯来分配URL,各自爬取部分)、规范化URL、广度优先爬取、节流、限制url大小、黑名单、内容指纹、人工监视等等
  • 内容协商技术可以使得服务器把内容的不同版本发送给对应的用户,主要有3种机制:客户端驱动、服务器驱动、透明。
  • 重定向普遍存在,原因无非:可靠的执行Http事务(备用节点)、最小化时延(就近访问)、节约网络带宽(服务器分散,减少拥塞)。用到的主要技术有:Http重定向、DNS重定向、任播寻址、IP MAC转发、IP地址转发、显示浏览器配置、代理自动配置、web Proxy代理自动发现等等

这本书详细描述了HTTP协议的方方面面,而且给出了许多参考,如果我们真的要做一个完美的Http应用,还是值得我们细细翻阅的。

阅读原文



本文由 mageek 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。
此文章共有条评论, 人参与 |Powerd By Angboo