通过这一系列文章,我主要想回答以下几个问题。
理论和现实的差距。你是否觉得自己从学校毕业的时候只做过小玩具一样的程序?走入职场后哪怕没有什么经验也可以把文中提到的这些课外练习走一遍。学校课程总是从理论出发,作业项目都看不出有什么实际作用,到了工作上发现自己什么也不会干。
技术能力的瓶颈。你又是否觉得,在工作当中需要的技术只不过是不断地堆业务功能,完全没有什么技术含量。而你工作一段时间后,自己都感觉得非常地迷茫和彷徨,感觉到达了提高的瓶颈,完全不知道怎么提升了。
技术太多学不过来。你是否又觉得,要学的技术多得都不行了,完全不知道怎么学?感觉完全跟不上。有没有什么速成的方法?
对此,我有如下的一些解释,以端正一下你的态度。
学习建议。
学习编程你还需要会玩 Linux,虽然 Windows 占据着更多的桌面市场,但是你还是要了解 Linux。这里,你可以看一下,W3CSchool 上的在线教程 Linux 教程。
记得要看官方文档!!!!!
在系统地学习编程技能之前,我希望你能先看一下 " The Key To Accelerating Your Coding Skills", 这篇文章会告诉你如何有效地快速提高自己的编程能力。
然后接下来是下面几大块内容,但还只是入门级的。
学习 Java 语言有以下入门级的书(注意:下面一些书在入门篇中有所提及,但为了完整性,还是要在这里提一下,因为可能有朋友是跳着看的)。
前面推荐的几本书可以帮你成功入门 Java,但想要进一步成长,就要看下面我推荐的几本进阶级别的书了。
接下来,你需要了解了一下如何编写高效的代码,于是必需看一下《Effective Java》(注意,这里我给的引用是第三版的,也是 2017 年末出版的书),这本书是模仿 Scott Meyers 的经典图书《Effective C++》的。Effective 这种书基本上都是各种经验之谈,所以,这是一本非常不错的书,你一定要读。这里需要推荐一下 Google Guava 库 ,这个库不但是 JDK 的升级库,其中有如:集合(collections)、缓存(caching)、原生类型支持(primitives support)、并发库(concurrency libraries)、通用注解(common annotations)、字符串处理(string processing)、I/O 等库,其还是 Effective Java 这本书中的那些经验的实践代表。
《Java 并发编程实战》,是一本完美的 Java 并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则,如何将小的线程安全类组合成更大的线程安全类,如何利用线程来提高并发应用程序的吞吐量,如何识别可并行执行的任务,如何提高单线程子系统的响应性,如何确保并发程序执行预期任务,如何提高并发代码的性能和可伸缩性等内容。最后介绍了一些高级主题,如显式锁、原子变量、非阻塞算法以及如何开发自定义的同步工具类。
了解如何编写出并发的程序,你还需要了解一下如何优化 Java 的性能。我推荐《Java 性能权威指南》。通过学习这本书,你可以比较大程度地提升性能测试的效果。其中包括:使用 JDK 中自带的工具收集 Java 应用的性能数据,理解 JIT 编译器的优缺点,调优 JVM 垃圾收集器以减少对程序的影响,学习管理堆内存和 JVM 原生内存的方法,了解如何最大程度地优化 Java 线程及同步的性能,等等。看完这本书后,如果你还有余力,想了解更多的底层细节,那么,你有必要去读一下《深入理解 Java 虚拟机》。
《Java 编程思想》,真是一本透着编程思想的书。上面的书让你从微观角度了解 Java,而这本书则可以让你从一个宏观角度了解 Java。这本书和 Java 核心技术的厚度差不多,但这本书的信息密度比较大。所以,读起来是非常耗大脑的,因为它会让你不断地思考。对于想学好 Java 的程序员来说,这是一本必读的书。
《精通 Spring 4.x》,也是一本很不错的书,就是有点厚,一共有 800 多页,都是干货。我认为其中最不错的是在分析原理,尤其是针对前面提到的 Spring 技术,应用与原理都讲得很透彻,IOC 和 AOP 也分析得很棒,娓娓道来。其对任何一个技术都分析得很细致和全面,不足之处就是内容太多了,所以导致很厚,但这并不影响它是一本不错的工具书。
当然,学 Java 你一定要学面向对象的设计模式,这里就只有一本经典的书《设计模式》。如果你觉得有点儿难度了,那么可以看一下《Head First 设计模式》。学习面向对象的设计模式时,你不要迷失在那 23 个设计模式中,你一定要明白这两个原则:
Program to an ‘interface’, not an 'implementation’
Favor ‘object composition’ over 'class inheritance’
至此,如果你把上面的这些知识都融汇贯通的话,那么,你已是一个高级的 Java 程序员了,我保证你已经超过了绝大多数程序员了。基本上来说,你在技术方面是可以进入到一线公司的,而且还不是一般的岗位,至少是高级程序员或是初级架构师的级别了。
C 语言太原始了,C++ 太复杂了,Go 语言是不二之选。有了 C/C++ 的功底,学习 Go 语言非常简单。
首推 Go by Example 作为你的入门教程。然后,Go 101 也是一个很不错的在线电子书。如果你想看纸书的话,The Go Programming Language 一书在豆瓣上有 9.2 分,但是国内没有卖的。(当然,我以前也写过两篇入门的供你参考 “GO 语言简介(上)- 语法” 和 “GO 语言简介(下)- 特性”)。
另外,Go 语言官方的 Effective Go 是必读的,这篇文章告诉你如何更好地使用 Go 语言,以及 Go 语言中的一些原理。
Go 语言最突出之处是并发编程,Unix 老牌黑客罗勃·派克(Rob Pike)在 Google I/O 上的两个分享,可以让你学习到一些并发编程的模式。
然后,Go 在 GitHub 的 wiki 上有好多不错的学习资源,你可以从中学习到多。比如:
此外,还有个内容丰富的 Go 资源列表 Awesome Go,推荐看看。
算法是比较难学习的,而且学习“算法”是需要智商的。数组、链表、哈希表、二叉树、排序算法等一些基础知识,对大多数人来说是没什么问题的。但是一旦进入到路径规划、背包问题、字符串匹配、动态规划、递归遍历等一些比较复杂的问题上,就会让很多人跟不上了,不但跟不上,而且还会非常痛苦。是的,解决算法问题的确是可以区分人类智商的一个比较好的方式,这也是为什么好些公司用算法题当面试题来找到智商比较高的程序员。
然而,在很多时候,我们在工作中却发现根本用不到算法,或是一些基本的算法也没有必要实现,只需要使用一下第三方的库就好了。于是,导致社会上出现很多“算法无用论”的声音。
对此,我想说,算法真的很重要。我这 20 年的经历告诉我,无论是做业务还是做底层系统,经常需要使用算法处理各种各样的问题。比如,业务上我需要用算法比较两个数组中差异的布隆过滤器,或是在做监控系统时实时计算过去一分钟的 P99 统计时的蓄水池算法,或是数据库的 B+ 树索引,还有 Linux 内核中的 epoll 的红黑树,还有在做服务调度里的“背包问题”等都会用算法,真的是会本质上帮助到你,也是会让你瞬间会产生成就感的事情。
虽然算法很难,需要智商,但我还是想鼓励你,这其中是有很多的套路是可以学习的,一旦学会这些套路,你会受益无穷的。
这里有几本书着重推荐一下。
然后,你需要去做一些题来训练一下自己的算法能力,这里就要推荐 LeetCode 这个网站了。它是一个很不错的做算法训练的地方。现在也越做越好了。基本上来说,这里会有两类题。
我觉得每个程序员都应该花时间和精力做这些题,因为你会从这些题中得到很大的收益。我在 Leetcode 上做的一些题的代码在这——我的 GitHub 上,可以给你一些参考。
如果能够把这些算法能力都掌握了,那么你就有很大的概率可以很容易地通过这世界上最优的公司的面试,比如:Google、Amazon、Facebook 之类的公司。对你来说,如果能够进入到这些公司里工作,那么你未来的想像空间也会大很多。
最后,我们要知道这个世界上的数据结构和算法有很多,下面给出了两个网站。
下面这些书,基本上是计算机科学系的大学教材。如果你想有科班出身的理论基础,那么这些书是必读的。当然,这些理论基础知识比较枯燥,但我觉得如果你想成为专业的程序员,那么应该要找时间读一下。
进入专业的编程领域,学习系统知识是非常关键的一部分。
首先推荐的是翻译版图书《深入理解计算机系统》,原书名为《Computer Systems A Programmer’s Perspective》。不过,这本书叫做《程序员所需要了解的计算机知识》更为合适。
本书的最大优点是为程序员描述计算机系统的实现细节,帮助其在大脑中构造一个层次型的计算机系统。从最底层的数据在内存中的表示到流水线指令的构成,到虚拟存储器,到编译系统,到动态加载库,到最后的用户态应用。通过掌握程序是如何映射到系统上,以及程序是如何执行的,你能够更好地理解程序的行为为什么是这样的,以及效率低下是如何造成的。
再强调一下,这本书是程序员必读的一本书!
然后就是美国计算机科学家 理查德·史蒂文斯(Richard Stevens) 的三套巨经典无比的书。(理查德·史蒂文斯于 1999 年 9 月 1 日离世,终年 48 岁。死因不详,有人说是滑雪意外,有人说是攀岩意外,有人说是滑翔机意外。总之,家人没有透露。大师的 个人主页 今天还可以访问。)
这几本书的地位我就不多说了,你可以自己看相关的书评。但是,这三本书可能都不容易读,一方面是比较厚,另一方面是知识的密度太大了,所以,读起来有点枯燥和乏味。但是,这没办法,你得忍住。
这里要重点说一下《TCP/IP 详解》这本书,是一本很奇怪的书。这本书迄今至少被 近五百篇学术论文引用过 。这本写给工程师看的书居然被各种学院派的论文来引用,也是很神奇的一件事了。而且,虽然理查德·史蒂文斯不是 TCP 的发明人,但是这本书中把这个协议深入浅出地讲出来,还画了几百张时序图,也是令人叹为观止了。
如果你觉得上面这几本经典书比较难啃,你可以试试下面这些通俗易懂的(当然,如果读得懂上面那三本的,下面的这些也就不需要读了)。
另外,学习网络协议不单只是看书,你最好用个抓包工具看看这些网络包是什么样的。所以,这里推荐一本书《Wireshark 数据包分析实战》。在这本书中,作者结合一些简单易懂的实际网络案例,图文并茂地演示使用 Wireshark 进行数据包分析的技术方法,可以让我们更好地了解和学习网络协议。当然,也拥有了一定的黑客的技能。
看完《Unix 高级环境编程》后,你可以趁热打铁看看《Linux/Unix 系统编程手册》或是罗伯特·拉姆(Robert Love)的 Linux System Programming 英文电子版 。其中文翻译版Linux 系统编程 也值得一读,虽然和《Unix 高级环境编程》很像,不过其主要突出的是 Linux 的一些关键技术和相关的系统调用。
关于 TCP 的东西,你还可以看看下面这一系列的文章。
对于系统知识,我认为主要有以下一些学习要点。
然后,当你读完《Unix 网络编程》后,千万要去读一下 “C10K Problem (中文翻译版)”。提出这个问题的人叫丹·凯格尔(Dan Kegel),目前在 Google 任职。
他从 1978 年起开始接触计算机编程,是 Winetricks 的作者,也是 Wine 1.0 的管理员,同时也是 Crosstool( 一个让 gcc/glibc 编译器更易用的工具套件)的作者。还是 Java JSR 51 规范的提交者并参与编写了 Java 平台的 NIO 和文件锁,同时参与了 RFC 5128 标准中有关 NAT 穿越(P2P 打洞)技术的描述和定义。
C10K 问题本质上是操作系统处理大并发请求的问题。对于 Web 时代的操作系统而言,对于客户端过来的大量的并发请求,需要创建相应的服务进程或线程。这些进程或线程多了,导致数据拷贝频繁(缓存 I/O、内核将数据拷贝到用户进程空间、阻塞), 进程 / 线程上下文切换消耗大,从而导致资源被耗尽而崩溃。这就是 C10K 问题的本质。
了解这个问题,并了解操作系统是如何通过多路复用的技术来解决这个问题的,有助于你了解各种 I/O 和异步模型,这对于你未来的编程和架构能力是相当重要的。
另外,现在,整个世界都在解决 C10M 问题,推荐看看 The Secret To 10 Million Concurrent Connections -The Kernel Is The Problem, Not The Solution 一文。
我们已经学习完了编程语言、理论学科和系统知识三部分内容,下面就来做几个实践项目,小试牛刀一下。实现语言可以用 C、C++ 或 Java。
实现一个 telnet 版本的聊天服务器,主要有以下需求。
telnet ip:port
的方式连接到服务器上。实现一个简单的 HTTP 服务器,主要有以下需求。
实现一个生产者 / 消费者消息队列服务,主要有以下需求。
到今天,我们已经学习完了专业编程方面最为重要的三部分内容:编程语言、理论学科和系统知识,我们针对这些内容做个小结。如果想看完我推荐的那些书和知识,并能理解和掌握,我估计怎么也得需要 4-5 年的时间。嗯,是的,就是一个计算机科学系科班出身的程序员需要学习的一些东西。这其中,最重要的是下面这几点。
编程语言。以工业级的 C、C++、Java 这三门语言为主,这三门语言才是真正算得上工业级的编程语言,因为有工业级的标准化组织在控制着这几门语言,而且也有工业级的企业应用。尤其是 Java,还衍生出了大量的企业级架构上的开源生态。你至少需要掌握 C 语言和 Java 语言,这对你以后面对各式各样的编程语言是非常重要的。
此外,还推荐学习 Go 语言,它已成为云计算领域事实上的标准语言,尤其是在 Docker、Kubernetes 等项目中。而且,Go 语言在国内外一些知名公司中有了一定的应用和实践,并且其生态圈也越来越好。
算法和数据结构。这个太重要了,尤其是最基础的算法和数据结构,这是任何一个称职的程序员都需要学习和掌握的。你必需要掌握。
计算机的相关系统。你至少要掌握三个系统的基础知识,一个是操作系统,一个是网络系统,还有一个是数据库系统。它们分别代表着计算机基础构架的三大件——计算、存储、网络。
如果你能够走到这里,把前面的那些知识都了解了(不用精通,因为精通是需要时间和实践来慢慢锤炼出来的,所以,你也不用着急),那么你已经是一个合格的程序员了,而且你的潜力和可能性是非常非常高的。
如果经历过这些比较枯燥的理论知识,而且你还能有热情和成就感,那么我要恭喜你了。因为你已经超过了绝大多数人,而且还是排在上游的比较抢手的程序员了。我相信你至少可以找到年薪 50 万以上的工作了。
但是,你还需要很多的经验或是一些实践,以及一些大系统大项目的实际动手的经验。没关系,我们后面会有教你怎么实操的方法和攻略。
但是,往后面走,你需要开始需要术业有专攻了。下面给一些建议的方向。
这些方向你要仔细选择,因为一旦选好,就要勇往直前地走下去,当然,你要回头转别的方向也没什么问题,因为你有前面的这些基础知识在身,所以,不用害怕。只是不同的方向上会有不同的经验积累,经验积累是看书看不来的,这个是转方向的成本。
下篇文章,我们将进入《软件设计篇》。敬请期待。
编程范式
学习编程范式可以让你明白编程的本质和各种语言的编程方式。因此,我推荐以下一些资料,以帮助你系统化地学习和理解。
一个是我在极客时间写的《编程范式游记》系列文章,目录如下。
Wikipedia: Programming paradigm ,维基百科上有一个编程范式的页面,顺着这个页面看下去,你可以看到很多很多有用的和编程相关的知识。这些东西对你的编程技能的提高会非常非常有帮助。
Six programming paradigms that will change how you think about coding,中文翻译版为 六个编程范型将改变你对编程的看法。这篇文章讲了默认支持并发(Concurrent by default)、依赖类型(Dependent types)、连接性语言(Concatenative languages)、声明式编程(Declarative programming)、符号式编程(Symbolic programming)、基于知识的编程(Knowledge-based programming)等六种不太常见的编程范式,并结合了一些你没怎么听说过的语言来分别进行讲述。
比如在讲 Concatenative languages 时,以 Forth、cat 和 joy 三种语言为例讲述这一编程范式背后的思想——语言中的所有内容都是一个函数,用于将数据推送到堆栈或从堆栈弹出数据;程序几乎完全通过功能组合来构建(concatenation is composition)。作者认为,这些编程范式背后的思想十分有魅力,能够改变对编程的思考。我看完此文,对此也深信不疑。虽然这些语言和编程范式不常用到,但确实能在思想层面给予人很大的启发。这也是我推荐此文的目的。
Programming Paradigms for Dummies: What Every Programmer Should Know ,这篇文章的作者彼得·范·罗伊(Peter Van Roy)是比利时鲁汶大学的计算机科学教师。他在这篇文章里分析了编程语言在历史上的演进,有哪些典型的、值得研究的案例,里面体现了哪些值得学习的范式。
比如,在分布式编程领域,他提到了 Erlang、E、Distributed Oz 和 Didactic Oz 这四种编程语言。虽然它们都是分布式编程语言,但各有特色,各自解决了不同的问题。通过这篇文章能学到不少在设计编程语言时要考虑的问题,让你重新审视自己所使用的编程语言应该怎样用才能用好,有什么局限性,这些局限性能否被克服等。
斯坦福大学公开课:编程范式,这是一门比较基础且很详细的课程,适合学习编程语言的初学者。它通过讲述 C、C++、并发编程、Scheme、Python 这 5 门语言,介绍了它们各自不同的编程范式。以 C 语言为例,它解释了 C 语言的基本要素,如指针、内存分配、堆、C 风格的字符串等,并解释了为什么 C 语言会在泛型编程、多态等方面有局限性。通过学习这门课程,你会对一些常用的编程范式有所了解。
Don’t Repeat Yourself (DRY) ,DRY 是一个最简单的法则,也是最容易被理解的。但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易的事)。它意味着,当在两个或多个地方发现一些相似代码的时候,我们需要把它们的共性抽象出来形成一个唯一的新方法,并且改变现有地方的代码让它们以一些合适的参数调用这个新的方法。
Keep It Simple, Stupid(KISS) ,KISS 原则在设计上可能最被推崇,在家装设计、界面设计和操作设计上,复杂的东西越来越被众人所鄙视了,而简单的东西越来越被人所认可。宜家(IKEA)简约、高效的家居设计和生产思路;微软(Microsoft)“所见即所得”的理念;谷歌(Google)简约、直接的商业风格,无一例外地遵循了“KISS”原则。也正是“KISS”原则,成就了这些看似神奇的商业经典。而苹果公司的 iPhone 和 iPad 将这个原则实践到了极至。
Program to an interface, not an implementation,这是设计模式中最根本的哲学,注重接口,而不是实现,依赖接口,而不是实现。接口是抽象是稳定的,实现则是多种多样的。在面向对象的 S.O.L.I.D 原则中会提到我们的依赖倒置原则,就是这个原则的另一种样子。还有一条原则叫 Composition over inheritance(喜欢组合而不是继承),这两条是那 23 个经典设计模式中的设计原则。
You Ain’t Gonna Need It (YAGNI) ,这个原则简而言之为——只考虑和设计必须的功能,避免过度设计。只实现目前需要的功能,在以后你需要更多功能时,可以再进行添加。如无必要,勿增复杂性。软件开发是一场 trade-off 的博弈。
Law of Demeter,迪米特法则 (Law of Demeter),又称“最少知识原则”(Principle of Least Knowledge),其来源于 1987 年荷兰大学的一个叫做 Demeter 的项目。克雷格·拉尔曼(Craig Larman)把 Law of Demeter 又称作“不要和陌生人说话”。在《程序员修炼之道》中讲 LoD 的那一章将其叫作“解耦合与迪米特法则”。
关于迪米特法则有一些很形象的比喻:1) 如果你想让你的狗跑的话,你会对狗狗说还是对四条狗腿说?2) 如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿?和狗的四肢说话?让店员自己从钱包里拿钱?这听起来有点儿荒唐,不过在我们的代码里这几乎是见怪不怪的事情了。对于 LoD,正式的表述如下:
对于对象 ‘O’ 中一个方法’M’,M 应该只能够访问以下对象中的方法:
[面向对象的 S.O.L.I.D 原则](">http://en.wikipedia.org/wiki/Solid_(object-oriented_design):
职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而极大地损伤其内聚性和耦合度。单一职责,通常意味着单一的功能,因此不要为一个模块实现过多的功能点,以保证实体只有一个引起它变化的原因。
如果我们把这些功能都声明在电脑的抽象类里面,那么,我们的上网本、PC 机、服务器和笔记本的实现类都要实现所有的这些接口,这就显得太复杂了。所以,我们可以把这些功能接口隔离开来,如工作学习接口、编程开发接口、上网娱乐接口、计算和数据服务接口,这样,我们的不同功能的电脑就可以有所选择地继承这些接口。
CCP(Common Closure Principle) - 共同封闭原则,一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类。一个更简短的说法是:一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,那么我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。
CCP 原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果两个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。CCP 延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里。
CRP(Common Reuse Principle)- 共同重用原则 ,包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换个说法是,没有被一起重用的类不应该组合在一起。CRP 原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。
当一个包发生了改变,并发布新的版本,使用这个包的所有用户都必须在新的包环境下验证他们的工作,即使被他们使用的部分没有发生任何改变。因为如果包中包含未被使用的类,即使用户不关心该类是否改变,但用户还是不得不升级该包并对原来的功能加以重新测试。CCP 则让系统的维护者受益。CCP 让包尽可能大(CCP 原则加入功能相关的类),CRP 则让包尽可能小(CRP 原则剔除不使用的类)。它们的出发点不一样,但不相互冲突。
好莱坞原则 - Hollywood Principle ,好莱坞原则就是一句话——“don’t call us, we’ll call you.”。意思是,好莱坞的经纪人不希望你去联系他们,而是他们会在需要的时候来联系你。也就是说,所有的组件都是被动的,所有的组件初始化和调用都由容器负责。
简单来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:1) 不创建对象,而是描述创建对象的方式。2)在代码中,对象与服务没有直接联系,而是容器负责将这些联系在一起。控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。好莱坞原则就是IoC(Inversion of Control) 或DI(Dependency Injection)的基础原则。
高内聚, 低耦合 & - High Cohesion & Low/Loose coupling,这个原则是 UNIX 操作系统设计的经典原则,把模块间的耦合降到最低,而努力让一个模块做到精益求精。内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。对于面向对象来说,你也可以看看马萨诸塞州戈登学院的面向对象课中的这一节讲义High Cohesion and Low Coupling。
CoC(Convention over Configuration)- 惯例优于配置原则 ,简单点说,就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如,Hibernate 的映射文件,如果约定字段名和类属性一致的话,基本上就可以不要这个配置文件了。你的应用只需要指定不 convention 的信息即可,从而减少了大量 convention 而又不得不花时间和精力啰里啰嗦的东东。
配置文件在很多时候相当影响开发效率。Rails 中很少有配置文件(但不是没有,数据库连接就是一个配置文件)。Rails 的 fans 号称其开发效率是 Java 开发的 10 倍,估计就是这个原因。Maven 也使用了 CoC 原则,当你执行 mvn -compile
命令的时候,不需要指定源文件放在什么地方,而编译以后的 class 文件放置在什么地方也没有指定,这就是 CoC 原则。
SoC (Separation of Concerns) - 关注点分离 ,SoC 是计算机科学中最重要的努力目标之一。这个原则,就是在软件开发中,通过各种手段,将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题,就是相对较易解决的。问题太过于复杂,要解决问题需要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。
正如程序员的记忆力相对于计算机知识来说那么有限一样,程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候,如果我们把所有的东西混在一起讨论,那么就只会有一个结果——乱。实现关注点分离的方法主要有两种,一种是标准化,另一种是抽象与包装。标准化就是制定一套标准,让使用者都遵守它,将人们的行为统一起来,这样使用标准的人就不用担心别人会有很多种不同的实现,使自己的程序不能和别人的配合。
就像是开发镙丝钉的人只专注于开发镙丝钉就行了,而不用关注镙帽是怎么生产的,反正镙帽和镙丝钉按照标准来就一定能合得上。不断地把程序的某些部分抽象并包装起来,也是实现关注点分离的好方法。一旦一个函数被抽象出来并实现了,那么使用函数的人就不用关心这个函数是如何实现的。同样的,一旦一个类被抽象并实现了,类的使用者也不用再关注于这个类的内部是如何实现的。诸如组件、分层、面向服务等这些概念都是在不同的层次上做抽象和包装,以使得使用者不用关心它的内部实现细节。
DbC(Design by Contract)- 契约式设计 ,DbC 的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。如果在程序设计中一个模块提供了某种功能,那么它要:
ADP(Acyclic Dependencies Principle)- 无环依赖原则 ,包(或服务)之间的依赖结构必须是一个直接的无环图形,也就是说,在依赖结构中不允许出现环(循环依赖)。如果包的依赖形成了环状结构,怎么样打破这种循环依赖呢?
有两种方法可以打破这种循环依赖关系:第一种方法是创建新的包,如果 A、B、C 形成环路依赖,那么把这些共同类抽出来放在一个新的包 D 里。这样就把 C 依赖 A 变成了 C 依赖 D 以及 A 依赖 D,从而打破了循环依赖关系。第二种方法是使用 DIP(依赖倒置原则)和 ISP(接口分隔原则)设计原则。无环依赖原则(ADP)为我们解决包之间的关系耦合问题。在设计模块时,不能有循环依赖。
这一篇章,是本系列中最长的一篇,其中包括了如下的内容。
另外,这里需要说明几点。
今天分享的内容为系统底层知识中的 Linux 系统、内存和网络等方面的相关知识及推荐的学习资料。
学习 Linux 操作系统的原理是通向系统工程师的必经之路。我觉得,Unix/Linux 操作系统里的东西并不难学。你千万不要一下子扎到源代码里去,那样没用——你还是要在上层先通过读一些不错的文档来学习。下面我罗列了一些很不错的站点,其中有很多内容供你去钻研和探索。
我在这里默认你前面已经读过并读懂了我推荐的那些和 Unix/Linux 相关的图书了。所以,我相信你对 Unix/Linux 下的编程已经是有一些基础了,因此,你继续深挖 Linux 下的这些知识应该也不是很难的事了。
计算机内存管理是每一个底层程序员需要了解的非常重要的事儿。当然,这里我们重点还是 Linux 操作系统相关的内存管理上的知识。
首先,LWN.net 上有一系列的 “What every programmer should know about memory” 文章你需要读一下。当然,你可以直接访问一个完整的 PDF 文档。下面是这个系列文章的网页版列表。读完这个列表的内容,你基本上就对内存有了一个比较好的知识体系了。
然后是几篇和内存相关的论文。下面这三篇论文是我个人觉得能对你非常有帮助的文章,尤其是你要做一些程序的性能优化方面。
接下来是开发者最关心的内存管理方面的 lib 库。通常来说,我们有三种内存分配管理模块。就目前而言,BSD 的 jemalloc 有很大的影响力。后面我们可以看到不同公司的实践性文章。
关于 C 的这些内存分配器,你可以参看 Wikipedia 的 “C Dynamic Memory Allocation”这个词条。
下面是几篇不错的文章,让你感觉一下上面那三种内存分配器的一些比较和工程实践。
首先,推荐一本书——《计算机网络(第五版)》,这本“计算机网络”和前面推荐的那本计算机网络不一样,前面那本偏扫盲,这本中有很多细节。这本书是国内外使用最广泛、最权威的计算机网络经典教材。全书按照网络协议模型自下而上(物理层、数据链路层、介质访问控制层、网络层、传输层和应用层)有系统地介绍了计算机网络的基本原理,并结合 Internet 给出了大量的协议实例。
这本书还与时俱进地引入了最新的网络技术,包括无线网络、3G 蜂窝网络、RFID 与传感器网络、内容分发与 P2P 网络、流媒体传输与 IP 语音,以及延迟容忍网络等。另外,本书针对当前网络应用中日益突出的安全问题,用了一整章的篇幅对计算机网络的安全性进行了深入讨论,而且把相关内容与最新网络技术结合起来阐述。这本书读起来并不枯燥,因为其中有很多小故事和小段子。
然后,有两个网上的教程和讲义也可以让人入门。
接下来,你可能需要一些非常实用的可以操作的技术,下面的几篇文章相信可以帮助到你。
接下来,想要学习网络协议最好的方式就是学习通讯相关的 RFC。所以,在这里我会推荐一系列值得读的 RFC 给你。读 RFC 有几个好处,一方面可以学习技术,另一方面,你可以通过 RFC 学习到一个好的技术文档是怎么写的,还能看到各种解决问题的方案和思路。
对于第 2 层链路层,你可能需要了解一下 ARP:
以及 Tunnel 相关的协议:
对于第 4 层,你最需要了解的是 TCP/IP 了。和 TCP 相关的 RFC 相当多,这里给一系列经典的 RFC。这些 RFC 我都引用在了我在 CoolShell 上的《TCP 的那些事儿(上)》和《TCP 的那些事儿(下)》两篇文章中。如果你看不懂 RFC,你也可以去看我上述的文章。
我个人觉得 TCP 最牛的不是不丢包,而是拥塞控制。对此,如果你感兴趣,可以读一下经典论文《Congestion Avoidance and Control》。
关于 Linux 下的 TCP 参数,你需要仔仔细细地读一下TCP 的 man page 。
对于第 7 层协议,HTTP 协议是重点要学习的。
首先推荐的是《HTTP 权威指南 》,这本书有点厚,可以当参考书来看。这本书中没有提到 HTTP/2 的事,但是可以让你了解到 HTTP 协议的绝大多数特性。
HTTP 1.1 的原始 RFC 是 1999 年 6 月的 RFC 2616,但其在 2014 后很快被下面这些 RFC 给取代了。
关于HTTP/2,这是 HTTP 的一个比较新的协议,它于 2015 年被批准通过,现在基本上所有的主流浏览器都默认启用这个协议。所以,你有必要学习一下这个协议。下面是相关的学习资源。
最后,你可以上 Wikipedia 的 Internet Protocol Suite 上看看,这是一个很不错的网络协议的词条汇集地。顺着这些协议,你可以找到很多有用的东西。
异步 I/O 模型是我个人觉得所有程序员都必需要学习的一门技术或是编程方法,这其中的设计模式或是解决方法可以借鉴到分布式架构上来。再说一遍,学习这些模型,是非常非常重要的,你千万要认真学习。
史蒂文斯(Stevens)在《UNIX 网络编程》一书 6.2 I/O Models 中介绍了五种 I/O 模型。
然后,在前面我们也阅读过了 - C10K Problem 。相信你对 I/O 模型也有了一定的了解。 这里,我们需要更为深入地学习 I/O 模型,尤其是其中的异步 I/O 模型。
首先,我们看一篇和 Java 相关的 I/O 模型的文章来复习一下之前的内容。Thousands of Threads and Blocking I/O: The Old Way to Write Java Servers Is New Again (and Way Better) ,这个 PPT 中不仅回顾和比较了各种 I/O 模型,而且还有各种比较细节的方案和说明,是一篇非常不错的文章。
然后,你可以看一篇 Java 相关的 PPT - 道格·莱亚(Doug Lea)的 Scalable IO in Java,这样你会对一些概念有个了解。
接下来,我们需要了解一下各种异步 I/O 的实现和设计方式。
我简单总结一下,基本上来说,异步 I/O 模型的发展技术是: select -> poll -> epoll -> aio -> libevent -> libuv。Unix/Linux 用了好几十年走过这些技术的变迁,然而,都不如 Windows I/O Completion Port 设计得好(免责声明:这个观点纯属个人观点。相信你仔细研究这些 I/O 模型后,你会有自己的判断)。
看过这些各种异步 I/O 模式的实现以后,相信你会看到一个编程模式——Reactor 模式。下面是这个模式的相关文章(读这三篇就够了)。
然后是几篇有意思的延伸阅读文章。
Lock-Free - 无锁技术越来越被开发人员重视,因为锁对于性能的影响实在是太大了,所以如果想开发出一个高性能的程序,你就非常有必要学习 Lock-Free 的编程方式。
关于无锁的数据结构,有几篇教程你可以看一下。
然后强烈推荐一本免费的电子书:Is Parallel Programming Hard, And, If So, What Can You Do About It? ,这是大牛 保罗·麦肯尼(Paul E. McKenney) 写的书。这本书堪称并行编程的经典书,必看。
此时,Wikipedia 上有三个词条你要看一下,以此了解并发编程中的一些概念:Non-blocking algorithm 、Read-copy-update 和 Seqlock。
接下来,读一下以下两篇论文 。
最后,有几个博客你要订阅一下。
接下来,是一些编程相关的一些 C/C++ 的类库,这样你就不用从头再造轮子了(对于 Java 的,请参看 JDK 里的 Concurrent 开头的一系列的类)。
关于 64 位系统编程,只要去一个地方就行了: All about 64-bit programming in one place,这是一个关于 64 位编程相关的收集页面,其中包括相关的文章、28 节课程,还有知识库和相关的 blog。
What Scalable Programs Need from Transactional Memory ,事务性内存(TM)一直是许多研究的重点,它在诸如 IBM Blue Gene/Q 和 Intel Haswell 等处理器中得到了支持。许多研究都使用 STAMP 基准测试套件来评估其设计。然而,我们所知的所有 TM 系统上的 STAMP 基准测试所获得的加速比较有限。
例如,在 IBM Blue Gene/Q 上有 64 个线程,我们观察到使用 Blue Gene/Q 硬件事务内存(HTM)的中值加速比为 1.4 倍,使用软件事务内存(STM)的中值加速比为 4.1 倍。什么限制了这些 TM 基准的性能?在本论文中,作者认为问题在于用于编写它们的编程模型和数据结构上,只要使用合适的模型和数据结构,程序的性能可以有 10 多倍的提升。
Improving OpenSSL Performance ,这篇文章除了教你如何提高 OpenSSL 的执行性能,还讲了一些底层的性能调优知识。
关于压缩的内容。为了避免枯燥,主要推荐下面这两篇实践性很强的文章。
这里有两篇关于 SSD 硬盘性能测试的文章。Performance Testing with SSDs, Part 1 和 Performance Testing with SSDs Part 2 ,这两篇文章介绍了测试 SSD 硬盘性能以及相关的操作系统调优方法。
Secure Programming HOWTO - Creating Secure Software ,这是一本电子书,其中有繁体中文的翻译,这本电子书讲了 Linux/Unix 下的一些安全编程方面的知识。
Hints for Computer System Design ,计算机设计的忠告,这是 ACM 图灵奖得主 Butler Lampson 在 Xerox PARC 工作时的一篇论文。这篇论文简明扼要地总结了他在做系统设计时的一些想法,非常值得一读。(用他的话来说,“Studying the design and implementation of a number of computer has led to some general hints for system design. They are described here and illustrated by many examples, ranging from hardware such as the Alto and the Dorado to application programs such as Bravo and Star“。)
The 5 minute rule for trading memory for disc accesses and the 5 byte rule for trading memory for CPU time ,根据文章名称也可以看出,5 分钟法则是用来衡量内存与磁盘的,而 5 字节法则则是在内存和 CPU 之间的权衡。这两个法则是 Jim Gray 和 Franco Putzolu 在 1986 年的文章。
在该论文发表 10 年后的 1997 年,Jim Gray 和 Goetz Graefe 又在 The Five-Minute Rule Ten Years Later and Other Computer Storage Rules of Thumb 中对该法则进行了重新审视。2007 年,也就是该论文发表 20 年后,这年的 1 月 28 日,Jim Gray 驾驶一艘 40 英尺长的船从旧金山港出海,目的是航行到附近的费拉隆岛,在那里撒下母亲的骨灰。出海之后,他就同朋友和亲属失去了联系。为了纪念和向大师致敬,时隔 10 多年后的 2009 年 Goetz Graefe 又发表了 The Five-Minute Rule 20 Years Later (and How Falsh Memory Changes the Rules)。
注明一下,Jim Gray 是关系型数据库领域的大师。因在数据库和事务处理研究和实现方面的开创性贡献而获得 1998 年图灵奖。美国科学院、工程院两院院士,ACM 和 IEEE 两会会士。他 25 岁成为加州大学伯克利分校计算机科学学院第一位博士。在 IBM 工作期间参与和主持了 IMS、System R、SQL/DS、DB2 等项目的开发。后任职于微软研究院,主要关注应用数据库技术来处理各学科的海量信息。
好了,总结一下今天的内容。异步 I/O 模型是我个人觉得所有程序员都必需要学习的一门技术或是编程方法,这其中的设计模式或是解决方法可以借鉴到分布式架构上来。而且我认为,学习这些模型非常重要,你千万要认真学习。
接下来是 Lock-Free 方面的内容,由于锁对于性能的影响实在是太大了,所以它越来越被开发人员所重视。如果想开发出一个高性能的程序,你非常有必要学习 Lock-Free 的编程方式。随后,我给出系统底层方面的其它一些重要知识,如 64 位编程、提高 OpenSSL 的执行性能、压缩、SSD 硬盘性能测试等。最后介绍了几篇我认为对学习和巩固这些知识非常有帮助的论文,都很经典,推荐你务必看看。
前两篇文章分享的是系统底层方面的内容,今天我们进入高手成长篇的第二部分——Java 底层知识。
首先,Java 最黑科技的玩法就是字节码编程,也就是动态修改或是动态生成 Java 字节码。Java 的字节码相当于汇编,其中的一些细节你可以从下面的这几个教程中学习。
当然,一般来说,我们不使用 JVMTI 操作字节码,而是用一些更好用的库。这里有三个库可以帮你比较容易地做这个事。
就我而言,我更喜欢 Byte Buddy,它在 2015 年还获了 Oracle 的 “Duke’s Choice”大奖,其中说 Byte Buddy 极大地发展了 Java 的技术。
使用字节码编程可以玩出很多高级玩法,最高级的还是在 Java 程序运行时进行字节码修改和代码注入。听起来是不是一些很黑客,也很黑科技的事?是的,这个方式使用 Java 这门静态语言在运行时可以进行各种动态的代码修改,而且可以进行无侵入的编程。
比如, 我们不需要在代码中埋点做统计或监控,可以使用这种技术把我们的监控代码直接以字节码的方式注入到别人的代码中,从而实现对实际程序运行情况进行统计和监控。如果你看过我的《编程范式游记》,你就知道这种技术的威力了,其可以很魔法地把业务逻辑和代码控制分离开来。
要做到这个事,你还需要学习一个叫 Java Agent 的技术。Java Agent 使用的是 “Java Instrumentation API”,其主要方法是实现一个叫 premain()
的方法(嗯,一个比 main()
函数还要超前执行的 main 函数),然后把你的代码编译成一个 jar 文件。
在 JVM 启动时,使用这样的命令行来引入你的 jar 文件:java -javaagent:yourAwesomeAgent.jar -jar App.jar
。更为详细的文章你可以参看:“Java Code Geeks: Java Agents”,你还可以看一下这个示例项目:jvm-monitoring-agent 或是 EntryPointKR/Agent.java。如果想用 ByteBuddy 来玩,你可以看看这篇文章 “通过使用 Byte Buddy,便捷地创建 Java Agent”。如果你想学习如何用 Java Agent 做监控,你可以看一下这个项目 Stage Monitor。
接下来讲讲 Java 底层知识中另一个非常重要的内容——JVM。
说起 JVM,你有必要读一下 JVM 的规格说明书,我在这里放一个 Java 8 的, The Java Virtual Machine Specification Java SE 8 Edition 。对于规格说明书的阅读,我认为是系统了解 JVM 规范的最佳文档,这个文档可以让你对于搞不清楚或是诡异的问题恍然大悟。关于中文翻译,有人在 GitHub 上开了个 Repo - “java-virtual-machine-specification”。
另外,也推荐一下 JVM Anatomy Park JVM 解剖公园,这是一个系列的文章,每篇文章都不长,但是都很精彩,带你一点一点地把 JVM 中的一些技术解开。
学习 Java 底层原理还有 Java 的内存模型,官方文章是 JSR 133。还有马里兰大学的威廉·皮尤(William Pugh)教授收集的和 Java 内存模型相关的文献 - The Java Memory Model ,你可以前往浏览。
对于内存方面,道格·利(Doug Lea)有两篇文章也是很有价值的。
垃圾回收机制也是需要好好学习的,在这里推荐一本书 《The Garbage Collection Handbook》,在豆瓣上的得分居然是 9.9(当然,评价人数不多)。这本书非常全面地介绍了垃圾收集的原理、设计和算法。但是这本书也是相当难啃的。中文翻译《垃圾回收算法手册》翻译得很一般,有人说翻译得很烂。所以,如果可能,还是读英文版的。如果你对从事垃圾回收相关的工作有兴趣,那么你需要好好看一下这本书。
当然,更多的人可能只需要知道怎么调优垃圾回收, 那么推荐读读 Garbage Collection Tuning Guide ,它是 Hotspot Java 虚拟机的垃圾回收调优指南,对你很有帮助。
Quick Tips for Fast Code on the JVM 也是一篇很不错的文章,里面有写出更快的 Java 代码的几个小提示,值得一读。
好了,总结一下今天学到的内容。Java 最黑科技的玩法就是字节码编程,也就是动态修改或是动态生成 Java 字节码。Java 的字节码相当于汇编,学习其中的细节很有意思,为此我精心挑选了 3 篇文章,供你学习。我们一般不使用 JVMTI 操作字节码,而是用一些更好用的库,如 asmtools、Byte Buddy 和 BiteScript 等。使用字节码编程可以玩出很多高级玩法,其中最高级的玩法是在 Java 程序运行时进行字节码修改和代码注入。同时,我介绍了 Java Agent 技术,帮助你更好地实现这种高级玩法。
JVM 也是学习 Java 过程中非常重要的一部分内容。我推荐阅读一下 JVM 的规格说明书,我认为,它是系统了解 JVM 规范的最佳文档,可以让你对于搞不清楚或是诡异的问题恍然大悟。同时推荐了 JVM Anatomy Park 系列文章,也非常值得一读。
随后介绍的是 Java 的内存模型和垃圾回收机制,尤其给出了如何调优垃圾回收方面的资料。这些内容都很底层,但也都很重要。对于想成为高手的你来说,还是有必要花时间来啃一啃的。
对于数据库方向,重点就是两种数据库,一种是以 SQL 为代表的关系型数据库,另一种是以非 SQL 为代表的 NoSQL 数据库。关系型数据库主要有三个:Oracle、MySQL 和 Postgres。
在这里,我们只讨论越来越主流的 MySQL 数据库。首先,我们要了解数据库的一些实现原理和内存的一些细节,然后我们要知道数据的高可用和数据复制这些比较重要的话题,了解一下关系型数据库的一些实践和难点。然后,我们会进入到 NoSQL 数据库的学习。
NoSQL 数据库千奇百怪,其主要是解决了关系型数据库中的各种问题。第一个大问题就是数据的 Schema 非常多,用关系型数据库来表示不同的 Data Schema 是非常笨拙的,所以要有不同的数据库(如时序型、键值对型、搜索型、文档型、图结构型等)。另一个大问题是,关系型数据库的 ACID 是一件很讨厌的事,这极大地影响了数据库的性能和扩展性,所以 NoSQL 在这上面做了相应的妥协以解决大规模伸缩的问题。
对于一个程序员,你可能觉得数据库的事都是 DBA 的事,然而我想告诉你你错了,这些事才真正是程序员的事。因为程序是需要和数据打交道的,所以程序员或架构师不仅需要设计数据模型,还要保证整体系统的稳定性和可用性,数据是整个系统中关键中的关键。所以,作为一个架构师或程序员,你必须了解最重要的数据存储——数据库。
今天,关系型数据库最主要的两个代表是闭源的 Oracle 和开源的 MySQL。当然,还有很多了,比如微软的 SQL Server,IBM 的 DB2 等,还有开源的 PostgreSQL。关系型数据库的世界中有好多好多产品。当然,还是 Oracle 和 MySQL 是比较主流的。所以,这里主要介绍更为开放和主流的 MySQL。
如果你要玩 Oracle,我这里只推荐一本书《Oracle Database 9i/10g/11g 编程艺术》,无论是开发人员还是 DBA,它都是必读的书。这本书的作者是 Oracle 公司的技术副总裁托马斯·凯特(Thomas Kyte),他也是世界顶级的 Oracle 专家。
这本书中深入分析了 Oracle 数据库体系结构,包括文件、内存结构以及构成 Oracle 数据库和实例的底层进程,利用具体示例讨论了一些重要的数据库主题,如锁定、并发控制、事务等。同时分析了数据库中的物理结构,如表、索引和数据类型,并介绍采用哪些技术能最优地使用这些物理结构。
学习 MySQL,首先一定是要看MySQL 官方手册。
然后,官方还有几个 PPT 也要学习一下。
然后推荐《高性能 MySQL》,这本书是 MySQL 领域的经典之作,拥有广泛的影响力。不但适合数据库管理员(DBA)阅读,也适合开发人员参考学习。不管是数据库新手还是专家,都能从本书中有所收获。
如果你对 MySQL 的内部原理有兴趣的话,可以看一下这本书《MySQL 技术内幕:InnoDB 存储引擎》。当然,还有官网的MySQL Internals Manual 。
数据库的索引设计和优化也是非常关键的,这里还有一本书《数据库的索引设计与优化》也是很不错的。虽然不是讲 MySQL 的,但是原理都是相通的。这也是上面推荐过的《高性能 MySQL》在其索引部分推荐的一本好书。
你千万不要觉得只有做数据库你才需要学习这种索引技术。不是的!在系统架构上,在分布式架构中,索引技术也是非常重要的。这本书对于索引性能进行了非常清楚的估算,不像其它书中只是模糊的描述,你一定会收获很多。
下面还有一些不错的和 MySQL 相关的文章。
最后,还有一个 MySQL 的资源列表 Awesome MySQL,这个列表中有很多的工具和开发资源,可以帮助你做很多事。
MySQL 有两个比较有名的分支,一个是 Percona,另一个是 MariaDB,其官网上的 Resources 页面中有很多不错的资源和文档,可以经常看看。 Percona Resources、MariaDB Resources ,以及它们的开发博客中也有很多不错的文章,分别为 Percona Blog 和 MariaDB Blog。
然后是关于 MySQL 的一些相关经验型的文章。
关于 MySQL 的集群复制,下面有这些文章供你学习一下,都是很不错的实践性比较强的文章。
Better Parallel Replication for MySQL
Evaluating MySQL Parallel Replication Part 2: Slave Group Commit
Evaluating MySQL Parallel Replication Part 3: Benchmarks in Production
[Evaluating MySQL Parallel Replication Part 4: More Benchmarks in Production
](https://medium.com/booking-com-infrastructure/evaluating-mysql-parallel-replication-part-4-more-benchmarks-in-production-49ee255043ab)
Evaluating MySQL Parallel Replication Part 4, Annex: Under the Hood
对于 MySQL 的数据分区来说,还有下面几篇文章你可以看看。
然后,再看看各个公司做 MySQL Sharding 的一些经验分享。
[MailChimp: Using Shards to Accommodate Millions of Users
](https://devs.mailchimp.com/blog/using-shards-to-accommodate-millions-of-users/)
Uber: Code Migration in Production: Rewriting the Sharding Layer of Uber’s Schemaless Datastore
Sharding & IDs at Instagram
Airbnb: How We Partitioned Airbnb’s Main Database in Two Weeks
关于 NoSQL 数据库,其最初目的就是解决大数据的问题。然而,也有人把其直接用来替换掉关系型数据库。所以在学习这个技术之前,我们需要对这个技术的一些概念和初衷有一定的了解。下面是一些推荐资料。
选 SQL 还是 NoSQL,这里有两篇文章,值得你看看。
学习使用 NoSQL 数据库其实并不是一件很难的事,只要你把官方的文档仔细地读一下,是很容易上手的,而且大多数 NoSQL 数据库都是开源的,所以,也可以通过代码自己解决问题。下面我主要给出一些典型的 NoSQL 数据库的一些经验型的文章,供你参考。
列数据库 Column Database
针对于 HBase 有两本书你可以考虑一下。
文档数据库 Document Database - MongoDB, SimpleDB, CouchDB
数据结构数据库 Data structure Database - Redis
时序数据库 Time-Series Database
图数据库 - Graph Platform
搜索数据库 - ElasticSearch
好了,总结一下今天分享的内容。虽然有人会认为数据库与程序员无关,是 DBA 的事儿。但我坚信,数据库才真正是程序员的事儿。因为程序是需要和数据打交道的,所以程序员或架构师不仅需要设计数据模型,还要保证整体系统的稳定性和可用性,数据是整个系统中关键中的关键。
对于数据库方向,重点就是两种数据库,一种是以 SQL 为代表的关系型数据库,另一种是以非 SQL 为代表的 NoSQL 数据库。因而,在这篇文章中,我给出了 MySQL 和各种开源 NoSQL 的一些相关的有价值的文章和导读,主要是让你对这些数据库的内在有一定的了解,但又不会太深。同时给出了一些知名企业使用数据库的工程实践,这对于了解各种数据库的优劣非常有帮助,值得认真读读。
学习分布式系统跟学习其它技术非常不一样,分布式系统涵盖的面非常广,具体来说涵盖如下几方面:
所有这些形成了分布式架构的整体复杂度,也造就了分布式系统中的很多很多论文、图书以及很多很多的项目。要学好分布式系统及其架构,我们需要大量的时间和实践才能真正掌握这些技术。
这里有几点需要你注意一下。
总之,在分布式环境下,一切都变得非常复杂。要进入这个领域,你需要有足够多的耐性和足够强的心态来接受各式各样的失败。当拥有丰富的实践和经验后,你才会有所建树。这并不是一日之功,你可能要在这个领域花费数年甚至数十年的时间。
学习如何设计可扩展的架构将会有助于你成为一个更好的工程师。系统设计是一个很宽泛的话题。在互联网上,关于架构设计原则的资源也是多如牛毛。所以,你需要知道一些基本概念,对此,这里你先阅读下面两篇文章。
然后,我更强烈推荐 GitHub 上的一篇文档 - System Design Primer ,这个仓库主要组织收集分布式系统的一些与扩展性相关的资源,它可以帮助你学习如何构建可扩展的架构。
目前这个仓库收集到了好些系统架构和设计的基本方法。其中包括:CAP 理论、一致性模型、可用性模式、DNS、CDN、负载均衡、反向代理、应用层的微服务和服务发现、关系型数据库和 NoSQL、缓存、异步通讯、安全等。
我认为,上面这几篇文章基本足够可以让你入门了,因为其中基本涵盖了所有与系统架构相关的技术。这些技术,足够这世上 90% 以上的公司用了,只有超级巨型的公司才有可能使用更高层次的技术。
下面,我们来学习一下分布式方面的理论知识。
首先,你需要看一下 An introduction to distributed systems。 这只是某个教学课程的提纲,我觉得还是很不错的,几乎涵盖了分布式系统方面的所有知识点,而且辅以简洁并切中要害的说明文字,非常适合初学者提纲挈领地了解知识全貌,快速与现有知识结合,形成知识体系。这也是一个分布式系统的知识图谱,可以让你看到分布式系统的整体全貌。你可以根据这个知识图 Google 下去,然后你会学会所有的东西。
然后,你需要了解一下拜占庭将军问题(Byzantine Generals Problem)。这个问题是莱斯利·兰波特(Leslie Lamport)于 1982 年提出用来解释一致性问题的一个虚构模型(论文地址)。拜占庭是古代东罗马帝国的首都,由于地域宽广,守卫边境的多个将军(系统中的多个节点)需要通过信使来传递消息,达成某些一致的决定。但由于将军中可能存在叛徒(系统中节点出错),这些叛徒将努力向不同的将军发送不同的消息,试图会干扰一致性的达成。拜占庭问题即为在此情况下,如何让忠诚的将军们能达成行动的一致。
对于拜占庭问题来说,假如节点总数为 N
,叛变将军数为 F
,则当 N >= 3F + 1
时,问题才有解,即拜占庭容错(Byzantine Fault Tolerant,BFT)算法。拜占庭容错算法解决的是,网络通信可靠但节点可能故障情况下一致性该如何达成的问题。
最早由卡斯特罗(Castro)和利斯科夫(Liskov)在 1999 年提出的实用拜占庭容错(Practical Byzantine Fault Tolerant,PBFT)算法,是第一个得到广泛应用的 BFT 算法。只要系统中有 2/3 的节点是正常工作的,则可以保证一致性。PBFT 算法包括三个阶段来达成共识:预准备(Pre-Prepare)、准备(Prepare)和提交(Commit)。
这里有几篇和这个问题相关的文章,推荐阅读。
拜占庭容错系统研究中有三个重要理论:CAP、FLP 和 DLS。
CAP 定理,CAP 理论相信你应该听说过不下 N 次了。CAP 定理是分布式系统设计中最基础也是最为关键的理论。CAP 定理指出,分布式数据存储不可能同时满足以下三个条件:一致性(Consistency)、可用性(Availability)和 分区容忍(Partition tolerance)。 “在网络发生阻断(partition)时,你只能选择数据的一致性(consistency)或可用性(availability),无法两者兼得”。
论点比较直观:如果网络因阻断而分隔为二,在其中一边我送出一笔交易:“将我的十元给 A”;在另一半我送出另一笔交易:“将我的十元给 B”。此时系统要不是,a)无可用性,即这两笔交易至少会有一笔交易不会被接受;要不就是,b)无一致性,一半看到的是 A 多了十元而另一半则看到 B 多了十元。要注意的是,CAP 理论和扩展性(scalability)是无关的,在分片(sharded)或非分片的系统皆适用。
FLP impossibility,在异步环境中,如果节点间的网络延迟没有上限,只要有一个恶意的节点存在,就没有算法能在有限的时间内达成共识。但值得注意的是, “Las Vegas” algorithms(这个算法又叫撞大运算法,其保证结果正确,只是在运算时所用资源上进行赌博,一个简单的例子是随机快速排序,它的 pivot 是随机选的,但排序结果永远一致)在每一轮皆有一定机率达成共识,随着时间增加,机率会越趋近于 1。而这也是许多成功的共识算法会采用的解决问题的办法。
容错的上限,从DLS 论文 中我们可以得到以下结论:
当然,还有一个著名的“8 条荒谬的分布式假设(Fallacies of Distributed Computing)”。
阿尔农·罗特姆 - 盖尔 - 奥兹(Arnon Rotem-Gal-Oz)写了一篇长文 Fallacies of Distributed Computing Explained 来解释为什么这些观点是错误的。另外,加勒思·威尔逊(Gareth Wilson)的文章 则用日常生活中的例子,对这些点做了通俗的解释。为什么我们深刻地认识到这 8 个错误?是因为,这要我们清楚地认识到——在分布式系统中错误是不可能避免的,我们在分布式系统中,能做的不是避免错误,而是要把错误的处理当成功能写在代码中。
下面分享几篇一致性方面的论文。
当然,关于经典的 CAP 理论,也存在一些误导的地方,这个问题在 2012 年有一篇论文 CAP Twelve Years Later: How the Rules Have Changed (中译版)中做了一些讨论,主要是说,在 CAP 中最大的问题就是分区,也就是 P,在 P 发生的情况下,非常难以保证 C 和 A。然而,这是强一致性的情况。
其实,在很多时候,我们并不需要强一致性的系统,所以后来,人们争论关于数据一致性和可用性时,主要是集中在强一致性的 ACID 或最终一致性的 BASE。当时,BASE 还不怎么为世人所接受,主要是大家都觉得 ACID 是最完美的模型,大家很难接受不完美的 BASE。在 CAP 理论中,大家总是觉得需要“三选二”,也就是说,P 是必选项,那“三选二”的选择题不就变成数据一致性 (consistency)、服务可用性 (availability) 间的“二选一”?
然而,现实却是,P 很少遇到,而 C 和 A 这两个事,工程实践中一致性有不同程度,可用性也有不同等级,在保证分区容错性的前提下,放宽约束后可以兼顾一致性和可用性,两者不是非此即彼。其实,在一个时间可能允许的范围内是可以取舍并交替选择的。
Harvest, Yield, and Scalable Tolerant Systems ,这篇论文是基于上面那篇“CAP 12 年后”的论文写的,它主要提出了 Harvest 和 Yield 概念,并把上面那篇论文中所讨论的东西讲得更为仔细了一些。
Base: An Acid Alternative (中译版),本文是 eBay 的架构师在 2008 年发表给 ACM 的文章,是一篇解释 BASE 原则,或者说最终一致性的经典文章。文中讨论了 BASE 与 ACID 原则的基本差异, 以及如何设计大型网站以满足不断增长的可伸缩性需求,其中有如何对业务做调整和折中,以及一些具体的折中技术的介绍。一个比较经典的话是——“在对数据库进行分区后, 为了可用性(Availability)牺牲部分一致性(Consistency)可以显著地提升系统的可伸缩性 (Scalability)”。
Eventually Consistent ,这篇文章是 AWS 的 CTO 维尔纳·沃格尔(Werner Vogels)在 2008 年发布在 ACM Queue 上的一篇数据库方面的重要文章,阐述了 NoSQL 数据库的理论基石——最终一致性,对传统的关系型数据库(ACID,Transaction)做了较好的补充。
好了,总结一下今天分享的内容。文章的开头,我给出了学习分布式架构需要注意的几个关键点,然后列出了入门学习的资源,基本涵盖了所有与系统架构相关的技术。随后讲述了拜占庭容错系统研究中有三个重要理论:CAP、FLP 和 DLS,以及 8 条荒谬的分布式假设,从理论和认知等角度让你更为清楚地理解分布式系统。最后分享了几篇一致性相关的论文,很实用很经典,推荐阅读。
首先,我推荐几本分布式架构方面的经典图书。
Distributed Systems for fun and profit,这是一本免费的电子书。作者撰写此书的目的是希望以一种更易于理解的方式,讲述以亚马逊的 Dynamo、谷歌的 Bigtable 和 MapReduce 等为代表的分布式系统背后的核心思想。
Designing Data Intensive Applications,这本书是一本非常好的书,我们知道,在分布式的世界里,数据结点的扩展是一件非常麻烦的事。这本书深入浅出地用很多的工程案例讲解了如何让数据结点做扩展。作者马丁·科勒普曼(Martin Kleppmann)在分布式数据系统领域有着很深的功底,并在这本书中完整地梳理各类纷繁复杂设计背后的技术逻辑,不同架构之间的妥协与超越,很值得开发人员与架构设计者阅读。
这本书深入到 B-Tree、SSTables、LSM 这类数据存储结构中,并且从外部的视角来审视这些数据结构对 NoSQL 和关系型数据库的影响。这本书可以让你很清楚地了解到真正世界的大数据架构中的数据分区、数据复制的一些坑,并提供了很好的解决方案。最赞的是,作者将各种各样技术的本质非常好地关联在一起,令你触类旁通。
而且,这本书完全就是抽丝剥茧,循循善诱,从“提出问题”到“解决问题”、“解决方案”、“优化方案”和“对比不同的方案”,一点一点地把非常晦涩的技术和知识展开。本书的引用相当多,每章后面都有几百个 Reference,通过这些 Reference 你可以看到更为广阔、更为精彩的世界。
[Distributed Systems: Principles and Paradigms](http://barbie.uta.edu/~jli/Resources/MapReduce&Hadoop/Distributed Systems Principles and Paradigms.pdf) ,本书是由计算机科学家安德鲁·斯图尔特·塔能鲍姆(Andrew S. Tanenbaum)和其同事马丁·范·斯蒂恩(Martin van Steen)合力撰写的,是分布式系统方面的经典教材。
语言简洁,内容通俗易懂,介绍了分布式系统的七大核心原理,并给出了大量的例子;系统讲述了分布式系统的概念和技术,包括通信、进程、命名、同步化、一致性和复制、容错以及安全等;讨论了分布式应用的开发方法(即范型)。但本书不是一本指导“如何做”的手册,仅适合系统性地学习基础知识,了解编写分布式系统的基本原则和逻辑。中文翻译版为《分布式系统原理与范型》(第二版)。
Scalable Web Architecture and Distributed Systems,
这是一本免费的在线小册子,其中文翻译版 可扩展的 Web 架构和分布式系统。本书主要针对面向互联网(公网)的分布式系统,但其中的原理或许也可以应用于其他分布式系统的设计中。作者的观点是,通过了解大型网站的分布式架构原理,小型网站的构建也能从中受益。本书从大型互联网系统的常见特性,如高可用、高性能、高可靠、易管理等出发,引出了一个类似于 Flickr 的典型的大型图片网站的例子。
Principles of Distributed Systems ,本书是苏黎世联邦理工学院的教材。它讲述了多种分布式系统中会用到的算法。虽然分布式系统的不同场景会用到不同算法,但并不表示这些算法都会被用到。不过,作为学生来说,掌握了算法设计的精髓也就能举一反三地设计出解决其他问题的算法,从而得到分布式系统架构设计中所需的算法。
想了解分布式模型中最难的“分布式事务”,你需要看看 Google App Engine 联合创始人瑞恩·巴雷特(Ryan Barrett)在 2009 年的 Google I/O 大会上的演讲《Transaction Across DataCenter》(YouTube 视频)。
在这个演讲中,巴雷特讲述了各种经典的解决方案如何在一致性、事务、性能和错误上做平衡。而最后得到为什么分布式系统的事务只有 Paxos 算法是最好的。
下面这个图是这个算法中的结论。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kxc6l59o-1666747390244)(data:image/jpeg;base64,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)]
你也可以移步看一下我在 Coolshell 上写的这篇文章《分布式系统的事务处理》。
Paxos 算法,是莱斯利·兰伯特(Lesile Lamport)于 1990 年提出来的一种基于消息传递且具有高度容错特性的一致性算法。但是这个算法太过于晦涩,所以一直以来都属于理论上的论文性质的东西。其真正进入工程圈,主要是来源于 Google 的 Chubby lock——一个分布式的锁服务,用在了 Bigtable 中。直到 Google 发布了下面这两篇论文,Paxos 才进入到工程界的视野中来。
Google 与 Bigtable 相齐名的还有另外两篇论文。
不过,这几篇文章中并没有讲太多的 Paxos 算法上的细节,反而是在Paxos Made Live - An Engineering Perspective 这篇论文中提到了很多工程实现的细节。这篇论文详细解释了 Google 实现 Paxos 时遇到的各种问题和解决方案,讲述了从理论到实际应用二者之间巨大的鸿沟。
Paxos 算法的原版论文比较晦涩,也不易懂。这里推荐一篇比较容易读的—— Neat Algorithms - Paxos 。这篇文章中还有一些小动画帮助你读懂。还有一篇可以帮你理解的文章是 Paxos by Examples。
因为 Paxos 算法太过于晦涩,而且在实际的实现上有太多的坑,并不太容易写对。所以,有人搞出了另外一个一致性的算法,叫 Raft。其原始论文是 In search of an Understandable Consensus Algorithm (Extended Version) ,寻找一种易于理解的 Raft 算法。这篇论文的译文在 InfoQ 上,题为《Raft 一致性算法论文译文》,推荐你读一读。
这里推荐几个不错的 Raft 算法的动画演示。
后面,业内又搞出来一些工程上的东西,比如 Amazon 的 DynamoDB,其论文Dynamo: Amazon’s Highly Available Key Value Store 的影响力非常大。这篇论文中讲述了 Amazon 的 DynamoDB 是如何满足系统的高可用、高扩展和高可靠的。其中展示了系统架构是如何做到数据分布以及数据一致性的。GFS 采用的是查表式的数据分布,而 DynamoDB 采用的是计算式的,也是一个改进版的通过虚拟结点减少增加结点带来数据迁移的一致性哈希。
这篇文章中有几个关键的概念,一个是 Vector Clock,另一个是 Gossip 协议。
用来做数据同步的 Gossip 协议的原始论文是 Efficient Reconciliation and Flow Control for Anti-Entropy Protocols。Gossip 算法也是 Cassandra 使用的数据复制协议。这个协议就像八卦和谣言传播一样,可以“一传十、十传百”传播开来。但是这个协议看似简单,细节上却非常麻烦。
Gossip 协议也是 NoSQL 数据库 Cassandra 中使用到的数据协议,你可以上 YouTube 上看一下这个视频介绍: Understanding Gossip (Cassandra Internals)。
关于 Gossip 的一些图示化的东西,你可以看一下动画 Gossip Visualization。
除了前面的 Google 的 BigTable 和 Google File System 那两篇论文,还有 Amazon 的 DynamoDB 的论文,下面也有几篇也是要读一下的。
The Log: What every software engineer should know about real-time data’s unifying abstraction ,这篇文章好长,不过这是一篇非常好非常好的文章,这是每个工程师都应用知道的事,必看啊。你可以看中译版《日志:每个软件工程师都应该知道的有关实时数据的统一概念》。
The Log-Structured Merge-Tree (LSM-Tree) ,N 多年前,谷歌发表了 Bigtable 的论文,论文中很多很酷的方面,其一就是它所使用的文件组织方式,这个方法更一般的名字叫 Log Structured-Merge Tree。LSM 是当前被用在许多产品的文件结构策略:HBase、Cassandra、LevelDB、SQLite,甚至在 MongoDB 3.0 中也带了一个可选的 LSM 引擎(Wired Tiger 实现的)。LSM 有趣的地方是它抛弃了大多数数据库所使用的传统文件组织方法。实际上,当你第一次看它时是违反直觉的。这篇论文可以让你明白这个技术。(如果读起来有些费解的话,你可以看看中文社区里的这几篇文章:文章一、文章二。)
Immutability Changes Everything ,这篇论文是现任 Salesforce 软件架构师帕特·赫兰德(Pat Helland)在 CIDR 2015 大会上发表的(相关视频演讲)。
Tango: Distributed Data Structures over a Shared Log)。这个论文非常经典,其中说明了不可变性(immutability)架构设计的优点。随着为海量数据集存储和计算而设计的以数据为中心的新型抽象技术的出现,分布式系统比以往任何时候都更容易构建。但是,对于元数据的存储和访问不存在类似的抽象。
为了填补这一空白,Tango 为开发人员提供了一个由共享日志支持的内存复制数据结构(例如地图或树)的抽象。Tango 对象易于构建和使用,通过共享日志上简单的追加和读取操作来复制状态,而不是复杂的分布式协议。在这个过程中,它们从共享日志中获得诸如线性化、持久性和高可用性等属性。Tango 还利用共享日志支持跨不同对象的快速事务处理,允许应用程序跨机器进行状态划分,并在不牺牲一致性的情况下扩展到底层日志的上限。
除了上面上的那些我觉得不错的论文,下面还有三个我觉得不错的分布式系统论文的阅读列表,你可以浏览一下。
今天分享的内容是分布式架构方面的经典图书和论文,并给出了导读文字,几乎涵盖了分布式系统架构方面的所有关键的理论知识。这些内容非常重要,是学好分布式架构的基石,请一定要认真学习。
要学好分布式架构,你首先需要学习一些架构指导性的文章和方法论,即分布式架构设计原则。下面是几篇很不错的文章,值得一读。
有了方法论后,你还需要学习一些比较细节的落地的技术。最好的方式就是学习被前人总结出来的设计模式,虽然设计模式也要分场景,但是设计模式可以让你知道一些套路,这些套路对于我们设计的分布式系统有非常大的帮助,不但可以让我们少走一些弯路,而且还能让我们更为系统和健壮地设计我们的架构。
下面是一些分布式架构设计模式的网站。
首先,需要重点推荐的是微软云平台 Azure 上的设计模式。 Cloud Design Patterns ,这个网站上罗列了分布式设计的各种设计模式,可以说是非常全面和完整。对于每一个模式都有详细的说明,并有对其优缺点的讨论,以及适用场景和不适用场景的说明,实在是一个非常不错的学习分布式设计模式的地方。其中有如下分类。
除此之外,还有其它的一些关于分布式系统设计模式的网站和相关资料。
我个人觉得微服务也好,SOA 也好,都是分布式系统的一部分,这里有两个网站罗列了各种各样的服务架构模式。
当然,还有我在极客时间上写的那些分布式的设计模式的总结。
Life Beyond Distributed Transactions ,该文是 Salesforce 的软件架构师帕特·赫兰德(Pat Helland)于 2016 年 12 月发表的针对其在 2007 年 CIDR(创新数据库研究会议)上首次发表的同名文章的更新和缩写版本。业界谈到分布式事务通常指两段提交 2PC 事务(Spring/JEE 中 JTA 等) 或者 Paxos 与 Raft,这些事务都有明显缺点和局限性。
而赫兰德在本文讨论的是另外一种基于本地事务情况下的事务机制,它是基于实体和活动(Activity)的概念,其实类似 DDD 聚合根和领域事件的概念,这种工作流类型事务虽然需要程序员介入,依靠消息系统实现,但可以实现接近无限扩展的大型系统。赫兰德文中提出了重要的观点:“如果你不能使用分布式事务,那么你就只能使用工作流。”
How Sharding Works ,这是一篇很不错的探讨数据 Sharding 的文章。基本上来说,数据 Sharding 可能的问题都在这篇文章里谈到了。
Why you don’t want to shard ,这是 Percona 的一篇文章,其中表达了,不到万不得已不要做数据库分片。是的,最好还是先按业务来拆分,先把做成微服务的架构,然后把数据集变简单,然后再做 Sharding 会更好。
[How to Scale Big Data Applications](https://www.percona.com/sites/default/files/presentations/How to Scale Big Data Applications.pdf) ,这也是 Percona 给出的一篇关于怎样给大数据应用做架构扩展的文章。值得一读。
MySQL Sharding with ProxySQL ,用 ProxySQL 来支撑 MySQL 数据分片的一篇实践文章。
High Scalability ,这个网站会定期分享一些大规模系统架构是怎样构建的,下面是迄今为止各个公司的架构说明。
今天我们分享的内容是高手成长篇分布式架构部分的最后一篇——分布式架构工程设计,讲述了设计原则、设计模式等方面的内容,尤其整理和推荐了国内外知名企业的设计思路和工程实践,十分具有借鉴意义。
微服务是分布式系统中最近比较流行的架构模型,也是 SOA 架构的一个进化。微服务架构并不是银弹,所以,也不要寄希望于微服务架构能够解决所有的问题。微服务架构主要解决的是如何快速地开发和部署我们的服务,这对于一个能够适应快速开发和成长的公司是非常必要的。同时我也觉得,微服务中有很多很不错的想法和理念,所以学习微服务是每一个技术人员迈向卓越的架构师的必经之路。
首先,你需要看一下,Martin Fowler 的这篇关于微服务架构的文档 - Microservice Architecture (中译版),这篇文章说明了微服务的架构与传统架构的不同之处在于,微服务的每个服务与其数据库都是独立的,可以无依赖地进行部署。你也可以看看 Martin Fowler 老人家现身说法的视频。
另外,你还可以简单地浏览一下,各家对微服务的理解。
接下来,你可以看一下 IBM 红皮书:Microservices Best Practices for Java ,这本书非常好,不但有通过把 Spring Boot 和 Dropwizard 来架建 Java 的微服务,而且还谈到了一些标准的架构模型,如服务注册、服务发现、API 网关、服务通讯、数据处理、应用安全、测试、部署、运维等,是相当不错的一本书。
当然,有一本书你也可以读一下—— 微服务设计。这本书全面介绍了微服务的建模、集成、测试、部署和监控,通过一个虚构的公司讲解了如何建立微服务架构。主要内容包括认识微服务在保证系统设计与组织目标统一上的重要性,学会把服务集成到已有系统中,采用递增手段拆分单块大型应用,通过持续集成部署微服务,等等。
与此相似的,也有其它的一系列文章,值得一读。
下面是 Nginx 上的一组微服务架构的系列文章。
下面这是 Auto0 Blog 上一系列的微服务的介绍,有代码演示。
还有 Dzone 的这个 Spring Boot 的教程。
当然,如果你要玩得时髦一些的话,我推荐你使用下面的这套架构。
在对微服务有了一定的认识以后,一定有很多同学分不清楚微服务和 SOA 架构,对此,你可以看一下这本电子书 - 《Microservices vs. Service-Oriented Architecture》。通过这本书,你可以学到,服务化架构的一些事实,还有基础的 SOA 和微服务的架构知识,以及两种架构的不同。这本书的作者马克·理查兹(Mark Richards)同学拥有十年以上的 SOA 和微服务架构的设计和实现的经验。
另外,还有几篇其它对比 SOA 和微服务的文章你也可以看看。
除此之外,我们还需要知道微服务和其它架构的一些不同和比较,这样我们就可以了解微服务架构的优缺点。下面几篇文章将帮助获得这些知识。
然后,你可以看一下微服务的一些设计模式。
好了,总结一下今天的内容。我认为,微服务中有很多很不错的想法和理念,所以学习微服务是每一个技术人员迈向卓越的架构师的必经之路。在这篇文章中,我先给出了 AWS、Microsoft 和 Pivotal 对微服务的理解;然后给出了好几个系列的教程,帮你全面学习和理解微服务架构;然后通过一系列文章帮你来区分何为微服务,何为 SOA;最后给出了微服务架构的设计模式和最佳实践,以及相关资源。相信通过这一系列内容的学习,你一定会对微服务有全面、透彻的理解。
这篇文章我们来重点学习 Docker 和 Kubernetes,它们已经是分布式架构和自动化运维的必备工具了。对于这两个东西,你千万不要害怕,因为技术方面都不算复杂,只是它们的玩法和传统运维不一样,所以你不用担心,只要你花上一点时间,一定可以学好的。
有了上述的一些感性体会之后,你就可以阅读 Docker 官方文档 Docker Documentation 了,这是学习 Docker 最好的方式。
如果你想了解一下 Docker 的底层技术细节,你可以参看我的文章。
还有一些不错的与 Docker 网络有关的文章你需要阅读及实践一下。
Docker 有下面几种网络解决方案:Calico 、Flannel 和 Weave ,你需要学习一下。另外,还需要学习一下 netshoot ,这是一个很不错的用来诊断 Docker 网络问题的工具集。
关于这几个容器网络解决方案的性能对比,你可以看一下下面这几篇文章或报告。
如果你对 Docker 的性能有什么问题的话,你可以看一下下面这些文章。
下面是一些和存储相关的文章。
然后是跟运维相关的文章。
最后,推荐看看 Valuable Docker Links ,其中收集并罗列了一系列非常不错的 Docker 文章。
最佳实践
下面分享一些与 Docker 相关的最佳实践。
Kubernetes 是 Google 开源的容器集群管理系统,是 Google 多年大规模容器管理技术 Borg 的开源版本,也是 CNCF 最重要的项目之一,主要功能包括:
Kubernetes 发展非常迅速,已经成为容器编排领域的领导者。
首先,我推荐你阅读 Kubernetes 前世今生的一篇论文。
学习 Kubernetes,有两个免费的开源电子书。
这两本电子书都不错,前者更像是一本学习教程,而且面明显广一些,还包括 Cloud Natvie、Service Mesh 以及微服务相关的东西。而后者聚焦于 Kubernetes 本身,更像一本参考书。
另外,我这两天也读完了《Kubernetes in Action》一书,感觉写的非常好,一本很完美的教科书,抽丝剥茧,图文并茂。如果你只想读一本有关 Kubernetes 的书来学习 Kubernetes,那么我推荐你就选这本。
但是也别忘了 Kubernetes 的官方网站:Kubernetes.io,上面不但有全面的文档 ,也包括一个很不错的 官方教程 。
此外,还有一些交互式教程,帮助你理解掌握,以及一些很不错的文章推荐你阅读。
一些交互式教程
一些文章
这里还有一些不错的文档,你应该去读一下。
网络相关的文章
要学习 Kubernetes,你只需要读一下,下面这个 Kubernetes 101 系列的文章。
CI/CD 相关的文章
最佳实践
Docker 和 Kubernetes 资源汇总
下面是 GitHub 上和 Docker & Kubernetes 相关的 Awesome 系列。
虽然上面的这些系列非常全的罗列了很多资源,但是我觉得很不系统。对于系统的说明 Docker 和 Kubernetes 生态圈,我非常推荐大家看一下 The New Stack 为 Kubernetes 出的一系列的电子书或报告。
总结一下今天的内容。Docker 和 Kubernetes 已经成为分布式架构和自动化运维方面的不可或缺的两大基本构成,是你必需要学习的。虽然它们的玩法跟传统运维不一样,但技术方面并不算复杂,只要你花上一点时间,一定会学好的。
在这篇文章中,我推荐了 Docker 和 Kubernetes 基础技术方面的学习资料,并给出了存储、运维、网络、CI/CD 等多方面的资料,同时列出了与之相关的最佳实践。相信认真学习和消化这些知识,你一定可以掌握 Docker 和 Kubernetes 两大利器。
我之前写过一篇机器学习的入门文章,因为我也是在入门和在学习的人,所以,那篇文章和这篇机器学习和人工智能方向的文章可能都会有点太肤浅。如果你有更好的学习方式或资料,欢迎补充。
我们先来介绍一下机器学习的基本原理。
机器学习主要有两种方式,一种是监督式学习(Supervised Learning),另一种是非监督式学习(Unsupervised Learning)。下面简单地说一下这两者的不同。
监督式学习(Supervised Learning)。所谓监督式学习,也就是说,我们需要提供一组学习样本,包括相关的特征数据和相应的标签。我们的程序可以通过这组样本来学习相关的规律或是模式,然后通过得到的规律或模式来判断没有被打过标签的数据是什么样的数据。
举个例子,假设需要识别一些手写的数字,我们要找到尽可能多的手写体数字的图像样本,然后人工或是通过某种算法来明确地标注上什么是这些手写体的图片,谁是 1,谁是 2,谁是 3…… 这组数据叫样本数据,又叫训练数据(training data)。然后通过机器学习的算法,找到每个数字在不同手写体下的特征,找到规律和模式。通过得到的规律或模式来识别那些没有被打过标签的手写数据,以此完成识别手写体数字的目的。
非监督式学习(Unsupervised Learning)。对于非监督式学习,也就是说,数据是没有被标注过的,所以相关的机器学习算法需要找到这些数据中的共性。因为大量的数据是没被被标识过的,所以这种学习方式可以让大量的未标识的数据能够更有价值。而且,非监督式学习,可以为我们找到人类很难发现的数据里的规律或模型,所以也有人称这种学习为“特征点学习”,其可以让我们自动地为数据进行分类,并找到分类的模型。
一般来说,非监督式学习会应用在一些交易型的数据中。比如,你有一堆堆的用户购买数据,但是对于人类来说,我们很难找到用户属性和购买商品类型之间的关系。所以,非监督式学习算法可以帮助我们找到它们之间的关系。比如,一个在某年龄段的女性购买了某种肥皂,有可能说明这个女性在怀孕期,或是某人购买儿童用品,有可能说明这个人的关系链中有孩子,等等。于是,这些信息会被用作一些所谓的精准市场营销活动,从而可以增加商品销量。
我们这么来说吧,监督式学习是在被告诉过了正确的答案后的学习,而非监督式学习是在没有被告诉正确答案时的学习。所以,非监督式学习是在大量的非常乱的数据中找寻一些潜在的关系,这个成本也比较高。非监督式学习经常被用来检测一些不正常的事情发生,比如信用卡的诈骗或是盗刷。也被用在推荐系统,比如买了这个商品的人又买了别的什么商品,或是如果某个人喜欢某篇文章、某个音乐、某个餐馆,那么他可能会喜欢某个车、某个明星或某个地方。
在监督式学习算法下,我们可以用一组“狗”的照片来确定某个照片中的物体是不是狗。而在非监督式学习算法下,我们可以通过一个照片来找到其中有与其相似的事物的照片。这两种学习方式都有些有用的场景。
关于机器学习,你可以读一读 Machine Learning is Fun! ,这篇文章(中文翻译版)恐怕是全世界最简单的入门资料了。
接下来,我们需要比较专业地学习一下机器学习了。
在学习机器学习之前,我们需要学习数据分析,所以,我们得先学一些大数据相关的东西,也就是 Data Science 相关的内容。下面是两个不错的和数据科学相关的教程以及一个资源列表。
之后,有下面几门不错的在线机器学习的课程供你入门,也是非常不错。
除此之外,还有很多的在线大学课程可以供你学习。比如:
更多的列表,请参看——Awesome Machine Learning Courses。
《Pattern Recognition and Machine Learning》,这本书是机器学习领域的圣经之作。该书也是众多高校机器学习研究生课程的教科书,Google 上有[PDF 版的下载](http://users.isr.ist.utl.pt/~wurmd/Livros/school/Bishop - Pattern Recognition And Machine Learning - Springer 2006.pdf)。这本书很经典,但并不适合入门来看。GitHub 上有这本中的 Matlab 实现。
下面这两本电子书也是比较经典的,其中讲了很多机器学习的知识,可以当做手册或字典。
《Deep Learning: Adaptive Computation and Machine Learning series》 中文翻译为《深度学习》。这本书由全球知名的三位专家伊恩·古德费洛(Ian Goodfellow)、友华·本吉奥(Yoshua Bengio)和亚伦·考维尔(Aaron Courville)撰写,是深度学习领域奠基性的经典教材。
全书内容包括 3 部分:第 1 部分介绍基本的数学工具和机器学习的概念,它们是深度学习的预备知识;第 2 部分系统深入地讲解现今已成熟的深度学习方法和技术;第 3 部分讨论某些具有前瞻性的方向和想法,它们被公认为是深度学习未来的研究重点。这本书的官网为 “deeplearningbook.org”,在 GitHub 上也有中文翻译 - 《Deep Learning 中文翻译》。
《Neural Networks and Deep Learning》(中文翻译版),这是一本非常不错的神经网络的入门书,在豆瓣上评分 9.5 分,从理论讲到了代码。虽然有很多数学公式,但是有代码相助,就不难理解了。其中讲了很多如激活函数、代价函数、随机梯度下降、反向传播、过度拟合和规范化、权重初始化、超参数优化、卷积网络的局部感受野、混合层、特征映射的东西。
《Introduction to Machine Learning with Python》,算是本不错的入门书,也是本比较易读的英文书。其是以 Scikit-Learn 框架来讲述的。如果你用过 Scikit 这个框架,那么你学这本书还是很不错的。
《Hands-On Machine Learning with Scikit-Learn and TensorFlow 》,这是一门以 TensorFlow 为工具的入门书,其用丰富的例子从实站的角度来让你学习。这本书对于无基础的人也是适合的,对于小白来说虽然略难但是受益匪浅。
除了上述的那些课程和图书外,下面这些文章也很不错。
下面是一些和神经网络相关的不错的文章。
下面是 10 个非常经典的机器学习的算法。
如果你想了解更全的机器学习的算法列表,你可以看一下 Wikipedia 上的 List of Machine Learning Algorithms。
在 A Tour of Machine Learning Algorithms ,这篇文章带你概览了一些机器学习算法,其中还有一个"脑图"可以下载,并还有一些 How-To 的文章供你参考。
对于这些算法,SciKit-Learn有一些文档供你学习。
总结一下今天的内容。我首先介绍了机器学习的基本原理:监督式学习和非监督式学习,然后给出了全世界最简单的入门资料 Machine Learning is Fun!。随后给出了与机器学习密切相关的数据分析方面的内容和资料,然后推荐了深入学习机器学习知识的在线课程、图书和文章等,尤其列举了神经网络方面的学习资料。最后描述了机器学习的十大经典算法及相关的学习资料。
在机器学习和人工智能领域,我也在学习,也处于入门阶段,所以本文中推荐的内容,可能在你看来会有些浅。如果你有更好的信息和资料,欢迎补充。目前文章中给出来的是,我在学习过程中认为很不错的内容,我从中受益良多,所以希望它们也能为你的学习提供帮助。
对于前端的学习和提高,我的基本思路是这样的。首先,前端的三个最基本的东西 HTML 5、CSS 3 和 JavaScript(ES6)是必须要学好的。这其中有很多很多的技术,比如,CSS 3 引申出来的 Canvas(位图)、SVG(矢量图) 和 WebGL(3D 图),以及 CSS 的各种图形变换可以让你做出非常丰富的渲染效果和动画效果。
ES6 简直就是把 JavaScript 带到了一个新的台阶,JavaScript 语言的强大,大大释放了前端开发人员的生产力,让前端得以开发更为复杂的代码和程序,于是像 React 和 Vue 这样的框架开始成为前端编程的不二之选。
我一直认为学习任何知识都要从基础出发,所以这篇文章我会着重介绍基础知识和基本原理,尤其是如下的这些知识,都是前端程序员需要花力气啃下来的硬骨头。
而对于工具类的东西,这里我基本没怎么涉及,因为本文主要还是从原理和基础入手。那些工具我觉得都很简单,就像学习 Java 我没有让你去学习 Maven 一样,因为只要你去动手了,这种知识你自然就会获得,我们还是把精力重点放在更重要的地方。
下面我们从前端基础和底层原理开始讲起。先来讲讲 HTML5 相关的内容。
HTML 5 主要有以下几本书推荐。
对于 SVG、Canvas 和 WebGL 这三个对应于矢量图、位图和 3D 图的渲染来说,给前端开发带来了重武器,很多 HTML5 小游戏也因此蓬勃发展。所以,你可以学习一下。
学习这三个技术,我个人觉得最好的地方是 MDN。
最后是几个资源列表。
在《程序员练级攻略》系列文章最开始,我们就推荐过 CSS 的在线学习文档,这里再推荐一下 MDN Web Doc - CSS 。我个人觉得只要你仔细读一下文档,CSS 并不难学。绝大多数觉得难的,一方面是文档没读透,另一方面是浏览器支持的标准不一致。所以,学好 CSS 最关键的还是要仔细地读文档。
之后,在写 CSS 的时候,你会发现,你的 CSS 中有很多看起来相似的东西。你的 DRY - Don’t Repeat Yourself 洁癖告诉你,这是不对的。所以,你需要学会使用 LESS 和 SaSS 这两个 CSS 预处理工具,其可以帮你提高很多效率。
然后,你需要学习一下 CSS 的书写规范,前面的《程序员修养》一文中提到过一些,这里再补充几个。
如果你需要更有效率,那么你还需要使用一些 CSS Framework,其中最著名的就是 Twitter 公司的 Bootstrap,其有很多不错的 UI 组件,页面布局方案,可以让你非常方便也非常快速地开发页面。除此之外,还有,主打清新 UI 的 Semantic UI 、主打响应式界面的 Foundation 和基于 Flexbox 的 Bulma。
当然,在使用 CSS 之前,你需要把你浏览器中的一些 HTML 标签给标准化掉。所以,推荐几个 Reset 或标准化的 CSS 库:Normalize、MiniRest.css、sanitize.css 和 unstyle.css。
关于更多的 CSS 框架,你可以参看Awesome CSS Frameworks 上的列表。
接下来,是几个公司的 CSS 相关实践,供你参考。
最后是一个可以写出可扩展的 CSS 的阅读列表 A Scalable CSS Reading List 。
下面是学习 JavaScript 的一些图书和文章。
你需要了解一下浏览器是怎么工作的,所以,你必需要看《How browsers work》。这篇文章受众之大,后来被人重新整理并发布为《How Browsers Work: Behind the scenes of modern web browsers》,其中还包括中文版。这篇文章非常非常长,所以,你要有耐心看完。如果你想看个精简版的,可以看我在 Coolshell 上发的《浏览器的渲染原理简介》或是看一下这个幻灯片。
然后,是对 Virtual DOM 的学习。Virtual DOM 是 React 的一个非常核心的技术细节,它也是前端渲染和性能的关键技术。所以,你有必要要好好学习一下这个技术的实现原理和算法。当然,前提条件是你需要学习过前面我所推荐过的浏览器的工作原理。下面是一些不错的文章可以帮你学习这一技术。
High Performance Browser Networking ,本书是谷歌公司高性能团队核心成员的权威之作,堪称实战经验与规范解读完美结合的产物。本书目标是涵盖 Web 开发者技术体系中应该掌握的所有网络及性能优化知识。
全书以性能优化为主线,从 TCP、UDP 和 TLS 协议讲起,解释了如何针对这几种协议和基础设施来优化应用。然后深入探讨了无线和移动网络的工作机制。最后,揭示了 HTTP 协议的底层细节,同时详细介绍了 HTTP 2.0、 XHR、SSE、WebSocket、WebRTC 和 DataChannel 等现代浏览器新增的能力。
另外,HTTP/2也是 HTTP 的一个新的协议,于 2015 年被批准通过,现在基本上所有的主流浏览器都默认启用这个协议。所以,你有必要学习一下这个协议。下面相关的学习资源。
新的 HTML5 支持 WebSocket,所以,这也是你要学的一个重要协议。
总结一下今天的内容。我一直认为学习任何知识都要从基础出发,所以今天我主要讲述了 HTML 5、CSS 3 和 JavaScript(ES6)这三大基础核心,给出了大量的图书、文章以及其他一些相关的学习资源。之后,我建议你学习浏览器的工作原理和网络协议相关的内容。我认为,掌握这些原理也是学好前端知识的前提和基础。值得花时间,好好学习消化。
首先是推荐几本前端性能优化方面的图书。
Web Performance in Action ,这本书目前国内没有卖的。你可以看电子版本,我觉得是一本很不错的书,其中有 CSS、图片、字体、JavaScript 性能调优等。
Designing for Performance ,这本在线的电子书很不错,其中讲了很多网页优化的技术和相关的工具,可以让你对整体网页性能优化有所了解。
High Performance JavaScript ,这本书在国内可以买到,能让你了解如何提升各方面的性能,包括代码的加载、运行、DOM 交互、页面生存周期等。雅虎的前端工程师尼古拉斯·扎卡斯(Nicholas C. Zakas)和其他五位 JavaScript 专家介绍了页面代码加载的最佳方法和编程技巧,来帮助你编写更为高效和快速的代码。你还会了解到构建和部署文件到生产环境的最佳实践,以及有助于定位线上问题的工具。
High Performance Web Sites: Essential Knowledge for Front-End Engineers ,这本书国内也有卖,翻译版为《高性能网站建设指南:前端工程师技能精髓》。作者给出了 14 条具体的优化原则,每一条原则都配以范例佐证,并提供了在线支持。
全书内容丰富,主要包括减少 HTTP 请求、Edge Computing 技术、Expires Header 技术、gzip 组件、CSS 和 JavaScript 最佳实践、主页内联、Domain 最小化、JavaScript 优化、避免重定向的技巧、删除重复 JavaScript 的技巧、关闭 ETags 的技巧、Ajax 缓存技术和最小化技术等。
除了上面这几本书之外,Google 的 Web Fundamentals 里的 Performance 这一章节也有很多非常不错的知识和经验。
接下来是一些最佳实践性的文档。
接下来,重点推荐一个性能优化的案例学习网站 WPO Stats 。WPO 是 Web Performance Optimization 的缩写,这个网站上有很多很不错的性能优化的案例分享,一定可以帮助你很多。
然后是一些文章和案例。
接下来是一些性能工具。在线性能测试分析工具太多,这里只推荐比较权威的。
另外,中国的网络有各种问题(你懂的),所以,你不能使用 Google 共享的 JavaScript 链接来提速,你得用中国自己的。你可以到这里看看中国的共享库资源,Forget Google and Use These Hosted JavaScript Libraries in China 。
接下来,要学习的是 Web 前端的几大框架。目前而言,前端社区有三大框架 Angular.js、React.js 和 Vue.js。我认为,React 和 Vue 更为强劲一些,所以,我这里只写和 React 和 Vue 相关的攻略。关于两者的比较,网上有好多文章。我这里推荐几篇我觉得还不错的,供你参考。
其实,比较这些框架的优缺点还有利弊并不是要比出个输赢,而是让你了解一下不同框架的优缺点。我觉得,这些框架都是可以学习的。而在我们生活工作中具体要用哪个框架,最好还是要有一些出发点,比如,你是为了找份好的工作,为了快速地搭一个网站,为了改造一个大规模的前端系统,还是纯粹地为了学习……
不同的目的会导致不同的决定。我并不希望上述的这些比较会让你进入“二选一”或是“三选一”的境地。我只是想通过这些文章让你知道这些框架的设计思路和实现原理,这些才是让你受益一辈子的事。
下面先来学习一下 React.js 框架。
入门
React 学起来并不复杂,就看 React 官方教程 和其文档就好了( React 的中文教程 )。
然后,下面的文章会带你了解一下 React.js 的基本原理。
提高
学习一个技术最重要的是要学到其中的思想和方法。下面是一些我觉得学习 React 中最重要的东西。
状态,对于富客户端来说是非常麻烦也是坑最多的地方,这里有几篇文章你可以一读。
函数式编程。从 jQuery 过来的同学一定非常不习惯 React,而从 Java 等后端过来的程序员就会很习惯了。所以,我觉得 React 就是后端人员开发的,或者说是做函数式编程的人开发的。对此,你需要学习一下 JavaScript 函数式编程的东西。
这里推荐一本免费的电子书 《Professor Frisby’s Mostly Adequate Guide to Functional Programming》,其中译版为《JS 函数式编程指南中文版》。
下面有几篇文章非常不错。前两篇和函数式编程有关的文章非常值得一读。后三篇是一些比较实用的函数式编程和 React 结合的文章。
设计相关。接下来是学习一些 React 的设计模式。React Pattern 是一个不错的学习 React 模式的地方。除此之外,还有如下的一些不错的文章也会对你很有帮助的。
实践和经验
还有一些不错的实践和经验。
资源列表
最后就是 React 的资源列表。
Vue 可能是一个更符合前端工程师习惯的框架。不像 React.js 那样使用函数式编程方式,是后端程序员的思路。
最令人高兴的是,Vue 的作者是我的好朋友尤雨溪(Evan You),最近一次对他的采访 “Vue on 2018 - Interview with Evan You” 当中有很多故事以及对 Vue 的展望。(注意:Vue 是完全由其支持者和用户资助的,这意味着它更接近社区而不受大公司的控制。)
要学习 Vue 并不难,我认为上官网看文档( Vue 官方文档(中文版)),照着搞一搞就可以很快上手了。Vue.js screencasts 是一个很不错的英文视频教程。
另外,推荐 新手向:Vue 2.0 的建议学习顺序 ,这是 Vue 作者写的,所以有特殊意义。
Vue 的确比较简单,有 Web 开发经验的人上手也比较快,所以这里也不会像 React 那样给出很多的资料。下面是一些我觉得还不错的内容,推荐给你。
当然,最后一定还有 Awesome Vue ,Vue.js 里最为巨大最为优秀的资源列表。
总结一下今天的内容。我先介绍的是前端性能优化方面的内容,推荐了图书、最佳实践性的文档、案例,以及一些在线性能测试分析工具。随后重点讲述了 React 和 Vue 两大前端框架,给出了大量的文章、教程和相关资源列表。我认为,React.js 使用函数式编程方式,更加符合后端程序员的思路,而 Vue 是更符合前端工程师习惯的框架。因此,两者比较起来,Vue 会更容易上手一些。
上面的技术都讲完了,前端还有一个很重要的事就是设计。作为前端人员,我们有必要了解现在的一些知名且流行的设计语言或是一些设计规范或是设计方法,学习它们的设计思想和方法,有助于我们拓宽眼界、与时俱进。我并不觉得这些内容是设计师要学习的,如果你要成为一个前端程序员,那么学习这些设计上的东西可以让你有更好的成长空间。
对于学习设计的新手来说,推荐看看 7 steps to become a UI/UX designer ,这是一篇很不错的让新手入门的文章,非常具有指导性。首先,你得开始学习设计的一些原则和套路,如配色、平衡、排版、一致性等。还有用户体验的 4D 步骤——Discover、Define、Develop 和 Delivery。然后,开始到一些网站上找灵感。接下来,是到不同的网站上读各种文章和资源,开始学习使用设计工具,最后是找人拜师。此外,其中还链接了其它一些不错的文章、网站、博客和工具。我认为,这篇文章是一篇很不错的设计师从入门到精通的练级攻略。
虽然有这么一个速成的教程,但我觉得还是应该系统地学习一下,所以有了下面这些推荐。
先推荐几本书。
除了上面的这几本书,还有下面的这几篇文章也是很不错的,推荐一读。
在 2013 年网页设计师布拉德·弗罗斯特(Brad Frost)从化学中受到启发:原子(Atoms)结合在一起,形成分子(Molecules),进一步结合形成生物体(Organisms)。布拉德将这个概念应用在界面设计中,我们的界面就是由一些基本的元素组成的。
乔希·杜克(Josh Duck)的“HTML 元素周期表”完美阐述了我们所有的网站、App、企业内部网、hoobadyboops 等是如何由相同的 HTML 元素组成的。通过在大层面(页)和小层面(原子)同时思考界面,布拉德认为,可以利用原子设计建立一个适应组件的动态系统。
为什么要玩原子设计,我认为,这对程序员来说是非常好理解的,因为这就是代码模块化重用化的体现。于是,你就是要像搭积木一样开发和设计网页,当你把其模块化组件化了,也更容易规范整体的风格,而且容易维护……这些都意味着你可以更容易地维护你的代码。所以,这个方法论导致了 Web 组件化的玩法。这是设计中非常重要的方法论。
关于这个设计方法论,你可以阅读一下下面这几篇文章。
但是,真正权威的地方还是布拉德·弗罗斯特的电子书、博客和实验室,可以从中获取更多的信息。
接下来是关于这个设计方法和 React.js 框架的几篇文章。
下面来介绍一下设计语言和设计系统。
Fluent Design System 中文翻译为流畅设计体系,是微软于 2017 年开发的设计语言。流畅设计是 Microsoft Design Language 2 的改版,其中包含为所有面向 Windows 10 设备和平台设计的软件中的设计和交互的指导原则。
该体系基于五个关键元素:光感、深度、动效、材质和缩放。新的设计语言包括更多对动效、深度及半透明效果的使用。过渡到流畅设计体系是一个长期项目,没有具体的完成目标,但是从创作者更新以来,新设计语言的元素已被融入到个别应用程序中。它将在未来的 Windows 10 秋季创作者更新中更广泛地使用,但微软也表示,该设计体系不会在秋季创作者更新内完成。
微软于 2017 年 5 月 11 日的 Microsoft Build 2017 开发者大会上公开了该设计体系。
还有 Build 2018 上的一些微软的 YouTube 分享。
Material Design 中文翻译为质感设计,或是材质设计、材料设计。这是由 Google 开发的设计语言。扩展于 Google Now 的“卡片”设计,Material Design 基于网格的布局、响应动画与过渡、填充、深度效果(如光线和阴影)。设计师马蒂亚斯·杜阿尔特(Matías Duarte)解释说:“与真正的纸张不同,我们的数字材质可以智能地扩大和变形。材质具有实体的表面和边缘。接缝和阴影表明组件的含义。”Google 指出他们的新设计语言基于纸张和油墨。
Material Design 于 2014 年的 Google I/O 大会上发布(参看 Google I/O 2014 - Material witness: How Android material applications work)。其可借助 v7 appcompat 库用于 Android 2.1 及以上版本,几乎支持所有 2009 年以后制造的 Android 设备。随后,Material Design 扩展到 Google 的网络和移动产品阵列,提供一致的跨平台和应用程序体验。Google 还为第三方开发人员发布了 API,开发人员可将质感设计应用到他们的应用程序中。
除了到 官网 学习 Material Design,你还可以访问 Material Design 中文版 来学习。
另外,Wikipedia 上有一张 Material Design 实现的比较表,供你参考。
下面是几个可供你使用的 Material UI 的工程实现。
接下来再来推荐其它几家公司的设计语言。
我认为,要了解 Web 动画效果设计的第一步,最好的地方是 CodePen。这个网站不只是让人分享 HTML、CSS 和 JavaScript 代码的网站。其中也有很多分享样例都和动画效果有关。这个网站可以让你对动画效果有一些感性认识,当然还有代码供你参考。
接下来,我们要了解动画效果设计的一些方法。基本上来说,动画设计都会受 “动画的 12 项基本法则 ”的影响,这个方法论源自于迪士尼动画师奥利·约翰斯顿(Ollie Johnston)和弗兰克·托马斯(Frank Thomas)在 1981 年所出的《The Illusion of Life: Disney Animation》一书。这些法则已被普遍采用,至今仍与制作 3D 动画法则有关联。这里还有一篇文章 “Understand the 12 principles of animation” 是对这个法则的解读和理解。
除此之外,还有几个动画设计指南和相关文章供你参考和学习。
下面分享一下 UI/UX 设计的相关资源。文章资源主要有以下这些。
接下来推荐一些优秀设计的聚集地。
总结一下今天的内容。我并不认为 UI/UX 设计这些内容只是设计师要学习的,如果你要成为一个前端程序员,那么学习这些设计上的东西可以让你有更好的成长空间。首先,我推荐了一些图书和文章,让你更好地了解经典的设计原则和指导思想。
然后介绍了原子设计,以及深入学习和理解这一设计方法论的图书、文章和其他相关资源。最后分享了当下主流和知名公司中在用的设计语言和设计系统,并给出了大量的学习资源,推荐了一些优秀设计的聚集地。相信通过学习这些内容,你在 UI/UX 设计方面不仅能收获方法,还能获得非常多的灵感。
首先,我先推荐一些不错的个人技术博客。
其实还有很多不错的博客,不过,现在国外不错的博客都在一个叫 Medium 的网站,我也发现我 Google 很多东西时都会到这个网站上。这个网站上的内容不只有技术的,还有很多很多其他方面的内容,比如文化、艺术、科学等等。这个网站就是一个博客发布系统,其是由 Twitter 联合创始人埃文·克拉克·威廉姆斯(Evan Clark Williams)和克里斯多福·艾萨克·比兹·斯通(Christopher Isaac Biz Stone)创办的,这两个人觉得 Twitter 上全是垃圾没有营养的信息。所以,创办了 Medium,这个平台上有专业和非专业的贡献者,亦有受雇的编者。
我已经感觉到,未来高质量的文章都会在 Medium 这个平台上出现,因为有一些公司的技术博客也在这个平台上发布了,比如 Netflix 的。所以,你有必要上到这个平台上 follow 一些作者、专栏和主题。
下面是我订阅的一些我认为还不错的和编程相关的频道,推荐给你。