实战Java高并发程序设计
实战Java高并发程序设计 (豆瓣) https://book.douban.com/subject/26663605/
这本书是国产书籍里面质量较高的一本了(很多国产书都是东拼西凑或者敷衍了事),作者从实际出发,结合理论,讲的很有逻辑,而且还有很多形象的手绘,和那本Java并发编程实战相比更新一些,读起来也更容易一些。
亮点:
并行不一定是从性能角度考虑,有的时候是自然而然的,比如独立的任务分为不同的线程更易于理解
同步异步,阻塞非阻塞,并行并发,这几个概念估计很多人都混乱了很久,和作者的观点稍微有点出入,我这里总结一下:同步异步的区别关键在于A对B请求发出后,如果A要等待B的结果那就是同步,如果A直接返回不需要B的结果或者等待B完成后再通知就是异步;阻塞强调的是A请求B过后不能做其它事,只能干等着(自旋或者休眠),非阻塞指的是A在等B的过程中可以做其它事;并行是真正意义的的多个任务同时执行,并发可能是并行,也可能只是多个任务交替执行,但是切换的很快,给我们造成了并行的“假象”,真实的并行只可能出现在多核cpu上
死锁:是指两个或两个以上的或线程在执行过程中,因争夺资源而互相等待,都阻塞起来;活锁:是指线程A可以使用资源,但它让其他线程先使用资源,线程B也可以使用资源,但它也让其他线程先使用资源。这样线程虽然是活跃的,但是啥事也做不了;饥饿:是指如果线程T1占用了资源R,线程T2又请求R,于是T2等待。T3也请求资源R,当T1释放了R上的封锁后,系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后,系统又批准了T4的请求...T2一直等待。
并行对于效率的提升主要取决于业务中串行代码的比例和CPU数量,CPU数量越多,串行化代码比例越少,那么多线程的优化方式效果越好
JMM关注原子性(某个操作不能被中断),可见性(一个线程对某变量的修改对另一个线程立马可见),有序性(CPU为了性能可能指令重排,应用happen-before原则,能保证串行语义一致但是不一定保证多线程之间语义也一致)
类可以通过继承Thread,重写run方法来实现多线程,但考虑到Java是单继承的,继承是一种很宝贵的资源,所以类应该实现Runable接口并重写run方法,并把自己作为构造参数传给Thread类来实现多线程,这种做法也符合Effective Java里面提到的工作单元(Runable)与执行机制(Thread)分离的概念
加锁一定要注意加正确,主要有三种:指定加锁对象,获得该对象的锁才能进入同步区;作用于实例方法,锁加在当前实例上;作用于静态方法,锁加在当前类上。而且要注意多线程的锁一定要是真正的加在同一个对象上,比如
Integer i;
synchronized(i){
i++;
}
就不正确,因为 Integer 是不变类,在执行++运算的时候 实际上是创建了新类,所以那个同步块实际上指向了不同的对象,自然不会正确 。事实上这段代码 在 IDEA 里面他会提示你 synchronized 加在了非final变量上,都可能产生非预期结果。
java 内置的线程池非常强大:newSingleThreadExecutor,newFixedThreadPool,newCachedThreadPool,newScheduledThreadPool等等,其核心都是ThreadPoolExecutor实现的,不过是参数不同。而ThreadPoolExecutor又是可以高度定制的,threadFactory,handler 都可以自定义,实现自己的线程工厂,拒绝策略等。ThreadPoolExecutor可以扩展来实现更丰富的功能,例如监听所有任务的开始结束时间,避免线程池“吃掉异常”。
作者研究了ConcurrentLinkedQueue的源码,发现不用锁而是用CAS来实现多线程安全,代码复杂度高了很多,但是性能却高了很多;其实凡事有得必有失,就像Node一样,异步带来的性能提升是巨大的,但是代码的编写难度也提升了很多。此外作者还分析了java.util.concurrent里面其他的大量的数据结构,如CopyOnWriteArrayList,BlockingQueue,SkipList。这些数据结构都是Java设计者们精心设计的多线程安全数据结构,比传统的多线程安全数据结构性能提升很多
提高锁性能:减小锁持有时间,亦即只在必要的时候加锁;减小锁粒度,亦即缩小加锁范围,比如ConcurrentHashMap不对整个对象加锁而是一段;读写分离锁(ReentrantReadWriteLock)替换独占锁;读写锁的扩展,亦即按照功能分离不同功能的锁,使用多个ReentrantLock 来表达不同条件的锁,使之不互相影响,LinkedBlockingQueue就采用了这种思想;锁粗化,如果锁不停地获取释放反而浪费资源,所以把所有的锁整合成一个锁来提高性能,比如虚拟机就会实现这种操作。按我理解,如果锁里面的操作耗时,那么就要细化,给其他线程机会,提高吞吐量,反之就应该粗化,让自己早点执行完毕。
作者总结了一系列多线程相关的设计模式,比如单例模式、不变模式、生产者消费者、介绍了用框架实现的无锁生产者消费者、future模式实现的异步操作、并行流水线和搜索排序计算等等,这些都是我们业务中常常用到的模式,总结一下,以后运用更加自如
为了体会到本书的点,我写了一些代码。另外AKKA部分我没有看,因为没有用过,即使看了也体会不到精髓,所以暂时先不了解了。
计算机网络(第5版)
计算机网络(第5版) (豆瓣) https://book.douban.com/subject/10510747/
这本书当年大二的时候是教材,没意识到它的珍贵,本科毕业的时候居然拿去卖了,不过后来我又买了回来,好好的读下来,感觉对整个计算机网络都有了一个更清晰地概念。当然此书太厚,若非必要,不宜一一细读,挑些重要的基础概念(比如TCP/IP)和如今越来越重要的内容(比如CDN)细读,其余看个大概即可。
亮点:
首先讲了计算机网络的重要性(邮件,电子商务,web,在线娱乐游戏,GPS)以及广阔前景(IOT的万物互联,可穿戴计算机),事实上现在的人早就习惯了网络的存在,反而不觉得它有那么重要,就像空气一样,但是一旦你把它的作用列出来你就会发现,它是如此的重要,试着想想,如果没有网络,现代社会怎么运转?
为了降低网络设计的复杂性,绝大多数网络都组织成一个层次栈,每一层都构建于其下一层之上。这种分层的概念在计算机科学中广泛存在,比如面向对象、ADT、信息隐藏等,其根本思想都是提供服务但是隐藏实现。这种分而治之的思想可被我们借鉴,用来简化架构,逻辑解耦。比如 nginx 处理请求也是分了不同的阶段,Java 里面的 servlet 也是一样分阶段处理的
数据链路层只关注帧(frame)从一个节点到另一个节点的传输,是点到点的,而网络层的目的是采用数据报或者虚电路技术将数据包(package)从发送方传输至接收方,中间可能要经过很多点,是处理端到端数据传输的最底层,但仍然是点到点的(因为它必须关心发送方到接收方的中间节点),而传输层是真正的端到端,亦即发送接收方不必关心传输过程中有多少个节点,可以认为数据段(segment)是直接从发送方到接收方的。从另一方面来看,一个数据报在传输过程中,源/目的IP是不变的(除非NAT),但是源/目的MAC却是一跳一跳地变化的
拥塞控制和流量控制有很大差异。前者指确保网络能够承载所有到达的流量,是一个全局问题;后者指发送方不会持续以超过接收方能力的速率传输数据,解决这两个问题的最佳解法通常都是主机慢下来
网络层与传输层提供的服务有些类似,那么为什么不用一层做完呢?答案就是传输层代码主要运行在用户主机上,网络层主要运行在运营商的路由器上,对于网络层,用户自己有着真正的控制权,可以通过差错控制、顺序控制、流量控制等方式来获得比网络层(尽力而为的服务,无保证)更加可靠的服务;另一个方面,分层也是解耦的思想体现,只要实现了传输原语,就不必在乎网络层的实现(以太网或者WiMAX)
简单情况下,我发一包,你回一包,但是网络不佳的情况下,要保证包传输到位就必须引入重传机制,重传机制一引入就必须考虑如何解决包重复接受的问题,所以限制了包生存周期(跳计数器),然后通过段序号、日时钟、滑动窗口协议来丢弃已经接受的重复数据包,通过三步握手解决了初始序号获得的问题。这一个出现问题、解决问题、出现新问题、解决新问题的反复的过程体现了协议设计者们的智慧和不屈不挠的毅力,非常值得我们学习
由“两军对垒”问题引出释放连接的问题,无论怎么确认通信,也无法百分之百保证对方收到的消息,两次、三次、四次握手其实都是可行的(不一定是无误的),事实上释放连接的四步挥手完全就是“两军对垒”的一个折中方案,既保证了准确率,又避免了带宽浪费。建立连接的三次握手也同样是一种折中
内容分发不同于通信任务,目标主机不重要,重要的是获取了什么内容,获取内容的代价。主要有两个结构,CDN和P2P。CDN采用分发树结构,扩展性很高,实现原理是DNS重定向;P2P把多台计算机(对等节点)的资源集中起来,每台计算机既可以作为服务器向其他计算机提供资源,也可以作为客户端向其他计算机请求资源
本文最大特点就是故事性强,一环扣一环,引人入胜。
Netty实战
Netty实战 (豆瓣) https://book.douban.com/subject/27038538/
Netty是网络编程中不可不谈的一个大作,也是许多成功的网络应用的基础设施,本书就从为什么是Netty,引导我们尝试使用,并进行了细致的讲解,为我们全面的剖析了Netyy这个庞大而精妙的框架,组织得很有条理。
但是要注意,看本书之前或者说学习Netty之前,务必要对Java IO、NIO、AIO以及Reactor和Proactor的概念有一定的了解,不然就是没学会走先去学跑了。
亮点:
低级别的 API 不仅暴露了高级别直接使用的复杂性,而且引入了过分依赖于这项技术所造成的短板。因此,面向对象有一个基本原则:通过抽象来隐藏背后的复杂性。Netty提供了高层次的抽象来简化TCP和UDP服务器的编程,但是你仍然可以使用底层地API。(David John Wheeler有一句名言“计算机科学中的任何问题都可以通过加上一层逻辑层来解决”,这个原则在计算机各技术领域被广泛应用)
一个 Netty 的设计的主要目标是促进“关注点分离”:将你的业务逻辑从网络基础设施应用程序中分离
Netty组件说明:BOOTSTRAP启动应用,配置,为应用提供一个容器;Channel是一条通信信道,类似于socket;ChannelHandler是数据处理的容器,也是我们要写业务逻辑的地方,也是我们通常关注最多的地方;ChannelPipeline属于一个Channel,是ChannelHandler链的容器;EventLoop 用于处理 Channel 的 I/O 操作,一个单一的 EventLoop通常会处理多个 Channel事件,一个 EventLoopGroup 可以含有多于一个的 EventLoop;ChannelFuture 的 addListener 方法注册了一个 ChannelFutureListener ,当操作完成时,可以被通知(不管成功与否)。可见,Netty的组件划分真的很细致精小,优点就是模块间易于解耦,模块本身可重用性高,但是也就有点啰嗦,这也是Java本身比较受到诟病的原因,还是那句话,凡事有得必有失嘛,它的优点能让你忍受它的缺点就行了
Transport 使用情景:OIO(即老的,最原始的阻塞IO)-在低连接数、需要低延迟时、阻塞时使用;NIO-在高连接数时使用;Local-在同一个JVM内通信时使用;Embedded-测试ChannelHandler时使用
Netty提出的ByteBuf优于JDK原生的ByteBuffer因为:可以自定义缓冲类型(堆缓冲区,直接缓冲区,复合缓冲区),通过复合缓冲类型实现零拷贝;扩展性好,比如 StringBuilder;不需要调用 flip() 来切换读/写模式;读取和写入索引分开
直接缓冲区”是另一个 ByteBuf 模式,允许 JVM 通过本地方法调用分配内存,优点:通过免去 socket 发送数据之前,JVM 先将数据从用户区复制到内核区, 提升IO处理速度, 直接缓冲区的内容可以驻留在垃圾回收扫描的堆区以外;DirectBuffer 在 -XX:MaxDirectMemorySize=xxM大小限制下, 使用 Heap 之外的内存, GC对此”无能为力”,也就意味着规避了在高负载下频繁的GC过程对应用线程的中断影响
Netty 的使用一个包含 EventLoop 的 EventLoopGroup 为 Channel 的 I/O 和事件服务。EventLoop 创建并分配方式不同基于传输的实现。异步实现使用只有少数 EventLoop(和Threads)共享于 Channel 之间 。这允许最小线程数服务多个 Channel,不需要为他们每个Channel都有一个专门的 Thread
本书中对于异步同步和阻塞非阻塞的观念有些模糊,请见上面实战Java高并发程序设计一节我的理解,读者可以参考一下,自己取舍。事实上,Java的NIO不是异步的,AIO(或者说NIO2.0)才是,Netty基于NIO(尝试过并抛弃了AIO),通过自己的封装,实现了从使用者角度看起来的异步。学习Netty还有一个好地方就是官方文档
分布式java应用
分布式java应用 (豆瓣) https://book.douban.com/subject/4848587/
后端要搞得好,不上分布式肯定是不行的,因为写个小网站你可以懂点 SSM 或者 Spring Boot 就行了,但是如果要想搭建一个(或者说成为构建过程的一份子)用户成千上万甚至百万、千万、上亿的站点,那就必须要懂分布式了。这本书就可以当做学习分布式的入门书籍。
亮点:
实践是最好的成长,发表是最好的记忆。这句话的确可以放在电脑上当桌面背景
分布式系统通信底层都依赖于TCP、UDP,但是为了易用性,通常会使用一些更贴近应用层的协议,书中提到了原始的jdk通信,以及webservice,Mina,实际上还有许多,比如RPC、WebService、RMI、JMS(p2p或者发布订阅)、JNDI等等
网络IO分三类。BIO:用户线程在进行IO操作的时候进行系统调用,阻塞,只有读到(写入)了数据才释放资源;NIO:同步非阻塞IO,用户线程在发起IO操作之后可以立即返回,只有流可读(可写)操作系统才会通知用户线程进行读取(写入),但是用户线程需要不断轮询来请求数据,Linux采用epoll实现;AIO:用户线程发起请求时,由操作系统来进行读取(写入),IO事件就绪的时候,操作系统会通知对应的用户线程来处理,实现真正的异步IO,Windows下IOCP实现了AIO,Linux只有epoll实现模拟实现的AIO。同时,由于服务器的主力是Linux,所以AIO的用处目前不是特别广
BIO对应的是一连接一线程,可以引入线程池来优化,但是一个操作系统线程数量终究有限,所以不可能支撑大量连接数;NIO对应的是一请求一线程,只有某个连接有读写事件时才为其生成一个线程来处理,只有连接数不多或者连接数多且都请求频繁时才没有优势,但实际情况中,服务器通常会支持大量连接数,但是同时发送请求的连接不会太多,所以NIO应用比价广泛;AIO对应一个有效请求一个线程,亦即OS处理IO完毕后再生成用户线程处理,充分调用了OS参与,可以应对连接数多且操作频繁的场景,如果Linux对AIO的支持更好,这个模型可能会流行起来
SOA亦即面向服务的架构,强调系统之间以标准服务的方式进行交互,各系统可以采用不同的语言、框架来实现,交互则全部采用服务来进行,目前SCA和ESB是主流标准,分别有Tuscany和Mule等常用框架实现
调优的步骤:衡量系统现状,设定调优目标,寻找性能瓶颈,性能调优,衡量是否达标,反复进行前面三步直至达标。
以前我对负载均衡的理解还是停留在引入一个(群)中间节点作为调度者,调度者(硬件或软件)接受请求并转发给实际应用服务器。这实际上叫做中心化负载均衡,书中提到Gossip实现了无中间节点的软件负载均衡实际上是一种去中心化传播事件的负载均衡,去掉中心节点后请求响应直接进行加快了速度,而且能避免调度者单点故障问题。最高级别的负载均衡是全局负载均衡,实现不同地域机房的负载均衡,既可以避免单点故障还可以就近响应,提高访问速度,实现高可用,一般由硬件GSLB实现(在合肥 ping 了一下 http://qq.com 得到的结果是深圳的,在天津得到的结果就是北京的,虽然不一定,但是看得出来有差别),但是多机房的一致性很难保证,通常都要用消息中间件来实现
除了上面提到的机房等硬件上的故障,应用软件本身的故障也是高可用的大敌,所以要注意避免故障,及时发现并处理故障,具体包括:明确使用场景,做到最贴近场景的最简单系统,或者分阶段最简单系统;设计可容错系统,fail fast;系统可自我保护,对第三方依赖保持弱依赖,避免连锁失效;建立分级报警系统和日志记录与分析系统;注意总结故障分析便于下次快速利用,这也是架构即未来里面提到过的;按照业务拆分子系统......
构建可伸缩的系统关键在于水平伸缩,因为垂直伸缩是有限的(一台机器的性能终究有限),而水平伸缩理论上是无限的(可以添加“无数”台机器),水平伸缩通常通过SNA(share nothing architecture)实现,亦即应用系统之间不共享状态,把状态信息统一放入缓存或数据库(可以是分布式)。文件系统的水平扩展思想也是类似的
本书可以当做入门书籍,但是书中真正关于分布式的内容似乎少了些,倒是Java基础知识占据了很大篇幅,感觉有点不对题目,毕竟要看这些知识我可以看Java核心技术,Java并发编程实战或者深入理解jvm啊,但是也可以用来复习一下,而且有许多实验数据可供参考,还是可以的。
PS:我之前已经看了Netty了,所以书中关于Mina的部分,时间不够我就跳着看了,时间够还是可以了解一下的,两者对比。
分布式系统 概念与设计
分布式系统 原书第5版 (豆瓣) https://book.douban.com/subject/21624776/
读了上面一本分布式Java,不免对分布式系统想要有一个更全面、更系统的认识,那么这本书就是一本绝佳的书籍。
亮点:
资源共享是构建分布式系统的主要目;并发、缺乏全局时钟、故障独立性是其主要特征;处理其构件的异构性、开放性(允许增加或替换组件)、安全性(机密完整可用)、可伸缩性(用户的负载增加时能正常运行的能力)、故障处理、组件并发性、透明性(分布式的组件对外展示为一个整体)、QoS(在传输数据时满足要求的能力)等是其主要挑战
无论是 请求-应答(比如http) 还是 RPC 或者 RMI(类似于RPC但是仅限于Java且支持对象概念),收发双发都是双向的(直接通信),要想解耦收发双方(空间解耦和时间解耦),那么就得用间接通信:组通信(广播),发布订阅系统,消息队列,元组空间(生成通信),分布式共享内存等方式
中间件是一组计算机上的进程或对象,其目的是屏蔽异构性,亦即底层的通讯,交互,连接等复杂又通用的功能以服务的形式提供出来,分布式系统应用在交互时,直接使用中间件提供的高层编程抽象即可,实现了简洁的分布式系统应用的通信和资源共享的支持
套接字(socket)是通信的抽象,无论是TCP还是UDP的方式,它提供了进程间通信的端点,必须和协议,主机地址(分为目的端与源端),端口(分为目的端与源端)绑定起来
RPC的概念代表着分布式计算的重大突破,使得分布式编程和传统编程类似,实现了高级的透明性,这种相似性将传统的过程调用模型扩展到分布式环境中,隐藏了底层分布式环境的重要细节部分,简化了分布式开发
操作系统的任务是提供一个物理层(处理器,内存,硬盘等)之上的面向问题的抽象,例如给程序员提供文件和套接字而不是磁盘块和原始网络访问。操作系统分为网络操作系统(具有内置联网功能,能自主管理自己的资源并网络透明地访问其它一些资源但是不能跨节点管理进程,也就是有多个系统映像)和分布式操作系统(用户不必关心程序运行的地点和资源位置,能透明的将新的进程调度到合理的节点上,有单一的系统映像),后者几乎没有普遍应用的案例,因为中间件和网络操作系统的结合就能很好的满足用户需求
最重要的两种中间件风格:分布式对象(允许面向对象编程模型开发分布式系统,通信实体被表示成对象,通信使用RMI,但也可以使用其他的比如分布式事件);组件(基于对象方法的自然演化,主要克服其限制:隐式依赖,编程复杂性,缺少关注点分离支持,无部署支持)
面向服务的体系结构(SOA)是一套设计原则,分布式系统用松耦合的服务集开发,服务能被动态发现,能互相通信并通过编排进行协调从而提供更强的服务。可以基于分布式对象或者组件来实现,但实际中主要通过web服务实现,主要是因为web服务内在的松耦合性(比如不管子系统是CORBA还是.NET,只要用web服务暴露接口,就能轻易实现集成)
分布式是一门结合计算机网络与操作系统等多种门类的交叉学科,所以这本书也包含了许多这些方面的知识,不失为一种复习的方式。
大型网站系统与Java中间件开发实践
大型网站系统与Java中间件开发实践 (豆瓣) https://book.douban.com/subject/25867042/
上面的分布式Java是一本很基础的入门书籍,这本书算是一本更加全面和仔细的书籍,把它没讲到的部分都讲了一些,而且更注重中间件(支撑大型网站关键、必要的技术之一)的讲解。
亮点:
分布式系统中的控制节点协调系统之间的协作,可以用:硬件均衡负载设备(昂贵)、软件方式的透明代理(增加流量、易出现单点失效)、采用名称服务的直连请求(不易单点失效、流量少)、采用规则服务器的直连调用、采用master+worker的架构
从一个最基本、简单、部署在单机上的网站开始,数据量和访问量慢慢上去了,一步步演进:数据库与应用分离到两台服务器;多应用服务器与单数据库,涉及到session问题(sticky、replication、集中存储、cookie Based);数据库读写分离、引入搜索引擎、引入数据和页面缓存;引入分布式存储系统;数据库垂直拆分(一个业务的数据放到一个专用数据库)、水平拆分(单个表放到两个数据库中);拆分应用,可以根据业务特性把应用拆开或者走服务化的路,亦即每个web系统通过不同的服务来完成特定的业务;引入消息中间件来完成拆分、服务化等目的
大型网站主要用到三类中间件:远过程调用和对象访问中间件(解决分布式下应用互相访问的问题);消息中间件(解决应用之间消息传递、解耦、异步的问题);数据访问中间件(解决数据库访问共性的问题);
服务框架带来的好处:结构上,架构更清晰,底层资源由服务层统一管理,也更利于提高效率;稳定性上,一些散落在多个系统中的代码变成了服务,由专门的团队统一维护,可以提高代码质量,另一方面由于核心稳定,修改发布次数减少,也提高了稳定性
通过消息中间件,业务系统不必知道有多少个其他业务系统需要了解某一项业务,只需要把消息发布给消息中间价,消息中间价负责把消息投递给相关的其他业务系统,这样每个业务系统都能专注自己本身的业务,不必维护臃肿的依赖关系,达到解耦和消息异步的目的
消息中间件要点:保证业务与发布消息的一致性,一般直接紧挨着写代码就行,出错概率较小,但是对于要保证强一致的系统,需要采用类似于TCP三步握手的方式来保证;解决强依赖关系,尽量保证中间件的可靠性,最好达到和业务系统本身可靠性相同,从业务数据上能对消息进行补发是最彻底的容灾手段;消息模型对消息接受的影响,Queue(点对点)和Topic(发布/订阅)都不能完全适应集群模式(发送接收方都是集群,要做到不重不漏、互不干扰),解决方法是结合两者使用,集群之间用Topic模型,集群内部用Queue模型;做到持久订阅(除非显示取消订阅,否则即使订阅者宕机了重启后也应该能收到所有消息)
服务框架,数据访问层,消息中间价背后的基础产品就是软负载和配置中心。软负载中心的基本职责有两个:聚合地址信息,将所有方面的地址形成列表供其他方使用;生命周期感知,对服务的上下线自动感知并更新服务地址。软负载中心管理非持久数据,集中配置管理中心管理持久数据
ESI将页面分为相对静态的内容和动态的内容,如果整个页面在服务端渲染的话,可以将相对静态的内容进行缓存,而不是每次都全部重新渲染
在解决具体的问题的时候,完全从头写代码还是基于开源代码去发展一定要慎重思考,如果场景类似那么以比较活跃的开源产品为基础并根据自己的场景定制会起到事半功倍的效果,如果没有合适的那就需要从零开始了
京东基础架构建设之路
京东基础架构建设之路(全彩) (豆瓣) https://book.douban.com/subject/27202674/
本书主要作者是我们学校的一位师兄,前不久来学校办了个报告,主要讲了他在京东的基础架构经验,介绍了新的架构从无到有的过程,他们团队的技术现在一年能为京东省了很多亿,这本书里凝聚了他们的技术精华,当然值得我们去了解一下咯,而且当时的QA环节我提了个问题,主办方就送了这本书,师兄当场亲笔签名,我就更应该把这本书好好研究研究了。
亮点:
跳过虚拟化,直接容器化,结合团队的OpenStack经验,选择OpenStack+Docker的架构,用OpenStack来管理调度Docker容器
建议镜像和根目录只保留只读或者少量读\写文件,对于频繁的读\写或者大量写的文件尽量用外挂的volume,规范业务对容器的使用行为
MySQL运维自动化包括:自动备份,包括按时间点精准恢复;自动历史数据转移,将不再变更的历史数据定期转入大容量分布式存储系统(如HBASE),控制MySQL数据量;自动故障检测与切换,采用Orchestrator作为管理工具;全面容器化,提升MySQL实例交付效率
在故障检测和故障切换的方案中,比较容易想到的就是ZooKeeper的临时节点探测不存活的服务,但是由于需要修改服务端代码,不便于跨机房部署和连接数过多的问题,京东自己开发了分布式探测系统,为了避免临时网络不通导致的误判,采取多个探测程序部署到机房的不通机架里,对实例进行分布式投票,只要有一个探测存活就判断为存活的,否则通知故障恢复程序进行主从切换
容器中的数据最原始的是直接使用物理机上的volume来存储,但是这样容器数据就和物理机绑定了,无法实现数据的迁移也无法做到超大容量的存储。京东的ContainerFS就是为容器而生的分布式文件系统,它的每个volume都可以被一个或者多个容器当做本地文件直接使用,容器之间可以共享数据,支持弹性伸缩、线性扩展
JMQ由于要支撑订单、支付等要求对服务质量要求极其严格的业务,所以必须保证在整个机房都失效的情况下依然能恢复数据,所以采取了一主一从且至少一个备份的架构。主从分布在同一个数据中心,采取同步复制;备份在另一个数据中心,采取异步复制
分布式服务跟踪系统CallGraph通过核心的调用链(依靠全局唯一的ID实现)的概念能追踪到一次调用的从请求发出到最底层系统的所有环节,便于排查问题,而且具有低侵入性
全链路压测通过全国各个公网节点发来的数据请求来较好地模拟真实用户操作,同时压测会有参数标识,进行真实流量与测试流量的隔离,避免污染生产数据
大型互联网公司的数据中心架构一般都会经历单机房,同城双机房,异地灾备,最后是异地多活的各个阶段。京东目前有三大数据中心,每个中心都能承担读写业务(这就是异地多活),依据GLSB和HTTPDNS和全国网络质量检测数据来动态优化用户到某个具体中心获得服务,中心之间用专线打通保证数据秒级同步提供低延迟数据最终一致性保障
本书虽然很薄,但是讲述的知识平常我们可能都用不到(不是谁都有机会接触这么大的规模的集群管理),看的时候要想完全吸收还是有些费劲,还得在以后的实践中加以琢磨才行。
Docker进阶与实战
Docker进阶与实战 (豆瓣) https://book.douban.com/subject/26701218/
第一本docker书 为我们入门docker提供了很好的途径,这本书为我们提供了深入了解整个docker技术栈所需要的更多的知识,比如关键技术原理,高级技巧如安全、测试、集群管理等等。这两本书有重叠,但是后者对前者有许多的补充,利于我们更好的使用、更好的理解docker。
亮点:
Docker并没有传统虚拟化中的Hypervisor层,使用轻量级虚拟化技术,基于Cgroup和Namespace等内核容器技术,与内核深度融合,性能与物理机非常接近。通信上,Docker不与内核直接通信,而是通过LibContainer。libContainer是真正意义上的容器引擎,通过clone系统调用直接创建容器(一个新的子进程),通过pivot_root进入容器(新的rootfs),通过cgroupfs控制资源,Docker本身更侧重处理更上层的业务。
Docker主要工作是在LXC(linuxContainer亦即linux内核容器技术)的基础上提供了一些更高级的控制工具,包括:定义镜像格式,包含所有程序和依赖,使得镜像可以跨主机部署,实现了进程沙盒,同时保证程序及其依赖环境的一致性;提供自动构建工具,使得当前机器的配置不会影响镜像构建过程;提供了类似git的版本管理功能,支持镜像版本追踪、校验、回退等功能;通过镜像分层实现组件重用,减小镜像大小,加快构建速度;工具生态链......
虚拟机是硬件级别的虚拟化,利用VT-x、AMD-V等硬件虚拟化技术通过Hypervisor来实现对资源的彻底隔离;容器是操作系统级别的虚拟化,利用内核的Cgroup和Namespace等软件实现的特性实现来实现进程隔离,Docker容器与主机共享操作系统内核,不同容器可以共享部分资源,因此容器更加轻量级,资源消耗更少,启动更快。而虚拟机独占自己的资源,各个虚拟机几乎完全独立,不存在共享,消耗更多资源,启动也慢
容器技术是一种操作系统级别的虚拟化技术(还有硬件虚拟化、半虚拟化等技术),已经集成进linux内核,是内核提供的原生特性,主要包含Namespace(主要做访问隔离,针对一类资源进行抽象,并将其封装在一起供容器使用)和Cgroup(control group,主要做资源控制,将一组进程放进一个控制组,给这个控制组分配指定可用的资源,其原生接口由cgroupfs提供)。但是光有这两个还不够,还需要rootfs(文件系统隔离)和容器引擎(生命周期控制)
Docker引入联合挂载技术(把多个目录挂载到同一目录,对外呈现这些目录的联合)使得镜像分层成为可能,使用git式的管理方式使得基础镜像的重用成为可能
如果你在寻找一个清晰、简单、低耦合、无状态、面向资源的API设计方式,那么RESTFul API就是一种不错的选择,它充分利用了HTTP本身的语义,使你的API更易用更具有扩展性,同时优雅的展示你的资源。Docker API 就是一种 RESTFul API
Cgroup对系统资源的限制已经比较完善了,但是Namespace的隔离还不算很好,比如procfs的很多接口都没隔离,可以通过procfs可以读取和修改宿主系统的相关信息,所以procfs是以只读方式挂载的,还有syslog也是没隔离的。Namespace的隔离不完善,也不太可能完善,这是共享内核固有的缺陷。有许多安全策略被用来保护系统,比如Cgroup限制、容器运行在虚拟机中、镜像签名、日志审计、监控、文件保护
在制作镜像的时候,源码导入有两种策略:静态导入(使用COPY命令直接把代码放入镜像);动态导入(使用VOLUME命令把代码文件动态挂载到容器)。建议使用两个dockerfile,一个开发版本(dev)用动态挂载,便于随时修改代码,一个发布版本用静态导入,便于减少依赖、处处运行
作者还讲述了怎么参与到docker的开发和维护的过程中,如果我们在使用过程中遇上了问题,去看看源码和社区也是不错的,说不定就能解决
虽然只过去了1年多,但是书中已经有内容过时了,比如不要再安装 http://docker.io 或者 docker-engine ,而是使用docker-ce (免费版)或者 ee (企业版)。而且,如今 Docker-Compose、Swarm 已经比较成熟了,完全可以用在生产环境中,当然,(超)大规模的部署最终都得按照自己的业务情况来造轮子,完全依靠开源的肯定是不行的,这样是上面那本书的作者提到过的观点。