diff --git a/专栏/赵成的运维体系管理课/18持续交付流水线软件构建难吗?有哪些关键问题?.md b/专栏/赵成的运维体系管理课/18持续交付流水线软件构建难吗?有哪些关键问题?.md new file mode 100644 index 0000000..b979ea7 --- /dev/null +++ b/专栏/赵成的运维体系管理课/18持续交付流水线软件构建难吗?有哪些关键问题?.md @@ -0,0 +1,108 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 18 持续交付流水线软件构建难吗?有哪些关键问题? + 上期文章我们介绍了需求分解与应用对应的管理方式,以及提交环节的开发协作模式,今天我们详细介绍一下提交阶段的构建环节,也就是我们经常提到的代码的编译打包。 + +构建环节 + +由于静态语言从过程上要比动态语言复杂一些,代码提交后,对于Java和C++这样的静态语言,我们要进行代码编译和打包。而对于PHP和Python这样的动态语言,就不需要编译,直接打包即可。 + +同时,编译过程就开始要依赖多环境以及多环境下的配置管理,并根据不同的环境获取不同的配置,然后打包到最终的软件发布包中。 + +下面我就结合自己的实践经验,以Java为例,对构建环节做下介绍。 + +构建过程中我们要用到以下4种工具: + + +Gitlab,代码管理工具,也是版本管理工具; +Maven,依赖管理和自动化构建工具,业界同类型的工具还有Gradle等; +Docker,用来提供一个干净独立的编译环境; +自动化脚本和平台,自动化构建的任务我们使用Python脚本来实现代码获取、编译执行、软件包生成等。 + + +具体整个构建过程图示如下: + + + +我们以Java为例描述如下。 + +1.首先准备好JDK的编译镜像,这个镜像环境与线上运行环境保持一致,比如OS版本、内核参数以及JDK版本等基础环境。当需要启动一个构建任务时,就创建一个对应的Docker实例,作为独立的编译环境。 + +2.构建任务会根据应用配置管理中的Git地址,将代码克隆下来放到指定的编译目录。Docker实例启动后,将编译目录挂载到Docker实例中。 + +3.执行mvn package命令进行编译打包,最终会生成一个可发布war的软件包。同样的,对于C++、Go、Node.js,也会准备好类似的编译镜像。不同的是,打包时,对于C++中的cmake和make,Go中的go install等等,最终也会生成一个可发布的软件包。 + +4.构建完成后,生成软件包放到指定构件库目录,或者直接发布到maven的构件库中管理,然后将Docker实例销毁。 + +上述就是一个完整的构建过程。在这里,你一定会有一些疑问,那么,我先回答几个比较常见的问题,欢迎你留言和我继续讨论。 + +几个关键问题 + +1.配置文件如何打包? + +这个问题,我们在前面持续交付的多环境配置管理文章中,已经详细介绍过。这里我们结合构建过程,再介绍一下。 + +在上述第3个步骤中,我们要进行代码编译。按照持续交付理念,软件只需打包一次就可以各处运行,这对于代码编译是没有问题的,但是对于一些跟环境相关的配置就无法满足。 + +比如,我们前面讲到,不同的环境会涉及到不同的配置,如DB、缓存。而且,其他公共基础服务在不同环境中也会有不同的地址、域名或其他参数配置。 + +所以,我们就需要建立环境与配置之间的对应关系,并保存在配置管理平台中,至于如何来做,大家可以参考前面多环境配置管理的文章。 + +这里我们回到打包过程上来。 + +在做构建时,我们是可以确认这个软件包是要发布到哪个环境的。比如,按照流程,当前处于线下集成测试环境这个流程环节上,这时只要根据集成测试环境对应的配置项,生成配置文件,然后构建进软件包即可。如果是处于预发环境,那就生成预发环境对应的配置文件。 + +在我们的实际场景中,多个环境需要多次打包,这与我们持续交付中只构建一次的理念相悖。这并不是有意违背,而是对于Java构建出的交付件,最终无论生成的是war包,还是jar包,上述提到的跟环境相关的配置文件,是要在构建时就打入软件包中的。 + +而且在后续启动和运行阶段,我们是无法修改已经构建进软件包里的文件及其内容的。这样一来,配置文件无法独立发布,那么就必须跟软件包一起发布。所以,在实际场景下,我们要针对不同环境多次打包。 + +那么,我们如何确保多次打包的效果能够和“只构建一次”理念的效果相一致呢? + +这就还是要依赖我们前面介绍的各个环节的建设过程,主要有以下3个方面: + + +代码提交。通过分支提交管理模式,每次构建都以master为基线,确保合入的代码是以线上运行代码为基础的。且前面的发布分支代码未上线之前,后续分支不允许进入线上发布环节,确保发布分支在多环境下是同一套代码。 +编译环境统一。上述过程已经介绍,编译环境通过全新的Docker容器环境来保证。 +配置管理。前面介绍到的多环境配置管理手段, 通过模板和auto-config的配置管理能力,确保多环境配置项和配置值统一管理。 + + +至此,一个完整的软件构建过程就完成了。可以看到,如果充分完善前期的准备工作,在做后期的方案时就会顺畅很多。 + +2.为什么用Docker做编译环境的工具? + +Docker容器很大的一个优势在于其创建和销毁的效率非常高,而且每次新拉起的实例都是全新的,消除了环境共用带来的交叉影响。而且对于并发打包的情况,Docker可以快速创建出多个并行的实例来提供编译环境,所以无论在效率上还是环境隔离上,都有非常好的支持。 + +你可以尝试一下我的这个建议,确实会非常方便。 + +3.为什么不直接生成Docker镜像做发布? + +在使用Docker容器做编译的过程中,我们最终取得的交付件模式是一个war包,或者是一个jar包,这个也是我们后续发布的对象。 + +可能有读者会问:为什么不直接生成Docker镜像,后续直接发布镜像? + +这确实是一个好问题。如果单纯从发布的维度来看,直接发布镜像会更方便,更高效。不过,在现实场景下,我们应该更全面地看问题。 + +早期我们曾有一段时间使用OpenStack+Docker的模式进行物理机的虚拟化,以提升资源利用率。这实际上是将容器虚拟机化。 + +也就是说,虽然Docker是一个容器,但是我们的使用方式仍然是虚拟机模式,要给它配置IP地址,要增加很多常用命令比如top、sar等等,定位问题需要ssh到容器内。 + +这里一方面是因为基于Docker的运维工具和手段没有跟上,当时也缺少Kubernetes这样优秀的编排工具;另一方面,我们之前所有的运维体系都是基于IP模式建设的,比如监控、发布、稳定性以及服务发现等等,完全容器化的模式是没有办法一步到位的。 + +所以,这里我们走了个小弯路:容器虚拟机化。那为什么我们不直接使用虚拟机,还能帮我们省去很多为了完善容器功能而做的开发工作?所以一段时间之后,我们还是回归到了KVM虚拟机使用方式上来。 + +这样也就有了上述我们基于虚拟机,或者更准确地说,是基于IP管理模式下的持续交付体系。 + +经过这样一个完整的持续交付体系过程后,我们总结出一个规律: + +容器也好,虚拟机也罢,这些都是工具,只不过最终交付模式不一样。但是前面我们所讲的不管是标准化、多环境、配置管理等等这些基础工作,无论用不用容器都要去做。而且,容器的高效使用,一定是建立在更加完善和高度标准化的体系之上,否则工具只会是越用越乱。 + +关于持续交付流水线软件构建方面的内容,我们今天先分享到这里,欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/19持续交付中流水线构建完成后就大功告成了吗?别忘了质量保障.md b/专栏/赵成的运维体系管理课/19持续交付中流水线构建完成后就大功告成了吗?别忘了质量保障.md new file mode 100644 index 0000000..93888e3 --- /dev/null +++ b/专栏/赵成的运维体系管理课/19持续交付中流水线构建完成后就大功告成了吗?别忘了质量保障.md @@ -0,0 +1,114 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 19 持续交付中流水线构建完成后就大功告成了吗?别忘了质量保障 + 上期文章我结合自己的实践经验,介绍了持续交付中流水线模式的软件构建,以及在构建过程中的3个关键问题。我们可以看出,流水线的软件构建过程相对精简、独立,只做编译和打包两个动作。 + +但需要明确的是,在持续交付过程中,我们还要做很多与质量保障相关的工作,比如我们前面提到的各类功能测试和非功能测试。 + +所以,今天我们聊一聊在流水线构建过程中或构建完成之后,在质量保障和稳定性保障方面,我们还需要做哪些事情。 + +首先,我们回顾一下之前总结的这张流程图: + + + +可以看出,在流水线构建过程中,我们尤其要重视以下3个方面的工作内容。 + +依赖规则限制 + +主要是对代码依赖的二方包和三方包做一些规则限制。比如,严格限定不允许依赖Snapshot版本;不允许引入有严重漏洞的版本,如Struts2的部分版本;检测JAR包冲突,如我们常用的Netty、Spring相关的包;限定某些软件包的最低版本发布,如内部提供的二方包,以确保版本收敛,降低维护成本。 + +过滤规则上,通过Maven构建软件包过程中生成的dependency:list文件,将GroupID和ArtifactID作为关键字,与我们设定的版本限制规则进行匹配。 + +两个示例如下(真实版本信息做了修改): + +检测JAR包冲突: + + +[WARNING] 检测到jar包冲突: io.netty:netty-all, 版本: 4.0.88.Final, 当前使用: +4.0.22.Final + + +限定最低版本: + + +[WARNING] 检测到 mysql:mysql-connector-java, 版本 5.0.22, 版本不符合要求, 需要大于等于 +5.0.88。旧版存在已知兼容性bug,导致连不上数据库, 请在2018-01-15 00:00:00前升级完成, 否则将被禁止发布,如有疑问,请联系@发布助手 + + +JAR包依赖以及维护升级,通常是一件令我们比较头疼的事情,特别是在运行时出现的冲突异常,更是灾难性的。为了从技术角度更好地进行管理,我们需要做好隔离,这一点可以利用JVM类加载机制来实现。 + +如果你有兴趣,可以在网上参考阿里的潘多拉(Pandora)容器设计资料,这里我们就不作详细介绍了。 + +功能测试 + +包括单元测试、接口测试、联调测试和集成测试。这里的每个测试环节起到的作用不同,联调测试和集成测试依赖的主要手段还是手工验证,所以这里我们分享下可以通过自动化手段完成的单元测试和接口测试。 + +这里主要用到的几个工具: + + +JUnit 和TestNG,分别做单元测试和接口测试; +Maven插件,maven-surefire-plugin,用来执行JUnit或TestNG用例; +JaCoCo,分析单元测试和接口测试后的代码覆盖率; +Jenkins,自动化测试任务执行,报表生成和输出,与Maven、JUnit、GitLab这些工具结合非常好。 + + +关于上述这几种工具,我在此就不展开详细介绍了,你可以自行上网查询和学习。 + +下面,我们分析一下功能测试中的两个重要环节:单元测试和接口测试。 + + +单元测试,由开发完成测试用例的开发,对于需要连接DB的用例,可以用DBUnit这样的框架。用例的自动执行,每次代码开发完成,开发执行mvn test在本地进行自测通过,然后提交到GitLab。可以在GitLab中设置hook钩子,和回调地址,提交的时候在commitMsg增加钩子标识,如unitTest,这样提交后就触发回调自动化单元测试用例执行接口,确保提交后的代码是单元测试通过的,最终可以通过JaCoCo工具输出成功率和代码覆盖率情况。 +接口测试,用例编写上使用TestNG,这个测试框架相比JUnit功能更全面,也更灵活一些。但是过程上与单元测试类似,当然也可以不通过hook方式出发,可以通过手工触发进行测试。 + + +上述自动化测试环节结束,软件包就可以发布到我们之前说的项目测试环境或集成测试环境进行功能联调和测试了,这时就需要部分人工的介入。 + +非功能测试 + +在功能验证的同时,还需要并行进行一些非功能性验证,包括安全审计、性能测试和容量评估 。分别介绍如下: + + +安全审计,由安全团队提供的源代码扫描工具,在构建的同时,对源代码进行安全扫描,支持Java和PHP语言。可以对源代码中的跨站脚本、伪造请求、SQL注入、用户名密码等敏感信息泄露、木马以及各类远程执行等常见漏洞进行识别,对于高危漏洞,一旦发现,是不允许构建出的软件包发布的。而且实际情况是,不审不知道,一审吓一跳,我们前面几次做代码扫描时,各种漏洞触目惊心,但是随着工具的支持和逐步改进,基本已经将这些常见漏洞消灭在萌芽状态。 + + +下面是扫描结果的简单示例(目前扫描工具已经开源,请见文末链接): + + + + +性能和容量压测,主要针对核心应用,进行发布前后的性能和容量比对,如果出现性能或容量差异较大,就会终止发布。关于这一点,我在后面稳定性保障的文章中会详细介绍到。这个验证工作,会在预发或Beta环境下进行验证,因为这两个环境是最接近线上真实环境的。 + + +下图是一张发布前后的效果比对示意图,正常情况下,性能曲线应该是基本重叠才对,不应该出现较大的偏差。 + + + +最后 + +到这里,我们稍作一个总结。 + +关于持续交付中的流水线模式,我们在前面两期文章以及本期的分享中,相对完整地介绍了从需求分解开始,到代码提交、软件构建,再到功能和非功能测试验证的整个过程。这个过程就是我们常说的持续集成。 + +之所以我没有在一开始引入这个概念,是因为,如果我们将注意力集中到这一过程中具体的动作和问题上,会更有利于我们理解,而不是一开始就被概念性的术语和框架束缚住。 + +流水线模式功能测试和非功能测试的整个过程可以总结如下: + + + +同时,我们在上面持续集成的过程中,要基于前面介绍的各类环境和基础配置管理,比如功能验证就需要在线下环境中的开发环境、项目环境以及集成测试环境上进行验收。 + +而非功能验证的性能和容量评估,就需要在线上环境里的预发或Beta环境中完成。这里就已经涉及到了软件的部署发布。 + +下一期文章,我将分享线上发布的整个过程,并对整个持续交付体系内容做一个收尾。欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + +附:源代码安全审计工具 +https://github.com/wufeifei/cobra + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/20做持续交付概念重要还是场景重要?看笨办法如何找到最佳方案.md b/专栏/赵成的运维体系管理课/20做持续交付概念重要还是场景重要?看笨办法如何找到最佳方案.md new file mode 100644 index 0000000..c995db6 --- /dev/null +++ b/专栏/赵成的运维体系管理课/20做持续交付概念重要还是场景重要?看笨办法如何找到最佳方案.md @@ -0,0 +1,112 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 20 做持续交付概念重要还是场景重要?看笨办法如何找到最佳方案 + 上期文章中我们讲到,在经过严格的依赖规则校验和安全审计之后,构建出的软件包才可以部署发布。 + +在开发环境、项目环境、集成测试环境以及预发环境下,我们还要进行各类的功能和非功能性测试,最后才能发布到正式的生产环境之上。 + +通常状况下,做一次软件版本发布,必须经过以下几个环境(如下图所示)。需要明确的是,项目环境和“小蘑菇”(内部叫法)环境,只有特殊版本才会配备,这里我们不做强制。 + + + +上述这些环境我们在之前都介绍过。而历经如此多的环境,高效的自动化持续部署和发布就变得尤为重要。 + +特别是最后的线上发布环节,还需要确保业务连续稳定、无间断,所以,在复杂的微服务架构环境下,我们对软件的发布策略选择、自动化程度和稳定性要求就更高了。 + +今天,我们一起看看整个流水线软件部署和发布的细节。 + +软件的持续部署发布 + +这里,我们直接以生产环境的发布过程来讲解。软件的部署发布,简单来说就是: + +将构建完成和验证通过的应用软件包,发布到该应用对应环境下的IP主机上的指定目录下,并通过应用优雅上下线,来实现软件最新版本对外提供服务的过程。 + +这个过程会包含的环节,我以图示整理如下: + + + +我们可以看到,软件部署发布,听上去就是把软件部署一下,然后启动起来。这样的操作方式对于单体架构软件没有问题,但是在微服务架构下就满足不了要求了。 + +单体架构软件启动起来就可以提供服务,但是对于微服务应用,无论停止还是启动,都需要考虑到对周边其它依赖和被依赖应用的影响才可以,考虑的点也就相对较多。 + +我们针对单机发布,分环节来看一下: + + +从CMDB中,拿到线上生产环境下的应用主机IP列表去对应关系,目的是要将软件包发布到应用对应的IP主机上。 + +检查每台机器上的服务是否正常运行,如果是正常服务的,说明可以发布,但是服务本身异常,就要记录或跳过。 + +下载war包到指定目录。这里要依赖前期我们介绍的应用配置管理,在这一步要获取到该应用的源代码目录。 + +关闭该应用在这台主机上的监控,以免服务下线和应用终止产生线上错误告警。 + +优雅下线。RPC服务从软负载下线,如果该应用还提供了http的Web调用,就需要从Nginx这样的七层负载下线,下线动作均通过API接口调用方式实现。 + +下线后经过短暂静默,重启应用。对于Java应用来说,重启时可以自动解压,启停命令等还是之前从应用配置管理中获取响应路径和命令脚本名称。 + +优雅上线,进行健康监测,检查进程和应用状态是否正常,如果全部监测通过,则开始上线服务,开启监控。 + + +上述是一个应用的单机发布过程,过程比较长,但是可以看出,每个环节并不复杂。这里我们需要注意两个关键点: + + +针对场景,进行细分,这样就可以化整为零,把一个乍看上去很复杂的过程,分解成一个个可执行的步骤。 +与服务化的软负载和注册中心进行交互,确保应用是可以优雅上下线的,而不是简单粗暴地启动和停止。 + + +发布策略 + +上述过程是针对单机的操作步骤。但是,如果我们有上百台主机,甚至一些大的集群有上千台主机,这时应该怎么发布呢?这里就涉及到发布策略问题。 + +业界常见的几种模式,如蓝绿发布、灰度发布(金丝雀发布)、滚动发布等等,这几种模式网上资料丰富,在这里我们就不逐一展开详细介绍了。 + +这里,我们主要以灰度发布和滚动发布的组合方式为例,详细分析一下这种发布模式。 + +前面介绍的线上Beta环境,选择的就是金丝雀发布模式,我们内部称之为灰度发布或Beta发布。后来国外Netflix持续交付经验传播比较广,所以我们经常可以听到金丝雀发布这种方式,而其本质上还是灰度发布模式。 + +Beta环境下,我们会保留1-2台应用主机,引入较少的线上真实用户流量。发布到这个环境上时,对于核心应用和大规模的集群,我们会静默较长时间,以观察应用的新版本运行状态。 + +如果没有严重的报错或崩溃,静默期过后,我们认为软件质量和稳定性是没有问题的,接下来就会发布到正式的生产环境上。 + +因为生产环境上大的集群可能会有上百台甚至上千台主机,如果每台主机逐一单独发布,这样会导致发布效率过低;但是一次性发布数量太多,又会对线上应用容量大幅度缩减,极有可能会导致服务雪崩或业务中断。 + +所以我们选择的方式就是滚动发布,或者可以理解为分批次发布:即每批次发布10台或20台,升级完成后,再启动下一批次发布。这样每次发布的机器数量可以自行设定,但是必须低于50%。 + +至此,一个应用的滚动发布流程就结束了。根据我们实践的具体情况,这种灰度加滚动的发布模式,相对平稳和可控。相比于蓝绿发布,不需要额外再独立一个环境出来,且不需要每次发布都要做一次整体的流量切换,避免产生较大的操作风险。 + +对于回滚,我们会根据上个版本的war包名称记录,在发布过程中或发布后出现严重情况时,直接快速回滚。因为这个操作是在紧急和极端的情况下才执行,所以提供一键操作,过程跟上述的发布过程相似,在此也不再赘述。 + +持续交付体系的收益 + +持续交付体系运作起来后,整个流水线过程完全自助发布,运维无需介入,达到了DevOps,或者说是NoOps的效果。如下图所示: + + + +总结 + +至此,我们整个持续交付体系的内容就全部介绍完了。对于整个过程的总结,你可以参考本专栏“持续交付”主题的第一篇文章[《持续交付知易行难,想做成这事你要理解这几个关键点》],我在文中对整个持续交付体系进行了比较完整的梳理。 + +细心的你应该可以发现,到本期文章为止,我并没有提到太多DevOps相关的内容,而这个恰恰是当前业界非常火热的概念。在写作过程中,我也没有特别强调持续交付是什么,持续集成是什么,而这些又是当前DevOps里面特别强调的部分。 + +我之所以这样做是因为,概念都是一个个名词或者Buzzword(时髦名词),它们所表达的意思也都非常泛,每个人,每个团队或每个组织对它们的理解以及解读都是不一样的。 + +就拿DevOps举例,有谁能说清楚它到底是什么,到底代表什么意思?估计一千个人会有一千种理解,不同的团队对它的实践模式也不一样。 + +所以,如果直接从概念出发,反而容易让我们迷失方向,忘记想要解决的问题,让我们脱离所处的实际场景,把精力都放在了各种所谓的工具和技术上。这一点也恰恰与我所一直强调的,要从实际问题和业务场景出发来考虑解决方案相违背。 + +在我们“持续交付”主题的分享中,你可以看到,有很多的解决方案并没有标准化的模式,也没有哪一个工具或技术能够直接解决这些问题。 + +我们所采取的手段,其实都是些笨办法:即找到问题,分析问题,调研解决方案,讨论碰撞,然后慢慢摸索和实践,找出最合适我们的方式。 + +希望我的分享能够给你带来启发,就像我们开篇词提到的:思路上的转变远比技术上的提升更为重要。 + +欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/21极端业务场景下,我们应该如何做好稳定性保障?.md b/专栏/赵成的运维体系管理课/21极端业务场景下,我们应该如何做好稳定性保障?.md new file mode 100644 index 0000000..660b8c7 --- /dev/null +++ b/专栏/赵成的运维体系管理课/21极端业务场景下,我们应该如何做好稳定性保障?.md @@ -0,0 +1,89 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 21 极端业务场景下,我们应该如何做好稳定性保障? + 从今天开始,和你分享我对微服务和分布式架构下的稳定性保障的理解。 + +稳定性保障需要一定的架构设计能力,又是微服务架构比较核心的部分。在陈皓老师的“左耳听风”专栏,以及杨波老师的“微服务架构核心20讲”专栏都有非常详细的介绍。所以在我的专栏里,我会结合特定的场景,并着重从运维和技术运营的角度来分享。 + +我们所面对的极端业务场景 + +首先,看一下我们当前所面对的极端业务场景,我把它大致分为两类。 + +1.可预测性场景 + +什么是可预测性?简单来说,就像电商每年的大促,如618、双11、双12等等。这类业务场景是可预测的,业务峰值和系统压力峰值会出现在某几个固定的时间点,我们所做的所有准备工作和稳定性应对措施,都是针对这些固定时间点的,比如零点时刻。 + +峰值压力可预测,就意味着可以提前评估用户访问模型,并根据模型进行压测调优。发现系统中的瓶颈就调优或者扩容,调整完成之后,继续压测,继续调整,直至系统容量达到原来设定的目标。由此可见,在可预测的场景下,与后面的不可预测场景相对比,从准备过程上来说会更加从容。 + +但是,我们的优化或扩容是有限度的,也就是不会无限度地投入成本,来满足零点这个峰值时刻,让所有用户都能够正常访问。从成本和收益角度来说,这样做是不现实的。 + +所以,在峰值那个时间点上,当用户流量远远大于系统容量时,我们所采取的措施绝不是再去扩容或优化,因为无论是从时效性、系统稳定性还是成本收益上看,这样做都已经无法满足要求了。 + +那我们该采取什么策略呢?这里我们采取的策略是在系统承诺容量内,保证系统的核心功能能够正常运行。以电商为例,就是要确保整个交易链路是正常的,用户可以正常登陆,访问商品,下单并最终支付。对于非核心功能,就会通过预案执行功能降级。对于超出系统承诺容量的部分进行流量限流,并确保在某些异常状况下能够熔断或旁路,比如缓存故障,就要马上限流并将请求降级到数据库上。 + +所以,我们在618,双11和双12的零点峰值时刻去访问各大电商网站,很大概率上都会提示系统正忙,请稍后再试,短则2~3分钟,长则5~10分钟,再去访问,网站功能就一切正常了。这并不代表各大电商网站宕机了,而是其在瞬时超大流量访问压力下采取的一种保护措施,这一点反而说明这些电商网站的大促预案非常完善。 + +2.不可预测性场景 + +我刚刚提到的电商大促场景,其实已经非常复杂了,没有一定的整体技术能力,想做好从容应对也并非易事。我们这两年做大促模拟压测,动辄上百号人通宵投入,说到底还是在这方面的经验以及各类工具平台的积累不够,体系的完善需要一定的周期和过程。 + +那不可预测的场景就更为复杂。社交类业务就具有这种明显的特征,比如微博、朋友圈、空间等等。以微博为例,我们知道之前鹿晗公布恋情,王宝强以及乔任梁等的突发事件等等,这些事情什么时候发生,对于平台来说事先可能完全不知道,而且极有可能是大V的即兴发挥。当然,现在因为商业合作上的原因,某些大V的部分营销活动也会与各类社交业务平台提前沟通,确保活动正常执行,但是即使是提前沟通,周期也会非常短。 + +对于不可预测性的场景,因为不知道什么时候会出现突发热点事件,所以就无法像电商大促一样提前做好准备。社交类业务没法提前准备,就只能随时准备着,我认为这个挑战还是非常大的。 + +我们要迎接的技术挑战 + +说完了场景,我们来看看这给技术带来了哪些挑战。 + +1.运维自动化 + +这个不难理解,应对极端场景下的系统压力,一定要有资源支持,但是如何才能将这些资源快速扩容上去,以提供业务服务,这一点是需要深入思考的。结合前面我们讲过的内容,标准化覆盖面是否足够广泛,应用体系是否完善,持续交付流水线是否高效,云上资源获得是否足够迅速,这些都是运维自动化的基础。特别是对于不可预测的场景,考验的就是自动化的程度。 + +2.容量评估和压测 + +我们要时刻对系统容量水位做到心中有数,特别是核心链路,比如电商的交易支付链路。我们只有对系统容量十分清楚,才能针对特定场景判断出哪些应用和部件需要扩容,扩容多少,扩容顺序如何。同时,系统容量的获取,需要有比较完善的自动化压测系统,针对单接口、单应用、单链路以及全链路进行日常和极端场景下的模拟压测。 + +3.限流降级 + +我们前面提到了电商大促的例子,业务在峰值时刻,系统是无论如何也抵御不住全部流量的。这个时候,我们要做的就是保证在承诺容量范围内,系统可用;对于超出容量的请求进行限流,请用户耐心等待一下。如何判断是否需要限流呢?这时我们要看系统的各项指标,常见的指标有CPU、Load、QPS、连接数等等。 + +同时,对于非核心的功能,在峰值时刻进行降级,以降低系统压力。这里有两种方式,分别是主动降级和被动降级。主动降级就是在峰值时刻,主动把功能关掉,如商品评论和推荐功能等等;我们前面介绍到的静态化,也是一种降级策略。对于被动降级,也就是我们常听到的熔断。某个应用或部件故障,我们要有手段将故障隔离,同时又能够保证业务可用,所以会涉及故障判断和各类流量调度策略。 + +4.开关预案 + +上面介绍到的限流降级,也是一类开关,属于业务功能开关;还有一类是系统功能开关,比如当缓存故障时,我们就需要将请求转发到数据库上,目的也只有一个,让系统可用。但是问题来了,数据库的访问效率没有缓存高,所以缓存可以支撑的流量,数据库肯定是支撑不了的,怎么办呢?这时,就要与限流策略结合起来,先限流,限到数据库能够支撑的容量,再做降级。这样几个策略组合在一起,就是应急预案的执行了。当然,预案里面还会有业务预案。 + +5.故障模拟 + +上述预案,需要在日常,甚至是从经历过的故障中提炼出场景,制定好策略,然后不断进行模拟演练。只有这样,等到真正出现问题时,我们的预案才可以高效执行。我们知道Netflix的Chaos Engineering,其中的Chaos Monkey,就是专门搞线上破坏,模拟各种故障场景,以此来看各种预案执行是否到位,是否还有可以改进的地方。 + +所以,类似Chaos Engineering的故障模拟系统,也需要建设起来。我们需要模拟出一些场景,比如最常见的CPU异常,RT响应异常,QPS异常等等,看我们的预案是否能够快速执行,能够保持系统或将系统快速恢复到正常状态。 + +6.监控体系 + +最后,我再提一下监控。通过我们前面介绍的内容,监控的重要性就不言而喻了,因为所有的指标采集和统计,异常判断,都需要监控体系的支持。监控体系和前面介绍的运维自动化一样,都是最为基础的支撑平台。 + +极端业务场景下的不确定因素 + +上面我们讨论了极端业务场景给技术层面带来的挑战。但是对于稳定性保障而言,我认为最困难的部分,不在技术层面,而是在业务层面,也就是用户的业务访问模型。从技术层面来说,我们还有一些确定的套路可以去遵循,但是访问模型就是个极不确定的因素了。 + +我们这里还是以电商来举例说明,比如大促时用户下单这个逻辑。一个用户在购物车勾选商品去结算这个场景,用户的访问模型或业务场景就可能会有很多变化,比如是下1个商品的订单,还是同时5个商品的订单?每个商品的购买数量是1个、2个还是3个?商品的购买数量有没有限制?这些商品涉及1个卖家,还是多个卖家?不同卖家又会有不同的优惠折扣,是买二送一,还是满100送20?满一定额度之后是否包邮?全站促销是否有全站优惠,是否有时间段限制?优惠之间是否有优先级和互斥逻辑?支付方式是优先使用支付宝,还是微信,亦或是银行卡等等。 + +上面这些还只是简单描述,并且是针对单个用户的。当用户数量达到几十万,上百万之后,用户行为可能还有访问首页、详情页以及搜索的情况等等,场景更复杂,整个业务模型中的变量因素就会非常多,且不确定。 + +往往某个因素的变化,就可能带来容量模型的改变。假设1个商品,1个卖家,1个优惠策略,对DB产生的QPS是20,TPS是10,但是这其中的因素稍微一变化,产生的QPS和TPS就是翻倍的。如果这一点评估不到位,大促时实际的用户场景跟预估的偏差过大,系统可能就会挂掉。 + +所以,对于稳定性而言,用户访问模型才是关键,这个摸不准,只有技术是没用的,这就更需要我们能够深入业务,理解业务。 + +我们经常听到的“脱离业务谈架构,谈技术,都是不负责任的”,原因就在于此。希望今天的内容能够让你在学习到知识和技能的同时,也有所启发,切忌脱离业务,空谈技术。 + +今天我们分享了极端业务场景下,如何做好稳定性保障。关于稳定性保障,你还有哪些问题?有过怎样的经验?欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/22稳定性实践:容量规划之业务场景分析.md b/专栏/赵成的运维体系管理课/22稳定性实践:容量规划之业务场景分析.md new file mode 100644 index 0000000..2c07852 --- /dev/null +++ b/专栏/赵成的运维体系管理课/22稳定性实践:容量规划之业务场景分析.md @@ -0,0 +1,63 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 22 稳定性实践:容量规划之业务场景分析 + 上期文章我们从整体上介绍了极端业务场景下,如何做好稳定性保障工作。今天,我们结合电商大促这个场景,来看一下容量规划这项工作。 + +稳定性保障的一个难点是我们要面对一个非常复杂的因素,那就是业务模型,或者叫用户访问模型。因为它的不确定性,会衍生出很多不同的业务场景,而不同的场景,就会导致我们的应对策略有所不同。 + +所以,容量规划,就是对复杂业务场景的分析,通过一定的技术手段(如压力测试),来达到对资源合理扩容、有效规划的过程。 + +容量规划的场景分析 + +我们一直在讲,不能脱离业务场景空谈技术,所以我们还是先从电商大促这个业务场景入手来分析。 + +对于电商来说,核心链路就是交易链路。简单来说,就是用户要能登录,然后能通过浏览商品详情页下单订购,或者加购物车,通过购物车进行订购结算,这个过程中还要进行各种优惠的批价处理,库存的判断等等,形成订购之后,最终还要能够支付成功,一个完整的交易支付流程才算走完。 + +在大促的峰值时刻,场景可能又有不同,因为绝大部分用户选购什么商品,早已加入到了购物车中,且各种优惠券也已经申领成功,就等着最后这个时间点直接下单完成订购。所以,在大促这个场景下,交易下单这个环节是核心中的核心。 + +因为这个时间点的交易流量实在是太高了,所以近两年电商也改变了一些玩法,其目的就是希望减少峰值流量,让流量在整个大促阶段更加均匀。具体的运营和玩法细节这里就不详细介绍了。 + +那么,我们要应对的场景就相对清晰了,就是在大促零点峰值时刻,评估好交易流量,再进一步转化一下,就是每秒的交易订单峰值。 + +下图就是我们进行评估的路径分析示例,用户首先从首页、大促会场或者微信里的分享页面转化过来,然后通过搜索、店铺、详情页以及购物车进行最后的转化,形成订购下单和最终的支付。 + + + +具体数值的评估上,我们会跟产品运营团队共同讨论,整体的业务目标由运营团队给出,比如GMV目标收入,UV、PV、转化率、客单价以及商品单价,这些都是业务目标。通过这些业务数据,我们根据上图的路径逐步分解,就会逐步得出每一层的流量数据。 + +假设大促会场会有500万UV,根据GMV和客单价,如果要完成目标,推导出到详情页的转化率需要达到60%(产品运营需要努力达成这个业务目标),那详情页的UV就是300万;根据用户访问行为分析,对详情页的各个应用产生的QPS再做一个评估,这样单个应用的容量值就有了,然后再进一步向下转化,能够形成订购,形成加购物车的又有多少,再进行评估,最后就可以得出一个交易下单的峰值以及支付的峰值。 + +计算出峰值后,还要与历年评估的峰值数据,以及实际的峰值数据进行对比,根据对比情况进行调整。评估出来的这个峰值,就是系统要承诺支撑的容量,因为只有达到这个容量值,才能支撑业务完成对应的业务目标。 + +总结来说,这就是一个根据业务GMV、UV等目标,对技术上的交易下单峰值这个核心指标进行推导的过程。 + +那么,接下来就根据评估的各个应用和基础服务需要承担的流量,先扩容一轮,同时开始构造数据模型和压测模型来模拟真实流量,以此验证我们的系统是否能够达标,过程中再进行局部的扩容和优化。 + +一般来说,先进行单链路压测,比如购物车订购,详情页订购等场景的压测,达标后再进行多链路压测,最后再进行全链路压测,直至达成目标。为了能够保有一定的容量缓冲,最后几轮压测,我们会将压测流量调整到目标值的120%或150%,来保证系统能够应对足够极端的场景,这样才能够游刃有余地实现100%的目标。 + +构造压测的数据模型 + +如何构造压测的数据模型呢?这是一个比较复杂的问题,因为我们靠拍脑袋或者靠猜,是无法准确评估的。通常情况下,我们从以下两方面入手。 + +一方面,数据模型要接近真实场景。这就需要我们不断地积累经验,记录早期大促的详细数据和真实场景(比如不同用户购物车里的商品数量、优惠策略、不同渠道比例等,以及各种运营活动的玩法),这样可以最大程度地模拟真实的用户访问模型。这个过程,蘑菇街更多的还是手工推导,像阿里做得比较极致,可以通过BI系统,将往年模型和当年模型进行分析比对,直接生成对应的数据模型,甚至是多套模型。 + +另一方面,数据量要接近真实场景。数据量有很多维度,比如用户数量、商品数量、店铺数量、优惠券数量等等。这里一般会通过数据工厂这样的工具平台,结合运营团队给出的数据量评估,快速制造出对应量级的数据。另一种方式就是从线上将真实数据,进行敏感信息脱敏处理后,导出到另一张影子表中,专门提供给压测使用,这样做的好处是不会污染线上运行数据。 + +总结 + +通过上面的分享,我们应该不难发现,容量规划工作,单纯靠技术能力是无法解决的,需要经验和数据的积累,到了阿里这个体量就必须借助人工智能和各类分析算法这样更高级的手段才能解决。 + +同时,容量问题,也不是简简单单通过资源扩容这种成本投入就可以解决的,扩容是最后的执行手段,但是怎么合理的、科学的扩容,就需要有合理的规划。当业务体量和复杂度到达一定程度时,就要依靠技术人员对业务的深入理解。能够合理规划业务、技术和数据模型,是需要一些经验积累,以及在各类极端场景下的经历。 + +最后,如此复杂的技术体系,也只有在同样复杂的场景下才会被催生出来,才会有存在意义。所以,我们在学习借鉴时,还是要从各自的实际场景出发,慢慢积累,大可不必强求短期速成。 + +你自己是否有过容量规划的经历?遇到过哪些问题?欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/23稳定性实践:容量规划之压测系统建设.md b/专栏/赵成的运维体系管理课/23稳定性实践:容量规划之压测系统建设.md new file mode 100644 index 0000000..f3665d5 --- /dev/null +++ b/专栏/赵成的运维体系管理课/23稳定性实践:容量规划之压测系统建设.md @@ -0,0 +1,95 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 23 稳定性实践:容量规划之压测系统建设 + 容量规划离不开对业务场景的分析,分析出场景后,就要对这些场景进行模拟,也就是容量的压力测试,用来真实地验证系统容量和性能是否可以满足极端业务场景下的要求。同时,在这个过程中还要对容量不断进行扩缩容调整,以及系统的性能优化。 + +今天,我们就来看压力测试的技术实现方式:压力测试系统的建设。我们详细讲讲压力测试的几个维度。 + +第一个维度,压测粒度 + +压测粒度上,我们一般会遵照从小到大的规律来做。 + +1.单机单应用压力测试 + +优先摸清单个应用的访问模型是怎样的,再根据模型进行单机单应用压力测试。这时我们就可以拿到单个应用和单个应用集群的容量水位,这个值就是后续我们根据业务模型分析之后扩容的基础。 + +2.单链路压力测试 + +获取到单个应用集群的容量水位之后,就要开始对某些核心链路进行单独的压力测试,比如商品详情浏览链路、加购物车链路、订购下单链路等等。如下图的交易下单链路压测模型示例,连线上的数字是不同应用或接口调用的流量占比。 + + + +3.多链路/全链路压力测试 + +当单链路的压测都达标之后,我们就会组织多链路,或者全链路压测。多链路本质上就是多个单链路的组合,全链路就是多链路的组合。如下图就是多个交易场景的多链路组合。 + + + +第二个维度,压测接口及流量构造方式 + +接口一般分为HTTP接口和RPC接口,这一点应该不难理解,就不做过多讲解了。 + +流量构造方式上,根据压测粒度的不同,会采用不同的方式,我们常见的有以下几种方案。 + +1.线上流量回放 + +这种方式直接利用了线上流量模型,比较接近真实业务场景,常见的技术手段如TCPCopy,或者Tcpdump抓包保存线上请求流量。但是这种方式也存在一些代价,比如需要镜像请求流量,当线上流量非常大的时候就很难全部镜像下来,而且还需要大量额外的机器来保存流量镜像。到了回放阶段,还需要一些自动化的工具来支持,还要解决各种session问题,真正实施的时候,还是会有不少的工作量。 + +2.线上流量引流 + +既然线上回放比较麻烦,那为什么不直接使用线上流量进行压测呢?这个思路确实是可行的,我们前面讲过,压测的主要是HTTP和RPC两种类型的接口,为了保证单个应用的流量压力足够大,这里可以采取两种模式。 + +一个是将应用集群中的流量逐步引流到一台主机上,直到达到其容量阈值;另一个方案是,可以通过修改负载均衡中某台主机的权重,将更多的流量直接打到某台主机上,直到达到其容量阈值。 + +这个过程中,我们可以设定单台主机的CPU、Load或者QPS、RT等阈值指标,当指标超出正常阈值后就自动终止压测,这样就可以获取到初步的容量值。 + +这种方式的好处是,不需要额外的流量模拟,直接使用最真实的线上流量,操作方便,且更加真实。下图是两种引流的方案示例。 + + + +3.流量模拟 + +上述两种流量模拟方式,更适合日常单机单应用的容量压测和规划,但是对于大促这种极端业务场景,真实流量就很难模拟了,因为这种场景只有特定时刻才会有,我们在日常是无法通过线上流量构造出来的。 + +所以这里就需要利用数据工厂,最终通过流量平台来形成压测流量。这里的工具用到了Gatling,是一款开源的压测工具,用Scala开发的,后来我们针对自己的需求,比如自动生成压测脚本等,做了一些二次开发。 + + + +如果会有多种流量模型的话,就要生成多个流量模型,具体可见下图: + + + +第三个维度,施压方式 + +上面介绍了容量压测的构造过程,那接下来我们要做的就是对真实的线上系统施加压力流量了。很自然的,这里就需要有施加压力的机器,在上面“全链路压测系统”那张图中,你可以看到,我们的施压方式是通过上百台的机器根据压测脚本和压测数据对系统施压的,我来简单介绍一下大致过程。 + + +通过实现在Web控制台配置好的压测场景,自动生成压测脚本。 +利用数据工厂构造出压测数据,这个就是业务场景的模拟,像阿里做得比较完善,就可以借助AI和BI的技术手段生成很多压测模型,且基本都接近于现实情况下的业务场景。 +通过Web控制台,根据压测脚本和压测数据,生成压测任务,推送到压测集群的Master节点,再通过Master节点推动到上百台的Slave节点,然后就开始向线上系统施加模拟的流量压力了。 + + +关于施压机的分布,大部分仍然是跟线上系统在同机房内,少量会在公有云节点上。但是对于阿里,因为其自身的CDN节点遍布全球,所以他就可以将全球(主要是国内)的CDN节点作为施压机,更加真实地模拟真实用户从全球节点进入的真实访问流量。这种方式对于蘑菇街就显得成本过高,技术条件和细节也还达不到这个程度。 + +当前阿里已经将这种压测能力输出到了阿里云之上,可以说是对其云生态能力的有力补充,同时也为中小企业在容量规划和性能压测方面提供了很好的支持。 + +第四个维度,数据读写 + +压测过程中,对于读的流量更好构造,因为读请求本身不会对线上数据造成任何变更,但是对于写流量就完全不一样了,如果处理不好,会对线上数据造成污染,对商家和用户造成资损。 + +所以,对于写流量就要特殊处理,这块也有比较通用的解决方案,就是对压测的写请求做专门的标记。当请求要写数据库时,由分布式数据库的中间件框架中的逻辑来判断这个请求是否是压测请求,如果是压测写请求则路由到对应的影子库中,而不是直接写到线上正式的库中。 + +在这之前,要提前创建好对应的影子库。假设建立影子库的原则是原schema + mirro,如果正式库是order,则影子库为order_mirror,这时两个库中的数据量必须是一致的。对于非敏感信息,数据内容也可以保持一致,这样可以在最大程度上保证数据模型一致。 + +这里再呼应一下我们最开始提到的基础服务标准化工作,如果这个工作在前面做得扎实,它的优势在这里就体现出来了。我们刚刚提到的影子库的路由策略是基于中间件框架来实现的,如果使用的框架不一样,不是标准的,这个功能可能就很难应用起来。这一点在后面全链路以及开关等稳定性方案中,还会涉及到。 + +今天我们介绍了容量压测的技术方案,比较复杂,而且需要对相应场景进行针对性的建设。关于细节部分,你还有什么问题,欢迎留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/24稳定性实践:限流降级.md b/专栏/赵成的运维体系管理课/24稳定性实践:限流降级.md new file mode 100644 index 0000000..da319f7 --- /dev/null +++ b/专栏/赵成的运维体系管理课/24稳定性实践:限流降级.md @@ -0,0 +1,103 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 24 稳定性实践:限流降级 + 本周我们继续来讨论稳定性实践的内容。在现实情况下,当面对极端的业务场景时,瞬时的业务流量会带来大大超出系统真实容量的压力。 + +为了应对,前面我们介绍了容量规划方面的实践经验。不过,我们不会无限度地通过扩容资源来提升容量,因为无论从技术角度,还是从成本投入角度,这样做都是不划算的,也是不现实的。 + +所以,我们通常采取的策略就是限流降级,以保障承诺容量下的系统稳定;同时还有业务层面的开关预案执行,峰值时刻只保障核心业务功能,非核心业务功能就关闭。 + +今天我们就先来介绍一下限流降级的解决方案。 + +什么是限流和降级 + +首先,我们先梳理清楚限流和降级的概念,明白它们会发挥怎样的作用,这样才便于我们理解后续的解决方案。 + + +限流,它的作用是根据某个应用或基础部件的某些核心指标,如QPS或并发线程数,来决定是否将后续的请求进行拦截。比如我们设定1秒QPS阈值为200,如果某一秒的QPS为210,那超出的10个请求就会被拦截掉,直接返回约定的错误码或提示页面。 +降级,它的作用是通过判断某个应用或组件的服务状态是否正常,来决定是否继续提供服务。以RT举例,我们根据经验,一个应用的RT在50ms以内,可以正常提供服务,一旦超过50ms,可能就会导致周边依赖的报错或超时。所以,这时我们就要设定一个策略,如果应用的RT在某段时间内超过50ms的调用次数多于N次,那该应用或该应用的某个实例就必须降级,不再对外提供服务,可以在静默一定时间后(比如5s或10s)重新开启服务。 + + +这里再特别说一下降级,今天我们讲的内容可以理解为服务降级,后面我会介绍业务开关,可以理解为业务降级。这里只是叫法不同,不同的人有不同的理解,所以我们在讨论概念时,还是尽量回到我们要解决的问题和场景上来,上下文保持一致了,在观点和思路上也更容易达成一致。 + +讲到这里,再提个问题,我们讲的降级,和熔断这个概念是什么关系?你不妨停下来,按照我们刚刚讲过的思路思考一下。 + +常见的限流解决方案 + +我们先看几种常见的限流类型。 + +第一类,接入层限流。 + +作为业务流量的入口,我们限流的第一道关卡往往会设置在这里,而且接入层限流往往也是最有效的。这里又有两类解决方案,根据接入层所使用的技术方案而定。 + +1.Nginx限流 + +Nginx或其开源产品是最常用的Web服务器,我们使用的是TEngine。对于一个Web类应用,如Web页面或H5页面,我们通常会将限流策略增加到这一层,会设置QPS、并发数以及CPU的Idle作为限流指标。Nginx有对应的函数接口,可以获取到以上指标信息,然后通过Lua脚本实现限流的逻辑,并作为TEngine的插件安装即可。 + +2.API路由网关模式 + +对于客户端模式的接入,我们使用了API路由网关模式,一方面可以更方面地管理客户端与服务端的链接,另一方面也可以通过配置的方式管理服务接口,这里的服务管理会复用到微服务架构的配置中心,并实现相应的路由策略。对于QPS和并发限流,直接在配置中心中进行配置即可。 + +第二类,应用限流。 + +这一类的限流策略跟上面API路由网关模式的限流相似,同样是依赖配置中心管理,限流逻辑会配套服务化的框架完成。 + +第三类,基础服务限流。 + +主要针对数据库、缓存以及消息等基础服务组件的限流而设定。同样,限流逻辑会配套分布式数据库中间件,缓存或消息的框架来实现。 + +讲到这里,我来解释几个关键的技术点。 + + +资源和策略。资源是我们要进行限流的对象,可能是一个应用,或者一个方法,也可能是一个接口或者URL,体现了不同的限流粒度和类型。策略就是限流的规则,比如下面我们要提到的QPS和并发数限流。 +时间精度。主要指对于QPS、并发数或CPU的阈值判断。比如对于QPS,我们就会设定一个QPS时间精度(假设3s),如果低于阈值则不启用策略,如果超过阈值就启动限流策略。 +指标计数。对于并发限制请求,会统计当前的并发数,1次请求进入到限流模块加1,等请求结束退出时减1,当前正在处理的请求数就是并发数。对于QPS限流,统计QPS不能按照秒统计,因为第1s,系统可能就被打挂了,所以QPS得按照毫秒级别去统计,统计的级别越小,性能损耗越大。所以定在10ms~100ms的级别去统计会更平滑一些,比如将1s切成10份,每一份100ms,一个请求进来肯定会落在某一份上,这一份的计数值加1。计算当前的QPS,只需要将当前时间所在份的计数和前面9份的计数相加,内存里面需要维护当前秒和前面2秒的数据。 +限流方式。对于Nginx就针对总的请求进行限流即可,但是粒度会比较粗。对于应用层,因为配置中心的灵活性,其限流就可以做得更细化。比如可以针对不同来源限流,也可以针对去向限流,粒度上可以针对类级别限流,也可以针对不同的方法限流,同时还可以针对总的请求情况限流,这些灵活策略都可以在微服务的配置中心实现。 + + + + + +Spring AOP。对于Java应用,绝大多数公司都会用到Spring框架,包括我们上面讲到的分布式数据库等组件,也一样会依赖Spring框架,比如我们用到的MyBatis开源组件。而Spirng框架中的关键技术点,就是IoC和AOP,我们在限流方案的实现上,也会利用到相关技术。简单来说就是,我们通过配置需要限流的方法作为AOP的切入点,设定Advice拦截器,在请求调用某个方法,或请求结束退出某个方法时,进行上述的各种计数处理,同时决定是否要进行限流,如果限流就返回约定好的返回码,如果不限流就正常执行业务逻辑。基于AOP这样一个统一的技术原理,我们就可以开发出与业务逻辑无关的限流组件,通常会在对外的服务调用、数据库调用、缓存调用、消息调用这些接口方法上设置默认的切面,并在业务代码运行时注入,这样就可以做到对业务透明,无侵入性。 +Web类型的限流。对于Web类型URL接口限流,我们就利用Servlet的Filter机制进行控制即可。 +控制台。上面我们讲了各种配置和策略,如果都是通过人工来操作是不现实的,这时就需要开发对应的限流降级的控制台,将上述的各种配置和策略通过界面的方式进行管理,同时在配置完成之后,能够同步到对应的服务实例上。比如对于Nginx,当一个策略配置完成后,就要同步到指定的服务器上生成新的配置文件并Reload。对于配置中心模式的策略,也就是Spring AOP模式的限流,在控制台上配置完成后,就要将配置值同步更新到配置中心里,同时再通过运行时的依赖注入,在线上运行的业务代码中生效。 + + +整体简化的示意图如下: + + + +限流降级的难点 + +上面整体介绍了限流降级的解决方案,我们可以看到涉及到很多新概念,各种不同的限流类型,同时还有比较复杂的技术细节,所以要清晰地理解这些概念。 + +对于降级,主要是针对RT来进行判断,它的整个技术方案没有限流这么复杂,且思路上跟限流是相似的,所以我们就不再单独介绍降级的技术方案了。 + +从整个建设过程来看,我的体会是,限流降级的难点和关键还是在于整体技术栈的统一,以及后期对每个应用限流降级资源策略的准确把握和配置。 + +我们先来看整体技术栈的统一,这其实也就是我们在专栏最开始就讲到的标准化建设。这里我们会基于一个统一的技术栈进行限流降级方案的设计,要求有统一的Web服务器类型。对服务化框架、各类分布式框架以及代码开发框架(如Spring),这些都要有很明确的要求。如果这里面有某些应用使用的框架不同,那么这套统一的方案就无法推广落地。 + +我们在实际推广过程中就遇到很多类似的问题,导致大量的时间耗费在技术栈统一上,甚至会要求业务代码做出改变和调整,代码上线运行后再进行统一,这个代价是非常大的。 + +这也是为什么我们在一开始就非常强调标准化的重要性。这里我们再强调一下标准化,再来复习一下以应用为核心的运维体系的思维导图。 + + + +再来看对应用的限流降级资源策略的把握,这个就需要对应用和业务有深入的了解。比如开发人员要非常清楚哪些接口是核心接口,它的来源和去向有哪些;哪些来源是核心的,哪些是非核心的;如果要限流,需要对哪些接口限流,同时要重点保障哪些接口等等。 + +对于限流和降级的具体策略,就是QPS和并发数的配置,也要来源于线上实际运行维护的经验,才能知道配置多少是合适的,配置太大没有限流效果,太小又会频繁触发限流,影响正常业务运行。 + +所以,限流和降级也是一个动态调测和完善的过程,对于有些动态变化的资源是做不到一劳永逸的。 + +怎么办呢?一方面我们要依赖人的经验;另一方面,从最终的解决方案看,当调用次数和日志达到一定体量时,我们希望能够借助机器学习算法的手段,来帮助我们分析什么样的设置是最合理的。 + +今天我们讨论了限流和降级的概念、解决方案以及难点,欢迎留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/25稳定性实践:开关和预案.md b/专栏/赵成的运维体系管理课/25稳定性实践:开关和预案.md new file mode 100644 index 0000000..a766924 --- /dev/null +++ b/专栏/赵成的运维体系管理课/25稳定性实践:开关和预案.md @@ -0,0 +1,78 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 25 稳定性实践:开关和预案 + 在稳定性保障中,限流降级的技术方案,是针对服务接口层面的,也就是服务限流和服务降级。这里还有另外一个维度,就是业务维度,所以今天我们就从业务降级的维度来分享,也就是开关和预案。 + +如何理解开关和预案 + +开关,这个概念更多是业务和功能层面的,主要是针对单个功能的启用和停止进行控制,或者将功能状态在不同版本之间进行切换。 + +在业务层面,就像我们前面经常提到的大促场景案例,我们会关闭掉很多非核心功能,只保留交易链路的核心功能。比如我们认为商品评论是非核心功能,这时就会通过开关推送这种方案将这个功能关闭。当用户访问商品详情页时,这个接口就不再被调用,从用户角度来说,就是在大促峰值时刻看不到所浏览商品的评论列表。 + +在功能层面,我们技术架构中会使用缓存技术,但是要考虑到缓存有可能也会出现故障,比如不可访问,或者数据错乱异常等状况,这时我们就会考虑旁路掉缓存,直接将请求转到数据库这一层。 + +这里有两种做法:一种做法是通过我们上一篇介绍到的降级手段,也就是我们常说的熔断,自动化地旁路;另一种做法,比如在数据异常情况下,请求是正常的,但是数据是有问题的,这时就无法做到自动化旁路,就要通过主动推送开关的方式来实现。 + +预案,可以理解为让应用或业务进入到某种特定状态的复杂方案执行,这个方案最终会通过开关、限流和降级策略这些细粒度的技术来实现,是这些具体技术方案的场景化表现。 + +我们还是接着上面的这个案例来讨论。因为每个业务或应用都会有自己的开关配置,而且数量会有很多,如果在大促前一个个推送,效率就会跟不上,所以我们就会针对某个应用的具体场景,提供批量操作的手段,通过预案场景将同一应用,甚至多个应用的开关串联起来。 + +比如上面提到的商品详情页,我们不仅可以关闭商品评论,还可以关闭商品收藏提示、买家秀、店铺商品推荐、同类型商品推荐以及搭配推荐等等。有了场景化的预案,管理和维护起来就会更容易。 + +除了业务层面的预案,我们还可以将预案应用到应急场景下,比如上面提到的缓存故障异常。在真实场景下,要考虑得更全面,比如缓存能够支撑的业务访问量是要远远大于数据库的,这时我们就要做功能降级,这就要考虑数据库是否能够支撑住这么大的请求量(通常情况下肯定是支撑不住的)。所以,遇到这种场景,我们首要考虑的是限流,先将业务流量限掉三分之一甚至是一半,然后再将功能降级到数据库上。 + +这样就又涉及到多种策略的串行执行。如果没有预案都是单个执行的话,效率肯定会低,而且还可能涉及到多个应用都会执行相同的业务降级策略,这时就必须要有预案来统一管理,提前梳理好哪些应用需要在这种场景下执行对应的开关、限流和降级策略。 + +技术解决方案 + +技术方案上并不复杂,开关的字段主要以Key-Value方式管理,并从应用维度,通过应用名管理起来,这个对应关系就可以放到统一的控制台中管理。 + +下图是整个开关和预案管理,以及推送的示意图,我们一起分步骤看一下。 + + + +1.开关管理 + +通过上述我们所说的Key-Value方式保存,与代码中的具体Field字段对应起来。这里就又会涉及到我们上篇内容中讲到的Spring的AOP和注解技术。 + +如下面代码所示,我们通过注解方式定义了一个开关testKey,它与控制台中配置的Key相对应,并获取对应的Value取值,在业务运行阶段,我们就可以根据这个值,来决定业务执行逻辑,下面是简化的示例。 + +@AppSwitcher(key="key1",valueDes = "Boolean类型") + + private Boolean key1; + +代码中直接调用AppName对应的开关配置,进行不同业务逻辑的实现: + +Boolean key1 = MoguStableSwitch.isStableSwitchOn("key1"); + +if (key1) +{ +//开关打开时业务逻辑实现 +}else +{ +//开关关闭时业务逻辑实现 +} + + +2.开关推送 + +当在控制台上修改开关值后,会推送到微服务的配置中心做持久化,这样当应用下次重启时依然可以获取到变更后的值。还有另外一种方式,就是通过HTTP的方式推送,这种情况的应用场景是,当第一种情况失败时,为了让开关快速生效预留的第二个接口。 + +3.配置变更 + +应用中引入的开关SDK客户端会监听对应配置的变更,如果发生变化,就会马上重新获取,并在业务运行时生效。 + +4.预案执行 + +就是多个开关策略的串行执行,会重复上面这几个关键步骤。 + +关于开关和预案的内容,我们今天就介绍到这里。留一个问题,我们在上篇文章中介绍到限流降级方案的难点,请你思考一下,我们今天讲的开关预案这个内容,可能会遇到哪些难点呢?欢迎留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/26稳定性实践:全链路跟踪系统,技术运营能力的体现.md b/专栏/赵成的运维体系管理课/26稳定性实践:全链路跟踪系统,技术运营能力的体现.md new file mode 100644 index 0000000..199f543 --- /dev/null +++ b/专栏/赵成的运维体系管理课/26稳定性实践:全链路跟踪系统,技术运营能力的体现.md @@ -0,0 +1,107 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 26 稳定性实践:全链路跟踪系统,技术运营能力的体现 + 今天我们来分享全链路跟踪系统建设方面的内容。我们知道,随着微服务和分布式架构的引入,各类应用和基础组件形成了网状的分布式调用关系,这种复杂的调用关系就大大增加了问题定位、瓶颈分析、容量评估以及限流降级等稳定性保障工作的难度,如我们常见的调用网状关系。 + + + +图片出自:https://www.linkedin.com/pulse/100-million-members-125-hours-watched-per-day-hundreds-probst/ + +正是这样的背景,催生了分布式链路跟踪,也叫全链路跟踪的解决方案。 + +关于这一块的技术解决方案,在Google的Dapper论文发表之后,近些年业界已经有非常多且非常成熟的实践经验和开源产品。 + +比如阿里的鹰眼系统,就是全链路跟踪系统在国内的最佳实践;再比如美团点评的CAT分布式监控系统,也是从产品实践中逐步开源出来,在业界已经得到了非常广泛的应用;还有一些独立的开源产品,比如国内分布式监控技术专家吴晟创建的Skywalking项目,也是非常优秀的产品,而且也有比较广泛的应用。 + +除此之外,还有大量优秀的商业产品,这类产品通常叫APM,应用性能管理系统,比如国内的听云、博瑞、OneAPM等等,他们在产品化方面做的会更完善,在很多场景下可以非常方便地落地应用。 + +介绍上述这些产品,主要还是想说明,当前在分布式或全链路跟踪监控这个领域,无论是在技术还是产品层面都已经相对成熟,我们完全可以通过对这些产品的调研来选择适合自己的解决方案。 + +蘑菇街在这块也是自研了一套体系,但是技术方案和思路上跟上述这些开源或商业产品都很相似,所以技术层面我就不再做详细赘述。 + +如果想深入了解相关内容,一方面可以在网上找到非常多的资料,甚至是去阅读源码;另一方面还是推荐极客时间上陈皓老师的《左耳听风》专栏和杨波老师的《微服务架构核心20讲》,两位都是骨灰级的微服务和分布式架构专家,他们在技术层面的分享会更有深度和针对性。 + +全链路跟踪系统在技术运营层面的应用 + +接下来,主要分享我们利用全链路跟踪系统在技术运营层面做的一些事情,这里提到的运营,就是应用在线上运行时,我们根据应用运行数据所做的运行维护工作,这里我们会更加强调数据的作用。 + +同时,这里的一个核心技术点就是 TraceID,当请求从接入层进来时,这个TraceID就要被创建出来;或者是通过Nginx插件方式创建放到http的header里面;或者是通过RPC服务化框架生成。然后在后续的请求中,这个字段会通过框架自动传递到下一个调用方,而不需要业务考虑如何处理这个核心字段。 + +有了这个TraceID,我们就可以将一个完整的请求链路给串联起来了,这也是后面场景化应用的基础。下面我们就一起来看会有哪些具体的技术运营场景。 + +第一个场景,问题定位和排查 + +我们做全链路跟踪系统,要解决的首要问题就是在纷繁复杂的服务调用关系中快速准确地定位问题,所以这个场景是绕不开的。 + +我们常见的问题场景,主要有两类:瓶颈分析和异常错误定位。 + +首先看瓶颈分析。常见的问题就是某某页面变慢了,或者某个服务突然出现大量超时告警,因为无论是页面也好,还是服务也好,在分布式环境中都会依赖后端大量的其它服务或基础部件,所以定位类似的问题,我们期望能有一个详细的调用关系呈现出来,这样我们就可以非常方便快速地判断瓶颈出现在什么地方。 + +比如下图的情况,就是某个页面变慢。我们根据URL查看某次调用的情况,就发现瓶颈是在RateReadService的query接口出现了严重阻塞。接下来,我们就可以根据详细的IP地址信息,到这台机器上或者监控系统上,进一步判断这个应用或者这台主机的异常状况是什么,可能是机器故障,也可能是应用运行故障等等。 + + + +再来看一个案例。下图中我们可以看到,一次完整的请求耗时比较长,但是通过调用链分析会发现,其中任何一个单次请求的时延又非常低,并没有像上个案例那样有明显的请求瓶颈。我们再进一步分析,就会发现整个请求的列表非常长,且请求列表里面都是在访问缓存内容。很显然,这样的调用方式不合理,需要我们优化调用逻辑,要么通过批量接口方式,要么通过异步的方式,再或者要去分析业务场景是否合理。 + + + +通过上面的案例,我们可以看到,在应用了全链路跟踪的解决方案后,复杂调用关系下的问题定位就相对简单多了。 + +对于出现异常报错,也是一样的判断逻辑,限于篇幅我就不再赘述了。 + +第二个场景,服务运行状态分析 + +上面的问题定位,主要还是针对单次请求或相对独立的场景进行的。更进一步,我们在采集了海量请求和调用关系数据后,还可以分析出更有价值的服务运行信息。比如以下几类信息。 + +1.服务运行质量 + +一个应用对外可能提供HTTP服务,也可能提供RPC接口。针对这两类不同的接口,我们可以通过一段时间的数据收集形成服务接口运行状态的分析,也就是应用层的运行监控,常见的监控指标有QPS、RT和错误码,同时还可以跟之前的趋势进行对比。这样就可以对一个应用,以及对提供的服务运行情况有一个完整的视图。 + + + +2.应用和服务依赖 + +除了上述单个应用的运行状态,我们还可以根据调用链的分析,统计出应用与应用之间,服务与服务之间的依赖关系及依赖比例,如下图所示。 + + + +这个依赖管理的作用,就是给我们前面介绍的容量压测和限流降级这两个工作做好准备。我们可以根据来源依赖和比例评估单链路的扩容准备;同时根据去向依赖进行流量拆分,为下游应用的扩容提供依据,因为这个依赖比例完全来源于线上真实调用,所以能够反映出真实的业务访问模型。 + +同时,根据这个依赖关系,特别是服务依赖关系,我们还可以进一步分析依赖间的强弱关系,也就是强弱依赖。这一点又对我们做限流降级提供了对应的依据,也就是我们前面所说的,我们限流也好,降级也好,都是优先对非核心业务的限流和降级,这样的业务形成的依赖,我们就认为是弱依赖,是不关键的;但是对于核心业务我们就要优先保障,它们形成的依赖关系,是强依赖。无论是扩容也好,还是优化性能也罢,都要最大限度地确保强依赖关系的调用成功。 + +所以,强弱依赖的分析,还是要从业务场景入手。比如对于电商来说,核心就是交易链路,我们就要判断如果一条链路上的某个应用或服务失败了,是不是会影响订购下单,或者影响支付收钱,如果影响,就要标注为强依赖,这个应用就要标注为核心应用;如果这个应用失败了,可以通过限流或降级的方式绕过,只是影响用户体验,但是不影响用户订购支付,那这个依赖关系就可以标注为弱依赖,该应用就可以标注为非核心应用。 + +同时,因为我们的业务场景和需求在不断变化,应用和服务间的调用关系和依赖关系也是在不断变化中的,这就需要我们不断地分析和调整强弱依赖关系,同时也要关注各种调用间的合理性,这个过程中就会有大量的可优化的工作。 + +通常情况下,这些事情对于业务架构师和运维人员来说,都会比较关注。因为业务架构师要对业务访问模型十分了解,他要经常关注这些信息;而运维会关注线上稳定性,需要在关键时刻执行限流降级或开关预案策略,所以也必须对这些信息非常熟悉。 + +3.依赖关系的服务质量 + +上面介绍了应用和服务间的依赖管理,同样的我们也会关注被依赖的应用或服务的实时运行状态和质量,这样就可以看到应用间实时的调用状态。是不是有的应用调用QPS突然增加了,或者RT突然暴涨,通过这个依赖关系就可以快速确认。 + + + +第三类场景,业务全息 + +顾名思义,业务全息就是全链路跟踪系统与业务信息的关联。从上述的介绍中,我们可以看到,全链路跟踪系统的应用更多的还是在技术层面,比如定位“应用或服务”的问题,应用或服务间的依赖关系等等。 + +但是现实中,我们也会遇到大量的业务链路分析的场景,比如可能会有针对某个订单在不同阶段的状态等。假设一个情况是用户投诉,他的订单没有享受到满100元包邮的优惠,这时我们就要去查找用户从商品浏览、加购物车到下单整个环节的信息,来判断问题出在哪儿。其实,这个场景和一个请求的全链路跟踪非常相似。 + +所以,为了能够在业务上也采用类似的思路,我们就将前面介绍到的请求链路上的唯一TraceID与业务上的订单ID、用户ID、商品ID等信息进行关联,当出现业务问题需要排查时,就会根据对应的ID将一串业务链整个提取出来,然后进行问题确认。这就会极大地提升解决业务问题的效率。 + +总结 + +今天我们从技术运营层面的应用这个角度重新认识了全链路跟踪系统。同时,从这个案例中,我们也应该看到,技术、产品和运营相辅相成,共同促进彼此的完善和成熟。 + +全链路跟踪系统在技术方案的广泛应用,给我们提供了大量可分析处理的线上运行数据,从这些数据中,我们又能提炼出对线上稳定运行更有价值的信息。所以,技术之外,我们也应该更多地考虑技术在价值方面的呈现。 + +今天的内容就介绍到这里,你在这方面遇到过哪些问题,有怎样的经验,欢迎留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/27故障管理:谈谈我对故障的理解.md b/专栏/赵成的运维体系管理课/27故障管理:谈谈我对故障的理解.md new file mode 100644 index 0000000..033f5e5 --- /dev/null +++ b/专栏/赵成的运维体系管理课/27故障管理:谈谈我对故障的理解.md @@ -0,0 +1,80 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 27 故障管理:谈谈我对故障的理解 + 对于任何一个技术团队来说,最令人痛苦、最不愿面对的事情是什么?我想答案只有一个,那就是:故障。 + +无论是故障发生时的极度焦虑无助,还是故障处理过程中的煎熬痛苦,以及故障复盘之后的失落消沉,都是我们不愿提及的痛苦感受。在海外,故障复盘的英文单词是Postmortem,它有另外一个意思就是验尸,想想就觉得痛苦不堪,同时还带有一丝恐怖的意味。 + +写故障相关的文章,也着实比较痛苦。一方面回顾各种故障场景,确实不是一件令人愉悦的体验;另一方面,故障管理这个事情,跟技术、管理、团队、人员息息相关,也是一套复杂的体系。 + +我们看Google SRE这本书(《SRE:Google运维解密》),绝大部分章节就是在介绍故障相关的内容。其实看看这本书就能明白稳定性和故障管理这项系统工程的复杂度了,而且从本质上讲,SRE的岗位职责在很大程度上就是应对故障。 + +所以,接下来的几期文章,我会谈谈我对故障管理的理解,以及一些实际经历的感受,也希望我们每一个人和团队都能够在故障管理中得到涅槃重生。 + +今天,先谈谈我们应该如何来看待故障这个事情。 + +系统正常,只是该系统无数异常情况下的一种特例 + +上面这句话,来自Google SRE这本书,我认为这是一个观点,但是更重要的,它更是一个事实。所以,正确理解故障,首先要接受这个现实。 + +故障,是一种常态,任何一个软件系统都避免不了,国内最牛的BAT避免不了,国外最牛的Google、Amazon、Facebook、Twitter等也避免不了。业务体量越大,系统越复杂,问题和故障就越多,出现故障是必然的。 + +可能你会有疑问,既然他们也存在各种故障,但是在我们的印象中,好像也没经常遇到这些大型网站整天出问题或不可访问的情况,这恰恰说明了这些公司的稳定性保障做得非常到位。 + +这里有一个非常重要的体现,就是Design for Failure的理念。我们的目标和注意力不应该放在消除故障,或者不允许故障发生上,因为我们无法杜绝故障。所以,我们更应该考虑的是,怎么让系统更健壮,在一般的问题面前,仍然可以岿然不动,甚至是出现了故障,也能够让业务更快恢复起来。 + +其实对这个理念的实践,我们在前面都已经介绍过了,比如限流降级、容量评估以及开关预案等技术方案的稳定性保障体系,这些技术方案本质上并不是为了杜绝故障发生,而是为了能够更好地应对故障。 + +同样的,我们刚提到的那些国内外超大型网站,之所以能够保持很高的稳定性和业务连续性,恰恰是说明他们在故障隔离、快速恢复、容灾切换这些方面做得非常优秀,一般的问题或故障,根本不会影响到业务访问。 + +所以,转变一下思路,重新理解系统运行的这种特点,会给我们后续在如何面对故障、管理故障的工作中带来不一样的思考方式。 + +故障永远只是表面现象,其背后技术和管理上的问题才是根因 + +简单表述一下,就是永远不要将注意力放在故障本身上,一定要将注意力放到故障背后的技术和管理问题上去。 + +这里的逻辑是这样的,技术和管理上的问题,积累到一定量通过故障的形式爆发出来,所以故障是现象,是在给我们严重提醒。 + +有时我们过分关注故障本身,就容易揪着跟故障相关的责任人不放,这样会给责任人造成很大的负面压力,进而导致一些负面效应的产生,这一块在后面我还会专门分享。 + +与之对应的改进措施,往往就容易变成如何杜绝故障。前面我们讲到,从现实情况看这是完全不可能的,所以就容易输出一些无法落地、无法量化的改进措施。 + +你可以思考一下,面对故障的时候,是不是经常出现上述这两种情况。 + +所以,想要更好地应对和管理故障,当故障发生后,我们需要考虑的问题应该是其背后存在的技术和管理问题。这里和你分享我自己在故障后的复盘中,经常会反思和提出的几个问题。 + + +为什么会频繁出故障?是不是人员技术不过硬?人为操作太多,自动化平台不完善,操作没有闭环?代码发布后的快速回滚措施不到位? +为什么一个小问题或者某个部件失效,会导致全站宕机?进一步考虑,是不是业务高速发展,技术架构上耦合太紧,任何一个小动作都可能是最后一根稻草?是不是容量评估靠拍脑袋,系统扛不住才知道容量出问题了?是不是限流降级等保障手段缺失,或者有技术方案,但是落地效果不好? +为什么发生了故障没法快速知道并且快速恢复?进一步考虑,是不是监控不完善?告警太多人员麻木?定位问题效率低,迟迟找不到原因?故障隔离还不够完善?故障预案纸上谈兵? +管理上,团队成员线上敬畏意识不够?还是我们宣传强调不到位?Oncall机制是否还需要完善?故障应对时的组织协作是不是还有待提升? + + +总结下来,任何一个故障的原因都可以归结到具体的技术和管理问题上,在故障复盘过程中,通常会聚焦在某个故障个例上,归纳出来的是一个个非常具体的改进措施。 + +用一句话总结:“理解一个系统应该如何工作并不能使人成为专家,只能靠调查系统为何不能正常工作才行。”(From SRE ,by Brian Redman) + +最后,作为管理者,我会问自己一个终极问题:下次出现类似问题,怎么才能更快地发现问题,更快地恢复业务?即使这一次的故障应对已经做得非常好了,下次是否可以有更进一步的改进? + +这个问题,会促使我个人更加全面地思考,且能够关注到更全局的关键点上。比如,是不是应该考虑有更加完善的发布系统,减少人为操作;是不是应该有整体的稳定性平台建设,包括限流降级、开关预案、强弱依赖、容量评估、全链路跟踪等子系统,以及建设完成后,应该如何一步步的落地;还有,故障预案和演练应该如何有效的组织起来,毕竟这些是从全局考虑,自上而下的一个过程。 + +最后 + +再表达两个观点。 + +第一,出问题,管理者要先自我反省。不能一味地揪着员工的错误不放,员工更多的是整个体系中的执行者,做得不到位,一定是体系上还存在不完善的地方或漏洞。在这一点上,管理者应该重点反思才对。 + +第二,强调技术解决问题,而不是单纯地靠增加管理流程和检查环节来解决问题,技术手段暂时无法满足的,可以靠管理手段来辅助。比如我上面提到的就基本都是技术手段,但是要建设一个完善的体系肯定要有一个过程,特别是对于创业公司。这时可以辅以一些管理措施,比如靠宣传学习,提升人员的线上安全稳定意识,必要的Double Check,复杂操作的Checklist等,但是这些只能作为辅助手段,一定不能是常态,必须尽快将这些人为动作转化到技术平台中去。 + +这样做的原因也很明显,单纯的管理手段还是靠人,跟之前没有本质区别,只不过是更加谨小慎微了一些而已。同时,随着系统复杂度越来越高,迟早有一天会超出单纯人力的认知范围和掌控能力,各种人力的管理成本也会随之上升。 + +今天和你分享了我对故障这件事情的理解,期望这样一个不同角度的理解能够带给你一些启发,欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/28故障管理:故障定级和定责.md b/专栏/赵成的运维体系管理课/28故障管理:故障定级和定责.md new file mode 100644 index 0000000..0c62067 --- /dev/null +++ b/专栏/赵成的运维体系管理课/28故障管理:故障定级和定责.md @@ -0,0 +1,89 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 28 故障管理:故障定级和定责 + 故障管理的第一步是对故障的理解,只有正确地面对故障,我们才能够找到更合理的处理方式。今天就来和你分享关于故障定级和定责方面的经验。 + +故障的定级标准 + +上期文章中介绍到,如果我们的注意力仅仅盯着故障本身,就非常容易揪着责任人不放,进而形成一些负面效应,所以我们要将更多的注意力放到故障背后的技术和管理问题上。 + +但是,这并不是说对故障本身就可以不重视,相反,故障发生后,一定要严肃对待。这里就需要制定相应的标准和规范来指导我们的处理过程。这个过程并不是一定找出谁来承担责任,或者一定要进行处罚,而是期望通过这样的过程,让我们能够从故障中深刻地认识到我们存在的不足,并制定出后续的改进措施。 + +这里有一个关键角色,我们称之为技术支持,也有的团队叫 NOC(Network Operation Center)。这个角色主要有两个职责:一是跟踪线上故障处理和组织故障复盘,二是制定故障定级定责标准,同时有权对故障做出定级和定责,有点像法院法官的角色,而上面的两个标准就像是法律条款,法官依法办事,做到公平公正。 + +所以,这里的一个关键就是我们要有明确的故障定级标准。这个标准主要为了判定故障影响程度,且各相关利益方能够基于统一的标准判断和评估。 + +现实情况中,因为各方受到故障的影响不同,对故障影响的理解也不同,所以复盘过程中,经常会出现下面这两种争执场景。 + + +技术支持判定故障很严重,但是责任方认为没什么大不了的,不应该把故障等级判定到如此之高; +技术支持认为故障影响较小,但是受影响方却认为十分严重,不应该将故障等级判定得这么低。 + + +遇到这种情况,技术支持作为故障判定的法官,就必须拿出严格的判定标准,并说明为什么这么判定。 + +我们将故障等级设置为P0~P4这么5个级别,P0为最高,P4为最低。对于电商,主要以交易下跌、支付下跌、广告收入资损这些跟钱相关的指标为衡量标准。对于其他业务如用户IM等,主要区分业务类型,制定符合业务特点的定级标准。两个示例如下。 + +交易链路故障定级标准示例: + + + +用户IM故障定级标准示例: + + + +故障定级的标准,会由技术支持与各个业务研发团队进行点对点的细节沟通讨论,从业务影响角度把影响面、影响时长这些因素串联起来。这样即使在后续出现争执,也会有对应的标准参考。这个标准可能覆盖不到有些故障影响或特例,但是技术支持可以根据自己的经验进行“自由裁量”。同时,每个季度或半年对标准进行一次修订和完善。这样,我们前面提到的争执就会越来越少,再加上我们内部树立了“技术支持角色拥有绝对话语权和决策权”的制度,执行过程中就会顺畅很多。 + +对于P0故障,通常是由两个级以上的P1故障叠加造成的,这说明已经发生了非常严重的全站故障。 + +不同的故障定级,在故障应对时采取的策略也就不同。一般来说,P2及以上故障就需要所有相关责任人马上上线处理,并及时恢复业务。对于P3或P4的问题,要求会适当放宽。整个过程,技术支持会给出一个基本判断,然后会组织召集临时故障应急小组处理。 + +关于全年全站,或者分业务的可用性和可靠性,这个可以借鉴业界通用的MTBF(Mean Time Between Failures,平均故障间隔时间)、MTTR(Mean Time To Recovery,平均修复时间)、MTTF(Mean Time To Failure,平均失效前时间)这几个指标来衡量,这里我们就不详细介绍了。 + +故障的定责标准 + +上述的故障定级标准,主要是用来判定故障等级,使得故障相关方不至于过分纠结在等级标准上。而故障定责的主要目的是判定责任方。这就需要有明确的故障定责标准,我认为有两个主要目的。 + + +避免扯皮推诿。比如我认为是你的责任,你认为是我的责任,大家争执不清,甚至出现诋毁攻击的情况。 +正视问题,严肃对待。不是为了处罚,但是作为责任方或责任团队一定要正视问题,找出自身不足,作为改进的主要责任者,来落地或推进改进措施。 + + +关于第一点,避免扯皮推诿,大概是很多团队都会遇到的非常头疼的问题,也是最令人生厌的问题,所以避免这样的问题,就必须得有相对清晰的定责标准。 + +比如我们经常会提到的运维背锅的说法,这种情况出现的场景经常是,某个核心功能出现了故障,有大量超时或失败,对应的开发定位一下,说我的代码没有问题,场景也没复现,这个应该是运维负责的主机、网络或者其他基础服务有问题吧,这个责任很轻易地就甩给了运维。类似的上游把责任推脱到下游的情况是经常出现的。 + +我们自己的实践,是严禁这种情况出现的。也就是作为受影响方,开发负责人有责任端到端地把问题定位清楚,只有当定位出来的问题确实是发生在运维的某个部件时,才允许将责任传递 ,否则不允许出现将自己的问题简单排除,就推断或者感觉应该是其他责任方的问题,然后终止后续排查或者指定下游责任方的情况出现。 + +当然,在这个过程中,如果需要配合,是可以要求各方投入支持的,因为共同的目标还是要清晰定位问题,找到解决方案。 + +这时候,就更加需要开放和宽松的氛围,如果大家始终朝着如何摆脱责任或甩锅的目标行事,就会出现非常负面的效应,这一点后面我们会详细分享。 + +关于定责,我们划分了几个维度,我简单示例如下。 + +1.变更执行 + +比如变更方没有及时通知到受影响方,或者事先没有进行充分的评估,出现问题,责任在变更方;如果通知到位,受影响方没有做好准备措施导致出现问题,责任在受影响方;变更操作的实际影响程度大大超出预期,导致受影响方准备不足出现故障,责任在变更方。 + +2.服务依赖 + +比如私自调用接口,或者调用方式不符合约定规则,责任在调用方;如果是服务方没有明确示例或说明,导致调用方出现问题,责任在服务方等等。 + +3.第三方责任 + +比如机房IDC电力故障、服务器故障、运营商网络故障等等,如果确实是不可抗力导致,责任在第三方;但是因自身的冗余或故障预案问题导致故障,责任在应用Owner。 + +有了这样的原则,在故障复盘时,就可以有效减少不和谐氛围的出现。因为每个公司的业务形态和特点不一样,里面的具体内容可能也不一样,上述的定责标准可能不完全适用,所以仅供示例参考。如果你在日常深受故障定责的困扰,建议尽快把规则明确起来,并能够与各方达成一致,这样就会最大程度地减少扯皮推诿的情况出现。 + +总结 + +今天我们讨论了故障管理中的定级和定责标准。蘑菇街在这方面的具体管理执行中,还是取得了不错的效果,所以分享出来,欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/29故障管理:鼓励做事,而不是处罚错误.md b/专栏/赵成的运维体系管理课/29故障管理:鼓励做事,而不是处罚错误.md new file mode 100644 index 0000000..e733ce4 --- /dev/null +++ b/专栏/赵成的运维体系管理课/29故障管理:鼓励做事,而不是处罚错误.md @@ -0,0 +1,101 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 29 故障管理:鼓励做事,而不是处罚错误 + 故障发生后,我们一定要严肃对待,要对关键责任人或责任方定责,但是定责的目的不是处罚,因为故障复盘一旦以处罚为导向,就会导致非常严重的负面效应。 + +我们应该如何对待定责和处罚呢?今天就来分享一下我的理解,以及我个人的一些处理方式。 + +关于定责和处罚 + +定责的过程,是找出根因,针对不足找出改进措施,落实责任人。定责的目的,是责任到人,并且责任人能够真真切切地认识到自己的不足之处,能够主导改进措施的落地。同时,也让整个团队认识到,我们对于故障的态度一定是严肃严格的。 + +但是,在具体的执行过程中,我们一定要区分定责和处罚。定责是对事不对人的,但是处罚就变成对人不对事了,因为处罚一定会跟薪资、奖金、绩效、晋升等这些跟个人利益相关的事情直接挂钩。 + +我的观点是,处罚不能一刀切,更不能上纲上线,一定要慎重。 + +关于是否处罚,我个人认为可以遵循这样的原则:对于有明确底线,坚决不允许触碰的规则,如果因不遵守规则,故意触犯,导致了严重故障的出现,这种情况是要处罚的。 + +这样的规则建议通过设定高压线的方式让团队成员牢记心中,就像“酒后不开车”一样,简单明确。我大致列举几条我们的“高压线规则”。 + + +未经发布系统,私自变更线上代码和配置; +未经授权,私自在业务高峰期进行硬件和网络设备变更; +未经严格的方案准备和评审,直接进行线上高危设备操作,如交换机、路由器防火墙等; +未经授权,私自在生产环境进行调测性质的操作; +未经授权,私自变更生产环境数据信息。 + + +通过高压线去加强安全稳定意识,目的是要让每一个人对线上都心存敬畏。从我们的经验来看,绝大多数的严重故障都是因为无意识或意识薄弱导致的,并不是因为单纯的技术能力不足等技术因素。特别是那种自我感觉没问题,就把命令噼里啪啦敲到线上的操作,是最致命的。 + +2016年是公司业务高速发展的阶段,设备扩容比较频繁,网络割接操作也很多。因为没有明确严格的规则,导致团队成员为了赶工期,在白天进行网络设备变更,结果就是严重的P0和P1故障频发。复盘过程中,很多人的反馈就是:我以为是没问题的,我以为是没影响的。其实恰恰就是因为这种“想当然”,导致了严重故障。 + +后来我们总结,在这些关键的操作上,如果大家意识到位,能够谨小慎微,绝大多数低级失误都是可以避免的,所以针对这些场景,我们就专门制定了高压线。 + +制定高压线的效果也是很明显的。在近两年的时间里,我们没有出现过任何一例因为意识缺失或低级失误导致的P0和P1故障。反倒是跟我们有产品技术合作的第三方厂商,有时会出问题,我们了解下来,基本都是白天变更导致的,要么是没有放到凌晨实施,要么就是白天临时变更,没有准备充分。 + +所以,制定明确的高压线规则,提升意识,碰一次就要疼一次。这个时候的惩罚是为了提升责任人的敬畏意识和主观意识,人为失误才会减少,处罚也才会有效。 + +当然,更好的结果是,类似的故障越来少,处罚的执行也基本没有了。 + +鼓励做事,而不是处罚错误 + +前面我们分享过这句话: + + +理解一个系统应该如何工作并不能使人成为专家,只能靠调查系统为何不能正常工作才行。(From SRE ,by Brian Redman) + + +我想很多朋友跟我一样,都会产生共鸣。仔细考虑一下,我们每个人的技术能力提升,甚至是质的提升,基本都是伴随着大大小小故障的发生、处理、复盘和改进,这样一个过程提升起来的。 + +虽然我们不希望有故障发生,但是真的没有了故障,我们也就没有了真刀真枪实战成长的机会。我们对待故障一定要客观和辩证地理解,特别是对于管理者来说,对于故障,一定要有容忍度,一定要有耐心。 + +发生故障一方面暴露出我们整体技术架构的不足之处,另一方面,也给我们提供了未来改进的方向。同时,也是最重要的,我们的团队和人员,在这样一次次痛苦的经历后,各方面的能力都得到了锻炼,团队和个人素养也一定会有大幅度提升。所以,对故障有容忍度,有耐心,我们的团队就会变得越来越强,对于故障的应对也会变得更加游刃有余。 + +反观另一种管理方式,一出故障就劈头盖脸地把团队和责任人骂一通,并且还要严厉处罚的方式,最后的效果就是严重打击士气,适得其反。 + +所以,作为管理者,当一个故障发生之后,除故障本身外,还要关注更全面的内容,比如关注人、事情背景和前因后果。 + +列举一下我之前经常遇到的两种情况。 + +1.员工积极主动地承担了一些极具挑战性的工作,需要尝试某个新技术或解决方案,而团队、业界和社区可能都没有可供直接借鉴的经验,结果在落地的过程中踩到了一些坑,导致出现了问题。 + +这种情况在成熟的技术和产品中也极容易出现,比如开源产品,有时候不翻源码都不知道某个地方埋着深坑。即使是商业产品,像Oracle在他的官方bug库里列着一堆已知bug,就是明确告知用户使用时要非常小心,真的碰到bug了,官方一般也是建议升级版本。根据我的经验,对于这样一个庞大的bug库,不出问题一般没人会去把整个bug list都看一遍的。 + +2.业务高速发展时期,业务量成指数级增长时,团队人员技能和经验水平整体上还没法很好地应对,这个时候可能任何一个小变动都是最后一根稻草。这种时候就需要群策群力,而不是简单处罚了事。 + +这两种情况都需要全面了解信息之后,再做判断。甚至要优先传递信任,而不是不管三七二十一就直接批评和处罚。何况,如果不出问题,可能很多主管压根都没有关注过员工在做的事情,过程中是否有困难,是否需要支持等等,这本身就是管理者的失责。 + +在当前这种新业务和新形态不断涌现,又要求快速迭代的背景下,软件开发这种技术工作很大程度上还是要依赖员工的创新和创造。所以在很多情况下,管理者一定要对故障有一定的容忍度,因为员工努力做事的积极性一旦被打击,变得畏首畏尾起来,也就谈不上什么技术进步和突破了,而且想要再恢复起来也会非常困难,最终很大概率上会导致优秀人才流失,为别人做了嫁衣。 + +所以,团队内部一定要营造出鼓励做事向前冲的氛围,而不是制造担心犯错误被处罚的恐慌氛围。 + +处罚的“负”作用远超我们的想象 + +前面讲到,定责不是处罚,是就事论事。员工哪些地方做得不到位,是能力不足还是经验欠缺,这些东西主管可以基于事实,很正式、严肃地表达出来。通常情况下,员工也大多是可以接受的。同时帮助员工进一步分析应该怎么提升,或者聆听员工有什么求助或困难。这种情况下,员工的感受是,主管尊重我,在帮助我。 + +但是,话题和目的一旦转到处罚相关的事情,员工一般会有两种类型的反应:一种是消沉低落(反正都是我的错,你说咋样就咋样);另外一种是极力地反抗和质疑(凭什么罚我不罚别人,又不是我一个人的问题等等)。 + +这时,员工的注意力也会从怎么改进,转变到为什么要处罚我的角度上来。在这种消极和抵抗情绪中再去沟通什么改进措施,就没有任何效果了。作为管理者,也就非常容易陷入到与被沟通者的反复解释中,他质疑一句,你就解释一句,但是他压根就没听进去。 + +从我们的经验来看,如果定责跟绩效强挂钩,团队就陷入这种恐慌、质疑、挑战以致最终相互不信任的局面。员工害怕、甚至拒绝承担责任,宁可少做不做,也不愿多做多错,团队沟通成本上升,运作效率自然下降。特别是一个故障如果是涉及多方的,扯皮推诿就开始了,都想着把责任撇干净,甚至当众相互指责,这个负面效应杀伤力极大。 + +后来我们就取消挂钩,对于出现的故障有专门的系统记录,然后把这件事情放到员工一个季度,半年,甚至一年表现中进行整体判断。如果员工整体的表现都是不错的,甚至是突出的,说明员工已经改正或者那件事情确实是偶尔的失误导致,这种情况下员工仍然会有好的绩效。但如果是频繁出问题,这种情况就基于事实反馈,也会更加容易沟通。 + +我的团队中,就出现过类似的情况。有员工导致了线上严重故障,当个季度绩效较差,但是因为全年表现突出,年终仍然是优秀;也有员工,因为连着两个季度触碰高压线,全年又无明显突出的表现,年终绩效也就不理想。 + +总结 + +我们做个小总结,对于故障的态度,我们还是得要辩证地看。对于是否处罚,也要具体问题具体分析。完全不处罚,或者一刀切,一律处罚都是不可取的。作为管理者,还是要将规则和标准定义清楚,在执行时才能够做到公平公正。 + +另外,管理者除了关注故障本身之外,还要考虑得更加全面一些,要关注到人的感受,关注事情的前因后果,只有这样,在管理执行过程中才会让员工感受到尊重和信任。 + +最后,你在故障定责和处罚方面有什么经历和想法,欢迎留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/30故障管理:故障应急和故障复盘.md b/专栏/赵成的运维体系管理课/30故障管理:故障应急和故障复盘.md new file mode 100644 index 0000000..91d02e2 --- /dev/null +++ b/专栏/赵成的运维体系管理课/30故障管理:故障应急和故障复盘.md @@ -0,0 +1,101 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 30 故障管理:故障应急和故障复盘 + 故障发生后,我们一定要严肃对待,要对关键责任人或责任方定责,但是定责的目的不是处罚,因为故障复盘一旦以处罚为导向,就会导致非常严重的负面效应。 + +我们应该如何对待定责和处罚呢?今天就来分享一下我的理解,以及我个人的一些处理方式。 + +关于定责和处罚 + +定责的过程,是找出根因,针对不足找出改进措施,落实责任人。定责的目的,是责任到人,并且责任人能够真真切切地认识到自己的不足之处,能够主导改进措施的落地。同时,也让整个团队认识到,我们对于故障的态度一定是严肃严格的。 + +但是,在具体的执行过程中,我们一定要区分定责和处罚。定责是对事不对人的,但是处罚就变成对人不对事了,因为处罚一定会跟薪资、奖金、绩效、晋升等这些跟个人利益相关的事情直接挂钩。 + +我的观点是,处罚不能一刀切,更不能上纲上线,一定要慎重。 + +关于是否处罚,我个人认为可以遵循这样的原则:对于有明确底线,坚决不允许触碰的规则,如果因不遵守规则,故意触犯,导致了严重故障的出现,这种情况是要处罚的。 + +这样的规则建议通过设定高压线的方式让团队成员牢记心中,就像“酒后不开车”一样,简单明确。我大致列举几条我们的“高压线规则”。 + + +未经发布系统,私自变更线上代码和配置; +未经授权,私自在业务高峰期进行硬件和网络设备变更; +未经严格的方案准备和评审,直接进行线上高危设备操作,如交换机、路由器防火墙等; +未经授权,私自在生产环境进行调测性质的操作; +未经授权,私自变更生产环境数据信息。 + + +通过高压线去加强安全稳定意识,目的是要让每一个人对线上都心存敬畏。从我们的经验来看,绝大多数的严重故障都是因为无意识或意识薄弱导致的,并不是因为单纯的技术能力不足等技术因素。特别是那种自我感觉没问题,就把命令噼里啪啦敲到线上的操作,是最致命的。 + +2016年是公司业务高速发展的阶段,设备扩容比较频繁,网络割接操作也很多。因为没有明确严格的规则,导致团队成员为了赶工期,在白天进行网络设备变更,结果就是严重的P0和P1故障频发。复盘过程中,很多人的反馈就是:我以为是没问题的,我以为是没影响的。其实恰恰就是因为这种“想当然”,导致了严重故障。 + +后来我们总结,在这些关键的操作上,如果大家意识到位,能够谨小慎微,绝大多数低级失误都是可以避免的,所以针对这些场景,我们就专门制定了高压线。 + +制定高压线的效果也是很明显的。在近两年的时间里,我们没有出现过任何一例因为意识缺失或低级失误导致的P0和P1故障。反倒是跟我们有产品技术合作的第三方厂商,有时会出问题,我们了解下来,基本都是白天变更导致的,要么是没有放到凌晨实施,要么就是白天临时变更,没有准备充分。 + +所以,制定明确的高压线规则,提升意识,碰一次就要疼一次。这个时候的惩罚是为了提升责任人的敬畏意识和主观意识,人为失误才会减少,处罚也才会有效。 + +当然,更好的结果是,类似的故障越来少,处罚的执行也基本没有了。 + +鼓励做事,而不是处罚错误 + +前面我们分享过这句话: + + +理解一个系统应该如何工作并不能使人成为专家,只能靠调查系统为何不能正常工作才行。(From SRE ,by Brian Redman) + + +我想很多朋友跟我一样,都会产生共鸣。仔细考虑一下,我们每个人的技术能力提升,甚至是质的提升,基本都是伴随着大大小小故障的发生、处理、复盘和改进,这样一个过程提升起来的。 + +虽然我们不希望有故障发生,但是真的没有了故障,我们也就没有了真刀真枪实战成长的机会。我们对待故障一定要客观和辩证地理解,特别是对于管理者来说,对于故障,一定要有容忍度,一定要有耐心。 + +发生故障一方面暴露出我们整体技术架构的不足之处,另一方面,也给我们提供了未来改进的方向。同时,也是最重要的,我们的团队和人员,在这样一次次痛苦的经历后,各方面的能力都得到了锻炼,团队和个人素养也一定会有大幅度提升。所以,对故障有容忍度,有耐心,我们的团队就会变得越来越强,对于故障的应对也会变得更加游刃有余。 + +反观另一种管理方式,一出故障就劈头盖脸地把团队和责任人骂一通,并且还要严厉处罚的方式,最后的效果就是严重打击士气,适得其反。 + +所以,作为管理者,当一个故障发生之后,除故障本身外,还要关注更全面的内容,比如关注人、事情背景和前因后果。 + +列举一下我之前经常遇到的两种情况。 + +1.员工积极主动地承担了一些极具挑战性的工作,需要尝试某个新技术或解决方案,而团队、业界和社区可能都没有可供直接借鉴的经验,结果在落地的过程中踩到了一些坑,导致出现了问题。 + +这种情况在成熟的技术和产品中也极容易出现,比如开源产品,有时候不翻源码都不知道某个地方埋着深坑。即使是商业产品,像Oracle在他的官方bug库里列着一堆已知bug,就是明确告知用户使用时要非常小心,真的碰到bug了,官方一般也是建议升级版本。根据我的经验,对于这样一个庞大的bug库,不出问题一般没人会去把整个bug list都看一遍的。 + +2.业务高速发展时期,业务量成指数级增长时,团队人员技能和经验水平整体上还没法很好地应对,这个时候可能任何一个小变动都是最后一根稻草。这种时候就需要群策群力,而不是简单处罚了事。 + +这两种情况都需要全面了解信息之后,再做判断。甚至要优先传递信任,而不是不管三七二十一就直接批评和处罚。何况,如果不出问题,可能很多主管压根都没有关注过员工在做的事情,过程中是否有困难,是否需要支持等等,这本身就是管理者的失责。 + +在当前这种新业务和新形态不断涌现,又要求快速迭代的背景下,软件开发这种技术工作很大程度上还是要依赖员工的创新和创造。所以在很多情况下,管理者一定要对故障有一定的容忍度,因为员工努力做事的积极性一旦被打击,变得畏首畏尾起来,也就谈不上什么技术进步和突破了,而且想要再恢复起来也会非常困难,最终很大概率上会导致优秀人才流失,为别人做了嫁衣。 + +所以,团队内部一定要营造出鼓励做事向前冲的氛围,而不是制造担心犯错误被处罚的恐慌氛围。 + +处罚的“负”作用远超我们的想象 + +前面讲到,定责不是处罚,是就事论事。员工哪些地方做得不到位,是能力不足还是经验欠缺,这些东西主管可以基于事实,很正式、严肃地表达出来。通常情况下,员工也大多是可以接受的。同时帮助员工进一步分析应该怎么提升,或者聆听员工有什么求助或困难。这种情况下,员工的感受是,主管尊重我,在帮助我。 + +但是,话题和目的一旦转到处罚相关的事情,员工一般会有两种类型的反应:一种是消沉低落(反正都是我的错,你说咋样就咋样);另外一种是极力地反抗和质疑(凭什么罚我不罚别人,又不是我一个人的问题等等)。 + +这时,员工的注意力也会从怎么改进,转变到为什么要处罚我的角度上来。在这种消极和抵抗情绪中再去沟通什么改进措施,就没有任何效果了。作为管理者,也就非常容易陷入到与被沟通者的反复解释中,他质疑一句,你就解释一句,但是他压根就没听进去。 + +从我们的经验来看,如果定责跟绩效强挂钩,团队就陷入这种恐慌、质疑、挑战以致最终相互不信任的局面。员工害怕、甚至拒绝承担责任,宁可少做不做,也不愿多做多错,团队沟通成本上升,运作效率自然下降。特别是一个故障如果是涉及多方的,扯皮推诿就开始了,都想着把责任撇干净,甚至当众相互指责,这个负面效应杀伤力极大。 + +后来我们就取消挂钩,对于出现的故障有专门的系统记录,然后把这件事情放到员工一个季度,半年,甚至一年表现中进行整体判断。如果员工整体的表现都是不错的,甚至是突出的,说明员工已经改正或者那件事情确实是偶尔的失误导致,这种情况下员工仍然会有好的绩效。但如果是频繁出问题,这种情况就基于事实反馈,也会更加容易沟通。 + +我的团队中,就出现过类似的情况。有员工导致了线上严重故障,当个季度绩效较差,但是因为全年表现突出,年终仍然是优秀;也有员工,因为连着两个季度触碰高压线,全年又无明显突出的表现,年终绩效也就不理想。 + +总结 + +我们做个小总结,对于故障的态度,我们还是得要辩证地看。对于是否处罚,也要具体问题具体分析。完全不处罚,或者一刀切,一律处罚都是不可取的。作为管理者,还是要将规则和标准定义清楚,在执行时才能够做到公平公正。 + +另外,管理者除了关注故障本身之外,还要考虑得更加全面一些,要关注到人的感受,关注事情的前因后果,只有这样,在管理执行过程中才会让员工感受到尊重和信任。 + +最后,你在故障定责和处罚方面有什么经历和想法,欢迎留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/31唇亡齿寒,运维与安全.md b/专栏/赵成的运维体系管理课/31唇亡齿寒,运维与安全.md new file mode 100644 index 0000000..3c43196 --- /dev/null +++ b/专栏/赵成的运维体系管理课/31唇亡齿寒,运维与安全.md @@ -0,0 +1,73 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 31 唇亡齿寒,运维与安全 + 故障管理模块告一段落了,今天我们来分享运维安全的内容。在日常工作中,我们运维团队和安全团队的配合确实是非常紧密的,有非常多的交集,我觉得可以做个整体的分享,算是抛砖引玉,以激发更多的讨论和思考。 + +运维和安全的关系 + +运维和安全,双方有一个共同的特点,就是时常要面对非常棘手,甚至是影响公司口碑声誉的问题。对于安全来说,这一点尤甚,想一想用户信息泄露会造成什么样的后果就不难理解了。 + +所以,运维和安全的合作,最初都是在这种场景下触发的,也就让两个团队显得更像是难兄难弟。 + +另外一层关系,就像我们今天分享的题目,运维和安全的关系,就是唇亡齿寒的关系。因为安全是整个业务和技术体系的一道防线,当这道防线被突破,最直接的影响和损失就体现在主机、系统和数据上,而这些又正好是运维的范畴。说得直白一点,就是一旦发生了安全事故,造成的影响,以及后续的修复,这些工作都将由运维来承担。 + +所以,在双方工作的协作上,我一直认为运维不能只是被动响应,而应该主动与安全合作,共建安全体系,与运维体系融合,把防线建设好,从源头控制。 + +同时,安全问题和需求一般都会放置在较高优先级上来响应,并通过固定的问题响应机制实行联动,从而实现最高效的配合响应。 + +蘑菇街安全体系简介 + +这里首先介绍一下我们的安全体系,限于篇幅,我只对部分关键系统和平台做一个简要概述,同时会介绍一下每个系统与运维系统之间的关系。 + +1.入网管控 + +这是VPN接入的管控,并与员工的统一登录鉴权结合,做到一键登录。因为VPN接入后,就等于进入了线上的网络环境中,可以访问到很多敏感系统和数据,这里的入网管控就相当于整个环境的第一道防线。 + +2.堡垒机 + +当接入VPN之后,对于技术同学就会有进一步的需求,比如访问线下和线上环境,登录主机和网络设备做维护工作,这时我们就需要有另外一道关卡,就是硬件或虚拟设备的登录管控,这个就由堡垒机来实现。这里堡垒机维护的主机列表、主机用户名、权限配置等信息,就需要与运维系统中的CMDB以及运维标准化的内容保持统一。 + +因为堡垒机基本属于每个公司安全建设的第一个产品,所以属于强需求,业界也随之出现了很多优秀的开源及商业产品,你可以自行了解一下。同时,关于这块内容,我们的安全工程师齐剑涛在2017年CUNTCon全球运维技术大会上做过分享,如果有兴趣可以进一步了解。 + +固守服务器的第一道防线——美联集团堡垒机的前世今生 + +3.主机安全管控 + +在每台主机上运行一个安全Agent,实时地对可疑进程、可疑端口、可疑账号、可疑日志以及各类操作进行监控,一旦发现异常就会及时告警,确保能够第一时间发现异常,这种就可以一定程度上对黑客入侵导致的破坏进行控制。 + +4.黑盒扫描 + +这个系统主要针对主机上对外开放的端口和使用的服务进行扫描。比如我们之前遇到的Redis高危端口漏洞,OpenSSL心脏滴血漏洞等,同时从接入层会过滤出高频的url,通过注入或修改消息来模拟恶意攻击,量虽不会大,但是如果存在异常或高危漏洞就可以及时发现,同时这个扫描是不间断的。 + +5.白盒扫描(代码审计) + +这个系统我们在前面的持续交付流水线中介绍过,会针对代码中明显的漏洞进行审计,比如XSS漏洞,SQL注入等问题,如果在代码中存在类似问题是不允许发布上线的。 + +这个项目已经开源,地址:https://github.com/WhaleShark-Team/cobra + +这样,黑盒和白盒扫描搭配起来,就可以对内外部的服务和系统进程进行全方位的保障了。 + +6.WAF,Web Application Firewall + +WAF用来对外部的Web服务进行保护。我们知道,对于业务系统来说,通常会有很多不正规的渠道来网站爬取各种信息,更有甚者会通过模拟用户行为来注册大量虚假账号,以此来领取各种优惠,或者提交大量虚假订单、评论等等,同时还会对服务器负载造成很大压力。对于这种恶意行为,就需要由WAF来保护,通过一定的业务规则配置和识别来阻止恶意访问。 + +7.应急响应中心SRC + +在安全界,有这样一个不成文的说法,叫作三分靠技术,七分靠人脉,也就是安全的信息情报有时比单纯的技术攻防要重要得多。对于企业来说,除了要能够招聘到一些安全圈内的专业人士,另一方面,也要有对外公开的应急响应中心SRC,以此来聚集一些比较友好和善意的白帽子,通过他们主动发现一些网站和系统的漏洞,并能够通过SRC提交给公司安全部门。同时,作为企业也会有一些不同形式的奖励,比如奖金和纪念品这样的物质奖励,或者漏洞提交排名这样名誉上的认可,以此来形成更长久和深入的合作。 + +同时,SRC也能弥补一些我们主动做的,但是防护措施不到位的情况,最大程度地借助社区和圈子的力量,共同保障企业网站、信息和数据的安全。 + +上述我们提到的很多检测和限制规则,都会通过规则平台沉淀下来,这个就类似于我们的之前讲到的配置管理。 + +最后,以一张图作为总结,来整体呈现我们的安全体系。也欢迎你给我留言继续讨论。 + + + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/32为什么蘑菇街会选择上云?是被动选择还是主动出击?.md b/专栏/赵成的运维体系管理课/32为什么蘑菇街会选择上云?是被动选择还是主动出击?.md new file mode 100644 index 0000000..428e72f --- /dev/null +++ b/专栏/赵成的运维体系管理课/32为什么蘑菇街会选择上云?是被动选择还是主动出击?.md @@ -0,0 +1,129 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 32 为什么蘑菇街会选择上云?是被动选择还是主动出击? + 2018年1月22日凌晨,我们美丽联合集团旗下的蘑菇街和美丽说的业务,整体搬迁到腾讯云,完成了从托管IDC模式,到腾讯云上混合云模式的转变。 + +云计算发展到今天,无论是在技术、服务层面,还是在商业层面都已经相对比较成熟。当前绝大多数初创公司在基础设施上的策略一定是公有云,已经极少再有自建或托管IDC的情况,所以不会存在是否上云这样的纠结。 + +但是对于蘑菇街这样体量的公司,搬迁上云,就必须要考虑得更全面:考虑基础设施的变化,业务的平稳过度,运维模式的转变,成本管控的调整,以及众多的细节问题。 + +最近,有很多同行对我们为什么做这个选择比较感兴趣。因为尽管混合云模式是当下的大趋势,但真正面临抉择时,又总会被各种具体的细节问题所困扰,犹豫不决。 + +今天,我从蘑菇街的视角,结合真实情况,聊一聊我们为什么会做出上云这个选择。 + +我们所面临的问题 + +1.成本闲置问题 + +对于电商,大促已经常态化,除了“双11”“双12”以及“6·18”这样的例行大促,每个电商还会有自己的营销活动,比如我们就会有“3·21”春季促销,以及每个月不同的主题促销。这一点对于其它电商也是如此。 + +大促,从技术层面就意味着要在短时间内应对远远超过日常的峰值流量,可能是平时的十几倍,甚至是上百倍。为了支撑这么大的流量,就需要业务系统有足够的容量支持。 + +虽然我们会从技术和架构层面来提升容量,但是,无论如何优化,充足的硬件资源扩容是前提条件。 + +之前,我们在应对“双11”这样的大促时,只能采购更多的设备。与此同时,我们还要在机柜成本以及资源上下架等纯人工方面进行投入,这往往要花费几千万元的成本。 + +但是,每次大促峰值一过,这些设备基本就处于极低的负载状态。这批资源要经过将近一年时间,随着业务量快速增长才能逐步消化掉,然后再进入到下一轮大促的采购周期中。 + +所以,这部分成本投入的收益是非常低的,基本处于闲置状态。 + +2.基础设施维护问题 + +选择租用或托管IDC模式,随着业务量增长也会遇到一系列的问题。在我以往的实践操作中,我也遇到了以下几个问题,相信你也有过相似的困扰。 + +IDC机房的选址。在中国互联网八大节点所在城市的IDC资源无疑是最优的,但是这些地方的优质资源却也是最紧张的。通常会被国内各大互联网公司或云计算公司提前占据,所以很难找到相对独立且成规模的机柜区域,而零散的机柜分布对管理和维护工作来说十分不便。 + +退而求其次,就只能选择二级或三级节点,但是这样一来在网络质量上就降了一个或多个等级。同时,因为没有BGP线路,或者线路质量不高,就需要多线接入,这对业务体验以及管理维护都会带来很大影响。 + +IDC机房的扩展问题。一个机房内的机柜消耗完,想扩展就只能另找机房,但是属于同一运营商,或同一ISP服务商的同城机房资源是否充足,又是一个未知数。 + +不同机房间是否互联互通,以及是否增加跨地域的时延,对业务访问体验的影响很大。所以扩展性不足,会大大影响业务体验,甚至影响业务发展。 + +如果是通过第三方ISP接入的,特别是存在多个ISP服务商的时候,在互联互通时,服务商之间的沟通协调非常耗费精力,且不同机房以及多ISP之间的专线成本也会增加。当基础设施达到一定体量,这个问题会非常突出。 + +如果你也有过这方面的经历,相信你一定深有体会。 + +资源利用率问题。即使我们做了虚拟化,按照业界实际情况,CPU资源使用率一般也就在10%-15%左右。所以要想大幅提升使用率,就是要在离线的混部,也就是类似大数据消耗资源特别高的计算类业务上进行资源调配:比如,在凌晨调度到相对空闲的应用服务器上;而在白天,则将资源释放出来给业务应用。 + +但是,想要在离线混部技术上做文章,说起来容易做起来难,因为这在实际工作中是需要非常深厚的技术积累和非常高的技术门槛的。 + +业务层面的调度是一方面,另一方面,底层硬件、网络以及操作系统这些也需要相应的技术支持。这其中具体的复杂情况,你可以通过阿里最近在这方面的一些分享体会一下。 + +单考虑操作系统之上的应用和业务技术是无法满足要求的,所以,这就需要我们在进行技术规划时,在开始底层建设之前就要考虑全面。 + +我们知道,国内外超大型的互联网公司,以及各大云计算公司,在硬件选型上都有自己的定制化要求。其中一个重要原因,就是为了尽量保持几万甚至十几万硬件设备的系统架构一致,从底层硬件开始就为后续的超大规模运维做技术准备。 + +当然,这样的定制化需求,只有在需求量足够大的情况下才会被硬件厂商接受,一般如果只有百台或千台的规模,硬件厂商基本是不会考虑的。 + +所以这就会牵扯出下面这个问题。 + +3.底层技术投入和人才的问题 + +通常在互联网领域,越是底层的技术,技术门槛就越高、越复杂,也越离不开高端人才的投入。比如硬件资源虚拟化,就需要有懂内核、懂网络、懂OpenStack、懂分布式存储如Ceph等等的专业人才。 + +但是真正精通的人却不多,加上要搞定整套解决方案,还需要一个完整的团队,这就难上加难,在团队组建上面临更大的困难。 + +人才紧缺,就意味着人力成本会很高,这个就是技术投入的隐性成本。而且因为技术门槛高,一旦发生人员流动,那么,对于原有技术平台来说,无人能把控的风险就会更高。这一点往往会是最大的隐性管理成本所在。 + +当然,在人才招揽上,我们可以加大人力成本投入,招聘最优秀的人才。但是作为像蘑菇街和美丽说这样以更加聚焦于业务,以业务发展为生命线的公司,我们更期望能够在业务上取得创新和发展,而不是在技术上取得多么非凡的成就(这一点与公司的发展诉求是不一致的)。所以这就从根本上决定了,我们不会无限度地投入,或投入非常大的成本在这些基础技术的研究上。 + +对于以技术创业为主的公司,其考量的出发点就完全不同了,这里我们不展开讨论。 + +进一步讲,论体量和规模,我们自有的底层技术无论如何是无法与专业的云计算公司相比的,这就带来另一个问题:如何为这些优秀人才提供成长和发展?因为既然在体量和规模上比不过,那我们能够提供的个人成长空间和机会,一定也比不过专业云计算公司。这种情况下,大部分人才的去向选择就显而易见了。 + +对于大数据,分布式中间件等岗位,也会存在类似的情况,因为它们大多需要体量和规模才能体现技术挑战性和成长空间。 + +4.小结 + +到这里我们做个小结,随着基础设施体量越来越大,我们在基础设施和平台服务层面,将会投入越来越大的财力、人力和最宝贵的精力。 + +但是这项投入的收益和成效却不明显,且在这个层面的专业性上,我们与云计算平台之间的差距越来越大,脱节也越来越严重。 + +我们的决策过程就是,以未来3-5年,甚至更长远的视角考量,我们认为上述这些问题一定会成为我们将来业务发展的障碍,因此上云就成了我们的不二选择,并成为公司的战略决策之一。 + +纵观技术发展趋势 + +1.从软件架构发展的趋势上看,从最早期的物理机,到目前主流的虚拟机,再到当前非常火热的Docker,以及可能在未来会成为又一主流的Serverless,我们对于资源层面的依赖越来越少,而这个趋势恰恰是云计算不断发展带来的改变。 + + + +同时,像Serverless这样的技术理念,就是在公有云平台上,为了提升资源利用率,而衍生出来的。而且从目前看,Serverless也只有在公有云平台上才有意义。在私有云,或者是自建或托管IDC中,因为资源规模问题,没有看到太多的实践价值。 + +2017年AWS re:Invent 2017峰会上,AWS共发布了在数据库、容器、人工智能、物联网以及网络等等方面的几十项新的产品技术服务。可以说,如果想要技术为业务带来更多的可能性,拥抱云计算是最好的选择。 + +2.人工智能对云计算能力的释放。我们当前的人工智能主要是对机器学习算法的广泛应用,这里的两个前提条件,一个是要有足够大的数据量,另一个就是要有足够充足的计算资源。 + +我们先看一个2017年的新闻: + + +2017年5月份,谷歌宣布麻省理工学院的数学教授安德鲁·V·萨瑟兰使用抢占式虚拟机实例,在220000个GCE核心上运行了庞大的数学工作负载。据称这是迄今为止在公共云上运行的最庞大的高性能计算集群。 + + +计算任务阶段性的运行对资源需求是非常庞大的,一般企业很难提前预留足够的资源来做这个事情,这时云的资源优势和弹性能力就凸显出来了。 + +可以说,未来人工智能的发展和应用,必然会依托于云计算。 + +没有银弹 + +软件工程中,我们一直在讲,没有银弹。前面我们介绍了我们遇到的一些具体问题,以及云计算的优势所在,但是没有银弹这条规律,仍然也适用于云计算行业。 + +那么,是不是有了云计算,有了公有云,上述我们所说的问题就都不存在了呢? + +以公有云为例,它也一样会遇到IDC建设、扩展性以及基础技术投入等等问题,可能也会给我们带来一定的影响。但是对于公有云来说,因为自身财力和人力的优势,面对这样的问题会更容易解决一些,但对于我们可能就是难以逾越的难题了。 + +同时,公有云虽然解决了很多问题,但是,就目前这个阶段来讲,如果想要获得较高的客户满意度,仍然有很长的路要走,比如不同形态业务的差异化支持和服务问题。 + +我想,这一点并不是云计算厂商不想做好,因为无论在技术、产品以及服务上,它们并不是一蹴而就的,而是各方面都需要一个逐步积累、磨合和摸索的过程,这就需要云计算厂商与业务客户共同努力,朝着同一个目标前进,需要彼此多一些耐心。 + +我们也期望在国内见到AWS和Netflix这样的最佳组合,相互成就,共同成功。 + +欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/33为什么混合云是未来云计算的主流形态?.md b/专栏/赵成的运维体系管理课/33为什么混合云是未来云计算的主流形态?.md new file mode 100644 index 0000000..e644c90 --- /dev/null +++ b/专栏/赵成的运维体系管理课/33为什么混合云是未来云计算的主流形态?.md @@ -0,0 +1,129 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 33 为什么混合云是未来云计算的主流形态? + 2018年1月22日凌晨,我们美丽联合集团旗下的蘑菇街和美丽说的业务,整体搬迁到腾讯云,完成了从托管IDC模式,到腾讯云上混合云模式的转变。 + +云计算发展到今天,无论是在技术、服务层面,还是在商业层面都已经相对比较成熟。当前绝大多数初创公司在基础设施上的策略一定是公有云,已经极少再有自建或托管IDC的情况,所以不会存在是否上云这样的纠结。 + +但是对于蘑菇街这样体量的公司,搬迁上云,就必须要考虑得更全面:考虑基础设施的变化,业务的平稳过度,运维模式的转变,成本管控的调整,以及众多的细节问题。 + +最近,有很多同行对我们为什么做这个选择比较感兴趣。因为尽管混合云模式是当下的大趋势,但真正面临抉择时,又总会被各种具体的细节问题所困扰,犹豫不决。 + +今天,我从蘑菇街的视角,结合真实情况,聊一聊我们为什么会做出上云这个选择。 + +我们所面临的问题 + +1.成本闲置问题 + +对于电商,大促已经常态化,除了“双11”“双12”以及“6·18”这样的例行大促,每个电商还会有自己的营销活动,比如我们就会有“3·21”春季促销,以及每个月不同的主题促销。这一点对于其它电商也是如此。 + +大促,从技术层面就意味着要在短时间内应对远远超过日常的峰值流量,可能是平时的十几倍,甚至是上百倍。为了支撑这么大的流量,就需要业务系统有足够的容量支持。 + +虽然我们会从技术和架构层面来提升容量,但是,无论如何优化,充足的硬件资源扩容是前提条件。 + +之前,我们在应对“双11”这样的大促时,只能采购更多的设备。与此同时,我们还要在机柜成本以及资源上下架等纯人工方面进行投入,这往往要花费几千万元的成本。 + +但是,每次大促峰值一过,这些设备基本就处于极低的负载状态。这批资源要经过将近一年时间,随着业务量快速增长才能逐步消化掉,然后再进入到下一轮大促的采购周期中。 + +所以,这部分成本投入的收益是非常低的,基本处于闲置状态。 + +2.基础设施维护问题 + +选择租用或托管IDC模式,随着业务量增长也会遇到一系列的问题。在我以往的实践操作中,我也遇到了以下几个问题,相信你也有过相似的困扰。 + +IDC机房的选址。在中国互联网八大节点所在城市的IDC资源无疑是最优的,但是这些地方的优质资源却也是最紧张的。通常会被国内各大互联网公司或云计算公司提前占据,所以很难找到相对独立且成规模的机柜区域,而零散的机柜分布对管理和维护工作来说十分不便。 + +退而求其次,就只能选择二级或三级节点,但是这样一来在网络质量上就降了一个或多个等级。同时,因为没有BGP线路,或者线路质量不高,就需要多线接入,这对业务体验以及管理维护都会带来很大影响。 + +IDC机房的扩展问题。一个机房内的机柜消耗完,想扩展就只能另找机房,但是属于同一运营商,或同一ISP服务商的同城机房资源是否充足,又是一个未知数。 + +不同机房间是否互联互通,以及是否增加跨地域的时延,对业务访问体验的影响很大。所以扩展性不足,会大大影响业务体验,甚至影响业务发展。 + +如果是通过第三方ISP接入的,特别是存在多个ISP服务商的时候,在互联互通时,服务商之间的沟通协调非常耗费精力,且不同机房以及多ISP之间的专线成本也会增加。当基础设施达到一定体量,这个问题会非常突出。 + +如果你也有过这方面的经历,相信你一定深有体会。 + +资源利用率问题。即使我们做了虚拟化,按照业界实际情况,CPU资源使用率一般也就在10%-15%左右。所以要想大幅提升使用率,就是要在离线的混部,也就是类似大数据消耗资源特别高的计算类业务上进行资源调配:比如,在凌晨调度到相对空闲的应用服务器上;而在白天,则将资源释放出来给业务应用。 + +但是,想要在离线混部技术上做文章,说起来容易做起来难,因为这在实际工作中是需要非常深厚的技术积累和非常高的技术门槛的。 + +业务层面的调度是一方面,另一方面,底层硬件、网络以及操作系统这些也需要相应的技术支持。这其中具体的复杂情况,你可以通过阿里最近在这方面的一些分享体会一下。 + +单考虑操作系统之上的应用和业务技术是无法满足要求的,所以,这就需要我们在进行技术规划时,在开始底层建设之前就要考虑全面。 + +我们知道,国内外超大型的互联网公司,以及各大云计算公司,在硬件选型上都有自己的定制化要求。其中一个重要原因,就是为了尽量保持几万甚至十几万硬件设备的系统架构一致,从底层硬件开始就为后续的超大规模运维做技术准备。 + +当然,这样的定制化需求,只有在需求量足够大的情况下才会被硬件厂商接受,一般如果只有百台或千台的规模,硬件厂商基本是不会考虑的。 + +所以这就会牵扯出下面这个问题。 + +3.底层技术投入和人才的问题 + +通常在互联网领域,越是底层的技术,技术门槛就越高、越复杂,也越离不开高端人才的投入。比如硬件资源虚拟化,就需要有懂内核、懂网络、懂OpenStack、懂分布式存储如Ceph等等的专业人才。 + +但是真正精通的人却不多,加上要搞定整套解决方案,还需要一个完整的团队,这就难上加难,在团队组建上面临更大的困难。 + +人才紧缺,就意味着人力成本会很高,这个就是技术投入的隐性成本。而且因为技术门槛高,一旦发生人员流动,那么,对于原有技术平台来说,无人能把控的风险就会更高。这一点往往会是最大的隐性管理成本所在。 + +当然,在人才招揽上,我们可以加大人力成本投入,招聘最优秀的人才。但是作为像蘑菇街和美丽说这样以更加聚焦于业务,以业务发展为生命线的公司,我们更期望能够在业务上取得创新和发展,而不是在技术上取得多么非凡的成就(这一点与公司的发展诉求是不一致的)。所以这就从根本上决定了,我们不会无限度地投入,或投入非常大的成本在这些基础技术的研究上。 + +对于以技术创业为主的公司,其考量的出发点就完全不同了,这里我们不展开讨论。 + +进一步讲,论体量和规模,我们自有的底层技术无论如何是无法与专业的云计算公司相比的,这就带来另一个问题:如何为这些优秀人才提供成长和发展?因为既然在体量和规模上比不过,那我们能够提供的个人成长空间和机会,一定也比不过专业云计算公司。这种情况下,大部分人才的去向选择就显而易见了。 + +对于大数据,分布式中间件等岗位,也会存在类似的情况,因为它们大多需要体量和规模才能体现技术挑战性和成长空间。 + +4.小结 + +到这里我们做个小结,随着基础设施体量越来越大,我们在基础设施和平台服务层面,将会投入越来越大的财力、人力和最宝贵的精力。 + +但是这项投入的收益和成效却不明显,且在这个层面的专业性上,我们与云计算平台之间的差距越来越大,脱节也越来越严重。 + +我们的决策过程就是,以未来3-5年,甚至更长远的视角考量,我们认为上述这些问题一定会成为我们将来业务发展的障碍,因此上云就成了我们的不二选择,并成为公司的战略决策之一。 + +纵观技术发展趋势 + +1.从软件架构发展的趋势上看,从最早期的物理机,到目前主流的虚拟机,再到当前非常火热的Docker,以及可能在未来会成为又一主流的Serverless,我们对于资源层面的依赖越来越少,而这个趋势恰恰是云计算不断发展带来的改变。 + + + +同时,像Serverless这样的技术理念,就是在公有云平台上,为了提升资源利用率,而衍生出来的。而且从目前看,Serverless也只有在公有云平台上才有意义。在私有云,或者是自建或托管IDC中,因为资源规模问题,没有看到太多的实践价值。 + +2017年AWS re:Invent 2017峰会上,AWS共发布了在数据库、容器、人工智能、物联网以及网络等等方面的几十项新的产品技术服务。可以说,如果想要技术为业务带来更多的可能性,拥抱云计算是最好的选择。 + +2.人工智能对云计算能力的释放。我们当前的人工智能主要是对机器学习算法的广泛应用,这里的两个前提条件,一个是要有足够大的数据量,另一个就是要有足够充足的计算资源。 + +我们先看一个2017年的新闻: + + +2017年5月份,谷歌宣布麻省理工学院的数学教授安德鲁·V·萨瑟兰使用抢占式虚拟机实例,在220000个GCE核心上运行了庞大的数学工作负载。据称这是迄今为止在公共云上运行的最庞大的高性能计算集群。 + + +计算任务阶段性的运行对资源需求是非常庞大的,一般企业很难提前预留足够的资源来做这个事情,这时云的资源优势和弹性能力就凸显出来了。 + +可以说,未来人工智能的发展和应用,必然会依托于云计算。 + +没有银弹 + +软件工程中,我们一直在讲,没有银弹。前面我们介绍了我们遇到的一些具体问题,以及云计算的优势所在,但是没有银弹这条规律,仍然也适用于云计算行业。 + +那么,是不是有了云计算,有了公有云,上述我们所说的问题就都不存在了呢? + +以公有云为例,它也一样会遇到IDC建设、扩展性以及基础技术投入等等问题,可能也会给我们带来一定的影响。但是对于公有云来说,因为自身财力和人力的优势,面对这样的问题会更容易解决一些,但对于我们可能就是难以逾越的难题了。 + +同时,公有云虽然解决了很多问题,但是,就目前这个阶段来讲,如果想要获得较高的客户满意度,仍然有很长的路要走,比如不同形态业务的差异化支持和服务问题。 + +我想,这一点并不是云计算厂商不想做好,因为无论在技术、产品以及服务上,它们并不是一蹴而就的,而是各方面都需要一个逐步积累、磨合和摸索的过程,这就需要云计算厂商与业务客户共同努力,朝着同一个目标前进,需要彼此多一些耐心。 + +我们也期望在国内见到AWS和Netflix这样的最佳组合,相互成就,共同成功。 + +欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/35以绝对优势立足:从CDN和云存储来聊聊云生态的崛起.md b/专栏/赵成的运维体系管理课/35以绝对优势立足:从CDN和云存储来聊聊云生态的崛起.md new file mode 100644 index 0000000..123930d --- /dev/null +++ b/专栏/赵成的运维体系管理课/35以绝对优势立足:从CDN和云存储来聊聊云生态的崛起.md @@ -0,0 +1,99 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 35 以绝对优势立足:从CDN和云存储来聊聊云生态的崛起 + 前面几期文章我们介绍了混合云模式,以及面向应用层的云架构解决方案的Spring Cloud。接下来,我们就以蘑菇街的两个具体案例,来分享一下基于混合云模式的具体实践。 + +今天,我们先一起看一下我们最为熟悉的CDN和云存储建设。 + +CDN和云存储 + +我们之前提到,CDN应该算是最早期、最典型的公有云服务,如果我们在业务上用到了CDN服务,其实就已经算是在实践混合云模式了。 + +蘑菇街作为ToC的电商业务,自然会有大量的图片访问需求,其中尤以商品图片为最。为了保证用户体验,我们的产品在2011年上线之初,就应用了CDN技术,当时我们合作的都是专业的CDN厂商。 + +当然,大量的图片访问需求会带来大量的图片存储需求,且随着业务高速发展,这个需求量必然会极速增长。 + +图片的访问需求量往往会以几百万、几千万、几亿到几十亿、上百亿的增长态势呈现。而它所占用的存储空间也会从G扩展到T,到几十T、几百T,再到后来的P级别。 + +所以,起初我们在业务量不大的时候,还可以把图片存在自己的硬件设备上。再往后可以利用HDFS这样的对象存储技术来实现,CDN回源的请求全部回到我们自己的机房里。 + +但是再往后,我们在这方面的专业性就不够了。这也是我在[《为什么蘑菇街会选择上云?是被动选择还是主动出击?》]一文中介绍到的:随着体量的增加,CDN对于专业技术深度的要求就越来越高,技术层面的投入也会越来越大,这个就与我们的业务发展诉求不一致了。 + +因为对于蘑菇街来说,我们还是期望能够把更多的人力和物力应用到业务技术层面,而不是一味耗费在专业技术研究上。 + +在2013年左右,业界开始出现专业的云存储厂商。在专业技术层面要比我们精深很多,而且他们聚拢了一大批业界专业人才,积累了各类海量存储场景的实践经验,所以他们在产品研发和服务支持上也是能够持续深入的。 + +基于此,我们后来就选择了将海量的图片放到专业性更强的云存储之上。 + +到这个阶段,我们的图片访问和图片存储就近乎完全依赖外部第三方的服务,同时也形成了CDN回源访问云存储这样的云应用模式。 + +再往后发展,随着国内两大公有云巨头腾讯云和阿里云相继杀入CDN市场,这对传统的CDN厂商和新兴的云存储业务造成了很大的冲击。但究其主要原因,我认为还是由于云生态的规模优势发挥了作用。 + +云生态的优势 + +上面讲到,我们使用的CDN是专业的CDN厂商业务,但在当时它们并不提供云存储业务。 + +同时,专业的云存储厂商固然在存储技术上更擅长,加上它们大多属于创业公司,也没有太多的财力和人力再杀入CDN市场。 + +所以,这两块紧耦合的业务实际是由两类不同的公司在独立发展。但是,在阿里云和腾讯云这样的公有云巨头进入市场后,就极大地改变了这样一个格局。 + +因为阿里和腾讯各自都有遍布全球的超大规模的自有业务,在CDN、存储技术以及资源上也有很深厚的积累。当公有云蓬勃发展起来之后,这样的技术和资源积累就可以从内部通过云平台输出给公有云的用户。 + +那么,阿里云和腾讯云这种公有云模式有哪些优势呢?我们将CDN模式与其对比来看。 + +1.技术层面。CDN模式下,图片上传云存储以及CDN回源云存储,基本都是走公网网络,在国内复杂的网络条件下,传输质量就很难保障。 + +我记得2015年我来到蘑菇街接手运维的时候,就经常会遇到网站图片展示不出来或者打开特别慢的情况。这就需要找不同的CDN厂商定位一些个例问题,非常耗时且麻烦,令人头痛。 + +在这种形态下,因为是不同厂商间的协作,所以问题只能在小范围内优化,无法从根本上得到改善。但是,在以阿里云和腾讯云为代表的公有云模式下,这个问题就会迎刃而解。 + +因为阿里和腾讯这两大巨头各自都有全球规模的业务体量,为了保证自身业务的访问体验,它们一定会在CDN和存储技术的优化、整合上下足工夫,因此更具备自我改进的动力。 + +尤其是上面我们提到的上传和回源过程,在公有云模式下基本都有专线质量保障。即使没有专线,他们也会不断优化中间的线路质量。 + +上云后我们最明显的感受,就是上传和回源图片的成功率和速度都有非常大的改善,近一年来,我很少再碰到像图片展示慢这类问题了。 + +2.成本层面。主要包括三块费用: + + +CDN带宽费用。用户访问带来的CDN流量费用,由各CDN厂商收取。对于传统CDN厂商,之前价格相对固定和平稳,但是近两年公有云厂商入局后不断降价,在价格上有非常大的竞争优势,这也整体拉低了CDN价格。 +回源带宽费用。当CDN无法获取到对应图片时,回源到云存储获取,这部分回源带宽费用由云存储厂商收取。但是在云生态模式下,上面提到,CDN和云存储可以整合到某个公有云内部网络体系内,在这个生态中,这部分费用成本就变得非常低了。 +存储空间费用。这一项费用由云存储厂商收取,CDN和公有云这两种模式在这一点上没有太大区别。 + + +还有其他诸如裁图、HTTPS等服务,都分别会有不同的计费方式,但是这块不是主要成本。 + +说到这里,我们从整体的商务角度看一下:如果一位客户在同一个地方购买的服务和资源越多,那么商务运作和议价空间就会越大,得到的优惠力度也会更大。这就跟我们在同一家商店买的东西越多、折扣越多是一个道理。 + +但是,如果这些服务和资源都是不同厂商的,那就需要多方面沟通。整个过程耗时耗力,且能够得到的优惠空间也非常有限。 + +所以,整体来看,云生态在CDN和云存储这个层面,实实在在地帮我们降低了成本。 + +3.生态优势,强者越强。云生态的天然优势在于,云平台上聚集了海量的客户资源,只要进入到这个生态中,一般情况下客户都会首选生态内的产品,而不会再跳出去选择其它独立的产品服务。 + +甚至即使之前用到了第三方的服务,客户也会逐渐转移回云平台这个生态体系中来。 + +所以,我们现在可以看到这样一种趋势:很多独立的技术产品,正在向云生态靠拢,选择跟公有云合作,争取让产品进入到某个云生态中,并提供相应的云上解决方案和技术支持。 + +同时,各大巨头也会寻找在某些特定领域,有相当深度的技术产品进行投资,让这些好的产品和解决方案进入到自己的云生态中,以便进一步为云上的客户提供更全面、灵活和多样性的支持。 + +总结 + +通过CDN和云存储这个案例,我们可以清晰地看到,随着公有云的深入发展,特别是公有云巨头的飞速进步,公有云已经形成了自有的、独特的生态体系。 + +这不仅仅体现在技术和产品层面,而且可以预见其最终还会形成商业层面的体系闭环。 + +所以,利用云计算的优势,拥抱变化,才能够为我们的业务发展和创新带来更多的可能性。 + +以上分享的这些内容,是我在近两年的工作中真切经历过的案例和感受,希望能在思路拓展上对你有所帮助。 + +如果你在这方面有什么好的经验和想法,欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/36量体裁衣方得最优解:聊聊页面静态化架构和二级CDN建设.md b/专栏/赵成的运维体系管理课/36量体裁衣方得最优解:聊聊页面静态化架构和二级CDN建设.md new file mode 100644 index 0000000..d573bf1 --- /dev/null +++ b/专栏/赵成的运维体系管理课/36量体裁衣方得最优解:聊聊页面静态化架构和二级CDN建设.md @@ -0,0 +1,115 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 36 量体裁衣方得最优解:聊聊页面静态化架构和二级CDN建设 + 上期文章中我们介绍了CDN和云存储的实践,以及云生态的崛起之路,今天,我们继续聊一聊CDN。 + +我们通常意义上讲的CDN,更多的是针对静态资源类的内容分发网络,最典型的就是电商的各类图片,还有JS和CSS这样的样式文件。通过CDN能够让用户就近访问,提升用户体验。 + +但是这类文件只是以单纯的资源存在,与业务逻辑没有强关联。所以我们在技术上,可以使用业界通用的CDN和云存储解决方案。 + +需要注意的是,本文中我们讲到的实践内容,同样是遵从静态内容,就近访问这个原则的。 + +但是,因为其中包含了大量的业务逻辑,这就要求我们在面对不同的场景时,要有跟业务逻辑相关的定制化的解决方案。 + +下面,我们就一起来看看页面静态化架构和二级CDN建设。 + +静态化架构建设的业务场景 + +我们仍然回到电商的业务场景中来。对于电商,访问量最大的无疑是商品的详情页,绝大多数用户都要通过浏览商品详情,来决定是否下单。所以单就这一类页面,就占到全站30%+的流量。 + +那么,商品详情一般由哪些部分组成呢?我们看下面两个截图: + + + + + +以上两张图就是某个商品详情页的主要组成部分。我们可以看到,商品详情大致包括了商品名称、商品描述、产品参数描述、价格、SKU、库存、评价、优惠活动、优惠规则以及同款推荐等等信息。 + +这里我们仔细观察可以发现,其实对于商品描述类的信息,比如商品名称、商品描述、产品参数描述等等,一般在商品发布之后,就很少再变动,属于静态化的内容。 + +而优惠活动、优惠规则、价格等等则是可以灵活调整的,库存和评价这类信息也是随时变化,处于不断的更新中。 + +说到这里,我们会想到,如果能够把静态化的内容提取出来单独存放,业务请求时直接返回,而不用再通过调用应用层接口的方式,去访问缓存或者查询数据库,那访问效率一定是会大幅提升的。 + +所以,我们在参考和调研了业界的解决方案之后,引入了页面静态化架构。 + +页面静态化架构 + +静态架构中,我们采用的技术方案是ATS,也就是Apache Traffic Server。 + +ATS是一个开源产品,本质上跟Nginx、Squid以及Varnish这样的HTTP反向代理是一样的。但是它能对动静态分离的场景提供很好的支持,所以在最初,我们直接引入了这样的开源解决方案。 + +ATS的架构示意图如下: + + + +关键技术点: + + +动静态分离。将页面上相对固定的静态信息和随时在变化的动态信息区分出来,静态信息直接在ATS集群获取,动态信息则回源到应用层。通过HTTP请求调用获取,最终通过ATS组装后返回给调用方,从而实现了动静态资源的分离。 +动态数据获取。直接采用ATS的ESI标签模式,用来标记那些动态的被请求的数据。 +失效机制。分为主动失效、被动失效和定时失效。对于静态信息来说,我们也允许它变化,但因为静态信息自身的特性,决定了它不会频繁变化。所以,我们会有一个失效中心,即Purge Center。失效消息通过HTTP的Purge方法发送给ATS,而失效中心则会通过订阅消息系统中特定的Topic,或者MySql中特定的binlong变更,执行失效。 + + +以上就是静态化建设的框架性的解决方案,这个方案在电商大促时往往能够发挥更加突出的作用。下面我就简单说明下。 + +静态化架构在大促场景中的应用 + +我们还是以业务场景作为切入点来看。以“双11”为例,参与大促的商家和商品,一般会在11月初完成全部报名。届时所有的商品信息都将确认完毕,且直到“双11活动”结束,基本不会再发生大的变化。 + +它跟平时的不同之处在于,商品在大促期间是相对固定的,所以就可以将商品的静态化信息提前预热到ATS集群中,大大提升静态化的命中率。 + +同时,价格、优惠、库存这些动态信息在日常是会经常变化的,但是在大促阶段是必须固定的。即使有变化,也只能体现在最终的订购阶段,而在用户浏览阶段尽量保持不变。 + +所以,这时可做静态化处理的内容就会更多。换言之,静态化架构对于后端的访问请求就会进一步减少,特别是价格、优惠和库存这样的查询计算类请求。 + +同时,我们静态化页面的范围可以更广,不仅仅是详情页,还可以包括各类大促活动的页面、秒杀页面、会场页面,甚至是首页。 + +因为这些页面都是提前配置好再发布的,所以我们完全可以通过静态化解决方案,来分担更大的流量。 + +以详情页为例。在静态化方案全面铺开推广后,静态化内容在大促阶段的命中率为95%,RT时延从原来完全动态获取的200ms,降低到50ms。这大大提升了用户体验,同时也大幅提升了整体系统容量。 + +静态化方案和应用场景我们就介绍到这里。你可能会问:既然是静态化的内容,那是不是仍然可以借鉴CDN的思路,让用户就近访问呢? + +我们下面就介绍一下页面静态化与公有云相结合的方案:二级CDN建设。 + +二级CDN建设 + +上面我们提到的静态化方案,仅仅是我们自己中心机房的建设方案,也就是说,所有的用户请求还是都要回到中心机房中。 + +静态化方案提升的是后端的访问体验,但是用户到机房的这段距离的体验并没有改善。 + +从静态化的角度,这些内容我们完全可以分散到更多的地域节点上,让它们离用户更近,从而真正解决从用户起点到机房终点的距离问题。 + +所以,我们接下来的方案就是:选择公有云节点,进行静态化与公有云相结合的方案,也就是我们的二级CDN方案。简单示意如下: + + + +引入了这样的二级CDN架构后,下面几个技术点需要我们多加关注。 + + +回源线路,公网回源转变为专线回源。之前我们的中心机房还是托管IDC模式时,动态回源部分都是通过公网回源,同时,静态化配置的推送也是通过公网推送到公有云节点,这对成功率和访问质量上都会有一些影响。但是我们上云之后,做的第一件事情就是将公网访问模式调整成了内网调用模式,也就是动态回源直接改为专线的动态回源。这样大大提升了访问质量,且进一步节省了部分带宽费用。 +弹性伸缩。利用了公有云节点之后,在大促时就可以很方便地进行动态扩缩容,以便真正地按需使用。而且自动化的扩缩容,以及日常的静态化配置推送都需要完善。 +高可用保障。为了保障多节点的高可用,在单个节点故障时,要能够快速切换。当前我们的策略仍然是,当某个节点遭遇故障,直接全部切换回中心节点。这里为了能够达到快速切换的目的,需要通过HttpDNS这样切换IP的方式实现。因为DNS缓存生效周期较长,如果是通过域名切换,则造成的影响周期会比较长。 + + +总结 + +今天分享的页面静态化架构方案和二级CDN方案,是我在实际工作中较早跟公有云方案相结合的实践之一,并且在我们的日常和大促活动中,起到了非常好的效果。 + +同时也可以看到,我们的业务一旦与公有云相结合,云生态的各种优势就会马上体现出来。但是无论选择哪种方案,都要结合具体的业务场景,才能作出最优的方案选择。 + +公有云也好,云计算也好,都不能为我们提供完美的定制解决方案。正所谓具体问题具体分析,找出问题,优化解决路径,量体裁衣,才能得到最适合我们的“定制方案”。 + +正如我之前提到的:只有挖掘出对业务有价值的东西,我们的技术才会有创新,才会有生命力。 + +如果你在这方面有好的实践经验和想法,欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/37云计算时代,我们所说的弹性伸缩,弹的到底是什么?.md b/专栏/赵成的运维体系管理课/37云计算时代,我们所说的弹性伸缩,弹的到底是什么?.md new file mode 100644 index 0000000..e3a0801 --- /dev/null +++ b/专栏/赵成的运维体系管理课/37云计算时代,我们所说的弹性伸缩,弹的到底是什么?.md @@ -0,0 +1,57 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 37 云计算时代,我们所说的弹性伸缩,弹的到底是什么? + 现在,我们经常听到的一些高大上的词汇,比如弹性伸缩、水平扩展和自动化扩缩容等等,你能否说一说,这些技术手段的主体是谁,也就是谁的水平扩展?弹性伸缩的是什么?同时,这些名词之间又有什么关系呢? + +下面我们就从弹性伸缩入手一起来分析讨论。 + +弹性伸缩的主体是谁? + +弹性伸缩,一说到这个词,我们可能很自然地会联想到资源的弹性伸缩,服务器的弹性伸缩,容量的弹性伸缩,应用的弹性伸缩以及业务的弹性伸缩等等。我想这些理解都没有错误,但是可以发现,当弹性伸缩这个动词前面增加了这么多不同的主语之后,我们一下子就不知道到底该做什么了。 + +其实有这样的困惑很正常。我们在讲标准化的时候就提到,做运维和做架构的思路是相通的,我们碰到问题后,一定要找到问题的主体是什么,通过问题找主体,通过主体的特性制定问题的解决方案。 + +对于运维,一定要准确识别出日常运维过程中不同的运维对象,然后再进一步去分析这个对象所对应的运维场景是什么,进而才是针对运维场景的分解和开发。 + +这里可以看到,弹性伸缩其实是一个运维场景,但是我们并没有定义这个场景的主体是谁,所以就会出现上面这些不同的主体。我们来假设以下几种主体。 + + +服务器的弹性伸缩。针对这个场景,假设业务是运行在私有云或公有云上,那我们只要能够通过云平台的API申请和释放资源,申请时初始化我们的操作系统,释放时销毁资源就可以。不过,在私有云环境下,为了能够保障弹性,我们必须自己提前采购、上架、装机、配置然后交付资源,需要大量的准备工作,公有云上就可以省去这些步骤,可以即拿即用。 +应用的弹性伸缩。这个场景下其实是默认包含第一步的,就是我们首先必须要拿到应用运行的服务器资源才可以,这一步做到了,下面就是应用的部署、启动以及服务上线接入流量。 +业务的弹性伸缩。我们可以再进一步思考,通常一个业务可能会包括多个应用,所以为了保障整个业务容量充足,这个时候扩容单个的应用是没有意义的,所以这时要做的就是扩容多个应用,但是这里面就会有一个顺序问题,先扩哪个,后扩哪个,哪些又是可以同时扩容而不会影响业务正常运行的,再进一步,业务承载的服务能力提升了,那网络带宽、缓存、DB等等这些基础设施需不需要也同时扩容呢? + + +到这里,这个问题就已经变得非常复杂了,而且已经不仅仅是弹性伸缩这个词的表面含义所能覆盖的了,因为这个问题确实很复杂,所以这里暂时不做详述。 + +好了,分析到这里,我们就可以看到针对不同的运维对象,弹性伸缩这个概念背后的含义是不一样的,所执行的动作以及制定的方案也是不一样的。通过上面的分析过程,我们在日常思考和工作开展中应该注意以下两点。 + +第一,一定是从实际问题出发,找到问题的主体,然后才是针对问题的解决方案。要反复问自己和团队,我们解决的问题是什么?解决的是谁的问题?切记,一定不要拿着解决方案来找问题,甚至是制造问题。 + +比如弹性伸缩这个概念,它就是解决方案,而不是问题本身,问题应该是:业务服务能力不足时,如何快速扩容?业务服务能力冗余时,如何释放资源,节省成本?按照这个思路来,我们自然就提炼出业务服务能力这个主体,面对的场景是快速的扩缩容,然后针对场景进一步细化和分解。 + +第二,如果问题处于初期,且是发散状态时,主体可能表现出很多个,这时我们一定要找到最本质的那一个,往往这个主体所涉及的运维场景就包括了其它主体的场景。 + +比如上面我们看到的业务的弹性伸缩,就包含了应用和服务器的弹性伸缩场景,它们只不过是子场景而已。从这个角度出发,我们的思路才能打开,方案才会全面。不然如果只是聚焦在服务器、应用、DB、网络等等这些非本质的主体上,提供出来的解决方案肯定也是片面的,而且经常会出现这样的情况,每个人都只从自己的角度出发说问题,始终无法达成一致。问题的根本还是我们没有一个共同讨论的基础,结果就是工作没少做,却始终没有解决问题。 + +弹性伸缩、水平扩展和自动化扩缩容,它们之间的关系是什么? + +这个问题,我想已经不言自明了,找到它们的主体和要解决的问题,我们就会发现,其实它们之间的含义是一样的。 + +总结 + +今天我们以弹性伸缩为例,讨论了如何思考问题和分析问题。我们的讨论和分析归结到一点就是:独立思考和分析的能力很重要,意识也很重要,切忌不可人云亦云随大流,反而迷失了工作的方向。 + +现在业界各种技术上的Buzzword(时髦词)层出不穷,让人目不暇接,但是仔细观察和思考,你会发现它们背后常常隐藏着很多共性的特点,一定要抓住它们背后所要解决的问题和本质,这样也就不会乱花渐欲迷人眼了。 + +而且通过这样的分析,我们会更容易发现工作中还需完善的地方,从而引导我们聚焦到实际问题中来,而不是浮于表面,把一些高大上的词汇挂在嘴边,却不见效果。 + +关于今天我们讨论的主题,你还有哪些想法和心得,欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/38我是如何走上运维岗位的?.md b/专栏/赵成的运维体系管理课/38我是如何走上运维岗位的?.md new file mode 100644 index 0000000..a988d2c --- /dev/null +++ b/专栏/赵成的运维体系管理课/38我是如何走上运维岗位的?.md @@ -0,0 +1,114 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 38 我是如何走上运维岗位的? + 在专栏介绍中,我简单分享了自己为什么会走上运维这个岗位,一是责任心使然,出现问题时总是会主动冲在前面解决,另一个是在这个过程中技能提升得很快,很有成就感。不过当时受篇幅所限,并没有完整说明,所以今天我想再来聊一聊这个话题。 + +聊这个话题还有一个出发点,就是当下业界对运维的认知和定位还是存在很多问题的,有不少贬低运维的言论,所以我想结合自己的经历谈谈对这个事情的看法,期望能够带给你一些启发。 + +我是怎么开始做运维工作的? + +我做运维是在加入华为1年后开始的。在华为内部,我从来没有听说过任何贬低运维的说法,反倒是从华为出来,才开始听到一些言论,比如运维背锅、运维层次低等等,当时感觉还有点怪怪的,这一点下面会再详细讲到。 + +我当时是在华为电信软件部门,大家熟知的短信、彩信、智能网、BOSS计费系统以及运营商客服系统等都是这个部门的产品。我到公司没多久就进入了一个新成立的项目组,为运营商开发一个阅读类互联网产品,因为是工作后参与的第一个正式项目,从需求讨论、方案选型、代码开发到上线这样一路跟来下,几乎倾注了我所有的热情。当时完全是封闭式开发,除了吃饭睡觉,其它时间基本都用在这个项目上,周六日都是泡在公司的。 + +项目上线之后,基于运营商海量用户的积累,业务量很快就增长上来了,按照惯例,各种系统问题、故障宕机也随之而来了。当时我们团队规模不大,大家也都是齐心协力,出现问题我们总是一群人一起冲上去解决问题。之所以有这样的反应,主要是因为不忍心看到自己和团队一手打造出来的系统出问题。在华为,软件质量的荣誉感胜过一切。 + +因为我经验尚浅,所以一开始都是跟在后面看着主管和老员工解决,后来对于一些疑难问题,我就会主动要求接过来研究一下,有时候一个问题要研究好几天才会有些眉目,不过也是在这样的一个过程中,随着解决的问题越来越多,经验也就越来越丰富,很快就成长了起来。 + +再加上我一直是出现问题后,第一个做出响应和冲到最前面的那个人,主管和团队也对我有了足够的信任和认可,也正是因为获得了这样的信任和认可,后来我得到的成长机会就越来越多。 + +这里就分享一点: + + +要敢于承担责任,敢于表达自己的想法。特别是对于职场新人,只有承担,且敢于承担更多更重要的责任,才能够快速成长起来。一些重要事项,主管肯定是优先安排最稳妥和靠谱的人去做,这个时候老员工的优势会更明显,作为新人或经验尚浅的员工,如果没有积极主动的态度和令人放心的表现,很多好机会往往就与你失之交臂了。 + + +当时,我们解决完问题,不仅仅是内部解决完就好了,还要给客户汇报。说简单点,就是把问题原因、处理过程和后续改进措施,用客户能够听懂的表达方式讲出来。一般都是先用邮件发送正式的报告,然后再当面做解释和汇报。当客户不理解、不认可的时候,你就要花更多的时间和精力想办法去表达清楚。 + +说实话,我当时认为这是非常浪费时间的事情,我想要是把这些时间都花在技术研发上该多好。不过,多年以后回过头来看,这个过程对于培养、提升自己的“软技能”是很有帮助的,主要锻炼了以下几个能力。 + + +能站在对方的角度考虑问题,或者说要有服务心态。很简单的一点,你不能拿着一堆晦涩难懂的技术术语去给客户解释,而是得用客户听得懂的业务语言解释。 +文字表达和口头表达能力。不论是书面的还是口头的,表达一件事情都要有清晰的逻辑,把前因后果理顺畅,先有结论,然后分条陈述。我现在能写这么长篇幅的专栏文章,很大程度上也是受益于这个过程中写报告的锻炼。 +从更全面的角度看待问题。有时候有些问题可能不仅仅是技术层面的问题,也可能出在其他方面比如业务逻辑、第三方、用户自身以及信息沟通上。一开始,面对这种问题我都是直接反馈说不是技术问题,跟我们没关系,可以想到这样的回答总是会被客户诟病。慢慢地我才发现,即使问题最终不是由你来解决,也应该全面考虑问题,跟客户一起讨论最终的解决方案,这样才会得到认可。后来我发现这是一个技术人员普遍存在的问题,比如,“我的代码没问题”,“在我的电脑上是好的”等等,其实就是缺乏全面看问题的意识,也是缺少站在对方角度考虑问题的意识。 +首问负责,问题闭环。问题找到你,你就要端到端负责解决,最终要给客户一个满意的答复,即使问题解决不了,也要能够获得客户认可的反馈。而且,如果这个事情是比较复杂的,需要时间和一个过程来解决,一定要在过程中及时反馈,而不是迟迟不响应。一个最常见的反模式就是“这个跟我没关系,你去找谁谁谁吧”,一句话就把客户的满意度给消磨没了。 + + +这么多年工作经历下来,我感觉对我个人职业发展帮助最大的,恰恰是上面这些良好的工作习惯,没有这些好习惯的扶持,单纯的技术技能成长很容易遇到瓶颈。而且,如果说技术技能是在不断更迭变化的,上面这些做事的基本原则却可以随时随地迁移使用。趁早养成良好的职业习惯,会对个人发展有巨大的好处。 + +对这个阶段做个总结,我更愿意承担一些非常具有挑战性的工作,成长得就比较快。同时,在客户层面,我又相对比较愿意表达见解和意见。虽然那个时候没有什么沟通技巧,也没什么表达技巧,甚至有些时候是笨嘴拙舌的,但是,很多时候技巧是次要的,最关键的是要敢于表达,当团队需要这样一个角色时,是不是有人能够站出来承担起这个职责。慢慢地我在客户层面也得到了一定的认可和信任,成为一个真心诚意、关键时刻能靠得住的一个人。通过这样一个阶段,我不但在技能深度上有了积累,在广度上也体现出了明显的优势。 + +我为什么会把运维当作职业发展的方向? + +这个阶段大约也就1年左右,我的主管就开始跟我沟通,由我来组建这个产品的运维团队,把线上运维、稳定性和部分客户沟通工作完全交给我。 + +可能有些人觉得做运维是很低级的事情,让你做运维就是让你去填坑,其实对于这样的言论以及今天开头提到的什么运维背锅论,我是十分反对的。当然,更多的时候我也不是去解释,而是靠做事情来证明。 + +说回到当时的事情上,当时主管在跟我沟通独立带一个运维团队时,我的感受不仅仅是晋升层面的喜悦,更多的是因为能够做运维而感到非常自豪。 + +为什么会非常自豪,这就不得不提到华为内部,在当时来讲,就已经有非常完善和先进的运维体系和运作机制了,我们一起来看一下。 + +在华为内部,运维是非常受尊重而且非常关键的岗位。如果你在研发团队中一直写代码,没有做过运维工作,是很难晋升高级别岗位的。所以华为的架构师、技术经理甚至是更高级别的研发主管,按照不成文的规定,都默认要在运维团队轮岗过,然后再选拔出来。而且这里面最最关键的是,运维这个岗位不是你想做就能做的,是有条件要求的。 + +下面我们就来看看有什么样的条件要求。我当时是在华为电信业务软件部,华为的运维体系分为一、二、三线,我们分别来看。 + +1. 一线维护 + +这个团队是负责产品的交付服务和后续的客户服务工作。从技能上,很像传统运维,主要是对网络设备、硬件主机和操作系统层面要熟练。一方面要负责交付的项目管理;另一方面,也是非常重要的一点,要对一线客户满意度负责,也就是客户反馈的所有问题,甚至是客户工作中表现出来的喜怒哀乐都要关注。 + +一线维护,最重要的就是必须要有非常强的服务意识。 + +2. 二线技术支持 + +因为一线维护面对的是单个具体的运营商,在遇到一些问题的时候,往往没有经验,但是二线因为要面对某个产品全球的局点问题,所以在经验上更容易沉淀和积累。当某个一线团队遇到没有经验的问题时,二线有可能就可以很快很好地帮忙解决,而不用直接透传到三线。 + +同时,二线还要做好统筹协调,因为一线过来的问题不仅仅是产品本身问题,也可能是网络设备、硬件、操作系统、存储甚至数据库等的问题,这就需要二线帮助一线协调专家资源进行处理,而不是一线再一个个找人,这时一线只管反馈问题即可。 + +二线技术支持,大多由产品研发或者一线维护经验的人员抽调上来的,即使没有这些经验,也要下放到一线去锻炼很长时间,两三年都有可能,所以技术和经验上都相对更加全面,同时能够有较强的推进协调能力。 + +3. 三线研发维优 + +到了三线就是研发团队中的运维团队了,这个团队在华为叫做维优团队。这个团队就很牛了,一般都是从开发骨干精挑细选出来的,一方面是为了锻炼人,另一方面也是为了在出现问题时,能够有最专业、能力最强的人响应处理,因为电信级业务是国计民生的基础设施,一般传递到三线的问题,都是比较严重或者疑难的了,必须投入精兵强将第一时间解决问题。 + +处理问题的过程中,还会不断完善工具体系,提升日常维护和问题定位的效率。因为三线同样要面对全球局点问题,所以7*24响应,而且常年无休,比我们现在互联网运维的工作负荷要大得多,所以这个团队成员一般做个1~2年就会转岗晋升,不然身体肯定是承受不住的。 + +三线研发维优,这个团队的成员就像军队中的突击队或尖刀连一样,总是冲在最前面,在高压状态下,解决最复杂、最棘手的问题,所以从选拔阶段,就有非常高的要求。最终经过这个团队磨练出来的人,技术能力、沟通协作能力以及全面解决问题的能力,都是非常突出的。自然地,在晋升发展方面就会有更大竞争优势。 + +上述这样一个非常严密的一、二、三线运维机制和协作体系,各条线各司其职,发挥各自优势作用,串联起了客户、产品和研发整个技术支持体系,基本上就支撑起了华为电信软件在全球局点的技术支持和服务工作,这一点还是很强大的。 + +也因为各自都有独特的价值体现,所以运维岗位上人员的存在感和成就感就会比较强,当然就不会觉得做运维是很低级的事情。同时,因为人员非常优秀,能力突出,这个岗位得到尊重也是必然的,甚至是令人向往的。 + +其实,能够得到尊重,还有非常重要的一点,就是来自华为对客户和用户的尊重,真正的把“客户第一”融入到了整个公司的组织架构和运作机制中。 + +这里我们不做过多发散,理解下来就是谁离客户最近,谁对客户负责,谁就能代表客户,谁就有最大的话语权,甚至是指挥权和决策权。体现在上述我们所说的运维机制上,就是:一线的声音,代表了客户声音;一线反馈到二线的问题,二线必须响应;二线传递到三线的问题,三线必须响应。 + +当然,问题级别不同,响应效率可以不同。同时,三线可以根据客户现场情况,以及问题严重程度,对问题进行升级,以知会到更高层级的主管进行关注。 + +在考核上,如果一线提交的问题,最终被定性为二线支持问题,或者三线研发质量问题,那二、三线的全年考核将会受到影响,如果是频繁出现问题,那就会受到严重影响,而且各级主管要承担连带责任。 + +这套机制的根本目的,还是为了促进整个体系能够以尽快解决问题、提升软件质量为目标。整个团队树立起这样的观念,就自然会对质量和问题有敬畏感,研发维优那个时候大多都是远程电话与一、二线沟通,潜意识里就会把一、二线作为他们的客户,同样保持谦卑和尊重。 + +所以,无论是从对运维的定位上,还是整个公司文化以及运作机制上,都形成了对这个岗位的高度定位和尊重。 + +说回到我个人,因为当时项目性质的原因(前面提到本质上是一个互联网形态的项目),是高度定制化的,并不是传统电信业务的产品形态,所以当时我们无法直接获得一、二线的支持,所有的运维工作都由研发团队完全独立承担,当时我就是把一、三线的事情都做了,前面很长一段时间是既做开发又做三线维优工作,对我技能上的提升帮助非常大。再往后因为精力有限,在后端维优团队建立起来之后,我就花更多时间做一线工作,贴近客户多一些,这里就对我之前说的职场软技能的提升有很大帮助。 + +当时华为的三线研发维优,其实很像Google的SRE岗位,各方面能力要求很高,不仅仅是软件开发这么简单,所以当时让我去做运维,并且给到我足够的授权去组建和带团队,就相当于让我去做SRE这样高端的岗位,我自然会觉得非常自豪。 + +再往后,在这个专业方向上做精做细,形成差异化的优势,自然就会有更大的收获。 + +给我们的一点启发 + +这样的一个发展过程并不是我刻意设计过的,机会也不是刻意争取到的,就是平时多做一点,做得认真一点,确保最终能够拿到结果,而且稍微努力一下,尽量拿到比预期好一些的结果。在这个过程中,随着个人能力的提升和全面发展,后续各种机会也就随之而来了。 + +如果让我总结就是这么平淡无奇,如果让我给出个人发展建议,想要从普通做到优秀的话,就是上面几句话。 + +岗位上,可能不会跟我一样去做运维,但却一样可以做到优秀的架构师、技术专家、项目经理或产品经理等等,只要你有心即可。 + +最后,你在个人成长和实际工作中遇到过哪些问题呢,有哪些感悟和心得,欢迎留言和我讨论。如果今天的内容对你有帮助,也请你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/39云计算和AI时代,运维应该如何做好转型?.md b/专栏/赵成的运维体系管理课/39云计算和AI时代,运维应该如何做好转型?.md new file mode 100644 index 0000000..252248d --- /dev/null +++ b/专栏/赵成的运维体系管理课/39云计算和AI时代,运维应该如何做好转型?.md @@ -0,0 +1,117 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 39 云计算和AI时代,运维应该如何做好转型? + 今天我们来聊一聊,在云计算和AI时代,运维应该如何做好转型?今天的内容可以说是我们前面运维组织架构和协作模式转型的姊妹篇。针对运维转型这个话题,谈谈我的思考和建议。 + +我们先来看业界的三个典型案例,一个来自国外,一个来自国内,最后一个是我自己团队的案例,都非常具有代表性。 + +先看国外Netflix的模式。 + +Netflix从一开始就强调开发人员进行自助化运维。我们第一篇文章中就介绍到,Netflix内部的运维工作全部都由开发人员完成,平台也由开发自己完成,只保留极少的Core SRE角色专门响应和处理严重等级的故障。类似的还有亚马逊,无论是其电商业务,还是AWS公有云服务,全部都由开发搞定。 + +这样的团队因为其技术前瞻性,微服务以及分布式这样的技术架构早已落地,再加上其超强的技术能力,所以可以从一开始就这样来做。 + +再看国内阿里的模式。 + +阿里技术团队在2016年左右,也开始进行“去PE”的组织架构调整,原来需要PE完成的运维工作,全部由开发承担。原来的PE要么转岗去做工具平台开发,要么作为运维专家做产品规划和设计,还有一部分无法适应调整的PE就只能离开了。之前在业务稳定性保障过程中起到核心作用的PE,随着各类工具平台的逐步完善,在高度自动化之后,最终也只能面临职业和技能上的转型。 + +这种模式,与Netflix正好相反,也就是一开始技术能力无法满足要求的时候,能靠人就先靠人,然后过程中不断完善各类自动化平台。 + +最后,再说说我自己的团队,发展过程中的模式。 + +我大致回顾了一下,发现从去年年初到目前,将近两年的时间里,我自己的团队也没有招聘新的应用运维人员了。并不是没有合适的人,我总结了一下主要有两个原因。 + +第一个原因,随着自动化逐步完善,效率不断提升,单个PE能够支持的业务变得越来越多;同时,很多事情开发都可以自助完成。 + +第二个原因,我有意识地招聘具备开发能力的人员,他们入职后一方面参与各类平台的开发,另一方面还要定期轮岗做一些运维工作。后来我发现,只要有效进行引导,同时具备运维和开发能力是不成问题的。 + +所以,结果就是,应用运维的岗位需求已经没有这么强烈了。从趋势上看,跟阿里的发展过程非常相似。不过这个过程,我从来没有刻意地设计过,基本算是顺其自然。 + +从上面的这几个案例来看,无论哪种情况,就运维来说,随着日常重复的人工操作被逐步自动化之后,如果还是固守原有的工作模式和思路,能做的事情、能够提供的价值,一定会越来越少。终究有一天,我们会面临和阿里PE同样的转型问题,而且这个转型是在可预见的短期内就会到来。 + +既然预见到了趋势,那下面我们就来谈谈应该如何面对。 + +应用运维的转型 + +如果只允许给一条建议的话,我给出的建议就是:学会写代码。 + +我们早期的运维岗位,基本上都不会对代码能力有很强的要求。所以对这个岗位上的同学来说,这一点就成了技能上最大的短板,也成了后续职业发展的瓶颈限制。 + +但是,运维行业发展到现在,无论是从趋势上看,还是从我们现在所经历的实际现状来看,单纯靠人力维护的投入越来越无效。 + +所以,无论是我们做运维转型也好,还是做其它技术转型也好,具备代码开发能力,已经成为一项必备技能。 + +这里多说一点,我们大多数做运维的同学不具备代码开发能力,并不是自身的能力问题。很多情况下都是因为不够自信,对写代码心存畏惧,担心自己写不好,所以一开始就把自己给限制住了。如果是这样,我给的建议再多也没用,关键还是要靠自己先迈出第一步。 + +现在我自己的团队中,有很多同事做了多年运维工作后,因为乐于尝试和挑战,很快就可以独立编程,而且因为自身有很多一线运维经历和经验,可以说即懂业务又懂开发,反而比单纯做平台和工具的运维开发更有竞争力。 + +下面是一些具体的建议。 + + +代码开发上,我的建议是可以从Python、PHP或Go这些上手比较简单的语言开始。这里不是写脚本,一定要能够实现完整的业务功能或流程。一开始尝试去做一些简单的工具,实现一些简单的功能,再往后可以通过一些复杂的业务场景深入下去。一旦场景的复杂度高了,就会涉及到更高的开发技能,比如并发、缓存、消息甚至是服务化框架等等。关键是敢于迈出第一步,然后逐步转变。相信我,真的没有那么困难,我身边有太多的优秀转型案例,都是这么过来的。 +提升产品意识。这里不是要求运维同事都成为优秀的产品经理,或者具备很强的产品设计能力,而是一定要有产品意识,只要有这么一点点小转变就会带来大不同。我简单说明一下,我们很多运维同事,甚至是资深级别的,往往还是习惯于处在最末端,前面有什么事情找到我,我就处理什么事情,属于被动响应类型的。但是,如果你有产品意识,能够将你所做的事情整理汇总起来,然后做一下流程上的串联,再把流程中每个环节步骤的功能进行详细描述,同时在梳理的过程中,将一些不合理、不规范的地方进行标准化约定,也就是我们前面说的标准化过程,然后输出的内容就是平台开发所需要的需求分析和产品PRD的雏形了。如果能将所做的事情从单纯的运维操作转化到这个维度,那我们呈现的价值就完全不一样了。 +提升技术运营意识。这一点跟上面类似,简单来说,就是如何根据需求,把承载了标准化和规范体系的工具平台真正落地应用起来。同时,在落地的过程中,通过问题收集和一定的数据分析,然后再回到产品设计和需求实现流程中进行改进,形成一个良性的闭环。 + + +在阿里的PE转型过程中,有一部分转型去做效能工具研发,有一部分经验丰富的资深运维就转型去做了技术产品和技术运营这样的运维专家角色。对于开发人员已经可以自助完成的部分一线运维工作,运维专家还会在这个过程中对开发做一些赋能。 + +所以,对于当前运维岗位上的同事来说,有这样一个先天优势来承担这样的职责,可以参考阿里PE转型的经验,根据自己的优势特点提前做好方向规划。 + +云计算和AI带给我们的挑战 + +机遇与挑战并存,上面我们更多地讲了机遇,但是与此同时我们也要看到挑战,甚至是危机。 + +而最大的挑战和危机往往都不是来自内部,当我们还在纠结如何不被开发替代的时候,外面的技术环境已经发生了很大的变化,而这种变化带来的将是颠覆性的改变。 + +有两个最大的外部因素,一个是云计算,一个是火热的 AI,我们分别来看。 + +首先,云计算发展到今天,已经不是我们想象中的只能提供IaaS服务的云平台了,目前各大公有云上的PaaS产品体系也已经非常完善。我们前面讲的各类分布式中间件产品都有覆盖,而且这些产品,还都是各大公有云平台公司在自有业务上锤炼出来的非常优秀的产品。 + +简单一句话,现在我们去做一个业务,基于这些基础服务,完全无需自研纯技术产品,只要专注业务逻辑开发即可。我了解到国内某新兴的O2O,每日超过千万笔的订单量,除业务代码外,其它基础层面的服务就完全依赖于某大型公有云的IaaS、PaaS以及周边的各类服务体系。 + +这种情况下,非但不再需要大量的如SA、网络工程师、DBA以及应用运维这些岗位,就连技术门槛较高的分布式中间件研发岗位也会大量缩减,所以这个挑战和危机就会非常大了。 + +这种情况下,我们应该如何面对?其实,我在前面的文章中已经给出答案,大家可以先回顾一下,然后再往下看。 + +这里的答案就是,从价值呈现的角度来思考我们可以做什么。至于做什么我前面也提到过,比如持续交付以及稳定性保障体系。当然根据业务的不同特点,远不止这些内容。这些都是跟业务自身层面相结合的,与平台无关。与业务结合,就会有个性和独立的地方。如何根据自己的业务特点,找到跟业务相切合的价值呈现点,是我们每一个人应该去思考和探讨的。只有找到这些点,我们做的事情才会有价值和意义,我们所在的岗位才会有价值和意义。 + +然后,再谈谈AI。这里说明一下,我们现在谈到的AI,其实大部分情况是在谈论AI的一种实现方式,就是机器学习算法。关于这一点我在InfoQ分享过一篇文章,我把链接附在文末,如果你感兴趣可以读一读。 + +AI和Ops的结合,更多还是场景驱动的。就是我们要处理的数据量越来越多,面对的场景越来越复杂,而且会大大超出我们人力的认知范畴。比如BAT这样的公司,几十万台服务器的规模,出现一个问题,我怎么能够快速发现,快速定位,并最终快速恢复?如果是几百甚至几千台服务器,靠人还是可以搞定的,但是几十万台,靠人就不可能了。 + +所以,这个时候,就需要借助技术的能力,而机器学习算法又正好可以满足我们的诉求。 + +这里想特别提一点,机器学习算法的应用,是离不开场景和业务特点的,也就是说怎么用还是离不开人,离不开对业务和场景熟悉的人。从我现在了解到的情况来看,很多公司和团队,针对每一个场景都需要投入很大的精力去对某个特定曲线和算法进行调参优化,以确保它们的准确性,也还没有神乎其神地达到完全不靠人的无监督学习。这里面并不是说算法本身不具备这个能力,而是现实场景太复杂,我们不能用简单固化的算法来应对。 + +说到这里,我想我们应该可以抓住这里面的关键点了,就是懂线上运维实际情况的人做这个事情,会更加适合。当然,这是极其理想的状态,因为机器学习算法的应用还是存在比较高的门槛,不仅仅是技术层面,还要一定的数学基础,需要对大数据产品有所了解等等,是个相对复杂的过程。 + +所以,这里我的建议就是要多去了解,因为未来随着技术、数据和计算能力的提升,AI是一个必然的趋势。如果一点都不了解,极有可能就会被卡在门槛外面,这就不是转型的问题了。 + +总结 + +上面我结合一些运维发展到目前的现状以及呈现出的趋势,做了一些分析和建议。最后我们总结一下。 + +新时代下,机遇和挑战并存,我们确实面临着岗位和技能的转型。我给出的建议是:学会写代码,培养产品意识,提升技术运营意识。 + +当然,转型这个过程也不会完全是绝对和极端的,以后就一定是一个运维都不要,一个SA也不要,一个网络工程师也不要。但是,我们应该看到,这些岗位会更加收敛。一个是岗位设置上会收敛到各大云平台厂商这里,做专职的基础和后端的服务维护;同时随着自动化的完善,在岗位数量上也会收敛,不会再出现大批量的岗位需求。最重要的,这些岗位上的价值空间以及成长空间,将会变得极为有限,不管我们愿不愿意承认,这都是我们不得不接受的现实。 + +同时,在云计算和AI时代我们面临的这些挑战和危机是可以预见到的,而未来还会存在大量的不确定和我们预见不到的东西,这种情况我下我们又应该如何应对呢? + +或许,唯一的办法就是不断地学习和提升自己的技能,保持对技术发展趋势的敏锐性,及时做出调整和应对,才是根本的解决之道。 + +关于今天我们分享的主题和内容你有怎样的感想呢?你是否正在面临转型的困惑?是否已经成功转型,有什么经验?欢迎你留言与我分享讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + +拓展阅读 + + +AI时代,我们离AIOps还有多远? + + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/40运维需要懂产品和运营吗?.md b/专栏/赵成的运维体系管理课/40运维需要懂产品和运营吗?.md new file mode 100644 index 0000000..534044b --- /dev/null +++ b/专栏/赵成的运维体系管理课/40运维需要懂产品和运营吗?.md @@ -0,0 +1,75 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 40 运维需要懂产品和运营吗? + 在《云计算和AI时代,运维应该如何做好转型》这一期内容中,我提到两个转型建议:一个是技术产品,另一个就是技术运营。今天我就更加聚焦地来分享这个观点。 + +我们运维接触更多的是软件生命周期中的运行维护阶段,我之前总结过一张图,就是在这个阶段要做的一些事情,把它们串起来就是下图: + + + +这张图的思路应该非常清晰了,而且对照一下我们日常在做的工作,基本上也离不开图中所描述的这些事情。 + +这里我想表达的是,我们应该从这张图中敏锐地观察到,研发团队对运维团队的诉求,以及运维呈现的价值已经发生了变化,我们更加需要能够帮助团队建设出高效运维体系的角色,而不是能够被动响应更多问题的角色。 + +运维的角色转变和价值体现 + +打造一个运维体系,我们完全可以把它类比为一个产品业务体系。我们公司的组织架构中,针对一个产品或业务,如果要对其进行技术上的实现,自然就离不开类似运营提需求,产品分析设计、业务架构师设计建模、开发实现以及测试保障这样一环套一环的配合,每个角色都发挥着独特的价值。 + +那么,对于一个运维体系,就相当于是面向研发团队内部的一套技术业务体系,只不过我们的需求方和客户是开发人员,而不是业务人员。 + +我们对照一下可以发现,运维团队中技术环节的角色是不缺的,但是缺少的是业务环节的产品和运营角色。但是我们做事情,不一定非要有岗位上的明确设置才能往下做,只要有能起到这个作用的人承担这样的职责就够了。而这里,最合适做这个事情的,一定是运维,因为运维是日常线上运维的执行者,只有运维最清楚这里面的细节、问题和痛点,换其他人可能很难能够讲清楚。 + +当然了,我们也不能强制要求运维一定要完全具备产品经理和运营经理的专业素养,这样就本末倒置了。这里我们强调的是运维要有产品和运营意识,总结起来最本质的就两点:第一,能将需求讲清楚;第二,能将产品推广落地。 + +技术产品 + +关于技术产品,其实主要就是回答以下几个问题: + + +是不是能够把原本靠人工完成的很多工作转化成需求? +是不是能够把日常工作中运维和开发的痛点转化成需求? +是不是能够把当前系统存在的问题和隐患找出来,在解决的过程中,经过分析总结提炼成需求? + + +这个过程中,可以尝试把自己做的事情串一下,用流程图也好,时序图也好,把整个过程梳理一下。过程中每个环节具体要做的事情可以通过文字描述的方式写出来,尽量分条罗列,清晰有条理。这里也可以参考我们前面讲过的内容,把一些标准化和生命周期管理的方法论融进来。这样可以一举两得,我们的标准化制定能力,场景需求分析能力慢慢都提升上来了。 + +你可以按照我们刚才讲的内容动手做一下,这样整理出来的一份文档或者内容,其实就是一个产品PRD的雏形。如果你想要更进一步,有更加专业的输出,也可以参考了解一些产品设计方面的知识。 + +当需求提炼出来之后,跟对应的运维开发一起合作,将需求真正落地实现。这样一个过程下来,运维的价值和能力体现是不是就跟之前有了很大的不同呢? + +技术运营 + +通过上面技术产品的工作,可以做出一些有针对性的工具和平台来。但是,仅仅有工具和平台还远远不够,因为只有把这个平台真正落地,并产生了实际效果,才是有意义、有价值的。这个“真正落地”就是技术运营要做的事情。 + +所以,接下来要做的就是落地。 + + +平台推广落地。工具做出来了只是第一步,得要有人用,这就需要去推动落地,让大家都来使用,从而真正给团队带来规模上的效率提升。同时,我们的技术产品也是各种标准和规范的载体,在这个落地过程中,也是标准落地和执行的过程,就需要运维和开发配合做出一些改造,为后续更大规模的效率提升和平台建设打下基础。 +线上运行数据分析。通过我们的平台和工具,对线上业务和应用运行时的指标进行数据分析。比如,应用上线或者每次变更上线后,线上运行的情况是怎样的,容量有没有降,RT有没有上涨,监控有没有异常,用户体验有没有下降,用户和客服的反馈如何等等。以上这些维度和指标就需要通过数据、图表和曲线的方式呈现出来,并基于这些呈现进行分析和判断,做出后续运维决策,比如是否需要扩缩容,是否需要处理问题,是否有改进的地方。在这一点上,应该要形成对整个业务和技术架构体系改进和完善的正反馈才行。想想看,业务运营是不是也非常关注业务的数据报表,也要依赖数据情况决定后续的业务运营手段。 +过程改进。平台更多的是一个执行工具,但是工具的使用是要配合大量的标准和流程一起来运作的。比如上面我们提到的,如果一次发布之后流量下降,RT升高很多,面对这样的问题我们应该有怎样的应对机制,这里就体现出管理和流程的重要性了,要解决好不同角色和团队之间的协作问题。同时,过程中需要改进和完善的内容,能够落实到平台和工具的,也要形成正反馈,来提升我们工具和平台的效率。 + + +这个过程可以用下图来表示: + + + +我们面临的业务场景在不断发展和变化,这就决定了技术运营过程也必然是一个持续发展和完善的过程。所以从这个角度讲,技术运营的生命力和竞争力将会是持久的。 + +在腾讯,运维就被定义为技术运营,第一次听到这个岗位名称时,感觉还是很贴切的。另外,我在很多大会上听海外的华人工程师分享,Operation这个词都是被直译成运营,但是在国内我们大多还是把Operation翻译成运维,从字面上就把这个岗位的定位给拉低了。 + +不过,叫什么不重要,只要我们通过今天的内容看到,具备技术产品和技术运营的能力才是最关键的,这一点最重要。 + +总结 + +最后,我们再总结一下,运维虽然不是业务系统的实现者和代码的开发者,但是我们参与到了产品技术标准的制定、业务系统运维体系的建设以及后期的技术运营中,这个时候运维已然成了整个技术架构的设计者之一,而且是架构稳定和演进的看护者,这时我们所发挥的作用和呈现的价值已大不相同。 + +从技术产品和技术运营的角度再来思考一下运维,现在的运维还是之前那个运维吗?欢迎你留言与我一起讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/41冷静下来想想,员工离职这事真能防得住吗?.md b/专栏/赵成的运维体系管理课/41冷静下来想想,员工离职这事真能防得住吗?.md new file mode 100644 index 0000000..2068772 --- /dev/null +++ b/专栏/赵成的运维体系管理课/41冷静下来想想,员工离职这事真能防得住吗?.md @@ -0,0 +1,114 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 41 冷静下来想想,员工离职这事真能防得住吗? + 本周主要和你分享几个关于个人成长的话题。前面我们讨论了在新时期运维如何做好转型,运维是不是要懂产品和运营这两个内容,都是为了我们能够成长为技术骨干,最大限度地发挥出自己岗位的价值。 + +今天我们就往后聊一聊,当你从技术岗位转换到管理岗后,应该如何适应新的角色,做好技术管理。技术管理这个话题很大,不是一下子就能说透彻的。所以,我就把处理员工离职问题做为一个切入点,从这个角度说起。 + +从员工离职说起 + +年末年初,对于任何一家公司或者一个团队来说,最令人头疼的事情就是员工离职问题,特别是骨干员工的离职,对团队带来的影响和损失,以及团队氛围和信心上的影响是非常大的。 + +其实从员工离职这件事情上,我们还是能够反推出一些在日常管理中的问题,甚至是我们作为技术管理者自身存在的问题。同时,我们还应该从这件事情上找出我们的改进措施,不断提升我们的技术管理和团队建设能力。 + +Design For Failure的软件架构设计理念,同样也适用于技术管理工作。 + +因为我在EGO会员组(极客邦旗下高级技术管理者社群)专门分享过类似观点,引发了一些讨论。所以今天借着这个问题,详细分享下我在这方面的一些经历和感受,同时我也会给出一些技术管理中的反模式,这些是需要我们引以为戒的。 + +关于员工离职的两个观点 + +我先给出对于离职的第一个观点:对于离职这个事情,本质上就是员工个人发展和团队发展不匹配之间的矛盾。 + +稍微解读一下就是,如果员工个人发展速度很快,而团队提供的空间或机会不足,员工就会调岗甚至离职,寻求更好的利于个人发展的机会;而如果公司和团队发展很快,员工跟不上组织发展的节奏,这种情况极有可能就会被团队淘汰。 + +反观其它的理由,主管因素、薪酬福利,或者因为世界太大想去看看等等,这些都只能算是表面上的直接因素,或者叫导火索因素。 + +比如,我们现在经常说的,员工离职最主要的因素之一是与主管不合。如果我们仔细想想,根本上还是因为主管在目标方向上与员工个人诉求达不成一致,或者管理方式上限制了员工个人发展,所以员工选择离开,或者团队主动淘汰,本质上的原因还是离不开发展这个诉求。 + +但是,有时候员工对于个人成长的不满意和不顺心,无法客观理性地表达,最终就都归因到主管身上;当然作为主管,有时候不考虑员工感受和自身因素,就简单粗暴地进行管理,也不是没有问题。所以看问题要全面,我相信更多的情况是因为双方性格和风格上对不上,并没有什么是非对错,所以一切看开最好。 + +接着彼此间发展诉求的矛盾这个问题,我再给出第二个观点:对于员工离职,从管理者角度,我们应该理解为必然结果,坦然接受,而不是避而不谈。 + +每个人在不同阶段,总会有不同的成长和发展诉求,所以总会有一部分员工在一家公司待到一定年限之后,会为了寻求更加适合自己的机会而离开,因为员工和公司之间的发展不可能始终保持匹配,所以从这个角度来讲,员工个体上的离职是一个正常现象,也是必然结果。 + +上面这个观点来自于领英CEO里德·霍夫曼写的《联盟》这本书,这里结合我自己的感受和理解谈一下我个人的看法。 + +如果能意识到是必然结果,那我们要做的就是 Design For Failure,不要试图去完全避免和杜绝离职,而是要有措施能够有效规避离职带来的问题和风险。也许最大的问题在于,道理我们都懂,但是能做到的不多。 + +所以,下面就来谈谈应该如何做好技术管理。 + +谈谈如何做好技术管理 + +1. 帮助员工做好个人中长期发展目标规划 + +主管应该跟员工一起确认员工任期内的中长期成长和发展目标,让员工能够在任期内发挥最大的作用和价值,同时能够尽可能地让员工在任期内达成自己期望的成长目标。对管理者来说有一件很重要的事情,就是能够找到团队发展和员工个人发展相契合的价值点。 + +这里很重要的一点,做技术管理者,一定要从关注事情、管理事情转换到关注人的层面。要关注人的成长发展,关注成长发展中的问题和疑惑,关怀人的工作体验和心理感受,这个才是管理的核心。一定不要忽略人这个核心要素,人的事情搞不定,其它任何事情都无从谈起。 + +我们很多管理者当前是缺失帮助员工做中长期个人成长这件事情的,更多的是做眼前工作的任务指派,甚至是指令性下达。所以现在很多员工离职,都会提看不到发展目标,看不到空间等等。这里的关键还是上面说的管理工作缺失,大多是我们没有帮员工做,而不是真的没有空间了。 + +这个事情要放到平时做,从团队人数还不多的时候就开始。我的团队30多人,基本每个月都和团队的每一个人做一次一对一沟通。如果实在忙不过来,那就针对核心骨干和有潜力的员工。这个沟通要持续做,并持续调整。如果放到员工要离职了再做,再去承诺,就没有意义了。 + +2. 进行梯队建设 + +各层管理者和HRBP,要有意识做好梯队建设,确保人才能够源源不断地输入。如有有员工离职能够有人顶上来,至少是有Backup,不至于因为一两个人离职团队就垮掉了。这块就涉及到招聘和“传帮带”机制的建立。 + +团队成员不一定都是水平很高、能力很强的人,关键是要合适,能形成合力。所以团队中既要有经验丰富和技能突出的明星员工,又要有发展诉求强烈的骨干员工,还得要有对各种事情充满激情和新鲜感的小鲜肉。每个梯队的特点和风格不一样,形成互补,发挥各自独特的价值,这样的团队才能够有生命力。 + +3. 提升管理意识 + +我也是从技术转技术管理,说实话栽了很多跟头,吃了很多亏,才慢慢有所转变,这个过程比较漫长。所以,对于刚做技术管理者的员工,HR团队一定要辅助做好“转身”的培训和赋能,不然就得吃“一将无能,累死千军”的亏。 + +同时,在这个事情上,更高级主管层面也应该要有些耐心,甚至有些时候要容许一些管理上的失误,不至于一出问题就一竿子打死。因为管理这个事情很多时候是需要管理者个人从一件件事情,甚至是一个个跟头中,一点点去“悟”到的。 + +技术管理中引以为戒的一些反模式 + +上面讲了做技术管理应该重点做哪些事情,那下面再说说应该少做甚至不做哪些事。 + +1. 事必躬亲,不懂授权,不敢授权 + +最常见的情况就是所有事情都会参与、过问甚至是干涉,更甚者,因为自己做更快,就懒得指导员工,直接亲自上手做。这样下去,久而久之,就相当于剥夺了员工成长的机会,剥夺了员工独立思考的机会。 + +我的建议是,对于非紧急的事情,还是让员工自己动手完成。你可以指导,可以给建议,但是不要代替员工执行和思考。 + +2. 总认为自己才是最正确的 + +懂得授权和分配工作后,往往把自己的想法和经验强加于员工,因为总觉得自己的经验才是最好的,即使思路上和方式方法上仅有一点点不同,也要跟员工争个是非对错。长久下去,员工的主观能动性就没有了,不但容易失去与员工之间的信任,还极容易造成与员工之间的矛盾。 + +我的建议跟上条相似,可以更多地给员工授权,多从边界条件和目标结果上提问,让员工主动思考。如果员工的方案是可以达成目标的,就可以放手让员工去做,过程中关注进展和风险,多给辅导和建议。 + +3. 仅仅关注技术层面,忽略全局 + +因为技术管理者大多是从技术岗位上成长起来的,有时看问题难免会片面。典型的一个问题就是只关注技术层面,不看全局。比如外部的需求澄清、进度要求、服务支持等,这些都是非技术层面的,恰恰可能也是一线员工所不擅长的,所以更需要技术管理者来关注。 + +我的建议,技术管理者一定要先关注全局,然后再看细节。对于管理者的要求,更多的是要“做成事情”,而不再是技术骨干阶段的“做事情”。 + +总结 + +最后,我们来总结一下。 + +对于离职想表达的两个观点: + + +员工离职反应了个人发展与团队发展之间的矛盾; +员工离职是个必然结果,坦然接受,做出应对,而不是谈之色变,或避而不谈。 + + +对于技术管理,这其实是个很大的话题,今天我们提炼一个重点: + + +技术管理者,一定要重点关注人,而不仅仅是事情。这一点是做技术骨干和技术管理者之间的最大差别,也是转变思路的第一步。 + + +在技术管理上,你还有什么疑问,欢迎留言与我沟通讨论。 + +这里推荐一下我们隔壁的专栏《朱赟的技术管理课》,我订购后阅读了很多文章,产生了很强的共鸣,同时也收获到一些硅谷技术公司的先进管理经验,很有帮助。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/42树立个人品牌意识:从背景调查谈谈职业口碑的重要性.md b/专栏/赵成的运维体系管理课/42树立个人品牌意识:从背景调查谈谈职业口碑的重要性.md new file mode 100644 index 0000000..bf75fa5 --- /dev/null +++ b/专栏/赵成的运维体系管理课/42树立个人品牌意识:从背景调查谈谈职业口碑的重要性.md @@ -0,0 +1,103 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 42 树立个人品牌意识:从背景调查谈谈职业口碑的重要性 + 求职者的背景调查是我去年频繁遇到的一个职场场景,经过仔细琢磨,我有了一些自己的感受和思考。同时,这也带给我一些启发,让我对自己后续的职场表现有了一些新的认知和调整。 + +我在这里分享出来,希望能对你有所帮助。 + +对求职者的背景调查 + +2017年的时候,特别是年初和年中,我陆续接到了几家公司委托的第三方机构,对我所在团队的前成员,甚至是我上家公司团队的员工做非常详细的背景调查。 + +可能是因为与我相熟,有些公司的主管有时会直接联系我,对某些求职者在我们公司的表现和业绩情况进行了解。 + +而且这种了解不仅限于我,我团队中的部分员工因为工作年限较长,在同行业的圈子里彼此也非常熟络,所以也会有人直接联系这些员工了解更全面的信息。 + +背景调查对于关键岗位和高级别岗位,至少在互联网公司,已经成为了必须环节。而且越关键、越高端,背调审计就越严格。 + +不管来自哪一方面的背调,最终被咨询一方的意见,都有可能影响到求职者的面试结果。 + +第三方背调,一般都是在候选人经过层层选拔之后,在正式Offer发放前做的最后一项审核工作。所以如果在这个环节出现问题,眼睁睁和机会擦肩而过,就十分可惜。 + +而对于行业同行内的背调,一般会在面试前进行。所以当你参加面试时,面试官极有可能已经对你形成了初步印象和评价,面试过程就可能是这个初步印象的验证过程。 + +所以,面对这样的情况,我也会非常谨慎。我会尽量客观地给出我的评价标准,比如技术技能、沟通协作能力、态度表现,以及是否有比较突出的业绩贡献等等。 + +之所以提供这些信息,是为了让对方来判断候选人的自述是否符合真实情况,以便对方做出决策。 + +但是,两种情况下,我会比较旗帜鲜明地给出我个人的建议。 + +第一种,对于我认为特别优秀的,我会给出强烈推荐或者非常推荐的建议。 + +第二种,对于确实存在短板或较大自身问题的,我也会客观反馈,建议对方在候选人面试时或入职后,在管理上多加注意。 + +我之所以这样做是因为,别人来咨询我,是对我个人的信任,我既要客观不能夸大,也不能藏着掖着掩盖事实。同时,我也要给出更加真实的一手信息。当然,我在向别人求助时,也期望别人能以同样的方式来反馈我。 + +如何树立个人口碑 + +谈到这里,你可能会问,既然这个环节我们自己完全不可控,那我们又怎么能确保得到被咨询人正面客观的评价呢? + +我的答案是:背调过程不可控,但是我们自身的表现却从来都是可控的。 + +也就是说,我们应该把注意力放到日常工作的表现上来,因为我们所有的评价依据都来自于此。 + +我们应该常常自问:我在工作中是否能做到积极主动,具备主人翁意识,敢于承担更多更大的责任?我在工作中是否能够不断取得成果,在团队中或跟团队一起作出较大的贡献,取得较大的业绩? + +以我之前公司团队的一位同事为例。 + +这位同事是我们Oracle数据库的DBA(数据库管理员),当时他入职的时候刚毕业一年多,经验一般,但是到了岗位上很快就融入了,经常会给我提出一些在数据库层面的优化改进建议。并且在我们评审通过之后,他会自己主动去落地执行。 + +在整个过程中,他跟其他团队成员配合得很好。出结果后,还会主动跟我一起去客户那里作汇报。久而久之,我对于他的工作很放心。后来我就放手让他自己去沟通,在他的协助下,我也减轻了一定的工作压力。 + +后来随着能力的成长,他基本承担起了DB的全部运维职责。他在岗的那两年左右,跟团队密切配合,在确保数据库没有出现过大故障的同时,还输出了很多提升性能和容量的优化案例。(要知道Oracle数据库的优化,能够带来的成本收益还是非常巨大的。如果你了解或维护过Oracle,应该会深有体会。) + +在与他共事的时间里,我们更多还是同事关系,但是相比其他人,我会对他抱以更多的信任。后来他转投去别的公司工作。 + +就在去年,我突然接到一个电话,对方是一家第三方背调公司,代表国内顶级的某支付公司,对他申请DBA技术专家岗位进行背景调查。 + +这时我脑海里涌现出的,都是他以往种种优秀的表现。所以背调问我的每一个问题,例如表现是否突出,工作态度和责任心如何等等,我除了实事求是地回答之外,还会特别用到“非常突出”“非常优秀”之类的赞扬之词。 + +这是比较突出的一个案例。其实绝大多数情况下,我们只要能够做到尽职尽责,态度认真,把精力投入到工作上,就不用对背调过于担心。 + +但是如果想要树立个人的好口碑,那就需要我们付出更多,要让团队和其他成员明确你独特的个人价值,就像我上面所讲的这位同事一样。 + +要引以为戒的反例 + +讲到这里,我也要举两个反例,这都是我在实际工作中遇到过的情况,请你引以为戒。 + +第一个,诚信问题,这是高压线,触碰不得。比如填写个人薪酬信息时,为了想获得更高的薪酬定价,故意捏造事实,肆意夸大之前的薪酬待遇。其实这些都是可以通过背调调查清楚的,如果存在造假,用人单位会直接拒绝。 + +我遇到的一个真实案例就是,候选人经过了终面,但当我们通知他最后一步背调完成,然后准备发放Offer时,他却反馈不准备接受offer了。 + +我们当然感觉比较可惜,但通过推荐人了解情况后,才得知他因为虚报薪酬太多,自己感觉心虚而不敢来了。 + +同样,在工作岗位上千万不要故意制造假数据,或者泄露工作信息谋取个人利益,这类错误触犯的后果将会更加严重。 + +第二个,消极怠工问题,这一点我认为是职业道德问题,是令人厌恶的。比如有的人在离职前的时间里,消极怠工,交接工作时不积极配合,经常迟到或早退,中途睡大觉或者跑出去不知所踪。 + +可能他认为反正要离职了,一切都无所谓了,也就不再重视个人行为是否妥帖,是否合乎公司规范,是否符合起码的职业道德和职业素养。 + +而这一行为恰恰会让团队其他人给他贴上“不负责任”“不靠谱”“职业素养欠缺”的负面标签。如此,他在将来背调时,可能就会因为这么简单的几个形容词,而被否定掉了,造成个人职业生涯莫大的遗憾。 + +当然,如果你因为意见和思路与团队或他人无法达成一致,而闹情绪,传播负能量,为团队协作制造障碍等等,这种消极表现也是不可取的。 + +共勉 + +在职场中,我们个人的职场信息和表现,就像我们的整个求学经历一样,会被详细地记录、跟踪和完善,这就更要求我们必须要学会树立我们的个人品牌,珍视自己的职场口碑。 + +而职场口碑的树立,关键还是来自于我们日常工作中一点一滴的表现,甚至是通过某些很细微的工作慢慢积累起来的,这是一个渐进的过程。 + +俗话说“千里之堤溃于蚁穴”,往往一不留神,因为一件负面的事情或行为,就会使你个人职场口碑和个人品牌瞬间坍塌。 + +所以,请守住职场底线,让我们共勉。 + +欢迎你留言与我讨论。 + +如果今天的内容对你有帮助,也欢迎你分享给身边的朋友,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(一).md b/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(一).md new file mode 100644 index 0000000..bb1988f --- /dev/null +++ b/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(一).md @@ -0,0 +1,48 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 划重点:赵成的运维体系管理课精华(一) + 我们的专栏原计划共36期,在三个多月的时间里,收到了你的很多反馈,我也对应做了一些补充,所以我们的运维体系管理课算是拖堂了,一直更新了42期,至此告一段落。 + +从今天开始,我们按照专栏的四大模块,分三次来整体梳理一下。我从每一篇文章里选出一句最想告诉你的话,制作成知识卡,帮助你回顾文章内容 + +我们专栏的四大模块分别是: + + +应用运维体系建设 +效率和稳定性最佳实践 +云计算时代的运维实践 +个人成长 + + +愿你已拥有不一样的运维思考! + +应用运维体系 + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(三).md b/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(三).md new file mode 100644 index 0000000..d5ae60f --- /dev/null +++ b/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(三).md @@ -0,0 +1,48 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 划重点:赵成的运维体系管理课精华(三) + 今天我们来梳理专栏的另外两部分,“云计算时代的运维实践”和“个人成长”。我从每一篇文章里选出一句最想告诉你的话,制作成知识卡,帮助你回顾文章内容 + +我们专栏的四大模块分别是: + + +应用运维体系建设 +效率和稳定性最佳实践 +云计算时代的运维实践 +个人成长 + + +愿你已拥有不一样的运维思考! + +云计算时代的运维实践 + + + + + + + + + + + + + +个人成长 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(二).md b/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(二).md new file mode 100644 index 0000000..cfe084f --- /dev/null +++ b/专栏/赵成的运维体系管理课/划重点:赵成的运维体系管理课精华(二).md @@ -0,0 +1,64 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 划重点:赵成的运维体系管理课精华(二) + 今天我们来梳理第二模块“效率和稳定性最佳实践”。我从每一篇文章里选出一句最想告诉你的话,制作成知识卡,帮助你回顾文章内容 + +我们专栏的四大模块分别是: + + +应用运维体系建设 +效率和稳定性最佳实践 +云计算时代的运维实践 +个人成长 + + +愿你已拥有不一样的运维思考! + +效率和稳定性 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/新书《进化:运维技术变革与实践探索》.md b/专栏/赵成的运维体系管理课/新书《进化:运维技术变革与实践探索》.md new file mode 100644 index 0000000..0612400 --- /dev/null +++ b/专栏/赵成的运维体系管理课/新书《进化:运维技术变革与实践探索》.md @@ -0,0 +1,33 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 新书 《进化:运维技术变革与实践探索》 + 你好,我是赵成,久等了。 + +经过近5个月的打磨,《进化:运维技术变革与实践探索》这本书终于和你见面了。 + + + +这本书的内容主要来自我的专栏。当编辑告诉我专栏文章可以集结成书出版时,我才意识到,原来我已经不知不觉地输出了一个相对比较完整的运维体系,当时的感觉:一切都是水到渠成。 + +在专栏的基础上,我们对书的内容做了更加深度的修订。全书共十个章节,重新梳理了章节脉络使之更加清晰,让你能够更系统地阅读这本书。 + +书稿完成后,我邀请几位好朋友写推荐和序,他们写的内容反过来给了我很多启发。我在专栏的一篇文章里说过,我的很多看法也是和业界的很多专家多次沟通交流后形成的。在这里,我把优维科技CEO王津银老师给书写的序分享给你,我们一起听听他说的话,或许你和我一样,也会有很深的共鸣。 + + +随着基础架构服务化越来越成熟,应用架构的服务化正走在路上,应用作为业务需求的第一载体也越来越重要了。什么是应用?为什么需要应用?有了应用,如何构成体系化的管理能力?作者的书恰恰在这些维度上给了我们详细的答案。 + +从纵向维度——独立以Ops的阶段来看应用,应用体系的构建也涉及很多方面。作者从标准化规范体系、组织变革、工具平台、自动化、意识等多个侧面做出了讲解,论述非常全面。从横向维度来说,应用生命周期跨越了研发、测试和运维等多个角色,这里面产生了一条重要的IT交付价值链,也就是今天DevOps里面讲的持续交付。说到持续交付,看似一个简单的工具链打通,却需要突破诸多障碍——组织上、工具上、文化上等。组织上来说,必须要打破部门墙,否则工具链肯定对接不上。工具平台能力要分解来看,涉及多个方面:项目管理、需求管理、环境管理、配置管理、部署管理、测试管理、监控管理、服务治理等,这些在作者的书中都给出了答案。这是非常难得的总结,是因为作者过去在研发、测试和运维上的综合经历,才能够给出这么一个体系化的指引。 + +本书还给了我一个更重要的启示,并且作者也一直是这样践行的——对于每一个运维人来说,在如今IT业务瞬息万变的环境下,我们更需要深度地思考、总结和分享交流,才能触达问题的本质。 + +希望你如我一样,能够从书中获取大量的养分,并反哺到你的工作中。让我们一起通过书中的文字来仔细体会作者的思想吧。 + + +最后,开卷有益,期望这本书能够带给你不一样的运维思考,我们共同进步。 + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/特别放送我的2019:收获,静静等待.md b/专栏/赵成的运维体系管理课/特别放送我的2019:收获,静静等待.md new file mode 100644 index 0000000..8808760 --- /dev/null +++ b/专栏/赵成的运维体系管理课/特别放送我的2019:收获,静静等待.md @@ -0,0 +1,127 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 特别放送 我的2019:收获,静静等待 + 你好,我是赵成。好久不见! + +还有一周就是2020年的春节了,2019年就真的结束了。最近忙完各种年终总结、年会,终于有时间总结下自己的2019了。我把这个小复盘发布在公众号上,也想在这里分享给你。 + +我2019年定目标的时候,并没定多大的目标,主要是害怕最后打脸,所以就定了要做三件事情:健身、英语和写字。看到这三个目标,你是不是会心一笑,谁还没立过这样的Flag呀! + +现在看,我还真是做到了,而且还超额了。 + +不过,三件小事,虽然很小,但是因为坚持了一年,却给我带来了很大的改变,也是19年回顾下来,给我带来最大成就感的事情。 + +健身 + +到目前为止,健身坚持了一年半了,每周2~3次,每次1小时左右,有时候时间紧张,10~15分钟HIIT。 + +18年9月份开始跟教练锻练,到19年3、4月份开始,体重就开始降,一开始是小肚子没了(八块腹肌还没出来),再到后来胸挺了(胸下垂得到有效缓解),屁股也翘了,肩膀有棱角了,背也比之前直了,总之身材变好了,穿衣服也好看了。 + +好几次出去演讲,除了被问专业问题之外,竟然还会被人主动问到你是不是在健身,身材保持这么好,你是怎么做到的。每当被问到这样的问题,就非常开心,发自内心的喜悦。 + +其实,19年3月份之前,我也练了差不多快半年,这半年变化不大,反而因为健身消耗比较大的原因,吃得比较多,体重不降反升,差不多长了5、6斤,特别是肚子,比原来还粗一圈。 + +说实话,那个阶段,很长一段时间都觉得非常挫败,练了这么长时间没什么效果,真的都想放弃了。 + +但是,变化就是在几乎要放弃,但是又想再继续坚持一下的纠结点上出现了。我自己都不敢相信,这个临界点出现后,20天不到,体重就降了10多斤,到目前为止轻了十五六斤,稳定在69公斤左右,夏天的时候会更轻一些。 + +当然,健身带来的好处不止身材好,精神状态也好很多。之前不午休,下午就崩溃,现在反应也没这么大。之前出差折腾一下,就很疲惫,经常会头痛,要花大半天调整,现在没有任何反应,可以持续地保持非常好的精神状态。 + +9月去敦煌走了次戈壁,3天90公里,在每天睡眠只有4个小时的情况下,体力精力已然充沛,毫无压力。 + + + +还有,健身还要特别注意就是要控制饮食,所以健身后碳酸饮料基本不碰,特别甜的东西不碰,多吃蔬菜水果和高蛋白食物,早睡早起,生活状态也变地更加健康。 + +有时候我跟人开玩笑说,我现在可以做到想胖就胖,想瘦就瘦,其实我现在真的可以做到,秘诀就在于自律带来的“体重”自由。 + +这是坚持带来的第一个小成就。 + +英语 + +坚持了一年的英语学习,每周两次外教口语课,每天读几篇英文新闻和文章,听个5~10分钟英文新闻或演讲或访谈,坚持读了四本英语原版图书,看得比较慢,但是一点点都能看懂了,慢慢地速度也提上来了,虽然做不到一目十行,但是相对熟悉的内容范围内,一目2~3行还是可以的。 + + + +其实最早触发我重新捡起英语的原因是,我想申请海外的国际会议演讲。所以19年初尝试报名了SRECon亚太区演讲,申请的Proposal在好友的指导和帮助下竟然通过了。这个会议的通过率差不多是10%,当时真的很兴奋。 + +6月份去新加坡做了第一次英文演讲,终于走向国际舞台了,跟LinkedIn、Facebook和Google的大牛们同台了,当时我的外教老师给我发了条消息:The world is our stage(世界才是我们的舞台)。 + +哈哈,还是忍不住开心下。 + + + +不过,过程我不是很满意,因为那个时候无论在发音、节奏、流畅和精准表达上,都差很多,只能算是一次当场的英文表达,并不能算演讲。 + +当时在会场上有很多国家和地区的工程师,口音都不一样,特别是印度口音,完全follow不上,当时被一个印度的工程师提问,直接给问懵逼了,好在有朋友在,临时给救了场。 + +Anyway,这算是一个美好而有意思的开始。 + + + +再说件特别的事情。这次过去,还见到了我20多年没见的高中班主任,当时给过我莫大的鼓励和启蒙的老师,很多过往多年的记忆也一点点被勾起。 + +原本一个很小的期望,去做一场国际演讲,然后打开了英语学习之门,然后体会到了阅读英文原版图书和与国际友人交流的乐趣,也更加开拓了视野,见到了许久未见的故人。 + +所以,一件事情,想做就去做,做了就可能会带来完全意想不到的惊喜。 + +英语学习的积累,跟健身一样,靠一点一滴日积月累。有时候单词不认识,长句子理不清逻辑,听力跟不上,表达想不到合适的单词,真的是想快也快不起来。 + +咋办呢?只能一个单词一个单词地去查词典,反复地读和背,长句子反复读,把主谓宾一个个挑出来,再去看修饰部分,再理解再读,听力听不懂就反复一遍遍听,看原文,看懂了,再听,说不出来,就换啰嗦的方式表达,不行就Chinglish表达,完了再找地道的英文场景的表达方式来修正,反正就是使劲往外憋。 + +没办法,就是一点点磨。所以,我觉得真正的学习和能力提升是永远没有捷径的,过程一定是枯燥和乏味的。 + +其实现在也没做到流利表达,但是相比6月份,可以自信地讲,如果我再去讲一次,一定要比6月份好过N多倍。所以,争取20年再去申请一个英文Topic,做到比6月份更优秀。 + +写字 + +2017~2018我写了咱们这个专栏,19年就想继续沉淀自己的一些思考和收获。我看了下,19年我写了30多篇公众号文章。后来发现有些东西想到了就想写,但并不适合放到公众号里碎碎念,所以后来又开了知识星球,一年下来,写了360+篇,每篇300~800字。 + +所以,从量上看,我觉得ok。我翻了下公众号文章,特别是上半年写的几篇,还是下了功夫,也带来比较大的阅读量,特别是有几篇转来转去,阅读量是我公众号内部的好几倍了。 + +公众号的文章是收集信息并整理后输出的,有一定深度,自然阅读量也会不错,所以从这个角度讲,虽然有了星球可以随时写,但是今年在写作的思考深度上其实是有点偷懒了,公众号发的文章偏少就是这个特征,这里是要反思和改进的。 + +所以,2020年,我会继续回到极客时间,会有一个小专栏输出,可以期待下。 + +三件小事的总结 + +无论是健身、学英语、还是写文章,我当初并没有立什么具体的Flag,没有了Flag的压力,我反而没有了太多的压力,完全当兴趣和小挑战来做,就是坚持,尽量不中断。 + +而且每次努力做到跟上一次一样是最低要求,如果能做到比上一次好就更棒了,何况我健身将近半年,还有点倒退,将近半年,连及格都没做到,但是好在没放弃,反而最终是变得越来越好,甚至超出我原来自己心目中的想象。 + +后来,我也想明白一个理儿,就是无论健身还是英语,都是我对我自己的期待,我做得好与不好,只对我自己有影响,并不影响别人怎么样,当然其实别人也并不care你身材好不好,能不能张口说漂亮的英语,关人家啥事呢。 + +我想做的只是改变我自己,我没有必要非得立个Flag给别人看。 + +所以,在个人成长上,努力做好自己,坚持对个人有益的一些小事,努力做到比上一次好一点点,我就觉得已经受益匪浅了。 + +今年得到年会上,罗胖第一张PPT分享就是贝聿铭的名句:“我一直沉浸在如何解决我自己的问题之中”,很有共鸣。 + +健身给我带来了更多自信,身体好心情也不会太差,精神更充沛的情况下,也保证了我有更充足的精力做更多其它有挑战性的事情,英语可以帮我打开一个新的空间,比如看英文原版书,跟国际友人交流,去国外演讲,阅读和写字,让我可以结交和认识更多志同道合的朋友,也让更多的认识我、了解我,而且还有机会到更大的内容平台上是展示,本身就是个品牌建设过程。 + +所以,坚持的力量在一个人身上总会以某种方式呈现出来,比如,别人会主动问,你的身材这么好,是怎么练出来的。 + +工作 + +还是聊聊跟云的关系,18年的时候,我们把IaaS层的网络和主机迁到了云上,当时虚拟机、容器、还有很多中间件仍然是自建自运维。19年7月份,我们就把这些也全部迁移掉了,能用云的都用云,把上云做得更加彻底,团队精力上也可以更多地放在业务建设上,我也有更多的精力去探索一些新方向,比如5G、边缘计算、视频技术等。 + +19年仍然有一些机会接触不同行业的同行,讨论和学习IT技术,眼界也更宽了些,也发现自己的不足,就是对某些领域的描述和呈现提炼不够,与不同级别的专家沟通,其实是需要不同层次的语言和呈现方式的,这块后续在工作中要提升。 + +最后,2020年的计划 + +工作上全力以赴,仍然坚持健身、英语、阅读和写字这几件小事,几件小事坚持了一年,如果能够坚持2年、3年,我觉得其实也是不小的成就。 + +嗯,仍然不立Flag,每天或每周认真坚持几件事就好了。 + +转一个前两天看到的句子,作为对新一年的期待: + +人生,难以量化,收获,静静等待。 + +最后,你的2019是怎样的?对2020又有怎样的期待?来留言区一起聊聊吧! + + + + \ No newline at end of file diff --git a/专栏/赵成的运维体系管理课/结束语学习的过程,多些耐心和脚踏实地.md b/专栏/赵成的运维体系管理课/结束语学习的过程,多些耐心和脚踏实地.md new file mode 100644 index 0000000..710e066 --- /dev/null +++ b/专栏/赵成的运维体系管理课/结束语学习的过程,多些耐心和脚踏实地.md @@ -0,0 +1,74 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 学习的过程,多些耐心和脚踏实地 + 我的运维体系管理课专栏,已接近尾声,这是最后一篇,也是我们的结束语。 + +我打算分两部分来写,一部分来对我们的专栏学习做个总结,另一部分打算写一下专栏文章写作这件事情给我带来的改变,或者说我个人的一些收获。 + +专栏学习的总结 + +在学校的时候,曾经有一位历史老师讲过,历史书可以很厚,厚到将每一个历史事件和细节都记录下来,要用图书馆来保存这么多的文字内容;但是历史书也可以很薄,薄到用几句话,几段文字就可以描述,用人的脑子就可以记住,因为历史的发展规律总是相似的。 + +我想这条规律对于我们的学习也同样适用,学习也是一个从厚到薄的过程。起初我们对一个领域或行业不熟悉,这个时候要学习大量的知识,不断向别人请教。但是在这个过程中,随着我们自己的不断实践和思考,逐步总结提炼出一条条原则和经验,甚至形成自己独有的方法论,然后再通过这些原则和经验举一反三,指导我们来应对在这个领域中所遇到的各类问题。 + +我想我们专栏中所分享的内容,就是厚的那一部分。希望这个专栏可以给你一个指引,告诉你方向在哪里,应该从何做起,这样就不必在混沌中从头摸索;至于薄的那一部分,虽然在最后几篇文章中我们有一起复习,但是,更希望你能够亲自实践和参与,形成自己的总结。 + +这个过程,可以多一些耐心,多一些脚踏实地。 + +对专栏的总结,我借用Bob大叔(Robert C·Martin )最新图书《简洁架构》(Clean Architecture)中的一句话,原文如下: + + +The goal of software architecture is to minimize the human resources +required to bulid an maintain the required system. + + +翻译过来就是: + + +软件架构的目的,是将构建和维护所需的人力资源降到最低。 + + +万变不离其宗,我们整个专栏的文章和内容,其实就是围绕着这句话展开的。从厚到薄的学习过程,我想用这句话来总结,再准确不过。 + +我在专栏写作中的收获 + +再来谈谈专栏写作给我带来的一些改变,或者说我从中的收获,期望对你也有帮助。 + +1.专注带来效率提升 + +当我发现有一件非常重要,且优先级非常高的事情摆在面前时,自然而然地就能意识到哪些事情是不重要的了,也不会再为要做哪些事情,不做哪些事情而反复纠结。 + +专栏写作对于我来说就是非常重要的事情。在一个相对较短的周期内,持续输出系统化的高质量文章,无论对我的时间,还是精力,都是极大的挑战。 + +所以,我索性将那些相对不重要的事情暂时放下,不让它们占用我的宝贵时间,消耗太多精力。比如,我大大减少了使用手机的时间,准确来讲是减少了各类社交软件,以及各类资讯软件的使用频率,在工作时间基本不去碰这些软件,高效完成工作。写作过程中就更加坚决,除非电话打过来,否则手机也不碰。 + +再比如,大大减少不必要的活动和会议。我有一个很简单的原则,就是超过30分钟的会议,我就拒绝,而是通过当面沟通的方式了解会议要讨论的内容,通常情况下,5~10分钟就可以得出有效结论。所以凡事事先充分准备,也成为我这段时间提升效率的关键原则。 + +找到适合自己的节奏。我个人的习惯是早起早睡,所以我会将写作的时间安排在早上。晚上我一般会思考和策划输出内容的框架,这样第二天一早就可以专注地把内容写出来,在头脑最清醒的时候写作最高效。同时,避免周末不必要的活动和外出,大部分文章都是在周末集中输出的。 + +总之,当我全身心投入一件事情时,原来担心的精力不足,时间不够这些情况都没有发生,反而会觉得时间更加充足,对计划的掌控更加游刃有余。 + +2.总结回顾是最好最快的提升方式 + +我们常常认为不断地学习新知识,一定要跟得上这个时代的所有新热点,才不至于掉队,才会不断提升。保持这种对新知识的敏感,我认为是没有问题的。但我想表达的是,学习再多的新知识,如果不能学以致用,它们都只是停留在纸面的上的内容而已。所以,单纯地学习,并不能提升技能,丰富经验。 + +相反,对于自己正在做的事情,或者已经完成的事情,能够不断总结和反思,就能帮助我们完整地、体系化地获得提升。讲到这里,我分享下文章写作过程中的一个小细节,让我感受非常深刻,期望对你也有启发。 + +起初我在规划内容的时候,脑子里梳理出来的东西都是大的框架,比如持续交付部分,按照原计划写4~5篇文章。但是,当我真正开始写的时候,我非常详细地回顾了我们经历过的持续交付过程,把中间的建设过程,使用到的工具集,包括如何落地执行,以及如何与外部合作这些都仔仔细细地罗列出来,突然发现我之前罗列的条条框框仍然是很粗的,所以就重新细化分解,最终用更多的篇幅完整细致地详述了整个持续交付体系。 + +同时,这个过程中,因为要讲清楚一些细节,以及为什么要这样做,我会去翻阅和查询大量的资料,确保我自己的理解是深刻和正确的。同时,还要把以前我们为什么这样选型也重新梳理一遍,提炼出很多方案选型方面的原则。这个过程再次加深了我个人对一些原理和概念的理解,也让我自己对本来模糊或模棱两可的认知,有了更清晰的认识,对我来说也是更进一步。所谓教学相长,我觉得应该就是这个道理。 + +整个过程下来,我的感觉就是,如果没有这样详细的总结回顾过程,很多细节和有价值的信息就随时间的漂移而遗落了,而这些信息一旦遗落,我个人如果再经历类似的过程,可能还要重走弯路,也就谈不上什么进步了。 + +所以,在我们不断学习,接收新知识和新内容的同时,也不要忘了时常做一下总结和回顾。而总结和回顾的最好方式就是写作,希望你也可以逐步养成记录日志和博客的习惯,真的会受益匪浅。 + +最后,感谢你的阅读和反馈。一路相伴,我们共同成长。 + +希望你能够通过专栏的学习,更进一步! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/00篇首语张雪峰:我参与的饿了么成长轨迹中,这些事需要反思.md b/专栏/超级访谈:对话张雪峰/00篇首语张雪峰:我参与的饿了么成长轨迹中,这些事需要反思.md new file mode 100644 index 0000000..950c4aa --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/00篇首语张雪峰:我参与的饿了么成长轨迹中,这些事需要反思.md @@ -0,0 +1,60 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 篇首语 张雪峰:我参与的饿了么成长轨迹中,这些事需要反思 + 你好,欢迎来到《超级访谈:对话张雪峰》这个专栏,我是专栏编辑利莹。 + +专栏缘起 + +两个月前我和极客时间总编辑郭蕾去上海,采访前饿了么CTO张雪峰,我问他“饿了么曾经经历收购百度外卖,后又被阿里收购,技术团队经过不断分合,以及比较复杂的融合过程,你有什么感悟吗?”他给我讲了讲在不同的角色里,要保证什么样的心态。 + +假如你是收购方,刚开始的心态一定要有包容性,因为以胜利者的姿态去收购,被收购方总会是难受的,要理解。在收购百度外卖的时候,上海的同学就是包容性不够。那假如你是被收购方,也不用太紧张,提前设定好期望值,面对最终的结果就好。 + +这个回答是他真实经历过后的感悟,在动荡的组织中如何调整心态,很重要。收购百度外卖面临的是两个同质业务的融合,且从技术角度,是一个技术相对弱的团队收购技术相对强的团队;而被阿里收购是补充阿里生态,面临的情况是技术强的团队收购技术相对弱的团队,这是两个类型的案例,他给我做了这样的分析。 + +其实对于饿了么的这段历史,相信很多同学也略知一二,当然最后饿了么走向被阿里收购的终点,很多人唏嘘,昔日的独角兽企业,没有逃脱这个结局。但在饿了么的发展过程中,对外竞争与对内整合,都在体现一家企业顽强的生命力。 + +收购与被收购的故事,相信身在其中的人会有更深刻地体悟。张雪峰作为参与其中的一个重要角色,在我们的采访中,也分享了他自己的很多故事。 + +张雪峰和饿了么的故事 + +张雪峰从毕业后经历过几段工作,第一段是跟随国内最早的Blogger毛向辉做教育软件,两年后跳槽到第二家公司,很高兴拿到薪资翻倍,之后进入一家外企,他最大的喜悦是获得了正式地晋升。再然后就是被大家熟知的:进入微软、出来创业、成为携程国际BU CTO再到饿了么CTO,他的职业成长也是在不断做选择的过程中。 + +进入饿了么是张雪峰职业生涯里一段非常重要的经历,他和饿了么这个企业更是相互成就。我们往往会更关注他“CTO”这个标签,但他其实也是从一个普通程序员一步步走到CTO的岗位。 + +说一说他的成绩吧,在最初加入饿了么的三年时间里,他将技术团队从几十人扩张到近2000人,引进工程界学术界诸多技术牛人;饿了么所能支撑的日订单量级从几十万到千万。这背后的人才吸引、技术文化土壤打造、底层架构建设过程虽然艰难,但最终都变成饿了么飞速发展的坚实后盾。在IT圈,饿了么拥抱开源,还有如异地多活、智能调度等这样的技术实践非常具有行业影响力。 + +但他自己评价自己“算是做出一点成绩的,就是跟这个团队的融合是最成功的。”我想,这也源于他对组织融合、文化融合之难的体会。 + +在采访过程中,他更多讲了自己做得不好的地方,一些教训、失败或者要去反思的地方,比如: + +收购百度外卖后,在团队融合这件事上不够果敢。为了照顾两边情绪,一直不敢做大尺度融合,虽然留住了百度外卖技术团队三分之二的人,但是在这个过程中,增加了团队内耗。 + +在异地多活的技术决策上有过犹豫,没有一步到位。饿了么异地多活外界以为是一步到位,其实没有,在做异地多活之前还做了灾备,花了不少钱,他认为这是在技术决策上的一次偏差。 + +不搞团建,他自认为这是有错误的。有人跟他吐槽过,不搞团建,没有让团队之间良好的关系建立起来。对他来说,更是导致后面要付出更大成本去做协调,他最后也反思应该多搞搞能够让大家互相吐露心声的活动。 + +另外,还讲到管理1800多人这样规模的技术团队的无奈,1800多人的时候,这位CTO已经做不到让绝大多数人能够享受这个工作的过程,也很难管到一线,很多一线工程师都不认识这位CTO。张雪峰离职的那天,办理离职的IT跟他说,我还是第一次见您的面,他跟我说在那个情境下感觉是很惭愧的。 + +对于自己的职业经历,特别是在饿了么的经历,他和极客时间做了分享。我们聊了100多个问题,除了上面的故事,我还问了他这些问题,比如: + + +为什么说自己是“土八路”,把百度外卖说成是“正规军”? +从商业角度,收购百度外卖这件事是不是失败的? +两个团队融合,技术语言要统一吗,这事你怎么处理? +你如何让团队保持激情,喜欢自己做的事? +工程师除了基本的技术能力外,有哪些软技能是你比较看重的? +对团队中一个人偏爱,会不会让其他人觉得不公平? +技术团队的价值是什么? +…… + + +开始采访前,我是非常期待这些问题的答案的,如果你和我一样,那就从接下来的访谈中找答案吧!在极客时间,让技术人发声,为技术人发声,一直是一件很酷的事情,我们希望有更多技术人能分享自己,分享他们看问题的视角。这也是我们做这个专栏的初衷。 + +接下来每一篇内容我们都会以访谈形式展现,欢迎你多发表看法,如果你觉得内容不错,也欢迎把它分享给你的朋友。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/01收购百度外卖:“土八路”收购“正规军”.md b/专栏/超级访谈:对话张雪峰/01收购百度外卖:“土八路”收购“正规军”.md new file mode 100644 index 0000000..f2a2ed6 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/01收购百度外卖:“土八路”收购“正规军”.md @@ -0,0 +1,77 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 收购百度外卖:“土八路”收购“正规军” + 极客时间:为什么把自己比喻成“土八路”,把百度外卖说成是“正规军”? + +张雪峰:刚收购的时候,饿了么联合创始人汪渊跟我说过一句大白话,大致意思是他们(百度外卖技术团队)就是国军的整编七十四师,我们(饿了么技术团队)就是一帮还在陕北的红军,离“土八路”都还有距离,他们估计是看不上我们的,就感觉竟然被一帮“土八路”收购,大致就是这种味道。 + +其实有一个相似的案例,就是淘宝当年收购雅虎中国这件事儿,后来有很多骨干真的是很不爽,感觉你们一帮“乡巴佬”去收购我们。当然,站在当年两边的技术底蕴和技术能力对比看,他们(雅虎中国)有这个资格说,当时淘宝就是一帮“乡巴佬”。 + +我们虽然有一定技术成就,做了异地多活(基础设施)、智能调度(物流匹配)、Element(GitHub全球百强),但其实工程这一块还是不够成熟的,特别是跟百度这么强大的技术底蕴相比。为什么百度能成为人才工厂?是有它的道理的,而且你看现在还在源源不断输送人才,它的体系、底蕴不是一般企业能有的。 + +极客时间:2017年收购百度外卖的时候,他们有多少人?收购完走了多少人? + +张雪峰:业务的人我记不住了,技术团队大概在600左右,后来陆陆续续走了三分之一,留下400人的团队。 + +极客时间:收购消息刚下来的时候,你们都有做哪些动作?双方团队是怎么沟通的? + +张雪峰:是这样,最早是我们单向地找他们,我们6月底过去做尽职调查,后面7月份已经确定收购这件事,一般到尽职调查基本都确定了,百度外卖那边应该也开始通知了。 + +正式官宣前的全员会议在北京开,但那次我没去,Mark(张旭豪,饿了么创始人、CEO)他们包括CPO都去北京开这个会去了,就是要给团队信心,去安抚一下。 + +再之前还有一次,我们周末开管理会的时候,三个人有老巩(百度外卖原CEO巩振兵)、陈青(原百度外卖副总裁)、耿艳坤(原百度外卖CTO)到上海这边来,大家认识一下。那天我和汪渊就跟耿艳坤表达了我们的诚意,我跟艳坤说,即使收购,我们也保持不变,团队还是归你管,你不用汇报给我。 + +那次相当于两边团队见了一下,第二天,我们就到北京再跟他们的核心团队见面,那一次耿艳坤的核心团队(不完全是他的直线汇报,也有一些关键的角色)都来了,我们这边一些重要的产品、技术的负责人也都在,那一次,大家都有点拘束,我们也不会去说做两边的融合啊什么的,绝对不能提融合,就大家say hello,认识一下,就是这样的一个过程。 + +极客时间:当时从商业上是计划百度外卖单独运营是吧? + +张雪峰:其实更多是从业务上,主要是业务上的重合度比较高,当时并不是说要把百度外卖独立一个品牌,而是希望两边的份额1+1>2,形成效应。 + +因为那时候饿了么已经被美团反超了,我们希望能追上美团,再实现反超。其实更多还是指望百度外卖在北京的份额,上海是我们占优,北京是美团占优,但百度外卖在北京做得很不错。同时,百度当时也有一个做团购的团队,后来关掉了,叫糯米,所以当时是形成了一些联动效应的。 + +极客时间:最后耿艳坤他还是离开了。 + +张雪峰:我也尽力了,但确实不是我可以挽回的。 + +我们去做尽职调查的时候,技术+产品只是耿艳坤一个人来。做尽职调查,你也知道,即使你很温和,不咄咄逼人,对方可能也会感觉是有一点不爽的味道。因为我自己也有感受,我们被阿里尽职了两次(一次是投资,二次是收购)嘛,就是打破砂锅问到底。 + +当时也问了艳坤一些问题,我尽可能温和,但有些问题没办法,你肯定要问各种数据,以及他们做的同城多活怎么样之类的,这样的问题。我知道他们肯定是不爽的。我当时就想百度外卖干脆就让他独立搞一块。 + +但后来他闪电离职,没办法,我必须直接接管。最近我不是做“中介”吗,给大家搭线介绍工作,我也问了当时北京那边的同学,他们跟我说开始是很不信任我的,即使撸串也撸过了,喝酒也喝过了,但是他们还是感觉不信任的,估计感觉我们都是土包子,肯定会用土匪的做法。 + +第二个大家可能的感觉,就是因为你们艳坤才离职的,谁知道是不是你在里面使绊子。但这个我没法解释,就跟我离开阿里这个事情一样,我没法解释,即使解释了,局外人也不会相信,或者更倾向自己的推测,哪怕是善意的推测。 + +极客时间:你怎么评价百度外卖这个技术团队? + +张雪峰:他们的工程师,确实比我们甚至比阿里更“工程师”。阿里很多时候还要受运营的限制。 + +极客时间:所以从技术实力来说,饿了么需要这批人吗?我听之前在饿了么的人说你很看中百度外卖的这批人。 + +张雪峰:是的,而且他们并没有太辜负我们的期望,就按平均线来说,我们确实是逊色于百度外卖这支团队的,就是在个人素养、团队文化这方面。 + +当然这是排除饿了么几个特例团队的啊,比如兰建刚( 原饿了么中间件团队负责人)带的中间件团队,还有石佳宁(原饿了么中台技术团队负责人)带出来的中台团队比我们平均线要高一些。 + +我就说素养这个东西,因为他们本身的背景,很多都是通过校招进去的,有忠诚度,还有经过百度体系的洗礼,而且他们对技术的纯粹要比我们强。 + +极客时间:你觉得百度外卖技术团队的平均技术素养是比饿了么要高。这个技术素养是什么可以再具体讲讲么? + +张雪峰:这个技术素养我认为就类似于基本功,百度的人才是体系化培养出来的。饿了么其实都是江湖上搜刮来的,收购百度外卖之前我们团队有一千多人,怎么来的?在上海哪招得到那么多互联网人才啊,在上海你挖不到人的,那时候拼多多还没起来,拼拼多起来后,我也去挖人。 + +基本上海所有相关的企业包括eBay、大众点评,我们都撸了一圈了,没人了,至少没有合适匹配的人,另外顶尖人才不在其列啊,因为这是极少部分,到哪都很难挖动。没人你只能去招非互联网行业的人,包括外企的、民企的,非互联网的传统企业技术人员,基本功确实有点参差不齐。 + +这还是跟培养体系有关,百度就是中国特色培养出来的有很扎实技术基础的一帮人,它是相对纯粹的工程师文化或者叫工程师驱动文化。工程师文化相比技术文化会让技术人员感觉更好一些,工程师可以决定大部分的事,包括延期这些事都能决定。 + +如果非要说国内有一家一定规模公司的技术团队能在文化和底蕴上稍微接近一点Google,那绝对非百度莫属,华为、阿里、字节、腾讯还是要差一些。 + +而且他们很多校招进去的,校招进去培养是最好的。举个例子,你看金庸小说,为什么郭靖这么一张白纸能够练成那么厉害的武功,黄蓉这么聪明的人反而练不好?一张白纸好搞啊。江湖上混了五年的,即使不是老油条,他也有惯性思维,就像我,我也有惯性思维,即使反复提醒自己换位思考,也不是很容易接受新的一套东西重新开始的,我会认为我总是对的。 + +很多人都在百度待了8年10年12年没有换过公司,一是忠诚度,二是他们从一开始还是蛮注重工程师培养,不是催命三郎马上要你做交付的,当然现在可能有点不与时俱进了。所以,培养体系非常重要。 + +我们没时间搞,因为我们整天在收购被收购、融资被融资,我们只能说靠一些 Senior(高水平)的人去平衡这种技术风格,尽可能把它们聚合在一起,但其实我们更多的还是靠一些Leader的个人能力和职业素养,而不是完全靠组织培养,因为一是人才来源成分复杂,二是没有稳定周期。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/02饿了么上海本土团队和百度外卖北京团队的冲突.md b/专栏/超级访谈:对话张雪峰/02饿了么上海本土团队和百度外卖北京团队的冲突.md new file mode 100644 index 0000000..99ff4a9 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/02饿了么上海本土团队和百度外卖北京团队的冲突.md @@ -0,0 +1,79 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 饿了么上海本土团队和百度外卖北京团队的冲突 + 极客时间:我看过当时饿了么和百度外卖发的组织架构联合的通知,一共6项任命,有4项是和技术有关的,这是你们稳定百度外卖技术团队的手段吗? + +张雪峰:对。因为百度外卖收购价格差不多就8亿美金,离他们原先估值差很远,相当于除以3~4了,期权一下子就贬值了,所以他们其实是需要安抚的。最主要安抚的倒不是业务团队,主要还是安抚技术团队。有些同学说,钱不是万能的,但至少是对他的尊重,这个有一定道理。 + +回过头来讲,说直白点,当时稳定团队你至少从钱上要表现出诚意。说个小插曲,在阿里收购我们之后,有一天,新的CPO说了一句话,从来没见过像饿了么这样的公司收购一个团队,给出这么好的补偿方案。 + +极客时间:什么补偿方案? + +张雪峰:我们当时一是给兑换期权,应该不是完全1:N,但更重要的是我们给了现金补偿。当然现金补偿是有限制条件的,比如说一年内不能离职,逐月发给你,相当于就锁定一年不要走,当时这个策略还是相当有效果的。 + +在宣布收购之前,我们内部其实有激烈讨论,我们要商量Announcement上写什么,其实要解决的问题很简单,就是看给北京团队什么地盘,因为钱的尊重问题解决了,接下来和钱一样重要的就是地盘尊重问题了。从业务上讲,大家都是重叠的,也都知道业务很好说,就是先不重合,比如北京以百度外卖销售为主、上海以饿了么销售为主,还是维持现状,大家不变就好。 + +但是技术团队,我要提前考虑起来,因为我要留住这么多的核心技术人员,其实这一点上我跟公司有些分歧,但最后CEO、CPO他们还是支持了我的建议。 + +我提了一个比较激进的方案。这也是后来上海同学可能心里会感觉有一些失落的地方吧,我把两个很重要的团队:新零售和物流的一号位都给了北京。 + +关于新零售,我们原来也在做,其实这个是饿了么首先做出来的,就是可以在商超买东西,不光买吃的,还能送卷筒纸,送饮用水,送药,还有送充电线,我们手机充电线一直卖的很好,所以我们叫新零售,现在饿了么和美团都有。在这块业务上,两边都差不多,都有一摊。反正我们开电话会议讨论了很长时间,后来公司也是冒一定风险同意了我这个方案,就是把新零售、物流的一号位给北京的同学。实际上,新零售不止一号位,连整个技术团队都交给了北京。 + +后来百度的同学也觉得很惊讶,就感觉居然能够给到我们这样的一个地盘,这个可能比钱更重要,我是这么想啊,但实际不一定,没有钱的尊重,可能都走不到地盘尊重这一步。他们的惊讶在于,认为这是根本不可能的,就是当时没有这样的期望值,觉得能不血洗已经不错了,也做好走人的准备了。 + +极客时间:那上海的新零售团队呢? + +张雪峰:后来我们就准备把上海的新零售取消,当然这些同学我们会去安置,当时也走了一些人,事后把整个新零售技术线,全部交给北京,上海团队就等于Close掉了。 + +还有更重要的是,我们把物流一号位经过讨论也交给北京了。虽然两边都有物流团队,但一号位大家是非常在意的,你交给北京很多人就会解读是北京物流做得更好,但其实不完全是这样。这两项任命,就相当于把上海的地盘剥离了很大一块。 + +上海物流团队的人数跟北京相比还是多的,因为一号位给了北京,上海那个Leader差点离职,他说凭啥?他说你这不“丧权辱国”吗?但是从这个谈判来说,你收购了就是你的人,这不是“丧权辱国”。所以我当时坚持做了这件事。 + +极客时间:为什么要这样坚持? + +张雪峰:为了留住北京这个团队,我不管商业上怎么样,Mark给我两个目标,一,留住技术团队;二,业务上做融合,这个不全是我的目标,后来我们空降了一个CEO过去。所以在我能力范围内,用我当时能平衡好的方式,换句话说,即使上海团队有诸多不爽,但我能控住大盘,就这样基本留住了北京技术团队。 + +极客时间:上海的同学有说过,觉得你太照顾百度外卖的人了,他们其实有点委屈。 + +张雪峰:是,准备收购的时候,我去过北京,回去后跟上海同学说起,确实有点数落上海同学的味道,我说你们看看你们所在的高级写字楼,下面就是地铁,周围商业繁荣。你们再看北京的同学,在上地办公,四街五街周边,简直就是五线乡镇感觉,最多就是有几栋高楼的乡镇。 + +我说我在那边骑自行车,尘土飞扬,你们看看百度外卖同学,天之骄子啊,人家技术那么强,在那么恶劣的环境,人家把百度外卖做得那么好。我当时还专门做了一张表格,对比这两个团队,给上海同学们这样说。 + +但后来上海同学还是怨气很大,感觉我太照顾北京同学。因为他们跟北京同学接触之后,去过上地五街之后,就感觉没你说的有那么多土、外面环境那么恶劣啊。当时链家也在那,隔得很近嘛,就感觉楼都挺不错的。 + +上海同学说我偏心,还有一块跟Hackathon有关。饿了么每年都会举办一次Hackathon,百度外卖加入进来后,我们要看是在北京还是上海进行决赛,当然老员工们都希望在上海,后来我坚决反对,就拍死了,说必须在北京,你们的差旅费我跟公司去搞定。差旅费并不便宜,因为大部分是上海这边的人,上海参赛的人多,所以大家很不爽。 + +大伙说我偏心,还有一个细节:每天参赛团队都要提交代码,每天刷评分,只要百度外卖同学上来,我就会在群里鼓掌,上海同学我是一概没有的,类似于这种。在会上我也说人家才多少人,你们丢不丢人,你们是做基础设施的,人家百度外卖的同学都是做业务的,他们做中间件的参赛很少。 + +回头看,决赛到北京,其实有点劳民伤财,但对于南北一家亲以及两边团队的后续融合,我并不后悔。这方面我们很舍得投入,我也感谢公司,这一届比赛在组织上我也是做了很多努力,有些地方我比较独断专行。 + +极客时间:你刚说到对两个团队的对比,还做了一张表格,都比较了什么? + +张雪峰:我可以发给你,这张表里,百度外卖北京中心叫TPU,技术就是T(Technology),P(Produce)是生产,U就是是UED,它们是合在一起的,所以叫TPU。收购百度外卖前,我们有上海研发中心和北京研发中心,当时饿了么的北京研发中心在望京,由史海峰负责。 + + + +这个表格里,可能少数也有夸张成分,就是想和饿了么同学讲包容、换位思考。 + +极客时间:你觉得百度外卖做的还是很不错的。 + +张雪峰:其实针对我们外卖行业,我一度也怀疑是个伪需求,真正把伪需求这个“伪”抹掉的,就是百度外卖,因为他们开始做白领市场。 + +我们和美团一开始都没意识到这个,就做高校的生意,因为对于高校学生来说,培养习惯就一点:价格,但价格恰恰是商业模式双刃剑,尤其是伤害远超过它的收益,所以不能只靠价格血拼。在上海,高校周围也有一些商业区,我们去扫楼,那个商业区的楼是很难扫的,所以我们当时感觉白领市场肯定做不起来。 + +我们感觉白领看不上外卖,因为那时候还没有现在这么精美的包装,你现在看很多火锅外卖都很精美的,包装就值这个价。但那时候外卖都是白盒子,还被人吐槽说你们造成环境污染。所以我们跟美团就在高校硬干,你低一块钱,我少5毛钱,就血拼,没啥奇招。当然了,全国大网下的无数区域网格精细化运营,极其考验组织能力、运营能力,不是有钱就能砸出稳定市场份额。 + +外卖一开始做高校市场是绝对亏的,如果我们一直做高校一定死无葬身之地。百度外卖的逻辑其实蛮有道理的,我认为整个行业应该感谢百度外卖。百度外卖做白领,虽然还是亏损,但一下子找到了商业模式,因为客单价上去了。 + +我跟你说,开餐饮是很苦的,虽然毛利是有50%甚至60%,但是房租、人员成本都是很大开销,所以商户是比较欢迎让别人去配送,就是不要让他们自己去配送,他们想人越少越好。房租和人员成本这两个去掉,之后的毛利可能只有20%甚至15%,另外,平台还有费用,比如:佣金、物流、广告、营销等等。 + +其实饿了么本质上跟携程是一样的,就是抽佣,但携程客单价多高?虽然机票是薄的,但一般用携程订机票的有相当比例会用携程订酒店。度假就更高了,上万都可能。所以客单价真的是决定命运。你总要找到商业模式,要赚钱的,你可以现在亏,但不能看不到尽头的亏。 + +一个商业的正循环,一定是要有持续、合理的需求,确实C端用户能接受外卖,有这个需求。我自己刚开始感觉外卖可能是伪需求,所以我这人做商业大概率一败涂地。包括Mark已经是非常有商业头脑了,但是他一开始也没有铺开做白领。因为白领客单价上去,意味着补贴也要上去。一开始肯定是要砸钱,刚开始,白领砸的钱可能更多。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/03不够果敢带来的内耗.md b/专栏/超级访谈:对话张雪峰/03不够果敢带来的内耗.md new file mode 100644 index 0000000..b51512f --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/03不够果敢带来的内耗.md @@ -0,0 +1,85 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 不够果敢带来的内耗 + 极客时间:我们前面说到血洗这个词,大家为什么觉得会被血洗呢?收购案例中血洗的情况比较多是吗? + +张雪峰:血洗这词或许夸张了点,大家明白意思就行。收购本身就是后妈养的,很正常。很少有被收购还能占主导地位的,单个人或小团队这种情况是有的,但是如果说美团当时把百度外卖收购,美团只会用更激烈手段。它跟大众点评是互补的还好一点,但是美团收购点评的时候也闹出了很多动静,而它跟百度外卖几乎是重叠的。 + +当时我们收购之后,其实想着这就是和美团最后一波PK了。 + +极客时间:针对百度外卖技术团队,做了这样的决策,最后从结果上看,达到你的预期了吗? + +张雪峰:整体上讲,等于把上海的利益切掉三分之一给到北京,仅从一号位角度看,给了北京两块肥肉,尤其物流这块最肥的肉。其实从组织上看,物流就是上海和北京要做融合,没有裁撤任何一边物流,但上海的新零售裁撤掉了。 + +最后百度外卖技术团队留下了三分之二,也算是稳住了。尽管我们做了这些努力,最终还是有大约三分之一的人走掉了。其实刚开始没走三分之一那么多,刚开始只是几个Leader离开,我都是一个个谈,但后面他们还是走了。 + +他们大部分去了顺丰(跟随原百度外卖CTO),不去顺丰的话,主动离职不多。但耿艳坤还算比较仗义的,后来我跟他还有接触,他没有一股脑把人带走。你想,即使我们给了现金待遇,即使我们给出了地盘,但如果他要带走他这些嫡系,应该也不难。所以我后来感觉他还算做的ok的,没有说他一个人走了之后就把团队搞残了。 + +否则的话,没有管理层、没有中坚力量,你如果只留下400个兵是没有意义的。一我要从上海派人,或者我再去北京招人,但北京我不太熟,如果发展到这个地步,那业务交付就比较麻烦了,要是把上海同学派过去的话就不是出差了,而是Relocate,更麻烦。 + +但这样如果还留不住,我也没办法了。有几次开全员会,我说我没法让你们完全信任我,只能日久见人心了,到底我是司马昭之心还是日久见人心,看我后面的行动。 + +极客时间:除了留住了这么多人,从商业上这次收购其实是不成功的。 + +张雪峰:我们第一个目的,就是把它的份额叠加过来,后来发现效果不好。第二个要做的,就是留住人(主要指技术团队),减轻上海技术团队巨大的招聘压力,那时在上海能招到合适的人,已经非常困难了。所以对收购这件事啊,从商业上是失败的,从技术团队来说我打70分吧。这是在我当年能力范围内能做到的最好结果了。 + +极客时间:感觉你是有遗憾的,打了70分,如果让你现在做决策的话,你会做不同的决定吗? + +张雪峰:回过头来,如果我现在去做这件事,我可能会在适当的岗位做些牺牲。 + +极客时间:怎么讲? + +张雪峰:因为当时给我们的时间很少,业务压力巨大,和美团白热化激战,我们要快速做决定,要给什么地盘,而且这些人没怎么共事过,相当于你要做一些赌博的。后来证明,我们把物流一号位给到崔代锐,利弊各半。 + +有一次我们在外地开会,他在房间跟我说,雪峰,我可能要走了。我说为什么?他很坦诚,因为其实他也预感到我对他有一些不满。因为代锐到上海次数太少,一是对上海的技术团队管理不够,二是参加业务少,因为业务的总部在上海嘛,这个是被团队诟病的。后来我问他为什么,他说因为家庭原因很难频繁地到上海。最后他离职还有一个原因,他说,雪峰,我个人的兴趣还在AI算法,我对管工程团队其实有点力不从心,我兴趣不大。 + +当时那个背景下也是赶鸭子上架。相当于说我们认为他有一定潜力,当然这个潜力是有一点点因为要考虑两边团队快速融合,同时稳住北京物流团队。其实我知道对代锐来说,是有一些拔苗助长的。所以后来相当于这个对于潜力的尝试是失败的。当然代锐也稳了团队一段时间,虽然开始很艰难,也被业务产品同学反复Challenge,但后来北京的物流团队开始形成一定战斗力了。 + +极客时间:所以说重新再来的话,你会重新安排一些人。 + +张雪峰:事后诸葛亮吧,比如说代锐,其实我是知道让代锐担任一号位可能有一点难度,但是因为百度内部自驱还是比较强的,他们工程不需要太费劲。蒋凡(曾任百度外卖研发中心高级研究员)也在帮他一起搞。全世界第一个外卖智能调度,其实是百度外卖做出来的,就是蒋凡和崔代锐他们搞出来的,饿了么和美团是后面才搞出来。 + +所以如果让我重来的话,可能就安排别人接了,但这样可能会对北京的物流团队有不小冲击甚至解体,因为收购后流失的骨干,相当一部分来自北京物流团队。当时为了全局稳定,我们一下子把这两个一号位给了北京团队,当然对稳定团队还是起到很好效果。 + +我现在感觉,到底孰对孰错分不清了,也不好说,历史没有如果。或许我当时狠一点,即使这些同学走了,但是对于公司可能是好事,当然了,也可能上海一直招不到合适的人,业务交付更慢,因为当时内耗很大,上海的同学和北京的同学都互不买账,对立程度甚至超过了后来口碑团队跟饿了么技术团队这种味道,因为饿了么毕竟是被阿里投资过,不是一下子收购,而百度外卖是瞬间就被收购了,没有任何技术融合(饿了么和阿里云前期还有技术交流)。 + +就是因为这样一下子收购,我一直不敢做大尺度融合,当时可能有点逃避心态,就是为了照顾两边的情绪。 + +但后来我们也产生信任感,最后他们跟我交心的时候就说很感谢,其实是我为了留住他们,增加了团队内耗。 + +极客时间:所以你会更果敢一些。 + +张雪峰:我那个妥协可能是,如果按两个极端来说,一个是0,一个100,我那个偏的度呢可能已经逼近20了,那最好是在45-55之间游荡,按上海同学想的就应该保留90,你最多给他们10,从单量考虑也好,从其他角度考虑也好。但其实如果论单量,百度外卖全部砍掉也不可惜,就是说这个技术团队我不要了,但我们收购是为了啥呢?至于收购之后在商业上并不成功,那也是后话,没有谁能穿越历史再来一次。 + +我没有做到我个人心目中理想的45-55之间这样的一个balance,我一直在努力,但这件事上,现在回过头去看,如果以我现在阅历,可能会做得更好一点,但终归影响不了大局,包括之后饿了么被收购这个终点。 + +极客时间:所以正常的操作方式还是会像阿里或者美团那样。 + +张雪峰:对。阿里其实在本地生活上反而没怎么操作,所以昆阳(王磊,曾任阿里本地生活CEO)相当于包容了我们好几年。 + +美团收购点评这件事情绝对是做的很漂亮。那时候团购这个东西已经江河日下了,美团收购点评的时候自身业务已经往外卖倾斜了,而且点评跟美团的业务互补性还是比较强的。但是饿了么跟百度外卖真的没办法,90%-95%业务重叠,必须要有人动,除了这些技术团队。 + +极客时间:收购百度外卖后,你会经常到北京吗? + +张雪峰:对,我不知道你去过上地没有。上地信息路一直过了三街,五街是彩虹大厦,再往前,六街大概是百度地图,七街道有个拐角,拐角有一个CityGo,那时候那边成了饿了么的据点,就一直在那。 + +技术融合之后,我最多的时候一周半到两周就要去至少待两个晚上。后面该走的也都走了,团队稳定下来之后,慢慢的就不用去那么频繁。 + +其实这里面还有一个插曲,本来我跟耿艳坤说,我们还是两条线,你还是汇报给你那边CEO,我们融合就先别谈。先把耿艳坤留住,大家留人都是先留核心团队。而且我也知道他也是江湖味很重的,兄弟感情很深,留住他就ok了,后面稳定再说。 + +但是很麻烦的是我们的HR体系居然也割裂了,就是我们两边各有一个HRBP。这件事情其实也是蛮为难的,我相信Mark也是思索良久最后做这个决定。 + +后来,过了好一段时间,北京那边的HRBP走了,才慢慢把北京的HRG这条线给收起来,所以当时我同时要对接两个HR。我们半年有调薪晋升,每次这个时候,我都要用Excel跟两个人去对,还得拆开来,不能互相看到,就很痛苦,每次到晋升季的时候要选评委也是个很头疼的事。所以这里面内耗也蛮多,就是不得已的这种内耗。 + +极客时间:现在让你评价一下坚决留住百度外卖技术团队这个决策呢? + +张雪峰:我坚决要把百度外卖的技术团队留住,这个决策你可以认为成功,也可以认为是失败,我也不好评判,最后双方的融合还算度过了艰难时期。其实关键是留住人,系统我不是特别在意。 + +毛泽东曾经有一句话特别有名,“存人失地,人地皆存,存地失人,人地皆亡”,当然这个人我认为是值得留的人。但这个决策你也可以说,哎呀你们这个拖了很长时间,为了迁就百度外卖,搞得市值下跌,业务也没上去,当然不至于影响最后饿了么这个风向标,因为百度业务量少,你最多就花点钱养着,我们最后也并不是因为百度外卖这几百号员工的工资导致公司被收购。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/04“戏剧性”的裁员,反思组织融合之难“难于上青天”.md b/专栏/超级访谈:对话张雪峰/04“戏剧性”的裁员,反思组织融合之难“难于上青天”.md new file mode 100644 index 0000000..d8d061e --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/04“戏剧性”的裁员,反思组织融合之难“难于上青天”.md @@ -0,0 +1,83 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 “戏剧性”的裁员,反思组织融合之难“难于上青天” + 极客时间:之前我们聊到你说去北京要宣布裁员的事,那个过程是怎么样的? + +张雪峰:是这样,因为后来阿里收购我们的事情也有一些风声了。在2018年1月份,我是知道收购百度外卖这件事在商业上看来是难以继续了,远远没有达到我们预期的商业效果,当时就等于硬撑着。 + +然后春节前都要发年终奖嘛,我们管理层就在讨论这件事,就说可能要准备裁一些人,因为要省一些成本。 + +本来是准备要去裁员的,先从百度外卖做起,当然上海团队也要出一定裁员比例。当时我跟汪渊讨论方案,我们先跟Mark开了会,后来我俩又反复核算,说得直白点就是裁员数字,其实汪渊他是让着我的,他那边出的份额多一点,虽然我不太想搞这事,但没办法,上海、北京都要出数字。 + +上海那时候资金可能也有点紧张,上海bonus也会有点降,但百度外卖基本就腰斩,准备要去宣布了。那天我和汪渊先坐飞机过去,准备宣布,已经快到门口了,CFO在机场刚刚降落,说立即暂停,你们现在就当什么事都没有发生,听我指挥,等我到了你们现场配合我就行了。 + +后来我估摸着(没找CFO求证过),可能跟阿里已经到最后一步了,你这时候去裁员可能会导致一系列问题,收购之前不能出现团队的动荡。所以,后来进去之后,完全180度转变,CFO上来就是说,今天我们怎么样,我们业务怎么怎么样,说了一堆。有同学就提问说,那我们年终奖怎么办?他说如果发不到位,我就xyz。 + +CFO这么承诺,我和汪渊傻眼了,我们白跑一趟了,来做灯泡了,只能配合他说,我们绝对会重用你们,绝对怎么怎么样。因为那时候两边融合已走上正轨,开始形成合力,所以当时这么说比收购之初更有说服力。收购一个组织,业务是一方面,人也是一方面,百度外卖这个技术团队还是优质资源,也不枉我当年偏心了他们一点。 + +极客时间:所以阿里在收购之前,百度外卖和饿了么融合其实已经就相对是稳定的了。 + +张雪峰:对,至少没有说刚开始就一下子走了一些人。 + +极客时间:还有一个好奇,两个团队融合,语言要求统一吗?这个事情怎么办? + +张雪峰:饿了么早期用的Python,这还是比较头疼的问题,招人比较头疼,那时候还算有点体量的,就是豆瓣、知乎,我们有不少豆瓣过来的人,靠他们撑起来。后来我们再找不到用Python的人了,就只能用Java,我个人并不喜欢Java,过于臃肿,写代码太难受,但没办法。 + +关于用什么语言,也有曲折,没有被阿里收购的时候,我已经基本做了决定,Python、PHP团队转Go,但最后很遗憾,没有实现。 + +当时收购百度外卖就面临语言不统一这个问题,百度PHP可以说是国内最强PHP团队之一,百度外卖(核心都是老百度)过来之后坚持用PHP,后来两边折中,我说把上海的Python也废了(当时Python已经有一部分转到Java),把北京的PHP也废了,我们都用Go+Java,皆大欢喜。Java不能取代,不然你招不到人。当然,我选Go也是跟团队聊了很长时间。 + +那时候要准备搞这个东西,我们都很兴奋。但后来阿里收购我们,我知道这事儿没戏了,不可能用Go了,阿里核心还是Java。在阿里Go基本只适用于K8S,还有一些纯粹玩技术的团队。我那时候的心情非常低落。 + +后来行癫跟我说了一段话,他说雪峰你听我一句,我不是像外界传的那样,非要用Java,大家一直说阿里很霸道,我不是的,你们用Go,用PHP都行,但你相信我,到后面你们会很难受(指各种技术融合、业务接入等)。 + +我们其实每一年都在收购或被收购的路上。16年蚂蚁投我们,17年我们收百度外卖,18年被阿里收购,甚至美团也多次要收购我们(指15年美团、点评合并前,一度传闻美团、点评、饿了么三合一)。所以在这条路上你完全不可控的,Mark也很无奈,他也想独立发展。 + +所以在这种情况下,我们很多事情是有遗憾的。 + +极客时间:很少有公司能像饿了么经历这么跌宕起伏的过程,特别是对于技术团队来说,经过不断分合,还有比较复杂的融合。如果让你给正处在这个阶段的人一些建议的话,比如计划收购别人或者将被收购的情况下,大家一定要有一个怎么样的心态? + +张雪峰:我们是先收购别人,我先说这个层面。你作为收购方,一定要有包容性。 + +我们收购百度外卖,上海的同学包容性不够,真正有包容性的可能也就是我和汪渊。我知道是大家做的东西重复度是比较高的,他们道理是对的,但是我认为你好不容易收购一个团队,你先观察一段时间。我其实并不是说后面过程上的事,而是你刚开始的心态要有包容性。 + +说难听点,你以胜利者的姿态去收购,被收购方总会是难受的。而且至少从技术角度,是技术偏弱的团队去收购技术偏强的团队,那别人肯定不爽。你要说阿里收购我们,其实我们并没有什么受害者心态,因为阿里技术本来就比我们牛逼,虽然做外卖我们更擅长。我们被阿里收购,其实我们当时是有心理预期的。 + +被收购的团队,大家也不用太紧张。或者换句话说,你自己设定好期望值就好。 + +如果是非常同质的业务,又是棋逢对手,有这个担心是正常的,有必要的,因为他肯定是要把你Buyout,你全部走了都无所谓。但是如果是差异业务的话,即使阿里这么厉害的团队,后来事实也证明昆阳还是比较依赖我们这些老兵的。所以如果你是差异化收购,其实不用担心。 + +百度外卖的同学,他们担心的,一是他们感觉很委屈,我们的精锐被你们“土匪”收购。第二,这么同质的业务,他们担心你们只是把我Buyout。其实并不是收购就要Buyout,消灭一个对手,我们确实是希望把百度外卖的业务再做起来,合在一起,技术上更是这样,希望这个团队来补充我们的短板。所以也分两种情况。 + +但我是说,站在技术团队整体不如别人的角度,确实你要有包容性。如果我们当时,比如说是百度外卖收购饿了么团队,那除了像做异地多活的团队还有一些存活空间,其他大家就自求保佑吧。 + +所以我感觉还是看业务形态,因为技术也是为业务服务。说难听点,业务如果是要签“丧权辱国”的条约,那真没办法,但如果收购是为了补充它的短板还是有价值的。我们正好经历的第一桩Case,是业务重叠度很高的。第二桩是补充阿里的。所以两个经历过了。 + +极客时间:在饿了么这段经历,你自己怎么评价?有什么反思? + +张雪峰:其实我认为我最大的幸运或者说也算是做出一点成绩的,就是我跟这个团队的融合,我认为是最成功的,在饿了么这个业务上其实并不成功,在技术上也只是尽我们最大的能力做到了,不能说有多好。 + +但是在融合方面我对比了像国内其他这样体量的公司,要么就是空降的领导被干掉,要么就是空降的领导血洗现在的一批人,再把另一批人带进来,总之就是反复折腾。 + +在饿了么这里,我也有教训,就包括当时收购百度外卖的时候,上面我的做法偏温和,我现在想想血腥一点可能更好,但现在他们跟我私交很好,这也是收获吧。如果当时血腥一点,现在或许早就恩断义绝了。 + +当时我沿用了原来在饿了么的策略,快速做融合,那个效果很好,因为那时我相当于是空降,但现在变成百度外卖是收购来的,他们算是被迫空降,虽然不需要取代你的位置,但是他们希望把原来团队各种文化带来(指百度文化)。 + +而我们有很多从腾讯过来的人,还有后来阿里进来,还有本土的(指饿了么),本土也有两拨,一拨是我加入后进来的,一拨是最早创业的,这五拨人在一起非常有挑战。阿里收购之后,还有口碑这么大的团队加进来,所以文化融合真的是比较难的问题,甚至是最大挑战,远超过业务融合、技术融合。 + +这里有很多教训,我也反思多次。收购百度外卖,从业务看是一个失败尝试,从技术组织上看也有不少遗憾。大概走了三分之一。虽然后面我们尽可能还是把不少百度外卖骨干都留了下来,至少留到他们期权兑现为止,但是当时是有很多内耗的,即使我去强行调节,但两个团队坦率说,刚开始是完全互相看不上的。 + +后来到阿里内部也一样,我们被阿里收购,口碑的同学也看不上我们,其实现在反过来想想完全没有必要。反而我们在Leader层还好,我跟白起,白起是原来口碑CTO,我们两个之间倒没什么大问题,但是下面的团队真的是矛盾很大。即便阿里非收购团队内部的组织之间都可能存在这种情况,更何况是我们一个被收购团队,好在我们被收购还有点价值。 + +所以现在反过来想真的没必要。我跟你又没有什么仇,但是大家要拧成一股绳真的很难很难,尤其不同的文化。 + +阿里、华为应该算国内组织能力非常强的企业了,其他包括美团在内,技术架构、技术能力很强,但组织能力、组织文化都要差一截,包括百度也有这样问题。 + +组织融合跟历史很像,以李世民之能力,也不能轻易搞定他老爹之前的班底。并不是说这些老班底没有价值,只是说味道上跟你、包括你的嫡系不那么融洽。所以,我感觉一个组织内部能够真正融合,真的很难。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/05职业成长:从校园到职场的蜕变.md b/专栏/超级访谈:对话张雪峰/05职业成长:从校园到职场的蜕变.md new file mode 100644 index 0000000..2bdb862 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/05职业成长:从校园到职场的蜕变.md @@ -0,0 +1,125 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 职业成长:从校园到职场的蜕变 + 极客时间:我们会很关心用户的成长,我们平台也会有一部分用户,可能刚毕业面临一些职业选择,会有焦虑,会对自己的未来发展方向有迷茫,所以我们挺想从你的故事里让大家去找找自己的答案。 + +张雪峰:我明白你的意思,我用那句话回答你吧,就是“幸福都是相似的,不幸的各不相同”。其实我感觉我还算幸福的,我还没到毕业就工作了,最后一年是在我们教授的实验室里做项目,那时候也算接触了互联网初期阶段。 + +但我说的不一定大家可以借鉴,因为这种其实就是硬币的两面,你去做项目固然可以长见识,但也可能你前面三年还没打好基础,把地基打扎实也很重要,这个要因人而异。如果感觉基础没打扎实,那我建议不要直接去外面的企业实习,意义不大。 + +只是我感觉我自己还OK,所以最后一年我就去实验室做项目,接触外面的世界了。我们当时做了上海第一个电商网站,那时候电商才刚起来,当然我只是打杂的,在后面做一些东西,能接触到一些不同的技术。 + +迷茫也很正常,绝大部分人都是那样,天赋异禀毕竟极少数。沉下心来做事情,多和高手聊、多向前辈请教。 + +极客时间:2000年前后,做程序员对你们来说是一个很好的选择吧?大家都想干这行吗? + +张雪峰:是,我学的是计算机科学专业,就业还是挺不错的。当时我毕业的时候,1800块已经算可以了,还有600块补贴。 + +所以到了毕业的时候,我有一点底气。当时我同学有的投了100多份简历,我因为在实验室有项目经历可以写上去,所以还好,我就投了5份,拿到了2个Offer。最后我去了一家初创公司,就在上海交通大学旁边,三个创始人都是交大的。 + +那会我就觉得我们老板真的很有个人魅力,我上下班来回通勤要四个多小时,没有地铁,更没有座位,两部公交车都是长距离,一部横穿浦西市区,另一部横穿浦东住宅区,上下班高峰都非常拥挤,那时候也没有加班打车福利,但我也很开心,乐在其中。没别的,就觉得每天工作都能学到东西,都有进步。 + +现在大家讲所谓内卷,可能985的计算机专业都不一定有好工作,找工作都讲什么介绍、内推的,我们那时候没有的,搞编程的企业就没多少。大家比较倾向去大外企、大国企或事业单位,那时候民企是很难赚钱的。另一个靠编程赚钱的路子是做共享软件,但能做到一定规模且口碑好的很少,而能靠共享软件赚大钱的,就更少了,印象最深的就是 NetAnts、Foxmail,我自己也一度产生过是否做共享软件赚大钱的念头,还好很快掐灭了冲动的火苗。 + +第一家公司是做教育软件的,简单说就是学校校园网的信息管理,包括考勤系统、排课系统、成绩系统,还是能赚一点钱。我们老大,在这我必须提一下大名以表感谢和敬佩,他叫毛向辉(Issac Mao),非常有技术前瞻性,比如说我们是国内最早搞XML研究和布道推广的。当时还成立了一个对应的组织,依托交大和交大学生在外面做布道,我们那时候做布道可起劲,研究各种基于XML的技术和标准,后者虽然很难,但极有成就感。 + +同时,我印象中这位老大也是国内最早的Blogger(博客),好像没有之一,够超前吧?虽然第一家公司的业务最后没做起来,但这位老大是对我这么多年职业生涯中影响最大的一位,没有之一。之后我见过无数比这位老大更成功、更有名望、更有前瞻性的技术人,但即使算上王坚博士、行癫、鲁肃这样国内数一数二的人物,也没能再引发我当年那种对技术的无比狂热、无限憧憬,不能不说是个遗憾。 + +那时候编程语言的话,主要用Visual Basic、Visual C++,也接触了一些国际化标准,很超前,还有包括局域网这种概念,怎么样把电脑串起来,在这家公司我基本上都体验到。但这家公司后来挂掉的原因之一也是技术(idea/prototype)太超前了,从今天来看都还有点超前,定义了很多教育行业的标准(不只局限于校园网或国内教育相关领域),也做了一些 prototype(原型)。 + +第一段工作经历,对我个人的影响,直至今天都是巨大的,也让我在之后二十多年工作中,逐步看清了国内做软件或行业标准的无比艰难,从此不再有奢望。 + +这中间,我还经历过Leader离职,他也是创始人之一。那时候我很震惊,感觉是不是公司快倒闭了,因为有一天他突然跟我说,雪峰,我可能要离职,部门交给你了。我很懵啊,我都没管过人,但后来还算顺利,因为主要是CEO有人格魅力,对我非常信任。 + +极客时间:在第一家公司,你是自己在自驱地成长,还是说你前面其实有一个更强的人带着你成长? + +张雪峰:我自己成长。我的领导人很Nice,他都是把任务甩给我的,就让我自己摸索,我在学校有C++基础。入职后直接给了我一个月,让我做一个制作课件的软件。 + +我自己用VC做,我VC上手其实还挺快的,因为我大四实习就用VC,但是独立做一个软件还是第一次,然后我就自己摸索,反正出了很多Bug,还把公司的一些东西误删掉了。后来我就自己折腾用恢复软件,好在恢复出来,没造成大的损失。反正就让我各种折腾,公司氛围主要是老板非常包容。 + +所以,还是回来说刚才的问题,我觉得每个人都要走自己的路。刚毕业,没钱、没经验,但是有时间,所以一定要多折腾,怕什么,你一个光脚的怎么能怕穿鞋的啊。 + +极客时间:你在第一家公司待了多久? + +张雪峰:两年。后来第二家公司也是教育软件公司,做培训+考试的,这也影响了我之后对教育行业的持续关注。现在K12被双减,职业培训就上来了,但都是民间驱动的职业培训,我们那时候职业培训主要靠政府组织,相当于是政府的定点供应商。 + +我在这家公司的主要收获,不在技术方面,而在于收入翻倍。但现在看来,虽然工资高了,但是我职业方面的成长几乎没有。 + +生活上,我在第二家公司任职期间,当了丈夫,当了父亲,这也是比较大的变化。 + +极客时间:要说技术底蕴,反而第一家公司会更好,对吧? + +张雪峰:那绝对,第一家公司甚至超过了我自己“掌勺”的饿了么,因为饿了么我有很多要迁就的地方,但第一家公司我几乎是可以“为所欲为”的。 + +刚才说了,第二家公司除了钱,其实对于我的职业生涯没有太大提升(接触Delphi是唯一亮点)。那时候才开始思考自己的成长问题,因为第一家公司给我的成长空间太大了,之后包括微软、饿了么在内,都没带给我这么大的技术成长空间,导致我的期望值过高,第二家公司虽然薪水双倍,但兴奋不到一年也就淡了,然后就投简历去了。 + +后来到第三家公司,让我知道了什么叫正规化作业。第三家公司算是外企,名义上是外包软件公司,实际上也是有自己核心的东西。他们是用B/S架构,后来才知道原来美国这么先进,人家B/S早就已经如火如荼了,我们国内还在C/S呢,真是落后。 + +现在你说AI、云计算这些是不是落后美国五年,我不好说,可能国内看着好像很繁荣,其实也有水分的。但我们那时候感觉真的是美国先进,他们包括3层架构、4层架构、N层架构,我以前只是听说过,没有实践过。 + +那时候还没有感觉外包公司那么烂,现在外包公司就不怎么样了。那时候互联网公司没人愿意去,就感觉朝不保夕,因为我们被2000年“.com”泡沫吓怕了,那会造个网站就能融资的,2000年绝大多数就挂掉了。尤其在上海,以前的毕业生大家都希望去金融企业,去世界500强。 + +不过,第三家公司我得到了晋升,很正式的,要去答辩的那种。我感觉那次的兴奋度超过了我在第二家公司薪水翻倍那次,因为双倍薪水的快感没多久就淡了,举个不恰当例子,那些财务自由的同学们,估计过阵子就淡了,心中依然期待财务双倍自由、财务无限自由。但晋升通过我是感觉很意外的,因为当时基本没抱什么期望,就当走过场,后来晋升了之后,就让我独立带一个项目,开始跑客户了,不是做销售,主要是了解、分析需求,和客户对技术方案。 + +我还记得我那时候的领导是一个女孩子,岁数比我稍微大一点,她英文非常好,我有一天跟她说我要走了,她说是不是去微软?如果你去微软我就不拦你了。那时,好多 .NET 程序员,最大理想就是去微软。 + +其实我去微软也有曲折的,去微软是面第二次才进去的。第一次面试对我打击特别大。其实我并不想去做顾问或做微软的架构师,我一直想做微软的研发,就做它的核心软件。微软那个时候有个成立不久的亚洲工程院(ATC),主要是做一些工具,比如Visual Studio,我很喜欢。当时面试我准备了很长时间,但最后没过,对我打击太大了。 + +那个时候,我一直期待邮箱里收到一封邮件说经过怎么样怎么样我们决定怎么样怎么样,但后来等了一段时间,终于等到了一封“感谢你”,之后整个人都不好了,很长一段时间没缓过劲来。 + +你看,这就是我经历的较大挫折之一,之后还有很多,包括饿了么期间。我真心觉得我天资普通,普通人怎么可能那么顺利呢?这次失败之后,我也在复盘、调整自己,当时我还想了挺多东西的。具体就不和大家说了,我想换作任何人,那个年龄,遭遇失败,都会反思。 + +失败没有价值,失败之后的反思和行动才有价值。 + +极客时间:那个时候你觉得自己技术很厉害是吧? + +张雪峰:我感觉还行,我那时候写文章也有些读者,文章的阅读量也还可以。那会很少有左耳朵耗子这样的持续布道牛人,当时我们就自己买书研究,书都不便宜,比如说Windows核心原理、MFC编程这种东西,这些对我来说问题不是太大,也有兴趣研究。 + +后来第二次面试,面了5轮,我拿了Offer,去了MCS(微软咨询服务部)。当时这个岗位要出差,我那时候还比较傻,我说如果出差是不是要我自己出路费住宿费餐饮费什么的,有没有出差补贴就更不敢问了,老大说这怎么可能?全部是公司出。因为以前没出过差,就闹了这么个笑话。 + +极客时间:你在微软待了几年? + +张雪峰:5年半,后来就出来创业了。其实我最后几个月已经跟北京的HR说我准备离职。HR说,“雪峰,能不能帮我个忙,最近离职太多了,我有指标压力,你能不能延到明年1、2月份,或者3月份再离职,或者到时候我们再沟通一次,能不离职就更好了”。 + +我说我现在没法干微软的活,我也不好意思拿微软的薪水,因为接下来上班时间我要去搞另外一个事情,她说没关系,你可以做你的事情,没有薪资,但公司给你交社保公积金,微软这方面做得还是非常好的,后来接触多了,发现名声好的外企这方面都差不多。也因为这件事,我知道了一个专业术语:No Pay Leave。 + +那时候发现还有这种操作,真的很牛。我后来在饿了么也尝试这样的办法,为了留住一些员工,我会说先让他做自己喜欢的事,哪怕回老家休息都行,我给你把社保交了,让它不要断。 + +这里,和年轻的朋友说下,社保真的很重要,千万不要等闲视之,即使决定要离职休息几个月,也尽量通过第三方平台自己续上社保。我面试的时候,对于断过社保的同学会打个问号,因为我会想,这哥们是不是目光短浅,还是说家里有几套房? + +极客时间:微软这份工作给你带来最大的收获是什么? + +张雪峰:我认为最大的收获不在技术上,而是在视野上。因为我进了一个需要去一线的团队,需要自己解决各种问题,我要跟客户去解释,你这100万花得很值,接触这些东西,让我知道这是在做一个Business(生意)。 + +我以前是一个很纯粹的工程师,虽然我在第二家公司第三家公司也去客户现场,但那只是了解需求,还是为了代码服务,但我在微软的时候,才知道做成一个Business有多么艰辛,哪怕已经完成签约、只是完成一个合同上的Milestone(里程碑)让客户验收通过、客户最终同意公司寄出发票、公司确认收到客户打款等等,都很艰辛。 + +刚进微软时太天真,以为只要用Windows或Office的客户,那还不是手到擒来?只要自己嘴不太笨、整体架构不太烂、适配好所有上下游接口(那时相当多精力需要花在这上面),IBM、Oracle这些公司不都得靠边站?但是最终,每次的客户现场,都给了我最大锻炼。说人话就是,分分钟教你怎么理解业务、做好乙方。 + +微软这段经历之后,我发现永远是人适应环境,不可能别人来取悦你或适应你。我那会也算做了半个售前(另外1/4个架构师+1/4个程序员),喝酒也是商业的一部分,这个东西只要在国内就很难避免,你就要去适应这个环境。即使我在饿了么有不小的“权力”了,但我也必须经常去适应别人(当然不是喝酒),还有业务团队。所以我变得真正发自内心愿意,而不是忍受地去适应,这是对我最大的触动和改变。 + +还有,我们经常说去大公司学技术,我觉得不只是这样。对于一个工程师而言,技术是立命之本,但商业、管理这些东西也很重要,有机会,你还是应该跳出技术,理解技术之外的东西。 + +极客时间:你刚才说忍受着去适应到发自内心地去适应,这应该是你成长中的一次变化,至少从微软这段经历来讲。 + +张雪峰:对,因为我以前也经常不能换位思考,比如夫妻吵架。其实我刚从阿里退下来的时候,我跟我老婆刚过两人世界,吵架反而越来越多,后来和好,我老婆跟我总结说,以前有你爸妈在带小孩,你又在外地,念你辛苦,回来你累得要死,我也不找你发火。现在闲下来了,火气越来越大。后来我也知道她只是要找一个倾诉者、倾听者,这在企业里面也类似。 + +我老婆和我有一阵子生气,两个人不讲话,就背对背用微信聊天,有时候我看她只要打表情包了,我就知道怒气稍微下去一点(笑)。换位思考、设身处地为对方着想,真的说易行难,我现在做得还很不够,但是微软这段经历可能是我一个转折点,我知道了商业的不容易,就是知道了在我角色之外的不容易。 + +这个词说的简单,我毕业的时候我就说我算是一个很能换位思考的人,因为我有一定的包容性,但大部分还是忍受的包容,你不爽的时候,其实就存在你要忍受了。但是微软让我感觉可以尝试着真的去站在对方的角度思考,因为真正和你因仇恨而撕的人能有多少呢? + +很有意思,你看,就同样一个事情,你心态一变,结果马上就能发生变化。 + +极客时间:刚开始到饿了么整体感受如何? + +张雪峰:饿了么我是很有感情,我不是饿了么第一任CTO,其实饿了么第一任CTO是科班出身的,听说他有点完美主义,跟现在很多架构师也是一样,希望完美地去上线,但是Mark(张旭豪)希望快点上线,先做出一个版本来(饿了么创业时就有MVP萌芽),我听说两人意见不一致。 + +后来是Raymond(汪渊)接任CTO,他的第一家公司并不是饿了么。他的经历很有意思(之前InfoQ还拍过一个短视频),汪渊在第一家公司干了三个月,然后他觉得没意思,自己也不能掌控一些事情,于是就离开了。这里我说个题外话,他第一家公司的老板叫黄峥,他的直线老板是阿布。所以我跟汪渊说,你到哪都是“真龙天子”。 + +他心胸很宽广,其实创办饿了么的这批人都能容人,格局很大,否则他们也很难接受我这么一个中年大叔,我去的时候已经40岁了,他们都是85后,还有90年的,要考虑到也许我们之间可能很难融合,但这个团队最终还是选择并接受了我。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/06拆解CTO:CTO的岗位职责.md b/专栏/超级访谈:对话张雪峰/06拆解CTO:CTO的岗位职责.md new file mode 100644 index 0000000..679d396 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/06拆解CTO:CTO的岗位职责.md @@ -0,0 +1,115 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 拆解CTO:CTO的岗位职责 + 极客时间:我特别好奇,从你的视角来看,CTO的职责是怎么样的? + +张雪峰:不同阶段CTO的职责都不一样,这个要注意。经常也会有其他公司的技术同学来问我这问题,我说我只能和你分享我的经验,具体你的场景是怎么样的,得你判断。 + +几年前,InfoQ采访我,我当时说了5个点,分别是:找到合适人才、组建合理团队、稳定交付产品、极致经营产品和激发团队。现在我想了想,还是有一些认知变化吧。 + +CTO本身有一些共性的我先不讲了,哪怕3个人的公司也要考虑一些技术前瞻、选型、搭建团队,等等,这些都是需要的,不可能说招一个CTO,但是他不搭团队,那就不应该叫CTO了,应该叫全栈工程师。 + +这些前提之下呢,我认为CTO的职责主要取决于业务。业务的成长,包括业务规模,也包括它不同的边界方向,还有其他一些杂七杂八的事情,CTO都要看。 + +或者我从三个阶段来讲。 + +第一个阶段,以我自己的经历感觉,如果公司就是线性增长的话,一个成熟的CTO,他的职责应该没有大的变化。比如说我们从100万单量增长到500万、1000万,一个成熟的CTO他的职责应该没有大的变化。 + +什么是线性增长?我解释一下啊,有两种,一种是规模化,一种是你做的业务差不多,只是尝试性的延伸。比如抖音现在做了无数尝试,上面小业务很多,但抖音一直没变,这个我认为也不会有技术层面的大变化。商业层面他可能要去思考,比如做抖音教育和做飞书可能是不一样的,所以这还取决于你的业务。 + +第二个阶段,就是另外一种情况,你要做的是一个全新的、创新的,甚至大家都没做过的东西,或者说是一个你自己也吃不准的业务,这个阶段我认为CTO的职责可能会有些变化。重点就不在技术选型那些东西,而是你要为3年后考虑,考虑一个新业务的发展。 + +这两个阶段的区别是,前一个是有迹可循的,但第二个阶段就是无迹可寻的。到了第三阶段就是CTO变成CEO,因为CTO下一步就是CEO,很少有CTO去做其他角色(转型产品负责人也是个选项),创业不算啊,所以我只讲两个阶段,我没经历过第三阶段,不好讲。 + +第二阶段我以王坚博士做阿里云为例,这件事是对他产生挑战的,因为他要说服马云,同时马云也得相信他。做阿里云对他来说就是一个全新的职责,阿里到今天为止,给本地生活给的时间还稍微长一点,给了超过3年时间,剩下可能只有对阿里云是给了超过1年时间做探索。 + +以前我记得的,或者我知道的,基本业务1年没达标的话,要么换人,要么砍业务,砍业务可能是有时候为了面子再撑一撑,但人一定是换掉的,不管你是什么级别,到合伙人也要换掉的。所以阿里经常组织架构调整,多岗轮换什么的就是这个原因,就是换人,当然阿里有这个底气可以去试错,是试人的错,不是试业务的错。 + +所以对于王坚博士来说这是很罕见的,这对他的考验是:一、他自己要坚信云计算一定是未来,这时候考验CTO的眼光,绝对不是什么技术选型这种。大家说CTO还有一个词叫技术战略,我认为是跟业务相关,你是要决定公司生死的,你现在是“O”,所有的“O”都是决定公司生死存亡的,否则你就是一个执行者,我认为我在这一点上,只做到了一部分,没有完全达到我所说的这个层面。 + +我理解的技术战略就是技术跟业务相结合的,云计算表面是一个技术,其实内核是个业务,因为有商业价值。这个不是大家说的那种纯技术战略,比如选一个三层架构还是微服务,选一个放之各种设备都能用的一套代码,这样的技术战略还是比较窄。 + +纯粹的技术商业价值有限,最多它的价值是节流,节省成本,但它不会开源,我说的商业价值一定是开源(不是“开源软件”的开源),要能给公司带来商业的收益。包括像饿了么这么烧钱的公司,我们一直也对节流不敏感,如果一家公司要对节流敏感,要么就是出了问题,要么就是它已经很稳定要做一些精细化或人员裁汰。 + +我前面说的总结一下就是,第一阶段是大家普遍认为的CTO要做技术战略、团队组织,包括把团队热情激发出来,技术按时按质量交付、保证稳定性;第二阶段你要思考几年后,这个技术怎么快速帮业务实现商业化、突破,技术要去坚定地相信这个业务或支持这个业务做好;到了第三阶段就是CTO变成CEO。 + +极客时间:如果从从业视角看,比如某个人要去应聘某个公司的CTO岗位,那要往他身上安几个职责,比如3个或4个职责之类,可以这样分吗?就像刚才你其实也有说过有组织的建设,有文化的建设之类。 + +张雪峰:这个要看公司那时的瓶颈,不同阶段和不同公司找你去做CTO的瓶颈可能不一样。饿了么当时团队更需要一个Mentor,还有那时候老宕机,要你去解决问题。当然不是每个人都要解决那么多事情的,可能有的公司就是技术影响力不够,想找个人装个门面,或者要上市了找一个CTO,这是几种不同阶段。 + +假设抛开这些不同阶段的考虑,我把职责不分优先级地列一下。第一个职责是建组织,甭管3个人还是30个人,你一定要去Build,这个Build可能是Rebuild,有可能是Merge(融合),有可能是水乳交融(更大可能是水火难容),这个是建立组织层面,这里当然包括人才的吸引,因为组织涵盖人才。 + +我认为饿了么已经接近水乳交融,百度外卖除外啊,因为在我离开的时候和百度外卖还没有完全水乳交融,我指的是原来饿了么老的团队和我进去之后的这个团队是水乳交融的状态。 + +第二个职责是做所谓的技术战略,说的细一点叫技术选型,比如大到云计算这样惊天动地的抉择,小到语言,你都要做决定。其实语言也不小,但我假设是从CEO的视角出发来看技术团队的各种选择的啊,比如语言其实对CEO来说是很不敏感或很难理解的,但做异地多活这件事对CEO是敏感的,因为要花大钱,要停3个月业务,所以目前我认为可以用一个方法来看技术战略的大小、价值,就是:站在CEO角度去看CTO应该要怎么做。 + +建组织和做技术战略这两个职责可能最重要。 + +以饿了么为例,在第一阶段,我要解决当时团队头疼的组织问题,另外就是解决稳定性问题,当然这其实只是技术层面一小块任务啦。我总结要解决跟纯技术相关的主要就几件事:安全、稳定、性能、可扩展(灵活性伸缩性等)、可管理(运维运营等)。 + +关于可管理,说人话就是,无论代码层面还是产品层面,要让你接班人读得懂、玩得转,你这个系统是可管理的,而不是动不动就要靠某一个人、靠一个英雄去搞定,而是可以传承的。 + +另外大家提到CTO的职责肯定要包括稳定性,因为稳定性太突出了,尤其对规模化来说,稳定性可能超过了安全性。但我个人认为安全性是超过稳定性的,在饿了么,以前没有太多安全性方面考量,我接到这个任务是因为要解决稳定性的问题,但我进去之后,其实安全方面是花了我同等的心血。这些东西是常规的、本职的工作。我和团队经常说一句话:没有安全性,你连宕机的机会都没有。 + +另外,关于组织也涉及到一堆事情,我刚没展开。比如说要提升团队活力,或者叫激情(仅靠激励不持久),让他们能够喜欢自己的这个岗位,包括喜欢你的角色、喜欢你做的事业,这个事还看两个层面,你是喜欢这个过程,还是喜欢结果。我个人更倾向于喜欢过程,因为你只有热爱这个事情之后,才能大概率得到一个好结果。 + +极客时间:总结下,就是一个组织,一个技术战略? + +张雪峰:其实就是技术的本职和组织的本职,你要做好。因为你是CTO,这三个字母拆开来看,“T”就相当于是在技术层面,不说你写代码最强,至少技术视野没问题,你要自己做过这些东西或至少探索过,这个T你一定要有。 + +“C”就是组织层面,你是技术组织里最重要的一号位,相当于出了事你要直接担责的,你要去更多地激发人。也不是说每个技术方面或领域都非常过硬、必须最强,但至少你自己不能不懂,不能太外行,回到本职就是“C”和“T”这两块。 + +“O”其实是CTO的第二阶段,公司的命运、公司的新业务是跟你休戚相关的,你并不只是执行。 + +但“O”比较难,“O”基本就是公司决策层(最高管理团队),我认为所有的“O”,至少在几个因素当中,它的权重要做到不能忽略不计那种。换句话说,你的团队和结果是要在公司的财务报表里体现或影响公司重大决策的。有很多团队和结果,是根本连上财务报表或影响公司重大决策的资格都没有。 + +极客时间:你刚刚还说到要让团队保证激情,喜欢自己做的事,那你怎么能保证大家都喜欢过程,或者结果呢? + +张雪峰:我刚说更倾向让大家享受过程嘛,前提是你把他安在合适岗位。不满足的时候只能去调配,组织架构调整只是最后一招,是组织临近腐化甚至癌变的被迫手段,能不组织架构调整就不调整,当然也不可能人人都满意。 + +我以前跟团队说,第一你要尽可能喜欢你做的事,你不喜欢做这件事你来找我,真的是这么说。我还跟大家说,如果你感觉在饿了么做技术(包括尝试内部转组后)和在其他公司做技术没啥大差别,哪怕业务相似度高达90%以上的美团外卖,那你真要考虑换个公司(换公司的另一个说法,就是炒老板鱿鱼),当然了,也可能是我和你Leader做得不到位。 + +极客时间:在CTO所有的工作中,你觉得什么事情是最耗费你的时间和精力的? + +张雪峰:协调。其实最耗费时间可能是开会,但是耗费精力、耗费脑力或者最让我头疼的还是协调。 + +极客时间:什么样的协调工作? + +张雪峰:我说的协调可能包括大家发牢骚,有些是个人的原因,有些是团队之间的矛盾,包括还有技术跟业务团队之间的协调。当然和业务团队有矛盾我是可以顶在前面,但是有时候是业务团队去投诉跟他对口的那个技术团队,这种情况我就很难办。 + +有一次业务团队说,雪峰,我这边缺人,你看你们中间件团队都已经很稳定了,异地多活都搞了,还要那么多人干嘛?后来其实我也有点被他们“绑架”,我就强行跟兰建刚说,我没法完全照顾你们感受,你们要出人去做业务。 + +后来建刚被我逼着去调人,建刚也是比较强势,但是他不会不征求意见,尤其是涉及到调人。征求意见的时候大家都不愿意去做业务。后来勉勉强强有个团队去做业务了。最后证明这次试验不成功,业务部门也不满意,被征调同学更不满意,他说我这辈子绝对不再做业务了,不管离不离职。 + +因为他们有技术洁癖嘛,但业务那边催得急,他想做得好很难的,就觉得都是些什么狗屁需求、追求代码洁癖有错吗、质量不过关宁可推迟上线等,反正就是各种不爽。 + +极客时间:这种抱怨如果有失偏颇了你会怎么处理? + +张雪峰:我更倾向于相信他。 + +极客时间:他说的这句话,如果让做业务的同学听到,别人可能会觉得很不舒服。 + +张雪峰:站在业务的角度这样想是对的,但是我很难去强扭、强迫他接受做业务更有价值。当然业务有同等价值,做业务很有意思,只是每个人想法不同。 + +中间件团队建立了最扎实的地基,但是他们最讨厌装修。尤其讨厌什么呢?装修也就算了,你让我做中国式装修,每家都要推翻前面的房东,这个他绝对受不了。如果让他统一样式,精装像美国那种房子他也能装,因为一行代码就可以搞定了,但让他天天搞这种又不是完全重复,但是思想又是重复的东西,他就觉得很无聊,就感觉这是对他技术的侮辱。 + +我面对的是这种情景,所以我也不强扭,我也不能因为他做这个不成功就惩罚他,因为他毕竟也是勉勉强强来做支援,他也没有说不负责任,还是把这事情干了的,只是说这辈子再也不做业务了。 + +除非面临的问题是公司业务技术团队全部离职,必须你中间件团队冲上去了,那没什么悬念,你不做就走人,但没到这个程度,因为组织大了还是有一定弹性。我还是尽可能尊重个人选择。 + +极客时间:在技术跟业务中间,你其实是一个缓冲区?因为一般的业务它总是要求快,对吧? + +张雪峰:对,但我也不是缓冲区,我就是顶包,这可能是我自认稍微帮兄弟们担起来的点,包括出大的故障我自己扛,或者技术跟业务有矛盾的时候。 + +当然我自己团队内部不存在顶不顶包的问题,我肯定要协调他们,不可能所有的事都我来担,也没必要,总是老大自己担责也是有问题的,你不能走到另一个极端,什么都老大担,那小弟就会感觉反正犯错没有成本,就更不注意了。 + +饿了么早年这样是OK的,那时候大家比较单纯,而且大家责任心非常强。但是当你团队扩大规模之后,一个是平均素质下降,二是责任心也会下降,这是正常的,这是人的本性。因为地盘就这么多,1800人做这么多模块,肯定是利益分配不均的。 + +其实程序员就关心我做的东西能不能跟同学去吹牛。今天你做了一个不成功的东西,和一个天天可以跟同学或老婆吹牛,说这功能是我开发的,这两个事情,对于人的成就感肯定不一样,我不可能让所有人都去做那种天天可以跟同学或老婆吹牛的事情,总要有人去地里面浇水的,去收割的毕竟少。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/07对程序员来说,自由价更高.md b/专栏/超级访谈:对话张雪峰/07对程序员来说,自由价更高.md new file mode 100644 index 0000000..0da9637 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/07对程序员来说,自由价更高.md @@ -0,0 +1,87 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 对程序员来说,自由价更高 + 极客时间:饿了么的技术文化是什么? + +张雪峰:用一个词来总结就是自由。自由到就是同学不爽,我可以让他换岗之类。但后面我很难做到了,因为没有那么多岗位可以让你随便调,要么就是让另一个岗位挪,要么就是我造出岗位,但这其实是一个很大的风险,因人设岗是可以的,要看什么人,你也不能经常用这种招数,绝大部分情况下,你还是要守正。 + +刚开始我可以让同学们吐槽,因为你迁就了这个同学,让他做了自己感兴趣的,有可能他到一个新团队,别人可能跟他配合不好,所以这个也需要平衡。刚开始我基本上尽可能满足同学们的诉求。 + +对程序员来说,自由真的是价更高,你要给他一定自由度。 + +极客时间:除了“技术自由度”,还有别的关键词吗? + +张雪峰:饿了么以前其实是有六个字来代表企业精神的,叫极致、激情、创新。因为我们本身推崇的就是极客文化,刚开始完全按这个来的,因为我们CEO Mark他特别Geek,他特别推崇扎克伯格,特别推崇键盘改变世界这种理念,所以我们都受他的影响。 + +技术团队我们当时也总结过一些词,我印象最深的就是开放、自由,我认为开放、自由,这就是我给程序员最大的两个词。 + +极客时间:前面我们聊收购百度外卖的时候有说过工程师的技术素养问题,工程师的基础能力是基本的,除此之外,大家肯定也要有一些其他的软技能,你觉得比较重要的软技能都有哪些?比如沟通能力算不算? + +张雪峰:沟通这个词比较泛,举个例子,比如说最简单的Presentation的能力,不管你晋升述职还是跟老板汇报,PPT是绕不过去的,虽然很多人讨厌这种方式,但你绕不过去,因为你要让别人知道你的想法,不可能上来都是Demo、代码,这不行的。一是写PPT,二是把它讲出来。 + +大部分的工程师都是受Linus Torvalds(林纳斯·托瓦兹,Linux之父)的影响,在不想沟通或者自己不擅长沟通、表达、分享的时候,就扔出来那句话,“Talk is cheap,Show me the code”。其实,这句话有他的语境,你看Linus的表达能力是很好的。所以,我和团队说,“Talk is important”。 + +加入阿里后,我也参加过几场晋升述职,去担任评委,人家PPT几乎没有写得烂的,水准都很高,但是讲得有差异。演讲的技巧非常重要,而且其实这项能力在外企是特别重要,因为在外企还要用不同的语言跟别人去沟通。 + +另外,写Email也是个技能,我有时候写Email都要反复措辞的,要发给谁,抄送给谁,还有密抄给谁,这种都需要小心的,这方面我统称为Presentation。你说的人与人沟通那属于情商,Presentation Skill是你要让对方明白,同时你要看懂对方要表达的意思。 + +但也有些人走火入魔,比如PPT一搞就搞100页,那个也不行,还是需要抽象能力。一般来说,程序员抽象能力都还可以的。 + +我再讲个小故事,饿了么曾经有一个小伙子,那哥们特别神奇,我很看好他,他晋升述职不用PPT,一上来就说,我本来准备了一个PPT,后来想想没必要,我就跟大家讲一讲,然后就开始脱稿讲。 + +当时上海的评委只有一票投反对,因为他确实讲得不错,不需要PPT他能讲清楚。但这次晋升答辩北京团队清一色全部投否决票,因为这是百度体系不能容忍的方式,你这是对评委的不尊重。最后结果是我求情让他过了,这个其实不太好,但因为我对他有些偏爱。我只是举个例子说做演讲这个事情。 + +极客时间:对一个人偏爱,会不会让其他人觉得不公平呢?你怎么看这事呢? + +张雪峰:肯定会有不公平议论,说实话,饿了么让我偏爱的同学比较多,有我一直在孜孜不倦准备的 CTO backup(接班人),还有就是让很多同学有“苦”说不出的我最偏爱团队 CI(Core Infrastructure)。 + +没办法,人都有自己偏好,纯上帝视角、无利益相关几乎没人能做到,更不要说持续做到,对我来说,尽量在偏好和公平之间取得一定平衡。比如:我虽然偏爱 CI 团队,但出了故障或 CI 被吐槽、投诉,我一般也是先追杀 CI(兰建刚/徐盎/刘焱等同学苦张雪峰久矣),被我挑战最严厉的一般也是 CI 团队,即使主责或 Root cause 不在 CI 团队。 + +又比如:我虽然偏爱石佳宁(中台技术团队负责人,二代 CTO 首选)、黄晓路(全局架构组负责人)、许红涛(商户开放平台技术负责人,三代 CTO 首选)等同学,但佳宁、晓路他们也是我在部门例会或各种群里 Challenge 最多的几位同学。 + +极客时间:你觉得技术团队的价值体现在哪里? + +张雪峰:我后来到阿里,行癫(阿里云总裁、前阿里巴巴集团CTO)跟我们开会说过几次,他说技术团队的价值是什么?我们脱口而出驱动业务,他说不可能,我今天明确跟你们说,不可能。 + +在他看来,技术只有两个核心价值。第一就是对于验证成功或接近成熟的业务,你要帮它做Scale,快速做规模化,不要线性。比如从1-10用了10天,那你从10-100应该只用两天或一天。 + +线性就是用工程师堆业务逻辑嘛,业务逻辑就是需要人的,虽然很多人说讨厌CRUD,但也没办法,业务逻辑人工智能是搞不定的。反而中间件是很容易做规模化,非线性的东西是难度高,但一本万利。中间件一开始投入很大,比如投入100多人,这些人都很贵,公司可能说凭什么要投资他们,又看不出交付了多少业务需求。但这样考虑是不对的,一旦上线,价值非常大。 + +第二,就是技术团队要帮业务团队快速试错。这个我是在饿了么深有体会的,Mark就总是说,你要上线了,我才知道公司这个业务行不行,能不能活下来,哪有时间让你搞什么架构。饿了么2009年第一版快速上线,绝对归功于 No Architecture,汪渊(Raymond)居功至伟。我曾和他聊过这事,如果当初饿了么找我做第一版,我绝对迅速搞(拖)垮公司,最简产品、唯快不破 vs. 完美架构、技术洁癖之间很难调和。饿了么那时候最重要的是速度,你也不要管什么战略不战略的,你就给我最快速度上线、试错。 + +饿了么创业就是靠试错试出来的,快速创新完全是基于快速试错的,现在对这个理念我是绝对的拥趸。那种瞬间虎躯一震、如有神助、不用试错就一炮打响的成功业务,用凤毛麟角来描述都算夸张了。 + +极客时间:“技术驱动业务”这句话在技术圈太常说了,很多人也是这么认知的,你现在对这个观点的看法呢? + +张雪峰:就像我刚才说的,行癫的视野是非常开阔的,我很佩服他,他说你今后不要再跟我说妄图去驱动业务,只有一种情况,像Google这样靠技术驱动纯线上业务的,才有资格去说驱动业务(今后如果无人车实现商业规模化,或许也算一种)。阿里又不是纯线上的,我们还有很多销售人员要去谈合作的,技术主要还是推进业务实现。 + +所以现在一看到技术驱动业务的文章,我确实也不怎么会去看,除非你做的就是技术产品,比如数据库之类的基础软件产品。所以他总结的这两条技术团队的核心价值还是很淳朴的。 + +极客时间:前面聊了很多,能感觉到你很在意同学们的职业成长,希望给大家自由度,或者说希望大家都能好好思考自己的职业生涯。 + +张雪峰:肯定,我希望大家尽可能喜欢自己做的事,在这个层面,我也有挺多反思。比如说,其实我们前端团队经历过动荡的。 + +有一段时间,我们所有的前端都是归Sofish(原饿了么前端负责人)一个人管的,C端、B端、物流都要做,但是后来出了问题。有人就说,这些做前端的家伙,整天去搞开源(其实有失偏颇),搞最新的那些东西,搞他们自己喜欢的那些东西。但对业务来说,我们希望进度再快一点,也希望他们能和我们一起加班,哪怕你在那磨洋工呢?人就是这个心理,不患寡而患不均。 + +因为我跟你一个团队,都是搞技术的,你说产品经理不加班还可以理解,咱们都搞技术,你不加班,是不是你工作量不饱和?Leader会去质疑你的成员,平级(Peer)也会质疑的。 + +但Sofish他不管这些,后来我说你面儿上也得过得去,让同学们在那边加加班。其实联调几乎都是后端的锅,前端他们做的质量确实非常好。Sofish就说,我干嘛要让他们非要待到8点以后?后来他这个团队是被我摁着加班的。 + +后来摁不住了,我就跟他说算了,你把你做物流的前端切出去,切到了物流的研发团队,就汇报给那边。我们早期其实除了C端,都是这样的,就是每一个产品的研发包括前端和后端都是在一个团队。后来是因为Sofish做得真的很棒,所以我希望他把所有前端都管起来。这个事儿可能我当时也有点认知偏差,其实这里面是有矛盾的。 + +比如对前端来说,你汇报给业务的研发团队,这样做业务交付,效率是最高的。但是对于个人成长,对自身的技术追求来说,其实是比较糟糕的,因为你没有同道中人啊,你跟后端没法交流。 + +如果前端大家都坐在一起,就会很有共同语言。平时还能搞一些技术分享会,那如果你去了业务团队,你位子要搬,就可能缺少这样的条件。当然这不是绝对,只是我的看法,有些团队前端跟后端在一起也非常好,他们内部也会搞一些小技术创新什么的。 + +但有些团队他们就感觉很失落,尤其物流团队的前端切出去之后,他们业务的交付速度是快了,但同学的职业生涯可能会有遗憾。就是我认为技术的职业生涯和你做交付的效率其实是有天然的冲突的。你很难做到让他技术有很大收获,又让他交付速度很快,因为交付速度很快证明你很难有时间学习,更何况大家不坐在一起。所以在这件事上我们走了一些弯路。 + +另外,Sofish他其实是比较纯粹的技术人,他的主要精力还是放在对前端技术的不断追求上面,比如说性能、框架、工具。因为前端有很多的东西需要做,比如他们用的框架、工具,很多是他们自己做的。反过来,他们对业务的关注可能就有点不够。 + +有时候我感觉,“拔苗”是可以拔的,但有些“拔苗”其实非常违背同学们本意。虽然没出什么大事,但下面大家的冲突并不少、吐槽更多,后来我就做了调整。所以,组织架构调整很难的,像阿里这么成熟的组织体系还经常调组织架构呢。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/0850X增长:管理35到1800人团队的难题.md b/专栏/超级访谈:对话张雪峰/0850X增长:管理35到1800人团队的难题.md new file mode 100644 index 0000000..b38bfa5 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/0850X增长:管理35到1800人团队的难题.md @@ -0,0 +1,113 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 50X增长:管理35到1800人团队的难题 + 极客时间:我看到网上的资料,你刚到饿了么的时候技术团队一共35人是吗? + +张雪峰:2014年9月还是10月是35人,就技术加UED(用户体验设计)。然后第二年春节后就70个人了,一下翻倍,这速度很快。 + +到2015年底大概就是五六百了,具体数字现在我记不住了。 + +极客时间:增长蛮快的。 + +张雪峰:那时候当然也是一步步上来的,但是确实招人太快了,我自己也要去面试一些人。 + +从我自己的感觉,组织管理这一块,我感觉还好,还能驾驭住,没有出现吃力。跟业务团队相处也还好,可能我的强项之一就是,组建一个团队,然后快速把大家的能力或能量激发出来。 + +极客时间:到2018年被阿里收购之前你的团队有多少人? + +张雪峰:1800多一点。不收购的话,其实我们本来计划要做优化了。因为以前饿了么从来不提末位,都是让大家自觉走人嘛,但是后来发现,人力成本很高。 + +但这个也要对比来看,其实人力成本也还好,饿了么人员的薪资这些加起来,占的成本(百分比)其实是不如很多企业的,大部分线上的企业,人力成本是很大的。但是你对比其他支出来看,像我们一天三四千万的补贴就出去了,所以人员成本反而还好。 + +极客时间:你是后来觉得人员成本也是一个比较大的支出了? + +张雪峰:因为我自己一开始也没经验,就是为了招人,也没有职级观念,只要感觉是个人才,他开价我就答应对吧?就进来。后来我们学阿里,开始搞组织体系、人才盘点的时候,我发现真的很心酸。 + +我以前不看他们薪资表的,虽然能看,但我从来不看,那时候全员拉齐我看到大家的薪资,老员工们那么优秀,不少都是一万出头一点,新员工进来就两三万,而且老员工能力还比大部分新员工强。 + +我们越到后面,到一千人的时候,或者就到2015年年底五六百人的时候,人才的平均水平其实也是掉了,组织越大越会掉。但是到了2016年、2017年,我认为人才水平是下降比较严重。但没办法,因为有很多业务要做。业务方和我讲了,机器人搞不定增删改查,说到底也不是增删改查本身,而是业务逻辑嘛。这个东西你很难去靠一个抽象的方式去弄,虽然这曾经也是我的技术理想之一。 + +因为很多业务上线一周可能就下线了,你没法抽象,你搞得抽象就容易陷入完美主义。到今天做软件,即使你有低代码也搞不定,低代码只是让不怎么懂编程的人也可以编程,但这个还是体力活,即使拽拉也是体力活。 + +极客时间:是的。但是人员的水平下降这事,你后来反思过吗?对企业来说,这是否是一个必然趋势?以及你有想过用什么办法提高大家的水平吗? + +张雪峰:我尝试过,但效果一般,整体趋势还是下滑。虽说 A Player 更倾向与 A Player 共事,但有几个制约因素:一个是公司、业务的扩张和变化,包括投融资、收购、被收购,以及因此而产生的被迫技术融合与改造,最典型就是我们遍历了国内最大的“三朵云”。 + +第二,Leader包括我自己本身的视野局限,不是不想招更好的人才,而是你看中的Better不一定就是真正Better。一种情况是这个候选人面试水平高于实战水平,还有一种常见情况是候选人确实是真正Better,但不一定适合你的团队或者看不上你的团队。这样的候选人也会导致团队平均水准下降或氛围变差,至少团队协作方面会有问题。我认为,强强组合如果不能形成合力,有些时候甚至针尖对麦芒的话,那还不如“强+一般”这种组合形成的战斗力。 + +我个人认为,只要企业到了一定规模,平均水准下降或者说职级通货膨胀似乎难以避免。但比这更可怕的,是团队活力、热情逐步下降,所谓各种“卷”的应运而生也没什么奇怪了。 + +我之前尝试过拆分团队的方案,类似企业拆分 SBU/BU/BG 独立运营,但后来发现核心业务即使拆分了也是强耦合,很难独立运营,更不用说技术团队独立运营了。小而美,确实是“看上去很美”的解决方案,但有个大前提,就是:能否最大限度独立或者说灵活运营?我举个例子:微软如果拆分为 Windows、Office(平台中立)、Azure(平台中立)三个独立公司或团队,还是有可能的,但对饿了么当时业务来说,非核心业务可以拆分,核心业务没有这种可能性,因为就是强耦合,牵一发动全身。 + +极客时间:对于业务来说,资源永远是不够的。这是所有公司的常态。 + +张雪峰:有些时候我们要故意冗余,多招人,因为这个增删改查是绕不过去的。我现在发现几乎所有企业,就是做纯C端业务系统的,大家管程序员叫“码农”,真的有道理,确实以前码农还是一个有一点小得意的说法,还有点自鸣得意的感觉,但现在码农就是“农民”,绝大部分,相当于现在就是一个不在工地堆砖的人,真的很无聊,但没办法。 + +极客时间:3年的时间,团队从几十人一直到1800人,这些人你们怎么招进来的? + +张雪峰:坑蒙拐骗(笑)。我们其实要感谢校长(这里指:极客邦科技创始人Kevin)这边。我们技术文化这块一开始顾不上,2015年我们就是解决宕机问题,到2016年开始感觉要搞搞技术文化,因为要吸引人才嘛。 + +后来我们就想试试看能不能参加你们大会(QCon、ArchSummit等),其实你们还是很帮忙,像我们这种“屌丝”也能上大会去吹牛了(笑)。你们可能是看中饿了么业务还可以。 + +其实我们饿了么的业务团队,就销售团队,我觉得全国可以排进前五名。但是我们技术团队,就互联网这一块,就算吹牛最多也只能排进全国前二十名,不吹牛可能更靠后一些。所以我们技术团队跟业务团队完全不匹配,后来是吸引到很多人才,慢慢拉近距离。 + +对我们当时这样的团队来说,去大会真的是帮到大忙,稍微吹一吹牛,就能吸引到一些过去招聘渠道覆盖不到的人才。当然了,顶尖人才肯定不行,还得靠自己去聊,去“画饼”。 + +2016年,我们筹备搞异地研发中心,那时候Mark心气很高,他说硅谷要不要搞一个,你自己去Build。我跟他说,硅谷现在还不具备条件,我们在美国没什么影响力,就在北京搞了一个研发中心,先试试水。我说能把北京的搞好,已经很不错了。饿了么的名气主要在上海,虽然北京市场也还可以,但是技术毫无名气,所以后来就先从北京开始做,北京有人了就啥都好办。这个人才只要他有一技之长,就可以去宣导宣导。 + +我们那时候有PMO团队(Project Management Office,项目管理办公室),还帮忙搞Meetup,他们谈合作能谈到免费的场地,我觉得蛮牛逼的,我们就出去搞这种Meetup,在北京也搞过好几场。 + +后来史海峰(原饿了么北京研发中心第二任老大)进来之后,局面就不一样了。海峰很厉害,他来了之后,和PMO在北京搞的Meetup就多起来了。海峰没进来之前,我们在北京搞得比较吃力,那时候我们还没收购百度外卖。 + +饿了么那时候是特别需要这种宣传,但好在我感觉也没有太过火,略有一点包装,至少也达到我们的初衷了,慢慢吸引到人才。 + +那时候我们也只能吸引国内的一些人,但我们对BAT的人才吸引力还是相对差一点,像百度的人是不会考虑来饿了么的。 + +到后来我们有了一定名气了,为啥呢?因为那个时候腾讯云、阿里云都进来了,我们被这个投资,被那个注资。对应的,饿了么也是几朵大云的 Big Show Case,虽然很折腾,但有一个好处,至少证明你这家公司有价值,别人愿意来投你,就这样海外的人也都知道饿了么。 + +那时候海外有两拨人,一拨人就是想回国发展的,还有一波人说雪峰,你们卖不卖代码?把饿了么卖到比利时,卖到荷兰,我说代码是不卖的,今后我们可以考虑做一个SaaS云帮你们,因为欧洲的需求都很简单,他们吃的东西本来就很简单,哪有中餐这么复杂。 + +到了2017年,我们高阶的人才开始变多,加入不少牛人,比如Jason(张浩,现货拉拉CTO)是滴滴研究院五虎将之一,以前在Microsoft、LinkedIn、Uber;Alex(王胤)从Facebook过来,算是Facebook华人中级别还可以的大牛;Kay(范晓锋),以前在Microsoft、PayPal、蚂蚁(P9)。他们原来都在硅谷或西雅图,大部分薪资比我高不少,我们当时真的是不惜一切代价挖牛人。2017年加入这批人,对我们的技术品牌影响力非常大。 + +然后最牛的就是何田教授,他是ACM Fellow(注:在计算机领域,仅次于图灵奖),ACM就是发图灵奖的那个机构。何教授的这个ACM Fellow,据我了解,也是阿里 + 蚂蚁两大集团史上第一个。何教授来了之后,一下子又吸引一堆人。我们以前只能吸引工程界的,包括从Facebook、LinkedIn、滴滴、百度过来,但是科学界或学术界的人很难吸引到。有了这些牛人加入,我们的AI、IoT甚至小范围科研实力也上来了。 + +极客时间:人才吸引人才嘛。 + +张雪峰:得人才者得天下,所以你一定要找到一些人才,或者说牛人。当然我也有过一些人才选用上的不当,但整体我认为还算顺畅。至少从组织层面我认为还可以,包括后面跟口碑的磨合,虽然磨了一两年,但最终还是和平交接了。 + +极客时间:你之前说到喜欢过程大于结果,一般会有什么样的手段或者方法,去激活一个1800人的组织,让大多数的人去享受它的过程? + +张雪峰:1800人的时候我已经无能为力了,不是无能为力驾驭这个数量级的组织,而是无能为力让这个数量级的组织中的大多数人满意。以我这样的阅历、经历和驾驭能力,1800人的团队,我认为很难做到让大多数人Enjoy这个过程,或者说让每个人选自己的兴趣,这不可能的,业务还做不做呢? + +因为五六百人的时候我就很难管到一线了,很多人都不认识,但只要我听说过名字的,或者知道他曾经做出过一点事情的,我尽可能去关注。有人说干得郁闷,那我就跟他说你到底要什么,抛开钱不到位,郁闷就几种,要么就是他还喜欢这个工作,只不过周围的环境氛围出了问题;要么就是感觉干这个活已经很无趣,主要就这两类。我就跟他们谈,你想去哪儿,我尽可能满足。你想做什么随你挑。 + +但是我其实挽留住的少,最后还是留不住的多。所以尽可能还是防患,就跟稳定性建设一样,到了救火的时候,我救下一两个,但是还不如我去做预防,做预防我一个人不行,这个需要组织,你要让大家感觉在这做技术是不一样的。所以,从人的角度需要组织,因为到毛细血管不是我能覆盖到的了,到1800人连HR都Cover不了。 + +那时候(这里指2017年)我们就在楼下喝茶谈心,我下午茶就是那时候喝出来的,我其实不怎么喜欢喝下午茶的,但是大家有郁闷找我,然后就聊。对于我认为还值得留的人,我很直白,就是说随你挑,我这边虽然说比不上BAT,但也是五脏俱全。 + +有些同学他说做业务研发烦了,想去搞机器学习,那我说你去吧。我会跟他们说得很直白,很简单,你想搞机器学习,正常情况到外面连面试资格都没有,更不要说面试通过。在我这至少是平行调过去,我可以跟那边打招呼,让你有一段时间的学习期还照发工资,还不降级的,当然除了你犯了过错。说的功利一点,这也是一个吸引手段,只要他对这还有兴趣。 + +极客时间:你下面会有几个人直接向你汇报? + +张雪峰:最后收购之前大概9个、10个,最多的时候大概15个,正常状态也就大概9个、10个的样子,我们组织也经常动。我刚进去的时候虽然团队人不多,但汇报不少,但后来发现还是要靠组织力量。 + +极客时间:直线汇报多少个人,你感觉是一个比较舒服,或者合适的状态? + +张雪峰:不同时期心态不一样,如果以我离开时候的心态来说,我体会更深刻一点。比如像1800人这样的组织,大概100个人配一个直接汇报,平均一下大概是15~18个,15个左右可能合适。 + +因为我们当时1800人,其实汇报的只有那么几个,导致我后来开周会的时候,会发现这个人说不清,必须把他的直接汇报里面有一部分我认为重要的人也要拉进来才行。 + +一开始我想汇报的人少一点,几个大业务的Leader,但后来发现不行,汇报人少了你也会懒惰的。我现在感觉就是100~150人之间配一个直接汇报,这是平均的结果,有些岗位不太一样。 + +但只要汇报的人数多,肯定是有问题的,你要么去抽象业务,要么就是抽象组织,抽象能力是很重要的。 + +我自己现在钻研数学和历史带给我最大的收获,归根结底还是一个抽象能力,人类进步都是这样,螺旋式上升就是抽象,我讲过,你其实还是那么回事,但是抽象层次高,能适应的范围就更广。以前大家说到抽象就是架构师的责任,现在不是,现在我认为是技术人员一个基本能力。 + +反正我感觉就是100~150人,差不多这样的规模,以我的能力和视野,差不多可能合适,或者100~200人也无所谓,没有本质区别。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/09如何用人?.md b/专栏/超级访谈:对话张雪峰/09如何用人?.md new file mode 100644 index 0000000..3c88f78 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/09如何用人?.md @@ -0,0 +1,51 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 如何用人? + 极客时间:你有开除过人吗? + +张雪峰:有,如果说劝退都算开除的话。 + +极客时间:最开始的时候,会觉得很难吗? + +张雪峰:还好,我开始主要是考虑到汪渊的感受,对老员工从来没有开除过。 + +我进去之后半年才决定要让一位老员工离开,你也懂什么意思对吧?我问汪渊意见,因为包括其他老员工对这个人都有投诉,但这个人有汗马功劳,然后不听任何人建议。我说这个事情怎么办,很多项目尤其涉及到跨团队项目很难搞,汪渊说你别说了,明天就让他走人,他说你别顾及我感受。后来是汪渊亲自出面,去跟这个同学谈,当天下午就办手续。 + +我当时来的时候是希望两个团队不要出现摩擦,因为饿了么经不起大的组织折腾,当时现状就是:极速扩张、竞争激烈、频繁宕机、组织薄弱、融资不断等,我加入饿了么前那个夏天,老饿了么都经历过那段不堪回首的岁月,订单还没到日均百万,基本两天一宕机,我怎么敢随意折腾组织呢?你还得依赖老员工熟悉业务逻辑。 + +我自己当时在这件事上有点吃不准,虽然汪渊绝对信任我,印象中从我加入到离开,我提出的稍微大一点的建议,他几乎从没拒绝过,但我还是担心会触动这些老同学神经。即使过了一段时间,我还担心老员工会有反弹,但后来事实证明还好,大家比较接受我。 + +汪渊对我是完全信任,给我的权限非常大,无条件支持我,比如说后来搞定级,他说老员工你要降级就降级(实际并没出现这种Case),不要考虑我。 + +极客时间:我看过一篇文章,应该大概是2015年、2016年的时候,张旭豪好像更赞成从内部去培养一些管理者。 + +张雪峰:对,我们就那时候做的,后来发现有些培养不出来。比如人工智能我们就培养不出来,因为我自己不是这个专业的,我只是有兴趣,还是要专业人做专业事。还有就是大数据方向是有一个人,但人才还是不够。 + +极客时间:所以那个时候挖人就是能快速解决问题的办法。 + +张雪峰:对,当然也有一些推荐,也要聊。但是越是高阶的聊得越少,因为这种人你只能赌了,你不可能通过面试面出来的。先不说人家是不是有面试技巧,人家那个专业,就是你不擅长的领域,这时候真有点赌。 + +极客时间:随着饿了么越来越大,你会觉得挖人越来越简单吗? + +张雪峰:不简单,甚至我感觉可能难度更大一点。因为公司越来越大,你要招的人层次就高了,我认为这个东西还是线性的,甚至更难。因为你大了之后,美团外卖也在大,人家更能吸引人。 + +当年你可以不计代价的开Offer,到1800人的时候你就不能轻易打破薪酬体系了,老是破例也不行。而且到了1800人,比如某个岗位最开始没人,你招一个过来,后面你再招一个高阶过来,他们之间怎么汇报?很多高阶的人,或者说牛人在一起,尿不到一块,即使尿在一块,也很难做出好成绩。 + +极客时间:这种情况就是1+1没法大于2。 + +张雪峰:对,有时候反而就是一个很强的搭一个中等的,或者搭一堆普通的,反而能做得好。这个试错我都试过,强强组合,发现强强不行,强强都有个性,都想自由或者说自主,越强越这样。 + +极客时间:如果有人要离职,你会劝别人留下吗?怎么劝? + +张雪峰:会劝,但是往往也留不住。有一度大家都去拼多多找工作,拼多多离我们很近,都不用装模作样请假去的,直接中午出去就可以面试了,我们内部还成立一个小组叫“打拼组”,打拼多多的挖人组(笑),但其实这种情况,对要跳槽的人来说,在这已经没有动力了,即使我不鼓励他跳槽,他自己也会想去跳。 + +我跟他们说你在相似度这么高的业务中,如果没有发现Difference,那就再考虑考虑。我特别在意Difference,当然你根基要打牢,这叫守正出奇。“正”也就是基本功还是要有的,但要保证持续的热情还是要有一些催化剂,也就是Difference,催化剂是你一定要去思考的问题,这是我一直给他们说的话。 + +极端一点讲,如果你在一家公司做技术,跟同质的公司没有任何区别,可能这个角色不适合你。要么是你出了问题,要么公司没法Offer你,总有一个。当然,公司毕竟是商业组织,也不会给你那么尽善尽美,所以你还得信任公司。你要去努力找到让自己兴奋的点。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/10管理的不足:不团建,管理不到毛细血管.md b/专栏/超级访谈:对话张雪峰/10管理的不足:不团建,管理不到毛细血管.md new file mode 100644 index 0000000..e0148ad --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/10管理的不足:不团建,管理不到毛细血管.md @@ -0,0 +1,115 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 管理的不足:不团建,管理不到毛细血管 + 极客时间:你在饿了么典型的一天大概是什么样的?工作的节奏是怎么样的? + +张雪峰:我还是只讲被收购前。不算周一管理例会、周五Program(技术改造)例会这种特殊日子,常规的就是到公司,首先看业务的报表,看看业务的数据。 + +研发效能坦率说我是不看的,因为这个东西不是靠几张报表能看出来的,以前有所谓人均代码量、人均提交次数等,后来发现这很无聊,又搞什么交付时间,这也是被拼多多和抖音逼的,所以我个人不太喜欢总盯着研发效率搞KPI。其实讲研发效率就是看你自己的承诺,你自己对自己能力有一个估算,只要工作过几年,估算一个任务几天能做完,大致都能算出来的。 + +然后第二步就是看邮件,判断要回的邮件,我Email还是蛮多的,当然有一些是杂音,但杂音我已经自动过滤掉了,有一些发布计划我是要看的。 + +还有一点我不知道算做得好还是不好,我们其实并不要求日报,但是PMO很负责任,他们会给我发各种项目(业务交付、技术改造等)日报,会提出各种风险,他们不是暗的,不是东厂,他们就是明的锦衣卫,就是来督促大家的。比如昨天发布有什么问题让我看一下,项目的各种出错情况,有些是大项目、大节点,有些是常规的,我就看看邮件里面这一部分。 + +我的Peer(平级)和我的上级(CEO),他们找我都是直接电话,我们几乎不用邮件,我们要邮件就变成外企风格了,所以几乎不用邮件。我个人认为,邮件主要就三个作用:一、通知;二、存档;三、文明吵架。有了钉钉后,文明吵架也可以退出邮件舞台了。 + +另外,到1800人的时候,我的面试相对就少了,早期其实面试是我一天比较重要的工作,虽然不一定每次都能到我这,但还是有相当的面试量。 + +极客时间:早期是2015年吗? + +张雪峰:对,就刚进去嘛。后来从国外请了一些高手过来之后,他们面过的我就要面一下,只要到了他们这一级的我都会看一下。 + +被收购前其实我的面试量已经不大了,反而是收购完之后要重新调组织结构什么的,那时候也有些面试,但至少被收购前面试不是主要占用我时间的工作。刚说的这些工作差不多就要搞到下午了,上午是搞不完的,因为光项目日报和提出的问题风险,我就要看好一会,业务数据我基本心里有数。 + +其实还有一个常规的事情,饿了么基本上每天或多或少有一些告警,是真的有异常,但是它不是故障,我就要看一下。 + +从我加入饿了么第一天起到离开,我还是一直放不下这件事,其实按道理讲,这不应该是一个典型CTO在团队到这种规模的时候的一个常态,但是没办法,因为我们的迁移改造,包括百度外卖的PHP没有全部迁完,其实被收购前我还没有完全搞完。 + +除了这些,另外就是参加公司的会议,也就是例会。一般业务找我开会我不去的,之前业务他们开会我也去过几次,就是念PPT,会议一开就4个小时,轮番汇报。到后面团队越来越大,组织也有点臃肿,甚至有点腐化,所以后来我就不参加单个业务的会了,除非他们出了状况,出了状况我们一般私了。 + +我一直Follow不知道谁说过的一个原则:大事小会,后面我再补一句:小事钉钉,最好没事。做到极致就是当我这个角色不存在。当然了,有些会也不得不去,比如:技术准备和业务Battle,当然更多时候是和产品“吵架”,他们会拉我去站台;又比如:技术同学有个Idea,希望布道全公司,拉我到发布会现场伪装成CTO明令要求等等。 + +回到那句话“大事小会”,其实在大会上吐两句槽的一般我都是忽略的,不是特别重要,真的出事,没时间让你开会吐槽的。还有一种会,类似上面提到的,他们拉着我的虎皮去参加一些会,让我去现场压某个团队,或者要发起一个事情,需要我在现场,不一定要说话,就是要听一下汇报。 + +另外还有周会,我这边也是开的,但就我自己印象,除了第一次开会主要say hello,之后到我离开前最后一次周会,我从没搞过一次“各团队例行汇报”(注:疫情期间远程周会汇报各团队安然无恙除外)。我们都是这种模式:第一步,提前征集议题,一半以上是我提的;第二步,未结议题跟进或关闭;第三步,新议题讨论和决议,决议不了的,专人跟进;第四步,散会,不到点也散会,或者大家说点八卦。 + +其实我不喜欢开会,也不太喜欢听汇报,这种事我还是喜欢通过Email或钉钉,就是事务性工作尽量不开会,所以到了1800人的时候,很多员工只知道我,但从来没见过我。 + +我最后去办离职的时候,那位 IT 同学跟我说“我第一次见您的面”,我感觉很惭愧,我说今后反正微信上有你,到时候联系。我跟很多同事都是这样,到了那个规模,对基层的关怀可能真是少了一点,覆盖一线几乎不可能。 + +我盯一线只有一种可能,就是出故障的时候,我盯着工程师,但我不会去罚工程师。我们所有的惩罚,都是对至少是闹出故障的工程师的Leader,然后一直到VP,或到我的D(Direct Reports,指直接汇报给张雪峰的人)。 + +阿里进来之后,我就不只在故障的时候跟一线工程师有联系了,因为阿里经常会搞圆桌。我在阿里最后1年,圆桌会议开了无数次,在北京、上海都开过。 + +其实到1800人的时候,我晚上回家基本可以在8点左右,有时候更早一点。如果这一天没有什么特别的事,我也不会去发起一些会议或钉钉群,我就看看技术资料,比如说写点小程序,因为那时候毕竟在其位,需要了解一下前沿。 + +当然这一天工作中会有穿插的就是同学找我聊天,尤其在被收购前,大家都很紧张、很慌。虽然我自己也吃不准我的命运。但是我比较笃定,即使我感觉需要我让位,那我就直接让位,人就是这样,你有预期了,再碰到就不会惊慌,人的惊慌或者惊喜都来自于你根本没料想到,比如我在第三家公司时的那次晋升通过。所以那段时间,我的主要工作之一就是安抚大家。 + +极客时间:所以还是有非常多沟通的事情,是吧? + +张雪峰:对,但很少到毛细血管,这是我一个做得不足的地方,我当时没想到圆桌这样的形式。我在公司收购百度外卖那段时间,因为要安抚北京团队情绪,经常开All Hands,我在北京开的最多的就是All Hands。有时候下面部门会议,会搞些花样,比如月度新星这种活动,找我去颁个奖什么的,但这不算常态。从本质上说,我这人不喜欢暴露在大庭广众之下,很多时候没办法,被逼的。 + +极客时间:在阿里搞圆桌主要是什么样的形式? + +张雪峰:圆桌是这样的,HR会收集问题放在一个Excel表里,都是大家关心的问题,有些是普遍的,有些是个例的,就是让大家吐槽。我估计是在吐槽的同学里面筛选一部分,然后来跟我圆桌。 + +刚开始还比较机械,大家做个小游戏,热热身。当然目的很简单,就是让我“出丑”,这个出丑是比较温馨的出丑,绝不是网传破冰那种。就是调戏我一下,然后活跃一下大家的气氛,不要那么拘束,后面就是按部就班,开始念Excel里的问题。 + +其实圆桌开得多了之后,就感觉大家也有点提不起劲。因为啥呢?很简单,相似的问题或者我已经知道的问题,我要是之前解决不了,那还是解决不了。我也不能跟大家说,我承诺你几个月能搞定,要是我能解决的事,当场就拍了。所以后来发现圆桌会议,我也就是做一个倾听者角色,让大家知道我愿意听你发牢骚。 + +极客时间:所以能倾听,在管理中也是很重要的。 + +张雪峰:对,我在工作中特别善于做一个倾听者,甚至可能不亚于HR。其实员工找我也就是吐槽、抱怨,除了不方便直接说给我升职加薪,就是抱怨不爽嘛,包括晋升失败之后的不爽等等。 + +极客时间:还有什么是你觉得自己做得不好的地方吗? + +张雪峰:还有一点我做的不太好,这是有人给我吐槽过的。好几个同学离职的时候都跟我说,“雪峰,有一点我们认为你是做得很差的,就是不搞团建”,我离职之前,还有人跟我说这个事。 + +我确实不搞团建,都是单独吃饭的。我搞团建其实很勉强,都是HR去张罗着、劝着我搞,我们也很少去外面的风景区。阿里特别喜欢搞团建,我不喜欢。 + +我后来反思,这可能跟我性格有关。你别看我现在滔滔不绝,讲话也算利索,抽象也算到位,其实人一多,我还是比较紧张的,虽然到不了社交恐惧程度,但是我确实不太喜欢社交,哪怕是网上社交我也不太喜欢。所以,后来我几乎退掉了所有微信群,可能是我这个人的性格问题。 + +包括我去百度外卖开All Hands都是硬着头皮上的,其实我是很紧张的。有时候参加一些新闻发布会,有聚光灯的话我更紧张。所以这可能是我不太想做的事,我更喜欢的是单独聊,但是你管组织就没有办法。 + +极客时间:不搞团建是说你不参与大家团建,还是说你不鼓励团建呢? + +张雪峰:我没有不鼓励,就是我自己不搞团建。大家可以说这是我的一个问题,但现在回到过去,我还是不会搞。因为你搞什么呢?要么旅游一场,要么做游戏,但更多的估计还是围着领导转,这样除领导外都不爽,非我所欲也。 + +我们就搞过一种团建,是HR劝着我搞,叫裸心会,但后来我觉得蛮有价值的,基本都要到凌晨结束。裸心会就是开诚布公,大家甚至在裸心会上对骂,当然是文明的骂,就是平时有不满,有抱怨就说出来,然后最后一个环节是吐槽技术团队、吐槽张雪峰,说这个组织有多么烂,张雪峰做得最烂的一件事是什么,都要提。 + +极客时间:裸心会上大家都在吐槽什么? + +张雪峰:反正各种各样都有吧,当然有些是照顾我面子,但整体我感觉从裸心会来看呢,大家可以给我打70分。 + +极客时间:但其实是到了阿里才会有这样的东西是吧? + +张雪峰:是,到了阿里才有。那次开裸心会,是阿里收购了我们,但是还没有开始融合的时候。因为昆阳有一段时间是保持不变的,他还要在业务上解决两个组织融合的问题,技术融合还是第二位的。 + +当时搞裸心会,我们到上海淀山湖旁边搞一些节目,他们要我也做节目,我坚决不干,我说你们可以善意地“羞辱”我,但不要通过搞节目显示我菜。这个谈不上我的底线啊,如果大家非要坚持,我也只能硬着头皮上,反正裸心会上就各种平时的抱怨,对公司的抱怨,对技术组织的抱怨,对我的抱怨。这很好,完全达到了预期目标。当然了,最后还是要去解决裸心会暴露出来的一些可以有效解决的问题,否则就纯粹是发泄大会了。 + +极客时间:像裸心会这样的活动,你觉得对于团队融合是很有用的吗? + +张雪峰:有价值的,相当于是一个发泄的渠道,这个比倾听更重要。 + +极客时间:回到刚才说不搞团建这事,我很好奇,为什么你会对团建旅行很抵触呢?你觉得旅行对一个团队融合来说,意义不大吗? + +张雪峰:还是像刚才说的,除领导外都不爽,非我所欲也。我在携程倒是搞过团建,就是大家一起去旅行。我也不会“夹带私货”,给大家讲任何PPT,就是聊聊天或者玩一玩,纯粹的旅游放松,但我感觉我还是会破坏这个氛围。 + +吃饭的时候,不管怎么样大家总会拘束的,当然说难听一点也有来奉承你什么的,也有让你讲一些段子的,但总体我是感觉大家比较拘束,因为我也说了,这个组织的特点,大部分是比较年轻,我即使能跟90后保持沟通,但是在私下非工作层面,我感觉还是有代沟的,所以我是感觉自己会成为瓶颈,阻碍了大家的兴趣。宁做孤独的灯塔,不做惹人厌的灯泡。 + +他们下面经常团建,会拍个照回来给我看,都很快乐的。搞气氛我认为我是比较糟糕的,但同学们不吐槽我这点,可能他们认为这不是CTO主要的工作,但我认为这也是蛮重要的。 + +因为我之前拆解了CTO,“T”这层面我认为我还可以,“C”组建团队还可以,挖掘人才也凑合,技术文化也还说得过去,但是在促进大家更深度的情感融合方面,这个是指他们之间,不是跟我之间啊,我感觉差了很多。 + +因为我要开团建会,更多不是要我去跟我的直接汇报线沟通感情,跟他们沟通感情我每天喝一次咖啡就行了,更多的是促进他们互相之间的了解。 + +有一次他们说,峰哥,我们今年团建费比较多(公司官方团建费很少,主要靠会议迟到赞助费),要不去泰国搞一搞?后来我也没同意,我说你们要去日本,要去泰国自己去,但这些钱就不够分了,因为他要分到他的团队。我说一定是按人头分,你别跟我要多少钱,没钱。 + +其实饿了么以前团建费是比较少的,除非是搞一些特殊活动,我会去向Mark申请。搞团建、促进感情方面我投入不多,但是我们在技术文化方面,是很舍得投入的,你看我们Hackathon其实每年都要花很多钱,这也是吸引人才嘛。我们第一年就是全部大学生参赛,内部员工不允许去参加的,那次我感觉大家被带起劲儿来了。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/11CTO要给自己找backup.md b/专栏/超级访谈:对话张雪峰/11CTO要给自己找backup.md new file mode 100644 index 0000000..a65baae --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/11CTO要给自己找backup.md @@ -0,0 +1,61 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 CTO要给自己找backup + 极客时间:我听到很多人都说你会特别Care找替身,找backup这事。 + +张雪峰:先说一点:backup这事,指的不是替身,而是替换或超越。首先,我自己必须以身作则,否则自己老在这位置上不挪窝是不行的,这样哪有后浪出头的机会?说到招人,你首先要招自己的backup,先别去招你汇报线,别去想要蚕食多少业务,虽然大家都想把地盘做大。 + +这是管理层的职责,很多人是抵触,甚至明知道老板在暗示他找backup,他是坚决抵触的,就感觉自己位子不稳了。 + +我跟HR规划过我的backup,我也跟几个同事说过几次,当然不是跟所有人都说,怕引起大家的不满。我自己第一级的backup,其实并不是我认为最有潜力,或者最完美的,反而是在职级稍微低一点的中层,在这一层的年轻人,包括以前从交大出来的老员工,我反而是最看重的。还有就是第三层,更年轻的、接近95后也有我特别看重的。 + +我的这些backup里面,第一级backup说白了还是老气了一点,他们的能力、视野是够的,影响力也够,但是冲劲不足。第二梯队呢石佳宁(离开前负责中台技术团队)是首选,其实我是把石佳宁作为接班人培养的,他有这个综合能力,而且饿了么的业务他几乎都做过,除了APP没做过,物流没直接做过,但他和物流交互很多,他也很熟那一块。 + +再下面一层级就是工程师上面的直线Leader,一线Leader里面也有我特别看好的,接近95后的,可惜不少去了拼多多。这一梯队中,首选就是许红涛(先后负责商户开放平台、企业订餐),后来我推荐他去一家朋友的独角兽公司,朋友是圈内老炮,眼界非常高,但自从和红涛聊过一次,就特别欣赏他,虽然之后知道红涛想创业,但直至今日还给他留着位置,期待红涛回心转意,哎,我都不知道自己更希望红涛创业成功呢还是不成功(笑)? + +极客时间:你选backup的标准是什么? + +张雪峰:人品这些基本的我先不谈,我首看的是潜力。第二个看是否在业务做过,这很重要,也就是说你今后做CTO一定要在业务线做过,当然懂一点基础设施更好。因为我自己职业生涯是先做业务,再回过去搞基础的东西。 + +极客时间:你认为什么是有潜力? + +张雪峰:我认为的潜力(不等于天赋、能力、努力),就是“拔苗”试一试,假如你给一个人更高的职级,或者给了更大的Scope(领域),他最后搞定了,那就是有潜力。虽然大家都说看人要准、要有一定前瞻,但这个准和前瞻很难,除了受个人喜好甚至偏爱的影响,还有是否 Match 的更大挑战。这个类似幸存者偏差,大家往往很难理解为何常规认为的 Better/Best 输给了 Worse/Worst,适者生存、物竞天择才是这个世界不变的规律。 + +当然我说的“拔苗”有一点冒险,因为你只能试。我们其实也试过很多同学,就是拔一下,如果做成了,那晋升就水到渠成。 + +但也有另一种情况,有些同学可能我开始感觉他不是那么有潜力,但是有别的事情证明他有潜力,比如说P7岗位要晋升P8,要待两年才有资格,但有同学入职半年就提名了,最后还晋升成功,说明可能是我们没看到这颗“金子”,这种情况就是通过不常规的晋升看到一个人有潜力。 + +正常晋升就是干两年正常晋升,再干三年再晋升,这种就是Normal,这种我认为不能叫有潜力。 + +所以我说的能做backup不是按部就班过来的,而真的是我们拔了一下,结果他不负我们所望,还有一种就是你开始看走眼,但晋升成功的,这两类都是有潜力。但按部就班的我认为不是有潜力的。可能不够抽象,但我就是这么来看且实践的。 + +极客时间:backup必须要设定几层吗?我可以这么理解吗? + +张雪峰:也不是,我永远是看潜力。就是说第一位是看潜力,第二是要做过业务,基本这两点过了,其他什么技术、视野,这些都可以锻炼的,因为有潜力,我相信他就有视野,我也不看他什么背景。 + +潜力的表现还有一个是你要笃定,你自己要自信,然后眼光要准,你愿意跟这家公司走下去,不是说因为感情的原因,而是你看好这家公司,有眼光也是体现潜力的一种。眼光不一定CEO才有,有些人职级很高,但他没有眼光,就是守着这一块地,所以潜力也包括眼光。很多冉冉之星都是从基层做起来的,所以,做过业务很重要,要经过锻炼甚至磨练。在饿了么技术团队,我印象中,没有比石佳宁受过更多业务磨炼的同学了。 + +其实我不太建议校招生直接去做中间件这些东西,或者做工程算法(AI算法除外),他们还是要去业务团队先磨练一下。 + +极客时间:刚你说到接近95后的backup,你会不会觉得95后或者更年轻的同学,虽然有潜力,但是可能不够沉稳,或者说比较急躁呢? + +张雪峰:还好,还是要看人。我规划的第二、第三代backup,都是比较沉稳的,因为第一、他们经过业务线的考验;二、他们有潜力;三、情商也还可以。沉稳不沉稳这个跟职级无关,跟年龄是有一定关系,但过于沉稳可能就少了锐气,我认为人大概率到了40岁以后,确实锐气锐减,有锐气的很少,创业除外。 + +像左耳朵耗子是一个,还有涛思数据的陶建辉,我本来想请他来给同学们做讲座,讲一讲50岁还能做程序员的话题,为了让大家对饿了么能够更有黏度。但他们这种人很少,大部分到了40岁,雄心壮志就少了。要么就是还房贷苟着,要么就是自己出来再冲一把,因为到45岁你基本没机会了,到65岁就要退休了,这一辈子可能都没机会了。 + +极客时间:这是你对backup的理解,对他们有一些评价标准或者要求,那再往下一层,那对于普通工程师来说,你觉得一个称得上优秀的工程师要有什么特质呢? + +张雪峰:我认为一个优秀的工程师必须得纯粹,不要有杂念。“纯粹”这个词怎么解释呢?就是要专心把自己这块事情做好,尽量做扎实,自己尽全力,不过多关注利益。因为有些同学他做事情是有一些想法的,人总有欲望,要么是为了名,要么为了利,要么就是地盘扩大。所以要做到纯粹,就是要没有杂念,或者没有那种显性的杂念,非常难。 + +我前面也说过,老员工薪水很低,但他们也没有什么抱怨,就是把事做好。比如说兰建刚,一开始我只是给到高级经理,后来是通过晋升,到总监,再到高级总监,高级总监他还差一点没晋升过,因为建刚演讲简直一塌糊涂(笑),后来是我跟Mark好说歹说,我说这个你必须让他过,后来才勉强过了(要放在阿里晋升场,这几乎是不可能的事),但建刚自己倒无所谓,他团队好多技术强、够纯粹同学,都对晋升无所谓,我也无语。 + +换句话说啊,对于你在意、喜欢的事(自由、开放、洁癖、架构等),极度有所谓,对于你不在意的东西,极度无所谓。 + +人人心中都想追求完美,但有时候因为你对其他事情有欲望,就可能分心了,有时心里会敲锣打鼓,比如出故障的时候,你可能会想,到底应该选什么,是甩锅保命,还是承担责任?很多时候,故障能看出一个团队和一个人的担当。对技术同学来说,勇于承担责任也是一种纯粹,只要是我负责的、我在意的事情,出了问题一心去解决就好。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/12CTO的艰难时刻:差点引咎辞职.md b/专栏/超级访谈:对话张雪峰/12CTO的艰难时刻:差点引咎辞职.md new file mode 100644 index 0000000..80eed72 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/12CTO的艰难时刻:差点引咎辞职.md @@ -0,0 +1,113 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 CTO的艰难时刻:差点引咎辞职 + 极客时间:你去饿了么也属于空降的领导,刚进去会遇到什么挑战吗? + +张雪峰:其实大家都专心做自己的事,我是没有感受到所谓的火药味或防御性。第一天我担心是有防御性的,但我一直没有体会到,只体会到他们对技术的追求,所以刚开始团队这块倒没什么,我主要遇到的挑战就是宕机,2015年那个夏天,我差点要引咎辞职,七月份各种故障纷至沓来。 + +极客时间:到引咎辞职这么严重吗? + +张雪峰:宕机给我们造成的心理压力太大,那时候饿了么单量已经很大了,真是水深火热。我刚进去其实压力不大,因为我岁数大,大家都比较尊重我,Mark也比较尊重我。但三个月后我也开始被他挑战,被他骂,骂得很有道理,就是我们团队做得不好。 + +七月份我们基本一周要宕机一次,有些感知得到,有些感知不到。那时候我基本都是最后一个离开公司,都过零点的,因为我要确保没问题,其实也是有点拖延时间,就想怎么样才能完全破解。 + +当时觉得挺对不起公司的,而且Mark一开始是骂,到后来他也知道,我比他更着急,他也不骂了。所以15年那个夏天我真有可能就离开饿了么,后来反正不知道脑子怎么抽了筋,最后也没有提交引咎辞职报告。 + +当时我进来半年,也招了一些同学进来,跟新老团队融合得还可以。如果我选择离开,那这些人怎么办?所以后来想想还是考虑兄弟之情,咬咬牙硬挺过去了。 + +那时候就感觉自己真的快扛不住了。Mark有时候说两句,业务部门也会吼,但是都有尺度,但一线人员才不管,一线那时候把我们骂惨了,天天被骂成狗屎。 + +以前有个软件,比现在脉脉还要疯狂,叫无秘。脉脉职言区有时还是有顾忌的,因为有商业公司在运营,无秘是完全无官方运营的,就是一个畅所欲言的地方。那时候一天被骂得最多的就是:“拜托你们能不能招几个好点的网管?”。 + +一线说“招几个好点的网管”这样的话其实很朴实,他们觉得这些搞技术的都是天天吹空调、敲键盘,我们销售还要扫街,还要和美团“白刃战”,体力脑力兼有,然后一宕机还要被商户骂。 + +商户去骂也是对的,因为高峰期过了,消费者退单,宕机意味着做出来的饭只能倒掉,他也不知道要送给谁,浪费粮食啊,真的是很惨痛的教训。那时候大家可能认为赔商户钱就解决了,但其实商户心疼的是饭菜,眼睁睁看着好好做出来的几十份、上百份全部倒掉。 + +我们还要赔消费者,因为餐没送到。赔消费者是发红包,发红包的意思是说,这次我们宕机,下次你不要抛弃我们。除了商户和消费者,我们还要赔外卖小哥,因为小哥不能白跑。还要照顾销售的情绪,倒不用给销售赔钱,但我们要赔礼道歉。那时候真的很迷茫。 + +后来我们经常去一线调研,我也要送外卖的。一开始他们还不太敢说,我说咱们就当兄弟,有点江湖味,他们会给一些反馈。那个时候我是体会到兄弟之情,一线真的很苦,他们说得好听叫销售,但其实就跟扫大街的清洁工没什么区别,甚至更惨,清洁工还知道他是有固定任务,基本不会出问题。但销售要承担风险,万一宕机他要想怎么去赔礼道歉,甚至发生过有的销售快要给商户跪下去道歉。 + +极客时间:宕机的问题这么严重,你当时心里感受应该挺难的吧? + +张雪峰:对,压力过大,好在没有到睡不着觉的程度,但第二天肯定又会想这些事。 + +虽然心里难,但我要能控制住情绪。我的个性还是能控制住情绪,因为确实是自己和团队要扛的责任,团队已经够拼命、够努力了,这个时候你去压团队也没用,我也不可能再去招一批人,招人也救不了火,而且招来的人还得磨合,所以只能是自己去想各种对策去弥补,主要还是靠时间。 + +绝顶高手是能控制住情绪,也能很快能消化掉,我是能控制住情绪,但我不能很快消化掉,我需要时间来稀释,以及想一些辅助手段去弥补。那时候,每天晚上为什么都要过零点才走,并不是说我必须最后一个走,就是待在公司去想一些问题。每天坐在座位上,电脑也看不进去,就想这些东西,所以主要还是靠时间吧,把这个不好的情绪稀释掉。 + +极客时间:你觉得自己是一个乐观的人吗? + +张雪峰:对,我是偏乐观的,因为不乐观没法压制住自己的情绪。如果你是悲观又要压制情绪,一定会崩溃的。虽然我个人是乐观的,但是我对工作或者像安全性、稳定性这种事情,我会做最悲观准备,或者说设定一个较低期望值,这样不至于灾难或者故障来之时手足无措。因为很多时候一个人崩溃,是他根本没想过这件事,或没到他的期望值,才会出现很大问题,感觉难以释放。 + +从我的角度,这个期望值大概就是时间,时间可以解决这个问题。但首先你要能控制住情绪。 + +极客时间:后来宕机这个难题是怎么缓解的? + +张雪峰:硬挺过来的,八月初就缓解了。当时主要两个地方:PHP、RabbitMQ有问题。 + +我们当时用了一个开源的消息组件叫RabbitMQ,这是个巨坑,不是说它软件本身不好,而是我们没有能力完全驾驭。另外,我们当时的PHP水平也还算可以了,包括后来我们找新浪的人(新浪用PHP比较多,饿了么也找过新浪PHP大牛尝试破局),他们也搞不定。PHP底层有引擎,当时每周一次的故障,几乎都是这个引擎带来的连锁反应,我们没能彻底搞定,然后大家都去读C的源码,赶鸭子上架,源码是能读懂,但就是搞不定,这东西可不是你加一句两句代码就能彻底解决的。 + +后面我们就用了各种work around,你知道work around就是绕过去甚至很ugly的办法,或者做很多补丁。其中一种补丁的学名叫补偿,比如你这个订单没过去,正常就丢了嘛,但丢了很严重,我们就设置个轮巡机制,有个Job,假设本来订单是5秒内响应,现在就是1分钟后发现有个订单漏了,再给你搞一次,用这样的方式(甚至包括人肉),做各种补救措施、补丁工具等。 + +物流宕机最可怕,因为我们这个业务是强耦合,不可能松耦合,就是要高内聚。物流宕机当时我们怎么做?人的智慧真的无穷,刚开始大家就用QQ群解决。因为我们类似广域网下的无数个局域网(不像淘宝,淘宝全国任何一地商家都可以服务任何另一地用户),我们可以划片区。他们那个QQ群就是骑手、老板、调度经理等人组成,骑手就送这几个商户。 + +比如说物流宕机了,但这时候订单是有的(商户挂掉那就没机会了),老板能做出餐,但是没有办法传递到小哥怎么办?小哥在QQ群里说“我没接到单,你有多少单了?”商户说“我已经接了10个单,你快来。”然后就在QQ里面传单号。那时候微信还不怎么流行,小哥还是喜欢用QQ。但后来宕机问题解决,终于可以下线QQ work around了。 + +极客时间:有点曲线救国的意思。 + +张雪峰:是这个意思,后来我发现物流系统还有个很大的问题,搞物流系统这批同学,就是另一类极客。饿了么刚开始拆分服务,物流拆分得很夸张,直接同步变异步了。我说你们犯了一个错误,叫“为了异步而异步”。 + +大家以前的代码(交互)尽量都是一路撸到底嘛,直接写完,这个叫单体。后来搞微服务就要拆开了,结果他们不光拆开,拆开之后,还要用消息通知。我举个不太恰当但大家明白意思就行的例子,比如说算工资,本来可以直接算出来,他们非要先送一个你的职级,再送一个你的社保基数,然后送过来之后还不是马上给你,你要自己去取,我只是通知你有这个数据了。你取过来之后慢慢算,算完之后再推给另一个涉及工资计算的模块,诸如此类。物流同学就是用类似方式,他们真的把异步做到了“极致”(饿了么价值观:极致、激情、创新)。 + +但是他们做异步的初衷是什么?是因为物流的量很大。以前宕机是因为量很大,用同步的话服务器撑不住,所以就改异步。他们说至少可以缓和五秒钟,但后来我发现这五秒钟没意义。 + +我自己也体验过,比如我点个外卖,提交订单之后习惯性去刷一下,看看商户有没有接单,然后过一分钟看看骑手有没有接单。还要看地图,有时候看到小哥明明经过我这了,怎么先去送另一个人了?可能很多人都有这样的疑问,这个不能怪骑手,也不能怪系统,有各种原因,此处暂时不表。 + +大家都会去刷,后来我们发现用户在饿肚子时的心理承受能力就是三到五秒(淘宝、携程没这问题,大家对订单或物流状态变化的容忍度高很多),你是通过异步让这个订单不至于当场爆掉,但你延后五秒之后,堆积起来也很厉害,东西多了之后,最后还是爆掉,你只是让用户前五秒感觉系统没有宕机,但最终结果还是宕机。最后我们异地多活搞出来,几乎就没有大的事情了。 + +极客时间:其实这一次困难挺过去之后,后面再遇到难题,相对来说你就比较从容了。 + +张雪峰:对,那时候我自己心里有障碍,不好过去,至少刚开始不好过去。我和老婆说,估计要在公司旁边旅馆住几个月,直到彻底解决宕机问题。那时候,大家也看到我的焦虑,他们也竭尽全力,但宕机这个事情你不能去硬搞,比如每天都去揪每一个(可能引起宕机风险)模块的每一行代码写得怎么样,这个会出乱子,也极其耗时,ROI 极低。在当时那么紧迫时局下,我只能用另外的方式(如:局部技术改造、局部架构升级等)去缓解。当然了,中长期解决方案还是团队整体架构能力和代码质量提升,这里先不展开了。 + +那时候还没到根治的时候,因为当时不要说异地多活了,灾备还没有呢,所以只能去缓解。你如果要根治下猛药,那我那会真的可能实现了CTO里面“O”的价值,但这个就是负面价值了,会导致公司出问题。 + +如果那次我引咎辞职,汪渊是可以临时顶上去,但真的,就不说团队可能散架吧,后面凝聚力肯定会有大麻烦,因为汪渊已经去搞产品了,大概率还得再找个CTO过来。 + +极客时间:除了这个对你来说是个坎儿,还有没有其他的艰难时刻? + +张雪峰:还有一次是在阿里,出过一次事故。当时就是异地多活出了事情,理论上异地多活能切的,但是因为一个开源组件的问题导致出了一个大事故。 + +我后来也反省,我是有责任的,因为这个组件当时做过一次交接,本来是在中间件团队,后来交接到另外一个团队。但交接的时候,只做了个口头交接,没有立字为据,没有指定说这个就是你来管,这也是我们当时不够精细的地方。所以最后很难判责,确实判罚谁也不好说。 + +而且其实任何故障都会有端倪,没有无缘无故的故障。在那次事故发生前,其实有一些端倪了,只不过比较隐蔽,只有极少数同学发现了,但他们也没意识到持续堆积会造成重大问题。 + +后来我们复盘发现,当时出了故障,不做切换反而不会引起大错,最多就牺牲一小部分流量。但是按照SOP(标准作业程序),确实应该切换。恰恰是这个开源组件命中了一个Bug,所以导致切换引起了风暴,细节我就不多讲了,但其实事故发生前就有端倪了。 + +出了这个事,我跟建刚商量,我说建刚你做好准备,扣你全年的奖金,我自己降一级,我们跟公司主动一点。跟公司汇报完了,昆阳火比较大,他说你凭什么自己申请降级,你没这个资格要求自己降一级。他意思是说你自己感觉你有担当,但他们(包括CEO、CPO)不认为我这时候这么做是合理的。 + +他们灵魂拷问我说,“雪峰,我知道你有担当,你想帮团队扛下,你想保住他们,但是你有没有考虑过,进了阿里之后,如果一线工程师不为一个Bug负责,而且是他已经看到可能有问题,只是没有警觉,那你认为今后组织再扩张10倍,还能不能Run下去”,他说的是有道理的。他认为我是江湖气,当然我是有一点,但其实我也感觉自己没做好,不完全是为了帮团队顶锅。 + +后来我就只能出面去跟一线工程师谈。那位一线工程师对我们有汗马功劳,是一位非常优秀的员工,只是因为组织转变、汇报关系转变,包括责权不清晰等等问题,他自己也郁闷,所以就没怎么上心。我跟他说从结果角度你要承担责任,你要承担工程责任,我承担管理责任。 + +他认这个理,但是他也感觉比较委屈,最后他跟我吃顿饭,说自己也想通了,我说我对不起你。我们对他有一些小的处罚(不是劝退),但后来他主动提离职,其实相当于我劝退他,我认为这也是对我的一个考验,这也是我另外一道坎儿。 + +当时如果这件事被披露到网上,工程师们肯定会非常愤怒,说不应该惩罚一线工程师,这全应该你们Leader来担,但一线工程师在阿里也是要担一定责任的,不会让你降级或离职,但至少会对你的年度绩效有影响,比如绩效不能3.5,不能参加晋升,这种处罚也是有。我们也类似,并不是要让他辞职,基本都是通过绩效反映的。 + +我认为HR说的是有道理的,对于一个组织来说,确实应该这么做,特别是对一个越来越大、越来越正规化的组织,是应该这么做,否则没有规矩不成方圆,我再心疼他,他有再大的汗马功劳,也得这么做。跨过这个坎,对我来说也很不容易。 + +极客时间:我可以理解劝退别人这件事对你是一个坎儿吗? + +张雪峰:不是,我也劝退过不少人,但我感觉劝退我认为几乎没过错、又曾立下汗马功劳的优秀同学,是一个坎儿。 + +极客时间:如果那个时候不是在阿里,而是在没有被收购之前,遇到这样的事情,你会怎么做? + +张雪峰:没有收购之前的话,可能我就跟Mark说我降级,Leader他们就象征性的处罚,因为这个事情扯不清了已经,没有立字为据。所以责权不清这是我要反思的,也是我的责任。 + +所谓协调,相当一部分都是这类问题。表面看,这可能是件小事,组织架构有调整我也知道,但我在规范设计或制定上有漏洞。其实PMO已经帮了我很多,他们制定了很多细节,但我在这件事上确实有责任,而且引起了很严重的故障。但经历过就有收获嘛,你没经历过,后面还是会有类似的情况发生,尽量不要在同一个地方犯两次错。所以,我都是一路犯错过来的,一路被挑战。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/13大的决策与难做的决策.md b/专栏/超级访谈:对话张雪峰/13大的决策与难做的决策.md new file mode 100644 index 0000000..2fcd3de --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/13大的决策与难做的决策.md @@ -0,0 +1,79 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 大的决策与难做的决策 + 极客时间:列举一下你在饿了么做得比较大的决策? + +张雪峰:第一个大的决策,我冒着风险把兰建刚招进来了。我和他一面之缘,江湖上名声不显,就是因为他做过电信级软件,感觉可以聊一聊。建刚当时话不多,如果滔滔不绝的我肯定要打个问号的。我那时候没得选,之前谈妥的那位跳票了,建刚是跳票那位强力推荐,这个你可以认为是赌博,但有时候决策就是赌博。沟通或许看缘分,但特殊时期的快速决策,必须冒风险。事后都证明了一点,这个冒险赌对了:不止建刚自己绝对靠谱,他的个人能力和魅力,盘活了整个 CI 团队(草创的时候叫框架&工具团队)。赌 Leader,实际就是赌 Team,一本万利(笑)。 + +第二个就是2015年7、8月份,饿了么频繁宕机,我最后没有引咎辞职,这是我个人的决策。后来经历多了才体会到:当时自以为引咎辞职很有担当,实际却是逃避,是对公司和团队的不负责任。换句话说,即使要引咎辞职,也得把摊子收拾干净了再走人(当然了,公司不让你收拾干净直接干掉你,也无话可说)。 + +接下来就是决定做异地多活,当然这个有皓哥(左耳朵耗子)和建刚的推波助澜。 + +最后就是,2017年7月份收购百度外卖,我坚决要把百度外卖的技术团队留住。 + +极客时间:在这些决策里面,你觉得哪一个是比较难做的? + +张雪峰:就是我自己的那件事,没有辞职。因为组织上再怎么艰难,我都可以挺过去。包括识人这方面,其实有时候人和人之间就是有一些量子反应,量子形容到人就是两个人精神对上了,产生化学或生物反应,所以这方面我认为还好。 + +从组织层面讲,事后来看,自己在这方面相对比较笃定:我能够凝聚住大家。当然这个也是事后才能这么说,当时如果做的一塌糊涂,我现在就不可能厚颜无耻在这里参与访谈了。现在不管身在哪个公司,不少同学也会一直找我、“骚扰”我,说你什么时候重出江湖?我说你们说的江湖我可能不太会再考虑了,可能先做点有精神快乐的事。 + +我认为这样的事做一次可以,再做就有点重复了,现在也有些公司找我,但我认为几乎不可能再经历像饿了么这样跌宕起伏的五年了,人生又能耗费几个五年?我们经历过3家云厂商,都是3家云厂商曾经最大的客户;经历过BAT 3个巨头的入股或注资,或被收购;又经历过一个“土八路”团队从头到尾的“野蛮”生长,所以我也希望分享一些Difference,更多是我认为做得不好的地方,一些教训、失败,或者要去反思的地方。 + +极客时间:总结你自己做过的决定,你觉得你是一个做决策很快的人吗? + +张雪峰:大部分时间是的,我这边名义上是集体决策制,或者民主集中制,一般我还是比较听大家的意见,要让大家说话,有时候他们都“闷炮”一样,我会鼓励说你告诉我这个到底是不是有问题什么的,但最后拍板基本还是我。大部分时间我是直接拍死,或者直接毙掉。 + +极客时间:你有做过错误的决定吗? + +张雪峰:从事后结果看,多了去了。致命的错误我也不好判断,因为这个要旁观者来评判,我自己感觉致命的,就是对公司产生重大影响的可能应该没有,但是犯的错肯定不少。 + +极客时间:比如让你说一个你觉得相对比较大的错误。 + +张雪峰:我还是说组织方面吧,我不搞团建,我认为是有错误,就没有让团队之间良好的关系建立起来,后来我发现我不搞团建,导致我后面要付出更大成本,比如我要出面,线下喝咖啡去协调。我应该多搞搞类似裸心会这样的活动。很多时候,组织风险远甚于技术风险,而且是温水煮青蛙式风险,让你不知不觉中出现偏差直至产生重大负面影响。 + +前面我回答你不喜欢团建去旅游,纯粹的旅游团建我确实不喜欢,我一直认为纯粹旅游不夹任何私货挺难的,我觉得自己是个障碍,是个灯泡。但不搞团建这个倒不至于致命,只是你要付出更大的代价。 + +如果说可能致命的决定的话,就是我想过引咎辞职,可能对我不是致命,但对公司和技术团队可能影响很大(大概率再找一位 CTO),如果发生,我今天也不会在这了。 + +极客时间:在做CTO的时候,什么样的事情会刺激你,会让你觉得特别有成就感? + +张雪峰:我认为团队有成长,我就有最大成就感。我不能说业务有成长我就有最大成就感,而且我们业务的试错大部分也不成功,这个绝大部分公司类似,当然失败也是有价值的,但是毕竟没有成就感,或者说成就感差一些。 + +其实,我也说过,要尽可能让大家做自己喜欢的事,希望大家感觉到在饿了么做技术和在其他公司不同。 + +我们也有同学离开之后去了美团,他感觉好像也没什么不同。但说到“没什么不同”,我觉得有一部分是我的责任,我没有给大家创造出这样的机会,其实我们本身跟美团就有不同之处,我们是一个大学生创业团队。 + +可能到1800人的时候,职业化的人已经很多了,鱼龙混杂,但在四五百人,就是2015年、2016年的时候,其实大家的“味道”还是不错的,是相对纯粹的。美团你很少看到什么开源的东西,后来硬是捣鼓出几个,我们觉得也不咋地,异地多活更不说了,美团找我们交流过这方面经验教训,他们一直想做、在做但也一直没做成,不过现在有没有做成就不清楚了。 + +美团他们强在组织能力,我们是强在有各种突出的Leader,或者我们自己给大家发散,给大家充分的自由度。这是硬币两面,是好也是坏,当然没把公司搞挂也算幸运。但是另一方面,我相信有相当一部分同学,他们会认为在饿了么技术团队是值得的,可以实现职业发展上的提升甚至个人精神上的快乐。 + +但这个对公司,我不能说算是对还是错,因为公司花钱养着你们是以业务为第一的,不是让你们玩技术的,所以这里面要平衡,什么叫“玩技术”,什么是通过技术尝试去做一些支持业务的事,很难清晰界定。 + +极客时间:刚聊了管理上的各种决策,还想补充聊一个管理手段的问题,听说技术团队开会迟到,你会重罚? + +张雪峰:1分钟100块,这个是我定的游戏规则,这不是规矩,规矩变成有点你私人帮派味道,这只是游戏规则。 + +其实我也不是一定要罚钱,我跟大家说了,你如果预感自己要迟到,你要提前通知我。你不要跟我解释路上堵车,不管你坐什么交通工具,你自己作为成年人,先用高德地图看一下。 + +原来最早是说提前2小时还是3小时,后来我容忍了一点,提前1小时通知我就行。如果离开会不到1小时,结果你突然迟到,那没有任何理由,当然有时候家里有紧急情况,但钱还是要交。5年半以来,一直到我离职,我所有的会都准时参加,从没有迟到过。 + +极客时间:这个规则是一开始去的时候就定下的吗? + +张雪峰:就是形成例会之后定下来的。我到饿了么,一开始没有开周会。后来我跟汪渊把组织拆完之后开始有周会,应该是2015年还是2016年,记不清了,反正这个规则是所有人统一的。 + +这点上我老婆对我意见非常大,她都是掐着时间。我去机场、火车站,哪怕去高铁站,我几乎都是提前1小时,我要预留足够的时间。我老婆就一直说,高铁你提前15分钟就行了,我会担心万一排队或者出什么事,在这点上我比较坚持,宁愿我等别人。 + +这可能跟我一直的习惯有关,这个习惯应该在微软养成的,微软这一块非常严肃,外企这一点确实比民营企业强。在微软大家都比较自觉,这个叫Professional,说的好听叫职业素养,但是后来到民企发现不是这么回事,阿里还算是做得不错的。 + +极客时间:我很好奇,你会看一些管理类的书吗? + +张雪峰:基本不看,因为我的兴趣主要在技术(实际最大兴趣也不是技术,后面会提到)而不是管理,你可以说我自学成才,也可以说我不自量力。但其实也不完全是自学,因为管理实践本身也给了我一定时间,虽然最后的组织扩张规模较大,但也用了近3年时间,从几十人到1800人逐步增长起来。如果说饿了么今天30人,过一个月就变300人,以我当年的能力、阅历、视野,不一定能Hold住,速度太快了。 + +我印象中可能翻过这类书,但绝对没完整看过。借用一句话:那都是很好很好的,可我偏偏不喜欢。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/14不够坚定:异地多活没有一步到位的遗憾.md b/专栏/超级访谈:对话张雪峰/14不够坚定:异地多活没有一步到位的遗憾.md new file mode 100644 index 0000000..fd426f7 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/14不够坚定:异地多活没有一步到位的遗憾.md @@ -0,0 +1,95 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 不够坚定:异地多活没有一步到位的遗憾 + 极客时间:饿了么异地多活一次成功算是互联网技术圈挺浓墨重彩的一笔,当时你们决定做这件事之前,经历了一个什么样的过程,可以讲讲吗? + +张雪峰:2017年之前,整个公司所有的业务系统都是部署在北京机房,服务器大概有四千多台,另外还有灾备的机器是在云端,都是虚拟机,大概有三千多台。当时我们峰值的业务订单数量已接近千万级别,但是基本上北京机房(IDC)已无法再扩容了,也就是说没有空余的机架,没有办法添加新的服务器了,必须再建一个新机房,于是我们在上海建了一个新的机房。 + +做多活之前,其实这里面有一个失误(严格说不算失误,法不责众,国内几乎皆如此)。当然这个失误我认为大部分团队可能都会犯,就是先搞灾备,不敢一下子上异地多活,没能一步到位。其实当时我也提过“我们是不是一步到位”这个问题,虽然外界感觉饿了么一步到位了,其实我们之前还做过一个灾备,花了不少钱。 + +极客时间:所以一开始先做了灾备,才做了异地多活对吧? + +张雪峰:对,这是常规路径。中国绝大部分公司都是做了灾备后才想做多活,当然能不能做成另说,有的最终没做成,或不敢做,或者内部阻力很大。 + +美团当年就没做成,现在有没有彻底搞定异地多活我不太清楚。其实美团技术底蕴挺强的,因为跟着王兴早期创业的人不少,包括美团还曾做过美团云。只要做过云,哪怕这个云再烂,都储备了一批做基础设施挺强的人,这不是一般公司技术底蕴能比的。但是美团业务太强势,停几个月业务需求,或只能做些无关核心的边角料需求,不可能的。 + +饿了么好在技术这块拧成一股绳,基本就是我一个决断声音,说难听点也是一言堂,或者我跟团队即使吵得再凶,他们再挑战我,但最后出去都是一个声音。我跟他们说,你们当场挑战我,都没问题,我也无所谓面子,但是既然我们吵架吵出结果,你就不要再回去反悔,不允许反悔,你跟团队反悔、抱怨也是不允许的。 + +曾经有一个同学,当场同意,回去又开始磨磨唧唧,在群里乱发,这个不行那个不行。哪怕这件事事后证明错的(谁能在结果出来前证明这是错的?),在当时你就得错到底,证明它是个错也是有价值的。更何况,异地多活最终一次上线成功,之后更是挽救饿了么线上系统无数次。当初有异议的同学,也包括外部知道这事但并不看好饿了么技术团队能搞定的同学都无话可说,虽然最初下这个决断有一定赌博成分。 + +唯一一次美团跟我们做过的官方技术交流,就是我们做了异地多活后。大家知道,虽然我们两家商业上竞争白热化,但技术同学间还可以,大家不敌视,交流比较坦诚。 + +极客时间:灾备和多活区别是什么? + +张雪峰:灾备什么意思?灾备是我有一套一模一样的系统,每次发布的时候两边都发一次,但是生产的流量或者实际的数据不经过灾备系统,只经过主系统,然后通过数据同步模块全部同步过来。但它有很大缺陷和风险,如果做到极致,全是AI控制或机器人控制(包括发布后各种校验、验证等),理论上精度会很高,但实际很多环节还是人来控制。 + +我们发布很多,一天上千次,多的时候超过2000次,还有依赖,比如,你发了A 、A.1、A.1.1,这里面只要有一个漏发,可能你生产系统没问题,因为你要用流量灰度切换的,但是那边(指灾备IDC)是没有灰度的,没有流量,今天忘了就忘了,运气好明天又发了这个就OK。但万一来一个事故,流量一下子进来,反而把数据搞乱了,结果不应该发的补贴发了,这也就算了,赔点钱,但是如果数据错了,就大麻烦了。所以它没有经过真实流量考验。很多业务场景,并不是发布系统默认双发或多发就能解决的,还是需要大量人工验证或灰度流量验证才能确保万无一失。 + +做灾备,你要解决两个问题,备份和恢复,备份和恢复永远是一对的,不能切开。一般的企业更多在备份上花功夫,这只做了一半,不常态化做恢复演练的团队或公司,已经有太多沉痛教训了。而多活呢,既解决了备份,又解决了恢复问题,因为不需要恢复,数据始终在跑。换句话说就是只要确认是Workable,可工作的,就可以了。 + +极客时间:所以在多活之前做灾备其实没必要是吗? + +张雪峰:不是,这个要具体问题具体分析的。 + +当时我们为什么做灾备?因为直接上异地多活,我是有犹豫的。跟我很熟的人会经常质疑我,在公开的会上也会挑战我(鼓励 Challenge Up 也是饿了么技术文化中自由、开放的一种体现),说“峰哥,你当时不应该做灾备”。但我当时的理由是说,中农工建都做了,美团也做了,滴滴也做了,除了阿里系(淘宝、支付宝算一类),他们确实直接搞了异地多活。那么,除了阿里系,在全中国大家想做交易类系统第二只螃蟹吗(根据事后复盘,严格说这并不算第二只螃蟹,而是第一只网格化环境下的异地多活螃蟹)?我说我不敢冒这个险。 + +所以代价就是你做了一个用处不大的东西,但它也有一定用处,万一被勒索软件盯上了,我们还有一份数据备份,但是从现实意义来看就好像没有太大用处。这个其实也很难评价,就比如说你做各种形形色色监控软件,你投入很多资源搞中间件,应该很有作用吧?但只要没出事,大家可能看不出来,业务团队或CEO估计就更难以体会在这方面进行投入的价值了。 + +所以,在国内有一个很怪的现象,这确实跟发达国家不一样。在国内,不管什么行业,救火英雄永远得到最大赞扬,幕后英雄却无人问津,导致很多真正牛逼的人、防范于未然的人,反而委屈地走了。我在饿了么,尽可能去扭转这个局面,但也没有完全扭转过来。因为你很难界定什么叫防患于未然、什么是因为基础设施投入而带来的巨大价值或避免的巨大风险。 + +在技术决策上,这是我的一次偏差,但后来还是做了异地多活,当然皓哥(左耳朵耗子)和建刚他们让我坚定了决心,我当时不是反对,而是有犹豫。 + +极客时间:你不敢下决心搞多活,具体的原因是什么?纠结或者犹豫的点具体是什么? + +张雪峰:我吃不准中间件这个团队能不能承担并搞定(除淘宝外)这种规模(日均常态千万级订单)下的交易系统上去实现异地多活。更何况,异地多活几乎只有一次上线机会(最大风险不在上线后稳定性保障,而在多活节点间的数据错乱),不成功则成仁。 + +只不过后面我下了决心,我们也不用立什么军令状了,如果干砸,从我开始,核心团队在收拾完烂摊子后(这是15年夏天那次张雪峰快扛不住想引咎辞职的挣扎后,对责任的全新理解)全部下台。 + +做异地多活的话,业务要耽误三个月,接不了涉及核心系统的大需求或大变更,业务部门压力非常大,我要去说服。而且上线后如果出问题,那就是致命的问题(数据错乱),因为数据是分开的(多个机房)。我们原来就一份数据,宕机了可以恢复,至少数据不会乱。所以宕机不是最可怕的,就怕你连宕机的机会都没有(之前提到的安全问题类似),数据错乱或丢失,是最可怕的。 + +如果按我对自己的要求的话,这件事我认为算是一个失误,说得好听点叫偏差,应该一步到位搞异地多活。因为做灾备也投入很多钱,也有媒体说钱打水漂了。但这是事后诸葛亮,我当时也没这样的勇气,因为确实饿了么那时经不起任何大的波浪。虽然这是给自己找的理由,但我感觉如果要求苛刻一点,确实是一个不正确的决定。 + +决定做异地多活之后,当时找了毕玄、阿里云交流,但是后来发现不能直接用阿里那套(所以前面提到,不是国内第二只螃蟹,是真真切切第一只),因为阿里是适配全网应用,淘宝是没有网格概念的,三亚和哈尔滨是没什么本质区别的。但我们不行,我们是一个表面广域网实际却由无数局域网构成的特殊业务网格,而且我们网格之间是有重叠的,还比较复杂。所以后来决定只能自研。 + +即使这样,我们第一批上线的异地多活还是把物流落下了,这是后话。 + +极客时间:如果现在来选的话,直接就搞多活了对吗? + +张雪峰:对,如果我回到当年,我就知道这个团队是能承担这个使命的,肯定直接搞多活,当时我也不是太有底气。如果直接搞多活,也能省好多钱了(指投入到腾讯云做灾备的钱)。 + +极客时间:异地多活当时做完之后,其实效果还是非常好的是吧? + +张雪峰:是的,这件事我最为团队骄傲的,不是说异地多活后面帮我们挡过很多以前挡不过的灾难,而是我们有惊无险一次切换成功。因为一旦切换不成功就是灾难,打补丁都很难(数据错乱比宕机更可怕),核心团队散架(士气打击)、我直接下台(业务问责)几乎是必然的。 + +支付宝有一次严重事故,宁愿宕机不敢切换,就是因为不能出现一分钱差错,这个简直就像银行卡里面出问题一样,一分钱就致命,所以其实当时我不是很有信心能一次搞定,异地多活又是做得跟阿里不一样套路,如果真出事,毕玄、阿里云也帮不上,都是我们自研适配的架构,那一次,真的是大家连续几天,彻夜不眠。 + +打个不恰当比方,双十一或618出事,最多是稳定性层面故障,舆论压力大但不至于对公司产生致命影响;但如果异地多活切换出事,舆论初期或许感觉不到,但一旦短时间不能修复(数据错乱,时间越长错乱会以几何级数增长),对公司绝对是致命影响,CTO 中 “O ”的价值倒是体现出来了,超级负面价值,或许应了我之前说过的一句话:你可能连宕机的机会都不会再有了。 + +极客时间:你刚刚也说到第一批上线异地多活,把物流落下了,这个是什么事情? + +张雪峰:后来我反思这也是我另外一个不够坚定的地方,也不能说我心软吧,就是不坚定。因为当时物流业务老大,也是联合创始人康嘉,还有技术负责人刘昊旻都犹豫了,问我能不能把他们放第二批?物流出问题,他们都得“跳楼”。 + +但这样就等于帮物流填了个临时坑,却为整个技术团队挖了个更大的坑。后来我们要兼容第二批(其实第二批就物流,只不过物流涉及系统太多)上异地多活的物流,在这期间,要随时保证物流那边的顺畅,就做了很多开关,然后我们中间件团队、架构师团队还有PMO团队,把杂七杂八这两套东西并线。如果是两套业务系统并线还好一点,只要切个数据库就行了,但这两套异地多活很麻烦,有一堆的风险要考虑。等于我们维持了几个月的“大部分多活+物流非多活”并行,切换时又胆战心惊地担心物流切换出问题,还得切回去继续并行。 + +我们原来设想异地多活只能一次性切换,因为我们的业务是强耦合的,不像携程,携程机票、酒店关联度不大的,你要订机票+酒店,做个简单聚合就行了,但我们不一样,饿了么是用户下了单,商户接了单,物流就要送单,上下游其实是强耦合(高内聚)。 + +程序员可能会说,现实业务没你说的那么理想,该强耦合就强耦合,其实不是强耦合,另一个词叫高内聚,该内聚的时候你不要去追求什么微服务那些乱七八糟的东西,就应该高内聚,因为就是一个业务形态,业务才是最重要的判断耦合或内聚的依据。谁(调用方/消费方)也离不了谁(被调用方/服务方),你每次调用都涉及到它,干嘛非强扯开来?没太大好处,当然,可以分开发布算一个好处,但也仅是技术上的好处,不是业务或领域上的好处。 + +最后送大家一句话,也是我对当年多活改造迁就物流团队的一个教训总结,用英文描述比较贴切:Avoid solving one problem by creating a BIGGER one,希望大家今后都能顺利跨过这类坎,不再重蹈我当年覆辙。 + +极客时间:你刚刚说美团也想做异地多活是吗?相当于它也是走了常规路线,先做了灾备,再想做多活。 + +张雪峰:是,美团做了异地灾备。美团现在没有CTO了,在王慧文之前,CTO叫罗道锋,他原来是大众点评的CTO,后来美团和大众点评合并之后,他就做了美大的CTO。 + +之前提过,我们团队和道锋团队间做过技术交流,美团一直想做异地多活,但规划了几年一直没做成。后来我问了那边相关同学为什么,他们说道锋虽然是CTO,但主要管公共技术,也就是搞搞基础设施、大数据之类,业务那边的技术团队非常独立,业务对自己技术团队影响更大。 + +美团业务的强势度远远超过饿了么,技术要影响业务团队需求比较难,因为如果要做改造,就要有很多技术资源(时间、人员、保障等)投入,一定会影响业务交付,业务不干的。他们业务团队的技术负责人直接汇报给业务老大。 + +但饿了么不是这样,我进去之后,先是所有的技术都在我这边,但后来发现这样有问题,这个模式不太好,我就跟汪渊商量,把业务的技术跟业务的产品都实线汇报给他,但业务的技术虚线汇报给我。虽然是虚线,但因为汪渊和团队的信任,这个虚线还是比较强的。所以倒不是说罗道锋他搞不定,是业务根本不鸟你,我们这边就比他要好一点,我几乎能完全掌控,即使业务技术团队只是虚线。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话张雪峰/15兴趣与个人认知.md b/专栏/超级访谈:对话张雪峰/15兴趣与个人认知.md new file mode 100644 index 0000000..dfa0374 --- /dev/null +++ b/专栏/超级访谈:对话张雪峰/15兴趣与个人认知.md @@ -0,0 +1,107 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 15 兴趣与个人认知 + 极客时间:听说你特别喜欢数学。 + +张雪峰:我最爱的除了家人,就是数学、历史这两块,数学是我从小的兴趣,所以花的时间多一点,我主要在B站上数学系的专业网课。 + +也有人问我是不是现在闲了才看,我说不是的,我一直在看。以前只是周末有时间,要系统学习数学还是有点挑战,因为周末后再上5天班,基本就接不上了,所以那时候看科普的多一些、专业体系化的少一些,坚持了很多年。虽然很多人也不相信,但这个确实是我从小的爱好。 + +还有一个催化剂就是,我刚上高一就拿了当年全国高中数学联赛省一(上海),但离那年(上海)省队有点距离,遗憾没能进冬令营。当时还自我安慰,如果在其他奥数一般的省,大概率可以进冬令营(笑)。但即使这样,也是我们学校那年重磅新闻(张雪峰高中是当年浦东唯一市重点,但不以数学见长)。当然了,我师兄更强,他高一省一、高二冬令营、高三国家队且是那年 IMO (国际数学奥林匹克竞赛)满分金牌。师兄是我当年最好榜样,也激励我对数学的持续热爱,直至今天。 + +极客时间:数学能给你带来什么? + +张雪峰:数学带给我精神上持久享受,自然美感觉很棒。很多同学都有能带给他持续精神快乐的事物,对我来说,主要就是数学。或者更精确一点,是基础数学(纯数学)。 + +极客时间:是享受而不是那些枯燥的运算。 + +张雪峰:对,而且数学是自洽的,只要在这条路上,跑出来就是对的。数学和物理不一样,物理很多是人想出来的理论。杨振宁那套理论,已经是目前为止理论物理的天花板,几十年都看不到激动人心的突破,你看今年诺贝尔物理学奖,能知道个大概。物理更多是一个没法自洽的学科,它需要靠实验、工艺,靠现有的工业技术去证明,但数学几乎是完全自洽的。 + +极客时间:在工作之后还能持续钻研数学的人,好像很少,你会考虑到数学在工作中能不能用得着这样的问题吗? + +张雪峰:我就是Enjoy这个学习过程而已,其实我刚从阿里离开的时候,很多投资人找我,问我有没有兴趣创业或投资,我说我的兴趣主要在数学、历史。但对企业来说,企业更Enjoy结果,数学尤其基础数学,这个东西不太能直接产生商业价值。 + +极客时间:在平时的工作中,你会用数学的思维跟大家去讲道理吗? + +张雪峰:会,而且经常用,或者更多是用逻辑的思维。大家不要被一些所谓的计算机原则(典型如:低耦合、单一原则等)迷惑,数学原则才是宇宙终极原则。 + +就像我前面讲过的,什么时候该内聚、什么时候该解耦,你不要被表面迷惑。举个例子:当大于等于两个调用方(消费者),都会去调用一段逻辑的时候,就需要考虑抽象为 Function/Service/API,就这么简单。我经常会跟大家这样类比,不要把简单的业务问题人为地引向技术复杂化。如果是创新或试错业务,更要 Speed 第一,活下来后,才有时间谈 Architecture,最后才有机会上 Scale。之前提过的物流团队极致异步架构、部分团队微服务过度等,都是教训。还有,以上观点可能并不适用真正技术驱动业务的公司或业务,比如:Google、IaaS/PaaS、无人车等。 + +数学是解释抽象最好的方式(物理也高度依赖抽象,但还需实验与观察),有人说为什么要学数学?买菜只要学小学数学不就行了?那不叫数学,那叫算术。开始有未知数这个概念才算摸到数学一点边。解方程有未知数,这就是一个抽象,然后再往上还有更高级的抽象,比如大学的抽象代数,可以把很多物理现象抽象出来。 + +我还想说一点,跟抽象对应的还有一个词叫“想象”,但想象这个东西很多时候是天赋,不是说你想要想象就能有,想象是可遇不可求的,抽象是你需要去锻炼的能力,这是可以锻炼出来的,不是靠天赋。举个例子,你可以感觉一下:线性代数教会了我们如何抽象看待所有线性方程组,多次螺旋上升的抽象过程后,天下再无不可解线性方程组;但同样是数学,初等(平面/立体)几何中各种精巧的辅助线(有些可以靠刷题或训练,但大部分难题还是靠想象或天赋),却是很多同学数学学习过程中的绝对梦魇。要么一分钟解出,要么只能看答案后叹为观止。 + +极客时间:你刚刚说了两个思维,数学思维和逻辑思维,它们有什么区别吗? + +张雪峰:都属于“形式科学”,另两类是大家更熟悉的自然科学、社会科学。要说数学思维比逻辑思维更深一层的,除了抽象、缜密,还要有广阔想象、大胆创新。创新指创立全新数学工具甚至数学分支,用于解决当前体系无法解决的难题(详见“哥德尔不完备定理”)。如果只让我推荐一本关于数学想象的科普书,那一定是《思考的乐趣》。 + +极客时间:之前我们有聊到产研团队按领域划分的事情,饿了么之前的团队划分比较粗糙嘛,就是分C端和B端,C端就是APP和网站,B端就包括商户服务还有物流,你进去之后第一件事是做拆分,这里面其实就要用到逻辑思维。 + +张雪峰:饿了么原来就是个单体,所有的业务逻辑就是一个东西、一个源代码库,C端、B端、D端(Delivery,物流)全在一起,牵一发动全身,也就是说你在部署的时候,每个服务器都要布一坨这个东西,一是影响性能,二是发布很麻烦。只要有同学发布,即使跟你无关,你也要发布一遍,所有的机器都要扫一遍。我们做技术的就要拆解,肯定要至少再分一级。 + +拆分与否,我们当时就遵循一个原则:只要一个人有变化,一堆人要随着你动,或者叫“牵一发动大部分”的时候,这一定是有问题的。其实这也是逻辑原则或数学原则。所以我跟他们说,不要扯什么领域驱动、微服务了,就用这个原则。这个原则确实最容易讲清楚,但实践的时候要多次试、反复试。 + +单体是一个极端,微服务或单一原则是另一个极端。饿了么从来没有真正提过微服务,从来没有过,我不去用这个概念。我们就是从业务的合理性去拆分。对领域驱动呢,我当时也是持观望态度,不能说保留态度,我觉得领域驱动是一个模棱两可的东西(顶尖DDD牛人或在大规模超复杂体系下成功实践过的同仁勿喷,毕竟让绝大部分技术同学吃透DDD,无论ROI还是效率都很低),就跟架构一样,所以我希望回归朴素,就是从逻辑的角度,或者数学角度,给大家解释。所以当时我们也不做领域,我把以前的经验带过来,开始有一些中台的萌芽,比如说把交易系统、营销系统拆出来,把用户系统拆出来等等。 + +从逻辑上讲,当你十次里面有八次“牵一发要动大部分”的时候,你就没必要去拆,你就让它耦合(内聚)在那,哪怕最后合出来一个巨大的东西,那证明这个业务就是这样的,没办法。你要么抱怨很倒霉进入这个业务领域,要么你就自己想办法克服。当然还有一个办法就是你通过技术去改革这个业务,那意味着这个业务甚至整个行业的游戏规则都要变,在短时间内几乎不可能。之前也讲过,对绝大部分公司的技术团队来说,妄图通过技术驱动业务,还是省省吧。 + +极客时间:刚说到中台的萌芽,饿了么建立中台大概是一个什么样节奏呢? + +张雪峰:最早是整合交易系统,后来整合营销系统,搞营销系统就附带着把会员系统也搞在一起。其实说到底,也不叫营销,叫权益。所谓的发券、会员体系,其实归结为一点就叫权益。这一点国内有两家做得非常好,一个是携程,一个是淘宝的 88 VIP。 + +我们搞中台不是从0到1,应该说是从1到10,因为我们本来就有这部分(交易系统等),只是散在各个团队,后来就是交给石佳宁他们团队去整合,整合的过程当然也比较痛苦。 + +架构调整的时候,我们把C端、B端各个跟交易有关的团队交给中台去整合,这就涉及人员调配,有些Leader就不愿意交人嘛,就是“你动我的模块我可以忍受,你动我人坚决不干,我转模块不转人”。这种情况在阿里是严禁的,阿里一定是转模块必须转人,阿里做得比较到位,这就是成熟组织的表现。 + +我们以前这个团队,即使我强压也不行,大家就拒不交人,他说这个员工你让他过去,他要离职的。到后来磨了几次之后,大家也慢慢立下规矩:如果交出一个完整模块,必须人跟着模块走,去另一个团队,如果不愿意就离职。 + +所以一开始石佳宁其实拿到的是全部模块和光杆团队,相当于活儿给他,但要他自己找人,虽然我给了他很多权限。所以,石佳宁是很不容易的,他几乎只靠自己原来的班底,然后还要去熟悉C端、B端、D端(Delivery,物流)业务,把代码拿过来之后再拼起来。 + +他们从最难的交易系统开始做,搞定了之后,发现交易系统还不是最难的,最难的是营销系统。营销是千变万化的,运营介入最多的就是营销,因为营销有很多玩法,搞各种优惠活动。营销不好抽象,非常考验程序员对业务的抽象能力。 + +运营他们不管的,提了一堆需求,说给我实现这个、实现那个,技术人员就要去抽象。最后我们的营销系统变态到什么程度?那个界面刚开始做出来,只有运营看得懂,一堆Check Box,而且还不能点错(有些可以点错,后台有告警,但太多告警,人也会麻痹)。我们曾经因为运营点错按钮出过事故,最后责任归到运营,但人家也很委屈,吐槽技术做得太烂,说你们这个产品简直不能忍受,搞这么复杂,勾错就出问题。但技术人员就是一根筋,他说我已经全给你抽象出来了,所有的功能你只要点勾就能实现。所以营销系统是很难做的。 + +极客时间:可以谈谈你对中台的理解吗? + +张雪峰:中台就是业务(商业)驱动的。我以大家更能理解的阿里来说,阿里后来做中台,其实真正的核心动力还是商业驱动。比如我在优酷注册了一个会员有一个优惠券,为什么不能在双十一的时候去淘宝买货?我在淘宝有积分,为什么不能在阿里其他平台用?所以淘宝得到一个终极结果就是 88 VIP,就是会员体系,这才是中台的原动力,还是要从商业价值来看。把这些权益放在一起之后,用户就有更大几率留在你的平台。 + +饿了么后来也搞会员系统,我们早期就是发营销券(外卖券),做营销券打通,开始还没有把它抽象成权益打通,后来有了会员之后,才想到要做权益,因为围绕会员的、跟消费有关的都叫权益,这个就更有价值了,包括把混合券这些东西都包容进去。 + +所以中台不是个业务,但它有业务的味道。 + +极客时间:接下来我们聊几个大的话题,你的人生理想或者说人生追求是什么? + +张雪峰:除了家人,我还真没想过这个问题,如果抽象来说就是人和事两方面。 + +我先从人这个层面说,我希望我把一些不同的人,培养到超越我期望至少超越我的一个程度。就像我之前说,做CTO的最大成就感来自团队的成长。超出期望就意味着,他不是我原来可以掌控的,我会在这过程中和同学一起成长,对底下的同学来说,他感觉是我把他培养出来,但同时我也学习到很多。所谓相辅相成、相互成就,大概就是这个意思。 + +我有这样的理想,后来跟很多人也说过,我非常不想做CTO,我就想去挖掘高潜的人,我感觉我可能更擅长做这个角色。当然我现在在做这种“冒牌中介”,也是希望去把一个合适的人匹配到合适的岗位,其实做这种红娘是很有成就感的,不亚于去介绍两个同学或两个陌生人,最后成为夫妻这种成就感。 + +从事这个层面,我只能说是追求美,是精神上的一种美,不是视觉或感官上的美。什么叫精神上的美,或者精神上的成就感呢?对我来说排第一位的就是数学,可能直到离开这个世界,我也会一直追求下去,既是兴趣,也是挑战。 + +极客时间:你有比较喜欢的行业吗? + +张雪峰:我对一个行业很有热情,我过去大概20多年的经历,有一半时间是在这个行业里,就是教育。 + +育人是一件很有意思也很有意义的事,有时候我自己看一些科普文章,很多很深的数学问题,有人就能把它用很简单的方式讲出来,让别人豁然开朗,我觉得很厉害。可能我对这样的事比较有热情,而技术只是我第二或第三兴趣,有时也是没办法,或者说你得先有饭吃,必须养家糊口。 + +极客时间:你有偶像吗? + +张雪峰:历史上我有特别佩服的两个人,一个是商鞅,一个是李靖。但他们算不上偶像,我好像过了需要偶像的年纪,如果非说一个,应该是我第一家公司的老板。他20多年前的理念,甚至到今天依然前沿、足够前瞻,20多年前,他就已经在做标准化的事,是通过公司的产品尝试标准化,不止是停留在标准化理论上。 + +我的感受相当于什么呢?当时进这家公司,从编程来说,我算是小学毕业了,但是突然间一个中科院院士在那边给我辅导,就是这种感觉,而且他又非常平易近人,可以让我有学习的机会。一般的中科院院士即使再虚怀若谷,他也不可能跟一个小学毕业生去谈科学的。我至今还是一直认为,他对我的技术或技术观方面影响最大,但商业方面没有,因为他商业做得不太成功。 + +极客时间:你有没有自己坚持的人生信条或者是座右铭? + +张雪峰:我总结六个字:活到老,学到老,再加上对精神美的追求吧。我希望一直到我生命结束,我都尝试去影响其他人对“活到老,学到老”的追求。从我的家人开始,这一年我已经引起我太太对历史的兴趣,开始买书看,还有我女儿,她以前也从来不看,现在算是有一些起步。 + +当然我能影响更多人更好,但是影响更多人也比较难,我也不想去做博客、去B站开个号,甚至写书等等,我不太想做这样的事。可能我觉得也很难影响到别人,一本书里面的内容,很难放之四海皆准,就像我们今天的访谈,即使把这些观点尽可能的普适化,让别人真正理解也是很难的。 + +我更想个性化地去做一些力所能及的事,去挖掘出一些不同的东西。“活到老,学到老”的另一个说法就是,持续去学一些不同的东西(Difference)。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/01小厂项目:做程序员不难,难的是做职业程序员.md b/专栏/超级访谈:对话毕玄/01小厂项目:做程序员不难,难的是做职业程序员.md new file mode 100644 index 0000000..d4b242a --- /dev/null +++ b/专栏/超级访谈:对话毕玄/01小厂项目:做程序员不难,难的是做职业程序员.md @@ -0,0 +1,237 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 小厂项目:做程序员不难,难的是做职业程序员 + +你好,我是叶芊。- + - +欢迎来到访谈现场,今天我们会从毕玄进阿里之前的经历聊起,他的这段经历少有人知道,却格外有趣,又格外现实。- + - +2002年从江西南昌大学毕业的他,在大学因为兴趣,从学盲打,到玩组机,到写网站,早已练成了网站编程老手,但毕竟是生物学毕业,第一份工作他是怎么找的?能顺利转成程序员吗?- + - +我们正式开始对谈。 + + +  + +极客时间:你大学生物系毕业之后就想转去做程序员,虽然背景是非计算机专业的,但大学做了很多商业性的项目,这种条件你也还是非专业吗?你第一份工作怎么找的? + +毕玄:是非专业。我当时不想在江西呆,觉得还是应该去大城市,就想去北上广深,第一选择是北京。但北京真的太难了。 + +极客时间:那会在北京你是怎么选择目标企业的? + +毕玄:那个时候哪能选择目标企业(笑)。我大学的背景又不好,尽管南昌大学号称是211,但可能就江西有名声,在外面大家基本都没听过,北京就更不用说了。另外我学的还是生物系,要找的还是计算机系的工作,哇这个太难了。 + +我在北京逛了一圈,那个时候还必须去人才市场。我去人才市场逛了一圈就知道北京肯定没戏,所以觉得我还是不要在北京了,北京太难了,以后可以去,现在就算了。 + +后来我去了深圳,因为我有个朋友认识某家公司的人,就推荐了一下,我被推过去之后,别人觉得我不是学计算机的,但是我从大二开始就在外面的公司写商业的网站,有很多经历,这是拿出来很硬的,就跟现在找工作一样,至少我的项目比较实际,所以他们觉得也可以,就说那你来试试吧。 + +这算是彻底转变了,因为你想你的第一份工作是计算机,就进圈子了,后面其实没有人会关注你大学学的什么,已经无所谓了,也不会因为大学不好就刷掉你,不会,他更关注你的工作经历。但第一份确实挺难的我觉得,尤其跨专业,非常难。 + + + +极客时间:深圳那个公司是什么业务?你是做什么的? + +毕玄:那个时候有一家做手机的上市公司叫科健,我们是科健旗下专门做政府软件的子公司,都属于科健集团,背景说起来很挺好的,但这家公司知道的人很少。 + +为什么说跨专业难,我招进去的时候其实都不是程序员,我是负责做实施的,公司做了一套软件卖给了政府以后,要有人去现场安装、教人用啊各种,现在叫交付,以前叫实施。 + +我刚进去的时候他们其实让我干的这个,原因是我真的写不了程序。虽然我在大学确实写过一些网站,但是完全不一样,第一次进那家公司,到现在我都印象非常深刻。 + +当时项目组正在做佛山市政府的OA系统,去了一看,所有的人电脑上打开的,因为他们不是写ASP的,他们做OA都用的VB(Visual Basic),VB我完全不懂,当时我就看到一帮人打开VB在那“哇哇哇”写代码,写得可熟练了,我真的惊呆了,就很仰慕。 + +但我觉得这活我是真干不了。因为开始我也不想做实施,我想写代码,后来我去看了一眼之后觉得确实自己干不了这个(笑),我还是先做实施。然后就被派去佛山的一些政府部门帮他们装上OA,解答一些问题,陪他们聊天。当时政府部门虽然能讲普通话,但习惯讲粤语,我跟他们混了半年,听粤语的水平就是那个时候训练出来的,虽然我不能说,听没有什么问题。 + +  + + + +  + +极客时间:总搞实施,个人发展应该比较受限,那你后来是怎么开始写程序的? + +毕玄:半年后,当时我直属的老板,他决定给全部新人一次机会,我记得,印象很深刻。 + +当时我们有些人对写程序很感兴趣,加上又都跟项目组的人住一起,所以做实施的时候,我们会跟人家学一下VB怎么用,怎么写程序、搭系统等等。 + +然后经理他可能也觉得需要更多人写程序,因为说白了实施的人很容易找,但写程序的人有点难,尤其写软件相对还是有要求。后来半年左右,他想看看能不能从这批做实施的新人里面,挑几个人来写代码,就给我们出了一道题。那道题是要在他们现有的程序里改一个功能,限定所有人三天改完,并且交付出来。 + +刚好在第三天的时候,我终于找到了要改哪里,其实就一行,那个需求只用改一行代码,关键它可能有上万行代码,你要从这上万行代码里找出到底要改哪一行,但因为不是他们(写的人),我们不懂,也不大熟。反正刚好在第三天,我找到了然后改好了这行代码,我的角色从此就变了,变成了一个专职程序员了。 + +极客时间:这个直属老板你觉得,当时对你是什么样的影响? + +毕玄:这就是机会问题,说白了没有这个机会,你其实走不进这个行当的。非专业的人最大的挑战,一你的学校不够好,二你是非专业的,真的想进入一个行当,你的门槛会比别人高很多。 + +如果你是个名校生,还是学计算机的,程序员的工作随便找,还肯定是大公司,这就像很多人说的我工作很多年以后,才有资格跟你一起坐在那喝咖啡,但人一起步就是那样,我觉得这是事实,必须得承认。所以我跟他说还好当年有了这一次机会,让我正式开始做这个行当。 + + + + + +  + +极客时间:这次通过了考验,你也抓住了机会,成专职程序员之后有比较顺利吗?慢慢通过做项目提升自己的技术水平? + +毕玄:那没有。 + +我的主职变成写代码之后,就跟着他们做佛山的项目,项目做完一年以后就调回深圳,开始做整个深圳市的电子公文交换系统。 + +比如说我这个政府要和另外一个政府部门联合办公,就要发红头文件,之前是纸质的。但广东的电子化非常早,深圳是2003年开始建电子公文交换系统,各家就可以直接发电子公文,我们公司就承担了深圳市的那个项目。但那个项目我参与的很少。 + +极客时间:为什么没有参与进去?应该是个大项目。 + +毕玄:因为那个项目主力成员是写Java的,就这原因。 + +但我的背景对吧,以前学校做网站用的ASP,到佛山之后跟着项目学会了VB、Delphi,因为我们有些是用Delphi写的,有些用VB写的,这两个我慢慢也比较熟。然后深圳比较缺人嘛,把我调回深圳,一看Java,我又傻眼了,我说这啥,好不容易VB和Delphi我已经用得特别熟了,因为真的也特别好用,然后一切到Java,我就觉得这玩意简直烂到极致。 + +因为Java那个时候的IDE,说实话跟VB、Delphi比,即使到今天这俩还是神器,当时Java,哇塞连IDE都找不到,Eclipse是后来的事情,最早Java我们都用记事本写的,哇那效率简直低到离谱,后来才有了一个简单的IDE去学Java。 + +所以我就从那个时候开始学习Java,学这玩意到底该怎么弄,因为我确实外行不懂,所以在深圳那个项目里我就没进入多少,公司有非常主力的Java成员去负责,然后那个项目做成了。其实深圳这段经历让我很难忘。 + +极客时间:难忘的是什么,因为工作需求自己紧张学习新语言?还是说当时自己不太做的上事,心态上可能比较挫败? + +毕玄:因为不像我在佛山,说实话佛山一年我已经混的很熟了,而且已经是那个团队里比较主力的程序员,像公司在顺德这些地方的项目就是我自己去搞的,我一个人,面对需求方,直接改代码,然后交付给他们,可以全部自己干完,所以在佛山过得挺爽的。 + +结果调回深圳后,直接就啥也干不了,全部是别人干,你是一个啥也不懂的人,所以挺不爽的,那段时间本来我想走的,找另外的。但是发现也很难,那个时候我找到华为。 + +极客时间:你去面试了?结果怎么样? + +毕玄:那肯定,那个时候深圳最好的是华为,互联网公司像腾讯我们都看不上的,因为我们这种人在大学多数是做过网站的,会觉得不就做一个网站,能有多难,我几天就可以做一个,腾讯这样的公司,在我们这都是玩烂了的,互联网公司?我们觉得那都是做不了软件的人才去的公司,做政府软件还是要求挺高的。后来我们都后悔了。 + +极客时间:没有早进去(笑)。 + +毕玄:因为我以前的公司就在现在腾讯深圳南山区总部的隔壁,那个时候,园区是没有互联网公司的,全部都是软件企业,至少在圈子里,大家会觉得软件企业的程序员肯定比互联网公司的程序员更厉害。 + +当然这是因为我们这群做软件的人,根本不懂互联网的人面临了什么问题,后来我们了解了之后,才知道,哇还是你们牛。 + +  + + + +  + +毕玄:当时本来我想如果能离开,就去深圳最好的公司华为,我去华为面试了一下,然后面试不上,那我还是继续在现在公司待着。 + +而且我在那家公司刚好有了一个新机会,因为深圳项目快收尾,我第一任老板他也从佛山回来了,他那个时候已经是软件部的副经理,回来以后被任命了一个新项目,中山市的网上审批系统,非常类似现在浙江做的“最多跑一次”。 + +极客时间:材料网上审批这么早,那个时候是几几年? + +毕玄:所以说广东非常领先,那个时候是2004年,当时中山市就决定做。你要去政府办的所有事情,可以在网站上一次提交所有材料,提交完了就到政府内部去流转,流转完了之后,你最后去现场把所有资料拿走就可以了,几乎也只用跑一次。 + +当时我们公司中标了中山的这个项目,我之前的老板就跟我说,要么你别做深圳项目了,跟我做中山项目去。 + +极客时间:你有做过什么考虑吗?还是说很果断就直接过去了? + +毕玄:当然,我们这么熟的人,我当然跟他走(笑)。于是我就跟他去了中山,这个项目在当时是非常大的纯软件项目,我们中标的金额应该在1000多万。 + +但去的时候我们就3个人。 + +很多同事当时是不大想去的,因为我们老是做项目要到处跑,很漂泊,虽然我号称在深圳工作,但你看我在深圳的时间非常短,第一年根本没在深圳,我都在佛山。 + +所以就我们3个人,要去做一个1000多万的纯软件项目,而且我们都不知道是个什么东西,什么都不懂,去了以后就发现有很多问题。比如说我们当时面对了中山市的市长、市委书记和秘书长,这是秘书长亲自牵头的一个重要项目,但当时正逢换届,做的挺不容易的。但也不是坏事,所以后来我们也明白政府项目真的太难做了。 + +极客时间:这个项目可以具体聊聊吗,你当时负责什么? + +毕玄:这个项目金额大,公司给的支持还是很够的,允许招更多的人,但因为我们仨去的最早是绝对主力。我老板本来就负责这种项目很多年了,人脉很广,所以他主要对外搞定各方,另外一个同事负责写更上面一点的东西,我负责写网上审批系统整个大的基础,那个时候我已经偏向写框架了,很类似现在很多公司做的低代码框架。 + +因为我们做的东西是这样,中山市的项目要上线1000多个流程,如果你写代码上线,那做10年也做不完对不对,所以你必须要让别人很容易直接把这个流程配出来,这就需要在下面抽象一个东西。 + +极客时间:需求这么急,项目又很大,当时你们有考虑买个商用的自己改吗? + +毕玄:最早我们当然也准备买商用的,后来觉得不大好用,所以决定自己写一套流程引擎、表单引擎等等所有东西,我就写这个。那个时候我还有一段神奇的闭关体验。 + +因为我们判断这套流程引擎和表单引擎对这个项目至关重要,决定了这个项目到底能不能在2、3年收尾掉,而不是说要很多程序员去堆,最好的方案是一套框架加上大量的实施工程师、配置工程师。所以我们发现了这是核心。 + +然后我的老板就问我,多久能搞完?我说我一个人就行了,给我半个月。于是我就享有了整个项目组最高的待遇,我就圈了一间会议室,那个会议室就我一个人,闭关写了半个月代码,半个月后有了一套雏形,后来很多人就基于这个东西配流程上去。 + +极客时间:就你自己一个人半个月搞定了? + +毕玄:对呀,我就在那里面,吃喝什么的都有人送过来,全无打扰的环境,过的很爽(笑),写完了之后,反正也基本能用,他们后来就边用边改,撑住了系统后来的整个实施过程。 + +  + + + +  + +极客时间:但是当时写的时候,没有遇到什么难点吗?毕竟这应该是你第一次做这么大的项目。 + +毕玄:其实还好,因为工程说实话没那么难,很多只是抽象的问题,写计算机程序其实是个逻辑过程。 + +极客时间:写代码是逻辑过程我大概能明白,怎么理解是工程?是抽象问题? + +毕玄:就是数学。我现在特别理解写程序的人有两项技能特别重要。一是英语,这没办法,因为你看的大部分资料是英文,你写的代码也是英文;第二个是数学,写程序其实就是一个数学过程。 + +数学是一个什么过程呢?是我有一个问题要求解。程序也一样,程序是我有个问题,然后求解,所以程序的整个代码就是解问题,就像以前数学题我们写第一个字“解”之后的所有部分,那就是个逻辑。 + +对数学特别好的人来说,数学题可能会变,但他能从变化中找到一个规律,抽象成一个函数,变成一个公式,核心问题是举一反三。为什么高斯特别牛,从1加到100,首先他找到了一个规律解答了1加到100等于多少,但在这个基础上,他还能把1加到N变成一个公式等于(N+1)*N/2。这就叫抽象能力,他可以把一个问题抽象成一个公式,然后这个公式可以运用在所有场景里。 + +其实写程序就是这个过程。不好的程序员是有一个问题就解了,解完了以后问题稍微变一下,这个代码就得重写;但写的好的是看到这个问题,我可以做一个抽象,之后不管上面怎么变,我可能只需要换换参数之类的,但代码是不用改的,就能完成所有需求。 + +极客时间:所以像你当时在那个项目里负责写框架,就会更需要抽象一些? + +毕玄:对,我们写底层框架的说白了就是这个过程,底层框架为什么相对更难,是因为它对抽象能力的要求比上层代码的要求更高,上层是我面对一个需求,做到就可以。 + +所以做一个程序员不难,做一个职业程序员其实很难。程序员就是我有一个需求,然后翻译成代码而已嘛,这个东西小朋友确实能干,所以现在少儿编程很多。我就跟我家小朋友说你们学的那些东西,只能是写着玩,想拿这个当饭碗,少儿编程学100年也不会变成职业程序员,因为职业程序员学的东西,在那个阶段你是根本体会不了的。 + +但是如果你数学很好,你写代码的逻辑性、抽象能力会非常好,其实后面自然能领会,因为你想,写代码我们就是写一个方法,方法是什么?方法不就是个函数,一个典型的数学函数。 + +当然也不一定就能成为,毕竟你后面还有很多挑战,但至少有一个基础在,离成为职业程序员更近。但数学不好的人,说实话我们觉得是不合适做程序员这一行的,因为你逻辑性不够,抽象能力不够,就导致你写的代码经常要改。 + + + + + +  + +极客时间:最开始我们聊的时候提到非科班的程序员和科班程序员的区别,我记得你之前还特地写过一篇文章讲业余程序员和专业程序员的区别。这两种区别,是一样的吗? + +毕玄:不是,我是非专业的系,但我应该是职业的,就像现在大家都觉得好像是个人都能写程序,这我不否认,每个人都能写程序没错,多数人只是把这个问题翻译成了代码而已。 + +但是这段代码要变成能稳定执行的商业性的代码,这是职业程序员才能干的。说实话这我完全不相信任何人都能干,所以我最讨厌说什么人人都是程序员(笑),太鄙视这个行当的专业度了,其实每个行业都有专业度,有人觉得到最后AI可以替代所有,但那个真正的专业度,我觉得还是有一定难度替代的。 + +所以做中山市项目那段时间写偏基础的东西,对我自己还是有锻炼的,没有碰到太大的困难,可能因为我的数学比较好。一直以来我只有两门好,一门是英语,一门是数学,语文和物理、化学都非常糟糕。 + +极客时间:根据你的理论,那你可能注定走上程序员这条路(笑),有一点好奇,你数学好,为什么物理没有那么好? + +毕玄:我也很好奇。大家都认为数学好,物理一定好。我后来想因为物理是解释现实世界规律的,数学不是,数学其实是个工具,所以很多物理学家同时是数学学家。但物理我就很难理解,我的物理和化学都在及格线上徘徊,但数学就是在满分线上徘徊。 + +不过说实话我认为跟老师也有很大关系,因为后来我们在阿里听过一次北大前校长讲物理学,他是物理系出身的,讲完之后大家都觉得哇物理太有意思了,我说以前自己要是遇到这样的老师,物理至少应该不会那么差。 + +其实生物也一样的,最近我在看很多生物的东西,就觉得很有意思,一些非常知名的教授讲生物研究是要去解决人类的什么什么问题,你就会觉得,哇如果我真的能研究出这种问题,那在人类历史上,跟计算机系的人根本不是一个档次的。我就跟我家小朋友说,如果我以前看到的是这些,我应该不会去做计算机这行。 + +因为说实话我现在会觉得计算机只是个工程,你可以认为计算机是个工具,但对人类不会有根本性的变化。但生物、物理,可能会给人类带来非常巨大的改变,而且它有很多问题要解决,但计算机说实话对那些人类问题没有任何帮助,可能是个好工具可以帮我来让那些问题解决掉,但它只是个工具而已。 + +  + +水友讨论区 + +今天的对谈就暂时结束了,针对毕玄大学毕业之后找工作的经历,我们林林总总聊了很多,不知道有没有引发你对他之后经历的兴趣。 + +虽然是生物系毕业,但大学四年他基本都在玩计算机做了很多商业项目,也能算一名准程序员了,即使这样,找第一份工作也非常难,哪怕找到了,想正式成为程序员也是抓住了稍纵即逝的机会。想想大佬起步居然也这么难,突然有点安慰了:) + +“啥也干不了”的艰难开局后,让我印象最深刻的是聊编程、计算机和职业程序员的部分,毕玄说职业程序员是需要专业度的,但计算机只是个工程,是个工具。 + +不知道你最感兴趣的是什么,这里我列了几个话题,欢迎自由讨论: + + +“非专业的人最大的挑战,一学校不够好,二你是非专业的,真的想进入一个行当,门槛会比别人难很多,是个机会问题”,你的第一份工作是怎么找的?是非专业吗? +有人说人人都是程序员,毕玄说写计算机程序是个逻辑抽象过程,但职业程序员是需要专业度的,你怎么理解“职业程序员”? +毕玄现在觉得计算机只是个工程,是个工具,关于计算机在人类社会的作用,你的想法是什么呢? + + +如果你有其他更有发言欲的话题,欢迎交流,期待和你在留言区碰面。 + +下一讲我们接着毕玄在小厂做项目的波折经历聊,下讲见。 + +拓展阅读 + +1. 毕玄很早就开始写博客了,考古挖到了他工作三年写的复盘:程序人生(工作三年的回想) + +2. 如果你对业余程序员和职业程序员的不同感兴趣,可以看毕玄后来写的一篇文章:从小朋友的一道数学题聊聊职业程序员 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/02小厂创业:做出一个产品,卖给所有人.md b/专栏/超级访谈:对话毕玄/02小厂创业:做出一个产品,卖给所有人.md new file mode 100644 index 0000000..1843eec --- /dev/null +++ b/专栏/超级访谈:对话毕玄/02小厂创业:做出一个产品,卖给所有人.md @@ -0,0 +1,200 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 小厂创业:做出一个产品,卖给所有人 + +你好,我是叶芊。- + - +上一讲我们聊到毕玄艰难的非专业找工作经历,这个大学不怎么样的生物系青年是怎么毕业后成为一名程序员的,今天接着他在小厂做政府项目的经历聊。- + - +因为剧情太过跌宕,忍不住简单剧透一点点,在好不容易接了个千万级的大项目之后,他又想跳槽了,这次连简历都没写居然跳槽成功,之后又开始了一段神奇的创业之旅,但创业一年后,他却表示:太没意思了,不如回去打工算了。- + - +到底发生了什么?我们开始今天的对谈。 + + +  + +极客时间:做了佛山、顺德这些地方熟悉了VB,但是深圳项目又要从0开始学Java,所以你本来想走,结果峰回路转有了做中山1000万大项目的机会,做完了之后呢? + +毕玄:中山做了两年多我还是想走了,因为我一直漂泊,很不爽。 + +我们公司的项目就是这样,除非做深圳项目就在深圳,其他地方的一定在外面。当时,我们公司在广东的电子政府领域比较有话语权,因为做了深圳、佛山、中山,除了广州,这基本就是广东经济最好的三大城市。所以公司当时更希望继续拓展广东的其他城市,那就意味着我八成继续在外面。 + +极客时间:你说的“拓展其他的”会有点重复做的意思吗?这样你的个人发展可能不是太好? + +毕玄:会。一我觉得不想做项目,因为做项目太漂泊,二我觉得项目这个东西太不规模化了,我们每做一个项目都是重来。 + +尽管我们当时也希望,像很多软件公司,比如做了A政府,做完以后沉淀成一个产品卖给B,但是说实话,我当年做过以后就知道这条路是走不通的,绝对不可能,今天也是这样。 + +因为说白了是不能复用,A做了这个东西取得了一定的成绩,B想的并不是拷贝这套,他一定会改,想要在这个基础上创造一个新的东西,否则的话,请问你的创新在哪?所以现在做项目讲这个故事的人,只能说应该还是做得太少。 + +极客时间:这是做政府的,那做公司的是不是也一样?都是ToB的大思路? + +毕玄:办公这种系统有点不同,但如果是涉及它业务的也差不多。你卖给我A公司一个业务经营的软件,如果跟卖给B一样,请问我跟B怎么竞争?我要你做的就是不一样。所以最后,你就变成了一个大型交付公司。我觉得这是突破不了的,不可能改变。 + +中国做很大的像东软,你想东软难道没想过这个问题,肯定想过对不对,大家谁也不傻,他们难道想一个项目一个项目做吗?肯定不想的,他们当年肯定想过怎么把这个东西变成一个产品,或者说至少变成相对标准化的,去另外一个可以很快交付掉的。 + +这类公司中国有非常多,我相信他们一定探索过。因为他有这么多项目,理论上大家会觉得以前为什么做不成,是因为项目太少所以抽象不够,如果有100个项目我一定能抽象出一个东西。 + +极客时间:但是这个说法,大家好像都在这么说,也都这么认为。 + +毕玄:这基本不成立的,后来最多做到OA这玩意可以标准化的卖,但实施难度也很大。前期大家讲故事的时候说,要不断做项目,最后可以有个抽象,但后面很难。 + +所以你看做这些软件企业有很多家,从我们当年到今天,这些公司很多都还存在,但说实话没有多少是发展非常好的,东软比较大,然后泛微,就这几家,东软那么多的人,你说他有收入吗,他当然有,但你说他有多赚钱、体量能做到多大,就没有。就他其实没有成为非常好的行业。 + +极客时间:这是结合你当年经历的总结,我们看现在这种类型的创业,你觉得还是这样吗? + +毕玄:现在尤其很多技术人,出来创业先做政府项目,一开始这一点他们是完全没有搞明白的。我们做的多了就知道一上来,一定要先摸清楚项目需求到底是什么情况?为什么要做这个项目? + +除了诉求,你还得搞清楚各方的利益关系。因为一般项目时间很长,你还有周期,但凡不是一两个月能交付完的,跨年了甚至半年以上,还会有人员变动,那你就相当于有另外一个人再过来接你这事,他俩如果目标不一致,重新来一遍,需求重新提。 + +极客时间:噢就是要想清楚做产品的出发点。所以像现在很多人会纠结,为什么我的产品这么好,但客户不用,是不是没太想清楚的一种表现? + +毕玄:这就是诉求没搞清楚,因为客户的诉求可能跟你想的完全不一样,你是去解决他的问题,如果没有搞明白他的诉求,你瞎搞,客户反而很烦。 + +以前听别人聊,说有些客户的需求会非常奇怪,他对项目的效果没有任何诉求,目的其实是想跟你联合做个PR,他认为给你了比如1000万只是买个广告费而已,反正投放一个广告也要这么多钱。我们听到这个逻辑都震惊了。 + +但是你如果再想一步就很明白了,因为有些企业的行业竞争非常激烈,他们会觉得如果能跟你合作一把,可以塑造成我是科技典范、先进企业,我上下游的其他企业就会这么认为,然后我的订单量就增加了。所以他们其实根本不在乎这个项目,因为他获得的订单远比这个数量要大很多。 + +那如果你是这个项目的PM,是不是很爽?因为客户对项目没有要求,所以你正常流程做,验收完就收到了钱。难道不应该感谢这种客户?难道你们一定要做一个多牛的东西? + +所以乔布斯以前才说绝对不要做to B,他只要做ToC,ToC反正就是自己说的算。 + + + + + +  + +极客时间:好,我们聊回你的经历,当时你决定不做项目了,去面试了什么新公司新岗位吗? + +毕玄:当时我最想找的是做产品,不是做产品经理,我想做一个写产品代码的。所以我的诉求就是找一家能做产品的公司,但是没有面试,也很神奇。 + +当时大概是2004年我想换工作,开始找机会,我2003年就写Blog了,那个时候写博客最火,我以前写了挺多的,正好有一个人看了,然后那个人是上海一家公司的软件部经理,他看了之后觉得要不聊聊,就这样我去了上海。 + +那家是上海电力集团下属的一家生产企业,理论上是专门给电力做软件的,但它有点特殊,它除了给电力做,还希望拓展到非电力的行业,所以希望做一个产品能够覆盖其他的行业,叫协同办公,类似钉钉。办公这个行当其实很多年了,因为办公是刚需。 + +极客时间:在办公领域,当年和现在只是解决方案不一样? + +毕玄:只是解决方案在不断地进化,越来越好用了,因为以前都是PC,现在是移动。所以那家公司决定做这个产品,就说让我去,并且让我做整个产品的架构师,那我觉得这个位置太好了,是我梦寐以求的,又满足了做产品,还能做架构师,太好了,所以我就去了。 + +极客时间:那很好啊,你没面试,进去还是核心岗。 + +毕玄:但第一天简直就是悲剧(笑),我现在都记得。 + +因为去了之后,我周五报道的,报道完老板跟我讲,要我下周一出这个产品的架构设计思路,我想架构设计思路?啥玩意?我都不知道这个东西是什么,然后我就开始瞎编。 + +压力很大,但反正最后也给了一个什么东西乱七八糟的。然后我就开始负责这个系统的架构,以及核心的代码,都是Java的,从中山项目以后我就已经走向Java体系。 + +但产品真的太难做了,一家公司能下定决心做一个产品是非常难的。他们投入一段时候之后,觉得做了半年好像也没有出啥东西,所以他们反思还是做项目比较好,于是我们又接了一个项目,东风汽车的项目,在武汉,不在上海,我又带着几个人在东风驻场做了接近一年,唉我实在是抗不住了。 + +极客时间:所以你本来想摆脱做项目,才选择到新公司做产品,结果阴差阳错又回来了(笑)。 + +毕玄:尽管我变成了整个项目的负责人,面对的关系更综合一点,角色比以前更高,但做项目这种经历我真的太受不了了。 + +极客时间:那你当时又准备走了? + +毕玄:当时也很巧,我做了半年多,我的第一任老板决定出来创业。 + +极客时间:就是那个给你机会的经理吗? + +毕玄:对,他就跟我说一起创业吧,我就去了。 + +我本来就想换工作,东风项目快收尾了,我觉得也没什么意思,因为这样下去看来公司也不大可能继续做产品,可以理解,公司尽管有电力的背景,但还是会考核一年的营收,毕竟也是投入嘛。但如果你要专注投一个产品,真的要投挺久的,2年多很正常,投几十个人算下来钱也不少。很多公司就是这样,现在也一样,很多真正有壁垒的产品是它投入了很多年,外面的人想抄就要投很多的钱,这是壁垒。 + +所以,我就觉得既然他想创业,我们关系又很好,正好我干的也不爽,干脆就回去跟他创业去了。 + + + + + +  + +极客时间:当时你们创业是想做什么? + +毕玄:创业我们的目标就是继续做政府软件,因为我们之前做佛山、中山、深圳,我老板其实一直是最重要的负责人,跟政府各方都很熟,所以我们觉得应该可以做吧。这就是第一次创业的人,啥也不懂。 + +开始我们有3个人,我写代码,他各种都干,另外还有一个专门做商务的人,本来我们认为佛山、中山应该有些机会,但后来发现政府招标对资质有很多要求,但创业公司最难的就是资质,短时间也很难有。我们仨搞了将近一年,觉得太难了。 + +后来就去找了以前的朋友,他们是中国做公安非常大的一家,就给了我们一些项目,都很神奇。 + +极客时间:公安的项目?那是做什么的? + +毕玄:当时刚好是中国做二代证系统,我们负责做公安系统,就是把一代证的信息转移到二代证上的中间系统。这个可搞笑了,出了很多问题。 + +因为我们的代码能力尽管是有,但说实话,还是不大行的(笑)。基本一出问题,我就得在公安局呆着,因为我们公司也没几个人,就是我飞到各处,现场改代码处理各种问题,所以那段时间我更惨了,全国跑,在内蒙古最冷的时候我去做了呼和浩特的系统,然后甘肃、兰州也待了几个月,还有云南、昆明,我全部去的这些地方。 + +就那一年,我们做了一些相对偏远地区的公安系统。最终我们3个人都觉得,这玩意没戏,做不成,因为那个时候我们的梦想其实也是,做出一个产品,卖给所有人。 + +所以现在我跟所有在行业软件这个行当(这和通用性的软件例如CRM等不一样)创业的人都说,我真的不相信能成,除非软件有很革命性的进步,如果没有,你们做的方法跟我们当年其实没有太大区别,那为什么当年做不成,你们现在就能做成了呢。 + +别幻想这些故事, 不过因为这个故事是所有人的梦想,换一帮人,这个梦想还是可以讲下去的,但你没有真正做过这个行当的话,是不知道它背后很多东西的,你会觉得它好像是成立的。 + +  + + + +  + +极客时间:这么看感觉做不下去了,后来呢?你们就散伙了? + +毕玄:我们3个人做了接近一年,觉得这活肯定干不下去,也不可能做大,那有啥意思,不如大家回去打工算了。就在我们在要回去打工的时候,突然接到了一个神奇的机会,那个机会就是互联网公司,500彩票网。 + +500网当时是中国最大的彩票网站,准备冲刺上市,他们觉得原来的那套系统技术不大行,就希望有一个全新的团队,来写一套全新的系统彻底替换掉原来的那套。 + +他们的负责人刚好跟我老板比较熟,就说要不你们也来,当时我们创业反正也觉得干不下去了,我们觉得行,可以干,就去了。 + +就因为干这个项目,我才懂了互联网非常有技术含量。因为那个时候团队里有些是腾讯出来的,就跟我们讲为什么腾讯QQ是难做的,并不是大家想象得那么没有技术含量,讲当规模上来的以后,你会碰到什么问题,讲腾讯是怎么解决的,我们听完觉得那确实挺牛的。 + +所以我就去了,但这个项目因为主要是腾讯的人,而腾讯的主力语言是C。 + +极客时间:啊第一份工作被迫学VB,后来到Java,现在你又要学C了。 + +毕玄:我又悲剧了,但因为我Java已经做了那多年,不想再换语言了,那在这个团队里我到底能干啥呢?所以后来我就干了一个非常神奇的角色,做了这个项目里的DBA。 + +极客时间:但之前你也遇到相似情况了,VB和Java都学了,为什么当时就不想再学C了? + +毕玄:一是我不想换语言,另外这个团队里C背景的人也够多了,都是腾讯出身的,技能非常好,他们相当职业,而且这伙人见过当时中国最大规模的网站,我临时学一下能跟他们比?这个难度有点大。加上项目时间要求非常短,可能只有几个月,所以要的是大家尽量快。 + +我想了下,没有别的能干,就数据库没人干,好吧那我就干数据库,因为完全不懂,那个时候就开始每天学数据库。 + +极客时间:那之后呢? + +毕玄:做了几个月,本来这家公司上市的流程都快走完了,在美国IPO几乎没有什么问题了,但在就要上市的前一个月,中国颁布了一个规定不允许在互联网上买彩票。 + +说实话我们本来打算冲刺上市,获得一笔钱然后走的,没准备在这家公司待太久。这样一来觉得上市没有希望了,大部分人当然就撤了,我也在其中。 + +我走的原因很简单,一我也不想做数据库,这显然不是我的长远方向,说实话本来就是一个短期的活,第二我肯定想回到做Java的工作,然后也开始觉得互联网是一个很好、很有技术含量的公司,所以我希望找一个做Java的互联网公司。这就是2007年了,我年底加入的淘宝。 + +- +  + +水友讨论区 + +到这里对谈就暂时结束了,今天我们主要聊的是毕玄正式成为程序员之后做项目的故事,不知道有没有引发你的思考。 + +从毕玄写千万项目的底层框架开始,我就隐约有了大佬崛起的感觉,毕竟他说写程序其实没什么难的,就是工程,是抽象问题,果然老做项目的他觉得成长不好,开始找新工作了。虽然找新工作非常不顺利(又有一丝安慰;)),但是从曲折的经历中,他也深刻地明白了做项目和做产品的区别。 + +不知道你对今天的对谈最感兴趣的是什么,我列了几个话题: + + +有人说客户需求是个哲学问题,而不是与客户沟通的问题,不是客户提到的就是需求。你做过ToG/ToB的项目交付吗,感受如何? +对于“抽象成产品,卖给所有人”的梦想,你的看法是什么?你觉得未来会向什么方向发展呢? +毕玄的语言使用经历是:大学做网站的ASP -> 做项目学的VB/Delphi -> 第一眼觉得简直烂到极致Java -> 彩票网站被迫用的C,不同的语言,你在工作中学习/使用过哪些呢?换语言的体验如何呢? + + +欢迎在留言区参与讨论一起交流。下一讲我们会接着毕玄进淘宝的经历聊,下一讲见。 + +拓展阅读 + +如果对毕玄当年能吸引到offer的博客感兴趣,你可以看看这几篇:- +1. 项目杂感- +2. 做家中间件厂商到底有多难- +3. 产品规划- +4. 质量和快速决定了软件架构 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/03淘宝HSF:能让淘宝出重大故障的就那批人.md b/专栏/超级访谈:对话毕玄/03淘宝HSF:能让淘宝出重大故障的就那批人.md new file mode 100644 index 0000000..c92114d --- /dev/null +++ b/专栏/超级访谈:对话毕玄/03淘宝HSF:能让淘宝出重大故障的就那批人.md @@ -0,0 +1,228 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 淘宝HSF:能让淘宝出重大故障的就那批人 + +你好,我是叶芊。- + - +上一讲我们聊到毕玄加入互联网公司的经历,为冲刺上市的彩票网站写新系统,因为自己不会C语言,也比不过腾讯那拨人,迫不得己成为了一名DBA。但因为突然的规定上市无望,他也走了。- + - +后面发生的事情,说实话我只能说“很神奇”。- + - +就他准备找新工作的时候,在没写简历没主动面试的情况下,居然又有一根橄榄枝神奇地伸了过来,然后在一场很神奇的面试后,他进了淘宝,结果第一个项目就神奇地差点把淘宝搞挂了……- + - +到底发生了什么?我们马上开聊。 + + + - +极客时间:2007年底你就去阿里了,当时你是怎么进去的? + +毕玄:也是博客,我到上海工作是因为写博客,换到阿里很大原因也是这个,当时我在网上写了OSGi那份文档,OpenDoc,那个时候可流行开放性的文档。 + +极客时间:这篇文档影响力很大,你写了多久? + +毕玄:一点点积累的,前前后后可能有半年多。 + +极客时间:半年?这么久? + +毕玄:对,就写了那篇东西,写了之后技术圈的关注确实比较大,最早我们觉得产品化的基础可能是基于OSGi做,因为Eclipse那个时候已经是Java的垄断IDE了,而Eclipse的底层是OSGi。 + +当时Eclipse把OSGi把插件化的整个体系讲得,你如果听一下思想都会觉得太完美了,而且有Eclipse展示,相当于有了落地,还不光是一个概念,所以大家都觉得哇这是革命性的,基于这个抽象有可能做成产品,只是后来论证了一些东西,不是这样。所以当时OSGi在国内的关注度非常大,加上又没有文章,我写了第一个,很多人可能就因为这个知道了我。 + +极客时间:那个思想是什么,可以具体讲讲吗? + +毕玄:从业务上讲,以前做整套系统想做成产品,产品化的核心思路是复用,但你不大可能把所有东西都做了,那最好的方式肯定是插件,A、B如果有不同的需求,我做个插件,插在原来的上面做扩展就好了。 + +而Eclipse是完全基于插件体系的,在里面如果一个功能没有,你装一个插件就有了,然后插件还可以扩展,你想跟别人的不一样,改一下代码就可以,还只用改部分。我们觉得这是一个完美的思路,把很复杂的东西抽象掉,做插件、做扩展。 + +其实到今天为止,所有的扩展型系统仍然是这么构建的。只不过OSGi可能太复杂了,但它的思想其实被用在了所有系统上,现在想产品化的公司,面对复杂系统,还是插件、扩展点的套路。 + +我们当时觉得这个方向是有可能的,Eclipse也给我们展现了一个完美实践,所以我们说应该研究下来,然后我也比较感兴趣,也觉得这个东西应该就是灵丹妙药。中国又没资料,就开始自己研究了,加上我确实比较喜欢写文章,写上瘾了,就有了那篇非常长的文章。 + +极客时间:所以你写出影响力之后,你是怎么去淘宝的?像以前一样有人看到你的文档被联系过去的? + +毕玄:写完之后,有个人叫曹晓钢,他做了一个网站叫满江红,以前很多人会用,包括我们搞Java那帮人,那篇文档放在了网站上,可能中国学习OSGi的人都看过了,所以我跟晓钢就非常熟了。 + +当时比较凑巧,刚好我准备找工作的时候,晓钢就问我要不要考虑下淘宝。因为那个时候他的一个朋友刚刚加入淘宝,那个朋友就是菲青。因为菲青中国朋友比较少,很幸运晓钢就是他的其中一个,菲青刚刚加入淘宝在招人,也找不到别人,就问晓刚能不能推荐几个,就这样我被推荐给了淘宝。 + +然后我就到杭州面试,说实话那个时候我们不觉得淘宝名声很大,虽然有一定名声,因为它刚刚打败eBay,听肯定都听过,但觉得好像也没啥。出名的是老马。因为马云在前一年上了创业的节目,在那个节目里风格非常犀利,圈粉无数,那我觉得很好啊,就到淘宝面试。 + + + + + +  + +极客时间:你被推荐过去之后,然后就顺利进去了? + +毕玄:没有,进淘宝很狗血的。当时晓钢还问我面的咋样,我说我觉得不咋样。 + +面我的第一个是黄裳,淘宝比较老的一个工程师现在蘑菇街的联合创始人,黄裳第一轮,菲青第二轮,第三轮是马钰。 + +那个时候黄裳问我很多问题,我都不知道,菲青问我,我也很多都不知道,马钰问的时候我就更懵,因为他是带搜索团队的,问很多算法,算法我不懂,他让我做道题,也没做出来。后来我就说你们到底是怎么让我通过面试的(笑)。 + +但是最后很神奇的是,等面试结束的时候,马钰居然问我要不要考虑一下加入他的团队,我都震惊了,我是来面菲青的团队,然后马钰说你要不要来我们这。当时我想,啊?我题都没做出来,你们为什么让我加入?这什么团队。所以很搞笑的。 + +最后当时淘宝整个软件技术部的老大空闻面我,空闻说你前一年不是在做数据库吗,现在为什么面的是Java工程师?空闻简直震惊了。反正很神奇的,我就这么加入了淘宝。 + +极客时间:所以你到底是怎么通过面试的?时机原因吗? + +毕玄:后来我们一帮当时加入的人都开玩笑说,当时的淘宝估计是你只要投简历,就会招你,根本就没什么面试流程,就是个形式。这可能真的就是运气,但很多公司都这样。 + +因为07年愿意来淘宝的人非常少,包括我们去校招,淘宝绝对是阿里集团里最后一个被选择的。到后来我们才有了很多的要求,淘宝是09年经历的大规模发展,整个架构做了演进,就会有很多要求,比如希望你做过大型系统。 + +这像我们07年进来的这批人就废了,应该是进不了淘宝的,会被碾压,做过大型系统?没见过,并发量上十万的都没见过,更不用说其他非常非常多的要求了。阿里后来招的人,实践经验简直能亮瞎眼,本科就已经非常厉害了,不用说像硕士、博士,但这是阿里很正常。 + +极客时间:除了你说的运气,应该也有你写OSGi文章的原因吧? + +毕玄:应该有,后来我跟黄裳他们都说,你们招我,八成是因为OSGi吧。因为他们面试问的很多跟淘宝当时用的整个技术体系相关,淘宝当时又要做服务化,他们觉得这些你是不懂,OSGi你懂啊。但是问那个,他们就不知道我回答的怎么样,因为说实话我答的对还是不对,他们也不知道,只是觉得你有一个点是非常专业的,所以也可以(招进来)。但肯定不是因为我对淘宝多了解。 + + + + + +  + +极客时间:好,我们接着你进淘宝之后的经历聊,你是一进去淘宝就开始做HSF了? + +毕玄:对,因为OSGi是一个偏服务化的东西,淘宝当年最重要的策略就是做服务化的架构演进,只有我是有这个背景的,所以进来之后,我就很自然地负责做这个东西。 + +但问题是,其实我也没做过,而且我们那帮人都没做过大规模的系统。我当时面临的第一个问题是技术选型,但我们根本不懂大型系统的技术选型挑战是非常大的。 + +以前我做政府,说实话随便选好了,更多的是看功能,但互联网更多的是看性能,功能对互联网来讲一点都不重要,越少越好,但性能是核心。所以我进来做的第一个技术选型后面就是一个巨大问题。 + +极客时间:当时你做的选型可以多讲下吗? + +毕玄:我们做HSF,第一个要选的是通信框架,淘宝最早是WebLogic,菲青进来以后再换成了Jboss,我进来之后,因为菲青用JBoss,就决定也选Jboss的通信框架,把Jboss的老版本升级成新版本,顺带把通信也基于Jboss来做。这就是我们以前的选型(笑),所以后来我们会不断地教育新人,不要瞎来,有非常严格的规定。但以前互联网可能大家都不懂。 + +所以我们就选了,做了第一个版本上线,这是我面对的第一个大规模系统,日均访问量是200万。我以前做的10万不到,一下直接200万。那个时候所有人都会告诉你做一个网站,访问量10万、100万、1000万、1个亿、10个亿是不一样的,但没有人告诉你到底哪里不一样。 + +因为当时中国没有圈子,就很窄。但国外就有很多,QCon以前在国外的大会有很多互联网公司讲他们的技术,非常适合中国做互联网的那帮人,这点必须说QCon是真的打开了中国交流的圈子,所以我们以前都是看国外的东西。 + +极客时间:所以你上线就出问题了? + +毕玄:没有,我当时上线以后很正常,所以突然间我就觉得,完全感受不到你们吹嘘的到底有多难嘛,你们是不是在忽悠我啊。我就信心爆棚了,决定不要上这种不那么重要的系统,因为当时上的系统是给阿里的客服小二用的,出问题相对还好。 + +我们就决定直接上阿里最重要的系统,交易系统。当时交易系统日均访问量大概是1个亿左右,相当于从200万到1个亿的跨越。结果上线的当天就出问题了。 + +当时发布的时候,大家都去千岛湖Outing了,就留下我和黄裳俩人,因为大家觉得应该也不会有什么问题,所以你们俩就留下发布吧,那个时候发系统都是半夜,不是白天。我们俩半夜开始发系统,发完了以后回去睡觉了。 + +一大早我就接到监控的电话,说整个淘宝现在很诡异,也说不出来哪里诡异,好像比平时慢一点,就是这个描述。 + + + + + +  + +极客时间:接到监控的电话,你们就赶紧去公司查问题了?查了多久? + +毕玄:当年不像现在处理故障有一套专业的流程,而且要求响应非常非常快。当时我们觉得都无所谓,我和黄裳就去公司查,从早上查到晚上。所以后来我们在内部分享的时候都说,按我们当年处理故障的状态,都被阿里开除无数次了(笑),因为现在你必须立刻回滚掉,但当时我们俩觉得,不要回滚,先让我们查一下问题再说。 + +关键是我们从早上查到晚上都没查出问题在哪,但晚上的时候已经非常严重了,淘宝已经快打不开了,支付宝那边已经快挂了。我们俩也抗不住了,猜想八成就是HSF,虽然业务代码也改了,但换的核心就是这里,所以我们就把HSF回滚掉了,换成了原来的方式,一切恢复了。 + +然后我就悲剧了,这不用说压力就非常大了,那个时候我入职4个月,刚过试用期。 + +极客时间:有没有转正? + +毕玄:已经转正了,但也压力很大,毕竟出了很严重的问题,而且就我一个人做这个系统。我就下来开始仔细回顾整个系统,慢慢查,但因为已经回滚了,也不纠结啥了。后来我们就发现了只是一行代码。 + +极客时间:查了多久? + +毕玄:可能查了一两个星期。 + +极客时间:两星期?不会很焦虑吗? + +毕玄:心态都还好,我可能觉得不管怎么样,反正先查出问题,其他的后面再说,而且不至于说我出了这个故障就被开掉了,这菲青肯定还会给我机会。但下一次压力可能就真的很大了,因为下一次说实话是不能出问题的,再出问题我觉得我自己也不好意思呆下去了(笑)。 + +极客时间:刚转正就出现了这么大的故障,可能你属于比较冷静的。 + +毕玄:我觉得看得开就好了。 + +因为我们后来发现有些人会想太多,比如做一件事情,就想着我要通过这个事获得什么,那八成是会做的乱七八糟,出了故障就想会不会被处罚,对晋升、奖金有没有影响,老想这些,那肯定没法做了。如果你什么都不想,先搞定它,剩下的事情等搞定再想也不迟。关键是,你现在想也没有用,你想不会改变任何东西,先安心干好你的活不就好了。 + +极客时间:面对重大事件,你的这种心态,你觉得和之前自己做项目的各种坎坷经历有关吗? + +毕玄:那我可能高中就是,高中考大学不就是重大挫折嘛。因为我们高中是全省能排前几的,尤其我那个班,可以认为是超级重点班,只要能进那个班,基本都能上重点大学。当时我们每届都有奥赛金牌之类的,上一届还有省状元。 + +但我觉得这是个接受问题,因为我中考进我们班的时候是正数第8,但高中第一次考试,我就已经二十几名了。 + +极客时间:二十几名之后你是选择接受,不是说“不行,我得整回去”? + +毕玄:你当然会努力,但努力一两次之后,你就会发现你得接受。因为我的几个好朋友,成绩非常非常好,但他们一点都不努力的,根本不读书,上课听一下,下课从来都在玩游戏,但考试每次都能考前几名。所以我说这是接受,很多人可能接受不了,觉得好像很怎么样。 + +就像我的好朋友,他高中一直考第一名,高考的时候全校都在看他有没有机会得省状元,最后他稍微有点失常,但应该是全省前十,就去清华了,后来我去清华找他聊天,他就说他终于懂了,我们高中的时候是多么痛苦。 + +极客时间:他也像你们高中一样,发现自己学不过别人? + +毕玄:对,因为他进清华,第一次考试就是班上最后10名,他一看别人成绩这么好,就变得非常努力,每天早上7点就去图书馆学习,一直学到晚上。但即使这样,他一直都在后20名。 + +极客时间:可能他也需要适当接受。 + +毕玄:对,他就接受了,因为他看到前几名也是那样,跟他高中一样,也不怎么读书(笑)。 + +所以是心态,以前我们总说“但行好事,莫问前程”,当然面对重大问题的时候另外有抗压性的问题。 + +但抗压很难训练,我总不能给你造个故障,这对公司影响太大了,到底怎么训练面对这种重大问题,一个人的反应是什么。当然阿里后来有很多故障演练,你也不知道那个故障是真的还是假的,会故意出现一些,真假混合,就看你的反应。我们确实会发现不是所有人都适合的。 + +极客时间:面对故障,不是所有人都适合抗压,那有没有人因为这个离开了? + +毕玄:也会有的,但不多。因为说实话,真正要面对这种压力的人。 + +极客时间:人都是很少?如果能让你去做这么大的系统,他一定是经过检验的。 + +毕玄:对就是少,你说对了。真的让淘宝整个网站出严重故障的人,说实话也就那批人。 + +那批人,很多都能接受是因为觉得就算出故障然后被开掉了,那又怎么样,我还是能找到一个工作,而且不会差,就会觉得无所谓了,有啥好纠结的。说实话,很多人担心还是因为不够自信,害怕如果被开掉,可能就怎么样了。但是不害怕的都是那批,大不了就开掉,这就是最差的结果,这我都能接受,那我还怕什么。 + +极客时间:这是当年,有点好奇现在呢,能让淘宝出重大故障的人? + +毕玄:现在就不好说了。因为公司大了,出故障的影响面不一样,你现在出故障可能是真的会受到一些处罚。但以前没有处罚,以前尽管号称有,但也不会多严重,因为我们也确实被处罚过,包括绩效等等会受影响,反正大家看得开。 + +  + +水友讨论区 + +今天我们主要聊的是毕玄进淘宝,以及做第一段专业领域的系统设计的故事,不知道有没有引发你的一点思考。 + +之前在知乎搜毕玄,在一个不那么正经的帖下面找到了他相当正经的一段发言: + + + +当时我还将信将疑,但在今天了解了他07年的淘宝面试经历后,疑虑打消了,可能当年那波公司都有一段这样尴尬的时间,之后应该也会有。 + +进淘宝之后,毕玄做的HSF上第一个系统大获成功,于是这名热血青年信心满满上了1亿日均访问量的交易系统,结果被故障狠狠教训了一把,差点达成进厂就被裁的成就,但他表示稳住心态,先安心干好活,没有什么好纠结的。 + +不知道你对今天的对谈最感兴趣的是什么,照例是自由讨论环节: + + +OSGi的插件化思路,你是怎么理解的?在哪些系统里感受到这种设计思路吗? +工作中你出现过严重的故障吗?影响如何?当时自己怎么处理的呢? + + +欢迎留言参与讨论,留言区是匿名的,所以欢迎自爆,也欢迎马赛克人名爆别人的料,毕竟独乐乐不如众乐乐,让大家一起围观引以为戒:) + +我们下一讲会继续毕玄在淘宝的经历聊,下一讲见。 + +拓展阅读 + +1. 这是一扇传送门,关于毕玄做HSF的复盘,我们后面单独有一讲,聊一聊他对这次故障的深刻反思,以及之后怎么才能顺利做成事的参考经验。 + +2. 如果你对阿里的技术演进感兴趣,可以看《淘宝技术这十年》。 + +3. 满江红网站还在,首页也能看到OSGi那篇文章(毕玄网名Bluedavy),但链接失效了,如果感兴趣可以看看毕玄博客里的这篇:说说OSGi。 + +4. 如果你对用OSGi做服务框架的思路感兴趣,当年毕玄也写了一系列思考:- +服务框架的要素的blog- +基于OSGi实现服务框架的分析- +基于OSGi实现分布式服务框架历程(一)- +基于OSGi实现分布式服务框架历程(二)- +基于OSGi实现分布式服务框架历程(三)- +基于OSGi实现分布式服务框架历程(四)- +分析分布式服务框架 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/04淘宝消防队:真正最优秀的程序员不应该是英雄.md b/专栏/超级访谈:对话毕玄/04淘宝消防队:真正最优秀的程序员不应该是英雄.md new file mode 100644 index 0000000..59585a7 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/04淘宝消防队:真正最优秀的程序员不应该是英雄.md @@ -0,0 +1,243 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 淘宝消防队:真正最优秀的程序员不应该是英雄 + +你好,我是叶芊。- + - +上一讲我们简单聊到毕玄进淘宝后做HSF的经历,以前他做网站访问量10万不到,上来一下到200万,再直接跳到1亿,他的心情也从“信心爆棚”直接跳到了“上线爆雷”,毕竟刚转正就让公司出了严重故障,他表示压力山大,但是自己心态还行。- + - +今天我们接着他的经历聊,当时在淘宝他还有一段相当有趣的经历——淘宝消防队,在这里他锻炼了自己的解决问题能力,也结识了阿里的知名大神多隆,但是在采访中他却说这种机会对公司来说其实是个恶性循环。- + - +为什么他会有这个看法?淘宝消防队的经历对他来说究竟意味着什么?我们开始今天的对谈。 + + + - +极客时间:看你的经历,HSF做完你就去淘宝消防队了? + +毕玄:不是,淘宝消防队是一个插曲,就它是个民间组织。因为09年淘宝经常出问题,出问题以后,当时的最大问题是没有人去解决。 + +以前我们就一个系统,如果出了问题,大家都会去看看是不是跟自己相关,但是我们在09年以后有100多个系统,大家都觉得如果出问题了,肯定不是我的问题,所以就没有人查问题,导致那段时间很糟糕,客服感觉明明有很多用户在投诉,但技术这边反馈很慢,都不大去解决,就很混乱。 + +后来我们运维线有一个人很不爽,就拉了个群,这个群的名字叫“消防队”,群里拉了一些我们觉得愿意去解决问题的人,后来只要出问题,这个群里的人就会去解决。 + +极客时间:噢没有组织上的约束,那这些人确实没有责任必须要处理问题。 + +毕玄:没有。只是这帮人觉得问题老没有人解不好,当然还有一个原因是这帮人比较喜欢解问题,有这个爱好,以前你想解也没有问题,现在你一进这个群简直了,只要想解,问题一直有。然后我们就有一帮人进了这个消防队。 + +这个群以前有个默认规则是:不允许M线的人进来。阿里是P线、M线,M偏管理。后来有一个副总裁不小心被拉进来,但很快就被踢出去了,然后他就问为什么要把我踢掉?我们说因为解决故障的时候不需要你,解决故障的时候需要P线的人,而不是M线的人。他也很无语。 + +极客时间:副总都能直接踢吗?这个操作挺勇的。 + +毕玄:这个群慢慢大家都知道了,因为大家发现只要反馈给这个群,问题是会被解决的,就形成了效应,加上又没有官方组织。可能半年多的时候,不知道为什么群被更多的高层知道了,因为我们最早说一不允许M线,二要低调,最好没有人知道这个群的存在,尤其高层。因为我们要操作生产环境,理论上肯定是有点违规性质的,权限会过大,所以我们也不想太高调。 + +但被知道了以后,在淘宝年会上,公司临时决定要给这个虚拟组织颁奖,好像是总裁特别奖之类的。我们就突然被通知你们的群得了个奖,颁的时候我们印象都很深刻,因为是临时的来不及订奖品,就从现场,当时在黄龙体育中心,找了一个灭火器,颁给了这个消防队,然后颁奖的时候还跟我们说,等会儿下来,记得把这个灭火器还回去。 + +极客时间:所以是给了虚拟团队一个虚拟奖。 + +毕玄:就是给一个荣誉,我们都笑死了。 + +后来公司知道了,觉得不能靠虚拟组织,这个事情还得官方化,所以成立了官方的消防群,那就不一样了,进群的责任有一条就是处理问题。但尽管有官方了,我们有少数几个人被列在了某个名单里,那个名单里的人被特批拥有全部权限。但这是因为我们过去做了很多事情。 + +反正当时对我来讲挺好的,借机学了很多怎么解决问题,而且多隆也在里面,我跟多隆就是那样熟起来的。以前我只知道他很神,但不知道哪里神,但在消防队我充分感觉到了为什么,很多问题很难被解决,但多隆基本都能解决,而且几乎横跨所有领域,这太夸张了。 + +极客时间:这种解问题也是非常锻炼人的机会吧?因为我看你写自己的技术成长过程,总会提到在淘宝消防队的这段。 + +毕玄:因为这是个训练。我后来跟多隆总结出来就是“卖油翁,唯手熟尔”,你越被喂就越熟练,越熟练就越被喂,就像打游戏一样。但这是个什么循环?你可以认为是个良性,也是恶性。 + +对个人就是良性循环,比如说你解多了,公司很多人都觉得你解问题不错,所以大家有问题都找你,你被训练得越来越惊艳。但恶性就是公司其他人没有机会了,因为处理问题通常都是很紧急的,大家一定会第一时间想,谁能最快解决,就找谁。 + +  + + + +  + +极客时间:你淘宝消防队的这段经历,是不是给自己赢得了很多信任,也为你后面的很多机会做了铺垫?因为像你这种刚开始出于兴趣去做,后来被问题训练得越来越强,这种人,在技术的人眼里应该会相当受认可吧。 + +毕玄:通常来讲技术圈里,每家公司大家觉得最牛的那个人,多数是解决问题能力特别强的,公司的很多危机时刻,是他出来把问题解了,鲁肃当年就是这种。很神奇,阿里有多隆,Google有Jeff Dean,百度据说也有个这样的,腾讯也有,好像每家公司都会有一个神一样的存在,大家觉得没有什么问题是他解不了的。 + +后来我们说还有另外一种,但这种不显眼。因为解决问题就很像英雄,出了问题然后你出面把它解决掉,另一种人是他写的东西几乎没有出过问题,就不会被暴露出来,但这种人其实是真正要挖出来的。因为前面的不挖他就在那了,不需要挖。 + +极客时间:英雄和这种不出问题的,是不同的能力水平吗? + +毕玄:程序员的技能水平我一直说可以分成三段。 + +第一段是你的基本技能,比如做这个业务要写相应的代码,那这个代码相关的东西你得非常了解,包括背后的细节。我们觉得这是程序员的第一关,迈向职业化。 + +说实话这跟公司中、小、大没有任何关系,关键看你对自己的要求,公司可能不会明文说你要怎样,尤其是中小公司,因为说实话,每家公司不对个人成长负任何责任,反正你没成长,公司大不了换一个人。但你自己应该有追求,用到的所有东西都应该去了解它背后是怎么回事,否则现实讲很容易被淘汰的。 + +极客时间:这里想多聊两句,你有没有什么衡量自己了解程度的具体方法?你是怎么做的? + +毕玄:写文章和分享。我觉得分享是很大的挑战,有些东西你觉得自己很懂,真正讲两个小时,你可能就挂了。 + +以前博士(王坚)给我们上课,讲怎么判断一件事是不是个问题,因为大家会提出一堆公司可能存在的问题,他说,你们如果能就这个问题讲一天,说明这真的是个问题,如果不能,说明这根本不是个问题。 + +因为吐槽一个公司的问题很简单,关键是这是不是真问题,而且对高级别的人来讲,肯定也要想怎么解,如果你能讲一天,这才是公司值得重视的问题,说明解法很复杂,这就真是个问题。如果很简单,公司肯定会解的。 + +这就像技术分享,博士会挑战我们,你们不是觉得自己很牛吗?觉得对这个很精通吗?来,给我做个分享。很多人发现上去只能讲两个小时,这其实就是你掌握的全部,我以前在内部讲JVM,最早也只能讲两个小时,后来我能讲两天。 + +这是可以训练的。所以最重要的是先把一个东西讲厚,当然另一个技能是能不能把要讲一天的东西5分钟讲完,但首先要有讲厚的能力,不能上来就讲5分钟。 + +博士那一堂课是我们有史以来最受教的一堂课,因为这是一个很简单的方法,但所有人都可以被衡量出来。 + +那之后我们就懂了,后来跟很多技术线的人说,检验自己的能力有一个很简单的方法,就是分享,你讲多久能把自己讲空掉,就是你能力的极限。很多人用中文随便能讲两个小时,一换成用英文讲,10分钟就讲完了,这一看就不靠谱,这其实就是你的技能。 + + + + + +  + +极客时间:好,第一段是把基础打好,程序员技能水平的第二段是什么? + +毕玄:第二种,在程序员世界里,通常容易被认可的就是解决问题的人,就是英雄式人物。但这确实也有一个问题,因为解决问题的能力,目前看必须靠问题训练出来,很难靠自学,除非你给自己造一些问题,但还是非常难,最好有实战的机会。 + +极客时间:有问题出现的这种机会,是每个公司都有的吧? + +毕玄:对,因为每家公司都会出问题。你看好了,出现问题的时候,关键看有哪些人,跟这个事情一毛钱关系没有,他还冲上来,掺和一下,一定会有这样的人,每家公司都会有。 + +虽然开始掺和的可能也没什么用,但他掺和多了就不一样,这个人慢慢就会得到信任,然后他的机会越来越多,就会进入良性循环。这种人会慢慢成为程序员圈里的大牛,大家都会知道他,而且大家都会特别服他,不服不行,因为你真的干不过他。 + +就像多隆,你们不服对不对?来呀。因为我们老说阿里最神的技术人是多隆,后来阿里有些新人不认识他,内网就有人说凭什么?他做了什么?结果炸出来一堆老员工,全部出来喷上面的新员工说你们知道啥。 + +多隆很低调,不像我们这些很喜欢在台前的人,但事实上他解决问题就是比很多人强很多,这是经过事实论证的。程序员要服人还挺难,但解决问题绝对是一个,大家正面PK,两人一起解决,他就是比你快,那你还说啥?你说你能力比他强,那不可能。 + + + + + +  + +极客时间:所以第一关先把个人基础能力搞好,再当一个喜欢掺和的人用问题全面锻炼自己,那第三种呢?你前面说的不出问题的? + +毕玄:第三种程序员,那是真正职业里最优秀的程序员,因为他写的东西很稳定,就是我们说的鲁棒性特别好,不大会出问题,但这种反而特别难挖出来。 + +做一个程序员写正常逻辑不难,正常逻辑就是解问题,因为人脑是串行的,串行写下去非常简单,只是从说话变成了代码而已,是个翻译过程。翻译的好坏是另外一个要求,需要你对背后机制很了解,可以做到性能很高。 + +但是一段代码,在现在正常的环境下可以跑,同时环境稍微有点异常,如果你这个代码还能跑下去,那太牛了。这是最牛的。 + +这多数是因为他经历过前面,见过很多问题,也解决过很多问题,他才知道噢我写代码的时候,这里要注意一下,那里要注意一下。所以很多优秀程序员的代码,正常逻辑可能很少,其他都在处理一堆你觉得不可能发生的事情。 + +极客时间:我记得你前面讲HSF的时候提到性能是一个很重要的要求,但为什么你这里说稳定性能做到是最牛的? + +毕玄:对程序来讲,性能当然很重要,但其实最最重要的肯定还是鲁棒性,因为我们没有办法非常好地预判这段代码运行环境到底是什么,你现在设计的时候是这样,但过段时间环境可能就变了。 + +所以大家说看代码绝对能看出一个人的功底,只要秀几段代码,我们很快可以判断这个人的大概水平。现在我没写很多年了,但我还是能看一眼看出差别,为什么他写那几行代码,你看起来没有意义,但出问题的时候,因为有那关键几行,他的就保住了,而你的就挂了。 + +阿里以前出现很多Bug,比如说最简单的,用一个数据结构去缓存所有用户信息,以前写的时候,他觉得这公司用户量不可能到100万,所以那个缓存没问题,但很快量就突破了,然后缓存的内存就爆了,结果整个系统全挂了。但写得很好的,他一开始就会设一个保护,到了多少会直接自己异常掉,不会把整个系统搞挂,这就是有经验的人,这就是差别。阿里出过很多次这样的故障,都不是一两次,很多很多次。 + +后来很多人写接口也是这样,比如说我给你一个函数输入XYZ,你随便输入,因为很多人的意识是我已经在设计文档里告诉了你X不能大于1000,但是你还是输入了超过1000,然后我挂了,他觉得那是你的责任。 + +但事实上一个好的程序不是这样的,我不是靠文档,文档上既然这么写了,我的代码里就会控制,如果入参给超了,我一定会给你一个异常,这样整个系统不会出问题,这就是一个非常优秀的程序员。 + +极客时间:但要做到这种程度,考虑到可能存在的各种异常,然后写处理代码,这是不是也有时间成本? + +毕玄:这就看追求有些时候,比如中小公司,很多人就会说这个概率很低不会出现,所以他觉得我为什么要浪费时间写这些1/10000或者1/100000的事情,但我们说这就看你想不想做一个好的程序员。 + + + + + +  + +极客时间:如果我们总结一下你的这段消防队经历,你是一个喜欢解决问题的。 + +毕玄:对,因为我喜欢做英雄式的人,我本来以为很多人应该都喜欢,后来发现不是,像多隆就不是,很神奇。但以前我跟多隆比较过,我是能看出差距的,而且这个差距是很难弥补的。 + +极客时间:看到差距了,你和高中那会一样选择接受,也不挣扎? + +毕玄:因为我碰到过几类这样的程序员,我必须说他们可能天生就是应该做程序员的人。 + +就像我解决问题是要有现场在的,比如一段代码有问题,我要能看到现在输入了什么,执行的过程中出了什么问题,然后我来排查,但像多隆这种人他是可以大概推测的。因为现场经常就没有了,对我来讲就很难,我需要再等,但多隆不是,以前跟他一起排查,我告诉他这段代码可能有问题,然后多隆拿去看,过段时间来告诉我,你试一下把参数改成什么,然后跑一下看看会不会出问题,就真的会出问题。 + +这就比我高了一个档次。这种人是可以在自己大脑里运行代码的,像我们这种就不是。 + +极客时间:这种能力,不能多看案例训练出来吗? + +毕玄:很难训练,这真的应该是天生的。我们还见过更牛的。CPU可以多线程同时运行,但人脑最大问题是单线程,我们程序员写代码又经常要写多线程的并行代码,就导致查问题特别难,因为我得在脑袋里模拟计算机的并行。当时我们碰到一个并行问题,一帮人想了很久,头都要晕过去了,但以前阿里有个女程序员她就可以在头脑里同时并行跑代码,猜出问题来。 + +我们都承认这就是天生差距,是无法弥补的,其他的我可以靠后天不断地解决问题把技能堆上去,但碰到这种,我说我认了,那我认输了。 + +这种人你想,写代码的质量自然会更高很多,相当于在自己的脑袋里可以先跑一遍,这太恐怖了,以前传说Jeff Dean有个笑话,如果Jeff Dean写的代码跑不过,肯定是编译器出Bug了,这简直了。 + +有些人会觉得自己学得很累,但还是很难跟别人比,说实话,这真的是天分,所以我们会跟很多人说不要都做程序员,有些人并不适合,或者做到这个份差不多就可以了,然后就躺平多爽。不用纠结一定要成为多顶尖,这个世界上这么多程序员,最终顶尖的就那几个人,你没法跟他比,也没必要去跟他比,想成长可以理解。 + +  + + + +  + +极客时间:你喜欢解问题当英雄,所以从这个角度看,你在阿里之后的很多段转岗经历,好像背后都能用喜欢解决问题的逻辑来串,你看到一个问题,然后去解决? + +毕玄:对,我觉得跟每个人的定位有关系。我是想过我自己的相对优势的,因为每个人的优势是不一样的。 + +后来我对自己的判断就是,我很难成为一个非常专业性质的程序员,做非常深度的比如操作系统、语言这种,因为我也试过一段时间,我带过JVM团队,他的特质是必须在一个很小的问题上持续钻研,可能只提升了一点点,他也很愿意钻研。所以说实话,多数程序员都不适合干这个。 + +极客时间:这种持续钻研的专业性程序员,是不是有点像搞科研? + +毕玄:对,你可以认为这一定程度偏科学家性质。科学家就是这样在一个很小问题上,当然影响可能很大,但看起来就是一个很小的点上,不断地突破,他觉得突破0.1都是有意义的。但我不适合做这种,真的呆不住,解决问题型的人可能都有这个毛病,够用就结束了,得靠不断解决问题升级。 + +我更适合做宽,就是广度,因为相对来讲我对各方面都有点兴趣,都愿意去了解一下。另外我可能的优势是可以大概判断,做什么事情是对这家公司相对更有意义的。这其实是需要肯思考的。 + +所以综合起来我可能适合的是把一个事情从0分做到80分,但我不适合从80分继续做到90甚至更高。因为说白了做到一定阶段,再往下突破会很难,我需要找别的人,但我觉得,既然最后要靠我找的那帮人,那干嘛不让他们来,我就不想干了,无力感会太强烈。当然这是个人判断。 + +极客时间:比起更偏专业技术的钻研,你喜欢在各种新领域横跳探索,是不是更偏业务一些? + +毕玄:一家公司的高管,越高层级包括到副总裁以上,其实是需要这种人的,因为从80分带往90分带往更高,你可以组建更专业的团队,让他们来解决就好了,然后你专注在各方面业务层面。 + +极客时间:啊你很清楚自己更适合做什么,但能像你这样想得这么清楚,感觉也挺难的。你有什么方法吗? + +毕玄:当然刚毕业的时候,你不用纠结这个问题,因为刚毕业你的长短可能不是很突出,另外你其实也不知道你的长板真正在哪里,所以刚毕业的话,你也许可以多试几个角色。但是最后你一定会发现,在某个角色上你是相对轻松,而且同时干得也不错的,一定会有这样的角色,不可能没有,每个人都会有。 + +所以不用纠结我一定要在程序员上比他干得更好,没必要,因为你可能怎么干,都是干不过他的,这就真的不用纠结。每个人还是要想清楚自己适合的,还有你的长板。 + +但这也不一定是好事。以前阿里就有人跟我讲,你给自己画了个框,将来你是很难突破这个框的,因为你已经限定了自己,但如果把这个框去掉,想象空间变无限,就可以什么都干,尤其在大公司。 + +我自己的看法是,如果你很年轻,确实不要给自己画框,但你如果职业生涯有限,我觉得画个框不是坏事,因为你到中后期肯定是发挥自己的长板,短板就不用在乎了,没什么好在乎的。 + +极客时间:尤其大公司不要画框,那小公司不一样吗? + +毕玄:小公司我觉得完全不一样。因为小公司难道还需要一个人帮忙吸引人才,然后他啥也不干,就指望下面干活?这个人一定会被砍掉的。但大公司不一样,大公司需要有人能凝聚一帮人干成事,因为大公司管理有各种各样的问题,所以这样的人反而适合走向更高级别,他需要接受自己不是那个真正解决问题的人,但他的优势是能凝聚一帮专业的人来解决问题,这也很牛。 + +老马就是这样的人,马云就是我有一个想法,然后有一帮很专业的人都拜依你的想法为你拼命。我也很佩服,我觉得这是不一样的,但我很难接受。所以后来我换的每一步,都是我认为这家公司现在分比较低的一个领域,比如可能是0或者60分,我觉得我去可以做到80。所以导致我在阿里换了几个位置。但后面更被动,前面确实是主动的。 + +极客时间:HSF做完,像你后面做的HBase、T4都是主动换的? + +毕玄:对,后面的HBase就是因为HSF我觉得已经很难把它带到更高了,我不知道该做什么了,再做这个Leader就很不好,因为如果Leader都不知道去哪里,其他人肯定挂,所以我觉得没有必要。 + +  + +水友讨论区 + +到这里对谈就暂时结束了,在淘宝消防队处理各种故障的过程中,毕玄的编程能力也突飞猛进,也为他之后找到自己的优劣势埋下了种子。 + +今天对谈的重点是程序员能力三关,从基础能力过关的“职业程序员”,到解决问题高光出场的“英雄”,到写的东西不出问题的扫地僧“宗师”,每一关都能让你的编程能力实现阶梯式的提升。至于如何衡量你每个阶段的掌握程度,毕玄也给了一个简单又实用的方法——分享,看你能多久把自己讲空掉。 + +最后是今天的自由发言环节: + + +对于程序员的能力阶段,你是怎么看的呢?你有哪些好用的知识积累技巧吗? +毕玄解读了他认为做广和做深,他更擅长做广,回顾学习/工作生涯,你有发现自己在某个角色上是相对轻松,同时干得也不错的吗?你觉得自己的优势是什么呢? + + +欢迎留言交流,说不定今天就是你的小宇宙觉醒之日。 + +下一讲我们聊毕玄在淘宝的第一段转岗经历,做完中间件HSF,他居然又跑去做数据库和容器了,下一讲见。 + +拓展阅读 + +如果你处在程序员的第一关,关注如何精进自己的代码基础能力,毕玄之前总结了一套方法可以参考:- +程序员的成长路线- +程序员的成长路线(续)- +程序员的成长路线Remix- +怎么提升写代码的能力- +高质量的工程代码为什么难写- +又是一年校招季,我是这样考察学生的 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/05HBase_T4:Leader最重要的,说白了是要赌未来.md b/专栏/超级访谈:对话毕玄/05HBase_T4:Leader最重要的,说白了是要赌未来.md new file mode 100644 index 0000000..b29d9e1 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/05HBase_T4:Leader最重要的,说白了是要赌未来.md @@ -0,0 +1,279 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 HBase_T4:Leader最重要的,说白了是要赌未来 + +你好,我是叶芊。- + - +上一讲我们聊到毕玄的淘宝消防队经历,在这个民间组织里,他发现了自己喜欢做英雄式的人,也隐约对个人的相对优势有了一些判断,认为自己不适合做专,更适合做广,更有兴趣从公司的低分领域做起,这也是为什么之后他会在阿里各个部门间横跳转岗。- + - +但转岗说起来轻松,能像他这样跨大领域,还做一个成一个的,可不多见。他到底是怎么找新方向的?又是怎么把事情做成的?- + - +今天我们先揭秘他的第一段经历:从分布式的服务框架HSF,到数据库HBase,到容器化T4。 + + + - +极客时间:HSF是中间件,之后你又去做HBase,从中间件跳到数据库这个转变还挺大的。 + +毕玄:对,我去了数据平台。但其实我一开始没有想好做什么。 + +当时是2010年NoSQL比较火,阿里内部用什么的都有,MongoDB、Cassandra、HBase,这就又出现了淘宝消防队的问题,没有一个专业的团队维护,所以一出问题,业务就挂。我们就觉得必须成立一个专业团队专门维护一个,我刚好也没有别的事干,就决定把HBase落地。 + +但HBase做了一年之后,对我的吸引力比较少,把这个东西做得更好的动力我确实不太强,我就觉得应该干点别的,开始想到底该干啥。还好我的老板一直挺支持我的,不管我换哪个老板,基本都觉得反正你想干啥就去干。 + +极客时间:可能你老板觉得也管不住你(笑)? + +毕玄:也有可能,反正我在想该做点什么的时候,看了一本讲Web容量规划的书(《Web容量规划的艺术》),所以我跟他们说看书还是有点帮助的(笑)。 + +那本书到现在我印象都很深刻,核心观点就是一家公司如果不在乎成本的话,迟早有一天成本会超过收入,如果你不控制好成本增速和业务增速,那一定会失速,最后你业务就不能健康发展。 + + + +我觉得讲得很有道理,阿里也应该干一下这个,所以我就去干了,就是T4。这也一样,开始是个非官方组织。 + + +T4,2011 年启动,阿里早期基于 LXC(Linux Container) 和 Linux Kernel 定制的容器调度器,T4 的技术理念与现在云原生领域的核心技术容器一致,2017年开源了名为Pouch Container。 + + +极客时间:非官方,就是虚拟团队? + +毕玄:非常虚拟。因为我当时在带HBase也不确定要不要做,就去找了多隆,还有运维线的一个人、中间件的一个人,我们四个人聊了一下觉得挺有意思的,因为我们很清楚做的目标,也找运维拉了公司机器的利用率数据,觉得有戏。 + +极客时间:最开始这几个人你是怎么拉的? + +毕玄:都熟人。后来阿里很多人想做创新觉得很难,我就讲要相信一家公司到了这样的规模,你想做的事情,肯定也会有另外的人感兴趣,就看你能不能找一帮这样的人,然后私下把这个事情做到一定的阶段。 + +之后,你有可能做成,也有可能做不成,都很正常。但是在没有做到一定阶段前,要高层支持你很难,因为通常跨了很多团队,所以只能靠你自己。从下到上是很难,确实很难。 + +极客时间:项目刚开始是拉的熟人,后来呢,T4怎么进展的?因为看岗位,你们四个也不是容器这方面专业的。 + +毕玄:所以我们确实做错了很多,比如说第一版方案有很多问题,我们在第二版的时候才发现原来世界上就有类似的方案。 + +极客时间:Linux Container? + +毕玄:是,我们走了大概有半年的弯路,而且那半年很悲惨,因为是弯路,有很多返工,这要不是有人支持,肯定挂了。 + +极客时间:支持的人是谁?因为有多隆这个有影响力的大佬在? + +毕玄:有个运维的人。我们逼他不断返工,其实他们也很想投诉,受不了了(笑),我们不断折腾,有段时间让运维那个团队每周末连续加班,接近两个月,但我们都顶住了,正常情况下,说实话这个项目八成要黄。加上多隆也在,还是有帮助的。 + +所以大家还是要尊重专业,每个专业其实有壁垒、有门槛的,都觉得自己能做也是有点问题的,你当然也能做,但肯定跟我们差不多,来回不断折腾。 + +极客时间:这样折腾都没黄,是不是也有信任的因素?运维团队,包括你们项目里这几个人,相信这事能干成。 + +毕玄:对,其实任何公司都是刷脸。 + +为什么你能干成事?为什么大家觉得你说的可以干?是因为相信你,这个信任感只能靠时间,关键看你做了什么事情对这家公司好,都是看结果。 + +你过往的光环,顶多让你一开始很爽,因为大家都或多或少会让你刷下脸,但过了一年就没有人记得你的过去了,都只看你这一年在公司做过什么。如果还是没干啥,大家就会开始怀疑你,而且你以前背景越好,越受怀疑。 + +极客时间:怀疑你的能力,因为别人会对你的期待太高? + +毕玄:对,这是高级别的人最大的风险。像我这样的,如果去另外一家大公司,多数给的职位能比现在更高,大家对你的期待是完全不一样的,比如同样的事,我在阿里做要三年,别人就可能希望你在一年内带来很革命性的变化。 + +但事实上这个难度是很大的,技术的思想、方法都没问题,但工程就是工程,工程落地的节奏是很难压缩的,加速肯定会有风险。 + +极客时间:那你觉得新人应该怎么做成事呢? + +毕玄:很多新招来的人,都想一上来就铺天盖地做一件很大的事,真的想多了。 + +你先从一件很小的事证明你就是比别人做得好,然后慢慢的别人对你有了信任,那你机会多了去。技术人员就是这样,有信任,并且他也相信这个事情是有价值的,其实大家是愿意用业余时间去干的。当时我们几个人全是业余时间,但到了一定阶段,大家就都能看到成果。 + +极客时间:看你的经历,总感觉你做的项目都挺成功的,很好奇你有没有做失败的? + +毕玄:当然有,后来做完T4我还去拉了一个业务项目。因为我一直做基础技术,很多人就觉得基础技术没有前途,然后我就听他们忽悠,找了几个人私下攒了个业务项目,淘宝首页链接转化率的优化的一个工具。 + +极客时间:这种工具需要报备吗? + +毕玄:不用,因为我们拉的是负责这条线的产品,所以产品是认可的。但上线了觉得效果不大行,看来做偏商业的业务不是我的长板,后来我觉得不擅长就不尝试了,还是继续做我自己相对擅长。 + + + + + +  + +极客时间:好,我们继续聊回T4,你们虚拟团队私下搞了多久? + +毕玄:因为都不是我们的主业,干了可能有半年多,应该是到了2011年我们确实觉得这个事情可以干了,我就去跟当时的老板正明(章文嵩)聊了一下,决定不带HBase团队专职做T4。T4应该是最早的容器,跟百度同年开始的,但是阿里高层的支持原因,进展速度慢很多。 + +极客时间:什么支持原因,可以多讲下吗? + +毕玄:因为当时阿里已经有阿里云了,理论上讲云是阿里集团战略,肯定是重点发展的,公司希望大家用云的技术,整个阿里都跑在虚拟化上,而不是你们自己又搞一套容器化。 + + +虚拟化,通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。- +容器化,把软件代码和所需的所有组件例如库、框架和其他依赖项打包在一起,隔离在自己的容器中。 + + +但这就是技术之争,我们认为虚拟化不是未来,容器化才是,不是说我们不支持集团战略,是我们认为这个技术方向如果抹杀掉了,对阿里未来在技术领先上会有一些影响,所以我们希望两条路都保持。当时有很大冲突,但反正我们还是顶住了压力,就一直没用。 + +极客时间:感觉你们团队的价值当时不是很受认可啊,会不会因为公司当年正飞速发展,大家对成本的关注都很少? + +毕玄:对,他们觉得你们搞这?搞啥呢?但正明还是很支持我,他是个偏技术化的人。 + +说实话如果站在公司整个业务层面看,我也会认为没有太大意义,但我站在公司几年后看又觉得还是得干。反正我很坚持。他们尽管有些也反对,但他们觉得你要干,好像也拦不住你,加上又有人支持,算了你干吧。 + +所以就干了好多年,准确来讲我一直带着,尽管后来还带了其他团队,但这件事都在我手上。 + +极客时间:当时又是前期走弯路,又是集团的压力,你为什么会那么坚定要把这个项目做下去? + +毕玄:就是技术走向的判断问题。对于云来讲,它确实要走虚拟化,而不是走容器化,否则它没办法对外提供,但是我们认为虚拟化的未来是走向容器化。 + +因为淘系我们最早就是虚拟化,之前也试过一虚三但是利用率还很低,继续试发现内存超卖不好弄,才到容器化T4。所以我们相当于从虚拟化推进到了容器化,然后你们说要支持云,只做虚拟化,那我们技术的人当然不同意。 + +除非行政命令,集团说必须不允许搞,那我们只能认。但内部一定会引发非常大的反弹,因为技术的人最反对行政命令,我们讲的你没有认可,举着一个战略就说要站在战略的角度,虽然我们也能理解,但技术一定会去争取,我们说,我们会在内网掀起讨论。 + +其实内网之前专门有过讨论,简直火爆到吵架了,因为对技术走向的判断本来就很主观,我说是这样,他觉得不是这样,这是没法聊的。当时斗得确实比较厉害,很多人认为我是最反对阿里云的代表,因为阻止了他们把技术体系推进到淘系,而淘系又是最大的规模。其实不是,但反正也就这么过去了。 + +极客时间:你们能这么坚决,有没有部分原因是你跟多隆搞了小半年,确实有成果能拿出来? + +毕玄:效果非常好,所以我们觉得这一定是未来。 + +极客时间:你判断这是未来,除了有些成果,还有没有别的?比如说外界参考? + +毕玄:外界没有,Google都很保密,纯属我们觉得就是这样子。 + +极客时间:既然有成果,当时你们没有拿成果去跟淘宝总裁说,T4这个技术方向很有效果而且一定是未来? + +毕玄:但没用的,我也承认这个决定很难做的,因为一站在集团角度支持云是正确,可能更值得,二站在技术角度他没有办法判断,这就很难决策,所以最后他就不决策了,不决策相当于是保护了我们。 + +因为他们也确实害怕技术人员的反弹,技术人员会觉得信仰被冲击了,是很可怕的。就像为什么语言之争有些时候会上升得很夸张,对他来讲这是信仰,你不能突破我的信仰。技术方向趋势这种东西本来就很主观,不到那一天你怎么知道,所以两个人聊是不可能聊到一起的。 + +极客时间:总的来看,你们的团队方向和集团大力倡导的方向不一致,能做起来确实是够幸运的。因为对大厂来说,比如一个方向很好,它业务特别庞大,用了是不是就会变成大方向? + +毕玄:对。但后来因为Docker起来了,也不需要再说什么,自然能得到大力支持。 + + + + + +  + +极客时间:后来Docker起来了,我记得你们把T4和Docker的技术做融合了,那个时候有波折吗? + +毕玄:那不会,都是我带的团队,我们坚信这两个东西应该合掉,因为Docker还是有做得很好的地方。 + +极客时间:是T4去融合Docker还是反过来? + +毕玄:最早是T4融合Docker,做了阿里Docker,然后包括开源的Pouch。但这也是我自己当时的判断,我觉得阿里需要坚定地把容器相关的技术多掌握一些,不要过度依赖Docker。 + +原因是我当时从整体看,觉得Docker未来可能比较危险。从竞争角度上讲,Google在不断地让Docker剥离化,想把Docker变成一个标准实现,这是Google的诉求,如果按这个趋势发展,Docker这家公司能不能活下去可能会成为一个问题,如果他活不下去,我觉得这个世界会需要另外一个标准,阿里是有机会的。 + +所以我当时让团队一直保留这个可能性,现在应该还是有可能性。但这是很主观的,因为团队很多人希望不要自己搞,全部基于Docker算了,包括高层也有很多声音。但我觉得对阿里来讲,还是值得保留在这方面的能力,因为你是一家做云计算厂商的公司,你不是一家单纯用的公司。 + +极客时间:你的这种判断力,不管是前面对T4方向的坚信,还是对Docker可能被Google边缘化的分析,后来都印证是对的,是不是大家说的技术方向感? + +毕玄:对技术Leader来讲,其实判断是对是错不重要,因为对方向的判断是很主观的,没到那个时候很难说谁对谁错。但最怕你没有判断,外面怎么样,你觉得就是怎么样,那这就不要干了。 + +有想法,没问题,你可以说你为什么这么觉得,我可以说我为什么这么觉得,两个人至少都可以抛出来讨论,实在达不成一致也没关系,这很正常,反正最后没有办法达成决定,说白了就是看谁地位高,谁是团队的大Leader,因为我背了这个职责,那我做这个决定自然也会承担这个责任,但反正大家都说清楚了,也没有什么。 + +极客时间:那做判断做决策这事,你有什么心得吗?可以多聊一下。 + +毕玄:还是看你怎么思考,就是之前七公跟我聊的你的出发点很重要,为什么要做这件事。首先你一定要想清楚,你的出发点是为公司更好的发展,如果很多人想明白了就会知道,公司好了你自然会好。 + +所以站在公司的角度,越高的层级,要考虑的就会越贴近公司的核心,像阿里,我们说越高的层级,越应该去看阿里集团面临了什么挑战? + +当然阿里内部有时候也被我们带歪了,后来很多低层级的人上来也讲阿里集团面临了什么挑战,这跟他一毛钱关系都没有。要想好每个位置它的要求是什么,大家不能把位置给搞乱了,因为搞乱了那个位置就没人了,最可怕的是公司组织结构是这样设计的,结果发现所有人都去做别的了,没有人干活,就乱套了。 + +极客时间:所以是要结合团队和自己的位置,往上看。 + +毕玄:对,但越高的人他肯定看的就越大,比方说你在云板块,首先你要想做什么能帮助云更好发展,去解决它的问题,然后你觉得跟你团队最相关的可能会是什么。 + +如果你有了这样的出发点,再就可以讲你自己想做的可能是什么,基于各种判断,比如商业竞争上的,云可能涉及技术趋势上的,业务涉及业务趋势上的,其实都一样。 + +Leader最重要的说白了确实在赌未来,赌未来一定会走向哪里,这是很重要的,所以我觉得只要自己能逻辑闭环就可以,但闭环其实很难。双方逻辑都闭环,但方向判断不一样的两个人,确实很难达成一致,但这个我们是可以接受的。 + +极客时间:因为一旦逻辑闭环,会不断地给自己讲这个故事,他就很难插进来新想法? + +毕玄:越高级别的人,越大的Leader,肯定是越难被影响,你会觉得他很偏执,建立在自己很强的相信上,否则他很难走到这个位置。这其实没错,如果他太容易被影响,这样的Leader也太可怕了,你今天汇报一下是这样,明天你汇报一下又变了,那完蛋了,这家公司估计也干不下去。所以当Leader最好不要太急做决定。 + + + + + +  + +极客时间:现在你再回看当年自己T4这种虚拟团队做项目的方式,你会比较推荐吗?自己先看好方向,再私下拉人一起做。 + +毕玄:其实后来我也不鼓励大家这么做,T4让我觉得纯靠拉虚拟团队去做事是不好的,最好还是组建一个正规团队去做。 + +极客时间:你说的“不好”是指的哪一点?不好做成事? + +毕玄:拉虚拟团队最大的问题是会被认为有个灵魂人物,外面看到的就是一个人,背后的人全部被隐藏掉了。 + +我们用的都是业余时间,但他在主管那侧没有任何加分,甚至搞不好还减分,因为主管只看主业。所以最后你就会看到,你拉了一个虚拟团队做这个事,最后获得所有东西的人都是你一个人,这就很不好。 + +但是正规团队就不一样了,对不对?这是团队获得的。以前我也不想带团队,因为以前在阿里,想干什么基本都能用虚拟组织干,没有正规团队其实很爽的,因为你想,你对大家又不负管理责任,大家纯凭梦想,梦想驱动然后去做一件事情,那你做着不是很爽?如果组建团队就不一样了,你必须背上团队责任。 + +但我后来觉得还是得带团队,你才有可能做对这家公司更大改变的事情,因为纯靠虚拟团队规模也不可能很大。 + +极客时间:T4的团队规模是多少人? + +毕玄:人一直很少,七八个左右,后来到2016年左右才变成一个三四十人的大团队,但那已经是公司层面上大家公认这条技术路线非常重要了。 + +极客时间:但你前面说一个新项目刚起来的时候,需要先看到一定成绩,这个风险是正规团队能接受的吗? + +毕玄:我觉得取决于Leader,比如团队Leader如果能接受把10%的人投入在一个想尝试的或者团队大家觉得很值得尝试的方向。 + +后来我带600人的团队(系统软件事业/研发效能事业部/中间件)就搞了两个我们认为未来对技术侧很有影响的方向,我们内部叫X方向,然后我跟全员说,愿意的就可以去,但要求是加入的时候也意味着要从原来的团队脱离。这对很多人是很大的挑战。 + +我也会说清楚,你做这件事情,一年之内想得到很高的绩效评价,说实话可能性基本是零,因为未知方向挑战非常大,问题简直太多了。但作为Leader,我愿意投这样的方向,所以我可以保障你们不会是最后的一档。 + +极客时间:脱离原团队,不能兼职? + +毕玄:我绝对不允许兼职,因为兼职就有退路,这其实相当于考核试探,就看你愿不愿意为梦想买单。否则如果是业余,所有团队都会说有兴趣,反正无所谓嘛,做失败了也没关系,做成功了还白捡一个加分。 + +极客时间:那风险很大啊,有多少人愿意去? + +毕玄:我们发现很成功,有很多同学很满意新方向,也愿意离开自己的位置,甚至有个带着几十人团队的P9,他都愿意放弃原来的团队,加入新项目,七八人去做一个很未知的方向。 + +极客时间:这挺厉害的,确实能把一些人筛出来。 + +毕玄:但这个形式后来我发现了很大问题。 + +极客时间:项目很容易死? + +毕玄:很容易死倒不重要,重要的是如果我不带这个团队了会发生什么。因为这是Leader的主动行为,我一不带,大家就会认为这两方向不要搞了,那这帮人一年不就白干了。 + +所以这种也建议不要随意尝试,除非是组织性的行为,或者是创始人,创始人反正不会走,那没有问题,最怕的是个体的Leader行为。后来我就基本不做这种了,这样的话不管我在不在,都还能持续。 + +极客时间:有点好奇,X方向现在呢? + +毕玄:后来就挂了,有一个应该还在做,但就不会像当时那么多的资源。如果我在,我肯定会保持它,因为他们做到一定成果了,我会加大投入去赌这个全新方向能不能做起来。但这就是Leader的判断,新的Leader可能认为这个方向不是未来,就不做了,都是很主观的。 + +但从组织激发角度上来讲,我自己觉得这是个很好的方式,我们能看到在这个团队,有些人是非常愿意为梦想承担风险的。 + +  + +水友讨论区 + +到这里,今天的对谈就暂时结束了,主要聊的是毕玄的第一段转岗成事经历。 + +做完中间件HSF后他发现自己作为Leader不知道该干什么了,跑去数据平台做了HBase的落地,但做了一阵发现自己没有足够动力,又开始迷茫自己到底该干点啥,结果从书中找到灵感,去做了最早的容器T4。 + +作为一名技术人员,毕玄从2011年就开始关注业务增速和成本增速了,虽然在现在的大环境下,降本增效成了普遍口号,但当时各公司正处在高速发展期,很多人可能会觉得关注成本没必要,格局不够大,但居安思危,毕竟不影响业务发展的成本控制手段,从设计到落地可能需要好几年才能看到效果。 + +不知道你对今天对谈的哪个部分更感兴趣,我还是简单列了三条: + + +“一家公司如果不在乎成本的话,迟早有一天成本会超过收入”,关于成本增速和业务增速,你是怎么看的?你的团队/公司有采取什么措施吗? +“还是得带团队才有可能做对这家公司更大改变的事情”,你有加入过虚拟团队或者未知方向的团队吗?体验如何? +“其实判断是对是错不重要,因为对方向的判断是很主观的,但最怕你没有判断”,对技术方向的判断,你是怎么看的?你的决策出发点是什么呢? + + +自由发言环节,期待你的想法,我们留言区见。下一讲我们将聊到毕玄作为总架构师做淘宝异地多活的故事。 + +拓展阅读 + +1. 这是一扇传送门,今天简单聊到对技术方向的判断力,如果你想了解更多可以去这一讲,我们将专题讨论如何思考技术演进方向。 + +2. 对HBase感兴趣,可以看这篇阿里技术星球的文章:阿里HBase超详实践总结 | 一文读懂大数据时代的结构化存储 + +3. 物理机 -> 虚拟化 -> 容器化的演进,毕玄之前写了这篇:回顾过去看IaaS的Next + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/06异地多活:技术圈子的人,见过猪跑很重要.md b/专栏/超级访谈:对话毕玄/06异地多活:技术圈子的人,见过猪跑很重要.md new file mode 100644 index 0000000..9defcc3 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/06异地多活:技术圈子的人,见过猪跑很重要.md @@ -0,0 +1,273 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 异地多活:技术圈子的人,见过猪跑很重要 + +你好,我是叶芊。- + - +上一讲主要复盘了容器项目T4从立项到组织、落地的过程,今天我们聊的主题是异地多活,这是毕玄第一次做大的系统级架构演进。- + - +作为14年阿里职业生涯中的三大亮点之一,当时他要面对上千人,做一场世界上完全没有参考方案的架构,大家都认为做得很成功,但身为总架构师的他却说“这个项目我做得不好”。- + - +他遇到了哪些问题?解法上又有哪些缺陷?我们开始今天的复盘会。 + + + - +极客时间:看你的经历,T4之后就是异地多活了?是你主动的还是被动分配的? + +毕玄:说分配的话,异地多活也能算,因为当时刚好是我转岗。 + +当时2013年T4我做的太难了,集团各种阻力,再加上运维侧支持力度不够,我那个时候在核心系统部正明那,就决定转去运维团队,因为我觉得如果我在运维就可以把T4落地了。 + +我就去了那边,正好运维的人跟我说现在要做异地多活,要不你来干?因为异地多活在前一年其实失败了。 + +极客时间:做过一次了?失败是什么原因? + +毕玄:淘宝已经做过一次,蚂蚁也做过一次,也失败了,因为技术上还有很多问题没解决掉。所以就任命了两个新的架构师,我负责淘系的异地多活,俊义负责蚂蚁的异地多活。我们俩各自做了一套方案就开始落地了。 + +异地多活的难点是因为全世界完全没有可参考方案。Google虽然有全球部署,但广告搜索不一样,比较简单,腾讯也有,但腾讯是社交也比较简单,交易类型的网站一家都没有,我们最早本来想看亚马逊,结果发现他也不做了。到这里我们就很痛苦。 + +我们第一次开会,下面各方技术都问我,这个项目的方案是什么?我说这个方案现在还不知道,等我们先摸索一下。下面全听傻了,说还能这样。所以这个项目就很槽糕,我们花了一年左右来摸索方案到底是什么。 + +极客时间:一年出方案感觉挺久的,当时你们是怎么做的? + +毕玄:做这种大的架构,一般是你首先有了一个系统全貌,其次你有了解决的思路,你就可以大概知道我需要哪些人来帮我,然后你去找各领域的人,告诉他我的思路是这个,你看这个系统里怎么改能做成这样,慢慢拼出一张图。 + +但是异地多活是我第一次做这么大的,刚开始的时候确实很难,因为你不懂一个这么大的系统它涉及哪些地方,你又没办法让人跟你讲,因为涉及的人太多了,必须要你先提一个思路,然后大家开始探讨。 + +所以我们先有了个大概想法,但这个想法多成熟肯定是不知道的,只能先做一部分看看。但是因为没有全貌,不知道要改哪些,一开始就漏掉了很多,第一版设计方案最后要上线了我们才发现还有一堆要改的东西漏改了,然后临时去做那堆的方案。所以主要问题就是漏,漏这漏那。 + +花了半年多,我们逐步把方案试出来了,方案清楚了后面就很简单。但是前面比较难,如果有参考,你也有信心,大概也知道怎么做,一切都会好很多。 + + + + + +  + +极客时间:边试边做,不会有人质疑你吗? + +毕玄:我现在觉得跟信任有很大关系。当时做这个项目,我上面所有的Leader没有一个人过问我,也没有人挑战我,否则一定会很多人挑战,这啥也不知道就敢上线,你胆子也太大了。 + +极客时间:会不会因为别人没法挑战?他也不知道方案。 + +毕玄:对,所有人都没有方案,但即使后面有了方案,还是有人会挑战觉得你们这方法也不好,不靠谱,有没有更简单的?但我们比较强硬。 + +当时异地多活我是面对上千人,做完我刚好P9P10晋升,面试就有人问:这些人都不向你汇报,为什么要听你的?我说最关键的就一点:因为我还掌握着所有人的机器。你要机器是我给你的,我给你的就是异地的机器,所以你必须配合我做异地的方案,如果你不做,我无所谓,但你上线肯定会出问题,反正你想逼我给同一个地方的机器我是给不了的,没有,你找谁都给不了。 + +极客时间:这好强硬,不会反弹吗? + +毕玄:业务方很多抱怨,所以我后来跟很多人总结说,这个项目做得不算非常好。 + +而且从团队层面上来讲也不好。当然大家都认为很成功,项目组得到了很高的评价,也能看到我因为这个被晋升了,但这个项目里同样被晋升的人太少了,当然还有一些低级别的晋升,但高级别的差不多就我一个。这就是最大的败笔。 + +一个项目,如果对公司有非常大的价值,理论上应该有非常多的人被晋升,这也是你在公司能更好做成项目很重要的方面,因为各方都获得了利益。你想,如果只有你一个人获得利益,肯定是有问题的。所以异地多活做的整个过程,抱怨非常大,只是因为我们确实相对强势,但这种强势后面其实也会引发一些问题。 + +极客时间:但最后异地多活也确实是做成了,影响力也比较大,大家可能也没法说什么吧? + +毕玄:如果没做成,那肯定挂了。但即使成了,还是会有人有很多看法,这个我也认。 + +因为对一家公司来讲,以战养兵是最重要的,你一个这么大的战斗,竟然没有培养出很多的人,这确实有问题,我是承认的。 + +极客时间:没有培养出人?具体是什么原因,人才没有历练到位吗? + +毕玄:可能很多是曝光的原因。比如异地多活对外曝光全是我,讲各种方案等等,后来其他的项目比如统一调度,我们就会注意让更多人曝光,哪怕有些演讲看起来好像很不技术,但我们觉得对公司来讲这还是蛮重要的,公司应该看到更多人才。 + +因为一个这么大的项目,总不可能是一个人就干成了,肯定是有很多人一起,但是就看这些人有没有机会被更多高层知道。说实话高级别晋升,贡献很重要。但高层会认为那不就是他吗?其他人做了什么?高层认为就因为他,所以做成了。其实不是这样。 + +极客时间:一个项目,对贡献曝光上的关注,是不是参与的人越多,越重要? + +毕玄:对做非常大的项目很重要的,越是大项目,越要把大家团结住。 + +后来再做项目,我就会特别关注参加了这个项目的核心人员,他们情况是什么,有多少人会因为做了这个项目被晋升上去。如果比较多,说明这个项目不仅有个很好的成果,还让整个团队的人得到了很好的成长,那这就是一个非常好的项目。 + +你想,大家一起做了一个很大的事情,对公司有很大贡献,但是只有你有成绩,下面的人没有被认可,那这些人会很受不了,我明明干了很多事情,最后为什么还是没有得到认可。业务方就更不爽,因为他觉得这又不是我主要的事,浪费了我很多时间,我什么也没得到,然后你们都得到了,替你打工。 + +再加上光环如果过度集中在一个人身上,那个人会成为众矢之的,肯定是这样。因为其他人会觉得我无非是成就了你,所以后来做很大的项目我们会特别注意,不要塑造一个神,不是好事。 + + + + + +  + +极客时间:就项目曝光这一点上,现在的技术大会阿里确实会有很多人出来分享,是你说的为了增加曝光吗? + +毕玄:对,阿里有几年是高峰,现在可能少一点,都是去站个台。 + +极客时间:现在不多了是为什么? + +毕玄:以前对外分享,内部的定调就是在中国的自由分享,希望跟大家交流,因为技术很需要聊,你的想法可能跟别人不一样,而且可能别人干过,你实际上是想找一个干过的人跟你聊一下,看看自己想的有没有问题。但后来我们只能去树立技术品牌。 + +极客时间:做技术品牌的转变,是因为什么?你们是什么时候开始做的? + +毕玄:2009年。当时我们第一次校招,出去之后打击太大了,因为在09年,我们认为淘宝已经是集团最牛的一家公司了,淘宝的人在阿里横着走,因为说实话其他公司量比我们小非常多,而且我们也逐渐接近盈利,当然觉得自己是最牛的。但我们出去才发现根本不是这么回事。 + +学生都说,淘宝是什么公司?不就是卖东西的,那你们是不是专门帮人卖货?他们觉得肯定是雅虎、阿里研究院、B2B这些,因为B2B是香港上市公司,他们觉得肯定这几家好。淘宝?除非前几家我都没面过,那我再来你们这试一下。我们就深受打击。 + +所以淘宝从09年决定做技术品牌建设,就参加了很多技术大会,包括QCon等等都是这个目的。这也是菲青提议的,要树立起淘宝是整个阿里集团技术非常好的一家。 + +极客时间:开始是交流,到做品牌,现在就站个台?这个变化是为什么? + +毕玄:对从出来交流的人你也能看出来,阿里以前参加大会都是相对资深的,高级别的,但现在阿里高级别的人都不出来了。 + +近几年阿里的风格是,国内都派相对年轻的、有潜质的人练习他的分享表达能力。因为我们觉得工程师要往更高层走,你表达能力必须要的,毕竟你不是一个人做,除非都想成为多隆那样一个人守到底,但在现代化软件下也很难,一个人再强也不行,所以公司肯定需要这种类型的人。 + +剩下更资深的人都去国外了,拜访顶尖的公司,顶尖的名校。公司,我们只拜访Google、Facebook、AWS这些最顶的,其他的都不太见,他们对我们有兴趣,但我们可能对他们没有兴趣;然后学校,我们去拜访斯坦福、麻省这种规格的。因为后来我们觉得,只有跟这些人聊,互相才有一定的交流空间。 + +极客时间:交流空间?是指对话背后公司的技术问题吗? + +毕玄:因为说实话到现在为止,中国很多公司去拜访Google都没有交流,其实还是个学习状态。包括我前几年带队去Google拜访也还是这种状态,都是我们问Google技术上的挑战,看他们是怎么做的,然后我们问完,问他们对我们有什么问题,没有任何问题,因为Google能从你问的问题推测出你的状况,就没有任何兴趣跟我们聊。 + +像Facebook就不一样,阿里很明显,以前也几乎全是我们问他问题,但现在Facebook会问我们很多问题,技术地位已经不一样了。 + +极客时间:推断你的发展水平? + +毕玄:对,我们以前问他们很多问题,他们会说这个问题是我们七年前做的什么什么东西,有论文讲过了,你可以去看。这就很尴尬。 + +跟现在中国很多公司问阿里一样,比如说问分布式,阿里都告诉你我们是在2008年做的,相当于是已经是14年前了,那你觉得,对阿里来讲,讨论这个问题有多大意义?就没有了,因为阿里觉得我已经成功,不想跟你们讨论这些了。所以这就是纯单向交流,双向要求很高的。 + +这几年特别明显,很多大公司都这样,因为他们的水平确实已经能跟国外顶级对话,对国内的很多交流就没有任何兴趣。有一些公司愿意去,肯定是为了别的目的,比如说卖货这种商业诉求,才去做纯单向。 + +极客时间:不只是讲的人没兴趣,听的人如果没有那个场景不遇到那个技术问题,是不是也听不明白? + +毕玄:听的人,听完也只是觉得啊好牛,然后就没有然后了,就走了。 + +所以最高质量的交流肯定是一个非常小的圈子。但那个小圈子是很难进的。我后来带团队都说,比如去硅谷,如果你能找到硅谷这个领域最顶级的一帮人,大家一起吃个饭,这就决定了你有没有进圈。 + +其实所有的顶级领域最后都是混圈。只不过计算机的这个圈子相对来讲更难靠“混”混进去,因为这个圈子的人彼此文人相轻,他关键看你做过什么,不会因为你是某公司负责某团队的人就觉得你很牛,不会的。所以混进技术圈子难度太大,但你如果能进这个圈子,说明你确实得到一定的认可。 + +  + + + +  + +极客时间:混到这种顶级圈里能拿到什么?一些前沿问题的参考方案?像异地多活当时因为没有参考方案踩坑很多。 + +毕玄:对。因为面对一个问题,如果你不知道这个世界上已经有的、最好的解决方法到底有哪些,你就会觉得自己的解决特别牛,但其实可能是别人玩剩的。 + +架构跟写代码不同,架构最大的问题是如果解决思路有问题,最后的返工可能非常吓人。 + +阿里在这个地方犯过无数错误。以前做分布式的时候,我们只知道要做成分布式,但我们没有想清楚,一个公司的整个系统换成分布式以后,对团队会带来什么挑战?会有哪些问题?这些如果在架构层面没有解决,后面再解决就很难。 + +现在我们会说“见过猪跑是很重要的”。以前我们觉得见过猪跑有什么重要的,反正我现在没想好,以后可以再补嘛,我可以慢慢练成见过猪跑。但是架构这玩意,你现在没看到,未来要补的时候代价可能极大。所以架构师,对视野的要求非常高。 + +极客时间:架构的“看到”和“做到”,哪个要求会更高一点? + +毕玄:不是一个维度,因为“看到”有些时候是经历问题,另一个就是天花板问题,你有没有办法知道天花板。 + +中国其实很多人并不知道天花板,有人会说看一些大会就可以了,认为大会展示的是天花板。但其实可能根本不是。而且现在大会里还有很多商业目的,就更完蛋,其实他就是忽悠你的,在带节奏,但你不一定知道,因为你外行,这很正常。 + +极客时间:那你们去哪里看天花板? + +毕玄:其实要看学术论文。你看Google对这个世界的影响,在技术基础层面影响非常非常大,但它所有影响都体现在哪里?不是技术大会,是它发表的论文,虽然大家老觉得发学术论文都是忽悠,但必须说,真正改变这个世界技术的都是学术论文。只不过学术论文里你也要挑,因为混的确实也很多。 + +极客时间:挑论文,有什么具体方法可以讲讲吗? + +毕玄:最简单的办法,当然是先找你的领域含金量最高的学术会议,因为他论文的水分相对低一点,而且工程界的也会去那发,像Google发论文,很大目的是为了让他们那几个人去评美国的科学院院士,否则工程界的人根本不想写论文的,没有必要,但要评院士他就必须写,就写了那几篇。 + +看这种论文,第一你可以大概判断一下方向,因为在这种含金量很高的会议上,他发的论文肯定解他的问题,非常具备前瞻性,或者非常具备挑战,或者价值非常大;第二他解决的方法肯定是论证过的,也具备一定创新的。因为论文要的是创新,不能说和别人解过的一样。 + +加上工程届的论文,因为是工程公司发的,像Google这种公司都是工程类,他肯定会有实践兜底。当然必须说有真有假,既使Google也是,他以前还发过Omega之类的调度系论文,但Omega根本就没有大规模应用,他讲的是很美好的一个东西。 + +技术上是可以编的,但你最后要了解一下工程上它到底怎么落地,到底碰了什么障碍。这些肯定不会在论文里讲,你需要自己验证,你会听到很多声音,因为技术圈声音很多,有真有假,这就取决于你自己有没有能力判断。学术论文也一样,你要跟这些教授直接去聊或者侧面打听到底什么情况。 + +极客时间:为了看到天花板,成本应该挺高的,淘宝是怎么做的? + +毕玄:以前我们每年会专门关注这种顶级大会,不断研究里面发表的所有论文,去判断我们在这个问题上现在的解决方案跟他们对比大概是什么情况,未来是不是有更创新的方向可以去尝试。 + +因为对公司来讲,如果一个架构师不知道天花板,肯定不是个足够好的架构师。以前很多人会在PPT上写,我的解决方案是全世界最好的,但你得说清楚你为什么最好,如果你有论证,那我们可以认。 + +或者你说现在不是最好的,但你知道自己的位置,这种也可以,因为这个跟你公司的工程状况有关,工程落地是有节奏问题的,知道最好的是那样,只是我现在做不到,没关系,所有人都这样。如果知道天花板了,就不怕,不知道,你可能也不怕,但是对公司来讲就是一个很可怕的不怕。 + +极客时间:你有怕的阶段吗,当时做异地多活就没有参考方案? + +毕玄:我也不太怕,因为我觉得大不了自己折腾。像2013年做异地多活,我们心里当然有担心,但关键是这个问题对公司来讲是一定要解决的,那就没啥,那就试。 + +因为工程说白了,毕竟不是科学难题,做工程,实在不行是可以试出来的,只是节奏会长,而且有可能不那么可控,成本比较高。这个主要看信不信,你自己觉得可以搞,就可以。 + +极客时间:那你们招人的时候,会对天花板的认知有要求吗? + +毕玄:我们面试判断的核心就是看你对自己项目背后技术的理解程度,包括这个项目的问题、你的解法、业界对这个问题的解法、最后你为什么选择了这个方法而没有选择业界的方法。 + +极客时间:这个要求高吗? + +毕玄:非常高,如果你能回答得非常好,说明你的选择做得非常理性,这种我们觉得简直是太完美的候选人了。 + +但事实上知道天花板的人很少了,这是要花精力的。很多人不愿意干,觉得没有必要,不就是个需求?我干了就行了,你管我怎么干的,先不先进什么的都不重要。但是有些对技术非常有热情的人,他其实很有兴趣去了解,这个差别非常大的,而且很明显。 + + + + + +  + +极客时间:天花板的问题,因为阿里是大公司,有资源可以让大家去国外去交流,但是很多中小公司没有,怎么办? + +毕玄:我们觉得也可以关注,不一定要跟别人直接聊,但是大概要知道一下,比如顶会论文现在都在探讨什么问题,有什么创新性的解决方法。 + +如果你发现,顶会论文关注的领域跟你的领域一毛钱关系都没有,这说明他们认为这个领域目前没有什么可突破的空间,那就要赶快想下一步了。 + +极客时间:这是需要主动去关注? + +毕玄:那必须主动,被动不可能的。架构师没法被动。 + +极客时间:但是有些架构师可能是被安排上去的,对天花板的了解不是很好,怎么办? + +毕玄:这也确实存在,你被推到了这个位置,不得不去解决这个问题。有些人是这样成长起来的,很正常。 + +但这就太看命了,而且对公司来讲,可能会稍微有点问题,因为问题你是解了,但是解法可能有问题,最后会给公司埋下巨大的坑。 + +所以回顾当年,大家都觉得淘宝做得特别好,但事实上我们一帮人看,都觉得自己做的简直了糟糕到不行,有些就是我们当时埋下的坑。但如果让我们再去做,会完全不一样,这说明我们确实比当年更好,因为我们现在是见过猪跑的,也知道天花板在哪里。 + +当年我们其实是知道一些东西的,包括Google发的一些论文。但当时我们看了很多觉得做这个没有意义,这就是我们的判断,过了几年后就发现,我们确实错了(笑),其实是很有意义的。 + +极客时间:有没有部分原因是你们的业务没有到那个地步,还认识不到Google讲的东西的意义? + +毕玄:不是的,我们当时也快出现那个问题了,但是我们没有判断出来到底会有多严重。但Google是经历过的,我们其实应该相信的,要相信一个见过猪跑的人他说的是对的,不要太自以为是,觉得自己想的会比他更好。 + +工程的很多问题,没有经历,凭空想是想不出来的,比如阿里这么大体量,他在技术上到底面临了什么问题,我们在里面都不一定能想得出这个问题是啥,外围就更不提。所以很多人很难成长起来,因为做一个商业系统的机会就很少,你说你在家里想一个商业的系统是怎么做出来的,会面临什么问题,说实话这就是空想,跟这些真正经历过的人去比是不大可能的。 + +  + +水友讨论区 + +对谈到这里就暂时结束了,主要聊的是当年毕玄那伙人做异地多活的故事。 + +异地多活的技术资料阿里之前公开很多了,这次访谈没有太细致地聊这一话题(如果你对技术细节感兴趣,可以看看拓展阅读的链接),重点放在了对整个项目的复盘,从牵头,到具体落地,到收尾,以及后期曝光的全过程。 + +在对谈中,我印象最深刻的是很多人觉得异地多活做的很成功,但他却反思自己做项目的方法有很大缺陷,强调做大项目不要塑造一个神,不是好事。 + + +“在公司能更好做成项目,很重要的方面是各方都要获得利益”,关于这一点,你怎么看呢?在自己参与的项目里,或者见证过别人的项目,你有相关案例可以分享讨论吗? + + +另外今天也重点聊到架构师如何拓展视野,毕玄认为“对架构师来说,见过猪跑是很重要的”,总结架构师会面临两大困境:没见过猪跑的经历缺陷、对天花板认知的不足。 + + +对这两个问题,你怎么看?平时是怎么处理的呢? +关于天花板问题,毕玄的建议是先找自己的领域含金量最高的学术会议,如果你有兴趣也有这方面的需求,可以实操一下,如果有帮助,记得回来还愿 :) + + +如果你有其他有感想的话题,欢迎留言,参与讨论,说不定在留言区你能结识同领域的开发者哦。 + +下一讲我们将讨论毕玄的第二大段转岗经历——研发转运维,这段经历我觉得影响了他后面的整个职业发展,下一讲见。 + +拓展阅读 + +1. 这是一扇传送门,后面有一讲我们会专门讨论架构师到底该怎么提升,毕玄也“被迫”总结了一些方法论,期待和你再会。 + +2. 如果你对天花板感兴趣,可以看这篇毕玄之前写的拓宽技术视野的方法论:如何避免成为井底之蛙 + +3. 异地多活的具体技术细节,感兴趣可以看这个来自阿里开发者的链接:“异地多活”设计辣么难?其实是你想多了! + +4. 之前InfoQ也就异地多活的项目采访过毕玄:从冷备到多活,阿里毕玄谈数据中心的异地容灾 + +5. 《超级访谈:对话张雪峰》中也提到饿了么做异地多活的经历:不够坚定:异地多活没有一步到位的遗憾 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/07运维团队:我能干,只是我不想干而已.md b/专栏/超级访谈:对话毕玄/07运维团队:我能干,只是我不想干而已.md new file mode 100644 index 0000000..30c7c3b --- /dev/null +++ b/专栏/超级访谈:对话毕玄/07运维团队:我能干,只是我不想干而已.md @@ -0,0 +1,303 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 运维团队:我能干,只是我不想干而已 + +你好,我是叶芊。- + - +上一讲我们聊到异地多活,毕玄说他能做其实是因为自己正好转岗去运维了,但是从研发转岗到运维?这似乎也是一个不太寻常的职业方向横跳。- + - +作为阿里整个运维团队的Leader,我们问他对普遍存在的研发-运维岗位认知鄙视链是怎么处理的,他却说:“说实话,我也觉得解决不了。”- + - +运维团队面对的究竟是什么样的难题?又是如何置死地而后生找到了自己的团队价值呢? + + + - +极客时间:异地多活的时候你就已经转去运维团队了,去了以后你觉得外界对运维最大的误解是什么? + +毕玄:我以前是研发线的,研发线会觉得运维没有什么技术含量,解决不了的,还是要研发解。 + +说白了,大多数研发都认为像运维、测试,我完全可以干,之所以我不干,只是因为我不想干而已,但研发这活不是你能干的。如果有专业壁垒就不一样,比如研发对数据库就不会这么看,因为他觉得你那活我确实干不了(笑)。所以他自然会觉得我在鄙视链的上游,你运维在下游,很正常。但我去做运维以后,就跟很多人说真的不是这样。 + +运维是一个知识面非常广的岗位。如果你做研发,说实话知识面很窄的,很可能连你的代码在哪个机房、机型是什么、什么样的网络条件、运行环境是什么样的都不知道。我以前刚到运维团队的时候去开会,他们说的是啥我都不知道。 + +极客时间:像机房、网络条件等等,研发会不会觉得这也不用太关注? + +毕玄:这全部是基础设施,但如果你不知道,做系统设计的时候可能有很多偏差。 + +后来我去了运维才了解,可能研发你说一句话,运维为了满足你这个条件,花了很多的钱,但你如果在软件侧简单改一下,这个钱其实根本不用花。 + +但研发人也不懂,运维的人也不懂,他不知道原来你软件稍微改一下,这个需求就没有了,他觉得你可能是个合理需求,然后就去干,这就悲剧了,两个没法对话。因为运维如果没有足够的理由去挑战,那研发侧肯定认为我干嘛要改?所以后来我们去跟很多研发说,要整体看一下成本投入,那研发很多就懂了。 + +极客时间:还有什么新的认知吗? + +毕玄:还有对基础设施的了解。其实阿里后来做的很多创新是因为我们对基础设施演进的了解。 + +异地多活不用说,比如后来我们做统一调度,对整个基础设施有很多改造。因为对网络有了一定概念,知道网络带宽在不断演进,原来是万兆,现在能到25G、40G、100G;以前认为两台机器之间网络带宽太小,在软件侧做了很多东西,其实现在是不需要的;因为网络可以支持,才有了现在的计算存储分离等等。 + +如果软件的人不懂这些,他根本就想不到很多基础侧的创新。硬件也一样,虚拟化下沉到另外一块芯片上,是因为他对物理机型有更多了解,知道了可能性是具备的,不光是从存量来看,也能从运行环境各个方面来看。 + +这都是因为这些人知识面非常广,如果你了解很多,整个创新其实能做得非常好。 + +极客时间:因为了解,才有可能性? + +毕玄:对,我们之前也觉得做基础设施的人啥也不懂,因为研发始终认为自己是最牛的,除了业务以外,技术线研发肯定是站在顶端的,觉得其他所有人都是为我做配套的而已。 + +但是如果你不知道网络可以这么变,不可能敢提出在软件侧要做计算分离这种事,因为他根本就不可能实现,很多都是这样,包括基础设施的大机房建设等等。 + +所以我自己觉得运维这段经历,一是让我做了异地多活,这还是挺重要的,因为我以前没做过这么大的,也基本不涉及业务,我算是做了异地多活,大家才觉得你是能做整个大系统架构的人,都认可你是可以的。第二个就是知识面的拓宽,我终于知道了一个系统真正的全貌,因为系统事实上都涉及基础设施,其实你也逃不掉的。我们可能认为好像不用管基础设施,但事实上如果不管,软件设计或多或少会有点问题,就不是那么合理。 + +  + + + +  + +极客时间:异地多活之后,阿里有些运维侧工具就起来了,你是参与了吗? + +毕玄:我是2015年下半年开始带整个运维团队的,当时想让这个团队发生一些变化。 + +运维以前很苦,几乎所有涉及线上操作的工作全都是运维团队做,一天发几百个系统,根本没法干活,虽然发布也是一套系统,但必须要人点,线上还要盯着看,如果有问题还要查问题,背后还有机器管理等等,杂事非常多。 + +以前有个比例,1个一线运维要对100多个研发,所以他每天光是回答问题,比如机器在哪、怎么用各种,不用干别的就满了,所以非常苦。但是大家又总觉得,你们运维既然这么苦,为什么不做一些工具解放自己?是不是不想?是不是没有研发技能? + +我去了才知道,说实话多数运维做工具的技能还是有的,但事实是他每天根本没有时间做工具。因为研发是很需要连续性状态的,写代码的人都明白,你不能说写个十分钟,被IM弹出一个消息,回复完,半个小时后好不容易进入上下文,结果又被打乱,那代码就没法写了。 + +后来我就跟很多人说,那是因为你没干运维这个事,你去了之后会发现你也没空。以后中国的运维大会,可以让一线运维的人来个吐槽环节,绝对能把研发喷死。 + +极客时间:因为比例不够,每天杂事多,太忙没时间做工具,就一直在给支持。 + +毕玄:对,恶性循环。因为理论上你讲如果运维有很好的工具给研发,他是不会问你问题的,但是这是个悖论。 + +而且阿里当时也有专门的运维工具团队,就是我带的,但通常来讲业务运维团队跟运维工具团队很容易产生矛盾。 + +极客时间:运维工具团队给业务运维做工具不是很好吗,解掉刚才说的没时间做工具的死循环,为什么会有矛盾? + +毕玄:运维会觉得你工具团队做的东西不满足运维的需求,但是他们一天到晚实在太忙,工具如果不好用,他觉得我还不如手工,因为如果已经习惯了,其实手工效率也不会太低。但是工具需要成熟又需要不断使用。 + +所以这个局面,让运维这一侧的我们都觉得运维团队未来会越来越难,成长空间很有限,因为干杂活怎么有成长空间,但活又很重,对一家公司来讲,没有这样的人不行。但有了,大家又觉得这批人好像技能不行。 + +这样恶性循环下去的话,之后一定谁也不想干了,这么苦而且还全是责任。你想,运维不出问题没什么,不出问题大家觉得这个部门不存在,但一出问题,第一眼看到的就是运维。 + +极客时间:那你15年带这个团队的时候,做了什么? + +毕玄:当时我们就在讨论这个团队未来到底走向什么地方,这是一个非常纠结的话题,这也是后来让我去带整个团队很大的原因,我们想,既然工具和运维很容易矛盾,那干脆合成一个团队得了,探索一下,看看能不能解决。 + +极客时间:合并之后,探索了哪些方法? + +毕玄:我带以后,说实话我也觉得解决不了。 + +我当时接手最早的想法就是,让每个人一天腾出20%的时间来做一些工具的研发,后来发现这根本就不现实。但很多人就觉得是你们团队自己不想或者没有能力解救自己,肯定不是,只是因为有很多现实问题。包括运维的人,你说他不想?他自己当然也想,谁都不愿意每天就做一些很碎片化的工作,大家都想解救自己。 + +当时Google的SRE,大家都觉得是最好的团队对不对?但是如果你仔细看,关键一他的人多数来自以前非常资深的研发,根本就不是运维岗,那他和研发沟通,包括研发技能,肯定没有问题。第二至少我们外部得到的消息是,他们的人每天可以腾出30%的时间用来做研发类型的工作,而不是运维类型的工作,那就不一样了。 + +极客时间:Google他们的时间是怎么来的? + +毕玄:比例问题。如果能有一定的运维研发比例,让运维有时间去学习一些开发的技能,为自己写工具,这肯定满足需求。但是有多少公司能接受运维跟研发的比率是成一定比例?现在蚂蚁是接受的,所以蚂蚁跟阿里不一样。 + +极客时间:运维研发比例问题本质是什么? + +毕玄:其实是分工,就是运维到底是做什么的,这个认知。 + +比如说蚂蚁现在的安全生产团队,核心指标是整个系统全年的系统稳定性、资损情况,其他不承担的,很多让研发自己做掉。这样有了专业分工,运维团队相当于跟研发团队没有太大区别,只是业务研发团队做的是业务需求,你运维团队做的是运维业务的需求。 + +但之前,分工上大家就一直觉得运维是给支持的,再加上传统运维确实做了很多偏执行的工作。我不知道中国其他公司的状况是什么,反正在阿里,我们觉得这条路很难走。 + + + + + +  + +极客时间:在阿里很难走是为什么,当时运维团队的价值当时没有被看到?可以详细说下当时做了哪些尝试吗? + +毕玄:我们本来希望能跟集团谈一个数,比如运维就是一比多少研发,你不要老挑战我的人多。因为大家都觉得运维团队人很多。 + +我带的时候运维团队有一两百人,但是研发十几个人就能做一个非常核心的系统,所以研发团队看你,会觉得你比他人还多,运维动辄上百人,还说不够。问题是他没有想到我们是整个集团的运维,我们还觉得一两百人怎么够,得四五百更多。但这上面一看,你们有这么多人。 + +另外就是你说的,运维的价值其实很难被衡量,它的价值到底是什么? + +因为运维不像研发,研发直接面对业务,做的东西价值是可以被论证的,只要这个业务好,研发肯定有贡献。但运维到底做了什么?我说运维很核心的工作是保护整个系统的稳定,另外是成本控制,但这些都没有业务那么耀眼,而且保护稳定这种事,不出问题是没有人知道的,就跟安全团队一样,也是很痛苦的团队。 + +极客时间:蚂蚁论证了运维价值这个事? + +毕玄:为什么蚂蚁能搞定,是因为后来蚂蚁出过一些故障,阿里也出了,但蚂蚁对故障的重视度必须说比阿里高非常多。 + +因为蚂蚁是金融,如果它出故障,首先可能会引发用户的信任问题,第二会引发监管的问题,监管就是生命线,那对他们来讲就不是开玩笑的。所以蚂蚁会觉得我得力保稳定。 + +现在俊义带着的安全生产团队就是一个固定比例,每年不用申请运维名额的,研发你涨我就涨,非常简单。你想,一个固定比率,大家肯定不会一天到晚总那么饱满,就可以在日常工作以外,腾出时间做工具研发,把自己解放出来。其实这样就会越来越好,是个良性循环,但它需要时间。 + +所以我们当时想跟集团说定一个比例,然后团队要转型也需要一段时间,需要接受这段时间里人是比别人多的。 + +极客时间:比例的事,集团批了吗? + +毕玄:没有。我带了半年多,也试了一些其他方法,但这个问题在阿里太难解决了,所有人都这么觉得。 + +虽然我们知道Google大概是怎么做的,但都学不出来,而且那个时候蚂蚁也没有搞定运维和研发一比多少的问题,他们也是后来出了几次大故障以后,公司才觉得这是生命线。 + +当时我就觉得,我很难带领这个团队完成一次组织转型。我们是希望完成组织转型和升级的,向Google靠拢,变成一个非常资深的团队,能更专注做好工具研发,或者维护整个系统的稳定,因为SRE的定义准确讲是维护系统稳定,而不是什么发布、运维答疑这种杂活,但在中国绝大部分公司,我估计运维团队都是这个角色。 + +  + + + +  + +极客时间:这组织转型不了,你当时是怎么办的? + +毕玄:到16年这个问题太难解决了,当时正好行癫来任集团CTO,我们商量了一下,这么搞下去反正也解决不了问题,就提出了一个方案:把运维工作直接交还给研发,解散掉统一的运维团队,把运维还给了所有的BU,比如一部分人是支持淘宝的,一部分人支持搜索等等,按照BU把人全部还给研发,相当于没有了统一的运维团队。 + +等到分人的时候,所有人终于知道了,哦你们人确实太少了。以前他们觉得你怎么那么多人,但是现在大家摊在一张纸上来看好了,反正就是这个状况,支持你部门的人其实就1、2个人。 + +极客时间:这种拆分是怎么落的? + +毕玄:就是直接决定组织拆分。 + +极客时间:搞了多久? + +毕玄:应该是一个月就搞定了。 + +极客时间:会给这一批人选择吗,比如说哪个业务去多少个人? + +毕玄:没有选择,你以前对的哪个BU,现在你当然怎么分。不过因为拆的过程太粗暴,当时也引发了一波离职,导致有段时间运维很混乱,加上分散了人也不够,另外工具确实也比较缺失,各家BU又开始自己做工具,因为各家也受不了。 + +极客时间:直接组织拆分这一下够狠的,你们当时想到这个结果了吗? + +毕玄:我们能想到,但是还是可以接受。 + +因为我们认为只有这样,才有可能让研发承担掉一些很杂碎的运维工作,形成这个习惯,也希望让所有研发都至少知道你的代码跑在哪些机器上、这些机器在哪里、大概环境是什么。其他的方式很难做到这一点,你只要有个团队在这,研发就不可能去做。 + +你想,一个BU发现自己只有1、2个运维的时候,他们肯定也觉得人少,就会开始让部分研发承担运维的工作,这就是我们想要的,当然研发吐槽就很多了。反正我们觉得大思路上,想象的最后样子没有问题,但是怎么走到那个样子是个很大的挑战。 + +极客时间:选择组织拆分这种自损八百的方案,也是你们在15年到16年没跑通其他的方案? + +毕玄:因为最早大家能想到的方案肯定都是做一套很好的工具,研发用工具完成一些碎片工作,然后运维团队就可以像SRE一样很专注地去看系统稳定性,但我们当时看,这就偏理想化。因为这么走,第一你要先人员扩张。 + +极客时间:集团不给批,所以做不到。 + +毕玄:对,这第一步就很难,我的人是要翻倍的,但这个对公司来说…… + +极客时间:那统一的运维拆到每个BU之后,BU他们自己又开始做很多工具。 + +毕玄:对,工具又乱七八糟的。因为说实话工具重复做,意义很小,很多工具很类似,所以以前希望统一。 + +极客时间:以前运维团队统一的目的是什么? + +毕玄:最早应该是只有淘宝,因为就一家公司,它肯定是一个,但后来大家觉得淘宝双11的模式做得非常好,希望淘宝的经验可以去覆盖掉其他的,所以慢慢就把各家的运维全部合掉了。 + +统一的目的,一是相对来讲人效肯定是最好的,二是理论上你可以做统一工作来解决很多问题,运维尤其,这样基础设施能更好地统一演进,如果分散了没有规模,很多事情就很难搞。 + +比如A业务说我的业务,我要这样的机器,B业务说我要另外一台机器,因为业务方就是这样,什么对我最好,那我就要这样,但对后面设施的采购、维护都是极大的问题。如果能统一运维,还能节省成本,这个机器可能在你这个业务上浪费了点钱,但在全盘里其实是省钱的。但这件事,你如果没有统一的团队,太难做了。 + +极客时间:很多工具起来之后,效率和成本都不好,有把它们合在一起吗? + +毕玄:后来我不太管这个事,我知道分散造成了很多问题,但现在好像想统一了,鲁肃下面又成立了SRE团队,各个运维团队开始归拢了。 + +所以阿里对我们当年拆掉运维有很多争议,非常多,因为大家觉得现在的乱象就是我们当年那么暴力造成的。这个我们也认了,但我们觉得关键是没有解法。 + +而且现在合并,跟我们当年不一样,因为运维团队的职责变了,研发已经接受了。我们当年其实也是这样想的,我先拆一下,让大家习惯,后面我可以再整合,但职责就变了,只做全局稳定性的维护、机器管理这些。 + +极客时间:所以从组织演进的这个角度来看,当时拆分团队的核心目标也是达成了? + +毕玄:算的,至少现在SRE团队比当年的运维团队好很多,他们很多人合进去了觉得工作的挺开心,不像以前运维的人,简直是太苦了,他的情绪永远不是很好,压力又非常大。因为如果出故障,那不得了,运维绝对是第一个被问责的,但很多又可能是研发的代码系统设计问题,这个你又负不了责,这就很尴尬。所以研发总觉得运维没啥用,但事实上又离不开。 + + + + + +  + +极客时间:经过运维团队的合并和拆分这一招,这么看研发和运维的分工方式,是不是最开始就不太行? + +毕玄:是有点问题。你想,最早是一家创业小公司的时候,是不会有这个分工的,肯定是从头做到尾,写代码,自己发布上线,维护,全都是自己干。我2007年进淘宝的时候,发所有系统也都是我跟运维一起,没有说什么交给他了就不管了,没有,绝对不可能的。 + +后来某阿里高管有个形象的比喻,他说:你看你们现在,简直活得像大爷一样,写完代码就有一帮人服侍你,什么PM、研发效能、测试、运维,他们好像就是你们的保姆一样,你们写完就什么都不管了,什么都不知道。 + +所以从原则上讲,虽然我们觉得拆分运维团队这个动作是有点不大合理,但至少逼着研发提升了自己的技能,很多研发是不爽,但如果从他职业生涯来看,我们觉得不是坏事,你技能变好了,对不对? + +极客时间:但研发应该会压力比较大吧? + +毕玄:研发的抱怨其实差不多,我活已经很多了,每天要接一堆很碎片的需求,你们现在不想干,把活都扔给我们,包括测试团队当时也是希望研发自测,然后自己更多的做工具。所以研发那会觉得我不仅要管这些,还得管上线、发布后的整个状况,你们这些团队就是啥活都不想干,什么都扔给我们。 + +但研发你未来作为一个系统的掌控者,这些本来就应该知道的,也本来要掌握的,你做系统设计,不可能忽视下面的基础设施完全当它是个黑盒,不可能,就算现在是云,你也不可能把它当黑盒用,对你还得是个白盒。 + +极客时间:所以阿里强拆一定程度上解决了这个问题,但不能轻易用。 + +毕玄:必须要有很高的支持,因为这个动作非常大,会影响所有团队,内部肯定会有各种意见。 + +但我们认为组织演进的路线是什么?你不能说虽然团队的人整体都没有成长,但这样对公司是最好的。这样最大问题是团队会很难活下去,长期一定是个问题,一是不稳定离职率会很高,你老换人,研发线他每次找你也很痛苦,而且也很危险,公司哪天想换掉你很简单。 + +每个团队可能都要想一下成长空间问题,很重要的,因为不可能说公司哪个团队是个弃子。 + +  + + + +  + +极客时间:看未来,你觉得运维可能会往哪个方面发展? + +毕玄:我现在觉得,大厂的模式还是不错的,研发应该干掉运维的一些工作。 + +但这个前提确实是公司在运维侧的工具上要有一定的积累。如果你完全没有积累,按我们当年那么粗暴,也会引发问题,一定会有一段时间很崩溃。当然过了那段时间可能会工具百花齐放,但之后基础设施怎么统一又是一个问题。 + +但大厂的模式也有一个问题,对研发的技能要求太高了,就相当于研发你要懂运维。但从现实的人才池子来讲,这一点又很难实现,现实中多数研发其实不懂运维。 + +极客时间:小厂应该更是。 + +毕玄:对,这很正常,就像我们(贝联珠贯)招人也不可能这个要求,肯定首先在乎你的研发技能,运维差一点就算了,我们可以有运维团队,但也会陷入这个状况。但我们先期就会让运维更多偏向工具,一开始就是这个定位,同时也慢慢告诉研发,运维不是你的配套,只给你工具和文档,其他是不管的。 + +极客时间:对小厂来说,有必要制作自己的运维工具吗?现在市面上也有很多工具。 + +毕玄:但运维的工具很难完全通用,这个比较麻烦。通用的很难满足运维所有的需求,因为运维不像研发比较单纯,他杂活简直多到不可想象。 + +极客时间:所以只能慢慢转变? + +毕玄:只能看各家怎么看待运维。大部分公司其实就没有解法,但新一代的公司可能会慢慢好一点,现在研发在技能层面的要求更完整一点,运维团队也在慢慢转向类Google的SRE,更多提供工具,研究整个系统怎么样做得更加稳定,而不是发布。 + +极客时间:那运维的职业发展路径呢,你怎么看? + +毕玄:架构师。 + +以前我们跟运维团队说,其实他们最大的出路是成为真正掌控整个系统的最大架构师,因为说实话,架构师其实最好是从运维出来的,研发是出不来的,因为研发没有全貌,但运维是一定有的,他要负责线上,所以他看到的一定是整个系统,知识面一定是超级宽的。 + +只不过关键是运维你能不能成长为这样的角色,因为架构师最大的问题是研发线认不认可你。研发线会觉得你就一个运维,又没写过代码,你提的方案我觉得不靠谱。所以这就是为什么Google相对好很多,因为他的SRE就是原来的资深研发,研发挑战不了。 + +研发是一个很难伺候的角色。我后来去研发效能了体会更深,就更难了,研发效能是给研发和运维做工具,这个难度简直高到天了。就像我说的,只要研发觉得自己能干的事都很难。 + + + +  + +水友讨论区 + +对谈到这里就暂时结束了,主要聊的是毕玄从研发转运维的经历。 + +先说点正经的,运维团队,毕玄认为核心工作是保护整个系统的稳定和成本控制,而运维人最好的出路是架构师,但在当时的阿里,面对运维团队价值模糊的困境,他试图寻找解法,但似乎也看不到希望,迫不得已选择了一条争议巨大的路线。 + + +但每个团队确实都要想一下成长空间问题,你自己所在的团队,你觉得现在的空间如何,未来的成长空间可能是什么?能满足你的个人发展需要吗? + + +最后说点不太正经的,今天来个特别的吐槽环节: + + +“我完全可以干,之所以我不干,只是因为我不想干而已”,你的岗位存在鄙视链吗,是什么呢? +如果你是一线运维,欢迎吐槽当年和研发协作的那些事,当然不能厚此薄彼,如果你是一线研发、测试、安全或其他团队,也欢迎写下你的肺腑之言。 + + +毕竟有一位哲人说过,吐槽,是迈向理解的第一步,期待在留言区看到你的精彩发言;) + +下一讲我们会接着今天对谈的尾巴继续聊,毕竟毕玄说运维之后,他去了比运维更难做的研发效能团队,会发生什么呢,下一讲见。 + +拓展阅读 + +1. 如果你对阿里当时智能化运维的具体细节感兴趣,可以看阿里开发者的这篇:阿里毕玄:智能时代,运维工程师在谈什么?这是当时毕玄做“智能时代的新运维”演讲的整理稿。 + +2. 在2016 Velocity China 上毕玄也做了一次演讲,主题是阿里应用运维体系演变 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/08基础团队:研发效能部门,解决不了研发效能问题.md b/专栏/超级访谈:对话毕玄/08基础团队:研发效能部门,解决不了研发效能问题.md new file mode 100644 index 0000000..df2fc07 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/08基础团队:研发效能部门,解决不了研发效能问题.md @@ -0,0 +1,281 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 基础团队:研发效能部门,解决不了研发效能问题 + +你好,我是叶芊。- + - +上一讲我们聊到毕玄从研发转到运维,对于当时成长空间极其严峻的运维团队,他选择兵行险招,解散掉统一的运维团队,把人还给了所有BU,尝试改变研发乃至集团对运维团队的价值认知,毕竟只要研发觉得自己能干的事,都很难做。- + - +但是运维之后,他又去了更惨的研发效能团队,给研发和运维做工具。- + - +对于这个“难度简直高到天”的新团队,这次他要面对的是什么样的难题?他又是如何发挥自己的长板,分析运维未来的成长空间,找到那几个能体现团队价值的亮点呢? + + + - +极客时间:好不容易从运维出来,结果你又去研发效能了?那个时候应该是2016年? + +毕玄:对,其实就是运维拆完,我开始带系统软件事业部跟研发效能部。 + +极客时间:新团队多大? + +毕玄:一开始大概三四百人,后面有五六百。 + +极客时间:你当时带这两个部门的目标是什么? + +毕玄:当时安排我去带这两个,系统软件是有明确目标的,就是要做好统一调度,因为行癫上任作为CTO给阿里定的两个最重要的事,一个是统一存储,一个就是统一调度,统一存储就是盘古,统一调度就是我。所以系统软件的成立很清晰。 + +研发效能是因为之前基础设施的运维,在阿里云、集团各有一个团队,现在最大诉求是整合在一起,做成统一化。至于研发工具侧,没有太多诉求,当然我带团队以后肯定不希望没有任何目标,还是希望能解决研发效能团队的定位问题,这就是第二个目标。 + +极客时间:定位问题?也是像运维团队一样,研发效能当时的价值没有展现出来吗? + +毕玄:对,研发效能是做研发工具的,跟运维一样,大家也会觉得这个团队人很多,都觉得一个研发工具团队200多人?简直太多了,没有一个人觉得你手上人少。 + +我以前带运维在外围的时候也这么觉得:研发工具团队人怎么那么多,应该给我点人来做工具,给一半就挺好。等我带的时候一看,哇这个团队人也太少了。我带的这俩团队简直了(笑)。 + +极客时间:研发效能人少的原因是什么? + +毕玄:因为研发工具链条特别长。 + +研发工具你想包括了什么?首先就是代码托管、代码编译,然后需求管理,要做整套需求管理的系统,需求出来了以后你要做项目管理,还要对研发模式做管理,像阿里研发模式不统一有无数种,主干开发、分支开发、敏捷开发……各种都得支持。列举一下你最后就发现,团队里做每一项功能的可能只有俩人,然后就活不下去了。 + +我说这团队太难带了。因为跟运维一样,大家也会觉得你的价值对公司很有限,但事实上你又必须存在,你盘点觉得自己人很少,但公司觉得很多,其他团队也都认为你人很多,那你就很尴尬了。 + +极客时间:本质上,人多不尴尬,尴尬的是人多,别人还觉得你没价值。 + +毕玄:对,因为这个团队除了支持以外,确实很难找到新目标。他原来做的工作就是支持,你研发完对我有什么需求?像编译,以前只支持Java,现在你要C,那团队规模就会越来越大,Google的编译团队就上百人了。所以你看我们人很多,但把功能点全部拆开,类比其他公司,我们根本没人,哪有人? + +但是如果你,像运维一样,永远被别人定义成一个支持团队就很惨,你的价值永远得不到大家认可。我们就想尝试改变,因为你不做出改变,人永远不够。 + +极客时间:所以这个问题,你是怎么办的? + +毕玄:我当时带研发效能就在想,短期能不能稍微集中力量做出一两个亮点,只要有一两个亮点就足以证明这个团队对公司的整体研发效能是有价值的,这个团队还是有意义的。 + +后来我们认为对工具层面的研发效率来讲,代码方向是最好的,就谈了一些人开始做代码搜索、代码智能化,其他地方我们很难解。 + +  + + + +  + +极客时间:当时你是怎么分析的,可以详细讲讲吗? + +毕玄:核心就是想清楚你到底能真正解决研发的什么效率问题。 + +第一个最大的问题当然是协作效率。但这其实是个研发模式问题。 + +阿里因为强制统一成一套系统了,所以这套系统压力非常大,一是阿里太复杂了,各业务的状况不一样,有些是创新业务,有些是成熟业务,有些可能是晚期业务,每个都得来不同的。二你想任何一个团队说我觉得现在研发模式不好,想换一个,那你的系统就得支持吧,因为你挑战不了他。 + +所以我们当时说:想改变这个局面,只有一种可能——研发模式我们比他们还懂,就是对阿里各种不同阶段的业务,你能定义哪个更合适。但说实话这并不容易。 + +这么多年了,全世界只有某几位顶尖的天才软件工程大师,偶尔提出一两个开发模式,改变了整个业界的协作模式和研发模式,像Linux提出Git彻底颠覆了以前的CVS、SVN,除非能做这种,那你团队影响力绝对第一流。但这太难了,培养出一个大师的可能性不是很大。 + +而且研发模式很复杂,因为它其实是个协作问题。像敏捷在中国多少年了,包括Google讲代码仓库应该是一个大仓库提高整体的研发效率,但你看中国有多少公司是一个大仓库,阿里也想做,但也很难,理论上Google讲了有A、B、C、D多少好处,我们也觉得Google讲的很有道理,但关键是工程有个落地问题,你有这个想法,怎么走到那?这就是工程。但协作这个工程挑战非常难,我们觉得几乎不可能。 + +极客时间:所以第一个问题协作效率,短期是不可能了。 + +毕玄:想解决好协作效率是个长期的活,我们就不纠结了,囤一两个业界挺有名的理论专家尝试看有没有机会提出像敏捷开发、精益开发的思想去影响研发线,让他们接受。 + +但说实话,我们没抱太大的希望,公司有这么多种类型的业务,要高度抽象出一个很好的协作模式,太难了。到今天为止我觉得这个局面也很难改变,毕竟这相当于你要比他们研发团队还专家。 + + + + + +  + +极客时间:第一个协作效率短期PASS,第二个是什么? + +毕玄:第二个对研发来说以前阿里最影响的效率是什么呢?其实是测试。 + +测试是个大问题,到现在也是,很多公司尤其服务化以后更严重。因为你原来就一个系统,测试还好,不存在互相干扰,服务化以后好了,有几百上千个系统,那完蛋了,一放到测试环境,你都不知道谁影响了你,所以各家公司就搞出了N套方案,来解决互相干扰的问题。 + +比如阿里以前先搞了一套测试环境,所有人都在上面放新的代码,但慢慢的大家就发现这环境还是不要用了,没法用,全是异常,你查问题可能发现根本不是你代码的问题,都是别人的,但别人不理你,这就没法搞下去了。 + +所以,我们又搞了一套日常环境,日常环境是比较稳定的,里面只允许放测试过的正常代码,但因为前面的测试环境不受大家认可,所以大家就时不时在日常环境里跑,然后就又尴尬了(笑),所以日常环境也慢慢。 + +极客时间:变成测试环境了。 + +毕玄:对,但一旦变成测试环境就又很乱。 + +后来希望从中间件的层面去解决,我们把这个叫二套环境,比如在这个环境里,你现在是测试,但别人调用的时候不会调用到你,会调用一个稳定版本。但是中间件要解决,很复杂,因为不光有服务调用,还有消息各种各样的问题。最后我觉得也解的不是很好。 + +所以阿里后来都已经直接到预发环境去测了,预发其实就是线上生产环境,只不过用户访问不到,但我们内部能访问。因为用的是线上,可以确保如果出问题,应该就是我的问题。但是后来预发环境也乱套了(笑)。 + +反正为了一个环境问题,尝试了不知道多少年,现在应该还有个团队尝试新的,专注怎么解决环境干扰问题。我们跟很多做研发工具的团队说,如果能解好这个,你们的工具一定很好卖,中大厂这都是核心问题,因为测试效率如果很低,意味着最终上线的周期肯定会被拉长。 + + + + + +  + +极客时间:前面分析的问题倒是很多,一协作效率开发模式,二测试的干扰问题,但是短期都不好解啊。 + +毕玄:后来我们就觉得,对研发来讲,我们真正能做的是给他的工具。 + +你想研发的一天,除了协作这些乱七八糟的事情以外,核心的时间肯定还是花在写代码、编译、打包、测试上,所以我们就在想这4个我们能干啥。测试,前面说了只能看环境问题怎么缓解,解决不大可能。 + +剩下写代码、编译、打包这三个环节,能不能尽量为你缩短,这就是我们探索的。 + +编译,我们觉得可以搞一下,像Google Bazel提高编译的速度,我们也投入了一些资源,确实有效果。代码编写方面也是在做的,不管是IDE的改进,还是做代码智能化,重点力量开始投向这些。 + +其他的,支撑好业务和需求就行,在想不出什么创新的情况下我们也别纠结,就当就是个支持好了。但在这几个地方,我们是有机会做出亮点的。说实话对公司来讲,一个团队,一个这么大的团队,其实有一个亮点就足够了,就可以解读这个团队存在的必要性,最怕的是它一个亮点都没有。 + +极客时间:判断代码方向是亮点,是因为研发天天用,如果能有效提升的话,作用很大吗? + +毕玄:因为代码方向,我们觉得智能化是突破点。 + +像代码托管就没有突破点,GitHub就是天花板,你最多说我做一个中国的GitHub,等一个机会做备胎;或者你指望Git之后又来了一个新东西,但这需要另外一个天才,Git只是Linus随便玩玩做的东西,这就是天才,你没有。 + +但代码智能化,即使像阿里的工程师,质量已经是中国中上了,事实上,所有工程师写代码的水平也是有很大差距的,我们希望用工具把代码的平均质量拉高,比如从现在的50分拉到60、70分,对公司来讲就很好了,你说要做到比优秀程序员还好,也不现实。而且这是在阿里,如果做得好,这套工具对外可能会把别人从20直接拉到60,那完全不一样。 + +这个事,在阿里我们觉得是非常有机会做成的。因为你的代码想智能化,比你自己写得更好,首先你需要有一个优质的代码仓库。 + +极客时间:代码仓库是需要积累的。 + +毕玄:阿里绝对具备,因为阿里的大多数代码就是中国中上的优秀工程师写出来的,而且这些代码又是在生产环境运行的,是实际被论证过的。 + +我们还能捞到这些代码运行的实际数据,就能知道哪些代码其实是更优秀的,比如说这段代码每天被访问了很多次,但消耗的资源很少,而且响应时间很快,那说明写得挺好的。所以我们可以很好地训练AI什么叫优质代码,但这需要一个代码仓库。所以GitHub也在做一个代码智能化的工具Copilot,现在要收费了。 + +极客时间:GitHub Copilot那个自动补全的? + +毕玄:它基于的是大量Public的仓库代码,但这个代码的水平你很难说,当然GitHub有些高水平开源质量不错,但你还是没办法跟有实际生产环境数据论证的比,这是不在一个水平线的。 + +而且我们觉得阿里还有最后一步,如果实在不行,可以时不时搞全阿里代码Review比赛,选阿里大家公认的几个写代码非常好的人来评审,然后把这些反馈给AI,那这个AI就更加智能了。 + +所以阿里走通这条路的可能性比多数公司高非常多。当然大公司都有这样的体会,Google、腾讯都一样,因为大家确实是偏中上水准。 + +极客时间:有多年代码积累,有生产检验,加上AI技术现在也比较成熟,最后实在不行还能加人工评分。 + +毕玄:对,这如果有机会做好,可以让阿里所有工程师在写代码的时候有自动辅助,你写的时候我就能猜出你下面要写什么,然后给你一段相对优质的代码参考,你只用改一改就可以了。 + +极客时间:这种工具,研发认可吗?愿意用吗? + +毕玄:研发对这些接受度很高,你能帮我写得更快还写得更好,那好的。所以GitHub Copilot要收费了,我在很多程序员群里看到都是愿意付费支持的。而且说实话我觉得他做的也没有那么好,离我们当时想象的那个代码智能化工具还有差距。 + +很多协作工具像office、飞书等等为什么受欢迎?是因为能提高我协作的效率,对研发也一样,如果有个东西能提高我写代码效率,我愿意付钱的,个人都行,都不需要公司。其实IDE 不就是这样子,以前很多人觉得我用记事本写代码很牛,我们说那是傻,工具能极大提高你的效率,干嘛不用呢?那不是傻吗?所以IDE 每年能收这么多钱,你问工程师IDE 到底有什么好?就是因为它能让我们写代码的效率更高。工具就是这样。 + +极客时间:所以研发效能部的目标终于很清晰了,短期重点代码智能化做亮点,长期做研发模式、测试环境干扰。当时你们做到什么程度呢? + +毕玄:我们就做了一年多,因为一两年之后我就不带这个团队了,但现在这个团队应该还在,阿里前段时间好像对外发了一个代码智能化的开放工具(云效Codeup)。 + + + + + +  + +极客时间:亮点仔细分析了,也做了很多工作,你觉得研发效能的团队价值有明显提高吗? + +毕玄:还是很难,因为研发效能这个东西很难被衡量。 + +大家常见的是衡量研发对需求的实现速度。比如一个需求过来原来是一周,你现在变成两天,但这里有个问题,需求的粒度是什么?运维或者产品提一个需求过来,你很难拿什么东西去衡量这个需求的粒度,最后到了研发这边其实就没法衡量。这是个非标。 + +极客时间:大家没有公认的大概指标吗? + +毕玄:没有,我觉得只有一些小点。比如说大家公认Google的编译做得非常好,就Bazel,但这并不代表研发效率。研发效率是个综合话题。 + +而且像我们投入很多去做代码,你觉得自己做了一个特别牛的、在全球都很领先的工具,但对研发来讲,没用,对于研发侧来讲,他们认为我效率的核心不在这,阿里的研发抱怨最多的、最影响他效率的是什么?是前面的环节。所以这种团队很尴尬。 + +极客时间:抱怨最多的,是前面的需求搞不清? + +毕玄:对,是需求和协作,这环节你说你能帮他啥?研发说我最大的问题是一天能写代码的时间很少,大部分时间在开会。但我说这是管理问题。 + +你想为什么一家创业公司研发很快?以前淘宝也很快,上午提需求,下午就上线了,为什么?因为以前根本不需要评审,也不需要各种环节,你过来说一下,我立刻就开始写代码。现在怎么可能?现在你一个需求提过来,背后可能涉及十个团队,那这十个团队我得先讨论一下吧,还得排个期吧,开会就已经好几天了,这还做个啥?两周做完一个需求就不错了。 + +极客时间:所以总的来看研发效能,没做出东西很尴尬,做出了东西还是挺无力的,这怎么办? + +毕玄:就看团队定位了,如果对公司来说,能接受这个团队在某些点上能做到全球TOP,不需要我们去论证自己做出来的东西的价值,那这个团队的存在空间就有了。 + +像美国很多公司都认为工具才是核心,不需要说来论证一下你为什么做了这个工具?你效率提高了多少?他们信仰,只要工具做好了,效率就提高了。我以前拜访Facebook,他们的工具团队很受重视,大家都很向往,觉得他们简直太牛了,因为他觉得我用的都是你们团队做的东西。 + +但中美在软件这一侧的信仰差别是很大的。美国可能因为人太贵了,所以他们一开始就特别相信工具,中国相对来讲人的成本低一些,所以一开始中国不觉得工具有多重要,我就是堆人。只有等到两种情况:一发现堆人也解决不了问题,二开始感受到堆人的成本,只有到这两个阶段才会觉得那我们得做好工具。 + +但这个时候他对工具的期望太大了,所以这个工具团队很难做,因为工具并不能真的彻底解决问题,如果能彻底解决,那也太简单。因为很可能这个团队解的是研发效率里最小的那个环节,最大的环节根本是公司层面的问题。 + +极客时间:美国是很向往工具团队,但中国是你们作为保姆团队该给我支持? + +毕玄:而且还都觉得你们做的太烂了。研发就是那句话,只是我没空自己干,否则我一定能比写得你更好,高层,你又很难向他证明为什么工具很重要。 + +office的故事本来我们觉得可以一直讲,你说office重要吗?Office当然重要,所以你愿意付费,那为什么研发工具你不愿意?office你有论证过吗?你也没有,但你就是相信了。相信真的很重要。 + +海外对开源到商业也是信仰,他相信你开源做好了,接下来的商业化一定能做好。但在中国这个是要被论证的,但论证很难,它就变成了一个悖论。 + +中国像ToC不也是这样,最早ToC能吸引大量的免费用户,但你也要论证,你吸引了这么多免费用户为什么最终你能赚钱?其实没有证明,所以淘宝最早被无数人质疑,你每年亏这么多钱,到底能不能盈利?这就必须感谢雅虎。 + +极客时间:广告模式? + +毕玄:对,雅虎成功创造了互联网广告模式,直接把免费流量转移成羊毛出在猪身上,所以后来做ToC的人就不用证明能不能盈利这个事,只要你能做到用户量很高,大家就觉得你一定能赚钱,当然也不一定,就像共享单车,用户量做得很高,最后还是没有盈利。但是他一开始是不用解读的,这就是相信。 + +这在软件上很明显,国外就相信了,但在中国要面临很大挑战,所以中国各家做工具的团队都过得不是很好。 + +  + + + +  + +极客时间:近几年,业界特别重视提研发效能,这种关注度是不是也是分阶段的? + +毕玄:对,大公司肯定会提的,研发效能每年确实是被提的核心话题。 + +极客时间:去年好像提的更多一点,是因为疫情吗? + +毕玄:没有,我觉得都会提。 + +为什么大厂现在总提研发效能,因为大厂到了后面人效比是下降的,所以只要过了那个点,很多公司都会提,因为他每年都会觉得我业务增长是这样,但你研发人员怎么还在不断增长,他当然觉得有问题,加上研发又比较贵,成本比较高,他就会说你们研发的效能得提高啊。 + +但关键是研发效率到底怎么提高?大家就指望那个研发效能团队,但其实那个团队根本就承担不了这活,他承担的只是很小的一部分,最大的部分实际是管理问题。 + +极客时间:或者说因为到了现在这个阶段,其实很多公司都找不到更好的方式去盈利了? + +毕玄:研发砍十个人公司搞不好盈利了,阿里砍掉这一轮,可能这个季度的利润都要增加一些,因为研发太贵。 + +所以要说起来,他们不应该老挑战研发效能,想快的,还不如把产品线砍掉一半,研发效能立刻就提高了,因为无效需求太多了,只要产品经理够多,研发就永远不够,毕竟产品经理只要人在,他就会想需求。 + +极客时间:既然经过这么久的实践检验,为什么大家还是会对研发效能关注这么高? + +毕玄:因为大家还是幻想能有一个解决方案。毕竟一旦解决好了,必须说对公司的帮助无比巨大。 + +极客时间:那你怎么看,你对这个团队是抱有信心的? + +毕玄:对,我认为如果你能做一个工具,让研发很喜欢,其实就可以,至于对公司的价值,就看团队的人自己怎么看。 + +说实话,我觉得即使团队在代码层面做得多好,可能在研发团队能得到比较好的口碑,但公司也不会觉得你解决了研发效率的问题,因为公司层面只看最后的人员增长,就很复杂。 + +所以我跟他们讲,研发效率是个综合过程,你不能做的,再怎么叽歪也没用,但我们能做的,那就尽可能做好呗。如果你能做到世界顶流,就算不能在公司被认可,但你在圈子里是会被认可的,就像Google做Bazel的团队,你以后的职业生涯是没有问题的,如果在这家公司身上获取不到,在另外一家公司身上也会获取到。那就别纠结了,关键纠结也没啥用。 + +  + +水友讨论区 + +今天的对谈到这里就暂时结束了,毕玄担任研发效能部Leader的这段经历,确实让我感受到了为什么很多人会评价他对方向的判断非常好。 + +我们先简单复习一下。首先,出发点是对公司来讲,一个团队需要有一个亮点,才能解读这个团队存在的必要性。那对研发效能部门来说,他们平时做的事是给研发提供支持、做研发工具,如何找到亮点呢? + +毕玄分析核心就是想清楚这个部门到底能解决研发的什么效率问题,一个研发,一天的时间花在协作、写代码、编译、打包、测试上,于是拆解下去有了这么几个方向:协作效率、测试的干扰、代码智能化工具、编译工具等,再针对每一个方向思考可行性和见效周期。 + +虽然最后非常现实,也没能有效提高团队在集团的地位,但毕竟尝试有了效果,后面可能就是时间问题了。 + + +回顾自己呆过的团队,你觉得有清晰的亮点吗?参考毕玄的分析思路,你认为自己团队短期可以发力的亮点是什么呢? +工具,被公司寄托厚望却又很难被认可价值,你在工具团队做过吗?有遇到什么尴尬又难解的问题吗? + + +欢迎在留言区分享你的思考和故事。如果有其他感兴趣的话题,记得留言。 + +下一讲我们会聊一聊毕玄做统一调度的故事,下一讲见。 + +拓展阅读 + +1. 如果你对测试环境的难题感兴趣,可以看阿里开发者的这篇:在阿里,我们如何管理测试环境? + +2. 关于工具的作用,毕玄之前写过一篇小短文:程序员的生产力工具 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/09统一调度:只是问题非常多而已,摔出来就行了.md b/专栏/超级访谈:对话毕玄/09统一调度:只是问题非常多而已,摔出来就行了.md new file mode 100644 index 0000000..a97fda8 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/09统一调度:只是问题非常多而已,摔出来就行了.md @@ -0,0 +1,273 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 统一调度:只是问题非常多而已,摔出来就行了 + +你好,我是叶芊。- + - +上节课我们聊到16年毕玄拆完运维去带系统软件事业部跟研发效能部的经历。- + - +对于高层根本不感兴趣的研发效能团队,定位是个大问题,做了一通现状分析之后,他终于找到了清晰的发力点:短期做代码智能化,长期解研发模式和环境干扰。- + - +对于另一个团队——系统软件部,虽然高层给了明确目标,但他说做的也很不顺利,最后能做成也很难讲,可能时机比较巧。- + - +作为自己在阿里14年的第三大亮点,统一调度的成功居然归因于“时机”?为什么?让我们跟着亲历这个集团级项目的总架构师,看一看当时有哪些障碍?他又是怎么处理的? + + + - +极客时间:你当时带两个团队,研发效能之外就是统一调度,这个部门的目标是什么? + +毕玄:本质也是成本,阿里内部以前有好几套调度系统,这次想做成一套统一的,我们叫Sigma,向Borg的下一代Omega致敬。 + +调度我们做了很多年,2011年做容器化T4就是,核心目标就是为了控制成本,当时我们做了两三年,大概知道了在这方面Google的Borg做得非常好。 + +那个时候传闻Google认为自己的核心竞争力是什么?最早他就做搜索,他认为自己最重要的竞争力是,一我排序结果的准确度比多数公司好;二做同样的效果我付出的成本是你们的1/10。这确实是,成本如果差这么远,商业上就没办法做下去了,这里面,Google觉得调度系统Borg承担了很大角色,类似它的Page Rank算法,是整体竞争力的一部分。 + +所以外部很少有Borg的信息,保密性做得非常好。后来我们知道一点是,2015年Google发表Borg论文,那其实几年前就写好了,只是内部一直摁着不让发,觉得可能对业务的核心竞争力有影响。 + +极客时间:所以13年你们了解到的信息是什么,可以具体说下吗? + +毕玄:就是个思路。我们知道了以后都觉得哇这个思路简直太完美了,其实大家都能想到,但我们需要有人给信心,因为这个思路大家都觉得太难。 + +是这样,因为多数公司的机器会分成很多个池子,最典型的是一个机器池子用来跑在线业务,另外一个用来做大数据的业务。 + +大数据这个软件的核心设计思想就是尽量并行化,把一台机器的资源全部吃光,所以大数据特别吃资源,能把池子用得特别满。而在线业务,不是不想吃资源,它必须考虑的是什么?是稳定性,如果出问题了,我最好要有足够的冗余,加上一天还有很多不确定的高峰,所以我机器的余量一定是为高峰准备的,利用率就没有办法很高。 + +极客时间:在线的机器量,可以根据高低峰来伸缩吗? + +毕玄:但事实上这是一个悖论。因为对在线来讲,稳定性是最重要的,如果你不断伸缩,万一出问题了,可能得不偿失。很多高峰是无法预测的,即使是淘宝看起来都是做大促才有高峰,但如果有个社会热点,除了微博,淘宝也会受影响,以前比如谁出了名,有了件衣服同款,那立刻爆。这种事情是没有办法预测的。 + +除非你可以做到秒级以下的伸缩,那可以。我后来带调度,也给了人去探索这个方向,但必须说真的很难,至少目前我们觉得从技术上来讲很难突破,伸缩的风险不那么可控。 + +极客时间:所以一方面是大数据机器用满,一方面是在线空闲,在线也不好伸缩。 + +毕玄:很多公司到了一定规模,在线机器增长其实还好,因为在线跟业务基本成正比,就是QPS,比如说现在100,明年你希望做到200,那我就加机器;同时,因为每年的机器比上一年更好,所以从预算角度来讲,公司觉得是合理的,业务增长20%,你机器增长比如15%,那当然可以,至少没多出钱,能接受。 + +但大数据就有问题了。通常大数据机器只要开始用了,每年的增速会越来越快,因为存储量一直在那,而大家想采集的数据细节会越来越多,这样才能更精准地画出特征,所以大数据的机器就会多;另外公司的经营一定会越来越难,以前增长比较容易获得,可以粗放式,但后面你肯定要精细化,但精细化对大数据的要求又越来越高,所以机器会越来越多。 + +这个时候,技术层面大家都很容易想到一个方案:既然在线这边这么混,大数据这么满,能不能合在一起,让大数据用在线?这就是当时Borg给大家的核心思路。 + +极客时间:把在线和离线混部? + +毕玄:对,但其实Borg自己压根就不是这样,它诞生的时候就认为这两个本来应该在一起,我们以为一组机器跑大数据,一组机器跑在线,它一开始就认为干嘛要分?所以这就是Google。 + +极客时间:理念先进,都是机器,我只是时而跑离线时而跑在线。 + +毕玄:你说对了,Google觉得反正我机器在这儿了,该跑啥就跑啥,哪有说这个机器只能用来跑这个,没有。 + +Google当年有一个高管跳槽到百度,他第一次看预算的时候发现还分大数据机器和在线机器就很疑惑,为什么还要分机器类型?他说我们从来不分。所以百度后来做了Matrix,拿了两次一百万美金的百度最高奖。其实百度Matrix的思路就来源于Google。 + + + + + +  + +极客时间:在离线混部,这个思路倒很直白。 + +毕玄:这个思路大家确实也能想到,也都觉得应该这么干,很多人就会问了:为什么不这么干?那肯定是有原因的。 + +从技术上讲,大数据跑到在线确实有很多问题。首先机器以前是分开的,最典型的就是通常在线机器是1块盘,大数据机器是12块盘,因为它跑的时候需要算非常大的数据,但在线以前数据量非常小,大数据上去跑的时候,存储不够,大家觉得没法搞。 + +另外物理的基础设施条件也有要求。 + +第一在线机器和大数据机器要在同一个城市。比如从A到B的网络我们叫城际带宽,如果不在同一个地方,意味着要走这个,但城际的网络带宽非常贵,你如果跑大数据就更不得了,要求非常高,这条路是不可能的,所以首先要的是大数据和在线搬到同一个机房。 + +而且当时物理基础设施除了机房,还有网络的问题,以前我们的网络是千兆,对大数据来讲不够用的,它需要万兆以及更高。千兆就要搞各种限制,所以也很痛苦。我们2014年只能在上海机房搞个小的试验环境,去做一些试验。 + +除了基础设施,还有干扰的问题,因为大数据任务会吃光所有的资源,如果你在线也同时跑,会不会影响到在线业务的稳定性?如果被干扰了,导致你的响应时间下去了,就完蛋了,在线业务会不惜一切代价保稳定性,你想如果业务都挂了,省钱有什么意义,对不对? + +极客时间:以前机房分开也是出于成本考虑吧? + +毕玄:对中国西部城市比如说内蒙古,电非常充足,电费非常便宜,加上温度通常比较低,建机房就很好,很省钱。但问题是互联网的出口通常又在一线城市比如北京、上海这几个点,做在线业务是需要互联网出口的,我必须在这些城市或者附近。 + +这就奠定了以前大数据、在线都在不同机房,所以Google这个思路一开始在中国就做不了。当时我们面临的第一个挑战就是这个,怎么说服高层阿里建一个大机房,让在线和离线搬到同一个地方去? + +但这对任何一家公司都是一个非常大的决定。建一个大机房是几十亿的投入,要找到一个地方电费便宜,也要在互联网出口附近,也要探讨清楚ROI到底是什么?因为开始肯定要增加投入,以前没有这些,现在砸好多钱,你到底能不能省回来?这就是又是那个论证问题。 + +极客时间:所以机房问题,团队也没办法,只能等公司决策? + +毕玄:那没办法。现在阿里是建在张北、南通等几个地方,但你看这几个地方显然都不是大城市,但离大城市又不远,既享受到了电费、气候,又享受到了互联网出口,这就是非常好的选择。百度最早在阳泉也是,建了一个30万台机器的大机房,然后是阿里15年建的张北,腾讯也开始,后来思路就全部统一了。 + +极客时间:后来阿里建了大机房,是因为说服了高层?还是看别人做了? + +毕玄:那倒不是,阿里有很多原因,主要是因为云起来了,对我们来讲小机房效率不是很好。所以后来统一调度能做,也很难讲,可能是时机比较巧。 + +反正后期走这个方向的公司都很痛苦,因为你前期物理设施不是按照这个来的。像Google是一开始就是这么来的,所以它不存在这些问题。我们后走这条路的都很尴尬。 + +  + + + +  + +极客时间:在离线混部这么多问题,当时感觉都没有解法,你们做的时候有信心吗?具体进展是怎么样的? + +毕玄:信心是有的,因为统一调度说实话跟做异地多活不一样。 + +这次有参考对象,只是资料不公开而已,但大思路在,所以你的大方向是有的,异地多活是没有大方向的,纯靠自己摸索,完全不知道能不能走通,很可能会走挂,但调度,我们觉得Google能干成,至少这条路走下去应该没问题,不会走不通,只是要解决的问题肯定非常多而已。 + +但在阿里,必须说我们做这件事情难度比百度更大。百度是高层Push大家这样做,而且大数据团队有很强的动力,在阿里,我们有很强的动力,但我们是在线业务团队,不是大数据团队,这个事想做成,很多工作是要大数据团队做的,当年他们还有别的很多事情要干,觉得这不是我的重点。 + +所以各种原因,尽管我们从14年开始做,但进展一直比较慢。 + +物理上的限制,是等15年建大机房了才有可能性了,然后网络要升级到万兆,上面升级到25G,40G,100G,到了16、17年那个时候网络都具备了,也没有问题。剩下要解的核心问题就是大数据对在线的干扰,还有两边机器的磁盘不一样。 + +极客时间:磁盘问题,后来网络好了是不是就解决了? + +毕玄:网络升级上去之后我们可以走计算-存储分离。 + +但计算-存储分离内部当年也争论非常大。原因是大数据软件在一开始的核心设计思想,除了高度并行、充分使用资源,还有一个是“存储和计算一体化”,就是调度的时候会尽量让任务和存储在同一台机器上,这样算起来最快。但你现在告诉大数据团队不要放在一台机器上,这其实挑战了大数据的很多思想。 + +所以内部争论非常巨大,但我们反正可以逼着你必须走这条路,比如说卡预算各种。 + +极客时间:跟以前异地多活一样不给你分机器? + +毕玄:对,因为我所有机器是没有磁盘的,你必须走分离,否则我在线给你机器你也跑不了。 + +所以基本等16年阿里开始大力提统一调度,也有了正规军,很多问题才慢慢被解决,一是物理条件具备了,其实是18年才具备的,但大家在16年开始探讨,觉得这个方向可行,基础设施就去配套准备,所以大机房、网络都在那两年完成了。 + +极客时间:到16年那个时候,对在离线混部方案的价值,大家的认知也统一了? + +毕玄:我觉得很大的原因是预算上大数据对成本的压力已经非常大了,必须要控制。 + +但控制的思路我们探讨了很久,觉得最好最完美的仍然是Google。你想,在线有一大堆机器在手上,如果能把大数据跑上去,相当于不用花钱的,大家拍脑袋想都觉得能省好多钱,是个好方向。 + +极客时间:现在就剩下干扰问题。 + +毕玄:对,就是阿里的操作系统团队。系统软件部在我一开始组建的时候,操作系统团队可能只有10个人左右,人很少,然后到2018年的时候大概有100人,主要就是为了解决干扰问题。 + +极客时间:你们当时遇到了哪些问题,业界有参考吗? + +毕玄:Google尽管在论文里提及一两句,但不会讲更多细节,他论文的风格一般是这样,只是告诉你我很牛,但要怎么做到这么牛不会讲。你只能自己实践。 + +我们就必须靠大量人力去堆,在这个过程中肯定会出问题,但只要出了问题以后我有专业的人,可以把问题迅速解决掉就能做。所以我们其实是这样摔出来的,也没有什么。 + +这因为一方面公司信任,另外一方面是我们的在线业务有回滚、容灾各种策略,也有异地多活可以切流量,相对来讲是在比较安全的情况下做尝试,所以我们也不太在乎,出问题了就把流量切走。 + + + + + +  + +极客时间:你们当时尝试的结果怎么样? + +毕玄:从2016年到2018年,我们大概跑到了1万台机器,相当于在线有1万台可以给离线用,那一年离线少采购了5000多台机器,1台假设10万,所以一年省了5个亿。 + +关键是不光这一年,下一年我只要继续扩大在线规模,就能继续省钱,到后面每年省的钱会越来越多,因为技术已经是成熟可以被复用的了。方案上、技术层面上肯定不会有太大问题,剩下全是工程,工程是很缓慢的,你就算技术走通了,工程要完全落地也要个周期。 + +这可以用一个指标直接体现,公司所有服务器全天的平均利用率,像Google就只看这个指标。大部分公司应该都小于10%,阿里16年开始做的时候是8%,Google发表论文的时候利用率大概是50%,这意味着Google只用1/5的机器就可以做同样的业务,离它讲的核心竞争力非常接近。 + +极客时间:为什么业界、阿里和Google差距这么大? + +毕玄:中国公司更难是因为我们不是全球化的,大数据是很高,但晚上没有流量在线就是零,所以你平均一下就完蛋了,利用率就很低,但国外很多公司会好一点。 + +以前我们问Facebook这个问题,因为Facebook没有学Google走统一调度。我们问为什么?Facebook说因为我的在线业务全天流量都还挺高的,因为它是一个国际化的网站,全时区覆盖。那我们没有,中国公司这一点确实是个问题。 + +极客时间:所以从那之后,利用率就成团队重点关注的指标了? + +毕玄:阿里每年就在不断地推进利用率指标,我们甚至讲到连财务都理解了,财务挑战研发线的服务器成本,他不关注其他,只看利用率要拉上去。 + +以前,研发的服务器投入,财务线的人是没有方法挑战的。你说要采购100台,财务说太多了,你说业务有多少需求、量多大,所以需要采购,然后财务听完晕倒了,他没有办法反驳你,因为你是拿业务在说事。但后来我们的财务就说看看你们利用率才多少,为什么要采购?不给批。我们训练了他们怎么挑战研发,就很好,连我们CFO都懂。 + +极客时间:利用率,阿里现在做到多少了? + +毕玄:我是2019年初不带这个团队了,当时从原来的小于10%做到了20%左右,相当于翻倍了,就意味着成本有可能减半。 + +Google这两年据说已经推到了60%,我们以前认为50%是天花板,不可能再多。以前财务也问,你们说利用率可以低,但得告诉我多少是合理的。我们总不能说小于10%是合理的,这解释不过去,从技术上也得编个理由,但50%我们说是可以解读的。 + +一是因为这是全天的平均,如果说50%,意味着高峰肯定会比较高,平均一下已经很少了。第二是多数CPU的设计原理都是超线程,你看到两个核,物理上只有一个核,只是说软件层面具备跑出类似两个核的能力,但其实是不可能跑得出来的。所以我们说就打个折,当然这有点忽悠,但财务线非IT的人可以理解,觉得比较有道理(笑)。 + +极客时间:但是现在Google推到60%,这套解释又说不过去了(笑)。 + +毕玄:没想到Google竟然又突破了,做到60%左右还是很稳定,所以我们觉得它就是天花板了。 + +现在阿里这个团队还在做,去年做到了30%左右,天花板是60%,阿里规模这么大,我们假设50%是天花板,那30-50%也还有很大空间值得努力,ROI还是非常高的,所以这个团队一直评价都非常好。 + +  + + + + - +极客时间:调度系统Sigma,你们当时做的挺早的,看报道K8s生态起来了之后,你们就把Sigma的技术栈换到上K8s了? + +毕玄:那是后来,我们2016年做的时候,Docker最火,K8s还没起来。当时在调度上的竞争非常激烈,因为调度其实会把下面的容器屏蔽掉,Docker觉得如果自己不往上做调度很危险,就开始做Swarm,然后Mesos是另外一家,Google刚把K8s开源出来。 + +而且Google以前也不做开源,他的套路是发论文然后什么都不干,但他在大数据上的伤害比较大,之前他发了MapReduce那几篇三驾马车的论文,发现自己啥也没得到,然后什么Hadoop公司全起来了。 + +关键是后来他做云,云上放MapReduce,但所有开发者用的接口全部是Hadoop接口,导致Google不得不去兼容Hadoop接口,简直太搞笑了。他们内部觉得我MapReduce做的比Hadoop好太多了,我是个成熟的很牛的东西,你们竟然不用。 + +那之后Google才发现重要的不是发论文,是做开源抢占开发者,所以在K8s上,Google吸取了教训,开始对新套路有点概念了,我先发论文占领影响力,然后再发一个论文实现的开源产品。但Google不擅长做开源,就找了擅长开源圈子玩法的RedHat,联合起来做K8s,因为Borg跟内部很多系统搅在一起,没办法开源,只能重做。 + +极客时间:那16年你们刚做的时候,技术怎么选的型? + +毕玄:我们当时选择了Swarm做Sigma,但2018年的时候K8s就基本垄断了,我们很多的业务方用的是K8s的API,访问Sigma就不通,导致我们必须兼容K8s的API。 + +但在兼容上面,我们在很多开源路线上都犯过错误,一开始其实有一个开源的东西,但是我们先自研,然后等开源的那个拥有了最多的用户,我们就不断兼容,但是你兼容会越来越痛苦,因为开源一旦起来以后是一个很健康的社群,有非常多公司的合作,会越做越好,到了那个阶段你是抗衡不了社区的。 + +所以兼容这条路其实是走不下去的,我们就决定不做兼容,把Sigma扔掉,基于K8s把Sigma做的有些东西放到里面去,就是ASI。 + +极客时间:兼容的痛苦是指什么? + +毕玄:因为社区的关键问题是控制不住,我们当年经历过很多次这个过程。 + +比如说一开始对我来讲这个阶段非常重要的需求,对他来讲可能一点都不重要,那我们肯定觉得开源做的不好,需求又很急,我们就大量自研做了很多Patch的东西。但后面发现开源一旦起来节奏太快,它增加了很多东西,其实覆盖掉了以前我们做的很多改进,这个时候就很尴尬了,到底是升级成它?还是保留自己?我们后来觉得应该尽快升级成开源,因为会被它拉得越来越大。 + +极客时间:所以Sigma的事大家都能接受?当年大家也不太能预料到K8s会迅速起来,所以后来Sigma换到K8s上是比较必然的事? + +毕玄:大家能理解也能接受。确实肯定有些人会很不舍,毕竟做了几年,而且业务效果也在,最后不得不扔掉。 + +但这确实是我们当年判断的失误,如果更早选择K8s会更好。但16年的时候我们看Swarm和Mesos,加上K8s不成熟,不觉得开源有绝对优势,觉得自研有优势有机会。 + +所以我们后来反思,做技术选型的时候,如果开源界已经有一个很成功的东西,自己又没有什么很颠覆性的思想,还是拥抱开源比较好,没必要挑战。阿里在开源这条路上吃过很多亏,因为以前都自研,HSF和Dubbo也是典型。 + +极客时间:HSF和Dubbo是指什么,可以具体讲下吗? + +毕玄:HSF是我们自研的产品,Dubbo是开源的,在整个开发者群体里肯定有最多用户。但阿里收购完一家公司会告诉他,你把Dubbo换了,换成HSF。 + +很多公司觉得很尴尬,你们进来之后,业务啥也没干,先把技术换掉了,阿里以前经常这么做。我们后来说像这种,就不应该让别人换掉,应该把我们自己换掉,所以HSF后来新版本的目标就是换成以Dubbo为核心,支持内部HSF协议的解析,这样以后收购就非常简单。 + +极客时间:我们现在站在事后看,当时你们判断开源不成熟,选择自研Sigma,是不是因为大厂不可能说等两年,等开源成熟? + +毕玄:不可能等,而且大厂确实挑战也比较大,要解决的问题很多,所以如果开源如果不是很成熟,很难说我一开始就选择开源。 + +极客时间:但一个领域,开源如果已经成熟了,大厂才开始用,是不是说明你们没有更前瞻地看到这个领域的问题? + +毕玄:大厂很有可能比开源看到更快,所以确实就是你说的很尴尬,现在大厂的自研走上了一条很尴尬的路线。 + +开源反噬自研是之后业界的长期话题,以前很多公司都是自研,但现在开源已经被玩得太多了,什么都开源,那之前的自研到底怎么办。 + +反正我们的判断就是,如果开源的东西已经是主流了,比如说像Spring cloud,那没必要做一个新东西再去跟它竞争,因为我们也只能靠开源去争,但如果没有革命性进步,关键也竞争不过他,所以我们后来做了Spring Cloud Alibaba,就是觉得我竞争不过你,跟你一起玩好了。策略就是这样,总体还是拥抱开源,因为你要么就自己做个开源,要么就用开源做,就这两条路。 + + + +  + +水友讨论区 + +今天的对谈到这里就暂时结束了,重点聊毕玄当年做统一调度的经历。 + +虽然这些年毕玄换了不少领域,但从他对成本的关注这条线讲,之前做的所有事情又能大概串联起来,最早做的容器T4,到后来的异地多活,到今天的统一调度,后面他创业选择的方向也是做企业的云成本控制FinOps。这么看,成本可能是一个企业始终关注的话题。 + + +Google当年分析自己的优势也提到这一点,一是排序结果的准确度比多数公司好,二做同样的效果付出的成本是其他人的1/10。分析自己所在的团队/公司,你觉得优势是什么呢? +关于统一调度的技术选型,我们也聊到了开源和自研的问题,毕玄的反思是“如果开源界已经有一个很成功的东西,自己又没有什么很颠覆性的思想,还是拥抱开源比较好。”你是怎么看的呢? + + +如果你有更感兴趣的话题,欢迎在评论区留言,如果觉得有启发也欢迎分享给身边的朋友,一起讨论。 + +读到这一讲,时间线已经走到了2018年,毕玄马上就要从阿里离开了,下一讲我们聊聊这个话题。下一讲见。 + +拓展阅读 + +如果你对统一调度的具体技术细节感兴趣,可以看这几篇:- +阿里云云原生写的揭开阿里巴巴复杂任务资源混合调度技术面纱- +阿里巴巴中间件写的:给 K8s 装上大数据调度引擎:伏羲架构升级 K8s 统一调度- +云技术的新变革:阿里云13年后重构全部核心调度系统 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/10出走大厂:离职?还是不离职?这是一个问题.md b/专栏/超级访谈:对话毕玄/10出走大厂:离职?还是不离职?这是一个问题.md new file mode 100644 index 0000000..83967c2 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/10出走大厂:离职?还是不离职?这是一个问题.md @@ -0,0 +1,257 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 出走大厂:离职?还是不离职?这是一个问题 + +你好,我是叶芊。- + - +统一调度做完,时间来到了2018年。当时有这样一份报道:“2018 年的 12 月,CTO 行癫调任阿里云总裁,宣布阿里全面上云,组建了以毕玄为上云总架构师的架构组,确定了上云的方案和步骤。”- + - +从外部视角看,我们很容易推测好像毕玄又被委以重任了,但在当时他却有了离职的想法,那之后一年多就走了,当时到底发生了什么?- + - +我们继续对谈,聊一聊他离职的故事,以及对自己阿里14年的总结。 + + + - +极客时间:看你的经历,统一调度做完,你就去做集团上云的项目了? + +毕玄:当时18年年底有个组织调整,行癫宣布上云,让我兼集团上云的架构师,但我也没有做太多,集团上云说实话早就奠定了基础,我只是定调的,就什么叫集团上云。 + +腾讯那篇文章很像我们当时上云,把里面的公司名改一下就是阿里,一模一样,说明两家公司对上云这个事情,内部争论都是存在的。因为什么是上云?行癫是不会说的这么清楚,行癫只会说你们2年搞定,我对外说完了,剩下你们做不做得到,那就是你们的事了。 + +但没有人知道做到什么程度叫做上云,有100种方式解读,可以解读得很简单,也可以解读得非常难,我当时还是定了一个比较有挑战的目标。 + +极客时间:上云会分业务吗? + +毕玄:不分业务,肯定是先上核心。阿里后来做所有重大技术变革都是先做核心业务,然后再做边缘。 + +极客时间:但一般不是会用边缘业务先试吗? + +毕玄:我知道,但你先做边缘并不代表任何东西,因为核心不会觉得你上了边缘就解决了它的问题,它还是没有信心,但是如果你先上了核心业务,边缘会认为我也没问题,后面工程收尾就变得非常简单。这是阿里后来的调整,最早我们也是先做边缘,但发现那样工程周期更长,而且很难,但一上来就搞核心,后面其实很简单。 + + + +极客时间:后来你有段时间去带视频云团队了,那是怎么回事?你之前好像完全没有视频方向的经历。 + +毕玄:上云做完19年的时候我准备离开,其实18年我就想走了,因为一些汇报线上的问题,但公司各种谈,反正折腾了很久,19年年底刚好疫情,过年的时候行癫说要么你去带视频,他还是比较懂我的,知道我想做的是偏基础技术的东西,加上离职也离得不是很顺利,我就去了,一直带到21年8月离职。 + +我是一方面希望在技术上拓展一点,另外视频是阿里云一条不算小的产品线,在商业上更有要求,所以我觉得也挺好,可以去看看偏商业的业务是什么情况。可能我运气也相对好一点,2021年刚好是音视频在云赛道最火的一年,声网、Zoom都到天上了,声网市值100多亿美金,Zoom 1000多亿美金。 + +极客时间:那在视频云团队,你负责的什么? + +毕玄:当时技术我几乎是不管的,我带产品、销售和运营,全是业务团队,拜访了很多外面的各种客户。现在想想也不是坏事,算是一个积累,我对商业有了更多感受。 + +我发现商业是完全不一样的。商业不是说你做得多好就一定会成功,可能要看很多业务策略的东西,但业务策略就跟技术完全不一样,更模糊。技术,你觉得这个方向可以,解决技术问题就好,但业务,你觉得这个方向可以,有很多内外部因素会影响,很多问题也不一定能解决。 + +  + + + + - +极客时间:如果总结一下自己在阿里的14年,你觉得最骄傲的事是什么? + +毕玄:还是那三个,从来没有变化,HSF、异地多活和阿里的统一资源调度。 + +极客时间:最遗憾的事呢? + +毕玄:没啥遗憾,挺好的,阿里这一段我是真正在搞技术,专业性、深度、广度都得到了很好的提升,而且我们这拨人也跟着得到了很好的回报,这也很重要。 + +但说实话这就是幸运,我们这波做基础技术的人,从2007年到2021年,刚好是阿里最高速发展的一段时间,业务高速发展就带来了很多技术上的问题,总要有人去解,所以阿里给了很多人机会。 + +因为换一波人其实也一样能做好,工程师最擅长解决问题,在阿里,问题已经展现出来了,你只要去解决就可以,这对大多数工程师来讲都不是难题。如果你不在或者在另一类的公司就不可能,不是说你有没有能力的问题,没有平台,你有能力也没用。当然现在有点变化,因为中国技术创业的环境好了很多,以前是一点机会都没有,现在应该有那么一点。 + +极客时间:像之前讲的淘宝消防队,在外面很难知道问题是什么,但是你在平台里,问题就在那等你解决? + +毕玄:技术人就是这样,你平台不够大,碰到的问题就不是世界级,可能别人早就碰到并且已经解决掉了,那你就不可能做很创新性的,最多结合公司的情况,做一个很好的工程落地,但你不可能引领。 + +但大平台就不一样,你更有机会做出一个世界级的引领性解决方案,这对程序员,尤其做基础技术的人很重要,如果你想很好地发展,是很难离开大平台的。业务技术可能不大一样。 + +比如说阿里,我很多好朋友更早就走了,他们当时都问我为什么还不走?我就说,在阿里这个平台上能看到的问题,其它公司都很难看到,关键是在阿里,我又有机会去做,等我做完了,如果我看不到还有什么问题觉得很难了,那再说。 + +极客时间:看你当年的微博说除非去Google,不然你是不会跳槽的? + +毕玄:因为Google确实很让人羡慕,它看到的一定比我们更远,到现在我都很相信。 + +极客时间:有人说你是“技术大牛”“毕大师”,这样的标签你怎么看?像“毕玄”这个花名本身也是一个标签,还是说你其实不在意这些? + +毕玄:所以我没换花名。 + +极客时间:你对这些标签的感觉会有变化吗? + +毕玄:会有,我很多年前比较在乎。 + +极客时间:比较在乎,是什么时候? + +毕玄:05、06年吧,进阿里之前。当时去参加小规模的技术讨论会,以前BEA经常搞User Group那种,我们这些小技术同学就去参加,觉得哇上面的人简直太偶像了,那个时候我们的梦想就是能不能认识这些人一起聊个天,以后能不能自己去讲。 + +那个时候是很在乎的,最重要的就是希望能进入那个圈子。但我可能后来写了OSGi那篇文章,在那个圈子里,就觉得没什么了。 + +再后来,我觉得技术最重要的是带来了什么。很多人会说自己技术好,那你到底用什么来证明呢?我觉得最好的证明肯定是,你做的东西对这家公司的整个业务发展有所作用。当然技术人可能还会追求别的,比如方案具备引领性,推动了这个技术领域的发展,那就更好了。 + +像阿里,尽管我们是09年做的分布式改造,但即使到今天,做的思路至少影响了中国大部分互联网公司,后面做的异地多活、统一调度也在逐渐影响。 + +极客时间:你们当年有觉得自己做的东西影响很大吗? + +毕玄:我们觉得这些外面都用不到。就像分布式,我们觉得大部分公司不需要分布式,但现在我们认可AWS讲的,现在的软件跟当年的差别很大,所以分布式成为很多公司的选择。 + +做异地多活,我们更认为这玩意儿除了阿里、腾讯这种超级大的公司有诉求,中国其它公司应该都不算强烈,但这两年越来越多的公司开始去做这个方向,甚至国家比较重要业务的企事业单位都开始做。调度也一样,就很奇怪,Google其实做了很多年,中国也没有几家去跟进,但阿里做完,明显有好几家头部公司都在做了。 + +对技术人员来讲,这种就很有成就感,因为别人讲到的时候都会提及,你看阿里是怎么做的,这就像我们说Google是怎么做的,就是信心,这很重要。 + +极客时间:他们就不用再证明。 + +毕玄:对,而且周期肯定会缩短,人才池也会扩大。当年我们做分布式碰到很多问题,但现在中国能做分布式架构的人简直太多,之前能做异地多活的人也少,但慢慢也能看到各家做过的人变多,调度以后也会是这样。 + + + + + +  + +极客时间:当时你几次提离职,这么坚定,是想好自己之后要干什么了吗? + +毕玄:没有,但出来创业是确定的,创业干什么一点都不确定。所以我们现在也建议还是先想好你们到底要干什么,再出来,别冲动。因为创业跟在大公司差别非常大,我离职的时候跟很多人聊,总结出来就三个选择。 + +第一,继续去一家大公司。找一家公司,任一个更高级别的位置,理论上也能做更大的事情,但后来我想了想觉得没有太大意思。现在没有多少平台让我觉得面临的问题多不一样,可能更多就是把我以前做过的去那边重新落地。 + +而且所有大公司我相信不管是多高级别,你做事都会面临很多组织协作层面的问题,很多约束没法避免,因为大公司分工特别细,你要面对的横向部门会非常多,即使CEO也受很多限制,我想不出来什么职位不是,这很正常。所以我觉得大公司还是不考虑了。 + +第二个去一家中等的在上升期的公司,可以去任职CTO跟随上市。很多人会选这个,我以前有个朋友梦想就是离开阿里以后去美国敲钟,所以他就选了一家即将上市的公司去任职高管。不管怎么样,梦想肯定实现了,那也可以的,我觉得挺好。 + +但这个对我来讲,也没有太大意思,敲完钟然后呢?我就该走了吗?就没有太大的吸引力。所以只剩一个选择了——创业。还有退休,我觉得现在退休有点早,还是得再干几年。 + +极客时间:还是想做点什么。 + +毕玄:我们一帮技术人员,很多出来就一个原因,想找个机会做能再吹一把的事情,要的就是成就感。那成就感怎么来?就是前面讲的第一在公司有贡献,第二做的东西在业界有影响力,最好它还能被很多人用。 + +我以前跟有些做管理的说,对成就感、对一个人是不是成功的判断,技术人员跟很多非技术的人,尤其高层的管理者,是不一样的,说得更极致一点,世界观是不一样的。 + +对我们技术人来讲,只在乎你过往做了什么,你做了一个东西还能被很多人用,就是最牛的。像Trustin Lee做了Netty,更不用说Jeff Dean这种写了很多东西,还有写各种语言、框架的,我们不会在乎他在哪家公司任职,什么级别,带多少人,根本不Care好吗。 + +极客时间:之前不是有个段子Python之父简历上只有一行字,I wrote Python。 + +毕玄:对,最看重的是作品,但我们后来也发现,不仅技术是这样,事实上所有的都这样(笑)。 + +你说即使是娱乐圈,大家也看你拍了什么电视电影,唱了什么歌,越多的肯定就越大牛,像周杰伦这种一长串的就是巨牛。其实都一样,等你老了,回顾人生的时候,没有人会记得你在哪家公司任职了高管,一点都不重要。淘宝总裁有多少任,没有人记得,你说你做过淘宝总裁,但大家都忘了你做过,这才是最悲剧的。 + +好几个阿里出来的都是这个梦想,只是觉得在阿里确实有点难。想想自己大概还能工作多少年,阿里前面的事好像还挺值得吹下牛的,但我们发现如果再这样下去,就没什么可以吹的了,这就不大对劲了。 + +极客时间:不太对劲?是指一直当管理的状态吗? + +毕玄:我们不希望到老了回顾人生的时候,发现全是更早的,中间有10年没有任何东西,那大家肯定会说你那10年都在混。因为我们这种人退休了八成都会做点咨询,做咨询肯定不希望自己谈的都是十几年前的案例,至少能稍微近一点,否则有点难。 + +  + + + +  + +极客时间:离职后,创业是确定的,那当时你有想过内部创业吗? + +毕玄:如果你有机会在阿里做,确实没必要离开,何必呢?像玉伯做语雀挺好,你既然还在做一件足以吹一辈子的事情,那就继续干着,有人给钱,什么都不用你管,多好。因为想做一件大事,无非是找人、拿资源,在内部你去找上级、总裁要足够的支持,这就是一种资源。 + +但是,我要做的事情不光要资源,还要横向部门的各种支持,比如要销售线、市场,这就很难,总裁也很难,因为他很难偏向你。 + +以前我们想过,出来以后最大的损失是品牌,我们将失去阿里这个巨大的招牌,这是最值钱的,这确实是个非常大的伤害,但要资源也变得简单了,因为我只是找投资机构要钱,剩下所有都是自己决定。 + +极客时间:这个硬币两面你们是可以接受的。 + +毕玄:我出来创业前,很多阿里高管跟我说,创业也没你想得那么自由,最后你还会受限于股东、董事会等等。但必须说至少在前几年,你并不会受制于他们,自己有绝对话语权。 + +极客时间:一个创业公司,前几年的规模,可能也没有到别人想要插手。 + +毕玄:对,人家根本就不会插手,就你这,他还怕插手一下万一挂了。所以你考虑好两个问题就行。 + +一你想做的事,到底是在阿里做成的概率更大?还是你出来做成的概率更大?如果出来的概率更大,那就别纠结了,你要想好你会失去什么,阿里的品牌,对很多人来讲还有阿里的待遇。 + +二你能不能在这件事情上坚定地干个比如5-10年,在阿里说实话你要做一件事情5-10年很难,你可能自己想,但有一天你就被组织调了。 + +极客时间:所以当时想清楚之后,你还是决定出来为自己的技术梦想奋斗一下。 + +毕玄:但说实话我出来创业以后才发现,这个命题成不成立还要再想一下(笑)。 + +因为创业还是一个非常商业的事情。技术梦想当然可以有,没有问题,我也很羡慕用技术梦想创业的人,我也想过,能不能做个东西让很多开发者用,如果能做出来就很成功了,至于这玩意儿到底能赚多少钱都不重要。 + +但后来我觉得这有点不靠谱,因为你创办了一家公司,任何公司都是商业公司,想发展好,基础是这家公司能赚钱,这个当然很现实,技术的人都不太喜欢听。 + +极客时间:成本、收益? + +毕玄:技术人不愿意听这个,很多阿里出来创业的就非常技术梦想型,我想做这个事情,对世界产生一些影响,至于赚多少钱我都不Care。 + +但这种准确来讲,你不算创业,你只是换了个地方干活,然后你还需要上面有个人给你创造出一个空间来,这跟你在大厂的区别不是很大,可能还更难一点,因为你得在外面找个人盖住你,这个难度更大。 + +创业最大问题就是这个,你必须考虑能不能创办一家未来具备持续盈利能力的公司,如果不具备,梦想就不可能实现,因为你做到一定阶段,公司倒闭了,那也没了。所以我们就一直在想到底能干什么。 + + + + + +  + +极客时间:那想创业方向这事,你们想了多久? + +毕玄:很快的,我们出来以后,一两个月就基本确定要做什么了,无非就是几个方向想可能性。 + +极客时间:你的思路是什么样的,可以具体讲讲吗? + +毕玄:像我们这样第一次创业的人,八成只能做自己擅长的,其他的概率非常低。 + +比如我们看小鹏、雷军,你说后来雷军做手机,他难道懂手机吗?他肯定不懂,但是因为前面他成功经营了金山,小鹏也是,做汽车之前创办了UC。 + +极客时间:第二次创业的时候有之前的创业经历做担保? + +毕玄:因为已经有足够的人脉圈和钱了,最重要的是钱,持续找钱的能力非常强,投资人就根本不在乎现在做的事他擅不擅长,你很靠谱,给你砸再多钱都行,赛道最好越大越好。 + +现在我们说好后悔,如果你真的想做一家对世界影响非常大的公司,说实话,最好不要在大厂待太久,因为连续创业才有成功的可能。我不太相信一个人创办第一家公司就超级大,王兴之前折腾了好多家,黄峥也一样折腾了好多家,张一鸣也是,对不对?马云之前也创办了好多次。马化腾比较特殊,就创办了一家,运气比较好。 + +极客时间:你说的,想创业大厂不能呆太久,是为什么? + +毕玄:在大公司,你最大的优势是带领过大团队,见过大平台大世面,所以你的公司如果未来能成长成一家很大的公司,大家相信你能带领好。但关键是你现在创业,能不能走到那步其实是更大的挑战。 + +极客时间:你如果重来一遍的话,觉得自己什么时候出来更好? + +毕玄:也许上市后就可以走了(笑)。因为创业最好不要有太大经济压力,不然很容易变形,把公司做成目标就只有赚钱,这样会容易失去选择权。创办公司当然要赚钱,但选择还是需要的,比如说短期有些钱,如果对公司业务发展没什么意义,就不要赚好了,没有什么,亏损就亏损,你是能接受的。 + +但你想,如果创业者压力那么大,公司很容易出问题,他如果真的很缺钱,比如连生活都有问题,我觉得会不择一切手段。所以我也很佩服以前创业的一帮人,压上所有身家,哇简直太牛了。 + +极客时间:现在大家已经习惯了创业要找融资。 + +毕玄:都有退路,以前都是All in。 + +极客时间:中国的投资环境也在成熟,但刚刚讲到第一次创业,国外成功的好像多一点。 + +毕玄:国外也比较特殊,很神奇,你看微软,盖茨之前没干过啥,Facebook,马克也没干过啥,上来一把成功,Google也是。 + +我觉得是因为国外卷得没有中国厉害,他们如果面对一些成熟的创业者,我觉得也干不过。但中国,你如果真的想做对社会影响非常大的赛道,除非以前就是这个赛道的人,那很幸运,就像很多做芯片,做机器人的,他以前背景就是这个,像我们这种就没办法。 + +To be continued…… + +  + +水友讨论区 + +对谈到这里就暂时结束了。今天的重点话题是毕玄对自己在阿里14年工作生涯的反思。 + +对于工作中的成就,他说“说实话这就是幸运,因为换一波人也一样能做好。”聊到工作中的成就感来源,他说技术人的成就感很简单,第一在公司有贡献,第二做的东西在业界有影响力,最好第三还能被很多人用。 + +至于为什么会离职?他的回答背后折射出来的是对个人成长的高度关注,理由也很简单,因为这个平台不太能让他再做值得吹一把牛的事了。聊到这里,突然想起李诞写的脱口秀工作手册:“工作的本质是交易,我们在用自己的时间和才能,通过一家公司,与市场交换金钱。要意识到你的全部人生都理应要为你的创作提供养分,为它服务。好的工作节奏,就包含了学习,包含了养分。” + +如果让你对自己的学习/工作生涯做一次总结,你会说什么呢?你觉得自己的高光时刻有哪些?光环是平台带来的,还是个人能力带来的呢?你的成就感来自哪里?你现在正做的东西有给你带来成就感吗? + +期待在评论区见到你的身影,如果对今天对谈的其他内容有感想,也欢迎你发言讨论。 + +下一讲我们会接着毕玄思考自己创业方向的思路聊,下一讲见。 + +拓展阅读 + +1. 浅黑科技就腾讯上云的过程写过一篇文,毕玄说和阿里当年经历非常类似:腾讯在命运的棋盘上砸下一颗钉子 + +2. ArchSummit 全球架构师峰会 2019 北京站有一次演讲:把阿里巴巴的核心系统搬到云上,架构上的挑战与演进是什么? + +3. 毕玄离职的时候写了一篇对过去的总结和未来展望:再见,阿里毕玄 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/11CEO心得:大厂出来创业,最大问题是对钱没概念.md b/专栏/超级访谈:对话毕玄/11CEO心得:大厂出来创业,最大问题是对钱没概念.md new file mode 100644 index 0000000..a2d6877 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/11CEO心得:大厂出来创业,最大问题是对钱没概念.md @@ -0,0 +1,272 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 CEO心得:大厂出来创业,最大问题是对钱没概念 + +你好,我是叶芊。- + - +在一段波折的离职过程后,毕玄从阿里出来了,决定创业,但他却完全没想好自己要做什么。- + - +回顾他的职业生涯,异地多活、代码工具、统一调度都挺有市场的,他也提到自己差点就去做代码了,投资都快谈完了,最后想了想还是悬崖勒马改做现在的调度。今天我们就接着创业继续聊,看看他是怎么选择创业方向的。- + - +你可能暂时对创业不太感兴趣,没关系,听一听这位技术大牛对市场以及个人优劣势的分析,相信对你之后的职业发展也会有帮助。 + + + - +极客时间:之前聊到创业方向的选择,第一点是选一个自己相对有优势的领域,然后呢? + +毕玄:第二点我们特别关注的,从商业模式上来讲具不具备盈利的可能性。盈利,最重要的是你对客户的价值是什么?如果有价值,客户是肯定愿意付费的,所以关键是价值。 + +我们第一想法是代码相关,这对开发者影响是最大的,所有的人都会用。所以代码智能化其实是我们考虑的第一方向,几乎都谈完了。但最后我觉得,最大的问题是我们团队没有优势,之前有希望做成是因为阿里的环境,但离开了,就要跟GitHub对着做,但是跟他们比,我们一点优势都没有,这玩意儿咋成功? + +另外商业周期非常长,GitHub已经算很快了,但它做了两三年,现在也只是推进到了一定阶段。但在中国,你从开始到商业化可能要5年以上,那就要考虑一下5年你的公司持续没有收入,生存就得靠不断地融资,这难度太大了。 + +极客时间:而且就像你说的,技术的人,持续找钱的能力相对没那么强? + +毕玄:对。而且大公司的人最大的问题是做所有事情没有成本概念,这才是悲剧。 + +比如说我,以前就算财务给我一个数,一年亏了多少亿,我也没有任何感觉,反正不是我出钱,有人出,而且还会觉得亏了这么多亿又怎么样?我还得加人。你就会把这个故事讲下去,一点都不在乎,因为你亏损了不用自己去融。 + +大家对钱没有概念,很多大厂出来创业的人,钱都不知道花哪去了,稀里哗啦就没了,心里觉得相比以前,现在才这点钱,随便花,然后就没有钱了。 + +在阿里,你说服了上面给你投资源做一件事情,失败了最多也就是年底绩效差一点,奖金少一点,没有别的了,不会失业也不会倒闭。但出来了这都是事实,你没有钱,公司就倒闭了,员工是很难跟你讲梦想的,先必须有合理的报酬,梦想只是个加分项而已。 + +极客时间:所以代码方向还是大厂有优势,你当时还想过什么方向吗? + +毕玄:另一个就是中国现在特别火的开源,人简直太多了,做大数据的、做数据库的,不知道有多少家,一面墙都不一定能写完(笑),已经卷成比花卷还卷了。 + +对技术创业来讲,选这条路,你前几年还是挺爽的,因为你要考虑的是用户不是客户,用户都是免费的,免费好谈,你可以刷脸,别人用着合适还可以帮你推广站台,都没问题,如果前几年你还能融到资,生活简直不要太爽,就乌托邦。 + +但是我觉得,到了某一年,你需要尝试商业化的时候可能会面临非常巨大的挑战,尤其在中国。你看,有哪家公司通过开源积累了大量开发者,最后通过漏斗效应商业化成功的?就现在看,我觉得八成是不大可能。 + +极客时间:为什么国内商业化成功不大可能?大家对付费的观点不同? + +毕玄:你想中国市场购买技术软件的是什么行业?多数是金融,但金融又不会单为一个技术软件付钱,它是大项目招标,里面包含技术软件。除非你有能力直接去投标,不然就得在上面做一些应用软件。这种阿里云可以,因为阿里云有品牌,他其实也不是自己干,他也只卖下面基础设施,上面可以找合作伙伴,但你很难,你一家创业公司凭什么? + +而且大家去见客户,免费的情况下也是什么都好谈,随便谈,但只要说到要收一块钱就是另外一回事,这就变成了公司行为,不是个人的,公司对公司是一个非常正规的行为,因为决策不一定是他能做的,而且金额越大,决策链条一定会越长,不管什么公司。 + +极客时间:但是国外有很多开源到商业化的成功案例,如果参考国外经验呢,中国公司从开源到商业化的路? + +毕玄:海外没有问题,像Confluent、Elastic、Mongo等等,都成功走完从开源到商业化的过程,每个季度的营收都在几个亿美金,增速非常快,虽然还是亏损的,但你至少能看到希望。 + +但现在缺的是人才,到底去哪找一些人,把开源产品在欧美完成商业化。如果有谁能走通这条路,对中国的软件产业会是巨大贡献,只要有一家走通,关键人才就有了,以中国的复制能力,一家公司有了,中国就全有了,这不是问题,现在主要是没有人。 + +极客时间:对做开源的公司来说,以前大家会讲亚马逊的故事,人家强势,我就是不盈利。不能这样吗? + +毕玄:但你看他最终还是盈利了,而且一把就赚回来了。大家愿意投你钱,是因为他相信你一把可以赚回我以前投给你的所有的钱。 + +而且ToC是有可能一把赚回的,但ToB不可能,这个逻辑根本就不成立。所以这些开源商业化的公司还真是要看一下,因为美国这几家已经很成功了,一年营收接近10亿美金,增速70-80%,但仍然亏损,这太恐怖了,这样下去你到底能不能盈利?如果不能盈利,这其实是个泡沫,对商业公司来讲什么叫泡沫?就是这个,能盈利你就不是泡沫,不能光讲梦想。 + +如果可能性是存在的就可以了,就有信心,对一家公司来讲说实话这就是最重要的,尤其对创始人团队,这伙人是很苦逼的,因为就是熬,你得熬很多年,不到盈亏平衡的那一天,其实都不算熬出头。但到那一天情况会好非常多,很多都变了。 + +极客时间:所以现状是没一个跑通,又不能靠画饼,那中国的企业从开源到商业化,你觉得有希望吗? + +毕玄:我觉得肯定会有,无非是几年的问题,以及是谁的问题。这条路也确实是技术人员创业最好的选择,阿里出来了这么多人创业,多数选择的也都是这条路。 + +因为中国已经进步了一点。以前开源,很少有中国人做的东西是主导,或者中国人做出来影响了全世界,但现在有了。现在除了最底层的核心技术,中国在应用层的技术能力是可以跟国外抗衡的,没有太大差距,中国的场景又更丰富,能诞生出很好的开源产品,然后影响全世界。这个套路已经成熟了。 + +有些投资人赌也是对的,他们为什么很愿意投这些。我之前很震惊的,因为商业上很难讲通,但他们说其实很简单,他们相信中国一定会有一家走通的,是谁倒不重要,他只要押大赛道就可以了,只要有一家成功就没有问题,但小赛道还是不要押,成功了回报也很低。 + +极客时间:看我们讲到的几个领域,代码智能化、开源数据库等,这些方向都挺好,但是你认真想过还是决定算了。 + +毕玄:都挺好,我也挺想做,但以我们创始团队的背景,如果创办一家公司我觉得最好不要做这种。 + +创办一家公司,一做自己相对有优势的,二做对客户来讲是可以直接讲清楚价值的,因为这样商业模式和收费才能闭环,如果能完成这两步,我觉得未来这家公司是有可能走向盈利的。 + +如果你想做对社会有更大影响的大赛道,或者就纯粹想Buying一个梦想,最好你先创办一家成功的公司,做成具备盈利能力并且能持续盈利的,那个时候再投一点钱去梦想,或者再做对社会影响力大的陌生领域就可以了。有了很好很健康的现金流,你随意好了,想做什么领域都可以,像阿里,阿里云能做为什么?就是有淘宝,能持续产生健康的现金流。 + + + + + +  + +极客时间:你最后选择了做调度,你怎么看这个方向的,可以多讲下吗? + +毕玄:看我们在阿里的经历,有什么事情是相对外面有优势的,也要相对可能被产品化和规模化的,另外对客户有价值的,我们觉得只有调度有可能,其它都很难。 + +异地多活具备领先性,但它不具备通用性,所以如果做这个方向,会做成一家咨询公司,但咨询是我们退休的目标,不是现在,而且咨询不可能做大,虽然比较赚钱。因为我们现在发现,总体顺势而为还是很重要的。 + +像国外,FinOps(云成本优化)要开始起来了,算是个新词,一个方向火没火最容易判断的就是,看有没有越来越多的创业公司,并且拿到了不错的融资,过去一年,国外做FinOps拿几千万美金融资的公司已经出现了。 + +但中国FinOps还是一个概念,还没有能说出一家公司确实做出名堂的,当然这对我们来讲就是一个非常好的现象。 + +极客时间:如果FinOps还在概念阶段,那客户容易理解你的产品价值吗? + +毕玄:FinOps对客户的价值现在是被认可的。因为很多公司上云之后,发现成本完全不受控,原因是云太好用了。 + +以前不在云上的时候,机器的采购内部要过很多流程,还要真实地去买,物流都需要时间,但在云上可不是这样,你点一下就买来一台,而且钱还不是现在付,是一个月后付,所以就失控了。连我们这样的创业公司都出现了,到了月底,一看帐单,怎么花了这么多钱?所以我们相信这个问题确实存在。 + +另外是服务器成本控制,我们觉得阿里应该是做得非常好的一家,方法是有效果的,也是可以通用的,这对客户来讲是有价值的,尤其大环境都在喊降本增效。 + +为什么FinOps刚好在现在这个时间点火了?一是云的便利性带来了成本失控,二是以前大家都不Care成本而已,现在这个方法可能会被大家认可,所以我们觉得这个帽子对大家来讲不是那么虚幻的话题。 + +极客时间:有一点好奇,在服务器成本控制这个方面,除了你们在做的这种方案,有其他的方法吗? + +毕玄:我们觉得没有。 + +全球很多公司探索这个方向,但目前来看只有Google、阿里和百度在非常成功地往前推进,而且结果也证明是有效果的,其他公司都很难,所以现在中国头部公司做的方案都很类似。 + +而且做这件事涉及的专业领域很多,像我们为什么要这么多人?因为需要大数据团队、调度团队、操作系统内核团队,我们总共有5个合伙人,全是技术出身,覆盖了几乎各个技术领域。但对中型很多公司来讲,要组建三个这样的专业技术团队其实很难。 + +这意味着可能要投入比如四五十个人纯做技术,但一家中型公司的研发人员就那么多,很难投到这个数量,就算能投,但中国做过这件事情的人特别少,反正就这几家,就这伙人。如果他们不出来,其实基本也都不出来,你没有经验自己做,阿里都花了三年,我们觉得你不会比阿里快。 + +所以我们给公司的定位是,不是说你不能做,是我们可以加速你的整个进程,先让你快速拿到一定成果,对中型公司来讲这很重要,他们要的就是尽快拿结果,而不是在这里浪费太多时间。等他拿到结果,你要继续往下发展的,是没有问题。 + + + + + +  + +极客时间:你现在做的调度产品是什么样的模式? + +毕玄:它比较像PaaS,至少目前阶段的产品形态,你可以认为就是一个PaaS软件,可以部署在各种环境下,私有云、公共云等等,部署完客户做对接,基本就可以了。 + + +贝联珠贯,打造全球顶尖的资源调度产品,将全球企业的资源利用率提升到20%以上,从而显著降低各企业每年投入的机器总预算,节能减排促进碳中和。技术栈基于K8S/Yarn构建,对K8S/Yarn的关键部分做深度开发和定制,提升其规模能力、稳定性,以及多云/混合云管理,包括离在线等不同计算形态的混合部署。 + + +极客时间:这和你之前在阿里做的统一调度? + +毕玄:区别就是更加产品化,就这一点。 + +阿里或者说任何公司,内部做的东西都只是贴合那一家公司做的一个特殊的解决方案,阿里以前都叫一个能力。但出来你要把这个能力变成一个产品,能面对各种各样的场景,这是很不一样的。 + +因为能力,不需要考虑很多运行环境,环境就是设定好的,我只是解决这个环境下的问题,但现在你不知道客户是什么情况,能不能有个产品通用,说白了,比如A客户是这样的,我投了10个人做,在做B客户的时候不用投10个人,那就是产品。 + +所以你想,一家公司最后能盈利是因为产品的边际效应产生了,比如我总共50个人研发,第一个客户10个人做,第二个6个人,第三个可能只投了2个人,这就有了。如果不能就完蛋了,做一个10人,二个10,第三个客户还是10,这样研发是做不下去的,没有边际效应。SaaS为什么大家都觉得很好?是因为边际效应太强了,你投了一把,后面就躺着数钱。 + +极客时间:那对调度来说,你觉得有办法实现边际效应吗?因为每个公司的技术基础肯定不一样。 + +毕玄:对,会不一样,但我们希望能产生边际效应。交付成本肯定都是需要的,无非是成本我怎么收敛。 + +第一个项目一定投入非常大,搞不好是整个公司,但第二个客户我必须把这个成本压下去,压到一个数的时候最后我算嘛,总体投入是多少,我从每个客户身上能获得多少利润,然后算边际效应是多少,这里我就有一个很好的定价了,但前面的定价肯定是不那么合理的。 + +所以定价这个事,在大公司是不可能感受出来的,以这个产品的定价,要做到多少客户才盈利,很多人搞不好算一下发现自己永远都不可能,那就成做慈善了。当然大公司有很多补充,我这里可能是亏的,但带来了其它的,所以总体是盈利的。 + +但你小的创业公司就一个东西,所以别想太多,如果不赚钱,你就是不赚钱,而且会永远不赚钱,关键是你不赚钱公司迟早会倒闭,因为我不相信有人融资能力是无限的,公司一直亏损,还一直有人给钱,哇塞这也是见鬼了。 + +极客时间:现在像定价、财务这块,自己是在补课吗? + +毕玄:那必须补,还有行政,经营公司的各种乱七八糟的事情。 + +CEO不就是这样,很多技术人出来创业肯定想做很多技术什么的,别扯了,你出来以后就会发现,CEO就是一个打杂的,公司什么事没人干就是CEO干,如果有人干,你最好就不要干了,你就别管了,那个人如果你很信任的话就是这样。 + +以前在大公司你完全不用经历,都有人在背后帮你全搞定,但你出来以后不可能,除非你有这样的合伙人,但多数技术又没有,所以你全部得自己学。像财务,你得始终知道自己哪天会倒闭,哪天手上的钱就会没有了,这太重要了。 + +我们现在非常羡慕出来创业后能打平甚至盈利的,这才是公司。 + +极客时间:为什么?如果打平了,就验证了公司的业务方向是可行的? + +毕玄:打平了节奏就都在你手上(笑),而且你的心态会很安稳,否则你永远都活在会不会哪天就没有钱的噩梦里,想下个月怎么发工资,这个时候不管你有什么梦想,其实都没有意义,有啥意义?你首先得有钱。 + +我们一帮出来创业的CEO们,现在聊天已经没有人聊这些很虚幻的梦想了,都只谈大家手上的钱还够不够,够多久。 + +极客时间:不谈梦想,大家现实地互相勉励。比较理想的情况要屯几个月的钱? + +毕玄:现在的融资环境,至少要一年半以上,我们都是按照18个月准备的,就是未来18个月里没有一分钱的收入,这家公司也能活下去,如果不能的话,风险会比较大。 + +  + + + +  + +极客时间:你现在做的调度领域,有没有期待什么时候自己可以打平? + +毕玄:那早了去了。ToB太难了,一开始赚不了钱,后面你也很难讲能赚回来,千万别讲ToC的故事,我前面几年亏了多少钱,后面会一年把钱全部赚回来。 + +极客时间:你觉得ToB想打平要积累多久?或者说公司一般发展的节奏是什么样的? + +毕玄:至少3年以上。你看ToB做一个新东西,第一年说实话就是种子客户培养,如果种子客户做得很好,到第二年,你基于种子客户的赛道可以做规模化复制,就是复制多少个的问题,第三年看你能不能扩到一个更大的领域,不限赛道通用的,或者如果能在全球化上有所展现,这样的公司未来就很有可能发展得非常好。这是最理想的节奏。 + +极客时间:第三年你提到“全球化”,这个之前还挺少提的。 + +毕玄:全球化是个很有意思的技术新命题,因为在中国,技术人以前没有见过全球化,有什么新问题很难说,但我觉得我们这种人是做不了的。 + +极客时间:创始人的文化背景不一样? + +毕玄:差太远了,中国做全球化成功的几个业务,创始人基本都在海外生活了很多年,非常知道海外跟中国的异同是什么。 + +你看马云那一代创始人,跟移动互联网这一代差别很大,你可以认为他们确实偏草根,其实也没有那么草根,只是看起来偏草根,但到了移动互联网这一代全是精英,基本全是名校的,也都有海外背景,像张一鸣、王兴这些人天然对多元化文化有更深的理解,注定他们这一代有可能成为更好的企业家。 + +我觉得这是创始人基因决定的,没法弥补,即使老一代创始人你招了一个也没法弥补,因为过往太成功了,他肯定觉得我更懂,你们懂个啥。但新一代不一样了。 + +极客时间:新一代的创始人,面对的是新一代的问题? + +毕玄:对,他们就会去解决。美国为什么能做好全球化,他们小孩一出生面对的就是各个国家的人,他觉得这不很正常吗?但中国根本没这环境。 + +极客时间:你现在是在创业的第一年,感受怎么样? + +毕玄:第一年你还在相对乌托邦阶段,还在蒙头做一个自己觉得对这个世界很有价值的产品,是最爽的,拿了融资尤其,一你不缺钱,二业务增长也不是你这个阶段最重要的目标,诉求还不会很强烈,没有到比如要追求盈亏平衡,自己能做一两家,就已经觉得我这产品太成功了。 + +但等见到真正的客户让他付钱的时候,你可能就发现原来根本不是这样,就吐血了,而且后面业务压力会很明显,第二年可能就会期待你做复制的速度,第三年可能要求高增长,甚至开始要求你打平。大家觉得创业第一步就很痛苦,但后来你会越来越痛苦(笑)。 + +我们节奏不算太慢,去年11月成立的,到今年6月左右就开始接触外部客户,我就跟团队说美好的时代要结束了。 + +你会看到客户跟你想的肯定有一个GAP,还可能很大,这很正常,当然就看你怎么看待,像我们就觉得挺好,至少说明客户对我们还是有点兴趣的,虽然有GAP,但客户对我们有兴趣,只要弥补这个GAP,我们还是有点机会的嘛。 + +当然你也会有另外的打击,比如说拜访很多家客户可能最后感兴趣的就是几家,这就看你怎么接受了,因为这就是失去品牌后最大的问题,所以创业公司确实比较难。 + + + + + +  + +极客时间:在产品的运作节奏上,有做市场、品牌这些事吗? + +毕玄:我自己的看法,这取决于你公司定位的客户类型。比如说如果你做开源,那不用说了,肯定是要重点做品牌,做社区的运营,重兵配备。 + +但做ToB很多是商业的,像我们,每一个客户都是商业的,不存在免费用户,而且我们做的是中大型,他们其实不大会受Marketing和品牌的影响,因为中大客户没有多少是我网上看篇文章、听到哪家公司,来找你问一下能不能提供个什么卖给我,这不大会发生。 + +另外销售来了其实也没有用,之前我们见了很多客户,可能有20多家,很多对我们有兴趣,但我们后来都很害怕。 + +极客时间:啊见了这么多客户,也都有兴趣,为什么会很害怕? + +毕玄:说实话我们的成功率还是不错的,客户觉得挺有意思,就会说要不你们来测试一下。但最大问题是我们自己并没有准备得那么好。 + +极客时间:没准备好,是因为你们初版产品还在做? + +毕玄:比如说有四五家,让我们同时去试一下,但我们没有这个人力。所以后来我们7月份叫停了所有的客户拜访,都不去见了,我们觉得目前已经有明确可以共建的客户了,就先做好这些,其它的不用纠结,我们相信只要做好了,至少在中国目前的情况下,那几家应该还是我们的。 + +极客时间:所以你们去找这些客户的策略是什么? + +毕玄:逻辑很简单。首先我们认为中大公司一定有自己的客户群体,剩下就是一家一家拜访,当然你肯定先选赛道里最头部、最有影响力的,所以这种情况下反而越低调越好。 + +因为你现在没有客户,太高调在中国真不是好事,卷得非常厉害的,抄袭也非常严重,你最好能非常低调地出现在各中大客户的名单里,毕竟市场可能就这些客户,谁先做掉这里面的大部分,谁就赢了。 + +等需要做品牌的时候,其实我已经占领了不少客户了,需要更规模化的。但前期我们觉得那些一点用都没有。 + +极客时间:前期的客户触达具体是怎么做的? + +毕玄:刷脸。因为你创业不可能什么资源都没有,肯定或多或少有点,然后就要去拜访这些人。 + +但比较难的是因为我们每一个客户都是要付费的,加上单价又比较高,不是大家随便帮你刷个脸就可以的。付费到一定金额就非常复杂,决策链长决策成本高,创业公司又没有品牌,我为什么要选择你?光这个问题在内部都能被挑战到死,你跟那个人关系是很好,但你为什么要定向采购这一家?会不会涉及利益?为什么不引入更多家供应商比价? + +种子客户是纯属刷脸和信任,根本没什么别的,否则别人只要问你有客户Case吗?没有,你就已经出局了。但是创业公司第一步如果刷不到种子客户,基本就没有什么希望了。 + +  + +水友讨论区 + +今天的对谈到这里就暂时结束了,重点聊的是毕玄对创业的思考和认知,毕竟做复杂的业务,认知决定了产品成熟度。 + +这是他的第一次创业,在选择方向时,逻辑主要有三点:自己相对有优势具备领先性、相对能产品化和规模化、商业模式具备盈利性。 + +代码智能化方向他认为不满足第1点,异地多活不具备第2点,现在大火的开源方向在他看来暂时还不太满足第3点,所以为了成立一家真正的公司,他选择做调度,成为了一名在创业公司“打杂”的CEO。你在选择自己之后的从业方向和公司时,3点判断标准其实也可以借鉴。 + +不知道你对今天对谈的哪个部分印象比较深刻,欢迎留言讨论。 + +到这里,毕玄个人的20年程序人生我们就聊完了。后面我还更新了一讲番外,聊一聊一切的开始——毕玄的大学经历,希望了解毕玄接触计算机的初心,把他的早期想法展现出来,与他后期观点形成对比,看看我们是否能找到他在面临无数人生选择时的底层逻辑。 + +拓展阅读 + +作为一个喜欢写文章的人,自己的人生翻开了新的一页也少不了会手痒想写上一篇,之前毕玄就创业写了这篇:凡是过往,皆为序章,凡是未来,皆有可期 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/团队:在人身上,你到底愿意花多大精力?.md b/专栏/超级访谈:对话毕玄/团队:在人身上,你到底愿意花多大精力?.md new file mode 100644 index 0000000..b51ef1d --- /dev/null +++ b/专栏/超级访谈:对话毕玄/团队:在人身上,你到底愿意花多大精力?.md @@ -0,0 +1,294 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 团队:在人身上,你到底愿意花多大精力? + +你好,我是叶芊。- + - +今天我们讨论怎么带团队这个话题,哎先别急着走,你可能跟很多人一样,觉得带团队离我还太远,或者觉得我才不要做管理,我要一路技术走到底,但是你知道吗?带团队做事,其实本质不是你认为的怎么管人的问题。- + - +不管你对管理抱着什么样的认知,在对谈开始之际,我们先抱着空杯的心态,看看毕玄这个不愿意做管理但还是带了十年团队的人,他是怎么理解Leader的,他的灵魂三问一定会给你启发。 + + + - +极客时间:之前聊你带过的项目,你提“被任命”很多,感觉你不是很愿意做管理,但是还是做了这么多团队的管理,为什么?你对Leader这个词是怎么理解的? + +毕玄:这个变化,我自己觉得最大的原因是T4、异地多活的经历。T4,我带了一个很大的虚拟团队去做一个很大的事情,也拿到了一个相对不错的结果,但比起对我的影响,对其他人的影响是小很多的,这个状况我不是很喜欢。异地多活也是。 + +后来看运维团队的时候我更觉得,对一家公司来讲,一个组织能不能往前发展是更重要的,如果你想改变,就必须成为Leader,这不带团队是不可能的。所以两件事都让我觉得,如果你想对这个公司有更大的贡献,就必须带领更大的团队。 + +极客时间:所以就你来看,比如一位开发同学想成为Leader,需要关注哪些方面,会遇到哪些问题? + +毕玄:我觉得就是个心态。阿里给新管理者的培训,尤其我们技术线,要解决的第一个核心问题就是心态,因为很多人是我根本不想做Leader,你们觉得我技术很好,干活能力不错,所以就硬把我摁到了那个位置,所以他不觉得是个提拔,他觉得你这是坑我。 + +技术人会很害怕做管理以后,精力受到影响,最后导致我好像日常都在干一些很虚的事情,以后会不会工作都找不到了。但如果我一直写代码,至少找工作不会是大问题,尽管现在说什么35岁,但事实上如果你写代码能力强,确实就不存在找工作的问题,因为技能永远都在。这是技术人很正常的想法。 + +所以这第一关,很多技术人就很难过。但我跟很多人说最起码你应该试一下,有些人可能试了一下之后觉得也挺好能接受,如果试一下你真的觉得接受不了,那你就专心做一个程序员,其实也没什么。 + +极客时间:技术人想做管理,首先培训对管理的认知,从太虚了出去肯定找不到工作,变成带一帮人成事。那跟人打交道这个方面会有问题吗? + +毕玄:第二个很难迈过去的关就是看到人这个层面的问题。因为多数人选择做程序员就是不想跟人打交道,只想跟机器打交道,这一做管理者就完蛋了,你不可避免要跟人打交道,不然这个团队肯定会有些问题的。你可能觉得我只要做事就行了,但关键是你要做的事情谁来干?另外你怎么排兵布阵?你必须了解他才能排兵布阵。所以这一关我觉得技术人更难迈过去。 + +极客时间:这关你是怎么过的? + +毕玄:最大的前提还是我认可那一点,只有带领团队,才能做更大的事情,对这家公司才能有更大的影响。你就自己先想好,是觉得做一个专业人员更好,还是觉得做一个带领更大团队的Leader更好,而且要知道Leader也有很多种类型。 + +之前很多人不断让我带团队,但我一直拒绝,因为我在带100人的运维团队之前,规模就一直控制在20-30人,从来没有超过,而且说实话这种还不大存在管理问题,你稍微分出一点点精力就可以了,所以我很难理解现在为什么带20-30个人会有问题。 + +极客时间:为什么?因为20-30人团队规模小,管理比较简单,就是带着一帮兄弟做事? + +毕玄:因为20-30个人你很容易熟。每个人,我都知道他们在干啥,也大概知道他们的能力状况,而且我也不需要付出太大的精力。但带到100人的时候,很多人我都不一定认识,跟我有很多交互的就更少,非常少。 + +但我带团队的路线并不好。我是从20-30人上来就开始带100人,然后可能带了半年左右就变成带三四百的团队了,后来就带600多人,这不是个正常的路线。很多东西你会搞不太清楚,因为中间隔的层级变化太快,带100人你可能隔了一两层,但带600人就隔了很多层,但我的管理幅度最多只能到向下两级。 + + + + + +  + +极客时间:那你觉得怎样才能成为一个好的Leader呢?结合你自己从20-30人、100人,最后到600多人的管理经历,有哪些是你当时没做好,现在可以做好的? + +毕玄:其实核心还是花精力的问题,就是在人身上,你愿意花多久的精力。 + +当时15年下半年带100人的运维团队,那100人我都比较熟,因为我们一起战斗过,做过好多项目,大家跟我打交道非常多,所以我去带的压力没有那么大。而且我带的那个时间点刚好是阿里最重要的谈年度绩效,谈完几个月后团队就解散了,理论上是Leader最痛苦的半年,但因为我们大家都比较熟,谈得也没有很辛苦。 + +但后来16年带600人了,你发现一个最大的问题就是这些人你不熟,你到底怎么办? + +我以前带团队做得相对最好的是,对团队走向什么方向,我有自己的看法,然后我会告诉所有的人,但是你想好了方向之后,在团队里你到底怎么安排大家去把这些事情做好?怎么排兵布阵?怎么落实?这些当时我是有很大差距的,因为这里很关键的问题就是你对这个团队到底有多了解。 + +为什么很多Leader都喜欢用自己的老搭档,换一个领域他把那帮人又拉过来,就是因为不用磨合,大家都熟悉,就很简单。但你面对全新团队的时候就很难,你得愿意花精力。 + +极客时间:以前你没做好是为什么?因为自己不太愿意花精力了解人? + +毕玄:不愿意,我觉得那简直太累了,要了解一个人是很痛苦的过程,你要跟很多人聊天,得花非常大的时间和精力,技术人都不是很喜欢这个事情。 + +极客时间:聊天这么必要吗?不能说用很好的协作工具交流吗? + +毕玄:工作可以,但工作之外,你只能靠跟人聊才能有更多的了解。 + +极客时间:工作之外的东西是想看到什么? + +毕玄:有些时候我们需要看到的是一个更立体的人,我需要看到你在其它方面的一些状况,因为我们最关心的还是你的空间问题。 + +极客时间:个人成长? + +毕玄:对,就个人的空间问题,还有你对这个团队的看法。我以前只跟他们聊三个问题。 + +  + + + +  + +毕玄:第一个问题,虽然HR很不希望我问,但我基本都会问,就是你离开阿里巴巴的时候,你会找一份什么工作?我觉得这就是你的职业规划,如果你想过这个问题,我们就可以更好地规划你在阿里的路径。 + +因为说实话你不是一定要在我的部门,我是很无所谓的,如果你觉得去另外一个部门对你更好,那就去,我觉得这样对公司也是更好的,因为你自己有动力在,所以我从来不强制要求。很多Leader可能会放大这个,觉得怎么样,但我觉得把它放大也没什么用,说实话还不如给另外的人一些机会。 + +但你会发现很多人其实没有想过这个问题。 + +极客时间:对这个问题有想法的,大概是什么比例? + +毕玄:这就看级别,很明显的,级别越高的人通常越想过。 + +但是你没想过,后果很惨的,因为有可能有一天不是你要离开阿里巴巴,是阿里巴巴要你离开,这很正常,现在不就上演了?以前大家信心太强了,觉得公司发展得这么好,我怎么可能会走?或者阿里怎么可能让我走?但现在再看看,很多人出来真的傻了,根本不知道该怎么办,这就悲剧了。 + +如果你想过就会好很多,因为早就有所准备了。如果你实在搞不清楚自己的情况,你也可以去外面面试一下摸个底,这样你能立刻对自己有更好的判断,我们不反对这件事,因为阿里这个平台对多数人有极大的吸引力,很多人根本离不开。 + +极客时间:职业规划这事,你自己是怎么做的?一般会做多久? + +毕玄:我不会做太久,但我肯定会想出来以后的几种可能性,不是说你一定要固定下来做什么,但要有可能性,这样你可以反推自己每一年要怎么发展。 + +我们肯定希望每一年对自己的职业生涯都是有帮助的,多数人的职业生涯都在几十年,看起来很长,其实很短,像我们这帮人尤其,剩下的很短了,每一年都特别重要,错过就错过了,就没有了。 + +所以你要想好,也许这家公司不能给你符合你的回报,但只要职业生涯你是一直在成长的,总有一家公司会给你,那纠结个什么?你纠结也没有意义,因为你可能在阿里混得很好,但真的有一天要走了,那你怎么办? + +极客时间:所以第一个问题其实是作为Leader要关注团队同学比较长远的职业规划,也要帮他们去关注,那你聊的第二个问题是什么? + +毕玄:第二个我会问的就是,过去一年你觉得对你职业生涯是加分、减分还是持平,能不能写进简历? + +一个人,比如我在阿里工作14年,我不会说这14年里每年做了什么,我只会告诉你在阿里我做了三件事,这意味着中途肯定有几年是我觉得没必要提的,因为提了对我找工作没什么帮助。所有你觉得值得写进简历的事情,肯定对你是加分的。 + +这个问题是很落地的,我不是跟你谈虚的,问对你的职业生涯有没有帮助这种,员工当然告诉你有,但如果你问他会不会写进简历,有些人是会告诉你真话的,没有必要写。那你就要想一下你给他的空间是不是有问题。因为这种人如果真的很有能力,你这么带一两年,他肯定走了。 + +第三个我会问他对团队的一些看法,觉得有没有什么问题之类的,很轻松的,大家随便聊。但这个聊完其实对他会有很大影响,如果你对团队问题很有思考和想法,Leader就能结合你的发展潜力和职业规划,来更好地安排位置,帮助你成长。 + +极客时间:对团队的看法,会提到工作吗? + +毕玄:这个跟实际工作没有什么关系,对过去一年的工作做点评那是另外绩效的事。 + +  + + + +  + +极客时间:你当时带团队的时候认为Leader核心是要在团队的人身上花精力,人熟了团队就很好带,所以你就想用这三个问题来了解他们?这是带600人大团队的时候你问的? + +毕玄:对,我给自己定了指标,跟100个P8聊天,一定要跟这些人至少聊一次。 + +极客时间:当时怎么开展的?有周期吗? + +毕玄:一般是隔一个季度,我会先聊一圈,每个人半个多小时,然后我会从里面选出一些人经常跟我交流。 + +其实难度很大,我们就纯粹聊乱七八糟的,但大家没那么熟,不知道聊啥,而且两个技术的人没什么可聊的。多数Leader不会这么做,因为是闲聊,不是聊工作,所以做了可能对我业务也没帮助。 + +极客时间:那你聊完这100个P8有没有什么具体收获? + +毕玄:肯定会有,你只有聊过才会发现,有些人对一些问题其实是有思考的,然后你会觉得有些人其实挺有潜质去承担更大的事情。所以我以前也会跟下面的人说,我也需要你们跟下面更低一些级别的人聊。 + +极客时间:手上的人有没有潜质是怎么判断的? + +毕玄:这个就只能主观,就是我看你对一件事情的想法是什么,这其实很重要的,因为不同级别的人,说白了,无非是他看问题的角度不一样。每一层都要有自己的角度,像有些很高层级,需要看经济发展的大势,我们做决策就不需要。但这点对很多人的挑战很大。 + +极客时间:这个挑战是指什么?看问题的新角度很难训练? + +毕玄:因为你可能根本不觉得自己角度不够,就会导致你下的判断很有问题。 + +为什么大家很多时候觉得老板不靠谱?觉得老板都在瞎做决定?是因为你觉得自己很专业,看问题的角度很对,但事实上老板他看到的角度可能比你更多,他另外的一个角度导致他做了那个决定,但这个角度你压根不知道,也不关心。 + +但好的Leader是会讲这些的,阿里以前这点就做的很好。我会跟你讲我为什么做这个决定,我的考虑、出发点是什么,你可以不认可,这没关系,但是我至少已经告诉你了,所以你也就可以理解我为什么这样做这个决定,不会觉得我是闷头随便拍的。 + +因为多数Leader做选择肯定是有原因的,肯定不是闷头,只是他愿不愿意跟你讲而已。很多就比较粗犷,我懒得跟你讲,你按照我要的做就好了,管我怎么想的。但这样反而对执行层面的影响会很大,如果员工理解了,其实执行的偏差度会小很多,虽然他内心可能还是不认可,但这就不重要了。 + + + + + +  + +极客时间:所以作为Leader,团队人多了或者换到了新团队,一定要做好花精力了解手下人的准备,这一点是你的经验教训。还有什么吗? + +毕玄:后来我带的团队已经很大了,我发现大团队的Leader,更难的是排兵布阵的阵型问题,因为除了你,你团队的很多人都会面临这个挑战。 + +我后来经常问手上的人,你觉得,理想中你团队的阵型应该是怎么样的,阵型里的那几个位置,就是向你汇报的那几个人,能放一个P几? + +如果你认为这个位置到顶能放一个P8,但是我觉得可以放个P10,这就完全不一样了,我得考虑一下是不是该把你换了,因为你限制了下面人的空间,阿里以前有句老话,P10带的团队可以都是P10,但P9带的可能就只能到9。 + +包括招人也受这个影响,你能不能招到一个更好的人取决于你怎么给别人讲,这当然是画饼,但高级别画饼是很重要的,除了钱以外还得画饼。 + +极客时间:这里能容纳什么级别的人核心考虑的是什么问题?人才的成本问题? + +毕玄:主要取决于价值,为什么我能容纳一个这么高级的人?是因为我做的事情对这家公司的价值是足够的,面临的挑战,需要很高级别的能力才能稳得住。跟评晋升是一个道理。 + +这一点,很多人是很难想清楚的,他就觉得我招一个这样人够了。但我觉得应该招个更好的人,而且也完全可以招到。所以关键就看他到底怎么想,我坚定地认为一个Leader要做好,核心是先要对整个团队的方向有思考,你有自己的观点很重要。 + +极客时间:为什么你会觉得“思考团队方向”对Leader来讲最重要? + +毕玄:因为能开始思考方向首先意味着他有基础知识的积累,另外是他会主动去想,很多人其实也不太想这个事的,不想承担这个责任。 + +说实话很多人做到Leader的位置是可以不想的,上面说什么或者下面说什么,我就做,我不需要表达自己的观点,反正我是个执行者。这是最安全的Leader,因为没做好,要么是上面给他再发一个问题,要么是下面干活不靠谱。所有公司这样的Leader非常多。 + +所以做Leader,你能不能想好负责的这一块儿,要走到哪里去?你可以跟我观点相悖,这都不重要,但关键你想过,并且有逻辑,那我觉得就可以了,这个人我觉得未来是有潜质的。 + +我觉得,其他所有技能都是可以弥补的,但对方向的感知这个技能是很难被弥补的。如果你其他很强,但没有这个,我觉得你是不可能把团队带到一定高度的,没有前景,因为你不可能做创新,最多执行做得还可以。 + +  + + + +  + +极客时间:好,聊了你对Leader的看法,也聊了如何成为管理者,以及管理者需要关注哪些方面。最后总结一下,一名Leader想带好团队的几大关键问题,你觉得是什么? + +毕玄:Leader就几件事,把方向定好,阵型布好,位置放好,另外当然是事情的跟进,还有一点整个团队的成长,就是人才培养问题,这也是我以前带团队没有做好的一点。 + +如果成长问题没有解好的话,也没意思,因为最后可能是这个团队事情做得不错,不过成员没有得到成长,但最重要的其实是人,所以后来我们就会反过来看,只要这个团队的成员都成长了,通常来讲,事肯定就做成了。 + +极客时间:人才培养你以前没做好?后来对怎么做好有新想法吗? + +毕玄:我以前没做什么,所以就做得不好。 + +我们后来才觉得有些东西还是需要的,比如培训班什么的,尽管都觉得没有用,但也看你怎么开这个班,如果是讲怎么思考一些问题的就还可以,因为Leader可能最重要的就是训练他怎么看待问题,比如这个团队我们今年定这几个方向是为什么,现在团队面临的一些实际问题怎么思考,这些只能靠大家具体讨论。我们觉得这是可以训练一些Leader的,所以刻意培养还是很重要的。 + +当然除了开班以外,更重要的就是你去安排这些人的位置。 + +极客时间:安排人的位置,是刚刚讲的排兵布阵吗? + +毕玄:因为做项目其实是可以给有些人机会的。我们以前都太关注事情了,如果你太关注事情,就会以稳妥为主,在选人的时候肯定选最相信的人,以及你觉得最有可能做成功的人。 + +这对Leader来讲是好的,他自己能拿到很好的结果,但是手下的人没有办法成长。这种Leader是会把下面人全用废的。 + +极客时间:但一家公司肯定也有这种人。 + +毕玄:很多的。大家会说你是踩着一片人的尸骨走上了巅峰,但他确实走上去了,其他人也没法说什么,而且公司很多时候觉得也可以接受,也会给很高的评价,因为他通常是更能拿到结果的人。这就要他自己怎么看。 + +但这种人总体不是那么受欢迎的,因为他其实是没有团队的,尤其如果他的团队要去面临不同挑战的时候,这种人就很难,没有人愿意跟他,而且他也很难说每次都来一波新的人用,这也很难。 + + + + + +  + +极客时间:作为Leader你觉得最需要关注的是人,那在人才培养上,你有什么自己觉得很好用的方法吗? + +毕玄:人才的培养体系这个事情,阿里有个人讲的特别好,就是Lucy(彭蕾),她绝对是这方面的绝顶高手。做支付宝总裁前,Lucy有一段时间是集团的首席人才官,我们听她讲过一堂课,冲击太大了,对大家后来带团队都有很大影响。 + +当时她讲一个团队的核心资产是什么?不是大家认为的代码库,也不是机器什么的,其实是你手上的这帮人。所以她要求每个月或者每个季度,Leader应该做一份人才的资产负债表,就像做财务报表一样。这个报表里要体现出你这个团队里的人才是在亏损还是在盈利。 + +亏损盈利就是指,比如说你作为Leader,认为团队里有5个人必须关注他的发展,那这5个重点人才的职业生涯发展是向上的,还是平的,还是向下的。向上就是盈利,向下就是亏损。 + +极客时间:这个人才表是怎么具体操作的,可以详细讲讲吗? + +毕玄:首先你要盘点你现有的人,另外还要盘点你需要吸引的人的能力,最后你年度下来给一个报表,看看今年你在整个团队的人才资产上到底是盈利的还是亏损的。说实话按这个,多数Leader估计都得挂。 + +极客时间:算完人才帐就很清晰了,但感觉这个工作量挺大的?具体操作起来效果怎么样? + +毕玄:对,Lucy这张表真的很好,我们都觉得应该做,但大家因为业务精力各种原因没有做好。我后来就每年问我团队很核心的人那个问题,过去一年到底对你的职业生涯是向上、向下还是平的,你会写进简历吗? + +但技术线Leader确实特别难做,多数技术线Leader是被硬推上去的,他本身不想管人,但你真的要做好人才这件事,意味着你在人的身上要花费非常大的精力,要和很多人聊天。 + +业务线里更难,因为业务线里更直观,就是关注指标,比如说我只看今天的销售额有没有做到,才不管你们这帮人有没有成长,而且有时候想关注也没精力。所以我们可以理解。 + +极客时间:那你的下属如果是重点人才,他想离职,你会留吗? + +毕玄:我从来不留人,一般都是向我提离职,我就同意了。 + +因为开始的时候我会讲清楚我不留人,大家都比较熟,你提了我相信你肯定是经过考虑的,而且向我汇报的人级别不会太低,你作为一个这么高级别的人,如果想过了外面,基本上就会离职,至少在我接触的人里都是这样。因为你的心一旦想过,就太难阻挡了,我再留你没有任何必要,所以我从来不留,但我的HR有时候很不爽,他们会去帮忙留。 + +但我是会聊的,看看你是怎么想的,无非是你认为对自己的职业生涯成长更好就可以了,也不需要我认为,所以我都只问为什么你觉得这个选择对你的职业生涯更好?如果你觉得是,那不纠结,如果你想了一下觉得好像也不是,要留下来,那你就留好了。 + +极客时间:对职业生涯的选择,你会给建议吗? + +毕玄:我会说我的考虑,但肯定是以你的判断为主,因为职业生涯这个东西说不清楚。 + +极客时间:聊的时候,有没有人让你觉得他的想法其实不太理智? + +毕玄:很正常,这个跟你对趋势的判断有关系,很主观。 + +  + +水友讨论区 + +到这里今天的主题讨论就结束了,不知道你是否有启发。 + +如何成为Leader?毕玄认为核心是解决对管理的认知问题、和人打交道的心态问题。 + +一个Leader,毕玄总结需要做的就几件事,定方向、布阵、排兵、事情跟进,以及整个团队的人才培养,他认为想要成为一个好Leader,了解手上的人非常重要,他会周期性地问3个问题: + + +你离开公司的时候,你会找一份什么工作? +过去一年你觉得,对你的职业生涯是加分、减分还是持平,能不能写进简历? +对团队的一些看法,觉得有没有什么问题? + + +后来团队更大,他还会问手上的Leader,你觉得理想中你团队的阵型应该是怎么样的,阵型里的那几个位置能放P几? + +如果你现在是一个Leader,这几个问题你会怎么回答呢?如果你还没有开始带团队,其实在个人成长上,这几个问题也是非常好的复盘工具,甚至你也可以做一张自己各项技能的资产负债表,看看过去的大半年,哪些项是盈利的,总计是正还是负。 + +欢迎在留言区写下你的回答,如果今天的内容你读完有收获,也欢迎分享给身边的朋友,一起讨论。 + +下一讲我们会聊一聊有关做事的文化,下一讲见。 + +拓展阅读 + +之前毕玄就程序员的个人发展写过几篇文章:- +1024,节日快乐的同时说说中年危机- +“混”的中层们,你们的下一站是? + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/开篇词这一次,我们来采访毕玄.md b/专栏/超级访谈:对话毕玄/开篇词这一次,我们来采访毕玄.md new file mode 100644 index 0000000..c46535e --- /dev/null +++ b/专栏/超级访谈:对话毕玄/开篇词这一次,我们来采访毕玄.md @@ -0,0 +1,61 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 开篇词 这一次,我们来采访毕玄 + 你好,非常高兴能和你在《超级访谈:对话毕玄》专栏见面,我是编辑叶芊。 + +你是不是很好奇,为什么这次要和毕玄对谈?答案很简单,因为这个人有一份神奇到独一档的职业经历。 + +2002年大学生物系毕业后,他成为了一名做政府项目的小程序员,在小厂辗转了五年。到这里,看起来还没有什么太独特的,就是一个普通的非科班程序员。 + +但就在小厂的第五年,他被称为国内 OSGi 第一人,07年年底顺势加入淘宝后,就开启了自己仿佛转岗无上限的成长之旅,你也可以简称——“开挂”。 + +一进淘宝,他就负责HSF的设计和实现,在14年后的现在,HSF每天还都承担着百亿次以上的服务调用,是淘宝乃至阿里非常重要的中间件框架,但后来他居然跳去做了NoSQL数据库HBase的改进和落地,在接下来的2011年,他仿佛预测到了容器的技术浪潮,又转岗去负责淘宝私有云T4的设计和落地,比Docker都要早两年。 + +但那之后他居然又从研发转到了运维,成为了一名运维工具研究员,顺带2013年做了广为外界所熟知的淘宝异地多活改造,之后丝滑过渡到了更大的集团级项目——云资源统一调度,同时还带着研发效能部门,做研发效能工具。19年他开始带视频云的业务团队,结果因为疫情视频领域爆火,部门备受瞩目,但下一年他却从阿里离开了。 + +现在他创业快一年了,是一家做资源调度产品公司的CEO,据说天使轮已经融完了。 + + + +这么看毕玄的职业经历,你说这个人,转岗也就转岗吧,方向判断得这么准,就像能预测下一个技术风口一样,关键是怎么还能做一个成一个?简直就是“谋事在人,成事也在人”的真实写照。 + +现在你是不是对他的洞察力很佩服,是不是在想,如果能搞到他判断方向的技巧和成事经验,对你的帮助一定很大。 + +正是因为这一点,我们从今年2月份开始筹备这个专栏,采访问题写了十几版,但因为档期和疫情几度差点夭折。每个月,我一边立一定能去采访的Flag,一边因为北京杭州两地的疫情心惊胆战,果不其然Flag倒了无数次。最终到7月份我们才挤出了三天,完成了一场共计13个小时的长谈,聊了100多个问题,终于,不负你的期待,我们挖到了这份来自毕玄的独家秘籍。 + +于是,专栏最有代表性的“高手锦囊”模块也就此诞生,从个人成事、方向选择、团队带领、做事文化、架构修炼这5大方面,把他分析问题的思路和做事情的方法浓缩给你。 + + + +当然想要把方法论化为己用,你还需要丰富的案例,这就是专栏前三章的作用了。 + +在“初出茅庐”“江湖风云”“创业维艰”中,你将感受到这位技术大佬独特的人格魅力,这种魅力不仅来源于他判断方向的战略眼光和驾驭复杂多变局势的能力,还有他背后的有趣故事所带来的新维度、新认知、新启发。 + +毕竟单看他的经历,我们可能觉得他是一个“平平无奇”的成功人士,基本没什么障碍,一路升级上去就成功了。但在看完一两讲他的神奇经历之后,你对他的印象一定会有一百八十度的大反转…… + +简单举几个例子吧: + + +面试,没写简历,也没答对几道题,他却进了淘宝? +写第一个系统HSF,刚转正就差点把淘宝搞挂了? +处理故障,充分锻炼了编程能力,他却说这对公司是个恶性循环? +做异地多活被广为人知,他却说做大项目,千万不要塑造一个神? +转到前景堪忧的运维团队,他表示团队价值问题自己也解决不了? +集团级项目统一调度做成了,他说可能是因为时机? +…… + + +基本上所有我们认为有点奇怪的现象,在采访的时候他都说“这很正常”,这句毕玄的口头禅也能算得上是这个专栏的小彩蛋了,如果你好奇,可以统计一下都出现在了哪里。 + +所以如果要一句话介绍这个专栏,这并不是一位成功人士的回忆史,反而是一场20年踩坑经历的深度复盘,讲的是:一个很有个性的普通人,如何在互联网浪潮中辗转腾挪,找寻自己的发力点。当然等你看完,如果有其他想法,欢迎留言交流。 + +另外,如果你对毕玄做的一些项目感兴趣,期待了解更多的技术细节和方案,也不妨看看文末的「拓展阅读」,希望对你有帮助! + +好啦,现在万事俱备,我们一起来认识下这位大神吧! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/成事:技术人最大的问题就是情怀化.md b/专栏/超级访谈:对话毕玄/成事:技术人最大的问题就是情怀化.md new file mode 100644 index 0000000..f332163 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/成事:技术人最大的问题就是情怀化.md @@ -0,0 +1,209 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 成事:技术人最大的问题就是情怀化 + +你好,我是叶芊。- + - +从今天开始,“高手锦囊”板块的5场专题研讨即将展开,我们会从毕玄的具体工作经历出发,在“个人成事、方向选择、团队带领、做事文化、架构修炼”这5个方面,希望能总结出可供借鉴的分析思路和实操方法。- + - +毕竟对于这样一位技术敏感度如此之高的大佬,仿佛总是能提前站在下一个技术风口上,他是如何分析技术方向的,又是如何在一次次技术浪潮中求得自己和团队发展的,如果能总结出这种方法论,相信对你一定有很大帮助。- + - +今天我们先来对HSF做复盘,毕竟第一次总是印象深刻,对于第一个真正自己从0做的访问量巨大且核心的系统,又出了大故障,他总结了哪些成事方法呢? + + + - +极客时间:复盘一下你进淘宝做HSF的过程,这是你做的第一个专业性要求高的系统,之前所有人都讲做一个网站,访问量从 200 万到1 个亿是不一样的,但没有人知道到底哪里不一样。经历过上线故障之后,你觉得大量级的系统到底是什么不一样? + +毕玄:这一次之后我就懂了,量级其实不重要,重要的是要求。一个要求非常高的系统跟其他系统的差别是什么。 + +要求非常高的系统,核心是对整个系统的所有环节,你都要非常非常清楚。因为这是概率问题,你以前可能认为十万分之一的问题不会出现,但在一个大型系统里,它是必然会出现的。所以写一个小系统是容易的,写一个超大系统因为所有的概率问题都会爆发,对工程师的要求就变得非常非常高。 + +以前流行一篇文章,你在浏览器敲了一个回车发生了什么?这个问题其实挑战很大,说实话,到现在也没有多少人能非常好地回答出这个问题,因为背后有非常多链条。 + +你必须非常清楚你负责的那个部分从头到尾所有代码的细节,后来阿里对基础工程类的人都有这个要求,不能说我用了一个三方开源的东西,它出了故障,所以问题是它的,那当然不是,对我们来讲,都是你的故障。 + +在这个阶段,对工程师的要求已经完全不一样了,不是说你写完一个功能就拉倒,是你要能把代码维持着,这个挑战就非常非常大了。 + +极客时间:维持代码的成本,在开发之初也要考虑清楚。 + +毕玄:对。一个要在生产环境跑很多年的系统,未来怎么可持续地发展,在设计阶段是非常重要的。如果你做一个系统,面对1个用户和面对1000万个用户,只跑一个星期和可能要跑10年,都有很大差别的。这就是我最后交付的成本。 + +硬件也是一样,软硬件其实最终体现就是价格。我们看硬件,有些保修期很短,有些很长,有些寿命很长,有些寿命很短,决定了它背后的选择是差别很大的。当然,软件可能还做不到像硬件一样这么直接的映射,但差不多也是这个概念。 + +极客时间:所以为了保障对代码的掌握程度,大厂一般都自己写? + +毕玄:如果过多用别人的东西,你是很难清楚掌握的,最好全部自己写,在出问题的关键时候,你可以迅速反应。当然你也不可能说一切都自己写,肯定会用到一些开源,所以我们会要求,你如果用任何开源的东西,对这个东西的所有代码也必须非常清楚,否则,在大规模系统里你一定会栽跟头。所以对大规模系统来讲,技术选型是很不一样的,包括对团队的要求也是。 + +很多开源的东西,大家觉得外面很火,阿里为什么不引入?其实很简单,引入进来,阿里又没有一个专业的团队来维护,那最好不要引入,你还不如自己写算了。这样代价肯定是也有,但对公司来讲可以接受。这个就是认知。 + + + + + +  + +极客时间:我们继续一个系统的技术要求这个话题聊,如果说量级不是核心重要的,那对开发者来说,是什么导致了系统的高要求? + +毕玄:其实本质是业务,你看现在的几个网站,业务要求的差别非常大,也奠定了他们背后技术难题的差别非常大,我后来接触了一些要求更高的公司对这一点的感受更强烈。 + +淘宝你说以前要求高,当然也高,但是说实话还没那么高,因为淘宝出完一个故障,如果几分钟后能恢复,交易量是能恢复的,会冲高然后恢复。所以淘宝说实话出一次故障也就这样,能发生什么?所以出了再处理是可以的,如果还能快速处理,大家就觉得你很牛了。 + +但像饿了么这种外卖公司,很大的挑战是他的系统在11点12点的时候不能出故障,关键是不能出,压根不能,这就不一样了,跟我们出之后再弥补差很多。 + +极客时间:因为外卖的业务竞争太激烈了? + +毕玄:对,很简单你想,我饿了么跟美团打的这么辛苦,可能因为你们技术团队11点出一次故障,业务份额瞬间就变了,那还怎么玩,因为用户一定要点外卖,饿了么不能点,就会去美团点。包括当年滴滴跟快的的战争,快的背后是我们,当时我去快的呆了一段时间,处理各种问题,也是为了能让它更稳定,腾讯也派了大量工程师去滴滴。 + +这个时候技术真的是关键,因为谁稳定,就决定了用户会倾向谁,当然钱的补贴很重要,但技术也很重要。所以这个阶段,稳定性,已经是这些公司的核心能力了,本身就已经成为业务了。 + +这种对工程师的挑战非常非常大,因为一个不能出故障的系统是很难做的,但是如果说能接受出故障,给我1-5分钟的恢复时间,这两个系统的设计会有很大差别,因为出故障以后我可以想各种办法,但那个不能,我也很难回答。 + +所以我们做其他系统就会越来越明白,有些系统其实不像大家想象的那么简单。当时还有个段子,因为淘宝做的比较好,12306总出问题,大家都说淘宝双十一都没事,把他交给淘宝不就行了吗?但你如果仔细了解一下,会发现这些系统其实不一样的。 + +极客时间:什么不一样,可以展开讲讲吗? + +毕玄:12306这种是涉及民生类型的,像饿了么也可以认为涉及民生,包括共享单车都是,银行、金融这种就更不用说了。 + +到民生这种,挑战是挺复杂的,12306交给淘宝也不好解决,因为它和淘宝最大的区别是库存。淘宝的库存是静态的,买一个商品有多少库存,减1就好了,比方说我能卖1000件,那我减就行,虽然是高并发但也没什么,排队就好了。但你看12306最复杂的就是库存。 + +因为买一张火车票,从A站到B站中间有很多种可能性,是一个动态算库存的过程,但库存这个东西压力是最大的,还要动态算,淘宝都没想过这个问题该怎么解决,因为淘宝在库存上也很痛苦,但那也只是单品。 + +极客时间:所以和大家想的不一样,民生相关的系统,像12306、银行,看着业务很简单,但背后是不能出问题。 + +毕玄:对,包括以前我们觉得金融行业落后,像银行,你看看你们,做个变更简直了怎么这么难?后来我们懂了,如果我们是你们,估计也会变得那么严格,因为影响面是不一样的,银行只要哪一天钱取不出来,可能会立刻引发社会动荡,你说你是技术故障,别人可能都不信。 + +淘宝说实话并没有影响民生,而且商业竞争还不到出了问题用户就去另一个的程度。像外卖、打车太明显了,某天你份额异常飙升,基本是因为你的竞争对手挂了,根本就不是你业务做得多好。 + +现在,软件的核心系统对工程师的要求在直线上升,说实话新一代的互联网工程师是比我们更难的,因为我们有时间去成长,他们其实没有,上来就是必须做到这样。我们是出了无数次故障,然后慢慢学会的,知道每个地方是什么原因,但他们就真的太难了,因为没有机会。 + +  + + + +  + +极客时间:你说新一代互联网工程师更难,为什么会产生这个差别?和以前相比,你觉得现在这一代工程师面对的新问题是什么? + +毕玄:现在软件的要求跟以前的软件有很多差别,以前AWS取了一个名字,我们觉得很土,但后来觉得这名字也还不错,AWS给现在的软件取了一个新的定义,叫Modern Application。 + +如果大家去看以前的软件,一是相对来讲功能比较单一化,不会太复杂,现在大家要做的软件上来复杂度就比较高,软件要链接的东西更多。以前是我只实现一个功能,现在你看到的可能只是一个功能,但背后其实是N多功能叠加起来的,都是一个很复杂的公式,所以软件复杂度就很高。比如说外卖,背后涉及的链条非常长,软件写起来就很复杂的,不像以前我们很简单,这就是时代发展。 + +第二,以前的软件都是有成长期的,因为成长期很长,它对稳定性、可用性的要求不会那么高,以前大家不能用,也能接受,不能用就不能用,我可以换人工。但现在不一样了。 + +现在软件的成长周期太短了,在中国你从0做到1000万用户,以前要很多年,现在可能是几个月的事,这就完蛋了,淘宝当时能花一年来搞架构演进,现在的公司哪有这个时间,没等搞完他就倒闭了;而且这种倒闭就太冤了,我本来发展得很好,因为我发展得太好,用户量太大,系统撑不住,所以挂了,这简直了,发展不好挂了也就算了。 + +但现在技术侧就会面临这个问题,你的成长时间非常短,商业竞争非常激烈。当年不是这样的,淘宝起来的时候有竞对吗?自从把eBay踢出去,至少在那几年是没有竞争对手的,所以,稳定性等等都不重要,把业务、功能各种都做好就行了。 + +极客时间:所以还是回归到了业务、商业层面,当时如果淘宝有竞争对手,对业务和技术的态度会不一样? + +毕玄:我觉得很多都会不一样。但是现在中国不是这样了,现在你做了A,只要能起来一点点,你可能会发现中国瞬间诞生了100家做A的公司,那你要求的技术门槛就直接拔高了。 + +说白了就是利润空间的问题,一个行业只要开始赚钱,在中国绝对会卷到死。在海外这点会好很多,因为海外看你已经赚了那么多钱,那我还是不要做这个行业了,他会觉得我的机会很少。但中国不会这么看,中国会觉得,哇!他那么赚钱,我也可以做。 + +极客时间:不同时代的软件,以前就看业务,现在还要求技术门槛。 + +毕玄:以前技术真的不是核心,对业务来讲,说实话一点都不重要。 + +淘宝最早根本不认为技术有什么重要的,反正能用钱解决的,我全部用钱解决,技术最好不要干,最好全部是写业务代码做需求的。不要说我有一个团队是不做业务的,全部在研究什么基础技术,那完蛋了,因为这就是养人,而且这种人通常又比较贵,养一帮这种人都是成本。但现在不行。 + +现在很多公司上来如果技术没有达到一定的分数线,可能业务都没机会做,就已经挂了。因为中国用户被训练出来了,对体验的要求非常高,中国用户点一下恨不得立刻下一页,如果还多要转几下,他就放弃了,太卷了,但海外不会。这些背后全是巨大的投入,不管是人员、还是IT成本。 + +这就是现代,后来我们很认可AWS讲要跟原来区分开,当然它也是告诉你,为什么现在很多公司,尤其是初创还没有那么稳定的公司,应该用云,其实就是分数线。因为你用,至少一开始就一定达到了分数线,然后你可以更专注在业务上。但以前不存在这个问题。 + +极客时间:系统的技术要求变高,之前和现代的界限,是什么时候开始的呢? + +毕玄:中国我觉得就是移动起来。在PC时代,你看竞争其实不是很激烈,那个时候互联网还没爆发,没让大家觉得做这行有多赚钱。当然还有更赚钱的,当然也不能做。然后,大家突然间发现做互联网是最赚钱的,中国肯定卷死,这是中国最痛苦的地方。 + +极客时间:移动互联网起来的时候,大家都觉得怎么着做个5年10年的,但没有想到这么快,客户端没几年就发展到饱和了。 + +毕玄:因为PC就很多年。 + +极客时间:为什么移动时代会发展得这么快? + +毕玄:我觉得跟中国手机发展的比较好有关系,然后网络条件比较好,国外其实是因为网络太差,你想在印度点一下那么慢,那还是不要用了,手机也差。但中国这些基础设施太好了,基础设施真的是关键,基础设施越好,上面就越会爆发无数的可能。 + + + + + +  + +极客时间:关于HSF前面聊了这么多,主要是超大系统对程序员开发能力上的要求,在具体的技术选型上,之前你也在很多文章中反思用OSGi是非常错误的决定。 + +毕玄:对是的。 + +极客时间:现在你再去做HSF的技术选型会有不同吗? + +毕玄:不会,我觉得还是错的。这是我后来总结的,技术的人最大的问题是太情怀化。 + +我以前选择OSGi最大的出发点是什么?就是情怀。因为所有人都知道我出名是因为OSGi,很多人在我来了淘宝以后都问我:淘宝用OSGi吗?我说没用,这个对我这种技术人还是有点伤害。因为支付宝也用OSGi,我跟阿玺他们都很熟,他们用,我不用,他们又经常找我问问题,后来我就觉得那不行,淘宝也得用。这就是情怀,纯粹是情怀。 + +很多技术人做一个决定,其实他的出发点就是不对的,他的出发点就是情怀。 + +极客时间:但大家可能会举Linux的例子,很多做了最伟大技术的人,可能就是出于情怀。 + +毕玄:但我觉得在公司的层面,肯定是不能那么干的。所有的公司都是一家商业公司,那商业公司里做的所有事情,确实应该要对这家公司意味着什么。 + +所以关键是反思选择把原来的东西改造成基于OSGi的,对当时这家公司来讲,对客户、用户来讲,意味着什么?到底有没有帮助?是不是一个很好的长期发展选择?如果他的问题,你其实没有任何解决作用,那还不如以前,因为新方案一定会带来很多新的问题。 + +很多技术人,做决定最大的问题就是情怀太美了。比如说很多架构师Leader他为什么做这件事,可能就是技术上觉得这个东西看着不爽,想把它改的更完美一点。技术人确实有这样的癖好,因为技术这玩意确实可以很完美化,我觉得这个架构看起来不好看,这个代码看起来可以写的更好。 + +极客时间:但是从商业上看,把代码、架构改得更好看这些都是成本。 + +毕玄:对,商业是一个妥协的问题,事实上,一个平台要做好,架构师最大的挑战就是做平衡。所以在阿里我们面试很多P8升P9的架构师,问的核心话题都是你在这一轮架构设计里面做过什么选择和平衡,这才是最难的,而不是你做了什么很好看的玩意,一个看起来很完美的架构,最后有可能对公司是极大的伤害。 + +极客时间:这是业务上的考虑,是不是也有团队上的?因为后来听说你要做别的业务,但HSF这个项目用的技术栈又是OSGi,所以想托给其他人负责维护非常难? + +毕玄:是的,这个肯定有影响。因为你对整个团队的要求变高了,这很痛苦的,未来团队的招人等等都是大问题,包括团队的人出去以后,他们其实也会各种担心。 + +后来我跟很多人聊,包括有技术情怀的,他觉得有个语言特别好,用这个可以写一个更好的、性能更高的东西,对这家公司会有很大帮助。这个出发点看起来没错,因为出发点是对工作业务有帮助。 + +但这里没有考虑到一个问题,如果你离开了那个团队,这玩意谁能搞定?如果没有你在,就没人能接,那就是你给公司挖了一个坑,然后公司还不能把你怎么样,这不就是坑公司? + +所以很多时候讨论语言选择是一个最无聊的话题,因为语言的选择,事实上不是单一爱好的问题,是我站在公司整个层面上,包括人才储备上,做的综合判断,不是说我觉得这门语言多牛,这不重要,每门语言都有自己很适合的地方,否则它为什么活着呢? + +极客时间:你这么重视对公司、团队方面的考虑和权衡,在技术人里不是太常见,你是怎么意识到这一点的?是你在后面的项目经历里慢慢体会的? + +毕玄:这一点其实是我后面一个老板对我的影响,七公,就现在淘特的负责人。 + +那一年我被提名P8P9的晋升,写完PPT他帮我看了一下,然后他说你这个PPT最大的问题就是没有讲清楚你做这个事情的意义是什么,就是技术的出发点。 + +他说我技术层面当然没有问题,都讲的很好,但越高级别的人,越需要回答的问题是你为什么要干这件事情,而不是你怎么干这件事情,以及怎么解决里面各种各样的技术难题,这些是偏执行层面的,当然也需要,因为一家公司肯定有很多技术上非常难的事情需要人解决,但是更需要的是,有人去思考这家公司到底要做什么?为什么要做这件事?这是最大挑战。 + +他那次跟我聊了之后,我一定程度上开窍了,后来我所有的技术规划都是以这为出发点。很多技术的人可能很难接受这个,但这就是你走向成熟化的必然。因为我们也能看到有些技术很神的,可能有纯粹的技术立项,这种确实很好,但关键是在中国的生存环境比较困难。 + + + +  + +水友讨论区 + +到这里今天的讨论就暂时结束了。理解了毕玄做选择的出发点,我们再回看之前他的所有转岗决定,背后的理由是清晰且一致的。 + +在一个要求非常高的系统,你的敌人叫做墨菲定律,开发者需要具备更高的代码掌握程度,而且现代系统,因为业务复杂度和商业竞争度在不断提升,对开发者的要求也越来越高。 + +但对技术人来说,最致命的障碍常常并不因为外界,而是因为自己,在技术选型上,想清楚你的出发点是最重要的,一名成熟的技术人需要从对公司、对客户/用户,以及对团队的各角度,想清楚自己做事的意义是什么。这是毕玄做事且能成事的底层逻辑。 + +不知道你对今天讨论的哪个部分感兴趣,我还是列了几个讨论的话题: + + +AWS说的Modern Application,作为身处其中的开发者,你有哪些感受呢?回顾自己做过的系统,要求是什么样的?你觉得挑战是什么? +毕玄做事,他认为重要的是想清楚对公司/客户/用户来讲到底有没有帮助,是不是一个很好的长期发展选择。你做事的出发点是什么呢?关于技术人的情怀问题,你见过哪些坑?你有给团队/公司挖过坑吗? + + +欢迎留言交流,聊聊自己当年(或者现在)炫技的那些事。 + +下一讲我们聊一聊到底该如何思考技术演进方向,下一讲见。 + +拓展阅读 + +之前毕玄有自曝过自己因为技术情怀犯过一些错误:- +技术人员的情结- +我在系统设计上犯过的14个错 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/文化:你所在的团队,有多少人敢讲真话?.md b/专栏/超级访谈:对话毕玄/文化:你所在的团队,有多少人敢讲真话?.md new file mode 100644 index 0000000..4b41a62 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/文化:你所在的团队,有多少人敢讲真话?.md @@ -0,0 +1,326 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 文化:你所在的团队,有多少人敢讲真话? + +你好,我是叶芊。- + - +今天我们要讨论的话题是文化,说“文化”这个词你可能会觉得很虚,那我们换个词——“做事风格”,这就和你们团队平时的协作习惯密切相关了。- + - +做事风格,往小了讲,会影响团队成员对开会的认知、各成员的工作氛围,往大了讲会影响各部门间的协作成本,甚至公司人才培养的模式,乃至公司最根本的价值观。- + - +如果你所在的团队或者公司,也在这些方面有疑问,那让我们一起进入今天的专题对谈,看看毕玄这个热衷解问题的人,会给出什么解法? + + + - +极客时间:常听你讲蚂蚁的人才模式更好,为什么? + +毕玄:核心是文化不一样,蚂蚁是规划文化,淘宝是赛马文化,这就决定了两家公司有非常大的风格差异。淘宝的人很难适应蚂蚁,可能勉强能适应,但也不大适应,蚂蚁的人就更难适应淘宝。 + +像淘宝的人喜欢折腾一些乱七八糟的事情,当然这几年更难,但以前会有人真的搞赛马,这就是淘宝的风格,跟腾讯类似,但腾讯不大一样的是它顶上那些是不大动的。 + +极客时间:文化背后是什么?像这种做事风格应该也不是公司一开始就定了大家要这样。 + +毕玄:我认为跟整个商业竞争有很大关系,就是在你的商业竞争面上,你到底认为技术在里面是什么地位?如果技术只是个支撑,那就一点都不重要了,如果技术就是你的竞争力,那你的看法就会变,会觉得技术人才的培养、存留都很重要。像蚂蚁更规划一些,也有是金融行业的原因。 + +极客时间:在蚂蚁,技术就是业务竞争力,所以像架构师就会培养? + +毕玄:蚂蚁会刻意培养,他会有一个盘点,比如说你现在是负责这个系统的架构师,他如果觉得你有潜质负责整个业务板块,过一两年就会把你调岗,直接调去另外一个系统的团队里,这样你不就两个系统都熟了。 + +蚂蚁很多大架构师都是这样培养出来的,有目的地调整人员组织结构。淘宝是没有组织培养的,所以这个事情就乱套了,只能靠项目,但靠项目就很难,大家都是被临时弄上去的,就会硬做。 + +极客时间:直接从组织结构上调岗吗? + +毕玄:否则你没有机会的,很多人就是在一个系统干到死,他根本没有机会了解另外一个系统是怎么回事,他去哪了解?所以我们觉得,架构师这玩意儿是个组织问题。 + +淘宝一致认为人才是靠野蛮生长出来的,你能成长就能成长起来,不能成长他觉得培养了你也成长不出来。 + +这两种文化我们内部也讨论过,淘宝最终能长出来的人,当然确实不错,因为他是从一个非常复杂的环境出来的。这在公司前期还可以,因为野蛮生长总会长出来几个,只是不够多,但公司变大之后,业务多元化,要横向扩张,你面临的问题就需要一个厚的人才梯队池来解决,如果你不特意培养就没有了,公司就会断岗。 + +阿里后来典型就是这个问题,我们想了一件什么事情,想找人派过去解决问题,就发现有点少。 + +极客时间:什么时候开始发现有人才问题的? + +毕玄:后来选Leader都有这个问题,可能2017年就出现了,比如说要成立一个新的团队,想找一个什么样的人,我们一捞最后发现能选的就这几个人,而且他们进来这么多年,级别居然还没变,这就见鬼了。因为这些人很重要,但是又都没有得到晋升。 + +  + + + +  + +极客时间:对于人才问题,当时你们怎么办的?也没有再去招人或者后面培养起来几个吗? + +毕玄:没有,这种我们没得选,说白了,我们还是会选信任的,都是这样。 + +比如说架构师,如果已经有这样的架构师了,公司绝对不会冒险,他会选择我最信任的一帮人,一直做,除非这帮人全走。这也是我们一直反思的,阿里的架构师体系是一个巨大问题。 + +走前我做的最后一个大项目就是阿里的集团上云,但我还是项目的总架构师,说实话我并不是很需要这个Title,而且我以前已经做过类似的大系统改造了,做这件事对我没有多大成长意义,所以何必呢,不如让一个有潜质的新人来承担这个位置。但是最后发现还是很难。 + +极客时间:很难是因为什么,可以具体聊下吗? + +毕玄:因为一个大系统的架构,不是光有你,你下面还有很多系统的架构师,关键是这些人听不听你的。 + +之前我们聊架构师的判断都是主观的,这个问题在当前阶段,我讲的所有方法,另外一个架构师可能不这么认为,他觉得我们应该用另外一个更漂亮的方法,但我可能偏务实,这样双方很容易产生冲突。所以这个大架构师能不能控制一批架构师的想法,让他们听你的,在大公司是一个巨大的挑战。 + +一个新人上来,别人觉得你又没做过,不靠谱。像我是老人,我干过,你们都没干过有什么权利跟我说,我就能说:“我承认你说的挺好,但是你必须按我说的干。” + +所以架构师的培养为什么很难,尤其大架构师,因为他是以战培养出来的。 + +极客时间:但战又很少? + +毕玄:对。而且有战的时候,公司在有选择的情况下不会故意培养,其实就把他用废了,因为他自己觉得已经没有任何意义了,而且干的很苦。这一点是我们以前很反对的。 + +我们的想法是有选择的情况下,公司你也应该故意培养。有选择就是比如一个新人,一个老人,尽量用新人,不能说你觉得他以前干过,就继续让他干,关键他也不一定想干。 + +像阿里双十一的大队长,这活都多少年了,还让他继续干,你说对他有什么成长意义,其实没有,对他来讲也不是个荣誉,他觉得没有任何意思。但公司觉得这个项目很重要,你干过而且成功了,所以继续让你干,是靠谱的。但这样新人一点机会都不会有,永远不会有机会。 + +极客时间:那你们当时怎么办的,有没有什么解法? + +毕玄:我们当时就提议,公司其实不用纠结,你任命谁架构师,下面的人一定会听他的,因为我已经任命了这个人,下面就算叫的再凶,被任命的人也可以非常强势,实在不行就说我是大队长,你们就得听我的。 + +极客时间:这样公司承担的风险更大,能给批吗? + +毕玄:换新人无非就是有点风险的,但是也能干,老人的好处无非是你可以相对控制风险,然后做好整件事情。 + +但这也有方法的,你可以把老人放在后面。我以前就跟他们说,我不做这个总架构师,但我可以在背后支持,前面一定要推出一个新人,我可以坐在那张桌子上在背后支持他,只要是他说的,我都说都是对的。这个人只要这一战不会出太大的问题,其实他就起来了,成为了第二个。 + +双十一大队长阿里不也换了好几轮,后面一直换,也没出什么大事。因为又不是真的你一个人能决定的,除非你在这个圈子里已经有极强的信任感了,你做的决定就是决定,但多数新人上去做的决定,很多时候根本不是决定,下面要经过N轮讨论的,所以你完全不用害怕。 + +极客时间:但集团上云还是用的你,说明这个方案还是没过。那阿里后来架构师培养这个问题怎么办的? + +毕玄:阿里去年也想像蚂蚁一样搞,但关键问题是没法调,你觉得这个人有潜质成为大架构师,想让他今年做这个业务,明年做那个,下面的人会告诉你不行,这个人不能被调走。 + +极客时间:业务架构师的调换为什么很难?有业务风险? + +毕玄:其实可以调,纯粹就是因为Leader,好不容易有个人能帮我顶住事,你把这个人给我调走了,那不就我自己顶了?但蚂蚁因为从上到下都贯穿了这个思想,他们是没有问题的。 + +极客时间:到现在也没有一点改变? + +毕玄:在尝试,不过感觉很难,所以我说这是文化,一家公司的文化和基因是很难改变的。 + + + + + +  + +极客时间:你现在新公司的文化,看你朋友圈讲希望成为一家“透明平等”、“大神型工作氛围”的公司,你是怎么想要定这几个? + +毕玄:这是我离开阿里最强的几点感受,就变成了我们公司的核心价值观,因为我觉得这就是关键。 + +极客时间:先说透明平等,你定这条是想要什么? + +毕玄:这是奠定一家公司能不能做好决策,能不能让专业的人发挥价值。 + +阿里以前坐一张桌子上开会的时候,没有人关注你是什么级别的高管,这跟我一毛钱关系没有,我只表达我的观点,因为我相信至少在我的领域,我比你更专业,所以我应该表达我想说的。至于你最后怎么做决定,只要你讲清楚为什么就可以,毕竟你是负责那个人,又不是我。 + +如果谁一进去就跟别人说要这么干,我们肯定会问一句为什么?如果他不讲清楚,我们是绝对不会跟着干的,这就是以前。 + +这个环境就非常良好,因为高层做决定要考虑非常多因素,你应该把你自己在专业角度上的看法全部告诉他,不是说他就要听你的,他会自己再综合判断,但他会讲他的理由和逻辑,你可以不认他的决定,但你至少认他为什么这么做决定。 + +以前因为有这种氛围,我们都说阿里真的是一家非常牛的公司,因为做到这点非常难。 + +极客时间:这种文化是怎么形成的?很难是因为常常老板自己先过不了这关? + +毕玄:很多老板会觉得我简直被你们挑战到极致。因为我以前给管理者培训,很多外企的人都问阿里为什么下属能挑战主管?我们说不就是这样的吗,大家本来是平等的,而且你下属在那个领域一定比你专业,他如果不比你专业,不很诡异吗? + +极客时间:但有些Leader可能会觉得我的经验比下属丰富,所以自己比下属更专业呢? + +毕玄:那就没法弄。但如果一个公司非常透明平等,其实,高层做决策的准确性会高很多,因为你听到了来自各方真实的声音,但如果环境不透明平等,这个桌子就会变。 + +阿里后来有些人离开和这个是有些关系的。很多事情,你会觉得还是不要说好了,老板说什么就是什么,因为作为专业的人,你会觉得反正说了也从来没什么用,那干嘛说?有什么意义?这样,老板会越来越觉得我太明智、太聪明了,说什么都是对的,人都这样,肯定希望听到的是好话,我说一句你们每个人挑战我十句,很不爽的。但事实证明那样才是好的。 + +你看阿里近两年不断讲,要保持自己是一家透明、平等的公司。但不断讲的时候,或多或少说明肯定已经在这个地方出现了问题,但光讲是很难改变的。 + +以前的管理层,在宣布一个决定的时候,压力很大,很多人会挑战你很多问题,你得去思考怎么回答,总不能说我就这么决定,这样老板自己也会觉得很丢人,所以就会准备得非常充分来做宣讲。现在就没有太多人挑战,很好,你讲的都对。 + +极客时间:准备不充分然后被人挑战的,你经历过吗?如果想塑造这种文化你觉得核心要解决的问题是什么? + +毕玄:这很正常,我就告诉他我确实没想到这点。 + +这本质其实是心态问题,你能接受,就没有什么,被挑战了那又怎么样?很多人可能觉得作为Leader一定要什么都懂,或者是觉得损失了权威什么的。其实没有必要。 + +透明、平等阿里这两年做的肯定没有以前好了,不过内网还是保持了一个比较好的环境,阿里的内网应该是各家做得最好的。什么都敢讲,而且我们当年比现在还敢讲,新人看不到太多很挑衅的发帖,老人很挑衅的。 + +极客时间:大家都是实名,能有多挑衅? + +毕玄:以前马云任命博士(王坚)做集团CTO,马云发一个贴,下面一群人回帖反对,你想相当于是集团最大的头,说我要任命一个人,然后你们下面的人都跳出来说我不同意。这简直了,这就是以前的阿里,现在哪有多少人敢? + +  + + + +  + +极客时间:一个公司的价值观变化,比如说阿里,你觉得是因为什么? + +毕玄:很大的原因是加入这家公司的人的多元化。以前淘宝老人多,大家都这风格,我根本不在乎你是谁。但我们后来分析可能跟“安全感”有很大关系。 + +老人为什么敢?是因为老人觉得自己很安全,因为我是对事,又不是对你这个人怎么样,所以就算我对你这样了,我相信我不可能因为这个被干掉,或者你敢对我有什么绩效上的不公平等等,我们相信绝对不会发生。 + +但新人不敢,他可能以前在各种各样的公司,这么干一定会被人穿小鞋,那他就会先观察一下。再加上后来加入阿里的很多人,可能目的跟我们当年不一样,当年我也说不上,但我们肯定不是说来就是为了混两年拿股票,但新人有些是这样的,来镀金,股票能拿两年最好,拿到我就可以走了,但如果能再混一年多拿点,那更好。他当然不想挑战老板了,能让我混过去就可以了。 + +极客时间:所以当公司到一定规模,价值观或者做事的文化很容易滑坡。 + +毕玄:对,我也觉得,因为规模到后面,吸纳的人太多了,很难去严格挑,不现实。 + +尽管阿里说我们要找同路人,但问题是你要的人太多了,能招进来都行,管他同不同路。像阿里后来自己也改了,说能同走一段的也行。 + +现在大公司都这样,头条据说会稍微好一点,但我觉得是他们还没上市,阿里上市后有比较明显的分水岭,因为上市后股票能兑现,就意味着有钱了,大家加入的目标会有些改变。所以头条现在的状态应该最好,所有人目标完全一致,就是冲刺上市,但上完了估计也会碰到各种新的问题。 + +极客时间:那这个问题怎么解? + +毕玄:我觉得很难解决。这种大环境一定会出现,可能就看你的比例。 + +比如说作为团队Leader,在你的团队里,如果敢讲真话的人多,大家发现能挑战你而且好像也没被怎么样,那这个氛围就会被养成,大家也会被慢慢带过来。最怕敢讲真话的人变少,那就完蛋了,最后大家就一定都不讲了。 + +这是个整体文化,如果大家都这风格,就不会觉得这有什么问题。所以最大的老板最关键,因为最大的老板如果不大能被挑战,那你看着吧,所有人立刻就是一个风向。 + +因为阿里高层他们也不是不知道这些问题,但关键他们也觉得不好解。所有人都不知道该怎么解,这个问题涉及人,太复杂了。很多大公司到了一定规模,就跟人类的协作规模问题一样,你协作规模能突破多大。 + +我们也希望看到一家更大规模的科技类型公司,因为现在全球的大部分科技公司都是这个规模,没有谁突破,一旦突破我们估计都不怕了。所以如果一家公司能透明、平等,那大家真的应该珍惜。 + +  + + + +  + +极客时间:你公司的价值观除了“平等透明”,还有一个“大神型”工作氛围,这是想解决什么问题? + +毕玄:这句话其实是逍遥子讲的,他希望阿里也变成这样,大神型组织,而不是大咖型。 + +大咖型是指因为你在这个位置,所以你说的都对。阿里有些团队这个现象特别突出,因为经常组织结构调整,那个人以前压根没干过这事,他调到那儿了,就变成了权威。像我以前不做音视频,后来成了视频云的Leader,我就成了音视频技术晋升委员会P8P9的评委,我说这合理吗,难道不应该找一个专业的人来吗?不能因为我是Leader所以我就专业了。这就是典型。 + +大神型是指因为他专业,所以应该听他的意见。当然还是一样,最后的决策是另外一回事,是个综合判断,但我们要尊重大神,他更知道你讲的那个天花板是真的还是假的。 + +极客时间:但很多公司可能就是那个位置上的人说了算。 + +毕玄:因为他在那个位置,就会有很强的信心,我调到这个位置,我就比你们都懂,就会觉得下面专业的人提的都不靠谱。 + +极客时间:或者我必须得表现的比你们都懂? + +毕玄:也有可能,那就是他没有安全感,特别在乎自己的权威。这种对一家公司,尤其技术公司就非常要命。 + +极客时间:为了维护公司这样透明、平等、大神的文化,你有没有什么具体的措施? + +毕玄:只能看平时做事了。像我现在的公司会强调如果有人让我们做不了透明、平等,不管你是谁,不管什么原因,都会开掉。 + +其实这取决于公司怎么捍卫你的价值观。为什么大家都觉得阿里以前是一家特别有价值观的公司,说白了就是下手够狠,只要出现就开掉,那所有人就懂了,有些东西真的是不能碰,碰了真的会死,不管是谁,不管是什么职位。 + +极客时间:公司大了之后,直接开掉也可以吗? + +毕玄:也可以,只是很多人觉得好像缺了谁会发生什么。其实不会的。 + +但后来的人就不大敢,会有各种考虑,会不会对我的业务短期有影响等等各种各样的,所以有些事情发生,就会导致整体价值观的作用大幅下降。 + +极客时间:什么时候感觉价值观的作用下降了?一些奖罚? + +毕玄:对,这种类似的,因为大家都会关注。一个公司想守卫文化要付出很大代价,因为更多是靠惩罚来体现的,光喊大家肯定是不信的,只有处罚,而且处罚一定要够狠。所以文化价值观其实是公司的底线,如果不是底线,就不要把它列进去。 + +  + + + +  + +极客时间:这种下狠手做处罚,是你之前说的“做正确的事”吗? + +毕玄:那不一样。“正确地做事”和“做正确的事”大公司会比较严重。 + +比如说我是一个业务团队,为这个业务要做什么事情,但可能像财务、法务、安全团队等等会上来就告诉你,这个事有风险,最好不要干,这叫他们在“正确地做事”,因为站在他的角度他是对的。 + +但这不是“做正确的事”,为什么大公司开会都很痛苦?大家觉得根本不在一条船?很多团队纯粹站在自己的角度,而且说的还没有办法被反驳,所以我们做业务的都很恼火,你老要面对一堆不背业务责任的横向部门给你提的建议,但是他们说的确实我也承认都是对的,但这就很恶心了。 + +极客时间:我也不是搞你心态,我是真的在给你提我非常专业的建议。 + +毕玄:对,站在他专业的角度,他觉得你这样做对公司会造成风险,所以他是正确的。 + +但我作为业务方,承担了所有业务压力和责任,说实话,你们这些团队不是来上就告诉我不能干,是告诉我站在你的专业角度,你觉得怎样能干。比如说有法务风险,你要讲有什么方法避免或者怎么变成合规,但他怕这样说了他有风险。 + +但你上来就告诉我什么都不能干,那我怎么让业务做得更好,我做不了,要不你们来干业务?我要的是解法,这才叫做正确的事,大家是在一条船上的。 + +极客时间:这个问题怎么处理? + +毕玄:处理不了。有些人的风格是做正确的事,只要这个事对公司有帮助就做,不在乎比如能获得什么利益,或者有什么风险,这都不重要,大家的目标是一致的。 + +但有些人的目标是,我能很好地在公司活下去就可以了,至于其他的我不关心。从人性上来讲,你是可以理解这些部门为什么要这样的,他是我不做错就行了。 + +极客时间:所有大公司都一定会出现的?还是说因为公司文化? + +毕玄:我觉得大公司都会,但跟透明、平等一样,关键看怎么保持比例。 + + + + + +  + +极客时间:除了做事的风格,我还有一点比较好奇,开会,你是怎么看的? + +毕玄:开会是没有办法的,做Leader我从头到尾都在开会,根本不再干别的,这很正常,因为很多横向的团队协作都需要你,调动资源各方面的。 + +极客时间:有哪些你不参加吗?比如不同类型的会,信息同步的、问题讨论的、进度讨论的。 + +毕玄:我一般很少开纯粹进度跟进这种没太大意义的会,太浪费大家时间了,你的进展我有很多办法知道的,不管在系统上,还是一个简单的周报,其实你写几句就可以了,我就知道大概情况了。 + +极客时间:那有例会吗? + +毕玄:我们一般双周会开周会,通常一个多小时,更多是用来跟大家分享一些感受,或者同步大家不知道的公司上层的一些信息,如果有问题,大家就碰一下,如果没有问题就不要说,就结束了。 + +我们开会最重要的基调,就是为了解决问题,还有同步信息。所以周会很多人很轻松,你不用汇报进度,当然有可能我会直接问你,一种是我觉得很有问题的,一种是想借机夸一下的。 + +极客时间:那你们会有讨论方向或者规划的这种会吗? + +毕玄:但说实话我觉得很多都没有意义,因为不存在说什么几十个人讨论一个问题,这就不用讨论了,因为不会有任何结果的。 + +我们可能刚开始的规划会有讨论,但规划这个东西在开会之前,主导的人就是Leader他自己,肯定是有想法的,只是想让大家充分发表一下而已,所以这种我们叫同步会,就是我来告诉你我的决定和理由是什么,你们知道就行了,因为还是需要团队都知道今年核心的目标是什么、为什么要这么做,这种其实要花一些时间。 + +极客时间:所以是开之前基本已经有一个决定了,不会什么都没有然后所有人去讨论问题? + +毕玄:如果不解决问题,这种会就不要开,同步信息有很多方法,群里发一下就好了,干嘛要在这里。 + +以前逍遥子很搞笑的,因为给他汇报多数不是同步信息,肯定是希望他帮忙解决个什么问题,所以老逍说看其他人的PPT,他都是翻到最后两页,因为最后两页才是关键,前面全都是废话。 + +所以Leader是干嘛的?Leader是帮你解决问题的,其实他是你的资源,有些人向上管理做得很好,能把Leader用得很惨,这种人非常牛,是好下属。我以前的下属都很擅长这个,因为像进度和执行都是他们应该把握好的,我除了能解决问题还能干啥,我也干不了啥了。 + +极客时间:你主要会提供哪些资源? + +毕玄:很多都是偏横向的团队协调,另外有些可能是他自己很难判断到底要往哪走,比如一个决定他要怎么做,这种你告诉他你自己会怎么做就好,剩下他可以自己做。 + +大家对你的诉求肯定就是帮我解决问题,如果你解决不了,你就说解决不了,其实没什么的,很多Leader不愿意说,觉得我一定得帮助他解决,但很多确实不是你能解决的,因为大公司有些问题就是很难解决,很尴尬。 + + + +  + +水友讨论区 + +到这里关于“做事风格”的主题讨论就结束了,小话题比较多,所以聊的也稍微散一点,我们简单总结一下。 + + +人才培养:本质是从商业竞争面上看专业人才是什么地位?支撑VS竞争力 +工作氛围:公司能不能做好决策的基石?平等透明VS老板都对 +地位态度:专业的人才能不能发挥价值?大神型VS大咖型 +团队协作:各团队一起能不能做成事关键看比例?正确地做事VS做正确的事 +开会风格:开会是不可避免的但开会的基调是什么?讨论/同步VS解决问题 + + +至于解决方案,很可惜,涉及人的很多问题可能就是没有解法,关键看公司的想法,从公司对一些问题的具体做法里分析一下站在哪一方。 + +毕玄后来也在朋友圈说:“规则对每个人的影响差别可能很大,所以要统一很难,只能是宏观层面的ROI选择,创业了的第一感受如果不计代价,很多决定并不难做,但事实不是这样。” + +不知道你对今天对谈的哪个部分比较有感触,欢迎在留言区留言参与讨论。 + +不过别灰心,下一讲我们会聊一聊“架构师”这个很多程序员向往的职业,而且会有可实操的方法论哦,下一讲见。 + +拓展阅读 + +当时毕玄总结自己的2021年写了一次复盘,和公司文化相关:2021最大的几点感受 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/方向:技术演进,到底该怎么思考未来?.md b/专栏/超级访谈:对话毕玄/方向:技术演进,到底该怎么思考未来?.md new file mode 100644 index 0000000..d1f12a5 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/方向:技术演进,到底该怎么思考未来?.md @@ -0,0 +1,244 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 方向:技术演进,到底该怎么思考未来? + +你好,我是叶芊。- + - +欢迎来到技术视野的研讨主题,相信你一定非常期待,毕竟在当年阿里技术牛人辈出的时候,毕玄都是公认的技术方向感非常好,做4个方向最少能命中3个的那种。- + - +其实他自己也非常清楚这是他的个人优势,后来写离职复盘的时候他说:“我认为程序员的价值关键体现在作品上,作品里很重要的一点是对业务、技术趋势的判断,希望作为程序员的大伙,都能有机会打造一款世界级的作品,去为技术圈的发展做出贡献。”- + - +那他到底是怎么培养技术敏锐度的?话不多说,我们开始今天的对谈。 + + + - +极客时间:你对技术方向的判断力,经过了很多项目的检验,很多人公认你的方向感好。前面聊做决策的时候,你也提到Leader需要赌未来,带团队你也会团队成员讲清楚未来的方向和规划。这种判断力怎么形成的?或者说有没有什么? + +毕玄:方法论? + +极客时间:对,因为大家都喜欢,也都期待。 + +毕玄:但真的没有方法论。 + +极客时间:或者说技术敏感度? + +毕玄:我们觉得真的很难培养。以前我们就想过怎么培养大家对一个事情的判断,但这是个走势问题,未来可难说了,谁也判断不了。我们后来总结最重要的可能是你要去想。 + +很多人其实从来没有想过未来的走向,就像技术,未来会走向什么?一般觉得什么最火就是了。比如说从SOA,到服务化、微服务,到Service Mesh,这是个历程,我们以前经常问很多人:你对这个历程怎么看?每个阶段到底有什么区别? + +我问过很多人,自己也被问得很尬,因为回答不了。比如请问我们07年到09年做的服务化,跟现在讲的微服务到底区别在哪?没有人回答的出来,那说明这根本是个瞎扯的词,不就是概念嘛,为了卖个概念忽悠人。包括Service Mesh,很多人各有各的看法,我觉得是什么看法不重要,我们最怕的是没有看法的人,但是你追问他为什么好?如果回答因为很火,就简直是瞎扯。 + +极客时间:既然没有培养技术方向判断力的方法论,那别人说一个技术是颠覆,判断他瞎扯和不是瞎扯,你的方法是什么? + +毕玄:关键依旧是那一点,你觉得这个新东西的价值到底是什么?相比以前带来了什么变化? + +极客时间:新东西的价值怎么分析呢?每个新东西出来,看它技术演进背后的那个推动力是不是一致的? + +毕玄:对,以前在中间件的时候大家聊过这个话题,纯粹的技术演进到底怎么思考未来,以史为鉴是最重要的,但每个都不一样。 + +极客时间:是每个公司不一样,还是说每个技术的推动力不一样?可以举个具体例子说下吗? + +毕玄:跟公司关系不大,主要看这个技术怎么往前走。 + +我们经常举例大数据,因为大数据特别明显,从Hadoop,到Spark,到Flink,基本可以认为是三代。最关键的是你去看,为什么大数据会有这三代的演进?是因为它们都解决了用户对这玩意儿的核心诉求,如果这个诉求归拢到一起还是一样的,那就好办了。那用户对这几个大数据软件的核心诉求是什么? + +最核心的诉求是算更快。做一个大数据分析,想越快拿到结果越好,这就是我们的诉求。 + +Hadoop解决了什么问题?Hadoop解决了0到1的问题,以前没有一个大数据的东西,Google发了一篇论文,然后Hadoop做了一个开源版的实现有了0到1,所以它一定会很成功。当然那个时候也不只有它,却是它成功了,这就有很多因素,比如说最重要的是大厂的支持,因为Hadoop是Facebook等等大力去推,这是不一样的。 + +所以Hadoop解决能算出来,但有点慢;Spark解决的是我通过架构改造,让你整个具备了算更快的能力;Flink就更快,因为已经实时化了。你看它的技术演进,其实就是围绕了怎么解决掉客户的痛点,然后通过技术层面的架构改造来完成这个事情,当然这其中也有技术下面的基础设施成熟化的背景。 + +包括中间件,回顾一下这些年中间件的发展,过去为什么会有几次迭代?几次迭代解决的都是什么问题?那我们认为肯定都是解决了一个痛点。 + +痛点是要挖出来的,你围绕那个痛点去想,技术现在有没有可能把这个痛点往前推进一代产生很大的改变,你的新东西要解得更好,而不是小修小补。如果有,那我认,那就是技术真正的变革,而不是造了个概念。 + +很多人觉得自己做的是新一代,我们就问相比上一代,用户在这一代里碰到的什么问题,你帮他彻底解掉了? + +极客时间:第一步想清楚用户的问题是什么,第二步想具体怎么去解这个问题,解决方案感觉也很难想。 + +毕玄:技术演进本来就需要很多年,因为技术的下一代是很难想出来的,可能需要时机。数据库从关系到分布式走了多少年,理论上方向提了无数年,但直到现在才算是逐渐有了东西。现在数据库又在说要从OLTP、OLAP走向HTAP,这是更新一代,因为这也是用户的诉求,他们在不断往前推进。 + +以前很多团队总纠结要不要硬造一个概念说我们现在做的是新一代,但是如果不到时机,其实没关系,你就维护着,只是很多人觉得维护没有什么价值。但你们造概念,公司更不会认,外面有可能被你忽悠。 + +极客时间:但很多公司都会强调,技术人员要具备一定的影响力。 + +毕玄:千万不要为了做影响力而做影响力。反正在我的评判里,这种我是绝对不会认的。我先只看你对公司业务的影响,然后如果你的方案确实在这个技术领域具备领先性,我才认为你有影响力。 + +因为影响力是很容易培养的,你有了结果,也有贡献,做的方法也具备引领性,说实话,剩下的只要炒作包装一下就可以,如果你的公司影响力足够大,你想成为什么都可以,但前提都是你自己得有足够的实力,等你做到了,影响力什么的都是很自然的过程。 + + + + + +  + +极客时间:怎么判断是彻底解了还是小修小补呢?既然每个技术背后的推动力不一样,我们再看几个技术。比如说服务化、微服务,这个核心诉求到底是什么?开始阿里做服务化是为了去解什么问题? + +毕玄:以前做服务化,核心要解的问题有两个。 + +第一,要解决系统的水平伸缩能力的问题,因为服务化了以后,说白了你的每个应用,每个系统,要承担的责任变少了,所以伸缩性就能变得很强。 + +第二个服务化的核心问题其实是研发协作的问题。以前100人开发一个系统,大家都没有分工的,我接了一个需求,要改哪我就从头到尾全改,这个时候有可能会出现冲突,因为可能每个人都在改同一个地方,大家合并代码的时候就非常痛苦,效率很低。 + +服务化有分工了,你就改这块儿,他就改那块儿,当然这也增加了协作成本,但是它毕竟能让100人甚至上千人的研发团队可以并行做下去,这一点也跟AWS说的Modern Application有关,现代软件变得更复杂了,做任何软件上来就是一帮人,你必须考虑到一帮人协作的问题。 + +极客时间:“协作”这一点感觉比较少讲。 + +毕玄:我们当年其实也没有想到是用来解这个问题的。这是Jeff Dean在斯坦福讲Google技术演进的时候说的,Google做服务化的核心是为了具备千人以上的研发协作能力,这看问题的角度就比我们强太多。 + +当时我们主要想的前一个,而且我们还是被逼的,因为不做服务化,就加不了机器,业务就崩盘了。 + +虽然当时100多人研发同一个系统已经很痛苦了,但是我们没有想到服务化其实是用来解协作的,是做完才发现解掉了,后来看到Google说自己做服务化的核心目标是协作,我们觉得哇你讲得有道理,我们也加上了这一句(笑)。 + +你看,服务化当年最重要是为了解这些问题,那微服务,请问跟服务化相比到底解了什么问题? + +服务化现在留下的核心问题是粒度,一个服务的粒度是主观的,架构想切成多大粒度就多大粒度,但这是有问题的;还有服务里面,比如说一个应用背后依赖了100个服务,到底哪些服务是核心路径是必须依赖的?现在都是靠人来控制的。 + +除非你说服务化的新一代可以解决这两个问题,那才是革命性的。但现在做不到,说什么微服务,他们就编吧,可能觉得服务化这么多年没发展也不大好,总得编个新的。 + +极客时间:微服务这个概念刚出来的时候不是很好理解,之前有人开玩笑解释为什么要做微服务,一般就是因为公司人有点冗余了。 + +毕玄:所以以前很多小公司问要不要做,我们都说你做个啥,一个系统多爽,开发效率最高,分布式会带来很多问题,包括对研发能力都是巨大的挑战。只是看起来技术不值得吹,觉得技术很烂什么的,当然这不重要,对业务来讲这都不重要。有些人就会这样,想挑战技术难度,觉得这种才叫复杂度,其实不是。 + +极客时间:但这种对技术难度的认知在技术团队里应该很普遍? + +毕玄:很正常,因为技术的人都是有梦想的,梦想用一个很先进的技术去做一件事情。 + +但关键是在现今阶段,对公司来讲没有任何意义,所有公司不是不关注技术创新,只是出发点都是我到底面临什么问题。所以,这个问题除非你有一个创新的解法,可以比以前解得更好,那可以的,不能说你纯粹在外面看到一个很创新的玩意儿,一定要在公司用上,这就像拿着锤子找钉子。 + +而且很多人想用新语言、新框架,因为理论上来讲,这对他的职业路径可能更友好,否则他出去不好找工作。 + +  + + + +  + +极客时间:前面聊了大数据、服务化,现在最火的是云原生,这个技术方向我们也聊一聊,你觉得技术演进到云,本质是因为什么?云的下一步是什么? + +毕玄:CloudNative其实是个分布式,无非是原来搭建分布式的方法,现在变成基于云去搭建,这是颠覆我是认的,像新公司不需要投人了,都是云帮你搞定。之前从单体到分布式的架构是个必然,但现在分布式再走向哪里就说不清楚。 + +极客时间:容器呢?这个技术演进背后的推动力是什么? + +毕玄:从虚拟化到容器化是革命性进步,这解决了什么问题?虚拟化无非解的问题就是怎么让一台机器能跑更多东西,容器化解的是我会跑更多。 + +极客时间:所以全是资源的利用? + +毕玄:对,所以我们认为,容器就是个运行单位,而这个运行单位不一定叫容器,可能有个更小的运行单位。所以阿里后来在做轻量级容器等等方向的探索,包括Serverless一定程度你可以认为就是一个更小粒度的运行单位,这叫未来,是一个技术趋势,其实它一直都在解同一个问题,只不过比前一代解得更好一点。 + +极客时间:Serverless这个新方向可以多说一点吗?对资源利用率这个问题,它彻底解决的点是什么?小厂完全不用买设备? + +毕玄:肯定不用买了,而且Serverless是真正的只有用了才要付钱。 + +比如我的业务现在没有请求量就不用付费,请求量来了就只付那一点,但现在我们是按买的机器付钱,我可能现在根本没用,但还是要付钱。如果哪家云厂商敢说能按请求量付钱,那背后是不是Serverless我们都不在乎了,能这么牛的话,成本会省非常多,那所有都颠覆了。 + +极客时间:Serverless面临的核心难题是什么? + +毕玄:主要是弹性的速度。比如Serverless我要拉起一个系统,这不是几秒就能拉得起来的,但对在线业务来讲,你不可能点一下几秒都出不来,大家能接受的都是几毫秒。所以Serverless都只是拿来做一些像计算这种后期的事情,以前可能是一些离线的事情,都是对响应时间可以忍受的。 + +Serverless也许是未来,但还比较长远,现在非常不成熟,只是个补充方案,就很尴尬。 + +极客时间:那一个技术,什么程度才算成熟? + +毕玄:所有的技术如果想颠覆,必须进入核心业务。现在没有完全基于Serverless这种新理念来构造的在线业务的技术框架,因为在线业务对稳定性是至高要求,而且它完全不可预测。 + +尽管AWS推了无数年,一直希望能推进到一个全新的技术框架,阿里以前也想推,因为Serverless对云厂商来讲并不是坏事,如果真能做到,大家肯定都来用了,但很难,从技术上来讲目前真的做不到,这是最极致的弹性。 + +极客时间:在资源利用这个方向,除了Serverless,有别的方案吗? + +毕玄:就是混部,现在混部到CPU层面了,大数据和在线其实都是CPU层面的,所以在离线的统一调度是这个阶段我们可以做的,能把利用率提高的一个很落地的方案。 + +极客时间:调度的难题是什么? + +毕玄:之后最关键的是看GPU、FPGA这些能不能做好。 + +调度这个事,其实阿里最早很多高管都有这个梦想,博士成立阿里云的时候提出云的实际表现是统一存储和统一调度,他认为云的核心是可以把所有的机器像一台机器用,这就是统一调度。这个思想最早提出的应该是Google。 + +Google很早有篇文章叫《The Datacenter as a Computer》,一个数据中心像一台机器,这效率肯定是最高的,事实上现在也还做不到。Borg的思路是把CPU尽可能当一台机器用,但内存还不行,比如A机器现在空了1G,B机器有2G,但你不能说用户现在有3G可以用,这个真做不到,因为内存有时延的问题,在单机时延非常低,一旦跨了网络,但网络已经是光速了,这是物理决定的,就很难。 + +据说Google想探索这个方向,因为Google近两年把这篇文章改了一下,叫《The Datacenters as a computer》,这简直颠覆了我们的想象。 + +极客时间:Datacenters?跨地的那种? + +毕玄:对,跨地数据中心像一台机器,我们连一个都没实现,你已经开始提下一个概念了,我们没法跟你玩。 + + + + + +  + +极客时间:好,我们前面聊了如何分析技术演进的趋势,大概判断趋势之后,大家更关心的是在这波技术浪潮里自己能做什么。比方说现在所有人都知道云是趋势,有云了,基础设施会更集中在一批人手里。 + +毕玄:对,肯定是这样。 + +极客时间:那很多人在小厂,会担心自己之后的职业发展甚至岗位会受影响,怎么办? + +毕玄:纯基础技术,像计算、存储、网络等等这种IaaS层面的,基本会收拢到几大云厂商手里,如果你要做这种技术,没有别的选择,加入这些厂商好了。 + +你看创业,很少有人说我要创业做IaaS、要做计算,不可能的,这是需要更大规模效应的生意,为什么小的云厂商越来越难,因为只有你服务器规模到了多少万台的时候,才可能实现盈利,前期投入真的太大了。 + +但在IaaS之上应该会诞生很多垂直的PaaS,也是做技术的,但这些反而不在大厂手上,全都在一些创业公司手上,这其实就是美国的现状。然后PaaS更上面是做业务的。所以看你自己想怎么走,去相应的公司就好了。真正喜欢做计算、存储、网络的同学其实很少的。 + +极客时间:很少是为什么? + +毕玄:因为计算、存储、网络是个非常非常专业的领域,都是做虚拟化、计算资源这些东西,多数人根本就没有触及到这个领域,做的最多是PaaS,比如中间件、框架这些,而且未来外面会有好多家PaaS,空间是会越来越大的,不会越来越小。 + +极客时间:就现在的行业发展进度看,基础技术逐步商业化了,很多人创业做PaaS,包括阿里很多也都拿出来卖了,这个改变是行业的进步吗?对其它公司,对其他团队同学也可以参考? + +毕玄:对团队同学的话,我觉得很难说。 + +极客时间:为什么,这样不是能更好地专注研究技术了吗?毕竟商业化了,团队的业务目标就是要不断追求自己产品的技术竞争力。 + +毕玄:我知道,但是对研发线的人来讲,很难说这是一件好事还是坏事。 + +好事确实是你做的东西不仅仅这家公司用了,还被全社会更多公司用了,成就感肯定更强。 + +但在技术层面来讲,商业化之后,很难说大家会觉得我技术上有多大更进一步的空间,因为外部更多是复杂性,而不是纯粹的技术难度问题,是你要支持的业务类型越来越多了。 + +极客时间:不是说技术要更先进? + +毕玄:复杂性也算一种先进,只是大家怎么看待而已。 + +很多人觉得我支撑更高的并发量,用了更低的成本,这才叫追求了技术,所以很多公司做晋升的时候,业务技术的人会觉得自己没法跟基础技术的人PK,大家都觉得基础技术在解基础问题,复杂度很高,我们后来说还是得区分一下,业务复杂度也是难度。 + +做业务技术的人,他不是没有技术难度,他的技术难度在业务复杂度,能不能抽象成一个非常简单的东西,去支撑非常复杂的业务。就像阿里,阿里的营销够复杂了吧,每年双十一被吐槽最多的就是营销,但你想,写营销系统代码的人是不是更痛苦?做这套系统的人,他能不能做一个很好的抽象去支撑这么复杂的营销玩法,这也不是一般人做得到的,这也叫技术难度。 + +技术难度,不能光定义成纯技术侧的东西,其实还有很多是复杂性问题、抽象问题。当然每个技术人爱好不一样,有些会觉得我不喜欢面对这种,他喜欢解决技术问题,而业务是抽象,抽象是很复杂的东西。 + + + +  + +水友讨论区 + +好,到这里今天关于技术演进方向的主题讨论就结束了。其实说实话,在和毕玄聊之前,我非常期待能不能收获一个类似灵丹妙药的方法论,虽然都说没有银弹,但人嘛总是会有幻想,今天聊完,失望多少有一点,但惊喜也意外很大。 + +失望是因为他说对一个事情走势的判断,最重要的可能是你要去想。这句话非常正确,但是完全没有用。 + +但意外惊喜是在追问的过程中,他也举例详细分享了自己的思路:所有的技术演进,其实都是围绕着用户对这个产品的核心诉求展开的,通过技术层面的架构改造,来解决用户当下的痛点。这个痛点,是你要挖出来的,你去想现在技术有没有可能把这个痛点往前推进一代,给用户提供新价值,这样才能产生很大的改变。 + +这个出发点很朴素但也很值得参考,你可以借鉴他的思路,找一个领域的技术演进过程分析一下,看看是否对自己有启发。 + +欢迎在评论区留言,也欢迎分享你自己的技术方向分析思路。下一讲我们会聊一聊带团队成事的话题,下一讲见。 + +拓展阅读 + +1. 毕玄之前就喜欢搞预测,在年末写过几篇预测文(虽然很多后来被打脸),如果感兴趣可以围观: + +来一起预测2019、预测下云、AI在2018将会发生什么变化、2017年“工程时代”即将翻篇,迎接”科技时代”、2014年预测行业变化对技术岗位的冲击、回顾2007,展望2008 + +2. 毕玄非常关注对方向的判断,关于IaaS、PaaS、SaaS和云写了几篇文章: + +IT从业者都应关注的软件行业的变化、回顾过去看IaaS的Next、回顾过去看应用PaaS的Next、云计算是新瓶装旧酒吗 + +3. 关于服务化毕玄之前也写过几篇文章,你可以从中看出他想法的变化: + +2015年服务化,你真的需要吗、2017年大部分公司并不需要微服务、2019年Serverless:云时代的软件架构核心思想、服务化的过去、现在和未来、2022年应用架构演进到头了吗? + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/架构:架构师只是个角色,不是个岗位.md b/专栏/超级访谈:对话毕玄/架构:架构师只是个角色,不是个岗位.md new file mode 100644 index 0000000..eccb837 --- /dev/null +++ b/专栏/超级访谈:对话毕玄/架构:架构师只是个角色,不是个岗位.md @@ -0,0 +1,289 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 架构:架构师只是个角色,不是个岗位 + +你好,我是叶芊。- + - +今天我们对谈的主题是——架构师。知道你是抱着收获一二三四条方法论的想法来的,没错,今天一定会有,而且非常具体,甚至会具体到你在系统设计文档里的“系统设计思想”这个小章节应该写什么。是不是非常期待?- + - +不过,在讲具体的方法论之前,有几个至关重要的概念我们先要理解清楚,会对你的职业规划有巨大帮助。不然,很多人连架构师具体是干嘛的,能做到什么、不能做到什么都不清楚,就冲着那个概念高喊“我要当架构师!”,岂不是事倍功半,甚至还有可能南辕北辙了。- + - +好,话不多说,我们马上进入对谈。 + + + - +极客时间:关于架构师,有一些概念大家觉得非常基础,但又好像似懂非懂,想看看你是怎么理解的,比如说到底什么是架构师? + +毕玄:其实就是个Title。很多公司不会有明确的架构师岗位,就是做这个事情的时候需要一个架构师,所以你兼任或者指定了一个人而已,不会说这个人他就是个架构师,很少有公司这样,因为这确实会有点问题。 + +极客时间:那架构师部门是? + +毕玄:很少有公司会有架构师部门。就算有都是很小的,虚拟的也可以。是这样,多数不是说这个人的角色就是个架构师,因为这里有个最大的问题就是架构师到底干啥。我们一直都认为架构师他不是个岗位,他只是个角色。 + +大家的岗位全部是研发工程师,只是说在这个项目或者系统里,我需要有一个人承担架构师的职责,承担这个角色来负责设计整个系统。 + +极客时间:什么叫设计系统? + +毕玄:现在前面有一堆需求,不管是来自业务方还是来自技术方本身,技术方本身就是做基础技术的那群人。 + +需求有了以后,这个需求要被翻译成一个技术的解决方案,这就是架构师要干的,通常这个人也会承担比较核心的代码编写,其他人会遵照这个设计来完成整体落地,就这个活儿。 + +极客时间:但是有些好像就负责写方案? + +毕玄:有些可能是外部来的,有些是做业务架构的,比如业务方有大需求,这个时候有人要提解决思路,那可能是另外一回事,他可能是不负责落地的,翻译完就随便做做。但这种是很容易虚的,比如你做架构的时候画框说要这么做,最后研发具体怎么做你根本就不知道,可能跟你想的千差万别,因为研发有自己的想法,这很正常。 + +挺乱的,架构师也搞不清楚到底什么是架构师,反正我们看过很多,最后还是觉得架构师不大适合实体化。 + +极客时间:在阿里,架构师会分级别吗?比如初级、中级、高级这种? + +毕玄:这对我们来讲是负责的系统大小,但这也很主观,可大可小,比如一个小的系统里包括一些模块,更大一点的可能涉及两三个系统,更大涉及上百个,就需要很多的大架构师。 + +像大架构师会是一个团队,因为会跨很多专业,总架构师一个人不可能全懂,所以他只是根据整个全貌,划定一个指导思想的框架,判断出负责每个系统的架构师在设计的时候要解决好的几个问题,剩下就是你自己的事了,解的思路有时候会给出来,但具体你要跟你自己的想法结合,做好最后的落地工作。 + +极客时间:总架构师感觉很像导演,像张艺谋导了冬奥会。 + +毕玄:你可以认为他(张)就是个架构师,他讲思路,下面人支持,至于怎么确保下面执行的过程不要跟他的想法有太大的偏差,他不是靠系统去控制的,他是靠人在打。 + +但技术上的架构师是我必须要设立一些指标,可以确保你们上线了我也能看,这就是个很好的架构师,因为多数架构师是画完框就不管了,最后谁都不知道程序员自由发挥成啥样了。 + + + + + +  + +极客时间:现在有很多架构师的课程,教怎么成长为架构师。 + +毕玄:这课简直太难讲了。外面架构师的课程讲啥的,我都很好奇,以前培训的人问要不要采购,我就说你们不要听他们瞎扯。这个话题我们都想了很久。 + +极客时间:但大家一般觉得架构师是从初期培养的,比如说最开始先了解架构工具,再讲架构理论等等。 + +毕玄:这我们从来没有,我之前都是用PPT给大家讲的,因为架构师,我觉得工具一点都不重要。架构师最重要的传达清楚你的想法就可以了,至于你用什么工具一点都不重要。 + +架构理论,他学完了会发现一点用都没有。像设计原则、设计模式这些东西也不是完全没用,还是有点用,但这就跟数学公式一样,你知道了这些公式,就是学了一个天花板,只是那可能是一个很小的点。 + +极客时间:那你觉得架构师是可以培养的吗? + +毕玄:我以前觉得很难被培养,因为我觉得方法论这个东西没什么用,但后来我觉得方法论还是有点用的,所以我在离开阿里前特意搞了一个架构师培训的班。 + +极客时间:你讲的是什么?对参加的人有什么要求吗? + +毕玄:级别我们其实不区别,班里都是承担过架构师角色的人,大小都有,因为负责系统的大小是知识面的问题。但不管你是哪一级,负责多大系统,我看过我们以前所有系统设计方法都是一样的,大的无非就更复杂一点,可能是分下去有更多人组成。 + +所以做架构,首先做事的大概思路是可以抽象出来的,比如我的方法第一步是这个,第二步、第三步是什么;另外更重要的是我可以告诉你,我在每一步犯过的错是什么。 + +因为光讲方法论,最大的问题是讲完之后没有一个人能听懂,他也不会落到自己的实践上,如果你给他讲几个血淋淋的案例,等未来他真正干这个活儿的时候,他也许忘记了方法论,但他想起了那个教训,可能偏差也不大,这就是我们觉得架构师能培养的地方。但其它的,我们觉得确实培养不了,只能实战。 + +极客时间:讲案例倒很像分享,不太像培训了。 + +毕玄:因为我们觉得想成为更大的架构师,真的不是靠培训,是知识面问题。 + +像蚂蚁做的确实好,他们会划几个等级,上面有更大的域架构师。比如说你这个领域是交易,交易里面还有订单、优惠等等,你以前可能只负责优惠这个小系统,只能讲清楚这一块儿,如果你要负责整个,必须要清楚全部的问题。这根本不是培训的活,因为每家公司业务都不一样,而且外面培训很难帮他,只能靠他自己。 + +极客时间:这是业务架构师,那技术侧的架构呢,也是一样方法吗? + +毕玄:对,技术架构师也一样,都是全貌问题,越大的系统,架构师就越难有全貌。因为没有一个人的角色是我来专门看全貌的,只有你干的时候才承受,你被任命了。 + +他们的区别只是业务架构师的需求来源于业务,技术架构师更纯粹一点,最大的挑战是想好你要解的问题是什么。因为基础技术是需要自己想的,要做什么东西,自己创造需求,业务说实话不需要你想。所以事实上做事的方法都是同一个。 + +  + + + +  + +极客时间:所以这个抽象出来的方法论是什么,是你之前在公众号上写的架构师套路? + +毕玄:对。 + + +自己做系统设计的套路:系统设计的目的->系统设计的目标->围绕目标的核心设计->围绕核心设计形成的设计原则->各子系统、模块的详细设计。 + + +极客时间:这个做架构的套路,你一般是怎么开展的? + +毕玄:首先必须有个最大的架构师,他是划定框的,决定了这次的协作关系。这个架构师,最重要的是定义目的,做一套架构之前他要告诉所有人,这次为什么要做架构级的改造? + +因为架构级改造其实不多,这是超级大的动作,所以为什么架构师不大需要,因为老做这么大的,肯定是出了挺严重的问题,像阿里发生过大的架构其实就这四轮,哪有什么那么多架构的事情。正常情况下,在我原来的框里添添改改就行了。 + +极客时间:方法论有了,我们也用你开培训班的思路,举具体的案例来理解一下其中的每一个环节,第一步是目的,比如说集团上云当时的目的是什么? + +毕玄:第一个目的是以后采购的所有机器100%必须是阿里云的,不允许自己采购了,所以我们只看预算,如果大家全部是从阿里云走的,就说明成功了;第二个像PaaS这层我们定义的目的,凡是阿里云有的产品全部不允许自研了,而且要用标准的云产品。 + +然后下面的架构师会来分解。比如PaaS因为要全部切换成云资源,所有的系统架构师来看他们的系统要搬上去到底会面临什么问题,这些问题提出来会由阿里云负责计算资源的架构师来看应该怎么解决,这就是他们应该干的。 + +所以越大的架构师,其实越是个框架,是个方向性指导,下面的人都在这个框里。但是因为你定义了一个目的,最后你就可以确保这么多人协作不会走偏。 + +极客时间:你做统一调度也是类似,第一步提一个大的方向? + +毕玄:对,像统一调度涉及基础设施、网络、大数据各种各样的团队,那我就必须说清楚我的总体思路是什么,然后你这部分最重要的是要做好什么,就可以了。你们怎么做起来的跟我没关系,你也不用告诉我,因为我也不懂。 + +大架构师就是这样的,其实是我给一帮人提了一堆的问题,然后你们去做掉。最难的是提问题,而不是解。 + +极客时间:为什么方法论的第一步和第二步会有“目的”“目标”的区别? + +毕玄:目的是我做这件事情是为了什么,但是目标是我怎么考核你做到,其实是个指标。 + +因为通常来讲,架构设计最大的问题都是你最后做完了,发现跟当初设计是有偏差的。这很正常,因为程序员会自己发挥,像画建筑设计图一样,你一开始画成这样,最后盖成什么样谁都不知道,搞不好天差地别。所以目的、目标就指标,是做系统架构师在前面要做好的。 + +极客时间:所以目标这一步,比如说会给一个数字? + +毕玄:对,比如说异地多活,核心目的就是把切流要封闭在一个单元里,所以我们一定会考核跨单元的交互,这是会在系统指标里定义清楚的,以后系统上线了,只用看这个就能知道有没有达到我们的设计目标,确保偏差不会太大。 + +极客时间:会不会有些东西用数据衡量不了? + +毕玄:那不会,肯定可以的,如果不能衡量,应该是这个架构师没有想得很清楚。 + +像我刚讲的目的、目标,其实是应该写在系统设计文档里的“系统设计思想”部分的东西。以前很多系统设计文档里都有设计思想这一小章节,但很多人都瞎写。 + +极客时间:具体要写什么呢?怎么判断有没有在瞎写? + +毕玄:有的人不管做什么系统设计,放进去的设计思想都一模一样,这就是瞎写。设计思想是你针对这一次改造,你觉得所有人需要遵循的最基本的原则是什么。 + +就像异地多活,我们给的第一设计思想指导就是数据错乱保护,你要确保你的数据不会出现错乱,在下面写各个系统实现的时候,我会告诉你大概的解决思路是什么,在你的系统里一定要把这些写上,其它都是次要的。比如要做分流,我告诉你分流的思路是什么,剩下的就是你正常解决问题而已,随便你发挥。 + +所以你写的其实就是解决问题的一个大思路,也不用写太细,没有多少人真的会那么细地去看那个文档,你要看你写的这些条,在下面架构师的文档里会不会承接好,会的话说明这真的是设计思想。 + +极客时间:目的和目标后面就是具体的设计方案了,你们在实际做项目的时候会讨论吗? + +毕玄:这个我们没有,我们几乎不做架构理论,最多是在晋升面试的时候留用。 + +极客时间:不会找其他人看一下我的方案什么的? + +毕玄:不会,你想架构师是干啥的?就是前面有个问题,要想一个解决方法而已,这是工程,多数工程师是非常擅长的,尤其是写代码优秀的工程师。 + +这个能力就跟数学解题一样,只是你解的方法可能有问题而已,但你不会这么认为,技术的人都这样,都觉得我的方法当然没问题。前面大家空对空谈,有可能最终落地真的被你说中了哪些地方确实有问题,然后返工,这很正常,因为他只有到那个阶段才能明白,原来我确实设计的有点问题。 + + + + + +  + +极客时间:所以整体看架构设计,对架构师来说哪个环节要求最高?前面写设计思想? + +毕玄:对,因为很多人根本没有想清楚。大家对系统架构的认知也是这样,前面一页需求,后面一页就是框,觉得架构师这活也挺容易干的,不就是画框嘛。 + +但最关键的是你的框为什么要画成这样?这就是你的选择,你的观点,说白了前面有个问题,你准备怎么解决,你要把这个解决思路告诉给所有人。 + +但大家都知道解决思路有很多种,那你为什么选择这种?你的思路是不是最好的?如果不是最好的,你要知道最好是什么。很多架构师是讲不出这个逻辑的。他为什么要把框画成那样,是因为别人把框画成了那样,他只是套用了一下而已。这就不叫做架构设计,这只是复制一下。所以架构师通常是会讲的人,因为架构师是需要传达的。 + +极客时间:而且前面这一步感觉是不好通过培训来学习的? + +毕玄:对,我们只能讲案例,因为选择涉及很多方面,像技术选型需要你“见过猪跑”,还有工程落地问题,你要想到底哪几个地方是一定要解决的,哪些在当前阶段是不重要的,这就是架构师的取舍。你不能说我什么都要做好,那不可能。 + +像我们做统一调度也是,当时决定做两个调度器,在线的Sigma,离线的Fuxi,然后中间有个所谓的零层来做两层的交互,后来应该是19年合在一起了就是ASI。最早我们也有纠结,要不要做一个统一调度去同时支持在线和离线。 + +很多人从技术梦想上讲应该先统一,但我坚决不同意,我就跟团队讲这不光是技术问题,还有工程问题。如果统一成一套,光做这套调度器可能就3年了,这3年里我们不会看到任何成果,但我要的是3年内看到类似Borg的混部带来的整体收益,有了收益,以后自然有机会去实现技术梦想,如果没有收益,可能公司隔2年觉得这项目不值得干,直接就不让我们干了,这很正常。 + +而且关键是这个选择不是我们做到业务效果的障碍,不做统一,也能做到这个效果,统一纯属只是好看一点而已,技术上我也认,我们的方案一看就很丑陋很妥协,但从工程落地上来讲,这就是最高效的方案,因为落地才是最重要的。 + +极客时间:你觉得落地最重要,但在技术先进度、方案效果和工程落地等等方面上,很多人对优先级的看法可能不太一样? + +毕玄:为什么很多技术很好的人在一家公司做不成事情?最大的问题是他不能接受妥协。 + +他觉得必须我做成这样,才可以。但很多东西都不是这样的,你慢慢其实可以做到那样子,但你要能接受前面恶心一点的过程,反正也是为了最终能做到,要慢慢来,不要太着急。 + +当时我们做统一调度被很多人狂喷,说你们简直了。他们就觉得你们这伙人是不是觉得太难了想绕路,就被他们鄙视,但这些我都不在乎。 + +因为我当然也知道有另外一套更好看的,但关键是要判断工程节奏,以及判断如果用这种丑陋的方案是不是做不到业务效果,如果做不到,当然就是另外一个事,关键它不是阻碍的点,对不对?那干嘛挑战难度。反正我觉得挺好的,能做成就行了,你管我什么方案,而且这个方案我会逐步变完美的。 + +  + + + +  + +极客时间:总结一下,你觉得一名优秀的架构师应该具备哪些能力? + +毕玄:对架构师来讲,第一要非常清楚知道你做的这个事情的意义,这个还是核心问题,在这个意义上你再去做技术方案,思考技术上怎么去解决掉这个问题,那你肯定会有很多种方案,这就是权衡要考虑很多方面。 + +像早期的HSF、T4我们技术选型都出了很大的问题,这其实是架构师最重要的责任,如果你的解决方法有问题,那后面就彻底完蛋了。如果你做比较顶级的方案,还有之前讲的天花板问题。 + +另外就是你对未来发展趋势的看法,比如说HSF我们认为就是为Java场景设计的,但没有想到一家公司一定会发展成多元化,一开始没留口子,后面根本没有机会,所以后来多元进来就不知道怎么搞了,这是设计问题。 + +有时候架构师还会面临团队分工问题,比如说你需要的技术,有三个团队提供,你选谁?这也是架构师要解决的,我们会看平时哪些人做整个系统更有想法,而不是只是我那部分代码怎么写,也会从团队定位和长远发展看,做这个东西对哪个团队来讲是他的长期主业。但这是非常复杂的话题,你要有自己的观点,肯定会得罪一些人,这很正常。 + +所以架构师是有很大的挑战的,有些时候要面临一些比较复杂的问题和方案。 + +极客时间:但这几点也都很难培养,只能以战养兵。 + +毕玄:纯培养我觉得太难了,只能尽量告诉他们在做每一步的时候应该考虑好什么,比如技术选型,我们会大概告诉你技术选型的思路是什么,要注意哪些点。另外也会重点强调你做一个架构设计,千万别随便写设计思想。 + +极客时间:对于职业成长路径来说,现在大家好像普遍觉得不想做架构师的程序员不是个好程序员。 + +毕玄:因为看起来好像就是架构师负责了整个系统。 + +极客时间:那程序员的最终归宿是架构师是真的吗? + +毕玄:不会,这成长路径有问题。程序员成长路径的一条就是继续做程序员,这还是得一直做下去的,另外确实有些程序员成长为了架构师,但其实他就是一个兼职,研发工程师兼架构师角色。 + +如果谁说我就只设计系统,这人很多时候没活干的,因为系统不需要老设计。万一你真的设了这个岗位,那完蛋了,因为这个人就会老想,会凭空创造出来一些需求,这就尴尬了。 + +因为架构师是设计系统结构的人,不是设计细节的人,细节才需要经常变的,所以之前专职架构师部门最后会变成一个很虚的部门,因为你又不实干,你讲了很多,但别人研发觉得你又不懂,他还是自己干一套,跟你的设计方案一毛钱关系都没有。 + + + + + +  + +极客时间:对于想走架构师这条职业路径的同学,一般架构师是怎么选出来的? + +毕玄:一般就是研发里写代码写得比较好就去做,一个项目比如说业务方有需求了,那你就牵头去处理这个需求去做规划,所以平时根本不存在这个人,只有这个项目出现的时候才会突然说任命了谁去负责横向的架构。这当然是基于大家日常对他的认可。 + +极客时间:所以即使以架构师为目标,大家也还是要从程序员正常往上走? + +毕玄:对,不会因为你是架构师就给你定什么级,当然架构师肯定会比程序员更有优势,这个我们也认。 + +因为做过架构师的人说明你可以承担更大的责任,除非是做专业技术的像虚拟化、内核,对业务团队来讲,为什么那么多做业务研发的程序员想做架构师?说白了他只是想负起一个更大责任而已。 + +多数公司的发展路径是有些程序员他能兼任架构师,他可能就变成Leader了,可以负责这个系统了。因为架构师其实已经具备技术层面、业务层面的技能了,只是欠缺的是管理、行政什么的,他离Leader只剩一步。所以架构师,确实几乎就是Leader的前提。 + +极客时间:但好多人只想当架构师,不想当Leader。 + +毕玄:我觉得很多人想成为架构师的目标是想做Leader,而不是想做架构师。当然有些人也喜欢做架构师,就是我不想承担管理责任,但是我又想负责整个系统,这种人也有,这也没错。 + +但通常来讲,作为一个技术Leader,最重要的当然是技术能力,管理能力是可以培养的,所以我们不会接受说这个团队有一个管理能力很强的人,因为他技术能力差一点所以要给他配个架构师,这简直太丢人了。所以通常就是架构师做了Leader。 + +  + +水友讨论区 + +说实话,今天聊完我对架构师有了全新的认知,之前我一直以为架构师是一个岗位,就是有个人一路打怪升级上去最终成为了一名架构师。 + +毕玄认为,架构师就是个角色,有一堆架构演进的需求来了,需要被翻译成一个技术的解决方案,有个人出来干这活,根据系统全貌划定一个指导思想的框,也承担了比较核心的代码编写,来保障最终的项目效果,这个人就是架构师。 + +如果你还想成为一名优秀的架构师,毕玄认为应该具备几大核心能力,核心是要想清楚并且讲清楚你做这次架构改造的意义,包括目的和目标,在意义上再做技术方案的思考和权衡,包括技术选型,顶级方案的天花板问题、对未来发展趋势的看法、团队分工问题等等。 + +你对架构师的理解是什么呢?你眼中的架构师能力模型是什么样的,对这些能力有怎样的排序呢?如果你有自己的架构方法论,也欢迎分享,期待在留言区见到你的思考。 + +  + +到这里,5场专题研讨会就结束了,我们从“个人成事、方向选择、团队带领、做事文化、架构修炼”这5大方面,把毕玄分析问题的思路和做事情的方法浓缩给你,不知道你收获如何。 + +当然想要把方法论化为己用,你还需要丰富的案例,所以,我们接下来将坐上时光机展开一段探索之旅,看看毕玄在二十多年的技术人生中,做过怎样的选择,又经历过怎样的波折,才总结出这5大方法论。期待和你再会,下一讲见。 + +拓展阅读 + +毕玄在自己的公众号上写了一个架构师系列,详细讲了方法论中的每个步骤,你可以参考: + +0. 聊聊系统设计的套路- +1. 系统设计之系统建设的目的- +2. 系统设计之系统建设的目标- +3. 系统设计之达成目标的核心问题- +4. 系统设计之解决核心问题的设计- +5. 系统设计之设计原则 + +另外还有几篇针对自己经历的架构反思: + +我在系统设计上犯过的14个错- +多个团队的技术方案冲突,怎么决策- +架构师画像 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话毕玄/番外:一位险些没上得了大学的青年,如何开启计算机征程.md b/专栏/超级访谈:对话毕玄/番外:一位险些没上得了大学的青年,如何开启计算机征程.md new file mode 100644 index 0000000..480a97e --- /dev/null +++ b/专栏/超级访谈:对话毕玄/番外:一位险些没上得了大学的青年,如何开启计算机征程.md @@ -0,0 +1,260 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 番外:一位险些没上得了大学的青年,如何开启计算机征程 + +你好,我是叶芊。- + - +今天这篇又名“大佬养成番外之——大学篇”。- + - +毕玄的经历,相信你已经非常熟悉了,外界写他最吸引眼球的宣传语一般都会提到他是生物系毕业的,比如“从生物系学生,到阿里传奇P10:毕玄是如何完成自我蜕变的?”- + - +虽然八卦,但不得不说,他相当丰富的大学生经历,是毕业后能顺利转岗成为一名程序员的必备要素,毕竟大佬也是需要从零开始养成的。- + - +那让我们一起把时间线拨回1998年,看看这位险些没上得了大学的青年,是怎么居然因为太无聊和电脑结缘的? + + +一条重要说明👉:今天的对话是基于毕玄大学经历的一场漫谈,主题是“选择”,希望了解毕玄接触计算机的初心,把他的早期想法展现出来,与他后期观点形成对比,看看是否能找到他在面临无数人生选择时的底层逻辑。 + +如果你对他起步阶段的具体技术学习过程感兴趣,可以结合文末链接一起看。 + + - +极客时间:一直会有人好奇程序员非科班出身和科班出身的不同,你是非科班,为什么当时学生物后来转了程序员?大学那会为什么会想学生物,自己主动报进去的? + +毕玄:好吧,这是一个悲哀的故事。 + +极客时间:啊怎么讲? + +毕玄:因为我高中报大学的时候比较失误,我们以前考大学,你们不知道,跟现在差别是非常大的,当时是后出分先报志愿。 + +现在是先出分。关键现在是出分然后可以报很多志愿,当时我们是只能报很少的志愿,可能是3个志愿6所学校这样的,而且先录批只能报军校,第二个批才是正常。当时,我因为家里以前一帮人工作的原因,那个时候特别想读的是邮电大学。 + + +1998年的理科考试,理科考语文、数学、英语、物理和化学。报考顺序是5、6月份先报志愿,一个月左右后高考,然后出成绩,最后等录取通知书。 + + +极客时间:邮电大学?当时你的选择可以具体聊下吗? + +毕玄:我是1998年高考,我们那个年代最火的学校是邮电大学。邮电大学里面中国最牛的两所,一所北邮一所南邮,北京邮电大学,南京邮电大学。我们很多人都很想读邮电大学,我是因为我家有很多人在邮电这个体系内,所以我的志愿报的很差,第一志愿北邮,第二志愿南邮,没了。 + +因为那个时候邮电大学特别火,我住江西的,江西那年南邮的录取分数线接近清华,北邮应该是超过了清华。所以其实是我报的两个志愿都没有被录取,我都以为我要重新读高中了(笑)。 + +但是后来被我们省的大学南昌大学捞回去了,好像以前是可以这样的,就你可以没报它,但本省的大学应该可以从现在这批没有被录取的人里面捞,它想要哪些人。然后我就被捞进去了。 + +极客时间:没被理想的大学录取上,你有想过重读吗? + +毕玄:想了,我当时想了很久要不要重读,后来我觉得还是算了,重读还是太累了。 + +不重读,然后被南昌大学捞进去了以后,相对来讲我的分报南昌大学是比较好的,所以我可以选要读什么系。在我们那个年代最火的一句词是:21世纪是生物的世纪,所以我就觉得那就报生物系吧。 + +我们这些人回头看也觉得,最大的问题是我们那一代人,不过我觉得现在也一样,多数人其实读大学前你根本不知道自己要读什么,这是中国学生可能比较普遍的,国外我觉得好很多,因为他们多数是不先选专业的,就你进去先通识教育,加上选课又很自由,后面读了两年以后再选,我觉得是有可能会知道我想读什么的。 + +但是中国不行,我们那个时候更不行,生物系,我都不知道生物系是干啥的,也根本不知道生物系出去以后要干啥,你只是听到很多人说21世纪是生物的世纪,现在也还是这句话,22世纪是生物的世纪,应该还能听到这句话(笑)。所以像我知道的很多人也都学的生物,阿里之前有好几个人都是生物背景的,多隆也是生物系的,还有福贝,我们三个人都是学生物的。 + +极客时间:你们都是被世纪口号忽悠进去的? + +毕玄:也不知道,反正都是命运让我们选择了生物系,所以我就读了生物。 + +  + + + +  + +极客时间:那大学进去之后是生物系,你是怎么跟电脑结缘的?这个差别还挺大的。 + +毕玄:去学校读了生物以后,我很快可能就觉得生物不是我想读的,但电脑也很凑巧,反正有很多凑巧的因素。 + +最早是因为我到大学以后要军训,军训的时候很无聊,我们当时不是去军队军训,就是在学校,军训完了晚上的时候没有什么事可以干,我和另外一同学俩人就在学校里瞎逛。 + +逛的时候我们觉得太无聊了,真的太无聊了,因为1998年上网还没有,应该很少有学校能上网但非常慢,而且学生也不知道上网干啥,1999年才比较多,因为腾讯、阿里全都是1999年创办的。所以1998年的时候其实什么都没有,没有娱乐生活。 + +我们俩在学校逛的时候看到有个机房,最早机房好像可以上机玩Windows或者DOS,当时我们俩觉得反正也没什么事干,就进去了,结果进去之后,关键是我们发现也不知道玩啥(笑)。因为我俩以前在高中是没有怎么学过电脑的,什么都不懂。我们去了机房,也不知道玩什么,我就看旁边的人,发现旁边的人都在干啥呢,都在打字,练盲打。 + +极客时间:盲打?大家不是上机做实验或者玩游戏什么的? + +毕玄:那个时候不像现在,那会多数人刚到大学的时候,电脑相关的背景知识都是几乎没有的,其实都是从盲打开始,大家可能也很无聊(笑)。所以一进来,你发现整个机房的人哇居然全部在练盲打,简直了,也挺卷的,搁现在肯定在玩游戏了。 + +然后在那个环境里你就不由自主地被卷进去,因为你发现旁边人打字好快,就觉得我也要打得更快。所以军训一个月的结果就是我竟然练会了盲打,对电脑开始有一些兴趣了,觉得也挺好的。 + +极客时间:所以你是起步的时候有成就感了。 + +毕玄:然后大学就开始了,因为军训结束以后我们宿舍就讨论要不要买个电脑,就一起买,那个时候我们都是这样的,每个宿舍的人都觉得太无聊了,得买台电脑,当然了买电脑还是得玩游戏的,大家的第一诉求肯定是玩游戏。 + +反正就开始买了,但买电脑的这个过程,确实让我对这个行业可能更有兴趣了,因为我们那个时候买电脑跟现在是完全不一样的。 + +  + + + +  + +极客时间:那个时候买电脑,是组机吗? + +毕玄:对,以前买电脑,尤其学校学生,更倾向的是我买一堆的零件,自己组装,现在都是我买一台整机就用着好了,但以前不是。 + +我们去电脑市场买一堆的零件,开始学怎么组装起来。因为我们是生物系的宿舍,计算机系不在这个校区,他们在另外一个校区隔很远,所以我们跟计算机系的人其实不熟。但我们系有几个人,就很喜欢折腾电脑,也不知道来干啥,就觉得很好玩,所以后来这些人就开始负责各个宿舍电脑的组装。我们就开始瞎弄。但好处因为大家都不懂,你有瞎弄的空间。 + +那个时候大家希望是我用很少的钱,买到一台还不错的电脑,尽可能把这个电脑的能力发挥得比外面组装的更好,所以我们会学很多硬件层面的,比如说像什么CPU、调频啊各种各样,但这些都是自学,我们确实没有任何机会,因为选修,你也选修不到计算机系的课,就很尴尬。反正就自己玩。 + +极客时间:那个时候你学硬件是怎么学的?看书吗? + +毕玄:也不知道,我们好像没怎么看书,反正就瞎弄,可能有几个人会告诉你像CPU跳频主要是跳线问题什么的。我觉得最重要的是我们不怕。 + +跟很多人聊,现在学电脑硬件的很多人可能是不敢,比如给你一堆零件你敢不敢随便弄,你可能怕烧掉了。在那个时候对学生来讲,如果烧掉了也是很多钱的,可能几千块钱没了,哇那宿舍的人得把你砍死(笑)。但是我们好像也不怕,也不知道为什么,反正就瞎弄。 + +组装电脑的这段时间就会接触电脑行业的很多人,当然是偏硬件的人,因为我们那个时候经常去南昌的电脑城跟那些装电脑的人聊,后来他们说不然暑假你们来装电脑吧。那个时候装电脑挺赚钱的,因为信息很不对称的,就是看你懂不懂,你不懂我随便给你开个价,不像现在可以网上查一下,几乎不会有太大偏差。但以前偏差非常大。 + +极客时间:所以看你对电脑逐渐感兴趣的整个经历,大家如果兴趣不同,选择差异还挺大的,当时是你们在宿舍里面一起组机,还帮人家好多宿舍都组好,那其他人是就去玩游戏了吗? + +毕玄:对,那肯定呀,因为主要目的就是玩游戏,后来就变成了上网。 + +极客时间:你当时有玩游戏吗? + +毕玄:也玩,肯定也玩,但我可能没有那么感兴趣,我对游戏好像天生就没有那么强的兴趣,所以也就觉得没什么意思。 + +极客时间:这个差异性可能是大家刚开始就不一致的吗? + +毕玄:就目的不一样。对我来讲,电脑可能最主要的不是玩游戏,但对于别人可能我买电脑的目的就是玩游戏。 + +我后来也不大玩硬件了,因为组装门槛其实没有大家想象得那么高,硬件要么就是组装,要么就是设计,因为你不可能自己做硬件,这个可能性是不存在的,所以就剩组装。那会我们觉得去电脑城卖电脑,也就帮人装装电脑,好像也不是我们想干的,就不是很有意思,因为那个时候你也不会想着要赚钱,没有这个诉求。 + +  + + + +  + +极客时间:你大一接触电脑之后感觉有点意思,那大二呢?后来也不玩硬件了,你找了什么新活动? + +毕玄:大一可能是个开始阶段,我大二学少量的专业课,植物、动物、基因等等,这就很偏生物的,我上了几堂之后,实在不想上了,我觉得这个很不适合我。但那个时候我对计算机就越来越有兴趣了。 + +到了大二就是1999年、2000年,开始流行做网页,做网站了,因为腾讯阿里也创办了,外面有很多人很多公司就想做一些网站放在公网上,然后我们觉得做网站也挺有意思的,你可以在互联网上放个东西,可以自己随便搞。但那个时候其实是个静态,就不像现在是动态的网站,静态是每页都是写死的,里面是什么内容就是什么内容。 + +极客时间:写死的,类似展示页吗? + +毕玄:对,就是个展示,可以认为就是把文本放到了网上。那个时候大家开始学怎么做网页,就只能自学了。所以我以前还会说,自己挺擅长用PhotoShop之类东西的,因为以前做网站的三个东西三剑客,PhotoShop、Dreamweaver,还有个什么来着(Flash),反正就三个组合开始做的网页。 + +做网站,开始是觉得挺有意思,我学了一段时间之后,有家公司去我们学校想找人帮他们做一个网站。也不知道为什么我就去了(笑),虽然我是生物系的,计算机系的人去的好像还不多那个时候,当时我就去了。 + +去了之后,我就相当于变成在公司了,在一家江西那个时候的门户网站开始做一些网站静态的东西,我感觉还挺有意思的,哇还能赚钱,就觉得这个活简直太好了,所以越来越进坑了。 + +那个时候做网站是特别特别赚钱的,因为是按页收钱的,不是现在我给你做个网站多少钱,以前是我给你做一个网站,这个网站总共有几页,比如说有3页,你打开能点开的总共就3页哦,这样的网站大概要800块,在2000年800块对一个学生来讲还是很爽。后来我跟那家公司有些销售特别熟,他们会私下给我一些单子说我手上有个单子你去做吧,给你多少钱,就不走那家公司。 + +极客时间:跟销售的关系特别熟,这个在程序员中还挺少见的,你是平时会跟人家聊吗还是怎么? + +毕玄:那没有,其实这个是互利。销售他寻求利益最大化,对他来讲,找公司的其他同事肯定会要他更多的钱,这很正常,但学生嘛,也无所谓,给多少钱就多少,因为我完全不在乎多少钱。 + +加上我又不准备靠这个来赚钱,我只是需要做点东西就好了,如果顺带还能赚点钱对学生来讲那就更好。而且对学生来讲,钱其实也不少,干几天就能赚几百块简直太好了,因为我后来大三的时候正式上班一个月就2000。 + +极客时间:所以你就冲着做东西去,顺便赚点钱,但是后来很快网页就不赚钱了? + +毕玄:那个时候我做了一些小网站,你的技能就会越来越熟练,但这个技能很快就升级了,这种好日子大概持续了一年,一年之后就不再是这种报价体系了,就变成你的网站是要带动态的。 + +极客时间:所以是做了一年,技术就迅速升级了。 + +毕玄:对,我们回顾会发现很多技术是会非常快被平民化的,阿里后来的很多技术也都是这样,开始的时候它显得特别高端,其实是因为大家都不懂。 + +但很快,尤其中国,懂的人就会多,因为说白了是大家都会发现一个行业这么赚钱,就会涌入一大批人,如果学习的门槛又没有那么高,很快人才就饱和了、溢出了,所以大家就开始卷。以前说做3页是800块,后面可能变成了不管你多少页总共800块,那就不一样了,迅速市场价格就下去了。 + +当然也不是坏事,因为平民化了所有公司就开始做,以前太贵了不是所有人做的起,但对很多有技能的人来讲,我当时就是,会觉得哇这个太难了,太卷了。 + +极客时间:技术升级之后,你去学做动态页了? + +毕玄:恩后来我们就发现这个时候有另外一个门槛更高的东西是更赚钱的,那个东西就是做动态页,不再是原来的静态页。 + +因为静态页其实是很简单的,大家通常的做法是先用PhotoShop做一个完整网站的图片,做完以后开始切割,切割完写HTML就构成了整个网站,所以熟练的人一天就可以做完,速度非常快。但后来这个门槛上去了,大家开始学动态了,动态就写要程序。 + +所以我开始学习写代码,就彻底走向了“码农”方向了。以前只是个PhotoShop艺人,甚至只是去抄一下而已,因为那个时候很多人的做法是去抄韩国的网站,因为韩国做的特别好,他们网站设计都非常精良,中国的都很土,所以很多人就去韩国网站打开截图,把里面的字全换掉,搞定,那个时候也不存在版权。 + +极客时间:这会你是怎么学的? + +毕玄:到动态这个阶段开始学习一些代码,我只能自学了。那个时候用的还是ASP,现在应该是ASP.NET就Windows的,我们最早其实全部是微软体系,因为微软做的东西特别容易上手。然后我们开始学ASP,学习一些新闻发布的网站、留言板啊这种比较常见的动态程序开发,其实这个也还好。 + +但你能看到,这个门槛确实比前面是高很多了,就是前面学的人是非常多的,很容易进来,但到了这一步你会发现从静态变成写程序的人,确实是少了,就这一关就少了很多人,刷掉了很多人。 + +  + + + +  + +极客时间:从那之后,你开始搞动态网站写代码了,有一点好奇,你的学校和外面是怎么平衡的? + +毕玄:学会了以后,我应该是大三的暑假就正式开始写程序了,因为那个时候我已经觉得未来我肯定不会做生物了,真的没有兴趣,而且一开始的时候我混的就是外面的计算机圈,跟南昌做计算机网站的一帮人比较熟,他们都是工作的人。后来因为我在外面做了一些东西反向和学校计算机系的人认识了不少,我们当时有个工作室,就在南昌大学,是负责给南昌大学做网站的,所以第一版南大的网站是我们做的,里面就我一个生物系的人,其他全部是计算机系的。 + +所以我就变成了准计算机系的人了,但我也没有上过计算机的课,主要是我觉得大学上课也没什么意思,外面更实战,外面是有一家公司,我给了你个需求,你在多少天就必须做出来,这个可能逼着你反向更快地学习,因为这是必须的,你做不出来,后果不堪设想。 + +大三暑假,我就开始正式去一家专门做这种网站的公司,那个时候应该算上班了,都不是实习,我跟上班的人一样非常正式地干了几个月,那个公司的程序员特别少就三四个,后来干着干着就变成我是主力了,可能学生的学习动力更强一些。后来我就做他们那家公司相对大一点的网站项目,断断续续都在那家公司做,包括开学了以后我都在那做,所以生物我可能就学的很糟糕。 + +极客时间:那个时候上班是? + +毕玄:正式工资,不是实习工资。 + +极客时间:那学校的课程呢,不太去了吗? + +毕玄:几乎就不上了,所以在期末考试的时候,我老师还问你是我们系的?我说我是你们系的(笑)。 + +极客时间:考试是怎么解决的呢? + +毕玄:考前还是要稍微突击一下的,那个时候我们系专业课其实老师对我们很好,一般来讲不是很过分的专业课,老师在期末考试的时候都会出去,然后剩下就可以自由发挥了,所以一般我的专业课是可以踩线过关的,看我大学的成绩,专业课基本是在60-70之间,就是这样,没有更高了,没有一门会超过这个分数线,算是混过去了。 + +所以到了大三我已经算一个很职业的程序员了,应该算,因为我其实是正式公司的正式员工,跟别人没有太大区别。到大四是因为就不能了,要写论文,我就没在公司上班。另外也是因为到了大四我也不想在江西继续工作了,如果想的话也能继续呆,但我想离开,觉得还是应该去大城市。 + +  + + + +  + +极客时间:关于人生选择,在毕业之后,你是转成程序员了,那你同生物专业的同学们,有跟你一样换专业的吗? + +毕玄:我们班应该只有2、3个后来是不做生物的,有一个应该也是对计算机感兴趣,他是研究生直接就读计算机系了,可能因为我已经在公司工作,所以我觉得这玩意没有必要读研究生,我觉得还是社会可能更能让你学到东西。 + +因为我以前去计算机系了解过一点,跟计算机系的一些人聊了一下,我觉得他们好像也没学什么东西,而且他们很多人学了很多年,反而是不大想做计算机这个工作的,因为读的很枯燥,就不想学,但不在这个系的人,像我们可能就有很强的兴趣,然后会很主动。 + +极客时间:所以学习新领域,在一开始的阶段怎么激发自己的兴趣,可能很重要? + +毕玄:我大学做计算机只是觉得好玩,就这个原因,也没有什么,然后觉得自己学起来也很轻松。因为学生物我觉得太难了,学的很累,就算专业课认真听,我也就只能考到那个分,那我觉得我太难了,但计算机我随便搞搞,好像比多数人会好那么一点,当然这个兴趣就上来了,成就感就在。 + +我现在觉得为什么海外很多教学会好很多,因为他们大学是非常知名的教授来上本科生的课程,这个是不一样的,他会让你看到学这玩意到底有啥用。因为我们多数是不知道的,就是说你让我学物理,如果我不知道学物理有什么用,能够解决什么问题,那我就觉得这学来干啥,不就是考分,而且还那么难考,我才不考嘞,但是如果你发现哇学这个东西,原来能解决现在人类面临的问题。 + +极客时间:就有种使命感。 + +毕玄:所以我跟我家小孩说你就应该学生物。 + +因为他本来对动物、昆虫非常感兴趣,喜欢养各种昆虫,再加上我会跟他说生物里面对人类史有影响的很多问题,像看《人体简史》、脑神经科学家的采访什么的,听他们讲你就会知道哦原来研究这些的意义是对人到底能解决什么问题,你就会觉得,哇如果我真的能够研究出这种问题,那在人类历史上,跟计算机系的人根本不是一个档次的。 + +可能很多人都没有讲清楚这个问题,就是我学这个到底是干啥,有什么用。总得有点用吧,你不能说我学了个东西就为了考个60分,还是90分,这个意义很小,没有多少人是这个动力的。 + +极客时间:当年你那些同学,后来没有转行业的继续走生物路,你觉得他们有找到学生物的意义吗?现在大家的近况如何? + +毕玄:有些有,有些可能也没有。其实大部分人读这个专业不知道我出来能干什么,我们系的很多人毕业了问自己到底能干啥,没有人知道。 + +但我们那一届比较幸运,我们2002年毕业的,当时生物刚被列入了高考科目,那高中就有师资需要了。但生物系在江西是非常少的,那个时候江西师范大学还没有生物系,只有我们是正统的,尽管大家可能学的也很烂,但毕竟大学专业是生物工程,所以很多高中都是立刻来我们学校要生物工程的人,只要愿意,你就可以去,大学是多少分都不重要。 + +然后你竟然就此就进了江西的名校,现在我同学大部分都在江西非常重点的高中做生物系的教导主任,因为他们是第一届,这就是命运。第二届就不是这样了,因为江西师大就有了,那别人肯定是倾向师范大学的人,毕竟专业老师,我们这都是外行。 + +但我们有少数几个同学继续读到博士了,因为生物要非常好的话差不多都要到博士,然后开始领导实验室做一些研究,我们同学有的在北大带一个实验室。但做研究的比较多,大部分是做医药的,或者更危险的就是武器、军工。 + +  + +水友讨论区 + +今天我们就轻轻松松地随便聊点。现在你有在学什么东西吗,目的是什么呢?在学习过程中有挖掘到自己感兴趣的点吗?如果你是一名开发者,你和计算机的缘分是从什么时候开始的,当时有想用计算机做点什么吗? + +欢迎积极讨论参与盖楼,我们留言区见。 + +到这里我们和毕玄的所有对谈就结束了,非常感谢你的支持,希望这场毕玄的20年技术人生复盘对你有帮助。 + +最后为了能更好地了解你对专栏的看法,我也特别准备了一份问卷,欢迎你提出建议或意见,期待听到你的声音。 + +拓展阅读 + +从业余程序员到职业程序员,如果你对毕玄在起步阶段的技术学习过程感兴趣,可以看这篇他的复盘:程序员的成长路线Remix + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/00篇首语认识汤峥嵘.md b/专栏/超级访谈:对话汤峥嵘/00篇首语认识汤峥嵘.md new file mode 100644 index 0000000..2ecb776 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/00篇首语认识汤峥嵘.md @@ -0,0 +1,52 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 篇首语 认识汤峥嵘 + 你好,欢迎来到《超级访谈:对话汤峥嵘》这个专栏,我是专栏编辑王利莹。 + +汤峥嵘是谁?现为智能穿戴与健康大数据公司云柚智能的创始人兼CEO,1995年他在美国硕士毕业后,开始了美国10年的职业生涯,前6年在匹兹堡工作,后4年辗转硅谷。2004年回国加入阿里巴巴,前后在淘宝、支付宝、B2B任职,后历任微医(挂号网)CTO、途牛CTO、在线教育公司iTutorGroup (VIPABC) COO兼CTO。 + +回顾他留学至今的经历,相信你能看到一个普通程序员到CTO的成长跋涉。在美国十四年求学工作,他经历过职业快速成长期,也曾有迷茫。留学生活是汤峥嵘蜕变的开始,逼迫他无论大小事必须自己做选择;第一份工作让他明白做技术,技术能力是第一位的,只要有能力,公司倒了都不怕。而在机会众多的硅谷,他也曾看不清未来,遭遇职业发展瓶颈。回国后加入阿里,对他这个在美国工作10年的人来说,绝对是对价值观的一次洗礼,阿里8年,无论从做人做事方法上,还是在管理理念上,都改变了他,让他一度认为阿里文化无敌。之后经历的几家公司,其中途牛对他来说又是新的一次跃进,更深度参与业务,和公司一起野蛮生长,也让他真正明白了创业该怎么做。 + +时间回到 2021 年末,上海的疫情还有零星分布,我们与汤峥嵘初步商定了这个专栏的计划,并去上海进行了采访。采访回来,飞机落地北京后,上海某区出现阳性病例,距离我们的采访地点大概 10 公里,不在同一区,算是有惊无险。疫情时期,人与人之间的交流好像变得更艰难了。但我们总算拿到了采访的第一手资料。 + +这个专栏会围绕汤峥嵘的经历与思考,以访谈形式展现。我们将从他美国十四年的学习工作经历聊到回国后的发展,关注他回国后一些认知的改变。回顾一个普通程序员成长为 CTO、CEO 的历程,看看哪些重要节点在发挥作用?是运气使然,还是努力的结果?以及关注他在经历了二十多年职场工作后,现阶段对管理的理解。同时,你也会看到发生在他身上的有趣的故事。 + +其中让我印象比较深刻的是他在美国求学时期,曾经在纽约一家小超市打过一个暑期的工,看起来这段经历只是人生中的小碎片,没想到 22 年以后,途牛在纽约上市,汤峥嵘作为 CTO 去纳斯达克敲钟,机缘巧合下他又回到这个曾经打过工的地方,见到曾经一起工作的主管,一个巴基斯坦人,一切好像没怎么变,还是做着和 22 年前一样的工作。 + +这听起来是很戏剧性的经历,22 年前纽约市的一个最底层的打工仔,今天能够成为上市公司一员。对这段经历他还发过朋友圈,有人说这个故事很励志。但他经历过后,更多的是思考成功和幸福的意义。这件事让他意识到,靠自己的勤奋,在异国他乡找到一个属于自己的家,也是一种成功,或许更加值得炫耀。 + +在这个专栏中相信你还能看到更多有温度的故事,有态度的观点。如果你在成长过程中遇到过未获解答的疑问,比如: + + +职业成长中要实现上一个台阶,要经过哪些努力? +当遇到职业瓶颈的时候,该怎么做选择,改变现状? +怎么才能得到贵人相助?如果真的没有遇到怎么办? +假如遇到岗位频繁变动的情况,个人如何“拥抱变化”? +如何选择值得奋斗的行业方向? +…… + + +从汤峥嵘的个人经历和思考中,我们或许能找到一些答案。 + +经过 100 多个问题的碰撞,我们将这个专栏分为认知升级、职业成长、管理经验三大模块。在职业成长、管理经验模块,主要从他的成长经历、职业发展、管理提升过程中寻找可借鉴的选择、观点和方法。在认知升级模块,会主要跟你分享,性格对人的影响是很底层的,了解自己性格的重要性,性格也被当作他认知自己的底层数据。 + +汤峥嵘喜欢给自己团队的伙伴做性格分析,用的是国际流行的职业人格评估工具——MBTI职业性格测试,这套理论模型从复杂的个性特征中,按动力、信息收集、决策方式、生活方式4个维度,把人分成16种性格。他发现程序员大部分都是ISTJ型(内向、实感型、理性的,喜欢按规划、规则做事)的性格,他也是这个类型。如果把汤峥嵘作为一个ISTJ型的案例,那么他的思考方式,解决问题的方式你或许可以借鉴。 + +通过这个专栏,希望你能获得什么呢? + +第一,希望你通过看别人的经历,拓宽视野,了解原来没有了解过的领域。期待你通过某一段故事,某一个观点,对照自己面临的问题或困境,找到做下一步选择的更优解。更重要的是增加看待这个世界的新视角。 + +第二,关于成长、职业发展、管理等话题,虽然也老生常谈,但如果你能有“原来他有不同理解”或“我以前怎么没考虑到这个层面”的感悟,当然更好,这将是对这个专栏的重要肯定。 + +正如汤峥嵘说的,“每个人都会有自己的经历、自己的经验,别人的经验可能对你来说永远无法复制,只能借鉴。无论结果成功还是失败,当我们越了解自己,越能接纳自己的过去,就越能包容外面的世界,因为很多时候感觉很多人活得很挣扎,都是自己内心的问题。” + +“搞技术的人,大部分都是理性的人,也很难相信别人告诉他的理论。包括这个采访,其实只是一个案例,只在这个案例中找到适合自己听的东西就好,也不要忽略全世界其他的案例。但是这个世界的数据太复杂了,我们没法收集所有数据,我们能做的就是慢慢了解自己,看看自己的数据到底是什么样,根据数据改变自己,就是我现在的做人哲学。” + +接下来每一篇内容我们都会以访谈形式展现,欢迎你多发表看法,如果你觉得内容不错,也欢迎把它分享给你的朋友。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/01看似理性的程序员为什么可能是最不讲理的?.md b/专栏/超级访谈:对话汤峥嵘/01看似理性的程序员为什么可能是最不讲理的?.md new file mode 100644 index 0000000..066339f --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/01看似理性的程序员为什么可能是最不讲理的?.md @@ -0,0 +1,173 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 看似理性的程序员为什么可能是最不讲理的? + 编者按:这次采访,汤峥嵘提到最多的是他做人做事的方法,总结为 8 个字“了解自己,理解他人”。这几个字乍听起来似乎是鸡汤,你可能会想:我也知道应该按这 8 个字进行人生的修行,但具体怎么才算了解自己,又怎么才能理解他人呢? + +针对这个问题,汤峥嵘做了 360 度的解答,从他的思考总结、他的经历,你也能看到他在践行这套自己信任的方法论。 + +在我们的对话中,会频繁提到 MBTI 性格测试——一套非常流行的职业人格评估工具,它把人的性格分成 16 种,汤峥嵘是其中的 ISTJ 型人格(内向、实感、思考、判断),而他也发现大部分程序员也都是 ISTJ 性格的人,如果你也想了解自己的性格,可以自己先测试一下。 + +理科男看似是最讲理的,但为什么越理性的人在面对冲突的时候,越容易做出不合逻辑的选择呢?这和性格有关。如果把汤峥嵘作为一个案例解析,他是 ISTJ 型人格,大部分程序员也是这个性格,那么他的思考方式,解决问题的方式你或许可以借鉴。 + +接下来的两节,我们会从 MBTI 性格测试是什么讲起,包括它对汤峥嵘的影响,如何把这套理论付诸实践,他基于这套理论的思考。从个人职业性格延伸到“企业性格”,发散思考企业文化与价值观。 + +以下为访谈对话。 + +什么是 MBTI 性格测试? + +极客时间:我们就从你最想聊的性格分析开始吧,你怎么看性格对人的影响。 + +汤峥嵘:我特别想讲一讲性格分析,人对自己情绪的管理。我之前做过一些管理培训,我也会让大家做一些性格测试,用的就是 MBTI 那套比较传统的性格分析方法。我发现,十个技术人里可能七八个都是一类性格,叫 ISTJ 型,我自己也是这种性格。 + +这种性格的人,特点就是很客观,很理性。但是太理性的人往往容易陷入一个误区,认为只有理性的判断是靠谱的,非理性的判断都是不靠谱的。事实上很多人的非理性判断也是靠谱的。恰恰是因为你没有机会去锻炼非理性层面,你的非理性判断才容易不靠谱。 + +那么是不是理性的人总是在做理性的判断呢? + +有一种理论把大脑分为理性脑、情绪脑和本能脑。或者可以简单地分为理性脑和非理性脑。我们以为总是理性脑在做决策。但真实情况是,绝大部分时候是非理性脑在做决策,而理性脑负责把决策合理化。所以有人形容理性脑是非理性脑的下级,它就像 CTO一样,责任是把CEO的决定合理化和逻辑化。 + +所以越是理性的人,一旦犯了错就越固执。因为他逻辑性很强,总能给自己找出理由说自己是对的。这里说的“犯错”,通常是不那么绝对的错误。如果是特别明显的对错,通常也无法找理由了。 + +你可以理解为我们人是由一堆传感器和一个处理器组成的。传感器负责捕捉各种数据,处理器负责用算法对数据进行计算并得出结论。所谓理性的人,偏向用足够多的数据得出逻辑上相对合理的结论。所谓非理性的人,就是在看起来数据不够的情况下,仍然可以用他们擅长的算法得出他们认为合理的结论。但在理性人的眼里,这些结论既缺乏依据,也缺乏逻辑,往往认为不靠谱。所以两个性格之间就容易产生非常大的沟通障碍。 + +其实无论工作还是生活中,很多重要的决策,是缺乏数据和逻辑的。否则,创业什么的也太容易了,照着数据逻辑做就行了。就是因为你不知道未来啥样,你也不知道哪条路是对的,这时候就要靠你的非理性思考去做决策。 + +说起创业,我现在做的智能穿戴产品就是通过编织在服装内的传感器,来长期获取人的生物体征数据,从而来量化人们在各种生活场景下的健康、情绪甚至性格。现代科学和医学已经对人的神经有了非常多的研究。例如人的心跳、呼吸、眨眼、出汗、肾上腺分泌等各种生理反应是非常底层的。控制这些生理反应的不是我们的大脑,而是自主神经,也叫植物神经。我们现在已经可以通过智能穿戴获取的数据来测量人的植物神经。这对于我们了解自己非常重要。我后续会多分享一些这方面的内容。 + +极客时间:可以再简单介绍一下 MBTI 性格测试,以及 ISTJ 型人格?刚才只是笼统地聊了聊。 + +汤峥嵘:MBTI 是一套非常流行的职业人格评估工具,有4个维度,每个维度都是两种性格,分别是:外向(E)和内向(I)、实感(S)和直觉(N)、思考(T)和情感(F)、判断(J)和认知(P),不同搭配组成最后的 16 种性格。这四个维度我们分别说一说。 + + + +第一种,内向和外向。因为这是翻译过来的词,所以跟我们平常说内向外向有一定的相似,但也不完全相同。它的核心是说你的精力和注意力是对外的还是对内的。 + +内向型的人比较喜欢读书,喜欢一个人去获取外面的信息,大部分爱读书的人都是比较典型的内向人。外向型的人喜欢跟人聊天获取信息,喜欢出去走走,他的信息从外面听来的可能比较多一些。 + +内向的人一部分是不善于表达,但也有能表达的,只不过讲话容易累。这个特别有意思,曾经我上过一门课,有个老师很能讲。但是最后他问我们:“你们猜我是什么性格?”我们都猜他是外向,但他说自己是一个极度内向的人,他说我讲课是很累的,但我给自己找了一个方法:每讲两个小时,就得消失个 15 分钟,去喝咖啡、看会书,给自己充电。 + +现代社会对我们的要求可能和我们的性格是冲突的。比如外向型总体比较受欢迎,因为无论在工作还是家庭中,沟通都是非常重要的。内向型的人如果了解自己的性格,就知道有时候需要切换“性格”来适应场景。有点像戴着外向的面具扮演角色。中间过程或许阶段性地休息一下。当角色扮演结束后,最好还是回归到自己本来的性格。最怕的是我们不知道自己的性格,还以为这个面具就是自己的一部分。时间长了,摘不下来了,但又很痛苦。 + +我有个特别有意思的案例。以前途牛的一位创始人是典型的外向型。他特别喜欢出去看其它公司、其它行业是怎么做的。他每周要出去个三四天左右,在公司就只呆一两天,回来他就跟我们聊,在办公室把他看到的事情写在白板是给我们讲,我最开始就觉得他这样特别乱,没啥条理,因为像我这个性格的人,特别希望听逻辑和条理清晰的事情。 + +后来我就理解他了,因为他是个外向型的人。他花两个小时和我们互动之后,能把出去这趟的信息都整理得清清楚楚。结束的时候,白板上总能出现非常有逻辑和条理的结论。他需要通过和他人的互动来获取信息、整理思路,甚至做出关键决策。而我作为一个理性的人,可以帮他做梳理,在这个场景下我是个很好的工具。当我知道我俩不同的时候,配合就非常好了,我也不觉得痛苦了。 + +第二种,S 和 N,S 叫做实感型的,N 叫做直觉型的。我是实感型的,做决策是要数据的。直觉型的人就是凭直觉来做决策、做判断。 + +我做决策的时候,需要数据来支撑,信息不全的时候,我是不容易做决策的。以前我做CTO的时候,有时候老板会希望我马上对一个新产品新应用进行评估,希望我马上给出上线日期。我就跟老板说,我是内向+实感型的人。我不像你,能通过讨论,凭直觉做判断。我不行,我需要一个人静下来,用数据仔细算一算。遇到这种沟通问题,其实就是给对方讲清楚,我的处事逻辑是怎样的。 + +假设人脑里真有“电路”,往往直觉型的人,他的 CPU 的速度要比实感型的人快,他的传感器也非常多,很快就用自己的一套算法和自己获取的数据预测出一个结果。在他自己的运行体系中,这个可能是靠谱的。 + +在我们实感型的人来看,这就不靠谱嘛,因为我速度比他慢,比如说他一秒钟算完了,我可能要算十分钟,甚至一个小时。你就会想,这不可能,他怎么那么快算出来?他肯定缺失了很多数据。但是事实上,他可能经过长期练习,就有这个敏感度和自信。 + +这方面也有例子,比如说程序员去赌场赌博,我们都是相信概率。以打德州扑克为例,第一轮,两张牌发完,有人说“我 All in”,这时候你肯定吓一跳,这刚叫牌他就 All in,那我不跟了。为啥?因为你信息不够了,算不出来,你只能放弃。 + +那些直觉型的人或者职业打扑克的人,他就是敢做。我是认为,他也有获取一些什么信息,比如他看到我这个人的样子,看我的眼神就知道我是保守的,他只要吓唬我一下,那我肯定就不跟了。从这个角度来讲,他跟我们想的概率不是一个层面的。他的感觉比我更灵敏,信息量就比我大,也就是数据维度比我们多,对我们进行降维打击。我们做程序员的人可能根本就没想到,根本连脸都不看,我就盯着自己的牌,而且我还以为这个就是所有的信息了,所以我们这样的人直觉就不够。 + +很多S型的人总觉得直觉不靠谱,但是当信息不够的时候,你会发现N型的人靠直觉更靠谱,S型的决定反而不靠谱。 + +我就是 S 型的人,我是比较喜欢用数据,但是现在随着年龄增长和工作的需要,我也知道某个决策该拍就得拍了,我要求我自己必须得靠一下直觉。即便自己心里知道这个是拍脑袋的,但也要相信自己的判断力,你越坚定,团队执行得就越有信心,也许这条路就走成了。 + +第三种就是T(思考)和F(情感),我认为把它们翻译成理性和感性更好理解。感性的人基本上靠情感来做决策的。 + +拿辞退人作为例子,我是 T 型的人,比较理性,我觉得这个员工不好就应该辞掉。但是让 F 型的人来判断,就会说你看你把他辞掉了,他没工作了,他怎么养活孩子、养活家?同理心让他得出的结论是不应该辞掉他。 + +T型的人完全从逻辑进行思考,而F型的人同理心强。当缺乏充足证据和逻辑的时候,说服他人更好的方法也许是同理心和感性。 + +第四种是J(判断)和P(知觉),解释一下,J 型的人是比较喜欢按规划、规则做事,P 型的人喜欢自由,比较天马行空。 + +我是 J 型的人。比如我跟太太出去旅游,我就比较喜欢做好所有的准备工作,看好时间、做好攻略、安排好每天的行程,甚至规划好每个景点玩多久。如果我老婆说这个景点很好玩,想多玩儿一会,我也许就容易纠结。因为多玩一会是应该的,但因此也会打乱计划,影响后面的行程。 + +讲出来都有点搞笑,就是对J型的人来说,去旅游景点玩儿,和玩儿完之后我把这个事打上个勾,那个勾的满足感或许更强(笑)。你就可以理解这个区别了,最终是一个人的满足感。而 P 型的人,他根本就对那个勾没有感觉,他就是喜欢无拘无束,天马行空。 + +我有一次讲课,拿这套系统大概测了 30 多个人,不全是做技术的,还有做市场销售的人,竟然没有一个 P 型的人。显然是大家已经被工作改变了一点自己的性格,有的人最初性格说不定是 P 型的,但是因为几乎所有的工作都要求排计划,这个就导致大家必须改变自己,按规划、规则办事。 + +J 型的人在工作中也有不灵的时候。比如电商公司的系统平时很稳定,但偏偏在 11.11 出现了系统宕机,这对技术人员是很大的挑战,甚至是最头疼的事。这种突发事件对于 J 型的人是严重的打击,因为不按他计划走,如果有很好的备案还好,如果没有,他马上就会慌了。这个时候再做决策就容易被情绪支配了,但是他的非理性部分又不经常练习,所以就做不出好的决策来。 + +而这个时候,P 型的人就会跳到舞台中央来了,他在这时候状态都来了,或许能想出很多J型人想不出的解决方案。所以当出现突发状况时,公司要去用 P 型的人,大概他要比 J 型的人做得更好。 + +极客时间:之前在一次演讲中,我记得你说没有找过女朋友的IT男不适合做管理?你还记得吗? + +汤峥嵘:是的,我认为女生中非理性思维的人多一些,男生中理性的人多一些,特别是IT男。IT男如果是没谈过女朋友,比较不容易理解非理性思维的人,做管理的话挑战就比较大,因为管理需要和各种人打交道。如果结过婚,甚至有养过孩子,可能会更好。有孩子的肯定知道,孩子小时候是不理性的,带过孩子后或许能理解什么是非理性思维。 + +但是工程师的思维往往容易比较线性,不按照逻辑走的就认为是Bug,我要把你修复掉。我经常跟大家分享一个小经验。我给一个管理者出一道题,就能大概知道他的管理水平在哪个阶段。 + +“假设有个员工离职了,找你沟通,你是老板嘛,你好不容易劝说他不离职了,你这时候的心态是什么呢?” + +一种心态是,谢天谢地,这个人终于这次不离职了,我希望他不要再提离职,至少三个月、六个月不要再提离职了。大部分技术转管理的人很容易有这种心态,因为说服人不容易,说完了之后,心里还是不希望他来找你了。 + +另外一种心态是,我知道我今天只是暂时说服了他,他回去一想,可能还会再来给我提,我就等着他下次来找我,我早就准备好了,而且我跟他聊我还特开心,我还享受说服他这个过程。 + +这是两种完全不同的心态。很可能后者比前者在管理水平上更高些。 + +前者他认为提离职是个 Bug,Bug 修复了,不会再发生了。但是我们所有人知道,恰恰是提过离职的人更容易再提离职,从来不提离职的人,他不会去找你。所以呢,恰恰是这种 Bug 一旦发生,它可能复发率很高,你要用 Bug 的方式去修它,可能永远修复不了。 + +而且,你已经成为一个弱势,你觉得理亏,没有管理好他,他要来谈离职了,你好不容易说服了,但气场是弱的。提离职的人或多或少能感受到这个气场,被说服就比较难。 + +我们做技术的,包括我自己,肯定都是觉得要摆事实讲道理:你为什么提离职?你是钱不够还哪里不满意?反正一个个问题讲完,我一个个解决掉。 + +但是通常是,当这种离职事件发生,别人给你讲可能不见得是真话,其次就算真话,那个人未必就是因为这一个原因,你想着把这个坎儿给他解决了,他就能不离职了?很难。肯定是一个非常综合的东西,都已经积累到今天,要提离职了。 + +技术团队的人性格很相似,都比较理性。但往往理性的人容易冲突。大家都觉得应该自己的逻辑是最正确的,而且认为其他人也是同样的逻辑,所以都憋着不说,但是等到开始出现问题的时候,已经跟之前那个问题不是一个级别了,你再用这种修复 Bug 的方式去解决,就解决不了了。因为 A 问题产生了 B,又产生了 C,这么延续下去,你也不知道最后变成了一个多么复杂的问题。 + +写代码就是跟系统打交道,每天面对的是电脑和程序,是没有任何情绪的,是纯粹的逻辑。每天训练的都是理性,而非理性层面长期得不到锻炼。很容易就会认为人也都这样理性。而实际上有很多人不是这样思维的,甚至理性的人在做判断的时候也会是非理性的。 + +所以了解自己的性格挺重要的。了解性格的一种方法是用MBTI这样的评测工具。最近我们发现我们的智能衣产品也能帮我们我们了解性格,而且可以解释为什么性格是天生的。 + +举个例子,你觉得我是急性子还是慢性子? + +极客时间:你应该是慢性子。 + +汤峥嵘:所有人都认为我是个慢性子,但是通过测试心电,测出来我是个急性子。从心电图上我们能看到波形的高度和宽度,波峰较高,宽度较窄的,说明心脏对外界瞬间刺激的那一刻的反应特别快,特别敏感,这是一种人。 + +另外一种人是波形宽度较宽,高度较矮的,说明他对外界的瞬间刺激反应时间比较长。对着这个模型,我们后来问了各种各样的人,是急性子还是慢性子,几乎全对。所以我们认为性格可能是天生的。 + +我知道自己是急性子,只不过随着年龄增长,我在想办法让自己在做抉择的时候不要着急,所以你会觉得我是慢性子,但是我知道我的内心还是个急性子,这个没法变的。 + +你了解了自己,更重要是接纳自己,你老想把自己改成个慢性子,可能就没救了,因为你改不了,碰到刺激时你的反应是变不了的。我对事物比较敏感,我对很多细节也比较关注,你说这是后天练习还是先天就有的?我觉得很可能先天已经种下,基因让我对这样的事情敏感。 + +反过来,另外一种我们说“心大”的人,碰到相同情况他可能就觉得这不算什么事。 + +极客时间:明白了,对于这套理论我还有些疑问,比如说你是 ISTJ 型,这里面实感型和理性、讲数据它们之间是不是有直接关系,假如你是个理性的人,那大概率也是实感型的人? + +汤峥嵘:从理论上讲,有可能你既是一个实感型的人,但是也是个感性的人,你很注重数据,但是你也很注重情感,这是也有可能的。 + +但是从我们测下来的结果看,重视数据的实感型的人,和思考型的理性的人正相关性会更大一些。先抛开内向和外向这个维度不说,STJ 在程序员中估计是超过 50%的,这个我很有把握。但是呢,真正好的团队是性格越多样越好。 + +极客时间:我也去专门查了这个性格模型,四个维度代表什么,分别是代表动力、信息收集、决策方式和生活方式,总体来说称为“性格”这个词还是太大了,内向外向、感性理性还比较好理解,像实感和直觉主要看是不是依赖实际的数据,这个还是有点抽象。 + +汤峥嵘:我再试着解释一下,评判一个人是实感型的人,还是直觉型的人,主要看他关注细节、经验、数据等看起来实实在在的东西,还是关注模式、未来、感觉等看起来虚一些的东西。直觉型的人,我们说起来好像人家不靠谱,凭直觉做抉择。 + +但是我认为什么叫直觉呢?第一,就是他的传感器比较多。第二,他的决策模型不用那么多数据。然后他自己也很自信,他能说服自己。核心就是,因为他是这个性格的人,他做这个决策时候他其实很有信心的。只是你作为另一种性格的人来看,不相信他凭借这么点数据,就能做出这个决定来,感觉很不合逻辑。 + +但是你要理解,确实他的信息跟你获取的是不一样多,但他的算法跟你的也是不一样的,你的可能是百分之八九十的数据才能算出来,而他百分之二三十就够了。很多成功的人,成功的原因可能就是因为长期训练算法,从而算法比普通人优秀。当然肯定也会有很多靠直觉的人,天生算法不行,就被淘汰掉了。 + +极客时间:也就是说这样的人长此以往,不是特别牛,就可能特别傻? + +汤峥嵘:是这样的。你说像马云这样的人,大家一开始觉得他做淘宝这事很不靠谱,马云那时候跟 COO、CFO、CTO,三个人说,我要去做淘宝,这三个人都反对,这三个人是从 03 年那个时间点下,用理性分析问题的。03 年的时候美国 eBay 已经是全球最大的C2C电商公司,而且收购了中国最大的做 C2C 的公司——易趣,那时候易趣在中国市场份额是95%。 + +这个时间点,马云说做淘宝,理性角度看成功率很低,你不觉得这人是个疯子吗?他们四个人做决策,三个人都投票反对。但他觉得就要往那走。三个人明明告诉他这个事是不靠谱的。但是他把这个事做成了,他就是天才了,他肯定有他的决策模型。 + +如果你总按直觉做决策,但没成功过,那就是另外的故事了。所以这样的人少,往往就被誉为天才嘛。 + +极客时间:所以人的性格,或者说情绪会影响决策,我对你前面说的“你越坚定,下面执行的人越有信心,也许这条路就走成了”。这个逻辑还挺好奇的,怎么形成这种正向循环? + +汤峥嵘:很多时候,信心决定了做事情的方法,甚至是结果。一个老板做决策、引领方向的时候是否自信,这个其实关系到团队的稳定。一个真正的牛人在面对未知时该怎么做呢?他要先把自己给“欺骗”了,只有让自己彻底相信,才能在对外表达的时候让人信服。 + +再比如,你要决策准备在一家公司继续干,还是换个工作,无论你做哪个决策,做完决策这个过程中,如果你发现自己水平提升了,这就了不得了,你会给自己暗示,我这决策很棒很好,接下来做事又做成了之后,你就形成正向循环了,自信心也就越来越强了。 + +极客时间:还有另外一个问题,不同的性格搭配,会不会决定某个人他适合去做管理,还是适合在垂直的方向上发展? + +汤峥嵘:我觉得肯定是有,比如说管理的人,我是觉得首先外向型的比较好,直觉型的人比较好。但这是针对“管理人群”这个模糊群体的大致推测,但你让我今天我做个归纳:什么性格的人就适合做 CEO,我觉得这肯定不靠谱。回答这个问题,我还是数据不够。 + +因为性格只是一个方面的数据,但他的经历,做过什么事情,是否做过非理性的决策,这些真实数据你并不知道。 + +互动时间 + +关于 MBTI 性格测试的基础信息,先聊到这里,下一节我们会继续聊这套理论对汤峥嵘的影响。你知道自己是什么性格吗?欢迎在留言区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/02个人性格影响“企业性格”,企业文化离不开人.md b/专栏/超级访谈:对话汤峥嵘/02个人性格影响“企业性格”,企业文化离不开人.md new file mode 100644 index 0000000..1346fd9 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/02个人性格影响“企业性格”,企业文化离不开人.md @@ -0,0 +1,125 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 个人性格影响“企业性格”,企业文化离不开人 + 最开始接触这套理论是什么时候? + +极客时间:我们接上一节的问题聊,你最开始为什么会关注、重视MBTI这一套理论呢? + +汤峥嵘: 原因是为了提高情商,我现在要做的事情就是学习这些东西,这样我就可以去理解别人,通过这样的方法提高情商。 + +什么叫高情商?就是能够理解他人,站在他人的角度思考问题。有的人天生就很擅长,但我们做技术的人可能很多是智商高,情商低。因为跟系统、代码打交道不需要情商。 + +我慢慢觉得理解别人很重要,但理解别人又挺难的,我们可能总觉得身边人不靠谱,就自己最靠谱。这样的想法其实要改掉的。什么叫靠谱?他的做事逻辑自洽的时候就叫靠谱。 + +极客时间:怎么理解这句话? + +汤峥嵘:就是他的决策,和你想法不一致,但是在他的这套系统是一致的。 + +比如说直觉型的人做决策的时候,他内心都是很真实的。举个例子,假设早上碰到老板,他今天很开心,他觉得很久没和你一起聊天吃饭了,于是就说今晚有空吗?咱们一起吃个饭。那像我这样性格的人,我一定会把这件事记到日历上提醒自己。快到饭点,老板突然跟你说“哎呀不好意思,我忘了今天已经约好和另一个人吃饭了”,这个时候,你可能就会很受伤,因为你为了这顿饭把别的局推掉了。 + +但你理解了老板的性格,你可能就知道了,在他见你的那一刻,突然想和你吃饭这个决定是真心的,他只是不习惯看数据,喜欢靠感觉做决策。他的决策虽然是错的,但他的初心未必是坏的。这个时候,我们能心平气和地理解这个人,你就不会误解了,也不会把这个事情想得很严重,这个很重要。 + +极客时间:这个例子里,老板做事的方式感觉有问题。那换个角度,假设下属约老板吃饭却爽约,你会觉得这个人不靠谱吗? + +汤峥嵘:我们经常会给别人贴“靠谱”“不靠谱”的标签,因为职场对我们的要求应该都是这样的。但理解别人就是理解世界的运转,对你自己内心来说,你就更有包容性,不容易被伤害。拿刚才的例子,老板爽约,我们很容易被伤害,这是别人用他的错误来惩罚你,何必呢?你自己可以决定“要不要被他伤害,要不要把它当成一个事”。 + +反过来说,假如一个人总是做诸如“爽约”这样的事,无论在职场还是人际关系里,他是很容易被淘汰掉的,也不用你来评价他如何如何。 + +当你想通了这件事的时候,你就明白,他犯了错跟我没关系。我无非就跟另外的人约饭嘛,但如果你把这件事在内心中放大了,就会给人贴标签,最后你可能选择业务上不合作,事情性质就变大。 + +有时候我们说所谓的情商低,就是因为不愿意去了解和自己不一样的人,就老是伤害自己,老是自己痛苦。我们做技术的很多人都有这种小问题。就是生活在自己构建的一个乌托邦里,就认为世界上只有 ISTJ 这一种性格的人。 + +极客时间:哈哈,你讲这些,我们平常就会说“这人格局很大”,但真正能理解别人挺难的。 + +汤峥嵘:所以,这其实是修行,你身边比较熟的人,你去多观察,多了解。你去看他做事情的动机、方法、原理,你发现他就是一致的。只是他的算法和你不同而已。不管生活还是工作中,多关注和你协作较多的身边人,不要陷在自己的逻辑中。 + +极客时间:你是什么时候开始接触这套理论的? + +汤峥嵘:是我在阿里的时候,阿里的经历对我有非常大的帮助,后面我再细讲。关注这个是因为阿里给高管开过一门课,叫做《情商为零》,我当时第一个报名,因为我觉得自己情商不够高,这个课主要是讲怎么提高情商。 + +讲完以后真的让我一下子醍醐灌顶,我一下觉得,原来这个世界上的人性格是这么多。我对这个课的记忆一直是非常深。我今天讲的,应该和那个老师讲的核心差不多。 + +他说讲情商的目的是让我们理解这世界上很多不同种性格。我把这个称之为“理解他人”。我后来又增加了一条,“了解自己”。“理解他人”是指了解他人做事的原理。“了解自己”是指了解自己做事的原理。我认为一个优秀的管理者需要不断地提升“理解他人”和“了解自己”这两个能力。 + +在“理解他人”方面,可以借助MBTI这样的性格分析方法论,也可以用其它的方法论。它们的价值在于让我们理解他人做事的规律和原理。这方面已经花了不少篇幅讲述了。 + +在“了解自己”方面,MBTI这类的性格分析方法论可以帮助我们了解过去不知道的自己。了解自己性格的边界,了解自己在不同工作生活场景的的优势和劣势。在这个基础上,我们可能需要调整自己,优化自己。那么怎么调整和优化呢?这就需要另外一个概念:情绪。 + +情绪是什么?情绪到底是主观想出来的,还是有底层的客观物质基础?情绪是如何影响我们做事的? + +我以前以为喜怒哀乐等情绪是我们人类独有的,最多也就是在一些高级哺乳动物身上有。但科学家的一个实验颠覆了我的认知。和人类相比,龙虾是一种非常低级的生物,有3亿年的历史。龙虾之间会相互战斗。败下阵的龙虾,通常会退缩。但是给败下阵的龙虾注入人类用的抗抑郁药之后,它居然会兴奋并重新战斗。这个实验说明,有些情绪是非常底层的,在非常低级的动物身上就有。情绪是有底层的客观物质基础的。 + +那么为什么动物和人有情绪?一种观点是和进化相关。情绪是快捷键,可以让我们在一些场景下,无需思考就做出适当的反应。比如当我们的祖先遇到猛兽毒蛇等危险的时候,交感神经就会提高,从而导致心率上升、肾上腺分泌、瞳孔放大等。这个反应也被称之为战斗或逃跑反应。这种反应有利于躲避危险,让我们生存下来。当我们的祖先在安全的山洞里休息的时候,副交感神经会提高,从而导致心率下降、瞳孔缩小等相反的生理反应。这种反应可以让我们放松、生长、自我修复,也有利于生存。这些快捷键可能是在几百万年甚至几千万年的进化中也形成的。但在最近短短的几百到几千年中,人类几乎消除了这些自然界的危险。这些底层逻辑还来不及进化,于是就变成了焦虑、兴奋、烦恼等现代人的情绪。交感神经持续过高就是焦虑,副交感神经持续过高就是抑郁。 + +之前讲过,一种理论把人脑分为理性脑和非理性脑。非理性脑就是靠情绪甚至生物本能来决策的。而我们很多时候都是非理性脑在做决策。决策后,理性脑负责把决策合理化。结合上面的情绪理论,我认为非理性决策的模型可能是这样的。当我们遇到一些不确定性问题的时候,我们的情绪会首先做出反应。这种反应是非常底层的,是为了让我们逃避危险,因为不确定性就等同于风险、危险。这种反应表现为焦虑、兴奋、紧张等各种情绪。同时,非理性脑快速启动,做出了有利于逃避风险的决策。而理性脑总是慢半拍,于是只好把这些决策合理化。这就是为什么我们明明知道应该怎么做,明明知道第一性原理是对的,但我们做起来的时候总是做不到。因为情绪替我们做了决策。 + +因此了解自己的情绪就变得很重要。如果我们能识别自己的情绪,或许就能识别出我们的决策受到了情绪的影响,或许就能强迫理性脑去参与决策。我相信识别自己的情绪甚至管理自己的情绪是可以练习的,但难度肯定不小。特别是识别情绪,标准到底是什么呢?每个人对情绪的定义可能就不同。怎么定义紧张和放松?能否量化呢? + +我创业做的智能衣产品,就能够24X7不间断收集体表的生物电数据,然后通过后台大数据算法分析出交感神经和副交感神经的平衡。这对于我们了解自己的情绪或许很有帮助。特别是能帮我们了解睡眠中的情绪。大部分人是不太知道自己睡眠时候是什么样的。 + +发散思考:企业文化与价值观 + +汤峥嵘:说到人的身体数据,人的性格,我还想发散讲一点关于文化、价值观层面的事情,看你后面也有这个问题,就在这解答了。 + +其实我刚到阿里,一开始对阿里的文化是有点排斥的,甚至不愿意接受。因为我在美国工作了很多年,从来没经历过这么大力度推行价值观文化的公司。但后来我变成了阿里文化的拥护者、推广者,包括我后来去途牛、VIPABC,都在推广这个文化。那个年代很多年轻人没有所谓的信仰,缺乏一套对价值的认知体系。而在做互联网这种新模式时候,会遇到很多新问题,没有前车之鉴。什么是对的?什么是好的?什么是应该做的?阿里价值观文化的意义在于可以指导年轻人去做具体的价值判断。 + +现在,我的想法又有些不同了。经历了多家创业公司后,我发现阿里的文化很难复制到其它企业。一旦尝试复制就会变得很虚,很难落地。我就在想,既然人的情绪是有底层物质基础的,那么企业文化是不是也有底层的客观规律和数据基础? + +我觉得企业很像人。企业的文化很像人的情绪和性格。前面讲过,人在紧急情况或不确定性问题面前,情绪可以比理性脑更快地做出反应和决策。同样,企业对于外界的反应,很多时候来不及或者无法由扮演企业大脑的管理层做决策,那么就要靠企业文化和价值观。人在遇到相似的情况总是发生相似的反应。这就被描述成了性格。比如看到稍微恐怖的电影就害怕得心跳加速,就被称为胆小。看到美女就兴奋得心跳加速,就被称为好色。企业如果在相似的情况也总做出相似的反应,那么就可以被称为企业的性格了。 + +极客时间:所以企业文化和“企业性格”有关,企业性格是什么样子的呢? + +汤峥嵘:企业的文化和价值观往往指的是企业希望自己的变成的样子。因此很多企业会把文化写到墙上,希望大家做到。而企业的性格是指在遇到真实问题时表现出来的结果。 + +企业肯定都希望能做到完美,对不足的地方往往是希望复制其它优秀企业的文化。 + +例如阿里有个“拥抱变化”的价值观和文化。阿里做得比较好,我认为有外因和内因两层。外因是当时外部环境在倒逼。做了B2B,马上要做淘宝和支付宝。几乎是完全不同的商业模式,而且是新模式。但人才不够啊,外面也找不到,那只能内部调整,拥抱变化。内因是轮岗做得非常彻底,彻底到把技术总监调整为人事总监这种程度。轮岗的人心态积极、正向。管理者充分支持新人,坚定业务目标。有意思的是,拥抱变化的背后,恰恰是团队对方向的认同和坚定。拥抱变化靠的是不变的目标。当然这个最终能沉淀下来,是因为不断有好的结果和回报,信心也就不断建立起来了。后面再遇到同样情况时,已经不再需要管理层做决策了,拥抱变化已经变成了自然反应,变成了性格。 + +再例如大家都知道谷歌有鼓励创新的文化,允许员工每周花20%的时间做自己想做的事。这个虽然一直有争议,但既然能沉淀下来,也是因为不断有好的结果和回报。很多优秀的产品都是从这里冒出来的。 + +但其它企业很成功的文化和价值观,不一定能复制到自己的企业。 + +拥抱变化的文化,很容易因为管理层缺乏坚定的决心或者员工的强抵触心态而失败。创新文化很容易因为产出过低或者过于自由与缺乏自律,而坚持不下去。总之,外部环境是否有利造就成功案例,管理层是否能长期坚持,员工是否能积极执行,都会影响文化和价值观的落地效果。 + +我认为和情绪一样,文化和价值观看似是虚的,但有着底层的逻辑和规律。因此我在思考打造文化和价值观的时候,应该怎么去找逻辑找规律? + +首先要总结出团队的性格。团队的性格不是每个成员的性格简单叠加而成的,而是这些性格的组合在不确定性问题面前的反应。因此要动态地分析。比如9个实感的人与1个直觉的人组合在一起,未必是实感型的团队。如果那个直觉的人负责做决策,另外9个实感的人负责把决策理性化并且执行出来,而且多次成功,这个团队的性格就是直觉型的。 + +其次是利用团队性格,打造合适的文化。一个偏直觉的团队比较适合打造成拥抱变化或者追求创新的文化,一个偏实感的团队比较适合打造成客户第一或者质量至上的文化。用性格去打造对应的文化,应该比复制其它公司的文化要容易成功。 + +极客时间:我试着去总结一下,首先,了解自己有一些方法,对自己性格的了解是很重要的,性格不同对你做决策,和世界交互都是有很大影响的。但是因为这四个维度其实是有区间的, 0-100 的百分比,比如很少有人是 100%内向,可能会落在某个区间内,而且毕竟是主观题,随着时间的推移,也许选择也会改变,所以 MBTI 这套性格测试其实它不是一个科学。但我们完全可以参考。 + +汤峥嵘:关于它是不是科学,有不同的观点。我认为科学的方法论是先提出一个理论,之后通过不停做实验去验证。如果实验数据和理论一致,那么理论就暂时成立。如果实验数据和理论不一致,那么理论就要被修正甚至推翻。 + +MBTI是有一套理论,也有一套用问卷做实验的方法。但它无法验证。因此我认为它不算科学。但它是一套自洽的体系,蛮像哲学。 + +为啥我会比较喜欢用这套体系呢?第一,大部分被测试者对结果比较认同。第二,在我们程序员这个人群中,ISTJ 或 ESTJ型非常多,很符合我的分类方法,说明大家思考的方式基本上是一样的。也就意味着,我们这些人做决策的时候也是差不多的。第三,利用我们创业的技术,有可能把原本无法量化的性格进行数字化,从而去做验证,或许也能成为一门科学了。 + +平时是怎么用这套理论? + +极客时间:我还有个问题,关于这块,你刚才有讲,在阿里的时候学的这门课,学完之后,对你非常大的启发,经过这个启发之后,有哪些具体的行为、改变可以再聊一下吗? + +汤峥嵘:具体的行为我想想,开始肯定是在阿里的时候,我那时候又做技术又做管理,针对团队:我给他们都做了一遍性格测试。那时就已经发现,原来我们很多人性格是一致的。我记得那时候我们还专门找了几个跟我们不一样的性格的人来做对照。 + +这个测试做完之后,不但让我了解了团队的性格,也让所有的做技术的同事感觉更近了,认可度更高了。后来我在其它公司做技术管理,一直坚持做团队的性格测试。 + +所以我建议,你做管理,有机会的话,用这套体系去测一测周边人,去了解跟你合作的人是什么性格,同时你也要明白,两个性格一摸一样的人不见得就一定是合的来的,俩急性子碰一块,一旦闹恼了,那不是更着急了?就这个道理,性格一样的人不见得就是合得来。 + +我前面说过,“理解他人”是理解他人做事的原理,这是终极目标。一旦理解了他人做事的原理,我们就完全可以解释他们的历史甚至预测他人的未来了。但在达到这个终极目标之前,你不一定能理解其中的原理,但至少可以理解一个事实,就是与你不同性格的人,对于同样的问题,反应可能是完全不同的。他的反应不是针对你的,而是他性格中的一部分。 + +这时候,假如他犯了错误,你可以为这个“错误”找个理由,去原谅他,这是个好事。第一,这个世界在你眼里可能更容易产生亮点;第二,真正就是因为你想通了,你明白了人与人的不同,你找到一个理由,给自己舔伤。我是这么过来的,自己要多练,说的不好听就是,你的麻木的能力就变大一点了,对所谓的“伤害”就无所谓了一点。 + +真的要开始理解别人为什么会这么做事,否则,特别是像我这样性格的人,就很容易陷在自己的世界里。 + +互动时间 + +关于职业性格的内容先聊到这,最后,我也想留个小问题给你。汤峥嵘发现自己深度睡眠为 0 后,很长一段时间,他开始每天关注的睡眠数据,去医院检测,以及用他们现在的心电衣产品做监测,通过调整室温、调整睡姿等(相当于每天变一个参数),最后把自己的深睡眠硬是给提升上去了。 + +你有通过深度了解自身数据,从而改变自己的经历吗?欢迎在留言区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/03闲话家常(一):半工半读的留学生活.md b/专栏/超级访谈:对话汤峥嵘/03闲话家常(一):半工半读的留学生活.md new file mode 100644 index 0000000..4a067d0 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/03闲话家常(一):半工半读的留学生活.md @@ -0,0 +1,141 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 闲话家常(一):半工半读的留学生活 + 编者按:从这一讲开始,我们进入第二个模块:职业成长。回顾汤峥嵘从美国留学至今的经历。美国求学工作十四年,他经历过职业快速成长期,也曾有迷茫。其中留学生活是他真正蜕变的开始,从清华退学,选择去美国留学是他人生中一个重要选择。这一节我们主要跟你分享汤峥嵘半工半读的留学生涯。 + +极客时间:你当年从清华退学去美国留学,关于这段经历,还想跟你聊聊细节。为什么会选择冒这么大的风险去做这样一件事情?当时是怎么考虑的? + +汤峥嵘:我们那时候想出国的人还挺多的,尤其像清华的,比我们出国早的,有一批人是靠着李政道发起的 CUSPEA 项目(China-United States Physics Examination and Application,中美联合招考物理研究生项目),拿到美国那边大学的奖学金去读书。那时候大家基本没有能力负担出国的费用,都是靠奖学金。 + +我为啥离开清华呢,这件事现在看来就太不理性了。我已经有了一个不错的大学了,但看到清华中还有一些更厉害的人,还在考托福、GRE,我也想去试试,就又在琢磨更好的事了,总是好奇,总是不满足,就是想做更厉害的事情,开始的想法就是这么简单。 + +这个事情回到咱们前面说的“理性”“非理性”决策来讲,就是因为,一种自我优越感的非理性的情绪影响了我,我就觉得自己挺优秀的,就想去尝试更多东西,有时候这个情绪对你是好事。所以我说非理性是很重要的,它给你的是信心,让你坚持选择。如果我冷静下来,仔细琢磨这事,肯定感觉这事不靠谱,那没有后来了。当然这个事的结果不错,我觉得靠的是运气。 + +所以做这件事的动力就是因为自己想去尝试更多有挑战性的事情,就是这样。 + +极客时间:所以那个时候,身边有一批人就在准备出国了。 + +汤峥嵘:对,很多都是大三大四才准备出国,因为要毕业,再去申请研究生。我算是在我们年级中比较早的,开始准备的时候才大二。 + +当时整个学校里面,比较有气氛,像俞敏洪的新东方那时候就是在清华里面到处办课的,现在回想起来也挺怀念那种感觉。那个年代年轻人是非常有想法的、有朝气的,无论做什么事情,大家都觉得好像可以做成一样。现在正好有点倒过来,开始躺平了。 + +极客时间:这一点确实有所体会。当时你是拿到了奖学金,然后出国留学吗? + +汤峥嵘:对,我那个时候申请的是国外的本科。奖学金那时候是这样的,第一,你如果是研究生,你给老板干活,老板给你发工资,这样变成了一种奖学金的形式,大部分是读了研才有这个,当时很多人是等到毕业了再去申请研究生。 + +第二,那个时候比较容易拿奖学金的是纯理科类的,就是数理化生这些比较好拿。我学的是计算机,计算机算是工科,就非常难拿,我后来是拿到了一个学校的半奖,不是很多,但当时也觉得不错了。 + +极客时间:那时候是特别想出国,还是说想去个更好的学校? + +汤峥嵘:可能都有,那个时候真的不是特别明确到底要做什么,但是呢,我是学计算机的,肯定有一点我知道,就是中国的水平跟美国还是差的比较远。那时候清华计算机系是中国最好的计算机系之一,但我一出国就感觉,咱们的硬件水平不知道差到哪里去了。 + +极客时间:你看到的这个差距都体现在哪? + +汤峥嵘:我是 91 年到美国去读书的,那会每个学校的学生都可以在一个开放性的机房用计算机,里面大概几百台 486,全是 486,免费可以使用。因为我们有时候要交作业是要打印出来的,你写一篇文章,一按打印键,在另外一个打印室里就可以领到你的东西了。那时候就觉得国外的硬件条件实在太好了。 + +那会清华的计算机系只有两台 286。我们记得大一学编程,我们有个词叫“上机”,用的都是小型机或中型机,学校给每个人分配一点机时,从登录进去那一刻它就开始给你计时了,你就在里面编程,编完程退出来,给你算你今天用了多少时间。我记得很清楚,我们那时候第一次学编程,整个一学期就只有一百个小时的机时,还得包括你把程序通过键盘敲进去交作业的时间。每次作业都要在纸上演练很多遍才敢上机。 + +但它有一个漏洞,它不是用到一百小时就马上给你停了,而是退出的时候才给你算多长时间。我们就是为了占点便宜,最后一次上机,半夜里看着时间过了一百个小时,也不退出来,就在那等着。那时候那里面也没游戏,啥也没有,但用超过了 100 个小时,我们就觉得占了很大便宜。相比之下,我们那时的硬件资源有多么匮乏。 + +还有在美国,每个学生都有一个 Email 账号,我那时候在国外和我同学联系还是写信呢。后来我跟他说,我这都是用 Email,我要给你们发 Email 行不行?交流就方便多了。因为以前真的只是靠写信的,一封信一个来回得需要好长时间。 + +然后他们就说,也有 Email,我给你发个 Email 地址试试,他们那时候是 IBM 格式的,Email 地址就像一个目录一样,全是斜杠。而我在国外用的就是咱们现在用的邮件格式,后缀是.edu。根本没法用Email交流。 + +那时候国外和中国的硬件、软件差距真的非常远,从计算机那个角度来看,真的觉得美国是天堂,我们这太落后了。但是这么多年,回过头来再来看,我们中国的发展真的是很快。 + +极客时间:去了美国后,你心情怎么样? + +汤峥嵘:先从去美国的这一段路讲讲,对我们这些连飞机都没坐过的人,这个开端注定不那么顺利。 + +第一件事,我在日本转机,我搞笑到什么程度?因为没有坐飞机的经验,也从来没用过行李推车。我可能是把护照放到推车上,结果丢了,就在日本机场。 + +准备要上飞机了,一看,我的机票护照去哪了?都不见了。我当时还算比较理智,第一时间想到去找警察。我一进去,所有人都站起来就给我鞠躬,有人问我需要什么。这点倒是对国外的印象不错。他们英语也不好,我英语也不好,我们就靠关键词交流。还没讲完,就看到有一个人跑进来说捡到一本护照,送进来一看就是我的,如果真丢那了就惨了,就卡在日本了(笑)。 + +第二件事,到了美国,我先是飞到纽约,下了飞机,在行李转盘等我的行李,我看一个黑人就站在那把我的行李帮我拿出来了,我觉得还挺好,结果他张口就跟我要小费。当时完全听不懂。我甚至怀疑以前学的是不是英语。虽然听不懂,还是很容易猜出来是要小费,我也只能心疼的给了。 + +你后面问我有没有孤独,我刚到美国,真的最大的问题就是我去的时候就我一个人,钱也不多,确实很孤独。我现在好多高中同学还有我的信,后来老同学们都跟我说,一看你就是很孤独,写了那么多信。我现在都能体会到那种感觉,你在学校里面,你会感觉这些建筑、草地、景色都非常漂亮。但是你心里特别紧张,因为我把学费交了就没钱了,完全不知道明天还有没有钱,所以我在美国打过各种各样的工,一边打工,一边上学。 + +极客时间:上学的时候,你都打过什么工? + +汤峥嵘:最早我们都是在中餐馆打工,因为那些中国老板英语都不好,他们要接美国人的生意,就比较喜欢我们这种留学生,会讲英文。 + +但那时候我刚去,英语还很弱,到了餐馆后,人家给我打电话点餐,我接半天电话也记不下来他点什么东西,把老板气坏了,说你到底是不是留学生,是不是大学生?我当时又怀疑我学的是不是英语了?怎么感觉他们说的我一句也听不懂。后来老板看我不行,就让我去送外卖。 + +我在学校里也做过工,学校管理比较严格,留学生可以打工的种类很少,而且一个礼拜最多能打二十个小时的工。像我做过的,比如在餐厅里面擦擦桌子了,收收盘子了,这是一种。还有一种在自助餐厅,有一个冰激凌柜,我给大家挖冰激凌,我想反正是公家的钱,我就很慷慨(笑),很多女生都比较喜欢我的服务,因为我经常给她们挖一个很大的球,她们就很开心。 + +那个时候打工经历对我帮助还是蛮大的,因为这就是接触社会嘛。 + +极客时间:在美国的学习生活怎么样?可以讲讲你在大学里的一些学习故事吗? + +汤峥嵘:我希望在美国读两年就能毕业嘛,当时就得算需要读多少课,排一排课表。我记得大学第一学期,因为英语过不了关,我就尽可能学对英语要求不高的学科,比如计算机、数学,这些我都强。 + +第一学期,我就选了一门心理学课,剩下的全都是理科。但是我后来发现学这一门心理学课的时间,比我剩下的课加起来总和可能还多,因为刚开始纯英文的,对听力各方面要求也比较高。但是我将来还想读研究生,所以还想拿个好成绩,最后这门课结果还不错,居然还拿到了 A。 + +后来呢,我还是有大量文科的课是必修的,讲几个我觉得挺好玩的事,比如历史,要求我们至少得修两门历史,其中有个课叫世界历史,学历史对语言能力要求更高,我就很痛苦,后来我想了一个办法。 + +我在国内学过中国革命史,我就想,那对美国人来讲,我们的中国革命史,就是世界历史啊。我就把我在中国学的历史课的大纲,翻译成英文,写了一份,然后拿给那个历史系的老师去看,说我学过世界历史了。老师居然认了,我的世界历史课就免掉了。 + +另外对我来讲比较难的就是音乐了,属于艺术类的课。这个音乐课老师一上来,先告诉所有人成绩是怎么组成的。一次期中加一次期末,占总成绩的 30%,剩下的 70%是要听音乐会,写听后感。这一下难度就上去了。再加上听音乐会还是蛮贵的,幸好我们学校边上有很多免费的音乐会,或者专门给学生会留一些免费的位置,后来我全都是听的这个。 + +其实音乐课对我来说受益很大,这个老师讲音乐史的时候,把音乐史、绘画史,建筑史一起讲,他说这就是一个历史。我现在都能大概背出来,比如说巴洛克时代的教堂做得都是非常工整(对称),非常繁华(点缀多、富贵)的,对应到这个时代代表性的音乐家巴赫,他的音乐也是节奏非常工整,但是旋律特别复杂。罗曼蒂克时代,音乐代表人物是贝多芬,当时的绘画风格和音乐风格也有相通之处。到了印象派时代,代表人是德彪西,他的作品不能听每个音符,要抽离出来听整体。很像梵高、莫奈的绘画,近看都是点,要站远一点看才能看懂。 + +其实到现在我还不懂这些东西,但是他讲这些让我觉得很受启发,感觉这才是真正艺术的本质,让我们不懂的人也能知道,原来它的原理是这样子的。虽然在美国我学了那么多理工科的东西,但是恰恰这种文科的知识让我感到非常有意思,确实他们人文的东西讲得很不错。这些大概就是美国学习生活对我的改变。 + +极客时间:后来你是怎么慢慢适应美国的环境的,不管是学习环境还是生活环境? + +汤峥嵘:开始我们中国人有自己的圈子,但是我想毕竟要跟美国人沟通,你得想办法融入美国人的圈子,还是有很多中国人跳不出来。首先,先过语言这关。第一个,我就看美国的那些肥皂剧,里面总有些笑声作为背景,听的多了,我就知道这个地方好笑,慢慢有场景感,这样我的语感慢慢就锻炼出来了。另外,中国的文化比较含蓄,英语从语言上是比较直接的,我也强迫自己调整过来。 + +我当时还问过什么时候英语才能真正熟练,有一个人跟我说,你什么时候晚上做梦用英文做梦,你就过关了。刚开始做梦,梦里还是中文。 + +另外一个就是我比较喜欢美国的橄榄球,因为我发现美国的男生除了谈工作,绝大部分都在谈橄榄球,橄榄球是在差不多在 9 月份到第二年 2 月份,主流的比赛又分大学橄榄球和职业橄榄球。到礼拜五,大家就赌一下,赌哪个球队赢。我觉得这个事特别有意思。大家周末看场球,看完了,周一再讨论结论,就感觉一周都有话题。我那时候就被他们带领着去看这个东西,然后跟他们就有话题可聊了。 + +我也尝试过跟美国人聊中国的文化,因为文化差异比较大,我讲多了,他们也听不懂,或者也不感兴趣,当然有一部分美国人对全世界的文化都感兴趣,我跟这样的人就会聊很多。但是百分之八九十的人,就是关注自己的生活,每天上下班,哪也不去,生活特简单。我就尝试去融入这种生活,去了解他们的文化。 + +极客时间:不管是学习还是打工,都要求你必须独立,这个经验是很宝贵的。除了刚刚我们聊到的你在美国的这些经历,还有哪些让你印象深刻的事儿吗? + +汤峥嵘:有一年暑假去纽约打工,当时找了个很奇怪的工作,是一个韩国人开的小超市,我做收银员,一般收银员都是女的,我开始不知道为啥让我去。去了才知道,在那上晚班,从晚上八点到早上八点,女的上晚班可能不安全。工作倒是很简单,就是收收钱。 + +但是这个工作里面有几个很有意思的点,第一,管事的领导是巴基斯坦人,带了好几个移民和几个土生土长的美国人,我还记得还有个健身教练,负责切肉,有个泰国小伙负责面包,我没事就和他们聊天。他们觉得我很新鲜,因为之前他们超市里从来没有中国人。 + +第二,我住的地方离我上班的地方挺远的,坐完地铁还得走一段路。有一次半夜回家,在路上,我远远看到前面有一个人,就觉得不太妙,再往后看,发现后面也跟了一个人,我就知道这俩人要堵我,我就赶紧往旁边跑。在纽约我经历的最危险的应该就是这么一次。我能看出来,这两个人就是要抢我,当时我还想,他们要抢点钱就给他们,也不至于把我怎么样,但心里还是怕怕的,就赶紧朝着旁边的街道跑了,我对地理位置的认知还是不错的。他们可能看我认路,以为我也是当地人,最后也没追上来。 + +还有,我记得,我们那时候住的条件是非常差的,都是各地来的学生合租在一起。大家挤到什么程度?有人睡在壁橱里,有人分摊卧室,但我就是最奢侈,为什么?因为他们都是白天上班,晚上回来睡觉,我正好倒过来,白天整个大房子就我一个人睡。这个经历对我影响很大,整个暑期,接触的都是这种性质的生活,以前打工也没有条件这么差,那次的条件还是蛮差的。 + +那时候留学生的条件普遍都很差,每到人家扔家具,我们就去捡,因为那会真的很穷。我先捡了个黑白电视,过几天看到有谁丢了个彩色电视,我再把彩色电视捡回来,这回我也可以把黑白电视丢掉了。 + +还有我们的席梦思床垫都是捡来的,开始捡一个弹簧老化不太硬的,过两天又捡到一个比较新比较硬的床垫,就是因为经常有人会扔很新的东西。当时不觉得苦,为什么?因为他们扔的比我们国内的条件都好,那时候就感觉美国好发达,后来回到中国再看咱们发展的增速,真的很快,但 90年代的时候,差距还是蛮大的。 + +极客时间:在美国的学习经历对你来说影响应该挺大的,必须要求你开始独立生存了,可以说说,这段学习经历对你的影响吗? + +汤峥嵘:最重要的就是教会我“自己做选择”,当时我最大的感受就是太自由了,一下子不知道自己应该怎么做,反而压力非常大,再加上我还要打工,所以说那个时候,人的成熟是非常快的,因为你要对学校的情况熟悉,要想办法存活。 + +如果让我讲一些道理,我觉得,我们中国的大学生也好,年轻人也好,有一点需要提醒自己,你要努力去想办法去找机会。因为在中国,从小学、中学到大学都不用自己选,课也都是安排好的。在美国读大学给了我一次特别好的机会,就是要求自己,所有的事我必须得认认真真做选择了。 + +这段经历对我未来的影响就是,当自己选择的路自己走过了,后面很多事情也不怕了,因为我知道所有事情都是自己前面选择的结果。 + +极客时间:这和国内是很大的反差,以你的经历,你会建议在学校的学生应该怎么培养这种独立? + +汤峥嵘:咱们国内很多刚刚毕业的学生确实缺少独立的性格,比如找到工作,特别是去了大厂,他们自然会认为,公司还会继续培训。应届生一开始工作,很多人第一个问题都是,“咱们公司培训在哪?怎么培训,培训什么?”感觉还是没有离开学校,还是卡在学校的体系里。 + +在美国恰恰是,在大学的时候已经没人带你了。我经常跟很多小孩子讲,我在美国找到第一份工作的情形,我刚去,老板把我带到我的座位上,说这就是你的电脑,这是你的座位,大概讲讲你负责的工作,我的第一份工作就是做互联网,老板就说这是我们的网站,你看看,这是代码,你自己看,然后就走了。 + +到下班时候,我再观察着,看看大家几点下班走。到第二天我已经知道几点钟来上班,几点下班,中午跟谁去吃饭,我就清楚了,也就这点事。 + +但在国内,大家就是往往喜欢看公司的流程是什么,需要别人告诉他规范,告诉他应该怎么干。 + +但我还是觉得人要想办法越早独立越好。 + +对于在校的学生如何培养独立的性格,我的建议就是去独立承担一些学校和家庭以外的责任。比如去餐厅打工,去当骑手,去开发一个App,去做一个公众号,去直播卖货。无论做什么,要有一个参与方都能接受的目标,并且你自己能为此负责。所以关键词是“独立承担责任”这几个字。因为要对结果负责,选择才变得重要。当这个责任要自己承担的时候,就不得不独立起来了。 + +互动时间 + +注:文中提到的 CUSPEA 项目,是指“文革”刚结束,彼时出国留学尚无渠道,申请美国大学必须经过 GRE 和 TOEFL 考试,而当时在中国根本没有 GRE 和 TOEFL 考试,中国学生也不知道美国研究院和大学招生的手续和规矩,这种情况下,华裔物理学家、诺贝尔物理学奖获得者李政道专门设计了 CUSPEA 项目,给中国留学生创造了这种暂时的、独特的、公正有效的留学渠道。 + +你在学生时代经历过哪些,对未来成长有影响的事?欢迎你在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/04闲话家常(二):匹兹堡6年与硅谷4年工作经历.md b/专栏/超级访谈:对话汤峥嵘/04闲话家常(二):匹兹堡6年与硅谷4年工作经历.md new file mode 100644 index 0000000..9b2f597 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/04闲话家常(二):匹兹堡6年与硅谷4年工作经历.md @@ -0,0 +1,123 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 闲话家常(二):匹兹堡 6 年与硅谷 4 年工作经历 + 极客时间:研究生毕业之后,你就留在美国找工作了嘛,之前也看到采访说,第一次投简历投了两百份,这个数量当时也算不少吧? + +汤峥嵘:应该是很多的,因为那个时候的简历是纸质的,要一份一份寄出去,光邮票也不少钱。美国的经济应该是在 94、95 年开始逐渐回苏,但是整体形势仍然不是很好,找工作并不是特别容易找的,我当时也做好心理准备了。 + +留在美国的原因,一个是因为 90年代的时候,美国的工资水平,比国内还是要高很多,我的第一份工作就是四万美金年薪,肯定比国内要高。 + +第二,因为正好经济形势也开始变好,我就看到了一些机会,包括我选择了互联网行业。这个选择有运气成分,但也有自己选择的成分,那个时候,我觉得我已经开始有意识地挑选自己的未来了。我在学校的导师他其中关注的一块就是互联网,但是在学校里研究毕竟是小范围,所以我当时就开始往互联网领域去找工作了。 + +其实我投两百份简历的难度在于每份都做了个性化修改,这个是花了我比较多精力的,我当然不是每个都改了很多,但我确实是根据招聘的需求,匹配后对每份简历做了调整。 + +我把我这个行为叫“过度努力”,我特别相信这个,我做所有事就是喜欢多做一点,我才放心。某种意义上这简历是为我自己做的,因为我也知道,我这么改一点也许差异不大。但我总觉得,这是我对这家公司的一个态度,我把我的简历按照他的需求做了个性化的调整,我的这份心意或许就能够被看见。后来到阿里面试,我也做了很多工作,这个是我的习惯了。 + +这两百份简历花了我很多精力,但是最后找的那个公司也是一个很一般的公司,并不是理想中的公司。 + +极客时间:这两百份简历后来有多少给你回信呢? + +汤峥嵘:不多,没几个。我大概记得这个公司给我回信之后,我就去谈 Offer,我问了人家能给到什么工资,人家说四万美金,我一想挺好,就去了,整个过程很简单的。我运气特别好,这家公司当时做的互联网模式,跟阿里巴巴最早是非常相似的。 + +然后呢,这家公司里当时也没有什么特别厉害的程序员,我去之后,老板看到我很努力,很快就招了不少中国人,都让我来管理。这点我还是蛮自豪,第一,我利用了自己的能力,改变了他们对中国人的看法。他们一开始找中国人的原因可能是因为工资要求不高,后来发现我们的水平还不错,而且工作态度都特别好。 + +第二,我最后做出的结果是远远超出老板的预期的。我去了之后,反正我的习惯就是说,我看程序写的不好,就顺手改了,其他程序员也愿意让我改,改完之后就变成我来维护了,后来到 80%代码都是我一个人在维护。 + +极客时间:当时你的直属领导会怎么管你?第一任领导对你影响大吗? + +汤峥嵘:你说把我招去的那个领导啊,我觉得还好,他对我的职业发展是没有什么直接的影响。因为他不太懂我的工作内容,都让我自己来干,但这个工作机会确实改变了我的人生。在他身上直接学到什么没有?技术和管理方面的,大概没有。但是他用人是挺开放的,因为他们之前没有用过中国人,他尝试用了中国人之后,觉得不错,对我也很信任,这点很好。 + +说起如何获取信任,其实特挺简单的。因为我们做的是一个美国的 B2B 网站,美国人也需要去找公司采购什么的,他们也开始尝试把信息搬到网上。我记得最开始,他们想做一个功能,就是鼠标移到美国的一个州,这个州就变亮了,颜色就不一样了,再往下一点,就能看到这个州里的所有公司和分类。我当时想估计网上也有好多可参考的东西,结果搜了半天,没找到,因为那时候互联网不是很发达,我就自己想办法弄。 + +这个功能的原理很简单,50个州就是屏幕上50个多边形。鼠标的坐标位置落在哪个多边形里面,就让那个多边形变亮。但几乎每个多边形都是非常不规则的,要把50个多边形都标记出来,其实是个体力活。这是最麻烦的地方。我就吭哧吭哧地把所有点都标记出来,功能也就很快做好了。但老板觉得很神奇,感觉这个很酷,很好玩儿,对他们做业务的人来说,这种东西拿出去做客户演示的时候非常有用。你看,信任就是这样一点点靠体力建立起来的,没什么捷径。 + +所以,你说他对我的影响是什么?也许我并没有直接学到什么,但我通过“独立承担责任”和“过度努力”,我得到了很多宝贵的财富。 + +极客时间:还有一个问题,就是 95 年刚毕业那会,美国互联网发展很快,你在这样的环境中,对当时互联网的繁荣有哪些体会? + +汤峥嵘:那时候互联网真的就是太热了,就拿我第一个公司来说,我们那个公司就是运气不好,如果我们公司再晚两年做这个业务,我们肯定就上市了。我是 95 年去的,大概在 96 年还是 97 年的时候,我们找到了一个当时在美国可以说是非常牛的 CEO。 + +曾经有一个软件,叫 Lotus 1-2-3,是一个编辑表格的软件,它是现在 Excel 的前身,Lotus 这家公司后来被 IBM 公司收购了, Lotus 的这个老板,就变成 IBM 的 2 号人物,很牛。但这个人因为自己一辈子做大佬,他不愿意在 IBM 呆着,他就从 IBM 离开了。不知道怎么就找到我们公司来了,就成了我们的 CEO。这件事让我们觉得很膨胀,觉得我们公司很牛了。 + +他来了之后给我们吹了一通,说互联网怎么好,应该怎么做,然后我们就很兴奋。我们员工最兴奋,因为我们原来办公室是一个大仓库改造的。老板就说,咱们是做互联网的,咱们得搬到市中心,后来就找了个很高级的写字楼,搬进去了。一把椅子就九百美金,条件超级好。 + +老板这人心还不错,因为我是带技术团队,他还帮我放到一个最角的座位上,两边能看到的风景最好,给我安排这么个位置,美国人叫“Conner Office”。结果我后来发现这个位置特别热,很晒,一点都不好(笑)。 + +那时候老板为了把他自己的事业搞大,他又在波士顿买了一家公司,把两个公司合并了。这相当于我们公司一下子壮大,我记得我们那时候匹兹堡大概两百多人,波士顿那边也两百多人,加一起一下变成四五百人的大公司了。 + +那时候我们经常要飞去波士顿出差,动不动就说你出差一周,花销可多了,都是住最好的酒店。后来烧了几个月钱,很快就烧完了。到最后一天,我们老板从家里给我们所有人开会,说,对不起,我们撑不住了,我自己还掏了一百万美金。公司宣布破产倒闭,要裁掉几乎所有人,留下几个人过渡。 + +前面我也说过,因为代码大部分我写的,我就被留下来,整个公司就留五个人。那个夏天,公司由我们5人用最小的代价运营,寻找重生的机会。后来果然找到了买家,公司恢复到了之前的规模。 + +极客时间:你在这家公司呆了几年? + +汤峥嵘:六年,一直到 2001 年,在 2000 年 .com 泡沫过了之后,我和我太太就一起搬到硅谷去了,从匹兹堡这个公司离开了。 + +极客时间:在第一家公司你是从什么时候开始做技术负责人,开始做管理的? + +汤峥嵘:很快,应该是半年左右就把我提到了管理的岗位,但那个管理不是很难,因为工作内容都是比较清晰的,我的团队成员也非常成熟和优秀,我布置任务他们就干,干完了我就交上去,比较简单,我们还是挺和谐的。不用花很多精力管人,比如像现在还看看他们什么性格啊什么的,那会都没有的。 + +极客时间:这段工作经历对你最大的影响是什么? + +汤峥嵘:这段工作经历给我打下了挺重要的基础,因为我自己就意识到,自己的技术能力还是蛮重要的。不管到哪,我是靠技术吃饭的,有了这个技术能力,公司倒了都不怕。 + +极客时间:我看你在硅谷的 4 年经历了很多家公司,你在硅谷的工作和匹兹堡这段工作相比,有什么不同吗? + +汤峥嵘:在硅谷真是感觉有一大把机会,我在硅谷呆了四年,换了好多小公司,在硅谷这四年我应该算是比之前更努力了,但我觉得自己还是遇到了一些瓶颈。 + +一方面,我不喜欢大公司,那时候硅谷很多大公司已经起来了,像谷歌、雅虎等等,但我还想找创业公司,我还拿过 Salesforce 的 Offer,那时候 Salesforce 还很小,但是因为离太远了,它在旧金山,就没去。 + +我的性格就是不太喜欢大公司,因为你得遵守各种各样的制度。包括后来阿里发展的越来越大,我都觉得待着不舒服,所以我离开阿里也有一部分这个原因。 + +但是我喜欢小公司也不是为了想去淘金什么的,那个时候对股票这个东西的概念还非常弱的。我不是在匹兹堡工作了六年吗,我们这家公司后来被美国的一家大公司买了,那家美国公司上市了,一上市,股票就开始涨,第一次股票发生在自己的公司身上,我才开始知道股票是什么,以前都不知道什么是股票。也曾经去买了一点,但买了就亏了(笑),因为涨到最高点,就开始跌。很多公司都是会有六个月锁定期,锁定期一过,大家就开始卖了。我们那时候也不懂,开始看着涨,就觉得肯定还可以无限涨,我就留着,最后跌到比自己买的价格还低。 + +我后来对股票就没感觉了,也不过分关注这块的利益,包括后来到阿里都是这样的。我感觉这也是个好事。因为你对外界的感知当中,这部分被你过滤掉了,你反而不会痛苦。因为你不会把它当成一个评价你幸福的标准。 + +极客时间:你在硅谷遇到瓶颈,具体是什么呢? + +汤峥嵘:这个瓶颈属于职业发展的迷茫吧。在美国那个时候我看到的程序员发展路径,一般是普通程序员到高级程序员,再往上就是架构师、CTO,大家比较追求技术路线的发展。 + +我在第一家公司做到高级程序员,而且我同时还做管理,到了硅谷等于我又重新开始做程序员了。而且到了硅谷后我发现,大家对 Manager(经理) 这个岗位是很看不上的,甚至说是歧视,就是说这个人做经理了,肯定技术不行了,他们会是这么认为。 + +我在硅谷换工作的频率,大概是每年换一个,到第三个的时候,我就变成是首席架构师了,满足了我心理上的期待,这公司后来对我做教育也挺有影响。 + +我当时就比较迷茫,想将来是做管理还是往技术上发展呢?我内心中是想做技术的,但是,总觉得我曾经还做过 Manager,就还想回去做。我那时候都考虑过要不要再去读个 MBA(工商管理硕士)什么的,也在寻找方向。 + +极客时间:刚好你提到在美国的教育公司,因为后来你也在 VIPABC 工作嘛,那在美国教育公司的经历,对后面的选择有影响吗?因为我看你在美国经历这几个公司有做教育的(McGraw-Hill Research公司),有做医疗(Neoforma公司)的。 + +汤峥嵘:医疗那家公司没什么影响,因为我做的部分属于采购系统,核心不在医疗业务上。我以前核心做的技术就是订单系统,电子商务这个方向,这也是为什么后来去了淘宝,因为订单在电商中是非常重要的。 + +这个教育公司呢,对我有一定的启发,这公司有意思的地方是他们做特别传统的教育,客户是美国公立的中小学。我过去是为了帮他们做互联网化,但是我也从他们那学到很多。我去了之后才知道为什么美国的教育比我们还是厉害一点的。 + +这家公司里面 90%的员工是统计学博士,他们出的所有考题,都要做到不带偏见,啥意思呢?举个例子,考数学题的话,语文不能太难,因为这个孩子可能语文不好,不能让他题都看不懂,单词不认识。比如发现某个题外国人的答案跟别人不一样,他们就要怀疑这道题有文化背景的偏见。2003 年的时候,就已经做到了题目不能对人群有差异化。 + +第二个,他们要想通过考题,筛选出真正的天才来,会根据每道题的答题结果,给你调整难度,那时候已经做得挺成熟的了。前几年国内在线教育中热起来的自适应考试,其实就是这个。 + +当时我就在那家教育公司大概做了一年。然后 2003 年的时候我去阿里面试,拿到阿里的 Offer ,2004 年回国了,运气还相当不错。 + +极客时间:回顾你在美国的 14 年,你自己有过总结么? + +汤峥嵘:我觉得在美国的经历可以分为三个阶段,学生时代是一个阶段,在匹兹堡工作是一个阶段,硅谷算一个阶段。 + +前两个阶段,我认为自己在发展上是没什么杂念的,就是简单的一步步往前走。到了硅谷,我发现在发展上反而不容易,人越多的地方,你见到的企业也多、高手也多,人就开始冒出各种想法来了。我在硅谷看到了众多的机会,也看到很多人发财,我觉得人生在这种时候是容易迷茫的。 + +极客时间:刚刚你说,第一份工作让你觉得自己就是靠技术吃饭,我想了解,你在美国经历的公司是怎么把员工一步步培养起来的?是公司培养还是靠个人的成长。你怎么看企业在个人职业成长中发挥的作用? + +汤峥嵘:我认为培养这个概念在美国是没有的,或者很弱的。美国的环境是个非常典型的不靠组织,而靠个人的风格,厉害的人会互相欣赏,他觉得你行,就会招你过去。我当初去硅谷,也是我之前在匹兹堡就认识的一个人,他也到硅谷工作,就把我挖过去了。 + +你刚刚讲这个问题,我想澄清个概念,我们常常会说公司怎样怎样,公司对你好与不好,但公司是虚的。公司本身无法和你互动。真正影响你的,是公司里的人,以及发生在你身上具体的事。所以我认为不存在什么公司培养人的这个逻辑。不要简单的把个体的结果归结到公司这种宏观和抽象的概念上。公司的好与坏与个体的好与坏肯定是相关的,但未必有因果关系。公司不是学校,没有义务培养人,也不可能培养出人。以我的经历,我相信无论什么时候都要独立,靠的都是自己先把自己的事照顾好,周边人照顾好,这个非常重要。 + +我经常讲,无论在任何大的潮流背景下,你只要足够优秀,总是有机会的。还有不要因为大环境热,某个领域热就去做这个,我自己的例子,到目前为止,我都觉得硅谷本身没有给我多少资源,反倒是匹兹堡这么个小的城市,这么个小的公司对我的提升无比之大,恰恰在这个小公司里,我发挥了自己的作用,但是在硅谷,机会那么多的地方,我就不容易想清楚自己的未来。因为每个成功的人都有自己的成功的方法,你也不知道那个是对的。 + +我们现在比较容易得到一些宏观的信息,比如企业股票涨跌、行业形势等等,但是我现在越来越觉得,宏观信息对我们个体的指导意义不是那么大,除非你这个人做得事情就是要利用宏观信息,如果你不是靠宏观信息吃饭,那我觉得还是先把自己的事情想清楚, + +互动时间 + +你对自己的职业成长做过总结吗?你从自己的经历中学到了什么?欢迎在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/05从排斥到拥护,我眼中的阿里文化.md b/专栏/超级访谈:对话汤峥嵘/05从排斥到拥护,我眼中的阿里文化.md new file mode 100644 index 0000000..d38513a --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/05从排斥到拥护,我眼中的阿里文化.md @@ -0,0 +1,133 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 从排斥到拥护,我眼中的阿里文化 + 编者按:从硅谷到阿里,开始于吴炯(阿里第一任 CTO)在清华 Email 群组发的招聘广告,彼时汤峥嵘对未来如何发展感到迷茫,遇到了瓶颈,而远观大洋彼岸的中国正高速发展,这些让他感觉心里痒痒的。面试通过后,阿里为他保留了一年的 Offer,2004 年汤峥嵘加入淘宝,工号为 2000多。 + +2003 年,汤峥嵘对阿里不抱很大希望,因为那时 B2B 在美国已经走下坡路了,他并不看好。直到吴炯和他说阿里巴巴要做个类似 eBay 的业务,汤峥嵘觉得找到了兴趣点,那时还没有“淘宝”这个名字。虽然心系淘宝,汤峥嵘应该也没想到进入阿里之后,一方面被阿里文化洗礼,一方面配合组织调动,阿里 8 年时光,在 B2B 就待了 6 年,经历了从淘宝、支付宝、阿里国际网站、日本阿里巴巴,最后再回到支付宝、淘宝这样的过程,充满“拥抱变化”的阿里特色。 + +回国初,作为淘宝主架构师,负责架构迁移工作,第二期的 PHP 转 Java,整个数据库结构重新合理设计,贡献之一是引入 SCM 体系,开始把质量体系做起来。刚从美国回来,第一感觉是条件很艰苦,7 月的杭州天气很热,空调经常坏,虽然条件艰苦,但做这个项目特别有意思,当时淘宝一天的 PV 是一千万,他们正在为能够支撑一个亿的访问量而努力。 + +2004年10月,上层决策要把支付宝这个功能从淘宝拆出来,变成一个独立的网站,独立的公司,汤峥嵘被调去做支付宝,负责支付宝的技术,目标就是 1 月 1 号上线。说起来叫负责支付宝的技术,但其实是光杆司令一个。 + +做淘宝架构迁移时阿里请了 Sun 公司(Sun Microsystems,太阳微系统公司,SUN 取自 Stanford University Network 斯坦福大学校园网的首字母)做咨询(Java 语言由 Sun 公司推出),因为支付宝技术没有正式岗位,汤峥嵘只好申请经费找Sun的外包。 + +支付宝上线之后,汤峥嵘就被调到 B2B 去了,彼时 B2B 有三块业务,一个是国际网站、一个是中文网站、一个是 CRM。汤峥嵘前三年负责国际网站的技术,三年之后,阿里和日本软银合资组建了一家新的公司——日本阿里巴巴,汤峥嵘任CTO。三年后汤峥嵘重新调回支付宝和淘宝,最后从淘宝离职。 + +阿里 8 年,对汤峥嵘之后的职业生涯影响很大,对阿里价值观从不适应到拥护,是惊讶于文化建设对调动人的积极性竟有如此大的影响力,他变成阿里文化的积极推广者。他认为阿里给他最大的价值是让他从一个工程师转变成一个管理者,积累了很多信心。 + +我们聊了很多他在阿里的故事,从中你应该能看到汤峥嵘心态的转变以及他的思考。以下为访谈对话。 + +对阿里价值观从排斥到认同 + +极客时间:你说在硅谷的时候,感觉自己遇到了瓶颈。回国之后,那个瓶颈你觉得解决了是吗?然后从职业方向上,就更多的偏向管理了吗? + +汤峥嵘:对,我开始偏管理了。在硅谷我面临的选择太多,让我有点迷茫,不知道往哪发展。回国之后,走上管理这条路也不是我选的,因为阿里一下子给了很大的发展空间,即便有很多人竞争,大家都有空间,都能往上走,这对我来说是一个好处。另外,因为这个公司发展很快,我只要跟着它的脚步走,也不用想很多。你看我在阿里辗转了这么多部门,待了 8 年,但我觉得时间过得很快的,所以当时在阿里比在硅谷让我更喜欢。 + +在阿里,主要遇到的问题就是要适应它的文化、价值观。我明显感觉到,在阿里从技术到管理的转变过程中,必然要走过文化认同这个台阶的,我觉得在中国,管理的一个重要部分是让团队的价值观匹配。 + +在中国做管理是需要把人的因素考虑进去的,看起来好像管理成本高,包括我自己也分析过,我在阿里到后期,我起码超过一半的时间是在做管理,但是反过来你再想,这个时间是浪费掉了吗?我现在觉得没有浪费掉,为什么?对一个人了解更多,在合作的时候就有非常大的价值,知道这件事给谁干能成,而不是只考虑岗位职责这个单一维度。 + +极客时间:从美国刚回来,对阿里价值观有个态度的转变,这个是怎样的过程?你也说过开始不太能接受阿里的价值观。 + +汤峥嵘:我对人都是认可的,但我对价值观考核是不适应的,因为阿里价值观考核要占到总业绩的一半,我就感觉我做得再好,才得 50 分,另外 50 分看起来是非常主观的评判。 + +但是后来深入了解后我就发现,做价值观的初心是非常好的,公司也知道我们可能不理解,那就花时间讲,让大家理解。领导也不厌其烦地给大家做培训,讲大量的案例,比如说“客户第一”,里面有一条叫微笑面对客户,有大量的案例给我们讲,我就被折服了。而且经过一段时间,我看考评结果,发现打高分的人就是优秀,打低分的人就是表现差。 + +极客时间:后来你认同这种方式? + +汤峥嵘:我认同,而且我自己给团队做了一个技术版的案例。技术团队怎么算“客户第一”呢?比如公司大了,肯定会出现有人分不清 IT 部门和开发人员的区别,经常有人会找开发人员修电脑,以前有人就说这事你找错人了,去找 IT。后来,我给他们提了要求。 + +我当时跟团队说,我认为正确的做法是这样,第一,别人有需求找过来,你是做技术的,有能力识别出这个问题,请你必须从头到尾跟到底,即便你不会,你可以拉着 IT 同事一起去,看着这个问题解决,因为你是知道 IT 部门在哪的。这个问题解决掉之后,你跟客户说,我们俩不一样的,他才是修电脑的,下次你找他就行了。这就是“客户第一”。 + +这个案例总结出来,大家就理解了。我说你有什么委屈,这个世界上不懂电脑的人确实很多,你爸妈可能也找你修电脑,你不修吗?你其实是有能力可以修好的,只是你觉得大材小用,或者说你确实不会修,没有关系,你可以替别人找到专业的人。这个过程恰恰是给你一个机会去和客户建立一个很好的关系,因为我们大部分技术人员是面对内部的。因为你解决了人家的问题,说不定人家给你提需求的时候,就很照顾你,给你提一个很好的需求,同事之间的关系都是靠这种小事情维护起来的。这是双赢,你既解决了客户的问题,又给团队挣到了一个高分数,何乐而不为呢? + +这个很像美国的案例法,参考案例是最直接的。阿里很喜欢讲案例,也影响我后来一直比较喜欢用案例去讲事,我觉得特别有说服力。 + +阿里有一句话说“为结果付薪,为过程鼓掌”,过程做得再好,没有结果的话,只是给你鼓鼓掌就好了,阿里还是非常结果导向的。但是结果做到了之后,阿里还要看过程,因为一个好的过程可以让好结果不停地复制,一个不好的过程得到某个好的结果也是偶然出现的,因为你方法有问题。那么同时考核业绩和价值观,就是同时考核结果和过程,各占50%。 + +阿里文化对我的影响 + +极客时间:咱们之前聊到阿里文化对你的改变,除了提到的《情商为零》的课程对你影响比较大之外,还有哪些让你受益的东西吗?这个部分还想再展开一下。 + +汤峥嵘:可以,我可以稍微讲讲这些。第一,价值观考核,这个背后反映的是阿里对价值观的管理体系,怎么把绩效管理融合在一起,阿里想得比较清楚,它也一直花精力宣讲使命愿景价值观。我现在自己创业,体会更深,你要让我在这个事情上投入这么多,我都觉得确实是舍不得的,因为短期内很难看出效果。 + +阿里从 1999 年就已经想到这个事,而且经过这么长时间的发展,已经做得比较深了,它坚持做这个事情,加上它业务又做得好,就形成了一个正循环了。我也反思,现在很多企业想要照搬这一套,但都不能完全成功。我曾经想在途牛、VIPABC 把这些东西搬过去,都不能说是成功的。所以我想这套东西可能就适合于阿里或者跟阿里特别像的企业,而且这套价值观的提出可能也只是适合 90 年代末 2000 年代初那个时代,我们刚刚进入经济快速发展时期,大家一切都是向钱看。 + +现在呢,年轻人已经不再那么单独追求金钱了,更追求品质,现在再去做价值观肯定要与时俱进了,你再去讲以前那个就可能有点过时了。 + +第二,我在阿里感受到“管理的温度”和“方法”,领导班子有办法让员工产生非常大的工作动力。比如说关明生(Savio,曾为阿里COO)早期是如何激励 B2B 的销售的呢?他个人很有文采,他就想了个办法,你达到某个级别了,他就给你写一首藏头诗,大家就觉得公司老板亲自给我写一首诗,这个是很珍贵的。我觉得这个就是特别“阿里范儿”的一个东西,别人很难复制,因为并不是每个老板都会写诗,都会做这种事情,这个是激励个人的方法。 + +另外,他很早就考虑到怎么让销售组织变大,因为做得好的销售,那些人往往会转管理,那些销售转管理之后,他仍然会继续做销售,而且很有可能这个团队主要的销售额还是来自于他,这对团队发展来说不利啊,Savio 就强行要求不能这么干,把销售 Leader 的签单权重降低,甚至不算,就强迫这些人去培训带人,把其他人给锻炼出来。这是要承受压力的,比如说把 10 个 Top Sales 转成管理,一下这 10 个人的业绩就没了,对公司来说,就会承受一段时间业绩的下滑。他就咬着牙去推这个事情。 + +阿里在管理上,这几件事对我的影响是很大的,所以从开始有点消极抵抗到后面积极拥抱,甚至到后来主动去推广,就是因为我认同了。 + +第三个,对我影响比较大的,阿里把所有 P10/M5 这个级别以上的人,拉到一起,归到叫组织部的这么一个组织里。然后定期开会、培训、讨论问题。组织部这东西很显然是把我党的这套体系给引入到企业管理里了,我觉得这很好。《情商为零》这个课就是在这里学的。 + +组织部对大家的要求是啥呢?因为你属于组织了,组织让你去哪你就得去哪。比如我属于 B2B 的,从法律上讲我是和 B2B 签的协议,你如果把我调到淘宝,我相当于从这家公司离职去另一家公司的。当然也会尊重个人意愿,但是这个组织部的存在就是想告诉你,从内心上你要和组织关系更近。 + +极客时间:这个话题很有意思,刚好借着讲组织部这个背景,前面聊到你被调来调去,变过很多岗位嘛,你有觉得不舒服吗? + +汤峥嵘:刚开始我去的时候还没有组织部呢。开始是有不舒服的,因为我本来是看好 To C,拿的 Offer 就是去淘宝的,最后让我去 B2B,我肯定会有点不舒服。 + +但是呢,第一,我觉得当时我也没有特别强的理由说,一定要怎么样,因为 B2B 也不差。假设那会有组织部了,组织部先发声的话,我可能转过去就更顺了,我可能都不会去跟陆兆禧(2004 年任支付宝总裁)再去争一下要留在支付宝。 + +你放到西方要说搞组织部这种东西,相当于你没有自由了,可能马上人家就不干了,所以我觉得这也是中国文化一部分,在中国这种文化环境下,我们绝大部分人,还是比较愿意接受组织安排的。阿里是把中国文化、人性的东西充分利用起来了。而且它做得好的地方还有,整个氛围是比较透明的,大家公开讨论问题,我能感觉到那段时间的成长是非常快的。因为别人出现的问题,我可能也会遇到,我就可以提前借鉴了。 + +刚才说到部门变动,这其实还是小事,我还算幸运,一直都是做技术的。很多人直接岗位就变了,技术变 HR 了,前台变销售了,销售变服务,跨度更大。但是即便变来变去,在不同岗位还能出色的人,就证明了他就是优秀,金子到哪都会发光。 + +马云那时候也跟我们讲过这个道理,他认为一个人在某个岗位工作到两年左右,就会变得缺少创造力,因为他对周边都熟悉了,就不再那么努力,没有那么创新了。这时候就可以把他换到一个新的岗位,让他焕发生机。要是优秀的人他就会先想,到了一个新岗位,我怎么创新,怎么做得更好。 + +这也是我在阿里换岗位的过程中,对我比较大的影响。要在比较稳定的时候去担忧危机,要在晴天的时候修屋顶。 + +回到组织部这个点,如果说其他公司或者团队能参考这种方式,把公司一群比较核心的人放到一起,让大家能够互相碰撞分享,凝聚力也会更强。像我们搞技术的人可能对业务人员的经历不清楚,其实业务人员经历的问题是最多的。通过这样的方式,技术人能更了解公司情况,从技术上也能做好准备了。 + +极客时间:后来你在途牛,或者 VIPABC 有用过组织部这套思想吗? + +汤峥嵘:这个没有完全用,我还得补充说一说能运行组织部的基础,在途牛、VIPABC 很多基础都没达到。能运行组织部,首先公司的规模得比较大,比如阿里那会已经分成三个不同的子公司了,马云为什么要组织部?我觉得还有一个原因,成立三个子公司,是说公司把你们三个推出去,你们各自奔跑,用各自的能力,把三个公司带起来。 + +但是呢,公司可以用组织部这样的方式,从虚的层面把大家框住,否则也许就变成三个完全分头奔跑的子公司了。这个就跟我党一样,各省去发展,中央不可能全管了,但是我还是有组织部,我也可以把人调来调去,这是一个道理,你可以把国家想象成一个大公司。 + +无论是 VIPABC 还是途牛,公司都没大到这个程度,而且都还是围绕一个主体业务,没有必要用这种方式。 + +极客时间:关于组织部你讲的这些我可以理解了。你也聊了特别多阿里让你受益的地方,如果让你说一个,在阿里工作这几年给你带来的最大的启发,或者触动是什么?你现在脑海里立马能想到的是什么? + +汤峥嵘:太多了,这个话题现在说说倒也是挺好的,你让我说最大启发,我想不到哪一件具体的事,或者具体某一条道理。但8年时间,阿里对我的影响是非常大的,把刚从美国回来的那个我改变了很多。我现在能做这么多事,我能够再去创业,靠的是阿里给我的财富。这笔财富不仅是经济上的,更多的是做人做事的方法、管理的理念、以及阿里校友这种近似血脉的关系。 + +我刚刚讲,阿里的文化建设上绝对是让我做了 180 度转弯,后来从微医、途牛,到 VIPABC,我都认为阿里文化是最好的、阿里文化是无敌的,就是这种感觉,它改变我能到这个程度,因为确实事实证明了,这套文化能够把人的主观能动性给调动起来。 + +另外,阿里的技术在这个整个互联网体系里面,我觉得是特别务实的。以前在硅谷做的技术方案是特别理想型的,要做就做最好的,创新成本很高。要说阿里有多少非常大的创新,不一定有,但我觉得在这个阶段恰恰是对的,要先活下去。务实就是说技术上计划好到底要投入多少。 + +我曾经还负责给阿里采购一套电子邮件系统,我就按照硅谷的方式,做了几套方案,有免费的,有收费的,还有软硬件一体的,要老板批这个经费。我记得,最贵的可能都是几百万一套。老板会问我很多业务的问题,这个功能有什么用?为什么要花这么多钱,我给他讲技术上的考量,他就觉得没有什么意义,因为公司发展没到那个程度。在很多这种技术选型把控上,阿里更务实,现在来看,确实是对的,很多技术发展很快,当初选择一个技术觉得很好,付出很大代价,可能过几年就过时了。 + +在阿里有这样的体悟后,我就知道在中国互联网创业公司,应该怎么去做技术了,我就不用老板再给我讲这些事了。甚至我作为 CTO,我会去压成本,就是我们能省就省,这个省不是说啥钱都不花,但是我自己知道,我是有能力让新技术在上面做叠加,但是没有必要为了一些面子在开始的时候把某个系统先做大,这对创业来讲就更重要了。 + +还有就是,我在阿里毕竟工作了八年,这八年自己成长过程中,也有很多痛苦的时刻,但同时又看到成果,不断给我信心。我前面也讲到,我认为一个人往前走,你的信心是怎么来的很重要,我想就是因为我过去做成功的几件小事情,然后复用这个经验,或者凭借这些成功给我的信心,不断向前。 + +你如果是个老司机,有十年都没出过车祸,出去开车你会觉得马路是很安全的,但是如果你昨天刚出了车祸,你就一定会觉得很不安全。客观上讲,这个世界只不过多了一起车祸,出车祸的概率几乎没有改变,但你自己的经历对你的影响,大到让你可以忽略掉所有的客观数据。 + +所以我认为阿里给我最大的价值是让我积累了很多信心。 + +极客时间:所以说,不断做成事,不断有成果奖励,对一个人的职业发展来说会有很大帮助? + +汤峥嵘:肯定是有,我觉得有时候你经历了一家好的公司,你感觉苗头还不错,你一定要多呆一段时间,因为它可能会持续给你带来发展。虽然可能这个公司很多事甚至都不是你做的,都是别的部门做的,你都会觉得跟你特相关,这些就会不停地影响你的人生。 + +但是如果发现苗头不太好,不如及早离开,因为它对你影响即便不是当下,对你将来,也许就会有信心上的打击。 + +极客时间:你曾经说在阿里最遗憾的事情就是,重构阿里国际站底层架构,原本计划半年时间搞定,但最后用了将近一年的时间,对业务影响非常大,还受过马云的批评。经过这件事,你才更深刻理解了“一边飞一边换发动机”的道理。你怎么应对这种“遗憾”的感觉? + +汤峥嵘:我的人生哲学是,对做过的事情没有什么值得遗憾的。因为遗憾这个词,还是有点悲观的意思在里面。我是觉得,既然发生了,就向前看。接受自己是一个特别重要的事情,觉得遗憾可能还是有点不够接受自己。 + +遗憾分两种,一种是这件事做完之后,假如让我重来一次,我可能会换一种做法;一种是明明可以做到的事情,但开始就彻底放弃,那可能是一种真正的遗憾。 + +但是呢,对当初重构国际站这个做法,我觉得我可以接受,因为我基本上尽力做了,我当时的能力水平也就是那个样子了,在管理上确实还不成熟,所以我就没什么好遗憾的,因为水平没达到。假如让我再回到那个时间点,如果我能力不改变,我当时肯定还会做那样的选择。 + +互动时间 + +聊一聊你所在企业有怎样的文化与价值观,你是否认同,对你有何影响?欢迎在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/06怎样才能遇到自己的“贵人”?.md b/专栏/超级访谈:对话汤峥嵘/06怎样才能遇到自己的“贵人”?.md new file mode 100644 index 0000000..031eaf1 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/06怎样才能遇到自己的“贵人”?.md @@ -0,0 +1,95 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 怎样才能遇到自己的“贵人”? + 极客时间:在成长过程中你有特别崇拜过谁吗?或者说心里有没有一个标杆式、榜样式的人物牵引着你? + +汤峥嵘:没有特别的一个人,我对大部分所谓“成功”的人都很敬佩,但没有特别的感觉。可能因为我是实干型的,我比较喜欢具体和我合作、对我产生影响的人,从他们身上学习东西。 + +比如说我前面提到的关明生(Savio),他会把自己的亲身案例分享给我,手把手教我怎么做管理;在途牛的老板,他们很擅长捕捉市场的机会,让我在途牛学到了怎么快速试错、怎么融资、怎么创业;在 VIPABC,老板对教育和技术很有追求,让我学到了如何追求极致。这些可能是投射到我身上的,给我带来的财富。当然在今天这个时代有很多名人,他们做过的事,我都会看一看,但是我个人还是更喜欢看得见、摸得着的东西。 + +我觉得从自身出发,我们要经常去跟在你身边的、比你水平高的人交流,你才有东西可学。通过实际的案例和数据,经过自己的算法,变成自己的东西,而不是看别人给你讲几句话就奉为圣经,你就觉得自己学会了。哪怕我们都非常喜欢乔布斯、马斯克、马云的名言,但我们看到的都已经是很久前,别人传了无数次的道理了,我觉得可能是被严重过滤过的东西。我不擅长处理这样的信息。 + +像怎么解决 35 岁危机的问题,我觉得很难回答。如果你给我一个具体的人,说他需要帮忙,那我也许能帮得上。我可以跟他说,你现在的处境可以试试那个,试试这个,他去做了,且成了,这种才是真正的改变。但是要说这整个群体未来何去何从,怎么发展,我就是讲得再多,也改变不了什么。也许有这些困惑的人自己也有体会,听过很多道理,但还是改变不了自己。 + +我们这些凡人真的就要靠周边的贵人来帮你了,不要期望看一本书、听一个故事、听一句话就能改变自己的人生,大家心里都明白,这是不现实的。 + +极客时间:但是大家确实喜欢听这样的故事,或者说高人的建议,这也是为什么这样的内容、这样的问题一直会存在。 + +汤峥嵘:当然,故事都很精彩,我自己也喜欢听,有时候看到某个视频讲这些也容易激动一下。但是过了一段时间,我就发现这个事已经忘掉了。还是得靠实在的人和事,我自己的路一直是这么走出来的。 + +极客时间:几次提到关明生,你当时和他接触多吗? + +汤峥嵘:我在阿里时候不算多,反倒是离开阿里后接触很多。他是给我影响很大的人,他特别谦逊,而且很鼓励我。我刚去的时候,我的工资是比硅谷那边是降了一半的,但是比国内大部分人还是高很多,他就经常跟外面的人说,我们是把汤峥嵘给“骗”过来的,人家还降薪来支持我们什么的……他给我很多正面反馈来鼓励我。其实根本不是骗过来嘛,是我自己愿意回来的,但他给别人介绍的时候就会这样说。他这样做就给了我自信,给我很大的精神支撑。 + +我记忆最深的一次,是我后来在 VIPABC,那会经常有一些对外部企业的分享,有一次一个银行的高端客户到我们公司来,结果没想到他就在里面。因为我们已经有好久没见了,我也不知道他在,那次就突然见到了。 + +我那时候正好做 COO 了,他不是以前阿里的 COO 吗?我就跟他说我现在做 COO 了,他说太好了,我要教你怎么做 COO,给我讲很多。他就是特别喜欢培养人,把自己的东西教给别人,这点上他跟马云有点像,喜欢做老师。他做的每件事,很多细节都能让你折服的,确实非常难得的一个人。 + +极客时间:从更具体的角度来说,吴炯和关明生,他们两位在哪些层面上对你会有影响? + +汤峥嵘:我认为每个人的人生中都需要有贵人相助的。因为每个人在一个阶段到达天花板的时候,就需要有人能带你看到上一层的风景,贵人就是把你拉到上面让你看一眼的人。我的贵人就是吴炯和关明生,他们两个人让我看到更高水平的人是怎么干的之后,后面就靠我自己的悟性了。后来我自己也有意识去帮别人一把,帮一些我之前带过的人。我看到他可能需要被拉一拉的时候,我就去拉他一把,至于他能不能接受,能不能悟到我的意思,就靠他本身的能力了。 + +吴炯是两次把我带到下个阶段的人,第一次是他从硅谷把我带到阿里,第二次就是他把我从阿里拉出来,到了微医(挂号网)。而且第二次把我拉出来的时候,我就相信这可能是命运的安排,把我带进去,再把我带出来,不是挺好吗? + +而且我和吴炯当时在阿里并没有非常密切地沟通,他既然在这么长时间都没有找我了,他怎么就想到我了呢?肯定是有某种缘分又到了,那就再一起合作一下。去了挂号网,我跟Savio也接触更多了。 + +所以,什么是贵人?天天在你身边的不一定是贵人。我认为贵人往往是,不知道哪天就神秘出现的一个人,然后给了你一个很大的帮助。我认为这个因果可能是在你不停的努力中慢慢积蓄的,绝对不是你想今天找到就能有的。 + +你要发自内心的想“我就是想去跟别人学习”,而且你对人家是没有期望的,如果你是这个类型的人,贵人跟你就有缘。反过来想,很容易明白的道理,假设你去帮另外一个人,你肯定不想帮那个特别想从你身上获取的人。而当你发现有个小朋友很厉害,老想向你学习,但是从来也没管你要过啥,可能经常还给你送东西,那你对他就容易有好感,有好事就会想到他,我觉得道理是一样的。 + +所以,首先开启自己想学习的心态,但别人是不是收你为徒,真的是靠缘分,靠运气,而且靠你自己的努力。你做得多了,高僧自然会收你为徒弟。 + +随着工作年龄增长,所有人都会发现,我们自己是非常渺小的,全靠我们自己努力还是比较难的,往往要靠外力相助才能走得更远。也许你这辈子就自己奋斗,也很好。也许你运气好,就能往上跳一层。有的人天生就是富二代,含着金钥匙出生的,他就应该去做更好的事情,也应该做更大的事情。这个世界肯定是不公平的,这个世界一定不是把所有的资源公平分给每个人的,我们必须得接受这一点。 + +我们在宇宙中本就是一个尘埃,从这个角度想,接受自己的渺小,这个时候你就是无所谓的,当你看到别人很厉害,那有机会就去学习就好了。 + +极客时间:你会有意识地向这样优秀的人靠近吗? + +汤峥嵘:肯定是喜欢跟优秀的人在一起,物以类聚,人以群分,要想成为优秀的人,就得跟优秀的人在一起。和优秀的人在一起,其实是有压力的。特别是他们指出你身上缺点的时候。相比起来,刷刷成功人士的心灵鸡汤,似乎感觉好很多。所以成长一定是有痛苦和代价的。但是当你被比你优秀的人接受以后,你就知道自己离优秀又近了一点点,你的信心也多了一点点。 + +极客时间:成长过程中,很多人讲要顺势而为,要知道大势,跟风而动,看重环境对自己的影响。有个现象虽然老生常谈,还是想聊一下,就是大家都想进大厂这件事,国内特别明显,都想去大公司,大公司意味着更有前景的业务,更牛的人。这确实是一个刚性需求。你觉得大家在规划职业路径的时候,应该先去大公司,还是说在小公司会更好,总之要有一种成长的方法。 + +汤峥嵘:我可以说说我的看法,这个点是因人而异的,回到性格上,像我这样性格的人,可能恰恰适合先在小公司,再去大公司,为什么?因为小公司能够让我发挥更大的价值,让我更有自信。 + +大公司可能周边人都很强,你在大公司里分割那么一点小事情,做得可能更加机械。但是如果你这个人是特别善于捕捉外部的信息,从性格上讲,比如说你是一个直觉型的人、外向型的人,你可能去大公司之后,很早就感知到大方向了,你就能给自己规划好未来,那这样的情况去大公司更好。 + +这跟创业本质一样,有的人很容易感知到未来的变化,而像我这样的呢,都是完全靠自己经验跑出来的,反而在小公司积累的那些信心对我影响更大。而且,还是我前面讲过的,公司是一个很多人组成的平台,我还是更看重公司里的某个人对我的影响,举个反例,假如你去到心仪的大公司了,但是你碰到一个很差的老板怎么办呢?可能因为他的影响,你的自信心一下就被打击到了。所以确实有太多不确定性在里面,不能把复杂问题简单地黑白化。 + +归根结底,还是要了解自己,你自己是什么样的人,喜欢什么样的环境,在去大公司或者小公司之前,把公司情况、团队情况、你自己的情况统统考虑进去,再做选择,而不是盲目地认为公司大小对个人就有决定性影响,还是要关注具体的人。 + +极客时间:嗯,这个可以理解,比如在小公司,你已经是技术最厉害的人了,而且你的自驱力很强,你自己就可以让自己一直往前走。但对于很多人来说,他可能需要一个师傅或者领导带着他往前走,这可能是两种完全不同的人,不同的状态? + +汤峥嵘:有人能带你,教你一些东西,这是运气,但还是要靠你自己的努力,才能遇到我们讲所谓的“贵人”。你说的这种需要师傅来牵着走的呢,我认为他就是把自己框住了,自己没有努力过。现在很流行选择大于努力的理论,但我更相信努力。因为只有努力是自己可以控制的,选择、机遇、贵人、天分等都是自己无法控制的。 + +我在第一家公司工作的时候,我不是技术最厉害的人,我只是最钻牛角尖的那个人。技术牛人也大有人在,只是说那时候大家的技术平均水平没那么强。 + +那家公司的老板后来招了一个天才进来,真的是个天才,非常厉害,因为他太厉害了,什么都不干,我就在那苦干,遇到不会的东西就去找他,他都能给我解答。包括 Java 刚出来的时候,他自己不写程序,但是很快就把 Java 核心搞清楚了,那是真正的天才。 + +但是他后来发展很一般,公司淘汰人的时候他也被裁掉了。我是很多年之后跟他有联系,我发现他也没有变得所谓的“成功”。我很震惊,因为他的智商绝对是百万分之一的那种,可以轻易碾轧绝大多数人。 + +认识到世界上确实有这么高智商的人,而且他在你身边工作过,就越让我感到,我们这些凡人就得靠努力,没别的办法。刚刚说到要靠别人推着走的人,你又想要好东西,你得想想凭什么呀?你是真的没有能力吗,还是自己没努力过就觉得不行?技术人大部分的工作,都是脑力劳动,你要是说,因为脑子的问题自己做不了,那就没办法了。你要觉得自己不是脑子不行,做不好的原因无非就是没别人熟练,那就多练嘛。 + +别人有 20 年工作经验,和你没有工作经验的比当然比你厉害了,但是你想要,当你有 20 年工作经历的时候,要做到比他厉害,这其中怎么努力就是你自己的事了,不管是多付出时间还是怎样,我认为没有什么不可以去做的事,不要自己把自己先框住了。 + +极客时间:今天,成功这个概念是被高度狭隘化了,拥有财富、权利、地位的人往往被叫做成功人士。我觉得你其实也在表达,自己努力,同时拥有好运气,才达到成功。我也想探讨,一个普通人怎么才能不断靠近自己心目中的成功呢?除了上面谈到的关于个人努力、和水平高的人交流、向优秀的人靠近,在做选择前充分了解自己之外,你还有什么感悟和切身体会么? + +汤峥嵘:我不觉得自己有资格去回答如何成功这类的话题,甚至都没有资格去定义什么是成功。但是我可以分享一下我对人生的一些观点。 + +在信息技术高度发达的时代,我们被少数所谓成功的人搞得很焦虑。我们觉得自己很客观,但其实我们被焦虑的情绪控制着,我们很主观。简单地做个数学计算。今天地球上有70亿人,而我们每个人知道的成功人士应该不超过7000人吧。那么成功的概率最多也就是百万分之一。这和中彩票也不相上下吧。其实绝大部分人都是和我们一样平庸的人。买彩票不中肯定是可以接受的吧,那么同理,平庸也是完全可以接受的啊。 + +我前面讲过,其实情绪本来的作用是让我们逃避危险,让我们生存。适者生存,才是大自然的普遍规律。能存在,本身就是一件很了不起的事情了。我这里不是让大家变得佛系,放弃努力。而是不要被情绪绑架。试想你是一只龙虾,在龙虾的世界里,你靠不断地战斗获取了同类的认同,成为了龙虾中的王。而在人类眼中,那又怎么样呢,你不过是一只龙虾而已?更不要说在大自然这个造物主眼中你是什么了。情绪不过是大自然赋予龙虾的一种生存能力而已。我们现代人既然没有了生存的危险,就尽量放下情绪,放下焦虑。当然这个不容易做到。我的解决办法是我最擅长的领域:数据。 + +其实我是有轻度焦虑症的。我几乎每天都在用我们的产品测量自己的情绪,我在睡觉的时候要比绝大部分人紧张焦虑。但是当我知道自己的这个问题,并且可以量化以后,我反而觉得不那么焦虑了。因为焦虑的底层逻辑是对不确定性和风险的担心。我通过数据对问题的定位比过去清晰了很多,也就意味着解决的方案也清晰了很多。我在用各种方法减轻自己的焦虑。我们已经把一些有效的方法结合到了我们的产品中了。 + +当我们接受了自己的平庸,了解了自己的情绪和性格之后,我还是鼓励大家积极向上。这就回到了之前提到的两个我的价值观,“过度努力”和“独立承担责任”。过度努力的核心是积极争取,不计较得失,也不让自己后悔。独立承担责任的核心是接受任何后果,不把期望值放到其他人身上。我认为把这些我们能控制的事情都做了,那么就看运气了。运气好的话或许会中彩票,但大部分时候不会,那也再正常不过了。 + +互动时间 + +在职业成长过程中,你有遇到过自己的“贵人”吗?他曾经对你产生怎样的影响?欢迎在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/07选行业秘诀:技术是否能发挥重要作用?.md b/专栏/超级访谈:对话汤峥嵘/07选行业秘诀:技术是否能发挥重要作用?.md new file mode 100644 index 0000000..15d66c5 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/07选行业秘诀:技术是否能发挥重要作用?.md @@ -0,0 +1,121 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 选行业秘诀:技术是否能发挥重要作用? + 编者按:从阿里到微医、途牛、VIPABC,再到创业,汤峥嵘每次的选择都是积极向前看,离开一个地方到下一个地方是因为有了更好的机会,他永远希望选择更好的机会。每一次换公司,即便行业有所不同,他都遵循一条主线,那就是:业务能否因为技术的影响而产生显著的变革,如果技术能发挥重要作用,那么个人投入就有价值。 + +这一节,我们主要探究技术人在选择变换行业的时候,应该关注哪些方面的信息。以下为访谈对话。 + +每一次选择,都向更好的机会看齐 + +极客时间:在阿里待了 8 年,离开的时候,有没有自己对这段经历做总结?后来到微医、途牛、VIPABC,再到创业,每次决定要换一家新公司的时候,而且这些基本上都是不同行业,你都是怎么去做选择的? + +汤峥嵘:我其实很少因为所谓的要对过去做个总结了,就要反思自己。我比较实用主义,我总结的目的还是希望接下来能用上,比如工作中需要,我就总结一下过去有没有可以借用的经验,这样印象是最深的,否则就变成一个形式上的总结,给自己一个所谓的交代。比如说这次你们希望我能聊一下过去,才“逼迫”我有一个机会去总结一下,否则我平时自己空下来挺少去琢磨这个东西的。 + +在阿里8年后离开,那个时候我没有特别明显的遗憾的感觉,可能是因为我要去的这家公司(微医),又是阿里的两个 O(前 CTO 吴炯、前 COO 关明生)在里面做重要角色的,我也没有觉得那是一个特别大的改变,后来阿里几位离职的同事来和我一起干,我们仍然还在阿里的这个光环下在做事。 + +至于离开去下一个地方,有人可能觉得说,因为对上一家公司不满,就找更好的机会。本质上找更好的机会是对的,但不是因为我不满。我绝大部分过去的选择都不是因为对前面的企业不满意,而是说在比较两个机会的时候,下一个机会我认为明显可以给我更大的提升,那我就选择去下一个地方了。我的人生观还是积极向前看。 + +极客时间:因为你在微医时间很短,一年就离开了,事后分析,是不是觉得当初并不是一个正确的决策? + +汤峥嵘:不能叫不正确的决策,而是说当时在微医很多想做的事情,都没有条件,我们做得太早了。我觉得,我们当时想做的事情,如果搬到今天可能就有条件了,我认为是这样的。所以我为什么选择离开,也是这个道理。 + +从我们做互联网的产品和技术角度,那个时候很多条件都不成熟,比如说你没有大数据去算很多东西。你也没有好的的穿戴设备获取患者的健康数据,当时主要的一个产品就是挂号网,就是帮大家去挂个号,一个过于简单的模式。 + +从公司老板、业务团队的角度看,还是有很多可以做的,比如整合医疗资源之类的。但是我从技术角度没看到能发挥什么更大的作用。我之前采访中也讲过,我做选择,换公司,我有一条主线,我认为这个公司做的业务,或者是这个领域在这段时间内,能通过技术进行比较大的变革,我觉得投入就有价值。 + +我刚加入的时候是觉得,我们能把挂号这个业务做起来就很好,因为那时候心中觉得,这件事是个好事,能让大家足不出户就挂到号。但是业务推出去之后发现不对的地方了,稀缺的专家号一上线就被秒杀,被黄牛抢走,导致那些真正需要的人反而挂不上号了,因为我们做的挂号当中,主要的特色是能在三甲医院挂号,中国人就是缺的号就是三甲医院的专家号,别的号也不缺。我当时就觉得这有点事与愿违。 + +经过这段经历,我也就总结出一个道理,互联网应该是解决长尾资源的问题,假如这个资源多出来了,互联网可以帮你卖掉,但是如果本身就是个稀缺资源,就不适合放在互联网上,互联网恰恰是要卖那些不太好卖的东西。 + +我当时就看不出来,如果再往下走的话,我们这个技术,到底能对这个行业产生多大的变革了。后来也做了很多事情,无非就是把线下医院打通,都是很普通的一些技术,甚至去找个外包团队就直接把它干了。从技术上讲,因为整个医疗体系的技术是非常落后了,这个事情就变成把落后的医疗体系稍微往前赶一赶,就行了,我觉得这就完全不是我想象的,能够靠技术让行业产生变革。 + +而无论是途牛,还是 VIPABC,技术对商业的变革都产生了很大作用。虽然它不是决定性的,但是没有这些新的技术,这些商业肯定做不起来。当时微医是不具备这样的条件,也不能怪微医,也不能怪我,就是我们都试过了,尝试完了之后发现不成熟,那我就换一换。 + +我的性格上是这样,我是有意回避一些我认为悲观的东西,我觉得当你看到太多负面东西,或多或少就会在你脑海里沉淀下来,对你做判断会有影响。 + +极客时间:可以说一些细节吗,你会回避一些不太好的东西,无论是生活还是工作中,怎么回避负面信息? + +汤峥嵘:所有东西都可能有不好的一面,举个例子,在微医刚刚接触医疗,可能会质疑,中国医疗体系怎么这么差?值不值得再去把它给改变一下?确实我们跟国外差得还是蛮远的。 + +我们那时候得到医生给我们反馈的数据,从医生角度来讲,有 70%的误诊率。什么意思呢?就是这个病人本不应该是给他看的,但是挂到他那去了,来了 10 个病人,只有 3 个病人是应该给他看的,为什么?因为中国挂号体系是开放式的,哪怕很小的病,一个普通医生就能看,但大家也想挂最牛的专家,但对那个最牛的专家来说,可能就是浪费了他的资源,他本来可以看很多疑难杂症。到今天为止,还是这样,你要从负面去看,很悲观。但是从正面看,改进的空间不是很大吗?但凡我往前推进了一点,不是就赚了呀。 + +我相信所有的问题都有解决方案,所以当你看到的问题越多,那就说明你的机会越多。对纯技术人来说,你就是要去解决公司里最难的问题,这就是你的捷径,你就会成为大家心目中的专家,就像阿里的多隆,最后就变成“神”了。 + +一旦你真的解决了几个问题,打出了名声,“疑难杂症”就会主动找你来了,而比较平庸的人都没这个机会。最牛的专家,根本不缺用户,不缺流量,普通人可能没有机会解决难题,牛人天天都在解决难题。所以,为什么我说要乐观?给我们问题,就是在给我们机会。 + +极客时间:你刚刚讲到互联网应该解决长尾资源的问题,可以再解释一下这个长尾理论吗? + +汤峥嵘:长尾理论最先提出来是用于描述像亚马逊、Netflix 的经济模式的(美国《连线》杂志主编在 2004 年提出,主要观点为:如果把足够多非热门产品组合到一起,实际上就可以形成一个堪与热门市场匹配的大市场)。 + + + +拿亚马逊举例,以前在实体书店,极少数的书籍成为畅销书,书店营销就以畅销书为中心,越畅销的越摆在靠门口的地方。但是还有更多书“藏”在书店的深处,可能没人看,销量甚至为 0。畅销书虽占很小一部分,却产生巨大经济效益,但如果能把非畅销书的长尾销售额都加起来,也许可以超过头部的经济效益。这就是互联网能带来的好处,这些电商公司也都能赚到钱了。 + +但是挂号这个业务不一样,某个时间段,一个医院一共可能才那么多号。原来大家排队,可能周围几十、几百公里的人来抢,你现在放到网上,全中国的人不就都来了吗?瞬间就抢没了。后来我做教育也遇到同样的问题,好的老师就会变成稀缺资源,如果让大家抢,一定都抢最好的老师,这就会导致很多客户都不满意:怎么我就没抢到金牌的老师?所以,具体业务具体分析,到底需要解决什么样的问题?有的问题就不太适合用互联网解决。 + +技术驱动业务,经验与教训 + +极客时间:刚刚你聊了自己做选择时遵循的原则,一个是向更好的机会看齐,另外一方面,就是看技术到底能发挥多大的作用。你也说过在途牛和VIPABC技术是发挥了重大作用的,因此想聊一聊怎么才算技术发挥了重要作用。先从公司业务发展角度看,你觉得应该是技术驱动业务,还是业务来拉动技术? + +汤峥嵘:我先说结论,我认为有些技术本身可能就会把业务给颠覆了,都不是驱动,一定会有。但是你在没想好业务模式的情况下,就去期待所谓“颠覆”,这是不太现实的。途牛和 VIPABC 会比较典型,特别是 VIPABC,我们是有点想走技术驱动业务的这个模型的。 + +我觉得这个话题很有意思,技术人员比较容易陷入到一个自我畅想和狂欢中,都希望造出一个“原子弹”(颠覆性技术),然后就变成业务之王。但是我认为这个不太可能,你做技术的还得听业务的需求,但是牛人可能就会在做业务需求的时候,发现技术颠覆性的苗头了,这个时候就可以跳出来说,我本来是为这个需求去做的,但是我打算拿这个技术干别的去了,这个是有可能的。 + +像国外的很多公司就是这么干的,很多我们认为并不是一开始就想好的,比如说,我看最近很多视频都在讲乔布斯 15 年前发明了 iPhone。你说乔布斯当时能想象出今天的 iPhone 影响有这么大么?他当时就觉得 iPhone 就是一个超级牛的iPod音乐播放器,加一个超级牛的电话,加个一个手机上就可以打开的浏览器,这三者的组合,对那个时代来说,这已经非常颠覆了,但是没想到今天的手机已经远远不止这三样了。我是觉得连他这样级别的人创造出这么牛的技术,他在当时的场景下也还是为了业务。 + +极客时间:你说过途牛和 VIPABC 技术驱动业务比较典型,途牛的业务,是怎么体现技术驱动的? + +汤峥嵘:从技术的角度来讲,我认为途牛这段经历中,技术是对商业产生影响的。 + +首先,我们有自己本身业务的创新,另外,当时行业内几家竞争公司包括携程、去哪儿都在做技术创新,大家互相比拼着、共同做出来的一些技术,一定程度上改变了旅游业的形态。 + +途牛本身的创新来自于跟团游放到线上去做,之前大家认为跟团都是线下的、非标准化的产品。在途牛我们当初的想法就是,能不能把这种非标准化的东西拆开来,做到可以对比。但因为出发、回程的时间不一样、机票价格不一样,去的景点也不一样,这是非常复杂的,也很难比对。那就算不可比,我们能不能把里面的组合透明出来,供用户选择呢?所以我们又开始拆,比如你去旅游一次,肯定含交通、酒店、旅游景点门票,假设还有个导游。 + +当时,我们做了很多欧洲游,因为欧洲非常规范。就拿导游费这一项来说,可以做到透明。 + +因为你知道在那段时间,线下旅行社很多都已经被做烂了,特别在国内,打着不花钱的噱头,甚至还有说跟着去旅游一趟,他给你 50 块钱。但等你上了车,就拉着你去购物了,这些导游是靠购物来返点,你不购物他就不高兴,就不给你好好导,你就很难受。 + +我做了途牛之后,我更深入去了解整个链条,都变成什么样呢?相当于旅行团先找导游,说我现在有个团 40 人,导游你要不要?要的话,导游要先付旅行社钱,旅行社才把这些客户给你,这等于客户变成流量了。比如说导游一个人付两百块钱,他总得在每个游客身上赚回来两百吧,所以就得带你去购物,才能返点。碰到死活不买东西的,当然导游拿他也没办法,但是态度就会比较差嘛,这就是一个非常恶性循环的过程。 + +后来,我们做跟团,尽可能都是做这种“0 购物团”。像欧洲呢,第一,他们有一个导游协会来管这些导游;第二,你去欧洲所有地方购物,价格都是一样的,它们会记录是哪个导游带人,游客都花了多少钱,所以欧洲整个旅游系统就非常好,旅游体系比较健康。 + +欧洲游我们当时就是这么拆的,加了个导游费用,因为游欧洲确实需要导游,玩一圈经常跨几个国家,多国家语言。欧洲很多有特色的教堂建筑,确实需要导游给你介绍,这样就把整个旅游当中,所有该花钱的点都拆好。 + +做到透明了,接下来我们就开始做组合了。我们研究了很多跟团的数据之后,就抽象出大家比较喜欢的路线,我们就把这个路线规划出来,做一个标准化的产品。比如法瑞意三国七日游,很多人走过了,我们也做一个这样的路线,在线上售卖。如果你想做个性化调整,比如你不想住我规划的酒店,你可以换,没有问题,我们用技术支持个性化调整,这样的模式呢,我们就叫他自由行,但它核心原理还是参考跟团的路线模式。 + +要做到这些,显然是跟我们的计算能力各方面有非常大关系的。因为我们实际上是预先把所有的价格信息全部都获取到,放到我们数据库里面,每天争取更新多次。很多人都是 1 月份的时候想安排 6 月份出行,他得知道那时候酒店是什么价格,我们就做数据处理,按价格排序,哪天最便宜,都给算出来,这需要非常大的算力。 + +我们专门设立了一个价格中心,让非常优秀的人去做这个事情。从技术上讲,这就是推动业务发展的,相当于支撑了一种新的商业模式。 + +除了个性化的自由行,再反过来说,大部分人愿意牺牲个性化选择,可以跟团。以前跟团的做法都叫做:出发地成团。比如你在北京组的团要去欧洲玩,就是北京这边找几十个人,一起从北京出发。一般是40人左右,正好坐一个大巴的容量,好管理。但即使是北京这样的大城市,能在同一天或同一周去欧洲相同目的地旅游的人并不算多。于是旅行社只能每周发1个到2个团。而且有不成团的风险。当我们仔细研究的时候,发现国内的旅行社其实只负责提供从北京到欧洲这段的往返机票,甚至有些时候连机票都不负责。到了欧洲之后,完全由当地的地接社提供导游、酒店、门票等服务。 + +途牛就发明了一个新的模式,叫目的地成团。就是大家可以从全国各地出发,然后到了欧洲某个城市后,再组团。这样大大提升了成团的概率,因为全中国想要同一天去欧洲同一个目的地旅游的用户很容易超过40人。于是我们直接跟国外地接社谈,不仅保证每周有多个团,甚至每天有多个团。地接社当然愿意,而且因为量大,给我们更优惠的价格。我们只需要把从国内大城市出发到欧洲的往返机票给配上就行了。对于用户而言,可选择的日期一下子多起来了,几乎每天都有去欧洲的跟团游,而且价格还比以前便宜。 + +这个就是技术改变商业模式的案例。途牛当时做得非常不错,推动了旅游行业的变革和旅行方式的改变。 + +极客时间:VIPABC 的技术壁垒是什么?技术对教育的改变体现在哪里? + +汤峥嵘:我在 VIPABC 的时候,有一件事是站在他们的肩膀上,就是他们本身有一套课程的匹配算法叫DCGS(Dynamic Course Generation System,动态课程生成系统)。VIPABC 在当时教育行业里是第一个做匹配算法的,其他做教育的公司都没有。我们那时候是整个行业唯一一家,能够动态地把这么多学生、老师、课件都智能匹配的公司。匹配算法它创建了一个什么理念呢?打破无论是在线下教育还是线上教育中,学生选老师的模式。 + +之前的线下教育的选课模式是,先把不同老师的时间表公布出来,贴在墙上,供学生去挑选。因为在同一个校区或培训班中,老师的数量和上课的时间都是非常有限的,学生往往不会有选择焦虑。这个模式在线下完全没有问题。 + +但是当这个模式被搬到线上以后呢?因为互联网打破了时间和空间的限制,让老师的数量和可以上课的时间段大大增加,大到你可以认为是无限大。选择多了反而会成为焦虑。因为大部分人总想选好的。那么平台上很快就会冒出所谓的“好”老师和“好”时间段。而这些所谓“好”的资源永远是有限的,就跟挂号都挂专家号一样,“好”资源很快就不够了。当时好几家在线教育机构采用了用户自选老师的模式,导致用户抱怨总是抢不到“好”老师。 + +VIPABC发现在英语培训和其它素质类教育中,所谓的“好”,其实非常因人而异。比如对于初级学英语的人,名牌大学的英语系教授未必就好,甚至是浪费资源。对于一个想练习口语的人,一个富有感染力的人能让你自信,多开口,进步快。当我们做用户调研时,报告显示,白人和英美口音的老师最受喜爱。但实际的客户打分却不是这样,反而是黑人老师和东欧老师的打分最高。分析下来,因为黑人老师和东欧老师的肢体语言和表情都非常丰富,对提升上课效果有非常大的帮助。 + +所以VIPABC的课程系统,不允许学生选老师,但是允许你选任何时间上课。我们根据你的上课时间,我给你匹配最合适的老师。另外,我们还有给老师打分的体系,有了“老师喜爱度”这个维度的数据,后面在匹配课程的时候就越来越准了,满意度也会越来越高。 + +更有意思的是,早期我加入 VIPABC 的时候,还是以一对多形式为主的,一对一还没有开始流行。一对多的时候,我们连学生的组合也是攒出来的。我们把老师、学生和这节课要上的内容都动态匹配出来。 + +这又是一个技术改变商业模式的案例。虽然由于资本的大量涌入导致整个行业的非理性甚至坍塌,但我相信这个技术理念是对的。教育就是应该因人而异,给每个人一个适合的发展路径,而不是所有人都去追求少数“好”的路径。希望当教育行业回归到理性后,这样的技术可以再次冒出来推动社会的进步。 + +互动时间 + +你所在公司的技术团队做的事情,对业务或所在行业发挥着哪些作用呢,可以说说你的理解和看法么?欢迎在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/08途牛野蛮生长,也促使CTO“野蛮生长”.md b/专栏/超级访谈:对话汤峥嵘/08途牛野蛮生长,也促使CTO“野蛮生长”.md new file mode 100644 index 0000000..205924c --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/08途牛野蛮生长,也促使CTO“野蛮生长”.md @@ -0,0 +1,79 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 途牛野蛮生长,也促使CTO“野蛮生长” + 编者按:汤峥嵘2013年加入途牛,正值移动互联网浪潮,途牛乘风飞速发展,2014年就在美国上市了。2016年汤峥嵘离开途牛的时候,技术团队已经从刚来时的 200 人,扩张到了 1500 人。在途牛,这位CTO养成了一个更好的习惯,那就是深度参与业务,同时正是这段经历,让他真正知道了创业应该怎么做。途牛的野蛮生长,促使汤峥嵘进行了一场蜕变。 + +极客时间:感觉途牛应该对你改变挺大的,因为这是一家创业公司嘛,你又以非常核心的角色参与进去,应该比在阿里这个体系中要承担更多的职责。 + +汤峥嵘:确实,在阿里我并没有像途牛这样,参与到比较重要的角色当中去。而且阿里体量是比较大的,途牛相对来说还是体量小,我作为 CTO,对公司影响也比较大,而且途牛那个阶段又是几家(去哪儿、携程等)竞争非常激烈,高速增长的阶段,确实给我很大发挥空间。 + +而且对我来说,我当时去途牛还有一个很重要的原因,因为阿里也好,微医也好,创业团队都是 60 后、70 后主导,而我在加入途牛的时候,途牛这两个创始人他们才三十出头,很年轻。我那个时候认为未来是 80 后主导的,我就想要去体验跟 80 后创业是什么感觉。果然,到了途牛我就发现,面对不确定性的时候,80 后更具有快速尝试、快速迭代的风格和勇气。 + +70 后相对来说,还是愿意先定战略,再定规划,分析清楚后,最后再去做。80 后给我感觉很不一样,我前面也讲,那个喜欢天天出去外面看的创始人严海峰,他看到什么新的东西出来,就回来跟我们讲,快速分析一下,我们觉得 OK,就去做了,就是这样。 + +我认为我很幸运,因为我能感受到我们做出来的东西能产生商业价值,因为它确实改变了原来旅游业的商业模式。当然我觉得能成功还是因为,商业上有这样的需求,从技术角度我发现我们居然能做到动态打包成一个线路,那后面再做目的地成团什么的,自然就会有商业价值了。 + +那个时候,整个行业越往后做,越发现互联网对这些标准化的旅游产品,比如说机票、门票这些生意有很多影响,确实改变了行业。 + +比如说机票,过去都是旅行社卖机票,它的返点是非常高的,卖出一张,给一个返点,甚至有前返、后返等各种激励政策。比如旅行社卖给客户一千块钱一张机票,可能拿到一百块钱的返点。互联网公司一进来之后,想办法把这钱给到用户,然后各种各样的打折就跑出来了。于是各大航空公司就很不开心了,因为他们觉得互联网公司损害了他们的利益。到今天,你会发现,在任何旅行社、网站上定机票,跟航空公司官网定的价格是一模一样的。现在这些航空公司已经不给任何返点了。这也是为什么很多订票网站会不停地加各种保险让大家去选,因为没有别的赚钱方式了。 + +但是我认为整个行业是进步了的,因为消费者享受到了实实在在的好处。我觉得那段时间,途牛包括去哪儿、携程这几家一起做,确实改变了旅游行业,像去哪儿的技术是非常强的,它的搜索是做得最好的,途牛有些部分跟它做得非常相似,我们在此基础上再做创新,做组合比价。 + +极客时间:刚才你聊到,你觉得 70 后和 80 后 CEO 在管理上有些区别,在定战略,做规划上。你会觉得途牛这个方式就是好的吗,还是容易踩坑的? + +汤峥嵘:我是觉得没法说谁对谁错,谁好谁坏,我当时就感觉在这样“小步快跑”的方式下,途牛的发展速度是非常快的。 + +因为在那个时候,做在线旅游的这几家就是一团混战,必须分秒必争的。我 2013 年过去的时候还是网站模式,到 2014、2015 年的时候,就已经是移动互联网的风口了,我们也是相对比较早切到 App,抓住 App 这个流量红利的,专门分出了一个做移动的团队。我记得途牛 App 的增长速度真的是指数级增长,非常快,很快 App 上的交易额就超过了网站,这是我那时候明显感觉到的。 + +我觉得 80 后对移动互联网的崛起是天生的敏感,知道怎么做,而70 后从Web1.0到Web2.0,从PC到移动是需要时间的。这个差别我是觉得挺有意思的。 + +极客时间:你在之前的采访中也有说过,你选择加入一个团队,你会看自己和这个团队的人价值观是不是相符,气质是不是相投,具体是怎么去判断这个事情? + +汤峥嵘:还是要靠聊,靠沟通,比如说途牛的两个创始人,他们是很有诚意到杭州来找我聊的,当时我并不知道这个公司是什么样子,途牛其实那个时候还比较小,我就说可以跟他们聊聊。 + +第一,我知道他们俩是 80 后。第二,我发现他们俩人非常正直,因为他俩一个是大学毕业一年开始创业的,一个是相当于在大学期间直接就开始做途牛,都是没有在大公司里面工作过的,没有经历过很复杂的企业文化,我觉得他们就是非常的单纯、充满理想。这点我就比较看重,我也是比较简单的性格,通过接触我感觉跟我特别契合,这是让我加入途牛一个很主要的原因。 + +极客时间:我不知道这算不算偏见,像你作为 CTO 这样的级别在找团队的时候,应该更有话语权,但是比如像刚毕业的大学生,或者是工作经验不够多的时候,他怎么去找到和自己价值观相符的团队,这个会不会更难? + +汤峥嵘:我觉得是这样。还很年轻的朋友,可能还不清楚自己是什么样的人,对价值观这件事情本身就了解得不多。首先要搞明白你自己是什么样的价值观,才能做选择。 + +我是觉得做技术的人应该都比较单纯一些,不善于绕弯子。我个人认为,未来想长期在技术领域去做的人,就应该去找看起来比较正直的老板,这样我们就可以全心投入在技术领域。你在选择团队的时候,选择老板的时候就不要去选择看起来城府很深的人。我最怕城府深的人,我觉得大部分技术人也是这样的,我是什么事都写在脸上,别人能把我看得很清楚,但如果我看不清楚对方,我就觉得心里有点虚,因为你不知道他到底心里是怎么想你的。 + +但和人沟通也是一项重要技能,这种感觉也需要练出来,因为你想,要让简单的人去能够看懂复杂的人,这个事情肯定是有点悖论,我们就得靠练。所以对于刚毕业的同学你先搞清楚自己能接受什么,不能接受什么,锻炼和别人交流的能力,保持真诚,是不是合拍大概率是能感受出来的。 + +极客时间:你去的时候途牛技术团队有多少人?你在途牛遇到的比较大的挑战和困难是什么? + +汤峥嵘:到了途牛之后,我的核心任务就两个,一个是能够理解老板要做什么事情,甚至能够站在他的角度去思考,另一个就是要快速组建团队。 + +2013 年、2014 年整体形势都非常好,我 13 年加入途牛,14 年我们就去美国上市了,走势非常快。我觉得我运气特别好,我进去的时候,途牛就开始有一个往上的拐点。你看我去的时候技术团队大概两百人,到我 2016 年走的时候,技术团队已经 1500 人了。 + +遇到的比较大的挑战就是,怎么能够找到这么多优秀的人,并且能在队伍快速增长的时候保持好队形。另一个挑战是用什么样的技术架构和管理架构去承接快速增长的业务。压力更多来自这些方面。 + +我们有很多技术也是靠这些优秀的同学去做,比如说怎么能动态算价格啊,就是价格中心这个技术团队做的,价格中心依赖的是,你要能够去快速找到价格,有些是我们通过别人的 API 来提供价格,有些就直接爬取公开信息。因此搜索和数据非常重要。当时的搜索团队和大数据团队几乎是从零开始搭建的。我们很快做了一套非常好的搜索系统和大数据中心。非常实时地提供各种维度的报表,辅助决策。在爬虫、搜索技术、还有大数据上,我们技术团队还是非常强的。 + +极客时间:从200人到1500人,你管理的带宽也会比之前大很多,这么大的团队,当时在管理上感觉吃力吗?有什么经验分享么? + +汤峥嵘:还好,没有觉得很吃力,我对这个团队管理还是花了很多精力的,这些兄弟们跟我关系都一直特别好。我认为,我的责任不仅仅是一个 CTO,我要把这些人都带起来,让他们成为发动机,他们只要能力强了,他们成为每个部门的 CTO 了,我就轻松了,我的秘方就是这个,要不然我肯定会累死。我花了蛮多时间,跟他们做各种沟通的。 + +在管理上,我感受到阿里带给我的一些好处,特别是技术体系这块,我率先把阿里的技术层级体系(指阿里 P 序列)引入到途牛技术部,后来这套体系才推广到全公司的。当时动员了公司各个部门,一起来研究针对我们公司的层级应该怎么定义,不完全按照阿里的标准。为了做这个事,我还设置技术委员会,有点像阿里的组织部,让公司里比较优秀的干部、优秀的架构师,都是技术比较强的人加入进来,由他们来主要决定技术层级应该怎么定,相当于把这个职责就给到他们了,他们也会觉得自己很有价值和意义,会用心做这个事情。 + +同时呢,因为越往上晋升越难,大家肯定也遇到过吵架的情况:“这个人为什么能升,另一个人不能升?”还有比如说,这个人他在公司人缘好,认识的人多,而且做的事情委员会的人都了解,可能在晋升的时候就会占便宜,另一个人和技术委员会的人不熟,只用一个小时的汇报时间说服评委,总是有点难度。 + +我说我鼓励你们平时就去影响这些技术委员会的人,但凡有评审,就一定有人的因素在里面,你们不要把技术委员会想象成完全公平的。我说你要是有能力把他们买通都没关系,因为我相信这些技术人他们心里还是有一杆秤的,主要还是靠你做的东西好不好来评判。但我鼓励他们多表现,这种表现也是有正面影响的,因为太多技术人可能技术很强,但是不太善于表达,就容易吃亏。我也跟他们的领导说,你的责任之一就是要把你下面技术很牛的人,介绍给别人,要不然他上不去是你的责任。 + +当时在途牛,有这么一个大好的环境给了我,让我做这个事,我对这段经历,还是非常感恩的。因为整个业务在高速增长,老板也很善于把控方向,反过来给了我们技术发展的空间。因为严格意义上讲,这件事情本身为公司创造不了什么直接价值,你必须得有足够好的业务,赢得老板的信任之后,你才有空间去做这个事情。 + +极客时间:你有没有去复盘过那三年,对你未来发展的影响,那三年应该是让你更自信的过程? + +汤峥嵘:我认为那三年对我的价值是,给我今天创业绝对是奠定了基础,我完全知道怎么创业了。那三年是一个非常野蛮生长的过程,战略也不那么清晰,但恰恰是那个时候增长超快,每天忙各种事,不是跟业务沟通,就是招人,做新的技术,是一个不知不觉就做了非常多事情的阶段。我就有了新的体验,我觉得但凡一步一个脚印、有清晰战略去做事,可能自己的成长反而是不多的,因为你每一步都很稳,没有走出自己的舒适圈。 + +互动时间 + +你是怎么找到和自己价值观相符的团队的?欢迎在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/09闲话家常(三):纽约打工故事续集.md b/专栏/超级访谈:对话汤峥嵘/09闲话家常(三):纽约打工故事续集.md new file mode 100644 index 0000000..5c6ad0c --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/09闲话家常(三):纽约打工故事续集.md @@ -0,0 +1,45 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 闲话家常(三):纽约打工故事续集 + 这一节只讲一个小故事,就把它叫做“纽约打工故事续集”吧。美国求学时期,汤峥嵘曾经在纽约一家小超市打工,很多年之后,途牛在纽约上市,汤峥嵘也去敲钟,机缘巧合下他又回到这个曾经打过工的地方,见到曾经一起工作的管理员。 + +由此想到苏轼的诗《龟山》,诗人的境遇与这个故事也不完全贴切,只看前半首: + +我生飘荡去何求,再过龟山岁五周。 + +身行万里半天下,僧卧一庵初白头。 + +我这一生到处飘来荡去,到底要追求什么?先抛出一个哲学命题,上海财经大学人文学院教授李贵曾经在一场讲座中解读这首诗,当36岁的苏轼赴杭州任通判,再经过龟山镇,时间已过了五年。五年来走过大半个中国,再次经过此地,庙里的那个和尚已经长出白头发。我整日奔波,这位僧人始终安静修行,无所谓谁的人生更有意义,每个人都有属于自己的坚持和初心,如此而已。 + +汤峥嵘:我2013年加入途牛,2014年公司就上市了,我还有幸去美国敲钟。正好说到这,当时还有个故事可以讲一讲。我那个时候还发了个朋友圈,很多人都说这个故事很励志。 + +我在美国上学的时候不是在纽约打过工吗?就在那个韩国人开的小超市做收银员,打了一个暑期的工。然后呢,我 2014 年去纽约敲钟,敲完钟的第二天所有人都去购物了,我是没有购物的欲望,就一个人到处去纽约瞎逛了。 + +逛着逛着突然间想到那个小超市,我大概还记得在什么位置,就想去那看看那家超市还在不在,我就跑到那去了。跑到我记忆里那条路上一看,那家店不在了,我以为就关掉了嘛,然后我就继续往前走了,只多走了一个街区,突然看到有一家小超市,跟我印象中那个超市还有点像,我就走进去了。 + +进去一看,那个主管就是以前那个巴基斯坦领导,我一进去,他也把我认出来了。我当时的感受是什么呢?很复杂,我第一感觉就是,什么都变了,又好像什么都没变。 + +因为我们当时敲钟的时候,我还是觉得很自豪的。纳斯达克敲钟的那个地方,它的会场并不大,里面有一个酒吧,会给大家弄一些食物吃,我找服务员要饮料的时候就和他聊天,他跟我说,来这的都是中国公司,他知道很多中国城市的名字。我们当时觉得非常自豪的,有一种我们中国人是万能的感觉。 + +但是呢,突然间又回到了我 22 年前打过工的超市,见到了熟悉的人,发现这里的一切都没怎么变,还是这个主管,做得还是和 22 年前一样的事情。那个时候,他的老板已经不是那个韩国人了,老板都换了,但是他 22 年都在这个超市。他还跟我说,你看,现在收银的女孩,那是我女儿。他说的时候是带着自豪的感觉的。 + +所以,我觉得这是一个特别有意思的反差。我们轰轰烈烈地干一番事业,我们把赚钱、上市定义为成功,我们好像觉得自己很牛。但是其实有另外一些人,他们移民到异国他乡,靠自己的努力,找到了一份工作,干了一辈子,然后自己的女儿也继续在同一个地方工作、生活,对他们来说这也是一种成功,甚至有更强的幸福感,因为这种成功更加稳定,更加踏实。 + +22年前纽约市的一个最底层的打工仔,今天能够成为上市公司一员,在NASDAQ敲钟,有人说这个故事很励志。但我恰恰不觉得这是一个励志的事情。所谓的成功和幸福是什么?很难说。尤其是我们当时经历过这个高点,后面公司果然就是往下滑了,前面突然涨的太快了,大家都拼命地奔跑,反而落下了一堆东西。 + +当你再回头的时候,你发现还是人家 22 年来就干一份工作,更能享受稳定的幸福。我相信他在这个店工作,无论换多少老板,新老板都得聘请他。就跟我第一份工作一样,老板都得走,我也不能走。因为他太熟这套东西了,这个地方也很稳定。我猜他跟当地的富人邻居也很熟,因为那个社区是一个富人区,他管着这个小超市,天天给这些邻居打招呼的就是这个人。他不仅是这个超市最离不开的一个人,可能也是社区里大家离不开的一个人。我觉得他会一辈子甚至下一代的一辈子都在这家店工作,为这个社区服务。 + +那天发生了这件事,让我觉得特别有意思。在很多人眼里,去NASDAQ敲钟,应该是一件很成功很值得炫耀的事情。但这件事让我意识到,靠自己的勤奋,能够在异国他乡的一家店、一个社区找到一个属于自己的家,一辈子甚至几辈子都能稳定生存的家,或许更加成功,更加值得炫耀。 + +我很感恩那天没有去购物,也没有在原来的小超市位置停下脚步,而是多走了一个街区。 + +互动时间 + +看完这段经历你有什么感受,欢迎和朋友们一起分享想法。欢迎在评论区交流。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/10管理的本质:如何把硬性政策柔性执行?.md b/专栏/超级访谈:对话汤峥嵘/10管理的本质:如何把硬性政策柔性执行?.md new file mode 100644 index 0000000..24ac036 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/10管理的本质:如何把硬性政策柔性执行?.md @@ -0,0 +1,89 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 管理的本质:如何把硬性政策柔性执行? + 编者按:从这一节开始,我们进入第三模块:管理经验。如何理解“管理”二字?管理者应该怎样看待自己的职责?在日常管理中可以穿插哪些方法,让团队凝聚力、战斗力更上一层楼?在组建团队的过程中涉及到找优秀的人、划分组织架构、职级考核,甚至解决薪资倒挂的问题,管理者应该怎么办……从这一节开始,我们会聊一些管理的话题,和你分享汤峥嵘的管理经验。 + +极客时间:技术人要转管理这个话题有点老生常谈了,结合你最开始讲的性格理论,我觉得第一步大家要先了解自己的性格,那除此之外,肯定还是需要一些实践。你觉得技术人转管理,有什么需要注意的么? + +汤峥嵘:技术人转管理可能第一步遇到的问题,就是由管事到管人的转变,这几乎是两种不同的方法。比如评判代码好坏是很清晰的逻辑,就看对还是错,优秀还是不优秀,代码写的漂亮不漂亮等等,这是管事。到管人的时候,他可能也容易用同样的思维方式去套管理,用一些比较分明的标签去评价人,把人当成事情一样去评价,去分黑白。一开始绝对容易踩这个坑。唯一的方法就是通过工作本身去练,在犯错中反思前进。 + +极客时间:那从技术管理者要成长到 CTO,管理能力上会有哪些跨越? + +汤峥嵘:我先说一句,要做 CTO 的人,技术首先要强,不要误解 CTO 是个纯管理岗位,因为 CTO 是给一家公司把控技术方向的,如果这个人没有一定的技术深度,是做不好这个职责的。但如果你只是一个纯技术的人,做不到跟 CEO、COO 这些人讨论商业,这也不行,所以也要平衡。但我觉得更重要的还是技术,如果一个公司的 CTO 不懂技术或者技术能力不行,所有人心里应该很膈应吧,技术人员对这点还是很敏感的。 + +如果你真想做好 CTO,别的先不想,首先确保自己的技术不能差。千万不要放弃自己对技术的追求和对技术的研究,不能变成一个纯管理岗位;千万不要放弃在公司里一些小的技术上的沟通,那个是让你保持技术敏感的一个很重要的途径。 + +极客时间:回到最本质的问题,你怎么理解做管理? + +汤峥嵘:我认为做管理者,一个很重要的核心是把上级的刚性的、标准的需求,变成一个柔性的、可执行的方案,这才是管理的本质。 + +上级往往传达的都是一个政策,一个只有数字的目标。举个例子,上级说,我们公司准备年底要裁员了,你的部门一共 100 个人,要裁 20 个人。要求给你了,你要解决的问题就是这 100 个人到底裁哪 20 个?这是很痛苦的事。但是这个问题的背后还有一个大的标准是,你得说服所有的人都认同,淘汰的这 20 个人是相对来说最差的 20 个人。因此本质上就要给这 100 个人排个队,我也是慢慢在实践中得到这个经验,我也跟同事讲,你只要想把这 100 个人排个队,总是能排出来的,你不要说你排不出来。 + +你肯定不能首先就和领导说,我们部门是最优秀的,我们不应该淘汰人,为什么别的部门才裁这么点人?如果陷入到这个逻辑中去,你就完不成这个任务。因为你从内心根本就不接受这件事情。但是一旦公司层面做完这个决定了,大家首先得接受这个事实,因为你作为管理者,也得理解你的老板有他的难处。 + +另外,大家一定要接受一个现实的情况,这个排序本身就不存在完全客观的情况。以前阿里有一个讲法就很简单,如果说一套打分机制能帮你把所有的分数排完了,那扫地阿姨也可以做管理了,或者今天 AI 就可以替代你做管理了,还要你干吗?对吧?你做管理,就是要你来给这些人打分,你就是标准。从这个角度来理解管理者,你就是来管这些人的,你决定了他们的去留、工资,你就有这么大的责任。 + +我自己很早把这件事想通了,我的心态就好很多。在此以前总想追求一个完美的管理方案,想谁也不得罪,应该我打出的分所有人都叫好,老板也说好,员工也说好,后来你发现这根本不存在。慢慢我就悟出来了,作为管理者到底是为谁打分?其实是为你自己打分,你不是为你老板打分,不是说老板喜欢谁你就给谁打高分。你也不是为你的下属打分,你就是要建立自己内心的一把尺子,最后变成你在管理过程中的标准,并且说服你的下属。 + +就像现在的产品都要描绘自己的用户画像一样,你要把你不喜欢的人的画像,和你喜欢的人的画像画出来,告诉大家,你鼓励什么和不鼓励什么。这个很有用,因为大部分技术人员心里都明白谁最优秀,你不讲他们心里也知道,那不如就把优秀的人做出案例,他做过什么优秀的事情,达到什么效果,作为管理者就要表扬一下。反正要做成案例,或者有数字指标参考,把这个弄出来,就好管理了。 + +极客时间:这里面有个问题没太理解,你说管理者要认可自己的标准,这个要求是不是蛮高的?因为也许这个管理者他根本就没有自己的标准,他的标准也许就是他领导的标准呢? + +汤峥嵘:这个问题挺有意思,我给你解释个概念,中文中“管理”这个词,英文叫 Management,被翻译成管理特别好,为什么?Management 只有“理”这个意思,没有“管”的意思。这就是为什么西方人做管理比我们中国人更容易。 + +“理”就是说这个桌子上很乱,我把它整理整齐,理就是整理、梳理的这个理。什么叫管呢?就是这块地方归我管。英文中有一个词叫 Owner,代表我是主人的这种感觉,或者是主人翁的感觉,我认为阿里在这点上讲得很好,它对管理者的要求就得有 Ownership。 + +但是作为管理者,你先不要管公司给你多少权力,给你什么利益,首先你得从心里觉得这件事归你管了。在管理这件事上,我觉得中国人比较讲究秩序,就是我被谁管,或者我管谁,大家都希望明确。这个是我从美国回来之后,深刻感受到的不同。这就促使我发生转变,如果不理解中国文化里面的秩序观念,在这个文化体系下去做公司管理,我觉得可能管不好人。 + +因为我觉得不太存在一个大家公认的一套标准,能被所有人都认可,每个管理者都还有自己的不同方式,因为人和人就是不一样的,内心一定会有标准,如果没有标准,真的以上级的意志为意志,我觉得是做不好管理的。 + +当然,拿技术来说,大的评判标准是一样的,肯定是代码写得好、Bug 少、速度又快,这些是容易量化出来的。但是就管人来说,你一定得有自己的一杆秤,不只是关乎代码水平,还有你自己的维度,不管是工作态度、成长速度,或者其他好的工作品质。如果只盯着代码水平,把它过度量化了,会造成大家为了那个数字去想别的对策。比如你追求 Bug 少,那他就代码少写点;你要求能够准时提交,他就在评估时间的时候多预估一些工作量。人性就是这样的,这个是很难抗拒的。 + +我认为,客观的并且可量化的工作考评,绝对不是挑选谁优秀的一个标准,或许可以筛选出来谁差。但是这个东西我们不定也不行,这个是底线。如果连这个都不定了,大家就可能觉得太放纵了。 + +极客时间:回到你说的管理的本质,拿裁员这个事情来说,上级给了硬性指标,你会通过什么办法去柔性地执行呢? + +汤峥嵘:其实 VIPABC 在 2018 年底的时候做了一次裁员,比例还不小,那时候网上出了很多负面新闻。我们比其他在线教育企业提前经历公司的下降过程,确实裁那么多人是非常难受的。这是整个公司的问题。 + +当时公司要求每个部门要裁多少人,我的方法就是帮被我裁掉的人介绍工作。因为产品技术人员还是有很多工作机会的。这个时候,我就不再是他老板的身份了,而是作为朋友给他一些建议,比如我有什么资源可以介绍给他,他有新方向了,我可以给一些建议和参考。我也会给他们讲一些方法。比如你有加入某公司的意向了,你就可以问老板,能不能跟未来的同事见一下,甚至吃顿饭,我觉得如果是一个好老板,人家也觉得你的建议是不错,可能就会同意提前来聊一聊,这个时候你就知道以后跟你共事的人是谁,氛围喜不喜欢,等等。我会给一些类似这样的方法。 + +极客时间:互联网企业裁员,在现在这个时间点是进行时状态。很多人也会关心公司裁员的逻辑,一般企业都会说是末位淘汰。你现在站在 CEO 的角度来看,你怎么理解末位淘汰?什么时候适合做末位淘汰,你现在的创业公司(云柚智能)会有末位淘汰吗? + +汤峥嵘:我们现在没有定期的末位淘汰制度,因为公司现在太小了,小公司不适合这个制度。但我们从创业到现在一直在淘汰人,在平时工作中觉得这个人不行的,就淘汰掉。 + +凭我的感觉,我觉得公司规模总得到几百人才适合做末位淘汰。假如你的公司在 100 人这个阶段,可能主要管理者下面就带 10 个人、20 个人左右,还是比较容易看清楚谁行谁不行的。但当公司大了之后,情况就会不一样,作为 CEO 可能必须得接受一个事实:每个经理、每个总监、每个主管,没有上面压力他可能不愿意淘汰人,这时候公司就会强制“换一换血”,淘汰一批人,这是做末位淘汰背后的原理。 + +极客时间:接着裁员这个话题,企业扩张时期,往往一片欣欣向荣,有明确扩张规划的公司可能是有节制的招人,但也有疯狂招人的情况,当遇到市场低迷,或者融资困难等困境,往往又被迫裁员。员工受伤,老板内心肯定也会经历挣扎。你觉得企业是否应该有责任为一个员工3年后的发展考虑,在快速扩张期对扩招做好预判和规划?这个问题对企业管理者来说,是大家会面临的困境么? + +汤峥嵘:这个问题非常好。我正好可以解释一下我对企业、管理者和员工这三者关系的理解。首先,我认为企业是一个由一大堆标签组成的概念。这个概念是没有主观意识的,它随着环境的变化,而不由自主地变化着。所以我们不能简单孤立地看一家企业,而要看他在环境中所处的位置。其实企业的扩张和收缩,都是在应对外部变化甚至危机,都是为了生存,很多时候是无法控制的。动物为了生存,有时候要捕猎和扩张地盘,有时候要冬眠休息或养伤。企业其实做的是完全相同的事情。适者生存嘛。企业做得好坏,自然会得到市场的检验,没有必要用道德去评判企业。 + +但是管理者就不同了。我前面说了,管理者的责任是把刚性的政策变成柔性的执行方案,把外部的不确定性变成内部相对确定的东西。管理者就是要把招人和裁人这两道难题做好,才能体现出他的价值。说具体一点,就是当外部环境要求企业快速扩张的时候,管理者要冷静思考招什么样的人,怎么搭组织结构。企业快速扩张起来以后,一定要花足够精力去管理团队,并且不断灌输危机感,告诉大家随时会裁员。当真的遇到需要收缩的时候,同样要冷静思考让哪些人离开,理由是什么? + +最后对于个体员工而言,我前面也提到过。不要奢望依靠任何人。如果觉得不公平,那就是运气不好,抱怨是一点没用的。如果是能力不行,抱怨两句就要有行动。 + +最后回答一下你的关键问题。企业是否应该有责任为一个员工3年后的发展考虑。企业既然是个抽象的概念,就不可能为每个员工的发展考虑。企业和员工的关系就是简单的雇佣关系。但我认为一个优秀的管理者,应该为他的团队考虑这个问题,甚至不止3年。 + +极客时间:职业成长过程中,随着你管理的团队越来越大、管理的职责越来越大,这个过程中,你经历过怎样的蜕变?有什么小技巧可以分享一下么? + +汤峥嵘:我讲一个可以操作的方法,就是不停的逼着自己想象自己就是老板,然后再想自己该怎么做事,这就是个小技巧。我以前是职业经理人的时候,比如老板要个方案,我都是提三个方案,方案 A、B、C。A有A的优点,但也有缺点。B、C同样。讲清楚各自优缺点,让老板来做选择,一般都是这么干。 + +但当我把自己想象成老板的时候,我就不会这么做了。我仍然提三个方案,但我会跟老板说:如果我是你,我会选择其中哪个方案。这相当于我给了自己一次机会跟老板交流,如果老板比较厉害的话,还可以给我指点,我不就学到了吗?提三个方案的原因是,作为下属有义务给老板更多选择,同时自己也会体会到做选择是一个痛苦的过程,人越往上走,面临的选择越难。 + +对于中层管理者,我还有一个体会,中层的管理者是连接作用,他要把向上的不确定性转为向下的确定性。因为管理者越往上走,他承受的不确定性越大。公司的创始人一定会面临某个阶段公司不知道怎么走的情况,做为中层的管理者要让下面人有信心,坚定地告诉大家该怎么干,该怎么往下走。 + +你能承受越大的不确定性,你的级别就越高。你面对的只是一点点的不确定性,你可能就是一个比较初级的管理者。 + +当你觉得你是这件事的主人的时候,你会冒着非常大的风险去管理这个事情,去做选择。你可能会搞砸,但是你也敢承担这个责任。如果有机会,我鼓励大家把自己放到主人翁的位置上来,因为即使你搞砸了,也是公司承担责任,不会有太多公司真的会怪到你头上来的,你还能锻炼一把,失败了你没啥大损失,成功了你有大收获的。优秀的管理者就是靠这种日常的磨炼成长起来的。 + +互动时间 + +总的来说,管理者都会面临这个问题,就是最终你得把公司面向集体的一个指令,也就是刚性的政策,落到下面具体每个员工头上去。这是汤峥嵘理解的管理的本质。 + +你喜欢什么样的管理风格呢?欢迎在评论区留言。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/11为什么建议技术团队的组织架构按系统划分?.md b/专栏/超级访谈:对话汤峥嵘/11为什么建议技术团队的组织架构按系统划分?.md new file mode 100644 index 0000000..3b1440c --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/11为什么建议技术团队的组织架构按系统划分?.md @@ -0,0 +1,75 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 为什么建议技术团队的组织架构按系统划分? + 极客时间:技术团队的组织架构建设话题,我一直很想聊,你了解的,技术组织架构的建设都有哪些常用方法? + +汤峥嵘:其实技术团队的架构无非是以下几种方式。一种业务导向的团队架构。假设你的公司有国际业务和国内业务,每个业务给它分配一个技术团队,这种分类方式比较垂直。但它的缺点是,团队可能经常重复劳动。 + +第二种架构是统一的技术团队,全公司的需求汇总到一起,统一派工单。这种方式也非常常见,有很多公司在用。用这种方式的公司,说明它的 CTO 肯定是非常强势的,也许技术也是很强的,业务人员提完需求只能等着,要排队,具体排到什么时候他们也不知道。 + +这种方法我觉得更适合稳定一点的企业,因为对这些企业来说,不管业务人员提的需求做成了还是没做成,应该对业务影响都不是很大,所以才会用这种组织架构。 + +我比较喜欢用的架构是以上两种的组合。每个业务团队都配一个技术团队,但同时还有底层的技术团队。这里面的关键是,无论是业务团队还是底层技术团队,我要求每个团队都负责一个系统。拿途牛来说,我的组织上层有很多垂直的业务,比方说自助游、跟团游、机票、酒店等等,对应的就是自助游系统、跟团游系统、机票系统、酒店系统等等;底下还有几个共用的系统,比如说,搜索系统、价格系统、大数据中心,这些部门是要支撑全公司的。 + + + +极客时间:关于技术团队的组织架构话题,网上也有相关的各种讨论,除了刚刚说到的几种,是不是还有按项目划分的形式?你觉得技术组织的架构有一个标准答案吗? + +汤峥嵘:我觉得技术组织的架构方法肯定是没有标准答案的。我的分配原则是,一个团队在做的事情要相对稳定,我不希望大家老是换来换去,因为这样没有办法积累经验。 + +无论是对商业、产品还是技术类的知识,你都要有一个系统化的积累过程,这样才能进步和成长。所以我比较反对项目制,因为短期的项目一般很快就结束了,团队散掉之后,你就没有地方去做沉淀了。除非你要去做一个十年的长项目,但这种项目老板一般又不会批准。 + +当然,并不是说完全不能做项目,但你得在系统稳定的时候做。系统是稳定的,项目是灵活的,这个没问题。比方说,企业底层的一大堆系统都已经比较稳定了,有一千个人都是固定的。这时候抽一百个人出来做项目,这是可以的。这就像搭积木一样,底层的积木先搭好,上面才能做一些变动。 + +为什么我喜欢用系统来划分团队呢?因为我觉得系统是边界,这个边界定义好了,后面才不容易出问题。这可能跟我干了这么多年架构师有关,架构师最重要的任务就是把系统稳定地切分好。这个叫订单系统、那个叫支付系统,它们之间既有互动,又有一些差别,不能混为一谈。 + +切分的时候,首先肯定要划定一些横向的系统,这些系统是大家都要用到的。做这些系统的人通常是比较偏底层的,底层的逻辑性特别强,所以我们匹配的人技术能力也要很强,他在产品、商业上弱点都没关系。公司切出一些系统来之后呢,直接用系统划分团队就是最好的,这样技术就能跟业务对接上了。 + +业务这部分人(比方说做 APP 的人)跟做系统的人员又不一样,他得懂客户,他需要想象用户的使用场景。技术上,因为它依赖于最底层的系统,所以它的技术难度可能不是那么大,所以如果你脑子比较灵活,想法很多,就可以往业务方向发展。 + +比如以前在途牛,价格中心就是一个横向的底层系统。那个系统是支撑全公司所有人的。它没有直接的业务对接方,它就是把价格这个东西做好就可以了。但是在它之上的很多业务,包括自助游、跟团游啊,他们都有自己的系统。这些内部业务系统业务方就可以自己去做了。 + +把系统切割好还有一个好处,就是大家都很有责任心和归属感。因为每个系统有很明确的负责人,所以他会很有责任心,他不允许别人改他的东西。如果把这部分内容做好了,他们也很容易有很强的自豪感。我是喜欢去这么做的,我觉得对团队和个人来说都有好处。 + +极客时间:你说的这个团队组建思路,有大小公司之分吗?比方说有的是几十人的团队,有的是几千人的团队,在组织思路上会不会有差别? + +汤峥嵘:差别肯定是有的,但其实原理上是一样的。大公司的系统很多,小公司的系统就少,但都是要去切分的。比如我们创业公司现在的研发人员很少,你可以理解为只有一个系统,甚至说这些人同时在做三个系统,但是因为人少,所以没有关系,管理起来仍然非常方便。 + +但是设想一下,如果我们公司的开发人数达到了几千人,那我肯定会把不同的人分配到不同的系统上去,这样系统才能稳定。这还是基于那个前提,你得有个好的架构师。这就像我们国家治理一样,你得先把道路规划好。道路的宽窄不能是一样的,有些地方承载的人多,那道路就要宽一点,不然以后就会堵车。不同的地方,规划形式也不一样。 + +再说回系统,每个部分功能不一样,做好切割,将来打架的事儿就少。最怕的是一开始切分没做好,后面架构师要进行重构。就像一条路,太窄了,用它的人又太多,没办法了,你就只能把路全部推翻,再来一遍,成本就很高了。如果前期切分做好了,以后有问题只是在系统内部一直迭代,不影响外面的人,那都好办。 + +极客时间:所以在途牛和 VIPABC 都是在用你的这套方法去划分团队,在推行的时候有遇到什么问题么? + +汤峥嵘:我们在不同时期架构会有一些变化。比如说我们一开始在做途牛的时候,我们把 APP 单独拉出来作为一个团队。途牛那时候主推 App,大家(竞品公司)都在做移动,时间紧迫,就得有足够多的人快速赶上去。于是呢,就发生了App 把所有业务都做一遍,PC 端还要再做一遍,因为PC 也不能放弃。当时我们还没有底层这个东西(如价格中心)的。我就强制他们先自己做,自己内部做好分层,把未来调用公共平台的接口留出来,因为我知道未来底下的这层系统,一定是既能支撑 PC 也能支撑 App 的。 + +在这个过程中一定会出现没做好分层,拆不掉的情况,我们当时打架是很厉害的。在局部我允许出现做两份重复劳动的情况,先跑业务,将来再解决合在一起的问题。比如在某些新业务上,为了赶上线时间,就允许App和PC各自把业务逻辑实现一遍。上线后再把公共的逻辑抽象出来,沉淀到底层系统中。 + +我刚才讲这个做一层公共的底层系统支撑听起来好像很简单,但其实还是有一些挑战的。 + +第一,到底什么是公共的?怎么界定哪些功能或服务要抽象出来? + +第二,这个底层系统做好之后,不同部门提的需求,在优先级上可能会有冲突。如果你只是在自己的业务范围内有需求,比如说我自助游只改我自己的代码,这个没问题。你有自己的人,自己去排个位就好了。 + +但如果两个部门都有一些工作自己做不了,要给底层的系统提需求,这时候谁排在前面,谁排在后面?肯定会有冲突。我认为没有一个绝对好的机制可以解决这个问题。我当时的处理方法就是,我来排,你们都听我的就好了。工作的优先级问题,我负责跟业务的老板沟通,这样我团队的压力就小一些。 + +我这么做是因为,我知道中间这层人的压力到后来都是最大的,公司大部分的需求都在我们这里做。等你逐渐发现很多东西都需要重做的时候,这些东西就应该放到公共的地方去做了,像阿里的“大中台”组织就是这么个道理。 + +我为什么敢这么说呢?是因为阿里有行癫这样非常牛的人,他在一开始就把架构搭得很清楚了,在执行业务时候也很清楚。他指挥得好,中台搭建的速度就快。但如果公司的架构没做好,各个部门各自为政,这个大中台的转换就不会这么顺利。 + +你想一想,一个部门他把自己的系统搞好了,你突然让他做中台,给“国家”做贡献,还没有财政拨款。那这个部门的负责人肯定会想,公司分给我的人,他就应该干我这个部门的事,凭什么干别的部门的事。而且你让人家做中台的事,人家的业务指标就受影响。他说我业务指标达不到,你能来帮我背吗?你又没这个能力,一句话就把你封死了。 + +对这样的问题,如果说要提一点建议的话,我觉得作为 CTO,一开始就得留一点人出来,你不能把所有人都贡献出去支持业务。你得有点私心。但这里又有个度的问题,因为你留的人不能多到影响业务。这个平衡在哪,就得看每个公司的情况了。 + +我觉得公司最后发展到比较稳定的状态,一般都是有底层公共的东西,上面有垂直的业务,VIPABC 也是这样的。但是在 VIPABC 的时候,因为我自己同时是 COO,我可以给我的技术团队提需求,和途牛那时候相比,没有那么大矛盾了,会好一点。 + +互动时间 + +你所在公司的技术团队架构是什么形式的?欢迎在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/12技术、产品、业务三方关系?谁水平高听谁的.md b/专栏/超级访谈:对话汤峥嵘/12技术、产品、业务三方关系?谁水平高听谁的.md new file mode 100644 index 0000000..9c4122c --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/12技术、产品、业务三方关系?谁水平高听谁的.md @@ -0,0 +1,83 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 技术、产品、业务三方关系?谁水平高听谁的 + 极客时间:我发现技术人和业务人思考问题的重点常常是不一样的,比如 CEO (更多指非技术出身的CEO)或者是销售、运营同学经常会觉得业务起不来,技术再好也没用。而工程师会觉得,不管业务怎么样,我技术要去做好。为什么会有这么大差别呢? + +汤峥嵘:公司里面不同部门都有不同的看问题视角。每个人站在自己的位置上,就会无限放大跟自己相关的东西,因为他要实现自己的目标,这就是主观性。就比如你情绪好的时候,你看到的天就特别蓝,心情不好的时候,就觉得今天好像哪里都不怎么样。 + +具体来讲,业务人关注的就是业务量,因为他看不出你的技术到底能给他的业务带来什么价值。技术人呢,他们会说:没有技术拿什么支撑业务?当然应该把技术做好了。这种视角上的差异很正常。 + +极客时间:这两种不同的思维一定会导致二者的沟通合作出现一些问题,具体可以怎么协调呢? + +汤峥嵘:关键是找到一个第三方作为“桥梁”,把这两端打通。我觉得这是 CTO 的一个很重要的职责。我们要花很多精力去跟业务人明确问题,然后协调解决问题。在快速变化的公司里,能力强的技术人员是有能力把业务上的一些问题先整理好的。不过现在的互联网公司里,这部分责任往往会跑到产品经理身上。 + +极客时间:业务、技术,再加上产品经理,这就成了三方协作的问题了。 + +汤峥嵘:对,因为产品经理恰恰是业务和技术中间的一个特别重要的桥梁。产品、业务、技术就是三角关系,一个常见的说法是“三角形是最稳定的结构”,其实对应着这三方,这个结论是不成立的。也就是说,产品、业务、技术这三方并不是在所有情况下都能形成一个良性的制衡关系的。 + +那实际情况是啥样的呢?我们就把这三方具像化成三个人吧。我认为,这三个人谁水平高,另外那两个人就听他的,就这么简单。 + +比如说,如果三方关系里有一个大牛级的产品经理,那什么也不用说了,另外两个人肯定都听他的。业务人会说:你太厉害了,你做出什么产品,我就卖什么东西。就像乔布斯做出 iPhone,产品好绝不愁卖。技术这一方也是,产品经理说什么需求,他就做什么需求。这样,技术人和业务人就都能做好自己的事情,这三方保持稳定状态。 + +但是,如果产品经理很弱,那业务人和技术人肯定就会吵架。业务人会觉得技术不行,技术人会说产品讲不清楚。这种情况下,我们就没有一个稳定的平衡点。所以你看,产品经理这个位置挺关键的,但优秀的产品人确实特别少。相对地,优秀的销售(泛指业务方)和优秀的技术人很多,因为这两个相对来说能力要求比较单一。 + +极客时间:看来在业务、技术、产品这三角里,产品经理起的是很关键的支撑作用,对这个结构的稳定性影响很大。也正是因为这样,这个三角很容易不稳定? + +汤峥嵘:对,因为优秀的产品经理很难找,所以,我在途牛和 VIPABC 当 CTO 的时候,都坚持要把产品经理放在我们 CTO 的管理线上来,相当于让产品听我的。因为我认为自己是一个能够打通技术和业务两边的人。 + +如果分成三个部门,产品肯定有他们自己的分法。最后就变成什么样呢?这个三角结构在更高层的管理上更要打架了,因为相当于有三个强势的人都在指挥。 + +我在途牛和 VIPABC 当 CTO 的时候把产品放到我这,就是为了减少三方带来的不稳定因素。但是我们的组织结构也要求 CTO 得有产品能力。 + +我的做法是,把技术团队统一归成产品加开发。这样,我下面的技术 Leader 有可能是个技术经理,也有可能是产品经理。因为产品技术关系更近,产品和开发,都是“做东西”的人。它就是要做出个东西来,让业务方拿去用。 + +打个比方,我们做出一个杯子来,业务方拿去卖。做杯子,从设计到开发,再到生产,这都是一系列的技术。我觉得它们本质是一样的,区别无非就是我们“杯子”的设计是产品经理做出来的,具体的“制作”是技术团队写代码写出来的。 + +极客时间:除了你说的这种把产品和开发放一起管理的模式,是不是也有把产品放在业务那儿管的情况? + +汤峥嵘:我知道很多公司是这样的模式。业务人是老大,下面有一个产品、一个技术向他汇报,这时候就变成业务为主了。如果这个业务老大对技术是很有感觉的,这样绝对就 OK。 + +淘宝为什么后来发展得那么好?我认为和陆兆禧做了淘宝 CEO有很大的关系。一方面他自己做过销售,很懂业务,另一方面他对技术的人是非常喜欢的,他就不停地提拔技术人出来做业务负责人。三丰(姜鹏)、行癫,一开始都是技术人,都是后来成了业务负责人的。三丰后来变成了淘宝的 CEO,行癫变成了淘宝的 COO。 + +行癫负责淘宝的业务对淘宝的快速增长影响很大。我觉得,主要还是因为行癫确实是对整个淘宝的架构特别熟,而且淘宝的业务他从很早就了解甚至参与了。等他全面负责淘宝、天猫、聚划算之后,就很清楚应该做什么事,以及在哪些方面可以重点投入。这就是业务是三方中的老大,可以驱动产品和技术的情况,我认为这种状态是最良性的。 + +因为产品和技术都觉得,我们老大还挺靠谱的,给我提的需求也好做,做完了马上就有效果,肯定就跟着他做。但如果是一个完全不懂技术的人当业务老大,提了一大堆需求,其他两方做不完,产品和技术的人这时候可能变成一伙了,合起来去跟业务人对抗。 + +极客时间:这个例子很好理解。我的理解是,这种情况下就要求业务负责人的综合能力是比较高的。那在你待过的组织里,技术人会向业务方汇报吗?那产品经理呢? + +汤峥嵘:是这样的,我的组织里技术人是会向业务汇报的,产品经理呢,就放在我的技术团队里。比如说做跟团游,技术领导人可能是个产品经理,也可能是个技术经理,他下面有产品和技术(我每个团队下面都是有产品和技术的),这个人就相当于跟团游的 “CTO”了。跟团游的业务方完全不负责产品,有需求就找产品经理。所以产品经理就是要负责天天去跟业务沟通。 + +我不建议把业务、产品、技术作为三个独立的团队。我也在这样的组织中经历过,觉得这三方特别容易吵架。我一直认为,在一个快速发展过程的公司里,不适合搭出一个四平八稳的架构。你的架构可以是有缺陷的,但是它也得有个长处,来支持你快速增长。最后你想快速发展,就是大家(三方)一起拼命奔跑,谁在跑的过程中领先了,跑的姿势还好看,他就突出出来了。到最后你会发现这个人真的很关键,他一个人的能力高低会影响一大半人。 + +极客时间:前面我们聊了技术、业务、产品这三方的关系和组织划分。看来不管怎么划分,都是为了跑得快,公司能发展更好。 + +汤峥嵘:我们的体系要做的是去给员工赋能,而不是束缚我们的员工。我觉得很多做技术的人特别喜欢定规则,他们自认为规则很好,但是这些规则常常反过来变成束缚了。 + +就比如说写文档这个事儿吧。通常的规则是这样的:每做一个产品,大家开个会,从 PSD(产品详细说明文档),再到更细的功能需求,再到开发,再到测试,是要写一系列文档的。在公司里我就跟大家说,尽可能少写文档。你说你辛辛苦苦写了个一千页的产品需求,就算你不累,看的人累不累?有这时间,就把产品、开发、测试的三个经理关在一个房间里讨论,讨论到三个人都同意了才放出来,这个事儿就成了。讨论过程中,开发人员知道怎么开发了,测试人员也知道怎么测试了。你们觉得该补的一些核心文档,后面补一补就行了。因为市场变化太快了,等文档都写好了再讨论,肯定后面又得调整。 + +我自己特别不喜欢看需求文档,而且大家应该都觉得看文档挺累的。国内的情况跟国外还不一样,外国人就是觉得写个文档出来很清晰。但是我觉得国内大部分人都没经过这么好的训练,还是习惯直接沟通。 + +还有一个关键点是,有时候最核心的东西业务同学自己也没想清楚,辛辛苦苦做了这么久的文档,业务明天就推翻了重新来一遍,这不是浪费吗?我觉得比较合适的方式是,快速做出一个东西来,然后就拿上去试一试,不行就放弃。就是要快速试错、快速迭代,东西粗糙不要紧,关键是能让公司快速跑起来。 + +极客时间:快速跑的模式下是不是出问题的概率也大? + +汤峥嵘:只要做东西就容易出 Bug,所以必须得在效率和错误率之间找到平衡。我那时候会实时盯着大家的这些 Bug。一个产品刚上线的时候流量不大,出点问题就出点吧,业务可能觉得影响不大,速度快可能比这个 Bug 率更重要。但是如果Bug会影响业务,刚上线就出了一大堆 问题,那肯定会被被业务投诉。所以这些都是需要技术和业务两方提前商量好、说清楚的。 + +这里有个前提,就是技术团队修复Bug的能力要强。这个能力其实对技术要求挺高的。比如需要实时监控的能力。我经常讲,优秀的技术团队要能比用户先知道系统问题。我们用真实的一小批用户试错,等到大规模用户来临前,我已经把Bug修好了。 + +我们技术人容易踩一个坑,就是沉浸在技术当中,又想做得快,又想做一个很好的东西,这确实成本很高,很难达到。但是用这个视角看问题,就很容易不清楚业务的需求。技术跟业务的关系是啥样的呢?用打游戏来打比方的话,业务和技术是打配合的。业务人员说今天要把刀,技术人就得今天扔个刀给他。技术人先不要想做一把多厉害杀伤力多强的刀,业务的人可能也不 Care 这个,他们就是想要把能用的刀,能大概砍几下就行了。 + +那厉害的产品经理牛在哪儿呢?他跟业务的人聊完之后,就能明白在这种场景下业务真正的需求是什么样的。比如飞镖有时候就比刀的效率高,可能一个飞镖一下就能杀死十个人,产品经理就能说服业务方,其实飞镖更适合你。这时候业务的人才恍然大悟:还有这个玩意,挺好,我们要的就是这个。厉害的产品经理就是这样,需求看得很准。 + +所以,业务、技术、产品确实相爱相杀,在一个快速发展的组织中如果有哪一方很牛,能纵观全局,带着另外两方向前跑,结果还不错的话,我觉得这就是比较好的协作模式。 + +互动时间 + +你目前是在业务、技术、产品哪个团队?欢迎你聊一聊和协作团队相爱相杀的故事。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/13CTO直接下属有60个总监,怎么管理?.md b/专栏/超级访谈:对话汤峥嵘/13CTO直接下属有60个总监,怎么管理?.md new file mode 100644 index 0000000..6f95189 --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/13CTO直接下属有60个总监,怎么管理?.md @@ -0,0 +1,73 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 CTO 直接下属有 60 个总监,怎么管理? + 编者按:途牛总部在南京,业务飞速发展时期,公司创始团队也在考虑是不是搬到上海去,因为当时携程在上海,去哪儿在北京,都是在一线城市。而且,从业务角度来讲,大的旅行社都在这些大城市,途牛本身也避免不了要在上海招人,所以就在上海设立了一个办公室。最多的时候,上海和南京两个办公地,有 60 人直接向汤峥嵘汇报。为什么设置这么多的汇报线?直接管理这 60 人压力在哪?这一节我们聊了这些问题。 + +极客时间:在途牛技术团队有 1500 人的时候,你说有六十人直接向你汇报,为什么是这样设置?这样的模式是不是就需要这六十个人都比较成熟,自身相对能够做很好的完成决策,要不然你的压力会很大吧? + +汤峥嵘:我的团队那时候都是差不多二三十人一个部门,我下面的总监团队大概有 20 多个人在上海,30 多个在南京,我一般一个月去上海一次到两次,去做单独沟通,平时就是靠邮件或者办公软件。 + +你说的对,如果大家都不够独立的话,肯定会压力大,所以我认为要在流程上解决这个问题。一个是招人的标准,一个是淘汰的标准,这两个我要定好。第一步招人是最最重要的,我招人的时候是花了很大力气的。另一个关注的阶段,就是淘汰人,淘汰的标准我也会提前说好。技术能力是首位我先不说,在项目管理这块,我希望他们能让我提前预知风险,有什么问题预感到不妙,要提前让我知道,不要拖到最后一刻。 + +我记得有一个总监,技术很强,但他是比较内向的人,有问题不爱提前暴露出来,每次我知道的时候已经变成大问题了,我就问为什么不提前说?我也跟其他人说,你们得帮他,不要帮他掩盖问题,最后等出了大问题的时候,再去淘汰他。我们是一个团队,你们今天帮他去掩盖问题,明天他可能就要被淘汰,你们就害了他。我的原则就是有解决不了的问题要提前让我知道,我可以来调节。 + +如果他一开始就跟我说,老汤,我们这个业务方盯的很紧,我现在觉得这个时间赶不上了。那我就去解决嘛。越早告诉我,我就越有机会,去跟业务方沟通到底什么问题,因为他想做个事但现实情况确实做不到了,他自己可能也说不动业务方。但是我这个级别有可能用更低的成本说服人家。 + +我经常说,你们找我做事,你们想好,为什么找我?因为你搞不定了,就这么简单,我比你级别高一点,我讲话就力度大一点,就节省内力。你们一定要把我当成资源, 把我用起来。但是反过来,你站在我角度,假如你判断这个事找我和你自己做是一样的,那显然就是浪费我的资源了。而假如你已经试了各种方法都搞不定了,请我出个手,或者说你已经判断出来,假如自己去讲的话,可能要讲三天三夜,换成我可能讲三分钟讲完了,这种情况当然把我用起来。 + +我跟他们沟通,都是在解决问题,我是习惯性在解决问题过程中,去看每个人的潜质,在心里对他做个评价。如果我平白无故地去跟他去聊天,也聊不出啥来,大家肯定都说自己现在挺好的。我也是一样,老板找我来聊的时候,我肯定也不想说我现在有多难。如果总是表现很悲观的人,我反而不想留他,我认为人就是应该乐观的。一个人自我评价高、总能看到希望,我觉得是个好事。但再乐观的人总会碰到难题嘛,和我说就好了。 + +如果我再搞不定,我心里也有个预案,做好准备,都是可以在风险预估之内。 + +极客时间:但这不就需要你的一次次决策,就会占用你的时间和精力,六十个人如果经常都是这样不停的报问题,你的管理成本不就很高?如果在这六十个人上面再设一层,比如你只管理十个人左右,压力会不会小一点?为什么当时没有这样做呢? + +汤峥嵘:那肯定压力会小,但是我反而认为这些事情是我逃不掉的,那个时候必须要承担这些压力。我们那时候公司发展速度太快了,如果再在这 60 人上面加一层管理团队,我认为效率会变低。而且,当 CTO 离一线越来越远的时候,会真的看不见问题的。因为下属给你展示的基本上都是经过包装的真相。 + +当有五六十个人的时候,有一部分问题我已经看不到了,但我还能保证看到一些很重要的问题。如果说只有五个 VP 向我汇报,大概率我只能看到 10%的问题,剩下的 90%都被他们给盖住了。 + +极客时间:理解,我很好奇这六十人你怎么开会呢? + +汤峥嵘:那时候老板给了我一个特别大的办公室,我的办公室里放个很长的桌子,围一圈大概能坐 20 个人,剩下的人稍微站一站就行了。那时候我们开周会一般都是两个小时。 + +极客时间:这么多人开会,每周要开两个小时,你怎么定义周会的目的? + +汤峥嵘:第一,大家确实需要每周有一个碰面的机会,让大家讲一讲各自的数据、思考。第二,我想让大家知道我重视每个人的工作,我在关注他们。每周我肯定会把公司的一些新动态给大家讲一讲。这个就是例行周会。对管理团队,除了这个会,那时候我还经常会办培训会,一周抽出一个晚上,把大家召集过来,给大家做一些管理培训,这是对周会的补充。比如我前面讲的职级搭建,大家对做这种考核不习惯的时候,我要让他们认同,关于阿里的一些管理方式啊在这个培训会上我都讲过的。在途牛和 VIPABC,我都有做管理培训会这个习惯。 + +极客时间:你说这六十个人基本上能顶住一片天,那应该能说明你选人的能力非常强。招聘这一步常常会遇到面试看走眼的时候,你会怎么去面试总监级别的人呢? + +汤峥嵘:我面试的时候喜欢盯着一些细节去问。因为到总监级别,简历肯定都是很不错的。当然有的简历一眼看过去,没有相关经历,没有啥经验的肯定不行。但也可能这个人水平很高,只是他写不出来,那这种连简历都写不好的人,我也没办法,就不要了。 + +面试的时候候选人会请他们稍微讲一下自己的工作,我会判断几件事,第一,从他经历中挑几件比较重要的事,看他到底是他自己干的,还是因为公司的光环,或者是他把别人的工作安到自己的头上,会追问一些技术细节。第二,我会把我们公司遇到的一些真实的问题直接抛给他,看他怎么解决。假设这个人能通过,后面如果发现工作与面试表现不匹配,我也会反思是我当时给他的问题有偏差,还是就是这个人不行,我判断错了。 + +极客时间:我挺想接着这个招人的话题聊,你会喜欢什么样工程师,假设有几个工程师要竞争你下面的总监岗位,你会看什么特质? + +汤峥嵘:第一肯定是聪明,我觉得聪明是很重要的。但是要做到聪明应该不是一件难事,因为大部分人到了一定级别,智商都不会太差的。 + +第二,就是我喜欢比较单纯的。单纯这个点绝对是看到这么多优秀的人的共通之处。有的人就是活在简单的世界里,他到这个岗位就关注自己做的事,攻克了一个难题就很开心,可能也不太关注外面的机会。能在 20 到 30 岁的年纪遇到一个让自己沉下来工作的环境,是好事。对工程师这个群体,绝对就是要沉得下来。 + +第三,我喜欢责任心强的人,这也是很重要的。你给他布置一个任务,他要有主人翁精神,把事做成,我觉得现在很多人缺乏的就是责任心。 + +极客时间:你会怎么去培养这些人? + +汤峥嵘:具有这些特质的人是很好培养的,就是给他一个比他的水平稍微高一点的任务,让他去完成。适当的时候给他一些方向的暗示,告诉他哪个大方向是对的,但是同时呢要让他觉得这是他自己努力做出来的,这会让他产生满足感。然后再多跟他们聊聊天,多点鼓励,他们就会很开心,也会更有信心。 + +因为我经历过这个过程,我当初也是这样一个人,别人给了我鼓励我会觉得很开心,我就能不断往上走。其实优秀的工程师还有个特点,优秀工程师面子很薄的,很看重自己的口碑和别人的评价。这里我只说工程师啊,像很多优秀的创业者、企业家这些人还是要做到很能抗压的。 + +你要是跟一个工程师说“你这么厉害,怎么连这个东西都搞不定?”优秀的人肯定很郁闷,他心里就会想,我一定把它搞定了,因为我这么优秀。作为他的领路人,就要学会激励的方法,让他不停地去追求更高的东西。 + +我这样的人是特别能理解这种工程师性格的人。我认为一个好的 CTO 真的就是要做到自己的技术好,你才有同理心,才能知道那个技术好的人是什么样的人。所以我说管理是一个主观的活,因为你喜欢这种类型的人,你就会提拔和鼓励这些优秀的人,整个管理过程都是反应管理者个人的偏好的。大概率你是什么样的人,你就会带出什么样的人。 + +极客时间:你在途牛管理的人都是工程师,是不是正是因为是同样的角色,你就能 hold 住这直线汇报的几十人?假设除了技术外,还有设计、运营等等需要你去管,就像 CEO 这个角色,就不太可能有六十个人直接向他汇报。可以这样理解么? + +汤峥嵘:你说的对,因为这群人的工作是同质化的,我就能相对扁平的管理,用一套方法就可以管理很多团队。如果还有很多非技术的团队让我管,按照这个模式我肯定就累死了。所以这算是在特定情况下我选择的一个方式。 + +互动时间 + +你是否有被领导鼓励过的经历?是通过什么方式呢?欢迎在评论区分享。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/14无边界访谈:创业思考与高手视角.md b/专栏/超级访谈:对话汤峥嵘/14无边界访谈:创业思考与高手视角.md new file mode 100644 index 0000000..11adaff --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/14无边界访谈:创业思考与高手视角.md @@ -0,0 +1,89 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 无边界访谈:创业思考与高手视角 + 编者按:汤峥嵘目前在创业,站在一家公司创始人兼 CEO 的视角上,他给我们分享了一些对创业的思考。同时在我们的采访中也发散聊了几个话题,不再单独成文,这几个话题可能是很多人在职场中都会遇到的问题,那不妨一起来看看高手视角是怎样的。 + +创业思考 + +极客时间:你最开始创业的时候,会给自己定目标说,用几年把公司做到多大规模吗?如果没有做到,心里会不会有落差? + +汤峥嵘:创业的人肯定大部分都有这样的期望,很想一年就变成一个很大规模的公司。但还是要着眼现实,现实的情况就是,很难在一年甚至三年这么短的时间,就达到一个较大的规模。 + +我们创业的最开始,我就和团队说,我们这个事业起码要做 10 年。请团队来想象一下 10 年以后,公司会发展成什么样,会对社会有怎样的影响。但这个也说不准,也许 10 年过后,目标没有达到,还得再接着想下一个 10 年。对这个情况,我是有心理预期,可以接受的。 + +其实,很多创业公司,他们最担心的可能还不是用几年时间能不能做大。很多时候像我这样的创业者会更担心过早投入,可能这个领域崛起的时机还没到,我们就投入大量的资源,但市场的反馈没有达到预期的话,反而变成“先烈”。等你把这条路趟出来了,别人开始入局收割,有太多案例了。这可能是现在一部分创业者的担心。 + +经过这么多年,我也体会到自己身上的改变。比如之前在途牛、VIPABC,我都是带着团队去拼、去冲的,真的是起早贪黑地去拼。那个阶段就是自己身在局中,被市场逼着、被自己逼着往前跑的阶段。我虽然内心觉得,做技术要慢一点,要做到极致。真的热爱技术的人,可能都是这样的,但是我们身在企业中,有自己的责任,要考虑市场规律,有时必然要舍弃一些东西。 + +因此这次创业,我也想做一些改变。有的人选的是已经比较成熟的市场,那一定避免不了激烈竞争。你如果不起早贪黑,竞争对手可能很快把你超越。我现在做的智能穿戴,技术和理念都相对超前,就相对可以慢一点,追求极致一点。而且现在整个社会开始有“慢”的趋势了,大家更追求品质,更关心自己的健康、生活质量。对我们来说是一个特别好的信号。 + +极客时间:你是从什么角度去看各个行业、不同领域的发展趋势的,或者说你关注趋势么?比如你创业,为什么会选择健康大数据领域?而没有选择其他的? + +汤峥嵘:我的想法是这样,哪些领域好,有前景,对于这种大趋势其实每个人都能看得见。就像元宇宙,很多人入局,但轮到你入局不见得能做出什么东西来。而我呢,比较喜欢从小处着眼,从身边找机会。正好碰到智能穿戴这个机会,它就在我身边,是我能够得着的,就是适合我的。 + +我剖析自己,我的优点是很理性,缺点是直觉不够,大趋势出来的时候我可能比别人反应慢一点。也许可能别人做了什么创新,把你颠覆掉了。商业就是这样,发展中必然有竞争和颠覆。所以我现在也在学习,看看书,我也挺好奇这些颠覆、被颠覆背后到底有什么规律,哪些事是我可以学习的。 + +拿做菜做个比喻,川菜可能很多人都爱吃吧。假设要做餐饮,有的人很有大局观,能看到市场空白且可发力的地方,比如川菜在这个地方还没有的时候就打广告,让所有人知道川菜好吃,先把客户吸引来了,他再去做菜,这是一种打法。我可能比较保守,我得让所有朋友都吃一盘菜试试,好吃的话再去做更多的。 + +这个没有对错,而是跟性格有关。靠直觉敢做决策的人,可能会嗅到市场的味道就立刻冲进去。像我这样实感型的人,可能喜欢准备好了再冲进去。 + +极客时间:这个选择确实没办法在开始就知道对错、结果。我很好奇你现在因为身份的改变会让你看待公司的感觉不一样吗?很多公司发展到瓶颈的时候,增长疲软、市值下跌……可能会面临各种各样的难题。比如你说过途牛经过高速发展后来也经历下降的过程了,现在加上疫情影响,整个旅游行业发展也并不是很好;你也经历了VIPABC被平安集团收购。互联网大厂的收购案更多,如果一家公司没有蜕变好的话,可能就是要面临破产、收购的结局。在这样的时刻,被收购是不是一个好的选择呢?但是很多遇到过这种问题的创始人还是觉得不甘心吧。你现在自己创业有想过这些问题吗? + +汤峥嵘:这是一个对创始人心态,以及他对企业发展的理解,和企业生命周期理解程度的考验。企业和人一样,都要经过很多次蜕变。快速增长时期需要的能力、稳步前进时需要的能力,以及上市之后需要的能力都不太一样。像阿里这样的企业,做到这么大,蜕变过太多次了。 + +那站在 CEO、创始人的角度,确实对能力、视野、责任感的要求都上一个台阶,我也在适应身份的转变。在看待一家公司发展的时候,创始人得怎么想呢?今天我还是这家公司的负责人,我就要尽心尽力把它打理好。但是当有一天我已经做不了这个公司的负责人的时候,那可能就得放弃,找一个能力更强的人来接班。难是难在,你看我今天说这话很容易就讲出来了,但假如真有那么一天,你再问我能不能接受,能不能交给别人?我可能也不能保证。这可能就是很多被收购企业的创始人经历过的煎熬吧。 + +极客时间:经历过途牛和 VIPABC 这两个创业团队之后,你这次应该会更加从容吧? + +汤峥嵘:我尽可能先把我能掌控的事情从容地做好,假如未来真的出了可能让我纠结的问题,我也做过思想准备了,就可以避免陷入不好的情绪。 + +高手视角 + +一、企业一定有缺点,做好准备 + +极客时间:你选择加入一家公司的时候,会关注它的缺点么?就像你加入 VIPABC,你知道它的优点,看到技术能发挥重要作用,你会同时关注它的缺点么?比如你看到某个缺点不可接受,就不去。你做选择的时候有这个想法么? + +汤峥嵘:我去 VIPABC 这家公司,能看到最纯粹的优点就是它的选课系统,很厉害,这个优点是吸引我过去的。其实任何人加入一家公司之前,肯定是看到了这家公司的优点,足够吸引你,你就会去。但我会在心里告诉自己,它肯定也会有缺点,只是我现在没看到,我得告诉自己得做好准备。任何事都有两面,一个优点的背后,肯定就是个缺点。 + +二、薪资倒挂有平衡方法,但同时接受世界的不公平 + +极客时间:像薪资倒挂这样的问题,你在的这几家公司应该出现过,有什么方案做平衡么? + +汤峥嵘:薪资倒挂这个问题是我们以前经历过的,在阿里的时候,我们就已经把它解决掉了。首先,接受这个现象,为什么这么说?因为它的本质是市场涨薪太快了。我们得接受两个人级别一模一样,但可能新员工比老员工薪资高。然后,用设置薪资带宽的方式去慢慢平衡,当然所有这些制度的前提条件是员工之间的薪资要保密。 + +在阿里我们定薪资的时候设置带宽,至少分成高、中、低三档,比如都是 P5,会有初级,中级、高级。如果两个人级别完全一模一样,都是 P5 中级,但是一个是新来的薪资高,另一个老员工薪资偏低,我们调薪的时候就给老员工稍微多调一点,涨幅大一点,新来的人涨幅就小一点。不是把老员工的薪资一下子调到跟新员工一模一样的,而是逐渐往上调。因为你还要考虑人性,你一下调得高了,他可能猜到自己被倒挂,反而心里不平衡了。下次再涨薪的时候涨得少了他还可能产生落差。 + +还是那句话,我们必须得接受,这个世界是不公平的。 + +极客时间:我假设一个场景啊,假如你是我的老板,我有一天跟同事互相聊了薪资,聊完之后发现他工资比我高,我就去找你,说为啥我们俩级别一样,而他的工资比我高?你会怎么处理这种情况,有经历过这种情况吗? + +汤峥嵘:经历过。首先你来找我说这个事,我就可以不回答你。反问你,你怎么打听来的?公司明明是不允许互相透露薪资的,这是有规定的,你要对这个薪资不满意你可以离开。遇到这种问题,其实我没有必要进一步解释,因为解释成本很高,就算解释清楚又怎么样呢? + +如果比薪资,永远比不完,除了跟同部门的比,是不是还要跟别的部门比?凭什么销售比你工资高?销售也会问,凭什么做技术的就工资高?对吧?这个就太难比了,很多因素在里面。 + +这个世界就是不公平的,永远存在吃亏的时刻。但是换个角度看,老员工有老员工的好处,老员工在公司里的人脉、业务熟悉度各方面都比新员工好,哪天要晋升的时候,他可能就被提拔上去了。然后他工资就比新员工高了,这也是一种平衡。这个时刻吃亏,下个时刻也许好运就降临了,就是这样。 + +三、评判研发工作,我倾向定量考核 + +极客时间:对技术人员的考核,之前有很多争议,比如看加班时长,看代码行数之类,这个东西在外面看起来批评多于褒奖,你怎么看程序员的考核机制?从你的视角看,你更倾向 OKR,还是用 KPI? + +汤峥嵘:像你说的,有的公司考核员工,看代码行数。我们所有人都知道如果看代码行数,你可以写一大堆注释,即使你把注释扔掉,也有人一行拆两行,大家总会有方法去应对这个考核方法。我们怎么能从代码行数看出来这个人优不优秀呢?这些确实都不是真正能够选拔出优秀人才的标准。 + +但是,你要随便问一个程序员:怎么判断这个人是不是优秀?很多人肯定觉得标准是:他做出来的产品很牛、代码写得很好、Bug 很少……那什么叫好?什么叫少呢?大家都喜欢用定性的词。但要管理这么多人的话,就得定量。这是目前整个研发体系的悖论,大家都特想做出一套公平的体系,最好是能直接机器打分的,但假如要用这套机器管理你,你愿不愿意?肯定很多人又不愿意。一方面我们不愿意接受客观数据的考核方式,另一方面又对主观考核持怀疑态度。 + +从公司管理角度,我认为公司还是要定量,虽然这不是程序员们喜欢的方式,但公司到一定规模,应该要有标准。同时团队管理者要为员工发展考虑,这样就比较倾向 OKR 的管理方式了,但我们之前的模式有点类似 KPI+OKR。 + +我在这几家公司(途牛、VIPABC)的考核方式,都和公司整体考核体系保持一致,用的 KPI。但是我给技术团队的 KPI 是比较粗的。我首先给我下面的总监级的人定目标,把技术工作和他对口的业务联系起来,30%是和业务业绩直接挂钩的,这部分就是一个客观数字。剩下的 70%是技术指标,其中大概 50%要考核可量化的指标,比如系统稳定性、代码质量等等;另外 20%因人而异,针对每个人的能力模型做不同的要求,比如:如果这个总监带的团队技术比较弱,我会给他一个做培训的考核指标;如果这个总监和业务合作不是那么紧密,我会再增加业务方的满意度指标;如果是一个技术能力很强的人,我会希望他能多在团队内分享经验(提前沟通并确认目标),也会算作考核的一部分。 + +到一线员工身上,设置更灵活。在和业务业绩挂钩的指标上不是设置 30%这么高的比例,可能 10%,让他们心里知道和业务是绑定关系的就可以。总体来说,我在给技术人定考核标准的时候,一定是因人而异的,一定有 20%是针对他个人的提升。 + +互动时间 + +采访的正文内容到这里就先告一段落了,在课程结束,汤峥嵘还有一封给所有学习过这个专栏的同学的信,感谢你的支持。最后,留一个问题给你:你为什么选择现在的行业、领域?可以分享你的思考吗?欢迎评论区留言。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话汤峥嵘/结束语给技术人的一封信.md b/专栏/超级访谈:对话汤峥嵘/结束语给技术人的一封信.md new file mode 100644 index 0000000..660b9da --- /dev/null +++ b/专栏/超级访谈:对话汤峥嵘/结束语给技术人的一封信.md @@ -0,0 +1,48 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 给技术人的一封信 + 你好,我是汤峥嵘。最后一期分享我来做个结尾。 + +首先,我非常感谢所有购买这个内容的朋友,也感谢极客时间的工作组,特别是编辑利莹。我是生平第一次录制音频节目。虽然有些内容反复录制了很多遍,但还是有挺多不太满意的地方。但是你的购买和留言区的留言,给了我很大鼓励。 + +虽然这个内容被称为课程,但其实是一些对话,所以我觉得它的标题更准确些。既然是对话,那么内容可能就不那么严谨,而且涉及的内容也比较泛,可能会让你感觉有点跳跃。幸亏有编辑支持做了大量的整理工作,让我的经验分享更成体系。不过我希望能给你呈现出一些你之前可能不太熟悉的内容。无论是我之前在海外读书和工作的经历,还是有关性格、情绪等方面的知识,我都希望能给你一些技术以外的东西。如果你确实能有一点新鲜的感觉,那我觉得目的就达到了。 + +大家在留言区的留言,我都看过了。从目前留言的比例看,大部分人对于性格分析很感兴趣,而且很多人把自己的性格测试结果发在了留言区。这说明很多人对自己的性格很感兴趣。但我这里一定要强调一下,不要给自己或他人贴标签,更不能因此就把自己给框住了,说这个工作不适合,那个工作更适合之类。 + +我认为从群体的角度,性格和工作之间的相关性肯定是存在的。但从个体角度看,其实不确定性非常大。所以我认为 MBTI 这类的性格测试工具,它的最大价值是让我们可以有个窗口去看外面的世界,让我们了解这个世界是缤纷多彩的。让我们多一个维度去观察自己,观察同事,观察家人。让我们多一个方法去理解他人。如果你的工作需要你去扮演一个和你性格不同的角色,那就大胆地去扮演。让自己的人生更丰富多彩不是很好吗?只是记得,经常换回到自己的本色,经常回家充充电,该休息就休息一下。 + +另外,我讲的有关生物情绪的底层逻辑,比如理性脑和非理性脑的理论,这些都是众多剖析情绪本质理论中的一种,也是我目前知道的比较详细的理论,所以分享给你。但我觉得科学就是要保持开放的心态,做好随时被更合理的理论推翻的准备。所以我也鼓励你多去看看一些比较新的研究,让自己永远保持着一颗好奇的心。这方面我比较喜欢听吴军(硅谷投资人)、华大基因的尹烨(著有《生命密码》)、媒体人梁冬,还有罗胖和得到上的一些内容,也推荐给你。 + +我现在创业做的项目是基于大数据人工智能的健康管理。对健康知识的积累也是我在创业过程中不断完成的,很多内容我之前是完全缺乏认知的。而在我更了解人的生理、健康密码之后,我才知道“健康”两个字的重量,因此很想跟你分享一些健康相关的知识,希望你也能更关注自己的身体,重视健康。 + +首先是心脏健康。很多年轻人认为自己心血管很健康,不会有心脏问题,像猝死这样的事和自己没半毛钱关系。但实际上,90%的猝死是心律失常导致的。而心律失常,有些是心脏和血管疾病引起的,还有些是因为焦虑、愤怒等情绪引起的。现代人压力越来越大,后者导致心律失常的比例也越来越高。建议你去看一看医学博士杨进刚的一段演讲,他说“愤怒可以使心脏病风险在 2 小时内上升 750%”。所以如果你感觉心脏附近不舒服,一定要重视。 + +一种方式是去医院检查,背一下 24 小时 Holter(动态心电图监测),等于给你做一个 24 小时的心电图。当然背 Holter 不是很舒服,而且检查窗口也只有 24 小时,时间不够长。我们希望能更长时间监测心脏(心脏相关疾病的检出率,时间越长越有保障),也就是我现在创业在做的事,把监测仪器做进衣服,可以长期穿戴,而且很舒服。我们已经发现,有些人一喝咖啡或者茶,甚至奶茶,就会发生心律失常。如果每天只有一两百次心律失常,那还好。但如果到了 500 次以上甚至 1000 次,其实就需要注意了,这已经是亚健康状态了,而医院一般对 1000 次以下的心律失常都不认为是疾病。所以我认为定期检测一下,可以非常有效地减少猝死的风险。 + +其次是因为打鼾而引起的呼吸暂停。呼吸暂停会导致睡眠期间血氧饱和度不足,大脑缺氧,严重的甚至会窒息。亚洲人特别是偏胖的人群患呼吸暂停的比例比较高。呼吸暂停分为 3 个等级,轻度、中度和重度。一般到了中度或重度,可能就需要做手术或者使用呼吸机了。但是很多人缺乏这个常识,甚至有人认为打呼声音响是睡得香。我们看到了很多人面临这方面的健康难题,也确信这是科技能发挥作用的地方。因此如果有人发现自己打鼾严重的,我非常建议去医院做一次睡眠舱的检查。睡眠舱检测虽然很专业,但体验很差,头上、脸上、身上要插满管子(一定程度也会影响你的睡眠)。我们希望能做出一个不用插满管子也能达到医疗级测量的产品,于是智能睡衣产品就出现了。如果有人关注大数据健康领域,欢迎体验交流。 + +第三个问题是缺乏深睡眠。睡眠通常分成三个深度和阶段。最深的叫深睡眠期,这时候脑电波很慢,也叫慢波睡眠,这是最放松的阶段;其次是浅睡眠期;最浅的阶段是快速眼动期,也叫 REM(Rapid Eye Movement)期。在这个阶段,脑电波比较活跃,眼球会快速转动。做梦往往在这个阶段。一般来说,人的睡眠从浅睡眠阶段开始,逐渐进入深睡眠阶段,然后再回到浅睡眠,最后到快速眼动阶段。这样就完成了一个睡眠周期。通常一个周期平均 90 分钟,一个晚上有 5 个这样的周期。开始的几个周期,深睡眠会比较多些。越往后,深睡眠就越少。- + + +现在科学家发现,缺乏深睡眠会导致患阿尔兹海默症(也就是老年痴呆症)的概率大幅提升。因为科学家发现阿尔兹海默症患者的大脑中,有一种叫β淀粉样蛋白的沉淀比较多。而深睡眠期间,会发生一个脑脊液清洗的过程,也被戏称为“洗脑”。在这个过程中,β淀粉样蛋白会被清洗掉。换句话说,缺乏深睡眠,就不容易清洗β淀粉样蛋白,也就提升了阿尔兹海默症患病的概率。而目前呢,阿尔兹海默还没有好的治疗方案。而且一旦阿尔兹海默发病进程启动了,就不太容易逆转了。所以大家真的要关注自己的深睡眠。 + +我认为缺乏深睡眠这个问题,从事技术和互联网的人群会比较多一些。因为大家都是偏脑力劳动者,缺乏运动。白天动脑过度,晚上就容易做梦。我曾经就非常缺乏深睡眠,一开始用手表手环测,没有发现这个问题,数据都是 1-2 小时深睡眠。但用我们的产品测,发现一晚上的深睡眠经常不到 10 分钟。后来我专门去医院做了专业检测,确认就是这么少,那我就开始紧张了。但是我通过科学的训练,现在已经把自己的深睡眠调整到了 1 个小时,甚至接近 2 个小时。 + +我这里分享一点经验。一是要多锻炼,也可以练习站桩冥想等活动。马云的太极老师阎素杰大师曾经给我们公司的人开过一个 21 天的站桩初级课。很多人都反映练了以后,睡眠有改进。我自己的数据也特别好,连续好几天达到过 1 小时 40 分钟,20%左右的深睡眠。这几乎是从来没有过的。另外,室温很重要。美国睡眠协会建议的最佳温度是 15-18 度。现在我夏天的时候就开足了冷气,然后盖大棉被睡觉,效果确实好。也可以在睡前 30 分钟泡个脚或洗个热水澡,让体温先上升,等睡觉的时候,体温迅速下降,也可以达到同样的效果。 + +再有,就是要养成有规律的睡眠。倒不一定是要早睡。有人习惯早睡,有人习惯晚睡,每个人都有自己的生物节律,或者生物钟。你要试试找到自己的节律。习惯晚睡的人,早睡了反而睡不着。因为身体的生物钟一直在抗拒,因为还没到睡觉的时候。现在很多人出现的问题是等到了该睡的点,反而没有了睡意。有科学家提出一个“黄金 90 分钟”的理论。就是一定要把 5 个睡眠周期的第一个睡眠周期睡好,后面的自然就好。而且这5个睡眠周期的规律是,越到后面,睡眠深度越浅。因此如果一定要牺牲睡眠时间,可以牺牲一点后面的,但一定要保证第一个睡眠周期的质量。 + +最后一个健康问题是心理或情绪问题。这其实是现代人普遍的问题。大家可以通过心理量表进行测试(我们的智能衣也提供此类功能)。我在前面讲过多次,人在被情绪影响的时候,很容易做出不理性的判断或选择。所以了解一下自己的心理状态,对于我们回归理性还是有帮助的。当然如果发现自己有较强的焦虑或抑郁情绪,我强烈建议还是要去医院或者心理诊所看看。其实当我们接受了自己心理上有问题的时候,我们的问题已经好了一大半。很多人会害怕自己的心理问题被人发现,导致心理负担更重,这种情况更容易加重病情。因此你自己一定要了解自己的心理状态。 + +好了,一口气把这多么健康的知识分享给你,可能需要一点时间消化。以后有机会,我再给你分享更多的健康知识。那么这个专栏今天就在这里结束吧。借着健康这个话题,我也祝所有人身心健康,工作顺利,家庭美满,生活幸福!再见! + +最后,希望你能花两三分钟填写一下这份问卷,非常期待能听到你对这门课的反馈。 + + + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/00开篇词蚂蚁集团玉伯:人生不设限.md b/专栏/超级访谈:对话玉伯/00开篇词蚂蚁集团玉伯:人生不设限.md new file mode 100644 index 0000000..2642ebb --- /dev/null +++ b/专栏/超级访谈:对话玉伯/00开篇词蚂蚁集团玉伯:人生不设限.md @@ -0,0 +1,62 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 开篇词 蚂蚁集团玉伯:人生不设限 + 你好,欢迎来到《超级访谈:对话玉伯》这个专栏,我是专栏编辑利莹。 + +专栏缘起蚂蚁 A 空间 + +3 个月前,我在蚂蚁 A 空间一个会议室见到语雀创始人、支付宝体验技术部负责人玉伯,终于开启了这次采访。 + +玉伯真名王保平,有一个被人熟知的标签——前端大牛,他在淘宝时期曾经作出过前端领域很火的框架 SeaJS、KISSY,之后也一直带领团队通过开源做了很多技术产品,包括 UI 设计语言 Ant Design、数据可视化框架 AntV 等,是一个妥妥的技术大咖。另外,玉伯还有一个慢慢显现的标签——产品新星,他对生产力工具情有独钟,文档产品语雀是他的作品。他曾经说自己有三个梦:技术梦、产品梦、自由梦,“产品梦重要的不是做事的方式,而是做成事情本身”,他把在大公司内部做创新产品称为内部创业。 + +从技术到产品,玉伯都在尝试。我问他“从技术人到产品人的转变过程中,你遇到过哪些困难,你的思考方式会不会有什么转变?”玉伯分享了他的逻辑。 + +大众意义上,语雀才算产品,但这个理解也许有点狭隘,因为技术本身也可以是产品,玉伯一直以做产品的心态在做技术。曾经疯狂做开源,对开源技术他也是当成产品去做的。因此,所谓身份转变,可以说是从技术产品转向业务产品,而从技术产品到做业务产品最大的变化,就是对技术、产品、运营的整体理解更有层次。 + +玉伯与产品的不解之缘,并非从语雀开始。2008 年加入淘宝后,他曾参与淘宝内部赛马,跟团队搞了几个月创新产品后惨败,失败后回到 Java 团队搞技术。但因为创新产品之心不死,2010 年到 2011 年,他实际上一边做技术,一边继续摸索创新产品。他和同事一起做了一个图片社交产品,想做“中国版的 Pinterest”,最终这个项目里的小伙伴选择出去创业,玉伯选择留在阿里。又剩下他孤苦伶仃一人,最后转去支付宝。 + +一个产品能诞生、突破荆棘成长不是一件容易的事,玉伯像坐过山车一样几度起落地折腾,也让他看清两点:一、技术是工具,是要为产品服务的;二、单枪匹马难成事,有团队才能走得更长远。 + +玉伯加入支付宝后,创建体验技术部。他带领这个团队一路狂奔,在创新产品这条道路上越走越远,也越走越有方向。其中故事有成有败,在此次采访中,他也对这些或成或“败”的项目,以及自己一路走到现在的经历没有保留的一一做了分享。 + +加入阿里前的故事 + +在这里先讲一讲他加入阿里前的故事吧。2008 年加入淘宝 UED 是他职业生涯里很重要的一段经历,也是转变的开始,在加入淘宝之前,他就已经在关注用户体验。 + +当中科大很多本科同学都在准备雅思、托福,想要出国的时候,玉伯发现自己觉悟晚了,自己的学分已很难申请到国外学校的全额奖学金,家里的经济条件支撑不起出国读书。他最终选择在国内读研,保送中科院物理所硕博连读。 + +研二开始进实验室,要做一些光学材料的研究,在实验室废寝忘食的经历很难忘,有时候做实验连做几天,就直接在实验室里睡。实验中间涉及到大量的数值计算,这个时候开始需要写程序了,玉伯对计算机编程真正感兴趣也就从这个时候开始。处理实验数据要寻找规律,要求研究员有很强的数据处理能力,玉伯在这个过程中升级打怪,感觉自己天赋好像还不错,这种自我认知加上周边人的认可让他充满信心,这也是他真正入行计算机的一个原因。所以当他意识到好像写代码比写 paper 有意思多了,就在心中萌生了退学的想法。 + +研三退学后,他进入中科院软件所工作。在软件所工作的三年里,做的事情很杂,除了做技术,他还学习了设计、交互,并对用户体验产生了浓厚兴趣。在那里,尽管玉伯的工作压力不算大,但个人的成长压力很大,因为他看到互联网正在改变着身边的同学、同事,最直接的体现是在工资上。而对当时的互联网人来说,梦想的殿堂是微软、Google等大公司,因此,他也曾应聘微软的用户体验岗,但在最后一面被刷掉了。 + +后来玉伯才尝试投递淘宝 UED 的岗位,2007 年投完,两三个月没有回复,一直到他辞去软件所工作,到 2008 年 1 月份才收到淘宝面试电话,于是一个人从湖南跑到杭州面试、入职。后来才知道,这中间等待的三个月原来是因为他的简历邮件被过滤到垃圾箱,他的主管过了三个月才看到,差点错失一个面试题满分的人。对玉伯来说,最初加入淘宝,其实一开始并没有非要做前端,更多是觉得先有一份工作,而到此刻为止,玉伯才算开始他的西行之路,更深入地接触前端、体验和产品,逐步实现自己的三个梦。 + +这是他加入阿里之前的故事,加入阿里之后,一方面依托平台的力量,一方面也是依托自己的力量,积累能力与作品,不断创造一个个向上的转折点。具体的故事,就在专栏里和你相见吧! + +专栏规划 + +专栏分为技术、产品、管理三大模块,交付玉伯的经验和洞见。采访速记超过 16 万字,最终整理为近 8 万字的专栏和你见面。极客时间还向玉伯提出诸多问题,比如: + + +作为前端圈子里的大牛,你是如何看待前端的价值的? +互联网浪潮退去,客户端工程师何去何从? +你探索了很多产品,你怎么理解产品经理的角色,自己又是如何进阶的? +分清什么是反馈,什么是需求,为什么说这是产品经理的第一课? +一个产品从 0 到 1 难在哪,从 1 到 100 又有哪些必要条件? +对于文档产品语雀,你的最终畅想是什么?玉伯也将首次谈及。 +支付宝体验技术部能在蚂蚁内部保持独特性,并让外部同学向往加入,你都做了哪些努力? +…… + + +针对这些问题,你会从接下来的访谈中找到答案。 + +每个人都是独一无二的个体,那些成功背后的故事也许并不特殊,但其动人之处在于,每一次面对面的经历,都引导我们走向思考,比如这次它引导着我思考一些“最初的”问题:人生的意义是什么?我们如何与世界发生更好的连接?相信你在专栏中也能慢慢感受到玉伯的人生观。 + +谢谢你看到这,接下来每一篇内容我们都会以访谈形式展现,欢迎你多发表看法,如果你觉得内容不错,也欢迎把它分享给你的朋友。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/01从页面仔到工程师,前端到底在发挥什么价值.md b/专栏/超级访谈:对话玉伯/01从页面仔到工程师,前端到底在发挥什么价值.md new file mode 100644 index 0000000..1700d1f --- /dev/null +++ b/专栏/超级访谈:对话玉伯/01从页面仔到工程师,前端到底在发挥什么价值.md @@ -0,0 +1,100 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 从页面仔到工程师,前端到底在发挥什么价值 + 玉伯在前端圈子里摸爬滚打十几年,他对前端价值的理解是什么样的?在他眼里,前端到底是一个怎样的岗位?我们带着这样的问题向他提问。支付宝体验技术部是前端同学最希望加入的团队之一,玉伯带领这个团队做出诸多创新产品,一定程度也能代表前端团队的边界拓展方向。因此我们希望把玉伯的理解交付给你,也许能给你不一样的视角。 + + + +极客时间:经过你这么多年在前端方向的实践,你觉得前端的核心价值到底是什么? + +玉伯:前端的核心价值,可以等同为一个问题:“公司为什么需要前端团队?前端团队因何而存在?” 我找到的答案有三点。 + +第一点,前端可以为公司降本增效,这是一个基本盘的价值。一个公司要做互联网产品,可以采用外包的方式,也可以采用自建团队的方式。为什么阿里等互联网公司采用了自建技术团队,核心原因是,自建技术团队,可以让产品研发更快,质量有保证,整体可持续发展。长期来看,互联网公司自建技术团队,可以大幅降低产研成本并保证高效产出。 + +组织设计上,技术团队经常会集中在一起,前端团队往往也会集中在一起。集中可以带来效率提升。假设一个业务需要 30 个前端来支撑,放到我这里,可能只需要 20 人就能满足业务需求。因为集中化管理,可以复用专业经验,我们知道如何更高效更专业地支撑业务。前端团队放在一起,在判断需求的优先级时,全局取舍会更自然发生。当前端分散在各个业务时,好处是能形成自闭环,但会带来一个常见问题:零散前端往往会被迫接好多需求。一旦前端是集中的,同时前端人员整体又紧缺时,面对业务需求,前端往往就不会再无条件接需求了。在需求的取舍过程中,就砍掉了很多没必要做的需求。砍需求往往是对业务的最大提效,不做一些需求,反而能提升需求质量,最终让业务做对需求。 + +但如果自己就是业务方,独立负责一块业务,很多 Leader 肯定就会想自己闭环最好,不然还得等排期。还不如自己直接招几个人,这样更高效,这是人性。但实际上,需要大家更客观去看。早期自闭环,可以让业务从 0-1 更高效。发展到一定阶段后,特别是各个业务板块需要互相关联时,集中化的技术支撑,往往能复用专业能力,整体业务效能会更高。 + +我现在更能理解一句话:分工是整个社会效能提升的关键。工业社会的分工极大提高了社会运转效率。以前农业社会衣食住行所需要的东西都可以自己生产,这叫做自闭环,效率是极低的。正是因为有了社会分工,整个人类社会才飞速发展。 + +降本是最近几年才凸显。现在不少公司开始提经营责任制,各个 Leader 会更意识到要省钱。举个例子,设计师对业务来说很重要,但业务如果自己去招一个创意设计师,往往不如用设计大团队提供的创意同学,这样会更省钱。统一的设计或前端部门,可以整体统筹,议价能力也更强,可以非常实在地降低成本。 + +简言之,前端团队的存在,是因为技术专业分工能带来整体效能的提升。同时前端团队往往会是一个整体,集中化可以降低公司的整体成本。 + +极客时间:刚刚聊到增效,那你们怎么去和业务负责人沟通,能让他们感知到,实际上你们是在帮他提效的呢? + +玉伯:需要互相建立信任。如果业务方提什么需求你都拒绝,肯定不行。一方面要去接需求,一方面也要有勇气跟对方说我们的想法:为什么某个需求我们觉得不靠谱,为什么当前腾挪不出来同学来做新需求。可以主动把人员投入透明出来,让业务方知晓我们把人员都花在哪了。整个过程中,核心关键点是,如何让业务方相信前端的专业度。前端是技术岗位里离用户最近的,不少优秀的前端工程师经常具备不错的产品思维,往往能给到业务一些靠谱的建议。当这些好建议有一起两起,逐步跟对方开始有互信,开始建立正循环时,一切就好了。要相信,我们的专业,我们的善意,对方是能够感受到的。 + +带的团队比较大时,与业务大 Leader 的互信非常重要。具体业务 Leader 可能会抱怨“前端团队老不接我的需求”,但是业务大 Leader 往往会更有全局思维。资源投入的本质是优先级管理,上一个台阶看问题,很多优先级就能决策出来。 + +极客时间:前端团队放在一起,能够给业务更专业的指导或者专业的反馈。看起来你带的团队,前端还是有话语权的,前端的这种专业度是如何建立起来的呢? + +玉伯:这取决于前端 Leader 的想法。我一直有个观点,前端并不是为后端服务的,前端跟后端是平等的,共同服务于一块业务。首先自己心态要摆平,同时跟后端也要说清楚我们的想法。有些后端老觉得这是他提的需求,前端做就好了,但是需求不应该来自后端,而应该来自业务方。不少情况下,业务没有专职 PD(产品经理),后端兼了 PD 职责,比如在不少中后台业务场景,对这种情况,也要分开来看这两个岗位的需求。摆平位置之后,前端和后端一起以合作伙伴关系服务于某块产品或业务。 + +和后端的关系梳理清楚后,更难的,是如何能跟业务达成互信,这取决于你对产品域或业务域的了解程度。要能和业务对得上话,不能纯粹只是一个页面仔,要成为懂业务的工程师。鲁肃担任蚂蚁 CTO 期间,我刚过来蚂蚁不久,看到各种业务类型,琳琅满目,要学的东西很多。在了解业务之前,早期我也是个前端资源,后来随着越来越熟悉业务,对不少产品开始有深度理解,慢慢有了自己的判断,才感觉到能发挥的作用更大。 + +但我同时发现,前端再理解业务,一般来说都很难超越后端,因为后端掌握真正的业务逻辑。前端懂业务远远不如后端懂业务来得这么自然,但是前端懂体验,这是我们的长处。我们其实对于一些交互设计、体验细节、对用户使用产品的体感会更有优势。后端很多时候不太懂体验,他们甚至会觉得功能堆上去就好了,但是堆功能,往往会带来糟糕的产品体验。 + +衡量产品体验好不好,有好几个指标。传统常用的是 CSAT(Customer Satisfaction,客户满意度),很多行业都可以用。在 to B 领域往往会用 CES(客户费力度),用这个反向指标来判定用户使用产品费不费力。在 to C 领域常用的是 NPS(净推荐值),有太多介绍,不赘述。当我们能通过 CES 等专业指标去衡量产品体验,并能具体给出优化建议时,前端懂体验的优势就能体现出来。当我们体现出专业度时,大家就会尊重你、信任你。 + +极客时间:刚才聊了前端对业务的第一个价值点是降本增效,第二个价值点是什么? + +玉伯:第二个价值就是前端有助于产品体验的提升,因为前端是最靠近用户的工程师,这个真的是前端这个岗位能够给到产品和业务很大的价值点。一个产品最终展示给用户的界面,都是前端或客户端通过代码写出来的。前端在这个过程中要调试,调试过程中对产品的感觉很重要。当感觉不对劲时,好的前端会去找设计或产品同学反馈,共同去思考是不是哪里没考虑周全,是不是某个细节功能点有问题。我们常说,当代码写不下去的时候,大概率是产品的设计逻辑错了,这时停下来去修改,对业务的价值是很大的。 + +前端的体验优势,也有一个危机。老一辈的前端,有很多像我这种从物理、化学、生物等各行各业转过来的,是对体验有浓厚兴趣的一波人转行学做前端。这波老前端,普遍对用户体验的感知力很强。随着前端校招生源变好,大量计算机科班出身的同学开始做前端,最近几年有不少学算法的同学也来做前端了。这些科班出身的同学,整体特点更理性,更逻辑化,更像服务端开发,这些优势非常好。不足的是,不少新同学会对体验的感知有缺失,有像素眼、愿意写 CSS 做界面的同学越来越少。 + +举个例子,之前我面试的时候,经常会出一道题,拿 CSS 实现的页面给候选人看,这个页面中间有些像素偏差,可能只差一像素两像素,我想考的是他能不能看出像素偏差,看他具不具备“像素眼”。给老前端看,一眼就看出来了,但是现在很多前端新同学看了半天都觉得没问题,现在这道题我都不敢拿出来面了。以前前端对体验的感知还是不错的,我们之前已经往前走了一步,使得设计者关注核心的创意和关键元素就好了。早期设计可能不需要出完整设计稿,只需要出关键设计稿就行了,剩下的前端就实现了,效率很高。但现在不行了,现在前端和设计的协作关系又变成了设计要抓还原度。这个现状,是当下前端天空中的一朵乌云。 + +体验曾经是我们的优势,但是接下来慢慢也会淡化,目前我也在想一些其他解法。当下仍然是优势,至少团队里面还是有一半同学有这块的感知。现在一些前端领域的大会上,一些专家都在呼吁前端回归到关注体验,我认为这很重要。 + +科班出身的同学只是对用户人机交互层的感知变弱了,在前端工程化和专业代码逻辑层,以及往计算机底层探索的能力,都全面变强了。整体来看,在朝着更好的方向发展。 + +极客时间:降本增效、提升产品体验,前端在这两方面也都遇到来自业务方或者行业趋势的挑战。前端对业务的最后一个价值是什么呢? + +玉伯:第三个价值点,我最近几年有些感悟,发现前端技术开始真正为业务创造一些可能性。 + +我举个例子,比如数据可视化领域,当时中国最有名的产品应该是百度的 ECharts(注:2018 年捐给 Apache),ECharts 的基本思路是一图一表,比如饼图、趋势图、气泡图等,都是先有图,再去实现这个图。2014 年,我们也想做数据可视化,但如果只是再做一个 ECharts,意义不大。当时我们就在想,可视化领域有可能的创新点会是什么?如何才能做出差异化竞争力出来? + +于是我们开始研究学术界的进展,发现其实上个世纪就有一个学术大拿,已经写了一本书叫《The Grammar of Graphics 图形语法》。很厚的英文书,我们团队几个人一起研究,看完之后觉得如果以语法的方式去做数据可视化框架,我们非常有机会超越国内包括国外绝大部分同类产品。 + +当我们在 AntV 里,真的把图形语法实现出来后,发现跟 AI 领域可以天然结合。利用语法特性,能根据数据特征或用户指令,通过语法智能化生成一些图表,甚至可以生成全新的未见过的图表类型。这是产品方都没想到的,是技术给了业务新的可能性,并有机会成为一个产品亮点。 + +类似竞品在国外有,比如微软的 Power BI、IBM 的 Watson 等,也是倡导智能洞察,用户只要说一句话“想看特定人群过去一年的留存率”,它就可以把趋势图等洞察给展现出来。当时做 AntV 时,我们压根没想到智能图表场景,后来做着做着发现居然可以让业务智能化,才开始意识到前端技术也能成为产品的核心竞争力。 + +再举一个例子,大家可能都用过支付宝上的蚂蚁森林、蚂蚁庄园等产品。这些产品背后,是我们沉淀的一套图形互动技术。支付宝上不少应用已经不是传统前端应用,而是互动应用,具备不错的互动体验。基于这套技术,可以实现支付宝的五福、打年兽、神奇海洋等业务。这些互动应用可以做到较低成本研发(和传统游戏比),为支付宝的业务形态提供了新的可能性。 + +总结起来,前端的价值有三点:降本增效、提升体验、创新可能。 + +极客时间:经过这个过程,你觉得对于前端来说,大家的自豪感会更高吗,天花板会更高吗?因为逐渐有越来越多的事情可以尝试。 + +玉伯:这是一定的,我们都逐渐从页面仔变成了工程师。身份的转变花了很长时间,2014 年-2018 年对我来说,我觉得自己还是页面仔,2018 年以后才能自称为工程师。 + +Ant Design、AntV、前端工程化、前后端分离等事项,在经过 3-4 年发展后,到 2018 年才逐步显露成效。其他团队逐步不再把我们当成资源,会认可前端也是有技术厚度的,同时对效能提升和体验提升,也是显性可见的。 + +2018 年起,我们也开始有一个倡导,让前端工程师往产品工程师方向发展,目前还在路上,只在语雀等少部分团队实现了产品工程师的倡导。语雀的不少技术人,喜欢写代码,同时也喜欢语雀,在用技术实现语雀过程中,还抱着对产品的热爱和见解。语雀的一些产品模块是前端工程师在负责,这是语雀的核心产品竞争力的来源之一。 + +之所以语雀的前端工程师能成为产品工程师,有两个因素。第一个因素是全栈开发,语雀的主体是用 JavaScript 实现的,语雀很有可能是中国最大的 Node.js 系统,语雀 90%的代码都是 Node.js 写的。这意味着在语雀,前端不仅是前端,这个产品的后端实现、算法、运维等,前端工程师都在做,再加上对语雀的热爱,对产品有感知,前端写的代码是蕴含着对产品的喜爱的,在这种情况下,前端工程师就有机会成为一个产品工程师。 + +前端人群里,还很容易出现优秀的产品经理。语雀的产品经理、钉钉的产品经理、微信读书的产品经理、飞书的产品经理,据我所知,都有不少产品经理之前是做前端的。 + +当然,前端人群里,也有出现 CTO、CEO 等,虽然还不多。但我相信,数字化的大趋势下,有工程师背景的创业者,只会越来越多。前端从业者可以做的事情,也会越来越多。 + +小结时刻 + +玉伯总结前端对业务的三大价值,一是降本增效,二是助力产品业务的体验提升,三是前端技术逐渐可以成为产品的核心竞争力,为业务创造可能。其中,前端技术对体验的提升,在下一节我们会聊到,敬请关注。 + +你对前端这个岗位是如何理解的,如果你是从事前端的工程师,你希望做哪些突破边界的尝试呢?欢迎大家一块交流,我们下一讲见! + +延伸阅读 + + +玉伯:前端的现状之痛及未来趋势 +淘宝玉伯引发 Web 前后端研发模式讨论 + + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/02何为体验:把简单留给用户,也把简单留给自己.md b/专栏/超级访谈:对话玉伯/02何为体验:把简单留给用户,也把简单留给自己.md new file mode 100644 index 0000000..ce031f9 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/02何为体验:把简单留给用户,也把简单留给自己.md @@ -0,0 +1,71 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 何为体验:把简单留给用户,也把简单留给自己 + 2008 年加入淘宝前,玉伯曾在中科院软件所上班,在软件所的工作很杂,后端不愿意做的,他可能全包了,一边写前端,一边做交互设计,还要兼产品,那个时候开始他就对用户体验很感兴趣了。他进入淘宝时第一个岗位不是前端工程师,而是界面工程师。玉伯很早就开始关注用户体验,那么体验到底是什么,他如何理解这个词呢?到今天为止,用户体验的提升体现在哪,还有哪些做得不好的地方?他聊了聊自己的想法。 + + + +极客时间:你怎么理解体验这个词? + +玉伯:一般谈到体验,大家都会想到用户体验,这是业界通俗理解,狭义讲就是用户使用产品过程中的人机交互体验。我们内部在讲体验的时候是把体验分为两层,第一层是用户体验,第二层是研发体验。用户体验就是让用户用得爽,研发体验是让我们自己爽。业界说法是要把简单留给用户,把复杂留给自己,我们的提倡是:把简单留给用户,同时也把简单留给自己。 + +极客时间:你觉得在今天的互联网行业中,用户体验设计或者体验设计做得好的地方在哪里?有哪些你觉得做得还不够? + +玉伯:这个话题可以聊一下。我觉得做得好的,是守住了体验基线。以支付宝为例,支付宝每天有几亿用户,每类用户使用支付宝的场景都可能有所不同。在这么多使用场景里,如何保证不同用户能快速打开支付宝,能找到想要的功能点,能用完功能后回退到首页,能去探索一些新功能。在这么丰富的用户场景下,支付宝基本守住了体验地板,守住了体验基线。 + +极客时间:体验基线是什么意思?体验基线这个词感觉还是理解起来比较模糊,就比如说我们看某个产品的时候,我大概知道它挺好看的,然后用起来体验也好。但是标准是什么?一种感觉吗? + +玉伯:体验基线就是体验地板,是体验默认不糟心。这个点看起来好像挺容易达成,实际上挺不容易。体验基线的提升是系统化工程,经常会比单点的体验天花板的提升更难。 + +比如,在蚂蚁中后台应用大规模使用 Ant Design 之前,体验基线的提升几乎是不太可能的事情,因为需要推动每个系统单独去优化。后来 Ant Design 大规模应用后,我们可以自豪地说,只要用了 Ant Design 的中后台应用,页面颜值就上去了。虽然只是颜值提升,但实际上已整体提升了体验基线。 + +体验好的标准,挺难定义的。Ant Design 有个长远目标,是使用了 Ant Design 的产品,就能“默认好看并默认好用”。目前“默认好看”的目标初步达成了,但从默认好看到默认好用,还在过程中。 + +极客时间:那从好看到好用需要经历什么过程,需要怎么评判? + +玉伯:行业也没什么标准,只能说我的经验。去看一些中后台的操作页面,会发现其实很多时候,好用并不取决于长得是否好看,好用更多取决于产品的操作交互是否贴合业务场景。 + +分享个 Ant Design 在设计过程中的一个坑。我们内部有一个客户服务系统,Ant Design 在早期追求排版布局的美观好看。为了好看、有呼吸感,排版会比较大气,错落有致。但这种设计,对整个客服系统来说,就很痛苦了。后来我们才理解,一个好的客服操作页面,需要页面信息密度足够高,方便客服人员在一个页面里就能找到各种信息并做快速操作。这种情况下,别看页面上密密麻麻的,密密麻麻才好用,宽松排版虽然好看,但并不好用。 + +业界去讲体验不好的案例时,经常用的一个比喻是说飞机操作仪表盘是不好的体验,因为上面有非常多的按键,很不好用。但我要对这个说法打个问号,这很可能只是业界的一个谬误。如果真把飞机的操作盘简化成 iPhone 一样,那飞机可能就要失事了。要回到专业领域去思考,比如去调研真正在飞机控制室里的飞行员是怎么想的。我找过很多文章,到现在为止都没有看见真正从事飞机行业的人出来说话,都是一帮搞互联网行业的用户体验设计师在那里拿它举反例。 + +所以从好看到好用,我目前更多在做的事情是,让特定领域的设计师深入业务。目前语雀的 UED 在语雀团队,设计师必须懂业务,得去研究用户的场景是什么、用户的高频操作是什么,然后再回到设计上,考虑怎样做到体验最好。在这个过程中,大家在保证操作效率和好用的基础上,同样要保证好看,这个好看是指多数人默认的好看,不是设计师一人觉得的好看。通常好看是更容易做到的,好用是更难做到的。 + +在前端这块我们提过产品工程师的概念,在设计这块,我们也提过一个概念,叫产品设计师。我不大想提体验设计师的概念,我们更多倡导产品设计师。最终前端和设计,都要落回到产品上。做设计也好,做前端实现也好,有时候体验好了,产品也不一定好用,这是两回事。 + +极客时间:这个怎么理解?体验好了也不一定好用? + +玉伯:体验往简单了说很容易,一方面好看,一方面好用。但这里面有太多分支,比如新手体验好了,可能老手觉得特难用,经常会有这种情况。 + +好用,也很难定义。比如健康领域,我们觉得好用还是得跟功效相关。比如一个按摩椅,可以设计得躺着很舒服,体验很好,但是舒舒服服地按摩后,起来发现并没有缓解腰酸或腿疼。舒服的按摩椅,躺着再舒服,最终没有功效,那也是不好用的按摩椅。考虑功效后,体验会是一个特别大的话题。 + +体验跟时间有关系,很多时候当下觉得体验优秀的产品,有可能在未来某一天会突然觉察到体验极其糟糕,比如抖音。体验跟人群属性有关系,不同人群对体验理解是不一样的,比如 QQ,工作族和学生族对它的感受也不一样。体验跟面临问题的程度有关系,就像医生看病,如果是容易治的病,医生态度、医院服务就是更容易感知的体验,这个时候去私人诊所可能会觉得体验更好,很多人围着你转,给你一种 VIP 的体验。但如果是难治的病,大医院的体验会更好,因为大医院有更高概率治好病。考虑有用,考虑功效后,很多产品要达到好体验,还有好长的路要走。 + +在今天,体验不够好的地方,是体验本身的创新停滞。人机交互设计的创新,这些年有种停滞感。结合硬件比如 AR、VR 的发展,整体交互体验的创新也缺乏新意。从 PC 到手机的创新,特别是触摸屏的体验,是苹果带来的一个很大创新点,让人机交互更自然了。但是从手机之后,都是些微创新,甚至目前很多 App 的体验有倒退,整体趋同。这种同化的背后,是被已培养的用户习惯所裹挟。 + +说下体验同化。打开美团和支付宝,你会发现长得差不多;视频号也越来越像抖音。哪怕张小龙很坚持创新,也没办法做出多大的不同,因为视频号最终要服务的用户和抖音用户有巨大交集。如果服务的用户是同一个群体,最后的体验设计就会趋同。目前国内体验的设计其实极大地被同质化,在海外还是有好多在体验上会做创新的产品。打开一些海外的 App,他们的设计还是挺有创意的。但在国内基本上做个什么新产品,长得都差不多。 + +极客时间:这个现象也要两面看,经过时间的沉淀,大家默认这是体验好的。然后如果一个新产品它不是这样的体验,大家可能反而觉得体验不好。 + +玉伯:趋同的背后其实是为了体验好,能让用户没有迁移成本。比如我们习惯了微信,当你借鉴微信的设计思路去做新产品时,大概率就会直接借鉴微信的设计,好让用户在打开新产品的时候有熟悉感。 + +越是大众的产品,在体验层面的创新越难。因为所有的数据都会指向一个结果——保持老样子是最好的。就像《三体》说的,文明进化会被锁死,其实产品体验也会被锁死。 + +真的创新是要改变用户习惯的,在改变后,还能让用户喜欢,如果能做到这样才是真正的创新。很期待有产品敢于去尝试这种创新。 + +小结时刻 + +2013年,阿里宣布All In 无线,此时玉伯加入支付宝前端技术部仅一年,就面临支付宝前端团队解体,大家都需要面临选择,大部分人转岗去做移动开发,一部分人去做创新产品,而对于支付宝 PC 业务,公司希望有人留下带队,当时玉伯作为 P8,他选择留了下来。那时他对前端价值产生了怀疑,现在回看那时候很多前端人在网上的发言,仍能看出大家的迷茫。 + +2014年开始,集团提倡“大中台、小前台”,大量中后台业务开始增加,中后台的产品体验也越来越受重视,玉伯开始组建蚂蚁体验技术部,聚焦蚂蚁中后台业务,继续回归“老本行”,在体验方面做深做强。 + +玉伯曾经在《我们是如何从前端技术进化到体验科技的?》的分享中讲到“前端技术再牛,都很难直接解决产品层的用户体验。对中后台产品来说,设计的价值也远远不止于让产品的颜值提升,设计的更多价值,在于深入到产品的业务逻辑里去,去帮助业务梳理产品信息架构与任务流程。用户体验是一个非常综合的事,需要各种专业人士在同一个产品上聚焦发力,一起共同努力才能真正提升产品体验。设计师在这个过程中很痛苦,很多中后台产品都是非常垂直领域的业务产品,中间件、ECS、ODPS 等一堆专业术语让设计师们痛苦不堪,幸运的是,我们扛了过来。”正是因为扛了过来,才有了Ant Design、AntV等产品。要将后端服务传递到终端用户,中间隔着前端、设计和产品,体验技术部就是连接后端服务和终端用户的桥梁。 + +最后,也请你一起交流对体验的理解,你觉得今天的产品有哪些亮眼的体验设计呢?欢迎在评论区发表看法,我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/03终端技术:浅谈小程序与客户端的发展.md b/专栏/超级访谈:对话玉伯/03终端技术:浅谈小程序与客户端的发展.md new file mode 100644 index 0000000..0db59d6 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/03终端技术:浅谈小程序与客户端的发展.md @@ -0,0 +1,103 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 终端技术:浅谈小程序与客户端的发展 + 因为玉伯在前端领域的名气,我们原本计划请教很多关于前端的问题,但玉伯表示他其实可以多聊聊小程序和客户端的内容,目前这也是他比较关注的话题。除了蚂蚁的前端团队,他目前也在带领小程序团队,因此这一节,来看看玉伯对小程序及客户端发展的一些看法吧。 + + + +极客时间:很多媒体和工程师对一些技术名词的解读都不一样,包括不同企业的讲法也不太一样,比如终端、客户端、桌面端、跨端、大前端它们的概念,你是怎么理解的?蚂蚁的标准是什么? + +玉伯:确实业界有各种解释,蚂蚁这里,客户端比较清楚,客户端就是 iOS 加 Android,也可以叫移动端。有时候也会把 iPad 端也当成客户端,不过 iPad 端现在也不怎么提了,兼容一下就好了。这里有另外一个词叫做桌面端,桌面端目前更多指的是 PC 桌面端。 + +终端就等于客户端 + 前端 + 小程序 + IoT 等等技术,有时间我们也会用大终端来称呼。终端其实英文名字有两个翻译,一个是比较挫的翻译 Terminal,比较复古。还有一个更好的翻译是 Device(设备),也就是设备端。PC 端就是 PC 设备端,手机端就是手机设备端,IoT 端就是 IoT 设备端。从硬件角度看,刚才说的 PC 端、手机端、IoT 端、iPad 端等,是各种设备端。 + +从技术角度来看,我刚才说的终端技术主要等于客户端技术 + 前端技术 + 小程序技术,也包括 IoT 端的技术,以及 VR、AR 等技术。终端很多时候就简称端,在目前我们常说的语境里面,端就是终端。 + +但遇到“端到端”这个概念大家一定要谨慎,谈到端到端的时候,一定要搞清楚是哪个端到哪个端。PC 端到手机端是一种端到端,但我们有时候说用户界面端到数据库这一端,或者接口端,也叫端到端。它的含义太泛了。因此当你听到“端到端”的时候,可能什么都没听到。 + +大前端这个概念其实很好玩,经常会把前端团队和客户端团队放一起,然后叫大前端。还有一个词叫做泛前端,很多中小企业都是前端跟客户端在一个团队,这个团队就叫大前端团队或者泛前端团队。目前泛前端也不怎么提了,我个人也不太喜欢大前端这个词。蚂蚁这边,我们并不叫大前端,而是统一叫做终端,是前端加客户端一起融合成为了终端技术,是大终端的概念。 + +前端还常提的一个词是前端工程化。工程化的本质就一句话:让一群人做好一堆事。前端工程化就是前端基础设施层,会提供一些工具和平台,然后让前端同学基于这些平台能更好地协同工作,这就是工程化。 + +极客时间:现在你更多会关注小程序和客户端,你觉得客户端、小程序技术重要性在哪里? + +玉伯:回到蚂蚁侧,虽然前端本身的创新也还在继续,但真的要往前突破,做下一代技术的创新,这跟客户端、小程序、浏览器内核等技术是息息相关的。 + +无论在 PC 时代还是移动时代,前端的代码都要有一个运行容器,在 PC 时代这个容器就是浏览器,那个时候的前端代码运行容器都是由国外公司提供的,比如 Chrome、IE、Firefox 全是国外的公司在掌握。然后发展到小程序这个阶段,变成我们逐步有机会提升更多可控性。 + +小程序和客户端技术对于整个终端来说最大的价值点是它能拓展技术的大量可能性。国产化和自主可控是中国软件发展的一个重要方向。如果我们能够聚焦投入一些资源,基于开源项目,是有机会去做出一些关键基础技术的。 + +极客时间:从产品和技术两个方面来说,你是怎么理解小程序的?从大家的视角看,它首先是一个产品。 + +玉伯:目前业界谈的小程序,一般是指微信小程序和支付宝小程序。回到小程序的语境,小程序首先是块业务。比如说支付宝小程序,是基于支付宝小程序技术的生态开放业务。从技术视角看支付宝,支付宝的技术本质是一个 SaaS 服务平台,通过小程序这个载体,提供各种服务。 + +支付宝上的数字生活服务非常丰富:水电燃气缴费、市民中心、线下支付、公共出行等等,有大量是三方的 ISV(独立软件服务商)提供的。这些服务在支付宝上的载体都是小程序,所以小程序首先是一块业务。 + +也因为有小程序,支付宝才真正能做到生态开放。别人开发的小程序,能放在支付宝上跑,这就是支付宝的开放性。从技术视角看,小程序是生态开放的一个最好的技术和业务载体。目前中国是走在世界前沿的。这是小程序作为业务的理解。 + +回到技术侧去理解小程序技术,要回到整个软件的发展史去看。上个世纪 90 年代很多软件是 C/S 架构,现在用的很多软件比如 Photoshop、Sketch、Office 这些都是 C/S 的。然后到了互联网时代,互联网的前十年开始用大量 B/S 架构,典型的比如淘宝, PC 互联网的时候并不需要装载一个淘宝的软件,你只要打开浏览器访问“taobao.com”就好,浏览器加服务端就能搞定。到移动互联网时代,B/S 架构又往前进化到移动 App,这时候淘宝购物大家也不去网站了,而是用手机淘宝。 + +从 App 再往前演进是什么?在中国就是小程序。目前航母级的应用,无论是微信还是支付宝,其实都希望自己的 App 里还能够跑 App,App 里面跑的 App 就叫小程序。 + +极客时间:国外在小程序这块的尝试你有关注么? + +玉伯:小程序在国外其实目前没有怎么起来,因为它缺少航母级的应用,Facebook 其实有机会,但是目前看起来也没有搞成。国外更多是走向 PWA(Progressive Web Apps,渐进式 WebApp),PWA比较适合海外的应用形态,没有我们做得这么极致,一定程度上微信和支付宝是走在世界前列的。 + +但是小程序的下一代是什么?我自己的答案,真正的下一代可能是微应用。其实一个公司要开发一个小程序,再怎么节省成本,如果想要好用,还是得花费几十万甚至上百万的。是否能够把小程序的研发成本降低到你只要写一篇文档就可以了?这是有机会的,叫“Document as an App,文档即应用”,我们也在探索。像低代码的本质,也是降低软件研发成本。这些演进的核心都是为了降低成本,同时降低门槛,能让技术普惠。 + +极客时间:所以现在做小程序开发还是很有前途的。 + +玉伯:很有前途,也是趋势使然。我前面说了前端价值是降本增效,小程序技术本身的核心也是降本增效,它不光能够降低整个业务移动化或者业务应用化的成本,同时在数字化转型升级的浪潮里,也非常需要小程序。开发一个小程序和开发一个网站应用相比,小程序的成本能更低同时更方便。 + +为什么要有小程序,要搞低代码,甚至无代码,很大程度上是因为整个软件化或者数字化的大趋势非常明显,在中国有大量中小企业都需要把业务线上化,但这过程中程序员又是有限的。中国的程序员加起来不到 1000 万,真的要做数字化,只用写代码的方式去实现的话,可能至少需要 5000 万甚至更多程序员。这几千万的差距,意味着需要通过一些方法一方面让大家快速上手,一方面继续降本增效。小程序的核心优势是便捷、快捷,我感觉小程序的数量只会越来越多。 + +极客时间:现在大家再去做 App,是不是已经不是个很好的选择了,因为用户不太喜欢去装更多的 App。 + +玉伯:这也是最近几年比较明显的趋势,一方面 App 数量会逐步收敛,但也要看行业,不少行业还是有做 App 的需求。比如说在金融行业,在蚂蚁有个产品还卖得挺好的,叫 mPaaS。mPaaS 就是一个移动开发平台。基于 mPaaS 可以快速开发一个独立的 App。我最开始知道这个东西居然卖得很好,我也挺惊讶的。 + +但后来我发现确实还有好多金融机构、银行有很强的做自己独立 App 的需求,这涉及用户心智问题,App 可以给用户一种正规平台的感觉,给人安全感,比如转账操作在小程序很多人觉得不放心,毕竟大家对 App 的信任感也培养了好多年。除了金融行业之外,还有一个行业也对 App 有强需求,那就是偏工具性质的服务,如果做真正的工具,小程序的体验很难像一个独立的 App 做得那样好。 + +刚刚说小程序可以降本增效,除此之外它还适合做用户增长,因为微信是一个海量的用户池,所以现在流行很多私域运营,微信的公众号、直播、视频号、朋友圈都是它的私域工具,都去微信薅用户,把用户圈过来之后,他们还是想往自己的平台上引流,把小程序作为增长工具。独立 App 依旧会长期存在,至少在未来 5-10 年依旧会有不少需求量。 + +极客时间:当时咱们聊到阿里”All in 无线”的时候,有人选择转 iOS,有人选择去做创新产品,也有人留下。现在你可以说谁的选择更好吗? + +玉伯:很想说都一样,这样显得比较正确。认真来看,我觉得比较好的选择,还是坚持做前端的和去做产品的。当时转 iOS 的,从整体发展来看,后续不少同学遇到了一些瓶颈。接触过不少客户端同学,会发现客户端在当下对于职业的迷茫和困惑会比前端大多了。 + +前几年蚂蚁这边有一批客户端转回前端,包括淘宝那边也是一样。这还是需求问题,目前前端需求量远大于客户端,而这个局面也是两个技术栈不同带来的。前端技术栈和客户端技术栈相比,最大的特点就是快和灵活。客户端写一个东西是很慢的,客户端的变更也很慢,但前端分分钟能给你做个页面出来。打个比方,目前支付宝这边客户端发版应该算快的,但再快也是周级别的,前端可以做到小时级别。 + +互联网对速度的要求,以及对可控性灵活性的要求,使得前端技术栈在人机交互层比客户端更受欢迎,到现在为止这个趋势都还在演进。客户端更多下沉做容器、网络等基础技术。 + +前端的快是研发效能快,但是客户端也有核心竞争力,就是性能快。目前会在对性能真正有要求的产品中才会采用客户端技术,比如支付宝首页,以及扫一扫、收付款、出行、卡包等核心模块,我们叫四大金刚。这四大金刚里,有很多动态化需求,也是用大前端技术栈去做的。 + +快速灵活,这是一个很大的技术趋势。前端的灵活,印证了在前端圈很有名的一句话,“凡是能用 JavaScript 编写的应用,迟早都会用 JavaScript 编写”,从我入行以来到现在,这句话还在发挥作用。 + +极客时间:现在客户端工程师遇到的困境也比较明显,你觉得现在这一批人应该往哪个方向去发展呢? + +玉伯:我目前看到的现状是,客户端有三拨人在往不同的方向发展。有一拨转向了前端,都是写代码的,其实让自己多一门技能,学前端去做业务,也是一个不错的选择,就跟阿里当时“无线 All in”的时候,很多前端转 iOS 一样。 + +还有一拨人目前在做一些对性能要求很高的业务。比如说支付宝扫一扫,扫一扫如果用前端去做,体验永远不会有客户端做这么好的。所以这时候客户端好好地去做扫一扫的业务价值是很大的,挑战也很高。对性能要求很高的业务是客户端的第二条路。 + +第三个方向,其实是往整个终端的基础技术走,比如做小程序的容器、下面的网络,做端智能、容器等方向,其实这块有很大发展空间,需要高精尖的人才不断去做深做厚。 + +真正从技术深度,从纯粹狭义的技术角度来讲,客户端技术深度其实比前端要大。目前我们也在鼓励前端工程师,如果他还处于一个爱学习的阶段,那就应该去学客户端技术,这样他有更宏观的视野。 + +极客时间:曾经你也说过前端的天花板低,现在你对所谓的天花板问题怎么看?无论是新人选工作方向,还是换工作方向,都会考虑五年、十年的发展前景,很多从技术转到业务的应该也会这么考虑。 + +玉伯:有时候这是一种认知偏差,因为人是在变的啊。大家很多时候讨论天花板的话题,其实在讨论人的发展,不是讨论某个职业的发展。因为职业的发展要结合人来看,很多时候你是在假设一个人一辈子从事某一个职业,这肯定是非常少见的。 + +之前大家一直说前端的天花板比后端低,谈到三年、五年甚至到十年的某个职业的发展速度,或者说哪个职业天花板更高,背后只有一个因素,是需求。供大于求就发展慢,供不应求就发展快。如果什么时候企业说不缺前端了,那就可以不做前端了。但是实际经验告诉我,从 2008 年,一直到目前为止,都是缺前端的。还没有哪个团队说前端会富裕。甚至之前我通过一些数据,能够判断公司哪块业务做得好。当某个业务嚷着叫着缺前端的时候,这块业务是很不错的。但当某个业务我给他推简历,他不要的时候,那这个业务可能增长遇到了点问题,或者它才刚起步。 + +小结时刻 + +小程序和客户端技术对于整个终端来说最大的价值点是拓展技术的可能性,小程序降本增效的价值、开放平台价值,以及与航母级应用的连接关系,使得投身做小程序的人越来越多,这也是需求使然。客户端技术下沉,对客户端工程师提出更高的要求,但目前 App 还是强需求。就像玉伯建议前端同学也要学习客户端知识一样,时刻把握技术风向,具备学习能力的人,才是能走得更远的人。 + +另外,关于国内外小程序发展的话题,在极客时间曾有一篇文章也做过精彩的论述,在此推荐给你: Web开发:浏览器、小程序与PWA ,可以与本文结合,加深你的理解。 + +最后,你所感受到的客户端趋势、前端趋势是什么样的呢?欢迎在评论区发表想法。我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/04开源三大收获:异步协同、文档优先与快乐工作.md b/专栏/超级访谈:对话玉伯/04开源三大收获:异步协同、文档优先与快乐工作.md new file mode 100644 index 0000000..7c1df66 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/04开源三大收获:异步协同、文档优先与快乐工作.md @@ -0,0 +1,81 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 开源三大收获:异步协同、文档优先与快乐工作 + 希望玉伯能聊聊开源,因为他和团队折腾过 KISSY、SeaJS、Ant Design、AntV 等开源项目,KISSY、SeaJS 已经成为过去式,Ant Design、AntV 还在进程中。但说起对于开源的理解,对他触动最大的开源项目其实是 Yahoo 的 YUI3,玉伯也是在参与 YUI3 的过程中,体会开源带给他的快乐和反思。 + + + +极客时间:你最早参与开源是什么时候,是以什么方式?你对开源最开始的理解是什么样的? + +玉伯:这就早了,我谈谈在阿里的时候吧。2008 年入职淘宝时就比较关注开源,当时前端有个标杆公司叫雅虎,雅虎是前端工程师的摇篮,有一批牛人。当年雅虎有一个产品叫 YUI,是前端最早的著名开源项目之一。 + +淘宝最早用的组件库就是 YUI,当时我们基于 YUI2 在内部封装了一个版本叫 TBra,T 代表淘宝,淘宝前端的内部开源起步于 TBra 项目,代码写得很不错,我经常去看源码设计。 + +最开始我对开源的理解,就是把源代码放出来,然后大家能够一起去看源代码长什么样子,是怎么实现的,还可以参与贡献,这是我对开源最初的理解。 + +极客时间:在参与开源社区的过程中,从最开始开放自己的源代码,往后发展,越来越深入地去参与项目甚至主导项目,然后反馈社区,它是一个更进阶的过程。深入参与到社区里,你的状态是怎样的? + +玉伯:更进一步参与开源,是雅虎的 YUI3 项目,这是对我触动最大的开源项目。对我的触动点在于,当时 YUI3 的代码还没写多少,但是整个项目组就已经通过整个社区运作,共同探讨 YUI3 应该如何设计,比如应该分哪些组件,组件 API 应该怎样设计等等。设计先行,文档先行,而不是先写代码,这种开源协作,对我影响非常大。 + +当时有一波国外前端大牛也在里面,我就看他们用英语在探讨,我当时的英语阅读和写作能力都突飞猛进。基本上天天都会去看,一有时间就会看,同时会用英语参与回复讨论。 + +从参与文档讨论,后来逐步开始尝试去提交一些代码,YUI3 里面有些早期代码是我写的。这个过程很好玩,有点像打擂台。就是你提交了一个版本,对方一看这是什么啊,觉得你写得不好,然后别人会提交一版实现,把你的代码给覆盖掉了。这个时候,人的斗志就来啦,内心不服,就会想到更好的方案去实现,然后来个回马枪,把别人提交的代码再修改成自己的方案。 + +我现在印象很深刻的一个组件,是 AutoComplete 组件,实现起来挺复杂的,当时对里面的性能优化点、渲染性能做了很多研究。我一直对性能优化感兴趣,跟这段经历也很有关系。这是我参与开源的第二个阶段,从看源代码,到参与讨论,再到开始打擂台的一样贡献代码,年轻气盛少年狂。这个过程,现在回想起来都挺开心的。 + +那时候我天天盼着下班,我白天疯狂地在项目室闭关,就想尽快把业务代码的活干完,然后回到家里,快速吃个饭,就又开始疯狂地参与开源。印象中我每天晚上都干到凌晨两三点,很开心,很兴奋,好像不觉得累,睡眠质量也挺好的。 + +对我而言,开源就是开心的源泉,现在回想起来都很开心。 + +极客时间:你这种状态,当时坚持多久? + +玉伯:好久的。但后来发现熬夜对身体有影响,有时上午会没什么精神,而且因为凌晨几点了还在疯狂写代码,到了早上再继续写代码会有点疲。于是上午一般会选择去开会讨论或写文档,写文档时,脑海里已经在构思代码怎么写,怎么实现。吃完午饭精神就会好很多,中午打个盹后,下午会开始疯狂写业务代码。在我离开淘宝 UED 前,基本都是这种状态。 + +极客时间:有没有一种可能是这样?那几年时间其实你写代码写太多了,已经在写代码这件事情上找不到更多的乐趣了,所以你更多地会去关注产品、业务管理?有这种可能性吗? + +玉伯:肯定有这种可能性,但代码本身依旧很有乐趣,但 YUI3 项目的缓慢进展,让当时的自己的确有些受挫。当年 YUI3 项目搞着搞着,我觉得项目有点看不到希望了,后来越发展越不太好,内心很受挫。YUI3 项目的发展不顺利,的确让我开始思考写代码是为了什么,为什么 YUI3 的代码明明这么优秀,但社区却好像越来越不喜欢 YUI3。这块的思考,让我将精力开始转向产品思考。YUI3 是一个技术产品,作为技术产品,真正决定产品成败的,并不是代码。 + +写淘宝业务的前端代码时,更有体感,最终我发现淘宝的核心价值大都来自产品和运营,代码一定程度上只是实现工具。从代码实现,开始转向代码背后的技术产品设计,这是我参与开源的第三个阶段。 + +小结起来,我参与开源有三个阶段。第一个阶段是看代码为主,第二个阶段开始参与文档讨论和核心代码实现,第三个阶段则是从产品视角去看开源项目。决定一个开源产品成败的,往往是这个产品本身的设计理念和思路是否符合潮流,如果方向不对,代码就很容易白写。 + +代码是实现产品的工具,就像语言是沟通的工具一样。我们说话,本质上是沟通,语言只是沟通的一个工具。曾经有一段时间我很纠结自己有老家的口音,后来我一点都不纠结了,为什么?我发现其实在沟通这块,大家还是能够听懂稍微带点湖南口音的语言。我不会现阶段还纠结说我的语言要字正腔圆,反正大家能听懂。这是一种心态的变化。不是否定工具,而是于我而言,更看重用工具所做的事情本身。 + +极客时间:从你参与 YUI3 项目对你影响这么大,加上你和团队一起折腾的 KISSY、SeaJS、Ant Design、AntV 等开源项目,你觉得参与开源对你产生了哪些影响?或者说收获,可以聊聊吗? + +玉伯:我第一个深度参与的开源项目是 YUI3,最大的收获有三点。一点是深度感受到了写代码是很快乐的。同时,还有一个更大的收获是,意识到代码是工具。第二点我很少对外说。 + +很多时候人一定要知道自己拥有的所谓能力也好技能也好,究竟是有什么用?看开之后,人会坦然。比如我不会再纠结某个代码风格更好,我会更关注用技术能实现的产品价值点在哪。 + +开源收获的第三点,是真正感受到了开源社区异步协同的美好体验,超级喜欢。做语雀跟这段经历也很有关系。有时会开玩笑说,给我们发旺旺、钉钉消息,我们回复会很慢,大家都不怎么想去看 IM 消息,但是给我们提一个 issue,我们立刻就会响应。异步协作文化,让我以及整个体验技术部都深受影响。 + +除此之外还有一个收获就是理解文档的重要性,之前跟 YUI 项目那帮人聊的时候就是靠文档。文档跟微信聊天不一样,因为很多时候是跨时区的,每一次沟通都很珍贵,我们得把自己想说的话尽可能一次表达清楚。因此写文档的能力很关键,清晰表达才能做到几个回合之间达成一致,否则就是漫无目的地闲聊。还有比如你要去做某个功能,甚至你写了一堆代码要提交,你都得先写一堆注释,去告诉他我为什么要改你的代码,我改完之后究竟好在什么地方。在这些层面上是需要通过一些文档的方式来讲清楚的。文档先行的理念对我影响很大,我们做很多事情都养成了文档先行的习惯,这和亚马逊说的“六页纸”管理方法有异曲同工之妙。 + +极客时间:在你自己后续的开源项目中,你怎么把前面学到的东西运用在项目上? + +玉伯:我尝试过,当时做 Ant Design 之前还有一个开源项目叫做 Arale(阿拉蕾),充分践行 YUI3 的那套理念,就是文档先行,我们对外说我们开源了,打开一看,一行代码都没开始写,充分践行文档先行。当时很多人在讨论,认可文档先行的占大多数。Ant Design 开源社区这块,好多人从早期就参与了,他们全程见证了整个过程。 + +国内开源项目,一直有个我觉得不太好的做法,就是一定要等到代码写得差不多了才开源。很多时候怕自己代码写得太烂,一定要整到一定阶段,写到心目中最好了,才把代码放出来开源,但往往这时候别人都没有参与的机会了,对别人来说只能用。我一直觉得这种做法并不太好。 + +我之前跟某开源负责人讨论过这个问题,我说他们开源目的不纯,他说我是古典开源主义。他们的说法我也认的。就像做产品有古典产品主义者,这批产品经理,一开始不会怎么考虑用户增长,而是关注我想不想做,我具体想做什么,然后根据自己的想法和设计把这个产品给做出来,给用户用,这叫做古典产品主义。开源也存在这种情况。古典开源主义是一种比较纯粹的状态,开源最早的理念就是分享想法,并不是分享代码。像 W3C(World Wide Web Consortium,万维网联盟) 最开始的标准诞生,就是我有一个想法,我希望能够召集一拨人来讨论,来实现。这是我接触的开源最原始的状态,也是我最喜欢的开源阶段。 + +极客时间:最原始的开源你会去考古它的这些背景和发展,现在有没有什么好的资料可以来去看? + +玉伯:我接触过的最早是 Linux 开源社区。Linus Torvalds 本人挺有趣,他是开源的奇迹,对项目管控得非常好。社区里有很多观点,到目前为止都是很前沿的,比如什么是好的开源项目?我们很多时候会觉得要有影响力,觉得好的开源项目是目前还活跃的、还不断有人迭代的项目。但当时我看到过一个至今记忆深刻的观点:“死了的项目才是好的开源项目”。这句话是什么意思呢?就是说已经不再迭代的开源项目,说明它做完了,没有任何 Bug 了。Unix 里面很多底层小工具,做到删无可删了,做无可做了,Bug 也没了,不需要更新迭代了。这种东西才是最好的开源果实。 + +这个概念延伸到产品领域,我曾经看张小龙讲微信的设计,其中讲到摇一摇这个产品,就跟 Linux 社区的理念是一脉相承的。微信摇一摇为什么可以做到不可超越,是因为已经把这个功能做到了极简化,删无可删,你只要加功能,你就输了。我觉得开源好多理念和产品也是很相像的,可以互相借鉴。 + +小结时刻 + +玉伯是开源圈里的古典开源主义,他说今天国内开源一直有一个不太好的形态——代码写得差不多了才开源。很多开源项目一定要代码写到最好了才开源,但对社区的参与者来说,别人没有参与的份了,这是不太好的。他怀念Ant Design开源的时候,有部分社区的人是全程见证它的成长的。 + +《大教堂与集市》中讲“只要有足够多的 beta 测试者,几乎所有的问题都会很快呈现,自然会有人把它解决”,这种笃定和相信,今天是否还能大面积看到?“集市”精神就是社区精神,人的价值感、人与人的连接就在大大小小的社区里,开源社区就是一个庞大的意义共同体,而每个开源项目小组也是意义共同体,能够找到意义共同体的人无疑是幸福的。 + +最后,也请你一起交流对开源的理解,以及你从参与开源过程中获得的养分吧。我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/05蚂蚁内部开源:迈出第一步,但还有很长路要走.md b/专栏/超级访谈:对话玉伯/05蚂蚁内部开源:迈出第一步,但还有很长路要走.md new file mode 100644 index 0000000..2fd7525 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/05蚂蚁内部开源:迈出第一步,但还有很长路要走.md @@ -0,0 +1,71 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 蚂蚁内部开源:迈出第一步,但还有很长路要走 + 上一节玉伯分享了参与开源的故事与收获,开源精神和开源理念无论是在管理层面、产品层面都深深影响着他。除了 Open Source,我们也想让玉伯聊一聊对 Inner Source(内源)的看法。国内外许多大厂都在进行内源实践,蚂蚁也不例外。这一节玉伯主要分享了蚂蚁内源的发展,以及前端项目内部开源的经验。 + + + +极客时间:我知道蚂蚁现在在做内部开源,企业做内源需要什么条件呢?蚂蚁现在大概做到什么程度了? + +玉伯:我可以先讲一下为什么会有内部开源。印象中 2019 年时,蚂蚁正式提出要做 Inner Source,Inner Source 的实践源自 Google 等硅谷公司。有时不少内部项目,因为各种原因,并不适合直接做 Open Source,然而公司已经足够大,比如说公司上万人甚至十几万人的时候,公司里其他团队可能也会依赖你这个项目,给你提需求,这个时候怎么办呢?从公司角度就希望你把代码开放出来,别人基于你的代码再去实现某个需求,这是内部开源的需求源头。 + +再回到蚂蚁做内源的背景,当时集团已经开始“去中台”了(建中台的目标之一是去重复建设,当中台开始阻碍业务增速时,拆中台、去中台的声音开始出现),要求技术中台回归业务线。但是“去中台”也会带来一个隐患:回归业务线之后,如何保证不会又冒出一堆烟囱?这也是 Inner Source 应运而生的一个原因。 + +如果我们在内部搞开源,哪怕技术回归到各个业务线,但是代码还是开放的,同时如果你的项目在这个领域是做得最好的,那其他团队就可以直接基于你的代码去做自定义,或者是大家在一起维护这个版本。所以在“去中台”大背景之下,Inner Source 是可以防止再次重复建设,或者至少可以减少很多重复建设。 + +但时至今日,在软件界依旧会有一个观念叫做“Not Invented Here”,不是在我这里发明的我就不认,因为不是我亲手创造的,用起来我就觉得不放心或者不顺手,所以总想在自己的业务领域去发明轮子。这就是轮子满天飞的原因,不光是前端,后端轮子更多。 + +前端老被吐槽轮子多,所以我们前端开源一开始就是以 Open Source 这种方式去做,但是后来因为公司变大了,各种内部的规章制度一管,加上中美关系的变化,就发现某些技术是要受保护的,所以导致很多技术其实并不适合做开源。所以回到前端做内源的真正原因,一开始也是一种无奈之举。 + +当时刚好在蚂蚁做内源的人联系到我们,他们老觉得前端做开源是鼻祖,就会问我们的意见,我觉得内源确实挺适合我们的。很快我发现参与内源有个好处,Inner Source 真正用途是解决人性的问题,一定程度上能解决“Not Invented Here”的问题。 + +比如说 AntV 数据可视化这个项目,最开始是我们团队去做的,很长时间也都是我们团队维护,所以很多人开始的观念就认为 AntV 是体验技术部的。Inner Source 来了以后,我们干了一件事情,让所有内源项目去团队化,让 AntV 变成是蚂蚁的,而不是体验技术部的。怎么做呢?首先把 AntV 贡献出来,变成蚂蚁的一个内源项目,类比一下就跟捐给 Apache 一样,捐给 Apache 之后项目就不属于自己公司了。内源就意味着 AntV 不再属于某个团队的,而是属于蚂蚁的,可以实现轮流维护。比如今年体验技术部维护,明年数金前端维护,后年蚂蚁国际的人来维护。通过这个做法希望解决“Not Invented Here”的问题。所以我们挺认可 Inner Source 的价值。 + +但现在也看到一些问题,比如很多人不愿意进内源,或者进了内源之后还是强调项目是自己的,是有归属的,其他人是来做贡献的。这也可以理解,特别是在后端领域。我跟后端工程师们交流过,他们很难解决这个问题,一个原因是如果没有归属,就可能没人维护了,还有一个原因是别的团队没有这方面的专家,接不住这个项目。 + +在前端这块,我们的做法是想一些办法让其他团队接得住,比如部门间人才的流通,把人才放到其他部门里,打破边界墙,这样在不同的团队都有人能参与及影响身边的人。AntV 和 Ant Design 目前算是部分实现了内源,是比较正面的案例,但像 Chair(基于 Node.js 的 Web 框架)很难内源出去,因为很少有团队能够接住,对方缺少这块专家。像这种情况,我们的做法是把一些模块、组件让其他团队承担,部分实现内源的目的。 + +极客时间:整体来说蚂蚁内部开源推进得还算顺利吗? + +玉伯:谈不上顺利还是不顺利,蚂蚁内源还在路上。 + +这是我自己的判断,我理解所谓顺不顺利,是看做内源这件事解决了什么问题,从一级到十级,我心里至少得到六级才行。目前蚂蚁 Inner Source 只达到了源码开放,内部社区还在构建过程中,Not Invented Here 涉及的人性问题更难解,这涉及技术人才的发展体系设计。 + +极客时间:那未来要去做蚂蚁内源社区的规划,你认为要怎么去推进? + +玉伯:内源的蛮难的。回到开源的三个阶段,第一阶段是源码开放,第二个阶段是社区讨论,第三阶段是产品视角。内源很容易卡在第二步,因为第二步有很核心的点,是要有足够的人参与进来。对蚂蚁来说,能够达到第一步已经是个进步,至少代码开放了。 + +极客时间:很多大公司都在推进内源建设,但把内源做成还是任重道远。关于开源,最后还想聊一聊商业化的问题,这两年对开源软件企业的投资也非常火热,你对开源商业化有什么理解? + +玉伯:开源商业化的话题我一直比较关注,但这个话题,我没什么见解。这是因为,前端领域的开源,目前更多在做社区。某种程度上讲,前端开源项目的商业价值很难很高,业界的很多前端开源项目,有商业化尝试的并不多,商业化非常成功的凤毛麟角。 + +对蚂蚁前端的开源项目,我们受益于开源社区,我们的策略就是回馈社区,尽量不做商业化。 + +像 Ant Design,我们的核心代码都是开源的,大家用就好。时不时也有中大企业会联系我们,表示想购买 Ant Design 的商业服务,想付钱买服务。但后来我们自己算了笔账,他们要的真正服务是基于特定业务场景的 Ant Design 的深度定制,去做适合特定业务域的一套前端 UI 的解决方案,这是一个专家咨询加资产定制服务。这个服务极耗人力,然而在国内,大多企业给不到我们的成本价,如果去做,做一个亏一个。真要做,我们得给 Ant Design 组建一个交付团队,然后卖人力、卖咨询、卖服务,然而这是一个没有规模效应的生意,有能力付费的企业并不多,最终会很亏钱,这条路很难走通。 + +极客时间:所以如果要做开源商业化的话,基础软件还是更有竞争力。 + +玉伯:最近几年开源商业化确实很火,很多 VC(风险投资机构) 知道我做开源也总是找我,我其实都想清楚了前端还是做免费。但跟他们聊的时候确实也了解到,目前在后端的数据库领域,包括数据界的 DevOps 领域,开源商业化是挺热的话题。 + +很多做开源商业化的公司模式就是:开源就是商业本身。我开始也很好奇,这句话怎么理解呢?其实他们是分版本的,有一个开源版本来做影响力,另外的专业版本收费。 + +开源本身可以增加用户买单的信任度,因为你有开源版本,又有专业版,别人可以看到开源版本的代码,它不是黑盒,用户会更有信任感。 + +另外,通过开源可以抢占话语权,通过撬动一些生态的力量,然后一起去定义标准。通过开源可以博得业界声量,让业界开始认可而逐步成为事实标准,先开源就会抢占先机。 + +有了信任感、影响力,以及获得制定标准的话语权之后,往往就能够圈一波自然粉,会有很多用户跟随,其实这也是私域运营,到最后一步才会做付费。 + +这个模式能不能成功,这一波做开源商业化的能不能起来?还要再观察,因为 VC 的钱也刚进来,最终要看买单的客户有多少,是否能够支撑开源商业化的公司可以持续往前走。 + +小结时刻 + +蚂蚁做内源的原因之一是在“去中台”大背景之下,InnerSource 可以减少很多重复建设,尽量减少轮子满天飞的问题,同时前端团队参与内源开始也是因为一些项目不适合做 Open Source。蚂蚁内源现在做到源码开放也是迈出重要一步,只是离玉伯心目中的理想还有很长的距离。 + +你对内源有什么理解,是否从内源项目的参与过程中得到收获呢?欢迎在评论区发表想法。我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/06从淘宝到支付宝:几次项目失利,但创新产品之心未死.md b/专栏/超级访谈:对话玉伯/06从淘宝到支付宝:几次项目失利,但创新产品之心未死.md new file mode 100644 index 0000000..79d7bf0 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/06从淘宝到支付宝:几次项目失利,但创新产品之心未死.md @@ -0,0 +1,126 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 从淘宝到支付宝:几次项目失利,但创新产品之心未死 + 玉伯在淘宝曾经参与内部赛马,搞了几个月产品后惨败,失败后回淘宝技术部到 Java 团队做前端工程和性能优化。除了继续搞技术,玉伯其实还在“偷偷”搞产品,我们请玉伯讲了讲他和当年那些失败的创新产品的故事。- + + +极客时间:参与内部赛马是一个什么样的故事?你参加了什么样的创业项目? + +玉伯:2010 年左右,淘宝为了鼓励内部创新,发起了一个创新孵化项目,叫做淘宝赛马。你想做某个事情,就可以去立项并召集感兴趣的同学参与,组队成功后一起去做。 + +我当时加入了一个赛马项目。当年阿里依旧有一个社交梦,我们那个赛马项目就是在往社交方向做一些探索。干了几个月后,发现有点干不下去了。这段经历对我来说,一方面负责前端,但更多是参与产品讨论,整个过程挺折腾。 + +最后项目失败了。早期产品讨论阶段,大家就已经出现分歧。很多人都在表达想法,第一版 PRD 就花了一个多月,等到做开发时,人心已经有点不齐。代码最终需要我们来写,然而作为程序员的我们自己内心并不认,就不太想写,很犟。觉得我们是来创业的,一定不能这么妥协。最终我选择了主动离开。 + +参与赛马那时,真的是年轻气盛,就是不妥协,就拜拜了。那个产品真的是没有想清楚,社交领域非常大,熟人社交,还是陌生人社交,还是某个特定领域的社交,和竞品的差异化竞争优势是什么,第一批种子用户在哪,如何运营等等,都没怎么讨论清楚。选择了果断退出,现在想起来是一种勇气和幸运。 + +有一个细节印象深刻。赛马项目启动时,我们每个人写了一个心愿,塞到一个信封里封起来,说等成功时再拆开来看。现在回想起来,再也没机会拆开了。 + +这是我参与赛马的一段经历。 + +极客时间:那个时候这种赛马的项目成功率高么?参与这个赛马项目你最大的收获是什么? + +玉伯:赛马并行启动的项目,我记得有六七个,最终算成功有两三个,准确数据我不太清楚。 + +对我来讲,这段经历最大的收获是,发现做产品原来这么难。之前我做前端开发,是在支撑产品经理做产品,经常会觉得产品经理提的需求不太靠谱。在赛马之后,自己对产品经理的心态有了很大的变化,觉得产品经理真心不容易。 + +赛马失败后,我回归了淘宝技术部的 Java 团队,一边继续做性能优化等技术方向,一边更心痒痒想去做产品。赛马的失败,让我意识到产品经理的不容易,内心里反而更想去做产品,产品心在不断生根发芽。 + +极客时间:从 UED 到赛马,那就相当于你从 UED 离开了,到一个小创业团队里面去,成功或失败也并不知道,是一个冒险的选择。你除了在 Java 团队搞性能优化的事情,还在做产品,具体是什么类型的产品? + +玉伯:回到 Java 团队后,一方面做技术,同时有在尝试做一些业务型创新产品。 + +其中有一个创新产品,是想做“中国版的 Pinterest”,Pinterest 在国外当年很火。我在淘宝 UED 的经历,让我对设计包括基于图片的兴趣社交很感兴趣。其实从淘宝 UED 出来之后,我可能一直就没有怎么安分过,看起来在做技术,实际上在搞各种产品。 + +中国版 Pinterest 这个项目不算失败,项目成员里有产品经理、设计师还有后端开发,我当时是半个产品经理加前端的定位。后来除了我和一个设计师之外,项目组里其他同学都选择出去创业了。当年国外有个很有名的投资人,投过 Pinterest,他来中国在看类似的项目。看的过程中发现我们在做这块,同时杭州还有个公司也在做这一块。最终这个投资人选择投我们,这是出去创业的兄弟们的信心来源之一。图片兴趣社交在中国很难做,特别是朋友圈起来后。最后这个项目有转型到一个特定领域,杭州那家公司,发展成为了花瓣网。 + +极客时间:在淘宝做了这么多项目,各种折腾过后,你觉得你沉淀和收获了什么? + +玉伯:我觉得收获的东西有三个点。第一点是,那段经历真正让我开始想清楚自己对什么感兴趣,这是蛮有意思的。比如从中科院物理所退学,是让我想清楚我原来对物理科研不感兴趣,发现编程能给到自己更多快乐。后来参与赛马包括折腾各种创新产品,我的收获是,意识到技术不是万能的,技术只是工具,技术是产品的实现手段。 + +很多时候大家谈技术深度广度,我都觉得这有什么可聊的。真正的技术深度可能在什么地方?在学术界,在各种科研实验室。而在大厂里,所谓的技术深度往往只是工程成熟度,真正的有深度的技术创新凤毛麟角。在大厂里,技术更多是为产品和为运营服务的。很多时候,一个产品成功了,背后的技术就被说得很牛,然而很牛的产品,往往并不代表用到的技术就很牛,两者有交集,但并不多。 + +还有第二个收获,在当时可能自己已经有了初步的团队意识,开始知道很多事情单枪匹马是干不成的,需要有团队才有机会去把一些事情做得更长远或者更有可能性。 + +最后一个收获,是自己心态上的收获。经过这些折腾后,每次看到希望又跌下来,再次看到希望又跌下来,像过山车一样,这让自己遇事时变得比较冷静。现在面对一些折腾或者困难时,不会有太大的情绪起伏,就觉得都没啥,反正都经历过,会有一种看淡的感觉在内心里。 + +极客时间:图片社交产品最终没有做成,其他人选择出去创业,你来到了支付宝,开始另外一段旅程了。也是在企业服务开始被更多关注的时候,在支付宝建立起体验技术部,为中后台业务做支撑。但在建立体验技术部之前其实还经历了阿里“All In 无线”,前端面临价值危机的时期,在你早期的发言里也看出那时候的迷茫。在支付宝你感知到的这种趋势变化是怎样的? + +玉伯:到支付宝也是机缘巧合,因为之前的项目都失败了,当时我的 Leader 是范禹,范禹想留我,但是也很难找到一些重要的事情给我做。当时范禹相当于是淘宝首席架构师,鲁肃是支付宝的首席架构师,他们中间有些内部交流和沟通。这过程中,我偶然接触到鲁肃,发现鲁肃挺有意思的,鲁肃又一次分享里,居然反复提到了两次前端。于是我就找鲁肃聊了聊,然后联系上了支付宝前端团队,就来支付宝了。2012 年 2 月左右到了支付宝,一直到现在。 + +在支付宝就是老老实实做技术,确实因为之前各种折腾会使得自己在产品这一块有点灰心,之前做产品来回折腾了三四次,感觉做产品好难。转岗到支付宝后,心态很简单,就是好好做技术,同时开始在思考支付宝前端怎么往前走一步等问题。 + +2012 年干得挺欢,跟很多同学一起做开源,做整体架构,当时所在大团队 40 几人,我带了一个六七人的基础架构组,天天讨论问题,天天写代码,挺开心的。 + +到 2013 年后,开始要“All In 无线”,当时自己很懵,不知道何去何从。 + +后来整个前端分成三波,第一波是响应公司号召,去转 iOS 了,因为无线 All In 了,前端写 PC 没前途了,这时候转 iOS、安卓才更有前途,而且对公司来说也会更好,所以就有一波人转客户端。 + +还有一波,我当时的主管是么么茶(吴振昊,钉钉联合创始人),他先去做来往,后来去钉钉,现在是钉钉的产品总监,他号召了一些人去投身创新业务。理论上按照我的过往,我应该是极大概率跟着么么茶去做来往。但因为赛马的经历,让我很长时间对产品有点提不起兴趣。 + +剩下十几个人包括我,就是第三波,是被剩下的。当时支付宝收银台还有很多 PC 业务,这块无论怎么调,总得有人留下来去做既有的 PC 业务,我当时觉得 PC Web 开发也挺好,就选择了留下来。 + +面对这些变化,我也动过想法,当时第一反应是想转 iOS,当时也在学 iOS。只是后来觉得团队这么一弄,PC 实在没人,总得有人留守。同时因为我那时是 P8 层级,只剩下我这一个 P8,鲁肃也找我希望我更有担当。纠结过后,最后选择了留下来。 + +当时阿里无线 All In,对于前端来说打击很大,我印象中很多人开始怀疑前端的价值。我在 GitHub 上也写了很多文章,写了很多关于前端的价值、前端的低谷、前端如何发展的文章。当时自己也是通过文章在梳理自己的思路,现在回看,是一段难得的经历。 + +极客时间:你感受到前端价值的再次显现,是因为后来的蚂蚁金融云项目吗? + +玉伯:金融云是一个金融级的 PaaS 平台,底层基于阿里云,上层满足很多银行和金融机构的上云需求。蚂蚁是专业做支付和金融的,我们在云上基础设施上的经验,有机会让银行和机构复用,这是金融云的大背景。 + +云服务就意味着当年有大量的中后台开发,因为所有的云服务它从前端的角度来看都是SaaS,最终都要有人机交互界面。这些交互界面的载体就是SaaS产品,而且在蚂蚁内部也有大量SaaS产品,有很多中后台系统。加上我们要对外做金融云,所以当年有大量企业级需求,非常缺人。 + +前端往移动的转型,我的感知是到2016年达到峰值,从2013年到2016年,大家没想到时间竟然这么短,三四年就到峰值了,之前我的预测至少要5-10年。2016起,云服务开始逐步起来,中后台业务需求快速增长,需求的增长已经大于我们团队能承载的量,不光是缺前端,后端、产品、设计等人才都缺。 + +那时候我向鲁肃汇报,鲁肃给我的要求很简单,就是蚂蚁的前端出问题就找我。大家经常会说阿里 KPI 怎么样,我一直没什么体感,我很多年 KPI 就是鲁肃这么一句话,更多是自己给自己定方向。鲁肃提的要求挺好:如何做好蚂蚁的前端基础设施,蚂蚁前端出问题时,团队如何第一时间能解决。这些是一些基本要求,这些基本要求,就已经可以做很长时间。 + +我当时本职工作是解决前端的问题,同时 2014 年开始组建 UED 团队,服务于金融云等企业级业务,前端团队和 UED 整合在一起,通过技术和设计,共同服务于用户体验,这也是大团队命名为“体验技术部”的初心。 + +极客时间:以前的中后台业务,你觉得它的体验做得不好,具体是体现在哪些方面? + +玉伯:技术平台的产品经理,往往是技术同学在兼任,技术人做产品,往往会有很多坑。比如,抽象能力对产品经理来说是很关键的一个能力,然而,一个技术人去做产品,有时候会过于抽象,技术同学以为自己能理解用户就能理解,其实不是的,用户可能搞不清楚你为什么这么抽象,这是第一个坑,很容易踩到。 + +第二个常见的坑是,技术人的潜意识会有“技术万能化”,感觉没什么搞不定的,整个思维模式并不是用户视角,而是用很技术范的视角在看问题。这个坑,使得早期很多技术平台产品,做着做着就变成了功能堆积,在用户动线、产品体验地图、用户故事很多地方都说不清楚。技术人很容易缺失基于用户视角的产品设计思维,以为按照技术的方式把功能堆上去就好了,这使得很多 PaaS 产品的体验都比较糟糕。 + +极客时间:那时候是没有专门的产品经理角色么? + +玉伯:我印象中有那么一两个,但也是技术转的,并不是专业的产品经理。开始招产品,是从做金融云业务开始。到现在已经好太多,现在产品经理已经有百来人。之前中后台业务也没有设计师,都是前端在做,我后来从零到一组建了一支 UED 团队,有个设计师参与后,中后台的整个体验有了量级的体验。 + +极客时间:从这个节点搭团队,你是从零开始招的这些人,当时会有什么样的岗位模型么,怎么想清楚都要招什么样的人? + +玉伯:当时没怎么去定义岗位模型,更多是用情怀以及企业级设计的价值,去找感兴趣的人。当时搭团队,有个机缘巧合,“无线 All In”三四年发现到顶了,这时候有一批人才外溢,比如当时来往搞失败了,我就去找来往的设计师。创新项目搞失败了我自己经历过,就会跟他们说,要不回支付宝吧,以感同身受的方式去吸引了一些早期设计师进来。中间也踩过很多坑,因为一开始招聘设计师,我自己的专业度不够,又迫于业务需求,急于把团队组建起来,可以说有点饥不择食,这给团队往后发展带来了一些隐患。 + +从 2015 年到 2017 年发生了很多事情,过程中挺好玩的。一旦有了 UED 后,加上前端团队,他们之间就会开始产生化学反应。后来我们能做 Ant Design,跟设计师与前端工程师的化学反应息息相关。Ant Design 的基础是设计理念,在蚂蚁能从体验技术部诞生出来,这是融合带来的特色,不同工种有不同的思维模式,有时真的要放在一起,物理上放在一起,天天在一起碰撞,才有机会做出一些有创新的有特色的事情出来。 + +设计师经常会有很多想法,但是要去影响到业务,还是很难的。来了体验技术部,这边不缺程序员,缺设计师,刚好互补。提升用户体验,跟前端的实现息息相关,跟设计师的设计也息息相关。两者一拍即合,很自然就产生了化学反应。 + +极客时间:让这两个团队融合,你有做一些努力吗? + +玉伯:做了蛮多事情。比如,哪怕业务方给我的压力很大,我当时还是给 Ant Design 做了立项,作为一个正式项目去做,确定这个项目谁是前端负责人(偏右,网名 afc163),谁是设计负责人(他山,外号叫勺子),给团队信心是蛮重要的,虽然真正全职投入的也就三四个人。 + +当时我们还花了大量精力去研究竞品,会发现竞品也没那么强,有些地方也乱糟糟的。我们去做组件库,并不是一个超前的东西,后来发现组件库的底层是设计语言,就很兴奋,觉得有搞头。虽然现在设计语言的提法已经满大街,但当年谈这个概念的并不多,Ant Design 提设计语言,在当年是有引领性的。 + +还有一点就是开源,我们是以开源的方式去做 Ant Design,对很多程序员非常有吸引力,会觉得做开源能让自己很有成长感和成就感。 + +做 Ant Design 这件事,也源自我自己的信心。信心来源于早期做开源时的经历,之前在淘宝的时候做过淘宝的 UI 组件库,只不过当时没有设计语言,纯粹是一套前端组件库,但一定基础上对于业界处于什么情况,我们机会在什么地方,我内心比较笃定,心里已经有了七八成把握。确定 Ant Design 要做后,会一直强调长期主义,要有三五年的长期坚持,才有可能做出一点成绩。很幸运,我们坚持住了。 + +极客时间:感觉在体验技术部,你给这个团队找了很多目标,很多方向,做了很多产品。这是你提前规划的体验技术部的目标吗?你当时希望把这个团队打造成什么样呢? + +玉伯:当时团队最主要的职责是支持业务。业务在快速增长,支撑业务需要人员,团队如何组建起来,如何快速招聘到位,这是我有几年的关键目标。组建团队过程中,我需要一些事情去吸引人才,比如做 Ant Design 其中一个目的也是吸引人才,而且它确实起到了这个作用,实现了正循环。有人坚持做开源,开源开始做出一些影响力,从而进一步吸引更多人加入。现在 Ant Design 开源社区日常贡献活跃的有几十人,已远超开始投入的三四个人。 + +Ant Design、AntV 是外界比较了解的两个产品,还有一块是投入Node.js,把我认识的大拿苏千从淘宝挖过来了,然后放心地交给苏千负责。当时的主要布局,是在设计语言、数据可视化、Node.js,还有前端工程化这四个领域,有认真去规划和召集人才,然后坚持长期去做。 + +小结时刻 + +和印象中的前端大牛不同,从淘宝到支付宝,玉伯其实一直都在尝试做产品,一直有一颗产品心。到支付宝建立体验技术部后,有更多的前端人、设计师在成长为产品工程师。为了吸引人才,玉伯画了几张“饼”,Ant Design、Ant V都是吸引人才的饼,因为创新产品的从 0 到 1,并不是大张旗鼓地开干,前期更多是这些人在支持业务的同时兼职做这些事。 + +关于体验技术部的故事,现在在网上还可以找到“那些年的体验技术部”系列文章,记录了从这个部门孵化出的产品和成长起来的人的故事,如果你对这个团队感兴趣可自行了解。 + +下一讲,我们聊聊玉伯操刀的一个工具型产品——语雀。感谢你看到这,我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/07产品故事:语雀两度生死局.md b/专栏/超级访谈:对话玉伯/07产品故事:语雀两度生死局.md new file mode 100644 index 0000000..fd90370 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/07产品故事:语雀两度生死局.md @@ -0,0 +1,93 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 产品故事:语雀两度生死局 + 语雀是一款文档和知识库产品,2016 年从一个技术团队支付宝体验技术部生长出来,2021 年蚂蚁成立了智能协同事业部,重点产品即为语雀,以独立 BU 运作,算是完成了“成人礼”。我们和玉伯聊了聊语雀的成长故事。 + + + +极客时间:市面上有很多的文档类产品,语雀的口碑还不错,它能够在蚂蚁立足,特别是在阿里有钉钉文档的情况下,外界对二者的比较一直都在。我知道当时阿里准备做钉钉文档的时候,直接分了语雀的一批人去钉钉,对语雀来说应该是比较艰难的时刻。那么从开始一直到现在,你觉得语雀面临过哪些生死时刻? + +玉伯:我分享几个时间点。2016 年,体验技术部有个创新产品孵化机制,叫策马扬鞭,语雀是参与策马扬鞭的项目之一。那时语雀还叫云雀,英文名叫 lark,是一款为金融云服务的文档产品,出发点是“让技术人写文档更简单”。 + +语雀的发展可分成三个阶段,2016 年到 2018 年是语雀孵化期,怀胎两年,我们从文档这个需求起步,开始逐步替换掉公司里的 Confluence 和 Wiki(企业知识库软件)。我们实现了一个 Markdown 编辑器,程序员特别喜欢,语雀就在程序员团队开始流行。2016 年从零起步,到 2018 年在阿里内部语雀的 DAU(日活)已经破万,意味着每天有一万多人在使用内网语雀。10000 DAU 是决定这个产品能孵化出来的重要节点,在阿里内网,很多产品的使用人数都有天花板,过 1000 DAU 都挺难的,语雀能达到 10000 DAU,代表着无论是老板还是同学都会有基本感知。在我们团队内部,语雀也是一个内部创业项目的典范,这是语雀从零到一孵化出来的第一阶段。 + +第二个阶段,从 2018 年 1 月 8 号开始,我们有了 yuque.com 服务,正式对公网提供服务。现在我们会把 2018 年 1 月 8 号定义成语雀的生日,一方面是有了正式对外服务,一方面是因为这次发布会上,才把名称从云雀改名为语雀,同时也是因为到 2018 年,才算正式成立了一个团队去做。之前都是孵化期。 + +在国内,2018 年可以称之为文档爆发年。腾讯文档、钉钉文档、飞书文档等,我印象中都是这个时期开始出现在公众视野。语雀也不断加大投入,从七八个人,都 2018 年 7 月份时,团队已扩展到三十几人,可以撸起袖子好好干一场了。 + +在2018 年 7 月份时,语雀很快迎来了第一个生死点,各种文档都在起来,阿里也想做文档。公司在整体盘完后,确定要做阿里文档,同时最终决定要在钉钉做。那这个文档的初始团队怎么来?最后就是从语雀团队中分一拨人过去。语雀最终把三分之二的人,输送给了钉钉,成为了钉钉文档的初始团队。 + +这下语雀只剩下七八个人,团队非常灰心,但同时大家又很有信念,非常笃定。于是开始重新招聘,到 2019 年 4 月左右,团队重新回到二十几人,公网语雀也开始有了初步商业化的能力。2019 年 4 月 15 日是语雀商业化的开始时间,语雀算是又活了过来。 + +极客时间:大家觉得语雀可能要死掉,会不会有一个原因就是公司对做文档这个事情很重视,希望集中资源只做一个最好的。 + +玉伯:这是很重要的原因。另外,之所以还能留下语雀这个团队,而不是全部整合到钉钉文档,是因为语雀想做的,并不是纯文档。当时我会不断讲语雀是什么、钉钉文档是什么,这两个东西是有区别的。并打比方说,阿里文档是做 Google Docs,语雀是做 Confluence,这个类似,让很多决策者开始懂得钉钉文档是钉钉文档,语雀是语雀,这两者有交集,但更大的是差异,两者产品定位是不一样的。还有一个关键点是,语雀在内网已经有很多用户量,内部不可能直接关停,现有客户需要继续服务,再怎么说也得留下几个人来维护,所以最后才留下几个人。 + +另外还得感谢无招(陈航,原钉钉负责人)。说实在的,我觉得无招一定程度上是语雀的贵人,我能留下语雀的火苗,一方面我觉得产品定位不一样,一方面是现有客户要继续服务,同时,无招的一个观点也非常关键,无招觉得钉钉也是从无到有、好不容易创新出来的,语雀也是,所以对创新产品,公司应该要更有耐心,不能一下把语雀给掐死。其实他本来可以把语雀团队全部拿过去。就因为这句话,后来我跟他一直关系不错,彼此会敬佩。 + +到 2020 年时,语雀又迎来一次生死局。因为当时钉钉文档搞了很久,效果没达到预期,当时阿里云还是想尽快把文档方向发展起来,因此希望把语雀、钉钉文档、阿里云笔记等集团内各种做文档的团队聚集起来,成立一个独立的阿里文档事业部,想让我去带,不放在钉钉。这时,无招急了。 + +对无招来说,文档是钉钉很重要的一部分,独立发展文档并不妥当,这使得集结起来做阿里文档的事情又黄了。我说无招是语雀的贵人,也在这件事情上。无招不同意做阿里文档的思路,其实是间接又帮了语雀一次,让语雀能继续做。否则去搞阿里文档,语雀团队可能就又没了。 + +这个过程中,语雀算是经历了两次生死劫。后来一直到 2021 年 6 月份,正式成立了智能协同事业部,这个事业部的核心产品就是语雀,以一个独立 BU 去运作,语雀才算变成了公司的一块业务,正式比较稳定地能组织化去做。 + +在 2021 年之前,整个语雀团队都一直有个身份焦虑,虽然我们不用考虑下个月的工资从哪里来,但时时刻刻都得考虑语雀跟公司是什么关系。我们经常被人问起的就是“语雀和蚂蚁是什么关系?”,你又不是做支付,又不是做金融的,你在蚂蚁好奇怪啊。2019 年前后会天天被人问,我们自己也会这么想,会想公司什么时候会不让我们做了,会有这种担心。一直到去年,才不担心。 + +极客时间:你说钉钉和语雀这两个产品其实定位是不一样的,但从开始语雀也是做文档的,知识库这个概念,是一开始就想好的吗? + +玉伯:2016 年确实是文档,当时我们是借鉴了石墨文档,而且第一版在内部长得都像石墨,后来我们觉得石墨文档架构有问题,2017 年我们把整体架构推翻重来了。2017 年更多学习的是 GitHub,语雀其实就是文档界的 GitHub,GitHub 的核心是代码仓库,那么对应的文档仓库是什么?就是知识库,在语雀建一个文档的话你要先建知识库,从 2017 年我们就以知识库去定位语雀。 + +极客时间:知识库这个概念是怎么来的?是你命名的么?这个名字对于用户来说是有一个熟悉过程的,比如说我最开始看到知识库这三个字的时候不知道是什么意思。 + +玉伯:应该说语雀在文档产品里面正式把这个概念给推成了,我们一开始也不叫知识库,我们一开始叫仓库,因为我们最早是学 GitHub,代码仓库,我们是知识仓库,但是知识仓库这个名字对普通用户不太清晰易懂,于是我们觉得还是叫知识库比较好。 + +现在知识库这个概念钉钉文档和飞书文档都在用了,他们之前都不叫知识库,钉钉之前叫知识空间,里面有知识页、知识集等概念,后来才改成知识库这个概念。知识库这个概念还跟业界一个概念——智库是有相似性,语雀把这个概念变成了大家当下的理解,后来发现飞书他们也开始这么叫。 + +极客时间:再聊一聊语雀的定位和在一些事情的取舍,回到最开始做语雀的需求是想替代 Confluence 和 Wiki,在阿里内部大家用了这两个系统体验很不好是么? + +玉伯:对。因为当时阿里每个大点团队几乎都有自己的一套 Wiki 或者 Confluence,但实质上是彼此不互通的,都是各自搭一套。支付宝还好一点,整个技术部只有一套。同时 Confluence 有一个问题,它很容易知识僵化。在国外 Confluence 有很大市场,而且市场利润各方面都是挺好的,我觉得它在外海服务于一些中大企业是挺不错的。 + +但在阿里,大家可能都听过一个词叫拥抱变化,变化很快就意味着你用 Confluence 好不容易把目录树、各种结构都搭好了,然而组织结构一变化,Confluence 的文档就很容易变成一个荒岛,然后又要重新去搭建,很麻烦。很多时候,知识沉淀是跟组织结构有很大关系,当组织频繁变动时,当产品更新迭代很快时,需要的是一种更灵活的组织方式。在知识库领域,语雀切的是更灵活的结构化组织这个点。我们没有像 Confluence 一样去做很大的一个目录树,还是回到头来做非常离散的偏小团队的方式,同时借鉴了 Wiki 词条的组织方式,让文档之间都是平等的,文档树则通过目录编排去实现。这样在组织变化时,迁移成本会比 Confluence 会低很多,会比 Confluence 更适合组织的频繁变化。这个点,是语雀能在内部流行起来的重要因素之一。 + +当然还有一个原因,就是我们有很好用的 Markdown 编辑器,程序员特别喜欢,这也是一个很大原因。基本上早期就是靠这两个核心产品特色在阿里内部立住脚。 + +极客时间:你之前还说过,集团曾经对语雀也提过一些需求,但很多你们都没有做,大概是什么类型的需求,你们是怎么判断做还是不做的? + +玉伯:对,这个有好多。最典型的需求就是很多团队都希望有个知识门户,团队可以做整体管控,自己团队里的同学写的各种文档能够汇集到这个团队门户中。还有一些个性化的需求,比如希望当发布一个新人指南或者业务关键文档的时候,相关人员能看到且可以签到或者学习打卡等,这类需求偏学习平台性质。那语雀的做法是,宁可开放 API,让大家自己去基于语雀的 Open API 去定制包装,而不是我们去实现。因为每个团队的管理方式和所处的阶段不一样,这使得各种管控需求也往往会不一样,比如高德的需求,和菜鸟的需求就有比较大的差异性,菜鸟的需求,和阿里云的需求,往往甚至会彼此矛盾,每个大团队内部的知识管理体系不同,只有需求方自己最懂自己的需求,基于开放去自己定制,往往会更好,语雀去做,很容易极耗精力,最终用户可能还不满意。 + +极客时间:像这种需求的思考是最开始就已经非常清晰了,还是说开始的认识也有一些模糊的地方? + +玉伯:肯定一开始是模糊的,很容易被绑架,因为总拿客户第一说事,还会有来自某些高管的压力。我们早期也没看透,做过一些这种需求,后来发现基本全是坑。现在逐步看清楚之后,会非常谨慎。 + +极客时间:2019 年开始语雀尝试商业化,你也说语雀在商业模式上也算走过一些弯路,比如服务超过 1000 人的组织可能就涉及到定制,在做过这方面的尝试后,你们最终能确定就在企业服务中做真正的 SaaS 产品。能坚持这个目标也是很不容易的,因为现在很多所谓做 SaaS 的企业,有一个困扰是,因为业绩压力,就可能耐不住性子去做标品了,因为标品价格低嘛。这个时候如果有客户光顾,为了业绩他可能就会给客户去做定制,因为这样客单价高,企业就会很难去 hold 住这个立场。 + +玉伯:这个其实就是我没有选择出去创业的原因。对创业我也有自己的想法,创业其实很多时候可分成两种思路。第一种创业思路,就是为了钱或者是为了创业而创业,大家希望出去干一番事情,达到一定的规模,拿到 VC 的融资,最后还能够去敲个钟上市。很多人创业都是被这个故事给绑架了。我觉得这种创业,可以形容为“跳悬崖”,做很多事的出发点都是为了什么来钱快去做什么,因为这样才能保证在摔死之前能找到起飞点。这很残酷,很多这种奔着上市去的创业或者奔着资本去的创业,就是在玩九死一生的游戏。我觉得最后能成的都挺厉害的,成功之后就可以开始讲故事了,市面上充满着这种故事。 + +我自己的创业核心,还是回到“业”本身,“业”是佛学里面的一个概念。佛学里面叫做业障、业力,这个“业”就是你把自己内心想做的事情做完,同时你想做的事情也能帮到别人,这就是业。“创”是什么东西?就是创造,就是把业完成的过程。所以我内心的创业就变成:我想去做这件事情,且这件事情也能够帮助别人,那就去做。把这个定义好之后,除了前面那种出去开公司、去上市之外,对我来说,可选择的创业方式就太多了。 + +比如留在一个公司里,成为一个技术专家,通过大公司的平台去做自己想做的事情,这也是创业,就像当初我通过淘宝去服务更多人。我做语雀也是一样的,我想去做一款文档和知识库工具,希望更多人基于语雀能开始生产创作,让自己和让他人都能受益,这是我内心想去实现的一个“业”。 + +在这个选择过程里,也会有纠结,我有过很多选择,比如听 VC 的建议去拿几千万投资出去干,或者继续在大公司干。在大公司里干也会有很多选择,比如选择在阿里干,还是在腾讯干,还是去金山或者去字节,存在各种选择。 + +但最终我发现面对这些选择时,我只需回答一个问题:究竟哪个选择,可以把我想创的“业”更好做出来,也就是成功的概率更高,那我就去做哪个选择。如果出去开公司能够更让我更容易把“业”创出来,那我就去开公司。但是我自己分析下来,目前留在蚂蚁做,成功的概率反而是最高的。 + +极客时间:在大厂内部创业和出去创业相比,内部创业可能需要扛的压力小一点。 + +玉伯:焦虑的事情不一样吧,内部创业不用焦虑团队下个月的工资在哪,但可能有身份焦虑,在大公司里如何找到自己的定位,也是有压力的。那出去创业必须要焦虑一件事情:团队下个月的工资在哪?你首先要解决这个问题,才能够谈其他理想情怀,所以你就必然要围绕着资本和营收去做,围绕营收去做,很容易偏离本心。这类走歪的创业故事非常多,我就不多说了。 + +在大公司创业相对可以轻松自如去做,一定程度上压力是小于在外面创业的,但在产品上、业务思考上,很多方面压力也不会减少的,有时候也会变得更复杂一些,这是我的感受。 + +小结时刻 + +关于体验技术部的产品故事有很多,极客时间专栏《林外·专利写作第一课》,作者林外,是 Ant Design 的联合创始人之一,他曾经发文分享了 Ant Design 1.0 背后的故事,玉伯回复说:历史总是在经历时苦逼不堪,但在回忆时激荡不已。 + +成功背后也许都要经历一些磨难,想要升级必须打怪。语雀在那些生死时刻一定也是苦逼不堪的,但冲出重围就是不一样的故事了。 + +你用过语雀吗,在评论区说说你的使用感受吧,我们下一讲见。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/08产品经理能力进阶:用户洞察、抽象设计到看到远方.md b/专栏/超级访谈:对话玉伯/08产品经理能力进阶:用户洞察、抽象设计到看到远方.md new file mode 100644 index 0000000..97b3773 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/08产品经理能力进阶:用户洞察、抽象设计到看到远方.md @@ -0,0 +1,112 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 产品经理能力进阶:用户洞察、抽象设计到看到远方 + 上一节和玉伯聊了语雀的发展史,部分内容涉及玉伯对产品的理解,这一节继续围绕产品话题,聊一聊究竟产品经理是一个什么样的岗位,产品经理的基础能力模型是什么。 + + + +极客时间:依据你这么多年做产品的体会,你觉得应该如何定义产品经理这个岗位? + +玉伯:我可以聊聊产品经理应该具备什么能力,拿语雀举例,我觉得产品经理需要具备三个核心能力。 + +第一个核心能力是,产品经理必须要有需求洞察力。需求洞察力从哪里来?一个最关键的来源,是来自产品经理在特定领域的专业知识积累。比如做语雀,得对文档,对知识库,对传统的知识管理,以及知识协同等领域有体系化的理解。比如对历史上的卢曼卡片法,比如新近流行的双向链接,甚至最早期的互联网里网页与网页之间的连接,以及知识网络等等概念,作为产品经理,都需要去探究究竟是怎么回事。产品经理要把产品所在领域当成一个专业去钻研,要长期有兴趣,领域知识的专业积累非常重要。 + +领域知识的积累是一个产品经理具备需求洞察力的核心要素。此外,需求洞察力来自产品经理对用户的好奇,知道用户在什么场景下会去用这个产品,用户的使用路径是怎样的,使用过程中的心理状态是愉悦的还是有挫败感,用户会用产品里的什么样的功能去具体完成什么样一件事,等等。作为产品经理,要对用户的用法能感同身受,要有一种灵魂出窍和灵魂附体的感受。 + +产品经理的第二个核心能力是,必须要有抽象和设计能力。产品经理在日常工作中,经常干的活就是逻辑抽象和设计,你在做产品的时候可以洞察到很多用户需求,但你不可能每个需求都用一个产品功能去实现,删繁就简的过程,就需要抽象设计能力。 + +拿语雀举例,我们在文档里设计了斜杠命令,这是一个功能抽象。用户在写文档过程中,很多情况下写着写着可能需要快速插入一个卡片或者是一个链接之类的。在这个使用场景下,人眼的焦点在光标处,如果不用点击菜单而是直接在光标处快速唤起功能的话,效率会更高。如果不抽象成斜杠命令和斜杠面板,我们得把所有功能罗列在工具栏上。抽象设计往往没有对错,内蕴的是产品设计理念,产品经理的不同喜好,会带来不同的设计方式。比如我们一直没有做即时浮动工具栏,因为我们觉得浮动工具栏虽然便捷,但同时对专业用户是个强打扰。任何设计,往往都有利有弊,很难去谈对错,更需要的是取舍平衡。在抽象完之后,作为产品经理,还要能够与团队一起,去用 PRD、设计稿把具体设计做出来,抽象和设计能力是一体的。抽象是把需求化繁为简,而设计是化抽象为具象,这是产品经理的第二个核心能力。 + +产品经理的第三个核心能力是,需要有未来洞见和规划决策能力。这是我自己这么多年的体感,也是现在对团队里高级产品经理的一个要求。能够看见用户需求,把用户需求经过抽象、设计转化成一个产品功能,这是产品经理的基本能力。要把一个产品做长久,还非常需要产品经理的长期的洞见和规划决策能力,面对纷纷扰扰的各种用户声音时,面对各种相关方的质疑甚至否定时,产品经理必须要能看见未来,能和核心团队成员一起有内心的笃定,同时面对各种短中期的利益时,能有取舍判断力,敢于说不,同时又能不伤着对方,能保持长期友好的合作态度。面向未来的规划和决策能力,特别难,但这往往是优秀产品经理进阶的必由之路。 + +极客时间:我看到语雀里面有很多用户,无论是在知识库里还是其他地方,都会给你们提很多建议或者需求,小到某个功能怎么改,大到语雀未来应该走什么方向,你是怎么看待这些需求的?你有没有印象非常深刻的用户的需求。 + +玉伯:用户的声音确实蛮多的,现在每天都能收到几百个反馈。面对这么多反馈,首先我觉得要分清楚两个概念:用户反馈和产品需求是两个东西,绝对不能直接把用户反馈当成产品需求。从用户反馈到产品需求还需要经过产品经理的专业转化,有些反馈可以经过抽象与设计之后,变成一个有效产品需求。 + +更多用户反馈,反映的是用户使用过程中的情绪,反馈是用户情绪的一个出口。作为产品经理,非常有必要去看用户反馈,这也是我们团队的基本要求。同时,作为产品,在看反馈时,需要在看见一个个具体的反馈后,能在晚上闭上眼睛睡觉时,对用户群体有个感知,知道有哪几个用户群体,每个用户群体当前使用产品时,普遍的情绪是怎么样的,每个用户群体,看重的产品功能究竟是什么,普遍期待的产品改进又是什么。能闭上眼睛时,脑海里有一幕幕电影般的用户印象。 + +做产品还要有个心态,叫“弱水三千,只取一瓢饮”。张小龙说,每天都有一亿人教他做产品,语雀也是,每天也有几百人在教语雀做产品。这么多用户反馈里,能抽象总结出来的产品功能点,往往也是非常非常多。是否都要去做?还要取决于产品团队对产品本身的思考和定位,同时还要考虑到团队的产研能力,是否有精力去实现某个功能后,能长期有效发展下去。这里有很多很多交集,每个交集下来,都是弱水三千取一瓢,最后从一瓢瓢里,不断取下去,剩下的最后一瓢,才是当下需要去做的。 + +极客时间:用户反馈不等于产品需求,可以讲个案例么? + +玉伯:什么叫用户反馈,什么叫产品需求呢?这个蛮有意思。拿最近的一个例子来说,有一个语雀用户,很生气地给我们反馈一个事情,他说为什么我们写的文档发出来后,无法看到有谁读了? + +他希望某篇文档可以在有人看了后,他可以看到具体是谁读了,有没有读完,如果在规定时间内没看,语雀需要进一步提醒,不断倒计时提醒用户去阅读完成。对产品经理来说,这是一个文档的指定人群已读并提醒的功能,从技术角度讲,是可以去实现的。但如果真去做的话,就会发现这种需求做不完的。从语雀当前的产品定位来看,这个例子中的需求,就只是一个用户反馈。 + +后来我跟他深入沟通这个事情,问他为啥需要这个功能,了解到原来他在负责公司培训的事情,有一些培训材料要提前发给学员看,他想保证学员在上课前都已看过。我后来就问他公司里在用什么办公软件,用飞书还是钉钉,他说用钉钉。我说你可以拉一个群,把语雀文档发到群里,用钉钉已读未读来看大家是否已收到。钉钉已读虽然不等价于文档已读,但已经能解决很多。后来这个用户还自己研究出来了更好的解决方案,用钉钉的消息标签功能来做,让已读的同学在读完后,主动加一个已读的标签。 + +有时候跟用户沟通,深入了解使用场景后,往往用户自己就能找到合适的解决方案。但确实,有些情况下,与用户越沟通,会彼此越焦躁,用户的情绪甚至会被激发出来,会非常不理解语雀为什么不去做他想要的某个功能。这时,只能说抱歉了,冷静客观地说清楚我们的想法就好。客户第一这句话,讲的并不是把所有客户都当成第一,而是要分清楚,这么多客户里,哪些是语雀的第一客户,哪些是第二客户。分清楚后,做到有理有据有节,就好。 + +极客时间:拒绝用户反馈时,是不是也要看场景的大小呢? + +玉伯:这取决于产品定位。需求永远是无穷无止境的,如果什么需求都去做,这个产品就会什么都是,从而什么都不是。产品要有定位,有了定位就有了边界,有了边界才能清楚哪些需求应该做,哪些需求不应该做。 + +产品边界具体是什么?往往是探讨产品定位时,团队共同确定的一些共识约定,也包括共识的一些产品价值观。产品的价值观,往往就是团队的价值观。所谓价值观,最简单理解,就是你对一件事对错的判断,一件事情你觉得正确,是因为符合你的价值观,反之,一件事情符合你的价值观,你往往就会觉得是对的。像刚才那个用户的需求,希望能看到具体有谁读了文档,在我们的认知里,是有违背我们的价值观的,因为这涉及到用户隐私。真要做,也要分场景。在办公等互信场景里,因为已经在一个彼此了解的环境下,一篇文档具体谁读了的信息,是可以考虑公开的。然而进一步思考,语雀倡导的是轻松愉悦的学习工作氛围,如果自己是否阅读了某篇文档,是文档作者可看见的,那么就会对更多用户造成必须要去读完的心理压力,甚至会逐步引起同事与同事之间,在这种小事上的竞争,容易带来一些不必要的内卷。最后综合各种考虑,虽然这个用户的需求有其合理的使用场景,但语雀并没有选择去实现。 + +对于刚入门的产品经理,我们会让他去看用户反馈,一方面是让他增强用户体感,一方面是在训练他如何去真正看到用户需求。很多时候,用户给的反馈,你以为是一个需求,其实不是,而是用户在给你解决方案。比如“文档能指定别人阅读,能展现已读未读状态”,其实并不是用户需求,而是用户在给语雀提供解法,他真正的问题,在反馈里并没有告诉你,这在用户反馈中非常常见。分清楚什么是用户反馈,什么是产品功能,这是产品经理的第一课。 + +推荐一本书,叫《学会提问》。很经典的一本书,教你学会提问。产品经理有时候要反向看清楚这些问题,因为有大量的人向他提问。一个经典方法,是产品经理要学会 XYZ 思维。用户给你的是 X,你要去挖背后的 Y,甚至这个 Y 还是解决方案,你要继续挖 Z,你要挖到三四层才会知道原来用户是遇到了怎么样一个具体问题。张小龙在《微信背后的产品观》里也讲过,比如很多人都希望朋友圈有分组,挖到最后的需求并不是要分组,而是大家发朋友圈不想让部分人看见。不想让部分人看见那就有不同的解法了,在微信场景里,分组就不是最优解法,微信最终选择的解法是定向屏蔽。 + +极客时间:语雀的用户中,有没有超过你们预期的使用行为?比如你们本来没想到他会这么用。 + +玉伯:这个还蛮多的,因为语雀只是工具,这个工具可以做什么,更多是交给用户去发挥。就如你买一把刀,用来切西瓜还是切菜,这是用户的选择。 + +印象中很深刻的一个案例,是北大附中使用了语雀,除了常规的文档和知识库用法,我们发现,北大附中居然用语雀的话题功能,来组织同学进行线上辩论赛。参与的同学都很认真,一个话题,正方和反方发言有理有据,彼此讨论非常热烈。使用语雀来进行线上辩论赛,在北大附中老师主动跟我们说之前,我们自己都不知道语雀还能用来干这件事。 + +极客时间:对于高级产品经理的要求是未来洞见和规划决策能力,可以再具体解释一下么?比如拿语雀来说,未来洞见应该体现在哪里? + +玉伯:面向未来的洞见力,是指能看到更大的一个局,能想到这个产品三五年之后是什么样子。比如我们去看个人版语雀时,它的文档和知识库是可以公开的,这个公开看起来就是一个知识库文档的公开能力,但这个公开能力真的往下探,它究竟在解决用户什么场景的问题?有可能的机会点在什么地方?我们需要去想这些问题。目前语雀个人业务做了一个升级叫做数字花园,最最简单的理解就是新一代的博客专栏,我们会通过数字花园这个概念去把这个理念做包装,那为什么不叫专栏博客呢,这里面就会涉及对未来的理解。 + +洞见来自于什么地方?往往是来自历史,来自产品经理对过往的观察。比如说我们提到的数字花园,是我们发现,团队内部很多同学,包括我自己,不太逛朋友圈,也不大逛微博了,公众号也不太看了,抖音有时候会刷,但刷完之后很空虚。我们就在想这究竟是为什么。刚才谈到微博也好,朋友圈也好,所有这些产品都有一个共性:它们是基于时间的信息流或者内容流,都有一个 Timeline,让用户活在一个个时间流里。时间流产品一定是满足了当下很多用户的需求,但同时会让一波用户感到焦虑,想远离。 + +所以我们在想除了时间流之外,是不是还存在空间流,空间流的基本结构是怎样的,空间流是否能解决用户的焦虑问题,是否有可能构建一套更好的信息消费形态。比如说我想去了解某个人,我去微博上或者朋友圈上看他发了什么,只能了解他最近的一些情况。但如果这个人能够把自己的一些体系化知识、学习资料等按知识库分门别类的方式构建出来,形成一个个知识空间的话,那这个东西是从时间流里面脱离出来的一种空间沉淀。这个空间的沉淀就像花园里的景色一样,我们称之为数字花园。这是语雀很宏大的一个设想。 + +极客时间:我还有个特别好奇的,你说现在每天都会有几百条用户反馈,这个反馈量开始有多少,大概是怎么样的波动情况呢? + +玉伯:最开始一天只有几条,后来快速增长到了每天一两百条,现在每天有四五百条,还在持续增加。我们同时有在做一些观察和调整,希望反馈量的增速是收敛的。如果用户反馈量一直涨,往往意味着产品有问题。同时,如果没有用户反馈,那是更大的问题。 + +面对用户反馈,不能焦虑,语雀目前积累了几十万条用户反馈。曾经我们这边有一个运营同学,想做数据挖掘,想通过科学的数据分析之后,从这一堆用户反馈里推导出语雀应该做什么产品功能。曾有一段时间,我们迷信过这类数据,但最终发现这个方式不对,走过一段弯路。 + +极客时间:这个讲一讲为什么是走弯路?他是有分析出一个什么决策发现不行,还是说其实通过数据分析也分析不出来啥? + +玉伯:因为最后我们发现,数据的真正作用是辅助决策,数据一定不能绑架决策。 + +举个例子。曾经有段时间,我们挺焦虑语雀的用户增长,开始学其他网站做拉新,比如未登录时不让看,通过这种方式拉升注册用户数。从数据上看,用户注册数的确增加了。但同时,时常也会有用户反馈不好用,发给对方的语雀链接,对方看不了。这类反馈虽然不多,但每次看到时,总觉得哪里不太对。 + +后来我们把登录拦截去掉了。拦截虽然能带来短期注册上涨,然而用户的不爽,会影响产品的长期发展。做工具,还是得有良心,这是一个感性判断。 + +还有其他一些案例。我最大的一个感触是,一旦看数据很容易短视,这让我后怕。最终我们的经验是,数据是辅助决策的,最终决策还是得来自于产品的清晰定位和团队的长期信心。这不是说不应该看数据,而是要不断提醒自己,应该去看什么维度的数据。语雀目前最核心的两个指标,一个是付费客户数,代表有多少客户认可语雀,能让语雀长期健康发展下去。 + +语雀还有一个核心指标是自然留存率。自然留存率反映的是语雀的基础产品力。当用户来了语雀后,有多少用户会注册使用并长期留存,这是很关键的指标。如果一个用户主动想来用你,但你连这种用户都留不下的时候,这个产品就很危险了。当这个指标发生变化的时候,大概率表明什么地方出问题了。比如有段时间我们的短信服务验证码出了问题,导致自然留存率就暴跌,因为用户收不到验证码根本进不来。通过数据的反映,我们赶紧去解决这个问题。 + +找到一个好的数据指标挺难的。去找到合适的指标,也是产品经理的一项关键能力。语雀确定这两个关键指标,花了大半年不断讨论才达成共识。 + +极客时间:开始会看什么维度的数据?日活月活这种看么,如果数字很好的话,人性使然,人们还是更多愿意说这种证明产品很好的数字,或者叫虚荣指标。 + +玉伯:早期我们会看 MAU(月活)、DAU(日活)等数据。语雀的 MAU 很早就过千万了,但后来我们才想清楚,如果付费客户数没上去,MAU 越高,成本越大。 + +除了月活日活等流量指标,我们也会关注营收、用户平均使用时长等指标,会出现大量指标,充满诱惑。但一定要认识到,虚荣指标只是虚荣指标,并不代表产品的健康发展。 + +从这么多数据指标里,去找到符合语雀的关键北极星指标,需要不断去思考。比如,究竟语雀的业务方向是什么,语雀团队的竞争力在哪,语雀究竟有哪些重要用户群体,对用户群体提供的核心价值究竟是什么,把这些问题都逐步想清楚后,才能最终把关键数据指标确定下来。 + +小结时刻 + +关于产品经理能力进阶,这一节你可以关注: + + +产品经理的三个核心职责范围:需求洞察力、抽象设计能力、未来洞见和规划决策能力。 +产品经理的第一课就是分清什么是反馈,什么是需求。 +产品经理要具备需求洞察力的前提就是领域知识的积累。 +如果一个用户主动想来用你,但你连这种用户都留不下的时候,这个产品就很危险了。 +时间流产品一定是在当下满足了很多用户的需求,但同时会让一波用户感到焦虑,想远离。 +找到核心指标是产品需要关注的,语雀的核心指标是自然留存率和付费客户数。 + + +留个小问题,你所负责的产品的核心指标是什么呢,欢迎分享,我们下一节见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/09个人成长关键词一:全情投入.md b/专栏/超级访谈:对话玉伯/09个人成长关键词一:全情投入.md new file mode 100644 index 0000000..21940a2 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/09个人成长关键词一:全情投入.md @@ -0,0 +1,97 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 个人成长关键词一:全情投入 + 玉伯曾经做过一个分享,名字叫《我的前端成长之路》,这个分享是在回答一个问题,11年里,对自己的成长来说,最关键的是什么?他自己总结了三个关键词:全情投入、守正出奇、愿等花开,针对这三个关键词,我们和玉伯进行了更深入的沟通。如今,全情投入是目前对他来说更重要的关键词。 + + + +极客时间:想请你聊聊技术人成长的话题,你之前在内部的分享中提到三个成长的关键词:全情投入,守正出奇,愿等花开。现在这三个词对你来说依然很重要吗?有没有什么变化? + +玉伯:这三个词是我几年前的总结,对我自己的影响也蛮大。现在来看,会觉得全情投入是最重要的。无论工作在什么阶段,都需要全情投入。 + +很多时候,会想不清楚自己想要什么,或很容易想得太大。要谨慎去想人生价值、人生意义这种非常大的话题,想多了容易陷入虚无主义。我的经验是,关注具体的目标,关注短中期的目标,做到全情投入,忙起来,人会更容易快乐。 + +极客时间:忙起来还分这个工作是不是自己喜欢的,如果能忙自己感兴趣的事情,会不会更好,这是必要的吗? + +玉伯:我觉得不是必要的。很多人想不清楚自己究竟喜欢什么。很早就想清楚自己喜欢什么,是可遇不可求的。更多时候,需要具体投入到某件事,做到一定阶段,才能确认自己喜不喜欢。 + +打个比方,作为文字工作者,写文章是日常,刚开始写的时候往往很痛苦,会以为自己不喜欢。但是坚持写下去,写着写着,有更多读者看的时候,或者当自己达到某种心流状态(写东西时不是脑子指挥笔,而是能感受到笔指挥脑袋的状态)时,往往就会喜欢上写作。跑步也类似。 + +很多人总说找自己喜欢的事情做,我觉得有两种逻辑,要看是爱一行干一行,还是干一行爱一行。爱一行干一行,干的就是自己爱的,这是运气爆棚。先干一行,干起来,在干的过程中去确定自己是不是喜欢,或者开始喜欢上或讨厌上,这有更大的可行性。 + +极客时间:如果我们最开始选定做某一个事情,某个方向的工作,那就先抛却杂念,全心投入进去,慢慢再去寻找吧。 + +玉伯:与其选来选去,不如全情投入一件事情。因为一旦开始选择,就会有苦恼。天天想着寻找喜欢的,就会有失落,怕自己找不到,最终结果可能 99.99% 的概率的确找不到。 + +极客时间:那你觉得你有寻找的过程么,不管是做技术,还是做产品,你寻找到了感兴趣的方向了吗? + +玉伯:我最终找到的方向,不是做产品或做技术这种分类方式,而是换了一种视角去看问题,会去看所做事情的价值在哪,这个价值会让我觉得活着有意义,这是一种经历过后被赋予的意义。 + +很多事情要经历过后,才能通过自己或他人去赋予意义。比如,早期我在折腾开源项目时,天天干到两三点,经常腰酸背疼,虽然写了很多很多开源代码,但当时工作和生活状态都不是太好。我昨天讲述这些事时,你可能觉得我当时是觉得很有价值很有意义的,其实并不是。意义是在过了好几年后,回头看时,才发现那段时光,虽苦但很有意义。 + +往大了说,经历过万里长征的人一定在过程中天天骂娘,你只有熬到了延安,新中国成立,再回头看长征过程,才觉得自己太牛了。万里长征、红军四渡赤水,都是非常有意义的,但这些意义都是后面才赋予的。 + +所以我最喜欢的还是全情投入,当下要做什么样的事情,就把自己的全部精力投进去,不去想做完之后能够得到什么,能有什么意义。全情投入其实特别难,要把所有的杂念都去掉。 + +极客时间:那怎么才能跨越这个难点,跨越这个坎儿,然后全情投入进去呢?这是非常理想化的情况,而且我觉得现代人的困境特别是年轻人的困境是很明确的,如果不想自己能获得什么,不想怎么利益最大化而只沉心做一件事好难。 + +玉伯:现代年轻人挺有意思,我刻意观察过。他们特别容易全情投入,同时也特别不容易全情投入。 + +比如出去团建,打王者荣耀,很多同学会很享受,打游戏时非常全情投入。同时仔细观察,会发现也有部分同学虽然在打游戏,但有些勉强,眼神是飘忽的。 + +换一个场景,有些同学写代码时能心无旁骛,但如果去参加 PRD 评审,经常会默默坐在角落里,不太说话,不怎么融入。 + +全情投入并不是一种态度,而是一种能力。比如能全情投入写代码的同学,为什么参加会议时无法做到全情投入?往往是因为这位同学比较内向,不敢沟通,心有害怕,在会议时想全情投入也很难做到。 + +这需要提升自己的能力,比如当你面前不管站的是谁,你都不担心自己说错话,都敢于表达自己,能克服内心的恐惧,具备这种心态调节能力后,才能做到全情投入。 + +提升能力,方能全情投入,打游戏也需要达到一定能力后,才能进入全情投入的心流。这是一个过程,关键点还是能力提升。 + +极客时间:上面是在说在工作中的全情投入,那么在个人的成长中呢,你怎么全情投入?你也提到有早课的习惯,早上会有个固定学习的时间,现在还有吗? + +玉伯:我现在还保持着日课习惯。除了日课,还给自己增加了早课和晨课。日课、早课、晨课,是我的一日三课,这是我自己一直在坚持的小习惯。 + +日课是干嘛的呢?日课时间很短,只花 5-10 分钟抄一些东西。前不久我在抄《金刚经》,让自己每天开始有一种仪式感,向先哲致敬,我花了大概四个月把《金刚经》抄完了。日课我只定时不定量。只定时是指,我到了公司后,前 5-10 分钟就用来抄经,抄多少不做任何限制,时间到了我就结束。这样会无压力,使得能坚持很长时间,成为习惯。 + +早课是训练自己写作的能力,让自己的写作能力得到不断地刻意训练。工作日,会想办法给自己留出一个小时去写点东西。我会逼着自己在一周里面的写作练习不少于两次,现在能做到一周写 3-4 篇文章。 + +比如说我前天写的主题是如何沟通,会把《非暴力沟通》等经典书籍再翻一翻,同时结合实践,总结一些心得写下来。昨天写的是“什么是幸福”,是一种意识流的东西,会刻意去捕捉脑海里对幸福的思绪,比如“幸福就是好的变化”。 + +晨课就是一日之计在于晨。晨课很简单,每天早上会花 15 分钟左右去规划今天具体干什么。比如去参加哪些会议,要完成哪几件事等具体日程安排。 + +一日三课,这是我每天的微习惯。 + + + +极客时间:你前面讲的这些,其实感觉是有点活在当下的理念。 + +玉伯:全情投入就是活在当下。 + +对活在当下的感触,很大程度来自对日课的坚持。每天5-10分钟的日课,让我这几年抄完了《论语》《道德经》《庄子》《孙子兵法》《金刚经》,最近在抄《楞严经》。 + +抄的时候是不求甚解的,仅仅是抄,感受文字本身。很多文字到现在我也不知道在讲什么,但是当我反复抄时,过程中开始不断有新感悟。比如佛学里谈及的我相、人相、众生相,我之前不太理解,在抄《金刚经》时,反复出现这几个词,现在再看到这几词时,就特别熟悉,像是老朋友,觉得一切是明明白白的。 + +日课我只定时不定量,时间到了我就结束,只要是这个时间段,我只干这个事情。这种体验,完全没有压力,同时会有惊喜,比如会发现原来每天抄 5-10 分钟,3 个多月居然可以把《金刚经》抄完。这类经历,也会让自己真正理解“滴水石穿”、“功不唐捐”、“日拱一卒”是什么意思。以前只是认识了这些成语,现在通过经历真正感受过后,会有种敬畏感。 + +极客时间:为什么会想做三课这个事情? + +玉伯:这是受一本书的影响,叫做《微习惯》,每天一个微小行为,能生成巨大的力量。这本书讲的是,很多时候人的行为,包括思维,都是由一些微小习惯组成的。 + +日课是受《刻意练习》和《微习惯》这两本书影响,我是拿自己做实验,看到底坚持下来会怎么样。最早写日课是 2019 年,选择了定时抄书来实践,现在很感激自己的这个实验,让自己养成了日课的习惯,收获非常多。 + +小结时刻 + +人唯一的财富就是自己的时间,要对自己的时间负责。最好的时间管理,就是全情投入。 + +最后,留个小问题,你在日常生活中有什么持续坚持的小习惯么?欢迎在评论区分享,我们下一讲见! + +延伸阅读 + +我的前端成长之路 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/10个人成长关键词二:守正出奇.md b/专栏/超级访谈:对话玉伯/10个人成长关键词二:守正出奇.md new file mode 100644 index 0000000..318321f --- /dev/null +++ b/专栏/超级访谈:对话玉伯/10个人成长关键词二:守正出奇.md @@ -0,0 +1,109 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 个人成长关键词二:守正出奇 + 玉伯总结第二个成长关键词是守正出奇,那么对于一个技术 Leader 和业务 Leader 来说,什么是正?什么是奇?他又是经历过什么故事,才得到这样的体悟呢?这一节,我们就围绕这个词展开吧。 + + + +极客时间:守正出奇(qi)也可以读作守正出奇(ji),“出奇”可以解释为出奇制胜,也可以像你一样解释为要有多余的部署。从技术团队角度讲,你认为什么是正,什么是奇呢? + +玉伯:回到《孙子兵法》去看,“以正和,以奇胜”,是指两军对垒时,你的正面要守住,同时其实你还有一支很强悍的,也是人数比较多的军队去突袭对手,你就胜了。 + +我当时提守正出奇这个点,核心是让大家理解对我们来说“正”是什么?技术团队的本职工作就是产品研发,这个正是不能丢的。你不能说公司给你安排的活你不干,你去干其他的,这就不叫守正出奇了。当技术团队本职工作没有守住的时候,你去做一些技术创新,可能很容易就做歪了。 + +所以守正出奇的前提条件是团队 Leader 对这个“正”本身有足够的理解,真的是能够坚持把这个“正”给做好了,因为这是基本盘。奇(ji)就是多一点的意思。多一些人力和时间去做更多事情,才能赢取胜利。 + +极客时间:你带领团队一起做了很多创新产品,对你们来说做的这些事算是“正”还是“奇”呢?在这个过程中有没有偏离“正”的时候? + +玉伯:这个问题我也很有体悟,曾经因为没深刻理解这个词,导致有些产品夭折了。 + +做创新产品的第一步,是选择产品方向。华人管理大师杨国安教授,有一个著名的杨三角理论,是说在做一件事之前,请回答“团队愿不愿意做?”“有没有能力做?”“市场、公司容不容许做?”这三个问题。 + + + +我曾经在内部做过一个产品叫做“九色鹿”,大家应该没有听过,是一个洞察分析平台,或者叫流量分析平台,可以分析一个网站或应用的流量点击情况、整体的用户河流图等等。因为我们当时在做数据可视化,又有人懂全栈开发,我们拿杨三角理论一分析,觉得我们应该是具备可能性的,就去做了。 + +怎么分析的呢?一是觉得方向不错,我们愿意做。为什么是我们做?因为我们有能力做。公司允不允许做呢?公司也没说不允许做,那我们就感觉这件事靠谱,大家就干吧。 + +但这个产品发展到第三年第四年的时候,就发现我们当时的判断是错的。 + +首先公司不允许我们做,更适合做这件事的团队应该是数据平台,数据平台去做流量分析平台更合适。我们即便要做,也得是帮助数据平台去做这个产品。所以这个产品最终是交给了数据平台团队。包括我们当时回答“有没有能力做”这个点,也有点乐观,后来发现其实我们也缺失数据分析 BI 这块能力的。对于前端来说,要去学包括 ETL 等技能,可数据挖掘这个领域挺深的,我们低估了难度。 + +最终发现这件事只剩下我们愿意做,有意愿,但实际能力有欠缺,公司也不允许。最后这个产品交接给了数据平台团队,我们继续做前端研发。 + +极客时间:你们做前端支持工作,这样搭配起来是更合适的对吗?对你们来说,这次“出奇”其实没出对? + +玉伯:对,这是一次失败的创新尝试。我们原以为是奇,但其实不是。我们更应该的是帮助对方去把这个产品做成,这才是正,是我们的本职工作。但是我们把它当成了奇去做,这就变成了抢地盘。守正出奇,奇没定义清楚的话,往往就会变成抢地盘。 + +极客时间:九色鹿这个团队毕竟做了三四年,当时团队有多少人?最后这件事对大家有什么影响吗? + +玉伯:九色鹿一开始是三个同学,到后来是我们成立了正式团队去做。九色鹿团队是我第一次解散一个团队,是让他们去做其他事情,并不是离开公司。 + +说到这里,其实除了九色鹿,我们团队还做过一个存零花钱的项目。后来也发现并不合适在我们团队继续去做,最后交出去了。交出去,是为了这个产品更好发展。一个产品在不同阶段,往往需要不同的能力。 + +极客时间:经过这两件事,两个送出去的产品,会对你们团队产生不好的影响吗?在项目里的人肯定比较伤心吧。 + +玉伯:当然会,短时间来看影响一定是负面的,大家士气都很低,特别是身处其中的同学很受打击。甚至后来有几个同学都去了其他团队,因为确实情感上接受不了,相当于自己生了个孩子,最后要送给别人。短期对团队是有打击的。 + +但从长期看,大家能经历这么一场是好事,因为经过这些项目,才能真正看清楚,守正出奇的“奇”是什么,创新的方向应该如何选择。不是你做一个创新就是奇,而是你要找到适合本部门的创新才叫守正出奇。 + +去做一件新事情时,一定要思考清楚“为什么是你做”这个问题,一定要把这个问题先回答清楚。这个问题想得越早越清楚,越能减少遗憾,越能让想做的事情真有机会去做成。 + +极客时间:做了决定要把项目叫停的时候,你肯定也要跟同学们说为什么是这个结果,叫停的过程你也说大家很痛苦嘛,那对你来说你面临的痛苦是什么? + +玉伯:痛苦是回想起来很痛苦,当时更多是觉得很难。 + +难点之一,就是如何解决情绪问题。大家对自己做的东西有情感,人有情绪的时候你是很难去沟通的。 + +难点之二,是如何说清楚为什么要叫停。要说清楚停止的原因,要么拿数据,要么拿逻辑,只有这两样东西。在内部创新产品中,数据往往不能用,大家会说坚持长期主义可能数据就涨起来了,是要被挑战的。这跟外面创业不一样,在外面创业可以说没钱了,所以解散。最终我只能用逻辑来说明原因,可因果这东西,其实往往只是相关性。 + +最终,还是从理性上讲逻辑,让大家理解,从感性上晓之以情,让大家接受。 + +真正接受,还是得靠时间,可能要几个月。比如说九色鹿,有个同学两年以后才真正释然。 + +两年以后才释然,是因为他后来也做了一个类似的产品,知道原来内部创业是这么不容易。他真正理解那个 WHY 了,领会到了不是当时玉伯不让做,而是真的很难做。其实人的成长是自己的事情,主管也好,同学也好,只能帮他。他能不能成长、转变,取决于他自己。 + +现在我们立项时,会把一些问题想得更透彻后再去做,做的过程中如果发现走错了,不管是因为早期的贪婪也好,过程中做错了也好,都要果断停下来。停的过程是挺痛苦的,因为你会跟长期主义理念做博弈,会想也许再坚持一会儿可能一切都顺理成章了。但最终得到这样的结果,还得认知到一点,就是运气也是成功的必要因素,不必放大长期努力的因素。 + +一定要敬畏运气,运气往往代表着你所不知的各类环境要素。运气是天时地利人和,运气和很多因素相关。我们很容易崇拜名人名言,但其实每个人都可以说名言啊。你相信某个人说的某句话,背后是因为这个人做成了一些事情,人的大脑会潜意识地做归因,会觉得是因为这个人有这些认知才做成了这件事,但实际上很可能运气占很大成分。我们要谨慎看待“名言”。 + +极客时间:对于团队 Leader 来说,当你看到一个新项目的方向,去判断到底是正还是奇,这是非常关键的问题。经过九色鹿和零花钱这两件事,肯定也让你积累了很宝贵的经验。 + +玉伯:对,所以回头看,无论是发起一个技术产品还是创新产品,我们要回答好灵魂三问:Why?Why us?Why now?就是这三个问题。Why 是从整个大市场环境来看,你为什么要做?比如说你现在说我要做个微信,但是这个 Why 就说不过去了,已经有微信了,你凭什么要做?你要做,你也得切其他的一些细分领域,或者是说跟微信不一样,你才有机会,这是从整个大环境去看你做这个产品的可能性和机会点。 + +第二个问题,Why us,为什么是我们来做?像九色鹿也好,零花钱也好,很多时候是 Why us 没有回答好,我们傻傻地以为我们愿意做就等于我们就可以做。如果是技术产品的话,要看公司团队里面各个职责分工,比如我们作为大前端团队,对于前端的一些基建,我们来做是非常理所当然的。但这个时候比如你要给 Java 开发工程师去做一个 Serveless 平台(其实我们也尝试过,但是很快又转方向了),就发现其实我们这个团队不是最适合的,交给 Java 团队、中间件团队会更合适。 + +第三个问题 Why now?为什么是现在做?因为你做早了肯定是炮灰,你做晚了也没机会了。那你要去判断当下哪个这个时间点可能是最合适去切入的。 + +极客时间:聊到这,相信大家对守正出奇这个词有更深刻的理解了。对于你个人经历,我还有一点好奇,外界对你的印象很多还停留在前端大牛的标签上,但是通过你分享的这些经历,看出来你一直有颗产品心,一直都在做产品,所以从技术岗位到产品其实是很平滑的?这算不算“守正出奇”呢? + +玉伯:传统意义上,大家可能觉得像语雀才算产品,但我自己一直不太认可这个逻辑。我之前在淘宝的时候做开源,那些开源技术我也当成产品去做的,技术本身也是产品。或者说我一直以做产品的心态去做技术,那么我一开始就是在前端领域里做产品。 + +对我来说,可以叫从技术产品转向业务产品,这是变化比较大的。第一个让我感受到不同的就是零花钱项目,当时我招了一个不错的运营同学,很优秀。做这个产品过程中,对我最大启发的就是这位运营同学,他会不断强调说“产品就是运营,运营就是产品,产品和运营是一体化的”。现在我很容易理解这句话,但是当年我非常难以理解,为什么说运营就是产品,产品就是运营,这不是忽悠我吗? + +现在我理解了,我们谈运营总离不开拉新促活、AARRR 用户分析经典模型等等,但是本质上如何拉新要回到你的产品,特别是产品 0-1 的阶段,如果你的产品力本身不行,运营来了十个就会走十个,是留不下来的。“产品就是运营,运营就是产品”强调的是产品留存率,运营要有效果的话,那么产品留存率一定要达到一个值,比如来十个人能留住六个,这个时候来一百个就能留住六十个。这还是最粗浅的理解。运营和产品,就和人的左脚和右脚一样,都具备,人的活动才自如。 + +极客时间:是不是产品在 0-1 的阶段,必须要有自然增长才能往前做。不同类型产品的留存率定义也不一样,很难找到一个标准,比如 10 个能留下 4 个也是能投入的,所以判断能不能投入,其实还是要看个人的洞见。 + +玉伯:要看具体情况。十个留一个是对的,十个留十个也是对的,取决于一开始选择的用户池正确与否,如果选错了,那十个留零个也正常,因为目标人群搞错了。比如小天才手表给大学生推广,那肯定一个也留不下来。这个过程中的启发就是,除了留存率或者自然增长,运营还要回到用户人群定义上,其实本质上跟产品的思考是一样的,运营要想的点跟产品想的点在基本盘上一定要有强烈共识,最好是一套,否则就会有拉扯,产品说我们是给中学生服务,运营说我推广给大学生,就无法匹配。 + +运营和产品的关系,也分阶段。一开始往往是“运营是运营,产品是产品”,彼此不太理解,有内耗有拉扯,磕磕碰碰,往后发展是“运营就是产品,产品就是运营”,开始一体化思考,对很多地方都开始有共识,然后再往后,还是会回到“运营还是运营,产品还是产品”,就和左脚和右脚一样,虽然都是脚,但还是分开的,同时能良好协同,能健步如飞。 + +极客时间:对于产品和运营在什么阶段应该怎么配合,你有这些体悟,除此之外从技术产品到业务产品,还有哪些让你感受不同的地方? + +玉伯:从技术产品到做业务产品最大的变化,就是对技术、产品、运营整个理解会更有层次。不像之前做 Ant Design 的时候,那时候我们也做运营,但是那个技术就是运营,运营也是技术,在技术圈子里面靠个人影响力,不断增加 Star 数,项目做得好的话,也会上 GitHub 趋势榜,等于平台帮助你做了运营,莫名其妙你就有这么多粉丝,技术产品更多在做技术合作,通过社区做影响力顺带做运营。 + +之前我们在做前端开发支撑业务的时候,虽然天天服务运营,但很多时候对他们做的事其实也不太理解,总是搞红包拉新,那会觉得天天搞什么不靠谱的东西,但现在我肯定不这样想了。这是业务产品带给我的改变。 + +小结时刻 + +守正出奇的前提条件是团队 Leader 对这个“正”本身有足够的理解,守住本职工作,再去做创新。做创新产品第一步,要选择产品方向,无论是大公司内部产品还是外部创业,做决策前先回答“团队愿不愿意做?”“有没有能力做?”“市场、公司容不容许做?”这三个问题,谨慎作答后,才会更接近成功。 + +你对守正出奇有自己的理解吗?听完玉伯的解释,你有什么感受,欢迎在评论区发表看法,我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/11个人成长关键词三:愿等花开.md b/专栏/超级访谈:对话玉伯/11个人成长关键词三:愿等花开.md new file mode 100644 index 0000000..875a2ec --- /dev/null +++ b/专栏/超级访谈:对话玉伯/11个人成长关键词三:愿等花开.md @@ -0,0 +1,93 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 个人成长关键词三:愿等花开 + 玉伯总结第三个成长的关键词是愿等花开。愿等花开,就是长期主义。作为团队Leader如何带领大家坚持长期主义,从个人角度出发,玉伯自己又是怎么坚持长期主义的呢?这一节我们聊聊这些话题。 + + + +极客时间:你曾经说如果真的笃定一件事,一定要学会等待,不要着急去催熟,包括你跟团队经常强调,做事要快但不能急,这就是你坚持的信条,文艺一点叫愿等花开。你是通过哪些事情,总结出来要坚持长期主义的?是一开始就这样想呢,还是事后总结? + +玉伯:在阿里做规划时,有个提法是看五年、想三年、定一年。我们团队在实践中也会有这个要求,比如说 2022 年年度规划时,会去想 2024 年整个团队会达到什么样的状态,应该做哪些重要的事情。 + +坚持长期主义是我们团队的特色之一,已经变成了一种习惯。在阿里,很多团队都会用“五三一”去做规划,但是很多团队还是会偏短期,是因为经常容易会把规划跟 KPI、OKR 等考核周期绑在一起。我的做法是,会刻意错开,不跟公司财年或自然年绑在一起。比如说最近的三年规划,是 2021 年 8 月份去做的。为什么这么做呢?因为一旦跟绩效周期绑在一起,绩效没有完成的时候就会焦虑。哪怕是长期规划,执行时大概率也会变成短期绩效。 + +公司对团队的定位,和团队想做什么,要双向去看。比如公司对于前端团队的定位和期待,就是要降本增效,要帮助产品体验提升,但是回到我自身去看这个团队,就不光要达成公司降本增效的目标,还要达成公司没有明示但会买单的目标,比如团队的长期可持续发展。不能说今天我们帮公司降本增效了,明天团队散了,公司肯定也不希望看到这个结果。公司对团队的目标是有隐性的,是要自己挖掘的,比如团队的可持续发展,比如技术的创新性和先进性,这些隐形目标,需要靠我们的专业度去看见,这很重要。 + +举个例子,Ant Design 已经做到了西湖区第一和中国第一,最近我们对 Ant Design 的目标规划,其中影响力这块的目标就比较简单了,就是从中国第一变成全球第一。从 Star 数看,Ant Design 目前已经是全球第二,我们只剩下一个对手。我们以这种方式去定义 Ant Design 全球第一的三年目标规划,对公司来说,公司是不关心的,但是公司关心前端技术团队的人才吸引力、长期的稳定性。做 Ant Design 可以提升前端团队的人才密度,可以给公司带来良性的影响,公司是不会反对的。 + +极客时间:具体来说看五年,想三年,定一年,在制定目标的时候大概会看哪些方面? + +玉伯:想五年更多是根据团队定位,比如大家知道目前大环境不太好,很多公司都在做组织优化。这时候降本增效一定是接下来三到五年被关注的点。那么在前端侧,如何做到降本增效?这就是我必须要考虑的问题。 + +如何降本增效,关键策略是什么?比如什么样的业务应该用我们自己的前端支持,什么样的业务可以找外面的 ISV(Independent Software Vendors,独立软件开发商 )独立交付,这些用工策略,都需要去思考。同时是否加大低代码平台的研发力度,是否可以降低技术门槛,是否可以实现人员的统招统培等等。在降本增效的方向上,就可以得到一些非常具体的规划。 + +“看五年、想三年”的出发点,是站在公司角度去想,站在公司角度去思考如何降本增效,如何创新等等。结合过去和当下的一些难点,同时去看未来可能的发展趋势,再结合团队的实际情况,最后去制定今年和接下来三年做的事情。 + +在这种降本增效大环境下,必须还要考虑哪些方向需要做取舍,让事情更聚焦。这也是守正出奇,现在必须得把正守好,如果现在出奇的地方太多,就容易失衡。 + +再拿 Ant Design 举个例子,全球第一是我们的三年目标,但是同时这也是做了很大取舍的一个目标。比如 Ant Design 一直跟设计师团队走得很近,很早以来,就在探索设计工程化方向,这里面,有个梦想,就是想做“中国的 Figma” —— 一个在线设计工具,能够替代 Sketch。如果按照往年的情况,我们真的可能会投入人力做,但今年整体形势不好,“中国的 Figma”这个点子,在 Ant Design 大方向里面就被我们暂时砍掉了。另外,像 AntV 在智能可视化方面我们是非常看好的,还是会重点投入去做。所有规划,都需要取舍平衡。 + +极客时间:这是以团队角度去看长期主义,因为你是团队管理者的角色。那比如说从个人成长角度,让我印象深刻的就是你坚持了几年的三课:日课、早课、晨课,以你自己的经历,要怎么坚持这种长期主义? + +玉伯:刚才是从团队规划的角度去看长期主义。但是坚持长期主义,同时要把守正出奇放在前面,一定要弄清楚哪些是正,哪些是奇,对于“正”的部分也要清晰中短期的里程碑目标,这样才能保证活着。然后“奇”的部分,比如 Ant Design 跟我们的“正”并不相关,至少当时做的时候不直接相关,这时候就要花三年去看结果。这是从团队规划、守正出奇的角度看长期主义。 + +回到个人角度,坚持长期主义也是很好玩的事情,其实我一直没有太多的年龄焦虑,包括和一些同学聊天的时候,有时候都很难共情。很多人会关心自己 35 岁了,还没有到 P8,自己同时期的同学已经 P8、P9 了,会去比较。我不会这么去想,可能是和我在中科院物理所那时候的经历有关。那时候大家都要写论文嘛,很多很牛的一些 paper 是怎么写出来的呢?我就发现比如自己的导师或者学长,他可能前面几年就是写不出来,一直憋到研三快毕业了,他突然搞了一篇很牛的 paper 发表在《Nature》或者《Science》上,立刻就能毕业了。可能那些研二就写 paper 写得很顺的,即使写了很多篇,都不及最后人家发的这一篇。 + +有时候很多人把自己和同事、同辈朋友去比较,觉得别人混得好。可能他就是比你早几年接触一些东西,但你反过来想,那我就大不了比他多活几年嘛。这么着急干嘛呢?我只要比他多活几年,什么都有了。你可以认为这是阿 Q 精神,但无论它是什么精神,重要的是去找到一种自我和解的方式,敢于用自嘲、自讽或者自我调侃的东西来打破焦虑。 + +我曾经有过身高焦虑,但是后来应该在我高中长不了个子的时候就想通了,第一个,反正长不高了,改变不了啥。第二个,因为我们当时很喜欢姚明,姚明也才 2 米多,他再高也没有比我高出一米,就会觉得自己的身高也还可以,还会挺庆幸自己不是侏儒,身高焦虑就不见了。 + +包括别人说我长得黑,后来我的办法是以黑治黑。经常在团队拍合照时,我们的口号是“玉伯黑不黑”,大家再一起说“黑黑黑”,很开心很治愈。 + +极客时间:这种心态很好,能做到自嘲,自讽,很多时候也是自信的表现,特别有底气的时候才容易自嘲。你的自信是怎么建立起来的呢? + +玉伯:这个自信怎么来的我也不知道,我现在都觉得是个谜。比如姚明真的站在我面前,我也觉得你就是高出几十厘米,我没啥可自卑的。 + +如果真要归因,我回想起来可能有两个方面。第一个方面和自己小时候的成长环境相关。我小学是在小山村里面,村里小学总共两个教室,叫复式班,很多人应该都没经历过。全校就两个老师,校长和副校长,他们要管所有课程。当时一年级跟三年级在同一个教室,然后分成两边坐,一年级上课的时候我们在做作业,我们上课的时候一年级做作业。这是老师的安排,但是我们都不听,一年级上课的时候我们全部出去玩儿,老师使劲喊,上完课了,你们赶快回来,然后大家才跑回去上课。 + +小学阶段其实从来没有过学习方面的压力,真的是叫做山里的野孩子,当时小学是在山顶上,我们漫山遍野地跑,爬树都是很厉害的。这算是小学无忧无虑的阶段。 + +后来上了初中,初中是非常乱的。我还记得这个寝室跟那个寝室拿着铁锹、铁铲互相干仗,当时我就经历过同寝室的人挂了,就是睡在我上铺的兄弟。中学时候看了很多武侠小说,甚至有时候感觉武侠就在身边。 + +极客时间:这件事儿对你影响大么,那时候会感觉很害怕么? + +玉伯:当时觉得好像也没有那么恐惧,反正我现在回想起来就觉得这个兄弟走了,大家还是继续上课。当时还有打架斗殴被逮进去蹲监狱的,初中是非常乱的,到了高中这种情况才稍微好点。 + +中学这个阶段可能因为自己的成绩还可以,就一直没有啥压力。我的成绩属于一直在全校前十名徘徊,我也挺满意,觉得能在前十就挺好,可以继续上学。当时有观察第一二名,学得也太辛苦了,我没有那种去卷的意识,一直到高三我才觉得可以好好努一把劲。高三的时候开始有月考,我一般是第三名,有一回从第三名跌到第七名了,我觉得这不行,就更努力了些,最后高考拿了第一名。我就发现自己只要稍微一努力,还是比较容易达成自己想要的。 + +极客时间:是不是因为你学习好,比较聪明的原因,你就知道努力一定有结果的,所以能建立这种自信。 + +玉伯:可能我比较幸运吧,学习成绩确实也算自信的来源。而且当时那些老师对我还都挺照顾的。上数学课的时候,我都是一个人坐在最后面,不用听这堂课,我自己学自己的。数学老师也挺好的,我觉得老师对我的影响也挺大的,因为我的数学已经很好了,他就说那你就坐后面去,你的时间你自己去安排,有问题的时候可以去问他。他说,“我的职责一定是去帮助很多成绩不太及格的同学”。 + +老师会直接跟我说他的想法,到现在为止我都记得这句话,这对我的影响很大。你可以理解为当时这个老师就是我的 Leader,Leader 很多时候不是抓掐尖的,他的关注点是这个班级里他能够帮助到的更多的群体。这个对我其实影响挺大的,包括我现在的管理方式,有可能是当时受老师的影响。 + +所以我觉得一方面自信来源就是从小成长的经历,一直到高考前,其实都没有太经受过挫折,还是比较无忧无虑地成长,成长环境里面有一种优势心理。多说一句,现在总有人说“女孩要富养,男孩要穷养”这种理论,其实无论男孩女孩都应该富养,只有富养,一个人的性格自信才会养成。 + +后来从大学开始一直到工作,这种自信的形成,我觉得更多是发现可以依据自己的选择,决定自己的路。这是自己工作之后,自信心增强的很重要的原因。 + +有两个事情印象很深刻,我高中在县里面考第一第二都是习惯了,觉得自己很牛,考到了中科大,当时我去物理系,我们是一个系就一个班,大概 100 人左右。大一入学后,有摸底考试,考完之后我看成绩,全班 100 多人里,我是第 102 名。当时我就觉得怎么会这样?那也是第一次到大城市,其实还是挺受打击的。后来我花了将近一学期时间,很快成绩又回到了中上游水平,然后就去玩各种兴趣社团了。我发现只要自己真正做某件事情,全心投入去做,还是能赶上去的,这是大一的经历。 + +后来到了研究生,最终选择了退学。之所以敢下这个决心,也是跟大一经历有关,一直有一个很强的感知:自己做的选择不会错。有一种迷之自信,大不了再拼一把,有这种劲头在里面,包括工作后好多事情,比如开源做一些东西,都是这种劲头。这种劲头回到湖南人的性格里面叫吃得苦、霸得蛮、耐得烦。最终可能就因为自己是个湖南人吧。 + +回过头来看,自信往往源自过往的经历。每个人的经历都不一样,从而养成的内在心态也就不一样。 + +极客时间:我觉得你性格里面应该有很坚韧的部分,不容易被很多事情打击到,思考也很自由独立,好像也不太会被周边影响。 + +玉伯:可以这么说,但这有利有弊。说得好听一点是独立思考,说得难听一点是傲慢偏见。 + +回到坚持长期主义,这可能是我做事的风格,觉得一件事情既然认定了,就花一年去做,一年没有做成,不会太焦虑,会再看第二年,第二年不行再看第三年。如果花了几年时间证明确实做错了,到时再果敢放弃就好。 + +和全情投入一样,长期主义本质上是种能力,能看见未来的趋势潮流,能找到方向,同时能内心笃定,能和团队一起坚持往前,加上非常重要的运气成分,才能真正等到花开。 + +小结时刻 + +长期主义需要定力与耐心,支付宝体验技术部孵化出的很多创新产品都是坚持长期主义的果实。在玉伯的语境里,全情投入、守正出奇、愿等花开,这三个词不是割裂的,而是相互交融的。坚持长期主义也要“以正合,以奇胜”,这样做产品才能走得更长远。 + +性格坚韧者更能“愿等花开”,听完玉伯的解释,你有什么感受,欢迎在评论区发表看法,我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/12作为创新产品聚集地,体验技术部成长土壤从何来.md b/专栏/超级访谈:对话玉伯/12作为创新产品聚集地,体验技术部成长土壤从何来.md new file mode 100644 index 0000000..d47986b --- /dev/null +++ b/专栏/超级访谈:对话玉伯/12作为创新产品聚集地,体验技术部成长土壤从何来.md @@ -0,0 +1,105 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 作为创新产品聚集地,体验技术部成长土壤从何来 + 作为创新产品聚集地,体验技术部成长土壤从何来?为什么玉伯带领的这个团队能有这么多作品,他面临哪些压力,做了哪些努力,这个团队又是怎么运作的呢?接下来的两节内容,我们就来聊聊关于管理的,关于团队文化的话题。 + + + +极客时间:支付宝体验技术部包括语雀在蚂蚁应该算是比较特色的存在,而且很多人觉得语雀和蚂蚁的主营业务没有什么直接的关系,但是你们还能做成这些创新产品,一方面高层允许去做这样的业务,是一种开放,鼓励创新。另一方面,你作为Leader肯定也为团队创造了很大的成长空间。在这个过程中有没有一些别人没有看到的难点和困难,你是怎么去克服的?你是怎么给大家创造一个土壤让这个团队去发展的呢? + +玉伯:回想起来,有碰到过不少难点和困难,过程中有些经验可以分享给大家。 + +首先这个土壤不是有意为之,并不是我们要去创造土壤,然后通过一二三步,土壤就创造出来了。更多是在解决问题的过程中,自然而然出现的。体验技术部在 2013 年成立,2013 年我们就是扑到业务支撑上,在基础技术上的积累很少。到 2014 年才真正开始去做一些技术布局。布局的技术点,也是基于问题驱动的。 + +举个例子,之前我们用 YUI3 开源框架,它是面向全球用户的,其中有一个组件,就是我前面说过的 AutoComplete —— 打开一个门户型网站,在搜索框去输入关键词,会出来搜索提示框,这个搜索提示框组件就是 AutoComplete 自动完成组件。 + +这个组件很好,但对我们来说,性能有问题。当时国内网络环境一般,做性能优化需要从各方面去考虑。为了性能优化,我们仔细去看 AutoComplete 源码,发现它考虑了全球用户,在设计上支持很多语言。然而当时,淘宝只需要考虑中国用户,并不需要面向全球。这意味着对日韩语言的支持可以砍掉,还有对类似阿拉伯语自右向左的排版支持,也可以砍掉。这样从业务场景出发,就可以砍掉很多不必要的代码。 + +不断思考后,我们发现还不如自己实现一个,实现之后体积只有原来的 1/10,对当时的淘宝来说,性能会好非常多。做这些事都是问题驱动的。这个例子讲的是在淘宝阶段,在支付宝做 Ant Design 的思路也是类似的,会发现自己去实现一套 UI 组件库,性能和各方面的指标都会比 YUI3 等开源组件库更好。这就使得我们开始有机会去成立一个虚拟专项小组,把 Ant Design 作为一个部门级项目,确定下来往下走。 + +现在回过头来看,当时 2014 年这个决定就让 Ant Design 有了成长的土壤,这个土壤真正的来源是业务需求。基于问题提出解决方案,是成就土壤的第一步。 + +后续的发展,更多和长期主义相关。我比较有耐心,基本上每个产品立项,更多是以开源和长期主义的心态去做,每一件事情,都会尽量能坚持至少三到五年。 + +基于问题发现机会点,同时坚持长期主义,是为创新产品创造土壤的关键点。 + +极客时间:在做这些事的过程中,公司层面会给你什么压力吗? + +玉伯:一定是有压力的。压力无所不在,比如业务会吐槽说,业务都这么缺前端了,玉伯你怎么还让团队去搞开源,会被不理解。永远会有这种声音,一直没停过,到现在都有。 + +面对压力,重要的是要去分清楚什么是真压力,什么是假压力。如果真的某个业务线很紧急,在某个时间点就是要上线,这个时候一定要和业务绑在一起,从创新项目里把人员抽出来,优先保证紧急业务上线。 + +另外,说到这我补充一句,我总提的长期主义,一定程度上也是被逼出来的。因为我们人员一直没有太固定,很多创新产品开始时就是个虚拟小组,并没有全职投入的同学。比如参与 Ant Design、AntV 这些项目组的成员,绝大部分情况下他们都是兼职,他们本身有项目压力。有项目压力的时候要先保证项目。优先级上,默认就是业务优先的。 + +同时在团队文化里,很长一段时间,我们会一直强调“业务先赢”,最近几年的提法变成了“业务和技术要双赢”,一直以来就是业务优先的,这是大量团队的基础共识。在这种业务为先的背景下,技术的长期主义往往是被逼出来的,因为时间不可控,因为是兼职,会导致很多事情就是得以半年为维度去规划,才会有进展。你很难按正常全职方式去做 Ant Design,规定一个月要出来什么、两个月要出来什么,如果这样做的话我们可能什么也做不出来。 + +真压力都好说,无非是优先级的调整。日常更多面临的压力,是假压力。 + +假压力就是别人对你的评价,比如有人说前端不好好支持业务,天天去瞎搞开源,然而仔细跟他去聊,具体哪个项目前端没支持好时,他也说不上来。更多是对方对你有看法,而不是真的有什么问题。面对这种假压力时,搞清楚情况后,适当把耳朵堵起来就好了,不要太在乎他人的评价。 + +补充说一句,所有资源短缺问题,实际上都是优先级问题。当业务往前发展时,无论前端还是后端,包括设计师等资源,从项目排期上看永远会是短缺的。关键点在于,一堆项目里,得区分出优先级。只要业务能有优先级排序,前端就一定能抽调人力去支持好优先级高的项目。 + +带前端团队不要怕被投诉,问题上升来解决,往往对公司来说是好事情。这意味着可以一起从公司角度来重新盘下,哪些项目是最关键的,最需要提前支持的。优先级重新对焦清楚,对应调整好资源匹配,这从公司全局看是最优的。最怕的是遇到问题,不会彼此都往上上一个台阶去讨论。站高一点去看优先级,很多争吵都没必要。 + +面对假压力,要让自己有定力。很早以前我挺喜欢写文章,有人就会说玉伯居然还有时间发博客,我只能说关你屁事。别人觉得你只要在做和工作无关的事,就会觉得你没好好干。认清楚这个是假压力就好。假压力也怨不得别人,往往是自己太在乎他人评价,去做到不在乎就好。 + +极客时间:我大概能想象到你面临的这些压力,尽管有这些压力,支付宝体验技术部这个团队还是能顶住压力,去发现创新点,关键还能去验证,确实是很强的能力。你有想过为什么这个事情能发生在你的团队吗? + +玉伯:曾经思考过这个问题,现在总结起来,可以归结为三个因素。 + +第一个因素是人的因素,前端团队里有不少多面手。其他团队在创新这一块,到现在都会很羡慕我们这一点。前端是可以通过 Node.js 去写后端,但后端很少有人愿意去学前端。后端团队如果要去做创新产品,绕不过去的问题是缺前端。然而对我们来说,我们自己可以学后端,能具备全栈开发能力,但后端学前端很难做出品质感。 + +另外,体验技术部有设计师,你知道很多创新困境都是卡在缺设计、缺前端这两个点上。PD(产品经理)其实大家都不缺,因为大家都觉得自己就是 PD。在创新这块,我们有前端、设计师、Node 后端开发这三块人才,这让很多创新产品有了起步的可能。 + +第二个因素,前端团队更容易培养出 PD。很多创新产品、技术产品都是前端或者设计师当 PD,虽然很多后端技术也觉得自己能够当产品经理,但是前端和设计师当 PD,我们优势比后端更明显,因为离用户更近,我们更适合往 PD 转,我们概率比他们高很多。后端往 CTO 转更容易,我们往 CEO 转更容易。 + +第三个因素是,我们的团队文化非常鼓励创新。2016 年时,体验技术部做过一个内部创新机制,类似当年淘宝的内部赛马,我们叫“策马扬鞭”,搞过两次,不是由公司组织,而是我们部门自己去推动的。语雀、九色鹿、小钱袋等产品,都是策马扬鞭出来的。当然从上往下看,一定是这个公司是始终鼓励创新的,我们才能搞这个活动。创新文化很重要。 + +综合起来,创新产品的孵化,需要有多面手人才,需要有 PD 潜质人才,还需要有创新文化。 + +极客时间:策马扬鞭这事儿现在还在做吗? + +玉伯:没有继续了,就搞过两次。 + +极客时间:是因为九色鹿和小钱袋项目吗?你前面聊到九色鹿和小钱袋项目最终都交给了别的团队。 + +玉伯:跟这个有关系。当时 2017 年策马扬鞭出来了四五个项目,其实已经很耗精力。我们讲守正出奇,奇不能太多,如果每年都去搞的话,守正会出问题。同时当时也有一些外部的声音,大家觉得前端都不够,你们还在赛马,这种声音多到一定量级时,会对团队同学造成真压力。 + +极客时间:你已经回答了体验技术部成长土壤从何来?一方面你和团队顶住了一些压力,并且拥抱创新文化,把大家放到具体的有意义的事情里,允许尝试,坚持长期主义。这种团队文化其实就是土壤。 + +玉伯:团队给到同学一些信心、一些成长土壤是很重要的。我之前对这个话题没什么体感,现在越想越觉得重要。很多员工选择离开,跟他的直接主管是最相关的,看过一个数据,说一个员工离职,60% 的因素跟直接主管相关。 + +薪资很重要,同等重要的是好团队、好主管。我聊过不少同学,是因为喜欢团队而留下来。大多留不下来的原因,是因为万物之中没了希望,能留下来的原因,则是万物之中留有希望。一个同学,如果能看到团队里有让自己变好的可能性,他往往就会留下。变好的可能性包括成长,很多同学对个人成长的看重大于钱。 + +我们曾经做过一个问卷,问大家会因为什么原因而选择一个团队?我印象中排在第一的是个人成长,排在第二的是薪资,排在第三的是团队氛围。并没有那么多人因为钱这件事情就选择离开,但如果看不到成长的机会,往往分分钟就离开了。 + +极客时间:我很好奇,你们团队做这个问卷的背景是什么? + +玉伯:问卷这事我们有持续在做。当年我有一个 OKR 是“让同学心有笑容”,这个笑容怎么衡量?就是看同学对团队的满意度。满意度的衡量,我们通过问卷来做。比较幸运的是,第一次问卷结果,体验技术部就做到 80 几分。 + +极客时间:你觉得体验技术部为什么能够成为很多前端工程师,包括设计向往的团队?在体验设计,体验技术这块儿,支付宝体验技术部受欢迎程度数一数二,大家对这个团队评价非常高。 + +玉伯:我觉得从外界来看应该就是三个点。第一个点,这个团队做了好几个开源产品,包括 Ant Design、AntV、EggJS 等,开源产品的影响力是业界对于有好感的关键因素之一。 + +第二点,体验技术部比较喜欢对外分享,这也是一直坚持的团队文化。早期甚至会给大家设分享类 KPI,不是必须的,是加分项,做到了会考虑加分,要求同学不管是在内部还是外部都积极去做分享。这种鼓励对团队分享文化很有帮助。 + +在 QCon 等很多技术会议里,都会有我们团队同学的身影,这相当于是我们团队的一批布道师。除了技术分享,我们也鼓励设计师出去讲,我们还自己开了一个 SEEConf 支付宝体验科技大会,也是延续这种分享精神。我们常说“当我们开始分享,也许世界什么都不会变,但是我们自己已然变化”,至今我们依旧非常鼓励分享。 + +体验技术部受欢迎的第三点,我个人觉得是一直倡导的简单、自由、有爱的团队文化。我们一直推崇简单、自由、有爱,这三个词,会比公司的新六脉更受前端同学认可。 + +简单最开始强调的其实是专业,因为足够专业,才能让事情变得简单。后来我们会把简单的含义,扩充为简单直接、有话直说等简单直爽的做事风格。新六脉里会强调直言有讳,直言有讳是挺好的一个倡导,但新同学很容易对这个词很懵,直言有讳,那我具体要忌讳什么呢?在我们自己的简单文化里,会把这个“讳”换用“说话和气”来诠释,这样大家就都能理解了,会更接地气。 + +最后想说,体验技术部的受欢迎,离不开幸运成分。上面都是事后归因,可能只是相关性,未必有真正的因果性。 + +小结时刻 + +今天我们聊了一个关于团队创新土壤的话题,它一方面反映了一个团队管理者面临的压力,一方面也在观察一个管理者的努力。如何顶住来自各方的压力,为团队创造空间,如何让员工更有归属感?我所感受到的是,Leader 首先得有一颗强大且坚定的心脏,才能去分解压力,从而做出判断。 + +最后想和你讨论的是,你所在的团队被其他协作部门吐槽过么,你们如何应对?欢迎分享你的想法,我们下期再见。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/13行业内口碑第一的前端团队,如何打造文化.md b/专栏/超级访谈:对话玉伯/13行业内口碑第一的前端团队,如何打造文化.md new file mode 100644 index 0000000..38b34bb --- /dev/null +++ b/专栏/超级访谈:对话玉伯/13行业内口碑第一的前端团队,如何打造文化.md @@ -0,0 +1,99 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 行业内口碑第一的前端团队,如何打造文化 + 上一节我们提了三个关键词叫“简单、自由、有爱”,这是体验技术部的文化。为什么体验技术部会受到大家的欢迎,除了技术强之外,还有非常强的向心力,我们想了解玉伯所带领的团队是如何践行自己的文化的,这一节我们就继续听玉伯的分享吧。 + + + +极客时间:你是怎么理解团队文化的,体验技术部的文化有三个关键词嘛,简单自由有爱,可以讲讲这三个词代表的含义么? + +玉伯:谈团队文化,需要先去看一个更大的话题:团队管理。 + +我们经常会说,管理就是管人理事,可以从业务、组织和人三层去看。业务涉及使命、愿景、战略、规划等等,组织涉及招育用留、排兵布阵、梯队建设等等,业务和组织都是很大的话题,我们这里不展开聊。 + +除了业务、组织,管理还有很重要的一部分,就是人。人这一层,和文化息息相关,团队需要找到什么样的人,什么样的人在这个团队是被鼓励的等等,文化往往会涉及团队的价值观体系。 + +回到体验技术部,简单自由有爱的文化倡导,更多是帮助团队在寻找一批类似的人,会逐步成为选人育人用人的标准,长时间去坚持,团队的味道就出来了。 + + + +文化要落地,一定不能仅仅是口号。在体验技术部,伴随着简单自由有爱的,更在日常里被大家感知的,是下面的一些土话。比如“真实不装,用专业说话”强调的是简单文化,“要快,但不能急”强调的是长期主义等等。这些土话会在日常会议中被提及,慢慢就会影响大家的日常行为习惯。比如开项目评审会时,有些人不敢说话,我就会提醒“有什么不敢说的,用专业说话就好”,有些土话就会逐步变成日常场景里的一些口头禅,这样就能真实让文化落下去,变成日常点滴。 + +再举个例子。我们职级是隐藏的,但新人进来时,面对职级可能比他高的人时,会不太敢说话。但在我们团队,会不断强调不是谁职级高他就一定是对的,你在这个岗位上一定有你的专业,你得把你的观点表达出来,不需要去顾虑其他。通过这种方式引导一两次之后,一个新人会受到团队的影响,逐步变得敢说。这就是文化的力量。 + +极客时间:这让我想到,文化就在这些细节里,文化不是挂在墙上的。 + +玉伯:对,文化就是日常点滴,日常的行为举止才是文化本身。之前 Lucy(彭蕾)有一句话被传得很广:战略就是客户价值,文化就是言行举止。这句话我之前不太理解,但是后来发现我们自己在做文化的过程中还真是这么回事,文化真的就是你的言行举止。比如说作为一个 Leader,在跟同学沟通过程中,如果每次都说“你来汇报一下”,那种文化就不一样,它传递的信号是一种严明的上下级关系。我们很多时候会说,我们开个会一起讨论一下,脑海里不太会有汇报这个词。 + +举个例子。“简单文化”里还有句土话是“不要在毛坯房里雕花。这个土话的来源,是当时在做不少创新项目,设计师会去做很多精细化设计,可业务还明显处于 0 到 1 的阶段,这些精细化的设计就像在毛坯房里雕花。我说这不是折腾嘛,你设计得这么细节,花了很大精力,同时前端开发也要跟着花大量精力去实现,本来一起一周能完成的活,要因为雕花变成三周才能完成,可业务想法可能第二周就变了。该粗糙的要粗糙,不是所有阶段都要追求精细化设计的。这个背景下的心得,被总结成了“不要在毛坯房里雕花”,很多同学就记住了,同时会去思考,究竟当前产品是毛坯房,还是精装房。如果是精装房,可以好好雕花,但毛坯房没必要。 + +还有一句土话是“静水流深”,这跟长期主义、愿等花开一脉相承。所谓静水流深,很多很深的水表面上是波澜不惊,但下面流得很快。很多事情,并不需要在聚光灯下,需要的是坚持去做有价值的事情,然后到了某个点才可能会体现出大价值出来,或者才能够跟某些河流甚至大海汇合。过程中如果太着急,老想翻腾浪花,那可能就做不出来了。这些土话,都是文化。 + +再聊一句土话:“要快,但不能急”。急的是心态,强调做事的心态不能急,心态一急很多事情就会搞糟。要快,指的是要有整体规划节奏,要有条不紊、有节奏地去推进事情。在求快的整个过程中,要保持心不毛躁,心是平静的、稳的。这其实挺难,更多我们是通过这句土话,去提醒自己不要心急,这是用来反思自己心态的一个好工具。 + +这些土话,会每年迭代升级,每一年都有增加或删减。比如说你看这个图里一些标红的“土话”,像“敢说真话,说话和气”,就是前年增加的。当时因为团队里的文化太自由、太简单,大家有时候就会跟合作方怼,因此我们老被投诉,我们就感觉好像这个文化稍微有点偏离初心。怼完合作方后,我们同学是爽了,但是对方同学就会觉得玉伯团队怎么这么有个性,怎么一个个都像长了毛刺一样?那段时间觉得非常不对,于是增加了“说话和气”这条土话,一方面会继续强调要敢说真话,同时一方面强调一定要说话和气,要有爱。“说话和气”挺有意思,最早是在八路军的三大纪律八项注意里看见的,这是当年军队的一条重要注意事项,非常有意思。 + +通过土话来做文化,这是我在做团队文化时,觉得最有意思的一个点。这在很多管理书籍里面是看不到的。大家可以尝试用用,会有不一样的收获。 + +极客时间:这种标语很多公司也都会做,但也许就停留在宣扬标语上,但听你也说了很多实践案例,感觉你们是在日常中大家用实际行动去说明文化到底是什么,可以这样理解么? + +玉伯:对的,更多是靠日常的会议、讨论等各种环节里去强调的。 + +每个管理者在日常中的以身作则是关键,还有一个很重要的点是,我们每个季度会有一个叫“海阔天空”的全员大会,在这个会上,我会每次都强调下团队文化,特别是对增加或删减的土话。同时在新人圆桌等环节,也会带带货,把一些文化理解通过案例传递给新同学。 + +极客时间:我突然有这样一个感受,你们所说的文化很多时候来自团队中真实遇到的痛点,解决这些痛点的办法就是提倡的文化本身,你们把它总结出来,在各种场合去重复,最终把它沉淀为一些词,那另外,会不会有这样一个现象,因为团队同学想法太一致了,会导致看不惯别人的东西。会有这样的感受么?就像别人说过的“怎么感觉玉伯团队的人像长了毛刺一样”。 + +玉伯:这个现象也很有意思,我去研究过,这种现象叫做文化的反噬。 + +比如过于强调简单和自由,很多 Leader 或同学潜意识就会偷懒,比如遇到困难时,会说“怎么办呢?我就是这么一个简单的人”,会用文化来帮自己开脱,这就是文化的反噬。 + +意识到每个词都有两面性时,我有开始思考,究竟怎样的文化倡导是好的。后来就会有意去研究组织文化的书籍。很幸运很早年就看到了一本很好的书,叫《奈飞文化手册》,中间谈到了自由,但提法是自由与责任。这个提法,让我恍然大悟,突然就知道如何去让简单自由有爱更合理了。 + +具体做法就是,把简单、自由、有爱当成三枚硬币,硬币都有正反面。正反面的存在,共同组成了一枚硬币。因此当年仔细研究后,会把简单的背面刻上了专业两个字,自由的背面是责任,有爱的背面则是行动。具体含义也很好理解,比如简单的背后一定不是复杂,我们花了一点时间求证,最后发现专业才是简单的背面,因为足够专业才能把事情做得简单。自由的背后是责任,强调的是敬畏之心,是责任意识,特别是在支付宝,支付和金融,对责任心的要求很高。有爱的背后是行动,因为不能老谈有爱,但啥行动都没有。这也会落到一些管理的要求上,比如管理者如果想让团队同学工作更快乐一些,这个目标的达成,一定是看具体行动的。 + +通过硬币的比喻,就把原来的三个词,扩充到了互为关联的六个词。大家也很容易记住,同时看问题会变成更综合。经过这次升级后,文化的反噬就会少很多。 + +文化往往会显得很虚,哪怕转化成很多土话后,依旧很容易被看成是虚的。文化真的要落地,必须要有一些非常实在的具体的措施。 + +比如有段时间,我会要求每个 Leader 每个月要跟下面至少 5 个同学面对面沟通一次,沟通时一定要关注到同学有什么困难,关注到同学可能希望获得什么样的帮助。这个具体要求,在做的是“有爱”这条文化的落地。一个主管不能满眼只是事,同学有什么心结,遇到了什么困难,如果一个主管什么都没感知,很多时候是带不好兵的。 + +极客时间:你们一些会议、活动的命名也挺有意思的,比如海阔天空、策马扬鞭这类词。之前咱们聊到做产品内部需求评审的时候,你们还有一个叫 KK 制的流程,“KK”代表什么,需求评审怎么运作,可以展开聊聊么? + + + +玉伯:“KK 制”是语雀在用的项目管理机制,K 就是 King 的意思,K 就是小王,KK 就是大王。大王是指业务负责人、产品负责人、技术负责人和运营负责人。语雀里面分不同产品线,一条产品线会有一个 King 在负责的,他会去组织这个产品线的需求讨论和评审。在正式研发前整个流程分几个环节,叫“QT-QD-FD”。 + +第一个环节叫做 QT(QuickTalk),假如我们要做某个功能或者来自用户的某个反馈要变成需求,负责的人会先把文档(包括背景+解决方案)写好,快速讨论这个想法要不要去做。会一起确认这个需求是不是靠谱的,QT 环节有时候会来来回回好几次。 + +QT 结束之后,会有一个快速决策环节,叫做 QD(Quick Decision),需要确定这个需求究竟是否要做。这个时候除了小王之外,大王也会来参与。我们需要判断 QT 讨论出的版本,回答出两个问题:该不该做?是不是现在做?包括是否符合我们产品定位、是否真的在解当前的业务痛点,以及优先级怎么样。 + +QD 之后第三个环节是 FD(Final Decision),要做详细评审了。因为 QD 很多时候只是一篇文档,是一个非常粗的解决方案,还没有设计稿,PRD 也没写,很多地方还只是一个想法。到了 FD 的环节, PRD,设计稿都要具备的。一般 FD 一次过关的很少,也会反反复复不断探讨之后 FD 才能通过。 + +在 FD 通过之后就开始进入了常规的研发环节,研发环节之后测试上线。然后在上线之前我们有一个特色,叫做 Showtime,一个全民测试环节,把大家都调动起来,就是快要上线了大家一起来测一测。会测出一些 Bug,修完再上线。 + +极客时间:这个 King 小王一般是什么样的一个角色来承担? + +玉伯:目前来说会以产品经理为主,之前技术人员也尝试担当过,会鼓励同学自主担当。我们不太看角色分工,强调的是用专业说话,不是看谁的声调高,而是看谁说的话有道理。 + +极客时间:这几个环节里面你会主要参与哪几个? + +玉伯:我其实核心参与的是 Final Design,有时间的话会参与 Showtime。我更关注的是上线之后用户的反馈。 + +极客时间:这个 KK 制只在你们团队还是说蚂蚁都用这一套? + +玉伯:这是语雀特有的。因为公司那套流程是特别规范的,比如有 PMO 这个角色,专门做项目管理,语雀没有,所以就设置 K 来做项目管理。语雀是一个全功能型的创业团队,是完全自闭环的。从运营、产品、设计师到前后端,这些角色全在一个团队,这套规则是这个团队自己找出来的。 + +我们总得去找一套流程,最早期也去试过公司那套流程,发现不适用。比如说支付宝的项目流程里面有 CP0、CP1 的说法,就是设置 Checkpoint 检查点,这些检查点需要有业务一号位、技术一号位、质量一号位达成一致才能通过,这个流程非常重,如果套在语雀身上,并不合适。 + +后来我发现其他团队也在借鉴我们的做法,他们也开始有 FD 环节。内部有不少团队对语雀怎么从 0 到 1,怎么创业很感兴趣。我在内部做过一些分享,慢慢就会把一些做法传播出去。 + +小结时刻 + +今天我们聊了打造团队文化的话题,策马扬鞭、海阔天空、KK这些词让我印象深刻,总结起来,最重要的一句话应该就是“文化就是日常点滴”。最后想和你讨论,你们的团队文化是什么?你认可么?欢迎在评论区留言,我们下期再见。 + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/14管理能力提升:曾经影响过的书籍和启发过我的人.md b/专栏/超级访谈:对话玉伯/14管理能力提升:曾经影响过的书籍和启发过我的人.md new file mode 100644 index 0000000..f079aa2 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/14管理能力提升:曾经影响过的书籍和启发过我的人.md @@ -0,0 +1,131 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 管理能力提升:曾经影响过的书籍和启发过我的人 + 前面玉伯分享了管理者的职责,如何打造团队文化等话题,今天我们继续围绕管理话题,看看在管理这条路上,他是如何学习和实践的。 + + + +极客时间:你现在带了几个不同的团队,体验技术团队、小程序团队、语雀团队,有的是平台技术,有的就是业务技术团队,从管理上会有不同吗?会用不一样的管理办法么? + +玉伯:这三个团队都不太一样。体验技术部是综合型团队,有业务支撑部分,也有基础设施部分。小程序团队是基础技术型团队,更偏重技术本身的发展。语雀团队是内部创业型团队,各种角色会更多。 + +业务支撑型团队,要懂业务,要对业务更理解,有一个基础考核是业务支撑效能,需要把业务支撑好,保证重点项目不延期。 + +基础技术型团队,要对技术本身有认知,要热爱技术,知道技术对业务的价值在哪,同时要对技术产品化有经验,能不断带领团队做出一个个技术产品,服务好业务,能说清楚价值所在。 + +从表面看,这两种团队类型,对管理者的要求不一样,但抛开表象看底层又是一样的。比如都需要对需求做出的判断,都需要在某个领域有专业积累,这个领域专业积累,可以是某个技术领域,也可以是某个业务领域,同时还都需要管理者有规划决策能力,能看见未来,能面对各种不确定性时,做出取舍权衡。很多能力上,是相通的,最终都需要能拿出业务成绩。 + +极客时间:考量是否能拿到业务成绩,这很有意思,如果做技术的同学都这样想的话,对于职业发展方向,其实思路会更广一些。那么做业务需求的人和做更底层基础技术的人,彼此间是如何认知的,可以说说你的观察么? + +玉伯:做基础技术的同学,往往做着做着,想去做业务。做业务支撑的同学,往往做着做着,会想去做基础技术。这就是一个围城,城外的人想进去,城里的人想出来。 + +回到一线同学,这个围城带来的困惑非常常见。技术人对自身价值认知的不同,到现在为止都是一个普遍存在的问题。但这个问题很难解,靠沟通是解不掉的。体验技术部的做法是,尝试每隔半年,如果有做业务支撑的同学想去做基础技术,且也有这个能力的话,那就调整一下。反之也一样,让想做基础技术的同学,能选择去做做业务。 + +调整完成后,往往能让同学有完全不一样的感受。比如调整去做基础技术的,会刚开始很兴奋,但很快会发现,做基础技术也有一堆 bug 要改,也有各种项目排期,跟在业务线接需求是一样的。 + +这就是个围城。做基础技术的,也有很多同学觉得基础技术很难拿 3.75 绩效,基础技术很多是长周期,有同学就等不及,觉得做业务更容易拿到结果,公司很多奖项、聚光灯都是放在业务上的。每个基础技术方向,往往要做到一定阶段才会被看见,大部分情况下会在边缘,别人看不见,各有各的难处。 + +调完之后,最大的一个好处,是可以让做基础技术的和做业务支撑的同学,能彼此互相理解了,能真正感受到,原来都挺难。有些同学,能在调岗后,更善于去观察别人,同时开始更了解自己,知道自己原来更适合做业务,或者更适合做基础技术,自己的心就能安定了。心的安定,需要亲身去试试,很难通过沟通来解决。 + +极客时间:还有一个关于管理能力的问题,很多技术同学他可能不愿意做管理,或者做管理也是勉勉强强被任命推上去才去做的。你自己的体会是什么样的呢? + +玉伯:技术人应不应该去做管理,我曾经也纠结过,担心做管理后很多事情会被管理工作耗掉,导致写代码的时间减少。这时团队里一些很优秀的年轻人,天天在写代码,天天在学新东西,你可能就会恐惧自己的技术能力跟不上。这是一个很现实的问题。技术人如果转做管理,这中间必然会有所失的。 + +目前我自己的心得就是,很多时候还是要回到对自己的认知,所谓认知就是要判断自己究竟更适合写代码,还是适合做管理。但确实,很多时候一线同学第一次做管理,是被任命的,往往缺少充分沟通的过程。 + +在和一线同学沟通时,你问他要不要带个团队,他说不愿意,这样的人也有,但是极少数。像这种同学,我觉得是很难得的,他清楚自己的能力和长处。也有很多同学尝试做管理之后,发现不适合,然后再回到原来的角色,这样的情况也正常。 + +但是整体来说,当一个同学变成管理者之后,只要他真的愿意往前迈一步,是可以具备管理能力的。我觉得可能有80%的成功率。往前迈这一步,就是逼迫自己走出舒适区。这个过程中,涉及如何提升管理能力。目前大部分互联网公司都没有什么培养体系,绝大部分技术管理同学主要都是靠自学成才。 + +极客时间:你还记得参加过的培训么,你觉得参加过的培训有用吗? + +玉伯:很早时期参加过一些培训,觉得有用印象深刻的很少很少。现在还记得的,是曾经有个台湾讲师,来讲关于沟通交流的课程,核心是有个盒子模型,要沟通交流,需要双方能走出盒子(out of box)。当时我比较敢提问,老师对我印象很深,我对他印象也很深。 + +“out of box”是在讲沟通协作时,人必须打开自己,必须要有人从盒子里走出来,并且要把对方也从他的盒子里拉出来,然后再对话。我经历的培训只有这个课程让我印象深刻,而且对自己学会如何沟通对话感觉很有帮助。其他很多课程,感觉都是自己通过各种书籍里就已经知道的道理,很少有具体教会你怎么实践的。 + +极客时间:你觉得自己是自学成才类型的,那么你在管理上自学成才过程是什么样的呢? + +玉伯:有两个路径,一是看书,一是实践。我会定期去看很多管理相关的书。到目前为止,对我影响最大的一套书是德鲁克的管理学书籍,德鲁克是人文和管理大师。我最近还在重刷他的一本书,叫《卓有成效的管理者》。德鲁克对我影响很大。 + +一本上个世纪写成的书,很多观点放到现在来看,仍然感觉每天在公司里发生。这本书里提到很多管理者容易踩的坑,比如说很多公司都会做人才盘点,盘完之后都会针对每个人做一些人才改进计划,录入到系统里,或者是用一个文档存下来。但这件事,在录到系统里后,往往就没有后续了。等真正要做一些人事决策的时候,很少有管理者反过来去看当初这些人才盘点里的想法。 + +德鲁克讲了一个案例,谈到有一个公司,CEO年纪轻轻意外去世了。这位CEO很厉害,在位时制定了很多公司的政策方案,包括怎么用人,包括人才盘点等整套东西。他的接任者,是之前公司的第二号人物,比原来的 CEO 年纪要大,在公司时间更长,他从没想过自己会当一号位。所以当这个年轻CEO故去后,他还挺苦恼的,应该怎么办?他做了一个很有意思的决策,就是看之前的人才盘点,并根据人才盘点里的建议,去推动执行。他每天会花一个小时去看这些曾经的档案,看的过程中会打电话给相关 Leader,问当初人才盘点完成后,计划补充或替换某某的建议,有没有具体落地实施。相关 Leader 一接到这种电话时,开始恐慌,想着“CEO天天给我打电话,看我当年制定的策略有没有执行”。于是因为CEO这个举动,整个公司大概花了一年后焕然一新,然后他就成为这个公司最伟大的CEO。 + +这个案例讲的是一个大家都懂的道理:行动改变一切。但真正能做到的,凤毛麟角。看书的过程中,会时常汗颜,发现里面讲的很多错误自己曾经也犯过。看书是一方面,但做管理不能停留到书本上,最终要落实到行动,管理者更大的成长就在于实践。 + +比如说,最简单的管理实践之一,就是一对一沟通。这在管理上是非常重要的东西,但这个东西其实说起来很简单,就是作为一个 Leader,你得跟下面同学保持一定频率的沟通。你不沟通的话,其实永远不知道这个团队真实处于什么样的状态,或者你的感知很容易被一些传到你耳朵里的声音给蒙蔽掉,你不会知道一些真实的声音。一对一沟通我觉得是管理日常中很重要的部分。 + +另外,德鲁克《卓有成效的管理者》里面还有一点对我影响也很大,就是他在解释“成效”是什么。成效就是成果加效率。他有一个非常强的观点,就是成果永远来自外部。你在团队内部一对一沟通当然很重要,但是你如果永远做内部沟通,你就最多只能把自己的团队整理得很好,可能会提升一点效率,但是无法产生成果。 + +之前我也觉得把自己工作做好可能就好了,但是最后发现根本不是这么回事,管理者在保证内部稳定成长的情况下,更多的精力,最好80%以上的精力应该是向外看,去跟你的合作伙伴、客户、用户去交流,在这个层面上才会产生真正的成果和效益。 + +我自己在管理方面的成长,我特别感谢的就是德鲁克。 + +极客时间:刚刚说你看德鲁克的书,里面讲到的很多管理者会犯的错误你都犯过,具体指哪些,可以举个例子么? + +玉伯:好多例子。比如德鲁克说要用人所长,核心要看这个人他能不能成事,他的缺陷你并不需要去在乎,甚至你都可以不知道。这个观点让我反思自己以前的想法。 + +比如说我六七年前要去提拔一个 Leader 时,会综合考虑这个人的方方面面,最好是技术要好,又懂业务,同时人很正直、脾气好,沟通能力也强。各方面希望他是一个很好的人,当然肯定有这样的人,但是很难找,对我是个挑战。而且我发现等要给他打绩效或者是做评价时,管理者很容易去看人的短处。包括现在很多刚做管理者的同学也会犯这个错误,经常会说“你在某块做得挺好的,但是……”,这个“但是”后面就跟一句话也行,很多人“但是”后面能说出个一二三四五条,这就不是用人所长了,而是盯人所短,非常不好。这种错误,很多管理者都会犯。 + +用人所长,需要有意识地去看一个人的核心优势所在,去想某件事情让谁去做最有可能做成,以这个方式去想,能少掉很多纠结。我早期会说扬长补短,但是到后期我们只说扬长避短,甚至扬长就好了。 + +比如说有些岗位就是要有冲劲儿的,甚至就是要说话很简单直接的,而不是说有高情商沟通能力的人,那我就去找这个说话直接的人来干。也许他在沟通上会跟对方产生矛盾,但这没关系,他能把问题暴露出来,反而可能推动整件事情快速往前推进。 + +用人所长,需要懂得什么样的事情需要什么样特征的人才,同时也需要看懂每个 Leader 的特质,这样才能把合适的人放到合适的岗位上去,让大家的才华都能尽可能发挥出来。 + +极客时间:你现在和一线同学的沟通,除了一对一这种形式之外,还有什么通道吗? + +玉伯:好多。现在更多的沟通其实都是基于文档的,我会直接点赞回复,大家也习以为常。比如说直接回复同学的各种文档。这也是我们团队的一个习惯,好像因为我这么做,所以下面同学也会这么做,大家会比较习惯这种方式。有些新同学刚开始会不太习惯,感觉 Leader 回复了会很紧张,但隔上几个月,发现这就是团队的风格,很正常,也就习惯了。 + +极客时间:现在很多年轻的新同学加入一家公司,确实特别有斗志,也特别有热情,但有时候领导会觉得他某件事做得没那么好,但他自己觉得特厉害,这种情况你怎么去纠偏,你会怎么帮助他更踏实下来? + +玉伯:这个情况确实存在,团队里确实有不少同学,特别是新入职的、学校又好的同学,他可能从来没怎么经历过挫折,会对自己的要求和期待都很高。 + +经常出现的一个情况,就是这位同学会给自己打很高的绩效。比如打绩效都不是打3.75,而是直接给自己打4分。这个时候我觉得在目标设定和绩效评估这两个环节要非常严肃地跟他聊,我会很正式地约一个会议,叫上他的主管、HR,还有我,几个人一起沟通,说明白为什么绩效结果并没有达成心中的3.75甚至4。 + +对这部分同学我们定目标的时候可以拔高一些,这样的人很优秀,对自己有要求,可以让他做一些更有难度的事情,同时在绩效评估时,给到客观合理的分数。那句老话还是挺对的,叫做没有拿过3.25的人生是不完整的,当然这个话现在很少说了,因为任何人的人生都是完整的,所以这句话是一个有时代感的东西,目前很少这么说了,更多是说人要经历过一些低谷才会成长得更快。对这类同学,要求要高,同时评估要客观合理。 + +极客时间:还有一类同学,可能和上面这种不太一样,他们也很优秀,做事情也很精益求精,但他们给自己的评价没那么高,或者不敢自己争取更高的机会或者待遇,有没有遇到这种情况?对这种同学你会怎么办,怎么样激发他、鼓励他大胆地去争取自己应该有的机会和权益呢? + +玉伯:这种同学确实也有,比前面那种少一些,一般来说这种人对自我要求很高,而且谦虚到老是给自己打3.5-甚至3.25,也有这类同学。 + +对待这类同学,日常一定要给到一些肯定、鼓励,这样同学在日常就能感受到。但单通过沟通很难改变同学对自己的看法,这时更考验的是 Leader,作为 Leader 如何帮助同学提升自信,Leader 需要更主动为同学争取权益,对同学多些肯定,让同学对自己的认知慢慢能来回客观合理的水位。 + +这些情况,反映的都是人性问题。佛教里谈人性最基础的就是贪嗔痴,贪是什么?就是你想要、想得到,或者想靠近的东西。嗔是什么?就是你不想要、想远离的东西。痴是什么?并不是痴情的痴,而是白痴的痴,就是你也不知道想要啥,你也不知道自己喜欢什么,这种叫做痴。 + +在佛学里,有十二轮回图,会用动物形象去比喻贪嗔痴。贪一般是用鸽子去描述,寓意就是鸟类吃东西很贪,这也是为什么很多广场里面会提醒游客不要喂食,因为鸟会一直吃下去,会吃出问题,鸟在佛教里代表着贪。嗔一般形象是蛇,人一看到蛇就会很容易心生恐惧,想远离,这个就是嗔。痴的形象一般是用猪来表示,猪看起来很笨,成为了痴的代表。 + +回头再看管理,会发现像刚才的分析的两类人,一类是自我认知比较高,一类是自我要求很高。第一种自我认知很高的人,往往是嗔这块有问题,他可能从小被捧着长大,对不认可的东西会默默远离,他看到3.25就是不喜欢,内心里就是觉得自己很优秀应该打3.75,他看起来好像是自信满满,但实际上我觉得很多时候是在嗔的层面上没有经受过恐惧,也许他不知道怎么面对恐惧。当他真正经历过一些低谷,敢于正视内心恐惧的时候,就克服了嗔,后续就能更好地认识自己。 + +第二种是自我要求很高的人,很多时候是因为从小缺少关爱,他得到的认可是有些欠缺的,所以解他的困境,就是在日常中慢慢让他知道自己原来不是那么差,其实能力很强。但这个需要时间,而且还挺难改的。在心理学中,弗洛伊德说,人的过往经历对现在的你影响很大,这个我其实不大认同,我还是提倡阿德勒的理论,就是未来决定一个人,你对未来的一些憧憬会让你发生改变。自我要求很高的人,在管理上,可以适度激发他的“贪”并满足,让他能更自信一些。 + +对于痴的人,我不会招进来。但是痴这种情况,在工作中也经常能发现,特别是跨领域沟通时。有些团队互相吵架,就是因为互相对彼此是痴的状态,我不理解你的领域,你不理解我的领域,这个时候核心就是要打破痴的状态,去了解彼此的领域是怎么回事,把互相的痴解掉,就会少掉很多矛盾。 + +不论是做产品还是做管理,我觉得必须要“懂人性”,还是蛮关键的。我对张小龙印象深刻,是他的洞察能力很强。很早以前有一次去广州参加微信公开课,当时微信有个同学就带我进去参观,去到张小龙办公室门口,当时大概 11 点多,他还没上班,我们就在那里拍照合影。拍着拍着感觉好像有一个人在看我们,那个人就是张小龙,我们在那里摆拍时,他已经走过来了,但他看到我们在拍照,他就默默站在那里不动,等我们拍完之后才过来。这种细节里,折射的也是张小龙的懂人性。 + +懂人性这个词很容易被误解,觉得是一个贬义词。但如果你想成为一个好的管理者,想成为一个好的产品经理,对人性的洞察是非常关键不可或缺的。 + +极客时间:在管理这块,德鲁克给你影响很大。那在你身边的人呢,有没有对你在做管理、带团队、做文化这些方面产生过影响的人,或者你可以从别人身上学到的可以去借鉴的东西? + +玉伯:说起来,对我影响最大的一个人,是当时蚂蚁的CTO鲁肃的影响很大,体验技术部的成立跟鲁肃也有关系。他对我的要求就一句话:蚂蚁前端遇到任何问题,玉伯你要搞定。他给了我一种放养式的信任,或者说是托付式的信任,会让我觉得自己责任很大,从而愿意非常独立自主地去做好体验技术部。 + +鲁肃还有一个对我影响很大的点,是谦卑。鲁肃真的是一个谦谦君子,永远保持一种“空杯心态”,他的空杯能从行为上体现出来。举个例子,鲁肃还是我主管的时候,我每年的规划要向他做汇报。这个时候,鲁肃会认真在听,同时拿个本子认真在记,会跟我说,前端这块玉伯你是专业的,我不懂,玉伯你给我讲讲这块是怎么回事。非常谦卑。 + +但同时鲁肃又很专业。他的专业在于我讲了一个小时之后,他记了很多点,然后他真的在接下来半个小时能给到我实打实的建议。他会从过往经验里,通过横向类比的方式给我一些帮助,有非常具体建议,比如某个方向你要再考虑一下谁的意见,某个点要怎么去想,他能够给我一些有帮助的建议。鲁肃对人的谦卑又专业的态度,让我非常佩服。 + +在管理这条路上,我也是从点滴日常里向他学习。我曾经说话有些结巴、很紧张,后来鲁肃给我推荐了一本书,里面讲的也是管理里面很有名的一个案例:人才分两种类型,一种是善于说的,一种是善于写和读的。鲁肃会跟我说,玉伯你别紧张,你回去给我写份邮件吧,你是能表达好的。他通过这种方式引导我成长。我也是因为这个原因,一直保持着良好的写作习惯。 + +还有一个人对我影响比较大,就是行癫。我和他接触后发现,原来一个做技术的人心中,是可以如此满眼是产品。他坚定了我一边做技术一边做产品的信念。他是典型的从做技术到做产品,到带业务的人。我看到一个人做事原来可以这么投入,行癫那种热情和那种做事的决心,对我影响很大。 + +小结时刻 + +最后,做一个延伸分享,关于玉伯带领的体验技术部对管理团队的要求,通过图片方式展示给你,包含了“体验技术部管理共学手册”和“管理者的六不要”,也欢迎你在评论区交流对管理培训、管理学习方式的看法,我们下一讲再见! + + + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/15从浆手、掌舵人到兜底人,管理者进阶的三阶段.md b/专栏/超级访谈:对话玉伯/15从浆手、掌舵人到兜底人,管理者进阶的三阶段.md new file mode 100644 index 0000000..35dbe1f --- /dev/null +++ b/专栏/超级访谈:对话玉伯/15从浆手、掌舵人到兜底人,管理者进阶的三阶段.md @@ -0,0 +1,95 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 15 从浆手、掌舵人到兜底人,管理者进阶的三阶段 + 从淘宝到支付宝,从阿里到蚂蚁,从带领几个人到带领几百人,玉伯管理的团队在不断变大,他从自己的经历出发,总结了管理者进阶的三个阶段,聊了聊他对这三个阶段的理解。 + + + +极客时间:你带领的团队从十几个人到几百个人,在团队逐渐变大的过程中,对管理的要求也是不断提高的过程吧?会有什么不同? + +玉伯:团队从小变大的过程,对管理者的要求也在变化。我觉得以我的经历和经验,管理者成长可以分为三个阶段。 + +第一个阶段就是开始带一个小团队,可能七八个人或二三十人,这是第一个阶段。这个阶段作为技术管理者,最核心的能力是以身作则做表率,你得冲在前面,你做事情的方式对目标达成有直接影响,而且你往往就是主力之一。 + +这个阶段如果就开始搞管人那一套,我觉得会很难管起来,更需要做的就是身先士卒、冲在一线,跟同学们在一起。我非常建议一线 Leader 坚持写代码,一旦 Leader 不写代码,就容易脱离真实情况。 + +第一阶段要保持具象的体感,对重要的问题要能身先士卒去做。 + +极客时间:这个阶段是不是管理成分比较少,管的是哪个方面? + +玉伯:也有管理的部分,往往和事情融在一块。比如如果带的团队是做业务支撑,你得熟悉这个业务线当前重要的项目是哪几个,以及在当下遇到什么样的技术难点或人员缺失。一方面作为一线 Leader 要冲在前面,另一方面 Leader 要很清楚团队里谁的战斗力比较强,如果要调整人员,比如说要从 A 项目调整人去支撑 B 项目的话,你得知道怎么补位。 + +这个阶段的管理是在做一些非常具体同时价值非常明显的事情。技术同学刚转做管理时,这个阶段也是一个缓冲期。这个缓冲期必须要保持住写代码,甚至写代码的时间要超过 50%,同时兼顾做招聘、绩效、项目安排等管理事宜。 + +这阶段程序员的特质会更多一些。 + +管理的第二个阶段,就是你的团队可能到四五十人甚至上百人了,下面开始有二级管理者梯队。这时对管理者的挑战会快速变大。 + +其中有一个挑战,就是一下子不知道怎么做。因为看起来是往上走了一步,但实际上会感觉自己被架空了,一下子好像被拎了起来。 + +这时你会顾虑,要不要继续直接冲到一线,跟一线同学一起继续写代码。在一些重点项目中,Leader 依旧可以继续写代码。但一般这时,也容易暴露出管理问题。你很容易绕过二级管理者,直接跟他下面的一线同学去写代码,你会越级,会让你下面的 Leader 感到困惑。这中间,什么可为,什么不可为,如何把握好度,需要沟通讨论清楚。这个阶段,管理者需要去真正理解管理书籍上说的“学会授权”这四个字。“学会授权”四个字,看似简单,要做到,并不容易。 + +学会授权的管理能力上,最容易犯的两个错误,一是要么太松,一是要么太严。太严就是事无巨细,但凡下面做什么,都要向你汇报,搞得下面的 Leader 很痛苦。太松好听一点是放权,然而换言之,就是不管,是放羊式管理,长期放羊是会出问题的。 + +在松和严之间,一定要去找到适合团队的度在哪。程序员很多时候,很容易去追求确定性,会问那我到底是放权 30% 还是放权 50% ?一旦理工科思维,问出百分比时,就没法回答了,因为管理关于人性,管理是门艺术。面对不同的事情,授权的松紧是不一样的,变量很多,特别是涉及人。人往往是管理里的最大变量。拿人的性格来讲,如果下面的 Leader 性格比较开放,你紧一点他可能也不会有太多顾虑。但如果下面这个 Leader 本身比较敏感,这个时候你松一点会让他更舒服。需要根据人的不同特征,把握好授权的方式方法。管理最核心的,是把人激发出来,有干劲。 + +再谈谈管理的第三个阶段,可能带几百人近千人,这时不光有二级梯队,也有三级及以上梯队。我自己是最近几年团队变得很大,刚带五六百人时,几乎每天都会去想怎么才能带好大团队。我当时的感受是,一方面还是会有架空感,另一方面又会变得特别忙。 + +架空感来源于,大量事情,没有自己也都能落地,会觉得自己是多余的。公司里其实没有谁是不可替代的,没有你,所有事情也都能运转,项目也不太会因为你而延期,这时就会有架空感。另一方面,发现自己很忙,有很多会议,无论是兄弟团队的、业务方的,还是自己团队的,会收到很多会议邀请,日程里被排得满满的。 + +这时,作为 Leader,往往需要停下来,仔细思考,重新去寻找到自己的核心价值点在哪。 + +第三个阶段,说起来也很好玩,德鲁克又拯救了我。重读《卓有成效的管理者》,里面有个最基本的观点是:作为一个管理者,你一定要掌控自己的时间。有架空感还觉得很忙时,其实面临的问题是:我究竟应该去做什么事情才更有价值?这个事情得从目前公司的大环境出来,要从自己的核心能力出发,去结合具体团队情况,去思考得更清楚。架空感,是因为自己核心能力没有被充分用起来,没有持续产生价值,这时就容易有架空感。忙,是因为没有想清楚哪些会议是可以不参加的,是没有找到自己的关键发力点。 + +其实不仅对于管理者,对所有同学都是一样的,你唯一拥有的其实只有时间。想清楚自己的时间应该花在什么地方,并且真实去做到,这就能走出第三阶段的管理困境。 + +无论身处哪个阶段,作为管理者来说,如何更有成效,最终都是回到自己的时间应该分配在哪些事情上。我现在的做法是分两步走,第一步是基于现状理清楚我究竟应该主抓哪几件事情,在年初做规划时,确定几场大战役要打,那这几场大战役作为管理者就要盯着。当战役进度有延后的时候你要问为什么,当一个战役遇到困难时,你要为它寻找一些外部帮助、一些资源,解决掉危机。具体去做关键的事,就不会有架空感。 + +但一个 Leader 在做规划时,有个很容易犯的错误,就是没有取舍,或者说他想要照顾团队所有人的情绪。我曾看到有 Leader 在团队大概一两百人时,他下面有几个小团队,每个团队都有自己重要的事,结果他的年终规划就变成了汇总,他把每个小团队的规划汇总起来,面面俱到,尝试去照顾所有一线 Leader 的情绪,这种规划是很糟糕的。 + +学会不用照顾所有人的情绪也是一个课题。因为在一个团队里,不是所有人都能参与大的战役,总有人不在这几大战役里。最近有一首歌唱得挺好:谁说站在光里的才算英雄。很多 Leader 在早期是不太敢在规划里大胆取舍的,规划往往会做得四平八稳。四平八稳往往会导致团队看似和谐,但时间一久,就会缺少锐度,同时人员会慢慢冗余。该做的加大投入,该舍的大胆说出来,这需要 Leader 具备说出来的勇气。作为管理者,不要指望每个一线同学都理解。如果大家都理解的话,往往就是一个平庸的管理者。 + +极客时间:人的成就感要么就来自于自己做成了一件事,通过自己拿结果,要么通过别人拿结果。通过别人拿结果也就意味着你有个团队了,很多人说在这个阶段,管理者应该把注意力放在你的团队有多厉害,你要成为团队的后盾,全心全意为团队付出,这样来找到自己的价值感。这个过程其实也是一个自我心理说服的过程。你有过这样的想法或者阶段吗? + +玉伯:马云曾经分享过一个观点,叫“用人成事”,这种观点需要站在 CEO 的高度去用人,对更大管理者来说,要谨慎“用人成事”。 + +更多管理者所处的阶段,更需要的是“因事用人”。无论做组织设计还是定年度目标,一个基本出发点,还是要回到事情本身。德鲁克强调的也是因事用人,会很强调根据事情、目标去用人所长,通过这个方式把人的能量尽可能发挥出来。因事用人的过程,就是在成就他人。 + +马总所说的“用人成事”,可能是管理的第四个阶段,是更大的 Leader 思考的方式。体验技术部的日常管理中,更多还是“因事用人”。 + +极客时间:刚刚讲了管理的三阶段,有一点挺有感触的,之前我们一般讲怎么做管理都会讲管理者怎么跟这个团队去交互,怎么去管下面的人,但你其实分享了一些关于管理者心态,关注自我状态的内容,比如从程序员刚转管理,转变身份的不适感,架空感等等,这些是正常的。 + +玉伯:很多时候我们说管理就是管人理事,我觉得还缺了一个维度,就是管理自己。无论在管理的哪个阶段,有一条暗线就是,要提升自我管理能力,这个非常重要。 + +特别是到管理的第三个阶段后,自我管理能力很关键。比如每天要开很多会,都说需要你参与,这时候谈不上你控制时间,而是会议时间在控制你,你就得提升判断事情重要性的能力。 + +我有个前主管,他有个习惯,会给自己定些原则,比如说,所有要求他去做发言的会议,他一律拒绝,因为一般让他发言就给十几分钟开个场或做个总结,很无聊。他的原则是,让他去的话要最少给 40 分钟,因为只有这样,才能真正分享出有用的东西。当你在可控范围内定一些原则时,别人会知道你的原则所在,慢慢时间就会回到自己手里。 + +每次别人给我发会议邀请,我之前都是习惯性接受,但现在我学会了明确拒绝。最开始会觉得直接拒绝是不是不礼貌啊,但点过第一个拒绝之后,你会上瘾的,你会发现其实不去也没啥。我现在拒绝的会议至少占了一半。 + +作为管理者,一定要掌控好自己的时间。 + +极客时间:在管理三阶段里,每个阶段每个管理者要发挥的作用是不同的,有一个递进过程,能不能再抽象一下?每个阶段管理者应该发挥的重要作用。比如说第一个阶段管理者其实有点像一个冲锋者的角色。 + +玉伯:可以用“划龙舟”来打个比方。 + +第一个阶段的管理者,他也是划船的主力之一,甚至是最有力气的那个人,但同时不光自己划船很牛,还得跟团队的节奏匹配上,这样船才会走得比较快。第一个阶段的管理者,是桨手之一。 + +第二个阶段,管理者有点像站在船头的那个人,在把握方向。有时候甚至需要打打鼓,鼓舞士气,并且一直思考前进的路在何方。这个阶段管理者是站在船头的掌舵人,往往能看到身边船只的情况,但有时候并不太能看到全局。掌舵人的核心职能,是让自己这条船,能一往直前。 + +第三个阶段,管理者是站在船尾的人,他要看的可能不只是一个龙舟,而是好多龙舟,他可能自己划着一个小龙舟,在所有龙舟的后面。这过程中,他要去观察谁掉队了,以及究竟大家应该往什么地方去。这时指挥方向的方式,可能变成打电话给前面的龙舟,告诉怎么调整方向。管理者是站在最后面,看全局并兜底的一个孤独者。 + +简单来说,第一个阶段是参与其中,第二个阶段是指引方向,第三个阶段是提供帮助。 + +小结时刻 + +玉伯对管理三个阶段的总结更多从自己的经验出发。第一个阶段,技术管理者最核心的要求是做表率,冲在一线。第二个阶段,管理者可能有种被拎起来的感觉,这个阶段管理者要学会授权,并调整自己的心态。第三个阶段,管理者一方面觉得被架空,一方面又很忙,这个阶段做好取舍,越感觉不可控的时候越要管理自己的时间,不被裹挟,才能看好全局,做好兜底。 + +你是否同意玉伯所说?欢迎表达看法。我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/超级访谈:对话玉伯/结束语我想聊的一些与技术无关的话.md b/专栏/超级访谈:对话玉伯/结束语我想聊的一些与技术无关的话.md new file mode 100644 index 0000000..7f645e1 --- /dev/null +++ b/专栏/超级访谈:对话玉伯/结束语我想聊的一些与技术无关的话.md @@ -0,0 +1,91 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 我想聊的一些与技术无关的话 + 整个对话过程中,玉伯提到了一些他读过的书,比如《被讨厌的勇气》《卓有成效的管理者》《亚马逊逆向工作法》《大教堂与集市》《微习惯》《为什么是毛泽东》《邓小平传》《哈佛幸福公开课》《学会提问》等等,面临难题的时候,往往可以从书中寻找答案。 + +最后一次采访的话题与技术、产品、管理无关,而是聊了聊他的个人兴趣,为什么对佛学和哲学感兴趣,以及对人生意义的思考,内心的追求、心中的自留地。 + +以下为玉伯的自述: + +你好,我是玉伯。 + +今天想跟你聊聊有关个人兴趣的私人话题。我为什么对佛学、哲学感兴趣,一个原因是学物理的人特别容易空虚,另一个原因是我现在做产品、做管理,总说要懂人性。当我在管理中遇到问题,去书中求解时,很多管理学书籍都会导向心理学和哲学。 + +哲学学习之旅 + +哲学的书籍我平常看得很杂,不太成体系,如果你想做基础了解,推荐你看《西方哲学史》。最近让我触动挺大的是康德的书《纯粹理性批判》,特别难懂,初看这个书名以为他要批判什么,读完发现原来我连书名都误解了,他所说的批判更多是对“纯粹理性”的考察和思辨。 + +在接触哲学的过程中,我发现很好玩的是,发现哲学和科学、宗教有着千丝万缕的联系。哲学发展过程中有一条线是: + + +从古希腊亚里士多德时代的朴素唯物主义,发展到理性唯物主义;再到以贝克莱等人为代表的唯心主义;然后再到以康德为代表的理性唯心主义,最后才是我们熟知的马克思、恩格斯的辩证唯物主义。 + + +朴素唯物主义就是我见即我得,看见的就是真实存在的。但很快会发现,我们看见的,只是物体的影像,并不是物体本身,只是大家在心里默默将物体和影像划等号了。理性唯物主义的一个认知,就是指出我们看到的是影像,同时认为影像等于实物。这两个阶段都比较好理解。 + +再往下就到了理性唯心主义。有个很牛的哲学家,叫贝克莱。他提出,人看见的是影像,而影像不是实物,无法证明影像是实物,那么我看见的其实只是心里感受到的,也就是“我心即世界”。贝克莱甚至说,我看到的跟你看到的可能不一样,大家以为看到的东西一样,只是语言沟通的结果。贝克莱的言论,很快遭到科学界、哲学界一堆人的挑战:既然你说“我心即世界”,那为什么鱼不能在天上飞、鸟不能在水里游?如果“我心即世界”的话,应该说每个人看到的都不一样,理论上有些鱼会在天上飞,有些鸟会在水里游,为什么所有人哪怕通过语言沟通,最后都是说鸟在天上飞,鱼在水里游,这是怎么回事呢?所以“我心即世界”是不对的。当时哲学家们的讨论思辨很有意思,充满了思考的乐趣。 + +真正把这个问题解决掉的是康德,康德有一个假说:实物跟影像其实跟认知有关系。康德开始研究人的认知,把人的认知分成:感性、理性、知性。 + +感性一定程度上跟佛学里面的六根六入是一样的,眼﹑耳﹑鼻﹑舌﹑身﹑意,五官感受就是感知。如果把人比喻成一个电脑,感知就是输入。造物主就是这么造的,我们能够接触到的外界的输入只有这六个通道。这叫感性。 + +理性是什么?理性就是人类特有的,比如说人类能总结出勾股定律,康德觉得只有人类才会从万物里总结出规律,且把规律用一个纯理性的方式提炼表达出来。我们观察狮子捕猎时,它们不知道勾三股四弦五,但是狮子知道捕猎时斜线是最短路径,动物有本能,有感知能力,但是动物没有理性,总结不出数学公式。是人类独有的理性,成就了数学、科学。数学并不是科学,数学就是一套游戏。数学的底层是一些基本假设,基于这些假设,纯理性推导出整个体系,一些科学定律也是纯理性。康德会很强调理性和感性是分开的,是有着巨大不同的。 + +康德最大的创新,是提出人有知性。知性是什么呢?康德提出一个概念叫“先验统觉”。就是每个人的知性都来自个体的遗传。按照康德的说法,上帝就是这么创造你的,怎么来的你别问,每个人都有一套先验统觉的东西。这个先验统觉按我们现在的话,就如一个笔记本电脑,它的操作系统就是先验统觉。感性就是用感知给你输入,理性就是你这个 OS 还有一些产生智慧的能力,相当于算法或模型。 + +康德的观点是,人不是一张白纸,因为有先验统觉,所以我看见的鱼跟你看见的鱼是一样的,这是操作系统决定的。然后你想再去问 WHY,请不要问,康德不会回答这个问题,他解决问题的方法是先划定范围,对我们可知的领域进行论证。这叫理性唯心主义,或者叫先验唯心主义。 + +研究过哲学这一条线后,再来对照心理学。心理学我看过三个人的著作:弗洛伊德、阿德勒、荣格。弗洛伊德的经典著作是《梦的解析》,阿德勒最近些年比较火,《被讨厌的勇气》总结了阿德勒的思想,很符合当前这个时代。 + +我最近在看荣格,他有本书叫《The Red Book》(《红书》),看着像圣经一样。我在研究康德的时候发现这里面有关联性,甚至从荣格心理学里面可以找到康德说的“先验统觉”是什么东西。荣格的核心理念是“人是群体的产物”。弗洛伊德说人是过去决定的,阿德勒说人是由未来决定的,荣格的观点是人不是由过去决定的,也不是由未来决定的,人是由人类这个物种决定的,或者说人是由群体环境决定的。 + +在荣格的语境里有一个东西,叫做集体潜意识。我当时看的时候就觉得,这不就是先验统觉嘛。很多时候群体或者这个社会在往前发展,甚至说不同种族、不同国家产生冲突,我们以为不受影响,但实际上只要你生在中国你就是中国人。按集体潜意识说法,你的基因就是中国人的。 + +在看这些书时,觉得挺有意思的,会发现不同学科之间,点可以串成线、连成面,这个过程很好玩。最近重读张小龙的《微信背后的产品观》,第一篇就讲到“人是环境的反应器”,我认为整本书最重要的就是这一句话。这句话其实就是来自先验统觉和集体潜意识的思想。 + +研究哲学就是研究人本身,很多对人生的体验,都会被哲学串起来。 + +我对人生意义的理解 + +我是物理专业的,学物理的人一般特别容易空虚。为什么这么说呢?从科学的发展历史来看,会有一个很有趣的说法,休谟把科学的根基给挖了,康德则给科学封上了天花板。 + +所谓休谟挖了科学的根基,是因为科学背后都是基本假设。比如说光速不可超越,比如说解析几何里的两点之间直线最短,这些都是公理,公理就是假设。这些基本假设怎么来的?无论是数学还是科学,最基本的方法只有归纳法和演绎法。但休谟说,哪怕发生了一万次都是对的,但是第一万零一次可能是错的,所以归纳法就是不靠谱的。而演绎的基础是归纳,所以只要把归纳法一否定,就等于把整个科学大厦的根基给动摇了。 + +再说康德,康德除了提出“先验统觉”很厉害之外,他还提出一个不可知论,康德一方面在反对贝克莱,一方面也认可贝克莱。康德非常认可影像和实物是两件事,但这中间是无法证明的,这就是不可知论。因为不可知论的存在,意味着科学无论多么发达,以我们渺小人类的大脑是永远无法认知宇宙的真理或者宇宙全貌的。康德通过不可知论,给科学扣上了天花板。 + +在研究这些理论的过程中,我发现无论是科学、数学还是社会学,最终都会基于一些假设出发,假设是一切的起点。不同民族或者不同文化之间的冲突,往往也是因为各自的基础假设不一样。数学领域有基本假设,物理学里也有基本假设。回到社会学领域,要回答人活着的意义,首先得研究人类社会的基本假设是什么呢? + +我深信一个词叫意义共同体,当我们问人生的意义在哪,就要先回答意义是什么,只要有一群人认为一件事有意义,它就有意义,意义就在于寻找共同体的过程中。 + +比如说回到中华民族,要去研究中华民族底层假设是什么。我在看一些书,目前理解还非常粗浅。比如说费孝通的《乡土中国》,他对华夏民族的民族性是有研究的。他研究的是历史,但是现在我们在快速往前发展,可能民族的秉性、根性也在发生变化,我觉得这些根性的东西跟刚才说的基本假设有很大关系,这些根性里面,内植了一个群体的意义追求。我们活在当下,但意义往往是被历史中的群体的根性所决定的。 + +我有跟大学里的一些老师交流,然后我发现我现在研究的这些东西,在学术派里是习以为常的,我以为发现了新大陆,原来只是人家不屑于讨论的话题。学术派现在在讨论啥呢?比如说北大哲学系有个教授他目前研究的话题,叫做《中国传统文化对现代社会的影响》。他们的研究我觉得有点偏科学,科学其实本身不追求真理,科学追求实用。社会学里,会进一步思考,到底应该去研究一个什么样的课题,反过来能对这个社会产生影响,或者是引起新的讨论或变革? + +曾和大学一位老师探讨,期待中国是不是有机会出现新的文艺复兴。如果会出现,那是会在江浙出现,还是在北京出现,会在什么地方呢?如果出现文艺复兴,该怎么去点火,怎么去推动其发生。我非常喜欢这样的话题讨论,我也挺期待有生之年,能见到新的文艺复兴出现。 + +中国很多伟大思想家或者是哲学家都诞生在两种年代,一个是兵荒马乱的年代,比如春秋战国,还有一种年代是和平了一段时间,开始遇到困顿的时候。我觉得目前整个世界有点处于群体精神困顿的时代,在这个阶段是有可能出现文艺复兴的。 + +我们看中国近代史,去看新中国成立的艰难,毛泽东、周恩来他们就是生活在一个兵荒马乱的时代。周恩来有句名言:“为中华之崛起而读书”。现在我们看到这句话,可能没什么体感,觉得很空很大,但在当年,为中华之崛起真的是周恩来、毛泽东他们天天想的事情。我们现在呢? + +文艺复兴是重新燃起整个社会的意义感,这个意义肯定也是多元化的,无论是老年人、中年人、年轻一代,不同的人群,会有不同的意义感。 + +语雀的意义共同体 + +最后围绕意义共同体我还想聊一个话题,是关于语雀的,我很少对外说。语雀长远畅想的,是构建多元化的意义共同体。语雀现在的数字花园概念,是指在线上,大家可以把自己所思所想沉淀成知识库,能够展现给别人看,这过程中有朋友认可,这是一种最简单的意义共同体。 + +比如说公众号,也是建立最小意义共同体的通道。比如说像脉脉工作圈里的匿名吐槽,这也是一个小的意义共同体。 + +回到语雀,当下聚焦做线上工具,尝试通过数字花园,让大家能够构建属于自己的意义共同体。更长远来看,可能 5-10 年以后,语雀真正想做的,可能是从线上能走向线下的意义共同体。现在互联网上的戾气很重,粉丝这个词感觉都是对用户的嘲讽。语雀真正想做的,是让人与人之间有更良好的互动,能彼此构建意义共同体。 + +这可能真的会改变一个人的生活,改变人生轨迹。我喜欢创作,喜欢古典文学,一定程度上是因为在读大学的时候加入过两个社团,一个是古典文学社,还有一个是红楼梦社团。包括现在到蚂蚁,仍然有参加过一些社团,我内心感觉到社团对一个人的影响是很大的。哪怕这个社团只是你人生一个阶段,可能只是一段时间你参与过,但实际上在这个环境里交的一些朋友,你们之间一些探讨的话题,已经在改变你。 + +中国社会如果有一些很好的线下意义共同体产品,能够让人与人的关系发生改变,那也许是一件非常有意义的事情。 + +到这里,整个专栏就正式结束了,和你说声再见。感谢你坚持到现在,也欢迎你继续参与讨论,共同进步。 + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/00开篇词想成为技术牛人?先搞定网络协议!.md b/专栏/趣谈网络协议/00开篇词想成为技术牛人?先搞定网络协议!.md new file mode 100644 index 0000000..effd37a --- /dev/null +++ b/专栏/趣谈网络协议/00开篇词想成为技术牛人?先搞定网络协议!.md @@ -0,0 +1,57 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 开篇词 想成为技术牛人?先搞定网络协议! + 你好,我是刘超,网易研究院云计算技术部的首席架构师。我主要负责两部分工作,对内支撑网易核心业务上云,对外帮助客户搞定容器化与微服务化架构。 + +当极客时间约我做“趣谈网络协议”专栏的时候,我非常开心,因为网络协议也是我长期研究和关注的点。摸爬滚打15年,有了一些收获也溅了一身血,我才能在这里和你分享。 + +为什么网络协议这么重要呢?为什么“计算机组成与系统结构”“数据结构与算法”“操作系统”“计算机网络”“编译原理”,会成为大学计算机的核心课程呢?至少看起来,这些内容没有“多少天搞定MFC、Structs”这样的内容更容易帮你找到工作。我毕业的时候,也感到很困惑。 + +不过当时我抱着一个理想,也可能是大多数程序员的理想:我要做技术牛人,我要搞定大系统。 + +工作15年,我在EMC做过类似GFS的分布式存储开发,做过基于Lucene的搜索引擎,做过Hadoop的运维;在HP和华为做过OpenStack的开发、实施和解决方案;还创业倒腾过Mesos容器平台,后来在网易做Kubernetes。 + +随着见过的世面越来越多,我渐渐发现,无论是对于大规模系统的架构,还是对于程序员的个人职业生涯,网络和网络协议都是绕不过去的坎儿。 + +集群规模一大,我们首先想到的就是网络互通的问题;应用吞吐量压不上去,我们首先想到的也是网络互通的问题。不客气地讲,很多情况下,只要搞定了网络,一个大型系统也就搞定了一半。所以,要成为技术牛人,搞定大系统,一定要过网络这一关,而网络协议在网络中占有举足轻重的地位。 + +相信大部分人都思考过“技术变化太快,容易过时”的问题。毕竟,技术浪潮一浪接一浪,新技术层出不穷。从搜索引擎、大数据、云计算,到人工智能、区块链,简直就是“你方唱罢我登场”。这里面究竟有没有最本质的东西,使得你掌握了它,就能在新技术的滚滚浪潮中,保持快速学习的能力? + +通过对大量开源技术的代码进行分析,我发现很多技术看起来轰轰烈烈,扒下外衣,本质的东西其实就是基础知识和核心概念。想要不被滚滚而来的新技术淘汰,就要掌握这些可以长久使用的知识,而网络协议就是值得你学习,而且是到40岁之后依然有价值的知识。 + +但是,要想真正学习和掌握网络协议,也并非易事。下面这些场景,你是不是也感同身受呢? + + +网络协议知识点太多,学完记不住。我们都学过计算机网络课程,学的时候感觉并不难。尤其这门课没有公式,更像是文科。学了一大堆,也背了一大堆,应付完考试之后,最终都“还给老师”了。 + +看上去懂了,但是经不住问。没关系,网上有很多的文章嘛。于是,你会搜索很多文章去看。看的时候,你感觉别人说的很有道理,好像理解了,但是经不住问,一问就发现,你只是了解了大概的流程,很多细节还是不知道。所以说,从能看懂到能给别人讲明白,中间还有很长一段距离。 + +知识学会了,实际应用依旧不会。细节都摸索得差不多了,但是当你自己去应用和调试的时候,发现还是没有思路。比如,当创建出来的虚拟机不能上网的时候,该怎么办呢?学过的东西,怎么还是不会用? + + +我把这样的网络协议学习过程总结为:一看觉得懂,一问就打鼓,一用就糊涂。 + +那网络协议究竟该怎么学?基于这个问题,我决定从以下三个角度和你分享我所理解的网络协议。 + +第一,我会从身边经常见到的事情出发,用故事来讲解各种网络协议,然后慢慢扩展到不熟悉的领域。 + +例如,每个人都会查看IP地址,那我们就从这个命令开始,展开一些概念;很多人都在大学宿舍组过简单的网络来打游戏,我就从宿舍里最简单的网络概念开始讲;然后说到办公室,说到日常上网、购物、视频下载等过程涉及的协议;最后说到最陌生的数据中心。 + +第二,我会用贴近场景的方式来讲解网络协议,将各个层次的关系串起来,而非孤立地讲解某个概念。 + +常见的计算机网络课程往往会按照网络分层,一层一层地讲,却很少讲层与层之间的关系。例如,我们学习路由协议的时候,在真实场景中,这么多的算法和二层是什么关系呢?和四层又是什么关系呢?例如,在真实的网络通信中,我们访问一个网站,做一个支付,在TCP进行三次握手的时候,IP层在干嘛?MAC层又在干嘛?这些你是不是都清楚? + +第三,我会在讲解完各个层次的网络协议之后,着重剖析如何在当下热门领域使用这些协议,比如云计算、容器和微服务。 + +一方面你可以知道网络协议真实应用的地方,另一方面你也可以通过上手使用云计算、容器、微服务来进一步加深对于协议的理解。 + +千里之行,始于足下。不管何时,我相信,扎实的功底和过硬的技术,都会是你职业发展的助力器。 + +希望这个专栏,不仅可以帮你理清繁杂的网络协议概念,帮你构建一个精准的网络协议知识框架,帮你在热门领域应用这些底层知识,更重要的是给你一种学习知识的方法和态度:看似最枯燥、最基础的东西往往具有最长久的生命力。 + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/01为什么要学习网络协议?.md b/专栏/趣谈网络协议/01为什么要学习网络协议?.md new file mode 100644 index 0000000..8201778 --- /dev/null +++ b/专栏/趣谈网络协议/01为什么要学习网络协议?.md @@ -0,0 +1,156 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 为什么要学习网络协议? + 《圣经》中有一个通天塔的故事,大致是说,上帝为了阻止人类联合起来,就让人类说不同的语言。人类没法儿沟通,达不成“协议”,通天塔的计划就失败了。 + +但是千年以后,有一种叫“程序猿”的物种,敲着一种这个群体通用的语言,连接着全世界所有的人,打造这互联网世界的通天塔。如今的世界,正是因为互联网,才连接在一起。 + +当”Hello World!“从显示器打印出来的时候,还记得你激动的心情吗? + +public class HelloWorld { + public static void main(String[] args){ + System.out.println("Hello World!"); + } +} + + +如果你是程序员,一定看得懂上面这一段文字。这是每一个程序员向计算机世界说“你好,世界”的方式。但是,你不一定知道,这段文字也是一种协议,是人类和计算机沟通的协议,只有通过这种协议,计算机才知道我们想让它做什么。 + +协议三要素 + +当然,这种协议还是更接近人类语言,机器不能直接读懂,需要进行翻译,翻译的工作教给编译器,也就是程序员常说的compile。这个过程比较复杂,其中的编译原理非常复杂,我在这里不进行详述。 + + + +但是可以看得出,计算机语言作为程序员控制一台计算机工作的协议,具备了协议的三要素。 + + +语法,就是这一段内容要符合一定的规则和格式。例如,括号要成对,结束要使用分号等。 + +语义,就是这一段内容要代表某种意义。例如数字减去数字是有意义的,数字减去文本一般来说就没有意义。 + +顺序,就是先干啥,后干啥。例如,可以先加上某个数值,然后再减去某个数值。 + + +会了计算机语言,你就能够教给一台计算机完成你的工作了。恭喜你,入门了! + +但是,要想打造互联网世界的通天塔,只教给一台机器做什么是不够的,你需要学会教给一大片机器做什么。这就需要网络协议。只有通过网络协议,才能使一大片机器互相协作、共同完成一件事。 + +这个时候,你可能会问,网络协议长啥样,这么神奇,能干成啥事?我先拿一个简单的例子,让你尝尝鲜,然后再讲一个大事。 + +当你想要买一个商品,常规的做法就是打开浏览器,输入购物网站的地址。浏览器就会给你显示一个缤纷多彩的页面。 + +那你有没有深入思考过,浏览器是如何做到这件事情的?它之所以能够显示缤纷多彩的页面,是因为它收到了一段来自HTTP协议的“东西”。我拿网易考拉来举例,格式就像下面这样: + +HTTP/1.1 200 OK +Date: Tue, 27 Mar 2018 16:50:26 GMT +Content-Type: text/html;charset=UTF-8 +Content-Language: zh-CN + + + + + + 网易考拉3周年主会场 + + +这符合协议的三要素吗?我带你来看一下。 + +首先,符合语法,也就是说,只有按照上面那个格式来,浏览器才认。例如,上来是状态,然后是首部,然后是内容。 + +第二,符合语义,就是要按照约定的意思来。例如,状态200,表述的意思是网页成功返回。如果不成功,就是我们常见的“404”。 + +第三,符合顺序,你一点浏览器,就是发送出一个HTTP请求,然后才有上面那一串HTTP返回的东西。 + +浏览器显然按照协议商定好的做了,最后一个五彩缤纷的页面就出现在你面前了。 + +我们常用的网络协议有哪些? + +接下来揭秘我要说的大事情,“双十一”。这和我们要讲的网络协议有什么关系呢? + +在经济学领域,有个伦纳德·里德(Leonard E. Read)创作的《铅笔的故事》。这个故事通过一个铅笔的诞生过程,来讲述复杂的经济学理论。这里,我也用一个下单的过程,看看互联网世界的运行过程中,都使用了哪些网络协议。 + +你先在浏览器里面输入 https://www.kaola.com ,这是一个URL。浏览器只知道名字是“www.kaola.com”,但是不知道具体的地点,所以不知道应该如何访问。于是,它打开地址簿去查找。可以使用一般的地址簿协议DNS去查找,还可以使用另一种更加精准的地址簿查找协议HTTPDNS。 + +无论用哪一种方法查找,最终都会得到这个地址:106.114.138.24。这个是IP地址,是互联网世界的“门牌号”。 + +知道了目标地址,浏览器就开始打包它的请求。对于普通的浏览请求,往往会使用HTTP协议;但是对于购物的请求,往往需要进行加密传输,因而会使用HTTPS协议。无论是什么协议,里面都会写明“你要买什么和买多少”。 + + + +DNS、HTTP、HTTPS所在的层我们称为应用层。经过应用层封装后,浏览器会将应用层的包交给下一层去完成,通过socket编程来实现。下一层是传输层。传输层有两种协议,一种是无连接的协议UDP,一种是面向连接的协议TCP。对于支付来讲,往往使用TCP协议。所谓的面向连接就是,TCP会保证这个包能够到达目的地。如果不能到达,就会重新发送,直至到达。 + +TCP协议里面会有两个端口,一个是浏览器监听的端口,一个是电商的服务器监听的端口。操作系统往往通过端口来判断,它得到的包应该给哪个进程。 + + + +传输层封装完毕后,浏览器会将包交给操作系统的网络层。网络层的协议是IP协议。在IP协议里面会有源IP地址,即浏览器所在机器的IP地址和目标IP地址,也即电商网站所在服务器的IP地址。 + + + +操作系统既然知道了目标IP地址,就开始想如何根据这个门牌号找到目标机器。操作系统往往会判断,这个目标IP地址是本地人,还是外地人。如果是本地人,从门牌号就能看出来,但是显然电商网站不在本地,而在遥远的地方。 + +操作系统知道要离开本地去远方。虽然不知道远方在何处,但是可以这样类比一下:如果去国外要去海关,去外地就要去网关。而操作系统启动的时候,就会被DHCP协议配置IP地址,以及默认的网关的IP地址192.168.1.1。 + +操作系统如何将IP地址发给网关呢?在本地通信基本靠吼,于是操作系统大吼一声,谁是192.168.1.1啊?网关会回答它,我就是,我的本地地址在村东头。这个本地地址就是MAC地址,而大吼的那一声是ARP协议。 + + + +于是操作系统将IP包交给了下一层,也就是MAC层。网卡再将包发出去。由于这个包里面是有MAC地址的,因而它能够到达网关。 + +网关收到包之后,会根据自己的知识,判断下一步应该怎么走。网关往往是一个路由器,到某个IP地址应该怎么走,这个叫作路由表。 + +路由器有点像玄奘西行路过的一个个国家的一个个城关。每个城关都连着两个国家,每个国家相当于一个局域网,在每个国家内部,都可以使用本地的地址MAC进行通信。 + +一旦跨越城关,就需要拿出IP头来,里面写着贫僧来自东土大唐(就是源IP地址),欲往西天拜佛求经(指的是目标IP地址)。路过宝地,借宿一晚,明日启程,请问接下来该怎么走啊? + + + +城关往往是知道这些“知识”的,因为城关和临近的城关也会经常沟通。到哪里应该怎么走,这种沟通的协议称为路由协议,常用的有OSPF和BGP。 + + + +城关与城关之间是一个国家,当网络包知道了下一步去哪个城关,还是要使用国家内部的MAC地址,通过下一个城关的MAC地址,找到下一个城关,然后再问下一步的路怎么走,一直到走出最后一个城关。 + +最后一个城关知道这个网络包要去的地方。于是,对着这个国家吼一声,谁是目标IP啊?目标服务器就会回复一个MAC地址。网络包过关后,通过这个MAC地址就能找到目标服务器。 + +目标服务器发现MAC地址对上了,取下MAC头来,发送给操作系统的网络层。发现IP也对上了,就取下IP头。IP头里会写上一层封装的是TCP协议,然后将其交给传输层,即TCP层。 + +在这一层里,对于收到的每个包,都会有一个回复的包说明收到了。这个回复的包绝非这次下单请求的结果,例如购物是否成功,扣了多少钱等,而仅仅是TCP层的一个说明,即收到之后的回复。当然这个回复,会沿着刚才来的方向走回去,报个平安。 + +因为一旦出了国门,西行路上千难万险,如果在这个过程中,网络包走丢了,例如进了大沙漠,或者被强盗抢劫杀害怎么办呢?因而到了要报个平安。 + +如果过一段时间还是没到,发送端的TCP层会重新发送这个包,还是上面的过程,直到有一天收到平安到达的回复。这个重试绝非你的浏览器重新将下单这个动作重新请求一次。对于浏览器来讲,就发送了一次下单请求,TCP层不断自己闷头重试。除非TCP这一层出了问题,例如连接断了,才轮到浏览器的应用层重新发送下单请求。 + +当网络包平安到达TCP层之后,TCP头中有目标端口号,通过这个端口号,可以找到电商网站的进程正在监听这个端口号,假设一个Tomcat,将这个包发给电商网站。 + + + +电商网站的进程得到HTTP请求的内容,知道了要买东西,买多少。往往一个电商网站最初接待请求的这个Tomcat只是个接待员,负责统筹处理这个请求,而不是所有的事情都自己做。例如,这个接待员要告诉专门管理订单的进程,登记要买某个商品,买多少,要告诉管理库存的进程,库存要减少多少,要告诉支付的进程,应该付多少钱,等等。 + +如何告诉相关的进程呢?往往通过RPC调用,即远程过程调用的方式来实现。远程过程调用就是当告诉管理订单进程的时候,接待员不用关心中间的网络互连问题,会由RPC框架统一处理。RPC框架有很多种,有基于HTTP协议放在HTTP的报文里面的,有直接封装在TCP报文里面的。 + +当接待员发现相应的部门都处理完毕,就回复一个HTTPS的包,告知下单成功。这个HTTPS的包,会像来的时候一样,经过千难万险到达你的个人电脑,最终进入浏览器,显示支付成功。 + +小结 + +看到了吧,一个简简单单的下单过程,中间牵扯到这么多的协议。而管理一大片机器,更是一件特别有技术含量的事情。除此之外,像最近比较火的云计算、容器、微服务等技术,也都需要借助各种协议,来达成大规模机器之间的合作。 + +我在这里列一下之后要讲的网络协议,之后我会按照从底层到上层的顺序来讲述。 + + + +上面的“双十一”故事只是为了给你一个大致的框架,这里面有些协议,我在故事里已经提到了,有些还没有提到。在这门课的最后一章,当所有的协议都讲过之后,我会再重新讲一遍这个故事,到时候你就能明白更多的细节。 + +最后,学完了这一节,给你留一个问题吧。 + +当网络包到达一个城关的时候,可以通过路由表得到下一个城关的IP地址,直接通过IP地址找就可以了,为什么还要通过本地的MAC地址呢? + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/02网络分层的真实含义是什么?.md b/专栏/趣谈网络协议/02网络分层的真实含义是什么?.md new file mode 100644 index 0000000..3650af1 --- /dev/null +++ b/专栏/趣谈网络协议/02网络分层的真实含义是什么?.md @@ -0,0 +1,111 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 网络分层的真实含义是什么? + 长时间从事计算机网络相关的工作,我发现,计算机网络有一个显著的特点,就是这是一个不仅需要背诵,而且特别需要将原理烂熟于胸的学科。很多问题看起来懂了,但是就怕往细里问,一问就发现你懂得没有那么透彻。 + +我们上一节列了之后要讲的网络协议。这些协议本来没什么稀奇,每一本教科书都会讲,并且都要求你背下来。因为考试会考,面试会问。可以这么说,毕业了去找工作还答不出这类题目的,那你的笔试基本上也就挂了。 + +当你听到什么二层设备、三层设备、四层LB和七层LB中层的时候,是否有点一头雾水,不知道这些所谓的层,对应的各种协议具体要做什么“工作”? + +这四个问题你真的懂了吗? + +因为教科书或者老师往往会打一个十分不恰当的比喻:为什么网络要分层呀?因为不同的层次之间有不同的沟通方式,这个叫作协议。例如,一家公司也是分“层次”的,分总经理、经理、组长、员工。总经理之间有他们的沟通方式,经理和经理之间也有沟通方式,同理组长和员工。有没有听过类似的比喻? + +那么第一个问题来了。请问经理在握手的时候,员工在干什么?很多人听过TCP建立连接的三次握手协议,也会把它当知识点背诵。同理问你,TCP在进行三次握手的时候,IP层和MAC层对应都有什么操作呢? + +除了上面这个不恰当的比喻,教科书还会列出每个层次所包含的协议,然后开始逐层地去讲这些协议。但是这些协议之间的关系呢?却很少有教科书会讲。 + +学习第三层的时候会提到,IP协议里面包含目标地址和源地址。第三层里往往还会学习路由协议。路由就像中转站,我们从原始地址A到目标地址D,中间经过两个中转站A->B->C->D,是通过路由转发的。 + +那么第二个问题来了。A知道自己的下一个中转站是B,那从A发出来的包,应该把B的IP地址放在哪里呢?B知道自己的下一个中转站是C,从B发出来的包,应该把C的IP地址放在哪里呢?如果放在IP协议中的目标地址,那包到了中转站,怎么知道最终的目的地址是D呢? + +教科书不会通过场景化的例子,将网络包的生命周期讲出来,所以你就会很困惑,不知道这些协议实际的应用场景是什么。 + +我再问你一个问题。你一定经常听说二层设备、三层设备。二层设备处理的通常是MAC层的东西。那我发送一个HTTP的包,是在第七层工作的,那是不是不需要经过二层设备?或者即便经过了,二层设备也不处理呢?或者换一种问法,二层设备处理的包里,有没有HTTP层的内容呢? + +最终,我想问你一个综合的问题。从你的电脑,通过SSH登录到公有云主机里面,都需要经历哪些过程?或者说你打开一个电商网站,都需要经历哪些过程?说得越详细越好。 + +实际情况可能是,很多人回答不上来。尽管对每一层都很熟悉,但是知识点却串不起来。 + +上面的这些问题,有的在这一节就会有一个解释,有的则会贯穿我们整个课程。好在后面一节中我会举一个贯穿的例子,将很多层的细节讲过后,你很容易就能把这些知识点串起来。 + +网络为什么要分层? + +这里我们先探讨第一个问题,网络为什么要分层?因为,是个复杂的程序都要分层。 + +理解计算机网络中的概念,一个很好的角度是,想象网络包就是一段Buffer,或者一块内存,是有格式的。同时,想象自己是一个处理网络包的程序,而且这个程序可以跑在电脑上,可以跑在服务器上,可以跑在交换机上,也可以跑在路由器上。你想象自己有很多的网口,从某个口拿进一个网络包来,用自己的程序处理一下,再从另一个网口发送出去。 + +当然网络包的格式很复杂,这个程序也很复杂。复杂的程序都要分层,这是程序设计的要求。比如,复杂的电商还会分数据库层、缓存层、Compose层、Controller层和接入层,每一层专注做本层的事情。 + +程序是如何工作的? + +我们可以简单地想象“你”这个程序的工作过程。 + + + +当一个网络包从一个网口经过的时候,你看到了,首先先看看要不要请进来,处理一把。有的网口配置了混杂模式,凡是经过的,全部拿进来。 + +拿进来以后,就要交给一段程序来处理。于是,你调用process_layer2(buffer)。当然,这是一个假的函数。但是你明白其中的意思,知道肯定是有这么个函数的。那这个函数是干什么的呢?从Buffer中,摘掉二层的头,看一看,应该根据头里面的内容做什么操作。 + +假设你发现这个包的MAC地址和你的相符,那说明就是发给你的,于是需要调用process_layer3(buffer)。这个时候,Buffer里面往往就没有二层的头了,因为已经在上一个函数的处理过程中拿掉了,或者将开始的偏移量移动了一下。在这个函数里面,摘掉三层的头,看看到底是发送给自己的,还是希望自己转发出去的。 + +如何判断呢?如果IP地址不是自己的,那就应该转发出去;如果IP地址是自己的,那就是发给自己的。根据IP头里面的标示,拿掉三层的头,进行下一层的处理,到底是调用process_tcp(buffer)呢,还是调用process_udp(buffer)呢? + +假设这个地址是TCP的,则会调用process_tcp(buffer)。这时候,Buffer里面没有三层的头,就需要查看四层的头,看这是一个发起,还是一个应答,又或者是一个正常的数据包,然后分别由不同的逻辑进行处理。如果是发起或者应答,接下来可能要发送一个回复包;如果是一个正常的数据包,就需要交给上层了。交给谁呢?是不是有process_http(buffer)函数呢? + +没有的,如果你是一个网络包处理程序,你不需要有process_http(buffer),而是应该交给应用去处理。交给哪个应用呢?在四层的头里面有端口号,不同的应用监听不同的端口号。如果发现浏览器应用在监听这个端口,那你发给浏览器就行了。至于浏览器怎么处理,和你没有关系。 + +浏览器自然是解析HTML,显示出页面来。电脑的主人看到页面很开心,就点了鼠标。点击鼠标的动作被浏览器捕获。浏览器知道,又要发起另一个HTTP请求了,于是使用端口号,将请求发给了你。 + +你应该调用send_tcp(buffer)。不用说,Buffer里面就是HTTP请求的内容。这个函数里面加一个TCP的头,记录下源端口号。浏览器会给你目的端口号,一般为80端口。 + +然后调用send_layer3(buffer)。Buffer里面已经有了HTTP的头和内容,以及TCP的头。在这个函数里面加一个IP的头,记录下源IP的地址和目标IP的地址。 + +然后调用send_layer2(buffer)。Buffer里面已经有了HTTP的头和内容、TCP的头,以及IP的头。这个函数里面要加一下MAC的头,记录下源MAC地址,得到的就是本机器的MAC地址和目标的MAC地址。不过,这个还要看当前知道不知道,知道就直接加上;不知道的话,就要通过一定的协议处理过程,找到MAC地址。反正要填一个,不能空着。 + +万事俱备,只要Buffer里面的内容完整,就可以从网口发出去了,你作为一个程序的任务就算告一段落了。 + +揭秘层与层之间的关系 + +知道了这个过程之后,我们再来看一下原来困惑的问题。 + +首先是分层的比喻。所有不能表示出层层封装含义的比喻,都是不恰当的。总经理握手,不需要员工在吧,总经理之间谈什么,不需要员工参与吧,但是网络世界不是这样的。正确的应该是,总经理之间沟通的时候,经理将总经理放在自己兜里,然后组长把经理放自己兜里,员工把组长放自己兜里,像套娃娃一样。那员工直接沟通,不带上总经理,就不恰当了。 + +现实生活中,往往是员工说一句,组长补充两句,然后经理补充两句,最后总经理再补充两句。但是在网络世界,应该是总经理说话,经理补充两句,组长补充两句,员工再补充两句。 + +那TCP在三次握手的时候,IP层和MAC层在做什么呢?当然是TCP发送每一个消息,都会带着IP层和MAC层了。因为,TCP每发送一个消息,IP层和MAC层的所有机制都要运行一遍。而你只看到TCP三次握手了,其实,IP层和MAC层为此也忙活好久了。 + +这里要记住一点:只要是在网络上跑的包,都是完整的。可以有下层没上层,绝对不可能有上层没下层。 + +所以,对TCP协议来说,三次握手也好,重试也好,只要想发出去包,就要有IP层和MAC层,不然是发不出去的。 + +经常有人会问这样一个问题,我都知道那台机器的IP地址了,直接发给他消息呗,要MAC地址干啥?这里的关键就是,没有MAC地址消息是发不出去的。 + +所以如果一个HTTP协议的包跑在网络上,它一定是完整的。无论这个包经过哪些设备,它都是完整的。 + +所谓的二层设备、三层设备,都是这些设备上跑的程序不同而已。一个HTTP协议的包经过一个二层设备,二层设备收进去的是整个网络包。这里面HTTP、TCP、 IP、 MAC都有。什么叫二层设备呀,就是只把MAC头摘下来,看看到底是丢弃、转发,还是自己留着。那什么叫三层设备呢?就是把MAC头摘下来之后,再把IP头摘下来,看看到底是丢弃、转发,还是自己留着。 + +小结 + +总结一下今天的内容,理解网络协议的工作模式,有两个小窍门: + + +始终想象自己是一个处理网络包的程序:如何拿到网络包,如何根据规则进行处理,如何发出去; +始终牢记一个原则:只要是在网络上跑的包,都是完整的。可以有下层没上层,绝对不可能有上层没下层。 + + +最后,给你留两个思考题吧。 + + +如果你也觉得总经理和员工的比喻不恰当,你有更恰当的比喻吗? +要想学习网络协议,IP这个概念是最最基本的,那你知道如何查看IP地址吗? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/03ifconfig:最熟悉又陌生的命令行.md b/专栏/趣谈网络协议/03ifconfig:最熟悉又陌生的命令行.md new file mode 100644 index 0000000..39694a3 --- /dev/null +++ b/专栏/趣谈网络协议/03ifconfig:最熟悉又陌生的命令行.md @@ -0,0 +1,155 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 ifconfig:最熟悉又陌生的命令行 + 上一节结尾给你留的一个思考题是,你知道怎么查看IP地址吗? + +当面试听到这个问题的时候,面试者常常会觉得走错了房间。我面试的是技术岗位啊,怎么问这么简单的问题? + +的确,即便没有专业学过计算机的人,只要倒腾过电脑,重装过系统,大多也会知道这个问题的答案:在Windows上是ipconfig,在Linux上是ifconfig。 + +那你知道在Linux上还有什么其他命令可以查看IP地址吗?答案是ip addr。如果回答不上来这个问题,那你可能没怎么用过Linux。 + +那你知道ifconfig和ip addr的区别吗?这是一个有关net-tools和iproute2的“历史”故事,你刚来到第三节,暂时不用了解这么细,但这也是一个常考的知识点。 + +想象一下,你登录进入一个被裁剪过的非常小的Linux系统中,发现既没有ifconfig命令,也没有ip addr命令,你是不是感觉这个系统压根儿没法用?这个时候,你可以自行安装net-tools和iproute2这两个工具。当然,大多数时候这两个命令是系统自带的。 + +安装好后,我们来运行一下ip addr。不出意外,应该会输出下面的内容。 + +root@test:~# ip addr +1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 + link/ether fa:16:3e:c7:79:75 brd ff:ff:ff:ff:ff:ff + inet 10.100.122.2/24 brd 10.100.122.255 scope global eth0 + valid_lft forever preferred_lft forever + inet6 fe80::f816:3eff:fec7:7975/64 scope link + valid_lft forever preferred_lft forever + + +这个命令显示了这台机器上所有的网卡。大部分的网卡都会有一个IP地址,当然,这不是必须的。在后面的分享中,我们会遇到没有IP地址的情况。 + +IP地址是一个网卡在网络世界的通讯地址,相当于我们现实世界的门牌号码。既然是门牌号码,不能大家都一样,不然就会起冲突。比方说,假如大家都叫六单元1001号,那快递就找不到地方了。所以,有时候咱们的电脑弹出网络地址冲突,出现上不去网的情况,多半是IP地址冲突了。 + +如上输出的结果,10.100.122.2就是一个IP地址。这个地址被点分隔为四个部分,每个部分8个bit,所以IP地址总共是32位。这样产生的IP地址的数量很快就不够用了。因为当时设计IP地址的时候,哪知道今天会有这么多的计算机啊!因为不够用,于是就有了IPv6,也就是上面输出结果里面inet6 fe80::f816:3eff:fec7:7975/64。这个有128位,现在看来是够了,但是未来的事情谁知道呢? + +本来32位的IP地址就不够,还被分成了5类。现在想想,当时分配地址的时候,真是太奢侈了。 + + + +在网络地址中,至少在当时设计的时候,对于A、B、 C类主要分两部分,前面一部分是网络号,后面一部分是主机号。这很好理解,大家都是六单元1001号,我是小区A的六单元1001号,而你是小区B的六单元1001号。 + +下面这个表格,详细地展示了A、B、C三类地址所能包含的主机的数量。在后文中,我也会多次借助这个表格来讲解。 + + + +这里面有个尴尬的事情,就是C类地址能包含的最大主机数量实在太少了,只有254个。当时设计的时候恐怕没想到,现在估计一个网吧都不够用吧。而B类地址能包含的最大主机数量又太多了。6万多台机器放在一个网络下面,一般的企业基本达不到这个规模,闲着的地址就是浪费。 + +无类型域间选路(CIDR) + +于是有了一个折中的方式叫作无类型域间选路,简称CIDR。这种方式打破了原来设计的几类地址的做法,将32位的IP地址一分为二,前面是网络号,后面是主机号。从哪里分呢?你如果注意观察的话可以看到,10.100.122.2/24,这个IP地址中有一个斜杠,斜杠后面有个数字24。这种地址表示形式,就是CIDR。后面24的意思是,32位中,前24位是网络号,后8位是主机号。 + +伴随着CIDR存在的,一个是广播地址,10.100.122.255。如果发送这个地址,所有10.100.122网络里面的机器都可以收到。另一个是子网掩码,255.255.255.0。 + +将子网掩码和IP地址进行AND计算。前面三个255,转成二进制都是1。1和任何数值取AND,都是原来数值,因而前三个数不变,为10.100.122。后面一个0,转换成二进制是0,0和任何数值取AND,都是0,因而最后一个数变为0,合起来就是10.100.122.0。这就是网络号。将子网掩码和IP地址按位计算AND,就可得到网络号。 + +公有IP地址和私有IP地址 + +在日常的工作中,几乎不用划分A类、B类或者C类,所以时间长了,很多人就忘记了这个分类,而只记得CIDR。但是有一点还是要注意的,就是公有IP地址和私有IP地址。 + + + +我们继续看上面的表格。表格最右列是私有IP地址段。平时我们看到的数据中心里,办公室、家里或学校的IP地址,一般都是私有IP地址段。因为这些地址允许组织内部的IT人员自己管理、自己分配,而且可以重复。因此,你学校的某个私有IP地址段和我学校的可以是一样的。 + +这就像每个小区有自己的楼编号和门牌号,你们小区可以叫6栋,我们小区也叫6栋,没有任何问题。但是一旦出了小区,就需要使用公有IP地址。就像人民路888号,是国家统一分配的,不能两个小区都叫人民路888号。 + +公有IP地址有个组织统一分配,你需要去买。如果你搭建一个网站,给你学校的人使用,让你们学校的IT人员给你一个IP地址就行。但是假如你要做一个类似网易163这样的网站,就需要有公有IP地址,这样全世界的人才能访问。 + +表格中的192.168.0.x是最常用的私有IP地址。你家里有Wi-Fi,对应就会有一个IP地址。一般你家里地上网设备不会超过256个,所以/24基本就够了。有时候我们也能见到/16的CIDR,这两种是最常见的,也是最容易理解的。 + +不需要将十进制转换为二进制32位,就能明显看出192.168.0是网络号,后面是主机号。而整个网络里面的第一个地址192.168.0.1,往往就是你这个私有网络的出口地址。例如,你家里的电脑连接Wi-Fi,Wi-Fi路由器的地址就是192.168.0.1,而192.168.0.255就是广播地址。一旦发送这个地址,整个192.168.0网络里面的所有机器都能收到。 + +但是也不总都是这样的情况。因此,其他情况往往就会很难理解,还容易出错。 + +举例:一个容易“犯错”的CIDR + +我们来看16.158.165.91/22这个CIDR。求一下这个网络的第一个地址、子网掩码和广播地址。 + +你要是上来就写16.158.165.1,那就大错特错了。 + +/22不是8的整数倍,不好办,只能先变成二进制来看。16.158的部分不会动,它占了前16位。中间的165,变为二进制为‭10100101‬。除了前面的16位,还剩6位。所以,这8位中前6位是网络号,16.158.,而.91是机器号。 + +第一个地址是16.158..1,即16.158.164.1。子网掩码是255.255..0,即255.255.252.0。广播地址为16.158..255,即16.158.167.255。 + +这五类地址中,还有一类D类是组播地址。使用这一类地址,属于某个组的机器都能收到。这有点类似在公司里面大家都加入了一个邮件组。发送邮件,加入这个组的都能收到。组播地址在后面讲述VXLAN协议的时候会提到。 + +讲了这么多,才讲了上面的输出结果中很小的一部分,是不是觉得原来并没有真的理解ip addr呢?我们接着来分析。 + +在IP地址的后面有个scope,对于eth0这张网卡来讲,是global,说明这张网卡是可以对外的,可以接收来自各个地方的包。对于lo来讲,是host,说明这张网卡仅仅可以供本机相互通信。 + +lo全称是loopback,又称环回接口,往往会被分配到127.0.0.1这个地址。这个地址用于本机通信,经过内核处理后直接返回,不会在任何网络中出现。 + +MAC地址 + +在IP地址的上一行是link/ether fa:16:3e:c7:79:75 brd ff:ff:ff:ff:ff:ff,这个被称为MAC地址,是一个网卡的物理地址,用十六进制,6个byte表示。 + +MAC地址是一个很容易让人“误解”的地址。因为MAC地址号称全局唯一,不会有两个网卡有相同的MAC地址,而且网卡自生产出来,就带着这个地址。很多人看到这里就会想,既然这样,整个互联网的通信,全部用MAC地址好了,只要知道了对方的MAC地址,就可以把信息传过去。 + +这样当然是不行的。 一个网络包要从一个地方传到另一个地方,除了要有确定的地址,还需要有定位功能。 而有门牌号码属性的IP地址,才是有远程定位功能的。 + +例如,你去杭州市网商路599号B楼6层找刘超,你在路上问路,可能被问的人不知道B楼是哪个,但是可以给你指网商路怎么去。但是如果你问一个人,你知道这个身份证号的人在哪里吗?可想而知,没有人知道。 + +MAC地址更像是身份证,是一个唯一的标识。它的唯一性设计是为了组网的时候,不同的网卡放在一个网络里面的时候,可以不用担心冲突。从硬件角度,保证不同的网卡有不同的标识。 + +MAC地址是有一定定位功能的,只不过范围非常有限。你可以根据IP地址,找到杭州市网商路599号B楼6层,但是依然找不到我,你就可以靠吼了,大声喊身份证XXXX的是哪位?我听到了,我就会站起来说,是我啊。但是如果你在上海,到处喊身份证XXXX的是哪位,我不在现场,当然不会回答,因为我在杭州不在上海。 + +所以,MAC地址的通信范围比较小,局限在一个子网里面。例如,从192.168.0.2/24访问192.168.0.3/24是可以用MAC地址的。一旦跨子网,即从192.168.0.2/24到192.168.1.2/24,MAC地址就不行了,需要IP地址起作用了。 + +网络设备的状态标识 + +解析完了MAC地址,我们再来看 是干什么的?这个叫做net_device flags,网络设备的状态标识。 + +UP表示网卡处于启动的状态;BROADCAST表示这个网卡有广播地址,可以发送广播包;MULTICAST表示网卡可以发送多播包;LOWER_UP表示L1是启动的,也即网线插着呢。MTU1500是指什么意思呢?是哪一层的概念呢?最大传输单元MTU为1500,这是以太网的默认值。 + +上一节,我们讲过网络包是层层封装的。MTU是二层MAC层的概念。MAC层有MAC的头,以太网规定正文部分不允许超过1500个字节。正文里面有IP的头、TCP的头、HTTP的头。如果放不下,就需要分片来传输。 + +qdisc pfifo_fast是什么意思呢?qdisc全称是queueing discipline,中文叫排队规则。内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的qdisc(排队规则)把数据包加入队列。 + +最简单的qdisc是pfifo,它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。pfifo_fast稍微复杂一些,它的队列包括三个波段(band)。在每个波段里面,使用先进先出规则。 + +三个波段(band)的优先级也不相同。band 0的优先级最高,band 2的最低。如果band 0里面有数据包,系统就不会处理band 1里面的数据包,band 1和band 2之间也是一样。 + +数据包是按照服务类型(Type of Service,TOS)被分配到三个波段(band)里面的。TOS是IP头里面的一个字段,代表了当前的包是高优先级的,还是低优先级的。 + +队列是个好东西,后面我们讲云计算中的网络的时候,会有很多用户共享一个网络出口的情况,这个时候如何排队,每个队列有多粗,队列处理速度应该怎么提升,我都会详细为你讲解。 + +小结 + +怎么样,看起来很简单的一个命令,里面学问很大吧?通过这一节,希望你能记住以下的知识点,后面都能用得上: + + +IP是地址,有定位功能;MAC是身份证,无定位功能; + +CIDR可以用来判断是不是本地人; + +IP分公有的IP和私有的IP。后面的章节中我会谈到“出国门”,就与这个有关。 + + +最后,给你留两个思考题。 + + +你知道net-tools和iproute2的“历史”故事吗? +这一节讲的是如何查看IP地址,那你知道IP地址是怎么来的吗? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/04DHCP与PXE:IP是怎么来的,又是怎么没的?.md b/专栏/趣谈网络协议/04DHCP与PXE:IP是怎么来的,又是怎么没的?.md new file mode 100644 index 0000000..e65bbcf --- /dev/null +++ b/专栏/趣谈网络协议/04DHCP与PXE:IP是怎么来的,又是怎么没的?.md @@ -0,0 +1,181 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 DHCP与PXE:IP是怎么来的,又是怎么没的? + 上一节,我们讲了IP的一些基本概念。如果需要和其他机器通讯,我们就需要一个通讯地址,我们需要给网卡配置这么一个地址。 + +如何配置IP地址? + +那如何配置呢?如果有相关的知识和积累,你可以用命令行自己配置一个地址。可以使用ifconfig,也可以使用ip addr。设置好了以后,用这两个命令,将网卡up一下,就可以开始工作了。 + +使用net-tools: + +$ sudo ifconfig eth1 10.0.0.1/24 +$ sudo ifconfig eth1 up + + +使用iproute2: + +$ sudo ip addr add 10.0.0.1/24 dev eth1 +$ sudo ip link set up eth1 + + +你可能会问了,自己配置这个自由度太大了吧,我是不是配置什么都可以?如果配置一个和谁都不搭边的地址呢?例如,旁边的机器都是192.168.1.x,我非得配置一个16.158.23.6,会出现什么现象呢? + +不会出现任何现象,就是包发不出去呗。为什么发不出去呢?我来举例说明。 + +192.168.1.6就在你这台机器的旁边,甚至是在同一个交换机上,而你把机器的地址设为了16.158.23.6。在这台机器上,你企图去ping192.168.1.6,你觉得只要将包发出去,同一个交换机的另一台机器马上就能收到,对不对? + +可是Linux系统不是这样的,它没你想的那么智能。你用肉眼看到那台机器就在旁边,它则需要根据自己的逻辑进行处理。 + +还记得我们在第二节说过的原则吗?只要是在网络上跑的包,都是完整的,可以有下层没上层,绝对不可能有上层没下层。 + +所以,你看着它有自己的源IP地址16.158.23.6,也有目标IP地址192.168.1.6,但是包发不出去,这是因为MAC层还没填。 + +自己的MAC地址自己知道,这个容易。但是目标MAC填什么呢?是不是填192.168.1.6这台机器的MAC地址呢? + +当然不是。Linux首先会判断,要去的这个地址和我是一个网段的吗,或者和我的一个网卡是同一网段的吗?只有是一个网段的,它才会发送ARP请求,获取MAC地址。如果发现不是呢? + +Linux默认的逻辑是,如果这是一个跨网段的调用,它便不会直接将包发送到网络上,而是企图将包发送到网关。 + +如果你配置了网关的话,Linux会获取网关的MAC地址,然后将包发出去。对于192.168.1.6这台机器来讲,虽然路过它家门的这个包,目标IP是它,但是无奈MAC地址不是它的,所以它的网卡是不会把包收进去的。 + +如果没有配置网关呢?那包压根就发不出去。 + +如果将网关配置为192.168.1.6呢?不可能,Linux不会让你配置成功的,因为网关要和当前的网络至少一个网卡是同一个网段的,怎么可能16.158.23.6的网关是192.168.1.6呢? + +所以,当你需要手动配置一台机器的网络IP时,一定要好好问问你的网络管理员。如果在机房里面,要去网络管理员那里申请,让他给你分配一段正确的IP地址。当然,真正配置的时候,一定不是直接用命令配置的,而是放在一个配置文件里面。不同系统的配置文件格式不同,但是无非就是CIDR、子网掩码、广播地址和网关地址。 + +动态主机配置协议(DHCP) + +原来配置IP有这么多门道儿啊。你可能会问了,配置了IP之后一般不能变的,配置一个服务端的机器还可以,但是如果是客户端的机器呢?我抱着一台笔记本电脑在公司里走来走去,或者白天来晚上走,每次使用都要配置IP地址,那可怎么办?还有人事、行政等非技术人员,如果公司所有的电脑都需要IT人员配置,肯定忙不过来啊。 + +因此,我们需要有一个自动配置的协议,也就是动态主机配置协议(Dynamic Host Configuration Protocol),简称DHCP。 + +有了这个协议,网络管理员就轻松多了。他只需要配置一段共享的IP地址。每一台新接入的机器都通过DHCP协议,来这个共享的IP地址里申请,然后自动配置好就可以了。等人走了,或者用完了,还回去,这样其他的机器也能用。 + +所以说,如果是数据中心里面的服务器,IP一旦配置好,基本不会变,这就相当于买房自己装修。DHCP的方式就相当于租房。你不用装修,都是帮你配置好的。你暂时用一下,用完退租就可以了。 + +解析DHCP的工作方式 + +当一台机器新加入一个网络的时候,肯定一脸懵,啥情况都不知道,只知道自己的MAC地址。怎么办?先吼一句,我来啦,有人吗?这时候的沟通基本靠“吼”。这一步,我们称为DHCP Discover。 + +新来的机器使用IP地址0.0.0.0发送了一个广播包,目的IP地址为255.255.255.255。广播包封装了UDP,UDP封装了BOOTP。其实DHCP是BOOTP的增强版,但是如果你去抓包的话,很可能看到的名称还是BOOTP协议。 + +在这个广播包里面,新人大声喊:我是新来的(Boot request),我的MAC地址是这个,我还没有IP,谁能给租给我个IP地址! + +格式就像这样: + + + +如果一个网络管理员在网络里面配置了DHCP Server的话,他就相当于这些IP的管理员。他立刻能知道来了一个“新人”。这个时候,我们可以体会MAC地址唯一的重要性了。当一台机器带着自己的MAC地址加入一个网络的时候,MAC是它唯一的身份,如果连这个都重复了,就没办法配置了。 + +只有MAC唯一,IP管理员才能知道这是一个新人,需要租给它一个IP地址,这个过程我们称为DHCP Offer。同时,DHCP Server为此客户保留为它提供的IP地址,从而不会为其他DHCP客户分配此IP地址。 + +DHCP Offer的格式就像这样,里面有给新人分配的地址。 + + + +DHCP Server仍然使用广播地址作为目的地址,因为,此时请求分配IP的新人还没有自己的IP。DHCP Server回复说,我分配了一个可用的IP给你,你看如何?除此之外,服务器还发送了子网掩码、网关和IP地址租用期等信息。 + +新来的机器很开心,它的“吼”得到了回复,并且有人愿意租给它一个IP地址了,这意味着它可以在网络上立足了。当然更令人开心的是,如果有多个DHCP Server,这台新机器会收到多个IP地址,简直受宠若惊。 + +它会选择其中一个DHCP Offer,一般是最先到达的那个,并且会向网络发送一个DHCP Request广播数据包,包中包含客户端的MAC地址、接受的租约中的IP地址、提供此租约的DHCP服务器地址等,并告诉所有DHCP Server它将接受哪一台服务器提供的IP地址,告诉其他DHCP服务器,谢谢你们的接纳,并请求撤销它们提供的IP地址,以便提供给下一个IP租用请求者。 + + + +此时,由于还没有得到DHCP Server的最后确认,客户端仍然使用0.0.0.0为源IP地址、255.255.255.255为目标地址进行广播。在BOOTP里面,接受某个DHCP Server的分配的IP。 + +当DHCP Server接收到客户机的DHCP request之后,会广播返回给客户机一个DHCP ACK消息包,表明已经接受客户机的选择,并将这一IP地址的合法租用信息和其他的配置信息都放入该广播包,发给客户机,欢迎它加入网络大家庭。 + + + +最终租约达成的时候,还是需要广播一下,让大家都知道。 + +IP地址的收回和续租 + +既然是租房子,就是有租期的。租期到了,管理员就要将IP收回。 + +如果不用的话,收回就收回了。就像你租房子一样,如果还要续租的话,不能到了时间再续租,而是要提前一段时间给房东说。DHCP也是这样。 + +客户机会在租期过去50%的时候,直接向为其提供IP地址的DHCP Server发送DHCP request消息包。客户机接收到该服务器回应的DHCP ACK消息包,会根据包中所提供的新的租期以及其他已经更新的TCP/IP参数,更新自己的配置。这样,IP租用更新就完成了。 + +好了,一切看起来完美。DHCP协议大部分人都知道,但是其实里面隐藏着一个细节,很多人可能不会去注意。接下来,我就讲一个有意思的事情:网络管理员不仅能自动分配IP地址,还能帮你自动安装操作系统! + +预启动执行环境(PXE) + +普通的笔记本电脑,一般不会有这种需求。因为你拿到电脑时,就已经有操作系统了,即便你自己重装操作系统,也不是很麻烦的事情。但是,在数据中心里就不一样了。数据中心里面的管理员可能一下子就拿到几百台空的机器,一个个安装操作系统,会累死的。 + +所以管理员希望的不仅仅是自动分配IP地址,还要自动安装系统。装好系统之后自动分配IP地址,直接启动就能用了,这样当然最好了! + +这事儿其实仔细一想,还是挺有难度的。安装操作系统,应该有个光盘吧。数据中心里不能用光盘吧,想了一个办法就是,可以将光盘里面要安装的操作系统放在一个服务器上,让客户端去下载。但是客户端放在哪里呢?它怎么知道去哪个服务器上下载呢?客户端总得安装在一个操作系统上呀,可是这个客户端本来就是用来安装操作系统的呀? + +其实,这个过程和操作系统启动的过程有点儿像。首先,启动BIOS。这是一个特别小的小系统,只能干特别小的一件事情。其实就是读取硬盘的MBR启动扇区,将GRUB启动起来;然后将权力交给GRUB,GRUB加载内核、加载作为根文件系统的initramfs文件;然后将权力交给内核;最后内核启动,初始化整个操作系统。 + +那我们安装操作系统的过程,只能插在BIOS启动之后了。因为没安装系统之前,连启动扇区都没有。因而这个过程叫做预启动执行环境(Pre-boot Execution Environment),简称PXE。 + +PXE协议分为客户端和服务器端,由于还没有操作系统,只能先把客户端放在BIOS里面。当计算机启动时,BIOS把PXE客户端调入内存里面,就可以连接到服务端做一些操作了。 + +首先,PXE客户端自己也需要有个IP地址。因为PXE的客户端启动起来,就可以发送一个DHCP的请求,让DHCP Server给它分配一个地址。PXE客户端有了自己的地址,那它怎么知道PXE服务器在哪里呢?对于其他的协议,都好办,要有人告诉他。例如,告诉浏览器要访问的IP地址,或者在配置中告诉它;例如,微服务之间的相互调用。 + +但是PXE客户端启动的时候,啥都没有。好在DHCP Server除了分配IP地址以外,还可以做一些其他的事情。这里有一个DHCP Server的一个样例配置: + +ddns-update-style interim; +ignore client-updates; +allow booting; +allow bootp; +subnet 192.168.1.0 netmask 255.255.255.0 +{ +option routers 192.168.1.1; +option subnet-mask 255.255.255.0; +option time-offset -18000; +default-lease-time 21600; +max-lease-time 43200; +range dynamic-bootp 192.168.1.240 192.168.1.250; +filename "pxelinux.0"; +next-server 192.168.1.180; +} + + +按照上面的原理,默认的DHCP Server是需要配置的,无非是我们配置IP的时候所需要的IP地址段、子网掩码、网关地址、租期等。如果想使用PXE,则需要配置next-server,指向PXE服务器的地址,另外要配置初始启动文件filename。 + +这样PXE客户端启动之后,发送DHCP请求之后,除了能得到一个IP地址,还可以知道PXE服务器在哪里,也可以知道如何从PXE服务器上下载某个文件,去初始化操作系统。 + +解析PXE的工作过程 + +接下来我们来详细看一下PXE的工作过程。 + +首先,启动PXE客户端。第一步是通过DHCP协议告诉DHCP Server,我刚来,一穷二白,啥都没有。DHCP Server便租给它一个IP地址,同时也给它PXE服务器的地址、启动文件pxelinux.0。 + +其次,PXE客户端知道要去PXE服务器下载这个文件后,就可以初始化机器。于是便开始下载,下载的时候使用的是TFTP协议。所以PXE服务器上,往往还需要有一个TFTP服务器。PXE客户端向TFTP服务器请求下载这个文件,TFTP服务器说好啊,于是就将这个文件传给它。 + +然后,PXE客户端收到这个文件后,就开始执行这个文件。这个文件会指示PXE客户端,向TFTP服务器请求计算机的配置信息pxelinux.cfg。TFTP服务器会给PXE客户端一个配置文件,里面会说内核在哪里、initramfs在哪里。PXE客户端会请求这些文件。 + +最后,启动Linux内核。一旦启动了操作系统,以后就啥都好办了。 + + + +小结 + +好了,这一节就到这里了。我来总结一下今天的内容: + + +DHCP协议主要是用来给客户租用IP地址,和房产中介很像,要商谈、签约、续租,广播还不能“抢单”; + +DHCP协议能给客户推荐“装修队”PXE,能够安装操作系统,这个在云计算领域大有用处。 + + +最后,学完了这一节,给你留两个思考题吧。 + + +PXE协议可以用来安装操作系统,但是如果每次重启都安装操作系统,就会很麻烦。你知道如何使得第一次安装操作系统,后面就正常启动吗? +现在上网很简单了,买个家用路由器,连上WIFI,给DHCP分配一个IP地址,就可以上网了。那你是否用过更原始的方法自己组过简单的网呢?说来听听。 + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/05从物理层到MAC层:如何在宿舍里自己组网玩联机游戏?.md b/专栏/趣谈网络协议/05从物理层到MAC层:如何在宿舍里自己组网玩联机游戏?.md new file mode 100644 index 0000000..f8a2bda --- /dev/null +++ b/专栏/趣谈网络协议/05从物理层到MAC层:如何在宿舍里自己组网玩联机游戏?.md @@ -0,0 +1,130 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 从物理层到MAC层:如何在宿舍里自己组网玩联机游戏? + 上一节,我们见证了IP地址的诞生,或者说是整个操作系统的诞生。一旦机器有了IP,就可以在网络的环境里和其他的机器展开沟通了。 + +故事就从我的大学宿舍开始讲起吧。作为一个八零后,我要暴露年龄了。 + +我们宿舍四个人,大一的时候学校不让上网,不给开通网络。但是,宿舍有一个人比较有钱,率先买了一台电脑。那买了电脑干什么呢? + +首先,有单机游戏可以打,比如说《拳皇》。两个人用一个键盘,照样打得火热。后来有第二个人买了电脑,那两台电脑能不能连接起来呢?你会说,当然能啊,买个路由器不就行了。 + +现在一台家用路由器非常便宜,一百多块的事情。那时候路由器绝对是奢侈品。一直到大四,我们宿舍都没有买路由器。可能是因为那时候技术没有现在这么发达,导致我对网络技术的认知是逐渐深入的,而且每一层都是实实在在接触到的。 + +第一层(物理层) + +使用路由器,是在第三层上。我们先从第一层物理层开始说。 + +物理层能折腾啥?现在的同学可能想不到,我们当时去学校配电脑的地方买网线,卖网线的师傅都会问,你的网线是要电脑连电脑啊,还是电脑连网口啊? + +我们要的是电脑连电脑。这种方式就是一根网线,有两个头。一头插在一台电脑的网卡上,另一头插在另一台电脑的网卡上。但是在当时,普通的网线这样是通不了的,所以水晶头要做交叉线,用的就是所谓的1-3、2-6交叉接法。 + +水晶头的第1、2和第3、6脚,它们分别起着收、发信号的作用。将一端的1号和3号线、2号和6号线互换一下位置,就能够在物理层实现一端发送的信号,另一端能收到。 + +当然电脑连电脑,除了网线要交叉,还需要配置这两台电脑的IP地址、子网掩码和默认网关。这三个概念上一节详细描述过了。要想两台电脑能够通信,这三项必须配置成为一个网络,可以一个是192.168.0.1/24,另一个是192.168.0.2/24,否则是不通的。 + +这里我想问你一个问题,两台电脑之间的网络包,包含MAC层吗?当然包含,要完整。IP层要封装了MAC层才能将包放入物理层。 + +到此为止,两台电脑已经构成了一个最小的局域网,也即LAN。可以玩联机局域网游戏啦! + +等到第三个哥们也买了一台电脑,怎么把三台电脑连在一起呢? + +先别说交换机,当时交换机也贵。有一个叫做Hub的东西,也就是集线器。这种设备有多个口,可以将宿舍里的多台电脑连接起来。但是,和交换机不同,集线器没有大脑,它完全在物理层工作。它会将自己收到的每一个字节,都复制到其他端口上去。这是第一层物理层联通的方案。 + +第二层(数据链路层) + +你可能已经发现问题了。Hub采取的是广播的模式,如果每一台电脑发出的包,宿舍的每个电脑都能收到,那就麻烦了。这就需要解决几个问题: + + +这个包是发给谁的?谁应该接收? +大家都在发,会不会产生混乱?有没有谁先发、谁后发的规则? +如果发送的时候出现了错误,怎么办? + + +这几个问题,都是第二层,数据链路层,也即MAC层要解决的问题。MAC的全称是Medium Access Control,即媒体访问控制。控制什么呢?其实就是控制在往媒体上发数据的时候,谁先发、谁后发的问题。防止发生混乱。这解决的是第二个问题。这个问题中的规则,学名叫多路访问。有很多算法可以解决这个问题。就像车管所管束马路上跑的车,能想的办法都想过了。 + +比如接下来这三种方式: + + +方式一:分多个车道。每个车一个车道,你走你的,我走我的。这在计算机网络里叫作信道划分; + +方式二:今天单号出行,明天双号出行,轮着来。这在计算机网络里叫作轮流协议; + +方式三:不管三七二十一,有事儿先出门,发现特堵,就回去。错过高峰再出。我们叫作随机接入协议。著名的以太网,用的就是这个方式。 + + +解决了第二个问题,就是解决了媒体接入控制的问题,MAC的问题也就解决好了。这和MAC地址没什么关系。 + +接下来要解决第一个问题:发给谁,谁接收?这里用到一个物理地址,叫作链路层地址。但是因为第二层主要解决媒体接入控制的问题,所以它常被称为MAC地址。 + +解决第一个问题就牵扯到第二层的网络包格式。对于以太网,第二层的最开始,就是目标的MAC地址和源的MAC地址。 + + + +接下来是类型,大部分的类型是IP数据包,然后IP里面包含TCP、UDP,以及HTTP等,这都是里层封装的事情。 + +有了这个目标MAC地址,数据包在链路上广播,MAC的网卡才能发现,这个包是给它的。MAC的网卡把包收进来,然后打开IP包,发现IP地址也是自己的,再打开TCP包,发现端口是自己,也就是80,而nginx就是监听80。 + +于是将请求提交给nginx,nginx返回一个网页。然后将网页需要发回请求的机器。然后层层封装,最后到MAC层。因为来的时候有源MAC地址,返回的时候,源MAC就变成了目标MAC,再返给请求的机器。 + +对于以太网,第二层的最后面是CRC,也就是循环冗余检测。通过XOR异或的算法,来计算整个包是否在发送的过程中出现了错误,主要解决第三个问题。 + +这里还有一个没有解决的问题,当源机器知道目标机器的时候,可以将目标地址放入包里面,如果不知道呢?一个广播的网络里面接入了N台机器,我怎么知道每个MAC地址是谁呢?这就是ARP协议,也就是已知IP地址,求MAC地址的协议。 + + + +在一个局域网里面,当知道了IP地址,不知道MAC怎么办呢?靠“吼”。 + + + +广而告之,发送一个广播包,谁是这个IP谁来回答。具体询问和回答的报文就像下面这样: + + + +为了避免每次都用ARP请求,机器本地也会进行ARP缓存。当然机器会不断地上线下线,IP也可能会变,所以ARP的MAC地址缓存过一段时间就会过期。 + +局域网 + +好了,至此我们宿舍四个电脑就组成了一个局域网。用Hub连接起来,就可以玩局域网版的《魔兽争霸》了。 + + + +打开游戏,进入“局域网选项”,选择一张地图,点击“创建游戏”,就可以进入这张地图的房间中。等同一个局域网里的其他小伙伴加入后,游戏就可以开始了。 + +这种组网的方法,对一个宿舍来说没有问题,但是一旦机器数目增多,问题就出现了。因为Hub是广播的,不管某个接口是否需要,所有的Bit都会被发送出去,然后让主机来判断是不是需要。这种方式路上的车少就没问题,车一多,产生冲突的概率就提高了。而且把不需要的包转发过去,纯属浪费。看来Hub这种不管三七二十一都转发的设备是不行了,需要点儿智能的。因为每个口都只连接一台电脑,这台电脑又不怎么换IP和MAC地址,只要记住这台电脑的MAC地址,如果目标MAC地址不是这台电脑的,这个口就不用转发了。 + +谁能知道目标MAC地址是否就是连接某个口的电脑的MAC地址呢?这就需要一个能把MAC头拿下来,检查一下目标MAC地址,然后根据策略转发的设备,按第二节课中讲过的,这个设备显然是个二层设备,我们称为交换机。 + +交换机怎么知道每个口的电脑的MAC地址呢?这需要交换机会学习。 + +一台MAC1电脑将一个包发送给另一台MAC2电脑,当这个包到达交换机的时候,一开始交换机也不知道MAC2的电脑在哪个口,所以没办法,它只能将包转发给除了来的那个口之外的其他所有的口。但是,这个时候,交换机会干一件非常聪明的事情,就是交换机会记住,MAC1是来自一个明确的口。以后有包的目的地址是MAC1的,直接发送到这个口就可以了。 + +当交换机作为一个关卡一样,过了一段时间之后,就有了整个网络的一个结构了,这个时候,基本上不用广播了,全部可以准确转发。当然,每个机器的IP地址会变,所在的口也会变,因而交换机上的学习的结果,我们称为转发表,是有一个过期时间的。 + +有了交换机,一般来说,你接个几十台、上百台机器打游戏,应该没啥问题。你可以组个战队了。能上网了,就可以玩网游了。 + +小结 + +好了,今天的内容差不多了,我们来总结一下,有三个重点需要你记住: + +第一,MAC层是用来解决多路访问的堵车问题的; + +第二,ARP是通过吼的方式来寻找目标MAC地址的,吼完之后记住一段时间,这个叫作缓存; + +第三,交换机是有MAC地址学习能力的,学完了它就知道谁在哪儿了,不用广播了。 + +最后,给你留两个思考题吧。 + + +在二层中我们讲了ARP协议,即已知IP地址求MAC;还有一种RARP协议,即已知MAC求IP的,你知道它可以用来干什么吗? +如果一个局域网里面有多个交换机,ARP广播的模式会出现什么问题呢? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/06交换机与VLAN:办公室太复杂,我要回学校.md b/专栏/趣谈网络协议/06交换机与VLAN:办公室太复杂,我要回学校.md new file mode 100644 index 0000000..9ed30f2 --- /dev/null +++ b/专栏/趣谈网络协议/06交换机与VLAN:办公室太复杂,我要回学校.md @@ -0,0 +1,164 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 交换机与VLAN:办公室太复杂,我要回学校 + 上一次,我们在宿舍里组建了一个本地的局域网LAN,可以愉快地玩游戏了。这是一个非常简单的场景,因为只有一台交换机,电脑数目很少。今天,让我们切换到一个稍微复杂一点的场景,办公室。 + +拓扑结构是怎么形成的? + +我们常见到的办公室大多是一排排的桌子,每个桌子都有网口,一排十几个座位就有十几个网口,一个楼层就会有几十个甚至上百个网口。如果算上所有楼层,这个场景自然比你宿舍里的复杂多了。具体哪里复杂呢?我来给你具体讲解。 + +首先,这个时候,一个交换机肯定不够用,需要多台交换机,交换机之间连接起来,就形成一个稍微复杂的拓扑结构。 + +我们先来看两台交换机的情形。两台交换机连接着三个局域网,每个局域网上都有多台机器。如果机器1只知道机器4的IP地址,当它想要访问机器4,把包发出去的时候,它必须要知道机器4的MAC地址。 + + + +于是机器1发起广播,机器2收到这个广播,但是这不是找它的,所以没它什么事。交换机A一开始是不知道任何拓扑信息的,在它收到这个广播后,采取的策略是,除了广播包来的方向外,它还要转发给其他所有的网口。于是机器3也收到广播信息了,但是这和它也没什么关系。 + +当然,交换机B也是能够收到广播信息的,但是这时候它也是不知道任何拓扑信息的,因而也是进行广播的策略,将包转发到局域网三。这个时候,机器4和机器5都收到了广播信息。机器4主动响应说,这是找我的,这是我的MAC地址。于是一个ARP请求就成功完成了。 + +在上面的过程中,交换机A和交换机B都是能够学习到这样的信息:机器1是在左边这个网口的。当了解到这些拓扑信息之后,情况就好转起来。当机器2要访问机器1的时候,机器2并不知道机器1的MAC地址,所以机器2会发起一个ARP请求。这个广播消息会到达机器1,也同时会到达交换机A。这个时候交换机A已经知道机器1是不可能在右边的网口的,所以这个广播信息就不会广播到局域网二和局域网三。 + +当机器3要访问机器1的时候,也需要发起一个广播的ARP请求。这个时候交换机A和交换机B都能够收到这个广播请求。交换机A当然知道主机A是在左边这个网口的,所以会把广播消息转发到局域网一。同时,交换机B收到这个广播消息之后,由于它知道机器1是不在右边这个网口的,所以不会将消息广播到局域网三。 + +如何解决常见的环路问题? + +这样看起来,两台交换机工作得非常好。随着办公室越来越大,交换机数目肯定越来越多。当整个拓扑结构复杂了,这么多网线,绕过来绕过去,不可避免地会出现一些意料不到的情况。其中常见的问题就是环路问题。 + +例如这个图,当两个交换机将两个局域网同时连接起来的时候。你可能会觉得,这样反而有了高可用性。但是却不幸地出现了环路。出现了环路会有什么结果呢? + + + +我们来想象一下机器1访问机器2的过程。一开始,机器1并不知道机器2的MAC地址,所以它需要发起一个ARP的广播。广播到达机器2,机器2会把MAC地址返回来,看起来没有这两个交换机什么事情。 + +但是问题来了,这两个交换机还是都能够收到广播包的。交换机A一开始是不知道机器2在哪个局域网的,所以它会把广播消息放到局域网二,在局域网二广播的时候,交换机B右边这个网口也是能够收到广播消息的。交换机B会将这个广播信息发送到局域网一。局域网一的这个广播消息,又会到达交换机A左边的这个接口。交换机A这个时候还是不知道机器2在哪个局域网,于是将广播包又转发到局域网二。左转左转左转,好像是个圈哦。 + +可能有人会说,当两台交换机都能够逐渐学习到拓扑结构之后,是不是就可以了? + +别想了,压根儿学不会的。机器1的广播包到达交换机A和交换机B的时候,本来两个交换机都学会了机器1是在局域网一的,但是当交换机A将包广播到局域网二之后,交换机B右边的网口收到了来自交换机A的广播包。根据学习机制,这彻底损坏了交换机B的三观,刚才机器1还在左边的网口呢,怎么又出现在右边的网口呢?哦,那肯定是机器1换位置了,于是就误会了,交换机B就学会了,机器1是从右边这个网口来的,把刚才学习的那一条清理掉。同理,交换机A右边的网口,也能收到交换机B转发过来的广播包,同样也误会了,于是也学会了,机器1从右边的网口来,不是从左边的网口来。 + +然而当广播包从左边的局域网一广播的时候,两个交换机再次刷新三观,原来机器1是在左边的,过一会儿,又发现不对,是在右边的,过一会,又发现不对,是在左边的。 + +这还是一个包转来转去,每台机器都会发广播包,交换机转发也会复制广播包,当广播包越来越多的时候,按照上一节讲过一个共享道路的算法,也就是路会越来越堵,最后谁也别想走。所以,必须有一个方法解决环路的问题,怎么破除环路呢? + +STP协议中那些难以理解的概念 + +在数据结构中,有一个方法叫做最小生成树。有环的我们常称为图。将图中的环破了,就生成了树。在计算机网络中,生成树的算法叫作STP,全称Spanning Tree Protocol。 + +STP协议比较复杂,一开始很难看懂,但是其实这是一场血雨腥风的武林比武或者华山论剑,最终决出五岳盟主的方式。 + + + +在STP协议里面有很多概念,译名就非常拗口,但是我一作比喻,你很容易就明白了。 + + +Root Bridge,也就是根交换机。这个比较容易理解,可以比喻为“掌门”交换机,是某棵树的老大,是掌门,最大的大哥。 + +Designated Bridges,有的翻译为指定交换机。这个比较难理解,可以想像成一个“小弟”,对于树来说,就是一棵树的树枝。所谓“指定”的意思是,我拜谁做大哥,其他交换机通过这个交换机到达根交换机,也就相当于拜他做了大哥。这里注意是树枝,不是叶子,因为叶子往往是主机。 + +Bridge Protocol Data Units (BPDU) ,网桥协议数据单元。可以比喻为“相互比较实力”的协议。行走江湖,比的就是武功,拼的就是实力。当两个交换机碰见的时候,也就是相连的时候,就需要互相比一比内力了。BPDU只有掌门能发,已经隶属于某个掌门的交换机只能传达掌门的指示。 + +Priority Vector,优先级向量。可以比喻为实力 (值越小越牛)。实力是啥?就是一组ID数目,[Root Bridge ID, Root Path Cost, Bridge ID, and Port ID]。为什么这样设计呢?这是因为要看怎么来比实力。先看Root Bridge ID。拿出老大的ID看看,发现掌门一样,那就是师兄弟;再比Root Path Cost,也即我距离我的老大的距离,也就是拿和掌门关系比,看同一个门派内谁和老大关系铁;最后比Bridge ID,比我自己的ID,拿自己的本事比。 + + +STP的工作过程是怎样的? + +接下来,我们来看STP的工作过程。 + +一开始,江湖纷争,异常混乱。大家都觉得自己是掌门,谁也不服谁。于是,所有的交换机都认为自己是掌门,每个网桥都被分配了一个ID。这个ID里有管理员分配的优先级,当然网络管理员知道哪些交换机贵,哪些交换机好,就会给它们分配高的优先级。这种交换机生下来武功就很高,起步就是乔峰。 + + + +既然都是掌门,互相都连着网线,就互相发送BPDU来比功夫呗。这一比就发现,有人是岳不群,有人是封不平,赢的接着当掌门,输的就只好做小弟了。当掌门的还会继续发BPDU,而输的人就没有机会了。它们只有在收到掌门发的BPDU的时候,转发一下,表示服从命令。 + + + +数字表示优先级。就像这个图,5和6碰见了,6的优先级低,所以乖乖做小弟。于是一个小门派形成,5是掌门,6是小弟。其他诸如1-7、2-8、3-4这样的小门派,也诞生了。于是江湖出现了很多小的门派,小的门派,接着合并。 + +合并的过程会出现以下四种情形,我分别来介绍。 + +情形一:掌门遇到掌门 + +当5碰到了1,掌门碰见掌门,1觉得自己是掌门,5也刚刚跟别人PK完成为掌门。这俩掌门比较功夫,最终1胜出。于是输掉的掌门5就会率领所有的小弟归顺。结果就是1成为大掌门。 + + + +情形二:同门相遇 + +同门相遇可以是掌门与自己的小弟相遇,这说明存在“环”了。这个小弟已经通过其他门路拜在你门下,结果你还不认识,就PK了一把。结果掌门发现这个小弟功夫不错,不应该级别这么低,就把它招到门下亲自带,那这个小弟就相当于升职了。 + +我们再来看,假如1和6相遇。6原来就拜在1的门下,只不过6的上司是5,5的上司是1。1发现,6距离我才只有2,比从5这里过来的5(=4+1)近多了,那6就直接汇报给我吧。于是,5和6分别汇报给1。 + + + +同门相遇还可以是小弟相遇。这个时候就要比较谁和掌门的关系近,当然近的当大哥。刚才5和6同时汇报给1了,后来5和6在比较功夫的时候发现,5你直接汇报给1距离是4,如果5汇报给6再汇报给1,距离只有2+1=3,所以5干脆拜6为上司。 + +情形三:掌门与其他帮派小弟相遇 + +小弟拿本帮掌门和这个掌门比较,赢了,这个掌门拜入门来。输了,会拜入新掌门,并且逐渐拉拢和自己连接的兄弟,一起弃暗投明。 + + + +例如,2和7相遇,虽然7是小弟,2是掌门。就个人武功而言,2比7强,但是7的掌门是1,比2牛,所以没办法,2要拜入7的门派,并且连同自己的小弟都一起拜入。 + +情形四:不同门小弟相遇 + +各自拿掌门比较,输了的拜入赢的门派,并且逐渐将与自己连接的兄弟弃暗投明。- + + +例如,5和4相遇。虽然4的武功好于5,但是5的掌门是1,比4牛,于是4拜入5的门派。后来当3和4相遇的时候,3发现4已经叛变了,4说我现在老大是1,比你牛,要不你也来吧,于是3也拜入1。 + +最终,生成一棵树,武林一统,天下太平。但是天下大势,分久必合,合久必分,天下统一久了,也会有相应的问题。 + +如何解决广播问题和安全问题? + +毕竟机器多了,交换机也多了,就算交换机比Hub智能一些,但是还是难免有广播的问题,一大波机器,相关的部门、不相关的部门,广播一大堆,性能就下来了。就像一家公司,创业的时候,一二十个人,坐在一个会议室,有事情大家讨论一下,非常方便。但是如果变成了50个人,全在一个会议室里面吵吵,就会乱得不得了。 + +你们公司有不同的部门,有的部门需要保密的,比如人事部门,肯定要讨论升职加薪的事儿。由于在同一个广播域里面,很多包都会在一个局域网里面飘啊飘,碰到了一个会抓包的程序员,就能抓到这些包,如果没有加密,就能看到这些敏感信息了。还是上面的例子,50个人在一个会议室里面七嘴八舌地讨论,其中有两个HR,那他们讨论的问题,肯定被其他人偷偷听走了。 + +那咋办,分部门,分会议室呗。那我们就来看看怎么分。 + +有两种分的方法,一个是物理隔离。每个部门设一个单独的会议室,对应到网络方面,就是每个部门有单独的交换机,配置单独的子网,这样部门之间的沟通就需要路由器了。路由器咱们还没讲到,以后再说。这样的问题在于,有的部门人多,有的部门人少。人少的部门慢慢人会变多,人多的部门也可能人越变越少。如果每个部门有单独的交换机,口多了浪费,少了又不够用。 + +另外一种方式是虚拟隔离,就是用我们常说的VLAN,或者叫虚拟局域网。使用VLAN,一个交换机上会连属于多个局域网的机器,那交换机怎么区分哪个机器属于哪个局域网呢?- + + +我们只需要在原来的二层的头上加一个TAG,里面有一个VLAN ID,一共12位。为什么是12位呢?因为12位可以划分4096个VLAN。这样是不是还不够啊。现在的情况证明,目前云计算厂商里面绝对不止4096个用户。当然每个用户需要一个VLAN了啊,怎么办呢,这个我们在后面的章节再说。 + +如果我们买的交换机是支持VLAN的,当这个交换机把二层的头取下来的时候,就能够识别这个VLAN ID。这样只有相同VLAN的包,才会互相转发,不同VLAN的包,是看不到的。这样广播问题和安全问题就都能够解决了。- + + +我们可以设置交换机每个口所属的VLAN。如果某个口坐的是程序员,他们属于VLAN 10;如果某个口坐的是人事,他们属于VLAN 20;如果某个口坐的是财务,他们属于VLAN 30。这样,财务发的包,交换机只会转发到VLAN 30的口上。程序员啊,你就监听VLAN 10吧,里面除了代码,啥都没有。 + +而且对于交换机来讲,每个VLAN的口都是可以重新设置的。一个财务走了,把他所在座位的口从VLAN 30移除掉,来了一个程序员,坐在财务的位置上,就把这个口设置为VLAN 10,十分灵活。 + +有人会问交换机之间怎么连接呢?将两个交换机连接起来的口应该设置成什么VLAN呢?对于支持VLAN的交换机,有一种口叫作Trunk口。它可以转发属于任何VLAN的口。交换机之间可以通过这种口相互连接。 + +好了,解决这么多交换机连接在一起的问题,办公室的问题似乎搞定了。然而这只是一般复杂的场景,因为你能接触到的网络,到目前为止,不管是你的台式机,还是笔记本所连接的网络,对于带宽、高可用等都要求不高。就算出了问题,一会儿上不了网,也不会有什么大事。 + +我们在宿舍、学校或者办公室,经常会访问一些网站,这些网站似乎永远不会“挂掉”。那是因为这些网站都生活在一个叫做数据中心的地方,那里的网络世界更加复杂。在后面的章节,我会为你详细讲解。 + +小结 + +好了,这节就到这里,我们这里来总结一下: + + +当交换机的数目越来越多的时候,会遭遇环路问题,让网络包迷路,这就需要使用STP协议,通过华山论剑比武的方式,将有环路的图变成没有环路的树,从而解决环路问题。 +交换机数目多会面临隔离问题,可以通过VLAN形成虚拟局域网,从而解决广播问题和安全问题。 + + +最后,给你留两个思考题。 + + +STP协议能够很好地解决环路问题,但是也有它的缺点,你能举几个例子吗? +在一个比较大的网络中,如果两台机器不通,你知道应该用什么方式调试吗? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/07ICMP与ping:投石问路的侦察兵.md b/专栏/趣谈网络协议/07ICMP与ping:投石问路的侦察兵.md new file mode 100644 index 0000000..ba92d6b --- /dev/null +++ b/专栏/趣谈网络协议/07ICMP与ping:投石问路的侦察兵.md @@ -0,0 +1,134 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 ICMP与ping:投石问路的侦察兵 + 无论是在宿舍,还是在办公室,或者运维一个数据中心,我们常常会遇到网络不通的问题。那台机器明明就在那里,你甚至都可以通过机器的终端连上去看。它看着好好的,可是就是连不上去,究竟是哪里出了问题呢? + +ICMP协议的格式 + +一般情况下,你会想到ping一下。那你知道ping是如何工作的吗? + +ping是基于ICMP协议工作的。ICMP全称Internet Control Message Protocol,就是互联网控制报文协议。这里面的关键词是“控制”,那具体是怎么控制的呢? + +网络包在异常复杂的网络环境中传输时,常常会遇到各种各样的问题。当遇到问题的时候,总不能“死个不明不白”,要传出消息来,报告情况,这样才可以调整传输策略。这就相当于我们经常看到的电视剧里,古代行军的时候,为将为帅者需要通过侦察兵、哨探或传令兵等人肉的方式来掌握情况,控制整个战局。 + +ICMP报文是封装在IP包里面的。因为传输指令的时候,肯定需要源地址和目标地址。它本身非常简单。因为作为侦查兵,要轻装上阵,不能携带大量的包袱。 + + + +ICMP报文有很多的类型,不同的类型有不同的代码。最常用的类型是主动请求为8,主动请求的应答为0。 + +查询报文类型 + +我们经常在电视剧里听到这样的话:主帅说,来人哪!前方战事如何,快去派人打探,一有情况,立即通报! + +这种是主帅发起的,主动查看敌情,对应ICMP的查询报文类型。例如,常用的ping就是查询报文,是一种主动请求,并且获得主动应答的ICMP协议。所以,ping发的包也是符合ICMP协议格式的,只不过它在后面增加了自己的格式。 + +对ping的主动请求,进行网络抓包,称为ICMP ECHO REQUEST。同理主动请求的回复,称为ICMP ECHO REPLY。比起原生的ICMP,这里面多了两个字段,一个是标识符。这个很好理解,你派出去两队侦查兵,一队是侦查战况的,一队是去查找水源的,要有个标识才能区分。另一个是序号,你派出去的侦查兵,都要编个号。如果派出去10个,回来10个,就说明前方战况不错;如果派出去10个,回来2个,说明情况可能不妙。 + +在选项数据中,ping还会存放发送请求的时间值,来计算往返时间,说明路程的长短。 + +差错报文类型 + +当然也有另外一种方式,就是差错报文。 + +主帅骑马走着走着,突然来了一匹快马,上面的小兵气喘吁吁的:报告主公,不好啦!张将军遭遇埋伏,全军覆没啦!这种是异常情况发起的,来报告发生了不好的事情,对应ICMP的差错报文类型。 + +我举几个ICMP差错报文的例子:终点不可达为3,源抑制为4,超时为11,重定向为5。这些都是什么意思呢?我给你具体解释一下。 + +第一种是终点不可达。小兵:报告主公,您让把粮草送到张将军那里,结果没有送到。 + +如果你是主公,你肯定会问,为啥送不到?具体的原因在代码中表示就是,网络不可达代码为0,主机不可达代码为1,协议不可达代码为2,端口不可达代码为3,需要进行分片但设置了不分片位代码为4。 + +具体的场景就像这样: + + +网络不可达:主公,找不到地方呀? +主机不可达:主公,找到地方没这个人呀? +协议不可达:主公,找到地方,找到人,口号没对上,人家天王盖地虎,我说12345! +端口不可达:主公,找到地方,找到人,对了口号,事儿没对上,我去送粮草,人家说他们在等救兵。 +需要进行分片但设置了不分片位:主公,走到一半,山路狭窄,想换小车,但是您的将令,严禁换小车,就没办法送到了。 + + +第二种是源站抑制,也就是让源站放慢发送速度。小兵:报告主公,您粮草送的太多了吃不完。 + +第三种是时间超时,也就是超过网络包的生存时间还是没到。小兵:报告主公,送粮草的人,自己把粮草吃完了,还没找到地方,已经饿死啦。 + +第四种是路由重定向,也就是让下次发给另一个路由器。小兵:报告主公,上次送粮草的人本来只要走一站地铁,非得从五环绕,下次别这样了啊。 + +差错报文的结构相对复杂一些。除了前面还是IP,ICMP的前8字节不变,后面则跟上出错的那个IP包的IP头和IP正文的前8个字节。 + +而且这类侦查兵特别恪尽职守,不但自己返回来报信,还把一部分遗物也带回来。 + + +侦察兵:报告主公,张将军已经战死沙场,这是张将军的印信和佩剑。 +主公:神马?张将军是怎么死的(可以查看ICMP的前8字节)?没错,这是张将军的剑,是他的剑(IP数据包的头及正文前8字节)。 + + +ping:查询报文类型的使用 + +接下来,我们重点来看ping的发送和接收过程。 + + + +假定主机A的IP地址是192.168.1.1,主机B的IP地址是192.168.1.2,它们都在同一个子网。那当你在主机A上运行“ping 192.168.1.2”后,会发生什么呢? + +ping命令执行的时候,源主机首先会构建一个ICMP请求数据包,ICMP数据包内包含多个字段。最重要的是两个,第一个是类型字段,对于请求数据包而言该字段为 8;另外一个是顺序号,主要用于区分连续ping的时候发出的多个数据包。每发出一个请求数据包,顺序号会自动加1。为了能够计算往返时间RTT,它会在报文的数据部分插入发送时间。 + +然后,由ICMP协议将这个数据包连同地址192.168.1.2一起交给IP层。IP层将以192.168.1.2作为目的地址,本机IP地址作为源地址,加上一些其他控制信息,构建一个IP数据包。 + +接下来,需要加入MAC头。如果在本节ARP映射表中查找出IP地址192.168.1.2所对应的MAC地址,则可以直接使用;如果没有,则需要发送ARP协议查询MAC地址,获得MAC地址后,由数据链路层构建一个数据帧,目的地址是IP层传过来的MAC地址,源地址则是本机的MAC地址;还要附加上一些控制信息,依据以太网的介质访问规则,将它们传送出去。 + +主机B收到这个数据帧后,先检查它的目的MAC地址,并和本机的MAC地址对比,如符合,则接收,否则就丢弃。接收后检查该数据帧,将IP数据包从帧中提取出来,交给本机的IP层。同样,IP层检查后,将有用的信息提取后交给ICMP协议。 + +主机B会构建一个 ICMP 应答包,应答数据包的类型字段为 0,顺序号为接收到的请求数据包中的顺序号,然后再发送出去给主机A。 + +在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了 ICMP 应答包,则说明目标主机可达。此时,源主机会检查,用当前时刻减去该数据包最初从源主机上发出的时刻,就是 ICMP 数据包的时间延迟。 + +当然这只是最简单的,同一个局域网里面的情况。如果跨网段的话,还会涉及网关的转发、路由器的转发等等。但是对于ICMP的头来讲,是没什么影响的。会影响的是根据目标IP地址,选择路由的下一跳,还有每经过一个路由器到达一个新的局域网,需要换MAC头里面的MAC地址。这个过程后面几节会详细描述,这里暂时不多说。 + +如果在自己的可控范围之内,当遇到网络不通的问题的时候,除了直接ping目标的IP地址之外,还应该有一个清晰的网络拓扑图。并且从理论上来讲,应该要清楚地知道一个网络包从源地址到目标地址都需要经过哪些设备,然后逐个ping中间的这些设备或者机器。如果可能的话,在这些关键点,通过tcpdump -i eth0 icmp,查看包有没有到达某个点,回复的包到达了哪个点,可以更加容易推断出错的位置。 + +经常会遇到一个问题,如果不在我们的控制范围内,很多中间设备都是禁止ping的,但是ping不通不代表网络不通。这个时候就要使用telnet,通过其他协议来测试网络是否通,这个就不在本篇的讲述范围了。 + +说了这么多,你应该可以看出ping这个程序是使用了ICMP里面的ECHO REQUEST和ECHO REPLY类型的。 + +Traceroute:差错报文类型的使用 + +那其他的类型呢?是不是只有真正遇到错误的时候,才能收到呢?那也不是,有一个程序Traceroute,是个“大骗子”。它会使用ICMP的规则,故意制造一些能够产生错误的场景。 + +所以,Traceroute的第一个作用就是故意设置特殊的TTL,来追踪去往目的地时沿途经过的路由器。Traceroute的参数指向某个目的IP地址,它会发送一个UDP的数据包。将TTL设置成1,也就是说一旦遇到一个路由器或者一个关卡,就表示它“牺牲”了。 + +如果中间的路由器不止一个,当然碰到第一个就“牺牲”。于是,返回一个ICMP包,也就是网络差错包,类型是时间超时。那大军前行就带一顿饭,试一试走多远会被饿死,然后找个哨探回来报告,那我就知道大军只带一顿饭能走多远了。 + +接下来,将TTL设置为2。第一关过了,第二关就“牺牲”了,那我就知道第二关有多远。如此反复,直到到达目的主机。这样,Traceroute就拿到了所有的路由器IP。当然,有的路由器压根不会回这个ICMP。这也是Traceroute一个公网的地址,看不到中间路由的原因。 + +怎么知道UDP有没有到达目的主机呢?Traceroute程序会发送一份UDP数据报给目的主机,但它会选择一个不可能的值作为UDP端口号(大于30000)。当该数据报到达时,将使目的主机的 UDP模块产生一份“端口不可达”错误ICMP报文。如果数据报没有到达,则可能是超时。 + +这就相当于故意派人去西天如来那里去请一本《道德经》,结果人家信佛不信道,消息就会被打出来。被打的消息传回来,你就知道西天是能够到达的。为什么不去取《心经》呢?因为UDP是无连接的。也就是说这人一派出去,你就得不到任何音信。你无法区别到底是半路走丢了,还是真的信佛遁入空门了,只有让人家打出来,你才会得到消息。 + +Traceroute还有一个作用是故意设置不分片,从而确定路径的MTU。要做的工作首先是发送分组,并设置“不分片”标志。发送的第一个分组的长度正好与出口MTU相等。如果中间遇到窄的关口会被卡住,会发送ICMP网络差错包,类型为“需要进行分片但设置了不分片位”。其实,这是人家故意的好吧,每次收到ICMP“不能分片”差错时就减小分组的长度,直到到达目标主机。 + +小结 + +好了,这一节内容差不多了,我来总结一下: + + +ICMP相当于网络世界的侦察兵。我讲了两种类型的ICMP报文,一种是主动探查的查询报文,一种异常报告的差错报文; +ping使用查询报文,Traceroute使用差错报文。 + + +最后,给你留两个思考题吧。 + + +当发送的报文出问题的时候,会发送一个ICMP的差错报文来报告错误,但是如果ICMP的差错报文也出问题了呢? +这一节只说了一个局域网互相ping的情况。如果跨路由器、跨网关的过程会是什么样的呢? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/08世界这么大,我想出网关:欧洲十国游与玄奘西行.md b/专栏/趣谈网络协议/08世界这么大,我想出网关:欧洲十国游与玄奘西行.md new file mode 100644 index 0000000..d3005d8 --- /dev/null +++ b/专栏/趣谈网络协议/08世界这么大,我想出网关:欧洲十国游与玄奘西行.md @@ -0,0 +1,212 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 世界这么大,我想出网关:欧洲十国游与玄奘西行 + 前几节,我主要跟你讲了宿舍里和办公室里用到的网络协议。你已经有了一些基础,是时候去外网逛逛了! + +怎么在宿舍上网? + +还记得咱们在宿舍的时候买了台交换机,几台机器组了一个局域网打游戏吗?可惜啊,只能打局域网的游戏,不能上网啊!盼啊盼啊,终于盼到大二,允许宿舍开通网络了。学校给每个宿舍的网口分配了一个IP地址。这个IP是校园网的IP,完全由网管部门控制。宿舍网的IP地址多为192.168.1.x。校园网的IP地址,假设是10.10.x.x。 + +这个时候,你要在宿舍上网,有两个办法: + +第一个办法,让你们宿舍长再买一个网卡。这个时候,你们宿舍长的电脑里就有两张网卡。一张网卡的线插到你们宿舍的交换机上,另一张网卡的线插到校园网的网口。而且,这张新的网卡的IP地址要按照学校网管部门分配的配置,不然上不了网。这种情况下,如果你们宿舍的人要上网,就需要一直开着宿舍长的电脑。 + +第二个办法,你们共同出钱买个家庭路由器(反正当时我们买不起)。家庭路由器会有内网网口和外网网口。把外网网口的线插到校园网的网口上,将这个外网网口配置成和网管部的一样。内网网口连上你们宿舍的所有的电脑。这种情况下,如果你们宿舍的人要上网,就需要一直开着路由器。 + +这两种方法其实是一样的。只不过第一种方式,让你的宿舍长的电脑,变成一个有多个口的路由器而已。而你买的家庭路由器,里面也跑着程序,和你宿舍长电脑里的功能一样,只不过是一个嵌入式的系统。 + +当你的宿舍长能够上网之后,接下来,就是其他人的电脑怎么上网的问题。这就需要配置你们的网卡。当然DHCP是可以默认配置的。在进行网卡配置的时候,除了IP地址,还需要配置一个Gateway的东西,这个就是网关。 + +你了解MAC头和IP头的细节吗? + +一旦配置了IP地址和网关,往往就能够指定目标地址进行访问了。由于在跨网关访问的时候,牵扯到MAC地址和IP地址的变化,这里有必要详细描述一下MAC头和IP头的细节。 + + + +在MAC头里面,先是目标MAC地址,然后是源MAC地址,然后有一个协议类型,用来说明里面是IP协议。IP头里面的版本号,目前主流的还是IPv4,服务类型TOS在第三节讲ip addr命令的时候讲过,TTL在第7节讲ICMP协议的时候讲过。另外,还有8位标识协议。这里到了下一层的协议,也就是,是TCP还是UDP。最重要的就是源IP和目标IP。先是源IP地址,然后是目标IP地址。 + +在任何一台机器上,当要访问另一个IP地址的时候,都会先判断,这个目标IP地址,和当前机器的IP地址,是否在同一个网段。怎么判断同一个网段呢?需要CIDR和子网掩码,这个在第三节的时候也讲过了。 + +如果是同一个网段,例如,你访问你旁边的兄弟的电脑,那就没网关什么事情,直接将源地址和目标地址放入IP头中,然后通过ARP获得MAC地址,将源MAC和目的MAC放入MAC头中,发出去就可以了。 + +如果不是同一网段,例如,你要访问你们校园网里面的BBS,该怎么办?这就需要发往默认网关Gateway。Gateway的地址一定是和源IP地址是一个网段的。往往不是第一个,就是第二个。例如192.168.1.0/24这个网段,Gateway往往会是192.168.1.1/24或者192.168.1.2/24。 + +如何发往默认网关呢?网关不是和源IP地址是一个网段的么?这个过程就和发往同一个网段的其他机器是一样的:将源地址和目标IP地址放入IP头中,通过ARP获得网关的MAC地址,将源MAC和网关的MAC放入MAC头中,发送出去。网关所在的端口,例如192.168.1.1/24将网络包收进来,然后接下来怎么做,就完全看网关的了。 + +网关往往是一个路由器,是一个三层转发的设备。啥叫三层设备?前面也说过了,就是把MAC头和IP头都取下来,然后根据里面的内容,看看接下来把包往哪里转发的设备。 + +在你的宿舍里面,网关就是你宿舍长的电脑。一个路由器往往有多个网口,如果是一台服务器做这个事情,则就有多个网卡,其中一个网卡是和源IP同网段的。 + +很多情况下,人们把网关就叫做路由器。其实不完全准确,而另一种比喻更加恰当:路由器是一台设备,它有五个网口或者网卡,相当于有五只手,分别连着五个局域网。每只手的IP地址都和局域网的IP地址相同的网段,每只手都是它握住的那个局域网的网关。 + +任何一个想发往其他局域网的包,都会到达其中一只手,被拿进来,拿下MAC头和IP头,看看,根据自己的路由算法,选择另一只手,加上IP头和MAC头,然后扔出去。 + +静态路由是什么? + +这个时候,问题来了,该选择哪一只手?IP头和MAC头加什么内容,哪些变、哪些不变呢?这个问题比较复杂,大致可以分为两类,一个是静态路由,一个是动态路由。动态路由下一节我们详细地讲。这一节我们先说静态路由。 + +静态路由,其实就是在路由器上,配置一条一条规则。这些规则包括:想访问BBS站(它肯定有个网段),从2号口出去,下一跳是IP2;想访问教学视频站(它也有个自己的网段),从3号口出去,下一跳是IP3,然后保存在路由器里。 + +每当要选择从哪只手抛出去的时候,就一条一条的匹配规则,找到符合的规则,就按规则中设置的那样,从某个口抛出去,找下一跳IPX。 + +IP头和MAC头哪些变、哪些不变? + +对于IP头和MAC头哪些变、哪些不变的问题,可以分两种类型。我把它们称为“欧洲十国游”型和“玄奘西行”型。 + +之前我说过,MAC地址是一个局域网内才有效的地址。因而,MAC地址只要过网关,就必定会改变,因为已经换了局域网。两者主要的区别在于IP地址是否改变。不改变IP地址的网关,我们称为转发网关;改变IP地址的网关,我们称为NAT网关。 + +“欧洲十国游”型 + +结合这个图,我们先来看“欧洲十国游”型。 + + + +服务器A要访问服务器B。首先,服务器A会思考,192.168.4.101和我不是一个网段的,因而需要先发给网关。那网关是谁呢?已经静态配置好了,网关是192.168.1.1。网关的MAC地址是多少呢?发送ARP获取网关的MAC地址,然后发送包。包的内容是这样的: + + +源MAC:服务器A的MAC + +目标MAC:192.168.1.1这个网口的MAC + +源IP:192.168.1.101 + +目标IP:192.168.4.101 + + +包到达192.168.1.1这个网口,发现MAC一致,将包收进来,开始思考往哪里转发。 + +在路由器A中配置了静态路由之后,要想访问192.168.4.0/24,要从192.168.56.1这个口出去,下一跳为192.168.56.2。 + +于是,路由器A思考的时候,匹配上了这条路由,要从192.168.56.1这个口发出去,发给192.168.56.2,那192.168.56.2的MAC地址是多少呢?路由器A发送ARP获取192.168.56.2的MAC地址,然后发送包。包的内容是这样的: + + +源MAC:192.168.56.1的MAC地址 + +目标MAC:192.168.56.2的MAC地址 + +源IP:192.168.1.101 + +目标IP:192.168.4.101 + + +包到达192.168.56.2这个网口,发现MAC一致,将包收进来,开始思考往哪里转发。 + +在路由器B中配置了静态路由,要想访问192.168.4.0/24,要从192.168.4.1这个口出去,没有下一跳了。因为我右手这个网卡,就是这个网段的,我是最后一跳了。 + +于是,路由器B思考的时候,匹配上了这条路由,要从192.168.4.1这个口发出去,发给192.168.4.101。那192.168.4.101的MAC地址是多少呢?路由器B发送ARP获取192.168.4.101的MAC地址,然后发送包。包的内容是这样的: + + +源MAC:192.168.4.1的MAC地址 + +目标MAC:192.168.4.101的MAC地址 + +源IP:192.168.1.101 + +目标IP:192.168.4.101 + + +包到达服务器B,MAC地址匹配,将包收进来。 + +通过这个过程可以看出,每到一个新的局域网,MAC都是要变的,但是IP地址都不变。在IP头里面,不会保存任何网关的IP地址。所谓的下一跳是,某个IP要将这个IP地址转换为MAC放入MAC头。 + +之所以将这种模式比喻称为欧洲十国游,是因为在整个过程中,IP头里面的地址都是不变的。IP地址在三个局域网都可见,在三个局域网之间的网段都不会冲突。在三个网段之间传输包,IP头不改变。这就像在欧洲各国之间旅游,一个签证就能搞定。 + + + +“玄奘西行”型 + +我们再来看“玄奘西行”型。 + +这里遇见的第一个问题是,局域网之间没有商量过,各定各的网段,因而IP段冲突了。最左面大唐的地址是192.168.1.101,最右面印度的地址也是192.168.1.101,如果单从IP地址上看,简直是自己访问自己,其实是大唐的192.168.1.101要访问印度的192.168.1.101。 + +怎么解决这个问题呢?既然局域网之间没有商量过,你们各管各的,那到国际上,也即中间的局域网里面,就需要使用另外的地址。就像出国,不能用咱们自己的身份证,而要改用护照一样,玄奘西游也要拿着专门取经的通关文牒,而不能用自己国家的身份证。 + +首先,目标服务器B在国际上要有一个国际的身份,我们给它一个192.168.56.2。在网关B上,我们记下来,国际身份192.168.56.2对应国内身份192.168.1.101。凡是要访问192.168.56.2,都转成192.168.1.101。 + +于是,源服务器A要访问目标服务器B,要指定的目标地址为192.168.56.2。这是它的国际身份。服务器A想,192.168.56.2和我不是一个网段的,因而需要发给网关,网关是谁?已经静态配置好了,网关是192.168.1.1,网关的MAC地址是多少?发送ARP获取网关的MAC地址,然后发送包。包的内容是这样的: + + +源MAC:服务器A的MAC + +目标MAC:192.168.1.1这个网口的MAC + +源IP:192.168.1.101 + +目标IP:192.168.56.2 + + +包到达192.168.1.1这个网口,发现MAC一致,将包收进来,开始思考往哪里转发。 + +在路由器A中配置了静态路由:要想访问192.168.56.2/24,要从192.168.56.1这个口出去,没有下一跳了,因为我右手这个网卡,就是这个网段的,我是最后一跳了。 + +于是,路由器A思考的时候,匹配上了这条路由,要从192.168.56.1这个口发出去,发给192.168.56.2。那192.168.56.2的MAC地址是多少呢?路由器A发送ARP获取192.168.56.2的MAC地址。 + +当网络包发送到中间的局域网的时候,服务器A也需要有个国际身份,因而在国际上,源IP地址也不能用192.168.1.101,需要改成192.168.56.1。发送包的内容是这样的: + + +源MAC:192.168.56.1的MAC地址 + +目标MAC:192.168.56.2的MAC地址 + +源IP:192.168.56.1 + +目标IP:192.168.56.2 + + +包到达192.168.56.2这个网口,发现MAC一致,将包收进来,开始思考往哪里转发。 + +路由器B是一个NAT网关,它上面配置了,要访问国际身份192.168.56.2对应国内身份192.168.1.101,于是改为访问192.168.1.101。 + +在路由器B中配置了静态路由:要想访问192.168.1.0/24,要从192.168.1.1这个口出去,没有下一跳了,因为我右手这个网卡,就是这个网段的,我是最后一跳了。 + +于是,路由器B思考的时候,匹配上了这条路由,要从192.168.1.1这个口发出去,发给192.168.1.101。 + +那192.168.1.101的MAC地址是多少呢?路由器B发送ARP获取192.168.1.101的MAC地址,然后发送包。内容是这样的: + + +源MAC:192.168.1.1的MAC地址 + +目标MAC:192.168.1.101的MAC地址 + +源IP:192.168.56.1 + +目标IP:192.168.1.101 + + +包到达服务器B,MAC地址匹配,将包收进来。 + +从服务器B接收的包可以看出,源IP为服务器A的国际身份,因而发送返回包的时候,也发给这个国际身份,由路由器A做NAT,转换为国内身份。 + +从这个过程可以看出,IP地址也会变。这个过程用英文说就是Network Address Translation,简称NAT。 + +其实这第二种方式我们经常见,现在大家每家都有家用路由器,家里的网段都是192.168.1.x,所以你肯定访问不了你邻居家的这个私网的IP地址的。所以,当我们家里的包发出去的时候,都被家用路由器NAT成为了运营商的地址了。 + +很多办公室访问外网的时候,也是被NAT过的,因为不可能办公室里面的IP也是公网可见的,公网地址实在是太贵了,所以一般就是整个办公室共用一个到两个出口IP地址。你可以通过 https://www.whatismyip.com/ 查看自己的出口IP地址。 + +小结 + +好了,这一节内容差不多了,我来总结一下: + + +如果离开本局域网,就需要经过网关,网关是路由器的一个网口; + +路由器是一个三层设备,里面有如何寻找下一跳的规则; + +经过路由器之后MAC头要变,如果IP不变,相当于不换护照的欧洲旅游,如果IP变,相当于换护照的玄奘西行。 + + +最后,给你留两个思考题吧。 + + +当在你家里要访问163网站的时候,你的包需要NAT成为公网IP,返回的包又要NAT成你的私有IP,返回包怎么知道这是你的请求呢?它怎么就这么智能的NAT成了你的IP而非别人的IP呢? +对于路由规则,这一节讲述了静态路由,需要手动配置,如果要自动配置,你觉得应该怎么办呢? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/09路由协议:西出网关无故人,敢问路在何方.md b/专栏/趣谈网络协议/09路由协议:西出网关无故人,敢问路在何方.md new file mode 100644 index 0000000..b547d65 --- /dev/null +++ b/专栏/趣谈网络协议/09路由协议:西出网关无故人,敢问路在何方.md @@ -0,0 +1,231 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 路由协议:西出网关无故人,敢问路在何方 + 俗话说得好,在家千日好,出门一日难。网络包一旦出了网关,就像玄奘西行一样踏上了江湖漂泊的路。 + +上一节我们描述的是一个相对简单的情形。出了网关之后,只有一条路可以走。但是,网络世界复杂得多,一旦出了网关,会面临着很多路由器,有很多条道路可以选。如何选择一个更快速的道路求取真经呢?这里面还有很多门道可以讲。 + +如何配置路由? + +通过上一节的内容,你应该已经知道,路由器就是一台网络设备,它有多张网卡。当一个入口的网络包送到路由器时,它会根据一个本地的转发信息库,来决定如何正确地转发流量。这个转发信息库通常被称为路由表。 + +一张路由表中会有多条路由规则。每一条规则至少包含这三项信息。 + + +目的网络:这个包想去哪儿? + +出口设备:将包从哪个口扔出去? + +下一跳网关:下一个路由器的地址。 + + +通过route命令和ip route命令都可以进行查询或者配置。 + +例如,我们设置ip route add 10.176.48.0/20 via 10.173.32.1 dev eth0,就说明要去10.176.48.0/20这个目标网络,要从eth0端口出去,经过10.173.32.1。 + +上一节的例子中,网关上的路由策略就是按照这三项配置信息进行配置的。这种配置方式的一个核心思想是:根据目的IP地址来配置路由。 + +如何配置策略路由? + +当然,在真实的复杂的网络环境中,除了可以根据目的ip地址配置路由外,还可以根据多个参数来配置路由,这就称为策略路由。 + +可以配置多个路由表,可以根据源IP地址、入口设备、TOS等选择路由表,然后在路由表中查找路由。这样可以使得来自不同来源的包走不同的路由。 + +例如,我们设置: + +ip rule add from 192.168.1.0/24 table 10 +ip rule add from 192.168.2.0/24 table 20 + + +表示从192.168.1.10/24这个网段来的,使用table 10中的路由表,而从192.168.2.0/24网段来的,使用table20的路由表。 + +在一条路由规则中,也可以走多条路径。例如,在下面的路由规则中: + +ip route add default scope global nexthop via 100.100.100.1 weight 1 nexthop via 200.200.200.1 weight 2 + + +下一跳有两个地方,分别是100.100.100.1和200.200.200.1,权重分别为1比2。 + +在什么情况下会用到如此复杂的配置呢?我来举一个现实中的例子。 + +我是房东,家里从运营商那儿拉了两根网线。这两根网线分别属于两个运行商。一个带宽大一些,一个带宽小一些。这个时候,我就不能买普通的家用路由器了,得买个高级点的,可以接两个外网的。 + +家里的网络呢,就是普通的家用网段192.168.1.x/24。家里有两个租户,分别把线连到路由器上。IP地址为192.168.1.101/24和192.168.1.102/24,网关都是192.168.1.1/24,网关在路由器上。 + +就像上一节说的一样,家里的网段是私有网段,出去的包需要NAT成公网的IP地址,因而路由器是一个NAT路由器。 + +两个运营商都要为这个网关配置一个公网的IP地址。如果你去查看你们家路由器里的网段,基本就是我图中画的样子。 + + + +运行商里面也有一个IP地址,在运营商网络里面的网关。不同的运营商方法不一样,有的是/32的,也即一个一对一连接。 + +例如,运营商1给路由器分配的地址是183.134.189.34/32,而运营商网络里面的网关是183.134.188.1/32。有的是/30的,也就是分了一个特别小的网段。运营商2给路由器分配的地址是60.190.27.190/30,运营商网络里面的网关是60.190.27.189/30。 + +根据这个网络拓扑图,可以将路由配置成这样: + +$ ip route list table main +60.190.27.189/30 dev eth3 proto kernel scope link src 60.190.27.190 +183.134.188.1 dev eth2 proto kernel scope link src 183.134.189.34 +192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.1 +127.0.0.0/8 dev lo scope link +default via 183.134.188.1 dev eth2 + + +当路由这样配置的时候,就告诉这个路由器如下的规则: + + +如果去运营商二,就走eth3; + +如果去运营商一呢,就走eth2; + +如果访问内网,就走eth1; + +如果所有的规则都匹配不上,默认走运营商一,也即走快的网络。 + + +但是问题来了,租户A不想多付钱,他说我就上上网页,从不看电影,凭什么收我同样贵的网费啊?没关系,咱有技术可以解决。 + +下面我添加一个Table,名字叫chao。 + +# echo 200 chao >> /etc/iproute2/rt_tables + + +添加一条规则: + +# ip rule add from 192.168.1.101 table chao +# ip rule ls +0: from all lookup local +32765: from 10.0.0.10 lookup chao +32766: from all lookup main +32767: from all lookup default + + +设定规则为:从192.168.1.101来的包都查看个chao这个新的路由表。 + +在chao路由表中添加规则: + +# ip route add default via 60.190.27.189 dev eth3 table chao +# ip route flush cache + + +默认的路由走慢的,谁让你不付钱。 + +上面说的都是静态的路由,一般来说网络环境简单的时候,在自己的可控范围之内,自己捣鼓还是可以的。但是有时候网络环境复杂并且多变,如果总是用静态路由,一旦网络结构发生变化,让网络管理员手工修改路由太复杂了,因而需要动态路由算法。 + +动态路由算法 + +使用动态路由路由器,可以根据路由协议算法生成动态路由表,随网络运行状况的变化而变化。那路由算法是什么样的呢? + +我们可以想象唐僧西天取经,需要解决两大问题,一个是在每个国家如何找到正确的路,去换通关文牒、吃饭、休息;一个是在国家之间,野外行走的时候,如何找到正确的路、水源的问题。 + + + +无论是一个国家内部,还是国家之间,我们都可以将复杂的路径,抽象为一种叫作图的数据结构。至于唐僧西行取经,肯定想走的路越少越好,道路越短越好,因而这就转化成为如何在途中找到最短路径的问题。 + +咱们在大学里面学习计算机网络与数据结构的时候,知道求最短路径常用的有两种方法,一种是Bellman-Ford算法,一种是Dijkstra算法。在计算机网络中基本也是用这两种方法计算的。 + +1.距离矢量路由算法 + +第一大类的算法称为距离矢量路由(distance vector routing)。它是基于Bellman-Ford算法的。 + +这种算法的基本思路是,每个路由器都保存一个路由表,包含多行,每行对应网络中的一个路由器,每一行包含两部分信息,一个是要到目标路由器,从那条线出去,另一个是到目标路由器的距离。 + +由此可以看出,每个路由器都是知道全局信息的。那这个信息如何更新呢?每个路由器都知道自己和邻居之间的距离,每过几秒,每个路由器都将自己所知的到达所有的路由器的距离告知邻居,每个路由器也能从邻居那里得到相似的信息。 + +每个路由器根据新收集的信息,计算和其他路由器的距离,比如自己的一个邻居距离目标路由器的距离是M,而自己距离邻居是x,则自己距离目标路由器是x+M。 + +这个算法比较简单,但是还是有问题。 + +第一个问题就是好消息传得快,坏消息传得慢。 如果有个路由器加入了这个网络,它的邻居就能很快发现它,然后将消息广播出去。要不了多久,整个网络就都知道了。但是一旦一个路由器挂了,挂的消息是没有广播的。当每个路由器发现原来的道路到不了这个路由器的时候,感觉不到它已经挂了,而是试图通过其他的路径访问,直到试过了所有的路径,才发现这个路由器是真的挂了。 + +我再举个例子。 + + + +原来的网络包括两个节点,B和C。A加入了网络,它的邻居B很快就发现A启动起来了。于是它将自己和A的距离设为1,同样C也发现A起来了,将自己和A的距离设置为2。但是如果A挂掉,情况就不妙了。B本来和A是邻居,发现连不上A了,但是C还是能够连上,只不过距离远了点,是2,于是将自己的距离设置为3。殊不知C的距离2其实是基于原来自己的距离为1计算出来的。C发现自己也连不上A,并且发现B设置为3,于是自己改成距离4。依次类推,数越来越大,直到超过一个阈值,我们才能判定A真的挂了。 + +这个道理有点像有人走丢了。当你突然发现找不到这个人了。于是你去学校问,是不是在他姨家呀?找到他姨家,他姨说,是不是在他舅舅家呀?他舅舅说,是不是在他姥姥家呀?他姥姥说,是不是在学校呀?总归要问一圈,或者是超过一定的时间,大家才会认为这个人的确走丢了。如果这个人其实只是去见了一个谁都不认识的网友去了,当这个人回来的时候,只要他随便见到其中的一个亲戚,这个亲戚就会拉着他到他的家长那里,说你赶紧回家,你妈都找你一天了。 + +这种算法的第二个问题是,每次发送的时候,要发送整个全局路由表。网络大了,谁也受不了,所以最早的路由协议RIP就是这个算法。它适用于小型网络(小于15跳)。当网络规模都小的时候,没有问题。现在一个数据中心内部路由器数目就很多,因而不适用了。 + +所以上面的两个问题,限制了距离矢量路由的网络规模。 + +2.链路状态路由算法 + +第二大类算法是链路状态路由(link state routing),基于Dijkstra算法。 + +这种算法的基本思路是:当一个路由器启动的时候,首先是发现邻居,向邻居say hello,邻居都回复。然后计算和邻居的距离,发送一个echo,要求马上返回,除以二就是距离。然后将自己和邻居之间的链路状态包广播出去,发送到整个网络的每个路由器。这样每个路由器都能够收到它和邻居之间的关系的信息。因而,每个路由器都能在自己本地构建一个完整的图,然后针对这个图使用Dijkstra算法,找到两点之间的最短路径。 + +不像距离距离矢量路由协议那样,更新时发送整个路由表。链路状态路由协议只广播更新的或改变的网络拓扑,这使得更新信息更小,节省了带宽和CPU利用率。而且一旦一个路由器挂了,它的邻居都会广播这个消息,可以使得坏消息迅速收敛。 + +动态路由协议 + +1.基于链路状态路由算法的OSPF + +OSPF(Open Shortest Path First,开放式最短路径优先)就是这样一个基于链路状态路由协议,广泛应用在数据中心中的协议。由于主要用在数据中心内部,用于路由决策,因而称为内部网关协议(Interior Gateway Protocol,简称IGP)。 + +内部网关协议的重点就是找到最短的路径。在一个组织内部,路径最短往往最优。当然有时候OSPF可以发现多个最短的路径,可以在这多个路径中进行负载均衡,这常常被称为等价路由。 + + + +这一点非常重要。有了等价路由,到一个地方去可以有相同的两个路线,可以分摊流量,还可以当一条路不通的时候,走另外一条路。这个在后面我们讲数据中心的网络的时候,一般应用的接入层会有负载均衡LVS。它可以和OSPF一起,实现高吞吐量的接入层设计。 + +有了内网的路由协议,在一个国家内,唐僧可以想怎么走怎么走了,两条路选一条也行。 + +2.基于距离矢量路由算法的BGP + +但是外网的路由协议,也即国家之间的,又有所不同。我们称为外网路由协议(Border Gateway Protocol,简称BGP)。 + +在一个国家内部,有路当然选近的走。但是国家之间,不光远近的问题,还有政策的问题。例如,唐僧去西天取经,有的路近。但是路过的国家看不惯僧人,见了僧人就抓。例如灭法国,连光头都要抓。这样的情况即便路近,也最好绕远点走。 + +对于网络包同样,每个数据中心都设置自己的Policy。例如,哪些外部的IP可以让内部知晓,哪些内部的IP可以让外部知晓,哪些可以通过,哪些不能通过。这就好比,虽然从我家里到目的地最近,但是不能谁都能从我家走啊! + +在网络世界,这一个个国家成为自治系统AS(Autonomous System)。自治系统分几种类型。 + + +Stub AS:对外只有一个连接。这类AS不会传输其他AS的包。例如,个人或者小公司的网络。 + +Multihomed AS:可能有多个连接连到其他的AS,但是大多拒绝帮其他的AS传输包。例如一些大公司的网络。 + +Transit AS:有多个连接连到其他的AS,并且可以帮助其他的AS传输包。例如主干网。 + + +每个自治系统都有边界路由器,通过它和外面的世界建立联系。 + + + +BGP又分为两类,eBGP和iBGP。自治系统间,边界路由器之间使用eBGP广播路由。内部网络也需要访问其他的自治系统。边界路由器如何将BGP学习到的路由导入到内部网络呢?就是通过运行iBGP,使得内部的路由器能够找到到达外网目的地的最好的边界路由器。 + +BGP协议使用的算法是路径矢量路由协议(path-vector protocol)。它是距离矢量路由协议的升级版。 + +前面说了距离矢量路由协议的缺点。其中一个是收敛慢。在BGP里面,除了下一跳hop之外,还包括了自治系统AS的路径,从而可以避免坏消息传得慢的问题,也即上面所描述的,B知道C原来能够到达A,是因为通过自己,一旦自己都到达不了A了,就不用假设C还能到达A了。 + +另外,在路径中将一个自治系统看成一个整体,不区分自治系统内部的路由器,这样自治系统的数目是非常有限的。就像大家都能记住出去玩,从中国出发先到韩国然后到日本,只要不计算细到具体哪一站,就算是发送全局信息,也是没有问题的。 + +小结 + +好了,这一节就到这里了,我来做个总结: + + +路由分静态路由和动态路由,静态路由可以配置复杂的策略路由,控制转发策略; + +动态路由主流算法有两种,距离矢量算法和链路状态算法。基于两种算法产生两种协议,BGP协议和OSPF协议。 + + +最后,再给你留两个思考题: + + +路由协议要在路由器之间交换信息,这些信息的交换还需要走路由吗?不是死锁了吗? + +路由器之间信息的交换使用什么协议呢?报文格式是什么样呢? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/10UDP协议:因性善而简单,难免碰到“城会玩”.md b/专栏/趣谈网络协议/10UDP协议:因性善而简单,难免碰到“城会玩”.md new file mode 100644 index 0000000..8df30c7 --- /dev/null +++ b/专栏/趣谈网络协议/10UDP协议:因性善而简单,难免碰到“城会玩”.md @@ -0,0 +1,141 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 UDP协议:因性善而简单,难免碰到“城会玩” + 讲完了IP层以后,接下来我们开始讲传输层。传输层里比较重要的两个协议,一个是TCP,一个是UDP。对于不从事底层开发的人员来讲,或者对于开发应用的人来讲,最常用的就是这两个协议。由于面试的时候,这两个协议经常会被放在一起问,因而我在讲的时候,也会结合着来讲。 + +TCP和UDP有哪些区别? + +一般面试的时候我问这两个协议的区别,大部分人会回答,TCP是面向连接的,UDP是面向无连接的。 + +什么叫面向连接,什么叫无连接呢?在互通之前,面向连接的协议会先建立连接。例如,TCP会三次握手,而UDP不会。为什么要建立连接呢?你TCP三次握手,我UDP也可以发三个包玩玩,有什么区别吗? + +所谓的建立连接,是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结构来保证所谓的面向连接的特性。 + +例如,TCP提供可靠交付。通过TCP连接传输的数据,无差错、不丢失、不重复、并且按序到达。我们都知道IP包是没有任何可靠性保证的,一旦发出去,就像西天取经,走丢了、被妖怪吃了,都只能随它去。但是TCP号称能做到那个连接维护的程序做的事情,这个下两节我会详细描述。而UDP继承了IP包的特性,不保证不丢失,不保证按顺序到达。 + +再如,TCP是面向字节流的。发送的时候发的是一个流,没头没尾。IP包可不是一个流,而是一个个的IP包。之所以变成了流,这也是TCP自己的状态维护做的事情。而UDP继承了IP的特性,基于数据报的,一个一个地发,一个一个地收。 + +还有TCP是可以有拥塞控制的。它意识到包丢弃了或者网络的环境不好了,就会根据情况调整自己的行为,看看是不是发快了,要不要发慢点。UDP就不会,应用让我发,我就发,管它洪水滔天。 + +因而TCP其实是一个有状态服务,通俗地讲就是有脑子的,里面精确地记着发送了没有,接收到没有,发送到哪个了,应该接收哪个了,错一点儿都不行。而UDP则是无状态服务。通俗地说是没脑子的,天真无邪的,发出去就发出去了。 + +我们可以这样比喻,如果MAC层定义了本地局域网的传输行为,IP层定义了整个网络端到端的传输行为,这两层基本定义了这样的基因:网络传输是以包为单位的,二层叫帧,网络层叫包,传输层叫段。我们笼统地称为包。包单独传输,自行选路,在不同的设备封装解封装,不保证到达。基于这个基因,生下来的孩子UDP完全继承了这些特性,几乎没有自己的思想。 + +UDP包头是什么样的? + +我们来看一下UDP包头。 + +前面章节我已经讲过包的传输过程,这里不再赘述。当我发送的UDP包到达目标机器后,发现MAC地址匹配,于是就取下来,将剩下的包传给处理IP层的代码。把IP头取下来,发现目标IP匹配,接下来呢?这里面的数据包是给谁呢? + +发送的时候,我知道我发的是一个UDP的包,收到的那台机器咋知道的呢?所以在IP头里面有个8位协议,这里会存放,数据里面到底是TCP还是UDP,当然这里是UDP。于是,如果我们知道UDP头的格式,就能从数据里面,将它解析出来。解析出来以后呢?数据给谁处理呢? + +处理完传输层的事情,内核的事情基本就干完了,里面的数据应该交给应用程序自己去处理,可是一台机器上跑着这么多的应用程序,应该给谁呢? + +无论应用程序写的使用TCP传数据,还是UDP传数据,都要监听一个端口。正是这个端口,用来区分应用程序,要不说端口不能冲突呢。两个应用监听一个端口,到时候包给谁呀?所以,按理说,无论是TCP还是UDP包头里面应该有端口号,根据端口号,将数据交给相应的应用程序。 + + + +当我们看到UDP包头的时候,发现的确有端口号,有源端口号和目标端口号。因为是两端通信嘛,这很好理解。但是你还会发现,UDP除了端口号,再没有其他的了。和下两节要讲的TCP头比起来,这个简直简单得一塌糊涂啊! + +UDP的三大特点 + +UDP就像小孩子一样,有以下这些特点: + +第一,沟通简单,不需要一肚子花花肠子(大量的数据结构、处理逻辑、包头字段)。前提是它相信网络世界是美好的,秉承性善论,相信网络通路默认就是很容易送达的,不容易被丢弃的。 + +第二,轻信他人。它不会建立连接,虽然有端口号,但是监听在这个地方,谁都可以传给他数据,他也可以传给任何人数据,甚至可以同时传给多个人数据。 + +第三,愣头青,做事不懂权变。不知道什么时候该坚持,什么时候该退让。它不会根据网络的情况进行发包的拥塞控制,无论网络丢包丢成啥样了,它该怎么发还怎么发。 + +UDP的三大使用场景 + +基于UDP这种“小孩子”的特点,我们可以考虑在以下的场景中使用。 + +第一,需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用。这很好理解,就像如果你是领导,你会让你们组刚毕业的小朋友去做一些没有那么难的项目,打一些没有那么难的客户,或者做一些失败了也能忍受的实验性项目。 + +我们在第四节讲的DHCP就是基于UDP协议的。一般的获取IP地址都是内网请求,而且一次获取不到IP又没事,过一会儿还有机会。我们讲过PXE可以在启动的时候自动安装操作系统,操作系统镜像的下载使用的TFTP,这个也是基于UDP协议的。在还没有操作系统的时候,客户端拥有的资源很少,不适合维护一个复杂的状态机,而且因为是内网,一般也没啥问题。 + +第二,不需要一对一沟通,建立连接,而是可以广播的应用。咱们小时候人都很简单,大家在班级里面,谁成绩好,谁写作好,应该表扬谁惩罚谁,谁得几个小红花都是当着全班的面讲的,公平公正公开。长大了人心复杂了,薪水、奖金要背靠背,和员工一对一沟通。 + +UDP的不面向连接的功能,可以使得可以承载广播或者多播的协议。DHCP就是一种广播的形式,就是基于UDP协议的,而广播包的格式前面说过了。 + +对于多播,我们在讲IP地址的时候,讲过一个D类地址,也即组播地址,使用这个地址,可以将包组播给一批机器。当一台机器上的某个进程想监听某个组播地址的时候,需要发送IGMP包,所在网络的路由器就能收到这个包,知道有个机器上有个进程在监听这个组播地址。当路由器收到这个组播地址的时候,会将包转发给这台机器,这样就实现了跨路由器的组播。 + +在后面云中网络部分,有一个协议VXLAN,也是需要用到组播,也是基于UDP协议的。 + +第三,需要处理速度快,时延低,可以容忍少数丢包,但是要求即便网络拥塞,也毫不退缩,一往无前的时候。记得曾国藩建立湘军的时候,专门招出生牛犊不怕虎的新兵,而不用那些“老油条”的八旗兵,就是因为八旗兵经历的事情多,遇到敌军不敢舍死忘生。 + +同理,UDP简单、处理速度快,不像TCP那样,操这么多的心,各种重传啊,保证顺序啊,前面的不收到,后面的没法处理啊。不然等这些事情做完了,时延早就上去了。而TCP在网络不好出现丢包的时候,拥塞控制策略会主动的退缩,降低发送速度,这就相当于本来环境就差,还自断臂膀,用户本来就卡,这下更卡了。 + +当前很多应用都是要求低时延的,它们可不想用TCP如此复杂的机制,而是想根据自己的场景,实现自己的可靠和连接保证。例如,如果应用自己觉得,有的包丢了就丢了,没必要重传了,就可以算了,有的比较重要,则应用自己重传,而不依赖于TCP。有的前面的包没到,后面的包到了,那就先给客户展示后面的嘛,干嘛非得等到齐了呢?如果网络不好,丢了包,那不能退缩啊,要尽快传啊,速度不能降下来啊,要挤占带宽,抢在客户失去耐心之前到达。 + +由于UDP十分简单,基本啥都没做,也就给了应用“城会玩”的机会。就像在和平年代,每个人应该有独立的思考和行为,应该可靠并且礼让;但是如果在战争年代,往往不太需要过于独立的思考,而需要士兵简单服从命令就可以了。 + +曾国藩说哪支部队需要诱敌牺牲,也就牺牲了,相当于包丢了就丢了。两军狭路相逢的时候,曾国藩说上,没有带宽也要上,这才给了曾国藩运筹帷幄,城会玩的机会。同理如果你实现的应用需要有自己的连接策略,可靠保证,时延要求,使用UDP,然后再应用层实现这些是再好不过了。 + +基于UDP的“城会玩”的五个例子 + +我列举几种“城会玩”的例子。 + +“城会玩”一:网页或者APP的访问 + +原来访问网页和手机APP都是基于HTTP协议的。HTTP协议是基于TCP的,建立连接都需要多次交互,对于时延比较大的目前主流的移动互联网来讲,建立一次连接需要的时间会比较长,然而既然是移动中,TCP可能还会断了重连,也是很耗时的。而且目前的HTTP协议,往往采取多个数据通道共享一个连接的情况,这样本来为了加快传输速度,但是TCP的严格顺序策略使得哪怕共享通道,前一个不来,后一个和前一个即便没关系,也要等着,时延也会加大。 + +而QUIC(全称Quick UDP Internet Connections,快速UDP互联网连接)是Google提出的一种基于UDP改进的通信协议,其目的是降低网络通信的延迟,提供更好的用户互动体验。 + +QUIC在应用层上,会自己实现快速连接建立、减少重传时延,自适应拥塞控制,是应用层“城会玩”的代表。这一节主要是讲UDP,QUIC我们放到应用层去讲。 + +“城会玩”二:流媒体的协议 + +现在直播比较火,直播协议多使用RTMP,这个协议我们后面的章节也会讲,而这个RTMP协议也是基于TCP的。TCP的严格顺序传输要保证前一个收到了,下一个才能确认,如果前一个收不到,下一个就算包已经收到了,在缓存里面,也需要等着。对于直播来讲,这显然是不合适的,因为老的视频帧丢了其实也就丢了,就算再传过来用户也不在意了,他们要看新的了,如果老是没来就等着,卡顿了,新的也看不了,那就会丢失客户,所以直播,实时性比较比较重要,宁可丢包,也不要卡顿的。 + +另外,对于丢包,其实对于视频播放来讲,有的包可以丢,有的包不能丢,因为视频的连续帧里面,有的帧重要,有的不重要,如果必须要丢包,隔几个帧丢一个,其实看视频的人不会感知,但是如果连续丢帧,就会感知了,因而在网络不好的情况下,应用希望选择性的丢帧。 + +还有就是当网络不好的时候,TCP协议会主动降低发送速度,这对本来当时就卡的看视频来讲是要命的,应该应用层马上重传,而不是主动让步。因而,很多直播应用,都基于UDP实现了自己的视频传输协议。 + +“城会玩”三:实时游戏 + +游戏有一个特点,就是实时性比较高。快一秒你干掉别人,慢一秒你被别人爆头,所以很多职业玩家会买非常专业的鼠标和键盘,争分夺秒。 + +因而,实时游戏中客户端和服务端要建立长连接,来保证实时传输。但是游戏玩家很多,服务器却不多。由于维护TCP连接需要在内核维护一些数据结构,因而一台机器能够支撑的TCP连接数目是有限的,然后UDP由于是没有连接的,在异步IO机制引入之前,常常是应对海量客户端连接的策略。 + +另外还是TCP的强顺序问题,对战的游戏,对网络的要求很简单,玩家通过客户端发送给服务器鼠标和键盘行走的位置,服务器会处理每个用户发送过来的所有场景,处理完再返回给客户端,客户端解析响应,渲染最新的场景展示给玩家。 + +如果出现一个数据包丢失,所有事情都需要停下来等待这个数据包重发。客户端会出现等待接收数据,然而玩家并不关心过期的数据,激战中卡1秒,等能动了都已经死了。 + +游戏对实时要求较为严格的情况下,采用自定义的可靠UDP协议,自定义重传策略,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成的影响。 + +“城会玩”四:IoT物联网 + +一方面,物联网领域终端资源少,很可能只是个内存非常小的嵌入式系统,而维护TCP协议代价太大;另一方面,物联网对实时性要求也很高,而TCP还是因为上面的那些原因导致时延大。Google旗下的Nest建立Thread Group,推出了物联网通信协议Thread,就是基于UDP协议的。 + +“城会玩”五:移动通信领域 + +在4G网络里,移动流量上网的数据面对的协议GTP-U是基于UDP的。因为移动网络协议比较复杂,而GTP协议本身就包含复杂的手机上线下线的通信协议。如果基于TCP,TCP的机制就显得非常多余,这部分协议我会在后面的章节单独讲解。 + +小结 + +好了,这节就到这里了,我们来总结一下: + + +如果将TCP比作成熟的社会人,UDP则是头脑简单的小朋友。TCP复杂,UDP简单;TCP维护连接,UDP谁都相信;TCP会坚持知进退;UDP愣头青一个,勇往直前; + +UDP虽然简单,但它有简单的用法。它可以用在环境简单、需要多播、应用层自己控制传输的地方。例如DHCP、VXLAN、QUIC等。 + + +最后,给你留两个思考题吧。 + + +都说TCP是面向连接的,在计算机看来,怎么样才算一个连接呢? + +你知道TCP的连接是如何建立,又是如何关闭的吗? + + +欢迎你留言和讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/11TCP协议(上):因性恶而复杂,先恶后善反轻松.md b/专栏/趣谈网络协议/11TCP协议(上):因性恶而复杂,先恶后善反轻松.md new file mode 100644 index 0000000..f129746 --- /dev/null +++ b/专栏/趣谈网络协议/11TCP协议(上):因性恶而复杂,先恶后善反轻松.md @@ -0,0 +1,169 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 TCP协议(上):因性恶而复杂,先恶后善反轻松 + 上一节,我们讲的UDP,基本上包括了传输层所必须的端口字段。它就像我们小时候一样简单,相信“网之初,性本善,不丢包,不乱序”。 + +后来呢,我们都慢慢长大,了解了社会的残酷,变得复杂而成熟,就像TCP协议一样。它之所以这么复杂,那是因为它秉承的是“性恶论”。它天然认为网络环境是恶劣的,丢包、乱序、重传,拥塞都是常有的事情,一言不合就可能送达不了,因而要从算法层面来保证可靠性。 + +TCP包头格式 + +我们先来看TCP头的格式。从这个图上可以看出,它比UDP复杂得多。 + + + +首先,源端口号和目标端口号是不可少的,这一点和UDP是一样的。如果没有这两个端口号。数据就不知道应该发给哪个应用。 + +接下来是包的序号。为什么要给包编号呢?当然是为了解决乱序的问题。不编好号怎么确认哪个应该先来,哪个应该后到呢。编号是为了解决乱序问题。既然是社会老司机,做事当然要稳重,一件件来,面临再复杂的情况,也临危不乱。 + +还应该有的就是确认序号。发出去的包应该有确认,要不然我怎么知道对方有没有收到呢?如果没有收到就应该重新发送,直到送达。这个可以解决不丢包的问题。作为老司机,做事当然要靠谱,答应了就要做到,暂时做不到也要有个回复。 + +TCP是靠谱的协议,但是这不能说明它面临的网络环境好。从IP层面来讲,如果网络状况的确那么差,是没有任何可靠性保证的,而作为IP的上一层TCP也无能为力,唯一能做的就是更加努力,不断重传,通过各种算法保证。也就是说,对于TCP来讲,IP层你丢不丢包,我管不着,但是我在我的层面上,会努力保证可靠性。 + +这有点像如果你在北京,和客户约十点见面,那么你应该清楚堵车是常态,你干预不了,也控制不了,你唯一能做的就是早走。打车不行就改乘地铁,尽力不失约。 + +接下来有一些状态位。例如SYN是发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接等。TCP是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。 + +不像小时候,随便一个不认识的小朋友都能玩在一起,人大了,就变得礼貌,优雅而警觉,人与人遇到会互相热情的寒暄,离开会不舍地道别,但是人与人之间的信任会经过多次交互才能建立。 + +还有一个重要的就是窗口大小。TCP要做流量控制,通信双方各声明一个窗口,标识自己当前能够的处理能力,别发送的太快,撑死我,也别发的太慢,饿死我。 + +作为老司机,做事情要有分寸,待人要把握尺度,既能适当提出自己的要求,又不强人所难。除了做流量控制以外,TCP还会做拥塞控制,对于真正的通路堵车不堵车,它无能为力,唯一能做的就是控制自己,也即控制发送的速度。不能改变世界,就改变自己嘛。 + +作为老司机,要会自我控制,知进退,知道什么时候应该坚持,什么时候应该让步。 + +通过对TCP头的解析,我们知道要掌握TCP协议,重点应该关注以下几个问题: + + +顺序问题 ,稳重不乱; + +丢包问题,承诺靠谱; + +连接维护,有始有终; + +流量控制,把握分寸; + +拥塞控制,知进知退。 + + +TCP的三次握手 + +所有的问题,首先都要先建立一个连接,所以我们先来看连接维护问题。 + +TCP的连接建立,我们常常称为三次握手。 + +A:您好,我是A。 + +B:您好A,我是B。 + +A:您好B。 + +我们也常称为“请求->应答->应答之应答”的三个回合。这个看起来简单,其实里面还是有很多的学问,很多的细节。 + +首先,为什么要三次,而不是两次?按说两个人打招呼,一来一回就可以了啊?为了可靠,为什么不是四次? + +我们还是假设这个通路是非常不可靠的,A要发起一个连接,当发了第一个请求杳无音信的时候,会有很多的可能性,比如第一个请求包丢了,再如没有丢,但是绕了弯路,超时了,还有B没有响应,不想和我连接。 + +A不能确认结果,于是再发,再发。终于,有一个请求包到了B,但是请求包到了B的这个事情,目前A还是不知道的,A还有可能再发。 + +B收到了请求包,就知道了A的存在,并且知道A要和它建立连接。如果B不乐意建立连接,则A会重试一阵后放弃,连接建立失败,没有问题;如果B是乐意建立连接的,则会发送应答包给A。 + +当然对于B来说,这个应答包也是一入网络深似海,不知道能不能到达A。这个时候B自然不能认为连接是建立好了,因为应答包仍然会丢,会绕弯路,或者A已经挂了都有可能。 + +而且这个时候B还能碰到一个诡异的现象就是,A和B原来建立了连接,做了简单通信后,结束了连接。还记得吗?A建立连接的时候,请求包重复发了几次,有的请求包绕了一大圈又回来了,B会认为这也是一个正常的的请求的话,因此建立了连接,可以想象,这个连接不会进行下去,也没有个终结的时候,纯属单相思了。因而两次握手肯定不行。 + +B发送的应答可能会发送多次,但是只要一次到达A,A就认为连接已经建立了,因为对于A来讲,他的消息有去有回。A会给B发送应答之应答,而B也在等这个消息,才能确认连接的建立,只有等到了这个消息,对于B来讲,才算它的消息有去有回。 + +当然A发给B的应答之应答也会丢,也会绕路,甚至B挂了。按理来说,还应该有个应答之应答之应答,这样下去就没底了。所以四次握手是可以的,四十次都可以,关键四百次也不能保证就真的可靠了。只要双方的消息都有去有回,就基本可以了。 + +好在大部分情况下,A和B建立了连接之后,A会马上发送数据的,一旦A发送数据,则很多问题都得到了解决。例如A发给B的应答丢了,当A后续发送的数据到达的时候,B可以认为这个连接已经建立,或者B压根就挂了,A发送的数据,会报错,说B不可达,A就知道B出事情了。 + +当然你可以说A比较坏,就是不发数据,建立连接后空着。我们在程序设计的时候,可以要求开启keepalive机制,即使没有真实的数据包,也有探活包。 + +另外,你作为服务端B的程序设计者,对于A这种长时间不发包的客户端,可以主动关闭,从而空出资源来给其他客户端使用。 + +三次握手除了双方建立连接外,主要还是为了沟通一件事情,就是TCP包的序号的问题。 + +A要告诉B,我这面发起的包的序号起始是从哪个号开始的,B同样也要告诉A,B发起的包的序号起始是从哪个号开始的。为什么序号不能都从1开始呢?因为这样往往会出现冲突。 + +例如,A连上B之后,发送了1、2、3三个包,但是发送3的时候,中间丢了,或者绕路了,于是重新发送,后来A掉线了,重新连上B后,序号又从1开始,然后发送2,但是压根没想发送3,但是上次绕路的那个3又回来了,发给了B,B自然认为,这就是下一个包,于是发生了错误。 + +因而,每个连接都要有不同的序号。这个序号的起始序号是随着时间变化的,可以看成一个32位的计数器,每4微秒加一,如果计算一下,如果到重复,需要4个多小时,那个绕路的包早就死翘翘了,因为我们都知道IP包头里面有个TTL,也即生存时间。 + +好了,双方终于建立了信任,建立了连接。前面也说过,为了维护这个连接,双方都要维护一个状态机,在连接建立的过程中,双方的状态变化时序图就像这样。 + + + +一开始,客户端和服务端都处于CLOSED状态。先是服务端主动监听某个端口,处于LISTEN状态。然后客户端主动发起连接SYN,之后处于SYN-SENT状态。服务端收到发起的连接,返回SYN,并且ACK客户端的SYN,之后处于SYN-RCVD状态。客户端收到服务端发送的SYN和ACK之后,发送ACK的ACK,之后处于ESTABLISHED状态,因为它一发一收成功了。服务端收到ACK的ACK之后,处于ESTABLISHED状态,因为它也一发一收了。 + +TCP四次挥手 + +好了,说完了连接,接下来说一说“拜拜”,好说好散。这常被称为四次挥手。 + +A:B啊,我不想玩了。 + +B:哦,你不想玩了啊,我知道了。 + +这个时候,还只是A不想玩了,也即A不会再发送数据,但是B能不能在ACK的时候,直接关闭呢?当然不可以了,很有可能A是发完了最后的数据就准备不玩了,但是B还没做完自己的事情,还是可以发送数据的,所以称为半关闭的状态。 + +这个时候A可以选择不再接收数据了,也可以选择最后再接收一段数据,等待B也主动关闭。 + +B:A啊,好吧,我也不玩了,拜拜。 + +A:好的,拜拜。 + +这样整个连接就关闭了。但是这个过程有没有异常情况呢?当然有,上面是和平分手的场面。 + +A开始说“不玩了”,B说“知道了”,这个回合,是没什么问题的,因为在此之前,双方还处于合作的状态,如果A说“不玩了”,没有收到回复,则A会重新发送“不玩了”。但是这个回合结束之后,就有可能出现异常情况了,因为已经有一方率先撕破脸。 + +一种情况是,A说完“不玩了”之后,直接跑路,是会有问题的,因为B还没有发起结束,而如果A跑路,B就算发起结束,也得不到回答,B就不知道该怎么办了。另一种情况是,A说完“不玩了”,B直接跑路,也是有问题的,因为A不知道B是还有事情要处理,还是过一会儿会发送结束。 + +那怎么解决这些问题呢?TCP协议专门设计了几个状态来处理这些问题。我们来看断开连接的时候的状态时序图。 + + + +断开的时候,我们可以看到,当A说“不玩了”,就进入FIN_WAIT_1的状态,B收到“A不玩”的消息后,发送知道了,就进入CLOSE_WAIT的状态。 + +A收到“B说知道了”,就进入FIN_WAIT_2的状态,如果这个时候B直接跑路,则A将永远在这个状态。TCP协议里面并没有对这个状态的处理,但是Linux有,可以调整tcp_fin_timeout这个参数,设置一个超时时间。 + +如果B没有跑路,发送了“B也不玩了”的请求到达A时,A发送“知道B也不玩了”的ACK后,从FIN_WAIT_2状态结束,按说A可以跑路了,但是最后的这个ACK万一B收不到呢?则B会重新发一个“B不玩了”,这个时候A已经跑路了的话,B就再也收不到ACK了,因而TCP协议要求A最后等待一段时间TIME_WAIT,这个时间要足够长,长到如果B没收到ACK的话,“B说不玩了”会重发的,A会重新发一个ACK并且足够时间到达B。 + +A直接跑路还有一个问题是,A的端口就直接空出来了,但是B不知道,B原来发过的很多包很可能还在路上,如果A的端口被一个新的应用占用了,这个新的应用会收到上个连接中B发过来的包,虽然序列号是重新生成的,但是这里要上一个双保险,防止产生混乱,因而也需要等足够长的时间,等到原来B发送的所有的包都死翘翘,再空出端口来。 + +等待的时间设为2MSL,MSL是Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为TCP报文基于是IP协议的,而IP头中有一个TTL域,是IP数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。协议规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。 + +还有一个异常情况就是,B超过了2MSL的时间,依然没有收到它发的FIN的ACK,怎么办呢?按照TCP的原理,B当然还会重发FIN,这个时候A再收到这个包之后,A就表示,我已经在这里等了这么长时间了,已经仁至义尽了,之后的我就都不认了,于是就直接发送RST,B就知道A早就跑了。 + +TCP状态机 + +将连接建立和连接断开的两个时序状态图综合起来,就是这个著名的TCP的状态机。学习的时候比较建议将这个状态机和时序状态机对照着看,不然容易晕。 + + + +在这个图中,加黑加粗的部分,是上面说到的主要流程,其中阿拉伯数字的序号,是连接过程中的顺序,而大写中文数字的序号,是连接断开过程中的顺序。加粗的实线是客户端A的状态变迁,加粗的虚线是服务端B的状态变迁。 + +小结 + +好了,这一节就到这里了,我来做一个总结: + + +TCP包头很复杂,但是主要关注五个问题,顺序问题,丢包问题,连接维护,流量控制,拥塞控制; + +连接的建立是经过三次握手,断开的时候四次挥手,一定要掌握的我画的那个状态图。 + + +最后,给你留两个思考题。 + + +TCP的连接有这么多的状态,你知道如何在系统中查看某个连接的状态吗? + +这一节仅仅讲了连接维护问题,其实为了维护连接的状态,还有其他的数据结构来处理其他的四个问题,那你知道是什么吗? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/12TCP协议(下):西行必定多妖孽,恒心智慧消磨难.md b/专栏/趣谈网络协议/12TCP协议(下):西行必定多妖孽,恒心智慧消磨难.md new file mode 100644 index 0000000..12c2861 --- /dev/null +++ b/专栏/趣谈网络协议/12TCP协议(下):西行必定多妖孽,恒心智慧消磨难.md @@ -0,0 +1,231 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 TCP协议(下):西行必定多妖孽,恒心智慧消磨难 + 我们前面说到玄奘西行,要出网关。既然出了网关,那就是在公网上传输数据,公网往往是不可靠的,因而需要很多的机制去保证传输的可靠性,这里面需要恒心,也即各种重传的策略,还需要有智慧,也就是说,这里面包含着大量的算法。 + +如何做个靠谱的人? + +TCP想成为一个成熟稳重的人,成为一个靠谱的人。那一个人怎么样才算靠谱呢?咱们工作中经常就有这样的场景,比如你交代给下属一个事情以后,下属到底能不能做到,做到什么程度,什么时候能够交付,往往就会有应答,有回复。这样,处理事情的过程中,一旦有异常,你也可以尽快知道,而不是交代完之后就石沉大海,过了一个月再问,他说,啊我不记得了。 + +对应到网络协议上,就是客户端每发送的一个包,服务器端都应该有个回复,如果服务器端超过一定的时间没有回复,客户端就会重新发送这个包,直到有回复。 + +这个发送应答的过程是什么样呢?可以是上一个收到了应答,再发送下一个。这种模式有点像两个人直接打电话,你一句,我一句。但是这种方式的缺点是效率比较低。如果一方在电话那头处理的时间比较长,这一头就要干等着,双方都没办法干其他事情。咱们在日常工作中也不是这样的,不能你交代你的下属办一件事情,就一直打着电话看着他做,而是应该他按照你的安排,先将事情记录下来,办完一件回复一件。在他办事情的过程中,你还可以同时交代新的事情,这样双方就并行了。 + +如果使⽤这种模式,其实需要你和你的下属就不能靠脑⼦了,⽽是要都准备⼀个本⼦,你每交代下属⼀个事情,双方的本子都要记录⼀下。 + +当你的下属做完⼀件事情,就回复你,做完了,你就在你的本⼦上将这个事情划去。同时你的本⼦上每件事情都有时限,如果超过了时限下属还没有回复,你就要主动重新交代⼀下:上次那件事情,你还没回复我,咋样啦? + +既然多件事情可以一起处理,那就需要给每个事情编个号,防止弄错了。例如,程序员平时看任务的时候,都会看JIRA的ID,而不是每次都要描述一下具体的事情。在大部分情况下,对于事情的处理是按照顺序来的,先来的先处理,这就给应答和汇报工作带来了方便。等开周会的时候,每个程序员都可以将JIRA ID的列表拉出来,说以上的都做完了,⽽不⽤⼀个个说。 + +如何实现一个靠谱的协议? + +TCP协议使用的也是同样的模式。为了保证顺序性,每一个包都有一个ID。在建立连接的时候,会商定起始的ID是什么,然后按照ID一个个发送。为了保证不丢包,对于发送的包都要进行应答,但是这个应答也不是一个一个来的,而是会应答某个之前的ID,表示都收到了,这种模式称为累计确认或者累计应答(cumulative acknowledgment)。 + +为了记录所有发送的包和接收的包,TCP也需要发送端和接收端分别都有缓存来保存这些记录。发送端的缓存里是按照包的ID一个个排列,根据处理的情况分成四个部分。 + +第一部分:发送了并且已经确认的。这部分就是你交代下属的,并且也做完了的,应该划掉的。 + +第二部分:发送了并且尚未确认的。这部分是你交代下属的,但是还没做完的,需要等待做完的回复之后,才能划掉。 + +第三部分:没有发送,但是已经等待发送的。这部分是你还没有交代给下属,但是马上就要交代的。 + +第四部分:没有发送,并且暂时还不会发送的。这部分是你还没有交代给下属,而且暂时还不会交代给下属的。 + +这里面为什么要区分第三部分和第四部分呢?没交代的,一下子全交代了不就完了吗? + +这就是我们上一节提到的十个词口诀里的“流量控制,把握分寸”。作为项目管理人员,你应该根据以往的工作情况和这个员工反馈的能力、抗压力等,先在心中估测一下,这个人一天能做多少工作。如果工作布置少了,就会不饱和;如果工作布置多了,他就会做不完;如果你使劲逼迫,人家可能就要辞职了。 + +到底一个员工能够同时处理多少事情呢?在TCP里,接收端会给发送端报一个窗口的大小,叫Advertised window。这个窗口的大小应该等于上面的第二部分加上第三部分,就是已经交代了没做完的加上马上要交代的。超过这个窗口的,接收端做不过来,就不能发送了。 + +于是,发送端需要保持下面的数据结构。 + + + + +LastByteAcked:第一部分和第二部分的分界线 + +LastByteSent:第二部分和第三部分的分界线 + +LastByteAcked + AdvertisedWindow:第三部分和第四部分的分界线 + + +对于接收端来讲,它的缓存里记录的内容要简单一些。 + +第一部分:接受并且确认过的。也就是我领导交代给我,并且我做完的。 + +第二部分:还没接收,但是马上就能接收的。也即是我自己的能够接受的最大工作量。 + +第三部分:还没接收,也没法接收的。也即超过工作量的部分,实在做不完。 + +对应的数据结构就像这样。- +- + + + +MaxRcvBuffer:最大缓存的量; + +LastByteRead之后是已经接收了,但是还没被应用层读取的; + +NextByteExpected是第一部分和第二部分的分界线。 + + +第二部分的窗口有多大呢? + +NextByteExpected和LastByteRead的差其实是还没被应用层读取的部分占用掉的MaxRcvBuffer的量,我们定义为A。 + +AdvertisedWindow其实是MaxRcvBuffer减去A。 + +也就是:AdvertisedWindow=MaxRcvBuffer-((NextByteExpected-1)-LastByteRead)。 + +那第二部分和第三部分的分界线在哪里呢?NextByteExpected加AdvertisedWindow就是第二部分和第三部分的分界线,其实也就是LastByteRead加上MaxRcvBuffer。 + +其中第二部分里面,由于受到的包可能不是顺序的,会出现空档,只有和第一部分连续的,可以马上进行回复,中间空着的部分需要等待,哪怕后面的已经来了。 + +顺序问题与丢包问题 + +接下来我们结合一个例子来看。 + +还是刚才的图,在发送端来看,1、2、3已经发送并确认;4、5、6、7、8、9都是发送了还没确认;10、11、12是还没发出的;13、14、15是接收方没有空间,不准备发的。 + +在接收端来看,1、2、3、4、5是已经完成ACK,但是没读取的;6、7是等待接收的;8、9是已经接收,但是没有ACK的。 + +发送端和接收端当前的状态如下: + + +1、2、3没有问题,双方达成了一致。 + +4、5接收方说ACK了,但是发送方还没收到,有可能丢了,有可能在路上。 + +6、7、8、9肯定都发了,但是8、9已经到了,但是6、7没到,出现了乱序,缓存着但是没办法ACK。 + + +根据这个例子,我们可以知道,顺序问题和丢包问题都有可能发生,所以我们先来看确认与重发的机制。 + +假设4的确认到了,不幸的是,5的ACK丢了,6、7的数据包丢了,这该怎么办呢? + +一种方法就是超时重试,也即对每一个发送了,但是没有ACK的包,都有设一个定时器,超过了一定的时间,就重新尝试。但是这个超时的时间如何评估呢?这个时间不宜过短,时间必须大于往返时间RTT,否则会引起不必要的重传。也不宜过长,这样超时时间变长,访问就变慢了。 + +估计往返时间,需要TCP通过采样RTT的时间,然后进行加权平均,算出一个值,而且这个值还是要不断变化的,因为网络状况不断地变化。除了采样RTT,还要采样RTT的波动范围,计算出一个估计的超时时间。由于重传时间是不断变化的,我们称为自适应重传算法(Adaptive Retransmission Algorithm)。 + +如果过一段时间,5、6、7都超时了,就会重新发送。接收方发现5原来接收过,于是丢弃5;6收到了,发送ACK,要求下一个是7,7不幸又丢了。当7再次超时的时候,有需要重传的时候,TCP的策略是超时间隔加倍。每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。 + +超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢? + +有一个可以快速重传的机制,当接收方收到一个序号大于下一个所期望的报文段时,就会检测到数据流中的一个间隔,于是它就会发送冗余的ACK,仍然ACK的是期望接收的报文段。而当客户端收到三个冗余的ACK后,就会在定时器过期之前,重传丢失的报文段。 + +例如,接收方发现6收到了,8也收到了,但是7还没来,那肯定是丢了,于是发送6的ACK,要求下一个是7。接下来,收到后续的包,仍然发送6的ACK,要求下一个是7。当客户端收到3个重复ACK,就会发现7的确丢了,不等超时,马上重发。 + +还有一种方式称为Selective Acknowledgment (SACK)。这种方式需要在TCP头里加一个SACK的东西,可以将缓存的地图发送给发送方。例如可以发送ACK6、SACK8、SACK9,有了地图,发送方一下子就能看出来是7丢了。 + +流量控制问题 + +我们再来看流量控制机制,在对于包的确认中,同时会携带一个窗口的大小。 + +我们先假设窗口不变的情况,窗口始终为9。4的确认来的时候,会右移一个,这个时候第13个包也可以发送了。 + + + +这个时候,假设发送端发送过猛,会将第三部分的10、11、12、13全部发送完毕,之后就停止发送了,未发送可发送部分为0。 + + + +当对于包5的确认到达的时候,在客户端相当于窗口再滑动了一格,这个时候,才可以有更多的包可以发送了,例如第14个包才可以发送。 + + + +如果接收方实在处理的太慢,导致缓存中没有空间了,可以通过确认信息修改窗口的大小,甚至可以设置为0,则发送方将暂时停止发送。 + +我们假设一个极端情况,接收端的应用一直不读取缓存中的数据,当数据包6确认后,窗口大小就不能再是9了,就要缩小一个变为8。 + + + +这个新的窗口8通过6的确认消息到达发送端的时候,你会发现窗口没有平行右移,而是仅仅左面的边右移了,窗口的大小从9改成了8。 + + + +如果接收端还是一直不处理数据,则随着确认的包越来越多,窗口越来越小,直到为0。 + + + +当这个窗口通过包14的确认到达发送端的时候,发送端的窗口也调整为0,停止发送。 + +- +如果这样的话,发送方会定时发送窗口探测数据包,看是否有机会调整窗口的大小。当接收方比较慢的时候,要防止低能窗口综合征,别空出一个字节来就赶快告诉发送方,然后马上又填满了,可以当窗口太小的时候,不更新窗口,直到达到一定大小,或者缓冲区一半为空,才更新窗口。 + +这就是我们常说的流量控制。 + +拥塞控制问题 + +最后,我们看一下拥塞控制的问题,也是通过窗口的大小来控制的,前面的滑动窗口rwnd是怕发送方把接收方缓存塞满,而拥塞窗口cwnd,是怕把网络塞满。 + +这里有一个公式 LastByteSent - LastByteAcked <= min {cwnd, rwnd} ,是拥塞窗口和滑动窗口共同控制发送的速度。 + +那发送方怎么判断网络是不是慢呢?这其实是个挺难的事情,因为对于TCP协议来讲,他压根不知道整个网络路径都会经历什么,对他来讲就是一个黑盒。TCP发送包常被比喻为往一个水管里面灌水,而TCP的拥塞控制就是在不堵塞,不丢包的情况下,尽量发挥带宽。 + +水管有粗细,网络有带宽,也即每秒钟能够发送多少数据;水管有长度,端到端有时延。在理想状态下,水管里面水的量=水管粗细 x 水管长度。对于到网络上,通道的容量 = 带宽 × 往返延迟。 + +如果我们设置发送窗口,使得发送但未确认的包为为通道的容量,就能够撑满整个管道。 + +- +如图所示,假设往返时间为8s,去4s,回4s,每秒发送一个包,每个包1024byte。已经过去了8s,则8个包都发出去了,其中前4个包已经到达接收端,但是ACK还没有返回,不能算发送成功。5-8后四个包还在路上,还没被接收。这个时候,整个管道正好撑满,在发送端,已发送未确认的为8个包,正好等于带宽,也即每秒发送1个包,乘以来回时间8s。 + +如果我们在这个基础上再调大窗口,使得单位时间内更多的包可以发送,会出现什么现象呢? + +我们来想,原来发送一个包,从一端到达另一端,假设一共经过四个设备,每个设备处理一个包时间耗费1s,所以到达另一端需要耗费4s,如果发送的更加快速,则单位时间内,会有更多的包到达这些中间设备,这些设备还是只能每秒处理一个包的话,多出来的包就会被丢弃,这是我们不想看到的。 + +这个时候,我们可以想其他的办法,例如这个四个设备本来每秒处理一个包,但是我们在这些设备上加缓存,处理不过来的在队列里面排着,这样包就不会丢失,但是缺点是会增加时延,这个缓存的包,4s肯定到达不了接收端了,如果时延达到一定程度,就会超时重传,也是我们不想看到的。 + +于是TCP的拥塞控制主要来避免两种现象,包丢失和超时重传。一旦出现了这些现象就说明,发送速度太快了,要慢一点。但是一开始我怎么知道速度多快呢,我怎么知道应该把窗口调整到多大呢? + +如果我们通过漏斗往瓶子里灌水,我们就知道,不能一桶水一下子倒进去,肯定会溅出来,要一开始慢慢的倒,然后发现总能够倒进去,就可以越倒越快。这叫作慢启动。 + +一条TCP连接开始,cwnd设置为一个报文段,一次只能发送一个;当收到这一个确认的时候,cwnd加一,于是一次能够发送两个;当这两个的确认到来的时候,每个确认cwnd加一,两个确认cwnd加二,于是一次能够发送四个;当这四个的确认到来的时候,每个确认cwnd加一,四个确认cwnd加四,于是一次能够发送八个。可以看出这是指数性的增长。 + +涨到什么时候是个头呢?有一个值ssthresh为65535个字节,当超过这个值的时候,就要小心一点了,不能倒这么快了,可能快满了,再慢下来。 + +每收到一个确认后,cwnd增加1/cwnd,我们接着上面的过程来,一次发送八个,当八个确认到来的时候,每个确认增加1/8,八个确认一共cwnd增加1,于是一次能够发送九个,变成了线性增长。 + +但是线性增长还是增长,还是越来越多,直到有一天,水满则溢,出现了拥塞,这时候一般就会一下子降低倒水的速度,等待溢出的水慢慢渗下去。 + +拥塞的一种表现形式是丢包,需要超时重传,这个时候,将sshresh设为cwnd/2,将cwnd设为1,重新开始慢启动。这真是一旦超时重传,马上回到解放前。但是这种方式太激进了,将一个高速的传输速度一下子停了下来,会造成网络卡顿。 + +前面我们讲过快速重传算法。当接收端发现丢了一个中间包的时候,发送三次前一个包的ACK,于是发送端就会快速地重传,不必等待超时再重传。TCP认为这种情况不严重,因为大部分没丢,只丢了一小部分,cwnd减半为cwnd/2,然后sshthresh = cwnd,当三个包返回的时候,cwnd = sshthresh + 3,也就是没有一夜回到解放前,而是还在比较高的值,呈线性增长。 + + + +就像前面说的一样,正是这种知进退,使得时延很重要的情况下,反而降低了速度。但是如果你仔细想一下,TCP的拥塞控制主要来避免的两个现象都是有问题的。 + +第一个问题是丢包并不代表着通道满了,也可能是管子本来就漏水。例如公网上带宽不满也会丢包,这个时候就认为拥塞了,退缩了,其实是不对的。 + +第二个问题是TCP的拥塞控制要等到将中间设备都填充满了,才发生丢包,从而降低速度,这时候已经晚了。其实TCP只要填满管道就可以了,不应该接着填,直到连缓存也填满。 + +为了优化这两个问题,后来有了TCP BBR拥塞算法。它企图找到一个平衡点,就是通过不断地加快发送速度,将管道填满,但是不要填满中间设备的缓存,因为这样时延会增加,在这个平衡点可以很好的达到高带宽和低时延的平衡。 + + + +小结 + +好了,这一节我们就到这里,总结一下: + + +顺序问题、丢包问题、流量控制都是通过滑动窗口来解决的,这其实就相当于你领导和你的工作备忘录,布置过的工作要有编号,干完了有反馈,活不能派太多,也不能太少; + +拥塞控制是通过拥塞窗口来解决的,相当于往管道里面倒水,快了容易溢出,慢了浪费带宽,要摸着石头过河,找到最优值。 + + +最后留两个思考题: + + +TCP的BBR听起来很牛,你知道他是如何达到这个最优点的嘛? + +学会了UDP和TCP,你知道如何基于这两种协议写程序吗?这样的程序会有什么坑呢? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/13套接字Socket:Talkischeap,showmethecode.md b/专栏/趣谈网络协议/13套接字Socket:Talkischeap,showmethecode.md new file mode 100644 index 0000000..2bb0f82 --- /dev/null +++ b/专栏/趣谈网络协议/13套接字Socket:Talkischeap,showmethecode.md @@ -0,0 +1,150 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 套接字Socket:Talk is cheap, show me the code + 前面讲完了TCP和UDP协议,还没有上手过,这一节咱们讲讲基于TCP和UDP协议的Socket编程。 + +在讲TCP和UDP协议的时候,我们分客户端和服务端,在写程序的时候,我们也同样这样分。 + +Socket这个名字很有意思,可以作插口或者插槽讲。虽然我们是写软件程序,但是你可以想象为弄一根网线,一头插在客户端,一头插在服务端,然后进行通信。所以在通信之前,双方都要建立一个Socket。 + +在建立Socket的时候,应该设置什么参数呢?Socket编程进行的是端到端的通信,往往意识不到中间经过多少局域网,多少路由器,因而能够设置的参数,也只能是端到端协议之上网络层和传输层的。 + +在网络层,Socket函数需要指定到底是IPv4还是IPv6,分别对应设置为AF_INET和AF_INET6。另外,还要指定到底是TCP还是UDP。还记得咱们前面讲过的,TCP协议是基于数据流的,所以设置为SOCK_STREAM,而UDP是基于数据报的,因而设置为SOCK_DGRAM。 + +基于TCP协议的Socket程序函数调用过程 + +两端创建了Socket之后,接下来的过程中,TCP和UDP稍有不同,我们先来看TCP。 + +TCP的服务端要先监听一个端口,一般是先调用bind函数,给这个Socket赋予一个IP地址和端口。为什么需要端口呢?要知道,你写的是一个应用程序,当一个网络包来的时候,内核要通过TCP头里面的这个端口,来找到你这个应用程序,把包给你。为什么要IP地址呢?有时候,一台机器会有多个网卡,也就会有多个IP地址,你可以选择监听所有的网卡,也可以选择监听一个网卡,这样,只有发给这个网卡的包,才会给你。 + +当服务端有了IP和端口号,就可以调用listen函数进行监听。在TCP的状态图里面,有一个listen状态,当调用这个函数之后,服务端就进入了这个状态,这个时候客户端就可以发起连接了。 + +在内核中,为每个Socket维护两个队列。一个是已经建立了连接的队列,这时候连接三次握手已经完毕,处于established状态;一个是还没有完全建立连接的队列,这个时候三次握手还没完成,处于syn_rcvd的状态。 + +接下来,服务端调用accept函数,拿出一个已经完成的连接进行处理。如果还没有完成,就要等着。 + +在服务端等待的时候,客户端可以通过connect函数发起连接。先在参数中指明要连接的IP地址和端口号,然后开始发起三次握手。内核会给客户端分配一个临时的端口。一旦握手成功,服务端的accept就会返回另一个Socket。 + +这是一个经常考的知识点,就是监听的Socket和真正用来传数据的Socket是两个,一个叫作监听Socket,一个叫作已连接Socket。 + +连接建立成功之后,双方开始通过read和write函数来读写数据,就像往一个文件流里面写东西一样。 + +这个图就是基于TCP协议的Socket程序函数调用过程。 + + + +说TCP的Socket就是一个文件流,是非常准确的。因为,Socket在Linux中就是以文件的形式存在的。除此之外,还存在文件描述符。写入和读出,也是通过文件描述符。 + +在内核中,Socket是一个文件,那对应就有文件描述符。每一个进程都有一个数据结构task_struct,里面指向一个文件描述符数组,来列出这个进程打开的所有文件的文件描述符。文件描述符是一个整数,是这个数组的下标。 + +这个数组中的内容是一个指针,指向内核中所有打开的文件的列表。既然是一个文件,就会有一个inode,只不过Socket对应的inode不像真正的文件系统一样,保存在硬盘上的,而是在内存中的。在这个inode中,指向了Socket在内核中的Socket结构。 + +在这个结构里面,主要的是两个队列,一个是发送队列,一个是接收队列。在这两个队列里面保存的是一个缓存sk_buff。这个缓存里面能够看到完整的包的结构。看到这个,是不是能和前面讲过的收发包的场景联系起来了? + +整个数据结构我也画了一张图。- + + +基于UDP协议的Socket程序函数调用过程 + +对于UDP来讲,过程有些不一样。UDP是没有连接的,所以不需要三次握手,也就不需要调用listen和connect,但是,UDP的交互仍然需要IP和端口号,因而也需要bind。UDP是没有维护连接状态的,因而不需要每对连接建立一组Socket,而是只要有一个Socket,就能够和多个客户端通信。也正是因为没有连接状态,每次通信的时候,都调用sendto和recvfrom,都可以传入IP地址和端口。 + +这个图的内容就是基于UDP协议的Socket程序函数调用过程。- + + +服务器如何接更多的项目? + +会了这几个基本的Socket函数之后,你就可以轻松地写一个网络交互的程序了。就像上面的过程一样,在建立连接后,进行一个while循环。客户端发了收,服务端收了发。 + +当然这只是万里长征的第一步,因为如果使用这种方法,基本上只能一对一沟通。如果你是一个服务器,同时只能服务一个客户,肯定是不行的。这就相当于老板成立一个公司,只有自己一个人,自己亲自上来服务客户,只能干完了一家再干下一家,这样赚不来多少钱。 + +那作为老板你就要想了,我最多能接多少项目呢?当然是越多越好。 + +我们先来算一下理论值,也就是最大连接数,系统会用一个四元组来标识一个TCP连接。 + +{本机IP, 本机端口, 对端IP, 对端端口} + + +服务器通常固定在某个本地端口上监听,等待客户端的连接请求。因此,服务端端TCP连接四元组中只有对端IP, 也就是客户端的IP和对端的端口,也即客户端的端口是可变的,因此,最大TCP连接数=客户端IP数×客户端端口数。对IPv4,客户端的IP数最多为2的32次方,客户端的端口数最多为2的16次方,也就是服务端单机最大TCP连接数,约为2的48次方。 + +当然,服务端最大并发TCP连接数远不能达到理论上限。首先主要是文件描述符限制,按照上面的原理,Socket都是文件,所以首先要通过ulimit配置文件描述符的数目;另一个限制是内存,按上面的数据结构,每个TCP连接都要占用一定内存,操作系统是有限的。 + +所以,作为老板,在资源有限的情况下,要想接更多的项目,就需要降低每个项目消耗的资源数目。 + +方式一:将项目外包给其他公司(多进程方式) + +这就相当于你是一个代理,在那里监听来的请求。一旦建立了一个连接,就会有一个已连接Socket,这时候你可以创建一个子进程,然后将基于已连接Socket的交互交给这个新的子进程来做。就像来了一个新的项目,但是项目不一定是你自己做,可以再注册一家子公司,招点人,然后把项目转包给这家子公司做,以后对接就交给这家子公司了,你又可以去接新的项目了。 + +这里有一个问题是,如何创建子公司,并如何将项目移交给子公司呢? + +在Linux下,创建子进程使用fork函数。通过名字可以看出,这是在父进程的基础上完全拷贝一个子进程。在Linux内核中,会复制文件描述符的列表,也会复制内存空间,还会复制一条记录当前执行到了哪一行程序的进程。显然,复制的时候在调用fork,复制完毕之后,父进程和子进程都会记录当前刚刚执行完fork。这两个进程刚复制完的时候,几乎一模一样,只是根据fork的返回值来区分到底是父进程,还是子进程。如果返回值是0,则是子进程;如果返回值是其他的整数,就是父进程。 + +进程复制过程我画在这里。- + + +因为复制了文件描述符列表,而文件描述符都是指向整个内核统一的打开文件列表的,因而父进程刚才因为accept创建的已连接Socket也是一个文件描述符,同样也会被子进程获得。 + +接下来,子进程就可以通过这个已连接Socket和客户端进行互通了,当通信完毕之后,就可以退出进程,那父进程如何知道子进程干完了项目,要退出呢?还记得fork返回的时候,如果是整数就是父进程吗?这个整数就是子进程的ID,父进程可以通过这个ID查看子进程是否完成项目,是否需要退出。 + +方式二:将项目转包给独立的项目组(多线程方式) + +上面这种方式你应该也能发现问题,如果每次接一个项目,都申请一个新公司,然后干完了,就注销掉这个公司,实在是太麻烦了。毕竟一个新公司要有新公司的资产,有新的办公家具,每次都买了再卖,不划算。 + +于是你应该想到了,我们可以使用线程。相比于进程来讲,这样要轻量级的多。如果创建进程相当于成立新公司,购买新办公家具,而创建线程,就相当于在同一个公司成立项目组。一个项目做完了,那这个项目组就可以解散,组成另外的项目组,办公家具可以共用。 + +在Linux下,通过pthread_create创建一个线程,也是调用do_fork。不同的是,虽然新的线程在task列表会新创建一项,但是很多资源,例如文件描述符列表、进程空间,还是共享的,只不过多了一个引用而已。 + + + +新的线程也可以通过已连接Socket处理请求,从而达到并发处理的目的。 + +上面基于进程或者线程模型的,其实还是有问题的。新到来一个TCP连接,就需要分配一个进程或者线程。一台机器无法创建很多进程或者线程。有个C10K,它的意思是一台机器要维护1万个连接,就要创建1万个进程或者线程,那么操作系统是无法承受的。如果维持1亿用户在线需要10万台服务器,成本也太高了。 + +其实C10K问题就是,你接项目接的太多了,如果每个项目都成立单独的项目组,就要招聘10万人,你肯定养不起,那怎么办呢? + +方式三:一个项目组支撑多个项目(IO多路复用,一个线程维护多个Socket) + +当然,一个项目组可以看多个项目了。这个时候,每个项目组都应该有个项目进度墙,将自己组看的项目列在那里,然后每天通过项目墙看每个项目的进度,一旦某个项目有了进展,就派人去盯一下。 + +由于Socket是文件描述符,因而某个线程盯的所有的Socket,都放在一个文件描述符集合fd_set中,这就是项目进度墙,然后调用select函数来监听文件描述符集合是否有变化。一旦有变化,就会依次查看每个文件描述符。那些发生变化的文件描述符在fd_set对应的位都设为1,表示Socket可读或者可写,从而可以进行读写操作,然后再调用select,接着盯着下一轮的变化。 + +方式四:一个项目组支撑多个项目(IO多路复用,从“派人盯着”到“有事通知”) + +上面select函数还是有问题的,因为每次Socket所在的文件描述符集合中有Socket发生变化的时候,都需要通过轮询的方式,也就是需要将全部项目都过一遍的方式来查看进度,这大大影响了一个项目组能够支撑的最大的项目数量。因而使用select,能够同时盯的项目数量由FD_SETSIZE限制。 + +如果改成事件通知的方式,情况就会好很多,项目组不需要通过轮询挨个盯着这些项目,而是当项目进度发生变化的时候,主动通知项目组,然后项目组再根据项目进展情况做相应的操作。 + +能完成这件事情的函数叫epoll,它在内核中的实现不是通过轮询的方式,而是通过注册callback函数的方式,当某个文件描述符发送变化的时候,就会主动通知。- + + +如图所示,假设进程打开了Socket m, n, x等多个文件描述符,现在需要通过epoll来监听是否这些Socket都有事件发生。其中epoll_create创建一个epoll对象,也是一个文件,也对应一个文件描述符,同样也对应着打开文件列表中的一项。在这项里面有一个红黑树,在红黑树里,要保存这个epoll要监听的所有Socket。 + +当epoll_ctl添加一个Socket的时候,其实是加入这个红黑树,同时红黑树里面的节点指向一个结构,将这个结构挂在被监听的Socket的事件列表中。当一个Socket来了一个事件的时候,可以从这个列表中得到epoll对象,并调用call back通知它。 + +这种通知方式使得监听的Socket数据增加的时候,效率不会大幅度降低,能够同时监听的Socket的数目也非常的多了。上限就为系统定义的、进程打开的最大文件描述符个数。因而,epoll被称为解决C10K问题的利器。 + +小结 + +好了,这一节就到这里了,我们来总结一下: + + +你需要记住TCP和UDP的Socket的编程中,客户端和服务端都需要调用哪些函数; + +写一个能够支撑大量连接的高并发的服务端不容易,需要多进程、多线程,而epoll机制能解决C10K问题。 + + +最后,给你留两个思考题: + + +epoll是Linux上的函数,那你知道Windows上对应的机制是什么吗?如果想实现一个跨平台的程序,你知道应该怎么办吗? + +自己写Socket还是挺复杂的,写个HTTP的应用可能简单一些。那你知道HTTP的工作机制吗? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/14HTTP协议:看个新闻原来这么麻烦.md b/专栏/趣谈网络协议/14HTTP协议:看个新闻原来这么麻烦.md new file mode 100644 index 0000000..3f5a4df --- /dev/null +++ b/专栏/趣谈网络协议/14HTTP协议:看个新闻原来这么麻烦.md @@ -0,0 +1,221 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 HTTP协议:看个新闻原来这么麻烦 + 前面讲述完传输层,接下来开始讲应用层的协议。从哪里开始讲呢,就从咱们最常用的HTTP协议开始。 + +HTTP协议,几乎是每个人上网用的第一个协议,同时也是很容易被人忽略的协议。 + +既然说看新闻,咱们就先登录 http://www.163.com 。 + +http://www.163.com 是个URL,叫作统一资源定位符。之所以叫统一,是因为它是有格式的。HTTP称为协议,www.163.com是一个域名,表示互联网上的一个位置。有的URL会有更详细的位置标识,例如 http://www.163.com/index.html 。正是因为这个东西是统一的,所以当你把这样一个字符串输入到浏览器的框里的时候,浏览器才知道如何进行统一处理。 + +HTTP请求的准备 + +浏览器会将www.163.com这个域名发送给DNS服务器,让它解析为IP地址。有关DNS的过程,其实非常复杂,这个在后面专门介绍DNS的时候,我会详细描述,这里我们先不管,反正它会被解析成为IP地址。那接下来是发送HTTP请求吗? + +不是的,HTTP是基于TCP协议的,当然是要先建立TCP连接了,怎么建立呢?还记得第11节讲过的三次握手吗? + +目前使用的HTTP协议大部分都是1.1。在1.1的协议里面,默认是开启了Keep-Alive的,这样建立的TCP连接,就可以在多次请求中复用。 + +学习了TCP之后,你应该知道,TCP的三次握手和四次挥手,还是挺费劲的。如果好不容易建立了连接,然后就做了一点儿事情就结束了,有点儿浪费人力和物力。 + +HTTP请求的构建 + +建立了连接以后,浏览器就要发送HTTP的请求。 + +请求的格式就像这样。 + + + +HTTP的报文大概分为三大部分。第一部分是请求行,第二部分是请求的首部,第三部分才是请求的正文实体。 + +第一部分:请求行 + +在请求行中,URL就是 http://www.163.com ,版本为HTTP 1.1。这里要说一下的,就是方法。方法有几种类型。 + +对于访问网页来讲,最常用的类型就是GET。顾名思义,GET就是去服务器获取一些资源。对于访问网页来讲,要获取的资源往往是一个页面。其实也有很多其他的格式,比如说返回一个JSON字符串,到底要返回什么,是由服务器端的实现决定的。 + +例如,在云计算中,如果我们的服务器端要提供一个基于HTTP协议的API,获取所有云主机的列表,这就会使用GET方法得到,返回的可能是一个JSON字符串。字符串里面是一个列表,列表里面是一项的云主机的信息。 + +另外一种类型叫做POST。它需要主动告诉服务端一些信息,而非获取。要告诉服务端什么呢?一般会放在正文里面。正文可以有各种各样的格式。常见的格式也是JSON。 + +例如,我们下一节要讲的支付场景,客户端就需要把“我是谁?我要支付多少?我要买啥?”告诉服务器,这就需要通过POST方法。 + +再如,在云计算里,如果我们的服务器端,要提供一个基于HTTP协议的创建云主机的API,也会用到POST方法。这个时候往往需要将“我要创建多大的云主机?多少CPU多少内存?多大硬盘?”这些信息放在JSON字符串里面,通过POST的方法告诉服务器端。 + +还有一种类型叫PUT,就是向指定资源位置上传最新内容。但是,HTTP的服务器往往是不允许上传文件的,所以PUT和POST就都变成了要传给服务器东西的方法。 + +在实际使用过程中,这两者还会有稍许的区别。POST往往是用来创建一个资源的,而PUT往往是用来修改一个资源的。 + +例如,云主机已经创建好了,我想对这个云主机打一个标签,说明这个云主机是生产环境的,另外一个云主机是测试环境的。那怎么修改这个标签呢?往往就是用PUT方法。 + +再有一种常见的就是DELETE。这个顾名思义就是用来删除资源的。例如,我们要删除一个云主机,就会调用DELETE方法。 + +第二部分:首部字段 + +请求行下面就是我们的首部字段。首部是key value,通过冒号分隔。这里面,往往保存了一些非常重要的字段。 + +例如,Accept-Charset,表示客户端可以接受的字符集。防止传过来的是另外的字符集,从而导致出现乱码。 + +再如,Content-Type是指正文的格式。例如,我们进行POST的请求,如果正文是JSON,那么我们就应该将这个值设置为JSON。 + +这里需要重点说一下的就是缓存。为啥要使用缓存呢?那是因为一个非常大的页面有很多东西。 + +例如,我浏览一个商品的详情,里面有这个商品的价格、库存、展示图片、使用手册等等。商品的展示图片会保持较长时间不变,而库存会根据用户购买的情况经常改变。如果图片非常大,而库存数非常小,如果我们每次要更新数据的时候都要刷新整个页面,对于服务器的压力就会很大。 + +对于这种高并发场景下的系统,在真正的业务逻辑之前,都需要有个接入层,将这些静态资源的请求拦在最外面。 + +这个架构的图就像这样。 + + + +其中DNS、CDN我在后面的章节会讲。和这一节关系比较大的就是Nginx这一层,它如何处理HTTP协议呢?对于静态资源,有Vanish缓存层。当缓存过期的时候,才会访问真正的Tomcat应用集群。 + +在HTTP头里面,Cache-control是用来控制缓存的。当客户端发送的请求中包含max-age指令时,如果判定缓存层中,资源的缓存时间数值比指定时间的数值小,那么客户端可以接受缓存的资源;当指定max-age值为0,那么缓存层通常需要将请求转发给应用集群。 + +另外,If-Modified-Since也是一个关于缓存的。也就是说,如果服务器的资源在某个时间之后更新了,那么客户端就应该下载最新的资源;如果没有更新,服务端会返回“304 Not Modified”的响应,那客户端就不用下载了,也会节省带宽。 + +到此为止,我们仅仅是拼凑起了HTTP请求的报文格式,接下来,浏览器会把它交给下一层传输层。怎么交给传输层呢?其实也无非是用Socket这些东西,只不过用的浏览器里,这些程序不需要你自己写,有人已经帮你写好了。 + +HTTP请求的发送 + +HTTP协议是基于TCP协议的,所以它使用面向连接的方式发送请求,通过stream二进制流的方式传给对方。当然,到了TCP层,它会把二进制流变成一个个报文段发送给服务器。 + +在发送给每个报文段的时候,都需要对方有一个回应ACK,来保证报文可靠地到达了对方。如果没有回应,那么TCP这一层会进行重新传输,直到可以到达。同一个包有可能被传了好多次,但是HTTP这一层不需要知道这一点,因为是TCP这一层在埋头苦干。 + +TCP层发送每一个报文的时候,都需要加上自己的地址(即源地址)和它想要去的地方(即目标地址),将这两个信息放到IP头里面,交给IP层进行传输。 + +IP层需要查看目标地址和自己是否是在同一个局域网。如果是,就发送ARP协议来请求这个目标地址对应的MAC地址,然后将源MAC和目标MAC放入MAC头,发送出去即可;如果不在同一个局域网,就需要发送到网关,还要需要发送ARP协议,来获取网关的MAC地址,然后将源MAC和网关MAC放入MAC头,发送出去。 + +网关收到包发现MAC符合,取出目标IP地址,根据路由协议找到下一跳的路由器,获取下一跳路由器的MAC地址,将包发给下一跳路由器。 + +这样路由器一跳一跳终于到达目标的局域网。这个时候,最后一跳的路由器能够发现,目标地址就在自己的某一个出口的局域网上。于是,在这个局域网上发送ARP,获得这个目标地址的MAC地址,将包发出去。 + +目标的机器发现MAC地址符合,就将包收起来;发现IP地址符合,根据IP头中协议项,知道自己上一层是TCP协议,于是解析TCP的头,里面有序列号,需要看一看这个序列包是不是我要的,如果是就放入缓存中然后返回一个ACK,如果不是就丢弃。 + +TCP头里面还有端口号,HTTP的服务器正在监听这个端口号。于是,目标机器自然知道是HTTP服务器这个进程想要这个包,于是将包发给HTTP服务器。HTTP服务器的进程看到,原来这个请求是要访问一个网页,于是就把这个网页发给客户端。 + +HTTP返回的构建 + +HTTP的返回报文也是有一定格式的。这也是基于HTTP 1.1的。 + + + +状态码会反映HTTP请求的结果。“200”意味着大吉大利;而我们最不想见的,就是“404”,也就是“服务端无法响应这个请求”。然后,短语会大概说一下原因。 + +接下来是返回首部的key value。 + +这里面,Retry-After表示,告诉客户端应该在多长时间以后再次尝试一下。“503错误”是说“服务暂时不再和这个值配合使用”。 + +在返回的头部里面也会有Content-Type,表示返回的是HTML,还是JSON。 + +构造好了返回的HTTP报文,接下来就是把这个报文发送出去。还是交给Socket去发送,还是交给TCP层,让TCP层将返回的HTML,也分成一个个小的段,并且保证每个段都可靠到达。 + +这些段加上TCP头后会交给IP层,然后把刚才的发送过程反向走一遍。虽然两次不一定走相同的路径,但是逻辑过程是一样的,一直到达客户端。 + +客户端发现MAC地址符合、IP地址符合,于是就会交给TCP层。根据序列号看是不是自己要的报文段,如果是,则会根据TCP头中的端口号,发给相应的进程。这个进程就是浏览器,浏览器作为客户端也在监听某个端口。 + +当浏览器拿到了HTTP的报文。发现返回“200”,一切正常,于是就从正文中将HTML拿出来。HTML是一个标准的网页格式。浏览器只要根据这个格式,展示出一个绚丽多彩的网页。 + +这就是一个正常的HTTP请求和返回的完整过程。 + +HTTP 2.0 + +当然HTTP协议也在不断的进化过程中,在HTTP1.1基础上便有了HTTP 2.0。 + +HTTP 1.1在应用层以纯文本的形式进行通信。每次通信都要带完整的HTTP的头,而且不考虑pipeline模式的话,每次的过程总是像上面描述的那样一去一回。这样在实时性、并发性上都存在问题。 + +为了解决这些问题,HTTP 2.0会对HTTP的头进行一定的压缩,将原来每次都要携带的大量key value在两端建立一个索引表,对相同的头只发送索引表中的索引。 + +另外,HTTP 2.0协议将一个TCP的连接中,切分成多个流,每个流都有自己的ID,而且流可以是客户端发往服务端,也可以是服务端发往客户端。它其实只是一个虚拟的通道。流是有优先级的。 + +HTTP 2.0还将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。常见的帧有Header帧,用于传输Header内容,并且会开启一个新的流。再就是Data帧,用来传输正文实体。多个Data帧属于同一个流。 + +通过这两种机制,HTTP 2.0的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,并且可以根据优先级,决定优先处理哪个流的数据。 + +我们来举一个例子。 + +假设我们的一个页面要发送三个独立的请求,一个获取css,一个获取js,一个获取图片jpg。如果使用HTTP 1.1就是串行的,但是如果使用HTTP 2.0,就可以在一个连接里,客户端和服务端都可以同时发送多个请求或回应,而且不用按照顺序一对一对应。 + + + +HTTP 2.0其实是将三个请求变成三个流,将数据分成帧,乱序发送到一个TCP连接中。 + + + +HTTP 2.0成功解决了HTTP 1.1的队首阻塞问题,同时,也不需要通过HTTP 1.x的pipeline机制用多条TCP连接来实现并行请求与响应;减少了TCP连接数对服务器性能的影响,同时将页面的多个数据css、js、 jpg等通过一个数据链接进行传输,能够加快页面组件的传输速度。 + +QUIC协议的“城会玩” + +HTTP 2.0虽然大大增加了并发性,但还是有问题的。因为HTTP 2.0也是基于TCP协议的,TCP协议在处理包时是有严格顺序的。 + +当其中一个数据包遇到问题,TCP连接需要等待这个包完成重传之后才能继续进行。虽然HTTP 2.0通过多个stream,使得逻辑上一个TCP连接上的并行内容,进行多路数据的传输,然而这中间并没有关联的数据。一前一后,前面stream 2的帧没有收到,后面stream 1的帧也会因此阻塞。 + +于是,就又到了从TCP切换到UDP,进行“城会玩”的时候了。这就是Google的QUIC协议,接下来我们来看它是如何“城会玩”的。 + +机制一:自定义连接机制 + +我们都知道,一条TCP连接是由四元组标识的,分别是源 IP、源端口、目的 IP、目的端口。一旦一个元素发生变化时,就需要断开重连,重新连接。在移动互联情况下,当手机信号不稳定或者在WIFI和 移动网络切换时,都会导致重连,从而进行再次的三次握手,导致一定的时延。 + +这在TCP是没有办法的,但是基于UDP,就可以在QUIC自己的逻辑里面维护连接的机制,不再以四元组标识,而是以一个64位的随机数作为ID来标识,而且UDP是无连接的,所以当IP或者端口变化的时候,只要ID不变,就不需要重新建立连接。 + +机制二:自定义重传机制 + +前面我们讲过,TCP为了保证可靠性,通过使用序号和应答机制,来解决顺序问题和丢包问题。 + +任何一个序号的包发过去,都要在一定的时间内得到应答,否则一旦超时,就会重发这个序号的包。那怎么样才算超时呢?还记得我们提过的自适应重传算法吗?这个超时是通过采样往返时间RTT不断调整的。 + +其实,在TCP里面超时的采样存在不准确的问题。例如,发送一个包,序号为100,发现没有返回,于是再发送一个100,过一阵返回一个ACK101。这个时候客户端知道这个包肯定收到了,但是往返时间是多少呢?是ACK到达的时间减去后一个100发送的时间,还是减去前一个100发送的时间呢?事实是,第一种算法把时间算短了,第二种算法把时间算长了。 + +QUIC也有个序列号,是递增的。任何一个序列号的包只发送一次,下次就要加一了。例如,发送一个包,序号是100,发现没有返回;再次发送的时候,序号就是101了;如果返回的ACK 100,就是对第一个包的响应。如果返回ACK 101就是对第二个包的响应,RTT计算相对准确。 + +但是这里有一个问题,就是怎么知道包100和包101发送的是同样的内容呢?QUIC定义了一个offset概念。QUIC既然是面向连接的,也就像TCP一样,是一个数据流,发送的数据在这个数据流里面有个偏移量offset,可以通过offset查看数据发送到了哪里,这样只要这个offset的包没有来,就要重发;如果来了,按照offset拼接,还是能够拼成一个流。 + + + +机制三:无阻塞的多路复用 + +有了自定义的连接和重传机制,我们就可以解决上面HTTP 2.0的多路复用问题。 + +同HTTP 2.0一样,同一条QUIC连接上可以创建多个stream,来发送多个 HTTP 请求。但是,QUIC是基于UDP的,一个连接上的多个stream之间没有依赖。这样,假如stream2丢了一个UDP包,后面跟着stream3的一个UDP包,虽然stream2的那个包需要重传,但是stream3的包无需等待,就可以发给用户。 + +机制四:自定义流量控制 + +TCP的流量控制是通过滑动窗口协议。QUIC的流量控制也是通过window_update,来告诉对端它可以接受的字节数。但是QUIC的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个stream控制窗口。 + +还记得吗?在TCP协议中,接收端的窗口的起始点是下一个要接收并且ACK的包,即便后来的包都到了,放在缓存里面,窗口也不能右移,因为TCP的ACK机制是基于序列号的累计应答,一旦ACK了一个序列号,就说明前面的都到了,所以只要前面的没到,后面的到了也不能ACK,就会导致后面的到了,也有可能超时重传,浪费带宽。 + +QUIC的ACK是基于offset的,每个offset的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空档会等待到来或者重发即可,而窗口的起始位置为当前收到的最大offset,从这个offset到当前的stream所能容纳的最大缓存,是真正的窗口大小。显然,这样更加准确。 + + + +另外,还有整个连接的窗口,需要对于所有的stream的窗口做一个统计。 + +小结 + +好了,今天就讲到这里,我们来总结一下: + + +HTTP协议虽然很常用,也很复杂,重点记住GET、POST、 PUT、DELETE这几个方法,以及重要的首部字段; + +HTTP 2.0通过头压缩、分帧、二进制编码、多路复用等技术提升性能; + +QUIC协议通过基于UDP自定义的类似TCP的连接、重试、多路复用、流量控制技术,进一步提升性能。 + + +接下来,给你留两个思考题吧。 + + +QUIC是一个精巧的协议,所以它肯定不止今天我提到的四种机制,你知道它还有哪些吗? + +这一节主要讲了如何基于HTTP浏览网页,如果要传输比较敏感的银行卡信息,该怎么办呢? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/15HTTPS协议:点外卖的过程原来这么复杂.md b/专栏/趣谈网络协议/15HTTPS协议:点外卖的过程原来这么复杂.md new file mode 100644 index 0000000..213de0f --- /dev/null +++ b/专栏/趣谈网络协议/15HTTPS协议:点外卖的过程原来这么复杂.md @@ -0,0 +1,166 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 15 HTTPS协议:点外卖的过程原来这么复杂 + 用HTTP协议,看个新闻还没有问题,但是换到更加严肃的场景中,就存在很多的安全风险。例如,你要下单做一次支付,如果还是使用普通的HTTP协议,那你很可能会被黑客盯上。 + +你发送一个请求,说我要点个外卖,但是这个网络包被截获了,于是在服务器回复你之前,黑客先假装自己就是外卖网站,然后给你回复一个假的消息说:“好啊好啊,来来来,银行卡号、密码拿来。”如果这时候你真把银行卡密码发给它,那你就真的上套了。 + +那怎么解决这个问题呢?当然一般的思路就是加密。加密分为两种方式一种是对称加密,一种是非对称加密。 + +在对称加密算法中,加密和解密使用的密钥是相同的。也就是说,加密和解密使用的是同一个密钥。因此,对称加密算法要保证安全性的话,密钥要做好保密。只能让使用的人知道,不能对外公开。 + +在非对称加密算法中,加密使用的密钥和解密使用的密钥是不相同的。一把是作为公开的公钥,另一把是作为谁都不能给的私钥。公钥加密的信息,只有私钥才能解密。私钥加密的信息,只有公钥才能解密。 + +因为对称加密算法相比非对称加密算法来说,效率要高得多,性能也好,所以交互的场景下多用对称加密。 + +对称加密 + +假设你和外卖网站约定了一个密钥,你发送请求的时候用这个密钥进行加密,外卖网站用同样的密钥进行解密。这样就算中间的黑客截获了你的请求,但是它没有密钥,还是破解不了。 + +这看起来很完美,但是中间有个问题,你们两个怎么来约定这个密钥呢?如果这个密钥在互联网上传输,也是很有可能让黑客截获的。黑客一旦截获这个秘钥,它可以佯作不知,静静地等着你们两个交互。这时候你们之间互通的任何消息,它都能截获并且查看,就等你把银行卡账号和密码发出来。 + +我们在谍战剧里面经常看到这样的场景,就是特工破译的密码会有个密码本,截获无线电台,通过密码本就能将原文破解出来。怎么把密码本给对方呢?只能通过线下传输。 + +比如,你和外卖网站偷偷约定时间地点,它给你一个纸条,上面写着你们两个的密钥,然后说以后就用这个密钥在互联网上定外卖了。当然你们接头的时候,也会先约定一个口号,什么“天王盖地虎”之类的,口号对上了,才能把纸条给它。但是,“天王盖地虎”同样也是对称加密密钥,同样存在如何把“天王盖地虎”约定成口号的问题。而且在谍战剧中一对一接头可能还可以,在互联网应用中,客户太多,这样是不行的。 + +非对称加密 + +所以,只要是对称加密,就会永远在这个死循环里出不来,这个时候,就需要非对称加密介入进来。 + +非对称加密的私钥放在外卖网站这里,不会在互联网上传输,这样就能保证这个密钥的私密性。但是,对应私钥的公钥,是可以在互联网上随意传播的,只要外卖网站把这个公钥给你,你们就可以愉快地互通了。 + +比如说你用公钥加密,说“我要定外卖”,黑客在中间就算截获了这个报文,因为它没有私钥也是解不开的,所以这个报文可以顺利到达外卖网站,外卖网站用私钥把这个报文解出来,然后回复,“那给我银行卡和支付密码吧”。 + +先别太乐观,这里还是有问题的。回复的这句话,是外卖网站拿私钥加密的,互联网上人人都可以把它打开,当然包括黑客。那外卖网站可以拿公钥加密吗?当然不能,因为它自己的私钥只有它自己知道,谁也解不开。 + +另外,这个过程还有一个问题,黑客也可以模拟发送“我要定外卖”这个过程的,因为它也有外卖网站的公钥。 + +为了解决这个问题,看来一对公钥私钥是不够的,客户端也需要有自己的公钥和私钥,并且客户端要把自己的公钥,给外卖网站。 + +这样,客户端给外卖网站发送的时候,用外卖网站的公钥加密。而外卖网站给客户端发送消息的时候,使用客户端的公钥。这样就算有黑客企图模拟客户端获取一些信息,或者半路截获回复信息,但是由于它没有私钥,这些信息它还是打不开。 + +数字证书 + +不对称加密也会有同样的问题,如何将不对称加密的公钥给对方呢?一种是放在一个公网的地址上,让对方下载;另一种就是在建立连接的时候,传给对方。 + +这两种方法有相同的问题,那就是,作为一个普通网民,你怎么鉴别别人给你的公钥是对的。会不会有人冒充外卖网站,发给你一个它的公钥。接下来,你和它所有的互通,看起来都是没有任何问题的。毕竟每个人都可以创建自己的公钥和私钥。 + +例如,我自己搭建了一个网站cliu8site,可以通过这个命令先创建私钥。 + +openssl genrsa -out cliu8siteprivate.key 1024 + + +然后,再根据这个私钥,创建对应的公钥。 + +openssl rsa -in cliu8siteprivate.key -pubout -outcliu8sitepublic.pem + + +这个时候就需要权威部门的介入了,就像每个人都可以打印自己的简历,说自己是谁,但是有公安局盖章的,就只有户口本,这个才能证明你是你。这个由权威部门颁发的称为证书(Certificate)。 + +证书里面有什么呢?当然应该有公钥,这是最重要的;还有证书的所有者,就像户口本上有你的姓名和身份证号,说明这个户口本是你的;另外还有证书的发布机构和证书的有效期,这个有点像身份证上的机构是哪个区公安局,有效期到多少年。 + +这个证书是怎么生成的呢?会不会有人假冒权威机构颁发证书呢?就像有假身份证、假户口本一样。生成证书需要发起一个证书请求,然后将这个请求发给一个权威机构去认证,这个权威机构我们称为CA( Certificate Authority)。 + +证书请求可以通过这个命令生成。 + +openssl req -key cliu8siteprivate.key -new -out cliu8sitecertificate.req + + +将这个请求发给权威机构,权威机构会给这个证书卡一个章,我们称为签名算法。问题又来了,那怎么签名才能保证是真的权威机构签名的呢?当然只有用只掌握在权威机构手里的东西签名了才行,这就是CA的私钥。 + +签名算法大概是这样工作的:一般是对信息做一个Hash计算,得到一个Hash值,这个过程是不可逆的,也就是说无法通过Hash值得出原来的信息内容。在把信息发送出去时,把这个Hash值加密后,作为一个签名和信息一起发出去。 + +权威机构给证书签名的命令是这样的。 + +openssl x509 -req -in cliu8sitecertificate.req -CA cacertificate.pem -CAkey caprivate.key -out cliu8sitecertificate.pem + + +这个命令会返回Signature ok,而cliu8sitecertificate.pem就是签过名的证书。CA用自己的私钥给外卖网站的公钥签名,就相当于给外卖网站背书,形成了外卖网站的证书。 + +我们来查看这个证书的内容。 + +openssl x509 -in cliu8sitecertificate.pem -noout -text + + +这里面有个Issuer,也即证书是谁颁发的;Subject,就是证书颁发给谁;Validity是证书期限;Public-key是公钥内容;Signature Algorithm是签名算法。 + +这下好了,你不会从外卖网站上得到一个公钥,而是会得到一个证书,这个证书有个发布机构CA,你只要得到这个发布机构CA的公钥,去解密外卖网站证书的签名,如果解密成功了,Hash也对的上,就说明这个外卖网站的公钥没有啥问题。 + +你有没有发现,又有新问题了。要想验证证书,需要CA的公钥,问题是,你怎么确定CA的公钥就是对的呢? + +所以,CA的公钥也需要更牛的CA给它签名,然后形成CA的证书。要想知道某个CA的证书是否可靠,要看CA的上级证书的公钥,能不能解开这个CA的签名。就像你不相信区公安局,可以打电话问市公安局,让市公安局确认区公安局的合法性。这样层层上去,直到全球皆知的几个著名大CA,称为root CA,做最后的背书。通过这种层层授信背书的方式,从而保证了非对称加密模式的正常运转。 + +除此之外,还有一种证书,称为Self-Signed Certificate,就是自己给自己签名。这个给人一种“我就是我,你爱信不信”的感觉。这里我就不多说了。 + +HTTPS的工作模式 + +我们可以知道,非对称加密在性能上不如对称加密,那是否能将两者结合起来呢?例如,公钥私钥主要用于传输对称加密的秘钥,而真正的双方大数据量的通信都是通过对称加密进行的。 + +当然是可以的。这就是HTTPS协议的总体思路。 + + + +当你登录一个外卖网站的时候,由于是HTTPS,客户端会发送Client Hello消息到服务器,以明文传输TLS版本信息、加密套件候选列表、压缩算法候选列表等信息。另外,还会有一个随机数,在协商对称密钥的时候使用。 + +这就类似在说:“您好,我想定外卖,但你要保密我吃的是什么。这是我的加密套路,再给你个随机数,你留着。” + +然后,外卖网站返回Server Hello消息, 告诉客户端,服务器选择使用的协议版本、加密套件、压缩算法等,还有一个随机数,用于后续的密钥协商。 + +这就类似在说:“您好,保密没问题,你的加密套路还挺多,咱们就按套路2来吧,我这里也有个随机数,你也留着。” + +然后,外卖网站会给你一个服务器端的证书,然后说:“Server Hello Done,我这里就这些信息了。” + +你当然不相信这个证书,于是你从自己信任的CA仓库中,拿CA的证书里面的公钥去解密外卖网站的证书。如果能够成功,则说明外卖网站是可信的。这个过程中,你可能会不断往上追溯CA、CA的CA、CA的CA的CA,反正直到一个授信的CA,就可以了。 + +证书验证完毕之后,觉得这个外卖网站可信,于是客户端计算产生随机数字Pre-master,发送Client Key Exchange,用证书中的公钥加密,再发送给服务器,服务器可以通过私钥解密出来。 + +到目前为止,无论是客户端还是服务器,都有了三个随机数,分别是:自己的、对端的,以及刚生成的Pre-Master随机数。通过这三个随机数,可以在客户端和服务器产生相同的对称密钥。 + +有了对称密钥,客户端就可以说:“Change Cipher Spec,咱们以后都采用协商的通信密钥和加密算法进行加密通信了。” + +然后发送一个Encrypted Handshake Message,将已经商定好的参数等,采用协商密钥进行加密,发送给服务器用于数据与握手验证。 + +同样,服务器也可以发送Change Cipher Spec,说:“没问题,咱们以后都采用协商的通信密钥和加密算法进行加密通信了”,并且也发送Encrypted Handshake Message的消息试试。当双方握手结束之后,就可以通过对称密钥进行加密传输了。 + +这个过程除了加密解密之外,其他的过程和HTTP是一样的,过程也非常复杂。 + +上面的过程只包含了HTTPS的单向认证,也即客户端验证服务端的证书,是大部分的场景,也可以在更加严格安全要求的情况下,启用双向认证,双方互相验证证书。 + +重放与篡改 + +其实,这里还有一些没有解决的问题,例如重放和篡改的问题。 + +没错,有了加密和解密,黑客截获了包也打不开了,但是它可以发送N次。这个往往通过Timestamp和Nonce随机数联合起来,然后做一个不可逆的签名来保证。 + +Nonce随机数保证唯一,或者Timestamp和Nonce合起来保证唯一,同样的,请求只接受一次,于是服务器多次收到相同的Timestamp和Nonce,则视为无效即可。 + +如果有人想篡改Timestamp和Nonce,还有签名保证不可篡改性,如果改了用签名算法解出来,就对不上了,可以丢弃了。 + +小结 + +好了,这一节就到这里了,我们来总结一下。 + + +加密分对称加密和非对称加密。对称加密效率高,但是解决不了密钥传输问题;非对称加密可以解决这个问题,但是效率不高。 + +非对称加密需要通过证书和权威机构来验证公钥的合法性。 + +HTTPS是综合了对称加密和非对称加密算法的HTTP协议。既保证传输安全,也保证传输效率。 + + +最后,给你留两个思考题: + + +HTTPS协议比较复杂,沟通过程太繁复,这样会导致效率问题,那你知道有哪些手段可以解决这些问题吗? + +HTTP和HTTPS协议的正文部分传输个JSON什么的还好,如果播放视频,就有问题了,那这个时候,应该使用什么协议呢? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/16流媒体协议:如何在直播里看到美女帅哥?.md b/专栏/趣谈网络协议/16流媒体协议:如何在直播里看到美女帅哥?.md new file mode 100644 index 0000000..12b576d --- /dev/null +++ b/专栏/趣谈网络协议/16流媒体协议:如何在直播里看到美女帅哥?.md @@ -0,0 +1,226 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 16 流媒体协议:如何在直播里看到美女帅哥? + 最近直播比较火,很多人都喜欢看直播,那一个直播系统里面都有哪些组成部分,都使用了什么协议呢? + +无论是直播还是点播,其实都是对于视频数据的传输。一提到视频,大家都爱看,但是一提到视频技术,大家都头疼,因为名词实在是太多了。 + +三个名词系列 + +我这里列三个名词系列,你先大致有个印象。 + + +名词系列一:AVI、MPEG、RMVB、MP4、MOV、FLV、WebM、WMV、ASF、MKV。例如RMVB和MP4,看着是不是很熟悉? + +名词系列二:H.261、 H.262、H.263、H.264、H.265。这个是不是就没怎么听过了?别着急,你先记住,要重点关注H.264。 + +名词系列三:MPEG-1、MPEG-2、MPEG-4、MPEG-7。MPEG好像听说过,但是后面的数字是怎么回事?是不是又熟悉又陌生? + + +这里,我想问你个问题,视频是什么?我说,其实就是快速播放一连串连续的图片。 + +每一张图片,我们称为一帧。只要每秒钟帧的数据足够多,也即播放得足够快。比如每秒30帧,以人的眼睛的敏感程度,是看不出这是一张张独立的图片的,这就是我们常说的帧率(FPS)。 + +每一张图片,都是由像素组成的,假设为1024*768(这个像素数不算多)。每个像素由RGB组成,每个8位,共24位。 + +我们来算一下,每秒钟的视频有多大? + +30帧 × 1024 × 768 × 24 = 566,231,040Bits = 70,778,880Bytes + +如果一分钟呢?4,246,732,800Bytes,已经是4个G了。 + +是不是不算不知道,一算吓一跳?这个数据量实在是太大,根本没办法存储和传输。如果这样存储,你的硬盘很快就满了;如果这样传输,那多少带宽也不够用啊! + +怎么办呢?人们想到了编码,就是看如何用尽量少的Bit数保存视频,使播放的时候画面看起来仍然很精美。编码是一个压缩的过程。 + +视频和图片的压缩过程有什么特点? + +之所以能够对视频流中的图片进行压缩,因为视频和图片有这样一些特点。 + + +空间冗余:图像的相邻像素之间有较强的相关性,一张图片相邻像素往往是渐变的,不是突变的,没必要每个像素都完整地保存,可以隔几个保存一个,中间的用算法计算出来。 + +时间冗余:视频序列的相邻图像之间内容相似。一个视频中连续出现的图片也不是突变的,可以根据已有的图片进行预测和推断。 + +视觉冗余:人的视觉系统对某些细节不敏感,因此不会每一个细节都注意到,可以允许丢失一些数据。 + +编码冗余:不同像素值出现的概率不同,概率高的用的字节少,概率低的用的字节多,类似霍夫曼编码(Huffman Coding)的思路。 + + +总之,用于编码的算法非常复杂,而且多种多样,但是编码过程其实都是类似的。 + + + +视频编码的两大流派 + +能不能形成一定的标准呢?要不然开发视频播放的人得累死了。当然能,我这里就给你介绍,视频编码的两大流派。 + + +流派一:ITU(International Telecommunications Union)的VCEG(Video Coding Experts Group),这个称为国际电联下的VCEG。既然是电信,可想而知,他们最初做视频编码,主要侧重传输。名词系列二,就是这个组织制定的标准。 + +流派二:ISO(International Standards Organization)的MPEG(Moving Picture Experts Group),这个是ISO旗下的MPEG,本来是做视频存储的。例如,编码后保存在VCD和DVD中。当然后来也慢慢侧重视频传输了。名词系列三,就是这个组织制定的标准。 + + +后来,ITU-T(国际电信联盟电信标准化部门,ITU Telecommunication Standardization Sector)与MPEG联合制定了H.264/MPEG-4 AVC,这才是我们这一节要重点关注的。 + +经过编码之后,生动活泼的一帧一帧的图像,就变成了一串串让人看不懂的二进制,这个二进制可以放在一个文件里面,按照一定的格式保存起来,这就是名词系列一。 + +其实这些就是视频保存成文件的格式。例如,前几个字节是什么意义,后几个字节是什么意义,然后是数据,数据中保存的就是编码好的结果。 + +如何在直播里看到帅哥美女? + +当然,这个二进制也可以通过某种网络协议进行封装,放在互联网上传输,这个时候就可以进行网络直播了。 + +网络协议将编码好的视频流,从主播端推送到服务器,在服务器上有个运行了同样协议的服务端来接收这些网络包,从而得到里面的视频流,这个过程称为接流。 + +服务端接到视频流之后,可以对视频流进行一定的处理,例如转码,也即从一个编码格式,转成另一种格式。因为观众使用的客户端千差万别,要保证他们都能看到直播。 + +流处理完毕之后,就可以等待观众的客户端来请求这些视频流。观众的客户端请求的过程称为拉流。 + +如果有非常多的观众,同时看一个视频直播,那都从一个服务器上拉流,压力太大了,因而需要一个视频的分发网络,将视频预先加载到就近的边缘节点,这样大部分观众看的视频,是从边缘节点拉取的,就能降低服务器的压力。 + +当观众的客户端将视频流拉下来之后,就需要进行解码,也即通过上述过程的逆过程,将一串串看不懂的二进制,再转变成一帧帧生动的图片,在客户端播放出来,这样你就能看到美女帅哥啦。 + +整个直播过程,可以用这个的图来描述。 + +- +接下来,我们依次来看一下每个过程。 + +编码:如何将丰富多彩的图片变成二进制流? + +虽然我们说视频是一张张图片的序列,但是如果每张图片都完整,就太大了,因而会将视频序列分成三种帧。 + + +I帧,也称关键帧。里面是完整的图片,只需要本帧数据,就可以完成解码。 + +P帧,前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面,叠加上和本帧定义的差别,生成最终画面。 + +B帧,双向预测内插编码帧。B帧记录的是本帧与前后帧的差别。要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的数据与本帧数据的叠加,取得最终的画面。 + + +可以看出,I帧最完整,B帧压缩率最高,而压缩后帧的序列,应该是在IBBP的间隔出现的。这就是通过时序进行编码。 + + + +在一帧中,分成多个片,每个片中分成多个宏块,每个宏块分成多个子块,这样将一张大的图分解成一个个小块,可以方便进行空间上的编码。 + +尽管时空非常立体地组成了一个序列,但是总归还是要压缩成一个二进制流。这个流是有结构的,是一个个的网络提取层单元(NALU,Network Abstraction Layer Unit)。变成这种格式就是为了传输,因为网络上的传输,默认的是一个个的包,因而这里也就分成了一个个的单元。 + + + +每一个NALU首先是一个起始标识符,用于标识NALU之间的间隔;然后是NALU的头,里面主要配置了NALU的类型;最终Payload里面是NALU承载的数据。 + +在NALU头里面,主要的内容是类型NAL Type。 + + +0x07表示SPS,是序列参数集, 包括一个图像序列的所有信息,如图像尺寸、视频格式等。 + +0x08表示PPS,是图像参数集,包括一个图像的所有分片的所有相关信息,包括图像类型、序列号等。 + + +在传输视频流之前,必须要传输这两类参数,不然无法解码。为了保证容错性,每一个I帧前面,都会传一遍这两个参数集合。 + +如果NALU Header里面的表示类型是SPS或者PPS,则Payload中就是真正的参数集的内容。 + +如果类型是帧,则Payload中才是正的视频数据,当然也是一帧一帧存放的,前面说了,一帧的内容还是挺多的,因而每一个NALU里面保存的是一片。对于每一片,到底是I帧,还是P帧,还是B帧,在片结构里面也有个Header,这里面有个类型,然后是片的内容。 + +这样,整个格式就出来了,一个视频,可以拆分成一系列的帧,每一帧拆分成一系列的片,每一片都放在一个NALU里面,NALU之间都是通过特殊的起始标识符分隔,在每一个I帧的第一片前面,要插入单独保存SPS和PPS的NALU,最终形成一个长长的NALU序列。 + +推流:如何把数据流打包传输到对端? + +那这个格式是不是就能够直接在网上传输到对端,开始直播了呢?其实还不是,还需要将这个二进制的流打包成网络包进行发送,这里我们使用RTMP协议。这就进入了第二个过程,推流。 + +RTMP是基于TCP的,因而肯定需要双方建立一个TCP的连接。在有TCP的连接的基础上,还需要建立一个RTMP的连接,也即在程序里面,你需要调用RTMP类库的Connect函数,显示创建一个连接。 + +RTMP为什么需要建立一个单独的连接呢? + +因为它们需要商量一些事情,保证以后的传输能正常进行。主要就是两个事情,一个是版本号,如果客户端、服务器的版本号不一致,则不能工作。另一个就是时间戳,视频播放中,时间是很重要的,后面的数据流互通的时候,经常要带上时间戳的差值,因而一开始双方就要知道对方的时间戳。 + +未来沟通这些事情,需要发送六条消息:客户端发送C0、C1、 C2,服务器发送S0、 S1、 S2。 + +首先,客户端发送C0表示自己的版本号,不必等对方的回复,然后发送C1表示自己的时间戳。 + +服务器只有在收到C0的时候,才能返回S0,表明自己的版本号,如果版本不匹配,可以断开连接。 + +服务器发送完S0后,也不用等什么,就直接发送自己的时间戳S1。客户端收到S1的时候,发一个知道了对方时间戳的ACK C2。同理服务器收到C1的时候,发一个知道了对方时间戳的ACK S2。 + +于是,握手完成。 + + + +握手之后,双方需要互相传递一些控制信息,例如Chunk块的大小、窗口大小等。 + +真正传输数据的时候,还是需要创建一个流Stream,然后通过这个Stream来推流publish。 + +推流的过程,就是将NALU放在Message里面发送,这个也称为RTMP Packet包。Message的格式就像这样。 + + + +发送的时候,去掉NALU的起始标识符。因为这部分对于RTMP协议来讲没有用。接下来,将SPS和PPS参数集封装成一个RTMP包发送,然后发送一个个片的NALU。 + +RTMP在收发数据的时候并不是以Message为单位的,而是把Message拆分成Chunk发送,而且必须在一个Chunk发送完成之后,才能开始发送下一个Chunk。每个Chunk中都带有Message ID,表示属于哪个Message,接收端也会按照这个ID将Chunk组装成Message。 + +前面连接的时候,设置的Chunk块大小就是指这个Chunk。将大的消息变为小的块再发送,可以在低带宽的情况下,减少网络拥塞。 + +这有一个分块的例子,你可以看一下。 + +假设一个视频的消息长度为307,但是Chunk大小约定为128,于是会拆分为三个Chunk。 + +第一个Chunk的Type=0,表示Chunk头是完整的;头里面Timestamp为1000,总长度Length 为307,类型为9,是个视频,Stream ID为12346,正文部分承担128个字节的Data。 + +第二个Chunk也要发送128个字节,Chunk头由于和第一个Chunk一样,因此采用Chunk Type=3,表示头一样就不再发送了。 + +第三个Chunk要发送的Data的长度为307-128-128=51个字节,还是采用Type=3。 + + + +就这样数据就源源不断到达流媒体服务器,整个过程就像这样。 + + + +这个时候,大量观看直播的观众就可以通过RTMP协议从流媒体服务器上拉取,但是这么多的用户量,都去同一个地方拉取,服务器压力会很大,而且用户分布在全国甚至全球,如果都去统一的一个地方下载,也会时延比较长,需要有分发网络。 + +分发网络分为中心和边缘两层。边缘层服务器部署在全国各地及横跨各大运营商里,和用户距离很近。中心层是流媒体服务集群,负责内容的转发。智能负载均衡系统,根据用户的地理位置信息,就近选择边缘服务器,为用户提供推/拉流服务。中心层也负责转码服务,例如,把RTMP协议的码流转换为HLS码流。 + + + +这套机制在后面的DNS、HTTPDNS、CDN的章节会更有详细的描述。 + +拉流:观众的客户端如何看到视频? + +接下来,我们再来看观众的客户端通过RTMP拉流的过程。 + + + +先读到的是H.264的解码参数,例如SPS和PPS,然后对收到的NALU组成的一个个帧,进行解码,交给播发器播放,一个绚丽多彩的视频画面就出来了。 + +小结 + +好了,今天的内容就到这里了,我们来总结一下: + + +视频名词比较多,编码两大流派达成了一致,都是通过时间、空间的各种算法来压缩数据; + +压缩好的数据,为了传输组成一系列NALU,按照帧和片依次排列; + +排列好的NALU,在网络传输的时候,要按照RTMP包的格式进行包装,RTMP的包会拆分成Chunk进行传输; + +推送到流媒体集群的视频流经过转码和分发,可以被客户端通过RTMP协议拉取,然后组合为NALU,解码成视频格式进行播放。 + + +最后,给你留两个思考题: + + +你觉得基于RTMP的视频流传输的机制存在什么问题?如何进行优化? + +在线看视频之前,大家都是把电影下载下来看的,电影这么大,你知道如何快速下载吗? + + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/17P2P协议:我下小电影,99%急死你.md b/专栏/趣谈网络协议/17P2P协议:我下小电影,99%急死你.md new file mode 100644 index 0000000..e69de29 diff --git a/专栏/趣谈网络协议/18DNS协议:网络世界的地址簿.md b/专栏/趣谈网络协议/18DNS协议:网络世界的地址簿.md new file mode 100644 index 0000000..cdb5f5b --- /dev/null +++ b/专栏/趣谈网络协议/18DNS协议:网络世界的地址簿.md @@ -0,0 +1,135 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 18 DNS协议:网络世界的地址簿 + 前面我们讲了平时常见的看新闻、支付、直播、下载等场景,现在网站的数目非常多,常用的网站就有二三十个,如果全部用IP地址进行访问,恐怕很难记住。于是,就需要一个地址簿,根据名称,就可以查看具体的地址。 + +例如,我要去西湖边的“外婆家”,这就是名称,然后通过地址簿,查看到底是哪条路多少号。 + +DNS服务器 + +在网络世界,也是这样的。你肯定记得住网站的名称,但是很难记住网站的IP地址,因而也需要一个地址簿,就是DNS服务器。 + +由此可见,DNS在日常生活中多么重要。每个人上网,都需要访问它,但是同时,这对它来讲也是非常大的挑战。一旦它出了故障,整个互联网都将瘫痪。另外,上网的人分布在全世界各地,如果大家都去同一个地方访问某一台服务器,时延将会非常大。因而,DNS服务器,一定要设置成高可用、高并发和分布式的。 + +于是,就有了这样树状的层次结构。 + +- 根DNS服务器 :返回顶级域DNS服务器的IP地址 + + +顶级域DNS服务器:返回权威DNS服务器的IP地址 + +权威DNS服务器 :返回相应主机的IP地址 + + +DNS解析流程 + +为了提高DNS的解析性能,很多网络都会就近部署DNS缓存服务器。于是,就有了以下的DNS解析流程。 + + +电脑客户端会发出一个DNS请求,问www.163.com的IP是啥啊,并发给本地域名服务器 (本地DNS)。那本地域名服务器 (本地DNS) 是什么呢?如果是通过DHCP配置,本地DNS由你的网络服务商(ISP),如电信、移动等自动分配,它通常就在你网络服务商的某个机房。 + +本地DNS收到来自客户端的请求。你可以想象这台服务器上缓存了一张域名与之对应IP地址的大表格。如果能找到 www.163.com,它就直接返回IP地址。如果没有,本地DNS会去问它的根域名服务器:“老大,能告诉我www.163.com的IP地址吗?”根域名服务器是最高层次的,全球共有13套。它不直接用于域名解析,但能指明一条道路。 + +根DNS收到来自本地DNS的请求,发现后缀是 .com,说:“哦,www.163.com啊,这个域名是由.com区域管理,我给你它的顶级域名服务器的地址,你去问问它吧。” + +本地DNS转向问顶级域名服务器:“老二,你能告诉我www.163.com的IP地址吗?”顶级域名服务器就是大名鼎鼎的比如 .com、.net、 .org这些一级域名,它负责管理二级域名,比如 163.com,所以它能提供一条更清晰的方向。 + +顶级域名服务器说:“我给你负责 www.163.com 区域的权威DNS服务器的地址,你去问它应该能问到。” + +本地DNS转向问权威DNS服务器:“您好,www.163.com 对应的IP是啥呀?”163.com的权威DNS服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。 + +权威DNS服务器查询后将对应的IP地址X.X.X.X告诉本地DNS。 + +本地DNS再将IP地址返回客户端,客户端和目标建立连接。 + + +至此,我们完成了DNS的解析过程。现在总结一下,整个过程我画成了一个图。 + + + +负载均衡 + +站在客户端角度,这是一次DNS递归查询过程。因为本地DNS全权为它效劳,它只要坐等结果即可。在这个过程中,DNS除了可以通过名称映射为IP地址,它还可以做另外一件事,就是负载均衡。 + +还是以访问“外婆家”为例,还是我们开头的“外婆家”,但是,它可能有很多地址,因为它在杭州可以有很多家。所以,如果一个人想去吃“外婆家”,他可以就近找一家店,而不用大家都去同一家,这就是负载均衡。 + +DNS首先可以做内部负载均衡。 + +例如,一个应用要访问数据库,在这个应用里面应该配置这个数据库的IP地址,还是应该配置这个数据库的域名呢?显然应该配置域名,因为一旦这个数据库,因为某种原因,换到了另外一台机器上,而如果有多个应用都配置了这台数据库的话,一换IP地址,就需要将这些应用全部修改一遍。但是如果配置了域名,则只要在DNS服务器里,将域名映射为新的IP地址,这个工作就完成了,大大简化了运维。 + +在这个基础上,我们可以再进一步。例如,某个应用要访问另外一个应用,如果配置另外一个应用的IP地址,那么这个访问就是一对一的。但是当被访问的应用撑不住的时候,我们其实可以部署多个。但是,访问它的应用,如何在多个之间进行负载均衡?只要配置成为域名就可以了。在域名解析的时候,我们只要配置策略,这次返回第一个IP,下次返回第二个IP,就可以实现负载均衡了。 + +另外一个更加重要的是,DNS还可以做全局负载均衡。 + +为了保证我们的应用高可用,往往会部署在多个机房,每个地方都会有自己的IP地址。当用户访问某个域名的时候,这个IP地址可以轮询访问多个数据中心。如果一个数据中心因为某种原因挂了,只要在DNS服务器里面,将这个数据中心对应的IP地址删除,就可以实现一定的高可用。 + +另外,我们肯定希望北京的用户访问北京的数据中心,上海的用户访问上海的数据中心,这样,客户体验就会非常好,访问速度就会超快。这就是全局负载均衡的概念。 + +示例:DNS访问数据中心中对象存储上的静态资源 + +我们通过DNS访问数据中心中对象存储上的静态资源为例,看一看整个过程。 + +假设全国有多个数据中心,托管在多个运营商,每个数据中心三个可用区(Available Zone)。对象存储通过跨可用区部署,实现高可用性。在每个数据中心中,都至少部署两个内部负载均衡器,内部负载均衡器后面对接多个对象存储的前置服务器(Proxy-server)。 + + + + +当一个客户端要访问object.yourcompany.com的时候,需要将域名转换为IP地址进行访问,所以它要请求本地DNS解析器。 + +本地DNS解析器先查看看本地的缓存是否有这个记录。如果有则直接使用,因为上面的过程太复杂了,如果每次都要递归解析,就太麻烦了。 + +如果本地无缓存,则需要请求本地的DNS服务器。 + +本地的DNS服务器一般部署在你的数据中心或者你所在的运营商的网络中,本地DNS服务器也需要看本地是否有缓存,如果有则返回,因为它也不想把上面的递归过程再走一遍。 + +至 7. 如果本地没有,本地DNS才需要递归地从根DNS服务器,查到.com的顶级域名服务器,最终查到 yourcompany.com 的权威DNS服务器,给本地DNS服务器,权威DNS服务器按说会返回真实要访问的IP地址。 + + +对于不需要做全局负载均衡的简单应用来讲,yourcompany.com的权威DNS服务器可以直接将 object.yourcompany.com这个域名解析为一个或者多个IP地址,然后客户端可以通过多个IP地址,进行简单的轮询,实现简单的负载均衡。 + +但是对于复杂的应用,尤其是跨地域跨运营商的大型应用,则需要更加复杂的全局负载均衡机制,因而需要专门的设备或者服务器来做这件事情,这就是全局负载均衡器(GSLB,Global Server Load Balance)。 + +在yourcompany.com的DNS服务器中,一般是通过配置CNAME的方式,给 object.yourcompany.com起一个别名,例如 object.vip.yourcomany.com,然后告诉本地DNS服务器,让它请求GSLB解析这个域名,GSLB就可以在解析这个域名的过程中,通过自己的策略实现负载均衡。 + +图中画了两层的GSLB,是因为分运营商和地域。我们希望不同运营商的客户,可以访问相同运营商机房中的资源,这样不跨运营商访问,有利于提高吞吐量,减少时延。 + + +第一层GSLB,通过查看请求它的本地DNS服务器所在的运营商,就知道用户所在的运营商。假设是移动,通过CNAME的方式,通过另一个别名 object.yd.yourcompany.com,告诉本地DNS服务器去请求第二层的GSLB。 + +第二层GSLB,通过查看请求它的本地DNS服务器所在的地址,就知道用户所在的地理位置,然后将距离用户位置比较近的Region里面,六个内部负载均衡(SLB,Server Load Balancer)的地址,返回给本地DNS服务器。 + +本地DNS服务器将结果返回给本地DNS解析器。 + +本地DNS解析器将结果缓存后,返回给客户端。 + +客户端开始访问属于相同运营商的距离较近的Region 1中的对象存储,当然客户端得到了六个IP地址,它可以通过负载均衡的方式,随机或者轮询选择一个可用区进行访问。对象存储一般会有三个备份,从而可以实现对存储读写的负载均衡。 + + +小结 + +好了,这节内容就到这里了,我们来总结一下: + + +DNS是网络世界的地址簿,可以通过域名查地址,因为域名服务器是按照树状结构组织的,因而域名查找是使用递归的方法,并通过缓存的方式增强性能; + +在域名和IP的映射过程中,给了应用基于域名做负载均衡的机会,可以是简单的负载均衡,也可以根据地址和运营商做全局的负载均衡。 + + +最后,给你留两个思考题: + + +全局负载均衡为什么要分地址和运营商呢? + +全局负载均衡使用过程中,常常遇到失灵的情况,你知道具体有哪些情况吗?对应应该怎么来解决呢? + + +我们的专栏更新过半了,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/19HttpDNS:网络世界的地址簿也会指错路.md b/专栏/趣谈网络协议/19HttpDNS:网络世界的地址簿也会指错路.md new file mode 100644 index 0000000..675b6f8 --- /dev/null +++ b/专栏/趣谈网络协议/19HttpDNS:网络世界的地址簿也会指错路.md @@ -0,0 +1,171 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 19 HttpDNS:网络世界的地址簿也会指错路 + 上一节我们知道了DNS的两项功能,第一是根据名称查到具体的地址,另外一个是可以针对多个地址做负载均衡,而且可以在多个地址中选择一个距离你近的地方访问。 + +然而有时候这个地址簿也经常给你指错路,明明距离你500米就有个吃饭的地方,非要把你推荐到5公里外。为什么会出现这样的情况呢? + +还记得吗?当我们发出请求解析DNS的时候,首先,会先连接到运营商本地的DNS服务器,由这个服务器帮我们去整棵DNS树上进行解析,然后将解析的结果返回给客户端。但是本地的DNS服务器,作为一个本地导游,往往有自己的“小心思”。 + +传统DNS存在哪些问题? + +1.域名缓存问题 + +它可以在本地做一个缓存,也就是说,不是每一个请求,它都会去访问权威DNS服务器,而是访问过一次就把结果缓存到自己本地,当其他人来问的时候,直接就返回这个缓存数据。 + +这就相当于导游去过一个饭店,自己脑子记住了地址,当有一个游客问的时候,他就凭记忆回答了,不用再去查地址簿。这样经常存在的一个问题是,人家那个饭店明明都已经搬了,结果作为导游,他并没有刷新这个缓存,结果你辛辛苦苦到了这个地点,发现饭店已经变成了服装店,你是不是会非常失望? + +另外,有的运营商会把一些静态页面,缓存到本运营商的服务器内,这样用户请求的时候,就不用跨运营商进行访问,这样既加快了速度,也减少了运营商之间流量计算的成本。在域名解析的时候,不会将用户导向真正的网站,而是指向这个缓存的服务器。 + +很多情况下是看不出问题的,但是当页面更新,用户会访问到老的页面,问题就出来了。例如,你听说一个餐馆推出了一个新菜,你想去尝一下。结果导游告诉你,在这里吃也是一样的。有的游客会觉得没问题,但是对于想尝试新菜的人来说,如果导游说带你去,但其实并没有吃到新菜,你是不是也会非常失望呢? + +再就是本地的缓存,往往使得全局负载均衡失败,因为上次进行缓存的时候,缓存中的地址不一定是这次访问离客户最近的地方,如果把这个地址返回给客户,那肯定就会绕远路。 + +就像上一次客户要吃西湖醋鱼的事,导游知道西湖边有一家,因为当时游客就在西湖边,可是,下一次客户在灵隐寺,想吃西湖醋鱼的时候,导游还指向西湖边的那一家,那这就绕得太远了。 + + + +2.域名转发问题 + +缓存问题还是说本地域名解析服务,还是会去权威DNS服务器中查找,只不过不是每次都要查找。可以说这还是大导游、大中介。还有一些小导游、小中介,有了请求之后,直接转发给其他运营商去做解析,自己只是外包了出去。 + +这样的问题是,如果是A运营商的客户,访问自己运营商的DNS服务器,如果A运营商去权威DNS服务器查询的话,权威DNS服务器知道你是A运营商的,就返回给一个部署在A运营商的网站地址,这样针对相同运营商的访问,速度就会快很多。 + +但是A运营商偷懒,将解析的请求转发给B运营商,B运营商去权威DNS服务器查询的话,权威服务器会误认为,你是B运营商的,那就返回给你一个在B运营商的网站地址吧,结果客户的每次访问都要跨运营商,速度就会很慢。 + + + +3.出口NAT问题 + +前面讲述网关的时候,我们知道,出口的时候,很多机房都会配置NAT,也即网络地址转换,使得从这个网关出去的包,都换成新的IP地址,当然请求返回的时候,在这个网关,再将IP地址转换回去,所以对于访问来说是没有任何问题。 + +但是一旦做了网络地址的转换,权威的DNS服务器,就没办法通过这个地址,来判断客户到底是来自哪个运营商,而且极有可能因为转换过后的地址,误判运营商,导致跨运营商的访问。 + +4.域名更新问题 + +本地DNS服务器是由不同地区、不同运营商独立部署的。对域名解析缓存的处理上,实现策略也有区别,有的会偷懒,忽略域名解析结果的TTL时间限制,在权威DNS服务器解析变更的时候,解析结果在全网生效的周期非常漫长。但是有的时候,在DNS的切换中,场景对生效时间要求比较高。 + +例如双机房部署的时候,跨机房的负载均衡和容灾多使用DNS来做。当一个机房出问题之后,需要修改权威DNS,将域名指向新的IP地址,但是如果更新太慢,那很多用户都会出现访问异常。 + +这就像,有的导游比较勤快、敬业,时时刻刻关注酒店、餐馆、交通的变化,问他的时候,往往会得到最新情况。有的导游懒一些,8年前背的导游词就没换过,问他的时候,指的路往往就是错的。 + +5.解析延迟问题 + +从上一节的DNS查询过程来看,DNS的查询过程需要递归遍历多个DNS服务器,才能获得最终的解析结果,这会带来一定的时延,甚至会解析超时。 + +HttpDNS的工作模式 + +既然DNS解析中有这么多问题,那怎么办呢?难不成退回到直接用IP地址?这样显然不合适,所以就有了 HttpDNS。 + +HttpDNS其实就是,不走传统的DNS解析,而是自己搭建基于HTTP协议的DNS服务器集群,分布在多个地点和多个运营商。当客户端需要DNS解析的时候,直接通过HTTP协议进行请求这个服务器集群,得到就近的地址。 + +这就相当于每家基于HTTP协议,自己实现自己的域名解析,自己做一个自己的地址簿,而不使用统一的地址簿。但是默认的域名解析都是走DNS的,因而使用HttpDNS需要绕过默认的DNS路径,就不能使用默认的客户端。使用HttpDNS的,往往是手机应用,需要在手机端嵌入支持HttpDNS的客户端SDK。 + +通过自己的HttpDNS服务器和自己的SDK,实现了从依赖本地导游,到自己上网查询做旅游攻略,进行自由行,爱怎么玩怎么玩。这样就能够避免依赖导游,而导游又不专业,你还不能把他怎么样的尴尬。 + +下面我来解析一下 HttpDNS的工作模式。 + +在客户端的SDK里动态请求服务端,获取HttpDNS服务器的IP列表,缓存到本地。随着不断地解析域名,SDK也会在本地缓存DNS域名解析的结果。 + +当手机应用要访问一个地址的时候,首先看是否有本地的缓存,如果有就直接返回。这个缓存和本地DNS的缓存不一样的是,这个是手机应用自己做的,而非整个运营商统一做的。如何更新、何时更新,手机应用的客户端可以和服务器协调来做这件事情。 + +如果本地没有,就需要请求HttpDNS的服务器,在本地HttpDNS服务器的IP列表中,选择一个发出HTTP的请求,会返回一个要访问的网站的IP列表。 + +请求的方式是这样的。 + +curl http://106.2.xxx.xxx/d?dn=c.m.163.com +{"dns":[{"host":"c.m.163.com","ips":["223.252.199.12"],"ttl":300,"http2":0}],"client":{"ip":"106.2.81.50","line":269692944}} + + +手机客户端自然知道手机在哪个运营商、哪个地址。由于是直接的HTTP通信,HttpDNS服务器能够准确知道这些信息,因而可以做精准的全局负载均衡。 + + + +当然,当所有这些都不工作的时候,可以切换到传统的LocalDNS来解析,慢也比访问不到好。那HttpDNS是如何解决上面的问题的呢? + +其实归结起来就是两大问题。一是解析速度和更新速度的平衡问题,二是智能调度的问题,对应的解决方案是HttpDNS的缓存设计和调度设计。 + +HttpDNS的缓存设计 + +解析DNS过程复杂,通信次数多,对解析速度造成很大影响。为了加快解析,因而有了缓存,但是这又会产生缓存更新速度不及时的问题。最要命的是,这两个方面都掌握在别人手中,也即本地DNS服务器手中,它不会为你定制,你作为客户端干着急没办法。 + +而HttpDNS就是将解析速度和更新速度全部掌控在自己手中。一方面,解析的过程,不需要本地DNS服务递归的调用一大圈,一个HTTP的请求直接搞定,要实时更新的时候,马上就能起作用;另一方面为了提高解析速度,本地也有缓存,缓存是在客户端SDK维护的,过期时间、更新时间,都可以自己控制。 + +HttpDNS的缓存设计策略也是咱们做应用架构中常用的缓存设计模式,也即分为客户端、缓存、数据源三层。 + + +对于应用架构来讲,就是应用、缓存、数据库。常见的是Tomcat、Redis、MySQL。 + +对于HttpDNS来讲,就是手机客户端、DNS缓存、HttpDNS服务器。 + + + + +只要是缓存模式,就存在缓存的过期、更新、不一致的问题,解决思路也是很像的。 + +例如DNS缓存在内存中,也可以持久化到存储上,从而APP重启之后,能够尽快从存储中加载上次累积的经常访问的网站的解析结果,就不需要每次都全部解析一遍,再变成缓存。这有点像Redis是基于内存的缓存,但是同样提供持久化的能力,使得重启或者主备切换的时候,数据不会完全丢失。 + +SDK中的缓存会严格按照缓存过期时间,如果缓存没有命中,或者已经过期,而且客户端不允许使用过期的记录,则会发起一次解析,保障记录是更新的。 + +解析可以同步进行,也就是直接调用HttpDNS的接口,返回最新的记录,更新缓存;也可以异步进行,添加一个解析任务到后台,由后台任务调用HttpDNS的接口。 + +同步更新的优点是实时性好,缺点是如果有多个请求都发现过期的时候,同时会请求HttpDNS多次,其实是一种浪费。 + +同步更新的方式对应到应用架构中缓存的Cache-Aside机制,也即先读缓存,不命中读数据库,同时将结果写入缓存。 + + + +异步更新的优点是,可以将多个请求都发现过期的情况,合并为一个对于HttpDNS的请求任务,只执行一次,减少HttpDNS的压力。同时可以在即将过期的时候,就创建一个任务进行预加载,防止过期之后再刷新,称为预加载。 + +它的缺点是当前请求拿到过期数据的时候,如果客户端允许使用过期数据,需要冒一次风险。如果过期的数据还能请求,就没问题;如果不能请求,则失败一次,等下次缓存更新后,再请求方能成功。 + + + +异步更新的机制对应到应用架构中缓存的Refresh-Ahead机制,即业务仅仅访问缓存,当过期的时候定期刷新。在著名的应用缓存Guava Cache中,有个RefreshAfterWrite机制,对于并发情况下,多个缓存访问不命中从而引发并发回源的情况,可以采取只有一个请求回源的模式。在应用架构的缓存中,也常常用数据预热或者预加载的机制。 + + + +HttpDNS的调度设计 + +由于客户端嵌入了SDK,因而就不会因为本地DNS的各种缓存、转发、NAT,让权威DNS服务器误会客户端所在的位置和运营商,而可以拿到第一手资料。 + +在客户端,可以知道手机是哪个国家、哪个运营商、哪个省,甚至哪个市,HttpDNS服务端可以根据这些信息,选择最佳的服务节点访问。 + +如果有多个节点,还会考虑错误率、请求时间、服务器压力、网络状况等,进行综合选择,而非仅仅考虑地理位置。当有一个节点宕机或者性能下降的时候,可以尽快进行切换。 + +要做到这一点,需要客户端使用HttpDNS返回的IP访问业务应用。客户端的SDK会收集网络请求数据,如错误率、请求时间等网络请求质量数据,并发送到统计后台,进行分析、聚合,以此查看不同的IP的服务质量。 + +在服务端,应用可以通过调用HttpDNS的管理接口,配置不同服务质量的优先级、权重。HttpDNS会根据这些策略综合地理位置和线路状况算出一个排序,优先访问当前那些优质的、时延低的IP地址。 + +HttpDNS通过智能调度之后返回的结果,也会缓存在客户端。为了不让缓存使得调度失真,客户端可以根据不同的移动网络运营商WIFI的SSID来分维度缓存。不同的运营商或者WIFI解析出来的结果会不同。 + + + +小结 + +好了,这节就到这里了,我们来总结一下,你需要记住这两个重点: + + +传统的DNS有很多问题,例如解析慢、更新不及时。因为缓存、转发、NAT问题导致客户端误会自己所在的位置和运营商,从而影响流量的调度。 + +HttpDNS通过客户端SDK和服务端,通过HTTP直接调用解析DNS的方式,绕过了传统DNS的这些缺点,实现了智能的调度。 + + +最后,给你留两个思考题。 + + +使用HttpDNS,需要向HttpDNS服务器请求解析域名,可是客户端怎么知道HttpDNS服务器的地址或者域名呢? + +HttpDNS的智能调度,主要是让客户端选择最近的服务器,而有另一种机制,使得资源分发到离客户端更近的位置,从而加快客户端的访问,你知道是什么技术吗? + + +我们的专栏更新过半了,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/20CDN:你去小卖部取过快递么?.md b/专栏/趣谈网络协议/20CDN:你去小卖部取过快递么?.md new file mode 100644 index 0000000..cacb115 --- /dev/null +++ b/专栏/趣谈网络协议/20CDN:你去小卖部取过快递么?.md @@ -0,0 +1,125 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 20 CDN:你去小卖部取过快递么? + 上一节,我们看到了网站的一般访问模式。 + +当一个用户想访问一个网站的时候,指定这个网站的域名,DNS就会将这个域名解析为地址,然后用户请求这个地址,返回一个网页。就像你要买个东西,首先要查找商店的位置,然后去商店里面找到自己想要的东西,最后拿着东西回家。 + +那这里面还有没有可以优化的地方呢? + +例如你去电商网站下单买个东西,这个东西一定要从电商总部的中心仓库送过来吗?原来基本是这样的,每一单都是单独配送,所以你可能要很久才能收到你的宝贝。但是后来电商网站的物流系统学聪明了,他们在全国各地建立了很多仓库,而不是只有总部的中心仓库才可以发货。 + +电商网站根据统计大概知道,北京、上海、广州、深圳、杭州等地,每天能够卖出去多少书籍、卫生纸、包、电器等存放期比较长的物品。这些物品用不着从中心仓库发出,所以平时就可以将它们分布在各地仓库里,客户一下单,就近的仓库发出,第二天就可以收到了。 + +这样,用户体验大大提高。当然,这里面也有个难点就是,生鲜这类东西保质期太短,如果提前都备好货,但是没有人下单,那肯定就坏了。这个问题,我后文再说。 + +我们先说,我们的网站访问可以借鉴“就近配送”这个思路。 + +全球有这么多的数据中心,无论在哪里上网,临近不远的地方基本上都有数据中心。是不是可以在这些数据中心里部署几台机器,形成一个缓存的集群来缓存部分数据,那么用户访问数据的时候,就可以就近访问了呢? + +当然是可以的。这些分布在各个地方的各个数据中心的节点,就称为边缘节点。 + +由于边缘节点数目比较多,但是每个集群规模比较小,不可能缓存下来所有东西,因而可能无法命中,这样就会在边缘节点之上。有区域节点,规模就要更大,缓存的数据会更多,命中的概率也就更大。在区域节点之上是中心节点,规模更大,缓存数据更多。如果还不命中,就只好回源网站访问了。 + + + +这就是CDN分发系统的架构。CDN系统的缓存,也是一层一层的,能不访问后端真正的源,就不打扰它。这也是电商网站物流系统的思路,北京局找不到,找华北局,华北局找不到,再找北方局。 + +有了这个分发系统之后,接下来,客户端如何找到相应的边缘节点进行访问呢? + +还记得我们讲过的基于DNS的全局负载均衡吗?这个负载均衡主要用来选择一个就近的同样运营商的服务器进行访问。你会发现,CDN分发网络也是一个分布在多个区域、多个运营商的分布式系统,也可以用相同的思路选择最合适的边缘节点。 + + + +在没有CDN的情况下,用户向浏览器输入www.web.com这个域名,客户端访问本地DNS服务器的时候,如果本地DNS服务器有缓存,则返回网站的地址;如果没有,递归查询到网站的权威DNS服务器,这个权威DNS服务器是负责web.com的,它会返回网站的IP地址。本地DNS服务器缓存下IP地址,将IP地址返回,然后客户端直接访问这个IP地址,就访问到了这个网站。 + +然而有了CDN之后,情况发生了变化。在web.com这个权威DNS服务器上,会设置一个CNAME别名,指向另外一个域名 www.web.cdn.com,返回给本地DNS服务器。 + +当本地DNS服务器拿到这个新的域名时,需要继续解析这个新的域名。这个时候,再访问的就不是web.com的权威DNS服务器了,而是web.cdn.com的权威DNS服务器,这是CDN自己的权威DNS服务器。在这个服务器上,还是会设置一个CNAME,指向另外一个域名,也即CDN网络的全局负载均衡器。 + +接下来,本地DNS服务器去请求CDN的全局负载均衡器解析域名,全局负载均衡器会为用户选择一台合适的缓存服务器提供服务,选择的依据包括: + + +根据用户IP地址,判断哪一台服务器距用户最近; + +用户所处的运营商; + +根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需的内容; + +查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。 + + +基于以上这些条件,进行综合分析之后,全局负载均衡器会返回一台缓存服务器的IP地址。 + +本地DNS服务器缓存这个IP地址,然后将IP返回给客户端,客户端去访问这个边缘节点,下载资源。缓存服务器响应用户请求,将用户所需内容传送到用户终端。如果这台缓存服务器上并没有用户想要的内容,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉到本地。 + +CDN可以进行缓存的内容有很多种。 + +保质期长的日用品比较容易缓存,因为不容易过期,对应到就像电商仓库系统里,就是静态页面、图片等,因为这些东西也不怎么变,所以适合缓存。 + + + +还记得这个接入层缓存的架构吗?在进入数据中心的时候,我们希望通过最外层接入层的缓存,将大部分静态资源的访问拦在边缘。而CDN则更进一步,将这些静态资源缓存到离用户更近的数据中心。越接近客户,访问性能越好,时延越低。 + +但是静态内容中,有一种特殊的内容,也大量使用了CDN,这个就是前面讲过的[流媒体]。 + +CDN支持流媒体协议,例如前面讲过的RTMP协议。在很多情况下,这相当于一个代理,从上一级缓存读取内容,转发给用户。由于流媒体往往是连续的,因而可以进行预先缓存的策略,也可以预先推送到用户的客户端。 + +对于静态页面来讲,内容的分发往往采取拉取的方式,也即当发现未命中的时候,再去上一级进行拉取。但是,流媒体数据量大,如果出现回源,压力会比较大,所以往往采取主动推送的模式,将热点数据主动推送到边缘节点。 + +对于流媒体来讲,很多CDN还提供预处理服务,也即文件在分发之前,经过一定的处理。例如将视频转换为不同的码流,以适应不同的网络带宽的用户需求;再如对视频进行分片,降低存储压力,也使得客户端可以选择使用不同的码率加载不同的分片。这就是我们常见的,“我要看超清、标清、流畅等”。 + +对于流媒体CDN来讲,有个关键的问题是防盗链问题。因为视频是要花大价钱买版权的,为了挣点钱,收点广告费,如果流媒体被其他的网站盗走,在人家的网站播放,那损失可就大了。 + +最常用也最简单的方法就是HTTP头的referer字段, 当浏览器发送请求的时候,一般会带上referer,告诉服务器是从哪个页面链接过来的,服务器基于此可以获得一些信息用于处理。如果refer信息不是来自本站,就阻止访问或者跳到其它链接。 + +referer的机制相对比较容易破解,所以还需要配合其他的机制。 + +一种常用的机制是时间戳防盗链。使用CDN的管理员可以在配置界面上,和CDN厂商约定一个加密字符串。 + +客户端取出当前的时间戳,要访问的资源及其路径,连同加密字符串进行签名算法得到一个字符串,然后生成一个下载链接,带上这个签名字符串和截止时间戳去访问CDN。 + +在CDN服务端,根据取出过期时间,和当前 CDN 节点时间进行比较,确认请求是否过期。然后CDN服务端有了资源及路径,时间戳,以及约定的加密字符串,根据相同的签名算法计算签名,如果匹配则一致,访问合法,才会将资源返回给客户。 + +然而比如在电商仓库中,我在前面提过,有关生鲜的缓存就是非常麻烦的事情,这对应着就是动态的数据,比较难以缓存。怎么办呢?现在也有动态CDN,主要有两种模式。 + + +一种为生鲜超市模式,也即边缘计算的模式。既然数据是动态生成的,所以数据的逻辑计算和存储,也相应的放在边缘的节点。其中定时从源数据那里同步存储的数据,然后在边缘进行计算得到结果。就像对生鲜的烹饪是动态的,没办法事先做好缓存,因而将生鲜超市放在你家旁边,既能够送货上门,也能够现场烹饪,也是边缘计算的一种体现。 + +另一种是冷链运输模式,也即路径优化的模式。数据不是在边缘计算生成的,而是在源站生成的,但是数据的下发则可以通过CDN的网络,对路径进行优化。因为CDN节点较多,能够找到离源站很近的边缘节点,也能找到离用户很近的边缘节点。中间的链路完全由CDN来规划,选择一个更加可靠的路径,使用类似专线的方式进行访问。 + + +对于常用的TCP连接,在公网上传输的时候经常会丢数据,导致TCP的窗口始终很小,发送速度上不去。根据前面的TCP流量控制和拥塞控制的原理,在CDN加速网络中可以调整TCP的参数,使得TCP可以更加激进地传输数据。 + +可以通过多个请求复用一个连接,保证每次动态请求到达时。连接都已经建立了,不必临时三次握手或者建立过多的连接,增加服务器的压力。另外,可以通过对传输数据进行压缩,增加传输效率。 + +所有这些手段就像冷链运输,整个物流优化了,全程冷冻高速运输。不管生鲜是从你旁边的超市送到你家的,还是从产地送的,保证到你家是新鲜的。 + +小结 + +好了,这节就到这里了。咱们来总结一下,你记住这两个重点就好。 + + +CDN和电商系统的分布式仓储系统一样,分为中心节点、区域节点、边缘节点,而数据缓存在离用户最近的位置。 + +CDN最擅长的是缓存静态数据,除此之外还可以缓存流媒体数据,这时候要注意使用防盗链。它也支持动态数据的缓存,一种是边缘计算的生鲜超市模式,另一种是链路优化的冷链运输模式。 + + +最后,给你留两个思考题: + + +这一节讲了CDN使用DNS进行全局负载均衡的例子,CDN如何使用HttpDNS呢? + +客户端对DNS、HttpDNS、CDN访问了半天,还没进数据中心,你知道数据中心里面什么样吗? + + +我们的专栏更新到第20讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/21数据中心:我是开发商,自己拿地盖别墅.md b/专栏/趣谈网络协议/21数据中心:我是开发商,自己拿地盖别墅.md new file mode 100644 index 0000000..0fc5812 --- /dev/null +++ b/专栏/趣谈网络协议/21数据中心:我是开发商,自己拿地盖别墅.md @@ -0,0 +1,149 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 21 数据中心:我是开发商,自己拿地盖别墅 + 无论你是看新闻、下订单、看视频、下载文件,最终访问的目的地都在数据中心里面。我们前面学了这么多的网络协议和网络相关的知识,你是不是很好奇,数据中心究竟长啥样呢? + +数据中心是一个大杂烩,几乎要用到前面学过的所有知识。 + +前面讲办公室网络的时候,我们知道办公室里面有很多台电脑。如果要访问外网,需要经过一个叫网关的东西,而网关往往是一个路由器。 + +数据中心里面也有一大堆的电脑,但是它和咱们办公室里面的笔记本或者台式机不一样。数据中心里面是服务器。服务器被放在一个个叫作机架(Rack)的架子上面。 + +数据中心的入口和出口也是路由器,由于在数据中心的边界,就像在一个国家的边境,称为边界路由器(Border Router)。为了高可用,边界路由器会有多个。 + +一般家里只会连接一个运营商的网络,而为了高可用,为了当一个运营商出问题的时候,还可以通过另外一个运营商来提供服务,所以数据中心的边界路由器会连接多个运营商网络。 + +既然是路由器,就需要跑路由协议,数据中心往往就是路由协议中的自治区域(AS)。数据中心里面的机器要想访问外面的网站,数据中心里面也是有对外提供服务的机器,都可以通过BGP协议,获取内外互通的路由信息。这就是我们常听到的多线BGP的概念。 + +如果数据中心非常简单,没几台机器,那就像家里或者宿舍一样,所有的服务器都直接连到路由器上就可以了。但是数据中心里面往往有非常多的机器,当塞满一机架的时候,需要有交换机将这些服务器连接起来,可以互相通信。 + +这些交换机往往是放在机架顶端的,所以经常称为TOR(Top Of Rack)交换机。这一层的交换机常常称为接入层(Access Layer)。注意这个接入层和原来讲过的应用的接入层不是一个概念。 + + + +当一个机架放不下的时候,就需要多个机架,还需要有交换机将多个机架连接在一起。这些交换机对性能的要求更高,带宽也更大。这些交换机称为汇聚层交换机(Aggregation Layer)。 + +数据中心里面的每一个连接都是需要考虑高可用的。这里首先要考虑的是,如果一台机器只有一个网卡,上面连着一个网线,接入到TOR交换机上。如果网卡坏了,或者不小心网线掉了,机器就上不去了。所以,需要至少两个网卡、两个网线插到TOR交换机上,但是两个网卡要工作得像一张网卡一样,这就是常说的网卡绑定(bond)。 + +这就需要服务器和交换机都支持一种协议LACP(Link Aggregation Control Protocol)。它们互相通信,将多个网卡聚合称为一个网卡,多个网线聚合成一个网线,在网线之间可以进行负载均衡,也可以为了高可用作准备。 + + + +网卡有了高可用保证,但交换机还有问题。如果一个机架只有一个交换机,它挂了,那整个机架都不能上网了。因而TOR交换机也需要高可用,同理接入层和汇聚层的连接也需要高可用性,也不能单线连着。 + +最传统的方法是,部署两个接入交换机、两个汇聚交换机。服务器和两个接入交换机都连接,接入交换机和两个汇聚都连接,当然这样会形成环,所以需要启用STP协议,去除环,但是这样两个汇聚就只能一主一备了。STP协议里我们学过,只有一条路会起作用。 + + + +交换机有一种技术叫作堆叠,所以另一种方法是,将多个交换机形成一个逻辑的交换机,服务器通过多根线分配连到多个接入层交换机上,而接入层交换机多根线分别连接到多个交换机上,并且通过堆叠的私有协议,形成双活的连接方式。 + + + +由于对带宽要求更大,而且挂了影响也更大,所以两个堆叠可能就不够了,可以就会有更多的,比如四个堆叠为一个逻辑的交换机。 + +汇聚层将大量的计算节点相互连接在一起,形成一个集群。在这个集群里面,服务器之间通过二层互通,这个区域常称为一个POD(Point Of Delivery),有时候也称为一个可用区(Available Zone)。 + +当节点数目再多的时候,一个可用区放不下,需要将多个可用区连在一起,连接多个可用区的交换机称为核心交换机。 + + + +核心交换机吞吐量更大,高可用要求更高,肯定需要堆叠,但是往往仅仅堆叠,不足以满足吞吐量,因而还是需要部署多组核心交换机。核心和汇聚交换机之间为了高可用,也是全互连模式的。 + +这个时候还存在一个问题,出现环路怎么办? + +一种方式是,不同的可用区在不同的二层网络,需要分配不同的网段。汇聚和核心之间通过三层网络互通的,二层都不在一个广播域里面,不会存在二层环路的问题。三层有环是没有问题的,只要通过路由协议选择最佳的路径就可以了。那为啥二层不能有环路,而三层可以呢?你可以回忆一下二层环路的情况。 + + + +如图,核心层和汇聚层之间通过内部的路由协议OSPF,找到最佳的路径进行访问,而且还可以通过ECMP等价路由,在多个路径之间进行负载均衡和高可用。 + +但是随着数据中心里面的机器越来越多,尤其是有了云计算、大数据,集群规模非常大,而且都要求在一个二层网络里面。这就需要二层互连从汇聚层上升为核心层,也即在核心以下,全部是二层互连,全部在一个广播域里面,这就是常说的大二层。 + + + +如果大二层横向流量不大,核心交换机数目不多,可以做堆叠,但是如果横向流量很大,仅仅堆叠满足不了,就需要部署多组核心交换机,而且要和汇聚层进行全互连。由于堆叠只解决一个核心交换机组内的无环问题,而组之间全互连,还需要其他机制进行解决。 + +如果是STP,那部署多组核心无法扩大横向流量的能力,因为还是只有一组起作用。 + +于是大二层就引入了TRILL(Transparent Interconnection of Lots of Link),即多链接透明互联协议。它的基本思想是,二层环有问题,三层环没有问题,那就把三层的路由能力模拟在二层实现。 + +运行TRILL协议的交换机称为RBridge,是具有路由转发特性的网桥设备,只不过这个路由是根据MAC地址来的,不是根据IP来的。 + +Rbridage之间通过链路状态协议运作。记得这个路由协议吗?通过它可以学习整个大二层的拓扑,知道访问哪个MAC应该从哪个网桥走;还可以计算最短的路径,也可以通过等价的路由进行负载均衡和高可用性。 + + + +TRILL协议在原来的MAC头外面加上自己的头,以及外层的MAC头。TRILL头里面的Ingress RBridge,有点像IP头里面的源IP地址,Egress RBridge是目标IP地址,这两个地址是端到端的,在中间路由的时候,不会发生改变。而外层的MAC,可以有下一跳的Bridge,就像路由的下一跳,也是通过MAC地址来呈现的一样。 + +如图中所示的过程,有一个包要从主机A发送到主机B,中间要经过RBridge 1、RBridge 2、RBridge X等等,直到RBridge 3。在RBridge 2收到的包里面,分内外两层,内层就是传统的主机A和主机B的MAC地址以及内层的VLAN。 + +在外层首先加上一个TRILL头,里面描述这个包从RBridge 1进来的,要从RBridge 3出去,并且像三层的IP地址一样有跳数。然后再外面,目的MAC是RBridge 2,源MAC是RBridge 1,以及外层的VLAN。 + +当RBridge 2收到这个包之后,首先看MAC是否是自己的MAC,如果是,要看自己是不是Egress RBridge,也即是不是最后一跳;如果不是,查看跳数是不是大于0,然后通过类似路由查找的方式找到下一跳RBridge X,然后将包发出去。 + +RBridge 2发出去的包,内层的信息是不变的,外层的TRILL头里面。同样,描述这个包从RBridge 1进来的,要从RBridge 3出去,但是跳数要减1。外层的目标MAC变成RBridge X,源MAC变成RBridge 2。 + +如此一直转发,直到RBridge 3,将外层解出来,发送内层的包给主机B。 + +这个过程是不是和IP路由很像? + +对于大二层的广播包,也需要通过分发树的技术来实现。我们知道STP是将一个有环的图,通过去掉边形成一棵树,而分发树是一个有环的图形成多棵树,不同的树有不同的VLAN,有的广播包从VLAN A广播,有的从VLAN B广播,实现负载均衡和高可用。 + + + +核心交换机之外,就是边界路由器了。至此从服务器到数据中心边界的层次情况已经清楚了。 + +在核心交换上面,往往会挂一些安全设备,例如入侵检测、DDoS防护等等。这是整个数据中心的屏障,防止来自外来的攻击。核心交换机上往往还有负载均衡器,原理前面的章节已经说过了。 + +在有的数据中心里面,对于存储设备,还会有一个存储网络,用来连接SAN和NAS。但是对于新的云计算来讲,往往不使用传统的SAN和NAS,而使用部署在x86机器上的软件定义存储,这样存储也是服务器了,而且可以和计算节点融合在一个机架上,从而更加有效率,也就没有了单独的存储网络了。 + +于是整个数据中心的网络如下图所示。 + + + +这是一个典型的三层网络结构。这里的三层不是指IP层,而是指接入层、汇聚层、核心层三层。这种模式非常有利于外部流量请求到内部应用。这个类型的流量,是从外到内或者从内到外,对应到上面那张图里,就是从上到下,从下到上,上北下南,所以称为南北流量。 + +但是随着云计算和大数据的发展,节点之间的交互越来越多,例如大数据计算经常要在不同的节点将数据拷贝来拷贝去,这样需要经过交换机,使得数据从左到右,从右到左,左西右东,所以称为东西流量。 + +为了解决东西流量的问题,演进出了叶脊网络(Spine/Leaf)。 + + +叶子交换机(leaf),直接连接物理服务器。L2/L3网络的分界点在叶子交换机上,叶子交换机之上是三层网络。 + +脊交换机(spine switch),相当于核心交换机。叶脊之间通过ECMP动态选择多条路径。脊交换机现在只是为叶子交换机提供一个弹性的L3路由网络。南北流量可以不用直接从脊交换机发出,而是通过与leaf交换机并行的交换机,再接到边界路由器出去。 + + + + +传统的三层网络架构是垂直的结构,而叶脊网络架构是扁平的结构,更易于水平扩展。 + +小结 + +好了,复杂的数据中心就讲到这里了。我们来总结一下,你需要记住这三个重点。 + + +数据中心分为三层。服务器连接到接入层,然后是汇聚层,再然后是核心层,最外面是边界路由器和安全设备。 + +数据中心的所有链路都需要高可用性。服务器需要绑定网卡,交换机需要堆叠,三层设备可以通过等价路由,二层设备可以通过TRILL协议。 + +随着云和大数据的发展,东西流量相对于南北流量越来越重要,因而演化为叶脊网络结构。 + + +最后,给你留两个思考题: + + +对于数据中心来讲,高可用是非常重要的,每个设备都要考虑高可用,那跨机房的高可用,你知道应该怎么做吗? + +前面说的浏览新闻、购物、下载、看视频等行为,都是普通用户通过公网访问数据中心里面的资源。那IT管理员应该通过什么样的方式访问数据中心呢? + + +我们的专栏更新到第21讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/22VPN:朝中有人好做官.md b/专栏/趣谈网络协议/22VPN:朝中有人好做官.md new file mode 100644 index 0000000..0e90473 --- /dev/null +++ b/专栏/趣谈网络协议/22VPN:朝中有人好做官.md @@ -0,0 +1,252 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 22 VPN:朝中有人好做官 + 前面我们讲到了数据中心,里面很复杂,但是有的公司有多个数据中心,需要将多个数据中心连接起来,或者需要办公室和数据中心连接起来。这该怎么办呢? + + +第一种方式是走公网,但是公网太不安全,你的隐私可能会被别人偷窥。 + +第二种方式是租用专线的方式把它们连起来,这是土豪的做法,需要花很多钱。 + +第三种方式是用VPN来连接,这种方法比较折中,安全又不贵。 + + + + +VPN,全名Virtual Private Network,虚拟专用网,就是利用开放的公众网络,建立专用数据传输通道,将远程的分支机构、移动办公人员等连接起来。 + +VPN是如何工作的? + +VPN通过隧道技术在公众网络上仿真一条点到点的专线,是通过利用一种协议来传输另外一种协议的技术,这里面涉及三种协议:乘客协议、隧道协议和承载协议。 + +我们以IPsec协议为例来说明。 + + + +你知道如何通过自驾进行海南游吗?这其中,你的车怎么通过琼州海峡呢?这里用到轮渡,其实这就用到隧道协议。 + +在广州这边开车是有“协议”的,例如靠右行驶、红灯停、绿灯行,这个就相当于“被封装”的乘客协议。当然在海南那面,开车也是同样的协议。这就相当于需要连接在一起的一个公司的两个分部。 + +但是在海上坐船航行,也有它的协议,例如要看灯塔、要按航道航行等。这就是外层的承载协议。 + +那我的车如何从广州到海南呢?这就需要你遵循开车的协议,将车开上轮渡,所有通过轮渡的车都关在船舱里面,按照既定的规则排列好,这就是隧道协议。 + +在大海上,你的车是关在船舱里面的,就像在隧道里面一样,这个时候内部的乘客协议,也即驾驶协议没啥用处,只需要船遵从外层的承载协议,到达海南就可以了。 + +到达之后,外部承载协议的任务就结束了,打开船舱,将车开出来,就相当于取下承载协议和隧道协议的头。接下来,在海南该怎么开车,就怎么开车,还是内部的乘客协议起作用。 + +在最前面的时候说了,直接使用公网太不安全,所以接下来我们来看一种十分安全的VPN,IPsec VPN。这是基于IP协议的安全隧道协议,为了保证在公网上面信息的安全,因而采取了一定的机制保证安全性。 + + +机制一:私密性,防止信息泄露给未经授权的个人,通过加密把数据从明文变成无法读懂的密文,从而确保数据的私密性。- +前面讲HTTPS的时候,说过加密可以分为对称加密和非对称加密。对称加密速度快一些。而VPN一旦建立,需要传输大量数据,因而我们采取对称加密。但是同样,对称加密还是存在加密密钥如何传输的问题,这里需要用到因特网密钥交换(IKE,Internet Key Exchange)协议。 + +机制二:完整性,数据没有被非法篡改,通过对数据进行hash运算,产生类似于指纹的数据摘要,以保证数据的完整性。 + +机制三:真实性,数据确实是由特定的对端发出,通过身份认证可以保证数据的真实性。 + + +那如何保证对方就是真正的那个人呢? + + +第一种方法就是预共享密钥,也就是双方事先商量好一个暗号,比如“天王盖地虎,宝塔镇河妖”,对上了,就说明是对的。 + +另外一种方法就是用数字签名来验证。咋签名呢?当然是使用私钥进行签名,私钥只有我自己有,所以如果对方能用我的数字证书里面的公钥解开,就说明我是我。 + + +基于以上三个特性,组成了IPsec VPN的协议簇。这个协议簇内容比较丰富。 + + + +在这个协议簇里面,有两种协议,这两种协议的区别在于封装网络包的格式不一样。 + + +一种协议称为AH(Authentication Header),只能进行数据摘要 ,不能实现数据加密。 + +还有一种ESP(Encapsulating Security Payload),能够进行数据加密和数据摘要。 + + +在这个协议簇里面,还有两类算法,分别是加密算法和摘要算法。 + +这个协议簇还包含两大组件,一个用于VPN的双方要进行对称密钥的交换的IKE组件,另一个是VPN的双方要对连接进行维护的SA(Security Association)组件。 + +IPsec VPN的建立过程 + +下面来看IPsec VPN的建立过程,这个过程分两个阶段。 + +第一个阶段,建立IKE自己的SA。这个SA用来维护一个通过身份认证和安全保护的通道,为第二个阶段提供服务。在这个阶段,通过DH(Diffie-Hellman)算法计算出一个对称密钥K。 + +DH算法是一个比较巧妙的算法。客户端和服务端约定两个公开的质数p和q,然后客户端随机产生一个数a作为自己的私钥,服务端随机产生一个b作为自己的私钥,客户端可以根据p、q和a计算出公钥A,服务端根据p、q和b计算出公钥B,然后双方交换公钥A和B。 + +到此客户端和服务端可以根据已有的信息,各自独立算出相同的结果K,就是对称密钥。但是这个过程,对称密钥从来没有在通道上传输过,只传输了生成密钥的材料,通过这些材料,截获的人是无法算出的。 + + + +有了这个对称密钥K,接下来是第二个阶段,建立IPsec SA。在这个SA里面,双方会生成一个随机的对称密钥M,由K加密传给对方,然后使用M进行双方接下来通信的数据。对称密钥M是有过期时间的,会过一段时间,重新生成一次,从而防止被破解。 + +IPsec SA里面有以下内容: + + +SPI(Security Parameter Index),用于标识不同的连接; + +双方商量好的加密算法、哈希算法和封装模式; + +生存周期,超过这个周期,就需要重新生成一个IPsec SA,重新生成对称密钥。 + + + + +当IPsec建立好,接下来就可以开始打包封装传输了。 + + + +左面是原始的IP包,在IP头里面,会指定上一层的协议为TCP。ESP要对IP包进行封装,因而IP头里面的上一层协议为ESP。在ESP的正文里面,ESP的头部有双方商讨好的SPI,以及这次传输的序列号。 + +接下来全部是加密的内容。可以通过对称密钥进行解密,解密后在正文的最后,指明了里面的协议是什么。如果是IP,则需要先解析IP头,然后解析TCP头,这是从隧道出来后解封装的过程。 + +有了IPsec VPN之后,客户端发送的明文的IP包,都会被加上ESP头和IP头,在公网上传输,由于加密,可以保证不被窃取,到了对端后,去掉ESP的头,进行解密。 + + + +这种点对点的基于IP的VPN,能满足互通的要求,但是速度往往比较慢,这是由底层IP协议的特性决定的。IP不是面向连接的,是尽力而为的协议,每个IP包自由选择路径,到每一个路由器,都自己去找下一跳,丢了就丢了,是靠上一层TCP的重发来保证可靠性。 + + + +因为IP网络从设计的时候,就认为是不可靠的,所以即使同一个连接,也可能选择不同的道路,这样的好处是,一条道路崩溃的时候,总有其他的路可以走。当然,带来的代价就是,不断的路由查找,效率比较差。 + +和IP对应的另一种技术称为ATM。这种协议和IP协议的不同在于,它是面向连接的。你可以说TCP也是面向连接的啊。这两个不同,ATM和IP是一个层次的,和TCP不是一个层次的。 + +另外,TCP所谓的面向连接,是不停地重试来保证成功,其实下层的IP还是不面向连接的,丢了就丢了。ATM是传输之前先建立一个连接,形成一个虚拟的通路,一旦连接建立了,所有的包都按照相同的路径走,不会分头行事。 + + + +好处是不需要每次都查路由表的,虚拟路径已经建立,打上了标签,后续的包傻傻的跟着走就是了,不用像IP包一样,每个包都思考下一步怎么走,都按相同的路径走,这样效率会高很多。 + +但是一旦虚拟路径上的某个路由器坏了,则这个连接就断了,什么也发不过去了,因为其他的包还会按照原来的路径走,都掉坑里了,它们不会选择其他的路径走。 + +ATM技术虽然没有成功,但其屏弃了繁琐的路由查找,改为简单快速的标签交换,将具有全局意义的路由表改为只有本地意义的标签表,这些都可以大大提高一台路由器的转发功力。 + +有没有一种方式将两者的优点结合起来呢?这就是多协议标签交换(MPLS,Multi-Protocol Label Switching)。MPLS的格式如图所示,在原始的IP头之外,多了MPLS的头,里面可以打标签。 + + + +在二层头里面,有类型字段,0x0800表示IP,0x8847表示MPLS Label。 + +在MPLS头里面,首先是标签值占20位,接着是3位实验位,再接下来是1位栈底标志位,表示当前标签是否位于栈底了。这样就允许多个标签被编码到同一个数据包中,形成标签栈。最后是8位TTL存活时间字段,如果标签数据包的出发TTL值为0,那么该数据包在网络中的生命期被认为已经过期了。 + +有了标签,还需要设备认这个标签,并且能够根据这个标签转发,这种能够转发标签的路由器称为标签交换路由器(LSR,Label Switching Router)。 + +这种路由器会有两个表格,一个就是传统的FIB,也即路由表,另一个就是LFIB,标签转发表。有了这两个表,既可以进行普通的路由转发,也可以进行基于标签的转发。 + + + +有了标签转发表,转发的过程如图所示,就不用每次都进行普通路由的查找了。 + +这里我们区分MPLS区域和非MPLS区域。在MPLS区域中间,使用标签进行转发,非MPLS区域,使用普通路由转发,在边缘节点上,需要有能力将对于普通路由的转发,变成对于标签的转发。 + +例如图中要访问114.1.1.1,在边界上查找普通路由,发现马上要进入MPLS区域了,进去了对应标签1,于是在IP头外面加一个标签1,在区域里面,标签1要变成标签3,标签3到达出口边缘,将标签去掉,按照路由发出。 + +这样一个通过标签转换而建立的路径称为LSP,标签交换路径。在一条LSP上,沿数据包传送的方向,相邻的LSR分别叫上游LSR(upstream LSR)和下游LSR(downstream LSR)。 + +有了标签,转发是很简单的事,但是如何生成标签,却是MPLS中最难修炼的部分。在MPLS秘笈中,这部分被称为LDP(Label Distribution Protocol),是一个动态的生成标签的协议。 + +其实LDP与IP帮派中的路由协议十分相像,通过LSR的交互,互相告知去哪里应该打哪个标签,称为标签分发,往往是从下游开始的。 + + + +如果有一个边缘节点发现自己的路由表中出现了新的目的地址,它就要给别人说,我能到达一条新的路径了。 + +如果此边缘节点存在上游LSR,并且尚有可供分配的标签,则该节点为新的路径分配标签,并向上游发出标签映射消息,其中包含分配的标签等信息。 + +收到标签映射消息的LSR记录相应的标签映射信息,在其标签转发表中增加相应的条目。此LSR为它的上游LSR分配标签,并继续向上游LSR发送标签映射消息。 + +当入口LSR收到标签映射消息时,在标签转发表中增加相应的条目。这时,就完成了LSP的建立。有了标签,转发轻松多了,但是这个和VPN什么关系呢? + +可以想象,如果我们VPN通道里面包的转发,都是通过标签的方式进行,效率就会高很多。所以要想个办法把MPLS应用于VPN。 + + + +在MPLS VPN中,网络中的路由器分成以下几类: + + +PE(Provider Edge):运营商网络与客户网络相连的边缘网络设备; + +CE(Customer Edge):客户网络与PE相连接的边缘设备; + +P(Provider):这里特指运营商网络中除PE之外的其他运营商网络设备。 + + +为什么要这样分呢?因为我们发现,在运营商网络里面,也即P Router之间,使用标签是没有问题的,因为都在运营商的管控之下,对于网段,路由都可以自己控制。但是一旦客户要接入这个网络,就复杂得多。 + +首先是客户地址重复的问题。客户所使用的大多数都是私网的地址(192.168.X.X;10.X.X.X;172.X.X.X),而且很多情况下都会与其它的客户重复。 + +比如,机构A和机构B都使用了192.168.101.0/24网段的地址,这就发生了地址空间重叠(Overlapping Address Spaces)。 + +首先困惑的是BGP协议,既然VPN将两个数据中心连起来,应该看起来像一个数据中心一样,那么如何到达另一端需要通过BGP将路由广播过去,传统BGP无法正确处理地址空间重叠的VPN的路由。 + +假设机构A和机构B都使用了192.168.101.0/24网段的地址,并各自发布了一条去往此网段的路由,BGP将只会选择其中一条路由,从而导致去往另一个VPN的路由丢失。 + +所以PE路由器之间使用特殊的MP-BGP来发布VPN路由,在相互沟通的消息中,在一般32位IPv4的地址之前加上一个客户标示的区分符用于客户地址的区分,这种称为VPN-IPv4地址族,这样PE路由器会收到如下的消息,机构A的192.168.101.0/24应该往这面走,机构B的192.168.101.0/24则应该去另外一个方向。 + +另外困惑的是路由表,当两个客户的IP包到达PE的时候,PE就困惑了,因为网段是重复的。 + +如何区分哪些路由是属于哪些客户VPN内的?如何保证VPN业务路由与普通路由不相互干扰? + +在PE上,可以通过VRF(VPN Routing&Forwarding Instance)建立每个客户一个路由表,与其它VPN客户路由和普通路由相互区分。可以理解为专属于客户的小路由器。 + +远端PE通过MP-BGP协议把业务路由放到近端PE,近端PE根据不同的客户选择出相关客户的业务路由放到相应的VRF路由表中。 + +VPN报文转发采用两层标签方式: + + +第一层(外层)标签在骨干网内部进行交换,指示从PE到对端PE的一条LSP。VPN报文利用这层标签,可以沿LSP到达对端PE; + +第二层(内层)标签在从对端PE到达CE时使用,在PE上,通过查找VRF表项,指示报文应被送到哪个VPN用户,或者更具体一些,到达哪一个CE。这样,对端PE根据内层标签可以找到转发报文的接口。 + + + + +我们来举一个例子,看MPLS VPN的包发送过程。 + + +机构A和机构B都发出一个目的地址为192.168.101.0/24的IP报文,分别由各自的CE将报文发送至PE。 + +PE会根据报文到达的接口及目的地址查找VPN实例表项VRF,匹配后将报文转发出去,同时打上内层和外层两个标签。假设通过MP-BGP配置的路由,两个报文在骨干网走相同的路径。 + +MPLS网络利用报文的外层标签,将报文传送到出口PE,报文在到达出口PE 2前一跳时已经被剥离外层标签,仅含内层标签。 + +出口PE根据内层标签和目的地址查找VPN实例表项VRF,确定报文的出接口,将报文转发至各自的CE。 + +CE根据正常的IP转发过程将报文传送到目的地。 + + +小结 + +好了,这一节就到这里了,我们来总结一下: + + +VPN可以将一个机构的多个数据中心通过隧道的方式连接起来,让机构感觉在一个数据中心里面,就像自驾游通过琼州海峡一样; + +完全基于软件的IPsec VPN可以保证私密性、完整性、真实性、简单便宜,但是性能稍微差一些; + +MPLS-VPN综合和IP转发模式和ATM的标签转发模式的优势,性能较好,但是需要从运营商购买。 + + +接下来,给你留两个思考题: + + +当前业务的高可用性和弹性伸缩很重要,所以很多机构都会在自建私有云之外,采购公有云,你知道私有云和公有云应该如何打通吗? + +前面所有的上网行为,都是基于电脑的,但是移动互联网越来越成为核心,你知道手机上网都需要哪些协议吗? + + +我们的专栏更新到第22讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/23移动网络:去巴塞罗那,手机也上不了脸书.md b/专栏/趣谈网络协议/23移动网络:去巴塞罗那,手机也上不了脸书.md new file mode 100644 index 0000000..e0b87f0 --- /dev/null +++ b/专栏/趣谈网络协议/23移动网络:去巴塞罗那,手机也上不了脸书.md @@ -0,0 +1,211 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 23 移动网络:去巴塞罗那,手机也上不了脸书 + 前面讲的都是电脑上网的场景,那使用手机上网有什么不同呢? + +移动网络的发展历程 + +你一定知道手机上网有2G、3G、4G的说法,究竟这都是什么意思呢?有一个通俗的说法就是:用2G看txt,用3G看jpg,用4G看avi。 + +2G网络 + +手机本来是用来打电话的,不是用来上网的,所以原来在2G时代,上网使用的不是IP网络,而是电话网络,走模拟信号,专业名称为公共交换电话网(PSTN,Public Switched Telephone Network)。 + +那手机不连网线,也不连电话线,它是怎么上网的呢? + +手机是通过收发无线信号来通信的,专业名称是Mobile Station,简称MS,需要嵌入SIM。手机是客户端,而无线信号的服务端,就是基站子系统(BSS,Base Station SubsystemBSS)。至于什么是基站,你可以回想一下,你在爬山的时候,是不是看到过信号塔?我们平时城市里面的基站比较隐蔽,不容易看到,所以只有在山里才会注意到。正是这个信号塔,通过无线信号,让你的手机可以进行通信。 + +但是你要知道一点,无论无线通信如何无线,最终还是要连接到有线的网络里。前面讲[数据中心]的时候我也讲过,电商的应用是放在数据中心的,数据中心的电脑都是插着网线的。 + +因而,基站子系统分两部分,一部分对外提供无线通信,叫作基站收发信台(BTS,Base Transceiver Station),另一部分对内连接有线网络,叫作基站控制器(BSC,Base Station Controller)。基站收发信台通过无线收到数据后,转发给基站控制器。 + +这部分属于无线的部分,统称为无线接入网(RAN,Radio Access Network)。 + +基站控制器通过有线网络,连接到提供手机业务的运营商的数据中心,这部分称为核心网(CN,Core Network)。核心网还没有真的进入互联网,这部分还是主要提供手机业务,是手机业务的有线部分。 + +首先接待基站来的数据的是移动业务交换中心(MSC,Mobile Service Switching Center),它是进入核心网的入口,但是它不会让你直接连接到互联网上。 + +因为在让你的手机真正进入互联网之前,提供手机业务的运营商,需要认证是不是合法的手机接入。你别自己造了一张手机卡,就连接上来。鉴权中心(AUC,Authentication Center)和设备识别寄存器(EIR,Equipment Identity Register)主要是负责安全性的。 + +另外,需要看你是本地的号,还是外地的号,这个牵扯到计费的问题,异地收费还是很贵的。访问位置寄存器(VLR,Visit Location Register)是看你目前在的地方,归属位置寄存器(HLR,Home Location Register)是看你的号码归属地。 + +当你的手机卡既合法又有钱的时候,才允许你上网,这个时候需要一个网关,连接核心网和真正的互联网。网关移动交换中心(GMSC ,Gateway Mobile Switching Center)就是干这个的,然后是真正的互连网。在2G时代,还是电话网络PSTN。 + +数据中心里面的这些模块统称为网络子系统(NSS,Network and Switching Subsystem)。 + + + +因而2G时代的上网如图所示,我们总结一下,有这几个核心点: + + +手机通过无线信号连接基站; + +基站一面朝前接无线,一面朝后接核心网; + +核心网一面朝前接到基站请求,一是判断你是否合法,二是判断你是不是本地号,还有没有钱,一面通过网关连接电话网络。 + + +2.5G网络 + +后来从2G到了2.5G,也即在原来电路交换的基础上,加入了分组交换业务,支持Packet的转发,从而支持IP网络。 + +在上述网络的基础上,基站一面朝前接无线,一面朝后接核心网。在朝后的组件中,多了一个分组控制单元(PCU,Packet Control Unit),用以提供分组交换通道。 + +在核心网里面,有个朝前的接待员(SGSN,Service GPRS Supported Node)和朝后连接IP网络的网关型GPRS支持节点(GGSN,Gateway GPRS Supported Node)。 + + + +3G网络 + +到了3G时代,主要是无线通信技术有了改进,大大增加了无线的带宽。 + +以W-CDMA为例,理论最高2M的下行速度,因而基站改变了,一面朝外的是Node B,一面朝内连接核心网的是无线网络控制器(RNC,Radio Network Controller)。核心网以及连接的IP网络没有什么变化。 + + + +4G网络 + +然后就到了今天的4G网络,基站为eNodeB,包含了原来Node B和RNC的功能,下行速度向百兆级别迈进。另外,核心网实现了控制面和数据面的分离,这个怎么理解呢? + +在前面的核心网里面,有接待员MSC或者SGSN,你会发现检查是否合法是它负责,转发数据也是它负责,也即控制面和数据面是合二为一的,这样灵活性比较差,因为控制面主要是指令,多是小包,往往需要高的及时性;数据面主要是流量,多是大包,往往需要吞吐量。 + +于是有了下面这个架构。 + + + +HSS用于存储用户签约信息的数据库,其实就是你这个号码归属地是哪里的,以及一些认证信息。 + +MME是核心控制网元,是控制面的核心,当手机通过eNodeB连上的时候,MME会根据HSS的信息,判断你是否合法。如果允许连上来,MME不负责具体的数据的流量,而是MME会选择数据面的SGW和PGW,然后告诉eNodeB,我允许你连上来了,你连接它们吧。 + +于是手机直接通过eNodeB连接SGW,连上核心网,SGW相当于数据面的接待员,并通过PGW连到IP网络。PGW就是出口网关。在出口网关,有一个组件PCRF,称为策略和计费控制单元,用来控制上网策略和流量的计费。 + +4G网络协议解析 + +我们来仔细看一下4G网络的协议,真的非常复杂。我们将几个关键组件放大来看。 + + + +控制面协议 + +其中虚线部分是控制面的协议。当一个手机想上网的时候,先要连接eNodeB,并通过S1-MME接口,请求MME对这个手机进行认证和鉴权。S1-MME协议栈如下图所示。 + + + +UE就是你的手机,eNodeB还是两面派,朝前对接无线网络,朝后对接核心网络,在控制面对接的是MME。 + +eNodeB和MME之间的连接就是很正常的IP网络,但是这里面在IP层之上,却既不是TCP,也不是UDP,而是SCTP。这也是传输层的协议,也是面向连接的,但是更加适合移动网络。 它继承了TCP较为完善的拥塞控制并改进TCP的一些不足之处。 + +SCTP的第一个特点是多宿主。一台机器可以有多个网卡,而对于TCP连接来讲,虽然服务端可以监听0.0.0.0,也就是从哪个网卡来的连接都能接受,但是一旦建立了连接,就建立了四元组,也就选定了某个网卡。 + +SCTP引入了联合(association)的概念,将多个接口、多条路径放到一个联合中来。当检测到一条路径失效时,协议就会通过另外一条路径来发送通信数据。应用程序甚至都不必知道发生了故障、恢复,从而提供更高的可用性和可靠性。 + +SCTP的第二个特点是将一个联合分成多个流。一个联合中的所有流都是独立的,但均与该联合相关。每个流都给定了一个流编号,它被编码到SCTP报文中,通过联合在网络上传送。在TCP的机制中,由于强制顺序,导致前一个不到达,后一个就得等待,SCTP的多个流不会相互阻塞。 + +SCTP的第三个特点是四次握手,防止SYN攻击。在TCP中是三次握手,当服务端收到客户的SYN之后,返回一个SYN-ACK之前,就建立数据结构,并记录下状态,等待客户端发送ACK的ACK。当恶意客户端使用虚假的源地址来伪造大量SYN报文时,服务端需要分配大量的资源,最终耗尽资源,无法处理新的请求。 + +SCTP可以通过四次握手引入Cookie的概念,来有效地防止这种攻击的产生。在SCTP中,客户机使用一个INIT报文发起一个连接。服务器使用一个INIT-ACK报文进行响应,其中就包括了Cookie。然后客户端就使用一个COOKIE-ECHO报文进行响应,其中包含了服务器所发送的Cookie。这个时候,服务器为这个连接分配资源,并通过向客户机发送一个COOKIE-ACK报文对其进行响应。 + +SCTP的第四个特点是将消息分帧。TCP是面向流的,也即发送的数据没头没尾,没有明显的界限。这对于发送数据没有问题,但是对于发送一个个消息类型的数据,就不太方便。有可能客户端写入10个字节,然后再写入20个字节。服务端不是读出10个字节的一个消息,再读出20个字节的一个消息,而有可能读入25个字节,再读入5个字节,需要业务层去组合成消息。 + +SCTP借鉴了UDP的机制,在数据传输中提供了消息分帧功能。当一端对一个套接字执行写操作时,可确保对等端读出的数据大小与此相同。 + +SCTP的第五个特点是断开连接是三次挥手。在TCP里面,断开连接是四次挥手,允许另一端处于半关闭的状态。SCTP选择放弃这种状态,当一端关闭自己的套接字时,对等的两端全部需要关闭,将来任何一端都不允许再进行数据的移动了。 + +当MME通过认证鉴权,同意这个手机上网的时候,需要建立一个数据面的数据通路。建立通路的过程还是控制面的事情,因而使用的是控制面的协议GTP-C。 + +建设的数据通路分两段路,其实是两个隧道。一段是从eNodeB到SGW,这个数据通路由MME通过S1-MME协议告诉eNodeB,它是隧道的一端,通过S11告诉SGW,它是隧道的另一端。第二端是从SGW到PGW,SGW通过S11协议知道自己是其中一端,并主动通过S5协议,告诉PGW它是隧道的另一端。 + +GTP-C协议是基于UDP的,这是[UDP的“城会玩”]中的一个例子。如果看GTP头,我们可以看到,这里面有隧道的ID,还有序列号。 + + + +通过序列号,不用TCP,GTP-C自己就可以实现可靠性,为每个输出信令消息分配一个依次递增的序列号,以确保信令消息的按序传递,并便于检测重复包。对于每个输出信令消息启动定时器,在定时器超时前未接收到响应消息则进行重发。 + +数据面协议 + +当两个隧道都打通,接在一起的时候,PGW会给手机分配一个IP地址,这个IP地址是隧道内部的IP地址,可以类比为IPsec协议里面的IP地址。这个IP地址是归手机运营商管理的。然后,手机可以使用这个IP地址,连接eNodeB,从eNodeB经过S1-U协议,通过第一段隧道到达SGW,再从SGW经过S8协议,通过第二段隧道到达PGW,然后通过PGW连接到互联网。 + +数据面的协议都是通过GTP-U,如图所示。 + + + +手机每发出的一个包,都由GTP-U隧道协议封装起来,格式如下。 + + + +和IPsec协议很类似,分为乘客协议、隧道协议、承载协议。其中乘客协议是手机发出来的包,IP是手机的IP,隧道协议里面有隧道ID,不同的手机上线会建立不同的隧道,因而需要隧道ID来标识。承载协议的IP地址是SGW和PGW的IP地址。 + +手机上网流程 + +接下来,我们来看一个手机开机之后上网的流程,这个过程称为Attach。可以看出来,移动网络还是很复杂的。因为这个过程要建立很多的隧道,分配很多的隧道ID,所以我画了一个图来详细说明这个过程。 + + + + +手机开机以后,在附近寻找基站eNodeB,找到后给eNodeB发送Attach Request,说“我来啦,我要上网”。 + +eNodeB将请求发给MME,说“有个手机要上网”。 + +MME去请求手机,一是认证,二是鉴权,还会请求HSS看看有没有钱,看看是在哪里上网。 + +当MME通过了手机的认证之后,开始分配隧道,先告诉SGW,说要创建一个会话(Create Session)。在这里面,会给SGW分配一个隧道ID t1,并且请求SGW给自己也分配一个隧道ID。 + +SGW转头向PGW请求建立一个会话,为PGW的控制面分配一个隧道ID t2,也给PGW的数据面分配一个隧道ID t3,并且请求PGW给自己的控制面和数据面分配隧道ID。 + +PGW回复SGW说“创建会话成功”,使用自己的控制面隧道ID t2,回复里面携带着给SGW控制面分配的隧道ID t4和控制面的隧道ID t5,至此SGW和PGW直接的隧道建设完成。双方请求对方,都要带着对方给自己分配的隧道ID,从而标志是这个手机的请求。 + +接下来SGW回复MME说“创建会话成功”,使用自己的隧道ID t1访问MME,回复里面有给MME分配隧道ID t6,也有SGW给eNodeB分配的隧道ID t7。 + +当MME发现后面的隧道都建设成功之后,就告诉eNodeB,“后面的隧道已经建设完毕,SGW给你分配的隧道ID是t7,你可以开始连上来了,但是你也要给SGW分配一个隧道ID”。 + +eNodeB告诉MME自己给SGW分配一个隧道,ID为t8。 + +MME将eNodeB给SGW分配的隧道ID t8告知SGW,从而前面的隧道也建设完毕。 + + +这样,手机就可以通过建立的隧道成功上网了。 + +异地上网问题 + +接下来我们考虑异地上网的事情。 + +为什么要分SGW和PGW呢,一个GW不可以吗?SGW是你本地的运营商的设备,而PGW是你所属的运营商的设备。 + +如果你在巴塞罗那,一下飞机,手机开机,周围搜寻到的肯定是巴塞罗那的eNodeB。通过MME去查询国内运营商的HSS,看你是否合法,是否还有钱。如果允许上网,你的手机和巴塞罗那的SGW会建立一个隧道,然后巴塞罗那的SGW和国内运营商的PGW建立一个隧道,然后通过国内运营商的PGW上网。 + + + +因此,判断你是否能上网的是国内运营商的HSS,控制你上网策略的是国内运营商的PCRF,给手机分配的IP地址也是国内运营商的PGW负责的,给手机分配的IP地址也是国内运营商里统计的。运营商由于是在PGW里面统计的,这样你的上网流量全部通过国内运营商即可,只不过巴塞罗那运营商也要和国内运营商进行流量结算。 + +由于你的上网策略是由国内运营商在PCRF中控制的,因而你还是上不了脸书。 + +小结 + +好了,这一节就到这里了,我们来总结一下: + + +移动网络的发展历程从2G到3G,再到4G,逐渐从打电话的功能为主,向上网的功能为主转变; + +请记住4G网络的结构,有eNodeB、MME、SGW、PGW等,分控制面协议和数据面协议,你可以对照着结构,试着说出手机上网的流程; + +即便你在国外的运营商下上网,也是要通过国内运营商控制的,因而也上不了脸书。 + + +最后,给你留两个思考题: + + +咱们上网都有套餐,有交钱多的,有交钱少的,你知道移动网络是如何控制不同优先级的用户的上网流量的吗? + +前面讲过的所有的网络都是基于物理机的,随着云计算兴起,无论是电商,还是移动网络都要部署在云中了,你知道云中网络的设计有哪些要点吗? + + +我们的专栏更新到第23讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/24云中网络:自己拿地成本高,购买公寓更灵活.md b/专栏/趣谈网络协议/24云中网络:自己拿地成本高,购买公寓更灵活.md new file mode 100644 index 0000000..44a40e7 --- /dev/null +++ b/专栏/趣谈网络协议/24云中网络:自己拿地成本高,购买公寓更灵活.md @@ -0,0 +1,169 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 24 云中网络:自己拿地成本高,购买公寓更灵活 + 前面我们讲了,数据中心里面堆着一大片一大片的机器,用网络连接起来,机器数目一旦非常多,人们就发现,维护这么一大片机器还挺麻烦的,有好多不灵活的地方。 + + +采购不灵活:如果客户需要一台电脑,那就需要自己采购、上架、插网线、安装操作系统,周期非常长。一旦采购了,一用就N年,不能退货,哪怕业务不做了,机器还在数据中心里留着。 + +运维不灵活:一旦需要扩容CPU、内存、硬盘,都需要去机房手动弄,非常麻烦。 + +规格不灵活:采购的机器往往动不动几百G的内存,而每个应用往往可能只需要4核8G,所以很多应用混合部署在上面,端口各种冲突,容易相互影响。 + +复用不灵活:一台机器,一旦一个用户不用了,给另外一个用户,那就需要重装操作系统。因为原来的操作系统可能遗留很多数据,非常麻烦。 + + +从物理机到虚拟机 + +为了解决这些问题,人们发明了一种叫虚拟机的东西,并基于它产生了云计算技术。 + +其实在你的个人电脑上,就可以使用虚拟机。如果你对虚拟机没有什么概念,你可以下载一个桌面虚拟化的软件,自己动手尝试一下。它可以让你灵活地指定CPU的数目、内存的大小、硬盘的大小,可以有多个网卡,然后在一台笔记本电脑里面创建一台或者多台虚拟电脑。不用的时候,一点删除就没有了。 + +在数据中心里面,也有一种类似的开源技术qemu-kvm,能让你在一台巨大的物理机里面,掏出一台台小的机器。这套软件就能解决上面的问题:一点就能创建,一点就能销毁。你想要多大就有多大,每次创建的系统还都是新的。 + +我们常把物理机比喻为自己拿地盖房子,而虚拟机则相当于购买公寓,更加灵活方面,随时可买可卖。 那这个软件为什么能做到这些事儿呢? + +它用的是软件模拟硬件的方式。刚才说了,数据中心里面用的qemu-kvm。从名字上来讲,emu就是Emulator(模拟器)的意思,主要会模拟CPU、内存、网络、硬盘,使得虚拟机感觉自己在使用独立的设备,但是真正使用的时候,当然还是使用物理的设备。 + +例如,多个虚拟机轮流使用物理CPU,内存也是使用虚拟内存映射的方式,最终映射到物理内存上。硬盘在一块大的文件系统上创建一个N个G的文件,作为虚拟机的硬盘。 + +简单比喻,虚拟化软件就像一个“骗子”,向上“骗”虚拟机里面的应用,让它们感觉独享资源,其实自己啥都没有,全部向下从物理机里面弄。 + +虚拟网卡的原理 + +那网络是如何“骗”应用的呢?如何将虚拟机的网络和物理机的网络连接起来? + + + +首先,虚拟机要有一张网卡。对于qemu-kvm来说,这是通过Linux上的一种TUN/TAP技术来实现的。 + +虚拟机是物理机上跑着的一个软件。这个软件可以像其他应用打开文件一样,打开一个称为TUN/TAP的Char Dev(字符设备文件)。打开了这个字符设备文件之后,在物理机上就能看到一张虚拟TAP网卡。 + +虚拟化软件作为“骗子”,会将打开的这个文件,在虚拟机里面虚拟出一张网卡,让虚拟机里面的应用觉得它们真有一张网卡。于是,所有的网络包都往这里发。 + +当然,网络包会到虚拟化软件这里。它会将网络包转换成为文件流,写入字符设备,就像写一个文件一样。内核中TUN/TAP字符设备驱动会收到这个写入的文件流,交给TUN/TAP的虚拟网卡驱动。这个驱动将文件流再次转成网络包,交给TCP/IP协议栈,最终从虚拟TAP网卡发出来,成为标准的网络包。 + +就这样,几经转手,数据终于从虚拟机里面,发到了虚拟机外面。 + +虚拟网卡连接到云中 + +我们就这样有了虚拟TAP网卡。接下来就要看,这个卡怎么接入庞大的数据中心网络中。 + +在接入之前,我们先来看,云计算中的网络都需要注意哪些点。 + + +共享:尽管每个虚拟机都会有一个或者多个虚拟网卡,但是物理机上可能只有有限的网卡。那这么多虚拟网卡如何共享同一个出口? + +隔离:分两个方面,一个是安全隔离,两个虚拟机可能属于两个用户,那怎么保证一个用户的数据不被另一个用户窃听?一个是流量隔离,两个虚拟机,如果有一个疯狂下片,会不会导致另外一个上不了网? + +互通:分两个方面,一个是如果同一台机器上的两个虚拟机,属于同一个用户的话,这两个如何相互通信?另一个是如果不同物理机上的两个虚拟机,属于同一个用户的话,这两个如何相互通信? + +灵活:虚拟机和物理不同,会经常创建、删除,从一个机器漂移到另一台机器,有的互通、有的不通等等,灵活性比物理网络要好得多,需要能够灵活配置。 + + +共享与互通问题 + +这些问题,我们一个个来解决。 + +首先,一台物理机上有多个虚拟机,有多个虚拟网卡,这些虚拟网卡如何连在一起,进行相互访问,并且可以访问外网呢? + +还记得我们在大学宿舍里做的事情吗?你可以想象你的物理机就是你们宿舍,虚拟机就是你的个人电脑,这些电脑应该怎么连接起来呢?当然应该买一个交换机。 + +在物理机上,应该有一个虚拟的交换机,在Linux上有一个命令叫作brctl,可以创建虚拟的网桥brctl addbr br0。创建出来以后,将两个虚拟机的虚拟网卡,都连接到虚拟网桥brctl addif br0 tap0上,这样将两个虚拟机配置相同的子网网段,两台虚拟机就能够相互通信了。 + + + +那这些虚拟机如何连接外网呢?在桌面虚拟化软件上面,我们能看到以下选项。 + + + +这里面,host-only的网络对应的,其实就是上面两个虚拟机连到一个br0虚拟网桥上,而且不考虑访问外部的场景,只要虚拟机之间能够相互访问就可以了。 + +如果要访问外部,往往有两种方式。 + +一种方式称为桥接。如果在桌面虚拟化软件上选择桥接网络,则在你的笔记本电脑上,就会形成下面的结构。 + + + +每个虚拟机都会有虚拟网卡,在你的笔记本电脑上,会发现多了几个网卡,其实是虚拟交换机。这个虚拟交换机将虚拟机连接在一起。在桥接模式下,物理网卡也连接到这个虚拟交换机上,物理网卡在桌面虚拟化软件上,在“界面名称”那里选定。 + +如果使用桥接网络,当你登录虚拟机里看IP地址的时候会发现,你的虚拟机的地址和你的笔记本电脑的,以及你旁边的同事的电脑的网段是一个网段。这是为什么呢?这其实相当于将物理机和虚拟机放在同一个网桥上,相当于这个网桥上有三台机器,是一个网段的,全部打平了。我将图画成下面的样子你就好理解了。 + + + +在数据中心里面,采取的也是类似的技术,只不过都是Linux,在每台机器上都创建网桥br0,虚拟机的网卡都连到br0上,物理网卡也连到br0上,所有的br0都通过物理网卡出来连接到物理交换机上。 + + + +同样我们换一个角度看待这个拓扑图。同样是将网络打平,虚拟机会和你的物理网络具有相同的网段。 + + + +在这种方式下,不但解决了同一台机器的互通问题,也解决了跨物理机的互通问题,因为都在一个二层网络里面,彼此用相同的网段访问就可以了。但是当规模很大的时候,会存在问题。 + +你还记得吗?在一个二层网络里面,最大的问题是广播。一个数据中心的物理机已经很多了,广播已经非常严重,需要通过VLAN进行划分。如果使用了虚拟机,假设一台物理机里面创建10台虚拟机,全部在一个二层网络里面,那广播就会很严重,所以除非是你的桌面虚拟机或者数据中心规模非常小,才可以使用这种相对简单的方式。 + +另外一种方式称为NAT。如果在桌面虚拟化软件中使用NAT模式,在你的笔记本电脑上会出现如下的网络结构。 + + + +在这种方式下,你登录到虚拟机里面查看IP地址,会发现虚拟机的网络是虚拟机的,物理机的网络是物理机的,两个不相同。虚拟机要想访问物理机的时候,需要将地址NAT成为物理机的地址。 + +除此之外,它还会在你的笔记本电脑里内置一个DHCP服务器,为笔记本电脑上的虚拟机动态分配IP地址。因为虚拟机的网络自成体系,需要进行IP管理。为什么桥接方式不需要呢?因为桥接将网络打平了,虚拟机的IP地址应该由物理网络的DHCP服务器分配。 + +在数据中心里面,也是使用类似的方式。这种方式更像是真的将你宿舍里面的情况,搬到一台物理机上来。 + + + +虚拟机是你的电脑,路由器和DHCP Server相当于家用路由器或者寝室长的电脑,物理网卡相当于你们宿舍的外网网口,用于访问互联网。所有电脑都通过内网网口连接到一个网桥br0上,虚拟机要想访问互联网,需要通过br0连到路由器上,然后通过路由器将请求NAT成为物理网络的地址,转发到物理网络。 + +如果是你自己登录到物理机上做个简单配置,你可以简化一下。例如将虚拟机所在网络的网关的地址直接配置到br0上,不用DHCP Server,手动配置每台虚拟机的IP地址,通过命令iptables -t nat -A POSTROUTING -o ethX -j MASQUERADE,直接在物理网卡ethX上进行NAT,所有从这个网卡出去的包都NAT成这个网卡的地址。通过设置net.ipv4.ip_forward = 1,开启物理机的转发功能,直接做路由器,而不用单独的路由器,这样虚拟机就能直接上网了。 + + + +隔离问题 + +解决了互通的问题,接下来就是隔离的问题。 + +如果一台机器上的两个虚拟机不属于同一个用户,怎么办呢?好在brctl创建的网桥也是支持VLAN功能的,可以设置两个虚拟机的tag,这样在这个虚拟网桥上,两个虚拟机是不互通的。 + +但是如何跨物理机互通,并且实现VLAN的隔离呢?由于brctl创建的网桥上面的tag是没办法在网桥之外的范围内起作用的,因此我们需要寻找其他的方式。 + +有一个命令vconfig,可以基于物理网卡eth0创建带VLAN的虚拟网卡,所有从这个虚拟网卡出去的包,都带这个VLAN,如果这样,跨物理机的互通和隔离就可以通过这个网卡来实现。 + + + +首先为每个用户分配不同的VLAN,例如有一个用户VLAN 10,一个用户VLAN 20。在一台物理机上,基于物理网卡,为每个用户用vconfig创建一个带VLAN的网卡。不同的用户使用不同的虚拟网桥,带VLAN的虚拟网卡也连接到虚拟网桥上。 + +这样是否能保证两个用户的隔离性呢?不同的用户由于网桥不通,不能相互通信,一旦出了网桥,由于VLAN不同,也不会将包转发到另一个网桥上。另外,出了物理机,也是带着VLAN ID的。只要物理交换机也是支持VLAN的,到达另一台物理机的时候,VLAN ID依然在,它只会将包转发给相同VLAN的网卡和网桥,所以跨物理机,不同的VLAN也不会相互通信。 + +使用brctl创建出来的网桥功能是简单的,基于VLAN的虚拟网卡也能实现简单的隔离。但是这都不是大规模云平台能够满足的,一个是VLAN的隔离,数目太少。前面我们学过,VLAN ID只有4096个,明显不够用。另外一点是这个配置不够灵活。谁和谁通,谁和谁不通,流量的隔离也没有实现,还有大量改进的空间。 + +小结 + +好了,这一节就到这里了,我们来总结一下: + + +云计算的关键技术是虚拟化,这里我们重点关注的是,虚拟网卡通过打开TUN/TAP字符设备的方式,将虚拟机内外连接起来; + +云中的网络重点关注四个方面,共享、隔离、互通、灵活。其中共享和互通有两种常用的方式,分别是桥接和NAT,隔离可以通过VLAN的方式。 + + +接下来,给你留两个思考题。 + + +为了直观,这一节的内容我们以桌面虚拟化系统举例。在数据中心里面,有一款著名的开源软件OpenStack,这一节讲的网络连通方式对应OpenStack中的哪些模型呢? + +这一节的最后,我们也提到了,本节提到的网络配置方式比较不灵活,你知道什么更加灵活的方式吗? + + +我们的专栏更新到第24讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/25软件定义网络:共享基础设施的小区物业管理办法.md b/专栏/趣谈网络协议/25软件定义网络:共享基础设施的小区物业管理办法.md new file mode 100644 index 0000000..ad734ee --- /dev/null +++ b/专栏/趣谈网络协议/25软件定义网络:共享基础设施的小区物业管理办法.md @@ -0,0 +1,269 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 25 软件定义网络:共享基础设施的小区物业管理办法 + 上一节我们说到,使用原生的VLAN和Linux网桥的方式来进行云平台的管理,但是这样在灵活性、隔离性方面都显得不足,而且整个网络缺少统一的视图、统一的管理。 + +可以这样比喻,云计算就像大家一起住公寓,要共享小区里面的基础设施,其中网络就相当于小区里面的电梯、楼道、路、大门等,大家都走,往往会常出现问题,尤其在上班高峰期,出门的人太多,对小区的物业管理就带来了挑战。 + +物业可以派自己的物业管理人员,到每个单元的楼梯那里,将电梯的上下行速度调快一点,可以派人将隔离健身区、景色区的栅栏门暂时打开,让大家可以横穿小区,直接上地铁,还可以派人将多个小区出入口,改成出口多、入口少等等。等过了十点半,上班高峰过去,再派人都改回来。 + +软件定义网络(SDN) + +这种模式就像传统的网络设备和普通的Linux网桥的模式,配置整个云平台的网络通路,你需要登录到这台机器上配置这个,再登录到另外一个设备配置那个,才能成功。 + +如果物业管理人员有一套智能的控制系统,在物业监控室里就能看到小区里每个单元、每个电梯的人流情况,然后在监控室里面,只要通过远程控制的方式,拨弄一个手柄,电梯的速度就调整了,栅栏门就打开了,某个入口就改出口了。 + +这就是软件定义网络(SDN)。它主要有以下三个特点。 + + + + +控制与转发分离:转发平面就是一个个虚拟或者物理的网络设备,就像小区里面的一条条路。控制平面就是统一的控制中心,就像小区物业的监控室。它们原来是一起的,物业管理员要从监控室出来,到路上去管理设备,现在是分离的,路就是走人的,控制都在监控室。 +控制平面与转发平面之间的开放接口:控制器向上提供接口,被应用层调用,就像总控室提供按钮,让物业管理员使用。控制器向下调用接口,来控制网络设备,就像总控室会远程控制电梯的速度。这里经常使用两个名词,前面这个接口称为北向接口,后面这个接口称为南向接口,上北下南嘛。 +逻辑上的集中控制:逻辑上集中的控制平面可以控制多个转发面设备,也就是控制整个物理网络,因而可以获得全局的网络状态视图,并根据该全局网络状态视图实现对网络的优化控制,就像物业管理员在监控室能够看到整个小区的情况,并根据情况优化出入方案。 + + +OpenFlow和OpenvSwitch + +SDN有很多种实现方式,我们来看一种开源的实现方式。 + +OpenFlow是SDN控制器和网络设备之间互通的南向接口协议,OpenvSwitch用于创建软件的虚拟交换机。OpenvSwitch是支持OpenFlow协议的,当然也有一些硬件交换机也支持OpenFlow协议。它们都可以被统一的SDN控制器管理,从而实现物理机和虚拟机的网络连通。 + + + +SDN控制器是如何通过OpenFlow协议控制网络的呢? + + + +在OpenvSwitch里面,有一个流表规则,任何通过这个交换机的包,都会经过这些规则进行处理,从而接收、转发、放弃。 + +那流表长啥样呢?其实就是一个个表格,每个表格好多行,每行都是一条规则。每条规则都有优先级,先看高优先级的规则,再看低优先级的规则。 + + + +对于每一条规则,要看是否满足匹配条件。这些条件包括,从哪个端口进来的,网络包头里面有什么等等。满足了条件的网络包,就要执行一个动作,对这个网络包进行处理。可以修改包头里的内容,可以跳到任何一个表格,可以转发到某个网口出去,也可以丢弃。 + +通过这些表格,可以对收到的网络包随意处理。 + + + +具体都能做什么处理呢?通过上面的表格可以看出,简直是想怎么处理怎么处理,可以覆盖TCP/IP协议栈的四层。 + +对于物理层: + + +匹配规则包括从哪个口进来; +执行动作包括从哪个口出去。 + + +对于MAC层: + + +匹配规则包括:源MAC地址是多少?(dl_src),目标MAC是多少?(dl_dst),所属vlan是多少?(dl_vlan); +执行动作包括:修改源MAC(mod_dl_src),修改目标MAC(mod_dl_dst),修改VLAN(mod_vlan_vid),删除VLAN(strip_vlan),MAC地址学习(learn)。 + + +对于网络层: + + +匹配规则包括:源IP地址是多少?(nw_src),目标IP是多少?(nw_dst)。 +执行动作包括:修改源IP地址(mod_nw_src),修改目标IP地址(mod_nw_dst)。 + + +对于传输层: + + +匹配规则包括:源端口是多少?(tp_src),目标端口是多少?(tp_dst)。 +执行动作包括:修改源端口(mod_tp_src),修改目标端口(mod_tp_dst)。 + + +总而言之,对于OpenvSwitch来讲,网络包到了我手里,就是一个Buffer,我想怎么改怎么改,想发到哪个端口就发送到哪个端口。 + +OpenvSwitch有本地的命令行可以进行配置,能够实验咱们前面讲过的一些功能。我们可以通过OpenvSwitch的命令创建一个虚拟交换机。然后可以将多个虚拟端口port添加到这个虚拟交换机上。比如说下面这个add-br命令,就是创建虚拟交换机的。 + +ovs-vsctl add-br br0 + + +实验一:用OpenvSwitch实现VLAN的功能 + +下面我们实验一下通过OpenvSwitch实现VLAN的功能,在OpenvSwitch中端口port分两种,分别叫做access port和trunk port。 + +第一类是access port: + + +这个端口可以配置一个tag,其实就是一个VLAN ID,从这个端口进来的包都会被打上这个tag; +如果网络包本身带有某个VLAN ID并且等于这个tag,则这个包就会从这个port发出去; +从access port发出的包就会把VLAN ID去掉。 + + +第二类是trunk port: + + +这个port是不配置任何tag的,配置叫trunks的参数; +如果trunks为空,则所有的VLAN都trunk,也就意味着对于所有的VLAN的包,无论本身带什么VLAN ID,我还是让他携带着这个VLAN ID,如果没有设置VLAN,就属于VLAN 0,全部允许通过; +如果trunks不为空,则仅仅允许带着这些VLAN ID的包通过。 + + +我们通过以下命令创建如下的环境: + +ovs-vsctl add-port br0 first_br +ovs-vsctl add-port br0 second_br +ovs-vsctl add-port br0 third_br +ovs-vsctl set Port vnet0 tag=101 +ovs-vsctl set Port vnet1 tag=102 +ovs-vsctl set Port vnet2 tag=103 +ovs-vsctl set Port first_br tag=103 +ovs-vsctl clear Port second_br tag +ovs-vsctl set Port third_br trunks=101,102 + + +另外要配置禁止MAC地址学习。 + +ovs-vsctl set bridge br0 flood-vlans=101,102,103 + + +这样就形成了如下的拓扑图,有三个虚拟机,有三个网卡,都连到一个叫br0的网桥上,并且他们被都打了不同的VLAN tag。 + + + +创建好了环境以后,我们来做这个实验。 + +首先,我们从192.168.100.102来ping 192.168.100.103,然后用tcpdump进行抓包。由于192.168.100.102和first_br都配置了tag103,也就是说他们都属于同一个VLAN 103的,因而这个first_if是能够收到包的。但是根据access port的规则,从first_br出来的包头是没有带VLAN ID的。 + +由于second_br是trunk port,所有的VLAN都会放行,因而second_if也是能收到包的,并且根据trunk port的规则,出来的包的包头里面是带有VLAN ID的。 + +由于third_br仅仅配置了允许VLAN 101和102通过,不允许103通过,因而third_if他是收不到包的。 + +然后我们再尝试,从192.168.100.100来ping 192.168.100.105。 因为192.168.100.100是配置了VLAN 101的,因为second_br是配置了trunk的,是全部放行的,所以说second_if是可以收到包的。那third_br是配置了可以放行VLAN 101和102,所以说third_if是可以收到包的。当然ping不通,因为从third_br出来的包是带VLAN的,而third_if他本身不属于某个VLAN,所以说他ping不通,但是能够收到包 + +这里补充说明一下,收到包和ping不同不矛盾,要想ping的通,需要发送ICMP包,并且收到回复,而仅仅收到包,则不需要回复。这里正是这种情况,third_if收到了这个包,但是发现VLAN ID匹配不上,就会把包丢了,不回复,也就Ping不通了。 + +first_br是属于VLAN 103的,因而first_if是收不到包的。second_if是能够收到包的,而且可以看到包头里面是带VLAN 101的。third_if也是能收到包的,而且包头里面也是带VLAN I101的。 + +最后我们再尝试,从192.168.100.101来ping 192.168.100.104,因为192.168.100.101是属于VLAN 102的, 因而second_if和third_if都因为配置了trunk,是都可以收到包的。first_br是属于VLAN 103的,他不属于VLAN 102,所以first_if是收不到包的。second_br能够收到包,并且包头里面是带VLAN ID 102的。third_if也能收到包,并且包头里面也是带VLAN ID 102的。 + +通过这个例子,我们可以看到,通过OpenvSwitch,不用买一个支持VLAN的交换机,你也能学习VLAN的工作模式了。 + +实验二:用OpenvSwitch模拟网卡绑定,连接交换机 + +接下来,我们来做另一个实验。在前面,我们还说过,为了高可用,可以使用网卡绑定,连接到交换机,OpenvSwitch也可以模拟这一点。 + +在OpenvSwitch里面,有个bond_mode,可以设置为以下三个值: + + +active-backup:一个连接是active,其他的是backup,只有当active失效的时候,backup才顶上; +balance-slb:流量按照源MAC和output VLAN进行负载均衡; +balance-tcp:必须在支持LACP协议的情况下才可以,可根据L2、L3、L4进行负载均衡(L2、L3、L4指的是网络协议2、3、4层) + + +我们搭建一个测试环境。这个测试环境是两台虚拟机连接到br0上,另外两台虚拟机连接到br1上,br0和br1之间通过两条通路进行bond(绑定)。形成如下的拓扑图。 + + + +我们使用下面的命令,建立bond连接。 + +ovs-vsctl add-bond br0 bond0 first_br second_br +ovs-vsctl add-bond br1 bond1 first_if second_if +ovs-vsctl set Port bond0 lacp=active +ovs-vsctl set Port bond1 lacp=active + + +默认情况下bond_mode是active-backup模式,一开始active的是左面这条路,也即first_br和first_if这条路。 + +这个时候如果我们从192.168.100.100 来ping 192.168.100.102,以及从192.168.100.101 来ping 192.168.100.103的时候,我从tcpdump可以看到所有的包都是从first_if这条路通过。 + +接下来,如果我们把first_if这个网卡设成down的模式,则包的走向就会改变,你会发现second_if这条路开始有流量了,对于192.168.100.100和192.168.100.101从应用层来讲,感觉似乎没有收到影响。 + +如果我们通过以下命令,把bond_mode改为balance-slb。然后我们同时在192.168.100.100 来ping 192.168.100.102,同时也在192.168.100.101 来ping 192.168.100.103,我们通过tcpdump会发现,包已经被分流了。 + +ovs-vsctl set Port bond0 bond_mode=balance-slb +ovs-vsctl set Port bond1 bond_mode=balance-slb + + +通过这个例子,我们可以看到,通过OpenvSwitch,你不用买两台支持bond的交换机,也能看到bond的效果。 + +那OpenvSwitch是怎么做到这些的呢?我们来看OpenvSwitch的架构图。 + + + +OpenvSwitch包含很多的模块,在用户态有两个重要的进程,也有两个重要的命令行工具。 + + +第一个进程是OVSDB进程。ovs-vsctl命令行会和这个进程通信,去创建虚拟交换机,创建端口,将端口添加到虚拟交换机上,OVSDB会将这些拓扑信息保存在一个本地的文件中。 +第二个进程是vswitchd进程。ovs-ofctl命令行会和这个进程通信,去下发流表规则,规则里面会规定如何对网络包进行处理,vswitchd会将流表放在用户态Flow Table中。 + + +在内核态,OpenvSwitch有内核模块OpenvSwitch.ko,对应图中的Datapath部分。他会在网卡上注册一个函数,每当有网络包到达网卡的时候,这个函数就会被调用。 + +在内核的这个函数里面,会拿到网络包,将各个层次的重要信息拿出来,例如: + + +在物理层,会拿到in_port,即包是从哪个网口进来的。; +在MAC层,会拿到源和目的MAC地址; +在IP层,会拿到源和目的IP地址; +在传输层,会拿到源和目的端口号。 + + +在内核中,还有一个内核态Flow Table。接下来内核态模块在这个内核态的流表中匹配规则,如果匹配上了,就执行相应的操作,比如修改包,或者转发,或者放弃。如果内核没有匹配上,这个时候就需要进入用户态,用户态和内核态之间通过Linux的一个机制叫Netlink,来进行相互通信。 + +内核通过upcall,告知用户态进程vswitchd,在用户态的Flow Table里面去匹配规则,这里面的规则是全量的流表规则,而内核态的Flow Table只是为了做快速处理,保留了部分规则,内核里面的规则过一段时间就会过期。 + +当在用户态匹配到了流表规则之后,就在用户态执行操作,同时将这个匹配成功的流表通过reinject下发到内核,从而接下来的包都能在内核找到这个规则,来进行转发。 + +这里调用openflow协议的,是本地的命令行工具。当然你也可以是远程的SDN控制器来进行控制,一个重要的SDN控制器是OpenDaylight。 + +下面这个图就是OpenDaylight中看到的拓扑图。是不是有种物业管理员在监控室里的感觉? + + + +我们可以通过在OpenDaylight里,将两个交换机之间配置通,也可以配置不通,还可以配置一个虚拟IP地址为VIP,在不同的机器之间实现负载均衡等等,所有的策略都可以灵活配置。 + +如何在云计算中使用OpenvSwitch? + +OpenvSwitch这么牛,如何用在云计算中呢? + + + +我们还是讨论VLAN的场景。 + +在没有OpenvSwitch的时候,如果一个新的用户要使用一个新的VLAN,就需要创建一个属于新的VLAN的虚拟网卡,并且为这个租户创建一个单独的虚拟网桥,这样用户越来越多的时候,虚拟网卡和虚拟网桥会越来越多,管理就越来越复杂。 + +另一个问题是虚拟机的VLAN和物理环境的VLAN是透传的,也即从一开始规划的时候,这两个就需要匹配起来,将物理环境和虚拟环境强绑定,这样本来就不灵活。 + +而引入了OpenvSwitch,状态就得到了改观。 + +首先,由于OpenvSwitch本身就是支持VLAN的,这样所有的虚拟机都可以放在一个网桥br0上,通过不同的用户配置不同的tag,就能够实现隔离。例如上面的图左面的部分,用户A的虚拟机都在br0上,用户B的虚拟机都在br1上,有了OpenvSwitch,就可以都放在br0上,只是设置了不同的tag就可以了。 + +另外,还可以创建一个虚拟交换机br1,将物理网络和虚拟网络进行隔离。物理网络有物理网络的VLAN规划,虚拟机在一台物理机上,所有的VLAN都可以从1开始。由于一台物理机上的虚拟机肯定不会超过4096个,所以VLAN在一台物理机上如果从1开始,肯定够用了。例如在图中右面部分的上面的那台物理机里面,用户A被分配的tag是1,用户B被分配的tag是2,而在下面的物理机里面,用户A被分配的tag是7,用户B被分配的tag是6。 + +如果物理机之间的通信和隔离还是通过VLAN的话,需要将虚拟机的VLAN和物理环境的VLAN对应起来,但为了灵活性,不一定一致,这样可以实现分别管理物理机的网络和虚拟机的网络。好在OpenvSwitch可以对包的内容进行修改。例如通过匹配dl_vlan,然后执行mod_vlan_vid来改变进进出出物理机的网络包。 + +尽管租户多了,物理环境的VLAN还是不够用,但是有了OpenvSwitch的映射,将物理和虚拟解耦,从而可以让物理环境使用其他技术,而不影响虚拟机环境,这个我们后面再讲。 + +小结 + +好了,这一节就到这里了,我们来总结一下: + + +用SDN控制整个云里面的网络,就像小区保安从总控室管理整个物业是一样的,将控制面和数据面进行了分离; +一种开源的虚拟交换机的实现OpenvSwitch,它能对经过自己的包做任意修改,从而使得云对网络的控制十分灵活; +将OpenvSwitch引入了云之后,可以使得配置简单而灵活,并且可以解耦物理网络和虚拟网络。 + + +最后,给你留两个思考题: + + +在这一节中,提到了通过VIP可以通过流表在不同的机器之间实现复杂均衡,你知道怎样才能做到吗? +虽然OpenvSwitch可以解耦物理网络和虚拟网络,但是在物理网络里面使用VLAN,数目还是不够,你知道该怎么办吗? + + +我们的专栏更新到第25讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/26云中的网络安全:虽然不是土豪,也需要基本安全和保障.md b/专栏/趣谈网络协议/26云中的网络安全:虽然不是土豪,也需要基本安全和保障.md new file mode 100644 index 0000000..46b5408 --- /dev/null +++ b/专栏/趣谈网络协议/26云中的网络安全:虽然不是土豪,也需要基本安全和保障.md @@ -0,0 +1,200 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 26 云中的网络安全:虽然不是土豪,也需要基本安全和保障 + 在今天的内容开始之前,我先卖个关子。文章结尾,我会放一个超级彩蛋,所以,今天的内容你一定要看到最后哦! + +上一节我们看到,做一个小区物业维护一个大家共享的环境,还是挺不容易的。如果都是自觉遵守规则的住户那还好,如果遇上不自觉的住户就会很麻烦。 + +就像公有云的环境,其实没有你想的那么纯净,各怀鬼胎的黑客到处都是。扫描你的端口呀,探测一下你启动的什么应用啊,看一看是否有各种漏洞啊。这就像小偷潜入小区后,这儿看看,那儿瞧瞧,窗户有没有关严了啊,窗帘有没有拉上啊,主人睡了没,是不是时机潜入室内啊,等等。 + +假如你创建了一台虚拟机,里面明明跑了一个电商应用,这是你非常重要的一个应用,你会把它进行安全加固。这台虚拟机的操作系统里,不小心安装了另外一个后台应用,监听着一个端口,而你的警觉性没有这么高。 + +虚拟机的这个端口是对着公网开放的,碰巧这个后台应用本身是有漏洞的,黑客就可以扫描到这个端口,然后通过这个后台应用的端口侵入你的机器,将你加固好的电商网站黑掉。这就像你买了一个五星级的防盗门,卡车都撞不开,但是厕所窗户的门把手是坏的,小偷从厕所里面就进来了。 + +所以对于公有云上的虚拟机,我的建议是仅仅开放需要的端口,而将其他的端口一概关闭。这个时候,你只要通过安全措施守护好这个唯一的入口就可以了。采用的方式常常是用ACL(Access Control List,访问控制列表)来控制IP和端口。 + +设置好了这些规则,只有指定的IP段能够访问指定的开放接口,就算有个有漏洞的后台进程在那里,也会被屏蔽,黑客进不来。在云平台上,这些规则的集合常称为安全组。那安全组怎么实现呢? + +我们来复习一下,当一个网络包进入一台机器的时候,都会做什么事情。 + +首先拿下MAC头看看,是不是我的。如果是,则拿下IP头来。得到目标IP之后呢,就开始进行路由判断。在路由判断之前,这个节点我们称为PREROUTING。如果发现IP是我的,包就应该是我的,就发给上面的传输层,这个节点叫作INPUT。如果发现IP不是我的,就需要转发出去,这个节点称为FORWARD。如果是我的,上层处理完毕后,一般会返回一个处理结果,这个处理结果会发出去,这个节点称为OUTPUT,无论是FORWARD还是OUTPUT,都是路由判断之后发生的,最后一个节点是POSTROUTING。 + +整个过程如图所示。 + + + +整个包的处理过程还是原来的过程,只不过为什么要格外关注这五个节点呢? + +是因为在Linux内核中,有一个框架叫Netfilter。它可以在这些节点插入hook函数。这些函数可以截获数据包,对数据包进行干预。例如做一定的修改,然后决策是否接着交给TCP/IP协议栈处理;或者可以交回给协议栈,那就是ACCEPT;或者过滤掉,不再传输,就是DROP;还有就是QUEUE,发送给某个用户态进程处理。 + +这个比较难理解,经常用在内部负载均衡,就是过来的数据一会儿传给目标地址1,一会儿传给目标地址2,而且目标地址的个数和权重都可能变。协议栈往往处理不了这么复杂的逻辑,需要写一个函数接管这个数据,实现自己的逻辑。 + +有了这个Netfilter框架就太好了,你可以在IP转发的过程中,随时干预这个过程,只要你能实现这些hook函数。 + +一个著名的实现,就是内核模块ip_tables。它在这五个节点上埋下函数,从而可以根据规则进行包的处理。按功能可分为四大类:连接跟踪(conntrack)、数据包的过滤(filter)、网络地址转换(nat)和数据包的修改(mangle)。其中连接跟踪是基础功能,被其他功能所依赖。其他三个可以实现包的过滤、修改和网络地址转换。 + +在用户态,还有一个你肯定知道的客户端程序iptables,用命令行来干预内核的规则。内核的功能对应iptables的命令行来讲,就是表和链的概念。 + + + +iptables的表分为四种:raw–>mangle–>nat–>filter。这四个优先级依次降低,raw不常用,所以主要功能都在其他三种表里实现。每个表可以设置多个链。 + +filter表处理过滤功能,主要包含三个链: + + +INPUT链:过滤所有目标地址是本机的数据包; + +FORWARD链:过滤所有路过本机的数据包; + +OUTPUT链:过滤所有由本机产生的数据包。 + + +nat表主要是处理网络地址转换,可以进行Snat(改变数据包的源地址)、Dnat(改变数据包的目标地址),包含三个链: + + +PREROUTING链:可以在数据包到达防火墙时改变目标地址; + +OUTPUT链:可以改变本地产生的数据包的目标地址; + +POSTROUTING链:在数据包离开防火墙时改变数据包的源地址。 + + +mangle表主要是修改数据包,包含: + + +PREROUTING链; + +INPUT链; + +FORWARD链; + +OUTPUT链; + +POSTROUTING链。 + + +将iptables的表和链加入到上面的过程图中,就形成了下面的图和过程。 + + + + +数据包进入的时候,先进mangle表的PREROUTING链。在这里可以根据需要,改变数据包头内容之后,进入nat表的PREROUTING链,在这里可以根据需要做Dnat,也就是目标地址转换。 + +进入路由判断,要判断是进入本地的还是转发的。 + +如果是进入本地的,就进入INPUT链,之后按条件过滤限制进入。 + +之后进入本机,再进入OUTPUT链,按条件过滤限制出去,离开本地。 + +如果是转发就进入FORWARD链,根据条件过滤限制转发。 + +之后进入POSTROUTING链,这里可以做Snat,离开网络接口。 + + +有了iptables命令,我们就可以在云中实现一定的安全策略。例如我们可以处理前面的偷窥事件。首先我们将所有的门都关闭。 + +iptables -t filter -A INPUT -s 0.0.0.0/0.0.0.0 -d X.X.X.X -j DROP + + +-s表示源IP地址段,-d表示目标地址段,DROP表示丢弃,也即无论从哪里来的,要想访问我这台机器,全部拒绝,谁也黑不进来。 + +但是你发现坏了,ssh也进不来了,都不能远程运维了,可以打开一下。 + +iptables -I INPUT -s 0.0.0.0/0.0.0.0 -d X.X.X.X -p tcp --dport 22 -j ACCEPT + + +如果这台机器是提供的是web服务,80端口也应该打开,当然一旦打开,这个80端口就需要很好的防护,但是从规则角度还是要打开。 + +iptables -A INPUT -s 0.0.0.0/0.0.0.0 -d X.X.X.X -p tcp --dport 80 -j ACCEPT + + +这样就搞定了,其他的账户都封死,就一个防盗门可以进出,只要防盗门是五星级的,就比较安全了。 + +这些规则都可以在虚拟机里,自己安装iptables自己配置。但是如果虚拟机数目非常多,都要配置,对于用户来讲就太麻烦了,能不能让云平台把这部分工作做掉呢? + +当然可以了。在云平台上,一般允许一个或者多个虚拟机属于某个安全组,而属于不同安全组的虚拟机之间的访问以及外网访问虚拟机,都需要通过安全组进行过滤。 + + + +例如图中,我们会创建一系列的网站,都是前端在Tomcat里面,对外开放8080端口。数据库使用MySQL,开放3306端口。 + +为了方便运维,我们创建两个安全组,将Tomcat所在的虚拟机放在安全组A里面。在安全组A里面,允许任意IP地址0.0.0.0/0访问8080端口,但是对于ssh的22端口,仅仅允许管理员网段203.0.113.0/24访问。 + +我们将MySQL所在的虚拟机放在安全组B里面。在安全组B里面,仅仅允许来自安全组A的机器访问3306端口,但是对于ssh的22端口,同样允许管理员网段203.0.113.0/24访问。 + +这些安全组规则都可以自动下发到每个在安全组里面的虚拟机上,从而控制一大批虚拟机的安全策略。这种批量下发是怎么做到的呢?你还记得这幅图吗? + + + +两个VM都通过tap网卡连接到一个网桥上,但是网桥是二层的,两个VM之间是可以随意互通的,因而需要有一个地方统一配置这些iptables规则。 + +可以多加一个网桥,在这个网桥上配置iptables规则,将在用户在界面上配置的规则,放到这个网桥上。然后在每台机器上跑一个Agent,将用户配置的安全组变成iptables规则,配置在这个网桥上。 + +安全问题解决了,iptables真强大!别忙,iptables除了filter,还有nat呢,这个功能也非常重要。 + +前面的章节我们说过,在设计云平台的时候,我们想让虚拟机之间的网络和物理网络进行隔离,但是虚拟机毕竟还是要通过物理网和外界通信的,因而需要在出物理网的时候,做一次网络地址转换,也即nat,这个就可以用iptables来做。 + +我们学过,IP头里面包含源IP地址和目标IP地址,这两种IP地址都可以转换成其他地址。转换源IP地址的,我们称为Snat;转换目标IP地址的,我们称为Dnat。 + +你有没有思考过这个问题,TCP的访问都是一去一回的,而你在你家里连接WiFi的IP地址是一个私网IP,192.168.1.x。当你通过你们家的路由器访问163网站之后,网站的返回结果如何能够到达你的笔记本电脑呢?肯定不能通过192.168.1.x,这是个私网IP,不具有公网上的定位能力,而且用这个网段的人很多,茫茫人海,怎么能够找到你呢? + +所以当你从你家里访问163网站的时候,在你路由器的出口,会做Snat的,运营商的出口也可能做Snat,将你的私网IP地址,最终转换为公网IP地址,然后163网站就可以通过这个公网IP地址返回结果,然后再nat回来,直到到达你的笔记本电脑。 + +云平台里面的虚拟机也是这样子的,它只有私网IP地址,到达外网网口要做一次Snat,转换成为机房网IP,然后出数据中心的时候,再转换为公网IP。 + + + +这里有一个问题是,在外网网口上做Snat的时候,是全部转换成一个机房网IP呢,还是每个虚拟机都对应一个机房网IP,最终对应一个公网IP呢?前面也说过了,公网IP非常贵,虚拟机也很多,当然不能每个都有单独的机房网和公网IP了,因此这种Snat是一种特殊的Snat,MASQUERADE(地址伪装)。 + +这种方式下,所有的虚拟机共享一个机房网和公网的IP地址,所有从外网网口出去的,都转换成为这个IP地址。那又一个问题来了,都变成一个公网IP了,当163网站返回结果的时候,给谁呢,再nat成为哪个私网的IP呢? + +这就是Netfilter的连接跟踪(conntrack)功能了。对于TCP协议来讲,肯定是上来先建立一个连接,可以用“源/目的IP+源/目的端口”唯一标识一条连接,这个连接会放在conntrack表里面。当时是这台机器去请求163网站的,虽然源地址已经Snat成公网IP地址了,但是conntrack表里面还是有这个连接的记录的。当163网站返回数据的时候,会找到记录,从而找到正确的私网IP地址。 + +这是虚拟机做客户端的情况,如果虚拟机做服务器呢?也就是说,如果虚拟机里面部署的就是163网站呢? + +这个时候就需要给这个网站配置固定的物理网的IP地址和公网IP地址了。这时候就需要详细配置Snat规则和Dnat规则了。 + +当外部访问进来的时候,外网网口会通过Dnat规则将公网IP地址转换为私网IP地址,到达虚拟机,虚拟机里面是163网站,返回结果,外网网口会通过Snat规则,将私网IP地址转换为那个分配给它的固定的公网IP地址。 + +类似的规则如下: + + +源地址转换(Snat):iptables -t nat -A -s 私网IP -j Snat –to-source 外网IP + +目的地址转换(Dnat):iptables -t nat -A -PREROUTING -d 外网IP -j Dnat –to-destination 私网IP + + +到此为止iptables解决了非法偷窥隐私的问题。 + +小结 + +好了,这一节就讲到这里了,我们来总结一下。 + + +云中的安全策略的常用方式是,使用iptables的规则,请记住它的五个阶段,PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。 + +iptables分为四种表,raw、mangle、nat、filter。其中安全策略主要在filter表中实现,而虚拟网络和物理网络地址的转换主要在nat表中实现。 + + +最后,给你留两个思考题。 + + +这一节中重点讲了iptables的filter和nat功能,iptables还可以通过QUEUE实现负载均衡,你知道怎么做吗? + +这一节仅仅讲述了云中偷窥的问题,如果是一个合法的用户,但是不自觉抢占网络通道,应该采取什么策略呢? + + +我们的专栏更新到第26讲,不知你掌握得如何?是不是有很多问题想要跟我面对面探讨呢?这里就有一个机会。 + +今天晚上8:30,我会在极客时间APP里做一个直播,主题是“技术人如何在技术浪潮中线性成长?”,我会把我们讲过的网络协议作为案例,在直播中展开讲解,也会分享我从业多年来的心得体会。你可以直接在这里留言提问,也可以准备好问题在直播的时候和我交流。 + +欢迎你来看直播!我们晚上见! + + + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/27云中的网络QoS:邻居疯狂下电影,我该怎么办?.md b/专栏/趣谈网络协议/27云中的网络QoS:邻居疯狂下电影,我该怎么办?.md new file mode 100644 index 0000000..5cc46e5 --- /dev/null +++ b/专栏/趣谈网络协议/27云中的网络QoS:邻居疯狂下电影,我该怎么办?.md @@ -0,0 +1,160 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 27 云中的网络QoS:邻居疯狂下电影,我该怎么办? + 在小区里面,是不是经常有住户不自觉就霸占公共通道,如果你找他理论,他的话就像一个相声《楼道曲》说的一样:“公用公用,你用我用,大家都用,我为什么不能用?”。 + +除此之外,你租房子的时候,有没有碰到这样的情况:本来合租共享WiFi,一个人狂下小电影,从而你网都上不去,是不是很懊恼? + +在云平台上,也有这种现象,好在有一种流量控制的技术,可以实现QoS(Quality of Service),从而保障大多数用户的服务质量。 + +对于控制一台机器的网络的QoS,分两个方向,一个是入方向,一个是出方向。 + + + +其实我们能控制的只有出方向,通过Shaping,将出的流量控制成自己想要的模样。而进入的方向是无法控制的,只能通过Policy将包丢弃。 + +控制网络的QoS有哪些方式? + +在Linux下,可以通过TC控制网络的QoS,主要就是通过队列的方式。 + +无类别排队规则 + +第一大类称为无类别排队规则(Classless Queuing Disciplines)。还记得我们讲[ip addr]的时候讲过的pfifo_fast,这是一种不把网络包分类的技术。 + + + +pfifo_fast分为三个先入先出的队列,称为三个Band。根据网络包里面TOS,看这个包到底应该进入哪个队列。TOS总共四位,每一位表示的意思不同,总共十六种类型。 + +通过命令行tc qdisc show dev eth0,可以输出结果priomap,也是十六个数字。在0到2之间,和TOS的十六种类型对应起来,表示不同的TOS对应的不同的队列。其中Band 0优先级最高,发送完毕后才轮到Band 1发送,最后才是Band 2。 + +另外一种无类别队列规则叫作随机公平队列(Stochastic Fair Queuing)。 + + + +会建立很多的FIFO的队列,TCP Session会计算hash值,通过hash值分配到某个队列。在队列的另一端,网络包会通过轮询策略从各个队列中取出发送。这样不会有一个Session占据所有的流量。 + +当然如果两个Session的hash是一样的,会共享一个队列,也有可能互相影响。hash函数会经常改变,从而session不会总是相互影响。 + +还有一种无类别队列规则称为令牌桶规则(TBF,Token Bucket Filte)。 + + + +所有的网络包排成队列进行发送,但不是到了队头就能发送,而是需要拿到令牌才能发送。 + +令牌根据设定的速度生成,所以即便队列很长,也是按照一定的速度进行发送的。 + +当没有包在队列中的时候,令牌还是以既定的速度生成,但是不是无限累积的,而是放满了桶为止。设置桶的大小为了避免下面的情况:当长时间没有网络包发送的时候,积累了大量的令牌,突然来了大量的网络包,每个都能得到令牌,造成瞬间流量大增。 + +基于类别的队列规则 + +另外一大类是基于类别的队列规则(Classful Queuing Disciplines),其中典型的为分层令牌桶规则(HTB, Hierarchical Token Bucket)。 + +HTB往往是一棵树,接下来我举个具体的例子,通过TC如何构建一棵HTB树来带你理解。 + + + +使用TC可以为某个网卡eth0创建一个HTB的队列规则,需要付给它一个句柄为(1:)。 + +这是整棵树的根节点,接下来会有分支。例如图中有三个分支,句柄分别为(:10)、(:11)、(:12)。最后的参数default 12,表示默认发送给1:12,也即发送给第三个分支。 + +tc qdisc add dev eth0 root handle 1: htb default 12 + + +对于这个网卡,需要规定发送的速度。一般有两个速度可以配置,一个是rate,表示一般情况下的速度;一个是ceil,表示最高情况下的速度。对于根节点来讲,这两个速度是一样的,于是创建一个root class,速度为(rate=100kbps,ceil=100kbps)。 + +tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps + + +接下来要创建分支,也即创建几个子class。每个子class统一有两个速度。三个分支分别为(rate=30kbps,ceil=100kbps)、(rate=10kbps,ceil=100kbps)、(rate=60kbps,ceil=100kbps)。 + +tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps +tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps +tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps + + +你会发现三个rate加起来,是整个网卡允许的最大速度。 + +HTB有个很好的特性,同一个root class下的子类可以相互借流量,如果不直接在队列规则下面创建一个root class,而是直接创建三个class,它们之间是不能相互借流量的。借流量的策略,可以使得当前不使用这个分支的流量的时候,可以借给另一个分支,从而不浪费带宽,使带宽发挥最大的作用。 + +最后,创建叶子队列规则,分别为fifo和sfq。 + +tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 5 +tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 5 +tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10 + + +基于这个队列规则,我们还可以通过TC设定发送规则:从1.2.3.4来的,发送给port 80的包,从第一个分支1:10走;其他从1.2.3.4发送来的包从第二个分支1:11走;其他的走默认分支。 + +tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10 +tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 flowid 1:11 + + +如何控制QoS? + +我们讲过,使用OpenvSwitch将云中的网卡连通在一起,那如何控制QoS呢? + +就像我们上面说的一样,OpenvSwitch支持两种: + + +对于进入的流量,可以设置策略Ingress policy; + +ovs-vsctl set Interface tap0 ingress_policing_rate=100000 +ovs-vsctl set Interface tap0 ingress_policing_burst=10000 + +对于发出的流量,可以设置QoS规则Egress shaping,支持HTB。 + + +我们构建一个拓扑图,来看看OpenvSwitch的QoS是如何工作的。 + + + +首先,在port上可以创建QoS规则,一个QoS规则可以有多个队列Queue。 + + + +ovs-vsctl set port first_br qos=@newqos -- --id=@newqos create qos type=linux-htb other-config:max-rate=10000000 queues=0=@q0,1=@q1,2=@q2 -- --id=@q0 create queue other-config:min-rate=3000000 other-config:max-rate=10000000 -- --id=@q1 create queue other-config:min-rate=1000000 other-config:max-rate=10000000 -- --id=@q2 create queue other-config:min-rate=6000000 other-config:max-rate=10000000 + + +上面的命令创建了一个QoS规则,对应三个Queue。min-rate就是上面的rate,max-rate就是上面的ceil。通过交换机的网络包,要通过流表规则,匹配后进入不同的队列。然后我们就可以添加流表规则Flow(first_br是br0上的port 5)。 + +ovs-ofctl add-flow br0 "in_port=6 nw_src=192.168.100.100 actions=enqueue:5:0" +ovs-ofctl add-flow br0 "in_port=7 nw_src=192.168.100.101 actions=enqueue:5:1" +ovs-ofctl add-flow br0 "in_port=8 nw_src=192.168.100.102 actions=enqueue:5:2" + + +接下来,我们单独测试从192.168.100.100,192.168.100.101,192.168.100.102到192.168.100.103的带宽的时候,每个都是能够打满带宽的。 + +如果三个一起测试,一起狂发网络包,会发现是按照3:1:6的比例进行的,正是根据配置的队列的带宽比例分配的。 + +如果192.168.100.100和192.168.100.101一起测试,发现带宽占用比例为3:1,但是占满了总的流量,也即没有发包的192.168.100.102有60%的带宽被借用了。 + +如果192.168.100.100和192.168.100.102一起测试,发现带宽占用比例为1:2。如果192.168.100.101和192.168.100.102一起测试,发现带宽占用比例为1:6。 + +小结 + +好了,这一节就讲到这里了,我们来总结一下。 + + +云中的流量控制主要通过队列进行的,队列分为两大类:无类别队列规则和基于类别的队列规则。 + +在云中网络Openvswitch中,主要使用的是分层令牌桶规则(HTB),将总的带宽在一棵树上按照配置的比例进行分配,并且在一个分支不用的时候,可以借给另外的分支,从而增强带宽利用率。 + + +最后,给你留两个思考题。 + + +这一节中提到,入口流量其实没有办法控制,出口流量是可以很好控制的,你能想出一个控制云中的虚拟机的入口流量的方式吗? + +安全性和流量控制大概解决了,但是不同用户在物理网络的隔离还是没有解决,你知道怎么解决吗? + + +我们的专栏更新到第27讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/28云中网络的隔离GRE、VXLAN:虽然住一个小区,也要保护隐私.md b/专栏/趣谈网络协议/28云中网络的隔离GRE、VXLAN:虽然住一个小区,也要保护隐私.md new file mode 100644 index 0000000..9dd2b6d --- /dev/null +++ b/专栏/趣谈网络协议/28云中网络的隔离GRE、VXLAN:虽然住一个小区,也要保护隐私.md @@ -0,0 +1,252 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 28 云中网络的隔离GRE、VXLAN:虽然住一个小区,也要保护隐私 + 对于云平台中的隔离问题,前面咱们用的策略一直都是VLAN,但是我们也说过这种策略的问题,VLAN只有12位,共4096个。当时设计的时候,看起来是够了,但是现在绝对不够用,怎么办呢? + +一种方式是修改这个协议。这种方法往往不可行,因为当这个协议形成一定标准后,千千万万设备上跑的程序都要按这个规则来。现在说改就放,谁去挨个儿告诉这些程序呢?很显然,这是一项不可能的工程。 + +另一种方式就是扩展,在原来包的格式的基础上扩展出一个头,里面包含足够用于区分租户的ID,外层的包的格式尽量和传统的一样,依然兼容原来的格式。一旦遇到需要区分用户的地方,我们就用这个特殊的程序,来处理这个特殊的包的格式。 + +这个概念很像咱们[第22讲]讲过的隧道理论,还记得自驾游通过摆渡轮到海南岛的那个故事吗?在那一节,我们说过,扩展的包头主要是用于加密的,而我们现在需要的包头是要能够区分用户的。 + +底层的物理网络设备组成的网络我们称为Underlay网络,而用于虚拟机和云中的这些技术组成的网络称为Overlay网络,这是一种基于物理网络的虚拟化网络实现。这一节我们重点讲两个Overlay的网络技术。 + +GRE + +第一个技术是GRE,全称Generic Routing Encapsulation,它是一种IP-over-IP的隧道技术。它将IP包封装在GRE包里,外面加上IP头,在隧道的一端封装数据包,并在通路上进行传输,到另外一端的时候解封装。你可以认为Tunnel是一个虚拟的、点对点的连接。 + + + +从这个图中可以看到,在GRE头中,前32位是一定会有的,后面的都是可选的。在前4位标识位里面,有标识后面到底有没有可选项?这里面有个很重要的key字段,是一个32位的字段,里面存放的往往就是用于区分用户的Tunnel ID。32位,够任何云平台喝一壶的了! + +下面的格式类型专门用于网络虚拟化的GRE包头格式,称为NVGRE,也给网络ID号24位,也完全够用了。 + +除此之外,GRE还需要有一个地方来封装和解封装GRE的包,这个地方往往是路由器或者有路由功能的Linux机器。 + +使用GRE隧道,传输的过程就像下面这张图。这里面有两个网段、两个路由器,中间要通过GRE隧道进行通信。当隧道建立之后,会多出两个Tunnel端口,用于封包、解封包。 + + + + +主机A在左边的网络,IP地址为192.168.1.102,它想要访问主机B,主机B在右边的网络,IP地址为192.168.2.115。于是发送一个包,源地址为192.168.1.102,目标地址为192.168.2.115。因为要跨网段访问,于是根据默认的default路由表规则,要发给默认的网关192.168.1.1,也即左边的路由器。 + +根据路由表,从左边的路由器,去192.168.2.0/24这个网段,应该走一条GRE的隧道,从隧道一端的网卡Tunnel0进入隧道。 + +在Tunnel隧道的端点进行包的封装,在内部的IP头之外加上GRE头。对于NVGRE来讲,是在MAC头之外加上GRE头,然后加上外部的IP地址,也即路由器的外网IP地址。源IP地址为172.17.10.10,目标IP地址为172.16.11.10,然后从E1的物理网卡发送到公共网络里。 + +在公共网络里面,沿着路由器一跳一跳地走,全部都按照外部的公网IP地址进行。 + +当网络包到达对端路由器的时候,也要到达对端的Tunnel0,然后开始解封装,将外层的IP头取下来,然后根据里面的网络包,根据路由表,从E3口转发出去到达服务器B。 + + +从GRE的原理可以看出,GRE通过隧道的方式,很好地解决了VLAN ID不足的问题。但是,GRE技术本身还是存在一些不足之处。 + +首先是Tunnel的数量问题。GRE是一种点对点隧道,如果有三个网络,就需要在每两个网络之间建立一个隧道。如果网络数目增多,这样隧道的数目会呈指数性增长。 + + + +其次,GRE不支持组播,因此一个网络中的一个虚机发出一个广播帧后,GRE会将其广播到所有与该节点有隧道连接的节点。 + +另外一个问题是目前还是有很多防火墙和三层网络设备无法解析GRE,因此它们无法对GRE封装包做合适地过滤和负载均衡。 + +VXLAN + +第二种Overlay的技术称为VXLAN。和三层外面再套三层的GRE不同,VXLAN则是从二层外面就套了一个VXLAN的头,这里面包含的VXLAN ID为24位,也够用了。在VXLAN头外面还封装了UDP、IP,以及外层的MAC头。 + + + +VXLAN作为扩展性协议,也需要一个地方对VXLAN的包进行封装和解封装,实现这个功能的点称为VTEP(VXLAN Tunnel Endpoint)。 + +VTEP相当于虚拟机网络的管家。每台物理机上都可以有一个VTEP。每个虚拟机启动的时候,都需要向这个VTEP管家注册,每个VTEP都知道自己上面注册了多少个虚拟机。当虚拟机要跨VTEP进行通信的时候,需要通过VTEP代理进行,由VTEP进行包的封装和解封装。 + +和GRE端到端的隧道不同,VXLAN不是点对点的,而是支持通过组播的来定位目标机器的,而非一定是这一端发出,另一端接收。 + +当一个VTEP启动的时候,它们都需要通过IGMP协议。加入一个组播组,就像加入一个邮件列表,或者加入一个微信群一样,所有发到这个邮件列表里面的邮件,或者发送到微信群里面的消息,大家都能收到。而当每个物理机上的虚拟机启动之后,VTEP就知道,有一个新的VM上线了,它归我管。 + + + +如图,虚拟机1、2、3属于云中同一个用户的虚拟机,因而需要分配相同的VXLAN ID=101。在云的界面上,就可以知道它们的IP地址,于是可以在虚拟机1上ping虚拟机2。 + +虚拟机1发现,它不知道虚拟机2的MAC地址,因而包没办法发出去,于是要发送ARP广播。 + + + +ARP请求到达VTEP1的时候,VTEP1知道,我这里有一台虚拟机,要访问一台不归我管的虚拟机,需要知道MAC地址,可是我不知道啊,这该咋办呢? + +VTEP1想,我不是加入了一个微信群么?可以在里面@all 一下,问问虚拟机2归谁管。于是VTEP1将ARP请求封装在VXLAN里面,组播出去。 + +当然在群里面,VTEP2和VTEP3都收到了消息,因而都会解开VXLAN包看,里面是一个ARP。 + +VTEP3在本地广播了半天,没人回,都说虚拟机2不归自己管。 + +VTEP2在本地广播,虚拟机2回了,说虚拟机2归我管,MAC地址是这个。通过这次通信,VTEP2也学到了,虚拟机1归VTEP1管,以后要找虚拟机1,去找VTEP1就可以了。 + + + +VTEP2将ARP的回复封装在VXLAN里面,这次不用组播了,直接发回给VTEP1。 + +VTEP1解开VXLAN的包,发现是ARP的回复,于是发给虚拟机1。通过这次通信,VTEP1也学到了,虚拟机2归VTEP2管,以后找虚拟机2,去找VTEP2就可以了。 + +虚拟机1的ARP得到了回复,知道了虚拟机2的MAC地址,于是就可以发送包了。 + + + +虚拟机1发给虚拟机2的包到达VTEP1,它当然记得刚才学的东西,要找虚拟机2,就去VTEP2,于是将包封装在VXLAN里面,外层加上VTEP1和VTEP2的IP地址,发送出去。 + +网络包到达VTEP2之后,VTEP2解开VXLAN封装,将包转发给虚拟机2。 + +虚拟机2回复的包,到达VTEP2的时候,它当然也记得刚才学的东西,要找虚拟机1,就去VTEP1,于是将包封装在VXLAN里面,外层加上VTEP1和VTEP2的IP地址,也发送出去。 + +网络包到达VTEP1之后,VTEP1解开VXLAN封装,将包转发给虚拟机1。 + + + +有了GRE和VXLAN技术,我们就可以解决云计算中VLAN的限制了。那如何将这个技术融入云平台呢? + +还记得将你宿舍里面的情况,所有东西都搬到一台物理机上那个故事吗? + + + +虚拟机是你的电脑,路由器和DHCP Server相当于家用路由器或者寝室长的电脑,外网网口访问互联网,所有的电脑都通过内网网口连接到一个交换机br0上,虚拟机要想访问互联网,需要通过br0连到路由器上,然后通过路由器将请求NAT后转发到公网。 + +接下来的事情就惨了,你们宿舍闹矛盾了,你们要分成三个宿舍住,对应上面的图,你们寝室长,也即路由器单独在一台物理机上,其他的室友也即VM分别在两台物理机上。这下把一个完整的br0一刀三断,每个宿舍都是单独的一段。 + + + +可是只有你的寝室长有公网口可以上网,于是你偷偷在三个宿舍中间打了一个隧道,用网线通过隧道将三个宿舍的两个br0连接起来,让其他室友的电脑和你寝室长的电脑,看起来还是连到同一个br0上,其实中间是通过你隧道中的网线做了转发。 + +为什么要多一个br1这个虚拟交换机呢?主要通过br1这一层将虚拟机之间的互联和物理机机之间的互联分成两层来设计,中间隧道可以有各种挖法,GRE、VXLAN都可以。 + +使用了OpenvSwitch之后,br0可以使用OpenvSwitch的Tunnel功能和Flow功能。 + +OpenvSwitch支持三类隧道:GRE、VXLAN、IPsec_GRE。在使用OpenvSwitch的时候,虚拟交换机就相当于GRE和VXLAN封装的端点。 + +我们模拟创建一个如下的网络拓扑结构,来看隧道应该如何工作。 + + + +三台物理机,每台上都有两台虚拟机,分别属于两个不同的用户,因而VLAN tag都得打地不一样,这样才不能相互通信。但是不同物理机上的相同用户,是可以通过隧道相互通信的,因而通过GRE隧道可以连接到一起。 + +接下来,所有的Flow Table规则都设置在br1上,每个br1都有三个网卡,其中网卡1是对内的,网卡2和3是对外的。 + +下面我们具体来看Flow Table的设计。 + + + +1.Table 0是所有流量的入口,所有进入br1的流量,分为两种流量,一个是进入物理机的流量,一个是从物理机发出的流量。 + +从port 1进来的,都是发出去的流量,全部由Table 1处理。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 in_port=1 actions=resubmit(,1)" + + +从port 2、3进来的,都是进入物理机的流量,全部由Table 3处理。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 in_port=2 actions=resubmit(,3)" +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 in_port=3 actions=resubmit(,3)" + + +如果都没匹配上,就默认丢弃。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=0 actions=drop" + + +2.Table 1用于处理所有出去的网络包,分为两种情况,一种是单播,一种是多播。 + +对于单播,由Table 20处理。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 table=1 dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)" + + +对于多播,由Table 21处理。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 table=1 dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,21)" + + +3.Table 2是紧接着Table1的,如果既不是单播,也不是多播,就默认丢弃。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=0 table=2 actions=drop" + + +4.Table 3用于处理所有进来的网络包,需要将隧道Tunnel ID转换为VLAN ID。 + +如果匹配不上Tunnel ID,就默认丢弃。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=0 table=3 actions=drop" + + +如果匹配上了Tunnel ID,就转换为相应的VLAN ID,然后跳到Table 10。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 table=3 tun_id=0x1 actions=mod_vlan_vid:1,resubmit(,10)" +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 table=3 tun_id=0x2 actions=mod_vlan_vid:2,resubmit(,10)" + + +5.对于进来的包,Table 10会进行MAC地址学习。这是一个二层交换机应该做的事情,学习完了之后,再从port 1发出去。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1 table=10 actions=learn(table=20,priority=1,hard_timeout=300,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1" + + +Table 10是用来学习MAC地址的,学习的结果放在Table 20里面。Table20被称为MAC learning table。 + +NXM_OF_VLAN_TCI是VLAN tag。在MAC learning table中,每一个entry都仅仅是针对某一个VLAN来说的,不同VLAN的learning table是分开的。在学习结果的entry中,会标出这个entry是针对哪个VLAN的。 + +NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]表示,当前包里面的MAC Source Address会被放在学习结果的entry里的dl_dst里。这是因为每个交换机都是通过进入的网络包来学习的。某个MAC从某个port进来,交换机就应该记住,以后发往这个MAC的包都要从这个port出去,因而源MAC地址就被放在了目标MAC地址里面,因为这是为了发送才这么做的。 + +load:0->NXM_OF_VLAN_TCI[]是说,在Table20中,将包从物理机发送出去的时候,VLAN tag设为0,所以学习完了之后,Table 20中会有actions=strip_vlan。 + +load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[]的意思是,在Table 20中,将包从物理机发出去的时候,设置Tunnel ID,进来的时候是多少,发送的时候就是多少,所以学习完了之后,Table 20中会有set_tunnel。 + +output:NXM_OF_IN_PORT[]是发送给哪个port。例如是从port 2进来的,那学习完了之后,Table 20中会有output:2。 + + + +所以如图所示,通过左边的MAC地址学习规则,学习到的结果就像右边的一样,这个结果会被放在Table 20里面。 + +6.Table 20是MAC Address Learning Table。如果不为空,就按照规则处理;如果为空,就说明没有进行过MAC地址学习,只好进行广播了,因而要交给Table 21处理。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=0 table=20 actions=resubmit(,21)" + + +7.Table 21用于处理多播的包。 + +如果匹配不上VLAN ID,就默认丢弃。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=0 table=21 actions=drop" + + +如果匹配上了VLAN ID,就将VLAN ID转换为Tunnel ID,从两个网卡port 2和port 3都发出去,进行多播。 + +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1table=21dl_vlan=1 actions=strip_vlan,set_tunnel:0x1,output:2,output:3" +ovs-ofctl add-flow br1 "hard_timeout=0 idle_timeout=0 priority=1table=21dl_vlan=2 actions=strip_vlan,set_tunnel:0x2,output:2,output:3" + + +小结 + +好了,这一节就到这里了,我们来总结一下。 + + +要对不同用户的网络进行隔离,解决VLAN数目有限的问题,需要通过Overlay的方式,常用的有GRE和VXLAN。 + +GRE是一种点对点的隧道模式,VXLAN支持组播的隧道模式,它们都要在某个Tunnel Endpoint进行封装和解封装,来实现跨物理机的互通。 + +OpenvSwitch可以作为Tunnel Endpoint,通过设置流表的规则,将虚拟机网络和物理机网络进行隔离、转换。 + + +最后,给你留两个思考题。 + + +虽然VXLAN可以支持组播,但是如果虚拟机数目比较多,在Overlay网络里面,广播风暴问题依然会很严重,你能想到什么办法解决这个问题吗? + +基于虚拟机的云比较复杂,而且虚拟机里面的网卡,到物理网络转换层次比较多,有一种比虚拟机更加轻量级的云的模式,你知道是什么吗? + + +我们的专栏更新到第28讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/29容器网络:来去自由的日子,不买公寓去合租.md b/专栏/趣谈网络协议/29容器网络:来去自由的日子,不买公寓去合租.md new file mode 100644 index 0000000..87b2544 --- /dev/null +++ b/专栏/趣谈网络协议/29容器网络:来去自由的日子,不买公寓去合租.md @@ -0,0 +1,300 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 29 容器网络:来去自由的日子,不买公寓去合租 + 如果说虚拟机是买公寓,容器则相当于合租,有一定的隔离,但是隔离性没有那么好。云计算解决了基础资源层的弹性伸缩,却没有解决PaaS层应用随基础资源层弹性伸缩而带来的批量、快速部署问题。于是,容器应运而生。 + +容器就是Container,而Container的另一个意思是集装箱。其实容器的思想就是要变成软件交付的集装箱。集装箱的特点,一是打包,二是标准。 + + + +在没有集装箱的时代,假设要将货物从A运到B,中间要经过三个码头、换三次船。每次都要将货物卸下船来,弄得乱七八糟,然后还要再搬上船重新整齐摆好。因此在没有集装箱的时候,每次换船,船员们都要在岸上待几天才能干完活。 + +有了尺寸全部都一样的集装箱以后,可以把所有的货物都打包在一起,所以每次换船的时候,一个箱子整体搬过去就行了,小时级别就能完成,船员再也不用耗费很长时间了。这是集装箱的“打包”“标准”两大特点在生活中的应用。 + + + +那么容器如何对应用打包呢? + +学习集装箱,首先要有个封闭的环境,将货物封装起来,让货物之间互不干扰,互相隔离,这样装货卸货才方便。 + +封闭的环境主要使用了两种技术,一种是看起来是隔离的技术,称为namespace,也即每个 namespace中的应用看到的是不同的 IP地址、用户空间、程号等。另一种是用起来是隔离的技术,称为cgroup,也即明明整台机器有很多的 CPU、内存,而一个应用只能用其中的一部分。 + +有了这两项技术,就相当于我们焊好了集装箱。接下来的问题就是如何“将这个集装箱标准化”,并在哪艘船上都能运输。这里的标准首先就是镜像。 + +所谓镜像,就是将你焊好集装箱的那一刻,将集装箱的状态保存下来,就像孙悟空说:“定!”,集装箱里的状态就被定在了那一刻,然后将这一刻的状态保存成一系列文件。无论从哪里运行这个镜像,都能完整地还原当时的情况。 + + + +接下来我们就具体来看看,这两种网络方面的打包技术。 + +命名空间(namespace) + +我们首先来看网络namespace。 + +namespace翻译过来就是命名空间。其实很多面向对象的程序设计语言里面,都有命名空间这个东西。大家一起写代码,难免会起相同的名词,编译就会冲突。而每个功能都有自己的命名空间,在不同的空间里面,类名相同,不会冲突。 + +在Linux下也是这样的,很多的资源都是全局的。比如进程有全局的进程ID,网络也有全局的路由表。但是,当一台Linux上跑多个进程的时候,如果我们觉得使用不同的路由策略,这些进程可能会冲突,那就需要将这个进程放在一个独立的namespace里面,这样就可以独立配置网络了。 + +网络的namespace由ip netns命令操作。它可以创建、删除、查询namespace。 + +我们再来看将你们宿舍放进一台物理机的那个图。你们宿舍长的电脑是一台路由器,你现在应该知道怎么实现这个路由器吧?可以创建一个Router虚拟机来做这件事情,但是还有一个更加简单的办法,就是我在图里画的这条虚线,这个就是通过namespace实现的。 + + + +我们创建一个routerns,于是一个独立的网络空间就产生了。你可以在里面尽情设置自己的规则。 + +ip netns add routerns + + +既然是路由器,肯定要能转发嘛,因而forward开关要打开。 + +ip netns exec routerns sysctl -w net.ipv4.ip_forward=1 + + +exec的意思就是进入这个网络空间做点事情。初始化一下iptables,因为这里面要配置NAT规则。 + +ip netns exec routerns iptables-save -c +ip netns exec routerns iptables-restore -c + + +路由器需要有一张网卡连到br0上,因而要创建一个网卡。 + +ovs-vsctl -- add-port br0 taprouter -- set Interface taprouter type=internal -- set Interface taprouter external-ids:iface-status=active -- set Interface taprouter external-ids:attached-mac=fa:16:3e:84:6e:cc + + +这个网络创建完了,但是是在namespace外面的,如何进去呢?可以通过这个命令: + +ip link set taprouter netns routerns + + +要给这个网卡配置一个IP地址,当然应该是虚拟机网络的网关地址。例如虚拟机私网网段为192.168.1.0/24,网关的地址往往为192.168.1.1。 + +ip netns exec routerns ip -4 addr add 192.168.1.1/24 brd 192.168.1.255 scope global dev taprouter + + +为了访问外网,还需要另一个网卡连在外网网桥br-ex上,并且塞在namespace里面。 + +ovs-vsctl -- add-port br-ex taprouterex -- set Interface taprouterex type=internal -- set Interface taprouterex external-ids:iface-status=active -- set Interface taprouterex external-ids:attached-mac=fa:16:3e:68:12:c0 + + +ip link set taprouterex netns routerns + + +我们还需要为这个网卡分配一个地址,这个地址应该和物理外网网络在一个网段。假设物理外网为16.158.1.0/24,可以分配一个外网地址16.158.1.100/24。 + +ip netns exec routerns ip -4 addr add 16.158.1.100/24 brd 16.158.1.255 scope global dev taprouterex + + +接下来,既然是路由器,就需要配置路由表,路由表是这样的: + +ip netns exec routerns route -n +Kernel IP routing table +Destination Gateway Genmask Flags Metric Ref Use Iface +0.0.0.0 16.158.1.1 0.0.0.0 UG 0 0 0 taprouterex +192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 taprouter +16.158.1.0 0.0.0.0 255.255.255.0 U 0 0 0 taprouterex + + +路由表中的默认路由是去物理外网的,去192.168.1.0/24也即虚拟机私网,走下面的网卡,去16.158.1.0/24也即物理外网,走上面的网卡。 + +我们在前面的章节讲过,如果要在虚拟机里面提供服务,提供给外网的客户端访问,客户端需要访问外网IP3,会在外网网口NAT称为虚拟机私网IP。这个NAT规则要在这个namespace里面配置。 + +ip netns exec routerns iptables -t nat -nvL +Chain PREROUTING +target prot opt in out source destination +DNAT all -- * * 0.0.0.0/0 16.158.1.103 to:192.168.1.3 +Chain POSTROUTING +target prot opt in out source destination +SNAT all -- * * 192.168.1.3 0.0.0.0/0 to:16.158.1.103 + + +这里面有两个规则,一个是SNAT,将虚拟机的私网IP 192.168.1.3 NAT成物理外网IP 16.158.1.103。一个是DNAT,将物理外网IP 16.158.1.103 NAT成虚拟机私网IP 192.168.1.3。 + +至此为止,基于网络namespace的路由器实现完毕。 + +机制网络(cgroup) + +我们再来看打包的另一个机制网络cgroup。 + +cgroup全称control groups,是Linux内核提供的一种可以限制、隔离进程使用的资源机制。 + +cgroup能控制哪些资源呢?它有很多子系统: + + +CPU子系统使用调度程序为进程控制CPU的访问; + +cpuset,如果是多核心的CPU,这个子系统会为进程分配单独的CPU和内存; + +memory子系统,设置进程的内存限制以及产生内存资源报告; + +blkio子系统,设置限制每个块设备的输入输出控制; + +net_cls,这个子系统使用等级识别符(classid)标记网络数据包,可允许Linux 流量控制程序(tc)识别从具体cgroup中生成的数据包。 + + +我们这里最关心的是net_cls,它可以和前面讲过的TC关联起来。 + +cgroup提供了一个虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用cgroup,必须挂载cgroup文件系统,一般情况下都是挂载到/sys/fs/cgroup目录下。 + +所以首先我们要挂载一个net_cls的文件系统。 + +mkdir /sys/fs/cgroup/net_cls +mount -t cgroup -onet_cls net_cls /sys/fs/cgroup/net_cls + + +接下来我们要配置TC了。还记得咱们实验TC的时候那颗树吗? + + + +当时我们通过这个命令设定了规则:从1.2.3.4来的,发送给port 80的包,从1:10走;其他从1.2.3.4发送来的包从1:11走;其他的走默认。 + +tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10 +tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 flowid 1:11 + + +这里是根据源IP来设定的,现在有了cgroup,我们按照cgroup再来设定规则。 + +tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 1: cgroup + + +假设我们有两个用户a和b,要对它们进行带宽限制。 + +首先,我们要创建两个net_cls。 + +mkdir /sys/fs/cgroup/net_cls/a +mkdir /sys/fs/cgroup/net_cls/b + + +假设用户a启动的进程ID为12345,把它放在net_cls/a/tasks文件中。同样假设用户b启动的进程ID为12346,把它放在net_cls/b/tasks文件中。 + +net_cls/a目录下面,还有一个文件net_cls.classid,我们放flowid 1:10。net_cls/b目录下面,也创建一个文件net_cls.classid,我们放flowid 1:11。 + +这个数字怎么放呢?要转换成一个0xAAAABBBB的值,AAAA对应class中冒号前面的数字,而BBBB对应后面的数字。 + +echo 0x00010010 > /sys/fs/cgroup/net_cls/a/net_cls.classid +echo 0x00010011 > /sys/fs/cgroup/net_cls/b/net_cls.classid + + +这样用户a的进程发的包,会打上1:10这个标签;用户b的进程发的包,会打上1:11这个标签。然后TC根据这两个标签,让用户a的进程的包走左边的分支,用户b的进程的包走右边的分支。 + +容器网络中如何融入物理网络? + +了解了容器背后的技术,接下来我们来看,容器网络究竟是如何融入物理网络的? + +如果你使用docker run运行一个容器,你应该能看到这样一个拓扑结构。 + + + +是不是和虚拟机很像?容器里面有张网卡,容器外有张网卡,容器外的网卡连到docker0网桥,通过这个网桥,容器直接实现相互访问。 + +如果你用brctl查看docker0网桥,你会发现它上面连着一些网卡。其实这个网桥和[第24讲],咱们自己用brctl创建的网桥没什么两样。 + +那连接容器和网桥的那个网卡和虚拟机一样吗?在虚拟机场景下,有一个虚拟化软件,通过TUN/TAP设备虚拟一个网卡给虚拟机,但是容器场景下并没有虚拟化软件,这该怎么办呢? + +在Linux下,可以创建一对veth pair的网卡,从一边发送包,另一边就能收到。 + +我们首先通过这个命令创建这么一对。 + +ip link add name veth1 mtu 1500 type veth peer name veth2 mtu 1500 + + +其中一边可以打到docker0网桥上。 + +ip link set veth1 master testbr +ip link set veth1 up + + +那另一端如何放到容器里呢? + +一个容器的启动会对应一个namespace,我们要先找到这个namespace。对于docker来讲,pid就是namespace的名字,可以通过这个命令获取。 + +docker inspect '--format={{ .State.Pid }}' test + + +假设结果为12065,这个就是namespace名字。 + +默认Docker创建的网络namespace不在默认路径下 ,ip netns看不到,所以需要ln软链接一下。链接完毕以后,我们就可以通过ip netns命令操作了。 + +rm -f /var/run/netns/12065 +ln -s /proc/12065/ns/net /var/run/netns/12065 + + +然后,我们就可以将另一端veth2塞到namespace里面。 + +ip link set veth2 netns 12065 + + +然后,将容器内的网卡重命名。 + +ip netns exec 12065 ip link set veth2 name eth0 + + +然后,给容器内网卡设置ip地址。 + +ip netns exec 12065 ip addr add 172.17.0.2/16 dev eth0 +ip netns exec 12065 ip link set eth0 up + + +一台机器内部容器的互相访问没有问题了,那如何访问外网呢? + +你先想想看有没有思路?对,就是虚拟机里面的桥接模式和NAT模式。Docker默认使用NAT模式。NAT模式分为SNAT和DNAT,如果是容器内部访问外部,就需要通过SNAT。 + +从容器内部的客户端访问外部网络中的服务器,我画了一张图。在[虚拟机]那一节,也有一张类似的图。 + + + +在宿主机上,有这么一条iptables规则: + +-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE + + +所有从容器内部发出来的包,都要做地址伪装,将源IP地址,转换为物理网卡的IP地址。如果有多个容器,所有的容器共享一个外网的IP地址,但是在conntrack表中,记录下这个出去的连接。 + +当服务器返回结果的时候,到达物理机,会根据conntrack表中的规则,取出原来的私网IP,通过DNAT将地址转换为私网IP地址,通过网桥docker0实现对内的访问。 + +如果在容器内部属于一个服务,例如部署一个网站,提供给外部进行访问,需要通过Docker的端口映射技术,将容器内部的端口映射到物理机上来。 + +例如容器内部监听80端口,可以通Docker run命令中的参数-p 10080:80,将物理机上的10080端口和容器的80端口映射起来, 当外部的客户端访问这个网站的时候,通过访问物理机的10080端口,就能访问到容器内的80端口了。 + + + +Docker有两种方式,一种是通过一个进程docker-proxy的方式,监听10080,转换为80端口。 + +/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10080 -container-ip 172.17.0.2 -container-port 80 + + +另外一种方式是通过DNAT方式,在-A PREROUTING阶段加一个规则,将到端口10080的DNAT称为容器的私有网络。 + +-A DOCKER -p tcp -m tcp --dport 10080 -j DNAT --to-destination 172.17.0.2:80 + + +如此就可以实现容器和物理网络之间的互通了。 + +小结 + +好了,这一节就到这里了,我们来总结一下。 + + +容器是一种比虚拟机更加轻量级的隔离方式,主要通过namespace和cgroup技术进行资源的隔离,namespace用于负责看起来隔离,cgroup用于负责用起来隔离。 + +容器网络连接到物理网络的方式和虚拟机很像,通过桥接的方式实现一台物理机上的容器进行相互访问,如果要访问外网,最简单的方式还是通过NAT。 + + +最后,给你留两个思考题: + + +容器内的网络和物理机网络可以使用NAT的方式相互访问,如果这种方式用于部署应用,有什么问题呢? + +和虚拟机一样,不同物理机上的容器需要相互通信,你知道容器是怎么做到这一点吗? + + +我们的专栏更新到第29讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/30容器网络之Flannel:每人一亩三分地.md b/专栏/趣谈网络协议/30容器网络之Flannel:每人一亩三分地.md new file mode 100644 index 0000000..fad0f08 --- /dev/null +++ b/专栏/趣谈网络协议/30容器网络之Flannel:每人一亩三分地.md @@ -0,0 +1,117 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 30 容器网络之Flannel:每人一亩三分地 + 上一节我们讲了容器网络的模型,以及如何通过NAT的方式与物理网络进行互通。 + +每一台物理机上面安装好了Docker以后,都会默认分配一个172.17.0.0/16的网段。一台机器上新创建的第一个容器,一般都会给172.17.0.2这个地址,当然一台机器这样玩玩倒也没啥问题。但是容器里面是要部署应用的,就像上一节讲过的一样,它既然是集装箱,里面就需要装载货物。 + +如果这个应用是比较传统的单体应用,自己就一个进程,所有的代码逻辑都在这个进程里面,上面的模式没有任何问题,只要通过NAT就能访问进来。 + +但是因为无法解决快速迭代和高并发的问题,单体应用越来越跟不上时代发展的需要了。 + +你可以回想一下,无论是各种网络直播平台,还是共享单车,是不是都是很短时间内就要积累大量用户,否则就会错过风口。所以应用需要在很短的时间内快速迭代,不断调整,满足用户体验;还要在很短的时间内,具有支撑高并发请求的能力。 + +单体应用作为个人英雄主义的时代已经过去了。如果所有的代码都在一个工程里面,开发的时候必然存在大量冲突,上线的时候,需要开大会进行协调,一个月上线一次就很不错了。而且所有的流量都让一个进程扛,怎么也扛不住啊! + +没办法,一个字:拆!拆开了,每个子模块独自变化,减少相互影响。拆开了,原来一个进程扛流量,现在多个进程一起扛。所以,微服务就是从个人英雄主义,变成集团军作战。 + +容器作为集装箱,可以保证应用在不同的环境中快速迁移,提高迭代的效率。但是如果要形成容器集团军,还需要一个集团军作战的调度平台,这就是Kubernetes。它可以灵活地将一个容器调度到任何一台机器上,并且当某个应用扛不住的时候,只要在Kubernetes上修改容器的副本数,一个应用马上就能变八个,而且都能提供服务。 + +然而集团军作战有个重要的问题,就是通信。这里面包含两个问题,第一个是集团军的A部队如何实时地知道B部队的位置变化,第二个是两个部队之间如何相互通信。 + +第一个问题位置变化,往往是通过一个称为注册中心的地方统一管理的,这个是应用自己做的。当一个应用启动的时候,将自己所在环境的IP地址和端口,注册到注册中心指挥部,这样其他的应用请求它的时候,到指挥部问一下它在哪里就好了。当某个应用发生了变化,例如一台机器挂了,容器要迁移到另一台机器,这个时候IP改变了,应用会重新注册,则其他的应用请求它的时候,还是能够从指挥部得到最新的位置。 + + + +接下来是如何相互通信的问题。NAT这种模式,在多个主机的场景下,是存在很大问题的。在物理机A上的应用A看到的IP地址是容器A的,是172.17.0.2,在物理机B上的应用B看到的IP地址是容器B的,不巧也是172.17.0.2,当它们都注册到注册中心的时候,注册中心就是这个图里这样子。 + + + +这个时候,应用A要访问应用B,当应用A从注册中心将应用B的IP地址读出来的时候,就彻底困惑了,这不是自己访问自己吗? + +怎么解决这个问题呢?一种办法是不去注册容器内的IP地址,而是注册所在物理机的IP地址,端口也要是物理机上映射的端口。 + + + +这样存在的问题是,应用是在容器里面的,它怎么知道物理机上的IP地址和端口呢?这明明是运维人员配置的,除非应用配合,读取容器平台的接口获得这个IP和端口。一方面,大部分分布式框架都是容器诞生之前就有了,它们不会适配这种场景;另一方面,让容器内的应用意识到容器外的环境,本来就是非常不好的设计。 + +说好的集装箱,说好的随意迁移呢?难道要让集装箱内的货物意识到自己传的信息?而且本来Tomcat都是监听8080端口的,结果到了物理机上,就不能大家都用这个端口了,否则端口就冲突了,因而就需要随机分配端口,于是在注册中心就出现了各种各样奇怪的端口。无论是注册中心,还是调用方都会觉得很奇怪,而且不是默认的端口,很多情况下也容易出错。 + +Kubernetes作为集团军作战管理平台,提出指导意见,说网络模型要变平,但是没说怎么实现。于是业界就涌现了大量的方案,Flannel就是其中之一。 + +对于IP冲突的问题,如果每一个物理机都是网段172.17.0.0/16,肯定会冲突啊,但是这个网段实在太大了,一台物理机上根本启动不了这么多的容器,所以能不能每台物理机在这个大网段里面,抠出一个小的网段,每个物理机网段都不同,自己看好自己的一亩三分地,谁也不和谁冲突。 + +例如物理机A是网段172.17.8.0/24,物理机B是网段172.17.9.0/24,这样两台机器上启动的容器IP肯定不一样,而且就看IP地址,我们就一下子识别出,这个容器是本机的,还是远程的,如果是远程的,也能从网段一下子就识别出它归哪台物理机管,太方便了。 + +接下来的问题,就是物理机A上的容器如何访问到物理机B上的容器呢? + +你是不是想到了熟悉的场景?虚拟机也需要跨物理机互通,往往通过Overlay的方式,容器是不是也可以这样做呢? + +这里我要说Flannel使用UDP实现Overlay网络的方案。 + + + +在物理机A上的容器A里面,能看到的容器的IP地址是172.17.8.2/24,里面设置了默认的路由规则default via 172.17.8.1 dev eth0。 + +如果容器A要访问172.17.9.2,就会发往这个默认的网关172.17.8.1。172.17.8.1就是物理机上面docker0网桥的IP地址,这台物理机上的所有容器都是连接到这个网桥的。 + +在物理机上面,查看路由策略,会有这样一条172.17.0.0/24 via 172.17.0.0 dev flannel.1,也就是说发往172.17.9.2的网络包会被转发到flannel.1这个网卡。 + +这个网卡是怎么出来的呢?在每台物理机上,都会跑一个flanneld进程,这个进程打开一个/dev/net/tun字符设备的时候,就出现了这个网卡。 + +你有没有想起qemu-kvm,打开这个字符设备的时候,物理机上也会出现一个网卡,所有发到这个网卡上的网络包会被qemu-kvm接收进来,变成二进制串。只不过接下来qemu-kvm会模拟一个虚拟机里面的网卡,将二进制的串变成网络包,发给虚拟机里面的网卡。但是flanneld不用这样做,所有发到flannel.1这个网卡的包都会被flanneld进程读进去,接下来flanneld要对网络包进行处理。 + +物理机A上的flanneld会将网络包封装在UDP包里面,然后外层加上物理机A和物理机B的IP地址,发送给物理机B上的flanneld。 + +为什么是UDP呢?因为不想在flanneld之间建立两两连接,而UDP没有连接的概念,任何一台机器都能发给另一台。 + +物理机B上的flanneld收到包之后,解开UDP的包,将里面的网络包拿出来,从物理机B的flannel.1网卡发出去。 + +在物理机B上,有路由规则172.17.9.0/24 dev docker0 proto kernel scope link src 172.17.9.1。 + +将包发给docker0,docker0将包转给容器B。通信成功。 + +上面的过程连通性没有问题,但是由于全部在用户态,所以性能差了一些。 + +跨物理机的连通性问题,在虚拟机那里有成熟的方案,就是VXLAN,那能不能Flannel也用VXLAN呢? + +当然可以了。如果使用VXLAN,就不需要打开一个TUN设备了,而是要建立一个VXLAN的VTEP。如何建立呢?可以通过netlink通知内核建立一个VTEP的网卡flannel.1。在我们讲OpenvSwitch的时候提过,netlink是一种用户态和内核态通信的机制。 + +当网络包从物理机A上的容器A发送给物理机B上的容器B,在容器A里面通过默认路由到达物理机A上的docker0网卡,然后根据路由规则,在物理机A上,将包转发给flannel.1。这个时候flannel.1就是一个VXLAN的VTEP了,它将网络包进行封装。 + +内部的MAC地址这样写:源为物理机A的flannel.1的MAC地址,目标为物理机B的flannel.1的MAC地址,在外面加上VXLAN的头。 + +外层的IP地址这样写:源为物理机A的IP地址,目标为物理机B的IP地址,外面加上物理机的MAC地址。 + +这样就能通过VXLAN将包转发到另一台机器,从物理机B的flannel.1上解包,变成内部的网络包,通过物理机B上的路由转发到docker0,然后转发到容器B里面。通信成功。 + + + +小结 + +好了,今天的内容就到这里,我来总结一下。 + + +基于NAT的容器网络模型在微服务架构下有两个问题,一个是IP重叠,一个是端口冲突,需要通过Overlay网络的机制保持跨节点的连通性。 + +Flannel是跨节点容器网络方案之一,它提供的Overlay方案主要有两种方式,一种是UDP在用户态封装,一种是VXLAN在内核态封装,而VXLAN的性能更好一些。 + + +最后,给你留两个问题: + + +通过Flannel的网络模型可以实现容器与容器直接跨主机的互相访问,那你知道如果容器内部访问外部的服务应该怎么融合到这个网络模型中吗? + +基于Overlay的网络毕竟做了一次网络虚拟化,有没有更加高性能的方案呢? + + +我们的专栏更新到第30讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/31容器网络之Calico:为高效说出善意的谎言.md b/专栏/趣谈网络协议/31容器网络之Calico:为高效说出善意的谎言.md new file mode 100644 index 0000000..98a7efe --- /dev/null +++ b/专栏/趣谈网络协议/31容器网络之Calico:为高效说出善意的谎言.md @@ -0,0 +1,216 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 31 容器网络之Calico:为高效说出善意的谎言 + 上一节我们讲了Flannel如何解决容器跨主机互通的问题,这个解决方式其实和虚拟机的网络互通模式是差不多的,都是通过隧道。但是Flannel有一个非常好的模式,就是给不同的物理机设置不同网段,这一点和虚拟机的Overlay的模式完全不一样。 + +在虚拟机的场景下,整个网段在所有的物理机之间都是可以“飘来飘去”的。网段不同,就给了我们做路由策略的可能。 + +Calico网络模型的设计思路 + +我们看图中的两台物理机。它们的物理网卡是同一个二层网络里面的。由于两台物理机的容器网段不同,我们完全可以将两台物理机配置成为路由器,并按照容器的网段配置路由表。 + + + +例如,在物理机A中,我们可以这样配置:要想访问网段172.17.9.0/24,下一跳是192.168.100.101,也即到物理机B上去。 + +这样在容器A中访问容器B,当包到达物理机A的时候,就能够匹配到这条路由规则,并将包发给下一跳的路由器,也即发给物理机B。在物理机B上也有路由规则,要访问172.17.9.0/24,从docker0的网卡进去即可。 + +当容器B返回结果的时候,在物理机B上,可以做类似的配置:要想访问网段172.17.8.0/24,下一跳是192.168.100.100,也即到物理机A上去。 + +当包到达物理机B的时候,能够匹配到这条路由规则,将包发给下一跳的路由器,也即发给物理机A。在物理机A上也有路由规则,要访问172.17.8.0/24,从docker0的网卡进去即可。 + +这就是Calico网络的大概思路,即不走Overlay网络,不引入另外的网络性能损耗,而是将转发全部用三层网络的路由转发来实现,只不过具体的实现和上面的过程稍有区别。 + +首先,如果全部走三层的路由规则,没必要每台机器都用一个docker0,从而浪费了一个IP地址,而是可以直接用路由转发到veth pair在物理机这一端的网卡。同样,在容器内,路由规则也可以这样设定:把容器外面的veth pair网卡算作默认网关,下一跳就是外面的物理机。 + +于是,整个拓扑结构就变成了这个图中的样子。 + + + +Calico网络的转发细节 + +我们来看其中的一些细节。 + +容器A1的IP地址为172.17.8.2/32,这里注意,不是/24,而是/32,将容器A1作为一个单点的局域网了。 + +容器A1里面的默认路由,Calico配置得比较有技巧。 + +default via 169.254.1.1 dev eth0 +169.254.1.1 dev eth0 scope link + + +这个IP地址169.254.1.1是默认的网关,但是整个拓扑图中没有一张网卡是这个地址。那如何到达这个地址呢? + +前面我们讲网关的原理的时候说过,当一台机器要访问网关的时候,首先会通过ARP获得网关的MAC地址,然后将目标MAC变为网关的MAC,而网关的IP地址不会在任何网络包头里面出现,也就是说,没有人在乎这个地址具体是什么,只要能找到对应的MAC,响应ARP就可以了。 + +ARP本地有缓存,通过ip neigh命令可以查看。 + +169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee STALE + + +这个MAC地址是Calico硬塞进去的,但是没有关系,它能响应ARP,于是发出的包的目标MAC就是这个MAC地址。 + +在物理机A上查看所有网卡的MAC地址的时候,我们会发现veth1就是这个MAC地址。所以容器A1里发出的网络包,第一跳就是这个veth1这个网卡,也就到达了物理机A这个路由器。 + +在物理机A上有三条路由规则,分别是去两个本机的容器的路由,以及去172.17.9.0/24,下一跳为物理机B。 + +172.17.8.2 dev veth1 scope link +172.17.8.3 dev veth2 scope link +172.17.9.0/24 via 192.168.100.101 dev eth0 proto bird onlink + + +同理,物理机B上也有三条路由规则,分别是去两个本机的容器的路由,以及去172.17.8.0/24,下一跳为物理机A。 + +172.17.9.2 dev veth1 scope link +172.17.9.3 dev veth2 scope link +172.17.8.0/24 via 192.168.100.100 dev eth0 proto bird onlink + + +如果你觉得这些规则过于复杂,我将刚才的拓扑图转换为这个更加容易理解的图。 + + + +在这里,物理机化身为路由器,通过路由器上的路由规则,将包转发到目的地。在这个过程中,没有隧道封装解封装,仅仅是单纯的路由转发,性能会好很多。但是,这种模式也有很多问题。 + +Calico的架构 + +路由配置组件Felix + +如果只有两台机器,每台机器只有两个容器,而且保持不变。我手动配置一下,倒也没啥问题。但是如果容器不断地创建、删除,节点不断地加入、退出,情况就会变得非常复杂。 + + + +就像图中,有三台物理机,两两之间都需要配置路由,每台物理机上对外的路由就有两条。如果有六台物理机,则每台物理机上对外的路由就有五条。新加入一个节点,需要通知每一台物理机添加一条路由。 + +这还是在物理机之间,一台物理机上,每创建一个容器,也需要多配置一条指向这个容器的路由。如此复杂,肯定不能手动配置,需要每台物理机上有一个agent,当创建和删除容器的时候,自动做这件事情。这个agent在Calico中称为Felix。 + +路由广播组件BGP Speaker + +当Felix配置了路由之后,接下来的问题就是,如何将路由信息,也即将“如何到达我这个节点,访问我这个节点上的容器”这些信息,广播出去。 + +能想起来吗?这其实就是路由协议啊!路由协议就是将“我能到哪里,如何能到我”的信息广播给全网传出去,从而客户端可以一跳一跳地访问目标地址的。路由协议有很多种,Calico使用的是BGP协议。 + +在Calico中,每个Node上运行一个软件BIRD,作为BGP的客户端,或者叫作BGP Speaker,将“如何到达我这个Node,访问我这个Node上的容器”的路由信息广播出去。所有Node上的BGP Speaker 都互相建立连接,就形成了全互连的情况,这样每当路由有所变化的时候,所有节点就都能够收到了。 + +安全策略组件 + +Calico中还实现了灵活配置网络策略Network Policy,可以灵活配置两个容器通或者不通。这个怎么实现呢? + + + +虚拟机中的安全组,是用iptables实现的。Calico中也是用iptables实现的。这个图里的内容是iptables在内核处理网络包的过程中可以嵌入的处理点。Calico也是在这些点上设置相应的规则。 + + + +当网络包进入物理机上的时候,进入PREOUTING规则,这里面有一个规则是cali-fip-dnat,这是实现浮动IP(Floating IP)的场景,主要将外网的IP地址dnat作为容器内的IP地址。在虚拟机场景下,路由器的网络namespace里面有一个外网网卡上,也设置过这样一个DNAT规则。 + +接下来可以根据路由判断,是到本地的,还是要转发出去的。 + +如果是本地的,走INPUT规则,里面有个规则是cali-wl-to-host,wl的意思是workload,也即容器,也即这是用来判断从容器发到物理机的网络包是否符合规则的。这里面内嵌一个规则cali-from-wl-dispatch,也是匹配从容器来的包。如果有两个容器,则会有两个容器网卡,这里面内嵌有详细的规则“cali-fw-cali网卡1”和“cali-fw-cali网卡2”,fw就是from workload,也就是匹配从容器1来的网络包和从容器2来的网络包。 + +如果是转发出去的,走FORWARD规则,里面有个规则cali-FORWARD。这里面分两种情况,一种是从容器里面发出来,转发到外面的;另一种是从外面发进来,转发到容器里面的。 + +第一种情况匹配的规则仍然是cali-from-wl-dispatch,也即from workload。第二种情况匹配的规则是cali-to-wl-dispatch,也即to workload。如果有两个容器,则会有两个容器网卡,在这里面内嵌有详细的规则“cali-tw-cali网卡1”和“cali-tw-cali网卡2”,tw就是to workload,也就是匹配发往容器1的网络包和发送到容器2的网络包。 + +接下来是匹配OUTPUT规则,里面有cali-OUTPUT。接下来是POSTROUTING规则,里面有一个规则是cali-fip-snat,也即发出去的时候,将容器网络IP转换为浮动IP地址。在虚拟机场景下,路由器的网络namespace里面有一个外网网卡上,也设置过这样一个SNAT规则。 + +至此为止,Calico的所有组件基本凑齐。来看看我汇总的图。 + + + +全连接复杂性与规模问题 + +这里面还存在问题,就是BGP全连接的复杂性问题。 + +你看刚才的例子里只有六个节点,BGP的互连已经如此复杂,如果节点数据再多,这种全互连的模式肯定不行,到时候都成蜘蛛网了。于是多出了一个组件BGP Route Reflector,它也是用BIRD实现的。有了它,BGP Speaker就不用全互连了,而是都直连它,它负责将全网的路由信息广播出去。 + +可是问题来了,规模大了,大家都连它,它受得了吗?这个BGP Router Reflector会不会成为瓶颈呢? + +所以,肯定不能让一个BGP Router Reflector管理所有的路由分发,而是应该有多个BGP Router Reflector,每个BGP Router Reflector管一部分。 + +多大算一部分呢?咱们讲述数据中心的时候,说服务器都是放在机架上的,每个机架上最顶端有个TOR交换机。那将机架上的机器连在一起,这样一个机架是不是可以作为一个单元,让一个BGP Router Reflector来管理呢?如果要跨机架,如何进行通信呢?这就需要BGP Router Reflector也直接进行路由交换。它们之间的交换和一个机架之间的交换有什么关系吗? + +有没有觉得在这个场景下,一个机架就像一个数据中心,可以把它设置为一个AS,而BGP Router Reflector有点儿像数据中心的边界路由器。在一个AS内部,也即服务器和BGP Router Reflector之间使用的是数据中心内部的路由协议iBGP,BGP Router Reflector之间使用的是数据中心之间的路由协议eBGP。 + + + +这个图中,一个机架上有多台机器,每台机器上面启动多个容器,每台机器上都有可以到达这些容器的路由。每台机器上都启动一个BGP Speaker,然后将这些路由规则上报到这个Rack上接入交换机的BGP Route Reflector,将这些路由通过iBGP协议告知到接入交换机的三层路由功能。 + +在接入交换机之间也建立BGP连接,相互告知路由,因而一个Rack里面的路由可以告知另一个Rack。有多个核心或者汇聚交换机将接入交换机连接起来,如果核心和汇聚起二层互通的作用,则接入和接入之间之间交换路由即可。如果核心和汇聚交换机起三层路由的作用,则路由需要通过核心或者汇聚交换机进行告知。 + +跨网段访问问题 + +上面的Calico模式还有一个问题,就是跨网段问题,这里的跨网段是指物理机跨网段。 + +前面我们说的那些逻辑成立的条件,是我们假设物理机可以作为路由器进行使用。例如物理机A要告诉物理机B,你要访问172.17.8.0/24,下一跳是我192.168.100.100;同理,物理机B要告诉物理机A,你要访问172.17.9.0/24,下一跳是我192.168.100.101。 + +之所以能够这样,是因为物理机A和物理机B是同一个网段的,是连接在同一个交换机上的。那如果物理机A和物理机B不是在同一个网段呢? + +- + + +例如,物理机A的网段是192.168.100.100/24,物理机B的网段是192.168.200.101/24,这样两台机器就不能通过二层交换机连接起来了,需要在中间放一台路由器,做一次路由转发,才能跨网段访问。 + +本来物理机A要告诉物理机B,你要访问172.17.8.0/24,下一跳是我192.168.100.100的,但是中间多了一台路由器,下一跳不是我了,而是中间的这台路由器了,这台路由器的再下一跳,才是我。这样之前的逻辑就不成立了。 + +我们看刚才那张图的下半部分。物理机B上的容器要访问物理机A上的容器,第一跳就是物理机B,IP为192.168.200.101,第二跳是中间的物理路由器右面的网口,IP为192.168.200.1,第三跳才是物理机A,IP为192.168.100.100。 + +这是咱们通过拓扑图看到的,关键问题是,在系统中物理机A如何告诉物理机B,怎么让它才能到我这里?物理机A根本不可能知道从物理机B出来之后的下一跳是谁,况且现在只是中间隔着一个路由器这种简单的情况,如果隔着多个路由器呢?谁能把这一串的路径告诉物理机B呢? + +我们能想到的第一种方式是,让中间所有的路由器都来适配Calico。本来它们互相告知路由,只互相告知物理机的,现在还要告知容器的网段。这在大部分情况下,是不可能的。 + +第二种方式,还是在物理机A和物理机B之间打一个隧道,这个隧道有两个端点,在端点上进行封装,将容器的IP作为乘客协议放在隧道里面,而物理主机的IP放在外面作为承载协议。这样不管外层的IP通过传统的物理网络,走多少跳到达目标物理机,从隧道两端看起来,物理机A的下一跳就是物理机B,这样前面的逻辑才能成立。 + +这就是Calico的IPIP模式。使用了IPIP模式之后,在物理机A上,我们能看到这样的路由表: + +172.17.8.2 dev veth1 scope link +172.17.8.3 dev veth2 scope link +172.17.9.0/24 via 192.168.200.101 dev tun0 proto bird onlink + + +这和原来模式的区别在于,下一跳不再是同一个网段的物理机B了,IP为192.168.200.101,并且不是从eth0跳,而是建立一个隧道的端点tun0,从这里才是下一跳。 + +如果我们在容器A1里面的172.17.8.2,去ping容器B1里面的172.17.9.2,首先会到物理机A。在物理机A上根据上面的规则,会转发给tun0,并在这里对包做封装: + + +内层源IP为172.17.8.2; + +内层目标IP为172.17.9.2; + +外层源IP为192.168.100.100; + +外层目标IP为192.168.200.101。 + + +将这个包从eth0发出去,在物理网络上会使用外层的IP进行路由,最终到达物理机B。在物理机B上,tun0会解封装,将内层的源IP和目标IP拿出来,转发给相应的容器。 + +小结 + +好了,这一节就到这里,我们来总结一下。 + + +Calico推荐使用物理机作为路由器的模式,这种模式没有虚拟化开销,性能比较高。 + +Calico的主要组件包括路由、iptables的配置组件Felix、路由广播组件BGP Speaker,以及大规模场景下的BGP Route Reflector。 + +为解决跨网段的问题,Calico还有一种IPIP模式,也即通过打隧道的方式,从隧道端点来看,将本来不是邻居的两台机器,变成相邻的机器。 + + +最后,给你留两个思考题: + + +将Calico部署在公有云上的时候,经常会选择使用IPIP模式,你知道这是为什么吗? + +容器是用来部署微服务的,微服务之间的通信,除了网络要互通,还需要高效地传输信息,例如下单的商品、价格、数量、支付的钱等等,这些要通过什么样的协议呢? + + +我们的专栏更新到第31讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/32RPC协议综述:远在天边,近在眼前.md b/专栏/趣谈网络协议/32RPC协议综述:远在天边,近在眼前.md new file mode 100644 index 0000000..b24833f --- /dev/null +++ b/专栏/趣谈网络协议/32RPC协议综述:远在天边,近在眼前.md @@ -0,0 +1,177 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 32 RPC协议综述:远在天边,近在眼前 + 前面我们讲了容器网络如何实现跨主机互通,以及微服务之间的相互调用。 + + + +网络是打通了,那服务之间的互相调用,该怎么实现呢?你可能说,咱不是学过Socket吗。服务之间分调用方和被调用方,我们就建立一个TCP或者UDP的连接,不就可以通信了? + + + +你仔细想一下,这事儿没这么简单。我们就拿最简单的场景,客户端调用一个加法函数,将两个整数加起来,返回它们的和。 + +如果放在本地调用,那是简单的不能再简单了,只要稍微学过一种编程语言,三下五除二就搞定了。但是一旦变成了远程调用,门槛一下子就上去了。 + +首先你要会Socket编程,至少先要把咱们这门网络协议课学一下,然后再看N本砖头厚的Socket程序设计的书,学会咱们学过的几种Socket程序设计的模型。这就使得本来大学毕业就能干的一项工作,变成了一件五年工作经验都不一定干好的工作,而且,搞定了Socket程序设计,才是万里长征的第一步。后面还有很多问题呢! + +如何解决这五个问题? + +问题一:如何规定远程调用的语法? + +客户端如何告诉服务端,我是一个加法,而另一个是乘法。我是用字符串“add”传给你,还是传给你一个整数,比如1表示加法,2表示乘法?服务端该如何告诉客户端,我的这个加法,目前只能加整数,不能加小数,不能加字符串;而另一个加法“add1”,它能实现小数和整数的混合加法。那返回值是什么?正确的时候返回什么,错误的时候又返回什么? + +问题二:如果传递参数? + +我是先传两个整数,后传一个操作符“add”,还是先传操作符,再传两个整数?是不是像咱们数据结构里一样,如果都是UDP,想要实现一个逆波兰表达式,放在一个报文里面还好,如果是TCP,是一个流,在这个流里面,如何将两次调用进行分界?什么时候是头,什么时候是尾?把这次的参数和上次的参数混了起来,TCP一端发送出去的数据,另外一端不一定能一下子全部读取出来。所以,怎么才算读完呢? + +问题三:如何表示数据? + +在这个简单的例子中,传递的就是一个固定长度的int值,这种情况还好,如果是变长的类型,是一个结构体,甚至是一个类,应该怎么办呢?如果是int,不同的平台上长度也不同,该怎么办呢? + +在网络上传输超过一个Byte的类型,还有大端Big Endian和小端Little Endian的问题。 + +假设我们要在32位四个Byte的一个空间存放整数1,很显然只要一个Byte放1,其他三个Byte放0就可以了。那问题是,最后一个Byte放1呢,还是第一个Byte放1呢?或者说1作为最低位,应该是放在32位的最后一个位置呢,还是放在第一个位置呢? + +最低位放在最后一个位置,叫作Little Endian,最低位放在第一个位置,叫作Big Endian。TCP/IP协议栈是按照Big Endian来设计的,而X86机器多按照Little Endian来设计的,因而发出去的时候需要做一个转换。 + +问题四:如何知道一个服务端都实现了哪些远程调用?从哪个端口可以访问这个远程调用? + +假设服务端实现了多个远程调用,每个可能实现在不同的进程中,监听的端口也不一样,而且由于服务端都是自己实现的,不可能使用一个大家都公认的端口,而且有可能多个进程部署在一台机器上,大家需要抢占端口,为了防止冲突,往往使用随机端口,那客户端如何找到这些监听的端口呢? + +问题五:发生了错误、重传、丢包、性能等问题怎么办? + +本地调用没有这个问题,但是一旦到网络上,这些问题都需要处理,因为网络是不可靠的,虽然在同一个连接中,我们还可通过TCP协议保证丢包、重传的问题,但是如果服务器崩溃了又重启,当前连接断开了,TCP就保证不了了,需要应用自己进行重新调用,重新传输会不会同样的操作做两遍,远程调用性能会不会受影响呢? + +协议约定问题 + +看到这么多问题,你是不是想起了我[第一节]讲过的这张图。 + + + +本地调用函数里有很多问题,比如词法分析、语法分析、语义分析等等,这些编译器本来都能帮你做了。但是在远程调用中,这些问题你都需要重新操心。 + +很多公司的解决方法是,弄一个核心通信组,里面都是Socket编程的大牛,实现一个统一的库,让其他业务组的人来调用,业务的人不需要知道中间传输的细节。通信双方的语法、语义、格式、端口、错误处理等,都需要调用方和被调用方开会协商,双方达成一致。一旦有一方改变,要及时通知对方,否则通信就会有问题。 + +可是不是每一个公司都有这种大牛团队,往往只有大公司才配得起,那有没有已经实现好的框架可以使用呢? + +当然有。一个大牛Bruce Jay Nelson写了一篇论文Implementing Remote Procedure Calls,定义了RPC的调用标准。后面所有RPC框架,都是按照这个标准模式来的。 + + + +当客户端的应用想发起一个远程调用时,它实际是通过本地调用本地调用方的Stub。它负责将调用的接口、方法和参数,通过约定的协议规范进行编码,并通过本地的RPCRuntime进行传输,将调用网络包发送到服务器。 + +服务器端的RPCRuntime收到请求后,交给提供方Stub进行解码,然后调用服务端的方法,服务端执行方法,返回结果,提供方Stub将返回结果编码后,发送给客户端,客户端的RPCRuntime收到结果,发给调用方Stub解码得到结果,返回给客户端。 + +这里面分了三个层次,对于用户层和服务端,都像是本地调用一样,专注于业务逻辑的处理就可以了。对于Stub层,处理双方约定好的语法、语义、封装、解封装。对于RPCRuntime,主要处理高性能的传输,以及网络的错误和异常。 + +最早的RPC的一种实现方式称为Sun RPC或ONC RPC。Sun公司是第一个提供商业化RPC库和 RPC编译器的公司。这个RPC框架是在NFS协议中使用的。 + +NFS(Network File System)就是网络文件系统。要使NFS成功运行,要启动两个服务端,一个是mountd,用来挂载文件路径;一个是nfsd,用来读写文件。NFS可以在本地mount一个远程的目录到本地的一个目录,从而本地的用户在这个目录里面写入、读出任何文件的时候,其实操作的是远程另一台机器上的文件。 + +操作远程和远程调用的思路是一样的,就像操作本地一样。所以NFS协议就是基于RPC实现的。当然无论是什么RPC,底层都是Socket编程。 + + + +XDR(External Data Representation,外部数据表示法)是一个标准的数据压缩格式,可以表示基本的数据类型,也可以表示结构体。 + +这里是几种基本的数据类型。 + + + +在RPC的调用过程中,所有的数据类型都要封装成类似的格式。而且RPC的调用和结果返回,也有严格的格式。 + + +XID唯一标识一对请求和回复。请求为0,回复为1。 + +RPC有版本号,两端要匹配RPC协议的版本号。如果不匹配,就会返回Deny,原因就是RPC_MISMATCH。 + +程序有编号。如果服务端找不到这个程序,就会返回PROG_UNAVAIL。 + +程序有版本号。如果程序的版本号不匹配,就会返回PROG_MISMATCH。 + +一个程序可以有多个方法,方法也有编号,如果找不到方法,就会返回PROC_UNAVAIL。 + +调用需要认证鉴权,如果不通过,则Deny。 + +最后是参数列表,如果参数无法解析,则返回GABAGE_ARGS。 + + + + +为了可以成功调用RPC,在客户端和服务端实现RPC的时候,首先要定义一个双方都认可的程序、版本、方法、参数等。 + + + +如果还是上面的加法,则双方约定为一个协议定义文件,同理如果是NFS、mount和读写,也会有类似的定义。 + +有了协议定义文件,ONC RPC会提供一个工具,根据这个文件生成客户端和服务器端的Stub程序。 + + + +最下层的是XDR文件,用于编码和解码参数。这个文件是客户端和服务端共享的,因为只有双方一致才能成功通信。 + +在客户端,会调用clnt_create创建一个连接,然后调用add_1,这是一个Stub函数,感觉是在调用本地一样。其实是这个函数发起了一个RPC调用,通过调用clnt_call来调用ONC RPC的类库,来真正发送请求。调用的过程非常复杂,一会儿我详细说这个。 + +当然服务端也有一个Stub程序,监听客户端的请求,当调用到达的时候,判断如果是add,则调用真正的服务端逻辑,也即将两个数加起来。 + +服务端将结果返回服务端的Stub,这个Stub程序发送结果给客户端,客户端的Stub程序正在等待结果,当结果到达客户端Stub,就将结果返回给客户端的应用程序,从而完成整个调用过程。 + +有了这个RPC的框架,前面五个问题中的前三个“如何规定远程调用的语法?”“如何传递参数?”以及“如何表示数据?”基本解决了,这三个问题我们统称为协议约定问题。 + +传输问题 + +但是错误、重传、丢包、性能等问题还没有解决,这些问题我们统称为传输问题。这个就不用Stub操心了,而是由ONC RPC的类库来实现。这是大牛们实现的,我们只要调用就可以了。 + + + +在这个类库中,为了解决传输问题,对于每一个客户端,都会创建一个传输管理层,而每一次RPC调用,都会是一个任务,在传输管理层,你可以看到熟悉的队列机制、拥塞窗口机制等。 + +由于在网络传输的时候,经常需要等待,因而同步的方式往往效率比较低,因而也就有Socket的异步模型。为了能够异步处理,对于远程调用的处理,往往是通过状态机来实现的。只有当满足某个状态的时候,才进行下一步,如果不满足状态,不是在那里等,而是将资源留出来,用来处理其他的RPC调用。 + + + +从这个图可以看出,这个状态转换图还是很复杂的。 + +首先,进入起始状态,查看RPC的传输层队列中有没有空闲的位置,可以处理新的RPC任务。如果没有,说明太忙了,或直接结束或重试。如果申请成功,就可以分配内存,获取服务的端口号,然后连接服务器。 + +连接的过程要有一段时间,因而要等待连接的结果,会有连接失败,或直接结束或重试。如果连接成功,则开始发送RPC请求,然后等待获取RPC结果,这个过程也需要一定的时间;如果发送出错,可以重新发送;如果连接断了,可以重新连接;如果超时,可以重新传输;如果获取到结果,就可以解码,正常结束。 + +这里处理了连接失败、重试、发送失败、超时、重试等场景。不是大牛真写不出来,因而实现一个RPC的框架,其实很有难度。 + +服务发现问题 + +传输问题解决了,我们还遗留一个问题,就是问题四“如何找到RPC服务端的那个随机端口”。这个问题我们称为服务发现问题。在ONC RPC中,服务发现是通过portmapper实现的。 + + + +portmapper会启动在一个众所周知的端口上,RPC程序由于是用户自己写的,会监听在一个随机端口上,但是RPC程序启动的时候,会向portmapper注册。客户端要访问RPC服务端这个程序的时候,首先查询portmapper,获取RPC服务端程序的随机端口,然后向这个随机端口建立连接,开始RPC调用。从图中可以看出,mount命令的RPC调用,就是这样实现的。 + +小结 + +好了,这一节就到这里,我们来总结一下。 + + +远程调用看起来用Socket编程就可以了,其实是很复杂的,要解决协议约定问题、传输问题和服务发现问题。 + +大牛Bruce Jay Nelson的论文、早期ONC RPC框架,以及NFS的实现,给出了解决这三大问题的示范性实现,也即协议约定要公用协议描述文件,并通过这个文件生成Stub程序;RPC的传输一般需要一个状态机,需要另外一个进程专门做服务发现。 + + +最后,给你留两个思考题。 + + +在这篇文章中,mount的过程是通过系统调用,最终调用到RPC层。一旦mount完毕之后,客户端就像写入本地文件一样写入NFS了,这个过程是如何触发RPC层的呢? + +ONC RPC是早期的RPC框架,你觉得它有哪些问题呢? + + +我们的专栏更新到第32讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/33基于XML的SOAP协议:不要说NBA,请说美国职业篮球联赛.md b/专栏/趣谈网络协议/33基于XML的SOAP协议:不要说NBA,请说美国职业篮球联赛.md new file mode 100644 index 0000000..fe22d26 --- /dev/null +++ b/专栏/趣谈网络协议/33基于XML的SOAP协议:不要说NBA,请说美国职业篮球联赛.md @@ -0,0 +1,198 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 33 基于XML的SOAP协议:不要说NBA,请说美国职业篮球联赛 + 上一节我们讲了RPC的经典模型和设计要点,并用最早期的ONC RPC为例子,详述了具体的实现。 + +ONC RPC存在哪些问题? + +ONC RPC将客户端要发送的参数,以及服务端要发送的回复,都压缩为一个二进制串,这样固然能够解决双方的协议约定问题,但是存在一定的不方便。 + +首先,需要双方的压缩格式完全一致,一点都不能差。一旦有少许的差错,多一位,少一位或者错一位,都可能造成无法解压缩。当然,我们可以用传输层的可靠性以及加入校验值等方式,来减少传输过程中的差错。 + +其次,协议修改不灵活。如果不是传输过程中造成的差错,而是客户端因为业务逻辑的改变,添加或者删除了字段,或者服务端添加或者删除了字段,而双方没有及时通知,或者线上系统没有及时升级,就会造成解压缩不成功。 + +因而,当业务发生改变,需要多传输一些参数或者少传输一些参数的时候,都需要及时通知对方,并且根据约定好的协议文件重新生成双方的Stub程序。自然,这样灵活性比较差。 + +如果仅仅是沟通的问题也还好解决,其实更难弄的还有版本的问题。比如在服务端提供一个服务,参数的格式是版本一的,已经有50个客户端在线上调用了。现在有一个客户端有个需求,要加一个字段,怎么办呢?这可是一个大工程,所有的客户端都要适配这个,需要重新写程序,加上这个字段,但是传输值是0,不需要这个字段的客户端很“冤”,本来没我啥事儿,为啥让我也忙活? + +最后,ONC RPC的设计明显是面向函数的,而非面向对象。而当前面向对象的业务逻辑设计与实现方式已经成为主流。 + +这一切的根源就在于压缩。这就像平时我们爱用缩略语。如果是篮球爱好者,你直接说NBA,他马上就知道什么意思,但是如果你给一个大妈说NBA,她可能就不知所云。 + +所以,这种RPC框架只能用于客户端和服务端全由一拨人开发的场景,或者至少客户端和服务端的开发人员要密切沟通,相互合作,有大量的共同语言,才能按照既定的协议顺畅地进行工作。 + +XML与SOAP + +但是,一般情况下,我们做一个服务,都是要提供给陌生人用的,你和客户不会经常沟通,也没有什么共同语言。就像你给别人介绍NBA,你要说美国职业篮球赛,这样不管他是干啥的,都能听得懂。 + +放到我们的场景中,对应的就是用文本类的方式进行传输。无论哪个客户端获得这个文本,都能够知道它的意义。 + +一种常见的文本类格式是XML。我们这里举个例子来看。 + + + + + 2018-07-01 + 趣谈网络协议 + 刘超 + 68 + + + + +我这里不准备详细讲述XML的语法规则,但是你相信我,看完下面的内容,即便你没有学过XML,也能一看就懂,这段XML描述的是什么,不像全面的二进制,你看到的都是010101,不知所云。 + +有了这个,刚才我们说的那几个问题就都不是问题了。 + +首先,格式没必要完全一致。比如如果我们把price和author换个位置,并不影响客户端和服务端解析这个文本,也根本不会误会,说这个作者的名字叫68。 + +如果有的客户端想增加一个字段,例如添加一个推荐人字段,只需要在上面的文件中加一行: + + Gary + + +对于不需要这个字段的客户端,只要不解析这一行就是了。只要用简单的处理,就不会出现错误。 + +另外,这种表述方式显然是描述一个订单对象的,是一种面向对象的、更加接近用户场景的表示方式。 + +既然XML这么好,接下来我们来看看怎么把它用在RPC中。 + +传输协议问题 + +我们先解决第一个,传输协议的问题。 + +基于XML的最著名的通信协议就是SOAP了,全称简单对象访问协议(Simple Object Access Protocol)。它使用XML编写简单的请求和回复消息,并用HTTP协议进行传输。 + +SOAP将请求和回复放在一个信封里面,就像传递一个邮件一样。信封里面的信分抬头和正文。 + +POST /purchaseOrder HTTP/1.1 +Host: www.geektime.com +Content-Type: application/soap+xml; charset=utf-8 +Content-Length: nnn + + + + + + 1234 + + + + + + 2018-07-01 + 趣谈网络协议 + 刘超 + 68 + + + + + + +HTTP协议我们学过,这个请求使用POST方法,发送一个格式为 application/soap + xml 的XML正文给 www.geektime.com,从而下一个单,这个订单封装在SOAP的信封里面,并且表明这是一笔交易(transaction),而且订单的详情都已经写明了。 + +协议约定问题 + +接下来我们解决第二个问题,就是双方的协议约定是什么样的? + +因为服务开发出来是给陌生人用的,就像上面下单的那个XML文件,对于客户端来说,它如何知道应该拼装成上面的格式呢?这就需要对于服务进行描述,因为调用的人不认识你,所以没办法找到你,问你的服务应该如何调用。 + +当然你可以写文档,然后放在官方网站上,但是你的文档不一定更新得那么及时,而且你也写的文档也不一定那么严谨,所以常常会有调试不成功的情况。因而,我们需要一种相对比较严谨的Web服务描述语言,WSDL(Web Service Description Languages)。它也是一个XML文件。 + +在这个文件中,要定义一个类型order,与上面的XML对应起来。 + + + + + + + + + + + + + +接下来,需要定义一个message的结构。 + + + + + + +接下来,应该暴露一个端口。 + + + + + + + + + +然后,我们来编写一个binding,将上面定义的信息绑定到SOAP请求的body里面。 + + + + + + + + + + + + + + +最后,我们需要编写service。 + + + + + + + + +WSDL还是有些复杂的,不过好在有工具可以生成。 + +对于某个服务,哪怕是一个陌生人,都可以通过在服务地址后面加上“?wsdl”来获取到这个文件,但是这个文件还是比较复杂,比较难以看懂。不过好在也有工具可以根据WSDL生成客户端Stub,让客户端通过Stub进行远程调用,就跟调用本地的方法一样。 + +服务发现问题 + +最后解决第三个问题,服务发现问题。 + +这里有一个UDDI(Universal Description, Discovery, and Integration),也即统一描述、发现和集成协议。它其实是一个注册中心,服务提供方可以将上面的WSDL描述文件,发布到这个注册中心,注册完毕后,服务使用方可以查找到服务的描述,封装为本地的客户端进行调用。 + +小结 + +好了,这一节就到这里了,我们来总结一下。 + + +原来的二进制RPC有很多缺点,格式要求严格,修改过于复杂,不面向对象,于是产生了基于文本的调用方式——基于XML的SOAP。 + +SOAP有三大要素:协议约定用WSDL、传输协议用HTTP、服务发现用UDDL。 + + +最后,给你留两个思考题: + + +对于HTTP协议来讲,有多种方法,但是SOAP只用了POST,这样会有什么问题吗? + +基于文本的RPC虽然解决了二进制的问题,但是SOAP还是有点复杂,还有一种更便捷的接口规则,你知道是什么吗? + + +我们的专栏更新到第33讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/34基于JSON的RESTful接口协议:我不关心过程,请给我结果.md b/专栏/趣谈网络协议/34基于JSON的RESTful接口协议:我不关心过程,请给我结果.md new file mode 100644 index 0000000..8069950 --- /dev/null +++ b/专栏/趣谈网络协议/34基于JSON的RESTful接口协议:我不关心过程,请给我结果.md @@ -0,0 +1,142 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 34 基于JSON的RESTful接口协议:我不关心过程,请给我结果 + 上一节我们讲了基于XML的SOAP协议,SOAP的S是啥意思来着?是Simple,但是好像一点儿都不简单啊! + +你会发现,对于SOAP来讲,无论XML中调用的是什么函数,多是通过HTTP的POST方法发送的。但是咱们原来学HTTP的时候,我们知道HTTP除了POST,还有PUT、DELETE、GET等方法,这些也可以代表一个个动作,而且基本满足增、删、查、改的需求,比如增是POST,删是DELETE,查是GET,改是PUT。 + +传输协议问题 + +对于SOAP来讲,比如我创建一个订单,用POST,在XML里面写明动作是CreateOrder;删除一个订单,还是用POST,在XML里面写明了动作是DeleteOrder。其实创建订单完全可以使用POST动作,然后在XML里面放一个订单的信息就可以了,而删除用DELETE动作,然后在XML里面放一个订单的ID就可以了。 + +于是上面的那个SOAP就变成下面这个简单的模样。 + +POST /purchaseOrder HTTP/1.1 +Host: www.geektime.com +Content-Type: application/xml; charset=utf-8 +Content-Length: nnn + + + + 2018-07-01 + 趣谈网络协议 + 刘超 + 68 + + + +而且XML的格式也可以改成另外一种简单的文本化的对象表示格式JSON。 + +POST /purchaseOrder HTTP/1.1 +Host: www.geektime.com +Content-Type: application/json; charset=utf-8 +Content-Length: nnn + +{ + "order": { + "date": "2018-07-01", + "className": "趣谈网络协议", + "Author": "刘超", + "price": "68" + } +} + + +经常写Web应用的应该已经发现,这就是RESTful格式的API的样子。 + +协议约定问题 + +然而RESTful可不仅仅是指API,而是一种架构风格,全称Representational State Transfer,表述性状态转移,来自一篇重要的论文《架构风格与基于网络的软件架构设计》(Architectural Styles and the Design of Network-based Software Architectures)。 + +这篇文章从深层次,更加抽象地论证了一个互联网应用应该有的设计要点,而这些设计要点,成为后来我们能看到的所有高并发应用设计都必须要考虑的问题,再加上REST API比较简单直接,所以后来几乎成为互联网应用的标准接口。 + +因此,和SOAP不一样,REST不是一种严格规定的标准,它其实是一种设计风格。如果按这种风格进行设计,RESTful接口和SOAP接口都能做到,只不过后面的架构是REST倡导的,而SOAP相对比较关注前面的接口。 + +而且由于能够通过WSDL生成客户端的Stub,因而SOAP常常被用于类似传统的RPC方式,也即调用远端和调用本地是一样的。 + +然而本地调用和远程跨网络调用毕竟不一样,这里的不一样还不仅仅是因为有网络而导致的客户端和服务端的分离,从而带来的网络性能问题。更重要的问题是,客户端和服务端谁来维护状态。所谓的状态就是对某个数据当前处理到什么程度了。 + +这里举几个例子,例如,我浏览到哪个目录了,我看到第几页了,我要买个东西,需要扣减一下库存,这些都是状态。本地调用其实没有人纠结这个问题,因为数据都在本地,谁处理都一样,而且一边处理了,另一边马上就能看到。 + +当有了RPC之后,我们本来期望对上层透明,就像上一节说的“远在天边,尽在眼前”。于是使用RPC的时候,对于状态的问题也没有太多的考虑。 + +就像NFS一样,客户端会告诉服务端,我要进入哪个目录,服务端必须要为某个客户端维护一个状态,就是当前这个客户端浏览到哪个目录了。例如,客户端输入cd hello,服务端要在某个地方记住,上次浏览到/root/liuchao了,因而客户的这次输入,应该给它显示/root/liuchao/hello下面的文件列表。而如果有另一个客户端,同样输入cd hello,服务端也在某个地方记住,上次浏览到/var/lib,因而要给客户显示的是/var/lib/hello。 + +不光NFS,如果浏览翻页,我们经常要实现函数next(),在一个列表中取下一页,但是这就需要服务端记住,客户端A上次浏览到20~30页了,那它调用next(),应该显示30~40页,而客户端B上次浏览到100~110页了,调用next()应该显示110~120页。 + +上面的例子都是在RPC场景下,由服务端来维护状态,很多SOAP接口设计的时候,也常常按这种模式。这种模式原来没有问题,是因为客户端和服务端之间的比例没有失衡。因为一般不会同时有太多的客户端同时连上来,所以NFS还能把每个客户端的状态都记住。 + +公司内部使用的ERP系统,如果使用SOAP的方式实现,并且服务端为每个登录的用户维护浏览到报表那一页的状态,由于一个公司内部的人也不会太多,把ERP放在一个强大的物理机上,也能记得过来。 + +但是互联网场景下,客户端和服务端就彻底失衡了。你可以想象“双十一”,多少人同时来购物,作为服务端,它能记得过来吗?当然不可能,只好多个服务端同时提供服务,大家分担一下。但是这就存在一个问题,服务端怎么把自己记住的客户端状态告诉另一个服务端呢?或者说,你让我给你分担工作,你也要把工作的前因后果给我说清楚啊! + +那服务端索性就要想了,既然这么多客户端,那大家就分分工吧。服务端就只记录资源的状态,例如文件的状态,报表的状态,库存的状态,而客户端自己维护自己的状态。比如,你访问到哪个目录了啊,报表的哪一页了啊,等等。 + +这样对于API也有影响,也就是说,当客户端维护了自己的状态,就不能这样调用服务端了。例如客户端说,我想访问当前目录下的hello路径。服务端说,我怎么知道你的当前路径。所以客户端要先看看自己当前路径是/root/liuchao,然后告诉服务端说,我想访问/root/liuchao/hello路径。 + +再比如,客户端说我想访问下一页,服务端说,我怎么知道你当前访问到哪一页了。所以客户端要先看看自己访问到了100~110页,然后告诉服务器说,我想访问110~120页。 + +这就是服务端的无状态化。这样服务端就可以横向扩展了,一百个人一起服务,不用交接,每个人都能处理。 + +所谓的无状态,其实是服务端维护资源的状态,客户端维护会话的状态。对于服务端来讲,只有资源的状态改变了,客户端才调用POST、PUT、DELETE方法来找我;如果资源的状态没变,只是客户端的状态变了,就不用告诉我了,对于我来说都是统一的GET。 + +虽然这只改进了GET,但是已经带来了很大的进步。因为对于互联网应用,大多数是读多写少的。而且只要服务端的资源状态不变,就给了我们缓存的可能。例如可以将状态缓存到接入层,甚至缓存到CDN的边缘节点,这都是资源状态不变的好处。 + +按照这种思路,对于API的设计,就慢慢变成了以资源为核心,而非以过程为核心。也就是说,客户端只要告诉服务端你想让资源状态最终变成什么样就可以了,而不用告诉我过程,不用告诉我动作。 + +还是文件目录的例子。客户端应该访问哪个绝对路径,而非一个动作,我就要进入某个路径。再如,库存的调用,应该查看当前的库存数目,然后减去购买的数量,得到结果的库存数。这个时候应该设置为目标库存数(但是当前库存数要匹配),而非告知减去多少库存。 + +这种API的设计需要实现幂等,因为网络不稳定,就会经常出错,因而需要重试,但是一旦重试,就会存在幂等的问题,也就是同一个调用,多次调用的结果应该一样,不能一次支付调用,因为调用三次变成了支付三次。不能进入cd a,做了三次,就变成了cd a/a/a。也不能扣减库存,调用了三次,就扣减三次库存。 + +当然按照这种设计模式,无论RESTful API还是SOAP API都可以将架构实现成无状态的,面向资源的、幂等的、横向扩展的、可缓存的。 + +但是SOAP的XML正文中,是可以放任何动作的。例如XML里面可以写< ADD >,< MINUS >等。这就方便使用SOAP的人,将大量的动作放在API里面。 + +RESTful没这么复杂,也没给客户提供这么多的可能性,正文里的JSON基本描述的就是资源的状态,没办法描述动作,而且能够出发的动作只有CRUD,也即POST、GET、PUT、DELETE,也就是对于状态的改变。 + +所以,从接口角度,就让你死了这条心。当然也有很多技巧的方法,在使用RESTful API的情况下,依然提供基于动作的有状态请求,这属于反模式了。 + +服务发现问题 + +对于RESTful API来讲,我们已经解决了传输协议的问题——基于HTTP,协议约定问题——基于JSON,最后要解决的是服务发现问题。 + +有个著名的基于RESTful API的跨系统调用框架叫Spring Cloud。在Spring Cloud中有一个组件叫 Eureka。传说,阿基米德在洗澡时发现浮力原理,高兴得来不及穿上裤子,跑到街上大喊:“Eureka(我找到了)!”所以Eureka是用来实现注册中心的,负责维护注册的服务列表。 + +服务分服务提供方,它向Eureka做服务注册、续约和下线等操作,注册的主要数据包括服务名、机器IP、端口号、域名等等。 + +另外一方是服务消费方,向Eureka获取服务提供方的注册信息。为了实现负载均衡和容错,服务提供方可以注册多个。 + +当消费方要调用服务的时候,会从注册中心读出多个服务来,那怎么调用呢?当然是RESTful方式了。 + +Spring Cloud提供一个RestTemplate工具,用于将请求对象转换为JSON,并发起Rest调用,RestTemplate的调用也是分POST、PUT、GET、 DELETE的,当结果返回的时候,根据返回的JSON解析成对象。 + +通过这样封装,调用起来也很方便。 + +小结 + +好了,这一节就到这里了,我们来总结一下。 + + +SOAP过于复杂,而且设计是面向动作的,因而往往因为架构问题导致并发量上不去。 + +RESTful不仅仅是一个API,而且是一种架构模式,主要面向资源,提供无状态服务,有利于横向扩展应对高并发。 + + +最后,给你留两个思考题: + + +在讨论RESTful模型的时候,举了一个库存的例子,但是这种方法有很大问题,那你知道为什么要这样设计吗? + +基于文本的RPC虽然解决了二进制的问题,但是它本身也有问题,你能举出一些例子吗? + + +我们的专栏更新到第34讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/35二进制类RPC协议:还是叫NBA吧,总说全称多费劲.md b/专栏/趣谈网络协议/35二进制类RPC协议:还是叫NBA吧,总说全称多费劲.md new file mode 100644 index 0000000..104e85e --- /dev/null +++ b/专栏/趣谈网络协议/35二进制类RPC协议:还是叫NBA吧,总说全称多费劲.md @@ -0,0 +1,198 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 35 二进制类RPC协议:还是叫NBA吧,总说全称多费劲 + 前面我们讲了两个常用文本类的RPC协议,对于陌生人之间的沟通,用NBA、CBA这样的缩略语,会使得协议约定非常不方便。 + +在讲CDN和DNS的时候,我们讲过接入层的设计,对于静态资源或者动态资源静态化的部分都可以做缓存。但是对于下单、支付等交易场景,还是需要调用API。 + +对于微服务的架构,API需要一个API网关统一的管理。API网关有多种实现方式,用Nginx或者OpenResty结合Lua脚本是常用的方式。在上一节讲过的Spring Cloud体系中,有个组件Zuul也是干这个的。 + +数据中心内部是如何相互调用的? + +API网关用来管理API,但是API的实现一般在一个叫作Controller层的地方。这一层对外提供API。由于是让陌生人访问的,我们能看到目前业界主流的,基本都是RESTful的API,是面向大规模互联网应用的。 + + + +在Controller之内,就是咱们互联网应用的业务逻辑实现。上节讲RESTful的时候,说过业务逻辑的实现最好是无状态的,从而可以横向扩展,但是资源的状态还需要服务端去维护。资源的状态不应该维护在业务逻辑层,而是在最底层的持久化层,一般会使用分布式数据库和ElasticSearch。 + +这些服务端的状态,例如订单、库存、商品等,都是重中之重,都需要持久化到硬盘上,数据不能丢,但是由于硬盘读写性能差,因而持久化层往往吞吐量不能达到互联网应用要求的吞吐量,因而前面要有一层缓存层,使用Redis或者memcached将请求拦截一道,不能让所有的请求都进入数据库“中军大营”。 + +缓存和持久化层之上一般是基础服务层,这里面提供一些原子化的接口。例如,对于用户、商品、订单、库存的增删查改,将缓存和数据库对再上层的业务逻辑屏蔽一道。有了这一层,上层业务逻辑看到的都是接口,而不会调用数据库和缓存。因而对于缓存层的扩容,数据库的分库分表,所有的改变,都截止到这一层,这样有利于将来对于缓存和数据库的运维。 + +再往上就是组合层。因为基础服务层只是提供简单的接口,实现简单的业务逻辑,而复杂的业务逻辑,比如下单,要扣优惠券,扣减库存等,就要在组合服务层实现。 + +这样,Controller层、组合服务层、基础服务层就会相互调用,这个调用是在数据中心内部的,量也会比较大,还是使用RPC的机制实现的。 + +由于服务比较多,需要一个单独的注册中心来做服务发现。服务提供方会将自己提供哪些服务注册到注册中心中去,同时服务消费方订阅这个服务,从而可以对这个服务进行调用。 + +调用的时候有一个问题,这里的RPC调用,应该用二进制还是文本类?其实文本的最大问题是,占用字节数目比较多。比如数字123,其实本来二进制8位就够了,但是如果变成文本,就成了字符串123。如果是UTF-8编码的话,就是三个字节;如果是UTF-16,就是六个字节。同样的信息,要多费好多的空间,传输起来也更加占带宽,时延也高。 + +因而对于数据中心内部的相互调用,很多公司选型的时候,还是希望采用更加省空间和带宽的二进制的方案。 + +这里一个著名的例子就是Dubbo服务化框架二进制的RPC方式。 + + + +Dubbo会在客户端的本地启动一个Proxy,其实就是客户端的Stub,对于远程的调用都通过这个Stub进行封装。 + +接下来,Dubbo会从注册中心获取服务端的列表,根据路由规则和负载均衡规则,在多个服务端中选择一个最合适的服务端进行调用。 + +调用服务端的时候,首先要进行编码和序列化,形成Dubbo头和序列化的方法和参数。将编码好的数据,交给网络客户端进行发送,网络服务端收到消息后,进行解码。然后将任务分发给某个线程进行处理,在线程中会调用服务端的代码逻辑,然后返回结果。 + +这个过程和经典的RPC模式何其相似啊! + +如何解决协议约定问题? + +接下来我们还是来看RPC的三大问题,其中注册发现问题已经通过注册中心解决了。下面我们就来看协议约定问题。 + +Dubbo中默认的RPC协议是Hessian2。为了保证传输的效率,Hessian2将远程调用序列化为二进制进行传输,并且可以进行一定的压缩。这个时候你可能会疑惑,同为二进制的序列化协议,Hessian2和前面的二进制的RPC有什么区别呢?这不绕了一圈又回来了吗? + +Hessian2是解决了一些问题的。例如,原来要定义一个协议文件,然后通过这个文件生成客户端和服务端的Stub,才能进行相互调用,这样使得修改就会不方便。Hessian2不需要定义这个协议文件,而是自描述的。什么是自描述呢? + +所谓自描述就是,关于调用哪个函数,参数是什么,另一方不需要拿到某个协议文件、拿到二进制,靠它本身根据Hessian2的规则,就能解析出来。 + +原来有协议文件的场景,有点儿像两个人事先约定好,0表示方法add,然后后面会传两个数。服务端把两个数加起来,这样一方发送012,另一方知道是将1和2加起来,但是不知道协议文件的,当它收到012的时候,完全不知道代表什么意思。 + +而自描述的场景,就像两个人说的每句话都带前因后果。例如,传递的是“函数:add,第一个参数1,第二个参数2”。这样无论谁拿到这个表述,都知道是什么意思。但是只不过都是以二进制的形式编码的。这其实相当于综合了XML和二进制共同优势的一个协议。 + +Hessian2是如何做到这一点的呢?这就需要去看Hessian2的序列化的语法描述文件。 + + + +看起来很复杂,编译原理里面是有这样的语法规则的。 + +我们从Top看起,下一层是value,直到形成一棵树。这里面的有个思想,为了防止歧义,每一个类型的起始数字都设置成为独一无二的。这样,解析的时候,看到这个数字,就知道后面跟的是什么了。 + +这里还是以加法为例子,“add(2,3)”被序列化之后是什么样的呢? + +H x02 x00 # Hessian 2.0 +C # RPC call + x03 add # method "add" + x92 # two arguments + x92 # 2 - argument 1 + x93 # 3 - argument 2 + + + +H开头,表示使用的协议是Hession,H的二进制是0x48。 + +C开头,表示这是一个RPC调用。 + +0x03,表示方法名是三个字符。 + +0x92,表示有两个参数。其实这里存的应该是2,之所以加上0x90,就是为了防止歧义,表示这里一定是一个int。 + +第一个参数是2,编码为0x92,第二个参数是3,编码为0x93。 + + +这个就叫作自描述。 + +另外,Hessian2是面向对象的,可以传输一个对象。 + +class Car { + String color; + String model; +} +out.writeObject(new Car("red", "corvette")); +out.writeObject(new Car("green", "civic")); +--- +C # object definition (#0) + x0b example.Car # type is example.Car + x92 # two fields + x05 color # color field name + x05 model # model field name + +O # object def (long form) + x90 # object definition #0 + x03 red # color field value + x08 corvette # model field value + +x60 # object def #0 (short form) + x05 green # color field value + x05 civic # model field value + + +首先,定义这个类。对于类型的定义也传过去,因而也是自描述的。类名为example.Car,字符长11位,因而前面长度为0x0b。有两个成员变量,一个是color,一个是model,字符长5位,因而前面长度0x05,。 + +然后,传输的对象引用这个类。由于类定义在位置0,因而对象会指向这个位置0,编码为0x90。后面red和corvette是两个成员变量的值,字符长分别为3和8。 + +接着又传输一个属于相同类的对象。这时候就不保存对于类的引用了,只保存一个0x60,表示同上就可以了。 + +可以看出,Hessian2真的是能压缩尽量压缩,多一个Byte都不传。 + +如何解决RPC传输问题? + +接下来,我们再来看Dubbo的RPC传输问题。前面我们也说了,基于Socket实现一个高性能的服务端,是很复杂的一件事情,在Dubbo里面,使用了Netty的网络传输框架。 + +Netty是一个非阻塞的基于事件的网络传输框架,在服务端启动的时候,会监听一个端口,并注册以下的事件。 + + +连接事件:当收到客户端的连接事件时,会调用void connected(Channel channel) 方法。 + +当可写事件触发时,会调用void sent(Channel channel, Object message),服务端向客户端返回响应数据。 + +当可读事件触发时,会调用void received(Channel channel, Object message) ,服务端在收到客户端的请求数据。 + +当发生异常时,会调用void caught(Channel channel, Throwable exception)。 + + +当事件触发之后,服务端在这些函数中的逻辑,可以选择直接在这个函数里面进行操作,还是将请求分发到线程池去处理。一般异步的数据读写都需要另外的线程池参与,在线程池中会调用真正的服务端业务代码逻辑,返回结果。 + +Hessian2是Dubbo默认的RPC序列化方式,当然还有其他选择。例如,Dubbox从Spark那里借鉴Kryo,实现高性能的序列化。 + +到这里,我们说了数据中心里面的相互调用。为了高性能,大家都愿意用二进制,但是为什么后期Spring Cloud又兴起了呢?这是因为,并发量越来越大,已经到了微服务的阶段。同原来的SOA不同,微服务粒度更细,模块之间的关系更加复杂。 + +在上面的架构中,如果使用二进制的方式进行序列化,虽然不用协议文件来生成Stub,但是对于接口的定义,以及传的对象DTO,还是需要共享JAR。因为只有客户端和服务端都有这个JAR,才能成功地序列化和反序列化。 + +但当关系复杂的时候,JAR的依赖也变得异常复杂,难以维护,而且如果在DTO里加一个字段,双方的JAR没有匹配好,也会导致序列化不成功,而且还有可能循环依赖。这个时候,一般有两种选择。 + +第一种,建立严格的项目管理流程。 + + +不允许循环调用,不允许跨层调用,只准上层调用下层,不允许下层调用上层。 + +接口要保持兼容性,不兼容的接口新添加而非改原来的,当接口通过监控,发现不用的时候,再下掉。 + +升级的时候,先升级服务提供端,再升级服务消费端。 + + +第二种,改用RESTful的方式。 + + +使用Spring Cloud,消费端和提供端不用共享JAR,各声明各的,只要能变成JSON就行,而且JSON也是比较灵活的。 + +使用RESTful的方式,性能会降低,所以需要通过横向扩展来抵消单机的性能损耗。 + + +这个时候,就看架构师的选择喽! + +小结 + +好了,这节就到这里了,我们来总结一下。 + + +RESTful API对于接入层和Controller层之外的调用,已基本形成事实标准,但是随着内部服务之间的调用越来越多,性能也越来越重要,于是Dubbo的RPC框架有了用武之地。 + +Dubbo通过注册中心解决服务发现问题,通过Hessian2序列化解决协议约定的问题,通过Netty解决网络传输的问题。 + +在更加复杂的微服务场景下,Spring Cloud的RESTful方式在内部调用也会被考虑,主要是JAR包的依赖和管理问题。 + + +最后,给你留两个思考题。 + + +对于微服务模式下的RPC框架的选择,Dubbo和SpringCloud各有优缺点,你能做个详细的对比吗? + +到目前为止,我们讲过的RPC,还没有跨语言调用的场景,你知道如果跨语言应该怎么办吗? + + +我们的专栏更新到第35讲,不知你掌握得如何?每节课后我留的思考题,你都有没有认真思考,并在留言区写下答案呢?我会从已发布的文章中选出一批认真留言的同学,赠送学习奖励礼券和我整理的独家网络协议知识图谱。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/36跨语言类RPC协议:交流之前,双方先来个专业术语表.md b/专栏/趣谈网络协议/36跨语言类RPC协议:交流之前,双方先来个专业术语表.md new file mode 100644 index 0000000..ccd7d13 --- /dev/null +++ b/专栏/趣谈网络协议/36跨语言类RPC协议:交流之前,双方先来个专业术语表.md @@ -0,0 +1,205 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 36 跨语言类RPC协议:交流之前,双方先来个专业术语表 + 到目前为止,咱们讲了四种RPC,分别是ONC RPC、基于XML的SOAP、基于JSON的RESTful和Hessian2。 + +通过学习,我们知道,二进制的传输性能好,文本类的传输性能差一些;二进制的难以跨语言,文本类的可以跨语言;要写协议文件的严谨一些,不写协议文件的灵活一些。虽然都有服务发现机制,有的可以进行服务治理,有的则没有。 + +我们也看到了RPC从最初的客户端服务器模式,最终演进到微服务。对于RPC框架的要求越来越多了,具体有哪些要求呢? + + +首先,传输性能很重要。因为服务之间的调用如此频繁了,还是二进制的越快越好。 + +其次,跨语言很重要。因为服务多了,什么语言写成的都有,而且不同的场景适宜用不同的语言,不能一个语言走到底。 + +最好既严谨又灵活,添加个字段不用重新编译和发布程序。 + +最好既有服务发现,也有服务治理,就像Dubbo和Spring Cloud一样。 + + +Protocol Buffers + +这是要多快好省地建设社会主义啊。理想还是要有的嘛,这里我就来介绍一个向“理想”迈进的GRPC。 + +GRPC首先满足二进制和跨语言这两条,二进制说明压缩效率高,跨语言说明更灵活。但是又是二进制,又是跨语言,这就相当于两个人沟通,你不但说方言,还说缩略语,人家怎么听懂呢?所以,最好双方弄一个协议约定文件,里面规定好双方沟通的专业术语,这样沟通就顺畅多了。 + +对于GRPC来讲,二进制序列化协议是Protocol Buffers。首先,需要定义一个协议文件.proto。 + +我们还看买极客时间专栏的这个例子。 + +syntax = “proto3”; +package com.geektime.grpc +option java_package = “com.geektime.grpc”; +message Order { + required string date = 1; + required string classname = 2; + required string author = 3; + required int price = 4; +} + +message OrderResponse { + required string message = 1; +} + +service PurchaseOrder { + rpc Purchase (Order) returns (OrderResponse) {} +} + + +在这个协议文件中,我们首先指定使用proto3的语法,然后我们使用Protocol Buffers的语法,定义两个消息的类型,一个是发出去的参数,一个是返回的结果。里面的每一个字段,例如date、classname、author、price都有唯一的一个数字标识,这样在压缩的时候,就不用传输字段名称了,只传输这个数字标识就行了,能节省很多空间。 + +最后定义一个Service,里面会有一个RPC调用的声明。 + +无论使用什么语言,都有相应的工具生成客户端和服务端的Stub程序,这样客户端就可以像调用本地一样,调用远程的服务了。 + +协议约定问题 + +Protocol Buffers是一款压缩效率极高的序列化协议,有很多设计精巧的序列化方法。 + +对于int类型32位的,一般都需要4个Byte进行存储。在Protocol Buffers中,使用的是变长整数的形式。对于每一个Byte的8位,最高位都有特殊的含义。 + +如果该位为 1,表示这个数字没完,后续的Byte也属于这个数字;如果该位为 0,则这个数字到此结束。其他的7个Bit才是用来表示数字的内容。因此,小于128的数字都可以用一个Byte表示;大于128的数字,比如130,会用两个字节来表示。 + +对于每一个字段,使用的是TLV(Tag,Length,Value)的存储办法。 + +其中Tag = (field_num << 3) | wire_type。field_num就是在proto文件中,给每个字段指定唯一的数字标识,而wire_type用于标识后面的数据类型。 + + + +例如,对于string author = 3,在这里field_num为3,string的wire_type为2,于是 (field_num << 3) | wire_type = (11000) | 10 = 11010 = 26;接下来是Length,最后是Value为“liuchao”,如果使用UTF-8编码,长度为7个字符,因而Length为7。 + +可见,在序列化效率方面,Protocol Buffers简直做到了极致。 + +在灵活性方面,这种基于协议文件的二进制压缩协议往往存在更新不方便的问题。例如,客户端和服务器因为需求的改变需要添加或者删除字段。 + +这一点上,Protocol Buffers考虑了兼容性。在上面的协议文件中,每一个字段都有修饰符。比如: + + +required:这个值不能为空,一定要有这么一个字段出现; + +optional:可选字段,可以设置,也可以不设置,如果不设置,则使用默认值; + +repeated:可以重复0到多次。 + + +如果我们想修改协议文件,对于赋给某个标签的数字,例如string author=3,这个就不要改变了,改变了就不认了;也不要添加或者删除required字段,因为解析的时候,发现没有这个字段就会报错。对于optional和repeated字段,可以删除,也可以添加。这就给了客户端和服务端升级的可能性。 + +例如,我们在协议里面新增一个string recommended字段,表示这个课程是谁推荐的,就将这个字段设置为optional。我们可以先升级服务端,当客户端发过来消息的时候,是没有这个值的,将它设置为一个默认值。我们也可以先升级客户端,当客户端发过来消息的时候,是有这个值的,那它将被服务端忽略。 + +至此,我们解决了协议约定的问题。 + +网络传输问题 + +接下来,我们来看网络传输的问题。 + +如果是Java技术栈,GRPC的客户端和服务器之间通过Netty Channel作为数据通道,每个请求都被封装成HTTP 2.0的Stream。 + +Netty是一个高效的基于异步IO的网络传输框架,这个上一节我们已经介绍过了。HTTP 2.0在[第14讲],我们也介绍过。HTTP 2.0协议将一个TCP的连接,切分成多个流,每个流都有自己的ID,而且流是有优先级的。流可以是客户端发往服务端,也可以是服务端发往客户端。它其实只是一个虚拟的通道。 + +HTTP 2.0还将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。 + +通过这两种机制,HTTP 2.0的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,并且可以根据优先级,决定优先处理哪个流的数据。 + + + +由于基于HTTP 2.0,GRPC和其他的RPC不同,可以定义四种服务方法。 + +第一种,也是最常用的方式是单向RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。 + +rpc SayHello(HelloRequest) returns (HelloResponse){} + + +第二种方式是服务端流式RPC,即服务端返回的不是一个结果,而是一批。客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取,直到没有更多消息为止。 + +rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){} + + +第三种方式为客户端流式RPC,也即客户端的请求不是一个,而是一批。客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。 + +rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {} + + +第四种方式为双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者读写相结合的其他方式。每个数据流里消息的顺序会被保持。 + +rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){} + + +如果基于HTTP 2.0,客户端和服务器之间的交互方式要丰富得多,不仅可以单方向远程调用,还可以实现当服务端状态改变的时候,主动通知客户端。 + +至此,传输问题得到了解决。 + +服务发现与治理问题 + +最后是服务发现与服务治理的问题。 + +GRPC本身没有提供服务发现的机制,需要借助其他的组件,发现要访问的服务端,在多个服务端之间进行容错和负载均衡。 + +其实负载均衡本身比较简单,LVS、HAProxy、Nginx都可以做,关键问题是如何发现服务端,并根据服务端的变化,动态修改负载均衡器的配置。 + +在这里我们介绍一种对于GRPC支持比较好的负载均衡器Envoy。其实Envoy不仅仅是负载均衡器,它还是一个高性能的C++写的Proxy转发器,可以配置非常灵活的转发规则。 + +这些规则可以是静态的,放在配置文件中的,在启动的时候加载。要想重新加载,一般需要重新启动,但是Envoy支持热加载和热重启,这在一定程度上缓解了这个问题。 + +当然,最好的方式是将规则设置为动态的,放在统一的地方维护。这个统一的地方在Envoy眼中被称为服务发现(Discovery Service),过一段时间去这里拿一下配置,就修改了转发策略。 + +无论是静态的,还是动态的,在配置里面往往会配置四个东西。 + +第一个是listener。Envoy既然是Proxy,专门做转发,就得监听一个端口,接入请求,然后才能够根据策略转发,这个监听的端口就称为listener。 + +第二个是endpoint,是目标的IP地址和端口。这个是Proxy最终将请求转发到的地方。 + +第三个是cluster。一个cluster是具有完全相同行为的多个endpoint,也即如果有三个服务端在运行,就会有三个IP和端口,但是部署的是完全相同的三个服务,它们组成一个cluster,从cluster到endpoint的过程称为负载均衡,可以轮询。 + +第四个是route。有时候多个cluster具有类似的功能,但是是不同的版本号,可以通过route规则,选择将请求路由到某一个版本号,也即某一个cluster。 + +如果是静态的,则将后端的服务端的IP地址拿到,然后放在配置文件里面就可以了。 + +如果是动态的,就需要配置一个服务发现中心,这个服务发现中心要实现Envoy的API,Envoy可以主动去服务发现中心拉取转发策略。 + + + +看来,Envoy进程和服务发现中心之间要经常相互通信,互相推送数据,所以Envoy在控制面和服务发现中心沟通的时候,就可以使用GRPC,也就天然具备在用户面支撑GRPC的能力。 + +Envoy如果复杂的配置,都能干什么事呢? + +一种常见的规则是配置路由策略。例如后端的服务有两个版本,可以通过配置Envoy的route,来设置两个版本之间,也即两个cluster之间的route规则,一个占99%的流量,一个占1%的流量。 + +另一种常见的规则就是负载均衡策略。对于一个cluster下的多个endpoint,可以配置负载均衡机制和健康检查机制,当服务端新增了一个,或者挂了一个,都能够及时配置Envoy,进行负载均衡。 + + + +所有这些节点的变化都会上传到注册中心,所有这些策略都可以通过注册中心进行下发,所以,更严格的意义上讲,注册中心可以称为注册治理中心。 + +Envoy这么牛,是不是能够将服务之间的相互调用全部由它代理?如果这样,服务也不用像Dubbo,或者Spring Cloud一样,自己感知到注册中心,自己注册,自己治理,对应用干预比较大。 + +如果我们的应用能够意识不到服务治理的存在,就可以直接进行GRPC的调用。 + +这就是未来服务治理的趋势Serivce Mesh,也即应用之间的相互调用全部由Envoy进行代理,服务之间的治理也被Envoy进行代理,完全将服务治理抽象出来,到平台层解决。 + +- +至此RPC框架中有治理功能的Dubbo、Spring Cloud、Service Mesh就聚齐了。 + +小结 + +好了,这一节就到这里了,我们来总结一下。 + + +GRPC是一种二进制,性能好,跨语言,还灵活,同时可以进行服务治理的多快好省的RPC框架,唯一不足就是还是要写协议文件。 + +GRPC序列化使用Protocol Buffers,网络传输使用HTTP 2.0,服务治理可以使用基于Envoy的Service Mesh。 + + +最后,给你留一个思考题吧。 + +在讲述Service Mesh的时候,我们说了,希望Envoy能够在服务不感知的情况下,将服务之间的调用全部代理了,你知道怎么做到这一点吗? + +我们《趣谈网络协议》专栏已经接近尾声了。你还记得专栏开始,我们讲过的那个“双十一”下单的故事吗? + +下节开始,我会将这个过程涉及的网络协议细节,全部串联起来,给你还原一个完整的网络协议使用场景。信息量会很大,做好准备哦,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/37知识串:用双十一的故事串起碎片的网络协议(上).md b/专栏/趣谈网络协议/37知识串:用双十一的故事串起碎片的网络协议(上).md new file mode 100644 index 0000000..1c243cc --- /dev/null +++ b/专栏/趣谈网络协议/37知识串:用双十一的故事串起碎片的网络协议(上).md @@ -0,0 +1,99 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 37 知识串:用双十一的故事串起碎片的网络协议(上) + 基本的网络知识我们都讲完了,还记得最初举的那个“双十一”下单的例子吗?这一节开始,我们详细地讲解这个过程,用这个过程串起我们讲过的网络协议。 + +我把这个过程分为十个阶段,从云平台中搭建一个电商开始,到BGP路由广播,再到DNS域名解析,从客户看商品图片,到最终下单的整个过程,每一步我都会详细讲解。这节我们先来看前三个阶段。 + +1.部署一个高可用高并发的电商平台 + +首先,咱们要有个电商平台。假设我们已经有了一个特别大的电商平台,这个平台应该部署在哪里呢?假设我们用公有云,一般公有云会有多个位置,比如在华东、华北、华南都有。毕竟咱们的电商是要服务全国的,当然到处都要部署了。我们把主站点放在华东。 + + + +为了每个点都能“雨露均沾”,也为了高可用性,往往需要有多个机房,形成多个可用区(Available Zone)。由于咱们的应用是分布在两个可用区的,所以假如任何一个可用区挂了,都不会受影响。 + +我们来回想[数据中心]那一节,每个可用区里有一片一片的机柜,每个机柜上有一排一排的服务器,每个机柜都有一个接入交换机,有一个汇聚交换机将多个机柜连在一起。 + +这些服务器里面部署的都是计算节点,每台上面都有Open vSwitch创建的虚拟交换机,将来在这台机器上创建的虚拟机,都会连到Open vSwitch上。 + + + +接下来,你在云计算的界面上创建一个VPC(Virtual Private Cloud,虚拟私有网络),指定一个IP段,这样以后你部署的所有应用都会在这个虚拟网络里,使用你分配的这个IP段。为了不同的VPC相互隔离,每个VPC都会被分配一个VXLAN的ID。尽管不同用户的虚拟机有可能在同一个物理机上,但是不同的VPC二层压根儿是不通的。 + +由于有两个可用区,在这个VPC里面,要为每一个可用区分配一个Subnet,也就是在大的网段里分配两个小的网段。当两个可用区里面网段不同的时候,就可以配置路由策略,访问另外一个可用区,走某一条路由了。 + +接下来,应该创建数据库持久化层。大部分云平台都会提供PaaS服务,也就是说,不需要你自己搭建数据库,而是采用直接提供数据库的服务,并且单机房的主备切换都是默认做好的,数据库也是部署在虚拟机里面的,只不过从界面上,你看不到数据库所在的虚拟机而已。 + +云平台会给每个Subnet的数据库实例分配一个域名。创建数据库实例的时候,需要你指定可用区和Subnet,这样创建出来的数据库实例可以通过这个Subnet的私网IP进行访问。 + +为了分库分表实现高并发的读写,在创建的多个数据库实例之上,会创建一个分布式数据库的实例,也需要指定可用区和Subnet,还会为分布式数据库分配一个私网IP和域名。 + +对于数据库这种高可用性比较高的,需要进行跨机房高可用,因而两个可用区都要部署一套,但是只有一个是主,另外一个是备,云平台往往会提供数据库同步工具,将应用写入主的数据同步给备数据库集群。 + +接下来是创建缓存集群。云平台也会提供PaaS服务,也需要每个可用区和Subnet创建一套,缓存的数据在内存中,由于读写性能要求高,一般不要求跨可用区读写。 + +再往上层就是部署咱们自己写的程序了。基础服务层、组合服务层、Controller层,以及Nginx层、API网关等等,这些都是部署在虚拟机里面的。它们之间通过RPC相互调用,需要到注册中心进行注册。 + +它们之间的网络通信是虚拟机和虚拟机之间的。如果是同一台物理机,则那台物理机上的OVS就能转发过去;如果是不同的物理机,这台物理机的OVS和另一台物理机的OVS中间有一个VXLAN的隧道,将请求转发过去。 + +再往外就是负载均衡了,负载均衡也是云平台提供的PaaS服务,也是属于某个VPC的,部署在虚拟机里面的,但是负载均衡有个外网的IP,这个外网的IP地址就是在网关节点的外网网口上的。在网关节点上,会有NAT规则,将外网IP地址转换为VPC里面的私网IP地址,通过这些私网IP地址访问到虚拟机上的负载均衡节点,然后通过负载均衡节点转发到API网关的节点。 + +网关节点的外网网口是带公网IP地址的,里面有一个虚拟网关转发模块,还会有一个OVS,将私网IP地址放到VXLAN隧道里面,转发到虚拟机上,从而实现外网和虚拟机网络之间的互通。 + +不同的可用区之间,通过核心交换机连在一起,核心交换机之外是边界路由器。 + +在华北、华东、华南同样也部署了一整套,每个地区都创建了VPC,这就需要有一种机制将VPC连接到一起。云平台一般会提供硬件的VPC互连的方式,当然也可以使用软件互连的方式,也就是使用VPN网关,通过IPsec VPN将不同地区的不同VPC通过VPN连接起来。 + +对于不同地区和不同运营商的用户,我们希望他能够就近访问到网站,而且当一个点出了故障之后,我们希望能够在不同的地区之间切换,这就需要有智能DNS,这个也是云平台提供的。 + +对于一些静态资源,可以保持在对象存储里面,通过CDN下发到边缘节点,这样客户端就能尽快加载出来。 + +2.大声告诉全世界,可以到我这里买东西 + +当电商应用搭建完毕之后,接下来需要将如何访问到这个电商网站广播给全网。 + +刚才那张图画的是一个可用区的情况,对于多个可用区的情况,我们可以隐去计算节点的情况,将外网访问区域放大。 + + + +外网IP是放在虚拟网关的外网网口上的,这个IP如何让全世界知道呢?当然是通过BGP路由协议了。 + +每个可用区都有自己的汇聚交换机,如果机器数目比较多,可以直接用核心交换机,每个Region也有自己的核心交换区域。 + +在核心交换外面是安全设备,然后就是边界路由器。边界路由器会和多个运营商连接,从而每个运营商都能够访问到这个网站。边界路由器可以通过BGP协议,将自己数据中心里面的外网IP向外广播,也就是告诉全世界,如果要访问这些外网IP,都来我这里。 + +每个运营商也有很多的路由器、很多的点,于是就可以将如何到达这些IP地址的路由信息,广播到全国乃至全世界。 + +3.打开手机来上网,域名解析得地址 + +这个时候,不但你的这个网站的IP地址全世界都知道了,你打的广告可能大家也都看到了,于是有客户下载App来买东西了。 + + + +客户的手机开机以后,在附近寻找基站eNodeB,发送请求,申请上网。基站将请求发给MME,MME对手机进行认证和鉴权,还会请求HSS看有没有钱,看看是在哪里上网。 + +当MME通过了手机的认证之后,开始建立隧道,建设的数据通路分两段路,其实是两个隧道。一段是从eNodeB到SGW,第二段是从SGW到PGW,在PGW之外,就是互联网。 + +PGW会为手机分配一个IP地址,手机上网都是带着这个IP地址的。 + +当在手机上面打开一个App的时候,首先要做的事情就是解析这个网站的域名。 + +在手机运营商所在的互联网区域里,有一个本地的DNS,手机会向这个DNS请求解析DNS。当这个DNS本地有缓存,则直接返回;如果没有缓存,本地DNS才需要递归地从根DNS服务器,查到.com的顶级域名服务器,最终查到权威DNS服务器。 + +如果你使用云平台的时候,配置了智能DNS和全局负载均衡,在权威DNS服务中,一般是通过配置CNAME的方式,我们可以起一个别名,例如 vip.yourcomany.com ,然后告诉本地DNS服务器,让它请求GSLB解析这个域名,GSLB就可以在解析这个域名的过程中,通过自己的策略实现负载均衡。 + +GSLB通过查看请求它的本地DNS服务器所在的运营商和地址,就知道用户所在的运营商和地址,然后将距离用户位置比较近的Region里面,三个负载均衡SLB的公网IP地址,返回给本地DNS服务器。本地DNS解析器将结果缓存后,返回给客户端。 + +对于手机App来说,可以绕过刚才的传统DNS解析机制,直接只要HTTPDNS服务,通过直接调用HTTPDNS服务器,得到这三个SLB的公网IP地址。 + +看,经过了如此复杂的过程,咱们的万里长征还没迈出第一步,刚刚得到IP地址,包还没发呢?话说手机App拿到了公网IP地址,接下来应该做什么呢? + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/38知识串:用双十一的故事串起碎片的网络协议(中).md b/专栏/趣谈网络协议/38知识串:用双十一的故事串起碎片的网络协议(中).md new file mode 100644 index 0000000..0231300 --- /dev/null +++ b/专栏/趣谈网络协议/38知识串:用双十一的故事串起碎片的网络协议(中).md @@ -0,0 +1,132 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 38 知识串:用双十一的故事串起碎片的网络协议(中) + 上一节我们讲到,手机App经过了一个复杂的过程,终于拿到了电商网站的SLB的IP地址,是不是该下单了? + +别忙,俗话说的好,买东西要货比三家。大部分客户在购物之前要看很多商品图片,比来比去,最后好不容易才下决心,点了下单按钮。下单按钮一按,就要开始建立连接。建立连接这个过程也挺复杂的,最终还要经过层层封装,才构建出一个完整的网络包。今天我们就来看这个过程。 + +4.购物之前看图片,静态资源CDN + +客户想要在购物网站买一件东西的时候,一般是先去详情页看看图片,是不是想买的那一款。 + + + +我们部署电商应用的时候,一般会把静态资源保存在两个地方,一个是接入层nginx后面的varnish缓存里面,一般是静态页面;对于比较大的、不经常更新的静态图片,会保存在对象存储里面。这两个地方的静态资源都会配置CDN,将资源下发到边缘节点。 + +配置了CDN之后,权威DNS服务器上,会为静态资源设置一个CNAME别名,指向另外一个域名 cdn.com ,返回给本地DNS服务器。 + +当本地DNS服务器拿到这个新的域名时,需要继续解析这个新的域名。这个时候,再访问的时候就不是原来的权威DNS服务器了,而是 cdn.com 的权威DNS服务器。这是CDN自己的权威DNS服务器。 + +在这个服务器上,还是会设置一个CNAME,指向另外一个域名,也即CDN网络的全局负载均衡器。 + +本地DNS服务器去请求CDN的全局负载均衡器解析域名,全局负载均衡器会为用户选择一台合适的缓存服务器提供服务,将IP返回给客户端,客户端去访问这个边缘节点,下载资源。缓存服务器响应用户请求,将用户所需内容传送到用户终端。 + +如果这台缓存服务器上并没有用户想要的内容,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器,将内容拉到本地。 + +5.看上宝贝点下单,双方开始建连接 + +当你浏览了很多图片,发现实在喜欢某个商品,于是决定下单购买。 + +电商网站会对下单的情况提供RESTful的下单接口,而对于下单这种需要保密的操作,需要通过HTTPS协议进行请求。 + +在所有这些操作之前,首先要做的事情是建立连接。 + + + +HTTPS协议是基于TCP协议的,因而要先建立TCP的连接。在这个例子中,TCP的连接是在手机上的App和负载均衡器SLB之间的。 + +尽管中间要经过很多的路由器和交换机,但是TCP的连接是端到端的。TCP这一层和更上层的HTTPS无法看到中间的包的过程。尽管建立连接的时候,所有的包都逃不过在这些路由器和交换机之间的转发,转发的细节我们放到那个下单请求的发送过程中详细解读,这里只看端到端的行为。 + +对于TCP连接来讲,需要通过三次握手建立连接,为了维护这个连接,双方都需要在TCP层维护一个连接的状态机。 + +一开始,客户端和服务端都处于CLOSED状态。服务端先是主动监听某个端口,处于LISTEN状态。然后客户端主动发起连接SYN,之后处于SYN-SENT状态。服务端收到发起的连接,返回SYN,并且ACK客户端的SYN,之后处于SYN-RCVD状态。 + +客户端收到服务端发送的SYN和ACK之后,发送ACK的ACK,之后处于ESTABLISHED状态。这是因为,它一发一收成功了。服务端收到ACK的ACK之后,也会处于ESTABLISHED状态,因为它的一发一收也成功了。 + +当TCP层的连接建立完毕之后,接下来轮到HTTPS层建立连接了,在HTTPS的交换过程中,TCP层始终处于ESTABLISHED。 + +对于HTTPS,客户端会发送Client Hello消息到服务器,用明文传输TLS版本信息、加密套件候选列表、压缩算法候选列表等信息。另外,还会有一个随机数,在协商对称密钥的时候使用。 + +然后,服务器会返回Server Hello消息,告诉客户端,服务器选择使用的协议版本、加密套件、压缩算法等。这也有一个随机数,用于后续的密钥协商。 + +然后,服务器会给你一个服务器端的证书,然后说:“Server Hello Done,我这里就这些信息了。” + +客户端当然不相信这个证书,于是从自己信任的CA仓库中,拿CA的证书里面的公钥去解密电商网站的证书。如果能够成功,则说明电商网站是可信的。这个过程中,你可能会不断往上追溯CA、CA的CA、CA的CA的CA,反正直到一个授信的CA,就可以了。 + +证书验证完毕之后,觉得这个服务端是可信的,于是客户端计算产生随机数字Pre-master,发送Client Key Exchange,用证书中的公钥加密,再发送给服务器,服务器可以通过私钥解密出来。 + +接下来,无论是客户端还是服务器,都有了三个随机数,分别是:自己的、对端的,以及刚生成的Pre-Master随机数。通过这三个随机数,可以在客户端和服务器产生相同的对称密钥。 + +有了对称密钥,客户端就可以说:“Change Cipher Spec,咱们以后都采用协商的通信密钥和加密算法进行加密通信了。” + +然后客户端发送一个Encrypted Handshake Message,将已经商定好的参数等,采用协商密钥进行加密,发送给服务器用于数据与握手验证。 + +同样,服务器也可以发送Change Cipher Spec,说:“没问题,咱们以后都采用协商的通信密钥和加密算法进行加密通信了”,并且也发送Encrypted Handshake Message的消息试试。 + +当双方握手结束之后,就可以通过对称密钥进行加密传输了。 + +真正的下单请求封装成网络包的发送过程,我们先放一放,我们来接着讲这个网络包的故事。 + +6.发送下单请求网络包,西行需要出网关 + +当客户端和服务端之间建立了连接后,接下来就要发送下单请求的网络包了。 + +在用户层发送的是HTTP的网络包,因为服务端提供的是RESTful API,因而HTTP层发送的就是一个请求。 + +POST /purchaseOrder HTTP/1.1 +Host: www.geektime.com +Content-Type: application/json; charset=utf-8 +Content-Length: nnn + +{ + "order": { + "date": "2018-07-01", + "className": "趣谈网络协议", + "Author": "刘超", + "price": "68" + } +} + + +HTTP的报文大概分为三大部分。第一部分是请求行,第二部分是请求的首部,第三部分才是请求的正文实体。 + +在请求行中,URL就是 www.geektime.com/purchaseOrder ,版本为HTTP 1.1。 + +请求的类型叫作POST,它需要主动告诉服务端一些信息,而非获取。需要告诉服务端什么呢?一般会放在正文里面。正文可以有各种各样的格式,常见的格式是JSON。 + +请求行下面就是我们的首部字段。首部是key value,通过冒号分隔。 + +Content-Type是指正文的格式。例如,我们进行POST的请求,如果正文是JSON,那么我们就应该将这个值设置为JSON。 + +接下来是正文,这里是一个JSON字符串,里面通过文本的形式描述了,要买一个课程,作者是谁,多少钱。 + +这样,HTTP请求的报文格式就拼凑好了。接下来浏览器或者移动App会把它交给下一层传输层。 + +怎么交给传输层呢?也是用Socket进行程序设计。如果用的是浏览器,这些程序不需要你自己写,有人已经帮你写好了;如果在移动APP里面,一般会用一个HTTP的客户端工具来发送,并且帮你封装好。 + +HTTP协议是基于TCP协议的,所以它使用面向连接的方式发送请求,通过Stream二进制流的方式传给对方。当然,到了TCP层,它会把二进制流变成一个个报文段发送给服务器。 + +在TCP头里面,会有源端口号和目标端口号,目标端口号一般是服务端监听的端口号,源端口号在手机端,往往是随机分配一个端口号。这个端口号在客户端和服务端用于区分请求和返回,发给那个应用。 + +在IP头里面,都需要加上自己的地址(即源地址)和它想要去的地方(即目标地址)。当一个手机上线的时候,PGW会给这个手机分配一个IP地址,这就是源地址,而目标地址则是云平台的负载均衡器的外网IP地址。 + +在IP层,客户端需要查看目标地址和自己是否是在同一个局域网,计算是否是同一个网段,往往需要通过CIDR子网掩码来计算。 + +对于这个下单场景,目标IP和源IP不会在同一个网段,因而需要发送到默认的网关。一般通过DHCP分配IP地址的时候,同时配置默认网关的IP地址。 + +但是客户端不会直接使用默认网关的IP地址,而是发送ARP协议,来获取网关的MAC地址,然后将网关MAC作为目标MAC,自己的MAC作为源MAC,放入MAC头,发送出去。 + +一个完整的网络包的格式是这样的。 + + + +真不容易啊,本来以为上篇就发送下单包了,结果到中篇这个包还没发送出去,只是封装了一个如此长的网络包。别着急,你可以自己先预想一下,接下来该做什么了? + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/39知识串:用双十一的故事串起碎片的网络协议(下).md b/专栏/趣谈网络协议/39知识串:用双十一的故事串起碎片的网络协议(下).md new file mode 100644 index 0000000..c75612a --- /dev/null +++ b/专栏/趣谈网络协议/39知识串:用双十一的故事串起碎片的网络协议(下).md @@ -0,0 +1,263 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 39 知识串:用双十一的故事串起碎片的网络协议(下) + 上一节,我们封装了一个长长的网络包,“大炮”准备完毕,开始发送。 + +发送的时候可以说是重重关隘,从手机到移动网络、互联网,还要经过多个运营商才能到达数据中心,到了数据中心就进入第二个复杂的过程,从网关到VXLAN隧道,到负载均衡,到Controller层、组合服务层、基础服务层,最终才下单入库。今天,我们就来看这最后一段过程。 + +7.一座座城池一道道关,流控拥塞与重传 + +网络包已经组合完毕,接下来我们来看,如何经过一道道城关,到达目标公网IP。 + +对于手机来讲,默认的网关在PGW上。在移动网络里面,从手机到SGW,到PGW是有一条隧道的。在这条隧道里面,会将上面的这个包作为隧道的乘客协议放在里面,外面SGW和PGW在核心网机房的IP地址。网络包直到PGW(PGW是隧道的另一端)才将里面的包解出来,转发到外部网络。 + +所以,从手机发送出来的时候,网络包的结构为: + + +源MAC:手机也即UE的MAC; + +目标MAC:网关PGW上面的隧道端点的MAC; + +源IP:UE的IP地址; + +目标IP:SLB的公网IP地址。 + + +进入隧道之后,要封装外层的网络地址,因而网络包的格式为: + + +外层源MAC:E-NodeB的MAC; + +外层目标MAC:SGW的MAC; + +外层源IP:E-NodeB的IP; + +外层目标IP:SGW的IP; + +内层源MAC:手机也即UE的MAC; + +内层目标MAC:网关PGW上面的隧道端点的MAC; + +内层源IP:UE的IP地址; + +内层目标IP:SLB的公网IP地址。 + + +当隧道在SGW的时候,切换了一个隧道,会从SGW到PGW的隧道,因而网络包的格式为: + + +外层源MAC:SGW的MAC; + +外层目标MAC:PGW的MAC; + +外层源IP:SGW的IP; + +外层目标IP:PGW的IP; + +内层源MAC:手机也即UE的MAC; + +内层目标MAC:网关PGW上面的隧道端点的MAC; + +内层源IP:UE的IP地址; + +内层目标IP:SLB的公网IP地址。 + + +在PGW的隧道端点将包解出来,转发出去的时候,一般在PGW出外部网络的路由器上,会部署NAT服务,将手机的IP地址转换为公网IP地址,当请求返回的时候,再NAT回来。 + +因而在PGW之后,相当于做了一次[欧洲十国游型]的转发,网络包的格式为: + + +源MAC:PGW出口的MAC; + +目标MAC:NAT网关的MAC; + +源IP:UE的IP地址; + +目标IP:SLB的公网IP地址。 + + +在NAT网关,相当于做了一次[玄奘西游型]的转发,网络包的格式变成: + + +源MAC:NAT网关的MAC; + +目标MAC:A2路由器的MAC; + +源IP:UE的公网IP地址; + +目标IP:SLB的公网IP地址。 + + + + +出了NAT网关,就从核心网到达了互联网。在网络世界,每一个运营商的网络成为自治系统AS。每个自治系统都有边界路由器,通过它和外面的世界建立联系。 + +对于云平台来讲,它可以被称为Multihomed AS,有多个连接连到其他的AS,但是大多拒绝帮其他的AS传输包。例如一些大公司的网络。对于运营商来说,它可以被称为Transit AS,有多个连接连到其他的AS,并且可以帮助其他的AS传输包,比如主干网。 + +如何从出口的运营商到达云平台的边界路由器?在路由器之间需要通过BGP协议实现,BGP又分为两类,eBGP和iBGP。自治系统之间、边界路由器之间使用eBGP广播路由。内部网络也需要访问其他的自治系统。 + +边界路由器如何将BGP学习到的路由导入到内部网络呢?通过运行iBGP,使内部的路由器能够找到到达外网目的地最好的边界路由器。 + +网站的SLB的公网IP地址早已经通过云平台的边界路由器,让全网都知道了。于是这个下单的网络包选择的下一跳是A2,也即将A2的MAC地址放在目标MAC地址中。 + +到达A2之后,从路由表中找到下一跳是路由器C1,于是将目标MAC换成C1的MAC地址。到达C1之后,找到下一跳是C2,将目标MAC地址设置为C2的MAC。到达C2后,找到下一跳是云平台的边界路由器,于是将目标MAC设置为边界路由器的MAC地址。 + +你会发现,这一路,都是只换MAC,不换目标IP地址。这就是所谓下一跳的概念。 + +在云平台的边界路由器,会将下单的包转发进来,经过核心交换,汇聚交换,到达外网网关节点上的SLB的公网IP地址。 + +我们可以看到,手机到SLB的公网IP,是一个端到端的连接,连接的过程发送了很多包。所有这些包,无论是TCP三次握手,还是HTTPS的密钥交换,都是要走如此复杂的过程到达SLB的,当然每个包走的路径不一定一致。 + +网络包走在这个复杂的道路上,很可能一不小心就丢了,怎么办?这就需要借助TCP的机制重新发送。 + +既然TCP要对包进行重传,就需要维护Sequence Number,看哪些包到了,哪些没到,哪些需要重传,传输的速度应该控制到多少,这就是TCP的滑动窗口协议。 + + + +整个TCP的发送,一开始会协商一个Sequence Number,从这个Sequence Number开始,每个包都有编号。滑动窗口将接收方的网络包分成四个部分: + + +已经接收,已经ACK,已经交给应用层的包; + +已经接收,已经ACK,未发送给应用层; + +已经接收,尚未发送ACK; + +未接收,尚有空闲的缓存区域。 + + +对于TCP层来讲,每一个包都有ACK。ACK需要从SLB回复到手机端,将上面的那个过程反向来一遍,当然路径不一定一致,可见ACK也不是那么轻松的事情。 + +如果发送方超过一定的时间没有收到ACK,就会重新发送。只有TCP层ACK过的包,才会发给应用层,并且只会发送一份,对于下单的场景,应用层是HTTP层。 + +你可能会问了,TCP老是重复发送,会不会导致一个单下了两遍?是否要求服务端实现幂等?从TCP的机制来看,是不会的。只有收不到ACK的包才会重复发,发到接收端,在窗口里面只保存一份,所以在同一个TCP连接中,不用担心重传导致二次下单。 + +但是TCP连接会因为某种原因断了,例如手机信号不好,这个时候手机把所有的动作重新做一遍,建立一个新的TCP连接,在HTTP层调用两次RESTful API。这个时候可能会导致两遍下单的情况,因而RESTful API需要实现幂等。 + +当ACK过的包发给应用层之后,TCP层的缓存就空了出来,这会导致上面图中的大三角,也即接收方能够容纳的总缓存,整体顺时针滑动。小的三角形,也即接收方告知发送方的窗口总大小,也即还没有完全确认收到的缓存大小,如果把这些填满了,就不能再发了,因为没确认收到,所以一个都不能扔。 + +8.从数据中心进网关,公网NAT成私网 + +包从手机端经历千难万险,终于到了SLB的公网IP所在的公网网口。由于匹配上了MAC地址和IP地址,因而将网络包收了进来。 + + + +在虚拟网关节点的外网网口上,会有一个NAT规则,将公网IP地址转换为VPC里面的私网IP地址,这个私网IP地址就是SLB的HAProxy所在的虚拟机的私网IP地址。 + +当然为了承载比较大的吞吐量,虚拟网关节点会有多个,物理网络会将流量分发到不同的虚拟网关节点。同样HAProxy也会是一个大的集群,虚拟网关会选择某个负载均衡节点,将某个请求分发给它,负载均衡之后是Controller层,也是部署在虚拟机里面的。 + +当网络包里面的目标IP变成私有IP地址之后,虚拟路由会查找路由规则,将网络包从下方的私网网口发出来。这个时候包的格式为: + + +源MAC:网关MAC; + +目标MAC:HAProxy虚拟机的MAC; + +源IP:UE的公网IP; + +目标IP:HAProxy虚拟机的私网IP。 + + +9.进入隧道打标签,RPC远程调用下单 + +在虚拟路由节点上,也会有OVS,将网络包封装在VXLAN隧道里面,VXLAN ID就是给你的租户创建VPC的时候分配的。包的格式为: + + +外层源MAC:网关物理机MAC; + +外层目标MAC:物理机A的MAC; + +外层源IP:网关物理机IP; + +外层目标IP:物理机A的IP; + +内层源MAC:网关MAC; + +内层目标MAC:HAProxy虚拟机的MAC; + +内层源IP:UE的公网IP; + +内层目标IP:HAProxy虚拟机的私网IP。 + + +在物理机A上,OVS会将包从VXLAN隧道里面解出来,发给HAProxy所在的虚拟机。HAProxy所在的虚拟机发现MAC地址匹配,目标IP地址匹配,就根据TCP端口,将包发给HAProxy进程,因为HAProxy是在监听这个TCP端口的。因而HAProxy就是这个TCP连接的服务端,客户端是手机。对于TCP的连接状态、滑动窗口等,都是在HAProxy上维护的。 + +在这里HAProxy是一个四层负载均衡,也即它只解析到TCP层,里面的HTTP协议它不关心,就将请求转发给后端的多个Controller层的一个。 + +HAProxy发出去的网络包就认为HAProxy是客户端了,看不到手机端了。网络包格式如下: + + +源MAC:HAProxy所在虚拟机的MAC; + +目标MAC:Controller层所在虚拟机的MAC; + +源IP:HAProxy所在虚拟机的私网IP; + +目标IP:Controller层所在虚拟机的私网IP。 + + +当然这个包发出去之后,还是会被物理机上的OVS放入VXLAN隧道里面,网络包格式为: + + +外层源MAC:物理机A的MAC; + +外层目标MAC:物理机B的MAC; + +外层源IP:物理机A的IP; + +外层目标IP:物理机B的IP; + +内层源MAC:HAProxy所在虚拟机的MAC; + +内层目标MAC:Controller层所在虚拟机的MAC; + +内层源IP:HAProxy所在虚拟机的私网IP; + +内层目标IP:Controller层所在虚拟机的私网IP。 + + +在物理机B上,OVS会将包从VXLAN隧道里面解出来,发给Controller层所在的虚拟机。Controller层所在的虚拟机发现MAC地址匹配,目标IP地址匹配,就根据TCP端口,将包发给Controller层的进程,因为它在监听这个TCP端口。 + +在HAProxy和Controller层之间,维护一个TCP的连接。 + +Controller层收到包之后,它是关心HTTP里面是什么的,于是解开HTTP的包,发现是一个POST请求,内容是下单购买一个课程。 + +10.下单扣减库存优惠券,数据入库返回成功 + +下单是一个复杂的过程,因而往往在组合服务层会有一个专门管理下单的服务,Controller层会通过RPC调用这个组合服务层。 + +假设我们使用的是Dubbo,则Controller层需要读取注册中心,将下单服务的进程列表拿出来,选出一个来调用。 + +Dubbo中默认的RPC协议是Hessian2。Hessian2将下单的远程调用序列化为二进制进行传输。 + +Netty是一个非阻塞的基于事件的网络传输框架。Controller层和下单服务之间,使用了Netty的网络传输框架。有了Netty,就不用自己编写复杂的异步Socket程序了。Netty使用的方式,就是咱们讲[Socket编程]的时候,一个项目组支撑多个项目(IO多路复用,从派人盯着到有事通知)这种方式。 + +Netty还是工作在Socket这一层的,发送的网络包还是基于TCP的。在TCP的下层,还是需要封装上IP头和MAC头。如果跨物理机通信,还是需要封装的外层的VXLAN隧道里面。当然底层的这些封装,Netty都不感知,它只要做好它的异步通信即可。 + +在Netty的服务端,也即下单服务中,收到请求后,先用Hessian2的格式进行解压缩。然后将请求分发到线程中进行处理,在线程中,会调用下单的业务逻辑。 + +下单的业务逻辑比较复杂,往往要调用基础服务层里面的库存服务、优惠券服务等,将多个服务调用完毕,才算下单成功。下单服务调用库存服务和优惠券服务,也是通过Dubbo的框架,通过注册中心拿到库存服务和优惠券服务的列表,然后选一个调用。 + +调用的时候,统一使用Hessian2进行序列化,使用Netty进行传输,底层如果跨物理机,仍然需要通过VXLAN的封装和解封装。 + +咱们以库存为例子的时候,讲述过幂等的接口实现的问题。因为如果扣减库存,仅仅是谁调用谁减一。这样存在的问题是,如果扣减库存因为一次调用失败,而多次调用,这里指的不是TCP多次重试,而是应用层调用的多次重试,就会存在库存扣减多次的情况。 + +这里常用的方法是,使用乐观锁(Compare and Set,简称CAS)。CAS要考虑三个方面,当前的库存数、预期原来的库存数和版本,以及新的库存数。在操作之前,查询出原来的库存数和版本,真正扣减库存的时候,判断如果当前库存的值与预期原值和版本相匹配,则将库存值更新为新值,否则不做任何操作。 + +这是一种基于状态而非基于动作的设计,符合RESTful的架构设计原则。这样的设计有利于高并发场景。当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。 + +最终,当下单更新到分布式数据库中之后,整个下单过程才算真正告一段落。 + +好了,经过了十个过程,下单终于成功了,你是否对这个过程了如指掌了呢?如果发现对哪些细节比较模糊,可以回去看一下相应的章节,相信会有更加深入的理解。 + +到此,我带着你用下单过程把网络协议的知识都复习了一遍。授人以鱼不如授人以渔。下一节,我将会带你来搭建一个网络实验环境,配合实验来说明理论。 + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/40搭建一个网络实验环境:授人以鱼不如授人以渔.md b/专栏/趣谈网络协议/40搭建一个网络实验环境:授人以鱼不如授人以渔.md new file mode 100644 index 0000000..8633827 --- /dev/null +++ b/专栏/趣谈网络协议/40搭建一个网络实验环境:授人以鱼不如授人以渔.md @@ -0,0 +1,198 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 40 搭建一个网络实验环境:授人以鱼不如授人以渔 + 因为这门课是基础课程,而且配合音频的形式发布,所以我多以理论为主来进行讲解。在专栏更新的过程中,不断有同学让我推荐一些网络方面的书籍,还有同学说能不能配合一些实验来说明理论。 + +的确,网络是一门实验性很强的学科,就像我在开篇词里面说的一样:一看觉得懂,一问就打鼓,一用就糊涂。 在写专栏的过程中,我自己也深深体会到了。这个时候,我常常会拿一个现实的环境,上手操作一下,抓个包看看,这样心里就会有定论。 + +《TCP/IP详解》实验环境搭建 + +对于网络方面的书籍,我当然首推Rechard Stevens的《TCP/IP illustrated》(《TCP/IP详解》)。这本书把理论讲得深入浅出,还配有大量的上手实践和抓包,看到这些抓包,原来不理解的很多理论,一下子就能懂了。 + +这本书里有个拓扑图,书上的很多实验都是基于这个图的,但是这个拓扑图还是挺复杂的。我这里先不说,一会儿详细讲。 + +Rechard Stevens,因为工作中有这么一个环境,很方便做实验,最终才写出了这样一本书,而我们一般人学习网络,没有这个环境应该怎么办呢? + +时代不同了,咱们现在有更加强大的工具了。例如,这里这么多的机器,我们可以用Docker来实现,多个网络可以用Open vSwitch来实现。你甚至不需要一台物理机,只要一台1核2G的虚拟机,就能将这个环境搭建起来。 + +搭建这个环境的时候,需要一些脚本。我把脚本都放在了Github里面,你可以自己取用。 + +1.创建一个Ubuntu虚拟机 + +在你的笔记本电脑上,用VirtualBox创建就行。1核2G,随便一台电脑都能搭建起来。 + +首先,我们先下载一个Ubuntu的镜像。我是从Ubuntu官方网站下载的。 + + + +然后,在VirtualBox里面安装Ubuntu。安装过程网上一大堆教程,你可以自己去看,我这里就不详细说了。 + +这里我需要说明的是网络的配置。 + +对于这个虚拟机,我们创建两个网卡,一个是Host-only,只有你的笔记本电脑上能够登录进去。这个网卡上的IP地址也只有在你的笔记本电脑上管用。这个网卡的配置比较稳定,用于在SSH上做操作。这样你的笔记本电脑就可以搬来搬去,在公司里安装一半,回家接着安装另一半都没问题。 + + + +这里有一个虚拟的网桥,这个网络可以在管理>主机网络管理里面进行配置。 + + + +在这里可以虚拟网桥的的IP地址,同时启用一个DHCP服务器,为新创建的虚拟机配置IP地址。 + +另一个网卡配置为NAT网络,用于访问互联网。配置了NAT网络之后,只要你的笔记本电脑能上网,虚拟机就能上网。由于咱们在Ubuntu里面要安装一些东西,因而需要联网。 + +你可能会问了,这个配置复杂吗?一点儿都不复杂。咱们讲[虚拟机网络]的时候,讲过这个。 + + + +安装完了Ubuntu之后,需要对Ubuntu里面的网卡进行配置。对于Ubuntu来讲,网卡的配置在/etc/network/interfaces这个文件里面。在我的环境里,NAT的网卡名称为enp0s3,Host-only的网卡的名称为enp0s8,都可以配置为自动配置。 + +auto lo +iface lo inet loopback + +auto enp0s3 +iface enp0s3 inet dhcp + +auto enp0s8 +iface enp0s8 inet dhcp + + +这样,重启之后,IP就配置好了。 + +2.安装Docker和Open vSwitch + +接下来,在Ubuntu里面,以root用户,安装Docker和Open vSwitch。 + +你可以按照Docker的官方安装文档来做。我这里也贴一下我的安装过程。 + +apt-get remove docker docker-engine docker.io +apt-get -y update +apt-get -y install apt-transport-https ca-certificates curl software-properties-common +curl -fsSL https://download.docker.com/linux/ubuntu/gpg > gpg +apt-key add gpg +apt-key fingerprint 0EBFCD88 +add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +apt-get -y update +apt-cache madison docker-ce +apt-get -y install docker-ce=18.06.0~ce~3-0~ubuntu + + +之后,还需要安装Open vSwitch和Bridge。 + +apt-get -y install openvswitch-common openvswitch-dbg openvswitch-switch python-openvswitch openvswitch-ipsec openvswitch-pki openvswitch-vtep + +apt-get -y install bridge-utils + +apt-get -y install arping + + +3.准备一个Docker的镜像 + + + +每个节点都是一个Docker,对应要有一个Docker镜像。这个镜像我已经打好了,你可以直接使用。 + +docker pull hub.c.163.com/liuchao110119163/ubuntu:tcpip + + +当然你也可以自己打这个镜像。Dockerfile就像这样: + +FROM hub.c.163.com/public/ubuntu:14.04 +RUN apt-get -y update && apt-get install -y iproute2 iputils-arping net-tools tcpdump curl telnet iputils-tracepath traceroute +RUN mv /usr/sbin/tcpdump /usr/bin/tcpdump +ENTRYPOINT /usr/sbin/sshd -D + + +4.启动整个环境 + +启动这个环境还是比较复杂的,我写成了一个脚本。在Git仓库里面,有一个文件 setupenv.sh ,可以执行这个脚本,里面有两个参数,一个参数是NAT网卡的名字,一个是镜像的名称。 + +git clone https://github.com/popsuper1982/tcpipillustrated.git +cd tcpipillustrated +docker pull hub.c.163.com/liuchao110119163/ubuntu:tcpip +chmod +x setupenv.sh +./setupenv.sh enp0s3 hub.c.163.com/liuchao110119163/ubuntu:tcpip + + +这样,整个环境就搭建起来了,所有的容器之间都可以ping通,而且都可以上网。 + +不过,我写的这个脚本对一些人来说可能会有点儿复杂,我这里也解释一下。 + +首先每一个节点,都启动一个容器。使用–privileged=true方式,网络先不配置–net none。有两个二层网络,使用ovs-vsctl的add-br命令,创建两个网桥。 + +pipework是一个很好的命令行工具,可以将容器连接到两个二层网络上。 + +但是我们上面那个图里有两个比较特殊的网络,一个是从slip到bsdi的P2P网络,需要创建一个peer的两个网卡,然后两个Docker的网络namespace里面各塞进去一个。 + +有关操作Docker的网络namespace的方式,咱们在[容器网络]那一节讲过ip netns命令。 + +这里需要注意的是,P2P网络和下面的二层网络不是同一个网络。P2P网络的CIDR是140.252.13.64/27,而下面的二层网络的CIDR是140.252.13.32/27。如果按照/24,看起来是一个网络,但是/27就不是了。至于[CIDR的计算方法],你可以回去复习一下。 + +另外需要配置从sun到netb的点对点网络,方法还是通过peer网卡和ip netns的方式。 + +这里有个特殊的地方,对于netb来讲,不是一个普通的路由器,因为netb两边是同一个二层网络,所以需要配置arp proxy。 + +为了所有的节点之间互通,要配置一下路由策略,这里需要通过ip route命令。 + + +对于slip来讲,bsdi左面13.66这个网口是网关。 + +对于bsdi和svr4来讲,如果去外网,sun下面的网口13.33是网关。 + +对于sun来讲,上面的网口1.29属于上面的二层网络了,它如果去外网,gateway下面的网口1.4就是外网网关。 + +对于aix,solaris,gemini来讲,如果去外网,网关也是gateway下面的网口1.4。如果去下面的二层网口,网关是sun上面的网口1.29。 + + +配置完了这些,图中的所有的节点都能相互访问了,最后还要解决如何访问外网的问题。 + +我们还是需要创建一个peer网卡对。一个放在gateway里面,一个放在gateway外面。外面的网卡去外网的网关。 + +在虚拟机上面,还需要配置一个iptables的地址伪装规则MASQUERADE,其实就是一个SNAT。因为容器里面要访问外网,因为外网是不认的,所以源地址不能用容器的地址,需要SNAT成为虚拟机的地址出去,回来的时候再NAT回来。 + +配置这个环境还是挺复杂的,要用到咱们学到的很多知识。如果没有学习前面那些知识,直接就做这个实验,你肯定会很晕。但是只学理论也不行,要把理论都学过一遍,再做一遍实验,这才是一个不断迭代、更新知识库的过程。 + +有了这个环境,《TCP/IP详解》里面的所有实验都能做了,而且我打的这个Docker镜像里面,tcpdump等网络工具都安装了,你可以“为所欲为”了。 + +Open vSwitch的实验 + +做了TCP/IP详解的实验之后,网络程序设计这部分,你就有了坚实的基础。但是涉及到数据中心内部的一些网络技术,什么VLAN、VXLAN、STP等偏运维方向的,学习还是会比较困难。好在我们有Open vSwitch,也可以做大量的实验。 + +Open vSwitch门槛比较高,里面的概念也非常多,可谓千头万绪。不过,通过我这么多年研究的经验,可以告诉你,这里面有一个很好的线索,那就是Open vSwitch会将自己对于网络的配置保存在一个本地库里面。这个库的表结构之间的关系就像这样: + + + +这个库其实是一个JSON,如果把这个JSON打印出来,能够看到更加详细的特性。按照这些特性一一实验,可以逐渐把Open vSwitch各个特性都掌握。 + + + +这里面最重要的概念就是网桥。一个网桥会有流表控制网络包的处理过程,会有控制器下发流表,一个网桥上会有多个端口,可以对端口进行流控,一个端口可以设置VLAN,一个端口可以包含多个网卡,可以做绑定,网卡可以设置成为GRE和VXLAN。 + +我写过一个Open vSwitch的实验教程,也放在了Github里面。这里面有这么几个比较重要的实验,你可以看一看。 + + +实验一:查看Open vSwitch的架构。我们在讲Open vSwitch的时候,提过Open vSwitch的架构,在这个实验中,我们可以查看Open vSwitch的各个模块以及启动的参数。 + +实验五:配置使用OpenFlow Controller,体验一把作为小区物业在监控室里面管控整个小区道路的样子。 + +实验八:测试Port的VLAN功能。看一下VLAN隔离究竟是什么样的。 + +实验十:QoS功能。体验一把如果使用HTB进行网卡限流。 + +实验十一:GRE和VXLAN隧道功能,看虚拟网络如何进行租户隔离。 + +实验十五:对Flow Table的操作,体验流表对网络包随心所欲的处理。 + + +好了,关于整个环境的搭建我就讲到这里了。 + +其实到这里,对于网络世界的探索才刚刚开始,只有经过你自己动手和思考产生的内容,才是真正属于你的知识!打开你的电脑,上手去实验吧! + +欢迎你留言和我讨论。趣谈网络协议,我们下期见! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/加餐1创作故事:我是如何创作“趣谈网络协议”专栏的?.md b/专栏/趣谈网络协议/加餐1创作故事:我是如何创作“趣谈网络协议”专栏的?.md new file mode 100644 index 0000000..5d51239 --- /dev/null +++ b/专栏/趣谈网络协议/加餐1创作故事:我是如何创作“趣谈网络协议”专栏的?.md @@ -0,0 +1,85 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 加餐1 创作故事:我是如何创作“趣谈网络协议”专栏的? + + +我用将近半年的时间在“极客时间”写了一个专栏“趣谈网络协议”。对于我自己来讲,这真的是个非常特殊而又难忘的经历。 + +很多人都很好奇,这个专栏究竟是怎么一步步创作出来的,每一篇文章是怎么写出来的?自己录音频又是什么样的感受?写完整个专栏之后,我终于有时间回顾、整理一下这半年的所感所想。对我来说,这是一次难得的体验,也是一次与“极客时间”的深度沟通。 + +专栏是写给谁的? + +和极客时间的编辑谈妥主题之后,他们首先要求我基于约定的主题,写一个36节至50节的大纲,之后会以每周三篇的频率,文字加音频的方式发布。每篇文章的体量要求在3000字左右,录成音频大约就是10分钟。 + +我本来觉得写这么一个专栏根本就不是个事儿。毕竟咱也是在IT圈摸爬滚打了许多年的“老司机”,干货积累得也不少。只要是熟悉的领域,不用准备,聊个把小时都没啥问题。况且我原来还写过书、写过博客、写过公众号。所以,我对自己文字方面的能力很有自信。 + +至于语言方面,咱常年出入各大技术论坛,什么场子没趟过。一个两天的线下培训,咱都能扛过来。每篇10分钟,总共36篇,那不才是6个小时嘛,肯定没问题。 + +但是,写了之后我发现,自己会是一回事儿,能讲给别人是另一回事儿,而能讲给“看不见的陌生人”听,是这世上最难的事儿。 + +我知道,很多技术人都有这样一个“毛病”,就是觉得掌握技术本身是最重要的,其他什么产品、市场、销售,都没技术含量。这种思维导致很多技术比较牛的人会以自我为中心,仅站在自己的角度思考问题。所以,常常是自己讲得很爽,完全不管听的人是不是真的接受了。写专栏的时候,这绝对是个大忌。 + +除此之外,这种思维对职业发展的影响也是很大的。单打独斗,一个人搞定一个软件的时代已经过去了。学会和别人合作,才是现代社会的生存法则,而良好的合作源于沟通。 + +但沟通不易,高质量的沟通更难。面对的人越多,沟通的难度就越大。因为每个人的背景、知识、基础都不同,想听的内容肯定更是千差万别。况且不是每个人都能准确地表达出自己的需求,加之需求的表达、转述都会因表达方式和传递媒介而发生变形,这样一来,接收信息的一方自然很难把握真实的需求。 + +写专栏的时候,“极客时间”的编辑不断地告诉我,我的受众只有一个人,就是“你”。我心想,这个简单啊,因为面对的人最少嘛!可是,事实上证明,我又“错”了。 + +这个抽象的“你”,看起来只有一个,其实却是看不到、摸不着的许许多多的人。所以,这个其实是最难的。协议专栏上线10天,就有10000多人订阅,而订阅专栏的用户里,只有少数人会留言。所以,对于很多读者的真实情况,我都无从得知,你可能每天都听但是没有留言的习惯,也可能买了之后觉得我讲得不好,骂一句“这钱白花了”,然后再也不听。 + +所以,如何把控内容,写给广大未知受众,是我写这个专栏面临的最大挑战。而这里面,文章的深度、广度,音频的语调、语气,每一个细节都非常重要。 + +专栏文章是怎么写的? + +经过大纲和前几篇文稿的打磨,我对“极客时间”和专栏创作也有了更深的了解。我私下和很多人交流过一个问题,那就是,咱们平时聊一个话题的时候,有很多话可以说。但是真正去写一篇文章的时候,好像又没有什么可讲的,尤其是那些看起来很基础的内容。 + +我在写专栏的过程中,仔细思考过这样一个问题:很多人对某一领域或者行业研究得很深入,也有自己长期的实践,但是有多少人可以从感性认识上升到理性认知的高度呢? + +现在技术变化这么快,我们每个人的精力都是有限的,不少人学习新知识的方式就是看看书,看看博客、技术文章,或者听同事讲一下,了解个大概就觉得可以直接上手去做了。我也是这样的。可是一旦到写专栏的时候,基础掌握不扎实的问题一下子全都“暴露”出来了。 + +落到文字上的东西一定要是严谨的。所以,在写到很多细节的时候,我查了大量的资料,找到权威的书籍、官方文档、RFC里面的具体描述,有时候我甚至要做个实验,或者打开代码再看一下,才放心下笔。 + +尽管我对自己写文章有很多“完美倾向”的要求,但是这其实依旧是站在我自己的角度去看的。读者究竟想要看什么内容呢? + +太深入了,看不懂;太浅显了,也不行。太长了,负担太重;太短了,没有干货;同时,每篇文字还要自成一体,所有文章要是一个完整的知识体系。我发现,原来我不仅是对知识的了解没那么全面、具体,对用户阅读和倾听场景也没有过多的考虑。 + +除了写文字,专栏还要录音频,所以为了方便“听”,文章内不能放大量代码、实验。如果很多人在通勤路上听,而我把一张图片讲得天花乱坠,听的人却根本看不到,那肯定是不行的,所以写文章的时候,我还要把故事性、画面感都考虑进去,尽量详尽而不啰嗦。 + +把这些限制条件加起来之后,我发现,写专栏这件事儿,真的太不容易了。每篇文章看起来内容不多,但是都是费了很多心思的,这也是为什么很多老师说,写完专栏就像是过了火焰山。 + +专栏音频是怎么录的? + +说完写文章,我来说说录音频。我平时听播音员说话,感觉非常轻松,所以当时我毫不犹豫地就说,“我要自己录”。但是在录开篇词的时候,我就觉得这完全不是我想的那么回事啊! + +专栏的文章在录音的时候一定会有个“音频稿”,我一开始很不理解,我对着发布的稿件直接讲就好了啊,为什么还要特意准备一个供录音频的稿件啊? + +我在没有音频稿的情况下,自己试着“发挥”了几次,结果,我发现我的嘴会“吃”字,会反复讲一个内容而且表达不清,但是自己却经常毫无察觉,还会自己讲着讲着就收不住等等。 + +咱们平时说话的时候,会有很多口头语和重复的词语。面对面交流的时候,我们为什么没有注意这个问题呢?因为我们会更注重对方的表情、手势,但是一旦录成音频,这些“啰嗦”的地方就特别明显。 + +而有了音频稿之后,整个过程就严谨很多。如果哪句话说错了,看着稿件再说一遍就好了。而且,你会发现录音的时间大大缩短了,原来需要用十分钟,现在五分钟就可以很精炼地讲出来了。 + +有了稿子,那我是不是对着念就好了?这不是很容易吗?不,我又遇到了新的难题。 + +录音频的时候,我常常一个人关在密闭的房间里,对着显示器“读”,这和公共演讲肯定是不一样的。加上因为有写好的音频稿,我常常感觉束手束脚,找不到演讲那种有激情的感觉,很容易就变成了念课文。 + +为了同时满足自然和严谨,一方面我会先熟记“台词”;另一方面,每次录的时候,我都假想对面有个人,我在对着他缓缓地讲出来。讲到某些地方,我还会假想他对这个知识点是不是有疑问,这样就更加有互动感。 + +录音频这件事对我的改变非常大。我说话、演讲的时候变得更加严谨了。我会下意识地不去重复已经说过的话。一旦想重复,也闭嘴不发音,等想好了下一句再说。后面,我的录音也越来越顺利,一开始要录五六遍才能成功,后面基本一遍就过了。 + +创作专栏的过程还有许多事情,都是我很难得的记忆。我很佩服“极客时间”的编辑做专栏时的专业和认真。我也很庆幸,我没有固执地按照自己认为正确的方向和方式来做,而是尊重了他们的专业。很显然,他们没有我懂技术,但是他们比我更懂“你”。 + +专栏结束后,我回看这半年的准备和努力,我发现,无论对自己的领域多么熟悉,写这个专栏都让我又上升了一个新高度。 + +我知道很多技术人都喜欢分享,而写文章又是最容易实现的方式。写文章的时候,可以检验你对基础知识的掌握是否扎实,是不是有换位思考能力,能不能从感性认识上升到理性认知。 + +除此之外,我觉得最重要的一点是,在创作专栏文章的过程中,我学到了很多技术之外的东西,比如换位思考能力和细节把控的能力。 + +我在这里记下与“极客时间”的相识和相知。希望看到更多人在极客时间,分享自己的知识和见解。 + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/协议专栏特别福利答疑解惑1期.md b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑1期.md new file mode 100644 index 0000000..3c0da35 --- /dev/null +++ b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑1期.md @@ -0,0 +1,203 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 协议专栏特别福利 答疑解惑1期 + 你好,我是刘超。 + +首先,感谢大家关注并在留言区写下近3000条留言。留言太多,没有及时回复,一是每周写三篇文章压力真的挺大的。为了保质保量地产出,晚上和周末的时间基本上都搭进去了。二是很多人的留言非常有深度,水平很高,提的问题一两句话解释不清楚。 + +每一节结尾我基本都会留两个思考题,其中第一个问题是启发思考的,是对本节内容的延伸学习;第二个问题是为了引出下一节,下一节的内容其实就是答案。 + +所以我会回答一下每一节的第一个问题,并列出第一个同我的思路最相近的同学,并对留言中比较有代表性的问题,做一个统一的回答,顺便也实现之前要送知识图谱和奖励礼券的承诺。 + +当然,这并不能说明我的回答就是一定是正确的或者全面的,有很多同学的留言有非常大的信息量,甚至更广的思路,也对这些同学表示感谢。还有些同学指出了我的错误,也感谢你们。 + +《第1讲 | 为什么要学习网络协议?》 + +课后思考题 + +当网络包到达一个城关的时候,可以通过路由表得到下一个城关的 IP 地址,直接通过 IP地址找就可以了,为什么还要通过本地的MAC地址呢? + + + +徐良红同学说的比较接近。在网络包里,有源IP地址和目标IP地址、源MAC地址和目标MAC地址。从路由表中取得下一跳的IP地址后,应该把这个地址放在哪里呢?如果放在目标IP地址里面,到了城关,谁知道最终的目标在哪里呢?所以要用MAC地址。 + +所谓的下一跳,看起来是IP地址,其实是要通过ARP得到MAC地址,将下一跳的MAC地址放在目标MAC地址里面。 + +留言问题 + +1.MAC地址可以修改吗? + + + + + +我查了一下,MAC(Media Access Control,介质访问控制)地址,也叫硬件地址,长度是48比特(6字节),由16进制的数字组成,分为前24位和后24位。 + +前24位叫作组织唯一标志符(Organizationally Unique Identifier,OUI),是由IEEE的注册管理机构给不同厂家分配的代码,用于区分不同的厂家。后24位是厂家自己分配的,称为扩展标识符。同一个厂家生产的网卡中MAC地址后24位是不同的。 + +也就是说,MAC本来设计为唯一性的,但是后来设备越来越多,而且还有虚拟化的设备和网卡,有很多工具可以修改,就很难保证不冲突了。但是至少应该保持一个局域网内是唯一的。 + +MAC的设计,使得即便不能保证绝对唯一,但是能保证一个局域网内出现冲突的概率很小。这样,一台机器启动的时候,就能够在没有IP地址的情况下,先用MAC地址进行通信,获得IP地址。 + +好在MAC地址是工作在一个局域网中的,因而即便出现了冲突,网络工程师也能够在自己的范围内很快定位并解决这个问题。这就像我们生成UUID或者哈希值,大部分情况下是不会冲突的,但是如果碰巧出现冲突了,采取一定的机制解决冲突就好。 + +2.TCP重试有没有可能导致重复下单? + + + +答案是不会的。这个在[TCP]那一节有详细的讲解。因为TCP层收到了重复包之后,TCP层自己会进行去重,发给应用层、HTTP层。还是一个唯一的下单请求,所以不会重复下单。 + +那什么时候会导致重复下单呢?因为网络原因或者服务端错误,导致TCP连接断了,这样会重新发送应用层的请求,也即HTTP的请求会重新发送一遍。 + +如果服务端设计的是无状态的,它记不住上一次已经发送了一次请求。如果处理不好,就会导致重复下单,这就需要服务端除了实现无状态,还需要根据传过来的订单号实现幂等,同一个订单只处理一次。 + +还会有的现象是请求被黑客拦截,发送多次,这在HTTPS层可以有很多种机制,例如通过 Timestamp和Nonce随机数联合起来,然后做一个不可逆的签名来保证。 + +3.TCP报平安的包是原路返回吗? + + + +谢谢语鬼同学的指正。这里的比喻不够严谨,容易让读者产生误会,这里的原路返回的意思是原样返回,也就是返回也是这个过程,不一定是完全一样的路径。 + +4.IP地址和MAC地址的关系? + + + +芒果同学的理解非常准确,讲[IP和MAC的关系]的时候说了这个问题。IP是有远程定位功能的,MAC是没有远程定位功能的,只能通过本地ARP的方式找到。 + +我个人认为,即便有了IPv6,也不会改变当前的网络分层模式,还是IP层解决远程定位问题,只不过改成IPv6了,到了本地,还是通过MAC。 + +5.如果最后一跳的时候,IP改变了怎么办? + + + +对于IP层来讲,当包到达最后一跳的时候,原来的IP不存在了。比如网线拔掉了,或者服务器直接宕机了,则ARP就找不到了,所以这个包就会发送失败了。对于IP层的工作就结束了。 + +但是IP层之上还有TCP层,TCP会重试的,包还是会重新发送,但是如果服务器没有启动起来,超过一定的次数,最终放弃。 + +如果服务器重启了,IP还是原来的IP地址,这个时候TCP重新发送的一个包的时候,ARP是能够得到这个地址的,因而会发到这台机器上来,但是机器上面没有启动服务端监听那个端口,于是会发送ICMP端口不可达。 + +如果服务器重启了,服务端也重新启动了,也在监听那个端口了,这个时候TCP的服务端由于是新的,Sequence Number根本对不上,说明不是原来的连接,会发送RST。 + +那有没有可能有特殊的场景Sequence Number也能对的上呢?按照Sequence Number的生成算法,是不可能的。 + +但是有一个非常特殊的方式,就是虚拟机的热迁移,从一台物理机迁移到另外一台物理机,IP不变,MAC不变,内存也拷贝过去,Sequence Number在内存里面也保持住了,在迁移的过程中会丢失一两个包,但是从TCP来看,最终还是能够连接成功的。 + +6.TCP层报平安,怎么确认浏览器收到呢? + + + +TCP报平安,只能保证TCP层能够收到,不保证浏览器能够收到。但是可以想象,如果浏览器是你写的一个程序,你也是通过socket编程写的,你是通过socket,建立一个TCP的连接,然后从这个连接里面读取数据,读取的数据就是TCP层确认收到的。 + +这个读取的动作是本地系统调用,大部分情况下不会失败的。如果读取失败呢,当然本地会报错,你的socket读取函数会返回错误,如果你是浏览器程序的实现者,你有两种选择,一个是将错误报告给用户,另一个是重新发送一次请求,获取结果显示给用户。 + +7.ARP协议属于哪一层? + + + +ARP属于哪个层,一直是有争议的。比如《TCP/IP详解》把它放在了二层和三层之间,但是既然是协议,只要大家都遵守相同的格式、流程就可以了,在实际应用的时候,不会有歧义的,唯一有歧义的是参加各种考试,让你做选择题,ARP属于哪一层?平时工作中咱不用纠结这个。 + +《第2讲 | 网络分层的真实含义是什么?》 + +课后思考题 + +如果你也觉得总经理和员工的比喻不恰当,你有更恰当的比喻吗? + + + + + +我觉得,寄快递和寄信这两个比喻都挺好的。关键是有了封装和解封装的过程。有的同学举了爬楼,或者公司各层之间的沟通,都无法体现封装和解封装的过程。 + +留言问题 + +1.为什么要分层? + + + +是的,仅仅用复杂性来解释分层,太过牵强了。 + + + +其实这是一个架构设计的通用问题,不仅仅是网络协议的问题。一旦涉及到复杂的逻辑,或者软件需求需要经常变动,一般都会通过分层来解决问题。 + +假如我们将所有的代码都写在一起,但是产品经理突然想调整一下界面,这背后的业务逻辑变不变,那要不要一起修改呢?所以会拆成两层,把UI层从业务逻辑中分离出来,调用API来进行组合。API不变,仅仅界面变,是不是就不影响后台的代码了? + +为什么要把一些原子的API放在基础服务层呢?将数据库、缓存、搜索引擎等,屏蔽到基础服务层以下,基础服务层之上的组合逻辑层、API层都只能调用基础服务层的API,不能直接访问数据库。 + +比如我们要将Oracle切换成MySQL。MySQL有一个库,分库分表成为4个库。难道所有的代码都要修改吗?当然只要把基础服务层屏蔽,提供一致的接口就可以了。 + +网络协议也是这样的。有的想基于TCP,自己不操心就能够保证到达;有的想自己实现可靠通信,不基于TCP,而使用UDP。一旦分了层就好办了,定制化后要依赖于下一层的接口,只要实现自己的逻辑就可以了。如果TCP的实现将所有的逻辑耦合在了整个七层,不用TCP的可靠传输机制都没有办法。 + +2.层级之间真实的调用方式是什么样的? + + + +如果文中是一个逻辑图,这个问题其实已经到实现层面上来了,需要看TCP/IP的协议栈代码了。这里首先推荐一本书《深入理解Linux网络技术内幕》。 + +其实下层的协议知道上层协议的,因为在每一层的包头里面,都会有上一层是哪个协议的标识,所以不是一个回调函数,每一层的处理函数都会在操作系统启动的时候,注册到内核的一个数据结构里面,但是到某一层的时候,是通过判断到底是哪一层的哪一个协议,然后去找相应的处理函数去调用。 + +调用的大致过程我这里再讲一下。由于TCP比较复杂,我们以UDP为例子,其实发送的包就是一个sk_buff结构。这个在[Socket]那一节讲过。 + +int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) + + +接着,UDP层会调用IP层的函数。 + +int ip_send_skb(struct net *net, struct sk_buff *skb) + + +然后,IP层通过路由判断,最终将包发给下一层。 + +int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb) + + +发送的时候,要进行ARP。如果有MAC,则调用二层的函数,neigh其实就是邻居系统,是二层的意思。 + +int neigh_output(struct neighbour *n, struct sk_buff *skb) + + +接收的时候,会调用这里的接收函数。 + +int netif_receive_skb(struct sk_buff *skb) + + +这个函数会根据是ARP或者IP等,选择调用不同的函数。如果是IP协议的话,就调用这里的函数。 + +int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) + + +这里也有路由判断。如果是本地的,则继续往上提交这个结构。 + +int ip_local_deliver(struct sk_buff *skb) + + +接着,还是根据IP头里面的协议号,来判断是什么协议,从而调用什么函数。下面这个是对UDP的调用。 + +int udp_rcv(struct sk_buff *skb) + + +3.什么情况下会有下层没上层? + + + +有时候我们自己写应用的时候,不一定是直接调用应用层协议的接口,例如HTTP等,而是自己写Socket编程,来约定应用层的协议。再如,ping也是一个应用,但是它没有用传输层的协议,而是用了ICMP的协议。 + + + +最后,感谢留言次数前15名的同学,谢谢你们持之以恒的学习,相信你们一定有自己的收获。(统计数据截止到2018年8月8日) + + + +同时感谢第1讲、第2讲中对内容有深度思考和提出问题的同学。我会为你们送上奖励礼券和知识图谱。(稍后运营同学会发送短信通知。) + +欢迎你继续提问! + + + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/协议专栏特别福利答疑解惑2期.md b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑2期.md new file mode 100644 index 0000000..823fef4 --- /dev/null +++ b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑2期.md @@ -0,0 +1,239 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 协议专栏特别福利 答疑解惑2期 + 你好,我是刘超。 + +第二期答疑涵盖第3讲至第6讲的内容。我依旧对课后思考题和留言中比较有代表性的问题作出回答。你可以点击文章名,回到对应的章节复习,也可以继续在留言区写下你的疑问,我会持续不断地解答。希望对你有帮助。 + +《第3讲 | ifconfig:最熟悉又陌生的命令行》 + +课后思考题 + +你知道 net-tools 和 iproute2 的“历史”故事吗? + + + +这个问题的答案,盖同学已经写的比较全面了。具体的对比,我这里推荐一篇文章https://linoxide.com/linux-command/use-ip-command-linux/,感兴趣的话可以看看。 + +留言问题 + +1.A、B、C类地址的有效地址范围是多少? + + + +我在写的时候,没有考虑这么严谨,平时使用地址的时候,也是看个大概的范围。所以这里再回答一下。 + +A类IP的地址第一个字段范围是0~127,但是由于全0和全1的地址用作特殊用途,实际可指派的范围是1~126。所以我仔细查了一下,如果较真的话,你在答考试题的时候可以说,A类地址范围和A类有效地址范围。 + +2.网络号、IP地址、子网掩码和广播地址的先后关系是什么? + + + +当在一个数据中心或者一个办公室规划一个网络的时候,首先是网络管理员规划网段,一般是根据将来要容纳的机器数量来规划,一旦定了,以后就不好变了。 + +假如你在一个小公司里,总共就没几台机器,对于私有地址,一般选择192.168.0.0/24就可以了。 + +这个时候先有的是网络号。192.168.0就是网络号。有了网络号,子网掩码同时也就有了,就是前面都是网络号的是1,其他的是0,广播地址也有了,除了网络号之外都是1。 + +当规划完网络的时候,一般这个网络里面的第一个、第二个地址被默认网关DHCP服务器占用,你自己创建的机器,只要和其他的不冲突就可以了,当然你也可以让DHCP服务自动配置。 + +规划网络原来都是网络管理员的事情。有了公有云之后,一般有个概念虚拟网络(VPC),鼠标一点就能创建一个网络,网络完全软件化了,任何人都可以做网络规划。 + +3.组播和广播的意义和原理是什么? + + + +C类地址的主机号8位,去掉0和255,就只有254个了。 + +在《TCP/IP详解》这本书里面,有两章讲了广播、多播以及IGMP。广播和组播分为两个层面,其中MAC层有广播和组播对应的地址,IP层也有自己的广播地址和组播地址。 + +广播相对比较简单,MAC层的广播为ff:ff:ff:ff:ff:ff,IP层指向子网的广播地址为主机号为全1且有特定子网号的地址。 + +组播复杂一些,MAC层中,当地址中最高字节的最低位设置为1时,表示该地址是一个组播地址,用十六进制可表示为01:00:00:00:00:00。IP层中,组播地址为D类IP地址,当IP地址为组播地址的时候,有一个算法可以计算出对应的MAC层地址。 + +多播进程将目的IP地址指明为多播地址,设备驱动程序将它转换为相应的以太网地址,然后把数据发送出去。这些接收进程必须通知它们的IP层,它们想接收的发给定多播地址的数据报,并且设备驱动程序必须能够接收这些多播帧。这个过程就是“加入一个多播组”。 + +当多播跨越路由器的时候,需要通过IGMP协议告诉多播路由器,多播数据包应该如何转发。 + +4.MTU 1500的具体含义是什么? + + + +MTU(Maximum Transmission Unit,最大传输单元)是二层的一个定义。以以太网为例,MTU为1500个Byte,前面有6个Byte的目标MAC地址,6个Byte的源MAC地址,2个Byte的类型,后面有4个Byte的CRC校验,共1518个Byte。 + +在IP层,一个IP数据报在以太网中传输,如果它的长度大于该MTU值,就要进行分片传输。如果不允许分片DF,就会发送ICMP包,这个在[ICMP]那一节讲过。 + +在TCP层有个MSS(Maximum Segment Size,最大分段大小),它等于MTU减去IP头,再减去TCP头。即在不分片的情况下,TCP里面放的最大内容。 + +在HTTP层看来,它的body没有限制,而且在应用层看来,下层的TCP是一个流,可以一直发送,但其实是会被分成一个个段的。 + +《第4讲 | DHCP与PXE:IP是怎么来的,又是怎么没的》 + +课后思考题 + +PXE 协议可以用来安装操作系统,但是如果每次重启都安装操作系统,就会很麻烦。你知道如何使得第一次安装操作系统,后面就正常启动吗? + + + +一般如果咱们手动安装一台电脑的时候,都是有启动顺序的,如果改为硬盘启动,就没有问题了。 + + + +好在服务器一般都提供IPMI接口,可以通过这个接口启动、重启、设置启动模式等等远程访问,这样就可以批量管理一大批机器。 + + + +这里提到Cobbler,这是一个批量安装操作系统的工具。在OpenStack里面,还有一个Ironic,也是用来管理裸机的。有兴趣的话可以研究一下。 + +留言问题 + +1.在DHCP网络里面,手动配置IP地址会冲突吗? + + + + + +在一个DHCP网络里面,如果某一台机器手动配置了一个IP地址,并且在DHCP管理的网段里的话,DHCP服务器是会将这个地址分配给其他机器的。一旦分配了,ARP的时候,就会收到两个应答,IP地址就冲突了。 + +当发生这种情况的时候,应该怎么办呢?DHCP的过程虽然没有明确如何处理,但是DHCP的客户端和服务器都可以添加相应的机制来检测冲突。 + +如果由客户端来检测冲突,一般情况是,客户端在接受分配的IP之前,先发送一个ARP,看是否有应答,有就说明冲突了,于是发送一个DHCPDECLINE,放弃这个IP地址。 + +如果由服务器来检测冲突,DHCP服务器会发送ping,来看某个IP是否已经被使用。如果被使用了,它就不再将这个IP分配给其他的客户端了。 + +2.DHCP的Offer和ACK应该是单播还是广播呢? + + + + + +没心没肺 回答得很正确。 + +这个我们来看DHCP的RFC,我截了个图放在这儿: + + + +这里面说了几个问题。 + +正常情况下,一旦有了IP地址,DHCP Server还是希望通过单播的方式发送OFFER和ACK。但是不幸的是,有的客户端协议栈的实现,如果还没有配置IP地址,就使用单播。协议栈是不接收这个包的,因为OFFER和ACK的时候,IP地址还没有配置到网卡上。 + +所以,一切取决于客户端的协议栈的能力,如果没配置好IP,就不能接收单播的包,那就将BROADCAST设为1,以广播的形式进行交互。 + +如果客户端的协议栈实现很厉害,即便是没有配置好IP,仍然能够接受单播的包,那就将BROADCAST位设置为0,就以单播的形式交互。 + +3.DHCP如何解决内网安全问题? + + + +其实DHCP协议的设计是基于内网互信的基础来设计的,而且是基于UDP协议。但是这里面的确是有风险的。例如一个普通用户无意地或者恶意地安装一台DHCP服务器,发放一些错误或者冲突的配置;再如,有恶意的用户发出很多的DHCP请求,让DHCP服务器给他分配大量的IP。 + +对于第一种情况,DHCP服务器和二层网络都是由网管管理的,可以在交换机配置只有来自某个DHCP服务器的包才是可信的,其他全部丢弃。如果有SDN,或者在云中,非法的DHCP包根本就拦截到虚拟机或者物理机的出口。 + +对于第二种情况,一方面进行监控,对DHCP报文进行限速,并且异常的端口可以关闭,一方面还是SDN或者在云中,除了被SDN管控端登记过的IP和MAC地址,其他的地址是不允许出现在虚拟机和物理机出口的,也就无法模拟大量的客户端。 + +《第5讲 | 从物理层到MAC层:如何在宿舍里自己组网玩联机游戏?》 + +课后思考题 + +1.在二层中我们讲了 ARP 协议,即已知 IP 地址求 MAC;还有一种 RARP 协议,即已知 MAC 求 IP 的,你知道它可以用来干什么吗? + + + +2.如果一个局域网里面有多个交换机,ARP 广播的模式会出现什么问题呢? + +盖还说出了环路的问题。 + + + +没心没肺不但说明了问题,而且说明了方案。 + + + +《第6讲 | 交换机与VLAN:办公室太复杂,我要回学校》 + +课后思考题 + +STP 协议能够很好地解决环路问题,但是也有它的缺点,你能举几个例子吗? + + + + + +STP的主要问题在于,当拓扑发生变化,新的配置消息要经过一定的时延才能传播到整个网络。 + +由于整个交换网络只有一棵生成树,在网络规模比较大的时候会导致较长的收敛时间,拓扑改变的影响面也较大,当链路被阻塞后将不承载任何流量,造成了极大带宽浪费。 + +留言问题 + +1.每台交换机的武力值是什么样的? + + + + + +当一台交换机加入或者离开网络的时候,都会造成网络拓扑变化,这个时候检测到拓扑变化的网桥会通知根网桥,根网桥会通知所有的网桥拓扑发生变化。 + +网桥的ID是由网桥优先级和网桥MAC地址组成的,网桥ID最小的将成为网络中的根桥。默认配置下,网桥优先级都一样,默认优先级是32768。这个时候MAC地址最小的网桥成为根网桥。但是如果你想设置某台为根网桥,就配置更小的优先级即可。 + +在优先级向量里面,Root Bridge ID就是根网桥的ID,Bridge ID是网桥的ID,Port ID就是一个网桥上有多个端口,端口的ID。 + + + +按照RFC的定义,ROOT PATH COST是和出口带宽相关的,具体的数据如下: + + + +2.图中的LAN指的是什么? + + + +在这一节中,这两张图引起了困惑。 + + + + + +本来是为了讲二层的原理,做了个抽象的图,结果引起了大家的疑问,所以这里需要重新阐述一下。 + +首先,这里的LAN1、LAN2、LAN 3的说法的确不准确,因为通过网桥或者交换机连接,它们还是属于一个LAN,其实这是三个物理网络,通过网桥或者交换机连接起来,形成一个二层的LAN。 + +对于一层,也即物理层的设备,主要使用集线器(Hub),这里我们就用Hub将物理层连接起来。 + +于是我新画了两个图。 + + + + + +在这里,我用Hub将不同的机器连接在一起,形成一个物理段,而非LAN。 + +3.在MAC地址已经学习的情况下,ARP会广播到没有IP的物理段吗? + + + + + +首先谢谢这两位同学指出错误,这里ARP的目标地址是广播的,所以无论是否进行地址学习,都会广播,而对于某个MAC的访问,在没有地址学习的时候,是转发到所有的端口的,学习之后,只会转发到有这个MAC的端口。 + +4.802.1Q VLAN 和Port-based VLAN有什么区别? + + + +所谓Port-based VLAN,一般只在一台交换机上起作用,比如一台交换机,10个口,1、3、5、7、9属于VLAN 10。1发出的包,只有3、5、7、9能够收到,但是从这些口转发出去的包头中,并不带VLAN ID。 + +而802.1Q的VLAN,出了交换机也起作用,也就是说,一旦打上某个VLAN,则出去的包都带这个VLAN,也需要链路上的交换机能够识别这个VLAN,进行转发。 + + + +感谢第3讲至第6讲中对内容有深度思考和提出问题的同学。我会为你们送上奖励礼券和知识图谱。(稍后运营同学会发送短信通知。) + +欢迎你继续提问! + + + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/协议专栏特别福利答疑解惑3期.md b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑3期.md new file mode 100644 index 0000000..a83ce8b --- /dev/null +++ b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑3期.md @@ -0,0 +1,340 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 协议专栏特别福利 答疑解惑3期 + 你好,我是刘超。 + +第三期答疑涵盖第7讲至第13讲的内容。我依旧对课后思考题和留言中比较有代表性的问题作出回答。你可以点击文章名,回到对应的章节复习,也可以继续在留言区写下你的疑问,我会持续不断地解答。希望对你有帮助。 + +《第7讲 | ICMP与ping:投石问路的侦察兵》 + +课后思考题 + +当发送的报文出问题的时候,会发送一个ICMP的差错报文来报告错误,但是如果 ICMP 的差错报文也出问题了呢? + +我总结了一下,不会导致产生ICMP差错报文的有: + + +ICMP差错报文(ICMP查询报文可能会产生ICMP差错报文); + +目的地址是广播地址或多播地址的IP数据报; + +作为链路层广播的数据报; + +不是IP分片的第一片; + +源地址不是单个主机的数据报。这就是说,源地址不能为零地址、环回地址、广播地址或多播地址。 + + +留言问题 + +1.ping使用的是什么网络编程接口? + + + +咱们使用的网络编程接口是Socket,对于ping来讲,使用的是ICMP,创建Socket如下: + +socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) + + +SOCK_RAW就是基于IP层协议建立通信机制。 + +如果是TCP,则建立下面的Socket: + +socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) + + +如果是UDP,则建立下面的Socket: + +socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + + +2.ICMP差错报文是谁发送的呢? + +我看留言里有很多人对这个问题有疑惑。ICMP包是由内核返回的,在内核中,有一个函数用于发送ICMP的包。 + +void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info); + + +例如,目标不可达,会调用下面的函数。 + +icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); + + +当IP大小超过MTU的时候,发送需要分片的ICMP。 + +if (ip_exceeds_mtu(skb, mtu)) { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); + goto drop; + } + + +《第8讲 | 世界这么大,我想出网关:欧洲十国游与玄奘西行》 + +课后思考题 + +当在你家里要访问 163 网站的时候,你的包需要 NAT 成为公网 IP,返回的包又要 NAT 成你的私有 IP,返回包怎么知道这是你的请求呢?它怎么能这么智能地 NAT 成了你的 IP 而非别人的 IP 呢? + +这是个比较复杂的事情。在讲云中网络安全里的iptables时,我们讲过conntrack功能,它记录了SNAT一去一回的对应关系。 + +如果编译内核时开启了连接跟踪选项,那么Linux系统就会为它收到的每个数据包维持一个连接状态,用于记录这条数据连接的状态。 + + + +根据咱们学过的Netfilter的流程图,我们知道,网络包有三种路径: + + +发给我的,从PREROUTING到INPUT,我就接收了; + +我发给别人的,从OUTPUT到POSTROUTING,就发出去的; + +从我这里经过的,从PREROUTING到FORWARD到POSTROUTING。 + + +如果要跟踪一个网络包,对于每一种路径,都需要设置两个记录点,相当于打两次卡,这样内核才知道这个包的状态。 + +对于这三种路径,打卡的点是这样设置的: + + +发给我的,在PREROUTING调用ipv4_conntrack_in,创建连接跟踪记录;在INPUT调用ipv4_confirm,将这个连接跟踪记录挂在内核的连接跟踪表里面。为什么不一开始就挂在内核的连接跟踪表里面呢?因为有filter表,一旦把包过滤了,也就是丢弃了,那根本没必要记录这个连接了。 + +我发给别人的,在OUTPUT调用ipv4_conntrack_local,创建连接跟踪记录,在POSTROUTING调用ipv4_confirm,将这个连接跟踪记录挂在内核的连接跟踪表里面。 + +从我这里经过的,在PREROUTING调用ipv4_conntrack_in,创建连接跟踪记录,在POSTROUTING调用ipv4_confirm,将这个连接跟踪记录挂在内核的连接跟踪表里面。 + + +网关主要做转发,这里主要说的是NAT网关,因而我们重点来看“从我这里经过的”这种场景,再加上要NAT,因而将NAT的过程融入到连接跟踪的过程中来: + + +如果是PREROUTING的时候,先调用ipv4_conntrack_in,创建连接跟踪记录; + +如果是PREROUTING的时候,有NAT规则,则调用nf_nat_ipv4_in进行地址转换; + +如果是POSTROUTING的时候,有NAT规则,则调用nf_nat_ipv4_out进行地址转换; + +如果是POSTROUTING的时候,调用ipv4_confirm,将这个连接跟踪记录挂在内核的连接跟踪表里面。 + + +接下来,我们来看,在这个过程中涉及到的数据结构:连接跟踪记录、连接跟踪表。 + +在前面讲网络包处理的时候,我们说过,每个网络包都是一个struct sk_buff,它有一个成员变量_nfct指向一个连接跟踪记录struct nf_conn。当然当一个网络包刚刚进来的时候,是不会指向这么一个结构的,但是这个网络包肯定属于某个连接,因而会去连接跟踪表里面去查找,之后赋值给sk_buff的这个成员变量。没找到的话,就说明是一个新的连接,然后会重新创建一个。 + +连接跟踪记录里面有几个重要的东西: + + +nf_conntrack其实才是_nfct变量指向的地址,但是没有关系,学过C++的话应该明白,对于结构体来讲,nf_conn和nf_conntrack的起始地址是一样的; + +tuplehash虽然是数组,但是里面只有两个,IP_CT_DIR_ORIGINAL为下标0,表示连接的发起方向,IP_CT_DIR_REPLY为下标1,表示连接的回复方向。 + +struct nf_conn { + …… + struct nf_conntrack ct_general; + …… + struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; + …… + unsigned long status; + …… +} + + +在这里面,最重要的是nf_conntrack_tuple_hash的数组。nf_conn是这个网络包对应的一去一回的连接追踪记录,但是这个记录是会放在一个统一的连接追踪表里面的。 + +连接跟踪表nf_conntrack_hash是一个数组,数组中的每一项都是一个双向链表的头,每一项后面都挂着一个双向链表,链表中的每一项都是这个结构。 + +这个结构的第一项是链表的链,nf_conntrack_tuple是用来标识是否同一个连接。 + +从上面可以看出来,连接跟踪表是一个典型的链式哈希表的实现。 + +每当有一个网络包来了的时候,会将网络包中sk_buff中的数据提取出来,形成nf_conntrack_tuple,并根据里面的内容计算哈希值。然后需要在哈希表中查找,如果找到,则说明这个连接出现过;如果没找到,则生成一个插入哈希表。 + +通过nf_conntrack_tuple里面的内容,可以唯一地标识一个连接: + + +src:包含源IP地址;如果是TCP或者UDP,包含源端口;如果是ICMP,包含的是ID; + +dst:包含目标IP地址;如果是TCP或者UDP,包含目标端口;如果是ICMP,包含的是type, code。 + + +有了这些数据结构,我们接下来看这一去一回的过程。 + +当一个包发出去的时候,到达这个NAT网关的时候,首先经过PREROUTING的时候,先调用ipv4_conntrack_in。这个时候进来的包sk_buff为: {源IP:客户端IP,源端口:客户端port,目标IP:服务端IP,目标端口:服务端port},将这个转换为nf_conntrack_tuple,然后经过哈希运算,在连接跟踪表里面查找,发现没有,说明这是一个新的连接。 + +于是,创建一个新的连接跟踪记录nf_conn,这里面有两个nf_conntrack_tuple_hash: + + +一去:{源IP:客户端IP,源端口:客户端port,目标IP:服务端IP,目标端口:服务端port}; + +一回:{源IP:服务端IP,源端口:服务端port,目标IP:客户端IP,目标端口:客户端port}。 + + +接下来经过FORWARD过程,假设包没有被filter掉,于是要转发出去,进入POSTROUTING的过程,有NAT规则,则调用nf_nat_ipv4_out进行地址转换。这个时候,源地址要变成NAT网关的IP地址,对于masquerade来讲,会自动选择一个公网IP地址和一个随机端口。 + +为了让包回来的时候,能找到连接跟踪记录,需要修改两个nf_conntrack_tuple_hash中回来的那一项为:{源IP:服务端IP,源端口:服务端port,目标IP:NAT网关IP,目标端口:随机端口}。 + +接下来要将网络包真正发出去的时候,除了要修改包里面的源IP和源端口之外,还需要将刚才的一去一回的两个nf_conntrack_tuple_hash放入连接跟踪表这个哈希表中。 + +当网络包到达服务端,然后回复一个包的时候,这个包sk_buff为:{源IP:服务端IP,源端口:服务端port,目标IP:NAT网关IP,目标端口:随机端口}。 + +将这个转换为nf_conntrack_tuple后,进行哈希运算,在连接跟踪表里面查找,是能找到相应的记录的,找到nf_conntrack_tuple_hash之后,Linux会提供一个函数。 + +static inline struct nf_conn * +nf_ct_tuplehash_to_ctrack(const struct nf_conntrack_tuple_hash *hash) +{ + return container_of(hash, struct nf_conn, + tuplehash[hash->tuple.dst.dir]); +} + + +可以通过nf_conntrack_tuple_hash找到外面的连接跟踪记录nf_conn,通过这个可以找到来方向的那个nf_conntrack_tuple_hash,{源IP:客户端IP,源端口:客户端port,目标IP:服务端IP,目标端口:服务端port},这样就能够找到客户端的IP和端口,从而可以NAT回去。 + +留言问题 + +1.NAT能建立多少连接? + + + +SNAT多用于内网访问外网的场景,鉴于conntrack是由{源IP,源端口,目标IP,目标端口},hash后确定的。 + +如果内网机器很多,但是访问的是不同的外网,也即目标IP和目标端口很多,这样内网可承载的数量就非常大,可不止65535个。 + +但是如果内网所有的机器,都一定要访问同一个目标IP和目标端口,这样源IP如果只有一个,这样的情况下,才受65535的端口数目限制,根据原理,一种方法就是多个源IP,另外的方法就是多个NAT网关,来分摊不同的内网机器访问。 + +如果你使用的是公有云,65535台机器,应该放在一个VPC里面,可以放在多个VPC里面,每个VPC都可以有自己的NAT网关。 + + + +其实SNAT的场景是内网访问外网,存在端口数量的问题,也是所有的机器都访问一个目标地址的情况。 + +如果是微信这种场景,应该是服务端在数据中心内部,无论多少长连接,作为服务端监听的都是少数几个端口,是DNAT的场景,是没有端口数目问题的,只有一台服务器能不能维护这么多连接,因而在NAT网关后面部署多个nginx来分摊连接即可。 + +2.公网IP和私网IP需要一一绑定吗? + + + +公网IP是有限的,如果使用公有云,需要花钱去买。但是不是每一个虚拟机都要有一个公网IP的,只有需要对外提供服务的机器,也即接入层的那些nginx需要公网IP,没有公网IP,使用SNAT,大家共享SNAT网关的公网IP地址,也是能够访问的外网的。 + +我看留言中的困惑点都在于,要区分内主动发起访问外,还是外主动发起访问内,是访问同一个服务端,还是访问一大批服务端。这里就很明白了。 + +《第9讲 | 路由协议:西出网关无故人,敢问路在何方》 + +课后思考题 + +路由协议要在路由器之间交换信息,这些信息的交换还需要走路由吗?不是死锁了吗? + + + +OSPF是直接基于IP协议发送的,而且OSPF的包都是发给邻居的,也即只有一跳,不会中间经过路由设备。BGP是基于TCP协议的,在BGP peer之间交换信息。 + +留言问题 + +1.多线BGP机房是怎么回事儿? + + + +BGP主要用于互联网AS自治系统之间的互联,BGP的最主要功能在于控制路由的传播和选择最好的路由。各大运营商都具有AS号,全国各大网络运营商多数都是通过BGP协议与自身的AS来实现多线互联的。 + +使用此方案来实现多线路互联,IDC需要在CNNIC(中国互联网信息中心)或APNIC(亚太网络信息中心)申请自己的IP地址段和AS号,然后通过BGP协议将此段IP地址广播到其它的网络运营商的网络中。 + +使用BGP协议互联后,网络运营商的所有骨干路由设备将会判断到IDC机房IP段的最佳路由,以保证不同网络运营商用户的高速访问。 + +《第10讲 | UDP协议:因性善而简单,难免碰到“城会玩”》 + +课后思考题 + +都说 TCP 是面向连接的,在计算机看来,怎么样才算一个连接呢? + +赵强强在留言中回答的是正确的。这是TCP的两端为了维护连接所保持的数据结构。 + + + + + +《第11讲 | TCP协议(上):因性恶而复杂,先恶后善反轻松》 + +课后思考题 + +TCP 的连接有这么多的状态,你知道如何在系统中查看某个连接的状态吗? + + + +留言问题 + +1.TIME_WAIT状态太多是怎么回事儿? + + + + + +如果处于TIMEWAIT状态,说明双方建立成功过连接,而且已经发送了最后的ACK之后,才会处于这个状态,而且是主动发起关闭的一方处于这个状态。 + +如果存在大量的TIMEWAIT,往往是因为短连接太多,不断的创建连接,然后释放连接,从而导致很多连接在这个状态,可能会导致无法发起新的连接。解决的方式往往是: + + +打开tcp_tw_recycle和tcp_timestamps选项; + +打开tcp_tw_reuse和tcp_timestamps选项; + +程序中使用SO_LINGER,应用强制使用rst关闭。 + + +当客户端收到Connection Reset,往往是收到了TCP的RST消息,RST消息一般在下面的情况下发送: + + +试图连接一个未被监听的服务端; + +对方处于TIMEWAIT状态,或者连接已经关闭处于CLOSED状态,或者重新监听seq num不匹配; + +发起连接时超时,重传超时,keepalive超时; + +在程序中使用SO_LINGER,关闭连接时,放弃缓存中的数据,给对方发送RST。 + + +2.起始序列号是怎么计算的,会冲突吗? + +有同学在留言中问了几个问题。Ender0224的回答非常不错。 + + + + + +起始ISN是基于时钟的,每4毫秒加一,转一圈要4.55个小时。 + +TCP初始化序列号不能设置为一个固定值,因为这样容易被攻击者猜出后续序列号,从而遭到攻击。 RFC1948中提出了一个较好的初始化序列号ISN随机生成算法。 + +ISN = M + F (localhost, localport, remotehost, remoteport) + +M是一个计时器,这个计时器每隔4毫秒加1。F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证Hash算法不能被外部轻易推算得出,用MD5算法是一个比较好的选择。 + +《第12讲 | TCP协议(下):西行必定多妖孽,恒心智慧消磨难》 + +课后思考题 + +TCP的BBR听起来很牛,你知道它是如何达到这个最优点的吗? + + + +《第13讲 | 套接字Socket:Talk is cheap, show me the code》 + +课后思考题 + +epoll是Linux上的函数,那你知道Windows上对应的机制是什么吗?如果想实现一个跨平台的程序,你知道应该怎么办吗? + + + +epoll是异步通知,当事件发生的时候,通知应用去调用IO函数获取数据。IOCP异步传输,当事件发生时,IOCP机制会将数据直接拷贝到缓冲区里,应用可以直接使用。 + +如果跨平台,推荐使用libevent库,它是一个事件通知库,适用于Windows、Linux、BSD等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。 + + + +感谢第7讲至第13讲中对内容有深度思考和提出问题的同学。我会为你们送上奖励礼券和知识图谱。(稍后运营同学会发送短信通知。) + +欢迎你继续提问! + + + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/协议专栏特别福利答疑解惑4期.md b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑4期.md new file mode 100644 index 0000000..d9b75a9 --- /dev/null +++ b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑4期.md @@ -0,0 +1,211 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 协议专栏特别福利 答疑解惑4期 + 你好,我是刘超。 + +第四期答疑涵盖第14讲至第21讲的内容。我依旧对课后思考题和留言中比较有代表性的问题作出回答。你可以点击文章名,回到对应的章节复习,也可以继续在留言区写下你的疑问,我会持续不断地解答。希望对你有帮助。 + +《第14讲 | HTTP协议:看个新闻原来这么麻烦》 + +课后思考题 + +QUIC是一个精巧的协议,所以它肯定不止今天我提到的四种机制,你知道还有哪些吗? + +云学讲了一个QUIC的特性。 + + + +QUIC还有其他特性,一个是快速建立连接。这个我放在下面HTTPS的时候一起说。另一个是拥塞控制,QUIC协议当前默认使用了TCP协议的CUBIC(拥塞控制算法)。 + +你还记得TCP的拥塞控制算法吗?每当收到一个ACK的时候,就需要调整拥塞窗口的大小。但是这也造成了一个后果,那就是RTT比较小的,窗口增长快。 + +然而这并不符合当前网络的真实状况,因为当前的网络带宽比较大,但是由于遍布全球,RTT也比较长,因而基于RTT的窗口调整策略,不仅不公平,而且由于窗口增加慢,有时候带宽没满,数据就发送完了,因而巨大的带宽都浪费掉了。 + +CUBIC进行了不同的设计,它的窗口增长函数仅仅取决于连续两次拥塞事件的时间间隔值,窗口增长完全独立于网络的时延RTT。 + +CUBIC的窗口大小以及变化过程如图所示。 + + + +当出现丢包事件时,CUBIC会记录这时的拥塞窗口大小,把它作为Wmax。接着,CUBIC会通过某个因子执行拥塞窗口的乘法减小,然后,沿着立方函数进行窗口的恢复。 + +从图中可以看出,一开始恢复的速度是比较快的,后来便从快速恢复阶段进入拥塞避免阶段,也即当窗口接近Wmax的时候,增加速度变慢;立方函数在Wmax处达到稳定点,增长速度为零,之后,在平稳期慢慢增长,沿着立方函数的开始探索新的最大窗口。 + +留言问题 + +HTTP的keepalive模式是什么样? + + + +在没有keepalive模式下,每个HTTP请求都要建立一个TCP连接,并且使用一次之后就断开这个TCP连接。 + +使用keepalive之后,在一次TCP连接中可以持续发送多份数据而不会断开连接,可以减少TCP连接建立次数,减少TIME_WAIT状态连接。 + +然而,长时间的TCP连接容易导致系统资源无效占用,因而需要设置正确的keepalive timeout时间。当一个HTTP产生的TCP连接在传送完最后一个响应后,还需要等待keepalive timeout秒后,才能关闭这个连接。如果这个期间又有新的请求过来,可以复用TCP连接。 + +《第15讲 | HTTPS协议:点外卖的过程原来这么复杂》 + +课后思考题 + +HTTPS 协议比较复杂,沟通过程太繁复,这样会导致效率问题,那你知道有哪些手段可以解决这些问题吗? + +通过HTTPS访问的确复杂,至少经历四个阶段:DNS查询、TCP连接建立、TLS连接建立,最后才是HTTP发送数据。我们可以一项一项来优化这个过程。 + +首先如果使用基于UDP的QUIC,可以省略掉TCP的三次握手。至于TLS的建立,如果按文章中基于TLS 1.2的,双方要交换key,经过两个来回,也即两个RTT,才能完成握手。但是咱们讲IPSec的时候,讲过通过共享密钥、DH算法进行握手的场景。 + +在TLS 1.3中,握手过程中移除了ServerKeyExchange和ClientKeyExchange,DH参数可以通过key_share进行传输。这样只要一个来回,就可以搞定RTT了。 + +对于QUIC来讲,也可以这样做。当客户端首次发起QUIC连接时,会发送一个client hello消息,服务器会回复一个消息,里面包括server config,类似于TLS1.3中的key_share交换。当客户端获取到server config以后,就可以直接计算出密钥,发送应用数据了。 + +留言问题 + +1.HTTPS的双向认证流程是什么样的? + + + +2.随机数和premaster的含义是什么? + + + +《第16讲 | 流媒体协议:如何在直播里看到美女帅哥?》 + +课后思考题 + +你觉得基于 RTMP 的视频流传输的机制存在什么问题?如何进行优化? + +Jason的回答很对。 + + + +Jealone的回答更加具体。 + + + +当前有基于自研UDP协议传输的,也有基于QUIC协议传输的。 + +留言问题 + +RTMP建立连接的序列是什么样的? + + + +的确,这个图我画错了,我重新画了一个。 + + + +不过文章中这部分的文字描述是没问题的。 + +客户端发送C0、C1、 C2,服务器发送S0、 S1、 S2。 + +首先,客户端发送C0表明自己的版本号,不必等对方的回复,然后发送C1表明自己的时间戳。 + +服务器只有在收到C0的时候,才能返回S0,表明自己的版本号。如果版本不匹配,可以断开连接。 + +服务器发送完S0后,也不用等什么,就直接发送自己的时间戳S1。客户端收到S1的时候,发一个知道了对方时间戳的ACK C2。同理服务器收到C1的时候,发一个知道了对方时间戳的ACK S2。 + +于是,握手完成。 + +《第17讲 | P2P协议:我下小电影,99%急死你》 + +课后思考题 + +除了这种去中心化分布式哈希的算法,你还能想到其他的应用场景吗? + + + +留言问题 + +99%卡住的原因是什么? + + + +《第18讲 | DNS协议:网络世界的地址簿》 + +课后思考题 + +全局负载均衡使用过程中,常常遇到失灵的情况,你知道具体有哪些情况吗?对应应该怎么来解决呢? + + + +留言问题 + +如果权威DNS连不上,怎么办? + + + +一般情况下,DNS是基于UDP协议的。在应用层设置一个超时器,如果UDP发出没有回应,则会进行重试。 + +DNS服务器一般也是高可用的,很少情况下会挂。即便挂了,也会很快切换,重试一般就会成功。 + +对于客户端来讲,为了DNS解析能够成功,也会配置多个DNS服务器,当一个不成功的时候,可以选择另一个来尝试。 + +《第19讲 | HttpDNS:网络世界的地址簿也会指错路》 + +课后思考题 + +使用 HttpDNS,需要向 HttpDNS 服务器请求解析域名,可是客户端怎么知道 HttpDNS 服务器的地址或者域名呢? + + + +《第20讲 | CDN:你去小卖部取过快递么?》 + +课后思考题 + +这一节讲了CDN使用DNS进行全局负载均衡的例子,CDN如何使用HttpDNS呢? + + + +《第21讲 | 数据中心:我是开发商,自己拿地盖别墅》 + +课后思考题 + +对于数据中心来讲,高可用是非常重要的,每个设备都要考虑高可用,那跨机房的高可用,你知道应该怎么做吗? + +其实跨机房的高可用分两个级别,分别是同城双活和异地灾备。 + + + +同城双活,就是在同一个城市,距离大概30km到100km的两个数据中心之间,通过高速专线互联的方式,让两个数据中心形成一个大二层网络。 + +同城双活最重要的是,数据如何从一个数据中心同步到另一个数据中心,并且在一个数据中心故障的时候,实现存储设备的切换,保证状态能够快速切换到另一个数据中心。在高速光纤互联情况下,主流的存储厂商都可以做到在一定距离之内的两台存储设备的近实时同步。数据双活是一切双活的基础。 + +基于双数据中心的数据同步,可以形成一个统一的存储池,从而数据库层在共享存储池的情况下可以近实时地切换,例如Oracle RAC。 + +虚拟机在统一的存储池的情况下,也可以实现跨机房的HA,在一个机房切换到另一个机房。 + +SLB负载均衡实现同一机房的各个虚拟机之间的负载均衡。GSLB可以实现跨机房的负载均衡,实现外部访问的切换。 + +如果在两个数据中心距离很近,并且大二层可通的情况下,也可以使用VRRP协议,通过VIP方式进行外部访问的切换。 + +下面我们说异地灾备。 + + + +异地灾备的第一大问题还是数据的问题,也即生产数据中心的数据如何备份到容灾数据中心。由于异地距离比较远,不可能像双活一样采取近同步的方式,只能通过异步的方式进行同步。可以预见的问题是,容灾切换的时候,数据会丢失一部分。 + +由于容灾数据中心平时是不用的,不是所有的业务都会进行容灾,否则成本太高。 + +对于数据的问题,我比较建议从业务层面进行容灾。由于数据同步会比较慢,可以根据业务需求高优先级同步重要的数据,因而容灾的层次越高越好。 + +例如,有的用户完全不想操心,直接使用存储层面的异步复制。对于存储设备来讲,它是无法区分放在存储上的虚拟机,哪台是重要的,哪台是不重要的,只会完全根据块进行复制,很可能就会先复制了不重要的虚拟机。 + +如果用户想对虚拟机做区分,则可以使用虚拟机层面的异步复制。用户知道哪些虚拟机更重要一些,哪些虚拟机不重要,则可以先同步重要的虚拟机。 + +对业务来讲,如果用户可以根据业务层情况,在更细的粒度上区分数据是否重要。重要的数据,例如交易数据,需要优先同步;不重要的数据,例如日志数据,就不需要优先同步。 + +在有异地容灾的情况下,可以平时进行容灾演练,看容灾数据中心是否能够真正起作用,别容灾了半天,最后用的时候掉链子。 + + + +感谢第14讲至第21讲中对内容有深度思考和提出问题的同学。我会为你们送上奖励礼券和知识图谱。(稍后运营同学会发送短信通知。) + +欢迎你继续提问! + + + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/协议专栏特别福利答疑解惑5期.md b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑5期.md new file mode 100644 index 0000000..bfabb2e --- /dev/null +++ b/专栏/趣谈网络协议/协议专栏特别福利答疑解惑5期.md @@ -0,0 +1,251 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 协议专栏特别福利 答疑解惑5期 + 你好,我是刘超。 + +第五期答疑涵盖第22讲至第36讲的内容。我依旧对课后思考题和留言中比较有代表性的问题作出回答。你可以点击文章名,回到对应的章节复习,也可以继续在留言区写下你的疑问,我会持续不断地解答。希望对你有帮助。 + +《第22讲 | VPN:朝中有人好做官》 + +课后思考题 + +当前业务的高可用性和弹性伸缩很重要,所以很多机构都会在自建私有云之外,采购公有云,你知道私有云和公有云应该如何打通吗? + + + +留言问题 + +DH算法会因为传输随机数被破解吗? + + + +这位同学的笔记特别认真,让人感动。DH算法的交换材料要分公钥部分和私钥部分,公钥部分和其他非对称加密一样,都是可以传输的,所以对于安全性是没有影响的,而且传输材料远比传输原始的公钥更加安全。私钥部分是谁都不能给的,因此也是不会截获到的。 + +《第23讲 | 移动网络:去巴塞罗那,手机也上不了脸书》 + +课后思考题 + +咱们上网都有套餐,有交钱多的,有交钱少的,你知道移动网络是如何控制不同优先级的用户的上网流量的吗? + +这个其实是PCRF协议进行控制的,它可以下发命令给PGW来控制上网的行为和特性。 + +《第24讲 | 云中网络:自己拿地成本高,购买公寓更灵活》 + +课后思考题 + +为了直观,这一节的内容我们以桌面虚拟化系统举例。在数据中心里面,有一款著名的开源软件OpenStack,这一节讲的网络连通方式对应OpenStack中的哪些模型呢? + + + +OpenStack的早期网络模式有Flat、Flat DHCP、VLAN,后来才有了VPC,用VXLAN和GRE进行隔离。 + +《第25讲 | 软件定义网络:共享基础设施的小区物业管理办法》 + +课后思考题 + +在这一节中,提到了通过VIP可以通过流表在不同的机器之间实现负载均衡,你知道怎样才能做到吗? + +可以通过ovs-ofctl下发流表规则,创建group,并把端口加入group中,所有发现某个地址的包在两个端口之间进行负载均衡。 + +sudo ovs-ofctl -O openflow11 add-group br-lb "group_id=100 type=select selection_method=dp_hash bucket=output:1 bucket=output:2" +sudo ovs-ofctl -O openflow11 add-flow br-lb "table=0,ip,nw_dst=192.168.2.0/24,actions=group:100" + + +留言问题 + +SDN控制器是什么东西? + + + +SDN控制器是一个独立的集群,主要是在管控面,因为要实现一定的高可用性。 + +主流的开源控制器有OpenContrail、OpenDaylight等。当然每个网络硬件厂商都有自己的控制器,而且可以实现自己的私有协议,进行更加细粒度的控制,所以江湖一直没有办法统一。 + +流表是在每一台宿主机上保存的,大小限制取决于内存,而集中存放的缺点就是下发会很慢。 + +《第26讲 | 云中的网络安全:虽然不是土豪,也需要基本安全和保障》 + +课后思考题 + +这一节中重点讲了iptables的filter和nat功能,iptables还可以通过QUEUE实现负载均衡,你知道怎么做吗? + +我们可以在iptables里面添加下面的规则: + +-A PREROUTING -p tcp -m set --match-set minuteman dst,dst -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j NFQUEUE --queue-balance 50:58 +-A OUTPUT -p tcp -m set --match-set minuteman dst,dst -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j NFQUEUE --queue-balance 50:58 + + +NFQUEUE的规则表示将把包的处理权交给用户态的一个进程。–queue-balance表示会将包发给几个queue。 + +libnetfilter_queue是一个用户态库,用户态进程会使用libnetfilter_queue连接到这些queue中,将包读出来,根据包的内容做决策后,再放回内核进行发送。 + +《第27讲 | 云中的网络QoS:邻居疯狂下电影,我该怎么办?》 + +课后思考题 + +这一节中提到,入口流量其实没有办法控制,出口流量是可以很好控制的,你能想出一个控制云中的虚拟机的入口流量的方式吗? + + + +在云平台中,我们可以限制一个租户的默认带宽,我们仍然可以配置点对点的流量控制。 + +在发送方的OVS上,我们可以统计发送方虚拟机的网络统计数据,上报给管理面。在接收方的OVS上,我们同样可以收集接收方虚拟机的网络统计数据,上报给管理面。 + +当流量过大的时候,我们虽然不能控制接收方的入口流量,但是我们可以在管理面下发一个策略,控制发送方的出口流量。 + +留言问题 + +对于HTB借流量的情况,借出去的流量能够抢回来吗? + + + +首先,借出去的流量,当自己使用的时候,是能够抢回来的。 + +有一篇著名的文章《HTB Linux queuing discipline manual》里面很详细,你可以看看。 + + + +很多人看不懂,这是一棵HTB树,有三个分支。A用户使用www访问网页,SMTP协议发送邮件,B用户不限协议。 + +在时间0的时候,0、1、2都以90k的速度发送数据,也即A用户在访问网页,同时发送邮件;B也在上网,干啥都行。 + +在时间3的时候,将0的发送停止,A不再访问网页了,红色的线归零,A的总速率下来了,剩余的流量按照比例分给了蓝色的和绿色的线,也即分给了A发邮件和B上网。 + +在时间6的时候,将0的发送重启为90k,也即A重新开始访问网页,则蓝色和绿色的流量返还给红色的流量。 + +在时间9的时候,将1的发送停止,A不再发送邮件了,绿色的流量为零,A的总速率也下来了,剩余的流量按照比例分给了蓝色和红色。 + +在时间12,将1的发送恢复,A又开始发送邮件了,红色和蓝色返还流量。 + +在时间15,将2的发送停止,B不再上网了,啥也不干了,蓝色流量为零,剩余的流量按照比例分给红色和绿色。 + +在时间19,将1的发送停止,A不再发送邮件了,绿色的流量为零,所有的流量都归了红色,所有的带宽都用于A来访问网页。 + +《第28讲 | 云中网络的隔离GRE、VXLAN:虽然住一个小区,也要保护隐私》 + +课后思考题 + +虽然VXLAN可以支持组播,但是如果虚拟机数目比较多,在Overlay网络里面,广播风暴问题依然会很严重,你能想到什么办法解决这个问题吗? + + + +很多情况下,物理机可以提前知道对端虚拟机的MAC地址,因而当发起ARP请求的时候,不用广播全网,只要本地返回就可以了,在Openstack里面称为L2Population。 + +《第29讲 | 容器网络:来去自由的日子,不买公寓去合租》 + +课后思考题 + +容器内的网络和物理机网络可以使用NAT的方式相互访问,如果这种方式用于部署应用,有什么问题呢? + + + +_CountingStars对这个问题进行了补充。 + + + +其实就是性能损耗,随机端口占用,看不到真实IP。 + +《第30讲 | 容器网络之Flannel:每人一亩三分地》 + +课后思考题 + +通过 Flannel 的网络模型可以实现容器与容器直接跨主机的互相访问,那你知道如果容器内部访问外部的服务应该怎么融合到这个网络模型中吗? + + + +Pod内到外部网络是通过docker引擎在iptables的POSTROUTING中的MASQUERADE规则实现的,将容器的地址伪装为node IP出去,回来时再把包nat回容器的地址。 + +有的时候,我们想给外部的一个服务使用一个固定的域名,这就需要用到Kubernetes里headless service的ExternalName。我们可以将某个外部的地址赋给一个Service的名称,当容器内访问这个名字的时候,就会访问一个虚拟的IP。然后,在容器所在的节点上,由iptables规则映射到外部的IP地址。 + +《第31讲 | 容器网络之Calico:为高效说出善意的谎言》 + +课后思考题 + +将Calico部署在公有云上的时候,经常会选择使用IPIP模式,你知道这是为什么吗? + +_CountingStars的回答是部分正确的。 + + + +一个原因是中间有路由,如果VPC网络是平的,但是公有云经常会有一个限制,那就是器的IP段是用户自己定义的,一旦出虚拟机的时候,云平台发现不是它分配的IP,很多情况下直接就丢弃了。如果是IPIP,出虚拟机之后,IP还是虚拟机的IP,就没有问题。 + +《第32讲 | RPC协议综述:远在天边,近在眼前》 + +课后思考题 + +在这篇文章中,mount的过程是通过系统调用,最终调用到RPC层。一旦mount完毕之后,客户端就像写入本地文件一样写入NFS了,这个过程是如何触发RPC层的呢? + +_CountingStars的回答很有深度。 + + + +是的,是通过VFS实现的。 + +在讲[Socket]那一节的时候,我们知道,在Linux里面,很多东西都是文件,因而内核中有一个打开的文件列表File list,每个打开的文件都有一项。 + + + +对于Socket来讲,in-core inode就是内存中的inode。对于nfs来讲,也有一个inode,这个inode里面有一个成员变量file_operations,这里面是这个文件系统的操作函数。对于nfs来讲,因为有nfs_file_read、nfs_file_write,所以在一个mount的路径中读取某个文件的时候,就会调用这两个函数,触发RPC,远程调用服务端。 + +《第33讲 | 基于XML的SOAP协议:不要说NBA,请说美国职业篮球联赛》 + +课后思考题 + +对于HTTP协议来讲,有多种方法,但是SOAP只用了POST,这样会有什么问题吗? + + + +《第34讲 | 基于JSON的RESTful接口协议:我不关心过程,请给我结果》 + +课后思考题 + +在讨论RESTful模型的时候,举了一个库存的例子,但是这种方法有很大问题,那你知道为什么要这样设计吗? + + + +这个我在双十一包的例子中已经分析过了。 + +《第35讲 | 二进制类RPC协议:还是叫NBA吧,总说全称多费劲》 + +课后思考题 + +对于微服务模式下的RPC框架的选择,Dubbo和SpringCloud各有优缺点,你能做个详细的对比吗? + + + +《第36讲 | 跨语言类RPC协议:交流之前,双方先来个专业术语表》 + +课后思考题 + +在讲述Service Mesh的时候,我们说了,希望Envoy能够在服务不感知的情况下,将服务之间的调用全部代理了,你知道怎么做到这一点吗? + + + + + +iptables规则可以这样来设置: + +首先,定义的一条规则是ISTIO_REDIRECT转发链。这条链不管三七二十一,都将网络包转发给envoy的15000端口。但是一开始这条链没有被挂到iptables默认的几条链中,所以不起作用。 + +接下来,在PREROUTING规则中使用这个转发链。从而,进入容器的所有流量都被先转发到envoy的15000端口。而envoy作为一个代理,已经被配置好了,将请求转发给productpage程序。 + +当productpage往后端进行调用的时候,就碰到了output链。这个链会使用转发链,将所有出容器的请求都转发到envoy的15000端口。 + +这样,无论是入口的流量,还是出口的流量,全部用envoy做成了“汉堡包”。envoy根据服务发现的配置,做最终的对外调用。 + +这个时候,iptables规则会对从envoy出去的流量做一个特殊处理,允许它发出去,不再使用上面的output规则。 + + + +感谢第22讲至第36讲中对内容有深度思考和提出问题的同学。我会为你们送上奖励礼券和知识图谱。(稍后运营同学会发送短信通知。) + + + +咱们的答疑环节暂时告一段落了,如果你在学习过程中有什么问题,欢迎你继续留言给我! + + + + \ No newline at end of file diff --git a/专栏/趣谈网络协议/结束语放弃完美主义,执行力就是限时限量认真完成.md b/专栏/趣谈网络协议/结束语放弃完美主义,执行力就是限时限量认真完成.md new file mode 100644 index 0000000..2d18079 --- /dev/null +++ b/专栏/趣谈网络协议/结束语放弃完美主义,执行力就是限时限量认真完成.md @@ -0,0 +1,71 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 放弃完美主义,执行力就是限时限量认真完成 + 你好,我是刘超。 + +从筹备、上线到今天专栏完结,过去了将近半年的时间。200多天,弹指一挥间。 + +我原本计划写36篇,最后愣是写到了45篇。原本编辑让我一篇写两三千字,结果几乎每篇都是四五千字。这里面涉及图片张数我没具体数过,但是据说多到让编辑上传到吐。编辑一篇我的稿件的工作量相当于别的专栏的两倍。 + +人常说,有多少付出,就有多少回报。但是,写这个“趣谈网络协议”专栏,我收获的东西远超过我的想象。我希望你的收获也是如此。为什么这么说呢?我们把时间放回到这个专栏最开始的时候,我慢慢跟你讲。 + +我不是最懂的人,但我想尝试成为这样的人 + +今年年初,极客时间来找我,希望我讲一些偏重基础的知识,比如网络协议。 + +他们一提到这个主题,我就很兴奋,因为这也触动了我心中长期以来的想法,因为网络这个东西学起来实在是太痛苦。 + +但是,说实话,接下这个重任,我心里其实是有点“怕”的。我怕自己不够专业,毕竟业内有这么多网络工程师和研究网络理论的教授。我讲这个课会不会贻笑大方啊? + +我知道,很多技术人员不敢写博客、写公众号,其实都有这种“怕”的心理:我又不牛,没啥要分享的,要是误导了别人怎么办? + +如果你想做出一些成绩,这个心理一定要克服。其实每个人都有自己的相对优势。对于某个东西,你研究的时间不一定是最长的,但是你可能有特殊的角度、表达方式和应用场景。坚定了这个想法之后,我就开始投入热火朝天的专栏写作了。 + +一旦开始写,我发现,这个事情远没有看上去那么简单。它会花费你非常多的个人时间。写专栏这几个月,晚上两点之后睡,周末全在写专栏,基本成为我的生活常态。但是我想挑战一下自己,我觉得,只要咬牙挺过去,自己的技术就会上升一个层次。 + +放弃完美主义,执行力就是限时限量认真完成 + +技术人都有完美主义倾向,觉得什么事情都要钻研个底儿朝天,才拿出来见人。我也一样。 + +我曾经答应某出版社写一本搜索引擎的书。这本书分为原理篇和实践篇。我总觉得我还没把原理篇写完,就不能写实践篇。但是,仅原理篇我就写了一年。搜索引擎就火了一两年,最后时间窗口过了,书稿没有完成,这件事儿也就这么搁浅了。 + +所以,完美主义虽然是个很好听的词,但是它往往是和拖延症如影随形的,它常常会给拖延症披上一个华丽的外衣,说,我是因为追求完美嘛。但是,最终的结果往往是,理论研究半天还没动手,执行力很差。时间点过了,就心安理得地说,反正现在也不需要了,那就算了吧。久而久之,你就会发现,自己好像陷入了瓶颈。 + +我慢慢明白过来,我们不是为了做技术而做技术,做技术是为了满足人类需求的。完美主义是好事儿,但是,坚持完美主义的同时要限时限量地完成,才能形成执行力。 + +写这个专栏之后,我更加深刻地体会到这一点。每周都要写三篇文章,压力很大,根本容不得任何拖延。如果我还是坚持以前完美主义的做法,读完十本书,用三年时间把网络协议都研究透再来写,那现在就没有这个专栏了。 + +如果我们要强调执行力,时间点这个因素就至关重要。在固定的时间点上,就要把控范围,不能顾虑太多,要勇于放弃。就像给产品做排期,先做最小闭环的功能集合,其他的放在以后再补充。在这个前提下,以自己最大的限度往完美的方向上努力。比如,我觉得每天2点睡是我的身体极限,努力到这个程度,我也就无愧于心了。 + +所以说,我们做事情的目的并不是完美,而是在固定的时间点,以固定的数量和质量,尽可能认真地满足当时的客户需求,这才是最重要的。 + +这样做肯定会有不满意的地方,比如很多同学在留言区指出我的错误,甚至有的同学提的问题,我原来都没思考过。但是,我觉得这些都不是事儿。我可以再查资料,再补充、再完善。所以,后来时间宽裕了,我还增加了5期答疑,回答了一下之前没来得及回答地问题。高手在民间,咱们一起来讨论和进步。这个过程已经让我受益良多。 + +保持饥渴,不怕被“鄙视”,勇于脱离舒适区 + +有人可能会问了,你看你既不是最专业的,还不追求完美,真的不怕被人“鄙视”吗? + +被“鄙视”,谁都怕,这也是为什么越大的会议,参加人数越多的演讲,越是没有人提出具体的问题。大家都怕丢人,看上去好像大家都听懂了,就我啥都不懂,我要是问,被大家笑话怎么办?我想很多人都有这样的经历吧?我也来给你讲讲我的亲身经历。 + +我从Windows开发去做Linux的存储系统开发时,连Linux man都不会看;我在惠普从事OpenStack实施工作的时候,对于网络的了解一塌糊涂,一直被甲方骂;我在华为做云计算,支撑运营商项目的时候,面对一大堆核心网词汇,一脸懵;我在网易云对内支撑考拉的时候,在微服务架构方面也是小白……被“鄙视”了这么多次之后,我不怕了。因为这每一次“鄙视”都可以让我发现自己的短板,然后啃下这些东西,这不就是最大的收获吗? + +我就是这样一直被“鄙视”着成长起来的人,我就是常常在别人分享的时候坐第一排问很傻的那种问题的人,我就是常常一知半解还愿意和别人讨论的人…… + +怕被“鄙视”,说明你还不够饥渴,还没有勇气脱离你的舒适区。 在你熟悉的领域里面,你是最最权威的,但是,天下之大,你真的只满足于眼前这一亩三分地吗? + +很多人因为怕被“鄙视”,不敢问、不敢做,因而与很多美好的东西都擦肩而过了。直到有一天你用到了,你才后悔,当时自己怎么没去多问一句。 + +所以,当你看到一个特别好的、突破自己的学习机会,别犹豫,搭上这辆车。等过了十年,你会发现,当年那些嘲笑、轻视,甚至谩骂,都算不了什么,进步本身才是最最重要的。 + +今天,咱们没有谈具体的知识,我只表达了一下我的观点。我就是那个你在直播里看到的,那个邋遢、搞笑、不装,同时做事认真,愿意和你一起进步的技术大叔。 + +脱离舒适区吧,希望我们可以一起成长! + +最后,我在这里放了一个毕业调查问卷。如果你对这个专栏或者我本人有什么建议,可以通过这个问卷进行反馈,我一定会认真查看每一封的内容。期待你的反馈! + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/00开篇词为什么每个人都应该学会复盘?.md b/专栏/跟着高手学复盘/00开篇词为什么每个人都应该学会复盘?.md new file mode 100644 index 0000000..5a62dc5 --- /dev/null +++ b/专栏/跟着高手学复盘/00开篇词为什么每个人都应该学会复盘?.md @@ -0,0 +1,159 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 开篇词 为什么每个人都应该学会复盘? + 你好,我是张鹏。 + +很高兴能在这门课程中与你相遇,和你分享我的复盘方法。 + +作为独立商业顾问和工信部数字化管理特聘专家,我做复盘已经有将近20年了。在这个过程中,我深刻地体会到复盘在职场中的重要性。 + +所以我认为,复盘已经成为一项职场必备技能,而且越早学会学好。 + +不是只有高管才要学复盘 + +你可能会想:“复盘不都是公司高管来做的吗?我又用不上。”如果你有这种想法,那么你可能要更新一下自己的认知了! + +首先,也许复盘是由中高层组织的,但有些具体工作往往会安排给下面的人做,比如资料准备和数据分析。所以,复盘能力是你现在就用得上的。 + +其次,复盘能力是团队负责人的一项重要能力,学会复盘,你就更有机会从基层员工成为一线主管,从一线主管成为部门经理。所以,复盘能力很可能会成为你晋升的关键。 + +跟你介绍一个我亲自指导的案例吧。 + +2016年,我的一位朋友加入了一家创业公司,担任用户运营,职级相当于阿里P5,手底下没有带人。 + +当时他的很多同事都有BAT大厂经历,从背景资历看,他的上升通道很窄。 + +但是,在我连续两年的指导下,他凭借复盘能力实现了职场跃升,现在已经升任助理副总裁,相当于阿里P11/M6,分管一条年营收几十亿的业务线,团队规模也到了千人级别。 + + + +你可能会想:“凭什么说这是凭借复盘能力实现的呢?”别着急,我一步一步来为你拆解。 + +第一步,在领导做复盘的时候,他承担了用户调研和数据分析的任务,发现如果把海量产品的C2C匹配模型改为头部产品的B2C直卖模型,效果会更好。 + +这个发现,让他得到了领导的认可和器重。 + +第二步,由于当时公司内部并没有做B2C的团队,他主动申请以内部创业的形式组建团队,执行复盘结论。 + +于是,他争取到了带团队的机会,招来了两个兼职实习生,Title也变成了部门经理。 + +第三步,他们团队全都使用我的复盘方法做迭代,反复优化匹配模型和客群画像,从2017年开始连续8个月的月环比增长达到100%,又在接下来不到2年的时间里增长了400%。与此同时,这家公司也在2017~2019年实现了1000倍的业务增长,并顺利完成了IPO。 + +现在,不只是他实现了职场跃升,当初那两位实习生也已经成为了带百人团队的主管。 + +复盘的常见问题 + +听完这个故事,你可能会想:“这个案例太特殊了,其他人做复盘也有这么顺利吗?”其实这并不是个例,这些年我手把手教过很多人做复盘,像这样成功的例子还有很多。 + +不过我很理解你的疑问,在做复盘的时候,我们的确会遇到不少问题,最典型的就是以下这些。 + +第一个问题是,复盘变成了甩锅和背锅。 + +事情一旦没做好,大部分人都不愿意承认自己的失误,只想在别人身上找原因。 + +之所以会出现这种情况,原因在于很多人错误地理解了复盘的目的,认为复盘是对过去的追责和惩罚,所以就会有意逃避,逃避不了,就只好把责任甩给别人。 + +其实恰恰相反,复盘的目的应该是对未来的优化。所以,复盘虽然也包括对过去事实的总结,但更重要的是对原因的分析和对认知的修正。 + +比如在前面的案例中: + + +整理用户和行业资料,这就是对事实的总结。 +找出原来C2C策略的问题,这是对原因的分析。 +认识到用户的痛点不在于产品丰富度,而在于匹配精准度,这就是对认知的修正。 +用B2C模型取代的C2C模型,这就是对未来的优化。 + + +当我们不以追责和惩罚为目的来做复盘,把重点放在对未来的优化上,才可能拥有“群众基础”,这是复盘的前提。 + +第二个问题是,不知道怎么得到有价值的复盘结论。 + +面对大量的资料,很多人找不到分析的切入点,更不用说制定针对性的改进计划了。 + +这是因为,准确地分析原因和修正认知,需要比较强的洞察力。你要从盘根错节的信息中抽丝剥茧,定位到问题的本质。 + +比如在前面的案例中,我的朋友在内部创业的第一年也走过弯路,虽然优化匹配模型提高了引流转化率,但是到年底并没有实现业务目标。 + +后来我指导他继续复盘,确认了引流转化手段本身没有问题,但是客群画像还不够精准。于是他第二年升级了客群画像,业务才终于迎来了爆发式增长。 + +第三个问题是,复盘结论得不到别人支持。 + +很多人都遇到过这样的情况,复盘的意见得不到领导的资源支持,其他部门的同事也不愿意帮忙出力。 + +这是因为,让自己的想法得到别人的支持,需要比较强的说服力。你要充分利用手头的资源作为筹码,并且站在对方角度思考问题。 + +我的朋友一开始提出要内部创业的时候,他的领导也很犹豫。那么他是怎么做的呢? + + +提交完整的商业计划书详细地阐述规划,并承诺每个季度都会向领导汇报成果和复盘结论,让领导觉得更加可信和可控。 +提出用自己未来1年的工资作为项目预算,而公司只需要多提供2个Head Count名额和工位,让领导看到自己的决心和信心。 +算了一笔账,公司还没有做B2C的团队,就算最后营收没有达到目标,至少打造了一支专业团队,让领导看到兜底的收益。 +业务开始爆发式增长之后,马上跟领导争取更多的资源和政策倾斜,结果其他部门都争先恐后地来主动寻求合作。 + + +所以,虽然总结事实的能力也很重要,但它属于复盘的基本功。对市场的洞察力和对别人的说服力,才是复盘的核心能力,它们最终也将体现为你个人在职场中的影响力,以及你的公司在市场上的竞争力。 + + + +复盘的课程安排 + +基于对复盘目的和核心能力的理解,我把这门课程分为三个部分。 + +第一部分是基础概念篇(3讲)。 + +我会带你了解复盘的本质,为你解读几种传统复盘模型的特点,并且介绍一个由我自己原创的、更加适应当下的复盘模型。同时,我也会教你怎么利用这个模型来完成不同类型的复盘。这是你掌握洞察力和说服力的必备条件。 + +第二部分是实操流程篇(9讲)。 + +我把一个完整的复盘流程分成了会前准备、复盘会议和会后执行三个阶段,其中复盘会议是核心阶段。 + +我会为你拆解每个阶段的不同环节,介绍每个环节的常见问题、解决方案与实践经验,教你怎么使用相关的复盘工具来组织团队完成复盘,从而帮助你提升在职场中的影响力。 + +第三部分是实战案例篇(6讲)。 + +我会从组织结构、公司战略、业务节点、组织管理、财务预算和人才发展等六个典型的、能创造巨大商业价值的实战场景出发,详细解读我最近3年亲自操盘的真实案例。 + +这些案例会为你提供解决各类问题的参考模板,从而让你能够帮助公司提升在市场上的竞争力。 + + + +跟我学复盘的理由 + +你可能会想:“教复盘的人那么多,为什么一定要跟你学呢?你和别人有什么不一样呢?” + +第一,多元的视角让我拥有很强的洞察力,这是一般人不具备的。 + +一方面,我做过独立的咨询顾问,掌握了大量不同行业和不同类型的案例,拥有横向对比的视野,可以迅速诊断出问题的症结所在。 + +另一方面,我也当过公司创始人,完整地经历过公司发展的不同阶段,拥有纵向透视的视野,可以迅速给出问题对应的改进方案。 + +这些经验可以让你的复盘不再是走过场,而是能够真正产生价值。 + +第二,管理的经历让我拥有很强的说服力,这也是一般人不具备。 + +我担任过HRVP(人力资源副总裁),参与过很多次跨部门资源整合,也解决过很多起部门冲突。也许你会认为,因为我是高管,才有这样的控制力。 + +但其实就算是高管,如果观点和方案不能服众,只靠威权强压,团队氛围和执行效果肯定会很差。而我恰恰是因为在公司有足够的影响力,说话有分量,才被一步一步被提拔到HRVP的位置上。 + +这些经验可以让你在复盘时走出“不是甩锅就是背锅”的死循环,获得高层的认可和同事的支持。 + +第三,轻理论重实践、轻概念重工具的课程安排,让我的复盘方法拥有很强的可复制性,这是一般方法不具备的。 + +如果你看过市面上其他关于复盘的书籍和课程,多半会有这样的感觉:学了一大半,作者还在讲理论和概念,比如复盘的定义、理念、角色和类型等等。 + +你学了很久,觉得好像很有道理,结果还停留在纸上谈兵的阶段。领导让你组织一次复盘,你瞬间蒙了,不知道具体先做什么、再做什么。 + +而在刚才的课程安排中,你可以看到,我讲基础概念的部分非常少,绝大部分内容都在讲实操流程和实战案例,我教给你的是可以直接复制的流程工具和案例模版。 + +这样的设计可以让你即学即用,将学到的复盘能力直接在工作中落地。 + +最后,你在复盘的过程中遇到过什么问题,或者你对这门课程有什么期待呢?欢迎在留言区跟我交流,我一定会认真答复。 + +我是张鹏,我们下一讲见! + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/01CLAP模型:一个优秀的复盘模型是什么样的?.md b/专栏/跟着高手学复盘/01CLAP模型:一个优秀的复盘模型是什么样的?.md new file mode 100644 index 0000000..4823636 --- /dev/null +++ b/专栏/跟着高手学复盘/01CLAP模型:一个优秀的复盘模型是什么样的?.md @@ -0,0 +1,194 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 CLAP模型:一个优秀的复盘模型是什么样的? + 你好,我是张鹏。 + +在这门课程的第1讲,我想跟你一起探讨一个问题:一个优秀的复盘模型是什么样的? + +为什么要学复盘模型 + +你可能会想:“学会一些技巧不就能做复盘了吗,为什么还要费时费力地学模型呢?” + +首先,技巧虽然可能很管用,但却是零散的,而模型是体系化的,所以掌握了模型,你就不容易遗漏关键环节,考虑问题会比较周全。 + +其次,技巧虽然可能很巧妙,但是往往对使用条件有要求,而模型的通用性比较强,所以掌握了模型,你就不容易突然傻眼,哪怕遇到少见的、复杂的情况,也能不慌不忙地应对。 + +复盘模型,就相当于出门前“伸手要钱”(身份证、手机、钥匙和钱包)的口诀,解数学方程的万能公式,你说重不重要呢? + +复盘常见的模型 + +不过,模型本身也有高下之分。在开篇词中我提到过,复盘的目的是对未来的优化,所以一个模型到底好不好用,就要看它帮助我们做优化的能力怎么样。 + +带着这样的思路,我先介绍两个常见的复盘模型,看看它们是怎么做优化的。 + +模型一:PDCA(Plan-Do-Check-Act) + +第一个模型是PDCA模型,它是美国质量管理专家休哈特(Walter A. Shewhart)提出的,后来因为另一位美国质量管理专家戴明(William Edwards Deming)的宣传推广而得到普及,所以又叫戴明环。 + +PDCA这4个字母分别代表4个环节: + + +Plan(计划):制定目标和计划。 +Do(执行):展开任务、组织实施。 +Check(检查):检查过程中的关键节点和最终结果。 +Act(处理):处理检查结果,肯定成功的经验,纳入标准流程;总结失败的教训,引起重视;对于没有解决的问题,提交到下一次PDCA循环中去解决。 + + +整个模型的运行过程如下图所示: + + + +其中最核心的部分是Check检查环节和Act处理环节,这两个环节要审视工作完成情况,总结经验教训。 + +PDCA的优化体现在对具体做事标准的修订,比如技术标准、管理制度和财务规范等,常见易错点可以整理成Checklist,行之有效的方法可以固化到流程中。PDCA每循环一次,做事的标准就验证或优化了一次。 + +这也跟它的历史起源有关,因为最早它是用来做质量管理的,就是要狠抓品控,“死磕”标准,精益求精。 + +使用PDCA的典型代表就是诺基亚(Nokia)公司了。从20世纪90年代到21世纪的前10年,诺基亚可以说引领了手机行业的发展,在行业内是无敌的存在。 + +你可能没有用过诺基亚的手机,但你一定听过“诺基亚手机可以砸核桃”的段子,江湖上甚至流传着“诺基亚手机挡子弹”的传说。这些质量品控方面的口碑,就有PDCA的功劳。 + +直到3G时代初期,诺基亚仍然是手机行业的老大,当时在通信质量、制作成本和可靠性等方面都比初代iPhone做得更好。 + +那么问题来了,既然PDCA这么管用,为什么诺基亚会在后面的竞争中一败涂地呢? + +这就要说到PDCA模型的局限性了。刚才我们提到,PDCA的优化体现在修订具体做事的标准,这也意味着它缺乏对业务战略的调整机制。 + +换句话说,PDCA只会教你实现目标,不会教你调整目标;只会告诉你怎么把事情做好,不会告诉你是不是该做别的事情了。 + +诺基亚的确能把手机做好,但是苹果公司的iPhone却重新定义了手机。 + +所以PDCA模型比较适合工业化时代。工业化时代的特点是,市场需求和竞争对手基本上都是确定的,比的是质量、成本和效率等传统核心价值。 + +但是当今社会已经逐渐步入后工业化时代,又叫VUCA时代,因为它的特点包括易变性(Volatility),不确定性(Uncertainty),复杂性(Complexity)和模糊性(Ambiguity)。 + +一个具体表现就是,市场需求要靠自己去发掘和创造,我们甚至连自己的竞争对手是谁都不知道。 + +质量、成本和效率,这些当然还是很重要,但是这方面的能力早已经不是决胜能力,而只是必备的基础能力。在这个时代,洞察和满足用户需求的能力,或者说面向用户创造价值的能力,才是企业的核心竞争力。 + +相应地,一个优秀的复盘模型就要能够分析需求、调整目标,今年行,明年行,而且五年十年以后还能行。 + +模型二:PDF(Preview-Do-FuPan) + +第二个模型是PDF模型,它是由联想集团创始人柳传志引入做事过程中的,所以也叫柳传志环。 + +PDF这3个字母分别代表3次行动: + + +Preview(沙盘推演):在做事之前把所有的可能性推演一遍,找出最佳方案。 +Do(执行):具体的方案落地执行。 +FuPan(复盘):事后对做过的事情进行复盘。 + + +很多人第一次看到PDF模型的时候,会觉得它跟PDCA模型很像:Preview就是Plan,Do和Do完全一样,FuPan就相当于Check和Act。 + +其实这是不对的,两个模型完全不是一回事。PDCA是把一件事情分成4个环节来做;而PDF是把一件事情重复做3次,其中第1次的沙盘推演和第3次的复盘是虚拟地做,只有第2次执行是实际地做。 + +我们重点关注的是PDF模型的复盘部分,它可以分为4个环节,运行过程如下图所示: + + + + +目标:当初设定的目标是什么,设定目标的原因是什么。 +结果:对照目标评估现在的工作成果,是完成了,没有完成,还是部分完成。 +分析:通过不停地追问,找到成功或失败的根本原因。 +总结:继续深挖成功的经验或失败的教训。 + + +其中最核心的部分是分析原因和总结规律,并且在实际应用中,规律还要经过其他案例验证从而避免偶然性的因素,这是一个非常了不起的发现。 + +PDF的优化体现在对业务战略的调整,比如销售模式、盈利模式和目标客群等,可以根据行业趋势、竞品策略和用户需求等因素来灵活调整,适应变化。 + +2002年,戴尔在中国的PC市场异军突起,联想的市场份额急剧下降。 + +于是联想集团内部组织了复盘。因为联想采用的是分级代理分销模式,而戴尔采用的是直销模式,拥有低库存、去中间商和按需定制等优势,所以一开始的时候,很多人都认为份额下降的原因是“直销模式比分销模式好”,结论是要变更销售模式。 + +但是核心高层经过周密的推演得到一条关键的战略洞察:与戴尔的竞争,本质上是组织效率和客户价值感受的竞争。所以后来的结论是: + +第一,针对内部沟通成本太高、决策流程固化和反应慢的问题,将结构扁平化,把全国7个大区重组为18个分区,决策权进一步下放,从而更高效地响应市场。 + +第二,针对戴尔的直销优势,把销售模式调整为直销与分销结合,针对个人客户采用直销模式,针对大客户和企业客户,采用增值服务更好的的分销模式。 + +最终,联想在一年之内夺回了丢失的市场份额。直到2020年,联想在全球和中国的PC市场份额都名列前茅,稳居行业前两名。 + +那么问题又来了,既然PDF模型这么管用,为什么联想的手机业务却做不起来呢? + +要知道,中国国产手机市场也曾有过“中华酷联”(中兴、华为、联想和酷派)的时代,现在却变成了华为、OPPO、VIVO和小米的天下。 + +这说明,PDF模型也有它的局限性。虽然PDF提出了一个很好的思路,但是因为缺乏颗粒度更细的流程和标准,所以它严重依赖复盘者的个人能力,可复制性不强。 + +也就是说,这个人成功了,换一个人不一定成功;甚至这个人这次成功了,下一次也不一定成功。 + +事实上,PDF模型要想用出效果,复盘者不但要有摸爬滚打多年的行业经验和跨部门跨领域的大局观,因为这样才能洞察到业务问题的关键;也要得到老板充分的支持和信任,因为这样才有魄力去否定大部分人的错误认知,才有底气去坚持自己的看法。 + +但是这样的人和这样的机会注定是非常稀缺的,而且在VUCA时代会更加稀缺。 + +因为VUCA时代特点的另一个具体表现就是,市场碎片化和用户碎片化。 + +自上而下的业务规划和指令传达当然还是很重要,但光靠这一点已经很难完全满足用户的需求。在这个时代,让更多的员工能直接参与到洞察和满足用户需求的过程中来,同样是企业的核心竞争力。 + +于是现在企业趋向于结构扁平化和服务多元化。层级变少了,高管变少了,束缚也变少了;但是虚拟团队或行动小组变多了,独立负责一小块业务的人变多了,基层的能量得到更大限度的发挥。 + +相应地,一个优秀的复盘模型,要能够让更多的普通人用出效果、互相赋能,我用我行,你用你也行,不但自己行,还带动团队和公司一起行。 + +CLAP模型 + +分析到这里,我想起了达尔文的一句关于进化的名言:“能够生存下来的物种,并不是那些最强壮的,也不是那些最聪明的,而是那些能对变化做出快速反应的。” + +其实,复盘的本质就是进化,复盘能力就是适应环境变化的能力。在VUCA时代,环境的变化更快、更复杂、更模糊、更加充满不确定性,所以企业更加需要复盘,也需要更多的人来参与复盘。这也对复盘模型提出了很高的要求。 + +基于开篇词对复盘目的的解读和这一讲对复盘模型的分析,我构建了一个CLAP模型。CLAP这4个字母,分别组成一次循环的4个环节,如下图所示: + + + + +对比(Comparison):陈述结果,和最初设定的目标进行对比,也就是对事实的总结。 +逻辑(Logic):梳理逻辑,解释结果和目标之间的差距,也就是对原因的分析。 +认知(Acknowledge):提出假设,更新对业务和用户的理解,也就是对认知的修正。 +规划(Project):制定改进规划,在实践中验证,也就是对未来的优化。得到的结果,又可以作为下一次循环的依据。 + + +CLAP模型不但能优化做事细节,也能优化业务战略,从而弥补了PDCA模型的局限性。 + +当然,如果光是这样,CLAP模型看上去似乎和PDF模型的复盘部分并没有本质区别。 + +不过别着急,我在CLAP的基础上,还补充了OPTM框架。OPTM这4个字母,分别代表复盘的3个层级,如下图所示: + + + + +组织(Organization)层:一支专业的复盘团队,负责组织复盘行动,建设和维护流程、方法和工具,以及分享复盘结论等工作,这是人的层级。 +流程(Process)层:一套标准的复盘流程,包括会前准备、召开复盘会议和会后执行三个阶段,具体展开就是CLAP模型,这是事的层级。 +工具&方法(Tools & Methods)层:一系列实用的复盘工具和方法,拿来就能用,保证每个步骤都能顺利落地,这是物的层级。 + + +CLAP模型有了OPTM的框架加持,实际应用时的颗粒度就更细了,可复制性大大提高,非核心高层的普通人也可以用出效果,互相赋能,从而弥补了PDF模型的局限性。 + +实际上,OPTM是英文单词Optimize的缩写,意思是优化,正好是复盘的目的。 + +CLAP在英文中有掌声的意思,它也是你在完成一次高质量的复盘之后会收获的东西。 + +那么,OPTM框架到底包括哪些具体内容,CLAP模型又要怎么使用呢?别着急,下一讲,我会用一个具体案例为你详细讲解。 + +小结 + +这一讲我们探讨了一个问题,一个优秀的复盘模型是什么样的?我先为你分析了两个常见的模型,然后向你介绍了我自己提出的OPTM框架下的CLAP模型。现在,我们回顾一下重点内容。 + + +PDCA模型包括4个环节,计划、执行、检查和处理,它的优化主要体现在修订具体做事标准,但是局限性在于缺乏对业务战略的调整机制。 +PDF模型的复盘部分包括4个环节,回顾目标、评估结果、分析原因和总结规律,它的优化主要体现在调整业务战略,但是局限性在于严重依赖复盘者的个人能力、可复制性不强。 +CLAP模型包括4个环节,对比、逻辑、认知和规划,它既能优化做事细节,也优化业务战略;而且在OPTM框架下,从组织、流程、工具&方法这三个层级细化了实际应用时的颗粒度,提高了可复制性。 + + +思考题 + +这就是今天的全部内容,最后留一道思考题给你吧。你或者你所在的团队之前是怎么做复盘的呢?学完这一讲,你觉得以前的复盘方式有什么优缺点? + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/02OPTM框架:怎么使用CLAP模型?.md b/专栏/跟着高手学复盘/02OPTM框架:怎么使用CLAP模型?.md new file mode 100644 index 0000000..7ce1a91 --- /dev/null +++ b/专栏/跟着高手学复盘/02OPTM框架:怎么使用CLAP模型?.md @@ -0,0 +1,227 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 OPTM框架:怎么使用CLAP模型? + 你好,我是张鹏。 + +在上一讲,我为你介绍了CLAP模型。它包括对比(Comparison)、逻辑(Logic)、认知(Acknowledge)和规划(Project)4个环节,既能帮你优化做事细节,也能帮你优化业务战略。 + +同时,我也为你介绍了OPTM框架。它包括组织(Organization)、流程(Process)以及工具和方法(Tools&Methods)3个层级,分别从人、事和物3个角度充实了CLAP模型,让这个模型更加方便落地,能够为更多人创造价值。 + + + +那么,OPTM框架的3个层级分别包括哪些具体内容?CLAP模型又到底要怎么使用呢?这一讲我将为你详细说明。 + +OPTM框架 + +组织(Organization) + +在OPTM框架中,第一个介绍的层级是组织。 + +复盘需要有专业的团队负责,这个团队一般是一个虚拟组织,也就是说,团队成员只是专门做复盘,而不是专职做复盘,他们在公司内部有自己的本职工作。这种形式有点类似于人民代表大会,人大代表们平时在各行各业工作,需要开会的时候再聚集起来。 + +这个虚拟组织没有统一的叫法,正式一点可以叫“复盘组委会”,随意一点也可以叫“复盘小组”。在某些公司内部可能会有“战略委员会”或“执行委员会”来组织复盘,不过复盘只是它们的临时工作。 + +为了方便交流,在这门课程里,我就把负责复盘的这类虚拟组织统一称为“复盘团队”。 + +复盘团队的职责是对重大事件或阶段性任务组织复盘,从而提升认知水平,在业务发展和内部管理等方面帮助公司和员工成长;具体工作包括推动复盘按部就班地进行,以及完善和优化复盘的流程、工具和方法等。 + +流程(Process) + +第二个介绍的层级是流程。 + +复盘整体的流程分为三个阶段,如下图所示: + + + +第一个阶段是会前准备阶段,也就是对复盘会议的方方面面做细致的筹备安排,包括主题、场地、人员、物料、流程和规则等。其中最主要的工作是三件事,筹建团队,分配任务和整理资料。 + +这个阶段是CLAP模型的信息输入部分。 + +第二个阶段是复盘会议阶段,也就是召开会议,会议议程包括陈述结果、对比目标,梳理逻辑、分析原因,提出假设、修正认知等,目的是得出复盘结论,从而指导未来的工作。 + +这个阶段是CLAP模型的CLA环节,也是整个复盘的重头戏。 + +第三个阶段是会后执行阶段,也就是让复盘会议落实为真正的成果,包括会议记录同步、资料归档、制定规划、评估效果和实践验证等。其中最主要的工作是两件事,制定规划和评估效果。 + +这个阶段包括CLAP模型的P环节,也包括模型的信息输出部分。 + +工具和方法(Tools&Methods) + +第三个介绍的层级是工具和方法。 + +你可能会问:“工具和方法的区别是什么呢?” + +在这门课程中,工具是指以具体形式呈现的技巧,比如表格和思维导图等;而方法是指以抽象形式呈现的技巧,比如原则和思路等。 + +比如在筹建复盘团队的时候,岗位画像设计表属于工具,而选人原则就属于方法。 + +这些工具和方法都是拿来就能用的,其中工具你可以直接复制,根据实际工作需求稍微修改一下就行了,而方法则可能需要在思考和实践的过程中慢慢加深理解。 + +我把这门课程涉及的主要工具和方法总结到了下面这个表格里,供你参考。- + + +案例:CLAP模型 + +接下来,我就通过一个实战案例带你体验一下CLAP模型的使用过程。 + +我曾经帮助过一个烘焙公司做复盘。这家公司当时正处在快速增长期,每年都会开设新的门店,需要大量的资金。于是他们每到节假日,尤其是双十一、圣诞、元旦和春节前,都会有各种各样的充值回馈活动,也就是“充xx送xx”。 + +可是公司在营业额逐年增长的同时,利润率却在逐年下降。公司管理层觉得,问题可能出在会员充值的折扣上,但是他们又担心,如果不给折扣,就难以回笼现金,而没有现金就不能开新店了。 + +眼看着又快到年底了,他们希望高效地做一个复盘,能够设计出“在不伤害现金流的前提下提高利润率”的方案。其中重点关注的就是会员充值这一块的业务,他们想知道是否要调整,要怎么调整。 + +阶段一:会前准备 + +筹建团队和分配任务 + +因为这家烘焙公司并没有专门的战略委员会作为班底,所以需要临时组建一个复盘团队。我们采用了非常精简的配置,公司总经理担任主持人,主导整个复盘流程;我担任引导师,负责提问和逻辑引导;财务代表、营销代表和一线区域主管担任陈述人,负责整理和介绍各项资料。 + +资料准备 + +于是,财务代表提取了单品的物料成本、售价和销售数量,以及包括人工、场租和折旧在内的固定成本;营销代表打包提供了公司过去两年的所有营销策略;一线区域主管画出了常规业务的流程图。 + +因为这些数据和资料都是现成的,所以会前准备工作只用了不到半小时。 + +阶段二:复盘会议 + +对比目标 + +这家公司的盈利公式非常简单: + +销售收入 - 成本 = 利润 + +利润率 = 利润/销售收入 + +通过比对数据,我们发现,利润率跟去年相比下降了15%。 + +虽然利润率下降了,但其实销售收入和利润都是增加的,这就说明成本也在增加,而且增幅更大。我们初步判断,原因很可能出在成本控制上。 + +梳理逻辑 + +接着,我们用关键事件法把公司的整个价值创造过程分解为生产价值链: + + + +通过对整个价值链的分析,我们发现前5个环节的成本比例并没有显著增加,所以问题可以定位在销售环节。 + +然后,我们用情境还原法对销售环节的成本做了全面拆解,如下图所示: + + + +调用财务数据计算每一个部分的比例之后,我们发现,参加“买2000送500”这一项充值优惠活动的人数比例不大,但是送出的优惠总金额非常大。 + +财务快速推算了一下,如果去掉这一项,利润率会大大提升。 + +修正认知 + +于是我提出了一条洞察:对烘焙公司而言,总用户数比一次性充值金额更重要,因为产品品质足够好,所以并不担心用户复购问题。 + +基于这条洞察,我们做出了一个假设:选择“2000送500”的都是忠实客户,就算门店2C业务把这个活动直接砍掉,他们还是会选“买1000送150”的活动,所以总的充值人数不会减少很多。这些活动用过往的数据模型计算,对总销售额的影响并不大。 + +现在因为要顾及现金回笼,开设更多门店,服务更多客户,我们基本上确定了一个决策方向:对“买2000送500”采取一些动作,从而降低销售成本,提升利润率。 + +就这样,针对“在不伤害现金流回报的前提下提高利润率”这个目标,我们通过CLAP模型摸索出了一条有可能成功的路,整个复盘会议只用了2个小时。 + +阶段三:会后执行 + +制定规划 + +基于复盘会议形成的决策方向,我们制定了以下措施: + + +对于原有的2000元充值卡客户,规则不变。 +新的充值营销策略中,去掉“充2000元送500”这一项。 +强化“充1000送150”,一次买两张,送200元等值新品(推新)。 + + +评估效果 + +最后,我们通过效率、氛围、信心等角度评估了这次复盘的效果。 + + +效率:会议流程开放且高效,达成了有效结论,没有超时。 + +氛围:所有人的参与度都非常高,对过程和结果持正面积极态度,这和整个过程不是追责,而是群策群力关注如何围绕“不影响现金收入,但是降低销售成本”来规划。 + +信心:经过调研,团队整体对未来的目标达成非常有信心(信心指数四档:没信心/不好说/有信心/非常有信心)。 + + +总的来说,这次复盘非常成功。 + +适用场景 + +这个案例虽然并不复杂,但是“麻雀虽小,五脏俱全”,它已经囊括了整个复盘流程中的几乎所有事情。像这样一套标准的复盘流程,非常适合综合的、复杂度高的、时间跨度长的业务复盘或项目复盘。 + +不过在实际工作中,我们不是每次复盘都要像这样“一招一式,拳拳到肉”。比如突发灾难等紧急事件,我们必须快速响应;又比如只是一个规模比较小的问题,我们用不着动用太多的资源。 + +这些情况下,我们就需要结合具体场景的需求和条件,针对性地简化复盘流程,从而提高效率,节省资源。(实际上,在前面这个案例中,会前准备阶段就相对比较精简了。) + +美国陆军提出的任务后检视方法,又叫AAR模型,就可以看成CLAP模型的一个特定简化版本,适合需要现场决策和指挥的事情。 + +AAR(After Action Review) + +AAR模型有6个要素,也就是4W2H: + + +Who 参与者都有谁?主要角色包括:组织者、评论者、参与者。 +What 讨论什么?关键事件。 +When 何时?一般而言,最好马上开始。 +Where 何地?离现场越近越好。 +How long 需要多长时间?一般15分钟~1个小时。 +How to do 怎么做?一般包括6个步骤。 + + +其中“怎么做”的6个步骤,也就是AAR模型的复盘部分。 + + + +战争环境的不确定性和紧迫性造就了AAR模型复盘的特点,它非常敏捷,目标感极强,角色分工清晰,所以也非常适合同样具有不确定性同时又要求时效性的商业环境。 + +对于创新型企业或者转型期企业,这种模型应用很广泛,尤其是在扁平的项目化组织中。接下来,我还是用之前那家烘焙公司的例子来为你说明。 + +案例:AAR模型 + +在2020年新冠疫情期间,刚才提到的那家烘焙公司的业务也受到重创,老板发现年初设定的营业额目标缺口很大(AAR中的1,CLAP的C)。这是因为在商场不许进人之后 ,以前的所有营销策略都行不通了(AAR中的2,CLAP的L)。 + +这家公司的高管团队约我开电话复盘会议,一起研讨对策,商量的结果就是:既然人来不了,咱们就要走出去。 + +很多店长都加了客户的微信,之前他们都是被动地等客户发信息买产品,很少主动联系客户,因为怕打扰到对方,引起对方反感。 + +的确,每天一对一发产品信息,不管是优惠信息还是新品信息,是很容易让对方反感。 + +但是我们提出了一条假设:如果在主题群里发这些信息,只要对方不退群,仍然保持关注,那么我们就有机会(AAR中的3,CLAP的A)。 + +虽然没有社群运营的经验,但这件事情也没什么技术难度。于是我们迅速地制定了社群运营方案,并选取了总部所在地的三家代表性的门店试运营,两家商场店,一家小区店,它们都属于最会和客户聊天的团队。 + +每天后台营销部门都会推出各类产品组合的海报图文和小视频,店长以小区为单位拉群,定时在群里发布这些内容营造气氛,并通过私信和意向客户沟通转化(AAR中的4,CLAP的P)。 + +结果几天下来,每天的营业额几乎追平了门店正常时期的营业额(AAR中的5,CLAP的会后执行)。 + +后来,我们吸取了社群的沟通经验并形成FAQ指南,结合这个简单粗暴的社群营销方案形成MVP推广到了其他区域的门店,也都非常有效。(AAR中的6,CLAP的会后执行)后续各个门店在初始MVP的基础上优化迭代,形成更适合各自门店实际情况的社群运营方案。 + +小结 + +这一讲,我详细讲解了OPTM框架,并且以烘焙公司为例介绍了CLAP模型和AAR模型的使用方法。现在,我们回顾一下重点内容。 + + +在烘焙公司提高利润率的例子中,我们提出假设,选择“买2000送500”的主要是忠实粉丝,就算没有这个活动,他们依然会选择“买1000送150”,对总销售额影响不大。于是我们砍掉了这个活动,削减了成本,从而在不伤害现金流回报的前提下,提高了利润率。 +标准的CLAP模型复盘流程适合综合的、复杂度高的、时间跨度长的业务复盘或项目复盘。但是在实际工作中,我们需要结合具体场景的需求和条件,针对性地简化复盘流程,从而提高效率,节省资源,比如AAR模型就适合需要现场决策和指挥的事情。 +AAR模型是具备CLAP模型的特点的简化版本。在烘焙公司应对疫情的例子中,我们提出假设,如果在主题群里发产品信息,只要客户不退群,就不会太打扰对方。于是,我们制定了社群营销方案,追平了正常时期的营业额。 + + +思考题 + +这就是今天的全部内容,最后留一道思考题给你吧。 + +对于普通人来说,最常见的应用场景也许就是对自己工作的某个阶段(日/周/月/季度/半年/年或者实习期/试用期/入职一年/两次晋升之间)来做复盘了。 + +你能针对自己近段时间的工作,用这一讲学到的方法来做一次复盘吗?你觉得流程应该怎样简化呢?复盘之后你得到了什么有价值的结论吗? + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/03教练技术:怎么做复盘才能化解冲突?.md b/专栏/跟着高手学复盘/03教练技术:怎么做复盘才能化解冲突?.md new file mode 100644 index 0000000..623274d --- /dev/null +++ b/专栏/跟着高手学复盘/03教练技术:怎么做复盘才能化解冲突?.md @@ -0,0 +1,181 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 教练技术:怎么做复盘才能化解冲突? + + +你好,我是张鹏! + +上一讲我介绍了CLAP模型的使用方法。但是在实际做复盘的时候,你可能会经常遇到一个问题,那就是不同部门的员工之间发生冲突。 + +其实,大部分的冲突都是心态失衡引发的情绪冲突,也就是说,出于某些原因在主观情感上盲目地相信或者刻意地回避一些事情,所以固执地坚持错误的判断。比如,因为被质疑挑战之后面子上挂不住,就上头了,激动地跟人抬杠;又比如因为害怕被追责惩罚,就拼命地甩锅。 + +这种情况在复盘的各个环节都有可能发生,它一方面会让我们的讨论偏离主线,另一方面也会导致我们无法冷静客观地探讨问题,所以它也成了导致复盘效果不好的一个主要原因。 + +那么,有什么解决办法吗?当然有了,那就是这一讲我要介绍的教练技术。 + +教练技术 + +教练技术的创始人是美国的网球教练添·高威(Tim Gallway)。1975年,他找到了让任何人都能很快学会打网球的诀窍,从而引起了AT&T(美国电话电报公司)的兴趣。AT&T把他的教学方式借鉴到了企业管理领域,于是一种全新的管理方法,教练技术,就这样诞生了。 + +后来随着AT&T、IBM、通用电器、苹果、可口可乐和福特等知名企业的引入和推广,教练技术迅速在欧美等地区流行起来。 + +添·高威在《工作的内在诀窍》这本书中,总结了教练技术的公式:- + + +教练的追求并不是让学员因为不那么精湛的技术而感到羞愧,而是帮助学员提升表现,这种帮助具体可以分为两个方面,消除障碍和激发潜能。 + +这恰恰就是教练技术适合用来做复盘的原因,因为复盘的目的是优化,复盘团队的追求就是帮助团队和个人学习成长,从而拥有解决问题的能力。 + +所以,优秀的复盘组织者(或引导者)和厉害的体育教练一样,都是让团队成员的心态经历从“不行”到“行”的转变。 + +我做咨询顾问的过程中,除了帮助客户分析问题,提供相应的建议或者方案,还有很多的时候是担任高管(比如CEO)的复盘教练。我发现,就算是能力差不多的人,做出来的成果可能也有很大的差距,其中一个非常重要的原因就是他们做事时的心态不同。 + +我的任务并不是直接插手帮他们把活儿干了,而是当一个启蒙者、激励者、支持者和守护者,通过教练技术帮助他们发掘自己的潜能,探索出更多的可能性,从而更加快捷和从容地达成目标。 + +GROW模型 + +教练技术的应用可以分为四个步骤,英文首字母刚好组成GROW,所以又叫GROW模型,如下图所示: + + + + +Goal(界定目标):放下情绪冲突的意气之争,聚焦于真正的目标探讨。 +Reality(反映真相):Reality本意是“事实”,这里我说的是“真相”,因为我想强调要从行为的表象中意识到心态的本质。 +Options(改善心态):Option本意是“选项”,这里我说的是“心态”,因为我想强调不但要关注方案上的选择,更要关注心理上的选择。 +Will(计划行动):积极地制定行动计划,跨越障碍,解决问题,达成目标。 + + +接下来,我通过一个真实的案例来为你说明教练技术的GROW模型到底是怎么用的。 + +案例:打造爆款课程 + +前段时间,我参加了一家英语培训学校的战略复盘会。老板提出了一个目标,在一个季度内打造出新的爆款课程,从而提升引流效果。结果围绕这个目标,研发部门的A主管就和营销部门的B主管发生了一些摩擦。 + +第一步 界定目标 + +研发的A主管之前的主要工作是对老产品做优化迭代,还没有从0到1打造爆款的经验,所以她问了一个问题:“到底什么是爆款课程?” + +营销的B主管说:“很简单啊,就是那些卖得很好的课程啊。” + +A主管听了这话,反问道:“难道我们之前的课卖得不好吗?”气氛一下子变得有点剑拔弩张的感觉。 + +其实像这样的摩擦,在很多公司的产品和销售部门都会经常发生。别人轻描淡写的一句话,在自己听来,可能就是对自己专业程度或者工作成绩的否认。 + +实际上,A主管负责迭代的课程一直卖得不错,只是因为光靠以前的老课,公司增长稍微有点乏力,所以老板才想要打造新的爆款。 + +我马上意识到这是重点跑偏的信号,如果不想办法收住,复盘会就会从“大家对如何打造爆款的讨论”,变成“两位主管对能力和业绩的争论”。 + +所以我赶紧说:“小A,之前的课程你们做得很棒!接下来,公司还需要推出更多的爆款课程,非常需要你们的支持!” + +听完我的话,A主管的情绪缓和了一些,继续向大家寻求认可:“超级受孩子们喜爱的课程就是爆款,对吧?” + +大家都纷纷支持她:“是的,超级受孩子们喜欢的课程就是爆款。” + +B主管继续说:“小A,你说得没错,超级受孩子们喜欢的课程才可能成为爆款,同时我们也要考虑一下买单的人,也就是家长的需求。比如,家长花了这么多钱,能够收获什么?” + +这时,B主管做了一个特别好的区分:对于少儿课程来说,用户和客户是分开的,用户是孩子们,客户是家长。所以爆款也分为两个层面,既要是用户的爆款,也要是客户的爆款,也就是说,既要孩子们超级喜欢,也要家长愿意买单。 + +手机游戏就是反例,孩子们超级喜欢,但是家长往往特别抗拒。 + +于是,包括A主管在内,所有都达成了共识:爆款课程是孩子超级喜欢、家长又愿意买单的课程。 + +第二步 反映真相 + +接着,B主管又问:“小A,可以说一下你打算怎么打造新的爆款课程吗?” + +A主管回答说:“我们准备继续升级原来的课程,包括增加更多互动性的环节,加大知识密度等。” + +我察觉到了问题,A主管虽然认可了对爆款课程的定义,但内心还是在隐隐地抗拒做新课,所以我问她:“小A,你去一线多吗?” + +A主管说:“嗯嗯,我们每个月都有去校区的,我会和老师沟通很多关于学生和家长的情况。” + +我继续问她:“那你会亲自给孩子们上课吗?” + +A主管沉默了,一直不说话,我们都不知道为什么,但是她眼圈越来越红,眼泪都快掉下来了。旁边的同事连忙递给她一包纸巾,她在哭出来的同时说道:“对不起,我不敢去上课……” + +这句话震惊了在场的所有人。 + +之前我就听到有人反馈说,A主管好像不太愿意去一线上课。只不过当时他们都觉得A主管可能是认为上课这项工作太简单了,用不着身为教研主管的她亲自去做,完全没想到她居然是因为害怕而不敢去。 + +要知道,爆款一定是基于对用户需求的洞察来打造的,如果没有保持跟一线用户群的深度接触,要从哪里得出深刻的洞察呢? + +不过,使用教练技术的时候千万不能急于灌输结论,而应该想方设法地帮助对方自主提升。于是我带头行动,并示意参与复盘的其他人一起拥抱A主管,跟她说:“没事没事,小A你是最棒的!” + +很明显,A主管在大家的支持和鼓励中得到了力量,她停下了哭泣,开始吐露原因:“其实一年多以前,我也在一线上过课。当时我自己感觉上课的效果很好,孩子们反馈也特别好,但是家长的付费转化率却很低,甚至比一些新老师都低。” + +第三步 改善心态 + +当A主管坦诚地说出这个心结的时候,问题就变得简单了。 + +她因为害怕转化率低,被同事看不起,就不敢去一线上课;又因为长期没有在一线直接接触孩子和家长,就得不到深刻的洞察,更加害怕转化率低,也不敢做新课,结果就掉进了恶性循环当中。 + +问题的根源就在于,她因为看到了自己的一个短板,就否定了自己的全部,从此在个别方面变得很不自信。长期处在这种状态下,她的心理负担越来越重,所以才会因为一些小事就激化了情绪。 + +于是,我帮A主管做了一个区分:“给孩子上课,带团队做课,向家长卖课,是三种完全不同的能力。公司既然安排你当教研主管,就是肯定了你的业务能力,尤其是上课和做课的能力。至于转化率不高,可能有别的原因,比如你长得太好看,家长怕孩子上课分心。 + +“但是你现在的职责不是去做转化,而是要打造出新的爆款课程,重新回到一线上课,直接接触孩子们和家长,这是获得洞察最好的方式。” + +其实真正的原因更可能是A主管上课特别注重互动性和趣味性,结果很多家长看到孩子学得那么开心,反而担心学不到东西。不过当时为了迅速引导她调整心态,我故意那么说的。 + +其他人也跟着说了很多玩笑话来打趣她。终于,A主管卸下了负担,非常认可打造爆款产品需要回到一线这一点,于是她开始制定接下来的行动计划。 + +A主管的坦诚,既深深地打动了我,也赢得了所有人的真心支持。在后续的工作中,我发现这份坦诚也给整个团队带来了力量。 + +第四步 计划行动 + +最终,A主管不但决定回到一线上课,而且还补充了竞对研究等内容,她的计划是这样做的: + + +x月x日之前,完成按照年龄段/学科的竞对SWOT分析。 +x月x日之前,协同教研部门同事,在不同校区一线上课xx课时,积累至少100个孩子1000学时的学习观察。 +每周迭代一个新品的版本,在不同的校区检验效果,同时做阶段复盘,并且将复盘结论记入公司知识库,交由其他同事交叉验证。 + + +怎么样,这样的A主管很棒吧?其实你的身边可能也有很多这样的同事,他们原本可以很好地理解你的想法,配合你的工作,和你一起完成目标,只不过因为一时的心态问题,站在了你的对立面,和你发生冲突。 + +但如果你掌握了教练技术,就可以有效地和他们沟通,帮助他们调整好状态。 + +四种基本能力 + +刚才我已经介绍了教练技术的4个步骤,带你了解了使用的流程。 + +那么,怎么才能用好教练技术呢?这就需要你培养4种基本能力了。- + + + +聆听:通过聆听发现对方可能存在的问题,比如我从一开始的对话中,听出了A主管的情绪变化是因为担心自己不被认可。 +发问:通过提问让对方袒露出真实的想法,比如我通过问A主管有没有亲自去一线上课的问题时,找出了她的心结。 +区分:通过区分帮对方卸下心理上的负担,比如我区分了上课、做课和卖课的能力,帮A主管重拾信心。 +回应:通过回应及时给予对方有力的支持,比如我对A主管的拥抱、鼓励和调侃,帮她恢复到更加积极的心态。 + + +当你拥有了这4种基本能力,就能像我一样非常顺利地使用教练技术了。我把4种能力在4个阶段的常见应用技巧总结在了下面这张表格里,供你参考。 + + + +注意,这张表只选取了一部分具有代表性的技巧,并没有总结所有的技巧,你需要结合自己的工作实践慢慢扩充完善它。这个扩充完善的过程,就是你自己提升能力、沉淀经验的过程了。 + +小结 + +这一讲,我为你讲解了教练技术,并通过一家英语培训学校的例子,为你说明了教练技术的使用方法。现在,我们回顾一下重点内容: + + +教练技术追求的是帮助对方提升表现,这种帮助可以分为两个方面,消除障碍和激发潜能。复盘时使用教练技术,可以和对方有效沟通,化解冲突,帮助团队和个人成长。 +教练技术包括4个步骤,界定目标,反映真相,改善心态和计划行动,它们组成了GROW模型。 +教练技术需要4种基本能力,聆听,发问,区分和回应。你可以通过扩充完善技巧表格,来提升能力、沉淀经验。 + + +思考题 + +这就是今天的全部内容,留一道课后思考题给你吧。 + +请你回忆一下,在以前的复盘过程中,有没有因为心态失衡而跟同事发生冲突呢?当时心态出现问题的人是你还是对方?在这一讲学完教练技术之后,你觉得当时怎么处理会更好? + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/04制度和文化:怎么营造复盘环境?.md b/专栏/跟着高手学复盘/04制度和文化:怎么营造复盘环境?.md new file mode 100644 index 0000000..c38267f --- /dev/null +++ b/专栏/跟着高手学复盘/04制度和文化:怎么营造复盘环境?.md @@ -0,0 +1,213 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 制度和文化:怎么营造复盘环境? + 你好,我是张鹏。 + +从今天开始,我们进入到课程的第二部分,实操流程篇。 + +在第2讲,我已经介绍过,标准的复盘流程分为三个阶段,会前准备、复盘会议和会后执行。 + + + +但是,这一套标准的流程建立在团队内部已经对复盘建立了共识的基础上。如果大家都不认可复盘的价值,不愿意投入足够多的时间和精力来做复盘,那么你会发现,再怎么推都推不动这件事情。 + +所以要想让复盘顺利落地,不光要掌握复盘流程、工具和方法,也要营造复盘的环境。这就像种地,要想有好的收成,在施展耕作的经验之前,先要做好农田的养护。 + +那么,怎么营造复盘环境呢?具体来说分为两个层面,制度层面和文化层面。其实不只是复盘,但凡你想让一项新政策在管理上落地,都可以从这两个层面出发来思考问题。 + + + +制度层面 + +对于制度层面的设计,我想从三种不同的视角来介绍。 + +1. 中高层管理者:效率 + +首先是中高层管理者的视角。前段时间,一位高管朋友就跟我聊到他遇到的烦恼: + + +直播团队最近业绩不太理想,我让他们的Leader组织了一次复盘会,结果这个Leader就让每个成员轮流发言,介绍自己的工作,最后整个会议非常冗长拖沓。 + +而且,当时虽然总结了几点改进措施,但是等我半个月之后想起这件事,想要了解后续执行进度的时候,我发现不但需要专门一个一个找人去问,还要等对方临时整理材料,最后汇报的成果也不是很清晰。- + +我就感觉这次复盘非常低效,不但耽误我很多的时间,而且还看不到什么效果。 + + +其实这是中高层管理者面临的非常典型的问题。因为他们的时间非常宝贵,往往都会养成高效推进、聚焦重点的工作习惯。 + +所以,当他们想在整个公司或者某个业务部门推行复盘的时候,一定会关心效率,尤其是开会的效率和对齐同步的效率。 + +怎么才能让复盘会不那么冗长拖沓呢?让每个团队成员挨个发言的开会方式太低效了,一定要优化。 + +科学管理之父弗雷德里克·泰勒(Frederick W. Taylor)在《科学管理原理》这本书提出了两个关键概念,流程标准和专职管理。 + +基于他的理念,我设计了两个方法。一个是五步法,用来高效地组织复盘会议,我会在第7讲详细介绍;五步法要依靠团队来执行,所以另一个方法是三角法,用来组建复盘团队、做好角色分工,我会在第5讲详细介绍。 + +怎么才能让中高层管理者方便地查看复盘之后的执行结果呢?对于中长期复盘,主要是通过OKRAP工具,我会在第11讲详细介绍;对于短期复盘,就要做好归档和更新工作,我会在第12讲详细介绍。 + +2. 小团队Leader:可控性 + +然后是3~10人小团队Leader的视角。做培训的时候,我曾经遇到很多小团队的Leader跟我请教类似的问题: + + +快到年底了,领导让我组织团队成员一起做个复盘。我把任务分发下去之后,他们都说工作太饱和,没时间做。催了好多次,才陆续收上了一些材料。- + +超过截止时间才发的人有好几个,而且有些人发的材料一看就是没用心做,随便糊弄一下完事。我如果不管,他们以后更不把复盘当回事了;可要是上纲上线地惩罚,又有点小题大做。 + + +这是小团队Leader面临的典型问题。因为他们要为团队的业务结果负责,所以非常在意事情是不是按部就班,能不能稳定地产出结果。 + +所以,当他们在接到复盘任务的时候,一定会关心可控性。 + +优先级 + +怎么帮团队成员处理好本职工作和复盘之间的冲突呢?这其实是工作事项优先级排序的问题,有4条通用的方法: + + +让团队成员判断各项工作的优先级,先做“重要性”更高的事情。 +提供求助通道,比如无法确定优先级时,应该主动向Leader求助。 +建立督导机制,定时检查和确认每个人的工作优先级。 +平时就要养成良好的工作习惯,为一些常见的临时性工作做好准备。 + + +关于工作优先级,我补充说明一下。很多人都学过“紧急重要矩阵”,优先级最高的是重要且紧急。 + + + +我想告诉你的是,这样做可能会让你掉进陷阱。如果你总是做重要又紧急的事情,慢慢地你会发现重要又紧急的事情越来越多。其实这是一个错觉,真实的情况是你慢慢地变得只会按照紧急排序,重不重要已经很难分清楚了。 + +其实对于工作比较饱和的岗位来说,根本没有充足的时间和精力去做4个象限的优先级判定(而且还有及时更新,比如之前不紧急的事过一会儿也许就变得紧急了)。可行性高的做法是,按照重不重要这一个维度去排就行了。 + +更何况,每天的工作中本来就应该预留一定的时间给临时性工作。而对常见的临时性工作,平时就要做好资料的整理工作,这样事到临头才不会慌了手脚、顾此失彼。 + +奖惩措施 + +本职工作比较好约束,直接跟绩效挂钩。但是对于临时性的非本职工作,只能通过一些小型的奖惩措施来影响。这个事情看似简单,不过也有一些细节需要提升。 + +一般来说,对于确定性标准的行为,我建议用“处罚”来约束;而对于不确定性标准,我建议用“奖励”来激励。 + +举个例子,比如复盘会迟到,或者没有按时提交自己负责的材料,这就是违反确定性标准的行为,怎么办呢?你可以制定一个惩罚制度,每违反一次要贡献20元团建经费。 + +而随便应付复盘工作,就属于违反不确定性标准的行为。这个情况,处罚是不合适的,毕竟很难评估一个人是能力不行,还是意愿度不够。 + +这时候,奖励是更好的办法。你每次复盘结束的时候,可以加多一个环节,评选这次贡献最大的人,并予以奖励(比如一杯奶茶或者“小红花”等积分荣誉)! + +3. 新人:学习成本 + +最后是新人的视角。很多管理者都会遇到一个问题,好不容易让现在的团队成员适应了复盘,但人员流动是不可避免的。每次有新员工加入,又要费好大的力气去做传帮带。 + +很多新人入职之后,还是会害怕复盘变成追责和背锅,或者仍然习惯性地把复盘当总结来做。这是因为,改变一个人长期形成的工作习惯,是一件比较难的事情,而且任何人学习新事物,都有一个逐渐适应的过程。 + +管理者要做的,就是通过各种手段尽量降低新人的学习成本,具体可以分为两个方面。 + +书面文档 + +一是把团队之前积累的经验沉淀下来,整理成系统的书面文档资料。 + + +利用协同办公工具,比如钉钉、Tower、石墨文档等,记录复盘经验。 +对复盘经验进行分类,按照项目、部门或流程来分都可以,形成清晰的逻辑,方便查找。 +定期组织迭代工作。 + + +新人加入后,把这些资料提供给他就行了。 + +辅导机制 + +二是形成系统的辅导机制,形成竞争和帮扶的环境。Facebook在这一点上做得特别好,他们设计了两个机制: + + +新兵营:它比较像国内娱乐频道的偶像天团打造过程,在新兵营中,大家一开始就接触任务、进行PK,同时导师和学员互选、进行辅导,继续进行PK。 +导师制(Mentor):导师是自愿带新人的老员工,不一定是新人的直接领导。这个角色相当于是新人的教练,会把公司的各种工作流程注入新人的心智,帮助新人尽快融入公司。 + + +文化层面 + +制度层面的措施,只是减少了复盘的障碍。如果想让团队成员发自内心地认可复盘,还需要从文化层面入手,把复盘融入企业文化的一部分。 + +接下来我就从是什么(What)、为什么(Why)和怎么做(How)三个角度来介绍。 + +1. 是什么(What) + +首先说是什么。 + +企业文化有三大核心:使命、愿景、价值观。 + + +使命指的是我们存在的理由,或者说我们解决了什么问题; +愿景是我们要发展成什么样子; +价值观说的是我们倡导什么,反对什么,遵守什么信条、准则。 + + +如果对应在复盘中: + + +使命是提升团队成员的认知,优化未来的目标和计划; +愿景是帮助我们成为学习型组织; +价值观是反对追责导向,反对敷衍,倡导以学习和提升认知为导向,倡导知行合一等等。 + + +2. 为什么(Why) + +其次说为什么。 + +为什么要搞企业文化这种看上去很虚的东西呢?因为业务增长多数情况下会带来熵增,而组织成长的本质是提升墒减的能力。 + +一开始,伴随着业务增长,团队协同变得越来越复杂,所以需要建立各种流程和制度让事情变得有序和高效。但是慢慢地你会发现,越来越多的流程和制度,反而成了高效协同的掣肘。 + +好的企业文化,恰恰能够精简制度和流程,消减这种掣肘,这样团队才有可能升级成为人人向往的、没有部门墙的无边界组织。 + +同样地,复盘文化的影响也会体现在各种流程和制度的小事上,比如: + + +类似的错误每隔一段时间有没有反复出现? +复盘会的决议,后续有没有跟进落实? +复盘会的氛围是甩锅还是优化? +组织和参与复盘的时候,大家是积极主动还是拖沓被动? +大家对于不同意见的心态,是开放接纳还是封闭抵制? + + +如果我问你,你更愿意在哪种环境下工作?哪种环境下更容易高效地产出成果?我想答案应该是不言自明的吧。 + +3. 怎么做(How) + +最后说作为管理者,应该怎么做。 + +第一,提取几个希望长期坚持的复盘文化的关键词,比如不追责、不甩锅、开放、共识和知行合一等。 + +第二,对关键词做出解释,具体提出我们要遵守什么,反对什么,倡导什么?举出违背和相符的具体言行作为例子。 + +第三,保持对关键词的敏感度,勇于改变,比如当流程制度让团队成员感到疲惫不堪的时候,及时迭代优化。文化是让人没有制度也会去做事的行为准则和习惯,对一个人是这样,对一群人也是这样。 + +整个过程我们做成一张这样的文化词典表格: + + + +在表格中,我仅仅是示范填写了一部分,你也可以按照你们团队的具体情况来设计这样的文化词典表格。 + +当然,仅仅设计出这样的文化词典还是不够的,为了保证制度和文化能够落地,我们需要组建专门的复盘团队来负责管控、辅助和督导等工作,这就是第5讲将要介绍的。 + +小结 + +在这一讲中,我从制度和文化两个层面介绍了营造复盘环境的方法(其实这些方法不只适合推行复盘,也可以用来推行其他事情)。现在,我们回顾一下重点内容。 + + +想让复盘顺利落地,不光要掌握复盘流程、工具和方法,也要营造复盘的环境,包括设计制度和打造文化两部分。 +制度层面,可以从中高层管理者、小团队Leader和新人三个角度出发,提升效率,加强可控性,降低学习成本。 +文化层面,可以提取和解释关键词,保持对关键词的敏感度,及时对流程制度进行迭代优化。 + + +思考题 + +这就是今天的全部内容,最后留一道思考题给你吧。 + +除了我举的这些例子,你还能想到哪些在制度或文化层面营造复盘氛围的措施吗? + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/05三角法:怎么组建复盘团队?.md b/专栏/跟着高手学复盘/05三角法:怎么组建复盘团队?.md new file mode 100644 index 0000000..0e7385a --- /dev/null +++ b/专栏/跟着高手学复盘/05三角法:怎么组建复盘团队?.md @@ -0,0 +1,217 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 三角法:怎么组建复盘团队? + - +你好,我是张鹏。 + +上一讲我讲解了怎么在制度和文化层面营造复盘环境,其中很重要的一个举措就是组建复盘团队,并且做好团队成员的角色分工。这一讲,我将为你介绍具体的方法。 + +发起 + +复盘团队的组建,需要有一个发起人,通常由团队的一把手兼任。因为复盘能否落地,涉及到设计制度和打造文化,这些都需要绝对的推动力,一把手显然是最好的人选。 + +通过前面的学习,你明确了复盘需要实现什么样的目标,创造什么样的环境,发起人确定之后,就需要选人了。 + +选人原则 + +选人招聘工作中,越是重要又难以培养的品质,优先级越高。所以,绝大部分涉及到选人组队的工作,都可以使用一个基本的排序原则: + +意愿度 > 基本素质 > 知识结构 > 专业能力 + +同样地,复盘团队选人的首要要求也是意愿度,愿意成长、愿意付出、不计较短期利益得失的人优先。 + +担任复盘团队成员,要做很多本职工作以外的事情,但是一般没有额外的物质奖励,借用乔布斯的话来说,“The journey is the reward(过程即回报)”。 + +在实际情况中,虽然我们在选人的时候要重视对方自己的意愿度,但是一般来说,优秀的人在公司里往往已经承担了比较多的工作,可能因为担心时间和精力照顾不过来,而对参与复盘不是很积极。 + +所以我们不但要公开招募,也要结合一定程度的强制摊派。怎么保证因为强制摊派而接到任务的的人,也能有比较高的意愿度呢?这可能就需要发起者做一些思想工作了。 + +比如,可以告诉他们,复盘的历练可以让他们得到更有深度的思考训练和多角度看待问题的视野,同时复盘也是对未来的储备干部进行培养和选拔的一个重要环节。 + +另外,如果在确认意愿度的情况下,还需要进一步挑选,可以评估一下参选者的基本素质,比如逻辑思维能力和做事情的靠谱程度。 + +由于复盘经常涉及到跨部门的业务,因此,知识结构和专业能力并不是最重要的。比如,一个研发部门的同事,完全可以作为复盘团队的成员,协助去做营销部门的复盘工作。这样的历练对培养T型人才非常有帮助。 + +复盘团队的成员选出来之后,需要明确告诉他们接下来要做什么,这就涉及到角色定向。 + +角色定向 + +定向指的是界定工作的内容、范畴、边界和流程等。 + +我们可以对参加复盘会议的组织者整体进行定向,比如复盘的重点是调整团队的情绪,还是解决具体的问题;也可以对每个关键角色一对一地进行定向,比如不同的角色在会议中主要的职责是什么,该说什么该做什么。 + +也就是说,每个角色都需要对以下问题得出确定且清晰的答案,同时取得复盘会议全体成员的认同,并与高层对复盘的要求对齐。并且能够保证在复盘会议中保持高度觉知力,不会偏离。 + +在这里我们需要清晰的对常见的定向问题进行了一个分类: + +整体定向的问题包括: + + +我们这次复盘会议的目标是什么?(如:解决哪个关键问题?) +我们要创造一个什么样的氛围?(如:安全、开放、包容) +确认这次复盘与提升团队认知有什么帮助?(如:务必是紧密相关) +确认这次主题的探讨与公司的战略目标有什么关系?(如:务必是因果关系) +我们的承诺是什么?(如:我们会引领大家达成怎样的目标?如果没有达成,我们会怎么做?如果达成,我们会怎么做?) + + +这个部分,主要是让复盘团队全体对复盘的目标有个共识。 + +一对一定向的问题包括: + + +针对记录人的问题: + +我们需要准备什么?(如:资料收集、数据分析等。) + +会议设备是否都准备好了?(录音笔、相机、白板纸笔、笔记本等等) + +针对主持人的问题: + +我们的流程是什么?(如:第一步做什么,第二步做什么) + +会议中有可能会出现什么样的情况?我们如何应对。 + +针对纪律官的问题: + +会议的规则是什么?(如:陈述时间多长?发问时间多长?是轮流来,还是举手随机来?) + +如果遇到领导发飙,你怎么处理? + +如果一个人说话超时太多,你怎么处理? + + +(注:记录人、主持人和纪律官三种角色,下面就会详细介绍。) + +这个部分,是让各个角色提前做好预案,相当于把流程彩排一遍,这样做能够提升复盘会议的效率。 + +职责分工:三角法 + +在数学上,三角形是最稳定的形状。而组建复盘团队的时候,我们也可以用三角法,搭建一个各司其责的三角团队,从而保障复盘会议的产出、效率和氛围。 + + + +在三角团队中: + + +一方是辅助者,由复盘团队担当,他们并不负责生产内容,主要负责会议氛围、流程规范和效率; +一方是参与者,由项目或者业务团队担当,他们主要负责内容的贡献和复盘结论的共创; +还有一方是引导者,一般由外部复盘专家担当,主要负责站在中立的立场上,引导参与者从现象到本质的探讨和逻辑推演过程。 + + +下面我对每一种角色的工作职责逐一说明。 + +辅助者 + +复盘会议的辅助者包括主持人,纪律官和记录人,这些人员基本上都是固定的,一般由复盘团队产生(也就是刚才一对一角色定向的人)。辅助者是确保复盘会议能够正常按流程筹备、召开和迭代下一次任务的团队。下面我把辅助者的三种角色的主要工作职责和实际会议场景下的话术范式给你做个讲解。 + + +主持人 + + +当有人开始长篇大论的时候,大家可能会在某个细节讨论过多,拖慢了整体的节奏(特别是领导,一般人不敢轻易打断),导致复盘会议特别冗长低效。 + +所以,复盘团队需要有主持人来推进流程,把握主线,比如可以说: + + +“提醒一下,我们现在主要是讨论XX。” +“请XX发言。” +“还有谁有不同的看法吗?没有的话这一部分就过了。” + + + +纪律官 + + +在复盘会议中,当领导发现某个负责人的工作结果很不好,很可能忍不住就开始劈啦啪啦一顿“怼”,这一方面会耽误时间,另一方面也可能把复盘优化变成“对人不对事”的训斥甚至情绪发泄。 + +另外,还有很多人参加复盘会议的时候会出现迟到、资料准备不合格、一会儿出去接个电话等情况,也会影响会议的效果。 + +所以,复盘团队需要有纪律官来提醒监督,维护秩序,比如可以说: + + +“XX,记得提前准备好资料。” +“XX,请注意把焦点转移到对事情的讨论上来。” + + + +记录人 + + +你可能遇到过这种情况,明明复盘的时候讨论得热火朝天,也得到了有价值的结论,制定了相关的计划,但是在会后总结和执行的时候,大家却发现很多信息记不清楚,或者不同的人说法不一样,各执一词。 + +所以,复盘团队需要有记录人来记录总结,及时同步。 + +参与者 + +参与者作为内容输出最主要的贡献者,由项目或者业务团队成员担当,根据每个环节的设置情况,可以分为陈述人和设问人。 + + +陈述人 + + +陈述人的职责是整理资料,陈述事实,通常是复盘的主要对象(团队小伙伴)。比如对产品运营策略的复盘,陈述人就是产品和运营部门的代表,部门其他成员一般也会来参与,方便随时补充,毕竟只有他们自己才最了解事情本身的信息。 + + +设问人 + + +如果介绍情况的时候只有陈述人,很容易出现“自嗨”的情况。因为陈述人虽然对信息比较熟悉,了解比较全面,但是分析不一定透彻。 + +陈述人口沫横飞地讲了半天,如果没有人给出不同角度或不同深度的反馈,最后可能还是会陷在以前的思维误区里,这样得出的结论和计划,效果可想而知。 + +所以,复盘团队还需要设问人来提出问题,协助深挖,一般是陈述人的上下游部门成员来承担。 + +在复盘会议中,陈述人和设问人可能会经常交换角色。比如A做产品,B做市场,A陈述产品的内容时,B就可以从市场的角度对A进行提问,反过来B陈述市场的内容时,A也可以从产品的角度对B进行提问。 + +就算A和B都是做产品的,比如负责不同的模块,也可以同样的形式推进会议——一个说,一个问。这样会提供多样性的观察视角,有利于提升团队看问题的全面性。 + +引导者 + +团队长期在一起工作,思想往往会越来越趋于同化。这时候特别需要一个引导者来观察控场,激活思路,比如可以说: + + +“接下来,XX提一下你对这个问题的看法。” +“这个事情还有没有别的做法?” +“如果这样XX做,你看看结果会不会不一样?” + + +如果没有这个角色,一方面容易导致“集体自嗨”,另一方面很难实现真正的创新,因为大家都全部PDCA去了。 + +所以引导者一般不能是项目内部人员,而是专门请外部的咨询顾问或者跨项目组的同事来担任,这个角色必须有很强的控场能力和业务理解能力,但是又不在项目中,立场保持相对中立。 + +我把每个角色的复盘工作注意事项整理在了这个表格里。 + + + +你可能会有疑问,我们团队一共也没几个人,每个角色分配一个人都不够。大家在这里不要被这些角色给吓住了,一个人可以扮演若干个角色,最极致的情况是,整个团队只有一个人,那也没关系,一个人就是一支团队,你干好所有的活儿就行。 + +这种角色划分和职责要求几乎已经涵盖了大型业务复盘的方方面面。如果只是小问题或者项目级复盘,你需要心里有根弦儿,在所有任务中选择复盘的任务和对应的角色就行了。 + +我总结了一张表,可以作为复盘团队做角色分工时的参考,你也可以根据你自己团队的实际情况进行调整。 + + + +小结 + +在这一讲中,我为你讲述了复盘团队如何组建以及在复盘之前的角色定向。现在,我们回顾一下重点内容。 + + +复盘团队的发起人一般是团队一把手,选人的时候遵守:意愿度优先,重点考察团队成员的逻辑分析能力和做事靠谱程度; +在会议之前需要做好复盘团队的角色定向,团队整体负责会议的目的、进度和氛围,主持人、纪律官和记录人各司其职; +在复盘会议中,用三角法对所有与会者进行分工,分别是:负责流程的辅助者,负责内容生产的参与者和负责逻辑推演和控场的引导者。 + + +思考题 + +这就是今天的全部内容,最后留一道思考题给你吧。 + +如果针对你所在团队的业务战略或你参与的某个项目的问题来做复盘,你觉得对应三角法中的三类角色(六种细分角色),分别应该选哪些人呢(不用写具体姓名)? + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/06MECE原则:怎么准备复盘资料?.md b/专栏/跟着高手学复盘/06MECE原则:怎么准备复盘资料?.md new file mode 100644 index 0000000..8e485e4 --- /dev/null +++ b/专栏/跟着高手学复盘/06MECE原则:怎么准备复盘资料?.md @@ -0,0 +1,185 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 MECE原则:怎么准备复盘资料? + - +你好,我是张鹏。 + +上一讲,我介绍了组建复盘团队的方法,这样你就可以完成复盘的人员准备工作。今天这一讲,我会介绍准备复盘资料的方法,学完之后你就可以完成复盘的资料准备工作了。 + +你可能会想,准备资料还需要专门去学吗?不就是写写工作的总结,准备一些图表就行了吗?有些公司甚至都准备好了固定的模板,我只要把最重要的数字填进去就行了。 + +其实并不是这么简单。很多人做复盘之所以得不出正确的、有价值的结论,往往就是因为准备资料时敷衍了事。这就好比打仗,如果没有掌握准确的情报,又怎么把握战机,制定合理的作战计划呢? + +常见问题 + +我想请你回忆一下,开复盘会议的时候,你有没有遇到过有下面这样的情况: + + +信息重复:会议中重复的问题多,包括: + + + +自己跟自己重复,看似列了十多条,实际上归纳整理成3~4条就够了。 +自己跟别人重复,轮流发言,没有分工,前面提到的问题,又翻来覆去地说。 + + +结果就是大家觉得会议拖沓,久而久之,参与复盘会议的热情也开始降低了。 + + +重点环节数据缺失:在复盘会议中,讨论的议题遗漏了关键数据,被人指出的时候,临时去找又来不及,结果讨论就无法有效地进行下去了。 + +内容散乱:内容都有,但是特别散,不同部门很难通盘考虑自己的工作和其他部门的工作之间的关联关系,或者说关联的颗粒度不够细致,导致逻辑串联不起来。结果,可能的结果是更爱发言的人主导了整个复盘会议,会议失去了把控全局的平衡感。 + + +如果你也被这些情况困扰过,那么不妨试一试MECE原则。 + +MECE原则 + +MECE的全称是“Mutually Exclusive, Collectively Exhaustive”,意思是“相互独立,完全穷尽”。 + +它是麦肯锡的第一个女咨询顾问巴巴拉·明托(Barbara Minto)在《金字塔原理》(The Minto Pyramid Principle)中提出的一个很重要的原则, 就是对于一个重大的议题,能够做到不重复、不遗漏地进行分类,从而有效地把握问题的核心,找到解决问题的方法。 + +MECE原则要怎么理解呢?不知道你还记不记得,在第2讲中我举过一个烘焙公司的例子。现在假设你是一家门店的店长,正在准备复盘要用到的客户数据,你会怎么对客户进行分类呢? + +第一种分法,也是最简单的分法,就是根据性别把客户分为男女两类。这样每个人都能归入其中一类,这就叫“不遗漏”,并且没有人会同时属于这两类,这就叫“不重复”,显然这种分法是符合MECE原则的。 + +这样分类之后,你就可以区分不同性别客户在购买产品时关注的价值点,以便设计不同产品的包装和摆放位置,从而根据消费者在店内的动线设计自己的MOT(Moment Of Truth,这个理论用于设计客户在每个决策点上的峰值体验)。 + +第二种分法是根据年龄把客户分为“10岁以下”“10到20岁”“20到30岁”,一直到“60岁以上”,这也是符合MECE原则的。 + +第三种分法是同时考虑性别和年龄,比如把“10岁以下”进一步分成“10岁以下男性”和“10岁以下女性”,这也是可以的。 + +这样分类之后,你就可以分析在不同时间段的主流客群,再针对这些客群喜欢的产品细分品类进行映射,针对特殊节假日(比如儿童节、情人节、母亲节和父亲节等)搞一些相应的活动,制定相应的备货计划和销售策略。 + +但是,如果把“成年女性”进一步细分为“学生”“老师”“公司职员”和“家庭主妇”,就不符合MECE原则。一方面,作为个体户的女性无法被归入其中,出现了遗漏;另一方面,老师也有体制外的,可能和公司职员重复。 + +这种情况下,你可能会眼睁睁地失去一些客户群体,或者在做市场分析的时候产生混乱。 + +所以,如果你想要得到准确的复盘结论,那么在资料准备阶段,就必须着重检查是否有重要因素被遗漏,以及是否存在相同要素的重复,尤其是遗漏,因为它会大幅削弱说服力和解决问题的效果,这一点需要特别留意。 + +不过,虽然MECE原则确实非常重要,但是过度拘泥于MECE就会本末倒置,反而会对逻辑思维产生阻碍。 所以有的时候,你也可以活用“其他”这一项,适当简化分析过程。 + +注意事项 + +下面,我来说明一下使用MECE原则时的注意事项。 + +1. 速度优先 + +真正百分之百的无遗漏和无重复,在实操过程中其实是很难做到的。哪怕是把客户分成男女两类,可能也不严谨——“人妖”怎么算?性别认同障碍的客户怎么算? + +一旦过度追求细节,就容易浪费时间,干扰正常思路。在商业活动中,速度是第一位的,存在一些不够精细的部分是可以容忍的,重要的是一定要保证高效地推进工作进度。 + +所以你必须清醒地认知到,重复是难以避免的,尽一切可能确保没有重大的遗漏就行了。 + +2. 明确目的 + +比如说烘焙公司的目的是开发和销售产品,所以会按性别、年龄和职业来分。如果你按照有没有房、有没有车、户口是城市还是农村这些标准来分,结果可能没什么意义。 + +明确最终的目的,才能做出有价值的分类。 + +3. 打破定势 + +就算是在同一个行业、同一家公司、基于同一个目的,分类方式也未必是固定的。有的时候我们恰恰是被思维定势给限制了。 + +跟你分享一个故事吧。刚才我提到的这家烘焙公司的老板是从一家门店开始创业做起来的,开了十几家门店之后,她有了更大的梦想,于是开始对标行业龙头H公司。 + +经过对H公司的实地调研和资料对比,她发现自己的门店无论是人效还是坪效,都不比H公司差,但是平均到单店的业绩却比H公司差很多(H公司已经上市,经营数据都是公开可查的,单店业绩=总业绩/门店数量)。 + +后来,她终于找到了原因。原来自己用MECE原则划分客户的时候,一直是按照单个门店的分法,分成不同年龄段的男性和女性;但是H公司却从整体上把客户分为个人客户和企业客户。企业客户贡献了差不多H公司一半的业绩,但这个市场她一直都没有做。 + +这个发现让她如获至宝,于是她马上成立了大客户部专门负责企业客户,而个人客户继续交给门店负责,于是在接下来的几年里又取得了很大的增长。 + +其实无论是创业者还是上班族,多数人都是从基层一点点走上来的,每次升迁,需要更高层次的视野和格局来支撑。如果你能够打破思维定势,及时更新使用MECE原则的方式,会少走很多弯路。 + +MECE的应用:逻辑树 + +刚才解释MECE原则,我用的主要是客群分类的例子。不过MECE原则的应用远不止这一项,在复盘过程中,还有一个非常重要的应用值得专门介绍一下,那就是逻辑树(Issue Tree)。 + +逻辑树是基于MECE原则把主题逐层分解的工具,具体做法是把作为整体的主题放在最上方(或是最左边),然后自上而下(或从左到右)展开分枝,写出要素。因为分解之后的形状很像树木,所以才叫这个名字。 + +比如复盘会议需要讨论如何减少成本,我们就可以使用逻辑树来对成本进行分析(如下图所示),看看从哪一块成本入手效果最明显。 + + + +使用逻辑树来准备资料和数据的好处在于: + + +提前完成去重工作,能提升会议的效率和大家的参与度; +重要信息不会遗漏,避免在会议中讨论议题时,出现论据缺失的情况; +提前完成对资料的逻辑梳理,避免在会议上讨论失去平衡,在某些局部问题上纠缠过多。 + + +所以,通过逻辑树看问题或者看业务,很容易看清全局,理解公司业务和自己负责的模块之间的关系,经过逻辑分析之后再聚焦到某个局部展开,最终找到问题的关键并解决问题。 + +复盘需要什么资料? + +了解了MECE原则之后,我再给你介绍一下在复盘中常用的资料。 + +资料要素 + +每个团队在复盘过程中用到的图表模板可能不同,但是一定不能缺少以下三条要素。 + +要素一是目标设定,也就是对设定的目标做出符合MECE原则的分类以便观察。想做好目标设定,需要注意以下4点: + + +高层共识:公司的目标至少要在分管各个业务模块的O级或者VP级取得全体共识。 +上下对齐:团队的目标要和公司的目标,以及团队中个人的目标做对齐。 +左右拉通:自己的目标需要和业务或者价值链的上下游部门的目标能够对应得上。 +归类聚集:光有数字型的目标,大家很难理解这些数字之间的关系到底是什么、这些数字背后的意义是什么,所以需要对目标做归类聚集并给出文字说明。 + + +要素二是每个目标要有对应的行动计划,包括3W3H要素: + + +What:计划内容和对应策略是什么? +Who:谁负责? +When:截止时间点是什么时候? +How:具体步骤和任务分配是怎样的? +How To Measure:结果怎么衡量? +How Much:需要多少Head Count和预算? + + +要素三是结果呈现,与目标的行动计划格式一致,根据实际情况填写就行了,这一部分内容我会在第11讲详细说明。 + +CheckList + +基于这些要素,我整理了一份关于复盘资料准备的CheckList供你参考。 + + + +总的来说,复盘资料可以分为公司、项目和会议三个层面,业务、事件和个人三种不同类型的复盘需要的东西也不完全相同。在这份CheckList中,打√的是我建议的必选项,具体内容你可以根据自己工作的实际情况进行修订。 + +下面,我专门介绍一下会议层面的资料。 + + +日程说明:主要是介绍会议的流程以及每个环节的议题是什么,大概需要多长时间。 +会议守则:这个部分采取列表的方式告知大家,会议要求每个人都要遵守的规则。比如手机静音,发言需要举手,不要迟到,发言注意不要超时等。 +目标结果对照表:这个主要用于比较目标和结果,在第8讲中会详细说明。 +会议记录:记录会议过程中大家的发言、图片或者照片影像资料。 +决议结果:整个复盘会议的最终输出,包含达成共识的部分,以及遗留问题。 + + +小结 + +这一讲,我介绍了MECE原则,它可以帮助你系统地整理复盘资料。现在,我们回顾一下重点内容。 + + +资料准备得不好,会导致复盘会议中出现信息重复、重点环节数据缺失以及内容散乱的问题; +MECE最大的好处,是可以确保数据准备做到不重复、不遗漏,再加上逻辑树就可以很好的梳理议题的业务逻辑,让复盘参与者能够进行全局观察和理解公司业务和自己负责的模块之间的关系。 +根据MECE原则,我给出复盘资料的要素和CheckList,你也可以根据实际情况进行调整。 + + +思考题 + +这就是今天的全部内容,最后留一道思考题给你吧。 + +按照这一讲学到的知识,构建一下你最近参与的一个项目的逻辑树,并检查是否符合的MECE原则。 + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/07五步法:如何召开一次高效的复盘会议?.md b/专栏/跟着高手学复盘/07五步法:如何召开一次高效的复盘会议?.md new file mode 100644 index 0000000..e1d9bd4 --- /dev/null +++ b/专栏/跟着高手学复盘/07五步法:如何召开一次高效的复盘会议?.md @@ -0,0 +1,248 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 五步法:如何召开一次高效的复盘会议? + 你好,我是张鹏。 + +在前三讲中,我们学习了在复盘会议之前,怎么完成准备工作,包括营造环境、组建团队和准备资料。从现在开始,我们进入到复盘会议阶段。这一讲,我会教你怎么召开一次高效的复盘会议。 + + + +复盘会议的要求 + +请你回想一下,以前参加复盘会议的时候,有没有遇到这样的情况呢? + + +自说自话:每个人抱着“你好我好大家好”的态度各说各的,不关注别人说了什么,就算有提问和回答也完全按照套路走个过场,挖掘不出问题的原因,也提不出有价值的改进方案。 +节奏拖沓:一场原定2小时的复盘会议,从下午2点搞到晚上8点,原定每个人15分钟到30分钟的时间安排完全失控,那些排在后面的同学,不得不等到很晚才能排上。 + + +想要避免因为这些问题导致复盘不成功,我们首先就要明确复盘会议的要求。复盘会议从本质上说,是公司对组织成长的一种投资行为。公司的任何行为都是要讲ROI(Return on Investment)的,也就是投入产出比(或者叫投资回报率)。 + +所以,复盘会议也是有要求的: + +第一个要求是从产出(Return)的角度看,要结论有效。 + +在复盘会议中,我们虽然会对前一阶段的工作进行回顾总结,但真正目的是得到指导未来工作的关键建议。所以我们需要形成有效的交流,通过客观对比目标与结果、分析逻辑过程和修正认知,输出有效的结论指导下一阶段的工作。 + +第二个要求是从投入(Investment)的角度看,要流程高效。 + +流程是确保目标能够落地的一系列过程节点及执行方式有序组成的过程。好的流程是复盘成功的基础,它能让复盘的参与者按照约定的环节一步一步地推进,从而得出有价值的结论。 + +复盘操作明细 + +那么,复盘会议到底要怎么开呢?在第5讲中,我介绍了复盘团队的作用和各个角色的职责分工。其中,辅助者和引导者需要明确复盘的目的、准备、注意事项、会议记录的要求等等,并且形成一个完整的说明,我称之为“复盘操作明细文档”。 + +这个部分需要在复盘会议正式开始之前和所有参加会议的人同步。我在这里提供了一份针对业务战略复盘的操作明细供你参考,你也可以根据实际需求做出自己的操作明细。 + + + +五步法:复盘会议流程 + +除了操作明细之外,我还总结出了召开复盘会议的五步法,也就是把整个复盘会议流程分成五个步骤:破冰、事实比较、分析原因、洞察规律和制定计划。 + + + +接下来我以一次长达16小时(2天*8小时)的业务战略复盘会议为例,为你进行完整的说明。 + +第一步:破冰 + +首先是破冰,主要目的是活跃氛围,创造一个平等和开放的对话环境,一般在10分钟以内,具体包括: + + +开场:鼓励每位参与者与身边人拥抱或握手,感谢与身边的人一起在这个项目中奋斗。可以从“早上好(下午好/晚上好),很高兴与你一起共事和奋斗”这样的话开始。 +引导者讲解复盘会议(以下简称“会议”)的原则,比如客观、开放和中立。 +引导者说明会议的目的和意义。 +主持人告诉大家会议的日程安排。 +主持人说明引导者及参与者这两个角色和他们的主要职责。 + + +第二步:事实比较 + +破冰结束之后,进入到第二步,目标和结果的对比。这时候需要用到会议之前准备的资料、数据以及白板笔、提前画好数据分析或者写好里程碑的白板纸等工具。 + +这个部分的时间根据项目或者业务的内容设定,一般2小时左右,具体做法如下。 + +1. 回顾目标 + +引导者提问现场所有参与者: + +“我们做这个项目的初心(愿景,价值观)是什么?” + +“我们原定要达到的目标(过去一年的公司级目标)是什么?” + +引导者鼓励大家两两交流,自由发言(第一轮分2人组,尽量采用同工同组的原则)。 + +最后,引导者邀请相关负责的参与者,一般是CEO(下面直接以CEO来替代)修正和归纳大家的发言结果,并书写在白板纸上。 + + +在回溯目标时不仅有直接的成果或业绩等直观、外显的目标,也可以延伸到团队能力成长、客户满意、品牌形象提升等隐性的目标或收益。 + + +2. 总结结果 + +引导者将提前收集的结果数据书写在复盘墙上。 + +然后,引导者会向全体参与者和项目负责人确认、修正。 + +接下来,引导者会带领大家一起评估结果的达成情况,书写在复盘墙 上。用“ ↑ 、↓ 、0、+、-”分别表示“超出、低于、100%完成、新增和消失”的目标。 + +最后,引导者邀请CEO修正和归纳大家的发言结果,并书写在白板纸上。 + + +结果尽量用数值或百分比量化呈现。 + + +3. 进行对比 + +引导者将提前梳理好的战略里程碑、关键里程碑分解挂图张贴在复盘墙上。 + +接着,引导者向全体参与者和CEO再次确认、修正里程碑的分解结果。 + +然后,引导者再根据里程碑数量及现场参与人数,将现场成员分组,最好一个组负责一个里程碑,人数少里程碑多,可一组负责两个里程碑。 + +小组进行研讨: + + +各小组认领负责的里程碑,研讨做得好的部分(GOOD)和待提升的部分(NEED)。 + + + +GOOD从结果反推,NEED因为没结果,主要考虑方向是否值得做。 + + + +为保证能够充分的集思广益,小组成员采用书写式头脑风暴。 + + + +每个人先将自己的观点写下来(一个观点写一张小号彩纸),不交流。每人充分思考写完后,再在小组内头脑风暴,汇总意见,筛除重复内容。 + + + +小组将研讨的成果张贴在相应里程碑挂图对应的阶段上,GOOD写在左半部分,NEED写在右半部分。 + + +接下来,引导者组织开放空间:每个小组留一名成员在本组的里程碑前负责介绍,其他全体成员在场内自由走动,在每个里程碑前驻足,补充自己的看法和观点(用白色小号彩纸)。 + +最后,引导者请CEO对大家的研讨成果做总结和补充。 + +第三步:分析原因 + +接下来进行原因分析,详细操作步骤如下: + + +引导者邀请小组成员聚集到本小组负责的里程碑前,对GOOD和NEED进行可控性分析。 +小组成员在每条里程碑的原因前进行标记,用实心圆、半实心圆和空心圆分别代表可控、半可控和不可控。 +各小组分享可控性评估结果,其他小组帮助纠正。最后输出所有成员共识的可控性分析。 +引导者将项目各里程碑中“半可控和不可控”GOOD项汇总挑出粘贴,将所有“可控和半可控”的NEED项挑出汇总粘贴,其余的移至其他空白墙面处。 + + + +(可选)如果现场汇总后的待讨论项过多,可以让大家一起来投票,筛选出分别不超过6条的待分析的 GOOD和NEED,若过多,GOOD首选“半可控”,再选“不可控”(GOOD看客观是否有可复制成功因素),Need首选“可控”,再选“半可控”。(NEED修正主观上的明显缺失)。 + + +第四步:洞察规律 + +分析完原因,接下来就要看看哪些是成功的关键因素,哪些是失败的关键因素,换句话说,也就是和成功、失败有直接因果关系的因素,这一步就是洞察规律。 + +GOOD关键因素 + + +每组负责讨论本组里程碑的每个GOOD的成功关键因素(本质原因)。 + + + +对应的GOOD成功因素用中号彩纸书写,一个成功因素写一张,贴在对应的Good右边。- + +小组成员将讨论项后的成功根本因素对应贴在粘贴墙上。 + + + +各组代表发表研讨成果并相互设问。 + + + +每组派代表发表他们的研讨成果,其他小组倾听时,如果认为挖掘的还不够本质,可以随时发问:影响这件事的因素共有多少?哪个是最关键的?为什么一定是它?发表组其它成员根据其他组的设问情况,酌情修订研讨成果。 + + +这里需要注意的是:GOOD关键因素分析多谈客观因素和外因,因为这是可以在未来借鉴和重复利用的。 + +NEED关键原因 + + +每组负责讨论本组里程碑的每个NEED的失败关键因素(本质原因)。 + + + +对应的NEED失败因素用中号彩纸书写,一个失败因素书写一张。- + +小组成员将讨论项后的失败根本因素对应贴在粘贴墙上。 + + + +各组代表发表研讨成果并相互设问。 + + + +每组派代表发表他们的研讨成果,其他小组倾听时,如果认为挖掘的还不够本质,可以随时发问“为什么?”发表组其它成员根据其他组的设问情况,酌情修订研讨成果。 + + +这里需要注意的是:NEED关键因素分析多谈主观因素和内因,从自身找问题,主挑劣势,因为这是未来改进和提升的重点! + +第五步:制定计划 + +完成第四步之后,就要对接下来的工作做出目标和计划了,一个最简单的原则是错误的事情停下来(Stop doing),正确的事情继续做(Continue doing),开始没想到又有利于实现目标的事情开始做(Start to do)。在复盘会上,可以分为以下步骤: + + +根据GOOD/NEED挖掘出的本质因素,每组分别讨论本组里程碑中的停止、继续和新增的行动计划。 +各小组头脑风暴研讨,并书写在中号彩纸上,一个行动计划写一张,并用**“×”“√”“+”标记出停止、继续和新增**的行动计划 。 +各小组分别发表研讨结果,全体成员及项目负责人进行补充。 + + +这里需要注意的是: + + +书写行动计划尽量用3W原则,WHEN WHO WHAT。 +(可选)挑出“+”行动,识别并分级:组织级、部门级、员工级,力争放在合适的牵头部门,取得最优成果。 + + +再往后就是复盘文案的归档和下一步的计划安排。到这里,我已经把复盘会议的整个流程全部讲完了,每个部分的细节,以及可能用到的逻辑思考框架,我会在后续的课程中继续为你讲解。 + +掌握复盘思路 + +你可能会想:“我只是想对自己的工作(或一个短期项目)做个复盘啊,这种大规模的业务战略复盘思路适合我吗?” + +其实复盘的整体思路都是一致的,如果你想做中等规模的事件复盘或者小型规模的个人复盘,可以在阅读的同时思考,哪些环节是必须的,哪些环节是可以简化的。 + +你可能还会想:“每个公司情况不一样,照搬这个方法来开会,会不会水土不服啊?” + +这里我想给你分享一个小故事。1998年,《华为公司基本法》定稿之后,任正非便开始全面引进世界级管理体系,其中最重要的就是对华为内部做了流程化组织落地的管理改革。 + +任正非对系统改革提出了“先僵化,后优化,再固化”的指导方针,也就是“让员工们接受并适应了新的管理系统后,再对这套管理系统进行优化调整,然后根据自己公司的情况,做出一套量身打造的管理模式。” + +不管是五步法,还是这门课程中的其他方法,你都可以借鉴华为这种“先僵化,后优化,再固化”的态度。每种方法,我列出了大量的细节,并不是要框死你的思维,而是想通过一些实例来帮助你学习理解。 + +掌握复盘思路,因地制宜,灵活应用,你才能成为真正的复盘高手。 + +小结 + +在这一讲中,我为你分析了复盘会议的要求和高效召开复盘会议的方法。现在,我们回顾一下重点内容。 + + +复盘会议的要求主要有两点,结论有效,流程高效。 +复盘会议前,辅助者和引导者要准备好复盘操作明细文档,和所有人同步。 +想要高效地召开复盘会议,并形成有效的结论,可以采用五步法,也就是把整个复盘会议流程分成五个步骤,破冰,事实比较,分析原因,洞察规律和制定计划。 + + +思考题 + +这就是这一讲的全部内容,最后留道思考题给你:如果用五步法来做个人复盘或者针对事件/项目的复盘,跟业务战略复盘有什么样的区别呢? + +欢迎你把答案写在留言区,与我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/08事实比较:如何快速比较目标与结果?.md b/专栏/跟着高手学复盘/08事实比较:如何快速比较目标与结果?.md new file mode 100644 index 0000000..b284fb7 --- /dev/null +++ b/专栏/跟着高手学复盘/08事实比较:如何快速比较目标与结果?.md @@ -0,0 +1,227 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 事实比较:如何快速比较目标与结果? + 你好,我是张鹏。 + +上一讲我为你介绍了召开高效复盘会议的“五步法”,带你了解了复盘会议的标准流程。其实五步法如果不算第一步破冰的话,第二步到第五步刚好对应CLAP模型的四个环节。 + + + +复盘的基石部分就是对比,也就是事实比较,毕竟如果脱离事实,一切都是空谈。 + +事实比较这一步听起来好像很简单,但实际上做起来还是有一定难度的。这一讲我就会和你分享事实比较的方法。- + + +事实的组成 + +首先,我们要搞清楚事实包括什么,其实它由目标和结果两个部分组成。 + +目标可以分为两种,一种是数据型目标(Goal),比较容易用数据进行衡量,比如经营数据、运营数据、技术参数等,相对客观和简单;另一种是里程碑型目标(Objective),不太容易用数字进行衡量,一般需要用文字来进行描述,比如“新产品研发获得重大突破”等。 + +结果(Result)的情况往往会更复杂,大多数情况下最终的结果和当初设定的目标不太一样。 + +有些时候你会看到,有的结果和目标是可以进行比较的,比如超出了目标,或者没有达成目标。 + +还有些时候,因为某些原因,原来制定的目标“消失”了,或者产生的结果不在原定的目标设定范围之内。这时候,我们就要看实际的结果和项目的战略战术方向是否保持一致了。 + +目标的来源 + +关于目标,我们要思考两个关键问题: + + +目标从哪里来?或者说,目标是如何制定的? +目标制定得合理合规吗? + + +如果清楚了这两个问题的答案,你将获得鸟瞰全局的视野,无论你在职场的任何阶段,都能“做正确的事”,而不仅仅是“正确地做事”。这样你就能够事半功倍,更容易获得晋升的机会。 + +公司的本质是一个商业组织,通过创造价值和价值变现来实现生存发展。一个公司如果想要有长久的生命力,就一定要有使命、愿景、价值观、战略、目标、结果和行动计划这样的东西,如下图所示: + + + + +使命是我们(公司)存在的意义,即我们解决了什么社会性、市场性的问题。- + +愿景是我们的未来是什么样子?比如:成为行业领导者,最佳产品提供商、受人尊敬的雇主等等。- + +价值观是保证我们完成使命愿景所提倡和反对的底线原则。- + +战略是实现愿景的方向、中长期规划、策略等。- + +目标是实现战略近期需要聚焦达成的具体事项。- + +关键结果是如何知道我们在向目标推进。- + +行动计划是实现关键结果的行动序列。 + + +从图中可以看到,目标是上承战略,下接行动的。目标与战略的匹配度,基本上可以反映出这个目标好还是不好。它既是目标制定的标准,也是目标的审视标准。 + +了解这一点,你就不再是只能被动接受目标的小白了,而是已经有足够的能力去区分目标的好坏优劣,去“做正确的事”。 + +如果目标和战略的匹配度可以定性目标的好与坏,那目标的制定是否符合SMART原则则是定量了。 + +SMART原则 + +如果公司很重视客户服务,那么客服部门的目标如果定成“增强客户服务意识”就很不明确,而定成“一个季度内,把客户满意度提升至95%”或者“1个季度内,在满意度不变的情况下,把客户响应时间缩短到2小时内”就更为精准和易于理解。 + +SMART是由5个英文单词的首字母构成,它们分别是: + + +Specific:目标必须是具体的。 +Measurable:目标是可以被衡量的。 +Attainable/Aspirational:目标是可达成的/目标是有野心的。 +Relevant:和其他目标的相关性。 +Time-bound:目标是要有时间截止点的。 + + +其中A有两种,这里补充说明一下:如果是确定性的目标,用KPI考核的,那么A就是Attainable,强调可达成;如果是不确定的目标,用OKR管理的,比如创新项目,那么A就是Aspirational(有野心的),强调高挑战。 + +用一句话来说,SMART原则可以这么理解:要干什么?结果是什么?条件是什么?什么时间完成? + +填写结果 + +看完目标,我们这里再看看结果。 + +对于数据型目标的结果,我总结了一个快速对比表格工具。 + + + +比如,某公司开发了三种产品A\B\C,期待的销售额分别都是100万,针对的是不同的客群比如:品牌企业客户、非品牌企业客户、个人客户。而最后的结果是:A产品销售了200万,B产品销售额是50万,C产品销售额是5万,填入上述表格之后,就是: + + + +这个数据highlight出来之后,就会在下一步的逻辑分析中,进一步分析ABC卖得好还是不好的原因到底是什么。 + +对于里程碑型目标的结果,我们可以用第6讲中介绍的逻辑树进行整理,也就是基于MECE原则逐层分解。 + +比如分析成本升高的问题,就可以把成本按照“固定成本/可变成本”的逻辑树先列出来,进一步分析到底是哪些项目上升导致的问题。 + + + +对齐 + +对于自己团队内的目标和结果,我们可以独立地完成对比工作;但如果涉及跨部门的项目或者更大范围内的业务,我们可能无法把自己团队的结果与更高层次的目标直接进行对比,这时候就可以用另一个办法,对齐(Align)。 + +对齐是指,让里程碑型目标的结果要和公司战略目标的方向保持一致,或者说有直接的因果关系。无论是在一开始设定目标的时候,还是在复盘做事实比较的时候,对齐都特别的重要。 + +你还记得我在开篇词中讲的那个案例吗?我的一位朋友从用户运营做到了助理副总裁,他在公司做业务复盘的时候,提出把C2C模型改为B2C模型,然后自己内部创业去做MVP(Minimum Viable Product,最小化可用原型),并且采用OKR的方式来进行管理和沟通。 + +下面这张图展示了他的OKR规划: + + + +内部创业的第一年,他并没有达成经营目标约定的数字(KR3)。但是在年底复盘的时候,他还是做出了一个结果——成功打造了B2C的运营团队,这个很关键。 + +选品和客群有关(KR2),虽然客群选择出了偏差,但是他成功打造了B2C的运营团队,相当于解决了OKR中的两个关键结果(KR1&KR4),流量转化率和交付满意度。他对齐了“打造B2C直卖业务MVP”这个目标,依然算是取得了成功。 + +如果复盘的时候,他只是泛泛地说“锻炼了队伍”“开拓新业务”“打硬仗”之类的话,领导估计就不会买单给支持了。 + +所以事实比较的时候,需要找到结果与战略目标之间的关系: + +如果属于增强循环的因果关系,即使没有达成预定目标,依然不失为好结果。反之,如果结果和战略目标之间并没有这种关系(包括结果与战略目标是反向关系或无关),就属于不好的结果。 + +SCQA架构 + +即使是好的结果,在复盘会议上把来龙去脉讲清楚,也很关键。这里,我介绍一个把事实讲清楚的方法,SCQA架构。 + +SCQA架构是结构化表达的一种语言范式,四个字母分别代笔Situation(情境)、Complication(冲突)、Question(问题)和Answer(答案)。学会SCQA框架,会提升你表达的结构性,突出重点。 + +根据不同的目的或者表达重点,SCQA框架可以分为以下4种形式:标准式、开门见山式、突出忧虑式和突出信心式。你想强调什么,就先说什么。 + +1. 标准式 + +第1种是标准式,SCA,也就是情境-冲突-答案。这种形式适合一般的事实描述。 + +“你有没有遇到过这样的客户?她每天带孩子来体验你家的产品,孩子喜欢得不肯走,似乎觉得什么都好。但是最后要成交的时候,她又觉得价格贵了。” + +这是在讲述“价格认知”这个概念的背景,也就是S情境。 + +“真的是因为她没钱吗?不一定,你可能会发现,她住的小区和开的车都很高档,因为没钱和不舍得花钱是两回事儿。那么有没有什么办法,可以让这些有钱却不舍得花钱的客户变得肯花钱呢?” + +这是指出了一个和常识的不一样的地方,也就是C冲突。 + +“今天我来跟大家讲一下我遇到这类客户是怎么做转化的,看看对大家是否有帮助……” + +这是给出了A答案。 + +2. 开门见山式 + +第2种是开门见山式,ASC,也就是答案-情境-冲突。这种形式适合抛出自己的主张,得到大家的重视。 + +“我今天要向大家汇报一下我们的工作结果,关于把公司的C2C匹配模型改为B2C精品直卖模型的MVP构想。” + +这是开门见山地直接给出自己的主张“改变平台模型”,也就是A答案。 + +“公司从创始以来一直使用C2C平台匹配模型,一端是海量的产品,另一端是无限宽广的人群,平台靠收交易佣金做价值变现。” + +这是描述之前的C2C匹配情况和商业模式,也就是S情景。 + +“但是用户在我们的平台上并不清楚如何选择适合他们需求的产品,包括产品的品质如何,价位是否虚高,好评是不是由水军刷出来的。现实的结果是,虽然我们在流量端做了重金投入,但是只有极少数商家获得了高额营收,而绝大多数商家并没有太多成交量。这样下去,靠收交易佣金的平台模式很难取得成功,这两年的经营数据也证实了这一点。” + +这是指出“实践证明C2C并没有解决客户问题”,也就是C冲突。 + +用“背景-冲突-答案”的开门见山式和团队沟通,你的第一句话,就是重点。 + +3. 突出忧虑式 + +第3种是突出忧虑式,CSA,也就是冲突-情境-答案。这种形式适合highlight问题,引发关注。 + +“哎呀,返券优惠再这么搞下去,公司就没有利润了,年底大家的奖金就泡汤了。” + +听到这句话的时候,估计大家心里都要咯噔一下,这是C冲突。 + +“还好,我们项目组做过一个新模式的数据分析以及小规模测试,如果按照我们的策略在全公司范围内推广,这个季度可以在不影响营收的前提下,降低折扣点。” + +听到这句话,一颗悬到嗓子眼的心总算是放下来了,这就是S情境。 + +“目前还缺一个资深的运营经理,如果能来一个这样的人,我们有把握把返利降低10个点,这些都是利润。” + +这是A答案,要一个资深运营经理的head count,事情就搞定了。 + +突出忧虑式,关键在于强调冲突,引导听众的忧虑,从而激发对背景的关注,和对答案的兴趣。 + +4. 突出信心式 + +第4种是突出信心式,QSCA,也就是问题-情境-冲突-答案。这种形式适合有创新想法向老板(广义的老板包括投资人、媒体等)申请政策和资源。 + +“今天全人类面临的最大威胁是什么?” + +这是引发大家(投资人)对未来的思考,也就是Q问题。 + +“在过去的几十年里,科技高速发展,人类拥有的先进武器可以摧毁地球几十次。” + +这是现实情况,也就是S情景。 + +“但是我们拥有了摧毁地球的能力,却没有逃离地球的方法。” + +这是现存的矛盾,也就是C冲突。 + +“所以,我们今天面临的最大威胁,是没有移民外星球的科技,我们公司将致力于私人航天技术,在可预见的未来,实现火星移民计划。” + +这是A答案。 + +上面这段话就是埃隆·马斯克说过的,火星移民计划用这种表达框架来介绍,是不是突然觉得靠谱多了?试想一下,如果你用这样的框架在复盘的时候申请一个内部创业项目,是不是更有机会? + +小结 + +在这一讲,我为你介绍了在事实比较这一步快速比较目标与结果的方法。现在,我们回顾一下重点内容。 + + +事实包括两个部分,目标和结果。目标又可以分为数据型目标和里程碑型目标。 +一个目标设定得好不好,主要看目标定性时是否与公司的战略对齐,定量时是否遵循SMART原则。 +数据型目标的结果,可以用我总结的快速对比表格来对比;对于里程碑型目标的结果,可以遵循SCQA框架进行逻辑组织和表达,这样可以极大地提升复盘会议的参与度。 + + +思考题 + +这就是这一讲的全部内容了,最后留一道思考题给你。如果让你对现在或曾经负责的某个项目进行事实对比(里程碑型目标及其结果),你会采用SCQA框架中的哪种形式(SCQ/ASC/CSA/QSCA)来描述呢?请你试着描述一下,并说明选择这种形式的原因。 + +欢迎你把答案写在留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/09分析原因:梳理逻辑时怎样找到切入点?.md b/专栏/跟着高手学复盘/09分析原因:梳理逻辑时怎样找到切入点?.md new file mode 100644 index 0000000..902fcc2 --- /dev/null +++ b/专栏/跟着高手学复盘/09分析原因:梳理逻辑时怎样找到切入点?.md @@ -0,0 +1,218 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 分析原因:梳理逻辑时怎样找到切入点? + 你好,我是张鹏。 + +上一讲,我为你介绍了快速比较结果和目标的方法。完成事实比较之后,我们就来到了分析原因这一步,它正好对应CLAP模型的逻辑环节。 + + + +分析原因的能力有多重要呢?我给你讲个故事吧。 + +M公司曾经是我的客户,一开始做了一个很normal的互联网教育平台(高仿国内某知名教育平台Y),但是在融资的时候,除了一些几乎不懂互联网又不想错过风口的纯粹财务投资人有兴趣之外,几乎没有真正的战略投资人愿意参与进来。 + +于是,M公司的CEO请我来帮他做复盘。 + +我问CEO:“你们到底希望什么样的投资人进来?” + +他说:“当然最好是像BAT这样的战略投资人进来呀。” + +我问:“为什么?” + +他说:“现在流量越来越贵,融再多的钱,最后还是给这些流量入口了,还不如让他们成为战略合作伙伴,钱多钱少都不是问题。” + +我接着问:“怎么让BAT进来呢?” + +CEO回答:“要有好的经营数据啊。” + +我问:“哪些经营数据是最重要的呢?” + +CEO答道:“每个部分都很重要啊。” + +这是一个典型的深挖式教练提问过程,一层层递进,直到找出答案或者对方无法回答为止。交流到这里,我基本可以判断,CEO还没有找到解决问题的关键点。 + +很多人都和这位CEO有同样的感觉:业务分析模型多少也知道一点,比如增长黑客的AARRR漏斗模型,但是用起来不太称手,分析问题的时候总觉得找不到切入点(或者说关键点)。 + +如果不会分析,事实了解得再清楚也没有用。这一讲,我就会教你怎么提升复盘时分析问题原因的能力。 + + + +逻辑推理:演绎与归纳 + +分析原因需要先梳理逻辑。逻辑是什么?简单来说,就是事物之间的因果关系。 + +怎么从事实(也就是目标和结果的比较)中发现业务的因果关系呢?基本的逻辑推理方法有两种,演绎法(Deductive Reasoning)和归纳法(Inductive Reasoning)。 + +演绎法就是经典的三段论,比如: + +“所有的人都一定会死”,这是大前提。 + +“苏格拉底是人”,这是小前提。 + +“所以苏格拉底一定会死”,这是结论。 + +大前提成立,小前提正确,自然能够得出结论。 + +而归纳法是这样的: + +现象:A死了,B死了,C死了,D死了……我们观察到的所有人,最后都会走向死亡。 + +结论:所有的人都一定会死。 + +很明显,演绎法是从一般到个别,而归纳法是从个别到一般。演绎论证得到的是必然的结论,而归纳论证只能得出可能的结论。最典型的就是“黑天鹅”理论,即使你见过100万只白天鹅,也不能保证你见的下一只还是白天鹅,这就是归纳法的局限。 + +刚才提到的M公司的CEO朋友,他一开始的思路差不多也是归纳法: + +现象:平台Y成功融资了,其他几个类似的平台也成功融资了。 + +结论:这一类平台都可以成功融资。 + +“你这样做能挣钱,那么我这样做也能挣钱”,这种思路几乎也是国内2VC式创业的通病,所以这类公司生命周期短,也就不奇怪了。 + +这也是现在越来越多的人重视复盘的原因。总结只是对过去的归纳,光靠总结很可能分析不出问题的关键。 + +现在我们回到M公司的故事。先不谈战略规划、核心团队以及经营数字等问题,我们按照融资的逻辑,粗略地梳理一下业务的基本面: + + +首先评估业务所在的市场有多大。 +然后看优劣势分析。 +再看价值创造链是怎么做的。 +最后看竞争对手之间的关系。 + + +接下来,在说清楚这几点的同时,我也会为你分享4种常用的分析框架。- + + +分析框架 + +逻辑树 + +第1种分析框架是逻辑树,基于MECE原则把主题逐层分解(第6讲详细介绍过)。 + +M公司是做K12教育的,说穿了就是做提分的。我们想看看市场上还有哪些大的“价值洼地”存在,于是一起画了这样一张图: + + + +在中学部分,有学而思等巨头,在产品和模式没有出现颠覆式创新的情况下,M公司几乎没有任何胜算,所以中学部分可以去掉。 + +在小学部分,体音美一类不属于K12用户愿意付费的刚需,继续去掉,还剩下语数英。 + +相对英语和数学,语文提分并不容易,去掉。 + +小学数学并不难,不需要课外辅导,去掉。 + +最后,就只剩下了小学英语。 + +矩阵图 + +第2种分析框架是矩阵图。其实我们常用的SWOT、波士顿矩阵和安索夫矩阵等都是矩阵图, 矩阵图法就是从多维问题的事件中,找出成对的因素,排列成矩阵图,然后根据矩阵图来分析问题,确定关键点。 + +M公司确定了小学英语这个方向,但是英语也分听、说、读、写,M公司在这几个方面并没有特别的产品或者服务。 + +公司的主营业务往往都是人与事的结合,于是,我们并没有从产品本身着手分析,而是从M公司创始团队本身的基因切入(这一点特别重要,分析的时候完全可以从不同的角度进行切入)。 + +在沟通过程中,我发现M公司的创始人团队有一个共同点——他们都是超级游戏迷。于是我们探讨了一下,小学阶段的教育最重要的是什么? + +分数?不是,分数只是呈现学习效果的形式。 + +知识?也不太是,小学的知识密度不高,难度也不大。 + +学习习惯?有点接近了,也不全是。 + +…… + +最后,当我们把答案聚焦到“培养孩子对学习的兴趣”的时候,几乎所有人都举手同意了。 + +好,我们就从这一块着手。我抛出了一个简化版的“心流”的矩阵图(坐标图): + + + +从矩阵图中,我们可以看出: + +团队要想办法,让孩子的学习处在从由低到高的挑战难度排列的过程中,用类似游戏的方式来提升能力。不要重复低挑战难度的考题、训练记忆,也不一定要完全对照教材的章节顺序来安排内容。 + +做完这一步,产品内容就基本上有了头绪。 + +流程图 + +第3种分析框架是流程图,它可以帮助我们在做价值创造的时候找到着力点和优势所在。 + +M公司选择了用类似游戏的方式来帮助孩子提升,那么怎样才能把游戏“好玩上瘾”的体验嵌入到学习过程中去呢? + +因为团队都是骨灰级游戏玩家,我们在复盘中仔细研究了游戏的机制,发现了一些共同点: + + +快速上手:再复杂的游戏,一开始为了吸引玩家,都会提供一个非常友好的入口,让几乎零基础的人也能够快速上手。 +即时反馈:玩家在游戏中做出的任何一项操作,都会立刻产生视觉化、听觉化、数据化成绩效果。 +通关:所有的游戏会为玩家提供一个阶段性小关卡,总能让玩家找到一个很快就要完成的小目标。在完成这个小目标之后,就会又激活一个,并且越来越难,无限不循环。 +PK:让玩家在对决中找到输赢带来的快感,赢的会上瘾,输的也不会甘心。 +成就:让玩家有一种自我能力的确认,积分和青铜、白银、黄金、钻石等级标识显示也和别人不一样,是一种身份的象征。让玩家有一种自己独一无二的荣耀感。 +炫耀:取得成就之后,一定要有可分享的渠道,尤其是在熟悉的玩家之间进行炫耀。 + + +…… + +根据游戏的特点,我们将所有的试题按照挑战难度重新编排,并按照游戏重新设计流程,我们看一下这个产品体验流程的前后对比: + + + +可以看出,在流程中,课程的“章节”变成了游戏的“关卡”,原有的内容只有试题,而在后面的设计中: + + +内容包括试题、勋章和分享海报。 +每一关的难度在逐步递增。 +海报的分享很容易拉进新的玩家(使用APP的同学)。 +如果其他玩家不服气,可以选择“PK模式”。 + + +…… + +当M公司改进了APP,把学习变成了游戏之后,甚至发生了这样的故事:某个孩子每天在APP上玩刷题,半个月从学渣变学霸的,不但全程都不需要父母管,而且最后还把全年级的同学都拉了进来。 + +关系图 + +第4种框架是关系图,主要用于分析业务或者项目所在的环境。业内竞争很容易被看见,而更大范围的外部因素,有时候是业务成长或者萎缩的趋势性因素。 + +经过一段时间的运营,M公司发现实际效果比预期的还要好:因为游戏好玩有趣,用户的在线时长增加了;而且由于勋章的炫耀和拉同学PK的设计,用户通过自发传播引流实现了快速增长,跟直接投广告相比效率更高、成本更低。 + +到了这个阶段,我们就要看一下M公司在整个生态环境中的发展态势,这时候可以用到关系图,比如经典的波特五力模型: + + + + +客户角度:无论是花时间的孩子,还是花钱的家长,接触M公司的产品之后都很喜欢,觉得超出期待,并没有特别关心价格。 +教育供应商角度(主要是版权方和老师):由于重组试题安排,对传统版权供应商依赖度不大,而“去名师化”的设计让降低了未来的成本,又打开了产品设计的空间。 +替代者和新进入者角度:BAT等大厂有机会进入这个“游戏化教学云服务”的领域吗?可能性不大,因为这些做好教育平台需要太多的专业经验了,只要价格合适,大厂更愿意投资或者收购,而不是直接竞争。 +业内竞争角度:业内多数巨头还在采用买流量、推名师的模式在玩,品牌一旦形成,如果要掉头,相当于把原来花重金打造的用户心智全盘否定,跟进的可能性比较小。 + + +这样看来,M公司占据了总成本领先和定位游戏化教育训练心智这两个领先竞争战略高地,基本上锁死了竞争对手的战略空间,而且在相当长的一段时间内,这个业务还有巨大的成长空间。 + +后来M公司找到了百度的投资部,百度看完M公司的运营数据,只用了一天就决定投了。 + +现在我们再来看AARRR模型。M公司已经解决了5个环节中的4个环节,都非常有亮点和特色,百度正好是流量巨大,寻找变现渠道,天作之合! + + + +小结 + +这一讲,我通过M公司的产品定位和业务转型的案例,讲解了在分析原因时常用的2种逻辑推理方法和4种逻辑分析框架。现在,我们回顾一下重点内容。 + + +想要发现业务的因果关系,可以通过演绎法和归纳法。演绎论证得到的是必然的结论,而归纳论证只能得出可能的结论,存在一定局限性。 +常用的逻辑分析框架包括逻辑树、矩阵图、流程图和关系图。 +逻辑树可以帮助我们应用MECE原则细分复盘主题的每个分支;矩阵图可以聚焦某个业务板块,找出需要聚焦的重点;流程图可以帮助我们在做价值创造的时候找到切入点和自身的优势所在;关系图可以让我们在更大的生态下分析出竞争策略。 + + +思考题 + +这就是今天的全部内容,最后留一道思考题给你吧。你使用过这4种分析框架吗,有什么问题或者心得呢? + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/10洞察规律:怎样更新认知才能找准发力点?.md b/专栏/跟着高手学复盘/10洞察规律:怎样更新认知才能找准发力点?.md new file mode 100644 index 0000000..0078fe2 --- /dev/null +++ b/专栏/跟着高手学复盘/10洞察规律:怎样更新认知才能找准发力点?.md @@ -0,0 +1,170 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 洞察规律:怎样更新认知才能找准发力点? + 你好,我是张鹏。 + +上一讲,我为你介绍了梳理逻辑时寻找切入点的方法。找到切入点,分析出问题原因之后,我们就到了洞察规律这一步,它正好对应CLAP模型的认知环节。 + + + +所谓洞察规律,就是系统地思考整个业务的运转,找准下一步计划的发力点。洞察规律的能力有多重要呢?我带你回顾一下Uber的故事吧。 + +2014年,Uber进入中国之后,迅速在上海、杭州、深圳、广州、成都、武汉、重庆、北京和天津等一二线核心城市开展业务。 + +一开始,Uber的获客模式是人肉两头拉。“人肉”是指让用户自发通过口碑传播,“两头拉”是指一边拉开车的司机,另一边拉坐车的乘客。这种模式的好处是获客非常精准,适合早期破局。 + +到了2015年,“滴滴”“快的”等中国本土企业也逐渐崛起,在巨大的竞争压力下,Uber对市场扩张速度的要求更高了。接下来的半年中,他们准备在华东、华南、西南和华北四大区域拓展自己的业务。 + +但是Uber内部有个不成文的规定——不批准任何市场费用,这也意味着不能以广告等粗暴的模式来获客,所以Uber的城市合伙人只能从运营手段本身想办法。 + +而当时他们面临的现实问题在于,以前的人肉两头拉模式虽然精准,但速度不够快,到了快速扩张期增长乏力,很难支撑业务的升级。 + +与此同时,“出行场景单一”“无法满足更多出行需求”“价格没有优势”“等车时间长”等一系列问题也都冒了出来,而且这些问题之间还会相互影响。 + +这种局面给当时的Uber管理层提出了一个挑战:到底哪个问题才是重中之重,怎么才能找准下一阶段的业务发力点呢? + + + +因果循环图 + +为了解决战略重心问题,Uber华东区域的管理层使用了一个工具——因果循环图。 + + + +因果循环图(CLD,Casual Loop Diagram)出自1990年出版的管理学经典著作《第五项修炼》,它是这本书的作者、学习型组织理论的鼻祖彼得·圣吉(Peter M. Senge)提出的用于系统思考的工具。 + +那么,因果循环图要怎么用呢?我们继续看Uber华东区域业务的例子。 + +1. 确定节点 + +用因果循环图的第一步是确定节点,也就是标记业务的关键节点。 + +Uber华东区域一开始的业务破局还是做得不错的,得到了车主和乘客的认可,但是要在宣传资源受限的条件下拓展业务,管理层还需要深挖能够撬动整个市场运转的关键节点。 + +首先,他们想到的是满足更多的出行需求,于是他们标记了一个关键节点,更多需求。 + +接着,为了满足更多的出行需求,从供给端看,核心是更多区域覆盖,从数量和密度两个维度进行拆解,可以细分为更多司机和更少司机闲时,于是他们又标记了两个关键节点。 + +从消费端看,核心是乘客能更快能坐上车和车费更便宜,分别代表时间和价格两个维度,于是他们又标记了两个关键节点,更快接客和更低价格。 + +最后,整个逻辑树用MECE原则检查一下,确保没有问题。 + + + +到这里,关键节点就基本上找齐了。 + +2. 确定关系 + +第二步是确定关系,也就是分析连接关键节点的因果关系。 + +关键节点之间的关系用带箭头的曲线来表示,箭头的方向代表因果,类型有三种,分别是: + + +增强,用“+”表示。 +减弱,用“-”表示。 +延迟,用“=”表示。 + + +这三种类型很好理解: + +踩油门和速度之间就是增强关系,油门踩得越大,速度越快。 + +刹车和速度是减弱关系,刹车踩得越狠,速度越慢。 + +延迟在生活中更为常见,比如你在浴室拧开热水开关,需要过一段时间水温才会升高。 + +确定因果关系,重点是搞清楚A节点与B节点之间的关系是增强还是减弱。对于Uber来说: + + +更多需求会让更多司机来到Uber平台,所以它们的关系是更多需求增强更多司机(+)。 +更多司机会让Uber覆盖的区域更广,所以它们的关系是更多司机增强更多区域覆盖(+)。 +更多区域覆盖会让更多乘客也注册和使用Uber打车,并且进一步促进更少司机闲时和更快接客(+)。 +更少司机闲时让客单价有进一步降低的空间,所以会导致更低价格(+)。 +当更低价格和更快接客这两个乘客都最在意的痛点得到满足,会进一步促成更多需求(+)。 + + +到这里,单个节点之间的关系就分析清楚了,Uber华东区域管理层也就可以完成Uber整个业务的拼图了。 + + + +3. 确定根因 + +第三步是找出根因,就是判断推动运转最底层的根因。 + +因果循环图非常清晰地展示了单个节点之间的直接因果关系。但是底层的根因要怎么看呢。毕竟各个节点之间是互为因果的。 + +我跟你分享一个小窍门: + + +连接线入口多出口少的,一般是“果”,比如更多需求。 +连接线入口少出口多的,一般是“因”。 +去掉之后会导致整个增强循环断裂的(“没它不行”),一般是“根因”。 + + +从图中可以看出,更多司机和更多区域覆盖这两个节点是满足根因条件的。而更少司机闲时以及更低价格即使单独去掉,虽然会影响效率,但整个循环依然可以转动,因此属于重要因素,但并不属于根因。 + +我们可以从业务逻辑的角度验证一下,更多需求确实是Uber追求的终极结果,而更多司机和更多区域覆盖如果拿掉,就会导致供给端司机少,从而导致消费端注册和使用少,最终导致Uber的业务无法升级。 + +最终,Uber确认了以更多司机和更多区域覆盖作为提升整体业务的目标,并设定OKR来进行执行,在华东市场取得重大突破。这部分内容我会在下一讲详细介绍。 + +现在,因果循环图的使用就讲完了,我用一个流程图来总结一下: + + + +为了方便你理解,我再为你讲解另一个案例,你可以像阅读推理小说一样,看看能否在我揭示谜底之前想出答案。 + +案例补充:Youtube + +2013年,YouTube已经成为全球最大的视频平台之一。但是它的创新步伐已经放缓,一旦刹车,重新加速并不容易。 + +当时YouTube的CEO是Susan Wojcicki,她意识到YouTube再这样下去,会沦落为一家很普通的公司,于是决定组织高层做战略复盘。她提出了一个问题:“如果重新让YouTube走上‘10倍速’的指数型增长之路,我们到底应该怎么做?” + +Youtube当时面临的考验和Uber很像,都是要在一大堆重要的事情中找到重中之重。对于YouTube来说,流量、点击率、观看率、广告、视频内容、日活……到底哪一个才是应该首先考虑的呢? + +当时视频网站普遍看重点击率和日活数据,YouTube内部就有一个工程师正在开发引导观众选择“观看下一个”的推荐系统。显然,这能够对点击率的提高产生巨大的影响。 + +但是需要思考的是,点击率的提高是YouTube真正想要的吗?或者换句话说,业界都认为提高点击率就能提升业务,这个“常识”就一定是对的吗? + +YouTube在这次战略复盘中发现,很多因素是相互影响的。它最重要的收入来自广告,但是让用户过多地点击广告,会影响用户的体验,而体验不好就可能造成用户流失,从而降低平台对广告商的吸引力。 + +当用户花更多宝贵的时间来观看YouTube上的视频时,这些视频必须要给他们提供乐趣,这样才能形成良性循环: + +更满意的观看体验带来更长的观看时间,更长的观看时间会吸引更多的广告商,从而激励更多的内容创作者进行创作,于是又会吸引更多的观众。 + + + +根据“确定根因”的原则,“没它不行”的节点就是根因。 + +YouTube最终洞察到:真正的收益来源不是观看率或点击率,而是观看时长。虽然短期内,用户时长的增加甚至会降低用户对广告的点击率,但从长远来看,显然会让YouTube走上增强循环。 + +Susan和她的团队制定了一个BHAG(Big, Hairy, Audacious Goals,宏伟、艰难和大胆的目标):“日均用户观看时长达到10亿小时”。在之后的4年时间里,他们通过各种努力,最终实现了这个BHAG。 + + + +小结 + +这一讲,我用Uber的案例为你讲解了使用因果循环图的技巧,并且通过Youtube的案例来帮助你加强理解。现在,我们来回顾一下重点内容: + + +因果循环图中的节点,就是真实业务中的关键节点,你可以理解成是关键子业务,用逻辑树进行划分,用MECE原则进行验证。 +关键节点之间的关系用带箭头的曲线来表示,箭头的方向代表因果,类型有三种,分别是增强、减弱和延迟。 +如果因果循环图中,某个关键节点去掉之后会导致整个增强循环断裂,那么它很可能就是根因。 + + +思考题 + +这就是今天的全部内容,最后留两道思考题给你吧。 + + +请你回忆一下学习Youtube案例的过程,你是什么时候找出“根因”的呢?为什么是那个时候? +请尝试画一下你手头项目的因果循环图,看看有什么发现或者问题。 + + +欢迎把你的答案写在留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/11OKR:怎样根据复盘结论制定计划?.md b/专栏/跟着高手学复盘/11OKR:怎样根据复盘结论制定计划?.md new file mode 100644 index 0000000..0447a0f --- /dev/null +++ b/专栏/跟着高手学复盘/11OKR:怎样根据复盘结论制定计划?.md @@ -0,0 +1,169 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 OKR:怎样根据复盘结论制定计划? + 你好,我是张鹏。 + +上一讲,我为你介绍了洞察规律的方法。洞察到规律,找准发力点,我们就形成了初步的复盘结论,接下来就到了复盘五步法的最后一步,制定计划,它正好对应CLAP模型的规划环节。 + + + +在上一讲提到的Uber复盘案例中,华东区域管理层通过因果循环图得出了这样的结论:为了满足更多需求,最关键的是更多司机和更多区域覆盖。 + +如果你是区域总监,你会怎么制定下一步的具体目标和计划呢? + + +更多司机到底要招募多少司机?司机来了之后不干活怎么办? +更多区域覆盖到底要覆盖哪些区域?是华东所有城市一起提升覆盖率,还是优先覆盖重点城市?不同的城市有没有不同的策略? +还有没有其他没想到的目标,或者说隐含的目标? + + +这些问题都是你要考虑的。毕竟光有一个粗略的方向是远远不够的,想要得出能够落地的目标和计划,你还需要更系统地思考,更细致地规划,这时候就要用到OKR了。- + + +OKR + +OKR的全称是Objectives & Key Results,意思是目标和关键结果。作为应对VUCA时代不确定环境的目标管理工具,OKR近几年在中国的创业圈大火。 + +OKR最早是由Intel的前CEO安迪·格鲁夫提出的,并且在Intel取得了非常好的效果,这让当时还是Intel实习生的约翰·道尔非常着迷。 + +等到了1999年,约翰·道尔已经成为风投公司凯鹏华盈(KPMG)的董事会主席。在投资谷歌的时候,面对谷歌的两位25岁左右、没有什么企业管理经验的创始人拉里·佩奇和谢尔盖·布林,约翰·道尔把OKR引入到了谷歌,谷歌业务后来也呈现出指数级增长,让OKR的效果再次得到验证。 + +再后来,包括FaceBook、Twitter和YouTube在内的多个硅谷创业公司都陆续引入OKR作为管理工具。约翰·道尔还出了一本书_Measure What Matters_(《这就是OKR》),这本书是介绍OKR的开山之作,也是我个人认为迄今为止关于OKR写得最好的一本书。 + + + +我在决胜网担任HRVP的时候,有一个投资机构叫线性资本,其中的一个创始合伙人叫王淮,他是FaceBook的第一个华人研发经理,写过一本畅销书,叫《打造FaceBook》。 + +2015年,他来我司做高管访谈的时候告诉我:“你们公司的管理很适合用OKR,建议你去了解一下。”于是,我开启了对OKR的探索和实践之路。 + +当时国内关于OKR的书还非常少,于是我从与FaceBook和Google的员工访谈开始,详细地了解OKR的运作机制和文化背景,并且在工作中实践和总结。 + +后来,我在咨询顾问工作中,也帮助了很多创业企业甚至是成熟企业引进OKR,满足它们对高速增长和转型的需求。 + +在我看来,OKR的底层逻辑非常简单,只需要回答两个问题: + +O:我要去哪儿? + +KRs:我怎么知道我正在去那儿的路上? + +很多公司最早使用OKR的部门或者岗位都和研发有关,因为传统的KPI很难量化研发人员的工作。作为程序员,你能说解决一个Bug的价值就一定比解决100个Bug的价值低吗?或者说作为诗人,写了两万首诗的人(比如乾隆皇帝)就一定比只有一首诗流传下来的人(比如《春江花月夜》的作者张若虚)文学成就更高吗? + +但是如果你以为KPI无法考核的工作,只要全都甩给OKR就行了,那就太天真了。因为KPI和OKR都有各自的优势,有些问题其实KPI是可以解决的(甚至用KPI解决更合适),只是你不知道如何设计而已。 + +关于KPI和OKR的选择策略,我画了一张矩阵图来说明: + + + + +存量市场的确定性业务,适合用KPI考核。 +不确定性的业务,适合用OKR管理。 +确定性业务要做出超级增量的,适合KPI和OKR一起使用。 + + +另外,业务可能在不确定性和确定性之间转化,也可能在存量市场和增量市场之间转化。当环境发生变化时,原来的方式可能不再适合,所以KPI和OKR最好也能及时地动态转化。 + +总之,复盘是一个认知提升的过程,每一次提升都会带来巨大的改变。当你想要探索未知、期待做出增量的时候,OKR就会是你最好的选择。 + +OKR设定完成之后,需要做具体的执行计划,也就是AP(Action Plan),意思是行动计划。没有具体行动计划的目标和结果也是没有价值的,只有全部连通起来才有意义。 + +怎样使用OKR? + +那么,怎么使用OKR呢?在用OKR制定目标和计划的时候,你会遇到三层挑战。 + +第一层挑战是,从O到KR。 + +O需要是鼓舞人心的,定性而非定量的(更关注目标的意义),并且降低与其他的O的耦合度,从而让团队可控,有时间限制的,符合SMART原则的(第8讲介绍过,KPI中的A是Attainable可达成的,而OKR中的A是Aspirational有野心的)。 + +KR需要是有挑战的,定量的,具体的,双向认同的。 + +O要能被KR支撑,它们之间要符合因果关系而非分解关系,KR之间要符合MECE原则。 + +举例来说,如果你在营销部门,你的OKR中的O是总销售额100万,那么KR绝对不能是把这100万分给A员工50万,B员工30万,C员工20万,因为这样他们只是知道一个数字,仍然不知道应该怎么做。 + +你可以应用这个公式: + +销售额 = 流量 × 转化率 × 客单价 + +设定KR的思路是: + + +流量应该做到多少? +转化率达到多少? +平均客单价应该设定为多少? + + +第二层挑战是,不同部门之间的OKR对齐。 + +很多项目涉及部门之间的协同,比如研发与市场、生产与销售等。所以,部门之间需要充分对齐,并且建立协议机制,这样才能形成“1+1>2”的效果,否则很容易在回顾目标的时候扯皮,复盘的时候甩锅和背锅。 + +终极挑战是,找到正确的O。 + +“做正确的事”远比“正确地做事”要难得多,这是我做顾问的过程中体会最深的一点。上一讲介绍的因果循环图这个工具可以帮助我们尽量考虑周全,但最终能否找到正确的O,还是取决于你对业务、市场以及其他环境因素的理解。 + +现在我们来看一下Uber华东区域的管理层制定的OKR: + + + +O1是“招募更多司机”,它拆解出来的KR1是“所有地区的司机基数提升20%”,关注的是司机数量;KR2是“所有活跃地区的司机平均工作时长提高到90小时/周”,这说明光有司机数量还不够,还需要司机工作时间长,才能满足更多乘客乘车的需求。 + +O2是“提高更多覆盖率”,它拆解出来的KR1是“上海的覆盖率提升到100%”,这是华东最重要的城市优先级最高的表达,是重点城市深度服务的要求;KR2是“所有活跃城市的覆盖率提高到75%”,这是广度的要求;KR3“通勤高峰期,覆盖区域的接客时间小于10分钟”,这是满足缩短接客时间的需求。 + +O3是“提高司机满意度”,这一点并没有在业务分析的因果循环图中反映出来,但它也是一个深度业务洞察的体现:司机满意度不高,会导致乘客的服务满意度下降。之前很可能没有设计过这项指标,所以KR1是先“定义并评估司机满意度”,KR2是“提升司机满意度大于75%”。 + +我们可以看到,每一项OKR中O和KR都有因果关系,如果KR达成,O就可以视为达成,并且KR之间都符合MECE原则——“完全穷尽,互相独立”,所以这是一个很棒的OKR。 + +至于Uber的Action Plan,因为还在保密期,我在这里就不展开详细讲了。 + +复盘归档 + +制定下一步的目标和计划之后,复盘会议就可以结束了。结束之后,复盘团队需要在第一时间对以下内容进行归档。 + +1. 会议记录 + +这部分内容主要由复盘团队的记录人负责填写(分工详见第5讲),要求详实、客观和完整。- + + +2. 目标和结果的比较 + +目标的意义是回答“当初是为什么设立这个目标”这个问题。- + + +3. 新的认知 + +列出成功和失败的主观因素与客观因素。- + + +4. 下一步的目标和计划 + +用表格形式整理OKRAP的内容。- +- +以上四个部分的内容,都需要备份到所属项目组的知识库中,分类归档。 + + + +小结 + +这一讲,我介绍了使用OKR制定目标和计划的方法,并且整理了复盘会议结束后需要归档的材料。现在,我们来回顾一下重点内容: + + +KPI和OKR的选择策略是:存量市场的确定性业务,适合用KPI考核;不确定性的业务,适合用OKR管理;而对于确定性业务要做出超级增量的,适合KPI和OKR一起使用。 +OKR的三层挑战包括:从O到KR;不同部门之间的OKR对齐;找到正确的O。 +复盘归档材料包括:会议记录;目标和结果的比较;新的认知;下一步的目标和计划。 + + +最后还想强调一点,OKR和复盘是应对VUCA时代的最佳拍档,我辅导过的创业公司中,甚至有一些只依靠这两种工具就在业内做到了非常靠前的位置。如果你有时间,建议认真读一读约翰·道尔的_Measure What Matters_(《这就是OKR》)。 + +思考题 + +这就是今天的全部内容,留一道思考题给你吧。 + +刚才因为商业保密的原因,我无法详细展开Uber华东区域制定的OKR的AP(Action Plan)。请你思考一下,如果你是负责人,会怎么设计呢? + +欢迎把你的答案写在留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/12DoubleCheck:怎么检查评估一次复盘的效果?.md b/专栏/跟着高手学复盘/12DoubleCheck:怎么检查评估一次复盘的效果?.md new file mode 100644 index 0000000..6c47369 --- /dev/null +++ b/专栏/跟着高手学复盘/12DoubleCheck:怎么检查评估一次复盘的效果?.md @@ -0,0 +1,223 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 Double Check:怎么检查评估一次复盘的效果? + 你好,我是张鹏。 + +在前几讲中,我带你了解了召开一次复盘会议的完整过程。那么,是不是说制定了目标和计划,做好了文档材料的归档工作,复盘就大功告成了呢? + + + +其实还没有,到这里复盘流程还差最后一步,检查评估,或者叫Double Check。你可以回想一下,以前复盘会议快结束的时候,领导是不是经常会说一句:“大家再看看,还有没有什么问题?” + +不过很可惜,很多人只是有这个意识,但不知道具体要怎么做。其实Double Check在职场上应用很多,它是指让不同的人用不同的视角、不同的方法再复查一次,确保方案有效或者结果可信。 + +做复盘同样需要Double Check,它的目的在于评估复盘效果,看看复盘过程本身以后有没有需要改进的地方。具体来说,检查评估的内容可以分为准备工作、会议氛围、团队状态、流程效率、成员信心、环节重点这六个方面。这一讲,我就分别介绍一下。 + + + +准备工作 + +第一个方面是准备工作。 + +“磨刀不误砍柴工”,准备工作提前做得越充分,会议就会越高效。但实际上,准备工作往往是很多团队做复盘的时候最容易忽视的地方。 + +在第2讲介绍的烘焙公司复盘案例中,一开始他们只准备了很粗略的营收数据,根本没有买不同打折卡的用户统计数据(包括不同金额的用户成为会员的年限),结果导致公司决策层担心“如果减少优惠力度,可能会丢失客户”。 + +后来,我们请财务人员现场从ERP(Enterprise Resource Planning,企业资源计划,可以成理解成企业的综合管理系统)中查询统计数据,才让决策层最终下定决心。实际上,那次复盘会议真正讨论沟通的时间只有2个小时,而因为临时查数据耽误的时间也有2个小时。 + +而且,这种导致复盘会议节奏拖沓的情况还不算最糟糕的,有些会议因为准备工作做得不好,甚至会导致看不到重要的业务突破点,复盘自然也就起不到作用。 + +那么,怎么检查评估准备工作呢?主要是考察以下两组问题: + + +是否提前准备好了数据详实的资料?是否需要现场大量调用不同部门的数据资料? +参与复盘的全部人员是否已经仔细看过所有资料?还是需要现场花很多的时间再次展示研读? + + +如果每组的第一个问题,你的答案都是肯定的,那么恭喜你,做得不错。 + +如果不是,那么你就需要在下次复盘会议之前进行改进加油了。 + +会议氛围 + +第二个方面是会议氛围。 + +“氛围不对,努力白费。”在第3讲介绍的英语培训学校的复盘案例中,开始复盘会议的氛围火药味很重,大家基本都处于“战斗PK”的状态。产品研发和市场营销两个部门简直像一对死敌——反正没有做出爆款,不是产品的锅,就是市场的锅。 + +这样的氛围,怎么可能得出什么好的复盘结论呢?直到我通过教练技术帮助他们改变氛围,让大家达成共识、一起为爆款产品努力的时候,真正有价值的想法才涌现出来。 + +复盘团队在会议氛围上要保持高度的觉察力,尤其是对于情绪的感受力,在检查评估的时候需要回答以下问题: + + +整个复盘是在甩锅追责,还是学习? +复盘的结论是指向人,还是指向事? +复盘的结论是领导的“一言堂”,还是和数据、逻辑关联度更高? + + +如果这三个问题的答案都是后者,那么这一关也过了。 + +如果不是,那么你就要小心了,既然会议氛围不太对,后面能得到的复盘结论就值得重新审视一下了。 + +团队状态 + +第三个方面是团队状态。 + +我在开篇词中提到过,失败的复盘会议有两个典型表现,一个是甩锅背锅,另一个是走过场。如果说前者是会议氛围的问题,那么后者就是团队状态的问题。 + +很多公司的初始团队在创业多年的共事过程中,形成了很深的个人情感关系,这对于团队原本是好事,但是如果团队成员不能区分在团队中的角色职责和个人关系,那么对于组织成长来说就是坏事了。 + +比如我在很多公司的复盘会议中都遇到过这样的情况:同样一个问题,对老人和对新人完全是两个态度,某些老员工即使犯了错,也会照顾情面,轻拿轻放,而对新员工则苛刻很多。这种亲疏有别的态度反而会对团队凝聚力带来伤害。 + +在团队状态方面,你需要检查的是: + + +是不是每个人都踊跃参与发言? +引导师、主持人、纪律官和记录人是不是全程都状态在线,各司其职? +设问人问问题的时候,有没有追问到底的精神?还是说明显带着很多顾虑(比如熟人之间不好意思搞得太狠,或者觉得因为问的问题太幼稚不敢继续往下问等等),只是蜻蜓点水地走个形式? +陈述人在陈述的过程中,有没有做到信息真实、逻辑扎实? + + +(角色分工详见第5讲。) + +如果答案都是肯定的,恭喜你,这一关又过了。 + +如果没有,你就需要用复盘的逻辑找出原因,下次改进。 + +流程效率 + +第四个方面是流程效率。 + +我之前指导过一家互联网服务平台做复盘。第一天,大家聊得热火朝天,从早上9点讨论到晚上12点,但是围着一个问题绕来绕去,没有形成几个有价值的结论。 + +第二天,我建议把每个人的发言控制在5分钟以内,要求第一句话先说结论,并且一句话必须说明白。结果,不但会议没有延时,反而有价值的结论密度高了很多。 + +效率也是评估一个复盘会议做得好不好的一个重要标准: + + +会议的日程安排是否得当,人员设备场地是否都提前安排妥当,遇到突发情况有没有Plan B?比如重要参与人临时有事,不在现场,是否提前准备好连线会议。 +会议的流程是否基本按照议程安排的时间线在走? +主持人和纪律官在把握会议的节奏上是否发挥了作用?流程的进度并非完全每个环节都按部就班,而在于整体时间不超,但是节奏可以变换。 +每次发言,是否先说结论?这一点尤其重要,从我的经验看,至少可以节约30%-50%的时间,先说结论,可以倒逼发言者想好再说。 +陈述人是否熟练掌握SCQA范式进行表达?这也是能够极大提升团队参与度的一种语言范式。 + + +成员信心 + +第五个方面是成员信心。 + +我在辅导某些公司做复盘的时候遇到过这样的事情:领导讲得兴高采烈,具体负责项目的团队成员却无精打采。 + +这种情况非常值得警惕。因为一线成员才是真正和炮火打交道的人,无论复盘会议指点江山的过程多么精彩,会议结束之后,具体工作还是需要一线成员来完成。只有重视他们的感受,计划的执行才会更加靠谱。 + +所以,我们要在达成目标的信心层面对团队成员进行评估。方式可以是参与者自评,也可以由复盘团队对全体成员进行投票打分。信心指数可以分为四档,分别是: + + +没信心 +不确定 +有信心 +非常有信心 + + +环节重点 + +第六个方面是环节重点,也就是对CLAP模型四个环节的完成质量进行检查评估。 + +1. 对比(Comparison) + +在这个部分,我们要仔细评估对目标和结果的思考和对比的过程: + +回顾一开始我们的目标,我们可以尝试问一下以下几个问题: + + +为什么要设定这些目标(考虑愿景、环境、资源等因素)? +在制定目标时是否有合理的数据作为参照依据(目标依据)? +制定目标时我们以什么作为衡量标准(目标标准)? +影响最终是否达成目标的因素有哪些以及执行时对应的策略是什么(风险评估及防范)? +最终完成目标可以带来什么收益(收益预测)? +数据型目标是否都符合SMART原则,描述性目标是否符合SCQA架构? + + +通过这几个问题的答案,我们可以重新思考团队需求与目标本身的匹配程度,同样,也可以整理出目标相关的指标,这正是评估目标的核心。 + +再检视一下结果与目标的对比: + + +所有的结果是否都已经呈现(完整性)? +数据型结果是否都符合SMART原则,描述性结果是否符合SCQA架构? +所有的结果是否能够与目标进行对比,用“+/-/=/0”表示目标对比结果的“增加/降低/相符/消失”? +是否参与者都确认这个对比结果? + + +这个部分同样是确认,复盘的数据、信息已经做到客观、完整。 + +2. 逻辑(Logic) + +这个部分,主要看工具使用和验证逻辑推理过程: + + +关于潜在客群,是否用了逻辑树进行分类和聚焦? +关于产品分析和经营策略,是否用了矩阵图(如SWOT、波士顿矩阵、4P等)? +关于竞争分析,是否用了关系图(如波士顿矩阵)? +关于价值链创造的环节是否使用了价值链流程图表达? +每一步分析是否都符合MECE原则,最后在每一个环节都细分为“可控/半可控/不可控”等几个部分? +经营/运营数据与当初的目标之间的差距,标注的颗粒度是终极结果,还是每个环节? +团队士气、状态等因素对结果的影响是否有陈述? + + +这个部分主要是检视整个逻辑分析是否是系统性思考和结构化表达。 + +3. 认知(Acknowledge) + +这个部分要看我们的推论是偶然性因素导致,还是必然性因素导致: + + +各个因素之间的关系是否用因果循环图进行推导,逻辑推导是否通顺? +终极目标(多点汇入)和关键环节(循环的关键卡点)是否已经全部找出? +结论是通过归纳法得出的,还是演绎法得出?这个部分可以快速识别出结论是“可能的”,还是“确定的”? +团队是否对结论和目标进行了共识? + + +认识升级是整个复盘过程中最重要的环节,也是承前启后的环节。 + +4. 规划(Project) + +这个部分即将进入到下一个循环的开始,目标和计划一旦推出,会直接动员团队全员各司其职。 + + +对于确定性业务的目标,是否还有优化空间,是否经得起历史数据的推导? +对于不确定业务的目标,是否有个试错的范围和底线,比如时间、人手、资金投入等? +最低的期待是什么? + + +对成熟业务的目标标准,可以参照以往的数据模型进行推算;对于创新业务的目标,首先要有“输得起”的底气才可能去尝试。 + +我从很多持续成功企业的复盘中发现,业务“基本盘”是一个特别需要重视和加强的部分,这个部分适合优化和迭代,在创新业务起量之前,这种“确定性业务”千万不能丢,即使公司打算做转型,也要有足够的战略耐心,你永远不知道“创新业务”的前面是峰还是谷,“创新”是需要时间来验证的。 + + + +小结 + +在这一讲中,我为你讲述了检查评估复盘效果的方法。现在,我们回顾一下重点内容。 + + +复盘会议之后需要做检查评估,也就是Double Check,它的目的在于评估复盘效果,看看复盘过程本身以后有没有需要改进的地方。 +检查评估的内容可以分为准备工作、会议氛围、团队状态、流程效率、成员信心和环节重点这六个方面,每个环节都有用于检视复盘的关键问题。 +Double Check的核心点在于优化复盘过程本身,尤其是和业务本身无关的环节,容易被忽视,更值得重视。 + + +复盘是一个无限迭代的过程,永远不会有一个完美的流程和标准,所以Double Check更是显得重要,在做复盘中学习怎么做复盘,提升自己对于复盘的认知才是关键。 + +思考题 + +这就是今天的全部内容,最后留一道思考题给你吧。 + +请您根据这一讲学到的知识,对以前参加的某次复盘做一次检查评估,看看效果怎么样。 + +欢迎你把答案写到留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/13案例导学:我是怎么帮助企业做复盘的?.md b/专栏/跟着高手学复盘/13案例导学:我是怎么帮助企业做复盘的?.md new file mode 100644 index 0000000..9546780 --- /dev/null +++ b/专栏/跟着高手学复盘/13案例导学:我是怎么帮助企业做复盘的?.md @@ -0,0 +1,90 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 案例导学:我是怎么帮助企业做复盘的? + 你好,我是张鹏。 + +在前面的课程中,我们学习了复盘的基本方法和实操流程。从今天开始,我们进入到这门课程的第三部分,实战案例篇。 + +我会从六个典型的、能创造巨大商业价值的实战场景出发,为你详细解读最近3年我亲自操盘的真实案例。这些案例可以为你提供解决各类问题的参考模板,同时我也会通过它们帮助你进一步掌握我的复盘心法。 + +在正式讲解之前,我想先为你做一期学习指导,让你能更好地理解这些案例的内在逻辑。 + +工作流程 + +作为一名商业顾问,我的客户是企业,说得更明确一点,就是企业主。他们都是在意识到企业运营过程中已经出现问题或者即将出现问题的时候才来找我,所以在某种意义上,我的角色就相当于是企业的医生。 + +那么企业主一般会找我解决什么问题呢?我举几个最常见的例子吧: + + +业务增长速度太慢。 +企业发展太快,人才跟不上。 +现金流断裂。 +想要转型,但又没有把握,担心失败。 +明明产品或者业务有亮点,业绩表现却总是不好。 + + +…… + +如果用医学思维来打比方,这些问题只是表面的症状,而不是深层的病因。如果把企业所有的问题都简单地归结为经营不善(就像病人说“我不舒服”),那么商业顾问的工作就没有价值了。 + +同样是发烧咳嗽,既有可能是普通感冒,也有可能是新冠肺炎。如果只是“头痛医头、脚痛医脚”,不但解决不了问题,反而会错过解决问题的黄金时期。 + +面对这些问题的时候,我会学习医生看病的思路,先检查,再根据情况诊断病因,开出药方,最后给出服药指导以及平时的注意事项。 + +所以,我的工作流程可以分为5个步骤:需求理解、业务调研、企业诊断、方案评估和实施执行,如下图所示: + + + +为了方便你学习理解,实战案例篇每一讲的结构安排如下: + + +案例背景:开头简要介绍需求理解和业务调研的内容。 +复盘详解:主体部分详细讲解企业诊断和方案评估的内容,这一部分是由我指导企业高管和他们的复盘团队共同完成的。 +最终结果:结尾补充说明实施执行的内容,下一步的行动计划由企业自己完成,我会跟进了解最后的完成情况。 + + +成功公式 + +在刚才提到的5个步骤中,含金量最高的就是企业诊断了。我是怎么做诊断的呢? + +华人管理大师杨国安教授曾经总结过一个企业成功公式: + +企业成功 = 战略 × 组织能力 + +我根据这个公式的思路,对企业经常遇到的问题进行整理分类,画出了逻辑树: + + + +先来看战略部分。 + + +战略是指企业打算要干什么,方向、安排和规划都是什么样的。 +业务是指具体怎么干,研发、生产、运营和营销会怎么安排。 + + +再来看组织能力部分。 + + +组织能力是指团队能不能把计划执行好,达成战略目标,确定的事情能不能高效完成,不确定的事情有没有规避风险的办法。 +组织结构是指组织能力的框架。就像排兵布阵一样,组织结构如果出现遗漏和错搭,就会导致木桶的短板效应。 +流程是指能够优化现有资源配比的工具。尤其是对确定性的业务来说,流程能够极大地提升效率。 +员工能力是指个人能力。就算其他所有的条件都相同,员工个人能力不同,结果也会不一样。 +预算是指财务收支计划。它经常被人提起,又经常被人忽视。很多拿着投资人的钱,或者有预收款业务的公司,反而比那些精打细算的小商人在商业上阵亡率更高,就是因为预算没有控制好。 + + +而我诊断的办法是,先看企业的战略还是组织模块和他们的大目标(有时候是愿景或者战略目标)是否对齐(全面体检);如果发现有问题,再沿着分支往下找,并且和企业显示的问题进行对应(专项深度检查);当找出问题根源的时候,再看怎么调整最有效。 + +当然,这6个场景并不全面,还有一些问题并没有覆盖到。比如产品设计本身的问题,毕竟每家企业的主营产品完全不同,这种问题只能由业务团队自己搞定;再比如企业文化问题,这不是三五个月就能改变的。 + +所以需要声明一点,实战案例篇的案例主要是针对我在个人商业实践中经常遇到的、具有普遍性的、能够在短期(一个季度内)内解决的问题。 + +另外,由于这些都是真实的商业案例,有一部分具体数据需要保密,不能展示出来,所以请你把关注点放在复盘思路上,这也是这门课程最有价值的部分。 + +好了,导学就到这里,我们下一讲见。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/14战略升级:5年增长100倍的跨境电商做对了什么?.md b/专栏/跟着高手学复盘/14战略升级:5年增长100倍的跨境电商做对了什么?.md new file mode 100644 index 0000000..efe84c8 --- /dev/null +++ b/专栏/跟着高手学复盘/14战略升级:5年增长100倍的跨境电商做对了什么?.md @@ -0,0 +1,180 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 战略升级:5年增长100倍的跨境电商做对了什么? + 你好,我是张鹏。 + +在实战案例的导学中,我分享了诊断企业问题的基本思路:首先看企业的战略和组织能力是否和企业的愿景匹配。所以业内会有这么一句话:战略定生死,组织能力定成败。 + + + +战略不光是方向和目标,还有一系列的行动计划,环环相扣。如果一开始的方向错了,或者时机和行动计划不匹配,企业很容易陷入困境。 + +很少有人一次创业就成功的,连续创业者一个很显著的共性是经得起折腾,还有超强的复盘学习能力。在这一讲,我会分享一个战略升级带来企业重生的案例,主角是一家连续创业的团队,接下来我就从它第一次辉煌的尾声说起。 + +案例背景:破产 + +Y公司是一家深圳的创业公司,他们的主营产品是投影仪,专注国内市场,主要的销售渠道是国内的电商平台,包括某东和某宝。 + +得益于深圳当地电子产品供应链的优势,他们的生意做得风生水起,2013年在国内投影仪市场处于前三名的位置。 + +这时候,有投资人找到创始团队说:“我看好你们的业务,想给你们投资。” + +于是Y公司的高层开会讨论,采用了3C战略分析框架来分析: + + +Customer(客户):投影仪还没有全面地走进家庭,国内投影仪市场的主要客户是各类企业。就算还没有完全变成存量市场,增长也已经非常缓慢,并没有太多想象空间。 +Company(自己公司):从核心技术、生产工艺以及性价比等各方面来看,自己都已经做到国内顶级厂商的水平,很难有更大的突破。 +Competitor(竞争对手):主要的友商厂家某米和某果,在技术、质量控制、渠道、成本和团队等各个方面都没有绝对的领先优势。 + + +Y公司的创始团队当时对商业的认知是:做好产品、服务好客户就能赢得终极的胜利,不要被“融资”这一类虚头巴脑的事情给迷惑了心智。所以他们的选择是,直接拒绝投资人。 + +接下来,你可能也猜得到,每一次认知和事实本质不同的时候,现实就会啪啪打脸。就在他们拒绝接受投资的时候,友商某果和某米欣然接受了投资,并且开始对Y公司进行降维打击: + +首先,进行大规模采购,极大地降低了采购元器件的单位成本,从而取得了单位成本优势。 + +然后,在电商平台上大规模降价,把当时投影仪的单价直接从7000元以上拉到3000元以下。 + +这些动作依然没有引起Y公司高层的警觉。因为他们认为,“亏本占领市场”的策略在2C市场也许玩得转,但是在2B市场接近存量的情况下完全是个笑话,所以这种补贴式打法不可能长久。 + +如果对手看得见的动作让你笑话,那么看不见的动作可能就会要你的命。 + +某米和某果在补贴降价的同时,秘密招募了产品研发团队,针对个人和家庭市场陆续开发出1999元~4999元价位的30cm投影距离的新产品,其中某米的产品后来还拿到了德国“红点”设计大奖。 + +这一批产品的问世让传统的面向2B市场的投影仪厂商都面临灭顶之灾。当友商的卖价比自己的成本价格还要低,自己又没有时间和资源做转型的时候,Y公司就只有死路一条。 + +Y公司的一位创始人在接受我访谈的时候说:“当我们在电商平台上看到这些产品和价格之后,就彻底绝望了。” + +这一次失败直接导致了Y公司的破产,创始人只能变卖所有的房产偿还债务。在送别最后一个员工的时候,一位创始人说:“对不起,是我们决策错误,才导致你丢掉工作。”之后他躲进卫生间,拧开水龙头,痛哭流涕。 + +但是,Y公司真的死了吗? + +复盘详解 + +2015年春季,Y公司的创始团队对过去两年做了一个深度的复盘。 + +对比 + +Y公司2013年初的目标是守住国内市场的前三名,稳扎稳打,以质量和口碑赢得行业的认可,最终有机会能成为行业龙头,但结果却是: + +第一,从市场份额上看,不但没有守住行业前三的位置,反而在不到两年的时间里彻底丢掉了原有的市场。 + +第二,从产品上看,一直固守在原有的企业客户市场,根本没有发现个人和家庭市场有这么大的需求,让两家友商用新的产品兵不血刃地在新的市场里做出了更大的蛋糕。 + +第三,从速度上看,对行业的变化反应太慢,直到对手用了一年的时间全面地占领市场份额和用户心智之后,才反应过来。 + +逻辑 + +战术上的勤奋,不能掩盖战略上的懒惰。 + +Y公司一直把注意力放在产品的设计、质量和成本控制等因素上,忽视了同行引入资本后的降维打击。我们用波特五力图来解析一下Y公司战略上的失误。 + + + +首先是行业内的竞争,在2013年~2014年,针对企业客户的投影仪市场已经趋向于饱和,大家在这个领域都没有特别大的突破。Y公司的高层这一点上并没有误判,新的进入者也没有产生。 + +但是,替代者却是两个原有的老对手。他们融资之后,首先改变了供应商的“(质)量-(成)本-价(格)”体系,简单地说,就是通过增加电子元器件的采购量,使得成本下降,在单位成本上取得绝对的竞争优势。 + +不过,这样做也会带来问题,因为原有的市场根本吃不下这么大的产量。Y公司也是基于这一点才没有做出任何反应。但是注意,这个假设是基于“市场不变”这个前提做出的。 + +最后,我们来看客户层面,竞争对手根本没有在原有的企业客户市场上纠缠,而是开辟了新的客户群体,个人和家庭用户。 + +针对这一需求,他们打破了“只有企业级客户才需要投影仪,因为家里的客厅一般不够大”这样的认知,开发出只需要30cm投影距离的新产品,并且还和互联网视频内容相结合,非常契合个人和家庭用户的需求。 + +这种开辟新市场的做法和供应商的规模低价结合起来,形成一个完美的增强循环,迅速地干掉了这个行业其他的玩家,形成了垄断。 + +认知 + +竞争对手多数时候是最好的老师。 + +既然我们以前的基本盘业务是被竞争对手通过开辟新市场和低成本优势打败的,那么我们现在是不是也可以通过寻找另一个新市场来赢得下一场胜利呢?——这是Y公司的核心团队(以下简称Y团队)做出的第一个假设。 + +竞争对手融资之后带来的降维打击让Y团队记忆深刻,于是他们又做出第二个假设:不是缺钱的时候才需要融资,而是当你有了更多的资金,就可能改变行业竞争规则。这也是Y团队在二次创业过程中特别在意的地方。 + +虽然Y团队在国内投影仪这一块彻底失败,甚至公司都破产清算了,但是他们并没有丢失再次创业的决心。 + +“已经到谷底了,也该反弹了吧。”这是另一个联合创始人在回顾那一次复盘的时候说过的话。 + +通过朋友的推荐以及对各种市场数据的对比分析,Y团队一致认为可以尝试跨境电商。在正式决定之前,他们通过SWOT分析对自身和行业做了一个基本分析: + +S强项: + + +团队对消费类电子影像产品(不是专业级影像产品)整个研发生产及供应链都非常的熟悉。 +对线上运营很熟悉。 +经历过一次失败,核心团队没有散,背靠背的信任更强了。 + + +W弱点: + + +对其他产品不熟悉。 +对线下运营并不熟悉。 +国内电商平台,做同类产品的机会窗口已经过去。 + + +O机会: + + +跨境电商当时刚刚兴起,亚马逊上对中国的供应商有流量红利。 +和国外的同类产品比较,性价比依然非常地突出。 + + +T风险: + + +团队里并没有做过跨境电商的伙伴,属于“摸着石头过河”。 +公司当时只有一个投影仪品类,单一品类是否能在亚马逊上站住脚,还没看到先例。 + + +最终,大家决定开始试水跨境电商,并开始制定行动计划。 + +规划 + +根据前面的分析和洞察,Y团队判断出在亚马逊上做消费类影像产品是值得尝试的,他们最终采取了SO策略(优势+机会),实行增长型战略。 + +根据大家商议的结果,初步制定的计划如下: + +O:在亚马逊平台上验证销售自产消费级影像产品的MVP。 + +KR1: 实现2000万的销售额。 + +KR2: 构建完整的跨境电商运营团队。 + +KR3: 主要的3款产品在亚马逊的细分品类里排名第一。 + +最终结果:新生 + +团队对产品系列的供应链以及价值链非常熟悉,核心团队也都是现成的,这一点在业内也是有共识的,只是缺乏资金和跨境电商的运营经验。 + +于是Y团队接受了国内某头部跨境电商大佬的战略投资,并以in-house的模式驻场创业。 + +第一年,他们踉踉跄跄地实现了自己2000万销售额目标,同时也打造了一套能够复制的运营流程和能够把产品排名Listing做到亚马逊站内第一的方法,并且积累了相关的资源。 + +在验证了第一年的MVP之后,他们后续的每一年都会对整个运营模型进行迭代优化升级。在接下来的不到5年的时间里,Y公司的业绩超过了20亿,成为了国内跨境电商业内的明星企业。 + +小结 + +这一讲我为你介绍了Y公司的案例。他们从一开始因为固守原有市场,在竞争对手融资之后的降维打击很久之后都没反应过来,被人干到破产;后来向对手学习,开辟海外新市场,5年内增长了100倍。他们的转折点显然来自痛定思痛的那次战略复盘,其中有几点非常值得借鉴: + + +不要用固定思维看市场和产品,用波特五力模型可以帮助你更好的分析市场整体环境。 +SWOT模型是一个分析自身的好工具。 +增长的关键是对市场的深度洞察之后,在自身强项能力和市场的空白点中找到交集。 +战略是一个聚焦的过程,用优势与别人合作能够取得事半功倍的效果。 + + +思考题 + +这就是一讲的全部内容了,最后留一道思考题给你。 + +回忆一下,你所属的公司,在过去的几年中,有没有发生过重大的转型,结合这一讲学习的内容,请你分析一下为什么会有这种变化;再根据目前的市场环境和行业趋势预测一下,接下来你们公司还有可能发生什么样的转变? + +欢迎你把答案写在评论区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/15业务关键点:改进一个点业绩增长50%?.md b/专栏/跟着高手学复盘/15业务关键点:改进一个点业绩增长50%?.md new file mode 100644 index 0000000..e69de29 diff --git a/专栏/跟着高手学复盘/16组织结构设计:变动一个职位,就能带来100万_月的增量吗?.md b/专栏/跟着高手学复盘/16组织结构设计:变动一个职位,就能带来100万_月的增量吗?.md new file mode 100644 index 0000000..156e4a1 --- /dev/null +++ b/专栏/跟着高手学复盘/16组织结构设计:变动一个职位,就能带来100万_月的增量吗?.md @@ -0,0 +1,176 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 16 组织结构设计:变动一个职位,就能带来100万_月的增量吗? + 你好,我是张鹏。 + +前两讲我分别为你介绍了战略升级和业务关键点优化的案例。根据杨国安教授的企业成功公式:企业成功 = 战略 × 组织能力,在做企业诊断的时候,如果大的战略方向和具体业务开展都没什么问题,我就会从组织能力的角度来考虑。 + + + +说起组织能力,我想你可能首先会想到一个词,组织结构(或者叫组织架构)。我发现一个现象,企业一到要改革或优化的时候,管理层最喜欢做的事情就是“调整组织结构”。 + +组织结构的调整一定是大刀阔斧的整改吗?怎么设计组织结构,才能真正取得成效呢?这一讲,我会为你讲解英语培训机构A公司的例子,让我们看看一个小小的改动是怎么带来巨大的收益的吧。 + +案例背景:增长“虚胖” + +A公司是一家少儿英语线下培训机构,在广州的直营校区有20家左右,凭借独特的游戏化产品体验设计以及对孩子贴心的服务在当地赢得了非常高的美誉度。他们服务的用户群体主要是幼儿园和小学的孩子们,也就是3~12岁的这个年龄段的学生。 + +我刚接手这个项目的时候,问题并不具体,只不过是A公司的老板感觉公司的增长有些“虚胖”,因为各个直营校区的实力不平衡,有的校区赚钱,有的校区不太赚钱。 + +老板的直觉是,随着近3年的快速扩张,团队的领导力被稀释,但是提升领导力非常不容易,需要大量的时间和人才。所以他希望我帮忙诊断一下,看看问题是不是出在这个点上;如果是,就请我帮他提一些可用的建议。 + +于是,在我的指导下,A公司的战略委员会组织了一次复盘。 + +复盘详解 + +对比 + +一般而言,老板对自己公司的业务和组织有着天然的敏感度,所以我们就从老板提出的直觉着手研究。 + +我们从横向和纵向两个维度分析了每个月运营数据。横向维度就是对比各个校区的差异,纵向维度就是查看三年来每个月数据的变化。 + +我们发现,一年内的新校区的上门流量、转化率、业绩和交付等数据全方位落后于一年以上的成熟校区,这就说明老板的担忧是有道理的。 + +A公司的盈利模式,主要是通过交付培训来收取学费,他们的业绩公式是: + +业绩 = 流量 * 转化率 * 交付率 + +每年的暑假是他们的招生旺季,春节前后是淡季,其余时间则属于正常时段。通过时间轴数据对比,我们发现,淡季最大的问题是流量不够,而旺季最大的问题是转化率偏低。 + +逻辑 + +针对A公司的业务,我们可以按照AARRR模型把整个价值链分为五个阶段: + + +活动引流 +到店试听 +转化为常规班 +消课交付 +口碑转介绍 + + +用流程图表示如下: + + + +接下来,我们用排除法来寻找需要分析的重点。 + +A公司已经有统一的教学体系,并且得到了市场的认可,70%以上的新学生来自口碑推荐,所以到店试听、消课交付和口碑转介绍等和教学质量有关的环节都没有问题。 + +所以新校区的薄弱环节就是剩下的两个环节,活动引流和转化为常规班,它们都是和营销质量有关的环节。 + +每个校区的流量来源主要是以校区为圆心、以3公里为半径的圆形区域。在以往的模式下,每新开一个校区,必须同时保证教学质量和营销质量,才能带来利润增量,让校区实现盈利。 + +所以,人才建设是关键,而每个校区的负责人是校长,所以对校长的培养就成了关键中的关键,“千军易得,一将难求”说的大概也是这个意思。 + +我们用逻辑树对每个校区的负责人,也就是校长的能力做了一个模型分析。校长的能力主要包括4个方面: + + +教学能力 +管理能力 +引流能力 +转化能力 + + + + +很明显,作为教学、管理以及营销的复合型人才,校长的培养难度很大,正常的培养周期需要3年左右。 + +通过对比,我们发现在4项能力中,新校区的校长所欠缺的主要是活动引流能力和转化能力,也就是营销方面的能力。这一点正好跟新校区和营销质量有关的环节比较薄弱的发现相吻合。 + +这个时候,明确要解决的问题非常重要。所以我们在考虑,究竟是要解决培养校长的问题呢,还是解决活动引流和转化常规班的问题呢? + +如果选择先培养校长,那么难度大、周期长,如果选择先解决活动引流和转化常规班的问题,那么又缺乏优秀的校长来做这些事,看上去这似乎是一个两难的问题。 + +认知 + +为了解决两难问题,我们需要更新认知,突破思维瓶颈。所以我们画了一张因果循环图来系统地分析业务: + + + +从图中我们可以看出,围绕更高业绩这个终极目标,出现了2个增强循环: + + +以交付为核心的增强循环(编号2),通过更好的教学效果和更多的学校来驱动。 +通过更强的营销能力来驱动的增强循环(编号1)。 + + +所以,更高业绩的驱动力主要有3个: + + +更好的教学效果 +开设更多的学校 +更强的营销能力 + + +提升教学效果周期长;开设更多的学校,不光需要更多的校长,而且投入大,风险高。于是我们把重点放在了更强的营销能力上。 + +因为学校有淡旺季之分,如果按照平时需求配备人手,旺季流量多的时候就会损失转化率;如果按照满足校区旺季的需求配备人手,那么淡季的时候就会出现人员冗余,所以我们提出了一个建立校区共享营销团队的假设。 + +这个营销团队可以在各个校区之间流动,旺季的时候到校区帮忙转化,淡季的时候主要想办法策划更多更高质量的营销活动。这个团队的人并不需要太强的教学能力,只要营销能力够强,通过简单的培训掌握通用的行业知识就行了。这种通用的营销人员在市场上也更容易招聘到了。 + +经过计算,只要旺季转化率提升2%,就可以覆盖这个团队全面的费用(即公司增加的投入),对于这种可能以极小的投入就做出大业绩的项目,营销总监也很感兴趣。 + +规划 + +确定了建立校区共享营销团队的方向之后,我们迅速制定了接下来的目标和计划,因为属于创新项目,所以我们采用OKR管理下的MVP来迭代进行,具体分为三个阶段实施: + +第一阶段是MVP验证,主要的目的在于验证共享营销团队能提升旺季转化率的MVP是否跑得通。 + +第二阶段是逐步推广,如果MVP跑得通,大规模推广之后,是否对整体业绩有大体量的提升。 + +第三阶段是确立边界,如果第二阶段能够验证成功,要计算这种模式的边界在哪里,只是依靠营销提升业绩一旦超过某个边界,边际成本反而会上升,这时候原有模式就需要迭代升级了。 + +最终结果:利润大幅增加 + +MVP验证 + +由于第一阶段正好在销售旺季,所以关注点在于转化率,我们制定的OKR如下: + +O:打造业绩提升的第二增长曲线——“共享营销团队”。 + +KR1:做出团队成员画像。 + +KR2:招聘两个成员。 + +KR3:分配6-8个校区,在3个月内额外提升30%业绩。 + +经过简单的行业知识培训,我们为招聘到的2个共享营销团队人员分配了6个校区去做尝试。经过短暂的磨合,他们第一个月做出了60W的业绩,第二个月做出了80W的业绩,这个结果大大超出当初的预期。 + +考虑到并没有新增校区,我们的投入只是这两个人的工资和绩效支出,成本比例小到几乎可以忽略不计。初步验证成功之后,团队迅速地进行复盘迭代,夯实了这种“共享营销团队”的整体运营模式。 + +逐步推广 + +根据A公司的校区规模,我们按照3~4个学校分配1个共享营销团队人员的比例,最终把团队扩建到8个人,并且结合新校区和成熟校区的不同情况,继续升级运营模式。最终这个团队的成果远远超过100万/月的增量利润,相当于新增了若干个校区。 + +确立边界 + +当“共享营销”团队达到8人之后,这个模式也就对存量市场的业绩和利润提升做到了极限。这时,原有的MVP就已经成为固定的流程和组织结构。如果还想创造新的增量市场,就需要进一步迭代升级才有可能了。 + +小结 + +在这一讲中,我通过一个做少儿英语培训的A公司的案例对整个复盘的流程做了一个详细的说明。现在,我们回顾一下重点: + + +通过老板对业务天然的敏感点进行切入。 +通过AARRR模型梳理整体工作流程,通过逻辑树找出能力模型的选项。 +对比业务找出可能的增长点,通过因果循环图对假设进行推演,找出提升业务的关键点。 +用OKR来管理验证创新业务的MVP,根据业务模型计算创新的边界。 + + +组织结构的调整,一定是业务导向的。灵活运用流程图和矩阵图可以分析出提升业务的关键点,用因果循环图可以对假设进行推演,MVP可以小规模地进行实践迭代。你学会了吗? + +思考题 + +这就是这一讲的全部内容了,最后留一道思考题给你: + +回忆一下在你接触的项目或者业务中,有没有和这一讲类似的情况,你会怎样设计组织结构来实现提升业务产值的目标呢? + +欢迎你把答案写在留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/17高效赋能:怎么建立流程才能把人效提升3倍?.md b/专栏/跟着高手学复盘/17高效赋能:怎么建立流程才能把人效提升3倍?.md new file mode 100644 index 0000000..fbe9176 --- /dev/null +++ b/专栏/跟着高手学复盘/17高效赋能:怎么建立流程才能把人效提升3倍?.md @@ -0,0 +1,190 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 17 高效赋能:怎么建立流程才能把人效提升3倍? + 你好,我是张鹏! + +在前三讲中,我分享了战略、业务、组织结构方面的复盘案例。这些都属于宏观显化的调整,我们的感受比较直观。比如战略的变化会体现在内部方向上,业务的改进会直接反映在生产销售上,组织结构的变化也非常明显。 + + + +而在接下来的三讲中,我会分享流程、员工能力和预算的案例。它们属于微观内化的调整,带给人的日常感知可能没那么强烈,但同样能取得非常好的效果,适合战略、业务和组织结构短期不需要变化,或者不能变化的企业。 + +今天,我们就从通过优化流程把人效提升了3倍的T公司说起。 + +案例背景:缺乏竞争优势 + +T公司位于北京,是一家以新兴互联网企业为客群的创新型办公选址服务公司。创始人小Y是SOHO中国的TOP 3 sales,也是和我认识多年的朋友。 + +小Y创业的时候有两个选择: + + +做一家未来能够规模化的选址顾问公司,长期的愿景是成为像戴德梁行(Cushman & Wakefield)或者第一太平戴维斯(Savills)这样的综合物业管理公司。 +成为一个利润率很高但是规模不大的个人选址工作室。 + + +小Y的选择是前者,她的说法让我印象非常深刻:“中国需要一家有世界影响力的专业机构。” + +办公选址行业的业务模型并不复杂,是一个典型的撮合平台。一边是企业客户的选址需求,一边是写字楼的供给,而选址公司的重点就是撮合它们。 + + + +T公司的早期客户,95%以上都是小Y自己的个人客源,而且除了小Y,其他员工暂时还不具备开发新客源的能力。虽然公司成立了市场部、销售部和房源部等部门,但是大家的联动性不强。 + +办公室选址这个业务,对于企业来说是刚需。尤其是对于高科技行业来说,场租在成本项中的占比还是比较大的,一旦预测到团队规模即将发生变化,公司的行政主管第一时间就会想到要调整办公空间的大小。 + +但是,由于市场上提供办公室选址服务的竞争对手有很多(写字楼租赁中介),T公司一开始除了朋友信任和熟客推荐,并没有什么特别的优势。而且小Y还特别强调一点,她不愿意打折,因为这会显得自己的服务价值感不高。 + +那么问题来了,从客户端来看,知名度不高、同质化严重、还不愿意降价;从企业内部来看,部门之间联动性不强、员工的个人能力也不够,T公司要怎么才能在竞争中脱颖而出,快速占领市场呢? + +复盘详解 + +基于刚才介绍的情况,我们决定做一次深度复盘,一起探索在不改战略、不改业务模型、不改组织结构的前提下,能否找到制胜之道。 + +对比 + +在对比环节,可以自己和自己比,也可以自己和竞争对手比。对比能看出差异,差异能让人看到价值点所在。 + +由于T公司没有太多的历史数据积累,我们选择对标同行进行对比,用对方做到的当目标,用我方做到的当结果。 + +因为没有具体的营销策略和特色的产品,也没有渠道和价格等方面的优势,这一次,我并没有用4P理论做精细化比较,而是使用了颗粒度更粗的SWOT理论。 + +我们选定了在同行中做得比较好的YX公司作为标的,比较如下: + + +优势(S) + + +T公司最大的优势在于小Y的专业度够高,她在帮助客户选址的时候,能够结合客户的业务和业态,考虑得足够周全,提升客户的ROI。 + +除了客户能想到的写字楼面积、价格和免租期之外,小Y考虑的事情还包括: + + +楼宇广告的折扣。 +写字楼周边的配套设施给客户的员工带来的便利性。 +楼宇中其他企业是否能和该客户形成协同。 +客户下一次搬家、扩张或者收缩的后续企业接盘工作。 +风水。 + + +…… + +这种服务是包括YX公司在内的绝大多数同行不具备的,所以深得存量客户的认可。 + + +劣势(W) + + +T公司最大的劣势在于团队不够成熟,不但没有太多办法扩大客群,甚至就连现有的几百家客户,也做不到全部提供小Y本人这种水平的服务。 + +相比之下,YX公司内部的流程和标准能够保证他们对所有客户的服务能基本保持在同样的水平。 + + +机会(O) + + +T公司的机会在于抢占了细分市场的先机,北京的互联网公司这些年一直蓬勃发展,但是当时行业内并没有一家成规模、有品牌的公司专门为互联网创业公司提供选址服务。 + +YX公司的灵活度不够,还没有专门针对互联网创业公司这一块业务发力。 + + +威胁(T) + + +T公司面临的威胁在于业务门槛并不高,服务性的创新很容易被抄袭模仿,然后被资金更雄厚的竞争对手通过价格战打压,最终丧失生机。 + +像YX公司这样的竞争对手一旦觉察到这一点,完全可以通过“抄袭-规模-低价”这样粗暴的竞争方式碾压T公司。 + + + +逻辑 + +做完和YX公司的SWOT对比分析之后,经过讨论,我们共同认为应该扬长避短,采取SO战略,利用好互联网创业公司这个细分市场的先机。 + + + +所以我们要做的事情是: + + +把小Y一个人的经验复制到整个团队。 +制定流程和标准,保证对所有的客户能提供同样品质的服务。 +防止竞争对手抄袭T公司的模式通过低价对T公司形成致命的竞争优势。 + + +T公司当时只有小Y一个人具备前期获取客源、中间跟进客户、后期交付服务的全案能力,而锻炼这种能力需要大量的案例积累经验,无法通过短期培训的方式让其他团队成员迅速掌握。 + +于是我把选址流程做了拆解,想看看有没有办法在战略不变、组织结构不变的情况下激发整个团队的产值效率。 + +流程的第一步获取客源,这一步只有小Y能做。 + +接下来的每一步,包括跟进洽谈、需求确认、实地带看、签约交付以及售后服务,都是可以快速标准化和复制的动作,团队其他成员也可以做。当然,每个人的效率在每个环节点上是不一样的。 + + + +在对客户跟进洽谈的过程中,客户非常有可能同时和多家选址公司或者写字楼租赁中介进行接洽,这对T公司是非常不利的,这个环节最好能把控住,才能走到后续的流程。 + +由于T公司在服务上还是非常有特色的,加上还有之前的客户做背书(T公司的客户主要来自老客户推荐),这一块的客户信任度是非常高的,丢单主要是因为竞争对手一方销售的人为因素导致。 + +从需求确认环节开始,一直到签约交付,T公司几乎能做到100%的转化率。 + +我问小Y:“如果能加上一个能确保签约的环节,会不会更好?” + +小Y回答:“当然,关键是怎么加呢?” + +我说:“在跟进洽谈之后,需求确认之前,只要我们的服务展示让客户相信我们能够服务好他们,并且能够最大限度地为客户着想,争取到最大的利益,客户是不是就可以跟我们签一个独家选址服务委托合同?” + +小Y说:“对呀,这样就有排他性了。而且为了让客户利益最大化,我们可以在独家选址服务委托合同上写明,同类的服务优先选择我们,除非我们的竞争者能在明面上提供更有竞争力的条件。” + +我深深地为小Y叫好,良性的竞争有利于自己的进步。 + +认知 + +修改之后的流程图如下所示,在跟进洽谈和需求确认之间加入了独家委托这个环节。 + + + +接下来,我们根据团队中每个人的特点,把他们安排在不同的环节之中,进一步巩固和加强了T公司在互联网企业选址服务这个业务的能力。 + +当你把一个看似复杂的业务拆分成各个模块的时候,就会发现其实并不需要从头打到尾的超级员工,只要把合适的人放在合适的岗位上,就能发挥出好几倍的效能。 + +规划 + +在梳理好整个流程之后,就需要准备接下来的目标和计划了。我们制定了一个总体高于行业三倍人效的目标,并且拆解了这个目标,把任务根据每个人的能力做了分解: + + +小Y主抓获取客源,她的关键结果是当年新增两倍的有效客源。 +相对有经验的小伙伴,做跟进洽谈、独家委托和需求确认,关键结果是转化率提升50%。 +新进的小伙伴,做好实地带看和售后服务,保持转化率不变即可。 +签约交付和售后服务又转移给有经验的小伙伴,保持客户满意度不变或略高即可。 + + +最终结果:3倍人效 + +在这样的目标和关键结果的的指导下,T公司在加入独家委托这个关键环节的一年之后,在人员不变的情况下取得了超过同比去年自身的业绩3倍的成长,也达成当初制定的目标——3倍行业平均人效。 + +小结 + +在这一讲,我通过了一个选址服务公司的案例,为你讲解了通过优化流程为团队高效赋能,从而实现高速增长的过程。现在,我们回顾一下重点: + + +把确定性工作拆分成标准化的流程,能够帮助你观察到颗粒度更细的环节(着力点)。 +一般来说,转化率不够高或者流程不太顺的环节是需要复盘重点讨论的关键环节。 +通过SWOT分析找出怎么改变(增加、减少和改变)这些关键环节,让流程更顺畅。 +最后根据每个环节对能力的不同要求,把任务分配到合适的成员身上。 + + +这样可以把一个复杂的工作拆分成“只有关键节点要求能力高”的流程,从而达到更合理的团队能力配置,即使在整体能力无法快速提升的前提下,依然可以取得可观的增长。 + +思考题 + +这就是这一讲的全部内容了,最后留一道思考题给你: + +在你参与过的项目中,是否也有类似的情况?你能否运用这一讲学习的方法对这些工作进行流程化拆分,并通过复盘找出关键点,对工作重新进行合理地分配呢? + +欢迎你把答案写在留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/18人才培养:怎么把人才转化成实际生产力?.md b/专栏/跟着高手学复盘/18人才培养:怎么把人才转化成实际生产力?.md new file mode 100644 index 0000000..e3517d0 --- /dev/null +++ b/专栏/跟着高手学复盘/18人才培养:怎么把人才转化成实际生产力?.md @@ -0,0 +1,229 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 18 人才培养:怎么把人才转化成实际生产力? + 你好,我是张鹏! + +在上一讲中,我讲述了T公司通过优化流程把人效提升了3倍的案例。但是,当流程优化到极致的时候,制约公司产出的可能就是员工的个人能力了。 + + + +个人能力的提升,首先要靠工作实践,可是如果完全放养,单纯靠员工自己去实践摸索,提升速度太慢,很难跟上竞争激烈的市场环境。所以,很多公司也会给员工安排培训,比如请专家来演讲,鼓励员工买书买课,但是往往钱花了不少,效果却不是很理想。 + +那么,怎么培养人才,才能形成实际的生产力呢?今天我们继续来看T公司的故事。 + +案例背景:规模不够大 + +T公司优化流程的一年之后,流程已经把员工的能力发挥到了极致。这时创始人小Y还想让公司进一步成长,于是找我探讨了2个问题: + + +一个人总是重复做一个层次的类似工作,时间长了是不是就会丧失动力? +现有的团队还能不能进一步提升人效? + + +第一个问题的答案很明显,是的。无论工作一开始有多难,一旦精通了之后还要不停地重复,员工一定会产生倦怠感,这个也符合心流的原理。 + +心理学家米哈里·契克森米哈赖定义心流为一种将个体注意力完全投注在某活动上的感觉;心流产生时同时会有高度的兴奋及充实感。比如一个游戏迷在打游戏的时候会产生极度的专注和投入就是“心流”,因此现代企业都在想办法创造让员工能产生心流的工作体验。其中最核心的是三个机制: + + +清晰的目标; +即时反馈; +稍微超出能力的适度挑战。 + + +很多员工干得好好地就会离职,“手头的工作没啥意思”(缺乏挑战和目标)就是一个重要的原因。 + + + +所以,如果想让员工在工作的时候有更饱满的热情,增加挑战、提升员工能力就成为了必备条件。也就是说,公司高层需要根据业务发展方向,科学地设置员工的职业成长路径: + +首先,让员工成为“1”型人才(专才); + +接着,让员工从“1”型人才(一个点上专业,比如资深XX员)升级为“T”型人才(一专多能,能力覆盖一个业务,比如Team Leader); + +未来,甚至让员工成为“π”型人才(跨职能专家,比如负责一个BU事业部或者分公司)。 + +确定了员工的发展方向之后,我们再看看第二个问题。 + +第二个问题就要复杂一些了。理论上来说,人效的增长是没有尽头的,主要与认知和赋能的边界有关(你可以思考一下人类近代的几次时代变革:蒸汽机时代——电气化时代——信息化时代)。 + +我们真正需要思考的问题是:继续提升人效更重要,还是可复制的高人效更重要。 + +小Y最终选择了可复制的高人效。从行业3倍人效提升到4倍~5倍(4-5倍产值),只会让人觉得你很牛;但是无法复制的话,规模就上不去,对于企业的意义远不如3倍人效规模放大10倍(30倍产值)。 + +复盘详解 + +确定了“可复制的高人效”这个目标之后,小Y带着团队和我针对人才培养的问题做了一次复盘。 + +对比 + +这次复盘的目标是总结绩优员工的经验,复制到整个组织,从而提升整体绩效。于是我们就要看看,绩优员工的标准和普通员工到底有哪些差距。 + +我们还是从工作流程来开始分析: + + +获取客源 +跟进洽谈 +独家委托 +需求确认 +实地带看 +签约交付 +售后服务 + + + + +我们发现,流程中的各个环节分别需要不同的能力: + + +服务能力:对应跟进洽谈、实地带看和售后服务环节,这方面T公司以前就已经做得很棒了。 +获客能力:对应获取客源环节。 +成交能力:对应独家委托、需求确认和签约交付环节。 + + +另外,还有一项能力不体现在某个具体的环节,那就是领导力,也就是带团队的能力。 + + + +我把这些能力依照MECE原则重新用逻辑树梳理了一遍,发现绩优员工和普通员工的能力区别在于: + + +获客能力 +成交能力 +领导力 + + +到这里,能力提升的关键点就定位出来了。 + +逻辑 + +那么,T公司要做的就是帮助员工提升这些能力吗?没这么简单。请注意,T公司真正的问题是如何提高可复制的高人效,而绩效不等于能力。因此,我们需要做进一步的分析: + +绩效好不好,按照“杨三角”(杨国安教授发明的组织发展理论框架)的理论要从三个层面去研究,分别是: + + +员工能力 +员工治理 +员工意愿度 + + +其中,员工意愿度主要靠文化和激励机制,员工治理主要靠流程和规则,员工能力才是依靠培养机制。 + +于是在这次复盘中,我们决定聚焦讨论一个议题:如何提升员工能力。它涉及两个方面,一是确定培养的目标,二是确定培养的计划。 + +一、培养的目标 + +培养的目标对应的是岗位或流程环节的能力模型。我们使用了ASK框架(逻辑树框架)来进行能力建模: + + +素养(Attitude):包括诚信、责任心、服务意识等。 +技能(Skill):包括开拓能力、沟通能力、协调能力、应变能力等。 +知识(Knowledge):包括房产租赁专业知识、相关法律财务知识等。 + + +每个环节需要的能力是不同的,由于商业保密的关系,这里我只拿获客能力举个例子: + + +A:主动性和系统思考等。 +S:文案能力、组织能力和快速链接人的能力等。 +K:房产知识和商业活动主办流程知识等。 + + +针对主动性,我根据T公司的业务实际情况设计了若干考核行动条目,比如: + + +多长时间对已有客户进行电话回访。 +对于客户Call-in的问题,多长时间进行回复。 +多长时间做一轮陌拜等等。 + + +这样按照逻辑树的形式分解下去,就可以落实到具体的行动考核点。 + +用ASK框架完成建模之后,知识(K) 和 技能(S) 的部分可以在内部进行培养,而素养(S)则主要用于人员招聘的时候重点考核(因为这部分培养周期长和难度大)。 + +二、培养的计划 + +怎么根据培养的目标制定培养的计划呢? + +首先,我们把每一个流程中的大环节继续细分,比如获取客源环节又可以细分为两步: + +第一步,总结客户画像。 + +第二步,根据客户通路分为三个选项: + + +拓新客 +渠道合作 +熟客维护 + + +在每个分项上,根据绩优员工的成功经验总结出来形成流程、工具以及方法之后,逻辑树结合流程图就变成可以实操的培训内容了。 + +下面这张图展示了其中微信渠道拓新这一部分的实例: + + + +接下来,为了让员工能够掌握这些技能,我们又设计了培养的考核点。 + + +关于知识部分的,主要是通过培训学习和笔试、面试进行胜任力确认; +关于技能部分,需要通过关键历练(比如通过在微信上找到20个新客户来确认员工已经掌握了“微信拓客”能力)来验证; +关于素养部分,一部分是通过招聘,一部分是通过更长时间(半年以上)的合作过程来观察合不合适。 + + +整个员工能力提升的逻辑,其实是通过逻辑树拆分的过程,一直拆到无法拆分的状态为止。 + +认知 + +T公司在这一次复盘之前,做过无数次绩优员工的内部经验分享会,但是效果都不太好。在这次复盘的过程中,T公司的高层意识到一点,绩优员工零散的分享对于普通员工的帮助并不大。 + +因为场景不可能是一模一样的,如果普通员工无法领悟到每一个场景对应的流程的底层逻辑和需要具备的核心技能和知识,依然无法达成相应结果。 + +另外,人的素养是长年累月形成的,虽然好的企业文化和内部培训可以起到一些作用,但是主要是靠选择合适的人做合适的岗位才可能得到期望中人岗匹配之后的化学效应。 + +整个人才培养的过程其实是有一个基本流程闭环的,可以总结成一个口诀: + +流程节点做比较,差异部分ASK,重点环节萃经验,最后一定要考核。 + + + +规划 + +T公司高层基本了解了培养提升员工能力的思路之后,就开始制定员工的招聘和培养计划。 + +首先,他们成立人才培养小组,花了将近一周的时间详细梳理了颗粒度更细致的工作流程,并对每一个环节,进行了ASK的能力分解,这样就定制出了针对性更强的的岗位要求和培养计划。 + +接着,他们用了一个月的时间,形成了详细的培训内容,并制定培训计划,一开始的培训老师由高管以及绩优员工担当。 + +最后,针对不同的培养方式(知识、技能、素养)制定出考核的方式(笔试面试、关键历练、长时间观察评估)。由于身处服务行业,人是最重要的因素,在我的建议下,由创始人小Y亲自担当这个项目的总负责人,一抓到底。 + +最终结果:复制了高人效 + +经过大概半年的时间之后,T公司增加了一倍的新人,可喜的是,3倍的行业平均人效并没有下降。 + +并且他们规划在接下来的一年里,再增加2倍的人手,这样就几乎可以成为当地这个细分领域中第一梯队的选址服务商了。 + +小结 + +这一讲的内容中的案例,是上一讲的延续,在组织流程改造之后,接下来就是如何提升人效。现在,我们回顾一下重点内容: + + +可以通过对工作流程的环节上,绩优员工与普通员工的表现对比找出表现差距。 +把这些环节对应的能力通过ASK模型梳理出细分的素养、技能和知识,素养用于招聘和选拔,技能和知识用于培养的目标设定。 +把每个环节的颗粒度继续细分,对应给出每个节点对应的流程、工具和方法,达到可以培养的程度。 +最后根据不同的内容范畴,决定考核方式。 + + +思考题 + +这就是这一讲的全部内容了,最后留一道思考题给你: + +在你们公司肯定有一些工作流程(产品研发、市场营销等),你能否根据这个流程上的环节,对比一下绩优员工和普通员工的表现,找出其中一个环节对应能力的ASK模型? + +欢迎你把答案写在评论区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/19预算制定:人头和财务预算到底怎么定?.md b/专栏/跟着高手学复盘/19预算制定:人头和财务预算到底怎么定?.md new file mode 100644 index 0000000..fff6465 --- /dev/null +++ b/专栏/跟着高手学复盘/19预算制定:人头和财务预算到底怎么定?.md @@ -0,0 +1,172 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 19 预算制定:人头和财务预算到底怎么定? + 你好,我是张鹏! + +在上一讲中,我分享了提升人效的案例。在现代企业中,人既是收益,也是成本。人头和财务一样,都需要制定预算。 + +在大多数公司,部门或者项目负责人对预算的理解,可能还停留“多多益善”的程度——预算做得多一点,更加能够确保把事情做好。 + + + +但其实制定预算有两个目的,对于确定性的业务来说,主要是节约成本;对于不确定性的业务来说,主要是控制风险。 + +就我的观察,很多公司的人头和财务预算都有优化的空间。这一讲,我就来聊聊这两种不同导向的预算问题。 + +节约成本 + +对于节约成本来说,重要的是“把钱花在刀刃上”,所以我们首先得知道哪里是“刀刃”(关键点)。 + +案例背景:利润率比对手低 + +在第15讲中,我提到了生产木工机械传送设备的F公司,现在我还是以这家公司为例。 + +他们在做财务盘点的时候发现,他们的利润率比同业竞争对手低不少,在组织结构、流程和人工都差不多的情况下出现这种现象,原因可能有两个: + + +生产效率低,导致营收少、均摊固定成本高。 +成本结构本身出了问题。 + + +为了找准原因对症下药,我帮他们组织了一次复盘。 + +复盘详解 + +对比 + +一开始,F公司把成本划分为浮动成本和固定成本,比如用于制造的原材料属于浮动成本(与生产价值链直接相关的成本),而管理人员的工资属于固定成本。 + +根据F公司的实际情况,我按照MECE原则对成本做了进一步的细分: + + +浮动成本:直接产生价值的成本部分,包括研发、原材料、场租、水电耗材、设备折旧、工厂人工、物流、销售和市场等费用。 +固定成本:包括管理、税务和财务等费用。 + + +然后,我做出一张成本结构表交给F公司的老板,让F公司的财务把近2年的数据填进去,计算每一项的比例,并且把有明显差异的地方(尤其是成本比例上升的地方)标记出来,红色代表做得不好的地方,蓝色代表做得好的地方,如下所示: + + + +(注:表格数据仅用于本课程教学,并非真实数据。) + +有了这张表格,我们就能清楚地看出做得好的地方和做得不好的地方具体的区别在哪里。 + +逻辑 + +我们重点关注红色的部分,也就是做得不好的地方:设备折旧、人工、销售和管理部分的成本上升比较多。 + +对过程追溯之后,我们了解到: + + +设备折旧主要是因为过去一年有设备低价处理售出,这种事情属于偶然性事件。 +工厂人工比例增加是因为为了应急高峰期的用工需求做了很多长期人员备份,但是并没有采用短期合同工导致。 +销售费用比例的增加主要是因为大幅提升了销售提成导致。 +管理费用的增加是因为新增了ERP系统导致。 + + +然后,我们进一步分析发现: + + +设备折旧的提升虽然属于偶然性因素,但是以后在采购设备的时候,需要根据业务长期发展需要把好审核关,不会根据突发性的需求贸然地采购。 +人工需求要更灵活,如果是短期的需求量增加,可以采用短期工策略。 +销售提成的增加并不一定会带来销售额的增长,需要仔细研究市场环境定出销售策略,提成只是销售策略的其中一环。 +管理上ERP的引入,从长期来看反而属于一次性投入,从更长的周期会降低整体的生产成本。 + + +认知 + +制定来年的预算时,一般会参考往年数据以及当下对业务趋势的判断。 + +在以上所有的分析过程中,我们发现: + + +除了设备折旧和ERP引入等一次性分摊费用外,其他都和人工(工厂人工和销售)有关,所以人工是最大的成本节约关键点。 +结合未来一年的原材料供应商的竞争环境以及供应量,原材料成本这一项甚至还有进一步下降的趋势(上图中蓝色部分)。 + + +结合这两个关键点切入进行进一步做假设推导,我们发现: + +一方面,如果工人减少,那么工厂面积也可以减少(多的车间可以租给其他的公司),管理人员同样可以减少,最终导致公司整体的人工成本降低(如下图增强循环1)。 + +另一方面,如果想要降低原材料单价,集中招标采购的效果远远高于与供应商单次询价砍价的效果(如下图增强循环2) + + + +规划 + +根据以上的分析,F公司的老板最终决定从工人数量和原材料集采出发,制定新一年的预算。 + + +根据以往的用工的淡旺季,确定最少工人数量,这些Head Count采用长期合同制进入招聘计划;同时估算用工旺季需要的大致用工人数,提前布局,这些head count采用短期合同进入招聘计划,这个部分大概优化了2%的成本。 +算出旺季最多需要的厂房量,平时用作仓库,多余的部分厂房租给其他公司,这个部分大概优化0.5%的成本。 +重新优化管理结构配备管理人员,加上接下来没有新的信息系统采购计划,这个部分在接下来的一年中预估节约2.5%的成本。 +统计各类别原材料(包含元器件)全年的需求数量,对以往合作的供应商进行信用分级,以全年的需求为标的,邀请信用好的供应商过来投标,通过集采让供应商报价的模式降低全年的原材料单位成本,这个部分预估节约了2%的成本。 + + +最终结果:净利润提升 + +在成本预算上仅仅改变了这两项工作,其他照常,最终导致了接下来这一年的净利率从16%提升到23%,同比增长近50%。 + +对销售额以亿元计算的制造业来说,7%的利润率提升,意味着每1亿元会增加700万的净利润,这是非常可观的。 + +风险控制 + +接下来,我再分享一个关于控制风险的案例。 + +我在做咨询的过程中发现一个很有趣的现象,有预收款业务的公司(比如需要你提前充值的健身房、美容院和教培机构等),按理说现金流应该很好,但是它们却更容易死于现金流断裂。 + +原因在于这些公司提前拿到钱之后,最喜欢干的一件事就是继续扩大规模建新店。正因为手里钱多,所以建新店的时候,并没有极度认真地考虑风险,比如选址、团队能力、服务专业化和当地竞争等非常现实严峻的问题。 + +规模大意味着成本高,但并不见得效率就高(一般来说,快速开店反而低)。结果一旦有突发性的危机(比如新冠疫情)到来,这些公司就会死得比那些现金流看上去没那么宽裕的公司更快。 + +究其本质,就是因为大多数老板或者公司高管没有搞清楚“债”(预收款)、“收入”(已经完成服务的款项)和“利润”(收入-当年的运营成本-折旧-税)之间的关系。 + +现金流充裕是个好事情,一直趴在账面上不动的确是不对的,因为相当于资产收益率ROA很低;但是过渡使用也不对,一旦经营不好或者面临突发情况,企业就会陷入非常危险的境地。 + +那么,手头上到底该保持多少现金会比较好呢? + +案例:现金流管理 + +在第16讲中,我介绍了少儿英语线下培训机构A公司的故事,这一次我还是以它为例。 + +A公司2018年开店规模增加了一倍,2019年初手上还是握着大笔的现金流,准备继续开店,但是当时开的新店就已经呈现经营利润率低的问题了。 + +这时候,A公司到底还应不应该拿着手头上的现金继续开店呢? + +最保守的方案是,保留能够维持把全部缴费学员服务结束的现金流,大约12个月,这几乎相当于当时收到的全部预收款了。 + +最极限的方案是,保留公司能够撑到下一次规模收费的时间段维持公司正常运营所需要的现金流,比如正常时段是1个月,如果在12月份,就需要至少3个月的现金流(1~2月报名学习的人很少)。 + +折中的方案在两者之间。 + +在测算风险的时候,极限情况是必须要考虑的,按我以往的经验,极限情况的2倍是一个通常的做法。所以我们商讨出来方案是,保留6个月的运营费用(3个月的2倍)。除此之外的现金,就可以用于拓店、营销、培训、投资等方面了。 + + + +这个现金流风险控制方案在2020年的新冠疫情期间让A公司在教培行业政策性长期停课的情况,依然抵抗住了风险,并且在疫情期间没有裁员,增加了内部凝聚力。 + +小结 + +这一讲分享了预算制定中两个关键问题,“成本节约”和“风险控制”。现在,我们回顾一下重点内容: + + +在节约成本时候,找到关键成本项非常的重要,可以使用“成本结构表”来列出每一项的数据。 +通过与自身历史数据的纵向对比(如果可能,和同行的横向对比会更好),可以找出可调节的关键成本项。 +根据因果循环图,找出这些成本项之间的关系,决定调整优先级,进而制定调整计划,测算成本节约。 +在控制风险的时候,最核心的问题是根据业务属性找出最极限现金流要求,以及保守现金流要求。健康现金流在二者之间,行业通常的做法是极限现金流的2倍,当然,你可以根据实际情况进行调整。 + + +思考题 + +这就是这一讲的全部内容了,最后留一道思考题给你: + +请你判断一下自己负责的业务是确定性的还是不确定性的,如果是确定性的,还有哪些关键项是可能节约成本的?如果是不确定性的,那么怎么控制风险呢? + +欢迎你把答案写在留言区,和我一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/春节荐书(一)《原则》:在不确定中找到确定.md b/专栏/跟着高手学复盘/春节荐书(一)《原则》:在不确定中找到确定.md new file mode 100644 index 0000000..9f2c48c --- /dev/null +++ b/专栏/跟着高手学复盘/春节荐书(一)《原则》:在不确定中找到确定.md @@ -0,0 +1,130 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 春节荐书(一)《原则》:在不确定中找到确定 + 你好,我是张鹏。 + +今天,我想跟你推荐一本帮助理解复盘的书,《原则》,它的作者是桥水基金的创始人瑞·达利欧。 + +桥水基金在过去20年的时间,创造了超过20%的年均投资回报率,累计盈利450亿美金,远超历史上所有的对冲基金。成功的心法,瑞·达利欧就写在了《原则》这本书里。 + +原则有什么用? + +这本书的一开始有这么一句话:“不管我一生中取得了多大的成功,其主要原因都不是我知道多少事情,而是我知道在无知的情况下自己应该怎么做。” + +达利欧的这句话,恰好引出了为什么在工作和生活中需要“原则”的原因。 + +这句话可能不太好理解。简单来说,就是达利欧在他一生的工作和生活过程中,发现并遵循了一些原则,可以应对不确定性环境产生的各类问题,并通过结果来验证了这些原则,从而更好的迭代优化或者有了新的发现,继续指导工作,周而复始。 + +这是不是和复盘的过程异曲同工?复盘的终极目标也是要找到事物的客观规律,修正我们的认知,通过更加准确的认知,我们也能够应对未来的不确定性。 + +在这里,我想对知识和认知做一个区分。 + +人的知识分为三种,第一种是基因知识,与生俱来,比如饿了要吃,危险来了会躲;第二种是文化知识,需要通过教育熏陶习得;第三种是个体知识,是通过从自己或他人的经历中进行“悟道”,有时候,我们也把这类知识称之为“认知”。 + +人和人最大的区别就是认知的区别,获取认知最重要的方法就是复盘,达利欧所说的原则其实就是他通过复盘得到并经过实践验证的认知。 + + + +最重要的三条原则 + +《原则》这本书分为三个部分,第一个部分是达利欧的个人经历,第二个部分他讲了自己的人生原则,也就是他是如何思考的,第三个部分他讲了自己的工作原则,也就是他和桥水基金是如何工作的。 + +通过之前的分析,我们知道,这些原则是他通过复盘得到的非常有价值的认知。在这本书中,大大小小的原则加起来大概有430条。我在放下书很长时间之后,依然非常清楚地记得其中的三条。 + +1. 世界的本质是一个机器 + +达利欧把目标和结果之间的过程称为“机器”,他说我们要“像操作一部机器那样进行管理以实现目标”。他在书中用自己的切身经历对几十年为周期的经济循环规律做出深度的复盘,用以提升自己对机器的认知,从而更好的优化他后续的工作目标和计划,并取得斐然的成绩。 + +就机器这个比喻,达利欧给了我们做了一个细分: + +你可以把自己想象成一个大机器里运转的小机器,并明白你有能力改变自己的机器,实现更好的结果。通过比较你实现的结果和你当初设定的目标,你就能确定如何改进你的机器。 + +你观察机器有两种方式,一种是你是设计这个机器的人,一种是你是在机器里工作的人。 + +他在书中说道: + + +无论用什么方式,更高层次的角度都有助于你设计目标、建造机器来实现目标。我经常把桥水实际产出与我头脑中期望的产出结果进行对照,然后寻求改进的方法,从而保证了桥水这部机器的成功运转。 + + +所谓更高层次的思考,就是从机器设计者和管理者的角度来思考。 + +那些看似不确定的,偶然的,突发性的事件,放在更大的时空中来看,都有必然规律,而高手总是通过复盘找出这些规律。成功的人能超越自身,客观地看待事物,并主动塑造事物,以达成自己的目标。 + +在达利欧看来,“乔布斯是我们这个时代最伟大,最有偶像意义的塑造者。”乔布斯创造了当代最优美的科技产品,改变了电脑、手机、音乐、电影等一系列行业,创造了世界上最大,最成功的公司。 + +除了乔布斯,达利欧还列举了很多塑造者,比如特斯拉的马斯克,亚马逊的贝佐斯,诺贝尔得主尤努斯,微软创始人比尔·盖茨,还有政治界的丘吉尔、李光耀、邓小平,科学界的爱因斯坦、牛顿、达尔文等等。 + +达利欧认为,这些人的共同特点是:能提出独特和有价值的愿景,并以美好的方式将其实现。 + +他们通常会遭到别人的质疑和反对,但这不会阻碍他们把自己的愿景变成现实。这些塑造者都创造了一个伟大而美好的机器,然后这个机器改变了世界。 + +2. 用五步进程法实现你的人生愿望 + + + +达利欧在书中写道: + + +在你了解怎样建造并操作你的机器以后,你的下一步目标是如何对其进行改进。我们运用五步流程来实现:- + +(1)树立目标;- +(2)发现问题;- +(3)诊断并发现问题根源;- +(4)设计改进方案并解决问题;- + +(5)完成任务。- + +对任何机构而言,取得不同程度的成功都经历了这样的过程。 + + +大家可以看出,目标和结果对比就能发现问题,分析原因就是诊断的过程,找到问题的根源就是升级认知的过程,设计方案并完成任务的过程就是持续优化未来的目标和计划的过程,这和CLAP模型底层逻辑几乎是一模一样。 + +同时,多数长期的目标都不是一蹴而就的,因此会有一个迭代进化的过程,甚至有可能颠覆原来的想法。如果不能持续地做好复盘更新认知,公司可能陷入发展停滞甚至消亡。 + + + +达利欧在书中还说道: + + +当今世界充斥着诸多昙花一现的公司,是因为其最初的优秀品质已经丧失,而领导层又未能充分适应新情况对员工和设计进行改变。也有一些公司能不断改造自己,持续攀上新的高度。 + + +如果你希望自己的事业基业长青,不断勇攀高峰,复盘绝对是你的组织成长利器。 + +3. 阻碍人进步的因素有两个:自负(Ego)和认知盲点(Blind Spot) + +自负和认知盲点是人类的通病,不会因为身处不同国家不同种族或者不同行业而有任何不同;同时,它们也是每个人的成长都要面对的两个最大障碍,我们的大脑决定了这两个障碍的存在。 + +首先,我们看看自负。 + +说到自负,我指的是你大脑中的某些部分,阻止你客观地承认自己的弱点,让你无法想出解决办法。你最深的恐惧和需要就在你大脑中控制情感的部分,你的高等级自我意识是触及不到这部分的,因为自我正确的需求可能比真相更重要。 + +对我们自己的观点,我们喜欢不加审视地相信,我们尤其不喜欢看到自己的错误和弱点,我们本能地倾向于将别人的指正视为对我们的攻击。即便更理智的做法是对别人的指正保持开放,我们也可能会情绪失控。这导致我们做出差劲的决定,学习更少,不能发挥出自己的潜能。 + +其次是认知盲点。 + +每个人都有盲点,如果一个人相信自己无所不能,这就是盲点障碍。但真相是没有人能够凭借一己之力就能看清事实的全部,自然而然,人们就不会承认看不到的事。 + +正如我们都有不同的音域、听觉频率和颜色识别能力,我们看待和理解事物的范围都有不同。比如一些人更擅长看全局,另一些人更擅长看细节;有的人是直线思维,有的人是曲线;有的人有创意但不可靠,其他人可靠但是没创意等等。 + +因为我们思维方式的不同,所以看待周围世界的方式也不一样,仅仅依赖我们的天性,就会忽视我们的弱点,导致跌倒。要么继续下去,要么做出改变。 + +亚里士多德将悲剧定义成:“个人致命缺点带来的可怕结果。”如果能改善这个缺点,结果就会大大好转。 + +达利欧认为,这两个障碍是良好决策的道路上最大的两个拦路虎,而复盘和创造都需要有健康良好的组织和氛围。很多人忽视了对环境的营造和对角色的定向,从而很难进步。 + +如何创造一种开放、平等、多视角多维度的环境和团队,这是成功复盘的前提。在前面的内容中,我在《04 | 制度和文化:怎么营造复盘环境?》和《05 | 三角法:怎么组建复盘团队?》这两讲中做出了具体的说明,希望对你有所帮助。 + +最后的话 + +我在生活和工作中,帮助和见证了很多朋友和团队通过复盘得出极简的本质规律,制定了属于自己的原则,并且在不确定性的环境中恪守原则,从而取得斐然的成就。这也是我推荐《原则》这本书的初衷。 + +如果你还没有读过,不妨读读看;如果你已经读过了,但是希望对这门课程有更深的理解,也可以再读一遍。这本书里的方法论能帮助你在不确定中找到确定,更好地做出决策,实现你未来的目标。 + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/春节荐书(二)《人类简史》:故事的力量.md b/专栏/跟着高手学复盘/春节荐书(二)《人类简史》:故事的力量.md new file mode 100644 index 0000000..f2ca3df --- /dev/null +++ b/专栏/跟着高手学复盘/春节荐书(二)《人类简史》:故事的力量.md @@ -0,0 +1,67 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 春节荐书(二)《人类简史》:故事的力量 + 你好,我是张鹏。 + +今天我想跟你推荐另一本帮助理解复盘的书,《人类简史》,它的作者是尤瓦尔·赫拉利。这位牛津大学的历史学博士,从历史学、生物学、政治学和心理学等角度,全方位地解读了作为一个物种的智人,如何从原始森林中走出,成为这个星球的主宰,接近造物主的历史发展过程。 + +在我看来,如果说《原则》的重点是获取认知的能力,或者说在不确定中寻找确定的洞察力,那么《人类简史》的重点就是传播认知的能力,或者说用故事提升合作效果的说服力。 + +《人类简史》自出版以来,迅速席卷全球,成为现象级的畅销书,连奥巴马、比尔盖茨、扎克伯格、王兴和张一鸣等各路大佬都亲自推荐。到目前为止,它在豆瓣上评分是9.1分,从百度搜索的结果高达1680万条。 + +讲故事的能力 + +这本书的作者赫拉利显然也是一个非常擅长讲故事的人。 + +达尔文在自己的著作《进化论》中,认为从猿到人,最重要的变化是直立行走,以及制造和使用工具。这是从个体的角度给出了人类进化的洞察。 + +赫拉利继承了这个观点,同时也给出了另一个角度,也就是群体角度的洞察:智人(早期人类)的发展还源于团队合作和组织发展,而让智人能够进行合作最关键的就是“讲故事”的能力。 + +在书中提到的人类历史上的四次重大的变革中,讲故事的能力是贯穿始终的,尤其是在认知革命和全球融合这两次变革中,这项能力体现得最为明显。 + +认知革命:故事的力量 + +也许是某次偶然的基因突变,改变了智人脑内的连接方式,使智人能够以更灵活的语言表述大量环境信息,从而能执行更精密的捕猎计划。 + +一开始,智人是以“八卦”的方式进行交流。八卦是社交需求,能够帮助人们交流人际信息,对组织内部的人做出评价,建立稳定紧密的人际组织。 + +八卦能维持的组织规模大概在150人左右(邓巴数),但这还远远不够。真正的决定因素在于,特殊的语言赋予了智人虚构故事的能力。当一个虚构的故事被大批人相信时,哪怕这些人彼此并不认识,也能有效合作,更重要的是只要改变故事,就能迅速改变合作方式。究其本质原因,有时候相信本身比相信的依据更重要。相信推动着智人组织起来去改造社会,这就是故事的力量。 + +在智人走出非洲的时候,需要有两关要过,一是能打败最凶猛的野兽,成为食物链的顶端,二是能够打败在欧洲占霸主地位的个体更加高大强壮的尼安德特人,才能走向欧亚大陆迈向更广阔的世界。这都需要他们有极强的组织能力。 + +在打败这些对手之前,谁都没有成功的经验,到底是什么让智人们愿意去做这个事情呢? + +在真正的战斗开始之前,他们就做过预演,一部分人负责布置陷阱和其他的对己方有利的作战环境,一部分人负责情报收集,一部分人负责武器装备,一部分负责指挥协同,整个团队分工合作,结合反思之前的失败经验,一次次的迭代优化升级打法,直至最终获得胜利。那时候的智人可能并不懂什么叫复盘,但是做事情的思路完全和复盘一样。 + +其中,我想智人们在战斗开始之前,一定思考过一个问题:“如果我们要打败敌人,应该怎么做?” + +这种思路叫“假设”,这个方法如今依然在用,并且通过实践来验证“假设”是否正确。这种“假设验证”的方法特别适合用来做创新,即去规划从来没有做到过的事情。在第10讲,我会给你详细讲述。 + +全球融合:三个大故事 + +公元前一万年的地球上有数千个文明,而当今全球只有230个国家,而且全球经济、法律和文化有同化趋势,是什么推动了这个趋势? + +赫拉利在《人类简史》中给出的答案是三个故事:货币、帝国和宗教。 + +货币的本质是信任,从金属货币、纸质货币到如今的比特币,只要在故事系统内的人都相信货币可以换取你想要的货品,那么货币就有价值。货币系统在构建高效的大规模陌生人合作网络方面,可能是目前其他任何系统都望尘莫及的。 + +另外两个故事是帝国和宗教。帝国依靠暴力,宗教依靠传教,二者往往相辅相成。帝国给予宗教以土壤和后盾,宗教给予帝国以超人类的合法性。这二者的存在统一了数以百万记的民众思想和意识形态,形成更大规模的合作。 + +货币、帝国和宗教,完全是虚构出来,却是在全球范围内得到最广泛信任的三个故事。他们并没有以任何物理的形式存在,却真实存在于每一个人的心中。并且所有人都愿意按照三者约定的规则行事。这种现象从我们有意识和概念能力的时候就被人教导,却几乎没有被反思过。《人类简史》中提出来的时候,我开始反思:为什么这么大的“故事”会让几千年来的数以百亿的人们都愿意相信呢? + +《资本论》中有一句话叫做“生产力决定生产关系”。当生产力的大幅提升的时候,需要生产关系能够协同更大范围的群体合作,也就需要构建更宏伟的故事让大家达成共识。在《人类简史》中,赫拉利给了我们一个更宏伟的视角,放在民族、国家等层面去思考如何构建故事,组织协同,非常值得思考和学习。 + +最后的话 + +《人类简史》中,“故事”是推动整个人类发展的主要力量,故事的本质是为了让人们达成共识,组织起来去完成更复杂和更宏大的目标和愿景。 + +在我们的实际工作生活中,经常发现说服一个人接受自己的观点是非常难的,而《人类简史》中把整个人类发展史都建立在“讲故事”的核心能力上,这种能力,在《乔布斯传》中叫“现实扭曲力场”,很多杰出的行业领袖都有这种能力。 + +希望你也能拥有这种能力! + + + + \ No newline at end of file diff --git a/专栏/跟着高手学复盘/结束语复盘,见天地见众生见自己.md b/专栏/跟着高手学复盘/结束语复盘,见天地见众生见自己.md new file mode 100644 index 0000000..99aca59 --- /dev/null +++ b/专栏/跟着高手学复盘/结束语复盘,见天地见众生见自己.md @@ -0,0 +1,102 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 复盘,见天地见众生见自己 + 你好,我是张鹏! + +《跟着高手学复盘》到这里就结束了,感谢你的一路坚持! + +这门课有20讲,对你来说可能包括很多全新知识和案例,这也许让你感到学习压力有点大,所以能坚持到最后很不容易,让我为你点赞! + +学完这门课,相信你已经熟悉了复盘的方法、工具和流程,但是我还是想提醒你,这只是做复盘的开始。复盘是一件看起来简单、按流程做起来也不难,但是想得出真正有价值的结论却非常考验水平的事情。 + +我从事咨询工作的这些年,去过不同的城市,了解过不同的行业,见过不同的企业和企业创始团队。我发现,成功的企业都有两大共性: + + +极强的复盘能力 +超级执行力 + + +执行力代表着企业能够把事情搞定,而复盘能力则意味着企业能够在成长过程中形成越来越强的深度洞察力。有了洞察力,就能够根据事物规律和人性来做针对性更强的战略制定和组织发展工作。 + +在VUCA时代,企业比的不仅仅是质量、成本和效率,而是对需求的深度洞察和满足能力,这些都离不开复盘。 + +复盘的挑战 + +复盘是一个在纷繁复杂的问题中抽丝剥茧的过程,最终得出的有效结论往往就是几句话,有时候甚至只有一句话,看上去似乎没有你想的那么高大上。但是,只要有作用,能够推动业务,那就是有价值的规律。 + +某个做咨询服务的公司,在招聘的时候往往会看中一个很关键的条件,那就是“颜值高”。因为他们发现在举办活动的时候,“颜值高”是自带引流能力的,很多潜在客户会主动加这些高颜值小姐姐的微信。毕竟“爱美之心”是人性啊! + +有时候,复盘的结论依然只是理论上正确的假设,可能需要你做取舍。如果放弃的是现实可得的利益,取得的只是未经证实的利益,你还敢不敢做这个决定? + +某个高端别墅装饰的公司,最核心的竞争力之一是设计师,但是高级设计师把控了客户的多半主材供应。复盘时我们得出的结论是,需要取缔设计师对主材的把控,收归公司统一调配。 + +这会给公司带来更大的收益,但是也会打破设计师团队和公司原有的利益平衡,甚至可能会导致设计师大量离职。最终,公司高层还是下了这个决心,结果真的有很多设计师离职了,当年的业绩不但没有增长,反而成就了市场上大量的竞争对手。 + +幸运的是,之后的三年,这家公司经过一系列的改革,取得了前所未有的业绩增长。如果当时你是决策人,你会有这样的铁腕手段吗? + +有时候,复盘的结论证明你是错的,你过去的努力和认知完全不对,你真的能接受吗? + +某个公司在创业之初就高光满满,“改变世界”被每一个员工挂在嘴边,产品还没出来,就拿了中国创业史上最多的第一轮投资。结果在创业两年后,发现原有的C2C模式完全不符合市场的现实情况,后来复盘,发现公司最大的错误是创始人的战略判断失误,再后来通过无数次的复盘以及接近2年的内部创业孵化才找到新的增长点。 + +如果你是创始人,在复盘的时候,团队指出公司最大的失败就是创始人的战略误判,你真的能接受这样的结论吗? + +个人成长 + +在前面的课程中,很多同学留言问我,对于个人成长和中长期的项目复盘,有没有更简单的做法? + +关于个人成长,我给你分享一张图。 + + + +首先是要找到你的热爱。任何职业上的成就都是一个马拉松式的长跑过程,不可能一蹴而就。知识和技能都是可以在工作过程中学习到的,而热爱很难。如果没有热爱,工作过程中的各种困难、煎熬迟早会打败你。即使你还在工作,可能已经如同行尸走肉,缺乏生机与能量。 + +其次是要找到你擅长的领域。擅长需要长期的磨炼,“畏难”是最大的障碍。大多数人天生会对不确定的事情感到恐惧,但是你拥有了良好的逻辑思维能力,就可以把一个复杂的事情分解成若干步骤和组成要素,制定计划总会实现目标。这也应了那句古话:“不积跬步,无以至千里。” + +最后是你所做的事情要被市场需要。这需要深度的洞察,深度洞察多数时候都非常困难,但如果你本身是用户,那么这种洞察力就会极其敏锐。比如说你是某类产品的重度消费者,你就会非常敏锐地说出自己的痛点、产品的特性、性价比、使用场景和购买心理。生活本身就会带给你丰富的训练场景和数据。 + +三者的交集就是你长期的事业聚焦所在,热爱生活吧,朋友! + +项目复盘 + +关于长期项目的复盘,如果你还不能得出“规律性”的复盘结论。那么,做完逻辑分析后能得到三个结论: + + +什么该接着干(Continue doing) +什么该停下(Stop Doing) +什么该赶紧开始(Start to do) + + +这也是非常有价值的结论。等到项目做多了,对做过的项目集合做大的整合复盘的时候,你也会自然而言地洞见规律。 + +我在实际案例篇的导读中说过,做咨询就好比当企业医生: + +一开始看到一些企业问题,也认为是个案,绞尽脑汁想办法帮助客户想方案;等到积累足够多案例的时候,了解完企业的需求,心里基本就清楚了客户的问题所在,至于业务调研和企业诊断的时候,相当于通过事实和数据来验证当初的判断。 + +这就像你去看医生,有经验的医生和你沟通完,看你的状态心理基本就有谱了。而中医的“望闻问切”还是西医的“验血验尿透视CT”等也是验证的过程。 + +我相信,当你成为业务专家的时候,也会具备这样的能力。复盘的框架再好、工具再多,也取代不了你自己对业务本身的理解。 + +复盘的关键 + +复盘的关键,在我看来,主要是两点。 + +第一个是频率。我见过不同公司不同层级的员工。有些员工一开始看上去弱弱的,但是持续地每天、每周、每月复盘,一开始只是很简单地比较目标结果、逻辑分析、优化规划(洞察规律和更新认知这一块他们做得并不好);但是他们长年累月地坚持下来,等到再次见面的时候,我发现他们简直像换了个人,精神状态、思维、行为习惯和工作结果和以前截然不同。 + +第二个是氛围。我见过太多的复盘说好了不追责、不甩锅,一到了关键环节,依然改不了,大家争得脸红脖子粗的。一开始,我作为外部顾问也会感觉很有无力感。但是持续地讲、持续地反馈、持续地使用教练技术,效果和氛围就越来越好,复盘也有了高价值的输出。这也是很多公司把复盘纳入到企业文化的范畴的原因(不需要制度约束就能成为每个人的行事准则)。 + +最后的话 + +成长的快乐就像爬山。一开始,爬山开始的快乐是爬上山顶俯瞰更广阔的的天地众生,到后来,爬山本身才是最大的快乐,因为在这个过程中,你遇见了更好的自己。 + +复盘无论对团队还是对个人,都是帮助和记录成长的工具。祝你在复盘的过程中,遇见更好的自己! + + + +《跟着高手学复盘》课程结束了,这里有一份毕业问卷,题目不多,希望你能花两分钟填一下。十分期待能听到你说一说,你对这个课程的想法和建议。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/00开篇词你为什么应该学好软件工程?.md b/专栏/软件工程之美/00开篇词你为什么应该学好软件工程?.md new file mode 100644 index 0000000..b025647 --- /dev/null +++ b/专栏/软件工程之美/00开篇词你为什么应该学好软件工程?.md @@ -0,0 +1,139 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 开篇词 你为什么应该学好软件工程? + 你好,我是宝玉,欢迎加入我的专栏,和我一起开始软件工程的学习之旅。 + +和很多人一样,我的职业生涯是从一个自学编程的“野路子”程序员开始的。1999 年,我考入西北工业大学工程力学专业,但是却对编程很感兴趣。大一的时候自学网页编程,大二开始去学校网络中心兼职,同时在外面接了很多做网站的私活。 + +那时,虽然我的编程水平提升特别快,但是因为完全是自学,跟其他计算机科班的程序员一比,多少有点自卑感,觉得好像差点啥!在实际工作中,遇到具体的问题,我只能见招拆招,一个一个地解决。 + +当然,因为一开始我无法从系统层面整体看事情,所以虽然问题解决了,但也总有一种疲于奔命的感觉。我曾经遇到的问题,你肯定也不陌生,比如: + + +开发时没有分析没有设计,上手就写,后期难维护,加班熬夜去填“坑”; + +缺少理论指导,遇到新项目不能举一反三,工作很平庸; + +遇到需求变更这种事,除了抱怨两句客户,只能闷头做,无力反抗; + +做项目没计划性,想到哪做到哪,总是延期,比其他同事做的慢; + +不知道如何与团队协作,职业发展遇到瓶颈,无法得到晋升。 + + +那时候我不知道啥是正规做法,主要靠自己摸索。也特别困惑:科班出身的程序员是否与我有同样问题?像微软、阿里等这些大厂的程序员,他们又是怎样协调完成好那么庞大的项目?我这个“野路子”程序员面临的问题,他们又是怎么分工协作解决的? + +2002 年初,我有幸转了专业,成为了中国第一批软件工程专业的学生,有机会系统地学习软件工程的理论知识,这解开了我的很多困惑。 + +软件工程学让我知道,软件项目的开发其实是一个工程,整个开发过程是可以有效组织起来的;对于开发过程的各个阶段,已经有很多解决问题的最佳实践,有很多方法来帮助我们高效完成任务;我们还可以借助工具来协助管理,提升开发效率。 + +如果说以前自学编程时,我还是停留在学习各种编程方法(术)上面,那软件工程开始让我主动去思考这些“术”后面的“道”,去思考软件项目中各种问题背后的原因,以及各种方法后面的理论指导。 + +这种对“道”的思考,逐步影响了我思维方式,让我从单一的程序思维上升到系统的工程思维去看日常的问题;同时让我形成了一套自己对于软件开发和项目管理的方法论,能举一反三,指导我去灵活运用各种方法,或者根据项目特点创造合适的解决方法。 + +当然,软件工程学的价值不仅于此。有人说程序员是吃青春饭的,因为计算机技术更新太快,年纪大了学习能力下降,就很难跟得上了。于是就有人很焦虑,会关心未来技术发展趋势如何?我怎么才能跟得上这些技术变化? + +亚马逊的创始人杰夫·贝索斯(Jeff Bezos)曾经在一次演讲中说:“人们经常问我,未来 10 年什么会被改变?我觉得这个问题很有意思,但也很普通。从来没有人问我,未来 10 年,什么不会变?” + +这个回答同样适用于软件开发领域。在软件开发领域,有哪些知识十年前很重要,现在仍然重要,未来可能同样重要? + +其实仔细分析,这些知识不外乎:数据结构、算法、面向对象思想、设计模式、软件工程。如果范围不局限于程序开发,还要算上测试、产品设计、项目管理、运维这些岗位。 + +你会发现,无论你是什么岗位,只要你从事软件开发相关领域,都绕不开“软件工程”,因为现代软件项目开发,多多少少都离不开软件工程知识的应用。 + +想象下在日常工作中,不管你用什么开发语言,不管是前端和后端: + + +你接到一个开发任务,如果想开发出客户想要的功能,你是不是先要做需求分析; + +你接手一个复杂的、大的功能模块,是不是先要做设计,才能把复杂的拆成简单的,才能让大家一起分工去开发; + +你完成一个功能模块,如果要保证质量,是不是需要写一些测试代码,还要做一些功能测试; + +还有日常用的那些工具,像源代码管理、Bug 跟踪。 + + +而这些内容,都是软件工程相关的知识,和你用什么语言无关。十几年前我开始工作时就在用这些知识,现在还是在用这些知识,未来这些知识还不会过时。 + +换言之,这就是经典的价值,为什么说我们要学经典,因为经典就是这个行业最为本质的东西。你顺着这个逻辑想,就知道为什么大学的计算机专业要设计数据结构、算法、操作系统、软件工程这样的课程了。 + +技术更新迭代速度确实很快,难以把握,更难以预测,但是软件开发背后的逻辑却万变不离其宗。 + +你只有掌握了这些逻辑,才能步步为营,不被快速发展的软件开发行业所淘汰。因为你脑袋里装有软件开发的战略,相对于赤手空拳、盲打莽撞的人来说,你更能在未来获得先机。 + +我经常会跟身边的朋友“安利”软件工程的重要性,但是往往都没有下文。究其原因,主要是传统的软件工程教学方法出了问题,各个知识点过于偏理论,难以和实际项目的应用联系起来,理解起来生涩乏味。导致有人误以为软件工程是枯燥、无用的。 + +回想当初我在学习软件工程课程时,并没有觉得特别枯燥,主要归功于三点: + + +我学习前已经有项目实践经验,所以学习时,很容易能将理论和项目经历串起来; + +我在以前项目中有很多困惑,带着问题再去学习,这样效率更高; + +即学即用,获得正反馈。我不仅会把软件工程的知识应用在工作中,还会把日常生活中的问题当成一个项目去思考,不停练习和获得正反馈。 + + +我一直在思索,怎么让软件工程的学习,既不那么枯燥无味,同时,也具有实用性,即学即用,可以用来指引帮助我们来解决问题。 + +这样一直到 2015 年,我到美国攻读计算机的硕士学位,发现美国的计算机教育确实有可取之处,例如学校会聘请企业的专家作为兼职讲师,让学生有机会了解业界最前沿的技术趋势。 + +这些有丰富项目经验的企业专家讲师在讲课时,总能把一些知识点和鲜活的案例结合起来,和学生一起探讨这些知识点背后的历史和逻辑,让软件工程学变得易学、实用。 + +在美国读书的经历给了我很大启发,软件工程的学习,也可以不那么枯燥。恰好我的经历也比较特殊: + + +从自学编程的程序员到软件工程专业科班毕业;从技术开发到在微软飞信做项目管理;从程序员到技术总监;从几个人小团队到几千人的大厂;从国内公司到美国公司;从个人小项目到几千万用户的大项目;从传统瀑布模型到最新的敏捷开发。 + + +这些丰富的经历,帮助我更好地理解了软件工程的知识,也知道如何应用它,可以发挥最大的效用。 + +因此,在这个专栏中,我会结合自身在软件开发中的经历,将软件工程中的知识点和我所看到的国内外前沿的、典型的项目案例结合起来讲解,也会和你一起分享我对这些知识背后的思考。和你一起去软件工程学中,寻找软件项目中问题的答案。 + +我希望最终,你能把软件工程知识和项目经验有机地结合起来,转换成你自身能力的一部分。 + +另外,在实际软件项目开发中,离不开各种工具的使用,像源代码管理、持续集成、看板、监控报警等,帮助我们更好地协作、规范项目流程、上线维护。 + +在本专栏,我也会在穿插着介绍各种工具的用法,有哪些价值,让你在了解后能很快应用到项目中,达到即学即用的效果,提高项目开发效率、规范项目流程。 + +我们的专栏会从“道、术、器”三个维度来讲解软件工程的知识内容。 + + +“器”就是软件工程中的各种工具。 + +“术”就是软件工程中的各种方法。例如如何做需求分析?如何对需求变更做变更管理? + +“道”就是软件工程知识的核心思想、本质规律。例如为什么要有需求分析?需求变更产生的深层次原因是什么?项目中决策的依据是什么? + + +在专栏的模块设置上,我将它分成了三大部分。 + +1. 基础理论 + +从宏观的角度建立起软件工程的知识结构,展现软件工程学的全景图,让你掌握从软件工程的基础概念到主流的软件过程方法论。我会帮你开始思维上的转变,去尝试用工程化的思维模式,去分析和解决工作和生活中的问题。 + +2. 项目过程 + +我会按照软件生命周期,把知识点拆成:规划、需求分析、设计、编码、测试、运行维护这六个阶段,然后带着你一起去了解每个阶段要侧重做哪些事;分析每个阶段常见的问题,找到解决方法;了解各个阶段有哪些工具可以对项目有帮助,从而学会应用它们。 + +3. 案例分析 + +在这个模块中,我会带你一起去看看这些大公司是怎么应用软件工程的。之前你可能会有疑惑,认为软件工程学很虚,我们小公司用不着,或者不知道怎么在实际项目中应用软件工程。 + +其实软件工程的思想是润物细无声,包括微软、谷歌、华为、阿里巴巴这样的大公司早已经深得其精髓,把它用得炉火纯青了。 + +你的公司,你遇到的大部分项目问题,都可以回到软件工程的逻辑里来解决。我会给你分享我看到的经典的软件工程案例,让你能够通过综合案例,把前面的知识融会贯通,并逐步内化为自己的基础能力。 + +简单来说,我希望通过这个专栏,你可以从知到行,打好基本功,掌握软件工程学中涉及的方法和工具,学会举一反三,在软件项目的开发和管理过程中,能运用自如;也希望软件工程的思维,可以让你脱离技术的拘泥,有更高的格局和视角去看待工作和生活中的问题。 + +最后,也希望软件工程学这门基础学科,真正成为武装你职业上升的盔甲。无论你想走技术路线,还是转向做管理,都能从赤身肉搏、苦钻技术却不得法的“野路子”,变得行有章法,在未来软件的快速革新稳步前行。 + +如果你在专栏的学习过程中,遇到任何问题,或者有什么想法,欢迎留言与我交流。相信这段学习之旅,你我都将收获满满。 + +好,那就让我们开始吧! + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/01到底应该怎样理解软件工程?.md b/专栏/软件工程之美/01到底应该怎样理解软件工程?.md new file mode 100644 index 0000000..de17088 --- /dev/null +++ b/专栏/软件工程之美/01到底应该怎样理解软件工程?.md @@ -0,0 +1,133 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 到底应该怎样理解软件工程? + 你好,我是宝玉,我今天分享的主题是:什么是软件工程? + +当有人问你什么是软件工程,你会想到什么? + +也许你会想到软件,会想到建筑工程,也许对此还没有什么概念。不要紧,在回答这个问题前,我们先来看看软件工程和我们的日常生活有哪些关系。 + +作为一个程序员,你的日常大致是这样的:早上被手机的闹钟叫醒,然后坐地铁去上班。在路上你会打开微信和朋友闲聊几句,刷刷微博,还会用 Youtube 看看视频充充电;上班的时候,你打开 Mac 电脑,用 VS Code 写程序,用 Chrome 看网页,下班后再玩几局守望先锋游戏。 + +你发现了没?在这一天的日常生活里面,软件无处不在。手机操作系统软件、闹钟 App、微信、微博、Youtube、Mac OS、VS Code、Chrome、守望先锋,这些都是软件,你的日常生活已经和这些软件密不可分。 + +软件是怎么被创造出来的? + +那么你有没有想过,这些软件是怎么来的呢? + +首先,它们的诞生都是有人想要造一个东西。这其中的很多故事你都耳熟能详:张小龙创造微信、乔布斯创造 iOS、暴雪想做一款不一样的射击游戏……他们首先有着这样的意图,然后,他们立项做这样的产品。 + +每一款软件项目背后,都有很多人在参与。你无法想象微信这样复杂的 App 靠张小龙自己可以开发出来;你也不会相信 iOS 是乔布斯一个人设计出来的;像守望先锋这种游戏的背后,有成百上千的游戏策划、美术设计、程序开发人员。 + +从立项到第一个版本的发布,每个成功的软件都需要有计划、有步骤地进行,什么时候发布第一个版本、第一个版本有什么样的功能、什么时候发布第二个版本、第二个版本有哪些地方要改进,这些都是研发过程中需要考虑的问题。 + +比如,守望先锋是在 2013 年立项,他们计划做一个基于职业的 MMO 游戏,他们先花了几个月的时间做了第一个 Demo 演示,只有四个英雄和一个张地图。 + + + +然后,他们要在 2014 年的“暴雪嘉年华”发布这个游戏的试玩版,包含 12 个英雄和 4 张地图。 + + + +最终这个游戏在 2016 年 5 月正式发布了。这样从立项一步步到最终发布,历时三年,中间经过了诸多环节。 + +类似的例子还有微信,2010 年 11 月 20 日立项,2011 年 1 月 21 日第一版上线,当时的功能还非常简陋,只能发送文本消息和照片。之后才是一个个版本的迭代,直到你现在看到的微信。 + +像这种有人参与、有计划、有步骤地造一件产品,我们通常称为“工程”。 + +所有工程的本质,就是要做出有用的产品,比如造房子的建筑工程、造火箭的航天工程。像网红“手工耿”一样专搞无用发明的情况,我们是不能称为“工程”的。 + +在软件领域,对应的就是“软件工程”,这些我们日常使用的软件背后,都是基于软件工程的方法在开发、运行和维护的。 + +如何摆脱“软件危机”? + +也许有人会认为,不用软件工程,我一样可以开发软件出来。这确实没有错,因为如果一个人没有学过建筑工程,他也是可以造一个房子出来,只是造出来大概会是这个样子: + + + +我们知道,不按照建筑工程造房子,是会出事故甚至死人的。而在软件工程的历史上,也是真的有造成过很大损失、甚至还有人为之丧命的事件存在。 + +OS/360 操作系统是上世纪 60 年代最复杂的软件系统之一,也是第一个超大型的软件项目,一共有 1000 名左右的程序员参与了项目的研发,花费了 5000 个人年,最终无法运行。项目负责人佛瑞德·布鲁克斯后来写了一本软件工程的经典书籍《人月神话》,承认在他管理这个项目的时候,犯了很多错误,造成了价值数百万美元的损失。 + +如果是说 OS/360 还只是造成了经济损失的话,Therac-25 事件就是真的导致了人员死亡。Therac-25 是加拿大原子能有限公司(AECL)所生产的放射线疗法机器,在 1985 年到 1987 年之间,在美国及加拿大,至少有六起和 Therac-25 相关的医疗事故是因为程序 bug,导致部分病患受到比正常剂量高一百倍的辐射,因而造成患者重伤甚至死亡。 + +发生这些惨痛的事,原因却并不难理解。 + +在计算机刚发明出来的时候,计算机的能力非常有限,只能接收简单的指令和运算,不需要软件工程也可以开发出简单的软件。 + +但是,当软件的规模越来越大,复杂度不断增加,软件项目开发维护过程中的问题就逐步暴露出来:软件产品质量低劣、软件维护工作量大、成本不断上升、进度不可控、程序人员无限度地增加。所以在 60 年代,“软件危机”的概念被提出来。 + +为了摆脱软件危机,1968 年秋季,北大西洋公约组织的科技委员会召集了近 50 名一流的编程人员、计算机科学家和工业界巨头,讨论和制定对策。在那次会议上第一次提出了“软件工程”(software engineering)这个概念。 + +从此诞生了一门新兴的工程学科:软件工程,它是为研究和克服软件危机而生。 + +在这次会议上,同时也提出了“软件工程”的定义: + + +为了经济地获得在真实机器上可靠工作的软件而制定和使用的合理工程原则。 + +(Software engineering is the establishment and use of sound engineering principles in order to obtain economically software that is reliable and works efficiently on real machines.) + + +1993 年,电气电子工程师学会(IEEE)给出了一个更加综合的定义: + + +将系统化的、规范的、可度量的方法用于软件的开发、运行和维护的过程,即将工程化应用于软件开发中。 + +(Software Engineering: (1) The application of a systematic, disciplined, quantifiable approach to the development, operation, and maintenance of software; that is, the application of engineering to software. (2) The study of approaches as in (1).) + + +如果你去搜索一下“软件工程定义”,你还能找到很多其他定义,这里就不一一列举。我们没必要花太多时间在这些字面解释上,关键是要抓住这些定义的本质:就是要用工程化方法去规范软件开发,让项目可以按时完成、成本可控、质量有保证。 + +软件工程的演化史 + +对比传统的工程学科,和软件工程最接近的就是建筑工程了。设想一下建一座房子:首先要先立项、设定预算,然后画设计图,再是施工,施工完成后,由专业人士进行质量检查,质检合格后入住。 + +开发软件本质上也是像盖房子一样,是从无到有创造的过程。工程化的方式,就是你分步骤(过程),采用科学的方法,借助工具来做产品。 + +于是参考建筑工程,整个软件开发过程也被分成了几个阶段:需求定义与分析、设计、实现、测试、交付和维护,这也就是我们常说的软件项目生命周期。 + +当然,各个阶段都会有人的参与,于是产生了软件项目里的各种角色:项目经理、产品经理、架构师、程序员、测试工程师、运维工程师。而对这整个过程的管理,我们通常称之为“项目管理”。 + +同时,也很自然就衍生出一套最基础的过程模型:瀑布模型。 + + + +瀑布模型的诞生,在当时是有非常重大的意义的,让软件开发从无序到有序,让大家更好的分工协作,同时每个阶段又衍生出各自的方法学和工具,例如需求分析、软件测试等等。 + +然而瀑布的特性决定了它只能从上往下流,而且从上到下走完整个周期很长,所以一旦出现了需求的变更,将会非常痛苦,很多事情需要重头再来。 + +于是基于瀑布模型,又衍生出 V 模型、原型设计、增量模型、螺旋模型等模型,试图改善瀑布模型存在的一些缺陷。这些改进模型的发展趋势上就是缩短项目周期,快速迭代。 + +这样到了 90 年代,各种轻量级开发方法例如 Scrum、极限编程等也不断被提出。到了 2001 年,这些轻量级开发方法一起组成了敏捷联盟,其后敏捷开发如同星星之火,逐渐形成燎原之势。 + + + +近些年,云计算、微服务这些新技术的产生,也对软件工程产生了影响。云服务让分工更细,很多企业可以将运维、服务器维护、DBA、甚至某些独立服务交给云服务商;微服务让大团队变成小团队,每个小团队可以更专注于细分领域,减少相互之间的依赖。 + +一个公式 + +当你大致了解整个软件工程的演变发展史,你会发现,软件工程的知识,都是建立在软件项目的过程,或者说软件项目生命周期之上的。 + +基于软件过程,我们有了角色分工,有了对过程的管理和工具,对过程中每个阶段细分的方法学和工具。 + +现在,如果再回头看看我们的问题“什么是软件工程?”其实可以总结为:软件工程就是用工程化的方法来开发维护软件。也可以说软件工程就是用一定的过程,采用科学的方法,借助工具来开发软件。 + +如果用一个简单的公式表达,那就是:软件工程 = 过程 + 方法 + 工具。 + +总结 + +今天,我和你一起追溯了一下软件工程的起源,你知道了软件工程学科的产生,就是为了解决软件危机。也见证了整个软件工程的演变历史,从瀑布模型到敏捷开发。 + +从 1968 年提出软件工程到现在,正好是 50 年。在 2002 年,我最开始学软件工程专业的时候,还只有瀑布模型、需求分析、系统设计等这些传统软件工程内容,但是经过十几年的发展,在软件项目中,敏捷开发、持续集成、微服务等这些新兴内容已经开始在软件项目中占据越来越重要的位置。 + +可以预见,未来软件工程领域还会有新的概念、新的知识诞生。但是万变不离其宗,只要你抓住软件工程的本质,无论将来如何变化,你总能很快掌握新的知识内容。 + +而软件工程的核心,就是围绕软件项目开发,对开发过程的组织,对方法的运用,对工具的使用。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/02工程思维:把每件事都当作一个项目来推进.md b/专栏/软件工程之美/02工程思维:把每件事都当作一个项目来推进.md new file mode 100644 index 0000000..69cd6d6 --- /dev/null +++ b/专栏/软件工程之美/02工程思维:把每件事都当作一个项目来推进.md @@ -0,0 +1,137 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 工程思维:把每件事都当作一个项目来推进 + 你好,我是宝玉。我今天分享的主题是:掌握工程思维,把每件事都当作一个工程项目来推进。 + +我大学学的是软件工程专业,毕业十多年后再回顾当年学的专业课,好多专业概念已经记忆模糊,唯有对一位老师的教诲记忆深刻,对我毕业后的职业生涯影响深远: + +软件工程是一门用工程化方法解决软件项目问题的学科,其本质也是一门工程学科,这门课的知识在学完后,不仅可以应用在软件项目中,还可以应用于日常生活中遇到的一些问题,Everything is a project。 + +这句话对我影响很大。我真的开始在日常生活中尝试应用“Everything is a project”的概念,小到做作业,大到完成工作中的复杂项目。 + +解决这些问题的方式,就是参考软件生命周期和瀑布模型,把一件事情分成几个阶段:分析、设计、实施、测试、完成,然后制定相应的计划。这种方法不仅非常有效,让我的做事效率大幅提高,而且让我在看待事情上,能够更全面地、站在更高的角度去思考。 + +2010 年在上海的时候,我机缘巧合参加了一个关于产品设计与用户体验的线下活动,我可能是与会人员中,为数不多的非专业产品设计的同学。 + +在活动中组织者安排了一个游戏环节,每 5 个或 6 个人分成一个小组,来设计一个给老年人使用的手机,限时 30 分钟。完成后,每组选一个人上台花 5 分钟展示作品,最后投票选出做得最好的一组。 + +我的第一反应就是把它当作一个项目,于是快速地拟定了如下计划。 + + +0~10 分钟(分析):头脑风暴,收集想法。 + +11~15 分钟(设计):根据头脑风暴结果,确定最终设计。 + +16~25 分钟(开发):将想法画在纸上。 + +26~30 分钟(发布):完善结果,准备展示。 + + +这个计划小组成员都很认可,于是我们严格按照这个计划进行手机的设计。同时我观察了一下其他组的情况,大家都在热火朝天地讨论各种想法,似乎没有意识到时间其实是有限的。 + +轮到演示的时候,我们组毫无争议地拿到了第一,因为我们不仅准备充分,而且设计的手机功能完整,而其他很多组甚至还没来得及把想法完整地画下来。 + +什么是工程方法? + +后来我才了解到,这种有目的、有计划、有步骤地解决问题的方法就是工程方法。工程方法不是软件工程独有的,几乎所有工程类别都可能会应用,例如建筑工程、电子工程等,只不过步骤可能略有不同。 + + + +工程方法通常会分成六个阶段:想法、概念、计划、设计、开发和发布。 + + +想法:想法阶段通常是想要解决问题。最开始问题通常是模糊的,所以需要清晰地定义好问题,研究其可行性,检查是否有可行的解决方案。 + +概念:概念阶段就是用图纸、草图、模型等方式,提出一些概念性的解决方案。这些方案可能有多个,最终会确定一个解决方案。 + +计划:计划阶段是关于如何实施的计划,通常会包含人员、任务、任务持续时间、任务的依赖关系,以及完成项目所需要的预算。 + +设计:设计阶段就是要针对产品需求,将解决方案进一步细化,设计整体架构和划分功能模块,作为分工合作和开发实施的一个依据和参考。 + +开发:开发阶段就是根据设计方案,将解决方案构建实施。开发阶段通常是一个迭代的过程,这个阶段通常会有构建、测试、调试和重新设计的迭代。 + +发布:将最终结果包括文档发布。 + + +如果你用这六个或者其中几个阶段对照日常工作和生活中遇到的问题,会发现绝大部分问题都可以看成一个项目,并且拆分成几个阶段,按照计划一步步完成。 + +站在整体而非局部去看问题 + +可能会有人说:“我不用这种工程方法去做事,一样可以做成呀,并没有什么区别。”确实,做一件事有很多种方式,但用工程方法去处理事情,有两点好处: + + +有一个被有效论证过的方法论指导你,可以帮助你提高成功概率,也可以提高效率。 + +当你用工程方法去思考的时候,你会更多的站在整体而非局部去思考,更有大局观。 + + +前面提到的“设计一个老年机”的游戏就是个很好的例子,后来我在不同场合、不同人群中都组织过这个游戏,无论我如何强调时间限制(30 分钟)和产出(必须要演示结果),绝大部分人还是会把时间和注意力放在各种稀奇古怪的想法上,并沉浸其中。等到时间快到了,他们才发现还来不及把方案画到纸上,甚至还没确定该选哪个方案。 + +这种现象其实很常见,我们在日常处理事务时,天然地会选择自己感兴趣的、擅长的那部分,而容易无视整体和其他部分。 + +所以问题的核心并不在于是不是用工程方法,而是有没有把这件事当作一个项目,是不是能看到这件事的全貌,而不是只看到局部。 + +在工作分工越来越细致的今天,一个项目里面有产品设计、开发、测试、运维等诸多岗位,每个岗位都有自己的价值追求,测试人员关注找出更多 Bug、开发人员关注技术和高效开发功能、运维关心系统稳定。 + +分工带来的好处,就是复杂的任务可以分给不同的人来做,这也有助于技能的专业化,提高工作效率。但如果只站在自己的立场去考虑问题,没有人关注整体价值,就容易相互误解,产生矛盾、增加成本。 + +以下这些工作场景,估计你不会陌生。 + + +产品经理提出一些天马行空、不切实际的需求,而技术上不可行或者实现成本很高,导致最后返工,造成资源浪费和进度延迟; + +架构师为了满足开发上的成就感,更愿意自己“造轮子”,而不愿意采用现有开源程序或者购买合适的组件; + +开发工程师喜欢在代码中使用各种设计模式或者最新技术,导致项目进度延迟,代码难以维护; + +测试工程师不愿意学习自动化测试技术,导致测试周期较长,且容易出现疏漏; + +除非产品经理特别注明,开发工程师和测试工程师不会注意用户体验上的细节。 + + +这样的场景问题还有很多,为什么会出现这种情况呢?事实上,这在很大程度上都归因于大家只是站在自己岗位的角度来看问题,没有站在项目的整体角度来看。 + +如果能站在项目整体来看问题,你就会去关注项目的质量、项目的进度、项目的成本、项目的最终用户,那么上面这些场景将变成: + + +为了项目整体的效率和避免返工浪费,产品经理会及早和开发人员确认技术可行性,并对产品设计先行验证; + +为了节约项目开发成本,提高开发效率,架构师选择成熟的架构,合理购买商业组件和使用开源程序; + +为了提升开发效率,不影响项目开发进度,开发工程师尽可能采用成熟的技术,高效简洁地落实项目; + +为了项目质量和效率,测试工程师学习自动化测试技术,将大部分测试变成自动化运行,极大地提高了测试效率和质量; + +为了让用户有好的体验,不仅产品经理,每个人都会仔细体验用户界面,对于不合理的地方提出改进意见。 + + +看起来很理想化对不对?但如果大家真能从自己做起,这样的结果并不是太难达到。 + +肯定有人会想,我又不是项目经理,干嘛要操这心呀?在这个问题上,我的看法是:每个项目成员,如果能多站在项目的角度去考虑,那么这样不仅对项目有利,更对自己有好处。 + +项目做成了,大家脸上都有光,也得到了更多的锻炼;项目没做成,不仅脸上无光,甚至可能面临丢工作的危险。很多人都有技术升管理的理想,能多站在项目整体角度去考虑的人,在日常工作中,也一定会有更多的锻炼机会,自然会多一些提升的空间。 + +我把这种思维方式称为“工程思维”。如果给一个定义的话,工程思维,本质上是一种思考问题的方式,在解决日常遇到的问题时,尝试从一个项目的角度去看待问题、尝试用工程方法去解决问题、站在一个整体而不是局部的角度去看问题。 + +在我的职业生涯中,一直习惯于用“工程思维”去思考问题,遇到问题,会尽可能把它当成一个项目,用工程方法有计划、有步骤地去解决它,这让我积累了不少的工程方法实践经验。 + +同时,我也更多站在整体的角度思考,这让我在项目中能更好地和其他同事合作,有更多的晋升机会。我还记得,我第一次开始管项目的时候,并没有慌张,而是把项目任务按阶段一拆分,然后按阶段制定好计划,再按照计划一点点执行、调整,很快就上手了项目管理的工作。 + +总结 + +改变,最有效的是方式是改变思想,这往往也是最难的部分。 + +当你开始学习这个软件工程专栏,我希望你不仅仅学到软件工程的理论知识,更希望你能用“工程思维”来思考你遇到的各类问题。 + +你不需要现在是一个项目经理或者管理者,也一样可以在日常生活中应用“工程思维”。比如学习这个专栏,你会制定一个什么样的计划?每个阶段达到一个什么样的成果?比如你今年有没有去旅行的计划?你会怎么制定你的旅行计划? + +如果有兴趣的话,你还可以看看我以前写过的一篇文章记录下两个孩子在 MineCraft 里面还原公寓的经历。 + +这也是一个很有意思的工程思维实践,帮助孩子们在游戏里面还原公寓。这本质上也是一个项目,需要制定计划,需要设计、实现。我希望他们从小就有工程思维,能在未来有目的、有计划、有步骤地去解决日常生活的问题。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/03瀑布模型:像工厂流水线一样把软件开发分层化.md b/专栏/软件工程之美/03瀑布模型:像工厂流水线一样把软件开发分层化.md new file mode 100644 index 0000000..c225e68 --- /dev/null +++ b/专栏/软件工程之美/03瀑布模型:像工厂流水线一样把软件开发分层化.md @@ -0,0 +1,277 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 瀑布模型:像工厂流水线一样把软件开发分层化 + 你好,我是宝玉,我今天分享的主题是:瀑布模型,像工厂流水线一样把软件开发分层化。 + +可以这么说:瀑布模型算是现代软件工程的起源,软件工程的发展,很大部分都是构建于瀑布模型的基础之上的。我们后面所学的软件工程的很多内容,都是源自瀑布模型的衍生,或者其中某个阶段的细分。 + +我在上大学期间,还并不懂软件工程瀑布模型这些知识。当时我自学了点编程知识,然后开始在外面接点做网站的小活,开发模式非常简单,接到活直接写代码,有问题就改。这样下来居然也做了不少小网站,但是大一点的网站项目就搞不定了,甚至手头的小网站项目,找个同学帮忙都不知道大家该怎么分工。 + +所以当时我也很好奇,大的软件系统是如何开发出来的?那么多人一起开发一个软件,系统是如何分工协作的? + +后来到大三的时候,开始系统学习软件工程课程,我才开始了解到一些理论知识,包括我做小网站的这种开发模式,都有一个专业术语,叫边写边改(Code And Fix)模型。 + +这不是我的发明。在 1960 年初,软件开发刚开始起步,这时的软件开发是混沌无序的,那时候编程语言还是汇编语言为主,开发模式就是边写边改模型。如果程序员水平高,功能简单,还是可行的。 + +后来软件开发需求越来越多,功能越来越复杂,从事软件开发的人员水平也参差不齐,这种落后的软件生产方式已经无法满足迅速增长的计算机软件需求,从而导致软件开发与维护过程中出现一系列严重问题,这个现象也被称之为“软件危机”。 + +像这种边写边改的开发模式,为什么说不能满足复杂软件项目的需要呢?主要是有几方面的原因: + + +整个开发过程不可控,想基于这种开发模式做项目计划太难; + +项目的人数多了后,无法有效分工协作; + +项目开始的时候对需求几乎没有进行有效分析,对需求的理解容易出现偏差,后期导致很多返工; + +项目编码完成后,没有有效测试,运行时 Bug 非常多。 + + +瀑布模型的诞生 + +为了解决软件危机中的这些问题,在 1970 年,Winston Royce 博士借鉴了其他工程领域的思想,比如建筑工程,提出了瀑布开发模型,指出软件开发应有完整之周期,并将软件开发过程分成了若干阶段。像瀑布一样,从上往下,完成一个阶段继续下一个阶段。 + + + +瀑布模型把整个项目过程分成了六个主要阶段: + +一、问题的定义及规划 + +这个阶段是需求方和开发方共同确定软件开发目标,同时还要做可行性研究,以确定项目可行。这个阶段会产生需求文档和可行性研究报告。 + +二、需求分析 + +对需求方提出的所有需求,进行详细的分析。这个阶段一般需要和客户反复确认,以保证能充分理解客户需求。最终会形成需求分析文档。 + +三、软件设计 + +根据需求分析的结果,对整个软件系统进行抽象和设计,如系统框架设计,数据库设计等等。最后会形成架构设计文档。 + +四、程序编码 + +将架构设计和界面设计的结果转换成计算机能运行的程序代码。 + +五、软件测试 + +在编码完成后,对可运行的结果对照需求分析文档进行严密的测试。如果测试发现问题,需要修复。最终测试完成后,形成测试报告。 + +六、运行维护 + +软件开发完成,正式运行投入使用。后续需要继续维护,修复错误和增加功能。交付时需要提供使用说明文档。 + +瀑布模型在提出后,因为其简单可行,切实有效,马上就在很多软件项目中应用起来,一直到 2000 年前后,都是最主流的软件开发模型,即使到现在,你也能在很多软件项目中看到它的影子。 + +也是从那时开始,有了“软件生命周期”(Software Life Cycle,SLC) 的概念。 + + +软件生命周期是软件的产生直到报废或停止使用的生命周期。而像瀑布模型这样,通过把整个软件生命周期划分为若干阶段来管理软件开发过程的方法,叫软件生命周期模型。 + + +虽然现在瀑布模型已经不是最主流的开发模式,那为什么我们现在还要学习瀑布模型呢? + +因为不管什么软件项目,不管采用什么开发模式,有四种活动是必不可少的,那就是需求、设计、编码和测试。而这四项活动,都是起源自瀑布模型,也是瀑布模型中核心的部分。 + +学好瀑布模型,才可以帮助你更好的理解这些内容。 + +如何用瀑布模型开发项目? + +如果单纯看这些阶段的概念介绍,还是有点难以直观地理解整个软件开发过程,在这里拿我经历过的一个网站开发项目作为案例,来看一下如何使用瀑布模型来开发一个软件项目。 + + +问题的定义及规划的阶段 + + +大概在 2009 年的时候,Web2.0 还正火,公司老板打算做一个游戏领域的社交网站。 + +问题很明确,就是要做一个社交网站,并且用户能按照游戏来交友。至于可行性分析嘛,按照当时 Web2.0 的热度,这个似乎是可行的。那么就立项了。 + +然后老板问项目经理,这么样一个网站,你大概得多久做出来?项目经理一看,这么复杂一个网站,怎么也得半年才能做出来一个版本,于是说半年。老板说半年太久了,给你三个月吧,项目经理心中叫苦,最后讨价还价,决定四个月上线。 + +于是,项目经理按照四个月开始倒推项目计划: + + +需求分析——2 周; + +软件设计——4 周; + +程序编码——6 周; + +软件测试——4 周。 + + + +需求分析的阶段 + + +在项目立项后,产品经理首先和老板充分的沟通,了解老板的想法是什么,要做一个什么样的网站。在了解老板的想法后,产品经理对市场上同类的社交网站进行了调研,然后用原型工具设计了网站的原型。原型虽然很简陋,但是从原型可以看出来,项目要做成什么样子,便于确认需求。 + +原型拿给老板看后,老板再根据自己的想法提一些反馈,这样反复沟通确认,在原型设计确认清楚后,产品经理开始撰写产品设计文档,将原型设计落实到文档,将整个网站划分成不同的功能模块,例如用户注册、登录、添加好友等,确定每个功能模块需要哪些功能。 + +这个阶段产品经理是最忙的,那这时候其他人在干嘛呢?其他人都还挺轻松的,架构师研究网上流行的社交网站都采用什么架构,程序员、测试看看技术文档。 + +虽然最终确定了产品设计文档,但是因为中间反复确认的时间过长,原定 2 周能完成的需求分析,最后拖到了 3 周。项目经理一看,最终上线时间点没办法延后,那就只好压缩编码时间了,不行加加班! + +项目计划变成了: + + +需求分析——3 周; + +软件设计——4 周; + +程序编码——5 周; + +软件测试——4 周; + + + +软件设计 + + +产品经理的产品设计文档确定后,架构师开始做架构设计,UI 设计师开始设计 UI,测试经理开始针对产品设计文档写测试用例,产品经理还要进一步设计交互。 + +由于前期原型设计工作做的好,所以 UI 设计还是很顺利的,主风格定下来以后,各个界面就是细节的确认了。 + +因为产品设计文档写的详细,输入输出很清楚,测试用例也进展顺利。 + +至于架构设计这边,架构师很有经验,先把整体架构确定,写了个技术方案文档,和大家一起开会讨论,几次后确认了整体技术方案。按照功能模块一拆分,把其中一个功能模块做了一个样板,然后把各个子模块分给开发人员,大家一起协助做详细设计,然后再分别确认。 + +大家都如火如荼地忙起来了。如果一切顺利的话,软件设计 4 周应该能完成,可以进入编码阶段了。但是软件设计进行到第 3 周的时候,老板的想法发生了一些变化。 + +因为市场上已经有了游戏社交的网站,而且运营结果不算太好,而网页游戏正流行,如果我们的平台能接入网页游戏,这会是个不错的机会。 + +于是需求变更了,我们要能和其他网页游戏的用户系统对接,这个需求最开始是没有提出来,也没有考虑的。 + +项目经理考虑再三,决定还是接受这个需求变更,但是希望能多一些时间,老板没同意,认为时间点很重要,哪怕砍一点功能,牺牲一点质量也要如期上线。但就算这时候砍功能,设计工作还是少不了多少。 + +于是产品经理重新修改相应原型,再确认,再重新修改产品设计文档。变更完后,UI 设计的相关页面重新修改设计、测试人员修改测试用例,最苦的是架构师,当初没有考虑到要和其他用户系统对接,现在用户系统的设计都要重新考虑了。 + +于是为了赶进度,项目组开始加班,即使如此,软件设计阶段也推迟到了第 5 周才勉强完成。 + +项目计划又变了: + + +需求分析——3 周; + +软件设计——5 周; + +程序编码——5 周; + +软件测试——3 周。 + + + +程序编码 + + +终于进入编码阶段了,为了保证进度,加班还在继续,哪怕前期做了大量的设计,真到编码的时候还是有好多没有考虑到的,同时各个模块之间还存在相互依赖,有时候虽然自己功能开发完成,还需要等待其他人的功能完成才能调试,所以 5 周时间很快就过去了,而程序还不能完整地跑起来。 + +其实中间还有个小插曲,老板觉得还要加上支付的功能,但是项目经理觉得这个阶段改需求已经不可能了,以辞职为威胁总算顶回去了,打算放在下个版本加上。 + +终于到第 6 周的时候,有了一个勉强可以测试的版本。 + +项目计划现在变成了: + + +需求分析——3 周 + +软件设计——5 周 + +程序编码——6 周 + +软件测试——2 周 + + + +软件测试 + + +留给测试的时间只有两周了,但是前期实在 bug 太多,两周测试时间过去,软件质量还是很糟糕,完全无法正常使用,于是项目不得不延期,一直延期了 4 周后,才算具备上线条件。 + +所以最终的项目计划差不多是: + + +需求分析——3 周 + +软件设计——5 周 + +程序编码——6 周 + +软件测试——6 周 + + +和原定计划已经延迟了 4 周。 + + +运行维护 + + +网站上线后,好在前期并没有多少用户,但是线上 Bug 还是不少,需要继续修复线上发现的 Bug。 + +瀑布模型的优缺点 + +以上案例是我参与过的、用瀑布模型开发的软件项目的一个缩影,你会发现瀑布模型其实跟我们传统的建筑建造方式非常类似。我们拿盖房子的过程来看看瀑布模型。 + + +客户想要盖一栋房子(初步的想法)。 + +客户一开始可能没想清楚想要什么样子的房子。(客户对需求还不清楚) + +施工方开始找客户确认:用途是什么,要个几层的房子,什么建筑风格,希望什么时间完工,预算多少。(问题定义) + +施工方根据客户提的需求,对比工期和预算,评估是不是值得做。(可行性研究) + +施工方评估后觉得可行,于是和客户签订合同,约定价钱和工期。(立项,制定项目计划) + +施工方开始跟客户沟通确认需求,例如每层户型如何,将来的装修风格等。(需求分析) + +确认完需求后,施工方开始出建筑施工图,还画了漂亮的建筑效果图。(系统设计和 UI 设计) + +施工方按照设计图开始施工。(程序编码) + +这期间如果客户去参观施工情况,客户只能看到毛胚,只有最后施工完成才能看到最终样子。(在中间客户看不到结果,只有最后能看到结果) + +原定二层是两个卧室,在房子施工过程中,突然客户说两个卧室不够,要改成三个卧室。这意味着施工方要对施工图重新设计,很多已经建好的房间要拆掉重建。(瀑布模型是很难响应需求变更的,而且越到后期代价越大) + +工程质量检查人员对施工结果进行质量检测,如果不满足质量要求,需要修改。(测试) + +最后验收通过后,客户入住。(上线) + + +所以你看,用瀑布模型开发软件,就像建筑工程里,盖房子一样简单和自然。每个阶段都有侧重的事情,就像需求阶段专注于搞清楚需求,编码阶段专注于实现。 + +最重要的是,这种编码前先设计、编码后测试、整个过程重视文档的方式,开发出来的产品,质量相对是有保障的。 + +但用瀑布模式开发,也存在一些问题。 + +最大的问题就是不能及时响应需求变更,越到后期变更代价越大。另外,通常要到最后阶段才能看到结果是什么样子。 + +我以前参与过的用瀑布模型方式开发的项目中,在开发和测试阶段加班是常态,原因就在于需求分析和系统设计可能会有延误,从而延迟了编码阶段的开始时间,压缩了编码实现的时间。 + +而在编码阶段,通常会发现很多设计时没有考虑清楚的问题,或者遇到需求变更,导致编码阶段即使加班加点也会大大延期,最后留给测试阶段的时间就不够多了。 + +鉴于瀑布模型存在的这些问题,后来又有很多人提出了其他的软件生命周期模型,比如快速原型开发模型、增量模型、迭代模型,以期保留瀑布模型的这些优点,克服瀑布模型中存在的问题。我们将会在后面的章节中,详细介绍瀑布模型衍生出的其他开发模型。 + + + +总结 + +从瀑布模型提出至今,将近 50 年过去了,虽然现在大家一提起瀑布模型,似乎已经成了落后的代名词,但在当时是有划时代意义的。如果类比一下,我觉得瀑布模型的价值相当于工业界第一次提出流水线作业。 + +1769 年,英国人乔赛亚·韦奇伍德开办埃特鲁利亚陶瓷工厂。以前制作陶瓷只有“制陶工”一个工种,一个人从挖泥、制胚到最后烧制,要求很高。但是乔赛亚把原本的制陶流程从开始到结束分成了若干阶段,每个阶段可以由不同的人完成,从单一的制陶工分成了挖泥工、运泥工、拌土工、制坯工等,这样就大大提高了生产效率,也降低对工人的要求。 + +同理,瀑布模型的出现,也解决了软件项目开发中的几个重要问题。 + + +让软件开发过程有序可控。瀑布模型的每个阶段都有明确的任务,每个阶段都有明确的交付产物,都有相应的里程碑。这些让整个过程更可控,而且能及早发现问题。 + +让分工协作变成可能。瀑布模型的六个阶段,也让软件开发产生相应的基础分工:项目经理、产品经理、架构师、软件工程师、测试工程师、运维工程师。 + +质量有保障。瀑布模型每个阶段都需要交付相应的文档,而文档的撰写和评审,可以帮助在动手之前把问题沟通清楚,想清楚。瀑布模型在编码结束后,会有严密的测试,只有测试验收通过后,才能上线发布。这些措施都让软件的质量更有保障。 + + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/04瀑布模型之外,还有哪些开发模型?.md b/专栏/软件工程之美/04瀑布模型之外,还有哪些开发模型?.md new file mode 100644 index 0000000..8028fa7 --- /dev/null +++ b/专栏/软件工程之美/04瀑布模型之外,还有哪些开发模型?.md @@ -0,0 +1,215 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 瀑布模型之外,还有哪些开发模型? + 你好,我是宝玉,我今天分享的主题是:瀑布模型的衍生模型都有哪些,你该如何选择? + +在上一篇文章中,我重点介绍了瀑布模型。你现在知道了,瀑布模型简单易行,对于软件质量是有比较高保障的。但是瀑布模型对于前期需求不明确的项目,很难开展需求分析,后续如果有需求变更,瀑布模型便很难响应。 + +而且,每个软件项目的情况各不相同,都有自己的特点。比如说: + + +有的项目风险很高,客户可能随时不给你钱了,得要做好准备,随时止损; + +有的项目客户自己没想清楚要想的是什么,做出来后再提各种修改意见,必须得想办法降低变更成本; + +有的项目客户希望能很快就能上线。 + + +如果选用瀑布模型做这些项目,就会导致成本过高或者周期过长等问题出现。所以,并不是所有的项目都适合使用瀑布开发模型,你需要针对不同的情况做一些调整。 + +实际上,为了应对瀑布模型的不足,已经衍生出了很多其他的开发模型。今天,我将为你分享一些有代表性的瀑布模型的衍生模型,你可以了解到这些衍生模型的本质,在接手不同类型的项目时,可以灵活地进行选择。 + +快速开发快速改 + + +快速原型模型 + + +我刚毕业时参加了一个项目的开发,项目经理跟我说,这个项目怎么快就怎么写,不要在意代码质量、架构、性能这些,当时我表示很不能理解,哪有这样做项目的? + +我还偷摸着花了很多时间想把代码写好,结果发现,这个快速做好的简单版本,主要目的就是为了给客户演示,跟客户确认需求,然后把客户的反馈记录下来,再去优化。这样几个小版本下来,基本上就把需求确定了,而我当时写的好多代码根本就用不上。 + +后来我才知道,这就是快速原型模型。 + +快速原型模型,就是为了要解决客户的需求不明确和需求多变的问题。 + +先迅速建造一个可以运行的软件原型,然后收集用户反馈,再反复修改确认,使开发出的软件能真正反映用户需求,这种开发模型就叫快速原型模型,也叫原型模型。 + +这就好比客户想要盖房子,但是他没想好要盖成什么样子,于是施工方就先搭了一栋彩钢房(就像工地里面搭的临时房子),让客户先用起来,然后再给反馈调整。 + +因为彩钢房搭建简单快速,改起来也相对容易。等到客户确定好需求,再在已经搭好的彩钢房的基础上完善,或者直接重新按照确定好的需求造房子。 + +不过,这样做也有一个问题,用彩钢房这种方式盖房子虽然快,但是房子质量不会太好,住的不算舒服,想有点个性化的风格也难。 + +同样的道理,也适用于软件项目。彩钢房就像是软件原型,重点是反映软件核心功能和交互,功能可以是不完整的,可靠性和性能要求不高,但开发速度可以很快。 + +原型模型因为能快速修改,所以能快速对用户的反馈和变更作出响应,同时原型模型注重和客户的沟通,所以最终开发出来的软件能够真正反映用户的需求。 + +但这种快速原型开发往往是以牺牲质量为代价的。 + +在原型开发过程中,没有经过严谨的系统设计和规划,可靠性和性能都难以保障。所以在实际的软件项目中,针对原型模型的这种快速、低质量的特点,通常有两种处理策略:抛弃策略和附加策略。 + +抛弃策略是将原型只应用于需求分析阶段,在确认完需求后,原型会被抛弃,实际开发时,将重新开发所有功能。类似于用彩钢房盖房子,确认完客户需求后,拆掉重新建。 + +附加策略则是将原型应用于整个开发过程,原型一直在完善,不断增加新功能新需求,直到满足客户所有需求,最终将原型变成交付客户的软件。类似于用彩钢房盖房子,最后还要做一番精装修,交付客户。 + +采用哪种策略来应用原型模型,还是要看项目特点,包括所采用原型开发工具和技术的成熟度。举例来说,如果客户对可靠性、性能要求高,那么就最好是抛弃策略,如果客户对质量要求不高,有简单功能就够了,那么可以试试附加策略。 + +快速原型模型即使到现在还一直有在用,用于低成本快速的确认需求。如果你将来遇到这种项目,就没必要花太长时间在代码质量上,赶紧做出来才是王道。 + +另外,原型制作并不一定要像传统代码一样进行设计编码,有很多原型工具,像 Axure、墨刀等,简单的拖拽就可以实现简单的界面和交互,同样可以达到确认需求的目的。现在原型设计已经成为产品经理确认需求的一个非常重要手段。 + +大瀑布拆小瀑布 + +瀑布模型的很多问题,根源都是周期太长。周期长所以中间难以响应变更,周期长所以客户很久才能看到结果,周期太长所以风险不好控制。如果能将周期变短,那么很多问题就迎刃而解了。 + +基于这种思路,产生了很多开发模型,比较典型的主要是:增量模型 和 迭代模型。 + + +增量模型——按模块分批次交付 + + +增量模型是把待开发的软件系统模块化,然后在每个小模块的开发过程中,应用一个小瀑布模型,对这个模块进行需求分析、设计、编码和测试。相对瀑布模型而言,增量模型周期更短,不需要一次性把整个软件产品交付给客户,而是分批次交付。 + +如果拿盖房子来比喻的话,就是先盖卫生间,然后盖厨房,再是卧室。 + +盖卫生间的时候,也要先分析需求,然后设计,再实施,最后验收。有时候也可以多模块并行,例如同时盖卫生间和厨房,前提是模块之间不能有依赖关系,比如,你不可能先盖二楼再盖一楼。 + +你会发现,增量模型将整个系统进行模块化处理,所以你可以分批次交付软件产品,使用户及时了解软件项目进展。如果一个模块有问题,或者需要做需求变更,对整体影响也有限。在开发的时候,也可以灵活地按照模块来分工,多个模块并行开发提升效率。 + + + +因为增量模型的根基是模块化,所以,如果系统不能模块化,那么将很难采用增量模型的模式来开发。另外,对模块的划分很抽象,这本身对于系统架构的水平是要求很高的。 + +基于这样的特点,增量模型主要适用于:需求比较清楚,能模块化的软件系统,并且可以按模块分批次交付。 + + +迭代模型——每次迭代都有一个可用的版本 + + +迭代模型每次只设计和实现产品的一部分,然后逐步完成更多功能。每次设计和实现一个阶段叫做一个迭代。 + +我们还是继续拿盖房子来举例:如果用迭代模型的方式盖房子,第一个迭代要先盖一个茅草屋,快速满足客户对房子的核心需求;第二个迭代再盖一个小木屋,比茅草房更大更舒适;第三个迭代再盖成一个豪华别墅,满足客户所有需求。 + +你要注意,无论是造小木屋还是大别墅,整个过程都会像一个完整的项目一样,包括需求分析、设计、实现与测试验收。 + + + +在迭代模型中,整个项目被拆分成一系列小的迭代。通常一个迭代的时间都是固定的,不会太长,例如 2~4 周。每次迭代只实现一部分功能,做能在这个周期内完成的功能。 + +在一个迭代中都会包括需求分析、设计、实现和测试,类似于一个小瀑布模型。迭代结束时要完成一个可以运行的交付版本。 + + + +迭代模型和增量模型很容易混淆,因为都是把大瀑布拆成小瀑布。这两种模型的主要差别在于如何拆分项目功能上。 + +增量模型是按照功能模块来拆分;而迭代模型则是按照时间来拆分,看单位时间内能完成多少功能。 + +还是用盖房子来理解,增量模型则是先盖厨房,再是卧室,这样一个个模块来完成。而迭代模型则是先盖一个简单的茅草房,有简易的土灶和土床,然后再升级成小木屋,有更好的灶和更好的卧室,这样一步步迭代成最终的房子。 + +我原来参与过的瀑布模型开发的项目,因为要很长时间才能看到最终结果,而且结果通常跟最初描述的结果相差较多,客户看到后多少会有些心理落差。 + +而后来改用迭代模型后,因为每次迭代完成后都有可以运行的版本,这样客户可以直观感受软件的进展,及时调整心理预期。尤其是当客户见证了一个软件从简陋到完善的过程,往往满意度是比较高的。 + +迭代模型最难的部分,在于规划每次迭代的内容和要达到的目标。多了可能完不成,少了可能造成每次迭代工作量不饱和,这需要在实践中去摸索,一个迭代一个迭代的去调整。 + +迭代模型由于在初始迭代时,只清楚当前迭代的需求,而不知道后续需求,设计可能会考虑不周全。这样的话,迭代一多,系统会有不少冗余,一段时间后就需要对系统进行重构。 + +另外每次迭代,用户可能会增加新的需求和对现有需求进行更改,因此开发时间上可能会比预期要长。如果你做的是小项目的话,并不建议使用迭代模型来开发。 + +我该选择什么过程模型? + +除了上面提到的这几种模型,还有很多其他开发模型,要记住所有的开发模型很难。你搞透了瀑布模型,搞清楚了其阶段划分,结合一些应用场景,你就可以举一反三,了解绝大部分衍生模型。 + +我在这里给你列举几个常见的项目场景,我们可以一起来分析下,看看用什么模型适合。 + +场景一:外包项目,需要阶段验收 + +假如你现在是一家外包公司,你可以采用瀑布模型开发,但是甲方需要对你项目的每个阶段进行验收测试,以确认你是不是达到要求。 + +针对从需求定义一直到编码阶段,每个阶段都有对应的测试验收。如果画成图,就是下面这个样子的。 + + + +这个模型就是 V 模型,本质上它还是瀑布模型,只不过它是更重视对每个阶段验收测试的过程模型。 + +场景二:项目风险高,随时可能会中断 + +如果你现在要做一个风险很高的项目,客户可能随时不给你钱了。这种情况下,如果采用传统瀑布模型,无疑风险很高,可能做完的时候才发现客户给不了钱,损失就很大了! + +这种情况,基于增量模型或者迭代模型进行开发,就可以有效降低风险。你需要注意的是,在每次交付的时候,要同时做一个风险评估,如果风险过大就不继续后续开发了,及时止损。 + + + +这种强调风险,以风险驱动的方式完善项目的开发模型就是螺旋模型。 + +场景三:山寨一款软件产品,希望能快速上线发布 + +其实软件行业山寨的案例不少,山寨项目的特点是,项目需求是明确的,不会有什么变化,这时候就可以选择增量模型,划分好模块,先实现核心模块,发布可运行版本,再增量发布其他模块。多模块可以同步开发。 + +场景四:客户都没想清楚想要什么,但是个大单子 + +很多项目,客户一开始都没想清楚想要的是什么,需要花很长时间去分析定义需求,但是单子很大,值得认真去做好。 + +那么这样的项目,你可以考虑拆分成四个阶段: + + +初始阶段 + + +主要是确定需求边界和主要风险,几乎没有什么开发工作。 + + +细化阶段 + + +这个阶段主要是确定需求,可以采用快速原型模型开发,和客户对需求反复确认,需要辅助一定量的开发和测试工作。对代码质量可以要求比较低,重点是确认需求。可能需要一个或多个版本迭代。 + + +构造阶段 + + +在需求确认清楚后,现在可以使用迭代模型来开发,逐步交付产品。这个阶段的重点是开发和测试。如果迭代中,有新的需求加入或者需求变更,也可以在新的迭代中加入。 + + +交付阶段 + + +在开发和测试完成后,产品可以交付客户,根据线上运行情况还需要修复一些 Bug。这个阶段重点是测试和部署。也会有多个迭代。 + +整个过程看起来就像下图这样。 + + + +上面这种开发方式来源自统一软件开发过程(Rational Unified Process,RUP),适用于复杂和需求不明确的软件系统。 + +场景五:我的产品已经上线,但是需要持续更新维护 + +很多产品在上线后,还在保持不停的更新维护,修复 Bug、增加新功能,每个月甚至每周更新。 + +在这种情况下,迭代模型是比较合适的。固定时间周期,在固定的周期内选择适合的需求开发任务和 Bug 修复任务去完成,按时发布。 + +另外还可以尝试敏捷开发,也是基于迭代的开发模型,它也强调快速交付,每次交付系统的部分功能,来保证客户满意度。在敏捷开发中,系统交付的周期称之为冲刺(Sprint)。 + +严格来说,敏捷开发并不算是一种开发模型,更像是框架或指南。有各种开发模型来实现敏捷开发,比如说极限编程(Extreme programming),看板(Kanban)和 Scrum。有关敏捷开发,我将在下一篇中向你详细讲解。 + +总结 + +现在的软件项目,各种类型都有,根据项目特点,选择好合适的开发模型,可以让你事半功倍,降低项目风险,提高项目开发效率,控制项目成本。比如说: + + +一个以确认需求为主要目的的项目,就可以不用花太多时间在代码质量上面,低成本、高效做出来才是最重要的; + +一个高风险的项目,则可以采用螺旋模型,出现问题及时止损; + +一个很长时间加班加点,却一直没法上线,导致士气低落的项目,可以改成增量模型,先上线一个小模块,让大家看到成绩提升士气,然后再迭代,逐步上线其他模块。 + + +同时,你也不必拘泥于这几种开发模型,还可以借鉴其他模型做的好的地方,甚至创造自己的开发模型,比如说你觉得敏捷的“站立会议”适合你的项目,那也可以借鉴过来。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/05敏捷开发到底是想解决什么问题?.md b/专栏/软件工程之美/05敏捷开发到底是想解决什么问题?.md new file mode 100644 index 0000000..88622d2 --- /dev/null +++ b/专栏/软件工程之美/05敏捷开发到底是想解决什么问题?.md @@ -0,0 +1,259 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 敏捷开发到底是想解决什么问题? + 你好,我是宝玉,我今天想跟你聊聊“敏捷开发”。 + +关于敏捷开发的实际应用,现在无外乎有以下几种常见的情形: + + +很多团队想敏捷开发,但不知道该怎么上手; + +有的团队已经应用了一些敏捷开发的实践,然而效果不理想,不知道是敏捷开发的问题,还是自己实践方式不得当; + +有的团队听说了敏捷开发,但是并不知道它是什么。 + + +为什么会这样呢?今天我们就围绕敏捷开发来谈一谈,看看敏捷开发是什么,能帮助我们解决哪些问题,要不要实施敏捷开发,以及怎么能应用好敏捷开发。 + +什么是敏捷开发? + +那什么是敏捷开发呢?有人认为: + + +敏捷开发就是 Scrum、极限编程; + +敏捷开发就是每天站立会议、每两周一个 Sprint(字面意思是冲刺,可以理解为迭代); + +敏捷开发就是把需求变成故事,把故事写在便签上贴到白板,然后根据状态移动到不同的列; + +敏捷开发就是用看板软件来管理项目。 + + +然而,这些是敏捷开发的真正含义吗? + +要理解敏捷开发,我们先要了解其诞生背景。在 2001 年那会,瀑布模型还是主流,我们知道,瀑布模型是一种“重型”的开发模式,整个流程走完通常周期很长,少则数月,多则数年。长周期导致风险增加、难以响应变化。 + +于是由瀑布模型衍生出很多模型,试图去改善瀑布模型存在的问题,我已经在上一篇文章中给你介绍了一些。不过除了介绍的那些以外,在当时还有一些不怎么有名,而现在却如雷贯耳的轻量级开发方法,例如极限编程(Extreme Programming,XP)、Scrum 等。 + +2001 年初,17 位代表上述各种轻量级软件开发过程流派的领军人物聚集在一起,讨论替代瀑布模型这种重量级软件开发过程的新方法。 + +但是没能达成一致,所以退而求其次,把大家都认同的理念整理出来,也就是后来的敏捷宣言。这些人还一起成立了敏捷联盟。 + + + +图片来源:敏捷开发宣言 + +我们再回头来看前面大家对敏捷的定义,其实都是在从方法论、工具等方面解释敏捷开发。而敏捷宣言指出: + + +敏捷不是一种方法论,也不是一种软件开发的具体方法,更不是一个框架或过程,而是一套价值观和原则。 + + +现实中关于敏捷的讨论,更多的是在讨论各种方法论和工具。不可否认,这些方法论和工具,能帮助团队“敏捷”起来,但它们和敏捷开发之间的关系,更像是“术”和“道”的关系。 + + +各种敏捷框架、方法论和工具,就像是“术”,告诉你敏捷开发的方式,而敏捷则是“道”,是一套价值观和原则,指导你在软件项目开发中做决策。 + + +这么说还是比较抽象,我给你举个例子。 + +敏捷开发中流行的站立会议,主要目的是为了保证团队成员充分的沟通,遇到困难可以及时寻求帮助。但是如果每天的站立会议流于形式,并不能起到有效的目的,则应该减少频度,甚至取消换成其他方式。 + +要不要在你的项目开发中使用站立会议,判断的依据就在于这样做是不是符合敏捷的价值观和原则。 + +也就是说,当你开发做决策的时候,遵守了敏捷开发的价值观和原则,不管你是不是用 Scrum 或者极限编程,那么都可以算是敏捷开发。 + +敏捷开发想解决什么问题? + +如果你仔细读了敏捷宣言,你会发现,宣言中右边的内容其实都是瀑布模型核心的内容:流程和工具、详尽的文档、合同谈判、遵循计划。 + +虽然敏捷开发并未对瀑布模型的价值进行否定,但也表明了瀑布模型做的还不够好,同时提出了一套自己的价值观。 + +比如说,我们开始做一个新项目,需要从客户那里收集整理需求,如果按照传统的软件开发模式,我们需要在开发前获得所有需求,然后和客户签订合同,在发布前都不会轻易修改需求。 + +但是如果我们采用敏捷开发模式来开发项目,那这样做显然违背敏捷的价值观:“客户合作高于合同谈判”。 + +所以如果是敏捷开发,在每个迭代后,都应该向客户收集反馈,然后在后面的迭代中,酌情加入客户反馈修改的内容。 + +结合敏捷开发提出的背景,你其实不难发现,敏捷开发就是想解决瀑布模型这样的重型软件开发方法存在的问题,用一种轻量的、敏捷的方法来改善甚至是替代它。 + +这些年敏捷开发也是一直这么做的。瀑布模型的典型问题就是周期长、发布烦、变更难,敏捷开发就是快速迭代、持续集成、拥抱变化。 + +如果用敏捷的方式盖房子 + +在讲瀑布模型的时候,我拿盖房子举了个例子,如果改成用敏捷开发的模式盖房子,则会是这样子的: + + +客户想要盖一栋房子(初步的想法)。 + +产品经理和客户进行了初步的沟通,把用户的需求写成了一个个用户故事(用简单的用户故事代替繁重的需求文档),例如: + + + +作为一个上班族,我想要一个卧室,以便于休息; + +作为一个家庭主妇,我想要一个厨房,以便于做饭。 + + + +施工人员根据用户故事和客户进一步沟通(客户合作高于合同谈判),然后对用户故事进行设计和实现; + +每个用户故事开发时,还要给一个测试机器人编写测试脚本,让机器人可以自动测试(大量采用自动化测试),并且做好的用户故事可以随时被测试验收(随时发布,持续集成); + +每个 Sprint 四个星期时间(时间盒子,迭代时间固定); + +第一个 Sprint 搭了个草棚,一张床就是卧室,厕所就挖了一个坑,厨房还来不及搭建(每个 Sprint 会选择高优先级的用户故事),屋顶还在漏水(每个 Sprint 会定期发布,客户可以随时看到可用版本,即使还不完整); + +第二个 Sprint 有了简易厨房,同时修复了屋顶漏水的毛病(每个 Sprint 不仅完成用户故事,还会修复 Bug); + +第三个 Sprint 升级成了小木屋,但是忘记加上窗户(敏捷推崇自动化测试,但可能会测试不完备); + +第四个 Sprint 升级成了砖瓦房,窗户也开好了,客户可以入住。但是这时候客户发现一家三口的话,完全不够用,需要扩建到 3 个卧室。于是决定下个迭代改成 3 个卧室(咱们还是讨论情感 房子 票子 比较好 ); + +第五个 Sprint,升级成了 3 个卧室,升级过程中把厨房下水道弄坏了(迭代过程中可能会导致质量不稳定); + +第六个 Sprint,修复了下水道的问题,房子也装修好了(迭代中不断完善); + +客户验收使用(上线)。 + + +用敏捷开发的方式,不再像瀑布模型那样有严格的阶段划分,会在迭代中不断完善;不再写很多文档,而是和客户一起紧密合作;不再抵制需求变更,而是即时响应变更;不再等到测试阶段才发布,而是随时发布,客户随时可以看到东西。 + +当然,采用敏捷开发的模式也存在一些问题,例如全程需要客户参与,由于测试相对少一些 ,问题也会相应多一些。 + +敏捷开发和瀑布模型的差异 + +由于我大学时学软件工程,那时学的就是瀑布模型,毕业后很多年的项目开发都是以瀑布模型为主的,所以我在刚开始去看敏捷开发,总会以瀑布模型的方式类比敏捷开发,实践的时候也难以摆脱瀑布模型的影响。 + +直到近些年,我完整的在日常项目中反复实践敏捷开发,才逐步领会到瀑布模型和敏捷开发的一些差别。 + +这些年敏捷开发,已经逐步发展出一套 “Scrum + 极限编程 + 看板” 的最佳实践,Scrum 主要用来管理项目过程,极限编程重点在工程实践,而看板将工作流可视化。 + +我将基于 Scrum 和极限编程的实践,来对比一下敏捷开发模型和瀑布模型的差异。 + + +敏捷开发是怎么做需求分析的? + + +瀑布模型的一个重要阶段就是需求分析,要有严谨的需求分析,产生详尽的需求分析文档。而敏捷开发的需求,主要是来源于一个个小的用户故事,用户故事通常是写在卡片上的一句话,在 Sprint 的开发中,再去确认需求的细节。 + +比如一个用户登录网站的需求,在用户故事里面就是一句话: + + +作为用户,我想登录网站,这样可以方便浏览。 + + +好处是减少了大量需求文档的撰写,可以早些进入开发。但这个对开发人员在需求理解和沟通的能力上要求更高了。 + + +敏捷开发是怎么做架构设计的? + + +瀑布模型在需求分析完了以后,就需要根据需求做架构设计。而在敏捷开发中,并不是基于完整的用户需求开发,每个 Sprint 只做一部分需求,所以是一种渐进式的架构设计,当前 Sprint 只做适合当前需求的架构设计。 + +这种渐进式的架构设计,迭代次数一多,就会出现架构满足不了需求的现象,产生不少冗余代码,通常我们叫它技术债务,需要定期对系统架构进行重构。 + + +敏捷开发怎么保证项目质量? + + +瀑布模型在编码完成后,会有专门的阶段进行测试,以保证质量。在敏捷开发的 Sprint 中,并没有专门的测试阶段,这就依赖于开发功能的同时,要编写单元测试和集成测试代码,用自动化的方式辅助完成测试。 + +相对来说,这种以自动化测试为主的方式,质量确实是要有些影响的。 + +微软的 Windows 就是个很好的例子,在 Windows 10 之前,Windows 的开发模式是传统的类瀑布模型,有很长一段测试的时间,质量有很好的保障,Windows 10 开始,采用的是敏捷开发的模式,每月发布更新,稳定性要稍微差一些。 + + +敏捷开发是怎么发布部署的? + + +瀑布模型通常在编码结束后,开始部署测试环境,然后在测试阶段定期部署测试环境。测试验收通过后,发布部署到生产环境。 + +在敏捷开发中,这种持续构建、持续发布的概念叫持续集成,因为整个过程都是全自动化的,每次完成一个任务,提交代码后都可以触发一次构建部署操作,脚本会拿最新的代码做一次全新的构建,然后运行所有的单元测试和集成测试代码,测试通过后部署到测试环境。 + +持续集成是一个非常好的实践,极大的缩短和简化了部署的流程,而且自动化测试的加入也很好的保证了部署产品的质量。前期搭建整个持续集成环境需要一定技术要求。 + + +敏捷开发的 Sprint 和迭代模型的迭代有什么区别? + + +在上一章我介绍了增量模型和迭代模型,这两种也是一种快速迭代的方式,那么敏捷开发和迭代模型的区别是什么呢? + +我们假设有两个团队,都要实现一个简单的用户系统,一个团队用迭代模型,一个团队用敏捷开发(Scrum),一个迭代 /Sprint 的时间周期都是 2 周(10 个工作日)。 + +迭代模型所在的团队,产品经理会先花 2 天时间去分析需求,写成需求分析文档,架构师会花 3 天时间来做设计,程序员会花 3 天时间编码,测试再花 2 天时间去测试,最后上线用户系统。 + +再看敏捷开发的团队,Product Owner(类似于产品经理)会把需求拆分成了几个简单的用户故事:用户登录、用户注册、找回密码、修改资料,然后放到当前 Sprint 的 Backlog(任务清单),Team(开发团队)成员开始从 Backlog 选择用户故事。 + +程序员 A 选了“用户登录”这个用户故事,他会去找 Product Owner 确认需求细节,之后动手实现这个用户故事。 + +功能完成后,同时程序员 A 还写了单元测试代码和集成测试代码,对登录的功能写了自动化测试。完成后,通过持续集成工具测试和部署到测试环境。部署完成后,用户登录功能就可以进行使用了。 + +这个过程,程序员 A 可能花了 4 天时间,做完“用户登录”这个用户故事之后,他又开始继续选取“找回密码”的用户故事来做,4 天时间也完成了。 + +其他程序员也和程序员 A 一样,他们也会从 Backlog 选择一些用户故事来做。 + +当团队中第 1 个用户故事部署完之后,测试人员就开始帮助测试,发现的 Bug 都提交到了 Backlog,程序员们在完成用户故事后,开始着手修复这些 Bug,正好在最后 2 天都修复完成。 + +从上面的例子,你可以看出,迭代模型本质上是一个小瀑布模型,所以在一个迭代里面,需要完整的经历从需求分析,到设计、编码、测试这几个完整的阶段。 + +所以像瀑布模型一样,刚开始测试的时候是不稳定的,到测试后期才逐步稳定下来,一般迭代前期也会相对轻松一点,而后期测试阶段可能会时间很紧张。 + +敏捷开发的 Sprint 中,没有像瀑布模型那样严格的开发阶段划分,而是一个个循环迭代的 Sprint。举例来说,一个瀑布模型的项目,可能会按照阶段分成:2 周需求分析,2 周设计,4 周编码,2 周测试,然后上线发布,一共 10 周。如果用敏捷开发的方式来进行,那么可能会是每 2 周一个 Sprint,每个 Sprint 结束后,都会发布上线,每次发布的可能只是完整功能的一部分,但是每次发布的都是一个可用的版本,通过多个 Sprint 的迭代,最终完成项目开发。 + +具体到每一个 Sprint 的开发周期中,在一个 Sprint 中会有多个小的开发任务,这些开发任务主要是新功能的开发和 Bug 的修复。由于每个 Sprint 周期很短,所以不能像瀑布模型那样有充足的时间去做需求分析、设计和测试,那么敏捷开发中怎么保证质量呢? + +在敏捷开发中,通常用“用户故事”这样的方式来代替传统的需求分析,也就是以用户故事的形式,对一个需求进行简单的描述,配合关键的测试用例,并且和需求方的紧密沟通,让开发人员可以理清楚需求;通过“只做刚刚好的设计”来节约设计上的时间;通过“自动化测试”、“持续集成”来提升测试效率。 + +相对来说,敏捷开发中,整个 Sprint 的节奏是比较恒定,产品也是相对稳定的,即使用户故事没有完成,也不影响版本的发布。 + +因此,敏捷开发更注重软件开发中人的作用,需要团队成员以及客户之间的紧密协作。 + +该不该选择敏捷开发? + +该不该选择敏捷开发,是很多团队纠结的问题。毕竟关于敏捷,有很多在中国落地失败的例子,是不是这种方法在国内水土不服? + +其实,敏捷开发无论国内还是国外,大厂还是小厂,都已经有无数成功案例。这些年,软件工程中一些好的实践,像持续集成、测试驱动开发、结对编程、看板等都来自于敏捷开发。可以肯定,敏捷开发是一种非常好的软件开发模式。 + +但在应用上,也确实需要满足一些条件才能用好,例如: + + +团队要小,人数超过一定规模就要分拆; + +团队成员之间要紧密协作,客户也要自始至终深度配合; + +领导们的支持。敏捷需要扁平化的组织结构,更少的控制,更多的发挥项目组成员的主动性; + +写代码时要有一定比例的自动化测试代码,要花时间搭建好源码管理和持续集成环境。 + + +所以在选择敏捷开发这个问题上,你先要参考上面这些条件。 + +因为敏捷开发对项目成员综合素质要求更高,做计划要相对难一些。如果团队大、客户不配合、领导不支持,再好的敏捷方法也很难有效实践起来。 + +如果你要实践敏捷开发,建议先找个小项目进行试点,能证明可行了,再进一步推广。有条件的话,可以和一些顾问公司合作,请人做专门的培训和指导。 + +如果不具备条件,应该考虑先把其中一些好的实践用起来,比如说持续集成、每日站会、自动化测试等。 + +总结 + +我们今天一起学习了什么是敏捷开发,也就是敏捷开发是一套价值观和原则。也对比了瀑布模型和敏捷开发,其中的差异还是很大的。 + +瀑布模型面向的是过程,而敏捷开发面向的是人。敏捷开发要解决的,恰恰是瀑布模型中存在的一些问题。 + +最后,在要不要用敏捷开发这个问题上,不用过于纠结,看好敏捷开发,那就放心去用,觉得时机还不成熟、还不够了解,就先试点或者只是先借鉴其好的实践。 + +软件开发,最核心的是人,而不是用什么方法,以前没有敏捷开发只有瀑布模型的时候,也一样诞生了大量伟大的软件,像 Windows、Office。现在有敏捷开发,更多的是让我们多了一些选择。 + +在下一篇文章,还会再从大厂如何应用敏捷开发的角度,继续讲一讲敏捷开发的应用。 + +另外,敏捷开发涉及内容还是比较多,如果想有更多了解,可以阅读一些书籍作为专栏的补充。 + +除了“学习攻略”中推荐的一些书,还有像《用户故事与敏捷方法》《敏捷武士︰看敏捷高手交付卓越软件》等这些敏捷实践的书籍也可以辅助看看。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/06大厂都在用哪些敏捷方法?(上).md b/专栏/软件工程之美/06大厂都在用哪些敏捷方法?(上).md new file mode 100644 index 0000000..fca068f --- /dev/null +++ b/专栏/软件工程之美/06大厂都在用哪些敏捷方法?(上).md @@ -0,0 +1,237 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 大厂都在用哪些敏捷方法?(上) + 你好,我是宝玉,我今天分享的主题是:大厂都在用哪些敏捷方法?我将分为上下两篇,来与你一起讨论这个话题。 + +在我还是一个野路子程序员,到处接私活做网站时,就开始好奇:大厂都是怎么开发软件项目的?直到毕业后,我前前后后加入了若干大中小型企业,包括这些年在美国高校、公司的一些经历,对大厂的项目开发有了比较多的了解。 + +其实大厂做项目也没有什么特别的,无非就是工程中常见的“分而治之”的策略:大项目拆成小项目,大服务拆成小服务,大团队拆成小团队。 + +服务之间通过商定好的标准协议进行通信,架构上将大的服务拆分隔离成微服务,大团队按照业务或者服务拆分成小组,按照一定的流程规范保障协作。最终,各个小组要负责的内容其实就不多了。 + +就像淘宝这种网站,不需要一个庞大的项目组,通过逐级分拆,一个小组可能就只需要负责一个页面中的一个小模块。 + +所以,也要归功于现在微服务、容器等新技术,可以将复杂的业务逐级拆分,让很多公司能真正敏捷起来。 + +在上一篇文章中,我有提到,团队要实施敏捷,不仅要小,还要组织扁平化。相对来说,美国的互联网大企业做的还是很不错的,组织架构都很扁平,工程师地位很高。 + +这些年,国内工程师地位应该也有很大提升,组织也在向扁平化发展。前些天我也看到阿里工程师写的一篇文章《敏捷开发的根本矛盾是什么?从业十余年的工程师在思考》,对这个问题有精彩的论述。 + +下面,我就带你一起看看,大厂具体是怎么应用敏捷方法的。 + +和敏捷开发相关的主要流程规范 + +大厂里流程规范很多,最开始你会不喜欢它们,后来会离不开它们。 + + +这些墙很有趣。刚入狱的时候,你痛恨周围的高墙;慢慢地,你习惯了生活在其中;最终你会发现自己不得不依靠它而生存。这就叫体制化。——《肖申克的救赎》 + + +这里,我简单将其中和敏捷开发相关的流程介绍一下。 + +一切工作任务围绕 Ticket 开展 + +早些年的项目开发,都是围绕着项目计划开展的,把甘特图打印贴在墙上,方便团队成员看项目进展到什么地步了。自从敏捷化后,开始变成了看板。 + +所谓的看板,就是把白板分成几个栏,每一栏为一类,分别写着“To Do(待选取)”、“In Progress(进行中)”、“Done(完成)”等,再把工作任务变成一个个五颜六色的即时贴,根据状态贴在不同的栏下面。 + + + +慢慢的物理的看板变成了电子看板,通过各种项目管理软件来管理跟踪这些任务,即时贴也变成了 Ticket(也有叫 Issue 的)。逐渐的,所有与开发相关的任务也都和 Ticket 挂钩了: + + +报一个 Bug,提交一个 Ticket ; + +提一条需求,提交一个 Ticket ; + +要重构一下代码,提交一个 Ticket 。 + + +看板这种基于 Ticket 来管理跟踪任务的方式,看起来繁琐,但确实是很高效的一种方式。 + + +每一个任务的状态都可以被跟踪起来:什么时候开始做的,谁在做,做完没有。 + +整个团队在做什么一目了然。 + +Ticket 和敏捷开发中的 Backlog(任务清单)正好结合起来,通过 Ticket 可以收集管理整个项目的 Backlog 和当前 Sprint(迭代)的 Backlog。 + + +有了看板后,大家每天上班第一件事就是打开看板,看看当前 Sprint 还有哪些 Ticket 没有完成,哪些已经完成,哪些正在进行中,非常直观。 + +作为项目成员来说,做完手头的事情也不用去问项目经理该干什么事情了,直接从 To Do 栏选一条 Ticket 做就是了;对于项目经理,看看 To Do 栏还有多少没有被选取,就知道还剩多少 Ticket 没完成,看看 In Progress 栏就知道哪些 Ticket 正在进行中。 + +如果有 Ticket 在这一栏待太久或者这一栏 Ticket 太多,那可能就有风险了,就可以及时介入。 + +对于项目管理软件和 Ticket,我在后面章节中还会有进一步介绍。 + +基于 Git 和 CI 的开发流程 + +如果你的团队应用瀑布模型来开发,大概会有两大烦恼:代码不稳定和部署太麻烦。 + +早些年虽然也用源代码管理,但是大家都是在 master(主干)上开发的,所以 master 的代码特别不稳定,一不小心就可能被人签入了不稳定的代码。所以在上线前,有一段时间叫“代码冻结期”,意思就是这期间,除非是紧急修复,否则谁都不能往上面提交代码。 + +还有,测试环境的部署也是个老大难问题,尤其是在服务较多时,编译要注意各种依赖和环境的配置。所以更新测试环境是个大工程,以至于当年我在飞信的时候,专门有人负责部署测试环境。 + +上面的“代码冻结”和“专人部署”方案,可一点都不敏捷。所以团队想要敏捷起来,一定要解决代码不稳定和部署太麻烦这两个大问题。 + +好在基于 Git 的开发流程结合 CI 的自动测试部署,很完美的解决了这两大问题。 + +Git 本来只是源代码管理工具,但是其强大的分支管理和灵活的权限控制,结合一定的开发流程,却可以帮助你很好的控制代码质量。 + +我们假设现在 master 的代码是稳定的,那么怎么保证新加入的代码也稳定呢? + +答案就是代码审查(Code Review)和自动化测试。如果代码有严格的审查,并且所有自动化测试代码都能测试通过,那么可以认为代码质量是可靠的。当然前提是自动化测试代码要有一定的覆盖比率。 + +关于这点,对于大厂来说倒不是什么问题,正规的项目组对于代码审查和自动测试代码的覆盖率都有严格的要求。现在还有一个问题,就是如何在合并到 master 之前把代码审查和自动化测试做好呢? + +简单来说,就是每次要往 master 添加内容,不是直接提交代码到 master,而是先基于当前稳定的 master,克隆一个 branch(分支)出来,基于 branch 去开发,开发完成后提交一个 PR(Pull Request,合并请求)。 + +图片来源:VSCode项目PR + +PR 提交后,就可以清楚的看出来代码做了哪些改动,其他人就可以针对每一行代码写评论提出修改意见。如果确认代码没问题了,就可以通过代码审查。 + +接下来还剩下自动化测试的问题。这时候该 CI (持续集成)出场了。 + +如果你不了解 CI 是什么,可以把它想象成一个机器人,每次你提交一个 PR(严格来说是 Commit,这里略作简化)到源代码服务器,这个机器人马上就知道了。 + +然后它创建一个干净的运行环境,把你提交的代码下载下来,再下载安装所有依赖项,然后运行你的所有测试代码,运行完后,把测试结果报告给你。测试结果直观的反馈在 PR 上,绿色表示通过,红色表示不通过。 + +图片来源:Video-React项目PR + +关于 Git 和 CI,我在之后的文章中会展开讲解,这里只是为了展现敏捷开发方法的流程。另外,阮一峰老师写过两篇文章,《Git 工作流程》,《持续集成是什么?》,你也可以先行阅读了解。 + +至此,代码审查和自动测试的问题都解决了。当一个 PR 代码审查通过,以及 CI 通过了所有自动化测试,就可以合并到 master 了,而且我们也可以认为合并到 master 后的代码也是稳定的。 + +至于自动部署测试环境,反倒是简单,就是 CI 这个机器人,在你代码合并到 master 的时候,再次运行自动化测试代码,测试通过后直接运行自动部署的脚本,把 master 代码部署到开发环境或测试环境上。 + + + +在这里以一个开发任务为例,大致讲解一下应用敏捷开发方法的基本开发流程: + + +把要开发的 Ticket 从“To Do”栏移动到“In Progress”栏; + +从主干(master)创建一个分支(branch),基于分支去开发功能或修复 Bug; + +编写实现代码和测试代码(单元测试和集成测试),是不是测试驱动不重要,看个人偏好或团队要求; + +持续提交代码更新到分支,直到完成; + +创建 PR(Pull Request,合并请求),邀请其他人帮忙 Review 代码,根据 Review 的结果,可能还需要更新几次; + +CI 在每一次提交代码到代码库后都会自动运行,运行后主要做这些工作: + + +检查代码格式是不是符合规范; +运行单元测试代码; +运行集成测试。 + + +最终这些检查都完成后,CI 会把执行结果显示在 PR 上。通常绿色表示通过,红色表示失败; + +PR 能合并需要满足两个条件:CI 变绿 + 代码 Review 通过; + +PR 合并后,CI 会自动构建 Docker Image,将 Image 部署到开发环境; + +将相应的 Ticket 从看板上的“In Progress”栏移动到“Done”栏。 + + + + +图片来源:Jira + +正常来讲,你是需要严格遵守开发流程的,但偶尔肯定也有紧急任务,来不及写测试代码,这种情况下,一定要再创建一条 Ticket 跟踪,以确保后续完成测试代码。 + +部署上线流程 + +最早的时候,程序员都是自己管服务器,但是由于这样过于随意,就会导致很多问题出现。 + +于是后来有专门的运维团队,将开发好的程序,编译好,数据生成脚本写好,然后写成部署文档,交给运维去手动部署。这个过程无比繁琐、无比慎重,通常几周才部署一次,遇上打补丁才隔几天部署。 + +这些年随着容器化、微服务、DevOps 这些技术或概念的兴起,部署已经变得越来越高效,大厂已经开始在部署流程上融合这些理念。 + +以前是运维人员按照文档部署,现在已经变成了 DevOps 写自动化部署工具,然后开发人员自己去部署生产环境。 + +现在大厂的部署也都实现了自动化,但是流程上还是有一些控制。 + + +首先,部署的不再是程序代码,而是 Docker 的 Image,每次代码合并后 CI 都会自动生成新的 Image,测试也是基于 Image 测试。 + +部署生产环境之前,先在内部的测试环境充分测试。 + +部署生产环境前,需要审批确认,有 Ticket 跟踪。 + +部署时,先部署一部分,监测正常后再全量部署。 + +整个过程都有监控报警,出现问题及时回滚。 + + +如果一切顺利的话,整个生产环境的服务部署过程通常几分钟就完成了,这在以前简直是不敢想象的事。 + +每日站立会议 + +在敏捷开发中,每日站会是非常有名的。在大厂,但凡实施敏捷开发的小组,上班第一件事,就是一起开一个站会,沟通一下项目的基本情况,这也导致会议室越发紧张起来。 + +虽然站立会议什么时间开都可以,但是早上无疑是最好的时机,一天工作的开始,开完会全身心去干活。 + +是不是站着开会其实不重要,重点是要高效沟通反馈。开会时间控制在半小时以内,半小时内不能完成的应该另外组织会议。 + +谁来主持站立会议呢?在敏捷的 Scrum 中,有一个角色叫 Scrum Master(敏捷教练、敏捷大师),主要任务就是保证各种敏捷流程的。 + +所以通常是由 Scrum Master 主持会议,也可以采用轮班制,每个星期换一名团队成员主持。负责主持会议的人,主要职责是组织会议,一个一个环节开展,控制好会议节奏。 + +开会都干什么呢?主要有三个话题: + + +成员轮流发言 + + +每个人轮流介绍一下,昨天干了什么事情,今天计划做什么事情,工作上有没有障碍无法推进。 + +一个成员的发言可能是这样的:“昨天我实现了用户登录模块的前端输入框,今天打算完成后端 API 调用,在实现后端的时候需要 API 组的支持,昨天发现他们文档有问题,不知道该找谁。” + +要注意的是,这过程中很容易偏离主题,比如突然有人提了一句:“我们好久没团建了,是不是该出去玩玩了。”很可能大家都很 high 的讨论起来了,这时候会议主持者要及时打断,记录到“问题停车场”,让下一个人继续,先保证大家能高效完成这一环节。 + + +问题停车场(Parking lot question),把需要进一步讨论的问题暂时放到这里,一会儿再讨论。 + + +通过这样的形式,项目成员可以相互了解任务进展,有困难也可以互相支援,及时发现问题和风险。还有一个重要因素,就是每个人对于自己提出的目标,也会信守承诺,努力完成。 + + +检查最新的 Ticket + + +前面提到所有日常工作都是基于 Ticket 来开展的,这些 Ticket 可能是测试报出的 Bug,也可能是产品经理提交的需求,也可能是其他。 + +所以每天例会都需要检查一下新增的 Ticket,并且要甄别一下优先级,然后决定是放到当前 Sprint,还是放到 Backlog(任务清单)。 + +这个阶段同样要注意不能发散,不要针对 Ticket 的细节展开过多讨论,有需要讨论的同样可以先收集到“问题停车场”,会议组织者需要做好控制。 + + +停车场问题 + + +在这个环节,大家可以针对之前来不及讨论的问题进行讨论,能在会议时间内解决的问题,就马上解决,不能解决的会后再私下讨论或者再组织会议。 + +当然,大厂的流程规范还有很多,在这里我仅列出与敏捷相关的主要开发流程。 + +总结 + +我们知道,在敏捷开发中有很多概念,像 Backlog、持续交付、每日站会等,这些概念最终要变成实践的话,就必须要通过一定的流程规范来保障这些概念的实施。 + +这就是为什么很多公司写代码要求你写自动化测试代码,为什么要用一些像 Jira、禅道这样的项目管理软件来管理任务,为什么要每天开站立会议,为什么要有代码审查。这些都不过是为了保障敏捷的实施。 + +如果你在实施敏捷开发的项目工作,就可以多去观察平时工作中这些和敏捷有关的流程规范,再结合敏捷开发中的知识点,就能很好的帮助你理解敏捷开发,理解这些流程规范背后的理论依据。 + +如果你工作中不是用的敏捷开发,也可以参考本文中提到的一些实践,尝试着试用起来。 + +在下一篇里,我还会以一个具体的项目小组对敏捷的应用为例,继续给你讲讲大厂都在用的那些敏捷方法。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/07大厂都在用哪些敏捷方法?(下).md b/专栏/软件工程之美/07大厂都在用哪些敏捷方法?(下).md new file mode 100644 index 0000000..e0a4e43 --- /dev/null +++ b/专栏/软件工程之美/07大厂都在用哪些敏捷方法?(下).md @@ -0,0 +1,233 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 大厂都在用哪些敏捷方法?(下) + 你好,我是宝玉,我今天继续与你分享大厂的敏捷方法应用。 + +在上一篇文章中,我们一起看了一下大厂和敏捷相关的一些流程规范,同时也为你留了一道思考题: + + +如果每周一个 Sprint,怎么保证每周都有交付,还能保证产品质量? + + +所以在这一篇中,我们就以每周一个 Sprint 的小项目组为例,看看它的日常是怎么应用敏捷开发的。 + +一个应用敏捷开发的小组日常 + +这个小组是做网站开发的,基于微服务负责网站的某一个小模块。标准配置 7 人左右,4 个程序员(至少有一个资深程序员,有架构能力),1 个产品经理(Scrum 里面叫 Product Owner),1 个测试,1 个项目经理(Scrum 里面叫 Scrum Master)。主要负责网站某模块的日常维护。 + +在分工上: + + +产品经理:写需求设计文档,将需求整理成 Ticket,随时和项目成员沟通确认需求; + +开发人员:每天从看板上按照优先级从高到低领取 Ticket,完成日常开发任务; + +测试人员:测试已经部署到测试环境的程序,如果发现 Bug,提交 Ticket; + +项目经理:保障日常工作流程正常执行,让团队成员可以专注工作,提供必要的帮助,解决问题。 + + +在敏捷开发框架下,已经形成了一些很好的敏捷实践,这个小组也是基于 Scrum 方法做过程管理,基于极限编程做工程实践,看板可视化。每周一个 Sprint。 + + +如何完成需求和修复 Bug? + + +这个小组的日常工作,也是围绕 Ticket 来开展的。所有的需求、Bug、任务都作为 Ticket 提交到项目的 Backlog,每个 Sprint 的任务都以看板的形式展现出来。 + +每个人手头事情忙完后,就可以去看板上的“To Do”栏,按照优先级从高到低选取新的 Ticket。选取后移动到“In Progress”栏。 + + +每周一部署生产环境 + + +没有人愿意星期五部署,那意味着如果部署后发现故障,可能周末都没法好好休息了。所以即使程序早已经测试好了,除非特别紧急,否则都会留在下一周再部署。所以部署放在上半周,这样后面遇到问题还有足够的时间去应对。 + +部署很简单,按照流程执行几个命令就可以完成生产环境部署。部署完成后,需要对线上监控的图表进行观察,如果有问题需要及时甄别,必要的话对部署进行回滚操作。但轻易不会打补丁马上重新上线,因为仓促之间的修复可能会导致更大的问题。 + +像敏捷开发这样一周一个 Sprint 的好处之一就是,即使这一周的部署回滚了,下周再一起部署也不会有太大影响。 + + +每周二开迭代回顾会议,总结上个 Sprint + + +每周二的早上,这个小组一般还会预留一个小时的时间,因为常规的站会完成后,还有一个迭代回顾会议 (Sprint Retrospective) 会议,目的是回顾一下在迭代中,团队有哪些做的好的地方,有哪些做的不好的地方。 + +对于需要后续改进的,需要创建相应的 Ticket,加入到 Backlog 中,在后续迭代中改进完善。 + +例如会议上,测试人员反馈说,上一个 Sprint,开发人员上线前几个小时还往预部署的分支里面更新代码,导致测试需要重新做回归测试,但因为时间不够了,没来得及测试完整,导致上线后不稳定,建议以后不要随意在上线前,在部署分支更新代码。 + +对于这样的问题,可能不止一次发生,说明流程上还是存在问题。所以最后大家商定,以后如果不是紧急的修复,就不要在预部署的分支上更新,确实要加,需要和测试先确认。 + +如果会议中要形成涉及项目的决策,最好是通过集体表决的方式决策,尽可能避免独裁式决策。因为敏捷的原则之一是要善于激励项目人员,给他们以所需要的环境和支持,并相信他们能够完成任务。 + + +每周四迭代规划会,计划下周工作 + + +每周四早上,也需要一个小时来组织会议。因为常规站会完成后,还有一个迭代规划会(Sprint Planning Meeting)。这个会议是要大家一起讨论下一个 Sprint 的内容。 + +在开会之前,产品经理和项目经理会商量好 Ticket 的优先级,会议上,大家一起按优先级从高到低的顺序,从 Backlog 中选出下个 Sprint 的内容。 + +团队每个成员都要对候选的下个 Sprint Backlog 中的 Ticket 从 1-5 分进行打分,1 分表示容易 1 天以内可以完成的工作量,2 分表示 2 天内可以完成的工作,5 分表示非常复杂,需要 5 天以上的工作量。 + +这里需要注意,打分时,要大家一起亮分,而不是挨个表态,不然结果很容易被前面亮分的人影响。 + +评估每条 Ticket 工作量的大概流程如下: + + +会议组织者阅读一条 Ticket,可能是用户故事,可能是 Bug,可能是优化任务。同时会询问大家对内容有没有疑问。 + +大家一起讨论这个 Ticket,确保充分理解这个 Ticket。 + +每个团队成员在心中对 Ticket 进行工作量估算。 + +会议组织者确认大家是否都已经确定估算结果,确认后,开始倒数:“3,2,1”,大家一起伸出一只手,亮出代表分数的手指头。 + +如果估算结果存在分歧,出分最高的和最低的各自说明理由,讨论后达成一致。 + + +这种估算工作量的方法有个名字叫估算扑克,因为亮分时用扑克牌亮分而得名,但并非一定要用扑克牌。 + +用这种方式评估工作量有几点很明显的好处: + + +大家积极参与,详细了解需求。相比以前,可能只有当某个功能模块分配到自己头上的时候,才会去详细了解那部分需求,而其他开发人员可能都不了解这部分需求。 + +工作量是由实际参与开发的成员作出评估,往往更准确也更容易被接受。以前项目经理代为估算的模式,很容易不准确,或者让开发人员抵触。 + +促进成员的交流和经验分享。我们知道一般经验浅的新手估算工作量都会偏乐观,而经验丰富的老手则会更准确,通过这种方式,新手可以向老手学习到很多工作量估算甚至技术实现的经验。 + + +所以,在经过几个 Sprint 的磨合后,一般一个团队在每个 Sprint 的产出是比较稳定的。比如说这样一个 7 人的小团队,一个 Sprint 预计可以完成 20-30 分的 Ticket。 + + +每周五分支切割 + + +周五标志着一周的工作要结束了,所以下班之前(4 点左右),要做 branch cut(分支切割),也就是要把当前主干上的代码,克隆到一个分支(branch)上。 + +为什么要做分支切割这一步操作呢? + +经过一周的开发,master (主干)已经合并了不少新的 PR(Pull Request,合并请求),但是如果你直接把 master 的代码部署到生产环境,肯定还是不放心,毕竟自动化测试还是不能完全代替专业测试人员的测试。 + +所以我们需要把 master 上的代码部署到测试环境进行测试,并且对测试出来的 Bug 进行修复,直到稳定下来为止。由于 master 还需要一直合并新的功能,所以最好的方式就是每次 Sprint 结束,从 master 创建一个分支版本出来,然后基于这个分支部署和修复 Bug。 + +所以需要基于主干做一个 branch cut,创建一个预部署的分支,将预部署分支的代码部署到测试环境,这样在下周,测试人员就可以测试新的版本。测试验收通过后,预部署分支的代码会部署到生产环境。 + + + + +每周轮值 + + +小组里面除了日常开发工作以外,其实还有不少琐碎的事情,比如每周部署生产环境,每天部署测试环境,每周的 branch cut(分支切割),回答其他小组的问题,主持每日会议(不一定需要项目经理),这些事情如果都是一个人做难免会有些枯燥。 + +在敏捷开发中,鼓励发挥每个成员的主动性,所以每周轮值是一个不错的方式,可以让每个人都有机会去体验一下,帮助团队完成这些事情,更有集体荣誉感和责任感。 + +一些问题解答 + +上面只是选取的一个项目小组的日常,所以估计你看完还会有些疑问,在这里我把可能的问题列一下,先行解答一下。 + + +基于这种敏捷开发的方式加班多吗? + + +其实加不加班,绝大部分时候和是不是敏捷开发没关系的,还是看项目组的情况。 + +通常来说,基于敏捷开发一个 Sprint、一个 Sprint 迭代,节奏还是比较稳定的,这个 Sprint 做不完的任务也可以顺延到下个 Sprint,不影响发布。不像瀑布模型那样前松后紧,后期加班可能性大一些。 + + +一周一个迭代怎么保证质量? + + +以前我在使用迭代模型开发时,一般是 4 周左右的迭代周期,2 周就是极限了,所以最开始看敏捷开发用 1 周的迭代周期,心中也有疑惑,1 周时间又要开发又要测试,怎么保证质量? + +实际实践下来,发现 1 周一个 Sprint 确实可行,而且质量也可以有保障,这里面有几个因素: + +(a) 有足够比例的自动化测试代码,可以很好地保证质量。当用户的主要功能都通过自动化测试覆盖时,基本可以保证主要功能流程不会出问题。 + +(b) 一个 Sprint 开发完成后,并不马上部署生产环境,而是先部署到测试环境,会有 1 周时间测试。 + +(c) 有专业的测试人员进行测试,并非完全依赖自动化测试。有时候一些大的功能更新,甚至会组织全组成员一起测试,以弥补测试人员不足的情况。 + +在一个 Sprint 开发结束后,并不马上部署生产环境,而是先部署测试环境测试。 + + + +也就是说,虽然是 1 周的 Sprint,但是其实还有 1 周的时间进行测试。每个 Sprint 不仅开发新功能,还要同步修复以前版本的 Bug。 + +这样基本上可以保证有好的质量。而且这种 1 周的迭代,可以保持每周都有内容更新,还有个好处就是每周更新的内容不多,出现问题的话,很容易就定位到是什么地方导致的问题。 + + +基于敏捷开发如何做计划? + + +大厂里面通常会在上一年底确定第二年整年的大的开发计划,并确定上线的时间范围,每个季度再根据情况做一些调整。 + +这些大的计划最终会变成具体的开发任务,一个大的开发任务,会分拆到各个部门,各部门再将任务分拆到各个项目组。基于敏捷开发的话,主要就是看把这些开发任务放到哪几个 Sprint 去做,并且确保在规定的时间范围内完成。 + +至于工期的估算,在迭代规划会上会对每个 Ticket 进行打分,根据分数可以预估有多少工作量,要花多少时间。 + + +如何沟通协作? + + +组和组之间的沟通协作,主要通过邮件、会议、内部沟通工具,最终任务会以 Ticket 的形式体现。 + +团队内部的话,因为都在一起,所以沟通起来很方便,每天站立会议都是很好的沟通方式。 + +在敏捷开发中,有一种实践叫结对编程,就是两个程序员在一台电脑上一起工作。这个一直争议比较大,但是如果用来两人一起排查一些问题,或者是资深程序员带新手程序员,则是一种非常好的协作方式。 + + +上面介绍的实践案例和标准 Scrum 有什么不同? + + +我上面介绍的内容,确实和标准的 Scrum 有不少不一样的地方。 + +首先是角色名称不一样,在 Scrum 里面是分 Product Owner、Scrum Master 和 Team 三种角色,而在这个案例中是产品经理、项目经理和团队成员,但其实只是名字叫法不一样。 + +还有要注意一点,就是传统的项目经理,会是偏控制型角色,Scrum Master 则更多是一种服务型的角色,主要职责是保障敏捷流程的执行,以及提供必要的帮助,很多团队的决策就是采用集体决策的方式。 + +另外,Scrum 有四种会议,除了前面介绍的三种:每日站会(Daily Scrum)、Sprint 计划会(Sprint Planning)和 Sprint 回顾会议(Sprint Retrospective),其实还有一种会议是 Sprint 评审会(Sprint Review)。 + +Sprint 评审会的作用是让客户审查 Sprint 的完成结果。因为上面这个小组并没有直接的客户,都是完成产品经理提交的需求,而且沟通紧密,所以没有安排专门会议。 + +这个小组的站立会议并不是“标准”的站立会议,Scrum 的站立会议通常只有 15 分钟,并且只有轮流发言环节。 + +这里增加的每天审查 Ticket 环节,主要是为了将优先级高的 Bug 修复之类的 Ticket 放到当前 Sprint,及时响应,及时处理。有的项目组没有这个环节,是由测试人员或者 Scrum Master 直接将 Ticket 放到看板。 + +这个小组并没有使用用户故事来开发需求,而是由产品经理事先写好需求文档。在上一篇文章里面,提到了 Scrum 采用用户故事的方式,分拆需求,减少繁重的需求文档,在实现的过程中再沟通确认需求。 + +这是 Scrum 推荐的一种方式,也是一种高效的方式,但并不代表这是唯一的方式。如果有产品经理,可以提前几个 Sprint 就将需求文档写详细,一样可以达到高效的理解需求的效果。 + +那么这样还算敏捷开发么? + +其实在《05 敏捷开发到底是想解决什么问题?》就有讲过,是不是敏捷开发,核心并不是应用了哪个方法,而是应用的时候,是否遵循了敏捷开发的价值观和原则。 + +比如说非标准的站立会议效率更优,那么就应该采用非标准的站立会议;如果有专业产品经理事先做好需求分析,可以达到解释清楚需求的效果,就没必要一定要用用户故事来理解需求。 + +总结 + +上一篇文章我们讲了大厂里和敏捷相关的一些流程规范,这一篇又讲了一个小组是怎么应用敏捷开发来开发项目的。 + +现在看上一篇文章中我留的思考题:如果每周一个 Sprint,怎么保证每周都有交付,还能保证产品质量?想必你已经有了答案。 + +要保障质量,还是离不开充分的测试,不仅要有自动化测试,还要辅助一定量的人工测试。敏捷开发虽然求快,但是不代表应该牺牲质量。 + +其实,大厂的敏捷实践并不神秘,关键是分而治之,最终团队小,项目小,所以才可以敏捷起来。大厂会注重流程和工具的应用,通过 Ticket 的方式来管理和跟踪开发任务,通过自动化的方式来部署。 + +大厂的敏捷实践,一般是基于 Scrum、极限编程和看板,针对各自项目组的特点,会有所侧重有所调整,在遵循敏捷的价值观和原则的前提下,做到高效使用。 + +希望上面介绍的敏捷应用,能对你理解敏捷开发有所启发,帮助你优化改进日常项目流程。还有要注意的一点就是,没有万能的开发模式,只有适合项目的开发模式,最重要的还是要摸索出一套适合你自己项目特色的开发模式。 + +限于篇幅,对于 Scrum、极限编程和看板,我并没有展开细讲,还需要大家自己辅助看看书,我在《学习攻略 | 怎样学好软件工程?》和《05 | 敏捷开发到底是想解决什么问题?》文章中也列了一些参考书籍。 + +留言区有同学推荐的文章《天下武功,唯快不破—新时代敏捷项目管理之道》对敏捷开发也有很不错的讲解,推荐阅读。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/08怎样平衡软件质量与时间成本范围的关系?.md b/专栏/软件工程之美/08怎样平衡软件质量与时间成本范围的关系?.md new file mode 100644 index 0000000..bf84da5 --- /dev/null +++ b/专栏/软件工程之美/08怎样平衡软件质量与时间成本范围的关系?.md @@ -0,0 +1,211 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 怎样平衡软件质量与时间成本范围的关系? + 你好,我是宝玉,我今天与你分享的主题是:怎样平衡软件质量与时间、成本、范围的关系。 + +在《从软件工程的角度解读任正非的新年公开信》这篇文章中,我已经提到了“软件项目管理金三角”的概念。由于这个内容对于软件工程来说,非常之重要,所以我今天特别展开再讲一下。 + +你会发现,在实际的软件项目中不乏这样的例子: + + +一个项目,正常估算,要三个月才能完成,但是老板或客户要压缩到一个月完成,而你不知道如何说服他们; + +项目开发一半,产品经理告诉你,有一个非常紧急的功能,要增加到这个版本中,你不知道该不该拒绝,或者如何拒绝; + +听说迭代模型很好,你也尝试使用迭代模型,但是每次迭代时间到了还是完不成,只能把迭代时间延长,最后又做回传统的瀑布模型了; + +你们组用瀑布模型开发,一到项目后期总免不了加班加点赶进度,为什么他们用敏捷开发的加班要少一些? + + +其实,这些日常项目中涉及时间、成本和范围的问题,都离不开“软件项目管理金三角”的概念。 + +掌握好这个知识点,学会平衡软件质量与时间成本范围的关系,可以帮助你更好的驾驭项目中的各种问题,也可以帮助你更好地理解软件工程中各个模型,尤其是瀑布模型和敏捷开发。 + +什么是软件项目管理金三角? + +在现实生活中,我们都知道,做产品想“多、快、好、省”都占着,是不可能的,最多只能选两样。 + +想要便宜和质量好,就要花时间等;想要快还要质量好,那就得多花钱;想要又便宜又快,那就得接受难用、质量差。 + + + +而在软件项目中,也有一个类似的平衡关系,就是软件质量(产品的质量,客户的满意度)与范围(需要实现多少功能)、时间(多久可以完成)、成本(花多少钱)四个要素之间的平衡。 + + + +上面这个图就是著名的项目管理金三角(以下简称“金三角”),三条边分别是时间、成本和范围,中间是质量。 + +为什么四个要素,是“质量”放在三角形的中间? + +因为软件工程的目标就是要构建和维护高质量的软件,所以项目的质量是高于一切的。也就是说,“质量”这个因素一般不会妥协,因此把“质量”放在三角形中间,然后在时间、成本、范围这三条边之间寻求平衡。 + +质量往往也是其他三个因素平衡后结果的体现,想要做的快、成本低、功能多,最后一定是个质量很差的产品。 + +如何应用“管理金三角”做决策? + +我在专栏中常用“道术器”来比喻软件工程中的各个知识点,“金三角”无疑就是“道”级别的。 + +项目管理其实就是项目中一系列问题的平衡和妥协,而“金三角”理论则为我们的平衡提供了理论指导,了解这三个因素分别对项目其他方面产生的影响,可以帮助你在做决策时进行权衡取舍。 + +当你接手一个项目,项目的进度、成本和范围指标很容易可以跟踪到。有了这些信息,你就可以及时发现问题,调整“金三角”的边,及时解决,以防止这些小问题发展成大问题。 + +我来举两个例子,看看“金三角”是如何应用的。 + + +老板要压缩项目时间怎么办? + + +当项目经理,常遇到的问题之一就是时间被压缩,比如文章中开头举的例子,老板问我一个项目多久能完成,我按照经验,觉得要三个月,老板觉得三个月太久了,要砍到一个月就上线。 + +最开始的时候,我就是据理力争,说这不科学,肯定不行呀。老板说时间点很重要,必须要一个月上线。结果就是大家吵得不欢而散,最后还得加班加点做,质量也不好。 + +后来我学乖了,先用“金三角”知识分析了一下:老板希望时间是 1 个月,也就是说时间这条边被缩短了,那么结果就是会影响到另两条边:范围和成本,如果另外两条边可以调整,也不是不可以。 + +于是再遇到这种问题,我就换了一种方式跟老板沟通:“一个月也不是不行,就是我们的需求调整一下,第一个版本只能做一些核心功能,剩下的后面版本再加上(调整范围)。另外还得给我加两人,不然真做不完!(增加成本)” + +这样的方案一提出来,就好沟通多了,最后重点就变成了砍多少功能和加多少人的事情了。 + + +产品经理要临时加需求怎么办? + + +在文章开篇我提到一种情况,项目开发一半,产品经理告诉你,有一个非常紧急的功能,要增加到这个版本中,怎么办?我们拿“金三角”知识先套用一下。 + +增加需求,也就是范围这条边要增加,那就必然对成本和时间这两条边造成影响,要么延期,要么增加成本。 + +面对这种临时加需求的情况,我们也不需要直接说不能加,而是清楚的让产品经理认识到这样做的后果:进度延期,需要更多的成本。如果这个功能真的太重要,可以接受延期,也不是不可以接受,那就重新制定新的项目计划好了。 + +所以你看,如果我们能应用好“金三角”的知识,很多软件项目中问题,一下子就多了很多方案可以选择了。 + +瀑布模型和敏捷开发如何平衡时间成本范围的关系? + +除了可以将“金三角”的知识应用在软件项目中,还可以应用它来理解和应用软件工程中的开发模式,尤其是瀑布模型和敏捷开发这两种典型的开发模式。 + +瀑布模型有严格的阶段划分,有需求分析、系统设计、开发和测试等阶段,通常在开发过程中不接受需求变更,也就是说,我们可以认为瀑布模型的范围是固定的,其他两条边时间和成本是变量。 + +所以使用瀑布模型开发,如果中间发现不能如期完成进度,通常选择的方案就是延期(加班),或者往项目中加人。 + + + +我们再来看敏捷开发,敏捷开发中,是采用固定时间周期的开发模式,例如每两周一个 Sprint,团队人数也比较少。所以,在敏捷开发中,时间和成本两条边是固定,就只有范围这条边是变量。 + +这就是为什么在敏捷开发中,每个 Sprint 开始前都要开 Sprint 计划会,大家一起选择下个 Sprint 能做完的任务,甚至于在 Sprint 结束时,没能完成的任务会放到下个 Sprint 再做。 + + + +这时候再想想文章开头我们提到的问题: + + +听说迭代模型很好,你也尝试使用迭代模型,但是每次迭代时间到了还是完不成,只能把迭代时间延长,最后又做回传统的瀑布模型了。 + + +你现在是不是就明白了:如果不能固定“时间”这条边,就会导致时间也成了变量,迭代自然无法正常推进。 + +如何平衡好软件质量与时间成本范围的关系? + +那么怎么样才能平衡好软件质量与时间成本范围的关系呢? + +前面我们说日常生活中“多、快、好、省”最多只能选两样,其实如何平衡好软件质量与时间成本范围的关系也是一样的道理,我们只能最多选择两样,然后在另一边或者另两条边去寻找平衡。 + +所以第一件事就是:从时间、成本和范围这三条边中找出来固定的一条或者两条边,再去调整另一条边。 + +下面,我来分析一些案例,帮助你更好地理解。 + + +淘宝网站第一个版本是怎么做到一个月上线的? + + +这个故事其实我是从极客时间《从 0 开始学架构》专栏看来的,李运华老师在《架构设计原则案例》一文中举了淘宝网站的例子: + + +2003 年 4 月 7 日马云提出成立淘宝,2003 年 5 月 10 日淘宝就上线了,中间只用了一个月时间。 + + +好,如果你是当时的淘宝网站负责人,马云要你一个月上线淘宝网站,功能还不能少,你怎么办? + +第一件事当然是先应用“金三角”分析一下:时间这条边被固定了,只能一个月;功能也不能少,范围这条边也限制住了,那就只能在成本上想办法了。要么一下子雇很多牛人,要么直接买一个现成的电子商务网站,然后修改。 + +显然,直接买一个网站,再雇一堆牛人的方案最好,所以淘宝网站就这样在一个买来的网站基础上,由一堆牛人快速搭建起来了。归功于淘宝网站的快速上线,刚推出后,正好赶上“非典”,网购需求增大,淘宝网一下子就火爆起来了。 + +从成本角度我们还有可以去做的,比如说有同学在看完《06 大厂都在用哪些敏捷方法?(上)》这篇文章后,也想在团队里面推行代码审查和 CI,但是苦于搭建这一套 git+CI 的系统没有经验,不知道该如何下手,怎么办呢? + +我的建议就是刚开始就没必要自己去折腾了,买一套 GitHub 的企业版,加上支持 GitHub 的商业 CI 系统,花不了多少钱,而且可以节约大量搭建这种系统的时间。 + + +极限编程是怎么做到“极限”的? + + +前面在介绍敏捷开发的时候,也提到了极限编程(eXtreme Programming,XP),是目前敏捷开发主流的工程实践方法,极限编程的“极限”(Extreme),意思就是如果某个实践好,就将其做到极限。比如: + + +如果做测试好,就让每个开发人员都做测试 ; + +如果集成测试重要,就每天都做几次测试和集成 ; + +如果简单的就是好,那么我们就尽可能的选择简单的方法实现系统功能 ; + +…… + + +极限编程的“极限”理念,产生了很多优秀的实践方法,例如持续集成、自动化测试、重构等。 + +这些实践帮助我们可以在短时间的迭代中,产生高质量的代码。我们用“金三角”的理论来分析一下极限编程在 Sprint 中的应用。 + +在一个 Sprint 中,计划好了当前 Sprint 要做的工作内容后,那么极限编程怎么帮助我们提高代码质量呢? + +一个 Sprint 要做的内容是确定的,相当于成本和范围这两条边都固定了,时间这条边就成为变量了。要么通过加班延长工作时间,要么通过提升效率、减少浪费帮助我们提升时间利用率。 + +极限编程,就是通过帮助我们提升效率和减少浪费这方面来做的。比如说: + + +持续集成,通过自动化的方式帮助我们部署,节约了大量需要人去手动部署的时间; + +自动化测试,通过自动化测试,节约测试时间,另外,有了自动化测试,可以避免后面修改代码产生 Bug,减少了大量的浪费; + +只做刚好的设计,避免设计时考虑了太多不必要的可能,造成浪费。 + + +其实我们在项目中也有很多地方可以借鉴这种思路,比如说写代码的时候,少自己造轮子,多使用成熟的开源或者商业组件,可以提升效率;比如把需求想清楚搞清楚再去开发,可以减少很多返工的时间成本! + + +MVP 模式是怎么诞生的? + + +这些年流行的 MVP(minimum viable product,最小化的可行性产品)模式,是一种快速推出产品的模式:一开始只推出最核心的功能,满足用户最核心的需求,然后在用户的使用过程中收集反馈,进一步升级迭代。 + +这种模式怎么诞生的呢?还是应用“金三角”理论,要快速推出产品,还想成本不用太高,那就意味着时间和成本这两条边是固定的,剩下范围这个变量。 + +所以最简单有效的办法就是砍掉一些重要性不那么高的功能需求,只保留最核心的需求。通过缩小范围的方式,达到快速推出高质量产品的效果。 + +类似的道理,我们程序员,在遇到很多功能忙不过来的时候,可以主动的去和项目经理协商,砍掉一些不那么重要的需求,把精力放在核心需求上,保证项目可以如期上线。 + +总结 + +其实,要平衡好软件质量与时间成本范围的关系并不难,你只需要记住,最重要的是根据“金三角”的三条边,找出来固定的一条或两条边,然后去调整剩下的边,达到平衡。 + +软件项目的“金三角”很多人都知道,主要是不知道如何应用到实际的项目中,希望这篇文章能为你提供一些思路,帮助你在项目中真正应用好这个非常实用的知识。 + +课后思考 + +关于今天的内容,邹欣老师在《构建之法》书中,提出了一个很好的问题。我也在这里列出来,希望你可以思考一下。 + + + +顾客对于要交付的软件和服务,都是有很多美好的需求的, 用户希望软件开发的又快, 又便宜 (人工便宜),质量又好, 最好是免费的。 那么,如果只满足部分的需求, 我们会得到什么样的软件呢? + +例如,上图的 ① 说明, 如果希望软件做得又快,又低成本(人工便宜), 不考虑其他要求, 那么,我们会得到大致什么样的软件呢? + +例如,上图的 ⑤ 说明, 如果希望软件是免费的,而且要很快交付,越快越好, 那么,这样的软件有什么特点呢? + +请把 ① 到 ⑦ 的需求组合会导致什么样的软件, 会出现什么样的问题, 都列出来。 + +另外,对于质量和时间成本范围的平衡,你有没有什么应用的案例?你对你当前项目的时间、范围和成本都清晰吗?有没有什么可以做的更好的地方?欢迎在留言区与我分享讨论。 + +感谢阅读,如果你觉得这篇文章对你有一些启发,也欢迎把它分享给你的朋友。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/09为什么软件工程项目普遍不重视可行性分析?.md b/专栏/软件工程之美/09为什么软件工程项目普遍不重视可行性分析?.md new file mode 100644 index 0000000..544384c --- /dev/null +++ b/专栏/软件工程之美/09为什么软件工程项目普遍不重视可行性分析?.md @@ -0,0 +1,193 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 为什么软件工程项目普遍不重视可行性分析? + 你好,我是宝玉,我今天分享的主题是:可行性研究, 一个从一开始就注定失败的跨平台项目。借此来与你一起讨论,为什么软件工程项目普遍不重视可行性分析。 + +如果你随手拿起本软件工程教材翻翻,第一章一般都是讲“可行性研究”的,呈现顺序仅次于“绪论”,可见其重要性。 + +“可行性研究”通常讲的是如何科学地论证项目的可行性,以及这个项目是不是值得做。这个知识点比较简单,落实到期末考试的题目上,一般只是一道像这样的选择题或填空题: + + +“可行性研究主要从哪几个方面进行?” + + +这个题目要回答的话也不难,记住答案即可。 + + +对于软件项目的可行性研究,主要从以下几个方面入手: + +经济可行性; + +技术可行性; + +社会可行性。 + + +看上去这么简单的知识点,到底重要在哪里呢?我们先来看一个真实的案例。 + +2015 年的时候,Facebook 推出了一个跨平台的移动端解决方案 React Native,只要用 JavaScript 一门语言就可以将写好的代码运行于 iOS、Android 移动平台。 + +所以在 2016 年的时候,某著名大型互联网公司的移动部门负责人非常看好这个技术,专门成立了项目组,用了不少人力,花了大半年时间将移动端 iOS、Android 产品迁移到 React Native 技术框架上。 + +就在项目快要上线的时候,法务部门却发现 React Native 的开源许可协议是“BSD+ 专利”。那这个“BSD+ 专利”的许可协议是什么呢? + +BSD 的许可协议本身是开放的、没有限制的,但 Facebook 在此基础增加了一个“专利”协议。也就是说,如果对 Facebook 及其子公司提出专利诉讼,不管诉讼的项目是否与该协议有关,用户的所有专利权利也都会自动终止。 + +也就是说,如果未来该公司因为专利问题与 Facebook 产生纠纷,那么该公司将会无条件输了官司。 + +而目前该公司和 Facebook 是有竞争关系的,所以法务部门为了避免未来可能的纠纷,不得不叫停这个项目。而此时,他们在这个项目上投入的大量人力财力,相当于全打水漂了。 + +即使后来 2018 年的时候,Facebook 把 React Native 的开源协议修改为友好的 MIT 协议,也为时晚矣。 + +你看,如果在立项之前,就让法务部门帮助评估一下社会可行性(具体到这里,也就是法律方面的可行性),该公司就可以避免很大一笔损失。 + +类似的案例其实不在少数。许多公司老板或者部门负责人不是很懂技术,天马行空想到一个点子,或者看到某个热门技术(比如说团购、共享经济、人工智能、区块链),不做可行性研究就直接立项去做,耗费了不少人力、物力和时间成本不说,最后项目也不得不以失败告终。 + +为什么软件项目很少做可行性研究? + +可行性研究不是软件项目的专利,在很多其他工程领域,项目正式启动前,都会有可行性研究这一环节,而且一般都会请一家甚至多家专业的评估机构帮助做可行性分析,并出具可行性研究报告,然后项目方来决定是不是立项。 + +拿建筑工程来说,你要在某条街上盖房子,却不做可行性研究,那么如果这条街两年后要拆迁,那就意味着你的房子也会面临被拆掉的命运,那损失就大了。 + +为什么在软件工程领域,可行性研究就不是很灵了?如果你也经历过或者听说过一些失败的软件项目,不知道你有没有想过,为什么这些软件项目很少有做可行性研究的?如果你有机会就这个问题去做一下调查,很可能会得到下面这些答案。 + + +“因为我们是软件项目,所以我们很特殊。” + + +“我们很特殊”,这句话听着有没有很熟悉?软件项目确实有和其他工程项目不一样的地方。 + +比如说软件项目很抽象,以至于在立项之前对于问题的描述(需求)和解决方案(技术方案)通常都是模糊不清的,只有随着项目的推进,才能逐步搞清楚需求。 + +而可行性研究是基于问题和解决方案来分析的,因此这有点像“先有鸡还是先有蛋”的问题:你得先立项才能慢慢搞明白需求是什么,然后才能有解决方案;而你只有搞明白需求是什么,以及解决方案是什么,才能去做可行性研究。 + +但“我们很特殊”,不能成为不做可行性分析的借口,可能项目需求最开始是模糊不清的,还不具备可行性研究的条件,那么等到项目有了一定的进展,需求逐步明确后,要继续对可行性做研究。 + +如果发现方案不具备可行性,也应及时调整方案或停止项目以止损。 + + +“老板拍板的项目,明知道不可行也得硬着头皮干呀!” + + +这个问题要分类讨论,有两种情况。 + +第一种情况,多半是由于老板或者项目负责人控制决策权,且对于不同意见容忍度较低。底下人不敢提不同意见,明知道不对也只能执行。 + +如果你是项目执行人员,不能参与决策,但觉得项目明显不可行,我仍然建议你尽可能站在专业的角度给出科学的分析,通过合理的方式反馈意见。毕竟,项目如果失败了,你也一样可能遭受损失。 + +如果你就是老板或者项目负责人,则应该建立可行性研究的意识,并理性听取不同意见,科学客观地进行可行性分析,以便有效降低项目失败概率。 + +第二种情况,老板或者项目负责人能接触到的信息更多、更全面,同时还有战略上的一些考虑,所以下面执行的人觉得不靠谱,并不代表真的不靠谱。 + +举个例子,2009 年阿里巴巴决定做阿里云的时候,公司反对者占绝大多数,只有马云和王坚等少数人觉得这个项目可行,而且必须做。最后,事实证明他们是对的。 + +所以有时候,也不要着急下结论,可以换个角度思考下,也许是你因为条件限制还没想清楚。 + + +“软件项目是鼓励创新、鼓励试错的,可行性研究会阻碍创新!” + + +这也是一种很典型的错误观点,认为创新就可以不做可行性研究,否则会阻碍创新。实际上可行性研究和创新从来就不是矛盾的,它反而可以帮助你提前过滤掉那些不靠谱的创新想法,提前发现可能的风险。 + +想一想文章开头关于 React Native 开源协议冲突的案例,虽然是一个创新性的项目,却未绕过开源协议引起的法律纠纷。如果当初有法律方面的可行性研究,完全可以改用开源协议更友好的同类开源技术,避免项目的失败。 + +如何做好可行性研究? + +前面,我们讲了可行性研究在软件工程中的重要性,也帮你厘清了几个常见的困惑,接下来我们来看看“如何做”的问题。 + +其实,当你决定要做可行性研究,你就已经成功一半了,怎么做反而是相对简单的部分! + +软件工程的教材里面,通常会讲如何写可行性研究报告,很繁琐,要撰写诸如引言、背景、定义等内容。在这里,我们关注的重点是,软件工程中是如何去做可行性研究的。如文章开头所说的,通常从三个方面着手做: + + +经济可行性。从成本和收益角度分析,看投入产出比。不仅要分析短期利益,还要分析长期利益,看是不是值得做。 + +技术可行性。软件项目最终是需要人通过技术来实现的,所以要分析技术上是不是可行,如果有技术上解决不了的问题又能否规避。 + +社会可行性。社会可行性涉及法律、道德、社会影响等社会因素。比如,触犯国家法律的事情肯定不能做;产品如若不符合道德标准,可能带来较大的社会负面影响,那么也要慎重考虑。 + + +仍然以文章开头提到的 React Native 项目为例,我们从这三个方面出发,来做一个简单的可行性研究。 + +先来看看经济可行性。按照投入成本和收益估算,我们在此仅做一些简单假设: + + +这个项目要投入 10 个人,每个人的人力成本预计是 10000 元 / 月,预计要花半年时间上线; + +每个人在项目实施过程中,所需要的硬件和软件成本预计在 1000 元 / 月; + +每年该部门预计完成 10 个项目,每个项目收益预计 1000000 元; + +该项目导致 2 个项目延期半年; + +该项目预计可以节约项目成本,所以每个项目收益可以提高 50000 元; + +该项目可以让部门每年完成的项目提升到 12 个。 + + +预计投入 1660000 元 / 半年,投入使用后每年可以产生约 2600000 元的收益,不到一年可以收回成本。 + + + +以前我写程序的时候并没有多少成本意识,觉得就改改代码而已,后来发现真要把工资和用的时间算一下,其实成本还是不低的。 + +就像上面这样一个 10 个人的项目,半年下来就要花一百多万。当然如果项目成功的话,不到一年就可以收回成本,而且后面还会持续创造价值。所以从经济可行性分析,还是可行的。 + +然后再来看看技术可行性。 + + +从技术本身来说,经过一年多的发展,技术已经成熟稳定,并且已经有了几个成功案例。 + +从人员储备来说,部门已经有 5 名成员有 React Native 项目经验,其他人员可以通过 3 个月左右的培训上手。 + + +从风险角度看,部分老的安卓机型无法支持,但是这部分机型占有率非常低,可以不予考虑。另外,部分视频组件需要自己实现,技术上可行,需要把这部分的开发任务放入项目计划中。 + + + +技术可行不可行,关键还是在人。就算技术成熟,如果短时间内找不到人来做,也是有很大风险的。同时也要评估可能存在的技术风险,像本例中的设备兼容问题,如果不兼容设备很多,那技术就不可行了。 + +像这个项目,已经有一定的人才储备,不会成为技术上的瓶颈,另外不支持的设备只占极少数,可以忽略不计,所以总体上技术还是可行的。 + +最后再来看看社会可行性。 + + +道德可行性是没有问题的,不会有任何不良道德行为。 + +社会影响方面,也没有负面影响。 + +法律可行性上,项目本身不违反国家法律法规。版权上,React Native 采用 BSD+ 专利开源协议,存在法律风险! + + + + +至此,我们可以得出结论,这个项目从经济可行性和技术可行性上来说,都没问题,但是社会可行性方面存在很大风险。于是,接下来我们和公司法务部门进一步沟通,确认并达成一致,最好的结果是暂时冻结该项目。 + +就这样,我们通过项目启动前的可行性研究,及时冻结项目,为公司避免了人力、物力和时间上的浪费。而且,这样一来,我们还有了及时寻找其他解决方案的时间和机会。 + +总结 + +可行性研究是项目启动前很关键的一步,可能最早帮你发现风险,甚至避免损失,千万要重视起来。就如我前面所说的: + + +哪怕你做的可行性研究不能改变决策,最后项目结束的时候,和当初做的可行性研究做一下对比,也都是非常宝贵的项目经验积累。 + + +结合《02 工程思维:把每件事都当作一个项目来推进》一文的内容,我建议你把每件事都当作一个项目来看,因此每次决定做一件事前,不妨先做一个“可行性研究”。 + + +比如说,你打算要做一个功能模块的性能优化,不妨先列一下成本和收益(经济可行性),看看你投入的时间精力,再看看最终带来的性能提升效果,来判断下是不是值得做。 + +比如说,你要换工作,那就列一下你工资提升带来的收益(经济可行性)。最好换算成时薪,看看长期是不是真的更合算,因为有时候虽然工资多了一点,但加班太多反而得不偿失。 + + +最重要的,你要关注一下法律上的风险(社会可行性),想一想你有没有签竞业协议,新工作会不会因违反竞业协议给你带来巨额赔偿问题。 + +最后,我想给你一个小建议:如果可行性研究并不能给你一个很明确的结果,也可以考虑小范围试点,先实现一个最小化可行产品,等验证了可行性,再逐步加大投入。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/10如果你想技术转管理,先来试试管好一个项目.md b/专栏/软件工程之美/10如果你想技术转管理,先来试试管好一个项目.md new file mode 100644 index 0000000..8fa4d79 --- /dev/null +++ b/专栏/软件工程之美/10如果你想技术转管理,先来试试管好一个项目.md @@ -0,0 +1,201 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 如果你想技术转管理,先来试试管好一个项目 + 你好,我是宝玉,我今天与你分享的主题是:如果你想技术转管理,先来试试管好一个项目。 + +技术转管理,是很多技术人员的梦想,所以经常有人问我,怎么样才能转型管理? + +项目管理,是最基础的管理,既要管理一个项目,又要协调整个团队一起,完成共同的目标。 + +我的管理转型就是从项目管理开始的,在从技术转型项目管理的过程中,让我从以前专注于局部技术实现,逐步转向关注项目整体;从个人的单打独斗,到借助整个团队的力量一起完成一个项目。 + +一直到后来做开发总监要去管理整个开发部门,发现还是一样绕不开要管理项目,只是从直接管项目变成了间接管项目而已。 + +所以我一般会建议:如果你想技术转管理,先试试管好一个项目。项目管理通常是技术人员转型管理的第一步,也是非常关键的一步! + +技术人员转型管理的障碍是什么? + +很多人认为技术人员是不适合做管理的,包括网上也有很多对程序员的刻板印象,比如说:极客、木纳、不善交际、头发少、穿格子衫…… + +而我了解的程序员却不是这样子的,他们都很聪明,学习能力强,而情商这些其实和其他职业群体是没有区别的。 + +那么为什么程序员会给人这种刻板印象呢? + +一方面原因是这个群体勇于自黑,不介意这些印象;另一方面则是他们过于专注技术实现,沉浸于细节中,而忽视了其他事情。 + +程序员总是想着如何技术实现、用什么语言框架、怎么提高效率……要钻研技术,这些是非常好的优点,但是要转管理,这反而会是一种障碍。 + +因为管理,最重要的一点就是大局观,要能从整个项目的角度,从整个团队的角度去思考,去确定方向,去发现问题,对问题及时解决及时调整。 + +但是当你把注意力都放在技术细节上,就容易忽视其他事情,例如和其他人之间的沟通、不关心当前项目进展。 + +就像有人说的: + + +关注细节的,是工程师; + +关注过程的,是项目经理; + +关注结果的,是老板。 + + +所以,如果你要技术转管理,可以先从管好一个项目开始。这也是为什么我在专栏一开始,就建议你要逐步转变思维,从技术思维到工程思维,不要仅仅局限于自己负责的那一个小模块,而是要多从项目的整体去思考。 + +怎么样去管理一个软件项目? + +软件项目管理涉及知识不少,既有传统的项目管理知识,又需要掌握软件工程的知识,所以很多人一谈到项目管理就觉得很难很复杂。 + +我在专栏中一直强调“道、术、器”,对于很多知识,如果我们能总结出其中的“道”,再去看很多问题,其实就没那么复杂了。 + +就软件项目管理来说,“道”就是管好人、管好事。如果从这两个维度去看如何管理项目,就会发现其实并不难,有很多“术”可以为我们所用。 + +怎样管好软件项目中的人? + +软件项目管理的一个维度是管人。项目管理中的人,主要涉及两类:客户和项目成员。 + + +管理好客户的预期 + + +客户,就是会使用你软件产品的人,通常也是给你项目出钱的人。 + +对于客户的管理,就是对于客户期望值的管理,如果你项目的结果高于客户的期望,那么就可以说你的项目就是成功的,如果没有达到客户的期望,可能就是不成功的。 + +想要满足客户预期,通常来说,就是你能在项目的质量、范围、时间和成本上达到要求。 + + +质量达标:交付产品是高质量的,满足客户需求的。 + +完整交付:按照约定的功能范围交付最终产品。 + +按时交付:项目按照客户认可的进度完成。 + +预算之内:在预算内完成项目。 + + +这四个要素,并不是说必须都要满足,其实很多时候是可以协商的,重点是要达到一个平衡,怎么达到平衡?具体你可以参考《08 怎样平衡软件质量与时间成本范围的关系?》,我已经在这篇文章中进行了详细的解答。 + + +用流程和规范让项目成员一起紧密协作 + + +项目成员,也就是帮助你一起完成项目的人。 + +对于项目成员的管理,不需要过多依赖人的管理,否则项目经理就会成为项目管理的瓶颈。所以更多要落实到流程和工具上。 + +好的项目管理,不需要直接去管人,而是管理好流程规范;项目成员不需要按照项目经理的指令做事,而是遵循流程规范。 + +合适的项目管理工具,也可以简化流程,保障流程的执行,提高效率。 + +关于具体怎样制定流程规范,我会后续更新的文章《12 流程和规范:红绿灯不是约束,而是用来提高效率》中有更多介绍。 + +关于项目管理的工具,也会在《14 项目管理工具:一切管理问题,都应思考能否通过工具解决》中有详细介绍。另外,你也可以先参考我在《06 大厂都是如何应用敏捷开发的?(上)》中提到的部分案例。 + +怎样管好软件项目中的事? + +软件项目管理的另一个维度就是管事。软件项目中的事,是指要完成项目目标,在整个开发过程中所产生的一系列任务。对项目中事情的管理,本质上就是对软件开发过程的管理。 + + +选择适合项目的开发模式 + + +软件项目的过程管理,和其他工程项目完全不一样,有其独特性,好在软件工程对这些过程的开发模式都已经有了很好的总结,我们直接借用就可以了。 + +选择好开发模式,才好确定后续的一系列问题,例如流程规范、使用什么工具,如何制定项目计划等。 + +所以对软件项目过程的管理,首先就是要根据项目特点选取合适的开发模式,是敏捷开发还是瀑布模型或者瀑布模型的衍生模型?是一步到位还是逐步迭代? + +对于开发模式的选择,可以参考《03 | 瀑布模型:像工厂流水线一样把软件开发分层化》《04 | 瀑布模型之外,还有哪些开发模型?》和《05 | 敏捷开发到底是想解决什么问题?》的内容。 + +当然,开发模式选好了后,还需要配套的流程规范,以及合适的工具,以保障开发模式的执行。 + + +制定好项目计划 + + +凡事预则立不预则废,在选择好开发模式后,紧接着就是要做好项目计划,有了项目计划,才能有计划有目的地去推动项目进展,出现问题也能及时发现、及时调整。 + +对于如何制定计划,我将在下一篇更新的文章《11 | 项目计划:代码未动,计划先行》中进行详细讲解。 + + +对计划进行跟踪和控制,同时做好风险管理 + + +计划制定后,并不是说事情就会完全按照我们设想的进行,实际执行难免会和计划有些出入,所以还需要对计划进行跟踪和控制。当项目的推进过程中,如果计划有出入时,需要分析原因,对计划做出调整。 + +同时,也不能盲目乐观,对于项目过程中可能存在的风险要进行识别,做好 B 计划,这样一旦风险发生变成问题,可以及时应对,减少风险导致的损失。有关风险管理的内容,可以参考《15 | 风险管理:不能盲目乐观,凡事都应该有 B 计划》。 + +管好人、管好事,你就能管好软件项目。除了上面介绍的一些项目管理知识,涉及软件项目管理的知识内容还有很多。这里并不是说其他知识内容不重要,而是在刚开始的时候,先把这些事情做好,可以保证项目管理不会出现大的偏差,然后逐步拓展到其他知识领域。 + +在这里,我把前面说的内容做了个简单的思维导图,希望可以对你的项目管理转型起到一定的帮助作用。 + + + +技术转管理的一些经验教训分享 + +技术转管理的路上肯定不会是一帆风顺的,要自己踩过很多坑才能成长,我这里也给你分享一点经验教训,希望能帮助你少走一点弯路。 + + +控制你想写代码的冲动 + + +我给每一个刚从技术转型管理的同学的第一个建议都是一样的,那就是:“不要写代码,不要写代码,不要写代码,控制你想自己动手写代码的冲动。” + +前面我说过技术人员转型管理的最大障碍是什么,那就是过于关注技术,而忽略了其他事情。从技术转型管理,是个巨大的转变,这种思维的转变是很难一蹴而就的。 + +对于程序员来说,写代码是自己的“舒适区”,而管理则是“学习区”或“恐慌区”,在转型的过程中,特别容易回到舒适区。 + +比如你看某个接手你的程序员代码写的实在是不够好,那是你最熟悉的,你只要一小时就写完了,而他要一整天的时间,还没有你写的质量好,你会很有冲动去帮他完成。 + +比如说在项目进度吃紧的时候,你可能第一想法就是自己去写代码帮助团队赶上进度。 + +但是,你要知道,当你转型管理后,你的主要职责就是管理,而不是写程序。如果你还是把大部分时间用在写程序上,那么你就很容易忽略项目中的问题。比如没有去关注项目的进展、目前项目的瓶颈、和客户以及其他项目组之间的沟通协调等。 + +这就是为什么你第一步是要控制自己写代码的冲动。作为一个项目管理者,你的第一要务是管理好项目,而不是去写代码。当你控制住不去写代码以后,你才能把注意力放到团队和项目上去,去领导团队。团队出现问题时,你能及时解决、及时调整。 + +所以,如果你带的项目进度吃紧时,你要做的不是去写代码,而是去帮助团队从其他角度想办法。具体怎么做,你可以参考我在《08 怎样平衡软件质量与时间成本范围的关系?》这篇文章里介绍的一些方法,看是不是可以用这些办法缓解进度压力。 + + +团队的成功,才是你的成功 + + +我刚转型做管理的时候,问过老板一个问题:“是不是我把上级的工作做了,我就能升职了?”老板的回答很出乎我意料:“并不是你把上级的工作做了就能升职,而是你的下级都成长了,能替代你的位置了,你就可以升职了。” + +这让我明白一个道理:作为一个管理者,团队的成功,才是你的成功。做程序员的时候,把代码写好就很成功了,但是转型做管理后,团队的成功和项目的成功,才是你的成功。 + + +形成自己的管理风格 + + +我在刚开始工作的时候,当时的项目经理很厉害,对我们要求非常严厉,做错了可能就要挨批评,项目管理的很好。那段时间我也进步很大,所以我觉得他是一个很好的项目经理,我就想着自己以后也要像他一样去管理项目。 + +等到我开始管理项目时,我也想像他一样去严厉的对待下属,但我的性格是比较温和的,我没有办法去做到动不动就去责骂、批评下属,这也让我有了很大的困惑。 + +后来我尝试着结合自己的性格特点,更多地去激励、帮助下属。在这种管理风格下,整个团队的氛围很融洽,大家做事情也积极主动,一样达到了很好的管理目标。 + +所以说管理这种事,并不是只有一种风格一种方法,你完全可以根据自己的特点,找到适合自己的管理风格。 + + +坚持就是胜利 + + +技术转型管理的过程,一定不会是一帆风顺的,你会面临很多挑战,会有非常大的压力。这时候最容易产生的冲动行为就是:“算了,还是回去写程序吧!” + +我在转型的过程中也遭遇过非常大的压力,遇到过各种困难,掉了好多头发。我有过好多次想放弃的念头,最终还是咬咬牙,坚持了下来。 + +这样过了几年后,我再回头看当初觉得特别难、压力特别大的事情,现在看起来根本不算什么。如果我当初真的放弃了,恐怕再难迈过那道坎,完成转型。 + +一旦你已经下定决心要转型,就不要轻言放弃,坚持就是胜利。 + +总结 + +想要技术转型管理,首先从转变思维方式开始,从技术思维到管理思维,从关注细节到关注整体。然后去改变习惯,控制自己想写代码的冲动,多去从其他角度想办法。 + +要管理好一个项目,关键是要管理好项目中的人和事。对客户要管理好期望,对项目成员则通过合理的流程规范更好的一起协作;对于项目中事的管理就是对软件开发过程的管理,选择好开发模型很重要,然后就是制定好计划,按照计划推进,过程中不断的调整,并且管理好项目中的风险。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/11项目计划:代码未动,计划先行.md b/专栏/软件工程之美/11项目计划:代码未动,计划先行.md new file mode 100644 index 0000000..5933d22 --- /dev/null +++ b/专栏/软件工程之美/11项目计划:代码未动,计划先行.md @@ -0,0 +1,187 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 项目计划:代码未动,计划先行 + 你好,我是宝玉,我今天想与你聊一聊“项目计划”的问题。 + +若干年前,我接手一个陷入困境的项目,当时的项目经理刚从技术高手转型项目管理,还是没有摆脱技术思维,项目没有什么计划。 + +他把关键模块分给了自己开发,同时还要兼顾项目管理,导致自己的工作遇到瓶颈,其他人的进度也受影响,大家加班加点也没什么进展,士气低落。 + +我接手后,第一件事是重新制定项目计划,在排任务时,避免了对某个人的过度依赖,设置了几个关键里程碑。我还特地把第一个里程碑设置的相对容易一点,只需要运行核心功能。 + +这样大家重整旗鼓,很快就完成了第一个里程碑。达到第一个里程碑的目标后,团队成员很受鼓舞,士气很快就上来了,后面按照新的计划,并没有太多加班加点,就完成了一个个的里程碑,最后顺利完成项目。 + +你看,如果没有计划,你的项目可能会陷入一种无序和混乱中。 + +计划,就像我们出行用的导航,你可以清楚地看到项目整体的安排,同时它还时刻提醒我们目标是什么,不要偏离方向。 + +执行计划的项目成员,就像使用导航的司机,可以知道什么时间做什么事情,保证任务得以执行。执行计划的过程,就像我们沿着导航前进,可以了解是不是项目过程中出现了偏差,及时的调整。 + +做技术的就不用关心计划吗? + +很多程序员对计划有误解,也不愿意做计划,他们通常都会用一些原因来说做计划是没必要的。 + +一种典型观点是:“既然计划总是在变,干嘛还要做计划?还不如上手就是干来的爽快!” + +这就好比我看过的一个段子:“既然飞机老是晚点,还要时间表干吗?”“没有时间表,你怎么知道飞机晚点了呢?” 计划也是这样,给你提供一个基准线,让你知道后面在执行的时候,是不是出现了偏差,可以根据计划不断地修正。 + +还有的人说,做计划那是项目经理的事,我是程序员,项目计划与我无关。 + +我在专栏中常说你要有大局观,不要将自己局限在程序员的身份中。试着做计划就是一个非常好的培养大局观的方式。比如说,你在制定计划的过程中,需要去综合考虑各种因素:有哪些任务要做,可能存在什么风险,任务之间的依赖关系是什么,等等。 + +参与做计划的过程,可以让你对项目的各种事情了然于胸,这就相当于扩大了你的上下文,让你有更高的视角看待当前工作遇到的问题。 + +另外,我还见过很多人抱怨项目经理制定的项目计划有问题,却很少看到会有人愿意主动参与制定项目计划。如果你不主动参与计划的制定,最终就只能按照项目经理制定的计划执行了。出现计划不合理的地方,你也只能接受,工作就会一直很被动。 + +当然,有时候你可能确实是没机会参与到当前的项目计划中。不过,万事皆项目,你一样要学会做计划,因为学会做计划,会对你工作生活的方方面面起到积极的作用。 + +比如很多人都有一些目标:要转型做管理、要移民、要写一个业余项目,然而很多目标都无疾而终了。这是因为光有目标还不够的,必须得要付诸行动。而要行动,就需要对目标进行分解,进而变成可以执行的计划。 + +如何制定计划? + +如果有一天,你接手了一个项目,通常第一件事就是得去制定一个项目计划。那么怎么制定计划呢? + +制定项目计划,通常有三个基本步骤: + + +第一步:任务分解; + +第二步:估算时间; + +第三步:排任务路径。 + + +以前我在飞信时,有一个项目叫“留言飞语”,就是飞信用户可以在网站或者 PC 客户端,互相留言,当时我负责这个项目的服务端,正好我还留着当年制定的计划,虽然不算一个很好的计划,但好在它是一个真实项目的计划,正好可以用它来说明一下如何制定计划。 + + + +备注:图片较大,需要点击查看大图 + +你看到的这个计划其实不是第一版,可能也不是最后一版,因为制定计划本身是一个反复迭代的过程,尤其是一开始在需求并不够明确的时候,只能比较粗粒度的分解任务和估算,在项目推进的过程中再逐步细化和完善。 + +第一步:任务分解 + +我们写程序的时候都有经验,就是要把复杂的问题拆分成简单的问题,大的模块拆成小的模块,在工程里面这个叫“分而治之”。做计划也是一样,第一步就是要对任务进行分解。 + +在项目管理中,对任务分解有个专业的词汇叫 WBS,它意思是工作分解结构(Work Breakdown Structure, WBS)。就是把要做的事情,按照一个树形结构去组织,逐级分解,分割成小而具体的可交付结果,直到不能再拆分为止。 + +下图就是“留言飞语”项目按照 WBS 拆分的结果。 + + + +可以看得出,整个过程是按照瀑布模型来划分的,大的阶段分成技术方案设计、编码和测试,然后每一个大的阶段下面再进一步细分。 + +例如技术方案设计下面再有需求分析、技术方案设计和评审等;而编码阶段则是按照功能模块再进一步拆分。拆分之后,都是小而具体、可交付结果的任务,且不能再进一步拆分。 + +这里需要注意的是,在制定计划时,除了要拆分任务,还需要反复思考各种可能存在的问题。 + +比如,这个项目不仅是网站可以访问,还需要在 PC 客户端能发留言,所以还需要考虑和 PC 客户端的通信协议、什么时间可以让 PC 客户端可以测试协议等。如果上手就写,没有良好的计划,就可能会忽略这些问题,最后导致 PC 客户端都不知道怎么去调用服务端接口,也不知道什么时候可以和客户端联调。 + +如果项目经理对技术细节不熟悉,可以邀请架构师或者技术负责人协助进行任务的分解。 + +第二步:估算时间 + +任务分解完之后,你就需要对每一个任务估算时间。就像下面这样。 + + + +估算时间这事,有很多方法可以参考,主要还是得依靠以前的经验。要想估算准确,需要从两个方面入手: + + +任务拆分的越细致,想的越清楚,就能估算的越准确。 + +要让负责这个任务的人员参与估算。 + + +举例来说,让你直接给出一个“留言飞语”这样项目的估算时间,是很难的,但对于某个具体功能模块的实现,就可以比较准确了。当把“留言飞语”这样大的项目拆分成足够小的任务时,你就可以很容易的对小的任务进行准确的估算,从而让整体的时间估算变得准确起来。 + +为什么要让开发人员参与估算呢? + +我们来对比一下。假如说,一个任务,项目经理估计需要 3 天,但是实际执行的时候,这个任务可能要 5 天,结果导致开发人员加班。这时候开发人员心中肯定会有不满的情绪,认为是项目经理的错误估算导致了他的加班。 + +如果这个任务所需的时间,是由项目经理和开发人员一起估算出来的,结果最终发现错误估算了任务的难度,这时候开发人员多半会主动加班加点,努力在 3 天之内完成,也不会轻易怪罪到项目经理头上。 + +但这不意味着项目经理对估算不需要控制,通常来说,项目经理需要自己有一个估算,然后再请开发人员一起评估。如果结果和自己的估算差不多,那就可以达成一致,如果估算不一致,那怎么办呢? + +其实很简单,就是要双方一起沟通,消除偏差。特别要注意的是,开发人员预估工作量通常会很乐观,所以最后时间会偏紧,这种情况一样要去沟通消除偏差。估算的主要目的是尽可能得到准确的时间。 + +但是在沟通中也要注意技巧,不要采用质问的方式:“这么简单一个模块居然要 5 天?”这只会让听者产生逆反心理,无法有效的沟通。可以恰当的提一些问题来达到有效沟通的目的,比如我通常会问两个问题: + + +“能不能把你这个任务再细化一下?” + +“能不能简单介绍一下这个模块你是打算如何实现的?” + + +估算出现偏差,可能是由于开发人员没想清楚,或者是项目经理自己低估了其难度。提问可以帮助双方搞清楚真实的情况是什么样的,而且也不会招致反感。同时项目经理还可以给予一些建议和支持。 + +沟通最好的方式就是倾听和恰当的提问。 + +如果任务的粒度太粗,就需要进行细化,细化后就能更准确的知道结果。 + +对于估算的结果,通常还要考虑增加一些余量,因为实际项目执行过程中,并没办法保证是 100% 投入,有可能并行还有其他事情,或者一些突发事情、事先没有考虑到的任务都有可能影响进度。至于加多少余量,还是要根据项目的情况和经验来判断。 + +第三步:排路径 + +我们知道,项目中有些任务是可以并行做的,而有些任务之间则是有依赖关系的。比如说“留言飞语”项目中,编码和测试方案是可以同时进行的,而 Code Review,要在编码完成后进行。 + +所以,排路径就是要根据任务之间的关系,资源的占用情况,排出合适的顺序。例如下图。 + + + +排路径是一个相对比较复杂的任务,比如要注意任务的依赖关系,要注意路径的长度,尽可能让几个任务可以并行的进行,避免相互等待。如果借助像 Project 这种工具会让这个过程相对容易些,可以直观的看出来哪些任务是相互依赖的,哪些是同时进行的。没有 MS Project 这类软件,也可以用一些替代手段,例如 Excel 上画表格。 + +制定计划时不要担心不够准确,先有一个基本的计划,可以粒度比较粗,不那么准确,让事情先推进起来。 + +设置里程碑 + +不知道你有没有参加过那种周期很长的项目,一直看不到结果,时间一长会很疲惫。所以有经验的项目经理会在项目启动后,根据制订好的初步计划,确定几个关键的里程碑。 + +里程碑的时间点确定后,计划可以灵活调整,但里程碑一般不会轻易改变,因为里程碑代表着一份承诺。这对于项目成员来说,有两个重要的影响,一方面,成员会有很明显的来自 DeadLine 的进度压力,自古 DeadLine 就是第一生产力;另一方面,就是在里程碑完成后,大家会获得一种正面激励。 + +里程碑的设置,并没有特别的规则,可以是项目生命周期的特定主要时间,也可以是一些关键的时间点。拿“留言飞语”这个项目来说,有三个时间点非常关键: + + +第一个时间点就是确定和 PC 客户端的通信协议,这样 PC 客户端可以根据这个协议开始开发功能了; + +第二个时间点就是服务端开发完成,PC 客户端可以服务端联调了; + +第三个时间点就是测试验收通过,可以上线了。 + + +最终这三个时间点被定义为里程碑。 + +在项目的推进过程中,根据里程碑完成的情况,你就可以很直观地知道项目的进展如何。如果发现不能如期完成里程碑,就需要进行适当的调整了,例如加班,或者砍掉一些功能需求。 + +当然,设置好的里程碑也不是不能调整,但是要注意调整次数不宜过多,不然就会变成“狼来了”,以后就没有人相信你的时间点了。 + +计划需要跟踪和调整 + +项目管理中,并不是计划制定好了就完事了,还需要跟踪和调整。就好比你要开车去什么地方,设置好导航还不够,还需要沿着导航前进,如果遇到障碍或者走错路了,得要及时调整。 + +项目的跟踪是很必要的,可以了解计划的执行情况,了解成员的工作情况,是否能按时完成,需要什么样的帮助。 + +跟踪进度的方式主要有两种,一种是项目经理定期收集跟踪,一种是项目成员主动汇报。项目经理挨个收集的话,会有一个沟通确认的过程,对进度会了解的更准确;项目成员主动汇报,可以减少项目经理的收集工作,但有可能不准确。 + +在这方面,我觉得敏捷开发的两个实践特别值得借鉴和推广。 + +第一个就是每日站立会议,在每天的站立会议上,每个项目成员都需要说一下自己昨天做了什么,明天计划做什么,有没有什么阻碍。通过这种方式,可以非常好的了解每个人的任务进展情况,同时对于成员遇到的困难,其他人也可以及时给予支持。 + +第二个就是看板,通过看板,可以非常直观的看到每个人在干什么,进展如何。 + +通过对项目计划的跟踪,可以很容易的看出来执行的情况,也会发现偏差,计划出现偏差是很常见的,所以需要定期进行调整,也不需要太频繁,例如可以每周一对计划做一次调整。 + +总结 + +项目计划是保障软件项目成功非常重要的手段,制定计划的过程,可以让你对项目有全面的了解,跟踪计划让你知道项目进展情况,出现问题也可以及时调整。 + +将任务分解、估算时间、排路径,三步就可以制定出一个项目计划,制定计划不要追求完美,制定好一个初步计划后,就可以先按照计划推进起来,进行过程中还可以继续调整细化。设置里程碑可以有效的保证项目的按时交付。 + +最后,并不需要当项目经理才能去制定计划,生活中每件事都可以当作一个项目,都可以去制定计划来帮助你实现目标。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/12流程和规范:红绿灯不是约束,而是用来提高效率.md b/专栏/软件工程之美/12流程和规范:红绿灯不是约束,而是用来提高效率.md new file mode 100644 index 0000000..abfa60f --- /dev/null +++ b/专栏/软件工程之美/12流程和规范:红绿灯不是约束,而是用来提高效率.md @@ -0,0 +1,177 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 流程和规范:红绿灯不是约束,而是用来提高效率 + 你好,我是宝玉,我今天想与你讨论流程和规范的价值,以及如何参与制定好的流程规范。 + +不知道你所在的软件项目中是不是也有各种流程规范,例如: + + +开发人员不能直接在生产环境修改代码操作数据库,必须在本地先测试验证后,由运维操作; + +代码需要 Review 通过才能合并主分支; + +代码需要遵守各种规范,像命名、格式,还有缩进用几个空格还是 tab 的细节问题; + +遇到 Bug,先提交到 Bug 跟踪系统。 + + +在我经历的项目中,或多或少都会有各种各样的流程规范,而且越是大的、正规的项目团队,流程规范越是多。 + +然而很多人对于流程规范并不是很理解,甚至觉得是一种约束。 + +为什么要有流程规范? + +从某种程度上来说,流程规范确实是一种约束:约束了我们如何做一件事,约束了我们用什么标准做事,约束了我们用特定的顺序做事。 + +既然如此约束我们,为什么还要有流程规范呢? + +提升团队效率 + +从个体来看,因为流程规范的存在,确实可能存在效率降低的情况,但从团队的角度来看,好的流程规范反而是提升效率的。 + +这其实很像我们生活中的红绿灯,用一个简单的规则:红灯停绿灯行,来约束车辆行人按照指示灯行进。 + +从单个车辆来看,看似是因为红绿灯的存在而影响了效率,但是从整体来看,因为红绿灯的存在,有效避免了拥堵,反而是提升了大家出行的效率。 + +其实红绿灯除了能提高效率,还有其他好处: + + +红绿灯这样好的管理交通的经验,形成流程规范后,就可以全世界共享这种先进的经验; + +红绿灯不再处处依赖于人指挥交通,而变成了让红绿灯的规则来指挥交通。 + + +软件项目中的流程规范是不是也有这样的效果呢? + +以代码审查的规范为例,对于技术高的程序员来说,代码审查可能会耽误一点时间,但对整个团队来讲: + + +即使是水平高的程序员,也可能会被发现有错误,代码审查可以降低出错的概率,保障质量; + +对于水平低的程序员,可以通过代码审查学习和成长,代码被高水平程序员审查后,可以有效提高质量。 + + +软件项目中这样的例子还有很多,类似的还有像遇到 Bug 要提交到 Bug 跟踪系统,还需要配合重现步骤说明,看起来繁琐,但是却让 Bug 可以有效跟踪,让开发人员可以重现和定位,从而高效的修复 Bug。 + +将好的实践标准化流程化,让大家可以共享经验 + +我们知道,在运动项目上,有些运动员特别有天分,总能拿好的成绩,而这些运动员的动作,会被反复的研究学习,最终形成标准化动作。而其他天分一般的运动员,按照研究出来的标准动作练习,也能取得非常好的成绩。 + +软件工程也是这样,早些年的软件项目,就是个人英雄主义盛行的时代,项目的成败极其依赖于个别厉害的项目经理或者技术高手,而这种牛人,总是稀缺的存在。 + +所以后来很多编程高手写代码的方式,甚至写代码的格式,也会被研究,最终形成一套套的代码规范。其他水平一般的程序员,按照代码规范,也能写出不错的代码。 + +代码规范还有个好处,就是大家写出来的代码看起来差不多,换个人接手别人的代码,也能很快上手。 + +如果我们站在流程规范的角度看软件工程的开发模式,它也是源自实践过程中,有些厉害的项目经理发现了好的、可以提升软件质量的开发实践,不断总结改进,最后变成了流程,让普通的项目经理按照这一套流程,也能做出不错的软件。 + +你看瀑布模型也好,敏捷开发也好,最后落实下来,不就是开发过程中一个个的流程规范么?所以瀑布模型我们需要各种阶段评审,敏捷开发需要每天开站立会议,需要每个 Sprint 有计划会、评审会。 + +借助流程规范,让项目管理从人治到“法治” + +在《10 如果你想技术转管理,先来试试管好一个项目》这篇文章中我就提到过,管理就是管人和管事,而管人,就要借助流程规范来管理。 + +因为如果在项目管理中,过于依赖人的管理,项目经理就会成为瓶颈,大事小事都需要项目经理来决策。再说项目经理也不能保证每次决策的正确性,如果决策失误,会很可能导致一些冲突。 + +而好的项目管理,不需要直接管人管事,而是管理好计划和流程规范;项目成员不需要按照项目经理的指令做事,而是遵循计划和流程规范。 + +我以前工作过的一个项目组,一个项目持续了好多年,中间人换了一批又一批,甚至有时候连项目经理都空缺,而项目一直井然有序的进行着,没有出什么问题,靠的就是多年积累下来的适合项目组的流程规范。 + +就像在《06 大厂都在用哪些敏捷方法?(上)》这篇文章中描述的那样:项目成员日常从看板就可以知道要做什么任务,代码审查、自动化测试可以有效保证质量,项目文档可以保证新人加入时能快速上手,结对编程可以保证新人遇到问题可以得到直接的帮助。 + +还有一个常见场景就是需求变更,产品经理想加一个紧急需求,这通常是让项目经理为难的事情:加吧,影响项目进度,开发人员有意见;不加呢,可能客户或者产品经理有意见。一个不小心就两边都得罪了。 + +如果你有一个大家认可的需求变更流程,就不再需要靠项目经理一个人决定该不该加需求,而是通过流程,来大家一起决策是不是要加这个流程。 + +所以你看,流程规范,看起来是约束,实际上你用的好的话,不仅可以提高团队效率,还可以将好的实践标准化流程化,让大家可以共享经验,还可以有效的管理项目。 + +如何制定好流程规范? + +在项目管理中,难免要去制定流程规范。即使你不是管理者,也可以提出合理的流程规范,帮助把项目管理好。 + +有一个科学的制定流程规范的方法,可以让你更好地制定出好的流程规范。 + +制定流程规范的四个步骤 + +对于流程规范的制定,可以通过四个步骤来开展。 + +第一步:明确要解决的问题 + +要制定一个流程规范,第一步就是明确你是要解决什么样的问题。项目中很多问题,都可以思考是不是能通过流程解决。 + +比如说有程序员在生产环境操作,误删了数据表,造成了严重问题。如果只是对程序员进行处罚,寄希望于小心谨慎避免类似问题,那么下一次还有可能会有类似的事情发生。 + +如果说在流程上规范起来,例如:数据库操作之前先备份数据库,事先写好 SQL 语句,需要有人审查,测试环境先测试通过,最后再生产环境执行,那么就可以避免以后再出现不小心删除数据表的事情发生。 + +第二步:提出解决方案 + +对于问题,也不用着急马上就想着用流程规范,可以先思考解决的方法,有了方法后再进一步思考是否能提炼流程规范。 + +那么方法和流程规范有什么区别呢? + +相对来说,方法更有针对性,可能只适用于特定场景或者特定人,而要将方法上升到流程规范,则需要有一定的普适性,能变成具体的步骤或者标准,让每个人都能执行。 + +比如说服务器部署后出现问题,高手可能就直接上服务器操作,直接修改代码编译解决,这是一个解决方法,但这不能成为一个流程规范,因为换一个水平不行或者对代码不熟悉的人来做,可能会搞出更大的问题。这时候回滚操作就是一个相对普适的方法,可以变成一个部署后出现问题的流程。 + +在提出解决方案,制定开发流程时,可以参考借鉴软件工程中,大家公认的好的实践。比如说: + + +敏捷开发的流程:虽然你的项目不一定采用敏捷开发的方式,但是敏捷开发中一些好的流程是可以借鉴的,例如参考我之前文章提到的像看板、站立会议、持续集成,这些好的工作流程,都可以借鉴。 + +代码规范:其实很多公司都公开了他们的代码规范,可以直接基于这些规范制定团队的规范。例如说前端的有 Airbnb 的代码规范 Airbnb JavaScript Style Guide,Java 的有 Google Java Style Guide ,.Net 的有.NET Guide,等等。 + +源代码管理流程:现在的源代码主流是 git,而基于 Git 的代码管理已经有很多成熟的流程规范可以参考。例如阮一峰老师写过的Git 使用规范流程,Git 工作流程和Git 分支管理策略,或者 Github 官方出品的Understanding the GitHub flow,Gitlab 官方推荐的Introduction to GitLab Flow。 + +部署流程:十年前,每日定时构建还是很时髦的部署流程,而现在,主流的部署流程已经变成了持续部署,每次代码合并到主分支都可以触发一次自动部署,这样一有问题,就能马上知道发生在哪个环节。 + + +像这样的好的流程还有很多,在我们专栏会介绍一些。如果平时多留心,你也可以学到很多。 + +第三步:达成共识,推广执行 + +在流程规范提出后,还需要得到大家认可,只有大家认可,达成共识,才能共同遵守,保障制度的执行。 + +对于大家都认可、很重要的流程规范,一定要让大家严格遵守,必要的时候需要配合一些奖惩制度,以保障其执行。 + +比如说流程规范的执行和绩效考评挂钩,对于没有执行的需要私下沟通提醒,严重的需要批评教育。否则流程规范会形同虚设,没有太大的意义。 + +第四步: 持续优化,不断改进 + +流程制定后,在实际执行的时候,难免发现一些不合理或者不科学的地方,这时候就需要对其进行调整。 + +还有一些流程规范,随着时间推移,可能已经不能符合要求了,也需要考虑改进甚至放弃,不然反而会成为一种阻碍。 + +比如说以前采用瀑布模型开发时,项目经理因为需要了解进度,所以每个项目成员要写日报,如果有站立会议了,日报这种形式就可以完全被站立会议替代,没有再存在的必要。 + +通过以上四个步骤,你就可以将日常项目中遇到的一些问题,用流程规范的方式逐步管理起来,在实施的过程中再不断优化改进,淘汰不合适的流程规范。 + +将流程规范工具化 + +如果说,以前我还是人为去推动一些流程规范的执行,近些年,我越来越感觉到,应该尽可能借助技术手段来推动甚至替代流程规范。 + +例如说代码规范,以前代码规范的执行,主要靠反复的教育宣传和代码审查中一个个去检查。而现在,借助 VSCode 这种强大的 IDE,以及 ESLint 这种代码检查工具,可以方便的检测出不符合规范的代码,甚至于可以帮你直接格式化成满足代码规范的格式。 + +还有像保证代码质量的问题,早些年必须依赖测试人员大量手工的测试,而现在借助 CI(Continuous Integration,持续集成)、自动化测试和 Git,可以保证代码必须在通过测试以后,才会合并到主分支,从而很好的保证了代码的质量。 + +就像任正非的《全面提升软件工程能力与实践,打造可信的高质量产品》公开信中题记的这一段话说的: + + +“软件工程”和“质量工程”需要依靠架构技术,而不是依靠 CMM 和 QA 管理流程。一切工程问题,首先要思考能否通过技术解决,当前技术无法解决的问题,暂时由管理手段代劳,同时不停止寻找技术手段。 + + +“软件工程”不要过于依赖流程和管理手段,要思考怎么通过技术手段去解决问题。 + +总结 + +流程和规范,就像红绿灯一样,不是一种约束,而是牺牲一点个体利益,提高团队效率;流程和规范将好的实践标准化流程化,让大家可以共享经验;流程和规范,让项目管理从人治变成“法治”。 + +要制定好项目规范,先明确要解决的问题,然后提出解决方案,看是否可以通过流程规范来解决,有了方案后需要团队成员一起达成一致,最后再推广执行。在执行过程中需要持续的优化,不断改进。 + +对于需要手动操作的流程,可以思考是不是能采用技术手段自动化,通过技术手段去解决。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/13白天开会,加班写代码的节奏怎么破?.md b/专栏/软件工程之美/13白天开会,加班写代码的节奏怎么破?.md new file mode 100644 index 0000000..c8f749a --- /dev/null +++ b/专栏/软件工程之美/13白天开会,加班写代码的节奏怎么破?.md @@ -0,0 +1,163 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 白天开会,加班写代码的节奏怎么破? + 你好,我是宝玉,我今天想与你讨论让很多程序员头疼的话题:开会。 + +说到开会,是很多人心中的痛,每天白天忙于参加各种会议,压缩了本来就少的可怜的工作时间。最可气的,有的人开完会工作就算完成了,而像我们写程序的,还得下班后加班加点赶进度! + +所以一说到开会,程序员们往往如遇洪水猛兽一般,避之不及。 + +开会是有价值的 + +但我想说的第一个问题是:开会是有价值的。软件项目中有不少会议,其实有其价值所在,给你简单举例分析一下。 + +像评审会议,通过会议,可以让产品设计或架构设计在确定前,收集大家的意见,及时发现问题。 + +像每日站立会议,可以及时了解项目的进展,了解当前的困难和瓶颈,及时调整计划,解决问题。另外在会议上,每个人都要当众讲一下做过的事情和计划要做的事情,这也是一种无形的监督和约束。 + +像项目立项会议,可以创建一种仪式感,让每个人都知道项目的关键信息: + + +项目目标:这个项目是要干什么的,达到一个什么目标; + +项目里程碑:项目的开始结束时间,项目的阶段划分,以及各个阶段的时间点; + +角色分工:项目成员的分工和角色是什么,每个人知道自己的任务是什么,知道遇到问题该找谁; + +流程规范:项目开发的主要流程是什么,基于瀑布还是敏捷。 + + +像项目总结会议,团队成员可以一起总结一下项目的得失,把经验总结下来,帮助团队在下一次做的更好。 + +还有很多会议我就不一一列举。从上面可以看出,这些会议都是可以创造价值的。 + +开会是有成本的 + +开会的成本,就像房间里的大象,显而易见而很多人却有意无意无视它的存在。 + +我们做个简单的数学计算,假设一个程序员月薪一万元,如果他每天四分之一的时间在开会,那就相当于公司每月花了 2500 元在会议上。 + +这还只是一个人的成本,如果开会的人多,那么仔细算算,整体的会议成本其实很夸张的。所以我想说的第二个问题就是:开会其实是有成本的,而且还不低。 + +什么样的会议是有效率的? + +其实软件项目中有些会议我是愿意参加的,因为是有价值的,高效的。比如说前面提到的每日站立会议,时间不长,但是收获很大。 + +再比如隔壁郑晔老师的《10x 程序员工作法》专栏中提到的“轻量级沟通”,其中建议的会议方式:人少,面对面沟通。这种小会通常也让我觉得很高效,经常能产生有价值的方案。 + +还有一些会议则让我觉得没什么价值,比如领导冗长的讲话,比如一堆人在偏离会议主题的讨论,比如跟我没什么关系却被迫参加的会议。 + +那么为什么这些会议给我的感觉完全不一样?这其实就是我想讲的第三个问题:会议是不是有效率,取决于它创造的价值是不是高于其成本。 + +我觉得像每日站立会这样的会议更有效率,其时间短、人数少,所以成本低,创造的价值高于其成本。而人数多,又偏离会议主题的讨论会则没有价值,这是因为人数多时间长,导致会议成本高,而其创造的价值远远不及成本。 + +那为什么还有那么多低效率的会议?因为有的会议,就不是为了创造价值。 + +比如说有的会议,花的成本不是组织者的,对他来说,得到他的会议价值就可以了。 + + +你是砍柴的,他是放羊的,你和他聊了一天,他的羊吃饱了,你的柴呢? + + +还有很多会议,是因为组织者和参与者,都没有意识到开会其实是有成本的,所以浪费了成本还不自知。不过这种情况还好,还是可以想办法改进的。 + +接下来,我们来看看如何提高开会效率,破除避免白天开会,晚上还要加班写代码的节奏。 + +如何提高开会效率? + +我们专栏有一篇文章《08 怎样平衡软件质量与时间成本范围的关系?》,很多同学看过里面讲的软件项目金三角理论后,直呼“醍醐灌顶”、“终于找到一套可以说服老板的说辞了”、“能够提高与产品经理打太极的水准”。 + +其实提高开会效率、提升开会价值的方法,就跟软件项目金三角的理论一样,只要从两个角度去想办法:减少开会的成本,增加开会创造的价值! + +在具体探讨这两类方法之前,我们先要认识到一个前提:那就是要让大家意识到开会是有成本的,如果开会创造的价值不能大于其成本,就是浪费。 + +就像金三角理论,你得先让老板、项目经理明白三条边不可能都占,才好去沟通讨论。要提高开会效率,也需要大家先有这个意识,才能在具体措施上达成一致。 + +那么,有哪些方法可以减少开会的成本呢? + + +砍掉一些没价值的会议 + + +在日常工作中,还有很多会议其实并没有什么价值的。如果一个会议符合这些标准,你就要慎重考虑参加了: + + +没有目标的会议。大家都在随意发散,完全没有主题; + +不能形成决策,没有会后行动。如果一场会议看完后都没有什么结果,那跟没开都没啥差别; + +你属于可有可无的角色。如果一个会议,跟你其实没什么关系,你无法提供有效的反馈,对你也没什么价值,只不过是被人拉过去开会的,那不如把这个时间用来做一点对项目更有价值的事。 + + +所以,你可以在每次要接受一个会议邀请之前,先问自己两个问题再做决定:这个会议我真的有必要参加吗?以及,有其他方式可以替代吗? + +其实,很多问题并不是非要通过“会议”的方式解决。 + +以前我负责的一个服务,其他组需要调用,所以有一个组的同事想跟我组织一个会议,让我介绍一下服务,以及如何调用。我思考了一下,觉得准备这个会议我也要写一个 PPT,开会还得要时间,这时间我足够写一个详细的说明文档出来了,而且以后其他组再要用,也只要看文档就可以了。 + +于是我就跟他们说:“我们不用开会,我一会发一个文档链接给你们,如果有问题我们可以在聊天工具上沟通。”后来他们看完文档后,有几处不清楚的地方,在咨询过我以后,我将文档更新好,就没什么问题了,而且后面再不需要为这件事开会了。 + + +减少参与会议的人 + + +会议的成本和两个因素相关:一个是人数,一个是时间。如果减少人数,就能减少成本。 + +减少人数好处还在于,人一少,每个人都会更投入,也更有效率,所以往往时间反而会少产出会高。而且,如果会议上要形成一些决议,人越多越难做决策,人越少越容易达成一致。要想有决议的话,先开几个小会,达成一致后再开大会,大会更多只是宣布一个结果。 + +像谷歌和 Facebook,他们对于会议的态度就是能不开就不开,无关的人不参与。Amazon 的规则也很简单:一场会议的人数,最多订两份披萨,如果超出这个规模,说明这个会议的人数太多了。 + + +缩短开会时间 + + +减少开会成本的另一个方法就是缩短开会时间。缩短开会时间有很多成熟可靠的方案可以选择。 + +比如说站立会议,通过站立的方式逼着大家快点结束。 + +另外,麦肯锡开会上有些做法也值得借鉴: + + +每个成员有一张黄牌,用于喊停其他人会议中发散讨论无意义的话题; + +有人控制节奏,大家快速发言; + +PPT 不超过 3 张,鼓励大家预先准备,多讨论。 + + +还有比如我们在前面敏捷开发介绍的例子,会议有人主持,当话题开始发散的时候,果断制止,放到“停车场问题”环节,也就是会议的最后专门讨论。 + +类似这样的缩短开会时间的办法,确实可以有效减少会议成本,这类提升效率的方法还有很多,你可以从这个角度多思考尝试一下。 + + +提升会议所创造的价值 + + +如果能有效提升会议产出,也一样可以达到很好的效果。 + +比如说,每个会议要有明确的目的和主题,所有的讨论都要围绕会议目的展开。当你发现会议上一些问题的讨论偏离了会议的主题,例如一个需求评审会,结果架构师在讨论技术细节,这就完全偏离了主题。 + +你就应该站出来提醒一句:“现在既然是讨论需求,不如先不讨论技术上的问题,等到需求确定了,我们后面再慢慢讨论技术问题。”或者说:“不如这个问题我们另外组织一个会议讨论。” + +还有开会后,要有明确的结论,有后续的待办事项,落实到个人,对待办事项有跟踪。 + +偷偷说一下,有时候一些没什么价值的会议,又必须要参加,我一般会参会前,用一个本子把一个技术难题、或者一篇博客主题,写下来。 + +开会的时候,把这个难题理清楚思路,把博客的提纲写出来,这样一个会议开完,我的问题也解决了,或者文章提纲也有了。同样也是收获满满,没有浪费太多时间。 + +这些都是提升会议价值的方式,相信你对会议成本有了概念以后,也可以找到很多可以帮助你提高开会效率、更好创造会议价值的方法。 + +总结 + +今天带你一起学习了解了开会的“道”,那就是开会是有价值的,开会是有成本,会议是不是高效,就看它创造的价值是不是高于其成本。 + +如果你想破除白天开会,加班写代码的节奏,就需要从缩减开会成本和提升开会价值的方向上去想办法,还需要让你的老板、项目经理都有“会议是有很高成本”的意识。 + +砍掉一些没价值的会议,减少开会的人数,缩短会议的时间,提高会议创造的价值。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/14项目管理工具:一切管理问题,都应思考能否通过工具解决.md b/专栏/软件工程之美/14项目管理工具:一切管理问题,都应思考能否通过工具解决.md new file mode 100644 index 0000000..d307595 --- /dev/null +++ b/专栏/软件工程之美/14项目管理工具:一切管理问题,都应思考能否通过工具解决.md @@ -0,0 +1,209 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 项目管理工具:一切管理问题,都应思考能否通过工具解决 + 你好,我是宝玉,我今天想与你分享的主题是:一切管理问题,都应思考能否通过工具解决。 + +早些年我在做项目管理工作的时候,除了制订计划外,还要花不少时间去跟踪计划的执行情况。 + +项目管理上出了问题,管理者总是喜欢从流程规范的角度去想办法,于是为此设定了不少流程规范,例如每天要写日报,根据日报更新项目进度,每周要开周例会,看看项目有没有执行上的问题。 + +对任务进度的量化也是个很困扰项目经理的事情,需要频繁地去问程序员:“你这个任务进展如何,大概完成比例多少?”,从程序员那得到的答复通常都是个很乐观的数字,例如 80%。第二天以为他能做完,结果一问是 90%,就这样要持续好多天才真的算做完。 + +所以后来我得出来一个结论:一个任务,只有 0% 和 100% 两种状态是准确的,中间状态都是不靠谱的。 + +除此之外,还有个问题就是,项目的进展并不太直观,除了项目经理每天看计划表,对计划有一个大概了解以外,其他人可能只有在到了计划设置的“里程碑”时,才对进度有比较直观的感觉。 + +项目成员手头事情做完,如果和计划有出入,也不知道自己接下来该干嘛,都要跑去问项目经理,所以项目经理对于很多事情都要从中协调,日常有很多繁重的任务管理工作。 + +后来我发现其实很多管理者都有类似的困惑:任务不好量化难以估算,项目成员对当前项目进度缺少直观感受,管理者要花大量时间在任务管理上。 + +这些年,随着软件项目管理工具的发展进化,发现当年困扰我的这些问题已经不再是一个主要问题,因为通过工具就能很好的解决这些问题。 + +这也是我这些年项目管理和技术管理的一点感悟: + + +一切管理问题,都应思考能否通过工具或技术解决,如果当前工具或技术无法解决,暂时由流程规范代替,同时不停止寻找工具和技术。 + + +下面的微博即是一例,当遇到问题时,不仅从流程上思考有没有问题,更要考虑是不是可以用工具或技术手段来解决。 + + + +在这里,我还是先带你看一下项目管理工具软件发展史,通过工具的演化,你可以更深入的了解到工具是怎么解决这些管理问题的。 + +项目管理工具软件发展史 + +在没有项目管理工具的年代,都是怎么管理项目的? + +早些年,我除了好奇过大厂是怎么开发大型软件项目以外,还好奇过像登月这种超大型项目是如何做项目管理的。正好前不久看了余晟老师写的一篇文章阿波罗“登月中的工程管理一瞥,让我有机会一窥究竟。 + +其实这种大项目的项目管理并不神秘,就是像我们专栏《11 项目计划:代码未动,计划先行》那一篇讲的,这种大项目也是采用 WBS(工作分解结构)把所有任务一级级分解,再排成计划,按照计划有序进行。 + +但阿波罗项目是个超大型项目,所有的任务分成了 A、B、C 三级,到 C 级已经有超过 4 万个任务。要给这四万多任务排出项目计划就太不容易了,一共要几十名分析人员来协调和跟踪所有的任务。最终列计划的图表贴在墙上超过 100 平米。 + + + +阿波罗登月项目巨型计划图 + +在没有项目管理工具的年代,要制订一个项目计划非常之不容易,需要专业人士花大量时间,而且每次修改调整,都要再花费大量时间精力。 + +最初的项目管理软件:项目计划工具 + +直到后来像微软的 MS Project 这样的项目计划工具软件普及,才让制订计划变成了一个相对容易的事情,可以方便的对分解好的任务排出计划。 + +图片来源:MS Project官网 + +早些年软件项目的开发以瀑布模型为主,瀑布模型的这种按阶段划分的开发模式,和 WBS (工作分解结构)这种将任务层层分解的理念不谋而合,MS Project 这种软件可以非常好的将所有任务分解、制订计划,按照计划跟踪执行。所以那时候,会使用 MS Project 就是项目经理的标配。 + +MS Projec 虽然解决了计划制订的问题,但还是有些不足之处。例如不方便跟踪任务进度,进度不直观等。 + +再加上后来敏捷开发开始兴起,很多项目都开始采用 Scrum 的方式来进行项目管理,开发变成了迭代的方式,以前单纯的项目计划工具,就不能很好的满足项目管理需要了。 + +基于 Ticket 的任务跟踪系统 + +传统的项目计划软件还有很多问题无法解决。比如,很多人都有过以下类似的项目经历: + + +产品经理口头让开发对产品做一点小改动,开发也答应了,后来就把这事忘了,或者测试都不知道还有这事,也不记得要测试这个模块; + +代码审查的时候,发现组内某个同事的代码没有写单元测试,但是因为任务紧,只能先上线,于是叮嘱他后面一定要把单元测试代码补上,结果还是忘了。 + + +日常项目中像这样的小事情不少,如果不记下来很容易忘记,如果用传统的项目计划软件排进去又很麻烦,直到后面有了基于 Ticket 的任务跟踪系统,才很好的解决了这个问题。 + +Ticket 跟踪最早源于客服的工单(Ticket)系统,每次客户接到一个问题,就创建一个工单,后续和客户的每一次交流和处理,都要更新工单内容和状态,直到结束。 + +最早在软件项目中,应用 Ticket 跟踪系统的领域是测试领域,用来追踪 Bug,后来逐步衍生到整个项目管理领域,不仅跟踪 Bug,还用来跟踪需求、开发任务等。 + +也有很多系统用 Issue 来表示 Ticket 的概念,无论 Ticket 还是 Issue,表示的都是一个工作任务,可以包括软件的 Bug、功能需求、某个模块的开发、系统的重构任务等。 + +那一个 Ticket 应该包含哪些主要信息呢? + +一个 Ticket,应该包含: + + +标题:摘要性的描述 Ticket 内容; + +类型:属于什么类型的 Ticket:Bug、需求、任务; + +内容:Ticket 的详细内容,例如,如果是 Bug 的话,除了要写清楚 Bug 内容,还需要重现步骤。如果是需求的话,要有需求的描述,可能还需要额外的文档链接辅助说明; + +创建人:谁创建的这条 Ticket; + +优先级:这个 Ticket 的优先级高还是低; + +状态:Ticket 的状态,例如:未开始、处理中、已解决、重新打开、关闭等; + +指派给谁:这个 Ticket 被指派给谁了,谁来负责; + +历史记录:整个 Ticket 改变的历史信息,用以跟踪; + + +当然除了这些外,还有一些其他信息,例如创建时间、附件、标签、版本等。另外现在的 Ticket 跟踪软件都有强大的定制功能,可以增加额外的辅助信息,例如你是基于敏捷开发,还可以加上 Sprint、故事分数等信息。 + +Ticket 的这些内容,基本上可以包含一个工作任务所需要的所有内容。有了 Ticket 之后,无论大到一个功能需求,还是小到一个 Bug,从它创建,一直到完成,整个过程都可以方便的被跟踪起来了。再也不担心像任务被忘记等前面提到的这些情况了。 + +基于 Ticket 去跟踪任务,不再需要通过日报、一对一会议的方式来收集任务执行情况,负责 Ticket 的项目成员在完成任务后,会直接修改 Ticket 的状态,这样其他人就可以看到 Ticket 是否已经完成。 + +Ticket 通过各种不同状态,例如未开始、开发中、完成等,可以很直观的了解任务的进展,这就避免了任务难以量化的问题。 + +Ticket 跟踪系统和敏捷开发也是很好的搭档。在敏捷开发中,产品 Backlog(产品待办任务列表)是一个用来放所有产品的待办任务的清单,在每个 Sprint 开始前的迭代计划会议上,从产品待办任务清单里面选取一部分任务到 Sprint 的待办任务清单(Sprint Backlog)中。 + +当使用 Ticket 跟踪系统后,就可以把所有产品的待办任务用 Ticket 都记录起来,当我们在迭代计划会议上选取好任务后,就标记为要在当前 Sprint 完成,这样后面就可以方便的筛选出属于当前 Sprint 的所有 Ticket,这样大家就可以从 Ticket 跟踪系统知道我们这个 Sprint 有哪些 Ticket 需要完成、进展如何。 + +如果将当前 Sprint 中,从开始到结束,每天记录一下 Sprint Backlog 中未完成 Ticket 的数量,绘制成一张图表,横轴表示时间,纵轴表示剩余 Ticket 数量,就可以通过图表直观地看到还剩下多少工作。 + +这种用于表示剩余工作量的工作图表也叫燃尽图(burn down chart),可以直观的预测工作将在何时全部完成。 + + + +图片来源:维基百科 + +基于 Ticket 的任务跟踪系统,很好的弥补了项目计划工具的不足,让项目中大大小小的各种开发任务都可以方便的记录跟踪起来。燃尽图也可以直观的了解剩余工作情况。 + +如果说美中不足的话,就是整体的 Ticket 状态还不是很直观,例如不能清楚的看到哪些任务在进行中,哪些任务待领取。 + +基于看板的可视化任务管理 + +看板本来是在 1940 年由“丰田汽车”发明的生产管理系统,其中一些理念被借鉴到软件开发中,尤其是其可视化的任务管理方式,很好地解决了早期 Ticket 跟踪系统不直观的问题。 + +所以现在的 Ticket 任务跟踪系统几乎都会有看板视图,通过看板可以很直观的看到当前任务进展情况。 + + + +参考上图,可以看出,在看板视图上的所有 Ticket,可以很直观的看出哪些还没开始,哪些进行中,哪些已经完成。 + +这种可视化的任务视图,不仅是对项目经理,可以很直观看到进展,对于普通项目成员也是很方便。 + + +从“待选取”栏选择一个 Ticket,拖动到“开发中”栏,表示这个 Ticket 已经选取,开始开发了。 + +手头上的 Ticket 开发完成后,就可以将 Ticket 拖动到下一栏——“测试”栏。 + +测试人员看到新加入“测试”栏就可以从测试栏选取 Ticket 进行测试。 + +如果测试没通过,Ticket 就会被拖动到“待选取”栏。 + +如果测试通过,Ticket 就会被拖动到下一栏——“待部署”栏。 + +部署完成后,所有“待部署”栏的 Ticket 就会被拖动到“完成”栏。 + + +整个过程完全不需要项目经理从中协调太多,尤其是结合每日站立会议,可以让项目成员自发有序地按照看板开展日常工作。 + +借助 Ticket 跟踪和看板可视化,项目经理可以从繁重的任务管理中解放出来,可以抽出来时间做一些其他更重要的事情。 + +以上就是项目管理工具的一个演化简史,可以看到,每一次工具的发展进化,相应的很多项目管理工作就可以得到简化,很多早期的项目管理问题,也就不再是问题了。 + +有哪些项目管理软件可以选择的? + +在了解完项目管理工具的发展历史后,再给你介绍一些目前国内国外主流的项目管理软件,帮助你根据自己项目需要进行选择。 + +如果单纯是项目计划工具,功能最好、最全的应该是微软的MS Project,但遗憾的是只能运行在 Window 上,不支持 Mac 平台。如果要在 Mac 上使用项目计划工具,可选的有OmniPlan和Merlin Project。 + +而且这些项目计划工具,现在也都支持了看板视图。不过如果只是单机支持的话,意义并没有那么大,需要在线版的 Ticket 跟踪结合看板视图,才能让整个团队可以一起浏览操作,发挥其最大效用。 + +基于 Ticket 的任务跟踪系统,最有名的应该是Atlassian公司出品的Jira软件,功能全面,体验很好。Jira 主要是在海外比较流行,因为访问速度和使用习惯等原因,国内用户要相对少一些。 + +同类产品也很多,微软的Azure DevOps (以前叫 TFS, Team Foundation Server),和微软系的产品如 Visual Studio、Azure 可以很好的整合。 + +代码托管平台GitHub本身也集成了一套 Issue 跟踪管理系统,虽然没有 Jira 那么强大,但是对于普通项目来说,足够用了。尤其是对于开源项目,完全可以基于 GitHub 的 Issue 进行日常的项目管理。 + +国内同类的软件有: + + +禅道:为数不多提供开源版本可以自己搭建的; + +Worktile:集成了即时消息软件; + +TAPD:腾讯出品,可以和腾讯的服务很好整合,例如企业微信和腾讯云; + +云效:阿里巴巴出品,可以和阿里的服务很好整合,例如阿里云和钉钉; + +DevCloud:华为出品,和华为云有很好的整合。 + + +还有一些其他产品,这里就不一一列举。 + +那么该如何选择适合的工具呢? + +从功能上来说,基本上,上面提到的每一款产品都能满足日常项目管理的基本需求,建议从项目特色、团队成员、价格和服务等因素综合考虑。 + +例如说你的项目完全是微软技术栈,就可以考虑使用 TFS;如果你深度使用阿里云和钉钉,那么就可以考虑阿里的云效;如果你想自己搭建,那么就可以考虑 Jira 或者禅道。 + +这些产品都有免费版本,可以先试用,你可以仔细对比后,根据自身的情况再最终决定。 + +总结 + +今天我带你一起了解了软件项目管理工具的发展历史:从完全手工方式管理项目,到借助计划工具分解安排计划,到基于 Ticket 跟踪管理任务,再到基于看板的任务可视化。每一次工具的升级,都是对项目管理工作的一次简化。 + +合理的使用项目管理工具,可以帮你极大提高管理效率,起到事半功倍的效果。我也列举了一些目前国内外主流的项目管理工具,希望可以帮助你做出选择。 + +最后,对于日常项目管理的问题,你也可以多思考是不是可以由工具或者技术手段来解决的。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/15风险管理:不能盲目乐观,凡事都应该有B计划.md b/专栏/软件工程之美/15风险管理:不能盲目乐观,凡事都应该有B计划.md new file mode 100644 index 0000000..192af11 --- /dev/null +++ b/专栏/软件工程之美/15风险管理:不能盲目乐观,凡事都应该有B计划.md @@ -0,0 +1,242 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 15 风险管理:不能盲目乐观,凡事都应该有B计划 + 你好,我是宝玉,我今天想与你分享的主题是:风险管理,凡事都应该有 B 计划。 + +说到风险,很多人都模模糊糊有一些了解,然而在软件项目中,有风险意识的却不多。比如说: + + +估算一个模块工作量,程序员总是会给出一个乐观的进度,而最终实现这个模块的时候,却发现总是有些其他的事情发生影响了进度; + +一个关键的程序员,突然离职了,导致项目进度停滞。其实早前就有一些迹象,而项目经理没引起重视; + +技术负责人很激进的采用了一个最近很流行的新技术,结果做的过程中,发现这个技术还不太成熟,很多坑没法填,导致项目最终失败; + +服务器突然挂了,才发现硬盘坏了而数据没有备份,造成巨大的损失。 + + +这些问题其实都和风险相关,如果没有及时发现这些潜在的风险,没有应对方案,轻则导致项目进度延迟,重则导致项目失败,造成重大损失。 + +在软件工程里面,针对这些可能造成风险的问题,对风险进行提前识别和管理,就可以有效地应对。 + +什么是风险管理? + +风险是指不确定的事件,一旦发生,将会造成消极的影响。风险包含两个方面的内容: + + +发生后,会造成什么样的损失? +发生的概率有多大? + + +所以也有人认为:风险 = 损失 x 发生概率。 + +比如说,有一次我负责一个小项目,激进的采用了刚开始流行的 React 框架,如果使用熟悉的 Angularjs 框架,正常来说一个月就能完成,当时很乐观的觉得 React 和 Angularjs 也差不多,时间应该不会多出来太多,就按照一个月时间来做项目计划。 + +最后到实际开发的时候,发现 React 和 Angularjs 有很多不一样的地方,必须要现学现用,原本计划一个月完成的,最后加班加点拖到一个半月时间才完成。 + +在这个项目中,使用新技术就是一个风险: + + +造成的损失就是导致了进度延误; + +发生延误的概率,如果项目开始前估算,大概在 60% 左右,项目进行中这个概率就上升到 80% 了。 + + +像软件项目中这样的风险,如果发生后就会变成问题,问题如果没有及时解决,就会影响到项目计划。如果我们能有效的对风险加以识别,加以管理,就能减少对项目的负面影响。 + + +风险管理就是指在项目进行过程中,识别可能的风险,对风险进行评估,并加以监控,从而减少风险对项目的负面影响。 + + +风险管理重要吗? + +对于风险管理,现在软件项目中提起的不多,应用的更少。 + +主要原因还在于大家都比较乐观,多少有一定侥幸心理,都觉得自己不会运气那么差,风险事件正好就发生了。还有就是因为如果要做风险管理,需要额外多做一些工作。 + +这就跟你买保险的道理一样,风险事件没发生,相当于你买保险的钱白花了,但是一旦发生了,你才知道买保险的钱花的值得。 + +对软件项目风险的管理,才是体现项目管理水平的地方。我们对比下面几种应对风险的层次来看: + + +被动应对:风险已经发生,造成了问题才被动应对; + +有备无患:事先制定好风险发生后的补救方案,但没有任何防范措施; + +防患未然:对可能的风险做出防范,并把风险防范作为项目任务的一部分。 + + +哪一种层次更体现项目管理水平,相信你心中已经有了答案。 + +拿前不久发生的拼多多被“薅羊毛”事件来举例。 + + +2019 年 1 月 20 日凌晨,拼多多平台出现系统漏洞,用户可以领取 100 元无门槛券。因此开始出现大批用户借此“薅羊毛”,利用无门槛券下单虚拟商品,例如充话费或 Q 币等等,并一度有消息传出,拼多多将因此损失 200 亿元。 + + +这个事件追究原因的话,当然可以说是开发代码没写好,可以说是测试没测好,也可以说运维没有监控好。 + +但另一个角度讲,如果这个项目的负责人有一点风险意识,做了风险管理,即使出现这样的问题,损失也一定不会这么大。 + +如何做好风险管理? + +1. 培养风险意识 + +风险管理其实最大的问题不是如何做,而是项目成员缺少风险意识,有了风险意识,才能去识别出来项目中可能的风险,进而去管理风险。 + +对于培养风险意识,我的经验就是:项目中的任务,不能盲目乐观,都思考一下它最坏的结果是什么,如果最坏的结果不能接受,就说明要有个 B 计划,考虑风险管理了。 + +比如说拼多多的无门槛券,最坏的情况是被无限刷,造成巨额经济损失,那这种结果是不能接受的,就需要考虑风险管理。 + +2. 管理风险 + +软件项目风险管理,通常分四步来做。 + +第一步:风险识别,识别可能的风险 + +风险识别,就是看项目中有哪些可能的风险,因为只有找出来有可能存在的风险,才会有后续的步骤。 + +识别风险这种事,经验很重要,因为大部分风险其实都是相似的。以前看 CSDN 总裁蒋涛发过一条微博,内容引发了很多人的共鸣,每一条无不应对着软件项目中的常见风险。 + + +10 个项目死亡的信号: + + +第一版做太多功能 ; + +太依赖新技术平台; + +与公司另一个有份量的产品竞争; + +团队人手不足; + +复杂的问题,需要复杂的解法; + +成员开始隐藏进度落后的事实和原因; + +不断更改、增加的需求 ; + +2.0 症候群 - 非要更大、更强、更美 ; + +产品没有市场立足点; + +你根本无法解决的大问题。 + + + +一个识别风险的方法叫检查表法,就是可以把类似于上面这些常见风险收集整理起来,分类列成清单,按照清单去检查对照。 + +软件项目的风险主要分成以下几类: + + +项目风险:项目预算、进度、用户和需求等方面的问题; + +人员风险:人员离职、人手不足等问题; + +技术风险:采用的技术所可能带来的风险; + +商业风险:与市场、产品策略等有关的商业风险。 + + +你也可以按照上面的分类整理出自己的风险检查表。 + +另外你还可以借助集体的智慧,定期针对风险问题开一些头脑风暴会议,一起发现可能的风险。另外,要有合适的渠道,让项目成员可以反馈可能发生的风险问题。 + +第二步:风险量化,对风险进行评估量化 + +在风险识别出来以后,需要从两个方面去评估: + + +发生的概率多大? + +发生后,后果多严重? + + +对于概率大,后果严重的风险,需要高优先级重点考虑;对于概率不高但后果严重的问题也要考虑,不过优先级略低;对于概率高但后果不严重的风险事件,可以优先级很低或者不考虑;对于概率低后果不严重的,则可以不予考虑。 + +拿拼多多的“无门槛券”来说,这就属于一个高风险、高概率的事,需要重点考虑。 + +第三步:应对计划,对风险制定应对策略 + +在评估后,需要后续进一步考虑的,就要制定好应对的计划。针对风险,主要分成以下几个策略。 + + + + +回避风险——更改导致风险的方案 + + +回避风险很好理解,就是要对可能发生的风险,放弃或者修改导致风险的方案。这样就从根源上消除了风险,简单而彻底。 + +就像我前面举的因为使用 React 技术导致风险的例子,可以直接放弃使用 React,用回熟悉的 Angularjs 技术,这样就可以避免技术风险的发生。 + +但这种方案不一定适合所有情况,例如拼多多“无门槛券”的风险,就无法采用这种方案。 + + +转移风险——将损失转嫁出去 + + +以前玩大富翁游戏,最开心就是有一张“嫁祸卡”,万一遇到倒霉事就可以转嫁到其他玩家身上。转移风险也是这个思路,就是为了避免承担风险损失,将损失转嫁出去的方法。 + +日常生活中买保险就是一个例子,发生意外,保险公司会帮助赔付。 + +在软件项目中,举例来说,如果你的团队对于服务器管理不是很在行,有可能会遇到服务器宕机或数据库丢失数据等风险,就可以考虑购买云服务,这样云服务商会帮你解决服务器宕机或数据库丢失的问题,而且万一宕机或丢数据了他们也会承担一定的责任。 + + +缓解风险——降低风险发生概率或减少可能造成的损失 + + +缓解风险就是在风险发生前采取一定措施,降低风险发生的概率,或者减少风险可能造成的损失。 + +比如你要担心数据库数据丢失的风险,就需要定期备份数据库;比如你担心核心成员要离职,那就涨点工资,避免人才流失。 + +拼多多的无门槛券也是个典型的例子,如果对券的消费增加一定的限制,比如说每个用户有领取的上限,一个月不能超过 100 张,每张券不超过 10 元,不能用于购买 Q 币手机话费等虚拟物品,这样即使出现问题,也不会造成很大损失。 + +我们在《04 瀑布模型之外,还有哪些开发模型?》中学到了“螺旋模型”,也是这种策略:每个迭代都要做一下风险评估,再决定项目是不是继续。 + + +接受风险——明知山有虎偏向虎山行 + + +还有一些风险本身很难避免,或者去应对这个风险的成本超过风险发生后造成的损失,那么就没必要应对,直接选择承担风险后果就好了。 + +比如说前面说的采用新技术 React 导致进度延迟的案例,这个风险虽然有很大概率发生,但是进度延迟的影响可以接受,并且让团队今后在技术栈上多了新的选择,长远来看对项目更有利,那么这个风险就是可以接受的,还是可以继续做下去。 + +回避风险、转移风险、缓解风险、接受风险,以上就是针对风险提前准备的一些应对策略,实际项目中,可以根据实际情况来灵活运用以上策略,有效应对风险,减少可能损失。 + +第四步:风险监控,对风险进行监控预警 + +风险在没发生的时候并不会变成问题也不会造成损失,如果风险可以监控,可以预知风险即将发生,或者可以在风险发生后,第一时间知道,那么就可以马上对风险进行干预,避免变成更大的问题。 + +要做好监控,第一要能对监控的内容量化,第二要设置阈值,第三就是要有后续的报警和处理机制。 + +很多公司都已经建立了自己的监控系统,将关键数值量化,并设置阈值,超过阈值后自动触发报警机制。 + +一个简单的例子就是服务器宕机了,监控系统发现机器没响应了,自动通过邮件、短信、电话等方式通知正在值班的人员。 + +还有稍复杂一点的方式,像网络服务,可以监控每一次请求结果的状态码,统计请求的成功率。如果单位时间内,服务出错的比例低于阈值,那说明服务是正常的;如果错误比例超过阈值,那说明是出现了问题,需要报警通知相关人员,马上处理。 + +再回到拼多多“无门槛优惠券”的例子,其实有很多数据可以监控到,比如说单位时间内优惠券使用数量,商品销售数量等,如果结合一些可视化视图,应该可以直观地看到当时有大量虚拟商品销售,大量的优惠款被使用。 + +如果针对优惠款设置了阈值,例如每分钟使用超过 1000 张就触发报警,那么也可以及时发现无限刷的问题,避免造成更大损失。 + +以上四个风险管理的步骤是一个连续循环的过程,在整个项目期间,都要持续地对风险进行识别,对风险量化,对于风险采取应对计划,对风险进行监控。 + + + +项目风险管理过程 + +总结 + +今天带你一起学习了软件项目管理中的风险管理知识。软件项目中的风险就是指那些不确定的但是可能会造成消极影响的事件,通过对风险的管理,可以有效降低风险发生的概率,减少风险发生后的损失。 + +软件项目风险管理包括风险识别、风险量化、应对计划和风险监控四个过程,这四个过程是一个循环的过程,需要在项目中持续进行。 + +希望你在学习后,能提高风险意识,不能盲目乐观,凡事都应该有 B 计划。并且能将学到的风险管理知识应用到项目中,做到对可能的风险了然于胸,未雨绸缪,运筹帷幄。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/16怎样才能写好项目文档?.md b/专栏/软件工程之美/16怎样才能写好项目文档?.md new file mode 100644 index 0000000..3c0e264 --- /dev/null +++ b/专栏/软件工程之美/16怎样才能写好项目文档?.md @@ -0,0 +1,223 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 16 怎样才能写好项目文档? + 你好,我是宝玉,我今天分享的主题是:为什么你不爱写项目文档?以及怎样才能写好项目文档? + +我以前看过一个投票,盘点程序员不喜欢的事,有两条和文档相关: + + +不喜欢写文档; + +不喜欢项目文档太少。 + + +看起来很矛盾,却很现实。基本上大家都认同:“项目文档很重要”,然而我们在项目中总是短期高估文档的重要性,而长期低估文档的重要性。 + +结果就是口号喊的很响:要重视文档、要写好文档、要多写文档,然而随着项目的推进,总有比文档优先级更重要的任务,文档的优先级总是被有意无意推迟,导致项目的文档缺失、老旧、无人维护。 + +那么为什么程序员都不爱写文档呢?我总结了一下大致有下面这些原因。 + + +不知道怎么写 + + +不知道怎么写文档的应该占很大一部分比例。 + + +太忙没时间写或者懒得写 + + +程序员确实很忙,但总有不那么忙的时候,却也很少见有人利用这时间去写文档。包括我自己也这样,有时候没那么忙的时候,宁可去想想怎么重构下代码,却很少会愿意去写文档,主要还是太懒。 + + +因为是敏捷开发,所以不用写文档? + + +对于这个问题,我其实反驳过多次,敏捷宣言最后一句话明确指出:“尽管右项有其价值,我们更重视左项的价值。”也就是说敏捷从来没有否认文档的价值,只是更重视“工作的软件”罢了。 + +为什么要写文档? + +写文档,其实对个人、对项目、对团队,都是非常重要的事情。 + + +帮助写文档的人理清楚思路 + + +我想你应该有这样的感受:写作的过程,就是一个思考的过程。 + +写文档,可以让你在写代码之前,梳理清楚思路,想清楚整体结构,比如说有哪些工作是重点难点;哪些要依赖其他人,需要及早协商的;哪些是要考虑安全性的。 + +如果上手就写代码,就很容易陷入到某个技术细节中,而忽略了整体结构。写的时候才发现一个技术难点无法解决,或者已经在某个不重要的细节上浪费了很多时间;或是发现有些依赖其他人提供的服务还没准备好;又或者是上线后才发现有安全漏洞。 + +先写文档,就会抛开代码细节,去站在全局思考。写的时候,各个模块之间的依赖关系、各种可能的安全隐患、各种可能需要其他人配合的地方,就都冒出来了,必须要去查资料,去找人讨论,反复缜密的思考后最终写出来。 + +有人觉得自己写作不行,所以不会写文档。写作不行,只是让你在用词遣句上会有所欠缺,而这不是写文档的真正障碍。 + +真正的障碍是没想清楚,在心中只有一些未成型的混乱的想法和概念,必须要努力把这些模糊的想法确定化和具体化,才能写出来。 + +换个角度来说,如果你连文档都写不出来,那又怎么能指望代码写得好呢? + + +便于未来的维护和交接 + + +“好记性不如烂笔头”,存在脑子里的内容是不可靠的,一个正常的项目组,如果需要长期维护,就需要一定的文档,把设计、操作流程、环境配置等内容记录下来,而不仅仅依赖于口口相传。 + +我有一个习惯,每到一个新项目组,就会把日常工作中遇到的问题、各种环境配置、一些操作的步骤等,所有以后可能还会用上的都记录下来,其中一些还会整理到团队的 WIKI 上。 + +一段时间后,这些随手记下来内容都会发挥大作用,对于我来说,很多问题就不需要问第二遍了。对于团队来说,随着人员更替,我记录的这些内容都是最好的一手资料,有新人过来,按照我当初记录的内容,就可以快速上手。 + + +便于团队更好的协作沟通 + + +在一个项目组中,大家都有不同的分工,有人负责产品设计,有人负责架构设计,有人负责测试。而文档,就成为了团队成员很好的沟通工具。 + +比如说产品设计有雏型的时候,会有一个产品设计的评审会议,基于文档,项目成员可以一起参与其中,提出自己的意见和看法。这样就不至于等到产品设计出来之后,大家才对于设计有改进想法或意见,造成无法更改的结果。 + +当然,写文档还有很多好处,在这里我就不一一列举了。 + +如何写好文档? + +其实文档的重要性真不用多说,很多人也不是不爱写项目文档,而是不知道该如何写好文档。所以在这里我来介绍一下该如何写软件项目文档。 + +很多人对于写文档是有心理压力,觉得自己写作水平不高,不知道该如何下手。首先你要对文档有一个正确的认识:文档写作,关键是通过文档把你的想法表达出来,至于用词、格式相对都是其次的。 + +打个比方,我们如果是大厨给餐馆做菜,得追求个宽油大火、色香味俱全,自己在家做饭,就没那么多讲究了,填饱肚子是第一要素,在这个基础上味道好一点就很好了。 + +我们写文档就像是在家做饭,是不需要追求太多华丽的词藻,也不需要追求字数,只要用简单的文字、图表把想法表达出来,最终在讲解的时候,配合一些口头说明就可以啦,其实比我们上学时写作文容易多了。 + +下面给你介绍一些具体可行的文档写作方式。 + + +从模仿开始 + + +前面有提到,我其实一开始是不知道如何写文档的,直到毕业两年后,我在飞信项目组,领导让我写一个新项目的技术方案文档,我两眼一抹黑说不会写呀,然后领导给了我另一个项目的技术方案文档,说你就“照葫芦画瓢”照着写吧! + +“依葫芦画瓢”就简单多了,同时又学习了一下如何画线框图、时序图等图形,很快就完成了一份技术方案文档,再反复修改几次,质量就还可以了。 + +后来我带团队时,让团队成员写文档,就把当时我写的文档给他们参考,很快他们也能写了。包括后来我写开源项目(angular-ui-tree, react-video),要写英文文档,也是去找了几个同类的开源项目的文档,参照他们的内容和格式,就把文档拼出来了。 + +模仿就是最好的写文档方式,尤其是现在网上可以参考的例子也很多,当你写文档不知道该如何下手的时候,不妨去找一个类似的文档,模仿着写试试。 + + +从小文档开始 + + +一开始写文档,内容不需要很多,可以从小的文档开始。就像前面我提到的,记一些笔记,不要在意格式,一两句话,一些截图,就是不错的笔记。 + +有一次和同事一起去开会,会上他给另一个组的人介绍了如何调用一个服务,介绍的很详细。我就建议他把刚才介绍的内容写成个小文档,这样下次再有类似会议就可以基于文档来说。 + +于是他就整理了一个简单的文档,再为别人讲解的时候就基于文档介绍,容易很多。同时,他每次还会再完善一点内容进去。之后再有同类问题时,他直接把文档发给人家就好了,都不需要再专门开会。 + +项目中很多文档都可以从这样小的内容开始:别人给你讲一个问题的时候记录下来;你给别人讲一个问题的时候记录下来;解决一个技术难题时记录下来方案…… + +这些记录下来的笔记,稍加整理,就可以是很不错的项目文档。 + + +从粗到细,迭代更新 + + +小时候写作文,老师给的最多的建议就是要列提纲,这个建议我小时候当耳边风没怎么听,后来要写项目文档的时候用起来反倒觉得非常实用。 + +我写一个大一点的文档,都是从脑图开始的,先基于脑图,把基本结构梳理清楚。然后第二步就是写 PPT,PPT 有个好处就是不用太多文字,列个一二三,画几张图,就是个简单的文档,PPT 还有个好处就是可以用来给别人讲解,收集反馈。 + +写完 PPT,也收集好了反馈,再写正式的文档。先按照脑图列的提纲把主要章节放上去,然后把 PPT 上的内容和画的图放到文档中,一篇文档的骨架就搭好了,剩下的就是对细节的补充了。 + +为什么我不一开始就写很细的文档呢? + +一个原因是太难写,要花很多时间精力,甚至可能写不下去;另一个原因就是在收集反馈的过程中,会有很多修改。写得越细则无用功越多,最后,你甚至会因为不想改文档而抵触不同的意见。 + +而从粗到细逐步迭代的方式就好多了,一开始的目的是为了梳理清楚思路,只要脑图这种级别的内容就好了,然后进行调整。因为文档很粗,调整也方便,等到基本确定后再写细节,就不会有大的反复。 + + +一些基本的画图的技巧 + + +有人说:“字不如表,表不如图,一图胜千言”。这个观点我非常认同,好的图能帮助你简单而直观地把问题说明清楚。 + +画图其实不复杂,不需要多专业的绘画技巧,也有很多工具软件可以帮助我们简化操作,像 Visio、PowerPoint、Keynote、OmniGraffle 等都是很好的画图软件。平时看到好的图也要注意收集整理,以后自己写的时候,也可以直接参考,可以帮你少走弯路。 + +写文档的时候,主要有几种图比较常用:线框图、流程图、时序图、各种格式的截图。 + + +线框图 + + +线框图是最常用也最实用的一种图形,用简单的方框代替功能、模块、服务等,再用箭头表示关系或者数据流向,非常简单直接。 + +要画好线框图并不难,主要是要理清楚有哪些模块,以及模块之间的关系是什么。用方框配上文字表示模块,方框之间的连线和箭头表示关系。 + +看几个例子: + +例:Twitter 当年的缓存方案。 + + + +图片来源:InfoQ + +例:Netflix 的账单系统架构图。 + + + +图片来源:Netflix技术博客 + + +流程图 + + +流程图是软件项目文档中一种常用图形,可以方便的表示各种不同条件下的逻辑路径。要画好流程图不难,重点是要理清楚逻辑关系,各个关键节点在不同条件下的走向。 + +例:重置密码流程图。 + + + +时序图 + +时序图也是软件项目所特有的一种图形,可以表示不同对象之间发送消息的时间顺序,尤其在涉及网络通信的文档中特别常用。 + +画好时序图,关键是要列清楚所有涉及的对象或者服务,以及消息发送的先后顺序。 + +例:注销登录过程的时序图。 + + + + +各种格式截图 + + +截图也是个非常简单直接的方式,把软件的 UI、交互设计的效果、数据趋势图、数据统计图等直接截图,必要的话配上一些箭头、文字,也可以很好的说明清楚问题。尤其是产品设计文档,经常用到。 + +上面就是如何写文档的一些具体建议,按照上面说的方法做,写好项目文档不会是多难的事情,你还可以在日后的工作中,不断学习不断改进。 + +一些关于文档的其他建议 + +有时候我也看到一些比较极端的情况,就是过于追求文档,项目中要花大量的时间写文档,而很多文档是形式化的,并没有太大意义,可能写完了不会用来讨论,也不会有人看。 + +所以我是比较认同敏捷宣言观点的:文档很重要,但是工作的软件高于详尽的文档。这里面的平衡很重要。 + +不需要为代码写很多文档,好的代码格式,良好的注释、完善的单元测试可以很大程度上代替针对代码而写的文档。 + +Markdown 是一种非常好的文档格式,可以让你更专注于内容上,而不是文档格式上面。 + +在线文档工具优于离线文档工具,在线文档有很好的版本管理,也更方便多人协作。像 GitHub WIKI、石墨文档、Google Docs、Evernote 等都是非常好的在线文档工具。 + +对于文档的撰写,要作为一个正规的项目任务进行,安排人、安排时间,放到项目计划中去。就像前面说的“懒得写”文档的情况,一旦把文档当成一个与开发同等重要的任务去执行,就没有借口去犯懒了。 + +重要的是,文档的写作一样需要多练习,写的越多,就越熟练。 + +总结 + +今天,带你一起分析了为什么不爱写项目文档的原因,也解释了为什么写文档很重要。 + +没时间写或者懒,不能成为不写文档的理由。对于重要的项目文档,就应该加入到日常的开发任务中,把写文档,摆在和设计、开发同等重要的位置。从某种角度来说,写不好文档,代码也很难写好。 + +针对程序员不爱写项目文档的情况,我也提出了切实可行的写文档的方法。比如说不会写,就可以从模仿别人写的文档开始,然后从粗到细,不断迭代,配合一些图表,就可以写出不错的项目文档。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/17需求分析到底要分析什么?怎么分析?.md b/专栏/软件工程之美/17需求分析到底要分析什么?怎么分析?.md new file mode 100644 index 0000000..22b17eb --- /dev/null +++ b/专栏/软件工程之美/17需求分析到底要分析什么?怎么分析?.md @@ -0,0 +1,279 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 17 需求分析到底要分析什么?怎么分析? + 你好,我是宝玉,我今天想与你分享的主题是“需求分析”。 + +通过前面的学习,我们知道在瀑布模型中,第二个阶段就是需求分析阶段,同时需求分析的结果也决定了后续的系统设计、开发、测试等阶段能否顺利如期进行。即使是用敏捷开发,同样也少不了对需求的分析整理。 + +可以说需求就是整个产品的源头,所以需求分析的结果往往决定了产品的成败。如果没有正确把握客户需求,可能就会一步错,步步错! + +就像我在《特别放送 从软件工程的角度解读任正非的新年公开信》提到的秋千的案例: + + +客户想要一个给三个孩子玩的秋千;产品经理以为就是一个板子加两绳子就行;架构师发现除非把树截开,否则秋千没法荡起来的;程序员以为用绳子和板子连一起就完事了;而真正满足客户需求的,也就只要在绳子上挂个轮胎而已! + + + + +所以在本篇文章中,我将带你去了解:需求分析到底要分析什么?以及我们怎么样才能做好需求分析,抓住用户的真实需求,做出来客户想要的软件产品,避免失败或浪费。 + +什么是需求? + +我们日常在项目中,经常会听到“需求”这个词,比如说: + + +项目经理对产品经理说:用户给我们提了一个需求,想要一个给三个孩子玩的秋千,你分析一下;- + +产品经理对架构师说:我们现在有一个需求,在树上拴两绳子,再吊一块板子,你做一下设计。 + + +很明显,这两个需求的意思不一样,前面这个需求是用户需求,后面这个需求是产品需求。 + +用户需求是由用户提出来的,期望满足自身一定需要的要求,例如用户说:“想要一个给三个孩子玩的秋千。”这种原始的用户需求通常是不能直接做成产品的,需要对其进行分析提炼,最终形成产品需求。 + +产品需求就是在分析提炼用户真实需求后,提出的符合产品定位的解决方案。就像上面“在树上栓两绳子,再吊一块板子”,就是产品经理针对用户需求提出的解决方案。 + +需求分析是要分析什么? + +其实对用户需求的分析,不是一个动作,而是一个过程。需求分析,就是对用户需求进行提炼分析,最终形成产品需求的过程。 + +而针对每个用户需求的需求分析过程,需要经过三个步骤。 + +第一步:挖掘真实需求 + +大部分用户提的需求,都不见得是其真实的需求,需要透过现象看本质,去挖掘其背后真实的需求。就像福特汽车创始人亨利福特说过的: + +如果我最初是问消费者他们想要什么,他们应该是会告诉我,“要一辆更快的马车!” + +这里“要一辆更快的马车”就是一个典型的用户需求,但这并非是用户的真实需求,用户的真实需求,需要通过分析才能得到。 + +要分析用户的真实需求,可以从三个角度入手。 + + +目标用户:用户不同,诉求也不一样; +使用场景:使用场景不一样,解决方案也会有所不同; +想要解决的问题:用户背后想要解决的问题是什么。 + + +我们假设目标用户是普通乘客,使用场景是日常出行,那么用户想要解决的问题其实并不简单是“要一辆更快的马车”,想要更快的马车只是用户自己能想到的解决方案,而他想解决的问题是“更快更舒适的出行方式”。 + +而现实项目中,大多数人需求分析的不正确,就是因为没有挖掘出用户的真实需求。 + +我们再看之前的秋千项目,目标用户是三个孩子,使用场景是一起户外玩耍,想解决的问题其实是能有一起玩的娱乐设施。 + +第二步:提出解决方案 + +我们知道了目标用户,其使用场景和想要解决的问题,就可以结合产品定位,提出相应的解决方案。 + +比如针对想要“更快更舒适的出行方式”日常出行的乘客,我们就可以提出汽车的解决方案,而不一定要局限于马车,汽车能更好的满足用户需求。 + +针对三个孩子想有一个在户外一起玩的娱乐设施这个需求,我们可以提供一个轮胎式的秋千,就可以很好的满足他们的需求,我们甚至可以建一个小型游乐园。 + +第三步:筛选和验证方案 + +在提出方案后,我们需要对方案进行筛选,比如对于秋千项目,建小型游乐园的方案虽然能满足需求,但是成本太高,需要排除掉。 + +在选好方案后,还需要对方案进行验证,以确保方案能解决用户需求。 + +在传统瀑布模型中,选定方案后,会写成产品设计文档,走相应的评审流程,评审完成后再进行设计、开发和测试,测试完成后会让客户再进行验收。而敏捷开发,在整个开发过程中,每个迭代或者关键的里程碑,也一样需要客户进行验收。 + +通过以上三步,就可以对用户需求进行提炼分析,最终形成产品需求。 + +所以在需求分析过程中,分析的就是一个个用户的需求,找出背后的真实诉求,再有针对性的提出解决方案。 + +对于解决方案,要进行筛选和验证,有些不可行的用户需求不会变成产品需求,可行的用户需求会按照优先级进入实施阶段,最终变成产品。 + +怎样做需求分析? + +前面我介绍了对单个用户需求的分析,主要经过三个步骤: + + +第一步:挖掘真实需求; + +第二步:提出解决方案; + +第三步:筛选和验证方案。 + + +而软件项目的用户需求,从来就不是单一的,而是一系列需求,所以对于软件项目的需求分析,还需要增加收集整理的步骤。整个过程是迭代进行的,如下所示: + + +收集需求:对用户需求进行收集整理; + +分析需求:对需求进行分析,挖掘用户真实需求; + +需求评估:筛选过滤掉不可行的需求; + +需求设计:针对用户需求提出解决方案,设计成产品方案; + +验证需求:验证方案是否可行。 + + + + +我在美国 DePaul 大学读书的时候,曾在学校兼职,当时接到一个项目,要为计算机学院的网络教学系统做一个网页版的播放器。 + +我们知道现在的课堂里面,老师上课的时候,会用电脑放 PPT 或者课件,同时还要在黑板(也有的是白板)上写写画画辅助说明。 + +DePaul 大学的网络教学系统,就是在老师上课的时候,用摄像头把老师讲课的整个过程都录制成视频,同时也会用特殊的软件,把当时电脑屏幕上显示的内容,和白板上写的内容,都录制下来。 + +这样选网络课程的同学可以通过网络直接观看,既不会漏了老师讲的内容,也不会错过老师在电脑上放的和白板上书写的内容。播放器要做的就是要播放录制的教学视频、电脑屏幕和白板。 + +我将以这个项目为例,讲讲如何做需求分析。 + + +收集需求 + + +这个项目的原始需求是老师给我的,只是告诉我要做这样一个播放器,让学生能看教学内容。而这个需求还不够,我还需要继续收集用户需求。 + +收集用户需求有很多方法,这里列举部分: + +头脑风暴:就是大家一起开会头脑风暴讨论; + +用户调研:通过调查问卷或者访谈,通过问用户一些问题收集反馈; + +竞品分析:通过分析其他同类产品的功能获得需求; + +快速原型:通过原型来收集反馈,收集确认需求。 + +拿播放器的项目来说,头脑风暴没有足够的项目成员,也没有同类产品可以做竞品分析,做原型的话,成本有点高,所以用户调研就是最适合的收集需求的方法。它不仅简单,而且能收集到真实的用户反馈。于是我通过微信群、邮件、用户访谈从老师、领导和学生那分别收集了一些反馈。 + +老师们没有什么有效反馈,因为他们基本不需要用到这个软件,领导们有个需求就是希望能播放字幕,而很多学生希望能有 2 倍速快进功能。 + + +分析需求 + + +收集了需求,就要分析用户的真实需求,这是最难的部分,也是最体现产品经理需求分析水平的地方。 + +用户需求背后的真实需求有三个层次: + + +表层需求:用户对解决问题的期望,例如马车更快; + +深层需求:用户的深层次动机,诉求产生的原因,例如乘客对出行速度的要求; + +底层需求:人性本能的需求,例如对安全感对舒适的追求。 + + +要分析好用户需求背后的真实需求,就是要结合“目标用户”和“使用场景”,按照上面三个层次去思考。 + +我们拿刚才播放器为例,目标用户是学生,使用场景是学校机房或者家里,希望解决以下问题。 + +字幕的问题: + + +表层需求:显示字幕; + +深层需求:语言不好,跟不上老师节奏; + +底层需求:聋哑学生无法听到声音,只能通过字幕学习。 + + +快进的问题: + + +表层需求:能快进播放; + +深层需求:可以节约学习的时间,提高效率; + +底层需求:取得好的学习成绩 + + +经过这么一分析,基本上就对于用户的真实需求心里有数了。 + + +需求评估 + + +需求收集分析完了后,还需要进一步评估,以决定做还是不做,优先级如何,先做哪些再做哪些。 + +需求评估考虑的因素有: + + +可行性:技术能否实现; + +成本:人力成本、时间成本; + +商业风险和收益:有没有商业上的风险,收益是否合理; + +紧急性与重要性:是不是用户迫切的需求。 + + +如果确定可行,还需要评估其优先级。评估优先级一个简单的方案就是用“紧急重要四象限”的方法来区分: + + + +复杂一点的有 KANO 模型,如下图所示。 + + + + +红色曲线,是用户认为必须要有的功能; + +绿色曲线,就是用户明确提出的需求; + +黄色曲线,属于兴奋型需求,就是用户自己没想到,超出预期的功能。 + + +回到我们播放器的例子: + + +红色曲线(必须要有的功能):能播放视频、播放电脑屏幕,播放白板; + +绿色曲线(用户明确提出的功能):字幕、2 倍速快进; + +黄色曲线(超出预期功能):10 秒快进、10 秒快退、在时间轴上记录笔记。 + + + +需求设计 + + +在分析和评估完需求后,还需要提出解决方案,也就是对需求进行设计,做出来有效的产品设计方案。最终的产品设计,会落实到人机交互上面,用户可以通过软件界面交互。 + +现在产品设计方面,各个平台都有一套比较成熟的界面标准控件,大部分产品设计都可以基于标准界面控件,组合成满足需求的用户界面,在满足功能的前提尽可能做得易用和美观。 + +在需求设计的时候,可以用草图、原型设计工具、界面设计工具进行设计。 + +在需求设计阶段,可以参考其他成熟的产品。比如我在设计播放器时,也是通过借鉴其他软件的设计来完成的,比如说向 Youtube 借鉴了视频播放器的设计,向 Skype 的电话会议系统借鉴了其播放区域切换的交互,最终完成了产品设计。 + + + +\5. 验证需求 + +在需求设计好后,还需要进行验证,看解决方案是否能满足用户的需求。 + +对需求的验证方式其实是贯穿整个软件项目生命周期的,在需求分析阶段,会反复验证确认设计好的需求是否满足用户的真实需求,例如各种设计评审。 + +在产品开发完成后,也需要有需求的验收,以确保开发出来的软件产品是客户想要的,满足客户需求的。 + +现在很多互联网产品,还有一种基于数据的验证需求方式,也就是 A/B 测试。 + +设计好一个功能上线后,并不直接让所有用户使用,而是先给一小部分用户使用,然后分析数据,看使用这个功能的用户群和不使用这个功能的用户群,在营收、访问量、活跃度等关键数据上是更好还是更坏。如果好,就加大比例,如果数据不好,可能就会调整甚至取消这个功能。 + +我在设计播放器的时候,首先用 PPT 做了一个简单的草图,拿去给老师确认,收集一些反馈后,写了一个 PC 版的软件原型,拿给一部分同学试用。在收集反馈后,做了一些修改和调整,最终确认了产品的设计。 + + + +在需求分析完成后,就可以基于需求分析形成的文档,进行设计和开发了。(DePaul 大学的网络教学系统产品演示) + +总结 + +今天带你一起学习了软件工程中一个非常重要的知识点:需求分析。 + +需求分析,就是一个将用户需求变成产品需求的过程。要做好用户需求的分析,需要找出来隐藏在用户需求背后的真实需求,还要针对用户的真实需求提出解决方案,最终验证方案是不是能满足好用户需求。 + +需求是整个产品的源头,很多软件项目失败的原因就在于没有做好需求分析,软件中很多浪费也来源于需求没想清楚导致的返工。做好需求分析对于软件项目来说非常的重要。 + +要做好软件项目的需求分析,需要做好需求的收集整理工作,然后对收集好的需求进行科学的分析,评估是不是可行以及划分优先级,对可行的需求项进行设计,最后还要验证设计出来的结果是不是满足需求。 + +希望你通过这节课的学习,能科学地运用好需求分析的知识,对项目的需求分析把好关,保证最终产品能满足用户需求,超出用户预期。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/18原型设计:如何用最小的代价完成产品特性?.md b/专栏/软件工程之美/18原型设计:如何用最小的代价完成产品特性?.md new file mode 100644 index 0000000..301a32c --- /dev/null +++ b/专栏/软件工程之美/18原型设计:如何用最小的代价完成产品特性?.md @@ -0,0 +1,228 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 18 原型设计:如何用最小的代价完成产品特性? + 你好,我是宝玉,我今天想与你分享的主题是“原型设计”。 + +我们都知道,软件项目中很多问题都和需求相关,比如说需求不明确,需求变更。这些问题轻则导致返工造成浪费,重则导致项目失败带来巨大损失。所以在软件工程中,搞明白需求是一件至关重要的事。 + +上一篇我带你学习了如何分析需求的方法,而分析需求,同样也离不开工具的支持。所以这一篇,我将带你学习需求分析中原型设计的用法,借助原型设计,用最小的代价完成产品特性。 + +什么是原型设计? + +对于原型设计,很多程序员可能比较陌生,但是对于产品经理来说,原型设计却是日常工作中最常用的技能之一。因为原型设计,是产品经理确认需求、设计产品最重要的沟通工具。 + +其实最早的原型设计,并不是作为一个需求沟通工具存在的。 + +原型设计的发展历史 + +早在上世纪 70 年代,在瀑布模型提出后,很大程度上改进了软件项目的开发。但是需求不明确、需求多变的问题从那时候开始就是一大难题。 + +《人月神话》的作者弗雷德里克·布鲁克斯(Frederick P. Brooks, Jr.)在《没有银弹 - 软件工程中的根本和次要问题》中第一次提出了:“在获取和制订软件需求时,将快速原型开发作为迭代计划的一部分”。 + +后来快速原型就逐步发展成为一个开发模型,叫快速原型模型,我在《04 瀑布模型之外,还有哪些开发模型?》这一篇里也有介绍。这种模型的特点就是快速开发,快速修改。目的是为了解决客户的需求不明确和需求多变的问题。 + +注意,这里的快速原型模型,是开发软件项目的一种模式,还不是工具。 + +给你举个例子,如果用快速原型模型开发网站,大概分三个阶段。 + +第一个阶段就是纯静态 HTML 页面,能看到页面什么样子,没有后台,无法保存数据。这种静态页面开发成本不高,速度很快,改起来也方便,做好了就可以拿去跟客户确认需求。 + +客户看了后就能知道产品长什么样子,是不是满足要求,会提出明确的反馈意见。根据反馈,开发方会继续修改静态页面。 + +第二个阶段就是模拟一个后台服务,没有数据库,数据直接保存内存中。但是可以让网站有真正的交互:从网站添加内容就能显示出来,修改网站内容,网站显示的内容也会跟着修改。 + +这个阶段客户可以在网站上体验交互,也能完整的体验操作的流程,可以进一步针对交互再提出反馈,开发方根据反馈继续修改。 + +第三个阶段就是完成最终的后台服务,接入真正的数据库或者其他后台服务,完成整个网站的开发。由于前面两个阶段,产品经理已经把需求和交互确认清楚,所以这个阶段的开发,就没有太多需求上的反复和修改,可以高效的完成设计和开发。 + +简单来说快速原型模型就是,第一阶段确认界面布局和内容,第二阶段确认交互,第三阶段实现。 + +通过快速原型模型来开发,可以低成本、快速地确认好需求。但也有一个问题:整个过程单靠产品经理是无法完成的,必须要有开发人员配合才能完成。而对产品经理来说,要开发人员配合还是一件高成本的事情。 + + +低保真原型设计 + + +于是有产品经理用线框图来代替第一阶段。线框图画起来简单,纸和笔就可以,展示效果不错,通过线框图可以直观地看到界面上有什么,布局是什么样的,一样可以用来和客户确认需求。 + + + +图片来源:WikiPedia + +线框图简单方便,可以起到沟通需求的效果。但缺点也很明显,就是看起来不够真实,不方便反映界面之间的关系,另外也不能反映界面交互。所以线框图这种模式也叫低保真原型。 + + +中等保真原型设计 + + +再后来就有像 Axure 这样专业的原型设计软件产生,不仅可以反映界面上的布局和内容,还可以展示网站的整体结构和交互。也就是说,借助原型设计工具,可以达到前面快速原型开发前两个阶段同等的效果。 + + + +图片来源:我参与过的某项目原型设计截图 + +这种原型设计,可以很好的用来确认需求和界面交互,虽然制作难度上比线框图要复杂一点,但是不需要开发人员介入,产品经理完全可以自己搞定。 + +但这样制作出来的原型,也不能做到 100% 真实,因为它在界面的真实度、色彩上要比最终产品差一些,所以也被称之为中等保真原型。 + + +高保真原型设计 + + +近些年移动端快速发展,对于移动端来说,因为界面比较小,布局和内容上已经没法玩出什么花样。所以客户更追求界面的美观和交互的炫酷,对原型的保真度要求也就越来越高。 + +所以很多原型工具就在高保真方面狠下功夫,让你简单操作就可以做出漂亮的界面和炫酷的交互,甚至完成后都不需要再做 UI 设计了。 + +当然,高保真原型的学习成本和制作成本都要高于低保真原型,所以变更成本更高,而且也很容易导致产品经理花大量时间在细节的调整上,影响整体的进度。所以通常高保真都会和低保真原型设计配合使用,先用低保真原型快速确认清楚需求,再用高保真原型确认最终的交互和 UI 设计。 + +就这样,原型设计从最开始的一种快速开发模式,逐步演进成了今天的原型设计工具。让产品经理不需要会编程知识,也可以做出很酷的软件原型,从而可以低成本、高效率的确认清楚产品需求。 + +怎么做好原型设计? + +虽然说现在原型设计工具已经让制作原型越来越简单,但即便如此,原型设计也可以算得上是一个小项目了。因为要做好原型设计,不仅要考虑单个界面怎么设计,还要考虑这个产品整体有多少个界面,各个界面的关系和流程是什么样的。 + +要做好原型设计,可以借鉴我在《02 工程思维:把每件事都当作一个项目来推进》中讲的内容,用工程方法来完成。 + + + +参考工程方法,我们可以将每次原型设计过程分成四个部分:分析、设计、实施和验证。 + +这里,我以极客时间 App 为例,假设我们需要制作一个极客时间的 iPad 版,应该如何制作原型呢? + +第一步:分析 + +在原型设计时,通常属于需求的最初阶段,需求还是很模糊、不具体的。所以这个阶段首先要做的,就是要对用户的需求有个初步的了解,分析清楚原型设计的目标是什么。 + +比如说,我们要设计极客时间的 iPad 版,其实在内容上,完全可以基于 iPhone 版本,只是在布局上,交互要重新设计,充分发挥 iPad 大屏幕的优势,展现更多有价值的信息。 + +第二步:设计 + +在对需求进行初步分析后,需要开始对原型进行整体设计。在设计阶段,主要从两个维度来考虑: + + +从信息架构的维度,考虑清楚整个产品的信息架构,划分出模块; +从使用流程的维度,考虑清楚界面之间的流程。 + + +画产品的信息结构图 + +产品的信息结构,就像一本书的目录,整体描述了架构信息。 + +在做原型设计前,先梳理清楚整体结构,有助于帮你想清楚产品有哪些功能模块,模块之间的关系如何,哪些模块是公共的,哪些模块是面向不同用户显示不同内容的。 + +参考极客时间 iPhone 的结构,我们可以把主体的信息架构梳理出来,如下图所示: + + + +在考虑清楚主体结构后,可以进一步细化。例如其中“讲堂”下面虽然分成了“专栏”、“视频课程”、“每日一课”和“微课”,但其实点进去都是一样的,我们可以称之为“课程模块”,对于课程模块,我们可以进一步细化。如下图所示: + + + +这样你就可以一步步将整体信息结构从粗到细,一点点整理清楚。 + +画产品使用流程图 + +用户在使用产品时,会在不同的模块之间跳转,比如说你从极客时间进入到一个没订阅过的专栏,还可以点击订阅按钮进入订阅界面,订阅成功又可以返回专栏界面。 + +所以,需要用流程图把这些界面之间跳转的逻辑梳理清楚。在设计流程图的时候,要重点考虑用户的使用场景,结合使用场景设计好流程。 + +举例来说,如果当前用户进入到专栏首页,如果用户没有订阅,最重要的就是让用户可以方便的订阅,然后继续阅读;如果用户已经订阅,就没必要显示订阅相关内容,直接可以看到文章列表,选取想看的文章直接阅读。 + +需要注意,画产品使用流程图时,不仅要考虑正常使用的流程,同时也要考虑清楚异常的情况。比如说用户留言输入错误,网络失败,怎么处理?如果把用户辛辛苦苦输入的消息弄丢了,将会让用户体验大打折扣。 + +我们以进入专栏,阅读专栏文章这个流程为例,画一个简单的流程图,来说明各种不同情况下的流程和跳转关系。 + + + +通过这样的流程图,可以考虑清楚界面和界面之间的跳转逻辑。 + +第三步:实施 + +在设计好整体的信息架构和使用流程图后,就可以开始对每个界面画流程图了。 + +在具体到界面时,要优先考虑满足产品需求,然后是让界面好看好用。 + +比如说阅读专栏文章这个界面,在 iPhone 上,屏幕很小,显示的信息有限,到 iPad 上,有了更大的屏幕,就可以增加更多的内容。但是注意不能造成太多的信息干扰,要突出重点,增强体验。 + +所以我们可以保留 Tab 导航的设计,让用户可以随时回到一级界面,并且针对 iPad 界面特点,对位置做一些针对性调整。 + + + +另外在专栏阅读时,一个常见的场景就是看完一篇后,想切换到目录查看其他文章。可以针对 iPad 的交互特点,点击显示目录,方便在文章之间切换跳转。 + + + +这样一个界面基本上就初步完成了。接下来还可以对一些可以点击的界面元素,例如按钮,增加跳转操作,按照前面产品使用流程图的设计,将界面之间连接起来,让用户可以方便的从一个界面跳转到另一个界面。 + +第四步:验证 + +原型设计完成后,还需要一个很重要的环节就是验证,产品经理自己反复验证几遍,如果发现有流程上走不通或者使用不方便的地方先自己调整。调整好了交给其他人去体验,让他们提出反馈意见。 + +一般在正式的项目中,针对原型设计,需要有相应的评审会议,让大家提出反馈,根据反馈再作出调整。 + +比如说我前面设计的文章阅读界面,在交给朋友体验后,他给我的建议是:这样的设计看起来文章之间跳转方便了,但是要返回专栏页面反而是不方便了。而相对来说,返回专栏是一个更常见的操作。所以对竖版界面来说,这样的设计可能会让用户不知道如何回去,不如还是改回传统的返回式导航。 + +我觉得他说的很有道理,所以最终界面变成了这样: + + + +而这样的调整,在原型设计工具中,几分钟就完成了,非常小的代价就完成了一个产品设计的确认。 + +经过分析、设计、实施、验证这四个阶段,再反复的修改和确认几次,基本上就可以做出来不错的原型设计了。 + +如何选择合适的原型设计工具? + +原型设计工具,选择非常多。我建议你选择的时候,可以从几个维度考虑: + +面向的平台:Web、桌面、手机; + + +保真度:中等保真度还是高保真度; + +功能:是否满足你的要求; + +成本:价钱是否可以接受。 + + +这里推荐几款主要的原型设计工具,供参考。 + +Axure RP:Axure RP 曾一度是原型设计工具的代名词,历史悠久功能强大,可以制作网站、桌面软件、移动 App 的原型。 缺点是专业度较高,价格高。 + +墨刀:墨刀 是一款优秀的国产原型设计工具,可以制作网站、桌面软件、移动 App 的原型。上手相对容易,价钱也较 Axure 便宜很多。 + +Adobe XD:Adobe XD 是 Adebe 出的一款设计兼原型设计工具,可以制作出高保真原型,对于设计师尤其容易上手。 + +ProtoPie:ProtoPie 是一款高保真原型设计工具,不需要编程基础,可以做出逼真强大的交互效果。 + +Framer X:Framer X是一款高保真的原型设计工具,功能很强大,但是需要一定的编程基础,尤其适合程序员使用。 + +关于原型设计工具更多的资料,可以到“人人都是产品经理”网站的原型设计分类下,可以找到很多有价值的资料。 + +总结 + +今天带你一起了解了原型开发的演变历史。原型开发,从一个软件开发模型,逐步演变成了一个需求设计工具,让产品经理不用依赖程序员就可以作出逼真的产品原型,也大大降低了项目成员了解需求的难度。 + +原型设计,让产品经理可以用最小的代价完成产品特性,逐步成为产品经理确认需求、设计产品最重要的沟通工具。原型设计工具有很多可以选择的,建议从面向的平台、保真度、功能和价格等多方面因素综合考虑。 + +要做好原型设计,可以结合工程方法,分成四个阶段:分析、设计、实施和验证。 + + +分析阶段,搞清楚用户的需求,原型设计的目标; + +设计阶段,划分好产品的信息架构,设计好产品操作的流程; + +实施阶段,按照设计的结果,对每个界面制作原型,并且将界面组织起来,让界面之间可以相互跳转; + +验证阶段,和项目成员、客户进行确认,收集意见反馈,根据反馈进行修改。 + + +如果你的项目还没有把原型设计作为确认需求、设计产品的沟通工具,可以考虑推广应用起来,不仅上手容易,而且可以帮你降低确认清楚需求的成本。 + +如果你打算做自己的产品,先不要着急动手写代码,不妨先做一个原型出来。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/19作为程序员,你应该有产品意识.md b/专栏/软件工程之美/19作为程序员,你应该有产品意识.md new file mode 100644 index 0000000..73e523d --- /dev/null +++ b/专栏/软件工程之美/19作为程序员,你应该有产品意识.md @@ -0,0 +1,225 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 19 作为程序员,你应该有产品意识 + 你好,我是宝玉,我今天分享的主题是:作为程序员,你应该有产品意识。 + +最近电视剧《都挺好》热播,没想到其中一段台词却引发了很多程序员的集体焦虑。台词说的是:“作为一个程序员,你的年龄已经很大了!我问你,你学新东西有年轻人快吗?” + +是呀,年纪越来越大,而新技术却层出不穷,是难免会焦虑。但如果你真的每个新的热点技术都去跟,都去学,就可以不焦虑了吗?我看也未必,因为新技术一直会有,学习也都是有成本的,只要你不能一直跟上新技术的步伐,你就会一直焦虑。 + +那焦虑是怎么产生的呢? + +在我看来,焦虑通常来源于压力,压力来源于对未来的不确定,对未来的不确定来源于不知道自己的价值在哪里,不知道未来是不是还能持续创造价值,会不会失业。 + +会不会失业,取决于你创造的价值是否高于你的工资水平,否则确实是有失业的风险。所以要想不焦虑,我们就要考虑如何提升自身价值,只要自己创造的价值够大,就不担心自己会失业,减少很多不必要的焦虑。 + +程序员的价值 + +虽然通常来说,技术水平越高,工资越高,但并不都是这样。你的工资,通常是和你创造的价值正相关的。而程序员的价值通常体现在两个方面。 + +第一,你的价值体现在你所做的产品之上。 + +也就是说,你所做的产品越有价值,你的价值就越大,相应的工资也就会高。 + +这也解释了为什么同一个公司内,负责热门产品的部门,奖金都能多分一点;在效益好的公司,不但不担心裁员,反而钱也拿的多。这些年程序员的待遇相对于其他行业要高,也主要是因为软件和互联网行业的产品估值高。 + +所以说,程序员的价值,并不完全是体现在技术上的,而在于用技术做成了产品,产品创造了价值,再回过头来成就了程序员的价值。 + +第二,你的价值体现在团队中的稀缺性。 + +很多时候程序员其实没机会去选择产品的。但即使在同一个产品中,技术水平相当的程序员,价值也有差别。那些价值高的程序员通常在技术上或者技术之外都有一技之长: + +有的程序员能搞定别人搞不定的技术难题; + + +有的程序员擅长培训新人; + +有的程序员擅长和业务部门沟通; + +有的程序员能高质量地完成功能模块; + +有的程序员能按照需求设计好的架构,可以让团队高效率低成本地完成需求。 + + +这些有一技之长的程序员,能帮助团队创造更高的价值,也因为其独特性,难以被取代,具有稀缺性,所以价值也更大。 + +那怎样来提升价值呢?努力提升自己技术水平,让自己成为技术大牛,这肯定是每个程序员都坚持在做的事。但技术水平提升到一定程度后,会有瓶颈的,进展会非常缓慢。 + +这时如果也在其他领域同步发展,就会起到事半功倍的效果。比如说有的程序员会发展写作能力,写很多好的技术文章,在业界具有影响力;有的培养产品意识,让自己在技术之外,还能更好理解产品需求,能很好地和产品经理沟通,根据业务需求做出好的设计,写出高质量代码,帮助团队在项目过程上做的更好。 + +写作固然是提升个人价值很好的方式,但要在写作上有成就,需要建立在长时间不断练习的基础上。而产品意识,是程序员的固有思维中比较欠缺、正好可以互补的,相对比较容易掌握,也能取得明显的效果。 + +什么是产品意识 + +产品意识,本质就是一种思维方式,一种站在产品角度思考问题的方式。如果细分一下,产品意识包含:商业意识、用户意识和数据意识。 + +商业意识 + +所谓商业意识,就是所做的产品是要有商业价值的。比如说成功的商业产品有 Windows、iPhone、Google 等,这些产品不仅满足用户需求,同时也能创造商业价值,让这些公司变成成功的商业公司,雇用了大批优秀的程序员,从而可以继续研究更多产生商业价值的产品。 + +其实很多程序员也有做产品的梦想,而且也有人付诸行动,业余时间做了不少产品,但是鲜有成功的。其中一个根本的原因就是,他们做的产品其实没有什么商业价值。 + +比如说程序员热衷于做个 Github 客户端、博客系统,虽然说确实有用,但是却没什么商业价值,没有用户愿意付钱,导致难以持续。 + +商业意识的另一方面其实是成本,成本意识也是程序员容易忽视的。比如说: + + +有时候为了炫技,采用了更难更酷的技术方案,而忽视了所采用的方案会导致很高的开发成本; + +花了太长时间去开会而忽略了开会的成本; + +有时候又为了省钱,舍不得买一些成熟的商业组件或服务,反而是浪费了更多成本。 + + +如果程序员有商业意识,就可以在项目中有更好的成本意识,为项目节约时间、经济等成本,帮助团队打造更有价值的产品。 + +用户意识 + +所谓用户意识,就是说做产品时,你要能挖掘出用户的真实需求,让产品有好的用户体验。这需要你要有同理心,能站在用户的角度去思考和体验产品。 + +大部分程序员可能更多专注于程序上,所以在用户意识上确实有所欠缺。举例来说: + + +一个产品功能,产品经理在细节上没有定义清楚,程序员可能并不会主动提出,最终做出来的产品会不好用; + +在做技术方案时,更追求技术炫酷,而不是用户体验更好; + +在设计接口时,并没有考虑调用者的便利性。 + + +如果程序员能跳出纯技术的局限,多一点用户意识,想到的问题将会多了很多维度,比如说: + + +能让自己的负责的模块有更好的体验; + +让自己的技术方案更好地满足用户需求,用户更满意; + +让自己设计的接口、API 更好用,与同事愉快合作。 + + +做到这样,无论对产品还是对自身,都是价值的提升。 + +数据意识 + +所谓数据意识,就是在产品设计、产品运营时,通过数据来发现问题、证实结果。 + +典型的有 A/B 测试,通过数据来发现用户更喜欢哪个功能,哪个功能带来更多的收入。像微博的“时间乱序”功能,虽然很多大 v 吐槽,但是数据证明了这是一个好的产品设计,最终还是一样上线。上线后新浪根据数据不断优化,到现在反倒是很多人喜欢这个功能。 + +程序员虽然逻辑很好,但是大多对数据倒是不敏感,对编译警告、测试覆盖率、程序 Crash 的比例、API 错误率、一个函数内上千行代码、性能指标等等这些数据经常选择性忽略。 + +还有个典型的例子就是语言框架之争,程序员经常为某些语言或者框架争论不休,其实不妨基于数据分析,讨论上会更加客观。比如,从数据里你就可以明显看到 jQuery 和 React 近些年在前端的发展趋势。 + + + +其实产品意识,并不难理解,只是需要你往前更迈一步,在商业意识、用户意识和数据意识上去多思考,就可以帮你在项目中做的更好。 + +如何培养产品意识? + +那么程序员要怎样培养产品意识呢?要培养产品意识,其实和程序员转管理的类似,首先要解放思想,然后要改变习惯,最后要多实践。这么说可能比较抽象,我们逐条展开来看。 + +首先要解放思想 + +解放思想,其实就是说,对于程序员,不要总是单纯的用技术眼光看问题,也可以从产品的角度看问题。这两者有什么区别呢? + +举个例子,办公聊天软件 Slack 可能很多人都知道,是一款在线沟通协作软件。在国内可能知名度要低一些,但是在海外有大量企业用户,非常的火。 + +这款软件在刚出来的时候我就知道,不过那时候我觉得这不就是一个聊天室么,我都能写一个!我站在技术角度也做了不少分析: + + +这个软件前端还是用的 jQuery,如果用 React 应该可以做的更好; +这个软件跨平台是基于 HTML5,如果是原生代码也许性能可以更好; +还是 REST API,如果用 GraphQL 那 API 请求效率会更好; +从国内访问的话,速度太慢了,应该架设一些国内的服务器或者 CDN。 + + +而现在,我会同时也从产品角度分析 Slack: + + +它的商业价值,在于它把工作的沟通,变得高效又好玩; +消息都在云端,检索也方便,也不担心像微信一样换设备消息就没了; +其开放 API 的设计,让它和很多其他办公软件可以无缝集成,极大提升了效率; +Slack 需要付费才能查看到 10000 条之前的消息,这是个很有意思的设计,当你已经有 10000 条消息时,说明已经有足够的意愿去付费了。 + + +其实这两个角度也代表了两种不同的思维方式,一种是很多程序员熟悉的技术思维,一种是产品思维。 + +技术思维会关注用什么技术,关注技术细节,关注功能“如何”实现;产品思维会关注用户体验,关注一个功能所创造的价值,会去思考为什么要或者不要一个功能。 + +这两种思维不同,也很容易导致沟通上的误解。比如程序员会更多考虑技术实现,产品经理会更多考虑产品设计。如果都能往前迈一步,程序员有产品意识、产品思维,产品经理能有一点技术思维、工程思维,那么相互沟通起来就会更通畅。 + +这两种思维之间的差别,其实也正是要培养产品意识的关键点。要想培养产品意识,就是要从纯粹的技术思维,有意识地培养产品思维。从关注技术、技术细节,到关注用户体验,关注产品创造的价值。 + +然后要改变习惯 + +改变习惯是是指在日常使用产品、开发产品的时候,多站在产品的角度思考,去思考它的商业价值、用户体验、使用场景等等。 + +比如你学习专栏用的极客时间 App,你聊天用的微信。使用一些具体功能时,可以思考一下这些问题: + + +这个产品的商业价值是什么? + +为什么要有这个功能?是为了满足用户哪方面需求的? + +这个产品目标用户是谁? + +这个功能的使用场景是什么? + +这个功能的体验好不好?有没有更好的方式提升体验? + + +也许你没法马上有清楚的答案,但寻找答案的过程也是一个很好的学习的过程。 + +如果你是程序员,在开发功能、设计架构的时候,也不妨跳出技术之外,从产品角度思考一下: + + +这个功能的需求是什么?我是否完全理解了需求? + +如果你是这个功能的用户,你觉得还有哪些地方值得改进? + +哪些技术可以帮助提升用户体验? + +这个 API 用起来是不是好用?有没有更好的设计? + +除了对产品的思考,日常工作中,遇到一些问题,也可以从产品思维的角度去想想。 + + +一个常见的场景就是,产品经理一下子提交了一堆新的需求任务,影响了正常的开发进度,这时候你不一定要拒绝他,你就可以和他一起把需求的优先级梳理一下。你就知道哪些要优先做,哪些其实没有那么着急,方便更好的安排你的工作。 + +还有像产品经理提交了一个技术很复杂的需求,你可以不用着急马上拒绝或者说要很长时间,而是跟他探讨一下这个需求背后要解决什么问题,是不是可以有替代的解决方案,既能降低技术难度又可以满足需求。 + +自己开发的功能模块完成后,可以把自己当成用户试试,如果觉得体验不好或者有更好的建议,都可以反馈给产品经理。 + +最后要多实践 + +光有理论还不够的,最好能自己实践一下。 + +你不妨在业余时间做个小应用程序,或者设计一个原型,做完了再找你的朋友试用一下,让他们提提意见。在做产品的过程中,你自然会去站在产品的角度去思考,这会让你对产品方面有更多感悟。 + +其实不用担心没有什么好的想法,可以从日常生活中,自己的需求、家人和朋友的需求中,去找到合适的产品需求。我当初做过很多产品都是这样的来的: + + +给孩子照的照片太多,写了个工具批量生成缩略图; + +老婆工作上需要经常对网页截取整张图片,设计一个帮助截图的工具; + +帮父亲建了个家谱应用; + +给校友们建了一个网上交流的论坛,写过一个论坛系统。 + + +用心观察,类似的需求你也可以找到很多。 + +总结 + +今天,我们一起分析了程序员的价值体现,主要体现在两方面:所创造产品的价值和自身的稀缺性。程序员有产品的意识,可以帮助产品和自身提升价值。 + +产品意识,主要包括商业意识、用户意识和数据意识。要提升产品意识,首先要解放思想,然后要改变习惯,最后要多实践。 + +当你慢慢培养了产品意识,不仅可以通过技术来打造更高价值的产品,也可以让你在技术之外有一技之长,能在项目中创造更大价值,减少技术快速革新带来的焦虑感。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/20如何应对让人头疼的需求变更问题?.md b/专栏/软件工程之美/20如何应对让人头疼的需求变更问题?.md new file mode 100644 index 0000000..e28a2e2 --- /dev/null +++ b/专栏/软件工程之美/20如何应对让人头疼的需求变更问题?.md @@ -0,0 +1,178 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 20 如何应对让人头疼的需求变更问题? + 你好,我是宝玉,我今天分享的主题是:如何应对让人头疼的需求变更问题? + +我以前在国内做开发的时候,加班加点是家常便饭。这几年在美国工作,极少加班,但是产出却并没有下降,所以我一直在思索其背后的原因。这里面涉及因素很多,包括大环境、管理水平、配套设施等,但是有一个因素至关重要,那就是需求变更。 + +在国内很多软件公司,需求变更是常态,开发到一半,很多原始需求就发生了变化,这时候当初的设计已经不能满足要求了,很多代码需要修改,不得不加班加点来赶上进度。 + +反观不加班的美国公司,需求确定后很少变更。这样开发人员就可以针对需求有良好的架构设计,而不用考虑太多可能的变更,从容地在项目计划的时间内完成任务,而不需要加班加点。 + +在需求变更这个事情上,没有赢家,每个人都是受害者。 + +程序员自己辛苦的工作白费了,得不到成就感,因为频繁变更的需求,不得不在设计的时候考虑很多可能的变更,导致代码臃肿,代码质量降低,加班加点成了常态。甚至有人说:“杀一个程序员不需要用枪,改三次需求就可以了!” + +产品经理也觉得很委屈:“客户要改,我有什么办法?”程序员和产品经理似乎变成了两个对立的岗位,程序员怪产品经理乱改需求,产品经理觉得是客户造成的,人人都觉得自己委屈。客户同样不满意,觉得做出来的软件不是他想要的,进度总是在延后,还想加钱? + +既然大家都不满意,那么我们就需要想办法去改善,这也是软件工程这门学科存在的目的和意义。 + +目前也已经有很多管理需求变更的解决方案,比如这两个常见的解决方案。 + +方案一:增强需求变更流程,让需求变更规范起来。 + +这个方案简单来说,就是通过严格的流程,来避免一些没有意义的变更,从而达到管理需求变更的目的。 + +方案二:快速迭代,缩短版本周期。 + +这个方案是另一个思路,就是将大的功能拆分,每个版本周期仅实现一部分功能需求,周期较短,这样需求发生变更时,就可以快速响应。 + +不过,在看到这两个方案后,我还是希望你不满足于当前的答案,自己停下来思考一下这两个问题: + + +这些方案是否解决了你当前项目的问题? +如果换一个项目环境,当前方案是否依然适用? + + +之所以要思考这样的问题,是因为对于像软件工程这样偏理论知识的学习,你一定不要仅仅停留在了解有什么样的解决方案上,而是要追本溯源,研究问题背后的原因,研究理论背后的来龙去脉。 + +因为,就算你记住了再多的解决方案,换个项目环境可能就不适用了。所以我们要多去思考和分析逻辑,这样未来遇到类似的问题时,才可以做到对症下药,选择合适的解决方案,甚至在没有现成解决方案的情况下,能自己创造合适的解决方案。 + +为什么建筑工程中少有需求变更? + +要解决需求变更的问题,你首先要知道,软件开发行业中的需求变更是怎么来的。 + +我很喜欢拿软件工程和建筑工程进行对比,你可以思考一下,同样是工程,建筑项目也是有需求变更的,但却不会像软件项目这么频繁和失控。为什么呢? + +我总结一下,这里有两个主要原因:需求的确定性和需求变更的成本。 + +原因一:需求的确定性 + +建筑需求是很具象的,而软件工程的需求是抽象的。所以建筑项目里面,无论是提出需求还是变更需求,客户和施工方都明确地知道他们想要什么。 + +软件需求则经常是抽象、模糊、不精确的,模糊不清的需求导致在软件开发有了雏形后,才慢慢想清楚真正的需求是什么,从而导致需求变更。 + +举个例子,客户最开始对软件界面的颜色是没有任何要求的,当第一版本的软件给客户看的时候,客户觉得白色背景太难看了,希望换成蓝色的;第二版本换成蓝色后,客户现在已经觉得黄色更好看,希望改成黄色背景;第三版本的时候,产品经理担心客户还想换颜色,就直接做成了换皮肤功能,用户可以自己选择颜色,客户还是不满意,问能不能把背景换成图片…… + +是不是很熟悉?类似的事情其实经常发生在我们日常的工作场景里。 + +原因二:需求变更的成本 + +建筑项目里面的需求变更,我们都很容易和成本挂钩,因为这些东西已经是生活常识了。而与此相对的是,很多人,包括很多老板都对软件项目需求变更导致的成本增加缺少系统认识。 + +举个例子,装修房子的时候,如果墙面已经刷成白色了,但是客户想都刷成蓝色,那么他会很清楚,这涉及一系列成本:需要重新购买涂料、需要找人重新粉刷。 + +但换成一个软件项目,客户想把界面的白色背景换成蓝色的,他会觉得这是很简单也是理所当然的,甚至产品经理也会这么想,他会对程序员这么说:“不就是换个颜色吗?几行代码的事,客户让换就换了嘛!” + +但是实际上,软件项目的需求变更,哪怕是换一个背景颜色,同样是要涉及成本的:需要修改所有涉及背景颜色的代码,需要更新相关测试代码,还需要对涉及的界面重新测试。 + +你可以说这成本是架构设计水平不到家导致的,但是如果设计时就考虑到要有支持换背景颜色的功能,那么开发的工作量从一开始就上去了,成本同样是提升了。 + +回到文章开始时我们提到的美国程序员不加班的问题,为什么美国的产品经理不敢随意更改需求?因为在美国很多 IT 公司都是工程师文化,工程师相对比较有话语权,正常情况下是不会加班加点的,所以产品经理变更需求的成本很高,在确定需求时,必须慎之又慎。 + +如何解决需求变更问题? + +说完了原因,咱们再来看看解决方案。 + +首先,你需要意识到,在软件项目开发中,需求变更其实是不可避免的,一味抵制需求变更也是不可取的。你能做的就是利用软件工程的知识,理解需求变更背后深层次的原因,找到合适的方案来改善,积极拥抱合理的需求变化,减少不必要的需求变更。这是大的前提条件,也是共识的基础。 + +好,既然引起需求频繁变更的原因我们已经清楚了,那么,怎样有针对性地想解决方案呢?这里你也不妨停下来思考一下,你会想到哪些办法? + +我的经验是从源头着手,既然需求变更的原因是需求不确定和需求变更成本太低,那么我们就针对性地提出相应的解决方案: + + +提升需求确定性,把需求分析做好,减少需求变更; + +提高需求变更的成本,让客户或者产品经理不能太容易就变更需求,这样就可以达到减少需求变更的目的。 + + +但在实施的时候,我们会发现一个问题,假如一味提高需求变更的成本,会让客户满意度下降,也造成了产品经理和开发人员之间的对立,不利于项目协作。 + +所以我们从另一个角度思考:需求变更之所以让你痛苦不堪,也是因为需求变更让项目成员付出了高昂的代价,例如返工、加班,如果我们可以低成本地响应需求变更,那么一样可以达到管理需求变更的效果。 + +所以解决方案上可以再加上一条: + + +降低响应需求变更的成本,可以方便快捷地响应需求变更。 + + +接下来,我来举一个在实际项目遇到的案例,我们一起来分析一下,通过对这个需求变更场景的分析,来解决以上提到的这些问题。 + +案例分析 + +我有个大学同学叫加龙,毕业后自己开了个公司,早些年企业建站火的时候专门接企业网站的活。刚开始的时候很艰苦,也没几个人,甚至都没有专门的产品经理。 + +开发流程比较简单,就是先把项目谈下来,客户提一个建站的需求,然后他们去开发网站,开发好了拿给客户演示。而客户在看到网站演示后,几乎每次都会提出很多变更的需求,例如说颜色变一下、布局变一下、给留言功能加上关键字过滤功能等等。客户还喜欢直接在 QQ 上找负责开发的程序员,让给改一下。 + +创业初期,加龙同学真的是不容易,每天和几个程序员一起加班加点,就是为了应对客户这种频繁变更的需求。如果你是加龙,参考前面总结的几种解决方案,你会怎么做? + +加龙作为软件工程专业毕业的学生,我觉得他当时运用软件工程知识去改善需求变更问题上是做得非常好的。他其实并没有采用一个单一的解决方案,而是分阶段逐步改进。 + +第一步:规范变更流程,提升客户变更成本。 + +加龙其实也知道,通过提升需求确定性,做好需求分析,和客户多沟通确认,是可以有效减少需求变更的。但是他当时确实人手太有限,也没有专业的产品经理,不能短时间内去提升需求分析、产品设计的水平,所以他第一步选择提升客户变更需求的成本,这样可以马上产生效果。 + +于是在后面的项目中,在和客户签订合同时,他会和客户约定,如果有需求变更,先统一提交到他那里,然后他甄别后再决定是否做,什么时候做,是否要重新签订新的附加合同(增加额外费用)。通过制定一系列标准,让双方合作的流程变得更规范。 + +这样,程序员就可以专注于开发,也不会因为频繁的需求变更影响进度,大家不用那么累,收入也在稳步上升。但是需求变更的情况还是时有发生。 + +第二步:用原型设计低成本响应需求变更;做好需求分析和确认,减少需求变更。 + +加龙在挺过最艰难的创业初期后,雇佣了一个全职的产品经理,专门去和客户确认需求。这个产品经理很专业,每次在了解完客户的需求后,不急于让程序员马上去写代码,而是自己先用 Axure 这样的原型设计工具,做一个简单的交互原型,给客户演示。 + +于是客户会针对原型的效果提出一些修改意见,他再快速地修改原型,这样反复确认,等到客户没有什么修改意见后,再让开发着手实现。 + +通过原型设计的方式,不仅可以方便地与客户沟通需求,还可以灵活响应需求变更。 + +通过提升需求确定性,加龙的公司进一步降低了需求变更的情况发生,营收又上了一个台阶,又增加了几个程序员和产品经理。 + +第三步:通过灵活的架构和强大的配置,低成本响应客户需求变更。 + +加龙公司经过两年的发展后,敏锐地发现其实大部分企业网站的功能都是很相似的,主要差别还是在界面样式上。 + +这些年大部分网站的开发其实都是把前一个网站复制一份,修修改改,但是这样还是效率太低,如果可以做到定制化,就可以更高效地定制网站。 + +于是加龙从公司抽调了几名骨干程序员,成立了一个专门的项目组,把这两年做的网站类型做了分析,做了一套建站系统,有点类似于后来流行的像 WordPress 这样的博客系统,可以通过换皮肤的的方式来定制界面,通过插件的方式增加功能。 + +由于前期积累充分,大约半年后他们就开始使用这套系统去建站,一下子就把建站的成本大大降低啦。而且当客户的需求有变化时,只要后台做简单的配置就可以马上支持需求变更。 + +但是这个模式也有问题,就是有些特别个性化的定制需求,还是满足不了。不过这也没关系,对于需要个性化的客户,要么增收额外的费用,要么就直接放弃掉。 + +如果咱们对他这三步采取的方案做一个总结,还就是我前面说的三个方案: + + +提升需求确定性; +提高需求变更的成本; +降低响应需求变更的成本。 + + +只不过他根据公司不同阶段的特点,来灵活运用。这就是我的同学加龙在处理需求变更时分阶段采取的方案,是不是跟你想的一样? + +总结 + +今天我通过对比建筑工程中的需求变更,和你一起分析了软件工程中需求变更产生的原因。需求频繁变更,主要是由于需求不确定和变更成本过低导致的。并由此提出了三种不同的解决方案。 + + +提升需求确定性,来减少需求的变更。这种方案的优势就是对需求理解透彻,后期返工少,缺点是对产品经理的需求分析能力要求很高。 +提高需求变更的成本,规范需求变更流程,减少需求变更。这种方案的优势就是可以马上起到效果,缺点就是过于繁琐的流程不利于项目协作。 +降低响应需求变更的成本,积极应对需求变更。这种方案的优势在于可以快速响应需求变更,能快速试错尽快调整,缺点在于对软件架构和项目管理要求比较高。 + + +就像我的同学加龙那样,你可以根据项目的实际情况,对比这些方案的优缺点,选择适合你的解决方案。 + +例如你是做企业外包项目的,客户不懂又喜欢瞎掺和,那么就可以适当提高变更成本,甚至每次变更都可以计入项目成本中;如果你是做互联网项目,需要快速推出产品,那么就可以选择降低响应变更成本,快速迭代,快速试错,尽快调整。 + +如果你是项目经理,希望你通过这次对需求变更的学习,能针对项目特点,制定合适的需求变更流程,选择适合项目的开发模式,管理好需求变更,从而提升整个项目的开发效率,避免重复返工导致的浪费。 + +如果你是产品经理,希望你通过这次学习,对需求变更导致的成本有很高的重视,努力提升专业水平,勤于和客户、开发测试人员沟通,勤总结,尽可能将需求变更的可能性降到最低。 + +如果你是开发或者测试,希望你在以后遇到需求变更时,不要置身事外,抱怨产品经理不专业,因为需求并不是产品经理一方的事情,项目参与者都有责任一起把需求分析做好。 + +在一些需求分析和变更的关键阶段,要主动参与其中,从开发和测试的角度提供一些专业的建议,去思考需求产生的背景,避免对立情绪。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/21架构设计:普通程序员也能实现复杂系统?.md b/专栏/软件工程之美/21架构设计:普通程序员也能实现复杂系统?.md new file mode 100644 index 0000000..04aff90 --- /dev/null +++ b/专栏/软件工程之美/21架构设计:普通程序员也能实现复杂系统?.md @@ -0,0 +1,237 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 21 架构设计:普通程序员也能实现复杂系统? + 你好,我是宝玉,我们已经正式进入到“系统设计”这个主题模块,今天我们先来聊一聊“架构设计”。 + +早些年,软件很简单的时候,不需要需求分析和架构设计,直接采用边写边改(Code And Fix)模型,也能做出来。后来软件复杂了,就对程序员要求特别高了,所以早些年的软件开发,都是个人英雄主义盛行。比如张小龙一个人完成了 Foxmail,求伯君完成 WPS,王江民写 KV 杀毒软件…… + +不过,那时候对于普通程序员来说,去写这样复杂的系统,也是可望而不可及的。再后来软件产品越发复杂后,靠高手的开发模式也不可行了。 + +软件需求越来越多,而高手又是稀缺资源,所以要解决的一个问题就是:让普通程序员也能参与其中,一起实现复杂系统,而不必依赖于很多精英。 + +为什么软件项目需要架构设计? + +要想实现让普通程序员也能实现复杂的软件系统,我们先要看看什么样的是复杂的软件项目。复杂的软件项目,通常有两个特点:需求不确定和技术复杂。 + + + +关于需求不确定,我在前面的文章已经讲了很多,我们主要来看看技术的复杂性。技术的复杂性,主要体现在四个方面。 + + +需求让技术变复杂 + + +如果需求本身很复杂,那么对应的技术也会很复杂。比如说你做一个个人博客网站,和做一个淘宝这样的网站,技术复杂度是有天壤之别的。 + +要响应需求的变化,也会让技术变复杂。对于明确的需求,相对来说技术实现是容易的。但是,随着需求地不断变化,新的需求可能会破坏原有的代码架构,导致系统越来越臃肿复杂,维护也越来越难。 + + +人员会让技术变复杂 + + +现在软件开发通常不是一个人,而是一个团队。团队成员水平不一样,擅长的技术方向也不一样,让这一群人有效地协作也是很大的考验,简单的技术问题也会变成复杂的技术问题。 + + +技术本身也是复杂的 + + +现在软件项目中选择编程语言、框架、技术组件、数据库等技术或工具,还可能需要应用像微服务、大数据、人工智能技术,这些技术本身就是复杂的,普通人都需要通过一定的学习才能掌握。 + + +要让软件稳定运行是复杂的 + + +软件在开发完成后,要发布运行,但运行时也充满了各种不确定性。比如说明星发布八卦可能会导致微博宕机;阿里云宕机导致很多基于阿里云的系统也跟着一起无法提供服务。 + +因为技术的这些复杂性,会导致软件开发变得很复杂,开发成本很高。而架构设计恰恰可以在这些方面很好地解决技术复杂的问题。 + +首先,架构设计可以降低满足需求和需求变化的开发成本。 + +对于复杂的需求,架构设计通过对系统抽象和分解,把复杂系统拆分成若干简单的子系统。就像淘宝这样复杂的网站,最终拆分成一个个小的微服务后,单个微服务开发的难度,其实和个人博客网站的难度已经差不太多了,普通程序员都可以完成,降低了人力成本。 + +对于需求的变化,已经有一些成熟的架构实践,比如说像分层架构这样把 UI 界面和业务逻辑分离,可以让 UI 上的改动,不会影响业务逻辑的代码;像 Wordpress 这样基于插件和定制化的设计,可以满足绝大部分内容类网站的需求,降低了时间成本。 + +其次,架构设计可以帮助组织人员一起高效协作。 + +通过对系统抽象,再拆分,可以把复杂的系统分拆。分拆后,开发人员可以各自独立完成功能模块,最后通过约定好的接口协议集成。 + +比如说前后端分拆后,有的开发人员就负责前端 UI 相关的开发,有的开发人员就负责后端服务的开发。根据团队规模还可以进一步细分,比如说前端可以有的程序员负责 iOS,有的程序员负责网站,这样最终各个开发小组规模都不大,既能有效协作,又能各自保证战斗力。 + +再次,架构设计可以帮助组织好各种技术。 + +架构设计可以用合适的编程语言和协议,把框架、技术组件、数据库等技术或者工具有效的组织起来,一起实现需求目标。 + +比如说经典的分层架构,UI 层通过选择合适的前端框架,例如 React/Vue 实现复杂的界面逻辑,服务层利用 Web 框架提供稳定的网络服务,数据访问层通过数据库接口读写数据库,数据库则负责记录数据结果。 + +最后,架构设计可以保障服务稳定运行。 + +现在有很多成熟的架构设计方案,可以保障服务的稳定运行。比如说分布式的架构,可以把高访问量分摊到不同的服务器,这样即使流量很大,分流到单台服务器的压力并不大;还有像异地多活这样的架构方案可以保证即使一个机房宕机,还可以继续提供服务。 + +其实,满足需求和需求变化、满足软件稳定运行是架构的目标,对人员和技术的组织是手段。架构设计,就是要控制这些技术不确定问题。 + +我们也可以说:架构设计,就是通过组织人员和技术,低成本满足需求以及需求的变化,保障软件稳定高效运行。 + +什么是架构设计? + +你现在已经知道了架构设计的价值,那么究竟什么是架构设计呢?要说清楚这点,我们可以分别从目标和方法两个角度来看。 + +最开始我以为架构设计的目标是满足业务需求,保证软件能正常工作。后来发现这其实只是最基本的要求,因为很多糟糕的架构设计,也能满足业务需求,让系统正常运行。 + +比如说有人把一个小网站拆分成几十个微服务运行,也是一种架构设计,但是这样,无论是开发成本还是运行成本都很高。 + +所以架构设计的目标,是用最小的人力成本来满足需求的开发和响应需求的变化,用最小的运行成本来保障软件的运行。 + +架构设计,已经有很多成熟的方法。比如说: + + +使用微服务这样的架构,把复杂系统拆分成一系列小的服务,服务再拆成功能模块,让人员更好地分工协作; + +通过前后端分离,让程序员更专注于某个知识领域,降低开发难度; + +用分层设计来隔离业务逻辑,减少需求变更带来的影响。 + + +这些架构设计的方法,其实都是基于工程领域分而治之的策略,本质上就是将系统分拆,将人员分拆。但是光拆还不够,拆完了还得能拼回来,所以你要清楚架构设计的“道”。 + +架构设计的道,就是组织人员和技术把系统和团队拆分,并安排好切分后的排列关系,让拆分后的部分能通过约定好的协议相互通信,共同实现最终的结果。 + +这很像乐高玩具,将一个个小的模块通过接口拼接在一起,搭成一个大的模型。只不过在程序中,换成了其他形式的接口,比如前后端通过 REST 这种协议交互,内部组件之间通过方法调用;在软件项目中,人员从大的开发团队被分拆成小组后,小组之间通过流程规范协作。 + +如何做好架构设计? + +架构设计,总给人感觉技术很高深。所以很多新手一提到架构设计,就有些畏足不前,不知道该从什么地方下手。 + +架构设计要做好,确实不是一个容易的事,需要大量的经验积累。但业界已经有了很多成熟的架构设计模式,我们不需要闭门造车,可以在理解清楚业务需求后,找到相近的架构设计,然后基于成熟的架构设计方案,进行改造,变成适合自己业务需求的架构。 + +接下来我就以极客时间的服务端为例,来简要说明一下如何做架构设计。假设现在你要设计第一版本极客时间服务端的架构,只有专栏课程一个核心功能,目标用户访问量是日 PV 上万,峰值每秒 10 个左右访问,对稳定性要求高。那么,你该如何做呢? + +第一步:分析需求 + +架构设计,最基本的就是要能满足业务需求,所以搞清楚需求是至关重要一步。而产品需求,只有功能的描述,界面的交互,还需要进一步进行抽象。 + +一个常用的分析方法就是分析用例,也就是了解主要用户角色和其使用的场景。 + +我们如果把极客时间的专栏课程功能画成用例图,大概如下所示: + + + +从图上可以看出,有四种角色:编辑、专栏作者、未付费用户和付费用户。每个角色有其独特的功能,有些角色之间还有通用的功能。还需要注意的一点是,每个用户,都可能会通过不同的设备终端来使用这些功能:网站、安卓手机、iPhone 手机。 + +第二步:选择相似的成熟的架构设计方案 + +在了解清楚需求后,就可以从业界成熟的架构设计模式中选取一个或几个。当然,具体选择哪些架构设计模式,需要你根据平时的学习积累来做判断。到这个阶段,同时还要考虑使用的语言和框架。 + +极客时间服务端,主要包含两部分内容,一个是给手机客户端提供的 API 服务,还有就是网站需要的 Web 服务。第一个版本其实访问量并不大,我们完全可以把 API 服务和网站服务合并成一个服务。另外专栏的内容,文字内容涉及数据库的存储,同时音频涉及文件存储。 + +这其实是一个典型的网站架构,可以基于传统的分层架构来实现。分层架构按照水平方向将系统拆分成几个层,每层都有清晰的角色和分工,不需要关心其他层的细节。 + + + +图片来源:软件架构入门 + +在选择好架构方案后,还需要考虑选择什么语言和开发框架。这部分选择需要根据团队情况和项目情况来综合评定。 + +比如说团队以 PHP 程序员为主,就没必要贸然选择 Java 作为开发语言;如果以 js 程序员为主,就可以考虑使用 Nodejs。因为这样的选择,能让团队不需要太多的学习成本。 + +第三步:自顶向下层层细化 + +其实我们专栏从工程思维开始,到后面提到的写文档、原型设计,都是建议能从整体到局部,这样更具有大局观,不容易过早陷入技术细节中。架构设计也是如此,好的实践是自顶向下层层细化。 + +在选择好成熟的架构设计方案后,可以基于方案,层层细化,逐步完善、调整和优化。 + + +部署架构 + + +这类分层架构网站,部署也比较简单。为了减少运维成本,我们可以基于云服务设计部署架构,选购云数据库和文件存储,选购虚拟机作为网站服务器。 + +那么部署架构可以比较简单,如下图所示: + + + +基本上这个架构就可以基本满足运行需求。但要做到稳定性高还不够,万一数据库挂了或者网站服务器挂了,都可能会让服务中断一段时间。 + +所以我们可以增加一台异地网站服务器和一个异地云数据库实例作为备份,这样一旦网站宕机或者数据库有问题,可以切换到备机,马上恢复访问。所以调整后架构如下: + + + +这样我们就很好的满足了对运行稳定性的要求。 + + +分层和分模块 + + +按照分层架构的思路,我们可以把系统分成四层。 + +用户界面层:用户界面,负责展现功能以及和用户交互。 + +服务层:API 服务和 Web 网站服务。 + +业务逻辑层:实现业务逻辑,比如说如何读取用户订阅的专栏列表。 + +数据访问层:对数据库的访问。 + +数据存储:用数据库保存数据,文件库保存音频文件。 + +分层分好后,还需要基于前面的用例图,把相同的功能抽象出来,设计成模块,比如说留言相关的都放到留言的模块,文稿相关的都放到文稿模块。 + +最终的设计图大概会是这个样子: + + + +在分层和分模块之后,就可以很好的对人员进行分工,可以把具体工作细分到某一层的某个模块。 + + +API 设计、数据库设计、模块的设计 + + +在分层和分模块的设计完成后,就可以对 API 进行设计,对数据库进行表设计。这部分就不展开细讲了。 + +还有一些模块的设计,还可以让负责开发该模块的程序员参与一起设计,这样一方面让他提前熟悉设计,另一方面也可以让他锻炼设计能力,提高参与积极性。 + +第四步:验证和优化架构设计方案 + +在技术方案完成后,还需要去验证方案是不是满足设计的目标,能否满足需求和未来需求的变化,能否保障软件有效地运行。 + +方案的验证是贯穿整个设计始终的,一个完整的架构设计方案,需要有多次的评审会议,充分收集各方面的反馈,反复修改后才能最终确定下来。 + +在第二、三步,可能会生成几个技术方案,这时候就需要做出一些技术上的决策。决策时,需要考虑清楚方案是否能低成本的完成软件需求的开发,同时能低成本的运行和维护该软件。还有你要考虑架构预期要满足多长时间的业务增长,比如说半年还是一年还是三年。 + +在架构设计确定后,就可以基于架构设计的结果大家一起分工协作了。架构设计并不是确定后就不修改了,在实际开发的过程中,还需要根据情况对架构进行优化和调整。 + +比如说实际运行的时候,发现 API 访问量很大,会拖慢网站访问速度,那我们就可以考虑把 API 和网站分拆开来,各自做成单独的服务,避免相互干扰。 + +推荐学习材料 + +O’Reilly 出版过一本免费的Software Architecture Patterns, 介绍了五种最常见的软件架构。阮一峰老师有在软件架构入门中对其各种模式进行介绍。 + +还有像架构师之路这样的开源电子书,对于服务端架构中常用的设计,有很不错的总结。图解:从单个服务器扩展到百万用户的系统这篇文章对单服务器到大用户系统的演变也有通俗易懂的总结。以“前浪微博”场景为例,谈谈架构设计流程四步曲这篇文章也介绍了一种非常好的架构设计方法。 + +最后,推荐一本书:架构整洁之道。我觉得是架构设计书籍中写的最透彻最浅显易懂的一本。 + +总结 + +今天,我们一起学习了软件工程中一个非常重要的知识,那就是架构设计。 + +架构设计,是为了控制软件项目中技术复杂的问题。架构设计,通过组织人员和技术,低成本满足需求以及需求的变化,保障软件稳定高效运行。 + +架构设计可以通过四个基本步骤: + + +第一步:分析需求; + +第二步:选择相似的成熟的架构设计方案; + +第三步:自顶向下层层细化; + +第四步:验证和优化架构设计方案。 + + +通过良好的架构设计,可以有效降低开发难度和成本,让普通程序员也能实现复杂系统。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/22如何为项目做好技术选型?.md b/专栏/软件工程之美/22如何为项目做好技术选型?.md new file mode 100644 index 0000000..89dbbc9 --- /dev/null +++ b/专栏/软件工程之美/22如何为项目做好技术选型?.md @@ -0,0 +1,153 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 22 如何为项目做好技术选型? + 你好,我是宝玉,我今天分享的主题是:如何为项目做好技术选型? + +在架构设计过程中,肯定绕不开技术选型这个话题,大到架构、框架、语言选择,小到用什么组件、设计模式。 + +这也是最容易引起争议的话题,无论是现实中还是网上,到处有各种语言、框架的争论:Java 好还是 C# 好?前端框架是 Vue 好还是 React 好?跨平台手机开发,该选 React Native 还是 Flutter…… + +虽然这种争论从来没什么结果,但当你做技术选型时,却很容易受到这些信息的干扰,尤其是你身边有几个某种语言或者框架的狂热粉丝的话,他们会不停地在你旁边吹风,说他喜欢的语言或框架的各种好处。 + +包括我们自己做技术选型时,也会有很多个人偏好在里面。比如我以前对微软技术栈特别熟悉,也特别喜欢,做技术方案就会偏向微软技术栈;我喜欢 React,做前端技术选型,也会偏向 React 的方案。 + +通过上一篇架构设计的学习,我们知道,架构设计的主要目标,是要能低成本地满足需求和需求变化,低成本地保障软件运行。然而对技术的个人偏好,很可能让你在技术选型时,忽略架构设计的目标,导致满足需求的成本变高,或者运行成本居高不下。 + +所以今天,我们一起来探讨一下,在软件工程中,怎么样才能避免这种选型的倾向性,科学客观地做好技术选型。 + +技术选型就是项目决策 + +技术选型,就是在两个或者多个技术方案中选择适合当时项目情况的方案。技术选型看起来是个技术的选择,但其实是一个和项目情况密切相关的项目决策。 + +在项目中,除了技术上的选型,类似的选择也有很多,比如说产品设计中:某个功能该不该加?该选哪种动画效果?比如制定测试方案的时候,选择哪一种压力测试工具?选择哪个测试框架?这些选择,本质上就是一种项目决策。 + +要做好技术选型,就是要做好项目决策。那么怎样从做项目决策的角度来选择合适的技术选型呢? + +受制于时间、范围和成本的约束 + +我们在《08 怎样平衡软件质量与时间成本范围的关系?》中学习了项目金三角的理论,也就是项目受制于三个因素:时间、范围和成本。 + +技术决策作为一种项目决策,也要受制于时间、范围和成本,在决策时不能超出这三者的边界。 + +比如说在项目时间紧时,决策上就要偏向能提升开发速度的技术;在成本吃紧的情况下,要多用成熟的免费的框架、工具,避免用贵的商业软件或者自己造轮子提升成本;在范围大、需求多的情况下,架构就要考虑如何能简单快速完成需求。 + +还要注意一个问题就是随着项目的推进,其实制约项目的三个因素一直在动态变化,需要及时根据情况调整技术决策。 + +举个例子来说,2004 年飞信 PC 客户端做第一个版本的时候,那时候主要的约束是成本,只有一个 C++ 程序员,这个程序员会用什么技术就用什么技术,谈不上选型。 + +到 2005 年做第二版本时,有了几个人,但是时间上要求快,所以就选择了能提升开发速度的 C# Winform 技术方案。到 2008 年做第三版时,人手充裕了,也没有进度上的压力,这时候主要就追求用户体验、性能,所以又选择了 C++ 的技术方案重新开发。 + +要分析可行性和风险 + +我们在专栏前面的内容中学习了可行性研究和风险管理的知识。如果在项目决策时,不考虑可行性,不预估风险,就极有可能导致决策失败。 + +就像在《09 为什么软件工程项目普遍不重视可行性分析》那篇文章中的案例,技术选型时,没有考虑到 License 的法律问题,导致项目失败。还有在《14 风险管理:不能盲目乐观,凡事都应该有 B 计划》那篇文章中的案例,选择 React 时,没有考虑到可能导致的风险,导致项目延迟。 + +当然,换个角度说,如果在项目中,选择新技术的风险可以接受,也能满足时间、成本和范围的约束,还可以达到丰富团队技术栈的目的,那也是可以的。 + +要考虑利益相关人 + +在做项目的决策时,如果决策时没有人代表利益相关的人,就可能会做出不考虑他们利益的决策。 + +选择适合的技术选型时,也要考虑到这一点。比如说光顾着选用新酷的技术,而没有考虑客户的利益,导致成本增加,进度延迟;比如在选择 UI 组件时,只想着哪个调用方便,而不考虑产品经理的利益,导致产品体验不好。 + +项目决策中常见的坑 + +无论是技术选型也好,还是其他项目决策,经常会遇到一些坑,一不小心就会踩上去。 + + +把听到的观点当事实 + + +现在网上充斥着各种观点:一个 React 的粉丝会给你描述 React 的各种优点,而不会告诉你学习曲线有多陡峭;一个不喜欢微软技术的程序员会把.Net 贬低的一文不值;一篇鼓吹 Mongodb 多好的文章可能是收了钱的软文。 + +每个人都有自己的观点没有问题,但是不能把观点当成事实,尤其是在做决策之前,至少需要验证一下。 + + +先入为主,有了结论再找证据 + + +在做技术选型或者项目决策时,还有一个问题就是可能心中已经有了答案,后面所谓的决策,不过是寻找有利于自己答案的证据。比如说我特别喜欢 React,在做技术选型时,就会拼命寻找对 React 有利的数据作为证据,这其实可能会导致结论并不客观。 + +所以当你选择技术选型的时候,要像做项目决策一样思考分析。要想你的决策能正确,就要注意项目中范围、时间和成本的约束,要分析可行性和风险,要考虑利益相关人,最后还得要避开常见的一些坑。 + +如何做好技术选型? + +现在我们知道了要像做项目决策一样,去选择适合自己项目的技术选型,那么具体该怎样做呢? + +我们在《02 工程思维:把每件事都当作一个项目来推进》中学习了工程思维和工程方法,在《12 流程和规范:红绿灯不是约束,而是用来提高效率》中学习了流程规范。对于技术选型问题,我们一样也可以考虑借鉴工程方法设计一套流程,基于流程去做技术选型或项目决策,来保证整个过程能科学可行,充分考虑项目决策的特点,避开常见的坑。 + +对于技术选型包括项目决策类的问题,我们可以分成:问题定义、调研、验证、决策这几个阶段。 + +问题定义 + +在问题定义阶段,需要搞清楚两个问题:为什么需要技术选型?技术选型的目标是什么? + +以前看过一个技术漫画The problem is not the tool itself。 + +“我想我们已经达到 MySql 的极限,非常慢…我们得面对数据巨大的事实”,“我们应该迁移到 NoSQL,我已经准备了一个 3 个月的战斗方案,瞧瞧这”,“框架重构、培训、内部工具升级”,“等等,让我检查一下”,“你忘了建索引,加上索引可以让速度提升 20 倍”,“忘记索引?你可真行。” + + + +图片来源:Commitstrip + +这种事情在软件项目中可不少见,很多时候为了解决问题引入一个新技术,然而真的需要吗?也许我们可以基于现有技术方案进行优化,根本就不需要引入一个新的技术或新的框架。 + +还有一个就是技术选型的目标需要明确,你的技术选型目标是为了使用新酷技术呢?还是为了提升开发效率?还是为了降低开发成本? + +只有明确了技术选型的目标,才能有一个标准可以来评判该选择哪一个方案。 + +就像上面例子中提到的第二版的飞信 PC 客户端,目标就是要提升开发速度,所以就选开发效率高的 C#。 + +调研 + +在明确技术选型的目标后,就可以去调研,看有哪些技术选型可以满足目标,包括开源的方案和商业的方案。 + +在调研时,可以参考前面“项目决策的特点”中的内容,从几个方面去分析: + + +满足技术选型目标吗? + +满足范围、时间和成本的约束吗? + +是不是可行? + +有什么样的风险?风险是不是可控? + +优缺点是什么? + + +在调研结束后,可以筛选掉明显不合适的,最终保留 2-3 种方案留待验证。必要的话,可以一起讨论,最终确认。 + +验证 + +一个技术是不是合适,如果不够了解,没有应用过的话,实际用一下是很有必要的。可以通过一个小型的快速原型项目,用候选的技术方案快速做一个原型出来,做的过程中才能知道,你选择的技术选型是不是真的能满足技术选型的目标。 + +就像前面举的飞信 PC 客户端的例子,在决定第二版本是否使用 C# 开发时,其实做了大量验证工作。当时.Net Framework 还不普及,要打包整个.Net Framework 到安装包里面,这体积就太大了,这是一个很大的问题。 + +后来发现有一个产品叫 Salamander,它可以只打包程序所需要的 dll 库文件,这样体积就可以控制在 20mb 以内,最后在制作安装包时,用 7zip 压缩,就可以让安装包控制在 10mb 左右。在验证阶段,证明了安装包体积是可以缩小的,基于 C# 开发是可行的,才最终选定了 C# 的技术方案。 + +决策 + +在调研和验证完成后,就可以召集所有利益相关人一起,就选择的方案有一个调研结果评审的会议,让大家提出自己的意见,做出最终的决策。 + +必须要承认,对于技术选型来说,是有不确定性的。即使通过上面的流程,也一样可能会做出错误的决策。但有一个科学的流程,至少可以保证提升做出正确决策的概率。 + +如果遇到很纠结的情况,就需要负责决策的人来拍板了,这时候其实并不一定有对错,重点的就是做出一个选择,然后按照选择去执行。有时候迟迟不选择、不拍板才是最坏的结果。 + +在项目结束后,也要对之前技术选型和项目决策做总结,不断的完善技术选型和项目决策的机制,帮助未来更好的进行决策。 + +总结 + +今天,我带你一起探讨了技术选型的问题。技术选型,本质上是项目决策的一种,也符合项目决策的一些特点。也就是说,技术选型的选择要受制于范围、时间和成本的约束,要分析可行性和风险,要考虑利益相关人。还有一些坑要小心避开,比如要避免把听到的观点当事实,要验证;要避免先入为主,不要有了结论再找证据。 + +要做好技术选项,要有一个科学的流程,首先要明确技术选型的目标,避免没必要的引入新技术;然后要充分调研;还要对备选的方案进行验证;最终和利益相关人一起决策。 + +技术选型,也不要太过于纠结,要勇于决策,选定了就坚定的去执行。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/23架构师:不想当架构师的程序员不是好程序员.md b/专栏/软件工程之美/23架构师:不想当架构师的程序员不是好程序员.md new file mode 100644 index 0000000..d8e33a7 --- /dev/null +++ b/专栏/软件工程之美/23架构师:不想当架构师的程序员不是好程序员.md @@ -0,0 +1,163 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 23 架构师:不想当架构师的程序员不是好程序员 + 你好,我是宝玉,今天我想与你讨论一下要想成为架构师,你需要具备哪些能力。 + +很多程序员的梦想,就是将来能成为一名架构师。包括我刚学编程那时候,也是以当架构师为目标,觉得不想当架构师的程序员不是好程序员,希望将来能成为一个优秀的架构师。就像拿破仑那句名言:“不想当将军的士兵不是好士兵。” + +随着工作经历的增多,我也开始参与到架构设计中。对架构设计了解的越多,我越发觉得,其实做架构设计,并不代表一定要有一个架构师的头衔。 + +拿破仑那句名言,原句是“Every French soldier carries a marshal’s baton in his knapsack”,意思是“每个士兵背包里都应该装有元帅的权杖”。 + +元帅的权杖,意味着大局观,元帅的思维方式。当士兵背包里装有元帅的权杖,就意味着士兵也能胸中有大局观,能有元帅的思维,理解元帅在特定战场上想什么,这样能更好的执行命令,提升整体的战斗力。 + +其实拿破仑的本意是激励每一名上战场的士兵都要有大局观,有元帅的思维,并不需要每一个人都一定去当将军、当元帅。 + +这也适用于技术领域,对于程序员来说,并不代表一定要有一个架构师的头衔,而是心中有大局观,有架构师的思维,从而能理解架构设计,能写出好的程序。 + +什么是架构师思维? + +通过上一篇的学习,我们知道架构设计,是要控制技术的复杂性。对于架构师来说,要控制技术复杂性,有几种有效的方式:抽象、分治、复用和迭代。 + +架构师思维,其实就是这几种思维的集合。 + +抽象思维 + +抽象思维可以说是整个架构设计的基础。因为对于架构设计来说,是要为了满足业务需求的,而业务需求都是一些文字性的描述、原型、UI 设计图,这些需求要最终变成代码让机器执行,就必须先进行抽象,抽象成计算机能识别的模型。 + +其实抽象思维我们不陌生,因为我们从小学习的数学,就有很多抽象思维的训练。举例来说,我们小时候做的鸡兔同笼问题,看起来很复杂,但是如果我们会二元一次方程,把鸡抽象成 x,兔子抽象成 y,就可以用二元一次方程列出相应的方程式,从而求出解。 + +在软件项目中,遇到类似的场景,就会考虑抽象出来,总结一个规则和方法。有时候即使场景不同,也可以把其中有共性的内容抽象出来,可以更方便的使用。 + +举个例子,我们在之前文章中有对极客时间专栏做用例分析,其中有四个角色:编辑、作者、未订阅用户和订阅用户。其实这四种角色,都可以抽象成“用户”模型,然后通过对用户设置不同的角色属性,来应用成不同的角色。 + +还有像极客时间专栏的一篇文稿、视频课程的一节视频课,都有标题、内容、作者、留言等信息,所以可以抽象成“文章”模型,通过文章的类型、内容来区分专栏文稿还是视频课。 + +在架构设计中,对需求进行抽象建模后,可以帮助我们隐藏很多无关紧要的细节,我们在高层次的架构设计时,可以关注在几个主要的模型上,而不必关心模型内的细节实现。 + +分治思维 + +架构设计的一个重点,就是要对复杂系统分而治之,分解成小的、简单的部分。但光分解还是不够的,同时还需要保证分解后的部分能够通过约定好的协议集成在一起。 + +分治思维在架构设计中有很多经典的应用。比如说上一篇介绍的分层架构,把 UI 部分与其业务逻辑部分隔离,这样这两部分就既可以各自进行变更,又互不影响。比如说 UI 交互修改,不需要修改业务逻辑代码,业务逻辑部分对性能进行优化,不需要修改 UI 界面。而每层之间,可以通过约定好的方法或者 API 进行交互。 + +还有像我们平时说的大数据,高并发这些复杂问题,也是通过分治来解决的。要知道单台机器,无论你性能如何优化,都是有其极限的。而像“双十一”这种高峰时刻,瞬间的流量可能是几百、几千万,就需要通过设计合理的策略,分化到不同的服务器,让每个服务器的流量不至于太大。参考:秒杀系统优化思路。 + +这种分治的思维其实不仅适用于架构上,也适用于平时程序员写代码。比如说有些程序员写代码,喜欢把大量的逻辑放在一个方法或者一个类里面,最后极其难以理解和维护,如果能分拆成几个小的方法或者小的类,不仅结构更清晰,也更容易理解和维护。 + +复用思维 + +复用是一种非常简单有效的提升开发效率的方法,通过对相同内容的抽象,让其能复用于不同的场景。 + +举例来说,我们前面提到极客时间的专栏和视频课程,可以作为两个不同的模块进行开发,但是实际上内容差不多,如果能抽象成同一个“课程”模块,这样专栏和视频课程的模块就可以复用“课程”模块,不需要维护两份相似的代码,进而提升开发和代码维护的效率。后面如果要增加每日一课和微课,也不需要重新开发,只要复用之前的“课程”模块即可。 + +以前我在 DePaul 读书时,要给学校做一个教学播放的软件,由于当时技术框架选的是 React,而 React 没有合适的视频播放组件,于是我只好自己实现了一个。实现完成之后,我觉得这个视频播放功能肯定有很多人也需要,如果能复用的话会很实用。于是我把它封装后放到 GitHub 上,解决了很多人需要在 React 中播放视频的需求。到现在已经有超过 1000 个 Star。 + +复用思维在日常写程序的时候也很常用,比如有的程序员喜欢复制粘贴代码,所以经常看到很多重复的代码,如果要修改,得修改好几个地方。如果能把这些重复的代码提取成公共的类或者方法,就可以减少很多重复,让代码更简洁和易于维护。 + +迭代思维 + +好的架构设计,通常不是一步到位,而是先满足好当前业务需求,然后随着业务的变化而逐步演进。 + +就像淘宝这样的业务,它背后的架构设计也不是一步到位成现在这样,拆分成好多微服务。最开始,它也只是个普通的分层架构,后来随着业务不断扩展,逐步迭代成今天这样复杂的架构。 + +这种迭代的思维,在写程序时也很重要。因为很多程序员喜欢追求完美,期望能一步到位,然而这样带来的问题是开发成本会大量增加,导致进度延误。另一方面,如果对需求的变化预测不正确,就会有很多冗余的代码,后面难以维护。 + +其实,开发人员对以上提到的这些思维模式都不陌生,只是在实践的时候,总是有意无意地忽略了。 + +好的架构师什么样? + +对于程序员来说,培养架构师思维,并不是很难的事情。然而要成为好的架构师,光有架构师思维还不够。 + +一个好的架构师,不仅技术要好,还要懂业务;能从整体设计架构,也能在局部实现功能。 + +比如说一个做互联网软件架构设计有丰富经验的架构师,要去做建筑行业软件的架构设计,短时间内一定是很难设计出好的架构,因为他需要先熟悉建筑行业软件的业务,才能设计出符合业务特点的架构。 + +有一种架构师叫“PPT 架构师”,也就是说擅长写 PPT,画架构图。对各种热门的名词如数家珍。但是脱离一线开发,对业务和底层基础知识知之甚少。这样的架构师设计出来的架构,通常是不接地气的,实现起来会非常困难,成本也高。 + +因为作为架构师,如果不写代码,是不能体会出设计不好带来的问题,无法及时地对架构中的问题做出调整。 + +所以好的架构师,一定要是程序员出身,并且能坚持做一线程序员。也许他不需要写大量的业务代码,但至少要参与一部分编码工作,以及代码审查工作,以保证架构的正确执行。 + +好的架构师,不仅要有技术深度,还要有一定的技术广度。因为技术的选型,通常不能局限于一种技术,需要根据业务特点和团队特点灵活地选择。 + +好的架构师还有一个能力就是沟通能力。作为程序员,可能把自己的模块开发好就不错了,相对不需要太多的沟通工作。但是架构师就不一样,除了架构设计,还有大量沟通工作。 + +首先架构师要经常和产品经理打交道,反复确认需求,了解需求细节,只有这样才能分析清楚需求,了解各种用户场景。 + +然后架构师设计出来的架构,要通过文档、会议来讲给其他人听,能让其他人理解架构,用好架构。 + +所以要成为好的架构师,需要具备几个条件。 + + +有架构师思维:具备良好的抽象思维、分治思维、复用思维和迭代思维; +懂业务需求:能很好地理解业务需求,能针对业务特点设计好的架构; + +有丰富的编码经验:像抽象、分治、复用这些能力,都需要大量的编码练习才能掌握;另外保持一定量的编码经验也有助于验证架构设计; +良好的沟通能力:架构师需要沟通确认需求,需要让团队理解架构设计。 + + +具备了这些条件,就可以成为很好的架构师,设计出好的架构,组织好人员和技术,低成本的满足好需求和需求变化,以及系统的运行。 + + + +如何成为好的架构师? + +想要成为好的架构师,没有什么捷径,需要比普通程序员更多的努力才行。如果你有志向成为架构师的话,我的建议是: + + +要成为一个优秀的程序员 + + +技术好是成为架构师的基础条件。需要让你的代码容易读,容易扩展,能重用。这样通过大量的编码实践,才能逐步地培养出好的架构师思维。 + + +多模仿多学习 + + +在刚开始的时候,不用想着闭门造车,想出一个特别牛的架构。反倒不如先把业界成熟的流行的架构吃透,用好。 + +现在网络上也有很多好的开源项目,这些开源项目都有良好的架构设计,可以找几个跟你研究方向相关的项目,本地搭建一下,然后自己试一下,最好能弄一个自己的项目二次开发或者模仿一遍,做中学,是最简单有效的。 + +我以前在用 Asp.Net 的时候,就基于一个开源的 Asp.Net 项目 Community Server 做了大量的二次开发工作,这对我后来做架构设计帮助非常大,因为我从里面学习和实践了很多非常好的架构设计思想。 + + +选择好行业和平台 + + +软件其实下面细分了很多行业领域,大类有像互联网应用、企业应用、游戏应用,大类下面又有细分的小类。比如说企业应用又和各行各业的业务结合在一起的,像建筑行业软件,就需要有建筑行业的专业知识。 + +前面我说过,架构师要同时懂业务和技术,而这些行业知识,也不是短时间内能积累起来的。所以如果想当架构师,最好能选择一个合适的行业,能在一个行业里面早点积累足够的行业知识,后面做架构设计的时候,就能更好地设计出符合业务特点的架构。 + +同时,这些行业领域的业务经验,和技术结合的架构经验,也会成为你个人独特的优势,不容易被替代。 + +还有平台也很重要,好的平台,能给你更多的实践机会。所以你看极客时间上那些开课讲架构、微服务的,无一例外都是大厂出来的,因为只有大厂,才有机会去实践这种高并发大数据的架构设计。 + +如果你有志成为架构师,不能光埋头写程序,也要早做打算,选择适合你自己的行业和平台,少走弯路。 + +总结 + +今天,我们谈了“不想当架构师的程序员不是好程序员”这个话题。其实对于程序员来说,并不代表一定要有一个架构师的头衔,而是心中有大局观,有架构师的思维。从而能理解架构设计,能写出好的程序。 + +架构师思维,指的是要具备良好的抽象思维、分治思维、复用思维和迭代思维。 + +另外没有架构师的头衔,也一样可以做架构设计,只要你有架构师的能力就可以了。而好的架构师,需要具备: + + +有架构师思维; + +懂业务需求; + +有丰富的编码经验; + +良好的沟通能力。 + + +要想成为好的架构师,没有什么捷径可以走,首先需要要成为一个优秀的程序员,然后多模仿、多学习好的架构设计,最后还要早点选择好行业和平台,积累好行业的业务知识,借助平台获得大量的实践机会。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/24技术债务:是继续修修补补凑合着用,还是推翻重来?.md b/专栏/软件工程之美/24技术债务:是继续修修补补凑合着用,还是推翻重来?.md new file mode 100644 index 0000000..b7a0537 --- /dev/null +++ b/专栏/软件工程之美/24技术债务:是继续修修补补凑合着用,还是推翻重来?.md @@ -0,0 +1,224 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 24 技术债务:是继续修修补补凑合着用,还是推翻重来? + 你好,我是宝玉,今天我想与你讨论一下关于技术债务的问题。 + +做开发的同学对以下场景应该不会陌生: + + +为了赶项目进度,单元测试代码就来不及写了,打算以后再补; + +随着需求的变化,原本的架构设计已经不能很好地满足新的需求,但是又不想对架构做改动,于是就绕开架构设计增加了很多代码; + +一个旧的系统,没有文档没有注释,技术老旧,难以维护。 + + +这些问题,如果没有及时修正,就会导致代码臃肿、系统效率低下,难以维护,也难以新增功能。 + +有一个很形象的名词叫“技术债务”,用来形容上面这些架构或代码上的质量问题。 + +所以今天的课程,我将带你一起来了解一下什么是技术债务,它形成的原因是什么,以及怎么来管理技术债务。 + +什么是技术债务? + +我们在学项目管理金三角时,有一张表示软件质量与时间、成本、范围关系的三角形图,也特别解释了为什么质量要放在三角形中间,因为质量往往是其他三个因素平衡后结果的体现。 + + + +范围不减,成本不增加,还想节约时间走捷径,就会影响到质量。这个“质量”,不只是产品质量,还有架构质量和代码质量。这种对质量的透支,就是一种债务。而技术债务,就是软件项目中对架构质量和代码质量的透支。 + +技术债务确实是个形象生动的比喻,让你意识到它是和成本挂钩的,而且技术债务也有金融债务的一些特点,比如有利息,再比如技术债务也有好的一面。 + +技术债务是有利息的 + +债务的“利息”,就是在后面对软件做修改的时候,需要额外的时间成本。 + +假设我们做一个项目,在刚开始时,架构良好代码整洁,添加一个功能可能需要 4 天时间。随着项目不断维护,因为走捷径积累了一些技术债务,这时候再开发一个同样复杂度的功能就需要 5 天时间了。 + +这多出来的 1 天,就是技术债务造成的利息。因为你需要时间去梳理现在臃肿的代码,以找到合适的位置添加代码;修改代码后还可能导致原有系统不稳定,需要额外的时间去修复系统不稳定的问题。 + +技术债务不一定都是坏的 + +现实中,如果是贷款买辆豪车,一方面要支付利息,一方面车子一直在贬值,这不一定是个良性的债务;但如果你贷款买房子,虽然支付了利息,但如果房子升值,这个债务其实是良性的。 + +在软件项目中,也经常会刻意的欠一些技术债务,提升短期的开发速度,让软件能尽快推出,从而抢占市场;还有像快速原型开发模型,通过欠技术债务的方式快速开发快速验证,如果验证不可行,甚至这笔技术债务都不需要偿还了。 + +但技术借债也一样不能是无限制的,因为借债越多,利息越大,当收益抵不过利息时,就会陷入恶性循环,导致开发效率低下,进度难以保障。 + +所以对于项目中的债务,我们要清楚的知道有哪些技术债务,以及它给项目带来的收益和产生利息,这样才能帮助我们管理好这些债务。 + +技术债务产生的原因 + +如果现实中有人负债累累,那么多半有几个原因:这人对债务没有规划、生活所迫不得不借债、为了长远利益而临时借债、不知情的情况下欠了债务。 + +其实技术债务产生的原因也类似,所以《重构》一书的作者 Martin Fowler 把技术债务产生的原因分成了两个维度: + + +轻率(reckless)还是谨慎(prudent); +有意(deliberate)还是无意(inadvertent)。 + + +这两个维度正好可以划分成四个象限,如下图所示: + + + +图片来源:Technical Debt Quadrant + + +轻率 / 有意的债务 + + +这个象限,反映的是团队因为成本、时间的原因,故意走捷径没有设计、不遵守好的开发实践,对于债务没有后续的改进计划的情况。 + +例如不做设计直接编码,后期也没有打算重构代码。或者是团队成员以新手程序员为主,没有足够的资深程序员指导和审查代码。 + +这样产生的债务,短期可能还好,但是因为技术债务会一直积累,会导致利息越来越多,最终带来的负面效果会越来越大。 + + +谨慎 / 有意的债务 + + +这个象限,则反映的是团队清楚知道技术债务的收益和后果,并且也制定了后续的计划去完善架构和提升代码质量的情况。 + +比如说为了尽快发布产品,先采用“快猛糙”的方式开发,后续再对代码进行重构。 + +这样产生的债务,因为能及时偿还,所以既可以短期有一定时间上的收益,长期也不会造成负面影响。 + + +轻率 / 无意的债务 + + +这个象限,反映了团队不知道技术债务,也不知道要后续要偿还技术债务的情况。 + +比如说一些开发团队对于什么是架构设计,什么是好的开发实践一无所知,代码一团糟。 + +这样产生的债务是最危险的,因为既没得到技术债务的收益,还要偿还其产生的利息。 + + +谨慎 / 无意的债务 + + +这个象限反映了团队其实很重视架构设计和技术债务,但因为业务的变化,或者其他客观因素的原因,造成技术债务的产生。 + +比如说最初设计的时候,无法准确预测后面业务的发展,随着业务的发展,设计无法满足好新的需求。 + +这样产生的债务难以避免,但如果能及时的对架构升级、重构,就能保证不会造成严重的影响。 + +以上就是软件项目中的四种技术债务,每一种技术债务产生的原因都不尽相同,对其处理的策略不同,也会造成不同的影响。 + +如何管理技术债务? + +既然技术债务有利息也有收益,那么我们怎么能保证软件项目中的收益大于支付的利息呢? + +Martin Fowler 画过一张图,来形象的描述了设计、时间和开发速度的关系。没有设计直接写代码,从短期看确实是节约时间的,但是跨过一个临界点后,开发速度会急剧下降。 + + + +图片来源:Is it worth the effort to design software well? + +技术债务的收益和利息也是类似的道理,最初的时候,利息低收益高,欠一些技术债务是会节约时间的,但是超过一个临界点后,利息高收益低,就会大大降低开发效率。 + +所以最好能让技术债务控制在临界点之下,这就要求我们能充分了解目前项目中的债务情况,然后才好制定相应的策略,从而达到控制债务的目的。 + +识别技术债务 + +如果是现实中的债务,查查银行账户就很容易知道是不是欠债了,而技术债务却没那么直观,但识别技术债务是很关键一步,只有发现系统中的技术债务,才能去找到合适的方案解决它。 + +你要是细心观察,还是可以通过很多指标来发现软件项目存在的技术债务。比如说: + +开发速度降低:通常项目正常情况下,在相同的时间间隔下,完成的任务是接近的。尤其是使用敏捷开发的团队,每个任务会评估故事分数,每个 Sprint 能完成的故事分数是接近的。但是如果单位时间内能完成的任务数明显下降,那很可能是技术债务太多导致的。 + +单元测试代码覆盖率低:现在大部分语言都有单元测试覆盖率的检测工具,通过工具可以很容易知道当前项目单元测试覆盖率如何,如果覆盖率太低或者下降厉害,就说明存在技术债务了。 + +代码规范检查的错误率高:现在主流的语言也有各种规范和错误检查工具,也叫 lint 工具,比如 Javascript 就有 eslint,Swift 有 SwiftLint,python 有 pylint。通过各种 lint 工具,可以有效发现代码中潜在的错误和不规范之处,如果错误率高,则说明代码质量不够好。 + +Bug 数量越来越多:正常情况下,如果没有新功能开发,Bug 数量会越来越少。但是如果 Bug 数量下降很慢,甚至有增多的迹象,那说明代码质量或者架构可能存在比较大问题。 + +除了上面这些指标,其实你还能找到一些其他指标,比如你用的语言或者框架的版本是不是太老,早已无人更新维护了;比如开发人员总是需要加班加点才能赶上进度,如果架构良好、代码质量良好,这些加班本是可以避免的。 + +选择处理技术债务策略 + +在识别出来技术债务后,就需要考虑如何来解决这些技术债务了。解决技术债务有三种策略。 + + +重写:推翻重来,一次还清 + + +将老系统推翻重写是很多程序员最热衷干的事情之一了。重写系统是一种优缺点都很明显的策略,这有点像你试图把债务一次性还清。 + +优点是你可以针对当前的需求和业务发展特点,重新进行良好的设计,精简掉不需要的功能和代码。缺点就是重写通常工作量很大,在新系统还没完成之前,同时还要对旧系统维护增加新功能,压力会非常大;另外新写的系统,重新稳定下来也需要一段时间。 + + +维持:修修补补,只还利息 + + +维持现状,只对严重问题修修补补,这其实是常见的一种策略,就跟还债的时候只还利息一样。 + +修修补补相对成本低,不用投入太大精力,如果项目不需要新增功能,只需要维护还好,如果项目还持续要新增功能,越到后面,维护的成本就越高了。 + + +重构:新旧交替,分期付款 + + +重构相对是一种比较折中的策略,就跟我们采用分期付款的方式偿还贷款一样。 + +每次只是改进系统其中一部分功能,在不改变功能的情况下,只对内部结构和代码进行重新整理,不断调整优化系统的结构,最终完全偿还技术债务。这种方式优点很多,例如不会导致系统不稳定,对业务影响很小。缺点就是整个过程耗时相对更久。 + +这三种策略并没有绝对好坏,需要根据当前项目场景灵活选择。有个简单原则可以帮助你选择,那就是看哪一种策略投入产出比更好。 + +无论选择哪种策略,都是要有投入的,也就是要有人、要花时间,而人和时间就是成本;同样,对于选择的策略,也是有收益的,比如带来开发效率的提升,节约了人和时间,这就是收益。 + +如果收益高于投入,那这事可以考虑做,否则就要慎重考虑。对一个生命周期不会太久,或者没有什么新功能开发的系统,花大力气去重构、重写是不合算的,不如维持现状。而如果有新技术新产品出现,可以以极低的成本替代原有系统,这样重写就是个好方案。 + +比如说我们项目中有个很老的自己写的 CMS 系统,问题很多也没法维护,于是最近找了一个开源的 CMS 系统,把原有的数据一导入,马上就很好用了,也没有花多少时间。 + +通常,如果你纠结于不知道该选择哪一种策略时,那就选择重构的策略,因为这是相对最稳妥有效的。 + +实施策略 + +当你选择好用哪种策略处理技术债务之后,就可以实施你的策略了。不同的策略可能实施方式上略有不同。 + + +对于重写的策略,要当作一个正式的项目来立项,按照项目流程推进; + +对于重构的策略,要把整个重构任务拆分成一个个小任务,放到项目计划中,创建成 Ticket,放到任务跟踪系统中跟踪起来; + +对于维持的策略,也要把需要做的修补工作作为任务,放到计划中,放到任务跟踪系统中。 + + +实施策略的关键就在于要落实成开发任务,作为项目计划的一部分。 + +预防才是最好的方法 + +前面说的方法策略,都是针对已经存在的技术债务而言的。其实最好的方法是预防技术债务的产生。像下面这些方法,都是行之有效的预防措施: + + +预先投资:好的架构设计、高质量代码就像一种技术投资,能有效减少技术债务的发生; + +不走捷径:大部分技术债务的来源都是因为走捷径,如果日常能做好代码审查、保障单元测试代码覆盖率,这些行之有效的措施都可以帮助你预防技术债务; + +及时还债:有时候项目中,因为进度时间紧等客观原因,导致不得不走捷径,那么就应该把欠下的技术债务记下来,放到任务跟踪系统中,安排在后续的开发任务中,及时还债及时解决,就可以避免债务越来越多。 + + +如果团队能提高对技术债务的认识,防患于未然,就能让技术债务保持在一个合理的水平,不会影响到开发效率。 + +总结 + +今天,我带你一起了解了软件项目中技术债务的知识。解释了技术债务的概念,技术债务,就是软件项目中架构质量和代码质量的透支。 + +技术债务,也并不都是坏事,如果合理利用,就可以在短期内缩短时间,但是后期如果不偿还技术债务,也会对项目及个人造成不好的后果。 + +技术债务产生的原因有四个方面:轻率 / 有意的债务、谨慎 / 有意的债务、轻率 / 无意的债务和谨慎 / 无意的债务。 + +可以分三个步骤来管理技术债务:识别技术债务、选择处理策略和实施策略。处理策略有三种:推翻重写、修修补补和重构。 + +对于技术债务,是继续修修补补凑合着用,还是推翻重来?其实取决于哪一种策略的投入产出比更好,如果推翻重来代价太大,那么就应该谨慎考虑,不如先修修补补或者局部重构;如果修修补补难以维持,就要考虑重写或者重构。 + +对于技术债务,还是要在日常开发中有好的意识,不走捷径,防患未然,预防技术债务的发生。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/25有哪些方法可以提高开发效率?.md b/专栏/软件工程之美/25有哪些方法可以提高开发效率?.md new file mode 100644 index 0000000..5415c79 --- /dev/null +++ b/专栏/软件工程之美/25有哪些方法可以提高开发效率?.md @@ -0,0 +1,171 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 25 有哪些方法可以提高开发效率? + 你好,我是宝玉,今天我想与你讨论一个每个开发人员和项目管理者都关心的话题:如何提高开发效率。 + +我其实也一直很关注这个话题,收集了很多方法让自己工作变得卓有成效。通过对这些方法的应用,我也可以算得上是一个高效的程序员:曾一个人在很短时间完成了飞信 Web 版客户端;在 DePaul 上学之余,帮学校完成了在线教学播放器系统的改造;三个月时间帮公司完成了主站从 jQuery 到 React 的迁移。 + +如果让我对学过的这些方法做个整理和总结,再进一步精选提炼,我觉得对我影响最大的是“积极主动”、“以终为始”和“要事第一”这几条看似简单的工作原则。 + +积极主动,行动起来改变自己 + +相信你也和我有过相同的经历。成为一个高效程序员,最大的阻力不是来自于不知道方法,而是自己的消极心态。遇到进度延迟、效率低下之类的问题,你就会下意识觉得: + + +时间进度太紧了; + +我已经尽力了; + +最近加班太多了没精神; + +产品经理太不靠谱了,需求没想清楚,害的我瞎忙活。 + + +是的,你也知道这些答案都很消极负面,可是怎么控制自己不这么想呢?首先你要知道,无论这些事情的本质责任在于环境还是个人,抱怨排斥的心态对于实际工作的改进是没有任何帮助的。 + +当然,很多人也知道抱怨没用,但具体怎样才能做到不抱怨,并且积极主动呢?史蒂芬・柯维写在他的《高效能人士的七个习惯》书中,对这个问题提到了两个行之有效的建议,我们可以结合着软件开发来分析一下。 + +想想再回应 + +我还记得第一次有人给我介绍单元测试很好用,能让你效率更高、代码质量更好时,我的第一反应是不可能,这样明明要多写很多代码,怎么可能会高效? + +于是我有一段时间是很排斥的,直到后来参与一个已经有单元测试的项目,尤其是在重构代码的时候,我发现修改了大量代码后,程序还是很稳定,当时便觉得应了网友的那句话,“真香”! + +每个人对于外界的刺激都会做出反应,本能的或者习惯性的,就像我前面举的例子,遇到事情会本能的觉得都是外部原因。如果一直这样,那就会进入恶性循环,变得更加消极麻木。 + +但如果在回应之前,给自己一点时间想想,站在积极的方面理性思考一下,就可以去控制你的本能反应。 + +所以很多次,就在我脱口而出“不可能”或者“不行”的时候,我提醒自己再想想。于是我会改口说:“我试试”、“我再想想”。这样很多次提醒自己以后,会一点点由“不可能”的本能变成“我想想”的习惯。 + +后来有人跟我说 CI(持续集成)很好,我思考过了之后进行了尝试,在 Github 上建了一个项目,把 CI 搭起来试了一下,觉得真的是很好。如果我还是秉持着以前的消极心态,不知道又要晚多久才能去尝试。 + +减少关注圈,扩大影响圈 + +我关注很多事情,比如编程语言、明星八卦、国家大事,这些都是“关注圈”。而这其中,要区分哪些事,是我可以影响和掌控的,这些事则是“影响圈”。 + +不要总盯着自己无法改变的部分,你需要要多花时间精力在影响圈上。 + +比如说,我不能改变 996,至少我可以利用这时间多学习一点,找机会换一个更好的环境;我不能要求每个人都写单元测试,但是我自己的代码写了单元测试,这样项目质量更好了我也更有价值;我不能决定跟什么样的人一起共事,但是我愿意跟他们分享我的经验,他们成长了我也受益。 + +工作一段时间后,你也可以尝试去扩大自己的影响圈。 + +比如说,很多程序员像我一样,有过不少因为产品经理需求没想清楚导致返工的经历,后来我就格外关注产品设计相关的知识,业余时间自己学习了不少,这就相当于扩大了我的影响圈。 + +所以后来产品经理给我一个需求,我不需要在开发完成后才抱怨他不靠谱,而是在给我需求的时候就去跟他讨论,是不是有可能没想清楚。 + +当你不仅仅局限于程序员的角色思维,扩大了影响圈之后,你就可以试着向产品经理提出很多有价值的建议,比如: + + +这个布局在文字很长的情况下会有什么变化? + +如果网络很慢,加载数据的时候应该显示什么?加载失败显示什么? + +如果数据为空的时候这个列表应该显示什么? + + +其实“减少关注圈,扩大影响圈”这个道理也很简单:接受不能改变的,改变能改变的,尽量扩大可改变项的范围。 + +以终为始,想清楚再开工 + +如果对比一下我在十几年前作为一个新手程序员,和现在作为一个老手写代码有什么不同的话,我认为在新手阶段,我是拿到需求就开始写,写到一半发现好像不对,马上修改,好像还不太对,就这样反反复复,效率很低下。 + +而现在拿到一个需求,我会先仔细的分析需求文档,反复和产品经理确认各种细节,然后做个简单的设计,考虑清楚模块怎么设计,他们之间是什么关系,然后再写,写完还要加上测试代码。 + +你知道最大的区别是什么吗?我现在做一件事之前,会先想清楚“终”,然后才知道怎么“始”。所以我先搞清楚需求这个“终”,然后再设计规划出这个从“始”通向“终”的路线,最后从“始”出发写代码,一气呵成,不仅快,而且质量好。这就是“以终为始”。 + +要做到“以终为始”,就是在做事情的时候注意三点:目标、原则和计划。 + +经常停下来想想目标 + +我刚毕业参加工作的时候,要开发一个内容管理系统,其中涉及有数据库访问,这就需要把数据表的字段和类对应起来,觉得太体力活了,于是我开始写数据库生成代码工具。而要想写代码生成工具,我还得学习 Winform 知识……就这样几个月过去了,关于这个系统的代码还是最开始的几行! + +我的目标是写一个内容管理系统,结果却跑去写代码生成工具,这样怎么能做到高效呢?正确的做法应该是手动完成这几个类的生成,其实用不了几分钟,或者用一个现成的工具。如果觉得代码生成工具是个有意义的项目,应该另外立项,而不应该影响当前的项目。 + +这样的事情在我身上还发生过几次,所以我后来就逼着自己隔一段时间要停下来想想:我的原始目标是什么?我正在做的事是我的目标吗?如果不是,那么马上回到自己的原始目标去。 + +制定原则 + +其实大部分很好的编程方法都是需要坚持做才有效果的,比如说自动化测试代码,有时候时间进度一紧,就会来不及写,时间一长,就会欠下技术债务。 + +所以我给自己定了一个原则:增加一个功能,就要写自动化测试,如果来不及写,就给自己写一条 Ticket。 + +这条原则我坚持得很好,所以我的自动化测试代码得以坚持,从而真正帮助我做到高效开发。 + +你也可以给自己定一些原则,比如: + + +“先运行再优化 (Make it Work Make It Right Make It Fast)”——也就是在优化代码之前,先用简单的方法实现,再考虑怎么优化,这样可以保证设计的简单,也可以避免你陷入技术细节中而忽视了原始目标。 + +“不复制粘贴代码 (Don’t repeat yourself)”——复制粘贴会导致代码臃肿,不便于维护,提取抽象可以保持简洁。 + +“每个 Pull Request 要尽可能小”——这有助于把复杂的任务分解成几个简单的任务,简单的任务更容易高效完成。 + + +有原则了,你才能不忘初心,有始有终。 + +公开自己的计划 + +那么有了原则就够了吗?显然不是,有了原则,你还要坚定不移地去执行。如何执行呢?做计划。 + +刚开始工作时,我是害怕做计划的,怕计划了完不成,问到我工作的时间安排时,我会给一个模凌两可的答复,这其实导致了我在实际开发时,缺少进度压力,从而迷失在细节中导致延误进度。 + +后来我尝试做出了一些改变,把任务细化,做个简单计划,主动给出一个明确的时间点。有了计划指引和时间点的压力,会倒逼着自己时刻专注于目标是什么,“终”在哪里,还有多少没有完成,这样下来工作效率自然而然就会高起来。 + +通过在做事时,围绕着目标、原则和计划这三个点,反复地刻意地练习,也可以让你慢慢养成“以终为始”的好习惯。 + +要事第一,把时间用在刀刃上 + +作为程序员,其实大部分时间并不能专注写程序,总有各种各样的事情打断我们,比如,一会产品经理找你过去开个会确认个需求;一会测试过来找你重现一个 Bug;一会还有同事过来请教你一个问题;微信上老朋友找你叙叙旧;突然生产环境出故障了,需要马上去解决。 + +就这样一天下去,感觉一直在忙忙碌碌,其实并没有多少时间在写程序。这时候怎么办呢,对手头的事情进行优先级管理。 + +时间四象限也许你不陌生,就是把事情分成重要紧急、重要不紧急、紧急不重要、不紧急不重要四个象限,不同的事情有不同的应对策略。 + + +重要紧急的事情马上处理 + + +比如说,生产环境出故障了,测试环境部署失败了,这些都是重要并且紧急的事情,只能是马上处理。 + + +重要不紧急的要事,要花最多的时间在上面 + + +对代码重构、写自动化测试代码、确认清楚需求文档,这些事情都属于重要不紧急的事情,但是如果不及时处理,就有可能变成重要紧急的事情,比如不偿还技术债务,就可能会变成生产环境故障。 + +所以这部分事情我会多花时间,重点做。通常我会每段时间只专注做一两件重要的事,其他事情尽可能忽略,比如前一个阶段我主要的工作就是重构前端代码,这个阶段我就在忙排查性能隐患,至于其他事情,就先放一放。 + + +紧急不重要的事凑一起集中做 + + +像微信的消息通知,无关紧要的会议,请教一个不算很急的技术问题,这些都是紧急不重要的问题,然而却会占用我们大量时间。如果时间都用在这些事情上面,必然会占用在重要事情上所需的时间。 + +所以我有些小技巧也许你可以参考。比如我在专注干活时,会全屏幕、关掉所有消息通知,保证没有消息干扰,忙完一段后再去集中处理。 + +还有如果有人找我时我正在忙,如果他的事情不是重要紧急的,我会礼貌地告诉他我正好手头有点事情,大约多少时间后我主动去找他。相应的我也会尊重别人的时间,找别人的时候会先问一下:“你什么时候有 10 分钟左右时间,我想请教你一个问题?” + + +不重要不紧急的事情能不做就不做 + + +不紧急不重要的事也很多,比如说我的 Mac 电脑突然提示我要更新系统了。我有点强迫症,以前系统一有要升级,我就迫不及待要升级到最新,结果一升级系统,半天就不能干活了。所以后来这种事情,就放在不重要的时间去做,比如周末、睡觉之前让它慢慢升级。 + +其实我在做开发的时候,觉得很多很杂的事情也不算太多,真正到后来转型做管理的时候,才真正体会到什么叫事情多而杂。但正是源于开发时期就形成的时间四象限方法的运用,让我可以在繁忙的时候,保证时间用在有价值的事情上。 + +要事第一,就是要保证你有限的时间用在最有价值的事情上。 + +总结 + +积极主动、 以终为始和要事第一,这三个原则以及其衍生出来的方法,正是帮助我逐步变成一个高效程序员的关键所在,希望也能对你有所帮助。 + +如果你已经学习了很多类似的原则或者方法,而觉得没什么效果,那也许只是因为没有尝试把它们变成习惯。你可以像我一样,把认同的好的原则或方法,通过反复的刻意练习,反复地提醒自己,训练成习惯,然后用习惯指导你的日常开发。 + +当然,这样的改变不会是一天两天就能完成,但也不用着急,因为习惯的养成需要时间的积累,才能变成条件反射。当你把好的原则或方法变成了直觉反应,自然就会成为一个高效的程序员。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/26持续交付:如何做到随时发布新版本到生产环境?.md b/专栏/软件工程之美/26持续交付:如何做到随时发布新版本到生产环境?.md new file mode 100644 index 0000000..1ae720b --- /dev/null +++ b/专栏/软件工程之美/26持续交付:如何做到随时发布新版本到生产环境?.md @@ -0,0 +1,213 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 26 持续交付:如何做到随时发布新版本到生产环境? + 你好,我是宝玉。到今天为止,持续交付已经成为一种公认的好的开发实践,越来越多的开发团队都已经应用持续交付,它通过自动化的方式,让你的代码在每一次提交后,都能自动化地走完编译、测试流程,完成后即可随时准备好部署发布。 + +持续交付如果细分,其实可以分成持续集成、持续交付和持续部署三个概念,这三个概念很相近,但又有所不同。 + +今天我将带你了解什么是持续集成、持续交付和持续部署?以及我们该如何用好它们,在项目中最大程度上发挥其效用。 + +集成、部署和交付的发展史 + +要想更好地理解并应用好持续集成、持续交付和持续部署,你需要了解它们的演变史。持续集成、持续交付和持续部署的历史还不算太长,但是集成、部署和交付却是伴随着软件工程一起发展的。 + +集成的发展演变 + +在多人软件项目开发的时候,每个人都会负责一部分功能模块的开发,集成指的就是每个人把自己开发的分支代码,合并到主干上,以便测试打包。 + +集成的原始阶段 + +早在瀑布开发的年代,在开发阶段,一般是不集成的。大家各自开发,等到开发阶段差不多快结束了,再一起提交代码到源代码管理工具,让代码集成在一起,再编译、部署发布到测试环境。 + +由于长时间都是在各自的开发环境运行,每次集成都是很痛苦的过程,会遇到各种问题,比如说编译无法通过、hard code 了开发环境地址、类库版本不一致、API 格式不一致等,通常要持续几天甚至几周才能逐步有一个相对稳定的版本。 + + + +图片来源:Understanding the Difference Between CI and CD + +《重构》的作者 Martin Fowler 说过:“如果一件事很痛苦,那么就更频繁的做(if it hurts, do it more often. )”,持续集成本质上也是把集成这件让人痛苦的事情,更加频繁地去做。 + + + +图片来源:Understanding the Difference Between CI and CD + +瀑布模型开发的集成,或者说传统的集成,都是在开发阶段整体完成的差不多了,才开始集成。而持续集成的做法,则是每次有代码合并入主干之前,都进行集成,持续的集成。代码集成到主干之前,必须通过自动化测试,只要有一个测试用例失败,就不能集成。 + +持续集成的好处很明显: + + +配合自动化测试,这样可以保证主干的代码是稳定的; + +频繁集成可以让开发人员总能从主干及时获得最新的代码,不至于像类库、API 不一致等问题到最后测试的阶段才暴露出来。 + + +持续集成主要的问题就是搭建整个持续集成环境,要稍微麻烦一点,另外需要配合一些流程规范来辅助执行,比如要求有一定自动化测试代码的覆盖率,要求测试通过才能合并到主干。 + +部署和交付的发展史 + +部署指的是将代码发布到各种环境,比如部署测试环境以供测试。交付则指的是软件产品在测试验收通过后,具备发布到生产环境交付给客户使用的条件。 + + +部署和交付的原始阶段 + + +在早些年,部署是一件很麻烦的事情。需要手动获取最新源代码、编译、再需要针对环境修改很多配置。 + +这事我确实深有体会,当年在飞信时就这样,几十个服务,一个服务有十几个项目,光挨个编译一遍就要好久,然后每个服务还有自己的配置。所以当年专门有一个人,就负责每天部署各种服务到测试环境。 + +生产环境就更麻烦了,因为出错了会导致服务中断。最初部署生产环境是开发人员自己做的,根据自己的经验把程序部署,然后手动修改很多配置,以保证正常运行。但这样经常会遗漏一些配置,导致程序无法正常运行,出问题后程序员很可能会直接在线上环境修复 Bug,导致更大问题。 + +随着分工的进一步细化,逐步发展成有专门的运维岗位,由运维人员负责部署。而开发人员上线前要写专门的部署文档和检查表,运维人员按照部署文档和检查表一步步部署生产环境。 + +这样确实有效减少了配置错误等问题,但整个部署过程还是很繁琐,尤其是服务器一多,耗时很长,仍然可能会因为人工操作错误导致失败。 + +所以为了避免部署出问题,会尽量避免进行生产环境部署,几周甚至几个月才会部署一次。 + + +从手动部署到脚本自动化部署 + + +对于程序员来说,如果一件事能自动化解决,迟早会有人找出自动化的解决方案,部署也由原来的手动部署发展成为自动化部署。 + +早期的自动化部署解决方案是每日构建(Daily Build),简单来说,就是大家在每天晚上下班后,每日构建程序自动从源代码管理器下载最新代码,编译、部署程序到测试环境。这样第二天测试人员就可以拿到最新的程序,对前一天修复的 Bug 进行测试。 + +每日构建是个很大的进步,因为初步实现了自动化对代码进行编译、部署测试环境。但也有一些不完善的地方,比如说如果有开发人员提交的代码有问题,可能会导致当天的编译或部署失败,那么第二天开发人员上班后,还需要手动解决。 + +你会发现,自动化逐步应用到运维领域,确实是让部署过程更容易,但也只是让部署过程更容易,还是无法解决发布版本的质量问题,还是可能会因为配置错误导致失败,测试环境正常的功能到生产环境就不工作了。 + + +从脚本部署到持续交付 + + +其实在理解了持续集成后,再理解持续交付就要容易多了。持续交付,就是在持续集成的基础上,再进一步,在功能合并到主干后,不仅会进行自动化测试,还会打包,并部署到测试环境中。 + +理论上来说也可以直接部署到生产环境,但是这个环节需要人工确认。参考下图,红色部分表示需要手动确认。 + + + +图片来源:Understanding the Difference Between CI and CD + +持续交付本质上也是把部署和交付这件让人痛苦的事情,更加频繁地去做,从而让部署和发布变得不但不痛苦,反而越来越简单。 + +把持续交付的工作做好后,部署生产环境会变得非常简单,只需要点一下按钮或者运行一个命令,就可以很快完成,不需要人为地去修改配置等手动操作,也将因为配置错误或者环境不一致导致的问题的可能性降到了最低。 + + +从持续交付到持续部署 + + +持续交付,对于生产环境的部署,依然需要有手动确认的环节。而持续部署,和持续交付唯一的不同,就是手动确认的环节都没有了,每次代码从分支合并到主干,在自动化测试通过后,会直接自动部署生产环境,不需要人工确认。 + +但是,持续部署要想做好,还是很有挑战的一件事,毕竟从代码合并到生产环境的部署,如果没有人工干预仅仅依赖于自动化测试,这对自动化测试的覆盖率和稳定性要求非常高。尤其在开发新功能时,还需要引入新的自动化测试代码,可能会导致测试不全面。 + +当然对于新功能可能导致的不稳定问题也有解决策略,就是把新功能用功能开关(Feature flag)隐藏起来,设置特定的 Cookie 或者 Header 才打开,到生产环境后人工再测试一遍,通过后再打开,如果没通过,就继续修复继续持续部署。 + +该不该应用持续交付? + +经常会有人问我类似的问题:我们是瀑布模型开发,该不该应用持续交付? + +我的答案是:持续交付和用什么开发模型是没有关系的,瀑布模型也可以应用持续集成,应该尽快将持续集成的环境和相应的开发流程搭建起来,可以马上看到好处。 + + +尽快暴露问题:Martin Fowler 说过,“持续交付并不能消除 Bug,而是让它们非常容易发现和改正。”自动化测试,可以保证很多问题在合并到分支之前就能被发现;每次合并后就部署到测试环境,也能让测试人员尽早介入,及时发现问题。 + +极大提升效率:持续交付让开发过程中从代码合并,一直到最终部署,都实现了自动化,能极大程度上提高效率。 + +提升质量:每次合并之前都需要通过自动化测试,因此错误会少很多。 + +降低项目成本:在最初搭建持续交付环境的时候,是要投入一定成本的,但是从长远看,开发效率提升了,代码质量提高了,反而是对降低项目的整体成本有帮助的。 + + +虽然现在持续交付还不够普及,但未来就像源代码管理一样,成为开发团队的标配。现在大厂都已经普及了持续交付,还会有专门的团队负责持续交付工具的开发和维护。对于中小厂,一般不需要自己开发持续交付工具,可以基于开源工具搭建,或者购买托管的持续交付工具,一样可以很好满足持续交付的需求。 + +如果你所在团队还没有开始用起来持续交付,那么不如现在开始应用起来,能有效提升团队的开发效率和代码质量。当然很多团队没有推行,主要问题还是不知道如何搭建一套持续交付的环境。接下来,我就给你介绍一下如何搭建自己的持续交付环境。 + +如何搭建持续交付环境? + +要搭建好自己的持续交付环境,其实并不算太难,已经有很多持续集成工具和教程帮助我们做这件事。 + +准备工作 + +根据前面对持续交付的说明,要想搭建自己的持续交付环境,并不是简单找一个持续集成工具一搭就可以工作了,而是还需要做一些准备工作。 + +我们先来看持续集成部分,持续集成相对要求简单: + + +需要有源代码管理工具,比如说 git、svn,因为持续集成工具需要从统一的源代码仓库获取代码; +需要写自动化测试代码,因为持续集成有一个很重要的条件,就是自动测试必须通过。 + + +第一个条件其实好满足的,现在源代码管理工具已经是标配,无论是免费的还是收费的,都有很多选择。第二个条件其实也不是太大的问题,因为自动化测试覆盖率,可以逐步提升,不要求一步到位。所以可以先把自动化测试写起来,然后在开发过程中逐步增加覆盖率。 + +持续交付相对比持续集成要求更高,因为整个过程需要高度的自动化。要实现持续交付,你的项目需要满足以下条件: + + +对代码构建的过程可以反复进行,并且每次构建的结果是一致的、稳定的; +所有环境的配置都存在于源代码管理工具中,不仅仅是代码; +需要自动创建针对于不同环境的发布包; +所有环境的部署发布步骤都必须是自动化的。 + + +上面这些要求,最难的部分其实就是自动化打包和自动化部署到各种环境,因为每套程序都不一样,每个服务器环境也不一样,这是必须要各个团队针对自己的项目情况去解决的问题。 + +选择合适的持续集成工具 + +持续集成工具现在已经有很多选择,有开源的、商业的,有线上托管的,还有自己搭建的。 + +主要的持续集成工具有这些: + + +Jenkins + + +Jenkins应该是目前最好的开源持续集成工具,可以自己搭建,插件非常丰富,可以满足绝大部分项目的需要。相对使用难度要高一些,需要花一点时间学习。 + + +Go CD + + +Go CD是 ThoughtWorks 公司出品的持续集成工具,可以免费使用。 + + +Travis CI + + +Travis CI是一个老牌的托管的商业 CI 系统,和 Github 集成的非常好,尤其是开源项目,可以免费使用。 + + +GitLab CI + + +GitLab CI是 Gitlab 推出的持续集成工具,可以自己搭建也可以使用它的在线托管,价钱便宜。 + + +Azure Pipelines + + +Azure Pipelines是微软的持续集成平台,可以自己搭建也可以使用它的在线托管,和微软的开发语言和服务集成很好。 + +根据选择的工具实施 + +在选好你要用的持续集成工具后,就需要根据工具的说明去搭建。这部分相对简单,网上也有比较多的教程,限于篇幅,这里我就不一一介绍啦,相信你通过它们的官方网站或者是搜索,很容易能找到很多相关的使用教程。 + +总结 + +今天我带你一起学习了与持续交付相关的一些概念: + + +持续集成,就是持续频繁地将代码从分支集成到主干,并且要保证在合并到主干之前,必须要通过所有的自动化测试。 + +持续交付,则是基于持续集成,在自动化测试完成后,同时构建生成各个环境的发布包,部署到测试环境,但生产环境的部署需要手动确认。 + +持续部署,是在持续交付的基础上,对生产环境的部署也采用自动化。 + + +要搭建持续交付环境,首先需要做好准备工作,例如自动化测试代码和自动部署脚本;然后要选择好持续集成工具;最后按照选择的持续集成工具来实施。 + +最后,推荐你配合阅读持续交付 : 发布可靠软件的系统方法,这本书很系统地讲述了持续交付的概念和如何去实施的过程。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/27软件工程师的核心竞争力是什么?(上).md b/专栏/软件工程之美/27软件工程师的核心竞争力是什么?(上).md new file mode 100644 index 0000000..77cd9b7 --- /dev/null +++ b/专栏/软件工程之美/27软件工程师的核心竞争力是什么?(上).md @@ -0,0 +1,171 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 27 软件工程师的核心竞争力是什么?(上) + 你好,我是宝玉。软件工程师,是软件项目重要的人员组成,从设计到编码实现以及上线后的运行维护,都需要软件工程师的参与。所以大家都很关心的一个问题是:软件工程师的核心竞争力是什么? + +软件工程师的核心竞争力 + +其实我在微博上问过“软件工程师的核心竞争力是什么?”这个问题,得到很多答案:技术、代码、天赋和激情、经验、想象力…… + +我思考过这些答案,觉得很多答案挺好,但是不够突出核心竞争力,或者没有突出软件工程师的特点。 + +比如说技术和代码的能力,属于基础能力,而不属于核心竞争力;天赋和激情其实放哪个职业都可以这么说,何况天赋也不是决定性因素,聪明人不是好程序员的例子也很多; + +经验是很重要,但是不能说明太多问题,有些号称有三年开发经验的程序员不过是把一年的经验重复了三次罢了;想象力是很重要,但我觉得更适合科学家或者艺术家,因为他们要通过想象力在未知的领域去探索,而工程师更多是利用已有的知识去打造产品,想象力算不上核心竞争力。 + +我觉得软件工程师的核心竞争力,不是单一能力的体现,而是几种能力和价值的合集。学习能力、解决问题能力和影响力构成了软件工程师的核心竞争力。 + +学习能力 + +对于软件工程师来说,最基本的要求是技术和代码。如果你看招聘网站上招聘软件工程师的要求,都需要能掌握一门或者多门编程语言,会熟练使用工具、框架。 + +但熟练掌握一门编程语言、框架,还不能构成核心竞争力。因为现在技术更新迭代很快,现在你熟悉的语言或者框架,可能过几年就没有太大的市场了,需要学习新的技术。 + +这点做前端的同学一定深有体会,比如说前几年 jQuery 很火,你好不容易精通了,过两年又要改用 Angularjs,而现在则是 React、Vue 的天下了。而且光会这些框架还不够,还要学习使用配套的打包工具,而这些工具也一直在推陈出新,每次更新升级,都需要重新学习。 + +就算是编程语言,时间周期能稍微长一点,但也没有谁能保证一门语言就可以一直很火。我早些年对 Asp.Net 的掌握程度已经到了技术专家级别,后来公司要转型做移动应用,就必须要去学习 Objective-C,否则就得换个公司才行。 + +出国留学后要重新找工作,这时候 Asp.Net 和 Objective-C 的工作机会很少,但是前端工作机会多,我就需要去学习前端,才能有更多的工作机会。 + +所以我觉得,编程语言、框架、工具,这些都不是软件工程师的核心竞争力,能快速学习掌握编程语言、框架、工具的学习能力才是软件工程师最基础的核心竞争力。 + +解决问题的能力 + +为什么我说学习能力只能算是基础的核心竞争力呢?因为技术学了是要用才能产生价值的。学的好不代表用的好,所以你看很多 Leetcode 上刷题分很高的同学,让他去实现一个需求,代码可能还是会写的一团糟。 + +那么软件工程师怎么运用学到的技术呢?我们可以先看看软件工程师的日常工作,有哪些主要工作以及需要什么样的能力去解决: + + +实现功能需求——需要先分析需求,然后抽象设计,最后实现; +修复 Bug——改 Bug 最大的挑战其实是重现问题,也就是发现问题,然后再分析问题,最后解决问题; +重构代码、优化性能——对代码重构,优化性能,最难的地方其实在于发现代码问题在哪,发现性能的瓶颈,后面再去寻找解决方案,最后再解决。 + + +也就是说,软件工程师这些日常开发工作的核心还是在发现问题、分析问题和解决问题,在这里我统称为解决问题的能力。这几个能力看起来没什么稀奇,但是要仔细分析,其实软件工程师的水平高低,恰恰就体现在解决问题的能力上面。 + +发现问题 + +若干年前我们要做一个抽奖系统,负责这个任务的程序员设计了技术方案让我审查,他的方案从功能方面是没问题的,但在安全方面却欠考虑。 + +我就问他如果有人用个工具模拟用户操作反复点,岂不是可以不停参加抽奖,极大提高中奖概率?于是他说那可以在前端增加限制重复点击。我说如果通过抓包工具反复直接发送数据包,不还是一样可以绕过你前端的检查吗? + +同样一个抽奖系统需求,新手程序员看到的是如何实现功能,而有经验的程序员,会发现可能存在的安全隐患,未雨绸缪,把可能的安全问题消除掉。这就是发现问题的能力体现。 + + +分析问题 + + +软件工程师经常遇到的一个问题就是生产环境发生故障,新手程序员通过分析也能把问题修复,但修复完了就觉得没事了。而有经验的程序员,会在恢复后还会进一步分析故障产生的深层次原因,以及以后可以怎么预防类似的故障再次发生。 + +分析问题,不仅是分析表面的问题,还需要去分析问题深层次的原因,以及思考预防同类问题的机制。 + + +解决问题 + + +在发现问题和分析问题后,解决问题相对会容易一些。但有时候明明知道问题在哪,但不知道怎么解决也是很苦恼的事情。 + +在工作中,就经常有新手程序员跑来找我请教问题,比如说遇到一个 API 不会用。这时候我通常不会直接告诉他答案,因为我可能也不知道某个 API 的用法,就算我知道,告诉他这一次了,他下一次还是不会自己去解决。 + +所以我会反问他: + + +“查过官方文档吗?”——基本上大部分 API 在官方文档都有说明的; + +“有没有去搜索过?”——很多问题已经有人遇到过,而且还把解决方案都贴出来了,比如 StackOverflow 就是个很好的地方; + +“用的什么关键字?”——很多时候没找到结果就是因为关键字没选好,比如用中文关键字很难搜索出匹配的技术帖子,改成英文使用谷歌就可以找到更多更合适的结果; + + +还有的问题确实不是通过搜索能解决的,需要去寻求其他组或者外部专业人士的帮助。就算是问问题这种小事,一样都能体现程序员的水平。 + +比如我就遇到过很多次有程序员去请教别人问题,但是都没人回复的,并不是没有人愿意帮忙,而是他基本的问题都没有描述清楚,从他的问题很难知道表达的意思和要解决的问题。这时候我通常会建议他先去看看《提问的智慧(How To Ask Questions The Smart Way)》这篇文章。 + +这样的发现问题、分析问题和解决问题的能力,就是软件工程师的进阶核心竞争力。 + +影响力 + +解决问题的能力,确实是软件工程师很重要的一种能力体现,但是还不能完全体现出软件工程师的核心竞争力。 + +为什么大部分程序员的技术水平和解决问题能力差不多,而有的升职加薪的机会更多?有的不需要去找工作,总有工作来找他们?这其中的差别,就在于他们的影响力不同。 + +有些程序员,做事情认真靠谱,做出来的结果让人放心,这样久而久之,就在公司形成了口碑和影响力,大家愿意与之共事。 + +有些程序员,乐于帮助其他人,分享自己的经验,跟一些新手程序员是亦师亦友的关系,在团队里有很高的威望。 + +有些程序员,有独特的项目、公司或者行业经历,比如在阿里巴巴这样的大厂有多年架构师经验,在业界有一定知名度。 + +有些程序员,写自己的技术博客,出去做技术讲座,成了技术大 v,在技术圈子里面有一定的名气和影响力。 + +这样的影响力不是一朝一夕能形成的,但却是一个软件工程师最核心的价值体现。 + +你需要通过一点点技术成长的积累,需要通过一个个成功项目的积累,需要通过一篇篇技术文章分享的积累,需要通过一次次帮助其他人成长的积累。而一旦形成足够的影响力,就会变成软件工程师职场发展最牢固的护城河。 + +所以综合来说,软件工程师最核心的竞争力其实分三层: + + +最底层、最基础的就是学习能力,通过学习能力,快速学习掌握新技术; + +中间一层就是解决问题的能力,充分利用学到的技术,去发现问题、分析问题和解决问题; + +最上一层就是影响力,是核心竞争力的综合体现。 + + +学习能力、解决问题能力和影响力一起构成了软件工程师的核心竞争力,就像下面这个金字塔图一样,我称之为软件工程师竞争力金字塔。 + + + +但必须要注意,这三层缺一不可,你不能光去追求影响力而不踏踏实实学习和做事,也不能光去做事而不学习。必须要踏实地、一层层地打好基础,这样才能让你的竞争力是持久的,而不是昙花一现。 + +软件工程师竞争力金字塔的意义 + +如果你是软件工程师,了解了软件工程师竞争力金字塔,你就可以自下而上,更有针对性地培养、提高软件工程师的核心竞争力。 + +不是把自己绑死在一门技术或者一个框架上,而是训练自己的学习能力,让自己可以拥有快速学习掌握新技术的能力。 + +日常工作不仅仅是实现一个个的功能,做一个个的项目,而是在这个过程中,去锻炼和提升你发现问题、分析问题和解决问题的能力。这样才能使你的工作经验最大化,而不是机械重复没有积累。 + +在工作中,不仅是把事情做成,还要把事情做好;不仅是自己成长,还要帮助其他人成长;最大化的利用好所在平台和行业的经历,转变成你的经验和影响力。工作之外,也多分享,打造自己的品牌。 + +这样你就可以一点点搭建出来属于你的竞争力金字塔,构建出自己的核心竞争力。 + +如果你是要招聘优秀的软件工程师,了解了软件工程师竞争力金字塔,你可以自上而下,更容易筛选出来优秀的软件工程师。 + +首先你可以通过内部推荐,优先找那些口碑好,做事情认真靠谱的软件工程师。然后你可以考察他解决问题的能力。比如说你可以问他: + + +“项目中遇到过的哪些复杂的问题?” + +“是怎么样去解决的?” + + +通过对问题的答复,再深入问一些细节问题,例如:“你当时是怎么考虑安全性问题的?”“如果访问量增加一倍,会有什么影响?”等等。你就可以对他解决问题的能力有个初步了解了。 + +最后你还可以考察一下学习能力。比如你可以问他: + + +“最喜欢的编程语言是什么?” + +“你是如何学习这门语言的?” + +“如果让你学一门新的语言,你打算怎么学?” + + +这样你也可以对他的学习能力有一个大致了解。 + +总结 + +由于篇幅限制,我将文章分成了上下两篇。上篇主要分析了软件工程师的核心竞争力,也就是学习能力、解决问题能力和影响力。我会在下篇中,为你讲具体怎么做,才能有效提升软件工程师的核心竞争力。 + +学习能力,就是你学习并掌握一门技术、框架和工具的能力。好的学习能力,能帮助软件工程师在技术日新月异的今天,快速跟上技术发展的步伐。 + +解决问题的能力,就是发现问题、分析问题和解决问题的能力。解决问题的能力,是软件工程师非常重要的能力,帮助软件工程师在日常工作中高效完成工作,创造价值。影响力则是软件工程师价值的综合体现。 + +如果你是软件工程师,那么就需要自下而上,训练自己的学习能力,积累解决问题的经验,工作的过程中形成影响力,一点点去构建自己的核心竞争力。如果你要招聘或筛选优秀的软件工程师,则需要自上而下,选择那些口碑好有影响力的,能帮你解决问题的,有很好学习能力的人。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/28软件工程师的核心竞争力是什么?(下).md b/专栏/软件工程之美/28软件工程师的核心竞争力是什么?(下).md new file mode 100644 index 0000000..65b24ce --- /dev/null +++ b/专栏/软件工程之美/28软件工程师的核心竞争力是什么?(下).md @@ -0,0 +1,159 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 28 软件工程师的核心竞争力是什么?(下) + 你好,我是宝玉。在上一篇中,我们讨论了什么是软件工程师的核心竞争力,也就是学习能力、解决问题的能力和影响力。 + +今天我就来跟你谈一谈,如何提升软件工程师的核心竞争力,也就是说,如何分别提升你的学习能力、解决问题能力和影响力。 + +如何提升学习能力? + +学习能力是软件工程师最基础的能力,学习能力直接决定了你掌握技术的速度。现实中,有的程序员掌握新技术就是比别人要快,似乎有什么秘籍,可以让他们快速学习和掌握新技术。真有这样的秘籍吗?如果有,那是什么样的内容呢? + +前不久我要写一个家谱的微信小程序,要实现列表、检索等功能。此前我没有任何关于小程序的开发经验,于是我粗略地看了下开发文档,搭建了开发环境,下载了几个示例程序,前后花了一天时间完成了一个不算简单的小程序开发。 + +为什么我能在一天时间就可以学习掌握小程序的开发呢?其实只是因为我已经构建了自己的开发知识体系,而小程序开发所需要的知识,绝大部分在我的知识体系里面已经有了存储。这些部分我就不需要重新再学习,我只要去学习小程序所独有的知识就好了,而这部分知识只有很小比例,所以很快就可以掌握。 + +那么你可能会问,怎样可以构建自己的知识体系呢? + + +首先需要在一个技术领域深耕 + + +每个人精力其实很有限的,一开始专注在一个技术领域容易在短时间取得成绩,同时也相当于建立起了最初的知识体系,在未来的知识森林里种下的第一棵大树,这样当你开始学习新的技术的时候,已有的知识就可以直接借用,相当于这棵大树可以帮助新的知识树的成长提供很好的养分,快速培养出新的大树。 + +如果一开始就同时涉猎多个领域,每个领域的知识又没有掌握好,这样的知识是没法共享的,就相当于你种的只是一片知识的灌木,最终只能收获像灌木丛的知识体系。 + +只有一个领域的知识你真正吃透,才能有效地共享到其他领域,构成一个知识领域的森林。 + +我在毕业之后,整整有 6 年时间,是一直专注于 Asp.Net 技术领域,让我能成为这个领域的专家,也帮我构建了最基础的知识体系。所以在后来我去学 iOS 开发时,发现像面向对象、网络协议、数据存储、MVC 开发模式这些知识,我就不需要再去学习,因为我的知识体系已经有了这部分知识。 + +换个角度说,如果我当时 Asp.Net 学的不够深入,那么面向对象、数据存储、MVC 开发模式这些我还是要再学一遍,自然也无法快速掌握 iOS 开发。 + +知识体系建立之初,确实是痛苦的,感觉什么都不懂,有很多要学习的。市面上有新技术出来也会觉得焦虑,觉得应该去学新的技术。但如果初期不能够专注在某个领域深入的话,你学了再多技术,结果也没有一门能深入,这就很难构建出有深度的知识树。 + +要在某一个领域的技术达到一定深度,通常需要三年以上的时间。当你熬过这个阶段,在一个技术领域取得了一定成就,不仅会收获你的知识树,还能收获技术上的自信,让你有信心在其他技术领域也同样取得成就。 + + +然后往相近的领域逐步横向拓展 + + +当在一个技术领域达到一定深度后,可以开始横向扩展。最好是往相近的领域扩展,因为这样你之前的知识有很多是可以共享的,容易快速取得成绩。 + +这也是为什么我后来选择了前端,因为对我来说,前端跟我以前掌握的 Asp.Net 的经验是有很多重叠的,我只需要去学习框架和工具那部分知识。 + +当然横向构建知识体系,也一样不是一个轻松的过程,因为以前你在某个领域取得的成就和经验,反过来也会成为一种阻力。因为以前你熟悉的知识,已经变成了你的舒适区,你会天然地不愿意走出舒适区,不愿意到挑战区或恐慌区去学习新的知识。 + +这其实就是我当初学 React 时候的感受,我觉得 jQuery 已经用的很好了,为什么要学这个?我老想着用以前 MVC 的经验去套用 React 的编程模式,反而更难理解。 + +但是,当你迈过去,掌握了新领域的知识,就会感觉整个知识体系一下子扩展了很大一块。相当于让你的知识体系,从一棵树,逐步变成了一个小树林,最终会成为一个森林。 + +快速掌握新技术的秘籍,就是要构建属于你的知识体系,让你在学习新知识时,能借用已有的知识,加快学习速度。 + +你可以现在思考一下你的知识体系是什么样子的,是一片灌木丛还是已经有大树了?如果只是一片灌木丛,你打算在哪个技术领域先打造你的知识树呢? + +如何提高解决问题的能力? + +解决问题的能力是软件工程师进阶的核心竞争力,你看现实中,那些解决问题能力强的程序员,遇到问题总是有办法,都能有条不紊地给解决了。 + +有一次我们组负责的一个网络服务出现异常,大约 1% 的请求会出现异常导致服务报警,当时值班的同事正好是一个新手程序员,他也试着分析日志,但是没有找到明显的错误日志,完全不知道如何应对,只好让我帮忙。这个问题我以前也从来没遇到过,但我有一套应对这类问题的思维方式和逻辑。 + +我第一步就是回滚上一次的部署,看是否恢复正常,结果没效果。于是我再从日志找出所有有问题的请求,寻找规律,从中选取几个有问题的请求,去跟踪它们整个请求过程,结果在某一个路由环节发现指向到了一台错误的服务器,最终定位是某个路由程序出现的问题。 + +我们将这个问题反馈到相关组,问题马上就得以解决。然后我建议他在这个路由环节增加监控,这样以后再出问题就能第一时间报警通知。 + +为什么我有这样解决问题的能力呢?是因为我在多年的开发经验中,形成了一套解决问题的方法论,即使遇到没有解决过的问题,也能借助一套方法论去解决。 + +这其实很像我在专栏开头就提到的工程思维,遇到一个项目,哪怕不是软件项目,我也可以借鉴瀑布模型,用工程方法去解决。所以你要提高解决问题的能力,就要形成自己的一套解决问题的方法论。 + +在这里分享我解决问题的一套方法论,其实很简单,只有三步。 + +第一步:明确问题 + +解决问题,最重要的一步就是要明确问题是什么,这其实就跟做项目需要先做需求分析一样,搞清楚目标是什么,才能做到有的放矢。 + +同时这一步也要透过现象看本质,去明确问题背后是不是还有其他问题。就像我在前一篇文章中举例的抽奖项目,不能光看到功能需求,还需要看到安全上的需求;网络异常的问题,不能光想着应用程序错误,还要看看网络是不是有问题。 + +第二步:拆分和定位问题 + +前面我们学习架构思维的时候,也提到了,一个复杂的问题,只有经过拆分,才好找到本质的问题。 + +就像上面举的解决网络异常的例子,我首先拆分成程序问题和网络问题,通过回滚观察,我就可以基本上断定不是程序问题,那就说明是网络问题。然后对于网络问题,将整个请求过程拆分,最终就可以定位到有问题的环节。 + +第三步:提出解决方案并总结 + +发现并分析完问题后,找到解决方案是容易的,但很有必要总结一下。总结要做的就是两点: + + +下次有这种问题怎么解决,是不是可以做的更好? + +这种问题是不是可以预防?如果可以,应该怎么做? + + +通过总结,就可以进一步提升解决问题的经验。如果你对于解决技术上的问题还没有总结出来自己的方法论,不妨可以先参考这套方法,一步步去发现问题,分析问题和解决问题。 + +尤其是在解决完问题后,再想一想如何预防以后类似问题的发生。如果每次解决完问题,你还能提出一个预防问题发生的方案,一定会让大家印象深刻的。 + +如何提升影响力? + +刻意地去提升自己的影响力,是很多软件工程师忽略去做的事情。但影响力,却是程序员核心竞争力的最顶层,也是一个软件工程师能力的综合体现。培养影响力也是我职业生涯中受益良多的一件事。 + +其实当你意识到影响力是有价值有意义的事,怎么去做反而没那么难,比如这些方面可以去考虑。 + + +在某个领域做出了足够牛的成绩 + + +有些程序员能在某一个技术领域做到一定深度,做出了常人难以达到的成绩,比如说 PHP 开发组核心成员的鸟哥惠新宸,写 Vue 框架的尤雨溪,前端的 Winter。做到他们这样,基本上就不用担心影响力的问题了。 + +要取得这样的成绩,要实力、要机缘、还要坚持。 + + +做事情超出预期 + + +在软件项目中,你作为一个程序员,每个人都会对你有预期,项目经理希望你如期完成项目,产品经理希望你完成需求,其他程序员希望你代码质量好。如果你是初级程序员,则大家期望你代码不要有太多问题就好,如果你是高级程序员,大家不仅期望你要写好程序,还要能带带新人。 + +如果你做事情的结果能超出预期,就会让人对你刮目相看,进而会形成口碑。就像我前面举的例子,如果你解决完一个问题,还能想到怎么预防问题再次发生的方案,这通常就超出他人对你的预期了。 + +要让自己的表现超出其他人的预期,除了要付出很多努力外,也要学会管理好其他人的预期,我以前有写过一篇文章程序员也可以懂一点期望值管理,谈如何去了解别人对你的期望,降低别人对你的期望,最后做出高于期望的事。 + + +帮助其他人就是在帮助自己 + + +我有遇到过很多程序员不愿意教别人,认为教会徒弟饿死师傅。其实这完全搞反了,帮助别人、教别人收获最大的恰恰是自己。 + +程序员的经验,很大部分来自于解决问题时积累的经验。你自己在工作中遇到的问题其实是很有限的,但如果帮助其他人解决问题,相当于增加了你解决问题的样本,这些样本能帮助放大你的工作经验。 + +帮助其他人,还是形成影响力最简单有效的途径。就像我微软最有价值的称号,就是因为我当年在社区热心组织活动,网上帮助其他人解决技术问题而获得的。 + + +分享就是学习和打造影响力 + + +刘未鹏老师写过的一篇博客为什么你应该(从现在开始就)写博客影响了很多人,这是一篇值得反复阅读的好文章,讲了写博客的好处,例如交朋友、帮助思考、学习。 + +我的观点也类似,包括在“学习路径”中,我就建议大家可以通过分享,在教中学。其实,在分享形式上也可以更多样化,除了写博客还有很多其他方式,比如公司内部的讲座就是很好的分享途径。 + +我在内部也会经常做培训分享,也会写博客分享技术经验,每一次分享都会帮助我学习巩固很多知识点,也是在帮我打造自己的影响力。 + +写东西这方面我做的不够好,写博客断断续续的,但还是在坚持,也收获很多,包括《软件工程之美》专栏的诞生,其实也是因为极客时间看到我写过的一篇《记在美国的一次校园招聘》,所以才邀请我开的专栏。 + +希望你也能思考一下影响力这个问题,想想你还可以在哪些方面,用哪些方法去打造你的影响力,和学习能力、解决问题能力一起,形成你的核心竞争力。 + +总结 + +最后简单总结一下。软件工程师的核心竞争力,体现在学习能力、解决问题能力和影响力三个方面。 + +要提升学习能力,要构建好自己的知识体系,首先需要在一个技术领域深耕然后往相近的领域逐步横向拓展。 + +要提升解决问题的能力,要形成自己的方法论,去发现问题,分析问题和解决问题。 + +要提升自己的影响力,可以在一个领域深入打造自己独特的有价值的能力,让自己做事情能超出别人的预期,同时乐于分享和帮助他人。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/29自动化测试:如何把Bug杀死在摇篮里?.md b/专栏/软件工程之美/29自动化测试:如何把Bug杀死在摇篮里?.md new file mode 100644 index 0000000..bec188c --- /dev/null +++ b/专栏/软件工程之美/29自动化测试:如何把Bug杀死在摇篮里?.md @@ -0,0 +1,255 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 29 自动化测试:如何把Bug杀死在摇篮里? + 你好,我是宝玉。前不久我所在项目组完成了一个大项目,把一个网站前端的 jQuery 代码全部换成 React 代码,涉及改动的项目源代码文件有一百多个,变动的代码有几千行,最终上线后出乎意料的稳定,只有几个不算太严重的 Bug。 + +能做到重构还这么稳定,是因为我们技术水平特别高吗?当然不是。还是同样一组人,一年前做一个比这还简单的项目,上线后却跟噩梦一样,频繁出各种问题,导致上线后不停打补丁,一段时间才逐步稳定下来。 + +这其中的差别,只是因为在那次失败的上线后,我们总结经验,逐步增加了自动化测试代码的覆盖率。等我们再做大的重构时,这些自动化测试代码就能帮助我们发现很多问题。 + +当我们确保每一个以前写好的测试用例能正常通过后,就相当于把 Bug 杀死在摇篮里,再配合少量的人工手动测试,就可以保证上线后的系统是稳定的。 + +其实对于自动化测试,我们专栏已经多次提及,它是敏捷开发能快速迭代很重要的质量保障,是持续交付的基础前提。 + +所以今天我将带你一起了解什么是自动化测试,以及如何在项目中应用自动化测试。 + +为什么自动化测试能保障质量? + +自动化测试并不难理解,你可以想想人是怎么做测试的:首先根据需求写成测试用例,设计好输入值和期望的输出,然后按照测试用例一个个操作,输入一些内容,做一些操作,观察是不是和期望的结果一致,一致就通过,不一致就不通过。 + +自动化测试,就是把这些操作,用程序脚本来完成的,本质上还是要输入和操作,要检查输出是不是和期望值一致。只要能按照测试用例操作和检查,其实是人来做还是程序来做,结果都是一样的。 + +不过,自动化测试有一个手工测试没有的优势,那就是可以直接绕过界面,对程序内部的类、函数进行直接测试,如果有一定量的自动化测试代码覆盖,相对来说软件质量是更有保障的。 + +而且,一旦实现了自动化,每测试一次的成本其实大幅降低了的,几百个测试用例可能几分钟就跑完了。尤其是每次修改完代码,合并到主干之前,把这几百个测试用例跑一遍,可以有效地预防“修复一个 Bug 而产生新 Bug”的情况发生。 + +但现阶段,自动化测试还是不能完全代替手工测试的,有些测试,自动化测试成本比手工测试成本要高,比如说测试界面布局、颜色等,还是需要一定量的手工测试配合。 + +有哪些类型的自动化测试? + +现在说到自动化测试,已经有很多的概念,除了大家熟悉的单元测试,还有像集成测试、UI 测试、端到端测试、契约测试、组件测试等。而很多时候同一个名字还有不同的解读,很容易混淆。 + +在对自动化测试类型的定义方面,Google 的分类方法我觉得比较科学:根据数据做出决策,而不仅仅是依靠直觉或无法衡量和评估的内容。Google 将自动化测试分成了三大类:小型测试、中型测试和大型测试。 + +假设我们有一个网站,是基于三层架构(如下图所示),业务逻辑层的类叫 UserService 类,数据访问层的类叫 UserDA,我们将以用户注册的功能来说明几种测试的区别 。 + + + +小型测试 + +小型测试是为了验证一个代码单元的功能,例如针对一个函数或者一个类的测试。我们平时说的单元测试就是一个典型的小型测试。 + +比如说 UserService 这个类,有一个注册用户的函数,现在要对它写一个单元测试代码,那么看起来就像下面这样: + + + +通过这样的测试代码,就可以清楚的知道 UserService 类的 create 这个函数是不是能正常工作。 + +小型测试的运行,不需要依赖外部。如果有外部服务(比如文件操作、网络服务、数据库等),必须使用一个模拟的外部服务。比如上面例子中我们就使用了 FakeUserDA 这个模拟的数据库访问类,实际上它不会访问真实的数据库。这样可以保证小型测试在很短时间内就可以完成。 + + + +小型测试,图片来源:《Google软件测试之道》 + +中型测试 + +中型测试是验证两个或多个模块应用之间的交互,通常也叫集成测试。 + +如果说要对用户注册的功能写集成测试,那么就会同时测试业务逻辑层的 UserService 类和数据访问层的 UserDA 类。如下所示: + + + +对于中型测试,可以使用外部服务(比如文件操作、网络服务、数据库等),可以模拟也可以使用真实的服务。比如上面这个例子,就是真实的数据库访问类,但是用的内存数据库,这样可以提高性能,也可以减少依赖。 + +至于中型测试要不要使用模拟的服务,有个简单的标准,就是看能不能在单机情况下完成集成测试,如果可以就不需要模拟,如果不能,则要模拟避免外部依赖。 + + + +中型测试,图片来源:《Google软件测试之道》 + +大型测试 + +大型测试则是从较高的层次运行,把系统作为一个整体验证。会验证系统的一个或者所有子系统,从前端一直到后端数据存储。大型测试也叫系统测试或者端对端测试。 + +如果说要对用户注册写一个端对端测试的例子,那么看起来会像这样: + + + +对于大型测试,通常会直接使用外部服务(比如文件操作、网络服务、数据库等),而不会去模拟。比如上面这个例子,就是直接访问测试环境的地址,通过测试库提供的 API 操作浏览器界面,输入测试的用户名密码,点击注册按钮,最后检查输出的结果是不是符合预期。 + + + +大型测试,图片来源:《Google软件测试之道》 + +区分测试类型的依据是什么? + +以上就是主要的自动测试类型了。捎带着补充一个测试类型,那就是契约测试,这个测试最近出现的频率比较高,主要是针对微服务的。其实就是让微服务在测试时,不需要依赖于引用的外部的微服务,在本地就可以模拟运行,同时又可以保证外部微服务的接口更新时,本地模拟的接口(契约)也能同步更新。对契约服务更多的说明可以参考这篇文章:聊一聊契约测试 + +那么契约测试,属于大型测试还是中型测试呢? + +Google 针对这几种测试类型列了一张表,根据数据给出了明确区分: + + + +图片来源:Google Testing Blog + +结合上面的表格其实就很好区分了: + + +小型测试,没有外部服务的依赖,都是要模拟的; + +中型测试,所有的测试几乎都不需要依赖其他服务器的资源,如果有涉及其他机器的服务,则本地模拟,这样本机就可以完成测试; + +大型测试,几乎不模拟,直接访问相关的外部服务。 + + +所以现在你应该就知道契约测试,也是中型测试的一种了,因为它不需要依赖外部服务,本机就可以完成测试。 + +为什么中型测试这么看重“能单机运行”这一点呢?因为这样才方便在持续集成上跑中型测试,不用担心外部服务不稳定而导致测试失败的问题。 + +上面的表中还反映出一个事实:越是小型测试,执行速度越快,越是大型测试,执行速度越慢。通常一个项目的小型测试,不超过一分钟就能全部跑完,一个中型测试,包括一些环境准备的时间,可能要几分钟甚至更久,而大型测试就更久了。 + +另外越是大型测试,写起来的成本也相应的会更高,所以一般项目中,小型测试最多,中型测试次之,大型测试最少。就像下面这张金字塔图一样。所以我们也常用测试金字塔来区分不同类型的测试粒度。 + + + +测试金字塔,图片来源: TestPyramid + +如果你对测试类型很感兴趣,可以参考测试金字塔实战这篇文章作为补充。 + +怎么写好自动化测试代码? + +很多人认为写自动化测试很复杂,其实测试代码其实写起来不难,包含四部分内容即可,也就是:准备、执行、断言和清理,我再把第一段代码示例贴一下: + + + +第一步就是准备,例如创建实例,创建模拟对象;第二步就是执行要测试的方法,传入要测试的参数;第三步断言就是检查结果对不对,如果不对测试会失败;第四步还要对数据进行清理,这样不影响下一次测试。 + +上面还有几个测试代码示例,都是这样的四部分内容。 + +这是针对写一个自动化测试的代码结构。对于同一个功能,通常需要写几个自动化测试才完整。 + +一个完整的自动化测试要包括三个部分的测试: + + +验证功能是不是正确:例如说输入正确的用户名和密码,要能正常注册账号; + +覆盖边界条件: 比如说如果用户名或密码为空,应该不允许注册成功; + +异常和错误处理:比如说使用一个已经用过的用户名,应该提示用户名被使用。 + + + + +所以你看,写一个测试代码并没有你想的那么复杂,那还有什么理由不去写测试呢? + +如何为你的项目实施自动化测试? + +现在你了解了有哪些类型的测试,如何写自动化测试代码,也许迫不及待想在项目中实施自动化测试。 + +选择好自动化测试框架 + +要写好自动化测试代码,首先要找对自动测试化框架。不同的语言,不同的平台,测试的框架都不一样。好在现在搜索引擎很方便,根据“你的语言 + 自动测试框架”的关键字,就能找到很多的结果。这里我也帮你找了一些,供参考。 + + +Web 前端 + + +Jest: Facebook 的前端测试框架; + +Mocha:历史悠久的一个 JS 测试框架; + +Nighwatch: 一个 API 很简单,但是功能很强大,可以直接操作浏览器的自动测试框架。 + + +iOS 开发 + + +可以参考这篇文章《iOS 自动化测试框架对比》。 + + +安卓开发 + + +可以参考这篇文章《Android 谈谈自动化测试》。 + +在持续集成环境上跑你的自动化测试 + +选好自动化测试框架后,你的自动化测试代码,其中的小型测试和中型测试,最好要能在持续集成环境上运行起来。 + +让自动化测试在持续集成上运行非常重要,只有这样才能最大化地发挥自动化测试的作用。 + +因为持续集成,会强制测试通过才能合并代码,在合并代码之前就能知道测试是不是都通过了,可以帮助程序员获得最直观的反馈,知道哪里可能存在问题,这样才能真正做到防患于未然,把 Bug 杀死在摇篮里。 + +下图描述的就是自动测试配合持续集成的一个标准流程: + + +在提交代码前,先本地跑一遍单元测试,这个过程很快的,失败了需要继续修改; + +单元测试成功后就可以提交到源代码管理中心,提交后持续集成服务会自动运行完整的自动化测试,不仅包括小型测试,还有中型测试; + +通过所有的测试后,就可以合并到主分支,如果失败,需要本地修改后再次提交,直到通过所有的测试为止。 + + + + +图片来源:Microservice Testing: Unit Tests + +新项目和老项目的不同策略 + +如果是新项目,那么可以在一开始就保持一定的自动化测试代码的覆盖率,你甚至还可以试试测试驱动(TDD)的开发模式,也就是先写测试代码,再写实现代码,保证测试通过,最后对代码进行重构。 + + + +图片来源:郑晔 《10x程序员工作法》专栏 + +如果是老项目,短期内要让自动化测试代码有覆盖是有难度的,可以先把主要的功能场景的中型测试写起来,这样可以保证这些主要功能不会轻易出问题。 + +后面在维护的过程中: + + +增加新功能的时候,同步对新功能增加自动化测试代码; + +修复 Bug 的时候,针对 Bug 补写自动化测试代码。 + + +这样一点一点,把自动化测试代码覆盖率加上去。 + +如果时间紧任务重,来不及写自动化测试怎么办? + +确实遇到时间紧的情况,我建议你要优先保证中型测试代码的覆盖,因为这样至少可以保证主要的用户使用场景是正常的。然后把来不及完成的部分,创建一个 Ticket,放到任务跟踪系统里面,后面补上。 + +总结 + +今天我带你一起学习了关于自动化测试有关的知识。自动化测试,分为三类: + + +小型测试,主要针对函数或者类进行验证,不调用外部服务,执行速度快; + +中型测试,主要验证两个或多个模块应用之间的交互,可能会调用外部服务,尽可能让所有测试能在本机即可完成,执行速度比较快; + +大型测试,对服务整体进行验证,执行速度慢。 + + +写好单元测试代码,基本结构就是:准备、执行、断言和清理;基本原则就是: + + +要验证正确性; + +覆盖边界条件; + +验证是否有异常和错误的处理。 + + +自动化测试,一定要配合好持续集成,才能最大化发挥其效用。 + +对于自动化测试的实施,开头是最难的,因为需要花时间选择自动化测试框架,需要针对自动化测试框架搭建环境,甚至要去搭建持续集成环境。但搭建持续集成和搭建自动化测试环境,并且保证持续更新维护自动测试代码,这个技术投资,一定是你在项目中最有价值的投资之一。 + +搭建持续集成环境和集成自动化测试框架的事情,要作为一个正式的项目任务去做,当作一个很重要的任务去推进。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/30用好源代码管理工具,让你的协作更高效.md b/专栏/软件工程之美/30用好源代码管理工具,让你的协作更高效.md new file mode 100644 index 0000000..18c9587 --- /dev/null +++ b/专栏/软件工程之美/30用好源代码管理工具,让你的协作更高效.md @@ -0,0 +1,316 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 30 用好源代码管理工具,让你的协作更高效 + 你好,我是宝玉。在今天,源代码管理工具在软件项目中已经是标准配置了,几乎每个软件项目都会应用到,可以说是最基础的项目开发工具。选择也很多,可以自己搭建源代码管理服务,也可以直接用网上托管的服务,例如 GitHub、GitLab、BitBucket 等。 + +但同样是应用源代码管理工具,为什么有的团队就能做到代码质量高,随时能发布新版本,高效开发?而有的团队却不能做到高效开发,拿到的代码也不稳定,合并时冲突很多? + +今天,我将带你了解一下源代码管理工具,以及如何才能应用好源代码管理工具,以保证代码质量稳定,协作高效。 + +源代码管理工具发展简史 + +源代码管理工具也叫版本控制系统,是保存文件多个版本的一种机制。每一次有人提交了修改,这个修改历史都会被版本控制系统记录下来。如下图所示,每一次对内容的修改,都会形成一个当前项目完整内容的快照。 + + + +图片来源:《什么是版本控制?》 + +源代码管理工具从诞生到现在已经有 40 多年的历史了,经历了四个阶段。 + +没有源代码管理工具的时代 + +早些年开发软件可没有我们这么幸运,在 1972 年之前都没有任何工具可以帮助我们做源代码管理。 + +这就意味着,当你开发时,必须要告知团队里的其他人,你正在对哪些文件进行编辑,好让他们不要操作相同的文件。当你花了很长时间完成你的修改后,可能这些文件早已经被团队里的其他开发成员修改或者删除了。 + +除了协作的问题,还有一个问题就是版本问题。没有源代码管理,你得经常性对项目的文件保存备份,很麻烦,而且还是一样有不少问题: + + +很难知道做了哪些修改,你可能需要挨个目录去查看文件修改时间; + +对版本命名是一个很麻烦的事情,每次备份都得有一个名字; + +很难知道两次备份之间,做了哪些修改。 + + +本地版本管理 + +最早的版本控制系统是 SCCS(Source Code Control System),诞生于 1972 年,它实现了对单个文件保留多个版本,这就意味着你可以看到每一个文件的修改历史了。 + +后来又有了 RCS (Revision Control System),它具有更好的文件比较算法,通过登录同一台中央大型机,可以实现每个人签出自己的拷贝。 + +但这个阶段只能本机使用,而且一次只能修改一个文件,无法满足好多人协作的问题。 + +集中式版本管理 + +1986 年问世的 CVS(Concurrent Versions System)是第一个采用集中式的服务器来进行版本库的管理工作,所有文件和版本历史都放在服务端,每个用户通过客户端获取最新的代码,可以多个人编辑一个文件,并且能提交到服务器合并在一起。 + +再后来的 SVN(Subversion)则对 CVS 进行了很多优化,比如支持文件改名、移动、全局版本号等,这些优化很大部分程度上解决了 CVS 存在的一些缺陷,所以在 2000 年后逐步取代了 CVS 成为主流的源代码管理工具。 + + + +图片来源:How Version Control Systems Work + +不过,这类集中式源代码管理工具,过于依赖服务器,如果服务器出问题或者连不上,就没法用了,如果服务器损坏,所有的版本历史也会丢失。 + +分布式版本管理 + +分布式版本管理工具的典型代表就是 Git,分布式版本控制系统的整个代码库的副本都可以存储在用户的本地系统上,这样文件和版本控制操作变得非常容易,离线也可以操作,如果主存储库关闭或者删除,可以很容易从本地存储库恢复。 + +现在 Git 已经逐步替代了 SVN、CVS 等源代码管理工具,成为最主流的源代码管理工具。 + +Git 的主要问题是学习成本要稍微高一点,要花点时间理解它的工作原理和记住主要的命令。 + + + +图片来源:How Version Control Systems Work + +如何选择合适的源代码管理系统 + +现在源代码管理系统已经有很多的选择,你可以选择网上托管的代码管理服务,或者是自己搭建。 + +自己搭建的好处就是可以有更多的控制,但是需要有自己的服务器,自己搭建环境,还要后续的维护。用网上的托管平台,可以减少运维成本,功能也很强大,但对平台有一定的依赖。 + +我的建议是如果项目规模不大,隐私要求不高,完全没必要自己搭建,可以直接选择网上的托管平台。这样可以节约很多时间成本,而且还可以方便和一些第三方服务,例如持续集成等进行整合。 + +如果希望对源代码管理有更多控制,也能接受运行维护上的投入,就可以选择自己搭建。 + +自己搭建源代码管理系统 + + +Git + + +Git 本身是开源免费的,所以每个人都可以搭建自己的 Git Server,具体的操作执行我就不在此处展开了,网上有很多教程。例如:《 服务器上的 Git - 在服务器上搭建 Git 》《搭建自己的 Git 服务器》 + + +GitLab + + +Git 自带的 Server 默认是没有 Web 界面进行管理的,只能用命令行操作交互,这在操作上有很多不便利性,尤其是不方便做代码审查,所以可以安装GitLab 的社区版本,开源免费的,有 Web 操作界面,可以像 GitHub 一样提交 Pull Request,并且和 CI (持续集成)系统例如 GitLab CI、Jenkins 都有很好的集成。 + +网上也有很多安装教程:《在自己的服务器上部署 GitLab 社区版》《GitLab 的安装及使用教程》。 + + +Gerrit + + +Gerrit是由 Google 开发的,用于管理 Google Android 项目源代码的一个系统。它支持 Git 和代码评审。参考教程:《Gerrit 代码审核服务器搭建全过程》 + +网上的代码托管平台 + + +GitHub + + +GitHub现在已经是全球最流行的代码托管平台,功能强大,和第三方服务集成非常好。而且私有的代码库如果不超过 3 个人都是免费的,我自己很多个人项目就都是放在 GitHub 上托管。 + +GitHub的 Web UI 非常强,尤其是代码浏览和审查,在网站上就可以提交 Pull Request 和进行代码审查。不过 GitHub 不提供 CI 服务,需要和第三方 CI 服务集成。 + + +GitLab + + +GitLab的网上托管服务很多地方和 GitHub 都很类似,但是价格更便宜。例如免费用户可以支持无限的私有项目,也内置了 CI 的支持。 + + +Coding + + +Coding是国内一个不错的代码托管平台,5 人以下的私有库免费,内置了 CI 支持,同时还有项目管理工具支持。 + +其他的服务还有像:码云、阿里云 Code、百度效率云、腾讯 Git 代码托管、华为云 CodeHub,这里就不一一介绍了。 + +如何用好源代码管理工具? + +用好源代码管理工具,有三个简单可行的原则: + +原则一:要频繁的提交 + +很多开发人员不愿意轻易提交代码到源代码管理中心,喜欢“憋个大招”,本地做了大量修改,希望代码能“完美”。但这样做却没能享受到频繁提交带来的好处。 + +频繁提交,这意味着你每次提交的代码变更是比较少的,便于 Code Review,同时如果出现问题,也可以迅速定位或者直接回滚。 + +频繁地提交,也让团队成员可以及时同步最新代码,不至于在最后合并时,产生有大量的合并冲突。 + +频繁提交,不意味着提交不完整的内容,而是将要提交的内容分拆,并且保证完整性。 + +比如说,有一个涉及前后端的功能,可以拆分成前端和后端两次提交,各自有独立的代码和测试;比如说你开发新功能的时候发现有代码需要重构,那么对于重构的代码单独一次提交,不要和新功能的代码提交放一起。 + +原则二:每次提交后要跑自动化测试 + +在前面章节我们学习了自动化测试和持续集成,自动化测试是非常有效的质量保障手段,而持续集成工具,可以让每次提交代码后,能自动地运行相应的自动化测试代码,有效保障提交代码的质量。 + +源代码管理的第二个原则,就是每次提交,必须要运行自动化测试代码,如果测试不通过就不能合并,要对问题进行甄别和修复,确保提交的代码质量是没问题的。 + +原则三:提交的代码要有人审查 + +代码审查是自动化测试之外,一种非常行之有效的提高质量的手段,通过代码审查,可以发现代码中潜在的问题。通过代码审查,也可以加强团队的技术交流,让水平高的开发人员 Review,可以帮助提升整体代码水平;Review 高水平的代码也是一种非常有效的学习方法。 + +怎么做好代码审查呢? + +我的经验是,在审查别人代码的时候,先了解清楚这个提交的代码要解决的是什么问题,想象一下如果是自己来写这个代码会怎么写。这样在审查的时候,就能发现一些和自己不一样的地方,别人好的地方我们可以学习,不对的地方应该指出。 + +对于审查出来的问题,可以分成三个类型: + + +问题:如果对代码有不清楚的地方,可以作为问题提出,进一步澄清确认; + +建议:原来的实现没有太大问题,但是可以有不同的或者更好的实现; + +阻塞:代码有明显问题,必须要修改。 + + +这样对于被审查的人可以针对你的问题进行针对性修改。 + +这三个原则很简单,可以有效提升代码质量,减少合并冲突,及时发现问题,从而让你的协作更高效。 + +我该选择什么样的开发流程? + +现在基于源代码管理有三种主要的开发流程: + + +Git flow + +GitHub flow + +GitLab flow + + +阮一峰老师有一篇文章Git 工作流程对它们有详细介绍。在这里我重点介绍一下 GitHub flow,因为它简单易懂,另外,它也对上面我提到的三个原则有很好的支持。 + +当然,我并不是说你一定要选择 GitHub flow,而是在理解它后,可以基于它的流程衍生出适合自己项目特点的开发流程。 + +GitHub 开发流程 + +GitHub 开发流程的关键在于两点: + + +有一个稳定的分支,例如 master; +每次创建新功能或者修复 Bug,必须创建一个分支。最后通过代码审查和自动化测试后,才能合并回稳定分支。 + + +通过这样的开发流程,就相当于把自动化测试和代码审查作为一种强制性要求了,所有的修改必须要通过代码审查和自动化测试通过才能合并,从而保证有一个可以随时部署发布的稳定分支。 + +我们具体看看基于 GitHub flow 是如何开发的。 + +第一步:创建一个分支 + +分支是 Git 中的核心概念,整个 GitHub 流程都是基于分支展开的,master 分支是要一直保持稳定的,不能直接在 master 上开发。 + +无论你是要开发一个新功能还是修复一个 Bug,第一件事永远是从 master 创建一个分支出来。 + + + +图片来源:GitHub网站截图 + +第二步:提交更新 + +当创建好分支后,就可以基于分支开始工作了,这时候就可以按照前面建议的原则,频繁的提交更新。注意每次提交的时候,要加上说明性的信息,让其他人明确知道你这次提交的内容是什么,如果开发过程中,发现错误了,还可以随时回滚之前的更改。 + + + +图片来源:GitHub网站截图 + +第三步:创建一个 Pull Request + +在开发完成后,创建一个 Pull Request(合并请求,简称 PR,GitLab 中叫 Merge Request),创建 PR 时,通常要附上描述性的信息,关联上相应的 Ticket 连接,让其他人知道你这个 PR 要完成什么任务。创建好 PR 后,其他人就可以直观的看到你所有的修改。 + + + +图片来源:GitHub网站截图 + +第四步:讨论和代码审查 + +当你的 PR 提交后,团队的其他人就可以对 PR 中的代码修改进行评论。比如说代码风格不符合规范、缺少单元测试、或者很好没有问题。PR 的主要目的就是为了方便大家做代码审查。 + +根据代码审查的结果,你可能要做一些修改,那么只要继续提交更新到这个分支就可以了,提交更新后,PR 就会自动更新,其他人可以基于你的更新进一步的讨论和审查,直到通过代码审查。 + + + +图片来源:GitHub网站截图 + +第五步:部署测试 + +在合并前,还需要把分支的修改进行测试。理论上来说,需要将修改的内容部署到测试环境测试,但这样效率太低了,所以通常的做法是借助持续集成工具,在每次提交代码后,就运行自动化测试代码,自动化测试代码全部通过后,就可以认为质量是可靠的。 + +这也意味着你需要让项目中的自动化测试代码保持一定的测试覆盖率,否则质量还是难以保障的。 + + + +图片来源:GitHub网站截图 + +第六步:合并 + +当你的代码通过了代码审查和自动化测试,就可以将代码合并到 master 分支了。合并后,之前的分支就可以删除,但你之前所有的提交记录在 master 都可以看到,所以完全不用担心丢失历史版本记录。 + + + +图片来源:GitHub网站截图 + +以上就是 GitHub 开发流程的主要步骤,通过分支开发新功能或者修复 Bug,强制通过代码审查和自动化测试才能合并 master,从而保证 master 的稳定。 + +GitHub 开发流程的几个常见问题 + +基于这个流程我再补充几个常见问题: + + +怎么发布版本? + + +要发布版本的话,从 master 上创建一个 Tag,例如 v1.0,然后将 Tag v1.0 上的内容部署到生产环境。 + + +怎么给线上版本打补丁? + + +如果线上发布的版本(例如 v1.0)发现 Bug,需要修复,那么基于之前的 Tag 创建一个分支(例如 hotfix-v1.0-xxx)出去,在分支上修复,然后提交 PR,代码审查和自动化测试通过后,从分支上创建一个新的 Tag (例如 v1.0.1),将新的 Tag 发布部署到生产环境,最后再把修改合并回 master。 + + +如果我经常需要打补丁,有没有比 Tag 更好的办法? + + +每次发布后,可以创建一个发布版本的分支,例如 release-v1.0,每次打补丁,都直接从发布分支 release-v1.0 而不是 master 创建新的分支(例如 hotfix-release-v1.0-xxx),修复后提交 PR,代码审查和自动化测试通过后,合并回分支 release-v1.0,然后基于 release-v1.0 分支发布补丁。 + +最后将合并的 PR,借助 git 的 cherry-pick 命令再同步合并回 master。 + +上面的例子其实主要是说明一下,GitHub Flow 只是一种基础的开发流程,你完全可以基于 GitHub Flow,衍生出适合你自己项目特点的开发流程。 + +无论你基于哪一种开发流程,最好能做到这两点: + + +有一个稳定的代码分支; +在合并分支之前,对代码有审查,自动化测试要能通过。 + + +这样你才能做到可以随时发布,质量稳定,高效协作。 + +总结 + +源代码管理工具也叫版本控制系统,是保存文件多个版本的一种机制,可以记录文件的历史版本。 + +用好源代码管理工具,有三个简单可行的原则: + + +原则一:要频繁的提交; + +原则二:每次提交后要跑自动化测试; + +原则三:提交的代码要有人审查。 + + +基于源代码的开发流程,要保证好两点: + + +有一个稳定的代码分支; +在合并分支之前,对代码有审查,自动化测试要能通过。 + + +用好源代码管理工具,设计好开发流程,保证好代码质量,你的协作才能更高效。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/31软件测试要为产品质量负责吗?.md b/专栏/软件工程之美/31软件测试要为产品质量负责吗?.md new file mode 100644 index 0000000..7893e44 --- /dev/null +++ b/专栏/软件工程之美/31软件测试要为产品质量负责吗?.md @@ -0,0 +1,141 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 31 软件测试要为产品质量负责吗? + 你好,我是宝玉。从这一篇开始,我们将进入软件工程中的测试模块的学习。 + +说到软件测试,你一定不会陌生,尤其是如果你做开发相关岗位的话,一定是对测试又爱又恨,一方面测试从你的程序找出 Bug,然后你还要费心去修复;另一方面测试帮你发现 Bug,修复后能很好的提升质量。 + +正因为测试能发现软件中的质量问题,通过测试能有效提升软件质量,慢慢的大家就觉得软件测试能保障质量,所以测试要对质量负责。开发也会对测试产生依赖心理,很多功能模块实现后,就扔给测试人员去测试。 + +上线后,如果因为有测试漏测导致的 Bug,测试人员还要为质量问题背锅,受到责备。上面这样的场景到现在也还在很多软件项目中上演。但这对测试人员其实是不公平的。 + +因为软件开发是多个环节组成的,从最开始的需求,到后面的设计、开发,每个环节都可能会导致质量问题,而测试只能对已经开发完成的软件产品进行检测,并不能干预整个过程。 + +比如说测试是无法对开发写的代码直接测试的,只能基于软件功能去测试,也就是说对于代码的质量,测试人员其实是没有什么办法的。 + +那到底谁应该为产品质量负责呢?在回答这个问题之前,你不妨先思考一个更本质的问题:什么是软件产品质量? + +什么是软件产品质量? + +我以前以为,软件质量就是由 Bug 数量、性能高低、安全性等指标决定的,现在看来这样划分其实并不全面。 + +因为不同的人对软件质量好坏的评判角度是不同的。比如对用户来说,更看重产品是不是满足需求,是不是美观好用;对开发来说,看重的是代码质量是不是高,是不是好维护;对于软件测试人员而言,看重的是 Bug 数量、安全、性能等指标;对于项目负责人,看重的是整个开发过程的质量,是不是成本可控、如期完成。 + +在这个问题上,我比较认同《The Three Aspects of Software Quality: Functional, Structural, and Process》这篇文章作者 David Chappell 的观点,他把软件质量分成了三个考量方面:功能、结构和流程。对于他提的“结构质量”,我认为定义为“代码质量”更贴切,也就是说,功能质量、代码质量和过程质量这三个方面组合在一起,很好地概括了软件质量。 + +所有的软件开发都是从一个想法开始的,用户需要一个软件,有人出钱,然后开发团队实施,把想法变成需求,需求变成设计,设计变成代码,代码变成软件。 + + +功能质量 + + +最终用户得到是软件,体验的是软件的功能,功能的质量直接决定了产品的质量。 + +满足用户需求,是对功能质量最基础的要求。在这个基础上,Bug 数量、性能、UI/UX 都是很重要的质量指标。如果你的软件 Bug 太多、性能差,用户不会满意;界面难看,操作体验也很差,这些因素都决定了你产品的功能质量。 + + +代码质量 + + +构成软件最重要的部分是代码,代码质量指的是实现软件功能的架构和代码的质量。代码的质量主要体现在以下这些方面: + + +代码的可维护性,也就是在不影响稳定性的前提下,是否能方便地添加或者修改现有的代码; + +代码的可读性,代码是否容易理解,是否能快速上手; + +代码的执行效率,代码执行效率直接影响了软件性能; + +代码的安全性,是否有安全漏洞,安全性是代码质量很重要的一个指标; + +代码的可测试性,代码是否能使用单元测试、集成测试进行测试验证。 + + +虽然用户不能直接感知到代码,但是代码质量高低会直接影响功能质量,同时代码质量低也会影响后续的维护升级。 + + +过程质量 + + +软件的开发离不开软件工程,离不开项目管理。软件开发过程的质量决定了你的项目是否能如期完成,开发成本是否在预算之内。 + +过程质量虽然也是用户不能直接感知的,但是过程质量会直接影响代码质量和功能质量,甚至是产品的成败。 + +以上就是软件质量的三个方面,软件质量从来不是单方面质量决定的,通常是几方面质量因素相互影响,共同决定的。 + +比如说改进流程,增加了自动化测试的覆盖,应用了持续集成,这样可以提高代码质量和功能质量。或者说对代码质量过于追求,又可能会影响过程质量,例如时间延期,成本超标。 + +谁该为产品质量负责? + +在梳理清楚产品质量的问题后,我们就可以来讨论谁该为产品质量负责的话题了。 + +既然产品质量是由功能质量、代码质量和过程质量共同决定的,那么对产品质量负责,意味着要对这三方面共同负责。 + +在说到责任之前,我想补充一下权责对等的问题。责任和权力是需要对等的,比如说你让开发人员对软件开发过程负责,那么前提是他必须有权力去影响和控制开发过程,否则离开权力谈责任就是耍流氓了。 + +然后,我们再一起看看项目中的主要角色,谁最应该为产品质量负责? + +软件测试,可以对功能质量负责,对软件产品进行测试验收,以确保产品满足功能需求,有好的功能质量。但是通常不能对代码质量和过程质量负责。 + +开发人员,可以对代码质量负责,也可以写测试代码,通过自动化的方式做功能测试,虽然还不能完全替代手工测试的作用,所以也可以算得上对功能质量负责。但开发人员通常对过程质量影响有限。 + +项目负责人,可以对过程质量负责,而且过程质量的水平高低,会间接影响代码质量和功能质量。但因为项目负责人不直接编码和测试,所以无法直接影响代码质量和功能质量。 + +所以综上,我觉得如果要排序的话,软件质量的首要负责人是项目负责人,其次是开发人员,然后才是软件测试。 + +虽然从权责的角度看,项目负责人是最应该对项目质量负责的,但是从效果来说,却是开发人员对项目质量负责最有利。 + +首先,开发人员是唯一能直接影响代码质量、能对代码质量负责的人。开发人员能更容易地找到代码中的 Bug,更容易通过架构设计、自动化测试代码等手段保证好代码质量,提升测试效率。 + +现在软件开发的发展趋势也是如此,软件测试的很大一部分手工测试工作已经被自动化代替。 + +所以很多公司就让开发负责产品质量,甚至都不设测试岗位,典型代表就是 Facebook。开发人员自己写代码实现功能,然后写自动化测试代码对功能进行测试,最后上线。这样不仅自己测试能保证功能的质量,又能通过自己写单元测试、集成测试来保证代码的质量。 + +当然,开发人员对功能质量负责,意味着必须在实现功能的同时,还要考虑如何去测试这个功能,这样让代码更具有可测试性,这就对开发人员的要求更高了。 + +就像 Facebook 强调的“Be there from start to ship”,就是让每个工程师能自始至终地负责产品。从想法到原型设计、到产品开发、上线和维护,全部是工程师自己完成。 + +我们不需要做到 Facebook 那样,从头到尾都一个人搞定,但至少,作为开发人员,我们可以对代码质量有更高要求,让项目有更多自动化代码的覆盖;可以在交付测试之前自己先测试一遍。 + +这样的话,开发就可以真正做到对代码质量和功能质量负责。如果你还想对过程质量也能负责,那么敏捷开发中一些理念是有可取之处的。 + +敏捷开发中强调的是:项目的所有人一起为产品质量负责,人人为产品质量负责。 + +但人人为质量负责,很容易变成一句口号而很难落实。就像三个和尚没水喝的故事里面那样,当质量变成每个人的责任时,就没有人真正为质量负责了。所以我们不只是要学习敏捷开发中的理念,还要学习它一些具体的方法。 + +如何做到“人人为产品质量负责”? + +只有真正在团队中建立了一种重视产品质量的文化,每个人才会确确实实地对质量负责。那么有哪些方法可以帮助团队建立这种“人人都重视产品质量”的文化呢? + +首先,可以参考敏捷开发中的扁平化管理。在敏捷开发中没有项目经理,只有产品负责人,而产品负责人更多是充当一种服务型的角色。大家都很平等,也就是说每个人都有权力去影响到项目过程,实现权责对等,大家才会为过程质量负责。 + +其次,可以选择将团队拆小。敏捷开发中的团队规模都不大,大的开发团队拆分成了小的开发小组,每个组人数都不多。人数多的时候容易推诿扯皮,但如果人少,每个人就必须要承担更多的责任,这有助于形成人人重视产品质量的文化。 + +另外,也可以鼓励工种之间的融合,例如开发人员多写自动化测试代码;测试人员在开发人员写自动化测试时,提供帮助,例如设计测试用例。这样不只是局限于各自负责的质量领域,也同时关注其他质量领域。 + +最后就是制定相应的制度,鼓励大家重视质量。比如说: + + +每个 Sprint 都有项目回顾会议,每个人都可以针对质量提出有效的建议,最终将这些建议落到实处; + +出现质量问题,不是推卸责任,而是分析原因,及时修复,避免以后出现类似问题。 + + +要做到“人人为产品质量负责”,还是要像上面提到的一样,要落到行动而不是口号上,组织上扁平化、小型化,分工上打破岗位墙,制度上鼓励大家重视质量,才能真正建立重视产品质量的文化,一起把产品的质量提升上去。 + +总结 + +今天我带你一起探讨了一个在软件项目中的常见问题:软件测试要为产品质量负责吗? + +保证软件高质量,并非只是测试人员的责任。软件质量体现在功能质量、代码质量和过程质量这三个方面,对产品质量负责,也意味着要对这三方面共同负责。 + +软件测试,不能影响代码质量和过程质量,所以并不需要为产品质量负责,项目负责人能直接影响过程质量,也能间接影响代码质量和功能质量,应该为产品质量负责。对于开发人员而言,不应只是局限于对代码质量负责,还应该注意功能质量。 + +对产品质量,最理想的状态还是能做到人人都为产品质量负责,而达到这样的目标,还是需要建立一种重视质量的文化,每个人才会确确实实地对质量负责。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/32软件测试:什么样的公司需要专职测试?.md b/专栏/软件工程之美/32软件测试:什么样的公司需要专职测试?.md new file mode 100644 index 0000000..a9b7e44 --- /dev/null +++ b/专栏/软件工程之美/32软件测试:什么样的公司需要专职测试?.md @@ -0,0 +1,207 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 32 软件测试:什么样的公司需要专职测试? + 你好,我是宝玉,我今天想与你讨论一下,什么样的公司需要专职测试这个问题。 + +若干年前,网络上对于软件开发是否需要专职测试有过一次讨论,代表文章有:左耳朵耗子老师写的我们需要专职的 QA 吗?,然后邹欣老师对此回复测试 QA 的角色和分工。 + +从这些年业界发展趋势来看,看起来很多公司都不需要专职测试了,只需要开发兼任测试工作就可以了。比如,Facebook 号称自己没有专职测试工程师,Google 和 Amazon 虽然有专职的测试工程师,但都是开发人员对质量负责,开发人员写大量的自动化测试代码。但这样真的可行吗? + +在回答这个问题之前,我们还是先来看看,软件测试的主要工作是什么?只有搞清楚软件测试的工作,才能搞清楚这部分工作是否可以由开发来替代,是否需要专职测试。 + +软件测试的主要工作是什么? + +在前面开发篇内容的学习中,我们对开发的工作已经比较了解了:在需求确定后,开发人员开始针对需求进行架构设计,然后编码,最后对发现的 Bug 进行修复,保障线上稳定运行。 + +而软件测试也类似,也是从需求开始,在需求确定后要去对需求进行分析,然后做测试设计。 + +如果说架构设计是对业务需求在技术方面的抽象,那么测试设计更像是对业务需求的具象,把业务需求分解成一个个具体的用户操作步骤,也就是测试用例。然后在开发完成后,按照设计好的测试用例进行逐一的测试验证,将发现的 Bug 报告给开发人员,并跟踪 Bug 的修复。 + +如果对软件测试的工作简单总结一下,就是发现 Bug,报告 Bug,跟踪 Bug。 + +软件测试怎么发现 Bug? + +这里面最难的就是发现 Bug,尤其是如何尽早、尽可能全面地发现 Bug。 + +举个例子来说,如果现在你需要开发一个用户登录的功能,你在开发完成后会怎么测试? + +一个普通的程序员通常只会简单测试一下以下用例: + + +输入正确的用户名、密码,能登录; +输入错误的用户名密码,提示错误,不能登录。 + + +而一个有经验的程序员还会测试一下其他情况,例如: + + +用户名或者密码为空,是否提示错误; +没有注册的用户名和密码,是否会提示错误。 + + +但如果是一个专业的测试人员来测试,是不是只做上面的测试就够了呢?专业测试还会测试哪些内容呢? + +对于专业测试人员来说,上面这些肯定是不够的,还需要有以下这些情况的功能性测试: + + +用户名密码是否大小写敏感; + +用户名或密码如果是用特殊字符,会不会导致程序异常; + +用户名或密码如果特别长,是不是会有异常; + +是不是所有主流浏览器和终端设备都能使用。 + + +这就完了吗?并没有。除了功能性的测试,还需要进行非功能性的测试,也就是像性能、安全性和用户体验等方面的测试。比如以下测试用例: + + +是否可以通过发送数据包反复登录,暴力破解密码; + +会不会有 Sql 注入的风险; + +大量用户同时登录,页面会不会崩溃; + +用键盘 tab、回车键是否可以操作。 + + +当然还会有其他测试用例,这里就不一一列举。为什么专业测试人员和开发人员的测试用例会差这么多? + +因为开发人员的重点,是放在如何实现功能上,就拿上面用户登录的例子,开发人员会想着如何能校验用户输入的用户名密码,并给出相应的提示,对于异常流程和场景会相对考虑较少。 + +而对于测试人员来说,重点是在检测,也就是会考虑所有可能的用户使用场景,正常的、异常的,甚至各种极端情况,例如大量并发访问、黑客恶意破解,所以他们能想到更多、更全面的测试用例。 + +测试人员设计测试用例,就是要尽可能做到覆盖所有用户操作的可能,但理论上来说这是不可能的,因为组合是有无限多个的。不过从测试的角度看,没有必要每一个可能都去测试,可以通过一些科学的方法来通过有限的测试用例,保证尽可能多的测试覆盖。 + +有哪些方法呢?我给你举几个例子: + + +等价类划分 + + +就是把所有用户可能输入的数据分类,如果一类数据对于发现 Bug 的效果是一样的,那么这类数据就是一个等价类,测试的时候只要从里面任意选取一个值就好了。比如一个输入框要求只能输入 1-100 之间的整数,那么 1 到 100 之间都是等价的,0 和任意负数也是等价的,101 和之上的整数也是等价的。 + +因为分类是有限的,这样就可以用有限的测试用例实现尽可能多的测试覆盖。 + + +边界值分析 + + +边界值是对等价类的补充,因为输入输出的边界是非常容易出错的一个地方。比如说上面输入框的例子,0,1,100,101 都是边界值,可以设计用例来测试是否会有可能出错。 + + +探索性测试 + + +探索性测试就是根据前面的测试结果,通过有效的策略进行测试。 + +举例来说,如果你玩过 RPG 游戏的话,里面通常会有走迷宫的地图,各种分叉,不同的分叉可能有不用的宝箱,如果你想打开所有的宝箱,那么就可以在遇到分叉的时候,每次优先走右边的分叉,除非右边已经去过了。 + +那么这里“右边的分叉是不是走过了”,就属于已经测试过的结果,策略就是优先走右边的分叉。关于探索性测试的介绍可以参考这篇文章《探索性测试揭秘》。 + +当然除了以上这几个主要的策略,还有很多其他的策略,比如说: + + +场景设计; + +因果图; + +错误推测法。 + + +这里我就不一一介绍,推荐阅读《微软的软件测试之道》,这本书上有很多具体的测试方法的详细介绍。 + +借助这些方法,测试人员就可以对需求功能设计出完整的、有较高覆盖率的测试用例。 + +所以,有时候测试人员的工作看起来不过是用鼠标点点测试,但他们在拿到需求后,其实花了很多时间和精力分析需求,然后根据需求设计测试用例,准备测试数据。等到开发人员完成软件开发后,就按照设计好的测试用例逐一测试,这样就可以做到及时发现 Bug。 + +软件测试怎么报告 Bug? + +在测试的过程中,如果测试人员发现了 Bug,就会通过 Bug 跟踪系统提交 Bug 给开发人员。Bug 跟踪系统其实跟我们在《14 项目管理工具:一切管理问题,都应思考能否通过工具解决》中介绍的任务跟踪系统是一样的,它可以方便地提交和跟踪 Bug。 + +测试人员要做的就是创建一个新的 Ticket,在 Ticket 的描述中,详细说明 Bug 是什么,具体的重现步骤,必要的话还要附上截图、日志等辅助信息。这样开发人员在收到 Bug 后就能快速定位问题,按照优先级对 Bug 进行修复。 + +软件测试怎么跟踪 Bug? + +Bug 的跟踪,并不仅仅是要跟踪开发人员什么时候修复了这个 Bug,通常还包括对 Bug 修复的验证。 + +开发人员修复完一个 Bug 后,测试人员首先会验证这个 Bug 是不是真的被修复了,然后还要对整体功能做一个回归测试,确保不会因为修复 Bug 而引起其他功能出现问题。 + +回归测试是指修改了旧代码后重新进行测试,以确认修改没有引入新的错误或导致其他代码产生错误。 + +回归测试这一步很重要,因为通常开发人员在修复完 Bug 后,只会验证其修复的 Bug,而不会验证其他功能是不是会有影响。但实际上,软件项目中经常会出现修复一个 Bug,而导致系统其他功能出现问题的情况。回归测试,则能有效、及时地发现修复 Bug 导致系统不稳定的情况。 + +什么样的公司需要专职测试? + +了解了测试的主要工作内容之后,我们再回过头来看看今天要讨论的问题:什么样的公司需要专职测试。 + +如果一个公司不需要专职测试,那么意味着专职测试的工作可以被其他工种替代,比如说由开发人员一起完成软件测试的工作。 + +想象一下,如果你是一名开发工程师,然后你要兼职做测试,那么你需要额外做好哪些工作? + +首先,你在拿到需求后,除了做技术上的设计外,还需要做测试上的设计,借助测试方法设计测试用例。 + +这样做好处很明显,可以在写程序时,让程序更易于测试,设计时会对需求考虑更全面。缺点也显而易见,你不止要学习编程知识、了解业务,还要学习测试方法。也许对你来说可以做到,但是对于绝大多数开发人员来说,这是一个很高的要求。 + +然后在开发完成后,要对自己写的程序进行测试。这里可能存在一个问题,也就是如果你在程序实现的时候,漏掉了一个逻辑处理,比如说漏了检测 Sql 注入,那么你在测试的时候也不会想到要去测试这部分。 + +测试自己的程序还要克服一个心理障碍,就是要对自己的程序进行破坏性测试,才可能找到潜在的问题,但去“破坏”自己完美的程序,对大多数开发人员来说也是很难接受的一件事。 + +如果上面两个问题都能克服,你还得再考虑一个问题:如果项目进度比较吃紧,作为开发人员你会压缩哪部分时间? + +正常来讲,测试时间必然要被压缩的,因为你首先得保证代码实现。这可能就导致只要项目进度一紧张,测试就被严重压缩了,进而会严重影响质量。 + +这样看来,完全由开发人员兼职测试,还是很有难度的,不仅对开发人员要求非常高,而且需要开发人员承担所有的开发和测试的压力。 + +为什么 Facebook 可以做到没有专职测试呢? + +首先 Facebook 的工程师水平确实是高于业界平均水平的,有能力同时做好开发和部分测试工作; + +其次,Facebook 的产品周期相对宽松,可以有时间完成自动化测试代码; + +Facebook 在功能发布之前,先发布新功能到内部环境,几千内部员工先测试,部分充当了测试人员角色; + +Facebook 的发布和监控也比较完善,有问题能通过监控及时发现,并且可以随时快速回滚或者发布补丁; + +最后就是用户对这类社交产品的 Bug 相对容忍度比较高,想想看如果是波音飞机上的软件能这么做吗? + +至于 Google 和 Amazon 这些公司,他们也是类似的情况: + + +大量优秀的工程师,可以同时兼任开发和测试; + +有大量的自动化测试代码覆盖; + +强大的发布和监控系统; + +时间进度比较宽松; + +用户对 Bug 容忍度较高。 + +对于不能满足上面条件的公司,有专职的测试是更有利于软件项目开发和质量保障的。 + + +大厂不设专职测试的启示 + +虽然对于大部分公司来说,要做到完全没有专职测试还不现实,但这些大厂不设专职测试的实践还是有值得借鉴和思考的地方。 + +首先,用自动化测试代替重复性的手工测试是必然趋势。随着自动化测试技术的成熟,写自动化测试代码的成本逐步降低,而自动化测试,可以极大提高测试效率,尤其是像回归测试这种需要频繁进行的。 + +其次,测试设计是软件测试人员的核心竞争力。无论是自动化测试还是手工测试,测试用例是核心。无效的测试用例,用任何方法去测试,都不会达到良好的测试目的,只有测试用例设计好了,真正做到有效高覆盖,测试才是高质量的。 + +最后,开发人员和测试人员的更多融合是一种双赢。比如说测试人员可以给开发人员提供测试用例作为测试参考,开发人员可以写更多自动化测试代码,这些方式都能有效保障产品质量。 + +总结 + +今天我带你一起分析了什么样的公司需要专职测试。同时也学习了软件测试的一些基本知识,简单来说软件测试的工作,就是发现 Bug,报告 Bug,跟踪 Bug。 + +要能及时发现 Bug,需要针对需求进行分析和测试设计,把需求具象成一个个用户操作步骤的测试用例。通过一些科学的测试方法,像等价类划分、边界值分析、探索性测试,能有效提升测试的覆盖率。 + +公司是否需要专职测试,还是取决于公司的具体情况,例如是否有大量优秀的工程师可以同时兼任开发和测试,有大量的自动化测试代码覆盖,有强大的发布和监控系统,时间进度宽松,用户对 Bug 容忍度较高。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/33测试工具:为什么不应该通过QQ微信邮件报Bug?.md b/专栏/软件工程之美/33测试工具:为什么不应该通过QQ微信邮件报Bug?.md new file mode 100644 index 0000000..ab28b69 --- /dev/null +++ b/专栏/软件工程之美/33测试工具:为什么不应该通过QQ微信邮件报Bug?.md @@ -0,0 +1,255 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 33 测试工具:为什么不应该通过QQ微信邮件报Bug? + 你好,我是宝玉。十多年前,当我还是个野路子程序员时,我在外面接私活做项目,客户在使用过程中遇到了 Bug,直接就截个图,或者是用 Word 文档整理在一起,从 QQ 或者邮件上把 Bug 信息发送给我,我收到后再修复更新上线。 + +而现在正规的软件项目已经不会再用这种原始的方式来报 Bug 了,而是会借助测试工具来帮助报告和跟踪 Bug,即使你偶尔能看到有项目还在采用原始方式报 Bug,你肯定也会觉得这样做不专业。 + +但不知道你有没有仔细想过这个问题,为什么现在不通过 QQ/ 微信 / 邮件报 Bug,又有哪些测试工具可以帮助你更好地发现、报告和跟踪软件中的 Bug 呢?今天我们来展开讨论这个问题。 + +Bug 跟踪工具 + +我想你对于 Bug 这个词一定不陌生,它是我们软件中的缺陷或错误。这个词的诞生也很有意思。 + + +1947 年 9 月 9 日,一只小飞蛾钻进了哈佛大学的一台计算机电路里,导致系统无法工作,操作员把飞蛾贴在计算机日志上,写下了“首个发现 Bug 的实际案例”。 + + + + +图片来源:WikiPedia + +虽然 Bug 的历史已经有 60 多年了,然而 Bug 跟踪工具却没有出现太久。软件项目中最早也是通过邮件、即时通讯等原始方式报告 Bug,直到 1992 年才有第一个专业的 Bug 跟踪软件GNATS。 + +在这之后才逐步有了像 Bugzilla、Jira、MantisBT 等专业的 Bug 跟踪工具。而现在,Bug 跟踪工具已经成为软件项目中必不可少的工具之一。 + +那么,Bug 跟踪工具是怎么逐步替代 QQ、邮件等方式来处理 Bug 的呢? + +为什么要使用 Bug 跟踪工具? + +我们在上一节学习了软件测试相关的理论知识,软件测试的主要工作就是发现 Bug、报告 Bug 和跟踪 Bug。测试人员发现 Bug 只是第一步,还需要报告 Bug 让开发人员可以知晓和定位,并且跟踪整个 Bug 修复的过程。 + +用 QQ 或者邮件报 Bug 的这种方式,看起来快捷简单,但是问题很多: + + +Bug 不能有效被跟踪,不知道一个 Bug 是不是已经被修复了; + +效率很低,开发人员频繁的被这样的报 Bug 的消息打断,不得不停下手头的工作去甄别 Bug; + +不能直观的了解当前项目的 Bug 状态,比如说:修复了多少,还有多少没有修复,近期 Bug 数量是增加了还是减少了。 + + +不难看出,通过 QQ 等方式报告的 Bug,都是文字配合图片等信息,很难检索和分类,而 Bug 跟踪工具,采用结构化的数据来定义 Bug,每一个 Bug 都有一些关键的信息可以对 Bug 进行分类和检索。 + +在 Bug 跟踪工具使用中,一个基本的 Bug 信息包括: + + +标题; + +描述(包括期望结果、实际结果和重现步骤等关键信息); + +优先级; + +指派人; + +状态(New、Open、 Rejected、Fixed 等); + +其他。 + + +那这样的话,就很容易的对 Bug 进行分类和检索,比如说: + + +张三想查看所有分配给他的 Bug,那只要列出所有指派人是张三的 Bug; + +想列出所有未解决的 Bug,只要列出所有状态不是 Close 或 Rejected 的 Bug 即可。 + + +这样对于开发人员来说,可以直观的看到自己有哪些 Bug 需要处理,Bug 的描述信息也可以帮助重现 Bug、快速定位到 Bug 的原因;对于项目经理或者测试人员来说,可以直观的看到哪些 Bug 还没解决,及时了解项目进展。 + +另外,我在《12 流程和规范:红绿灯不是约束,而是用来提高效率》这篇文章中提到了项目中的流程和规范,在软件项目中,要把好的实践流程化,把好的流程工具化。Bug 跟踪工具则很好的贯彻了这一点,将 Bug 的解决过程流程化。 + +你平时在 Bug 跟踪系统中看到的 Bug 状态,看起来只是一个有限的状态列表,但背后其实是一套解决 Bug 的流程。就像下面这张图表示的这样,一个 Bug 从创建到最后结束,其实是有一个完整的流程的。 + + + +通过这样的流程,开发人员就可以集中对 Bug 进行分配、按照优先级分别解决,而测试人员则可以第一时间知道 Bug 处理的状态变化,及时验证,方便跟踪整个过程。 + +使用 Bug 跟踪工具的注意事项 + +报告 Bug 的目的是为了能跟踪 Bug,以及帮助开发人员重现直到解决问题。要想做到测试和开发高效协作,这里面有一些需要注意的事项。 + +首先,所有的 Bug 都应该通过 Bug 跟踪系统管理和跟踪,不应该再通过 QQ/ 微信 / 邮件的方式跟踪 Bug。如果客户、同事通过 Bug 跟踪系统之外的其他途径反馈 Bug,应该统一提交到 Bug 跟踪系统管理跟踪起来。 + +然后,不能把多条 Bug 合并成一条,一个 Bug 创建一个独立的 Ticket。我遇到过有些测试为了省事,把几条 Bug 合并成一个 Ticket 来报,导致的问题就是,必须这几条 Bug 都修复了,这个 Ticket 才能改变状态,如果其中一个 Bug 没有验证通过,需要 Reopen 整个 Ticket。 + +再有,描述清楚如何重现 Bug 非常重要。一个 Bug 如果无法重现,也没有日志、截图等辅助信息,那是非常难以定位的,会浪费很多开发人员定位 Bug 的时间。 + +最后,不要把 Bug 跟踪系统当成讨论板用。在项目中一个常见的场景是,一个 Ticket 下面,跟讨论版一样添加了很多留言,开发认为不是 Bug,测试认为是一个 Bug,开发又觉得是产品设计没定义清楚,应该让产品经理来讲清楚,皮球踢来踢去,最后问题还没解决。 + +Bug 跟踪系统的主要功能是用来跟踪 Bug 的,不是用来讨论和扯皮的。遇到上面的情况,其中一方就应该主动一点,拉上相关人面对面讨论,当面确认清楚这个 Bug 到底是什么问题,然后马上解决掉。 + +自动化测试工具 + +除了 Bug 跟踪工具,软件测试中还有很重要的一个工具就是自动化测试工具,虽然我在《29 自动化测试:如何把 Bug 杀死在摇篮里?》中已经有了较多篇幅说明,但这里还是想继续提一下,因为我觉得,未来自动化测试会占据越来越多的比例,很多手工测试的工作会逐步被自动化测试代替。 + +像美国 Facebook、Google、Amazon 这些大厂,单纯的手工测试职位在减少,一些手工执行测试用例检查的工作外包到了人力成本更低的像中国、印度、罗马尼亚等国家,而美国本土主要招聘的都是能写自动化测试的软件测试人员,或者直接就是开发人员来写这些自动化测试代码。 + +这就意味着对于软件测试人员来说,要求越来越高了,不仅要会设计测试用例,还要能写自动化测试脚本。同时对于开发人员来说,不仅要写功能代码,还需要实现一定量的自动化测试代码。 + +这些年自动化测试工具的快速发展,也降低了自动化测试的实现难度,可以方便地搭建自动化测试环境,通过简单的脚本语言就可以模拟人工操作。 + +但很多团队还是不愿意投入在自动化测试的开发上面,宁可雇佣更多的初级测试人员手工测试。 + +其实这个问题还是要整体来看,这就像修路,如果你从一个地方到另一个地方(类比测试所有用例),偶尔走几次,那么可以不修路(手动测试),如果你未来一段时间需要频繁的在两个地方通行(反复测试),那么最好现在就开始修建高速公路(自动化测试),这样可以节约你大量通行的时间 (测试时间)。 + +当然更多的情况其实是团队不知道该如何实施自动化测试,比如说测试人员不会写程序,开发人员太忙,或者开发人员不会写测试用例,或者不知道该选择什么样的自动化测试工具。 + +对于这种情况,我的建议是: + +测试人员可以学习一些基本的编程知识,尝试自己实现自动化测试。自动化测试所需要的技术,主要是对 API 的调用,并不需要复杂的逻辑,其实学习门槛并不高,而且这种技术在工作效率、薪资、个人职业发展等方面的投资回报都是巨大的。 + +从项目的角度,应该加大对自动化测试的投入,让开发人员参与到自动化测试代码的开发中。增加自动化测试代码的覆盖,对于提升软件质量是有明显好处的,通过自动化测试可以提升测试效率,及时发现软件质量问题。 + +对于开发人员来说,如果已经有了测试用例,完成自动化测试并不复杂,这个投入其实比做一些重要性不高的功能回报更高。 + +自动化测试工具的选择,需要根据你的软件的特点,去找出来适合你软件自动化测试的几款,然后自己搭建环境试用一下。在本文后面的附录中,我会列出一些自动化测试工具供参考。 + +其他帮助发现 Bug 的测试工具 + +软件测试的一个主要工作就是发现 Bug,而要发现 Bug,就需要对软件的各个领域进行测试,比如说有性能、安全性、兼容性等领域。 + +这些不同领域的测试,要求也不一样,比如说性能测试要求能测试出软件是否有性能瓶颈,能达到多少用户的访问量,需要模拟大量用户并发访问;安全性测试则要求对软件可能存在的安全漏洞进行扫描、验证;兼容性测试则要针对不用环境不同设备,对软件进行测试,以确保不会因为环境不一致导致功能不正常。 + +这些测试要么人工很难完成,例如模拟大量用户并发访问;要么需要很深的专业知识,例如安全性测试;要么需要大量的设备和巨大的工作量,比如做兼容性测试。所以这些领域的测试,就需要借助工具的帮助才能进行测试,从而发现问题。 + +应用这些测试工具其实并不难,毕竟都有很成熟的 API,网上也有很多教程,真正需要的是去执行。另外如果想要最大化工具的价值,及时发现问题,还要考虑将测试工具的应用自动化,加入到你的持续集成流程中去。 + +以压力测试为例,你用 Jmeter 完成了压力测试脚本后,还可以考虑和 CI 集成,在每次构建时,运行一遍压力测试代码,可以在构建完成后看到直观的图表,还可以设置性能数据的阈值,如果性能指标低于阈值,会导致构建失败,这样就可以第一时间发现性能问题,缩小问题范围,并及时解决。 + +在这里,我也帮助搜集了一些相关的测试工具供参考,具体可以查看附录。 + +附录 + +Bug 跟踪工具 + +在项目管理工具那一篇文章中,我已经给你介绍了一些任务跟踪系统,比如说Jira、禅道、TAPD、云效等,都可以用来跟踪 Bug。 + +Bugzilla + +Bugzilla 是由 Mazilla 公司提供的一款开源免费的 bug 跟踪系统。这是一款历史很悠久的产品。 + +MantisBT + +MantisBT 是一个简单但功能强大的开源 bug 跟踪系统,可以通过各种插件来扩展其功能。 + +Redmine + +Redmine 是一款开源的综合性的项目管理工具,不仅可以用于 Bug 跟踪,还可以用来跟踪项目进度。 + +自动化测试工具 + +除了传统的桌面应用外,现在移动设备的普及,要测试的终端也越来越多。借助一些自动化测试工具,可以帮助简化多设备的测试。下面简单介绍几个自动化测试工具。 + +Selenium + +Selenium 是一个 Web 端的自动化测试工具,直接运行在浏览器中,用来模拟用户操作。类似的还有WebDriverIO 和 Nightwatch.js ,支持 Javascript,API 更简单更方便。 + +Appium + +Appium 是一个开源、跨平台的自动化测试工具,用于测试移动原生应用,支持 iOS, Android 系统。 + +Macaca + +Macaca 是阿里巴巴开源的一款面向多端的自动化测试工具,支持桌面端、Web、移动端、真实设备和模拟器。 + +更多自动化测试工具可以参考:Best Automation Testing Tools for 2019 (Top 10 reviews),(中文版)。 + +压力测试工具 + +很多软件在上线后,需要面对巨大的用户访问量,但如果等到上线后才发现程序性能不行,访问量一大就会导致服务崩溃,那就太晚了。所以最好是在测试阶段,就能测试出来程序的性能如何,瓶颈在哪里,然后在发布前对程序进行优化,确保能满足性能要求。 + +对程序性能的测试,就需要借助压力测试工具来模拟大量用户并发访问的场景。下面简单介绍一下几款常用的性能测试工具。 + +Apache JMeter + +JMeter 是一款开源的压力测试工具,纯 Java 应用程序。 + +LoadRunner + +LoadRunner 是惠普旗下的一款商业自动负载测试工具,可以通过录制的方式制作测试脚本,上手容易功能强大,可以方便的监控和分析性能测试结果。 + +阿里云性能测试 PTS + +阿里云性能测试 PTS 是基于云端的压力测试服务,可以模拟从全国各地域运营商网络发起的流量,真实地反映使用情况,生成有价值的性能测试报告。 + +WebPageTest + +WebPageTest 是一个可以用来测试和分析网页性能的在线工具,支持不同浏览器,支持 API。可参考《WebPagetest H5 性能测试工具入门详解》。 + +更多性能测试工具介绍可以参考:《10 大主流压力 / 负载 / 性能测试工具推荐》。 + +安全性测试工具 + +软件的安全性是非常重要的指标,有时候开发人员缺乏安全意识,就可能会导致程序存在安全漏洞。安全领域也是开发和测试之外的一个技术领域,中小公司一般不会有自己专业的安全团队,就需要借助一些安全性测试工具来帮助对软件进行安全性检测。 + +HP Fortify On Demand + +Fortify On Demand 是惠普旗下的一款安全检测工具,可以通过分析源代码、二进制程序或者应用程序 URL 检测程序安全漏洞。 + +Sqlmap + +Sqlmap是一款开源免费的检测 SQL 注入的工具。 + +IBM Application Security APPScan + +APPScan 是 IBM 旗下的一款漏洞扫描工具,支持网站和移动 App。 + +更多安全性测试工具介绍可以参考:11 款常用的安全测试工具, 安全测试工具篇(开源 & 商业), 最受欢迎的软件安全性测试工具有哪些?。 + +浏览器兼容性测试工具 + +网站开发最苦恼的问题之一就是浏览器兼容问题,不仅要兼容 Chrome、IE/Edge、Firefox 三大主流浏览器,还得考虑桌面设备和移动设备上的不同表现。如果人工对所有浏览器做兼容性测试,工作量比较大。好在也有一些不错的工具可以帮助做兼容性测试。 + +Browsera + +Browsera 可以对不同浏览器下的布局提供报告,包括截图和 Javascript 错误。 + +Browslering + +Browslering 可以针对不同浏览器进行测试,它在虚拟机中运行真实桌面浏览器,还可以人工进行交互。 + +更多浏览器兼容性测试工具可参考《10 个免费的顶级跨浏览器测试工具》 + +测试用例管理工具 + +我们在上一篇里面已经学习了,设计测试用例是软件测试很重要的工作,有专业的工具帮助管理测试用例,也可以起到事半功倍的效果。 + +TestRail + +TestRail 是 TestRail 是一个专注于管理测试用例的工具,可以用它来创建测试用例和用例集,跟踪测试用例的执行和生成报告。 + +飞蛾 + +飞蛾 是 Coding 旗下的测试管理工具,对中文支持好,界面美观。 + +更多测试用例管理工具可以参考:《有哪些比较好的测试用例管理工具?》 + +总结 + +今天,我带你一起学习了软件测试工具的相关知识。软件测试,主要工作就是发现 Bug、报告 Bug 和跟踪 Bug。软件测试工具,也是围绕这三方面来帮助我们提高效率的。 + +Bug 跟踪工具,不仅可以方便的报告 Bug 和跟踪 Bug,更可以帮助开发人员将 Bug 的解决过程流程化。 + +自动化测试工具是发展趋势,未来自动化测试会占据越来越多的比例,很多手工测试的工作会逐步被自动化测试代替。 + +除了 Bug 跟踪工具和自动化测试工具,软件测试中还有性能测试工具、安全性测试工具、兼容性测试工具等,这些工具都可以更好的帮我们发现软件中的质量问题。 + +如果想要最大化工具的价值,及时发现问题,还要考虑将测试工具的应用自动化,加入到你的持续集成流程中去。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/34账号密码泄露成灾,应该怎样预防?.md b/专栏/软件工程之美/34账号密码泄露成灾,应该怎样预防?.md new file mode 100644 index 0000000..af958a8 --- /dev/null +++ b/专栏/软件工程之美/34账号密码泄露成灾,应该怎样预防?.md @@ -0,0 +1,207 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 34 账号密码泄露成灾,应该怎样预防? + 你好,我是宝玉。我们日常总能看到各种与黑客和网络安全相关的新闻,而这其中大部分安全问题都和软件程序有关系。比如说像 CSDN 数据库泄露事件、携程泄露用户银行卡信息事件、有些电商网站用户可以篡改支付购买金额等等。 + +在软件项目开发时,安全是一个很容易被忽略的问题,但又可能会造成严重损失。所以我们在软件开发时有必要对安全问题引起重视,防患未然,构建安全软件。 + +今天,我将带你了解一下软件开发中的安全问题,学习如何构建安全的软件,以及出现了安全问题之后该怎么办。 + +安全问题本质是技术风险 + +如果你还记得《15 风险管理:不能盲目乐观,凡事都应该有 B 计划》这篇文章中的内容,我在其中提到,风险是指不确定的事件,一旦发生,将会造成消极的影响。 + +安全问题,本质上也是一种技术风险,没发生问题的时候一切都好,一旦发生就会有严重的影响。在对安全问题的应对上,你也可以借鉴对风险管理的方法来改进软件的安全问题,也就是风险识别、风险量化、应对计划和风险监控。 + +在做风险管理时,首先要做的就是识别风险和对风险量化,对于安全问题,你也可以先思考一下:软件项目中安全问题的主要来源是什么?搞清楚安全问题的来源,以及造成的后果,你就可以对软件中导致安全问题的情况有一个基本的识别和量化。 + +软件中的安全问题来源主要可以分为以下三大类。 + + +第一类:恶意输入 + + +很多我们熟知的软件安全问题都属于此类型,就是黑客通过恶意输入,然后绕过软件限制对系统进行攻击和破坏。 + +像 SQL 注入,就是黑客把 SQL 命令输入到软件的输入框或网页的 URL 查询参数,欺骗服务器,执行恶意的 SQL 命令。这样可以绕过密码验证,登录管理员账号,或者删除数据库数据,甚至控制服务器。 + +还有像 XSS 攻击,将恶意代码通过外部参数或者用户输入的方式植入网页中,获取用户的 Cookie 等敏感信息、盗用管理员权限,甚至非法转账。 + +这类问题都是由于恶意输入导致的,应对恶意输入的问题,最简单有效的方式就是对用户输入的数据,做严格的校验,格式化。 + + +第二类:假冒身份 + + +很多程序对于用户身份的校验比较弱,可能会导致黑客假冒用户身份做出超越权限的事情。 + +比如说,我见过有的网站,把后台入口隐藏起来,而没有做权限控制,导致黑客猜到地址后就可以进入后台操作。 + +还有的游戏后台不做验证,直接接收传入的数据,导致可以伪造游戏用户发送数据,破坏游戏平衡。 + +如果对用户的身份不做严格的验证,很可能就会导致假冒身份的安全问题。应对策略就是要对用户的身份做验证,尤其是涉及敏感权限的操作,甚至要做两重验证。 + + +第三类:数据泄露 + + +很多软件都储存了用户的敏感信息,比如用户帐号密码信用卡记录,或者服务器的敏感信息,比如数据库连接字符串、登录帐号密码,而这些数据是有被泄露风险的。 + +一些软件会把服务器上的敏感信息打包在程序中,而程序可能会被反编译导致敏感数据泄露。携程泄露用户银行卡信息事件中,就是因为把用户信用卡信息记录在日志中,日志泄露导致用户信用卡信息也被泄露,造成盗刷等严重问题。 + +还有 CSDN,对用户密码明文存储到数据库中,数据库泄露后,用户密码也跟着一起泄露了,而大多数用户习惯于在不同的网站也用相同的密码,导致在其他网站的密码也一起泄露了。 + +对于软件来说, 我们不能假设数据存储是安全的,而是要考虑到数据是有泄露的可能,提前做好预防措施,对敏感数据进行加密。 + +在了解了这些常见的安全问题来源和可能带来的后果之后,我们在软件开发时,就可以对薄弱环节、重点问题进行提前预防,在开发时就考虑到可能的安全漏洞,做出科学的应对方案。 + +如何预防软件中的安全问题? + +预防软件中的安全问题,也可以参考对风险管理的策略。在风险管理中,对风险识别和量化后,接下来就是要制定应对计划了。 + +很多开发人员觉得安全问题,只要在软件开发完成之后,测试阶段做一个安全测试就可以了,但这样做等于把安全问题留到了最后环节,是很难达到对安全问题进行高质量管控的。 + +一方面,对于安全测试来说,很难覆盖到所有可能存在的场景,可能会出现疏漏,导致安全漏洞被利用。另一方面,如果测试阶段发现安全问题,可能需要修改大量代码,甚至于要重新设计,这时候成本就太高了。 + +所以应对安全问题,最好的方式就是在整个生命周期中都做到重视安全问题,各个阶段都考虑到安全方面的问题,才能真正做到防患于未然,构建出安全的软件。 + +那么在软件开发的各个阶段,都需要如何考虑好安全方面的问题呢? + + +需求阶段 + + +需求是软件项目的源头,在确定需求,做产品设计的时候,不仅要考虑到功能上的需求,还要同时考虑到安全方面的要求。这样当你在架构设计的时候,就不会轻易遗漏安全方面的架构设计。 + +尤其是对于一些外包项目,如果在需求中没有提出安全需求,大概率外包公司是不会帮你考虑这些需求的。 + +需求阶段,涉及用户输入的内容,需要考虑到可能的恶意输入,做出针对性预防措施;对于涉及用户权限的,要求有身份的验证,一些对安全要求极高的,可以在需求上就要求做双重验证;对于有敏感数据的,可以在需求上就要求对数据进行加密。 + +举个简单例子,比如说用户登录功能,如果让你提出安全方面的需求,你会考虑到哪些需求?这里我简单列几个功能参考: + + +登录网页使用 Https 或者在传输密码时加密; + +增加图形校验码,避免恶意攻击; + +密码失败次数过多,应该锁定用户一段时间; + +记录用户登录 IP。 + + +当你在需求阶段就提出了安全性的需求,设计、实现和测试时自然不会遗漏掉安全方面的内容,从源头上就让大家有了安全方面的意识。 + + +设计阶段 + + +在做设计架构时,最重要的事就是要把安全加入到设计目标,有了安全方面的设计目标,自然能找到很多安全相关的解决方案。 + +为了保障在设计时就考虑好安全方面的问题,在做架构设计方案的评审时,也需要增加安全方面的评审,确保有安全方面的考虑,确保技术方案切实可行。 + +现在架构设计领域,也有了一些业界公认的好的安全相关的设计原则,比如说攻击面最小化、权限最小化、纵深防御等。 + + +攻击面最小化 + + +攻击面就是指程序被用户直接访问到的部分,比如 API、网站等,这些暴露给用户的地方也是最可能被黑客攻击的地方。 + +暴露的面越多则风险越高,攻击面最小化的设计原则,就是说尽量减少暴露黑客可能发现并试图利用的攻击面数量。 + +举例来说,你的数据库应该关闭外网访问,避免黑客直接攻击数据库导致数据泄露。还有像对于一些复杂的多网站业务系统,实行单点认证,就可以让所有业务都在一个地方登录,你可以在这一个地方做到足够安全,这样所有网站的登录都是相对安全的。 + + +权限最小化 + + +权限最小化的设计原则就是对于系统的用户、文件访问、进程运行等,都只给予其能拥有的最小权限,这样可以保证一个应用程序或者网站被攻击、破解,能将损害降到最低。 + +举例来说,以前在部署 Asp.Net 程序的时候,运行 Asp.Net 的程序是单独的一个用户,这个用户所拥有的权限是只能运行程序所在目录,不能超出其目录范围,这样即使用户上传了恶意木马文件,那么也只能控制这一个目录,避免了进一步的损失。 + + +纵深防御 + + +纵深防御的设计原则,指的是从不同的维度去实施安全保护措施,从而缓解被攻击的风险。纵深防御并不是同一个安全方案要做两遍或多遍,而是要从不同的层面、不同的角度对系统做出整体的解决方案。 + +这里我给你举一个电商网站的例子(摘自:代码未写,漏洞已出—架构和设计的安全)。 + + +国内中小电商,一半以上在早年都犯过这个错误,现在基本都修复了。电商的交易和支付系统之间流程是这样,一个人过来说老板我要买一台冰箱,多少钱?两千。OK,你把钱付给支付系统。因为支付请求也是在用户侧的浏览器里提交的。这个过程对用户是不可见的,但攻击者实际上可以修改这个数据。攻击者可以修改浏览器提交的数据,本来交易系统让他提交 2000 元,攻击者改为提交 1 元,然后支付系统就返回 OK,说我收到钱了。这个 OK 到交易系统那里,交易系统一看支付成功了,那就安排发货,1 元钱就把冰箱买到了。 + + +你看,单独从支付系统和交易系统来看,设计上都没有问题,都对数据输入做了校验,但问题是没有站在一个系统的整体角度去考虑,没有考虑到不仅要校验交易有没有成功,还要校验交易的金额是不是匹配。 + +当然解决方案其实也很简单: + + +不要只反馈是否 OK,同时也把支付的金额和 OK 一起返回过去。是支付 2000 元 OK 还是 1 元 OK。这就解决了问题,现在的电商都改成这个设计了。 + + +通过这样一些好的安全设计原则,就可以在设计阶段把很多安全问题预防好。 + +开发阶段 + +只是设计阶段做好安全相关的设计还不好,很多安全问题其实都是编码阶段时,没有好的编码习惯、没有良好的安全意识导致的。 + +对于开发阶段的安全问题预防,需要从以下几个方面入手。 + + +编码规范中加入安全相关内容 + + +对于用户输入的数据,需要有校验,防止恶意输入;对于涉及权限的操作,要检查用户权限;对于敏感数据要进行加密处理;对于用户的操作,要有日志记录;不能在日志中记录敏感信息等等。 + + +要有代码审查 + + +代码审查其实在我们专栏提过很多次,这也是预防安全问题一个行之有效的手段,通过代码审查,及时发现代码中的安全问题。 + + +增加安全相关的自动化测试 + + +现在有些安全工具,可以帮助对代码做安全检查,甚至可以和 CI 集成,如果增加相应的自动化安全测试代码,也可以第一时间对代码中的安全问题进行反馈。 + +测试阶段 + +在测试阶段,除了功能测试以外,增加对安全性方面的测试。除了增加相应的测试用例,也可以借助一些安全测试工具(可参考上一篇“软件测试工具”这篇文章)来进行测试。 + +上线维护 + +上线部署时,不部署源代码,只对编译后程序部署;删除 Debug 文件。 + +对服务器进行安全设置,比如说严格限制端口,只保留必须的端口;只对少数服务器开发外放服务;开启操作日志;对访问目录设置最小的权限。 + +通过对整个软件生命周期都做好安全方面的考虑,并落实到位,才能真正保证好软件的安全。 + +如果真的出现安全问题怎么办? + +是不是我们在整个软件生命周期都做好安全方面的考虑,也落实到位,就完全不会有安全问题了? + +安全问题就像程序的 Bug 一样,没有谁能保证绝对的安全,就像风险管理的最后一步风险监控那样,我们必须做好 Plan B,万一出现安全问题,马上应对,将损失降到最低。 + +首先,要设立应急的流程。当出现安全问题了,根据流程,知道该找谁,应该怎么去第一时间恢复生产,避免进一步损失。 + +其次,要分析程序的漏洞在哪里。通过分析日志,找出漏洞在哪里,才能针对性去修补漏洞。 + +最后,要总结原因。从错误中吸取教训,看问题是在哪个环节导致的,必要的话,就改进开发流程,避免类似的安全问题再次发生。 + +总结 + +今天我带你一起学习了如何构建安全的软件,软件的安全问题本质也是一种技术风险,可以借用风险管理的知识来帮助构建高安全性的软件。 + +软件安全问题主要来源是:恶意输入、假冒身份和数据泄露,要注意对输入数据的校验、对用户身份的校验和对敏感数据的加密。 + +构建高安全性软件,最好的方式就是在整个生命周期中都做到重视安全问题,各个阶段都考虑到安全方面的问题,才能真正做到防患于未然,构建出安全的软件。 + +安全问题就像程序的 Bug 一样,不能绝对避免,同时也要做好应对措施,在出现安全问题后,将损失降到最低,并且避免以后发生类似问题。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/35版本发布:软件上线只是新的开始.md b/专栏/软件工程之美/35版本发布:软件上线只是新的开始.md new file mode 100644 index 0000000..d62f81b --- /dev/null +++ b/专栏/软件工程之美/35版本发布:软件上线只是新的开始.md @@ -0,0 +1,165 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 35 版本发布:软件上线只是新的开始 + 你好,我是宝玉。上一章我们学习了软件测试篇,今天,我们将从版本发布这个话题开始,进入到运行维护篇的学习。 + +说到版本发布,对于很多开发人员来说,觉得是一件很简单的事情,就是将程序编译打包部署,但实际发布的时候,却经常出现发布错版本的问题,或者是发布前修改了一点代码导致上线出现 Bug 的情况发生。 + +而版本发布对于很多项目管理者来说,又是一个很纠结的事情,觉得还有很多功能没完成,很多 Bug 还没改完,害怕用户负面评价,结果时间一拖再拖,迟迟无法上线。 + +所以今天我将带你一起学习一下如何做好版本发布,保障好发布产品的质量。 + +关于软件版本 + +在讨论这个话题之前,我们需要了解“版本”的含义。也许你已经知道版本的含义,但是这里还是有必要明确一下,因为在不同的语境下,版本的含义也有所不同。比如产品经理会对开发人员说:“这个功能我们会放到下个版本中实现”,开发人员会对测试人员说:“这个 Bug 在昨天的版本中已经修复了”。 + +这里产品经理说的“版本”是指特定功能集合,开发人员说的“版本”是指某一次程序的构建结果。也就是说对软件版本来说,包含两部分含义,一部分代表特定功能集合,一部分代表某一次特定的代码构建结果。 + +为了明确标识软件版本,需要对版本进行编号。目前业界在软件版本的命名上,通常会采用以下方式: + + +主版本号 . 子版本号.[. 修正版本号.[构建版本号]] + + +比如说:1.2.1、2.0、3.0.1 build-123。 + +其中主版本号和子版本号用来标识功能变化,小的功能变化增加子版本号,大的功能变化增加主版本号。修正版本号则表示功能不变化的情况下修复 Bug,而构建版本号表示一次新的构建,这个通常由编译程序自动生成。 + +团队中对版本有了清晰的定义和良好的版本编号,在讨论版本时,你就可以根据版本号清楚地知道应该有哪些功能,属于哪一次的构建结果。在修复 Bug 或者增加功能时,开发人员也能清楚地知道代码应该加入哪个版本。在验证 Bug 时,测试人员就可以知道应该在哪个版本中验证 Bug 有没有被修复。 + +版本发布前,做好版本发布的规划 + +回到前面的问题,为什么有的项目管理者会在发布前感觉没准备好,害怕上线发布呢?根源上,他们还是对于功能和质量没有信心,担心发布后获得负面的评价。 + +而实际上,并不代表你需要完成所有的功能,或者没有任何 Bug,有一个完美的版本才能上线。毕竟追求完美是没有止境的,这世界上也不存在完美的软件,很多著名的软件,比如 Windows、Office、iOS 都免不了在发布后还要打补丁。 + +这里的关键在于,要在用户(或客户)的心理预期和你软件的实际情况之间,达到一种平衡,让软件的功能和质量,满足好用户的预期。 + +要合理管理好用户的预期,达到好的发布效果,就需要在版本发布前先做好版本发布的规划。 + +那么,版本的发布规划,是指规划哪些内容呢? + +首先是规划好要发布的功能。在发布前,搞清楚哪些是用户必须要有的功能,哪些是用户可以没有的功能。对于必须要有的功能,那么要保证软件中有这个功能才能发布,对于不是必需的功能,可以以后再逐步完善。 + +有一个经典的案例就是第一代的 iPhone,复制粘贴的功能都没有,但是在用户界面和触屏设计上做的非常好,一样取得了极大的成功。 + +然后是定义好发布的质量标准。在发布前,搞清楚你的用户对质量的容忍度如何,对哪些功能的质量要求高,对哪些功能的质量要求没那么高。对于那些用户在意的功能,要具有较高的发布标准,反之,则可以有较低的质量标准。 + +举例来说,像微博这种社交类应用软件,界面布局存在一点问题,用户是可以接受的,但是如果撰写内容的时候,因程序异常导致辛辛苦苦写的内容丢失,用户是比较难以忍受的。 + +再有就是要设计好发布的策略。考虑好是直接发布给所有用户?还是先让一部分用户试用?比如说可以先让内部用户使用,内部用户对软件质量问题容忍度是很高的,还可以帮助发现很多问题。 + +让一部分用户使用 Beta 版也是一个好的发布策略,当用户知道你的软件还是 Beta 版的时候,要求会比较低一点,可以接受一些不那么严重的 Bug。 还有就是采用灰度测试的发布策略,让一小部分用户先用新功能,如果没发现什么问题,再继续扩大使用的用户规模,如果有问题,也只是影响少量用户。 + +像 Gmail 在最初的几年,一直是 Beta 版本测试。还有像苹果的 iOS,用户也可以选择安装最新的 Beta 版本,可以先体验新功能,但是必须忍受系统的不稳定。 + +最后,就是有一个综合性的版本发布计划。在确定了要发布的功能、定义好了质量标准、设计好了发布策略,就可以制定一个综合性的版本发布计划了,确定好发布的时间点。 + +这个发布计划,不只是项目内部成员,还需要和项目之外利益相关方,比如客户、市场运营人员,大家一起确定最终的发布计划。 + +在对版本的发布做好规划后,就不用再纠结于该不该发布,该什么时候发布的问题。 + +有功能没完成没关系,关键要看这个功能是不是必须要有;有 Bug 没有修复完成,是不是影响发布,要看这些 Bug 是不是影响发布的质量标准;还可以采用一些像 Beta 版、小规模用户试用的发布策略,降低用户对功能和质量的预期。 + +规范好发布流程,保障发布质量 + +在规划好发布的版本后,要发布版本似乎是一件很简单的事,就是将源代码编码、部署。 + +但发布版本,可能并不是像你想的那么容易,这其中有几个需要注意的问题。 + +首先是必须保证要编译部署的是正确的版本。虽然一般来说,开发人员不会犯这样的错误,但是如果发布了错误的版本,后果可能很严重,所以要引起足够重视。比如说当年拼多多造成了几千万损失的薅羊毛事件,就是错误发布了一个测试用的无门槛券,导致被大量滥用。 + +然后要保证版本稳定可靠。如果你有开发经验的话,应该知道开发软件,一个常识就是每一次对代码的修改,都可能导致新的 Bug 产生。如果你的代码库在发布之前还一直在增加新的功能或者是不停地修复 Bug,那么质量是难以稳定下来的。 + +再就是要在发布失败后能回滚。没有谁能保证程序发布后没有严重问题,所以最保险的办法就是要在部署后,如果发现发布的版本出现严重问题,就应该对程序进行回滚操作,恢复到部署之前的状态。即使有些不可逆的升级,也需要事先做好应对措施,比如发布公告,停止服务,尽快修复。 + +针对这些问题,已经有些好的实践,比如说代码冻结、Bug 分级、回归测试等可以降低发布风险,保障发布产品的质量。在《12 流程和规范:红绿灯不是约束,而是用来提高效率》这篇文章中,我也提到了:流程和规范能将好的实践标准化流程化,让大家可以共享经验。所以在版本发布上,我们也可以制定合理的流程,来应用这些好的实践,保证发布的质量。 + +那么一般大厂都是什么样的发布流程呢?下面这个流程可以作为一个参考。 + + +在发布之前要做代码冻结。 + + +什么是代码冻结呢?就是在发布之前,对于要发布的版本,在源代码管理工具中,专门创建一个 release 分支,然后对于这个分支的代码,冻结功能的修改,不接受新功能的增加,甚至重要性不高的 Bug 都不修改,只修复重要的 Bug。 + +由于严格的控制代码的修改,这样可以让版本的质量逐步趋于稳定。 + + +对代码冻结后发现的 Bug 要分级 + + +在代码冻结后,可能还存在一些 Bug,测试的过程中也会新增一些 Bug。代码冻结的原则就是尽可能减少代码的修改,避免引起不稳定。所以对于这些 Bug,要有一个简单的分级:是否在发布前修改,还是留在发布后再修改。 + +至于如何对一个 Bug 分级,这需要项目负责人和产品负责人一起确认。 + + +每次修复 Bug 后,发布新的候选版本 + + +进入代码冻结后,开发人员还需要对一些 Bug 进行修复,每一次修复完 Bug 后,就要生成一个新的候选发布版本,比如说 1.1 RC1、1.1 RC2。 + +关于生成发布版本,现在比较流行的做法是和持续集成系统整合,完全自动化。也就是在自动化测试通过之后,会自动构建,生成各个环境的发布版本。这样好处是,可以避免人为失误导致的错误,另外程序的配置管理做好了的话,只要测试环境的版本在测试环境测试没问题,那么就可以认为在生产环境的版本也是正常的。 + +自动化构建,生成发布版本并不复杂,各个语言都有成熟的方案,如果你还不了解的话,可以通过搜索引擎搜索关键字:“[对应平台] 自动打包”,例如搜索“iOS 自动打包”、“iOS build automation”这样的关键字。 + +其中稍微有点麻烦的就是如何应用不同环境下的不同配置,比如说测试环境连测试环境服务器,生产环境连生产环境服务器。有关程序配置管理部分,可以参考这篇文章:大型项目程序配置管理演化之路 + + +每次部署新的候选发布版本后,要做回归测试 + + +在每次开发人员部署新的候选发布版本到测试环境后,还需要做一次回归测试。也就是说在 Bug 修复完,对主要流程要重新测试一遍,同时还要对之前确认过的 Bug 再确认一遍,以确保 Bug 确实修复了,并且没有引入新的 Bug。 + +如果当前候选发布版本达到版本发布的质量标准后,就可以准备发布了。 + + +申请上线发布 + + +上线发布是一件很严谨的事,所以在正式上线发布前,通常还需要有一个申请和审批的流程。审批的主要目的是要有人或者有部门统筹对所有的上线发布有一个全面的了解和控制,避免上线过于随意导致问题,避免和其他部门的上线冲突。 + + +部署发布 + + +如果已经实现了自动化,部署发布应该是非常简单的一步。如果还没有自动化部署发布,也需要事先将详细的操作步骤写下来,避免部署发布时发生纰漏,这样在实际部署发布时,按照事先写好的步骤操作就不容易出现错误。 + + +上线后的测试 + + +项目上线后,测试人员需要马上对已经上线的版本做一个主要功能的测试,以确保线上运行正常。如果做好了数据监控,还同时要对一些关键数据进行监控,例如服务器 CPU 利用率、内存占用率、服务出错率等数据。 + +如果万一发现版本上线后出现问题,需要考虑按照事先准备好的回滚方案进行回滚操作,尽量将损失降到最低。通常不到万不得已,不建议马上对问题打补丁进行修复。因为哪怕很小的代码修改,都可能会引入新的 Bug。而重新做一遍回归测试,耗时会比较长。 + +以上就是版本发布的一个常见流程,你也可以基于这个流程制定适合你项目的流程,让你的版本发布更加稳定可靠。 + +软件上线只是新的开始 + +当你的软件上线后,这不代表你的项目就结束了,可能这才只是新的开始。 + +用户在使用你的产品的时候,可能会遇到一些 Bug 或者是有一些建议,所以需要给用户反馈的渠道,让用户可以有途径对于 Bug 或者功能去反馈。通过收集用户的反馈,可以进一步完善你的软件产品。 + +只是靠用户主动反馈问题还是不够的,需要主动的对发布的版本进行监控,比如说要收集 App Crash 的 Log、监控服务器资源占用情况、监控 API 出错的比例、监控网页响应的速度等数据。当发现数据异常时,很可能说明发布的版本是有问题的,需要及时的应对,回滚版本或者发布新的更新补丁。 + +有关线上监控和报警的内容,将会在后续我们课程《38 日志管理:如何借助工具快速发现和定位产品问题 ?》中带你继续学习。 + +不管怎么样,软件成功上线了都是一件值得祝贺的事情,同时也是时候回顾总结一下整个项目过程了,关于这一点,我也会在后续专栏文章《39 项目总结:做好项目复盘,把经验变成能力》中跟你一起探讨如何做好项目总结复盘,把经验变成能力。 + +总结 + +今天带你一起学习了版本发布的相关知识。做好版本发布,关键在于版本发布前做好版本发布的规划,以及采用一个科学的发布流程。 + +版本规划,其实就是通过合理的规划,尽可能的让软件的功能和质量,满足好用户的预期。所以一方面要尽可能提供应有的功能和保证质量,另一方面也可以通过合理的发布策略,例如 beta 测试,降低用户预期。 + +通过规范的发布流程,可以确保要发布的版本正确以及发布质量的稳定。流程的关键在于发布前要对代码冻结,避免发布前频繁修改代码引入新的 Bug,同时在每次修复 Bug 后,要做回归测试保证 Bug 被修复以及没有引入新的 Bug,上线后还要对线上版本再一次测试,确保没有问题。整个流程中一些手工部署发布的操作应该尽可能自动化。 + +最后,软件上线只是新的开始,还需要收集用户的反馈,对线上服务进行监控和预警,对整个版本的开发过程进行总结回顾。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/36DevOps工程师到底要做什么事情?.md b/专栏/软件工程之美/36DevOps工程师到底要做什么事情?.md new file mode 100644 index 0000000..88909a0 --- /dev/null +++ b/专栏/软件工程之美/36DevOps工程师到底要做什么事情?.md @@ -0,0 +1,163 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 36 DevOps工程师到底要做什么事情? + 你好,我是宝玉。这些年,有关 DevOps 的概念很火,大家都在讨论 DevOps,有人说 DevOps 就是自动化运维,有人说 DevOps 是流程和管理,还有人说 DevOps 是一种文化。以前的运维工程师也纷纷变成了 DevOps 工程师。 + +今天,我将带你一起了解一下,究竟什么是 DevOps?DevOps 到底要做什么事情? + +传统的运维模式以及面临的挑战 + +在传统的瀑布模型开发中,软件生命周期中的运行维护这部分工作通常是交给运维工程师来完成的。 + +当开发人员完成编码,测试人员测试验收通过后,到了要发布的时候,就会将程序交给运维人员部署发布到生产环境。 + + + +(图片来源:The Product Managers’ Guide to Continuous Delivery and DevOps) + +除了程序的部署更新,传统运维工程师最重要的职责就是保障线上服务的稳定运行。对服务器 24 小时监控,有意外情况发生时需要及时处理和解决。 + +除此之外,还有日常的更新维护,比如说安装升级操作系统、安装更新应用软件,更新数据库、配置文件等。 + +早些年这种运维模式运行的很好,但随着这些年互联网发展,有两个主要的因素对传统的运维模式产生了很大挑战。 + +第一,服务器规模快速增长和虚拟化技术的高速发展。 + +早些年,一般的企业服务器数量都不会太多,运维工作以手工为主,自动化为辅。几个人几十台服务器,即使用手动方式管理,也不会太困难。但随着这些年技术的快速发展,大型互联网公司的服务器数量越来越庞大,而中小公司都开始往云服务上迁移,基于 Docker 这样的虚拟化技术来搭建在线服务的基础架构。 + +服务器规模的增加和虚拟化技术的使用,就意味着以前的手动方式或者半自动的方式难以为继,需要更多的自动化和基于容器技术或者相关工具的二次开发。对于运维的工作来说,运维人员也需要更多的开发能力。 + +第二,高频的部署发布。 + +传统的软件部署频率不高,一般几天甚至几个月才部署发布一次,同时每一次的部署发布,也可能会导致系统的不稳定。而敏捷开发和持续交付的概念兴起后,更新的频率越来越高,每周甚至每天都会有若干次的更新部署。 + +高频部署带来的挑战,首先就是会引起开发和运维之间的冲突,因为开发想要快速更新部署,而对于运维来说,每次更新部署会导致系统不稳定,最好是不更新,可以让系统维持在稳定的状态。另一个挑战就是想要快速的部署发布,也意味着运维要有更高的自动化能力。 + +为了解决这些挑战,DevOps 出现了,它帮助解决开发和运维之间的沟通协作问题,提升运维开发和自动化能力。 + +什么是 DevOps? + +DevOps 可以理解为一种开发(Development)和运维(Operations)一起紧密协作的工作方式,从而可以更快更可靠的构建、测试和发布软件。 + +DevOps 并不意味着开发一定要懂运维技术,运维要懂开发技术,而是说两个工种要更紧密的协作,有共同的目标:更快更可靠的构建、测试和发布软件。 + +这就意味着,对于运维来说,不再抵触开发的频繁更新部署,会帮助搭建自动化部署平台,提供自动化部署工具;对于开发来说,不再认为运维的工作和开发没关系,开发人员会邀请运维人员参与架构设计,帮助运维实现自动化脚本开发。 + +那么当你的团队采用 DevOps 的方式工作的话,会带来哪些好处呢? + + +整个软件的构建、测试和发布过程高度自动化 + + +DevOps 一个很重要的基础就是自动化,通过对自动化的应用,是最简单有效的打破开发和运维之间壁垒的方式。 + +因为应用自动化后,对于运维人员来说,自动化的交付流程,减少了繁重的手工操作,自动化测试可以有效对产品质量提供很好的保障。对于开发人员来说,可以方便高频率地进行部署。 + +如果你的团队还没有开始实施自动化,可以先从持续交付开始,具体可以参考我们专栏在《26 持续交付:如何做到随时发布新版本到生产环境?》中的介绍。 + + +信息更加透明和易于测量 + + +在传统的开发和运维合作模式中,开发和运维之间的信息不是那么的透明。对于开发来说,不了解程序在服务器上运行的情况,对于运维来说,程序就是个黑盒子,无法对程序内部进行监控,出现问题只能重启或者回滚。 + +当采用 DevOps 的工作方式,信息更加透明,通过日志和工具,数据也可以被更好测量。比如说: + +可以直观看到开发到部署需要多少时间,哪个环节可以改进? + +当前服务运行情况如何,每分钟访问数多少,API 出错率多少? + +当前用户数多少,有多少新增用户? + +这些数据,不仅可以帮助运维更好地预警,或者是帮助开发更好地优化程序,还可以帮助业务团队更好地了解服务的运营情况。 + + +培养跨职能协作的文化 + + +DevOps 的核心文化是不同职能工种之间的紧密协作的文化。其实不仅限于开发和运维之间,就像我们之前在《32 软件测试:什么样的公司需要专职测试?》)中讨论的,开发和测试之间也一样离不开紧密的协作。 + +如果你的团队是在真正地实践 DevOps 的工作方式,就会积极拥抱这样的跨职能协作的文化,在日常工作中包容错误、对事不对人,能对项目的开发流程持续改进,鼓励创新。 + +有关 DevOps,也有一些不错的文章,有兴趣的话可以进一步阅读:DevOps 前世今生 | mPaaS 线上直播 CodeHub #1 回顾,孙宇聪:来自 Google 的 DevOps 理念及实践和关于 DevOps ,咱们聊的可能不是一回事。 + +DevOps 看起来很美好,也许你迫不及待想去实施,但 DevOps 这种工作方式的建立,也不是一下子能完成的,上面提到的这些带来的好处,相应的也是你要去遵守的 DevOps 原则:自动化、信息透明可测量、构建协作文化。 + +这也意味着: + + +你需要去构建自动化部署的系统,从构建、测试到部署实现高度的自动化; + +建立数据监控的系统,让信息透明可测量; + +最后要形成跨职能协作的文化。 + + +看起来很难,但也不需要有压力,因为要实践 DevOps,不需要你改变开发模式,瀑布模型或者敏捷开发都可以实施;不需要靠管理层推动;也不一定要让开发人员去学习运维知识或者运维去学习开发知识。而是通过了解 DevOps 的核心价值,也就是跨职能之间紧密协作,更快更可靠地构建、测试和发布软件,一点一点地做出改变。 + +DevOps 工程师到底要做什么事情? + +在了解了什么是 DevOps 后,我们再来看看基于 DevOps 的实践,DevOps 工程师到底要做什么事情? + +对于 DevOps 工程师的定义其实是有争议的,因为有人认为 DevOps 是一种团队工作的方式,而不是一种职业。也有人认为 DevOps 工程师是一种职位,用来帮助团队形成 DevOps 工作方式的职位。 + +在这里我们没必要陷入这种争论,而是从 DevOps 实践的角度,来看看 DevOps 工程师,要做什么事情,可以帮助团队来实践 DevOps 的工作方式。至于是 Dev 来做这些事情,还是 Ops 来做这些事情,还是一起协作来做这些事情,并不是最重要的。 + +首先,DevOps 工程师要帮助团队建立基于持续集成和持续交付工作流程。 + +关于持续集成和持续交付,不仅仅是工具的使用,同时还是基于工具之上的一整套的交付工作流程。 + +这套工作流程已经是业界公认的好的实践,但在很多中小团队普及率还不高,主要的难点之一是搭建比较复杂,可能还涉及二次开发;另一个是不知道该怎么建立这样的流程。 + +对于这样的工具和流程的建设,最初的时候,就是需要有专门的人,专门的时间去建立,也是 DevOps 工程师首先要去解决的问题。 + +其次,要建立一套基于日志的监控报警的系统,以及故障响应的流程。 + +对于线上系统,应急响应非常重要,要在故障发生后,第一时间作出响应,及时恢复生产,避免更大损失。而要做到这一点,同样离不开工具和流程的支持。 + +需要能建立一套基于日志的监控报警的系统,将应用程序还有运行环境的各项数据监控起来,设置报警的阈值。当数据异常,超出阈值,就马上触发报警,然后进入应急响应的流程。 + +对于应急响应流程,首先应该能第一时间通知最合适的人去处理,比如负责这个服务值班的开发人员,然后对于怎么第一时间恢复应该有准备,涉及跨部门协作也应该有相应的配合流程;最后对于故障应该有总结,避免类似情况再次发生。 + +有关监控和日志分析,我还会在我们专栏后续文章《 监控和日志分析:如何借助工具快速发现和定位产品问题 ?》中有更多介绍。 + +然后,要构建基于云计算和虚拟化技术的基础设施。 + +虽然并非每一个软件项目都是基于云计算或虚拟化技术来搭建的,但云计算和虚拟化技术方面的技术,其实是横跨开发和运维的,可能对于大部分开发和运维来说,都只了解其中一部分知识,这就需要有人能同时懂软件开发和云计算或虚拟化技术,或者一起协作,才能搭建出真正适合云计算或虚拟化技术的架构。 + +构建出来基于云计算和虚拟化技术的基础设施后,对于开发人员来说,只要通过 API 或脚本即可搭建应用,对于运维来说,也只要通过脚本和工具即可管理。 + +这其实也是 DevOps 中的基础设施即代码的概念。 + +最后,要形成 DevOps 的文化。 + +DevOps 最核心的本质就是工作方式和协作的文化,而这样的文化需要有人引领,一点点去形成。 + +DevOps 工程师要帮助开发和运维相互理解对方的工作,帮助开发和运维在一起协作时多沟通,相互学习。出现问题不指责,而是分析原因,共同承担责任,找出改进的方案。 + +这些就是 DevOps 工程师要做的事情,本质上还是 DevOps 的几条基本原则:自动化、信息透明可测量、构建协作文化。不需要有 DevOps 工程师的头衔,基于 DevOps 的原则去做事情,就可以算的上是 DevOps 工程师。 + +总结 + +今天我带你一起学习了当前热门的 DevOps 概念,DevOps 可以理解为一种开发和运维一起紧密协作的工作方式,从而可以更快更可靠地构建、测试和发布软件。DevOps 的主要原则就是自动化、信息透明可测量、构建协作文化。 + +DevOps 工程师,要做的事情就是帮助团队来实践 DevOps 的工作方式。具体可以帮助团队: + + +建立基于持续集成和持续交付工作流程; + +建立基于日志的监控报警的系统,以及故障响应的流程; + +构建基于云计算和虚拟化技术的基础设施; + +形成 DevOps 的文化。 + + +DevOps 工程师做的事情,就是帮助团队基于 DevOps 原则来做事,让团队形成紧密协作的工作方式,更快更可靠的构建、测试和发布软件。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/37遇到线上故障,你和高手的差距在哪里?.md b/专栏/软件工程之美/37遇到线上故障,你和高手的差距在哪里?.md new file mode 100644 index 0000000..b71933c --- /dev/null +++ b/专栏/软件工程之美/37遇到线上故障,你和高手的差距在哪里?.md @@ -0,0 +1,173 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 37 遇到线上故障,你和高手的差距在哪里? + 你好,我是宝玉。在软件上线后,发生线上故障是一个常见的问题,但怎样对线上的故障进行处理,却很能反映出新手和高手程序员的差距。对于团队来说,如何应对线上故障,也同样能反映出线上运维水平的高低。 + +今天,我将带你一起分析一下,新手和高手在应对故障时有什么不同?大厂在处理线上故障时,有哪些可以学习借鉴的地方。 + +遇到线上故障,新手和高手的差距在哪里? + +在这里,我把新手在处理线上故障遇到的一些常见问题列一下,同时,我们也一起分析下,高手是怎么处理这些问题的。 + +新手遇到复杂的线上故障,不知道该怎么下手 + +对于线上故障,有的很简单,从界面或者错误日志上可以直观地看到问题在哪,从而也好找到方法去解决。但有的故障,却没办法直观地看出原因,比如说内存一直在涨,CPU 居高不下,遇到这种复杂的故障,通常新手就不知道该怎么下手了。 + +而对高手来说,会在实践中总结一套自己解决问题的步骤,遇到问题,会按照解决问题的步骤有条不紊地去分析和解决。(比如说 caoz 的这篇出了 bug 怎么办,就充分体现了一个高手的水平。)通常通过下面这样的步骤: + + +第一步,评估影响范围; + +第二步,试图重现问题; + +第三步,临时方案和终极方案; + +第四步,风险评估及持续优化。 + + +如果你还记得专栏文章《28 软件工程师的核心竞争力是什么?(下)》里的内容,就会发现,其实这本质就是一种解决问题的能力。一步步分析、解决和预防问题。 + +从新手到高手,可以从借鉴像这样的方法开始,然后在实践中不断总结经验,形成一套自己的分析问题、解决问题的方法。 + +新手遇到线上故障,会想着马上修复 Bug + +当发现 Bug 后,尤其是自己的 Bug,很多开发人员马上就想到了 Bug 的修复方案,迫不及待就要去写代码打补丁了。然而这样做的问题就是,匆忙之间打补丁,如果没有经过充分的测试,可能会引入新的 Bug,甚至是更严重的 Bug。如果要充分测试,那么意味着时间会比较长,而线上故障的时间越长,可能意味着损失也越大。 + +而对于高手来说,会首先对故障进行评级,看对用户的影响范围,如果是核心业务,大面积影响用户,那么当务之急是恢复生产,然后再考虑如何去修复 Bug。 + +恢复生产并不一定需要修复 Bug,可以用一些临时性的方案,比如说回滚系统到上一个稳定的版本;重启服务看是否能恢复正常。当然在恢复之前,还要尽可能保留当时的日志、故障场景的截图、内存的 Dump(把当前内存数据保存的静态文件)等信息,用来后续查找故障原因使用。 + +遇到线上故障,新手需要时刻牢记:恢复生产、降低损失是第一要务,修复 Bug 是其次的。 + +新手遇到线上故障,不知道如何快速定位到 Bug 在哪 + +在临时恢复业务后,还是需要找到 Bug 在哪,后续才能从根本上解决。对于比较复杂的线上故障,新手通常不知道从哪里下手,看日志看代码都看不出所以然,而高手却总能快速地定位到 Bug 在哪。 + +高手快速定位 Bug 在哪,关键在于通过有效的手段,逐步缩小问题范围,直到找到 Bug 在哪里。 + +比如说,一种常见手段就是先重现 Bug,因为有了重现的步骤,就等于将问题的范围,缩小到重现 Bug 的这几步操作相关的代码上,就很容易发现问题在哪。 + +还有一种手段就是分析错误日志,通过错误日志,可以马上定位到错误在哪里。所以对于平时写程序,无论是客户端还是服务端,注意收集错误日志是非常重要的,可以帮助你在排查问题的时候节约不少时间。 + +还有一些不能重现的 Bug,则不是那么容易发现,其实也可以按照缩小问题范围的思路来定位。 + +比如说,Bug 是在最近一次部署后发现的,并且回滚部署后就恢复了正常,那么就说明问题很可能是由于这一次部署和上一次部署之间的代码变更导致的,如果代码变更不多,就可以通过分析变更的代码来定位。 + +像内存泄漏或者 CPU 高的问题,一般就可以通过分析内存 Dump 文件,分析当前是哪些线程占用资源多,线程运行的代码是什么;哪些变量占用资源多。从而可以缩小范围,快速发现问题在哪。 + +排除法也是一种缩小范围的方法,尤其是在架构比较复杂的情况,一次用户请求的操作可能经过多个服务,如果配合日志,那么可以对一个请求经过的每一个服务都进行日志分析,对于正常的服务可以逐一排除,直到找到出问题的环节,从而可以缩小问题范围。 + +所以下一次你在遇到难以定位的 Bug 的时候,也可以想想怎么样可以逐步缩小问题的范围,直到发现问题。 + +新手解决完线上故障后,下次可能还会发生类似故障 + +新手在遇到线上故障后,采用一些临时解决方案,比如说重启服务,发现恢复了,然后就把这事忘记了。对于线上的故障,如果不找到产生的原因,那么下一次还会发生类似的故障,甚至比以前还更严重。 + +高手对于线上故障,会仔细分析 Bug 产生的原因,从根本上解决,避免类似的故障再次发生。 + +比如说,我以前所在的项目组,采用敏捷开发,每周一个 Sprint,每周会部署上线。但上线后经常会出现一些故障,导致在部署后就要回滚,或者再打补丁。 + +虽然每一次上线后的故障可能都不一样,但是如果仔细分析背后的深层次原因,还是因为上线前没有充分测试导致的。每周 3~4 天时间开发,1~2 天测试,来不及对程序进行充分的测试。所以我们后来从流程上改进,将一个 Sprint 内开发好的程序,在测试环境测试一周后再上线,这样调整后,极少出现线上故障。 + +有关上面案例中流程改进结果,可以参考专栏文章《07 大厂都在用哪些敏捷方法?(下)》中“一周一个迭代怎么保证质量?”的说明。 + +对于新手来说,每一次解决线上故障,同时也是一次学习和总结的机会,不仅是学习如何解决一个线上故障,还要学习解决一类的线上故障,避免类似的故障再次发生。 + +大厂都是怎么处理线上故障的? + +在处理故障方面,可以看到新手和高手的差距。同样,如果你留心观察,会发现各个大厂,也都有一套自己线上故障处理的流程。(比如:SRE:Google 运维解密, 阿里如何应对电商故障?, 滴滴是如何高效率处理线上故障的?) + + + +通过看这些大厂的故障处理流程,你会发现,大厂其实是把高手解决故障的方式,变成故障处理的流程和操作手册,并且通过反复地故障演习。不断练习和强化对故障处理的流程,让系统更健壮,让新手也可以快速上手,做到高效处理线上故障。 + +至于具体的处理流程,其实大同小异。 + + +首先,对故障进行评级。 + + +根据故障影响的范围,对故障进行评级,从而决定后续的处理方案。比如说 P0 是最严重最紧急的,可能是大面积服务瘫痪,影响大量用户,需要紧急处理;如果是 P5,可能只是用户体验相关的,晚一点处理也没关系。 + + +其次,要马上恢复生产,避免进一步损失。 + + +使用临时方案,恢复生产减少损失是第一位的。可以采用部署回滚、服务降级等处理手段。 + + +另外,要分析故障原因,修复故障。 + +最后,记录故障发生处理全过程,分析故障原因,提出后续改进方案。 + + +大厂处理线上故障处理机制有哪些值得借鉴的地方? + +从流程看,大厂处理线上故障的机制似乎并没有什么特别的,那么有没有值得学习借鉴的地方呢?答案是肯定有的。 + + +故障报警和轮值机制 + + +你可以先思考一个问题:如果你所在项目组的系统出现线上故障,要多长时间可以恢复正常?怎么样可以做到最快的速度恢复? + +要做到最快速度处理线上故障,关键就是要让正确的人第一时间就可以去响应。正确的人就是对故障服务最熟悉的人,通常就是这个服务的开发人员。 + +但让所有开发人员 7x24 小时随时待命也不现实,所以一般大厂会采用轮值的机制,比如说对于每个服务,每周要安排两个人值班,一个是主要的,出现故障第一时间响应;另一个人准备着,以防万一联系不上主要值班人员时可以顶替值班。 + +大厂都有一个报警系统,值班的那一周,值班人员手机要 24 小时开机,笔记本要随身携带,如果负责的服务出现故障,那么会在第一时间被报警系统呼叫。如果 15 分钟没有人响应,就会层层往上传递,值班开发人员没响应就呼叫经理,再是总监,VP,直到 CEO。 + +这套机制虽然被很多开发人员诟病良多,毕竟值班期间要随时待命,但确实是一套非常简单有效的机制,让最熟悉服务的开发人员第一时间去处理,可以帮助线上系统以最快的速度恢复服务。 + + +实战演习 + + +在我工作经历中,不止一次出现过数据丢失的情况,其实丢失数据前,都有完善的备份恢复方案和日常备份,然而这些备份恢复方案却从来没执行过,等到真正出问题,才发现这个方案完全是不可行的,日常备份也早已被破坏无法恢复,最终导致数据丢失。 + +如果日常对这些方案有演习,去实际测试一下,就不至于这么狼狈。实战演习就是频繁地对故障进行演练,来测试平时做的这些方案是不是真的可行,这样遇到真正的故障,才不至于手忙脚乱不知道如何应对。 + +其中最有名的就是 Netflix 的混乱猴子军团,Netflix 在亚马逊云上建立了一个叫做 Chaos Monkey(混乱猴子)的系统,这些猴子会在工作日期间随机杀死一些服务,制造混乱,来测试生产环境下的稳定性。 + +也有人把这样的实战演习叫“混沌工程”。 + + +混沌工程就像“疫苗”:注射少量潜在有害的异物以预防疾病,这种人为的“破坏”其实是有帮助的。混沌工程通过在技术系统中注入危害 (如延迟、CPU 故障或网络黑洞) 来建立这种免疫力,从而发现和修正潜在的弱点。以毒攻毒: Google、Amazon、Netflix 如何用混沌工程控制系统风险 + + + +日志记录和分析工具 + + +对于软件来说,线上出现问题,分析日志记录是最简单有效的定位问题方式。这就要求平时在开发的时候,就要注意对关键日志信息的记录,同时还要搭建像 ELK 或 Splunk 这样的日志分析系统,方便查询日志。 + +举个例子:一个 API 请求,出现了随机无法访问的故障,而这个 API 可能会经过 5-10 个服务,怎么快速定位是哪一个服务出现问题? + +一个好的实践是这样的: + +对于每一个请求,都会分配一个唯一的请求编号(requestId),在经过每一个服务的时候,都带上这个请求编号,每个服务都把这个请求的输入和输出记录下来,输入的 url 参数是什么?http 的 header 是什么?输出的状态码是什么,输出内容的大小是什么?如果出错,异常信息包括错误堆栈是什么? + +当出现故障的时候,找到一个有问题的 requestId,根据这个 requestId 去日志分析系统查询相关的所有服务的日志,这样马上就可以看出来哪一个服务返回的结果是有问题的。 + +当然还有一些其他好的实践,例如说新功能上线时,灰度发布的策略。通过开关控制,先让一小部分用户使用,如果出现故障,马上关闭开关,避免影响。 + +大厂的这些线上故障处理预防的实践都是公开的,通过网上的一些文章或者他们技术人员在技术大会上的分享,你也可以从中了解和学习到很多。重要的是看这些实践的好处是什么,哪些是可借鉴到你的项目中的。 + +总结 + +今天带你一起学习了线上故障的处理。对于线上故障的处理,基本原则就是要先尽快恢复生产减少损失,然后再去查找原因,最后不要忘记总结复盘。 + +要做到最快速度处理线上故障,关键就是要让正确的人第一时间就可以去响应。正确的人就是对故障服务最熟悉的人,通常就是这个服务的开发人员。 + +要让你的故障响应流程在真正遇到故障时能起到作用,需要经常做故障演习,测试你的故障响应流程,测试你的系统在故障下的稳健性。 + +线上故障的分析,少不了对日志的记录和分析,平时在开发阶段就应该要注意对日志的记录,同时也可以搭建一套适合你项目的日志分析系统,在遇到故障时,能及时的通过日志定位到问题所在。 + +最后,保持学习大厂对这些线上故障处理的好的实践,应用到你的项目中。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/38日志管理:如何借助工具快速发现和定位产品问题?.md b/专栏/软件工程之美/38日志管理:如何借助工具快速发现和定位产品问题?.md new file mode 100644 index 0000000..a2d9520 --- /dev/null +++ b/专栏/软件工程之美/38日志管理:如何借助工具快速发现和定位产品问题?.md @@ -0,0 +1,193 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 38 日志管理:如何借助工具快速发现和定位产品问题 ? + 你好,我是宝玉。在开始学习之前我想先问你几个问题: + + +如果你的网站或者服务出现故障,是谁第一时间发现问题的?用户还是运维人员? + +假设你的服务架构是由若干微服务组成的,其中一个微服务的异常导致了你的某个 API 请求异常,你是否能快速定位到是哪个微服务出了问题? + +在部署系统后,你是否能观察出来系统的性能是上升了还是下降了? + + +如果你自己对这些问题的答案不是很满意,那么就可以来看看,如何借助监控和日志分析工具,或者说日志管理工具,第一时间发现线上问题,以及快速定位产品问题。 + +什么是日志管理? + +要理解上面提到的这些问题,首先你要清楚,什么是日志管理。 + +日志就是操作系统和应用软件自动生成的事件说明或者消息记录,包含了时间、日志信息。举例来说,下面就是一个典型的 Web 请求日志: + + +10.0.1.22 – – [15/Oct/2018:13:46:46 -0700] “GET /favicon.ico HTTP/1.1” 404 + +10.0.1.22 – – [15/Oct/2018:13:46:58 -0700] “GET / HTTP/1.1” 200 + + +从上面的日志中,可以看出来,日志包含两次 http 请求,它们发生的时间、请求的 URL、请求的 IP 地址、最后返回的状态码等信息。 + +在日志数量不多的时候,凭借肉眼或者借助文本编辑器,还能大概看出日志的内容,但是当日志数量一多,从日志里面查找需要的信息就变得很困难了。 + +现在的应用程序越来越复杂了,尤其是像微服务这样的架构,一个系统需要由若干微服务组成,每个微服务可能还会部署在若干容器上,那么意味着如果你要根据日志去排查故障的话,需要从几十、上百个地方去收集日志,再逐个去分析。 + +要解决这样的问题,就需要对日志进行统一管理。日志管理就是指对系统和应用程序产生的日志进行处理的方法,包括对日志进行统一收集,对日志数据进行筛选和解析,统一存储,还要让它们可以方便被检索。 + +当然你不需要自己去从头实现这样的日志管理系统,现在已经有很多成熟的日志管理工具可以帮助你对日志进行管理,你只要去了解这些工具可以帮助你做什么,以及如何基于它们来搭建适合你项目的日志管理系统即可。 + +如何快速发现和定位问题? + +也许你会问,为什么说搭建了日志管理系统,就可以帮助快速发现和定位问题呢? + +首先,日志集中式管理后,就可以方便地对所有日志进行统一的检索。当所有日志都可以放在一起检索了,自然就能高效地定位到问题,而不再需要到各个应用程序的日志里面去分别检索。 + +同时在检索的方式上,可以用类似于 SQL 语句的方式来检索,高效地对结果进行查询和归类。 + + + +图片来源:Splunk + +然后,对日志进行集中式管理后,可以通过图表直观的看到应用运行情况。当所有的应用实时将日志传输到一起,日志管理系统就可以根据应用日志中记录的信息,动态地生成图表,实时看到应用运行的情况。 + +举例来说,某一个 API 服务,日志信息记录了每一次 Http 请求的状态、耗费时间等信息。 + + +127.0.0.1 [10/Oct/2018:13:55:36 -0700] “GET /api HTTP/1.1” 200 2326 0.038 + + +那么把这些信息统一收集、实时统计的话,就可以随时看到单位时间内,这个 API 错误率有多少,平均耗时多久,从而可以根据这样的信息生成实时的图表,方便查看当前 API 服务的运行情况。 + + + +图片来源:WaveFront + +最后,可以根据日志的数值设置规则自动报警。对于这些从日志中实时分析出来的数据结果,如果设置好相应的阈值,在超过阈值后,比如说 API 错误率超过 10%,或者 90% 的 API 请求时间超过 1 秒,就会自动触发报警,通知相关的开发人员进行维护。 + +所以你看,当你搭建好一整套日志管理系统后,不仅可以帮助你快速地对日志进行检索,你也可以根据图表看数据走势,还可以通过对日志分析结果的监控,设置自动报警的规则,第一时间了解系统故障。 + +大厂的日志管理系统的架构是什么样子? + +现在对于像阿里、新浪这样的大厂来说,对日志管理系统的应用已经是标配了,比如说阿里云:《基于 ELK 实时日志分析的最佳实践》、新浪:《ELK Stack 在新浪微博的最佳实践》、《新浪是如何分析处理 32 亿条实时日志的?》,七牛:《如何快速搭建智能化的统一日志管理系统》。 + +可以看得出,很多大厂是基于 ELK 搭建的自己的日志管理系统,而 ELK 的架构也是一套经典的日志管理的架构,所以这里我就以 ELK 为例来说明日志管理系统的基本架构。 + +先解释一下 ELK: + + +ELK 是 Elasticsearch+Logstash+Kibana 的缩写。 + +ElasticSearch 是一套搜索框架,提供了方便的接口,可以方便地做全文检索,可以用来对日志进行检索。 + +Logstash 是一个数据收集工具,可以用来收集日志数据。 + +Kibana 是一套可以和 ElasticSearch 交互的界面,通过 Kibana 可以方便的检索 ElasticSearch 内的所有数据,还可以用图形化的方式展示数据结果。 + + +基于 ELK 搭建的日志管理系统基本架构是这样的: + + + +这套架构有几个重要的模块:日志采集和解析、存储和搜索、结果可视化、监控和报警。 + + +日志采集和解析 + + +要想对日志进行统一管理,就必须要从各个应用系统收集日志。Logstash 就可以帮助实现对日志的采集。 + +如果日志文件只是一行行带时间戳的文本,那其实是无法有效检索的,必须将其解析成结构化的数据,才能方便地检索。 + +另外,一套系统可能由不同的应用类型组成,有的是 Java 写的,有的是 Go 写的,日志格式可能完全是不一样的,所以还有必要在对日志解析后,提取公共元素,比如时间、IP 地址、主机名、应用名称等。 + +Logstash 不仅可以对日志数据进行收集,还能对日志数据进行过滤和解析,解析完成后再将解析好的数据发送给 ElasticSearch。 + + +存储和搜索 + + +当所有的日志数据都被集中存储后,可以想象这个日志数据库是相当庞大的,直接查询效率是比较低下的,这就意味着还需要对日志数据进行索引和分析,从而让你可以快速地检索出来结果。 + +ElasticSearch 就是一套专业的全文检索和数据存储系统,同时还有一套类似于 SQL 的查询语句,这样你就可以基于它,方便对收集好的日志数据进行检索了。 + +但 ElasticSearch 本身类似于数据库,没有图形化界面。 + + +结果可视化 + + +可视化是日志管理的另一项重要功能。通过可视化的图表,可以直观地看到数据的走势,以及方便地和历史数据进行对比。 + +比如说通过观察交易数据的走势曲线,就能看出来这周的交易数据比上周是增长还是下降;根据 API 响应速度的走势,可以看得出新版本部署后,性能是提升了还是下降了。 + +像 Kibana 就是一套专门针对 ElasticSearch 的图形化操作工具,可以方便对 ElasticSearch 数据进行检索,也可以对结果用图表的方式展现。 + + +监控和报警 + + +ELK 本身只是提供了一套基础的日志管理框架,但是基于它之上还可以有很多扩展,比如说自动报警就是一个非常典型的场景,可以基于已经存储和索引好的日志数据,制定相应的自动报警规则,当线上服务发生异常时,可以自动地触发报警,通知相关值班人员及时处理。 + +ELK 可以通过插件的方式,安装像 ElastAlert 或Watcher这样的自动报警插件,实现自动报警功能。 + + + +图片来源:Build your own error monitoring tool + +怎样搭建一套日志管理系统? + +在了解了整个日志管理系统的基础架构后,再要去搭建这样一套日志管理系统,就可以做到心中有数了。你可以基于这套架构去寻找合适的工具,或者直接基于 ELK 去搭建一套日志管理系统。 + +关于 ELK 网上已经有很多安装使用教程,比如这一本电子教程《ELK 教程》就写的很详细。 + +ELK 本身是一套开源免费的工具,除了 ELK,还有一些类似的工具可以选择,可以和 ELK 配合使用。 + + +Splunk + + +Splunk 是一套商业的日志管理系统,搜索功能非常强大,操作方便,就目前来说,要比 ELK 好用,但价钱很高。 + + +Grafana + + +Grafana 是一套开源的数据监测和可视化工具,可以和 ELK 或 Splunk 配合使用,展示效果比 Kibana 要更好。同时可以支持自动报警功能。 + + +Wavefront + + +Wavefront 是 VMware 旗下的一款商业的图形化监控和分析工具,可以从 ELK 或 Splunk 等数据源收集数据,在此基础上分析应用的性能瓶颈所在,排除故障。也支持自动报警。 + + +PagerDuty + + +PagerDuty 是一套报警服务,不仅可以和手机、邮件、Slack 等方便地集成,还可以和企业的轮值安排结合,按照排班顺序呼叫当值人员。 + +以上就是一些常用日志管理系统以及配套系统工具,基本上可以很好地满足你对日志管理的需求,通过搜索引擎你也可以找到更多类似的服务。 + +总结 + +今天我带你一起学习了日志管理工具相关的内容。通过日志管理工具,可以集中的管理所有系统的日志,方便对日志进行检索,图形化的展示结果,还可以做到根据设置的规则进行自动报警。 + + +如果你想搭建属于自己的日志管理系统,可以基于 ELK 或者 Splunk 这样的日志管理工具,配合一些插件,实现你自己的日志监控和分析工具。 + +在搭建好日志管理系统后,如果我们再回头看文章开头那几个问题,你会发现: + +如果你的网站或者服务出现故障,可以通过你设置好的自动报警规则第一时间通知值班人员,及时解决; + +假如你的某一个微服务出现异常,你可以从你的日志管理系统中直接对所有微服务的日志进行查询,快速定位到问题所在; + +在部署系统后,通过对 API 响应时间等数据指标的图形化显示,你可以直观的看到性能是上升了还是下降了。 + + +总的来说,借助日志管理工具,可以帮助你快速发现和定位产品问题。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/39项目总结:做好项目复盘,把经验变成能力.md b/专栏/软件工程之美/39项目总结:做好项目复盘,把经验变成能力.md new file mode 100644 index 0000000..f5d4bc6 --- /dev/null +++ b/专栏/软件工程之美/39项目总结:做好项目复盘,把经验变成能力.md @@ -0,0 +1,176 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 39 项目总结:做好项目复盘,把经验变成能力 + 你好,我是宝玉。相信大家都有这种体验,经历了无数个“996”加班,项目终于成功上线了,也进入了稳定运行阶段,你终于可以松一口气,准备迎接下一个项目的挑战了。 + +然而,这时还有一件事不要忘记了,那就是对项目复盘,全面总结一下项目过程中的得与失。 + +什么是项目复盘? + +“复盘”本来是围棋术语,表示对弈之后,棋手把下棋的过程重演一遍,看看哪些地方下的好,哪些地方下的不好,有哪些更好的走法。把下棋的过程还原,并且分析、讨论的过程就是复盘。 + +软件项目中的复盘,也是通过分析、讨论开发中出现的问题,进而总结成功经验,吸取失败教训,提升团队能力。 + +一次项目过程,自然会有一些做的好的地方,也会犯一些错误,复盘就是要分辨出哪些是好的实践,继续保持;哪些是做的不够好的,找出原因,针对性改进,避免再犯同样的错误。 + +如果没有这样的项目复盘,那下一次做项目,你还会是用同样的方式来做事情,那恐怕踩过的坑可能还得再踩一遍。 + +也许你会认为,你们团队也开过项目复盘总结会议,但是似乎没有什么效果,是不是项目复盘并没那么有价值? + +并不是项目复盘没有价值,多数情况下,是因为没有做好项目的复盘,反而相当于浪费了一次学习提升的机会。比如说一些常见的存在问题的项目复盘情形: + + +总结不出来有效的结论 + + +有些团队流水账的回顾了一遍项目过程,感觉似乎有做的不好的地方,但说不上是什么地方做不好,所以也无法进一步的总结。 + + +没做好是客观原因导致的 + + +有些团队在复盘后,将结论归结为是客观原因导致的,比如说:“虽然这次做的不好,只是客户不靠谱,但是下一次遇到一个好客户肯定能做的更好!”,这相当于没有想清楚:是哪里做的不好?为什么做的不好?下一次遇到这样的客户怎么才能做的更好? + + +知道什么原因,但不知道该怎么办 + + +有些经过分析总结,能找到原因,但不知道如何应对。比如说发现主要原因是客户老变需求导致项目延迟,但是不知道如何应对需求变更。 + +类似于这样的项目复盘,确实达不到好的总结效果,也难有提升。那么怎么样才能做好项目复盘呢? + +如何做好项目复盘? + +项目复盘,首先就是知道项目中哪些是做的好的地方,哪些是做的不好的地方,这样才能把做的好的地方继续发扬光大,做的不好的地方进行改进修正。 + +那怎么样才能知道哪些地方做的好,哪些地方做的不好呢? + +只要对比一下你当初制定的项目目标和最终的项目结果,就可以发现差异,通过这些差异,就可以清楚地知道哪些地方是变好了、哪些地方变糟了,比如说项目延期了,功能被砍了,软件质量相比以前的项目质量提升了。 + +但光知道差异还不够,需要思考背后的原因,比如说为什么会导致项目延期?做了什么事情让软件质量提升了?也就是说,要从这些事情中能总结出来规律,从而知道哪些做法是真正有效的,值得继承或者推广?哪些做法是无效的? + +这里就需要结合软件工程的知识来分析,把实践经验概括为普适的理论或者原则。 + +最后就是要用这些从经验中学到的理论或原则,指导后续的项目开发,决定要停止做什么,开始做出怎样的改变,以及继续做哪些事。 + +联想公司对于项目的复盘总结了四个步骤,同样适用于软件项目,我们可以借鉴它的做法,采用四个基本的步骤来进行: + + +回顾项目目标; +评估项目结果; +分析原因; +总结规律,落实行动。 + + +接下来我就这四个步骤来分别讲一下,如何对项目进行复盘。 + +第一步:回顾项目目标 + +每个项目在最开始的时候都会确定项目的目标,所以复盘的第一步,就是要回顾最初的项目目标,方便对最终结果进行评估。 + +在这个环节,需要你描述清楚当初定的项目目标是什么?项目计划中制定的里程碑是什么?其中的关键就在于,对目标的描述要尽可能准确和客观。 + +因为只有做到准确和客观,在后续你才能对目标的完成情况进行准确地评估。 + +比如说:“我们的目标是做一款伟大的产品”,就不算是准确客观,因为“伟大”是一个根据主观评判的形容词,每个人对伟大的理解是不同的。 + +你需要将这类形容词换成具体可考核的检查项,比如,可以总结出类似于这样的目标:“三个月时间完成一款在线学习网站产品,包括登录、在线学习、留言等主要功能模块,上线后的 Bug 比例低于上一款产品。” + +最后再加上最初定的里程碑,比如说:“两个月开始内部测试,三个月正式上线。”这样,大家就可以对目标的完成情况有清晰地认识。 + +第二步:评估项目结果 + +在对项目的目标进行回顾后,就可以来看看项目的实际结果和当初的目标有多少差异了。这里需要列出两方面的差异:好的差异和坏的差异。 + +比如说项目的结果是:我们花了四个月时间完成整体项目,三个月才开始内部测试。原有功能作出了调整,学生留言老师回复的功能改成了类似于讨论版,大家一起讨论的功能,上线后质量稳定,Bug 比例低于上一款产品。 + +好的差异: + + +上线后质量很稳定,严重 Bug 很少; + +没有出现需求遗漏,开发和测试能及时同步需求的变更。 + + +坏的差异: + + +功能发生了变化,中间有比较多的需求变更; + +项目发生了延期。 + + +可以鼓励团队成员一起列出项目中好的差异和坏的差异。需要注意的是,在这一步,只需要客观描述结果就好了,不需要去分析原因,不然大家很容易思维发散,过早陷入对细节的讨论。 + +第三步:分析原因 + +在结果评估完了后,就可以来分析原因了,分析的时候也可以主要从两方面着手:是什么原因导致了好的差异,什么原因导致了坏的差异。 + +比如说,导致好的差异的原因: + + +增加了自动化测试代码的比例,改进了开发流程,代码合并之前有代码审核,并且要通过自动化测试; + +增加了工具的使用,比如持续集成系统的搭建,每次提交后可以清楚的看到测试结果; + +改进了项目流程,对于所有的需求细分后,都创建成了 Ticket,基于任务跟踪系统记录了起来,这样可以及时了解任务进程,有需求变更的情况,相关人员也能及时了解。 + + +比如说,导致坏的差异的原因: + +老板对于产品干预过多,导致需求变更频繁; + +项目周期过长,难以响应需求的变化; + +设计时没有考虑到需求的变更,导致需求变更发生后,很多设计需要修改,最终导致延期。 + +在分析的时候,可以营造一个宽松的氛围,让团队成员能畅所欲言,讨论时要做到对事不对人,尽可能客观地分析清楚成功和失败的原因。只有分析清楚原因,才能总结出规律。 + +第四步:总结规律,落实行动 + +分析出原因后还不够,最重要的是,还需要去总结背后的规律,才能真正把成功或失败的经验变成个人和团队的能力。这里也可以充分运用你在《软件工程之美》专栏中学习到的知识,去帮助你总结规律。 + +比如说,接着上面的案例你可以继续总结规律: + + +需求变更是导致项目延期的主要源头,需要在后续项目中控制好需求的变更; + +自动化测试加上代码审查,再配合持续集成工具,可以有效提升产品质量; + +任务跟踪系统可以方便地跟踪需求的执行情况,也能保证项目成员能及时同步需求的变更。 + + +总结出来规律后,还需要落实成行动,才能真正做出有效的改变,帮助你在以后的项目中做的更好。落实行动的关键就是:对于好的实践,继续保持;对于不好的实践,停止并寻求改变。 + +就上面的案例来说,针对上面总结出来的规律,你可以继续整理出需要在后续项目中落实成行动的事项: + + +参考专栏文章《20 如何应对让人头疼的需求变更问题?》中的解决方案,针对需求变更,我们将缩短项目周期,采用快速迭代的开发模式,及时响应需求变更,同时在一个迭代中,没有特殊情况,不做需求上的变更,有变更放到下一个迭代中; + +继续增加自动化测试代码的比例,代码在合并前要对代码进行审查,用好持续集成工具; + +继续使用任务跟踪系统,对需求任务进行跟踪,并且可以尝试对于一些临时性的任务也用任务跟踪系统跟踪起来。 + + +通过分析目标、评估结果、分析原因和总结规律这四个步骤对项目复盘,能有效帮助你发现项目中做的好的地方和做的不好的地方,找出背后的原因,最终总结出来规律,落实成行动,做出积极的改变,把经验变成个人和团队的能力。 + +总结 + +项目复盘,可以帮助你从刚刚经历过的软件项目中,总结成功经验,吸取失败教训。为什么在同样的工作时间内,有的人就是成长的比较快,收获更多的经验能力?其实他们就像优秀的棋手,通过不断地对做过的事情进行总结复盘,来快速提升自己的能力。 + +项目复盘主要通过四个步骤进行:回顾项目目标、评估项目结果、分析原因、总结规律落实行动。 + +另外你需要注意的是,对于项目的复盘,并不是说只有项目快结束了才要去做,日常项目中遇到一些特殊的事情,比如线上故障,也可以及时总结复盘,预防类似的事情再次发生;在每一个迭代结束之后,都可以阶段性的复盘,比如说敏捷开发中每个 Sprint 的项目回顾会议;在整个项目结束的时候进行全面的项目复盘。 + +在项目复盘的形式上,可以通过团队会议的形式来进行,但是要想做到会议有效率,还需要在会议之前就做好准备工作,事先收集内容;会议进行中要有人组织引导大家积极发言讨论,避免陷入细节的争吵中,更要避免互相甩锅、人身攻击等极端情况发生;会议后,要落实到行动。 + +关于项目复盘会议,我觉得阿里的这篇文章写的非常好:《开会 = 浪费时间?阿里技术团队这样开项目复盘会》,你可以作为参考。 + +希望你也可以不断地对做过的事、参与的项目进行总结复盘,把经验变成能力。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/40最佳实践:小团队如何应用软件工程?.md b/专栏/软件工程之美/40最佳实践:小团队如何应用软件工程?.md new file mode 100644 index 0000000..888698e --- /dev/null +++ b/专栏/软件工程之美/40最佳实践:小团队如何应用软件工程?.md @@ -0,0 +1,213 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 40 最佳实践:小团队如何应用软件工程? + 你好,我是宝玉。经过前期理论知识的学习,你可以开始尝试在实践中去应用所学到的软件工程知识了。 + +想象一下,现在你要加入一家创业公司,从头组建一个开发团队,一开始只有三五个人,你会怎么去应用软件工程的知识,让你的团队能高效率、高质量地开发软件产品? + +或者说,你现在就是在一个小团队,各种流程不规范,开发效率低,软件产品质量不高,你打算怎么应用学到的知识去改善现状呢? + +在这一篇里,我将带你一起运用学过的软件工程知识,看如何在小团队中应用软件工程?(在这里我补充说明一下:本文讨论的小团队,不是指大厂的一个小组,而是小公司或者三五个人的小开发团队) + +小团队在软件开发中存在的常见问题 + +不知道你有没有在小团队工作的经历,如果有的话,建议你可以自己先总结一下:小团队在软件开发中存在的一些常见问题是什么? + +为什么说你需要先自己去发现问题呢?因为在学习完软件工程的理论知识后,并不是说你把所有知识点一股脑儿全应用上就解决问题了,而是要先去发现问题在哪,然后针对这些问题,再去应用软件工程的知识去寻找问题的解决方案。 + +就像小团队如何应用软件工程这个问题,你首先要先找出来小团队的问题在什么地方,然后去分析这些问题可以应用软件工程的哪些知识,从而找到适合你的解决方案。 + +我个人有过一段时间的小团队工作经历,也见过很多类似的小团队的开发,就我的经验来说,小团队在软件项目开发上,主要问题体现在以下几个方面。 + +1. 小团队成本敏感 + +首先,小团队对成本都很敏感,成本是小团队很多问题的根源,对成本的控制也衍生出一系列大公司可能感受不到的问题。 + +因为控制成本,所以开不出好的薪水,难招到优秀的程序员;因为控制成本,所以进度都催的紧,毕竟多干一天就要多发一天工资;因为控制成本,舍不得在工具上的投入,都得要尽量用免费的、开源的;因为控制成本,通常几个项目并行,一个人可能要同时在几个项目中切换。 + +2. 小团队人少活多 + +小团队人一般不会多,但是活不一定少。 + +从分工上来说,通常在大厂前端后端几个人合作完成的事,在小团队就得一个人从前端写到后端了,可能甚至都不会有专业的产品设计和功能测试人员,都是开发兼任。 + +从人员构成来说,大厂在组建技术团队时会注意梯队的搭配,整个团队像金字塔的结构,顶部有几个特别资深的开发人员,中间有一些经验丰富的,底部的是有潜力但经验比较少的。而小团队就算是运气好,也可能只有一两个技术大牛,更多的是水平一般、经验比较少的。 + +这样的分工协作和人员构成,导致的问题就是大家每天都很忙,但是感觉技术上积累有限。对个别技术大牛的依赖性强,他们一旦离职,影响非常大。 + +3. 小团队缺少流程规范 + +在流程规范方面,恐怕是大家对小团队吐槽最多的地方,也是很多从大厂跳槽到小公司的程序员特别不适应的地方。 + +项目开发比较随意,拿到需求可能就开始直接写代码了,没有严格的需求分析、架构设计,写完了后简单测试一下就上线了,上线后再修修补补;需求变更是家常便饭;多个项目并行的时候,每个项目的负责人都觉得自己的项目是最重要的,希望你能把他的项目进度往前赶一赶;老板权力很大、想法多变,经常会直接干预项目。 + +这样不规范的开发流程,导致的结果通常就是开发效率低下,软件产品质量不高,项目计划难以遵守甚至没有计划。 + +小团队如何应用软件工程? + +成本敏感、人少活多、缺少流程规范,这几个是小团队在项目开发中存在的主要问题。那么在小团队应用软件工程的时候,我们就需要去解决好这些问题。 + +成本敏感的问题,如果这个是客观存在的,就没有太好的办法去解决,只能说我们在做一些决策、制定流程的时候,需要充分考虑好成本因素,减少浪费。 + +人少活多,那么我们就相应地提升个人和团队的整体水平和效率。缺少流程规范,那么我们就建立适合小团队特色的流程规范,让开发流程规范起来。 + +所以接下来,我就从团队建设、流程建设这两个维度来谈谈如何应用软件工程。 + +1. 团队建设 + +也许你会觉得好奇,软件工程的各个知识点,都是在讲过程、方法、工具,似乎都没有讲人的,但为什么在实践的时候,反而最先考虑的却是团队建设? + +但你要换个角度想就很容易理解了:软件工程上讲的所有的过程、方法和工具,最终还是落实在人身上,需要人去基于开发过程去制定流程遵守流程;需要人去应用软件工程中总结好的方法;需要人去使用工具。如果团队对软件工程缺少认识,那再好的方法和工具也无法落地。 + +所以要实施好软件工程,也要同步做好团队建设,让你的团队有一点基础的软件工程知识积累,有几个技术骨干可以帮助一起推广和实施。如果对软件工程知识的推广能扩大到团队之外,比如你的老板和业务部门,那么在后续推进一些流程规范,会起到事半功倍的效果。 + +团队建设,绕不开几件事:招人、培养人、管理人和开除人。 + + +小团队如何招人 + + +小团队招人,难点在于成本有限,开不出很高的工资,品牌也不够吸引人,招人的时候相对选择有限,能否直接招到技术大牛就得看运气了。但这不意味着就要大幅降低标准,比较现实的方法就是招有潜力的程序员培养。 + +那么怎么知道候选人是不是有培养潜力呢?可以参考我们专栏《27 软件工程师的核心竞争力是什么?(上)》这篇文章,考察候选人的学习能力、解决问题能力。 + +我以前在创业团队时,每年会招不少实习生,然后对实习生进行培训,参与实际项目,最后留下来一批优秀的有潜力的实习生,在一两年后,就能成长的不错,能独立完成小型的项目。 + +但我在这种方式上也犯过错误,就是新人的比例太高,中间断层,日常的技术指导和代码审查一度跟不上,导致代码质量低下。所以在招人时,也不能一味节约成本,还要注意梯队的建设,中间要有几个有经验的技术骨干帮助把控好代码质量。 + + +小团队如何培养人 + + +在培养人方面,相对来说,小团队不像大公司有完善的培训制度,资源也有限,难以请到外面的人来讲课,所以培养人主要还是要靠内部形成好的学习分享的机制。 + +在大厂,新人加入,通常会指定一个 Mentor,也就是导师或者师傅,可以帮助新人快速融入环境,新人有问题也可以随时请教。这种师傅带新人的机制其实对小团队一样适用,对新人来说可以快速融入,及时获得指导,对于师傅来说,通过带人,也能促进自身的成长。 + +除了有师傅带,新人的技术成长,更多还是来源于在工作过程中不断实践和总结,在这个过程中,及时准确的反馈很重要。软件工程中,像代码审查、自动化测试、持续集成都可以帮助对工作结果进行及时反馈。 + +代码审查,可以帮助团队及时发现代码问题,也能促进团队相互学习,代码风格统一;自动化测试,可以对代码结果马上有直观的反馈,有问题早发现修正;持续集成也是通过频繁地集成频繁地给出有效反馈,及早发现代码问题。 + +在小团队推行这样好的开发实践,让团队获得及时准确的反馈,有助于整个团队的成长。 + +另外,内部的技术分享也是很好的共同提升的方式,对于听的人来说可以学习到一些新鲜的知识,对于分享的人来说,准备一个技术分享,本身就是最好的学习总结方式。我以前在团队会定期组织这样的技术分享,不止我自己,每个团队成员都会去分享,整个团队分享讨论的技术氛围形成的很好。 + +还有在分工方面,不要因为一两个技术大牛能干,就把大部分工作都让他们做了,这其实对团队整体是不利的,“大牛”的发展也遇到瓶颈,而其他人缺少锻炼。所以最好是让“大牛”一半的精力负责一些重要的像架构设计、框架开发的工作任务,同时还要有一半的精力在代码审查、带新人等方面,帮助其他人一起成长,整个团队的发展才能更健康。 + + +小团队如何管理人 + + +因为小团队人数不多,对人的管理上,可以不需要像大公司一样用复杂的组织结构,用复杂的管理制度。小团队的管理,核心在于营造好的氛围,鼓励成员自我驱动去做事。 + +其实这个理念和敏捷开发的理念是吻合的。在专栏文章《05 敏捷开发到底是想解决什么问题?》中,我也提到了:敏捷开发的实施,离不开扁平化的组织结构,更少的控制,更多的发挥项目组成员的主动性。 + +要鼓励团队自驱动,具体做法上也可以参考敏捷开发的一些做法,比如说通过任务管理系统和看板,让团队成员自己领取开发任务;在制定一个迭代的计划的时候,让团队成员一起参与对任务的打分,参与计划的制定。 + +除了这些鼓励成员自驱动,发挥主动性的做法,在营造好的团队氛围上,还要注意的就是遇到线上故障、进度延迟这些不太顺利的情况,更多的是提供帮助,一起总结复盘,而不是甩锅问责。 + + +有关开除人 + + +在应用软件工程的时候,团队中可能有些人会成为障碍,要么是能力不足无法落实,要么是态度有问题抵触软件工程的实施。 + +在这种情况下,首先对于有问题的成员肯定是要努力挽救,如果是能力不足,就给予帮助,给时间成长,对于态度有问题的,明确指出其问题,限期改正。但如果最终结果还是达不到预期的话,那就必须要果断地将这些成员淘汰。 + +2. 流程建设 + +小团队被人诟病较多的地方就是在于流程规范的缺失,但像大公司,流程规范繁多,也容易造成效率低下,人浮于事的情况,这也就是为什么现在大公司的开发团队也在分拆,从大团队拆分成小组,精简流程规范。 + +对于小团队,一开始也不宜有太多的流程规范,不然,如果流程不合适反而会成为一种束缚,最好只是先设置最基本的流程规范,然后在实践过程中针对团队特点和业务特点去逐步完善。 + +那么哪些流程是软件开发中最基本的流程规范呢? + + +选择适合你的软件开发模型 + + +现在的软件开发,已经不再像以前那样采用原始边修边改的开发模型,而是应该采用科学的开发模型。我们专栏一开始就有大量的篇幅介绍各种开发模型,大的方面有瀑布模型和敏捷开发,基于瀑布模型还有很多衍生模型。 + +那么小团队应该采用哪种开发模型比较合适呢? + +也许你会认为应该采用敏捷开发。敏捷开发确实是一种非常适合小团队的开发模型,整个开发过程非常有效率。如果能采用敏捷开发是最好的。 + +但需要注意的是,如果你的团队是以瀑布模型为主,大家都有丰富的瀑布模型开发经验,但是对敏捷开发都没有实践过,对于敏捷开发的各项活动还不熟悉,还没能充分理解敏捷的价值观和原则,那么最好不好贸然直接换成敏捷开发。 + +因为这样做的话,团队在一段时间内,都需要去摸索如何用敏捷开发,可能反而会降低开发效率。 + +对于团队只熟悉瀑布模型这种情况,有条件的话,聘请外部的敏捷顾问帮助实施敏捷开发是个不错的选择。如果条件有限,可以先尝试逐步借鉴敏捷开发中好的实践。 + +敏捷开发中哪些实践是适合小团队借鉴的呢? + +首先在开发周期上,应该缩短交付的时间,使用快速迭代的开发模型。因为小团队的一个特点是需求变化快,要求交付的速度快,那么快速迭代或敏捷开发就是一个合适的开发方式。即使团队习惯了瀑布模型开发,切换到快速迭代也会比较容易,只需要把大瀑布拆分变成小瀑布。 + +具体在实施上,可以缩短并固定开发周期,比如说每 2~4 周可以发布一个版本。在做迭代的规划时,优先选择当前最核心最重要的功能;在一个版本内,不轻易接受新的需求变更,有需求变更放到下一个迭代中;在迭代时间结束了,无论新功能是否开发完成,都按时发布新版本,没完成的放入下一个迭代。 + +通过这样的变化,可以保证在一个迭代中整个团队的开发状态是稳定的,不需要受到需求变更的干扰,也可以慢慢形成适合团队的迭代节奏。 + +另外在会议上,敏捷 Scrum 的几个会议也可以借鉴,像每日站立会议,可以帮助团队及时了解项目进展,解决进度上的障碍;每个迭代的计划会议,可以让大家一起参与到计划的制定中;每个迭代的验收会议,可以让业务部门、老板及时的验收工作成果,看到大家的工作进展;每个迭代的回顾会议,可以帮助阶段性复盘总结,不断优化开发流程。 + +还有基于看板的任务可视化,也可以帮助团队直观的看到当前迭代中的任务进度,可以主动选取任务,而不需要去问项目经理下一步该做什么。 + +以上这些内容,也可以参阅专栏文章《06 大厂都在用哪些敏捷方法?(上)》,里面有更详细的解释。 + + +构建基于源代码管理工具的开发流程 + + +很多小团队开发质量低,开发混乱的一个原因就是没有使用源代码管理,也没有一套基于源代码管理的开发流程。在专栏文章《26 持续交付:如何做到随时发布新版本到生产环境?》和《30 用好源代码管理工具,让你的协作更高效》中,对于如何基于源代码管理工具构建和开发已经有了非常详细的介绍,这些开发流程一样适用于小型团队。 + +有一点要注意的是,小型团队完全没有必要自己去从头搭建自己的源代码管理工具、持续集成工具,应该尽可能采用在线托管的服务,这样可以节约大量搭建、维护工具的人力和时间成本。 + +类似的策略也应体现在技术选型上,小团队应该尽可能使用现成的工具、框架,而避免自己造轮子,把主要精力放在业务功能的开发上面。 + + +建立外部提交需求和任务的流程 + + +小团队在流程规范上混乱的一个体现是,业务部门包括老板对于提交开发任务非常随意,可能直接找某个开发人员私下让改一个需求,增加一个功能,导致开发人员不能专注于任务开发,经常被打断。还有多个项目并行而资源又紧缺的情况下,每个项目负责人都觉得自己的业务是最重要的,希望能尽快完成。 + +如果你有过在火车站售票口排队买火车票的经历,你会发现,无论人有多少,只要大家有序排队,售票窗口就能按照先后顺序为大家服务,如果大家一窝蜂挤上去买,就会乱成一团,如果有人插队,那么其他人的进度就会受影响。 + +其实软件项目开发也是类似的,对于开发团队来说就像是售票窗口,买票的人就相当于一个个的开发任务,无论开发任务有多少,只要你将这些开发任务排成队列,就可以有序地解决。如果一个业务团队的开发任务特别紧急要插队,那么意味着其他业务团队的任务就必须要受影响,那么就需要大家一起去协调。如果你不去通过流程规范任务,那么任务一多,必然就会乱成一团,无论是开发团队内部还是外部,都不会满意。 + +建立外部提交需求和任务的流程,可以参考专栏《14 项目管理工具:一切管理问题,都应思考能否通过工具解决》的内容,让所有人都基于任务跟踪系统去提交需求和开发任务,所有任务都先进入 Backlog(任务清单),然后在每个开发迭代中,去按照优先级选择当前迭代的任务,如果有优先级的冲突,应该需要事先沟通解决。对于提交需求和任务的人,也能通过任务跟踪系统,及时的了解到任务的进展。 + +在团队之外推行这样的流程是会有一定阻力的,最好是能事先去找几个关键的业务负责人私下沟通,取得理解和支持,让他们知道这样做对他们的好处,比如说可以更好地跟踪任务的进展,让开发效率更高,更好地为他们完成任务。 + +以上这几个流程,就是在小团队的软件开发中应用软件工程,需要建立几个最主要的的流程,把这几个基础流程建立起来后,就可以帮助小团队的开发,从无序逐步进入有序。 + +总结 + +今天,我带你一起分析了小团队在软件项目开发上的主要问题是:对成本敏感、人少活多和缺少流程规范。相应的,我们就需要从团队建设和流程建设两个地方入手,去解决这些问题。 + +在团队建设方面,需要从四个方面入手:招人、培养人、管理人和开人。 + + +招人的时候,找一些有潜力的培养,也要注意梯队建设,中间有技术骨干补充; + +对团队的人才要悉心培养,通过给新人安排师傅的方式培养新人,日常注意代码审查,内部技术分享是个不错的共同提高的方式,技术高手要注意不只是闷头干活,也要承担一定的带人的工作; + +管理人核心在于营造好的氛围,鼓励成员自我驱动去做事; + +对于不适合团队的人也不要手软,及时的淘汰。 + + +在流程建设方面,要着重建设好三个方面的流程: + + +选择合适的软件开发模型,建立项目开发流程; + +构建基于源代码管理工具的开发流程; + +建立外部提交需求和任务的流程。 + + +团队建设和流程建设是在小团队中应用软件工程的关键,通过团队建设让团队成员有共同的软件工程意识,有实施软件工程的基础,通过流程建设让软件工程好的实践流程化、工具化。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/41为什么程序员的业余项目大多都死了?.md b/专栏/软件工程之美/41为什么程序员的业余项目大多都死了?.md new file mode 100644 index 0000000..8f1aede --- /dev/null +++ b/专栏/软件工程之美/41为什么程序员的业余项目大多都死了?.md @@ -0,0 +1,133 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 41 为什么程序员的业余项目大多都死了? + 你好,我是宝玉。对于不会写程序的人来说,想做一个软件项目,就得找程序员帮忙写程序。而对于程序员来说,想做一个软件项目,写程序不是问题,产品设计自己也能做一点。所以对于很多程序员来说,一旦有了一个想法,可能就会利用工作之外的时间,做点自己的业余项目(也叫 Side Project)。 + +然而大部分项目,都是怀着美好的期望开始,结果做到一半就无疾而终,就算少数坚持到了上线发布,最终还是因为少人问津而不得不放弃。 + +所以今天将带你一起分析一下,为什么程序员的业余项目大多都死了?怎么样可以借助学习到的软件工程知识提升业余项目成功的概率? + +为什么程序员的业余项目大多都死了? + +作为程序员,我也很热衷于做业余项目,周围的程序员朋友们也有不少做业余项目的案例,通过对这些案例的观察和分析,我觉得程序员做的业余项目,主要死于以下这些情况。 + +1. 想法大,时间少 + +我有个朋友,前一段突然有了一个想法,想做一个类似于 Excel 的基于网页的在线电子表格程序,这是个很大的想法,毕竟微软和谷歌都是有一个团队在完成这样的项目。 + +但他觉得如果只是实现最核心功能还是可行的,于是激情满满地找资料,写原型代码。然而现实还是很残酷的,他上班就忙,经常加班,下班还要带娃,留给自己的时间其实不算多,一段时间看不到成果后,慢慢的激情就消逝了,这个项目也就不了了之了,而现在他已经又在尝试其他项目了。 + +这是一个常见的现象,很多程序员在业余做项目开始之前激情满满,经过一段时间没有进展,没有正向反馈,很容易就激情消逝,不想再继续了。尤其是一段时间后,可能又有新的项目想法了,于是就又开始了一个新的循环。 + +2. 过于追求技术,缺少约束 + +在公司的项目中,我还是比较保守的,毕竟要受限于项目的成本、时间和范围的限制,而且有 Dead Line 的强约束,所以不会太激进,能稳定上线运行是第一位的。而我一旦去做业余项目,就会陷入过于追求技术的困境。 + +我前年的时候,想对自己的一个网站进行升级,如果用传统的熟悉的技术方案应该不需要太长时间,但我想体验当时比较新的 React Universal 技术,后端 API 要使用当时时髦的 GraphQL,要考虑多数据库的支持,还要用上 WebSocket 保持内容及时更新。结果有些知识还需要边学边用,虽然在学习这些知识的时候收获不小,但是项目进度却是惨不忍睹,到今天还只有一点雏形。 + +程序员的业余项目,因为缺少成本、时间和范围的限制,没有设置 Dead Line 约束,所以经常会天马行空,只为了追求技术上的兴奋点,恨不得把新酷技术都用上。 + +但如果看看项目的定义: + + +项目是指一系列独特的、复杂的并相互关联的活动,这些活动有着一个明确的目标或目的,必须在特定的时间、预算、资源限定内,依据规范完成。(摘自百度百科) + + +你就可以发现,项目是要有目标要有约束的。一个缺少目标和约束的项目,是难以成功的。 + +3. 缺少产品能力和运营能力 + +有一些程序员,是有做产品的梦想的,希望能打造一款好的产品,解决工作生活中的一些问题,或者就是想通过做产品去赚点钱。其实这样的不乏成功案例,比如像业余时间打造出知名的 iOS 应用 Pin 和 JSBox 的钟颖,还有像图拉鼎这样的人,他们先是程序员,后来因为有了成功的产品而做独立开发者的例子。 + +但这样的例子并不多,普遍存在的情况是,当程序员们真正要去打造产品的时候,却发现要做一个产品并不是那么容易的事情,缺少产品能力就无法设计出好的产品,缺少运营能力就算产品做出来也鲜有人问津。而那些真正成功的独立开发者,无不是能兼顾产品设计能力和产品运营能力,既能设计出真正解决用户需求的产品,又能通过一定的运营让用户了解产品,为之买单付钱的人。 + +怎样提升业余项目成功的概率? + +想法大,时间少;过于追求技术,缺少约束;缺少产品能力和运营能力。这几点是程序员业余项目失败的主要原因。如果要应用软件工程知识来寻找这些问题的解决方案,你会想到什么方案呢? + +1. 怎么样让项目不至于半途而废? + +想法大,时间少怎么办?怎么样让项目不至于半途而废呢? + +回想一下专栏文章《08 怎样平衡软件质量与时间成本范围的关系?》中的金三角理论,在这一篇中,我解释了如何去平衡软件质量与时间成本范围的关系,今后你会发现项目中很多问题都能应用到金三角的理论。 + +比如说,想法大,其实就是范围大,按照金三角的理论,你要去固定一条边或者两条边,然后去调整剩下的边。 + +对于业余项目来讲,其实时间是很难去控制的,也就是你不可能像每天上班那样投入大量的时间在上面,所以这条边要被固定起来。 + +然后看成本这条边,虽然理论上来说你可以花钱请人帮你,也可以花钱买成熟的商业组件,但作为一个业余项目,一般来说前期不会投入大成本的。你也可以假定它是固定的。 + +那么最适合调整的边就是范围这条边,毕竟作为一个业余项目,你可以先实现最核心的功能。可以采用 MVP(minimum viable product,最小化的可行性产品)的模式,一开始只推出最核心的功能,满足用户最核心的需求,然后在用户的使用过程中收集反馈,进一步升级迭代。 + +前不久一个朋友做了一款播客的应用,他就是采用的 MVP 的开发模式,先快速发布了一个只有核心功能的版本,甚至还很多 Bug。发布后邀请了几个朋友试用,收集了反馈,并且也把发现的 Bug 修复了,再逐步增加新功能。这样几个迭代后,他的 App 已经登上了新闻分类的排行榜。如果一开始他就想的是要做一个很大的项目,也许到现在还在开发中呢。 + +即使程序员做的是业余项目,还有必要补充的一点就是:在决定做什么项目之前,一样要充分考虑项目的可行性研究。关于这一点,你可以参考专栏文章《09 可行性研究: 一个从一开始就注定失败的跨平台项目》去从经济可行性、技术可行性还有社会可行性方面,去分析一下项目是不是真的可行,再动手不迟。 + +2. 怎么避免陷入过于追求技术,项目难以交付的困境? + +过于追求技术,缺少约束是程序员业余项目失败的另一个主要原因。 + +程序员追求技术是天性,这一点其实也不是坏事,重点是要有所约束,毫无约束的结果就是迷失在技术中,而忘记了项目的整体。 + +这其实也是我在专栏一开始就写的《02 工程思维:把每件事都当作一个项目来推进》中提到的,要把业余项目也当作一个正式的项目,做你的业余项目时,也要站在项目的整体去思考项目的进展,而不是沉迷于局部的技术实现。 + +所以你有业余项目的话,也要像专栏文章《11 项目计划:代码未动,计划先行》中提到的那样,去做项目计划,去设置里程碑。还要敢于把计划和里程碑分享给你的家人和朋友们,公开的做出里程碑的承诺,让他们帮助监督你的计划执行。 + +当你有了一个可行的计划,有了真正的 Dead Line,你的项目交付就有了基本的保障。 + +我前些年运营过网站,一个针对我的母校西北工业大学校友们的论坛网站叫开放实验室,我需要负责这个网站的日常运营和程序开发,所以每次升级之前,我都会在论坛发帖子公布我的升级计划,设定一个上线时间,这样网站的用户会监督我的项目进度。有了进度的压力,就会逼着我必须按时完成,而不是老想着用什么新酷的技术。 + +在你的业余项目难以交付的时候,记住一句话:Dead Line 就是第一生产力。 + +3. 怎么弥补你的短板? + +产品能力和运营能力是大部分程序员的短板。 + +关于产品设计,我们专栏需求分析篇的几篇文章可以参考。《17 需求分析到底要分析什么?怎么分析?》可以帮助你去分析用户真正的需求是什么,从而让你可以做出来用户想要的,而不是自己凭空想象出来的用户需求。 + +产品能力的锻炼不是一朝一夕就能炼成的,但日常多模仿,多实践,还是能做出来不错的产品设计,我在专栏文章《19 作为程序员,你应该有产品意识》中如何培养产品能力上,给出了一些具体的建议。 + +比如说你可以从解决自己的需求,解决家人朋友的需求开始,设定一个小的产品目标,然后借鉴类似的产品,模仿它们的产品设计、交互设计,就能做出来一个基本可用的产品。 + +像 UI 设计,其实现在无论是网站的 UI 设计还是 App 的 UI 设计,都趋向于标准化,对于一个业余项目,使用一些标准模板,或者花点钱购买一套漂亮的界面模板,都是不错的选择。 + +对于产品的运营,这一点很遗憾,软件工程重点是讲如何做项目的,并没有太多运营相关的知识。我个人的一点经验就是,如果你要运营一款产品,你需要想清楚以下几个问题: + +想清楚你的产品能给用户带来什么样的价值?帮助用户解决什么问题? + +商业模式是什么?也就是用户是不是会为你的产品付钱?或者你的产品通过什么方式赚钱? + +如何让用户知道你的产品?如何让用户知道你产品所能带来的价值? + +只有想清楚了你的产品的核心价值是什么,才好去针对性的运营你的产品。具体产品的运营上,可以找你的朋友作为第一批用户,然后去像Product Hunt这样的网站发帖子自荐,还可以通过微博、Twitter 这样的社交媒体宣传。 + +除了自己去学习产品知识和运营知识之外,其实还有一种方式,就是组建一个小团队,找到志同道合的人一起,你写程序,有人做产品设计,有人负责运营推广,大家取长补短,一起把产品做好! + +总结 + +今天带你一起分析了程序员的业余项目失败的原因。想法大,时间少;过于追求技术,缺少约束;缺少产品能力和运营能力。这几点是程序员业余项目失败的主要原因。 + +针对想法大、时间少的问题,可以借助软件项目金三角的理论,去缩小范围,在做项目时,可以采用 MVP 的开发模式,先实现核心需求,再逐步增加功能。 + +针对过于追求技术、缺少约束的问题,应该要对你的项目制定计划,设定里程碑,把时间点告诉你的家人和朋友,让他们监督你执行,通过 Dead Line 来保障项目的进度。 + +针对缺少产品能力和运营能力的问题,需要有针对性地去学习相关知识,也可以去组建小团队,弥补这些方面能力的不足。 + +最后,即使程序员们的业余项目很可能会是以失败告终,我做过很多失败的业余项目,但我还是强烈的建议你多尝试做一做业余项目。因为做业余项目,即使项目失败了,一样可以让你收获很多: + + +通过业余项目,你可以学习和使用工作中不会使用的技术。你工作中做后端开发,你业余项目完全可以体验 iOS App 开发。 + +通过业余项目,你有机会去按照自己的想法去实现。很多时候在工作中,因为你无法去做决策,无法改变架构的设计或产品的设计,而在自己的业余项目中,你可以完全按照自己的想法去尝试,去证明自己。 + +通过业余项目,可以锻炼你的大局观和工程思维。当你真的去自己负责一个项目时,就会更多地去站在项目的整体去思考一个项目,而不是局限于专业领域。 + +通过业余项目,帮助你更好地在项目中沟通。在做过业余项目后,在工作中,和产品经理、测试沟通,你会更懂他们,因为他们的工作你也体验过了,你会体会到他们的工作其实不像你最初想的那么容易。 + + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/42反面案例:盘点那些失败的软件项目.md b/专栏/软件工程之美/42反面案例:盘点那些失败的软件项目.md new file mode 100644 index 0000000..ccde51c --- /dev/null +++ b/专栏/软件工程之美/42反面案例:盘点那些失败的软件项目.md @@ -0,0 +1,209 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 42 反面案例:盘点那些失败的软件项目 + 你好,我是宝玉。我想你日常一定看到过很多项目失败的案例,有些失败项目的案例甚至超出我们的想象,比如说我的朋友圈就被两个项目刷过屏,一个是号称史上最烂的开发项目,开发 12 年,六百万行代码;一个是美国联邦调查局的一个软件项目,花了 1.7 亿美元,最后变成了豆腐渣工程。 + +也许大多数人看完这类文章后,会当作一个有趣的故事,觉得他们软件工程水平太差了,居然会把项目做成这样。当你学习完软件工程知识后,再看到这些项目失败的案例,不妨从软件工程的角度来分析一下,这些项目失败的真正原因是什么?你能从中获得什么启发? + +什么样的软件项目算是失败的项目? + +如果我们说一个项目是失败的项目,那么怎么算是一个失败的项目呢? + +项目管理协会(PMI)认为成功的项目必须满足六个条件: + + +按时交付。 +成本在预算范围内。 +能按照当初的设计正常运行。 +有人使用。 +满足项目最初的目标。 +项目出资方对项目满意。 + + +相应的,如果上面有一个或者多个条件没有满足,那么项目就有可能是失败的,比如说: + + +没能按时交付。 +成本超出预算。 +Bug 太多,无法按照当初的设计正常运行。 +产品没有得到市场认可,没有人使用。 +产品偏移了最初的目标。 +项目出资方不满意。 + + +而那些特别失败的项目,往往是多个条件甚至所有条件都不能满足,并且时间、成本、交付结果跟最初目标都相差很大,无疑都造成了巨大的损失。 + +IEEE(电气和电子工程师协会)有一个专门的网页,把过去十年间,那些著名的失败软件项目,做了一个墓碑来展示,墓碑里的这些项目加起来的损失大约 700 亿美元。WikiPedia 上也有一个网页(List of failed and overbudget custom software projects)列出来那些损失严重的软件项目,也是惊人的数字。 + + + +(图片来源:Monument to Failure) + +而这些软件项目的失败,很大程度上是可以预测和避免的。如果把问题简简单单归结为软件工程水平太差了,或者是项目实施者的水平太差了,那么我们就无法真正的从这些失败中吸取教训,在下一次还会再犯同样的错误。 + +分析失败软件项目的原因 + +在航空业,如果一架飞机坠毁,会有专业的调查小组去对飞机失事原因进行详细调查,比如分析说当时的天气情况、飞机的维护记录、飞行员的性格特点,平时受到的培训是怎样的,航空公司的文化,对安全的重视程度等等,从而找到事故的根源,并且提出相应的改进方案,避免类似的灾难再次发生。 + +软件项目其实也是类似的,对于一个失败的软件项目案例,要去分析:外部环境、技术管理、项目管理和组织文化,这样才能帮助你找到项目失败的根源。 + + +外部环境 + + +在调查员去调查飞机失事原因的时候,首先会看的是不是外部环境导致的,例如恶劣的天气环境。分析软件项目失败原因,也可以首先看看外部环境。 + +如果你去看看历史上那些有名的失败的项目案例,其中政府主导的项目占大多数,而且通常主要因素不是成本,而是各种政治因素导致的不切实际的项目进度,或者是频繁变更的需求,从而严重的影响了成本和质量。 + +而对于商业软件项目,很多是由于缩减成本导致的。因为商业竞争的大环境,企业为了节约成本,总是希望用更少的人做更多的事情。 + +还有一些常见的场景就是在一个项目开始之前,销售为了拿下项目,通常会过度夸大项目的成果,而又会相应的压缩项目预算、时间,并且也可能低估了技术实现的难度,最终项目要开发的时候,开发人员才发现根本无法如期完成当初承诺的项目目标,最终导致项目失败。 + + +技术管理 + + +在调查飞机失事原因时,调查完外部环境,还要分析是不是飞机本身设计原因导致的,比如前不久的波音 737 MAX 飞机事故,就是因为软件故障导致的。类似的,分析软件项目失败原因,也一样要去分析技术管理上的问题,很多软件项目失败的原因也是技术原因导致的。 + +比如说在项目中使用了不成熟或不熟悉的技术,最终导致技术不可控,或者浪费大量的时间在技术的学习上。 + +项目的规模也会导致技术复杂度直线上升,想象一下,做一个普通的个人网站和做一个淘宝这样的网站,复杂度不可同日而语。通常越大的项目,技术越复杂,需要考虑各种软件硬件的交互,服务之间的耦合。也就是说,项目规模越大,失败的概率也更大。 + + +项目管理 + + +调查飞机失事,飞行员是重点调查对象,因为飞行员直接决定了飞机是否能安全行驶。对于软件项目来说,项目经理在软件项目中起着至关重要的作用。很多项目失败不是因为外部环境导致的,也不是技术原因,而是因为糟糕的项目管理。 + +在一个软件项目中,项目经理掌握了资源的分配,还要制定项目的计划,对任务进行分配,组织分工协作,管理风险,项目成员的日常沟通等等。而这些决策通常很难量化,需要基于当时的情况进行权衡,一旦这些决策出现大的失误,就会导致项目的失败。 + + +组织文化 + + +在飞机失事后,调查人员调查的最后一个领域就是所属航空公司的文化环境,看航空公司是不是足够重视安全。在软件项目中,一个开放、平等、注重沟通协作的团队或组织更容易及早发现和解决问题。 + +就像文章开头提到的美国联邦调查局的项目,当有雇员指出来项目中的问题,最后的结果竟然是被扫地出门。 + +当然,我们在分析盘点那些失败的软件项目时,从多个方面去分析,就是为了能找出这些项目失败的根本原因,从而避免类似的错误再次发生。 + +盘点那些失败的软件项目 + +接下来,我们来一起盘点几个著名的失败的软件项目,看看这些案例可以给我们的日常开发带来哪些启示。 + +在分析这些案例时,我会先分别从外部环境、技术管理、项目管理和组织文化这几个方面去分析问题和原因,最后一起总结从这些案例中收获的经验教训。 + +案例 1. 来自地狱的项目 + +案例描述: + +这个案例来自法国政府,当时参与项目的一名项目成员专门为这个项目开了一个博客叫ProjectFailures,将这个项目描述为来自地狱的项目。原计划 2-3 年开发,结果干了十几年都没有完成,最终以项目负责人被以欺诈罪关进监狱而告终。详细内容可以查看中文版本:《开发 12 年 整整 6 百万行代码:史上最烂的开发项目长这样》。 + +案例分析: + + +外部环境:法国政府官员腐败,对于项目进度并没有施加压力; + +技术管理:没有好的开发实践,完全 C++ 开发,600 万行代码,版本控制一团糟; + +项目管理:糟糕的项目管理,团队成员 55 人,35 名经理,20 名开发人员,管理人员比开发人员还多;不断开会,只是展示 PPT; + +组织文化:禁止超过 9 点打卡,禁止喝咖啡等奇葩要求。 + + +案例 2. 美国联邦调查局虚拟案件文档系统 + +案例描述: + +FBI(美国联邦调查局)虚拟案件文档系统的项目开始与 2001 年,项目初始目标是 3 年内将原有的 FBI 案件文档管理系统升级,但因为 911 恐怖袭击事件爆发,项目目标从升级变成了重写。最终 2005 年项目宣布废弃,而此时已经在这个项目上花费了 1.7 亿美元。有关项目的细节可以参考:《著名豆腐渣软件项目:美国联邦调查局虚拟案件文档系统》。 + +案例分析: + + +外部环境:FBI 没有真正懂技术的负责人领导和管控项目,对承包商缺少控制; + +技术管理:无法解决项目的复杂性,系统在设计上不完整,不充分,不到位,以至于在现实场景中完全无法使用,上线前没有测试; + +项目管理:开发方和客户之间沟通不畅;频繁需求变更,项目管理混乱,外行领导内行; + +组织文化:指出问题的雇员反而被调查和开除。 + + +案例 3. 微软 Vista 项目 + +案例描述: + +微软的 Windows Vista 项目开始与 2001 年 7 月,预计 2003 年发布。比尔盖茨为 Vista 提出了三大目标:1. 完全使用 C# 提升开发效率;2. 使用数据库作为新的文件系统 WinFS;3. 使用全新的显示技术 Avalon (后来改名为 WPF),打破桌面软件和网站的用户界面界限,提升微软竞争力。 + +目标非常好,但技术难度非常大,结果三年后也未能开发完成,不得不在 2004 年对目标进行调整:不用 C#、取消 WinFS、删改 Avalon ,一开始的三大目标就这样被完全否决,最终 2007 年才发布 Vista。参考文章:《五年磨砺: 微软 Vista 开发过程全记录》。 + +案例分析: + + +外部环境:在目标的设定上,主要不是为了满足用户需求,而是为了商业上的竞争需要; + +技术管理:技术上难度过大,超出团队控制范围,无法完成任务; + +项目管理:比尔盖茨对项目直接干预较多,项目周期太长; + +组织文化:盖茨制定目标后,核心团队明知困难,却不敢也没有反对,当看到任务无法完成时,他们不再努力工作,只想着如何推卸责任。 + + +通过对这些项目的分析,再结合我们之前学习过的软件工程知识,其实软件工程对这些问题都有方案可以应对。 + +在设定项目目标的时候,如果真正的将可行性分析落到实处,那么像 Vista 这样的技术不可行的项目目标,也许一开始就可以进行调整,而避免后续更大的损失。 + +如果在项目开始的时候,有认真的对需求进行分析,和客户有很好的沟通,对于需求的随意变更有管理和控制,那么像 FBI 这样的项目,就有机会做出来满足用户需求的软件项目。 + +在项目开发之前,如果做了架构设计,做了技术选型,那么像法国政府项目、FBI 项目,也许可以有更简单可行的技术方案,要知道架构设计就是控制技术复杂的最好手段。 + +在项目开发的时候,如果做好版本控制,持续集成,自动化测试,那么像法国政府项目、FBI 项目,质量上就更有保障,不至于一测试全是问题。 + +在设置项目周期的时候,如果能缩短版本发布周期,尽快发布第一个版本,那么很多延期本可以避免或者不至于那么严重。想想看法国政府项目花了 12 年,如果他们在第一年内能先发布一个简单的版本,后续再逐步迭代,也许结果会完全不一样。 + +缩短项目周期也是微软在 Vista 项目上收获的一大教训,在 Vista 之后,微软的项目周期都大幅缩短,而且发布频率也大幅提高,每天都有内部测试版本发布。缩短周期后,可以尽早发布,尽早验证项目的可行性,也让测试可以尽早介入。 + +在团队的文化上,如果日常营造平等的沟通协作的氛围,让项目成员敢于提出不同的意见,那么像 FBI、Vista 这样的错误也许可以早点被修正。 + +类似于这样的项目还有很多,比如有一本书叫《梦断代码》,讲述了一堆优秀程序员,一起开发一个大型的开源项目,最终如何走向失败的过程,有兴趣可以看看。邹欣老师对这本书也有非常独到的点评:《梦醒时分 - 梦断代码读后感》 + +以后你遇到类似的案例,也可以尝试去对它们进行盘点分析,找出它们失败的根本原因,能从中吸取教训,避免类似错误发生。 + +总结 + +今天我带你一起学习了如何从软件工程的角度分析失败的软件项目。 + +通过借鉴航空业对飞机坠毁原因的调查,也可以从四个方面去分析软件项目失败的原因,那就是外部环境、技术管理、项目管理和组织文化。 + +如果细化一下,还可以总结出一些具体的常见的失败原因: + + +不切实际或者不明确的项目目标; + +对项目所需要的资源估算不准确; + +需求不明确或者频繁变更; + +没有对风险进行有效管理; + +和客户之间沟通不畅; + +无法解决项目的复杂性; + +没有好的开发实践; + +糟糕的项目管理; + +上层的政治斗争; + +商业压力。 + + +其实软件项目失败并不可怕,最重要的还是在失败后,总结原因,吸取教训。就像微软在 Vista 项目失败后,总结经验,改进了开发流程,加快了发布周期,在 Windows 7 项目上重新取得了巨大的成功。还有像暴雪,在泰坦项目失败后,基于泰坦项目开发出了大受欢迎的守望先锋游戏。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/43以VSCode为例,看大型开源项目是如何应用软件工程的?.md b/专栏/软件工程之美/43以VSCode为例,看大型开源项目是如何应用软件工程的?.md new file mode 100644 index 0000000..97b15af --- /dev/null +++ b/专栏/软件工程之美/43以VSCode为例,看大型开源项目是如何应用软件工程的?.md @@ -0,0 +1,247 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 43 以VS Code为例,看大型开源项目是如何应用软件工程的? + 你好,我是宝玉。如果你所在的团队在日常的软件项目开发中,能科学地应用软件工程的知识,让你的项目能持续取得进展,最终交付的产品也有很好的质量,那么是一件非常幸运的事情。 + +然而现实中,很多人并没有机会去参与或观察一个好的项目是什么样子的,也没机会去分析一个好的项目是如何科学应用软件工程的。 + +好在现在有很多优秀的开源项目,不仅代码是公开的,它们整个项目的开发过程都是公开的。通过研究这些开源项目的开发,你能从中学习到一个优秀项目对软件工程的应用,加深你对软件工程知识的理解,进而应用到你自己的项目实践中。 + +我想你对 VS Code 应该不陌生,它是一个非常优秀的编辑器,很多程序员包括我非常喜欢它。VS Code 也是一个大型的开源项目,整个开发过程非常透明,所以今天我将带你一起看一下 VS Code 是如何应用软件工程的,为什么它能构建出这么高质量的软件。 + +如何从 VS Code 的开发中学习软件工程? + +也许你会很好奇,平时也去看过 VS Code 的网站,但并没有提到软件工程的呀? + +是的,VS Code 的网站并没有特别突出这些信息,但是如果你有心,可以找到很多有价值的信息,它的整个开发过程都是公开透明的。 + +比如通过它项目的WIKI和博客栏目,可以看到项目的计划、项目开发流程、测试流程、发布流程等信息。通过它的GitHub网站,你可以看到团队是如何基于分支开发,开发完成后提交 Pull Request,团队成员如何对代码进行审核,合并代码后如何通过持续集成运行自动化测试。 + +除此之外,团队成员在网上也有一些对于 VS Code 开发的分享,比如说 VS Code 主要负责人 Erich Gamma 2016 年在 GOTO 技术大会上有一个专门关于 VS Code 的主题演讲。 + +也许你还会想问:这些信息我也知道,也能从网上看到,但怎么通过这些信息去观察和学习它跟软件工程相关的部分呢? + +不知道你是否还记得,在我们专栏的第一篇文章《01 到底应该怎样理解软件工程?》中提到了:软件工程的核心,就是围绕软件项目开发,对开发过程的组织,对方法的运用,对工具的使用。所以当我们去观察一个软件项目,我们就可以去看它的开发过程是怎么被组织的?运用了哪些软件工程的方法?使用了哪些工具? + +接下来,我就带你一起从以下几个方面分析 VS Code 对软件工程的应用: + + +VS Code 的开发过程; + +团队的分工角色; + +各个阶段如何进行; + +使用了哪些工具。 + + +VS Code 的开发迭代过程 + +如果你是 VS Code 的用户,你会发现 VS Code 每个月都会有新版本的更新,每次更新都会有很多新酷的功能。这是因为 VS Code 每个版本的开发周期是 4 周,每四周都会发布一个新的版本。 + +从开发模式来说,VS Code 采用的是快速迭代的开发模式,每四周一个迭代。那么这四周迭代的工作都是如何进行的呢? + + +第一周 + + +每个版本的第一周,通常是起着承上启下的作用,一方面要准备新版本,一方面还要对上一个版本的工作进行收尾。 + +在这一周里,开发团队要去做一些偿还技术债务的事情,比如说重构代码,优化性能。所以如果你的团队抱怨说没有时间做偿还技术债务的事情,不妨也去学习 VS Code 团队,定期留出专门的时间,做偿还技术债务的事情。 + +另一个主要工作就是一起讨论下一个迭代要做的功能。其实这有点类似于敏捷开发中,每个 Sprint 开始之前的项目计划会议。 + +如果上一个版本开发完成的功能,发现了严重 Bug,第一周还要去修复这些紧急 Bug。 + + +第二周和第三周 + + +第二周和第三周主要工作就是按照计划去开发,一部分是开发新功能,一部分是修复 Bug,所有的 Bug 都是通过 GitHub 的 Issue 来分配和跟踪的。 + +团队成员每天还要先检查一下分配给自己的 Issue,如果遇到线上版本紧急的 Bug,要优先修复。 + + +第四周 + + +VS Code 团队把最后一周叫 End game,你可以理解为测试周,因为这一周只做测试和修复 Bug。 + +这一周要测试所有新的 Feature 和验证已经修复的 Bug,确保被修复。同时还要更新文档和写 Release Notes。 + +测试完成后就发布预发布版本,这个预发布版本会先邀请一部分人使用,比如说微软内部员工、热心网友。 + + +下一个迭代第一周 + + +每个迭代开发测试完成的版本,会放在下一个迭代的第一周发布。如果在预发布版本中发现严重 Bug,需要在第一周中修复。 + +如果没有发现影响发布的 Bug,那么第一周的周三左右就会正式发布上一个迭代完成的版本。 + +前面我在专栏文章《40 最佳实践:小团队如何应用软件工程?》中,建议小团队可以缩短迭代周期到 2-4 周,有同学担心不可行,但你看 VS Code 这样稳定的 4 周迭代,不但可行,而且还是 VS Code 能保持每月发布一个新版本的关键所在。 + +VS Code 团队的角色和分工 + +VS Code 的开发团队现在大约 20 人左右,一半在苏黎世,一半在西雅图。整个团队基本上都是开发人员,结构很扁平。 + +从分工上来说,在开发新功能和修复 Bug 的时候,会有一些侧重,比如有人侧重做 Git 相关的功能,有人侧重做编辑器部分功能。这样有侧重的分工对于提升开发效率是有好处的。 + +从角色上来说,除了开发,还有主要有两种角色:Inbox Tracker和Endgame Master。这两种角色在每个迭代的时候是轮值的,每个人都有机会去担任这两个角色。 + + +Inbox Tracker + + +Inbox Tracker 的主要任务就是收集、验证、跟踪 Bug。但这个工作对于 VS Code 团队来说可不轻松,现在 Issue 的总量已经超过了 5000,每天提交的新的 Issue 的量大概有 100 左右。所以 VS Code 团队写了一个机器人叫VSCodeBot,可以帮助对 Issue 先自动处理,打标签或回复,然后 Inbox Tracker 再对剩下的 Issue 进行人工处理。 + +Inbox Tracker 要检查新提交的 Issue 是不是一个真正的 Bug,如果是提问,建议到 StackOverflow 去问,如果是 Bug,打上 Bug 的标签,并指派给相应模块的负责人。 + + +Endgame Master + + +VS Code 团队是没有专职的测试人员的,所有的测试工作都是开发人员自己完成。在每一个迭代中。Endgame Master 在这里就很重要,要组织管理整个迭代的测试和发布工作。 + +Endgame Master 在每个迭代测试之前,根据迭代的开发计划制定相应的测试计划,生成 Check List,确保每一个新的功能都有在 Check List 中列出来。 + +因为 VS Code 团队没有专职测试,为了避免开发人员自己测试自己的代码会存在盲区,所以自己写的功能都是让其他人帮忙测试。Endgame Master 一个主要工作就是要将这些测试项分配给团队成员。 + +最后整个测试计划会作为一条 GitHub Issue 发出来给大家审查。比如说这是某一个月的Endgame 计划。 + +团队的日常沟通是通过 Slack,在测试期间,Endgame Master 需要每天把当前测试进展同步给所有人,比如说总共有多少需要测试的项,哪些已经验证通过,哪些还没验证。 + +VS Code 的各个阶段 + +接下来,我们来按照整个开发生命周期,从需求收集和版本计划、设计开发、测试到发布,来观察 VS Code 各个阶段是如何运作的。 + + +VS Code 的需求收集和版本计划 + + +VS Code 每次版本发布,都能为我们带来很多新酷的功能体验,那么这些功能需求是怎么产生的呢?又是怎么加入到一个个版本中的呢? + +VS Code 的需求,一部分是团队内部产生的;一部分是从社区收集的,比如 GitHub、Twitter、StackOverflow 的反馈。最终这些收集上的需求,都会通过 GitHub 的 Issue 管理起来。如果你在它的 GitHub Issue 中按照feature-request的标签去搜索,可以看到所有请求的需求列表。 + +VS Code 每半年或一年会对下一个阶段做一个Roadmap,规划下一个半年或一年的计划,并公布在 GitHub 的 WIKI 上,这样用户可以及时了解 VS Code 的发展,还可以根据 Roadmap 上的内容提出自己的意见。 + +大的 RoadMap 确定后,就是基于大的 RoadMap 制定每个迭代具体的开发计划了。前面已经提到了,在每个迭代的第一周,团队会有专门的会议讨论下一个迭代的开发计划。在 VS Code 的 WIKI 上,也同样会公布所有确定了的迭代计划。 + +那么,有了功能需求和 Bug 的 Issue,也有了迭代的计划,怎么将 Issue 和迭代关联起来呢? + +GitHub 的 Issue 管理有一个 Milestone 的功能,VS Code 有四个主要的 Milestone。 + + +当前迭代:当前正在开发中的 Milestone; + +On Deck:下一个迭代对应的 Milestone; + +Backlog:还没开始,表示未来要做的; + +Recovery:已经完成的迭代,但是可能要打一些补丁。 + + + + +(图片来源:VSCode Milestones) + + +VS Code 的设计和开发 + + +VS Code 的架构设计现在基本上已经定型,你在它的 WIKI 和博客上还能看到很多 VS Code 架构和技术实现的分享。 + +在每个迭代开发的时候,一般小的功能不需要做特别的架构设计,基于现有架构增加功能就好了。如果要做的是大的功能改造,也需要有设计,负责这个模块开发的成员会先写设计文档,然后邀请其他项目成员进行 Review,并给出反馈。 + +VS Code 的开发流程也是用的GitHub Flow,要开发一个新功能或者修复一个 Bug,都创建一个新的分支,开发完成之后提交 PR。PR 合并之前,必须要有核心成员的代码审查通过,并且要确保所有的自动化测试通过。 + +对于 GitHub Flow 的开发流程,我在专栏文章《30 用好源代码管理工具,让你的协作更高效》中有详细的介绍。你也可以在 VSCode 的Pull requests中看到所有提交的 PR,去看看这些 PR 是怎么被 Review 的,每个 PR 的自动化测试的结果是什么样的。通过自己的观察,去印证专栏相关内容的介绍,同时思考是否有可以借鉴到你自己项目中的地方。 + +VS Code 对自动化测试代码也是非常重视,在实现功能代码的时候,还要加上自动化测试代码。如果你还记得专栏文章《29 自动化测试:如何把 Bug 杀死在摇篮里?》中的内容:自动化测试有小型测试、中型测试和大型测试。VS Code 的自动化测试也分为单元测试、集成测试和冒烟测试。 + +VS Code 的CI(持续集成)用的是微软自己的 Azure DevOps,每一次提交代码到 GitHub,CI 都会运行单元测试和集成测试代码,对 Windows/Linux/macOS 三个操作系统分别运行测试。在持续集成上可以直观地看到测试的结果,VS Code 现在有大约 4581 个单元测试用例,运行一次 1 分钟多;集成测试 466 个,运行一次大约 3 分钟。 + + + +(图片来源:VSCode 的持续集成工具 Azure DevOps) + +如果你的团队还没有开始相应的开发流程,没有使用持续集成工具,不妨学习 VS Code,使用类似于 GitHub Flow 的开发流程,使用像 Azure DevOps 这样现成的持续集成工具。 + + +VS Code 的测试 + + +前面提到了,迭代的最后一周是 End game,这一周就是专门用来测试的,并且有轮值的 Endgame Master 负责整个测试过程的组织。 + +具体测试的时候,大家就是遵循 Endgame Master 制定好的测试计划,各自按照 Check List 逐一去检查验证,确保所有的新功能都通过了测试,标记为修复的 Bug 真的被修复了。对于验证通过的 Bug,在对应的 Issue 上打上 verified 的标签。 + +在人工测试结束后,Endgame Master 就需要跑冒烟测试,确保这个迭代的改动不会导致严重的 Bug 发生。 + +如果你的团队也没有专职测试,可以学习 VS Code 这样的做法:留出专门的测试阶段,事先制定出详细的测试计划,把所有要测试的项都通过测试跟踪工具跟踪起来,开发人员按照测试计划逐一测试。 + +\4. VS Code 的发布流程 + +在 Endgame 测试后,就要从 master 创建一个 release 分支出去,比如说 release/1.10 ,后面的预发布版本和正式版本包括补丁版本都将从这个 release 分支发布。 + +如果在创建 release 分支后发现了新的 Bug,那么对 Bug 修复的代码,要同时合并到 master 和 release 分支。每一次对 Release 的代码有任何改动,都需要重新跑冒烟测试。 + +在 Release 分支的代码修改后的 24 小时之内,都不能发布正式版本。每次 Release 代码修改后,都会发布一个新的预发布版本,邀请大约两万的内部用户进行试用,然后看反馈,试用 24 小时后没有什么问题就可以准备发布正式版本。 + +发布正式版本之前,还要做的一件事,就是 Endgame master 要写 Release Notes,也就是你每次升级 VS Code 后看到的更新说明,详细说明这个版本新增了哪些功能,修复了哪些 Bug。 + +如果版本发布后,发现了严重的线上 Bug,那么就要在 Release 分支进行修复,重新生成补丁版本。 + +除此之外,VS Code 每天都会将最新的代码编译一个最新的版本供内部测试,这个版本跟我们使用的稳定版 Logo 颜色不一样,是绿色的 Logo。VS Code 内部有“吃自己狗粮”(eat your own dog food)的传统,也就是团队成员自己会使用每天更新的测试版本 VS Code 进行开发,这样可以在使用过程中及时发现代码中的问题。 + + + +(图片来源:The Journey of Visual Studio Code) + +像 VS Code 这样的发布流程,通过创建 Release 分支可以保障有一个稳定的、可以具备发布条件的代码分支;通过预发布内部试用的机制,有问题可以及时发现,避免造成严重的影响。 + +关于发布流程的内容,你也可以将 VS Code 的发布流程 对照我们专栏文章《35 版本发布:软件上线只是新的开始》中的介绍,加深理解。 + +VS Code 使用的工具 + +VS Code 的源代码管理工具就是基于 GitHub,整个开发流程也完全是基于 GitHub 来进行的。 + +它的任务跟踪系统是用的 GitHub 的 Issue 系统,用来收集需求、跟踪 Bug。通过标记不同的 Label 来区分Issue 的类型和状态,比如 bug 表示 Bug,feature-request 表示功能请求,debt 表示技术债务。通过 Issue 的 Milestone 来标注版本。 + +VS Code 的持续集成工具最早用的是Travis CI和AppVeyor,最近换成了微软的Azure Pipelines,在他们的 Blog 上有一篇文章《Visual Studio Code using Azure Pipelines》专门解释了为什么要迁移过去。 + +VS Code 的文档一部分是用的 GitHub 的 WIKI 系统,一部分是它网站的博客系统。WIKI 主要是日常项目开发、维护的操作说明,博客上更多的是一些技术分享。 + +另外 VS Code 团队还自己开发了一些小工具,比如说帮助对 Issue 进行自动处理回复的 GitHub 机器人 VSCodeBot。 + +通过这些工具的使用,基本上就可以满足像 VS Code 这样一个项目的日常运作。像这些源代码管理、任务跟踪系统、持续集成工具的使用,在我们专栏也都有相应的文章介绍,你也可以对照着文章的内容和 VS Code 的使用情况加以印证,从而加深对这些工具的理解,更好把这些工具应用在你的项目中。 + +总结 + +当你日常在看一个开源项目的时候,不仅可以去看它的代码,还可以去观察它是怎么应用软件工程的,不仅可以加深你对软件工程知识的理解,还能从中学习到好的实践。 + +比如观察一个软件项目的开发过程是怎么被组织的,团队如何分工协作的,运用了哪些软件工程的方法,以及使用了哪些工具。 + +VS Code 使用的是快速迭代的开发模式,每四周一个迭代: + + +第一周:偿还技术债务,修复上个版本的 Bug,制定下一个版本的计划; + +第二、三周:按照计划开发和修复 Bug; + +第四周:测试开发完成的版本; + +下一迭代第一周:发布新版本。 + + +在团队分工上,VS Code 的团队很扁平,没有专职测试,通过轮值的 Inbox Tracker 和 Endgame Master 来帮助团队处理日常 Issue 和推动测试和发布工作的进行。 + +在工具的使用方面,VS Code 使用的是 GitHub 托管代码,基于 GitHub Flow 的开发流程使用。还有使用 Azure DevOps 作为它的持续集成系统。 + +通过观察对 VS Code 对软件工程知识点的应用,再对照专栏中相关文章的介绍,可以帮助你更好的理解这些知识点,也可以借鉴它们好的实践到你的项目开发中。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/44微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的?.md b/专栏/软件工程之美/44微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的?.md new file mode 100644 index 0000000..95366cd --- /dev/null +++ b/专栏/软件工程之美/44微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的?.md @@ -0,0 +1,199 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 44 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的? + 你好,我是宝玉。在上一篇文章里,我带你一起了解了像 VS Code 这样的开源项目对软件工程的应用,以及如何学习借鉴优秀的项目对软件工程的应用。今天我将带你去看看像微软、谷歌、阿里巴巴这些大厂是怎么应用软件工程的,以及我们应该如何学习和借鉴他们对软件工程的实践。 + +我想无论你现在是否在大厂工作,都有很多途径了解到大厂是如何应用软件工程的,网上已经有很多他们员工的分享。你可能更想知道的是: 从大厂应用软件工程的实践中,你能学习什么,又该如何学习借鉴。 + +每个公司,都有自己的历史和文化,他们的文化又影响了各自的软件开发模式。 + +比如说谷歌,谷歌崇尚工程师文化,请来的工程师都是万里挑一的,开发也没有太大的进度压力,所以 Google 的工程师做项目就会不紧不慢,质量优先,有统一的代码规范,严格的代码审查和严谨的自动化测试。还会频繁地重写系统,每隔几年,就把软件重写一遍。 + +再比如说 Facebook,Facebook 有一种黑客精神,创始人马克·扎克伯格有句名言是“Move Fast and Break Things”,也就是说快速做出产品,不要怕犯错。所以 Facebook 的工程师做软件开发的时候不会想太多,先实现再说,做出来就发布,哪怕可能有 Bug。发布后根据用户的反馈再不断完善,真的把线上功能弄坏了,再打补丁去修复。 + +然而这些带有各自文化特色的部分,却是我们很难学习借鉴的,因为这样的文化都只适合各自的公司。假设让微软去学习 Facebook 的黑客精神,发布带有很多 Bug 的 Windows 系统,那么用户是不能忍受的;而让普通公司去学 Google 的工程师文化,项目没有严格的 Dead Line,系统隔几年重写一遍,那公司恐怕都要撑不住了。 + +所以,要学习大厂,你要多去关注大厂们对软件工程实践共通的地方,可以应用在你自己项目的地方,另外还要去看大厂对软件工程实践的变化趋势,在朝什么方向发展。通常这些大厂的很多实践都是业界的风向标,一旦一些实践大厂都在应用,那么很多中小厂就会跟风,最终变成行业标准。 + +在上一篇《43 以 VS Code 为例,看大型开源项目是如何应用软件工程的?》中,我从项目的开发迭代过程,团队的角色分工和项目开发各个阶段来分析了 VS Code 对软件工程的应用。类似的,我也将从大厂的开发团队组成、开发工具的使用、项目开发流程这几个方面来分析一下大厂对软件工程的应用中,有哪些共同点?有哪些变化趋势?有什么地方可以借鉴? + +软件项目开发团队组成 + +软件项目开发,最终要落实到“人”上面。大厂在招人方面一向舍得投入,不仅花很多人力财力在招聘上面,同样对于员工待遇上也很大方,只为了能招到最优秀的人才。 + +对于普通的公司和团队来说,很难像大厂那样有一群行业内顶尖的人才,但是它在软件团队的一些管理和实践方面,还是有一些共通的地方,有值得普通公司学习借鉴之处。 + +1. 软件开发团队规模小 + +网上曾有一张流传甚广的关于各大公司的组织结构图。 + + + +(图片来源:HOW YOUR STARTUP’S ORG CHART CHANGES YOUR PRODUCT) + +这张图形象生动的描述了各大公司的组织结构,各具特色。然而这些大厂的组织结构具体细分到软件项目开发团队的时候,却惊人的相似:那就是一个软件项目开发团队都不会太大,一般不会超过 10 个人,如果超过就会被分拆。最著名的就是亚马逊的“两个披萨原则”,也就是团队的人数不应该多到让两个披萨不够吃。 + +其实大厂的软件项目都采用小团队的原因很好理解,那就是团队规模越大,交流就越复杂,成本也越高!要想沟通更高效,那么就要求团队的规模必须足够小。 + +组织架构的小型化也会对软件架构有影响,通过架构的隔离,让各个不同的团队可以在一起高效地协作。有在谷歌 YouTube 工作的朋友跟我说,YouTube 的 App,其中一个导航菜单,都是一个专门的小团队在维护。 + +如果你所在团队规模大,沟通效率不高,那么可以考虑向大厂学习,分拆成小团队,可以有效提高沟通协作的效率。 + +2. 没有专职测试 + +在我们专栏文章《32 软件测试:什么样的公司需要专职测试?》中,探讨了专职测试这个话题,而现在像微软、谷歌、Facebook、阿里巴巴这些大厂,都没有专职的测试人员。 + +但没有专职测试人员不代表他们不重视质量,只是他们在用更高效的方式来代替人工“点点点”的手工测试。就像专栏文章中介绍的,Facebook 能做到没有专职测试人员,是因为他们有大量的自动化测试;另外,Facebook 在功能发布之前,先在内部使用,上线之后能做到有效监控,出现问题能随时回滚或者打补丁。 + +大厂替代专职测试的这些手段,对于普通公司来说,可能现阶段去实施是有难度的,但是随着这些发布、监控工具的不断普及,自动化测试的普及,开发团队不设置专职测试会逐步变成一种趋势,现在的手工测试将来也许会被逐步淘汰。 + +3. DevOps 文化 + +在我们专栏文章《36 DevOps 工程师到底要做什么事情?》中,有过对 DevOps 进行探讨,DevOps,本质上就是一种紧密协作的工作方式。 + +早些年像微软这样的大厂,工程师团队有三种角色:项目经理,开发人员和测试人员,而运维团队则是工程师团队的另一组人。虽然好处是分工更明确,但是久而久之也造成了不同工种之间的隔离,尤其是各自目标不一致导致的利益冲突。 + +所以微软也在前些年进行了转型,将运维团队合并到了工程师团队,运维人员和开发人员协作更加紧密了,有效提高了编码效率,质量和产量。 + +除了微软,其他大厂也纷纷采用了类似的 DevOps 转型和实践。这里有两篇关于谷歌和阿里巴巴的 DevOps 实践文章可以参考:《孙宇聪:来自 Google 的 DevOps 理念及实践》《阿里研究员毕玄谈应用运维体系的变迁,DevOPS 是大势所趋》。 + +如果你的团队也存在不同工种之间协作的矛盾和冲突,不妨借鉴一下大厂对 DevOps 的实践。 + +开发工具的使用 + +大厂都爱自己造轮子,对开发工具也是如此,都有一个专门的部门去做内部工具的开发和维护。 + +如果你有幸在一个大厂工作,那么你会很幸福,基本上开发过程中,各种像编译、部署、持续集成等等都有好用的工具可以帮助你自动化,提升效率,你只要专注于写代码就好了。然而一旦离开大厂,你会发现这些日常工具都要自己去搭建,甚至得自己去写。 + +但好在大厂用的这些主要工具,你在网上几乎都能找到开源的或商业的替代品。只是没有那么好用罢了。 + +比如说谷歌一名前员工在 GitHub 上分享了他在谷歌工作时,日常会使用的一些工具,以及外界对应的替代方案(工具和替代方案)。再比如微软和阿里巴巴都将自己的工具(Azure DevOps 和 阿里云 DevOps)做成了服务供第三方使用。 + +有一点倒是可以看得出:这些大厂舍得在工具上投入。应用工具,也确实可以有效地提升效率,改进软件项目质量。 + +关于工具,我们专栏在各个章节也都有介绍,建议可以学习下大厂,把这些工具用起来,帮助你更好地完成项目。 + +项目开发流程 + +各个大厂基本上都没有规定必须要用什么开发模型或者不允许用什么开发模型,各个开发团队都可以自行决定采用的开发模型。 + +所以你会看到有的团队是敏捷开发,有的团队是快速迭代,甚至有的团队还用的是瀑布模型。但他们在项目开发中有很多共通之处。 + +1. 迭代周期短 + +即使是像微软这样,以前要几年才发布一个版本软件的公司,现在也加快了迭代。现在 Windows 10,每半年就会更新一个大的版本,每天都会发布可以测试的版本。 + +上一篇介绍的 VS Code 的开发,也是每个月就会有一个大的版本发布。还有像谷歌的 Chrome 浏览器,也是每 6 周发布一个新版本,如果某个新功能还没准备好,那么放到下个版本发布。 + + + +(图片来源:Chrome 发布周期) + +如果你的项目需要半年以上的开发周期,也要考虑一下,是否可以缩短开发周期,快速迭代起来。 + +2. 严格的开发流程 + +其实我在专栏已经反复地、苦口婆心地讲了很多开发的流程,比如说基于分支开发、代码审查、自动化测试、持续集成等等,希望大家能在实践中去应用这些好的实践。 + +然而在大厂,这些开发流程基本上都是硬性要求: + + +要基于分支进行开发新功能或者修复 Bug; + +要遵守公司或者团队的代码规范; + +合并之前要有至少一个人 Review 通过; + +要写自动化测试代码,并且保证所有测试用例通过。 + + +在谷歌的卫生间里面,甚至会张贴着有关 Testing on the Toilet 的贴纸,让你在去卫生间的时候还能学学怎么写测试,好让你的 PR 能早点通过审查。 + + + +(图片来源:Testing on the Toilet) + +3. 严谨的测试流程 + +虽然大厂都没有专职测试,但是测试可不含糊,都有一套严谨的,并且行之有效的测试流程。 + +以谷歌的 Chrome 浏览器为例,除了自动化测试以外,每个 Chrome 的版本发布之前,都要经历以下几个版本。 + + +金丝雀版本(Canary Channel): 过去煤矿工人要下井会带着金丝雀,这种鸟对危险气体的敏感度超过人。如果金丝雀死了,矿工便知道井下有危险气体,需要撤离。金丝雀版本会频繁发布,但并不太可靠,就像金丝雀一样用来第一时间发现严重的问题。 + +开发版本(Dev Channel):工程师日常使用的版本,一边开发一边使用,让工程师可以第一时间验证自己开发的功能。 + +测试版本(Test Channel):给内部员工的版本,就像上一篇 VS Code 介绍的 Eat your own food,自己人先试用。 + +Beta 版本或发布版本(The Beta Channel or Release Channel):是给外部用户使用的测试版本,并不保证稳定,但是用户可以提前体验新功能,也能帮助开发团队及时发现 Bug。 + + +类似的,如果你看 Windows 10 的发布流程,也是这样一个一个的测试版本的测试流程,最后正式发布的版本已经是经过千锤百炼,反复测试过的。 + + + +(图片来源:微软邹欣:Hit refresh 背后的软件工程革新) + +4. 完善的发布和监控流程 + +就算经过完整的测试,也不能保证质量就是可靠的。所以大厂们还会配合一套完善的发布和监控流程。 + +发布前,先评估风险,增加相应的监控数据和设置报警的阈值。制定出现问题的应对方案。 + +上线后,先推送一小部分用户,并同时进行线上数据的监控,如果没有发现异常,自动加大比例,直到完整覆盖;如果发现异常,自动报警通知相关负责人,上线处理,并直接关闭新功能。 + +有关上线发布和数据监控的内容,你也可以参考专栏文章《35 版本发布:软件上线只是新的开始》和《38 日志管理:如何借助工具快速发现和定位产品问题 ?》中的更多介绍。 + +5. 事后总结,不断改进 + +在专栏文章《39 项目总结:做好项目复盘,把经验变成能力》中,提到了项目复盘的重要性,以及如何做好项目复盘。 + +对于大厂来说,复盘也是整个项目开发过程中很重要的一部分,正是因为有这样一次次的“事后诸葛亮”会议,才让团队成员能从中总结成功经验,吸取失败教训。 + + + +(图片来源:微软邹欣:Hit refresh 背后的软件工程革新) + +参考阅读 + +其实,大厂的软件工程实践,网上有很多相关的文章,这里我将收集的一些内容在这里分享一下,供参考阅读: + +《Google 公司的软件工程之道》 + +《软件工程在微软的演化——邹欣》 + +《解密 Facebook 产品的开发流程》 + +《微软、谷歌、Facebook、Amazon 软件质量控制实践》 + +《微软开发团队的 DevOps 实践启示》 + +《-The Facebook Mobile Release Process》 + +《敏捷开发,你真的做对了吗?阿里文娱广告团队敏捷实践总结》 + +《如何在 2 周内交付 85% 以上需求?阿里工程师这么做》 + +总结 + +现在业界顶级的互联网或者软件公司,他们都对软件工程都有非常好的应用和实践,这也是他们能跻身成为顶级互联网的一个不可或缺的前提。通过学习和观察大厂的软件工程实践,能帮助我们拓宽视野,提升软件工程知识水平。 + +学习大厂,要多去关注大厂们对软件工程实践共通的地方,以及可以应用在你自己项目的地方。 + +在团队管理方面,大厂的软件项目团队规模都被拆的比较小,这有助于团队成员之间的沟通协作;没有专职的测试人员,测试工作被自动化测试代替;有很好的 DevOps 文化,各个工种之间紧密协作。 + +在开发工具方面,大厂都很重视工具的开发和使用,很多工具我们也能找到替代品,或者直接使用大厂提供的工具服务。 + +在项目开发流程上,大厂有严格的开发流程,代码必须写自动化测试代码,自动化测试通过,并且有人 Review 通过的 PR 才能被合并;大厂虽然没有专职测试人员,但是整个测试过程很严谨,在发布前都经过了充分的测试;大厂对于软件发布也都有完善的发布和监控流程,不仅可以快速发布,还可以及时发现线上问题;大厂也会有上线后的复盘总结,总结成功经验,吸取失败教训,将项目经验变成团队能力。 + +从大厂对软件工程实践中,你可以学习到一个优秀的公司是如何来应用软件工程,打造出高质量产品的,也可以借鉴其中好的实践到你自己的项目中。 + +最后你要清楚,即便是大厂,对软件工程的应用也不是一成不变的,会随着技术的发展、软件工程的发展不断改进。比如说微软早些年就使用的类似于瀑布模型的开发模式,现在也变成了敏捷的快速迭代的开发模式。这种不断学习改进的方式,也值得我们大家学习和思考。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/45从软件工程的角度看微服务、云计算、人工智能这些新技术.md b/专栏/软件工程之美/45从软件工程的角度看微服务、云计算、人工智能这些新技术.md new file mode 100644 index 0000000..c5175fb --- /dev/null +++ b/专栏/软件工程之美/45从软件工程的角度看微服务、云计算、人工智能这些新技术.md @@ -0,0 +1,141 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 45 从软件工程的角度看微服务、云计算、人工智能这些新技术 + 你好,我是宝玉。这些年来,新技术新概念层出不穷,比如说微服务、云计算、人工智能等。你有没有去学习和了解这些新技术呢?又是怎么去理解这些新技术的呢? + +也许你会从技术的角度,去学习和理解这些新技术,去看如何把服务分拆,看如何应用虚拟化、容器技术,如何用人工智能切页面。 + +这些新技术可能会让你很兴奋,毕竟又有很多新知识可以学习和应用;但另一方面也可能会增加一些困惑,比如说: + + +我该不该在项目中使用微服务? + +在设计微服务架构的时候,服务拆分的粒度该多细?该拆成 10 个服务还是 100 个? + +云计算对我的项目会带来什么影响?我应该怎么应用? + +人工智能会代替我写程序吗? + + +如果只是从技术角度思考这些问题,难免会陷入技术之中,反而不容易看清楚这些问题。在我们专栏一开始《02 工程思维:把每件事都当作一个项目来推进》这篇文章中,我就提到了工程思维的概念: + + +工程思维,本质上是一种思考问题的方式,在解决日常遇到的问题时,尝试从一个项目的角度去看待问题、尝试用工程方法去解决问题、站在一个整体而不是局部的角度去看问题。 + + +在学习使用这些新技术的时候,你不妨从项目的整体,从软件工程的角度来理解这些技术,这能给你带来不同的视角。那么怎么从软件工程的角度去理解呢? + +我想你应该对我们专栏上两篇文章有印象,我分别从团队、项目过程、工具这几个维度,分析了开源项目和优秀公司对软件工程的应用。类似的,你也可以跳出技术之外,从软件工程的角度来理解微服务、云计算、人工智能这些新技术和概念。 + +软件工程中技术架构和组织架构的关系 + +首先我们来看看微服务,你可能第一反应就是:它是一种架构技术。没错,从技术角度来看,微服务就是一种架构技术。经过对我们专栏的学习,我相信你对架构应该不会陌生,比如:前后端分离架构、微服务架构。 + +不知道你有没有观察过:通常系统架构和组织架构是相似的。比如说前后端分离的架构,那么在组织上一般也会分前端组和后端组;而微服务架构,则分组是和服务相关的,可能一个组就是负责一个微服务。 + +其实组织架构和技术架构相似这个现象不是偶然的,这个现象背后有个定律叫康威定律 (Conway’s Law)。康威(Melvin Conway)博士在 1967 年提交的一篇论文《How Do Committees Invent?》中最有名的一句话是: + + +Organizations which design systems are constrained to produce systems which are copies of the communication structures of these organizations. — Melvin Conway + + +如果对这句话翻译一下,它的意思是: + + +你设计的软件系统架构,会以某种方式反映出构建软件背后团队的组织架构,你在设计软件的系统架构时,同时也在设计你的组织架构,反之亦然。也可以简单理解为:组织架构的设计等同于系统架构的设计。 + + +如果你拿康威定律去验证你现在的团队组织架构,或者你熟悉的其他团队的组织结构,你会发现运行良好的项目,都很好地符合这条定律。那些大型复杂的单体软件系统,背后也对应着一个庞大的开发团队,那些应用微服务的项目,背后都是一个个的小组。 + + + +(图片来源:Conway’s Law) + +看完康威定律再回过头来看微服务,你会发现,微服务架构的设计,不仅仅是一个对服务拆分的架构设计,同时也是对组织架构拆分的设计。 + +当你在做架构设计,在考虑你的微服务拆分粒度的时候,不妨先想一想:你团队的组织结构是什么样的?真的大到需要用微服务了吗?你能按照微服务的设计去重新设计和调整你的组织结构吗? + +当你在设计系统架构的同时,把组织架构的设计也考虑进去,很多问题也就迎刃而解了。比如说你开发团队 30 个人,要使用微服务的架构,那么拆成 3~5 个微服务是比较合适的。因为每个小组 10 个人左右,每个小组维护 1~3 个微服务,是相对比较合适的配比。 + +然后你再看那些应用微服务失败的案例,比如说一个小开发团队,做出 100 多个微服务的架构,那团队维护这些服务的成本一定是相当高的,最终会难以维持。就像这篇文章:《再见微服务,从 100 多个问题儿童到一个超级明星》,虽然文章里面没有提到团队的组织结构,但是可以想象,这 140 多个微服务背后一定没有 140 个小团队,哪怕每个团队只有 10 个微服务,对团队来说也是有极大维护成本的。 + +还有一些传统大型企业,团队构成是按工种划分成不同团队的,开发一个团队、测试一个团队、运维一个团队,那么推行微服务阻力会非常大,因为这样的组织结构和微服务的组织结构是不兼容的。 + +对于微服务的组织结构,需要按服务划分团队,团队成员有开发、测试和运维,一起组成一个小团队,围绕着服务不断迭代,这样效率是最高的。 + + + +(图片来源:微服务写的最全的一篇文章) + +如果以后又出来什么新的概念和技术,你不妨从软件工程的角度,去看看它和组织结构的关系。比如说这些天网上开始流传一个新的概念叫:微前端(Micro Frontends),在我看来,这说明现在开发的重心在往前端倾斜,前端团队越来越庞大,需要分拆了。 + + + +(图片来源:Micro Frontends) + +新技术改变了软件工程中的分工协作 + +我们再来看看像云计算和人工智能这类技术,也许你会觉得它们代表了很多激动人心的高端技术,比如虚拟化、自动化、智能化等等。但这些新技术不仅是技术上有突破,更是对软件工程的开发过程,对分工协作都产生了深远的影响。云计算通过标准化的服务简化了开发的难度,人工智能和自动化在逐步替代项目中的一些手工操作。 + +在我大学上软件工程课的时候,老师跟我们说:“在建筑工程,用一些标准的模块,比如各种建筑材料组合在一起,就可以完成复杂的结构。这种标准化的结构可以极大地降低建造成本。希望未来软件工程也能像建筑工程一样,用一些标准的模块和组件,也可以构建出复杂的软件。”然而在十多年前,这个目标还是挺遥远的。 + +十多年前我办过网站,自己写程序,程序写完后要自己去买服务器放到机房托管,每年要给机房交托管费,还要定期数据备份,给服务器杀毒,装防火墙防止 DDos 攻击这种事情。万一服务器宕机了,需要给机房打电话,会有人帮忙重启一下,如果不行就得自己去机房了。 + +那时候的网络访问还有南北隔离的问题,也就是说你服务器放在南方电信,而用户是北方网通的,那么访问速度就特别慢,想要速度快就要用 CDN(Content Delivery Network,即内容分发网络),以保证访问速度。但当时 CDN 的价钱不是普通用户能承受得了的。 + +可以说我办个网站是操碎了心。这也是很多中小企业早些年自己开发运行软件系统的写照:需要兼顾开发、测试和线上运维,什么事情都需要自己做。 + +但是现在,如果我再要去办一个网站,我不会再自己去买服务器托管,而会选一家云计算服务商,将我的程序放在云服务商运行,出问题了就重新部署或者再新开一个虚拟服务器。数据库我也不会自己去安装维护,直接用云数据库,这样省去了数据备份的烦恼。 + +就算是程序开发,我也不会所有功能自己实现,比如文件存储我会直接用七牛云之类的云存储服务,还能使用 CDN 服务。网站内容的搜索我也会考虑阿里云的文档检索服务,如果有手机 App 消息推送,直接用云厂商的推送服务。 + +早些年像语音识别、图像识别、地图导航这些高精尖的技术,普通中小公司是没有机会去使用的,或者要付出昂贵的成本。而现在这些高端技术,都有服务提供。如果你有一个好的想法,不用担心技术会限制你的想象力,借助这些服务,你可以实现你的想法。 + +早些年的开发团队,服务端比前端人数要多,因为那时候界面简单,而后端需要实现很多数据库增删改查的逻辑。现在的趋势是,界面越来越复杂,而后端服务越来越强大,借助一些云服务甚至不需要去写程序,就能实现服务端 API 供前端调用。比如我曾开发过一个微信小程序,后端用的LeanCloud的服务,不需要写后端代码就有一个不错的后端 API 服务。 + +如果你从软件工程的角度去看云计算,它本质上是在将那些与业务无关的,而又很重要的基础设施、技术,作为一种标准服务提供,让你在软件开发时,只需要专注于业务所独有的部分,从而可以极大地减少开发工作,提升开发效率。 + +随着云计算的普及,软件工程的标准化、模块化也慢慢出现了一线曙光,希望未来构建软件系统,也能像盖房子一样,通过标准化降低开发成本和难度。 + +人工智能是另一个现在很火的技术,Alpha Go 在围棋上战胜了人类,无人驾驶也有了突破。另外,人工智能在软件工程领域,也有了一些应用和尝试,比如说微软开源了一个人工智能的项目叫Sketch2Code,可以把 UI 设计草图转成 HTML 代码,也就是一部分开发工作未来也许可以被人工智能替代了。 + +阿里巴巴在尝试智能化运维:《阿里智能运维平台的演进:从自动化到无人化》,也就是将人工智能应用在运维领域,从而让人工智能去替代人工的很多操作。在测试领域,虽然还没有见到有成功的用人工智能替代人工测试的案例,但是自动化测试替代了大量手工测试是一个可见的趋势。 + +现阶段,这些应用只是一个开始,但不会是结束,未来会有更多云服务、人工智能在软件工程领域的应用,会对软件开发的分工协作产生更多影响。 + +但云服务、人工智能再强大,也难以替代那些创造性的劳动,也就是那些你业务和项目所独有的东西,比如说你对业务的抽象和设计,测试用例的设计,对整个项目过程的组织。 + +在软件工程中,技术是工具 + +对于像微服务、云计算、人工智能这些新技术,如果站在技术角度看,技术人员永远有两种态度:拥抱新技术和抵触新技术。 + +但如果你站在软件工程的角度去看技术:技术服务于架构设计,架构设计服务于业务,业务服务于商业。也就是本质上来说,技术是为项目服务的工具。 + +做一个项目,首先是要去解决一个商业问题,比如说你要网上卖东西。然后基于这个商业问题,你要设计一个业务,比如做一个在线商城系统。当你确定了你的业务,你再去设计出适合这个业务的架构,比如设计一个三层架构。最后架构设计好了,你再去选择适合这个架构的技术,比如 PHP+MySQL。 + +但现实中常常不是这样的,开发人员学会了微服务的技术,就像有了一个锤子满世界找钉子,所以当你需要一个在线商城系统,他会给你按照微服务搭一个架构出来,也许你只要一个简单的 PHP+MySQL 系统就足够了。 + +或者说最开始你的架构就是简单的三层架构,能很好地满足当时的需求,然后业务不断壮大,于是服务越来越大,团队也越来越大,沟通成本非常高,非常有必要对团队进行分拆。那么这时候微服务就是适合你的架构,而你的技术负责人不懂微服务,也很抵触微服务,就不太可能推动这样的转变。 + +对于这些新技术,如果只是从技术角度去看,就会更多考虑这个技术喜不喜欢,酷不酷,难不难学,而不容易考虑到如何更好地去为架构服务。 + +但要是从软件工程的角度,就会把技术当作工具,去学习了解这些新技术,然后进一步思考:这个技术能解决什么问题?应用在项目中有什么样的优缺点? + +当你不仅仅是从技术角度去看这些新技术,而是能同时站在软件工程角度看这些新技术时,就能真正的让技术去为架构服务,让架构去为业务服务,从而帮助业务产生好商业价值。 + +总结 + +不管是现在还是将来,你总是免不了要去面对新技术。从技术角度去看新技术,也许你会兴奋,也许你会抵触,但是如果你跳出技术角度之外,站在软件工程的角度去看新兴技术,你会有不一样的收获。 + +技术架构等同于组织架构,当你在设计系统架构,你同时也在设计你的组织架构,反之亦然。当你纠结微服务的拆分粒度,不妨看看你的组织架构是不是能和微服务架构匹配。 + +云计算、人工智能这些新兴技术也逐步改变了分工协作,云计算这样的基础服务,可以降低开发成本,让你可以专注于业务开发;人工智能和自动化技术的发展,也逐步替代了原有的像手工测试、手工运维的工作。但对于创造性的劳动,例如业务的设计和抽象,测试用例的设计和项目过程的组织,是不太可能会被替代的。 + +最后,技术是工具。技术服务于架构设计,架构设计服务于业务,业务服务于商业。对新技术,保持学习和了解,知道新技术能为你解决项目中什么问题,就像工具一样,选择合适的技术,让技术为架构服务。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/一问一答第1期30个软件开发常见问题解决策略.md b/专栏/软件工程之美/一问一答第1期30个软件开发常见问题解决策略.md new file mode 100644 index 0000000..eed30a4 --- /dev/null +++ b/专栏/软件工程之美/一问一答第1期30个软件开发常见问题解决策略.md @@ -0,0 +1,570 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 一问一答第1期 30个软件开发常见问题解决策略 + 你好,我是宝玉。专栏已经上线更新一段时间,看到同学们对软件工程有了更深刻的理解和思考,我很受感触。 + +有些人说走了很多弯路,日常搬砖,增删改查;也有的同学跟我一样“野路子”程序员出身,非常困惑希望建立自信;还有的同学发表上千字的学习心得,可以说非常用心。 + +编码的最终目的还是为了实现一整个软件的开发,在程序员的晋升之路上,总有单独挑大梁负责项目的时候。你会发现软件开发中的很多问题,都是可以通过软件工程的知识来解决的。 + +在已经更新的文章中,同学们经过思考,结合自己的工作实践场景,提出了非常好的问题。我们专栏的留言内容成为了专栏最好的补充。于是我就将留言板中的答疑和精彩留言进行汇总,方便你更好的查阅和理解专栏内容。 + +一问一答 + +No.1 + +hua168:学这个专栏需要哪些基础知识为前提的?开发都要学哪些基础东西? + +宝玉:学习这个专栏,不需要你有特别的基础,当然有一些项目经验可以帮助你更好的理解。至于要学什么基础的东西,其实你可以从另一个角度思考一下:开发的价值是体现在哪的? + +开发的价值是通过在项目中创造价值体现的,所以你要考虑学什么能帮助到你更好的在项目中创造价值。比如说除了具体的编程技能外,还可以从这些方面思考: + + +提升对需求分析和理解的能力,这样你就知道要做的是什么,减少返工; + +提升架构和抽象的能力,能把需求抽象成架构设计,能把复杂的问题通过架构分解成简单的问题; + +高效率的编码,完成需求,等等。 + + +No.2 + +hua168:软件工程在游戏项目上,是不是也一样呢? + +zhxilin℃:对游戏行业的过程模型有什么理解? + +宝玉:万变不离其宗。游戏项目一样离不开软件工程,游戏开发本身也是软件开发,只是有些名字换了,比如产品经理变成了游戏策划,产品设计变成了游戏策划案。游戏开发一样要有需求分析、架构设计、编码、测试等关键活动。只是游戏项目的需求变更频繁、节奏快,用增量或者迭代要好很多!另外也可以试试敏捷开发。 + +No.3 + +于欣磊:现在已经进入云计算时代,基本上大中小企业都在上云,复杂逻辑都在云端处理,真的还需要软件工程里讲的开发要搞这么多流程么? + +宝玉:是的,云计算的兴起可以减少很多劳动,但不代表你就什么都不用做了,还是要做需求分析,再去做架构设计,做完架构设计你才能清楚哪些可以用云计算,那些需要自己去实现。最后编码完了,一样还要测试的。 + +No.4 + +hua168:现在运维的前景怎么样?感觉竞争很激烈,很多小公司都不招,开发兼职了运维,各大云出了一些维护监控工具,对他们来说够用了,感觉发展空间变小了,运维开发招也少了……也有人提到运维职业会消失,难道要转开发?那运维开发能力也争不过真正的开发啊。 + +宝玉:你这个问题很有代表性,现在云服务兴起后,传统运维的职位在减少,所以 DevOps 在兴起。DevOps 和运维的主要差别就是 DevOps 不仅有运维能力,还有开发能力,可以站在运维和开发更高的角度去看问题,帮助自动化的,稳定的交付部署产品。你不用完全转开发,但是应该要学习一些开发知识,尤其是自动化脚本相关的。 + +No.5 + +行者无疆:传统瀑布模型前期进行了完整的需求评估,在技术选型,系统架构,实施路径上可以做好全面的规划,虽然周期长,不必要的反复工作会少很多,目标也更容易控制。 + +那敏捷模型的迭代方式并不会把需求都考虑全面,未来的迭代可能会造成前面的技术架构或者实施细节等都不能满足新需求的要求。所有工作都要重来的问题,会存在大量的重复工作和资源浪费。 敏捷模型是如何有效地规避这些问题的呢? + +宝玉:你说的问题确实存在,导致常说的技术债务问题,所以需要定期去重构,改进这些问题。迭代过程中的重复工作确实存在,但是软件开发中的浪费其实主要不是在于迭代过程中的重复工作,而是在于需求不明确和需求变更导致的返工或失败。 + +敏捷开发持续发布稳定版本的理念还是利大于弊。有一些项目其实是瀑布模型和敏捷开发的结合,需求分析和系统设计的时候用瀑布模型,开发和测试阶段用敏捷,也是个不错的选择。 + +No.6 + +Geek_85f782:如果说软件工程 = 过程 + 方法 + 工具,其中过程是否就是具体指软件的生命周期?方法是指选用的生命周期模型,比如瀑布、螺旋、迭代、敏捷? + +宝玉:我认为过程应该包含过程模型采用的方法。而方法是指基于过程模型之下的方法。因为过程模型决定了软件开发过程是什么样的,进而决定了采用什么开发方法。 + +比如你选择了瀑布模型,整个软件开发过程就是按照瀑布模型的分阶段来进行,对应的方法就是瀑布模型中的方法,例如需求分析、架构设计;如果你选择了敏捷开发,则整个开发过程就是一种敏捷迭代方式,后面的方法对应的就是敏捷开发的一套方法体系,例如 Scrum、用户故事、持续集成等。 + +No.7 + +hua168:中小公司,开发人员流失严重,如果像工厂流水线那样,即使核心开发人员全部走了,新招的开发在没有人带的情况下,能继续接上开发……除了制定规范,开发文档之外还有哪些措施? + +宝玉:软件开发,核心就是人,如果没有人,规范和文档都没意义的。要留住人,一个是得舍得给钱,另一个得有个好的环境,还有就是要有梯队,能把新人培养上去。饭店里只有一个大厨,大厨当然敢乱提要求,如果大厨多几个,就不担心了。还是得要舍得下本钱招优秀的人。 + +No.8 + +白发青年:如果是外包项目,作为项目的乙方,如果采用敏捷开发,最初的工作量就很难完整估计,不利于双方的合同签订。不知老师是否有好的建议? + +宝玉:这个问题通常有两种解决方案供参考: + + +你按照瀑布模型的方式去估算工作量,然后签订合同。开发的时候你需求分析和架构设计还是用瀑布模型的方式,但是编码和测试用敏捷开发。这是一种不错的折中方案; + +你把所有需求拆分成用户故事,对用户故事进行打分(了解下计划扑克之类的打分方案),然后可以算出来一个总分数。另外按照你以前敏捷开发的经验,可以知道每个 Sprint 大概能完成多少分,这样你就能大致推算出来工期。 + + +No.9 + +Charles:瀑布模型非常考验人的能力,会造成互相扯皮推卸责任,上线以后有什么问题,还会互相推锅背,这种情况下管理者有啥好的方式去解决? + +宝玉:虽然我觉得甩锅不是什么好事,但是如果你真要甩锅,最简单有效就是设置流程去划分责任。上线后有问题其实很正常的,重要的是要有合理的机制: + + +及时发现问题,监控报警、用户投诉反馈等; +马上解决问题,对线上版本有专门的代码分支,可以随时打补丁修复,测试上线; +避免后续再犯同样的错误。要分析原因,看什么导致问题,然后改进流程。 + + +No.10 + +Linuxer:小公司有很多项目就是一两个人,没有那么多角色,怎么做到按这种流程去开发项目呢? 比如常常在代码编写过程中发现很多问题都没考虑全面又感觉在交流需求的时候根本就没想到,要怎么在之后的项目中不再犯这种问题呢? + +宝玉:即使只有一个人,建议也要做简单的需求分析和设计,做完后,形成简单的文档,找人评审一下,提一些意见。因为你写文档的过程,给别人讲的过程,其实是在帮助你思考,帮助你梳理清楚逻辑,避免在实现的时候发现好多问题没想清楚。 + +还有一个思路就是快一点迭代,每一个迭代解决优先级最高的问题,然后下一个迭代中改进上一个迭代的问题。项目中犯错误其实很正常的,重要的时候要总结,看看通过什么方式能改进,避免犯类似的错误。 + +No.11 + +bearlu:是不是每种模型,有其应用场景,不能只追求最新,要适用才行? + +宝玉:你说的太对了!举个例子来说,敏捷开发肯定又新又好,但是如果成员没一个懂敏捷开发,强行照葫芦画瓢,可能结果还不如用瀑布模型。 + +No.12 + +clever_P:瀑布模型难以快速响应需求变化,那有没有可能通过对业务领域的深入研究,对业务的发展和变化做出一些前瞻性预测,在软件设计的时候将这些预测考虑进去,以此来减小后期需求变化对整个项目的影响呢? + +宝玉:你说的是一个方向,但是要预测其实是很难的,结果很可能是过度设计,设计了很多可能最终完全用不上的架构,反倒不如快速迭代快速响应来得实际。 + +No.13 + +一年:开发一款测试市场反应的产品,使用快速原型模型是不是好点呢? + +宝玉:恐怕不行,因为快速原型模型是牺牲质量的。质量差的软件,去测试市场,你不知道是因为质量问题不行还是需求没抓住不行。这种情况,可以考虑用迭代模型,先开发核心需求,然后再逐步迭代。 + +No.14 + +凯纳软件:感觉自己之前做任何事情都没有章法,觉得只要做了就可以。通篇学完之后,知道自己哪里欠缺,应该怎样去学习及工作。 + +宝玉:谋定而后动。还有一点经验就是:如果你想更有章法,更有大局观,做一件事情前先做个计划,可以帮助你更好的思考,也更容易执行。 + +No.15 + +Joey:我们公司是比较大的国企(多个业务部门对一个开发部门),对质量要求较高,现在业务条线也比较多,业务部门基本都嫌我们开发部门效率低,对于我们研发部门,组织架构还是按照瀑布模型设计的,开发模型基本是迭代 + 增量,如果想推行敏捷,肯定需要调整组织架构,一旦调整,就会触发一些利益关系,在这种背景下,有没有什么好的招数,既可以提高研发效率,又可以保证质量? + +宝玉:如果你想推行敏捷,可以先找个小项目,组个小团队试点,成了可以作为一个参考,领导可以去邀功,以后可以更大规模尝试;失败了也损失不大,领导也不用担责任。 + +不管用不用敏捷开发,你都可以学习其中好的实践,例如持续集成用起来,帮助你高效的集成部署;自动化测试代码写起来,帮助你提高项目质量;迭代快起来,以前 3 个月变成 1 个月,以前 1 个月的变 2 周。有些事情即使只是程序员都是可控范围内的,做着做着其实你就“敏捷”起来了。 + +No.16 + +一步:最小可行性产品 MVP 应该就是迭代开发了? + +宝玉:MVP 更多的是需求定义上的概念,和开发模型并没有关系。但是你使用迭代开发或者敏捷开发,必然要优先选择最核心最重要的功能需求先开发。所以通常 MVP 的方式选择核心需求,用迭代模型或敏捷开发开发需求。 + +No.17 + +龙哥:有依赖交叉的用户故事应该怎么做,比如用户系统的数据库该由谁搭建。毕竟注册、登录、修改这些都可能基于一个数据表。表字段这些需要统一,不能一个程序员改一次字段名吧 + +宝玉:敏捷开发中有一个迭代 0,也就是第一个迭代,就是做这些准备工作、基础架构搭建的。敏捷团队小,有个好处就在于遇到你说的这种情况,在做之前,大家都在一起开个小会一商量就可以定下来了。 + +No.18 + +阿神:敏捷开发里开发也要写集成测试用例吗,那么测试人员主要做手工测试? + +宝玉:对,开发不仅要写单元测试,还要写集成测试。但开发都是用模拟数据,假的 API。而测试的自动化测试会用真实的数据,调用真实的 API,而且也要做一部分手动测试。至于比例多少,还得看项目特点。 + +No.19 + +holylin:如果合同金额一开始就是根据商务阶段了解的情况评估的工作量而确定的,那么在合同执行过程中,如果按敏捷开发的思路,客户不断改需求我们不断地响应,然后工作量甚至已经超过了原先合同的金额,这个时候要如何处理? + +宝玉:这是个好问题,我对这个问题上没有什么经验,但我可以试着帮你分析一下。 + +你的合同是按照当时的需求签订的,如果后期客户变更需求或者增加新需求,那相当于需要重新签订变更这部分的补充合同。 + +应用敏捷开发的时候,你也可以让产品经理或者项目经理充当客户的角色,这样他们会更偏重产品需求的解读,而不是重新提出新的需求。还有一点,合同执行的时候,这时候你不需要太过于纠结是不是用敏捷还是迭代还是瀑布,而是哪一种开发模式,可以让你高质量高效率的完成,那就是最好的最适合你的开发模式。 + +No.20 + +长眉 _ 张永:作为一个电商 ERP 服务商,既要关注产品的研发进度,又要对产品做维护。人员一旦离职,发现没有较为详细的文档,就需要去猜测,之前的业务了。敏捷后上线,留下的技术债务应该归谁负责呢? + +宝玉:敏捷还是要写必要的文档,只是会简化。尤其是这种涉及交接的、维护的,文档不能省。技术债务应该团队成员集体负责,大家在迭代计划会上应该将技术重构列入后续的 Sprint。 + +No.21 + +刘晓林:敏捷开发这么强调扁平化,这么重视人,这么强调开放而弱化约束,那和最初没有软件工程时期的开发主要区别是啥呀? + +宝玉:好问题,你难倒我了。前面介绍过,没有软件工程的时候呢,开发就是边写边改模式,没有需求分析、没有架构设计、没有测试,就导致很多问题。 + +No.22 + +邢爱明:对于企业管理的软件,核心需求涉及多个部门,需要反复沟通确认周期很长,这种情况下是否还适合使用用户故事的方式做需求分析呢? + +另外,我按照瀑布开发模式的习惯分析,开发人员和 po 沟通需求后,如果没有文档作为输出物,在开发和测试的时候就没有标准,反而会造成工作返工。这是否意味着,团队成员需要高度的协同和配合? 以完成任务为导向,而不是强调各自的分工? + +宝玉:好问题!敏捷开发这种方式,需要客户紧密配合,也就是可以方便确认需求,否则还是少不了要写需求文档。另外我在文章中描述用户故事,有些描写不清楚或者歧义的地方,其实用户故事还应该包括验收标准,这样可以解决你说的开发和测试没有标准的问题。 + +团队成员需要高度的协同和配合那是一定的,尤其是架构和需求两部分。需求简化后,就意味着开发过程中需要反复沟通确认;没有专门的设计阶段,也就意味着每个 Sprint 开始前,团队要商量有没有要设计或者修改架构的,有就需要有个简单可行的方案对架构进行修改。如果各自分工,这样的目标就很难达到。 + +No.23 + +D:在敏捷开发过程中如何保证业务的传承?当有新同事加进来,如何让他快速的熟悉整个业务。 + +宝玉:这个是个好问题,也是个大问题!通常我的经验是: + + +团队要有自己的知识库或 WIKI,常用的知识要花时间整理上去,这样新人来了可以自己查; +先给他简单的任务,再慢慢稍微复杂一点,给予必要的指导,做中学是最快速有效的; +遇到一些典型的问题可以通过结对编程的方式带着一起做。 +仅供参考。 + + +No.24 + +dancer:对比瀑布模型来说,敏捷开发在需求分析和软件设计上要薄弱一些,这会导致越向后迭代,软件越难以变更和维护,请问老师有什么好的方法和建议吗? + +宝玉:需求分析是在 Sprint 进行中同步进行,也就是开发具体的用户故事之前要和客户或产品经理充分沟通了解需求。如果用户故事不是特别大,这并不是很大的问题。另外并非只能用用户故事,也可以用传统的产品设计文档代替用户故事,也一样是很不错的实践。 + +对于架构设计,架构只设计当前迭代的,所以迭代到一定阶段,是要考虑重构的。通常重构代码也是 Sprint 的工作任务的一部分。 + +No.25 + +Dora:瀑布对人员要求不高 (各自负责各自的工作,比如需求只管需求),而敏捷流程,一个人什么都要过一遍。这样理解,对吗? + +宝玉:瀑布对人员也不说要求不高,但分工确实更细一点,比如像你说的,需求只管需求;开发一般就不操心怎么测试,写完等着测试报 bug;敏捷开发里面,分工没那么细,需求不仅要写需求文档或者用户故事,还要和团队成员紧密合作,及时讲解需求;开发也要自己写很多自动化测试代码;敏捷团队也不是没有测试,但是会用自动化测试分担一部分测试任务。 + +No.26 + +Tiger:在敏捷里面,开发写自动化脚本测试,那是不是就不需要测试这个角色了啊?感觉在敏捷里面,只需要开发这一个角色就可以了啊? + +宝玉:在《07 大厂都在用哪些敏捷方法?(下)》我有谈到这个问题。自动化测试是辅助的,还是离不开人工的测试。而且开发写的集成测试和测试写的自动化测试还是有一点差别的,一个是用程序模拟操作的固定数据,而测试用的是真实的数据环境。举个例子来说,网页的自动化测试,开发只会用 Chrome Headless,数据都是事先写好的模拟数据;测试的话会用主流的 Chrome、Safari、Firefox、Edge 分别测试(自动化或手动),数据都是测试环境的真实数据。 + +No.27 + +一路向北:对于小公司小团队的项目,因为项目经理,产品经理都是身兼数职,是否有更好的实施方式呢? + +宝玉:项目经理、产品经理兼多个项目是正常的,也没大问题。但是让程序员同时兼做开发和项目经理工作就很不好,因为项目经理需要更多全局掌控,而一旦要花精力在开发上,很难跳出具体的开发工作,会极大影响项目管理工作;项目管理工作也会频繁打断开发,造成进度延迟。 + +所以我建议应该有专职的项目经理,不应该让程序员兼职项目管理。新旧项目交织并不是问题,可以放在一个项目一个 Sprint 里面一起管理,也就是同一个 Sprint 里面有维护的 Ticket,也有新需求的 Ticket,只要保证开发人员同一时间只是做一件事,而不要几件事并行,就可以最大化发挥敏捷优势。 + +No.28 + +天之大舒:怎样培养团队成员? + +宝玉:有一些建议仅供参考: + + +招人和开人都很重要,招优秀的,开掉没有责任心,没能力的。这两点都不容易做到,不过得坚持做; +设置合理的流程,配合一定的奖惩制度;你奖励什么,团队就会往哪方面发展; +团队要有梯队,不能都是资历浅的也不能都是资深的,保持一个合适的比例是比较健康的; +实战中锻炼,实战中磨合;给他们有挑战的任务,给予合适的指导(这就是有梯队的原因,需要高一级别的待低一级别的)。 + + +No.29 + +星星童鞋:请问老师,对于需求更新极快,基本上每周都需要迭代更新上线的项目,在架构设计和项目部署上会不会有什么特殊的要求? + +宝玉:架构设计上,一定要定期需要重构,优化设计,不然后续新需求效率会降低,包括代码上也会越来越臃肿。比如我现在所在项目组,每 1-2 年会有一次大的架构升级调整,日常每隔几周会有小的架构优化,这样基本上可以保证快速迭代不会受太大影响。 + +部署的话,一个是要自动化,可以快速方便的部署,另外一个部署后,需要有配套的数据监控和高于阈值报警的机制,因为上线后可能会有严重问题,需要及时发现,及时处理。 + +No.30 + +alva_xu:如果一个迭代里没有评审会,怎么知道我上线的系统是符合要求的? + +宝玉:没有评审会,但是有专职测试针对最初提的需求进行测试,另外产品经理也会验收,如果验收不合格会提交 Ticket。也就是说是有验收,只是没有专门的会议。 + +精选留言 + +阿杜: + +软件过程不是搞科研,不是搞艺术,而是解决多人合作将一个想法落地的学科,其中包括严谨的过程步骤、规范,用于提高效率或防范风险的工具。软件工程的主体是工程,这就要求我们具备基本的工程思维:模块化思维、抽象思维;具备一些关键的意识:质量意识、风险意识、交付意识。 + +相关阅读:01 | 到底应该怎样理解软件工程? + +alva_xu: + +对于大型系统的建设,可否用敏捷方法来实现,一直是个问题。 + +敏捷方法,适合于小团队(比如两个披萨团队)、小架构。对于大型单体应用的开发,至少在架构设计上是不适合用敏捷迭代方式的。 + +为了解决大型系统建设的迭代开发、快速交付问题,业内不断在探索。随着微服务架构的提出,以及容器技术的成熟,和 cicd 的实现,单体巨石应用被拆解成分布式的微服务应用,此时,敏捷方法也就开始真正大行其到了。 + +所以,微服务、容器、devops 这三剑客和敏捷方法一起,互为依存、互相促进,成为了软件工程中最有生命力的技术工具和流程,使软件开发在质量和效率上得到极大提升。 + +相关阅读:01 | 到底应该怎样理解软件工程? + +老张: + +在今天没有不可替代的硬件,却有无数不可替代的软件。硬件早已不是共享的壁垒,而曾经被认为有很强可塑性的却已经是最硬的壁垒。一台服务器、一块磁盘、一根内存以及交换机、防火墙等网络设备,更遑论鼠标、键盘、显示器,在冗余、复用、虚拟化等等技术之下,更换、替代、扩容如此之方便,经过简单培训的工人就可以轻松完成。 + +可是即便是美国国会图书馆,依然认为纸质是保存资料最好的方式,因为大量资料电子化后存放在不同介质,需要当时定制的软件才能读取这些格式。今天的软件就是这么硬。也许有一天,有人会写写如何开发真正的软件。 + +相关阅读:01 | 到底应该怎样理解软件工程? + +阿银: + +软件工程的本质在于工程。利用工程理论来保证高质量软件产品的产出。工程讲究效率,成本,质量,除此之外,容易忽略的是工作量与效益的权衡,这一点尤为关键。 + +相关阅读:01 | 到底应该怎样理解软件工程? + +hyeebeen: + +软件工程的产生源自于对高效产出可靠稳定的软件产品的需求,在各种“+”的现实生活中,不掌握合格的软件工程管理技巧,日常的项目工作会很容易有瓶颈。 + +学习对应的工程技巧,内化为自身素质,在项目过程中既能预防工程风险,也能建设面对风险的反应机制。这种人,各个企业都喜欢。 + +相关阅读:01 | 到底应该怎样理解软件工程? + +阿杜: + + +做任何事情都要按照一定的理论指导来,例如,依靠系统化、结构化的“工程思维”,将生活和工作中的每个事情都看做一个项目,可以提高做事的成功率和效率,虽然不用这些理论指导也能做成事情,但是相对来说是偶然性的,不是常规性的。这就是常说的认知(意识)先行,持有高级的认知去跟低认知的人竞争,是一种降维打击。 + +工程思维的核心有两点:系统化,也就是全局观,要从站在整个项目的高度去看问题,不能做井底之蛙;结构化,也就是有步骤、有节奏得做事情的意识。 + +《软件工程之美》这个专栏,我给自己定了一个小目标:全部跟完,并且坚持留言跟老师交流想法。我制定了简单的阅读步骤:(1)文章至少阅读两次,第一次通读,第二次做笔记摘抄、整理文章的思维导图、提出自己的想法;(2)整理自己的学习心得,形成阅读笔记发到自己的博客(公众号)上。 + +课后思考:我今年年初开始运营自己的公众号,我把它当做一个项目,就从下面几个方面进行了思考:我要提供的内容和定位是什么样的、我的用户是谁、我应该如何去运营;这些东西想好后,我就将要做的事情拆分为:公众号设置、文章内容输出、运营推广三块,然后按照一定的步骤去执行,现在公众号的设置已经基本完成,整个项目进入内容输出和运营推广的循环中了。站在项目的角度去看这个问题,可以让我在动手执行的时候更有方向感和节奏感,也会对自己清楚自己某个小的点做的改动会对全局产生什么影响;在没有使用这个角度去看问题之前,我只是简单得主张内容才是核心,但是不懂运营和推广,没什么章法。 + + +相关阅读:02 | 工程思维:把每件事都当作一个项目来推进 + +起而行: + +项目思维的两个关键在于: + + +注意局部任务与总体时间的关系; + +用熟悉的办法解决问题。 + + +以留学为例。 + + +在距离留学申请还有两年的时间,可以先做不确定性强,见效慢的事情,不是不重要,而且短时间内会来不及。比如未来职业规划,兴趣的培养,长期的科研与项目。 + + +等到了申请还有半年的时候 要注意局部的任务与总体时间的关系。那么确定性不强的任务效果将不会特别好,而刷语言考试的成绩,这种确定性强,时间可控,反馈见效相对快的事情就要提上日程。 + + +用熟悉的办法解决问题。在留学,这种高度信息不对称的领域,可以自己试着了解前人经验,但我认为,专业的事情给专业的人去做,那么找中介辅助申请就是个好的主意。 + + +相关阅读:02 | 工程思维:把每件事都当作一个项目来推进 + +alva_xu: + +工程方法就是有目的、有计划、有步骤地解决问题的方法,而工程思维就是用工程方法解决问题的思维模式。这种思维模式,首先要求有全局观。 + +而事实上,由于工程中的不同职责分工,导致各个角色有可能只从自己的分工角度去考虑问题,这实际上是软件工程中最大的障碍,也是传统的 CMMI(过程域的规范化)、现在的 DevOps 和敏捷方法想要去解决的问题。 + +正如《凤凰项目》中的观点,既要有自左向右的工作流,又要有自右向左的反馈流。通过人员和组织(自组织)的调整、工具和技术(CICD 工具)的使用以及流程(Scrum 等敏捷工作流程)的推广,确保整个工程项目不会被不同角色割裂开来,从而确保工程的实现。 + +相关阅读:02 | 工程思维:把每件事都当作一个项目来推进 + +纯洁的憎恶: + +工程方法不仅给团队提供了一系列成熟的理论范式与实践工具,提高效率与成功率。更重要的是把团队的视角“强行”抬到全局高度,避免我们紧盯着自己关心局部问题,更好的统一思想、形成合力。 + +相关阅读:02 | 工程思维:把每件事都当作一个项目来推进 + +hyeebeen: + +非常赞同“一切即项目”的思考模式,作者经历的培训活动我也经历过,确实在时间意识上会比没受过工程训练的人强一些,更注意过程控制。 + +ps:学习自动化测试其实不等于一定能缩短测试周期,“测试周期”的定义如果是测试独占的项目时间段的话,可以通过测试前移,加强自测,契约优先的接口自动化测试等来缩短独占时间。没有系统或者不够工程化的自动化测试脚本,反而会增加测试时间。 + +相关阅读:02 | 工程思维:把每件事都当作一个项目来推进 + +纯洁的憎恶: + +我对瀑布模型感触颇多啊! + + +瀑布模型把复杂的软件生产过程,按照时间线索,切分为若干较为独立和专业的部分,条理清晰。 + +在每个阶段内只需要集中精力于阶段任务即可,不用胡子眉毛一把抓。 + +每个节点有交付件,过程可控、权责清楚明白。 + + +瀑布模型特别符合我所在的大型央企的性格。但是我经手好几个项目,也被瀑布模型折腾的死去活来。比如我现在正在处理的项目。 + +首先,从可行性分析、立项、批预算、采购建设单位就花了一年多的时间。可行性分析做了 1 个月,立项流程走了不到 1 个月,批预算的时候,主管业务的领导变卦了,要求重新做可行性分析,于是我们又花了 1 个月。 + +二次立项的时候,主管信息的大领导突然决定要把区块链和人工智能等热门技术加进去,于是又要求重新立项。但是我们要做的事情实在和区块链八竿子打不着,死去活来的找了个理由扯上关系了,来来回回又花了 2 个月,这就半年了。 + +再次走到批预算的环节,主管领导发现原来的预算干不了这么多事情,又不同意增补预算,于是继续扯皮。经过多番协调,总算解决了,这时候叶子已经黄了。 + +我们立刻开展需求调研,但各个需求部门和最终用户都借口工作太忙不搭理我们,我们只好自己憋需求,简直是闭门造车。等需求憋出来了,大领导把需求部门都叫来议一议,结果被集体口诛笔伐。 + +大领导怒了,强令需求部门专门抽时间参与需求调研,各部门也是不情不愿啊,效果可想而知。需求总算审查通过了,就在我们准备采购实施单位的时候,国资委红头文件一直下来,公司的采购流程发生重大调整,项目被硬生生搁置下来。眼看年根了。 + +第一年就这么过去了。等来年采购流程也理顺的差不多了,预算又出问题了。去年批的预算只能去年用,不允许跨年。只好等到年中调整预算,又小半年过去了。采购流程走完,实施单位也很够意思,不等合同签订就投入工作。我们用极短的时间完成了软件设计,并且开始如火如荼的开发工作,此时又到了金秋。 + +就在这时,新的纪检书记上任了,他对我们的系统设计很不满意,要求相关部门限期整改,于是需求大调整,可是这会儿编码已经进行了 1⁄3 啦…之后就是上线日期一推再推,从 10 月初推到 10 月底,再推到 11 月、12 月,眼看又要跨年了。我们和实施单位连续半年 997,总算看到上线的曙光,这时候公司一把手退休了… + +新领导上任后对整个流程极不满意,否定了纪检书记的指示,于是我们又开始第 2 伦大调整。现在已经是项目的第三年了,我们依旧没能上线。整个团队都要累趴下了,全公司一点成果也没看见。 + +相关阅读:03 | 瀑布模型:像工厂流水线一样把软件开发分层化 + +纯洁的憎恶: + +稳定、可靠、一步到位的瀑布模型,不太适用于违约风险大、需求不明确、快速见效的场景。 + +快速原型模型:不见兔子不撒鹰。期初不考虑质量、架构,用最快的速度见效,并向用户确认需求。经过几轮直观、快速的反馈,把需求确定下来。接下来,既可以抛弃原型用瀑布精密重构,也可以在模型基础上完善。优点是快速有效地确认需求。不足难以有效应对后续的需求变更。 + +增量模型:分而治之。将大系统横向拆分成相对独立的若干小模块,每个模块采用瀑布模式分批次交付。优点是较快见到成果,且能够及时了解项目进展。不足是存在需求明确、系统可拆分、交付可分批等适用条件。 + +迭代模型:罗马不是一天建成。把软件项目纵向划分成若干阶段,从核心功能入手,逐渐深化、细化,直到满足用户的全部需求。每个阶段都是一个瀑布,都要在前一阶段成果基础上加工、打磨。优点是快速满足基本需要,并体会软件演进的快感。不足是需求演化具有不确定性,会导致代码冗余、系统重构风险、项目周期不可控。 + +我做甲方管过不少外包项目,大 V 模型再熟悉不过了。整个过程冗长繁琐,走流程比建软件更累心。而且等项目结束的时候,需求早就变得面目全非了。乙方只能硬着头皮做,不然连业绩都没有,真是血本无归。在增量或迭代模型的每次交付后都做一次风险评估,演进为螺旋模型,可以及时止损。 + +项目做成这样,更深远的原因是业务都是在摸着石头过河,需求不变更才怪呢。但每年几个亿的信息化预算还是非常诱人的,投标单位络绎不绝。RUB 看起来不错,但需求快速演化会依然带来无法回避的系统重构压力,终归还要具体问题具体分析。 + +相关阅读:04 | 瀑布模型之外,还有哪些开发模型? + +西西弗与卡夫卡: + +当前不够明确、后期可能有较大变化的需求,准确说首先要考虑的不是用哪种开发方法,而是最好避免一开始就投入开发资源。开发的代价非常高,推倒重新开发的代价更高。最好是先想别的办法,验证需求是否真实存在之后再动手写代码。 + +相关阅读:04 | 瀑布模型之外,还有哪些开发模型? + +alva_xu: + +对于增量或迭代开发,大型企业需要考虑这些不适应点: + + +大型官僚机构的办事程序和敏捷过程不匹配。比如开发想敏捷,但财务采购等都不敏捷。代码敏捷了,基础环境不敏捷等。 + +伴随增量的添加,系统结构会逐渐退化。特别是对于大型系统,其系统结构的建设,就需要提前制定计划,而不是增量开发。 + +与开发方的合同问题,需要新形式的合同。旧形式的合同是固定合同,做多少事拿多少钱都在合同时谈好了,不适应工作量的变更。 + + +相关阅读:04 | 瀑布模型之外,还有哪些开发模型? + +纯洁的憎恶: + +流程、工具、文档、合同、计划都是工业化的标志。它们带来了稳定的质量、惊人的效率、超大规模的协作,对于软件工业也是如此。 + +然而软件工业具备轻资产、知识密集型、从业人员素质高等特点,充分发挥人的创造力和价值,是其相较传统工业更高阶的要求。加之软件工程面对的不确定性与复杂度更显著。于是“个体和互动高于流程和工具,工作的软件高于详尽的文档,客户合作高于合同谈判,响应变化高于遵循计划”的敏捷思想应运而生。 + +通过用户故事,理解用户需求;在迭代中采用渐进的架构设计;定期重构解决技术债务;功能开发的同时编写自动测试代码;自动化持续构建。 + +由于淡化了部分工业思维中兼顾稳定、质量、效率、成本的传统手段,敏捷思想的最终落地,需要素质极高的从业人员参与其中,且数量不宜过多,以此来弥补流程上的缺失。同时要团队与客户紧密协作,上级的充分信任,才能够有效发挥其灵活应变,又万变不离其宗的优势。 + +这是大胆的返璞归真,好似回到了瀑布模型前的蛮荒时代,实则是更高级的打法,就像独孤九剑一般。所以,敏捷开发“道”的属性更浓。 + +敏捷开发具有快速迭代、持续集成、拥抱变化等诱人的特点,但也有苛刻的条件要求。不过,即使无法推行完整的敏捷开发,依旧可以在传统模式下,有针对性的应用敏捷开发的实践方法。 + +相关阅读:05 | 敏捷开发打底是想解决什么问题? + +alva_xu: + +我们现在着手的一个项目,是一个软件框架建设项目,外包给供应商做的。在签合同时,基本需求已经梳理得差不多了。所以按理是可以采用瀑布式开发来进行的。但由于以下原因,所以我们结合了增量开发和 Scrum 项目管理的模式进行系统建设。 + + +基本需求是可以分模块来实现的。 +我们这个项目所依赖的其他部门提供的基础平台也不是一次性可以交付我们使用的。 +我们的使用方 (另外一个应用项目)对我们项目的时间要求很急,但可以接受我们分批次交付的模块。 + + +基于以上原因,我们设立了几个大的增量阶段,每个增量阶段我们有分几个 sprint 来进行开发管理。到目前为止,进展还比较顺利。 + +但由于我们这个框架建设项目的外部干系人比较多,所以在协调上游平台和下游应用系统的时候,确实遇到了许多沟通方面的问题。由于其他项目没有进行看板管理,所以需要进行例会形式的沟通来确保关键节点的功能实现。 + +所以,我认为,开发模式和项目管理模式不可以拘泥于一种形式,关键还是要看是否真正达到了整体的敏捷和精益。对于文中老师提及的 scrum 管理和极限开发,确实是小团队内部协同作战的比较好的实践。但对于多团队协同作战,就要考虑综合运用各种方法了。 + +另外,对于文中提及的站会形式,从“道”的角度来说,当然是可以视实际需求来确定是否要开,但往往一种文化的培养,需要有仪式感,需要不断锻炼。所以对于我们来说,我们还是坚持开 Scrum 中要求的四个重要会议。 + +相关阅读:05 | 敏捷开发打底是想解决什么问题? + +纯洁的憎恶: + +分治策略是应对庞大复杂系统的惯用思路,但它的难点或精髓在于如何确保形散神聚。 + + +详细计划(甘特图)VS 任务状态(Ticket)。 + +代码不稳定 & 环境部署麻烦 VS 代码审查 & 自动测试 & 自动部署(GIT、CI、DevOps)。 + +上传下达 VS 频繁沟通、提醒、分享。 + + +大厂的敏捷开发实践,把枯燥的编码变得跟玩游戏一样。借助有效的流程与工具,能够有效节约团队成员的精力,聚焦于任务或角色,不会因频繁“统一思想”导致“技术动作变形”。 + +而另一面,在大厂里每个人通常都是螺丝钉,长此以往也许会养成不谋全局的习惯。如果能从自己的角色中跳出来,俯瞰整个组织协作的全过程,并站在这个视角上思考问题,一定会有更喜人的收获。 + +相关阅读:06 | 大厂都在用哪些敏捷方法?(上) + +alva_xu: + +在一个以 Scrum 为方法的敏捷团队里,首先,Scrum master 是呵护 develop team 的保护神,他的其中一个职责是保护每一次迭代的工作量是 dev team 能按时完成的,而且保护 dev team 能专注于现有 sprint back log 的实现,不会被其他干系人的新需求所打断。 + +其次,Dev team 是一个 T 型团队,技术比较全面,许多事情多能自助搞定,比如,开发人员同时又有测试技能,同时如果结合结对开发,测试驱动开发,那么,交付物的质量就更有保障。 + +再者,在一个敏捷团队里,人数比较少,dev team 的沟通能力都比较强,沟通可以比较充分,所以解决问题的能力就比较强,工作效率比较高 + +最后,敏捷模式的开展,也依赖于工具的使用,目前常用的 CICD 工具,与 jira/confluence 需求沟通管理工具的打通,部署次数的提高,无疑大大提高了开发发布效率,同时也提高了发布质量。 + +综上所述,只要在人员组织架构、工具产品、流程这三个方面都达到了敏捷的要求,那么发布质量就有了保证。 + +相关阅读:06 | 大厂都在用哪些敏捷方法?(上) + +Felix: + +Git 方面也要求团队 Master 中的代码必须通过 Merge Request(Pull Request) 来,也作为 Code Review 的最后一道关卡。持续集成方面大部分通过 Jenkins、几个微服务是通过 Gitlab CI,我们的终极目标是基于镜像部署发布,屏蔽环境影响。 + +相关阅读:06 | 大厂都在用哪些敏捷方法?(上) + +alva_xu: + +我觉得在计划会上,有几个事情必须要做好. + +第一是需要定义 DOR 和 DOD,Define of Ready 和 Define of Done, 如果没有这两个定义,那么扑克牌可能会玩不起来。 + +第二 需求(用户故事分解成的 task) 一定要尽量明确。不管扑克估算还是其他估算方式,如果第一轮估算偏差过大,说明大家对需求不明确,需要产品经理进行更详细的说明。通过几轮估算,如果大家能达成比较一致的估算,那么工作量的估算就比较靠谱了,这也是 Scrum 这种工作方法带来的好处,能让需求得到合理的资源安排。 + +不管怎么说,在 Scrum 里,要重视估算,有了好的估算,速率才真正有意义,才能真正保证交付质量。 + +相关阅读:07 | 大厂都在用哪些敏捷方法?(下) + +纯洁的憎恶: + +“多、快、好、省”,软件工程的四难选择问题。由于质量是软件工程压倒一切的要素,因此“好”必须留在“盘子”里。剩下的要素都是可以根据具体情况权衡取舍的。四难选择变成了三难选择。于是,工程师在实践中面对不确定时,也能够有底气做到“不抵触,讲条件”了。 + +延长时间的另一面是提高效率。借助工具、优化流程、节约资源等方法,可以在一定程度上“冲销掉”延长的时间。 + +非常欣赏 MVP 模型,既可以快速见效,又降低了大量返工的可能。在瀑布模型中,通过会有过度设计的现象。一开始想了很多,结果发现恨不能 80% 都是瞎想。先拿出核心功能,再根据用户使用的情况,有指向性的完善,步步迭代演进,十分靠谱。 + +唯一令人担忧的是,在外包模式中,如果没有明确的需求,就难以估算出较为准确、合理的预算,进而无法立项、采购。如果先做一版需求申请下来预算再说,再用 MVP 模型步步试探。那么最后做出来的东西可能与需求文档严重不一致,存在较大的审计、内控风险。也许企业大了、规矩多了,做起事来确实别扭。 + +作者回复:迭代模型和 MVP 是非常好的组合,因为迭代的时候,会优先选取最重要的功能,慢慢的那些不重要的功能甚至永远不会被加入迭代中,就因为不需要浪费时间在上面了 + +相关阅读:08 | 怎样平衡软件质量与时间成本范围的关系? + +alva_xu: + +传统的大企业(不是指 BAT 这类大企业),比如我们企业,IT 项目牵涉到三个部门,一个是业务需求部门,一个是 IT 部门,一个是财务预算审批部门,采取的形式一般都是采用外包方式,而且往往是固定合同,也就是合同价格是确定的,需求范围也是确定的,这样的话,金三角的两条边就定下来了,剩下来的就是时间和质量的关系问题了。 + +按照金三角的理论,我们就可以知道前面所述的场景下项目组该重点抓什么了:作为甲方项目经理,重点抓的就是质量和时间了。如何通过提高效率,使单位时间的产出比原来的多(相当于增加了时间),来提高项目的交付质量,是我们甲方 IT 项目经理最关心的事。 + +所以这时候,我们的方法是建立统一软件框架、提供公共服务组件、制定代码和测试规范、培训乙方团队、搭建 CICD 平台和自动化测试平台、sonarqube 自动代码检测平台等,使原来几周一次测试变成一周几次测试,使原来低质量的代码快速变成高质量的代码… + +反正是采用各种方法,提高工作效率,用于抵消业务部门不时提出的变更导致的项目进度的风险。当然在开发模式上,也会衡量敏捷的开发模式(特别是 scrum 的管理模式)和传统瀑布及衍生模式哪种模式更高效。 + +当然,理解了金三角,对于前期申请项目预算也是有帮助的,比如,可以和预算部门谈判,如果要砍预算,在时间一定的情况下,就只能减少项目范围,这是我们业务需求部门所不能接受的。这样,就可以使 IT 项目经理名正言顺地把预算部门和 IT 部门的矛盾转嫁到预算部门和业务需求部门去。 + +当然,最合理的做法应该是向 BAT 公司看齐,IT 部门转变为利润中心,自己管预算、自己有开发团队,那么金三角的三条边就都可以进行调优了。 + +相关阅读:08 | 怎样平衡软件质量与时间成本范围的关系? + +最佳思辨 + +林云: + +文中提出可以借鉴软件开发模型中的特点,这一点并不是普通软件开发成员可以使用的。任何一个软件开发模式都有对应的主要问题。就像你把飞机的引擎放在拖拉机上一样。需要对模型进行总体考虑。而且不同的软件开发模式都有对交付团队有能力的要求。 + +举个不恰当的例子,组合软件开发模式的特点就像让一个摩托车驾驶员开着安装了飞机引擎的拖拉机。这并不是软件工程想达到的结果。希望作者对组合研发模式的前提和应用过程进行描述,以减少软件工程方法使用的随意性。 + +宝玉: + +谢谢指正,结合最近波音 747Max 的案例,确实不能乱用,不能说飞机的软件也用敏捷这种快速上线快速迭代的模式。我觉得组合研发模式的前提还是质量,软件工程的目标就是要构建和维护高质量的软件,无论怎么组合开发模式,都不能牺牲质量。 + +这里我也列一些我觉得好的实践: + + +瀑布模型可以参考敏捷开发,引入持续集成、自动化测试,提升效率; +敏捷开发可以参考瀑布模型,开发前多设计,开发后多测试,尤其是要辅助人工测试。 + + +相关阅读:04 瀑布模型之外,还有哪些开发模型? + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/一问一答第2期30个软件开发常见问题解决策略.md b/专栏/软件工程之美/一问一答第2期30个软件开发常见问题解决策略.md new file mode 100644 index 0000000..132106a --- /dev/null +++ b/专栏/软件工程之美/一问一答第2期30个软件开发常见问题解决策略.md @@ -0,0 +1,710 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 一问一答第2期 30个软件开发常见问题解决策略 + 你好,我是宝玉。我们专栏已经完成了项目管理和需求分析这两个模块的学习。这两个模块看起来都和技术没什么关系,但却是项目中至关重要的部分。 + +项目管理贯穿项目始终,需求是项目的源头。希望通过对这两个模块的学习,能加深你对项目管理和需求分析知识的理解,能应用其中一些方法,帮助你个人能力更上一层,项目越做越好。 + +这个过程中一如既往的收到很多同学的精彩留言。其中有一些是提问,一些是针对文章主题自己独到的思考,这些内容都是对专栏内容最好的补充,可以加深你对软件工程的理解和学习。 + +一问一答 + +No.1 + +Charles:可行性分析形同虚设,小公司岗位职责不清晰,互相照顾面子怕得罪人,谁都怕犯错背锅,感觉谁都对,最终就导致谁是“老板”谁拍板!我感觉这个问题挺严重的,很影响决策正确性,只能等所谓的市场反馈。也用类似项目成员“扑克牌”打分的方式可以解决吗?核心问题出在哪里? + +宝玉:这个问题已经不是可行性研究的问题了!核心问题在于没有一套合理的类似于扑克牌打分的机制和流程。 + +扑克牌为什么是个好机制: + + +公平合理,每个人都有机会不受他人影响的表达 +不用背锅,估错了也没关系,意见不一致还可以讨论 + + +可行性研究是不是也可以形成类似机制?有专门会议,大家提前准备,会议上一起讨论结果,不用背锅,根据讨论结果形成最终决议。项目结束后在回顾对比当初的分析,作为下一次的参考。 + +No.2 + +川杰:架构师是否也属于管理者的范畴?因为他需要对产品的整个框架的负责,进而涉及到对每个人的代码的管理,必要时还要给带领团队成员去做重难点问题的攻坚。那么对于架构师而言,是更偏向技术还是管理呢? + +宝玉:我觉得架构师和管理有相通的也有不同的,简单说一下我的观点: + +相同之处: + + +都需要大局观; + +都需要好的沟通能力,让团队清晰的理解自己的意图; + +都需要用好流程和工具; + +都要善于“分而治之”,把复杂的问题拆分成小的具体的问题。 + + +不同之处: + + +项目经理更多的是跟人打交道,对项目负责; + +架构设计更多是专注技术,对架构负责。 + + +两者互为补充,架构师有项目管理能力、项目经理有架构能力,都是非常好的! + +No.3 + +天之大舒:目标的一致性是遇到的困难,公司没有激励制度,导致项目经理和组员目标不一致,如何解决这个问题很挠头。 + +宝玉:解决目标一致性问题,一个方法是多一对一沟通,你了解组员想法,组员知道你的期望;另一个方法就是不必依赖于公司现有制度,自己创造激励制度,激励制度并不一定要花钱或者花很多钱,有时候正式的表扬比钱还有价值。 + +No.4 + +titan:小公司如何进行技术管理的问题?我所在的公司,开发人员多的 40、50 人,少的 10 多个人,这个阶段,是用制度来进行管理,还是人来管理比较合适? + +宝玉:我觉得无论大小公司,一定都要多用合理制度流程,多用工具,摆脱对人的过度依赖,只是在设计流程规范时,要充分结合公司特点、项目特点。 + +比如说小公司老板权力很大,有些流程普通员工有效,老板直接无视了,你还得做好隔离措施,让他不要破坏流程。比如说大公司很多工具、系统都是自建,小公司就不如买来的合算。 + +大公司各种会议和文档相对多很多,小公司这方面就可以多精简,但必要的也不能少;大公司用瀑布模型开发,一个项目几年耗得起,小公司还是敏捷一点,早点能看到产出更好。将来有一天,小公司也会变成大公司,如果你之前没有做好制度建设,将来团队壮大,项目多了,可能就会成为你的管理瓶颈。 + +No.5 + +风翱:“团队的成功,才是你的成功“,以前也坚信这个观点,但自身的例子,让我有些动摇。把下级培养起来了,结果不是升职,而是上级越来越把我边沿化。对于这种情况,怎么调整自己呢? + +宝玉:心情完全能理解,但建议还是看长远些。人生不只是一个下属,不只是一个老板,也不只是一个项目。以前我也纠结过这问题,现在不纠结了。因为我不止能培养好一个下属还能培养更多的下属,我能做好一个项目还能做好更多项目,我不需要靠一个老板的赏识与否来证明自己。 + +No.6 + +冰封血影:针对一些曾经贡献大的技术怎么管理呢?然而传统思维模式和产品迭代模式遗留的一些诟病,很难用新环境、新模式让他们去做改变。(这里并不是否定以前的模式) + +比如:对新推出的 KPI 这类漠不关心、对整个团队表现不出积极的面,反而带来了一些不好的点和面,但是做东西质量相比其他又高;这类怎么去处理和更好的提升整个团队的战斗力、协作力? + +宝玉:几点建议: + + +多一对一沟通,了解他的诉求,让他了解你的期望; + +尝试安排一些有挑战的任务; + +充分发挥其优势; + +“鲶鱼效应”,招聘技术相当的或者更高的,减少其不可替代性。 + +如果负面因素较多,可考虑隔离到某个项目中,避免对其他人造成负面效果;如果负面影响大于正向贡献,劝退也是个方案。 + + +No.7 + +Dora:技术人员呢一般很傲,所以做项目管理,可能要面对被技术人员心里瞧不起,甚至不听话。怎么办? + +宝玉:几点建议吧: + + +如果管理者技术牛,或者懂一点技术,那么就容易很多; + +让项目成功,就是最好的证明你实力让他们服气的方式; + +多换位思考,尊重技术,平等沟通; + +多帮助他们:帮助成长、帮助涨工资、帮助他们少无谓加班; + +多用流程规范来管理,少基于人管理。 + + +No.8 + +tcny:如果因为开发不紧不慢耽误了时间,如何处理呢。应该设置什么样的奖惩制度呢? + +宝玉:这是个好问题!计划恰恰就是为了预防类似于开发不紧不慢耽误了时间的问题。具体例子,一个模块,正常估算(开发和 PM 都认可)需要 5 天,但是如果你的计划粒度是 5 天,那么你到最后一天才能知道是不是会延迟,这时候补救已经晚了。 + +如果你能把粒度设置到半天一天,那么第二或第三天你大概就能知道进度是不是有问题,然后马上作出调整,要么加班,要么找人帮忙,要么换人,要么改计划。这样才可以做到防患未然!至于奖惩制度,只是手段,而不是目的! + +No.9 + +一路向北:计划是否也会有一个迭代的过程呢? + +宝玉:计划一定是个迭代的过程,计划也是个粗到细的过程。一开始不建议做特别细的计划,整体粗一点,定好大的时间节点,也就是里程碑,然后对于下一阶段的计划细化。 + +细化过程中要拉上具体参与的人一起制定,这样结果才科学也不会导致抵触。里程碑定了后不要轻易变,不然就失去了 DeadLine 的意义,即使变也不能过于随意和频繁。 + +No.10 + +Geek_85f782:如果是采用敏捷方法的项目,项目计划是否应该就是迭代计划?在这种情况下 WBS 的结构其实就是一轮接着一轮的“规划 - 分析 - 编码 - 测试 - 集成发布 - 与敏捷配套的一系列总结”?每一轮迭代的成果就是项目的里程碑? + +宝玉:敏捷的项目计划确实有些不一样,WBS 分解后会变成 backlog,backlog 的项会被打分(参考扑克牌打分),根据分数大致可以算出来需要多少 Sprint。因为敏捷开发磨合好后,每个 Sprint 能做的任务分数大致相当。算出来多少 Sprint,就能大概知道需要多少时间。通常里程碑不会那么密集的,一般会几个 Sprint 一个里程碑。 + +No.11 + +纯洁的憎恶:制定计划最好能让项目相关各方充分参与,这样计划更可行,偏差低,结果更可控、可预期。但我的经历却是需求、开发、运营、用户等角色几乎不参与制定计划,就连需求分析、功能设计、测试、验收也以工作忙为借口很少介入。项目管理人员主动拉他们,也遭到厌恶与不配合。在观念与体制不支持的环境里,如何能更好的调动各方充分参与、支持项目呢? + +宝玉:你这种性质的单位我确实没经历过,缺少经验。不过我可以帮你从另一个角度分析下,就是如果我不愿意参与计划可能有这些方面原因: + + +跟我利益不相关,做了没好处,不做没损失; +你已经做的够好够细了,没什么好发挥的; +就算参与了提了想法和意见也没用,最后还是项目经理说的算,那我还掺和个啥劲。 + + +所以你可以看看能不能让这事变成一个跟大家利益相关的事,跟绩效考评啥的扯上关系,必要的话拉上领导狐假虎威一番。拉他们参与时不用太细,让他们有机会参与制定,制定时能平衡好他们的利益关系。尤其是里程碑的确定,我觉得应该是和大部分人利益相关的,至少这个点得让他们参与进去。 + +No.12 + +bearlu:我一直想自己私下做一个项目,但是不知道如何开始,是不是第一步要确定做个什么软件? + +宝玉:对的,第一步先想好做什么。给你的建议是: + + +做个小的; +做个实用的,最好自己能用或者身边人能用; +迭代开发,第一版本只做核心功能。 + + +No.13 + +哥本:在做里程碑的时候需要花时间整合做集成测试吗?就比如像您说的,服务端开发完成后需要与 pc 客户端联调,那这就涉及到发布,环境搭建,部署…做 WBS 时要把这些时间也算进去吗? + +宝玉:做里程碑要不要整合做集成测试,取决于里程碑的目标,比如说如果目标是具备测试条件可以联调,只要能调就可以;也可以定义目标是要测试验收通过,这就需要做集成测试的。 + +这种需要人需要时间去做的事情,都应该放到计划里面。文章中的计划表没有放,是考虑不周。 + +No.14 + +alva_xu:需求文档和测试用例怎么验收?对于性能测试是否合格问题,你们是怎么解决测试环境和生产环境可比性问题的? + +宝玉:需求文档验收可以通过需求评审会议,评审时开发和测试都要有代表参加,一个是提出反馈,另一个是及早了解需求。评审会议通常要开几次才能最终定下来。测试用例通常是产品经理协助验收或者辅助确认。 + +原来我们在飞信时,会有一个模拟生产环境的压力测试环节,从生产环境同步真实数据过去,规模按生产环境比例缩放。还有的压力测试是直接在生产环境做的,在半夜人流量少的时候。 + +No.15 + +张驰:在日常工作中,流程应该由谁来制定呢?普通开发人员还是领导者,亦或者是公司有这种专职专岗的人?往往很多人都能够发现问题,甚至也有一些自己解决问题的方式方法,但是要想具体流程化对公司整体产生作用,往往感觉是有力无门,没有一个好的渠道。 + +宝玉:一个好的流程经常是跟问题切实相关的人员提出来的,或者把问题反馈出来,大家一起想办法,最后由项目经理或者部门负责人帮助落实推广。 + +其实像敏捷开发每次迭代结束后的 Sprint 回顾会议就是一个很好的讨论问题的方式。可以考虑参考 Sprint 回顾会议的做法,定期有专门的会议讨论这样的问题。另外如果有组员之间的“1-1”会议,也是讨论问题和解决方案的途径。也可以通过邮件、聊天工具讨论解决。 + +No.16 + +bearlu:能不能说说开会要留意些什么内容,我是个新手,每次开完会议,到开发的时候又找产品确认具体功能。 + +宝玉:我想你说的应该是需求评审会议或者需求讲解会议,对于这类会议,建议你会议前读一下文档,这样心中有数,同时对于文档中觉得不清楚或者有疑惑的地方记录下来,在会议中提出。不同的会议重点不一样,开会之前你都可以实现了解下这次会议的主要目的是什么,然后事先准备一下,这样开会就会更有效率一些。 + +No.17 + +hua168:整个项目开始前到项目完全结束,一般都要开那些会议呀?目的是什么? + +宝玉:整理如下。 + +项目启动会议: + +通常在项目启动后,会有一个正式项目启动会议,俗称 Kick off meeting,通过这个项目,你可以了解几个关键信息: + + +项目目标:这项目是要干什么的,达到一个什么目标; + +项目里程碑:项目的开始结束时间,项目的阶段划分,以及各个阶段的时间点; + +角色分工:项目成员的分工和角色是什么,每个人知道自己的任务是什么,知道遇到问题该找谁; + +流程规范:项目开发的主要流程是什么,基于瀑布还是敏捷。 + + +瀑布模型各个阶段的评审会议 + +瀑布模型因为阶段划分清楚,每个阶段都有明确的产出,所以通常每个阶段都有评审会议,典型的像需求评审会议和架构设计评审会议。这种会议主要目的是用来收集意见。 + +瀑布模型各阶段的说明会议 + +在评审会议结束后,需求设计、架构设计最终确定后,通常还会组织会议对需求设计和架构设计做说明,所以会有:需求设计说明会和架构设计说明会。 + +进度报告会议 + +无论是采用瀑布模型还是敏捷开发,通常都少不了进度报告会议。只是瀑布模型通常是以周为单位的周例会,而敏捷开发是以天为单位的每日站会。可以通过会议,了解项目的进展,了解当前的困难和瓶颈,及时调整计划,解决问题。另外在会议上,每个人都要当众讲一下做过的事情和计划要做的事情,也是一种无形的监督和约束。 + +项目计划会 + +在敏捷开发中,每个 Sprint 开始前都会有一个 Sprint 计划会(Sprint Planning),决定当前 Sprint 要做哪些内容。在瀑布模型中,每个版本开始之前也会有项目计划会。有所不同的是,瀑布模型通常是项目经理和开发经理、测试经理等少数几个人决定的,而敏捷开发中则是全体成员一起针对 Backlog 的内容进行选取和打分。 + +产品演示验收会 + +在瀑布模型中,项目在测试通过后,会对客户有一个产品演示验收的会议,向客户展示工作成功。敏捷开发中也有 Sprint 评审会(Sprint Review),在每个 Sprint 结束后,向客户演示当前 Sprint 成果。 + +项目总结会议 + +在项目结束,通常项目经理需要组织一个总结会议,希望大家能在会议上总结一下项目的得失,把经验总结下来,帮助下一次做的更好。在敏捷开发中,更是每个 Sprint 都会有一个 Sprint 回顾会议(Sprint Retrospective)。 + +一对一会议 + +虽然项目中有每日站会或者周例会这种让项目成员可以反馈问题的方式,但是对于很多人来说,并不愿意在很多人面前说太多,但是如果是一对一的私人对话,则更愿意反馈一些更实质性的内容,从而项目经理或者管理者能了解到更真实更准确的信息。 + +No.18 + +kirogiyi:能否把项目管理每个阶段用到的典型工具分享一下? + +宝玉:我们专栏每个阶段都有关于工具的章节。 + + +需求分析篇的工具要讲原型设计,需求阶段还有需求收集管理工具,通常可以用 Ticket 管理系统(如 Jira)、源代码管理(如 git)或文档管理工具(如 Google Docs/ 石墨文档)来做。 + +设计阶段其实主要用文档工具,用 MS Visio/PPT 画图。 + +编码阶段主要是源代码管理工具、各种 IDE、持续集成平台(Jenkins)的搭建。 + +测试阶段主要是有测试用例管理系统(例如 TestRail),有 Bug 跟踪系统(基本上和项目管理工具一起的,例如 Jira)。 + +运维监控有日志管理系统(例如 ELK),监控(例如 Wavefront),报警(例如 PagerDuty)。 + + +No.19 + +纯洁的憎恶:我看燃尽图好像是根据 ticket 数量的历史变化情况,线性的预测未来的工作进展。但工作真实进展很可能不是线性的,这是否说明燃尽图的剩余工作预测存在天然偏差呢? + +宝玉:燃尽图是有天然偏差的,因为任务的复杂度其实不一样的,有的几小时就完了,有的得好几天,有时候你看只剩下一个任务了,但这个可能是最难耗时最长的。所以我个人更喜欢看板视图,可以直观看到当前 Sprint 具体什么任务还没完成。 + +No.20 + +busyStone:请问新的这些工具还能看到并方便的编辑任务依赖么?有没有工具可以直接通过修改状态就自动换看板的? 另外,像同一个需求需要多端,安卓,苹果,PC 同时开发的,请问有没有好的方法来建立任务? 之前都是一样建一个,有点烦。 + +宝玉:以 Jira 为例: + + +Ticket 之间是可以建立关联的,好像不是强依赖。 + +修改 Sprint 属性就可以切换看板,修改状态就可以切换看板的泳道。 + +Ticket 可以克隆(Clone),同一个需求可以克隆多份,然后稍作修改。 + + +No.21 + +oldlee:请问前后端开发分离工具有没有好产品推荐?现在遇到的问题是,客户端经常需要等待服务端开发完,才能调用接口联调。 + +宝玉:这个问题有几种解决方案: + +服务端先实现一个模拟的接口; + +客户端自己模拟接口; + +第三方服务。例如: + +https://github.com/easy-mock/easy-mock + +https://getman.cn/mock/ + +https://apizza.net/ + +No.22 + +alva_xu:项目的不同时间节点,项目风险及其处理手段也是不一样的。所以,在讲风险管理的时候,还要加一个时间维度。老师能不能就这个维度来谈谈风险及管控处理方法? + +宝玉:其实要考虑时间维度,你只要把时间范围成本三要素的约束加上就好了。因为时间变了,这三要素的约束也在变。 + +给你举个例子:一个创业公司,人少缺钱,这时候人就是个很大的风险,有人离职项目就很危险,这其实本质就是三要素的成本;等到熬过这阶段,进入发展阶段,活下来有钱了,人也多了,相对来说,成本就不是最大的约束了,人的风险就没那么大了,这时候就是求快,时间会变成约束,所以如果你的技术和架构跟不上开发的效率,就会成为新的风险。 + +No.23 + +Bo:已经写好项目文档,但想更另一步优化文档,老师可以分享一下项目中需求规格说明书、概要设计、详细设计、代码规范文档、测试文档、部署文档等的优秀具体案例吗? + +宝玉:有些内部文档不方便分享。我在文中附了一个开源项目的链接:https://video-react.js.org/ + +这个是一个组件使用文档,其实类似的有很多开源项目的文档都写得很好。比如: + +Vue: https://cn.vuejs.org/v2/guide/ + +Redux: https://redux.js.org/ + +还有可以网上搜索一些,例如: + +产品需求文档模板:https://www.jianshu.com/p/e89e97858be1 + +微服务,从设计到部署: + +https://docshome.gitbooks.io/microservices/content/2-using-an-api-gateway.html + +No.24 + +hua168:老师能简单说一下项目前–> 项目中–> 项目完成,一般都需要哪些文档呀,有没有示例或链接或搜索关键词? + +宝玉:我大致列一下,可能有遗漏的。 + +项目立项: + + +原始需求文档; + +可行性分析报告; + +立项说明书。 + + +需求相关的: + + +原型设计文档; + +产品设计文档。 + + +系统设计相关的: + + +技术方案文档; + +详细设计文档。 + + +开发相关的: + + +代码规范文档。 + + +测试相关的: + + +测试用例; + +测试验收报告。 + + +运维相关的: + + +部署文档; + +故障报告。 + + +No.25 + +邢爱明:对于详细设计文档的颗粒度一直有点疑问。是写到类图或者时序图这种级别,说明不同类和方法之间的关系?还是要细化到类似于伪代码级别,需要写操作哪个数据库表,和调用哪个 api 接口? + +宝玉:我们 2002 年学软件工程的时候,推荐的写设计文档就是你说的这种细化到为伪代码级别,当时初衷是学习建筑行业,把写代码变成像搬砖砌墙一样,招一堆蓝翔培训出来就可以写代码。据说当年日本软件产业就是这样的。 + +实际上这些年下来,这种方法是不可行的(至少我没看到过成功案例),一个是设计文档写得太细,其实成本上已经跟写代码没差别了,不利于分工协作;另一个是写代码本身是一种创造性的劳动,当你把文档写到伪代码那么细,具体负责代码实现的没什么好发挥的空间了,都变成体力劳动了。 + +推荐的做法是写设计文档时不要太细,同时应该把具体模块的设计交给负责这个模块开发的人去做,指导他完成设计。这样既可以更好地分工协作,也可以让程序员有机会成长和充分发挥其主观能动性。 + +No.26 + +晓伟呢。☀:需求分析之后是不是应该还有产品需求分析文档(PRD)和产品需求规格说明书? + +宝玉:其实不必困惑这个问题,因为这本身没有特别的标准的。如果用瀑布模型开发,确实会有你说的文档,但如果是敏捷开发,可能会是另外的形式存在,例如每个小功能一个独立的用户故事,或者是独立的产品设计文档,只是讲清楚一个功能。 + +虽然形式不一样,但其目的都是一样:让大家可以讨论需求,可以理解需求。之前我有回复过有哪些文档的问题: + + +这件事需要讨论需要评审,要有文档作为讨论的依据,以及记录讨论的结果。比如各种设计文档; + +这件事要有规范,要有文档保证规范统一,比如各种规范文档; + +这件事要记录下来,作为以后的一个参考。比如各种报告、环境配置、操作手册、API 文档等。 + + +No.27 + +hua168:什么是模块呀?模块有哪些分类?一般说的模块是业务模块?模块间“高内聚低耦合”,如果模块之间要进行通讯是不是用接口实现?如果有依赖关系越多的话的话那不是独立性越差?要实现“高内聚低耦合”,如果项目复杂一点,会有难度吧? + +宝玉:这个话题其实属于架构下面的。在技术里面,模块其实是对某一种类型需求的抽象。举个例子来说,一个博客系统,博客的帖子是一个模块,评论是一个模块,用户是一个模块,帖子和评论又可以进一步抽象成内容模块。 + +模块的分类看你是从架构层面看还是从业务层面看。比如说从架构层面看,一个普通的博客网站可以看成三层:UI 层、业务逻辑层和数据访问层。其中 UI 层包含帖子列表模块和博客文章阅读模块;业务逻辑层则是帖子业务模块、用户业务模块。 + +如果从业务层面看,包含博客阅读模块和后台模块,更偏向功能的分类。 + +高内聚低耦合是架构里面的概念。因为架构设计,需要把大的系统拆分成小的模块,拆分后,还要通过约定的协议通信。典型的有前后端分离然后通过 REST API 通信,还有像类库之间直接通过公开的方法调用。 + +低耦合意思是项目没有什么依赖,改动互相不受影响。比如说前端和后端之间通过 API 通信,只要 API 不变,无论你后端用什么语言,跟前端都没关系。 + +高内聚指的是一个模块都是关系很紧密的代码,比如用户模块,所有用户操作的功能都在用户模块里面,关系紧密,也不需要依赖于其他模块。 + +No.28 + +kirogiyi:在敏捷开发中产品部门怎样参与产品设计会更好,或者以什么方式参与会更好? + +宝玉:你问的是产品部门参与产品设计,还是开发部门? + +首先从技术角度,好的产品设计要让产品设计在技术上实现不要成本太高,所以在一些技术难度高的设计上两边要多沟通确认,共同制定出技术难度适中,又满足好产品需求的设计。 + +然后从产品角度,开发必须充分理解产品设计才能设计出符合产品需求的架构,才能开发出满足需求和好的用户体验的产品。所以开发需要多和产品设计反复沟通需求,然后将确认后结果体现在文档上。 + +至于参与方式,主要还是看什么形式比较好。比如可以安排需求评审,关键节点让主要开发人员参与确认技术可行性和成本以及建议。比如有分批次的需求讲解会议向开发讲解产品设计,回答理解不清楚的问题。每个 Sprint 产品经理都要参与其中,及时和开发沟通确认需求不明确的问题! + +No.29 + +alva_xu:在目前前后端分离、Restful 风格的应用架构下,是否更容易实现原型设计时的代码的重用率,以提高开发速度?具体是怎么做的? + +宝玉:以前在讨论开发模型的时候有介绍,快速原型开发模型有两种模式,一种是抛弃型的,就是用工具开发的这种;一种是演化型原型,就是类似于 MVP,先做简单核心功能,然后不断演化,变成最终产品。 + +如果你要提升代码的重复率和开发速度,这种前后端分离的呀,我给你的建议是用一些第三方 API 云服务: + +https://www.apollographql.com/ + +https://firebase.google.com/products/firestore/ + +这样你就完全不用考虑后端开发了,直接用它们定制就好了。等到产品开发出来,你再考虑后端迁移。 + +No.30 + +LDxy:Windows 系统已开始就是作为一个产品开发的,最初的项目团队应该是很有产品意识的;而 Linux 系统的开发者最初好像并不是把它作为产品开发的,这是不是也是造成如今 Linux 和 Windows 相比对大多数用户的易用性差别很大的原因?这是不是也是产品意识差异导致的结果?能不能作为一个说明产品意识的例子? + +宝玉:我觉得 Windows 和 Linux 产生的差别还是因为产品定位的不同导致的。前者是商业产品,面向普通用户;后者是开源产品,面向专业用户。 + +精选留言: + +西西弗与卡夫卡: + +最近有个项目延期,原因之一就是用到的第三方库需要 https 绑定域名,测试环境因为用 http 所以没有发现该问题。事先的可行性研究,目的就是消除或者平衡项目中的技术风险、能力风险、协作成本、法律、部署等风险。 + +总结里给出了一个可行方法,即尽早上线部署,不对外公开服务即可。像法律问题,靠软件部署没法解决,可以有个检查清单,每类风险都给出适当评估意见。 + +相关阅读:09 | 可行性研究: 一个从一开始就注定失败的跨平台项目 + +Felix: + +机缘巧合转管理已经快两年了,以下说说我的看法: + + +大局观,我十分赞同老师的观点,我领导经常潜移默化地这么教我们,我也觉得这是我转管理后的最大收获,不能像以前看着自己的一亩三分地,站在全局考虑问题;正如张一鸣说的那句,“工作时不分哪些是我该做的,哪些是我不该做的”,这句话对我影响很大,做事不设边界,才能我有更大的成长,而这些对管理来说尤为如此 + +流程规范,接手管理后,发现虽然我们有一大堆流程规范的 wiki,但很多形同虚设,我个人总结有以下几点问题:(1)不能很容易找到对应规范 wiki; (2) 很多规范冗长复杂,不知所云; (3) 流程规范太多,没时间看。 + + +于是我第一步就是整理杂草丛生的 wiki,先保证目录清晰,让大家按目录写 wiki,对号入座,查阅起来方便有条理,接下来挑出重点的流程规范进行了简化微调,每周会重点强调 1-2 个,在本周工作中重点关注,并适当提醒,渐渐大家一起走入流程的正规,并也体会到了按流程规范走所带来的便捷。 + + +管理该不该写代码,我觉的这事不能一棒子打死,我的观点有点像党的一句老话:从群众中来,到群众中去,在项目关键时刻负责一个小的 Ticket,我觉的有以下几点好处: + + +因为平时的 code review 不可能面面俱到,这么做更加深入了解系统底层结构和代码,更加容易指导员工的技术细节问题 ; + +让自己不是光说不练,纸上谈兵的领导,我觉得从我本身而言,可能中高层确实不必要,但我认为这对于一线管理还是很有必要的,能够树立威信,合作沟通更加顺畅。 + + +自己的管理风格,关于大棒还是胡萝卜,确实不同的公司、团队应该有不同的管理风格,这里没有对与错,但我认为对于扁平化的互联网公司,各种大牛,严厉的风格是我不提倡的,像老师说的激励帮助下属,团队氛围融洽,大家自驱地做事情我认为更可取。 + + +相关阅读:10 | 如果你想技术转管理,先来试试管好一个项目 + +alva_xu: + +关于技术转管理,先从项目管理开始。这个观点我极其赞同。以下我谈谈自己的想法。 + + +老师举的是软件开发项目管理的例子,假定的项目经理是有开发技术的,所以需要克制自己不要有写代码的冲动,这一点我极其赞同。但假如项目经理以前并不是写代码的,这时候怎么办?我倒是觉得,应该学点代码,尝试写点代码,深入理解软件开发框架,培养点软件架构思想,才能充分理解开发人员的境况,更容易和自己团队甚至客户进行交流。 + + +同时无论你过去是开发大牛、还是应用架构师、领域专家、还是基础架构师,除非人员安排如此,否则,千万不要越俎代庖,把这些事情交给负责这些事情的人去做,你可以做的就是帮助指导,而且尽量要从方法上去指导,“授人以鱼不如授人以渔”。特别是一个比较固定的团队,培养一个人的成长比样样事必躬亲要好。 + + +管理牵涉到“人”“工具”“流程”三个部分的使用。项目经理首先需要学一些管理学的知识,如何激发”人“的潜力以完成目标是管理的最主要目的,所以一些管理理念,比如 MBO,管理方法(沟通技巧)都得学一点。 + + +对于“工具”,好的工具和差的工具效果不同,但更主要的是要用好工具,比如敏捷模式中,像 Jira,或者 VSTS 等都是很好的管理工具,也就是老师讲的 ticket 工具,但怎么用好它,需要项目经理在团队内外进行培训推广,常抓不懈。还要考虑怎么把“流程”固化到工具中,那么项目管理就如行云流水了,所谓子在川上曰,逝者如斯夫! + + +当“人”“工具”“流程”都发挥了它们的作用的时候,项目经理就需要凭借自己的知识和经验、善于发现风险,管控风险。这时候,我觉得风险管理是项目经理最大的责任。特别是控制好“范围”(防止项目过程中范围扩大或者变小),“成本”和“时间”,以最终达到合理成本下按时交付完整的达到质量要求的项目交付物。 + + +以上几点,也是我从基础架构规划实施、然后做基础架构项目,现在管理软件开发项目好多年来的对于项目管理的一些经验,和大家共享,也请老师点评。 + +相关阅读:10 | 如果你想技术转管理,先来试试管好一个项目 + +javaadu: + + +不同的岗位有不同的职责,基层管理者的职责并不是单纯的管理,要兼具技术深度、技术视野、项目管理、团队管理等技能。 + +关于“写不写代码”的讨论,作者说这句话的意思是,项目管理者要明白,写代码并不是万能药,不能过分得关注细节,要跳出来,看全局,要明白自己的职责——管理项目过程、控制风险,拿到结果。 + + +至于说是不是要写代码,那是另外一个问题,阿里现在已经取消了技术线的纯粹的管理岗位,就是希望技术线的基层领导者都不要把技术丢掉,要能跳出来看全局,同时也能带领团队打硬仗。 + + +我有转型管理的计划,我希望自己能够实现从个人的成功到团队的成功,原因是:个人的成功,影响力有限,团队的成功才能完成更大的成就。 + + +我计划按照老师说的,从项目管理入手。遇到的困难就是自己的大局观不够,一冲动就喜欢自己上,这样的情况很不好:自己累的要死,还没什么成就感,然后团队其他成员也得不到充分的的锻炼。希望在后面的工作中,如果有项目管理的机会,自己能够改善自己的大局观。 + +相关阅读:10 | 如果你想技术转管理,先来试试管好一个项目 + +纯洁的憎恶 : + +进步的关键是角色转换,级别越高离具体工作越远,对人和资源的驾驭能力越强。项目管理就是要管好人和事。管好人就是正确引导客户的期待,用流程和规范管理团队。管好事就是选择适当的模式,制定计划,防控风险。持续成长是勇于跳出舒适区进入学习区。 + +相关阅读:10 | 如果你想技术转管理,先来试试管好一个项目 + +MiracleWong: + +根据自己的经验写一下。 + + +很多的时候,我们不愿意制定计划的原因,简单地说是“懒”,深层次的原因是不愿意“思考”,因为这需要做很多准备工作,并消耗很脑细胞,是对自己认知上的一个考验。再往深处挖则是“不愿意承担责任”(工作中尤其会遇到类似的同事,我拿多少钱就做多少工作,偶尔自己也会成为这种人),因为要介入制定计划、以及后续的调整,就觉得这不应该是自己的工作量。 + +就是制定计划的颗粒度粗细的问题。自己经常会遇到类似的困扰,就是一次计划,分解的太过详细,导致行动的时候因为繁琐反而拖延或直接不做。(也明白这是一种心理上的自我欺骗,认为做了计划就等于行动了,类似于买个课程就等于学习了,收藏了就等于看了。) + + +这就会导致下一次的计划遭到“反噬”——上一次那么详细也没什么用,还不是不做或者稍微写一下呢。等到自己没有什么目标或者虚耗摸鱼时,又记起“详细计划”的好,一次次的循环。 + + +对于宝玉老师说的是否有制定计划的习惯,我经常是每个月的最后两天,制定下周的目标(类似工作计划)。将目标和自己的工作生活学习联系起来并进行分解,分散到每个月的四周里。每周做个小结,月底再做月总结和下月目标。目前还是在尝试练习中,在逐步形成自己做目标和总结的固定模板,省去部分重复性的工作。 + + +相关阅读:11 | 项目计划:代码未动,计划先行 + +alva_xu: + +计划就是为了把项目的各种资源(人力资源,软件资源,硬件资源等)有序组织起来,以便及时识别变化、应对变化。所以做计划的时候,一要考虑如何使计划更加精准,二要考虑一旦有变化、计划如何能更加容易调准。 + +方法可能就是: + + +尽量把任务拆解,和任务执行者一起确定故事点(scrum 里的说法,这里借用一下)。这样的话即使计划变化,并不是每个任务都变化,计划调整就快; + +对任务进行合理排序,找出关键路径和关键节点,项目的风险就比较容易识别。计划调整就能更加及时有效; + +通过设立指标和看板,利用项目管理工具及各种形式的会议、报告,及时收集监控项目情况,适时发现问题,及时调整计划。 + + +相关阅读:11 | 项目计划:代码未动,计划先行 + +alva_xu: + +我来谈谈对老师讲的几个点的个人看法和实践。 + + +方法和流程规范的区别 + + +老师讲的很对,流程规范是在很多经验总结后形成的。从 ITIL 流程来说,这里的方法实际上可以理解为事件管理的范畴,就是发现了一个 incident ,想办法去解决,甚至用 work around 的方法去解决。当相同的 incident 发现次数多了,在 review 的时候,事件就上升成为问题。问题管理就是用来彻底避免相同事件重复发生的。 + +而规范流程是问题管理的一种手段。问题管理会带来变更管理,规范流程的制定和修改,是可以纳入到变更管理中的,只要纳入到变更管理,就自然会考虑到沟通机制、回退计划等事情。 + +我们也碰到过类似老师提到的改数据库的问题。刚开始数据库改出问题了,我们就处理数据库问题,后来,总结下来,需要严格改数据库的流程,比如增加业务运维和基础运维的经理审批才允许修改数据库,改数据库的流程我们也花了很多时间进行优化才真正固定下来。 + + +流程规范工具化 + + +我觉得,除了工具化,还要尽量自动化。举个例子,我们这边最早采用 checkstyle 和 findbug 嵌入到 IDE 的方式进行代码检查,然后规定每个项目必须用这两个工具。但后来发现,这个规定执行的很不好,许多项目组没有自觉执行,增加了 QA 团队的检查工作量。后来我们采用 sonarqube, 并把它集成到 ci 里,就不怕项目组不执行了。 + + +推广执行的问题 + + +除了前面两个方法,纳入变更管理和纳入自动化流水线之外,还有一个特别重要,那就是考核问题。但这个有很大的难度。有些规范的执行力度很难量化考核 。举个简单的例子,测试用例和需求文档的匹配问题,还有比如压力测试的性能指标问题,如果没有工具和环境,这简直会把 QA 愁死。所以,流程执行的好坏,还是与人和工具技术有关,三者互相关联,缺一不可。 + +相关阅读:12 | 流程和规范:红绿灯不是约束,而是用来提高效率 + +青石: + +组织会议,一定要有会议时间、地点、人员、主题,会前要有准备、会中讨论要有结果(指定干系人)、会后要跟踪。没有主题、没有讨论结果、没有跟踪的会议,都属于无效会议。 + +相关阅读:13 | 白天开会,加班写代码的节奏怎么破? + +kirogiyi: + +完全手工方式管理的优点在于自由空间大、项目结构松散,比如临时添加需求、临时添加人员、临时改变策略等。一旦管理者没有足够的能力去驾驭项目的整体架构,随着项目时间的推移,项目不是越做越简单,而是越做越难,可能到处都是窟窿,根本没法持续下去,并且责任和义务大部分集中于项目管理者。 + +尽量采用软件工具管理的优点在于对需求、人员、进度、里程碑等可以进行事无巨细的分解或者组合,明确每个人的职责,明确每件事完成的要求,既可以让参与人员看到长期目标,也可以让他们看到短期目标,而不是遥遥无期。可以这样讲,没有路标的 100 公里总是比有路标的 100 公里来得费劲得多,还有就是很容易让参与者失去信心,丧失斗志。 + +相关阅读:14 | 项目管理工具:一切管理问题,都应思考能否通过工具解决 + +刘晓林: + +我觉得辅助计划工具是从项目规划和任务分解出发,以任务之间内在逻辑关系为依据组织任务,优点是能够清晰地看到整个项目的蓝图,缺点是结构化程度太高,不够灵活,不能适应项目执行期间遇到的变化。 + +基于 tickt 的管理跟踪系统是从项目执行的角度出发,以执行周期为依据组织任务 (如一个 sprint),注重任务的状态跟踪,优点是灵活;缺点是缺乏结构化,各任务之间的关系不明确,容易只见树木不见森林,因此不适合做项目规划和任务分解。 + +因此,需要将二者结合起来用,在规划和任务分解阶段,用项目规划工具,生成蓝图,最后把分解后的任务做成一个个 tickt,做项目跟踪。 + +相关阅读:14 | 项目管理工具:一切管理问题,都应思考能否通过工具解决 + +alva_xu : + +ms-project 这样的计划工具,适合于项目整体计划的把控,人财物的协调。ticket 系统适合于每个阶段任务的安排、变更和任务跟踪。 + +两者一个全局一个局部,在敏捷项目里应该结合起来使用会比较好。项目整体计划抓大的 WBS ,不做过度深入的 WBS,而 ticket 系统可以跟踪管理局部的变更,是计划管理的子集。 + +所以我的经验往往是先做一个全面的迭代计划(用甘特图), 基于此做人员安排和工作安排,并拿此作为汇报的依据向领导汇报。当然,这种模式适用于项目整体目标清晰,时间节点容易规划、每一阶段工作都容易估算的项目。 + +相关阅读:14 | 项目管理工具:一切管理问题,都应思考能否通过工具解决 + +青石 : + +领导常说“大脑是用来计算的,并不是用来记忆的。”工作年头越多,越习惯将平时操作的过程整理成文档,分享给内部成员。 + +学的东西越多,记住的内容往往越少,将重复或可整理的内容写成文字,保存起来,使用的时候知道去哪里找就好。这也正是索引 / 缓存的妙处,利用大脑有限的 Cache 资源缓存常用的内容索引,将不常用的内容存盘,需要的时候再次加载。 + +相关阅读:16 | 为什么你不爱写项目文档? + +一路向北: + +我们的项目文档基本上是以协议和流程为主,写这类文档的时候,实际上已经把项目的每一个细节都考虑清楚了,经过几次的 review 之后,后面的项目实现就是根据文档的内容再继续细化,一旦遇到不太清楚的地方,再回头翻阅文档,也很容易知道当初设计的时候是怎么一回事。 + +套用格式确实是一个比较好的方式,填空总是比直接写作文要简单的多,而一旦整个空都填满之后,再继续润色,细化那又会比一开始简单些。写文档,记笔记等,用对工具还是很重要的。 + +相关阅读:16 | 为什么你不爱写项目文档? + +青石: + +赞同老师的“价值体现在产品之上”。技术能力越强,增长曲线越缓慢。实际开发过程过程又大多是满足需求,而不关注质量。企业雇佣关系也更倾向于成本低、增长曲线高的程序员(大不了用你的薪水雇佣两个),所以就出现老程序员的无奈。那么技术在达到一定程度后(增长曲线减慢,收益比下降),同时横向扩展,丰富自己的知识体系结构,不失为一种保值方式。 + +技术通过努力都可以达到差不多的水平,不同的是思维方式和所处的高度。不断学习的过程,其实就是让自己了解的更多思考的越多,思考的越多站的高度自然更高。 + +入门时写代码是为了实现功能,深入下去会想了解它的实现方式,接着尝试举一反三将思想运用到其他地方。培养产品意识也是从全局看问题,站的越高,望的越远。 + +相关阅读:19 | 作为程序员,你应该有产品意识 + +kirogiyi : + +程序员的焦虑是自己吓着了自己,一些负面词汇听多了,潜意识里难以平息内心的恐慌(码农、大龄程序猿、996、ICU 等等),只想着能赚钱的时候赶紧赚一笔,至于技术进步和长远打算,都只是锦上添花而已,愿意做出一些无价值的付出。越临近这些负面词汇的边缘越是心急如焚,要么逃命去吧,要么留在原地观望,要么综合培养自身硬实力和软实力,前两种会逐渐淘汰,后一种会有顽强的生命力。 + +宝玉老师这里讲到产品意识,我认为这是一种软实力的培养,它能辅助你的硬实力做出更好的项目,也能拓展自身在技术以外的视野。其实,有时观察下来,技术能力强的人一直忙个不停,可以解决很多问题,问题却好像没玩没了似的;技术能力一般,喜欢沟通,具有一定产品意识的人,上午一杯咖啡,下午一杯茶,安安心心按时下班,轻轻松松交出项目成果。我认为,程序员在一定的阶段,不能只关注自身技术实力的成长,忽略了其他方面的成长。这就像一个偏科的人,永远拿不到第一名,而那些各科成绩均衡,没有一科成绩第一的人却成为了第一的道理是一样的。 + +有句话,一直记得很清楚:吾生有崖,而知无崖。学新技术也是一样的,不一定死搬硬套要去学会,这样学习成本会很高,但一定要去关注,知道什么时候、什么地方可以用得上,一般有经验的技术人都能在短时间内学会,尤其对大龄技术人员。一旦时间久了,关注的点就不一样了。思维开始转换,然后从更高、更深的层次去考虑问题,才能真正体会到“技术是工具”这句话的深刻含义:工具可以换,思维可以变,灵活多变最重要。 + +相关阅读:19 | 作为程序员,你应该有产品意识 + +alva_xu: + +InfoQ 上有篇文章供参考: 35 岁的程序员是“都挺好”还是“都挺惨”? + +https://mp.weixin.qq.com/s/1q82RO4gRAXtuFeDGV4qRw + +实际上,和年轻人相比,在学习能力上,总会有瓶颈。不拼体力、不拼脑力,我们拼经验,拼沉淀,拼吃的盐比你多。所以,我们在成长过程中,一定要注重学习、消化和沉淀,从表层易变部分向底层基础部分转移,从程序员向架构师产品经理转型。持续学习、多学方法论,不断扬弃,顺势而为! + +相关阅读:19 | 作为程序员,你应该有产品意识 + +果然如此 : + + +提升需求确定性:设计高保真的原型,如用 axure,不仅仅是画框图,还要加各种响应事件等; + +提高需求变更的成本:提前约定变更制度,签字画押; + +降低响应需求变更的成本:提高技术框架水平。 + + +以上是通过产品、流程、技术三个方面解决需求变更问题。 + +相关阅读:20 | 如何应对让人头疼的需求变更问题? + +思辨时刻 + +dancer : + +管人和管事,言简意赅,受教了! 但是对是否写代码,我个人的看法是,对于一个一线技术管理,比如不到十人技术团队的 leader,我觉得时刻保持学习新技术,写写代码还是有必要的。好处一是做技术选型或者评审设计的时候,不会把团队带跑;好处二是做技术决策的时候,更有说服力。总儿言之,就是要有一定的技术领导力。 + +宝玉: + +你这个补充很好,我在文中说的有点绝对了,客观一点说法应该是尽可能保持一个合适的比例!但管理的团队越大,职责越多,那么要写的代码比例就要越少。 + +相关阅读:10 如果你想技术转管理,先来试试管好一个项目 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/一问一答第3期18个软件开发常见问题解决策略.md b/专栏/软件工程之美/一问一答第3期18个软件开发常见问题解决策略.md new file mode 100644 index 0000000..e67fc21 --- /dev/null +++ b/专栏/软件工程之美/一问一答第3期18个软件开发常见问题解决策略.md @@ -0,0 +1,372 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 一问一答第3期 18个软件开发常见问题解决策略 + 你好,我是宝玉。我们专栏已经完成了架构设计和开发这两个模块的学习。这两个模块都是和技术有很大关系,也是很多人关心的内容。 + +希望你通过对架构设计内容的学习,能控制好软件项目中技术复杂的问题。遇到技术选型,能心中有数,通过一个科学的过程去完成选型;对于项目中的技术债务,能及早识别,及早应对。 + +通过对开发模块的学习,可以帮助你在项目中搭建持续集成环境,推行自动化测试,改进基于源代码管理工具的开发流程。借助工具和流程,让你项目的开发质量更上一个台阶。 + +本篇继续分享同学们的精彩留言,这些问答和同学们的分享都是对专栏内容的最好补充,希望可以帮助你更好地学习和理解软件工程知识。 + +一问一答 + +No.1 + +一路向北:每次看这些架构的思想方法的时候,总是和实际的应用没能很好的结合起来,原因是不是架构设计的实践不够?或者是对各种实现的分析和思考太少? + +宝玉:我觉得不仅要有架构实践,还要有不同场景的实践。 + +举个例子来说,你平时做企业应用架构,没什么流量,没多少数据,复杂的地方都在业务逻辑,这时候你去看那些讲大数据、讲高并发的文章,很难带入到场景去。 + +还有就是一些架构,不自己搭一遍是很难了解其中的优缺点的,这也是另一个原因。 + +可以考虑有机会自己尝试,把看到的一些好的架构用一个原型程序搭一遍,造一点数据出来,用工具压测一下,这样会更有感觉。 + +和实际应用想结合的问题,一方面说明你现有的架构可能并没有什么大问题,没有那么迫切的需求要改造;另一方面可能还是因为缺少实践经验,心里没底,不知道真用上了有没有用。 + +No.2 + +小伟:比较规范的文档有哪些,他们功能分别是什么? + +宝玉:对于瀑布模型,每个阶段结束后,都有相应的验收文档,而敏捷开发则没有那么多硬性的要求,而是根据项目需要,写必要的文档。 + +有些团队对于测试阶段,会有测试用例文档、测试验收报告,发布前还会有部署文档、维护手册,但现在这类文档基本上被测试工具、部署脚本替代了,也没有什么存在必要。 + +我觉得项目中必要的文档,主要包括这几类: + + +设计类文档 + + +这类文档主要用来说明、讨论需求设计、架构设计,可以用来了解、讨论和评审,以及记录后续结果。 + + +说明类文档 + + +这类文档用来对规范、API、配置、操作等做说明,便于规范和统一。 + + +报告类文档 + + +对事情结果的报告和说明,比如说验收报告、故障报告、调研等。 + +而这些文档的价值,在于帮助成员了解设计、参与讨论,记录项目成果,减少沟通成本。重要的不是文档多丰富,而是这些文档有没有价值,你能不能及时通过这些文档得到想要的答案。 + +所以你也可以对照一下你的项目中,现在的文档有哪些地方是可以简化的,哪些地方是要增强的。 + +比如说,概要设计 / 接口设计 / 详细设计是不是可以适当合并,减轻文档工作?PRD 是不是够详细?会不会引起歧义不容易理解,要不要增加原型设计文档辅助? + +No.3 + +邢爱明:项目团队的开发人员,基本都是从外包公司临时找的,水平参差不齐,稳定性差,因此技术选型更多考虑技术的普及度的和是否容易学习掌握,从这方面看基本不太可能选择比较小众、但在特定领域很高效的技术。 + +加上是企业内部管理的系统,数据量和用户数量可控,因此存在技术瓶颈的可能性很小,综合下来看,最好的选择就是最成熟和通用的技术,比如说选择 java 技术栈,web 开发的 ssm 框架等,但这样长远看团队和个人的技术能力很难提升,请问老师在这方面有什么建议? + +宝玉:我觉得团队的技术提升和项目的技术选型要分开,不要总想着两个都兼顾,优先保证好项目稳定、低成本运行。 + +技术提升这种事,需要让一部分人先成长起来,然后带动其他人。我自己工作之外会做一些业余项目,然后在这些项目中体验新的技术,体会其中优缺点,然后再逐步应用到工作的项目中,传授给同事们。 + +我也鼓励其他同事这么做,去做一点自己的项目。但工作中的项目,我是很保守的。 + +No.4 + +alva_xu:对于开源技术方面,老师有没有什么经验来指导选型? + +宝玉:开源技术选型,我的经验一般是这样的。 + + +先找朋友推荐,少走一点弯路。 +没有推荐的话,就去网上搜索,找几个满足需求的备选。 +对比以下几个指标: + + + +代码质量、有无测试; + +文档健全度; + +看 Issue 处理情况、最后更新时间(无人维护的项目后续恐怕有问题都没法解决); + +看 Star 数量,通过 Google 和 StackOverflow 看使用情况。 + + + +自己按照说明试试看。 + + +No.5 + +alva_xu:有没有什么大的原则可以指导技术选型?比如技术成熟度等? + +宝玉:我认为在满足设计目标的前提下,大的原则还是在于项目约束,尤其是成本和时间,然后就是看技术可行性和风险是不是可控,其他看团队风格,有的偏保守有的追新。 + +比如说我自己的原则: + + +成熟的好过新酷的; +流行的好过小众的; +团队熟悉的好过陌生的; +简单的好过复杂的; +开源的好过商业的(有时候也视情况而定)。 + + +No.6 + +Charles:有着正常职位或头衔的架构师,对一个全新的项目理解产品需求后进行架构设计,一般会产出哪些“东西”,来满足后续的架构讲解和项目开发过程中的沟通? + +宝玉:互联网产品特点是用户多,企业产品特点是业务复杂,所以架构的侧重点不一样。 + +架构师在架构设计后,产出首先是架构设计文档,让大家理解架构。然后还要写架构开发的文档,比如如何基于这个架构开发功能模块,有哪些公共 API 可以调用,怎么样是最佳实践,要遵守哪些规范等。 + +再要帮助搭脚手架和基础模块或示例项目,也就是要搭建一个最基础的可运行项目,通过这个项目,大家可以直观地理解你的架构是怎么落地的,通过基础模块或者示例项目,可以知道如何基于框架开发,后面就也可以照葫芦画瓢照着实现。 + +还有就是在开发过程中,要答疑、解决架构中存在的问题,对架构做优化,还要做代码审查,对于不符合架构规范的地方要指出和修正。 + +No.7 + +Dora:互联网架构,要考虑互联网很快的迭代速度,所以对于扩展等特别注意。企业架构,内部 IT 系统相对稳定,对比互联网架构,更简单? + +宝玉:挺好的分析。帮你补充几点:互联网架构不仅迭代会快一些,用户规模通常更大,但业务也会单一些;企业应用通常业务比较复杂,尤其是和行业会有一些结合,但是用户规模要小很多。这些特点,都会影响架构设计的选择。 + +No.8 + +WL:老师能不能具体讲讲重构有哪些原则和要注意的地方,感觉一直得不到要领。 + +宝玉:重构的要领我觉得两点。 + +第一:你要先写一部分自动化测试代码,保证重构后这些测试代码能帮助你检测出来问题; + +第二:在重构模块的时候,老的代码先保留,写新的代码,然后指向新代码,或者用特定开关控制新旧代码的指向(这样上线后可以自己先测试,有问题也可以及时关闭),然后让自动化测试通过,再部署测试,新代码没问题了,删除旧代码。 + +No.9 + +bearlu:有没有事情管理的工具?因为如果不记录下来,一会儿就忘记了。 + +宝玉:留言区 McCree 同学推荐了滴答清单。我个人的话,一般就用系统自带的记事本记一下,或者贴一个便签纸在显示器。如果时间跨度长,我就记到 Calendars 上,加上提醒。工作中的任务,我则会创建成 Ticket。 + +No.10 + +W.T:现在还有一种说法:提倡基于主分支开发,效率更高;而不是您提到的每人基于自己的分支开发完再合并回主分支。您怎么看待这个问题? + +宝玉:我认为对于软件工程来说,很多问题,并不是只有唯一解,即使是最佳实践,也得看适用的场景和团队。 + +无论是基于主干还是分支开发,有两点需要注意的: + + +就是一定要有一个稳定的分支,可以随时发布的那种,至于是叫 master 还是叫 release 并不重要。 +合并之前要有代码审查和自动化测试(配合 CI)。 + + +上面两点才是核心。 + +No.11 + +hua168:如果一个项目有 5 个开发做,持续集成怎么保证不乱?比如开发 A 刚刚修复的 bug1,开发 B 把自己修复的 bug2 上传,之前的代码 bug1 没修复,怎么办?如果采用分支怎么合并?如果是直接更新 master 分支,那 A 不是白做了? + +宝玉:要注意是“合并”而不是“覆盖”。比如说 bug1 涉及 file1 和 file3 的修改,那么开发 A 合并的时候只合并 file1 和 file3。 + +等到开发 B 修复了 bug2,修改了 file1 和 file2,file2 直接合并,file1 需要手动去修复合并冲突才能合并。 + +每个人开发之前,都会从 master 获取最新版本,合并的时候,如果出现冲突,要先解决冲突才能合并进去。这些其实应该自己去动手试试,会体会更深刻。 + +No.12 + +dancer:在微服务架构中,一个服务在测试环境的交付验证,往往还依赖于其他相关服务的新版本,导致新的 feature 很难独立的交付。对于这种情况,有什么好的方法吗? + +宝玉:我觉得对于大部分时候,微服务之间应该是独立的,而不是依赖过于紧密,如果每一个新功能都会这样,那架构设计一定是有问题的,需要重新思考服务划分的合理性。 + +但你需要有更多上线或者场景我才能针对性提出一些意见。对于有一些确实需要跨服务合作的大 Feature,这样也是正常的,就是需要一起协作,实现商量好通信协议,分头开发,再联调。 + +No.13 + +Gao:老师所讲排查生产问题的案例,首先回滚版本,再看日志。这会引发更多的系统功能不可用吧,两个版本之间的功能差异尚不清楚就直接回滚,系统风险是否被进一步扩大? + +宝玉:这个确实要具体情况具体看,因为我日常的系统上线,都会有回滚方案,回滚也是自动化的很方便。有些跟数据库相关的,如果数据库结构发生变化又产生了新数据,确实没法直接回滚。 + +No.14 + +kirogiyi:团队成员的能力和素质参差不齐,如何有效的去组织和管理项目的自动化测试,自动化集成? + +宝玉:首先,你要先搭建好自动化测试环境,让自动化测试代码能跑起来,最好要和 CI(持续集成工具)整合在一起,每次提交代码 CI 都会跑自动测试,然后能看到运行结果。 + +然后,把自动化测试作为开发流程的一部分,比如说要代码审查和自动化测试通过后才能合并代码。这部分工作如果和 CI 集成会容易很多。 + +再有就是要培训,比如遇到不会写的,开始先带着他写几个,确保他学会了自己能写,然后下次代码审查的时候,看到缺了就要求补上,还不会就继续教,来不及写的就创建个 Ticket 跟踪起来。 + +简单来说,就是代码审查 +CI+ 培训。 + +No.15 + +探索无止境:各种类型的测试覆盖率你们一般采用什么指标?个人感觉在理想的情况下最好是做到百分百覆盖率。 + +宝玉:100% 覆盖,这个我觉得可以作为一种理想追求,但是没必要追求极致,还是要在进度和质量之间有个平衡比较好,毕竟进度也很重要。 + +另外对于前端业务,我更重视集成测试的覆盖,对于主要业务场景集成测试覆盖到位后,单元测试也就有比较多的覆盖,相对性价比更高,然后再逐步补充单元测试的覆盖率。 + +No.16 + +起而行:持续集成怎么理解呢?我看知乎上说,有的团队成员在一天内多次进行编译,发布或自动化测试。 + +宝玉:狭义的持续集成不包括发布,主要指集成,持续的(每次提交代码变更都触发,频繁地提交)对代码进行集成(合并到主干),但集成前要确保自动化测试通过。广义的持续集成还包括部署,也就是集成后自动部署测试环境 (持续交付) 或者生产环境(持续部署)。 + +No.17 + +小小:请问下有没有介绍开发如何写好测试不错的书? + +宝玉:推荐:《how we test software at microsoft》中文版《微软的软件测试之道》。不过没有书其实你也可以找到很多资料的。比如我平时写前端程序,那么我会去 GitHub 或者 Google,通过关键字、语言找跟我项目类似的开源项目,然后看其中有没有自动化测试写得好的。 + +找到了 (例如:reactstrap、electron-react-boilerplate、kitematic) 就照葫芦画瓢好了,因为都是真实项目,所以特别简单有效,建议你也可以试试。 + +另外耐心一点,你也可以看到很多关于测试知识分享的技术文章,多看一看也有收获。 + +No.18 + +hua168:代码审核是纯手工做的吗?没有好的工具? + +宝玉:代码审查可以参考 GitHub 上一些开源项目的 PR Review,通常网页上可以清楚地标记出代码修改,针对代码行可以写 Review 的评论,这就已经很方便了。 + +其他工具主要是 Lint 检查代码规范、语法错误等,这个一般在 CI 里面就集成了。 + +精选留言 + +陈珙 : + +在没有特殊要求的情况下,项目中更加倾向选择更为熟悉的技术,因为我们需要对项目的质量与交付时间负责,可以做到可控的。而新技术有着新的设计思想与强大的功能,同时也伴随着无法预知的“坑”。在后续产品迭代的时间里,有针对性的升级或者选择更换同类技术里更优的。 + +相关阅读: 22 | 如何为项目做好技术选型? + +Y024: + +Appfuse(一个 Web 开发基础平台)的作者 Matt Raible 曾总结了选择 Web 框架的 20 条标准。 + +同时,他也整理了一份表格,你可以根据自己的权重进行调整,产生自己的分析。 + +但是现实情况,大家可能更遵循的是“经济适用原则”,比如:很多人提到的,负责人会啥就用啥,或者大公司者业界流行什么就用什么。 + +有位大佬说过,“这个世界是,你认为有很多选择,其实只是幻觉,大多数人只有很少的选项。技术研讨会,搞一个选型:hadoop + mysql + xx 时髦技术。架构师唾沫四溅吹一下午,结果老老实实上 Oracle 单例。” + +相关阅读: 22 | 如何为项目做好技术选型? + +kirogiyi : + +架构师是一个概念性职位,没有明确的界定,需要具备的能力和素质也是千差万别,每个开发人员心目中的架构师画像也都不一样,神秘的 IT 牛人,高级的保姆,无休的恶魔… + +在我看来,一名优秀的架构师应该具备良好的技术思维、产品思维和项目管理思维。技术思维是基础,评估技术难度、分析技术复杂度、准确把握技术方向,这些都是架构师在设计架构时面临的技术决策。 + +产品思维是骨架,在产品思维上构建起来的整体全面的产品意识,可以对业务、功能、模块进行明确的抽象、分治、迭代等等。 + +项目管理思维是方向,无论是敏捷管理模型还是瀑布管理模型,都需要在不同的时间、不同的环境条件下去关注金三角理论的取舍所带来的影响,降低技术以外对项目带来的局限性。 + +不过,架构师也不是想象中的那么神秘。开发人员和架构师的差别,最主要是层次和格局上的差别,导致最终产生了不同的结果而已。 + +试想,两个能力相同的开发人员,一个的目标是每年涨工资(80% 开发人员),他会去努力多做事,拓展技术的深度;一个的目标是 CTO(20% 开发人员),他会去努力多做事,多思考,多学习,多交流,尽力做到面面俱到。几年以后的结果就不言而喻,至少坚定的目标能够推动过程的发展。 + +相关阅读:23 | 架构师:不想当架构师的程序员不是好程序员 + +alva_xu : + +讲到架构,我想先得谈一下康威定律。康威在 1967 年曾说过: + + +Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. + + +通俗地说,就是组织形式等同于系统设计。所以系统架构设计的进化,是和组织形式的变化结合的。 + +从 ITIL 来说,BIA,(business IT alignment)是 IT 的核心,所以充分认识组织的业务模式和运营方式,才能让架构师设计出适合于企业的系统架构,架构设计的最高境界就是适合企业业务的运营。 + +从单体架构到微服务架构,从前后端分离到中台,都是架构适应业务(功能与非功能需求)的体现。所以架构师首先必须要有业务思维、产品思维。TOGAF 把企业架构分成业务架构、应用架构、数据架构、技术架构四个子域,我觉得相当全面。 + +从程序员开始,如果能培养好老师讲的架构师能力模型中的四个思维和三个能力,我们可以给自己规划出一个架构师的成长路径,从单个业务应用开始,然后扩展到一个业务领域,最终到达企业架构师,甚至成为跨企业应用架构师的境界。 + +相关阅读:23 | 架构师:不想当架构师的程序员不是好程序员 + +纯洁的憎恶: + +技术债务不全坏,与金融债务一样,需要具体问题具体分析。轻率 & 有意的债务要避免。谨慎 & 有意的债务有收益。 + +轻率 & 无意的债务要警惕。谨慎 & 无意的债务要改变。识别债务防患于未然。根据成本收益分析,决定重写(一次性还款)、维持(只还利息)还是重构(分期付款)。 + +相关阅读:24 | 技术债务:是继续修修补补凑合着用,还是推翻重来? + +kirogiyi : + +在研发过程中,产生技术债务的时候,稍微有点技术功底的人,或多或少都会有感觉的。 + +比如:有重复代码的时候,会意识到好像已经写过了;函数命名的时候,会意识到好像有个相似的名称已经命名过;函数行数过多的时候,自己心里会感觉不舒服等等。 + +更有甚者,你去整理这些问题还会被同事标上“强迫症”患者的称号,还是放弃吧。技术债务就这样在外部和内部双重压力下自然而然的产生了。 + +那么如何产生有利的技术债务呢?我觉得应该从公司制度、研发流程、个人素质培养三方面入手。 + +公司制度实际上是为领导层准备的,领导层以身作则去影响下面的员工,员工就没有冒犯的理由,比如合理的奖惩制度,要做到公平合理,一视同仁; + +研发流程主要是让团队成员知道自己什么时候该做什么事情,如何去按照指定的约束去做好自己的事情,除此之外,还应该给予明确的成长上升空间; + +员工素质的培养则需要从一个人的职业素质,技能优化,团队协作方面着手,让他们拥有积极努力的心态参与到工作中去,这基本上就能解决最基础的技术债务问题(领导决策错误产生的技术债务另当别论)。 + +在我遇到过的技术债务中,主要由领导决策、产品业务逻辑、技术最初选型、技术更新换代、团队综合素质中的一种或几种导致。对此,我只能说个人能力达到什么层次就应该去解决什么层次的技术债务,不能去推诿和落井下石,在你手中的技术债务就应该当成自己欠下的技术债务来解决,这样才能持续性的做好自己分内和分外的事情,工作起来才能得心应手。 + +相关阅读:24 | 技术债务:是继续修修补补凑合着用,还是推翻重来? + +kirogiyi: + +我觉得高效,意味着自律,自律的好坏是可以通过你散发出来的气息让周围的人感受到的,比如:说话有没有条理,做事拖不拖延等等。 + +生活自律,你会发现每一分每一秒都充满了希望和力量,用积极乐观的心态去完成每一件事,知道自己上一步做好什么,下一步才能做好什么。 + +工作也是一样,要想高效完成任务,需要利用前辈们总结的思想和方法,去长期实际应用,在使用的过程中就会体现出你的高效,不能说我知道单元测试,我知道 CI…,很少有人讲我一直在用。 + +如果我们注意观察,会看到身边的同事,有的很少加班(活蹦乱跳),有的经常加班(蔫头耷脑),做了一样的事情用了不一样的时间,此时就能真正的体会到高效做事的魅力了。 + +我不提倡加班的原因就在于此,但那是针对高效人士的,低效人士不加班,老板是不会答应的。而一般对自己的时间把握比较好的人,在估算工作时间或工作量的时候,都比较果断,不会支支吾吾,还会主动给出具体时间点和阶段性成果,让人觉得这才是真正做事的人应该有的态度。 + +我的看法是,积极、主动、自律是高效人士的必备素质。 + +相关阅读: 25 | 有哪些方法可以提高开发效率? + +nigel: + +就学习能力而言,“祭海先河,尤务本原之学”,重要的是对基础知识的掌握。就像侯捷先生说的“基础的东西不易变,不易变的可重用”。 + +相关阅读:27 | 软件工程师的核心竞争力是什么?(上) + +_CountingStars: + +之前看到过一个关于 code review 的观点:在让别人 review 你的代码的之前,你要确保你的代码没有基础的问题,比如单元测试要通过,不能有代码风格问题,首先你要确保你的代码是能正常工作并解决需求的。当然这些基本都可以通过自动化来操作,比如提交 PR 的时候,自动化的检查代码风格,运行单元测试。保证邀请别人 review 你代码的时候,不要为这些小事费精力,提高 review 效率和积极性。 + +相关阅读:30 | 用好源代码管理工具,让你的协作更高效 + +bearlu: + +其实我觉得用什么源代码管理工具都没关系,最重要是要了解工具,形成流程,按流程走,然后纠正流程。 + +相关阅读:30 | 用好源代码管理工具,让你的协作更高效 + +思辨时刻 + +Charles: + +三四线城市,技术选型前期主要考虑:当地市场什么人才比较充足,比如后端 PHP 人多,那就 PHP,学习成本也低,几人团队协作起来也不是大问题,而且前期扩充人员也比较好招人;另外前期应该也不会在语言层面出现性能问题。 + +然后数据库基本就选 MySQL,够熟悉够成熟。前端的话,web、小程序、ios、android 之类的都统一 MVVM 思想,进行前后端分离开发,这样各个用户端都可以统一 API 提升效率,这个也会从产品角度思考。 + +如果产品经理就只是需要一个 PC 网站,而且短期也没升级计划,就选择传统的后端渲染 web 页面方案。可能会站在目前项目或经历过的项目经验去思考问题,期待老师回复指正。 + +宝玉: + +我觉得你的选型思路在项目发展阶段,包括没有很大规模之前都是没有问题的。选最熟悉的、流行的往往也是风险比较低的。包括如果就是一个 PC 站也不做 SPA(单页应用),也没有必要前后端分离。还是看是不是能低成本满足好项目需求和业务发展。 + +有一点补充的,就是前端除了 MVVM,像 React 的 Flux 和 Redux 的架构模式,也是一种很好的架构模式,但在非 Rect/Vue 的项目中应用不多。 + +相关阅读:22 如何为项目做好技术选型? + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/一问一答第4期14个软件开发常见问题解决策略.md b/专栏/软件工程之美/一问一答第4期14个软件开发常见问题解决策略.md new file mode 100644 index 0000000..27060a6 --- /dev/null +++ b/专栏/软件工程之美/一问一答第4期14个软件开发常见问题解决策略.md @@ -0,0 +1,405 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 一问一答第4期 14个软件开发常见问题解决策略 + 你好,我是宝玉。恭喜你完成了软件测试和线上维护这两个模块的学习。 + +软件测试是保障软件质量的重要一环,但也不能过于依赖软件测试,毕竟软件质量体现在功能质量、代码质量和过程质量三个方面,而软件测试只能帮助保证功能质量,代码质量和过程质量还需要团队一起努力。 + +现在大厂已经很少有手工测试的岗位了,大部分测试工作已经转移到开发上,同时自动化测试的比例越来越高,但这相应的对开发的要求更高了,不仅要写功能代码,还要写自动化测试代码。 + +软件测试也离不开对工具的使用,通过 Bug 跟踪工具报 Bug 和跟踪 Bug,使用测试管理工具管理测试用例,基于自动化测试框架写自动化测试代码,借助性能测试工具对软件性能进行压力测试。测试工具还可以和持续集成一起整合,最大化发挥测试工具的效应。 + +软件测试做好了不代表你的软件就是安全的,不会导致账号密码泄漏,要想构建安全的软件,需要在整个软件生命周期中都重视安全问题,各个阶段考虑到安全方面的问题,防患于未然,构建出安全的软件。 + +当你的软件已经测试通过,准备上线发布了,不要忘记在软件发布前做好版本规划,尽可能的让软件的功能质量,满足好用户的预期。一方面要尽可能提供应有的功能和保证质量,另一方面也可以通过合理的发布策略来降低用户的预期。 + +传统软件发布上线后,就是运维负责保障线上运行了,但这种分工也导致了开发和运维之间在沟通协作上不够紧密,甚至有冲突,于是 DevOps 出现了,它帮助解决开发和运维之间的沟通协作问题,提升运维开发和自动化能力。通过自动化、信息透明可测量、共同协作的原则,达到更快更可靠的构建、测试和发布软件的目的。 + +软件发布后不代表项目就结束了,还需要处理线上故障,保障运行。遇到线上故障不用慌,要先恢复生产减少损失,然后再去找原因,最后再去总结复盘。日常还要对故障多演习,这样真的有故障发生了才能做到从容应对。日志管理工具是线上故障排查的好帮手,能帮助你快速定位线上故障,还可以对日志的数据进行监控,有问题提前预警。 + +最后,项目结束后不要忘记对故障进行复盘,总结成功经验,吸取失败教训。把好的实践继续发扬光大,对于不好的实践马上停止做出积极有效的改变。 + +以上就是两个模块内容的总结,希望你在学习这些知识后,能应用到实际的项目中去,帮助你项目的质量再上一个台阶。 + +今天加餐继续分享我们专栏的精彩问答和留言,这些问答和同学们的分享都是对专栏内容的最好补充,希望可以帮助你更好地学习和理解软件工程知识。 + +一问一答 + +No.1 + +hua168:质量是怎么打分的?算进 KPI 考核吧?直接用代码质量管理软件(如 sonar)实现自动检查可以吧? + +宝玉:很遗憾,都不好量化,软件检查只是辅助,可以作为一个参考。代码质量要看满足需求,是否设计良好,代码简洁逻辑清晰,可维护、可测试、安全高性能;过程质量要看开发过程对软件工程和项目管理知识的应用;功能质量要看客户满意度。 + +No.2 + +砍你一刀:能分享一个比较好的测试用例模板吗? + +宝玉:我建议你试试 testrail,它的测试用例模板非常专业。 + +对于测试用例: + + +几个关键的字段是:标题、描述、优先级、分类。 + +测试类型:功能测试、性能测试、回归测试、冒烟测试等。 + +自动化状态:没有自动化、只能手动测试、只能自动化、集成 CI 等。 + +先决条件:这个用例需要满足好什么条件。 + +测试步骤:写清楚一步步的执行步骤。 + +期望结果:操作完成后结果应该是什么样的。 + + +No.3 + +kirogiyi:最近发现一种现象,开发人员面对测试人员的时候,会展现出一种职业选手遇到业余选手的姿态,傲慢、理所当然,我觉得这是一种不正常的心理状态,应该怎么去管理? + +宝玉:这确实是常见的现象,核心还是多一起合作多相互了解吧,让开发人员看到测试的核心价值,就是对测试方案的设计。 + +我对测试人员敬佩的地方不在于他们会写自动化测试,毕竟这个我写起来还更好,而是他们总能从我没想到的角度测试出来 Bug,从而帮助我提升程序质量。 + +可以安排一些开发人员和测试人员一起合作的事情,比如测试人员提供测试方案测试用例,开发人员按照测试用例去实现自动化测试,让开发人员明白,做好测试其实不是他们想的那么容易。 + +No.4 + +和:从需求分析到设计,开发,测试,部署,运维,都是我一个人的工作。这样的情况,如何工作效果会更好一些? + +宝玉:对于这个问题,我觉得你可以自己先分析一下,你觉得目前哪些地方做的好,哪些地方可以有改进?可以从几个方面分析,比如: + +工具: + + +有没有用源代码管理工具? + +有没有用持续集成跑单元测试? + +有没有用 Bug/ 任务跟踪系统? + + +开发流程: + + +多长时间一个项目周期,一个功能从开发到上线要多长时间?质量如何? + +有没有做需求分析和确认?会不会做出来东西不是业务部门想要的? + +有没有开发前先做简单的技术设计? + +有没有写一些基础的测试用例? + +有没有开发后自己测试和找业务部门帮忙测试? + +线上的故障有没有一个合适的流程去处理? + +有没有写一些基本的文档,如果来一个新人了能否接手? + + +技术实践: + + +有没有写自动化测试? + +有没有用好的技术框架或开源组件? + +有没有自动化部署? + +分析出问题,知道哪些地方做的不够好以后,就可以有一个改进的计划 ,看如何将改进方案落实下来。 + + +还有就是一个人开发,缺少向其他人合作和学习的机会,可以有意识地创造更多这样的机会,比如内部多和其他部门合作。外面可以参与一些开源项目。 + +No.5 + +hua168:现在不是流行测试驱动开发吗?先写测试代码再写实现代码,那写完再让专门的测试去测? + +宝玉:测试驱动是一种很好的开发实践,但普及率也不算很高。可以看到自动化测试那一篇,测试驱动写的是单元测试,并不能保证不出 Bug,只是说能有效提升代码质量。还有就是开发人员测试自己写代码,很容易遗漏编码时就没考虑到的逻辑。 + +No.6 + +果然如此:如果每次发布都对所有方法自动化测试,那么:1. 一定会耗费很多时间;2. 数据库产生很多测试历史数据;3. 写测试用例能达到覆盖率高的写代码技巧,如边界测试代码、幂等测试代码如何实现。这种情况怎么办? + +宝玉: + + +自动化测试确实会耗费很多时间。自动化测试代码通常是金字塔结构: + + + +单元测试(小型测试)代码最多,执行也最快,占总比例的 70% 左右,通常 1 分钟内; + +集成测试(中型测试)代码其次,执行比较快,占比 20% 左右,控制在 10 分钟以内; + +端对端测试(大型测试)最少,执行慢,占比 10% 左右。 + + +一般 CI 里面跑单元测试和集成测试,耗时 10-15 分钟左右,其实还可以接受。 + + +跑自动化测试,数据库有不同策略。单元测试不访问数据库,完全模拟。集成测试只访问本机数据库,或者模拟的内存数据库,每次创建新数据库,或者使用完清空数据库。端对端测试,每次创建唯一数据(例如增加固定数据 + 时间戳),连接真实的测试环境,可以不清理数据。 + +高覆盖率的关键在于,在写代码时就注意让代码方便地被测试。也不必过于追求 100% 覆盖,70% 以上我觉得就不错了。 + + +No.7 + +宝宝太喜欢极客时间了:对测试这块一直很疑惑,测试脚本、测试用例、测试数据这三者如何配合一起通过 CI 进行自动化测试? + +宝玉:是这样的,CI 本质上只是一个像流水线传送带,你的代码提交了,流水线传送带开始工作,你可以在传送带上面添加任务。 + +简单来说,你可以想象成 CI 的一个任务启动后,给你一个干净的虚机(实际是运行 Docker Container),然后帮你把当前代码下载下来,帮助你配置好运行环境,然后你就可以在里面安装任何软件、服务和运行任何脚本。 + +举例来说,你可以在传送带上增加以下任务: + + +安装所有的依赖包; +运行模拟服务(比如一个内存数据库); +运行单元测试; +运行集成测试。 + + +如果上面所有任务都成功了,那么这一次的 CI 任务就成功了,其中一个失败,这一次的 CI 任务就失败了,然后你就要检查什么原因导致的,然后修复再重新执行,保障 CI 任务成功执行。 + +No.8**** + +一步:Code Review 是指把代码放到大屏幕上大家一起看,还是类似 Github 上的合并代码的时候发个 PR,然后另一个人对需要合并的代码进行检查,检查通过后才同意合并请求? + +宝玉:我觉得两种都算 Code Review,只是形式不一样。 + +通常 PR 这种 Code Review 应该是贯穿到日常开发过程中的,每个人都可以去 Review,有 1-2 个人 Review 通过就可以。合并的话不只是 Code Review 通过,还需要自动测试通过。 + +而大屏幕这种参与人多,成本高,属于偶尔针对特殊故障分析、学习研讨等目的才做的。 + +No.9 + +yellowcloud:我们目前有一个项目是做实时数据采集的,对方将实时数据推送给我们,基本上每天每个时刻都可能有数据推送过来。这样就导致一个问题,我们部署新的版本时,他们的数据还在推送,这样就不可避免地丢失了部署过程中的数据,对方也没有重新推送的机制。请问,这种问题有没有比较好的解决方案,以解决更新版本时数据丢失的问题? + +宝玉:这个问题其实不复杂,你可以将服务分拆,独立出来一个专门接受数据的服务,这个服务极其简单,只做一件事:接收数据,并存储到数据库或消息队列。 + +你原有的服务,改从数据库或者消息队列读取即可。更新部署的时候,接受数据的服务就不要轻易更新了,这样就不担心会丢数据了。真要更新,只要和对方协商一下,暂停推送就好了。 + +No.10 + +Charles:如果是瀑布流带点敏捷部分实践的开发方式,总觉得 UI 这个岗位工作量不怎么饱和,一个版本过来特别是小版本迭代,周期可能是两周,UI 可能 1 天就搞定了,其他岗位都差不多要全程跟下来,这个问题出现在哪里? + +宝玉:这个问题有点不好回答,毕竟对你项目情况不够了解。 + +我觉得,如果 UI 这个岗位对你的团队来说是必须的,并且 UI 设计师很好地完成了他 / 她的工作,那么就很好,没有任何问题。毕竟有的人效率就是比较高,好过故意磨洋工看起来很忙。 + +如果 UI 这个岗位是可有可无,那么就可以考虑不设置这岗位,将工作外包出去,或者尽可能用一些标准的 UI,或者让前端工程师兼职 UI 设计工作。 + +No.11 + +yellowcloud:宝玉老师能不能介绍一整套可以简易部署使用的 devOps 的工具,方便小公司快速部署、实现,在实践中感受 devOps 的魅力。 + +宝玉:如果你要部署持续集成环境,可以先试试 Jenkins 或者 Gitlab CI。如果你要部署日志和监控系统,可以试试 ELK,也就是 Elasticsearch、Logstash、Kibana 三者的结合。网上可以找到很多安装使用教程。 + +No.12 + +一步:搭建自动化测试,自动化部署,自动化监控系统,都自动化了,开发都做了,是不是就不需要运维和测试了? + +宝玉:自动化只是把重复的体力活做了。自动化测试的话,还是需要测试人员写测试用例才能有更好的测试效果;自动化部署和监控,也离不开专业运维人员的设计和搭建。 + +但是可以预见的是,以后低端的手工测试和运维岗位会被挤压的很厉害。如果你看大厂的招聘岗位,这些低端手工岗位都极少或者根本就没有。 + +No.13 + +邢爱明:1. 谁来主导线上故障处理的过程?2. 故障排查是不是应该有一个标准的分析过程,让运维、开发、安全各方能更好的协作?3. 便利性和安全性如何平衡? + +宝玉: + + +谁主导线上故障,我觉得有两个指标要考虑:一个是这个人或者角色要懂技术懂业务,这样出现故障,能对故障进行评级;另一个是要能调动开发和运维去协调处理,这样出现故障能找到合适的人去处理,不然也只能干着急。 +故障排查上:如果是操作系统、数据库、网络等非应用程序故障,应该是运维负责;如果是应用服务故障,应该是开发去负责,即使开发最近没有去做发布也应该是开发去查。因为只有开发对于应用程序的结构最清楚,才能找出来问题。排查过程中,运维要给予配合。 +应该搭建起来像 ELK 这样的日志管理系统(可参考《38 日志管理:如何借助工具快速发现和定位产品问题 ?》),将应用程序日志也放上去,这样正常情况下就不需要去登录服务器了,直接就可以通过日志工具查看到异常信息。另外,一些特殊情况应该允许开发人员登录服务器排查定位。 + + +No.14 + +Charles:目前只放了 nginx 日志到日志服务做一些简单的分析,还有其他什么日志是应该放到日志服务里的?有什么比较好的实践吗? + +宝玉:我觉得应用程序的日志也应该考虑放进去,对排查问题很有帮助。应用程序的异常信息、错误堆栈非常有用,必须确保记录下来了。 + +举个例子来说,你的一个手机 App,一些特定场景下,某个 API 请求出错,而这个 API 可能背后会连接多个服务或者数据库,这样的场景下,光靠 nginx 日志是不够的,必须要有应用程序的日志配合才好定位。 + +你可以参考我在《37 遇到线上故障,你和高手的差距在哪里?》提到了一个 requestId 的实践。 + +精选留言 + +kirogiyi: + +产品设计、软件开发、软件测试都应该对产品质量负责。 + +产品设计要重视产品需求的完整性,提升用户的操作舒适感,展现流畅的页面逻辑设计,这是产生良好软件质量的开端。在进行产品设计评审的时候,除了评审人员外,相应的软件开发团队和软件测试团队一定要派人员参加,不能坐等任务分配。有的开发团队和测试团队不去了解需求和产品设计情况,只是一味等待产品的 UI 设计,久而久之,就形成了少交流多看文档的习惯,于是大家就开始机械般各顾各的,做完了扔出去就好,就很难在产品质量上达成共识。 + +软件开发是核心,开发人员对产品的理解程度和自身的技术水平决定了产品的质量和迭代周期。如果开发团队不去与产品团队交流,不去与测试团队核对测试用例,那么在开发过程中大多只会去关注是否实现和能否实现,至于产品质量出现的问题,就不是他们关注的重点。然后就会自私地认为产品设计需要增删改是产品团队的原因,产品上线出现 Bug 是测试团队没有覆盖到,部门之间的战争就开始酝酿直至爆发。 + +软件测试更多的是发现问题、监督问题和约束行为。发现问题的重点在于通过完善的测试覆盖,去找到开发过程中的盲点,而不是去为别人的疏忽大意导致的错误埋单,比如:开发提交的产品有错别字什么的,这种错误就是开发负责人应该承担的。 + +在明确发现问题后,测试团队有权利去监督开发团队解决问题,直至问题得以彻底解决。除此之外,测试团队可以对开发团队行为进行约束,双方协作完成自动化测试体系和流程的构建,共同遵守规则:开发团队负责单元测试、集成测试和系统测试的代码编写,测试团队负责查缺补漏和必要的人工测试。 + +因此,个人认为,产品、开发、测试的紧密合作是保障产品质量的必备条件。 + +相关阅读:31 | 软件测试要为产品质量负责吗? + +纯洁的憎恶: + +解铃还须系铃人,要想提高软件质量,就要着眼于整个生产链条,每一个环节都要为提高质量出力,而绝不能仅仅依靠质量监控岗位或部门。相反,很多企业设置了类似的部门或岗位,并把质量、安全的重担压在他们肩上,但又没有赋予足够的权力去介入、影响整个链条,结果可想而知。不谋全局者不足以谋一域啊。 + +把整体质量按照生产链条或链条上的不同角色,划分为若干子部分。通过有机的把控各个子部分质量,形成合力,达到提高整体质量的目的。 + +相关阅读:31 | 软件测试要为产品质量负责吗? + +纯洁的憎恶: + +看来是否需要专职测试人员,在一定程度上需要视具体业务情境而定。不同的情境会有不同的异常情况和极端情况,需要有针对性的设计出完备的测试用例。而且在 Bug 修复后,也要保证修复本身没有 Bug。 + +所以测试也是一个系统性的工作,如果取消专职测试人员,不仅对开发业务水平要求更高,还需要项目自身的不确定性低一些。感觉有测试思维的开发人员,更有可能写出健壮的代码。 + +相关阅读:32 | 软件测试:什么样的公司需要专职测试? + +yasuoyuhao: + +代码就像程序员的名片,要对写出来的代码负责,最好的负责方式就是写测试代码,让每次代码变动,都不会影响到其他代码的运行,避免所谓的改 A 坏 B,节省迂回的时间浪费。也为 CI/ CD 做好准备,无论目前有没有。 + +在我们公司程序员不想写测试的原因大多是,不知道怎么开始写,不知道重点应该测试什么。先写测试的开发模式让他们觉得不习惯,但这些都是过程,培养良好的撰写测试代码习惯后,开发品质更有保证,提升开发效率,提升个人能力,我想都是有帮助的。 + +程序难免有 Bug,透过追踪软件,良好地管控 Bug 数量与修复进度,并且补足测试。 + +相关阅读:33 | 测试工具:为什么不应该通过 QQ/ 微信 / 邮件报 Bug? + +纯洁的憎恶: + +新手用野路子解决问题,高手用模型解决问题: + + +给问题评级。紧迫的调动优势资源先解决,一般的往后放放。 + +尽快恢复生产。生产是企业的首要职责,遇到问题优先恢复生产,减少直接损失,然后再正式地解决问题。 + +找到问题出现的位置。“搜集证据”,通过“粗调”在时空上缩小包围圈,再用“精调”明确问题点,运用排除法最终锁定问题。 + +分析原因,修复问题。 + +提出解决方案。钥匙不一定插在锁眼里,要沿着问题的线索不停“倒带”找到根源。再针对根源,站在系统和流程的高度制定解决方案,避免问题复现。 + + +重点: + + +通过故障报警 + 业务骨干轮值机制,让正确的人第一时间响应问题。 + +通过实战演习,确保应急预案稳定可行。 + +通过使用日志记录和分析工具,积累、整理日常生产信息,出现问题才有得分析,否则重现问题也无济于事。 + + +相关阅读:35 | 版本发布:软件上线只是新的开始 + +alva_xu: + +线上故障,这是 ITIL 要解决的问题。我觉得最主要还是从三个方面来看。一是从流程上,对于事件管理、问题管理、变更管理、服务等级管理等,要有明确的流程。二是要有合适的工具,比如 ticket 系统,CMDB,监控工具、日志平台等。三是从人员组织来看,要有一线、二线和三线团队的支持,根据所创建的 ticket 的严重性和紧急性,给予不同 level 的支持。当然这也是目前流行的 devops 要解决的问题。 + +相关阅读:37 | 遇到线上故障,你和高手的差距在哪里? + +alva_xu: + +我觉得 scrum 方法中提到的两个会,可以作为项目复盘会内容的参考。Sprint 评审会议(Sprint Review Meeting)和 Sprint 回顾会议(Sprint Retrospective Meeting)。Sprint 评审会议在 Sprint 快结束时举行 ,用以检视所交付的产品增量并按需调整产品待办列表,是对工作成果的评审。Sprint 回顾会议是 Scrum 团队检视自身并创建下一个 Sprint 改进计划的机会。是对方法论的回顾和提高。项目复盘会也应该从这两个角度去做总结提高。 + +相关阅读:39 | 项目总结:做好项目复盘,把经验变成能力 + +邢爱明: + +回想一下,项目复盘想要效果好,需要做一些准备工作: + + +复盘会前,要求项目组核心人员对项目的情况先自己进行总结,包括做得好和做的不好的方面,有书面文件输出。先要有思考,复盘会上大家才有可讨论的内容,否则会议上大家可能就是随便说说,复盘会成了走形式。 +复盘会的会议主持人,需要有比较强的会议主导能力,尤其是参加会议的人来自多个部门的时候。因为大家总结项目中做的不好的地方,难免会涉及到多个部门或团队配合的情况,且每个人的描述也不可能做到百分之百的客观和公正。 + + +如果有人认为总结的内容有问责的含义或需要自己承担责任,复盘会就很容易变成了甩锅会。这时候就需要会议主持人正面介入和引导,让大家讨论解决方案和改进措施,确保按照预定的议程开复盘会议。 + +相关阅读:39 | 项目总结:做好项目复盘,把经验变成能力 + +思辨时刻 + +成: + +我们公司团队小,每次 app 开发完成后,要求测试人员组织开发全体测试 2 次,用于保证质量。团队小测试人员技术有限,性能,安全等一般难以保证。 + +宝玉: + +其实即使是小团队,也应该加大对自动化测试对投入,绝对是磨刀不误砍柴工,这样 App 开发完成后,很多测试就可以自动化完成,节约时间和人力。当然没有自动化测试覆盖的话,这也是很好的一种测试方式。 + +相关阅读:31 | 软件测试要为产品质量负责吗? + +毅: + +项目负责人为软件质量总责任人。功能,代码,过程都要关注,并不一定要亲力亲为,因为除了质量他还要兼顾范围、时间和成本。提升质量意识最理想状态是组员有质量人人有责的意识与行动,但实际上这很难。如果自下而上做不到,就自上而下用制度强推,有奖有罚。 + +最后补充一点就是推行质量保障是需要公司层面作为支持的,否则在推行过程中会有不少阻力,也许在强人项目经理的推动下,个别项目能做的很好,但心会很累。 + +宝玉: + +确实还要考虑金三角的因素。软件项目,也并非一定要有强人项目经理,其实只要按照软件工程,踏踏实实做好每一个环节,质量就不会差到哪去。 + +比如说在需求上多花点时间精力,把需求确认清楚,这就成本一半了,然后再基于确定的需求做好架构设计再开发,最后开发后做好测试,那么质量就有了基本保障了。 + +相关阅读:31 | 软件测试要为产品质量负责吗? + +邢爱明: + +谁来做测试工作,这也是我一直比较疑惑的地方。我现在是甲方,主要做的是企业管理软件,业务逻辑和流程控制都比较复杂,部分系统是需要一些领域的专业知识。 + +软件开发的时候,基本采用的是瀑布模式。首先由专门的人员做需求澄清,分析和设计,一般我们称为业务分析师,他们这些人会输出软件的需求规格说明书,包括软件原型、详细说明 word 文件,然后交给开发团队进行功能设计、开发和交付。 + +在这种模式下,是否需要专职的软件测试人员,有两种意见。 + +第一种,不需要测试人员,原因是业务逻辑复杂性,找一个普通的外部测试人员进来,还需要花较长的时间去了解需求,学习如何操作一些专业的应用软件,还需业务分析师花费宝贵的时间去做辅导学习。如果不让测试人员学习,就只能测试一些很简单的功能,对把控整个软件交付的质量作用非常小。解决方案就是让业务分析师兼职做测试工作,因为对需求本身非常清楚,学一点测试基础知识,在付出点加班时间,也是能完成功能测试工作的。 + +第二种,需要专职的测试人员,因为上一种方案中,需求分析师大部分做的还是正向测试,即按照自己设计的功能和流程,判断软件交付是否合格。但是对异常场景的测试还是比较少的,会导致软件上线后,在用户实际操作过程和设想的不同的时候,往往会出现一些功能异常,给用户的直接感受就是软件不稳定。所以说希望通过专业的测试人员,多采用一些探索式的测试方法,尽量多地发现软件中存在的缺陷,提升交付质量。 + +哪种方案更加合理一点? + +宝玉: + +我的观点是这种情况下需要专职测试的,业务分析师的重点应该是把需求文档写清楚。 + +业务复杂不能成为一个借口,想想看开发人员是怎么理解需求的,难道也是业务分析师代劳?肯定也是由业务分析师写成需求文档,然后开发人员基于文档开发,当然中间少不了很多确认环节。 + +测试也是类似,应该专业的人来做比较好,可以有更好的测试覆盖。一开始肯定是难一点,但是一段时间业务熟悉后,会极大提升整个团队的测试效率,而你也不需要再为这个问题纠结了。 + +相关阅读:32 | 软件测试:什么样的公司需要专职测试? + +kirogiyi: + +对于 Devops 我只是听说过,并没有具体的去了解过它的使用和应用场景。根据宝玉老师的讲述,Devops 的基础是自动化,那么自动化之外好像更多的是一种概念,可以因环境而产生各种不同的方式和方法,并没有比较明确的定论。感觉就像敏捷开发一样,满足敏捷宣言思想的操作都可以是敏捷开发,最终适合自己或团队的才是最好的。 + +宝玉: + +自动化确实没有明确的定论,重要的是得要有应用自动化的意识,让自动化变成你项目开发流程的一部分,从而提升效率、改进质量。 + +应用自动化本质就是应用工具和基于工具二次开发,常用的自动化工具比如说自动测试框架、持续集成、日志监控报警。这些都是基础工具,还需要针对自己项目的环境,基于工具提供的 API,去定制化的写配置脚本,让它可以适合你的项目。 + +最重要的还是要把这些工具整合到你的开发流程中,比如说: + +当你提交代码的时候,持续集成能帮你自动运行自动化测试脚本,可以直观看到测试结果,根据结果再决定是否合并或者继续修改; + +当你合并代码后,持续集成能帮你自动化部署到测试环境,并且构建生成生产环境的部署包,甚至帮你自动部署生产环境。 + +当你要部署的时候,通过自动化的脚本直接部署到生产环境,而不需要手工去干预太多,避免人为因素的失误。 + +当你部署上线后,通过日志监控报警系统能实时看到部署后的数据变化,及时发现问题。 + +这样的流程其实是比较通用的、大部分项目都是适用的,只是前期需要投入一定的时间精力去研究和搭建,但是搭建好了这样的整套自动化环境和建设好了相应的开发流程,相应的从效率和质量上的回报也是很大的。 + +相关阅读:36 | DevOps 工程师到底要做什么事情? + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/一问一答第5期22个软件开发常见问题解决策略.md b/专栏/软件工程之美/一问一答第5期22个软件开发常见问题解决策略.md new file mode 100644 index 0000000..8ac267f --- /dev/null +++ b/专栏/软件工程之美/一问一答第5期22个软件开发常见问题解决策略.md @@ -0,0 +1,342 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 一问一答第5期 22个软件开发常见问题解决策略 + 你好,我是宝玉。恭喜你完成了经典案例解析篇的学习,这也意味着你坚持到最后,完成了我们专栏所有内容的学习。 + +学习软件工程的知识,最终还是为了要能去应用学到的知识。案例解析就是帮助你结合日常生活中一些常见的现象,去站在软件工程的角度思考和分析。 + +也许你所在的是一个小团队,日常并没有注意对软件工程的应用,但团队小不是拒绝应用软件工程的借口,小团队一样要做好团队的建设,基于软件工程做好流程建设。 + +也许你团队的软件工程已经很好了,但你做个人业余项目却总是失败告终,不妨用学到的软件工程知识去分析一下,看问题在什么地方,又该如何去改进,才能做出成功的项目。 + +也许你也经常在网上关注一些软件开发的信息,当你再看到一些失败的软件项目案例时,不仅仅是当作一个有趣的故事,不妨去站在软件工程的角度,看看它失败的原因是什么,从别人的失败案例中吸取经验教训。 + +也许你日常也关注开源项目,但只是关注技术层面,以后不妨也站在软件工程的角度去看看这些开源项目是怎么运作的?有哪些可以学习借鉴的地方? + +也许你身在大厂或者关注大厂的开发实践,那么不妨多学习和观察大厂的软件工程实践。因为大厂之所以成为大厂,对软件工程的应用自然是有其独到之处,学习大厂能帮助你拓宽视野,提升软件工程知识水平。 + +也许你为微服务、云计算、人工智能这些新技术兴奋或者焦虑,跳出技术角度之外,站在软件工程的角度去看新兴技术,你能有不一样的收获。 + +我对这些案例的分析,都只是为你提供一种不同的视角,帮助你从软件工程的角度看工作中遇到的问题。相信你在学习之后,也可以利用学到的知识,自己去观察软件工程实践,去应用软件工程知识,发现软件工程之美。 + +今天加餐,继续分享我们专栏的精彩问答和留言,这些问答和同学们的分享都是对专栏内容的最好补充,希望可以帮助你更好地学习和理解软件工程知识。 + +一问一答 + +No.1 + +hua168:小团队比较乱的话,最好是规范哪些关键流程?比如我们小团队开发,首先看这个功能有没有开发过,如果是开发过,就直接基于以前开发过的代码改。这就导致运维有问题,有些路径没有替换完,手工输入命令可以运行,用 shell 脚本监控发现程序异常,就重启,结果就报错了,用脚本死活启动不起来。然后发现没有路径及文件,叫开发改,要一拖再拖,都不愿意改。 + +宝玉:流程规范的建立是一个逐步的过程,发现单个的问题,首先解决问题,解决完后就需要思考一下:是不是可以通过流程规范规避类似问题。 + +就拿你这个例子来说,可以先把 CI 持续集成环境搭起来,然后在发现这个问题后,就针对这个路径的问题,提一个 Ticket,要求补上这部分的自动化测试代码。这样以后每次提交代码,CI 都会自动运行这个测试,出问题了就能及时发现,不至于到了生产环境再发现。 + +开发人员任务多可以理解,但是你需要把这些任务通过任务跟踪系统统一管理起来,写一个 Ticket 给他,排上优先级。等其他任务忙完,就该把这个任务给做了。 + +所以小团队乱,任务跟踪管理、开发规范,这都是需要优先建立的流程规范。 + +No.2 + +Joey:研发过程文档,是否有必要进行统一模版,比如方案设计文档、功能测试报告等。如果不设置模版,大家写的五花八门,别人不好检查;如果设置模版,研发人员又说限制他们的想象力。 + +宝玉:我倒是觉得有模板的文档好写一点,填空就好了。对于文档模板,我没有什么建议,毕竟每个公司情况不一样。我经历过的公司没有强制规定要模板的,但会提供两种模板,一种是风格样式的,字体颜色等都采用公司品牌的风格;一种是基于内容的模板,把大标题小标题都列出来,写的时候填内容就好了。文档审查重点是检查内容,而不是格式。 + +No.3 + +Charles:小团队可能就 10 来个人,每个岗位可能就 1~2 个人,这种情况下做内部分享,希望大家都来参与,那么分享内容不好把控;如果太局限于本岗位知识,其他岗位人员参与度不高,效果也不明显。如果只是本岗位的知识分享,那么就 2、3 个人讨论下就行了,有什么好办法解决这个问题? + +宝玉:可以设定一些学习的课题分享,比如说最近有什么新技术很火,但是大家都不知道具体是什么,也很想了解,可以让一个人去学习研究,然后跟大家一起分享。分享的过程其实以讨论为主,分享的人也不需要太多压力,自己也能学到东西,其他听的人在讨论的过程中也能学到东西,共同学习提高。你可以从中做好主持的作用,最好提前也学习准备一些。 + +No.4 + +yellowcloud:我们目前项目使用的管理工具是 TFS,它好像也自带 CI 和 CD 功能,我想请问一下,它和文中介绍的 Azure DevOps,哪个好用呢? + +宝玉:Azure DevOps 应该是 TFS 的升级版,如果在线托管的话,你应该考虑用 Azure DevOps。 + +No.5 + +乐爽:详细的需求分析是放在迭代内进行的,但此时的需求是一个很小的点,所以不会占据整个迭代太多的时间,是吗?如果在迭代内发现需求方案不合理,放入到下一个迭代,这是否合理呢? + +宝玉:是的,因为一个迭代内的需求不多,所以需求分析相对时间较短。如果一个需求在一个迭代内做不完,可以延到下一个迭代。如果一个需求不合理,那么需要重新讨论,讨论清楚了再决定是放当前迭代还是后续迭代。 + +No.6 + +hua168:小公司复盘,是这个弄好了,那个又变差了,也不想着怎么改进,强制执行大家都很抵触,怎么办? + +宝玉:如果是解决一个问题又导致了新的问题,按下葫芦起了瓢这种情况,需要多在整体思考一下原因,尤其是项目的整体流程和开发计划方面。推广开发流程导致大家反感,觉得时间紧还搞其他事情,解决这个问题,需要两方面入手: + + +首先要反省项目计划,如果只是加要求而不给相应时间计划,比如说要求写自动化测试,而不留出写自动化测试时间,那当然会抵触。所以相应要制定出更好的项目计划,避免为了砍时间而砍时间,给开发留出时间去设计、去写测试代码,不然就算你制定一个很紧的计划,还是要花很多时间修 Bug,最终花的其实时间差不多。 +提升大家的认知,不仅是团队内部,还包括团队外部,你的老板和业务部门,获得他们的支持。让大家知道磨刀不误砍柴工:前期投入时间在开发质量上面,后期会节约大量修改 Bug 的时间。 + + +No.7 + +浮生:目前执行过程中发现,如果不是自己负责的功能,团队成员在审查其他人代码的积极性并不高,再加上各自任务都很紧,即使审查也是匆匆过去,有时并未起到应有的效果,请问在流程机制中有方法可以提高审查的效果吗? + +宝玉:很抱歉我暂时没有好的建议。可以尝试的是: + + +首先强制 Review 才能合并是必须的; +让 PR 小一点,减少 Review 的难度; +时间进度上,考虑上代码审查的时间,毕竟代码审查是磨刀不误砍柴工; +鼓励资深的程序员做好带头作用,可以把 Review 代码参与度和 Review 代码质量作为绩效的一部分; +你可以每天检查一遍审查通过的代码,对于明显有问题的,私下找可以找相关人谈一谈。 + + +No.8 + +胡鹏:我现在遇到一些情况,需求出来了,估时的时候,通常有两种心理。第一种, 尽量压缩自己的时间,当然领导也会压缩时间, 这时心里想的是要好好表现,把时间压短一点;第二种,尽量多一点充裕时间,当出现问题能有足够的时间来解决,不至于延期。对于估时,取一还是取二还是在一和二之间平衡? + +宝玉:太紧和太松的时间估算都不可取,应该是尽可能准确地选择接近实际情况的时间,并且留有一点富裕应对意外情况。时间太紧了要加班加点还要被质疑能力;时间太松了会影响以后估算时间的真实性。 + +准确地估算时间是程序员能力的一种,做好不容易,一些建议供参考: + + +充分理解清楚需求,知道要做什么,这是基本前提,不然做着做着发现需求没搞清楚,那一定是要多出很多额外时间。 +非功能性的需求,比如说写自动化测试、搭环境、重构代码这些任务也应该作为计划的一部分,要把时间算进去。 +拿到任务后,将任务要分解到尽可能细,越小的任务力度估算越准确,而且在跟领导说时间进度的时候也有理有据,底气足扛得住。 +综合考虑任务并行的情况,给线上版本修 Bug、开会这些时间也要算进去,想想每天真正有效的工作时间是多少。 +计划保持及时更新,当出现延迟或者有延迟风险的时候,或者进度提前,需要及时和项目负责人沟通,作出调整,避免影响整体项目进度。 +留一点余量,应对突发情况。 + + +反过来,如果你是领导,在下属估算时间的时候,也要参考上面的一些建议,让计划尽可能地接近真实情况,而不是下属给一个很紧的时间就按照这个时间执行,最后得加班加点,加班是为了应对突发情况的,而不是正常情况。 + +No.9 + +Joey:1. 如何更好地推广 SonarLint 白盒扫描工具?2. 如何要求各开发团队更好地,有效地做代码走查,而不流于形式?(我们现在使用 Gerrit)3. 如何要求开发人员有效实施单元测试? + +宝玉:这种开发流程问题肯定还是要自上而下推才能推得动。我觉得首先应该先找一两个小项目组试点,摸索出一套适合你们的最佳实践,形成流程规范,比如说基于 Github Flow,把 CI(持续集成)环境搭建起来(如果没有的话),把你说的 SonarLint、自动化测试加入到 CI 流程中。再就是逐步扩大范围,在更多项目组推行最佳实践和流程规范,并且改进流程规范。最后就必须要借助行政手段强制推行了。 + +No.10 + +Liber:我们专栏之前的文章中,以本文注册用户为例,分别写了小、中、大型测试用例,但实际开发过程中,如何权衡对一个场景,是该小、中、大测试都写,还是只写部分? + +宝玉:实际开发中,理论上来说,是一个场景大中小测试都要写的。通常情况,开发写小型测试和中型测试,测试写大型测试,或者开发帮助写大型测试。小型测试:中型测试:大型测试比例大约为 7:2:1。小型测试尽可能多覆盖,不要求 100%,谷歌是 85%。中型测试覆盖大部分用户使用场景,小型测试覆盖主要用户场景。 + +No.11 + +OnRoad:客户需求频繁变更,大领导迫于客户压力全盘答应,导致开发节奏被打乱,除了量化风险上报之外,还有什么好办法? + +宝玉:需要和你的领导私下协商,需要在他的帮助下一起作出一些调整: + + +要设立流程提高客户变更需求的成本,可以需求变更,但不能太过于频繁随意; +缩短开发周期,采用迭代模型或者敏捷开发,2~4 周发布一个版本,每个版本实现当前已经确定的最重要的需求,在一个版本内不接受需求变化,变化的需求放在下一个迭代中实现。 + + +No.12 + +探索无止境:对于专栏中提到的“测试验收通过后,预部署分支的代码会部署到生产环境。”我的理解是,部署的分支的代码,上线测试没问题之后,再把这个代码合并回主分支,这样理解对不对? + +宝玉:这里有两种策略: + + +每次线上 Bug,修复后只合并到预部署分支,最后统一把预部署分支合并回主分支。优点是简单,缺点是合并时可能会有很多冲突; +每次线上 Bug,修复后同时合并预部署分支和主分支。优点是以后就不用再合并回去,还有可以及时同步 Bug 修复,缺点是麻烦,每次要 cherry pick。 + + +我们项目中选的是后一种策略,因为能及时同步 Bug 修复到主干,这一点对我们很重要。 + +No.13 + +maomaostyle:在敏捷开发中,如何结合标准的项目管理方法呢?比如 wbs 任务拆解,风险识别,因为这两点相对于项目的整体情况已经应该拿到了足够多的输入,但是在敏捷的背景下需求等细节都是不清晰的。另外比如最小化原型产品更难以结合大而全的项目管理方法了吧? + +宝玉:敏捷开发中,wbs 一样可以帮助分解任务,然后把任务拆分到 Sprint,还可以设置里程碑。风险识别应该和用什么开发模型没太大关系,关键还是识别和确定应对策略。最小化原型法可以是小瀑布开发模型也可以是敏捷开发,关键在于需求要定义清楚,要小。 + +No.14 + +宝宝太喜欢极客时间了:方法论、方法、模型这些名词具体怎么理解?敏捷开发属于哪一种?实施敏捷软件架构设计等文档都可以省略吗?如果文档都省略了,那开发人员离职后新接手人员怎么快速熟悉项目呢?公司的知识积累怎么体现? + +宝玉:敏捷宣言说的:“工作的软件 高于 详尽的文档。尽管右项有其价值,我们更重视左项的价值。”没有否认文档的价值,也不代表实施敏捷软件架构设计可以省略文档,只是没有必要写过多繁重的、没有价值的文档。 + +另一个角度来说,也不要过分夸大文档的作用,离职交接,光文档还不够,还离不开人和人之间的互动,交流;公司的知识积累更多靠的是人、代码、文档、流程规范、文化等多方面因素综合的结果,而不光是文档。 + +No.15 + +Tiger:我们做的项目外包,项目组的人数是固定的,每次都是项目组要离职一个才会再招一个人进来补充,这种情况无法培养技术后备,人员风险怎么把控? + +宝玉:这种确实有点困难,有两种策略你可以考虑: + + +减少对人的依赖,让人来了跟流水线工人一样可以马上上手。如果你的项目类型比较类似,其实可以考虑将相同部分通过架构简化,通过配置或者定制化适用于不同项目。 +培养现有的人,提升现有人的能力,提升归属感,都不容易做到,但都可以试试,或者你也可以想到更好的办法。 + + +No.16 + +ailei:除了《人月神话》《人件》,还有哪些偏管理的软件工程的书? + +宝玉:有几本项目管理的书可以看看: + +《项目管理修炼之道》 + +《项目管理 - 计划、进度和控制的系统方法》 + +《软件项目成功之道》 + +《做项目,就得这么干!》 + +No.17 + +成:如果一周开发,一周测试,测试的时候,开发人员开始下个迭代,那 Bug 啥时候修改呢?如果下一个迭代期间也要修改 Bug,那本次迭代工作也进度也难以保证一样,不是很理解如何操作? + +宝玉:是这样的,开发当前 Sprint 新功能的时候,同时要修改上个 Sprint 的 Bug。比如说这周是 Sprint 1.2,那么同时要修改 Sprint1.1 的 Bug。而且 Sprint 1.1 的 Bug 的优先级要高于 Sprint 1.2 新功能的开发。 + +其实改 Bug 通常不需要花太多时间,所以一般影响不大。如果偶尔 Bug 修改时间过长,不能如期完成的,需要推迟上线。如果团队不适应这种节奏,那么应该延长 Sprint 周期,例如两周一个 Sprint。 + +文章的例子只是一个参考,并不是说一定要这样做。 + +No.18 + +E:软件开发的过程和方法之间的关系是什么? + +宝玉:软件开发过程就是指开发软件时整个过程的开发模式,比如说瀑布模型还是敏捷开发。选择了开发过程,你就需要有具体方法来执行。 + +比如你选择了瀑布模型,整个软件开发过程就是按照瀑布模型的分阶段来进行,对应的方法就是瀑布模型中的方法,例如需求分析、架构设计;如果你选择了敏捷开发,则整个开发过程就是一种敏捷迭代方式,后面的方法对应的就是敏捷开发的一套方法体系,例如 Scrum、用户故事、持续集成等。 + +No.19 + +刘晓林:关于 Ticket 工期估算我有个疑问。团队中一般都是一两个人负责一个小模块,之所以这样做是为了提高工作效率,避免同一段代码每次迭代都由不同的人去修改,因为大家对自己的小模块很熟悉,所以工作效率很高。但这样带来的问题是,团队成员对其他人负责的模块不熟,所以工期估算只能由模块负责人自己完成,别人很难帮上忙。这种情况怎么解决? + +宝玉:这是个好问题。我的建议是模块要换着做,宁可慢一点,不然的话,不仅仅是其他人不能帮忙不能估算,万一有人离开团队了,会更麻烦的。如果团队不大,做的时候分工都不要太细,都不要太局限前端后端,这样其实对整个团队来讲是最好的,互相能替换。当然,也不要着急,慢慢来,不要一下子改变很大。 + +No.20 + +谢禾急文:我想到一个想法,就是通过用一个工具记录我自己开发过程中遇到的所有 Bug,通过记录、分析、反思这些 Bug,能够有助于提升我的编程能力,有助于避免犯同样的错误。我觉得你上面说的那些工具,能够满足我的需求。如果有一个网站,能够提供 Bug 记录、分享、解答的功能,是不是能够满足某些用户的需求?(好像 stackoverflow 就是这样的工具) + +宝玉:我觉得是有帮助,但这个问题的关键在于分析反思 Bug。自己对自己 Bug 的反思才是价值最大的,其他人看过之后不一定能有那么大的共鸣,因为一个 Bug 都有复杂的业务背景,是很难被记录,缺少上下文也很难理解。StackOverflow 是很有价值的,因为它是从问题切入,而问题是有很多共性的,很容易引起共鸣。 + +No.21 + +纯洁的憎恶:我很早就知道知识体系的重要性,我也比较重视构建知识体系,但并没有什么亲测有效的方法,且对知识体系是个什么样的存在缺乏体感认识。可能还是学得太浅,用的太少? + +宝玉:方法不是最主要的,最多让你学习提升一点速度。关键还是坚持,多练习多实践。 + +从知识转变成技能,一定需要通过反复的刻意的练习,才能形成条件反射,最终掌握。没有任何学习方法能替代练习,最多有催化剂,可以加速练习效果的学习方法。 + +还有就是对技术的学习,不能太依赖于工作上的输入,工作上如果项目好用户多,那还是很有挑战的,但大多数时候没有那么多挑战,可能就是个增删改查,那么几年的工作经验可能只是简单的重复,不能达到刻意练习的效果。那还是要在工作之外寻找一些练习的途径,比如上次我建议的:自己做一点项目、参与一些开源项目。 + +要想对知识体系有体感认识,还是建议先在一个领域有深度,有一棵树了才能想像出来森林是什么样子的,不然只能看到一片灌木丛。这过程难免要踩很多的坑,经历很多次的失败和挫折,反复的思考、总结和重试。 + +No.22 + +titan:敏捷开发在一些小公司落地是比较难的,原因我认为主要是人的综合素质达不到,敏捷的一些思想和原则不能落地,比如团队成员人人平等的价值观,在小公司,牛人比较少,大部分都是比较弱的人,你让牛人跟他们强调平等,似乎是不太可能的事情。 + +宝玉:平等和牛人,这其实不矛盾的。就像蜘蛛侠的叔叔说的:能力越大责任越大。牛人担负的责任会更大,贡献多,收入也多。 + +一个健康的开发团队,无论大小,都应该是有梯队的,有资深的,有新人,资深的(牛人)负责架构、模块划分、实现核心模块,新手则基于架构实现具体模块。不然单靠个别牛人完成功能也是不现实的。敏捷开发在小公司落地,最根本还是真的懂敏捷,能应用好敏捷的原则和实践,不要追求形式化,不要走捷径。 + +精选留言 + +alva_xu : + +就“选择适合你的软件开发模型”,这一点,我谈谈想法。软件开发模型是瀑布还是敏捷对于软件开发管理来说有很大的不同;但即使采用瀑布模型,对于团队管理来说,我们是可以借鉴敏捷模型的。 + +比如,我们可以采用看板管理来提高任务管理的效率和透明度,可以通过站会来加快问题的沟通,对于“建立外部提交需求和任务的流程”,我们也可以借助敏捷管理的思路,通过每天站会或者周例会的时候,一起做个新需求评估。对于技术交流,也可以像敏捷团队里说的培养 T 型技能的人员为目的来开展。 + +而且,先通过团队管理方式的转变,培养大家的敏捷文化,然后再切到敏捷开发模式,就会更加顺畅。我觉得,小团队管理,一定要培养自主自治合作分享的文化和能力,通过用轮值 Scrum master 的办法,一点点提高这方面的文化和能力。 + +相关阅读:40 | 最佳实践:小团队如何应用软件工程? + +纯洁的憎恶: + +抛弃妄念,脚踏实地。切忌追求过于宏大的目标、过于新奇的技术,而最终难以落地。做事要有边界和约束,向死而生才有效率。专业短板可以尝试自行补齐,也可以求助他人取长补短。 + +相关阅读:41 | 为什么程序员的业余项目大多都死了? + +思辨时刻 + +幻想: + +我觉得两周为一个发布周期,很有可能导致代码质量低下。例如:两周一迭代里,我们可能没有时间在上个迭代里就做好下个迭代需求的分析,只能遗留到当前迭代,这个时候,需求分析、代码设计、接口设计就要花好几天。好了,由于限制死了两周要发布一次,导致测试人员死盯着发布日期,进行倒推,让开发人员尽量在某个时间点提测,不然迭代上线就风险很大,这样就导致了开发这边压力很大很大,开发时间短,代码质量低,提测后,又有各种 Bug,也进而阻碍测试进度,整条线都非常疲惫紧张。 + +最惨的是,由于测试人员急着测试,也未能做到详细测试,就上线了。又是各种线上 Bug。因此这种两周一上线,会容易让人死盯着上线日期,给全部人员带来很大的压力,相当于是给自己挖坑和约束了,很不应该的。 + +我觉得软件工程里,开发阶段是最关键的阶段,得给到合理的时间,不然这个阶段被动了,乱了之后,就会产生一系列的不好级联反应。因此,我觉得应该有开发人员来把控节奏,给出工作量,给出哪些可以优先测试。 + +宝玉: + +好问题!你说的担忧完全合理,也确实可能会出现这样的情况。 + +我来解释一下为什么 2~4 周是可行的。我们假设你现在的项目是三个月周期,一共是 12 周,然后你大约 2~3 周在需求,2~3 周架构设计,4 周左右在编码,2~3 周测试。 + +那也就是说需求分析期间,其实开发、测试做不了啥事,架构设计的时候,主要是架构师在忙,编码的时候,主要是程序员在忙,测试的时候,开发和测试在忙。 + +再假设你大概要完成 10 个功能,也就是这 10 个功能从设计到开发预计花了 10 周时间,平均每周一个功能。 + +如果换成 2 周一个迭代,那么我们可以考虑每个迭代只选取 2 个功能,但是在这 2 周,整个团队的运作可能是这样的: + +迭代 v1.1(2 周) + +产品设计,准备下一个迭代 v1.2 的产品设计; + +开发,设计和开发这个迭代 v1.1 的功能,同步修复发现的 v1.0 的 Bug; + +测试,测试上一个迭代 v1.0 开发好的功能; + +开发完成后,部署开发完成的 v1.1 到测试环境; + +发布测试验收完的迭代 v1.0。 + +迭代 v1.2(2 周) + +产品设计,准备下一个迭代 v1.3 的产品设计; + +开发,设计和开发这个迭代 v1.2 的功能,同步修复发现的 v1.1 的 Bug; + +测试,测试上一个迭代 v1.0 开发好的功能; + +开发完成后,部署开发完成的 v1.2 到测试环境; + +发布测试验收完的迭代 v1.1。 + +也就是你差不多还是有两周时间开发新功能,两周时间测试,但是每两周可以发布一个小版本,而且整体节奏比较平缓。如果到时间内完不成所有功能,那么就发布完成的,没完成的放到下一个迭代,这样可以保证每周都可以发布。配合代码审查和自动化测试以及基于分支开发的流程,可以保证合并后代码质量相对是可靠的。 + +如果这样操作有难度的,那么采用 4 周一个迭代,但是每个迭代功能减少,还是一样可行的。还有每个迭代结束后的上线发布,可以有两种类型,小迭代可以不发布生产环境,只是测试环境,几个小迭代后再发布生产环境。也就是说,方法其实是有的,观念上可以先调整,因为这样的迭代周期肯定是可行的。 + +相关阅读:40 | 最佳实践:小团队如何应用软件工程? + +纯洁的憎恶 : + +深深地感受到,软件工程不是为了创造最伟大的软件项目而存在,却是为了保障每一个项目的成本、质量、工期、目标等等可控而存在的。 + +果然如此: + +软件工程是过程控制的方法论,而产品设计才是保证伟大的产品,两者应该结合。 + +相关阅读: 42 | 反面案例:盘点那些失败的软件项目 + +kirogiyi : + +软件工程方式的使用,或多或少会受到最高领导层管理理念的影响,这从各大公司的组织架构图可以看出一些端倪,比如:Amzon 的组织架构图,领导力准则得以全面体现,精确而清晰;Facebook 的组织架构图,更利于信息的快速传递和响应,管理方式相对其他公司更加扁平;Google 的组织架构图,上层倾向于层级管理,下层倾向于扁平管理,适合于公司指令的上传下达,也适合于不同层级之间的工程师进行沟通交流进步成长。 + +如果领导层倾向于规范化流程化,那么采用 Amazon 的开发方式,明确的分工,明确的目标,这使得贝佐斯的领导力、执行力、远见力得以全面实施。 + +如果领导层倾向于激进和冒险,那么采用 Facebook 的开发方式,只要你够积极,不断创新,即使犯错也是一种进步,不得不说这种方式在小公司开发团队中实施起来更可行,毕竟小公司需要快速响应,快速迭代,快速决策,不可预料的事情比较多。 + +如果领导层倾向于人性的发挥,那么采用 Google 的开发方式(个人认为适合资金比较雄厚的公司),它能让工程师在舒适的环境中充分发挥所长,并去尝试开拓自己感兴趣的新的技术领域,各自都对自己的领域精雕细琢,质量无形中就得到了一定程度上的保证。 + +从上面来看,我算是一个激进和冒险的人,更喜欢 Facebook 的开发方式,使我能够在不断的创新和错误中成长。 + +宝玉:你这个角度也很新颖!一个公司的文化和创始人的性格是有很大关系的,这些文化都没有绝对的好坏,都成就了伟大的公司,合适的就是最好的 + +相关阅读: 44 | 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的? + +传说中的胖子: + +我以前学习技术,就是看怎么实现,或者说是怎么用;现在学习技术,是学习技术在什么情况下产生的,适合解决什么场景下的问题,需要的资源是什么。多学习一些技术以及使用场景、然后在出现问题的时候可以结合实际情况做多种选择,根据其他因素选择一个比较合适的方案,方案确定了,技术实现就会方便很多。因为在 IT 行业边缘化的三线城市,也不知道这种想法有没有什么遗漏,希望老师帮着补充。 + +宝玉: + +我觉得从思路上是没问题的,我从实践的角度提一点建议:技术只有通过实践才能真正清楚其优缺点和使用场景。建议有些新的流行的技术,哪怕项目中不使用,业余时间也可以自己去试试,这样能给你未来的项目实践有更好的指导。当然也不要走偏,学了一个新技术就要应用到实际项目中,如你所说:学技术的目的是为了帮助你更好的选择,选择了合适的之后才是应用。 + +相关阅读: 45 | 从软件工程的角度看微服务、云计算、人工智能这些新技术 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/学习攻略怎样学好软件工程?.md b/专栏/软件工程之美/学习攻略怎样学好软件工程?.md new file mode 100644 index 0000000..e2da86b --- /dev/null +++ b/专栏/软件工程之美/学习攻略怎样学好软件工程?.md @@ -0,0 +1,147 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 学习攻略 怎样学好软件工程? + 你好,我是宝玉。 + +关于“什么是软件工程”和“为什么要学软件工程”,我在开篇词中已经简单介绍过了。总结来说:软件工程是软件行业知识体系的内核。无论你想走技术路线,还是转向做管理,想要走的更快更稳,那就绕不开软件工程。 + +在正式开始学习前,我们先来聊聊应该如何学习软件工程。你要先知道,软件工程学科的“知识树”结构是什么样的,才能更好地理解每个知识点和彼此间的联系。 + +软件工程知识架构全景图 + +首先你要明确,当我们谈软件工程学时,究竟在讲些什么呢? + +在《软件工程——实践者的研究方法》这本经典软件工程教材中,作者 Roger S.Pressman 画了一张图,高度概括了整个软件工程的核心知识。 + + + +由图可见,“质量焦点”在最底层,这不难理解软件工程是为了应对软件危机诞生的学科,其目标就是为了要聚焦于质量,构建和维护高质量的软件。可以说,聚焦于质量就是软件工程的基石。 + +那“过程”指的是什么呢? + +要构建高质量软件,则要解决软件过程中的混乱,将软件开发过程中的沟通、计划、建模、构建和部署等活动有效地组织起来。而软件过程,就是在软件项目的生命周期内,也就是软件从诞生到结束这期间,在开发与构建系统时要遵循的步骤。 + +有两种过程框架你一定经常听到,那就是瀑布模型和敏捷开发。这是在软件工程多年的发展中,逐步形成的两种主流的软件过程指导框架。 + +那么,何为“方法”? + +方法是指在整个过程中,如何构建系统的方法学。比如说,如何分析用户需求;如何对产品进行测试验收;如何进行系统架构设计等。 + +知道了过程,掌握了方法,那么具体落到操作层面,就会涉及到工具的使用。 + +我们需要工具来辅助方法的执行,提高效率。通过工具,可以把一些手动的工作自动化,比如自动化测试工具,自动构建部署工具;通过工具,可以帮助把一些流程规范起来,比如 Bug 跟踪、源代码管理;还可以通过工具,帮助提高编码效率,比如各种编辑器 IDE、各种高级语言。 + +如果现在再回头总结一下,软件工程的核心知识点,就是围绕软件开发过程,产生的方法学和工具。 + +你可以用一个简单的公式来理解软件工程,那就是:软件工程 = 工具 + 方法 + 过程。 + +根据这个公式,我将软件工程的知识结构做成了思维导图,方便你对知识点有更好地理解,高效学习。 + + + +如何学习软件工程? + +我给了你软件工程学的公式,也对软件工程有了更为全面的了解,看起来软件工程学很简单,但这些内容一下子要吃透也不容易。在开篇词中,我介绍了会从“道、术和器”三个维度去讲这个专栏,这其实对应了学习软件工程的四重境界。 + +学习软件工程的四重境界 + +第一重:用器 + +“器”就是工具,工具规则简单,上手就可以用,也很快就能看到效果。比如,原型设计工具可以帮助你确定需求,持续集成工具可以帮助你简化测试和部署的流程。对工具的学习是最为简单的,也是最基础的。 + +第二重:学术 + +“术”就是方法,学会方法,你就能应用方法去完成一个任务,例如用需求分析的方法,你去搞清楚用户想要什么,用 Scrum 去组织项目开发过程。 + +掌握了术,甚至是可以脱离器的,例如你没用原型设计工具,你用纸和笔,用白板,一样可以去沟通确认需求。 + +第三重:悟道 + +“道”就是本源,软件工程知识的核心思想和本质规律。就像敏捷开发,本身并不是一种方法,而是一套价值观和原则,领悟了这个道,就可以成为你在处理项目过程中各种问题决策的依据。道是可以产生术的,你掌握了敏捷开发的道,你就可以领悟出 Scrum、极限编程这样的术。 + +第四重: 传道 + +当你能把复杂的知识通过浅显易懂的方式传授给别人,那就说明你对知识的领悟已经到了更高的境界。同时,教学也是最好的学习方式,通过传授别人知识,可以让你对知识本身有更深入的理解。 + +做中学和教中学 + +你可能会问,怎样学,才能到达以上这四重境界?我在做技术管理的工作中,经常要做一些培训的工作,在这过程中我总结了两套行之有效的方法:“做中学”和“教中学”。 + + + +“做中学”,是一种自下而上的学习方法,通过实践,从使用工具到学习方法,再从方法中提炼出道。 + +在学习本专栏的时候,你可以采用“做中学”的方式,把专栏中的知识应用起来,在实践的过程中去巩固你学到的知识,去思考背后的道。把已经积累的项目经验和软件工程的知识点关联起来,这样才能加深你的理解,学以致用,把经验和知识转化为能力。 + +“教中学”,是一种自上而下的学习方法,通过教学,去进一步深入领会别人总结出来的道,去模仿推导方法,去学习如何使用工具。 + +比如,你学习完一篇专栏文章后,把学到的知识进行输出,写成微博或博客分享出去;在公司内部讲给你的同事们听等。在教学分享的过程中,去进一步深化吸收知识内容,构建你的知识体系。 + +“做中学”和“教中学”,这两种方法你可以配合起来使用。 + +参考书目 + +另外,在学习软件工程的过程中,我看过一些不错的相关书籍,在这里列个书单,供大家参考。 + + +《构建之法》 + + +作者邹欣是微软的研发总监,同时在多所高校进行了软件工程的教学实践,在此基础上对软件工程的各个知识点和技能要求进行了系统性整理,形成教材。也是对本专栏知识很好的补充。 + + +《人月神话》 + + +这是软件工程历史上的经典著作,内容发人深省,40 年来一直畅销不衰,里面的观点即使到现在也不过时。这本书即使你以前看过,隔一段时间再翻看一遍,可能都会有新的感悟。 + + +《人件》 + + +如果说《人月神话》关注“软件开发”本身,《人件》则关注软件开发中的“人”。作者指出知识型企业的核心是人,而不是技术。 + + +《知行合一: 实现价值驱动的敏捷和精益开发》 + + +作者丛斌有二十多年从事软件工程教学、咨询和研究的经验,所以书写的特别接地气,文章有很多真实案例,对敏捷开发和 CMMI 都有很深入描述。 + + +《软件工程——实践者的研究方法》 + + +这是大部分高校采用的软件工程标准教材,可以作为一个参考。 + + +《持续交付》 + + +讲述如何实现更快、更可靠、低成本的自动化软件交付,描述了如何通过增加反馈,并改进开发人员、测试人员、运维人员和项目经理之间的协作来达到这个目标。 + + +《走出软件作坊》 + + +这本书生动的描述了国内小型 IT 企业在发展过程中遇到的一系列项目管理问题,以及作者是如何去解决这些问题的。 + +总结 + +今天,我带你浏览了软件工程的全景图,也为你讲解了学习软件工程的四重境界。同时,我也介绍了“做中学”和“教中学”这两套行之有效,并且特别适合软件工程学科的学习方法,所以希望你在后面的学习中,可以付诸行动。 + + +分享你学到的知识。将你从专栏学习到的知识写成微博或博客等,分享给大家。写作是一种特别好的总结和学习方式,在你写的过程中,很多不清楚的问题就想明白了。 + +做几次内部分享或培训。如果你从来没做过公司内部的分享或培训,不妨迈出第一步,把你学到的知识,写成 PPT,小范围地讲给你的同事或朋友。如果你已经做过类似的分享,那么就再做几次软件工程相关的。准备 PPT 的过程,就是你最好的学习过程。 + +把你学习的知识应用起来。学到的知识只有用起来才能变成你自己的经验,尝试着把在专栏中学到的知识应用到你的项目中去。多问多思考。有疑问就提出来;看到其他人问的问题,也可以去思考为什么,一起探讨问题的答案。 + + +感谢阅读,如果你觉得这篇文章对你有一些启发,也欢迎把它分享给你的朋友。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/特别放送从软件工程的角度解读任正非的新年公开信.md b/专栏/软件工程之美/特别放送从软件工程的角度解读任正非的新年公开信.md new file mode 100644 index 0000000..9e0a95b --- /dev/null +++ b/专栏/软件工程之美/特别放送从软件工程的角度解读任正非的新年公开信.md @@ -0,0 +1,253 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 特别放送 从软件工程的角度解读任正非的新年公开信 + 你好,我是宝玉。 + +2019 年 1 月,任正非的那封《全面提升软件工程能力与实践,打造可信的高质量产品》公开信在朋友圈刷屏了。作为软件工程专业出身的程序员,这封公开信自然是引起了我的好奇,仔细阅读之下,确实让我大吃一惊。 + +于是,我从软件工程的角度对这封公开信进行了解读。在我们专栏内容正式更新前,我将它作为特别放送分享给你,希望可以帮助你更好地理解软件工程。 + +这封信看似像八股文一般,但细看之下,可以发现作者对于软件工程的理解确实非常深刻,各种专业术语信手拈来,比喻恰到好处。 + +我对华为的研发其实一直挺好奇的,从传统的硬件公司,到现在软硬件齐头并进,华为手机销量都已经超过了苹果,可见华为的软硬件研发实力早已处于全球领先水平。信中提到: + + +二十年前的 IPD 变革,重构了我们的研发模式,实现了从依赖个人、偶然性推出成功产品,到制度化、持续地推出高质量产品的转变。 + + +这一句话,也揭示了华为的软件研发能做到全球领先水平的原因。 + +华为是在 1999 年开始从 IBM 引进 IPD 的,到今年 2019 年正好 20 年,在过去的 20 年里,IPD 帮助华为从游击队变成了正规军,研发队伍从几千人到几万人,软件产品也覆盖到手机操作系统、应用、云服务。 + +我对 IPD 是不甚了解的,只知道 IPD(Integrated Product Development,集成产品开发)是一种产品开发方法,但如果说软件产品的开发方法,我是比较熟悉的,那就是软件工程。 + +任正非发出的这封信的大背景也很特殊,2018 年中美贸易战开始,中兴、华为首当其冲成为美国开刀的对象,跟风站队的澳大利亚、新西兰、英国也跳出来抵制华为,说华为不安全,可能含有间谍软件,窃听国家机密。这帽子一扣是很难扯清的,这就是为什么整封信从标题开始,一共 17 次提到两个关键字:可信。 + +只有让客户觉得华为的产品“可信”,华为才能尽快走出这场危机,那么怎么才能做到可信呢? + +如果你是餐厅老板,有人造谣你的厨房脏乱差,员工上完厕所不洗手,你怎么办?最好的办法自然是用先进的管理流程,并且让整个做菜的过程尽可能公开透明。 + +所以信中有这样一句话: + + +我们要转变观念,追求打造可信的高质量产品,不仅仅是功能、特性的高质量,也包括产品开发到交付过程的高质量。 + + +要转变观念,不再只认结果的质量,还要追求过程质量了!而如何追求过程质量呢?那就是要“全面提升软件工程能力和实践”。 + +如果信到此为止,也就是个普通官方“八股文”。领导们嘛,可不就是喜欢指个大方向,说你们要用软件工程,要实施软件工程,至于怎么用,那是你们的事情,毕竟做领导的哪有几个真的懂软件工程,难得的是这封信居然有很多具体怎么做的细节内容。 + +以下,我带你看看这封信都提到了哪些具体到操作层面的问题。 + +1. 软件项目管理金三角 + +先看这一句: + + +我们各级管理者和全体员工都不得以进度、功能、特性等为理由来降低可信的要求,确保可信的要求在执行过程中不变形。 + + +振聋发聩呀同志们,热泪盈眶呀!生活中有多少次这样的情况发生:三个月的项目,老板说你一个月就要给我做完;做到一半的项目,PM 说这个功能很重要,我们要加上去。最终怎么办?牺牲质量呗!又想要马儿跑得快,又想要马儿不吃草,天底下哪有那么好的事情。 + +软件工程里面早就告诉我们了:时间(多久可以完成)、范围(需要实现多少功能)、成本(花多少钱)这三个要素直接决定了产品的质量(产品的质量,客户的满意度)。 + + + +希望各位老板别光学乔布斯,也学学任正非!想要员工天天加班,还想着少发工资,还要产品质量好,做得快,这不现实呀! + + + +首先得明白一个最浅显的道理:想要“多、快、好、省”都占着,这是不存在的,你只能选两样。想要便宜还想要质量好,那就得等;想要快还想要质量好,那就得多花钱;要想便宜做的又快,那就得接受质量差! + +2. 自我精进 + +2018 年底,程序员被企业裁掉的不少,很多程序员开始担忧起前景来。关于这一点,这封信中也提到了一些指导意见,如果你能达到以下要求,应该是不必担心裁员的。 + + +我们要从最基础的编码质量做起,视高质量代码为尊严和个人声誉。代码就像是高楼大厦的一砖一瓦,没有高质量的代码,可信的产品就是空中楼阁。我们要优化并遵循公司各种编程规范,遵从架构与设计原则,熟练使用各种编程库和 API,编写出简洁、规范、可读性强、健壮安全的代码。 + + +这一段是说给我们程序员看的,这其实也是对程序员的基本要求,大家看看自己,看看身边,真能做到的有多少?像我一样觉得自己还做的不够好的,咱还是努力学习吧,多练练,多用点心肯定更没问题的。 + +3. 关于架构 + +讲完了程序员的自我精进,信中又开始说架构师了: + + +我们要深刻理解架构的核心要素,基于可信导向来进行架构与设计。 + + +看到没有,又提到可信了,架构设计的时候,别再天马行空,啥新酷用啥,啥流行用啥,一定要有“可信导向”,架构设计目标先搞清楚。 + +然后是细节: + + +在确保可信的前提下,要在性能、功能、扩展性等方面做好权衡;慎重地定义我们的模块与接口,真正做到高内聚与低耦合;我们要遵循权限和攻击面最小化等安全设计原则,科学设计模块之间的隔离与接口,提升安全性;低阶架构与设计要遵循高阶的架构与设计原则,在充分理解原有架构与设计的情况下,持续优化;我们要熟悉各种设计模式,重用公共成熟组件和服务,避免重复劳动。 + + + +“高内聚与低耦合”,这是讲架构设计时,让模块之间做到高内聚与低耦合。 + +“权限和攻击面最小化”,这是讲架构设计时,要注意对安全的防范,权限要控制好,暴露出来的入口要小,做好防范。 + +“重用公共成熟组件和服务”,不要浪费时间在造轮子上了,多重用现有的组件和服务,甚至要提取公共的组件和服务。减少重复劳动,提高效率。 + + +你看,多么浅显的道理,一看就明白,但要做到可不容易! + +4. 技术债务 + +华为这些年高速发展,早些年为了追求速度肯定也没少走捷径,这些年下来,也肯定没少欠技术债务,现在也是一个从追求速度到追求质量转型的契机。 + +所以信中说完架构开始讲技术债务了: + + +我们要重构腐化的架构及不符合软件工程规范和质量要求的历史代码。我们知道,再好的架构,其生命力也是有限的。随着时间的推移、环境的变化以及新技术、新功能特性的引入,架构也会腐化。面对腐化了的架构,要毫不犹豫地去重构它。同时主动以可信设计原则为导向,去重构不符合软件工程规范和质量要求的历史代码,提升软件架构的生命力。 + + +我们都知道,没有万能的架构,只有适合当时需求、当时技术条件和人员的架构。时间推移了,很多架构就满足不了要求了,就需要重构。 + +作为 80 后,小时候其实生活挺艰苦的,那时候我们穿衣服都讲究的是:“新三年,旧三年,缝缝补补又三年。”架构也一样嘛,不满足需求,我们先修修补补,真要重构挑战还是不小的,但是不去做的话,它会一直成为发展的障碍。 + +这封信也算是推了一把:“面对腐化了的架构,要毫不犹豫地去重构它。”当然你重构,也不要忘记“可信”这个根本目标,“同时主动以可信设计原则为导向。” + +其实 Google 在这方面已经走在前面了,一直鼓励重写代码,任何软件每隔几年就重写一遍,这样可以优化代码,采用最新技术,去掉一些没有价值的功能,最重要的是让新员工得到锻炼,保持高昂的斗志。不知道关于这点,华为是不是在向 Google 学习。 + +5. 安全性 + +这些年,互联网发展很快,但是安全事故却层出不穷:开房记录被泄露、密码被泄露、比特币被盗……这暴露出业界的普遍问题,对安全的重视度不够。 + +所以信中也不止一次提到安全问题: + + +公司已经明确,把网络安全和隐私保护作为公司的最高纲领。 + +我们要深入钻研软件技术,尤其是安全技术。 + +我们要遵循权限和攻击面最小化等安全设计原则,科学设计模块之间的隔离与接口,提升安全性 + +“编写出简洁、规范、可读性强、健壮安全的代码。 + + +要打造一个安全的软件,就是首先要有安全意识,然后要懂安全技术,在整个开发过程中要从架构设计、代码等方方面面去注意。 + +6. 技术是工具 + +这些年,开发界一直有些不好的风气,就是都认为自己的技术是最牛的,写后端的看不上前端的,用 Angular 的看不上 Vue,写 PHP 的认为自己的语言是全世界最好的,做开发的还看不上做测试的。 + +但是这封信中有一句话,大家不要忽视,“软件技术是我们打造产品的基本工具”,技术只是工具,只是我们用来打造产品的工具! + + +技术是否先进,技术选择是否合理,将决定我们软件的高度。 + + +技术的选型,不仅要看技术是不是先进,还要看它是不是适合当前的产品项目。并不是什么技术很新酷,就用什么! + + +我们要深入学习架构与设计、编码、测试、安全、可用性、性能、维护性、体验等技术,并科学运用这些技术。 + + +既然技术只是工具,那么我们就没必要给自己设置各种技术壁垒障碍。 + +如果开发就只学编码,测试就只学测试,认为安全问题,那应该是搞安全的人的事,这样的话是非常不利于团体协作的。 + +每个人都能在一个领域深入地钻研,同时对其他领域有一定了解,对个人、对团队都是非常有利的一件事。这样的话,也不需要 DevOps 这种为了兼顾开发、测试、运维三种角色而存在的工种。 + +7. 一致性 + +我们做软件开发工作的人都知道,一致性很重要,然而现实中这样不一致的例子却比比皆是: + + +从客户的需求,到最终的实现,总是差别很大; + +我们良好的设计,在编码实现的时候,因为赶进度、开发人员偷懒等各种原因绕开设计,抄近路,最后设计和编码无法一致; + +我们在项目初始的时候制定了很多规范,却总是不了了之,难以执行; + +…… + + +通常是一步错步步错,就像下面的秋千图:客户想要一个给三个孩子玩的秋千;产品经理以为就是一个板子加两绳子就行;架构师发现除非把树截开,否则秋千没法荡起来的;程序员以为用绳子和板子连一起就完事了;而真正满足客户需求的,也就只要在绳子上挂个轮胎而已! + + + +一致性在软件开发领域,一直都是理想美好而现实却很残酷,信中也提到: + + +我们要遵守过程的一致性。遵守适用的法律法规、遵循业界共识的标准、规范,确保规范到实现的一致性、代码到二进制的一致性。架构要符合架构原则,设计要遵循设计模式,代码要符合编程规范,最终做到需求与实现一致,达成各项对客户的承诺。我们只有脚踏实地做好每一步,才能真正打造出可信的高质量产品。 + + +无论这个目标有多难,但是从“遵守过程的一致性”开始,在每个阶段都去做到一致性,“脚踏实地做好每一步”,还是有希望能做到“真正打造出可信的高质量产品”。 + +8. 改变习惯 + +在实施软件工程的过程中,有两个难题,一个就是转变思想,另一个就是改变习惯了,这种改变的过程也一定是很痛苦的。 + +为此,我们要改变行为习惯,追求精品。我们要开放透明、积极和勇于揭示问题并主动推动改进。软件开发是一种创造性和艺术性的工作,需要充分发挥我们的聪明才智和潜力。我们要改变只重视功能结果、不重视代码质量的行为习惯,要严格遵守软件工程规范;改变被动的修修补补;改变碎片化知识获取,主动去学习提升并贡献经验、代码,形成共享知识库。我们需要改变的行为和习惯还有很多,对绝大多数人来讲都将是一个痛苦的转变过程,会脱一层皮,但我相信大家能够迎接这种挑战。 + +从事软件开发工作越久,恐怕养成的坏习惯就越多,信中列的几条都很有代表性: + + +“只重视功能结果、不重视代码质量。” + + +功能实现完了就完事了,质量那是 QA 的事。这种坏习惯不改, 质量是很难有保障的。 + + +“不遵守软件工程规范。” + + +软件工程的各种规范不是约束,也不是摆设,而是实实在在为了团队整体更好地协作。对于定好的规范,要严格执行,不合理的规范,也要提出来一起改进。 + + +“被动的修修补补。” + + +为了能继续凑合,继续修修补补,而没有考虑重构改进,也是一个不好的习惯。 + +“碎片化知识获取,不主动去学习提升。” + +在现在的信息时代,碎片化的知识获取是容易的,但是像软件工程这种知识,仅仅通过碎片化的学习还是不够的,必须主动的,系统的去学习,虽然这个过程会很辛苦,但是非常有必要。 + + +“不愿意贡献经验、代码,不去形成共享知识库。” + + +很多人不愿意去分享知识和经验,有的是因为太懒,有的是觉得没什么好处。但是分享本身就是学习和提升的最好手段。知识库这种事不仅是对别人有帮助,对自己也是一个特别好的学习精进的过程。 + +想象下你新加入一个团队,如果这个团队有很好的知识库,你可以通过知识库,很快上手工作。同样的,如果你把你的经验写到知识库,后面的新人也可以从你的分享中受益。 + +9. “软件工程”和“质量工程”需要依靠架构技术 + + +“软件工程”和“质量工程”需要依靠架构技术,而不是依靠 CMM 和 QA 管理流程。一切工程问题,首先要思考能否通过技术解决,当前技术无法解决的问题,暂时由管理手段代劳,同时不停止寻找技术手段。 + + +所有的涉及到的管理问题,最终都要归结到人管理还是制度管理的问题上,软件项目管理也不例外。如果过多的依赖于人的管理,那么项目经理的职责就太重了,优秀的项目经理本身就是稀缺资源,最终会变成瓶颈。 + +所以通过架构技术和工具,把管理流程落实下来是一个非常好的方式。有两个例子可以很好地说明这点。 + +早些年软件服务规模庞大、模块耦合度紧密,所以需要一个庞大的开发团队,团队一大,沟通成本就高,进而管理成本很高。后来微服务这种架构提出后,将大的服务拆成小的服务,整个组织也从大项目部门拆分成各个小组,各小组可以独立更新维护。 + +另一个例子是,以前单元测试和代码审查还有自动部署很难执行,后来借助源代码管理工具和 CI(Continuous integration,持续集成)工具,就可以很容易地进行代码审查,并且可以确保单元测试跑通过后才进行部署。 + +这一点其实信中也有体现: + + +我们将全面强化以 Committer 角色为核心的代码审核和提交机制,代码经过更加严格和系统的审核才能合入版本。为此我们将建立一支更高水平的 Committer 角色群体,负责软件架构的看护、代码的审核和提交,整体保障合入代码的高质量。我们要变革考核机制,要让架构设计好、代码写得好的人脱颖而出,对编程能力不满足要求的人给予帮助和培训。但任何人如果编写的代码长时间不能合入版本,将会被团队抛弃。 + + +10. 软件工程就像一个国家的农业 + +软件工程就像一个国家的农业,是最基础的设施!(出自:蓝血题记) + +看到这句时,我很感动。这些年软件工程被提起的其实不多,大家关注更多的是各种新酷的技术,而对于这种软件开发最基础的理论视而不见。 + +还有人一提到软件工程,就马上说软件工程不是银弹。软件工程从来不说自己是银弹,就像现代医学,也不会号称自己包治百病,它只会不断改进,对症下药。 + + + + \ No newline at end of file diff --git a/专栏/软件工程之美/结束语万事皆项目,软件工程无处不在.md b/专栏/软件工程之美/结束语万事皆项目,软件工程无处不在.md new file mode 100644 index 0000000..3d271ef --- /dev/null +++ b/专栏/软件工程之美/结束语万事皆项目,软件工程无处不在.md @@ -0,0 +1,93 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 万事皆项目,软件工程无处不在 + 你好,我是宝玉。不知不觉,我们专栏就接近尾声,也该告一段落啦。 + +在专栏更新之初我就提到,把每件事都当作一个项目来推进,现在我也用“万事皆项目”来作为我们专栏的结束。 + +我学习软件工程最大的收获,就是在看问题的时候,不再局限于从技术层面或者是一个局部去思考问题,而是站在整体,用软件工程的方法去指导自己的思考和决策。 + +所以在整个专栏的讲述中,我也希望能给你带来这样的转变:在做一件事情之前你可以先考虑一下,这是不是可以当作一个项目来推进,站在整体思考,有目的、有计划、有步骤地解决问题。 + +万事皆项目 + +实际上,日常工作生活中,不仅是软件项目可以应用软件工程的知识,很多事情都可以应用软件工程的知识。就拿我们《软件工程之美》的专栏来说,这也是一个项目,从专栏诞生到完成,你从中也可以感受到对软件工程知识的应用。 + + +项目都是从一个想法开始的 + + +在写这个专栏之前,我曾在微博上多次建议从事软件开发的开发人员学习一下软件工程,这是一门非常有价值的学科,所以后来极客时间邀请我开专栏,我首先想到的就是写一个软件工程相关的专栏,而极客时间还没有这样的专栏,所以我们初步达成了做软件工程专栏的意向。 + + +项目开始之前不要忘记可行性研究 + + +在有想法后,并不是直接就立项开始做,在软件工程中会有可行性研究。在专栏开始之初,也是有一个磨合的过程,我会试写几篇稿子与极客时间内容团队一起打磨,双方都需要通过磨合来确认是不是可能继续合作,当磨合好后,双方觉得没问题了才会最终立项。 + +如果没有经过可行性研究就贸然立项,到项目开始才发现不适合,那对双方都是不负责任的。所以项目开始之前,先进行可行性研究,是避免损失的有效方式。 + + +项目未动,计划先行 + + +一个项目开始之前,制定计划是保障项目正常推进的关键。一个专栏 40 多篇文章,每周更新 3 篇,如果没有一个科学的计划,没有 Dead Line,那是很难保证项目进度的。所以要做到项目未动,计划先行。 + +在制定计划时,也不能盲目乐观,还要考虑到项目风险。比如说作者临时有事可能就会有断更的风险,这时就需要对风险进行控制。 + + +在具体实现一个项目之前,先进行分析和设计 + + +在做一个软件项目的时候,首先要做的就是分析需求,然后根据需求分析的结果设计架构。对于一个专栏来说也是这样的,首先要考虑清楚专栏的主题是什么?专栏的受众是谁?他们想要什么样的内容,然后再去设计专栏的目录。 + +可以说专栏的目录对于一个专栏的成功至关重要,如果没有目录直接天马行空去写,可能会导致写出来的内容偏离主题,用户也不容易理解。当然目录也不是一成不变的,在写作的过程中,根据大家的反馈,我也会做一些微小调整。 + + +从瀑布模型到敏捷开发,从集中交付到持续交付 + + +传统书籍的出版,类似于瀑布模型,作者统一完成书稿,然后交给编辑修订,最后出版。而现在的专栏写作,更像敏捷开发,作者每完成一篇内容,就交给编辑审阅,反复修订后才可以上线发布。《软件工程之美》专栏采用的是持续交付模式,甚至于提纲写好了就先交付,编辑给出反馈意见后,进一步补充完善细节,最终完成上线。 + + +测试和线上维护也很重要 + + +我平时在网上也会写一些文章,通常写完就发出去了,甚至都没有仔细检查,结果经常是有读者留言才发现很多错误。但专栏文章的发布,每一篇除了自己检查,编辑还会对内容进行校对,避免出现错别字或者语法错误,从而保证大家阅读专栏时的体验。 + +软件发布后还会对线上的版本维护,保障稳定运行。专栏每一篇文章上线后,都会有同学留言,对同学的留言进行回复也是很重要的工作,可以帮助同学解答一些文章中的疑惑,也可以从同学们的留言中收获很多有价值的反馈。 + + +项目完成后,不忘总结复盘 + + +如果说软件工程之美专栏是一个项目,现在也到了总结复盘的时候了。 + +首先从做的不太完善的地方开始,总结经验教训。我平时在对于知识的理解和学习,更多是停留在思考上,日常并没有养成习惯去把这些思考写成文字。写作专栏的过程,是把这些知识系统思考梳理的过程,如果日常只是零散的思考,写作的时候就需要对很多细节进行学习和补充。 + +这其实也是我在《学习攻略 怎样学好软件工程?》中给你的建议:在学习软件工程时,尝试把学到的知识写下来,去教给其他人,这样你的收获也会是最大的。 + +接下来,我们复盘下哪些地方做的比较好。 + +软件工程的知识虽然不像一些编程语言或者框架更新的那样快,但也一直在演进。就像十多年前还是瀑布模型为主流,现在越来越多的是敏捷开发和快速迭代的开发模式;十多年前每日构建还是当时最好的实践之一,而现在持续集成已经取代了每日构建成为新的最佳实践。 + +我觉得我还算比较幸运,有很多不同的项目和团队的经历,有小创业团队也有大厂。因为大学转专业学习了软件工程的原因,也一直在留心观察工作中的软件工程实践,所以有机会去学习和实践很多软件工程知识,并在这个专栏中,将这些知识输出交付给你。 + +在专栏更新的过程中,确实引起了不少同学的思考,提出了很多有价值的问题。通过留言,让我有机会去解答一些文章中没有涵盖的内容,通过留言也诞生了很多对专栏精彩的补充分享。通过这些留言,我也切实地感受到很多同学从专栏学习中有收获、有进步,这也是我认为在专栏项目中做的还不错的地方。 + +埋下一颗种子 + +日常生活中很多事情,就像去写一个专栏,并不是一个软件项目,但是你应用软件工程的知识去指导去推进,一样可以帮助你有计划、有步骤地完成它,一样可以让你有很多机会去实践软件工程的知识。 + +你对软件工程知识实践的越多,你对它的理解也会越深刻,这样当你在做软件项目时,你就能更好地应用这些知识,帮助你高质量地完成项目。 + +通过对大家留言的观察,我也发现那些已经有丰富的项目经验的同学收获是最大的,因为他们很容易将丰富的项目经验和软件工程的知识串起来,把零散的知识点借助专栏的学习一点点构建成了完整的软件工程知识体系。 + +如果你现在觉得对这些软件工程知识的吸收有限也没关系,当你从第一天开始学习软件工程,就相当于埋下了一颗种子,当今后再遇到项目中的问题,相信你可以跳出细节之外,站在项目的整体去思考,去软件工程的知识中寻找答案,一点一滴的积累,这颗种子不久后就会变成参天大树。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/00开篇词从“小工”到“专家”,我的软件测试修炼之道.md b/专栏/软件测试52讲/00开篇词从“小工”到“专家”,我的软件测试修炼之道.md new file mode 100644 index 0000000..8d7104c --- /dev/null +++ b/专栏/软件测试52讲/00开篇词从“小工”到“专家”,我的软件测试修炼之道.md @@ -0,0 +1,79 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 开篇词 从“小工”到“专家”,我的软件测试修炼之道 + 你好,我是茹炳晟。我从2002年开始做软件开发,2005年时转型成为测试工程师,算起来已经在这个行业摸爬滚打了16年。2005年的时候,软件测试还停留在重复性手工测试的阶段,而且受重视程度远不如软件开发,所以当时我就像是一个“小工”。 + +可是,我很快就意识到,要真正把测试这件事做透做精,并不是那么容易,这中间有太多技术需要去研究和探索。 + +很多人第一印象会觉得做测试比做开发简单很多,但是我想说,在这个世界上,你想把任何一件事做好、做到极致都没那么容易,都需要付出比别人更多的努力。不管是一万小时定律还是厚积薄发,当你把知识积累到一定程度的时候就会发现,原来软件测试的世界这么有意思。 + +我是国内最早一批从事测试自动化的工程师,并经历了软件测试技术从“原始社会”向“现代文明”发展的整个历程,也经历了从“测试不受重视”到“测试和开发同等重要”的行业理念转变。目前我正在探索由Google等一线互联网巨头主导的“去QE,开发自己测试”的全新模式,也有了很多的感悟和思考。 + +在这期间,我经历了自动化测试用例设计与开发、测试框架选型、测试框架自行研发、测试基础架构设计以及最新的测试服务化(Test as a Service,TaaS)等一系列技术的变革与发展。 + +我带领过的测试项目也几乎涵盖了所有种类,包括嵌入式系统测试、金融平台单元测试、平台SDK测试、轨道交通安全软件测试、Web Service测试、大型电商网站GUI自动化以及性能全链路压测等。 + +由此,我个人也完成了从“小工”到“专家”的蜕变,成为了一名资深的测试架构师。 + +之所以要写这个专栏,我也是希望能把这么多年积累的经验和教训,通过极客时间这个平台,分享给你,让你能够用最短的时间了解整个软件测试行业技术发展的脉络,以“知其然知其所以然”的方式深入理解目前主流的测试技术,不仅知道应该怎么做,更明白为什么要这么做。 + +同时,在循序渐进的专栏学习过程中,我希望你能够学以致用,将所学应用到你所在企业的实际项目中,帮助企业提高测试质量以及测试效率,同时也为自己的职场晋升之路打好基础。 + +面对势不可挡的ABC(AI + Big Data + Cloud)技术浪潮,无论是从被测对象本身的复杂性、多样性和规模性来讲,还是从测试技术以及测试基础架构从无到有的发展来讲,都需要测试工程师的知识面、测试设计能力、测试开发能力和测试平台化抽象能力有质的提升。对此,我结合趋势总结了以下三点。 + +第一,自动化测试在软件质量工程中的地位发生了质的变化,从原本的“以自动化测试为辅”变成了“以自动化测试为主”。 + +所以,你不仅需要从业务本身出发来对软件进行手工测试验证,还需要掌握完整的自动化测试开发技术来设计自动化测试用例。 + +第二,传统软件企业的产品发布通常以“月”为单位,因此,测试执行总时间不会成为关键问题。但是,对于互联网企业,尤其是大型电商网站,产品上线周期通常都是以“天”甚至是以“小时”为单位,留给测试的时间非常有限,这也就对测试执行总时间提出了极大的挑战。为了解决这个难题,你需要一套完善的高并发测试执行基础架构的支持。 + +所以,作为测试工程师,你就必须掌握设计开发测试基础架构的关键技术。 + +第三,随着自动化测试的规模化,测试数据准备的各种问题被逐渐暴露并不断放大,成为影响自动化测试效率以及稳定性的“拦路虎”。早期的传统测试数据准备方法,无论是从测试数据准备的时间成本,还是从测试数据的稳定性和测试数据创建的便利性上看,都已经很难适应大规模自动化测试的要求。 + +所以,你必须系统性地思考如何才能将测试数据的准备工具化,服务化,最终实现平台化。 + +通过这个专栏的学习,我希望你能够对这些趋势与挑战应对自如,并能时刻紧跟测试技术发展的新趋势。 + +理想是美好的,但道路往往是曲折的,因为你和我都非常清楚,技术人想要“一口吃成胖子”几乎是不可能的。但是,不要气馁,我在这个专栏里根据多年的从业经验,给你总结了下面这个“三步走”的策略,助你破茧成蝶。 + +第一步,成为互联网时代合格的测试工程师。 + +如果你是入行不满3年的测试工程师,一定对此有迫切需求。此时,你必须具有快速学习的能力,能迅速掌握被测软件的业务功能与内部架构,并在此基础上运用各种测试方法,尽可能多地发现潜在缺陷,并能够在已知缺陷的基础上进一步发现相关的连带缺陷。 + +从知识体系上看,你需要有比开发人员更全面的计算机基础知识,还需要了解互联网的基础架构、安全攻击、软件性能、用户体验和常见缺陷等知识。从测试技术上看,你需要能够使用常见的测试框架或者工具,需要具有一定的自动化测试脚本的开发能力,这可以把你从大量重复的工作中解放出来,然后你才能有时间去做更有意思的工作。 + +第二步,成为互联网时代优秀的测试工程师。 + +如果你想从“合格”变为“优秀”,那必须先认识到两者的差距在哪里。 + +首先,合格的测试工程师关注的是纯粹的测试,而优秀的测试工程师关注更多的是软件整体的质量,需要根据业务风险以及影响来制定测试策略,有效控制测试的时间和成本,并且能够对测试框架以及工具做出适合项目需求的选型。 + +以新房装修为例,合格的测试工程师就是各个工序的装修师傅,他们只管按照设计要求做好自己的工序,而优秀的测试工程师更像是个包工头,他们关心的是整体交付的质量。 + +其次,优秀的测试工程师不仅可以娴熟地运用各类测试工具,还非常清楚这些测试工具背后的实现原理,以及多个同类测试工具各自的优缺点和适用场景。 + +在遇到问题时,你还需要能够通过二次开发解决工具和框架层面的问题,对于没有合适可用工具的场景,可以自行设计开发一些小工具来更好地展开测试工作。 + +当然这个阶段,你很有可能会接触到一些代码级的测试,这就要求你具有一定的开发背景,并能够很好地理解代码级的测试技术。 + +最后,随着自动化测试用例的不断增长,自动化测试的关注点也从原本的“如何把手工测试步骤用自动化脚本实现”变成了“如何构建低维护成本,可以灵活组装的自动化脚本”,这就要求你理解自动化脚本的分层设计、页面对象模型以及业务流程模型,并且能够把这些设计应用到你的测试框架里。 + +第三步,成为互联网时代的测试架构师。 + +当你经历了各种类型的测试项目,就会发现这些项目本身虽然差异巨大,但是有很多东西是相通的。 + +比如,面对大量测试用例的执行,无论是GUI还是API,都需要一套高效的能够支持高并发的测试执行基础架构;再比如,面对测试过程中的大量差异性数据要求,需要统一的测试数据准备平台;再比如,为了可以更方便地和持续集成与发布系统(CI/CD)以解耦的形式做集成,需要统一发起测试执行的接口。 + +这样的例子还有很多,如果你已经能够站在这样的高度看待软件测试,那么恭喜你,你已经具备了测试架构师的视野。当然,你还必须对一些前沿的测试方法和技术有自己的理解,并能够在恰当的时候、因地制宜地把它们应用到实际项目中。 + +这就是我给你总结的“三步走”进阶策略了。千里之行始于足下,接下来我会从测试基础知识讲起,结合实际案例,由浅入深地带你温故知新,提升自己的软件测试技能。 + +未来的四个月,我将和你一起探讨交流测试世界里各种有意思的技术,也希望四个月后,你我都能遇见更好的自己。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/01你真的懂测试吗?从“用户登录”测试谈起.md b/专栏/软件测试52讲/01你真的懂测试吗?从“用户登录”测试谈起.md new file mode 100644 index 0000000..b43602b --- /dev/null +++ b/专栏/软件测试52讲/01你真的懂测试吗?从“用户登录”测试谈起.md @@ -0,0 +1,135 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 你真的懂测试吗?从“用户登录”测试谈起 + 作为专栏的第一篇文章,我选择了一个你耳熟能详的“用户登录”功能作为测试对象,希望通过这样一个简单直白的功能帮助你理解如何做好测试,以及现阶段你需要加强和提高的测试技能。 + +可能你会说,“用户登录”这个测试对象也有点太简单了吧,我只要找一个用户,让他在界面上输入用户名和密码,然后点击“确认”按钮,验证一下是否登录成功就可以了。的确,这构成了一个最基本、最典型的测试用例,这也是终端用户在使用系统时最典型的Happy Path场景。 + +但是作为测试工程师,你的目标是要保证系统在各种应用场景下的功能是符合设计要求的,所以你需要考虑的测试用例就需要更多、更全面,于是你可能会根据“用户登录”功能的需求描述,结合等价类划分和边界值分析方法来设计一系列的测试用例。 + +那什么是等价类划分和边界值分析方法呢?首先,这二者都隶属于最常用、最典型、也是最重要的黑盒测试方法。 + + +等价类划分方法,是将所有可能的输入数据划分成若干个子集,在每个子集中,如果任意一个输入数据对于揭露程序中潜在错误都具有同等效果,那么这样的子集就构成了一个等价类。后续只要从每个等价类中任意选取一个值进行测试,就可以用少量具有代表性的测试输入取得较好的测试覆盖结果。 +边界值分析方法,是选取输入、输出的边界值进行测试。因为通常大量的软件错误是发生在输入或输出范围的边界上,所以需要对边界值进行重点测试,通常选取正好等于、刚刚大于或刚刚小于边界的值作为测试数据。 + + +从方法论上可以看出来,边界值分析是对等价类划分的补充,所以这两种测试方法经常结合起来使用。 + +现在,针对“用户登录”功能,基于等价类划分和边界值分析方法,我们设计的测试用例包括: + + +输入已注册的用户名和正确的密码,验证是否登录成功; +输入已注册的用户名和不正确的密码,验证是否登录失败,并且提示信息正确; +输入未注册的用户名和任意密码,验证是否登录失败,并且提示信息正确; +用户名和密码两者都为空,验证是否登录失败,并且提示信息正确; +用户名和密码两者之一为空,验证是否登录失败,并且提示信息正确; +如果登录功能启用了验证码功能,在用户名和密码正确的前提下,输入正确的验证码,验证是否登录成功; +如果登录功能启用了验证码功能,在用户名和密码正确的前提下,输入错误的验证码,验证是否登录失败,并且提示信息正确。 + + +列出这些测试用例后,你可能已经觉得比较满意了,因为你感觉已经把自己的测试知识都用在这些用例设计中了。 + +的确,上面的测试用例集已经涵盖了主要的功能测试场景。但是在一个优秀的测试工程师眼中,这些用例只能达到勉强及格的标准。 + +什么?才刚刚及格?如果你有这个想法,那我建议你在继续看下面的内容前,先仔细思考一下,这些测试用例是否真的还需要扩充。 + +现在,我跟你分享一下有经验的测试工程师会再增加的测试用例: + + +用户名和密码是否大小写敏感; +页面上的密码框是否加密显示; +后台系统创建的用户第一次登录成功时,是否提示修改密码; +忘记用户名和忘记密码的功能是否可用; +前端页面是否根据设计要求限制用户名和密码长度; +如果登录功能需要验证码,点击验证码图片是否可以更换验证码,更换后的验证码是否可用; +刷新页面是否会刷新验证码; +如果验证码具有时效性,需要分别验证时效内和时效外验证码的有效性; +用户登录成功但是会话超时后,继续操作是否会重定向到用户登录界面; +不同级别的用户,比如管理员用户和普通用户,登录系统后的权限是否正确; +页面默认焦点是否定位在用户名的输入框中; +快捷键Tab和Enter等,是否可以正常使用。 + + +看完这些用例,你可能会说:“哇塞,原来一个简简单单的登录功能居然有这么多需要测试的点”。但是,你别高兴得太早,“用户登录”功能的测试还没结束。 + +虽然改进后的测试用例集相比之前的测试覆盖率的确已经提高了很多,但是站在资深测试人员的角度来看,还有很多用例需要设计。 + +经我这么一说,你可能已经发现,上面所有的测试用例设计都是围绕显式功能性需求的验证展开的,换句话说,这些用例都是直接针对“用户登录”功能的功能性进行验证和测试的。 + +但是,一个质量过硬的软件系统,除了显式功能性需求以外,其他的非功能性需求即隐式功能性需求也是极其关键的。 + +显式功能性需求(Functional requirement)的含义从字面上就可以很好地理解,指的是软件本身需要实现的具体功能, 比如“正常用户使用正确的用户名和密码可以成功登录”、“非注册用户无法登录”等,这都是属于典型的显式功能性需求描述。 + +那什么是非功能性需求(Non-functional requirement)呢?从软件测试的维度来看,非功能性需求主要涉及安全性、性能以及兼容性三大方面。 在上面所有的测试用例设计中,我们完全没有考虑对非功能性需求的测试,但这些往往是决定软件质量的关键因素。 + +明白了非功能性需求测试的重要性后,你可以先思考一下还需要设计哪些测试用例,然后再来看看我会给出哪些用例,相信这种方式对你的帮助会更大。 + +安全性测试用例包括: + + +用户密码后台存储是否加密; +用户密码在网络传输过程中是否加密; +密码是否具有有效期,密码有效期到期后,是否提示需要修改密码; +不登录的情况下,在浏览器中直接输入登录后的URL地址,验证是否会重新定向到用户登录界面; +密码输入框是否不支持复制和粘贴; +密码输入框内输入的密码是否都可以在页面源码模式下被查看; +用户名和密码的输入框中分别输入典型的“SQL注入攻击”字符串,验证系统的返回页面; +用户名和密码的输入框中分别输入典型的“XSS跨站脚本攻击”字符串,验证系统行为是否被篡改; +连续多次登录失败情况下,系统是否会阻止后续的尝试以应对暴力破解; +同一用户在同一终端的多种浏览器上登录,验证登录功能的互斥性是否符合设计预期; +同一用户先后在多台终端的浏览器上登录,验证登录是否具有互斥性。 + + +性能压力测试用例包括: + + +单用户登录的响应时间是否小于3秒; +单用户登录时,后台请求数量是否过多; +高并发场景下用户登录的响应时间是否小于5秒; +高并发场景下服务端的监控指标是否符合预期; +高集合点并发场景下,是否存在资源死锁和不合理的资源等待; +长时间大量用户连续登录和登出,服务器端是否存在内存泄漏。 + + +兼容性测试用例包括: + + +不同浏览器下,验证登录页面的显示以及功能正确性; +相同浏览器的不同版本下,验证登录页面的显示以及功能正确性; +不同移动设备终端的不同浏览器下,验证登录页面的显示以及功能正确性; +不同分辨率的界面下,验证登录页面的显示以及功能正确性。 + + +说到这里,你还会觉得“用户登录”功能的测试非常简单、不值一提么?一个看似简单的功能测试,居然涵盖了如此多的测试用例,除了要覆盖明确的功能性需求,还需要考虑其他诸多的非功能性需求。 + +另外,通过这些测试用例的设计,你也可以发现,一个优秀的测试工程师必须具有很宽广的知识面,如果你不能对被测系统的设计有深入的理解、不明白安全攻击的基本原理、没有掌握性能测试的基本设计方法,很难设计出“有的放矢”的测试用例。 + +通过“用户登录”功能测试这个实例,我希望可以激发你对测试更多的思考,并且开拓你设计测试用例的思路,以达到抛砖引玉的效果。 + +看完了这些测试用例,你可能会说还有一些遗漏的测试点没有覆盖到,这个功能的测试点还不够全面。那么,接下来我再跟你说说测试的不可穷尽性,即绝大多数情况下,是不可能进行穷尽测试的。 + +所谓的“穷尽测试”是指包含了软件输入值和前提条件所有可能组合的测试方法,完成穷尽测试的系统里应该不残留任何未知的软件缺陷。 因为如果有未知的软件缺陷,你可以通过做更多的测试来找到它们,也就是说你的测试还没有穷尽。 + +但是,在绝大多数的软件工程实践中,测试由于受限于时间成本和经济成本,是不可能去穷尽所有可能的组合的,而是采用基于风险驱动的模式,有所侧重地选择测试范围和设计测试用例,以寻求缺陷风险和研发成本之间的平衡。 + +总结 + +首先,对于高质量的软件测试,用例设计不仅需要考虑明确的显式功能性需求,还要涉及兼容性、安全性和性能等一系列的非功能性需求,这些非功能性需求对软件系统的质量有着举足轻重的作用。 + +其次,优秀的测试工程师必须具有宽广的知识面,才能设计出有针对性、更易于发现问题的测试用例。 + +最后,软件测试的用例设计是不可穷尽的,工程实践中难免受制于时间成本和经济成本,所以优秀的测试工程师需要兼顾缺陷风险和研发成本之间的平衡。 + +思考题 + +从拓展思维的角度,请你思考一下“用户登录”功能是否还可以添加更多的测试用例。基于同样的思路,思考一下你目前工作中的测试用例设计是否需要加入更多的测试点。 + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/02如何设计一个“好的”测试用例?.md b/专栏/软件测试52讲/02如何设计一个“好的”测试用例?.md new file mode 100644 index 0000000..3a19ccd --- /dev/null +++ b/专栏/软件测试52讲/02如何设计一个“好的”测试用例?.md @@ -0,0 +1,165 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 如何设计一个“好的”测试用例? + 在上一篇文章中,我以“用户登录”这一简单直接的功能作为测试对象,为你介绍了如何设计测试用例。现在你应该已经知道,为了保证软件系统的质量,测试用例的设计不仅需要考虑功能性需求,还要考虑大量的非功能性需求。 + +那么,今天我会重点和你探讨如何才能设计出一个“好的”测试用例。 + +什么才算是“好的”测试用例? + +在正式开始讨论之前,我先跟你聊聊,什么才是“好的”测试用例,这个“好”又应该体现在哪些方面。这是一个看似简单实则难以回答的问题,即使深入思考后,也很难有非常标准的答案。 + +通常,你的第一反应很可能会是“发现了软件缺陷的测试用例就是好的用例”,我可能会反问你“如果说测试用例发现了缺陷就是好用例,那么在该缺陷被修复后,同样的用例难道就不是好用例了吗?”。 + +你可能还会说“发现软件缺陷可能性大的测试用例就是好用例”,这话看起来还是蛮有道理的,但是我同样会反问你“你打算用什么方法来量化测试用例发现缺陷的可能性?”。 + +类似地,你可能还会说“发现至今未被发现的软件缺陷的测试用例就是好用例”,那么我想问你的是:如何评估是否还存在未被发现的缺陷?如果软件中根本就没有错误了呢? + +其实,是你定义“好的”测试用例的思路错了,这就有点像“傻子吃烧饼”,连吃五个不饱,吃完第六个终于饱了,于是他说:早知道吃了第六个就会饱,何必吃前面五个呢。细想,他吃的六个烧饼其实是一个整体,一起吃下去才会饱,而你无法找到吃一个就能饱的“好”烧饼。 + +对于测试用例其实也是同样的道理,“好的”测试用例一定是一个完备的集合,它能够覆盖所有等价类以及各种边界值,而跟能否发现缺陷无关。 + +我举一个“池塘捕鱼”的例子,可以帮你更好地理解什么是“好的”测试用例。 + +如果把被测试软件看作一个池塘,软件缺陷是池塘中的鱼,建立测试用例集的过程就像是在编织一张捕渔网。“好的”测试用例集就是一张能够覆盖整个池塘的大渔网,只要池塘里有鱼,这个大渔网就一定能把鱼给捞上来。 + +如果渔网本身是完整的且合格的,那么捞不到鱼,就证明池塘中没有鱼,而渔网的好坏与池塘中是否有鱼无关。 + +“好的”测试用例必须具备哪些特征? + +一个“好的”测试用例,必须具备以下三个特征。 + + +整体完备性: “好的”测试用例一定是一个完备的整体,是有效测试用例组成的集合,能够完全覆盖测试需求。 + +等价类划分的准确性: 指的是对于每个等价类都能保证只要其中一个输入测试通过,其他输入也一定测试通过。 + +等价类集合的完备性: 需要保证所有可能的边界值和边界条件都已经正确识别。 + + +做到了以上三点,就可以肯定测试是充分且完备的,即做到了完整的测试需求覆盖。 + +三种最常用的测试用例设计方法 + +明白了“好的”测试用例的内涵和外延后,我再回过头来给你讲讲,为了能够设计出“好的”测试用例,你通常都要使用哪些设计方法。 + +从理论层面来讲,设计用例的方法有很多,如果你去翻阅测试图书或网络教程,会发现一堆让人眼花缭乱的测试方法,比如等价类划分法、边界值分析法、错误推测方法、因果图方法、判定表驱动分析法、正交实验设计方法、功能图分析方法、场景设计方法、形式化方法、扩展有限状态机方法等等,但是从软件企业实际的工程实践来讲,真正具有实用价值并且常用的只有前三种方法。 + +当然,对于那些与人的生命安全直接或间接相关的软件,比如飞行控制、轨道交通的列车控制、医疗检测相关的软件或者系统,由于需要达到几近变态的测试覆盖率要求,会采用更多的测试设计方法。但对大多数的软件测试而言,综合使用等价类划分、边界值分析和错误推测这三大类方法就足够了。 + +接下来,我会结合实际的例子,给你解释一下这三类方法的核心概念以及在使用时需要注意的问题。 + +第一,等价类划分方法 + +从上一篇文章中你已经知道了,等价类中任意一个输入数据对于揭露程序中潜在错误都具有同等效果。后续我们只要从每个等价类中任意选取一个值进行测试,就可以用少量具有代表性的测试输入取得较好的测试覆盖结果。 + +现在,我给你看一个具体的例子:学生信息系统中有一个“考试成绩”的输入项,成绩的取值范围是0~100之间的整数,考试成绩及格的分数线是60。 + +为了测试这个输入项,显然不可能用0~100的每一个数去测试。通过需求描述可以知道,输入0~59之间的任意整数,以及输入60~100之间的任意整数,去验证和揭露输入框的潜在缺陷可以看做是等价的。 + +那么这就可以在0~59和60~100之间各随机抽取一个整数来进行验证。这样的设计就构成了所谓的“有效等价类”。 + +你不要觉得进行到这里,已经完成了等价类划分的工作,因为等价类划分方法的另一个关键点是要找出所有“无效等价类”。显然,如果输入的成绩是负数,或者是大于100的数等都构成了“无效等价类”。 + +在考虑了无效等价类后,最终设计的测试用例为: + + +有效等价类1:0~59之间的任意整数; +有效等价类2:59~100之间的任意整数; +无效等价类1:小于0的负数; +无效等价类2:大于100的整数; +无效等价类3:0~100之间的任何浮点数; +无效等价类4:其他任意非数字字符。 + + +第二,边界值分析方法 + +边界值分析是对等价类划分的补充,你从工程实践经验中可以发现,大量的错误发生在输入输出的边界值上,所以需要对边界值进行重点测试,通常选取正好等于、刚刚大于或刚刚小于边界的值作为测试数据。 + +我们继续看学生信息系统中“考试成绩”的例子,选取的边界值数据应该包括:-1,0,1,59,60,61,99,100,101。 + +第三,错误推测方法 + +错误推测方法是指基于对被测试软件系统设计的理解、过往经验以及个人直觉,推测出软件可能存在的缺陷,从而有针对性地设计测试用例的方法。这个方法强调的是对被测试软件的需求理解以及设计实现的细节把握,当然还有个人的能力。 + +错误推测法和目前非常流行的“探索式测试方法”的基本思想和理念是不谋而合的,这类方法在目前的敏捷开发模式下的投入产出比很高,因此被广泛应用。但是,这个方法的缺点也显而易见,那就是难以系统化,并且过度依赖个人能力。 + +比如,Web界面的GUI功能测试,需要考虑浏览器在有缓存和没有缓存下的表现;Web Service的API测试,需要考虑被测API所依赖的第三方API出错下的处理逻辑;对于代码级的单元测试,需要考虑被测函数的输入参数为空情况下的内部处理逻辑等等。由此可见,这些测试用例的设计都是基于曾经遇到的问题而进行的错误推测,很大程度上取决于个人能力。 + +在软件企业的具体实践中,为了降低对个人能力的依赖,通常会建立常见缺陷知识库,在测试设计的过程中,会使用缺陷知识库作为检查点列表(checklist),去帮助优化补充测试用例的设计。 + +对于中小企业,可能最初的方法就是建立一个简单的wiki页面,让测试工程师完成测试用例的最初设计后对应这个wiki页面先做一轮自检,如果在后续测试中发现了新的点,就会继续完善这个wiki页面。 + +对于测试基础架构比较成熟的中大型软件企业,通常会以该缺陷知识库作为数据驱动测试的输入来自动生成部分的测试数据,这部分内容我会在后面的文章中详细介绍。 + +如何才能设计出“好的”测试用例? + +掌握了最基本的三种设计测试用例的方法,你就相当于拿到了打仗所需要的枪支弹药,接下来就是如何在实战中用这些武器打个大胜仗了。 + +在真实的工程实践中,不同的软件项目在研发生命周期的各个阶段都会有不同的测试类型。 比如,传统软件的开发阶段通常会有单元测试,软件模块集成阶段会有代码级集成测试,打包部署后会有面向终端用户的GUI测试;再比如,电商网站的测试会分为服务器端基于API的测试、中间件测试、前端GUI测试等。 + +对于每一种不同的测试类型,设计出“好的”测试用例的关注点和方法论可能会有很大的差异, 有些可能采用黑盒方法,有些可能采用白盒方法,有些还会采用灰盒方法(比如,微服务架构中的测试),所以很难有一套放之四海而皆准的套路。 + +所以,在这篇文章中,我仅以最常见、最容易理解的面向终端用户的GUI测试为例,跟你聊聊如何才能设计一个“好的”测试用例。 + +面向终端用户的GUI测试,最核心的测试点就是验证软件对需求的满足程度,这就要求测试工程师对被测软件的需求有深入的理解。在我看来,深入理解被测软件需求的最好方法是,测试工程师在需求分析和设计阶段就开始介入,因为这个阶段是理解和掌握软件的原始业务需求的最好时机。 + +只有真正理解了原始业务需求之后,才有可能从业务需求的角度去设计针对性明确、从终端用户使用场景考虑的端到端(End-2-End)的测试用例集。这个阶段的测试用例设计,主要目的是验证各个业务需求是否被满足,主要采用基于黑盒的测试设计方法。 + +在具体的用例设计时,首先需要搞清楚每一个业务需求所对应的多个软件功能需求点,然后分析出每个软件功能需求点对应的多个测试需求点,最后再针对每个测试需求点设计测试用例。 + +这个用例设计过程,你可能觉得有点绕,但是没关系,我以“用户登录”功能的测试用例设计为例,画了一张图来帮你理清这些概念之间的映射关系。 + +图中的业务需求到软件功能需求、软件功能需求到测试需求,以及测试需求到测试用例的映射关系,在非互联网软件企业的实践中,通常会使用需求追踪管理工具(比如ALM、DOORS、JIRA、TestLink等)来管理,并以此来衡量测试用例对业务需求、软件功能需求的覆盖率。 + + + +具体到测试用例本身的设计,有两个关键点需要你注意。 + + +从软件功能需求出发,全面地、无遗漏地识别出测试需求是至关重要的,这将直接关系到用例的测试覆盖率。 比如,如果你没有识别出用户登录功能的安全性测试需求,那么后续设计的测试用例就完全不会涉及安全性,最终造成重要测试漏洞。 + +对于识别出的每个测试需求点,需要综合运用等价类划分、边界值分析和错误推测方法来全面地设计测试用例。 这里需要注意的是,要综合运用这三种方法,并针对每个测试需求点的具体情况,进行灵活选择。- +以“用户登录”的功能性测试需求为例,你首先应该对“用户名”和“密码”这两个输入项分别进行等价类划分,列出对应的有效等价类和无效等价类,对于无效等价类的识别可以采用错误猜测法(比如,用户名包含特殊字符等),然后基于两者可能的组合,设计出第一批测试用例。- +等价类划分完后,你需要补充“用户名”和“密码”这两个输入项的边界值的测试用例,比如用户名为空(NULL)、用户名长度刚刚大于允许长度等。 + + +用例设计的其他经验 + +除了上面介绍的方法外,我再跟你分享三个独家“秘籍”,希望能够帮你设计出“好的”测试用例集。 + + +只有深入理解被测试软件的架构,你才能设计出“有的放矢”的测试用例集,去发现系统边界以及系统集成上的潜在缺陷。- +作为测试工程师,切忌不能把整个被测系统看作一个大黑盒,你必须对内部的架构有清楚的认识,比如数据库连接方式、数据库的读写分离、消息中间件Kafka的配置、缓存系统的层级分布、第三方系统的集成等等。 + +必须深入理解被测软件的设计与实现细节,深入理解软件内部的处理逻辑。- +单单根据测试需求点设计的用例,只能覆盖“表面”的一层,往往会覆盖不到内部的处理流程、分支处理,而没有覆盖到的部分就很可能出现缺陷遗漏。在具体实践中,你可以通过代码覆盖率指标找出可能的测试遗漏点。- +同时,切忌不要以开发代码的实现为依据设计测试用例。因为开发代码实现的错误会导致测试用例也出错,所以你应该根据原始需求设计测试用例。 + +需要引入需求覆盖率和代码覆盖率来衡量测试执行的完备性,并以此为依据来找出遗漏的测试点。 关于什么是需求覆盖率和代码覆盖率,我会在后续的文章中详细介绍。 + + +总结 + +最后,我来简单总结一下今天的主要内容。 + +首先,你需要明白,“好的”测试用例一定是一个完备的集合,它能够覆盖所有等价类以及各种边界值,而能否发现软件缺陷并不是衡量测试用例好坏的标准。 + +其次,设计测试用例的方法有很多种,但综合运用等价类划分、边界值分析和错误推测方法,可以满足绝大多数软件测试用例设计的需求。 + +再次,“好的”测试用例在设计时,需要从软件功能需求出发,全面地、无遗漏地识别出测试需求至关重要。 + +最后,如果想设计一个“好的”测试用例,你必须要深入理解被测软件的架构设计,深入软件内部的处理逻辑,需求覆盖率和代码覆盖率这两个指标可以帮你衡量测试执行的完备性。 + +思考题 + +在设计测试用例的过程中,你有哪些可供分享的好的实践和方法? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/03什么是单元测试?如何做好单元测试?.md b/专栏/软件测试52讲/03什么是单元测试?如何做好单元测试?.md new file mode 100644 index 0000000..d9723c8 --- /dev/null +++ b/专栏/软件测试52讲/03什么是单元测试?如何做好单元测试?.md @@ -0,0 +1,230 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 什么是单元测试?如何做好单元测试? + 今天我要跟你分享的主题是单元测试,如果你没有开发背景,感觉这篇文章理解起来有难度,那你可以在学完后续的“代码级测试”系列的文章后,再回过头来看一遍这篇文章,相信你会有醍醐灌顶的感觉。 + +什么是单元测试? + +在正式开始今天的话题之前,我先给你分享一个工厂生产电视机的例子。 + +工厂首先会将各种电子元器件按照图纸组装在一起构成各个功能电路板,比如供电板、音视频解码板、射频接收板等,然后再将这些电路板组装起来构成一个完整的电视机。 + +如果一切顺利,接通电源后,你就可以开始观看电视节目了。但是很不幸,大多数情况下组装完成的电视机根本无法开机,这时你就需要把电视机拆开,然后逐个模块排查问题。 + +假设你发现是供电板的供电电压不足,那你就要继续逐级排查组成供电板的各个电子元器件,最终你可能发现罪魁祸首是一个电容的故障。这时,为了定位到这个问题,你已经花费了大量的时间和精力。 + +那在后续的生产中,如何才能避免类似的问题呢? + +你可能立即就会想到,为什么不在组装前,就先测试每个要用到的电子元器件呢?这样你就可以先排除有问题的元器件,最大程度地防止组装完成后逐级排查问题的事情发生。 + +实践也证明,这的确是一个行之有效的好办法。 + +如果把电视机的生产、测试和软件的开发、测试进行类比,你可以发现: + + +电子元器件就像是软件中的单元,通常是函数或者类,对单个元器件的测试就像是软件测试中的单元测试; + +组装完成的功能电路板就像是软件中的模块,对电路板的测试就像是软件中的集成测试; + +电视机全部组装完成就像是软件完成了预发布版本,电视机全部组装完成后的开机测试就像是软件中的系统测试。 + + +通过这个类比,相信你已经体会到了单元测试对于软件整体质量的重要性,那么单元测试到底是什么呢? + + +单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类。 + + +单元测试通常由开发工程师完成,一般会伴随开发代码一起递交至代码库。单元测试属于最严格的软件测试手段,是最接近代码底层实现的验证手段,可以在软件开发的早期以最小的成本保证局部代码的质量。 + +另外,单元测试都是以自动化的方式执行,所以在大量回归测试的场景下更能带来高收益。 + +同时,你还会发现,单元测试的实施过程还可以帮助开发工程师改善代码的设计与实现,并能在单元测试代码里提供函数的使用示例,因为单元测试的具体表现形式就是对函数以各种不同输入参数组合进行调用,这些调用方法构成了函数的使用说明。 + +如何做好单元测试? + +要做好单元测试,你首先必须弄清楚单元测试的对象是代码,以及代码的基本特征和产生错误的原因,然后你必须掌握单元测试的基本方法和主要技术手段,比如什么是驱动代码、桩代码和Mock代码等。 + +第一,代码的基本特征与产生错误的原因 + +开发语言多种多样,程序实现的功能更是千变万化,我可以提炼出代码的基本特征,并总结出代码缺陷的主要原因么?答案是肯定,你静下心来思考时,会发现其中是有规律可寻的。 + +因为无论是开发语言还是脚本语言,都会有条件分支、循环处理和函数调用等最基本的逻辑控制,如果抛开代码需要实现的具体业务逻辑,仅看代码结构的话,你会发现所有的代码都是在对数据进行分类处理,每一次条件判定都是一次分类处理,嵌套的条件判定或者循环执行,也是在做分类处理。 + +如果有任何一个分类遗漏,都会产生缺陷;如果有任何一个分类错误,也会产生缺陷;如果分类正确也没有遗漏,但是分类时的处理逻辑错误,也同样会产生缺陷。 + +可见,要做到代码功能逻辑正确,必须做到分类正确并且完备无遗漏,同时每个分类的处理逻辑必须正确。 + +在具体的工程实践中,开发工程师为了设计并实现逻辑功能正确的代码,通常会有如下的考虑过程: + + +如果要实现正确的功能逻辑,会有哪几种正常的输入; + +是否有需要特殊处理的多种边界输入; + +各种潜在非法输入的可能性以及如何处理。 + + +讲到这里,你有没有回想起我跟你分享的“等价类”。没错,这些开发工程师眼中的代码“功能点”,就是单元测试的“等价类”。 + +第二,单元测试用例详解 + +在实际工作中,你想做好单元测试,就必须对单元测试的用例设计有深入的理解。 + +通常来讲,单元测试的用例是一个“输入数据”和“预计输出”的集合。 你需要针对确定的输入,根据逻辑功能推算出预期正确的输出,并且以执行被测试代码的方式进行验证,用一句话概括就是“在明确了代码需要实现的逻辑功能的基础上,什么输入,应该产生什么输出”。 + +但是,对于单元测试来讲,测试用例的“输入数据”和“预计输出”可能远比你想得要复杂得多。 + +首先,让我来解释一下单元测试用例“输入数据”都有哪些种类,如果你想当然的认为只有被测试函数的输入参数是“输入数据”的话,那就大错特错了。 这里我总结了几种“输入数据”,希望可以帮助你理解什么才是完整的单元测试“输入数据”: + + +被测试函数的输入参数; + +被测试函数内部需要读取的全局静态变量; + +被测试函数内部需要读取的成员变量; + +函数内部调用子函数获得的数据; + +函数内部调用子函数改写的数据; + +嵌入式系统中,在中断调用时改写的数据; + +… + + +然后,让我们再来看看“预计输出”,如果没有明确的预计输出,那么测试本身就失去了意义。同样地,“预计输出” 绝对不是只有函数返回值这么简单,还应该包括函数执行完成后所改写的所有数据。 具体来看有以下几大类: + + +被测试函数的返回值; + +被测试函数的输出参数; + +被测试函数所改写的成员变量; + +被测试函数所改写的全局变量; + +被测试函数中进行的文件更新; + +被测试函数中进行的数据库更新; + +被测试函数中进行的消息队列更新; + +… + + +另外,对于预计输出值,你必须严格根据代码的功能逻辑来设定,而不能通过阅读代码来推算预期输出,否则就是“掩耳盗铃”了。 + +你不要觉得好笑,这种情况经常出现。主要原因是,开发工程师自己测试自己写的代码时会有严重的思维惯性,以至于会根据自己的代码实现来推算预计输出。 + +最后,我还要再提一个点,如果某些等价类或者边界值,开发工程师在开发的时候都没有考虑到,测试的时候就更不会去设计对应的测试用例了,这样也就会造成测试盲区。 + +第三,驱动代码,桩代码和Mock代码 + +驱动代码,桩代码和Mock代码,是单元测试中最常出现的三个名词。驱动代码是用来调用被测函数的,而桩代码和Mock代码是用来代替被测函数调用的真实代码的。 + +- +驱动代码,桩代码和Mock代码三者的逻辑关系 + +驱动代码(Driver)指调用被测函数的代码,在单元测试过程中,驱动模块通常包括调用被测函数前的数据准备、调用被测函数以及验证相关结果三个步骤。驱动代码的结构,通常由单元测试的框架决定。 + +桩代码(Stub)是用来代替真实代码的临时代码。 比如,某个函数A的内部实现中调用了一个尚未实现的函数B,为了对函数A的逻辑进行测试,那么就需要模拟一个函数B,这个模拟的函数B的实现就是所谓的桩代码。 + +为了帮你理解,我带你看下这个例子:假定函数A是被测函数,其内部调用了函数B(具体伪代码如下): + +- +被测函数A内部调用了函数B + +在单元测试阶段,由于函数B尚未实现,但是为了不影响对函数A自身实现逻辑的测试,你可以用一个假的函数B来代替真实的函数B,那么这个假的函数B就是桩函数。 + +为了实现函数A的全路径覆盖,你需要控制不同的测试用例中函数B的返回值,那么桩函数B的伪代码就应该是这个样子的: + + +当执行第一个测试用例的时候,桩函数B应该返回true,而当执行第二个测试用例的时候,桩函数B应该返回false。 + + +这样就覆盖了被测试函数A的if-else的两个分支。 + + + +桩函数内部实现 + +从这个例子可以看出,桩代码的应用首先起到了隔离和补齐的作用,使被测代码能够独立编译、链接,并独立运行。同时,桩代码还具有控制被测函数执行路径的作用。 + +所以,编写桩代码通常需要遵守以下三个原则: + + +桩函数要具有与原函数完全相同的原形,仅仅是内部实现不同,这样测试代码才能正确链接到桩函数; + +用于实现隔离和补齐的桩函数比较简单,只需保持原函数的声明,加一个空的实现,目的是通过编译链接; + +实现控制功能的桩函数是应用最广泛的,要根据测试用例的需要,输出合适的数据作为被测函数的内部输入。 + + +Mock代码和桩代码非常类似,都是用来代替真实代码的临时代码,起到隔离和补齐的作用。但是很多人,甚至是具有多年单元测试经验的开发工程师,也很难说清这二者的区别。 + +在我看来,Mock代码和桩代码的本质区别是:测试期待结果的验证(Assert and Expectiation)。 + + +对于Mock代码来说,我们的关注点是Mock方法有没有被调用,以什么样的参数被调用,被调用的次数,以及多个Mock函数的先后调用顺序。所以,在使用Mock代码的测试中,对于结果的验证(也就是assert),通常出现在Mock函数中。 + +对于桩代码来说,我们的关注点是利用Stub来控制被测函数的执行路径,不会去关注Stub是否被调用以及怎么样被调用。所以,你在使用Stub的测试中,对于结果的验证(也就是assert),通常出现在驱动代码中。 + + +在这里,我只想让你理解两者的本质区别以确保你知识结构的完整性,如果你想深入比较,可以参考马丁·福勒(Martin Fowler)的著名文章《Mock代码不是桩代码》(Mocks Aren’t Stubs)。 + +因为从实际应用的角度看,就算你不能分清Mock代码和桩代码,也不会影响你做好单元测试,所以我并没有从理论层面去深入比较它们的区别。 + +实际项目中如何开展单元测试? + +最后我要跟你聊一下,实际软件项目中如何开展单元测试? + + +并不是所有的代码都要进行单元测试,通常只有底层模块或者核心模块的测试中才会采用单元测试。 + +你需要确定单元测试框架的选型,这和开发语言直接相关。比如,Java最常用的单元测试框架是Junit和TestNG;C/C++最常用的单元测试框架是CppTest和Parasoft C/C++test;框架选型完成后,你还需要对桩代码框架和Mock代码框架选型,选型的主要依据是开发所采用的具体技术栈。- +通常,单元测试框架、桩代码/Mock代码的选型工作由开发架构师和测试架构师共同决定。 + +为了能够衡量单元测试的代码覆盖率,通常你还需要引入计算代码覆盖率的工具。不同的语言会有不同的代码覆盖率统计工具,比如Java的JaCoCo,JavaScript的Istanbul。在后续的文章中,我还会详细为你介绍代码覆盖率的内容。 + +最后你需要把单元测试执行、代码覆盖率统计和持续集成流水线做集成,以确保每次代码递交,都会自动触发单元测试,并在单元测试执行过程中自动统计代码覆盖率,最后以“单元测试通过率”和“代码覆盖率”为标准来决定本次代码递交是否能够被接受。 + + +如果你有开发背景,那么入门单元测试是比较容易的。但真正在项目中全面推行单元测试时,你会发现还有一些困难需要克服: + + +紧密耦合的代码难以隔离; + +隔离后编译链接运行困难; + +代码本身的可测试性较差,通常代码的可测试性和代码规模成正比; + +无法通过桩代码直接模拟系统底层函数的调用; + +代码覆盖率越往后越难提高。 + + +总结 + +我给你详细介绍了单元测试的概念,和你重点讨论了用例的组成,以及在实际项目中开展单元测试的方法,你需要注意以下三个问题: + + +代码要做到功能逻辑正确,必须做到分类正确并且完备无遗漏,同时每个分类的处理逻辑必须正确; + +单元测试是对软件中的最小可测试单元在与软件其他部分相隔离的情况下进行的代码级测试; + +桩代码起到了隔离和补齐的作用,使被测代码能够独立编译、链接,并运行。 + + +思考题 + +你所在的公司有做单元测试吗?实施单元测试过程中遇到过哪些问题,你是如何解决的? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/04为什么要做自动化测试?什么样的项目适合做自动化测试?.md b/专栏/软件测试52讲/04为什么要做自动化测试?什么样的项目适合做自动化测试?.md new file mode 100644 index 0000000..c1ee4d9 --- /dev/null +++ b/专栏/软件测试52讲/04为什么要做自动化测试?什么样的项目适合做自动化测试?.md @@ -0,0 +1,134 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 为什么要做自动化测试?什么样的项目适合做自动化测试? + 在上一篇文章中,我为你介绍了什么是单元测试,以及如何做好单元测试,今天我来跟你聊聊什么是自动化测试,为什么要做自动化测试,以及什么样的项目适合做自动化测试。 + +什么是自动化测试? + +不管你是刚入行的小白,还是已经在做软件测试的工作,相信你一定听说过或者接触过自动化测试。那么,自动化测试到底是什么意思呢? + +顾名思义,自动化测试是,把人对软件的测试行为转化为由机器执行测试行为的一种实践,对于最常见的GUI自动化测试来讲,就是由自动化测试工具模拟之前需要人工在软件界面上的各种操作,并且自动验证其结果是否符合预期。 + +你是不是有点小激动?这似乎开启了用机器代替重复手工劳动的自动化时代,你可以从简单重复劳动中解放出来了。但现实呢? + +自动化测试的本质是先写一段代码,然后去测试另一段代码,所以实现自动化测试用例本身属于开发工作,需要投入大量的时间和精力,并且已经开发完成的用例还必须随着被测对象的改变而不断更新,你还需要为此付出维护测试用例的成本。 + +当你发现自动化测试用例的维护成本高于其节省的测试成本时,自动化测试就失去了价值与意义,你也就需要在是否使用自动化测试上权衡取舍了。 + +为什么需要自动化测试? + +为了让你更好地理解自动化测试的价值,即为什么需要自动化测试,我先来跟你聊聊自动化测试通常有哪些优势: + + +自动化测试可以替代大量的手工机械重复性操作,测试工程师可以把更多的时间花在更全面的用例设计和新功能的测试上; + +自动化测试可以大幅提升回归测试的效率,非常适合敏捷开发过程; + +自动化测试可以更好地利用无人值守时间,去更频繁地执行测试,特别适合现在非工作时间执行测试,工作时间分析失败用例的工作模式; + +自动化测试可以高效实现某些手工测试无法完成或者代价巨大的测试类型,比如关键业务7×24小时持续运行的系统稳定性测试和高并发场景的压力测试等; + +自动化测试还可以保证每次测试执行的操作以及验证的一致性和可重复性,避免人为的遗漏或疏忽。 + + +而为了避免对自动化测试的过度依赖,你还需要了解自动化测试有哪些劣势,这将帮你绕过实际工作中的“坑”。 + + +自动化测试并不能取代手工测试,它只能替代手工测试中执行频率高、机械化的重复步骤。你千万不要奢望所有的测试都自动化,否则一定会得不偿失。 + +自动测试远比手动测试脆弱,无法应对被测系统的变化,业界一直有句玩笑话“开发手一抖,自动化测试忙一宿”,这也从侧面反映了自动化测试用例的维护成本一直居高不下的事实。- +其根本原因在于自动化测试本身不具有任何“智能”,只是按部就班地执行事先定义好的测试步骤并验证测试结果。对于执行过程中出现的明显错误和意外事件,自动化测试没有任何处理能力。 + +自动化测试用例的开发工作量远大于单次的手工测试,所以只有当开发完成的测试用例的有效执行次数大于等于5次时,才能收回自动化测试的成本。 + +手工测试发现的缺陷数量通常比自动化测试要更多,并且自动化测试仅仅能发现回归测试范围的缺陷。 + +测试的效率很大程度上依赖自动化测试用例的设计以及实现质量,不稳定的自动化测试用例实现比没有自动化更糟糕。 + +实行自动化测试的初期,用例开发效率通常都很低,大量初期开发的用例通常会在整个自动化测试体系成熟,和测试工程师全面掌握测试工具后,需要重构。 + +业务测试专家和自动化测试专家通常是两批人,前者懂业务不懂自动化技术,后者懂自动化技术但不懂业务,只有二者紧密合作,才能高效开展自动化测试。 + +自动化测试开发人员必须具备一定的编程能力,这对传统的手工测试工程师会是一个挑战。 + + +什么样的项目适合自动化测试? + +看到这里,你心里可能在暗自嘀咕,“有没有搞错啊,自动化测试的劣势居然比优势还多”。那为什么还有那么多的企业级项目在实行自动化测试呢?那么,我接下来要讲的内容就是,到底什么样的项目适合自动化测试? + +第一,需求稳定,不会频繁变更。 + +自动化测试最怕的就是需求不稳定,过高的需求变更频率会导致自动化测试用例的维护成本直线上升。刚刚开发完成并调试通过的用例可能因为界面变化,或者是业务流程变化,不得不重新开发调试。所以自动化测试更适用于需求相对稳定的软件项目。 + +第二,研发和维护周期长,需要频繁执行回归测试。 + +1. 在我看来,软件产品比软件项目更适合做自动化测试。 + +首先,软件产品的生命周期一般都比较长,通常会有多个版本陆续发布,每次版本发布都会有大量的回归测试需求。 + +同时,软件产品预留给自动化测试开发的时间也比较充裕,可以和产品一起迭代。 + +其次,自动化测试用例的执行比高于1:5,即开发完成的用例至少可以被有效执行5次以上时,自动化测试的优势才可以被更好地体现。 + +2. 对于软件项目的自动化测试,就要看项目的具体情况了。 + +如果短期的一次性项目,就算从技术上讲自动化测试的可行性很高,但从投入产出比(ROI)的角度看并不建议实施自动化,因为千辛万苦开发完成的自动化用例可能执行一两次,项目就结束了。我还遇到过更夸张的情况,自动化测试用例还没开发完,项目都已经要上线了。 + +所以,对于这种短期的一次性项目,我觉得你应该选择手工探索式测试,以发现缺陷为第一要务。而对于一些中长期项目,我的建议是:对比较稳定的软件功能进行自动化测试,对变动较大或者需求暂时不明确的功能进行手工测试,最终目标是用20%的精力去覆盖80%的回归测试。 + +第三,需要在多种平台上重复运行相同测试的场景。 + +这样的场景其实有很多,比如: + + +对于GUI测试,同样的测试用例需要在多种不同的浏览器上执行; +对于移动端应用测试,同样的测试用例需要在多个不同的Android或者iOS版本上执行,或者是同样的测试需要在大量不同的移动终端上执行; +对于一些企业级软件,如果对于不同的客户有不同的定制版本,各个定制版本的主体功能绝大多数是一致的,可能只有个别功能有轻微差别,测试也是需要覆盖每个定制版本的所有测试; +…… + + +这些都是自动化测试的最佳应用场景,因为单个测试用例都需要被反复执行多次,能够使自动化测试的投资回报率最大化。 + +第四,某些测试项目通过手工测试无法实现,或者手工成本太高。 + +对于所有的性能和压力测试,很难通过手工方式实现。 + +比如,某一个项目要求进行一万并发用户的基准性能测试(Benchmark test),难道你真的要找一万个用户按照你的口令来操作被测软件吗?又比如,对于7×24小时的稳定性测试,难道你也要找一批用户没日没夜地操作被测软件吗? + +这个时候,你就必须借助自动化测试技术了,用机器来模拟大量用户反复操作被测软件的场景。当然对于此类测试是不可能通过GUI操作来模拟大量用户行为的,你必须基于协议的自动化测试技术,这个我会在后续的性能测试章节详细叙述。 + +第五,被测软件的开发较为规范,能够保证系统的可测试性。 + +从技术上讲,如果要实现稳定的自动化测试,被测软件的开发过程就必须规范。比如,GUI上的控件命名如果没有任何规则可寻,就会造成GUI自动化的控件识别与定位不稳定,从而影响自动化测试的效率。 + +另外,某些用例的自动化必须要求开发人员在产品中预留可测试性接口,否则后续的自动化会很难开展。 + +比如,有些用户登录操作,需要图片验证码,如果开发人员没有提供绕开图片验证码的路径,那么自动化测试就必须借助光学字符识别(OCR)技术来对图片验证码进行模式识别,而它的设计初衷是为了防止机器人操作,可想而知OCR的识别率会很低,就会直接影响用例的稳定性。 + +第六,测试人员已经具备一定的编程能力。 + +如果测试团队的成员没有任何开发编程的基础,那你想要推行自动化测试就会有比较大的阻力。这个阻力会来自于两个方面: + + +前期的学习成本通常会比较大,很难在短期内对实际项目产生实质性的帮助,此时如果管理层对自动化测试没有正确的预期,很可能会叫停自动化测试; +测试工程师通常会非常热衷于学习使用自动化测试技术,以至于他们的工作重点会发生错误的偏移,把大量的精力放在自动化测试技术的学习与实践上,而忽略了测试用例的设计,这将直接降低软件整体的质量。 + + +总结 + +自动化测试是,把人工对软件的测试转化为由机器执行测试行为的一种实践,可以把测试工程师从机械重复的测试工作中解脱出来,将更多的精力放在新功能的测试和更全面的测试用例设计上。 + +然而自动化测试试一把“双刃剑”,虽然它可以从一定程度上解放测试工程师的劳动力,完成一些人工无法实现的测试,但并不适用于所有的测试场景,如果维护自动化测试的代价高过了节省的测试成本,那么在这样的项目中推进自动化测试就会得不偿失。 + +思考题 + +你在实际项目中接触过哪些自动化测试,自动化测试用例的执行比通常是多少?如果执行比过低,需要频繁更新测试用例,那你思考过你的项目是否真的适合自动化测试吗?或者说,这个项目的哪些部分更适合实施自动化测试? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/05你知道软件开发各阶段都有哪些自动化测试技术吗?.md b/专栏/软件测试52讲/05你知道软件开发各阶段都有哪些自动化测试技术吗?.md new file mode 100644 index 0000000..8fa83f8 --- /dev/null +++ b/专栏/软件测试52讲/05你知道软件开发各阶段都有哪些自动化测试技术吗?.md @@ -0,0 +1,162 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 你知道软件开发各阶段都有哪些自动化测试技术吗? + 在前面的文章中,我介绍了为什么要做自动化测试,以及什么样的项目适合做自动化测试,那么现在我来说说软件开发生命周期的各个阶段都有哪些类型的自动化测试技术。 + +说到自动化测试,你可能最为熟悉的就是GUI自动化测试了。比如,早年的C/S架构,通常就是用自动化测试脚本打开被测应用,然后在界面上以自动化的方式执行一系列的操作;再比如,现今的Web站点测试,也是用自动化测试脚本打开浏览器,然后输入要访问的网址,之后用自动化脚本识别定位页面元素,并进行相应的操作。 + +因此,说到自动化测试时,你的第一反应很可能就是GUI自动化测试。然而,在软件研发生命周期的各个阶段都有自动化测试技术的存在,并且对提升测试效率有着至关重要的作用。 + +今天这篇文章,我将会以不同的软件开发阶段涉及的自动化测试技术为主线,带你了解单元测试、代码级集成测试、Web Service测试和GUI测试阶段的自动化技术,希望可以帮助你更深入地理解“自动化测试”的内涵以及外延。 + +单元测试的自动化技术 + +首先,你可能认为单元测试本身就是自动化的,因为它根据软件详细设计采用等价类划分和边界值分析方法设计测试用例,在测试代码实现后再以自动化的方式统一执行。 + +这个观点非常正确,但这仅仅是一部分,并没有完整地描述单元测试“自动化”的内涵。从广义上讲,单元测试阶段的“自动化”内涵不仅仅指测试用例执行的自动化,还应该包含以下五个方面: + + +用例框架代码生成的自动化; +部分测试输入数据的自动化生成; +自动桩代码的生成; +被测代码的自动化静态分析; +测试覆盖率的自动统计与分析。 + + +你可能感觉这些内容有些陌生,不过没关系,下面我就详细地跟你说说每一条的具体含义。 + +第一,用例框架代码生成的自动化 + +有些框架代码应该由自动化工具生成,而不是由开发者手工完成。这样一来,单元测试开发者可以把更多的精力放在测试逻辑的覆盖和测试数据的选择上,从而大幅提高单元测试用例的质量和开发效率。 + +- +TestNG框架代码应该由自动化工具生成 + +第二,部分测试输入数据的自动化生成 + +这部分是指,自动化工具能够根据不同变量类型自动生成测试输入数据。自动化工具本身不可能明白代码逻辑,你可能很难理解它是如何根据需要测试的代码逻辑生成合适的输入数据,并且去判断预计的测试结果的。那我给你举个例子,你就很容易明白了。 + +比如,某个被测函数的原型是void fun(int* p, short b),那么测试数据自动生成技术就会为输入参数int* p自动生成“空”和“非空”的两个指针p,然后分别执行函数void fun(int* p, short b),并观察函数的执行情况。 + +如果函数内部没有对空指针进行特殊处理,那么函数fun的调用必定会抛出异常,从而发现函数的设计缺陷。同样地,对于输入参数short b会自动生成超出short范围的b,测试函数fun的行为。 + +第三,自动桩代码的生成 + +简单地说,桩代码(stub code)是用来代替真实代码的临时代码。 比如,某个函数A的内部实现中调用了一个尚未实现的函数B,为了对函数A的逻辑进行测试,那么就需要模拟一个函数B,这个模拟的函数B实现就是所谓的桩代码。 + +自动桩代码的生成是指自动化工具可以对被测试代码进行扫描分析,自动为被测函数内部调用的其他函数生成可编程的桩代码,并提供基于测试用例的桩代码管理机制。此时,单元测试开发者只需重点关注桩代码内的具体逻辑实现,以及桩代码的返回值。 + +必要的时候,自动化工具还需要实现 “抽桩”,以适应后续的代码级集成测试的需求。 + +那什么是“抽桩”呢?其实也很简单,在单元测试阶段,假如函数A内部调用的函数B是桩代码,那么在代码级集成测试阶段,我们希望函数A不再调用假的函数B,而是调用真实的函数B,这个用真实函数B代替原本桩代码函数B的操作,就称为“抽桩”。 + +第四,被测代码的自动化静态分析 + +静态分析主要指代码的静态扫描,目的是识别出违反编码规则或编码风格的代码行。通常这部分工作是结合项目具体的编码规则和编码风格,由自动化工具通过内建规则和用户自定义规则自动化完成的。目前比较常用的代码静态分析工具有Sonar和Coverity等。 + +严格意义上讲,静态分析不属于单元测试的范畴,但这部分工作一般是在单元测试阶段通过自动化工具完成的,所以我也把它归入到了单元测试自动化的范畴。 + +第五,测试覆盖率的自动统计与分析 + +单元测试用例执行结束后,自动化工具可以自动统计各种测试覆盖率,包括代码行覆盖率、分支覆盖率、MC/DC覆盖率等。这些自动统计的指标,可以帮你衡量单元测试用例集合的充分性和完备性,并可以为你提供适当增补测试用例以提高测试覆盖率的依据。 + +代码级集成测试的自动化技术 + +通俗地讲,代码级集成测试是指将已经开发完成的软件模块放在一起测试。 + +从测试用例设计和测试代码结构来看,代码级集成测试和单元测试非常相似,它们都是对被测试函数以不同的输入参数组合进行调用并验证结果,只不过代码级集成测试的关注点,更多的是软件模块之间的接口调用和数据传递。 + +代码级集成测试与单元测试最大的区别只是,代码级集成测试中被测函数内部调用的其他函数必须是真实的,不允许使用桩代码代替,而单元测试中允许使用桩代码来模拟内部调用的其他函数。 + +以上的这些异同点就决定了代码级集成测试“自动化”的内涵与单元测试非常相似,尤其是在实际操作层面,比如测试用例的设计方法、测试用例的代码结构以及数据驱动思想的应用等等。 + +但是,代码级集成测试对测试框架的要求非常高,这个框架除了可以顺利装载自己的软件模块外,还必须能装载其他相互依赖的模块,做到被测软件模块可运行(Runnable)。 + +由于代码级集成测试主要应用在早期非互联网的传统软件企业,那时候的软件以“单体”应用居多,一个软件内部包含大量的功能,每一个软件功能都是通过不同的内部模块来实现的,那么这些内部模块在做集成的时候,就需要做代码级集成测试。 + +现在的开发理念追求的是系统复杂性的解耦,会去尽量避免“大单体”应用,采用Web Service或者RPC调用的方式来协作完成各个软件功能。所以现在的软件企业,尤其是互联网企业,基本不会去做代码级集成测试,我在这里也就不再进一步展开了。 + +Web Service测试的自动化技术 + +Web Service测试,主要是指SOAP API和REST API这两类API测试,最典型的是采用SoapUI或Postman等类似的工具。但这类测试工具基本都是界面操作手动发起Request并验证Response,所以难以和CI/CD集成,于是就出现了API自动化测试框架。 + +如果采用API自动化测试框架来开发测试用例,那么这些测试用例的表现形式就是代码。为了让你更直观地理解基于代码的API测试用例是什么样子的,我给你举一个“创建用户”API的例子,你只需要看代码的大致步骤就可以了,具体到每行代码的含义,我会在后续文章中详细讲解。 + +- +基于API自动化测试框架的测试用例示例(测试CreateUser API) + +对于基于代码的API测试用例,通常包含三大步骤: + + +准备API调用时需要的测试数据; +准备API的调用参数并发起API的调用; +验证API调用的返回结果。 + + +目前最流行的API自动测试框架是REST Assured,它可以方便地发起Restful API调用并验证返回结果。 + +同样地,Web Service测试“自动化”的内涵不仅仅包括API测试用例执行的自动化,还包括以下四个方面: + + +测试脚手架代码的自动化生成; +部分测试输入数据的自动生成; +Response验证的自动化; +基于SoapUI或者Postman的自动化脚本生成。 + + +接下来,我会依次为你解释这4个方面代表什么含义。 + +第一,测试脚手架代码的自动化生成- +和单元测试阶段的用例框架代码自动生成一个道理,你在开发API测试的过程中更关心的是,如何设计测试用例的输入参数以及组合,以及在不同参数组合情况下Response的验证,而你不希望将精力浪费在代码层面如何组织测试用例、测试数据驱动如何实现等非测试业务上。 + +这时,测试脚手架代码的自动生成技术就派上用场了。它生成的测试脚手架代码,通常包含了被测试API的调用、测试数据与脚本的分离,以及Response验证的空实现。 + +第二,部分测试输入数据的自动生成 + +这一点和单元测试的测试输入数据的自动化生成也很类似,唯一不同的是,单元测试针对的参数是函数输入参数和函数内部输入,而API测试对应的是API的参数以及API调用的Payload。数据生成的原则同样遵循边界值原则。 + +第三,Response验证的自动化 + +对于API调用返回结果的验证,通常关注的点是返回状态码(status code)、Scheme结构以及具体的字段值。如果你写过这种类型的测试用例,那你就会知道字段值的验证相当麻烦,只有那些你明确写了assert的字段才会被验证,但是通常你不可能针对所有的字段都写assert,这时就需要Response验证的自动化技术了。 + +Response验证自动化的核心思想是自动比较两次相同API调用的返回结果,并自动识别出有差异的字段值,比较过程可以通过规则配置去掉诸如时间戳、会话ID(Session ID)等动态值。 这部分内容,我会在后续文章中详细讲解。 + +第四,基于SoapUI或者Postman的自动化脚本生成 + +你在使用SoapUI或者Postman等工具进行Web Service测试时,已经在这些工具里面积累了很多测试用例。那么,在引入了基于代码实现的API测试框架之后,就意味着需要把这些测试用例都用代码的方式重写一遍,而这额外的工作量是很难被接受的。 + +我的建议是,开发一个自动化代码转换生成工具。这个工具的输入是SoapUI或者Postman的测试用例元数据(即测试用例的JSON元文件),输出是符合API测试框架规范的基于代码实现的测试用例。这样一来,原本的测试用例积累可以直接转换成在CI/CD上可以直接接入的自动化测试用例。 + +对于新的测试用例,还可以继续用SoapUI或者Postman做初步的测试验证,初步验证没有问题后,直接转换成符合API测试框架规范的测试用例。对于复杂的测试用例,也可以直接基于代码来实现,而且灵活性会更好。 + +GUI测试的自动化技术 + +GUI测试的自动化技术可能是你最熟悉的,也是发展时间最长、应用最广的自动化测试技术。它的核心思想是,基于页面元素识别技术,对页面元素进行自动化操作,以模拟实际终端用户的行为并验证软件功能的正确性。 + +目前,GUI自动化测试主要分为两大方向,传统Web浏览器和移动端原生应用(Native App)的GUI自动化。虽然二者采用的具体技术差别很大,但是用例设计的思路类似。 + + +对于传统Web浏览器的GUI自动化测试,业内主流的开源方案采用Selenium,商业方案采用Micro Focus的UFT(前身是HP的QTP); +对于移动端原生应用,通常采用主流的Appium,它对iOS环境集成了XCUITest,对Android环境集成了UIAutomator和Espresso。 + + +这部分内容,我会在后续的文章中详细展开。 + +总结 + +我给你梳理了软件研发生命周期各个阶段的自动化测试技术,包括单元测试、代码级集成测试、Web Service测试和GUI测试的自动化技术,并给你归纳了每一类技术的核心方法和应用场景。 + +我希望你通过这篇文章,可以先对自动化测试的全局有一个比较清晰的认识,然后在后续的文章中我还会针对这些技术展开讨论,并给你分享一些相应的实际案例。 + +思考题 + +你现在所在的公司,是否实行代码级测试,用到了哪些自动化测试技术? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/06你真的懂测试覆盖率吗?.md b/专栏/软件测试52讲/06你真的懂测试覆盖率吗?.md new file mode 100644 index 0000000..3437e26 --- /dev/null +++ b/专栏/软件测试52讲/06你真的懂测试覆盖率吗?.md @@ -0,0 +1,141 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 你真的懂测试覆盖率吗? + 在上一篇文章中,我为你介绍了软件测试各个阶段的自动化技术,在前面的文章中我也提到了测试覆盖率的概念,你当时可能有点不明白,那么今天我就和你详细聊聊测试覆盖率这个主题。 + +测试覆盖率通常被用来衡量测试的充分性和完整性,从广义的角度来讲,测试覆盖率主要分为两大类,一类是面向项目的需求覆盖率,另一类是更偏向技术的代码覆盖率。 + +需求覆盖率 + +需求覆盖率是指测试对需求的覆盖程度,通常的做法是将每一条分解后的软件需求和对应的测试建立一对多的映射关系,最终目标是保证测试可以覆盖每个需求,以保证软件产品的质量。 + +我们通常采用ALM,Doors和TestLink等需求管理工具来建立需求和测试的对应关系,并以此计算测试覆盖率。 + +需求覆盖率统计方法属于传统瀑布模型下的软件工程实践,传统瀑布模型追求自上而下地制定计划、分析需求、设计软件、编写代码、测试和运维等,在流程上是重量级的,已经很难适应当今互联网时代下的敏捷开发实践。 + +所以,互联网测试项目中很少直接基于需求来衡量测试覆盖率,而是将软件需求转换成测试需求,然后基于测试需求再来设计测试点。 + +因此,现在人们口中的测试覆盖率,通常默认指代码覆盖率,而不是需求覆盖率。 + +代码覆盖率 + +简单来说,代码覆盖率是指,至少被执行了一次的条目数占整个条目数的百分比。 + +如果“条目数”是语句,对应的就是代码行覆盖率;如果“条目数”是函数,对应的就是函数覆盖率;如果“条目数”是路径,那么对应的就是路径覆盖率。依此类推,你就可以得到绝大多数常见的代码覆盖率类型的定义。 + +这里我给你简单介绍一下最常用的三种代码覆盖率指标。 + + +行覆盖率又称为语句覆盖率,指已经被执行到的语句占总可执行语句(不包含类似C++的头文件声明、代码注释、空行等等)的百分比。这是最常用也是要求最低的覆盖率指标。实际项目中通常会结合判定覆盖率或者条件覆盖率一起使用。 +判定覆盖又称分支覆盖,用以度量程序中每一个判定的分支是否都被测试到了,即代码中每个判断的取真分支和取假分支是否各被覆盖至少各一次。比如,对于if(a>0 && b>0),就要求覆盖“a>0 && b>0”为TURE和FALSE各一次。 +条件覆盖是指,判定中的每个条件的可能取值至少满足一次,度量判定中的每个条件的结果TRUE和FALSE是否都被测试到了。比如,对于if(a>0 && b>0),就要求“a>0”取TRUE和FALSE各一次,同时要求“b>0”取TRUE和FALSE各一次。 + + +代码覆盖率的价值 + +现在很多项目都在单元测试以及集成测试阶段统计代码覆盖率,但是我想说的是,统计代码覆盖率仅仅是手段,你必须透过现象看到事物的本质,才能从根本上保证软件整体的质量。 + +统计代码覆盖率的根本目的是找出潜在的遗漏测试用例,并有针对性的进行补充,同时还可以识别出代码中那些由于需求变更等原因造成的不可达的废弃代码。 + +通常我们希望代码覆盖率越高越好,代码覆盖率越高越能说明你的测试用例设计是充分且完备的,但你也会发现测试的成本会随着代码覆盖率的提高以类似指数级的方式迅速增加。 + +如果想达到70%的代码覆盖率,你可能只需要30分钟的时间成本。但如果你想把代码覆盖率提高到90%,那么为了这额外的20%,你可能花的时间就远不止30分钟了。更进一步,你如果想达到100%的代码覆盖率,可想而知你花费的代价就会更大了。 + +那么,为什么代码覆盖率的提高,需要付出越来越大的代价呢?因为在后期,你需要大量的桩代码、Mock代码和全局变量的配合来控制执行路径。 + +所以,在软件企业中,只有单元测试阶段对代码覆盖率有较高的要求。因为从技术实现上讲,单元测试可以最大化地利用打桩技术来提高覆盖率。而你如果想在集成测试或者是GUI测试阶段将代码覆盖率提高到一定量级,那你所要付出的代价是巨大的,而且在很多情况下根本就实现不了。 + +代码覆盖率的局限性 + +我先来问你一个问题,如果你通过努力,已经把某个函数的MC/DC代码覆盖率(MC/DC覆盖率是最高标准的代码覆盖率指标,除了直接关系人生命安全的软件以外,很少会有项目会有严格的MC/DC覆盖率要求)做到了100%,软件质量是否就真的高枕无忧、万无一失了呢? + +很不幸,即使你所设计的测试用例已经达到100%的代码覆盖率,软件产品的质量也做不到万无一失。其根本原因在于代码覆盖率的计算是基于现有代码的,并不能发现那些“未考虑某些输入”以及“未处理某些情况”形成的缺陷。 + +我给你举个极端的例子,如果一个被测函数里面只有一行代码,只要这个函数被调用过了,那么衡量这一行代码质量的所有覆盖率指标都会是100%,但是这个函数是否真正实现了应该需要实现的功能呢? + +显然,代码覆盖率反映的仅仅是已有代码的哪些逻辑被执行过了,哪些逻辑还没有被执行过。以此为依据,你可以补充测试用例,可以去测试那些还没有覆盖到的执行路径。但也是仅此而已,对于那些压根还没有代码实现的部分,基于代码覆盖率的统计指标就无能为力了。 + +总结来讲,高的代码覆盖率不一定能保证软件的质量,但是低的代码覆盖率一定不能能保证软件的质量。 + +好了,现在你已经了解了代码覆盖率的概念、价值和局限性,那么接下来,我就以Java代码覆盖率工具为例,给你解释一下代码覆盖率工具的内部实现原理以及一些关键技术。 + +当你理解了这部分内容,以后再面对各个不同开发语言的不同代码覆盖率工具时,就可以做到胸有成竹地根据具体的项目性质,选择最合适的代码覆盖率工具了。 + +代码覆盖率工具 + +JaCoCo是一款Java代码的主流开源覆盖率工具,可以很方便地嵌入到Ant、Maven中,并且和很多主流的持续集成工具以及代码静态检查工具,比如Jekins和Sonar等,都有很好的集成。 + +首先,我先带你看看JaCoCo的代码覆盖率报告长什么样子。 + +如图1所示为JaCoCo的整体代码覆盖率统计报告,包括了每个Java代码文件的行覆盖率以及分支覆盖率统计,并给出了每个Java代码文件的行数、方法数和类数等具体信息。 + +- +图1 JaCoCo代码覆盖率统计报告实例 + +如图2所示为每个Java文件内部详细的代码覆盖率情况,图中绿色的行表示已经被覆盖,红色的行表示尚未被覆盖,黄色的行表示部分覆盖;左侧绿色菱形块表示该分支已经被完全覆盖、黄色菱形块表示该分支仅被部分覆盖。 + + + +图2 JaCoCo详细代码覆盖率实例 + +显然,通过这个详尽的报告,你就可以知道代码真实的执行情况、哪些代码未被覆盖。以此为基础,你再去设计测试用例就会更有针对性了。 + +代码覆盖率工具的实现原理 + +JaCoCo的详细报告,让你惊叹于代码覆盖率工具的强大。但你有没有仔细想过,这样的统计信息如何被获取到的呢? + +- +图3 统计代码覆盖率的不同注入实现技术 + +实现代码覆盖率的统计,最基本的方法就是注入(Instrumentation)。简单地说,注入就是在被测代码中自动插入用于覆盖率统计的探针(Probe)代码,并保证插入的探针代码不会给原代码带来任何影响。 + +对于Java代码来讲,根据注入目标的不同,可以分为源代码(Source Code)注入和字节码(Byte Code)注入两大类。基于JVM本身特性以及执行效率的原因,目前主流的工具基本都是使用字节码注入,注入的具体实现采用ASM技术。 + +ASM是一个Java字节码操纵框架,能被用来动态生成类或者增强既有类的功能,可以直接产生 class 文件,也可以在类被加载入JVM之前动态改变类行为。 + +根据注入发生的时间点,字节码注入又可以分为两大模式:On-The-Fly注入模式和Offline注入模式。 + +第一,On-The-Fly注入模式 + +On-The-Fly模式的特点在于无需修改源代码,也无需提前进行字节码插桩。它适用于支持Java Agent的运行环境。 + +这样做的优点是,可以在系统不停机的情况下,实时收集代码覆盖率信息。缺点是运行环境必须允许使用Java Agent。 + +实现On-The-Fly模式,主要有两种技术方案: + + +开发自定义的类装载器(Class Loader)实现类装载策略,每次类加载前,需要在class文件中插入探针,早期的Emma就是使用这种方案实现的探针插入; + +借助Java Agent,利用执行在main()方法之前的拦截器方法premain()来插入探针,实际使用过程中需要在JVM的启动参数中添加“-javaagent”并指定用于实时字节码注入的代理程序,这样代理程序在装载每个class文件前,先判断是否已经插入了探针,如果没有则需要将探针插入class文件中,目前主流的JaCoCo就是使用了这个方式。 + + +第二,Offline注入模式 + +Offline模式也无需修改源代码,但是需要在测试开始之前先对文件进行插桩,并事先生成插过桩的class文件。它适用于不支持Java Agent的运行环境,以及无法使用自定义类装载器的场景。 + +这样做的优点是,JVM启动时不再需要使用Java Agent额外开启代理,缺点是无法实时获取代码覆盖率信息,只能在系统停机时下获取。 + +Offline模式根据是生成新的class文件还是直接修改原class文件,又可以分为Replace和Inject两种不同模式。 + +和On-The-Fly注入模式不同,Replace和Inject的实现是,在测试运行前就已经通过ASM将探针插入了class文件,而在测试的运行过程中不需要任何额外的处理。Cobertura就是使用Offline模式的典型代表。 + +总结 + +测试覆盖率通常被用来衡量测试的充分性和完整性,包括面向项目的需求覆盖率和更偏向技术的代码覆盖率。而需求覆盖率的统计方式不再适用于现在的敏捷开发模式,所以现在谈到测试覆盖率,大多是指代码覆盖率。 + +但是,高的代码覆盖率不一定能保证软件的质量,因为代码覆盖率是基于现有代码,无法发现那些“未考虑某些输入”以及“未处理某些情况”形成的缺陷。 + +另外,对于代码覆盖率的统计工具,我希望你不仅仅是会用的层次,而是能够理解它们的原理,知其然知其所以然,才能更好地利用这些工具完成你的测试工作。 + +思考题 + +你在实际工作中,是否还接触过C/C++,JavaScript等语言的代码覆盖率工具,比如GCC Coverage、JSCoverage和Istanbul等?如果接触过的话,请你谈谈自己使用的感受以及遇到过的“坑”。 + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/07如何高效填写软件缺陷报告?.md b/专栏/软件测试52讲/07如何高效填写软件缺陷报告?.md new file mode 100644 index 0000000..9b1eb62 --- /dev/null +++ b/专栏/软件测试52讲/07如何高效填写软件缺陷报告?.md @@ -0,0 +1,157 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 如何高效填写软件缺陷报告? + 在上一篇文章中,我为你介绍了测试覆盖率的概念,并重点介绍了代码覆盖率的应用价值以及局限性。今天我会为你介绍如何才能写出一份高效的软件缺陷报告。 + +测试工程师需要利用对需求的理解、高效的执行力以及严密的逻辑推理能力,迅速找出软件中的潜在缺陷,并以缺陷报告的形式递交给开发团队,这看起来是不是有点像侦探柯南呢。 + +缺陷报告是测试工程师与开发工程师交流沟通的重要桥梁,也是测试工程师日常工作的重要输出。 作为优秀的测试工程师,最基本的一项技能就是,把发现的缺陷准确无歧义地表达清楚。 + +“准确无歧义地表达”意味着,开发工程师可以根据缺陷报告快速理解缺陷,并精确定位问题。同时,通过这个缺陷报告,开发经理可以准确预估缺陷修复的优先级、产品经理可以了解缺陷对用户或业务的影响以及严重性。 + +可见,缺陷报告本身的质量将直接关系到缺陷被修复的速度以及开发工程师的效率,同时还会影响测试工程师的信用、测试与开发人员协作的有效性。 + +那么,如何才能写出一份高效的缺陷报告呢?或者说,一份好的缺陷报告需要包括哪些具体内容呢? + +你可能觉得这并不是什么难事儿,毕竟软件企业通常都有缺陷管理系统,比如典型的ALM(以前的Quality Center)、JIRA、Bugzilla、BugFree和Mantis等。当使用这类系统递交缺陷时,会自动生成模板,你只要按照其中的必填字段提供缺陷的详细信息就可以了。 + +很多时候,你不用想应该提供些什么信息,系统会引导你提供相关的信息。但是,你有仔细想过为什么要填写这些字段,这些字段都起什么作用,以及每个字段的内容具体应该怎么填写吗? + +你必须牢牢记住的是,好的缺陷报告绝对不是大量信息的堆叠,而是以高效的方式提供准确有用的信息。 + +接下来,我就带你一起去看一份高效的缺陷报告主要由哪些部分组成,以及每部分内容的关键点是什么。 + +缺陷标题 + +缺陷标题通常是别人最先看到的部分,是对缺陷的概括性描述,通常采用“在什么情况下发生了什么问题”的模式。 + +首先,对“什么问题”的描述不仅要做到清晰简洁,最关键是要足够具体,切忌不能采用过于笼统的描述。描述“什么问题”的同时还必须清楚地表述发生问题时的上下文,也就是问题出现的场景。 + +“用户不能正常登录”“搜索功能有问题”和“用户信息页面的地址栏位置不正确”等,这样的描述会给人“说了等于没说”的感觉。这样的描述,很容易引发开发工程师的反感和抵触情绪,从而造成缺陷被拒绝修改(reject)。同时,还会造成缺陷管理上的困难以及过程的低效。 + +比如,当你发现了一个菜单栏上某个条目缺失的问题,在递交缺陷报告前,通常会去缺陷管理系统搜索一下是否已经有人递交过类似的缺陷。 + +当你以“菜单栏”为关键字搜索时,你可能会得到一堆“菜单栏有问题”的缺陷,如果缺陷标题的描述过于笼统,你就不得不点击进入每个已知缺陷点去看细节描述,这就会大大降低你的工作效率。 + +所以,如果缺陷标题本身就能概括性地描述具体问题,你就可以通过阅读标题判断类似的缺陷是否被提交过,大大提高测试工程师提交缺陷报告的效率。 + +其次,标题应该尽可能描述问题本质,而避免只停留在问题的表面。 + +比如,“商品金额输入框,可以输入英文字母和其他字符”这个描述就只描述了问题的表面现象,而采用诸如“商品金额输入框,没有对输入内容做校验”的方式,就可以透过标题看到缺陷的本质,这样可以帮助开发人员快速掌握问题的本质。 + +最后,缺陷标题不易过长,对缺陷更详细的描述应该放在“缺陷概述”里。 + +缺陷概述 + +缺陷概述通常会提供更多概括性的缺陷本质与现象的描述,是缺陷标题的细化。这部分内容通常是开发工程师打开缺陷报告后最先关注的内容,所以用清晰简短的语句将问题的本质描述清楚是关键。 + +缺陷概述还会包括缺陷的其他延展部分,比如你可以在这部分列出同一类型的缺陷可能出现的所有场景;再比如,你还可以描述同样的问题是否会在之前的版本中重现等。在这里,你应该尽量避免以缺陷重现步骤的形式来描述,而应该使用概括性的语句。 + +总之,缺陷概述的目的是,清晰简洁地描述缺陷,使开发工程师能够聚焦缺陷的本质。 + +缺陷影响 + +缺陷影响描述的是,缺陷引起的问题对用户或者对业务的影响范围以及严重程度。 + +缺陷影响决定了缺陷的优先级(Priority)和严重程度(Severity),开发经理会以此为依据来决定修复该缺陷的优先级;而产品经理会以此为依据来衡量缺陷的严重程度,并决定是否要等该缺陷被修复后才能发布产品。 + +测试工程师准确描述缺陷影响的前提是,必须对软件的应用场景以及需求有深入的理解,这也是对测试工程师业务基本功的考验。 + +环境配置 + +环境配置用以详细描述测试环境的配置细节,为缺陷的重现提供必要的环境信息。 比如,操作系统的类型与版本、被测软件版本、浏览器的种类和版本、被测软件的配置信息、集群的配置参数、中间件的版本信息等等。 + +需要注意的是,环境配置的内容通常是按需描述,也就是说通常只描述那些重现缺陷的环境敏感信息。 + +比如,“菜单栏上某个条目缺失的问题”只会发生在Chrome浏览器,而其他浏览器都没有类似问题。那么,Chrome浏览器就是环境敏感信息,必须予以描述,而至于Chrome浏览器是运行在什么操作系统上就无关紧要了,无需特意去描述了。 + +前置条件 + +前置条件是指测试步骤开始前系统应该处在的状态,其目的是减少缺陷重现步骤的描述。合理地使用前置条件可以在描述缺陷重现步骤时排除不必要的干扰,使其更有针对性。 + +比如,某个业务操作需要先完成用户登录,你在缺陷重现步骤里就没有必要描述登录操作的步骤细节,可以直接使用 “前置条件:用户已完成登录”的描述方式; + +再比如,用户在执行登录操作前,需要事先在被测系统准备好待登录用户,你在描述时也无需增加“用测试数据生成工具生成用户”的步骤,可以直接使用 “前置条件:用户已完成注册”的描述方式。 + +缺陷重现步骤 + +缺陷重现步骤是整个缺陷报告中最核心的内容,其目的在于用简洁的语言向开发工程师展示缺陷重现的具体操作步骤。 + +这里需要注意的是,操作步骤通常是从用户角度出发来描述的,每个步骤都应该是可操作并且是连贯的,所以往往会采用步骤列表的表现形式。 + +通常测试工程师在写缺陷重现步骤前,需要反复执行这些步骤3次以上:一是,确保缺陷的可重现性;二是,找到最短的重现路径,过滤掉那些非必要的步骤,避免产生不必要的干扰。 + +对于缺陷重现步骤的描述应该尽量避免以下3个常见问题: + + +笼统的描述,缺乏可操作的具体步骤。 + +出现与缺陷重现不相关的步骤。 + +缺乏对测试数据的相关描述。 + + +期望结果和实际结果 + +期望结果和实际结果通常和缺陷重现步骤绑定在一起,在描述重现步骤的过程中,需要明确说明期待结果和实际结果。期待结果来自于对需求的理解,而实际结果来自于测试执行的结果。 + +通常来讲,当你描述期望结果时,需要说明应该发生什么,而不是什么不应该发生;而描述实际结果时,你应该说明发生了什么,而不是什么没有发生。 + +优先级(Priority)和严重程度(Severity) + +我之所以将优先级和严重程度放在一起,是因为这两个概念看起来有点类似,而本质却完全不同。而且,很多入行不久的测试工程师,也很难搞清楚这两者的差异到底在哪里。 + +根据百度百科的解释,缺陷优先级是指缺陷必须被修复的紧急程度,而缺陷严重程度是指因缺陷引起的故障对软件产品的影响程度。 + +可见,严重程度是缺陷本身的属性,通常确定后就不再变化,而优先级是缺陷的工程属性,会随着项目进度、解决缺陷的成本等因素而变动。那么,缺陷的优先级和严重程度又有什么关系呢? + + +缺陷越严重,优先级就越高; + +缺陷影响的范围越大,优先级也会越高; + +有些缺陷虽然从用户影响角度来说不算严重,但是会妨碍测试或者是自动化测试的执行,这类缺陷属于典型的严重程度低,但是优先级高; + +有些缺陷虽然严重程度比较高,但是考虑到修复成本以及技术难度,也会出现优先级较低的情况。 + + +变通方案(Workaround) + +变通方案是提供一种临时绕开当前缺陷而不影响产品功能的方式,通常由测试工程师或者开发工程师完成,或者他们一同决定。 + +变通方案的有无以及实施的难易程度,是决定缺陷优先级和严重程度的重要依据。如果某个严重的缺陷没有任何可行的变通方案,那么不管修复缺陷代价有多大,优先级一定会是最高的,但是如果该缺陷存在比较简单的变通方案,那么优先级就不一定会是最高的了。 + +根原因分析(Root Cause Analysis) + +根原因分析就是我们平时常说的RCA,如果你能在发现缺陷的同时,定位出问题的根本原因,清楚地描述缺陷产生的原因并反馈给开发工程师,那么开发工程师修复缺陷的效率就会大幅提升,而且你的技术影响力也会被开发认可。 + +可以做好根原因分析的测试工程师,通常都具有开发背景,或者至少有较好的代码阅读以及代码调试的能力。 + +所以做为测试工程师,你很有必要去深入学习一门高级语言,这将帮助你体系化地建立起编程思想和方法,这样在之后的工作中,无论你是面对开发的代码,还是自动化测试代码和脚本都能做到得心应手,应对自如。 + +附件(Attachment) + +附件通常是为缺陷的存在提供必要的证据支持,常见的附件有界面截图、测试用例日志、服务器端日志、GUI测试的执行视频等。 + +对于那些很难用文字描述清楚的GUI界面布局的缺陷,你可以采用截图并高亮显示应该关注的区域的方式去提交缺陷报告。 + +总结 + +缺陷报告是测试工程师与开发工程师交流沟通的重要桥梁,也是测试工程师日常工作的重要输出。 + +一份高效的软件缺陷报告,应该包括缺陷标题、缺陷概述、缺陷影响、环境配置、前置条件、缺陷重现步骤、期望结果和实际结果、优先级和严重程度、变通方案、根原因分析,以及附件这几大部分。 + +缺陷报告的每一部分内容,都会因为目的、表现形式有各自的侧重点,所以想要写出一份高效的软件缺陷报告,需要对其组成有深入的理解。 + +思考题 + +关于高效填写软件缺陷报告,你还有哪些好的实践? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/08以终为始,如何才能做好测试计划?.md b/专栏/软件测试52讲/08以终为始,如何才能做好测试计划?.md new file mode 100644 index 0000000..f2a7105 --- /dev/null +++ b/专栏/软件测试52讲/08以终为始,如何才能做好测试计划?.md @@ -0,0 +1,168 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 以终为始,如何才能做好测试计划? + 在上一篇文章中,我为你介绍了如何高效填写软件缺陷报告,并为你解读了缺陷报告中的关键内容。今天,我将为你介绍一份成功的测试计划应该包含哪些内容,以及如何才能做好测试计划。 + +软件项目,通常都会有详细的项目计划。软件测试作为整个项目中的重要一环,也要执行详细的测试计划。正所谓运筹帷幄之中,决胜千里之外,强调的就是预先计划的重要性和必要性。 + +在早期的软件工程实践中,软件测试计划的制定通常是在需求分析以及测试需求分析完成后开始,并且是整个软件研发生命周期中的重要环节。 + +但是,在敏捷开发模式下,你可能会有这样的疑问,软件测试计划还有那么重要吗?我所在的软件项目压根儿就没有正式的测试计划,不也没出什么大问题吗? + +的确,对于很多非产品型的互联网公司,由于采用了敏捷开发模式,的确很少去制定传统意义上的测试计划了,但这并不是说它们就不再制定测试计划了。 + +只不过是,测试计划的表现形式已经不再是传统意义上庞大的、正式的测试计划文档了,而更多的是体现在每个迭代(sprint)的计划环节,而且这样的短期测试计划可以非常迅速地根据项目情况实时调整。 + +所以说,测试计划依旧存在,只是从原来的一次性集中制定测试计划,变成了以迭代的方式持续制定测试计划。 但是对于传统软件企业,或者是做非互联网软件产品的企业,它们通常还是会有非常正式的软件测试计划。 + +由此可见,无论对于早期最具典型性的瀑布开发模型,还是现在的敏捷开发模型,测试计划的重要性始终没有发生变化。那么,你可能会问,测试计划的重要性到底体现在哪些方面呢?在回答这个问题之前,我先跟你聊聊如果没有测试计划会带来什么问题。 + +没有测试计划会怎么样? + +如果没有测试计划,会带来哪些问题呢? + + +很难确切地知道具体的测试范围,以及应该采取的具体测试策略; + +很难预估具体的工作量和所需要的测试工程师数量,同时还会造成各个测试工程师的分工不明确,引发某些测试工作被重复执行而有些测试则被遗漏的问题; + +测试的整体进度完全不可控,甚至很难确切知道目前测试的完成情况,对于测试完成时间就更难预估准确的时间节点了; + +整个项目对潜在风险的抵抗能力很弱,很难应对需求的变更以及其他突发事件。 + + +从这些问题中,你可以逆向思维推导出,一份好的测试计划要包括:测试范围、测试策略、测试资源、测试进度和测试风险预估,这五大方面,并且每一部分都要给出应对可能出现问题的解决办法。 + +测试范围 + +顾名思义,测试范围描述的是被测对象以及主要的测试内容。 + +比如,对于用户登录模块,功能测试既需要测试浏览器端又需要测试移动端,同时还考虑登录的安全和并发性能相关的非功能性需求的测试等。 + +测试范围的确定通常是在测试需求分析完成后进行,所以确定测试范围的过程在一定程度上也是对测试需求分析的进一步检验,这将有助于在早期阶段就发现潜在的测试遗漏。 + +同时,由于不可能进行穷尽测试,而且测试的时间和资源都是有限的,所以必须有所取舍,进行有针对性的测试。因此,测试范围中需要明确“测什么”和“不测什么”。 + +测试策略的话题 + +测试策略简单来讲就是需要明确“先测什么后测什么”和“如何来测”这两个问题。 + +病有轻重缓急,测试也是一样的道理,重要的项先测,而不重要的项要后测。测试策略会要求我们明确测试的重点,以及各项测试的先后顺序。 + +比如,对用户登录模块来讲,“用户无法正常登录”和“用户无法重置密码”这两个潜在问题,对业务的影响孰轻孰重一目了然,所以,你应该按照优先级来先测“用户正常登录”,再测“用户重置密码”。 + +测试策略还需要说明,采用什么样的测试类型和测试方法。 这里需要注意的是,不仅要给出为什么要选用这个测试类型,还要详细说明具体的实施方法。 + +第一,功能测试 + +对于功能测试,你应该根据测试需求分析的思维导图来设计测试用例。 + +主线业务的功能测试由于经常需要执行回归测试,所以你需要考虑实施自动化测试,并且根据项目技术栈和测试团队成员的习惯与能力来选择合适的自动化测试框架。 + +这里需要注意的是,你通常应该先实现主干业务流程的测试自动化。 + +实际操作时,你通常需要先列出主要的功能测试点,并决定哪些测试点适合采用自动化测试,并且决定具体使用什么样的框架和技术。 + +对于需要手工测试的测试点,你要决定采用什么类型的测试用例设计方法,以及如何准备相关的测试数据。 + +另外,你还要评估被测软件的可测试性,如果有可测试性的问题,需要提前考虑切实可行的变通方案,甚至要求开发人员提供可测试性的接口。 + +第二,兼容性测试 + +对于兼容性测试来说,Web测试需要确定覆盖的浏览器类型和版本,移动设备测试需要确定覆盖的设备类型和具体iOS/Android的版本等。 + +你可能会问,我要怎么确定需要覆盖的移动设备类型以及iOS/Android的版本列表呢?这个问题其实并不难: + + +如果是既有产品,你可以通过大数据技术分析产品的历史数据得出Top 30%的移动设备以及iOS/Android的版本列表,那么兼容性测试只需覆盖这部分即可。 +如果是一个全新的产品,你可以通过TalkingData这样的网站来查看目前主流的移动设备,分辨率大小、iOS/Android版本等信息来确定测试范围。 + + +兼容性测试的实施,往往是在功能测试的后期,也就是说需要等功能基本都稳定了,才会开始兼容性测试。 + +当然也有特例,比如,对于前端引入了新的前端框架或者组件库,往往就会先在前期做兼容性评估,以确保不会引入后期无法解决的兼容性问题。 + +兼容性测试用例的选取,往往来自于已经实现的自动化测试用例。道理很简单,因为兼容性测试往往要覆盖最常用的业务场景,而这些最常用的业务场景通常也是首批实现自动化测试的目标。 + +所以,我们的GUI自动化框架,就需要能够支持同一套测试脚本在不做修改的前提下,运行于不同的浏览器。 + +第三,性能测试 + +对于性能测试,需要在明确了性能需求(并发用户数、响应时间、事务吞吐量等)的前提下,结合被测系统的特点,设计性能测试场景并确定性能测试框架。 + +比如,是直接在API级别发起压力测试,还是必须模拟终端用户行为进行基于协议的压力测试。再比如,是基于模块进行压力测试,还是发起全链路压测。 + +如果性能是背景数据敏感的场景,还需要确定背景数据量级与分布,并决定产生背景数据的技术方案,比如是通过API并发调用来产生测试数据,还是直接在数据库上做批量insert和update操作,或者是两种方式的结合。 + +最后,无论采用哪种方式,都需要明确待开发的单用户脚本的数量,以便后续能够顺利组装压测测试场景。 + +性能测试的实施,是一个比较复杂的问题。首先,需要根据你想要解决的问题,确定性能测试的类型;然后,根据具体的性能测试类型开展测试。 + + +性能测试的实施,往往先要根据业务场景来决定需要开发哪些单用户脚本,脚本的开发会涉及到很多性能测试脚本特有的概念,比如思考时间、集合点、动态关联等等。 + +脚本开发完成后,你还要以脚本为单位组织测试场景(Scenario),场景定义简单来说就是百分之多少的用户在做登录、百分之多少的用户在做查询、每个用户的操作步骤之间需要等待多少时间、并发用户的增速是5秒一个,还是5秒2个等等。 + +最后,才是具体的测试场景执行。和自动化功能测试不同,性能测试执行完成后性能测试报告的解读,是整个测试过程中最关键的点。 + + +如果你现在不太清楚我上面提到的一些概念也没关系,我会在后续的文章中为你详细讲解。 + +除了我给你详细分析的、最常用的功能测试、兼容性测试和性能测试外,还有很多测试类型(比如,接口测试、集成测试、安全测试、容量验证、安装测试、故障恢复测试等)。这些测试类型,都有各自的应用场景,也相应有独特的测试方法,在这里我就不再一一展开了,如果你有关于这些测试类型的问题,可以给我留言讨论。 + +测试资源 + +测试资源通常包括测试人员和测试环境,这两类资源都是有限的。测试计划的目的就是,保证在有限资源下的产出最大化。所以,测试资源就是需要明确“谁来测”和“在哪里测”这两个问题。 + +测试人员是最重要的,直接关系到整个测试项目的成败和效率。测试人员的资源通常有两个维度:一是,测试工程师的数量;二是,测试工程师的个人经验和能力。 + +你会发现,测试工程师的经验和能力不足,很难通过测试人员的数量来弥补。相反地,测试工程师的经验和能力都非常强的情况下,测试人员的数量可以适当地减少。 + +通常在测试团队中,测试工程师既有资深,也会有初级,那么你就必须针对团队的实际情况去安排测试计划。比如,难度较大的工作,或者一些新工具、新方法的应用,又或者自动化测试开发工作,通常由资深的测试工程师来承担;而那些相对机械性、难度较小的工作,则由初级工程师完成。 + +可见,你要想规划好测试资源,除了要了解项目本身外,还必须对测试团队的人员特点有清晰的把控。另外,我强烈建议你把具体的任务清晰地落实到每个人的身上,这将有利于建立清晰的责任机制,避免后续可能发生的扯皮。 + +相对于测试人员,测试环境就比较好理解了。不同的项目,可能会使用共享的测试环境,也可能使用专用的测试环境,甚至还会直接使用生产环境。另外,对于目前一些已经实现容器化部署与发布的项目,测试环境就会变得更简单与轻量级,这部分内容我会在后续的文章中给你详细讲解。 + +测试进度 + +在明确了测试范围、测试策略和测试资源之后,你就要考虑具体的测试进度了。测试进度主要描述各类测试的开始时间,所需工作量,预计完成时间,并以此为依据来建议最终产品的上线发布时间。 + +比如,版本接受测试(Build Acceptance Test)的工作量,冒烟测试(Smoke Test)的工作量,自动化脚本开发的工作量,缺陷修复的验证工作量,需要几轮回归测试、每一轮回归测试的工作量等等。 + +在传统瀑布模型中,测试进度完全依赖于开发完成并递交测试版本的时间。如果开发提交测试版本发生了延误,那么在不裁剪测试需求的情况下,产品整体的上线时间就同样会延期。 + +然而在敏捷模式下,测试活动贯穿于整个开发过程,很多测试工作会和开发工作同步进行,比如采用行为驱动开发(Behavior-Driven Development)模式,这样测试进度就不会完全依赖于开发递交可测试版本的时间。 + +行为驱动开发,就是平时我们经常说的BDD,指的是可以通过自然语言书写非程序员可读的测试用例,并通过StepDef来关联基于自然语言的步骤描述和具体的业务操作,最典型的框架就是知名“Cucumber”。 + +测试风险预估 + +俗话说,计划赶不上变化,对于测试也是一样的道理,很少有整个测试过程是完全按照原本测试计划执行的。通常需求变更、开发延期、发现重大缺陷和人员变动是引入项目测试风险的主要原因。 + +对于需求变更,比如增加需求、删减需求、修改需求等,一定要重新进行测试需求分析,确定变更后的测试范围和资源评估,并与项目经理和产品经理及时沟通因此引起的测试进度变化。测试经理/测试负责人切忌不能有自己咬牙扛过去的想法,否则无论是对测试团队还是对产品本身都不会有任何好处。 + +另外,随着测试的开展,你可能会发现前期对于测试工作量的预估不够准确,也可能发现需要增加更多的测试类型,也可能发现因为要修改测试架构的严重缺陷而导致很多的测试需要全回归,还有可能出现开发递交测试版本延期,或者人员变动等各种情况。 + +所以,在制定测试计划时,你就要预估整个测试过程中可能存在的潜在风险,以及当这些风险发生时的应对策略。 那么,在真的遇到类似问题时,你才可以做到心中不慌,有条不紊地应对这些挑战。 + +总结 + +软件测试同软件项目一样,也要制定详细的测试计划。虽然在敏捷开发模式下,软件测试不再局限于厚重的、正式的计划文档,但是测试计划的重要性丝毫没有发生变化。 + +一份成功的测试计划,必须清楚地描述:测试范围、测试策略、测试资源、测试进度和测试风险预估这五个最重要的方面。 + +测试范围需要明确“测什么”和“不测什么”;测试策略需要明确“先测什么后测什么”和“如何来测”;测试资源需要明确“谁来测”和“在哪里测”;测试进度是需要明确各类测试的开始时间,所需工作量和预计完成时间;测试风险预估是需要明确如何有效应对各种潜在的变化。 + +思考题 + +在这篇文章中,我只和你分享了做好测试计划中最最关键的内容,意在抛砖引玉。那么,你在工程实践中,还有哪些见解呢? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/09软件测试工程师的核心竞争力是什么?.md b/专栏/软件测试52讲/09软件测试工程师的核心竞争力是什么?.md new file mode 100644 index 0000000..fcc4bd8 --- /dev/null +++ b/专栏/软件测试52讲/09软件测试工程师的核心竞争力是什么?.md @@ -0,0 +1,169 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 软件测试工程师的核心竞争力是什么? + 在前面的文章中,我给你介绍了测试工程师应该具备的一些基础知识,包括如何设计测试用例、如何制定测试计划、什么是测试覆盖率,以及软件生命周期各个阶段的自动化技术等等内容,相信你通过这些文章,或温故知新,或拓展视野,希望你有所收获。 + +那么,在介绍完这些比较基础的内容后,今天我就来和你聊聊测试工程师的核心竞争力到底什么。只有当你真正明白了自己的核心竞争力,你才能理清“应该做什么”和“应该怎么做”这两个问题,才能朝着正确的方向前行。 + +我以我们团队招聘功能测试和测试开发工程师为例,带你了解一下测试工程师的核心竞争力到底是什么。 + + +案例一来自我们的资深功能测试工程师招聘。当时,有一位拥有近9年测试经验的资深测试候选人,我对他的简历还是比较满意的,所以就安排了面谈。但是,在聊的过程中我很快发现,这位候选人绝大多数的测试经验积累都“强”绑定在特定的业务领域。- +如果抛开这个特定的业务领域,他对测试技术本身以及产品技术实现都缺乏系统的思考和理解。换言之,他的价值仅仅能够体现在这个特定的产品业务上,而一旦离开了这个业务领域,他的经验积累很难被有效重用,也就是说他很难快速适应并胜任我们的业务领域测试。所以,他最终没有得到我们的offer。- +从这个案例中,你可以看出作为测试人员,必须要深入理解业务,但是业务知识不能等同于测试能力。 + +案例二来自我们的测试开发岗位招聘。当时,有一位5年测试开发从业经验的候选人,是南京大学软件学院的硕士,毕业后一直在国内的互联网巨头公司从事测试框架和工具平台的开发工作。- +看完他的简历,我发现他参与开发过的测试框架和工具和我们当时在做的项目很匹配,加之他的背景也相当不错,内心感觉这个职位基本就是他的了。但是,面谈结束后,我彻底改变了想法。- +他所做的的确是测试框架和工具平台的开发工作,但是他的核心能力纯粹就是开发,他只关注如何实现预先设计的功能,而完全不关心所开发的测试框架和工具平台在测试中的具体应用场景。- +我承认他的开发能力,但他并不能胜任我们的测试开发岗位。因为,测试开发岗位的核心其实是“测试”,“开发”的目的是更好地服务于测试,我们看重的是对测试的理解,以及在此基础上设计、开发帮助测试人员提高效率并解决实际问题的工具,而不是一个按部就班、纯粹意义上的开发人员。 + + +这两个实际案例,是否已经引发你去思考这样一个问题:什么才是测试工程师的核心竞争力? + +目前的测试工程师分为两大类别,一类是做业务功能测试的,另一类是做测试开发的,二者的核心竞争力有很大差别。那么,接下来我就带你一起去看看,功能测试和测试开发工程师的核心竞争力分别是什么。 + +我先带你看看业务功能测试工程师,也就是传统意义上的测试工程师的核心竞争力,我归纳了以下几点。 + +传统测试工程师应该具备的核心竞争力 + +这部分内容,我按照一项能力对测试工程师的重要程度的顺序,给你依次归纳了测试工程师要具备的七项核心竞争力,包括:测试策略设计能力、测试用例设计能力、快速学习能力、探索性测试思维、缺陷分析能力、自动化测试技术和良好的沟通能力。 + +或许,你感觉测试策略设计能力、探索性测试思维等对资深的测试工程师来说更重要,而你现在还处在培养快速学习能力、沟通能力、测试用例设计能力的维度。那也没有关系,不断地学习、丰富自己的知识体系,具备更强的职场竞争力,不正是你在追求的吗? + +所以,我在分析你应该具备的主要能力的同时,也会给你分享如何才能使自己具备这些能力 ,帮你成就更好的自己。 + +第一项核心竞争力,测试策略设计能力 + +测试策略设计能力是指,对于各种不同的被测软件,能够快速准确地理解需求,并在有限的时间和资源下,明确测试重点以及最适合的测试方法的能力。 + +具备出色的测试策略设计能力,你可以非常明确地回答出测试过程中遇到的这些关键问题: + + +测试要具体执行到什么程度; + +测试需要借助于什么工具; + +如何运用自动化测试以及自动化测试框架,以及如何选型; + +测试人员资源如何合理分配; + +测试进度如何安排; + +测试风险如何应对。 + + +培养出色的测试策略设计能力,不是一朝一夕的事情,通常需要经过大量项目的实际历练,并且你还要保持持续思考,主动去提炼共性的内容。 + +不像测试技术,你可以通过培训或者网上资料的学习而有快速的提升,测试策略设计能力一定是需要你在大量实践的基础上潜移默化形成的。 + +我认为,测试策略设计能力是功能测试工程师最核心的竞争力,也是最难培养的。 + +第二项核心竞争力,测试用例设计能力 + +测试用例设计能力是指,无论对于什么类型的测试,都能设计出高效地发现缺陷,保证产品质量的优秀测试用例。 + +要做好测试用例设计,不仅需要深入理解被测软件的业务需求和目标用户的使用习惯,还要熟悉软件的具体设计和运行环境,包括技术架构、缓存机制、中间件技术、第三方服务集成等等。 + +测试用例设计能力要求你不仅仅局限于熟悉业务领域的测试用例设计,而是能够融会贯通,熟练地把系统性的测试设计方法和具体业务有机结合,对任何被测软件都可以输出出色的测试用例。 + +要想提高测试用例设计能力,你平时就要多积累,对常见的缺陷模式、典型的错误类型以及遇到过的缺陷,要不断地总结、归纳,才能逐渐形成体系化的用例设计思维。 + +同时,你还可以阅读一些好的测试用例设计实例开阔思路,日后遇到类似的被测系统时,可以做到融会贯通和举一反三。 + +第三项核心竞争力,快速学习能力 + +快速学习能力,包含两个层面的含义: + + +对不同业务需求和功能的快速学习与理解能力; + +对于测试新技术和新方法的学习与应用能力。 + + +显然,快速学习能力是各行业从业者应该具备的能力,但为什么我会单独列出来呢? + +现今的软件项目,尤其是互联网项目,生命周期通常以“月”甚至是以“周”、“小时”为单位,一个测试工程师需要接触各种类型的测试项目,而不再像早年,可以在很长一段时间内只从事一个产品或者相关产品的测试了,所以快速学习能力对测试工程师来说,就是至关重要了,否则就容易被淘汰。 + +快速学习能力,乍一看是比较难培养的,但其实也有一些小窍门。 + +比如,当你学习一个新的开源工具时,建议你直接看官方文档:一来,这里的内容是最新而且是最权威的;二来,可以避免网上信息质量的参差不齐。知识输入源头是单一,而且权威的话,你的学习曲线也必然会比较平滑。 + +另外,当学习新内容时,你一定要做到理解其原理,而不是只停留在表面的、简单的操作和使用,长期保持这种学习状态,可以在很大程度上提高逻辑思维和理解能力。这样,当你再面对其他新鲜事物时候,也会更容易理解,形成良性循环。 + +第四项核心竞争力,探索性测试思维 + +探索性测试是指,测试工程师在执行测试的过程中不断学习被测系统,同时结合基于自己经验的错误猜测和逻辑推理,整理和分析出更多的有针对性的测试关注点。 + +本质上,探索性测试思维是“测试用例设计能力”和“快速学习能力”有机结合的必然结果。优秀的探索性测试思维可以帮助你实现低成本的“精准测试”,精准测试最通俗的理解可以概括为针对开发代码的变更,目标明确并且有针对性地对变更点以及变更关联点做测试,这也是目前敏捷测试主推的测试实践之一。 + +第五项核心竞争力,缺陷分析能力 + +缺陷分析能力,通常包含三个层面的含义: + + +对于已经发现的缺陷,结合发生错误的上下文以及后台日志,可以预测或者定位缺陷的发生原因,甚至可以明确指出具体出错的代码行,由此可以大幅缩短缺陷的修复周期,并提高开发工程师对于测试工程师的认可以及信任度; + +根据已经发现的缺陷,结合探索性测试思维,推断同类缺陷存在的可能性,并由此找出所有相关的潜在缺陷; + +可以对一段时间内所发生的缺陷类型和趋势进行合理分析,由点到面预估整体质量的健康状态,并能够对高频缺陷类型提供系统性的发现和预防措施,并以此来调整后续的测试策略。 + + +这三个层面是依次递进的关系,越往后越能体现出测试工程师的核心竞争力。 + +第六项核心竞争力,自动化测试技术 + +掌握自动化测试技术,可以把你从大量的重复性手工劳动中解放出来,这样你可以把更多的时间花在更多类型的测试上。 + +一方面,自动化测试技术本身不绑定被测对象,比如说你掌握了GUI的自动化测试技术,那么你就可以基于这个技术去做任何GUI系统的界面功能测试了。 + +另一方面,自动化测试技术需要测试工程师具备一定的写代码的能力,这通常与测试工程师职业发展的诉求不谋而合,所以你会看到很多测试工程师非常热衷做自动化测试。 + +但是切记,自动化测试的核心价值还是“测试”本身,“自动化”仅仅是手段,实际工作中千万不要本末倒置,把大量的精力放在“自动化”上,一味追求自动化而把本质的“测试”弱化了。 + +第七项核心竞争力,良好的沟通能力 + +测试工程师在软件项目中作用,有点像“润滑剂”: + + +一方面,你需要对接产品经理和项目经理,以确保需求的正确实现和项目整体质量的达标; +另一方面,你还要和开发人员不断地沟通、协调,确保缺陷的及时修复与验证。 + + +所以,测试工程师的沟通能力会直接影响事务开展的效率。良好清晰的沟通能力,是一个技术优秀的测试工程师能否获得更大发展的“敲门砖”,也是资深测试工程师或者测试主管的核心竞争力。 + +测试开发工程师的核心竞争力 + +接下来,我再带你一起看看测试开发工程师的核心竞争力。 + +首先既然是测试开发工程师,那么代码开发能力是最基本的要求。可以说,一个合格的测试开发工程师一定可以成为一个合格的开发工程师,但是一个合格的开发工程师不一定可以成为合格的测试开发工程师。这也就是案例二中的候选人没有通过面试的原因。 + +第一项核心竞争力,测试系统需求分析能力 + +除了代码开发能力,测试开发工程师更要具备测试系统需求分析的能力。你要能够站在测试架构师的高度,识别出测试基础架构的需求和提高效率的应用场景。从这个角度说,你更像个产品经理,只不过你这个产品是为了软件测试服务的。 + +第二项核心竞争力,更宽广的知识体系 + +测试开发工程师需要具备非常宽广的知识体系,你不仅需要和传统的测试开发工程师打交道,因为他们是你构建的测试工具或者平台的用户;而且还要和CI/CD、和运维工程师们有紧密的联系,因为你构建的测试工具或者平台,需要接入到CI/CD的流水线以及运维的监控系统中去。 + +除此之外,你还要了解更高级别的测试架构部署和生产架构部署、你还必须对开发采用的各种技术非常熟悉。可见,对于测试开发工程师的核心竞争力要求是非常高的,这也就是为什么现今市场上资深的测试开发工程师的价格会高于资深的开发工程师的原因。 + +总结 + +我把测试工程师按照工作内容,分为了功能测试工程师(即传统测试工程师)和测试开发工程师两类,分别给你分享了他们的核心竞争力。 + +对于功能测试工程师来说,其核心竞争力包括:测试策略设计能力、测试用例设计能力、快速学习能力、探索性测试思维、缺陷分析能力、自动化测试技术和良好的沟通能力这七大部分,你可以有针对性地提升自己某方面的能力,去获取更大发展空间的“敲门砖”。 + +而对于测试开发工程师来说,你需要具备优秀的测试系统需求分析能力和完备的知识体系,这样才能保证你设计的测试工作和平台,可以更好地满足提升测试效率的要求。 + +思考题 + +你有没有想过这样一个问题,你很少会听到开发工程师谈论自己的核心竞争力,往往都是测试工程师更关注这个问题,这是不是从某个侧面反映出测试工程师的核心竞争力不够清晰或者是随着互联网时代的到来而发生了很大变化,说说你的看法吧。 + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/10软件测试工程师需要掌握的非测试知识有哪些?.md b/专栏/软件测试52讲/10软件测试工程师需要掌握的非测试知识有哪些?.md new file mode 100644 index 0000000..b7bcce2 --- /dev/null +++ b/专栏/软件测试52讲/10软件测试工程师需要掌握的非测试知识有哪些?.md @@ -0,0 +1,131 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 软件测试工程师需要掌握的非测试知识有哪些? + 我在上一篇文章中,跟你分享了测试工程师应该具备的核心竞争力,大多是测试专业知识方面的内容。但是,在专栏第一篇文章中,我提到了这样一个观点:一个优秀的测试工程师,必须具备宽广的知识面,才能设计出有的放矢的测试用例,保证整个软件产品的质量。 + +所以,今天我要分享的主题就是,除了测试专业知识外,你还要掌握哪些知识,才能一路披荆斩棘,成长为一名优秀的测试工程师,或者是测试架构师。 + +与开发工程师相比,你需要了解的技术种类要多得多,视野也要宽广很多,只是在每类技术的深度方面不如开发工程师。 + +你可以参照下面这个比喻,来理解开发工程师和测试工程师的对知识的要求:开发工程师通常是“深度遍历”,关注的是“点”;而测试工程师通常是“广度遍历”,关注的是“面”。 + +那么,测试工程师需要掌握的非测试知识主要有哪些呢? + +如果你花时间静下心来仔细想一下,很可能会把自己吓一大跳,需要了解掌握的非测试知识实在是太多了,这简直就是一个mini版的系统架构师啊! + + +小到Linux/Unix/Windows操作系统的基础知识,Oracle/MySQL等传统关系型数据库技术,NoSQL非关系型数据库技术,中间件技术,Shell/Python脚本开发,版本管理工具与策略,CI/CD流水线设计,F5负载均衡技术,Fiddler/Wireshark/Tcpdump等抓包工具,浏览器Developer Tool等; +大到网站架构设计,容器技术,微服务架构,服务网格(Service Mesh),DevOps,云计算,大数据,人工智能和区块链技术等。 + + +可以说,测试工程师需要掌握的这些技术,几乎涵盖了当今主流软件技术的方方面面。当然,你也不可能一口气吃成胖子,所以我就挑选了几个我认为比较重要,又符合当前技术趋势的关键知识点,和你分享。 + +希望我的分享,可以帮助你在面对新的技术趋势时,站在更高的高度,更好地把握测试工作的内涵和外延。 + +网站架构的核心知识 + +现如今,互联网产品已经占据了软件行业的大半壁以江山。作为测试工程师,你很多时候都在和互联网产品,尤其是网站类应用产品的测试打交道。 + +这时,如果你想要做好互联网产品功能测试以外的其他测试,比如性能测试、稳定性测试、全链路压测、故障切换(Failover)测试、动态集群容量伸缩测试、服务降级测试和安全渗透测试等,就要掌握网站的架构知识。否则,面对这类测试时,你将束手无策。 + + +比如,如果你不清楚Memcached这类分布式缓存集群的应用场景和基本原理,如果你不清楚缓存击穿、缓存雪崩、缓存预热、缓存集群扩容局限性等问题,你就设计不出针对缓存系统特有问题的测试用例; +再比如,如果你对网站的可伸缩性架构设计不了解,不清楚应用服务器的各种负载均衡实现的基本原理,不了解数据库的读写分离技术,你就无法完成诸如故障切换、动态集群容量伸缩、服务降级等相关的测试,同时对于性能测试和全链路压测过程中可能遇到的各种瓶颈,也会很难定位和调整。 + + +这就有点像当年做传统软件产品测试时,我们必须了解软件的架构设计一样,现在被测对象成了互联网产品,我们就必须要了解网站架构。 + +所以,我强烈建议你要掌握网站架构的核心知识,你不需要像系统架构师那样能够熟练驾驭各种架构,并根据业务选型,但你至少需要理解架构相关的基本知识以及核心原理。 + +基于此,我在专栏的最后安排了一系列文章,包括了网站高性能架构设计、网站高可用架构设计、网站伸缩性架构设计和网站可扩展性架构设计,为你详细讲解互联网架构的核心知识,提升你的互联网产品测试能力。 + +容器技术 + +“容器”已不再是一个陌生词汇了,大多数人都在实际工作中或多或少地用到了容器技术。与传统的虚拟机相比,容器技术在轻量化程度、资源占用、运行效率等方面具有压倒性的优势。 + +除了那些专门做容器测试的测试工程师外,一般的测试工程师接触容器技术的机会也越来越多。 + +很多中大型互联网企业都在推行容器化开发与运维,开发人员递交给测试工程师的软件版本通常就是一个Docker Image,直接在容器上进行测试。有些公司还会把测试用例和执行框架也打包成Docker Image,配合版本管理机制,实现用容器测试容器。 + +对测试开发工程师来说,需要应用容器的场景就更多了。比如,目前主流的Selenium Grid就已经提供了官方Docker版本,可以直接以容器的方式建立测试执行环境,也可以很方便地在Pivotal Cloud Foundry和Google Cloud Platform等云计算平台上快速建立测试执行环境。 + +基于Docker的Selenium Grid大大减轻了批量虚拟机节点上Web Driver、浏览器版本和守护者进程版本等升级维护的工作量。 + +测试开发工程师还可以通过Docker Image的形式,提供某些测试工具,而不是以传统的安装包或者JAR文件的形式,可以实现测试工具开箱即用。 + +可见,容器技术已经慢慢渗透到软件研发与运维的各个层面,作为新时代的测试开发工程师,你必须像熟练使用VMware一样,掌握Docker和Kubernetes的原理和使用方法。 + +那对于一个测试工程师来说,怎么才能快速具备容器相关知识,并上手涉及容器技术的互联网产品测试呢? + +在这里,我还是要跟你强调选择学习资料时,一定要注意权威性,我给你的推荐依然是Docker官网的教程,在这里你完全可以理清Docker概念以及具体使用方法,那再结合具体的实战,相信你必定收获颇丰。 + +云计算技术 + +一方面,很多企业,尤其是互联网企业都在尝试“上云”, 也就是逐渐把生产环境从原本的集中式数据中心模式转向私有云或者混合云模式。 + +前段时间,eBay的一些产品线就对外宣布了和Pivotal Cloud Foundry的合作,会将部分产品线迁移到云端。显然,作为测试工程师,你必须理解服务在云端部署的技术细节才能更好的完成测试任务。 + +另一方面,测试基础服务作为提供测试服务的基础设施,比如测试执行环境服务(Test Execution Service)和测试数据准备服务(Test Data Service)等,也在逐渐走向云端。 比如,国外非常流行的Sauce Labs,就是一个著名的测试执行环境公有云服务。 + +一些大型互联网企业,通常还会考虑建立自己的测试执行私有云。最典型的就是,基于Appium + Selenium Grid,搭建移动终端设备的测试执行私有云。 + +所以,除了专门进行云计算平台测试的工程师,必须要掌握云计算的知识外,其他互联网产品的测试工程师,也要能够理解并掌握基本的云计算知识和技术。 + +在我看来,对于云计算的学习,你的侧重点应该是如何使用云提供的基础设施以及服务。我建议的高效学习方法是,参考你所采用的云方案的官方文档,再结合实际案例进行试用,学习效果会更好。 + +你可以尝试用云服务去部署自己的应用,同时还可以结合云平台提供的各类服务(配置服务,数据库服务等)和你的应用做集成。另外,我还建议你尝试用云平台建立自己的小应用集群,体验集群规模的动态收缩与扩展。你还可以尝试在云平台上直接使用Docker部署发布你的服务。 + +更进一步,你可以尝试在云端建立自己的Selenium Gird集群,现在Selenium Gird已经发布了对应的Docker版本镜像,你可以非常方便地在云平台上搭建自己的Selenium Grid。 + +不要以为这会有多复杂,理解了Docker的基本概念以及对应云平台的使用方法,你就可以在短时间内快速搭建起这样的Selenium集群。 + +相信以上这些基本的应用场景,都将更好地帮助你理解云平台的核心功能以及使用场景,从而帮你完成对应产品的测试。 + +DevOps思维 + +DevOps 强调的是,开发、测试和运维等组织团队之间,通过高效自动化工具的协作和沟通,来完成软件的全生命周期管理,从而实现更频繁地持续交付高质量的软件,其根本目的是要提升业务的交付能力。 + +DevOps的具体表现形式可以是工具、方法和流水线,但其更深层次的内涵还是在思想方法,以敏捷和精益为核心,通过发现问题,以系统性的方法或者工具来解决问题,从而实现持续改进。 + +因此,测试工程师也必须深入理解DevOps思想的核心和精髓,才能在自动化测试和测试工具平台的实现上做出最契合的设计。无论是测试工程师,还是测试开发工程师,都会成为DevOps实践成功落地的重要推动力。 + +要想真正学习和掌握DevOps,并不是简单地学习几款工具的使用,更重要的是需要有DevOps思维,能够将各个工具有机结合,提供高效的CI/CD流水线。 + +对于DevOps,我建议的学习路径是,你可以从深入掌握Jenkins之类的工具开始,到熟练应用和组合各种plugin来完成灵活高效的流水线搭建,之后再将更多的工具逐渐集成到流水线中以完成更多的任务。 + +相信通过这样的学习,当你再面对相关的测试工作时,必然可以轻松应对。 + +前端开发技术 + +前端开发技术的发展突飞猛进,新的框架与技术层出不穷,Vue.js,Angular和React等让人应接不暇。并且,还有很多在此类框架基础上开发的组件库可以直接使用,比如AntD,大大降低了前端开发的难度和时间成本。 + +但是,前端开发技术的发展和测试又有什么关系呢? + +从测试工程师的角度来讲,如果你能够掌握前端开发技术,也就意味着你可以更高效地做前端的测试,更容易发现潜在缺陷。同时,你还可以自己构建测试页面,来完成各类前端组件的精细化测试,大大提高测试覆盖率和效率。 + +从测试开发工程师的角度来讲,很多测试平台和工具都需要UI界面,比如很多公司内部构建的测试数据服务和测试执行服务,如果你能熟练掌握基本的前端开发技术,那你就可以很方便、高效地构建测试平台和工具的UI。 + +关于前端技术的学习路径,通常你首先需要掌握最基本的JavaScript、CSS、JQuery和HTML5等知识,然后再去学习一些主流的前端开发框架,比如Angular.js、Backbone.js等。当然现在的Node.js的生态圈非常发达,你如果能够掌握Node.js,那么很多东西实现起来都可以得心应手。 + +我个人推荐从网上下载一些样例代码进行学习,同时学习使用脚手架从无到有去建立自己的前端应用。 + +总结 + +为了应对技术发展趋势,做好软件产品的测试工作,软件测试工程师需要掌握非常多的非测试专业知识,包括:网站架构、容器技术、云计算技术、DevOps思维,以及前端开发技术的核心知识以及实践。 + +对于这类新技术的学习,我强烈推荐你直接阅读官方网站的文档以及代码示例。这种方式,可以让你少走弯路,同时保证所学内容是最新的。 + +当然,我跟你分享的这些非测试专业知识,只是众多技术的冰山一角,你在实际的测试工作中也会遇到更多的技术,希望你可以举一反三,不断扩充自己的知识面,向着一个优秀测试工程师、架构师努力! + +思考题 + +你所在的测试领域,还有哪些非测试知识是必须掌握的,这些知识可以从哪些方面帮到你呢? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/11互联网产品的测试策略应该如何设计?.md b/专栏/软件测试52讲/11互联网产品的测试策略应该如何设计?.md new file mode 100644 index 0000000..53be531 --- /dev/null +++ b/专栏/软件测试52讲/11互联网产品的测试策略应该如何设计?.md @@ -0,0 +1,160 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 互联网产品的测试策略应该如何设计? + 在上一篇文章中,我跟你分享了做好互联网产品测试你要具备的非测试知识,那么现在我就来跟你聊聊应该如何设计互联网产品的测试策略。 + +在我开始今天的话题之前,请你先思考一下为什么我会把互联网产品的测试策略单独拿出来讨论,互联网产品的测试策略和传统软件产品的测试策略到底有哪些不同? + +研发流程的不同决定了测试策略的不同 + +如果直接回答互联网产品和传统软件产品的测试策略有何不同,你会有些摸不着头脑,那么按照我一直在强调的知其然知其所以然的原则,你可以先去总结这两类产品的研发本身最大的不同是什么? + +那就是,互联网产品的“快”。 + +我在专栏前面的文章中,已经提到了互联网产品的上线周期通常是以“天”甚至是以“小时”为单位,而传统软件产品的周期多以“月”,甚至以“年”为单位。 + +发布周期的巨大差异决定了,传统软件产品的测试策略必然不适用于互联网产品的测试,二者的测试策略必然在测试执行时间和测试执行环境上有巨大差异。 + +比如,对于功能自动化测试用例,执行一轮全回归测试需要12小时,对传统软件来说这根本不是问题,因为发布周期很长,留给测试的时间也会很充裕。 + +不要说全回归测试执行时间需要12小时,哪怕是需要几天几夜也没有任何问题,就像我以前在思科(Cisco)做传统软件测试时,一轮完整的全回归测试的GUI测试用例数接近3000个,API测试用例数更是接近25000个,跑完全部用例需要将近60小时。 + +但对互联网产品来说,通常24小时就会有一到两次的发布,发布流程通常包含了代码静态扫描、单元测试、编译、打包、上传、下载、部署和测试的全流程。显然留给测试执行的时间就非常有限,传统软件动辄十几个小时的测试执行时间,在互联网产品的测试上,根本行不通。 + +通常情况下,互联网产品要求全回归测试的执行时间不能超过4小时。 + +那么,如何在保证测试质量和测试覆盖率的前提下,有效缩短测试执行时间呢? + + +首先,你可以引入测试的并发执行机制,用包含大量测试执行节点的测试执行集群来并发执行测试用例。- +测试执行集群,你可以简单理解为是一批专门用来并发执行测试用例的机器。常见的测试执行集群,由一个主节点(Master)和若干个子节点(Node)组成。其中,主节点用来分发测试用例到各个子节点,而各个子节点用来具体执行测试用例。- +目前,很多互联网企业都建立了自己的测试执行集群。 + +其次,你必须从测试策略上找到突破口,这也是我今天跟你分享的主题。- +接下来,我会先简单为你介绍一下传统软件产品的测试策略设计,然后再给你分享互联网产品的测试策略,这样可以通过对传统软件产品测试策略的回顾,加深你对互联网产品测试策略的认识。 + + +传统软件产品的测试策略设计 + +传统软件产品的测试策略,通常采用如图1所示的金字塔模型。该金字塔模型是迈克 · 科恩(Mike Cohn)提出的,在很长一段时间内都被认为是测试策略设计的最佳实践。 + + + +图1 传统软件产品的金字塔测试策略 + +第一,单元测试 + +金字塔最底部是单元测试,属于白盒测试的范畴,通常由开发工程师自己完成,由于越早发现缺陷其修复成本越低,所以传统软件产品的测试策略提倡对单元测试的高投入,单元测试这一层通常都会做得比较“厚”。 + +另外,传统软件产品,生命周期都比较长,通常会有多个版本持续发布,为了在后期的版本升级过程中能够尽早发现并快速定位问题,每次build过程中都会多次反复执行单元测试,这也从另一个角度反映出单元测试的重要性。 + +第二,API测试 + +金字塔中间部分是API测试,主要针对的是各模块暴露的接口,通常采用灰盒测试方法。灰盒测试方法是介于白盒测试和黑盒测试之间的一种测试技术,其核心思想是利用测试执行的代码覆盖率来指导测试用例的设计。 + +以API接口测试为例,首先以黑盒方式设计如何调用API的测试用例,同时在测试执行过程中统计代码覆盖率,然后根据代码覆盖率情况来补充更多、更有针对性的测试用例。 + +总体来看,API测试用例的数量会少于单元测试,但多于上层的GUI测试。 + +第三,GUI测试 + +金字塔最上层的是GUI测试,也称为端到端(E2E,End-to-end)测试,是最接近软件真实用户使用行为的测试类型。通常是模拟真实用户使用软件的行为,即模拟用户在软件界面上的各种操作,并验证这些操作对应的结果是否正确。 + +GUI测试的优点是,能够实际模拟真实用户的行为,直接验证软件的商业价值;缺点是执行的代价比较大,就算是采用GUI自动化测试技术,用例的维护和执行代价依然很大。所以,要尽可能地避免对GUI测试的过度依赖。 + +另外,GUI测试的稳定性问题,是长期以来阻碍GUI测试发展的重要原因。即使你采用了很多诸如retry机制以及异常场景恢复机制等方式,GUI测试的随机失败率依旧高居不下。 + +互联网产品的测试策略设计 + +对于互联网产品来说,迈克的金字塔模型已经不再适用,我会通过GUI测试、API测试、单元测试这三个方面,来跟你聊聊互联网产品的测试策略有哪些变化,应该如何设计。 + +第一,GUI测试 + +互联网产品的上线周期,决定了GUI测试不可能大范围开展。 + + +互联网产品的迭代周期,决定了留给开发GUI自动化测试用例的时间非常有限; + +互联网产品客户端界面的频繁变化,决定了开展GUI自动化测试的效率会非常低,这也是最糟糕的。- +因为敏捷模式下的快速反馈,在下一个迭代(sprint)可能就需要根据反馈来做修改和调整客户端界面,那么刚开发完,甚至是还没开发完的GUI自动化测试用例就要跟着一起修改。- +这种频繁地修改,对开发GUI自动化测试是非常不利的。因为,刚开发完的自动化用例只跑了一次,甚至是一次还没来得及跑就需要更新了,导致GUI自动化测试还不如手工测试的效率高。 + + +由此,互联网产品的GUI测试通常采用“手工为主,自动化为辅”的测试策略,手工测试往往利用探索性测试思想,针对新开发或者新修改的界面功能进行测试,而自动化测试的关注点主要放在相对稳定且核心业务的基本功能验证上。所以,GUI的自动化测试往往只覆盖最核心且直接影响主营业务流程的E2E场景。 + +另外,从GUI测试用例的数量来看,传统软件的GUI测试属于重量级的,动不动就有上千个用例,因为传统软件的测试周期很长,测试用例可以轮流排队慢慢执行,时间长点也没关系。 + +而互联网产品要求GUI测试是轻量级的,你见过或者听过有哪个互联网产品设计了上千个GUI测试用例吗?互联网产品的上线周期,直接决定了不允许你去执行大量的用例。 + +第二,API测试 + +你现在可能要问,既然互联网产品不适宜做重量级的GUI测试,那么怎样才能保证其质量呢? + +其实,对于互联网产品来说,把测试重点放在API测试上,才是最明智的选择。为什么呢?我给你总结了以下五条原因。 + + +API测试用例的开发与调试效率比GUI测试要高得多,而且测试用例的代码实现比较规范,通常就是准备测试数据,发起request,验证response这几个标准步骤。 + +API测试用例的执行稳定性远远高于GUI测试。 GUI测试执行的稳定性始终是难题,即使你采用了很多技术手段(这些具体的技术手段,我会在讲解GUI测试时再详细展开),它也无法做到100%的稳定。- +而API测试天生就没有执行稳定性的问题,因为测试执行过程不依赖于任何界面上的操作,而是直接调用后端API,且调用过程比较标准。 + +单个API测试用例的执行时间往往要比GUI测试短很多。当有大量API测试需要执行时,API测试可以很方便地以并发的方式执行,所以可以在短时间内完成大批量API测试用例的执行。 + +现在很多互联网产品采用了微服务架构,而对微服务的测试,本质上就是对不同的Web Service的测试,也就是API测试。- +在微服务架构下,客户端应用的实现都是基于对后端微服务的调用,如果做好了每个后端服务的测试,你就会对应用的整体质量有充分的信心。所以,互联网产品的API测试非常重要。 + +API接口的改动一般比较少,即使有改动,绝大多数情况下也需要保证后向兼容性(Backward Compatibility)。所谓后向兼容性,最基本的要求就是保证原本的API调用方式维持不变。- +显然,如果调用方式没有发生变化,那么原本的API测试用例也就不需要做大的改动,这样用例的可重用性就很高,进而可以保证较高的投入产出比(ROI)。 + + +可见,互联网产品的这些特性决定了,API测试可以实现良好的投入产出比,因此应该成为互联网产品的测试重点。这也就是为什么互联网产品的测试策略更像是个菱形结构的原因。 + +如图2所示就是这个菱形的测试策略,遵循“重量级API测试,轻量级GUI测试,轻量级单元测试”的原则。 + + + +图2 互联网产品的菱形测试策略 + +第三,单元测试 + +了解了“重量级API测试”和“轻量级GUI测试”,接下来,我就跟你说说为什么是“轻量级单元测试”。 + +从理论上讲,无论是传统软件产品还是互联网产品,单元测试都是从源头保证软件质量的重要手段,因此都非常重要。但现实是,互联网产品真正能全面开展单元测试,并严格控制代码覆盖率的企业还是凤毛麟角。 + +但凡存在的都会有其合理性,我认为最主要的原因还是在于互联网产品的“快”,快速实现功能,快速寻求用户反馈,快速试错,快速迭代更新。 + +在这样的模式下,互联网产品追求的是最快速的功能实现并上线,基本不会给你时间去做全面的单元测试。即使给你预留了单元测试的时间,频繁的迭代也会让单元测试处于不断重写的状态。因此,单元测试原本的价值,很难在实际操作层面得到体现。 + +那么,互联网产品真的可以不用做单元测试么?答案是否定的,只不是这里的单元测试策略要采用“分而治之”的思想。 + +互联网产品通常会分为应用层和后端服务,后端服务又可以进一步细分为应用服务和基础服务。 + +后端基础服务和一些公共应用服务相对稳定,而且对于系统全局来说是“牵一发而动全身”,所以后端服务很有必要开展全面的单元测试;而对于变动非常频繁的客户端应用和非公用的后端应用服务,一般很少会去做单元测试。 + +另外,对于一些核心算法和关键应用,比如银行网关接口,第三方支付集成接口等,也要做比较全面的单元测试。 + +总结来讲,互联网产品的全面单元测试只会应用在那些相对稳定和最核心的模块和服务上,而应用层或者上层业务服务很少会大规模开展单元测试。 + +总结 + +传统软件通常采用金字塔模型的测试策略,而现今的互联网产品往往采用菱形模型。菱形模型有以下四个关键点: + + +以中间层的API测试为重点做全面的测试。 +轻量级的GUI测试,只覆盖最核心直接影响主营业务流程的E2E场景。 +最上层的GUI测试通常利用探索式测试思维,以人工测试的方式发现尽可能多的潜在问题。 +单元测试采用“分而治之”的思想,只对那些相对稳定并且核心的服务和模块开展全面的单元测试,而应用层或者上层业务只会做少量的单元测试。 + + +思考题 + +你所在的公司或者产品线,采用的是什么测试策略?看完了本篇文章,你会如何评价你们公司的测试策略呢?有哪些好的地方,又有哪些地方需要改进? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/12从0到1:你的第一个GUI自动化测试.md b/专栏/软件测试52讲/12从0到1:你的第一个GUI自动化测试.md new file mode 100644 index 0000000..480c19f --- /dev/null +++ b/专栏/软件测试52讲/12从0到1:你的第一个GUI自动化测试.md @@ -0,0 +1,168 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 从0到1:你的第一个GUI自动化测试 + 在前面的测试基础知识系列文章中,我分享了测试相关的基础知识,从测试用例的设计,到测试覆盖率,再到测试计划的制定,这些都是我认为测试人要掌握的一些基本知识。 + +那么,接下来我将要带你走入GUI自动化测试的世界,和你一起聊聊GUI自动化测试的技术、原理和行业最佳实践。 + +作为该系列的第一篇文章,我直接以一个最简单的GUI自动化用例的开发为例,带你从0开始构建一个Selenium的GUI自动化测试用例。 + +先让你对GUI自动化测试有一个感性认识,然后以此为基础,我再来解释Selenium自动化测试实现的核心原理与机制,希望可以帮你由点到面建立起GUI测试的基础知识体系。 + +构建一个Selenium自动化测试用例示例 + +测试需求非常简单:访问百度主页,搜索某个关键词,并验证搜索结果页面的标题是“被搜索的关键词”+“_百度搜索”。 + +如果搜索的关键词是“极客时间”,那么搜索结果页面的标题就应该是“极客时间_百度搜索”。 + +明白了测试需求后,我强烈建议你先用手工方式执行一遍测试,具体步骤是: + + +打开Chrome浏览器,输入百度的网址“www.baidu.com”; + +在搜索输入框中输入关键词“极客时间”并按下回车键; + +验证搜索结果页面的标题是否是“极客时间_百度搜索”。 + + +明确了GUI测试的具体步骤后,我们就可以用Java代码,基于Selenium实现这个测试用例了。 + +这里,我要用到Chrome浏览器,所以需要先下载Chrome Driver并将其放入环境变量。接下来,你可以用自己熟悉的方式建立一个空的Maven项目,然后在POM文件中加入Selenium 2.0的依赖,如图1所示。 + + + +图1 在POM文件中加入Selenium 2.0的依赖 + +接着用Java创建一个main方法,并把如图2所示的代码复制到你的main方法中。 + + + +图2 基于Selenium的自动化测试用例的样本代码 + +现在,你可以尝试运行这个main方法,看看会执行哪些操作。 + + +这段代码会自动在你的电脑上打开Chrome浏览器; + +在URL栏自动输入“www.baidu.com”; + +百度主页打开后,在输入框自动输入“极客时间”并执行搜索; + +返回搜索结果页面; + +Chrome浏览器自动退出。 + + +以上这些步骤都是由自动化测试代码自动完成的。 + +如果你已经接触过GUI自动化测试,你可能习以为常了,感觉没什么神奇的。但如果你是第一次接触GUI自动化测试,是不是觉得还蛮有意思的。 + +现在,我来快速解读一下这些代码,你可以看看这些自动化步骤是怎么实现的,更具体的原理和内部机制我会在后面文章中详细展开。 + + +第11行,WebDriver driver = new ChromeDriver(),先创建一个Chrome Driver的实例,也就是打开了Chrome浏览器,但实际上没这么简单,后台还做了些额外的Web Service绑定工作,具体后面会解释; +第14行,driver.navigate().to(s: “http://www.baidu.com”)用刚才已经打开的Chrome浏览器访问百度主页; +第18行,WebElement search_input = driver.findElement(By.name(“wd”)),使用driver的findElement方法,并通过name属性定位到了搜索输入框,并将该搜索输入框命名为search_input; +第21行,search_input.sendKeys(…charSequences:“极客时间”),通过WebElement的sendKeys方法向搜索输入框search_input输入了字符串“极客时间”; +第24行,search_input.submit(),递交了搜索请求; +第27行,Thread.sleep(millis:3000),强行等待了固定的3秒时间; +第30行,Assert.assertEquals(expected:“极客时间_百度搜索”,driver.getTitle()),通过junit的assertEquals比较了浏览器的标题与预计结果,其中页面标题通过driver的getTitle方法得到,如果标题与预计结果一致,测试通过,否则测试失败; +第33行,driver.quit(),显式关闭了Chrome浏览器。 + + +现在,你对main方法中的代码,已经比较清楚了。但是,你知道Selenium内部是如何实现Web自动化操作的吗?这就要从Selenium的历史版本和基本原理开始讲起了。 + +Selenium的实现原理 + +首先,你要明确刚才建立的测试用例是基于Selenium 2.0,也就是Selenium + WebDriver的方案。 + +其次,你需要知道,对Selenium而言,V1.0和V2.0版本的技术方案是截然不同的,V1.0的核心是Selenium RC,而V2.0的核心是WebDriver,可以说这完全是两个东西。 + +最后,Selenium 3.0也已经发布一段时间了,V3.0相比V2.0并没有本质上的变化,主要是增加了对MacOS的Safari和Windows的Edge的支持,并彻底删除了对Selenium RC的支持。 + +所以接下来,我会针对V1.0和V2.0来解释Selenium实现Web自动化的原理。 + +第一,Selenium 1.0的工作原理 + +Selenium 1.0,又称Selenium RC,其中RC是Remote Control的缩写。Selenium RC利用的原理是:JavaScript代码可以很方便地获取页面上的任何元素并执行各种操作。 + +但是因为”同源政策(Same-origin policy)”(只有来自相同域名、端口和协议的JavaScript代码才能被浏览器执行),所以要想在测试用例运行中的浏览器中,注入JavaScript代码从而实现自动化的Web操作,Selenium RC就必须“欺骗”被测站点,让它误以为被注入的代码是同源的。 + +那如何实现“欺骗”呢?这其实就是引入Selenium RC Server的根本原因,其中的Http Proxy模块就是用来“欺骗”浏览器的。 + +除了Selenium RC Server,Selenium RC方案的另一大部分就是,Client Libraries。它们的具体关系如图3所示。 + + + +图3 Selenium RC的基本模块 + +Selenium RC Server,主要包括Selenium Core,Http Proxy和Launcher三部分: + + +Selenium Core,是被注入到浏览器页面中的JavaScript函数集合,用来实现界面元素的识别和操作; +Http Proxy,作为代理服务器修改JavaScript的源,以达到“欺骗”被测站点的目的; +Launcher,用来在启动测试浏览器时完成Selenium Core的注入和浏览器代理的设置。 + + +Client Libraries,是测试用例代码向Selenium RC Server发送Http请求的接口,支持多种语言,包括Java、C#和Ruby等。 + +为了帮你更好地理解Selenium RC的基本原理,我从Selenium的官方网站截取了以下执行流程图,并把具体的7个步骤做了如下翻译。 + + + +图4 Selenium RC的执行流程 + + +测试用例通过基于不同语言的Client Libraries向Selenium RC Server发送Http请求,要求与其建立连接。 + +连接建立后,Selenium RC Server的Launcher就会启动浏览器或者重用之前已经打开的浏览器,把Selenium Core(JavaScript函数的集合)加载到浏览器页面当中,并同时把浏览器的代理设置为Http Proxy。 + +测试用例通过Client Libraries向Selenium RC Server发送Http请求,Selenium RC Server解析请求,然后通过Http Proxy发送JavaScript命令通知Selenium Core执行浏览器上控件的具体操作。 + +Selenium Core接收到指令后,执行操作。 + +如果浏览器收到新的页面请求信息,则会发送Http请求来请求新的Web页面。由于Launcher在启动浏览器时把Http Proxy设置成为了浏览器的代理,所以Selenium RC Server会接收到所有由它启动的浏览器发送的请求。 + +Selenium RC Server接收到浏览器发送的Http请求后,重组Http请求以规避“同源策略”,然后获取对应的Web页面。 + +Http Proxy把接收的Web页面返回给浏览器,浏览器对接收的页面进行渲染。 + + +第二,Selenium 2.0的工作原理 + +接下来,我们回到上面那个百度搜索的测试用例,这个测试用例用的就是Selenium 2.0。Selenium 2.0,又称Selenium WebDriver,它利用的原理是:使用浏览器原生的WebDriver实现页面操作。它的实现方式完全不同于Selenium 1.0。 + +Selenium WebDriver是典型的Server-Client模式,Server端就是Remote Server。以下是Selenium 2.0工作原理的解析。 + + + +图5 Selenium WebDriver的执行流程 + + +当使用Selenium2.0启动浏览器Web Browser时,后台会同时启动基于WebDriver Wire协议的Web Service作为Selenium的Remote Server,并将其与浏览器绑定。绑定完成后,Remote Server就开始监听Client端的操作请求。 + +执行测试时,测试用例会作为Client端,将需要执行的页面操作请求以Http Request的方式发送给Remote Server。该HTTP Request的body,是以WebDriver Wire协议规定的JSON格式来描述需要浏览器执行的具体操作。 + +Remote Server接收到请求后,会对请求进行解析,并将解析结果发给WebDriver,由WebDriver实际执行浏览器的操作。 + +WebDriver可以看做是直接操作浏览器的原生组件(Native Component),所以搭建测试环境时,通常都需要先下载浏览器对应的WebDriver。 + + +总结 + +首先,我基于Selenium 2.0,带你从0到1建立了一个最简单直接的GUI自动化测试用例。这个用例的实现很简单,但是只有真正理解了Selenium工具的原理,你才能真正用好它。 + +所以,我又分享了Selenium 1.0和Selenium 2.0的内部实现机制和原理:Selenium 1.0的核心是,基于JavaScript代码注入;而Selenium 2.0的核心是,运用了浏览器原生支持的WebDriver。 + +思考题 + +除了Selenium,业内还有很多常用的GUI自动化测试框架,比如UFT(以前的QTP)、RFT、Nightwatch等,你在平时的工作中接触过哪些GUI自动化测试框架?你知道它们的内部实现原理吗? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/13效率为王:脚本与数据的解耦+PageObject模型.md b/专栏/软件测试52讲/13效率为王:脚本与数据的解耦+PageObject模型.md new file mode 100644 index 0000000..e69de29 diff --git a/专栏/软件测试52讲/14更接近业务的抽象:让自动化测试脚本更好地描述业务.md b/专栏/软件测试52讲/14更接近业务的抽象:让自动化测试脚本更好地描述业务.md new file mode 100644 index 0000000..e268c05 --- /dev/null +++ b/专栏/软件测试52讲/14更接近业务的抽象:让自动化测试脚本更好地描述业务.md @@ -0,0 +1,156 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 更接近业务的抽象:让自动化测试脚本更好地描述业务 + 在上一篇文章中,我介绍了GUI自动化测试中的两个主要的概念“脚本与数据的解耦 ”以及“ 页面对象模型”。在引入“操作函数”封装时,我提到操作函数在改善测试脚本可读性问题的同时,也引入了两个新的问题,即: 如何把控操作函数的粒度,以及如何衔接两个操作函数之间的页面。 + +现在,我就以这两个问题作为引子,为你介绍GUI自动化测试中“业务流程(business flow)”的概念、核心思想以及应用场景。 + +如何把控操作函数的粒度? + +操作函数的粒度是指,一个操作函数到底应该包含多少操作步骤才是最合适的。 + + +如果粒度太大,就会降低操作函数的可重用性。极端的例子就是,前面文章中涉及的百度搜索的案例,把“登录”“搜索”“登出”的操作作为一个操作函数。 +如果粒度太小,也就失去了操作函数封装的意义。极端的例子就是,把每一个步骤都作为一个操作函数。 +更糟糕的是,在企业实际自动化测试开发中,每个测试工程师对操作函数的粒度理解也不完全相同,很有可能出现同一个项目中脚本粒度差异过大,以及某些操作函数的可重用性低的问题。 + + +那么,操作函数的粒度到底应该如何控制呢?其实这个问题,在很大程度上取决于项目的实际情况,以及测试用例步骤的设计,并没有一个放之四海而皆准的绝对标准。 + +但是,脚本粒度的控制还是有设计依据可以遵循的,即往往以完成一个业务流程(business flow)为主线,抽象出其中的“高内聚低耦合”的操作步骤集合,操作函数就由这些操作步骤集合构成。 + +比如,对于“用户注册”这个业务流程,其中的“信用卡绑定”操作就会涉及多个操作步骤,而这些操作在逻辑上又是相对独立的,所以就可以包装成一个操作函数。也就是说,业务流程会依次调用各个操作函数,来完成具体的业务操作。 + +如何衔接两个操作函数之间的页面? + +完成一个业务流程操作,往往会需要依次调用多个操作函数,但是操作函数和操作函数之间会有页面衔接的问题,即前序操作函数完成后的最后一个页面,必须是后续操作函数的第一个页面。 + +如果连续的两个操作函数之间无法用页面衔接,那就需要在两个操作函数之间加入额外的页面跳转代码,或者是在操作函数内部加入特定的页面跳转代码。 + +业务流程抽象 + +在解决如何把控操作函数的粒度,以及如何衔接两个操作函数之间的页面这两个问题的过程中,我引入了业务流程的概念。那么,接下来我就跟你详细说说什么是业务流程。 + +业务流程抽象是,基于操作函数的更接近于实际业务的更高层次的抽象方式。基于业务流程抽象实现的测试用例往往灵活性会非常好,你可以很方便地组装出各种测试用例。 + +这个概念有点拗口,难以理解。但是,没关系,我举个例子,你就豁然开朗了。 + +假设,某个具体的业务流程是:已注册的用户登录电商平台购买指定的书籍。那么,基于业务流程抽象的测试用例伪代码,如图1所示。 + + + +图1 基于业务流程抽象的测试用例伪代码 + +这段伪代码的信息量很大,但是理解了这段代码的设计思想,你也就掌握了业务流程抽象的精髓。 + +首先,从整体结构上看,段伪代码顺序调用了4个业务流程, 依次是完成用户登录的LoginFlow、完成书籍查询的SearchBookFlow、完成书籍购买的CheckoutBookFlow、完成用户登出的LogoutFlow。 + +这4个业务流程都是作为独立的类封装的,可以被很方便的重用并灵活组合,类的内部实现通常是调用操作函数。而操作函数内部,则是基于页面对象模型完成具体的页面控件操作。 + +然后,对于每一个业务流程类,都会有相应的业务流程输入参数类与之一一对应。具体的步骤通常有这么几步: + + +初始化一个业务流程输入参数类的实例; + +给这个实例赋值; + +用这个输入参数实例来初始化业务流程类的实例; + +执行这个业务流程实例。 + + +执行业务流程实例的过程,其实就是调用操作函数来完成具体的页面对象操作的过程。 + +为了让你更好地理解业务流程抽象提供了哪些功能,接下来我会为你逐行解读这段伪代码。 + +伪代码的第2-6行,调用的是LoginFlow,完成了用户登录的操作。 + +2: LoginFlowParameters loginFlowParameters = new LoginFlowParameters(); +3: loginFlowParameters.setUserName("username"); +4: loginFlowParameters.setPassword("password"); +5: LoginFlow loginFlow = new LoginFlow(loginFlowParameters); +6: loginFlow.execute(); + + +第2行,初始化了LoginFlow对应的LoginFlowParameters的实例。 + +第3-4行,通过setUserName和setPassword方法将用户名和密码传入该参数实例。 + +第5行,用这个已经赋值的参数实例来初始化LoginFlow。 + +第6行,通过execute方法发起执行。执行之后,LoginFlow会调用内部的操作函数,或者直接调用页面对象方法,完成用户登录的操作。 + +伪代码的第9-12行,用和2-6行类似的方式调用了SearchBookFlow,完成了书籍搜索的操作。 + +9: SearchBookFlowParameters searchBookFlowParameters = new SearchBookFlowParameters(); +10: searchBookFlowParameters.setBookName("bookname"); +11: SearchBookFlow searchBookFlow = new SearchBookFlow(searchBookFlowParameters); +12: searchBookFlow.withStartPage(loginFlow.getEndPage()).execute(); + + +需要特别注意的是,第12行中withStartPage(loginFlow.getEndPage())的含义是,SearchBookFlow的起始页面将会使用之前loginFlow的结束页面。显然,通过这种方式可以很方便地完成两个业务流程之间的页面衔接。 + +同时,从中还可以看出,其实每个业务流程都可以接受不同的起始页面。以SearchBookFlow为例,它的起始页面既可以是书籍首页,也可以是其他页面,但是需要在它的内部对不同的初始页面做出相应的处理,以保证这个业务流程真正开始的页面是在书籍搜索页面。 + +同样,由于业务流程存在分支的可能性,每个业务流程执行完成的最终页面也不是唯一的,你可以使用getEndPage方法拿到这个业务流程执行结束后的最后页面。 + +通过这段代码的解读,你可以很清楚地理解,业务流程之间的页面衔接是如何实现的。 + +伪代码的第15-18行,调用了CheckoutBookFlow,完成了书籍购买操作。 + +15: CheckoutBookFlowParameters checkoutBookFlowParameters = new CheckoutBookFlowParameters(); +16: checkoutBookFlowParameters.setBookID(searchBookFlow.getOutPut().getBookID()); +17: CheckoutBookFlow checkoutBookFlow = new CheckoutBookFlow(checkoutBookFlowParameters); +18: checkoutBookFlow.withStartPage(searchBookFlow.getEndPage()).execute(); + + +第15行,初始化了CheckoutBookFlow对应的checkoutBookFlowParameters的实例。 + +第16行,通过setBookID(searchBookFlow.getOutPut().getBookID()),将上一个业务流程searchBookFlow的输出参数,作为了当前业务流程的输入参数。这是典型的业务流程之间如何传递参数的示例,也是很多测试场景中都要用到的。 + +第17行,用checkoutBookFlowParameters参数实例来初始化checkoutBookFlow。 + +第18行,通过execute方法发起执行。这里需要注意的是,checkoutBookFlow的起始页面将会使用之前searchBookFlow的结束页面。开始执行后,checkoutBookFlow会调用内部的操作函数,或者直接调用页面对象方法,完成书籍的购买操作。 + +伪代码的第21-22行,调用LogoutFlow,完成了用户登出操作。 + +21: LogoutFlow logoutFlow = new LogoutFlow(); +22: logoutFlow.withStartPage(checkoutBookFlow.getEndPage()).execute(); + + +第21行,由于LogoutFlow不带参数,所以直接初始化了LogoutFlow。 + +第22行,通过execute方法发起执行。这里LogoutFlow的起始页面将会使用之前CheckoutBookFlow的结束页面。开始执行后,LogoutFlow会调用内部的操作函数,或者直接调用页面对象方法,完成用户登出操作。 + +通过对这些代码的解读,我解释了业务流程是什么,并从使用者的角度分析了它的主要特点。比如,如何实现不同业务流程间的页面衔接,如何在不同的业务流程间传递参数等。 + +为了加深印象,我再来总结一下业务流程的优点: + + +业务流程(Business Flow)的封装更接近实际业务; + +基于业务流程的测试用例非常标准化,遵循“参数准备”、“实例化Flow”和“执行Flow”这三个大步骤,非常适用于测试代码的自动生成; + +由于更接近实际业务,所以可以很方便地和BDD结合。BDD就是Behavior Driven Development,即行为驱动开发,你不了解的话,可以看看郑晔老师[这篇文章]。 + + +总结 + +我以如何把控操作函数的粒度,和如何衔接两个操作函数之间的页面,这两个问题为引子,为你介绍了业务流程的概念、核心思想和适用的场景。 + +业务流程抽象是,基于操作函数的更接近于实际业务的更高层次的抽象方式。基于业务流程抽象实现的测试用例往往具有较好的灵活性,可以根据实际测试需求方便地组装出各种测试用例。 + +业务流程的核心思想是,从业务的维度来指导测试业务流程的封装。由于业务流程封装通常很贴近实际业务,所以特别适用于组装面向终端用户的端到端(E2E)的系统功能测试用例,尤其适用于业务功能非常多,并且存在各种组合的E2E测试场景。 + +思考题 + +你所在公司的GUI自动化测试是否已经运用了业务流程级别的封装?在使用过程中,你是否遇到什么瓶颈,是如何解决的? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/15过不了的坎:聊聊GUI自动化过程中的测试数据.md b/专栏/软件测试52讲/15过不了的坎:聊聊GUI自动化过程中的测试数据.md new file mode 100644 index 0000000..1adc9d9 --- /dev/null +++ b/专栏/软件测试52讲/15过不了的坎:聊聊GUI自动化过程中的测试数据.md @@ -0,0 +1,166 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 15 过不了的坎:聊聊GUI自动化过程中的测试数据 + 在前面几篇文章中,我从页面操作的角度介绍了GUI自动化测试,讲解了页面对象模型和业务流程封装,今天我将从测试数据的角度再来谈谈GUI自动化测试。 + +为了顺利进行GUI测试,往往需要准备测试数据来配合测试的进行,如果不采用事先数据准备的方式,测试效率将会大打折扣,而且还会引入大量不必要的依赖关系。 + +以“用户登录”功能的测试为例,如果你的目的仅仅是测试用户是否可以正常登录,比较理想的方式是这个用户已经存在于被测系统中了,或者你可以通过很方便的方式在测试用例中生成这个用户。否则,难道你要为了测试用户登录功能,而以GUI的方式当场注册一个新用户吗?显然,这是不可取的。 + +其实从这里,你就可以看出测试数据准备是实现测试用例解耦的重要手段,你完全不必为了测试GUI用户登录功能而去执行用户注册,只要你能够有方法快速创建出这个登录用户就可以了。 + +在正式讨论测试数据的创建方法前,我先来分析一下GUI测试中两种常见的数据类型: + + +第一大类是,测试输入数据,也就是GUI测试过程中,通过界面输入的数据。比如“用户登录”测试中输入的用户名和密码就就属于这一类数据;再比如,数据驱动测试中的测试数据,也是指这一类。 + +第二大类是,为了完成GUI测试而需要准备的测试数据。比如,“用户登录”测试中,我们需要事先准备好用户账户,以便进行用户的登录测试。今天我分享的测试数据创建的方法,也都是围着这一部分的数据展开的。 + + +那么接下来,我就带你一起去看看创建测试数据的方法都有哪些,以及它们各自的优缺点,和适用场景。 + +从创建的技术手段上来讲,创建测试数据的方法主要分为三种: + + +API调用; + +数据库操作; + +综合运用API调用和数据库操作。 + + +从创建的时机来讲,创建测试数据的方法主要分为两种: + + +测试用例执行过程中,实时创建测试数据,我们通常称这种方式为On-the-fly。 + +测试用例执行前,事先创建好“开箱即用”的测试数据,我们通常称这种方式为Out-of-box。 + + +在实际项目中,对于创建数据的技术手段而言,最佳的选择是利用API来创建数据,只有当API不能满足数据创建的需求时,才会使用数据库操作的手段。 + +实际上,往往很多测试数据的创建是基于API和数据库操作两者的结合来完成,即先通过API创建基本的数据,然后调用数据库操作来修改数据,以达到对测试数据的特定要求。 + +而对于创建数据的时机,在实际项目中,往往是On-the-fly和Out-of-box结合在一起使用。 + +对于相对稳定的测试数据,比如商品类型、图书类型等,往往采用Out-of-box的方式以提高效率;而对于那些只能一次性使用的测试数据,比如商品、订单、优惠券等,往往采用On-the-fly的方式以保证不存在脏数据问题。 + +接下来,我就先从测试数据创建的技术手段开始今天的分享吧。 + +基于API调用创建测试数据 + +先看一个电商网站“新用户注册”的例子,当用户通过GUI界面完成新用户注册信息填写后,向系统后台递交表单,系统后台就会调用createUser的API完成用户的创建。 + +而互联网产品,尤其是现在大量采用微服务架构的网站,这个API往往以Web Service的形式暴露接口。那么,在这种架构下,你完全可以直接调用这个API来创建新用户,而无须再向后台递交表单。 + +由于API通常都有安全相关的token机制来保护,所以实际项目中,通常会把对这些API的调用以代码的形式封装为测试数据工具(Test Data Utility)。 + +这种方式最大的好处就是,测试数据的准确性直接由产品API保证,缺点是并不是所有的测试数据都有相关的API来支持。 + +另外,对需要大量创建数据的测试来说,基于API调用方式的执行效率,即使采用了并发机制也不会十分理想。为了解决执行效率的问题,就有了基于数据库操作的测试数据创建手段。 + +基于数据库操作创建测试数据 + +实际项目中,并不是所有的数据都可以通过API的方式实现创建和修改,很多数据的创建和修改直接在产品代码内完成,而且并没有对外暴露供测试使用的接口。 + +那么,这种情况下,你就需要通过直接操作数据库的方式来产生测试数据。 + +同样地,我们可以把创建和修改数据的相关SQL语句封装成测试数据工具,以方便测试用例的使用。但是,如果你正尝试在实际项目中运用这个方法,不可避免地会遇到如何才能找到正确的SQL语句来创建和修改数据的问题。 + +因为,创建或修改一条测试数据往往会涉及很多业务表,任何的遗漏都会造成测试数据的不准确,从而导致有些测试因为数据问题而无法进行。 + +那么,现在我就提供两个思路来帮你解决这个问题: + + +手工方式。查阅设计文档和产品代码,找到相关的SQL语句集合。或者,直接找开发人员索要相关的SQL语句集合。 + +自动方式。在测试环境中,先在只有一个活跃用户的情况下,通过GUI界面操作完成数据的创建、修改,然后利用数据库监控工具获取这段时间内所有的业务表修改记录,以此为依据开发SQL语句集。 + + +需要注意的是,这两种思路的前提都是,假定产品功能正确,否则就会出现“一错到底”的尴尬局面。 + +基于数据库操作创建测试数据的最大好处是,可以创建和修改API不支持的测试数据,并且由于是直接数据库操作,执行效率会远远高于API调用方法。 + +但是,数据库操作这种方式的缺点也显而易见,数据库表操作的任何变更,都必须同步更新测试数据工具中的SQL语句。 + +但很不幸的是,在实际项目中,经常出现因为SQL语句更新不及时而导致测试数据错误的问题,而且这里的数据不准确往往只是局部错误,因此这类问题往往比较隐蔽,只有在特定的测试场景下才会暴露。 + +所以,在实际工程项目中,需要引入测试数据工具的版本管理,并通过开发流程来保证SQL的变更能够及时通知到测试数据工具团队。 + +综合运用API调用和数据库操作创建测试数据 + +你如果已经理解了基于API调用和基于数据库操作创建测试数据这两类方法,那么综合运用这两类方法,就是使得测试数据工具能够提供更多种类的业务测试数据。 + +具体来讲,当你要创建一种特定的测试数据时,你发现没有直接API支持,但是可以通过API先创建一个基本的数据,然后再通过修改数据库的方式来更新这个数据,以此来达到创建特定测试数据的要求。 + +比如,你需要创建一个已经绑定了信用卡的新用户,如果创建新用户有直接的API,而绑定信用卡需要操作数据库,那这种情况下就需要综合运用这两种方式完成测试数据工具的开发。 + +实时创建数据:On-the-fly + +GUI测试脚本中,在开始执行界面操作前,我们往往会通过调用测试数据工具实时创建测试数据,也就是On-the-fly方式。 + +这种方式不依赖被测试系统中的任何原有数据,也不会对原有数据产生影响,可以很好地从数据层面隔离测试用例,让测试用例实现“自包含”。 + +从理论上讲,On-the-fly是很好的方法,但在实际测试项目中却并不是那么回事儿,往往会存在三个问题: + + +在用例执行过程中实时创建数据,导致测试的执行时间比较长。 我曾经粗略统计过一个大型Web GUI自动化测试项目的执行时间,将近30%的时间都花在了测试数据的准备上。 + +业务数据的连带关系,导致测试数据的创建效率非常低。 比如,你需要创建一个订单数据,而这个订单必然会绑定买家和卖家,以及订单商品信息。- +如果完全基于On-the-fly模式,你就需要先实时创建买家和卖家这两个用户,然后再创建订单中的商品,最后才是创建这个订单本身。- +显然,这样的测试数据创建方式虽然是“自包含”的,但创建效率非常低,会使得测试用例执行时间变得更长,而这恰恰与互联网产品的测试策略产生冲突。 + +更糟糕的情况是,实时创建测试数据的方式对测试环境的依赖性很强。 比如,你要测试用户登录功能,基于On-the-fly方式,你就应该先调用测试数据工具实时创建一个用户,然后再用这个用户完成登录测试。- +这时,创建用户的API由于各种原因处于不可用的状态(这种情况在采用微服务架构的系统中很常见),那么这时就会因为无法创建用户,而无法完成用户登录测试。 + + +基于这三种常见问题,实际项目中还会引入Out-of-box方式(即在执行测试用例前,预先创建好测试数据)准备测试数据。 + +事先创建测试数据:Out-of-box + +Out-of-box的含义是开箱即用,也就是说,已经在被测系统中预先创建好了充足的、典型的测试数据。这些数据通常是在搭建测试环境时通过数据库脚本“预埋”在系统中的,后续的测试用例可以直接使用。 + +Out-of-box的方式有效解决了On-the-fly的很多问题,但是这种方法的缺点也很明显,主要体现在以下三个方面: + + +测试用例中需要硬编码(hardcode)测试数据,额外引入了测试数据和用例之间的依赖。 + +只能被一次性使用的测试数据不适合Out-of-box的方式。 测试用例往往会需要修改测试数据,而且有些测试数据只能被一次性使用。比如,一个商品被买下一次后就不能再用了;再比如,优惠券在一个订单中被使用后,就失效了,等等。所以如果没有很好的全局测试数据管理,很容易因为测试数据失效而造成测试失败。 + +“预埋”的测试数据的可靠性远不如实时创建的数据。 在测试用例执行过程中,经常会出现测试数据被修改的情况。比如,手动测试,或者是自动化测试用例的调试等情况。 + + +On-the-fly和Out-of-box的互补 + +基于On-the-fly和Out-of-box的优缺点和互补性,在实际的大型测试项目中,我们往往会采用两者相结合的方式,从测试数据本身的特点入手,选取不同的测试数据创建方式。 + +针对应该选择什么时机创建测试数据,结合多年的实践经验,我为你总结了以下三点: + + +对于相对稳定、很少有修改的数据,建议采用Out-of-box的方式,比如商品类目、厂商品牌、部分标准的卖家和买家账号等。 + +对于一次性使用、经常需要修改、状态经常变化的数据,建议使用On-the-fly的方式。 + +用On-the-fly方式创建测试数据时,上游数据的创建可以采用Out-of-box方式,以提高测试数据创建的效率。以订单数据为例,订单的创建可以采用On-the-fly方式,而与订单相关联的卖家、买家和商品信息可以使用Out-of-box方式创建。 + + +其实,为了更好地解决测试数据本身组合的复杂性和多样性,充分发挥测试数据工具的威力,还有很多大型企业的最佳实践值得讨论,在本专栏后面的测试数据章节,我会再为你详细介绍。 + +总结 + +今天我从创建测试数据的技术手段和时机两个方面,介绍了GUI测试数据的准备。 + +在实际测试项目中,往往需要综合运用API调用和数据库操作来创建测试数据,并且会根据测试数据自身的特点,分而治之地采用On-the-fly和Out-of-box的方式,以寻求数据稳定性和数据准备效率之间的最佳平衡。 + +思考题 + +你所在的公司是如何准备GUI测试的测试数据的?遇到了哪些问题,对应的有哪些解决方案呢? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/16脑洞大开:GUI测试还能这么玩(PageCodeGen+DataGen+Headless)?.md b/专栏/软件测试52讲/16脑洞大开:GUI测试还能这么玩(PageCodeGen+DataGen+Headless)?.md new file mode 100644 index 0000000..e69de29 diff --git a/专栏/软件测试52讲/17精益求精:聊聊提高GUI测试稳定性的关键技术.md b/专栏/软件测试52讲/17精益求精:聊聊提高GUI测试稳定性的关键技术.md new file mode 100644 index 0000000..2a632bd --- /dev/null +++ b/专栏/软件测试52讲/17精益求精:聊聊提高GUI测试稳定性的关键技术.md @@ -0,0 +1,148 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 17 精益求精:聊聊提高GUI测试稳定性的关键技术 + 不知不觉,我已经介绍完了GUI测试相关的知识点,你可以先回顾一下这些知识点,是否还有不清楚的地方,也欢迎你给我留言进行讨论。同时,我希望这些知识点,已经帮你搭建了GUI自动化测试的知识体系。 + +那么,今天我将从实际工程应用的角度,和你一起聊聊GUI测试的稳定性问题。 + +如果你所在的公司已经规模化地开展了GUI测试,那我相信你们也一定遇到过测试稳定性的问题。GUI自动化测试稳定性,最典型的表现形式就是,同样的测试用例在同样的环境上,时而测试通过,时而测试失败。 这也是影响GUI测试健康发展的一个重要障碍,严重降低了GUI测试的可信性。 + +所以,今天我分享的主题就是,如何提高GUI测试的稳定性。虽然从理论上来讲,GUI测试有可能做到100%稳定,但在实际项目中,这是一个几乎无法达到的目标。根据我的经验,如果能够做到95%以上的稳定性,就已经非常不错了。 + +要提高GUI测试稳定性,首先你需要知道到底是什么原因引起的不稳定。你必须找出尽可能多的不稳定因素,然后找到每一类不稳定因素对应的解决方案。 + +为此,根据我的实践经验,以及所遇到的场景,我为你总结了五种造成GUI测试不稳定的因素: + + +非预计的弹出对话框; + +页面控件属性的细微变化; + +被测系统的A/B测试; + +随机的页面延迟造成控件识别失败; + +测试数据问题。 + + +并且,我提供了针对这五种不稳定因素的解决思路。 + +非预计的弹出对话框 + +非预计的弹出对话框,一般包含两种场景; + + +GUI自动化测试用例执行过程中,操作系统弹出的非预计对话框, 有可能会干扰GUI测试的自动化执行。- +比如,GUI测试运行到一半,操作系统突然弹出杀毒软件更新请求、病毒告警信息、系统更新请求等对话框。这种对话框的弹出往往是难以预计的,但是一旦发生就有可能造成GUI自动化测试的不稳定。 + +被测软件本身也有可能在非预期的时间弹出预期的对话框, GUI自动化测试有可能会因此而失败。- +比如,被测软件是一个电子商务网站,你在网站上进行操作时,很可能会随机弹出“用户调查”对话框。虽然这种对话框是可知的,但是具体会在哪一步弹出却是不可预期的。而这,往往会造成GUI自动化测试的不稳定。 + + +怎么解决这类问题呢? + +先试想一下,如果你在手工测试时,遇到了这种情况,会如何处理?很简单啊,直接点击对话框上的“确认”或者“取消”按钮,关闭对话框,然后继续相关的业务测试操作。 + +对GUI自动化测试来说,也是同样的道理。具体做法是: + + +当自动化脚本发现控件无法正常定位,或者无法操作时,GUI自动化框架自动进入“异常场景恢复模式”。 +在“异常场景恢复模式”下,GUI自动化框架依次检查各种可能出现的对话框,一旦确认了对话框的类型,立即执行预定义的操作(比如,单击“确定”按钮,关闭这个对话框),接着重试刚才失败的步骤。 + + +需要注意的是:这种方式只能处理已知可能出现的对话框。而对于新类型的对话框,只能通过自动化的方式尝试点击上面的按钮进行处理。每当发现一种潜在会弹出的对话框,我们就把它的详细信息(包括对象定位信息等)更新到“异常场景恢复”库中,下次再遇到相同类型的对话框时,系统就可以自动关闭了。 + +页面控件属性的细微变化 + +如果页面控件的属性发生了变化,哪怕只是细微的变化,也会导致测试脚本的定位元素失效。 + +比如,“登录”按钮的ID从“Button_Login_001”变成了“Button_Login_888”,那么如果GUI自动化测试脚本还是按照原来的“Button_Login_001”来定位“登录”按钮,就会因为ID值的变化,定位不到它了,自动化测试用例自然就会失败。 + +如何解决这个问题呢?还是先试想一下,如果手动操作时遇到了这个问题会怎么处理,然后再把手动处理的方式用编程语言实现。 + +当“登录”按钮的ID 从“Button_Login_001”变成了 “Button_Login_888”,你手动操作时可能一眼就发现了。那你是怎么做到一眼发现的呢? + +细想一下,你会发现人的思维过程应该是这样的: + + +你发现页面上的按钮(Button)就那么几个,而且从ID中包含的关键字(Login)可以看出是“登录”按钮,再加上这个按钮的ID是“Button_Login_001”,“Button_Login_888”怎么看都是同一个对象,只是ID最后的数字发生了变化而已。 + + +现在,我来提炼一下这个定位控件的思路: + + +通过控件类型(Button)缩小了范围; + +通过属性值中的关键字(Login)进一步缩小范围; + +根据属性值变化前后的相似性,最终定位到该控件。 + + +看到这里,你得到什么启发了吗? + +采用“组合属性”定位控件会更精准,而且成功率会更高,如果能在此基础上加入“模糊匹配”技术,可以进一步提高控件的识别率。 + +“模糊匹配”是指,通过特定的相似度算法,控件属性发生细微变化时,这个控件依旧可以被准确定位。 + +目前,一些商用GUI自动化测试工具,比如UFT,已经实现了模糊匹配。通常情况下,你只需要启用“模糊匹配”选项即可。如果某个对象的定位是通过模糊匹配完成的,那么,测试报告中将会显示该信息,明确告知此次对象识别是基于模糊匹配完成的,因为GUI自动化工具并不能保证每次模糊匹配都一定正确。 + +但是,开源的GUI自动化测试框架,目前还没有现成的框架直接支持模糊匹配,通常需要你进行二次开发,实现思路是:实现自己的对象识别控制层,也就是在原本的对象识别基础上额外封装一层,在这个额外封装的层中加上模糊匹配的实现逻辑。 + +通常,我不建议把模糊匹配逻辑以硬编码的方式写在代码里,而是引入规则引擎,将具体的规则通过配置文件的方式与代码逻辑解耦。 + +被测系统的A/B测试 + +A/B测试,是互联网产品常用的一种测试方法。它为Web或App的界面或流程提供两个不同的版本,然后让用户随机访问其中一个版本,并收集两个版本的用户体验数据和业务数据,最后分析评估出最好的版本用于正式发布。 + +A/B 测试通常会发布到实际生产环境,所以就会造成生产环境中GUI自动化测试的不稳定。 + +这种问题的解决思路是,在测试脚本内部对不同的被测版本做分支处理,脚本需要能够区分A和B两个的不同版本,并做出相应的处理。 + +随机的页面延迟造成控件识别失败 + +随机的页面延迟,也是GUI测试防不胜防的。既然是随机的,也就是说我们没有办法去控制它,那有没有什么办法去减少它造成的影响呢? + +一个屡试不爽的办法就是,加入重试(retry)机制。重试机制是指,当某一步GUI操作失败时,框架会自动发起重试,重试可以是步骤级别的,也可以是页面级别的,甚至是业务流程级别的。 + +对于开源GUI测试框架,重试机制往往不是自带的功能,需要自己二次开发来实现。 + +比如,eBay的GUI自动化测试框架,分别实现了步骤级别、页面级别和业务流程级别的重试机制,默认情况下启用的是步骤级别的重试,页面级别和业务流程级别的重试可以通过测试发起时的命令行参数进行指定。 + +需要特别注意的是,对于那些会修改一次性使用数据的场景,切忌不要盲目启用页面级别和业务流程级别的重试。 + +测试数据问题 + +测试数据问题,也是造成GUI自动化测试不稳定的一个重要原因。 + +比如,测试用例所依赖的数据被其他用例修改了;再比如,测试过程中发生错误后自动进行了重试操作,但是数据状态已经在第一次执行中被修改了。 + +这样的场景还有很多,我会在后面的测试数据准备系列文章中详细展开,并分析由此引入的测试不稳定性问题的解决思路。 + +总结 + +根据我的实践经验,我归纳了五种造成GUI自动化测试不稳定的主要因素,并给出了对应的解决思路。 + + +对于非预计的弹出对话框引起的不稳定,可以引入“异常场景恢复模式”来解决。 + +对于页面控件属性的细微变化造成的不稳定,可以使用“组合属性”定位控件,并且可以通过“模糊匹配技术”提高定位识别率。 + +对于A/B测试带来的不稳定,需要在测试用例脚本中做分支处理,并且需要脚本做到正确识别出不同的分支。 + +对于随机的页面延迟造成的不稳定,可以引入重试机制,重试可以是步骤级别的,也可以是页面级别的,甚至是业务流程级别的。 + +对于测试数据引起的不稳定,我在这里没有详细展开,留到后续的测试数据准备系列文章中做专门介绍。 + + +思考题 + +在工作中,你还遇到过哪些造成GUI测试不稳定的因素,你又是如何来解决的? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/18眼前一亮:带你玩转GUI自动化的测试报告.md b/专栏/软件测试52讲/18眼前一亮:带你玩转GUI自动化的测试报告.md new file mode 100644 index 0000000..5e1434a --- /dev/null +++ b/专栏/软件测试52讲/18眼前一亮:带你玩转GUI自动化的测试报告.md @@ -0,0 +1,147 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 18 眼前一亮:带你玩转GUI自动化的测试报告 + 在GUI自动化测试系列的文章中,我围绕着GUI自动化测试进行了各种讨论:从最原始的GUI测试谈起,逐渐引入了脚本与数据的解耦,并谈论了页面对象模型,以及在此基础上的业务流程模型,接着分享了一些GUI自动化测试过程中的新技术,最后和你讨论了GUI自动化测试的稳定性问题。 + +今天,我会再和你聊聊GUI自动化测试过程中另外一个很实用的部分:GUI自动化测试报告。 + +GUI测试报告是GUI自动化测试的重要组成部分,当有任何的测试用例执行失败时,我们首先就会去分析测试报告,希望从中看到测试用例到底是在哪一步出错了,错误发生时被测系统是在哪个页面上,并且前序步骤又是哪些页面等等。 + +早期的基于视频的GUI测试报告 + +为了分析测试用例的执行过程与结果,早期就出现了基于视频的GUI测试报告。也就是说,GUI自动化测试框架会对测试执行整个过程进行屏幕录像并生成视频。 + +这种基于视频的测试报告可以提供清晰的GUI测试执行上下文,看起来也很不错。但是,这种方式主要的问题是: + + +报告的体积通常都比较大,小的几MB,大的上百MB,这对测试报告的管理和实时传输非常不利。 + +分析测试报告时,往往需要结合测试用例以及服务端的日志信息,视频报告这一点上也有所欠缺。 + + +所以,理想中的GUI测试报告应该是由一系列按时间顺序排列的屏幕截图组成,并且这些截图上可以高亮显示所操作的元素,同时按照执行顺序配有相关操作步骤的详细描述。 + +但是,早期的商业GUI自动化测试软件也只是具备最基本的顺序截图,并不具备高亮所操作元素的功能,后来商用工具厂商根据用户的实际使用反馈,逐渐完善和改进。 + +目前,商业的GUI自动化测试软件,比如使用最为广泛的UFT(就是以前的QTP),已经自带了截图以及高亮显示操作元素功能。也就是说,使用UFT执行一个GUI自动化测试用例,你无需做任何额外的工作,就能得到一份比较理想的GUI测试报告。 + +开源GUI测试框架的测试报告实现思路 + +但是,如果你使用的是开源软件,比如Selenium WebDriver,那就需要自己去实现截图以及高亮显示操作元素的功能。实现的思路通常是: + + +利用Selenium WebDriver的screenshot函数在一些特定的时机(比如,页面发生跳转时,在页面上操作某个控件时,或者是测试失败时,等等)完成界面截图功能。 + + +具体到代码实现,通常有两种方式: + + +扩展Selenium原本的操作函数; + +在相关的Hook操作中调用screenshot函数。 + + +下面,我会分别针对这两个实现方式,给出具体的示例,帮你理解并实现这个功能。 + +第一,扩展Selenium原本的操作函数实现截图以及高亮显示操作元素的功能 + +既然Selenium原生的click操作函数并不具备截图以及高亮显示操作元素的功能,那我们就来实现一个自己click函数。 + +当自己实现的click函数被调用时: + + +首先,用Javascript代码高亮显示被操作的元素,高亮的实现方式就是利用JavaScript在对象的边框上渲染一个5-8个像素的边缘; +然后,调用screenshot函数完成点击前的截图; +最后,调用Selenium原生的click函数完成真正的点击操作。 + + +那么,以后凡是需要调用click函数时,都直接调用这个自己封装的click函数,直接得到高亮了被操作对象的界面截图。 + +如图1所示,就是用这种方式产生的界面截图,图中依次显示了登录过程中每一个操作的控件,第一张高亮了“Username”的输入框,因为自动化代码会在“Username”框中输入用户名;第二张高亮了“Password”的输入框,因为自动化代码会在“Password”框中输入密码;第三张高亮了”Sign in“按钮,因为自动化代码会去点击这个按钮。 + + + +图1 GUI界面的时间顺序截图示例 + +第二,在相关的Hook操作中调用screenshot函数实现截图以及高亮显示操作元素的功能 + +其实使用Hook的方法比较简单和直观,但是你首先要理解什么是Hook。 + +Hook中文的意思是“钩子”,直接通过定义介绍什么是“钩子”会有些难以理解,那么我就通过一个实例来跟你解释一下。 + +当执行某个函数F时,系统会在执行函数F前先隐式执行一个空实现的函数,那么当你需要做一些扩展或者拦截时,就可以在这个空实现的函数中加入自定义的操作了。那么这个空实现的函数就是所谓的Hook函数。 + +这样的例子有很多,比如Java的main函数,系统在执行main函数之前会先在后台隐式执行premain函数;JUnit和TestNG,都有所谓的BeforeTest和AfterTest方法,这些都是可以在特定步骤的前后插入自定义操作的接口。 + +说到这里,你可能已经知道要怎么做了:我可以在这些Hook函数中添加截图、元素高亮,以及额外的任意操作,比如更多的详细日志输出等等。 + +另外,我在前面的文章中分享了基于业务流程的脚本封装,你可以再思考一下,如何在GUI报告中体现出业务流程的概念,这样的测试报告会具有更好的可读性。 + +比如,图2所示的GUI测试报告就显示了具体的Flow名称。这个功能,就是通过Hook函数实现的。 + +具体的实现逻辑也比较简单的,就是在Flow开始的第一个Hook函数中调用增加报告页的函数,并在这个新增的报告页中输出Flow的名字。 + + + +图2 在GUI测试报告中体现业务流程 + +上面所讲的GUI测试报告都是针对一个国家的,当面对多个国家站点的GUI测试时,事情就会变得更加复杂,你就必须去考虑全球化GUI测试报告应该如何设计。 + +全球化GUI测试报告的创新设计 + +所谓全球化测试是指,同一个业务在全球各个国家都有自己网站。比如,一些大型全球化电商企业在很多国家都有自己的站点,那么对这些站点的测试除了要关注基本的功能,以及各个国家特有的功能外,还要去验证界面布局以及翻译在上下文环境中是否合适。 + +早期的做法是,雇佣当地的测试工程师,由他们手工执行主要的业务场景测试,并验证相关的页面布局,以及翻译内容与上下文中的匹配度。在当地专门雇佣的这些测试工程师,被称为LQA。 + +显然,聘请LQA的效率非常低,主要原因是:全部测试工作都由LQA在项目后期手工执行,执行前还需要对他们进行业务培训;同时,我们需要准备非常详尽的测试用例文档,LQA也要花很大的精力去截图并完成最终的测试报告。 + +为了解决这种低效的模式,最好的解决方法就是:利用GUI自动化测试工具生成完整的测试执行过程的截图。这样,LQA就不再需要去手工执行测试用例了,而是直接分析测试报告中业务操作过程中GUI界面截图就可以了,然后发现页面布局问题或者是不恰当的翻译问题。 + +这个方案看起来已经比较完美了,LQA的工作重点也更清晰了,但这并不是最优的方案。因为这些LQA在实际工作中,还会有以下三个比较痛苦的地方: + + +需要经常在多个国家的测试报告之间来回切换去比较页面布局; + +需要频繁切换到美国网站(也就是主站)的报告,去比较翻译内容与上下文的匹配度; + +发现缺陷后,还是需要从GUI测试报告中复制截图,并用图像软件标注有问题的点,然后才能打开缺陷管理系统递交缺陷报告。 + + +为了解决这三个问题,我建议你建立以下形式的测试报告。这里有一张图片展示了一份包含多国语言比较报告的示例,听音频的用户可以点击文稿查看如图3所示。 + + + +图3 多国语言比较报告 + +报告的横向,是一个国家的业务测试顺序截图,比如图中第一行是英国网站的登录业务流程顺序截图,第二行是德国网站的登录业务流程顺序截图。报告的纵向,展示的自然就是同一界面在不同国家的形式了。 + +整个报告可以用键盘上下左右依次移动。可想而知,这样的GUI测试报告设计一定可以大幅提高LQA的效率。 + +同时,由于这个GUI测试报告是基于Web展现的,所以我们可以在测试报告中直接提供递交缺陷的按钮,一旦发现问题直接递交缺陷,同时还可以把相关截图一起直接递交到缺陷管理系统,这将更大程度地提高整体效率。 + +那么,怎么才能在技术上实现测试报告和缺陷管理系统的交互呢?其实,现今的缺陷管理系统往往都有对外暴露API接口,我们完全可以利用这些API接口来实现自己的缺陷递交逻辑。 + +这种测试报告的形式就是eBay在全球化站点测试中采用的方案,目前已经取得了很好地效果,降低了工作量的同时,还大幅度提高了全球化测试的质量。 + +总结 + +好了,希望上面的测试报告设计方法可以让你有眼前一亮的感觉。接下来,我总结一下今天的主要知识点。 + +早期基于视频的GUI测试报告由于体积较大,而且不能比较方便地和日志适配,所以并不是最好的解决方案。理想的GUI测试报告应该是由一系列按时间顺序的屏幕截图组成,并且可以在这些截图上高亮你所操作的元素,同时按照执行时序配有相关操作步骤的详细描述。 + +商业GUI自动化测试框架的GUI测试报告已经做得非常成熟,通常不需要做额外的定制或者开发。但是开源GUI自动化测试框架的GUI测试报告往往需要自己来开发,主要使用了扩展Selenium原本的操作函数的方式以及Hook函数来实现。 + +最后,我介绍了eBay面对全球化测试过程中GUI测试报告的创新设计,希望你也可以借鉴这种方法。 + +思考题 + +如果自己去开发GUI测试报告的功能,你还能想到其他更多实用的功能吗?你又是如何实现这些功能的? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/19真实的战场:如何在大型项目中设计GUI自动化测试策略.md b/专栏/软件测试52讲/19真实的战场:如何在大型项目中设计GUI自动化测试策略.md new file mode 100644 index 0000000..35b9545 --- /dev/null +++ b/专栏/软件测试52讲/19真实的战场:如何在大型项目中设计GUI自动化测试策略.md @@ -0,0 +1,142 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 19 真实的战场:如何在大型项目中设计GUI自动化测试策略 + 在前面的文章中,我介绍过GUI自动化测试的页面对象模型和业务流程封装等相关知识,也提到过大型全球化电商网站的GUI自动化测试,那如何把已经学到的GUI测试理论知识用到大型全球化电商网站的测试中呢? + +今天,我的分享就从“实战”这个角度展开,带你看看实际的大型全球化电商网站的GUI自动化测试如何开展。这场实战,我将从以下两个方面展开: + + +测试策略如何设计?这一点,我会根据亲身经历的实际项目,和你探讨GUI测试的分层测试策略。 + +测试用例脚本如何组织?需要注意的是,对于这个问题,我不是要和你讨论测试用例的管理,而是要讨论测试用脚本的管理。比如,当需要组装上层的端到端(E2E)测试时,如何才能最大程度地重用已有的页面对象以及业务流程(business flow)。 + + +如果你所在的企业或者项目正在大规模开展GUI测试,并且准备使用页面对象模型以及业务流程封装等最佳实践的话,那么,你很可能会遇到本文所描述的问题并且迫切需要相应的解决办法。 + +大型全球化电商网站的前端模块划分 + +在正式讨论大型全球化电商网站的GUI自动化测试策略设计之前,我先简单介绍一下电商网站的前端架构,为避免过多的技术细节引起不必要的干扰,我只会概要性地介绍与GUI自动化测试密切相关的部分。 + +由于大型全球化电商网站的业务极其庞大,所以前端架构也要按照不同的业务模块来划分,比如用户管理模块、商户订单管理模块、商户商品管理模块等等。 + +当然由于这些前端模块都会使用项目自己封装的组件库,比如自定义开发的列表组件、登录组件、信用卡组件等,我们通常会把自定义开发的这些所有组件都放在一个“公共组件库”中,为前端模块提供依赖。 + +所以,从代码库(Repository)的角度来看,各个前端模块都有各自独立的代码库,除此之外还会有一个公共组件的代码库。 + +大型全球化电商网站的GUI自动化测试策略设计 + +了解了大型全球化电商网站前端模块的划分后,我们再来看看它的GUI自动化测试策略是如何设计的。 + +总体来看,对大型网站来讲,GUI自动化测试往往应该做得比较轻量级,而不应该把大量的功能测试,以及功能的组合测试放在GUI自动化测试中,正如我在第11篇文章《互联网产品的测试策略应该如何设计?》中谈到的,GUI测试通常只覆盖最核心且直接影响主营业务流程的E2E场景。 + +但同时,GUI的验证一定不是在系统全部完成后才真正开展的,也应该是分阶段、分层次来设计制定测试策略的,那么接下来我也将会按照自底向上的顺序分层次介绍GUI自动化的测试策略。 + +首先,要从前端组件的级别来保证质量,也就是需要对那些自定义开发的组件进行完整全面的测试。 + +公共组件库会被很多上层的前端模块依赖,它的质量将直接影响这些上层模块的质量,所以我们往往会对这些公共组件进行严格的单元测试。最常用的方案是:基于Jest开展单元测试,并考量JavaScript的代码覆盖率指标。 + +Jest是由Facebook发布的,是一个基于Jasmine的开源JavaScript单元测试框架,是目前主流的JavaScript单元测试方案。 + +完成单元测试后,往往还会基于被测控件构建专用的测试页面,在页面层面再次验证控件相关的功能和状态。这部分测试工作也需要采用自动化的形式实现,具体的做法是: + + +先构建一个空页面,并加入被测控件,由此可以构建出一个包含被测控件的测试页面,这个页面往往被称为Dummy Page; + +从黑盒的角度出发,在这个测试页面上通过手工和自动化的方式操作被测控件,并验证其功能的正确性。 + + +对于自动化的部分,需要基于GUI自动化测试框架开发对应的测试用例。这些测试用例,往往采用和GUI E2E一样的测试框架,也是从黑盒的角度来对被测控件做功能验证。 + +其次,每一个前端模块,都会构建自己的页面对象库,并且在此基础上封装开发自己的业务流程脚本。这些业务流程的脚本,可以组装成每个前端模块的测试用例。 + +以用户管理模块为例,测试用例的组装过程如下: + + +首先,把用户管理模块中涉及到的所有页面,比如登录页面、用户注册页面等,按照页面对象模型的要求写成Page类; +然后,利用这些Page类封装业务流程脚本,比如用户登录流程,用户注册流程等; +最后,在GUI测试用例脚本中,调用封装好的业务流程脚本构成该模块的GUI测试用例。 + + +在这个阶段,测试用例需要完整覆盖该模块的所有业务逻辑以及相关的功能测试点,但是并不会实现所有测试用例的自动化。 + +自动化测试用例的原则,通常是:优先选取业务关键路径以及Happy Path作为自动化测试的范围。在资源充裕的情况下,我们希望这个阶段的自动化率可以达到70%-80%。 所以,前端模块的质量保证主要依赖这部分测试。 + +如果你比较细心,一定还记得我在之前的文章中有提到过,“GUI的自动化测试往往只覆盖最核心且直接影响主营业务流程的E2E场景“,并且”GUI测试遵循“手工测试为主,自动化为辅”的策略,而这里又建议说理想的自动化率应该达到70%~80%,是不是有点前后矛盾的感觉。 + +其实,这是两个层面的测试,这里70%-80%的GUI自动化覆盖率是针对模块级别的要求;而“自动化测试为辅,手工为主,以及只覆盖核心业务场景”针对的是系统级别的E2E测试。这里容易引起混淆的点是模块测试和系统级别E2E测试都是属于GUI自动化测试的范畴。 + +最后,组合各个前端模块,并站在终端用户的视角,以黑盒的方式使用网站的端到端(E2E)测试。 这部分的测试主要分为两大部分: + + +一部分是,通过探索式测试的方法手工执行测试,目标是尽可能多地发现新问题; +另一部分是,通过GUI自动化测试执行基本业务功能的回归测试,保证网站核心业务相关的所有功能的正确性。 + + +虽然这部分端到端GUI测试用例的绝对数量不多,往往是几百个的规模,但是对于保证最终网站的质量却起着非常关键的作用。 + +可以这样说,如果这些端到端的GUI自动化测试用例100%通过,那么上线后基本业务功能的质量就不会有大问题。所以,这部分测试工作的重要性不言而喻。 + +那么,接下来的问题是,应该由谁来开发这部分端到端的GUI自动化测试用例呢? + +每个前端模块都会有对应的Scrum团队,他们会负责开发该模块的页面对象模型、业务流程脚本以及测试用例。而端到端的GUI自动化测试不隶属于任何一个Scrum团队。 + +这种情况下,最好的做法就是:成立一个专门的测试团队,负责这种系统级别的GUI测试。这样的团队,往往被称为E2E测试团队。 + +很显然,如果由E2E团队从无到有地开发这部分GUI自动化测试的脚本,效率低下。而且,这部分测试会涉及很多前端模块,当各个前端模块的需求、业务流程以及页面实现有任何变动时,E2E团队都很难做到及时更新。 + +所以,解决这个问题的最佳实践就是:E2E团队应该尽可能地利用各个模块已有的页面对象和业务流程脚本,组装端到端的GUI测试。 + +这样一方面最大程度地减少了重复工作,另一方面可以把各个模块的变更及时反映到端到端的GUI测试中,因为端到端的GUI测试用例是直接调用各个模块的页面对象和业务流程脚本,而这些页面对象和业务流程脚本都是由每个模块自己的Scrum团队维护的。 + +而为了能够在端到端的GUI自动化测试中,复用各个模块的页面对象和业务流程脚本,我们就必须考虑的问题,也就是我今天要和你探讨的第二个话题:GUI自动化测试脚本应该如何组织? + +大型全球化电商网站的GUI自动化测试脚本管理 + +原有的方案,不能解决端到端的GUI自动化测试复用各个模块的页面对象和业务流程脚本的问题,在不断的实践中,我总结了一个如图1所示的脚本组织结构来解决这个问题。 + + + +图1 大型全球化电商网站的GUI自动化测试脚本管理 + +也就是说,将各个模块的页面对象和业务流程脚本放在各自的代码库中,并引入页面对象和业务流程脚本的版本管理机制,通常采用页面对象和业务流程脚本的版本号和开发版本号保持一致的方案。 + +比如模块A的版本号是V1.0.0,那么对应的页面对象库和业务流程脚本的版本号也应该是V1.0.0。 + +在端到端的GUI自动化测试脚本中,引用各个模块正确的页面对象和业务流程脚本的版本号,测试用例代码就可以直接调用模块的页面对象和业务流程脚本了。 + +具体在测试项目中,模块版本的依赖往往是用POM来配置的,如图2展示了一个典型测试项目的POM文件中的版本依赖关系,其中引用了两个模块,appcommon模块对应的就是上文提到的“公共组件库”,而app.buy对应的就是具体依赖的前端模块。 + +由于这只是一个示例,所以我只保留了两个依赖模块,实际的端到端GUI测试项目往往会包含大量的模块依赖。 + + + +图2 典型测试项目的POM文件中的版本依赖关系 + +在这种管理机制下,E2E团队不需要重复开发任何的页面对象和业务流程脚本,而且可以始终保证与各个模块的最新实现同步,同时端到端的GUI测试用例脚本也会比较稳定,不会因为各个模块的改动而频繁地修改。 + +这样一来,E2E团队就会有更多的时间和精力去设计并执行探索式测试,发现更多的潜在缺陷,形成良性循环。 + +总结 + +我从实战的角度,介绍了大型全球化电商网站GUI测试的策略设计以及测试脚本管理的问题: + +首先,要从前端组件的级别来保证质量,也就是需要对那些自定义开发的组件进行完整全面的测试。通常前端组件会基于Jest做比较严格的单元测试。 + +其次,每一个前端模块,都会构建自己的页面对象库,并且在此基础上封装开发自己的业务流程脚本。这些业务流程的脚本,可以组装成每个前端模块的测试用例。 + +最后,把各个前端模块组合在一起之后,站在终端用户的视角以黑盒的方式使用网站的端到端的测试。端到端的测试应该尽可能多地重用各个模块的页面对象库和业务流程脚本来完成。 + +而为了能够在端到端的GUI自动化测试中,复用各个模块的页面对象和业务流程脚本,我建议的方案是:对各个前端业务模块的页面对象库和业务流程脚本,实施版本化管理机制。 + +思考题 + +你所在的公司或者项目团队,是否已经或者正计划开展E2E GUI测试?开展过程中,遇到过什么难题,你们又是如何解决的? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/20与时俱进:浅谈移动应用测试方法与思路.md b/专栏/软件测试52讲/20与时俱进:浅谈移动应用测试方法与思路.md new file mode 100644 index 0000000..d96570b --- /dev/null +++ b/专栏/软件测试52讲/20与时俱进:浅谈移动应用测试方法与思路.md @@ -0,0 +1,240 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 20 与时俱进:浅谈移动应用测试方法与思路 + 你好,我是茹炳晟。我今天分享的主题是“与时俱进:浅谈移动应用测试方法与思路”。 + +在GUI自动化测试这个系列,我讲了很多基于浏览器的业务测试的内容,你可能会说,现在移动App大行其道,对移动应用测试的方法和思路才更重要。 + +确实,现今移动互联网蓬勃发展,很多互联网应用的流量大部分已经不是来自于传统PC端的Web浏览器,而是来自于移动端。 + +图1展示了最近12个月来亚洲地区的流量分布统计,可见,现如今将近三分之二的流量是来自于手机端的,剩下的三分之一来自于传统PC端,还有很少一部分流量来自于平板电脑(其实这部分也可以归为移动端)。 + + + +图1 Mobile端和PC端流量统计数据 + +但是,在我看来无论是移动端测试还是PC端测试,都属于GUI测试的范畴,所以基本的测试思路,比如基于页面对象封装和基于业务流程封装的思想是相通的,之前介绍的那些脚本分层的实现方法也都同样适用于移动端的GUI测试。 + +与此同时,移动端应用的测试也会因为其自身特点,有一些独特的测试方法与思路。严格来讲,移动端应用又可以进一步细分为三大类:Web App、Native App和Hybrid App。所以,我今天分享的内容重点就是,这三类移动应用的测试方法,以及移动专项测试的思路与方法。 + +三类移动应用的特点 + +Web App指的是移动端的Web浏览器, 其实和PC端的Web浏览器没有任何区别,只不过Web浏览器所依附的操作系统不再是Windows和Linux了,而是iOS和Android了。 + +Web App采用的技术主要是,传统的HTML、JavaScript、CSS等Web技术栈,当然现在HTML5也得到了广泛的应用。另外,Web App所访问的页面内容都是放在服务器端的,本质上就是Web网页,所以天生就是跨平台的。 + +Native App指的是移动端的原生应用, 对于Android是apk,对于iOS就是ipa。Native App是一种基于手机操作系统(iOS和Android),并使用原生程序编写运行的第三方应用程序。 + +Native App的开发,Android使用的语言通常是Java,iOS使用的语言是Objective-C。通常来说,Native App可以提供比较好的用户体验以及性能,而且可以方便地操作手机本地资源。 + +Hybrid App(俗称:混血应用),是介于Web App和Native App两者之间的一种App形式。 + +Hybrid App利用了Web App和Native App的优点,通过一个原生实现的Native Container展示HTML5的页面。更通俗的讲法可以归结为,在原生移动应用中嵌入了Webview,然后通过该Webview来访问网页。 + +Hybrid App具有维护更新简单,用户体验优异以及较好的跨平台特性,是目前主流的移动应用开发模式。 + + + +图2 三类移动应用的架构原理 + +三类不同移动应用的测试方法 + +了解了Web App、Native App和Hybrid App这三类应用的特性,接下来,我就跟你说说它们的测试方法。 + +好了,我们已经知道了移动应用的三个主要种类,接下来我们从测试的角度再来看看这三类不同的移动应用。 + +对于Web App,显然其本质就是Web浏览器的测试,我在前面文章中介绍的所有GUI自动化测试的方法和技术,比如数据驱动、页面对象模型、业务流程封装等,都适用于Web App的测试。 + +如果你的Web页面是基于自适应网页设计(即符合Responsive Web设计的规范),而且你的测试框架如果支持Responsive Page,那么原则上你之前开发的运行在PC Web端的GUI自动化测试用例,不做任何修改就可以直接在移动端的浏览器上直接执行,当然运行的前提是你的移动端浏览器必须支持Web Driver。 + +其中,自适应网页设计(Responsive Web Design)是指,同一个网页能够自动识别屏幕分辨率、并做出相应调整的网页设计技术。比如,图3所示的例子就是同一个网页在不同分辨率下的不同展示效果。 + + + +图3 自适应网页设计实例 + +对Native App的测试,虽然不同的平台会使用不同的自动化测试方案(比如,iOS一般采用XCUITest Driver,而Android一般采用UiAutomator2或者Espresso等),但是数据驱动、页面对象以及业务流程封装的思想依旧适用,你完全可以把这些方法应用到测试用例设计中。 + +对Hybrid App的测试,情况会稍微复杂一点,对Native Container的测试,可能需要用到XCUITest或者UiAutomator2这样的原生测试框架,而对Container中HTML5的测试,基本和传统的网页测试没什么区别,所以原本基于GUI的测试思想和方法都能继续适用。 + +唯一需要注意的是,Native Container和Webview分别属于两个不同的上下文(Context),Native Container默认的Context为“NATIVE APP”,而Webview默认的Context为“WEBVIEW_+被测进程名称”。 + +所以,当需要操作Webview中的网页元素时,需要先切换到Webview的Context下,如图4所示代码就完成了这一切换操作。 + + + +图4 Hybrid App中切换Context的代码示例 + +如此看来,移动端的测试除了使用的测试框架不同以外,测试设计本身和GUI测试有异曲同工之妙,似乎并没有什么新的内容,那真的是这样吗? + +答案显然是否定的。 + +移动应用专项测试的思路和方法 + +对于移动应用,顺利完成全部业务功能测试往往是不够的。如果你的关注点只是业务功能测试,那么,当你的移动应用被大量用户安装和使用时,就会暴露出很多之前完全没有预料到的问题,比如: + + +流量使用过多; +耗电量过大; +在某些设备终端上出现崩溃或者闪退的现象; +多个移动应用相互切换后,行为异常; +在某些设备终端上无法顺利安装或卸载; +弱网络环境下,无法正常使用; +Android环境下,经常出现ANR(Application Not Responding); +… + + +这样的问题还有很多,为了避免或减少此类情况的发生,所以移动应用除了进行常规的功能测试外,通常还会进行很多移动应用所特有的专项测试。 + +今天这篇文章,我就从交叉事件测试、兼容性测试、流量测试、耗电量测试、弱网络测试、边界测试这6个最主要的专项测试来展开。 + +第一,交叉事件测试 + +交叉事件测试也叫中断测试,是指App执行过程中,有其他事件或者应用中断当前应用执行的测试。 + +比如,App在前台运行过程中,突然有电话打进来,或者收到短信,再或者是系统闹钟等等情况。所以,在App测试时,就需要把这些常见的中断情况考虑在内,并进行相关的测试。 + +注意,此类测试目前基本还都是采用手工测试的方式,并且都是在真机上进行,不会使用模拟器。 + +首先,采用手工测试的原因是,此类测试往往场景多,而且很多事件很难通过自动化的方式来模拟,比如呼入电话、接收短信等,这些因素都会造成自动化测试的成本过高,得不偿失,所以工程实践中,交叉事件测试往往全是基于手工的测试。 + +其次,之所以采用真机,是因为很多问题只会在真机上才能重现,采用模拟器测试没有意义。 + +交叉事件测试,需要覆盖的场景主要包括: + + +多个App同时在后台运行,并交替切换至前台是否影响正常功能; +要求相同系统资源的多个App前后台交替切换是否影响正常功能,比如两个App都需要播放音乐,那么两者在交替切换的过程中,播放音乐功能是否正常; +App运行时接听电话; +App运行时接收信息; +App运行时提示系统升级; +App运行时发生系统闹钟事件; +App运行时进入低电量模式; +App运行时第三方安全软件弹出告警; +App运行时发生网络切换,比如,由Wifi切换到移动4G网络,或者从4G网络切换到3G网络等; +… + + +其实你可以发现,这些需要覆盖的场景,也是我们今后测试的测试用例集,每一场景都是一个测试用例的集合。 + +第二,兼容性测试 + +兼容性测试顾名思义就是,要确保App在各种终端设备、各种操作系统版本、各种屏幕分辨率、各种网络环境下,功能的正确性。常见的App兼容性测试往往需要覆盖以下的测试场景: + + +不同操作系统的兼容性,包括主流的Andoird和iOS版本; +主流的设备分辨率下的兼容性; +主流移动终端机型的兼容性; +同一操作系统中,不同语言设置时的兼容性; +不同网络连接下的兼容性,比如Wifi、GPRS、EDGE、CDMA200等; +在单一设备上,与主流热门App的兼容性,比如微信、抖音、淘宝等; +… + + +兼容性测试,通常都需要在各种真机上执行相同或者类似的测试用例,所以往往采用自动化测试的手段。 同时,由于需要覆盖大量的真实设备,除了大公司会基于Appium + Selenium Grid + OpenSTF去搭建自己的移动设备私有云平台外,其他公司一般都会使用第三方的移动设备云测平台完成兼容性测试。 + +第三方的移动设备云测平台,国外最知名的是SauceLab,国内主流的是Testin。 + +第三,流量测试 + +由于App经常需要在移动互联网环境下运行,而移动互联网通常按照实际使用流量计费,所以如果你的App耗费的流量过多,那么一定不会很受欢迎。 + +流量测试,通常包含以下几个方面的内容: + + +App执行业务操作引起的流量; +App在后台运行时的消耗流量; +App安装完成后首次启动耗费的流量; +App安装包本身的大小; +App内购买或者升级需要的流量。 + + +流量测试,往往借助于Android和iOS自带的工具进行流量统计,也可以利用tcpdump、Wireshark和Fiddler等网络分析工具。 + +对于Android系统,网络流量信息通常存储在/proc/net/dev目录下,也可以直接利用ADB工具获取实时的流量信息。另外,我还推荐一款Android的轻量级性能监控小工具Emmagee,类似于Windows系统性能监视器,能够实时显示App运行过程中CPU、内存和流量等信息。 + +对于iOS系统,可以使用Xcode自带的性能分析工具集中的Network Activity,分析具体的流量使用情况。 + +但是,流量测试的最终目的,并不是得到App的流量数据,而是要想办法减少App产生的流量。虽然,减少App消耗的流量不是测试工程师的工作,但了解一些常用的方法,也将有助于你的测试日常工作: + + +启用数据压缩,尤其是图片; +使用优化的数据格式,比如同样信息量的JSON文件就要比XML文件小; +遇到既需要加密又需要压缩的场景,一定是先压缩再加密; +减少单次GUI操作触发的后台调用数量; +每次回传数据尽可能只包括必要的数据; +启用客户端的缓存机制; +… + + +第四,耗电量测试 + +耗电量也是一个移动应用能否成功的关键因素之一。 + +在目前的生态环境下,能提供类似服务或者功能的App往往有很多,如果在功能类似的情况下,你的App特别耗电、让设备发热比较严重,那么你的用户一定会卸载你的App而改用其他App。最典型的就是地图等导航类的应用,对耗电量特别敏感。 + +耗电量测试通常从三个方面来考量: + + +App运行但没有执行业务操作时的耗电量; +App运行且密集执行业务操作时的耗电量; +App后台运行的耗电量。 + + +耗电量检测既有基于硬件的方法,也有基于软件的方法。我所经历过的项目都是采用软件的方法,Android和iOS都有各自自己的方法: + + +Android通过adb命令“adb shell dumpsys battery”来获取应用的耗电量信息; +iOS通过Apple的官方工具Sysdiagnose来收集耗电量信息,然后,可以进一步通过Instrument工具链中的Energy Diagnostics进行耗电量分析。 + + +第五,弱网络测试 + +与传统桌面应用不同,移动应用的网络环境比较多样,而且经常出现需要在不同网络之间切换的场景,即使是在同一网络环境下,也会出现网络连接状态时好时坏的情况,比如时高时低的延迟、经常丢包、频繁断线,在乘坐地铁、穿越隧道,和地下车库的场景下经常会发生。 + +所以,移动应用的测试需要保证在复杂网络环境下的质量。具体的做法就是:在测试阶段,模拟这些网络环境,在App发布前尽可能多地发现并修复问题。 + +在这里,我推荐一款非常棒的开源移动网络测试工具:Facebook的Augmented Traffic Control(ATC)。 + +ATC最好用的地方在于,它能够在移动终端设备上通过Web界面随时切换不同的网络环境,同时多个移动终端设备可以连接到同一个Wifi,各自模拟不同的网络环境,相互之间不会有任何影响。也就是说,只要搭建一套ATC就能满足你所有的网络模拟需求。 + +如果你对ATC感兴趣,可以在它的官方网站找到详细的使用说明。 + +第六,边界测试 + +边界测试是指,移动App在一些临界状态下的行为功能的验证测试,基本思路是需要找出各种潜在的临界场景,并对每一类临界场景做验证和测试。 主要的场景有: + + +系统内存占用大于90%的场景; +系统存储占用大于95%的场景; +飞行模式来回切换的场景; +App不具有某些系统访问权限的场景,比如App由于隐私设置不能访问相册或者通讯录等; +长时间使用App,系统资源是否有异常,比如内存泄漏、过多的链接数等; +出现ANR的场景; +操作系统时间早于或者晚于标准时间的场景; +时区切换的场景; +… + + +总结 + +好了,最后我来总结一下今天的主要的知识点: + +移动应用根据技术架构的不同,主要分为Web App、Native App和Hybrid App三大类,这三类应用的测试方法本质上都属于GUI测试的范畴。 + +从业务功能测试的角度看,移动应用的测试用例设计和传统PC端的GUI自动化测试策略比较类似,只是测试框架不同,数据驱动、页面对象模型和业务流程封装依旧适用; + +各种专项测试是移动应用的测试重点,也有别于传统GUI测试。专项测试包括:交叉事件测试、兼容性测试、流量测试、耗电量测试、弱网络测试和边界测试。 + +思考题 + +请你谈谈对移动应用测试的看法,你所在的企业,是如何开展移动测试的?你们又涉及了哪些类型的专项测试? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/21移动测试神器:带你玩转Appium.md b/专栏/软件测试52讲/21移动测试神器:带你玩转Appium.md new file mode 100644 index 0000000..0dfaf4d --- /dev/null +++ b/专栏/软件测试52讲/21移动测试神器:带你玩转Appium.md @@ -0,0 +1,265 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 21 移动测试神器:带你玩转Appium + 在上一篇文章中,我介绍了Web App、Native App和Hybrid App三种不同类型的移动应用以及对应的测试设计方法,也介绍了移动应用所特有的专项测试知识。 + +今天,我就以移动应用的自动化测试为主题,介绍目前主流的移动应用自动化测试框架Appium。Appium 是一个开源的自动化测试框架,支持iOS和Android上Web App、Native App和Hybrid App的自动化测试。 + +由于基于Appium的移动应用环境搭建相对复杂,虽然网上也有不少教程,但是知识点都比较零碎,而且大多都是基于早期版本的示例,所以我会使用最新版本的Appium Desktop 1.6.2和Appium Server 1.8.1来展开今天的内容: + + +首先,我会展示如何在Mac环境下一步一步地搭建Appium测试环境; +接下来,我以iOS为例,实际开发两个测试用例,一个是Native App的测试用例,另一个是Web App的测试用例(因为Hybird App的测试用例其实是类似的,Native App的壳,Web App的内容,所以就不再单独举例子了); +然后,我会在iOS的模拟器上实际执行这两个测试用例(之所以选择iOS模拟器,而不用iOS真机做例子,是因为iOS真机的测试需要用到Apple开发者账号,还需要对被测应用进行签名等,会在环境搭建过程中引入很多额外步骤,而这些步骤对于讲解Appium并没有直接的关系); +最后,当你已经通过实际使用对Appium形成感性认识后,我再来简单介绍一下Appium的内部原理,让你做到知其然知其所以然。 + + +移动应用的自动化测试需求 + +在开始设计测试用例前,我们首先需要明确要开发的这两个自动化测试用例的具体测试需求。 + + +Native App的测试用例,被测App我选用了Appium官方的示例App,被测App的源代码可以通过“https://github.com/appium/ios-test-app” 下载,然后在Xcode中编译打包成TestApp.app。- +具体的测试需求是输入两个数字,然后点击“Compute Sum”验证两个数字相加后的结果是否正确。 + +Web App的测试用例,具体需求是在iPhone上打开Safari浏览器,访问Appium的官方主页“http://appium.io”,然后验证主页的标题是否是“Appium: Mobile App Automation Made Awesome”。 + + + + +图1 Native App和Web App的GUI界面示例 + +接下来,我将从最初的环境搭建开始,和你来一起开发iOS上的Native App和Web App的测试用例。首先我们看一下iOS的环境搭建,如果你之前没有接触过这部分内容,你可以跟着我的步骤一步一步来做;而如果你已经比较熟悉Xcode的话,可以跳过这部分内容,直接从“Appium环境搭建”部分看起。 + +iOS环境搭建 + +在正式搭建Appium环境前,我们先来搭建iOS开发环境: + + +首先,下载安装Xcode; +然后,在Xcode中下载iOS的模拟器; +接着,使用Xcode编译打包被测试App; +最后,在iOS的模拟器中尝试手工执行这两个测试用例。 + + +在iOS模拟器中,手动执行测试用例的具体操作步骤如下: + + +启动Xcode,导入ios-test-app下的TestApp.xcodeproj项目。 + +在Xcode中,打开“Preferences”中的“Components”,完成iOS 10.0 Simulator的下载。 + +在Xcode的“General”页面,将TestApp的“Deployment Target”设置为10.0,并且将“Devices”设置为“iPhone”,如图2所示。 + + + + +图2 TestApp的General配置 + + +在Xcode中编译运行TestApp,之后系统会自动启动iPhone模拟器,自动完成TestApp的安装,并在iPhone模拟器中自动启动TestApp。 + +在TestApp中手动执行自定义的加法测试用例。 + +退出TestApp,然后打开Safari浏览器,在Safari中执行访问Appium官方主页的测试用例。 + + +至此,你已经搭建好了iOS开发环境,并且成功编译打包了TestApp。接下来,我们再一起来搭建Appium测试环境,并尝试在Appium中开发上述的两个测试用例。 + +Appium测试环境搭建 + +通过Appium的官方网站下载并安装最新版本的Appium,截止本文写作的时间,最新版本是Appium-1.6.2.dmg。 + +需要注意的是,早期版本和网上很多教程都建议用命令行的形式启动Appium Server,但在这里我是想强调的是,你完全可以通过界面启动(在Launchpad中找到Appium的图标,点击即可启动),而且新版本的Appium也推荐这个启动方式。通过界面启动,是目前最简单直接的方式。 + +然后,你需要用命令行“npm install -g appium-doctor”安装Appium的环境诊断工具appium-doctor,用于检查Appium所依赖的相关环境变量以及其他安装包是否都已经配置好了。如果还没有,就需要逐个安装,并根据appium-doctor的提示配置环境变量。 + +这里,Appium最主要的依赖项主要有:Java、Node.js、Xcode、Carthage、Android SDK、adb等。如果你所有的环境依赖都正常配置的话,你就会看到appium-doctor返回这样一个截图,如图3所示。 + + + +图3 正常配置环境依赖后,appium-doctor返回的截图 + +按照上面的步骤,配置好Appium的环境依赖后,就可以继续启动Appium Server了。 + +Appium Inspector的使用 + +为了后续测试用例的顺利执行,我们可以先来熟悉一下Appium Inspector的使用。Appium Inspector主要是用来协助对界面元素进行定位的工具。 + +首先,我们来看看如何使用Appium Inspector启动iPhone的模拟器,并在模拟器上运行TestApp,以及如何通过Inspector定位TestApp界面上的元素(了解元素的定位是后续开发自动化脚本的基础)。具体的操作过程如下。 + + +通过Appium Server的“Start Inspector Session”按钮,进入Session配置界面。 + + + + +图4 点击“Start Inspector Session”按钮打开Session配置界面 + + +在Session配置界面完成必要参数的配置。这里你需要根据选用的移动设备操作系统、模拟器/真机等具体情况来完成参数配置工作。需要配置的参数主要包括:platformName、platformVersion、DeviceName、automationName和app。- +其中,automationName,指自动化测试框架的名称,这里采用了XCUITest;app指被测Native App的安装包路径,这里使用之前Xcode打包生成的TestApp.app,这样启动模拟器时,就会自动把TestApp.app安装到模拟器中。- +其他参数的配置非常简单,我就不再一一展开了。 + + + + +图5 Session配置界面 + + +完成配置后,点击Session界面的“Start Session”按钮,启动iPhone模拟器,并在iPhone模拟器中启动TestApp,同时还会打开Inspector窗口。如图6所示。 + + + + +图6 启动Session后的Inspector窗口 + + +在Inspector窗口,我们可以利用“Select Elements”功能,通过点击元素显示Native App上的元素定位信息。如图7所示。 + + + + +图7 “Select Elements”功能示例 + + +在Inspector窗口,可以通过“Recording”功能生成不同语言的自动化脚本。比如在启用了“Recording”功能后,点击“Compute Sum”按钮,就会生成如图8所示的自动化脚本片段。 + + + + +图8 “Recording”功能示例 + +了解了如何通过Inspector获取元素定位信息的方法之后,我们就来正式开发基于Appium的第一个Web App和第一个Native App的测试用例。 + +基于Appium开发你的第一个Native App的测试用例 + +第一步,建立一个空的Maven项目,然后在POM文件中加入如图9所示的依赖。 + +在这个案例里面,我们会使用TestNG组织测试用例,所以代码的第14行加入了TestNG的依赖。 + +第19行的java-client是关键,java-client的作用是利用Java代码将测试用例中的操作步骤发送给Appium Server,然后由Appium Server自动完成这些操作。 + +目前Appium支持多种编程语言,每种语言都有自己的client,比如这里使用Java语言,所以引入了java-client;如果你使用Python语言,那么就需要引用python-client。 + + + +图9 POM文件加入TestNG和java-client的依赖 + +第二步,创建一个类,并命名为“iOS_NativeApp_DemoTest”,然后按照如图10所示的代码实现这个class。 + +注意,这里的代码是真实的可执行Java代码,你可以直接拿去使用。 + + + +图10 Native App测试用例实例 + + +代码第21行的@BeforeTest,第38行的@AfterTest,以及第44行的@Test,都是利用了TestNG的annotation对函数进行标注。- +标有@Test的函数是真正的测试主体,所有测试相关的步骤都放在这个函数中;- +标有@ BeforeTest的函数会在@Test函数之前执行测试的相关准备工作,图中的代码用这个函数完成了DesiredCapabilities的设置,并用该Capabilities构造了iosdriver;- +标有@ AfterTest的函数在@Test函数执行结束后执行,主要用于环境的清理和收尾,图示的代码用这个函数完成了iosdriver的退出操作。 +代码的第24-33行构造了DesiredCapabilities对象,并对APPIUM_VERSION、PLATFORM_VERSION、PLATFORM_NAME、AUTOMATION_NAME、DEVICE_NAME和APP等参数进行了设置。其中APP的值是被测Native App安装包的绝对路径。 +代码的第46-58行是测试用例的主体部分,主要分为三部分:- +第47-50行通过iosdriver的findElementByAccessibilityId方法定义了页面上的四个元素,分别是输入参数框A、输入参数框B、计算按钮和加法结果显示框。代码中具体的AccessibilityId可以通过Inspector获取。- +第53-55行通过自定义元素的操作执行加法运算。- +第58行通过断言方法assertEquals验证加法运算的结果。 + + +第三步,为了运行这个TestNG的测试用例,我们需要再添加一个testng.xml文件, 具体内容如图11所示。 + + + +图11 testng.xml文件示例 + +第四步,在保证Appium Server已经启动的情况下,就可以运行testng.xml执行测试了。 测试开始后,首先会自动启动基于iOS 10.0的iPhone 7模拟器,然后依次自动完成WebDriverAgent(WDA)和被测Native App的安装。 + +WDA是由Facebook开源的支持iOS自动化的代理工具,其底层通过XCUItest实现自动化。 + +接着,就会自动运行被测Native App,并根据@Test函数中定义的步骤完成自动化测试的步骤和验证。 + +到此,我们的第一个基于Appium的Native App自动化测试用例就设计完了。 + +基于Appium开发你的第一个Web App的测试用例 + +有了Native App测试用例的设计基础,再来实现一个基于Appium的Web App自动化测试用例就简单得多了。 + +第一步,在上述的Maven项目中再创建一个类,并命名为“iOS_WebApp_DemoTest”,然后按照如图12所示的代码实现这个类。 + + + +图12 Web App测试用例实例 + +代码的整体结构和上述Native App测试用例的完全一致,只有一个地方需要特别注意:代码的第29行,由于Web App是基于浏览器的测试,所以这里不需要指定App这个参数,而是直接用BROWSER_NAME指定浏览器的名字即可。 + +对于测试用例的主体部分,也就是代码的第45-47行就比较简单了,首先打开Safari浏览器并访问“http://appium.io/”,接着用断言方法assertEquals验证页面的Title是不是“Appium: Mobile App Automation Made Awesome.”。其中,实际页面的Title,可以通过mobiledriver的getTitle方法获得。 + +第二步,在testng.xml中添加这个Web App的测试用例,然后我们就可以在Appium Server已经启动的情况下执行这个测试用例了。 + +这个测试用例,首先会自动启动基于iOS 10.0的iPhone 7模拟器,然后自动打开Safari浏览器并访问Appium的官方网站。执行完成后的界面如下图13所示。 + + + +图13 测试用例执行完成的界面 + +进行到这里,我们基于Appium开发的第一个Web App的自动化测试用例,也就开发完成了。 + +经过前面Appium环境搭建,以及两个测试用例的设计,相信你已经对Appium有了一个感性的认识了。那么,Appium的实现原理又是怎样的呢?理解了Appium的使用原理,可以帮助你更好地使用这个工具,设计更加“有的放矢”的测试用例。 + +Appium的实现原理 + +Appium作为目前主流的移动应用自动化测试框架,具有极强的灵活性,主要体现在以下5个方面: + + +测试用例的实现支持多种编程语言,比如Java、Ruby、Python等; +Appium Server支持多平台,既有基于Mac的版本,也有基于Windows的版本; +支持Web App、Native App和Hybird App三大类移动应用的测试; +既支持iOS,也支持Android; +既支持真机,也支持模拟器。 + + +实际应用中,你可以根据项目情况灵活组合完成移动应用的自动化测试。比如,用Java写iOS上的Native App的测试用例,测试用例跑在Mac平台的iPhone虚拟机上;或者,用Python写Android上的Web App的测试用例,测试用例通过Windows平台跑在Android的真机上。 + +这样的组合还有很多很多。那你有没有想过,Appium为什么可以做到如此强大的灵活性呢?这就要从Appium的基本原理讲起了。 + +要真正理解Appium的内部原理,你可以把Appium分成三大部分,分别是Appium Client、Appium Server和设备端。这三部分的关系如图14所示。 + + + +图14 Appium内部原理 + +我们先来看看处于中间位置的Appium Server。 + +Appium Server有Mac和Windows版本,也就是说Appium Server可以运行在Mac或者Windows电脑上。本质上,Appium Server是一个 Node.js 应用,接受来自Appium Client的请求,解析后通过WebDriver协议和设备端上的代理打交道。 + + +如果是iOS,Appium Server会把操作请求发送给WebDriverAgent(简称WDA),然后WDA再基于XCUITest完成iOS模拟器或者真机上的自动化操作; +如果是Android,Appium Server会把操作请求发送给appium-UIautomator2-server,然后appium-UIautomator2-server再基于UIAutomator V2完成Android模拟器或者真机上的自动化操作。 + + +Appium Client其实就是测试代码,使用对应语言的Client将基于JSON Wire协议的操作指令发给Appium Server。 + +整体来说,Appium的内部原理可以总结为:Appium属于C/S架构,Appium Client通过多语言支持的第三方库向Appium Server发起请求,基于Node.js的Appium Server会接受Appium Client发来的请求,接着和iOS或者Android平台上的代理工具打交道,代理工具在运行过程中不断接收请求,并根据 WebDriver 协议解析出要执行的操作,最后调用iOS或者Android平台上的原生测试框架完成测试。 + +总结 + +好了,我来总结一下今天的主要的内容: + +目前网络上,Appium工具使用相关的资料都比较零散,为此我以最新版本的Appium Desktop 1.6.2和Appium Server 1.8.1为例,手把手地带你搭建了iOS环境,以及Appium测试环境,并介绍了如何通过Appium Inspector来定位页面元素。 + +搭建好了测试环境后,我分别针对Native App和Web App这两类移动应用,基于Appium实现了两个测试用例,这也是我在这个专栏里面,为你实现的第一个移动应用的测试用例。虽然测试需求比较简单,但是你也可以从中体会到移动应用测试用例设计的思想、方法。 + +最后,本着知其然知其所以然的原则,我介绍了Appium的实现原理:它属于C/S架构,Appium Client通过第三方库向Appium Server发起请求,Appium Server接受请求,然后和移动平台上的代理工具打交道,代理工具在运行过程中不断接收来自Appium Server的请求,并解析出要执行的操作,最后调用移动平台原生的测试框架完成测试操作。 + +思考题 + +我在这篇文章里面举的例子都是基于iOS的,建议你基于Android分别实现一个Web App和Native App的测试用例。 + +如果实现过程中,遇到了问题,或者有一些自己的想法,请给我留言讨论吧。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/22从0到1:API测试怎么做?常用API测试工具简介.md b/专栏/软件测试52讲/22从0到1:API测试怎么做?常用API测试工具简介.md new file mode 100644 index 0000000..aaa7890 --- /dev/null +++ b/专栏/软件测试52讲/22从0到1:API测试怎么做?常用API测试工具简介.md @@ -0,0 +1,279 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 22 从0到1:API测试怎么做?常用API测试工具简介 + 你好,我是茹炳晟,我今天分享的主题是“从0到1:API测试怎么做?常用API测试工具简介”。 + +在第11篇文章《互联网产品的测试策略应该如何设计?》中,我介绍过当今互联网产品的测试策略往往会采用菱形结构,即重量级 API 测试,轻量级 GUI 测试,轻量级单元测试,由此可见API测试在现今测试中的重要性不言而喻。 + +这篇文章是API自动化测试系列的第一篇文章,我会先为你打好API测试的基础。所以,我会先从0到1设计一个API测试用例,通过这个测试用例,你可以体会到最基本的API测试是如何进行的,并介绍几款常用的API测试工具。 + +API测试的基本步骤 + +通常来讲,无论采用什么API测试工具,API测试的基本步骤主要包括以下三大步骤: + + +准备测试数据(这是可选步骤,不一定所有API测试都需要这一步); + +通过API测试工具,发起对被测API的request; + +验证返回结果的response。 + + +对API的测试往往是使用API测试工具,比如常见的命令行工具cURL、图形界面工具Postman或者SoapUI、API性能测试的JMeter等。 + +为了让你更好地理解API测试具体是怎么做的,并掌握常见API测试工具的使用,我会以基于主流Spring Boot框架开发的简单Restful API为例,分别介绍如何使用cURL和Postman对其进行最基本的功能测试,目的是让你对API测试有一个基本的感性认识。 + +基于Spring Boot构建的API + +因为基于Spring Boot从0到1构建一个API,并不是本文的重点,为了不影响你对文章主要内容的把握,我直接采用了一个预先开发好的Account API为例展开讲解。你可以从https://github.com/SpectoLabs/spring-cloud-contract-blog下载完整的代码。 + +这个Account API的功能非常简单,就是基于你提供的ID值创建一个Account对象,并返回这个新创建Account对象。 + +比如,如果你的请求是“account/ID008”,那么返回的response就应该是“{“id”:“ID008”,“type”:“friends”,“email”:“[email protected]”}”。 + +这个Account API的功能逻辑实现非常简单,图1和图2列出了主要的代码逻辑。 + +图1中,代码的第21行说明了API的endpoint以及对应的操作是GET方法,第22行明确说明了GET方法具体的业务逻辑是由accountService.getById()方法实现的。 + + + +图1 RestController的实现 + +图2中,代码的第8行实现了accountService.getById()方法,具体逻辑就是返回一个以传入ID为ID的Account对象。 + + + +图2 具体业务逻辑的实现 + +我推荐使用IntelliJ打开这个下载的项目,然后直接启动其中的account-service。启动成功后,account-service会运行在本地机器的8080端口。启动成功后的界面如图3所示。 + + + +图3 成功启动基于Spring Boot的Account API + +使用cURL命令行工具进行测试 + +首先,你需要下载安装cURL,然后就可以通过以下命令发起Account API的调用。调用结束后的界面如图4所示。 + +curl -i -H "Accept: application/json" -X GET "http://127.0.0.1:8080/account/ID008" + + + + +图4 使用cURL测试Account API + +这行命令中参数的含义如下: + + +第一个参数“-i”,说明需要显示response的header信息; +第二个参数“-H”,用于设定request中的header; +第三个参数“-X”,用于指定执行的方法,这里使用了GET方法,其他常见的方法还有POST、PUT和DELETE等,如果不指定“-X”,那么默认的方法就是GET。 +最后“ http://127.0.0.1:8080/account/ID008 ”,指明了被测API的endpoint以及具体的ID值是“ID008”。 + + +当使用cURL进行API测试时,常用参数还有两个: + + +“-d”:用于设定http参数,http参数可以直接加在URL的query string,也可以用“-d”带入参数。参数之间可以用“&”串接,或使用多个“-d”。 +“-b”:当需要传递cookie时,用于指定cookie文件的路径。 + + +需要注意的是这些参数都是大小写敏感的。 + +了解了这几个最常用的参数后,我再来分析一些最常用的cURL命令以及使用的场景,包括Session的场景和Cookie的场景。 + +第一,Session的场景 + +如果后端工程师使用session记录使用者登入信息,那么后端通常会传一个 session ID给前端。之后,前端在发给后端的requests的header中就需要设置此session ID,后端便会以此session ID识别出前端是属于具体哪个session,此时cURL的命令行如下所示: + +curl -i -H "sessionid:XXXXXXXXXX" -X GET "http://XXX/api/demoAPI" + + +第二,Cookie的场景 + +如果是使用cookie,在认证成功后,后端会返回cookie给前端,前端可以把该cookie保存成为文件,当需要再次使用该cookie时,再用“-b cookie_File” 的方式在request中植入cookie即可正常使用。具体的cURL的命令行如下所示: + +// 将cookie保存为文件 +curl -i -X POST -d username=robin -d password=password123 -c ~/cookie.txt "http://XXX/auth" + +// 载入cookie到request中 +curl -i -H "Accept:application/json" -X GET -b ~/cookie.txt "http://XXX/api/demoAPI" + + +最后,需要特别说明的是,cURL只能发起API调用,而其本身并不具备结果验证能力(结果验证由人完成),所以严格意义上说cURL并不属于测试工具的范畴。但是由于cURL足够轻量级,经常被很多开发人员和测试人员使用,所以我在这里做了简单的介绍。 + +接下来,我们再来看看如何使用目前主流的Postman完成API测试。 + +使用图形界面工具Postman进行测试 + +Postman是目前使用最广泛的Http请求模拟工具之一,常常被用于Web Service API的测试。 + +早期的Postman,是以Chrome浏览器的插件(plugin)形式存在的,最新版本的Postman已经是独立的应用了。我猜想是因为这个工具的应用日益广泛,所以才有了今天的独立版本。 + +你可以通过官方网站下载对应于Mac、Windows和Linux操作系统的不同版本,截止文章写作完成时,最新的Mac版本是6.2.2。 + +接下来,我就会以Mac 6.2.2版本为例,跟你分享如何用Postman完成你的API测试。如果你使用浏览器的plugin版本,或者是基于其他操作系统的版本,这都没问题,基本的操作和步骤都是一样的。 + +具体的操作,主要包括: + + +发起API调用; + +添加结果验证; + +保存测试用例; + +基于Postman的测试代码自动生成。 + + +第一步,发起API调用 + +我们的目标是对Account API做测试,所以这里你需要选择Postmant的“Request”模块。进入相应界面后,你需要按照图5的提示依次执行以下三步操作,发起Account API的调用。 + + +在endpoint输入框中输入“http://127.0.0.1:8080/account/ID_008”; + +选择“GET”方法; + +点击“Send”按钮发起API调用。 + + + + +图5 Postman发起Account API的测试 + +完成以上步骤后,界面如图6所示。我们看到返回的response默认以JSON文件的形式显示在下面的Body中。 + + + +图6 Postman执行GET后的界面 + +这样就完成了一次Account API的调用,是不是非常简单。但问题是,这只是一个API调用,并没有对调用结果进行自动化验证。接下来,我们就加上结果验证的部分,一起看看会有什么效果。 + +第二步,添加结果验证 + +在Postman中添加结果验证也非常方便,假定我们在Account API测试过程中有以下四个验证点: + + +请求的返回状态码(Status Code)应该是200; + +请求的响应时间应该小于200 ms; + +请求返回的response header中应该包含“Content-Type”参数; + +请求返回的response body中,“type”的值应该是“friends”; + + +那么,接下来我们一起来看看如何使用Postman来添加这四个验证点。 + +为此,我们首先打开“Tests”界面,然后在右下角的“SNIPPETS”中依次点击: + + +“Status code: Code is 200” + +“Response time is less than 200 ms” + +“Response headers:Content-Type header check” + +“Response body: JSON value check” + + +完成以上操作后,“Tests”中会自动生成验证代码,接着只要按照具体的测试要求,对这些生成的代码进行一些小修改就可以了。 + +在这个例子中,你只需修改需要验证的JSON键值对即可,即代码的第15行。修改完成后我们可以再次点击“Send”按钮发起测试。测试通过的界面如图7所示,最下面的“Test Results”显示四个测试全部通过。 + + + +图7 测试通过的界面 + +第三步,保存测试用例 + +测试通过后,我们往往希望可以把这个测试request保存下来,以方便后续使用,为此Postman提供了保存测试request的功能,并提供了Collection来分类管理保存多个测试request。 + +Collection是用来保存测试request的一个集合,Collection内部还可以建立目录结构以方便进一步的分类和管理。 + +这里我们点击“Save As”按钮,在弹出的对话框中可以建立Collection,并且可以命名测试request并将其保存到Collection中。 + +我建立了“API Test Demo”的Collection,并且将刚才的测试request命名为“AccountAPI”保存到这个Collection中。 + +以后再要使用这个测试request时,直接在Collection中打开它,即可使用。同时你如果申请注册了一个Postman账号,就可以很方便地在多个环境中共享这个Collection了。 + +第四步,基于Postman的测试代码自动生成 + +至此,你已经掌握了Postman最基本的使用方法,但还有一个问题没有解决。很多时候,你希望将你的测试request作为回归测试用例集成到CI/CD的流程中,这就要求可以通过命令行的方式执行你的测试。为了达到这个目的,目前有两种做法: + + +将Postman中的测试request用自动化的方式直接转换成API测试的代码。 目前Postman已经支持这个功能了,可以将保存的测试request自动化转换成常见测试框架直接支持的代码,而且支持多语言。- +比如,基于Java的“OK HTTP”和“Unirest”,基于Python的“http.client”和“Requests”,基于NodeJS的“Native”“Request”和“Unirest”,基于JavaScript的“JQuery AJAX”和“XHR”等等。你可以点击如图8所示的“Code”按钮进入代码生成界面。 + + + + +图8 自动生成API测试代码 + + +利用Newman工具直接执行Postman的Collection。 你需要先将Postman中的Collection导出为JSON文件,然后执行以下命令行。 + +newman run examples/sample-collection.json; + + +如何应对复杂场景的API测试? + +我在前面分享的Restful API测试案例中,只涉及到了最基本的API的测试方法,而且测试场景也很比较简单(只是单个API的调用)。 + +但在实际项目中,除了这种单个API的测试场景外,还有很多复杂场景的API测试。所以,为了解决你在实际项目中可能会碰到的一些问题,我再和你聊聊目前一些常见的典型复杂场景,以及相应的测试思路和方法。 + +测试场景一:被测业务操作是由多个API调用协作完成 + +很多情况下,一个单一的前端操作可能会触发后端一系列的API调用,由于前端测试的相对不稳定性,或者由于性能测试的要求,你必须直接从后端通过模拟API的顺序调用来模拟测试过程。 + +这时,API的测试用例就不再是简单的单个API调用了,而是一系列API的调用,并且经常存在后一个API需要使用前一个API返回结果的情况,以及需要根据前一个API的返回结果决定后面应该调用哪个API的情况。 + +好在,我们已经实现了API的调用和结果解析的代码化,这也就意味着我们可以很灵活地直接用代码来处理这些场景了。 比如,通过代码将上个API调用返回的response中的某个值传递给下一个API,再比如根据上一个API的返回结果决定下一个应该调用哪个API等。 + +除此之外,我们还需要迫切解决的一个问题是:如何才能高效地获取单个前端操作所触发的API调用序列。 + +解决这个问题的核心思路是,通过网络监控的手段,捕获单个前端操作所触发的API调用序列。比如,通过类似于Fiddler之类的网络抓包工具,获取这个调用序列;又比如,目前很多互联网公司还在考虑基于用户行为日志,通过大数据手段来获取这个序列。 + +测试场景二:API测试过程中的第三方依赖 + +API之间是存在依赖关系的,比如你的被测对象是API A,但是API A的内部调用了API B,此时如果由于某种原因,API B在被测环境中处于不可用状态,那么API A的测试就会受到影响。 + +在单体架构下,通常只会在涉及到第三方API集成的场景中才会遇到这个问题,所以还不算严重。但是,在微服务架构下,API间相互耦合的依赖问题就会非常严重。 + +解决这个问题的核心思路是,启用Mock Server来代替真实的API。那么,Mock Server怎么才能真实有效地模拟被替代的API呢?这个问题,我会在分享《紧跟时代步伐:微服务模式下API测试要怎么做?》这个主题时,和你详细探讨。 + +测试场景三:异步API的测试 + +异步API是指,调用后会立即返回,但是实际任务并没有真正完成,而是需要稍后去查询或者回调(Callback)的API。 + +一直以来,异步API测试都是API测试中比较困难的部分。在我看来,对异步API的测试主要分为两个部分:一是,测试异步调用是否成功,二是,测试异步调用的业务逻辑处理是否正确。 + + +异步调用是否成功,这个还比较简单,主要检查返回值和后台工作线程是否被创建两个方面就可以了。 +但是,对异步调用业务逻辑的测试就比较复杂了,因为异步API通常发生在一些比较慢的操作上,比如数据库I/O、消息队列I/O等,此时测试往往需要去验证数据库中的值、消息队列中的值等,这就需要测试代码具有访问和操作数据库或者消息队列的能力。- +在实际工程项目中,这些能力一般会在测试框架级别提供,也就是说要求API测试框架中包含对应的工具类去访问和操作数据库或者消息队列等。 + + +总结 + +通常情况下,无论你采用什么API测试工具,基本的测试步骤往往都是三步,即准备测试数据(并不是所有的API测试都需要这一步)、通过API测试工具发起对被测API的request、验证返回结果的response。 + +接下来,我通过一个简单的Restful API测试为例,和你分享了cURL和Postman这两个常用API测试工具的使用。 + +其中,cURL只具备发起API调用的功能,而不具备结果验证能力,所以严格地说它并不属于测试工具的范畴。Postman常常被用于Web Service API的测试具体的操作,测试流程主要包括:发起API调用、添加结果验证、保存测试用例、基于Postman的测试代码自动生成。 + +最后,为了帮你应对实际工程项目中复杂的API测试场景,我分享了被测业务操作是由多个API调用协作完成、API测试过程中的第三方依赖、异步API的测试,这三个复杂场景下的测试思路和方法。 + +思考题 + +单个API测试是比较简单的,但在实际项目中,往往存在按时序的API调用以及异步API调用,这类API你是如何测试的?遇到过什么难题,又是如何解决的? + +感谢你的收听,欢迎给我留言讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/23知其然知其所以然:聊聊API自动化测试框架的前世今生.md b/专栏/软件测试52讲/23知其然知其所以然:聊聊API自动化测试框架的前世今生.md new file mode 100644 index 0000000..3bc46d2 --- /dev/null +++ b/专栏/软件测试52讲/23知其然知其所以然:聊聊API自动化测试框架的前世今生.md @@ -0,0 +1,179 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 23 知其然知其所以然:聊聊API自动化测试框架的前世今生 + 你好,我是茹炳晟,今天我和你分享的主题是“知其然知其所以然:聊聊API自动化测试框架的前世今生”。 + +在上一篇文章中,我以一个简单的Restful API为例,分别介绍了cURL和Postman的使用方法,相信你已经对API测试有个感性认识了。 + +但是,我们不能仅仅停留在感性认识的层面,还需要熟悉并掌握这些测试方法,完成相应的API测试工作。所以,也就有了我今天分享的主题,希望可以通过对API自动化测试框架发展的介绍,让你理解API测试是如何一步一步地发展成今天的样子,以“知其所以然”的方式加深你对API自动化测试的理解。 + +接下来,我将会遵循由简入繁的原则,为你介绍API测试框架,以发现问题然后解决问题的思路为主线,展开今天的分享。 + +早期的基于Postman的API测试 + +早期的API测试,往往都是通过类似Postman的工具完成的。但是,由于这类工具都是基于界面操作的,所以有以下两个问题亟待解决: + + +当需要频繁执行大量的测试用例时,基于界面的API测试就显得有些笨拙; + +基于界面操作的测试难以与CI/CD流水线集成。 + + +所以,我们迫切需要一套可以基于命令行执行的API测试方案。这样,API测试可以直接通过命令行发起,与CI/CD流水线的整合也就方便得多了。 + +基于Postman和Newman的API测试 + +于是就出现了集成Postman和Newman的方案,然后再结合Jenkins就可以很方便地实现API测试与CI/CDl流水线的集成。Newman其实就是一个命令行工具,可以直接执行Postman导出的测试用例。 + +用Postman开发调试测试用例,完成后通过Newman执行,这个方案看似很完美。但是在实际工程实践中,测试场景除了简单调用单个API以外,还存在连续调用多个API的情况。 + +此时,往往会涉及到多个API调用时的数据传递问题,即下一个API调用的参数可能是上一个API调用返回结果中的某个值。另外,还会经常遇到的情况是,API调用前需要先执行一些特定的操作,比如准备测试数据等。 + +因此,对于需要连续调用多个API并且有参数传递的情况,Postman+Newman似乎就不再是理想的测试方案了。 + +基于代码的API测试 + +为了解决这个问题,于是就出现了基于代码的API测试框架。比较典型的是,基于Java的OkHttP和Unirest、基于Python的http.client和Requests、基于NodeJS的Native和Request等。 + +小型的互联网企业,往往会根据自己的业务需求,选用这些成熟的API测试框架。 + +但是,对于中大型的互联网企业,一般都会自己开发更适合自身业务上下文的API测试框架,比如eBay,我们为了实现代码化的API测试,开发了自己的HttpClient,后期为了使API测试的代码更简洁易懂,就基于Rest-Assured封装了全新的API测试框架。 + +这种根据公司业务上下文开发实现的API测试框架,在使用上有很多优点,而且灵活性也很好,主要体现在以下几个方面: + + +可以灵活支持多个API的顺序调用,方便数据在多个API之间传递,即上一个API调用返回结果中的某个字段值可以作为后续API调用的输入参数; + +方便在API调用之前或者之后执行额外的任意操作,可以在调用前执行数据准备操作,可以在调用后执行现场清理工作等; + +可以很方便地支持数据驱动测试,这里的数据驱动测试概念和GUI测试中的数据驱动测试完全相同,也就是可以将测试数据和测试代码分离解耦; + +由于直接采用了代码实现,所以可以更灵活地处理测试验证的断言(Assert); + +原生支持命令行的测试执行方式,可以方便地和CI/CD工具做集成。 + + +这里我给出了一段伪代码示例,用于展示如何用代码实现一个简单的API测试。 + + + +图1 基于代码的API测试的伪代码示例 + + +代码的第1-12行,创建了CreateUserAPI类,其中包含了endpoint、操作方法PUT、InlineParam和Param的设置,并且构建了对应的request对象; + +代码的第14-19行,是测试的主体函数。这段函数的逻辑是这样的: + + +首先,构建CreateUserAPI的对象; +然后,用CreateUserAPI对象的buildRequest方法结合输入参数构建request对象; +接着,通过request对象的request()方法发起了API调用; +最后,验证response中的状态码是不是200。 + + + +在这段伪代码中,有以下几点需要你特别注意: + + +代码中“CreateUserAPI的父类RestAPI”“_buildRequest()方法”“request()方法”“addInlineParam()方法”等,都是由API测试框架提供的。 + +为了简化代码,这里并没有引入数据驱动的data provider。但在实际项目中,代码第14行的测试输入参数,往往来自于data provider,即由数据驱动的方式提供测试输入数据。 + +由于测试过程完全由代码实现,所以可以很方便的在测试执行前后增加任意的额外步骤。比如,需要在CreateUser前增加数据创建的步骤时,只需要在代码第15行前直接添加就可以了。 + +这里的例子只有一个API调用,当需要多个API顺序调用时,直接扩展testCreateUser方法即可,两个API之间的数据传递可以通过上一个API返回的response.XXXX完成。 + + +通过这段伪代码,我们可以看到,虽然基于代码的API测试灵活性很好,也可以很方便地和CI/CD集成,但是也引入了一些新的问题,比如: + + +对于单个API测试的场景,工作量相比Postman要大得多; +对于单个API测试的场景,无法直接重用Postman里面已经积累的Collection。 + + +在实际工程中,这两个问题非常重要,而且必须要解决。因为公司管理层肯定无法接受相同工作的工作量直线上升,同时原本已经完成的部分无法继续使用,所以自动化生成API测试代码的技术也就应运而生了。 + +自动生成API测试代码 + +自动生成API测试代码是指,基于Postman的Collection生成基于代码的API测试用例。 + +其实,在上一篇文章《从0到1:API测试怎么做?常用API测试工具简介》最后的部分,我已经提到过Postman工具本身已经支持将Collection转化成测试代码,但如果直接使用这个功能的话,还有两个问题需要解决: + + +测试中的断言(assert)部分不会生成代码,也就是说测试代码的生成只支持发起request的部分,而不会自动生成测试验证点的代码; + +很多中大型互联网企业都是使用自己开发的API测试框架,那么测试代码的实现就会和自研API测试框架绑定在一起,显然Postman并不支持这类代码的自动生成。 + + +鉴于以上两点,理想的做法是自己实现一个代码生成工具,这个工具的输入是Postman中Collection的JSON文件,输出是基于自研API框架的测试代码,而且同时会把测试的断言一并转化为代码。 + +这个小工具实现起来并不复杂,其本质就是解析Collection JSON文件的各个部分,然后根据自研API框架的代码模板实现变量替换。 具体来讲,实现过程大致可以分为以下三步: + + +首先,根据自研API框架的代码结构建立一个带有变量占位符的模板文件; +然后,通过JSON解析程序,按照Collection JSON文件的格式定义去提取header、method等信息; +最后,用提取得到的具体值替换之前模板文件中的变量占位符,这样就得到了可执行的自研框架的API测试用例代码。 + + +有了这个工具后,我建议你的工作模式(Working Model)可以转换成这样: + + +对于Postman中已经累积的Collection,全部由这个工具统一转换成基于代码的API测试用例; +开发人员继续使用Postman执行基本的测试,并将所有测试用例保存成Collection,后续统一由工具转换成基于代码的API测试用例; +对于复杂测试场景(比如,顺序调用多个API的测试),可以组装由工具转换得到的API测试用例代码,完成测试工作。 + + +如图2所示,就是一个组装多个由工具转换得到的API测试用例代码的例子。其中,代码第3行的类“CreateUserAPI”和第10行的类“BindCreditCardAPI”的具体代码就可以通过工具转换得到。 + + + +图2 多个API顺序调用的测试用例代码 + +至此,基于代码的API测试发展得算是比较成熟了,但在实际应用过程中还有一个痛点一直未被解决,那就是测试验证中的断言,也是我接下来要和你一起讨论的话题。 + +Response结果发生变化时的自动识别 + +在实际的工程项目中,开发了大量的基于代码的API测试用例后,你会发现一个让人很纠结的问题:到底应该验证API返回结果中的哪些字段? + +因为你不可能对返回结果中的每一个字段都写assert,通常情况下,你只会针对关注的几个字段写assert,而那些没写assert的字段也就无法被关注了。 + +但对API测试来说,有一个很重要的概念是后向兼容性(backward compatibility)。API的后向兼容性是指,发布的新API版本应该能够兼容老版本的API。 + +后向兼容性除了要求API的调用参数不能发生变化外,还要求不能删减或者修改返回的response中的字段。因为这些返回的response会被下游的代码使用,如果字段被删减、改名或者字段值发生了非预期的变化,那么下游的代码就可能因为无法找到原本的字段,或者因为字段值的变化而发生问题,从而破坏API的后向兼容性。 + +所以,我们迫切需要找到一个方法,既可以不对所有的response字段都去写assert,又可以监测到response的结构以及没有写assert的字段值的变化。 + +在这样的背景下,诞生了“Response结果变化时的自动识别”技术。也就是说,即使我们没有针对每个response字段都去写assert,我们仍然可以识别出哪些response字段发生了变化。 + +具体实现的思路是,在API测试框架里引入一个内建数据库,推荐采用非关系型数据库(比如MongoDB),然后用这个数据库记录每次调用的request和response的组合,当下次发送相同request时,API测试框架就会自动和上次的response做差异检测,对于有变化的字段给出告警。 + +你可能会说这种做法也有问题,因为有些字段的值每次API调用都是不同的,比如token值、session ID、时间戳等,这样每次的调用就都会有告警。 + +但是这个问题很好解决,现在的解决办法是通过规则配置设立一个“白名单列表”,把那些动态值的字段排除在外。 + +总结 + +为了让你可以更好地理解今天的API测试框架,我从其发展历程的角度进行了分析: + +早期的基于Postman的API测试在面临频繁执行大量测试用例,以及与CI/CD流水线整合的问题时,显得心有余而力不足。为此,基于命令行的API测试实践,也就是Postman+Newman,具有很好的灵活性,解决了这两个问题。 + +但是,Postman+Newman的测试方案,只能适用于单个API调用的简单测试场景,对于连续调用多个API并涉及到参数传递问题时,这个方案就变得不那么理想和完美了。随后,API测试就过渡到了基于代码的API测试阶段。 + +一些小型企业,则往往会选择适合自己业务的成熟API测试框架。中大型的互联网企业,一般都会根据自己的业务上下文,在成熟API测试框架的基础上封装自己的API测试框架,提升测试效率和灵活性。 + +但是,不管是采用现成的还是自己去开发API测试框架,都会遇到测试用例开发效率低下,以及无法直接重用Postman中积累的Collection的问题,为此我分享了两个比较好用的方法,也就是:自动生成API测试代码和Response结果变化的自动识别,并给出了这两个方法的实现思路。 + +希望我分享的这些内容,可以帮你解决在实际测试项目中遇到的问题。 + +思考题 + +目前,基于代码的API测试框架已经比较成熟了,所以在此基础上又出现了基于配置文件的API测试框架,比如典型的HttpRunner,在此类API测试框架的支持下,测试用例本身往往就是纯粹的配置文件了。你是否有接触过这类API测试框架,对此又有什么看法呢? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/24紧跟时代步伐:微服务模式下API测试要怎么做?.md b/专栏/软件测试52讲/24紧跟时代步伐:微服务模式下API测试要怎么做?.md new file mode 100644 index 0000000..a743594 --- /dev/null +++ b/专栏/软件测试52讲/24紧跟时代步伐:微服务模式下API测试要怎么做?.md @@ -0,0 +1,203 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 24 紧跟时代步伐:微服务模式下API测试要怎么做? + 你好,我是茹炳晟,今天我分享的主题是“紧跟时代步伐:微服务模式下API测试要怎么做?”。 + +通过一个的Restful API实例,我介绍了cURL和Postman工具的基本用法,这样我们对API测试有了一个感性认识;在此基础上,我介绍了API自动化测试框架发展的来龙去脉,借此我们对API测试框架的理解又更深入了一层。 + +今天,我将更进一步,带你去了解当下最热门的技术领域的API测试,即微服务模式下的API测试。微服务架构下,API测试的最大挑战来自于庞大的测试用例数量,以及微服务之间的相互耦合。所以,我今天分享这个主题的目的就是,帮你理解这两个问题的本质,以及如何基于消费者契约的方法来应对这两个难题。 + +而为了掌握微服务模式下的API测试,你需要先了解微服务架构(Microservice Architecture)的特点、测试挑战;而要了解微服务架构,你又需要先了解一些单体架构(Monolithic Architecture)的知识。所以,今天的话题我将逐层展开,目的就是希望你可以真正理解,并快速掌握微服务模式下的API测试。 + +单体架构(Monolithic Architecture) + +单体架构是早期的架构模式,并且存在了很长时间。单体架构是将所有的业务场景的表示层、业务逻辑层和数据访问层放在同一个工程中,最终经过编译、打包,并部署在服务器上。 + +比如,经典的J2EE工程,它就是将表示层的JSP、业务逻辑层的Service、Controller和数据访问层的DAO(Data Access Objects),打包成war文件,然后部署在Tomcat、Jetty或者其他Servlet容器中运行。 + +显然单体架构具有发布简单、方便调试、架构复杂性低等优点,所以长期以来一直被大量使用,并广泛应用于传统企业级软件。 + +但是,随着互联网产品的普及,应用所承载的流量越来越庞大,单体架构的问题也被逐渐暴露并不断放大,主要的问题有以下几点: + + +灵活性差:无论是多小的修改,哪怕只修改了一行代码,也要打包发布整个应用。更糟的是,由于所有模块代码都在一起,所以每次编译打包都要花费很长时间。 +可扩展性差:在高并发场景下,无法以模块为单位灵活扩展容量,不利于应用的横向扩展。 +稳定性差:当单体应用中任何一个模块有问题时,都可能会造成应用整体的不可用,缺乏容错机制。 +可维护性差:随着业务复杂性的提升,代码的复杂性也是直线上升,当业务规模比较庞大时,整体项目的可维护性会大打折扣。 + + +正是因为面对互联网应用时,单体架构有这一系列无法逾越的鸿沟,所以催生了微服务架构。 + +其实,微服务架构也不是一蹴而就的,也经历了很长时间的演化发展,中间还经历了著名的SOA架构。但是这个由单体架构到SOA架构再到微服务架构的演进过程,并不是本文的重点,所以我就不再详细展开了,如果你感兴趣的话,可以自行去查阅一些相关资料。 + +微服务架构(Microservice Architecture) + +微服务是一种架构风格。在微服务架构下,一个大型复杂软件系统不再由一个单体组成,而是由一系列相互独立的微服务组成。其中,各个微服务运行在自己的进程中,开发和部署都没有依赖。 + +不同服务之间通过一些轻量级交互机制进行通信,例如 RPC、HTTP 等,服务可独立扩展伸缩,每个服务定义了明确的边界,只需要关注并很好地完成一件任务就可以了,不同的服务可以根据业务需求实现的便利性而采用不同的编程语言来实现,由独立的团队来维护。 + +图1就很形象地展示了单体架构和微服务架构之间的差异。 + + + +图1 单体架构 VS 微服务架构 + +微服务架构具有以下特点: + + +每个服务运行在其独立的进程中,开发采用的技术栈也是独立的; +服务间采用轻量级通信机制进行沟通,通常是基于HTTP协议的RESTful API; +每个服务都围绕着具体的业务进行构建,并且能够被独立开发、独立部署、独立发布; +对运维提出了非常高的要求,促进了CI/CD的发展与落地。 + + +微服务架构下的测试挑战 + +由于微服务架构下,一个应用是由很多相互独立的微服务组成,每个微服务都会对外暴露接口,同时这些微服务之间存在级联调用关系,也就是说一个微服务通常还会去调用其他微服务,鉴于以上特点,微服务架构下的测试挑战主要来自于以下两个方面: + + +过于庞大的测试用例数量; + +微服务之间的耦合关系。 + + +接下来,我会针对这两项挑战分别展开,包括它们从何而来,以及如何应对这些挑战,最终完成测试。 + +第一,过于庞大的测试用例数量 + +在传统的API测试中,我们的测试策略通常是: + + +根据被测API输入参数的各种组合调用API,并验证相关结果的正确性; +衡量上述测试过程的代码覆盖率; +根据代码覆盖率进一步找出遗漏的测试用例; +以代码覆盖率达标作为API测试成功完成的标志。 + + +这也是单体架构时代主流的API测试策略。为了让你更好地理解这种测试策略,我来举一个实际的例子。 + +假设我们采用单体架构开发了一个系统,这个系统对外提供了3个Restful API接口,那么我们的测试策略应该是: + + +针对这3个API接口,分别基于边界值和等价类方法设计测试用例并执行; +在测试执行过程中,启用代码覆盖率统计; +假设测试完成后代码行覆盖率是80%,那么我们就需要找到那些还没有被执行到的20%的代码行。比如图2中代码的第242行就是没有被执行到,分析代码逻辑后发现,我们需要构造“expected!=actual”才能覆盖这个未能执行的代码行; +最终我们要保证代码覆盖率达到既定的要求,比如行覆盖率达到100%,完成API测试。 + + + + +图2 基于代码覆盖率指导测试用例设计的示例 + +而当我们采用微服务架构时,原本的单体应用会被拆分成多个独立模块,也就是很多个独立的service,原本单体应用的全局功能将会由这些拆分得到的API共同协作完成。 + +比如,对于上面这个例子,没有微服务化之前,一共有3个API接口,假定现在采用微服务架构,该系统被拆分成了10个独立的service,如果每个service平均对外暴露3个API接口,那么总共需要测试的API接口数量就多达30个。 + +如果我还按照传统的API测试策略来测试这些API,那么测试用例的数量就会非常多,过多的测试用例往往就需要耗费大量的测试执行时间和资源。 + +但是,在互联网模式下,产品发布的周期往往是以“天”甚至是以“小时”为单位的,留给测试的执行时间非常有限,所以微服务化后API测试用例数量的显著增长就对测试发起了巨大的挑战。 + +这时,我们迫切需要找到一种既能保证API质量,又能减少测试用例数量的测试策略,这也就是我接下来要分享的基于消费者契约的API测试。 + +第二,微服务之间的耦合关系 + +微服务化后,服务与服务间的依赖也可能会给测试带来不小的挑战。 + +如图3所示,假定我们的被测对象是Service T,但是Service T的内部又调用了Service X和Service Y。此时,如果Service X和Service Y由于各种原因处于不可用的状态,那么此时就无法对Service T进行完整的测试。 + + + +图3 API之间的耦合示例 + +我们迫切需要一种方法可以将Service T的测试与Service X和Service Y解耦。 + +解耦的方式通常就是实现Mock Service来代替被依赖的真实Service。实现这个Mock Service的关键点就是要能够模拟真实Service的Request和Response。当我介绍完基于消费者契约的API测试后,你会发现这个问题也就迎刃而解了。 + +基于消费者契约的API测试 + +那到底什么是基于消费者契约的API测试呢?直接从概念的角度解释,会有些难以理解。所以我打算换个方法来帮助你从本质上真正理解什么是基于消费者契约的API测试。接下来,就跟着我的思路走吧。 + +首先,我们来看图4,假设图4中的Service A、Service B和Service T是微服务拆分后的三个Service,其中Service T是被测试对象,进一步假定Service T的消费者(也就是使用者)一共有两个,分别是Service A和Service B。 + + + +图4 Service A、Service B和Service T的关系 + +按照传统的API测试策略,当我们需要测试Service T时,需要找到所有可能的参数组合依次对Service T进行调用,同时结合Service T的代码覆盖率进一步补充遗漏的测试用例。 + +这种思路本身没有任何问题,但是测试用例的数量会非常多。那我们就需要思考,如何既能保证Service T的质量,又不需要覆盖全部可能的测试用例。 + +静下心来想一下,你会发现Service T的使用者是确定的,只有Service A和Service B,如果可以把Service A和Service B对Service T所有可能的调用方式都测试到,那么就一定可以保证Service T的质量。即使存在某些Service T的其他调用方式有出错的可能性,那也不会影响整个系统的功能,因为这个系统中并没有其他Service会以这种可能出错的方式来调用Service T。 + +现在,问题就转化成了如何找到Service A和Service B对Service T所有可能的调用方式。如果能够找出这样的调用集合,并以此作为Service T的测试用例,那么只要这些测试用例100%通过,Service T的质量也就不在话下了。 + +从本质上来讲,这样的测试用例集合其实就是,Service T可以对外提供的服务的契约,所以我们把这个测试用例的集合称为“基于消费者契约的API测试”。 + +那么接下来,我们要解决的问题就是:如何才能找到Service A和Service B对Service T的所有可能调用了。其实这也很简单,在逻辑结构上,我们只要在Service T前放置一个代理,所有进出Service T的Request和Response都会经过这个代理,并被记录成JSON文件,也就构成了Service T的契约。 + +如图5所示,就是这个过程的原理了。 + + + +图5 收集消费者契约的逻辑原理 + +在实际项目中,我们不可能在每个Service前去放置这样一个代理。但是,微服务架构中往往会存在一个叫作API Gateway的组件,用于记录所有API之间相互调用关系的日志,我们可以通过解析API Gateway的日志分析得到每个Service的契约。 + +至此,我们已经清楚地知道了如何获取Service的契约,并由此来构成Service的契约测试用例。接下来,就是如何解决微服务之间耦合关系带来的问题了。 + +微服务测试的依赖解耦和Mock Service + +在前面的内容中,我说过一句话:实现Mock Service的关键,就是要能够模拟被替代Service的Request和Response。 + +此时我们已经拿到了契约,契约的本质就是Request和Response的组合,具体的表现形式往往是JSON文件,此时我们就可以用该契约的JSON文件作为Mock Service的依据,也就是在收到什么Request的时候应该回复什么Response。 + +下面的图6就解释了这一关系,当用Service X的契约启动Mock Service X后,原本真实的Service X将被Mock Service X替代,也就解耦了服务之间的依赖,图6中的Service Y也是一样的道理。 + + + +图6 基于Mock Service解决API之间的调用依赖 + +代码实例 + +自此,我已经讲完了基于消费者契约的API测试的原理,你是否已经都真正理解并掌握了呢? + +由于这部分内容的理论知识比较多,为了帮你更好地理解这些概念,我找了一个基于Spring Cloud Contract的实际代码的示例演示契约文件格式、消费者契约测试以及微服务之间解耦,希望可以帮到你。 + +具体的实例代码,你可以从https://github.com/SpectoLabs/spring-cloud-contract-blog下载,详细的代码解读可以参考https://specto.io/blog/2016/11/16/spring-cloud-contract/。 + +这个实例代码,基于Spring Boot实现了两个微服务:订阅服务(subscription-service)和账户服务(account-service),其中订阅服务会调用账户服务。这个实例基于Spring Cloud Contract,所以契约是通过Groovy语言描述的,也就是说实例中会通过Groovy语言描述的账户服务契约来模拟真实的账户服务。 + +这个实例的逻辑关系如图7所示。 + + + +图7 基于Spring Cloud Contract的契约测试实例 + +总结 + +单体架构,具有灵活性差、可扩展性差、可维护性差等局限性,所以有了微服务架构。 + +微服务架构的本身的特点,比如微服务数量多,各个微服务之间的相互调用,决定了不能继续采用传统API测试的策略。 + +为了既能保证API质量,又能减少测试用例数量,于是有了基于消费者契约的API测试。基于消费者契约的API测试的核心思想是:只测试那些真正被实际使用到的API调用,如果没有被使用到的,就不去测试。 + +基于消费者契约的测试方法,由于收集到了完整的契约,所以基于契约的Mock Service完美地解决了API之间相互依赖耦合的问题。 + +这已经是API自动化测试系列的最后一篇文章了,短短的三篇文章可能让你感觉意犹未尽,也可能感觉并没有涵盖到你在实际工程项目中遇到的API测试的所有问题,但是一个专栏区区几十篇文章的确无法面面俱到。 + +我通过这个专栏更想达到的目的是:讲清楚某一技术的来龙去脉及其应用场景,但是很多具体操作级别、代码实现级别的内容,还是需要你在实践中不断积累。 + +所以,如果你还有关于API测试的其他问题,非常欢迎你给我留言讨论,让我们一起来碰撞出思想火花吧! + +思考题 + +基于消费者契约的API测试中,对于那些新开发的API,或者加了新功能的API,由于之前都没有实际的消费者,所以你无法通过API Gateway方法得到契约。对于这种情况,你会采用什么方法来解决呢? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/25不破不立:掌握代码级测试的基本理念与方法.md b/专栏/软件测试52讲/25不破不立:掌握代码级测试的基本理念与方法.md new file mode 100644 index 0000000..ad47895 --- /dev/null +++ b/专栏/软件测试52讲/25不破不立:掌握代码级测试的基本理念与方法.md @@ -0,0 +1,179 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 25 不破不立:掌握代码级测试的基本理念与方法 + 你好,我是茹炳晟,今天我和你分享的主题是“不破不立:掌握代码级测试的基本理念与方法”。 + +我在第三篇文章《什么是单元测试?如何做好单元测试?》中,为你介绍了单元测试的基本概念和方法,和你聊到了单元测试用例的“输入数据”和“预计输出”,也谈到了驱动代码和桩代码,其实这些概念和方法在代码级测试中也是最基本的。 + +通常情况下,代码级测试的工作都是由开发人员完成,但是测试框架选型、覆盖率统计工具选型、测试用例设计原则等都需要资深的测试工程师或者测试架构师参与。 + +所以,代码级测试这个系列,我会和你分享测试人员应该具备的代码级测试基础知识,为你呈现一幅包括代码级测试技术入门、方法论、用例设计,以及覆盖率衡量、典型难点、解决思路的全景技术视图。 + +为了能更好地协助开发人员做好代码级测试,所以我今天的这次分享是根据实际工程项目中的实践,总结了五种常见的代码错误,以及对应的四大类代码级测试方法。 + +掌握了这些错误类型、测试方法,相信你就可以搞定代码级测试了,即使自己不用去完成测试工作,也可以让开发人员对你另眼相看,可以更高效地互相配合完成整个项目。 + +这里需要注意的是,代码级测试的测试方法一定是一套测试方法的集合,而不是一个测试方法。 因为单靠一种测试方法不可能发现所有潜在的错误,一定是一种方法解决一部分或者一类问题,然后综合运用多种方法解决全部问题。 + +本着先发现问题,然后解决问题的思路,我在正式介绍代码级测试方法之前,先来概括一下常见的代码错误类型,然后我们再一起讨论代码级测试有哪些方法。这样,我们就可以清晰地看出,每一种代码级测试方法都能覆盖哪些类型的代码错误。 + +根据过往的经验来看,代码错误,可以分为“有特征”的错误和“无特征”的错误两大类。“有特征”的错误,可进一步分为语法特征错误、边界行为错误和经验特征错误;“无特征”的错误,主要包括算法错误和部分算法错误。 + +接下来,我将和你详细说说这五类代码错误的具体含义是什么。 + +常见代码错误类型 + +第一,语法特征错误 + +语法特征错误是指,从编程语法上就能发现的错误。比如,不符合编程语言语法的语句等。 + +如果你使用IDE环境进行代码开发,那么IDE可以提示你大部分的这类错误,而且只有解决了这类错误,才能编译通过。但是,还会有一些比较隐晦的语法特征错误,IDE不能及时发现,而且也不会影响编译,只会在运行阶段出错。 + +void demoMethod(void) +{ + int a[10]; + a[10]=88; + ... +} + + +比如,这段C语言代码就存在数据越界的问题。 + +很显然,你从语法上很容易就能发现,这段代码初始化了一个长度为10的整型数组a,但数组下标从0开始,所以最大可用的数组空间应该是a[9],而这里却使用了a[10],造成数组越界,访问了未被初始化的内存空间,代码运行时(Runtime)就会造成意想不到的结果。 + +第二,边界行为特征错误 + +边界行为特征错误是指,代码在执行过程中发生异常,崩溃或者超时。之所以称为“边界”,是由于此类错误通常都是发生在一些边界条件上。 + +int Division(int a, int b) +{ + return a/b; +} + + +这段C语言代码就存在具有边界行为特征的错误。当b取值为0时,Division函数就会抛出运行时异常。 + +第三,经验特征错误 + +经验特征错误是指,根据过往经验发现代码错误。 + +void someMethod(void) +{ + ... + if(i=2) + { + // if the value of i equals to 2, call method "operationA" + operationA(); + } + else + { + // if the value of i doesn't equal to 2, call method "operationB" + operationB(); + } +} + + +这段C语言代码,就是一个典型的具有经验特征错误的代码片段。代码想要表达的意思是:如果变量i的值等于2,就调用函数operationA;否则,调用函数operationB。 + +但是,代码中将“if(i==2)”错误地写成了“if(i=2)”,就会使原本的逻辑判断操作变成了变量赋值操作,而且这个赋值操作的返回结果永远是true,即这段代码永远只会调用operationA的分支。 + +显然,“if(i=2)”在语法上没有错误,但是从过往经验来看,这就很可能是个错误了。也就是说,当你发现一个原本应该出现逻辑判断语句的地方,现在却出现了赋值语句,那就很有可能是代码写错了。 + +第四,算法错误 + +算法错误是指,代码完成的计算(或者功能)和之前预先设计的计算结果(或者功能)不一致。 + +这类错误直接关系到代码需要实现的业务逻辑,在整个代码级测试中所占比重最大,也是最重要的。但是,完全的算法错误并不常见,因为不能准确完成基本功能需求的代码,是一定不会被递交的。所以,在实际工程项目中,最常见的是部分算法错误。 + +第五,部分算法错误 + +部分算法错误是指,在一些特定的条件或者输入情况下,算法不能准确完成业务要求实现的功能。这类错误,是整个代码级测试过程中最常见的类型。 + +int add(int a, int b) +{ + return a+b; +} + + +这段C语言代码,完成了两个int类型整数的加法运算。在大多数情况下,这段代码的功能逻辑都是正确的,能够准确地返回两个整数的加法之和。但是,在某些情况下,可能存在两个很大的整数相加后和越界的情况,也就是说两个很大的int数相加的结果超过了int的范围。这就是典型的部分算法错误。 + +代码级测试常用方法 + +介绍完了语法特征错误、边界行为特征错误、经验特征错误、算法错误、部分算法错误这五类代码错误后,我们再回过头来看看代码级测试的方法有哪些,这些测试方法又是如何揭露这五类代码错误的。 + +在我看来,代码级测试方法主要分为两大类,分别是静态方法和动态方法。 + + +静态方法,顾名思义就是在不实际执行代码的基础上发现代码缺陷的方法,又可以进一步细分为人工静态方法和自动静态方法; +动态方法是指,通过实际执行代码发现代码中潜在缺陷的方法,同样可以进一步细分为人工动态方法和自动动态方法。 + + +这里需要注意到的是,我在这篇文章中只会和你分享这四种方法具体是什么,各有何局限性和优势,分别可以覆盖哪些错误类型。而对于,具体如何用这四种方法完成代码级测试,测试用例如何设计、常用的测试工具如何使用,我会在后面两篇文章(《深入浅出之静态测试方法》和《深入浅出之动态测试方法》)中详细展开。 + +第一,人工静态方法 + +人工静态方法是指,通过人工阅读代码查找代码中潜在错误的方法,通常采用的手段包括,开发人员代码走查、结对编程、同行评审等。 + +理论上,人工静态方法可以发现上述五类代码错误,但实际效果却并不理想。 这个方法的局限性,主要体现在以下三个方面: + + +过度依赖于代码评审者的个人能力,同样的评审流程,发现的问题却相差悬殊; + +如果开发人员自行走查自己的代码,往往会存在“思维惯性”,开发过程中没有能考虑的输入和边界值,代码走查时也一样会被遗漏; + +由于完全依赖人工,效率普遍较低。 + + +第二,自动静态方法 + +自动静态方法是指,在不运行代码的方式下,通过词法分析、语法分析、控制流分析等技术,并结合各种预定义和自定义的代码规则,对程序代码进行静态扫描发现语法错误、潜在语义错误,以及部分动态错误的一种代码分析技术。 + +自动静态方法可以发现语法特征错误、边界行为特征错误和经验特征错误这三类“有特征”的错误,但对于算法错误和部分算法错误这两种“无特征”的错误却无能为力。根本原因在于,自动静态方法并不清楚代码的具体业务逻辑。 + +目前,自动静态方法无论是在传统软件企业,还是在互联网软件企业都已经被广泛采用,往往会结合企业或项目的编码规范一起使用,并与持续集成过程紧密绑定。 + +你需要根据不同的开发语言,选择不同的工具。目前有很多工具都可以支持多种语言,比如Sonar、Coverity等,你可以根据实际需求来选择。 + +第三,人工动态方法 + +人工动态方法是指,设计代码的输入和预期的正确输出的集合,然后执行代码,判断实际输出是否符合预期。我在之前的第三篇文章《什么是单元测试?如何做好单元测试?》中介绍的单元测试,采用的测试方法本质上就是人工动态方法。 + +在代码级测试中,人工动态方法是最主要的测试手段,可以真正检测代码的逻辑功能,其关注点是“什么样的输入,执行了什么代码,产生了什么样的输出”,所以最善于发现算法错误和部分算法错误。 + +目前,不同的编程语言对应有不同的单元测试框架,比如,对Java语言最典型的是Junit和TestNG,对于C语言比较常用的是Google Test等。 + +第四,自动动态方法 + +自动动态方法,又称自动边界测试方法,指的是基于代码自动生成边界测试用例并执行,以捕捉潜在的异常、崩溃和超时的方法。 + +自动动态方法,可以覆盖边界行为特征错误, 通常能够发现“忘记处理某些输入”引起的错误(因为容易忘记处理的输入,往往是“边界”输入)。但是它对于发现算法错误无能为力,毕竟工具不可能了解代码所要实现的功能逻辑。 + +总结 + +作为代码级测试系列的第一篇文章,我今天主要和你分享了代码级测试中的常见代码错误类型,以及常用测试方法。 + +代码错误,可以划分为“有特征”的错误和“无特征”的错误两大类。其中,“有特征”的错误,又可以进一步细分为语法特征错误、边界行为特征错误和经验特征错误;而“无特征”的错误,主要包括算法错误和部分算法错误两类。 + +针对这五种代码错误,我将代码级测试的方法分成了静态方法和动态方法两大类。顾名思义,静态方法不需要执行实际代码,而动态方法需要通过执行具体的代码去发现代码错误。而每一类方法又可以根据执行方式,进一步细分。也因此,每种测试方法,所能覆盖的错误类型也不同,所以进行代码级测试时,你需要综合运用这些方法,并结合所在公司或者项目的编码规范一起使用。 + +这四类测试方法的特点,以及可以覆盖的错误类型,可以概括如下: + + +人工静态方法,本质上通过开发人员代码走查、结对编程、同行评审来完成的,理论上可以发现所有的代码错误,但也因为其对“测试人员”的过渡依赖,局限性非常大; +自动静态方法,主要的手段是代码静态扫描,可以发现语法特征错误、边界行为特征错误和经验特征错误这三类“有特征”的错误; +人工动态方法,就是传统意义上的单元测试,是发现算法错误和部分算法错误的最佳方式; +自动动态方法,其实就是自动化的边界测试,主要覆盖边界行为特征错误。 + + +思考题 + +你所在的公司,还采用过哪些代码级测试的方法,你们又是如何具体开展的呢? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/26深入浅出之静态测试方法.md b/专栏/软件测试52讲/26深入浅出之静态测试方法.md new file mode 100644 index 0000000..f0bcdd6 --- /dev/null +++ b/专栏/软件测试52讲/26深入浅出之静态测试方法.md @@ -0,0 +1,208 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 26 深入浅出之静态测试方法 + 你好,我是茹炳晟,今天我和你分享的主题是:深入浅出之静态测试方法。 + +我在分享《不破不立:掌握代码级测试的基本理念与方法》]这个主题时,系统地介绍了代码级测试常见的五种错误类型(包括语法特征错误、边界行为特征错误、经验特征错误、算法错误,以及部分算法错误),以及对应的四大类测试方法(包括人工静态方法、自动静态方法、人工动态方法,以及自动动态方法)。 + +今天,我将和你详细讨论人工静态测试方法和自动静态测试方法,来帮你理解研发流程上是如何保证代码质量的,以及如何搭建自己的自动静态代码扫描方案,并且应用到项目的日常开发工作中去。 + +人工静态方法本质上属于流程上的实践,实际能够发现问题的数量很大程度依赖于个人的能力,所以从技术上来讲这部分内容可以讨论的点并不多。但是,这种方法已经在目前的企业级测试项目中被广泛地应用了,所以我们还是需要理解这其中的流程,才能更好地参与到人工静态测试中。 + +而自动静态方法,可以通过自动化的手段,以很低的成本发现并报告各种潜在的代码质量问题,目前已经被很多企业和项目广泛采用,并且已经集成到CI/CD流水线了。作为测试工程师,我们需要完成代码静态扫描环境的搭建。接下来我会重点和你分享这一部分内容。 + +人工静态方法 + +通过我上一次的分析,我们知道了人工静态方法检查代码错误,主要有代码走查、结对编程,以及同行评审这三种手段。那么我们接下来就看一下这三种方法是如何执行的。 + + +代码走查(Code Review),是由开发人员检查自己的代码,尽可能多地发现各类潜在错误。但是,由于个人能力的差异,以及开发人员的“思维惯性”,很多错误并不能在这个阶段被及时发现。 +结对编程(Pair Programming),是一种敏捷软件开发的方法,一般是由两个开发人员结成对子在一台计算机上共同完成开发任务。其中,一个开发人员实现代码,通过被称为“驾驶员”;另一个开发人员审查输入的每一行代码,通常被称为“观察员”。- +当“观察员”对代码有任何疑问时,会立即要求“驾驶员”给出解释。解释过程中,“驾驶员”会意识到问题所在,进而修正代码设计和实现。- +实际执行过程中,这两个开发人员的角色会定期更换。 +同行评审(Peer Review),是指把代码递交到代码仓库,或者合并代码分支(Branch)到主干(Master)前,需要和你同技术级别或者更高技术级别的一个或多个同事对你的代码进行评审,只有通过所有评审后,你的代码才会被真正递交。- +如果你所在的项目使用GitHub管理代码,并采用GitFlow的分支管理策略,那么在递交代码或者分支合并时,需要先递交Pull Request(PR),只有这个PR经过了所有评审者的审核,才能被合并。这也是同行评审的具体实践。目前,只要你采用GitFlow的分支管理策略,基本都会采用这个方式。 + + +对于以上三种方式,使用最普遍的是同行评审。因为同行评审既能较好地保证代码质量,又不需要过多的人工成本投入,而且递交的代码出现问题后责任明确,另外代码的可追溯性也很好。 + +结对编程的实际效果虽然不错,但是对人员的利用率比较低,通常被用于一些非常关键和底层算法的代码实现。 + +自动静态方法 + +自动静态方法,主要有以下三个特点: + + +相比于编译器,可以做到对代码更加严格、个性化的检查; +不真正检测代码的逻辑功能,只是站在代码本身的视角,基于规则,尽可能多地去发现代码错误; +由于静态分析算法并不实际执行代码,完全是基于代码的词法分析、语法分析、控制流分析等技术,由于分析技术的局限性以及代码写法的多样性,所以会存在一定的误报率。 + + +基于这些特点,自动静态方法通常能够以极低的成本发现以下问题: + + +使用未初始化的变量; +变量在使用前未定义; +变量声明了但未使用; +变量类型不匹配; +部分的内存泄漏问题; +空指针引用; +缓冲区溢出; +数组越界; +不可达的僵尸代码; +过高的代码复杂度; +死循环; +大量的重复代码块; +… + + +正是由于自动静态方法具有自动化程度高,检查发现问题的成本低以及能够发现的代码问题广等特点,所以该方法被很多企业和项目广泛应用于前期代码质量控制和代码质量度量。 + +在实际工程实践中,企业往往会结合自己的编码规范定制规程库,并与本地IDE开发环境和持续集成的流水线进行高度整合。 + +代码本地开发阶段,IDE环境就可以自动对代码实现自动静态检查;当代码递交到代码仓库后,CI/CD流水线也会自动触发代码静态检查,如果检测到潜在错误,就会自动邮件通知代码递交者。 + +接下来,我们一起来看两个自动静态方法发现错误的实际案例,希望可以加深你对自动静态方法的认识。 + +自动静态方法的实际例子 + +第一个例子,自动静态方法检查语法特征错误。 + +如图1左侧所示的C语言代码,存在数组越界的问题,一种典型的语法特征错误。 + +图1右侧,就是通过C语言的自动静态扫描工具splint发现的这个问题,并给出的分析结果。 + + + +图1 数组越界的错误 + +第二个例子,自动静态方法检查内存空间被释放后继续被赋值的错误。 + +如图2左侧所示的C语言代码,我们用malloc函数申请了一个内存空间,并用指针a指向了这个空间,然后新建了一个指针b也指向这个空间,也就是指针a和指针b实际上指向了同一个内存空间。之后,我们把指针a指向的空间释放掉了,意味着指针b指向的空间也被释放了。但是,此时代码却试图去对指针b指向的空间赋值,显然这会导致不可预料的后果。 + +幸运的是,C语言的自动静态扫描工具splint发现了这个问题,并给出了详细解释。 + + + +图2 内存空间释放后还继续使用的错误 + +实际案例:Sonar实战 + +现在,我们已经了解了自动静态代码扫描的基本概念,那怎么把这些知识落地到你的实际项目中呢?我们就从目前主流的自动静态工具Sonar的使用开始吧。 + +考虑到你可能以前并没有接触过Sonar,所以我会按照step by step的节奏展开。如果你已经用过Sonar了,你可以跳过在Mac 电脑上建立Sonar的步骤,从完成你的Maven项目的自动静态分析开始。 + +通过这个Sonar实例,你可以掌握: + + +搭建自己的SonarQube服务器; +扫描Maven项目,并将结果报告递交到SonarQube服务器; +在IntelliJ IDE中集成SonarLint插件,在IDE中实现实时的自动静态分析; + + +首先,在Sonar官网下载LTS(Long-term Support)版本的SonarQube 6.7.5。这里需要注意的是,我不推荐在实际工程项目中使用最新版的SonarQube,而是建议使用LTS版本以保证稳定性和兼容性。 + +解压后运行其中的bin/macosx-universal-64目录下的sonar.sh,这里需要注意运行sonar.sh时要带上“console”参数。如果执行完成的界面如下图3所示,那么说明你的SonarQube服务已经成功启动。 + + + +图3 SonarQube启动成功的界面 + +此时,你可以尝试访问localhost:9000,并用默认账号(用户名和密码都是“admin”)登录。 + +为了简化建立SonarQube的步骤,所有的内容我都使用了默认值。比如,我直接使用了SonarQube内建的数据库,端口也采用了默认的9000。但是,在实际工程项目中,为了Sonar数据的长期可维护和升级,我们通常会使用自己的数据库,需要执行下面这些步骤: + + +安装SonarQube之前,先安装数据库; + +建立一个空数据库并赋予CRUD权限; + +修改SonarQube的conf/sonar.properties中的JDBC配置,使其指向我们新建的数据库。我们也可以采用同样的方法,来修改默认的端口。 + + +因为要在Maven项目中执行代码静态扫描,为此我们需要先找到$MAVEN_HOME/conf下的settings.xml文件,在文件中加入Sonar相关的全局配置,具体需要加入的内容如下所示: + + + + org.sonarsource.scanner.maven + + + + sonar + + true + + + + http://myserver:9000 + + + + + + + +最后,我们就可以在Maven项目中,执行“mvn clean verify sonar:sonar”命令完成静态代码扫描。 + +如果你是第一次使用这个命令,那么mvn会自动下载依赖maven-sonar-plugin,完成后发起代码的静态扫描,并会自动把扫描结果显示到SonarQube中。 + +图4所示的结果,就是我对《从0到1:你的第一个GUI自动化测试》一文中的GUI测试项目代码的扫描结果。 + + + +图4 SonarQube的静态扫描结果页面 + +扫描结果是Passd,但同时也发现了三个Code Smell问题,或者说是改进建议,如图5所示。 + + +Class建议放在package中; + +导入了java.io.BufferedInputStream,但没有在实际代码中使用,建议删除; + +建议变量名字不要包含下划线。 + + + + +图5 详细扫描结果示例 + +至此,你已经使用Sonar完成了一次代码的静态扫描,是不是还挺方便的? + +但是,在日常工作中你可能还想要实时看到Sonar分析的结果,这样可以大幅提高修改代码的效率。为此,我们可以在IDE中引入SonarLint插件。你可以通过IDE的plugin(插件)管理界面安装SonarLint。 + +安装完成后重启IDE,你就可以在IDE环境中实时看到Sonar的静态分析结果了,如图6所示。 + + + +图6 在IDE中直接查看静态扫描结果 + +另外,在IDE中绑定SonarQube,就可以把SonarLint和SonarQube集成在一起了,如图7所示。集成完成后,IDE本地的代码扫描就能使用SonarQube端的静态代码规则库了,在企业级的项目中,一般要求所有开发人员都使用统一的静态代码规则库,所以一般都会要求本地IDE的SonarLint与SonarQube集成。 + + + +图7 IDE中的SonarLint和SonarQube绑定 + +目前,自动静态扫描通常都会和持续集成的流水线做绑定,最常见的应用场景是当你递交代码后,持续集成流水线就会自动触发自动静态扫描,这一功能是通过Jenkins以及Jenkins上的SonarQube插件来完成的,当你在Jenkins中安装了SonarQube Plugin,并且将SonarQube服务器相关的配置信息加入Plugin之后,你就可以在Jenkins Job的配置中增加Sonar静态扫描步骤了。 + +总结 + +人工静态方法,主要有代码走查、结对编程和同行评审三种常用方法。在工程实践中,同行评审因为可以保证代码质量、效率高、责任明确等特点,已经被广泛采用。 + +自动静态方法,因为自动化程度高、成本低、发现的代码问题广等特点,是常用的代码级测试方法。 + +在这里,测试工程师需要完成代码静态扫描环境的搭建,考虑到你以前可能没有接触过Sonar,我按照step by step的思路,带你一起搭建了一套代码静态扫描环境,并分享了一个Maven项目代码静态扫描的实例。 + +这就是我今天分享的主要内容了,希望可以帮助你解决实际工作中遇到的问题。 + +思考题 + +除了Sonar,你还用过哪些静态代码扫描工具,使用过程中遇到过哪些问题? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/27深入浅出之动态测试方法.md b/专栏/软件测试52讲/27深入浅出之动态测试方法.md new file mode 100644 index 0000000..0344323 --- /dev/null +++ b/专栏/软件测试52讲/27深入浅出之动态测试方法.md @@ -0,0 +1,267 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 27 深入浅出之动态测试方法 + 你好,我是茹炳晟,今天我和你分享的主题是:深入浅出之动态测试方法。 + +相较于,静态测试方法是不需要实际执行代码去发现潜在代码错误的方法,我今天要和你讨论的动态测试方法,则是要通过实际执行代码去发现潜在代码错误的测试方法。 + +正如我在分享《不破不立:掌握代码级测试的基本理念与方法》这个主题时,将动态测试方法进一步划分为人工动态方法和自动动态方法,今天这次关于动态测试方法的分享,我也会从这两个方面展开。 + +由于自动动态方法并不能理解代码逻辑,所以仅仅被用于发现异常、崩溃和超时这类“有特征”的错误,而对于代码逻辑功能的测试,主要还是要依靠人工动态方法。 + +人工动态方法 + +人工动态方法,可以真正检测代码的业务逻辑功能,其关注点是“什么样的输入,执行了什么代码,产生了什么样的输出”,主要用于发现算法错误和部分算法错误,是最主要的代码级测试手段。 + +从人工动态方法的定义中,你可以很清楚地看出:代码级测试的人工动态测试方法,其实就是单元测试所采用的方法。所以,下面的分享,我会从单元测试方法的角度展开。 + +如果有一些代码基础,那么你在学习单元测试框架或者工具时,会感觉单元测试很简单啊,一点都不难:无非就是用驱动代码去调用被测函数,并根据代码的功能逻辑选择必要的输入数据的组合,然后验证执行被测函数后得到的结果是否符合预期。 但是,一旦要在实际项目中开展单元测试时,你会发现有很多实际的问题需要解决。 + +我在专栏第4篇文章《什么是单元测试?如何做好单元测试?》中,已经分享过单元测试中的主要概念了,所以今天的分享我不会重复前面的内容,只和你分享前面没有涉及到的部分。如果你有哪些概念已经记不太清楚了,建议你先回顾一下那篇文章的内容。 + +接下来,我将和你分享单元测试中三个最主要的难点: + + +单元测试用例“输入参数”的复杂性; + +单元测试用例“预期输出”的复杂性; + +关联依赖的代码不可用。 + + +单元测试用例“输入参数”的复杂性 + +提到“输入参数”的复杂性,你应该已经记起了,我在前面的分享中提到过:如果你认为单元测试的输入参数只有被测函数的输入参数的话,那你就把事情想得过于简单了。 + +其实,这也是源于我们在学习单元测试框架时,单元测试用例的输入数据一般都是被测函数的输入参数,所以我们的第一印象会觉得单元测试其实很简单。 + +但是到了实际项目时,你会发现单元测试太复杂了,因为测试用例设计时需要考虑的“输入参数”已经完全超乎想象了。 + +我在《什么是单元测试?如何做好单元测试?》一文中已经总结了多种常见的单元测试输入数据,但是并没有详细解释每种输入数据的具体含义,你可能也对此感到困惑,那么今天我就结合一些代码示例和你详细聊聊这些输入参数吧。 + +第一,被测试函数的输入参数 + +这是最典型,也是最好理解的单元测试输入数据类型。假如你的被测函数是下面这段代码中的形式,那么函数输入参数a和b的不同取值以及取值的组合就构成了单元测试的输入数据。 + +int someFunc(int a, int b) +{ + … +} + + +第二,被测试函数内部需要读取的全局静态变量 + +如果被测函数内部使用了该函数作用域以外的变量,那么这个变量也是被测函数的输入参数。 + +下面这段代码中,被测函数Func_SUT的内部实现中使用了全局变量someGlobalVariable,并且会根据someGlobalVariable的取值去执行FuncA()和FuncB()这不同的代码分支。 + +在做单元测试时,为了能够覆盖这两个分支,你就必须构造someGlobalVariable的不同取值,那么自然而然,这个someGlobalVariable就成为了被测函数的输入参数。 + +所以,在这段代码中,单元测试的输入参数不仅包括Func_SUT函数的输入参数a,还包括全局变量someGlobalVariable。 + +bool someGlobalVariable = true; +void Func_SUT(int a) +{ + ... + if(someGlobalVariable == true) + { + FuncA(); + } + else + { + FuncB(); + } + ... +} + + +第三,被测试函数内部需要读取的类成员变量 + +如果你能理解“被测函数内部需要读取的全局静态变量”是单元测试的输入参数,那么“被测试函数内部需要读取的类成员变量”也是单元测试的输入参数就不难理解了。因为,类成员变量对被测试函数来讲,也可以看做是全局变量。 + +我们一起看一段代码。这段代码中,变量someClassVariable是类someClass的成员变量,类的成员函数Func_SUT是被测函数。Func_SUT函数,根据someClassVariable的取值不同,会执行两个不同的代码分支。 + +同样地,单元测试想要覆盖这两个分支,就必须提供someClassVariable的不同取值,所以someClassVariable对于被测函数Func_SUT来说也是输入参数。 + +class someClass{ + ... + bool someClassVariable = true; + ... + void Func_SUT(int a) + { + ... + if(someClassVariable == true) + { + FuncA(); + } + else + { + FuncB(); + } + ... + } + ... +} + + +第四,函数内部调用子函数获得的数据 + +“函数内部调用子函数获得的数据”也是单元测试的输入数据,从字面上可能不太好理解,那我就通过一段代码,和你详细说说这是怎么回事吧。 + +void Func_SUT(int a) + { + bool toggle = FuncX(a); + if(toggle == true) + { + FuncA(); + } + else + { + FuncB(); + } +} + + +函数Func_SUT是被测函数,它的内部调用了函数FuncX,函数FuncX的返回值是bool类型,并且赋值给了内部变量toggle,之后的代码会根据变量toggle的取值来决定执行哪个代码分支。 + +那么,从输入数据的角度来看,函数FuncX的调用为被测函数Func_SUT提供了数据,也就是这里的变量toggle,后续代码逻辑会根据变量toggle的取值执行不同的分支。所以,从这个角度来看,被测函数内部调用子函数获得的数据也是单元测试的输入参数。 + +这里还有一个小细节,被测函数Func_SUT的输入参数a,在内部实现上只是传递给了内部调用的函数FuncX,而并没有在其他地方被使用,我们把这类用于传递给子函数的输入参数称为“间接输入参数”。 + +这里需要注意的是,有些情况下“间接输入参数”反而不是输入参数。 + +就以这段代码为例,如果我们发现通过变量a的取值很难控制FuncX的返回值(也就是说,当通过间接输入参数的取值去控制内部调用函数的取值,以达到控制代码内部执行路径比较困难)时,我们会直接对FuncX(a)打桩,用桩代码来控制函数FuncX返回的是true还是false。 + +这样一来,原本的变量a其实就没有任何作用了。那么,此时变量a虽然是被测函数的输入参数,但却并不是单元测试的输入参数。 + +第五,函数内部调用子函数改写的数据 + +理解了前面几种单元测试的输入参数类型后,“函数内部调用子函数改写的数据”也是单元测试中被测函数的输入参数就好解释了。 + +比如,当被测函数内部调用的子函数改写了全局变量或者类的成员变量,而这个被改写的全局变量或者类的成员变量又会在被测函数内部被使用,那么“函数内部调用子函数改写的数据”也就成为了被测函数的输入参数了。 + +第六,嵌入式系统中,在中断调用中改写的数据 + +嵌入式系统中,在中断调用中改写的数据有时候也会成为被测函数的输入参数,这和“函数内部调用子函数改写的数据也是单元测试中的输入参数”类似,在某些中断事件发生并执行中断函数时,中断函数很可能会改写某个寄存器的值,但是被测函数的后续代码还要基于这个寄存器的值进行分支判断,那么这个被中断调用改写的数据也就成了被测函数的输入参数。 + +其实在实际工程项目中,除了这六种输入参数,还有很多输入参数。在这里,我详细分析这六种输入参数的目的,一来是帮你理解到底什么样的数据是单元测试的输入数据,二来也是希望你可以从本质上认识单元测试的输入参数,那么在以后遇到相关问题时,你也可以做到触类旁通,不会再踌躇无措。 + +理解了“输入参数”的复杂性,接下来我们再一起看看“预期输出”的复杂性表现在哪些方面。 + +单元测试用例“预期输出”的复杂性 + +同样地,单元测试用例的“预期输出”,也绝对不仅仅是函数返回值这么简单。通常来讲,“预期输出”应该包括被测函数执行完成后所改写的所有数据,主要包括:被测函数的返回值,被测函数的输出参数,被测函数所改写的成员变量和全局变量,被测函数中进行的文件更新、数据库更新、消息队列更新等。 + +第一,被测函数的返回值 + +这是最直观的预期输出。比如,加法函数int add(int a, int a)的返回值就是预期输出。 + +第二,被测函数的输出参数 + +要理解“被测函数的输出参数”是预期输出,最关键的是要理解什么是函数的输出参数。如果你有C语言背景,那么你很容易就可以理解这个概念了。 + +我们一起来看一段代码。被测函数add包含三个参数,其中a和b是输入参数,而sum是个指针,指向了一个地址空间。 + +如果被测函数的代码对sum指向的空间进行了赋值操作,那么在被测函数外,你可以通过访问sum指向的空间来获得被测函数内所赋的值,相当于你把函数内部的值输出到了函数外,所以sum对于函数add来讲其实是用于输出加法结果的,那么显然这个sum就是我们的“预期输出”。 + +如果你还没有理解的话,可以在百度上搜索一下“C语言的参数传递机制”。 + +void add(int a, int b,int *sum) +{ + *sum = a + b; +} +void main() +{ + int a, b,sum; + a = 10; + b = 8; + add(a, b, &sum); + printf("sum = %d \n", sum); +} + + +第三,被测函数所改写的成员变量和全局变量 + +理解了单元测试用例“输入参数”的复杂性,“被测函数所改写的成员变量和全局变量”也是被测函数的“预期输出”就很好理解了,此时如果你的单元测试用例需要写断言来验证结果,那么这些被改写的成员变量和全局变量就是assert的对象。 + +第四,被测函数中进行的文件更新、数据库更新、消息队列更新等 + +这应该不难理解。 + +但在实际的单元测试实践中,因为测试解耦的需要,所以一般不会真正去做这些操作,而是借助对Mock对象的断言来验证是否发起了相关的操作。 + +关联依赖的代码不可用 + +什么是关联依赖的代码呢? + +假设被测函数中调用了其他的函数,那么这些被调用的其他函数就是被测函数的关联依赖代码。 + +大型的软件项目通常是并行开发的,所以经常会出现被测函数关联依赖的代码未完成或者未测试的情况,也就是出现关联依赖的代码不可用的情况。那么,为了不影响被测函数的测试,我们往往会采用桩代码来模拟不可用的代码,并通过打桩补齐未定义部分。 + +具体来讲,假定函数A调用了函数B,而函数B由其他开发团队编写,且未实现,那么我们就可以用桩函数来代替函数B,使函数A能够编译链接,并运行测试。 + +桩函数要具有与原函数完全相同的原形,仅仅是内部实现不同,这样测试代码才能正确链接到桩函数。一般来讲桩函数主要有两个作用,一个是隔离和补齐,另一个是实现被测函数的逻辑控制。 + +用于实现隔离和补齐的桩函数实现比较简单,只需拷贝原函数的声明,加一个空的实现,可以通过编译链接就可以了。 + +用于实现控制功能的桩函数是最常用的,实现起来也比较复杂,需要根据测试用例的需要,输出合适的数据作为被测函数的内部输入。 + +自动动态方法 + +我们先来回顾一下,什么是自动动态方法。自动动态方法是,基于代码自动生成边界测试用例并执行来捕捉潜在的异常、崩溃和超时的测试方法。 + +自动动态方法的重点是:如何实现边界测试用例的自动生成。 + +解决这个问题最简单直接的方法是,根据被测函数的输入参数生成可能的边界值。 + +具体来讲,任何数据类型都有自己的典型值和边界值,我们可以预先为它们设定好典型值和边界值,然后组合就可以生成了。 + +比如,函数int func(int a, char *s),就可以按下面的三步来生成测试用例集。 + + +定义各种数据类型的典型值和边界值。 比如,int类型可以定义一些值,如int的最小值、int的最大值、0、1、-1等;char*类型也可以定义一些值,比如“”、“abcde”、“非英文字符串”等。 + +根据被测函数的原形,生成测试用例代码模板,比如下面这段伪代码: + +try{ + int a= @a@; + char *s = @s@; + int ret = func(a, s); +} +catch{ + throw exception(); +} + +将参数@a@和@s@的各种取值循环组合,分别替换模板中的相应内容,即可生成用例集。 + + +由于该方法不可能自动了解代码所要实现的功能逻辑,所以不会验证“预期输出”,而是通过try…catch来观察是否会引发代码的异常、崩溃和超时等具有边界特征的错误。 + +总结 + +代码级测试的动态测试方法,可以分为人工动态测试方法和自动动态测试方法。其中人工动态测试方式,是最常用的代码级测试方法,也是我们在进行单元测试时采用的方法。 + +人工动态方法,也就是单元测试方法,通常看似简单,但在实际的工程实践中会遇到很多困难,总结来看这些困难可以概括为三大方面: + + +单元测试用例“输入参数”的复杂性,表现在“输入参数”不是简单的函数输入参数。本质上讲,任何能够影响代码执行路径的参数,都是被测函数的输入参数。 + +单元测试用例“预期输出”的复杂性,主要表现在“预期输出”应该包括被测函数执行完成后所改写的所有数据。 + +关联依赖的代码不可用,需要我们采用桩代码来模拟不可用的代码,并通过打桩补齐未定义部分。 + + +而自动动态方法,需要重点讨论的是:如何实现边界测试用例的自动生成。解决这个问题最简单直接的方法是,根据被测函数的输入参数生成可能的边界值。 + +思考题 + +除了我们一起讨论的这些单元测试的难点,还有复杂数据初始化、函数内部不可控子函数的调用、间接输入参数的估算等难点。你在单元测试中是否遇到过这些问题呢,又是如何解决的? + +感谢你的收听,欢迎给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/28带你一起解读不同视角的软件性能与性能指标.md b/专栏/软件测试52讲/28带你一起解读不同视角的软件性能与性能指标.md new file mode 100644 index 0000000..bd7f3db --- /dev/null +++ b/专栏/软件测试52讲/28带你一起解读不同视角的软件性能与性能指标.md @@ -0,0 +1,257 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 28 带你一起解读不同视角的软件性能与性能指标 + 你好,我是茹炳晟。今天我和你分享的主题是:带你一起解读不同视角的软件性能与性能指标。 + +我用三篇文章的篇幅,和你分享了代码级测试的一些基本概念和测试方法,希望可以帮助你解决在实际工作中遇到的问题,如果你感觉还有一些问题没有解决的话,欢迎你给我留言,我们一起去讨论、解决。 + +从今天开始,我将和你分享一个新的测试主题:性能测试。这个系列,我准备了七个主题,要和你详细聊聊软件性能测试相关的知识点。 + +因为性能测试的专业性很强,所以我会以从0到1的入门者视角,系统性地阐述性能测试的方法以及应用领域,用实例去诠释各种性能指标;我还会分享一些前端和后端性能测试工具的基本原理,并基于LoadRunner去分析一些大型企业性能测试的规划、设计、实现的具体实例;我还会和你分享一些跨国软件公司性能测试卓越中心的实际案例。 + +希望这个系列的内容,可以完善你的性能测试知识体系,真真正正地帮你解决实际项目中遇到的问题。 + +那么,作为性能测试系列的第一次分享,我会站在全局的视角,帮你梳理软件性能、软件性能测试相关的知识点,让你对那些你或许已经耳熟能详的性能指标有一个更清晰的理解,为你完成后续的性能测试工作打好基础。 + +如果你对软件性能的理解还停留在响应时间的快慢上,那么赶紧和我一起开始今天的内容吧。 + +在开始下面的内容前,请你先思考一个问题:当我们谈及软件性能的时候,我们到底谈的是什么? + +目前,对软件性能最普遍的理解就是软件处理的及时性。但其实,从不同的系统类型,以及不同的视角去讨论软件性能,都会有所区别。 + +对于不同类型的系统,软件性能的关注点各不相同,比如: + + +Web类应用和手机端应用,一般以终端用户感受到的端到端的响应时间来描述系统的性能; +非交互式的应用,比如典型的电信和银行后台处理系统,响应时间关注更多的是事件处理的速度,以及单位时间的事件吞吐量。 + + +这很容易理解。同样地,对同一个系统来说,不同的对象群体对软件性能的关注点和期望也不完全相同,甚至很多时候是对立的。这里,不同的对象群体可以分为四大类:终端用户、系统运维人员、软件设计开发人员和性能测试人员。 + + + +图1 衡量软件性能的四个维度 + +终端用户是软件系统的最终使用者,他们对软件性能的反馈直接决定了这个系统的应用前景;而,软件开发人员、运维人员、性能测试人员,对性能测试的关注点则直接决定了一个系统交付到用户手中的性能。 + +只有全面了解各类群体对软件系统的不同需求,才能保证这个系统具有真正高可靠的性能。所以,接下来我会从这四类人的视角和维度去分享软件性能到底指的是什么。 + +终端用户眼中的软件性能 + +从终端用户(也就是软件系统使用者)的维度来讲,软件性能表现为用户进行业务操作时的主观响应时间。具体来讲就是,从用户在界面上完成一个操作开始,到系统把本次操作的结果以用户能察觉的方式展现出来的全部时间。对终端用户来说,这个时间越短体验越好。 + +这个响应时间是终端用户对系统性能的最直观印象,包括了系统响应时间和前端展现时间。 + + +系统响应时间,反应的是系统能力,又可以进一步细分为应用系统处理时间、数据库处理时间和网络传输时间等; +前端展现时间,取决于用户端的处理能力。 + + +从这个角度来看,你就可以非常容易理解性能测试为什么会分为后端(服务器端)的性能测试和前端(通常是浏览器端)的性能测试了。 + +系统运维人员眼中的软件性能 + +从软件系统运维(也就是系统运维人员)的角度,软件性能除了包括单个用户的响应时间外,更要关注大量用户并发访问时的负载,以及可能的更大负载情况下的系统健康状态、并发处理能力、当前部署的系统容量、可能的系统瓶颈、系统配置层面的调优、数据库的调优,以及长时间运行稳定性和可扩展性。 + +大多数情况下,系统运维人员和终端用户是站在同一条战线上的,希望系统的响应速度尽可能地快。但,某些情况下他们的意见是对立的,最常见的情况就是,系统运维人员必须在最大并发用户数和系统响应时间之间进行权衡取舍。比如,当有两套系统配置方案可以提供以下系统能力的时: + + +配置方案A可以提供100万并发访问用户的能力,此时用户的登录响应时间是3秒; +配置方案B可以提供500万并发访问用户的能力,此时用户的登录响应时间是8秒。 + + +这时,从全局利益最大化角度来看,系统具有更大并发用户承载能力的价值会更大,所以运维人员一般都会选择方案B。 + +目前,有些系统为了能够承载更多的并发用户,往往会牺牲等待时间而引入预期的等待机制。比如,火车票购票网站,就在处理极大并发用户时采用了排队机制,以尽可能提高系统容量,但却增加了用户实际感受到的响应时间。 + +软件设计开发人员眼中的软件性能 + +从软件系统开发(也就是软件设计开发人员)的角度来讲,软件性能关注的是性能相关的设计和实现细节,这几乎涵盖了软件设计和开发的全过程。 + +在大型传统软件企业中,软件性能绝不仅仅是性能测试阶段要考虑的问题,而是整个软件研发生命周期都要考虑的内容,我们往往把围绕性能相关的活动称为“性能工程”(Performance Engineering)。我曾在惠普软件研发中心的性能测试卓越中心负责这方面的技术工作,所以感受颇深。 + +在软件设计开发人员眼中,软件性能通常会包含算法设计、架构设计、性能最佳实践、数据库相关、软件性能的可测试性这五大方面。其中,每个方面关注的点,也包括很多。 + +第一,算法设计包含的点: + + +核心算法的设计与实现是否高效; +必要时,设计上是否采用buffer机制以提高性能,降低I/O; +是否存在潜在的内存泄露; +是否存在并发环境下的线程安全问题; +是否存在不合理的线程同步方式; +是否存在不合理的资源竞争。 + + +第二,架构设计包含的内容: + + +站在整体系统的角度,是否可以方便地进行系统容量和性能扩展; +应用集群的可扩展性是否经过测试和验证; +缓存集群的可扩展性是否经过测试和验证; +数据库的可扩展性是否经过测试和验证。 + + +第三,性能最佳实践包含的点: + + +代码实现是否遵守开发语言的性能最佳实践; +关键代码是否在白盒级别进行性能测试; +是否考虑前端性能的优化; +必要的时候是否采用数据压缩传输; +对于既要压缩又要加密的场景,是否采用先压缩后加密的顺序。 + + +第四,数据库相关的点: + + +数据库表设计是否高效; +是否引入必要的索引; +SQL语句的执行计划是否合理; +SQL语句除了功能是否要考虑性能要求; +数据库是否需要引入读写分离机制; +系统冷启动后,缓存大量不命中的时候,数据库承载的压力是否超负荷。 + + +第五,软件性能的可测试性包含的点: + + +是否为性能分析(Profiler)提供必要的接口支持; +是否支持高并发场景下的性能打点; +是否支持全链路的性能分析。 + + +需要注意的是,软件开发人员一般不会关注系统部署级别的性能,比如软件运行目标操作系统的调优、应用服务器的参数调优、数据库的参数调优、网络环境的调优等。 + +系统部署级别的性能测试,目前一般是在系统性能测试阶段或者系统容量规划阶段,由性能测试人员、系统架构师,以及数据库管理员(DBA)协作完成。 + +性能测试人员眼中的软件性能 + +从性能工程的角度看,性能测试工程师关注的是算法设计、架构设计、性能最佳实践、数据库相关、软件性能的可测试性这五大方面。 + +在系统架构师、DBA,以及开发人员的协助下,性能测试人员既要能够准确把握软件的性能需求,又要能够准确定位引起“不好”性能表现的制约因素和根源,并提出相应的解决方案。 + +一个优秀的性能测试工程师,一般需要具有以下技能: + + +性能需求的总结和抽象能力; +根据性能测试目标,精准的性能测试场景设计和计算能力; +性能测试场景和性能测试脚本的开发和执行能力; +测试性能报告的分析解读能力; +性能瓶颈的快速排查和定位能力; +性能测试数据的设计和实现能力; +面对互联网产品,全链路压测的设计与执行能力,能够和系统架构师一起处理流量标记、影子数据库等的技术设计能力; +深入理解性能测试工具的内部实现原理,当性能测试工具有限制时,可以进行扩展二次开发; +极其宽广的知识面,既要有“面”的知识,比如系统架构、存储架构、网络架构等全局的知识,还要有大量“点”的知识积累,比如数据库SQL语句的执行计划调优、JVM垃圾回收(GC)机制、多线程常见问题等等。 + + +看到如此多的技能要求你可能有点害怕,的确,性能测试的专业性比较强,我经常把优秀的性能工程师比作是优秀的医生,也是这个原因。你需要在实际项目中积累大量的实际案例,才能慢慢培养所谓的“性能直觉”,从我个人的学习路径来讲也是如此。 + +天下无难事只怕有心人,所以抓住一切可以充实自己的机会吧,我们终将会破茧成蝶。 + +这就是终端用户、系统运维工程师、软件开发工程师,以及性能测试工程师眼中的性能测试了,至此我们也就非常容易理解,不同的群体对同一个系统的性能要求为什么会如此不同。 + +现在,我再来和你说说衡量软件性能的三个最常用的指标:并发用户数、响应时间,以及系统吞吐量。只要你接触过性能测试,或者你的团队开展过性能测试,你都应该听说这三个指标。但其实很多人对它们的理解还都停留在表面,并没有深入细致地考虑过其本质与内涵,这也导致了性能测试很多时候并没有发挥应有的作用。 + +因此,接下来我会和你深入地聊聊这三个指标的内涵和外延,帮助你获得一个全新的认识。 + +并发用户数 + +并发用户数,是性能需求与测试最常用,也是最重要的指标之一。它包含了业务层面和后端服务器层面的两层含义。 + + +业务层面的并发用户数,指的是实际使用系统的用户总数。但是,单靠这个指标并不能反映系统实际承载的压力,我们还要结合用户行为模型才能得到系统实际承载的压力。 +后端服务器层面的并发用户数,指的是“同时向服务器发送请求的数量”,直接反映了系统实际承载的压力。 + + +为了让你更好地理解这两层含义之间的区别,我们先一起来看一个实例:一个已经投入运行的ERP系统,该系统所在企业共有5000名员工并都拥有账号,也就是说这个系统有5000个潜在用户。 + +根据系统日志分析得知,该系统最大在线用户数是2500人,那么从宏观角度来看,2500就是这个系统的最大并发用户数。但是,2500这个数据仅仅是说在最高峰时段有2500个用户登录了系统,而服务器所承受的压力取决于登录用户的行为,所以它并不能准确表现服务器此时此刻正在承受的压力。 + +假设在某一时间点上,这2500个用户中,30%用户处于页面浏览状态(对服务器没有发起请求),20%用户在填写订单(也没有对服务器发起请求),5%用户在递交订单,15%用户在查询订单,而另外的30%用户没有进行任何操作。那么此时,这2500个“并发用户”中真正对服务器产生压力的只有500个用户((5%+15%)*2500=500)。 + +在这个例子中,5000是最大的“系统潜在用户数”,2500是最大的“业务并发用户数”,500则是某个时间点上的“实际并发用户数”。而此时这500个用户同时执行业务操作所实际触发的服务器端的所有调用,叫作“服务器并发请求数”。 + +从这个例子可以看出,在系统运行期间的某个时间点上,有一个指标叫作“同时向服务器发送请求的数量”,这个“同时向服务器发送请求的数量”就是服务器层面的并发用户数,这个指标同时取决于业务并发用户数和用户行为模式,而且用户行为模式占的比重较大。 + +因此,分析得到准确的用户行为模式,是性能测试中的关键一环。但,获得精准的用户行为模式,是除了获取性能需求外,最困难的工作。 + +目前,获取用户行为模式的方法,主要分为两种: + + +对于已经上线的系统来说,往往采用系统日志分析法获取用户行为统计和峰值并发量等重要信息; +而对于未上线的全新系统来说,通常的做法是参考行业中类似系统的统计信息来建模,然后分析。 + + +响应时间 + +通俗来讲,响应时间反映了完成某个操作所需要的时间,其标准定义是“应用系统从请求发出开始,到客户端接收到最后一个字节数据所消耗的时间”,是用户视角软件性能的主要体现。 + +响应时间,分为前端展现时间和系统响应时间两部分。其中,前端时间,又称呈现时间,取决于客户端收到服务器返回的数据后渲染页面所消耗的时间;而系统响应时间,又可以进一步划分为Web服务器时间、应用服务器时间、数据库时间,以及各服务器间通信的网络时间。 + +除非是针对前端的性能测试与调优,软件的性能测试一般更关注服务器端。但是,服务器端响应时间的概念非常清晰、直接,就是指从发出请求起到处理完成的时间,没有二义性;而前端时间的定义,在我看来存在些歧义。所以,接下来我会和你详细聊聊前端时间这个话题。 + +虽然前端时间一定程度上取决于客户端的处理能力,但是前端开发人员现在还会使用一些编程技巧在数据尚未完全接收完成时呈现数据,以减少用户实际感受到的主观响应时间。也就是说,我们现在会普遍采用提前渲染技术,使得用户实际感受到的响应时间通常要小于标准定义的响应时间。 + +鉴于此,我认为响应时间的标准定义就不尽合理了,尤其是对于“接收到最后一个字节”。 + +我来举个实际案例吧。加载一个网页时,如果10秒后还是白屏,那你一定会感觉很慢、性能无法接受。但是,回想一下你曾经上新浪网的经历,当加载新浪首页时,你应该不会感觉速度很慢吧。其实,实际情况是,新浪首页的加载时间要远大于10秒,只是新浪采用了数据尚未完全接收完成时进行呈现的技术,大大缩短了用户主观感受到的时间,提升了用户体验。 + +所以,严格来讲,响应时间应该包含两层含义:技术层面的标准定义和基于用户主观感受时间的定义。而在性能测试过程中,我们应该使用哪个层面的含义将取决于性能测试的类型。显然,对于软件服务器端的性能测试肯定要采用标准定义,而对于前端性能评估,则应该采用用户主观感受时间的定义。 + +当然,我们在前端性能测试中,会利用一些事件的触发(比如DOM-Load、Page-load等)来客观地衡量“主观的前端性能”。这部分内容我会在后面介绍前端性能测试时,和你详细讨论。 + +系统吞吐量 + +系统吞吐量,是最能直接体现软件系统负载承受能力的指标。 + +这里需要注意的是,所有对吞吐量的讨论都必须以“单位时间”作为基本前提。其实,我认为把“Throughput”翻译成吞吐率更贴切,因为我们可以这样理解:吞吐率=吞吐量/单位时间。但既然国内很多资料已经翻译为了“吞吐量”,所以通常情况下我们不会刻意去区分吞吐量和吞吐率,统称为吞吐量。 + +对性能测试而言,通常用“Requests/Second”“Pages/Second”“Bytes/Second”来衡量吞吐量。当然,从业务的角度来讲,吞吐量也可以用单位时间的业务处理数量来衡量。 + +以不同方式表达的吞吐量可以说明不同层次的问题。比如: + + +“Bytes/Second”和“Pages/Second”表示的吞吐量,主要受网络设置、服务器架构、应用服务器制约; +“Requests/Second”表示的吞吐量,主要受应用服务器和应用本身实现的制约。 + + +这里需要特别注意的是:虽说吞吐量可以反映服务器承受负载的情况,但在不同并发用户数的场景下,即使系统具有相近的吞吐量,但是得到的系统性能瓶颈也会相差甚远。 + +比如,某个测试场景中采用100个并发用户,每个用户每隔1秒发出一个Request,另外一个测试场景采用1000个并发用户,每个用户每隔10秒发出一个Request。显然这两个场景具有相同的吞吐量, 都是100 Requests/second,但是两种场景下的系统性能拐点肯定不同。因为,两个场景所占用的资源是不同的。 + +这就要求性能测试场景的指标,必然不是单个,需要根据实际情况组合并发用户数、响应时间这两个指标。 + +总结 + +作为性能测试系列的第一篇文章,我和你一起梳理了软件性能、软件性能测试相关的知识点,旨在你对加深软件性能指标的理解,为后续的性能测试实战打好基础。 + +首先,我从终端用户、系统运维人员、软件设计开发人员和性能测试人员,这四个维度介绍了软件系统的性能到底指的是什么: + + +终端用户希望自己的业务操作越快越好; +系统运维人员追求系统整体的容量和稳定; +开发人员以“性能工程”的视角关注实现过程的性能; +性能测试人员需要全盘考量、各个击破。 + + +然后,我介绍了软件性能的三个最常用的指标:并发用户数,响应时间,系统吞吐量: + + +并发用户数包含不同层面的含义,既可以指实际的并发用户数,也可以指服务器端的并发数量; +响应时间也包含两层含义,技术层面的标准定义和基于用户主观感受时间的定义; +系统吞吐量是最能直接体现软件系统承受负载能力的指标,但也必须和其他指标一起使用才能更好地说明问题。 + + +思考题 + +系统吞吐量的表现形式有很多,比如“Requests/Second”“Pages/Second”“Bytes/Second”等,你在性能测试项目中,选择具体的系统吞吐量指标时,会考虑哪些因素呢? + +欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/29聊聊性能测试的基本方法与应用领域.md b/专栏/软件测试52讲/29聊聊性能测试的基本方法与应用领域.md new file mode 100644 index 0000000..668bd3a --- /dev/null +++ b/专栏/软件测试52讲/29聊聊性能测试的基本方法与应用领域.md @@ -0,0 +1,250 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 29 聊聊性能测试的基本方法与应用领域 + 你好,我是茹炳晟。今天我和你分享的主题是:聊聊性能测试的基本方法与应用领域。 + +在上一次分享《带你一起解读不同视角的软件性能与性能指标》这个主题时,我介绍了衡量软件性能的三个最主要的指标:并发用户数、响应时间和系统吞吐量,和你分享了这个指标的内涵和外延。 + +所以,今天我会先继续上次的话题,和你分享并发用户数、响应时间和系统吞吐量这三个指标之间的关系和约束;然后,我会再和你分享性能测试七种常用方法,以及四大应用领域。 + +由于性能测试是一个很宽泛的话题,所以不同的人对性能测试的看法也不完全一样,同样一种方法可能也会有不同的表述方式。但是,从我亲身经历的实践来看,我们最关键的还是要去理解这些方法的本质和内涵,这样在面对实际问题时才能处变不惊,灵活应对。 + +虽然关于概念、方法和原理的内容会有些枯燥,但是掌握了这些看似枯燥的内容后,你会发现自己的性能测试知识体系越发完善了。当然,在这些看似枯燥的理论讲解中,我也会通过类比的方式,帮助你理解。如果你觉得不过瘾,还想知道一些更细节的实现,欢迎你给我留言,我们一起来讨论。 + +并发用户数、响应时间、系统吞吐量之间的关系 + +并发用户数、响应时间、系统吞吐量,这三个名词的含义可能就已经让你感觉云里雾里了,因此我会通过一个我们日常生活中的体检为例,再来解释一下它们到底是什么,以及它们之间的关系和约束。 + +你先来想象这样一个场景:假设你找了一份新工作,入职前需要到体检中心完成入职体检。 + +在体检中心做检查的过程,通常是先到前台登记个人信息并领取体检单,然后根据体检单的检查项目依次完成不同科室的检查。 + +假设一共有5个科室,每个科室有3个候诊室,你发现体检中心有很多人都在做检查,那么你一般会选择先做排队人数较少的检查项目,直至完成5个科室的全部检查,最后离开体检中心。 + +现在,我们做个类比:把整个体检中心想象成一个软件系统,从你进入体检中心到完成全部检查离开所花费的时间就是响应时间,而同时在体检中心参加体检的总人数就是并发用户数,那么系统吞吐量就可以想象成是单位时间内完成体检的人数,比如每小时100人。 + +如果你到达体检中心的时间比较早,这时人还很少,5个科室都不用排队,那么你就能以最短的时间完成体检。 + +也就是说,当系统的并发用户数比较少时,响应时间就比较短;但是由于整体的并发用户数少,所以系统的吞吐量也很低。从中,我们可以得出这样的结论: + + +当系统并发用户数较少时,系统的吞吐量也低,系统处于空闲状态,我们往往把这个阶段称为 “空闲区间”。 + + +如果你到达体检中心时,这里的人已经比较多了,只有部分科室不需要排队,但好在每个科室都有3个候诊室同时进行检查,所以排队时间不会很长,你还是可以在较短的时间完成体检。 + +也就是说,当系统的并发用户数比较多时,响应时间不会增加太多,因此系统的整体吞吐量也随着并发用户数的变大而变大的。从中,我们可以得出这样的结论: + + +当系统整体负载并不是很大时,随着系统并发用户数的增长,系统的吞吐量也会随之呈线性增长,我们往往把这个阶段称为 “线性增长区间”。 + + +但是,当体检中心的人越来越多时,每个科室都需要排队,而且每个科室的队伍都很长,你每检查完一个项目都要花很长时间去排队进行下一个检查项目。这样一来,你完成体检的时间就会明显变长。 + +也就是说,系统的并发用户数达到一定规模时,每个用户的响应时间都会明显变长,所以系统的整体吞吐量并不会继续随着并发用户数的增长而增长。从中,我们可以得出这样的结论: + + +随着系统并发用户数的进一步增长,系统的处理能力逐渐趋于饱和,因此每个用户的响应时间会逐渐变长。相应地,系统的整体吞吐量并不会随着并发用户数的增长而继续呈线性增长。我们往往把这个阶段称为系统的“拐点”。 + + +最糟糕的情况来了,如果体检中心的人继续增加,你会发现连排队、站人的地方都没有了,所有人都被堵在了一起,候诊室中检查完的人出不来,排队的人又进不去。 + +也就是说,系统的并发用户数已经突破极限,每个用户的响应时间变得无限长,因此系统的整体吞吐量变成了零。换言之,此时的系统已经被压垮了。从中,我们可以得出这样的结论: + + +随着系统并发用户数的增长,系统处理能力达到过饱和状态。此时,如果继续增加并发用户数,最终所有用户的响应时间会变得无限长。相应地,系统的整体吞吐量会降为零,系统处于被压垮的状态。我们往往把这个阶段称为“过饱和区间”。 + + +通过这个类比,相信你已经对并发用户数、响应时间和系统吞吐量理解得更透彻了,对于它们之间的关系和约束,也都了然于胸了。 + +只有理解了这些主要性能指标之间的约束关系,我们才能在实际的性能测试实践中设计有的放矢的性能测试场景。比如,后端性能测试的测试负载,我们一般只会把它设计在“线性增长区间”内;而压力测试的测试负载,我们则会将它设计在系统“拐点”上下,甚至是“过饱和区间”。 + +那么,接下来让我们一起来看一下性能测试的方法都有哪些。 + +常用的七种性能测试方法 + +根据在实际项目中的实践经验,我把常用的性能测试方法分为七大类:后端性能测试(Back-end Performance Test)、前端性能测试(Front-end Performance Test)、代码级性能测试(Code-level Performance Test)、压力测试(Load/Stress Test)、配置测试(Configuration Test)、并发测试(Concurrence Test),以及可靠性测试(Reliability Test)。接下来,我将详细为你介绍每一种测试方法。 + +第一,后端性能测试 + +其实,你平时听到的性能测试,大多数情况下指的是后端性能测试,也就是服务器端性能测试。 + +后端性能测试,是通过性能测试工具模拟大量的并发用户请求,然后获取系统性能的各项指标,并且验证各项指标是否符合预期的性能需求的测试手段。 + +这里的性能指标,除了包括并发用户数、响应时间和系统吞吐量外,还应该包括各类资源的使用率,比如系统级别的CPU占用率、内存使用率、磁盘I/O和网络I/O等,再比如应用级别以及JVM级别的各类资源使用率指标等。 + +由于需要模拟的并发用户数,通常在“几百”到“几百万”的数量级,所以你选择的性能测试工具,一定不是基于GUI的,而是要采用基于协议的模拟方式,也就是去模拟用户在GUI操作的过程中实际向后端服务发起的请求。 + +只有这样才能模拟很高的并发用户数,尽可能地模拟出真实的使用场景,这也是现在所有后端性能测试工具所采用的方法。 + +根据应用领域的不同,后端性能测试的场景设计主要包括以下两种方式: + + +基于性能需求目标的测试验证; +探索系统的容量,并验证系统容量的可扩展性 + + +第二,前端性能测试 + +前端性能测试并没有一个严格的定义和标准。 + +通常来讲,前端性能关注的是浏览器端的页面渲染时间、资源加载顺序、请求数量、前端缓存使用情况、资源压缩等内容,希望借此找到页面加载过程中比较耗时的操作和资源,然后进行有针对性的优化,最终达到优化终端用户在浏览器端使用体验的目的。 + +目前,业界普遍采用的前端测试方法,是雅虎(Yahoo)前端团队总结的7大类35条前端优化规则,你可以通过雅虎网站查看这些规则,以及对各规则的详细解读。 + +我在这里列出了其中几个最典型也是最重要的规则,来帮助你理解前端性能测试优化的关注范围。 + + +减少http请求次数:http请求数量越多,执行过程耗时就越长,所以可以采用合并多个图片到一个图片文件的方法来减少http请求次数,也可以采用将多个脚本文件合并成单一文件的方式减少http请求次数; +减少DNS查询次数:DNS的作用是将URL转化为实际服务器主机IP地址,实现原理是分级查找,查找过程需要花费20~100ms的时间,所以一方面我们要加快单次查找的时间,另一方面也要减少一个页面中资源使用了多个不同域的情况; +避免页面跳转:页面跳转相当于又打开一个新的页面,耗费的时间就会比较长,所以要尽量避免使用页面跳转; +使用内容分发网络(CDN):使用CDN相当于对静态内容做了缓存,并把缓存内容放在网络供应商(ISP)的机房,用户根据就近原则到ISP机房获取这些被缓存了的静态资源,因此可以大幅提高性能; +Gzip压缩传输文件:压缩可以帮助减小传输文件的大小,进而可以从网络传输时间的层面来减少响应时间; + + +第三,代码级性能测试 + +代码级性能测试,是指在单元测试阶段就对代码的时间性能和空间性能进行必要的测试和评估,以防止底层代码的效率问题在项目后期才被发现的尴尬。 + +如果你从事过性能测试相关的工作,一定遇到过这样的场景:系统级别的性能测试发现一个操作的响应时间很长,然后你要花费很多时间去逐级排查,最后却发现罪魁祸首是代码中某个实现低效的底层算法。这种自上而下的逐级排查定位的方法,效率通常都很低,代价也很高。 + +所以,我们就需要在项目早期,对一些关键算法进行代码级别的性能测试,以防止此类在代码层面就可以被发现的性能问题,遗留到最后的系统性能测试阶段才被发现。 + +但是,从实际执行的层面来讲,代码级性能测试并不存在严格意义上的测试工具,通常的做法是:改造现有的单元测试框架。 + +最常使用的改造方法是: + + +将原本只会执行一次的单元测试用例连续执行n次,这个n的取值范围通常是2000~5000; + +统计执行n次的平均时间。如果这个平均时间比较长(也就是单次函数调用时间比较长)的话,比如已经达到了秒级,那么通常情况下这个被测函数的实现逻辑一定需要优化。 + + +这里之所以采用执行n次的方式,是因为函数执行时间往往是毫秒级的,单次执行的误差会比较大,所以采用多次执行取平均值的做法。 + +第四,压力测试 + +压力测试,通常指的是后端压力测试,一般采用后端性能测试的方法,不断对系统施加压力,并验证系统化处于或长期处于临界饱和阶段的稳定性以及性能指标,并试图找到系统处于临界状态时的主要瓶颈点。所以,压力测试往往被用于系统容量规划的测试。 + +还有些情况,在执行压力测试时,我们还会故意在临界饱和状态的基础上继续施加压力,直至系统完全瘫痪,观察这个期间系统的行为;然后,逐渐减小压力,观察瘫痪的系统是否可以自愈。 + +第五,配置测试 + +配置测试,主要用于观察系统在不同配置下的性能表现,通常使用后端性能测试的方法: + + +通过性能基准测试(Performance Benchmark)建立性能基线(Performance Baseline); + +在此基础上,调整配置; + +基于同样的性能基准测试,观察不同配置条件下系统性能的差异,根本目的是要找到特定压力模式下的最佳配置。 + + +这里需要注意的是,“配置”是一个广义配置的概念,包含了以下多个层面的配置: + + +宿主操作系统的配置; +应用服务器的配置; +数据库的配置; +JVM的配置; +网络环境的配置; +… + + +第六,并发测试 + +并发测试,指的是在同一时间,同时调用后端服务,期间观察被调用服务在并发情况下的行为表现,旨在发现诸如资源竞争、资源死锁之类的问题。 + +谈到并发测试,我就不得不和你说说“集合点并发”的概念了,它源于HP的LoadRunner,目前已经被广泛使用了。那,到底什么是“集合点并发”呢? + +假设我们希望后端调用的并发数是100,如果直接设定100个并发用户是无法达到这个目标的,因为这100个并发用户会各自执行各自的操作,你无法控制某一个确定的时间点上后端服务的并发数量。 + +为了达到准确控制后端服务并发数的目的,我们需要让某些并发用户到达该集合点时,先处于等待状态,直到参与该集合的全部并发用户都到达时,再一起向后端服务发起请求。简单地说,就是先到的并发用户要等着,等所有并发用户都到了以后,再集中向后端服务发起请求。 + +比如,当要求的集合点并发数是100时,那么前99个到达的用户都会等在那里,直到第100个用户到了,才集中向后端服务发起请求。当然,实际达到服务器的并发请求数,还会因为网络延迟等原因小于100。 + +所以,在实际项目中,我建议在要求的并发数上进行适当放大,比如要求的并发数是100,那我们集合点并发数可以设置为120。 + +第七,可靠性测试 + +可靠性测试,是验证系统在常规负载模式下长期运行的稳定性。 + +虽然可靠性测试在不同公司的叫法不同,但其本质就是通过长时间模拟真实的系统负载来发现系统潜在的内存泄漏、链接池回收等问题。 + +由于真实环境下的实际负载,会有高峰和低谷的交替变化(比如,对于企业级应用,白天通常是高峰时段,而晚上则是低峰时段),所以为了尽可能地模拟出真实的负载情况,我们会每12小时模拟一个高峰负载,两个高峰负载中间会模拟一个低峰负载,依次循环3-7天,形成一个类似于“波浪形”的系统测试负载曲线。 + +然后,用这个“波浪形”的测试负载模拟真实的系统负载,完成可靠性测试。同样地,可靠性测试也会持续3-7天。 + +聊完了常用性能测试方法的种类后,我们再来简单看一下性能测试的四大应用领域,以及每个应用领域都会使用哪些性能测试方法。 + +性能测试的四大应用领域 + +不同的性能测试方法适用于不同的应用领域去解决不同的问题,这里“不同的应用领域”主要包括能力验证、能力规划、性能调优、缺陷发现这四大方面。每个应用领域可以根据自身特点,选择合适的测试方法。 + +第一,能力验证 + +能力验证是最常用,也是最容易理解的性能测试的应用领域,主要是验证“某系统能否在A条件下具有B能力”,通常要求在明确的软硬件环境下,根据明确的系统性能需求设计测试方案和用例。 + +能力验证这个领域最常使用的测试方法,包括后端性能测试、压力测试和可靠性测试。 + +第二,能力规划 + +能力规划关注的是,如何才能使系统达到要求的性能和容量。通常情况下,我们会采用探索性测试的方式来了解系统的能力。 + +能力规划解决的问题,主要包括以下几个方面: + + +能否支持未来一段时间内的用户增长; +应该如何调整系统配置,使系统能够满足不断增长的用户数需求; +应用集群的可扩展性验证,以及寻找集群扩展的瓶颈点; +数据库集群的可扩展性验证; +缓存集群的可扩展性验证; +… + + +能力规划最常使用的测试方法,主要有后端性能测试、压力测试、配置测试和可靠性测试。 + +第三,性能调优 + +性能调优,其实是性能测试的延伸。在一些大型软件公司,会有专门的性能工程(Performance Engineering)团队,除了负责性能测试的工作外,还会负责性能调优。 + +性能调优主要解决性能测试过程中发现的性能瓶颈的问题,通常会涉及多个层面的调整,包括硬件设备选型、操作系统配置、应用系统配置、数据库配置和应用代码实现的优化等等。 + +这个领域最常用的测试方法,涵盖了我在上面分享的七大类测试方法,即后端性能测试、前端性能测试、代码级性能测试、压力测试、配置测试、并发测试和可靠性测试。 + +第四,缺陷发现 + +缺陷发现,是一个比较直接的应用领域,通过性能测试的各种方法来发现诸如内存泄露、资源竞争、不合理的线程锁和死锁等问题。 + +缺陷发现,最常用的测试方法主要有并发测试、压力测试、后端性能测试和代码级性能测试。 + +上面这些内容就是性能测试的常用方法和应用领域了,我用一张表汇总了各个应用领域需要用到的测试方法,希望可以帮助你记忆、理解。 + + + +总结 + +今天我通过一个生活中“体检”的实例,和你分享了并发用户数、响应时间和系统吞吐量三者之间的关系: + + +当系统整体负载并不是很大时,随着并发用户数的增长,系统的吞吐量也会随之线性增长; +随着并发用户数的进一步增长,系统处理能力逐渐趋于饱和,因此每个用户的响应时间会逐渐变长,相应地,系统的整体吞吐量并不会随着并发用户数的增长而继续线性增长。 +如果并发用户数再继续增长,系统处理能力达到过饱和状态,此时所有用户的响应时间会变得无限长,相应地,系统的整体吞吐量会降为零,系统处于被压垮的状态。 + + +然后,我跟你分享了后端性能测试、前端性能测试、代码级性能测试、压力测试、配置测试、并发测试,以及可靠性测试这七种常用的性能测试方法,并探讨了这七种方法在能力验证、能力规划、性能调优和缺陷发现这四种场景下的使用情况。 + +希望我今天的分享,可以帮你打好性能测试知识体系的基础,解决你在实际项目中遇到的问题。 + +思考题 + +我今天提到的性能测试的七种测试方法,以及四大应用领域,你在实际的工程项目中接触过哪些呢?在使用过程中,你有没有遇到过什么问题,又是如何解决的? + +感谢你的收听,欢迎给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/30工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介.md b/专栏/软件测试52讲/30工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介.md new file mode 100644 index 0000000..4cf9f5a --- /dev/null +++ b/专栏/软件测试52讲/30工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介.md @@ -0,0 +1,173 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 30 工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介 + 你好,我是茹炳晟。今天我和你分享的主题是:工欲善其事必先利其器之后端性能测试工具原理与行业常用工具简介。 + +我在《聊聊性能测试的基本方法与应用领域》这个主题里介绍了七种测试方法,但不管是什么类型的性能测试方法,都需要去模拟大量并发用户的同时执行,所以性能测试基本都是靠工具实现。没有工具,性能测试将寸步难行。 + +所以,我今天就从后端性能测试的工具讲起,和你一起讨论它们的实现原理,以及如何用于后端的性能测试。另外,我还会和你分享一些中大型互联网企业选择的性能测试工具。 + +由于我今天要分享的知识点比较多,而且是相对独立的,所以我会采用问答的形式展开这些内容。我希望通过今天的分享,你能够对以下的问题和知识点有完整、清晰的理解与认识: + + +后端性能测试和后端性能测试工具之间的关系是什么? +后端性能测试工具和GUI自动化测试工具最大的区别是什么? +后端性能测试工具的原理是什么? +后端性能测试中,性能测试场景设计是什么意思,具体会涉及哪些内容? +业内主流的后端性能测试工具有哪些? + + +后端性能测试和后端性能测试工具之间的关系是什么? + +后端性能测试工具是实现后端性能测试的技术手段,但是千万不要简单地把使用后端性能测试工具等同于后端性能测试,它只是后端性能测试中的一个必要步骤而已。 + +完整的后端性能测试应该包括性能需求获取、性能场景设计、性能测试脚本开发、性能场景实现、性能测试执行、性能结果报告分析、性能优化和再验证。 + +在这其中,后端性能测试工具主要在性能测试脚本开发、性能场景实现、性能测试执行这三个步骤中发挥作用,而其他环节都要依靠性能测试工程师的专业知识完成。 + +是不是感觉有点抽象,难以理解呢?我来做个类比吧。 + +假如你现在要去医院看病,医生会根据你对身体不适的描述,要求你先去验血,并确定需要检查的血液指标。验血是通过专业的医疗仪器分析你的血样,并得到验血报告。 + +医生拿到验血报告后,根据常年积累的专业知识,然后结合验血报告的各项指标以及指标之间的相互关系判断你的病情,并给出诊断结果以及相应的治疗措施。 + +同样的验血报告,如果给不懂医术的人看,就是一堆没有意义的数据;如果给一个初级医生看,他可能只能基于单个指标的高低给出可能的推测;但是,如果是给一个具有丰富临床经验的医生看,他往往可以根据这些指标以及它们之间的相互关系给出很明确的诊断结果。 + +现在,我把这个过程和性能测试做个类比,把性能测试对应到整个看病的过程: + + +需求获取对应的是你向医生描述身体不适细节的过程,医生需要知道要帮你解决什么问题; +设计性能场景对应的是医生决定需要检查哪些血液指标的过程; +使用性能测试工具对应的是使用医疗仪器分析血样的过程; +性能测试报告对应的就是验血报告; +性能测试人员分析性能结果报告的过程,对应的是医生解读验血报告的过程; +性能测试人员根据性能报告进行性能优化的过程,对应的是医生根据验血报告判断你的病情,并给出相应治疗措施的过程。 + + +所以,在我看来使用性能测试工具获得性能测试报告只是性能测试过程中的一个必要步骤而已,而得出报告的目的是让性能测试工程师去做进一步的分析,以得出最终结论,并给出性能优化的措施。 + +后端性能测试工具和GUI自动化测试工具最大的区别是什么? + +虽然后端性能测试工具和GUI自动化测试工具都是通过自动化的手段模拟终端用户使用系统的行为,但是两者实现的原理截然不同。 + +第一个显著区别是,模拟用户行为的方式。 + +GUI自动化测试工具模拟的是用户的界面操作,因此测试脚本记录的是用户在界面上对控件的操作;而性能测试工具模拟的是用户的客户端与服务器之间的通信协议和数据,这些通信协议和数据往往是用户在界面上执行GUI操作时产生的。 + +明白了这一点,你自然就能明白为什么录制虚拟用户性能测试脚本时,我们需要先选定录制协议了。 + +另外,正是由于脚本的模拟是基于协议的,所以我们才能比较方便地模拟成千上万并发用户同时使用系统的场景;否则,如果性能测试基于GUI发起,那我们就需要成千上万的浏览器同时执行用例,而这显然是不可能的。 + +第二个显著的区别是,测试的执行方式。 + +GUI自动化测试的执行,一般是单用户执行并验证功能结果;而性能测试的执行,往往需要同时模拟大量的并发用户,不仅需要验证业务功能是否成功完成,还要收集各种性能监控指标,会涉及到压力产生器、并发用户调度控制、实时监控收集等内容,所以性能测试的执行控制要比GUI自动化测试复杂得多。 + +这部分内容,我稍后在第32和33这两篇文章中详细展开。 + +后端性能测试工具的原理是什么? + +虽然后端性能测试工具种类很多,但是由于都不能通过GUI的方式来模拟并发,所以其基本原理和主要概念基本一致。 + +首先,后端性能测试工具会基于客户端与服务器端的通信协议,构建模拟业务操作的虚拟用户脚本。对于目前主流的Web应用,通常是基于HTTP/HTTPS协议;对于Web Service应用,是基于Web Service协议;至于具体基于哪种协议,你需要和开发人员或者架构师确认,当然现在有些后端性能测试工具也可以直接帮你检测协议的种类。 + +我们把这些基于协议模拟用户行为的脚本称为虚拟用户脚本,而把开发和产生这些脚本的工具称为虚拟用户脚本生成器。 + +不同后端性能测试工具的虚拟用户脚本生成器,在使用上的区别比较大:比如,LoadRunner是通过录制后再修改的方式生成虚拟用户脚本;而JMeter主要是通过添加各种组件,然后对组件进行配置的方式生成虚拟用户脚本。 + +虽然LoadRunner也支持采用直接开发的方式产生虚拟用户脚本,但是因为开发难度太大,所以基本上都是采用先录制再开发的方式,不会直接去开发。另外,虽然JMeter也支持录制,但是JMeter的录制功能是通过设置代理完成的,而且录制出来的脚本都是原始的http请求,并没有经过适当的封装,所以录制功能比较弱。 + +虽然不同工具的使用方式各有特色,但其本质上都是通过协议模拟用户的行为。 + +然后,开发完成了虚拟用户脚本之后,后端性能测试工具会以多线程或多进程的方式并发执行虚拟用户脚本,来模拟大量并发用户的同时访问,从而对服务器施加测试负载。 + +其中,我们把实际发起测试负载的机器称为压力产生器。受限于CPU、内存,以及网络带宽等硬件资源,一台压力产生器能够承载的虚拟用户数量是有限的,当需要发起的并发用户数量超过了单台压力产生器能够提供的极限时,就需要引入多台压力产生器合作发起需要的测试负载。 + +一旦有了多台压力产生器,那就需要一个专门的控制器来统一管理与协调这些压力产生器,我们把这个专门的控制器称为压力控制器。压力控制器会根据性能测试场景的设计,来控制和协调多台压力产生器上的多线程或多进程执行的虚拟用户脚本,最终模拟出性能测试场景中的测试负载。 + +接着,在施加测试负载的整个过程中,后端性能测试工具除了需要监控和收集被测系统的各种性能数据以外,还需要监控被测系统各个服务器的各种软硬件资源。比如,后端性能测试工具需要监控应用服务器、数据库服务器、消息队列服务器、缓存服务器等各种资源的占用率。我们通常把完成监控和数据收集的模块称为系统监控器。 + +在性能测试执行过程中,系统监控器的数据显示界面是性能测试工程师最密切关注的部分,性能测试工程师会根据实时的数据显示来判断测试负载情况下的系统健康状况。 + +不同的后端测试工具中,系统监控器能力差别也比较大。比如,LoadRunner的系统监控器就很强大,支持收集各种操作系统的系统参数,还支持与SiteScope等第三方专业监控工具的无缝集成。 + +最后,测试执行完成后,后端性能测试工具会将系统监控器收集的所有信息汇总为完整测试报告,后端性能测试工具通常能够基于该报告生成各类指标的各种图表,还能将多个指标关联在一起进行综合分析来找出各个指标之间的关联性。我们把完成这部分工作的模块称为测试结果分析器。 + +需要强调的是,测试结果分析器只是按需提供多种不同维度和表现形式的数据展现工作,而对数据的分析工作,还是要依赖于具有丰富经验的性能测试工程师。 + +后端性能测试场景设计是什么意思,具体会涉及哪些内容? + +性能测试场景设计,是后端性能测试中的重要概念,也是压力控制器发起测试负载的依据。 + +性能测试场景设计,目的是要描述性能测试过程中所有与测试负载以及监控相关的内容。通常来讲,性能测试场景设计主要会涉及以下部分: + + +并发用户数是多少? +测试刚开始时,以什么样的速率来添加并发用户?比如,每秒增加5个并发用户。 +达到最大并发用户数后持续多长时间? +测试结束时,以什么样的速率来减少并发用户?比如,每秒减少5个并发用户。 +需要包含哪些业务操作,各个业务操作的占比是多少?比如,10%的用户在做登录操作,70%的用户在做查询操作,其他20%的用户在做订单操作。 +一轮虚拟用户脚本执行结束后,需要等待多长时间开始下一次执行? +同一虚拟用户脚本中,各个操作之间的等待时间是多少? +需要监控哪些被测服务器的哪些指标? +脚本出错时的处理方式是什么?比如,错误率达到10%时,自动停止该脚本。 +需要使用多少台压力产生器? + + +以上这些场景组合在一起,就构成了性能测试场景设计的主要内容。也就是说,性能测试场景会对测试负载组成、负载策略、资源监控范围定义、终止方式,以及负载产生规划作出定义,而其中的每一项还会包含更多的内容。具体请参见如图1所示的思维导图。 + + + +图1 性能测试场景的设计 + +业内主流的后端性能测试工具有哪些? + +目前,业内有很多成熟的后端性能测试工具,比如传统的LoadRunner、JMeter、NeoLoad等。另外,现在还有很多云端部署的后端性能测试工具或平台,比如CloudTest、Loadstorm、阿里的PTS等。 + +其中,最为常用的商业工具是HP软件(现在已经被Micro Focus收购)的LoadRunner,由于其强大的功能和广泛的协议支持,几乎已经成了性能测试工具的代名词。大量的传统软件企业,也基本都使用LoadRunner实施性能测试,所以我在后面分享企业级服务器端性能测试的实践时,也是以LoadRunner为基础展开的。 + +另外,JMeter是目前开源领域最主流的性能测试工具。JMeter的功能非常灵活,能够支持HTTP、FTP、数据库的性能测试,也能够充当HTTP代理来录制浏览器的HTTP请求,还可以根据Apache等Web服务器的日志文件回放HTTP流量,还可以通过扩展支持海量的并发。 + +然后,再加上JMeter开源免费的特点,已经被很多互联网企业广泛应用。比如,饿了么就是使用JMeter来完成系统的全链路压力测试。 + +其实,传统软件企业偏向于使用LoadRunner,而互联网企业普遍采用JMeter,是有原因的。 + +LoadRunner License是按照并发用户数收费的,并发用户数越高收费也越贵,但是LoadRunner的脚本开发功能、执行控制、系统监控以及报告功能都非常强大,易学易用。 + +而传统软件企业,需要测试的并发用户数并不会太高,通常是在几百到十几万这个数量级,而且它们很在意软件的易用性和官方支持能力,所以往往热衷于直接选择成熟的商业工具LoadRunner。 + +但是,互联网企业的并发用户请求数量很高,很多软件都会达到百万,甚至是千万的级别。那么,如果使用LoadRunner的话: + + +费用会高的离谱; + +LoadRunner对海量并发的测试支持并不太好; + +很多互联网企业还会有特定的工具需求,这些特定的需求很难在LoadRunner中实现,而在开源的JMeter中,用户完全可以根据需求进行扩展。 + + +所以互联网企业往往选用JMeter方案,而且通常会自己维护扩展版本。 + +总结 + +今天,我以问答的形式,和你分享了后端性能测试的理论,以及工具使用的问题。 + +首先,我和你解释了后端性能测试和后端性能测试工具之间的关系。在我看来使用性能测试工具获得性能测试报告,只是性能测试过程中的一个必要步骤而已,而得出报告的目的是让性能测试工程师去分析并给出性能优化的措施。 + +然后,我解释了后端性能测试工具和GUI自动化测试工具最大的区别,即它们模拟用户行为的方式以及测试的执行方式不同。 + +接着,我介绍了后端性能测试工具的基本原理。它首先通过虚拟用户脚本生成器生成虚拟用户脚本;然后根据性能测试场景设计的要求,通过压力控制器控制协调各个压力产生器以并发的方式执行虚拟用户脚本;同时,在测试执行过程中,通过系统监控器收集各种性能指标以及系统资源占用率;最后,通过测试结果分析器展示测试结果数据。 + +最后,我介绍了性能测试场景设计,并分析了业内主流的后端性能测试工具LoadRunner和JMeter,以及传统软件企业和互联网企业在选择后端性能测试工具时的考量。 + +思考题 + +除了我今天提到的后端性能测试工具外,你还接触过哪些后端性能测试工具?这些后端性能测试工具中又有哪些好的设计呢? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/31工欲善其事必先利其器:前端性能测试工具原理与行业常用工具简介.md b/专栏/软件测试52讲/31工欲善其事必先利其器:前端性能测试工具原理与行业常用工具简介.md new file mode 100644 index 0000000..1c954c8 --- /dev/null +++ b/专栏/软件测试52讲/31工欲善其事必先利其器:前端性能测试工具原理与行业常用工具简介.md @@ -0,0 +1,217 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 31 工欲善其事必先利其器:前端性能测试工具原理与行业常用工具简介 + 你好,我是茹炳晟。今天我和你分享的主题是:工欲善其事必先利其器之前端性能测试工具原理与行业常用工具简介”。 + +不同于后端性能测试知识的琐碎、独立,今天我将从问答形式回到正常的分享思路上,为你介绍前端性能测试工具为。我会以一个具体网站为例,和你分析WebPagetest的用法,以及前端性能相关的主要概念与指标。 + +WebPagetest功能简介 + +WebPagetest,是前端性能测试的利器: + + +可以为我们提供全方位的量化指标,包括页面的加载时间、首字节时间、渲染开始时间、最早页面可交互时间、页面中各种资源的字节数、后端请求数量等一系列数据; +还可以自动给出被测页面性能优化水平的评价指标,告诉我们哪些部分的性能已经做过优化处理了,哪些部分还需要改进; +同时,还能提供Filmstrip视图、Waterfall视图、Connection视图、Request详情视图和页面加载视频慢动作。 + + +可以说,WebPagetest为我们提供了前端页面性能测试所需要的一切,而且还是免费的。接下来,我们就通过测试一个具体的网站,实践一下它的强大功能,以及具体使用方法吧。 + +使用WebPagetest测试某网站的首页 + +那么,接下来我就以某网站首页的前端性能测试和评估为例,和你一起看看如何使用这个强大的前端性能工具。 + +首先,访问WebPagetest的主页“http://www.webpagetest.org/” ,也就是工具的使用界面。 + + +将被测页面的地址填写到被测Website URL栏中; + +选择测试发起的地域(Test Location)以及你希望使用的浏览器,这里我选择了美国旧金山、Chrome浏览器。 + + +WebPagetest在全球各大洲的很多国家和地区都有自己的测试代理机,这些测试代理机可能是虚拟机,也可能是真机,还有很多是建立在Amazon EC2上的云端机器。另外,WebPagetest除了支持各种浏览器以外,还支持主流的Android设备和Apple设备。 + +然后,选择需要模拟的网络情况。这里我选择了有线网络Cable,当然你也可以根据你的测试要求选择各种3G或者4G移动网络。 + +接着,在Repeat View中选择“First View and Repeat View”。这是一个很关键的选项。我们知道当使用浏览器访问网站时,第一次打开一个新的网页往往会很慢,而第二次打开通常就会快很多,这是因为浏览器端会缓存很多资源。这个选项的意思就是既要测试第一次打开页面的前端性能(First View),也要测试在有缓存情况下重复打开的前端性能(Repeat View)。 + +最后,点击“Start Test”发起测试。最终的测试设置界面,如图1所示。由于全球所有的用户会共享这些散布在各地的测试代理机,所以发起测试后,一般情况下我们的测试并不会被立即执行,而是会进入排队系统。当然,WebPagetest界面会清楚地告诉你排在第几位。 + + + +图1 WebPagetest的测试执行界面 + +测试执行完成后,我们会看到如图2所示的测试结果页面。这个页面包含非常多的信息,接下来我会一一解读这些信息,同时跟你分享前端性能指标相关的概念。 + + + +图2 WebPagetest的测试结果页面 + +前端性能评估结果评分 + +图2右上角的性能评估结果栏,列出了主要性能评估项目的评分。可以看到“First Byte Time”“Keep-alive Enabled”和“Compress Transfer”三项的评分都是A级,说明这三项做得比较好。但是,“Compress Images”“Cache static content”和“Effective use of CDN”的评分比较差,是需要优化的部分。 + +那么,接下来我们就看看这六项前端性能指标分别代表什么涵义。 + +第一,First Byte Time + +First Byte Time,指的是用户发起页面请求到接收到服务器返回的第一个字节所花费的时间。这个指标反映了后端服务器处理请求、构建页面,并且通过网络返回所花费的时间。 + +本次测试的结果,首次打开页面(First View)花费的时间是999 ms,重复打开页面(Repeat View)花费的时间是860 ms。这两个指标都在1 s以下,所以WebPagetest给出了A级的评分。 + +第二,Keep-alive Enabled + +页面上的各种资源(比如,图片、JavaScript、CSS等)都需要通过链接Web服务器来一一获取,与服务器建立新链接的过程往往比较耗费时间,所以理想的做法是尽可能重用已经建立好的链接,而避免每次使用都去创建新的链接。 + +Keep-alive Enabled就是,要求每次请求使用已经建立好的链接。它属于服务器上的配置,不需要对页面本身进行任何更改,启用了Keep-alive通常可以将加载页面的时间减少40%~50%,页面的请求数越多,能够节省的时间就越多。 + +如图3所示,本次测试的结果显示,所有的请求都复用了同一个链接,所以WebPagetest也给出了A级的评分。 + + + +图3 Keep-alive Enabled的统计结果 + +第三,Compress Transfer + +如果将页面上的各种文本类的资源,比如Html、JavaScript、CSS等,进行压缩传输,将会减少网络传输的数据量,同时由于JavaScript和CSS都是页面上最先被加载的部分,所以减小这部分的数据量会加快页面的加载速度,同时也能缩短First Byte Time。 + +为文本资源启用压缩通常也是服务器配置更改,无需对页面本身进行任何更改。 + +如图4所示,本次测试结果显示,这个网站绝大多数的文本类资源都通过GZip进行了压缩,所以WebPagetest也给出了A级的评分。但是第13和第20项的两个资源并没有被压缩,报告中显示如果这两个资源也经过压缩,将可以减少额外的24.3KB的数据传输量。 + + + +图4 Compress Transfer的统计结果 + +第四,Compress Images + +为了减少需要网络传输的数据量,图像文件也需要进行压缩处理。显然本次测试结果显示(图5),所有的JPEG格式图片都没有经过必要的压缩处理,并且所有的JPEG格式图片都没有使用渐进式JPEG(Progressive JPEG)技术,所以WebPagetest给出了D级的评分。 + +如果你不是专门做前端的工程师,可能并不知道什么是渐进式JPEG,没关系,我来简单解释一下吧。 + +普通JPEG文件存储方式是按从上到下的扫描方式,把每一行顺序地保存在JPEG文件中。打开这个文件显示它的内容时,数据将按照存储时的顺序从上到下一行一行地被显示,直到所有的数据都被读完,就完成了整张图片的显示。 + +如果文件较大或者网络下载速度较慢,就会看到图片是被一行一行加载的。为了更好的用户体验,渐进式JPEG技术就出现了。 + +渐进式JPEG包含多次扫描,然后将扫描顺序存储在JPEG文件中。打开文件的过程,会先显示整个图片的模糊轮廓,随着扫描次数的增加,图片会变得越来越清晰。这种格式的主要优点是在网络较慢时,通过图片轮廓就可以知道正在加载的图片大概是什么。 + +所以,这种技术往往被一些网站用于打开较大图片。 + +- + + +图5 Compress Images的统计结果 + +第五,Cache Static Content + +一般情况下,页面上的静态资源不会经常变化,所以如果你的浏览器可以缓存这些资源,那么当重复访问这些页面时,就可以从缓存中直接使用已有的副本,而不需要每次向Web服务器请求资源。这种做法,可以显著提高重复访问页面的性能,并减少Web服务器的负载。 + +如图6所示,本次测试结果显示,被测网站有超过一半的静态资源没有被浏览器缓存,每次都需要从Web服务器端获取,所以WebPagetest给出了F级的评分。 + +- + + +图6 Cache Static Content的统计结果 + +第六,Effective use of CDN + +首先,我来解释一下什么是CDN。 + +CDN是内容分发网络的缩写,其基本原理是采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区的网络供应商机房内,当用户访问网站时,利用全局负载技术将用户的访问指向距离最近的、工作正常的缓存服务器上,由缓存服务器直接响应用户请求。 + +理解了什么是CDN后,我们再一起来看一下本次测试中CDN的使用情况。如图7所示,显然本次被测网站绝大多数的资源并没使用CDN。也许是由于本次发起测试的机器是在美国旧金山,而旧金山可能并不是该网站的目标市场,所以它并没有在这里的CDN上部署资源。 + + + +图7 CDN使用的统计结果 + +其他前端性能指标解读 + + + +图8 WebPagetest测试显示的前端性能指标 + +现在,我们再回过头来看看如图8所示的表格,这个表格包含了很多的前端性能指标,大多数指标,我们可以从字面上很容易理解其含义,比如Load Time、First Byte、Requests等,我就不再赘述了。 + +但是,Start Render、First Interactive和Speed Index这三个指标,相对较难理解,所以我会简单为你解释一下。 + +第一,Start Render + +Start Render,指的是浏览器开始渲染的时间,从用户角度看就是在页面上看到第一个内容的时间。该时间决定了用户对页面加载快慢的的第一直观印象,这个时间越短用户会感觉页面速度越快,这样用户也会有耐心去等待其他内容的展现。如果这个时间过长,则用户会在长时间内面对一个空白页面后,失去耐心。 + +理论上讲,Start Render时间主要由三部分组成,分别是“发起请求到服务器返回第一个字节的时间(也就是First Byte时间)”“从服务器加载HTML文档的时间”,以及“HTML文档头部解析完成所需要的时间”,因此影响Start Render时间的因素就包括服务器响应时间、网络传输时间、HTML文档的大小以及HTML头中的资源使用情况。 + +本次测试中,第一次打开网页的Start Render时间是5 s,而第二次打开网页的Start Render时间是1.83 s。理想的Start Render时间并没有严格的标准,一般来情况下,这个值最好不要大于3 s,所以这个网站还可以再优化一下这个指标。 + +优化的基本思路是先找出时间到底花在了哪里,由于第二次的结果还是比较理想,所以可以从首次资源加载的角度找出突破口。 + +第二,First Interactive + +First Interactive,可以简单地理解为最早的页面可交互时间。页面中可交互的内容,包括很多种类,比如点击一个链接、点击一个按钮都属于页面可交互的范畴。First Interactive时间的长短对用户体验的影响十分重要,决定着用户对页面功能的使用,这个值越短越好。 + +为了使这个值尽可能得小,我们通常会采取以下措施: + + +只有页面控件内容渲染出来了,才有可能进行交互,所以First Interactive依赖于Start Render时间。 +尽量将交互控件的代码放置在HTML BODY的前部,让其尽早加载。 +尽早做JavaScript的初始化和绑定,目前大多数做法有两种,一是在DOM Ready中做所有JavaScript的初始化和功能绑定,二是在页面底部做JavaScript的初始化和功能绑定。 + + +这两种方式的优点在于简单,不需要关注具体DOM结点的位置;缺点则在于初始化的时间太晚。因此,应该将JavaScript的初始化提前到相关DOM元素建立起来后进行,例如将初始化的操作直接放在元素之后进行,这样就可以使控件尽可能早地变成可交互状态。 + +本次测试中,第一次打开网页的First Interactive时间是7.788 s,而第二次打开网页的First Interactive时间是略大于1.686 s的某个值。理想的First Interactive时间也没有严格的标准,一般情况下,这个值最好不要大于5 s,所以这个网站还可以根据上面的三条措施再优化一下这个指标。 + +第三,Speed Index + +严格来说,Speed Index是通过微积分定义的。我们理解起来会比较困难,所以在这里我们和你只做定性的讨论。 + +通常,影响网页性能体验的一个重要指标是页面打开时间。打开时间越短,其体验越好。但是,当存在两个打开时间完全相同的网站A和B时,其中网站A的打开过程是逐渐渲染页面完成的,而网站B的打开过程则是空白了一段时间后在短时间内完成页面渲染完成的。 + +毫无疑问,网站A的用户体验一定好于B。Speed Index就是用来衡量这种体验的,通常来讲,它的值越小越好。 + +本次测试中,第一次打开网页的Speed Index是8036,而第二次打开网页的Speed Index是2373。 + +WebPagetest实际使用中需要解决的问题 + +讨论到这里,你是不是觉得WebPagetest是一个很强大的免费工具,但是如果想要在实际工程项目中全面推行该工具的话,还需要解决两个问题。 + +第一个问题是,如果被测网站部署在公司内部的网络中,那么处于外网的WebPagetest就无法访问这个网站,也就无法完成测试。要解决这个问题,你需要在公司内网中搭建自己的私有WebPagetest以及相关的测试发起机。具体如何搭建,你可以参考WebPagetest官网的建议,这里我就不再继续展开了。 + +第二个问题是,用WebPagetest执行前端测试时,所有的操作都是基于界面操作的,不利于与CI/CD的流水线集成。要解决这个问题,就必须引入WebPagetest API Wrapper。 + +WebPagetest API Wrapper是一款基于Node.js,调用了WebPagetest提供的API的命令行工具。也就是说,你可以利用这个命令行工具发起基于WebPagetest的前端性能测试,这样就可以很方便地与CI/CD流水线集成了。具体的使用步骤如下: + + +通过“npm install webpagetest -g”安装该命令行工具; + +访问https://www.webpagetest.org/getkey.php获取你的WebPagetest API Key; + +使用“webpagetest test -k API-KEY 被测页面URL”发起测试,该调用是异步操作,会立即返回,并为你提供一个testId; + +使用“webpagetest status testId”查询测试是否完成; + +测试完成后,就可以通过“webpagetest results testId”查看测试报告,但是你会发现测试报告是个很大的JSON文件,可读性较差; + +通过“npm install webpagetest-mapper -g”安装webpagetest-mapper工具,这是为了解决测试报告可读性差的问题,将WebPagetest生成的JSON文件格式的测试报告转换成为HTML文件格式; + +使用“Wptmap -key API-KEY –resultIds testId –output ./test.html”将JSON文件格式的测试结果转换成HTML格式。 + + +总结 + +今天,我使用WebPagetest测试了一个具体网站的前端性能。在解读这个测试报告的同时,我和你分享了几个重要的前端性能指标,以及如何提升这些指标,最终达到优化网站用户体验的目的。 + +虽然,WebPagetest是一款免费的、功能强大的前端性能测试工具,但是用于实际测试项目时,往往还存在两个方面的问题,我给你分析了这两个问题出现的原因,以及如何解决这两个问题,以达到更高效地完成前端性能测试的目的。 + +思考题 + +除了我今天介绍的WebPagetest外,前端测试工具还有YSlow等。你还在工作中接触过哪些前端性能测试工具,它们各自有什么特点呢? + +感谢收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/32无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(上).md b/专栏/软件测试52讲/32无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(上).md new file mode 100644 index 0000000..ce1d303 --- /dev/null +++ b/专栏/软件测试52讲/32无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(上).md @@ -0,0 +1,177 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 32 无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(上) + 你好,我是茹炳晟。今天我和你分享的主题是:无实例无真相之基于LoadRunner实现企业级服务器端性能测试的实践(上)。 + +从今天开始的两篇文章,我将介绍如何基于LoadRunner实际开展企业级服务器端的性能测试。分享这个主题时,我会从最开始的性能需求获取开始讲起,带你完整地经历一个实际服务器端性能测试项目。通过这个过程,我希望可以帮助你快速建立服务器端性能测试的全局观,并了解各主要步骤的关键技术细节。 + +听到这里,你可能就有些困惑了。我在分享《工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介》这个主题时,曾经说到:LoadRunner比较适合于传统软件企业开展性能测试,而JMeter更适用于互联网企业的软件性能测试。那么,为什么我没有选择以JMeter为例来展开后端性能测试呢? + +我选择LoadRunner,是经过深思熟虑的,主要原因包括: + + +JMeter的官方文档对其使用方法介绍得很详细,而且其操作基本属于“傻瓜式”的。JMeter使用的难点在于:如何支持海量并发,以及实现更好的load控制,解决这个问题你可以参考LoadRunner的实现方式,然后从你所在企业的实际业务场景出发,进行二次开发。 + +互联网企业和传统软件企业的软件产品的后端性能测试,在原理以及基本方法上是基本一致的,区别较大的只是全链路压测。所以,我以传统企业的软件产品为例展开分享,你因此学到的原理以及测试方法将同样适用于互联网软件产品的性能测试。 + +关于互联网软件产品的全链路压测,由于需要实现海量并发以及流量隔离等操作,所以目前只有一些大型企业在做,比如饿了么、淘宝、ebay、美团等超级大的网站。但是,如果你也想了解全链路压测的话,我也会准备一篇“加餐”文章,和你分享开展全链路压测的难点,以及应对方案。我会更新完性能测试这个系列以后,为你准备这篇“加餐”文章。 + + +为了让你在进行服务器端性能测试时更充分地利用好LoadRunner,所以在正式开始讲解这个测试案例前,我会先给你简单介绍一下LoadRunner的基本原理,以及主要的功能模块。这些功能模块不仅在这个案例中会用到,也会在实际工程项目被经常使用,所以如果你有什么不理解的地方,欢迎给我留言。 + +LoadRunner的基本原理 + +你还记得我在《工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介》这个主题中,介绍过的后端性能测试工具的基本原理吗? + +我们先一起来回忆一下吧: + + +后端性能测试工具首先通过虚拟用户脚本生成器生成基于协议的虚拟用户脚本,然后根据性能测试场景设计的要求,通过压力控制器控制协调各个压力产生器以并发的方式执行虚拟用户脚本,并且在测试执行过程中,通过系统监控器收集各种性能指标以及系统资源占用率,最后通过测试结果分析器展示测试结果数据。 + + +LoadRunner的基本原理,与上面的描述完全一致。在LoadRunner中,Virtual UserGenerator对应的就是虚拟用户脚本生成器,Controller + +对应的就是压力控制器和系统监控器,Load Generator对应的就是压力产生器,Analysis对应的就是测试结果分析器。 + +为了帮助你理解LoadRunner的工作原理和模块,先撇开这些名词不谈,设想一下如果没有专用的后端性能测试工具,我们如何开展后端性能测试。 + +其实,“人肉”开展后端性能测试也不算太难。这个过程大致是这样的: + + +首先,我们需要一批测试机器,每台测试机器雇佣一个测试人员; +然后,我们需要一个协调员拿着话筒发号施令,统一控制这些测试人员的步调,协调员会向所有测试人员喊话,比如“1号到100号测试人员现在开始执行登录操作,100号到1000号测试人员5分钟后开始执行搜索操作”,同时协调员还会要求每个测试人员记录操作花费的时间; +测试完成后,测试协调员会要求性能工程师分析测试过程中记录的数据。 + + +这个过程,如图1所示。 + + + +图1 如果没有专用的后端性能测试工具,如何“人肉”开展后端性能测试 + +理解了这种“人肉”模式的后端性能测试后,我们再回过头来看LoadRunner的各个模块就豁然开朗了。 + + +测试协调员以及完成数据记录的部分就是Controller模块; +大量的测试机器以及操作这些测试机器的人就是Load Generator模块; +操作这些测试机器的人的行为就是Virtual User Generator产生的虚拟用户脚本; +对测试数据的分析就是Analysis模块。 + + +LoadRunner的主要模块 + +通过对“人肉”模式和LoadRunner工具的类比,我们可以很清楚的看到,使用LoadRunner进行性能测试,主要需要Virtual User Generator、Controller(这个模块包含了Load Generator),以及Analysis这三大模块组合使用。接下来,我再和你详细聊聊这三大模块的作用,以及需要注意的问题。 + +第一,Virtual User Generator + +Virtual User Generator,用于生成模拟用户行为的测试脚本,生成的手段主要是基于协议的录制,也就是由性能测试脚本开发人员在通过GUI执行业务操作的同时,录制客户端和服务器之间的通信协议,并最终转化为代码化的LoadRunner的虚拟用户脚本。 + +这样转化得到的虚拟脚本往往并不能被直接使用,还需要经历数据参数化(Parameterization)、关联建立(Correlation),以及运行时设置(Run Time Settings)等操作,然后才能用于性能测试场景中。 + +具体什么是数据参数化、什么是关联建立、运行时设置都有哪些可选项,我会在分享实例时再详细展开。 + +第二,LoadRunner Controller + +Controller相当于性能测试执行的控制管理中心,负责控制Load Generator产生测试负载,以执行预先设定好的性能测试场景;同时,它还负责收集各类监控数据。 + +在实际执行性能测试时,Controller是和性能工程师打交道最多的模块,性能工程师会在Controller的UI界面上完成性能测试场景的设计、运行时的实时监控、测试负载的开始与结束等操作。 + +第三,LoadRunner Analysis + +Analysis是LoadRunner中一个强大的分析插件。它不仅能图形化展示测试过程中收集的数据,还能很方便地对多个指标做关联分析,找出它们之间的因果关系。它最根本的目的就是,分析出系统可能的性能瓶颈点以及潜在的性能问题。 + +现在,你已经了解了LoadRunner的原理和各个模块了,接下来我们就开始实战吧。通过这个实战,我希望你可以掌握如何基于LoadRunner进行企业级的性能测试。 + +从宏观角度来讲,基于LoadRunner完成企业级性能测试,可以划分为五个阶段: + + +性能需求收集以及负载计划制定; + +录制并增强虚拟用户脚本; + +创建并定义性能测试场景; + +执行性能测试场景; + +分析测试报告。 + + +图2清晰地描述了这5个阶段的先后顺序,以及需要LoadRunner各模块发挥作用的部分。接下来,我和你详细聊聊每个阶段的具体工作,以及关键的技术细节。 + + + +图2 使用LoadRunner完成企业级后端性能测试的典型流程与步骤 + +阶段1:性能需求收集以及负载计划制定 + +其实,无论是进行什么类型的测试,你的第一步工作都是要根据测试目的明确测试的具体需求。企业级的后端性能测试,当然也不例外。 + +一般情况下,企业级后端性能测试的具体需求,主要包含以下内容: + + +系统整体的并发用户数。比如,高峰时段会有10万用户同时在线; +并发用户业务操作的分布情况。比如,20%的用户在做登录操作,30%的用户在做订单操作,其他50%的用户在做搜索操作; +单一业务操作的用户行为模式。比如,两个操作之间的典型停留时间,完成同一业务的不同操作路径等; +并发用户高峰期的时间分布规律。比如,早上8点会有大量用户登录系统,晚上6点后用户逐渐退出; +达到最高峰负载的时间长度。比如,并发用户从0增长到10万花费的总时间; +… + + +完成这些点的测试,其实并不复杂。你只要按照这个已经明确的需求,开发后续的测试脚本、设计性能测试场景就可以了。 + +但是,如果你想要成长为更资深的性能测试工程师,或者已经是性能测试的设计者、资深的性能测试工程师了,那么你就需要全程参与到这些需求的获取和确定中。 + +其实,在我看来,获取这些测试需求时性能测试中最难的两个工作之一。另一个最难的工作是,测试结果分析与性能问题定位。而其他类似性能测试脚本开发、场景设计等工作看起来很有技术含量,但实际都是一些相对机械性的重复工作。 + +那为什么获取测试需求难做呢?因为绝大多数情况下没人会明确告诉你具体的性能需求。 + +对于软件的功能测试来说,如果需求不明确,你可以直接求助于产品经理。 + +而对性能测试需求来讲,产品经理通常无法准确告诉你用户的各个业务操作所占的百分比,也无法告诉你准确的用户行为模式。产品经理能做的,往往是给出定性描述,然后需要你去计算或者根据过往经验得到具体的定量需求。所以,我们经常会听到产品经理对性能测试人员说:“你是性能专家,你来告诉我性能需求”。 + +那么,对于性能测试设计人员来说,到底如何获得这个明确的性能需求呢?说到这里,你应该明白了这是一个非常复杂的话题,因为测试目的不同,所用的方法也各不相同。所以,在这次分享中,我也只是可以给你准备一个实际的测试案例,和你分享获取具体测试需求的思考方式。 + +还记得我在第29篇文章《聊聊性能测试的基本方法与应用领域》中介绍的医院体检的例子吗?假设,产品经理对医院体检的性能要求是“每天支持完成8000个体检”,这个需求看似很具体,但是要转化成实际可操作的性能测试需求,还需要再细化。 + +首先,你要明确这里的“每天”是否指的是24小时。显然,这取决于产品本身的属性。比如,产品是为单一时区的用户提供服务,还是要面向全球所有时区的用户。那么,根据体检中心的属性,你很容易就可以确定“每天”一定是指8小时的工作时间。因为,体检中心一定是在一个确定的时区,并且不会24小时营业。 + +然后,你明确了这个8小时后,那么原始需求是不是可以转化为“每小时支持完成1000个体检”? + +如果按照这个套路设计后续的性能测试的话,你会发现即使测试顺利完成,并且各项性能指标都达标了,但是一旦上线后,系统还是很有可能被压垮。因为实际情况是,验血往往需要空腹,所以上午往往是体检中心的高峰时段,体检者会在上午集中涌入体检中心。也就是说,这8000个体检并不是平均分布在8小时内完成的,而是有明显的高峰时段。 + +最后,你可以采用80/20原则对高峰时段的用户负载进行建模,比如80%的体检(6400个)是发生在上午20%的时间(96分钟)里。当然,为了使模型更接近真实的情况,你还应该分析历史数据,然后对该模型做进一步的修正,这也是目前被普遍采用的方法。 + +另外,在得到了负载模型后,性能测试设计人员往往还会在此基础上加入一定的负载冗余,比如在峰值的基础上再额外放大20%,以增强系统上线后稳定运行的信心。 + +通过上面这个分析过程,你可以认识到,性能测试需求的定义与计划非常复杂,牵涉到项目的方方面面,不可能通过阅读一两篇文章就快速掌握这一技能,需要不断地沉淀在实战中获得的经验。 + +制订了性能测试计划后,接下来你就需要根据性能计划中涉及的用户业务操作来开发性能测试的脚本了。比如,前面提到“20%的用户在做登录操作,30%的用户在做订单操作,剩下50%的用户在做搜索操作”,接下来你需要分别开发“用户登录”“下订单”和“搜索”这三个虚拟用户脚本。 + +在LoadRunner中,开发虚拟用户脚本的工作主要是基于录制后再修改的方式完成的。其中,录制由Virtual UserGenerator基于协议完成,录制后的修改主要是实现参数化、建立关联、建立事务、加入必要的检查点以及加入思考时间。 + +所以,我会在下次分享时,和你详细讨论这四部分工作的作用,以及具体如何完成。同时,我还会和你分享,企业级服务器端性能测试的后四个阶段如何实现。 + +总结 + +今天我和你讨论的主题是,如何基于LoadRunner实现企业级服务器端性能测试。 + +首先,我用一个“人肉”测试的流程类比,为你介绍了LoadRunner这个工具的基本原理,并分析了Virtual User Generator、Controller(内含Load Generator模块),以及Analysis这3个模块的功能。 + +然后,从宏观角度,我把整个性能测试过程划分成了五个阶段:性能需求收集以及负载计划制定、录制并增强虚拟用户脚本、创建并定义性能测试场景、执行性能测试场景,以及分析测试报告。 + +在我看来,这五个阶段最难的两部分工作分别是:明确具体的性能测试需求,以及测试结果分析与性能问题定位。因为这两部分工作,要大量依赖于测试工程师的能力以及经验积累。所以,就像一名优秀的医生一样,优秀的测试工程师,需要在实际的工程项目中不断积累和总结经验。 + +最后,我以前面文章提到过的体检中心为例,和你详细讨论了如何收集性能需求,以及制定负载计划的内容。我也解释了为什么性能测试的需求不能直接从产品经理那里获得:因为产品经理定义的性能需求层次比较高、比较抽象,要落实到实际可执行的性能测试需求往往需要分析和细化。这也是为什么获取具体的性能需求比较难的一个原因。 + +思考题 + +你在实际工作中,是如何获取并细化性能测试需求的呢? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/33无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(下).md b/专栏/软件测试52讲/33无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(下).md new file mode 100644 index 0000000..37613d5 --- /dev/null +++ b/专栏/软件测试52讲/33无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(下).md @@ -0,0 +1,266 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 33 无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(下) + 你好,我是茹炳晟。今天我和你分享的主题是:无实例无真相之基于LoadRunner实现企业级服务器端性能测试的实践(下)。 + +今天,我会继续和你分享如何基于LoadRunner完成企业级服务器端的性能测试。通过我上一次的分享,你已经清楚知道了,整个性能测试过程可以分为五个阶段,并且解决了整个测试过程中最难的一部分工作,即如何获取具体的性能测试需求。 + +现在,我们先来回顾一下,性能测试包含的五个阶段:性能需求收集以及负载计划制定、录制并增强虚拟用户脚本、创建并定义性能测试场景、执行性能测试场景,以及分析测试报告。所以,今天,我们就要解决剩下的4个阶段的问题了。 + +阶段2:录制并增强虚拟用户脚本 + +我已经在上篇文章中和你提到,完成了性能测试需求分析后,你就已经明确了要开发哪些性能测试脚本。现在,我们就一起来看看开发性能测试脚本的步骤,以及相关的技术细节。 + +从整体角度来看,用LoadRunner开发虚拟用户脚本主要包括以下四个步骤: + + +识别被测应用使用的协议; + +录制脚本; + +完善录制得到的脚本; + +验证脚本的正确性。 + + +这里需要注意的是,完善录制得到的脚本这一步,会包含大量的技术细节,也有很多对你来说可能是新概念的名词,所以我会着重讲解这一步,帮你克服性能测试道路上的这些“拦路虎”。 + +步骤1:识别被测应用使用的协议 + +如果你已经和系统设计、开发人员沟通过,明确知道了被测系统所采用的协议,那么你可以跳过这一步。如果还不知道具体使用的哪种协议的话,你可以使用Virtual User Generator模块自带的Protocol Advisor识别被测应用使用的协议,具体的操作方法也很简单: + + +在Virtual User Generator中依次点击File、Protocol、AdvisorAnalyze、Application,展开这些菜单。 + +在打开的界面上按要求填写被测应用的信息。 + +Protocol Advisor会自动运行被测系统。如果是网页应用,就会打开浏览器。 + +在页面上执行一些典型的业务操作,完成这些业务操作后点击”Stop Analyzing”按钮停止录制。 + +Protocol Advisor会根据刚才录制的内容自动分析被测应用使用的协议,并给出最终的建议。 + + +接下来,你就可以使用Protocol Advisor建议的录制协议开始脚本录制工作了。如图1所示就是Protocol Advisor给出的建议录制协议界面。 + + + +图1 Protocol Advisor给出的建议录制协议界面 + +步骤2:录制脚本 + +脚本录制的基本原理是,通过GUI界面对被测系统进行业务操作,Virtual User Generator模块在后台捕获GUI操作所触发的客户端与服务器端的所有交互,并生产基于C语言的虚拟用户脚本文件。 + +也就是说,录制脚本的过程需要通过GUI实际执行业务操作,所以我建议你在开始录制脚本前,先多次演练需要这些GUI操作步骤,并明确知道哪些操作步骤会对服务器端发起请求。 + +我们要知道哪些操作步骤会对服务器发起请求的原因是,要将这些操作步骤在虚拟用户脚本中封装成“事务”(Transaction)。封装为“事务”的目的是统计响应时间,因为LoadRunner中的响应时间都是以“事务”为单位的。 + +具体的录制步骤,主要包括如下三步, + + +首先,选择Create/Edit Scripts进入Virtual User Generator创建脚本的协议选择界面。 + +选择正确的协议后进入Start Recording界面,选择需要录制的应用类型,并填写应用的详细信息。如果是Web应用,Application type就应该选择Internet Application,然后选择浏览器并填写这个Web应用的URL,完成后自动打开浏览器。 + +在该浏览器中执行业务操作,Virtual User Generator模块会记录所有的业务操作,并生成脚本。 + + +在录制脚本的过程中,我强烈建议直接对发起后端调用的操作添加事务定义,而不要等到脚本生成后再添加。因为LoadRunner脚本的可读性并不好,在录制完的脚本中添加事务定义的难度会很大。 + +在录制过程中,直接添加事务操作也很简单,主要包括如下三步: + + +在开始执行GUI操作前,先点击图2中的“事务开始”按钮并填写事务名称; + +执行GUI操作; + +操作完成后,点击图2中的“事务结束”按钮。 + + +这样你刚才执行GUI操作的脚本就会被lr_start_transaction(“事务名称”)和lr_end_transaction(“事务名称”,LR_AUTO)包围起来,也就完成了添加事务的定义。 + + + +图2 Virtual User Generator的脚本录制控制条 + +步骤3:完善录制得到的脚本 + +脚本录制,只是虚拟用户脚本开发中最简单的一步。我在上一次分享《无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(上)》时,提到由Virtual User Generator模块录制的脚本不能直接使用,我们还需要对录制的脚本做以下处理: + + +在两个事务之间加入思考时间(Think Time); +对界面输入的数据做参数化(Parameterization)操作; +完成脚本的关联(Correlation)操作; +加入检查点(Check Point)。 + + +这4步处理操作是虚拟用户脚本开发中最关键的地方,你不仅需要知道为什么要进行这些处理,更要能够完成这些处理,否则你录制的脚本无法成功回放。 + +第一,在两个事务之间加入思考时间 + +什么是思考时间呢? + +用户在实际使用系统时,并不会连续不断地向后端服务器发起请求,在两次发起请求之间往往会有一个时间的间隔,这个时间间隔主要来自于两个方面: + + +一是,用户操作的人为等待时间,因为用户不可能像机器人那样快速地执行操作; +二是,用户可能需要先在页面上填写很多信息后之后,才能提交操作,那么填写这些信息就需要花费一定的时间。 + + +所以,为了让虚拟用户脚本能够更真实地模拟实际用户的行为,我们就需要在两个事务之间加入一定的等待时间。这个等待时间,就是LoadRunner中的思考时间。 + +你只要直接调用LoadRunner提供的lr_think_time()函数,就可以在两个事务之间加入思考时间。但是,这个思考时间到底设置为多少,并没有那么容易知道。思考时间往往会涉及多方面的因素,严格计算的话会非常复杂。 + +所以,在实际项目中,一般先粗略估计一个值(比如15 s),然后在实际执行负载场景的过程中,再根据系统吞吐量调整。 + +你在后续调整思考时间时,无需逐行修改虚拟用户脚本代码,可以在Run-time Settings(运行时设置)中很方便地完成。如图3所示,Run-time Settings中支持多种方式调整思考时间。 + + + +图3 通过Run-time Settings统一调整思考时间 + + +As recorded,代表的是直接使用lr_think_time()函数中指定的时间。 +Mutiply recorded think time by,代表的是在lr_think_time()函数中指定的时间基础上乘以一个数字。比如这个数字是2,那么所有的思考时间都会翻倍。 +Use random percentage of recorded think time,指的是使用指定思考时间范围内的随机值。例如,如果lr_think_time()函数中指定的时间是2 s,并且指定最小值为50%,最大值为200%,则实际的思考时间会取最小值1 s(2 s_50%)和最大值4 s(2 s_200%)之间的随机值。 +Limit think time to,指的是为思考时间设置一个上限值,只要lr_think_time()函数中指定的时间没有超过这个上限值,就按照lr_think_time()函数指定的值,如果超过了就取这个上限值作为思考时间。 + + +第二,对界面输入的数据做参数化操作 + +数据的参数化,其实很好理解,我再给你举个例子,你马上就能明白。 + +假设,你录制的虚拟用户脚本完成的是用户登录操作,那么由于脚本回放时需要支持多用户的并发,所以必须要把脚本中的用户名和密码独立出来,放入专门的数据文件中,然后在这个文件中提供所有可能被用到的用户名和密码。 + +有没有感觉这个概念很熟悉,它其实和我以前介绍到的[数据驱动的自动化测试]完全相同。 + +图4给出了参数化配置的界面截图,LoadRunner支持的参数化的数据源很丰富,既可以是excel文件,也可以是数据库中的表等。 + + + +图4 虚拟用户脚本参数化配置的界面截图 + +这里需要特别说明的是,凡是参数文件中使用的测试数据都需要在执行性能测试前,在被测系统中事先准备好。比如,还是以用户登录的脚本为例,假定你的参数文件中提供了5000个用于并发执行的用户信息,那么这5000个用户必须是已经实际存在于系统中的,这就要求你要在开始测试前事先准备好这5000个用户。 + +所以,参数化操作其实由两部分组成: + + +性能测试脚本和测试数据的分离; + +事先建立性能测试的数据。 + + +也就是说,参数化的过程往往与性能测试数据准备密不可分。 + +第三,完成脚本的关联操作 + +关联操作,是LoadRunner虚拟用户脚本开发过程中最关键的部分,直接关系到脚本是否可以回放成功。 + +从概念上讲,关联的主要作用是,取出前序调用返回结果中的某些动态值,传递给后续的调用。是不是听起来很拗口,不太好理解?我们来看一个具体的例子吧。 + +假设,每次客户端连接服务器端时,服务器端都会用当前的时间戳(Time Stamp)计算CheckSum,然后将Time Stamp和CheckSum返回给客户端。然后,客户端就把Time Stamp + CheckSum的组合作为唯一标识客户端的Session ID。录制脚本时,录制得到的一定是硬编码(hardcode)的Time Stamp值和CheckSum值。 + +图5展示了这个交互过程,录制得到Time Stamp的值是TS,而CheckSum的值是CS。 + + + +图5 关联原理图-脚本录制过程 + +采用Time Stamp + CheckSum的组合作为Session ID的方式,在我们回放这个脚本的时候就有问题了。因为回放时,这段硬编码已经有了新的Time Stamp值和CheckSum值,并且显然与之前的值不同,所以服务器无法完成Session ID的验证,也就导致了脚本回放失败。 + + + +图6 关联原理图-脚本回放过程 + +其实,这种情况几乎存在于所有的虚拟用户脚本中,所以我们必须要解决这个问题。 + +解决方法就是,在脚本回放的过程中,实时抓取Time Stamp值和CheckSum值,然后用实时抓取到的值替换后续需要使用这两个值的地方。这个过程就是“关联”。 + +如图7所示,关联就是解析服务器端的返回结果,抓取新的Time Stamp值和CheckSum值,然后后续的操作都使用新抓取的值,这样脚本就能回放成功了。 + + + +图7 关联原理图-使用“关联”后的脚本回放过程 + +理解了关联操作,在脚本中处理关联就比较简单了,LoadRunner提供了功能强大的关联函数web_reg_save_param()。这个关联函数支持多种动态值的获取方式,用得最多的是基于“前序字符串匹配”加上“后续字符串匹配”的方式。其中,字符串匹配,支持正则表达式。 + +我们一起来看个具体的例子吧。 + +假设,服务器端返回的结果是“LB=name=timestamp value=8888.LB=name=CheckSum”,那么为了能够获取到“8888”这个动态值,我们就可以用“前序字符串=LB=name=timestamp value=”和“后续字符=.LB=name=CheckSum”来“框出” 8888”这个动态值。 + +另外,需要特别注意的是web_reg_save_param()函数是注册型函数,必须放在获取动态值所属的请求前面,相当于先声明,后调用。 + +更多的关联函数用法,你可以参考LoadRunner官方文档。 + +第四,加入检查点 + +检查点,类似于功能测试中的断言。但是,性能测试脚本,不像功能测试脚本那样需要加入很多的断言,往往只在一些关键步骤后加入很少量的检查点即可。这些检查点的主要作用是,保证脚本按照原本设计的路径执行。 + +最常用的检查点函数是web_reg_find(),它的作用是通过指定左右边界的方式“在页面中查找相应的内容”。这里需要注意的是,这个函数也是注册型函数,即需要放在所检查的页面之前,否则会检查失败。更多的检查点函数以及用法也请参考LoadRunner官方文档。 + +步骤4:验证脚本的正确性 + +完成了脚本开发后,根据我的个人经验,我强烈建议你按照以下顺序检查脚本的准确性: + + +以单用户的方式,在有思考时间的情况下执行脚本,确保脚本能够顺利执行,并且验证脚本行为以及执行结果是否正确; + +以单用户的方式,在思考时间为零的情况下执行脚本,确保脚本能够顺利执行,并且验证脚本行为以及执行结果是否正确; + +以并发用户的方式,在有思考时间的情况下执行脚本,确保脚本能够顺利执行,并且验证脚本行为以及执行结果是否正确; + +以并发用户的方式,在思考时间为零的情况下执行脚本,确保脚本能够顺利执行,并且验证脚本行为以及执行结果是否正确。 + + +只有上述四个测试全部通过,虚拟用户脚本才算顺利完成。 + +至此,我们完成了第二个阶段的“录制并增强虚拟用户脚本”的工作,顺利拿到了虚拟用户脚本。那么接下来,我们就会进入第三个阶段,使用开发完成的虚拟用户脚本创建并定义性能测试场景。 + +阶段3:创建并定义性能测试场景 + +还记得我在分享《工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介》这个主题时,介绍过的性能测试场景的内容吗?如果有点忘记了,我建议你先回顾一下这篇文章的内容。 + +这个阶段的工作,就是在LoadRunner Controller中设置性能测试场景。由于整个设置过程,都是基于Controller的图形用户界面的操作,本身没什么难度,所以我就不再详细展开了,如果有这方面的问题,你也可以自行百度或者给我留言。 + +阶段4:执行性能测试场景 + +完成了性能测试场景的设计与定义后,执行性能测试场景就非常简单了。 + +这个过程一般是在LoadRunner Controller中完成。你可以通过Controller发起测试、停止测试、调整性能测试场景的各种参数,还可以监控测试的执行过程。 + +阶段5:分析测试报告 + +执行完性能测试后,LoadRunner会根据自己的标准并结合性能测试场景中定义的系统监控器指标,生成完整的测试报告。在Analysis中,不仅可以以图形化的方式显示单个指标,也可以将多个指标关联在一起进行比较分析。 + +图8展示了使用LoadRunner Analysis展示事务平均响应时间的界面,我们可以看到图片右下角各个事务的最小响应时间、最大响应时间和平均响应时间。 + + + +图8 性能测试报告的分析 + +性能测试报告的分析,是一项技术含量非常高的工作。优秀的性能测试工程,通过报告中的数值以及数值之间的相互关系,就能判断出系统中可能存在的问题。这就好比医生看验血报告,经验丰富的医生可以根据验血报告对病情做出八九不离十的判断。 + +性能测试报告的解读,需要丰富的系统架构、性能理论以及大量实战经验的积累。这个话题已经超出了我今天要分享的范围,所以我也就不再继续展开了。 + +总结 + +今天接着上一篇文章,我和你分享了企业级后端性能测试的后四个阶段的内容,包括录制并增强虚拟用户脚本、创建并定义性能测试场景、执行性能测试场景,以及分析测试报告。现在,我再为你总结一下每一个阶段的重点内容。 + +录制并增强虚拟用户脚本,这个阶段的工作又可以分为识别被测应用使用的协议、录制脚本、完善录制得到的脚本、验证脚本的正确性四步。其中,完善录制得到的脚本这一步,涉及到了很多概念和基础知识,所以我进行了重点讲解,希望帮你克服性能测试的难点。 + +创建并定义性能测试场景,以及执行性能测试场景,这两个阶段的工作都是在LoadRunner的Controller模块中完成的,也都比较简单。你可以参考我在《工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介》这篇文章分享的内容,完成这两个阶段的工作。 + +分析测试报告,这个工作的技术含量非常高。深入解读性能测试报告的能力,需要丰富的系统架构、性能理论,以及大量实战经验。所以,我们需要在平时工作中,不断地丰富自己的知识体系。 + +思考题 + +你们公司的性能测试是否使用LoadRunner,在使用过程中遇到了什么难题?你们又是如何解决的呢? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/34站在巨人的肩膀:企业级实际性能测试案例与经验分享.md b/专栏/软件测试52讲/34站在巨人的肩膀:企业级实际性能测试案例与经验分享.md new file mode 100644 index 0000000..b91931b --- /dev/null +++ b/专栏/软件测试52讲/34站在巨人的肩膀:企业级实际性能测试案例与经验分享.md @@ -0,0 +1,152 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 34 站在巨人的肩膀:企业级实际性能测试案例与经验分享 + 你好,我是茹炳晟,今天我分享的主题是:站在巨人的肩膀之企业级实际性能测试案例与经验分享”。 + +在前面的四篇文章中,我介绍了前端性能测试和后端性能测试的理论与方法,还分享了使用LoadRunner实现后端性能测试的过程。有了这些内容的铺垫,我今天会和你聊聊传统的企业级软件企业如何开展性能测试工作。 + +其实,传统的企业级软件产品和互联网产品的性能测试,在原理和测试方法上基本一致,它们最大的区别体现在并发数量的数量级上,以及互联网软件产品的性能测试还需要直接在生产环境下进行特有的全链路压测。而全链路压测其实是在传统的企业级软件产品的性能测试基础上,又进行了一些扩展。 + +所以,在我看来,只要掌握了传统的企业级软件产品的性能测试的原理和方法,搞定互联网产品的性能测试自然不在话下。 + +言归正传,传统企业级软件产品的性能测试重点是在服务器端。为了达到不同的测试目标,往往会有多种不同类型的性能测试。今天,我就和你聊聊这其中都有哪些测试类型,以及每类测试的目的、所采用的方法。 + +所以,今天的分享,我会从以下四种测试类型的角度展开: + + +性能基准测试; + +稳定性测试; + +并发测试; + +容量规划测试。 + + +性能基准测试 + +性能基准测试,通常被称为Performance Benchmark Test,是每次对外发布产品版本前必须要完成的测试类型。 + +性能基准测试,会基于固定的硬件环境和部署架构(比如专用的服务器、固定的专用网络环境、固定大小的集群规模、相同的系统配置、相同的数据库背景数据等),通过执行固定的性能测试场景得到系统的性能测试报告,然后与上一版本发布时的指标进行对比,如果发现指标有“恶化”的趋势,就需要进一步排查。 + +典型的“恶化”趋势,主要表现在以下几个方面: + + +同一事务的响应时间变慢了。比如,上一版本中,用户登录的响应时间是2 s,但是在最新的被测版本中这个响应时间变成了4 s; +系统资源的占用率变高了。比如,上一版本中,平均CPU占用率是15%,但是在最新的被测版本中平均CPU占用率变成了30%; +网络带宽的使用量变高了。比如,上一版本中,发送总字节数是20 MB,接收总字节数是200 MB,但是在最新的被测版本中发送总字节数变成了25 MB,接收总字节数变成了250 MB。 + + +这里需要注意的是,这些“恶化”趋势的前提是:完全相同的环境以及测试负载。不同“恶化”指标的排查,有不同的方法。我以最常见的事务响应时间变慢为例,和你说明一下排查方法。 + +假设,通过性能基准测试的比较结果得知,用户登录的响应时间从2 s变成了4 s。 + +那么,我们首先要做的是验证在单用户的情况下,是否会出现响应时间变长的问题。具体做法是,将用户登录的虚拟用户脚本单独拿出来,建立一个单用户运行的性能测试场景并执行,观察用户登录的响应时间是否变慢。 + +如果变慢了,就说明这是单用户登录场景就可重现的性能问题,后续的处理也相对简单了。解决方法是:分析单用户登录的后端日志文件,看看完成登录操作的时间具体都花在了哪些步骤上,相比之前哪些步骤花费的时间变长了,或者是多出了哪些额外的步骤。 + +如果没有变慢,则说明我们必须尝试在有压力的情况下重现这个性能问题。为此,我们要基于用户登录的虚拟用户脚本构建并发测试的场景,但是我们并不清楚在这个场景设计中到底应该采用多少并发用户、加入多长的思考时间。这时,通常的做法是,直接采用性能基准测试中的并发用户数和思考时间,去尝试重现问题。如果无法重现,我们可以适当地逐步加大测试负载,并观察响应时间的变化趋势。 + +这里需要注意的是,千万不要使用过大的测试负载。因为测试负载过大的话,系统资源也会成为性能瓶颈,一定会使响应时间变长。但这时,响应时间变长主要是由资源瓶颈造成的,而不是你开始要找的那个原因。 + +如果此时可以重现问题,那就可以进一步去分析并发场景下,用户登录操作的时间切片,找到具体的原因。如果此时还是不能重现问题的话,情况就比较复杂了,也就是登录操作的性能可能和其他的业务操作存在依赖,或者某种资源竞争关系,这就要具体问题具体分析了。 + +一般来说,当定位到性能“恶化”的原因并修复后,我们还会再执行一轮性能基准测试,以确保系统对外发布前的性能基准测试指标没有“变坏”。可以说,通过对每个预发布版本的性能基准测试,我们可以保证新发布系统的整体性能不会下降,这也就是性能基准测试最终要达到的目的。 + +很多大型的传统软件公司都有专门的性能测试团队,这个团队会建立标准的性能基准测试场景,并把性能基准测试的结果作为产品是否可以发布的依据之一。比如,我曾工作过的HP软件,就由性能测试卓越中心负责维护、执行性能基准测试,并分析测试结果。 + +从性能基准测试的设计角度来看,你需要特别注意以下三点: + + +性能基准测试中虚拟用户脚本的选择以及配比,需要尽可能地匹配实际的负载情况; + +总体的负载设计不宜过高,通常被测系统的各类占用率指标需要控制在30%以内,尽量避免由于资源瓶颈引入的操作延时; + +每次性能基准测试前,一般需要对系统资源以及网络资源做一轮快速的基准测试,以保证每次被测环境的一致性,同时也要保证数据库的数据量在同一个级别上。总之,你需要采用一切可能的手段,来确保多次性能基准测试之间的环境一致性。 + + +稳定性测试 + +稳定性测试,又称可靠性测试,主要是通过长时间(7*24小时)模拟被测系统的测试负载,来观察系统在长期运行过程中是否有潜在的问题。通过对系统指标的监控,稳定性测试可以发现诸如内存泄漏、资源非法占用等问题。 + +很多企业级的服务器端产品,在发布前往往都要进行稳定性测试。稳定性测试,通常直接采用性能基准测试中的虚拟用户脚本,但是性能测试场景的设计和性能基准测试场景会有很大不同: + + +一般是采用“波浪式”的测试负载,比如先逐渐加大测试负载,在高负载情况下持续10多个小时,然后再逐渐降低负载,这样就构成了一个“波浪”,整个稳定性测试将由很多个这样的波浪连续组成。 + + +稳定性测试成功完成的标志,主要有以下三项: + + +系统资源的所有监控指标不存在“不可逆转”的上升趋势; +事务的响应时间不存在逐渐变慢的趋势; +事务的错误率不超过1%。 + + +实际工程项目中,由于稳定性测试执行的时间成本很高,往往需要花费3~7天的时间,所以我们一般是在其他所有测试都已经完成,并且所有问题都已经修复之后才开始稳定性测试。 + +另外,有些企业为了缩短稳定性测试的执行时间,往往还会采用“时间轴压缩”的方法,具体的做法就是:在加大测试负载的前提下,适当缩短每个“波浪”的时间,从而减少整体的测试执行时间。 + +最后,需要强调的一点是,虽然很多时候,尤其是产品版本已经逐渐走向成熟期时,稳定性测试并不会发现问题,但是千万不要小看稳定性测试带来的价值。因为稳定性测试一旦发现问题,那么这些问题都是很严重而且非常隐蔽的大问题。 + +所以,很多大型的企业级软件企业都会执行严格的稳定性测试,并把稳定性测试的结果作为产品是否可以发布的硬性要求。比如,我曾经工作过的HP软件研发中心,它每次产品发布前都会由专门的性能测试团队完成严格的稳定性测试,并以此来决定是否要发布这个产品。 + +并发测试 + +并发测试,是在高并发情况下验证单一业务功能的正确性以及性能的测试手段。高并发测试一般使用思考时间为零的虚拟用户脚本来发起具有“集合点”的测试。 + +“集合点”的概念,我已经在《聊聊性能测试的基本方法与应用领域》中解释过了。如果你不清楚的话,可以再回顾一下这篇文章。如果你还有不理解的地方,也欢迎和我留言讨论。 + +并发测试,往往被当作功能测试的补充,主要用于发现诸如多线程、资源竞争、资源死锁之类的错误。要执行并发测试,就需要加入“集合点”,所以往往需要修改虚拟用户脚本。 + +加入“集合点”一般有两种做法: + + +在虚拟用户脚本的录制过程中直接添加; + +在虚拟用户脚本中,通过加入lr_rendezvous()函数添加。 + + +容量规划测试 + +容量规划测试,是为了完成容量规划而设计执行的测试。 + +那什么是容量规划呢?所谓容量规划,是软件产品为满足用户目标负载而调整自身生产能力的过程。 + +所以,容量规划的主要目的是,解决当系统负载将要达到极限处理能力时,我们应该如何通过垂直扩展(增加单机的硬件资源)和水平扩展(增加集群中的机器数量)增加系统整体的负载处理能力的问题。 + +目前来讲,容量规划的主要方法是基于水平扩展。但是,具体应该增加多少机器,以及增加后系统的负载处理能力是否会线性增长,这些问题都需要通过容量规划测试进行验证。 + +那么,容量规划测试具体要怎么做呢? + +我们可以使用性能基准测试中的虚拟用户脚本,以及各个业务操作脚本的百分比,压测单机部署的被测系统。我们会采用人工的方式不断增加测试负载直到单机系统的吞吐量指标到达临界值,由此就可以知道单台机器的处理能力。 + +理论上讲,整个集群的处理能力将等于单台机器的处理能力乘以集群的机器数,但是实际情况并不是这样。实际的集群整体处理能力一定小于这个值,但具体小多少就是要靠实际的测试验证了。 + +理想的状态是,集群整体的处理能力能够随着集群机器数量的增长呈线性增长。但是,随着机器数量的不断增长,总会在达到某个临界值之后,集群的整体处理能力不再继续呈线性增长。这个临界值是多少,我们也需要通过容量规划测试找出来了。 + +另外,容量规划测试的测试结果还可以被用作系统容量设计的依据。比如,企业级软件产品的目标用户规模通常是可以预估的,那么我们就可以通过这些预估的系统负载计算出软件部署的集群规模,并且可以在具体实施后通过容量测试的方式进行验证。 + +总结 + +在前面的两篇文章中,我和你分享了如何基于LoadRunner开展性能测试,但是并没有具体去讲解要开展哪些类型的性能测试。所以,今天我就挑选了最重要的四类性能测试方法,和你分享如何在实际项目中完成这些测试,确保软件的性能。 + + +性能基准测试,可以保证新发布系统的整体性能不会下降; +稳定性测试,主要通过长时间模拟被测系统的测试负载,观察系统在长期运行过程是否存在问题; +并发测试,往往被当作功能测试的补充去发现多线程、资源竞争、资源死锁之类的问题。 +容量规划测试,主要用于确定给定负载下的系统集群规模,其测试结果可以被用作系统容量设计的依据。 + + +思考题 + +你所在企业,还会采用哪些性能测试方法,又是如何展开具体的测试工作的呢? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/35如何准备测试数据?.md b/专栏/软件测试52讲/35如何准备测试数据?.md new file mode 100644 index 0000000..74a89f2 --- /dev/null +++ b/专栏/软件测试52讲/35如何准备测试数据?.md @@ -0,0 +1,159 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 35 如何准备测试数据? + 你好,我是茹炳晟,今天我和你分享的主题是:如何准备测试数据。 + +从今天开始,我们将一起进入测试数据准备这个新的系列了。我会用四篇文章,和你详细探讨软件测试过程中关于测试数据准备的话题。我会依次分享测试数据创建的基本方法、测试数据准备的痛点、自行开发的测试数据工具,以及目前业内最先进的统一测试数据平台。 + +你我都非常清楚,测试数据的准备是软件测试过程中非常重要的一个环节,无论是手工测试,还是自动化测试,无论是GUI测试,还是API测试,无论是功能测试,还是性能测试,都避不开测试数据准备的工作。 + +所以,如果你想要成长为一名优秀的测试工程师,那就非常有必要深入理解测试数据准备的方法,以及它们各自的优缺点、适用场景了。 + +今天,我们就先从测试数据准备的基本方法开始吧。 + +从创建测试数据的维度来看,测试数据准备方法主要可以分为四类: + + +基于GUI操作生成测试数据; +通过API调用生成测试数据; +通过数据库操作生成测试数据; +综合运用API和数据库的方式生成测试数据。 + + +这时,相信你已经回想起我曾在第15篇文章《过不了的坎:聊聊GUI自动化过程中的测试数据》]中从创建测试数据的维度和你分享过这些内容,这次的分享只不过是多了“通过GUI调用生成测试数据”的方法。 + +其实,我在第15篇文章的分享内容,只是简单的介绍了GUI测试数据准备的方法,并没有详细展开。事后,你可能也感觉不太过瘾,想知道一些更深入、更细节、更贴近业务场景的测试数据准备的知识。所以,也就有了我今天的这次分享。 + +那么,接下来我们就赶紧开始吧,一起聊聊这四种测试数据准备的方法。 + +基于GUI操作生成测试数据 + +基于GUI操作生成测试数据,是最原始的创建测试数据的方法。简单地说,它就是采用E2E的方式来执行业务场景,然后生成数据的方法。 + +比如,你想要测试用户登录功能,那么首先就要准备一个已经注册的用户,为此你可以直接通过GUI界面来注册一个新用户,然后用这个新创建的用户完成用户登录功能的测试。 + +这个方法的优点是简单直接,在技术上没有任何复杂性,而且所创建的数据完全来自于真实的业务流程,可以最大程度保证数据的正确性。但是,该方法的缺点也十分明显,主要体现在以下这四个方面: + + +创建测试数据的效率非常低。一是因为每次执行GUI业务操作都只能创建一条数据,二是因为基于GUI操作的执行过程比较耗时。 + +基于GUI的测试数据创建方法不适合封装成测试数据工具。由于测试数据的创建是通过GUI操作实现的,所以把这种数据创建方法封装成测试数据准备工具的过程,其实就是在开发GUI自动化测试用例。无论是从开发工作量,还是从执行效率来讲,把基于GUI操作的测试数据创建方法封装成测试数据准备工具都不是最佳的选择。 + +测试数据成功创建的概率不会太高。因为,测试数据准备的成功率受限于GUI自动化执行的稳定性,而且任何界面的变更都有可能引发测试数据创建的失败。 + +会引入不必要的测试依赖。比如,你的被测对象是用户登录功能,通过GUI页面操作准备这个已经注册的用户,就首先要保证用户注册功能没有问题,而这显然是不合理的。 + + +鉴于以上四方面的原因,在实际的测试过程中,我们很少直接使用基于GUI的操作生成测试数据。只有在万不得已的情况下,比如没有其他更好的方式可以创建正确可靠的测试数据时,我们才会使用这个方法。 + +而且,这里我需要说明的是,基于GUI操作生成测试数据的方法一般只用于手工测试,因为自动化测试中使用这种数据准备方法,基本相当于要开发一个完整的GUI自动化测试用例,代价太大。 + +那我为什么还要介绍这个方法呢?其实,这个方法更重要的应用场景是,帮助我们找到创建一个测试数据的过程中,后端调用了哪些API,以及修改了哪些数据库的业务表,是“通过API调用生成测试数据”,以及“通过数据库操作生成测试数据”这两种方法的基础。 + +通过API调用生成测试数据 + +通过API调用生成测试数据,是目前主流的测试数据生成方法。其实,当我们通过操作GUI界面生成测试数据时,实际的业务操作往往是由后端的API调用完成的。所以,我们完全可以通过直接调用后端API生成测试数据。 + +还是以用户登录功能的测试为例,我们通过GUI界面注册新用户时,实际上就是调用了createUser这个API。既然知道了具体要调用哪个API,那么我们就可以跳过在GUI界面的操作,直接调用createUser生成“已经注册的用户”这个测试数据了。 + +为了规避在创建测试数据时过于在乎实现细节的问题,在实际工程实践中,我们往往会把调用API生成测试数据的过程封装成测试数据准备函数。那问题是,我怎么才能知道前端新用户注册这个操作到底调用了哪些后端API呢?这里,我推荐三种方式: + + +直接询问开发人员,这是最直接的方法; + +如果你有一定的代码基础,可以直接阅读源代码,这个方法也可以作为直接询问方法的补充; + +在一个你可以独占的环境上执行GUI操作创建测试数据,与此同时监控服务器端的调用日志,分析这个过程到底调用了哪些API。 + + +通过API调用生成测试数据的方法,优点主要体现在以下几个方面: + + +可以保证创建的测试数据的准确性,原因是使用了和GUI操作同样的API调用; +测试数据准备的执行效率更高,因为该方法跳过了耗时的GUI操作; +把创建测试数据的API调用过程,封装成测试数据函数更方便,因为这个调用过程的代码逻辑非常清晰; +测试数据的创建可以完全依赖于API调用,当创建测试数据的内部逻辑有变更时,由于此时API内部的实现逻辑也会由开发人员同步更新,所以我们依旧可以通过调用API来得到逻辑变更后的测试数据,而这个过程对使用来说是完全透明的。 + + +但是,该方法也不是完美无瑕的,其缺点主要表现在: + + +并不是所有的测试数据创建都有对应的API支持。也就是说,并不是所有的数据都可以通过API调用的方式创建,有些操作还是必须依赖于数据库的CRUD操作。那么,这时,我们就不得不在测试数据准备函数中加入数据库的CRUD操作生成测试数据了。 + +有时,创建一条业务线上的测试数据,往往需要按一定的顺序依次调用多个API,并且会在多个API调用之间传递数据,这也无形中增加了测试数据准备函数的复杂性。 + +虽然相比于GUI操作方式,基于API调用的方式在执行速度上已经得到了大幅提升,并且还可以很方便地实现并发执行(比如,使用JMeter或者Locust),但是对于需要批量创建海量数据的场景,还是会力不从心。 + + +因此,业界往往还会通过数据库的CRUD操作生成测试数据。 + +通过数据库操作生成测试数据 + +通过数据库操作生成测试数据,也是目前主流的测试数据生成方法。这个方法的实现原理很简单,就是直接通过数据库操作,将测试数据插入到被测系统的后台数据库中。 + +常见的做法是,将创建数据需要用到的SQL语句封装成一个个的测试数据准备函数,当我们需要创建数据时,直接调用这些封装好的函数即可。 + +还是以用户登录功能测试为例,当我们通过GUI界面注册新用户时,实际上是在后端调用了createUser这个API,而这个API的内部实现逻辑是,将用户的详细信息插入到了userTable和userRoleTable这两张业务表中。 + +那么此时,我们就可以直接在userTable和userRoleTable这两张业务表中插入数据,然后完成这个新用户的注册工作。 + +这样做的前提是,你需要知道前端用户通过GUI操作注册新用户时,到底修改了哪些数据库的业务表。这里,我也推荐三种方式: + + +直接向开发人员索要使用到的SQL语句; + +直接阅读产品源代码; + +在一个你可以独占的环境上执行GUI操作产生测试数据,与此同时,监控独占环境的数据库端业务表的变化,找到哪些业务表发生了变化。 + + +通过数据库操作生成测试数据的方法,主要优点是测试数据的生成效率非常高,可以在较短的时间内创建大批量的测试数据。 + +当然,这个方法的缺点也非常明显,主要体现在以下几个方面: + + +很多时候,一个前端操作引发的数据创建,往往会修改很多张表,因此封装的数据准备函数的维护成本要高得多; +容易出现数据不完整的情况,比如一个业务操作,实际上在一张主表和一张附表中插入了记录,但是基于数据库操作的数据创建可能只在主表中插入了记录,这种错误一般都会比较隐蔽,往往只在一些特定的操作下才会发生异常; +当业务逻辑发生变化时,即SQL语句有变化时,需要维护和更新已经封装的数据准备函数。 + + +综合运用API和数据库的方式生成测试数据 + +目前,在实际的工程实践中,很少使用单一的方法生成测试数据,基本都是采用API和数据库相结合的方式。最典型的应用场景是,先通过API调用生成基础的测试数据,然后使用数据库的CRUD操作生成符合特殊测试需求的数据。所以,你经常会看到很多的数据准备函数中,既有API操作,又有数据库操作。 + +我以创建用户为例,和你分享一下如何综合运用API和数据库两种方式创建测试数据吧。 + +假设,我们需要封装一个创建用户的函数,这个函数需要对外暴露“用户国家”和“支付方式”这两个参数。由于实际创建用户是通过后台createUser API完成的,但是这个API并不支持指定“用户国家”和“支付方式”,所以我们就需要自己封装一个创建用户的函数。 + +自己封装用户创建函数的方法,你可以通过下面这个思路实现: + + +首先,调用createUser API完成基本用户的创建; +然后,调用paymentMethod API实现用户对于不同支付方式的绑定,其中paymentMethod API使用的userID就是上一步中createUser API产生的用户ID; +最后,通过数据库的SQL语句更新“用户国家”。 + + +在这个例子中,createUser API和paymentMethod API只是为了说明如何综合运用API的顺序调用,而其具体参数并不是我要阐述的关键内容,所以我并没有和你详细说明这两个API的参数、实现方式等问题。另外,我在最后一步综合运用了数据库的CRUD操作,完成了创建测试数据的全部工作。 + +这,就是一个封装测试数据准备函数的典型例子了。 + +总结 + +今天,我从测试数据创建的角度,和你分享了准备测试数据的四种方法。 + +其中,基于GUI操作生成测试数据是最原始的方法,但是效率很低,而且会引入不必要的依赖;通过API调用以及数据库操作的方式生成测试数据是目前主流的做法,通过API调用的方式具有数据准确度高但是创建效率较低的特点,而通过数据库的方式具有创建效率高但是维护复杂度也高的特点。 + +所以,在实际项目中,业界往往会综合采用API和数据库的方式生成测试数据,即通过API调用生成基础数据,然后使用数据库的CRUD操作进一步生成符合特殊测试需求的数据。 + +思考题 + +目前,我们需要创建的测试数据并不仅仅局限于数据库,很多时候还需要创建消息队列里面的数据。你在实际工作中遇到过这类测试数据吗?你又是如何处理的呢? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/36浅谈测试数据的痛点.md b/专栏/软件测试52讲/36浅谈测试数据的痛点.md new file mode 100644 index 0000000..35ea3fe --- /dev/null +++ b/专栏/软件测试52讲/36浅谈测试数据的痛点.md @@ -0,0 +1,135 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 36 浅谈测试数据的痛点 + 你好,我是茹炳晟。今天我和你分享的主题是:浅谈测试数据的痛点。 + +在上一篇文章中,我和你分享了创建测试数据的四大类方法,即基于GUI操作生成测试数据、通过API调用生成测试数据、通过数据库操作生成测试数据,以及综合运用API和数据库的方式生成测试数据。 + +但是,我并没有谈到应该在什么时机创建这些测试数据。比如,是在测试用例中实时创建测试数据,还是在准备测试环境时一下子准备好所有的测试数据呢。 + +其实,在不同的时机创建测试数据,就是为了解决准备测试数据的不同痛点。那么,准备测试数据的痛点,都体现在哪些方面呢? + + +在测试用例执行过程中,创建所需的数据往往会耗时较长,从而使得测试用例执行的时间变长; +在测试执行之前,先批量生成所有需要用到的测试数据,就有可能出现在测试用例执行时,这些事先创建好的数据已经被修改而无法正常使用了的情况; +在微服务架构下,测试环境本身的不稳定,也会阻碍测试数据的顺利创建。 + + +那么,今天我们就先来聊聊与测试数据创建时机相关的话题。 + +从测试数据创建的时机来看,主要分为On-the-fly(实时创建)和Out-of-box(事先创建测试数据)两类方法。这两类方法都有各自的优缺点,以及适用的最佳场景。而且在工程实践中,我们往往会综合使用这两种方法。 + +接下来,我先和你分别介绍一下这两类方法。其实,这两类方法我已经在第15篇文章《过不了的坎:聊聊GUI自动化过程中的测试数据》中提到过了。但是,当时我只是笼统地和你分享了这两类方法的概念,并没有详细展开讨论。所以,我今天就会通过一些实例,和你更加详细地讨论这两类方法。 + +On-the-fly + +On-the-fly方法,又称实时创建方法,指的是在测试用例的代码中实时创建要使用到的测试数据。比如,对于用户登录功能的测试,那么在测试用例开始的部分,首先调用我在上一篇文章中介绍的创建新用户的数据准备函数来生成一个新用户,接下来的测试将会直接使用这个新创建的用户。 + +对于On-the-fly,测试用例中所有用到的测试数据,都在测试用例开始前实时准备。采用On-the-fly方式创建的数据,都是由测试用例自己维护的,不会依赖于测试用例外的任何数据,从而保证了数据的准确性和可控性,最大程度地避免了出现“脏”数据的可能。 + +那到底什么是“脏”数据呢?这里的“脏”数据是指,数据在被实际使用前,已经被进行了非预期的修改。 + +从理论上来讲,这种由自己创建和维护数据的方式,是最佳的处理方式,很多早期的测试资料都推荐采用这种方式。但是,随着软件架构的发展,以及软件发布频率的快速增长,这种方式的弊端越来越明显,主要体现在以下三方面: + +首先,实时创建测试数据比较耗时。在测试用例执行的过程中实时创建测试数据,将直接导致测试用例的整体执行时间变长。 + +我曾统计过一个大型电商网站的测试用例执行时间,总的测试用例执行时间中,有30%-40%的时间花在了测试数据的实时准备上,也就是说测试数据的实时准备花费了差不多一半的测试用例执行时间。 + +对传统软件企业来说,它们可能并不太在意这多出来的测试执行时间,因为它们的软件发布周期比较长,留给测试的时间也比较长,所以这多出来的时间可以忽略不计。 + +但是,对于互联网软件企业来说,它们的软件发布频率很高,相应地留给测试执行的时间也都很短,那么缩短测试数据的准备时间的重要性就不言而喻了。 + +要解决创建测试数据耗时的问题,除了从测试数据准备函数的实现入手外,还可以考虑采用我后面要介绍的事先创建测试数据Out-of-box的方式。 + +其次,测试数据本身存在复杂的关联性。很多时候你为了创建一个你需要使用的业务数据,往往需要先创建一堆其他相关联的数据,越是业务链后期的数据,这个问题就越严重。 + +比如,创建订单数据这个最典型的案例。由于创建订单的数据准备函数需要提供诸如卖家、买家、商品ID等一系列的前置数据,所以你就不得不先创建出这些前置数据。这样做,一方面测试数据准备的复杂性直线上升,另一方面创建测试数据所需要的时间也会变得更长。 + +为了缓解这个问题,你可以考虑将部分相对稳定的数据事先创建好,而不要采用On-the-fly的方式去创建所有的数据。 + +最后一个问题来自于微服务架构的调整。早期的软件架构都是单体的,只要测试环境部署成功了,那么所有的功能就都可以使用了。而现如今,大量的互联网产品都采用了微服务架构,所以,很多时候测试环境并不是100%处于全部可用的状态。也就是说,并不是所有的服务都是可用的,这就给测试数据准备带来了新的挑战。 + +比如,你为了测试用户登录功能,根据On-the-fly的策略,你首先需要创建一个新用户。假设在微服务架构下,注册用户和用户登录隶属于两个不同的微服务,而此时注册用户的微服务恰好因为某种原因处于不可用状态,那么这时你就无法成功创建这个用户,也就是无法创建测试数据。因此,整个测试用例都无法顺利执行,显然这不是我们想要的结果。 + +为了解决这个问题,你可以采用事先创建数据Out-of-box的方式,只要能够保证测试环境在某个时间段没有问题,那么就可以在这个时间段事先创建好测试数据。 + +为了解决上述三个问题,Out-of-box(即事先创建测试数据)的方式就应运而生了。那么, + +接下来我们就一起看看这个方式的原理,以及适用的场景吧。 + +Out-of-box + +Out-of-box方法,又称开箱即用方法,指的是在准备测试环境时就预先将测试需要用到的数据全部准备好,而不是在测试用例中实时创建。因此,我们可以节省不少测试用例的执行时间,同时也不会存在由于环境问题无法创建测试数据而阻碍测试用例执行的情况。也就是说Out-of-box方法可以克服On-the-fly方法的缺点,那么这个方式又会引入哪些致命的新问题呢? + +Out-of-box最致命的问题是“脏”数据。 + +比如,我们在测试用例中使用事先创建好的用户进行登录测试,但这个用户的密码被其他人无意中修改了,导致测试用例执行时登录失败,也就不能顺利完成测试了。那么,此时这个测试用户数据就成为了“脏”数据。 + +再比如,我们在测试用例中使用事先创建的测试优惠券去完成订单操作,但是由于某种原因这张优惠券已经被使用过了,导致订单操作的失败,也就意味着测试用例执行失败。那么,此时这个测试优惠券数据也是“脏”数据。 + +由此可见,这些事先创建好的测试数据,在测试用例执行的那个时刻,是否依然可用其实是不一定的,因为这些数据很有可能在被使用前已经发生了非预期的修改。 + +这些非预期的修改主要来自于以下三个方面: + + +其他测试用例使用了这些事先创建好的测试数据,并修改了这些数据的状态; + +执行手工测试时,因为直接使用了事先创建好的数据,很有可能就会修改了某些测试数据; + +自动化测试用例的调试过程,修改了事先创建的测试数据; + + +为了解决这些“脏”数据,我们只能通过优化流程去控制数据的使用。目前,业内有些公司会将所有事先创建好的测试数据列在一个Wiki页面,然后按照不同的测试数据区段来分配使用对象。 + +比如,假设我们事先创建了1000个测试用户,那么用户ID在0001-0200范围内数据给这个团队使用,而用户ID在0201-0500范围内的数据则给另一个团队使用。这个分配工作,要靠流程保证,那么前提就是所有人都要遵守这些流程。 + +但我一直认为,但凡需要靠流程保证的一定不是最靠谱的,因为你无法确保所有人都会遵守流程。也正是因为这个原因,在实际项目中我们还是会经常看到由“脏”数据引发测试用例执行失败的案例。 + +更糟糕的是,如果自动化测试用例直接采用硬编码的方式,去调用那些只能被一次性使用的测试数据(比如订单数据、优惠券等)的话,你会发现测试用例只能在第一次执行时通过,后面再执行都会因为测试数据的问题而失败。 + +所以,你还需要在测试用例级别保证测试数据只被调用一次,而这往往会涉及到跨测试用例的测试数据维护问题,往往实现起来非常麻烦。所以说,Out-of-box方法不适用于只能一次性使用的测试数据场景。 + +综合运用On-the-fly和Out-of-box + +为了充分利用On-the-fly和Out-of-box这两种方式的各自优点,并且规避各自的缺点,实际的工程实践中,往往是采用综合运用On-the-fly和Out-of-box的方式来实现测试数据的准备的。 + +在实际的测试项目中,我们可以根据测试数据的特性,把它们分为两大类,用业内的行话来讲就是“死水数据”和“活水数据”。 + +“死水数据”是指那些相对稳定,不会在使用过程中改变状态,并且可以被多次使用的数据。比如,商品分类、商品品牌、场馆信息等。这类数据就非常适合采用Out-of-box方式来创建。 + +这里需要特别说明的是,哪些数据属于“死水数据”并不是绝对的,由测试目的决定。 + +比如,用户数据在大多数的非用户相关的测试用例中基本属于“死水数据”,因为绝大多数的业务测试都会包含用户登录的操作,而且并不会去修改用户本身的数据属性,所以这时我们就可以将用户数据按照“死水数据”处理,也就是采用Out-of-box的方式创建。 + +但是,对于那些专门测试用户账号的测试用例来讲,往往会涉及到用户撤销、激活、修改密码等操作,那么此时的用户数据就不再是“死水数据”了,而应该按照“活水数据”处理。 + +“活水数据”是指那些只能被一次性使用,或者经常会被修改的测试数据。最典型的数据是优惠券、商品本身、订单等类似的数据。这类数据通常在被一次性使用后状态就发生了变化,不能反复使用。那么这类测试数据,就更适合采用On-the-fly自维护的方式。 + +同时,由于有Out-of-box数据的支持,这类数据往往不需要从最源头开始创建,而是可以基于已有的Out-of-box数据生成。 + +比如,在使用On-the-fly方式创建订单数据时,你可以直接使用Out-of-box的用户数据来作为买家数据。 + +由此可见,综合运用这两类方法,可以以互补的方式解决测试数据准备的很多痛点,比如测试数据准备比较耗时、测试数据存在“脏”数据的可能,以及测试环境不稳定造成的测试数据无法创建等问题。 + +总结 + +今天我从测试数据创建时机的角度,和你分享了On-the-fly和Out-of-box这两类创建数据的方式。 + +On-the-fly方法又称为实时创建方法,指的是在测试用例的代码中实时创建测试用例所要使用到的测试数据,具有数据可靠性高的优点,但是会比较耗时。 + +而Out-of-box方法又称为开箱即用方法,指的是在准备测试环境时就事先准备好测试需要用到的全部数据。这样可以有效缩短测试用例的执行时间,但是存在“脏”数据的问题。 + +最后,我从“死水数据”和“活水数据”的角度讨论了如何综合运用上述两种方式创建测试数据,其中“死水数据”适合用Out-of-box的方式,而“活水数据”适合采用On-the-fly的方式。 + +思考题 + +你所在的项目中,采用的是什么样的测试数据准备策略,这个策略的优缺点是什么?为什么会选择这样的策略呢?另外,你所在团队会使用线上真实的数据进行测试吗? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/37测试数据的“银弹”-统一测试数据平台(上).md b/专栏/软件测试52讲/37测试数据的“银弹”-统一测试数据平台(上).md new file mode 100644 index 0000000..21b160a --- /dev/null +++ b/专栏/软件测试52讲/37测试数据的“银弹”-统一测试数据平台(上).md @@ -0,0 +1,126 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 37 测试数据的“银弹”- 统一测试数据平台(上) + 你好,我是茹炳晟。今天我和你分享的主题是:测试数据的“银弹”之统一测试数据平台(上)。 + +在《如何准备测试数据?》和《浅谈测试数据的痛点》这两篇文章中,我介绍了创建测试数据的主要方法,以及创建测试数据的时机。在此基础上,今天我将和你聊聊全球大型电商企业中关于准备测试数据的最佳实践。 + +这个主题,我会从全球大型电商企业早期的测试数据准备实践谈起,和你一起分析这些测试数据准备方法在落地时遇到的问题,以及如何在实践中解决这些问题。其实,这种分析问题、解决问题的思路,也是推动着测试数据准备时代从1.0到2.0再到3.0演进的原因。 + +所以,在这个过程中,你可以跟着时代的演进,理解测试数据准备技术与架构的发展历程,并进一步掌握3.0时代出现的业内处于领先地位的“统一测试数据平台”的设计思路。 + +因为这个主题的内容相对较多,为了降低你的学习负担、便于理解消化,我把它分成了两篇文章。同时,为了和你深入地讨论这个话题,也可以真正做到“接地气儿”,我会在这两篇文章中列举很多工程中的实际问题,并给出相应的解决方案。或许这些问题你也曾经遇到过,或者正在被其折磨,希望我给出的这些方案,可以给你启发,帮你攻克这些难关。 + +我们就先从数据准备的1.0时代谈起吧。 + +测试数据准备的1.0时代 + +其实,据我观察,目前很多软件企业还都处于测试数据准备的1.0时代。 + +这个阶段最典型的方法就是,将测试数据准备的相关操作封装成数据准备函数。这些相关操作,既可以是基于API的,也可以是基于数据库的,当然也可以两者相结合。 + +有了这些数据准备函数后,你就可以在测试用例内部以On-the-fly的方式调用它们实时创建数据,也可以在测试开始之前,在准备测试环境的阶段以Out-of-box的方式调用它们事先创建好测试数据。 + +那么,一个典型的数据准备函数长什么样子呢?我们一起来看看这段代码吧,里面的createUser函数,就是一个典型的数据准备函数了。 + +public static User createUser(String userName, String password, UserType userType, PaymentDetail paymentDetail, Country country, boolean enable2FA) +{ + //使用API调用的方式和数据库CRUD的方式实际创建测试数据 + ... +} + + +乍一看,你可能觉得,如果可以将大多数的业务数据创建都封装成这样的数据准备函数,那么测试数据的准备过程就变成了调用这些函数,而无需关心数据生成的细节,这岂不是很简单、直观嘛。 + +但,真的是这样吗? + +这里,我建议你在继续阅读后面的内容之前,先思考一下这个方法会有什么短板,然后再回过头来看答案,这将有助于加深你对这个问题的理解。当然,如果你已经在项目中实际采用了这个方法的话,相信你已经对它的短板了如指掌了。 + +好了,现在我来回答这个问题。利用这种数据准备函数创建测试数据方法的最大短板,在于其参数非常多、也非常复杂。在上面这段代码中,createUser函数的参数有6个。而实际项目中,由于测试数据本身的复杂性、灵活性,参数的数量往往会更多,十多个都是很常见的。 + +而在调用数据准备函数之前,你首先要做的就是准备好这些参数。如果这些参数的数据类型是基本类型的话,还比较简单(比如,createUser函数中userName、password是字符串型,enable2FA是布尔型),但这些参数如果是对象(比如,createUser函数的userType、paymentDetail和Country就是对象类型的参数)的话,就很麻烦了。为什么呢? + +因为,你需要先创建这些对象。更糟糕的是,如果这些对象的初始化参数也是对象的话,就牵连出了一连串的数据创建操作。 + +下面这段代码,就是使用createUser函数创建测试数据的一个典型代码片段。 + +//准备createUser的参数 +UserType userType = new UserType("buyer"); +Country country = new Country("US"); + +//准备createPaymentDetail的参数 +PaymentType paymentType = new PaymentType("Paypal"); +//调用createPaymentDetail创建paymentDetail对象 +PaymentDetail paymentDetail = createPaymentDetail(paymentType,2000); + +//对主要的部分,调用createUser产生用户数据 +User user=createUser(“TestUser001”, “abcdefg1234”, userType, paymentDetail, country, true); + + +由此可见,每次使用数据准备函数创建数据时,你都要知道待创建数据的全部参数细节,而且还要为此创建这些参数的对象,这就让原本看似简单的、通过数据准备函数调用生成测试数据的过程变得非常复杂。 + +那么,你可能会问,这个过程是必须的吗,可以用个某些技术手段“跳过”这个步骤吗? + +其实,绝大多数的测试数据准备场景是,你仅仅需要一个所有参数都使用了缺省值的测试数据,或者只对个别几个参数有明确的要求,而其他参数都可以是缺省值的测试数据。 + +以用户数据创建为例,大多情况下你只是需要一个具有缺省(Default)参数的用户,或者是对个别参数有要求的用户。比如,你需要一个美国的用户,或者需要一个userType是buyer的用户。这时,让你去人为指定所有你并不关心的参数的做法,其实是不合理的,也没有必要。 + +为了解决这个问题,在工程实践中,就引入了如图1所示的封装数据准备函数的形式。 + + + +图1 数据准备函数的封装 + +在这个封装中,我们将实际完成数据创建的函数命名为createUserImpl,这个函数内部将通过API调用和数据库CRUD操作的方式,完成实际数据的创建工作,同时对外暴露了所有可能用到的user参数A、B、C、D、E。 + +接着,我们封装了一个不带任何参数的createDefaultUser函数。函数内部的实现,首先会用默认值初始化user的参数A、B、C、D、E,然后再将这些参数作为调用createUserImpl函数时的参数。 + +那么,当测试用例中仅仅需要一个没有特定要求的默认用户时,你就可以直接调用这个createDefaultUser函数,隐藏测试用例并不关心的其他参数的细节,此时也就真正做到了用一行代码生成你想要的测试数据。 + +而对于那些测试用例只对个别参数有要求的场景,比如只对参数A有要求的场景,我们就可以为此封装一个createXXXUser(A)函数,用默认值初始化参数B、C、D、E,然后对外暴露参数A。 + +当测试用例需要创建A为特定值的用户时,你就可以直接调用createXXXUser(A)函数,然后createXXXUser(A)函数会用默认的B、C、D、E参数的值加上A的值调用createUserImpl函数,以此完成测试数据的创建工作。 + +当然,如果是对多个参数有特定要求的场景,我们就可以封装出createYYYUser这样暴露多个参数的函数。 + +通过这样的封装,对于一些常用的测试数据组合,我们通过一次函数调用就可以生成需要的测试数据;而对于那些比较偏门或者不常用的测试数据,我们依然可以通过直接调用最底层的createUserImpl函数完成数据创建工作。可见,这个方法相比之前已经有了很大的进步。 + +但是,在实际项目中,大量采用了这种封装的数据准备函数后,还有一些问题亟待解决,主要表现在以下几个方面: + + +对于参数比较多的情况,会面临需要封装的函数数量很多的尴尬。而且参数越多,组合也就越多,封装函数的数量也就越多。 + +当底层Impl函数的参数发生变化时,需要修改所有的封装函数。 + +数据准备函数的JAR包版本升级比较频繁。由于这些封装的数据准备函数,往往是以JAR包的方式提供给各个模块的测试用例使用的,并且JAR会有对应的版本控制,所以一旦封装的数据准备函数发生了变化,我们就要升级对应JAR包的版本号。- +而这些封装的数据准备函数,由于需要支持新的功能,并修复现有的问题,所以会经常发生变化,因此测试用例中引用的版本也需要经常更新。 + + +为了可以进一步解决这三个问题,同时又可以最大程度地简化测试数据准备工作,我们就迎来了数据准备函数的一次大变革,由此也将测试数据准备推向了2.0时代。 + +这里需要强调一下,我往往把到目前为止所采用的测试数据实践称为数据准备的1.0时代。我会在下一篇文章中,和你详细介绍2.0时代下的测试数据准备都有哪些关键的技术创新,相信一定会让你有眼前一亮的感觉。 + +总结 + +在1.0时代,准备测试数据最典型的方法就是,将测试数据准备的相关操作封装成数据准备函数。 + +归纳起来,这个时代的数据准备函数,主要有两种封装形式: + +第一种是,直接使用暴露全部参数的数据准备函数,虽说灵活性最好,但是每次调用前都需要准备大量的参数,从使用者的角度来看便利性比较差; + +第二种是,为了解决便利性差的问题,我们引入了更多的专用封装函数,在灵活性上有了很大的进步,但是也带来了可维护差的问题。 + +所以,为了可以更高效地准备测试数据,我们即将迎来测试数据准备的2.0时代,拭目以待吧。 + +思考题 + +你所在的团队,是否已经在使用我今天聊到的这些方法了呢,使用过程中还遇到了哪些挑战?如果没有使用这些方法的话,你又是采用什么方法创建测试数据的呢? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/38测试数据的“银弹”-统一测试数据平台(下).md b/专栏/软件测试52讲/38测试数据的“银弹”-统一测试数据平台(下).md new file mode 100644 index 0000000..4485479 --- /dev/null +++ b/专栏/软件测试52讲/38测试数据的“银弹”-统一测试数据平台(下).md @@ -0,0 +1,138 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 38 测试数据的“银弹”- 统一测试数据平台(下) + 你好,我是茹炳晟,今天我分享的主题是:“测试数据的“银弹”之统一测试数据平台(下)”。 + +在上一篇文章中,我和你分享了测试数据准备1.0时代的实践,在这个1.0时代,测试数据准备的最典型方法是,将测试数据准备的相关操作封装成数据准备函数。今天,我将继续为你介绍测试数据准备的2.0和3.0时代的实践,看看创建测试数据的方法,又发生了哪些变革。 + +在1.0时代,为了让数据准备函数使用更方便,避免每次调用前都必须准备所有参数的问题,我和你分享了很多使用封装函数隐藏默认参数初始化细节的方法。 + +但是,这种封装函数的方式,也会带来诸如需要封装的函数数量较多、频繁变更的维护成本较高,以及数据准备函数JAR版本升级的尴尬。所以,为了系统性地解决这些可维护性的问题,我们对数据准备函数的封装方式做了一次大变革,也由此进入了测试数据准备的2.0时代。 + +测试数据准备的2.0时代 + +在测试数据准备的2.0时代,数据准备函数不再以暴露参数的方式进行封装了,而是引入了一种叫作Builder Pattern(生成器模式)的封装方式。这个方式能够在保证最大限度的数据灵活性的同时,提供使用上的最大便利性,并且维护成本还非常低。 + +事实上,如果不考虑跨平台的能力,Builder Pattern可以说是一个接近完美的解决方案了。关于什么是“跨平台的能力”,我会在测试数据准备的3.0时代中解释,这里先和你介绍我们的主角:Builder Pattern。 + +Builder Pattern是一种数据准备函数的封装方式。在这种方式下,当你需要准备测试数据时,不管情况多么复杂,你一定可以通过简单的一行代码调用来完成。听起来有点玄乎?没关系,看完我列举的这些实例,你马上就可以理解了。 + +实例一:你需要准备一个用户数据,而且对具体的参数没有任何要求。也就是说,你需要的仅仅是一个所有参数都可以采用默认值的用户。那么,在Builder Pattern的支持下,你只需要执行一行代码就可以创建出你需要的这个所有参数都是默认值的用户了。这行代码就是: + +UserBuilder.build(); + + +实例二:你现在还需要一个用户,但是这次需要的是一个美国的用户。那么这时,在Builder Pattern的支持下,你只用一行代码也可以创建出这个指定国家是美国,而其他参数都是默认值的用户。这行代码就是: + +UserBuilder.withCountry("US").build(); + + +实例三:你又需要这样一个用户数据:英国用户,支付方式是Paypal,其他参数都是默认值。那么这时,在Builder Pattern的支持下,你依然可以通过一行简单的代码创建出满足这个要求的用户数据。这行代码就是: + +UserBuilder.withCountry("US").withPaymentMethod("Paypal").build(); + + +通过这三个实例,你肯定已经感受到,相对于1.0时代的通过封装函数隐藏默认参数初始化的方法来说,Builder Pattern简直太便利了。 + +趁热打铁,我再来和你总结一下Builder Pattern的便利性吧: + + +如果仅仅需要一个全部采用缺省参数的数据的话,你可以直接使用TestDataBuilder.build()得到; +如果你对其中的某个或某几个参数有特定要求的话,你可以通过“.withParameter()”的方式指定,而没有指定的参数将自动采用默认值。 + + +这样一来,无论你对测试数据有什么要求,都可以以最灵活和最简单的方式,通过一行代码得到你要的测试数据。 + +在实际工程项目中,随着Builder Pattern的大量使用,又逐渐出现了更多的新需求,为此我归纳总结了以下4点: + + +有时候,出于执行效率的考虑,我们不希望每次都重新创建测试数据,而是希望可以从被测系统的已有数据中搜索符合条件的数据; +但是,还有些时候,我们希望测试数据必须是全新创建的,比如需要验证新建用户首次登录时,系统提示修改密码的测试场景,就需要这个用户一定是被新创建的; +更多的时候,我们并不关心这些测试数据是新创建的,还是通过搜索得到的,我们只希望以尽可能短的时间得到需要的测试数据; +甚至,还有些场景,我们希望得到的测试数据一定是来自于Out-of-box的数据。 + + +为了能够满足上述的测试数据需求,我们就需要在Builder Pattern的基础上,进一步引入Build Strategy的概念。顾名思义,Build Strategy指的是数据构建的策略。 + +为此,我们引入了Search Only、Create Only、Smart和Out-of-box这四种数据构建的策略。这四类构建策略在Builder Pattern中的使用很简单,只要按照以下的代码示例指定构建策略就可以了: + +UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.SEARCH_ONLY.build(); +UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.CREATE_ONLY).build(); +UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.SMART).build(); +UserBuilder.withCountry(“US”).withBuildStrategy(BuildStrategy.OUT_OF_BOX).build(); + + +结合着这四类构建策略的代码,我再和你分享一下,它们会在创建测试数据时执行什么操作,返回什么样的结果: + + +当使用BuildStrategy.SEARCH_ONLY策略时,Builder Pattern会在被测系统中搜索符合条件的测试数据,如果找到就返回,否则就失败(这里,失败意味着没能返回需要的测试数据); +当使用BuildStrategy.CREATE_ONLY策略时,Builder Pattern会在被测系统中创建符合要求的测试数据,然后返回; +当使用BuildStrategy.SMART策略时,Builder Pattern会先在被测系统中搜索符合条件的测试数据,如果找到就返回,如果没找到就创建符合要求的测试数据,然后返回; +当使用BuildStrategy.OUT_OF_BOX策略时,Builder Pattern会返回Out-of-box中符合要求的数据,如果在Out-of-box中没有符合要求的数据,build函数就会返回失败; + + +由此可见,引入Build Strategy之后,Builder Pattern的适用范围更广了,几乎可以满足所有的测试数据准备的要求。 + +但是,不知道你注意到没有,我们其实还有一个问题没有解决,那就是:这里的Builder Pattern是基于Java代码实现的,如果你的测试用例不是基于Java代码实现的,那要怎么使用这些Builder Pattern呢? + +在很多大型公司,测试框架远不止一套,不同的测试框架也是基于不同语言开发的,比如有些是基于Java的,有些是基于Python的,还有些基于JavaScript的。而非Java语言的测试框架,想要使用基于Java语言的Builder Pattern的话,往往需要进行一些额外的工作,比如调用一些专用函数等。 + +我来举个例子吧。对于JavaScript来说,如果要使用Java的原生类型或者引用的话,你需要使用Java.type()函数;而如果要使用Java的包和类的话,你就需要使用专用的importPackage()函数 和 importClass() 函数。 + +这些都会使得调用Java方法很不方便,其他语言在使用基于Java的Builder Pattern时也有同样的问题。 + +但是,我们不希望、也不可能为每套基于不同开发语言的测试框架都封装一套Builder Pattern。所以,我们就希望一套Builder Pattern可以适用于所有的测试框架,这也就是我在前面提到的测试准备函数的“跨平台的能力”了。 + +为了解决这个问题,测试数据准备走向了3.0时代。 + +测试数据准备的3.0时代 + +为了解决2.0时代跨平台使用数据准备函数的问题,我们将基于Java开发的数据准备函数用Spring Boot包装成了Restful API,并且结合Swagger给这些Restful API提供了GUI界面和文档。 + +这样一来,我们就可以通过Restful API调用数据准备函数了,而且由于Restful API是通用接口,所以只要测试框架能够发起http调用,就能使用这些Restful API。于是,几乎所有的测试框架都可以直接使用这些Restful API准备测试数据。 + +由此,测试数据准备工作自然而然地就发展到了平台化阶段。我们把这种统一提供各类测试数据的Restful API服务,称为“统一测试数据平台”。 + +最初,统一测试数据平台就是服务化了数据准备函数的功能,并且提供了GUI界面以方便用户使用,除此以外,并没有提供其他额外功能。如图1所示就是统一测试数据平台的UI界面。 + + + +图1 最初的统一测试数据平台UI界面 + +后来,随着统一测试数据平台的广泛使用,我们逐渐加入了更多的创新设计,统一测试数据平台的架构也逐渐演变成了如图2所示的样子。 + + + +图2 演变后的统一测试数据平台架构 + +接下来,我和你分享一下统一测试数据平台的架构设计中最重要的两个部分: + + +引入了Core Service和一个内部数据库。其中,内部数据库用于存放创建的测试数据的元数据;Core Service在内部数据库的支持下,提供数据质量和数量的管理机制。 + +当一个测试数据被创建成功后,为了使得下次再要创建同类型的测试数据时可以更高效,Core Service会自动在后台创建一个Jenkins Job。这个Jenkins Job会再自动创建100条同类型的数据,并将创建成功的数据的ID保存到内部数据库,当下次再请求创建同类型数据时,这个统一测试数据平台就可以直接从内部数据库返回已经事先创建的数据。- +在一定程度上,这就相当于将原本的On-the-fly转变成了Out-of-box,缩短整个测试用例的执行时间。当这个内部数据库中存放的100条数据被逐渐被使用,导致总量低于20条时,对应的Jenkins Job会自动把该类型的数据补足到100条。而这些操作对外都是透明的,完全不需要我们进行额外的操作。 + + +这就是测试数据准备的3.0时代的最佳实践了。关于这个统一测试数据平台,如果你还想了解更多的技术细节,欢迎你给我留言,我们一起讨论。 + +总结 + +我和你分享了测试数据准备2.0时代的Builder Pattern实践,以及3.0时代的统一测试数据平台。 + +2.0时代的Builder Pattern在提供了最大限度的数据灵活性的同时,还保证了使用上的最大便利性,并且维护成本还非常低。如果不考虑跨平台能力的话,Builder Pattern已经是一个接近完美的解决方案了。 + +3.0时代统一测试数据平台,其实是将所有的数据准备函数在Spring Boot的支持下转变为了Restful API,为跨平台和跨语言的各类测试框架提供了统一的数据准备方案。 + +思考题 + +关于统一测试数据平台,由于引入了Core Service和内部数据库,所以可以在此基础上实现更多的高级功能。对此,你觉得还可以引入哪些功能呢? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/39从小作坊到工厂:什么是SeleniumGrid?如何搭建SeleniumGrid?.md b/专栏/软件测试52讲/39从小作坊到工厂:什么是SeleniumGrid?如何搭建SeleniumGrid?.md new file mode 100644 index 0000000..8990fbb --- /dev/null +++ b/专栏/软件测试52讲/39从小作坊到工厂:什么是SeleniumGrid?如何搭建SeleniumGrid?.md @@ -0,0 +1,202 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 39 从小作坊到工厂:什么是Selenium Grid?如何搭建Selenium Grid? + 你好,我是茹炳晟,今天我分享的主题是“从小作坊到工厂:什么是Selenium Grid?如何搭建Selenium Grid?”。 + +从今天开始,我们就要一起进入测试基础架构这个新的系列了。我将用四篇文章的篇幅,从0到1,为你深入剖析大型互联网企业的测试基础架构设计,以及其原始驱动力,和你探讨测试执行环境设计、测试报告平台设计以及测试基础架构与CI/CD的集成等内容。当然,在这其中还会涉及到很多具有前瞻性的设计创新。 + +虽说测试基础架构是资深测试开发人员的必备技能,但此时你可能还并不清楚测试基础架构到底指的是什么?没关系,当你阅读完这个系列的文章之后,相信你一定可以对测试基础架构,以及其关键设计有一个清晰、全面的认识。 + +所以,今天我就先和你分享一下,我眼中的测试基础架构到底是指什么? + +什么是测试基础架构? + +测试基础架构指的是,执行测试的过程中用到的所有基础硬件设施以及相关的软件设施。因此,我们也把测试基础架构称之为广义的测试执行环境。通常来讲,测试基础架构主要包括以下内容: + + +执行测试的机器; +测试用例代码仓库; +发起测试执行的Jenkins Job; +统一的测试执行平台; +测试用例执行过程中依赖的测试服务,比如提供测试数据的统一测试数据平台、提供测试全局配置的配置服务、生成测试报告的服务等; +… + + +由于测试基础架构的核心是围绕测试执行展开的,所以我们今天就先来重点讨论一下“执行测试的机器”部分。 + +这部分内容的展开,我会从早期最简单的方法谈起,然后探讨这个方法在实际执行中的弊端,并由此引出我们今天讨论的主角:Selenium Grid。 + +先试想一下:你要在一个典型测试场景中,基于某种浏览器去执行Web应用的GUI测试。这时,你首先要做的就是找到相应的机器,并确保上面已经安装了所需的浏览器。如果这台机器上,还没有安装所需浏览器的话,你需要先安装这个浏览器。一切准备就绪后,你就可以使用这台机器执行测试了。 + +如果你要执行的测试只需要覆盖一种浏览器的话,那就很简单了,你只要事先准备好一批专门的机器或者虚拟机,然后安装好所需的浏览器就可以了。同时,如果测试用例的数量也不是很多的话,你需要的这批机器或者虚拟机的数量也不会很多。执行测试时,你只要将需要使用的那台机器的地址提供给测试用例就可以了。 + +其实,这种模式就是典型的“小作坊”模式。“小作坊”模式的特点就是,人工维护一批数量不多(通常在30台以内)的执行测试的机器,然后按需使用。 + +对于小团队来讲,“小作坊”模式的问题并不大。但是,随着测试覆盖率要求的提升,以及测试用例数量的增加,这种“小作坊”模式的弊端就逐渐显现,并被不断放大了。其中,最主要问题体现在以下四个方面: + + +当Web应用需要进行不同浏览器的兼容性测试时,首先你需要准备很多台机器或者虚拟机,并安装所需的不同浏览器;然后,你要为这些机器建立一个列表,用于记录各台机器安装了什么浏览器;最后,你在执行测试时,需要先查看机器列表以选择合适的测试执行机。 + +当Web应用需要进行同一浏览器的不同版本的兼容性测试时,你同样需要准备很多安装有同一浏览器的不同版本的测试执行机,并为这些机器建立列表,记录各台机器安装的浏览器版本号,然后执行测试时先查看列表以选择合适的测试执行机。 + +测试执行机的机器名或者IP发生变化,以及需要新增或者减少测试机时,都需要人工维护这些机器列表。很显然,这种维护方式效率低下,且容易出错。 + +在GUI自动化测试用例的数量比较多的情况下,你不希望只用一台测试执行机以串行的方式执行测试用例,而是希望可以用上所有可用的测试执行机,以并发的方式执行测试用例,以加快测试速度。为了达到这个目的,你还是需要人工管理这些测试用例和测试执行机的对应关系。 + + +这四种情况的问题,可以归结为:测试执行机与测试用例的关系是不透明的,即每个测试用例都需要人为设置测试执行机。 + +为了改善这种局面,Selenium Grid就应运而生了。 + + +一方面,使用Selenium Grid可以让测试机器的选择变得“透明”。也就是说,我们只要在执行测试用例时指定需要的浏览器版本即可,而无需关心如何找到合适的测试执行机。因为,这寻找符合要求的测试执行机的工作,Selenium Grid可以帮你完成。 +另一方面,Selenium Grid的架构特点,天生就能很好地支持测试用例的并发执行。 + + +接下来,我就和你详细聊聊到底什么是Selenium Grid,Selenium Grid的架构是什么样的。 + + + +图1 Selenium Grid的架构 + +从本质上讲,Selenium Grid是一种可以并发执行GUI测试用例的测试执行机的集群环境,采用的是HUB和Node模式。这个概念有些晦涩难懂,我来举个例子吧。 + +假如,现在有个律师事务所要接受外来业务,那么就会有一个老大专门负责对外接受任务。收到任务后,这个老大会根据任务的具体要求找到合适的手下,然后将该任务分发给手下去执行。 + +那么,这个老大是怎么知道哪个手下最适合处理这个任务呢?其实,这个老大手下的每个人都会事先报备自己具备的技能,这样老大在分发任务的时候,就可以做到“有的放矢”了。 + +现在,我们再回到Selenium Grid。Selenium Grid由两部分构成,一部分是Selenium Hub,另一部分是Selenium Node。 + +将这个律师事务所的例子,与Selenium Grid做个类比,它们的对应关系是: + + +这个对外的老大对应的是Selenium Hub; +具体执行任务的手下,对应的是Selenium Node; +老大接到任务后分配给手下执行的过程,就是Selenium Hub将测试分配到Selenium Node执行的过程; +老大的手下向他报备自己技能的过程,就是Selenium Node向Selenium Hub注册的过程。 + + +也就是说,Selenium Hub用来管理各个Selenium Node的注册信息和状态信息,并且接收远程客户端代码的测试调用请求,并把请求命令转发给符合要求的Selenium Node执行。 + +现在,我们已经搞明白了什么是Selenium Grid,以及Selenium Grid的工作模式。Selenium Grid的功能是不是很酷炫呢?那么,Selenium Grid的搭建是不是很难?接下来,我们就看看如何搭建自己的Selenium Grid吧。 + +在这里,我会依次给你介绍传统的Selenium Grid和基于Docker的Selenium Grid的搭建方法。通过这部分内容我要达到的目的是,可以帮你搭建起属于自己的Selenium Grid。 + +传统Selenium Grid的搭建方法 + +我将通过一个实例,和你分享如何搭建一个传统的Selenium Grid。 + +现在,我们的需求是,搭建一个具有1个Node的Selenium Grid。那么通常来讲我们需要2台机器,其中一台作为Hub,另外一台作为Node,并要求这两台机器已经具备了Java执行环境。 + + +通过官网下载selenium-server-standalone-.jar文件。这里需要注意的是,不管是Hub还是Node,都使用同一个JAR包启动,只是启动参数不同而已。 + +将下载的selenium-server-standalone-.jar文件分别复制到两台机器上。 + +选定其中一台机器作为Selenium Hub,并在这台机器的命令行中执行以下命令: + +java -jar selenium-server-standalone-.jar -role hub + + +在这条命令中,“-role hub”的作用是将该机器启动为Selenium Hub。启动完成后,这台机器默认对外提供服务的端口是4444。 + +然后,你就可以在这台机器上通过http://localhost:4444/grid/console观察Selenium Hub的状态,也可以在其他机器上通过http://:4444/grid/console观察Selenium Hub的状态。其中,是这台Selenium Hub机器的IP地址。由于此时还没有Node注册到该Hub上,所以你看不到任何的Node信息。 + +启动过程和状态信息,分别如图2、3所示。 + + + +图2 Selenium Hub启动过程 + + + +图3 没有挂载任何Node的Selenium Hub + + +在另一台作为Selenium Node的机器上执行以下命令: + +java -jar selenium-server-standalone-.jar -role node -hub http:// :4444/grid/register + + +这条命令中,“-role node”的作用是,将该机器启动为Selenium Node,并且通过“-hub”指定了Selenium Hub的节点注册URL。 + +执行成功后,你可以再次打开http://:4444/grid/console观察Selenium Hub的状态。此时,你可以看到已经有一个Node挂载到了Hub上。这个Node,就是用来实际执行测试的机器了。并且,这个Node上已经缺省提供了5个Firefox浏览器的实例、5个Chrome浏览器的实例和1个IE浏览器的实例,同时默认允许的并发测试用例数是5个。 + +如果你想自己配置这些内容,可以在启动Node的时候提供不同的启动参数。具体可以指定哪些参数,你可以参考Selenium Grid的官方文档。 + +如图4所示为Node的启动过程,如图5所示为在Hub端注册Node的过程,如图6所示为挂载完Node后Selenium Hub的状态。 + + + +图4 Node的启动过程 + + + +图5 Hub端Node注册的过程 + + + +图6 挂载完Node后的Selenium Hub状态 + + +完成上述操作后,在测试用例中通过以下代码将测试指向Selenium Hub,然后由Selenium Hub完成实际测试执行机的分配与调度工作。其中,最关键的部分是,创建RemoteWebDriver实例的第一个参数,这个参数不再是一个具体的测试执行机的IP地址或者名字了,而是Selenium Hub的地址。 + +DesiredCapabilities capability = DesiredCapabilities.firefox(); +WebDriver driver = new RemoteWebDriver(new URL(“http://:4444/wd/hub”), capability); + + +至此,我们就已经完成了Selenium Grid的搭建工作。正如上面的五个步骤所示,这个搭建过程非常简单。接下来,你就自己动手尝试一下吧。 + +基于Docker的Selenium Grid的搭建方法 + +目前,Docker技术的广泛普及,再加上它的轻量级、灵活性等诸多优点,使得很多软件都出现了Docker版本。当然,Selenium Grid也不例外。所以,我也会在这里和你简单介绍一下基于Docker的Selenium Grid搭建过程。 + +在这个搭建过程中,你将会发现基于Docker运行Selenium Grid的话,机器的利用率会得到大幅提高。因为,一台实体机或者虚拟机,往往可以运行非常多的Docker实例数量,而且Docker实例的启动速度也很快。因此,相对于虚拟机或者实体机方案而言,Docker方案可以更高效地创建Node。 + +接下来,我们就一起看看如何基于Docker来搭建Selenium Grid吧。 + +在基于Docker搭建Selenium Grid之前,你需要先安装Docker环境。具体安装方法,你可以参考Docker的官方文档。 + +接下来,你就可以通过以下命令分别启动Selenium Hub和Selenium Node了。 + +#创建了Docker的网络grid +$ docker network create grid + +#以Docker容器的方式启动Selenium Hub,并且对外暴露了4444端口 +$ docker run -d -p 4444:4444 --net grid --name selenium-hub selenium/hub:3.14.0-europium + +#以Docker容器的方式启动并挂载了Chrome的Selenium Node +$ docker run -d --net grid -e HUB_HOST=selenium-hub -v /dev/shm:/dev/shm selenium/node-chrome:3.14.0-europium + +#以Docker容器的方式启动并挂载了Firefox的Selenium Node +$ docker run -d --net grid -e HUB_HOST=selenium-hub -v /dev/shm:/dev/shm selenium/node-firefox:3.14.0-europium + + +相比基于实体机或者虚拟机搭建Selenium Grid的方法,基于Docker的方式灵活性更大、启动效率也更高、可维护性也更好。而且,在更高级的应用中,比如当我们需要根据测试用例的排队情况,动态增加Selenium Grid中的Node数量的时候,Docker都将是最好的选择。关于这部分内容具体的细节,我会在后面两篇文章中详细展开。 + +总结 + +今天,我从测试基础架构的概念讲起,并和你分享了传统Selenium Grid 和基于Docker的Selenium Grid的搭建方法。 + +首先,测试基础架构指的是,执行测试的过程中用到的所有基础硬件设施以及相关的软件设施,包括了执行测试的机器、测试用例代码仓库、统一的测试执行平台等。而,今天我针对测试执行的机器这个主题展开了分享。 + +在最早起的测试执行场景中,采用的方法是由人工维护一批数量不多(通常在30台以内)的执行测试的机器,然后按需使用,完成整个测试过程,这也是典型的“小作坊”模式。随着测试需求日益复杂,“小作坊”模式的缺点也暴露无疑,其中最主要的问题在于:测试执行机和测试用例的对应关系不“透明”,以及由此带来的测试用例并发执行难以实施的问题。 + +于是,为了解决这个问题,就出现了Selenium Grid。简单地说,Selenium Grid就是一种可以并发执行GUI测试用例的测试执行机的集群环境。由于它采用的是Hub和Node的架构模式,所以很容易就解决了“小作坊”模式的测试用例与测试执行机间的不“透明”关系,以及测试用例并发执行的问题。 + +而Selenium Grid的搭建也是非常简单。其中,传统Selenium Grid搭建时只要在理解了Selenium Grid架构之后,通过Java命令分别启动Hub和Node即可;而基于Docker的Selenium Grid在搭建时,就更简单了,直接通过Docker命令运行已经封装好的Image就可以了。 + +这么来看,Selenium Grid功能强大,搭建方法更是简单,也因此已经广泛应用于测试执行环境的搭建中。 + +思考题 + +目前Selenium Grid已经有Docker的版本了,你有没有考虑过可以在云端,比如PCF、GCP、AWS上搭建Selenium Grid呢?在我看来,这将是未来的主流方案,你也是类似的看法吗? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/40从小工到专家:聊聊测试执行环境的架构设计(上).md b/专栏/软件测试52讲/40从小工到专家:聊聊测试执行环境的架构设计(上).md new file mode 100644 index 0000000..5473ac6 --- /dev/null +++ b/专栏/软件测试52讲/40从小工到专家:聊聊测试执行环境的架构设计(上).md @@ -0,0 +1,143 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 40 从小工到专家:聊聊测试执行环境的架构设计(上) + 你好,我是茹炳晟,今天我和你分享的主题是“从小工到专家:聊聊测试执行环境的架构设计(上)”。 + +在上一篇文章中,我介绍了Selenium Grid的基础知识,以及如何搭建Selenium Grid。现在,你已经非常清楚,Selenium Grid的作用主要是承担了测试执行机器的角色,被用来执行实际的测试工作。但是,实际工程中的测试执行环境往往更复杂,而测试执行机器也只是其中的一个重要部分。 + +因此,我们还需要控制发起测试的Jenkins,并管理测试用例执行和结果显示的系统。同时,为了更方便地与CI/CD流水线集成,我们还希望不同类型的测试发起过程可以有统一的接口。 + +那么,从今天开始的两篇文章,我将由浅入深地和你聊聊测试执行环境中的基本概念,以及架构设计的思路。 + +什么是测试执行环境? + +测试执行环境的定义有广义和狭义之分: + + +狭义的测试执行环境,单单指测试执行的机器或者集群。比如,我在上一篇文章《从小作坊到工厂:什么是Selenium Grid?如何搭建Selenium Grid?》中介绍的Selenium Grid就是一个最经典的测试执行集群环境。 +广义的测试执行环境,除了包含具体执行测试的测试执行机以外,还包括测试执行的机器或者集群的创建与维护、测试执行集群的容量规划、测试发起的控制、测试用例的组织以及测试用例的版本控制等等。 + + +因此,广义的测试执行环境也被称为测试基础架构。而,我在测试基础架构这个系列里,要和你讨论的是广义上的测试执行环境。也就是说,我会和你重点讨论测试基础架构的概念和设计。 + +如果你是在一些小型的软件公司做测试工程师的话,可能并没有听说过“测试基础架构”这个概念,或者也只是停留在对其的一知半解上。但,实际情况是,无论小型的软件公司还是中大型的软件公司都存在测试基础架构。 + +只是,在小型的软件公司,由于自动化测试的执行量相对较小,测试形式也相对单一,所以测试执行架构非常简单,可能只需要几台固定的专门用于测试执行的机器就可以了。那么,此时测试基础架构的表现形式就是测试执行环境。 + +而对于中大型的软件公司,尤其是大型的全球化电商企业,由于需要执行的自动化测试用例数量非常多,再加上测试本身的多样性需求,测试基础架构的设计是否高效和稳定将直接影响产品是否可以快速迭代、发布上线。因此,中大型的软件公司都会在测试基础架构上有比较大的投入。 + +一般情况下,中大型企业在测试基础架构上的投入,主要是为了解决以下这几方面的问题: + + +简化测试的执行过程。我们不用每次执行测试时,都必须先去准备测试执行机,因为测试执行机的获取就像日常获取水电一样方便了。 +最大化测试执行机器的资源利用率,使得大量的测试执行机可以以服务的形式为公司层面的各个项目团队提供测试执行的能力。 +提供大量测试用例的并发执行能力,使得我们可以在有限的时间内执行更多的测试用例。 +提供测试用例的版本控制机制,使得测试执行的时候可以根据实际被测系统的软件版本自动选择对应的测试用例版本。 +提供友好的用户界面,便于测试的统一管理、执行与结果展示。 +提供了与CI/CD流水线的统一集成机制,从而可以很方便地在CI/CD流水线中发起测试调用。 + + +以此类推,如果你想要设计出高效的测试基础架构,就必须要从以下几个方面着手: + +对使用者而言,测试基础架构的“透明性”。也就是说,测试基础架构的使用者,无需知道测试基础架构的内部设计细节,只要知道如何使用就行。 + +我在上一篇文章中和你探讨的Selenium Grid,就是一个很好的案例。实际使用Selenium Grid时,你只需要知道Hub的地址,以及测试用例对操作系统和浏览器的要求就可以,而无需关注Selenium Grid到底有哪些Node,以及各个Node又是如何维护的技术细节。 + +对维护者而言,测试基础架构的“易维护性”。对于一些大型的测试而言,你需要维护的测试执行机的数量会相当大,比如Selenium的Node的数量达到成百上千台后,如果遇到WebDriver升级、浏览器升级、病毒软件升级的情况时,如何高效地管理数量庞大的测试执行机将会成为一大挑战。 + +所以,早期基于物理机和虚拟机时,这个执行机的管理问题就非常严重。但是,出现了基于Docker的方案后,这些问题都因为Docker容器的技术优势而被轻松解决了。 + +对大量测试用例的执行而言,测试基础架构执行能力的“可扩展性”。这里的可扩展性指的是,测试执行集群的规模可以随着测试用例的数量自动扩容或者收缩。 + +以Selenium Gird为例,可扩展性就是Node的数量和类型,可以根据测试用例的数量和类型进行自动调整。这里,建议你先记住这个概念,我还会在专栏的后续文章中详细展开。 + +随着移动App的普及,测试基础架构中的测试执行机需要支持移动终端和模拟器的测试执行。目前,很多的商业云测平台已经可以支持各种手机终端的测试执行了。其后台实现,基本都是采用的Appium + OpenSTF + Selenium Gird的方案。 + +很多中小企业,因为技术水平以及研发成本的限制,一般直接使用这类商业解决方案。但是,对于大型企业来说,出于安全性和可控制性的考量,一般会选择自己搭建移动测试执行环境。 + +理解了什么是测试执行环境后,我们再一起看看测试基础架构的设计吧。 + +但是,这里我需要说明的是,我并不会以目前业界的最佳实践为例,和你讨论应该如何设计测试基础架构。 + +为什么呢?因为这样做,虽然看似可以简单粗暴地解决实际问题,但是这中间涉及到的琐碎问题,将会淹没测试基础架构设计的主线,反而会让你更加困惑为什么我要这么做,而不能那么做。 + +因此,本着“知其所以然”的原则,我还是会以遇到问题然后解决问题的思路,由浅入深地从最早期的测试基础架构说起,带你一起去经历一次测试基础架构设计思路的演进。在我看来,这样的思路,才是深入理解一门技术的有效途径,也希望你可以借此将测试基础架构的关键问题吃得更透。 + +早期的测试基础架构 + +早期的测试基础架构,会将测试用例存储在代码仓库中,然后是用Jenkins Job来Pull代码并完成测试的发起工作。如图1所示。 + + + +图1 早期的测试基础架构 + +在这种架构下,自动化测试用例的开发和执行流程,是按照以下步骤执行的: + + +自动化测试开发人员在本地机器开发和调试测试用例。这个开发和调试过程,通常是测试开发人员自己的工作电脑上进行。也就是说,他们在开发完测试用例后,会在本机执行测试用例。这些测试用例,会在本机打开指定的浏览器并访问被测网站的URL,然后发起业务操作,完成自动化测试。 + +将开发的测试用例代码,Push到代码仓库。如果自动化测试脚本在测试开发人员本地的电脑上顺利执行完成,那么接下来,我们就会将测试用例的代码Push到代码仓库,至此标志着自动化测试用例的开发工作已经完成。 + +在Jenkins中建立一个Job,用于发起测试的执行。这个Jenkins Job的主要工作是,先从测试用例代码仓库中Pull测试用例代码,并发起构建操作;然后,在远端或者本地固定的测试执行机上发起测试用例的执行。- +这个Jenkins Job通常会将一些会发生变化的参数作为Job自身的输入参数。比如,远端或者本地固定的测试执行机的IP地址或者名字;再比如,被测系统有多套环境,需要指定被测系统的具体名字等。 + + +这种测试架构,对于测试用例数量不多、被测系统软件版本不太复杂的场景的测试需求,基本都可以满足。但在实际使用时,你总会感觉哪里不太方便。 + +比如,每次通过Jenkins Job发起测试时,你都需要填写测试用例需要在哪台测试执行机上执行。而此时,这台测试执行机是否处于可用状态,是否正在被其他测试用例占用都是不可知的,那么你就需要在测试发起前进行人为确认,或者开发一个执行机器环境检查的脚本帮你确认。并且,当远端测试执行机的IP或者名字有变化时,或者当远端测试执行机的数量有变动时,你都需要能提前获知这些信息。 + +所以,这些局限性,也就决定了这种架构只能适用于小型项目。 + +说到这里,你可能已经想到了,不是有Selenium Grid吗?我完全可以用Selenium Gird代替固定的测试执行机。没错,这就是测试基础架构的第一次的重大演进,也因此形成了目前已经被广泛使用的经典测试基础架构。 + +经典的测试基础架构 + +用Selenium Grid代替早期测试基础架构中的“远端或本地固定的测试执行机器”,就形成了经典的测试基础架构。其架构如图2所示。 + + + +图2 经典的测试基础架构 + +这样,你在每次发起测试时,就不再需要指定具体的测试执行机器了,只要提供固定的Selenium Hub地址就行,然后Selenium Hub就会自动帮你选择合适的测试执行机。 + +同时,由于Selenium Grid中Node的数量可以按需添加,所以整体的测试执行任务比较重时,你就可以增加Grid中Node的数量。 + +另外,Selenium还支持测试用例的并发执行,可以有效缩短整体的测试执行时间。 + +所以,这种基于Selenium Grid的经典测试基础架构,已经被大量企业广泛采用。 + +但是,随着测试用例数量的继续增加,传统的Selenium Grid方案在集群扩容、集群Node维护等方面遇到了瓶颈,并且Jenkins Job也因为测试用例的增加变得臃肿不堪。因此,变革经典的测试基础架构的呼声,也越来越高。 + +为此,业界考虑将Selenium Grid迁移到Docker,并且提供便于Jenkins Job管理的统一测试执行平台。这也是我将在下一篇文章中,要和你继续讨论的话题。 + +拭目以待吧。 + +总结 + +从广义上讲,测试执行环境除了包括测试执行机以外,还包括测试执行机的维护、集群的容量规划、测试发起的控制、测试用例的组织以及测试用例的版本控制等等。这也就是我要和你的测试基础架构的定义。 + +从定义上,我们也可以看出在设计一个高效的测试基础架构,应该从这几个方面着手: + + +保证对使用者的“透明性”; +需要具备对维护者而言的“易维护性”; +做到对大量测试用例并发执行的“可扩展性”; +兼顾移动App对测试执行环境的需求。 + + +然后,我以遇到问题然后解决问题的思路,和你分享了早期的测试基础架构向经典的测试基础架构的演进。这个演变可以归纳为,用Selenium Grid代替早期测试基础架构中的“远端或本地固定的测试执行机器”,打破了因为需要人为指定并维护测试执行机,而只能适用于小型测试项目的局限性,从而形成了已被广泛使用的经典测试基础架构。 + +而经典的测试基础架构,在测试用例持续增加时,也会面临诸如集群扩容、Jenkins Job臃肿不堪等等问题,于是基于Docker的测试基础架构便应运而生了。 + +思考题 + +你所在的团队是否在使用Selenium Grid?在实际使用过程中,你是否有遇到过什么瓶颈,又是如何解决的呢? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/41从小工到专家:聊聊测试执行环境的架构设计(下).md b/专栏/软件测试52讲/41从小工到专家:聊聊测试执行环境的架构设计(下).md new file mode 100644 index 0000000..814e962 --- /dev/null +++ b/专栏/软件测试52讲/41从小工到专家:聊聊测试执行环境的架构设计(下).md @@ -0,0 +1,163 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 41 从小工到专家:聊聊测试执行环境的架构设计(下) + 你好,我是茹炳晟,今天我和你分享的主题是“从小工到专家:聊聊测试执行环境的架构设计(下)”。 + +在上一篇文章中,我介绍了测试基础架构的概念,以及早期的和经典的两种测试基础架构。在文章的最后,我提到经典的测试基础架构中采用的Selenium Grid方案,在测试用例的数量持续增加的情况下,会带来集群扩容、Jenkins Job臃肿不堪等诸多问题,因此我们考虑将Selenium Grid迁移到Docker,并且提供便于Jenkins Job管理的统一测试执行平台。 + +所以,今天的这篇文章,我就会围绕这些瓶颈以及对应的解决方案来展开。 + +基于Docker实现的Selenium Grid测试基础架构 + +随着测试基础架构的广泛使用,以及大量的浏览器兼容性测试的需求,Selenium Grid中Node的数量会变得越来越大,也就是说我们需要维护的Selenium Node会越来越多。 + +在Node数量只有几十台的时候,通过人工的方式去升级WebDriver、更新杀毒软件、升级浏览器版本,可能还不是什么大问题。但是,当需要维护的Node数量达到几百台甚至几千台的时候,这些Node的维护工作量就会直线上升。虽然,你可以通过传统的运维脚本管理这些Node,但维护的成本依然居高不下。 + +同时,随着测试用例数量的持续增长,Selenium Node的数量也必然会不断增长,这时安装部署新Node的工作量也会难以想象。因为,每台Node无论是采用实体机还是虚拟机,都会牵涉到安装操作系统、浏览器、Java环境,以及Selenium。 + +而目前流行的Docker容器技术,由于具有更快速的交付和部署能力、更高效的资源利用,以及更简单的更新维护能力,也就使得Docker相比于传统虚拟机而言,更加得“轻量级”。 + +因此,为了降低Selenium Node的维护成本,我们自然而然地想到了目前主流的容器技术,也就是使用Docker代替原本的虚拟机方案。 + +基于Docker的Selenium Grid,可以从三个方面降低我们维护成本: + + +由于Docker的更新维护更简单,使得我们只要维护不同浏览器的不同镜像文件即可,而无需为每台机器安装或者升级各种软件; + +Docker轻量级的特点,使得Node的启动和挂载所需时间大幅减少,直接由原来的分钟级降到了秒级; + +Docker高效的资源利用,使得同样的硬件资源可以支持更多的Node。也就是说,我们可以在不额外投入硬件资源的情况下,扩大Selenium Grid的并发执行能力。 + + +因此,现在很多大型互联网企业的测试执行环境都在向Docker过渡。 + +而具体如何基于Docker搭建一套Selenium Grid,你可以参考我在第39篇文章《从小作坊到工厂:什么是Selenium Grid?如何搭建Selenium Grid?》中介绍的方法。由此可见,将原本基于实体机或者虚拟机实现的Selenium Grid改进成为基于Docker实现的过程也很简单、灵活。 + +如图1所示,就是一个基于Docker实现的Selenium Grid的测试基础架构。 + + + +图1 基于Docker实现的Selenium Grid测试基础架构 + +引入统一测试执行平台的测试基础架构 + +在实际的使用过程中,基于Docker的Selenium Grid使得测试基础架构的并发测试能力不断增强,也因此会有大量项目的大量测试用例会运行在这样的测试基础架构之上。 + +当项目数量不多,我们可以直接通过手工配置Jenkins Job,并直接使用这些Job控制测试的发起和执行。但是,当项目数量非常多之后,测试用例的数量也会非常多,这时新的问题又来了: + + +管理和配置这些Jenkins Job的工作量会被不断放大; + +这些Jenkins Job的命名规范、配置规范等也很难实现统一管理,从而导致Jenkins中出现了大量重复和不规范的Job; + +当需要发起测试,或者新建某些测试用例时,都要直接操作Jenkins Job。而这个过程,对于不了解这些Jenkins Job细节的人(比如,新员工、项目经理、产品经理)来说,这种偏技术型的界面体验就相当不友好了。 + + +为此,我们为了管理和执行这些发起测试的Jenkins Job实现了一个GUI界面系统。在这个系统中,我们可以基于通俗易懂的界面操作,完成Jenkins Job的创建、修改和调用,并且可以管理Jenkins Job的执行日志以及测试报告。 + +这,其实就是统一测试执行平台的雏形了。 + +有了这个测试执行平台的雏形后,我们逐渐发现可以在这个平台上做更多的功能扩展,于是这个平台就逐渐演变成了测试执行的统一入口。 + +在这里,我列举了这个平台两个最主要的功能和创新设计,希望可以给你以及你所在公司的测试基础架构建设带来一些启发性的思考。 + +第一,测试用例的版本化管理。我们都知道,应用的开发有版本控制机制,即:每次提测、发布都有对应的版本号。所以,为了使测试用例同样可追溯,也就是希望不同版本的开发代码都能有与之对应的测试用例,很多大型企业或者大型项目都会引入测试用例的版本化管理。最简单直接的做法就是,采用和开发一致的版本号。 + +比如,被测应用的版本是1.0.1,那么测试用例的版本也命名为1.0.1。在这种情况下,当被测应用版本升级到1.0.2的时候,我们会直接生成一个1.0.2版本的测试用例,而不应该直接修改1.0.1版本的测试用例。 + +这样,当被测环境部署的应用版本是1.0.1的时候,我们就选择1.0.1版本的测试用例;而当被测环境部署的应用版本是1.0.2的时候,我们就相应地选择1.0.2版本的测试用例。 + +所以,我们就在这个统一的测试执行平台中,引入了这种形式的测试用例版本控制机制,直接根据被测应用的版本自动选择对应的测试用例版本。 + +第二,提供基于Restful API的测试执行接口供CI/CD使用。这样做的原因是,测试执行平台的用户不仅仅是测试工程师以及相关的产品经理、项目经理,很多时候CI/CD流水线才是主力用户。因为,在CI/CD流水线中,每个阶段都会有不同的发起测试执行的需求。 + +我们将测试基础架构与CI/CD流水线集成的早期实现方案是,直接在CI/CD流水线的脚本中硬编码发起测试的命令行。这种方式最大的缺点在于灵活性差: + + +当硬编码的命令行发生变化,或者引入了新的命令行参数的时候,CI/CD流水线的脚本也要一起跟着修改; +当引入了新的测试框架时,发起测试的命令行也是全新的,那么CI/CD流水线的脚本也必须被一起改动。 + + +因此,为了解决耦合性的问题,我们在这个统一的测试执行平台上,提供了基于Restful API的测试执行接口。任何时候你都可以通过一个标准的Restful API发起测试,CI/CD流水线的脚本也无须再知道发起测试的命令行的具体细节了,只要调用统一的Restful API即可。 + +如图2所示,就是引入了统一测试执行平台的测试基础架构。 + + + +图2 引入统一测试执行平台的测试基础架构 + +基于Jenkins集群的测试基础架构 + +这个引入了统一测试执行平台的测试基础架构,看似已经很完美了。但是,随着测试需求的继续增长,又涌现出了新的问题:单个Jenkins成了整个测试基础架构的瓶颈节点。因为,来自于统一测试执行平台的大量测试请求,会在Jenkins上排队等待执行,而后端真正执行测试用例的Selenium Grid中很多Node处于空闲状态。 + +为此,将测试基础架构中的单个Jenkins扩展为Jenkins集群的方案就势在必行了。如图3所示,就是基于Jenkins集群的测试基础架构。 + + + +图3 基于Jenkins集群的的测试基础架构 + +因为Jenkins集群中包含了多个可以一起工作的Jenkins Slave,所以大量测试请求排队的现象就再也不会出现了。 + +而这个升级到Jenkins集群的过程中,对于Jenkins集群中Slave的数量到底多少才合适并没有定论。一般的做法是,根据测试高峰时段Jenkins中的排队数量来预估一个值。通常最开始的时候,我们会使用4个Slave节点,然后观察高峰时段的排队情况,如果还是有大量排队,就继续增加Slave节点。 + +测试负载自适应的测试基础架构 + +引入了Jenkins集群后,整个测试基础架构已经很成熟了,基本上可以满足绝大多数的测试场景了。但是,还有一个问题一直没有得到解决,那就是:Selenium Grid中Node的数量到底多少才合适? + + +如果Node数量少了,那么当集中发起测试的时候,就会由于Node不够用而造成测试用例的排队等待,这种场景在互联网企业中很常见; +而如果Node数量多了,虽然可以解决测试高峰时段的性能瓶颈问题,但是又会造成空闲时段的计算资源浪费问题。当测试基础架构搭建在按使用付费的云端时,计算资源的浪费就是资金浪费了。 + + +为了解决这种测试负载不均衡的问题,Selenium Grid的自动扩容和收缩技术就应运而生了。 + +Selenium Grid的自动扩容和收缩技术的核心思想是,通过单位时间内的测试用例数量,以及期望执行完所有测试的时间,来动态计算得到所需的Node类型和数量,然后再基于Docker容器快速添加新的Node到Selenium Grid中;而空闲时段则去监控哪些Node在指定时间长短内没有被使用,并动态地回收这些Node以释放系统资源。 + +通常情况下,几百乃至上千台Node的扩容都可以在几分钟内完成,Node的销毁与回收的速度同样非常快。 + +至此,测试基础架构已经演变得很先进了,基本可以满足大型电商的测试执行需求了。测试负载自适应的测试基础架构,具体如图4所示。 + + + +图4 测试负载自适应的测试基础架构 + +如何选择适合自己的测试基础架构? + +现在,我已经介绍完了测试基础架构的演进,以及其中各阶段主要的架构设计思路,那么对于企业来说,应当如何选择最适合自己的测试基础架构呢? + +其实,对于测试基础架构的建设,我们切忌不要为了追求新技术而使用新技术,而是应该根据企业目前在测试执行环境上的痛点,来有针对性地选择与定制测试基础架构。 + +比如,你所在的企业如果规模不是很大,测试用例执行的总数量相对较少,而且短期内也不会有大变化的情况,那么你的测试基础架构完全就可以采用经典的测试基础架构,而没必要引入Docker和动态扩容等技术。 + +再比如,如果是大型企业,测试用例数量庞大,同时还会存在发布时段大量测试请求集中到来的情况,那么此时就不得不采用Selenium Gird动态扩容的架构了。而一旦要使用动态扩容,那么势必你的Node就必须做到Docker容器化,否则无法完全发挥自动扩容的优势。 + +所以说,采用什么样的测试基础架构不是由技术本身决定的,而是由测试需求推动的。 + +总结 + +在今天这篇文章中,我从测试基础架构演进的视角,和你分享了测试基础架构发展的前世今生。 + +首先,为了降低测试用例过多时Selenium Grid的维护成本,我们用Docker容器代替了经典测试基础架构中的实体机/虚拟机,形成了基于Docker实现的Selenium Grid测试基础架构。 + +而后,我们发现测试用例的数量达到一定规模后,管理和执行发起测试的Jenkins Job成了问题。于是,我们引入了一个基于GUI界面的测试执行平台,并在其上扩展了诸如测试用例版本化、提供基于Restful API的测试执行接口等功能。从而,形成了由这个统一测试执行平台发起测试的测试基础架构形态。 + +而为了进一步解决由单个Jenkins带来的系统瓶颈问题,我们过渡到了基于Jenkins集群的测试基础架构,通过多个同时工作的Jenkins Slave,解决了大量测试请求排队的问题。 + +随后,为了解决Selenium Grid中Node的数量到底多少才合适的问题,我和你谈论了创新设计的Selenium Grid的自动扩容和收缩技术。至此,测试负载自适应的测试基础架构也终于“千呼万唤始出来”了。 + +最后,我谈论了不同企业该如何选择最适合自己的测试基础架构的问题。这里,我强调了一定要根据测试需求选择测试基础架构,而不能一味地追求最新的技术。 + +我希望通过这种授人以渔的方式,可以帮你拓宽思路,并将测试基础架构的设计思路、思想,运用到你的实际工作中去。 + +思考题 + +其实,在我讲述的这个测试基础架构的设计和搭建过程中,还有很多可以优化和创新的点。你觉得哪些部分可以再优化呢?你还有哪些想法呢? + +欢迎你给我留言,我们一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/42实战:大型全球化电商的测试基础架构设计.md b/专栏/软件测试52讲/42实战:大型全球化电商的测试基础架构设计.md new file mode 100644 index 0000000..c8ed88d --- /dev/null +++ b/专栏/软件测试52讲/42实战:大型全球化电商的测试基础架构设计.md @@ -0,0 +1,162 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 42 实战:大型全球化电商的测试基础架构设计 + 你好,我是茹炳晟。今天我和你分享的主题是“实战:大型全球化电商的测试基础架构设计”。 + +在前面的两篇文章中,我和你分享了测试基础架构的设计以及演进之路,其中涉及到了统一测试执行平台、Selenium Grid和Jenkins等一系列的概念。 + +在掌握了这些基础内容之后,今天我就和你一起看看大型全球化电商的测试基础架构又是如何设计的。这其中除了我之前介绍过的概念以外,还会引入一些新的服务和理念,我都会和你一一道来。 + +因为我们已经掌握了测试基础架构设计的基础知识,所以今天我会采用一种不同于以往由浅入深的方式,直接给出大型全球化电商网站的全局测试基础架构的最佳实践,然后再依次解释各个模块的主要功能以及实现基本原理。 + +其实,大型全球化电商网站全局测试基础架构的设计思路,可以总结为“测试服务化”。也就是说,测试过程中需要用的任何功能都通过服务的形式提供,每类服务完成一类特定功能,这些服务可以采用最适合自己的技术栈,独立开发,独立部署。而至于到底需要哪些测试服务,则是在理解了测试基础架构的内涵后再高度抽象后得到的。从本质上来看,这种设计思想其实和微服务不谋而合。 + +根据在大型全球化电商网站工作的实际经验,我把一个理想中的测试基础架构概括为了一张图(如图1所示)。 + + + +图1 大型全球化电商网站的全局测试基础架构设计 + +这个理想的测试基础架构,包括了6种不同的测试服务,分别是:统一测试执行服务、统一测试数据服务、全局测试配置服务、测试报告服务、测试执行环境准备服务,以及被测系统部署服务。 + +接下来,我们一起看看这6大测试服务,具体是什么,以及如何实现。 + +统一测试执行服务 + +从本质上看,统一测试执行服务,其实和统一测试执行平台(你可以再回顾一下第41篇文章《从小工到专家:聊聊测试执行环境的架构设计(下)》)是一个概念。只不过,统一测试执行服务,强调的是服务,也就是强调测试执行的发起是通过Restful API调用完成的。 + +总结来说,以Restful API的形式对外提供测试执行服务的方式,兼具了测试版本管理、Jenkins测试Job管理,以及测试执行结果管理的能力。 + +统一测试执行服务的主要原理是,通过Spring Boot框架提供Restful API,内部实现是通过调度Jenkins Job具体发起测试。如果你对此还有疑惑,请参考第40篇文章《从小工到专家:聊聊测试执行环境的架构设计(上)》。 + +还记得我在前面一直提到的将测试发起与CI/CD流水线集成吗?这个统一测试执行服务采用的Restful API调用,主要用户就是CI/CD流水线脚本。我们可以在这些脚本中,通过统一的Restful API接口发起测试。 + +统一测试数据服务 + +统一测试数据服务,其实就是统一测试数据平台(你也可以再回顾一下第37篇《测试数据的“银弹”- 统一测试数据平台(上)》和第38篇《测试数据的“银弹”- 统一测试数据平台(下)》文章的内容)。 + +任何测试,但凡需要准备测试数据的,都可以通过Restful API调用统一测试数据服务,然后由它在被测系统中实际创建或者搜索符合要求的测试数据。而具体的测试数据创建或者搜索的细节,对于测试数据的使用者来说,是不需要知道的。也就是说,统一测试数据服务,会帮助我们隐藏测试数据准备的所有相关细节。 + +同时,在统一测试数据服务内部,通常会引入自己的内部数据库管理测试元数据,并提供诸如有效测试数据数量自动补全、测试数据质量监控等高级功能。 + +在实际工程项目中,测试数据的创建通常都是通过调用测试数据准备函数完成的。而这些函数内部,主要通过API和数据库操作相结合的方式,实际创建测试数据。 + +如果你对测试数据的准备还有疑问,或者想知道更多的细节内容,可以再回顾一下前面“测试数据准备”的系列的第35~38篇文章。 + +测试执行环境准备服务 + +测试执行环境准备服务,其实我也已经介绍过了。这里“测试执行环境”,是狭义的概念,特指具体执行测试的测试执行机器集群:对于GUI自动化测试来说,指的就是Selenium Grid;对于API测试来说,指的就是实际发起API调用的测试执行机器集群。 + +测试执行环境准备服务的使用方式,一般有两种: + + +一种是,由统一测试执行服务根据测试负载情况,主动调用测试执行环境准备服务来完成测试执行机的准备,比如启动并挂载更多的Node到Selenium Grid中; +另一种是,测试执行环境准备服务不直接和统一测试执行服务打交道,而是由它自己根据测试负载来动态计算测试集群的规模,并完成测试执行集群的扩容与收缩。 + + +被测系统部署服务 + +被测系统部署服务,主要被用来安装部署被测系统和软件。虽然这部分内容我以前没有提到过,但它很好理解。其实现原理是,调用DevOps团队的软件安装和部署脚本。 + + +对于那些可以直接用命名行安装和部署的软件来说很简单,一般只需要把人工安装步骤的命名行组织成脚本文件,并加入必要的日志输出和错误处理即可。 +对于那些通过图形界面安装的软件,一般需要找出静默(Silent)模式的安装方式,然后通过命令行安装。 + + +如果被测软件安装包本身不支持静默安装模式,我强烈建议给发布工程师提需求,要求他加入对静默安装模式的支持。其实,一般的打包工具都能很方能地支持Silent安装模式,并不会增加额外的工作量。 + +被测系统部署服务,一般由CI/CD流水线脚本来调用。在没有被测系统部署服务之前,CI/CD流水线脚本中一般会直接调用软件安装和部署脚本。而在引入了被测系统部署服务后,我们就可以在CI/CD流水线脚本中直接以Restful API的形式调用标准化的被测系统部署服务了。这样做的好处是,可以实现CI/CD流水线脚本和具体的安装部署脚本解耦。 + +测试报告服务 + +测试报告服务,也是测试基础架构的重要组成部分,其主要作用是为测试提供详细的报告。 + +测试报告服务的实现原理,和传统测试报告的区别较大。 + +传统的软件测试报告,通常直接由测试框架产生,比如TestNG执行完成后的测试报告,以及HttpRunner执行结束后的测试报告等等,也就是说测试报告和测试框架绑定在了一起。 + +对于大型电商网站而言,由于各个阶段都会有不同类型的测试,所以测试框架本身就具有多样性,因此对应的测试报告也是多种多样。而测试报告服务的设计初衷,就是希望可以统一管理这些格式各异、形式多样的测试报告,同时希望可以从这些测试报告中提炼出面向管理层的统计数据。 + +为此,测试报告服务的实现中引入了一个NoSQL数据库,用于存储结构各异的测试报告元数据。在实际项目中,我们会改造每个需要使用测试报告服务的测试框架,使其在完成测执行后将测试报告的元数据存入到测试报告服务的NoSQL数据库。这样,我们再需要访问测试报告的时候,就可以直接从测试报告服务中提取了。 + +同时,由于各种测试报告的元数据都存在了这个NoSQL数据库中,所以我们就可以开发一些用于分析统计的SQL脚本,帮助我们获得质量相关信息的统计数据。 + +测试报告服务的主要使用者是测试工程师和统一测试执行服务。对统一测试执行服务来说,它会调用测试报告服务获取测试报告,并将其与测试执行记录绑定,然后进行显示。而测试工程师则可以通过测试报告服务这个单一的入口,来获取想要的测试报告。 + +全局测试配置服务 + +全局测试配置服务是这6个服务中最难理解的部分,其本质是要解决测试配置和测试代码的耦合问题。这个概念有点抽象,我们一起看个实例吧。 + +大型全球化的电商网站在全球很多国家都有站点,这些站点的基本功能是相同的,只是某些小的功能点会有地域差异(比如,因当地法务、政策等不同而引起的差异;又比如,由货币符号、时间格式等导致的细微差异)。 + +假设,我们在测试过程中,需要设计一个getCurrencyCode函数来获取货币符号,那么这个函数中就势必会有很多if-else语句,以根据不同国家返回不同的货币符号。 + +比如,如图2所示的“Before”代码中,就有4个条件分支,如果当前国家是德国(isDESite)或者法国(isFRSite),那么货币符号就应该是“EUR”; 如果当前国家是英国(isUKSite),那么货币符号就应该是“GBP”;如果当前国家是美国(isUSSite)或者是墨西哥(isMXSite),那么货币符号就应该是“USD”;如果当前国家不在上述的范围,那么就抛出异常。 + + + +图2 全局测试配置服务的原理示例 + +上述函数的逻辑实现本身并没有问题,但是当你需要添加新的国家和新的货币符号时,就需要添加更多的if-else分支,当国家数量较多的时候,代码的分支也会很多。更糟糕的是,当添加新的国家时,你会发现有很多地方的代码都要加入分支处理,十分不方便。 + +那么,有什么好的办法,可以做到在添加新的国家支持时,不用改动代码吗? + +其实,仔细想来,之所以要处理这么多分支,无非是因为不同的国家需要不同的配置值(这个实例中,不同国家需要的不同配置值就是货币符号),那如果我们可以把配置值从代码中抽离出去放到单独的配置文件中,然后代码通过读取配置文件的方式来动态获取配置值,这样就可以做到加入新的国家时,不用再修改代码本身,而只要加入一份新国家的配置文件就可以了。 + +为此,我们就有了如图2所示的“After”代码以及图中右上角的配置文件。“After”代码的实现逻辑是:通过GlobalRegistry并结合当前环境的国家信息来读取对应国家配置文件中的值。比如,GlobalEnvironment.getCountry()的返回值是“US”,也就是说当前环境的国家是美国,那么GlobalRegistry就会去“US”的配置文件中读取配置值。 + +这样实现的好处是,假定某天我们需要增加日本的时候,getCurrencyCode函数本身不用做任何修改,而只需要增加一个“日本”的配置文件即可。 + +至此,我们已经一起了解了大型全球化电商网站的全局测试基础架构设计,以及其中的6个主要测试服务的作用及其实现思路。现在,我再和你分享一个实例,看看这样的测试基础架构是如何工作的,帮助你进一步理解测试基础架构的本质。 + +大型全球化电商网站测试基础架构的使用实例 + +这个实例,我会以CI/CD作为整个流程的起点。因为,在实际工程项目中,自动化测试的发起与执行请求一般都是来自于CI/CD流水线脚本。 + +首先,CI/CD流水线脚本会以异步或者同步的方式调用被测系统部署服务,安装部署被测软件的正确版本。这里,被测系统部署服务会访问对应软件安装包的存储位置,并将安装包下载到被测环境中,然后调用对应的部署脚本完成被测软件的安装。之后,CI/CD脚本中会启动被测软件,并验证新安装的软件是否可以正常启动,如果这些都没问题的话,被测系统部署服务就完成了任务。 + +这里需要注意的是: + + +如果之前的CI/CD脚本是以同步方式调用的被测系统部署服务,那么只有当部署、启动和验证全部通过后,被测系统部署服务才会返回,然后CI/CD脚本才能继续执行; +如果之前的CI/CD脚本是以异步方式调用的被测系统部署服务,那么被测系统部署服务会立即返回,然后等部署、启动和验证全部通过后,才会以回调的形式通知CI/CD脚本。因此,CI/CD脚本也要为此做特殊处理。 + + +被测系统部署完成后,CI/CD脚本就会调用统一测试执行服务。统一测试执行服务会根据之前部署的被测软件版本选择对应的测试用例版本,然后从代码仓库中下载测试用例的Jar包。 + +接下来,统一测试执行服务会将测试用例的数量、浏览器的要求,以及需要执行完成的时间作为参数,调用测试执行环境准备服务。 + +测试执行环境准备服务会根据传过来的参数,动态计算所需的Node类型和数量,然后根据计算结果动态加载更多的基于Docker的Selenium Node到测试执行集群中。此时,动态Node加载是基于轻量级的Docker技术实现的,所以Node的启动与挂载速度都非常快。 + +因此,统一测试执行服务通常以同步的方式调用测试执行环境准备服务。 + +测试执行环境准备好之后,统一测试执行服务就会通过Jenkins Job发起测试的执行。测试用例执行过程中,会依赖统一测试数据服务来准备测试需要用到的数据,并通过全局测试配置服务获取测试相关的配置与参数。 + +同时,在测试执行结束后,还会自动将测试报告以及测试报告的元数据发送给测试报告服务进行统一管理。 + +以上就是这套测试基础架构的执行过程了。 + +总结 + +通过前面几篇文章,我们已经掌握了测试基础架构的基础知识,所以今天我分享的主题就是,从实战的角度帮你夯实测试基础架构的基础。 + +其实,大型全球化电商网站全局测试基础架构的设计思路,可以总结为“测试服务化”。于是,我总结了一个比较理想的测试基础架构,应该包括6大服务:统一测试执行服务、统一测试数据服务、全局测试配置服务、测试报告服务、测试执行环境准备服务,以及被测系统部署服务。 + +其中,统一测试执行服务,本质上讲就是统一测试执行平台;统一测试数据服务,其实就是统一测试数据平台;测试执行环境准备服务,指的是狭义的测试执行环境准备。这几部分内容,我都已经在前面的文章中分享过了,如果你有任何问题,也可以再给我留言一起讨论。 + +而被测系统部署服务,主要是被用来安装部署被测系统和软件,这部分也很简单;测试报告服务,虽然和传统的测试报告区别较大,但也可以通过引入一个NoSQL数据库,以存储的测试报告元数据的方式去实现。 + +全局测试配置服务是这6个服务中最难理解的部分,其本质是要解决测试配置和测试代码的耦合问题。我通过一个具体的不同国家对应不同货币符号的例子,和你讲述了具体如何解耦。 + +思考题 + +除了我今天分享的6大服务以外,其实还有更多的服务可以帮助我们提升测试效能,比如全局Mock服务、工程效能工具链仓库等等。你还能想到有哪些与测试相关的服务吗? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/43发挥人的潜能:探索式测试.md b/专栏/软件测试52讲/43发挥人的潜能:探索式测试.md new file mode 100644 index 0000000..c0e3a43 --- /dev/null +++ b/专栏/软件测试52讲/43发挥人的潜能:探索式测试.md @@ -0,0 +1,116 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 43 发挥人的潜能:探索式测试 + 你好,我是茹炳晟。今天我和你分享的主题是:发挥人的潜能之探索式测试。 + +从今天开始,我们又要一起进入一个新的系列了:测试新技术系列。在这个系列里,我将通过5篇文章,和你分享软件测试领域中比较新的5个测试主题:探索性测试、测试驱动开发(TDD)、精准测试、渗透测试,以及基于模型的测试。这五种新的测试技术,是我精挑细选的,初衷就是希望帮你拓宽知识面、思路。 + +今天这次的分享,我们就先从当下很热门的探索式测试开始吧。此时,你可能已经听说过了探索式测试,也很可能还不知道什么是探索式测试。这一切都没有关系,相信你经过我今天的这次分析,总能汲取到新的知识,对探索式测试有一个全面、清晰的认识。 + +软件测试与招聘面试类比 + +在正式开始介绍探索式测试之前,我们先一起看一个工作中招聘面试的例子吧。 + +假设,你是面试官,现在有一个候选人要应聘你负责的这个职位。那么,你通常都会在正式面试前,先仔细了解候选人的简历,然后根据简历情况以及这个职位的要求,设计一些高质量的面试问题。当然了,你这么做的目的是,试图通过这些面试问题判断候选人与这个职位的匹配程度。 + +但是,在实际面试的时候,你提出面试问题之后,通常会根据面试者的回答调整接下来的问题: + + +如果候选人的回答符合你的预期,你可能就会结束这个话题,然后开始一个新的话题去考察候选人其他方面的能力; +如果你对候选人的回答有疑问,你很可能就会顺着候选人的回答进行有针对性的提问,这时你提出的新问题完全可能是即兴发挥的,而不是事先准备好的。 + + +事实上,你在面试候选人的过程中,其实同时在开展候选人评估、面试问题设计、面试执行和面试问题回答评估。可以说,这个面试过程其实就是你在“探索”你的候选人,通过“探索”去判断候选人是否符合你的要求。 + +那么对于软件测试来说,也有非常类似的过程。 + +比如,你首先根据软件功能描述来设计最初的测试用例,然后执行测试;测试执行后,可能你得到的输出和预期输出不完全一致,于是你会猜测这种不一致是否可能是软件的缺陷造成的;为了验证你的想法,你会根据错误输出设计新的测试用例,然后采用不同的输入再次检查软件的输出。 + +在一次测试中,你可能会经过几轮这样的猜测和验证,进行反复“探索”,最终确定了一个软件的缺陷。而这个过程中,你会发现,识别缺陷的思路和测试用例的设计,并没有出现在最初的测试设计和测试用例文档中,而是以很快的速度在你的脑海中以及实际测试执行和验证中快速迭代。 + +从本质上来看,上述的两个过程就是探索式测试最基本的思维模型了。所以说,探索式测试本身并不是一种测试技术,而是一种软件测试风格。这个测试风格,强调测试工程师要同时开展测试学习、测试设计、测试执行和测试结果评估等一系列的活动,以持续优化测试工作。 + +其实,在目前的工程实践中,探索式测试发现的缺陷最多,而且发现的缺陷也很有代表性。所以,现在很多企业在探索式测试中都有较大投入。 + +那么,我现在就和你分享一下什么是探索式测试吧。 + +什么是探索式测试? + +探索式测试,最早是由测试专家Cem Kaner博士在1983年提出的,并受到当时语境驱动的软件测试学派的支持。后来,Cem Kaner博士在佛罗里达工学院的同事James A. Whittaker,凭借着在微软和谷歌担任测试架构师和测试总监的经验积累,撰写了最早的探索式测试书籍(Exploratory Software Testing),扩展了探索式测试的概念和方法。 + +从本质上来看,探索式测试具有即兴发挥、快速实验、随时调整等特征, Kaner博士在2006年1月的Exploratory Testing Research Summit会议上将探索式测试的定义总结为以下的一句话(在这里,我直接引用了原文)。接下来,我就和你一起解读一下其中最关键的几个概念吧。 + + +Exploratory software testing is a style of software testing that emphasizes the personal freedom and responsibility of the individual tester to continually optimize the value of her work by treating test-related learning, test design, test execution, and test result interpretation as mutually supportive activities that run in parallel throughout the project. + + +首先,探索式测试是一种软件测试风格,而不是一种具体的软件测试技术。作为一种思维方法,探索式测试强调依据当前语境与上下文选择最合适的测试技术。所以,切记不要将探索式测试误认为是一种测试技术,而应该理解为一种利用各种测试技术“探索”软件潜在缺陷的测试风格。 + +其次,探索式测试强调独立测试工程师的个人自由和责任,其目的是为了持续优化其工作的价值。测试工程师应该为软件产品负责,充分发挥主观能动性,在整体上持续优化个人和团队的产出。这种思想方法,与精益生产、敏捷软件开发的理念高度一致,这也正是探索式测试受到敏捷团队欢迎的原因之一。 + +这里需要特别指出的是,探索式测试对个人的能力有很高的依赖:同样的测试风格,由不同的人来具体执行,得到的结果可能会差别巨大。因此,对执行探索式测试的工程师的要求就会比较高,除了要能够从业务上深入理解被测系统外,还要有很强的逻辑分析与推理能力,当然对测试技术以及测试用例设计的融会贯通也是必不可少的技能。 + +最后,探索式测试建议在整个项目过程中,将测试相关学习、测试设计、测试执行和测试结果解读作为相互支持的活动,并行执行。 + +注意,这里的并行(run in parallel)并不是真正意义上的并行,而是指对测试学习、测试设计、测试执行和测试分析的快速迭代,即在较短的时间内(比如,1个小时或者30分钟内)快速完成多次循环,以此来不断收集反馈、调整测试、优化价值。这样,在外部看来,就会感觉这些活动是在并行地执行。 + +这样的理念与敏捷开发中的“小步快跑”、持续反馈的理念不谋而合,因此几乎所有的敏捷团队都会或多或少地应用探索式测试。而且,通常来讲,在敏捷开发的每个迭代中,新开发的功能基本都要依靠探索式测试保证产品的质量。 + +说到这里,你可能很容易陷入一个误区,那就是探索式测试看起来并没有书面严格意义上的测试设计文档,而且很多测试是在测试执行过程中即兴产生并执行的,那这样的测试风格是不是可以和即兴测试相提并论,两者又有什么区别和联系呢? + +探索式测试与即兴测试的区别和联系 + +虽说探索式测试与即兴测试(Ad-hoc Testing)的风格看起来类似,都是依靠测试工程师的经验和直觉来即兴发挥,快速地试验被测试应用,并不停地调整测试策略。但是,探索式测试相比即兴测试更强调及时“反馈”的重要性。 + +在探索式测试中,测试工程师不断提出假设,通过测试执行去检验假设,通过解读测试结果证实或推翻假设。在这个迭代过程中,测试工程师不断完善头脑中被测试应用的知识体系,并建立被测应用的模型,然后利用模型、过往经验,以及测试技术驱动进一步的测试。 + +相比即兴测试完全不注重测试计划和设计,探索式测试要不停地优化测试模型和测试设计。由于测试设计和测试执行的切换速度很快,也就是说切换频率很高,所以会很容易传达“探索式测试没有测试计划和设计”这个错误的信息。然而,实际情况是,探索式测试有明确的测试目标和测试设计,只是测试设计的时间很短,会以很高的频率与测试执行交替切换。 + +掌握了探索式测试的基本理念之后,接下来我们再简单看一下探索式测试具体是如何实施的。 + +如何开展探索性测试? + +探索式测试也是可以采用分层测试的策略。 + +通常,我们首先会对软件的单一功能进行比较细致的探索式测试。“探索”的过程主要是基于功能需求以及非功能性需求进行扩展和延伸,期间可以采用类似“头脑风暴”的工具,比如Xmind等,帮助我们整理思路。 + +比如,软件系统的用户登录功能就是一个单一的功能,所以作为探索式测试人员,首先应该站在最终用户的角度去理解和使用登录功能。也就是说,探索式测试人员需要了解真正的业务需求,然后基于这些业务需求“探索”软件的功能是否可以满足业务需求。 + +为此,探索式测试人员需要分析出用户登录功能的所有原子输入项。这里为了简化,假定原子输入项只有用户名、密码和登录按钮。接着,组合这些原子输入项构成最基本典型的测试场景。至于什么才是最基本典型的场景,则取决于探索式测试人员对需求的理解程度。 + +比如,用真实合法的用户名以及密码完成登录就是一个非常基本典型的场景,如果该场景能够成功登录,就可以切换到下一个;如果该场景不能够成功登录,就需要去“探索”为什么没能登录成功,比如你可能会怀疑是否是因为用户名或者密码是区分大小写的,又或者是不是因为你多次错误的尝试而导致的。 + +基于你的怀疑,进一步去设计新的测试用例来验证你的猜测。如果你怀疑是用户名或者密码大小写不一致造成的登录失败,那么就需要尝试使用大小写完全一致的用户名和密码进行尝试,之后还应该故意设计一些测试用例采用相同字符但是不同大小写的用户名和密码去做尝试;如果你怀疑登录失败是由于过多次的失败登录引起的,你就应该故意去设计这样的场景来观察系统的实际行为是否符合你的猜想。 + +总之,通过以上这样的“探索”过程,你就将测试学习、测试设计、测试执行和测试结果评估串联成了一个快速迭代的过程,并在你脑海中快速建立了登录功能的详细模型。 + +然后,我们往往会开展系统交互的探索式测试,这个过程通常会采用基于反馈的探索式测试方法。基于反馈的探索式测试方法,会运用所有可用的测试技术,以及基于对产品深入理解后的技术直觉,并结合上一次测试结果的反馈与分析结果,指导测试工程师下一步的测试行动。 + +还是以用户登录功能为例,在系统交互的探索式测试中,你就不仅要考虑单一的登录功能了,而是要考虑用户登录与系统其他功能相结合的场景。 + +比如,你可以尝试不登录直接访问登录后的路径去观察系统的行为;再比如,你可以尝试不登录就去查看订单状态的操作等等。这些组合场景的设计主要取决于你想要验证,或者说想要“探索”的系统功能。很多时候这些灵感来自于你之前对系统的探索而取得的系统认识,同时你的技术直觉也在此扮演了重要角色。 + +最后,我要特别强调一下:其实,很多时候你已经在不知不觉中运用了探索式测试的思想方法,只是你自己并不知道这就是探索式测试而已。所以,探索式测试离你并不遥远,而且实现起来也没想象中的那么难。但是,探索式测试是否可以帮你找出尽可能多的缺陷,还是取决你对系统需求的理解以及根据过往经验而积累的技术直觉。而探索式测试,只是一个测试风格,或者说一个流程方法,所以其本身并不具备任何缺陷发现能力。 + +总结 + +通过今天这篇文章,我阐述了一个基本思想是:探索式测试是一种软件测试风格,而不是一种具体的软件测试技术。 + +作为一种思维方法,探索式测试强调依据当前语境与上下文选择最适合的测试技术,并且强调独立测试工程师的个人自由和责任,其目的是为了持续优化其工作的价值。 + +探索式测试建议在整个项目中,将测试相关学习、测试设计、测试执行和测试结果解读作为相互支持的活动,并行地执行。 + +从中我们可以看出,探索式测试的思想方法和精益生产、敏捷软件开发的理念高度一致,这也是探索式测试颇受敏捷团队欢迎的原因之一。 + +思考题 + +如果查阅探索式测试相关的技术专著,你会发现探索式测试有很多方法和工具,从中你有哪些收获呢?如果再结合你在实际项目中使用探索式测试的经验,你又有哪些心得体会呢? + +欢迎你给我留言,我们一起讨论。- + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/44测试先行:测试驱动开发(TDD).md b/专栏/软件测试52讲/44测试先行:测试驱动开发(TDD).md new file mode 100644 index 0000000..d57f7de --- /dev/null +++ b/专栏/软件测试52讲/44测试先行:测试驱动开发(TDD).md @@ -0,0 +1,279 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 44 测试先行:测试驱动开发(TDD) + 你好,我是茹炳晟。今天我和你分享的主题是“测试先行:测试驱动开发(TDD)”。 + +通过上一篇文章,我们已经深入理解了什么是探索式测试,以及如何用探索式测试开展具体的测试。今天我这次分享的目的,就是和你聊聊软件测试领域中的另一个很热门的话题:测试驱动开发,也就是Test-Driven Development,通常简称为TDD。 + +听上去有些迷惑是不是?测试怎么可能驱动开发呢?在传统软件的开发流程中,软件开发人员先开发好功能代码,再针对这些功能设计测试用例、实现测试脚本,以此保证开发的这些功能的正确性和稳定性。那么,TDD从字面上理解就是要让测试先行,这又是怎么一回事呢? + +确切地说,TDD并不是一门技术,而是一种开发理念。它的核心思想,是在开发人员实现功能代码前,先设计好测试用例的代码,然后再根据测试用例的代码编写产品的功能代码,最终目的是让开发前设计的测试用例代码都能够顺利执行通过。 + +这样对于开发人员来说,他就需要参与到这个功能的完整设计过程中,而不是凭自己想象去开发一个功能。他有一个非常明确的目标,就是要让提前设计的测试用例都可以顺利通过,为此,他先实现测试用例要求的功能,再通过不断修改和完善,让产品代码可以满足测试用例,可以说是“小而美”的开发过程。 + +所以,从本质上来讲,TDD并不属于测试技术的范畴。那么,我为什么还要单独用一篇文章和你分享这个主题呢?因为,TDD中通常会用到很多常见的自动化测试技术,使得测试在整个软件生命周期中的重要性和地位得到了大幅提升。 + +可以说,TDD的思想和理念给软件研发流程带来了颠覆性的变化,使得测试工作从原本软件研发生命周期的最后端走向了最前端。也就是说,原本测试工作是软件研发生命周期最后的一个环节,而现在TDD相当于把测试提到了需求定义的高度,跑到了软件研发生命周期最前面。 + +那么,接下来我们就一起看看TDD的优势有哪些,以及TDD的具体实施过程。 + +TDD的优势 + +TDD的优势,可以概括为以下五个方面: + + +保证开发的功能一定是符合实际需求的。 + + +用户需求才应该是软件开发的源头,但在实际的软件开发过程中,往往会在不知情的情况下,或者自己的主观判断下,开发出一个完全没有实际应用场景的功能。而这些没有实际应用场景的功能,却因为产品验证和测试工作介入的时机都在项目后期,所以往往在集成测试中或者产品上线后才会被发现。 + +比如,开发人员在实现用户注册的功能时,认为需要提供使用手机号注册的功能。但是,这个功能开发完成后,测试人员却告知开发人员这个功能用不上,或者产品上线后才发现这个功能在实际场景中完全不是必须的,因为用户可以使用邮箱注册,然后再通过绑定手机号实现手机号登陆。所以,直接用手机号注册这个功能是不需要的,真正需要的是绑定邮箱和手机号的功能。 + +试想一下,如果是测试驱动开发,即先根据用户的实际需求编写测试用例,再根据测试用例来完成功能代码,就不会出现这种既浪费时间、精力,又没有必要的功能了。 + + +更加灵活的迭代方式。 + + +传统的需求文档,往往会从比较高的层次去描述功能。开发人员面对这种抽象的需求文档,往往会感觉无从下手。但是,在TDD的流程里,需求是以测试用例描述的,非常具体。那么,开发人员拿到这样的需求时,就可以先开发一个很明确的、针对用户某一个小需求的功能代码。 + +在开发过程中,开发人员可以不断的调试这个功能,通过测试->失败-修改/重构->测试->成功的过程,使开发的代码符合预期,而不是等所有功能开发完成后,再将一个笨重的产品交给测试人员进行一个长周期的测试,发现缺陷后再整个打回来修改,然后由此又可能会引入新的缺陷。 + +另外,如果用户需求有变化,我们能够很快地定位到要修改的功能,从而实现快速修改。 + + +保证系统的可扩展性。 + + +为了满足测试先行的灵活迭代方式,我们会要求开发人员设计更松耦合的系统,以保证它的可扩展性和易修改性。这就要求,开发人员在设计系统时,要考虑它的整体架构,搭建系统的骨架,提供规范的接口定义而非具体的功能类。 + +这样,当用户需求有变化时,或者有新增测试用例时,能够通过设计的接口快速实现新功能,满足新的测试场景。 + + +更好的质量保证。 + + +TDD要求测试先于开发,也就是说在每次新增功能时,都需要先用测试用例去验证功能是否运行正常,并运行所有的测试来保证整个系统的质量。在这个测试先行的过程中,开发人员会不断调试功能模块、优化设计、重构代码,使其能够满足所有测试场景。所以,很多的代码实现缺陷和系统设计漏洞,都会在这个不断调优的过程中暴露出来。 + +也就是说,TDD可以保证更好的产品质量。 + + +测试用例即文档。 + + +因为在TDD过程中编写的测试用例,首先一定是贴合用户实际需求的,然后又在开发调试的过程中经过了千锤百炼,即一定是符合系统的业务逻辑的,所以我们直接将测试用例生成需求文档。 + +这里,直接将测试用例生成需求文档的方法有很多、很简单的方法,比如JavaDoc。 + +这样,我们就无须再花费额外的精力,去撰写需求文档了。 + +你看,TDD真的是优势多多吧。那么,接下来我们就一起来看看实施TDD的具体过程。 + +测试驱动开发的实施过程 + +站在全局的角度来看,TDD的整个过程遵循以下流程: + + +为需要实现的新功能添加一批测试; + +运行所有测试,看看新添加的测试是否失败; + +编写实现软件新功能的实现代码; + +再次运行所有的测试,看是否有测试失败; + +重构代码; + +重复以上步骤直到所有测试通过。 + + +接下来,我们就通过一个具体的例子,来看看TDD的整个流程吧。 + +我们现在要实现这么一个功能:用户输入自己的生日,就可以输出还要多少天到下次生日。 + +根据TDD测试先行的原则,我们首先要做的是设计测试用例。 + +测试用例一,用户输入空字符串或者null: + +@Test +//测试输入空字符串null时,是否抛出"Birthday should not be null or empty"异常 +public void birthdayIsNull() { + RuntimeException exception = null; + try { + BirthdayCaculator.caculate(null); + }catch(RuntimeException e) { + exception = e; + } + Assert.assertNotNull(exception); + Assert.assertEquals(exception.getMessage(), "Birthday should not be null or empty"); +} + +@Test +//测试输入空字符串""时,是否抛出"Birthday should not be null or empty"异常 +public void birthdayIsEmpty() { + RuntimeException exception = null; + try { + BirthdayCaculator.caculate(""); + }catch(RuntimeException e) { + exception = e; + } + Assert.assertNotNull(exception); + Assert.assertEquals(exception.getMessage(), "Birthday should not be null or empty"); +} + + +根据这个测试用例,我们可以很容易地写出这部分的Java代码: + +public static int caculate(String birthday) { + if(birthday == null || birthday.isEmpty()) { + throw new RuntimeException("Birthday should not be null or empty"); + } +} + + +测试用例二,用户输入的生日格式不符合YYYY-MM-dd的格式: + +@Test +//测试输入错误的时间格式,是否抛出"Birthday format is invalid!"异常 +public void birthdayFormatIsInvalid() { + RuntimeException exception = null; + try { + BirthdayCaculator.caculate("Sep 3, 1996"); + }catch(RuntimeException e) { + exception = e; + } + Assert.assertNotNull(exception); + Assert.assertEquals(exception.getMessage(), "Birthday format is invalid!"); +} + + +那么,这部分的Java代码实现便要catch住ParseException, 重新自定义错误信息并抛出异常。 + +SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); +Calendar birthDate = Calendar.getInstance(); +try { + //使用SimpleDateFormat来格式化输入日期值 + birthDate.setTime(sdf.parse(birthday)); +} catch (ParseException e) { + throw new RuntimeException("Birthday format is invalid!"); +} + + +测试用例三,用户输入的生日格式正确,但是今年的生日已经过了,就应该返回离明年的生日还有多少天: + +@Test +//测试用户输入的日期晚于今年生日的情况,判断是否返回离明年的生日有多少天 +public void thisYearBirthdayPassed() { + Calendar birthday = Calendar.getInstance(); + birthday.add(Calendar.DATE, -1); + SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd"); + String date = sdf.format(birthday.getTime()); + int days = BirthdayCaculator.caculate(date); + //天数不应该出现负数 + Assert.assertTrue(days > 0); +} + + +测试用例四,用户输入的生日格式正确且今年生日还没过,返回的结果应该不大于365天: + +@Test +//测试用户输入的日期早于今年生日的情况,判断返回的天数是否小于365 +public void thisYearBirthdayNotPass() { + Calendar birthday = Calendar.getInstance(); + birthday.add(Calendar.DATE, 5); + SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd"); + String date = sdf.format(birthday.getTime()); + int days = BirthdayCaculator.caculate(date); + //天数不应该大于一年的天数,365天 + Assert.assertTrue(days < 365); +} + + +测试用例五,用户输入的生日格式正确并且是今天,返回的结果应该为0: + +@Test +//测试用户输入的日期恰好等于今年生日的情况,判断返回的天数是否是0 +public void todayIsBirthday() { + Calendar birthday = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd"); + String date = sdf.format(birthday.getTime()); + int days = BirthdayCaculator.caculate(date); + Assert.assertEquals(days, 0); +} + + +综合上述五种测试场景,根据测试用例,我们可以编写完整的功能代码覆盖所有类型的用户输入,完整代码如下: + +public static int caculate(String birthday) { + //首先对输入的日期是否是null或者是""进行判断 + if(birthday == null || birthday.isEmpty()) { + throw new RuntimeException("Birthday should not be null or empty"); + } + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + Calendar today = Calendar.getInstance(); + + //处理输入的日期恰好等于今年生日的情况 + if(birthday.equals(sdf.format(today.getTime()))) { + return 0; + } + + //输入日期格式的有效性检查 + Calendar birthDate = Calendar.getInstance(); + try { + birthDate.setTime(sdf.parse(birthday)); + } catch (ParseException e) { + throw new RuntimeException("Birthday format is invalid!"); + } + birthDate.set(Calendar.YEAR, today.get(Calendar.YEAR)); + + //实际计算的逻辑 + int days; + if (birthDate.get(Calendar.DAY_OF_YEAR) < today.get(Calendar.DAY_OF_YEAR)) { + days = today.getActualMaximum(Calendar.DAY_OF_YEAR) - today.get(Calendar.DAY_OF_YEAR); + days += birthDate.get(Calendar.DAY_OF_YEAR); + } else { + days = birthDate.get(Calendar.DAY_OF_YEAR) - today.get(Calendar.DAY_OF_YEAR); + } + return days; +} + + +以上场景,每添加一个新的功能点,都会添加一个测试方法;完成新功能点的软件代码后,接着运行当前所有的测试用例,以保证新加的功能代码能够满足现有的测试需求。这就是一个典型的TDD过程了。但是,在实际开发场景,肯定会更复杂, 你想要用TDD思想写出健壮稳定的代码,就需要深入理解TDD中的每一步。 + +首先,需要控制TDD测试用例的粒度。如果测试用例并不是最小粒度的单元测试,开发人员就不能不假思索地直接根据测试用例开发功能代码,而应该先把测试用例分解成更小粒度的任务列表,保证每一个任务列表都是一个最小的功能模块。 + +在开发过程中,要把测试用例当成用户,不断分析他可能会怎样调用这个功能,大到功能的设计是用类还是接口,小到方法的参数类型,都要充分考虑到用户的使用场景。 + +其次,要注意代码的简洁和高效。随着功能代码的增加,开发人员为了让测试能顺利通过,很可能会简单粗暴地使用复制粘贴来完成某个功能,而这就违背了TDD的初衷,本来是为了写出更优雅的代码,结果反而造成了代码冗余混乱。因此,在开发-测试循环过程中,我们要不断地检查代码,时刻注意是否有重复代码、以及不需要的功能,将功能代码变得更加高效优雅。 + +最后,通过重构保证最终交付代码的优雅和简洁。所有功能代码都完成,所有测试都通过之后,我们就要考虑重构了。这里可以考虑类名、方法名甚至变量名命名,是否规范且有意义,太长的类可以考虑拆分;从系统角度检查是否有重复代码,是否有可以合并的代码,你也可以参考市面上比较权威的关于重构的书完成整个系统的重构和优化。这里我建议你阅读Martin Fowler的《重构:改善既有代码的设计》这本书。 + +总的来说,TDD有其优于传统开发的特点,但在实际开发过程中,我们应该具体场景具体分析。 + +比如,最典型的一个场景就是,一个旧系统需要翻新重做,并且针对这个老系统已经有很多不错的测试用例了,这就很适合选用TDD。 + +总之,我们可以通过分析当前的时间、人、方式、效果各要素来最终决定是否选用TDD。另外,需要特别注意的是,选用TDD并不是测试人员或者测试部门的事情,而是需要公司层面的流程和体系的配合,也正是这种原因,虽然大家都能看到TDD的优势,但是在实际项目中的运用还是比较有限。 + +总结 + +今天我和你分享了测试驱动开发的核心理念,以及TDD的优势。 + +TDD的核心思想便是在开发人员实现功能代码前,先设计好测试用例,编写测试代码,然后再针对新增的测试代码来编写产品的功能代码,最终目的是让新增的测试代码能够通过。 + +相对于传统软件开发流程,TDD的优势主要包括对需求精准的把控、更灵活的迭代、促使更好的系统设计、更好的交付质量以及轻量级的文档等。 + +最后,我用用“用户输入自己的生日,就可以输出还要多少天到下次生日”作为例子,展示了测试驱动开发的完整流程,希望帮助你对TDD有更直观的认识。 + +思考题 + +在实际的工程项目中,你实际使用过TDD吗?如果有的话,是否可以分享一下你的实践心得?如果没有的话,你是否可以设象一下你会怎么规划和设计一个TDD的项目? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/45打蛇打七寸:精准测试.md b/专栏/软件测试52讲/45打蛇打七寸:精准测试.md new file mode 100644 index 0000000..520b62b --- /dev/null +++ b/专栏/软件测试52讲/45打蛇打七寸:精准测试.md @@ -0,0 +1,166 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 45 打蛇打七寸:精准测试 + 你好,我是茹炳晟。今天我和你分享的主题是“打蛇打七寸:精准测试”。 + +在前面的两篇文章中,我和你分享了探索式测试和测试驱动开发的概念、具体的实施方法。今天,我会继续和你分享软件测试领域中的另一个前沿话题:精准测试。 + +软件测试行业从最开始的手工测试到自动化测试,从黑盒测试到白盒测试,测试理念和技术都发生了日新月异的变化。现如今,几乎所有的软件公司都有一套强大且复杂的自动化测试用例,用来夜以继日地保证产品的正确性和稳定性。 + +然而,你有没有想过,现在你所掌握的软件测试技术和用例,真的是最准确、最适合你的产品的吗?这其中,是不是存在很多冗余的测试数据、根本用不上的测试用例、永远成功不了的测试场景? + +更糟糕的是,当产品代码有更新时,你根本不知道这些更新到底影响了哪些功能,也无法精准地选取测试用例,而不得不执行完整的全回归测试。 + +针对这类问题,精准测试的概念在2016年被提了出来。所谓精准测试,就是借助一定的技术手段、通过算法的辅助对传统软件测试过程进行可视化、分析以及优化的过程。也就是说,精准测试可以使得测试过程可视、智能、可信和精准。 + +为了可以帮助你更好地理解,为什么要有精准测试,以及它可以解决什么问题,我在和你分享精准测试的内容时,会先和你一起分析传统软件测试正面临着哪些痛点,而精准测试又是如何解决这些痛点的。 + +传统软件测试的主要短板 + +现如今,软件产品的规模以及复杂度的发展速度,可谓超乎想象,而传统的软件测试方法,在面临这些挑战时已经表现出了些许力不从心。这些力不从心,也就是传统软件测试的短板,我归纳为了下面这五大类: + +第一大短板,测试的维护成本日益升高。 + +当传统测试的用例逐渐增加时,需要花费越来越大的时间和人力成本,去维护一个庞大的测试用例集,以此保证产品新特性和老功能的正确性和稳定性。 + +而在这成千上万的测试用例中,有很多陈旧的用例已经失效了(不再能满足现有产品的测试需求了),但是整个团队还是要花费很多精力去维护这个庞大的测试用例集。 + +第二大短板,测试过程的低效。 + +随着软件功能不断丰富,相应的测试用例集也愈加庞大,这时难免会出现“杀虫剂”效应,即:测试用例越来越多,而产品的“免疫力”也越来越强。 + +造成这种问题的原因是,我们在测试早期已经发现了80%的软件缺陷,除非再花费巨大的人力和时间成本去分析和增加大批量的测试用例,否则后期新增的测试用例已经很难再发现新的缺陷了。 + +而精准测试可以通过对已有测试数据的跟踪分析,来定位或者缩小测试范围,以此减少发现剩下20%的软件缺陷的工作量。 + +第三大短板,缺乏有效的回归用例选取机制。 + +在传统测试理念中,每次添加新功能或者修复缺陷,一般都需要在产品上线前进行一轮全回归测试,哪怕这次的改动只有一行代码。但是,全回归测试的测试用例数量以及执行代价一般都比较大。 + +这里,我们之所以要采用全回归测试,是因为我们无法准确地知道这次的更新到底会影响哪些功能,也无法知道应该从回归测试中选取哪些必要的测试用例,无奈之下只能两眼一抹黑地执行全部用例。 + +第四大短板,测试结果的可信度不高。 + +在传统的软件测试中,测试数据的统计分析人工因素占据了绝大部分比重,由此导致测试数据本身的技术公信力不够高,进而需要依靠管理手段来保证真实的测试数据被准确地记录。 + +这种做法不仅可靠性差,而且执行成本高。 + +第五大短板,无论是白盒测试技术还是黑盒测试技术都有其局限性。 + +如果完全基于黑盒测试,那么注定无法深入代码实现的细节,也就无法做到有的放矢地设计测试;而如果基于白盒测试技术,为了保证代码质量,往往会采用代码级测试和代码覆盖率技术。 + +但是,这些测试方法都强依赖于产品代码,一旦代码发生改变,很多测试都会因此失效,因此很难适应高速迭代的开发流程。 + +另外,由于目前的代码级测试和代码覆盖率技术还不支持测试用例级别的覆盖率分析,而是要将所有测试结果混在一起,导致白盒测试时无法区分代码覆盖率的贡献到底来自于哪个测试用例,这将极大地限制白盒测试工具在测试结果分析上的应用。 + +精准测试的核心思想 + +而,精准测试便是为了解决传统测试的这些短板。它的核心思想是,借助一些高效的算法和工具,收集、可视化并且分析原生的测试数据,从而建立起一套测试分析系统。 + +所以,精准测试的主要特征可以概括为以下几个方面: + +第一,精准测试是对传统测试的补充。精准测试是基于传统测试数据的,并不会改变传统的软件测试方法,更不会取代传统测试。也就是说,精准测试在不改变原有测试集的基础下,能够优化测试过程和数据,提高测试效率。 + +第二,精准测试采用的是黑盒测试与白盒测试相结合的模式。在执行黑盒测试时,收集程序自动产生的白盒级别的运行数据,然后通过可视化或者智能算法识别出测试未覆盖的点,继而引导开发人员和测试人员有的放矢地补充测试用例。 + +同时,在黑盒测试的执行过程中,可以实现测试用例和产品代码的自动关联,将基于黑盒的功能测试直接映射到基于白盒的代码层,这将使智能回归测试用例选取的想法成为可能。 + +第三,精准测试的数据可信度高。精准测试的数据都是由系统自动录入和管理的,人工无法直接修改数据,因此我们可以直接将传统测试产生的数据导入精准测试系统,用于测试结果的分析,从而使测试结果具有更高的可信度。 + +第四,精准测试过程中,不直接面对产品代码。精准测试通过算法和软件实现对测试数据和过程的采集,因此并不会直接面向代码,也就不会强依赖于产品代码。 + +但是,精准测试能够实现测试用例和产品代码的自动关联,也就是说代码覆盖率的统计可以以测试用例为单位来进行,具体实现的核心思想还是基于代码覆盖率的统计,只是在代码覆盖率的元数据上增加了测试用例的信息。 + +因此,代码的改变并不会影响测试过程,但却能够将功能测试间接映射到代码级别。这样,精准测试就实现了测试用例和被测产品代码的双向追溯。 + +第五,精准测试是与平台无关的、多维度的测试分析算法系统。精准测试系统是一种通用的测试分析系统,独立于任何测试平台,其内部算法和业务无关,因此适用于各种不同的产品。 + +同时,精准测试为我们提供了多维度的测试分析算法,拓展了白盒测试的范畴。而,精准测试对测试用例和产品代码的自动关联,使得它可以为测试过程提供大量的智能分析结果。 + +接下来,我们再一起看看精准测试具体有哪些方法。 + +精准测试的具体方法 + +目前业界最成熟并且已经产品化的精准测试体系,来自于国内公司“星云测试”。所以,下面关于精准测试的具体方法的分享中,涉及到的很多概念我都参考了其官网的《星云精准测试白皮书》。如果你对完整的白皮书内容感兴趣的话,可以参考http://www.threadingtest.com/index.html。 + +目前,由星云测试实现的精准测试平台中,核心组件包括精准测试示波器、测试用例和被测产品代码的双向追溯、智能回归测试用例选取,以及测试用例聚类分析这四项最关键的技术。在这其中,最为核心的技术是测试用例和产品代码的双向追溯。 + +接下来,我会依次和你分享这4项核心技术,希望借此加强你对精准测试的理解。 + +软件精准测试示波器 + +软件精准测试示波器,即在软件测试(人工测试或者自动化测试)的过程中,自动分析代码运行的一些数据指标,并将其用图表的方式实时显示出来。其中,这些数据指标包括了代码的逻辑块执行速率、代码的条件执行速率、函数的调用速率等等。 + +同时,由于示波器记录了每个测试用例的产品代码执行序列,因此可以通过比较两个测试用例的产品代码执行序列来判断两个测试用例是否隶属于同一个等价类,这将有助于精简测试用例的数量。 + +另外,由于示波器所有的数据都是通过系统自动导入的,因此不存在人工导入可能引入的数据误差,借此保证了所有数据的分析和显示都是真实且可靠的。 + +最终,示波器以类似心电图的形状实时显示测试过程中被测代码的运行信息,因此我们可以很直观地看到测试中发生的变化。一旦测试过程稍有异常,就会立刻显示在示波器上,我们通过图形的变化就可以轻易地对平时不可见的程序行为进行分析,并作出判断。比如,是否存在计算密集的区域,是否有不该执行的代码在后台运行等。 + +测试用例和被测产品代码的双向追溯 + +顾名思义,测试用例和被测产品代码的双向追溯,就是通过一定的技术手段实现测试用例和被测产品代码的双向关联。这样,我们可以通过测试用例追溯到其执行的代码,也可以通过分析代码的功能为测试提供数据。 + +这里,测试用例和被测代码的双向追溯,包括正向追溯和反向追溯。 + +其中,正向追溯,即通过示波器将产品代码和测试用例进行自动关联。这个关联,可精确到方法或者代码块级别。而在关联之后,精准测试系统可以显示每个测试用例实际执行的代码。这样,当我们发现软件缺陷时,便可以快速定位出其所在的代码。 + +反向追溯是指,如果我要关注程序中的某一块代码,那么就可以通过精准测试系统追溯到所有测试这块代码的测试用例。这样,就使得测试数据便于统计和量化,同时测试和开发工程师之间就可以基于测试数据进行交流,为他们的沟通提供更有效的桥梁,降低沟通成本。 + +这里我画了一张图,来帮助你更好地理解正向追溯和反向追溯的概念。 + + + +图1 测试用例和被测产品代码的双向追溯示意图 + +总而言之,测试用例和被测产品代码的双向追溯能显著提升测试效率: + + +当我们发现了软件缺陷和错误时,通过这个方法可以迅速定位到有问题的代码逻辑; +当出现一些难以复现的缺陷时,这个方法可以帮助我们追溯有问题的代码而无需强行复现。 + + +说到这里,你可能会有一个疑问,双向追溯技术后台一定是采用了代码覆盖率的统计工具,但是这个代码覆盖率统计工具和双向追溯又具体有什么区别和联系呢? + +事实上,这两者之间最大的区别,体现在测试覆盖率的统计方式上。传统的代码覆盖率统计工具,会把所有测试产生的覆盖率混在一起,并不具备单个测试用例的覆盖率统计功能;而精准测试中的双向追溯技术,则可以将覆盖率的分析和计算精确到每个测试用例针对的产品代码。 + +另外,从实际工程的角度来看,传统的代码覆盖率统计工具都是单机运行,然后完成数据的统计,无法有效整合一个团队下多人的测试结果,也不能按照日期累计。而,现如今的双向追溯技术,则支持多人异地测试、整合计算覆盖率等功能。 + +智能回归测试用例选取算法 + +回归测试,就是在修复了某个错误或缺陷后,再对软件进行测试以确保没有引入新的错误或缺陷。而,智能回归测试用例选取算法便是针对需要执行的回归测试,通过算法得出各个测试用例的权重和优先级,使得在有限的时间和人力下,能够更高效地执行测试用例。 + +由于精准测试提供了智能算法来自动选取回归测试用例,因此既避免了人工选取回归测试用例时可能存在的测试盲点,也减少了执行回归测试的时间,同时还能够保证计算结果的精确性,大大降低了回归测试的风险。 + +另外,精准测试中测试用例和被测产品代码的双向追溯性,也使得当有代码变更需要执行回归测试时,可以直接找到具体应该执行哪些测试用例。 + +测试用例的聚类分析 + +测试用例的聚类分析,是指通过建立测试用例和代码执行的剖面关系,实现对测试用例的聚类分析。这个聚类分析的结果,将以两维数据呈现出来,即:测试用例ID及其对应的代码执行剖面。 + +通过聚类数据,我们可以很容易地发现测试用例的执行错误。比如,测试用例A应该执行代码块A,而通过聚类分析,我们发现用例执行完被分在了代码块B上。因此,我们就可以断定该测试用例发生了错误或者测试环境出现了问题。 + +同时,测试用例的聚类分析能够展示测试用例的分布情况,为我们调整测试用例的分布提供依据。也就是说,我们可以通过这个数据,对测试用例聚集较少的区域予以补充丰富,同时也可以在测试用例聚集丰富的区域内提取出相对重要的用例,然后执行,从而节省时间、提高测试效率。 + +精准测试的概念和理论体系虽然比较完善,但就目前来看实际落地的案例还比较少。有些公司可能并不会直接去使用星云测试的平台,而是会基于精准测试的理念和方法去开发自己的工具。比如,有些国内互联网公司,就自己实现了基于增量的代码覆盖率统计方案,以及具有双向追溯功能的代码覆盖率方案。 + +总结 + +在今天这篇文章中,我和你分享了传统软件测试的方法、理念,因为测试用例数量持续增加而导致的用例维护成本高、测试过程低效、缺乏有效的回归测试用例选取机制等等一系列的问题,而有些力不从心。于是,精准测试应运而生了。 + +可以说,精准测试是通过一系列的智能算法和技术实现了对测试过程的管理。它可以在测试运行时,分析源数据指标以指导传统测试,并在一次次的修正中大幅提升测试效率。并且,精准测试在测试过程中产生的海量精准数据,即使不在测试周期里,也可以进行分析和追溯,让测试变得更加高效和有价值。 + +所以说,精准测试在节省了人力成本的同时,保证了软件的质量。 + +思考题 + +在这里,我并没有和你分享精准测试的所有概念,但是我建议你可以仔细阅读一下星云测试的《精准测试框架白皮书》。然后,你可以再思考一下,还有哪些项目和产品更适于开展精准测试。 + +如果你还有任何关于精准测试的疑问,欢迎给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/46安全第一:渗透测试.md b/专栏/软件测试52讲/46安全第一:渗透测试.md new file mode 100644 index 0000000..6b01400 --- /dev/null +++ b/专栏/软件测试52讲/46安全第一:渗透测试.md @@ -0,0 +1,227 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 46 安全第一:渗透测试 + 你好,我是茹炳晟。今天我和你分享的主题是:安全第一之渗透测试。 + +随着互联网的发展,网络环境越来越复杂,各类软件涉及的领域也越来越多,这时系统与软件的安全问题就愈加重要了。各类隐私信息、财务信息等的泄露,稍有不慎就会造成难以挽回的损失。 + +所以,大多数的公司,尤其是中大型的公司,已经针对系统与软件的安全采取了很多手段。比如,安装杀毒软件、定期给系统打补丁、定期进行漏洞及安全扫描、测试并封杀应用自身的安全漏洞等等。 + +虽说这些措施已经可以防止大部分的安全漏洞了,但却还不足以完全保证系统的安全性。这个时候,渗透测试便以其独立的“风姿”出现在了你我的视野里。 + +那么,接下来我们就一起看看什么是渗透测试,以及具体如何执行渗透测试吧。 + +渗透测试的定义 + +渗透测试指的是,由专业安全人员模拟黑客,从其可能存在的位置对系统进行攻击测试,在真正的黑客入侵前找到隐藏的安全漏洞,从而达到保护系统安全的目的。 + +或许你会有这样的疑问,软件系统在研发阶段已经用了各种手段保证安全性,为什么还需要进行渗透测试呢? + +其实,这就好比让开发人员自己做测试一样,虽说他们对自己一手开发出来的软件产品再熟悉不过了,但却也是最难测出漏洞的。因为,开发人员的惯性思维,会使得他们在面对很多的潜在问题时,都误以为这不是问题的,所以我们需要引入独立的测试人员。 + +同样的道理,面对自己开发的系统,开发人员总是习惯性地去处理比较容易出现安全漏洞的地方,而对于一些隐藏的、不容易被发现的漏洞,却很难发现。 + +另外,除了惯性思维之外,开发人员通常并不是安全领域的专家,因此往往会缺少专业的安全知识,不了解常用的系统攻击手段,从而导致他们并不能对安全相关的场景进行充分、客观的测试。 + +这里,为了便于理解,我们可以将软件系统比喻成一座房子。 + +当房子建好后,我们会为其配备防盗门、防盗窗,甚至是安全警报器等,这时我们自认为这个房子足够安全了。但,我们永远都不知道,意图侵入者会使用什么样的方式找到漏洞从而攻克我们布置的安全防线。 + +所以,为了保证这座房子足够安全,我们会考虑聘请外部的安全专家来进行一系列的检测。比如,检测防盗门是否足够牢固、门锁是否容易被破坏、报警器是否在发生异常时能够正常发出警报、窗户和通道是否容易被侵入,或者从根本上判定我们所布下的安全防线,在安全机制上是否存在系统性的问题,以及需不需要更新等等。 + +我们甚至可以找人模拟入侵这座房子,在这个模拟过程中,由其发现这所房子是否还存在安全漏洞,以此验证房子真实的安全性。 + +那么,这个由外部的安全专家验证房子安全性的过程,便可以说是对这座房子进行渗透测试的过程。其中,这个房子便是我们的软件系统;而我们为验证房子安全性采取的这一系列方法,就是我们所说的安全渗透测试。 + +渗透测试的常用方法 + +那么,安全渗透测试应该怎样进行呢?在这里,我总结了渗透测试的五种常用测试方法,包括: + + +有针对性的测试; +外部测试; +内部测试; +盲测; +双盲测试。 + + +接下来,我们就一起看看,具体每种测试方法,要如何开展吧。 + + +有针对性的测试 + + +有针对性的测试,是由公司内部员工和专业渗透测试团队共同完成的。其中,公司内部员工不仅要负责提供安全测试所需要的基础信息,同时也要负责业务层面的安全测试;而专业渗透测试团队,则更多关注业务以外的、更普适的安全测试。 + +有针对性的测试,属于研发层面的渗透测试。参与这类测试的人员,可以得到被测系统的内部资料,包括部署信息、网络信息、详细架构设计,甚至是产品代码。 + +有时,我们也把这种测试方法叫作“开灯”测试。之所以称为“开灯”测试,是因为有针对性的测试,是在测试人员完全了解系统内部情况的前提下开展的,有区别于外部人员完全不知道系统内部细节而进行的渗透测试。 + + +外部测试 + + +外部测试,是针对外部可见的服务器和设备(包括:域名服务器(DNS)、Web服务器或防火墙、电子邮箱服务器等等),模拟外部攻击者对其进行攻击,检查它们是否能够被入侵,以及如果被成功入侵了,会被入侵到系统的哪一部分、又会泄露多少资料。 + +一般情况下,外部测试是由内部的测试人员或者专业渗透测试团队,在假定完全不清楚系统内部情况的前提下开展的。 + + +内部测试 + + +内部测试是由测试工程师模拟内部人员,在内网(防火墙以内)进行攻击,因此测试人员会拥有较高的系统权限,也能够查看各种内部资料,目的是检查内部攻击可以给系统造成什么程度的损害。 + +所以,内部测试是为了防止系统的内部员工对系统进行内部攻击,同时以此来制定系统内部员工的权限管理策略。 + + +盲测 + + +盲测,指的是在严格限制提供给测试执行人员或团队信息的前提下,由他们来模拟真实攻击者的行为和上下文。通常,测试人员可能只被告知被测系统公开的信息,而对系统细节以及内部实现一无所知。 + +因为这种类型的测试可能需要相当长的时间进行侦察,所以代价会相对昂贵。而且,这类测试的效果,将在很大程度上取决于测试人员的技术水平。一般来讲,盲测是由专业渗透测试团队在测试后期开展的,通常会借助很多黑客攻击工具。 + +可以想象,如果测试人员拥有专业黑客的技术水平,同时结合各类渗透和黑客工具,一定能发现安全漏洞;但是,如果测试人员并不具备专业的安全测试以及系统攻击知识,那么可想而知,他们能够发现的问题就非常有限了。 + + +双盲测试 + + +双盲测试比盲测更进一步,也叫作“隐秘测试”。 + +在这类测试中,不光测试人员对系统内部知之甚少,而且被测系统内部也只有极少数人知道正在进行安全测试。因此,双盲测试可以反映软件系统最真实的安全状态,能够有效地检测系统在正常情况下,对安全事件的监控和处理能力是否合格。 + +因此,双盲测试可以用于测试系统以及组织的安全监控和事故识别能力,及其响应过程。一般来说,双盲测试一般是由外部的专业渗透测试专家团队完成,所以实际开展双盲测试的项目并不多。 + +执行渗透测试的步骤 + +了解了渗透测试的常用方法,那么到底要怎样具体开展呢?现在,我就和你分享一下开展渗透测试的5个主要步骤: + +第一步:规划和侦察。这一步包含了定义测试的范围和目标、初步确定要使用的工具和方法、明确需要收集的情报(例如,网络和域名,邮件服务器),以更好地了解目标的工作方式及其潜在的安全漏洞。 + +第二步:安全扫描。安全扫描包括静态分析和动态分析两个阶段。 + + +静态分析阶段,是通过扫描所有代码来估计其运行时的方式。这里,我们可以借助一些工具来一次性地扫描所有代码。目前,主流工具有Fortify SCA和Checkmarx Suite。 + +动态分析阶段,则是在代码运行时进行扫描。这样的扫描更能真实反映程序的行为,可以实时提供应用程序的运行时视图,比静态扫描更准确、实用。 + + +第三步:获取访问权限。在这一步,测试人员将模拟黑客对应用程序进行网络攻击,例如使用SQL注入或者XSS跨站脚本攻击等,以发现系统漏洞。然后,利用找到的漏洞,通过升级自己的权限、窃取数据、拦截流量等方式了解其可能对系统造成的损害。 + +至于到底什么是SQL注入,什么是XSS跨站脚本攻击,你可以自行查阅一些资料,也可以给我留言一起讨论。 + +第四步:维持访问权限。这个阶段的目的是,查看被发现的漏洞是否可以长期存在于系统中,如果漏洞能够被持久化,那么在很长的一段时间内入侵者都可以对系统进行深入访问或进行破坏。 + +这个阶段模仿的是高级持续性威胁。这类威胁,通常在系统中可以存在数月之久,入侵者可以借此获取组织内较高级别的敏感数据。 + +第五步:入侵分析。完成以上的四步之后,我们就要分析得到的结果了。通常情况下,我们需要将测试结果汇总成一份详尽的测试报告,并详细说明: + + +可以被利用的特定漏洞; + +利用该漏洞的具体步骤; + +能够被访问的敏感数据; + +渗透测试人员能够在系统中不被侦测到的存在时间。 + + +专业的安全人员会分析这些信息,以指导和帮助我们配置企业的WAF(Web Application Firewall),同时提供对其他应用程序的安全解决方案,以修补安全漏洞并防范未来的恶意攻击。 + +渗透测试的常用工具 + +目前,在实际的渗透测试中,我们通常会使用大量的工具来完成测试。为此,我挑选了Nmap、Aircrack-ng、sqlmap、Wifiphisher、AppScan这五种常用工具,和你分享一下它们的功能,以及适用的场景。 + +这里需要特别注意的是,这些工具本身就具有黑客属性,所以很多杀毒软件会阻止该类软件的运行。同时,你也一定不要在非官方的网站下载和使用这类工具,以防被意图不轨的人预先注入了危险的攻击点,请务必小心。 + + +Nmap + + +Nmap是进行主机检测和网络扫描的重要工具。它不仅可以收集信息,还可以进行漏洞探测和安全扫描,从主机发现、端口扫描到操作系统检测和IDS规避/欺骗。 + +Nmap这类工具是渗透测试过程中最先要用到的,用来获取后续渗透测试过程中需要用到的系统基本信息,比如IP和端口等。 + +同时,Nmap适用于各大操作系统,包括Windows、Linux、macOS等,因此是一款非常强大、实用的安全检测工具。 + + +Aircrack-ng + + +Aircrack-ng是评估Wi-Fi网络安全性的一整套工具。它侧重于Wi-Fi安全的领域,主要功能有:网络侦测、数据包嗅探、WEP和WPA/WPA2-PSK破解。 + +Aircrack-ng可以工作在任何支持监听模式的无线网卡上并嗅探802.11a、802.11b、802.11g的数据。 + +Aircrack-ng的执行是通过命令行或者脚本文件的方式,并且可以运行在Linux和Windows操作系统上。它的典型应用场景,主要包括数据包注入重播攻击、解除身份验证、虚假接入点等,也可以用于破解WEP和WPA PSK。 + + +sqlmap + + +sqlmap是一种开源的基于命令行的渗透测试工具。它能够自动进行SQL注入和数据库接入,并且支持所有常见并广泛使用的数据库平台,包括Oracle、MySQL、Microsoft SQL Server、SQLite、Microsoft Access、IBM DB2、FireBird、Sybase和SAP Max DB等,使用的SQL注入技术也几乎涵盖了所有的攻击手段。 + +如果你不采用AppScan这类全面的商用安全测试工具,我的建议是通过sqlmap来确保系统数据库的安全性。 + + +Wifiphisher + + +Wifiphisher是一种恶意接入点工具,可以对Wi-Fi网络进行自动钓鱼攻击。 + +渗透测试执行人员,可以通过Wifiphisher执行有针对性的Wi-Fi关联攻击,轻松实现无线客户端的渗透测试。 + +Wifiphisher还可以用于对连接的客户端进行受害者定制的网络钓鱼攻击,用来获取凭证(例如,从第三方登录页面或WPA/WPA2预共享密钥)或用恶意软件感染受害者站点。 + + +AppScan + + +AppScan是IBM公司的一款企业级商业Web应用安全测试工具,采用的是黑盒测试,可以扫描常见的Web应用安全漏洞。 + +AppScan的工作原理是: + + +首先,从起始页爬取站下所有的可见页面,同时测试常见的管理后台; +然后,利用SQL注入原理测试所有可见页面,是否在注入点和跨站脚本攻击的可能; +同时,检测Cookie管理、会话周期等常见的Web安全漏洞。 + + +AppScan的功能十分强大,几乎涵盖了目前所有已知的攻击手段,而且攻击库还在不断地升级更新。此外,从AppScan的扫描结果中,我们不仅可以看到扫描的漏洞,还提供了详尽的漏洞原理、修改建议、手动验证等。 + +可以说,AppScan是目前最完美的渗透测试商用解决方案,但是其最大的问题在于其价格昂贵,一般只有中大型的企业才会购买使用。 + +渗透测试的收益 + +现在,你已经清楚了开展渗透测试的必要性,也大致清楚了具体要如何开展渗透测试。那么,接下来,为了让你对开展渗透测试的信心更足,我再为你总结一下它能解决的问题: + +通过渗透测试,公司可以识别出主要漏洞,并决定修补漏洞的优先级,同时合理分配系统补丁安装的时间,以确系统环境的安全性。 + +避免了安全漏洞,也就是避免了不必要的损失。因为,从安全漏洞中恢复出来,公司往往要花费巨大的代价去补救公司和客户的损失,甚至可能因此吃官司。而,渗透测试能够很好地避免这类问题,帮助公司树立良好的企业形象,因此赢得更高的信任度。 + +总的来说,我们应该按需选择适合自己产品的渗透测试方案,期间需要考虑到产品安全隐患和执行渗透测试的成本之间的平衡。 + +总结 + +在今天的这次分享中,我介绍了与渗透测试相关的知识点。 + +首先,渗透测试是指由专业安全人员模拟黑客,从其可能入侵的位置对系统进行攻击测试,以达到在真正的黑客攻击之前找到隐藏的安全漏洞,从而保护系统安全的目的。 + +然后,我根据发起渗透测试的位置以及对系统信息的掌握程度,将渗透测试分为了有针对性的测试、外部测试、内部测试、盲测和双盲测试这五种。 + +接着,我和你分享了开展渗透测试的5个步骤,分别包括了规划和侦察、安全扫描、获取访问权限、维持访问权限,以及入侵分析。 + +最后,我给你汇总了五款常用的渗透测试工具,其中功能最强大的要数IBM的AppScan了,但是其价格比较昂贵,适用于中大型企业。而关于如何选择适合自己的渗透测试方案,我的建议还是要综合考虑产品安全隐患和执行渗透测试的成本。 + +思考题 + +你所在的公司或者团队是否开展了渗透测试?你们会使用哪些渗透测试工具呢? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/47用机器设计测试用例:基于模型的测试.md b/专栏/软件测试52讲/47用机器设计测试用例:基于模型的测试.md new file mode 100644 index 0000000..e6ddef8 --- /dev/null +++ b/专栏/软件测试52讲/47用机器设计测试用例:基于模型的测试.md @@ -0,0 +1,172 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 47 用机器设计测试用例:基于模型的测试 + 你好,我是茹炳晟。今天我和你分享的主题是:用机器设计测试用例之基于模型的测试”。 + +我在前面4篇文章中,和你分享的探索式测试、测试驱动开发TDD、精准测试,以及渗透测试的内容,你是否已经掌握了呢?有没有尝试将这些比较新的理念用到你的工程项目中呢?如果你在应用的过程中,遇到了任何问题,也欢迎给我留言一起讨论。 + +那么,现在我们就正式开始测试新技术系列的最后一个话题:基于模型的测试。 + +可以说,软件测试是一款软件产品质量的最后一道防线,是产品上线前必不可少、最重要的一个环节。每一款高质量的软件产品背后,都蕴含了大量的测试工作。而且,这些测试工作很可能是整个软件开发过程中最昂贵、劳动最密集的工作。 + +虽说从最简单的功能性黑盒测试,到涉及定理证明的复杂测试,已经有很多种方法可以帮助我们提高测试的可靠性和有效性。但是,在设计测试用例的过程中,总还是存在着这样那样的问题,使得软件测试的结果没那么理想。 + +为此,我们新引入了基于模型的测试,即Model-Based-Testing,简称MBT。 + +MBT,是自动化测试的一个分支。它是将测试用例的设计依托于被测系统的模型,并基于该模型自动生成测试用例的技术。其中,这个被测系统的模型表示了被测系统行为的预期,也可以说是代表了我们对被测系统的预期。 + +从质量保证的角度来看,我们可以制定测试内容,但却无法保证测试会覆盖所有可能的组合。而MBT则允许软件开发人员和测试人员,只关注建立系统的正确性以及模型的规范性,再通过专门的MBT工具根据不同的测试用例设计策略从系统模型生成可靠的测试用例。 + +那么,MBT的原理是什么,而什么样的应用又适合进行MBT呢?接下来,我就重点为你回答这两个问题。 + +MBT的基本原理 + +MBT的基本原理是通过建立被测系统的设计模型,然后结合不同的算法和策略来遍历该模型,以此生成测试用例的设计。 + +我用下面的一张图片,为你描述了MBT的过程: + + + +图1 MBT的一般过程 + +如图1所示,开发者首先根据产品需求或者说明来构建模型,然后结合测试对象生成测试用例,测试用例针对测试对象执行完之后,生成测试报告比对测试结果。 + +接下来,我以简单的登录系统为例,和你说明如何建模。 + +当用户访问网站时,网站需要识别用户是否已经登录: + + +如果已经是登录状态,则让用户进入,结束这一分支; +如果用户还没有登录,那么页面需要返回登录框给用户。用户在登录框输入用户名和密码后,由后台服务验证用户名和密码是否正确,如果通过验证,则用户登录成功,结束分支;否则,返回错误信息,并再次返回登录框供用户登录。 + + +根据这个逻辑,我们可以建模如下: + + + +图2 网站登录系统建模 + +至此,我们就完成对这个登录系统的建模工作了。然后,我们通过具象化被测产品的需求行为,再通过工具来遍历模型中的各个路径,就可以得到我们需要的测试用例了。 + +所以,执行MBT的过程就好比你把软件系统的设计画为了一张由节点和边构成的数据结构意义上的“图”,然后通过一定的算法(比如,深度遍历或者广度遍历)来尽可能完整覆盖图中全部的可能路径的过程。 + +而根据被测系统的特点,我们可以创建不同类型的模型完成MBT。接下来,我们就一起看看有哪些常用的MBT模型吧。 + +常用模型简介 + +根据被测系统本身的特点,我们常用的模型主要有限状态机、状态图,以及UML三种。其中,有限状态机和状态图比较适合于用状态或者事件驱动的系统,而UML比较适合于靠业务流程驱动的系统。 + +第一,有限状态机。 + +有限状态机可以帮助测试人员根据选中的输入来评估输出,不同的输入组合对应着不同的系统状态。 + +在登录系统这个例子中,员工在未登录时的状态是“未登录”,一旦登录成功状态就会变为“已登录”。在已登录的状态下,员工可以访问各类资源,使用系统内的工具。 + +第二,状态图。 + +状态图是有限状态机的延伸,用于描述系统的各种行为,尤其适用于复杂且实时的系统。 + +状态图有一定数量的状态,系统的行为可以以事件的方式来驱动状态的变化。比如,缺陷管理工具中出现了缺陷,其初始状态为“new”;缺陷被开发人员修复后,就必须将其改为“Fixed”;但是,如果此时测试人员发现缺陷并未修复或者只是部分修复时,则需将状态更改为“Reopen”(重新打开)。 + +状态图的设计方式,要求为每个不同的状态创建一个事件。 + +第三,UML。 + +UML即统一建模语言,是一种标准化的通用建模语言。UML有自己定义的图形库,里面包含了丰富的图形用以描述系统、流程等。 + +UML可以通过创建可视化模型,来描述非常复杂的系统行为。 + +当我们完成被测系统的建模工作后,接下来就要将模型转化为可执行的测试用例了。这个转换过程,需要借助工具来完成。 + +因为不同领域的产品风格迥异,其使用的自动化框架和编程语言也各不相同,所以我们需要花费一些精力去寻找与自己产品匹配的MBT工具。其实,在很多情况下,我们还需要根据产品特点,去自行开发和定制工具。 + +MBT工具简介 + +这里,我为你罗列了一些常见的MBT工具,包括:BPM-X、fMBT、GraphWalker。在这里,我为你简单介绍这些工具的目的是,让你可以对MBT工具本身有个感性的认识,让你知道此类工具的应用场景和上下文。至于说如何来选择使用这些工具,这在很大程度上取决于被测系统的特点。 + +第一,BPM-X + +BPM-X根据不同的标准(比如,语句、分支、路径、条件)从业务流程模型创建测试用例。 + +它还可以从多个建模工具导入模型,并可以将测试用例导出到Excel、HP Quality Center等。这个工具,适用于业务流程比较清晰直观的场景。 + +第二,fMBT + +fMBT是一组免费的、用于全自动测试生成和执行的工具,也是一组支持高水平测试自动化的实用程序和库,主要被应用在GUI测试中。 + +fMBT包括用于多平台GUI测试的Python库,用于编辑、调试、运行和记录GUI测试脚本的工具,以及用于编辑和可视化分析测试模型和生成的测试工具。 + +第三,GraphWalker + +GraphWalker以有向图的形式读取模型,主要支持FSM、EFSM模型。它读取这些模型,然后生成测试路径。GraphWalker除了适用于GUI测试外,更适合于多状态以及基于事件驱动的状态转换的后台系统。 + +另外,GraphWalker还支持从有限状态机中生成测试用例。 + +除此之外,市面上还有很多MBT测试工具,比如GSL、JSXM、MaTeLo、MBT Suite等。这里就不再一一介绍了,你可以自行百度了解它们的特点和适用场景,从而选取合适自己的工具。 + +MBT的优势 + +其实,MBT并不能算是一种新颖的测试技术,早在七八年前就已经被提了出来并且试图应用于软件产品的测试工作中。但是,MBT在很长一段时间内,却一直停留在概念阶段,主要原因是一直没有普适的工具支持,所以很少有成功实施的实际案例。同时,业界一直以来都缺乏高效的测试用例设计生成策略,所以虽然大家都能看到MBT的优势,但能在实际项目中应用执行的却是寥寥无几。 + +与传统测试相比,MBT的优点如下: + + +测试用例的维护更轻松。由于测试用例是基于被测系统的模型生成的,因此我们只需维护好模型即可,而无需关注测试用例的细节。 + +软件缺陷发现得更早。由于我们在构建被测系统模型的过程中,已经对被测系统有了比较全面的理解,加之要根据系统需求/说明完成建模过程,所以我们可以在早期建模时发现被测系统可能存在的明显缺陷,而不用等到执行了大量的测试用例以后才发现这些缺陷。 + +测试自动化的水平更高。由于MBT只需建好模型便可以自动生成测试用例,所以不再需要人工编写测试文档。而更高级的应用,甚至可以直接生成可以直接执行的自动化测试脚本。 + +测试覆盖率变得更高,使得彻底的测试(即:穷尽测试)成为了可能。由于我们需要做的只是正确、详尽地用模型描述被测系统,而生成测试用例完全由MBT工具实现,所以这就避免了人工设计测试用例时的思维局限性,能够有效地提高测试覆盖率,让彻底的测试变为可能。- +当然,是否有必要开展彻底的测试还是要由风险决定。这里的风险指的是,由于漏测导致产品问题对业务的影响程度。MBT只是从技术上提供了可能性。 + +基于模型间接维护测试用例的方式更高效。在传统测试中,如果被测系统的流程或者功能发生了变化,我们需要耗费大量的人力和时间成本,去重新设计与之匹配的测试用例。而在MBT中,我们只需要更新被测系统的模型即可,剩下的测试用例生成工作可以由MBT工具自动完成。 + + +MBT的劣势 + +虽然MBT相对于传统测试有很多优点,但它也不是完美的测试方案。在实际开展MBT时,我们往往需要应对很多挑战,并克服很多困难。所以,到现在为止,MBT并没有被广泛使用于软件测试领域。 + +在这里,我总结了开展MBT的三大难点: + + +学习成本较高。MBT要求开发人员和测试人员都精通建模,这就需要一定的培训成本,需要让开发人员去学习测试的技能,让测试人员去学习建模概念。这其中还牵涉到建模工具的选择,以及学习等成本。 + +使用MBT的初期投资较大。在很多情况下,我们并不能找到适合自己产品的建模工具,而需要自行创建MBT工具。- +在自行定制MBT工具时,我们要考虑到这个工具必须是可扩展的,并且能够处理复杂的测试逻辑,提供足够高的测试覆盖率,因此刚开始的工具建设就需要花费大量时间和精力。- +更糟糕的情况是,当工具建好后,我们却发现它并不能满足所有的建模需求,因此还要在建模的同时对工具进行微调。而,这种微调工作的难度,也比较大。 + +早期根据模型生成测试用例的技术并不是非常成熟。很多时候只是根据图论的算法来遍历模型,这就会导致生成的很多测试用例在业务上根本没有任何实际意义,也因此阻碍了MBT在实际项目中的落地。- +不过好在近一两年来,基于人工智能(AI)生成测试用例的概念不断成熟,所以将基于AI的测试用例生成和MBT相结合,将会是接下来的一个发展方向。 + + +总的来说,MBT和传统测试各有优劣。所以,测试的方法多种多样,MBT只是其中的一种。 + +如果一个应用的任何组件都可以通过模型来模拟、通过驱动程序来驱动,并可以通过测试结果来比较的话,那么这个应用就是MBT的最佳候选者。 + +如果我们的产品特征符合开展MBT的要求,并且团队各方面的条件都支持使用MBT时,我们便可以尝试用这种方法来改革我们的测试方式。尤其是将MBT结合基于AI的测试用例生成技术,将可以大大加速MBT产业应用的步伐。 + +但是,不管是否采用MBT,开发人员或测试人员在接触到一款软件产品时,首先都会有一个心理建模的过程,自己先去理解并在脑中勾勒出系统的功能结构和流程。其实,这些内容很容易就可以转换成实际的模型,也就为MBT创造了条件。 + +总结 + +今天,我和你分享了MBT的基本概念和方法。 + +MBT是一种基于被测系统的模型,由工具自动生成测试用例的软件测试技术。所以,这也就决定了MBT相对于传统测试技术可以在测试用例维护、软件缺陷发现时机、测试自动化水平,以及测试覆盖率等方面有其独到的优势。 + +但同时,这也使得MBT相对于传统软件测试,在初期阶段投入较大,学习应用的成本较高。也正因为如此,MBT概念虽然已经提出了七八年的时间,但却并没有被广泛应用于软件测试领域。 + +所以,关于是否要在自己的项目中开展MBT,我们需要综合考虑项目本身的特点和人员的技术水平,以此决定是否有必要开展MBT。 + +思考题 + +假如你要在项目中开展MBT,你会如何判断你的项目是否适合采用MBT,以及你认为会遇到哪些问题可能会阻碍MBT的开展呢? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/48优秀的测试工程师为什么要懂大型网站的架构设计?.md b/专栏/软件测试52讲/48优秀的测试工程师为什么要懂大型网站的架构设计?.md new file mode 100644 index 0000000..39d777b --- /dev/null +++ b/专栏/软件测试52讲/48优秀的测试工程师为什么要懂大型网站的架构设计?.md @@ -0,0 +1,111 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 48 优秀的测试工程师为什么要懂大型网站的架构设计? + 你好,我是茹炳晟。今天我和你分享的主题是:优秀的测试工程师为什么要懂大型网站的架构设计? + +上周我准备了两期答疑文章,希望可以帮助你解决在阅读前11篇文章时的一些疑问。今天,我们一起回到正题上来,讨论一下互联网的架构设计这个话题。 + +在这个话题里,我会和你分享测试工程师为什么要具备架构知识、怎么学架构知识,以及学到什么程度就可以了。同时,我会针对网站架构设计中最关键的四个主题:高性能架构设计、高可用架构设计、伸缩性架构设计,以及可扩展性架构设计,和你分享一些案例,让你切实体会到懂得架构知识在测试范围确定和用例设计等方面的重要性。 + +为什么要懂得网站的架构设计? + +其实,如果你是工作在传统软件企业的工程师的话,网站的架构设计知识对你来说可能没那么重要。因为,你的测试对象是传统软件,此时你需要对你的被测软件的架构有比较深入的理解。 + +而现在如你所知,互联网企业已经占据软件产品的大半壁江山。如果你想跳出传统软件产品测试这个舒适区的话,那互联网企业将是一个最可能的去向。 + +而在互联网企业进行软件测试的话,很多时候需要针对互联网的架构来设计有针对性的测试,另外对于互联网的压力测试以及结果分析也需要对架构知识有比较清楚的认识。这时,不懂得网站架构设计知识,在开展测试时,就真的会有处处被掣肘的感觉了。更别提,这还会直接影响到你的能力提升和职业发展了。 + +在测试过程中,你可能会经常遇到诸如负载均衡器、缓存集群、数据库读写分离、消息队列、CDN、反向代理服务器和分布式数据库等概念,在测试执行中也经常会和这些系统打交道。但是,很多时候,你只是知道网站在架构设计上有这些组件,并不清楚这些组件真正的作用,在对应的测试设计时也很难做到“有的放矢”。 + +还有些时候,特别是性能测试,如果你不清楚详细的架构设计以及其中的技术细节,你可能根本无去解读和分析性能测试的报告。 + +我来举两个实际的例子吧。 + +基于消息队列的分布式系统测试设计的例子 + +在分布式系统的架构中,为了减少各个应用系统之间的直接耦合,往往会引入消息队列来实现解耦。 + +也就是说原本的功能实现是由系统A调用系统B来完成业务功能的,而引入了消息队列后,系统A不会再直接去调用系统B,而是将调用B所需要的数据放到了消息队列中,此时我们将系统A称为消息的生产者,然后系统B通过监听该消息队列,主动从消息队列中依次抓取数据进行系统B的处理,系统B在这种情况下称为消息的消费者。 + +通过这种方式,就完成了系统A和系统B之间的调用解耦。 + +那么,我们再来看测试的设计。测试用例的设计可以站在黑盒测试的视角,完全不需要知道消息队列的存在,而直接从业务功能的层面去设计用例。 + +但如果只这么做的话,你会发现虽然你的测试全部通过了,但是产品一旦到了线上,还可能会出现很多问题。 + +比如说,消息的生产者产生消息的速度远远大于消息消费者处理消息的速度时,很可能会造成消息队列满的情况,此时系统的行为是怎么样的? + +显然仅仅通过黑盒测试很难完成系统性的、全面的测试。要做到系统性全面的测试设计,你就必须知道消息队列的基本原理,然后在此基础上去设计针对具体架构的测试用例和场景。 + +另外,既然我们的系统设计希望是解耦的,那么我们的测试设计也希望是解耦的。也就是说,对于一些更详细的测试,我们希望系统A和系统B可以被单独的进行测试。 + +那么,这个时候,如果是对系统A进行测试的话,你的测试验证就需要在消息队列中进行。同样的道理,如果你是对系统B进行测试的话,你就需要在消息队列中构造测试输入数据了。 + +由此可见,如果你不知道消息队列的存在以及其基本原理的话,你的测试将寸步难行。 + +缓存的例子 + +很多时候,在我们搭建完性能测试的基准环境,开始执行性能基准测试的时候,往往会发现系统刚开始运行时业务处理的响应时间都会相对比较长,只有当性能测试执行了一段时间后,系统的各项指标以及事务的响应时间才逐渐趋于正常。 + +为此,在做性能基准测试的时候,有经验的工程师通常都会先用性能场景对系统进行一下“预热”,然后再真正开始测试。你有想过这其中的原因吗? + +另外,在做前端性能测试的时候,我们对于一个页面的打开时间通常会去统计两个指标,一个是首次打开时间,另一个是多次打开的时间。而且,通常来讲首次打开时间会远大于后面再次打开的时间。你有想过这其中的原因吗? + +其实,造成上述两种情况的背后原因都是采用了缓存技术。 + +造成第一个情况的原因,是服务器端会对“热点”数据进行缓存,而不是每次访问都直接从数据库中获取数据。那么,系统刚开始运行时,由于没有任何之前的访问记录,所有数据都需要访问数据库,所以前期的事务响应时间都会比较长。但是,随着缓存的建立,后续的访问就会比较快了。这个前期对系统的“预热”过程其实是在“预热”缓存。 + +对于第二种情况也是同样的道理。浏览器端也会缓存从服务器端拿到各种静态资源,在第一次访问时这些资源都需要从服务器端获取,而后面再访问时,这些静态资源已经在浏览器的缓存中了,所以访问速度会大大加快。 + +由此可见,如果不知道缓存的存在、不理解缓存的基本原理,你就不可能从根本上理解性能测试的方法设计以及测试结果数据。 + +其实,对于缓存还有很多需要考虑的测试点,但是需要解释这些测试点就需要深入理解缓存的原理以及缓存的架构设计,因为在互联网环境下,缓存本身也是分层的,浏览器端有本地缓存、网络端有CDN缓存、数据中心前端有反向代理的缓存、应用服务器端有本地缓存,对于大规模互联网应用更有大规模的专用缓存服务器集群。所以,要有针对性的设计缓存相关的测试场景,就需要理解这些缓存的架构。 + +那么,接下来我就和你聊聊作为测试工程师应该怎么学习架构知识。 + +测试工程师怎么学架构知识? + +其实,对于测试工程师来说,学习软件架构和系统架构知识的确是个不小的挑战。因为很多架构知识都是基于开发框架和系统设计的,对开发工程师来说,已经是个不小的挑战,对测试工程师来说更是一个难以驾驭的领域。 + +不过好在,同样是对架构知识的学习和掌握,不同角色的工程技术人员都有不同的视角,需要了解和掌握的全局知识和细节程度也各不相同。以消息队列知识为例: + + +如果你是系统架构师,那么你就不仅要掌握各个不同消息队列实现的技术细节,还清楚不同方案的优势和劣势,最关键的是能够根据业务的应用场景和特点来选择最合适的消息队列方案。 +如果你是软件开发人员,那么你就需要掌握消息队列的使用方法、消息push和pull的模式,以及在应用中如何以异步方式来对消息进行妥善处理,并且还要考虑到异常场景的处理。 +而作为软件测试人员,你需要知道消息队列的基本原理以及在被测系统中的部署情况,同时应该知道如何访问消息队列或者队列中消息的情况。在需要模拟消息进行解耦测试的场合,你还需要知道如何添加测试消息以满足测试的目的。 + + +可见,对于测试人员来讲,学习架构知识应该有自己独特的视角,基本只要做到清楚原理、了解在被测系统中的部署架构,从测试的角度能够调用必要的接口就可以了。 + +那么,我们到底应该怎么来学习架构知识呢?根据我的个人经验,我认为应该遵循“由广度到深度”和“自上而下”两个基本原则。 + +“由广度到深度”中的“广度”是指在平时工作以外的时间中,应该多注重全领域架构知识的积累,此时那些系统性地介绍架构知识的书籍或者专栏就可以给你最大程度的帮助了。 + +因为这类资料往往已经对纷繁复杂的架构知识做了系统性地梳理。这里,我个人非常推荐极客时间李运华老师的“从0开始学架构”专栏,以及李智慧老师所著的图书《大型网站技术架构:核心原理与案例分析》。它们都能帮你从广度上积累架构知识。 + +“由广度到深度”的“深度”是指,对于架构中某一领域的特定知识在项目中要实际使用的时候,必须要刨根问底,通过实际的测试来加深对架构知识细节的理解。 + +“自上而下”是指,在实际测试项目中,当需要设计涉及架构的测试用例和场景的时候,千万不要直接基于“点”来设计测试,而是应该:首先通过全局阅读理解上层架构设计;然后,在理解了架构设计的初衷和希望达成目的的基础上,再向下设计测试场景和用例。 + +这个过程,一方面可以帮你设计出有针对性的测试用例,另一方面可以帮助你理解架构在实际项目中是如何落地的。 + +随着你经历的项目越来越多,你的架构知识就会逐渐充实丰满起来。这就好比你在走一个旋转楼梯,一直感觉自己在原地打转,但是不知不觉走了一段时间后你回头往下看的时候,就会发现已经站在了比原来更高的点上。 + +最后,我再特别提一下,对于架构知识的学习没有任何捷径可走,你必须一步一个脚印,才能达到下一个高峰。 + +总结 + +今天我通过消息队列和缓存两个实例给你讲解了测试工程师学习架构知识的重要性,并且从我个人的经验出发,提出了“由广度到深度”和“自上而下”的架构学习思路,最后指出了学习架构没有捷径,你必须一步一个脚印夯实自己的知识结构。 + +思考题 + +对于架构知识的学习,我只是给出了我的一些方法和意见,你对此还有什么其他的想法或者学习方法吗? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/49深入浅出网站高性能架构设计.md b/专栏/软件测试52讲/49深入浅出网站高性能架构设计.md new file mode 100644 index 0000000..33cf5bf --- /dev/null +++ b/专栏/软件测试52讲/49深入浅出网站高性能架构设计.md @@ -0,0 +1,139 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 49 深入浅出网站高性能架构设计 + 你好,我是茹炳晟。今天我和你分享的主题是:深入浅出网站高性能架构设计。 + +在上一篇文章中,我从全局的角度和你分享了测试人员学习架构知识的重要性、应该学到什么程度,以及怎么学的问题,希望你可以借此明白网站架构的why、what、how。 + +接下来,我将站在测试人员的视角,通过这个专栏的最后四篇文章,和你分享网站的高性能架构、高可用架构、伸缩性架构以及扩展性架构。希望借此机会,可以让你对网站的架构设计做到心中有数,在设计测试用例时可以做到有的放矢。 + +今天我们就先从网站高性能架构的设计开始吧。 + +性能是网站的重要指标,如果一个网站的访问速度很慢,就会直接导致大量用户的流失。所以说,性能是设计网站架构时要考虑的关键因素。也因此,网站的性能问题成了网站架构升级、优化的导火索。 + +目前,为了优化网站性能,业界出现了很多相关的架构改进方案和技术手段。而包括了这些升级、优化网站性能的方案、技术手段在内的高性能架构设计,是个很大的话题,单单依靠几篇文章是很难讲清楚的。所以,我从中精选了一些对测试工程师比较关键的概念和技术,和你展开今天的分享。 + +如果你想了解更细节的技术实现的话,可以参考我在上一篇文章中推荐的学习资料,也可以直接在留言区给我留言。 + +从全局来看,网站的高性能架构设计包括两大部分内容:一是前端性能,二是后端服务器相关的性能优化和架构设计。 + +前端的高性能架构 + +关于什么是前端性能,以及如何设计针对前端性能的测试,你可以直接参考第31篇文章《工欲善其事必先利其器:前端性能测试工具原理与行业常用工具简介》中的相关内容。 + +相对来说,前端高性能架构比较直观易懂,其本质就是通过各种技术手段去优化用户实际感受到的前端页面展现时间。 + +目前,业内的标准实践是来自于雅虎前端性能团队提出的35条原则,我已经在第29篇文章《聊聊性能测试的基本方法与应用领域》中,为你解读了其中几个比较典型的规则,你可以再回顾下。同时,你还可以访问雅虎网站查看经典的35条规则,以及对各规则的详细解读。 + +前端的高性能架构相对于后端来讲比较容易实现,因为前端性能优化的方法是相对标准的。而且,目前的前端性能测试工具,比如我在前面文章中曾经介绍过的WebPageTest和YSlow之类的工具等,都能系统性地分析前端的性能问题,并给出对应的解决方案建议。 + +可以说,我们只要在项目开发过程中,把前端性能优化纳入了测试范围,那么一般来讲都能获得比较理想的性能优化结果。 + +后端服务器的高性能架构 + +后端服务器的高性能架构,业内采用的最主要的技术手段是缓存。同时,集群也可以从计算能力的角度,提升后端的处理性能。 + +缓存 + +可以说,在计算机的世界中,凡是想要提高性能的场合都会使用到缓存的思想。缓存是指将数据存储在访问速度相对较快的存储介质中,所以从缓存中读取数据的速度更快。 + +另外,如果缓存中的数据是经过复杂计算得到的,那么再次使用被缓存的数据时,就无需再重复计算即可直接使用。从这个意义上讲,缓存还具有降低后端运算负载的作用。 + +可见,缓存在软件系统和网站架构中几乎无处不在。当然了,在系统和软件的不同级别对应有不同层级的缓存: + + +浏览器级别的缓存,会用来存储之前在网络上下载过的静态资源; +CDN本质也是缓存,属于部署在网络服务供应商机房中的缓存; +反向代理服务器本质上同样也是缓存,属于用户数据中心最前端的缓存; +数据库中的“热点”数据,在应用服务器集群中有一级缓存,在缓存服务集群中有二级缓存; +甚至是用于URL和服务器IP地址转换DNS服务器,为了减少重复查询的次数也采用了缓存。 + + +启用了缓存后,当应用程序需要读取数据时,会先试图从缓存中读取: + + +如果读取成功,我们称为缓存命中,此时就可以在很大程度上降低访问数据库的时间开销。 +如果没有读取到数据或者缓存中的数据已经过期失效,那么应用程序就会访问数据库去获取相应的数据。获取到数据后,在把数据返回给应用程序的同时,还会把该数据写入到缓存中,以备下次使用。 + + +缓存主要用来存储那些相对变化较少,并且遵从“二八原则”的数据。这里的“二八原则”指的是80%的数据访问会集中在20%的数据上。 + +也就是说,如果我们将这20%的数据缓存起来,那么这些数据就会具有非常高的读写比。读写比越高,说明缓存中的数据被使用的次数也就越多,从而节省的数据库访问也就越多,缓存的优势也就越明显。 + +需要特别注意的是,缓存技术并不适用于那些需要频繁修改的数据。对于这种需要频繁修改的数据来说,经常会出现刚刚写入缓存的数据还没来得及被读取就已经失效了的场景。所以,在这种情况下,缓存不仅不会带来性能提升,反而会增加系统开销。 + +从理论上来讲,缓存的作用是辅助提升数据的读取性能,缓存数据丢失或者缓存不可用不应该影响整个系统的可用性,因为即使没有了缓存,数据依旧可以从数据库中获得。但是,现在的数据库已经习惯了有缓存的日子,假如哪天缓存系统奔溃了,就会在短时间内有大量的请求来访问数据库,数据库就很可能会因为无法承受这样的并发压力而宕机。 + +为了解决这个问题,有些网站会使用缓存热备等技术手段来提供缓存的高可用性,即:当某台缓存服务器宕机的时候,会将缓存访问切换到热备的缓存服务器上。 + +另外,如果你采用了分布式缓存服务器集群的话,那么缓存的数据将被分布到集群中的多台服务器上,当其中一台服务器宕机的时候,也只会丢失一部分缓存数据,此时通过访问数据库来重建这些缓存数据的开销并不算太大。 + +目前,分布式缓存架构的主流技术方案有两种: + + +一种是,在企业级应用中广泛采用的JBoss Cache。JBoss Cache需要在缓存集群中的每台机器上同步所有缓存的副本,当集群规模比较大的时候,同步代价会很高。而且,多份副本也会造成存储资源的浪费。但其最大的优点是速度非常快,所以JBoss Cache更适用于企业级规模不是很大的缓存集群。这种企业级的集群一般在几台到十几台服务器的规模。 +另一种是,在互联网应用的主流Memcached。Memcached属于互不通信的分布式架构,集群中各个节点缓存的数据都不一样,缓存使用者基于Hash一致性算法来定位具体的内容到底缓存在集群中的哪个节点。- +因此,Memcached具有缓存容量大,存储效率高,可以很方便地支持集群的扩展,但是速度相对较慢的特点。这些特点决定了Memcached非常适用于现如今的互联网产品架构,几乎成为了网站分布式缓存架构的代名词。 + + +互联网产品架构的应用服务器集群规模一般都很大,即使小规模的应用集群也有上百台机器,规模大的话可以达到上万台,这种架构下的缓存集群规模要求也非常大。 + +通过上面这些些缓存的基础知识,再结合着你在平时项目中积累的相关经验,相信你已经理解了缓存的原理。那么,接下来我们再从测试人员的视角来看看,在执行测试时需要考虑到哪些与缓存相关的测试场景: + + +对于前端的测试场景,需要分别考虑缓存命中和缓存不命中情况下的页面加载时间。 +基于缓存过期测试策略的设计,需要考虑到必须要重新获取数据的测试场景。 +需要针对可能存在的缓存“脏数据”,进行有针对性的测试。缓存“脏数据”,是指数据库中的数据已经更新,但是缓存中的数据还没来得及更新的场景。 +需要针对可能的缓存穿透进行必要的测试。缓存穿透,是指访问的数据并不存在,所以这部分数据永远不会有被缓存的机会,因此此类请求会一直重复访问数据库。 +系统冷启动后,在缓存预热阶段的数据库访问压力是否会超过数据库实际可以承载的压力。 +对于分布式缓存集群来说,由于各集群使用的缓存算法不同,那么如果要在缓存集群中增加更多节点进行扩容的话,扩容对原本已经缓存数据的影响也会不同。所以,我们需要针对缓存集群扩容的场景,进行必要的测试和性能评估。 + + +集群 + +集群也是提升网站性能和并发处理能力的典型架构设计方法。 + +当一台服务器不足以满足日益增长的用户流量时,我们就可以考虑使用多台服务器来组成一个集群:外部请求将统一和负载均衡器打交道;负载均衡器根据不同的负载调度算法,将访问请求传递给集群中的某台服务器处理。 + +需要注意的是,在这种模式下,集群中的任何一台服务器宕机都不会给整个系统带来明显的影响。此时,每台服务器的地位也都不怎么高,我们可以直接替换掉出现了问题的某台服务器。同样地,当需要支持更大的系统负载时,我们就可以在集群中添加更多的服务器。 + +这时,集群中的每台服务器都可以被随时替换或者淘汰掉,就像“牲口”似的可以任人宰割。所以,这种模式,就有点类似于“牲口”模式。 + +与“牲口”模式对应的是“宠物”模式,比如一些企业级的应用,它们往往不通过集群来扩展系统的能力和提高系统的性能,而是采用更为强劲的服务器。 + +这种性能非常强大的单台服务器,价格往往十分昂贵,所以通常都会被特别关照,比如给其配备最好的机房和UPS等等。另外,大家都不敢对这样的服务器有任何大的动作,生怕把它们搞坏了。此时,这些价格昂贵的服务器更像是“宠物”。 + +综上所述,现今的互联网应用采用的都是“牲口”模式。在这种模式下,我们在开展测试时,相应地需要额外关注以下这些测试点: + + +集群容量扩展。也就是说,集群中加入新的节点后,是否会对原有的session产生影响。 +对于无状态应用,是否可以实现灵活的实效转移。 +对于基于session的有状态应用,需要根据不同的session机制验证会话是否可以正常保持,即保证同一session始终都有同一个确定的节点在处理。 +当集群中的一个或者多个节点宕机时,对在线用户的影响是否符合设计预期。 +对于无状态应用来说,系统吞吐量是否能够随着集群中节点的数量呈线性增长。 +负载均衡算法的实际效果,是否符合预期。 +高并发场景下,集群能够承载的最大容量。 + + +总结 + +今天,我以测试人员的视角,和你分享了网站高性能架构设计中,需要重点关注的点。 + +首先,网站的性能,在很大程度上和用户的体量有直接关系。因此,开发人员在设计网站架构时,必须要重点考虑与性能相关的架构设计。相应地,测试人员在测试网站性能时,也要考虑到这其中的架构设计。 + +其次,网站高性能架构设计,主要包括前端性能优化和后端服务器的性能调优。所以,我从这两个方面,和你展开了今天的分享。测试人员在理解了两大部分知识的基础上,在设计具体的测试时,要考虑到这些网站高性能架构设计的方案、技术手段,以此制定出需要额外增加的测试点,以及对应的测试方法。 + +最后,关于网站性能测试的理论与方法,你可以参考我在第28~34篇文章(也就是性能测试系列文章)中的相关内容。 + +思考题 + +在你接触过的项目中,都遇到过哪些系统层面的高性能架构设计方案?从测试的角度来看,你又会设计怎样的测试场景和用例? + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/50深入浅出网站高可用架构设计.md b/专栏/软件测试52讲/50深入浅出网站高可用架构设计.md new file mode 100644 index 0000000..995349b --- /dev/null +++ b/专栏/软件测试52讲/50深入浅出网站高可用架构设计.md @@ -0,0 +1,137 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 50 深入浅出网站高可用架构设计 + 你好,我是茹炳晟。今天我和你分享的主题是:深入浅出网站高可用架构设计。 + +在今天这篇文章中,我将沿着网站架构的话题,和你继续聊聊高可用的架构设计。 + +顾名思义,网站高可用指的就是,在绝大多的时间里,网站一直处于可以对外提供服务的正常状态。业界通常使用有多少个“9”来衡量网站的可用性指标,具体的计算公式也很简单,就是一段时间内(比如一年)网站可用的时间占总时间的百分比。 + +我用下面这个表格,列出了四种最常见的可用性等级指标,以及允许的系统不可用时长。 + + + +一般,我们以“年”为单位来统计网站的可用性等级。“9”的个数越多,一年中允许的不可用时间就越短,当达到5个“9”的时候,系统全年不可用时间只有区区5分钟,可想而知这个指标非常难达到。 + +所以一般来讲,业界的网站能做到4个“9”,也就是说在一年内只有53分钟的时间网站是处于不可用状态,就已经是算是非常优秀了。 + +另外,可用性指标还有个特点,越往后越难提高,需要付出的经济成本和技术成本都会呈现类似指数级的增长。因此,在实际的网站架构设计过程中,到底需要做到几个“9”还需要结合具体的业务要求,以及风险评估来最终确定。 + +那么,接下来我就首先和你分析一下造成网站不可用的主要原因,然后再基于这些原因谈谈我们可以通过哪些对策和方法,将这些造成网站不可用的因素的影响降到最低。 + +其实,造成网站不可用的主要原因有以下三大类: + + +服务器硬件故障; + +发布新应用的过程; + +应用程序本身的问题。 + + +服务器硬件故障 + +网站物理架构中,随机的硬件服务器的故障,比如某台服务器由于硬件故障宕机,可以说不是偶然,而是必然会发生的。尤其是目前互联网企业普遍采用的“牲口”模式集群方案。 + +而且随着网站规模不断扩大,网站后台的服务器数量也越来越多,所以由硬件故障引起问题的概率也是不断飙升。 + +所以,网站的高可用架构设计,需要保障的是即使出现了硬件故障,也要保证系统的高可用。 + +发布新应用的过程 + +网站的新版本发布过程中,往往会出现需要重新部署新的应用程序版本,然后再重启服务的情况。如果这个更新过程中不采用特殊技术手段的话,也会造成短暂的服务不可用。而且这种形式的不可用,相比服务器硬件故障的不可用更为常见。 + +原因很简单,互联网网站的功能更新迭代非常快,基本都是以“天”为单位来发布上线的,也就是说几乎每天都有需要中断服务来完成服务升级的可能。 + +显然,从业务角度来看,这种为了应用升级造成的服务不可用,完全不可能被接受。这就好比eBay或者淘宝告诉你说,我们每天某个时间段需要内部升级维护无法对外提供服务一样,让人无法接受。 + +从网站可用性指标的角度来看,这种频繁出现的停机升级过程将大大增加网站的不可用时间。因此,我们的高可用架构设计必须能够提供切实可行的方案,将这种停机升级的影响降到最小。 + +应用程序本身的问题 + +造成网站不可用的最后一个原因是,应用程序本身的问题。 + +比如,发布的应用程序版本身存在潜在的内存泄露,那么经过较长时间的运行积累后,最终会造成服务器的内存被占满,之后必须要靠重启服务来恢复。那么,这个时候就会引入短暂的服务不可用时间。 + +再比如,应用程序在测试环境没有经过充分的测试验证,或者说由于测试环境的配置和实际生产环境之间存在差异,有可能造成应用程序在生产环境部署完后无法使用的情况,从而造成服务不可用。 + +由此可见,应用程序在上线发布前进行充分、全面的测试,是多么的重要。无论是立竿见影就能发现的功能缺陷,还是需要长期运行才能暴露的软件问题,都可以通过软件测试去发现,然后反馈给开发人员去解决,从而避免造成系统的不可用。同时,我们也需要尽可能减少测试环境和生产环境的差异,尽可能采用完全相同的环境以及第三方依赖。 + +网站高可用架构设计 + +为了系统性地解决造成系统不可用的上述三类问题,提高网站的可用性,我们在网站高可用架构设计上,探索出了对应的三类方法。 + + +第一类方法是,从硬件层面加入必要的冗余; +第二类方法是,灰度发布; +第三类方法是,加强应用上线前的测试,或者开启预发布验证。 + + +对于第一类硬件故障造成的网站不可用,最直接的解决方案就是从硬件层面加入必要的冗余,同时充分发挥集群的“牲口”优势。 + +比如,对于应用服务器来说,即使没有伸缩性的要求,我们也会至少采用两台同样的服务器,并且引入一台额外的负载均衡器,所有的外部请求会先到负载均衡器,然后由负载均衡器根据不同的分配算法选择其中的某一台服务器来提供服务。 + + +备注:伸缩性是指通过增加或减少服务器的数量,就可以扩大或者减小网站整体处理能力。我会在下一篇文章中和你详细分享。 + + +这样,当其中一台服务器硬件出现问题甚至宕机的时候,另一服务器可以继续对外提供服务。这时,在外部看来系统整体依然是可用的,这就给恢复那台故障服务器提供了时间。而两台服务器同时出现硬件故障的概率是很低的。 + +因此,从测试人员的角度来看,知道了应用服务器集群的工作原理,就可以在设计测试的时候,针对集群中的某一个或者某几个节点的故障情况设计测试用例。 + +再比如,对于数据存储的服务器来说,往往通过数据冗余备份和失效转移机制来实现高可用。为了防止存储数据的服务器发生硬件故障而造成数据丢失,我们往往会引入多个数据存储服务器,并且会在数据有更新操作的时候自动同步多个数据存储服务器上的数据。 + +也就是说,数据的存储存在多个副本,那么当某台数据存储服务器故障的时候,我们就可以快速切换到没有故障的服务器,以此保证数据存储的高可用。 + +那么,从测试人员的角度来看,我们依旧可以针对这种情况设计出针对部分数据服务器发生故障时的测试用例,以完成系统应对故障的反应情况的测试。 + +对于第二类由于发布新应用造成的系统不可用,我们采用的主要技术手段是灰度发布。 + +使用灰度发布的前提是,应用服务器必须采用集群架构。假定现在有一个包含100个节点的集群需要升级安装新的应用版本,那么这个时候的更新过程应该是: + + +首先,从负载均衡器的服务器列表中删除其中的一个节点; +然后,将新版本的应用部署到这台删除的节点中并重启该服务; +重启完成后,将包含新版本应用的节点重新挂载到负载均衡服务器中,让其真正接受外部流量,并严密观察新版本应用的行为; +如果没有问题,那么将会重复以上步骤将下一个节点升级成新版本应用。如果有问题,就会回滚这个节点的上一个版本。 +如此反复,直至集群中这100个节点全部更新为新版本应用。 + + +在这个升级的过程中,服务对外来看一直处于正常状态,宏观上并没有出现系统不可用的情况。就好比是为正在飞行中的飞机更换引擎,而飞机始终处于“正常飞行”的状态一样。 + +对于第三类应用程序本身的问题造成的系统不可用,我们一方面要加强应用程序上线部署前的测试以保证应用本身的质量,另一方面需要启用所谓的预发布验证。 + +我们一定遇到过这样的尴尬情况:应用在测试环境中经过了完整、全面的测试,并且所有发现的缺陷也已经被修复并验证通过了,可是一旦发布到了生产环境,还是立马暴露出了很多问题。 + +这其中的主要原因是,测试环境和生产环境存在差异。比如,网络环境的限制可能不一样;再比如,依赖的第三方服务也可能不一样,测试环境连接的是第三方服务的沙箱环境,而生产环境连接的是真实环境。 + +为了避免这类由于环境差异造成的问题,我们往往会预发布服务器。预发布服务器和真实的服务所处的环境没有任何差别,连接的第三方服务也没有任何差别,唯一不同的是预发布服务器不会通过负载均衡服务器对外暴露,只有知道其IP地址的内部人员才可以对其进行访问。 + +此时,我们就可以借助自动化测试来对应用做快速的验证测试。如果测试通过,新的应用版本就会进入到之前介绍的灰度发布阶段。这种做法,可以尽最大可能保证上线应用的可用性。 + +总结 + +今天我和你分享了衡量网站高可用性的指标,对于一些大型网站来说,达到4个“9”(即99.99%,一年中的不可用时间不超过53分钟)已经算是优秀了。 + +然后,我将影响网站高可用的因素归为了三类,并相应地给出了解决这三类问题的方案: + + +由服务器硬件故障引起的网站不可用,对应的解决方案是从硬件层面加入必要的冗余; + +由发布新应用的过程引入的网站不可用,对应的解决方案是采用灰度发布的技术手段; + +由应用本身质量引入的网站不可用,对应的解决方案是,一方面加强测试提高应用本身的质量,另一方面是引入预发布服务器消除测试环境和生产环境的差异。 + + +思考题 + +关于高可用架构设计,我在文章中和你分享了应用服务器和数据存储服务器的高可用架构。但是,我并没有介绍缓存服务器的高可用架构。那么,你认为缓存服务器是否也需要高可用架构的支持呢?如果需要的话,缓存集群的高可用架构应当如何设计?如果不需要,也请你分享一下你的理由。 + +感谢你的收听,欢迎你给我留言。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/51深入浅出网站伸缩性架构设计.md b/专栏/软件测试52讲/51深入浅出网站伸缩性架构设计.md new file mode 100644 index 0000000..e40cb92 --- /dev/null +++ b/专栏/软件测试52讲/51深入浅出网站伸缩性架构设计.md @@ -0,0 +1,175 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 51 深入浅出网站伸缩性架构设计 + 你好,我是茹炳晟,今天我分享的主题是:深入浅出网站伸缩性架构设计。 + +目前,很多测试工程师,甚至是开发工程师都一直分不清楚可伸缩性和可扩展性这两个概念,主要原因是从字面上看这两个概念的确有相似之处。但实际情况呢,可伸缩性和可扩展性这两两个概念的含义相差十万八千里,根本不具有任何可比性。 + +所以,我将通过两篇文章来和你分享,网站的伸缩性和可扩展性架构设计到底是什么,以及在设计测试用例时需要注意哪些点。 + +可伸缩性和可扩展性的概念区别 + +可伸缩性翻译自Scalability,指的是通过简单地增加硬件配置而使服务处理能力呈线性增长的能力。最简单直观的例子,就是通过在应用服务器集群中增加更多的节点,来提高整个集群的处理能力。 + +而可扩展性翻译自Extensibility,指的是网站的架构设计能够快速适应需求的变化,当需要增加新的功能实现时,对原有架构不需要做修改或者做很少的修改就能够快速满足新的业务需求。 + +接下来,我就先和你分享下网站的可伸缩性架构。而关于网站的可扩展性架构设计,我会在下一篇文章中和你详细分享。 + +分层的可伸缩性架构 + +网站的可伸缩性架构设计主要包含两个层面的含义: + + +一个是指,根据功能进行物理分离来实现伸缩; +另一个是指,物理分离后的单一功能通过增加或者减少硬件来实现伸缩。 + + +在根据功能本身进行物理分离来实现伸缩的过程中,还有两种不同的实现方式: + + +一种是功能的“横切”,比如一个电商网站的购物功能从上至下就可以分为界面UI层、业务逻辑处理层、公共服务层和数据库层,如果我们将这些层区分开来,每个层就可以独立实现可伸缩; + + + + +图1 功能“横切”示意图 + + +另一种是功能的“纵切”,比如一个电商网站可以根据经营的业务范围(比如书店、生鲜、家电和日化用品等)进行功能模块的划分,划分后的每个业务模块都可以独立地根据业务流量和压力来实现最适合自己规模的伸缩性设计。 + + + + +图2 功能“纵切”示意图 + +同样地,对于单一功能可以通过增加或者减少硬件来实现的可伸缩性,也有两种不同的实现方式: + + +一种是纵向的可伸缩性,指的是通过增加单一服务器上的硬件资源来提高处理能力。比如,在现有服务器上增加CPU、内存,或者在现有的RAID/SAN存储中增加硬盘等。- +传统软件企业使用的“宠物”模式,就是通过这个思路来实现有限的可伸缩性,我们往往把这种方式的伸缩性称为单节点的可伸缩性。显然,在如今海量互联网流量的情况下,想仅仅依赖于某一台服务器来处理各种请求显然是不可能的。 +另一种是横向的可伸缩性,指的是通过使用服务器集群来实现单一功能的可扩展性。当一台机器不足以处理大量用户并发请求的时候,我们就采用多台机器组成集群来共同负担并发压力。- +这种方式是基于集群的可伸缩性实现的,也是目前最主流的网站可伸缩性方法,也就是我之前提到过的“牲口”模式。很多时候当我们谈及网站的可伸缩性设计时,如果没有特定的上下文或者特指的场景,往往指的都是基于集群的可伸缩性。 + + + + +图3 单一功能通过增加或者减少硬件来实现的可伸缩性 + +基于集群的可伸缩性设计,是和网站本身的分层架构设计相对应的: + + +在应用服务器层面有应用服务器集群的可伸缩性架构设计; +在缓存服务器层面有缓存服务器的可伸缩性架构设计; +在数据库层面有数据库服务器的可伸缩性架构设计。 + + +虽然都是可伸缩性设计,但是由于应用服务器、缓存服务器和数据库服务器本身的架构在设计上就有所区别,加之它们的使用场景不同,使得它们的可伸缩性架构设计就有着巨大的差异。 + +接下来,我就先简单解释一下这三个层面的可伸缩性设计指的是什么,以及从测试人员的角度来看我们需要关注哪些点。 + +应用服务器的可伸缩性设计 + +应用服务器的可伸缩性设计是最直观,也是最容易理解的。当一台应用服务器不足以支撑业务流量的时候,我们就可以用多台服务器来分担业务流量。 + +但是,为了保证这批服务器对外暴露的是一个统一的节点,我们就需要一个负载均衡器作为统一的窗口来对外提供服务,同时负载均衡器会把实际的业务请求转发给集群中的机器去具体执行。 + +这里需要特别注意的是,负载均衡器并不是按照你在字面上理解的“均衡”那样,把业务负载平均分配到集群中的各个节点,而是通过负载均衡算法(比如轮询算法、基于加权的轮询算法、最小链接算法等)将用户流量分配到集群机器。从这个意义上说,将负载均衡器称为任务分配器才更合适。 + + + +图4 通过负载均衡器实现的应用服务器集群示意图 + +为了实现线性可伸缩性,我们希望应用本身是无状态的。此时,任何请求都可以在集群中任意节点上来执行,也就是说集群的处理能力将随着节点数量的增多呈现线性增长的态势。 + +但是,如果应用本身是有状态的,那么就会要求基于一次会话(session)的多次请求都被分配到集群中某一台固定的服务器上去执行。 + +理解了上述应用服务器集群的可伸缩性架构原理后,我们再从测试人员的角度来想想,应该考虑哪些相关的测试场景。为此,我总结了以下几点供你参考: + + +需要通过压力测试来得出单一节点的负载承受能力; +验证系统整体的负载承受能力,是否能够随着集群中的节点数量呈现线性增长; +集群中节点的数量是否有上限; +新加入的节点是否可以提供和原来节点无差异的服务; +对于有状态的应用,是否能够实现一次会话(session)的多次请求都被分配到集群中某一台固定的服务器上; +验证负载均衡算法的准确性。 + + +缓存集群的可伸缩性设计 + +缓存集群的可伸缩性设计,相比应用服务器集群要复杂得多。 + +传统的缓存服务器集群是无法通过简单地加入新的节点来实现扩容的,其中的根本原因,就要从缓存的核心原理开始讲起了。 + +假定,一个缓存集群中有3台机器,那么我们在将需要缓存的内容存入缓存集群的过程,包括了这三步: + + +首先,将需要缓存的内容的Key值做Hash运算; +然后,将得到的Hash值对3取余数; +最后,将缓存内容写入余数所代表的那台服务器。 + + +而此时,如果我们在缓存集群中加入了一台新的机器,也就是说缓存集群中机器的数量变成了4。这时Key的Hash值就应该对4取余,你会发现这么一来,原本已经缓存的绝大多数内容就都失效了,必须重构整个缓存集群。而这,显然不能被接受。 + +为了解决上述这个问题,使得缓存集群也可以做到按需、高效地伸缩,那就必须采用更为先进的Hash一致性算法。这个算法可以很巧妙地解决缓存集群的扩容问题,保证了新增机器节点的时候大部分的缓存不会失效。 + +如果你想了解Hash一致性算法更详细的细节,请自行百度。 + +同样地,知道了缓存集群扩容的实现细节后,我们再从测试人员的角度出发,看看需要额外关注哪些点。这里,我总结了以下几点供你参考: + + +针对缓存集群中新增节点的测试,验证其对原有缓存的影响是否足够小; +验证系统冷启动完成后,缓存中还没有任何数据的时候,如果此时网站负载较大,数据库是否可以承受这样的压力; +需要验证各种情况下,缓存数据和数据库数据的一致性; +验证是否已经对潜在的缓存穿透攻击进行了处理,因为如果有人刻意利用这个漏洞来发起海量请求的话,就有可能会拖垮数据库。 + + +数据库的可伸缩性设计 + +从实际应用的角度来看,数据库的可伸缩性设计主要有四种方式: + +第一种方式是目前最常用的业务分库,也就是从业务上将一个庞大的数据库拆分成多个不同的数据库。比如,对于电商网站来说,它们可以考虑将用户相关的表放在一个数据库中,而商品相关的表放在另一个数据库中。 + +这种方式本身也符合模块设计分而治之的思想,但最大的问题是跨数据库数据的join操作只能通过代码在内存中完成,实现代价和成本都比较高。这种方式目前在一些中大型电商有不同程度的应用。 + +第二种方式是读写分离的数据库设计,其中主库用于所有的写操作,从库用于所有的读操作,然后主从库会自动进行数据同步操作。这样一来,主库就可以根据写操作来优化性能,而从库就可以根据读操作来优化性能。 + +但是,这个架构最大的问题在于可能出现数据不一致的情况。比如,写入的数据没能及时同步到从库,就可能会出现数据不一致。另外,这种读写分离的设计对数据库可伸缩性的贡献来讲,比较有限,很难从根本上解决问题。 + +这种方式主要应用在中小型规模的网站中,同时读写分离的设计也通常会和业务分库的设计一起采用,来提高业务分库后的数据库性能。 + +为了进一步提高数据库的可伸缩性,于是就出现了第三种数据库的可伸缩性设计:分布式数据库。分布式数据库同样存在数据不一致的问题,并且,这个方法通常只在单个数据表异常庞大的时候才会被采用,否则我还是更推荐业务分库的方法。这种数据库设计可以说是比较主流的应对大规模高并发应用的数据库方案。 + +第四种方式则是完全颠覆了传统关系型数据数据库的NoSQL设计。NoSQL放弃了事务一致性,并且天生就是为了可伸缩性而设计的,所以在可伸缩性方面具有天然优势。因此,在互联网领域被广泛使用。 + +从测试的角度出发,无论是数据库架构哪种设计,我们一般都会从以下几个方面来考虑测试用例的设计: + + +正确读取到刚写入数据的延迟时间; +在数据库架构发生改变,或者同样的架构数据库参数发生了改变时,数据库基准性能是否会发生明显的变化; +压力测试过程中,数据库服务器的各项监控指标是否符合预期; +数据库在线扩容过程中对业务的影响程度; +数据库集群中,某个节点由于硬件故障对业务的影响程度。 + + +总结 + +首先,我和你分享了可伸缩性翻译自Scalability,而可扩展性翻译自Extensibility,从英文单词的含义上我们就可以看出这两个概念间的差异了。 + +在我看来,网站的可伸缩性架构设计主要包含两个层面的含义,一个是指根据功能进行物理分离来实现伸缩,另一个是指物理分离后的单一功能通过增加或者减少硬件来实现伸缩。 + +从整体架构的角度来看,应用服务器、缓存集群和数据库服务器各自都有适合自己的可伸缩性设计策略:应用服务器主要通过集群来实现可伸缩性,缓存集群主要通过Hash一致性算法来实现,数据库可以通过业务分库、读写分离、分布式数据库以及NoSQL来实现可伸缩性。 + +而相应的理解了网站的可伸缩性架构设计后,我们在开展测试时,就可以非常自信地设计出有针对性的测试用例了。 + +思考题 + +你所接触的被测系统,是否采用了可伸缩性的架构设计方案?在具体开展测试的时候,你又是如何设计测试用例的呢? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/52深入浅出网站可扩展性架构设计.md b/专栏/软件测试52讲/52深入浅出网站可扩展性架构设计.md new file mode 100644 index 0000000..1c8088c --- /dev/null +++ b/专栏/软件测试52讲/52深入浅出网站可扩展性架构设计.md @@ -0,0 +1,120 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 52 深入浅出网站可扩展性架构设计 + 你好,我是茹炳晟。今天我和你分享的主题是:深入浅出网站可扩展性架构设计。 + +在上一篇文章中,我从可伸缩性和可扩展性对应的英文术语的角度,和你分享了这两个概念的差异,并且和你详细介绍了网站的可伸缩性架构设计主要包括的内容,以及从测试人员的视角需要关注哪些点进行针对性的测试。 + +所以,在今天这篇文章,也是这个专栏最后一篇正文中,我会再和你详细讨论网站的可扩展性(Extensibility)架构设计。这里,我先带你一起回顾一下可扩展性的定义: + + +可扩展性,指的是网站的架构设计能够快速适应需求的变化,当需要增加新的功能实现时,对原有架构不需要做修改或者做很少的修改就能够快速实现新的业务需求。 + + +从这个定义中,我们很容易就可以得出衡量网站可扩展性设计优秀与否的主要标准,就是增加新功能的时候对原有系统的影响是否足够小。 + +当今的商业环境决定了网站新功能开发与上线的时间周期必须非常短,如果每次添加新功能,都需要对原有系统进行大量修改,从而还会牵连出更多测试工作的话,那么你的竞争力就会被大打折扣,用一个不太恰当的比喻就是直接“输在了起跑线上”。 + +其实,你我都清楚添加新功能时必须要对系统进行大幅度修改的原因是,系统架构设计上的耦合性。那么,有什么“好的”架构设计方案可以使得我们添加新功能的时候,只需对原有系统做少量修改,甚至完全不需要修改吗? + +咋一听起来,这就像“又要马儿跑,又要马儿不吃草”。但,其实不是的。我们往往可以通过架构上的设计优化来达到事半功倍的效果。 + +为了帮助你理解可扩展性,我先和你分享一个案例。 + +网站可扩展性架构设计的案例 + +假设你现在为了实时监控服务器的健康状态,需要为网站添加一个实时收集服务器端监控指标的功能,此时最直接的方案就是用代码去实现对每一个监控指标的收集,然后将所有的这些代码集成在一起形成一个可执行程序运行在服务器端后台。 + +这样的设计固然简单直接,而且也能实现所有的功能需求(收集各种监控指标),但是当你需要收集一个新的监控指标时,就不得不更新整个可执行程序了。如果你需要经常添加新的监控指标的话,那么这样的设计就不能满足可扩展性的要求了。 + +我们希望的是,当增加新的监控指标的时候,原有的系统不需要做任何修改,甚至可以做到实时添加全新的监控指标。为了达到这个目的,现有的其他方案都不能满足或者不容易满足这个要求,所以我们就必须要在架构设计上做些文章了。 + +我们可以把对每一个监控指标的代码实现,直接打包成一个个的可执行监控子程序,比如收集CPU使用率的程序A、收集内存使用率的程序B等,然后运行在服务器后台的监控主程序通过调用这些子程序,比如程序A和B,来实现所有的监控需求。 + +这时,再增加新的监控指标时,原有系统就不需要做任何改动,只需要独立实现新的监控子程序,然后以配置文件的形式“告诉”主程序新添加的监控子程序的路径即可。这也就实现了系统的可扩展性。 + +接下来,我们再一起回到网站的可扩展性设计上来。其实,提升网站可扩展性性的核心,就是降低系统各个模块和组件之间的耦合。耦合程度越低,各个模块和组件的复用可能性就越大,系统的可扩展性也会越好。 + +从现在来看,实现网站可扩展性架构的主要技术手段包括事件驱动架构和微服务架构。 + +微服务架构从根本上改变了网站的架构形式,带来可扩展性便利的同时,还带来了很多其他优秀的特性。在微服务架构下,一个大型复杂软件系统不再由一个单体组成,而是由一系列的微服务组成。其中每个微服务可被独立开发和部署,各个微服务之间是松耦合的。每个微服务仅专注于完成一件任务,并要很好地完成该任务。 + +在微服务架构下,当网站需要增加新功能时,我们除了可以添加新的业务逻辑外,还可以利用原本已经存在的微服务来构建新的功能。由于服务和服务之间是相互隔离的,并且单个服务还可以被其他多个服务复用,所以系统的可扩展性会比较好。 + +而关于微服务架构下,测试人员应该关注的测试点,建议你参考专栏的第24篇文章《紧跟时代步伐:微服务模式下API测试要怎么做?》中的相关内容。如果还有哪些不清楚的,你可以再自行查找更多的相关资料,或者给我留言一起讨论。 + +所以,在今天这篇文章中,我会和你重点分享事件驱动架构是如何提升网站的可扩展性的。 + +而事件驱动架构的落地靠的是消息队列,所以我会同时和你分享消息队列的内容。最后,我会再和你分享引入了消息队列后,从测试人员的角度来看会有哪些需要额外关注的点。 + +事件驱动架构与消息队列 + +事件驱动架构设计的出发点源于这样一个事实:如果系统的各个模块之间的协作不是通过直接的调用关系来实现的,那么系统的可扩展性就一定会更好。问题是,系统的各个模块间的协作如何才能不基于调用关系呢? + +答案就是事件消息。系统各个模块之间只是通过消息队列来传输事件消息,而各模块之间并没有直接的调用关系、保持松散的耦合关系。 + +事件驱动架构最典型的一个应用就是操作系统中常见的生产者和消费者模式,将其应用到网站设计中就是分布式消息队列。 + +分布式消息队列同样采用了生产者和消费者模式: + + +消息的发送者负责将消息发布到消息队列中,也就是“生产者”; +另外,系统中会有一个或者多个消息接收者订阅消息,订阅目的是为了获取消息并进行处理,这里的消息订阅者其实就是“消费者”。消息接收者发现消息队列中有新的消息后,就会立马对其进行处理。 + + +可以看到,在这种模式下,消息的发送者和接收者之间并没有任何直接的联系,是松耦合的。它们的协作是通过消息队列这个“中间人”进行的。消息的发送者将消息发送至消息队列后,就结束了对消息的处理,而消息的接收者只是从消息队列中获取消息进行后续的处理,并不需要知道这些消息从哪里来,因此可以很方便地实现高可扩展性。 + +所以,采用这种模式的话,当网站需要增加新功能的时候,只要增加对应的新模块,再由对此模块感兴趣的“消费者”进行订阅,就可以实现对原有系统功能的扩展了,而对原本的系统模块本身并没有影响。 + +此时,消息队列的架构如图1所示。 + + + +图1 消息队列的原理图 + +引入了消息队列后,我们不仅可以提高系统的可扩展性,还可以再一定程度上改善网站架构的高性能、高可用性和可伸缩性。 + + +从性能方面来看,消息发送者不需要等接收者实际处理完成后才返回,也就是从原本的同步处理变成了异步处理,所以用户会感知到网站性能的提升。 +从高可用方面来看,假如消息的接收者模块发生了短时间的故障,此时并不会影响消息发送者向消息队列中发送消息,等到消息接收者模块恢复后可以继续后续的处理,只要这段时间内消息队列本身没有被塞满而出现消息丢失的情况。从整体角度看,系统并不会感知到消息接收者模块曾经发生过短暂故障,也就相当于保证了系统的高可用。 +从可伸缩性方面来看,消息队列的核心其实就是一个无状态的存储。所以,当系统需要能够保留更多的消息时,我们通过简单地增加存储空间就可以实现。尤其是,大规模的电商网站来更会将消息队列扩展成为分布式消息队列集群,来实现消息队列的可伸缩性。 + + +引入消息队列后,测试人员需要额外关注的点 + +现在,你应该已经掌握了消息队列的基本原理,以及在网站架构中的用法。接下来,我们再一起看看消息队列对测试的影响,以及我们在测试时需要特别关注哪些点。 + +这里,我把测试人员需要额外关注的点,归纳为了以下几点: + + +从构建测试数据的角度来看,为了以解耦的方式测试系统的各个模块,我们就需要在消息队列中构造测试数据。这也是为什么很多互联网的自动化测试框架中都会集成有消息队列写入工具的主要原因。 + +从测试验证的角度来看,我们不仅需要验证模块的行为,还要验证模块在消息队列中的输出是否符合预期。为此,互联网的自动化测试框架中也都会集成消息队列的读取工具。 + +从测试设计的角度来看,我们需要考虑消息队列满、消息队列扩容等情况下系统功能是否符合设计预期。 + +除此之外,我们还需要考虑,某台消息队列服务器宕机的情况下,丢失消息的可恢复性以及新的消息不会继续发往宕机的服务器等等。 + + +总结 + +在今天这篇文章中,我和你分享了网站架构知识中的最后一个内容:可扩展性。 + +可扩展性指的是网站的架构设计能够快速适应需求的变化,当需要增加新功能时,我们只要对原有架构进行少量修改,甚至不用修改就能快速实现新的业务需求。 + +从技术实现上来看,消息队列是实现可扩展性的重要技术手段之一。其基本核心原理是各模块之间不存在直接的调用关系,而是使用消息队列,通过生产者和消费者模式来实现模块间的协作,从而保持模块与模块间的松耦合关系。 + +引入消息队列后,测试数据的创建和测试结果的验证工作,都需要通过读写消息队列来完成。同时,我们还要考虑到消息队列满、消息队列扩容,以及消息队列服务器宕机情况下的系统功能验证。这几个点,就是测试人员需要额外关注的点了。 + +思考题 + +你在实际工作中接触过哪些种类的消息队列?在测试过程中,是否遇到过和消息队列有关的缺陷呢? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送浅谈全链路压测.md b/专栏/软件测试52讲/测试专栏特别放送浅谈全链路压测.md new file mode 100644 index 0000000..d7e8d64 --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送浅谈全链路压测.md @@ -0,0 +1,156 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 浅谈全链路压测 + 你好,我是茹炳晟。今天我和你分享的主题是:浅谈全链路压测。 + +时光飞逝,从专栏上线至今,我已经和你分享了52篇文章和7篇答疑文章,和你分享了软件测试中的各个主题,希望已经帮你构建了一幅软件测试的知识全景图。 + +在前面的“性能测试”系列文章中,我以LoadRunner为例,和你分享了传统企业在实际开展企业级性能测试的实践。并且在第32篇文章《无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(上)》中,我和你分享了这么安排的原因,并承诺在专栏结束前,通过一篇“加餐”文章,和你分享开展全链路压测的难点,以及应对方案。 + +现在,就是我践行承诺的时间了。 + +我也不太清楚,你现在具备多少全链路压测的知识。所以,我会先和你分享一些全链路压测的理论知识,然后再分享具体的难点以及解决思路,帮你加深理解,希望可以让你听得明白、学得会、用得着。 + +什么是全链路压测? + +全链路压测,是基于真实的生产环境来模拟海量的并发用户请求和数据,对整个业务链路进行压力测试,试图找到所有潜在性能瓶颈点并持续优化的实践。 + +目前,一线互联网公司的大型电商平台都会不定期地开展全链路压测,比如淘宝、京东、饿了么和美团这些企业,基本都已经有了自己的全链路压测方案和平台。 + +其中,最为典型的要数淘宝的双11活动了。每年到了11月11日的零点,淘宝的整个系统都会面临极大的流量冲击,如果事先没有经过充分的测试和容量预估,很可能会在流量爆发时瘫痪。 + +记得在早些年的淘宝双11大促中,就出现了不同程度的网站故障,严重影响了用户体验,所以从2013年开始,淘宝开始实施全面的全链路压测。由于在真正的双11到来前,淘宝内部已经模拟了比双11流量还要高的负载,并且逐个解决了已经发现的问题,因此真正双11到来的时候,就不会出现严重的问题了。 + +因此,为了防止此类事故,淘宝会在每在双11之前,就对系统的稳定性以及负载承受能力进行必要的测试和评估。 + +当然,全链路压测的应用场景,不仅仅包括验证系统在峰值期间的稳定性,还会包含新系统上线后的性能瓶颈定位以及站点容量的精准规划。 + +比如,由于某些业务模块的操作负载会集中到几个最核心的组件上,那么通过全链路压测的模拟,我们就能快速识别出哪些模块的负载过大,哪些模块的负载偏小。这样我们在对系统进行扩容时,就可以把资源更多地给到那些承受大负载的模块,而那些承受负载偏小的模块就可以进行适当的收缩来让出更多的可用资源。这,就是精准的容量规划。 + +单系统的独立压测 + +早先的时候,压测并不是针对业务的全链路来开展的,而是采用了“各个击破”的原则,即对生产环境中的单机或者单系统进行独立的压测。这时,压测主要是通过模拟单一系统的海量并发请求来实现的。而模拟海量请求主要有两种实现方式: + + +一种是,根据设计的压力来直接模拟大量的并发调用; +另一种是,先获取线上真实的流量请求,然后经过数据清洗后,再回放模拟大量的并发调用。 + + +不管采用的是哪种方式,都会涉及流量模拟、数据准备、数据隔离等操作。除此之外,单系统的独立压测局限性也非常明显。 + +这里,我把单系统独立压测的局限性,归纳为了以下几点: + + +单系统压测的时候,会假设其依赖的所有系统能力都是无限的,而实际情况一定不是这样,这就造成了单系统压测的数据普遍比较乐观的情况; +在大压力环境下,各系统间的相互调用会成为系统瓶颈,但这在单系统压测的时候根本无法体现; +大压力环境下,各系统还会出现抢占系统资源(比如网络带宽、文件句柄)的情况,这种资源抢占必然会引入性能问题,但是这类问题在单系统压测过程中也无法体现出来; +由于是单系统测试,所以通常都只会先选择最核心的系统来测试,这就意味着其他的非核心系统会被忽略,而在实际项目中,这些非核心系统也很有可能会造成性能瓶颈。 + + +因此,为了解决单系统独立压测的一系列问题,业界就衍生出了全链路压测。全链路压测会把整个系统看作一个整体,然后在真实的生产环境上尽可能真实地去模拟业务的海量并发操作,以此来衡量系统的实际承载能力,或者找出系统可能的瓶颈点并给出相应的解决方案。 + +目前来看,全链路压测需要解决的技术难点有很多,这里我会和你讨论其中最重要的四个点: + + +海量并发请求的发起; + +全链路压测流量的隔离; + +实际业务负载的模拟; + +测试完成后的数据清理。 + + +海量并发请求的发起 + +由于全链路压测需要发起的海量并发,通常会超过每秒1000万次以上请求的压力量级,所以传统的性能测试工具LoadRunner已经很难满足要求了,原因有二: + + +一来,LoadRunner按并发用户数收费,这就使得采用LoadRunner进行互联网的全链路压测的费用会异常高; +二来,LoadRunner本身也很难支持千万级乃至亿级的海量并发。 + + +所以,业界基本都是采用免费的JMeter来完成全链路压测,这也是JMeter近几年被互联网企业广泛使用的原因。 + +但是,即便有了JMeter,我们在开展全链路压测时,也会有很多问题需要解决。其中,最主要的问题包括以下三个: + + +虽然采用了分布式的JMeter方案,并发数量也会存在上限,比如面对亿级的海量并发时,主要原因是分布式的JMeter方案中,Master节点会成为整个压测发起的瓶颈。- +为了解决这个难题,很多公司并不会直接采用分布式JMeter架构来完成海量并发,而是会使用Jenkins Job单独调用JMeter节点来控制和发起测试压力。这样就避免了Master节点引发的瓶颈问题。而且,由于各个JMeter是完全独立的,所以只要Jenkins Job足够多,并且网络带宽不会成为瓶颈的情况下,就能发起足够大的并发。 + +测试脚本、测试数据和测试结果在分布式JMeter环境中的分发难题。如果直接采用分布式的JMeter方案,测试脚本需要通过JMeter的Master节点来分发,测试数据文件则要用户自行上传至每套虚拟机,同时测试结果还要通过JMeter的Slave节点回传至Master节点。- +所以,更好的做法是基于JMeter来搭建一个压测框架,诸如脚本分发、数据分发以及结果回传等工作,都由压测框架完成。这也是目前绝大多数大型互联网企业的做法。比如,饿了么就采用这种方式搭建了压测平台,并且取得了很好的效果。 + +流量发起的地域要求。全链路压测流量的发起很多时候是有地理位置要求的,比如30%的压力负载来自上海、30%的压力负载来自北京等,这就要求我们在多个城市的数据中心都搭建JMeter Slave,以便可以发起来自多个地域的组合流量。 + + +全链路压测流量和数据的隔离 + +因为全链路压测是在实际的生产环境中执行的,所以测试产生的数据与真实的用户数据必须进行有效隔离,以防止压测的流量和数据污染、干扰生产环境的情况。比如,不能将压测数据记录到统计分析报表里;再比如,压测完成后可以方便地清洗掉压测产生的数据。 + +为了达到这个目的,我们就需要对压测流量进行特殊的数据标记,以区别于真实的流量和数据。这就要求各个链路上的系统,都能传递和处理这种特殊的数据标记,同时写入数据库中的数据也必须带有这种类型的标记以便区分数据,或者直接采用专门的影子数据库来存储压测的数据。 + +可以看出,为了实现压测产生的和真实的流量和数据隔离,我们就需要对各个业务模块和中间件进行特殊的改造和扩展。而这个工作量相当大,而且牵涉的范围也非常广,也就进一步增加了实施全链路压测的难度。 + +而且通常来讲,首次全链路压测的准备周期会需要半年以上的时间,这其中最大的工作量在于对现有业务系统和中间件的改造,来实现压测流量和数据的隔离。所以,在实际的工程项目中,如果全链路压测不是由高层领导直接牵头推动的话,很难推进。 + +另外,在对各个业务模块和中间件添加特殊标记的改造过程中,我们会尽可能少地改动业务模块,而是更倾向于通过中间件来尽可能多地完成特殊数据标记的处理和传递。 + +实际业务负载的模拟 + +一直以来,如何尽可能准确地模拟业务系统的负载,都是设计全链路压测时的难题。这里的难点主要体现在两个方面:首先,要估算负载的总体量级;其次,需要详细了解总负载中各个操作的占比情况以及执行频次。 + +业界通常采用的策略是,采用已有的历史负载作为基准数据,然后在此基础上进行适当调整。具体到执行层面,通常的做法是,录制已有的实际用户负载,然后在此基础上做以下两部分修改: + + +录制数据的清洗,将录制得到的真实数据统一替换成为压测准备的数据,比如,需要将录制得到的真实用户替换成专门为压测准备的测试用户等等·; +基于用户模型的估算,在全链路压测过程中,按比例放大录制脚本的负载。 + + +最后,再用这个负载来模拟全链路压测的负载。 + +真实交易和支付的撤销以及数据清理 + +由于全链路压测是在真实的生产环境中进行的,那么完成的所有交易以及相关的支付都是真实有效的,所以我们就需要在测试结束后,将这些交易撤销。 + +因为,我们已经对这些交易的流量和数据进行了特定标记,所以我们可以比较方便地筛选出需要撤销的交易,然后通过自动化脚本的方式来完成批量的数据清理工作。 + +除了上面的四大问题以外,全链路压测还需要考虑测试执行过程中的性能监控、高强度压测负载下的测试熔断机制、全链路压测执行期间对原有系统正常负载的影响、全链路压测数据对外的不可见等等。 + +所以说,全链路压测的技术含量很高,而且需要多方共同配合才有可能顺利完成。所以,今天这篇文章的目的,意在抛砖引玉。希望你可以借由这篇文章,先对全链路压测的难点以及对应的解决思路有个全局的认识。而如果你想要更好地了解并掌握全链路压测,最好的方式还是要在实际项目中多加历练。 + +另外,你还可以参考一些网上的优秀资源,我在这里列出了两条供你参考: + + +https://mp.weixin.qq.com/s?__biz=MzIzMzk2NDQyMw==&mid=2247486703&idx=1&sn=0c448c40469e6a8f32476a7c7fbab6cd&source=41#wechat_redirect + +https://www.cnblogs.com/imyalost/p/8439910.html + + +总结 + +今天这篇文章,我和你分享了全链路压测的基本知识,以及在开展全链路压测的难点、对应的解决思路。现在,我再和你一起回顾下。 + +全链路压测,是基于真实的生产环境来模拟海量并发用户请求和数据,对整个业务链路进行压力测试,试图找到所有潜在性能瓶颈点并持续优化的实践。它的应用领域不仅仅包含验证系统在峰值期间的稳定性,还会包含新系统上线后的性能瓶颈定位以及站点容量的精准规划。 + +了解了全链路的基本概念,以及适用场景后,我和你分享了全链路压测中最关键的四个技术难点,即:海量并发请求的发起、全链路压测流量和数据的隔离、实际业务负载的模拟,以及测试完成后的数据清理。 + + +海量并发请求的发起主要借助于JMeter,并且通过Jenkins Job来实现海量并发的调度控制; +全链路压测流量和数据的隔离主要借助含有特定标记的流量和数据来实现,同时需要对业务模块以及中间件进行必要的改造,数据库这边还会使用影子数据库; +实际业务负载的模拟,主要是采用基于历史流量修改后的回放来实现; +全链路压测完成后的数据清洗,则是借助自动化的手段来批量完成。 + + +思考题 + +你能想到全链路压测中还有哪些技术上的难点吗? + +感谢你的收听,欢迎你给我留言一起讨论。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送答疑解惑第一期.md b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第一期.md new file mode 100644 index 0000000..93d43ba --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第一期.md @@ -0,0 +1,100 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 答疑解惑第一期 + 你好,我是茹炳晟。 + +首先,感谢大家对《软件测试52讲》专栏的支持与参与。 + +到目前为止,我已经通过测试基础知识、GUI自动化测试、API自动化测试、代码级测试、性能测试、测试数据准备、测试基础架构、测试新技术8个系列、47篇文章,和你分享了软件测试相关的所有知识点。 + +每篇文章后面,我都为你留下了1~2个思考题。其中,一部分思考题是让你分享你所在项目和团队的实践,和其他读者一起探讨、交流,这样大家可以互相借鉴好的做法;还有一部分思考题,是针对当篇文章中的内容,希望你可以分享一些你的想法,也想借此了解你在实际的测试工作中遇到的问题,尽我的能力再多为你提供些帮助。 + +47篇文章的写作,基本上已经占满了我所有的个人时间。1000多条留言,我也没有精力去一一答复,这其中还有很多不是一两句就能解释清楚的问题。还有些留言质量非常高,分享了一些我未覆盖到的内容,观点都非常赞。 + +所以,特别选在专栏即将结束的节点,也可以说是和你分享完了软件测试的基本概念、原理、方法的节点上,我和编辑一起策划了这个“答疑解惑”系列,从已发布的文章,以及对应的留言中,精选出一些问题,为你解答。 + +当然了,我的专栏还有5篇正文没有更新,我已经根据大家的反馈,重新调整了这5篇文章的主题,选择了大家最关注的、对大家更有帮助的五个技术点,和你展开分享。下周,我将继续为你更新这些文章,敬请期待。 + +今天这篇文章,我就先挑选了五个问题,和你分享一下我的看法。你也可以继续在留言区给我留言,说出你的见解,我们继续讨论。 + +问题一:从拓展思维的角度,还可以为“用户登录”功能补充哪些测试用例? + +在专栏第一篇文章《你真的懂测试吗?从“用户登录”测试谈起》中,我从“用户登录”功能的测试用例设计谈起,和你分享了一个看似简单的功能,在设计测试用例时需要考虑的方方面面。在文后,我希望你可以从拓展思维的角度,思考一下还可以为这个“用户登录”功能补充哪些测试用例。 + +大家针对这个问题的留言质量都非常高,考虑的也都非常全面,可以说涵盖了很多我在文章中并没有涉及到的点。 + +你也许已经发现了,针对“用户登录”这个简单直接的功能,我们居然可以设计出这么多的测试用例。这些测试用例,既覆盖了明确定义的软件功能性需要,也覆盖了软件非功能性需求的方方面面。 + +可是,当在实际项目的实施过程中,当老板让你预估一下测试一个“用户登录”功能需要多少时间和工作量的时候,如果完全按照文章中的介绍的测试用例设计方法来执行测试的话,你可能会告诉老板说:“我需要至少2-3天”。此时,你的老板一定会火冒三丈,怀疑你是不是在忽悠他,就这么个简单明了的功能居然要花这么长时间的测试。 + +因此,这么全面的测试设计和执行通常不会出现在实际的工程项目中。站在工程实践的角度,我推荐的是“够用就好”的原则。 + +其实,测试的目的并不是要求软件中不存在任何错误,而是希望软件在上线运行的过程中,错误不会被触发。这就要求我们在测试用例选择的过程中,要尽可能覆盖用户最常使用的操作场景进行重点测试,同时也应该基于风险的大小来决定哪些测试应该纳入当前的测试范围。这里的风险指的是,万一软件缺陷被触发将会对业务造成哪些影响。 + +问题二:你有哪些好的测试用例设计实践和方法? + +在专栏的第二篇《如何设计一个“好的”测试用例》文章中,我和你分享了一个“好的”的测试用例应该具备的特征、常用的几种测试用例设计方法,以及三个独家“秘籍”。相应地,我也希望你可以在留言区分享一些好的实践和方法。 + +大家在留言区的评论很活跃,也很精彩。这里,我再和你说说我的一些实践和方法吧。 + + +我在文章中分享的方法都是从技术层面展开的,其实还有很多流程上的方法可以帮助测试用例的设计。比如,一些资深的测试工程师,可以将一些典型的测试用例设计整理成检查点列表(Checklist)。这样,当你完成测试用例设计后,可以再基于检查点列表来自查你的用例设计是否还存在不完备的地方。 + +通常情况下,测试用例的设计需要通过文档来体现。所以,你往往会花费很多的时间在文档写作上,而不能将有限的精力用在“刀刃”上。为了减少设计测试用例的文档写作时间,二把更多的时间花在测试用例的设计上,我建议你引入一些速记工具,或者是“头脑风暴”类的工具,来简化测试用例设计的文档工作。 + +“小黄鸭”方法不仅适用于程序的调试,同样适用于测试用例设计的自我检查。“小黄鸭”方法原本的含义是,当你在调试程序的过程中,因为无法顺利找到问题源头而无法继续的时候,可以假想有一个小黄鸭是你的听众,然后你将程序实现的思路以及业务逻辑处理的细节一一讲给它听。这样在讲述的过程中,会引发你全面的思考,并可以帮助你自发地发现问题。同样地,你也可以将测试用例的设计思路讲给小黄鸭听,以此来帮助你理清思路。 + + +问题三:你在实施单元测试时,遇到了哪些问题,又是如何解决的? + +在专栏的第三篇文章《什么是单元测试?如何做好单元测试?》中,我和你分享了单元测试的概念,和你重点讨论了用例的组成,以及在实际项目中开展单元测试的方法。在文后的思考题中,我希望你可以分析一下你所在公司在实施单元测试时遇到了的问题,以及对应的解决方案。 + +在留言区,我也看到了一些读者的见解,也很受益。这里,我首先会分享一下eBay在开展单元测试时的实践;然后,再挑选一位回答得很精彩的用户,做下点评。 + +eBay是全球知名的大型电子商务网站,已经大规模使用了微服务架构。对于单元测试而言,eBay并不会对所有的微服务以及所有的中间件全面开展单元测试。我们只会选取一些偏底层的核心应用来全面开展单元测试,而对于产品的前端代码、偏业务应用的代码,很少会执行完整意义上的单元测试。 + +如果你在项目中推行过单元测试,并且要求能够提供代码级的覆盖统计数据的话,那么你在实施和推行单元测试的过程中,一定会遇到诸如单元测试代码覆盖率低下的问题。这里,我的建议是,在项目的早期不要硬性规定绝度百分比的代码覆盖率指标,而是建立一个覆盖率基线,然后保证代码在每次发布前,都可以与这个基线持平或者高于这个基线的覆盖率。 + +另外,我还在留言区看到了一个非常具有代表性的留言,也就是这个叫作“小志”的读者留言。 + + + +他很好地归纳了当今互联网企业中单元测试的现状。的确,很多的互联网公司都在追求快速的功能实现,却一直没有强调单元测试,并且由于灰度发布机制的支持,所以对软件质量隐患的容忍度比较高。这点和传统软件测试的观念差别比较大,因为传统软件测试没有灰度发布的概念,所以对每个即将发布版本的质量控制都会非常严格。 + +问题四:你所在项目开展的自动化测试,真的有必要吗? + +在第四篇文章《为什么要做自动化测试?什么样的项目适合做自动化测试?》中,我和你分享了自动化测试的概念,以及具备哪些特征的项目才适合做自动化测试。我还希望你能理解自动化测试是一把“双刃剑”,必要一味地为了自动化而自动化。 + +所以,在文后我希望你根据自己所经历的项目中的自动化测试所占比重,去进一步思考这个项目开展自动化测试的必要性。 + +这里,我先来分享一下eBay在自动化测试方面的具体实践。eBay早期是非常重视GUI自动化测试的,所以投入了大量的资源去大规模开发GUI测试用例。可是,随着用例规模的不断扩大,GUI测试的稳定性问题也被不断放大。同时,大量GUI测试用例的并发执行时间也成了个大问题。 + +为了缓解这种状态,我们逐渐将测试重心迁移到了后端服务的API测试上。API测试具有代码形式统一、执行稳定性高的特点。而原本GUI测试,我们降低了自动化测试用例的比例,并由探索式测试来完成部分GUI测试。 + +另外,在留言区,我看到了一些比较赞的观点。比如,这位“关柱鹏”的留言,就是站在了全局的视角描述了可重用的测试架构。这样的架构设计将非常有利于自动化测试脚本的应用。你可以参考。 + + + +问题五:你所在公司,开展代码级测试时用到了哪些自动化测试技术? + +在专栏的第5篇文章《你知道软件开发各阶段都有哪些自动化测试技术吗?》中,我和你分享了在软件的全生命周期中,涉及到的自动化自动测试技术。我从单元测试、代码级集成测试、Web Service测试,以及GUI测试阶段的自动化技术这四个方面,和你分享了“自动化测试”的技术内涵和外延。 + +我希望你读完这篇文章后,可以回忆或者归纳下:你所在公司,开展代码级测试时用到的自动化测试技术。 + +这里,我还是先和你分享一下eBay的实践经验。可以说,eBay非常重视自动化测试开发,并在这方面投入了很多成本,几乎在软件研发生命周期的各个阶段都有自动化测试的支持。 + +比如,在单元测试阶段,有基于参数分析的自动化测试数据准备系统;在API测试阶段,有基于REST Assured的API自动化测试;在系统测试或者E2E测试中,用到了GUI自动化框架设计与开发。可以说,自动化测试已经渗入到了eBay软件研发的各个领域。 + +在留言区中,“康美之心 淇水之情”的留言,在我看来是一个能够很好的落地实践。Spring-boot、REST Assured、Cucumber都是当下最热门的技术。由于使用了Cucumber,所以完全有可能可以实现自己特有的基于API的行为驱动开发(BDD)。 + +一方面Cucumber本身就是非常成熟的BDD框架,另一方面REST Assured也支持三段式的测试用例描述。所以,用Cucumber结合REST Assured的方案,可以实现更灵活的API级别的BDD,用Cucumber结合Selenium可以实现GUI级别的BDD。 + +- +最后,感谢你能认真阅读这五篇文章的内容,并留下你的学习痕迹。相信这些留言,于你于我而言,都是一笔宝贵的财富。期待你能继续关注我的专栏,继续留下你对文章内容的思考,我也在一直关注着你的留言(虽然有时未能及时回复)。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送答疑解惑第七期.md b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第七期.md new file mode 100644 index 0000000..b73cad8 --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第七期.md @@ -0,0 +1,146 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 答疑解惑第七期 + 你好,我是茹炳晟。 + +今天的“答疑解惑”系列文章,我们一起来解决测试新技术、测试人员的互联网架构核心知识这最后两个系列相关的问题。 + +这期的答疑文章,我不会针对每篇文章后面的思考题展开,而是会选择了四个大家比较关注的问题,和你分享我的观点。如果你的看法不同,或者你还有哪些其他问题的话,欢迎你在这篇文章下面给我留言,我会持续不断地解答你的问题。 + +当然了,我还是会先用一句话简单概括下每篇文章的内容,并给出对应的链接,方便你复习。 + +测试新技术系列文章回顾 + +在专栏的第43篇文章《发挥人的潜能:探索式测试》中,我和你阐述了这样一个基本思想:探索式测试是一种软件测试风格,而不是一种具体的软件测试技术。 + +作为一种思维方法,探索式测试强调依据当前语境与上下文选择最适合的测试技术,并强调独立测试工程师的个人自由和责任,其目的是为了持续优化其工作的价值。 + +看到有用户在留言中说到想在实际项目中开展探索式测试,这里我想再给个建议: + +高效开展探索式测试的前提是,对被测系统的设计以及行业应用有非常清晰的认识,同时在此基础上以发散的方式对系统可能存在的缺陷进行探索。所以,这就要求测试人员不仅要具有很深的业务领域知识,还需要很强的逻辑推理和分析能力。而这样的人才,属于比较稀缺的。 + +另外,探索式测试不要到了项目后期再集中展开,而是应该在各个模块级别就尽可能多地去探索,尽量在测试早期就能发现问题。 + +需要注意的是,一定不要在执行层面,将探索式测试变成了随机测试,你设计的所有后续测试步骤都必须是在你之前的步骤上推演出来的。而且,在执行探索性测试的过程中,你需要明确每个操作的目的是什么,是想证实自己的推论还是要推翻自己的假设。对此,你一定要做到心中有数,否则很容易就会变成无明确目的随机测试。 + +而从管理层的角度来看,千万不要以探索式测试发现的缺陷数量来考核团队的绩效。因为这样不仅不能提升测试效率,反而会把大量的时间浪费在一些非核心功能的测试上。 + +在专栏的第44篇文章《测试先行:测试驱动开发(TDD)》中,我和你分享了TDD的核心思想是:在开发人员实现功能代码前,先设计好测试用例,编写测试代码,然后再针对新增的测试代码来编写产品的功能代码,最终目的是让新增的测试代码能够通过。 + +正如“叶夏立”在留言中所说,TDD如何落地才是最核心的问题。所以,我会将这个问题,作为今天这篇文章要回答的第一个问题,和你分享些我的经验。 + +在专栏的第45篇文章《打蛇打七寸:精准测试》中,我通过分析传统软件测试的短板,和你分享了精准测试的概念、必要性、核心思想,以及具体的测试方法。 + +因为这种测试理念,国内外成功落地的案例非常少,而这个理论也是由星云测试公司提出的,所以我借鉴了《星云精准测试白皮书》中的内容,并从我的视角为你解读了其中的部分内容。如果其中有哪些不清楚的问题,我们可以一起探讨,共同进步。 + +在专栏的第46篇文章《安全第一:渗透测试》中,我分享了渗透测试是由专业的安全专家来模拟黑客对系统发起攻击,找到并修复系统的安全漏洞,从而让真正的黑客无机可乘。在这其中,我和你详细分享了渗透测试的知识点,包括常用的测试方法、步骤、工具。 + +这篇文章更新后,有的用户反馈希望看到实例的演示,这样可以更生动、易于理解,所以这里我决定在今天的第二个问题中,和你分享一个实际的渗透测试实例,满足你的需求。 + +在专栏的第47篇文章《用机器设计测试用例:基于模型的测试》中,我分享了基于模型的测试(MBT)是一种基于被测系统的模型,由工具自动生成测试用例的软件测试技术。这也就决定了,相对于传统软件测试技术来说有优优劣。 + +所以,我们需要综合考虑项目本身的特点和人员的技术水平,以此决定是否有必要开展MBT。关于如何判断你的项目是否适合开展MBT,我决定作为今天的第三个问题,和你展开分享。 + +测试人员的互联网架构核心知识系列文章回顾 + +在48篇文章《优秀的测试工程师为什么要懂大型网站的架构设计?》中,我主要和你分享了测试人员学习网站架构知识的why、what、how的问题,并提出了“由广度到深度”和“自上而下”的架构学习思路,希望可以增强你学习网站架构的信心。 + +在第49篇文章《深入浅出网站高性能架构设计》中,我从测试人员的视角,和你分享了网站的高性能架构设计包括哪些部分,以及在设计测试用例时,需要着重考虑哪些点。而设计到具体的测试方法、工具问题,你可以再回顾一下第28~34篇文章(也就是性能测试系列文章)中的相关内容。 + +在第50篇文章《深入浅出网站高可用架构设计》中,我将影响网站高可用的因素归为了三类(即:服务器硬件故障、新应用的发布、应用程序本身的问题),并相应地给出了解决这三类问题的方案。希望这些内容可以帮到你。 + +在第51篇文章《深入浅出网站伸缩性架构设计》中,我和你分享了一个网站的可伸缩性架构设计主要包含的两个层面。其中,一个是指根据功能进行物理分离来实现伸缩,另一个是指物理分离后的单一功能通过增加或者减少硬件来实现伸缩。 + +在第52篇文章《深入浅出网站可扩展性架构设计》中,我和你分享了本专栏的最后一个主题,即网站的可扩展性架构设计。从已有的实现方案来看,实现网站可扩展性架构的主要技术手段包括事件驱动架构和微服务架构。 + +而在微服务的实现方案中,需要测试人员关注的点,你可以参考第24篇文章《紧跟时代步伐:微服务模式下API测试要怎么做?》中的相关内容。所以,在这篇文章中,我和你重点分享的是事件驱动架构实现的大致原理,以及测试人员需要额外关注的点。 + +因为这个系列的文章,更新日期比较近,所以很多用户还没来得及看。所以,我就没有在这篇答疑文章中,设置与这个系列有关的问题。如果你阅读完这个系列的文章,有任何困惑,都可以给我留言,我将和你一起讨论、解决。 + +问题一:什么样的项目适合TDD?TDD如何才能落地? + +的确,TDD这个概念从提出来到现在已经有很长时间了,但实际落地的项目并不多,甚至可以说少之又少。造成TDD落地困难的原因有很多,比如很多大型项目本身就不适合做TDD,TDD初期阶段的工作划分以及粒度控制都是难点。但我认为最重要的原因是,TDD需要大幅改变研发团队的流程规范。这种改变在公司层面,尤其是中大型公司是很难实际执行的。 + +虽然明知落地TDD困难重重,但是你又特别想在自己的项目中尝试TDD,以解决现在的测试方法不能解决的问题。那么,落地TDD有哪些可值得借鉴的经验呢?这也正是很多用户关心的,比如昵称为“叶夏立”的用户在文章下面的留言。 + +- +这里,我根据自己的时间,为你总结了如下几点: + + +只在一些小型项目,比如前期的POC项目中,尝试开展TDD; +一定要借助Cucumber之类的TDD或者BDD工具,来协助TDD的开展; +必须把控好每个测试用例的粒度,不能太大,也不能太小,需要与开发函数以及功能的粒度相匹配; +项目管理的流程必须去适应TDD的实践,原本管理的是需求,现在管理的可能是测试用例了,因为用例本身就是对需求的解读; +测试人员必须要有开发背景,否则TDD只能是空谈; +必须要得到管理层的大力支持,最好是能自顶向下的推广。 + + +问题二:渗透测试在落地的时候,需要注意哪些问题? + +首先说一下,我设立这个问题的初衷,是想通过一个实际的例子,来帮助你理解渗透测试的本质。 + +所以,我以最常见的SQL注入攻击为例,和你简单分享下渗透测试落地时需要主要的问题。假设,我现在要测试用户登录功能,用户登录时会在界面上分别输入用户名(userName)和密码(passWord),然后程序代码会将输入的userName和passWord填充到下面SQL语句中。 + +SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');" + + +假设,我们输入的用户名是“Robin”,密码是“12345678”。那么,此时的SQL语句如下所示: + +SELECT * FROM users WHERE (name = 'Robin') and (pw = '12345678');" + + +然后,系统就会使用这个SQL语句去数据库中查询是否存在该用户,以及该用户的密码是否正确。 + +此时,如果你是黑客希望通过渗透来非法获取系统信息,你就会尝试设计以下的用户名和密码: + +username 输入 "1' OR '1'='1"; +password 输入 "1' OR '1'='1"; + + +这种情况下,用于数据库查询的SQL语句就会变成如下所示的样子,就是将userName和password的部分用 “1’ OR ‘1’=‘1”都代替: + +SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');" + + +如果你熟悉SQL语句的语法,你就会发现黑客查询数据库使用的SQL语句,其实和下面这个SQL语句是等价的: + +SELECT * FROM users; + + +也就是说,原本用于查询单条用户信息的SQL语句已经被黑客改造成了获取全部用户信息的SQL语句。这,就是最典型的SQL注入攻击手段了。 + +而我们所讲的渗透测试,就是会去人为模拟这种攻击,以判断系统是否能够成功应对此类攻击。 + +问题三:如何判断你的项目是否适合采用MBT,以及你认为会遇到哪些问题可能会阻碍MBT的开展呢? + +一般来讲,只要系统的设计可以用状态转移图来描述的话,基本都可以采用MBT。另外,基于GUI的系统,因为本身就可以画出页面之间相互跳转的关系图,所以也适合采用MBT。 + +很可惜,eBay内部的项目除了一些实验性的尝试,并没有大规模开展MBT。但据我所知,业界最近有一家初创企业AutoTest正在全力推进MBT的落地和应用,而且还发表了很多相关文章,如果你对此感兴趣可以去关注一下。 + +在我看来,阻碍MBT落地的最关键问题,有两个: + + +一个是,探索路径的有效性问题。早期的实施方案,完全基于图论来覆盖可能路径,造成了大量的非法或者不合理的路径。但是,近几年来由于人工智能的介入,大大提升了路径探索的有效性。 +另一个是,如果只是用MBT完成单纯测试的话,收益比会比较低。只有将MBT和自动化测试结合在一起,才能发挥MBT的优势。在这方面,eBay一直在尝试,试图将Selenium和MBT集成到一起,目前已经有了初步成果,实现了用模型导出实际可以执行的自动化测试用例的POC。 + + +问题四:测试工程师如何应对面试? + +首先,我并不鼓励为了应对面试,而去做特别的准备,你还是应该在平常的工作中多积累。面试的过程,本来就是尽可能地反映你真实的技术水平以及业务熟练程度的交流,关注的重点应该是如何将你自己掌握的技术和知识更好地展现出来,而不是把你不懂的知识包装成你已经“懂”的知识。为此,我有如下几点小建议供你参考: + + +当被问及相关测试工具的时候,除非问到了该工具使用上的细节,否则尽可能避免谈及工具使用的细节,而是应该更多地阐述工具本身的原理、和同类工具相比的优劣势,以及这个工具可以以什么样的方式帮你解决问题。这时,你的视野一定要高,不能局限于细节。当然,这也就要求你能够充分理解这个工具的原理和使用方法。 +当被问及特定算法实现的时候,刚开始的时候,一定不要试图去寻求最优解法,而是要考虑最基本的实现,然后在此基础上迭代优化。因为,优秀的面试官希望看到的不是最优解,而是你解决问题的过程,以及在这个迭代过程中的逻辑推理。 +当被问及你所不熟悉的测试技术时,如实回答就好,不要试图去掩饰。很多时候面试官看重的并不是你知不知道,而是当你不知道的时候你会怎么做。 + + +最后,感谢你能认真阅读第43~52这10篇文章的内容,并写下了你的想法和问题。期待你能继续关注我的专栏,继续留下你对文章内容的思考,我也在一直关注着你的留言、你的学习情况。 + +咱们的答疑环节暂告一段落了,但这并不意味着结束。如果你在学习过程中遇到了什么问题,还可以继续给我留言,我会持续不断地回答你的问题。 + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送答疑解惑第三期.md b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第三期.md new file mode 100644 index 0000000..23c6cfc --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第三期.md @@ -0,0 +1,129 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 答疑解惑第三期 + 你好,我是茹炳晟。 + +今天这篇文章是“答疑解惑”系列文章的第三期,在这一期里面我做了个小小的改变,不再是针对每一篇文章后面的思考题,以及“你”的留言,来展开分享。这次,我选择将这个专栏的第二个系列:GUI自动化测试系列,作为一个整体,回答下你的问题。 + +根据目前GUI测试技术的应用情况,以及“你”在留言中留下的期望进一步了解的问题,也为了能够讲清、讲透,我还是选择了五个问题,为你解答。当然了,如果你关注的问题并未涵盖其中的话,也不要有遗憾,你可以在这篇文章下继续给我留言,我会持续关注并回答你的问题。 + +接下来,我还是会先为你总结一下,GUI自动化测试系列的全部10篇文章的内容,并为你提供文章链接,希望你可以通过回顾旧文获得新知。 + +系列文章内容回顾 + +在专栏的12篇文章《从0到1:你的第一个GUI自动化测试》中,我基于Selenium 2.0,带你从0到1建立了一个最简单直接的GUI自动化测试用例。通过这篇文章我希望传达给你的内容是,基于Selenium搭建GUI自动化测试用例的方法,以及Selenium 1.0、2.0的实现原理。所以,对于具体的用例设计方面的细节,比如构建一个GUI测试用例时需要封装哪些类等,我并没有详细展开。 + +也正如“Cynthia”在留言中所说的,一些资料会直接带你分析源码,但不会为你讲述工具的原理,所以虽然你会用这个工具,但是类似于为啥Chrome可以跑的case、Firefox跑不了、为啥Web Driver还要一个浏览器装一个这种问题,你永远不会明白。反馈到具体的测试工作上,就会出现知道要这么做,但是不知道为什么要这么做的尴尬局面。 + +在专栏的第13篇文章《效率为王:脚本与数据的解耦 + Page Object模型》中,我和你分享了什么是数据驱动的测试,让你明白了“测试脚本和数据解耦”的实现方式以及应用场景;然后,我从GUI自动化测试历史发展演变的角度,引出了GUI测试中的“页面对象模型”的概念。 + +在这篇文章最后,我希望你思考一下“是否应该在页面对象模型中封装控件的操作”这个问题。我看到留言区的回复,也都很精彩,可谓是思维清晰、逻辑严谨。这里,我想再阐述一下我的观点,详情请见问题二。 + +在专栏的第14篇文章《更接近业务的抽象:让自动化测试脚本更好地描述业务》中,我以“如何把控操作函数的粒度”和“如何衔接两个操作函数之间的页面”这两个问题为引子,和你分享了业务流程的概念、核心思想和适用的场景。 + +说实话,看到这篇文章下面的留言我很感动,为什么呢?因为我分享的测试思想得到了你的认可,也吸引你围绕着这一个主题展开了讨论。在这些留言中,我也仿佛看到了自己当年为了做好某一项测试而尝试的一系列方案。所以,在此我也想对你道声感谢。 + +在专栏的第15篇文章《过不了的坎:聊聊GUI自动化过程中的测试数据》中,我从创建测试数据的技术手段和时机两个方面,和你分享了在实际项目中,需要综合运用API调用和数据库操作来创建测试数据,并根据测试数据自身的特点,分而治之地采用On-the-fly和Out-of-box的方式,以寻求数据稳定性和数据准备效率之间的最佳平衡。 + +因为这个专栏后面有一个单独的系列去讲测试数据准备的那些事儿了,所以今天我就不再过多地展开这个问题了。当然,你在也可以在这篇文章下面,留下你关注的与测试数据相关的问题,等到测试数据准备系列的文章答疑时,我再挑选些典型问题进行解答。 + +在专栏的第16篇文章《脑洞大开:GUI测试还能这么玩(Page Code Gen + Data Gen + Headless)?》中,我一下和你分享了页面对象自动生成、测试数据自动生成、无头浏览器这三种技术。或许,在你看来有些停留在概念层面了。 + +说到为什么我没有针对某一个具体的概念,展开分享,其实我是这么考虑的:页面对象自动生成,主要用到的就是QTP这个工具,而对这个工具的使用,你可以通过它的文档来轻松上手,并且这个工具的内部实现也没有对外公开,所以这部分内容完全从工具使用的角度来讲的话, 并不能发挥这个专栏的价值。同样地,测试数据自动生成、无头浏览器也是这个道理。我还是希望你能在知道了这些概念之后,可以结合自己的实际工作,动手实践,这样的效果才是最好的。 + +所以,今天我也就不再过多的展开这个话题了。如果你在落地这三个概念的时候有任何问题,也可以给我留言,希望可以帮到你。 + +在专栏的第17篇文章《精益求精:聊聊提高GUI测试稳定性的关键技术》中,我为你归纳了五种造成GUI自动化测试不稳定的主要因素,即:非预计的弹出对话框、页面控件属性的细微变化、被测系统的A/B测试、随机的页面延迟造成控件识别失败,以及测试数据问题。然后,我针对每种问题,给出了对应的解决思路。 + +当时为了不淹没提高GUI测试稳定性的关键技术这个主题,我并没有通过实例展开这五个因素及其对应的解决方案。今天为了加深你的理解,我决定针对随机失败的重试(retry)和你分享一个实例,作为今天我要回答的第三个问题。 + +在专栏的第18篇文章《眼前一亮:带你玩转GUI自动化的测试报告》中,我和你分享了一份理想的GUI自动化测试报告应该包括哪些内容,并和你分享了eBay在全球化GUI测试报告中的创新设计。希望这些方法,你也可以经过适当改进,用到自己的测试项目中。 + +有些读者感觉这篇文章不够过瘾,想要知道某一个技术细节的实现。所以,这里我会针对基于GUI测试报告,和你再分享一个小例子,和你说说故事板样式的GUI测试报告具体是如何实现的,也就是后面我要分享的第四个问题。 + +但是,我还是想要再强调一下,这篇文章的设计初衷并不是去解释每种报告的实现细节,而是帮助你从多元化的视角去思考一个高效的测试报告应该是什么样的。 + +在专栏的第19篇文章《真实的战场:如何在大型项目中设计GUI自动化测试策略》中,我从“实战”的角度,分享了实际的大型全球化电商网站的GUI自动化测试如何开展,希望可以帮你解决测试策略如何设计、测试用例脚本如何组织这两个问题。 + +其实,通过这篇文章,我已经和你说清楚了大型项目中的GUI自动化测试策略这个问题,所以在今天这篇文章中我就不再继续展开这个话题了。 + +在专栏的第20篇文章《与时俱进:浅谈移动应用测试方法与思路》中,我和你分享了Web App、Native App和Hybrid App,这三类移动应用的测试方法,以及移动专项测试的思路与方法。其实,这三类移动App的测试,和GUI自动化测试的思想是相通的,只不过是针对移动应用自身的特点,又有了一些独特的方法而已。 + +在专栏的第21篇文章《移动测试神器:带你玩转Appium》中,我用Appium手把手地带你实现了一个移动应用的测试用例,并本着知其所以然的原则,和你分享了Appium的实现原理,希望借此可以带你玩转Appium,完成自己的移动应用测试。 + +这两篇关于移动App测试的文章更新后,大家留言都很踊跃,也可以反映出大家很关注这块内容。所以,今天我再选择微信小程序的自动化测试,和你再分享下业界的主流工具,以此作为今天的最后一个问题。 + +问题一:目前互联网企业在GUI测试上的投入在不断减少,而为什么我要花这么多精力去讲GUI测试? + +这个问题,很多人都想知道答案。那么我就说说这么设计的考量吧。 + +当前互联网产品的测试重点的确逐渐在向后端(API)迁移,GUI自动化测试所占比重越来越低,出现这种情况的原因主要有两个: + + +GUI自动化测试用例的ROI一直上不去,投入较大。一方面,测试用例本身还很难做到100%的稳定;另一方面,被测系统界面发生变化时,测试用例的维护成本一直高居不下。 +微服务架构的普及使得后端逻辑可以被重用到大量的前端界面中,比如同一个后端API既可以通过PC端的浏览器请求来访问,也可以通过移动端Native App的请求来访问,此时保证后端API的质量就成了保证前端质量的基础。也是因为这个原因,API测试取代了部分GUI自动化测试。 + + +这么说来,我们完全就应该把测试重点放在API端,而没必要花很大的精力去做GUI的自动化测试。但现实情况是,如果你没有站在终端用户的角度,对系统基本业务功能通过用户实际使用的GUI做过测试,你是否敢直接发布上线。 + +很多时候,后端API的功能都正常并不能保证前端的GUI功能测试就一定没有问题。所以,现在很多互联网企业依然会在GUI自动化测试上做投入,只不过是将GUI自动化测试用例的设计原则从原本“全面覆盖”,进化成为了“只覆盖最基本的核心业务功能”。 + +问题二:是否应该在页面对象模型中封装控件的操作? + +这其实是个测试架构设计的问题,而且一直以来都有争议。 + +如果在页面对象中封装控件的操作,实现起来相对简单,而且符合传统的面向对象的设计思想,即:一个对象既有对象描述又有对象操作。 + +但是,从测试架构的层次上来看,这种设计不够规整,而且将对象的识别和操作进行了耦合。而这种耦合就意味着需要对对象的识别和操作一同进行更新和版本管理,但现实情况是对象的识别需要经常更新,而对象的操作相对来说很稳定,这种不必要的依赖关系又增加了测试的维护成本。 + +如果将页面对象和对象的操作分开封装,即:让对象操作的类依赖于对象定义的类。这种设计,在测试架构的层次上来看比较清晰,而且可以分别来实现页面对象识别和对象操作的版本管理。同时,如果你已经采用了页面对象自动生成技术,就可以直接生成页面对象类,从而保证页面操作类的稳定。这样做的好处也很明显,即能够以自动化的方式来降低由页面变化引起的改动工作量。 + +由此可见,这两种方式各有优劣。那么,根据我的经验来看,对于初级阶段的GUI自动化测试,直接在页面对象中封装控件操作的方式更简单直接;而随着GUI自动化测试不断成熟,在需要考虑引入页面对象的版本管理以及页面对象的自动生成的时候,将页面对象和对象的操作分开封装的方式,就是大势所趋了。 + +问题三:如何通过重试(retry)机制,来应对由随机的页面延迟造成的控件识别失败? + +正如我在第17篇文章《精益求精:聊聊提高GUI测试稳定性的关键技术》中提到的,重试可以是步骤界别的,也可以是页面级别的,甚至可以是业务流程级别的。 + +而在这其中,最下面的一层重试机制是在控件操作这一层的。比如,控件识别失败的时候,我们会在自动化测试框架层面发起重试。一般来讲,我们会在自动化测试框架中的控件操作函数上封装重试逻辑,而且默认情况下,我建议你可以启用这个级别的重试。 + +再往上一层是业务流程,也就是Business Flow的重试。如果一个业务操作失败了,我们就可以考虑重新执行这个业务流程。 + +但是,这其中需要特别注意的是, 业务流程对数据本身可能存在依赖,所以业务流程重试的合理性需要根据测试用例的上下文来决定,尤其要关注测试数据是否可以支持可重复执行。 + +一般来讲,我们会在自动化测试框架中实现Business Flow的重试。但是,我建议默认关闭该功能,只在你确定需要进行业务流程级别的重试时才显式启用Business Flow层面的重试。这么做的主要原因是,业务流程的重试会涉及测试数据的问题,只有当你非常肯定业务流程的重试不受测试数据的影响时,我们才会人为启用业务流程级别的重试。 + +最上面一层是测试用例级别的重试。当一个测试用例执行失败的时候,我们可以考虑重新执行整个测试用例,一来希望排除随机出错的可能,二来是评估出错的步骤是否可以重现。 + +这种测试用例级别的重试,一般是由CI流水线完成的,而不是由自动化测试框架实现的。CI流水线会整理出所有失败的测试用例列表,然后在CI流水线脚本中发起重试。 + +问题四:故事板样式的GUI测试报告,具体是如何实现的? + +故事板样式的GUI测试报告,指的是按时序对GUI操作界面进行截图生成的测试报告。对于这类报告的实现主要考虑两个方面的因素: + + +一是,获取屏幕截图。这一步,我们可以通过封装控件操作函数来实现,比如在自行封装的click函数中,先调用截图函数然后再调用真正的click操作。 +二是,截图在报告中的展现形式。这一步,我们可以直接使用现成的HTML5的PPT框架,这样我们只要按照框架要求来生成屏幕截图的数据结构即可,而无需去关注复杂的HTML5的处理逻辑。 + + +这里,我强烈推荐reveal.js。eBay就是用这个框架开发了自己的故事板样式的GUI测试报告。其中,在第18篇文章《眼前一亮:带你玩转GUI自动化的测试报告》中的测试报告截图就是基于reveal.js实现的。关于reveal.js的详细用法和数据结构,请参考这里。 + +问题五:关于微信小程序的自动化测试,以及用到的测试工具。 + +很多读者在留言中问到了微信小程序的测试问题,这里我稍微展开下。 + +其实,微信小程序的测试,在测试设计和执行思路上同移动端测试并没任何区别。那为什么很多人会问到微信小程序的测试呢? + +我觉得主要原因是,在实际开展自动化测试的时候,由于缺少专业的、有效的工具,使得微信小程序的测试在我们看来难以执行。 + +其实呢,有一款来自腾讯的微信小程序测试工具XTest就是个不错的选择,可以很方便地支持测试用例的录制。这里有一篇使用这个工具进行实际小程序测试的文章,你可以从中获得一些使用细节。 + +最后,感谢你能认真阅读第12~21这10篇文章的内容,并写下了你的想法和问题。期待你能继续关注我的专栏,继续留下你对文章内容的思考,我也在一直关注着你的留言、你的学习情况。 + +感谢你的支持,我们下一期答疑文章再见! + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送答疑解惑第二期.md b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第二期.md new file mode 100644 index 0000000..dfaabab --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第二期.md @@ -0,0 +1,146 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 答疑解惑第二期 + 你好,我是茹炳晟。 + +今天这篇文章是“答疑解惑”系列文章的第二期,我还是选取了五个问题。这五个问题来自于第6~11篇这6篇文章,其中第9和第10篇这两篇文章中的两个问题被我合并为了一个问题,并且我会针对这个问题,再次为你简单梳理一条学习路径。 + +然后,我还会选择这6篇文章下的精彩留言,为你稍作解答、分析。 + +在这篇文章中,我依旧为你添加了每篇文章的链接,并用一句话概括了这篇文章的主要内容,你也可以再次回到这篇文章中,回忆一下第一次阅读后的想法,看看第二次阅读又有了哪些新的感想。欢迎你继续给我留言,我仍在关注着你的问题、反馈,希望可以为你提供更多的帮助。 + +现在,我们就开始今天的五个问题吧。 + +问题一:你在使用代码覆盖率工具的时候,遇到过哪些“坑”? + +在专栏第6篇文章《你真的懂测试覆盖率吗?》中,我针对代码覆盖率这个主题,和你分享了代码覆盖率的的价值、局限性,并结合JaCoCo分析了代码覆盖率工具的实现原理。这也是这个专栏中第一篇为你讲解某种测试工具实现原理的文章,希望你可以认真体会。 + +通过阅读这篇文章,我希望你回顾一下曾用过的代码覆盖率工具,并说说你的使用感受和遇到的“坑”。 + +留言中,我也看到了一些留言很精彩,提到了自己使用代码覆盖率工具的一些实践。所以,也很感谢你们的分享。 + +接下来,我再和你分享下,在使用代码覆盖率工具时可能面临的两个最大的问题: + + +测试覆盖率越往后越难。 + + +统计代码覆盖率的根本目的是,指导用户的测试用例设计。也就是说,我们要通过代码覆盖率的结果去发现哪些代码没有被执行到,以此为依据再去设计有针对性的测试用例。 + +在这个过程中,你会发现前期达到一个不错的覆盖率指标(比如70%)还是比较容易的,但是越往后就越难提高了。 + +因为,后期没有覆盖到的代码往往都是一些出错异常分支的处理,为了能够覆盖这部分内容,往往需要构造特殊的数据和环境。很多时候,这些数据和环境并不好得到,就不得不采用各种Mock的手段来实现。 + +因此,在实际项目中,到底要不要一定到达很高的覆盖率,就应该根据项目情况结合风险驱动的概念来综合分析了。 + + +推行代码覆盖率的初期阶段,很难要求很高的覆盖率指标。 + + +代码覆盖率的挑战并不是来自于技术本身,而是来自于管理。很多公司在刚刚推行覆盖率统计的时候,会发现各个模块和项目的覆盖率普遍较低,有些甚至还不到20%。这时,我们就不应该依靠行政手段来强行规定高的代码覆盖率。 + +因为这会在短时间内增加很多工作,一来会引起开发人员的抵触与反感,二来会耽误项目本身的进度。此时最好的做法是采用持续改进的策略,也就是随着迭代更新,代码覆盖率不允许出现下降的趋势,至少保持持平或者逐渐增长的态势。 + +问题二:你在填写软件缺陷报告时,还有哪些好的实践值得分享呢? + +在专栏第7篇文章《如何高效填写软件缺陷报告?》中,我分享了想要把发现的缺陷准确无歧义地表达清楚,一份高效的软件缺陷报告应该包括的内容,以及需要注意的问题。 + +在这篇文章最后,我希望你分享一下自己在填写软件缺陷报告时,还有哪些好的实践。 + +在这里,我想再和你分享另外一个观点。你在实际提交软件缺陷的时候,有没有感觉这个过程很繁琐。对于缺陷的重现规律和步骤需要做很多尝试,然后写文档时还有很多工作量,比如需要考虑措辞和语句的组织,这往往会花费你不少时间。那有没有什么好方法可以减少写文档的工作量吗? + +其实,对于传统软件的开发流程来讲,这种重量级的缺陷报告是必须的。特别是对于一些大公司来说,开发人员和测试人员可能散布在全球不同时区的地域,这种严格的缺陷文档就很有必要了。 + +但是,现在的很多互联网企业,尤其是国内的互联网企业,所有的工程技术人员都在一个办公室,而且普遍采用敏捷开发的模式,此时面对面地沟通软件缺陷的效果将远远好于文档描述,而缺陷报告本身的作用也会退化成一条简单的记录。 + +所以,缺陷报告的详细程度应该在很大程度上取决于团队特征。 + +另外,我发现读者在文章的留言中也提出了很多建设性的方法。比如,下面这位昵称为“卫宣安”的读者,提出了让开发人员主动来认领缺陷的方式。如果说所有的开发人员都具有很强的责任心,同时系统规模也不是太大,或者说报告的缺陷数量不是很多的情况下,这的确是解决问题的一个好思路。但是,当团队规模比较大,缺陷数量也比较多的时候,要求每个开发人员都去挨个查看所有缺陷的效率就会很低。 + + + +那么,在这种情况下,我们是否有更好的方法来解决这个问题呢?答案就是,采用基于AI和机器学习的缺陷分类方法。我们可以通过缺陷的特征值提取,并结合分类算法对缺陷应该归属的团队进行自动分类。 + +在eBay的自动化测试体系中,就有专门的系统对失败用例和缺陷做自动进行分类。 + +问题三:你觉得在实际工程项目中,一份高效的测试计划应该包括哪些内容? + +在第8篇文章《以终为始,如何才能做好测试计划?》中,我和你分享了虽然在敏捷开发模式下,软件测试不再局限于厚重的、正式的计划文档,但是测试计划的重要性丝毫没有发生变化。一份成功的测试计划,依旧必须要清楚地描述出测试范围、测试策略、测试资源、测试进度和测试风险预估这五个最重要的方面。 + +而在读完这篇文章之后,我希望你思考的是,在一份测试计划中,除了这五个最最关键的内容外,你觉得在实际工程项目中还需要再增加的内容,以及是不是所有的项目都需要有很详实的测试计划。 + +其实很多时候, 你会发现计划的速度远远赶不上变化,尤其是互联网产品的开发。就像下面这两位用户在留言中描述的现象,相信你在实际的工作中一定遇到过类似的场景。 + + + +- +所以,这个时候,你有没有反问过自己一个问题,此时文档化的详细测试计划还真的有必要吗?或者说有没有可能采用轻量级的测试计划。 + +首先,轻量级的测试计划并不是说没有计划,而是指计划的文档化表现形式应该尽可能简单,只是用一些关键词来描述纲领性的东西。这样做,一来可以降低测试计划本身的写作时间;二来当测试计划由于各种原因发生变化的时候,也可以非常快速灵活地进行修改和更新。 + +其实,目前的敏捷开发模式(比如Scrum模式)下的测试计划就会在每个Sprint最开始的时候,以非常轻量级的方式来确定测试计划,有些时候甚至可以没有文档化的测试计划。这时,关于测试范围、测试策略和测试设计之类的内容都在测试人员的脑子中。 + +注意,这时候虽然没有书面的测试计划,但并不代表说没有测试计划。 + +问题四:软件测试工程师的高效进阶路径是什么? + +我在第9篇文章《软件测试工程师的核心竞争力是什么?》和第10篇文章《软件测试工程师需要掌握的非测试知识有哪些?》中,分别和你分享了一个软件测试工程师需要具备的核心竞争力,以及需要掌握的非测试专业知识。看到这两篇文章后面,有很多用户留言说:觉得很迷茫,抓不住自己要重点培养的能力、要怎么快速学习。 + +虽然今年7月6日我在极客时间做直播时回答过这个问题,但这里为了帮助你快速找到一条适合自己的道路,我就再简单为你梳理下。 + +首先,我把在软件测试岗位上工作了0~5(或者0~3)年的工程师,划分为初级测试人。为什么有这个划分呢?因为0-5年工作经验的测试工程师往往工作的中心都还是在软件产品测试本身,还没有将测试上升到质量工程的高度。当然了,我这里说的测试指的是广义上的测试,包括功能测试、自动化测试、性能测试等各个方面。 + +然后,我把初级测试人可以进阶的方向,归纳为了三类: + + +业务专家,也就是业务功能测试方向; + +开发测试工程师,也就是自动化测试方向,指的是把业务功能的测试转换成自动化的脚本; + +测试开发工程师,指的是负责开发测试平台、工具,以及服务。 + + +接下来,我就简单总结下,向每个方向进阶的相对高效的路径吧。 + +首先,如果你想成长为一名业务专家的话,那你就需要精通于某一项具体的测试业务,比如说电子商务网站、EPR系统和SAP系统等等。这样的角色更像是产品经理,你需要能准确把握业务产品的定位,了解整个业务流程的操作、用户的使用习惯,以及如何提到整个产品的转化率。 + +而要成为这样的人,你首先需要在该领域中有较长时间的积累,能够从真正的终端用户视角来使用被测软件,能够对该软件应用领域的行业知识有比较清楚的了解。 + +但精通于某一个业务领域的缺点是,一旦离开了这个业务领域,之前的业务积累就没用了。 + +其次,如果想要成长为一名开发测试工程师的话,你平时需要积累高效的测试用例组织方法、对自己用到的测试框架的优劣势有深入理解,并在使用某种测试工具时要深入到其原理的层面。 + +这样的话,你就可以快速具备测试用例设计、测试框架选型、灵活运用测试工具的技能。并且,你也会因为这种长期的“知其所以然”的积累,可以从容应对新的技术趋势。比如说,你掌握了Selenium 1.0、2.0的实现原理后,对API测试的原理也就可以做到触类旁通了。 + +最后,如果想要成长为一名测试开发工程师的话,那你需要培养的是开发能力,以及测试意识。说白了,测试开发工程师,更像是一个开发人员,只不过需要在理解测试上下文的基础上,为其他测试工程师开发一些平台、工具、服务。这时,我建议的成长路径,就是一个开发工程师的成长路径了。 + +问题五:你觉得你现在团队采取的自动化测试策略,有哪些好的地方,又有哪些需要改进的? + +在第11篇文章《互联网产品的测试策略应该如何设计?》中,我和你分享了互联网产品的特性决定了它采用的测试策略,遵循的是“重量级 API 测试,轻量级 GUI 测试,轻量级单元测试”的原则,更像是一个菱形结构。这与传统软件产品的金字塔测试策略有所区别。 + +而在文章最后,我希望你可以针对你所在公司采取的测试策略,谈谈自己的看法。 + +这里,我可以给你分享一下eBay的自动化测试策略的演变和发展路径,希望可以帮助你理解什么叫作因地制宜地选择和设计最适合你的自动化测试策略。 + +eBay在早期阶段,也和现今大多数公司一样,采用的是基于GUI来大规模开展自动化测试。尤其早期阶段的网站本身还不是太复杂,而且还不是现在的微服务架构,所以基于GUI的自动化测试在页面对象模型和业务流程模型的支持下,能够很好地完成业务功能的验证和测试。 + +这个阶段,我们关注的重点是通过GUI层面的操作来对整个网站的业务功能进行验证。 + +可是,后来随着业务的不断发展与壮大,基于GUI的测试用例数量不断增长,同时再加上浏览器兼容性测试等的要求,测试用例的数量越来越多,测试执行的效率也越来越低下,所以单靠GUI测试已经很难满足全面测试的要求了。 + +再加上系统架构本身也从原本的单体应用逐渐发展成为了微服务,甚至是服务网格的架构形式,同时普遍采用了前后端分离的架构设计,所以此时的测试重点也就从原来以GUI为主转变成了以后端API为主的阶段。 + +此时GUI测试只会去覆盖一些最最基本的业务功能,而API测试则会关注各种参数的组合以及各种边界场景。 + +通过这个实际的例子,我们可以看出,并不存在一个放之四海而皆准的自动化测试策略。测试策略的选择,在很大程度上取决你的测试诉求以及被测系统本身的架构设计。 + +最后,感谢你能认真阅读第6到11这六篇文章的内容,并写下了你的想法和问题。期待你能继续关注我的专栏,继续留下你对文章内容的思考,我也在一直关注着你的留言、你的学习情况。 + +感谢你的支持,我们下一期答疑文章再见! + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送答疑解惑第五期.md b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第五期.md new file mode 100644 index 0000000..a548017 --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第五期.md @@ -0,0 +1,152 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 答疑解惑第五期 + 你好,我是茹炳晟。 + +今天的“答疑解惑”系列文章,我们一起来解决性能测试系列中7篇文章的问题。你可以通过下面对每篇文章的简单总结回顾一下文章内容,也可以点击链接回到对应的文章复习。 + +现在,我们就开始今天的问题吧。 + +问题一:你在性能测试项目中,选择具体的系统吞吐量指标时,会考虑哪些因素呢? + +在专栏的第28篇文章《带你一起解读不同视角的软件性能与性能指标》中,我首先从终端用户、系统运维人员、软件设计开发人员和性能测试人员,这四个维度介绍了软件系统的性能到底指的是什么;然后,和你分享了软件性能的三个最常用的指标:并发用户数、响应时间、系统吞吐量。 + +而在这篇文章最后,我针对系统吞吐量提出了这样一个问题:系统吞吐量的表现形式有很多,比如“Requests/Second”“Pages/Second”“Bytes/Second”等,你在性能测试项目中,选择具体的系统吞吐量指标时,会考虑哪些因素呢? + +其实选择哪种类型的吞吐量指标,和你的测试目标以及被测系统的特点是息息相关的。 + +如果你的被测系统是后台处理系统,而且你的测试目标是要优化它的处理能力,那么这个时候你的关注点必然就是每秒能够处理的请求数量,即Requests/Second。当然如果你发现你的后台处理能力有可能是受限于网络传输的带宽,那么这个时候你就可能需要去考虑“Bytes/Second”这种类型的吞吐量指标了。 + +总结来讲,选取哪个吞吐量指标,取决于你最关注的内容。 + +下面这位昵称为“假装乐”的读者的留言很典型,是很多刚刚结束性能测试的同学都会有的疑惑。 + + + +其实,性能测试应该贯穿于软件研发生命周期的各个阶段: + + +单元测试阶段就要衡量代码级别的时间复杂度和空间复杂度,以及多线程并发情况下的功能准确性等等; +每个API也需要进行单独的性能测试和评估; +集成测试阶段需要考虑跨组件或者模块的数据大小,以及缓存的使用情况等等; +系统测试阶段,还需要从模拟终端用户负载的角度来衡量系统全局的性能指标等等。 + + +说到底,一个最基本的原则就是,性能问题一定是越早发现越容易定位,也越容易被修复。而到了软件研发生命周期的后期,性能问题的定位成本和复杂度会呈指数级增长。 + +所以,如果你有机会去了解大型软件公司的测试的话,就会发现它们没有所谓的性能测试团队,而是有一个性能工程团队。这个团队会从软件研发生命周期的各个阶段去测试、评估和优化软件的性能。 + +问题二:你在实际工程项目中,接触过性能测试的哪些方法,其中遇到了哪些问题,又是如何解决的? + +在专栏第29篇文章《聊聊性能测试的基本方法与应用领域》中,我通过一个医院体检的例子,和你分享了并发用户数、响应时间和系统吞吐量这三个指标之间的关系和约束;然后,又和你分享了性能测试七种常用方法,以及四大应用领域。 + +在这篇文章最后,我希望你能够分享一下你在实际开展性能测试时,都遇到过哪些问题,又是如何解决的。虽然这篇文章的留言比较少,但也能从中看出大家在开展性能测试的时候,确实也如我当初一样,遇到了各种各样的问题。 + +那么,现在我就来和你分享一下性能测试中可能遇到的一些典型问题吧。 + +其实,性能测试中可能会遇到的问题实在是太多了,架构中的各个层面、每个软件模块、模块配置、数据库中的数据量、多线程的锁机制、进程设计、JVM配置参数、数据库配置参数,以及网络参数等等,都会成为性能测试中的问题。 + +可以说,性能测试的问题,只有你想不到的,没有你遇不到的。所以,如果我通过一个实际案例和你分享的话,肯定会是长篇大论,有违答疑系列文章的设计初衷。为什么?因为性能测试的问题,一般都和架构、设计、配置、数据量有着密不可分的关系。所以,我今天会通过一个简化的案例,和你展开分享,意在抛砖引玉。 + +首先,我想问你一个问题:当你做压力测试的时候,你觉得硬件资源占用率是低好,还是高好?很多人可能会说,当面对大量并发请求的时候系统资源占用率当然低好。因为资源用得少,说明系统后续的容量可以继续大幅度扩充。 + +听起来很有道理,但真的是这样吗? + +我就遇到过一个测试,当你不断加大并发访问量的时候,系统CPU的使用率一直处在15%左右,不管并发用户数如何加大,CPU的使用率一直上不去,但是事务响应时间却随着并发用户的上升而有持续上升的趋势。所以,一定是有某些机制限制了CPU的使用。 + +其实,在这种情况下我们希望看到的是,随着并发用户数的不断增长,这些CPU敏感性的并发操作会尽可能多地去使用CPU的计算能力,而不是现在这种CPU使用率上不去的情况。 + +为此,我分析了这部分的代码逻辑,发现其中使用了一个固定大小的数组来存放并发任务进程的句柄,当这个数组满了的时候,新进程的创建处于阻塞状态,只有之前的进程处理结束后,数组中出现了空位,新的进程才会被创建。当时这个数据的大小是128,也就是最多只能有128个并发进程同时运行,我当时就怀疑这是限制CPU使用率的主要原因。 + +为了验证这个想法,我直接将这个固定数组的大小调整成了256,然后继续并发测试。果然,CPU的使用率徘徊在30%左右,就验证了我的猜测。 + +那么,接下来就需要修复这个问题了。显然,这是一个设计上的问题,压根儿这里就不应该采用固定大小的数组,而是应该采用可变长度的数据结构。 + +问题三:你接触过哪些后端性能测试工具?你认为这款工具中,有哪些好的设计吗? + +在专栏第30篇文章《工欲善其事必先利其器:后端性能测试工具原理与行业常用工具简介》中,我以问答的形式,和你分享了后端性能测试的理论,以及工具使用的问题。这也是这个专栏中,唯一一篇采用问答形式的文章,有没有感觉读起来比较省力呢? + +因为我后面增加了一篇JMeter的加餐文章,所以这里我也就不再过多地介绍后端性能测试工具了。这次,我来回答一下“Robert小七”提到的问题。 + +正如我在今天的第一个问题中提到的,高效的性能测试一定是从源头抓起的,也就是研发的每个阶段都需要进行性能测试,而不是等到系统开发完了,再一次性地进行黑盒级别的性能测试。 + +所以,对每个API的性能测试是非常必要的。而且,很多公司,比如eBay等,都会对每个API进行独立的性能测试。其实,在对API开展独立的性能测试之前,还需要在一些关键代码上做基于函数方法的性能测试和评估。直到这些都完成了以后,才会开展基于性能场景的测试。 + + + +问题四:你在工作中接触过哪些前端性能测试工具,它们各自有什么特点呢? + +在专栏的第31篇文章《工欲善其事必先利其器:前端性能测试工具原理与行业常用工具简介》中,我以一个具体网站为例,和你分享了使用WebPagetest进行前端性能测试的方法,以及前端性能相关的主要概念与指标。 + +在这篇文章最后,我希望你可以分享一下自己曾使用过的前端性能测试工具。 + +在这里,我来分享下我的经验吧。 + +前端性能测试工具除了我在文章中介绍的WebPagetest,比较常用的还有YSlow,但是这些工具的基本原理是类似的,所以如果你已经掌握了我在这篇文章中介绍的WebPagetest的原理的话,对于YSlow等前端性能测试工具的原理,基本就可以做到触类旁通了。 + +此外,有些公司还会特别关注一些特定的前端性能指标。这些性能指标,一般不能从性能测试工具中直接得到,需要自行定制开发。 + +这个昵称为“木然”的用户,提出的问题很典型。很多刚开始使用WebPagetest的同学都会有这个疑问,但是很不幸,WebPagetest是无法来做这种需要登录才能访问到的页面的前端性能调优的。 + + + +WebPagetest这类工具的初衷,就是纯粹站在前端页面优化的角度来设计的,本身并不会涉及业务操作,所以对这块的支持很弱。虽然WebPagetest支持Http Auth以及自定义脚本的扩展,但是Http Auth还是会受到服务器端本身配置的影响,而自定义脚本的扩展还受限于特定的浏览器,所以实际的应用价值有限,也很少有人去用。 + +而至于有什么更好、更灵活的方法来处理这种需要登录,以及特定业务操作的前端页面性能优化,很可惜,目前我并没有什么好的方案。如果你对此有一些好的想法或者实践的话,就给我留言一起讨论吧。 + +问题五:在实际工作中,获取并细化性能测试需求,需要怎么做,注意哪些问题呢? + +在专栏的第32篇文章《无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(上)》和第33篇文章《无实例无真相:基于LoadRunner实现企业级服务器端性能测试的实践(下)》中,我从最基础的性能测试需求获取、LoadRunner的原理开始,和你分享了基于LoadRunner实际开展企业级服务器端性能测试的整个过程。 + +通过这两篇文章,我希望能够帮你快速建立服务器端性能测试的全局观,并了解各主要步骤的关键技术细节。其实,正如我在文中所说的,在开展整个性能测试的过程中,测试需求的获取是其中最关键、最难的一个环节。这里我再针对测试需求的获取,和你分享一个实例吧。希望可以真正帮到你。 + +很多时候,我们从产品经理那里拿到的需求是很笼统的,必须经过必要的分析和细化才能转换为可以用于性能测试场景设计的需求,就像文章中提到的“每天支持完成8000个体检”的例子。这样的例子还有很多,比如我们经常可以看到类似“系统最大支持500万用户同时在线”的需求,这同样是一个看似具体,实则非常笼统的性能需求。 + +500万用户在线,这些在线的用户在具体执行什么类型业务操作,对后端服务器造成的压力差别是巨大的。比如,这500万个用户都在执行查询操作和这500万个用户什么不做,对后端服务器的压力是天壤之别的。 + +那么,这里需求获取的难点就是,要能够准确估算这500万在线用户执行的各种类型的业务操作所占的百分比,只有这样才能真实、客观地反应后端服务器承受的实际压力。 + +但是除此之外,还有很多的性能需求并不是直接从产品经理那里获取的,而是需要资深的性能测试人员根据以往的经验,以及同类系统和竞品的业务流量来自己估算。 + +比如,产品经理不会告诉你一个实现具体业务的API操作应该要在多长时间内完成;产品经理也不会明确告诉你在API层面的业务吞吐量是多少。这些测试需求都是需要性能测试人员来预估,甚至是基于一些实验来细化的。 + +所以说,性能需求的获取是一个关键且困难的环节。 + +这个昵称为“Sunshine”的用户,在留言中的问题虽然简单,但也是个典型问题。我来和你一起分析一下。 + +关于如何实现每隔10 s增加100个用户的方法,其实还算是简单。LoadRunner的场景设计界面中,直接提供了该功能。你可以通过GUI界面填写你的用户增加策略,比如在什么时间段内每多少秒增加或者减少多少个并发用户,并且LoadRunner还会自动提供并发用户数随着时间变化的曲线图。你甚至可以直接修改这个曲线图,来修改用户数量变化的规律,使其符合你的需求。 + +另外,场景设计中的很多配置都可以在LoadRunner的场景设计界面中实现。具体内容,你可以参考LoadRunner的使用文档。 + + + +问题六:你所在企业,在开展性能测试时采用了哪些方法呢? + +在专栏的第34篇文章《站在巨人的肩膀:企业级实际性能测试案例与经验分享》中,我挑选了最重要的四类性能测试方法(性能基准测试、稳定性测试、并发测试,以及容量规划测试),和你分享如何在实际项目中完成这些测试,确保软件的性能。 + +通过这篇文章,我希望可以帮助你从整体上理解性能测试,形成一个体系知识。而在这篇文章最后,我希望你能够分享一个你所在企业采用的性能测试方法,大家互相取长补短。 + +对于eBay这样的大型全球化电商企业,性能测试除了文章中提到四类性能测试方法以外,还会开展一些其他类型的性能测试。 + + +对于关键业务代码以及中间件代码的核心算法部分,一般都会开展基于时间复杂度和空间复杂度的代码级别的性能评估; +对于各个独立的中间件或者公共服务组件本身,也会开展性能基准测试和容量规划测试; +对于基于微服务的各个API接口,会开展性能基准测试和压力测试; +对于前端Web页面,会开展基于前端性能的调优; +对于整体系统,会不定期开展全链路压力测试,其中还会使用历史流量回放等技术来模拟海量实际的并发场景。 + + +以上这些是从性能测试的类型来讲的。从性能测试工具的支持上来看,eBay还建立了一些内部使用的公共性能测试平台,任何人都可以通过这些性能测试平台方便地发起压力负载,而不用去关心诸如Load Generator之类的细节,对于后端性能测试以及API性能测试,你只要上传压测脚本和性能测试场景设计,就能很方便地发起测试。这个很像淘宝对外提供的PTS服务。 + +其实,上述的这些方法适用于很多的互联网产品。而至于到底实施哪几条,则取决于你想通过性能测试希望达到什么样的目的。 + +最后,感谢你能认真阅读第28~34这7篇文章的内容,并写下了你的想法和问题。期待你能继续关注我的专栏,继续留下你对文章内容的思考,我也在一直关注着你的留言、你的学习情况。 + +感谢你的支持,我们下一期答疑文章再见! + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送答疑解惑第六期.md b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第六期.md new file mode 100644 index 0000000..ad50bfc --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第六期.md @@ -0,0 +1,121 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 答疑解惑第六期 + 你好,我是茹炳晟。 + +今天的“答疑解惑”系列文章,我们一起来解决测试数据准备和测试基础架构这两个系列8篇文章中的问题。你可以通过下面对每篇文章的简单总结回顾一下文章内容,也可以点击链接回到对应的文章复习。 + +这两个系列下的文章留言已经很少了,或许是你没有坚持学习,也或许是这部分内容并没有切中你现在的痛点。毕竟,广义上的软件测试,包括了测试数据平台、测试执行平台等,而我们也不能每一种都有机会接触。 + +但这里,我想再给你打打气,有些知识虽然你在接触时感觉自己不会用到,但随着技术发展、公司业务转型,或者是你个人的职业晋升,都会需要越来越宽广的知识面,这也正应了我在专栏里面提到的一个比喻,测试工程师通常是“广度遍历”,关注的是“面”。所以,坚持学习,才是我们从“小工”蜕变为“专家”的正确路径。 + +问题一:有些时候,我们需要创建消息队列里的数据,这类数据应该如何创建呢? + +在第35篇文章《如何准备测试数据?》中,我从测试数据创建的维度,和你详细分享了生成测试数据的四种方法:基于GUI操作生成测试数据、通过API调用生成测试数据、通过数据库操作生成测试数据,以及综合运用API和数据库的方式生成测试数据。 + +其实,我们要创建的测试数据并不仅仅局限于数据库,很多时候还需要创建消息队列里面的数据。所以,在阅读完这篇文章后,我希望你可以思考一下如何处理这类问题?或者,请你分享一下你曾经是如何解决这个问题的。 + +这里,我来分享下我的方法吧。 + +通过模拟消息队列中的测试数据,可以实现各个被测模块之间的解耦,这个思路非常关键。至于如何来模拟消息队列中的测试数据,在技术上其实没有任何难度。 + +我们通常的做法是,在测试数据工具的底层封装一个工具类,这个工具类内部通过调用消息队列的API或者操作接口函数来实现消息队列的CRUD操作,然后凡是需要改变消息队列中数据的地方,都通过这个工具类来完成实际操作。 + +问题二:你所在公司,采用是什么测试数据策略?为什么选用了这种策略? + +在专栏的第36篇文章《浅谈测试数据的痛点》中,我和你分享了选择不同时机去创建测试数据,是为了解决不同的数据准备痛点。为了解决这些痛点,我的经验是把测试数据分为“死水数据”和“活水数据”,其中:“死水数据”适合用Out-of-box的方式,而“活水数据”适合采用On-the-fly的方式。 + +在这篇文章最后,我希望你可以分享一下自己项目中采用的是什么测试数据准备策略,以及会不会使用线上真实的数据进行测试。 + +这里我来分享下eBay在准备测试数据时的策略吧。 + +eBay在准备测试数据时,采用的并不是单一的某种方法,而是针对实际的业务场景选取了不同方法,最后将这些方法进行组合完成整个测试的数据准备工作。可谓是多管齐下。 + +这里,我和你分享下eBay主要使用了的几种策略: + + +能用API的地方就一定不用数据库操作; +数据库操作只用在API无法支持,以及需要批量创建性能测试数据的场景; +对于“活水数据”,比如订单和优惠券等,一定采用On-the-fly的方式实现测试数据用例内的自维护; +对于“死水数据”,比如商品类目和品牌等,一定采用Out-of-box的方式来提高测试数据准备的效率; +对于性能测试的背景数据,采用生产环境的实际数据(注意,这里使用的实际生产数据是经过了必要的“脱敏”操作的); +对于复杂数据,采用了预先在系统中预埋template数据,然后在需要使用的时候,通过复制template数据然后再修改的策略; +对于生产环境的测试,除了全链路压力测试,都会采用真实的数据来进行。 + + +所以,测试数据策略的选择,最重要的是适合自己所在的公司或者项目的实际情况。这里我和你分享的eBay的实践,希望可以在针对特定场景选择测试策略的时候,可以感到有据可依。 + +问题三:如果你所在公司,也处于测试数据1.0时代,你们还用到过哪些测试数据准备函数的实现方法吗? + +在专栏的第37篇文章《测试数据的“银弹”- 统一测试数据平台(上)》和第38篇文章《测试数据的“银弹”- 统一测试数据平台(下)》中,我从全球大型电商企业早期的测试数据准备实践谈起,和你一起分析这些测试数据准备方法在落地时遇到的问题,以及如何在实践中解决这些问题。 + +我希望通过这种遇到问题解决问题的思路,可以带着你去体会时代的演进,理解测试数据准备技术与架构的发展历程,并进一步掌握3.0时代出现的业内处于领先地位的“统一测试数据平台”的设计思路。 + +正如我在文中所说,目前大多数企业都还处于测试数据1.0时代。其实,这也很正常。因为,1.0时代的测试数据准备函数,才是真正实现数据创建业务逻辑的部分,后续测试数据准备时代的发展都是在这个基础上,在方便用户使用的角度上进行的优化设计。所以说,测试数据1.0时代是实现后续发展的基础。 + +其实,对于测试数据准备函数的内部逻辑实现来说,除了我在文章中提到的基于API、数据库,以及API和数据库相结合的三种方式以外,还有一种方法也很常用,尤其适用于没有API支持的复杂测试数据场景。 + +这种方法就是,事先在数据库中插入一条所谓的模板数据,在下一次需要创建这类数据的时候,就直接从模块数据种复制一份出来,然后再根据具体的要求来修改相应的字段。 + +这里需要特别注意的是,对于1.0时代的测试数据准备函数来说,我们还需要建立工具的版本管理机制,使其能够应对多个不同的数据版本。 + +问题四:目前Selenium Grid已经有Docker的版本了,你有没有考虑过可以在云端搭建Selenium Grid呢? + +在专栏的第39篇文章《从小作坊到工厂:什么是Selenium Grid?如何搭建Selenium Grid?》中,我从测试基础架构的概念讲起,并和你分享了传统Selenium Grid 和基于Docker的Selenium Grid的搭建方法。 + +不知道,你有没有在课后去尝试搭建Selenium Grid呢,这其中是否遇到了什么问题?如果你遇到了问题,欢迎你给我留言,我将帮助你一起解决。 + +在这篇文章最后,我希望你可以畅想一下是否可以在云端搭建Selenium Grid。这里我结合eBay的探索,来谈谈我的看法吧。 + +对于一些大公司来说,在云端来搭建Selenium Grid已经被证明是切实可行的,而且也已经呈现出逐渐向云端过度的趋势。这主要得益于云端部署的易维护性和上云本身的便利性。 + +比如,eBay已经实现了在PCF上基于Docker来运行Selenium Grid的方案,其中落地的难点在于配置Docker的网络和IP地址。主要原因是,PCF会为部署的应用提供统一App URL的命名转换。 + +从本质上讲,只要Selenium Grid是基于Docker实现的,那么上不上云本身并没有本质区别。但是,考虑到将来的App以及所有的后台服务都会逐渐向云端过度,所以测试基础架构这块必然也会遵循这个趋势,和App以及后台服务的环境保持在一个技术栈上,将会减少公司整体基础架构的多样性,从而提高研发效能。 + +问题五:你觉得测试基础架构的设计和搭建过程中,还有哪些点需要再优化呢?又可以从哪些方面进行创新呢? + +在专栏的第40篇文章《从小工到专家:聊聊测试执行环境的架构设计(上)》和第41篇文章《从小工到专家:聊聊测试执行环境的架构设计(下)》中,我首先和你分享了测试执行环境的概念,然后为你剖析了测试基础架构的演进历程,希望可以帮助你理解测试基础架构的设计,最终可以定制一套适合自己的测试基础架构。 + +学习完这两篇文章,我希望你思考的是,你所在团队,还可以再测试执行环境的架构设计上,进行哪些优化和创新? + +其实,测试基础架构的优化和创新都是由问题本身驱动的。如果不是为了解决实际遇到的问题,企业是不会为了追求更新的技术而去寻求测试架构的改进方案的。所以,通常情况下,我们都是在测试或者DevOps的过程中,遇到了问题或者瓶颈才会考虑新的技术与方法。 + + +比如我在文中提到的,为了解决大量测试用例在短时间内执行完成的要求,才出现了测试执行集群的架构; +再比如,当测试用例数量非常多,每次测试结束,需要分析所有失败的用例时,就必须要考虑基于机器学习的缺陷分类方法; +再比如,为了增加测试环境的有效使用时间,避免开发人员要在发生缺陷的环境上实时Debug而造成的测试环境占用问题,我们就会考虑在测试用例执行失败的时候自动获取全链路的日志; +再比如,如果我们的大量测试执行请求都是API调用,我们就可以实现完全基于Docker的并发执行环境。 + + +除了上面这些技术驱动的原因外,还有些是由企业的组织结构驱动的。比如,eBay的某些产品线,CI/CD团队在印度,而测试架构团队在中国。大家都知道CI/CD的流水线脚本和测试执行是强耦合的,而要求两个异地团队实时合作并保证完全同步很困难。为此,我们就需要考虑为CI/CD提供统一的测试接口,这样流水线脚本就可以以解耦的方式与测试集成了。 + +问题六:你觉得在我给出的全球化电商的测试基础架构中,还可以增加哪些与测试相关的服务? + +在专栏的第42篇文章《实战:大型全球化电商的测试基础架构设计》中,我根据自己的实践经验,把大型全球化电商网站全局测试基础架构的设计思路归纳为了“测试服务化”,并用一张图展示了整体的测试基础架构。 + +为了便于你回顾文章内容,我把文中用到的大型全球化电商网站的全局测试基础架构设计图,放到了这里。 + +- +其实,除了图中的各类测试服务以外,你完全可以根据你的业务场景来搭建更多的测试服务,以提高团队的测试效率和研发效能。 + + +比如,你可以提供完整和详细被测系统所有版本信息的服务,通过一个简单的Restful API调用,就可以得到被测系统中所有组件、各个微服务的详细版本信息,还可以自动比较多个被测环境的版本差异。 +再比如,为了解决微服务测试过程中的依赖性,你可以考虑提供基于消费者契约的统一Mock服务,以契约文件作为输入,来提供模拟真实服务的Mock服务,从而方便微服务的测试。 +再比如,你还可以提供压力测试服务,由这个服务统一管理所有的Load Generator来发起测试,这个服务还可以进一步细分为前端性能测试服务,以及后端压力测试服务。 + + +这样的例子还有很多,但在实际工作中,我们还是要根据测试本身的上下文以及被测产品的性质,来决定将哪些需求实现成测试服务会。 + +假如,你的项目只是偶尔会做前端性能测试,那么你就完全没必要去自己实现前端性能测试服务,直接采用现成的工具效率会更高。而如果你的产品需要经常做前端性能测试和优化,而且还不止一个团队会需要这种类型测试的时候,你就应该考虑将前端性能测试服务化了。 + +最后,感谢你能认真阅读第35~42这8篇文章的内容,并写下了你的想法和问题。期待你能继续关注我的专栏,继续留下你对文章内容的思考,我也在一直关注着你的留言、你的学习情况。 + +感谢你的支持,我们下一期答疑文章再见! + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/测试专栏特别放送答疑解惑第四期.md b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第四期.md new file mode 100644 index 0000000..8aa8c1e --- /dev/null +++ b/专栏/软件测试52讲/测试专栏特别放送答疑解惑第四期.md @@ -0,0 +1,137 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 测试专栏特别放送 答疑解惑第四期 + 你好,我是茹炳晟。 + +今天的“答疑解惑”文章,我将针对API自动化测试和代码级测试这两个系列6篇文章中的问题,和你展开分享。 + +我还是会先简单概括下每篇文章的内容,并给出文章链接,帮助你复习相应的内容。同时,如果你再次阅读时还有哪些疑问的话,也欢迎你在文章下面继续留言。我会一直关注着你的学习情况,希望可以扫清软件测试精进道路上的障碍。 + +现在,我们就开始今天的主题吧。 + +问题一:实际项目中,往往会存在按时序的API调用以及异步API调用,这类API测试要如何开展? + +在专栏的第22篇文章《从0到1:API测试怎么做?常用API测试工具简介》中,我以基于主流Spring Boot框架开发的简单Restful API为例,分别介绍如何使用cURL和Postman对其进行最基本的功能测试,希望可以让你先对API测试有个感性认识。另外,在这篇文章中,我还和你分享了目前一些常见的典型复杂场景,以及相应的测试思路和方法。 + +而在文章最后,我希望你思考的是实际项目中往往会存在按时序的API调用以及异步API调用,这类API测试要如何开展?现在,我来说说我的经验吧。 + +我们先一起看看按时序调用的API序列的测试场景。 + +对于此类测试,我一般建议通过GUI操作来录制API的调用。比如,在启用Fiddler的情况下,通过GUI来完成业务操作,随后去分析Fiddler抓取到的后端API请求顺序,然后以此来开发API测试用例。 + +开发测试用例的过程中还需要特别关注前后两个API调用之间的数据传递,比如需要将前一个API调用返回的response中的某个值作为参数传递给下一个API调用。 + +其次是异步API的测试。对于异步API测试的场景,我们往往先会采取“只验证其是否发起了正确的调用,而不直接验证操作结果”的方式。比如,你的被测API是一个异步操作的API,那么我只会去验证这个API是否按照预期发起了正确的异步调用请求,而不会直接去验证异步操作的结果。如果这类测试全部通过后,我们才会考虑真正验证异步操作结果的测试用例。 + +举个实际的例子,假设你的被测API A完成的是下订单的操作。这个API A完成下订单操作要通过调用另外一个API B将订单信息写入到消息队列中去。而真正下订单成功指的是消息队列中的消息被后续服务正确处理并且成功了。此时,这里的后续消息处理就是异步的操作了。 + +那么,当我们要测试这个API A的时候,我们只需要验证它是否正确地发起了对API B的调用即可,而不用关心API B的具体行为结果。 + +也就是说,我们只关注API A是否以正确的参数调用了API B即可,而无需关注API B是否正确地执行了将订单信息写入消息队列的操作,更不用关注,消息队列中的消息被异步处理的结果。 + +注意这里的测试重点,应该更多地放在前面的部分,而真正验证异步操作结果的测试在资源有限的情况下只需覆盖最典型的场景即可。 + +问题二:对基于配置文件的API测试框架,你有哪些看法呢? + +在专栏的第23篇文章《知其然知其所以然:聊聊API自动化测试框架的前世今生》中,我和你分享了API自动化测试框架的发展历程,帮助你理解API测试是如何一步一步地发展成今天的样子,希望可以以这种“知其所以然”的方式加深你对API自动化测试的理解。 + +而在这篇文章最后,我提到了基于配置文件的API测试框架,比如典型的HttpRunner。在此类API测试框架的支持下,测试用例本身往往就是纯粹的配置文件了。如果你用过这个框架的话,我希望你可以谈谈你的看法。 + +对于基于配置文件的API测试框架的确是个不错的方向,尤其是国内的开源框架HttpRunner更是推动了这种测试框架的普及。 + +基于配置文件的API测试框架的优势,可以归纳为以下三方面: + + +降低了测试用例开发的门槛,使得完全没有代码基础的同学也可以很容易地完成API测试; + +可以很方便地将API功能测试用例直接转换成API性能测试的用例(HttpRunner可以使用lucust直接实现这样的转换); + +同时,HttpRunner这类工具还支持直接从网络转发工具得到的HAR中提取API调用的测试用例,进一步降低了API测试用例的开发成本。 + + +所以,基于配置文件的API测试框架很受初学者的欢迎。 + +但是,为了完成一些复杂场景的测试用例设计以及复杂的结果判断,你还是需要具备基本的代码能力,以完成这些复杂场景的测试实现。比如,HttpRunner就会涉及到使用debugtalk.py来实现hook函数的功能扩展。 + +也就是说,完全不写代码的API测试只能覆盖大部分的简单测试场景,如果你要搞定复杂场景的API测试的话,你是要必须掌握一些基本的开发技能,这里没有任何捷径可走。 + +另外,很多读者的留言也很精彩,我这里特地选取了两条供大家参考。从Cynthia的留言中,我看得出她已经完全习得了这篇文章中描述的方法的精髓,这也正是我想要传达给你的最核心的内容。 + +- +Martin在留言的后半部分中提到,通过HttpRunner来实现轻量级的API测试的确是个好方法,也最大程度地发挥了HttpRunner的价值。但是,留言前半部分的“Postman转Python或者Java”的观点我并不是很认同。其实,Postman是有直接代码转换功能的,而且支持各种语言的各种框架,基本可以实现一键操作,所以,其实很多没有采用HttpRunner的企业都还在普遍使用这个方法。 + + + +问题三:如果无法通过API Gateway方法得到契约的话,应该采用什么方法来解决呢? + +在专栏的第24篇文章《紧跟时代步伐:微服务模式下API测试要怎么做?》中,我和你分享了微服务模式下的API测试,旨在帮助你认清庞大的测试用例数量、微服务之间的相互耦合这两个问题的本质,以更好地开展测试。所以,我今天分享这个主题的目的就是,帮你理解这两个问题的本质,以及如何基于消费者契约的方法来解决这两个难题。 + +而在今天这篇文章最后,我希望你思考的是:基于消费者契约的API测试中,对于那些新开发的API,或者加了新功能的API,由于之前都没有实际的消费者,所以你无法通过API Gateway方法得到契约。对于这种情况,你会采用什么方法来解决呢? + +从我的经验来看,因为缺乏契约,所以还是会采用传统的API测试方法,也就是根据API设计文档来设计测试用例。 + +这时,我们采取的API测试策略是: + + +对于已经上线的API我们会通过契约测试来保证质量; +而对于新的API,或者是加了新功能的API,则还是采用传统的基于API设计文档来设计测试用例,同时基于代码覆盖率来指导补充遗漏测试用例的方式来保证质量。当这些新API上线运行了一段时间后,我们就会缩小测试的范围,逐渐向契约测试过渡。 + + +问题四:你所在公司,在进行代码级测试时,采用过哪些方法呢? + +在专栏的第25篇文章《不破不立:掌握代码级测试的基本理念与方法》中,我根据实际工程项目中的实践,总结了五种常见的代码错误,以及对应的四大类代码级测试方法。这里我还想在多啰嗦一句,代码级测试的测试方法一定是一套测试方法的集合,而不是一个测试方法。 + +而在这篇文章最后,我希望你分享一下你所在公司,在进行代码级测试时采用过哪些方法,又是如何具体开展的。这里我来分享下我在eBay的经验吧。 + +在eBay,代码质量保障已经完全纳入了CI/CD流水线。 + +首先,我们基于Sonar启用了静态代码。除了在上传Git的时候触发静态扫描外,开发人员在本地IDE中也会进行实时的静态扫描,并可以实时看到分析结果,这样就可以在代码被递交到代码仓库前就已经完成了预检测。 + +其实,我们并没有直接采用标准的代码静态扫描的规则库,而是删除了其中很多我们认为过于严格的规则,同时加入了一些我们认为比较重要的检测项,使得这个规则库更符合我们的业务场景。一般情况下,这些规则的修订是由测试架构师牵头,与开发主管和资深的开发人员一起协商决定的。 + +这里需要注意的是,我们并不要求静态扫描上报的所有错误都被修复后才能发布,只要求解决最关键的问题即可。而对于那些所谓的Code Smell问题,基于研发成本的考虑,我们并不会要求完全修复。 + +接着CI/CD流水线会触发代码动态测试,即单元测试。这里,我们不仅要求单元测试能够100%通过,并且会要求达到一定的代码覆盖率。在eBay,我们对不同模块的代码覆盖率要求也不一样,并没有一个硬性指标。其实,这也是出于研发成本的考虑。 + +通常来讲,对底层模块以及提供公共服务的中间件的代码覆盖率指标的要求,一般都会比较高。而我们对前端模块的覆盖率要求,就会低很多。 + +问题五:除了Sonar,你还用过哪些静态代码扫描工具,使用过程中遇到过哪些问题? + +在专栏的第26篇文章《深入浅出之静态测试方法》中,我和你详细分享了人工静态测试方法和自动静态测试方法,来帮你理解研发流程上是如何保证代码质量的。另外,我以Sonar为例,和你分享了如何搭建自己的自动静态代码扫描方案,并且应用到项目的日常开发工作中去。 + +而在这篇文章最后,我希望你分享的是除了Sonar你还用过哪些静态代码扫描工具,使用过程中遇到过哪些问题。 + +其实优秀的代码静态扫描工具远远不止Sonar,比如Fortify SCA和Checkmarx CxSuite等都是很优秀的静态扫描工具。至于使用过程中需要的问题,我觉得主要有这么三个: + +第一是误报率。过高的误报率会降低开发人员对测试工具的信任度,而且还会引入很多人为标注的工作量。 + +第二是规则库的完备性和实用性。很多时候你会发现,标准代码规则库中的一些规则设计不够合理,有点教条主义。比如,有些规则库会强行规定一个函数的代码行数不能超过200行,从代码的模块化和易维护性角度来讲,过长的函数实现体的确不利于代码健康。但是,也不能完全一刀切,毕竟有些函数就是实现起来比较复杂。所以,很多时候我们需要对标准规则库进行深层次地裁剪,以更好地适应企业的实际情况。 + +第三是自定义规则的难易程度。虽然很多静态代码工具都提供了规则编辑器,来方便你实现自己的规则,但是这些规则编辑器的使用方法和语法的学习成本比较高,对初学者不够友好。 + +问题六:在单元测试过程中,你都遇到过哪些问题,又是如何解决的呢? + +在专栏的第27篇文章《深入浅出之动态测试方法》中,我和你分享了人工动态测试方法和自动动态测试方法。因为自动动态方法并不能理解代码逻辑,所以仅仅被用于发现异常、崩溃和超时这类“有特征”的错误,而对于代码逻辑功能的测试,主要还是要依靠人工动态方法。 + +在这篇文章最后,我希望你分享的是,除了我在文中提到的几个单元测试的难点问题,你还遇到过哪些问题,又是如何解决的。 + +这里,我想再和你分享我曾在单元测试中遇到过的问题。 + +单元测试的难点中较为典型的就是对内部输入的控制。对于内部输入的控制有数十种不同的场景,这里我就举一个例子,意在抛砖引玉。 + +首先为了达到较高的测试代码覆盖率,如果代码中包括了if-else分支,那么我们的测试就需要分别执行到这两个分支。假设,现在有一个if-else分支是根据malloc这个内存分配函数的结果进行不同的处理,如果内存分配成功了,就执行A逻辑,如果执行失败了就执行B逻辑。 + +那么,在做单元测试的时候,通常情况下很容易覆盖内存分配成功的场景,但是想要实现“可控”的内存分配失败就比较困难了。因为malloc是个底层系统函数,根本无法对其控制。 + +为了解决这个问题,我们就可以采用桩函数的思想,引入一个malloc的桩函数,在这个桩函数的内部再去调用真正的系统malloc函数,如果需要模拟真正的malloc函数的失败,就在桩函数里面直接返回malloc函数失败的返回值,来达到模拟真正malloc函数失败场景的目的。这样就能在被测函数中通过可控的方式,来模拟系统底层函数的返回值了。 + +最后,感谢你能认真阅读第22~27这6篇文章的内容,并写下了你的想法和问题。期待你能继续关注我的专栏,继续留下你对文章内容的思考,我也在一直关注着你的留言、你的学习情况。 + +感谢你的支持,我们下一期答疑文章再见! + + + + \ No newline at end of file diff --git a/专栏/软件测试52讲/结束语不是结束,而是开始.md b/专栏/软件测试52讲/结束语不是结束,而是开始.md new file mode 100644 index 0000000..79274df --- /dev/null +++ b/专栏/软件测试52讲/结束语不是结束,而是开始.md @@ -0,0 +1,47 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 不是结束,而是开始 + 你好,我是茹炳晟。 + +不知不觉间,我们已经一起走过了将近5个月的时间。 + +在这5个月的时间里,我和你分享了软件测试的方方面面,小到一个“用户登录”功能测试用例的设计,大到从网站架构设计的视角去审视测试设计,相信我已经帮你建立起了软件测试工程领域的全局观。 + +同时,也正是你的支持与认可,在这5个月的时间里,一直激励着我,不断地总结自己从事软件测试工作这些年来的收获与成长,并形成文字,分享给你,以惠及更多的软件测试行业的同伴。 + +这里,我也想再和你分享下,我在专栏创作过程中的一些心路历程,希望你也能看到我在这5个月时间里的思考与成长。 + +首先,专栏写作的过程真的很辛苦。从今年4月初接受极客时间邀请,到今天专栏结束,这中间7个多月的时间,26万字的专栏写作,几乎占尽了我所有的个人时间,以至于家人误会我改行做了“作家”,天天晚上躲在书房里写“武林秘籍”。 + +其次,专栏定位也真的是我冥思苦想许久,才最终得以确定。即使这样,在专栏更新的过程中,我也一度因为“你”的留言,而倍感困惑。因为,8000多个订阅用户的知识储备和诉求各有不同。所以,你现在看到的每篇文章中的知识点、案例,我都进行了谨慎细致地考量和打磨。 + +这其中我考虑到了文章的难易程度,以求可以让处于各个成长阶段的“你”都能有所收获,还要尽量保证知识结构的完整性,以保证可以为你描绘出软件测试领域的全景技术视图。同时,每一个知识点,我都尽量避免去讲解具体某一款工具的使用,希望做到有点有面,能够给你呈现全方位的测试视角。 + +但是,不管这个过程如何的艰辛和痛苦,我写作专栏的决心却一直未变。因为,我希望,通过这个专栏我可以帮助到无数处于迷茫阶段的测试技术人员,帮助他们明确定位、认识不足、建立信心,并为他们指明方向。 + +虽然现在市场上已经有很多软件测试类的书籍了,但是大部分都是专门讲解具体测试工具的使用,而且对原理的讲述并没有那么透彻。另外,在我看来,市面上一直缺少一份从工程实践角度出发,尤其是从互联网的实际工程实践出发,来全面介绍软件测试技术的学习资料。为此,我希望这个专栏能够填补这一空缺。 + +现在专栏已经进入了尾声,于我而言是一个创作过程的结束,但于你而言这只是开始。 + +其实,这短短的61篇文章,更多的是帮你开阔视野,感受到软件测试庞大的知识体系,并了解到原来其中的每个领域(比如性能测试、安全测试、自动化测试等),都是一个很大的分支。 + +而我把你带进了软件测试世界的大门后,后面更多的探索与修炼就全要靠你自己来完成了。这就好比金庸先生笔下的武侠世界,专栏本身更像是一本武林秘籍,而想学得一身本领行走江湖,还得要靠你自己的修炼。 + +所以,接下来,我想再和你多啰嗦几句,用什么样的“姿势”去修炼,才能事半功倍。 + +第一,苦练基本功。我在专栏文章中,提到了很多测试工具,但是都没有对这个工具本身的使用做详细展开,这部分内容需要你自己去学习并在项目中实践。这么设计的很大一部分原因在于,对工具的学习我始终推荐直接参考官方文档。熟练掌握这些工具,是成为武林高手的敲门砖。这相当于是你要重点练习的基本招式,没有人可以代替你完成。 + +第二,行走江湖。我在专栏文章中,和你分享了很多来自于大公司的工程实践方法,你应该根据你所在公司或者项目的上下文,来综合考虑如何实践。这里你一定不要全盘照抄,一定需要根据实际情况进行取舍,甚至需要你做到融会贯通、综合应用这些实践。这是成为武林高手必定要经历的过程,讲究的是此时无招胜有招。 + +第三,自立门派。我还在专栏文章中,和你分享了很多测试相关的创新设计,比如测试数据服务、基于Docker的测试执行环境、页面对象的自动生成、API返回结果的自动比较等等,这些都是工程实践沉淀的精华内容。虽然很多技术实现并没有开源,但是这些设计思想和方法都是可以借鉴的,可以帮助你大幅度提升测试效率。这里,你要做的就是根据这些设计思路,然后结合你所在企业的技术栈,来落地实现这些方法。这就是打造你的测试核心竞争力,对应的就是修炼你的独门绝活,在武林中自立门户。 + +在专栏开始时,我曾在开篇词中和你说到“希望四个月后,你我都能遇见更好的自己”。通过这几个月的专栏创作,我是真的遇见了更好的自己,那么你呢? + +](http://lixbr66veiw63rtj.mikecrm.com/uR2pWiQ) + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/00开篇词|ToBeaHTTPHero.md b/专栏/透视HTTP协议/00开篇词|ToBeaHTTPHero.md new file mode 100644 index 0000000..5620138 --- /dev/null +++ b/专栏/透视HTTP协议/00开篇词|ToBeaHTTPHero.md @@ -0,0 +1,94 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 开篇词|To Be a HTTP Hero + 你好,我是罗剑锋(Chrono(加微信:642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。)),一名埋头于前线,辛勤“耕耘”了十余载的资深“码农”。 + +工作的这十多年来,我开发过智能 IC 卡,也倒腾过商用密码机;做过政务项目,也做过商务搜索;写过网游核心引擎,也写过 CDN 存储系统;在 Windows 上用 C/C++ 做客户端,在 AIX、Linux 上用 Java、PHP 写后台服务……现在则是专注于“魔改”Nginx,深度定制实现网络协议的分析与检测。 + +当极客时间的编辑联系我,要我写 HTTP 专栏的时候,我的第一反应是:“HTTP 协议好简单的,有这个必要吗?” + +你可能也会有同样的想法:“HTTP 不就是请求 / 响应、GET/POST、Header/Body 吗?网络上的资料一抓一大把,有什么问题搜一下就是了。” + +不瞒你说,我当时就是这么想的,在之前的工作中也是一直这么做的,而且一直“感觉良好”,觉得 HTTP 就是这个样子,没有什么特别的地方,没有什么值得讲的。 + +但在编辑的一再坚持下,我“勉为其难”接下了这个任务。然后做了一个小范围的“调查”,问一些周围的同事,各个领域的都有,比如产品、开发、运维、测试、前端、后端、手机端……想看看他们有什么意见。 + +出乎我的意料,他们无一例外都对这个“HTTP 专栏”有很强烈的需求,想好好“补补课”,系统地学习了解 HTTP,这其中甚至还包括有七、八年(甚至更多)工作经验的老手。 + +这不禁让我陷入了思考,为什么如此“简单”的协议却还有这么多的人想要学呢? + +我想,一个原因可能是 HTTP 协议“太常见”了。就像现实中的水和空气一样,如此重要却又如此普遍,普遍到我们几乎忽视了它的存在。真的很像那句俗语所说:“鱼总是最后看见水的”,但水对鱼的生存却又是至关重要。 + +我认真回忆了一下这些年的工作经历,这才发现 HTTP 只是表面上显得简单,而底层的运行机制、工作原理绝不简单,可以说是非常地复杂。只是我们平常总是“KPI 优先”,网上抓到一个解决方法用过就完事了,没有去深究里面的要点和细节。 + +下面的几个场景,都是我周围同事的实际感受,你是否也在工作中遇到过这样的困惑呢?你能把它们都解释清楚吗? + + +用 Nginx 搭建 Web 服务器,照着网上的文章配好了,但里面那么多的指令,什么 keepalive、rewrite、proxy_pass 都是怎么回事?为什么要这么配置? +用 Python 写爬虫,URI、URL“傻傻分不清”,有时里面还会加一些奇怪的字符,怎么处理才好? +都说 HTTP 缓存很有用,可以大幅度提升系统性能,可它是怎么做到的?又应该用在何时何地? +HTTP 和 HTTPS 是什么关系?还经常听说有 SSL/TLS/SNI/OCSP/ALPN……这么多稀奇古怪的缩写,头都大了,实在是搞不懂。 + + +其实这些问题也并不是什么新问题,把关键字粘贴进搜索栏,再点一下按钮,搜索引擎马上就能找出几十万个相关的页面。但看完第一页的前几个链接后,通常还是有种“懵懵懂懂”“似懂非懂”的感觉,觉得说的对,又不全对,和自己的思路总是不够“Match”。 + +不过大多数情况下你可能都没有时间细想,优先目标是把手头的工作“对付过去”。长此以来,你对 HTTP 的认识也可能仅限于这样的“知其然,而不知其所以然”,实际情况就是 HTTP 天天用,时时用,但想认真、系统地学习一下,梳理出自己的知识体系,经常会发现无从下手。 + +我把这种 HTTP 学习的现状归纳为三点:正式资料“少”、网上资料“杂”、权威资料“难”。 + +第一个,正式资料“少”。 + +上购书网站,搜个 Python、Java,搜个 MySQL、Node.js,能出一大堆。但搜 HTTP,实在是少得可怜,那么几本,一只手的手指头就可以数得过来,和语言类、数据库类、框架类图书真是形成了鲜明的对比。 + +现有的 HTTP 相关图书我都看过,怎么说呢,它们都有一个特点,“广撒网,捕小鱼”,都是知识点,可未免太“照本宣科”了,理论有余实践不足,看完了还是不知道怎么去用。 + +而且这些书的“岁数”都很大,依据的都是 20 年前的 RFC2616,很多内容都不合时宜,而新标准 7230 已经更新了很多关键的细节。 + +第二个,网上资料“杂”。 + +正式的图书少,而且过时,那就求助于网络社区吧。现在的博客、论坛、搜索引擎非常发达,网上有很多 HTTP 协议相关的文章,也都是网友的实践经验分享,“干货”很多,很能解决实际问题。 + +但网上文章的特点是细小、零碎,通常只“钉”在一个很小的知识点上,而且由于帖子长度的限制,无法深入展开论述,很多都是“浅尝辄止”,通常都止步在“How”层次,很少能说到“Why”,能说透的更是寥寥无几。 + +网文还有一个难以避免的“毛病”,就是“良莠不齐”。同一个主题可能会有好几种不同的说法,有的还会互相矛盾、以讹传讹。这种情况是最麻烦的,你必须花大力气去鉴别真假,不小心就会被“带到沟里”。 + +可想而知,这种“东一榔头西一棒子”的学习方式,用“碎片”拼凑出来的 HTTP 知识体系是非常不完善的,会有各种漏洞,遇到问题时基本派不上用场,还得再去找其他的“碎片”。 + +第三个,权威资料“难”。 + +图书少,网文杂,我们还有一个终极的学习资料,那就是 RFC 文档。 + +RFC 是互联网工程组(IETF)发布的官方文件,是对 HTTP 最权威的定义和解释。但它也是最难懂的,全英文看着费劲,理解起来更是难上加难,文档之间还会互相关联引用,“劝退率”极高。 + +这三个问题就像是“三座大山”,阻碍了像你这样的很多有心人去学习、了解 HTTP 协议。 + +那么,怎么才能更好地学习 HTTP 呢? + +我为这个专栏定了一个基调:“要有广度,但更要有深度”。目标是成为含金量最高的 HTTP 学习资料,新手可以由浅入深、系统学习,老手可以温故知新、查缺补漏,让你花最少的时间,用最少的精力,掌握最多、最全面、最系统的知识。 + +由于 HTTP 应用得非常广泛,几乎涉及到所有的领域,所以我会在广度上从 HTTP 尽量向外扩展,不只讲协议本身,与它相关的 TCP/IP、DNS、SSL/TLS、Web Server 等都会讲到,而且会把它们打通串联在一起,形成知识链,让你知道它们之间是怎么联系、怎么运行的。 + +专栏文章的深度上我也是下足了功夫,全部基于最新的 RFC 标准文档,再结合我自己多年的实践体会,力求讲清讲透,能让你看了以后有豁然开朗的感觉。 + +比如分析 HTTPS,我会用 Wireshark 从建立 TCP 连接时就开始抓包,从二进制最底层来分析里面的 Record、Cipher Suite、Extension,讲 ECDHE、AES、SHA384,再画出详细的流程图,做到“一览无余”。 + +陆游有诗:“纸上得来终觉浅,绝知此事要躬行”。学习网络协议最重要的就是实践,在专栏里我还会教你用 Nginx 搭建一个“麻雀虽小,五脏俱全”的实验环境,让你与 HTTP 零距离接触。 + +它有一个最大的优点:自身就是一个完整的网络环境,即使不联网也能够在里面收发 HTTP 消息。 + +我还精心设计了配套的测试用例,最小化应用场景,排除干扰因素,你可以在里面任意测试 HTTP 的各种特性,再配合 Wireshark 抓包,就能够理论结合实践,更好地掌握 HTTP 的知识。 + +每一讲的末尾,我也会留几个思考题,你可以把它当作是求职时的面试官问题,尽量认真思考后再回答,这样能够把专栏的学习由“被动地听”,转变为“主动地学”,实现“学以致用”。 + +当然了,你和我的“兴趣点”不可能完全一样,我在讲课时也难免“顾此失彼”“挂一漏万”,希望你积极留言,我会视情况做些调整,或者用答疑的形式补充没讲到的内容。 + +今年是万维网和 HTTP 诞生 30 周年,也是 HTTP/1.1 诞生 20 周年,套用莎翁《哈姆雷特》里的名句,让我们在接下来的三个月里一起努力。 + +“To Be a HTTP Hero!” + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/01时势与英雄:HTTP的前世今生.md b/专栏/透视HTTP协议/01时势与英雄:HTTP的前世今生.md new file mode 100644 index 0000000..891345a --- /dev/null +++ b/专栏/透视HTTP协议/01时势与英雄:HTTP的前世今生.md @@ -0,0 +1,167 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 时势与英雄:HTTP的前世今生 + HTTP 协议在我们的生活中随处可见,打开手机或者电脑,只要你上网,不论是用 iPhone、Android、Windows 还是 Mac,不论是用浏览器还是 App,不论是看新闻、短视频还是听音乐、玩游戏,后面总会有 HTTP 在默默为你服务。 + +据 NetCraft 公司统计,目前全球至少有 16 亿个网站、2 亿多个独立域名,而这个庞大网络世界的底层运转机制就是 HTTP。 + +那么,在享受如此便捷舒适的网络生活时,你有没有想过,HTTP 协议是怎么来的?它最开始是什么样子的?又是如何一步一步发展到今天,几乎“统治”了整个互联网世界的呢? + +常言道:“时势造英雄,英雄亦造时势”。 + +今天我就和你来聊一聊 HTTP 的发展历程,看看它的成长轨迹,看看历史上有哪些事件推动了它的前进,它又促进了哪些技术的产生,一起来见证“英雄之旅”。 + +在这个过程中,你也能够顺便了解一下 HTTP 的“历史局限性”,明白 HTTP 为什么会设计成现在这个样子。 + +史前时期 + +20 世纪 60 年代,美国国防部高等研究计划署(ARPA)建立了 ARPA 网,它有四个分布在各地的节点,被认为是如今互联网的“始祖”。 + +然后在 70 年代,基于对 ARPA 网的实践和思考,研究人员发明出了著名的 TCP/IP 协议。由于具有良好的分层结构和稳定的性能,TCP/IP 协议迅速战胜其他竞争对手流行起来,并在 80 年代中期进入了 UNIX 系统内核,促使更多的计算机接入了互联网。 + +创世纪 + + + +蒂姆·伯纳斯 - 李 + +1989 年,任职于欧洲核子研究中心(CERN)的蒂姆·伯纳斯 - 李(Tim Berners-Lee)发表了一篇论文,提出了在互联网上构建超链接文档系统的构想。这篇论文中他确立了三项关键技术。 + + +URI:即统一资源标识符,作为互联网上资源的唯一身份; +HTML:即超文本标记语言,描述超文本文档; +HTTP:即超文本传输协议,用来传输超文本。 + + +这三项技术在如今的我们看来已经是稀松平常,但在当时却是了不得的大发明。基于它们,就可以把超文本系统完美地运行在互联网上,让各地的人们能够自由地共享信息,蒂姆把这个系统称为“万维网”(World Wide Web),也就是我们现在所熟知的 Web。 + +所以在这一年,我们的英雄“HTTP”诞生了,从此开始了它伟大的征途。 + +HTTP/0.9 + +20 世纪 90 年代初期的互联网世界非常简陋,计算机处理能力低,存储容量小,网速很慢,还是一片“信息荒漠”。网络上绝大多数的资源都是纯文本,很多通信协议也都使用纯文本,所以 HTTP 的设计也不可避免地受到了时代的限制。 + +这一时期的 HTTP 被定义为 0.9 版,结构比较简单,为了便于服务器和客户端处理,它也采用了纯文本格式。蒂姆·伯纳斯 - 李最初设想的系统里的文档都是只读的,所以只允许用“GET”动作从服务器上获取 HTML 文档,并且在响应请求之后立即关闭连接,功能非常有限。 + +HTTP/0.9 虽然很简单,但它作为一个“原型”,充分验证了 Web 服务的可行性,而“简单”也正是它的优点,蕴含了进化和扩展的可能性,因为: + +“把简单的系统变复杂”,要比“把复杂的系统变简单”容易得多。 + +HTTP/1.0 + +1993 年,NCSA(美国国家超级计算应用中心)开发出了 Mosaic,是第一个可以图文混排的浏览器,随后又在 1995 年开发出了服务器软件 Apache,简化了 HTTP 服务器的搭建工作。 + +同一时期,计算机多媒体技术也有了新的发展:1992 年发明了 JPEG 图像格式,1995 年发明了 MP3 音乐格式。 + +这些新软件新技术一经推出立刻就吸引了广大网民的热情,更的多的人开始使用互联网,研究 HTTP 并提出改进意见,甚至实验性地往协议里添加各种特性,从用户需求的角度促进了 HTTP 的发展。 + +于是在这些已有实践的基础上,经过一系列的草案,HTTP/1.0 版本在 1996 年正式发布。它在多方面增强了 0.9 版,形式上已经和我们现在的 HTTP 差别不大了,例如: + + +增加了 HEAD、POST 等新方法; +增加了响应状态码,标记可能的错误原因; +引入了协议版本号概念; +引入了 HTTP Header(头部)的概念,让 HTTP 处理请求和响应更加灵活; +传输的数据不再仅限于文本。 + + +但 HTTP/1.0 并不是一个“标准”,只是记录已有实践和模式的一份参考文档,不具有实际的约束力,相当于一个“备忘录”。 + +所以 HTTP/1.0 的发布对于当时正在蓬勃发展的互联网来说并没有太大的实际意义,各方势力仍然按照自己的意图继续在市场上奋力拼杀。 + +HTTP/1.1 + +1995 年,网景的 Netscape Navigator 和微软的 Internet Explorer 开始了著名的“浏览器大战”,都希望在互联网上占据主导地位。 + + + +这场战争的结果你一定早就知道了,最终微软的 IE 取得了决定性的胜利,而网景则“败走麦城”(但后来却凭借 Mozilla Firefox 又扳回一局)。 + +“浏览器大战”的是非成败我们放在一边暂且不管,不可否认的是,它再一次极大地推动了 Web 的发展,HTTP/1.0 也在这个过程中经受了实践检验。于是在“浏览器大战”结束之后的 1999 年,HTTP/1.1 发布了 RFC 文档,编号为 2616,正式确立了延续十余年的传奇。 + +从版本号我们就可以看到,HTTP/1.1 是对 HTTP/1.0 的小幅度修正。但一个重要的区别是:它是一个“正式的标准”,而不是一份可有可无的“参考文档”。这意味着今后互联网上所有的浏览器、服务器、网关、代理等等,只要用到 HTTP 协议,就必须严格遵守这个标准,相当于是互联网世界的一个“立法”。 + +不过,说 HTTP/1.1 是“小幅度修正”也不太确切,它还是有很多实质性进步的。毕竟经过了多年的实战检验,比起 0.9⁄1.0 少了“学术气”,更加“接地气”,同时表述也更加严谨。HTTP/1.1 主要的变更点有: + + +增加了 PUT、DELETE 等新的方法; +增加了缓存管理和控制; +明确了连接管理,允许持久连接; +允许响应数据分块(chunked),利于传输大文件; +强制要求 Host 头,让互联网主机托管成为可能。 + + +HTTP/1.1 的推出可谓是“众望所归”,互联网在它的“保驾护航”下迈开了大步,由此走上了“康庄大道”,开启了后续的“Web 1.0”“Web 2.0”时代。现在许多的知名网站都是在这个时间点左右创立的,例如 Google、新浪、搜狐、网易、腾讯等。 + +不过由于 HTTP/1.1 太过庞大和复杂,所以在 2014 年又做了一次修订,原来的一个大文档被拆分成了六份较小的文档,编号为 7230-7235,优化了一些细节,但此外没有任何实质性的改动。 + +HTTP/2 + +HTTP/1.1 发布之后,整个互联网世界呈现出了爆发式的增长,度过了十多年的“快乐时光”,更涌现出了 Facebook、Twitter、淘宝、京东等互联网新贵。 + +这期间也出现了一些对 HTTP 不满的意见,主要就是连接慢,无法跟上迅猛发展的互联网,但 HTTP/1.1 标准一直“岿然不动”,无奈之下人们只好发明各式各样的“小花招”来缓解这些问题,比如以前常见的切图、JS 合并等网页优化手段。 + +终于有一天,搜索巨头 Google 忍不住了,决定“揭竿而起”,就像马云说的“如果银行不改变,我们就改变银行”。那么,它是怎么“造反”的呢? + +Google 首先开发了自己的浏览器 Chrome,然后推出了新的 SPDY 协议,并在 Chrome 里应用于自家的服务器,如同十多年前的网景与微软一样,从实际的用户方来“倒逼”HTTP 协议的变革,这也开启了第二次的“浏览器大战”。 + +历史再次重演,不过这次的胜利者是 Google,Chrome 目前的全球的占有率超过了 60%。“挟用户以号令天下”,Google 借此顺势把 SPDY 推上了标准的宝座,互联网标准化组织以 SPDY 为基础开始制定新版本的 HTTP 协议,最终在 2015 年发布了 HTTP/2,RFC 编号 7540。 + +HTTP/2 的制定充分考虑了现今互联网的现状:宽带、移动、不安全,在高度兼容 HTTP/1.1 的同时在性能改善方面做了很大努力,主要的特点有: + + +二进制协议,不再是纯文本; +可发起多个请求,废弃了 1.1 里的管道; +使用专用算法压缩头部,减少数据传输量; +允许服务器主动向客户端推送数据; +增强了安全性,“事实上”要求加密通信。 + + +虽然 HTTP/2 到今天已经四岁,也衍生出了 gRPC 等新协议,但由于 HTTP/1.1 实在是太过经典和强势,目前它的普及率还比较低,大多数网站使用的仍然还是 20 年前的 HTTP/1.1。 + +HTTP/3 + +看到这里,你可能会问了:“HTTP/2 这么好,是不是就已经完美了呢?” + +答案是否定的,这一次还是 Google,而且它要“革自己的命”。 + +在 HTTP/2 还处于草案之时,Google 又发明了一个新的协议,叫做 QUIC,而且还是相同的“套路”,继续在 Chrome 和自家服务器里试验着“玩”,依托它的庞大用户量和数据量,持续地推动 QUIC 协议成为互联网上的“既成事实”。 + +“功夫不负有心人”,当然也是因为 QUIC 确实自身素质过硬。 + +在去年,也就是 2018 年,互联网标准化组织 IETF 提议将“HTTP over QUIC”更名为“HTTP/3”并获得批准,HTTP/3 正式进入了标准化制订阶段,也许两三年后就会正式发布,到时候我们很可能会跳过 HTTP/2 直接进入 HTTP/3。 + +小结 + +今天我和你一起跨越了三十年的历史长河,回顾了 HTTP 协议的整个发展过程,在这里简单小结一下今天的内容: + + +HTTP 协议始于三十年前蒂姆·伯纳斯 - 李的一篇论文; +HTTP/0.9 是个简单的文本协议,只能获取文本资源; +HTTP/1.0 确立了大部分现在使用的技术,但它不是正式标准; +HTTP/1.1 是目前互联网上使用最广泛的协议,功能也非常完善; +HTTP/2 基于 Google 的 SPDY 协议,注重性能改善,但还未普及; +HTTP/3 基于 Google 的 QUIC 协议,是将来的发展方向。 + + +希望通过今天的介绍,你能够对 HTTP 有一个初步但清晰的印象,知道了“来龙”才能更好地知道“去脉”。 + +课下作业 + + +你认为推动 HTTP 发展的原动力是什么? +你是怎么理解 HTTP(超文本传输协议)的? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。暂时回答不出来也不要紧,你可以带着这些问题在后续的课程里寻找答案。 + +如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/02HTTP是什么?HTTP又不是什么?.md b/专栏/透视HTTP协议/02HTTP是什么?HTTP又不是什么?.md new file mode 100644 index 0000000..6504e26 --- /dev/null +++ b/专栏/透视HTTP协议/02HTTP是什么?HTTP又不是什么?.md @@ -0,0 +1,153 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 HTTP是什么?HTTP又不是什么? + 首先我来问出这个问题:“你觉得 HTTP 是什么呢?” + +你可能会不假思索、脱口而出:“HTTP 就是超文本传输协议,也就是HyperText Transfer Protocol。” + +回答非常正确!我必须由衷地恭喜你:能给出这个答案,就表明你具有至少 50%HTTP 相关的知识储备,应该算得上是“半个专家”了。 + +不过让我们换个对话场景,假设不是我,而是由一位面试官问出刚才的问题呢? + + + +显然,这个答案有点过于简单了,不能让他满意,他肯定会再追问你一些问题: + + +你是怎么理解 HTTP 字面上的“超文本”和“传输协议”的? +能否谈一下你对 HTTP 的认识?越多越好。 +HTTP 有什么特点?有什么优点和缺点? +HTTP 下层都有哪些协议?是如何工作的? +…… + + +几乎所有面试时问到的 HTTP 相关问题,都可以从这个最简单的“HTTP 是什么?”引出来。 + +所以,今天的话题就从这里开始,深度地解答一下“HTTP 是什么?”,以及延伸出来的第二个问题“HTTP 不是什么?” + +HTTP 是什么 + +咱们中国有个成语“人如其名”,意思是一个人的性格和特点是与他的名字相符的。 + +先看一下 HTTP 的名字:“超文本传输协议”,它可以拆成三个部分,分别是:“超文本”“传输”和“协议”。我们从后往前来逐个解析,理解了这三个词,我们也就明白了什么是 HTTP。 + + + +首先,HTTP 是一个协议。不过,协议又是什么呢? + +其实“协议”并不仅限于计算机世界,现实生活中也随处可见。例如,你在刚毕业时会签一个“三方协议”,找房子时会签一个“租房协议”,公司入职时还可能会签一个“保密协议”,工作中使用的各种软件也都带着各自的“许可协议”。 + +刚才说的这几个都是“协议”,本质上与 HTTP 是相同的,那么“协议”有什么特点呢? + +第一点,协议必须要有两个或多个参与者,也就是“协”。 + +如果只有你一个人,那你自然可以想干什么就干什么,想怎么玩就怎么玩,不会干涉其他人,其他人也不会干涉你,也就不需要所谓的“协议”。但是,一旦有了两个以上的参与者出现,为了保证最基本的顺畅交流,协议就自然而然地出现了。 + +例如,为了保证你顺利就业,“三方协议”里的参与者有三个:你、公司和学校;为了保证你顺利入住,“租房协议”里的参与者有两个:你和房东。 + +第二点,协议是对参与者的一种行为约定和规范,也就是“议”。 + +协议意味着有多个参与者为了达成某个共同的目的而站在了一起,除了要无疑义地沟通交流之外,还必须明确地规定各方的“责、权、利”,约定该做什么不该做什么,先做什么后做什么,做错了怎么办,有没有补救措施等等。例如,“租房协议”里就约定了,租期多少个月,每月租金多少,押金是多少,水电费谁来付,违约应如何处理等等。 + +好,到这里,你应该能够明白 HTTP 的第一层含义了。 + +HTTP 是一个用在计算机世界里的协议。它使用计算机能够理解的语言确立了一种计算机之间交流通信的规范,以及相关的各种控制和错误处理方式。 + +接下来我们看 HTTP 字面里的第二部分:“传输”。 + +计算机和网络世界里有数不清的各种角色:CPU、内存、总线、磁盘、操作系统、浏览器、网关、服务器……这些角色之间相互通信也必然会有各式各样、五花八门的协议,用处也各不相同,例如广播协议、寻址协议、路由协议、隧道协议、选举协议等等。 + +HTTP 是一个“传输协议”,所谓的“传输”(Transfer)其实很好理解,就是把一堆东西从 A 点搬到 B 点,或者从 B 点搬到 A 点,即“A<===>B”。 + +别小看了这个简单的动作,它也至少包含了两项重要的信息。 + +第一点,HTTP 协议是一个“双向协议”。 + +也就是说,有两个最基本的参与者 A 和 B,从 A 开始到 B 结束,数据在 A 和 B 之间双向而不是单向流动。通常我们把先发起传输动作的 A 叫做请求方,把后接到传输的 B 叫做应答方或者响应方。拿我们最常见的上网冲浪来举例子,浏览器就是请求方 A,网易、新浪这些网站就是应答方 B。双方约定用 HTTP 协议来通信,于是浏览器把一些数据发送给网站,网站再把一些数据发回给浏览器,最后展现在屏幕上,你就可以看到各种有意思的新闻、视频了。 + +第二点,数据虽然是在 A 和 B 之间传输,但并没有限制只有 A 和 B 这两个角色,允许中间有“中转”或者“接力”。 + +这样,传输方式就从“A<===>B”,变成了“A<=>X<=>Y<=>Z<=>B”,A 到 B 的传输过程中可以存在任意多个“中间人”,而这些中间人也都遵从 HTTP 协议,只要不打扰基本的数据传输,就可以添加任意的额外功能,例如安全认证、数据压缩、编码转换等等,优化整个传输过程。 + +说到这里,你差不多应该能够明白 HTTP 的第二层含义了。 + +HTTP 是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。 + +讲完了“协议”和“传输”,现在,我们终于到 HTTP 字面里的第三部分:“超文本”。 + +既然 HTTP 是一个“传输协议”,那么它传输的“超文本”到底是什么呢?我还是用两点来进一步解释。 + +所谓“文本”(Text),就表示 HTTP 传输的不是 TCP/UDP 这些底层协议里被切分的杂乱无章的二进制包(datagram),而是完整的、有意义的数据,可以被浏览器、服务器这样的上层应用程序处理。 + +在互联网早期,“文本”只是简单的字符文字,但发展到现在,“文本”的涵义已经被大大地扩展了,图片、音频、视频、甚至是压缩包,在 HTTP 眼里都可以算做是“文本”。 + +所谓“超文本”,就是“超越了普通文本的文本”,它是文字、图片、音频和视频等的混合体,最关键的是含有“超链接”,能够从一个“超文本”跳跃到另一个“超文本”,形成复杂的非线性、网状的结构关系。 + +对于“超文本”,我们最熟悉的就应该是 HTML 了,它本身只是纯文字文件,但内部用很多标签定义了对图片、音频、视频等的链接,再经过浏览器的解释,呈现在我们面前的就是一个含有多种视听信息的页面。 + +OK,经过了对 HTTP 里这三个名词的详细解释,下次当你再面对面试官时,就可以给出比“超文本传输协议”这七个字更准确更有技术含量的答案:“HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范”。 + +HTTP 不是什么 + +现在你对“HTTP 是什么?”应该有了比较清晰的认识,紧接着的问题就是“HTTP 不是什么?”,等价的问题是“HTTP 不能干什么?”。想想看,你能回答出来吗? + +因为 HTTP 是一个协议,是一种计算机间通信的规范,所以它不存在“单独的实体”。它不是浏览器、手机 APP 那样的应用程序,也不是 Windows、Linux 那样的操作系统,更不是 Apache、Nginx、Tomcat 那样的 Web 服务器。 + +但 HTTP 又与应用程序、操作系统、Web 服务器密切相关,在它们之间的通信过程中存在,而且是一种“动态的存在”,是发生在网络连接、传输超文本数据时的一个“动态过程”。 + +HTTP 不是互联网。 + +互联网(Internet)是遍布于全球的许多网络互相连接而形成的一个巨大的国际网络,在它上面存放着各式各样的资源,也对应着各式各样的协议,例如超文本资源使用 HTTP,普通文件使用 FTP,电子邮件使用 SMTP 和 POP3 等。 + +但毫无疑问,HTTP 是构建互联网的一块重要拼图,而且是占比最大的那一块。 + +HTTP 不是编程语言。 + +编程语言是人与计算机沟通交流所使用的语言,而 HTTP 是计算机与计算机沟通交流的语言,我们无法使用 HTTP 来编程,但可以反过来,用编程语言去实现 HTTP,告诉计算机如何用 HTTP 来与外界通信。 + +很多流行的编程语言都支持编写 HTTP 相关的服务或应用,例如使用 Java 在 Tomcat 里编写 Web 服务,使用 PHP 在后端实现页面模板渲染,使用 JavaScript 在前端实现动态页面更新,你是否也会其中的一两种呢? + +HTTP 不是 HTML,这个可能要特别强调一下,千万不要把 HTTP 与 HTML 混为一谈,虽然这两者经常是同时出现。 + +HTML 是超文本的载体,是一种标记语言,使用各种标签描述文字、图片、超链接等资源,并且可以嵌入 CSS、JavaScript 等技术实现复杂的动态效果。单论次数,在互联网上 HTTP 传输最多的可能就是 HTML,但要是论数据量,HTML 可能要往后排了,图片、音频、视频这些类型的资源显然更大。 + +HTTP 不是一个孤立的协议。 + +俗话说“一个好汉三个帮”,HTTP 也是如此。 + +在互联网世界里,HTTP 通常跑在 TCP/IP 协议栈之上,依靠 IP 协议实现寻址和路由、TCP 协议实现可靠数据传输、DNS 协议实现域名查找、SSL/TLS 协议实现安全通信。此外,还有一些协议依赖于 HTTP,例如 WebSocket、HTTPDNS 等。这些协议相互交织,构成了一个协议网,而 HTTP 则处于中心地位。 + +小结 + + +HTTP 是一个用在计算机世界里的协议,它确立了一种计算机之间交流通信的规范,以及相关的各种控制和错误处理方式。 +HTTP 专门用来在两点之间传输数据,不能用于广播、寻址或路由。 +HTTP 传输的是文字、图片、音频、视频等超文本数据。 +HTTP 是构建互联网的重要基础技术,它没有实体,依赖许多其他的技术来实现,但同时许多技术也都依赖于它。 + + +把这些综合起来,使用递归缩写方式(模仿 PHP),我们可以把 HTTP 定义为“与 HTTP 协议相关的所有应用层技术的总和”。 + +这里我画了一个思维导图,也可以算是这个专栏系列文章的“知识地图”。 + + + +你可以对照这张图,看一下哪些部分是自己熟悉的,哪些部分是陌生的,又有哪些部分是想要进一步了解的,下一讲我会详细讲解这张图。 + +课下作业 + + +有一种流行的说法:“HTTP 是用于从互联网服务器传输超文本到本地浏览器的协议”,你认为这种说法对吗?对在哪里,又错在哪里? +你能再说出几个“HTTP 不是什么”吗? + + +欢迎你通过留言分享答案,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/03HTTP世界全览(上):与HTTP相关的各种概念.md b/专栏/透视HTTP协议/03HTTP世界全览(上):与HTTP相关的各种概念.md new file mode 100644 index 0000000..a089fd8 --- /dev/null +++ b/专栏/透视HTTP协议/03HTTP世界全览(上):与HTTP相关的各种概念.md @@ -0,0 +1,156 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 HTTP世界全览(上):与HTTP相关的各种概念 + 在上一讲的末尾,我画了一张图,里面是与 HTTP 关联的各种技术和知识点,也可以说是这个专栏的总索引,不知道你有没有认真看过呢? + +那张图左边的部分是与 HTTP 有关系的各种协议,比较偏向于理论;而右边的部分是与 HTTP 有关系的各种应用技术,偏向于实际应用。 + +我希望借助这张图帮你澄清与 HTTP 相关的各种概念和角色,让你在实际工作中清楚它们在链路中的位置和作用,知道发起一个 HTTP 请求会有哪些角色参与,会如何影响请求的处理,做到“手中有粮,心中不慌”。 + +因为那张图比较大,所以我会把左右两部分拆开来分别讲,今天先讲右边的部分,也就是与 HTTP 相关的各种应用,着重介绍互联网、浏览器、Web 服务器等常见且重要的概念。 + + + +为了方便你查看,我又把这部分重新画了一下,比那张大图小了一些,更容易地阅读,你可以点击查看。 + +暖场词就到这里,让我们正式开始吧。 + +网络世界 + +你一定已经习惯了现在的网络生活,甚至可能会下意识地认为网络世界就应该是这个样子的:“一张平坦而且一望无际的巨大网络,每一台电脑就是网络上的一个节点,均匀地点缀在这张网上”。 + +这样的理解既对,又不对。从抽象的、虚拟的层面来看,网络世界确实是这样的,我们可以从一个节点毫无障碍地访问到另一个节点。 + +但现实世界的网络却远比这个抽象的模型要复杂得多。实际的互联网是由许许多多个规模略小的网络连接而成的,这些“小网络”可能是只有几百台电脑的局域网,可能是有几万、几十万台电脑的广域网,可能是用电缆、光纤构成的固定网络,也可能是用基站、热点构成的移动网络…… + +互联网世界更像是由数不清的大小岛屿组成的“千岛之国”。 + +互联网的正式名称是 Internet,里面存储着无穷无尽的信息资源,我们通常所说的“上网”实际上访问的只是互联网的一个子集“万维网”(World Wide Web),它基于 HTTP 协议,传输 HTML 等超文本资源,能力也就被限制在 HTTP 协议之内。 + +互联网上还有许多万维网之外的资源,例如常用的电子邮件、BT 和 Magnet 点对点下载、FTP 文件下载、SSH 安全登录、各种即时通信服务等等,它们需要用各自的专有协议来访问。 + +不过由于 HTTP 协议非常灵活、易于扩展,而且“超文本”的表述能力很强,所以很多其他原本不属于 HTTP 的资源也可以“包装”成 HTTP 来访问,这就是我们为什么能够总看到各种“网页应用”——例如“微信网页版”“邮箱网页版”——的原因。 + +综合起来看,现在的互联网 90% 以上的部分都被万维网,也就是 HTTP 所覆盖,所以把互联网约等于万维网或 HTTP 应该也不算大错。 + +浏览器 + +上网就要用到浏览器,常见的浏览器有 Google 的 Chrome、Mozilla 的 Firefox、Apple 的 Safari、Microsoft 的 IE 和 Edge,还有小众的 Opera 以及国内的各种“换壳”的“极速”“安全”浏览器。 + + + +那么你想过没有,所谓的“浏览器”到底是个什么东西呢? + +浏览器的正式名字叫“Web Browser”,顾名思义,就是检索、查看互联网上网页资源的应用程序,名字里的 Web,实际上指的就是“World Wide Web”,也就是万维网。 + +浏览器本质上是一个 HTTP 协议中的请求方,使用 HTTP 协议获取网络上的各种资源。当然,为了让我们更好地检索查看网页,它还集成了很多额外的功能。 + +例如,HTML 排版引擎用来展示页面,JavaScript 引擎用来实现动态化效果,甚至还有开发者工具用来调试网页,以及五花八门的各种插件和扩展。 + +在 HTTP 协议里,浏览器的角色被称为“User Agent”即“用户代理”,意思是作为访问者的“代理”来发起 HTTP 请求。不过在不引起混淆的情况下,我们通常都简单地称之为“客户端”。 + +Web 服务器 + +刚才说的浏览器是 HTTP 里的请求方,那么在协议另一端的应答方(响应方)又是什么呢? + +这个你一定也很熟悉,答案就是服务器,Web Server。 + +Web 服务器是一个很大也很重要的概念,它是 HTTP 协议里响应请求的主体,通常也把控着绝大多数的网络资源,在网络世界里处于强势地位。 + +当我们谈到“Web 服务器”时有两个层面的含义:硬件和软件。 + +硬件含义就是物理形式或“云”形式的机器,在大多数情况下它可能不是一台服务器,而是利用反向代理、负载均衡等技术组成的庞大集群。但从外界看来,它仍然表现为一台机器,但这个形象是“虚拟的”。 + +软件含义的 Web 服务器可能我们更为关心,它就是提供 Web 服务的应用程序,通常会运行在硬件含义的服务器上。它利用强大的硬件能力响应海量的客户端 HTTP 请求,处理磁盘上的网页、图片等静态文件,或者把请求转发给后面的 Tomcat、Node.js 等业务应用,返回动态的信息。 + +比起层出不穷的各种 Web 浏览器,Web 服务器就要少很多了,一只手的手指头就可以数得过来。 + +Apache 是老牌的服务器,到今天已经快 25 年了,功能相当完善,相关的资料很多,学习门槛低,是许多创业者建站的入门产品。 + +Nginx 是 Web 服务器里的后起之秀,特点是高性能、高稳定,且易于扩展。自 2004 年推出后就不断蚕食 Apache 的市场份额,在高流量的网站里更是不二之选。 + +此外,还有 Windows 上的 IIS、Java 的 Jetty/Tomcat 等,因为性能不是很高,所以在互联网上应用得较少。 + +CDN + +浏览器和服务器是 HTTP 协议的两个端点,那么,在这两者之间还有别的什么东西吗? + +当然有了。浏览器通常不会直接连到服务器,中间会经过“重重关卡”,其中的一个重要角色就叫做 CDN。 + +CDN,全称是“Content Delivery Network”,翻译过来就是“内容分发网络”。它应用了 HTTP 协议里的缓存和代理技术,代替源站响应客户端的请求。 + +CDN 有什么好处呢? + +简单来说,它可以缓存源站的数据,让浏览器的请求不用“千里迢迢”地到达源站服务器,直接在“半路”就可以获取响应。如果 CDN 的调度算法很优秀,更可以找到离用户最近的节点,大幅度缩短响应时间。 + +打个比方,就好像唐僧西天取经,刚出长安城,就看到阿难与迦叶把佛祖的真经递过来了,是不是很省事? + +CDN 也是现在互联网中的一项重要基础设施,除了基本的网络加速外,还提供负载均衡、安全防护、边缘计算、跨运营商网络等功能,能够成倍地“放大”源站服务器的服务能力,很多云服务商都把 CDN 作为产品的一部分,我也会在后面用一讲的篇幅来专门讲解 CDN。 + +爬虫 + +前面说到过浏览器,它是一种用户代理,代替我们访问互联网。 + +但 HTTP 协议并没有规定用户代理后面必须是“真正的人类”,它也完全可以是“机器人”,这些“机器人”的正式名称就叫做“爬虫”(Crawler),实际上是一种可以自动访问 Web 资源的应用程序。 + +“爬虫”这个名字非常形象,它们就像是一只只不知疲倦的、辛勤的蚂蚁,在无边无际的网络上爬来爬去,不停地在网站间奔走,搜集抓取各种信息。 + +据估计,互联网上至少有 50% 的流量都是由爬虫产生的,某些特定领域的比例还会更高,也就是说,如果你的网站今天的访问量是十万,那么里面至少有五六万是爬虫机器人,而不是真实的用户。 + +爬虫是怎么来的呢? + +绝大多数是由各大搜索引擎“放”出来的,抓取网页存入庞大的数据库,再建立关键字索引,这样我们才能够在搜索引擎中快速地搜索到互联网角落里的页面。 + +爬虫也有不好的一面,它会过度消耗网络资源,占用服务器和带宽,影响网站对真实数据的分析,甚至导致敏感信息泄漏。所以,又出现了“反爬虫”技术,通过各种手段来限制爬虫。其中一项就是“君子协定”robots.txt,约定哪些该爬,哪些不该爬。 + +无论是“爬虫”还是“反爬虫”,用到的基本技术都是两个,一个是 HTTP,另一个就是 HTML。 + +HTML/WebService/WAF + +到现在我已经说完了图中右边的五大部分,而左边的 HTML、WebService、WAF 等由于与 HTTP 技术上实质关联不太大,所以就简略地介绍一下,不再过多展开。 + +HTML是 HTTP 协议传输的主要内容之一,它描述了超文本页面,用各种“标签”定义文字、图片等资源和排版布局,最终由浏览器“渲染”出可视化页面。 + +HTML 目前有两个主要的标准,HTML4 和 HTML5。广义上的 HTML 通常是指 HTML、JavaScript、CSS 等前端技术的组合,能够实现比传统静态页面更丰富的动态页面。 + +接下来是Web Service,它的名字与 Web Server 很像,但却是一个完全不同的东西。 + +Web Service 是一种由 W3C 定义的应用服务开发规范,使用 client-server 主从架构,通常使用 WSDL 定义服务接口,使用 HTTP 协议传输 XML 或 SOAP 消息,也就是说,它是一个基于 Web(HTTP)的服务架构技术,既可以运行在内网,也可以在适当保护后运行在外网。 + +因为采用了 HTTP 协议传输数据,所以在 Web Service 架构里服务器和客户端可以采用不同的操作系统或编程语言开发。例如服务器端用 Linux+Java,客户端用 Windows+C#,具有跨平台跨语言的优点。 + +WAF是近几年比较“火”的一个词,意思是“网络应用防火墙”。与硬件“防火墙”类似,它是应用层面的“防火墙”,专门检测 HTTP 流量,是防护 Web 应用的安全技术。 + +WAF 通常位于 Web 服务器之前,可以阻止如 SQL 注入、跨站脚本等攻击,目前应用较多的一个开源项目是 ModSecurity,它能够完全集成进 Apache 或 Nginx。 + +小结 + +今天我详细介绍了与 HTTP 有关系的各种应用技术,在这里简单小结一下要点。 + + +互联网上绝大部分资源都使用 HTTP 协议传输; +浏览器是 HTTP 协议里的请求方,即 User Agent; +服务器是 HTTP 协议里的应答方,常用的有 Apache 和 Nginx; +CDN 位于浏览器和服务器之间,主要起到缓存加速的作用; +爬虫是另一类 User Agent,是自动访问网络资源的程序。 + + +希望通过今天的讲解,你能够更好地理解这些概念,也利于后续的课程学习。 + +课下作业 + + +你觉得 CDN 在对待浏览器和爬虫时会有差异吗?为什么? +你怎么理解 WebService 与 Web Server 这两个非常相似的词? + + +欢迎你通过留言分享答案,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/04HTTP世界全览(下):与HTTP相关的各种协议.md b/专栏/透视HTTP协议/04HTTP世界全览(下):与HTTP相关的各种协议.md new file mode 100644 index 0000000..f3a8b3e --- /dev/null +++ b/专栏/透视HTTP协议/04HTTP世界全览(下):与HTTP相关的各种协议.md @@ -0,0 +1,162 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 HTTP世界全览(下):与HTTP相关的各种协议 + 在上一讲中,我介绍了与 HTTP 相关的浏览器、服务器、CDN、网络爬虫等应用技术。 + +今天要讲的则是比较偏向于理论的各种 HTTP 相关协议,重点是 TCP/IP、DNS、URI、HTTPS 等,希望能够帮你理清楚它们与 HTTP 的关系。 + +同样的,我还是画了一张详细的思维导图,你可以点击后仔细查看。 + + + +TCP/IP + +TCP/IP 协议是目前网络世界“事实上”的标准通信协议,即使你没有用过也一定听说过,因为它太著名了。 + +TCP/IP 协议实际上是一系列网络通信协议的统称,其中最核心的两个协议是TCP和IP,其他的还有 UDP、ICMP、ARP 等等,共同构成了一个复杂但有层次的协议栈。 + +这个协议栈有四层,最上层是“应用层”,最下层是“链接层”,TCP 和 IP 则在中间:TCP 属于“传输层”,IP 属于“网际层”。协议的层级关系模型非常重要,我会在下一讲中再专门讲解,这里先暂时放一放。 + +IP 协议是“Internet Protocol”的缩写,主要目的是解决寻址和路由问题,以及如何在两点间传送数据包。IP 协议使用“IP 地址”的概念来定位互联网上的每一台计算机。可以对比一下现实中的电话系统,你拿着的手机相当于互联网上的计算机,而要打电话就必须接入电话网,由通信公司给你分配一个号码,这个号码就相当于 IP 地址。 + +现在我们使用的 IP 协议大多数是 v4 版,地址是四个用“.”分隔的数字,例如“192.168.0.1”,总共有 2^32,大约 42 亿个可以分配的地址。看上去好像很多,但互联网的快速发展让地址的分配管理很快就“捉襟见肘”。所以,就又出现了 v6 版,使用 8 组“:”分隔的数字作为地址,容量扩大了很多,有 2^128 个,在未来的几十年里应该是足够用了。 + +TCP 协议是“Transmission Control Protocol”的缩写,意思是“传输控制协议”,它位于 IP 协议之上,基于 IP 协议提供可靠的、字节流形式的通信,是 HTTP 协议得以实现的基础。 + +“可靠”是指保证数据不丢失,“字节流”是指保证数据完整,所以在 TCP 协议的两端可以如同操作文件一样访问传输的数据,就像是读写在一个密闭的管道里“流动”的字节。 + +在[第 2 讲]时我曾经说过,HTTP 是一个”传输协议”,但它不关心寻址、路由、数据完整性等传输细节,而要求这些工作都由下层来处理。因为互联网上最流行的是 TCP/IP 协议,而它刚好满足 HTTP 的要求,所以互联网上的 HTTP 协议就运行在了 TCP/IP 上,HTTP 也就可以更准确地称为“HTTP over TCP/IP”。 + +DNS + +在 TCP/IP 协议中使用 IP 地址来标识计算机,数字形式的地址对于计算机来说是方便了,但对于人类来说却既难以记忆又难以输入。 + +于是“域名系统”(Domain Name System)出现了,用有意义的名字来作为 IP 地址的等价替代。设想一下,你是愿意记“95.211.80.227”这样枯燥的数字,还是“nginx.org”这样的词组呢? + +在 DNS 中,“域名”(Domain Name)又称为“主机名”(Host),为了更好地标记不同国家或组织的主机,让名字更好记,所以被设计成了一个有层次的结构。 + +域名用“.”分隔成多个单词,级别从左到右逐级升高,最右边的被称为“顶级域名”。对于顶级域名,可能你随口就能说出几个,例如表示商业公司的“com”、表示教育机构的“edu”,表示国家的“cn”“uk”等,买火车票时的域名还记得吗?是“www.12306.cn”。 + + + +但想要使用 TCP/IP 协议来通信仍然要使用 IP 地址,所以需要把域名做一个转换,“映射”到它的真实 IP,这就是所谓的“域名解析”。 + +继续用刚才的打电话做个比喻,你想要打电话给小明,但不知道电话号码,就得在手机里的号码簿里一项一项地找,直到找到小明那一条记录,然后才能查到号码。这里的“小明”就相当于域名,而“电话号码”就相当于 IP 地址,这个查找的过程就是域名解析。 + +域名解析的实际操作要比刚才的例子复杂很多,因为互联网上的电脑实在是太多了。目前全世界有 13 组根 DNS 服务器,下面再有许多的顶级 DNS、权威 DNS 和更小的本地 DNS,逐层递归地实现域名查询。 + +HTTP 协议中并没有明确要求必须使用 DNS,但实际上为了方便访问互联网上的 Web 服务器,通常都会使用 DNS 来定位或标记主机名,间接地把 DNS 与 HTTP 绑在了一起。 + +URI/URL + +有了 TCP/IP 和 DNS,是不是我们就可以任意访问网络上的资源了呢? + +还不行,DNS 和 IP 地址只是标记了互联网上的主机,但主机上有那么多文本、图片、页面,到底要找哪一个呢?就像小明管理了一大堆文档,你怎么告诉他是哪个呢? + +所以就出现了 URI(Uniform Resource Identifier),中文名称是 统一资源标识符,使用它就能够唯一地标记互联网上资源。 + +URI 另一个更常用的表现形式是 URL(Uniform Resource Locator), 统一资源定位符,也就是我们俗称的“网址”,它实际上是 URI 的一个子集,不过因为这两者几乎是相同的,差异不大,所以通常不会做严格的区分。 + +我就拿 Nginx 网站来举例,看一下 URI 是什么样子的。 + +http://nginx.org/en/download.html + + +你可以看到,URI 主要有三个基本的部分构成: + + +协议名:即访问该资源应当使用的协议,在这里是“http”; +主机名:即互联网上主机的标记,可以是域名或 IP 地址,在这里是“nginx.org”; +路径:即资源在主机上的位置,使用“/”分隔多级目录,在这里是“/en/download.html”。 + + +还是用打电话来做比喻,你通过电话簿找到了小明,让他把昨天做好的宣传文案快递过来。那么这个过程中你就完成了一次 URI 资源访问,“小明”就是“主机名”,“昨天做好的宣传文案”就是“路径”,而“快递”,就是你要访问这个资源的“协议名”。 + +HTTPS + +在 TCP/IP、DNS 和 URI 的“加持”之下,HTTP 协议终于可以自由地穿梭在互联网世界里,顺利地访问任意的网页了,真的是“好生快活”。 + +但且慢,互联网上不仅有“美女”,还有很多的“野兽”。 + +假设你打电话找小明要一份广告创意,很不幸,电话被商业间谍给窃听了,他立刻动用种种手段偷窃了你的快递,就在你还在等包裹的时候,他抢先发布了这份广告,给你的公司造成了无形或有形的损失。 + +有没有什么办法能够防止这种情况的发生呢?确实有。你可以使用“加密”的方法,比如这样打电话: + + +你:“喂,小明啊,接下来我们改用火星文通话吧。” +小明:“好啊好啊,就用火星文吧。” +你:“巴拉巴拉巴拉巴拉……” +小明:“巴拉巴拉巴拉巴拉……” + + +如果你和小明说的火星文只有你们两个才懂,那么即使窃听到了这段谈话,他也不会知道你们到底在说什么,也就无从破坏你们的通话过程。 + +HTTPS 就相当于这个比喻中的“火星文”,它的全称是“HTTP over SSL/TLS”,也就是运行在 SSL/TLS 协议上的 HTTP。 + +注意它的名字,这里是 SSL/TLS,而不是 TCP/IP,它是一个负责加密通信的安全协议,建立在 TCP/IP 之上,所以也是个可靠的传输协议,可以被用作 HTTP 的下层。 + +因为 HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”,其中的“HTTP”和“TCP/IP”我们都已经明白了,只要再了解一下 SSL/TLS,HTTPS 也就能够轻松掌握。 + +SSL 的全称是“Secure Socket Layer”,由网景公司发明,当发展到 3.0 时被标准化,改名为 TLS,即“Transport Layer Security”,但由于历史的原因还是有很多人称之为 SSL/TLS,或者直接简称为 SSL。 + +SSL 使用了许多密码学最先进的研究成果,综合了对称加密、非对称加密、摘要算法、数字签名、数字证书等技术,能够在不安全的环境中为通信的双方创建出一个秘密的、安全的传输通道,为 HTTP 套上一副坚固的盔甲。 + +你可以在今后上网时留心看一下浏览器地址栏,如果有一个小锁头标志,那就表明网站启用了安全的 HTTPS 协议,而 URI 里的协议名,也从“http”变成了“https”。 + +代理 + +代理(Proxy)是 HTTP 协议中请求方和应答方中间的一个环节,作为“中转站”,既可以转发客户端的请求,也可以转发服务器的应答。 + +代理有很多的种类,常见的有: + + +匿名代理:完全“隐匿”了被代理的机器,外界看到的只是代理服务器; +透明代理:顾名思义,它在传输过程中是“透明开放”的,外界既知道代理,也知道客户端; +正向代理:靠近客户端,代表客户端向服务器发送请求; +反向代理:靠近服务器端,代表服务器响应客户端的请求; + + +上一讲提到的 CDN,实际上就是一种代理,它代替源站服务器响应客户端的请求,通常扮演着透明代理和反向代理的角色。 + +由于代理在传输过程中插入了一个“中间层”,所以可以在这个环节做很多有意思的事情,比如: + + +负载均衡:把访问请求均匀分散到多台机器,实现访问集群化; +内容缓存:暂存上下行的数据,减轻后端的压力; +安全防护:隐匿 IP, 使用 WAF 等工具抵御网络攻击,保护被代理的机器; +数据处理:提供压缩、加密等额外的功能。 + + +关于 HTTP 的代理还有一个特殊的“代理协议”(proxy protocol),它由知名的代理软件 HAProxy 制订,但并不是 RFC 标准,我也会在之后的课程里专门讲解。 + +小结 + +这次我介绍了与 HTTP 相关的各种协议,在这里简单小结一下今天的内容。 + + +TCP/IP 是网络世界最常用的协议,HTTP 通常运行在 TCP/IP 提供的可靠传输基础上; +DNS 域名是 IP 地址的等价替代,需要用域名解析实现到 IP 地址的映射; +URI 是用来标记互联网上资源的一个名字,由“协议名 + 主机名 + 路径”构成,俗称 URL; +HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”,为 HTTP 套了一个安全的外壳; +代理是 HTTP 传输过程中的“中转站”,可以实现缓存加速、负载均衡等功能。 + + +经过这两讲的学习,相信你应该对 HTTP 有了一个比较全面的了解,虽然还不是很深入,但已经为后续的学习扫清了障碍。 + +课下作业 + + +DNS 与 URI 有什么关系? +在讲代理时我特意没有举例说明,你能够用引入一个“小强”的角色,通过打电话来比喻一下吗? + + +欢迎你通过留言分享答案,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/05常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md b/专栏/透视HTTP协议/05常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md new file mode 100644 index 0000000..e0b1269 --- /dev/null +++ b/专栏/透视HTTP协议/05常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md @@ -0,0 +1,161 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了? + 在上一讲中,我简单提到了 TCP/IP 协议,它是 HTTP 协议的下层协议,负责具体的数据传输工作。并且还特别说了,TCP/IP 协议是一个“有层次的协议栈”。 + +在工作中你一定经常听别人谈起什么“四层负载均衡”“七层负载均衡”,什么“二层转发”“三层路由”,那么你真正理解这些层次的含义吗? + +网络分层的知识教科书上都有,但很多都是“泛泛而谈”,只有“学术价值”,于是就容易和实际应用“脱节”,造成的后果就是“似懂非懂”,真正用的时候往往会“一头雾水”。 + +所以,今天我就从 HTTP 应用的角度,帮你把这些模糊的概念弄清楚。 + +TCP/IP 网络分层模型 + +还是先从 TCP/IP 协议开始讲起,一是因为它非常经典,二是因为它是目前事实上的网络通信标准,研究它的实用价值最大。 + +TCP/IP 当初的设计者真的是非常聪明,创造性地提出了“分层”的概念,把复杂的网络通信划分出多个层次,再给每一个层次分配不同的职责,层次内只专心做自己的事情就好,用“分而治之”的思想把一个“大麻烦”拆分成了数个“小麻烦”,从而解决了网络通信的难题。 + +你应该对 TCP/IP 的协议栈有所了解吧,这里我再贴一下层次图。 + + + +TCP/IP 协议总共有四层,就像搭积木一样,每一层需要下层的支撑,同时又支撑着上层,任何一层被抽掉都可能会导致整个协议栈坍塌。 + +我们来仔细地看一下这个精巧的积木架构,注意它的层次顺序是“从下往上”数的,所以第一层就是最下面的一层。 + +第一层叫“链接层”(link layer),负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标记网络上的设备,所以有时候也叫 MAC 层。 + +第二层叫“网际层”或者“网络互连层”(internet layer),IP 协议就处在这一层。因为 IP 协议定义了“IP 地址”的概念,所以就可以在“链接层”的基础上,用 IP 地址取代 MAC 地址,把许许多多的局域网、广域网连接成一个虚拟的巨大网络,在这个网络里找设备时只要把 IP 地址再“翻译”成 MAC 地址就可以了。 + +第三层叫“传输层”(transport layer),这个层次协议的职责是保证数据在 IP 地址标记的两点之间“可靠”地传输,是 TCP 协议工作的层次,另外还有它的一个“小伙伴”UDP。 + +TCP 是一个有状态的协议,需要先与对方建立连接然后才能发送数据,而且保证数据不丢失不重复。而 UDP 则比较简单,它无状态,不用事先建立连接就可以任意发送数据,但不保证数据一定会发到对方。两个协议的另一个重要区别在于数据的形式。TCP 的数据是连续的“字节流”,有先后顺序,而 UDP 则是分散的小数据包,是顺序发,乱序收。 + +关于 TCP 和 UDP 可以展开讨论的话题还有很多,比如最经典的“三次握手”和“四次挥手”,一时半会很难说完,好在与 HTTP 的关系不是太大,以后遇到了再详细讲解。 + +协议栈的第四层叫“应用层”(application layer),由于下面的三层把基础打得非常好,所以在这一层就“百花齐放”了,有各种面向具体应用的协议。例如 Telnet、SSH、FTP、SMTP 等等,当然还有我们的 HTTP。 + +MAC 层的传输单位是帧(frame),IP 层的传输单位是包(packet),TCP 层的传输单位是段(segment),HTTP 的传输单位则是消息或报文(message)。但这些名词并没有什么本质的区分,可以统称为数据包。 + +OSI 网络分层模型 + +看完 TCP/IP 协议栈,你可能要问了,“它只有四层,那常说的七层怎么没见到呢?” + +别着急,这就是今天要说的第二个网络分层模型:OSI,全称是“开放式系统互联通信参考模型”(Open System Interconnection Reference Model)。 + +TCP/IP 发明于 1970 年代,当时除了它还有很多其他的网络协议,整个网络世界比较混乱。 + +这个时候国际标准组织(ISO)注意到了这种现象,感觉“野路子”太多,就想要来个“大一统”。于是设计出了一个新的网络分层模型,想用这个新框架来统一既存的各种网络协议。 + +OSI 模型分成了七层,部分层次与 TCP/IP 很像,从下到上分别是: + + + + +第一层:物理层,网络的物理形式,例如电缆、光纤、网卡、集线器等等; +第二层:数据链路层,它基本相当于 TCP/IP 的链接层; +第三层:网络层,相当于 TCP/IP 里的网际层; +第四层:传输层,相当于 TCP/IP 里的传输层; +第五层:会话层,维护网络中的连接状态,即保持会话和同步; +第六层:表示层,把数据转换为合适、可理解的语法和语义; +第七层:应用层,面向具体的应用传输数据。 + + +至此,我们常说的“四层”“七层”就出现了。 + +不过国际标准组织心里也很清楚,TCP/IP 等协议已经在许多网络上实际运行,再推翻重来是不可能的。所以,OSI 分层模型在发布的时候就明确地表明是一个“参考”,不是强制标准,意思就是说,“你们以后该干什么还干什么,我不管,但面子上还是要按照我说的来”。 + +但 OSI 模型也是有优点的。对比一下就可以看出,TCP/IP 是一个纯软件的栈,没有网络应有的最根基的电缆、网卡等物理设备的位置。而 OSI 则补足了这个缺失,在理论层面上描述网络更加完整。 + +还有一个重要的形式上的优点:OSI 为每一层标记了明确了编号,最底层是一层,最上层是七层,而 TCP/IP 的层次从来只有名字而没有编号。显然,在交流的时候说“七层”要比“应用层”更简单快捷,特别是英文,对比一下“Layer seven”与“application layer”。 + +综合以上几点,在 OSI 模型之后,“四层”“七层”这样的说法就逐渐流行开了。不过在实际工作中你一定要注意,这种说法只是“理论上”的层次,并不是与现实完全对应。 + +两个分层模型的映射关系 + +现在我们有了两个网络分层模型:TCP/IP 和 OSI,新的问题又出现了,一个是四层模型,一个是七层模型,这两者应该如何互相映射或者说互相解释呢? + +好在 OSI 在设计之初就参考了 TCP/IP 等多个协议,可以比较容易但不是很精确地实现对应关系。 + + + + +第一层:物理层,TCP/IP 里无对应; +第二层:数据链路层,对应 TCP/IP 的链接层; +第三层:网络层,对应 TCP/IP 的网际层; +第四层:传输层,对应 TCP/IP 的传输层; +第五、六、七层:统一对应到 TCP/IP 的应用层。 + + +所以你看,这就是“理想与现实”之间的矛盾。理想很美好,有七层,但现实很残酷,只有四层,“多余”的五层、六层就这样“消失”了。 + +但这也有一定的实际原因。 + +OSI 的分层模型在四层以上分的太细,而 TCP/IP 实际应用时的会话管理、编码转换、压缩等和具体应用经常联系的很紧密,很难分开。例如,HTTP 协议就同时包含了连接管理和数据格式定义。 + +到这里,你应该能够明白一开始那些“某某层”的概念了。 + +所谓的“四层负载均衡”就是指工作在传输层上,基于 TCP/IP 协议的特性,例如 IP 地址、端口号等实现对后端服务器的负载均衡。 + +所谓的“七层负载均衡”就是指工作在应用层上,看到的是 HTTP 协议,解析 HTTP 报文里的 URI、主机名、资源类型等数据,再用适当的策略转发给后端服务器。 + +TCP/IP 协议栈的工作方式 + +TCP/IP 协议栈是如何工作的呢? + +你可以把 HTTP 利用 TCP/IP 协议栈传输数据想象成一个发快递的过程。 + +假设你想把一件毛绒玩具送给朋友,但你要先拿个塑料袋套一下,这件玩具就相当于 HTTP 协议里要传输的内容,比如 HTML,然后 HTTP 协议为它加一个 HTTP 专用附加数据。 + +你把玩具交给快递小哥,为了保护货物,他又加了层包装再贴了个标签,相当于在 TCP 层给数据再次打包,加上了 TCP 头。 + +接着快递小哥下楼,把包裹放进了三轮车里,运到集散点,然后再装进更大的卡车里,相当于在 IP 层、MAC 层对 TCP 数据包加上了 IP 头、MAC 头。 + +之后经过漫长的运输,包裹到达目的地,要卸货再放进另一位快递员的三轮车,就是在 IP 层、MAC 层传输后拆包。 + +快递员到了你朋友的家门口,撕掉标签,去除了 TCP 层的头,你朋友再拆掉塑料袋包装,也就是 HTTP 头,最后就拿到了玩具,也就是真正的 HTML 页面。 + +这个比喻里省略了很多 TCP/IP 协议里的细节,比如建连、路由、数据切分与重组、错误检查等,但核心的数据传输过程是差不多的。 + +HTTP 协议的传输过程就是这样通过协议栈逐层向下,每一层都添加本层的专有数据,层层打包,然后通过下层发送出去。 + +接收数据是则是相反的操作,从下往上穿过协议栈,逐层拆包,每层去掉本层的专有头,上层就会拿到自己的数据。 + +但下层的传输过程对于上层是完全“透明”的,上层也不需要关心下层的具体实现细节,所以就 HTTP 层次来看,它不管下层是不是 TCP/IP 协议,看到的只是一个可靠的传输链路,只要把数据加上自己的头,对方就能原样收到。 + +我为这个过程画了一张图,你可以对照着加深理解。 + + + +小结 + +这次我们学习了 HTTP 所在的网络分层模型,它是工作中常用的交流语言,在这里简单小结一下今天的内容。 + + +TCP/IP 分为四层,核心是二层的 IP 和三层的 TCP,HTTP 在第四层; +OSI 分为七层,基本对应 TCP/IP,TCP 在第四层,HTTP 在第七层; +OSI 可以映射到 TCP/IP,但这期间一、五、六层消失了; +日常交流的时候我们通常使用 OSI 模型,用四层、七层等术语; +HTTP 利用 TCP/IP 协议栈逐层打包再拆包,实现了数据传输,但下面的细节并不可见。 + + +有一个辨别四层和七层比较好的(但不是绝对的)小窍门,“两个凡是”:凡是由操作系统负责处理的就是四层或四层以下,否则,凡是需要由应用程序(也就是你自己写代码)负责处理的就是七层。 + +课下作业 + + +你能用自己的话解释一下“二层转发”“三层路由”吗? +你认为上一讲中的 DNS 协议位于哪一层呢? +你认为 CDN 工作在那一层呢? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/06域名里有哪些门道?.md b/专栏/透视HTTP协议/06域名里有哪些门道?.md new file mode 100644 index 0000000..4aa4bd1 --- /dev/null +++ b/专栏/透视HTTP协议/06域名里有哪些门道?.md @@ -0,0 +1,148 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 域名里有哪些门道? + 在上一讲里,我们学习了 HTTP 协议使用的 TCP/IP 协议栈,知道了 HTTP 协议是运行在 TCP/IP 上的。 + +IP 协议的职责是“网际互连”,它在 MAC 层之上,使用 IP 地址把 MAC 编号转换成了四位数字,这就对物理网卡的 MAC 地址做了一层抽象,发展出了许多的“新玩法”。 + +例如,分为 A、B、C、D、E 五种类型,公有地址和私有地址,掩码分割子网等。只要每个小网络在 IP 地址这个概念上达成一致,不管它在 MAC 层有多大的差异,都可以接入 TCP/IP 协议栈,最终汇合进整个互联网。 + +但接入互联网的计算机越来越多,IP 地址的缺点也就暴露出来了,最主要的是它“对人不友好”,虽然比 MAC 的 16 进制数要好一点,但还是难于记忆和输入。 + +怎么解决这个问题呢? + +那就“以其人之道还治其人之身”,在 IP 地址之上再来一次抽象,把数字形式的 IP 地址转换成更有意义更好记的名字,在字符串的层面上再增加“新玩法”。于是,DNS 域名系统就这么出现了。 + +域名的形式 + +在第 4 讲曾经说过,域名是一个有层次的结构,是一串用“.”分隔的多个单词,最右边的被称为“顶级域名”,然后是“二级域名”,层级关系向左依次降低。 + +最左边的是主机名,通常用来表明主机的用途,比如“www”表示提供万维网服务、“mail”表示提供邮件服务,不过这也不是绝对的,名字的关键是要让我们容易记忆。 + +看一下极客时间的域名“time.geekbang.org”,这里的“org”就是顶级域名,“geekbang”是二级域名,“time”则是主机名。使用这个域名,DNS 就会把它转换成相应的 IP 地址,你就可以访问极客时间的网站了。 + +域名不仅能够代替 IP 地址,还有许多其他的用途。 + +在 Apache、Nginx 这样的 Web 服务器里,域名可以用来标识虚拟主机,决定由哪个虚拟主机来对外提供服务,比如在 Nginx 里就会使用“server_name”指令: + +server { + listen 80; # 监听 80 端口 + server_name time.geekbang.org; # 主机名是 time.geekbang.org + ... +} + + +域名本质上还是个名字空间系统,使用多级域名就可以划分出不同的国家、地区、组织、公司、部门,每个域名都是独一无二的,可以作为一种身份的标识。 + +举个例子吧,假设 A 公司里有个小明,B 公司里有个小强,于是他们就可以分别说是“小明.A 公司”,“小强.B 公司”,即使 B 公司里也有个小明也不怕,可以标记为“小明.B 公司”,很好地解决了重名问题。 + +因为这个特性,域名也被扩展到了其他应用领域,比如 Java 的包机制就采用域名作为命名空间,只是它使用了反序。如果极客时间要开发 Java 应用,那么它的包名可能就是“org.geekbang.time”。 + +而 XML 里使用 URI 作为名字空间,也是间接使用了域名。 + +域名的解析 + +就像 IP 地址必须转换成 MAC 地址才能访问主机一样,域名也必须要转换成 IP 地址,这个过程就是“域名解析”。 + +目前全世界有几亿个站点,有几十亿网民,而每天网络上发生的 HTTP 流量更是天文数字。这些请求绝大多数都是基于域名来访问网站的,所以 DNS 就成了互联网的重要基础设施,必须要保证域名解析稳定可靠、快速高效。 + +DNS 的核心系统是一个三层的树状、分布式服务,基本对应域名的结构: + + +根域名服务器(Root DNS Server):管理顶级域名服务器,返回“com”“net”“cn”等顶级域名服务器的 IP 地址; +顶级域名服务器(Top-level DNS Server):管理各自域名下的权威域名服务器,比如 com 顶级域名服务器可以返回 apple.com 域名服务器的 IP 地址; +权威域名服务器(Authoritative DNS Server):管理自己域名下主机的 IP 地址,比如 apple.com 权威域名服务器可以返回 www.apple.com 的 IP 地址。 + + + + +在这里根域名服务器是关键,它必须是众所周知的,否则下面的各级服务器就无从谈起了。目前全世界共有 13 组根域名服务器,又有数百台的镜像,保证一定能够被访问到。 + +有了这个系统以后,任何一个域名都可以在这个树形结构里从顶至下进行查询,就好像是把域名从右到左顺序走了一遍,最终就获得了域名对应的 IP 地址。 + +例如,你要访问“www.apple.com”,就要进行下面的三次查询: + + +访问根域名服务器,它会告诉你“com”顶级域名服务器的地址; +访问“com”顶级域名服务器,它再告诉你“apple.com”域名服务器的地址; +最后访问“apple.com”域名服务器,就得到了“www.apple.com”的地址。 + + +虽然核心的 DNS 系统遍布全球,服务能力很强也很稳定,但如果全世界的网民都往这个系统里挤,即使不挤瘫痪了,访问速度也会很慢。 + +所以在核心 DNS 系统之外,还有两种手段用来减轻域名解析的压力,并且能够更快地获取结果,基本思路就是“缓存”。 + +首先,许多大公司、网络运行商都会建立自己的 DNS 服务器,作为用户 DNS 查询的代理,代替用户访问核心 DNS 系统。这些“野生”服务器被称为“非权威域名服务器”,可以缓存之前的查询结果,如果已经有了记录,就无需再向根服务器发起查询,直接返回对应的 IP 地址。 + +这些 DNS 服务器的数量要比核心系统的服务器多很多,而且大多部署在离用户很近的地方。比较知名的 DNS 有 Google 的“8.8.8.8”,Microsoft 的“4.2.2.1”,还有 CloudFlare 的“1.1.1.1”等等。 + +其次,操作系统里也会对 DNS 解析结果做缓存,如果你之前访问过“www.apple.com”,那么下一次在浏览器里再输入这个网址的时候就不会再跑到 DNS 那里去问了,直接在操作系统里就可以拿到 IP 地址。 + +另外,操作系统里还有一个特殊的“主机映射”文件,通常是一个可编辑的文本,在 Linux 里是“/etc/hosts”,在 Windows 里是“C:\WINDOWS\system32\drivers\etc\hosts”,如果操作系统在缓存里找不到 DNS 记录,就会找这个文件。 + +有了上面的“野生”DNS 服务器、操作系统缓存和 hosts 文件后,很多域名解析的工作就都不用“跋山涉水”了,直接在本地或本机就能解决,不仅方便了用户,也减轻了各级 DNS 服务器的压力,效率就大大提升了。 + +下面的这张图比较完整地表示了现在的 DNS 架构。 + + + +在 Nginx 里有这么一条配置指令“resolver”,它就是用来配置 DNS 服务器的,如果没有它,那么 Nginx 就无法查询域名对应的 IP,也就无法反向代理到外部的网站。 + +resolver 8.8.8.8 valid=30s; # 指定 Google 的 DNS,缓存 30 秒 + + +域名的“新玩法” + +有了域名,又有了可以稳定工作的解析系统,于是我们就可以实现比 IP 地址更多的“新玩法”了。 + +第一种,也是最简单的,“重定向”。因为域名代替了 IP 地址,所以可以让对外服务的域名不变,而主机的 IP 地址任意变动。当主机有情况需要下线、迁移时,可以更改 DNS 记录,让域名指向其他的机器。 + +比如,你有一台“buy.tv”的服务器要临时停机维护,那你就可以通知 DNS 服务器:“我这个 buy.tv 域名的地址变了啊,原先是 1.2.3.4,现在是 5.6.7.8,麻烦你改一下。”DNS 于是就修改内部的 IP 地址映射关系,之后再有访问 buy.tv 的请求就不走 1.2.3.4 这台主机,改由 5.6.7.8 来处理,这样就可以保证业务服务不中断。 + +第二种,因为域名是一个名字空间,所以可以使用 bind9 等开源软件搭建一个在内部使用的 DNS,作为名字服务器。这样我们开发的各种内部服务就都用域名来标记,比如数据库服务都用域名“mysql.inner.app”,商品服务都用“goods.inner.app”,发起网络通信时也就不必再使用写死的 IP 地址了,可以直接用域名,而且这种方式也兼具了第一种“玩法”的优势。 + +第三种“玩法”包含了前两种,也就是基于域名实现的负载均衡。 + +这种“玩法”也有两种方式,两种方式可以混用。 + +第一种方式,因为域名解析可以返回多个 IP 地址,所以一个域名可以对应多台主机,客户端收到多个 IP 地址后,就可以自己使用轮询算法依次向服务器发起请求,实现负载均衡。 + +第二种方式,域名解析可以配置内部的策略,返回离客户端最近的主机,或者返回当前服务质量最好的主机,这样在 DNS 端把请求分发到不同的服务器,实现负载均衡。 + +前面我们说的都是可信的 DNS,如果有一些不怀好意的 DNS,那么它也可以在域名这方面“做手脚”,弄一些比较“恶意”的“玩法”,举两个例子: + + +“域名屏蔽”,对域名直接不解析,返回错误,让你无法拿到 IP 地址,也就无法访问网站; +“域名劫持”,也叫“域名污染”,你要访问 A 网站,但 DNS 给了你 B 网站。 + + +好在互联网上还是好人多,而且 DNS 又是互联网的基础设施,这些“恶意 DNS”并不多见,你上网的时候不需要太过担心。 + +小结 + +这次我们学习了与 HTTP 协议有重要关系的域名和 DNS,在这里简单小结一下今天的内容: + + +域名使用字符串来代替 IP 地址,方便用户记忆,本质上一个名字空间系统; +DNS 就像是我们现实世界里的电话本、查号台,统管着互联网世界里的所有网站,是一个“超级大管家”; +DNS 是一个树状的分布式查询系统,但为了提高查询效率,外围有多级的缓存; +使用 DNS 可以实现基于域名的负载均衡,既可以在内网,也可以在外网。 + + +课下作业 + + +在浏览器地址栏里随便输入一个不存在的域名,比如就叫“www. 不存在.com”,试着解释一下它的 DNS 解析过程。 +如果因为某些原因,DNS 失效或者出错了,会出现什么后果? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/07自己动手,搭建HTTP实验环境.md b/专栏/透视HTTP协议/07自己动手,搭建HTTP实验环境.md new file mode 100644 index 0000000..dc7e3f6 --- /dev/null +++ b/专栏/透视HTTP协议/07自己动手,搭建HTTP实验环境.md @@ -0,0 +1,167 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 自己动手,搭建HTTP实验环境 + 这一讲是“破冰篇”的最后一讲,我会先简单地回顾一下之前的内容,然后在 Windows 系统上实际操作,用几个应用软件搭建出一个“最小化”的 HTTP 实验环境,方便后续的“基础篇”“进阶篇”“安全篇”的学习。 + +“破冰篇”回顾 + +HTTP 协议诞生于 30 年前,设计之初的目的是用来传输纯文本数据。但由于形式灵活,搭配 URI、HTML 等技术能够把互联网上的资源都联系起来,构成一个复杂的超文本系统,让人们自由地获取信息,所以得到了迅猛发展。 + +HTTP 有多个版本,目前应用的最广泛的是 HTTP/1.1,它几乎可以说是整个互联网的基石。但 HTTP/1.1 的性能难以满足如今的高流量网站,于是又出现了 HTTP/2 和 HTTP/3。不过这两个新版本的协议还没有完全推广开。在可预见的将来,HTTP/1.1 还会继续存在下去。 + +HTTP 翻译成中文是“超文本传输协议”,是一个应用层的协议,通常基于 TCP/IP,能够在网络的任意两点之间传输文字、图片、音频、视频等数据。 + +HTTP 协议中的两个端点称为请求方和应答方。请求方通常就是 Web 浏览器,也叫 user agent,应答方是 Web 服务器,存储着网络上的大部分静态或动态的资源。 + +在浏览器和服务器之间还有一些“中间人”的角色,如 CDN、网关、代理等,它们也同样遵守 HTTP 协议,可以帮助用户更快速、更安全地获取资源。 + +HTTP 协议不是一个孤立的协议,需要下层很多其他协议的配合。最基本的是 TCP/IP,实现寻址、路由和可靠的数据传输,还有 DNS 协议实现对互联网上主机的定位查找。 + +对 HTTP 更准确的称呼是“HTTP over TCP/IP”,而另一个“HTTP over SSL/TLS”就是增加了安全功能的 HTTPS。 + +软件介绍 + +常言道“实践出真知”,又有俗语“光说不练是假把式”。要研究 HTTP 协议,最好有一个实际可操作、可验证的环境,通过实际的数据、现象来学习,肯定要比单纯的“动嘴皮子”效果要好的多。 + +现成的环境当然有,只要能用浏览器上网,就会有 HTTP 协议,就可以进行实验。但现实的网络环境又太复杂了,有很多无关的干扰因素,这些“噪音”会“淹没”真正有用的信息。 + +所以,我给你的建议是:搭建一个“最小化”的环境,在这个环境里仅有 HTTP 协议的两个端点:请求方和应答方,去除一切多余的环节,从而可以抓住重点,快速掌握 HTTP 的本质。 + + + +简单说一下这个“最小化”环境用到的应用软件: + + +Wireshark +Chrome/Firefox +Telnet +OpenResty + + +Wireshark是著名的网络抓包工具,能够截获在 TCP/IP 协议栈中传输的所有流量,并按协议类型、地址、端口等任意过滤,功能非常强大,是学习网络协议的必备工具。 + +它就像是网络世界里的一台“高速摄像机”,把只在一瞬间发生的网络传输过程如实地“拍摄”下来,事后再“慢速回放”,让我们能够静下心来仔细地分析那一瞬到底发生了什么。 + +Chrome是 Google 开发的浏览器,是目前的主流浏览器之一。它不仅上网方便,也是一个很好的调试器,对 HTTP/1.1、HTTPS、HTTP/2、QUIC 等的协议都支持得非常好,用 F12 打开“开发者工具”还可以非常详细地观测 HTTP 传输全过程的各种数据。 + +如果你更习惯使用Firefox,那也没问题,其实它和 Chrome 功能上都差不太多,选择自己喜欢的就好。 + +与 Wireshark 不同,Chrome 和 Firefox 属于“事后诸葛亮”,不能观测 HTTP 传输的过程,只能看到结果。 + +Telnet是一个经典的虚拟终端,基于 TCP 协议远程登录主机,我们可以使用它来模拟浏览器的行为,连接服务器后手动发送 HTTP 请求,把浏览器的干扰也彻底排除,能够从最原始的层面去研究 HTTP 协议。 + +OpenResty你可能比较陌生,它是基于 Nginx 的一个“强化包”,里面除了 Nginx 还有一大堆有用的功能模块,不仅支持 HTTP/HTTPS,还特别集成了脚本语言 Lua 简化 Nginx 二次开发,方便快速地搭建动态网关,更能够当成应用容器来编写业务逻辑。 + +选择 OpenResty 而不直接用 Nginx 的原因是它相当于 Nginx 的“超集”,功能更丰富,安装部署更方便。我也会用 Lua 编写一些服务端脚本,实现简单的 Web 服务器响应逻辑,方便实验。 + +安装过程 + +这个“最小化”环境的安装过程也比较简单,大约只需要你半个小时不到的时间就能搭建完成。 + +我在 GitHub 上为本专栏开了一个项目:http_study,可以直接用“git clone”下载,或者去 Release 页面,下载打好的压缩包。 + +我使用的操作环境是 Windows 10,如果你用的是 Mac 或者 Linux,可以用 VirtualBox 等虚拟机软件安装一个 Windows 虚拟机,再在里面操作。 + +首先你要获取最新的 http_study 项目源码,假设 clone 或解压的目录是“D:\http_study”,操作完成后大概是下图这个样子。 + + + +Chrome 和 WireShark 的安装比较简单,一路按“下一步”就可以了。版本方面使用最新的就好,我的版本可能不是最新的,Chrome 是 73,WireShark 是 3.0.0。 + +Windows 10 自带 Telnet,不需要安装,但默认是不启用的,需要你稍微设置一下。 + +打开 Windows 的设置窗口,搜索“Telnet”,就会找到“启用或关闭 Windows 功能”,在这个窗口里找到“Telnet 客户端”,打上对钩就可以了,可以参考截图。 + + + +接下来我们要安装 OpenResty,去它的官网,点击左边栏的“Download”,进入下载页面,下载适合你系统的版本(这里我下载的是 64 位的 1.15.8.1,包的名字是“openresty-1.15.8.1-win64.zip”)。 + + + +然后要注意,你必须把 OpenResty 的压缩包解压到刚才的“D:\http_study”目录里,并改名为“openresty”。 + + + +安装工作马上就要完成了,为了能够让浏览器能够使用 DNS 域名访问我们的实验环境,还要改一下本机的 hosts 文件,位置在“C:\WINDOWS\system32\drivers\etc”,在里面添加三行本机 IP 地址到测试域名的映射,你也可以参考 GitHub 项目里的 hosts 文件,这就相当于在一台物理实机上“托管”了三个虚拟主机。 + +127.0.0.1 www.chrono.com +127.0.0.1 www.metroid.net +127.0.0.1 origin.io + + +注意修改 hosts 文件需要管理员权限,直接用记事本编辑是不行的,可以切换管理员身份,或者改用其他高级编辑器,比如 Notepad++,而且改之前最好做个备份。 + +到这里,我们的安装工作就完成了!之后你就可以用 Wireshark、Chrome、Telnet 在这个环境里随意“折腾”,弄坏了也不要紧,只要把目录删除,再来一遍操作就能复原。 + +测试验证 + +实验环境搭建完了,但还需要把它运行起来,做一个简单的测试验证,看是否运转正常。 + +首先我们要启动 Web 服务器,也就是 OpenResty。 + +在 http_study 的“www”目录下有四个批处理文件,分别是: + + + + +start:启动 OpenResty 服务器; +stop:停止 OpenResty 服务器; +reload:重启 OpenResty 服务器; +list:列出已经启动的 OpenResty 服务器进程。 + + +使用鼠标双击“start”批处理文件,就会启动 OpenResty 服务器在后台运行,这个过程可能会有 Windows 防火墙的警告,选择“允许”即可。 + +运行后,鼠标双击“list”可以查看 OpenResty 是否已经正常启动,应该会有两个 nginx.exe 的后台进程,大概是下图的样子。 + + + +有了 Web 服务器后,接下来我们要运行 Wireshark,开始抓包。 + +因为我们的实验环境运行在本机的 127.0.0.1 上,也就是 loopback“环回”地址。所以,在 Wireshark 里要选择“Npcap loopback Adapter”,过滤器选择“HTTP TCP port(80)”,即只抓取 HTTP 相关的数据包。鼠标双击开始界面里的“Npcap loopback Adapter”即可开始抓取本机上的网络数据。 + + + +然后我们打开 Chrome,在地址栏输入“http://localhost/”,访问刚才启动的 OpenResty 服务器,就会看到一个简单的欢迎界面,如下图所示。 + + + +这时再回头去看 Wireshark,应该会显示已经抓到了一些数据,就可以用鼠标点击工具栏里的“停止捕获”按钮告诉 Wireshark“到此为止”,不再继续抓包。 + + + +至于这些数据是什么,表示什么含义,我会在下一讲再详细介绍。 + +如果你能够在自己的电脑上走到这一步,就说明“最小化”的实验环境已经搭建成功了,不要忘了实验结束后运行批处理“stop”停止 OpenResty 服务器。 + +小结 + +这次我们学习了如何在自己的电脑上搭建 HTTP 实验环境,在这里简单小结一下今天的内容。 + + +现实的网络环境太复杂,有很多干扰因素,搭建“最小化”的环境可以快速抓住重点,掌握 HTTP 的本质; +我们选择 Wireshark 作为抓包工具,捕获在 TCP/IP 协议栈中传输的所有流量; +我们选择 Chrome 或 Firefox 浏览器作为 HTTP 协议中的 user agent; +我们选择 OpenResty 作为 Web 服务器,它是一个 Nginx 的“强化包”,功能非常丰富; +Telnet 是一个命令行工具,可用来登录主机模拟浏览器操作; +在 GitHub 上可以下载到本专栏的专用项目源码,只要把 OpenResty 解压到里面即可完成实验环境的搭建。 + + +课下作业 + + +按照今天所学的,在你自己的电脑上搭建出这个 HTTP 实验环境并测试验证。 + +由于篇幅所限,我无法详细介绍 Wireshark,你有时间可以再上网搜索 Wireshark 相关的资料,了解更多的用法。 + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/08键入网址再按下回车,后面究竟发生了什么?.md b/专栏/透视HTTP协议/08键入网址再按下回车,后面究竟发生了什么?.md new file mode 100644 index 0000000..3e4381f --- /dev/null +++ b/专栏/透视HTTP协议/08键入网址再按下回车,后面究竟发生了什么?.md @@ -0,0 +1,142 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 键入网址再按下回车,后面究竟发生了什么? + 经过上一讲的学习,你是否已经在自己的电脑上搭建好了“最小化”的 HTTP 实验环境呢? + +我相信你的答案一定是“Yes”,那么,让我们立刻开始“螺蛳壳里做道场”,在这个实验环境里看一下 HTTP 协议工作的全过程。 + +使用 IP 地址访问 Web 服务器 + +首先我们运行 www 目录下的“start”批处理程序,启动本机的 OpenResty 服务器,启动后可以用“list”批处理确认服务是否正常运行。 + +然后我们打开 Wireshark,选择“HTTP TCP port(80)”过滤器,再鼠标双击“Npcap loopback Adapter”,开始抓取本机 127.0.0.1 地址上的网络数据。 + +第三步,在 Chrome 浏览器的地址栏里输入http://127.0.0.1/,再按下回车键,等欢迎页面显示出来后 Wireshark 里就会有捕获的数据包,如下图所示。 + + + +如果你还没有搭好实验环境,或者捕获与本文里的不一致也没关系。我把这次捕获的数据存成了 pcap 包,文件名是“08-1”,放到了 GitHub 上,你可以下载到本地后再用 Wireshark 打开,完全精确“重放”刚才的 HTTP 传输过程。 + +抓包分析 + +在 Wireshark 里你可以看到,这次一共抓到了 11 个包(这里用了滤包功能,滤掉了 3 个包,原本是 14 个包),耗时 0.65 秒,下面我们就来一起分析一下”键入网址按下回车”后数据传输的全过程。 + +通过前面“破冰篇”的讲解,你应该知道 HTTP 协议是运行在 TCP/IP 基础上的,依靠 TCP/IP 协议来实现数据的可靠传输。所以浏览器要用 HTTP 协议收发数据,首先要做的就是建立 TCP 连接。 + +因为我们在地址栏里直接输入了 IP 地址“127.0.0.1”,而 Web 服务器的默认端口是 80,所以浏览器就要依照 TCP 协议的规范,使用“三次握手”建立与 Web 服务器的连接。 + +对应到 Wireshark 里,就是最开始的三个抓包,浏览器使用的端口是 52085,服务器使用的端口是 80,经过 SYN、SYN/ACK、ACK 的三个包之后,浏览器与服务器的 TCP 连接就建立起来了。 + +有了可靠的 TCP 连接通道后,HTTP 协议就可以开始工作了。于是,浏览器按照 HTTP 协议规定的格式,通过 TCP 发送了一个“GET / HTTP/1.1”请求报文,也就是 Wireshark 里的第四个包。至于包的内容具体是什么现在先不用管,我们下一讲再说。 + +随后,Web 服务器回复了第五个包,在 TCP 协议层面确认:“刚才的报文我已经收到了”,不过这个 TCP 包 HTTP 协议是看不见的。 + +Web 服务器收到报文后在内部就要处理这个请求。同样也是依据 HTTP 协议的规定,解析报文,看看浏览器发送这个请求想要干什么。 + +它一看,原来是要求获取根目录下的默认文件,好吧,那我就从磁盘上把那个文件全读出来,再拼成符合 HTTP 格式的报文,发回去吧。这就是 Wireshark 里的第六个包“HTTP/1.1 200 OK”,底层走的还是 TCP 协议。 + +同样的,浏览器也要给服务器回复一个 TCP 的 ACK 确认,“你的响应报文收到了,多谢。”,即第七个包。 + +这时浏览器就收到了响应数据,但里面是什么呢?所以也要解析报文。一看,服务器给我的是个 HTML 文件,好,那我就调用排版引擎、JavaScript 引擎等等处理一下,然后在浏览器窗口里展现出了欢迎页面。 + +这之后还有两个来回,共四个包,重复了相同的步骤。这是浏览器自动请求了作为网站图标的“favicon.ico”文件,与我们输入的网址无关。但因为我们的实验环境没有这个文件,所以服务器在硬盘上找不到,返回了一个“404 Not Found”。 + +至此,“键入网址再按下回车”的全过程就结束了。 + +我为这个过程画了一个交互图,你可以对照着看一下。不过要提醒你,图里 TCP 关闭连接的“四次挥手”在抓包里没有出现,这是因为 HTTP/1.1 长连接特性,默认不会立即关闭连接。 + + + +再简要叙述一下这次最简单的浏览器 HTTP 请求过程: + + +浏览器从地址栏的输入中获得服务器的 IP 地址和端口号; +浏览器用 TCP 的三次握手与服务器建立连接; +浏览器向服务器发送拼好的报文; +服务器收到报文后处理请求,同样拼好报文再发给浏览器; +浏览器解析报文,渲染输出页面。 + + +使用域名访问 Web 服务器 + +刚才我们是在浏览器地址栏里直接输入 IP 地址,但绝大多数情况下,我们是不知道服务器 IP 地址的,使用的是域名,那么改用域名后这个过程会有什么不同吗? + +还是实际动手试一下吧,把地址栏的输入改成“http://www.Chrono.com”,重复 Wireshark 抓包过程,你会发现,好像没有什么不同,浏览器上同样显示出了欢迎界面,抓到的包也同样是 11 个:先是三次握手,然后是两次 HTTP 传输。 + +这里就出现了一个问题:浏览器是如何从网址里知道“www.Chrono(加微信:642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。).com”的 IP 地址就是“127.0.0.1”的呢? + +还记得我们之前讲过的 DNS 知识吗?浏览器看到了网址里的“www.Chrono(加微信:642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。).com”,发现它不是数字形式的 IP 地址,那就肯定是域名了,于是就会发起域名解析动作,通过访问一系列的域名解析服务器,试图把这个域名翻译成 TCP/IP 协议里的 IP 地址。 + +不过因为域名解析的全过程实在是太复杂了,如果每一个域名都要大费周折地去网上查一下,那我们上网肯定会慢得受不了。 + +所以,在域名解析的过程中会有多级的缓存,浏览器首先看一下自己的缓存里有没有,如果没有就向操作系统的缓存要,还没有就检查本机域名解析文件 hosts,也就是上一讲中我们修改的“C:\WINDOWS\system32\drivers\etc\hosts”。 + +刚好,里面有一行映射关系“127.0.0.1 www.Chrono(加微信:642945106 发送“赠送”领取赠送精品课程 发数字“2”获取众筹列表。).com”,于是浏览器就知道了域名对应的 IP 地址,就可以愉快地建立 TCP 连接发送 HTTP 请求了。 + +我把这个过程也画出了一张图,但省略了 TCP/IP 协议的交互部分,里面的浏览器多出了一个访问 hosts 文件的动作,也就是本机的 DNS 解析。 + + + +真实的网络世界 + +通过上面两个在“最小化”环境里的实验,你是否已经对 HTTP 协议的工作流程有了基本的认识呢? + +第一个实验是最简单的场景,只有两个角色:浏览器和服务器,浏览器可以直接用 IP 地址找到服务器,两者直接建立 TCP 连接后发送 HTTP 报文通信。 + +第二个实验在浏览器和服务器之外增加了一个 DNS 的角色,浏览器不知道服务器的 IP 地址,所以必须要借助 DNS 的域名解析功能得到服务器的 IP 地址,然后才能与服务器通信。 + +真实的互联网世界要比这两个场景要复杂的多,我利用下面的这张图来做一个详细的说明。 + + + +如果你用的是电脑台式机,那么你可能会使用带水晶头的双绞线连上网口,由交换机接入固定网络。如果你用的是手机、平板电脑,那么你可能会通过蜂窝网络、WiFi,由电信基站、无线热点接入移动网络。 + +接入网络的同时,网络运行商会给你的设备分配一个 IP 地址,这个地址可能是静态分配的,也可能是动态分配的。静态 IP 就始终不变,而动态 IP 可能你下次上网就变了。 + +假设你要访问的是 Apple 网站,显然你是不知道它的真实 IP 地址的,在浏览器里只能使用域名“www.apple.com”访问,那么接下来要做的必然是域名解析。这就要用 DNS 协议开始从操作系统、本地 DNS、根 DNS、顶级 DNS、权威 DNS 的层层解析,当然这中间有缓存,可能不会费太多时间就能拿到结果。 + +别忘了互联网上还有另外一个重要的角色 CDN,它也会在 DNS 的解析过程中“插上一脚”。DNS 解析可能会给出 CDN 服务器的 IP 地址,这样你拿到的就会是 CDN 服务器而不是目标网站的实际地址。 + +因为 CDN 会缓存网站的大部分资源,比如图片、CSS 样式表,所以有的 HTTP 请求就不需要再发到 Apple,CDN 就可以直接响应你的请求,把数据发给你。 + +由 PHP、Java 等后台服务动态生成的页面属于“动态资源”,CDN 无法缓存,只能从目标网站获取。于是你发出的 HTTP 请求就要开始在互联网上的“漫长跋涉”,经过无数的路由器、网关、代理,最后到达目的地。 + +目标网站的服务器对外表现的是一个 IP 地址,但为了能够扛住高并发,在内部也是一套复杂的架构。通常在入口是负载均衡设备,例如四层的 LVS 或者七层的 Nginx,在后面是许多的服务器,构成一个更强更稳定的集群。 + +负载均衡设备会先访问系统里的缓存服务器,通常有 memory 级缓存 Redis 和 disk 级缓存 Varnish,它们的作用与 CDN 类似,不过是工作在内部网络里,把最频繁访问的数据缓存几秒钟或几分钟,减轻后端应用服务器的压力。 + +如果缓存服务器里也没有,那么负载均衡设备就要把请求转发给应用服务器了。这里就是各种开发框架大显神通的地方了,例如 Java 的 Tomcat/Netty/Jetty,Python 的 Django,还有 PHP、Node.js、Golang 等等。它们又会再访问后面的 MySQL、PostgreSQL、MongoDB 等数据库服务,实现用户登录、商品查询、购物下单、扣款支付等业务操作,然后把执行的结果返回给负载均衡设备,同时也可能给缓存服务器里也放一份。 + +应用服务器的输出到了负载均衡设备这里,请求的处理就算是完成了,就要按照原路再走回去,还是要经过许多的路由器、网关、代理。如果这个资源允许缓存,那么经过 CDN 的时候它也会做缓存,这样下次同样的请求就不会到达源站了。 + +最后网站的响应数据回到了你的设备,它可能是 HTML、JSON、图片或者其他格式的数据,需要由浏览器解析处理才能显示出来,如果数据里面还有超链接,指向别的资源,那么就又要重走一遍整个流程,直到所有的资源都下载完。 + +小结 + +今天我们在本机的环境里做了两个简单的实验,学习了 HTTP 协议请求 - 应答的全过程,在这里做一个小结。 + + +HTTP 协议基于底层的 TCP/IP 协议,所以必须要用 IP 地址建立连接; +如果不知道 IP 地址,就要用 DNS 协议去解析得到 IP 地址,否则就会连接失败; +建立 TCP 连接后会顺序收发数据,请求方和应答方都必须依据 HTTP 规范构建和解析报文; +为了减少响应时间,整个过程中的每一个环节都会有缓存,能够实现“短路”操作; +虽然现实中的 HTTP 传输过程非常复杂,但理论上仍然可以简化成实验里的“两点”模型。 + + +课下作业 + + +你能试着解释一下在浏览器里点击页面链接后发生了哪些事情吗? +这一节课里讲的都是正常的请求处理流程,如果是一个不存在的域名,那么浏览器的工作流程会是怎么样的呢? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/09HTTP报文是什么样子的?.md b/专栏/透视HTTP协议/09HTTP报文是什么样子的?.md new file mode 100644 index 0000000..21e84a0 --- /dev/null +++ b/专栏/透视HTTP协议/09HTTP报文是什么样子的?.md @@ -0,0 +1,227 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 HTTP报文是什么样子的? + 在上一讲里,我们在本机的最小化环境了做了两个 HTTP 协议的实验,使用 Wireshark 抓包,弄清楚了 HTTP 协议基本工作流程,也就是“请求 - 应答”“一发一收”的模式。 + +可以看到,HTTP 的工作模式是非常简单的,由于 TCP/IP 协议负责底层的具体传输工作,HTTP 协议基本上不用在这方面操心太多。单从这一点上来看,所谓的“超文本传输协议”其实并不怎么管“传输”的事情,有点“名不副实”。 + +那么 HTTP 协议的核心部分是什么呢? + +答案就是它传输的报文内容。 + +HTTP 协议在规范文档里详细定义了报文的格式,规定了组成部分,解析规则,还有处理策略,所以可以在 TCP/IP 层之上实现更灵活丰富的功能,例如连接控制,缓存管理、数据编码、内容协商等等。 + +报文结构 + +你也许对 TCP/UDP 的报文格式有所了解,拿 TCP 报文来举例,它在实际要传输的数据之前附加了一个 20 字节的头部数据,存储 TCP 协议必须的额外信息,例如发送方的端口号、接收方的端口号、包序号、标志位等等。 + +有了这个附加的 TCP 头,数据包才能够正确传输,到了目的地后把头部去掉,就可以拿到真正的数据。 + + + +HTTP 协议也是与 TCP/UDP 类似,同样也需要在实际传输的数据前附加一些头数据,不过与 TCP/UDP 不同的是,它是一个“纯文本”的协议,所以头数据都是 ASCII 码的文本,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂。 + +HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成: + + +起始行(start line):描述请求或响应的基本信息; +头部字段集合(header):使用 key-value 形式更详细地说明报文; +消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。 + + +这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”。 + +HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”。 + +所以,一个完整的 HTTP 报文就像是下图的这个样子,注意在 header 和 body 之间有一个“空行”。 + + + +说到这里,我不由得想起了一部老动画片《大头儿子和小头爸爸》,你看,HTTP 的报文结构像不像里面的“大头儿子”? + +报文里的 header 就是“大头儿子”的“大头”,空行就是他的“脖子”,而后面的 body 部分就是他的身体了。 + +看一下我们之前用 Wireshark 抓的包吧。 + + + +在这个浏览器发出的请求报文里,第一行“GET / HTTP/1.1”就是请求行,而后面的“Host”“Connection”等等都属于 header,报文的最后是一个空白行结束,没有 body。 + +在很多时候,特别是浏览器发送 GET 请求的时候都是这样,HTTP 报文经常是只有 header 而没 body,相当于只发了一个超级“大头”过来,你可以想象的出来:每时每刻网络上都会有数不清的“大头儿子”在跑来跑去。 + +不过这个“大头”也不能太大,虽然 HTTP 协议对 header 的大小没有做限制,但各个 Web 服务器都不允许过大的请求头,因为头部太大可能会占用大量的服务器资源,影响运行效率。 + +请求行 + +了解了 HTTP 报文的基本结构后,我们来看看请求报文里的起始行也就是请求行(request line),它简要地描述了客户端想要如何操作服务器端的资源。 + +请求行由三部分构成: + + +请求方法:是一个动词,如 GET/POST,表示对资源的操作; +请求目标:通常是一个 URI,标记了请求方法要操作的资源; +版本号:表示报文使用的 HTTP 协议版本。 + + +这三个部分通常使用空格(space)来分隔,最后要用 CRLF 换行表示结束。 + + + +还是用 Wireshark 抓包的数据来举例: + +GET / HTTP/1.1 + + +在这个请求行里,“GET”是请求方法,“/”是请求目标,“HTTP/1.1”是版本号,把这三部分连起来,意思就是“服务器你好,我想获取网站根目录下的默认文件,我用的协议版本号是 1.1,请不要用 1.0 或者 2.0 回复我。” + +别看请求行就一行,貌似很简单,其实这里面的“讲究”是非常多的,尤其是前面的请求方法和请求目标,组合起来变化多端,后面我还会详细介绍。 + +状态行 + +看完了请求行,我们再看响应报文里的起始行,在这里它不叫“响应行”,而是叫“状态行”(status line),意思是服务器响应的状态。 + +比起请求行来说,状态行要简单一些,同样也是由三部分构成: + + +版本号:表示报文使用的 HTTP 协议版本; +状态码:一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误; +原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。 + + + + +看一下上一讲里 Wireshark 抓包里的响应报文,状态行是: + +HTTP/1.1 200 OK + + +意思就是:“浏览器你好,我已经处理完了你的请求,这个报文使用的协议版本号是 1.1,状态码是 200,一切 OK。” + +而另一个“GET /favicon.ico HTTP/1.1”的响应报文状态行是: + +HTTP/1.1 404 Not Found + + +翻译成人话就是:“抱歉啊浏览器,刚才你的请求收到了,但我没找到你要的资源,错误代码是 404,接下来的事情你就看着办吧。” + +头部字段 + +请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头,我画了两个示意图,你可以看一下。 + + + + + +请求头和响应头的结构是基本一样的,唯一的区别是起始行,所以我把请求头和响应头里的字段放在一起介绍。 + +头部字段是 key-value 的形式,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字段结束。比如在“Host: 127.0.0.1”这一行里 key 就是“Host”,value 就是“127.0.0.1”。 + +HTTP 头字段非常灵活,不仅可以使用标准里的 Host、Connection 等已有头,也可以任意添加自定义头,这就给 HTTP 协议带来了无限的扩展可能。 + +不过使用头字段需要注意下面几点: + + +字段名不区分大小写,例如“Host”也可以写成“host”,但首字母大写的可读性更好; +字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线“_”。例如,“test-name”是合法的字段名,而“test name”“test_name”是不正确的字段名; +字段名后面必须紧接着“:”,不能有空格,而“:”后的字段值前可以有多个空格; +字段的顺序是没有意义的,可以任意排列不影响语义; +字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。 + + +我在实验环境里用 Lua 编写了一个小服务程序,URI 是“/09-1”,效果是输出所有的请求头。 + +你可以在实验环境里用 Telnet 连接 OpenResty 服务器试一下,手动发送 HTTP 请求头,试验各种正确和错误的情况。 + +先启动 OpenResty 服务器,然后用组合键“Win+R”运行 telnet,输入命令“open www.chrono.com 80”,就连上了 Web 服务器。 + + + +连接上之后按组合键“CTRL+]”,然后按回车键,就进入了编辑模式。在这个界面里,你可以直接用鼠标右键粘贴文本,敲两下回车后就会发送数据,也就是模拟了一次 HTTP 请求。 + +下面是两个最简单的 HTTP 请求,第一个在“:”后有多个空格,第二个在“:”前有空格。 + +GET /09-1 HTTP/1.1 +Host: www.chrono.com + + +GET /09-1 HTTP/1.1 +Host : www.chrono.com + + +第一个可以正确获取服务器的响应报文,而第二个得到的会是一个“400 Bad Request”,表示请求报文格式有误,服务器无法正确处理: + +HTTP/1.1 400 Bad Request +Server: openresty/1.15.8.1 +Connection: close + + +常用头字段 + +HTTP 协议规定了非常多的头部字段,实现各种各样的功能,但基本上可以分为四大类: + + +通用字段:在请求头和响应头里都可以出现; +请求字段:仅能出现在请求头里,进一步说明请求信息或者额外的附加条件; +响应字段:仅能出现在响应头里,补充说明响应报文的信息; +实体字段:它实际上属于通用字段,但专门描述 body 的额外信息。 + + +对 HTTP 报文的解析和处理实际上主要就是对头字段的处理,理解了头字段也就理解了 HTTP 报文。 + +后续的课程中我将会以应用领域为切入点介绍连接管理、缓存控制等头字段,今天先讲几个最基本的头,看完了它们你就应该能够读懂大多数 HTTP 报文了。 + +首先要说的是Host字段,它属于请求字段,只能出现在请求头里,它同时也是唯一一个 HTTP/1.1 规范里要求必须出现的字段,也就是说,如果请求头里没有 Host,那这就是一个错误的报文。 + +Host 字段告诉服务器这个请求应该由哪个主机来处理,当一台计算机上托管了多个虚拟主机的时候,服务器端就需要用 Host 字段来选择,有点像是一个简单的“路由重定向”。 + +例如我们的试验环境,在 127.0.0.1 上有三个虚拟主机:“www.chrono.com”“www.metroid.net”和“origin.io”。那么当使用域名的方式访问时,就必须要用 Host 字段来区分这三个 IP 相同但域名不同的网站,否则服务器就会找不到合适的虚拟主机,无法处理。 + +User-Agent是请求字段,只出现在请求头里。它使用一个字符串来描述发起 HTTP 请求的客户端,服务器可以依据它来返回最合适此浏览器显示的页面。 + +但由于历史的原因,User-Agent 非常混乱,每个浏览器都自称是“Mozilla”“Chrome”“Safari”,企图使用这个字段来互相“伪装”,导致 User-Agent 变得越来越长,最终变得毫无意义。 + +不过有的比较“诚实”的爬虫会在 User-Agent 里用“spider”标明自己是爬虫,所以可以利用这个字段实现简单的反爬虫策略。 + +Date字段是一个通用字段,但通常出现在响应头里,表示 HTTP 报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略。 + +Server字段是响应字段,只能出现在响应头里。它告诉客户端当前正在提供 Web 服务的软件名称和版本号,例如在我们的实验环境里它就是“Server: openresty/1.15.8.1”,即使用的是 OpenResty 1.15.8.1。 + +Server 字段也不是必须要出现的,因为这会把服务器的一部分信息暴露给外界,如果这个版本恰好存在 bug,那么黑客就有可能利用 bug 攻陷服务器。所以,有的网站响应头里要么没有这个字段,要么就给出一个完全无关的描述信息。 + +比如 GitHub,它的 Server 字段里就看不出是使用了 Apache 还是 Nginx,只是显示为“GitHub.com”。 + + + +实体字段里要说的一个是Content-Length,它表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度。服务器看到这个字段,就知道了后续有多少数据,可以直接接收。如果没有这个字段,那么 body 就是不定长的,需要使用 chunked 方式分段传输。 + +小结 + +今天我们学习了 HTTP 的报文结构,下面做一个简单小结。 + + +HTTP 报文结构就像是“大头儿子”,由“起始行 + 头部 + 空行 + 实体”组成,简单地说就是“header+body”; +HTTP 报文可以没有 body,但必须要有 header,而且 header 后也必须要有空行,形象地说就是“大头”必须要带着“脖子”; +请求头由“请求行 + 头部字段”构成,响应头由“状态行 + 头部字段”构成; +请求行有三部分:请求方法,请求目标和版本号; +状态行也有三部分:版本号,状态码和原因字符串; +头部字段是 key-value 的形式,用“:”分隔,不区分大小写,顺序任意,除了规定的标准头,也可以任意添加自定义字段,实现功能扩展; +HTTP/1.1 里唯一要求必须提供的头字段是 Host,它必须出现在请求头里,标记虚拟主机名。 + + +课下作业 + + +如果拼 HTTP 报文的时候,在头字段后多加了一个 CRLF,导致出现了一个空行,会发生什么? +讲头字段时说“:”后的空格可以有多个,那为什么绝大多数情况下都只使用一个空格呢? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/10应该如何理解请求方法?.md b/专栏/透视HTTP协议/10应该如何理解请求方法?.md new file mode 100644 index 0000000..0892c2a --- /dev/null +++ b/专栏/透视HTTP协议/10应该如何理解请求方法?.md @@ -0,0 +1,171 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 应该如何理解请求方法? + 上一讲我介绍了 HTTP 的报文结构,它是由 header+body 构成,请求头里有请求方法和请求目标,响应头里有状态码和原因短语,今天要说的就是请求头里的请求方法。 + +标准请求方法 + +HTTP 协议里为什么要有“请求方法”这个东西呢? + +这就要从 HTTP 协议设计时的定位说起了。还记得吗?蒂姆·伯纳斯 - 李最初设想的是要用 HTTP 协议构建一个超链接文档系统,使用 URI 来定位这些文档,也就是资源。那么,该怎么在协议里操作这些资源呢? + +很显然,需要有某种“动作的指示”,告诉操作这些资源的方式。所以,就这么出现了“请求方法”。它的实际含义就是客户端发出了一个“动作指令”,要求服务器端对 URI 定位的资源执行这个动作。 + +目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式,我先简单地列把它们列出来,后面再详细讲解。 + + +GET:获取资源,可以理解为读取或者下载数据; +HEAD:获取资源的元信息; +POST:向资源提交数据,相当于写入或上传数据; +PUT:类似 POST; +DELETE:删除资源; +CONNECT:建立特殊的连接隧道; +OPTIONS:列出可对资源实行的方法; +TRACE:追踪请求 - 响应的传输路径。 + + + + +看看这些方法,是不是有点像对文件或数据库的“增删改查”操作,只不过这些动作操作的目标不是本地资源,而是远程服务器上的资源,所以只能由客户端“请求”或者“指示”服务器来完成。 + +既然请求方法是一个“指示”,那么客户端自然就没有决定权,服务器掌控着所有资源,也就有绝对的决策权力。它收到 HTTP 请求报文后,看到里面的请求方法,可以执行也可以拒绝,或者改变动作的含义,毕竟 HTTP 是一个“协议”,两边都要“商量着来”。 + +比如,你发起了一个 GET 请求,想获取“/orders”这个文件,但这个文件保密级别比较高,不是谁都能看的,服务器就可以有如下的几种响应方式: + + +假装这个文件不存在,直接返回一个 404 Not found 报文; +稍微友好一点,明确告诉你有这个文件,但不允许访问,返回一个 403 Forbidden; +再宽松一些,返回 405 Method Not Allowed,然后用 Allow 头告诉你可以用 HEAD 方法获取文件的元信息。 + + +GET/HEAD + +虽然 HTTP/1.1 里规定了八种请求方法,但只有前四个是比较常用的,所以我们先来看一下这四个方法。 + +GET方法应该是 HTTP 协议里最知名的请求方法了,也应该是用的最多的,自 0.9 版出现并一直被保留至今,是名副其实的“元老”。 + +它的含义是请求从服务器获取资源,这个资源既可以是静态的文本、页面、图片、视频,也可以是由 PHP、Java 动态生成的页面或者其他格式的数据。 + +GET 方法虽然基本动作比较简单,但搭配 URI 和其他头字段就能实现对资源更精细的操作。 + +例如,在 URI 后使用“#”,就可以在获取页面后直接定位到某个标签所在的位置;使用 If-Modified-Since 字段就变成了“有条件的请求”,仅当资源被修改时才会执行获取动作;使用 Range 字段就是“范围请求”,只获取资源的一部分数据。 + +HEAD方法与 GET 方法类似,也是请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。 + +HEAD 方法可以看做是 GET 方法的一个“简化版”或者“轻量版”。因为它的响应头与 GET 完全相同,所以可以用在很多并不真正需要资源的场合,避免传输 body 数据的浪费。 + +比如,想要检查一个文件是否存在,只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。再比如,要检查文件是否有最新版本,同样也应该用 HEAD,服务器会在响应头里把文件的修改时间传回来。 + +你可以在实验环境里试一下这两个方法,运行 Telnet,分别向 URI“/10-1”发送 GET 和 HEAD 请求,观察一下响应头是否一致。 + +GET /10-1 HTTP/1.1 +Host: www.chrono.com + + +HEAD /10-1 HTTP/1.1 +Host: www.chrono.com + + +POST/PUT + +接下来要说的是POST和PUT方法,这两个方法也很像。 + +GET 和 HEAD 方法是从服务器获取数据,而 POST 和 PUT 方法则是相反操作,向 URI 指定的资源提交数据,数据就放在报文的 body 里。 + +POST 也是一个经常用到的请求方法,使用频率应该是仅次于 GET,应用的场景也非常多,只要向服务器发送数据,用的大多数都是 POST。 + +比如,你上论坛灌水,敲了一堆字后点击“发帖”按钮,浏览器就执行了一次 POST 请求,把你的文字放进报文的 body 里,然后拼好 POST 请求头,通过 TCP 协议发给服务器。 + +又比如,你上购物网站,看到了一件心仪的商品,点击“加入购物车”,这时也会有 POST 请求,浏览器会把商品 ID 发给服务器,服务器再把 ID 写入你的购物车相关的数据库记录。 + +PUT 的作用与 POST 类似,也可以向服务器提交数据,但与 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义。 + +在实际应用中,PUT 用到的比较少。而且,因为它与 POST 的语义、功能太过近似,有的服务器甚至就直接禁止使用 PUT 方法,只用 POST 方法上传数据。 + +实验环境的“/10-2”模拟了 POST 和 PUT 方法的处理过程,你仍然可以用 Telnet 发送测试请求,看看运行的效果。注意,在发送请求时,头字段“Content-Length”一定要写对,是空行后 body 的长度: + +POST /10-2 HTTP/1.1 +Host: www.chrono.com +Content-Length: 17 + +POST DATA IS HERE + +PUT /10-2 HTTP/1.1 +Host: www.chrono.com +Content-Length: 16 + +PUT DATA IS HE + + +其他方法 + +讲完了 GET/HEAD/POST/PUT,还剩下四个标准请求方法,它们属于比较“冷僻”的方法,应用的不是很多。 + +DELETE方法指示服务器删除资源,因为这个动作危险性太大,所以通常服务器不会执行真正的删除操作,而是对资源做一个删除标记。当然,更多的时候服务器就直接不处理 DELETE 请求。 + +CONNECT是一个比较特殊的方法,要求服务器为客户端和另一台远程服务器建立一条特殊的连接隧道,这时 Web 服务器在中间充当了代理的角色。 + +OPTIONS方法要求服务器列出可对资源实行的操作方法,在响应头的 Allow 字段里返回。它的功能很有限,用处也不大,有的服务器(例如 Nginx)干脆就没有实现对它的支持。 + +TRACE方法多用于对 HTTP 链路的测试或诊断,可以显示出请求 - 响应的传输路径。它的本意是好的,但存在漏洞,会泄漏网站的信息,所以 Web 服务器通常也是禁止使用。 + +扩展方法 + +虽然 HTTP/1.1 里规定了八种请求方法,但它并没有限制我们只能用这八种方法,这也体现了 HTTP 协议良好的扩展性,我们可以任意添加请求动作,只要请求方和响应方都能理解就行。 + +例如著名的愚人节玩笑 RFC2324,它定义了协议 HTCPCP,即“超文本咖啡壶控制协议”,为 HTTP 协议增加了用来煮咖啡的 BREW 方法,要求添牛奶的 WHEN 方法。 + +此外,还有一些得到了实际应用的请求方法(WebDAV),例如 MKCOL、COPY、MOVE、LOCK、UNLOCK、PATCH 等。如果有合适的场景,你也可以把它们应用到自己的系统里,比如用 LOCK 方法锁定资源暂时不允许修改,或者使用 PATCH 方法给资源打个小补丁,部分更新数据。但因为这些方法是非标准的,所以需要为客户端和服务器编写额外的代码才能添加支持。 + +当然了,你也完全可以根据实际需求,自己发明新的方法,比如“PULL”拉取某些资源到本地,“PURGE”清理某个目录下的所有缓存数据。 + +安全与幂等 + +关于请求方法还有两个面试时有可能会问到、比较重要的概念:安全与幂等。 + +在 HTTP 协议里,所谓的“安全”是指请求方法不会“破坏”服务器上的资源,即不会对服务器上的资源造成实质的修改。 + +按照这个定义,只有 GET 和 HEAD 方法是“安全”的,因为它们是“只读”操作,只要服务器不故意曲解请求方法的处理方式,无论 GET 和 HEAD 操作多少次,服务器上的数据都是“安全的”。 + +而 POST/PUT/DELETE 操作会修改服务器上的资源,增加或删除数据,所以是“不安全”的。 + +所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP 协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。 + +很显然,GET 和 HEAD 既是安全的也是幂等的,DELETE 可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等的。 + +POST 和 PUT 的幂等性质就略费解一点。 + +按照 RFC 里的语义,POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的;而 PUT 是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的。 + +我对你的建议是,你可以对比一下 SQL 来加深理解:把 POST 理解成 INSERT,把 PUT 理解成 UPDATE,这样就很清楚了。多次 INSERT 会添加多条记录,而多次 UPDATE 只操作一条记录,而且效果相同。 + +小结 + +今天我们学习了 HTTP 报文里请求方法相关的知识,简单小结一下。 + + +请求方法是客户端发出的、要求服务器执行的、对资源的一种操作; +请求方法是对服务器的“指示”,真正应如何处理由服务器来决定; +最常用的请求方法是 GET 和 POST,分别是获取数据和发送数据; +HEAD 方法是轻量级的 GET,用来获取资源的元信息; +PUT 基本上是 POST 的同义词,多用于更新数据; +“安全”与“幂等”是描述请求方法的两个重要属性,具有理论指导意义,可以帮助我们设计系统。 + + +课下作业 + + +你能把 GET/POST 等请求方法对应到数据库的“增删改查”操作吗?请求头应该如何设计呢? +你觉得 TRACE/OPTIONS/CONNECT 方法能够用 GET 或 POST 间接实现吗? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/11你能写出正确的网址吗?.md b/专栏/透视HTTP协议/11你能写出正确的网址吗?.md new file mode 100644 index 0000000..2317af7 --- /dev/null +++ b/专栏/透视HTTP协议/11你能写出正确的网址吗?.md @@ -0,0 +1,194 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 你能写出正确的网址吗? + 上一讲里我们一起学习了 HTTP 协议里的请求方法,其中最常用的一个是 GET,它用来从服务器上某个资源获取数据,另一个是 POST,向某个资源提交数据。 + +那么,应该用什么来标记服务器上的资源呢?怎么区分“这个”资源和“那个”资源呢? + +经过前几讲的学习,你一定已经知道了,用的是 URI,也就是统一资源标识符(Uniform Resource Identifier)。因为它经常出现在浏览器的地址栏里,所以俗称为“网络地址”,简称“网址”。 + +严格地说,URI 不完全等同于网址,它包含有 URL 和 URN 两个部分,在 HTTP 世界里用的网址实际上是 URL——统一资源定位符(Uniform Resource Locator)。但因为 URL 实在是太普及了,所以常常把这两者简单地视为相等。 + +不仅我们生活中的上网要用到 URI,平常的开发、测试、运维的工作中也少不了它。 + +如果你在客户端做 iOS、 Android 或者某某小程序开发,免不了要连接远程服务,就会调用底层 API 用 URI 访问服务。 + +如果你使用 Java、PHP 做后台 Web 开发,也会调用 getPath()、parse_url() 等函数来处理 URI,解析里面的各个要素。 + +在测试、运维配置 Apache、Nginx 等 Web 服务器的时候也必须正确理解 URI,分离静态资源与动态资源,或者设置规则实现网页的重定向跳转。 + +总之一句话,URI 非常重要,要搞懂 HTTP 甚至网络应用,就必须搞懂 URI。 + +URI 的格式 + +不知道你平常上网的时候有没有关注过地址栏里的那一长串字符,有的比较简短,有的则一行都显示不下,有的意思大概能看明白,而有的则带着各种怪字符,有如“天书”。 + +其实只要你弄清楚了 URI 的格式,就能够轻易地“破解”这些难懂的“天书”了。 + +URI 本质上是一个字符串,这个字符串的作用是唯一地标记资源的位置或者名字。 + +这里我要提醒你注意,它不仅能够标记万维网的资源,也可以标记其他的,如邮件系统、本地文件系统等任意资源。而“资源”既可以是存在磁盘上的静态文本、页面数据,也可以是由 Java、PHP 提供的动态服务。 + +下面的这张图显示了 URI 最常用的形式,由 scheme、host:port、path 和 query 四个部分组成,但有的部分可以视情况省略。 + + + +URI 的基本组成 + +URI 第一个组成部分叫scheme,翻译成中文叫“方案名”或者“协议名”,表示资源应该使用哪种协议来访问。 + +最常见的当然就是“http”了,表示使用 HTTP 协议。另外还有“https”,表示使用经过加密、安全的 HTTPS 协议。此外还有其他不是很常见的 scheme,例如 ftp、ldap、file、news 等。 + +浏览器或者你的应用程序看到 URI 里的 scheme,就知道下一步该怎么走了,会调用相应的 HTTP 或者 HTTPS 下层 API。显然,如果一个 URI 没有提供 scheme,即使后面的地址再完善,也是无法处理的。 + +在 scheme 之后,必须是三个特定的字符“://”,它把 scheme 和后面的部分分离开。 + +实话实说,这个设计非常的怪异,我最早上网的时候看见地址栏里的“://”就觉得很别扭,直到现在也还是没有太适应。URI 的创造者蒂姆·伯纳斯 - 李也曾经私下承认“://”并非必要,当初有些“过于草率”了。 + +不过这个设计已经有了三十年的历史,不管我们愿意不愿意,只能接受。 + +在“://”之后,是被称为“authority”的部分,表示资源所在的主机名,通常的形式是“host:port”,即主机名加端口号。 + +主机名可以是 IP 地址或者域名的形式,必须要有,否则浏览器就会找不到服务器。但端口号有时可以省略,浏览器等客户端会依据 scheme 使用默认的端口号,例如 HTTP 的默认端口号是 80,HTTPS 的默认端口号是 443。 + +有了协议名和主机地址、端口号,再加上后面标记资源所在位置的path,浏览器就可以连接服务器访问资源了。 + +URI 里 path 采用了类似文件系统“目录”“路径”的表示方式,因为早期互联网上的计算机多是 UNIX 系统,所以采用了 UNIX 的“/”风格。其实也比较好理解,它与 scheme 后面的“://”是一致的。 + +这里我也要再次提醒你注意,URI 的 path 部分必须以“/”开始,也就是必须包含“/”,不要把“/”误认为属于前面 authority。 + +说了这么多“理论”,来看几个实例。 + +http://nginx.org +http://www.chrono.com:8080/11-1 +https://tools.ietf.org/html/rfc7230 +file:///D:/http_study/www/ + + +第一个 URI 算是最简单的了,协议名是“http”,主机名是“nginx.org”,端口号省略,所以是默认的 80,而路径部分也被省略了,默认就是一个“/”,表示根目录。 + +第二个 URI 是在实验环境里这次课程的专用 URI,主机名是“www.chrono.com”,端口号是 8080,后面的路径是“/11-1”。 + +第三个是 HTTP 协议标准文档 RFC7230 的 URI,主机名是“tools.ietf.org”,路径是“/html/rfc7230”。 + +最后一个 URI 要注意了,它的协议名不是“http”,而是“file”,表示这是本地文件,而后面居然有三个斜杠,这是怎么回事? + +如果你刚才仔细听了 scheme 的介绍就能明白,这三个斜杠里的前两个属于 URI 特殊分隔符“://”,然后后面的“/D:/http_study/www/”是路径,而中间的主机名被“省略”了。这实际上是 file 类型 URI 的“特例”,它允许省略主机名,默认是本机 localhost。 + +但对于 HTTP 或 HTTPS 这样的网络通信协议,主机名是绝对不能省略的。原因之前也说了,会导致浏览器无法找到服务器。 + +我们可以在实验环境里用 Chrome 浏览器再仔细观察一下 HTTP 报文里的 URI。 + +运行 Chrome,用 F12 打开开发者工具,然后在地址栏里输入“http://www.chrono.com/11-1”,得到的结果如下图。 + + + +在开发者工具里依次选“Network”“Doc”,就可以找到请求的 URI。然后在 Headers 页里看 Request Headers,用“view source”就可以看到浏览器发的原始请求头了。 + +发现了什么特别的没有? + +在 HTTP 报文里的 URI“/11-1”与浏览器里输入的“http://www.chrono.com/11-1”有很大的不同,协议名和主机名都不见了,只剩下了后面的部分。 + +这是因为协议名和主机名已经分别出现在了请求行的版本号和请求头的 Host 字段里,没有必要再重复。当然,在请求行里使用完整的 URI 也是可以的,你可以在课后自己试一下。 + +通过这个小实验,我们还得到了一个结论:客户端和服务器看到的 URI 是不一样的。客户端看到的必须是完整的 URI,使用特定的协议去连接特定的主机,而服务器看到的只是报文请求行里被删除了协议名和主机名的 URI。 + +如果你配置过 Nginx,你就应该明白了,Nginx 作为一个 Web 服务器,它的 location、rewrite 等指令操作的 URI 其实指的是真正 URI 里的 path 和后续的部分。 + +URI 的查询参数 + +使用“协议名 + 主机名 + 路径”的方式,已经可以精确定位网络上的任何资源了。但这还不够,很多时候我们还想在操作资源的时候附加一些额外的修饰参数。 + +举几个例子:获取商品图片,但想要一个 32×32 的缩略图版本;获取商品列表,但要按某种规则做分页和排序;跳转页面,但想要标记跳转前的原始页面。 + +仅用“协议名 + 主机名 + 路径”的方式是无法适应这些场景的,所以 URI 后面还有一个“query”部分,它在 path 之后,用一个“?”开始,但不包含“?”,表示对资源附加的额外要求。这是个很形象的符号,比“://”要好的多,很明显地表示了“查询”的含义。 + +查询参数 query 有一套自己的格式,是多个“key=value”的字符串,这些 KV 值用字符“&”连接,浏览器和客户端都可以按照这个格式把长串的查询参数解析成可理解的字典或关联数组形式。 + +你可以在实验环境里用 Chrome 试试下面这个加了 query 参数的 URI: + +http://www.chrono.com:8080/11-1?uid=1234&name=mario&referer=xxx + + +Chrome 的开发者工具也能解码出 query 里的 KV 对,省得我们“人肉”分解。 + + + +还可以再拿一个实际的 URI 来看一下,这个 URI 是某电商网站的一个商品查询 URI,比较复杂,但相信现在的你能够毫不费力地区分出里面的协议名、主机名、路径和查询参数。 + +https://search.jd.com/Search?keyword=openresty&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=openresty&psort=3&click=0 + + +你也可以把这个 URI 输入到 Chrome 的地址栏里,再用开发者工具仔细检查它的组成部分。 + +URI 的完整格式 + +讲完了 query 参数,URI 就算完整了,HTTP 协议里用到的 URI 绝大多数都是这种形式。 + +不过必须要说的是,URI 还有一个“真正”的完整形态,如下图所示。 + + + +这个“真正”形态比基本形态多了两部分。 + +第一个多出的部分是协议名之后、主机名之前的身份信息“user:passwd@”,表示登录主机时的用户名和密码,但现在已经不推荐使用这种形式了(RFC7230),因为它把敏感信息以明文形式暴露出来,存在严重的安全隐患。 + +第二个多出的部分是查询参数后的片段标识符“#fragment”,它是 URI 所定位的资源内部的一个“锚点”或者说是“标签”,浏览器可以在获取资源后直接跳转到它指示的位置。 + +但片段标识符仅能由浏览器这样的客户端使用,服务器是看不到的。也就是说,浏览器永远不会把带“#fragment”的 URI 发送给服务器,服务器也永远不会用这种方式去处理资源的片段。 + +URI 的编码 + +刚才我们看到了,在 URI 里只能使用 ASCII 码,但如果要在 URI 里使用英语以外的汉语、日语等其他语言该怎么办呢? + +还有,某些特殊的 URI,会在 path、query 里出现“@&?“等起界定符作用的字符,会导致 URI 解析错误,这时又该怎么办呢? + +所以,URI 引入了编码机制,对于 ASCII 码以外的字符集和特殊字符做一个特殊的操作,把它们转换成与 URI 语义不冲突的形式。这在 RFC 规范里称为“escape”和“unescape”,俗称“转义”。 + +URI 转义的规则有点“简单粗暴”,直接把非 ASCII 码或特殊字符转换成十六进制字节值,然后前面再加上一个“%”。 + +例如,空格被转义成“%20”,“?”被转义成“%3F”。而中文、日文等则通常使用 UTF-8 编码后再转义,例如“银河”会被转义成“%E9%93%B6%E6%B2%B3”。 + +有了这个编码规则后,URI 就更加完美了,可以支持任意的字符集用任何语言来标记资源。 + +不过我们在浏览器的地址栏里通常是不会看到这些转义后的“乱码”的,这实际上是浏览器一种“友好”表现,隐藏了 URI 编码后的“丑陋一面”,不信你可以试试下面的这个 URI。 + +http://www.chrono.com:8080/11-1? 夸父逐日 + + +先在 Chrome 的地址栏里输入这个 query 里含有中文的 URI,然后点击地址栏,把它再拷贝到其他的编辑器里,它就会“现出原形”: + +http://www.chrono.com:8080/11-1?%E5%A4%B8%E7%88%B6%E9%80%90%E6%97%A5 + + +小结 + +今天我们学习了网址也就是 URI 的知识,在这里小结一下今天的内容。 + + +URI 是用来唯一标记服务器上资源的一个字符串,通常也称为 URL; +URI 通常由 scheme、host:port、path 和 query 四个部分组成,有的可以省略; +scheme 叫“方案名”或者“协议名”,表示资源应该使用哪种协议来访问; +“host:port”表示资源所在的主机名和端口号; +path 标记资源所在的位置; +query 表示对资源附加的额外要求; +在 URI 里对“@&/”等特殊字符和汉字必须要做编码,否则服务器收到 HTTP 报文后会无法正确处理。 + + +课下作业 + + +HTTP 协议允许在在请求行里使用完整的 URI,但为什么浏览器没有这么做呢? +URI 的查询参数和头字段很相似,都是 key-value 形式,都可以任意自定义,那么它们在使用时该如何区别呢? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/12响应状态码该怎么用?.md b/专栏/透视HTTP协议/12响应状态码该怎么用?.md new file mode 100644 index 0000000..6973218 --- /dev/null +++ b/专栏/透视HTTP协议/12响应状态码该怎么用?.md @@ -0,0 +1,155 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 响应状态码该怎么用? + 前两讲中,我们学习了 HTTP 报文里请求行的组成部分,包括请求方法和 URI。有了请求行,加上后面的头字段就形成了请求头,可以通过 TCP/IP 协议发送给服务器。 + +服务器收到请求报文,解析后需要进行处理,具体的业务逻辑多种多样,但最后必定是拼出一个响应报文发回客户端。 + +响应报文由响应头加响应体数据组成,响应头又由状态行和头字段构成。 + +我们先来复习一下状态行的结构,有三部分: + + + +开头的 Version 部分是 HTTP 协议的版本号,通常是 HTTP/1.1,用处不是很大。 + +后面的 Reason 部分是原因短语,是状态码的简短文字描述,例如“OK”“Not Found”等等,也可以自定义。但它只是为了兼容早期的文本客户端而存在,提供的信息很有限,目前的大多数客户端都会忽略它。 + +所以,状态行里有用的就只剩下中间的状态码(Status Code)了。它是一个十进制数字,以代码的形式表示服务器对请求的处理结果,就像我们通常编写程序时函数返回的错误码一样。 + +不过你要注意,它的名字是“状态码”而不是“错误码”。也就是说,它的含义不仅是错误,更重要的意义在于表达 HTTP 数据处理的“状态”,客户端可以依据代码适时转换处理状态,例如继续发送请求、切换协议,重定向跳转等,有那么点 TCP 状态转换的意思。 + +状态码 + +目前 RFC 标准里规定的状态码是三位数,所以取值范围就是从 000 到 999。但如果把代码简单地从 000 开始顺序编下去就显得有点太“low”,不灵活、不利于扩展,所以状态码也被设计成有一定的格式。 + +RFC 标准把状态码分成了五类,用数字的第一位表示分类,而 0~99 不用,这样状态码的实际可用范围就大大缩小了,由 000~999 变成了 100~599。 + +这五类的具体含义是: + + +1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作; +2××:成功,报文已经收到并被正确处理; +3××:重定向,资源位置发生变动,需要客户端重新发送请求; +4××:客户端错误,请求报文有误,服务器无法处理; +5××:服务器错误,服务器在处理请求时内部发生了错误。 + + +在 HTTP 协议中,正确地理解并应用这些状态码不是客户端或服务器单方的责任,而是双方共同的责任。 + +客户端作为请求的发起方,获取响应报文后,需要通过状态码知道请求是否被正确处理,是否要再次发送请求,如果出错了原因又是什么。这样才能进行下一步的动作,要么发送新请求,要么改正错误重发请求。 + +服务器端作为请求的接收方,也应该很好地运用状态码。在处理请求时,选择最恰当的状态码回复客户端,告知客户端处理的结果,指示客户端下一步应该如何行动。特别是在出错的时候,尽量不要简单地返 400、500 这样意思含糊不清的状态码。 + +目前 RFC 标准里总共有 41 个状态码,但状态码的定义是开放的,允许自行扩展。所以 Apache、Nginx 等 Web 服务器都定义了一些专有的状态码。如果你自己开发 Web 应用,也完全可以在不冲突的前提下定义新的代码。 + +在我们的实验环境里也可以对这些状态码做测试验证,访问 URI“/12-1”,用查询参数“code=xxx”来检查这些状态码的效果,服务器不仅会在状态行里显示状态码,还会在响应头里用自定义的“Expect-Code”字段输出这个代码。 + +例如,在 Chrome 里访问“http://www.chrono.com/12-1?code=405”的结果如下图。 + + + +接下来我就挑一些实际开发中比较有价值的状态码逐个详细介绍。 + +1×× + +1××类状态码属于提示信息,是协议处理的中间状态,实际能够用到的时候很少。 + +我们偶尔能够见到的是“101 Switching Protocols”。它的意思是客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了。 + +2×× + +2××类状态码表示服务器收到并成功处理了客户端的请求,这也是客户端最愿意看到的状态码。 + +“200 OK”是最常见的成功状态码,表示一切正常,服务器如客户端所期望的那样返回了处理结果,如果是非 HEAD 请求,通常在响应头后都会有 body 数据。 + +“204 No Content”是另一个很常见的成功状态码,它的含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的。 + +“206 Partial Content”是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,它与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分。 + +状态码 206 通常还会伴随着头字段“Content-Range”,表示响应报文里 body 数据的具体范围,供客户端确认,例如“Content-Range: bytes 0-99/2000”,意思是此次获取的是总计 2000 个字节的前 100 个字节。 + +3×× + +3××类状态码表示客户端请求的资源发生了变动,客户端必须用新的 URI 重新发送请求获取资源,也就是通常所说的“重定向”,包括著名的 301、302 跳转。 + +“301 Moved Permanently”俗称“永久重定向”,含义是此次请求的资源已经不存在了,需要改用改用新的 URI 再次访问。 + +与它类似的是“302 Found”,曾经的描述短语是“Moved Temporarily”,俗称“临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问。 + +301 和 302 都会在响应头里使用字段Location指明后续要跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。两者的根本区别在于语义,一个是“永久”,一个是“临时”,所以在场景、用法上差距很大。 + +比如,你的网站升级到了 HTTPS,原来的 HTTP 不打算用了,这就是“永久”的,所以要配置 301 跳转,把所有的 HTTP 流量都切换到 HTTPS。 + +再比如,今天夜里网站后台要系统维护,服务暂时不可用,这就属于“临时”的,可以配置成 302 跳转,把流量临时切换到一个静态通知页面,浏览器看到这个 302 就知道这只是暂时的情况,不会做缓存优化,第二天还会访问原来的地址。 + +“304 Not Modified” 是一个比较有意思的状态码,它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。 + +301、302 和 304 分别涉及了 HTTP 协议里重要的“重定向跳转”和“缓存控制”,在之后的课程中我还会细讲。 + +4×× + +4××类状态码表示客户端发送的请求报文有误,服务器无法处理,它就是真正的“错误码”含义了。 + +“400 Bad Request”是一个通用的错误码,表示请求报文有错误,但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说,只是一个笼统的错误,客户端看到 400 只会是“一头雾水”“不知所措”。所以,在开发 Web 应用时应当尽量避免给客户端返回 400,而是要用其他更有明确含义的状态码。 + +“403 Forbidden”实际上不是客户端的请求出错,而是表示服务器禁止访问资源。原因可能多种多样,例如信息敏感、法律禁止等,如果服务器友好一点,可以在 body 里详细说明拒绝请求的原因,不过现实中通常都是直接给一个“闭门羹”。 + +“404 Not Found”可能是我们最常看见也是最不愿意看到的一个状态码,它的原意是资源在本服务器上未找到,所以无法提供给客户端。但现在已经被“用滥了”,只要服务器“不高兴”就可以给出个 404,而我们也无从得知后面到底是真的未找到,还是有什么别的原因,某种程度上它比 403 还要令人讨厌。 + +4××里剩下的一些代码较明确地说明了错误的原因,都很好理解,开发中常用的有: + + +405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET; +406 Not Acceptable:资源无法满足客户端请求的条件,例如请求中文但只有英文; +408 Request Timeout:请求超时,服务器等待了过长的时间; +409 Conflict:多个请求发生了冲突,可以理解为多线程并发时的竞态; +413 Request Entity Too Large:请求报文里的 body 太大; +414 Request-URI Too Long:请求行里的 URI 太大; +429 Too Many Requests:客户端发送了太多的请求,通常是由于服务器的限连策略; +431 Request Header Fields Too Large:请求头某个字段或总体太大; + + +5×× + +5××类状态码表示客户端请求报文正确,但服务器在处理时内部发生了错误,无法返回应有的响应数据,是服务器端的“错误码”。 + +“500 Internal Server Error”与 400 类似,也是一个通用的错误码,服务器究竟发生了什么错误我们是不知道的。不过对于服务器来说这应该算是好事,通常不应该把服务器内部的详细信息,例如出错的函数调用栈告诉外界。虽然不利于调试,但能够防止黑客的窥探或者分析。 + +“501 Not Implemented”表示客户端请求的功能还不支持,这个错误码比 500 要“温和”一些,和“即将开业,敬请期待”的意思差不多,不过具体什么时候“开业”就不好说了。 + +“502 Bad Gateway”通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。 + +“503 Service Unavailable”表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。 + +503 是一个“临时”的状态,很可能过几秒钟后服务器就不那么忙了,可以继续提供服务,所以 503 响应报文里通常还会有一个“Retry-After”字段,指示客户端可以在多久以后再次尝试发送请求。 + +小结 + + +状态码在响应报文里表示了服务器对请求的处理结果; +状态码后的原因短语是简单的文字描述,可以自定义; +状态码是十进制的三位数,分为五类,从 100 到 599; +2××类状态码表示成功,常用的有 200、204、206; +3××类状态码表示重定向,常用的有 301、302、304; +4××类状态码表示客户端错误,常用的有 400、403、404; +5××类状态码表示服务器错误,常用的有 500、501、502、503。 + + +课下作业 + + +你在开发 HTTP 客户端,收到了一个非标准的状态码,比如 4××、5××,应当如何应对呢? +你在开发 HTTP 服务器,处理请求时发现报文里缺了一个必需的 query 参数,应该如何告知客户端错误原因呢? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/13HTTP有哪些特点?.md b/专栏/透视HTTP协议/13HTTP有哪些特点?.md new file mode 100644 index 0000000..1fbf07d --- /dev/null +++ b/专栏/透视HTTP协议/13HTTP有哪些特点?.md @@ -0,0 +1,114 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 HTTP有哪些特点? + 通过“基础篇”前几讲的学习,你应该已经知道了 HTTP 协议的基本知识,了解它的报文结构,请求头、响应头以及内部的请求方法、URI 和状态码等细节。 + +你会不会有种疑惑:“HTTP 协议好像也挺简单的啊,凭什么它就能统治互联网这么多年呢?” + +所以接下来的这两讲,我会跟你聊聊 HTTP 协议的特点、优点和缺点。既要看到它好的一面,也要正视它不好的一面,只有全方位、多角度了解 HTTP,才能实现“扬长避短”,更好地利用 HTTP。 + +今天这节课主要说的是 HTTP 协议的特点,但不会讲它们的好坏,这些特点即有可能是优点,也有可能是缺点,你可以边听边思考。 + + + +灵活可扩展 + +首先, HTTP 协议是一个“灵活可扩展”的传输协议。 + +HTTP 协议最初诞生的时候就比较简单,本着开放的精神只规定了报文的基本格式,比如用空格分隔单词,用换行分隔字段,“header+body”等,报文里的各个组成部分都没有做严格的语法语义限制,可以由开发者任意定制。 + +所以,HTTP 协议就随着互联网的发展一同成长起来了。在这个过程中,HTTP 协议逐渐增加了请求方法、版本号、状态码、头字段等特性。而 body 也不再限于文本形式的 TXT 或 HTML,而是能够传输图片、音频视频等任意数据,这些都是源于它的“灵活可扩展”的特点。 + +而那些 RFC 文档,实际上也可以理解为是对已有扩展的“承认和标准化”,实现了“从实践中来,到实践中去”的良性循环。 + +也正是因为这个特点,HTTP 才能在三十年的历史长河中“屹立不倒”,从最初的低速实验网络发展到现在的遍布全球的高速互联网,始终保持着旺盛的生命力。 + +可靠传输 + +第二个特点, HTTP 协议是一个“可靠”的传输协议。 + +这个特点显而易见,因为 HTTP 协议是基于 TCP/IP 的,而 TCP 本身是一个“可靠”的传输协议,所以 HTTP 自然也就继承了这个特性,能够在请求方和应答方之间“可靠”地传输数据。 + +它的具体做法与 TCP/UDP 差不多,都是对实际传输的数据(entity)做了一层包装,加上一个头,然后调用 Socket API,通过 TCP/IP 协议栈发送或者接收。 + +不过我们必须正确地理解“可靠”的含义,HTTP 并不能 100% 保证数据一定能够发送到另一端,在网络繁忙、连接质量差等恶劣的环境下,也有可能收发失败。“可靠”只是向使用者提供了一个“承诺”,会在下层用多种手段“尽量”保证数据的完整送达。 + +当然,如果遇到光纤被意外挖断这样的极端情况,即使是神仙也不能发送成功。所以,“可靠”传输是指在网络基本正常的情况下数据收发必定成功,借用运维里的术语,大概就是“3 个 9”或者“4 个 9”的程度吧。 + +应用层协议 + +第三个特点,HTTP 协议是一个应用层的协议。 + +这个特点也是不言自明的,但却很重要。 + +在 TCP/IP 诞生后的几十年里,虽然出现了许多的应用层协议,但它们都仅关注很小的应用领域,局限在很少的应用场景。例如 FTP 只能传输文件、SMTP 只能发送邮件、SSH 只能远程登录等,在通用的数据传输方面“完全不能打”。 + +所以 HTTP 凭借着可携带任意头字段和实体数据的报文结构,以及连接控制、缓存代理等方便易用的特性,一出现就“技压群雄”,迅速成为了应用层里的“明星”协议。只要不太苛求性能,HTTP 几乎可以传递一切东西,满足各种需求,称得上是一个“万能”的协议。 + +套用一个网上流行的段子,HTTP 完全可以用开玩笑的口吻说:“不要误会,我不是针对 FTP,我是说在座的应用层各位,都是垃圾。” + +请求 - 应答 + +第四个特点,HTTP 协议使用的是请求 - 应答通信模式。 + +这个请求 - 应答模式是 HTTP 协议最根本的通信模型,通俗来讲就是“一发一收”“有来有去”,就像是写代码时的函数调用,只要填好请求头里的字段,“调用”后就会收到答复。 + +请求 - 应答模式也明确了 HTTP 协议里通信双方的定位,永远是请求方先发起连接和请求,是主动的,而应答方只有在收到请求后才能答复,是被动的,如果没有请求时不会有任何动作。 + +当然,请求方和应答方的角色也不是绝对的,在浏览器 - 服务器的场景里,通常服务器都是应答方,但如果将它用作代理连接后端服务器,那么它就可能同时扮演请求方和应答方的角色。 + +HTTP 的请求 - 应答模式也恰好契合了传统的 C/S(Client/Server)系统架构,请求方作为客户端、应答方作为服务器。所以,随着互联网的发展就出现了 B/S(Browser/Server)架构,用轻量级的浏览器代替笨重的客户端应用,实现零维护的“瘦”客户端,而服务器则摈弃私有通信协议转而使用 HTTP 协议。 + +此外,请求 - 应答模式也完全符合 RPC(Remote Procedure Call)的工作模式,可以把 HTTP 请求处理封装成远程函数调用,导致了 WebService、RESTful 和 gPRC 等的出现。 + +无状态 + +第五个特点,HTTP 协议是无状态的。 + +这个所谓的“状态”应该怎么理解呢? + +“状态”其实就是客户端或者服务器里保存的一些数据或者标志,记录了通信过程中的一些变化信息。 + +你一定知道,TCP 协议是有状态的,一开始处于 CLOSED 状态,连接成功后是 ESTABLISHED 状态,断开连接后是 FIN-WAIT 状态,最后又是 CLOSED 状态。 + +这些“状态”就需要 TCP 在内部用一些数据结构去维护,可以简单地想象成是个标志量,标记当前所处的状态,例如 0 是 CLOSED,2 是 ESTABLISHED 等等。 + +再来看 HTTP,那么对比一下 TCP 就看出来了,在整个协议里没有规定任何的“状态”,客户端和服务器永远是处在一种“无知”的状态。建立连接前两者互不知情,每次收发的报文也都是互相独立的,没有任何的联系。收发报文也不会对客户端或服务器产生任何影响,连接后也不会要求保存任何信息。 + +“无状态”形象地来说就是“没有记忆能力”。比如,浏览器发了一个请求,说“我是小明,请给我 A 文件。”,服务器收到报文后就会检查一下权限,看小明确实可以访问 A 文件,于是把文件发回给浏览器。接着浏览器还想要 B 文件,但服务器不会记录刚才的请求状态,不知道第二个请求和第一个请求是同一个浏览器发来的,所以浏览器必须还得重复一次自己的身份才行:“我是刚才的小明,请再给我 B 文件。” + +我们可以再对比一下 UDP 协议,不过它是无连接也无状态的,顺序发包乱序收包,数据包发出去后就不管了,收到后也不会顺序整理。而 HTTP 是有连接无状态,顺序发包顺序收包,按照收发的顺序管理报文。 + +但不要忘了 HTTP 是“灵活可扩展”的,虽然标准里没有规定“状态”,但完全能够在协议的框架里给它“打个补丁”,增加这个特性。 + +其他特点 + +除了以上的五大特点,其实 HTTP 协议还可以列出非常多的特点,例如传输的实体数据可缓存可压缩、可分段获取数据、支持身份认证、支持国际化语言等。但这些并不能算是 HTTP 的基本特点,因为这都是由第一个“灵活可扩展”的特点所衍生出来的。 + +小结 + + +HTTP 是灵活可扩展的,可以任意添加头字段实现任意功能; +HTTP 是可靠传输协议,基于 TCP/IP 协议“尽量”保证数据的送达; +HTTP 是应用层协议,比 FTP、SSH 等更通用功能更多,能够传输任意数据; +HTTP 使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求; +HTTP 本质上是无状态的,每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。 + + +课下作业 + + +就如同开头我讲的那样,你能说一下今天列出的这些 HTTP 的特点中哪些是优点,哪些是缺点吗? +不同的应用场合有不同的侧重方面,你觉得哪个特点对你来说是最重要的呢? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/14HTTP有哪些优点?又有哪些缺点?.md b/专栏/透视HTTP协议/14HTTP有哪些优点?又有哪些缺点?.md new file mode 100644 index 0000000..9c0a9b9 --- /dev/null +++ b/专栏/透视HTTP协议/14HTTP有哪些优点?又有哪些缺点?.md @@ -0,0 +1,134 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 14 HTTP有哪些优点?又有哪些缺点? + 上一讲我介绍了 HTTP 的五个基本特点,这一讲要说的则是它的优点和缺点。其实这些也应该算是 HTTP 的特点,但这一讲会更侧重于评价它们的优劣和好坏。 + +上一讲我也留了两道课下作业,不知道你有没有认真思考过,今天可以一起来看看你的答案与我的观点想法是否相符,共同探讨。 + +不过在正式开讲之前我还要提醒你一下,今天的讨论范围仅限于 HTTP/1.1,所说的优点和缺点也仅针对 HTTP/1.1。实际上,专栏后续要讲的 HTTPS 和 HTTP/2 都是对 HTTP/1.1 优点的发挥和缺点的完善。 + +简单、灵活、易于扩展 + +首先,HTTP 最重要也是最突出的优点是“简单、灵活、易于扩展”。 + +初次接触 HTTP 的人都会认为,HTTP 协议是很“简单”的,基本的报文格式就是“header+body”,头部信息也是简单的文本格式,用的也都是常见的英文单词,即使不去看 RFC 文档,只靠猜也能猜出个“八九不离十”。 + +可不要小看了“简单”这个优点,它不仅降低了学习和使用的门槛,能够让更多的人研究和开发 HTTP 应用,而且我在[第 1 讲]时就说过,“简单”蕴含了进化和扩展的可能性,所谓“少即是多”,“把简单的系统变复杂”,要比“把复杂的系统变简单”容易得多。 + +所以,在“简单”这个最基本的设计理念之下,HTTP 协议又多出了“灵活和易于扩展”的优点。 + +“灵活和易于扩展”实际上是一体的,它们互为表里、相互促进,因为“灵活”所以才会“易于扩展”,而“易于扩展”又反过来让 HTTP 更加灵活,拥有更强的表现能力。 + +HTTP 协议里的请求方法、URI、状态码、原因短语、头字段等每一个核心组成要素都没有被“写死”,允许开发者任意定制、扩充或解释,给予了浏览器和服务器最大程度的信任和自由,也正好符合了互联网“自由与平等”的精神——缺什么功能自己加个字段或者错误码什么的补上就是了。 + +“请勿跟踪”所使用的头字段 DNT(Do Not Track)就是一个很好的例子。它最早由 Mozilla 提出,用来保护用户隐私,防止网站监测追踪用户的偏好。不过可惜的是 DNT 从推出至今有差不多七八年的历史,但很多网站仍然选择“无视”DNT。虽然 DNT 基本失败了,但这也正说明 HTTP 协议是“灵活自由的”,不会受单方面势力的压制。 + +“灵活、易于扩展”的特性还表现在 HTTP 对“可靠传输”的定义上,它不限制具体的下层协议,不仅可以使用 TCP、UNIX Domain Socket,还可以使用 SSL/TLS,甚至是基于 UDP 的 QUIC,下层可以随意变化,而上层的语义则始终保持稳定。 + +应用广泛、环境成熟 + +HTTP 协议的另一大优点是“应用广泛”,软硬件环境都非常成熟。 + +随着互联网特别是移动互联网的普及,HTTP 的触角已经延伸到了世界的每一个角落:从简单的 Web 页面到复杂的 JSON、XML 数据,从台式机上的浏览器到手机上的各种 APP,从看新闻、泡论坛到购物、理财、“吃鸡”,你很难找到一个没有使用 HTTP 的地方。 + +不仅在应用领域,在开发领域 HTTP 协议也得到了广泛的支持。它并不限定某种编程语言或者操作系统,所以天然具有“跨语言、跨平台”的优越性。而且,因为本身的简单特性很容易实现,所以几乎所有的编程语言都有 HTTP 调用库和外围的开发测试工具,这一点我觉得就不用再举例了吧,你可能比我更熟悉。 + +HTTP 广泛应用的背后还有许多硬件基础设施支持,各个互联网公司和传统行业公司都不遗余力地“触网”,购买服务器开办网站,建设数据中心、CDN 和高速光纤,持续地优化上网体验,让 HTTP 运行的越来越顺畅。 + +“应用广泛”的这个优点也就决定了:无论是创业者还是求职者,无论是做网站服务器还是写应用客户端,HTTP 协议都是必须要掌握的基本技能。 + +无状态 + +看过了两个优点,我们再来看看一把“双刃剑”,也就是上一讲中说到的“无状态”,它对于 HTTP 来说既是优点也是缺点。 + +“无状态”有什么好处呢? + +因为服务器没有“记忆能力”,所以就不需要额外的资源来记录状态信息,不仅实现上会简单一些,而且还能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。 + +而且,“无状态”也表示服务器都是相同的,没有“状态”的差异,所以可以很容易地组成集群,让负载均衡把请求转发到任意一台服务器,不会因为状态不一致导致处理出错,使用“堆机器”的“笨办法”轻松实现高并发高可用。 + +那么,“无状态”又有什么坏处呢? + +既然服务器没有“记忆能力”,它就无法支持需要连续多个步骤的“事务”操作。例如电商购物,首先要登录,然后添加购物车,再下单、结算、支付,这一系列操作都需要知道用户的身份才行,但“无状态”服务器是不知道这些请求是相互关联的,每次都得问一遍身份信息,不仅麻烦,而且还增加了不必要的数据传输量。 + +所以,HTTP 协议最好是既“无状态”又“有状态”,不过还真有“鱼和熊掌”两者兼得这样的好事,这就是“小甜饼”Cookie 技术(第 19 讲)。 + +明文 + +HTTP 协议里还有一把优缺点一体的“双刃剑”,就是明文传输。 + +“明文”意思就是协议里的报文(准确地说是 header 部分)不使用二进制数据,而是用简单可阅读的文本形式。 + +对比 TCP、UDP 这样的二进制协议,它的优点显而易见,不需要借助任何外部工具,用浏览器、Wireshark 或者 tcpdump 抓包后,直接用肉眼就可以很容易地查看或者修改,为我们的开发调试工作带来极大的便利。 + +当然,明文的缺点也是一样显而易见,HTTP 报文的所有信息都会暴露在“光天化日之下”,在漫长的传输链路的每一个环节上都毫无隐私可言,不怀好意的人只要侵入了这个链路里的某个设备,简单地“旁路”一下流量,就可以实现对通信的窥视。 + +你有没有听说过“免费 WiFi 陷阱”之类的新闻呢? + +黑客就是利用了 HTTP 明文传输的缺点,在公共场所架设一个 WiFi 热点开始“钓鱼”,诱骗网民上网。一旦你连上了这个 WiFi 热点,所有的流量都会被截获保存,里面如果有银行卡号、网站密码等敏感信息的话那就危险了,黑客拿到了这些数据就可以冒充你为所欲为。 + +不安全 + +与“明文”缺点相关但不完全等同的另一个缺点是“不安全”。 + +安全有很多的方面,明文只是“机密”方面的一个缺点,在“身份认证”和“完整性校验”这两方面 HTTP 也是欠缺的。 + +“身份认证”简单来说就是“怎么证明你就是你”。在现实生活中比较好办,你可以拿出身份证、驾照或者护照,上面有照片和权威机构的盖章,能够证明你的身份。 + +但在虚拟的网络世界里这却是个麻烦事。HTTP 没有提供有效的手段来确认通信双方的真实身份。虽然协议里有一个基本的认证机制,但因为刚才所说的明文传输缺点,这个机制几乎可以说是“纸糊的”,非常容易被攻破。如果仅使用 HTTP 协议,很可能你会连到一个页面一模一样但却是个假冒的网站,然后再被“钓”走各种私人信息。 + +HTTP 协议也不支持“完整性校验”,数据在传输过程中容易被窜改而无法验证真伪。 + +比如,你收到了一条银行用 HTTP 发来的消息:“小明向你转账一百元”,你无法知道小明是否真的就只转了一百元,也许他转了一千元或者五十元,但被黑客窜改成了一百元,真实情况到底是什么样子 HTTP 协议没有办法给你答案。 + +虽然银行可以用 MD5、SHA1 等算法给报文加上数字摘要,但还是因为“明文”这个致命缺点,黑客可以连同摘要一同修改,最终还是判断不出报文是否被窜改。 + +为了解决 HTTP 不安全的缺点,所以就出现了 HTTPS,这个我们以后再说。 + +性能 + +最后我们来谈谈 HTTP 的性能,可以用六个字来概括:“不算差,不够好”。 + +HTTP 协议基于 TCP/IP,并且使用了“请求 - 应答”的通信模式,所以性能的关键就在这两点上。 + +必须要说的是,TCP 的性能是不差的,否则也不会纵横互联网江湖四十余载了,而且它已经被研究的很透,集成在操作系统内核里经过了细致的优化,足以应付大多数的场景。 + +只可惜如今的江湖已经不是从前的江湖,现在互联网的特点是移动和高并发,不能保证稳定的连接质量,所以在 TCP 层面上 HTTP 协议有时候就会表现的不够好。 + +而“请求 - 应答”模式则加剧了 HTTP 的性能问题,这就是著名的“队头阻塞”(Head-of-line blocking),当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据。 + +为了解决这个问题,就诞生出了一个专门的研究课题“Web 性能优化”,HTTP 官方标准里就有“缓存”一章(RFC7234),非官方的“花招”就更多了,例如切图、数据内嵌与合并,域名分片、JavaScript“黑科技”等等。 + +不过现在已经有了终极解决方案:HTTP/2 和 HTTP/3,后面我也会展开来讲。 + +小结 + + +HTTP 最大的优点是简单、灵活和易于扩展; +HTTP 拥有成熟的软硬件环境,应用的非常广泛,是互联网的基础设施; +HTTP 是无状态的,可以轻松实现集群化,扩展性能,但有时也需要用 Cookie 技术来实现“有状态”; +HTTP 是明文传输,数据完全肉眼可见,能够方便地研究分析,但也容易被窃听; +HTTP 是不安全的,无法验证通信双方的身份,也不能判断报文是否被窜改; +HTTP 的性能不算差,但不完全适应现在的互联网,还有很大的提升空间。 + + +虽然 HTTP 免不了这样那样的缺点,但你也不要怕,别忘了它有一个最重要的“灵活可扩展”的优点,所有的缺点都可以在这个基础上想办法解决,接下来的“进阶篇”和“安全篇”就会讲到。 + +课下作业 + + +你最喜欢的 HTTP 优点是哪个?最不喜欢的缺点又是哪个?为什么? +你能够再进一步扩展或补充论述今天提到这些优点或缺点吗? +你能试着针对这些缺点提出一些自己的解决方案吗? + + +欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/15海纳百川:HTTP的实体数据.md b/专栏/透视HTTP协议/15海纳百川:HTTP的实体数据.md new file mode 100644 index 0000000..c6b3830 --- /dev/null +++ b/专栏/透视HTTP协议/15海纳百川:HTTP的实体数据.md @@ -0,0 +1,184 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 15 海纳百川:HTTP的实体数据 + 你好,我是 Chrono。 + +今天我要与你分享的话题是“海纳百川:HTTP 的实体数据”。 + +这一讲是“进阶篇”的第一讲,从今天开始,我会用连续的 8 讲的篇幅来详细解析 HTTP 协议里的各种头字段,包括定义、功能、使用方式、注意事项等等。学完了这些课程,你就可以完全掌握 HTTP 协议。 + +在前面的“基础篇”里我们了解了 HTTP 报文的结构,知道一个 HTTP 报文是由“header+body”组成的。但那时我们主要研究的是 header,没有涉及到 body。所以,“进阶篇”的第一讲就从 HTTP 的 body 谈起。 + +数据类型与编码 + +在 TCP/IP 协议栈里,传输数据基本上都是“header+body”的格式。但 TCP、UDP 因为是传输层的协议,它们不会关心 body 数据是什么,只要把数据发送到对方就算是完成了任务。 + +而 HTTP 协议则不同,它是应用层的协议,数据到达之后工作只能说是完成了一半,还必须要告诉上层应用这是什么数据才行,否则上层应用就会“不知所措”。 + +你可以设想一下,假如 HTTP 没有告知数据类型的功能,服务器把“一大坨”数据发给了浏览器,浏览器看到的是一个“黑盒子”,这时候该怎么办呢? + +当然,它可以“猜”。因为很多数据都是有固定格式的,所以通过检查数据的前几个字节也许就能知道这是个 GIF 图片、或者是个 MP3 音乐文件,但这种方式无疑十分低效,而且有很大几率会检查不出来文件类型。 + +幸运的是,早在 HTTP 协议诞生之前就已经有了针对这种问题的解决方案,不过它是用在电子邮件系统里的,让电子邮件可以发送 ASCII 码以外的任意数据,方案的名字叫做“多用途互联网邮件扩展”(Multipurpose Internet Mail Extensions),简称为 MIME。 + +MIME 是一个很大的标准规范,但 HTTP 只“顺手牵羊”取了其中的一部分,用来标记 body 的数据类型,这就是我们平常总能听到的“MIME type”。 + +MIME 把数据分成了八大类,每个大类下再细分出多个子类,形式是“type/subtype”的字符串,巧得很,刚好也符合了 HTTP 明文的特点,所以能够很容易地纳入 HTTP 头字段里。 + +这里简单列举一下在 HTTP 里经常遇到的几个类别: + + +text:即文本格式的可读数据,我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等。 +image:即图像文件,有 image/gif、image/jpeg、image/png 等。 +audio/video:音频和视频数据,例如 audio/mpeg、video/mp4 等。 +application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有 application/json,application/javascript、application/pdf 等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是 application/octet-stream,即不透明的二进制数据。 + + +但仅有 MIME type 还不够,因为 HTTP 在传输时为了节约带宽,有时候还会压缩数据,为了不要让浏览器继续“猜”,还需要有一个“Encoding type”,告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据。 + +比起 MIME type 来说,Encoding type 就少了很多,常用的只有下面三种: + + +gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式; +deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip; +br:一种专门为 HTTP 优化的新压缩算法(Brotli)。 + + +数据类型使用的头字段 + +有了 MIME type 和 Encoding type,无论是浏览器还是服务器就都可以轻松识别出 body 的类型,也就能够正确处理数据了。 + +HTTP 协议为此定义了两个 Accept 请求头字段和两个 Content 实体头字段,用于客户端和服务器进行“内容协商”。也就是说,客户端用 Accept 头告诉服务器希望接收什么样的数据,而服务器用 Content 头告诉客户端实际发送了什么样的数据。 + + + +Accept字段标记的是客户端可理解的 MIME type,可以用“,”做分隔符列出多个类型,让服务器有更多的选择余地,例如下面的这个头: + +Accept: text/html,application/xml,image/webp,image/png + + +这就是告诉服务器:“我能够看懂 HTML、XML 的文本,还有 webp 和 png 的图片,请给我这四类格式的数据”。 + +相应的,服务器会在响应报文里用头字段Content-Type告诉实体数据的真实类型: + +Content-Type: text/html +Content-Type: image/png + + +这样浏览器看到报文里的类型是“text/html”就知道是 HTML 文件,会调用排版引擎渲染出页面,看到“image/png”就知道是一个 PNG 文件,就会在页面上显示出图像。 + +Accept-Encoding字段标记的是客户端支持的压缩格式,例如上面说的 gzip、deflate 等,同样也可以用“,”列出多个,服务器可以选择其中一种来压缩数据,实际使用的压缩格式放在响应头字段Content-Encoding里。 + +Accept-Encoding: gzip, deflate, br +Content-Encoding: gzip + + +不过这两个字段是可以省略的,如果请求报文里没有 Accept-Encoding 字段,就表示客户端不支持压缩数据;如果响应报文里没有 Content-Encoding 字段,就表示响应数据没有被压缩。 + +语言类型与编码 + +MIME type 和 Encoding type 解决了计算机理解 body 数据的问题,但互联网遍布全球,不同国家不同地区的人使用了很多不同的语言,虽然都是 text/html,但如何让浏览器显示出每个人都可理解可阅读的语言文字呢? + +这实际上就是“国际化”的问题。HTTP 采用了与数据类型相似的解决方案,又引入了两个概念:语言类型与字符集。 + +所谓的“语言类型”就是人类使用的自然语言,例如英语、汉语、日语等,而这些自然语言可能还有下属的地区性方言,所以在需要明确区分的时候也要使用“type-subtype”的形式,不过这里的格式与数据类型不同,分隔符不是“/”,而是“-”。 + +举几个例子:en 表示任意的英语,en-US 表示美式英语,en-GB 表示英式英语,而 zh-CN 就表示我们最常使用的汉语。 + +关于自然语言的计算机处理还有一个更麻烦的东西叫做“字符集”。 + +在计算机发展的早期,各个国家和地区的人们“各自为政”,发明了许多字符编码方式来处理文字,比如英语世界用的 ASCII、汉语世界用的 GBK、BIG5,日语世界用的 Shift_JIS 等。同样的一段文字,用一种编码显示正常,换另一种编码后可能就会变得一团糟。 + +所以后来就出现了 Unicode 和 UTF-8,把世界上所有的语言都容纳在一种编码方案里,UTF-8 也成为了互联网上的标准字符集。 + +语言类型使用的头字段 + +同样的,HTTP 协议也使用 Accept 请求头字段和 Content 实体头字段,用于客户端和服务器就语言与编码进行“内容协商”。 + +Accept-Language字段标记了客户端可理解的自然语言,也允许用“,”做分隔符列出多个类型,例如: + +Accept-Language: zh-CN, zh, en + + +这个请求头会告诉服务器:“最好给我 zh-CN 的汉语文字,如果没有就用其他的汉语方言,如果还没有就给英文”。 + +相应的,服务器应该在响应报文里用头字段Content-Language告诉客户端实体数据使用的实际语言类型: + +Content-Language: zh-CN + + +字符集在 HTTP 里使用的请求头字段是Accept-Charset,但响应头里却没有对应的 Content-Charset,而是在Content-Type字段的数据类型后面用“charset=xxx”来表示,这点需要特别注意。 + +例如,浏览器请求 GBK 或 UTF-8 的字符集,然后服务器返回的是 UTF-8 编码,就是下面这样: + +Accept-Charset: gbk, utf-8 +Content-Type: text/html; charset=utf-8 + + +不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset,而服务器也不会发送 Content-Language,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有 Accept-Language 字段,响应头里只会有 Content-Type 字段。 + + + +内容协商的质量值 + +在 HTTP 协议里用 Accept、Accept-Encoding、Accept-Language 等请求头字段进行内容协商的时候,还可以用一种特殊的“q”参数表示权重来设定优先级,这里的“q”是“quality factor”的意思。 + +权重的最大值是 1,最小值是 0.01,默认值是 1,如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个“;”,然后是“q=value”。 + +这里要提醒的是“;”的用法,在大多数编程语言里“;”的断句语气要强于“,”,而在 HTTP 的内容协商里却恰好反了过来,“;”的意义是小于“,”的。 + +例如下面的 Accept 字段: + +Accept: text/html,application/xml;q=0.9,*/*;q=0.8 + + +它表示浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重是 0.8。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML。 + +内容协商的结果 + +内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个Vary字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如: + +Vary: Accept-Encoding,User-Agent,Accept + + +这个 Vary 字段表示服务器依据了 Accept-Encoding、User-Agent 和 Accept 这三个头字段,然后决定了发回的响应报文。 + +Vary 字段可以认为是响应报文的一个特殊的“版本标记”。每当 Accept 等请求头变化时,Vary 也会随着响应报文一起变化。也就是说,同一个 URI 可能会有多个不同的“版本”,主要用在传输链路中间的代理服务器实现缓存服务,这个之后讲“HTTP 缓存”时还会再提到。 + +动手实验 + +上面讲完了理论部分,接下来就是实际动手操作了。可以用我们的实验环境,在 www 目录下有一个 mime 目录,里面预先存放了几个文件,可以用 URI“/15-1?name=file”的形式访问,例如: + +http://www.chrono.com/15-1?name=a.json +http://www.chrono.com/15-1?name=a.xml + + +在 Chrome 里打开开发者工具,就能够看到 Accept 和 Content 头: + + + +你也可以把任意的文件拷贝到 mime 目录下,比如压缩包、MP3、图片、视频等,再用 Chrome 访问,观察更多的 MIME type。 + +有了这些经验后,你还可以离开实验环境,直接访问各大门户网站,看看真实网络世界里的 HTTP 报文是什么样子的。 + +小结 + +今天我们学习了 HTTP 里的数据类型和语言类型,在这里为今天的内容做个小结。 + + + + +数据类型表示实体数据的内容是什么,使用的是 MIME type,相关的头字段是 Accept 和 Content-Type; +数据编码表示实体数据的压缩方式,相关的头字段是 Accept-Encoding 和 Content-Encoding; +语言类型表示实体数据的自然语言,相关的头字段是 Accept-Language 和 Content-Language; +字符集表示实体数据的编码方式,相关的头字段是 Accept-Charset 和 Content-Type; +客户端需要在请求头里使用 Accept 等头字段与服务器进行“内容协商”,要求服务器返回最合适的数据; +Accept 等头字段可以用“,”顺序列出多个可能的选项,还可以用“;q=”参数来精确指定权重。 + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/16把大象装进冰箱:HTTP传输大文件的方法.md b/专栏/透视HTTP协议/16把大象装进冰箱:HTTP传输大文件的方法.md new file mode 100644 index 0000000..05a8752 --- /dev/null +++ b/专栏/透视HTTP协议/16把大象装进冰箱:HTTP传输大文件的方法.md @@ -0,0 +1,205 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 16 把大象装进冰箱:HTTP传输大文件的方法 + 上次我们谈到了 HTTP 报文里的 body,知道了 HTTP 可以传输很多种类的数据,不仅是文本,也能传输图片、音频和视频。 + +早期互联网上传输的基本上都是只有几 K 大小的文本和小图片,现在的情况则大有不同。网页里包含的信息实在是太多了,随随便便一个主页 HTML 就有可能上百 K,高质量的图片都以 M 论,更不要说那些电影、电视剧了,几 G、几十 G 都有可能。 + +相比之下,100M 的光纤固网或者 4G 移动网络在这些大文件的压力下都变成了“小水管”,无论是上传还是下载,都会把网络传输链路挤的“满满当当”。 + +所以,如何在有限的带宽下高效快捷地传输这些大文件就成了一个重要的课题。这就好比是已经打开了冰箱门(建立连接),该怎么把大象(文件)塞进去再关上门(完成传输)呢? + +今天我们就一起看看 HTTP 协议里有哪些手段能解决这个问题。 + +数据压缩 + +还记得上一讲中说到的“数据类型与编码”吗?如果你还有印象的话,肯定能够想到一个最基本的解决方案,那就是“数据压缩”,把大象变成小猪佩奇,再放进冰箱。 + +通常浏览器在发送请求时都会带着“Accept-Encoding”头字段,里面是浏览器支持的压缩格式列表,例如 gzip、deflate、br 等,这样服务器就可以从中选择一种压缩算法,放进“Content-Encoding”响应头里,再把原数据压缩后发给浏览器。 + +如果压缩率能有 50%,也就是说 100K 的数据能够压缩成 50K 的大小,那么就相当于在带宽不变的情况下网速提升了一倍,加速的效果是非常明显的。 + +不过这个解决方法也有个缺点,gzip 等压缩算法通常只对文本文件有较好的压缩率,而图片、音频视频等多媒体数据本身就已经是高度压缩的,再用 gzip 处理也不会变小(甚至还有可能会增大一点),所以它就失效了。 + +不过数据压缩在处理文本的时候效果还是很好的,所以各大网站的服务器都会使用这个手段作为“保底”。例如,在 Nginx 里就会使用“gzip on”指令,启用对“text/html”的压缩。 + +分块传输 + +在数据压缩之外,还能有什么办法来解决大文件的问题呢? + +压缩是把大文件整体变小,我们可以反过来思考,如果大文件整体不能变小,那就把它“拆开”,分解成多个小块,把这些小块分批发给浏览器,浏览器收到后再组装复原。 + +这样浏览器和服务器都不用在内存里保存文件的全部,每次只收发一小部分,网络也不会被大文件长时间占用,内存、带宽等资源也就节省下来了。 + +这种“化整为零”的思路在 HTTP 协议里就是“chunked”分块传输编码,在响应报文里用头字段“Transfer-Encoding: chunked”来表示,意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。 + +这就好比是用魔法把大象变成“乐高积木”,拆散了逐个装进冰箱,到达目的地后再施法拼起来“满血复活”。 + +分块传输也可以用于“流式数据”,例如由数据库动态生成的表单页面,这种情况下 body 数据的长度是未知的,无法在头字段“Content-Length”里给出确切的长度,所以也只能用 chunked 方式分块发送。 + +“Transfer-Encoding: chunked”和“Content-Length”这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked),这一点你一定要记住。 + +下面我们来看一下分块传输的编码规则,其实也很简单,同样采用了明文的方式,很类似响应头。 + + +每个分块包含两个部分,长度头和数据块; +长度头是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度; +数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF; +最后用一个长度为 0 的块表示结束,即“0\r\n\r\n”。 + + +听起来好像有点难懂,看一下图就好理解了: + + + +实验环境里的 URI“/16-1”简单地模拟了分块传输,可以用 Chrome 访问这个地址看一下效果: + + + +不过浏览器在收到分块传输的数据后会自动按照规则去掉分块编码,重新组装出内容,所以想要看到服务器发出的原始报文形态就得用 Telnet 手工发送请求(或者用 Wireshark 抓包): + +GET /16-1 HTTP/1.1 +Host: www.chrono.com + + +因为 Telnet 只是收到响应报文就完事了,不会解析分块数据,所以可以很清楚地看到响应报文里的 chunked 数据格式:先是一行 16 进制长度,然后是数据,然后再是 16 进制长度和数据,如此重复,最后是 0 长度分块结束。 + + + +范围请求 + +有了分块传输编码,服务器就可以轻松地收发大文件了,但对于上 G 的超大文件,还有一些问题需要考虑。 + +比如,你在看当下正热播的某穿越剧,想跳过片头,直接看正片,或者有段剧情很无聊,想拖动进度条快进几分钟,这实际上是想获取一个大文件其中的片段数据,而分块传输并没有这个能力。 + +HTTP 协议为了满足这样的需求,提出了“范围请求”(range requests)的概念,允许客户端在请求头里使用专用字段来表示只获取文件的一部分,相当于是客户端的“化整为零”。 + +范围请求不是 Web 服务器必备的功能,可以实现也可以不实现,所以服务器必须在响应头里使用字段“Accept-Ranges: bytes”明确告知客户端:“我是支持范围请求的”。 + +如果不支持的话该怎么办呢?服务器可以发送“Accept-Ranges: none”,或者干脆不发送“Accept-Ranges”字段,这样客户端就认为服务器没有实现范围请求功能,只能老老实实地收发整块文件了。 + +请求头Range是 HTTP 范围请求的专用字段,格式是“bytes=x-y”,其中的 x 和 y 是以字节为单位的数据范围。 + +要注意 x、y 表示的是“偏移量”,范围必须从 0 计数,例如前 10 个字节表示为“0-9”,第二个 10 字节表示为“10-19”,而“0-10”实际上是前 11 个字节。 + +Range 的格式也很灵活,起点 x 和终点 y 可以省略,能够很方便地表示正数或者倒数的范围。假设文件是 100 个字节,那么: + + +“0-”表示从文档起点到文档终点,相当于“0-99”,即整个文件; +“10-”是从第 10 个字节开始到文档末尾,相当于“10-99”; +“-1”是文档的最后一个字节,相当于“99-99”; +“-10”是从文档末尾倒数 10 个字节,相当于“90-99”。 + + +服务器收到 Range 字段后,需要做四件事。 + +第一,它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码416,意思是“你的范围请求有误,我无法处理,请再检查一下”。 + +第二,如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分。 + +第三,服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”,与 Range 头区别在没有“=”,范围后多了总长度。例如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。 + +最后剩下的就是发送数据了,直接把片段用 TCP 发给客户端,一个范围请求就算是处理完了。 + +你可以用实验环境的 URI“/16-2”来测试范围请求,它处理的对象是“/mime/a.txt”。不过我们不能用 Chrome 浏览器,因为它没有编辑 HTTP 请求头的功能(这点上不如 Firefox 方便),所以还是要用 Telnet。 + +例如下面的这个请求使用 Range 字段获取了文件的前 32 个字节: + +GET /16-2 HTTP/1.1 +Host: www.chrono.com +Range: bytes=0-31 + + +返回的数据是(去掉了几个无关字段): + +HTTP/1.1 206 Partial Content +Content-Length: 32 +Accept-Ranges: bytes +Content-Range: bytes 0-31/96 + +// this is a plain text json doc + + +有了范围请求之后,HTTP 处理大文件就更加轻松了,看视频时可以根据时间点计算出文件的 Range,不用下载整个文件,直接精确获取片段所在的数据内容。 + +不仅看视频的拖拽进度需要范围请求,常用的下载工具里的多段下载、断点续传也是基于它实现的,要点是: + + +先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小; +开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据; +下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了。 + + +多段数据 + +刚才说的范围请求一次只获取一个片段,其实它还支持在 Range 头里使用多个“x-y”,一次性获取多个片段数据。 + +这种情况需要使用一种特殊的 MIME 类型:“multipart/byteranges”,表示报文的 body 是由多段字节序列组成的,并且还要用一个参数“boundary=xxx”给出段之间的分隔标记。 + +多段数据的格式与分块传输也比较类似,但它需要用分隔标记 boundary 来区分不同的片段,可以通过图来对比一下。 + + + +每一个分段必须以“- -boundary”开始(前面加两个“-”),之后要用“Content-Type”和“Content-Range”标记这段数据的类型和所在范围,然后就像普通的响应头一样以回车换行结束,再加上分段数据,最后用一个“- -boundary- -”(前后各有两个“-”)表示所有的分段结束。 + +例如,我们在实验环境里用 Telnet 发出有两个范围的请求: + +GET /16-2 HTTP/1.1 +Host: www.chrono.com +Range: bytes=0-9, 20-29 + + +得到的就会是下面这样: + +HTTP/1.1 206 Partial Content +Content-Type: multipart/byteranges; boundary=00000000001 +Content-Length: 189 +Connection: keep-alive +Accept-Ranges: bytes + + +--00000000001 +Content-Type: text/plain +Content-Range: bytes 0-9/96 + +// this is +--00000000001 +Content-Type: text/plain +Content-Range: bytes 20-29/96 + +ext json d +--00000000001-- + + +报文里的“- -00000000001”就是多段的分隔符,使用它客户端就可以很容易地区分出多段 Range 数据。 + +小结 + +今天我们学习了 HTTP 传输大文件相关的知识,在这里做一下简单小结: + + +压缩 HTML 等文本文件是传输大文件最基本的方法; +分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding: chunked”来表示,分块的格式是 16 进制长度头 + 数据块; +范围请求可以只获取部分数据,即“分块请求”,实现视频拖拽或者断点续传,使用请求头字段“Range”和响应头字段“Content-Range”,响应状态码必须是 206; +也可以一次请求多个范围,这时候响应报文的数据类型是“multipart/byteranges”,body 里的多个部分会用 boundary 字符串分隔。 + + +要注意这四种方法不是互斥的,而是可以混合起来使用,例如压缩后再分块传输,或者分段后再分块,实验环境的 URI“/16-3”就模拟了后一种的情形,你可以自己用 Telnet 试一下。 + +课下作业 + + +分块传输数据的时候,如果数据里含有回车换行(\r\n)是否会影响分块的处理呢? +如果对一个被 gzip 的文件执行范围请求,比如“Range: bytes=10-19”,那么这个范围是应用于原文件还是压缩后的文件呢? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/17排队也要讲效率:HTTP的连接管理.md b/专栏/透视HTTP协议/17排队也要讲效率:HTTP的连接管理.md new file mode 100644 index 0000000..6d21fc3 --- /dev/null +++ b/专栏/透视HTTP协议/17排队也要讲效率:HTTP的连接管理.md @@ -0,0 +1,150 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 17 排队也要讲效率:HTTP的连接管理 + 在[第 14 讲]里,我曾经提到过 HTTP 的性能问题,用了六个字来概括:“不算差,不够好”。同时,我也谈到了“队头阻塞”,但由于时间的限制没有展开来细讲,这次就来好好地看看 HTTP 在连接这方面的表现。 + +HTTP 的连接管理也算得上是个“老生常谈”的话题了,你一定曾经听说过“短连接”“长连接”之类的名词,今天让我们一起来把它们弄清楚。 + +短连接 + +HTTP 协议最初(0.9⁄1.0)是个非常简单的协议,通信过程也采用了简单的“请求 - 应答”方式。 + +它底层的数据传输基于 TCP/IP,每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。 + +因为客户端与服务器的整个连接过程很短暂,不会与服务器保持长时间的连接状态,所以就被称为“短连接”(short-lived connections)。早期的 HTTP 协议也被称为是“无连接”的协议。 + +短连接的缺点相当严重,因为在 TCP 协议里,建立连接和关闭连接都是非常“昂贵”的操作。TCP 建立连接要有“三次握手”,发送 3 个数据包,需要 1 个 RTT;关闭连接是“四次挥手”,4 个数据包需要 2 个 RTT。 + +而 HTTP 的一次简单“请求 - 响应”通常只需要 4 个包,如果不算服务器内部的处理时间,最多是 2 个 RTT。这么算下来,浪费的时间就是“3÷5=60%”,有三分之二的时间被浪费掉了,传输效率低得惊人。 + + + +单纯地从理论上讲,TCP 协议你可能还不太好理解,我就拿打卡考勤机来做个形象的比喻吧。 + +假设你的公司买了一台打卡机,放在前台,因为这台机器比较贵,所以专门做了一个保护罩盖着它,公司要求每次上下班打卡时都要先打开盖子,打卡后再盖上盖子。 + +可是偏偏这个盖子非常牢固,打开关闭要费很大力气,打卡可能只要 1 秒钟,而开关盖子却需要四五秒钟,大部分时间都浪费在了毫无意义的开关盖子操作上了。 + +可想而知,平常还好说,一到上下班的点在打卡机前就会排起长队,每个人都要重复“开盖 - 打卡 - 关盖”的三个步骤,你说着急不着急。 + +在这个比喻里,打卡机就相当于服务器,盖子的开关就是 TCP 的连接与关闭,而每个打卡的人就是 HTTP 请求,很显然,短连接的缺点严重制约了服务器的服务能力,导致它无法处理更多的请求。 + +长连接 + +针对短连接暴露出的缺点,HTTP 协议就提出了“长连接”的通信方式,也叫“持久连接”(persistent connections)、“连接保活”(keep alive)、“连接复用”(connection reuse)。 + +其实解决办法也很简单,用的就是“成本均摊”的思路,既然 TCP 的连接和关闭非常耗时间,那么就把这个时间成本由原来的一个“请求 - 应答”均摊到多个“请求 - 应答”上。 + +这样虽然不能改善 TCP 的连接效率,但基于“分母效应”,每个“请求 - 应答”的无效时间就会降低不少,整体传输效率也就提高了。 + +这里我画了一个短连接与长连接的对比示意图。 + + + +在短连接里发送了三次 HTTP“请求 - 应答”,每次都会浪费 60% 的 RTT 时间。而在长连接的情况下,同样发送三次请求,因为只在第一次时建立连接,在最后一次时关闭连接,所以浪费率就是“3÷9≈33%”,降低了差不多一半的时间损耗。显然,如果在这个长连接上发送的请求越多,分母就越大,利用率也就越高。 + +继续用刚才的打卡机的比喻,公司也觉得这种反复“开盖 - 打卡 - 关盖”的操作太“反人类”了,于是颁布了新规定,早上打开盖子后就不用关上了,可以自由打卡,到下班后再关上盖子。 + +这样打卡的效率(即服务能力)就大幅度提升了,原来一次打卡需要五六秒钟,现在只要一秒就可以了,上下班时排长队的景象一去不返,大家都开心。 + +连接相关的头字段 + +由于长连接对性能的改善效果非常显著,所以在 HTTP/1.1 中的连接都会默认启用长连接。不需要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长连接,在这个连接上收发数据。 + +当然,我们也可以在请求头里明确地要求使用长连接机制,使用的字段是Connection,值是“keep-alive”。 + +不过不管客户端是否显式要求长连接,如果服务器支持长连接,它总会在响应报文里放一个“Connection: keep-alive”字段,告诉客户端:“我是支持长连接的,接下来就用这个 TCP 一直收发数据吧”。 + +你可以在实验环境里访问 URI“/17-1”,用 Chrome 看一下服务器返回的响应头: + + + +不过长连接也有一些小缺点,问题就出在它的“长”字上。 + +因为 TCP 连接长时间不关闭,服务器必须在内存里保存它的状态,这就占用了服务器的资源。如果有大量的空闲长连接只连不发,就会很快耗尽服务器的资源,导致服务器无法为真正有需要的用户提供服务。 + +所以,长连接也需要在恰当的时间关闭,不能永远保持与服务器的连接,这在客户端或者服务器都可以做到。 + +在客户端,可以在请求头里加上“Connection: close”字段,告诉服务器:“这次通信后就关闭连接”。服务器看到这个字段,就知道客户端要主动关闭连接,于是在响应报文里也加上这个字段,发送之后就调用 Socket API 关闭 TCP 连接。 + +服务器端通常不会主动关闭连接,但也可以使用一些策略。拿 Nginx 来举例,它有两种方式: + + +使用“keepalive_timeout”指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。 +使用“keepalive_requests”指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。 + + +另外,客户端和服务器都可以在报文里附加通用头字段“Keep-Alive: timeout=value”,限定长连接的超时时间。但这个字段的约束力并不强,通信的双方可能并不会遵守,所以不太常见。 + +我们的实验环境配置了“keepalive_timeout 60”和“keepalive_requests 5”,意思是空闲连接最多 60 秒,最多发送 5 个请求。所以,如果连续刷新五次页面,就能看到响应头里的“Connection: close”了。 + +把这个过程用 Wireshark 抓一下包,就能够更清晰地看到整个长连接中的握手、收发数据与挥手过程,在课后你可以再实际操作看看。 + + + +队头阻塞 + +看完了短连接和长连接,接下来就要说到著名的“队头阻塞”(Head-of-line blocking,也叫“队首阻塞”)了。 + +“队头阻塞”与短连接和长连接无关,而是由 HTTP 基本的“请求 - 应答”模型所导致的。 + +因为 HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列。队列里的请求没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求被最优先处理。 + +如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本。 + + + +还是用打卡机做个比喻。 + +上班的时间点上,大家都在排队打卡,可这个时候偏偏最前面的那个人遇到了打卡机故障,怎么也不能打卡成功,急得满头大汗。等找人把打卡机修好,后面排队的所有人全迟到了。 + +性能优化 + +因为“请求 - 应答”模型不能变,所以“队头阻塞”问题在 HTTP/1.1 里无法解决,只能缓解,有什么办法呢? + +公司里可以再多买几台打卡机放在前台,这样大家可以不用挤在一个队伍里,分散打卡,一个队伍偶尔阻塞也不要紧,可以改换到其他不阻塞的队伍。 + +这在 HTTP 里就是“并发连接”(concurrent connections),也就是同时对一个域名发起多个长连接,用数量来解决质量的问题。 + +但这种方式也存在缺陷。如果每个客户端都想自己快,建立很多个连接,用户数×并发数就会是个天文数字。服务器的资源根本就扛不住,或者被服务器认为是恶意攻击,反而会造成“拒绝服务”。 + +所以,HTTP 协议建议客户端使用并发,但不能“滥用”并发。RFC2616 里明确限制每个客户端最多并发 2 个连接。不过实践证明这个数字实在是太小了,众多浏览器都“无视”标准,把这个上限提高到了 6~8。后来修订的 RFC7230 也就“顺水推舟”,取消了这个“2”的限制。 + +但“并发连接”所压榨出的性能也跟不上高速发展的互联网无止境的需求,还有什么别的办法吗? + +公司发展的太快了,员工越来越多,上下班打卡成了迫在眉睫的大问题。前台空间有限,放不下更多的打卡机了,怎么办?那就多开几个打卡的地方,每个楼层、办公区的入口也放上三四台打卡机,把人进一步分流,不要都往前台挤。 + +这个就是“域名分片”(domain sharding)技术,还是用数量来解决质量的思路。 + +HTTP 协议和浏览器不是限制并发连接数量吗?好,那我就多开几个域名,比如 shard1.chrono.com、shard2.chrono.com,而这些域名都指向同一台服务器 www.chrono.com,这样实际长连接的数量就又上去了,真是“美滋滋”。不过实在是有点“上有政策,下有对策”的味道。 + +小结 + +这一讲中我们学习了 HTTP 协议里的短连接和长连接,简单小结一下今天的内容: + + +早期的 HTTP 协议使用短连接,收到响应后就立即关闭连接,效率很低; +HTTP/1.1 默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率; +服务器会发送“Connection: keep-alive”字段表示启用了长连接; +报文头里如果有“Connection: close”就意味着长连接即将关闭; +过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接; +“队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”技术缓解。 + + +课下作业 + + +在开发基于 HTTP 协议的客户端时应该如何选择使用的连接模式呢?短连接还是长连接? +应当如何降低长连接对服务器的负面影响呢? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/18四通八达:HTTP的重定向和跳转.md b/专栏/透视HTTP协议/18四通八达:HTTP的重定向和跳转.md new file mode 100644 index 0000000..9dddf8a --- /dev/null +++ b/专栏/透视HTTP协议/18四通八达:HTTP的重定向和跳转.md @@ -0,0 +1,169 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 18 四通八达:HTTP的重定向和跳转 + 在专栏[第 1 讲]时我曾经说过,为了实现在互联网上构建超链接文档系统的设想,蒂姆·伯纳斯 - 李发明了万维网,使用 HTTP 协议传输“超文本”,让全世界的人都能够自由地共享信息。 + +“超文本”里含有“超链接”,可以从一个“超文本”跳跃到另一个“超文本”,对线性结构的传统文档是一个根本性的变革。 + +能够使用“超链接”在网络上任意地跳转也是万维网的一个关键特性。它把分散在世界各地的文档连接在一起,形成了复杂的网状结构,用户可以在查看时随意点击链接、转换页面。再加上浏览器又提供了“前进”“后退”“书签”等辅助功能,让用户在文档间跳转时更加方便,有了更多的主动性和交互性。 + +那么,点击页面“链接”时的跳转是怎样的呢?具体一点,比如在 Nginx 的主页上点了一下“download”链接,会发生什么呢? + +结合之前的课程,稍微思考一下你就能得到答案:浏览器首先要解析链接文字里的 URI。 + +http://nginx.org/en/download.html + + +再用这个 URI 发起一个新的 HTTP 请求,获取响应报文后就会切换显示内容,渲染出新 URI 指向的页面。 + +这样的跳转动作是由浏览器的使用者主动发起的,可以称为“主动跳转”,但还有一类跳转是由服务器来发起的,浏览器使用者无法控制,相对地就可以称为“被动跳转”,这在 HTTP 协议里有个专门的名词,叫做“重定向”(Redirection)。 + +重定向的过程 + +其实之前我们就已经见过重定向了,在[第 12 讲]里 3××状态码时就说过,301 是“永久重定向”,302 是“临时重定向”,浏览器收到这两个状态码就会跳转到新的 URI。 + +那么,它们是怎么做到的呢?难道仅仅用这两个代码就能够实现跳转页面吗? + +先在实验环境里看一下重定向的过程吧,用 Chrome 访问 URI “/18-1”,它会使用 302 立即跳转到“/index.html”。 + +从这个实验可以看到,这一次“重定向”实际上发送了两次 HTTP 请求,第一个请求返回了 302,然后第二个请求就被重定向到了“/index.html”。但如果不用开发者工具的话,你是完全看不到这个跳转过程的,也就是说,重定向是“用户无感知”的。 + +我们再来看看第一个请求返回的响应报文: + + + +这里出现了一个新的头字段“Location: /index.html”,它就是 301⁄302 重定向跳转的秘密所在。 + +“Location”字段属于响应字段,必须出现在响应报文里。但只有配合 301⁄302 状态码才有意义,它标记了服务器要求重定向的 URI,这里就是要求浏览器跳转到“index.html”。 + +浏览器收到 301⁄302 报文,会检查响应头里有没有“Location”。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接。 + +在“Location”里的 URI 既可以使用绝对 URI,也可以使用相对 URI。所谓“绝对 URI”,就是完整形式的 URI,包括 scheme、host:port、path 等。所谓“相对 URI”,就是省略了 scheme 和 host:port,只有 path 和 query 部分,是不完整的,但可以从请求上下文里计算得到。 + +例如,刚才的实验例子里的“Location: /index.html”用的就是相对 URI。它没有说明访问 URI 的协议和主机,但因为是由“http://www.chrono.com/18-1”重定向返回的响应报文,所以浏览器就可以拼出完整的 URI: + +http://www.chrono.com/index.html + + +实验环境的 URI“/18-1”还支持使用 query 参数“dst=xxx”,指明重定向的 URI,你可以用这种形式再多试几次重定向,看看浏览器是如何工作的。 + +http://www.chrono.com/18-1?dst=/15-1?name=a.json +http://www.chrono.com/18-1?dst=/17-1 + + +注意,在重定向时如果只是在站内跳转,你可以放心地使用相对 URI。但如果要跳转到站外,就必须用绝对 URI。 + +例如,如果想跳转到 Nginx 官网,就必须在“nginx.org”前把“http://”都写出来,否则浏览器会按照相对 URI 去理解,得到的就会是一个不存在的 URI“http://www.chrono.com/nginx.org” + +http://www.chrono.com/18-1?dst=nginx.org # 错误 +http://www.chrono.com/18-1?dst=http://nginx.org # 正确 + + + + +那么,如果 301⁄302 跳转时没有 Location 字段会怎么样呢? + +这个你也可以自己试一下,使用第 12 讲里的 URI“/12-1”,查询参数用“code=302”: + +http://www.chrono.com/12-1?code=302 + + +重定向状态码 + +刚才我把重定向的过程基本讲完了,现在来说一下重定向用到的状态码。 + +最常见的重定向状态码就是 301 和 302,另外还有几个不太常见的,例如 303、307、308 等。它们最终的效果都差不多,让浏览器跳转到新的 URI,但语义上有一些细微的差别,使用的时候要特别注意。 + +301俗称“永久重定向”(Moved Permanently),意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。 + +浏览器看到 301,就知道原来的 URI“过时”了,就会做适当的优化。比如历史记录、更新书签,下次可能就会直接用新的 URI 访问,省去了再次跳转的成本。搜索引擎的爬虫看到 301,也会更新索引库,不再使用老的 URI。 + +302俗称“临时重定向”(“Moved Temporarily”),意思是原 URI 处于“临时维护”状态,新的 URI 是起“顶包”作用的“临时工”。 + +浏览器或者爬虫看到 302,会认为原来的 URI 仍然有效,但暂时不可用,所以只会执行简单的跳转页面,不记录新的 URI,也不会有其他的多余动作,下次访问还是用原 URI。 + +301⁄302 是最常用的重定向状态码,在 3××里剩下的几个还有: + + +303 See Other:类似 302,但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作; +307 Temporary Redirect:类似 302,但重定向后请求里的方法和实体不允许变动,含义比 302 更明确; +308 Permanent Redirect:类似 307,不允许重定向后的请求变动,但它是 301“永久重定向”的含义。 + + +不过这三个状态码的接受程度较低,有的浏览器和服务器可能不支持,开发时应当慎重,测试确认浏览器的实际效果后才能使用。 + +重定向的应用场景 + +理解了重定向的工作原理和状态码的含义,我们就可以在服务器端拥有主动权,控制浏览器的行为,不过要怎么利用重定向才好呢? + +使用重定向跳转,核心是要理解“重定向”和“永久 / 临时”这两个关键词。 + +先来看什么时候需要重定向。 + +一个最常见的原因就是“资源不可用”,需要用另一个新的 URI 来代替。 + +至于不可用的原因那就很多了。例如域名变更、服务器变更、网站改版、系统维护,这些都会导致原 URI 指向的资源无法访问,为了避免出现 404,就需要用重定向跳转到新的 URI,继续为网民提供服务。 + +另一个原因就是“避免重复”,让多个网址都跳转到一个 URI,增加访问入口的同时还不会增加额外的工作量。 + +例如,有的网站都会申请多个名称类似的域名,然后把它们再重定向到主站上。比如,你可以访问一下“qq.com”“github.com ”“bing.com”(记得事先清理缓存),看看它是如何重定向的。 + +决定要实行重定向后接下来要考虑的就是“永久”和“临时”的问题了,也就是选择 301 还是 302。 + +301 的含义是“永久”的。 + +如果域名、服务器、网站架构发生了大幅度的改变,比如启用了新域名、服务器切换到了新机房、网站目录层次重构,这些都算是“永久性”的改变。原来的 URI 已经不能用了,必须用 301“永久重定向”,通知浏览器和搜索引擎更新到新地址,这也是搜索引擎优化(SEO)要考虑的因素之一。 + +302 的含义是“临时”的。 + +原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护,把网站重定向到一个通知页面,告诉用户过一会儿再来访问。另一种用法就是“服务降级”,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行。 + +重定向的相关问题 + +重定向的用途很多,掌握了重定向,就能够在架设网站时获得更多的灵活性,不过在使用时还需要注意两个问题。 + +第一个问题是“性能损耗”。很明显,重定向的机制决定了一个跳转会有两次请求 - 应答,比正常的访问多了一次。 + +虽然 301⁄302 报文很小,但大量的跳转对服务器的影响也是不可忽视的。站内重定向还好说,可以长连接复用,站外重定向就要开两个连接,如果网络连接质量差,那成本可就高多了,会严重影响用户的体验。 + +所以重定向应当适度使用,决不能滥用。 + +第二个问题是“循环跳转”。如果重定向的策略设置欠考虑,可能会出现“A=>B=>C=>A”的无限循环,不停地在这个链路里转圈圈,后果可想而知。 + +所以 HTTP 协议特别规定,浏览器必须具有检测“循环跳转”的能力,在发现这种情况时应当停止发送请求并给出错误提示。 + +实验环境的 URI“/18-2”就模拟了这样的一个“循环跳转”,它跳转到“/18-1”,并用参数“dst=/18-2”再跳回自己,实现了两个 URI 的无限循环。 + +使用 Chrome 访问这个地址,会得到“该网页无法正常运作”的结果: + + + +小结 + +今天我们学习了 HTTP 里的重定向和跳转,简单小结一下这次的内容: + + +重定向是服务器发起的跳转,要求客户端改用新的 URI 重新发送请求,通常会自动进行,用户是无感知的; +301⁄302 是最常用的重定向状态码,分别是“永久重定向”和“临时重定向”; +响应头字段 Location 指示了要跳转的 URI,可以用绝对或相对的形式; +重定向可以把一个 URI 指向另一个 URI,也可以把多个 URI 指向同一个 URI,用途很多; +使用重定向时需要当心性能损耗,还要避免出现循环跳转。 + + +课下作业 + + +301 和 302 非常相似,试着结合第 12 讲,用自己的理解再描述一下两者的异同点。 +你能结合自己的实际情况,再列出几个应当使用重定向的场景吗? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/19让我知道你是谁:HTTP的Cookie机制.md b/专栏/透视HTTP协议/19让我知道你是谁:HTTP的Cookie机制.md new file mode 100644 index 0000000..7082686 --- /dev/null +++ b/专栏/透视HTTP协议/19让我知道你是谁:HTTP的Cookie机制.md @@ -0,0 +1,150 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 19 让我知道你是谁:HTTP的Cookie机制 + 在之前的第 13、14 讲中,我曾经说过,HTTP 是“无状态”的,这既是优点也是缺点。优点是服务器没有状态差异,可以很容易地组成集群,而缺点就是无法支持需要记录状态的事务操作。 + +好在 HTTP 协议是可扩展的,后来发明的 Cookie 技术,给 HTTP 增加了“记忆能力”。 + +什么是 Cookie? + +不知道你有没有看过克里斯托弗·诺兰导演的一部经典电影《记忆碎片》(Memento),里面的主角患有短期失忆症,记不住最近发生的事情。 + + + +比如,电影里有个场景,某人刚跟主角说完话,大闹了一通,过了几分钟再回来,主角却是一脸茫然,完全不记得这个人是谁,刚才又做了什么,只能任人摆布。 + +这种情况就很像 HTTP 里“无状态”的 Web 服务器,只不过服务器的“失忆症”比他还要严重,连一分钟的记忆也保存不了,请求处理完立刻就忘得一干二净。即使这个请求会让服务器发生 500 的严重错误,下次来也会依旧“热情招待”。 + +如果 Web 服务器只是用来管理静态文件还好说,对方是谁并不重要,把文件从磁盘读出来发走就可以了。但随着 HTTP 应用领域的不断扩大,对“记忆能力”的需求也越来越强烈。比如网上论坛、电商购物,都需要“看客下菜”,只有记住用户的身份才能执行发帖子、下订单等一系列会话事务。 + +那该怎么样让原本无“记忆能力”的服务器拥有“记忆能力”呢? + +看看电影里的主角是怎么做的吧。他通过纹身、贴纸条、立拍得等手段,在外界留下了各种记录,一旦失忆,只要看到这些提示信息,就能够在头脑中快速重建起之前的记忆,从而把因失忆而耽误的事情继续做下去。 + +HTTP 的 Cookie 机制也是一样的道理,既然服务器记不住,那就在外部想办法记住。相当于是服务器给每个客户端都贴上一张小纸条,上面写了一些只有服务器才能理解的数据,需要的时候客户端把这些信息发给服务器,服务器看到 Cookie,就能够认出对方是谁了。 + +Cookie 的工作过程 + +那么,Cookie 这张小纸条是怎么传递的呢? + +这要用到两个字段:响应头字段Set-Cookie和请求头字段Cookie。 + +当用户通过浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份标识数据,格式是“key=value”,然后放进 Set-Cookie 字段里,随着响应报文一同发给浏览器。 + +浏览器收到响应报文,看到里面有 Set-Cookie,知道这是服务器给的身份标识,于是就保存起来,下次再请求的时候就自动把这个值放进 Cookie 字段里发给服务器。 + +因为第二次请求里面有了 Cookie 字段,服务器就知道这个用户不是新人,之前来过,就可以拿出 Cookie 里的值,识别出用户的身份,然后提供个性化的服务。 + +不过因为服务器的“记忆能力”实在是太差,一张小纸条经常不够用。所以,服务器有时会在响应头里添加多个 Set-Cookie,存储多个“key=value”。但浏览器这边发送时不需要用多个 Cookie 字段,只要在一行里用“;”隔开就行。 + +我画了一张图来描述这个过程,你看过就能理解了。 + + + +从这张图中我们也能够看到,Cookie 是由浏览器负责存储的,而不是操作系统。所以,它是“浏览器绑定”的,只能在本浏览器内生效。 + +如果你换个浏览器或者换台电脑,新的浏览器里没有服务器对应的 Cookie,就好像是脱掉了贴着纸条的衣服,“健忘”的服务器也就认不出来了,只能再走一遍 Set-Cookie 流程。 + +在实验环境里,你可以用 Chrome 访问 URI“/19-1”,实地看一下 Cookie 工作过程。 + +首次访问时服务器会设置两个 Cookie。 + + + +然后刷新这个页面,浏览器就会在请求头里自动送出 Cookie,服务器就能认出你了。 + + + +如果换成 Firefox 等其他浏览器,因为 Cookie 是存在 Chrome 里的,所以服务器就又“蒙圈”了,不知道你是谁,就会给 Firefox 再贴上小纸条。 + +Cookie 的属性 + +说到这里,你应该知道了,Cookie 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。所以,就需要在“key=value”外再用一些手段来保护,防止外泄或窃取,这些手段就是 Cookie 的属性。 + +下面这个截图是实验环境“/19-2”的响应头,我来对着这个实际案例讲一下都有哪些常见的 Cookie 属性。 + + + +首先,我们应该设置 Cookie 的生存周期,也就是它的有效期,让它只能在一段时间内可用,就像是食品的“保鲜期”,一旦超过这个期限浏览器就认为是 Cookie 失效,在存储里删除,也不会发送给服务器。 + +Cookie 的有效期可以使用 Expires 和 Max-Age 两个属性来设置。 + +“Expires”俗称“过期时间”,用的是绝对时间点,可以理解为“截止日期”(deadline)。“Max-Age”用的是相对时间,单位是秒,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。 + +Expires 和 Max-Age 可以同时出现,两者的失效时间可以一致,也可以不一致,但浏览器会优先采用 Max-Age 计算失效期。 + +比如在这个例子里,Expires 标记的过期时间是“GMT 2019 年 6 月 7 号 8 点 19 分”,而 Max-Age 则只有 10 秒,如果现在是 6 月 6 号零点,那么 Cookie 的实际有效期就是“6 月 6 号零点过 10 秒”。 + +其次,我们需要设置 Cookie 的作用域,让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用。 + +作用域的设置比较简单,“Domain”和“Path”指定了 Cookie 所属的域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。 + +使用这两个属性可以为不同的域名和路径分别设置各自的 Cookie,比如“/19-1”用一个 Cookie,“/19-2”再用另外一个 Cookie,两者互不干扰。不过现实中为了省事,通常 Path 就用一个“/”或者直接省略,表示域名下的任意路径都允许使用 Cookie,让服务器自己去挑。 + +最后要考虑的就是Cookie 的安全性了,尽量不要让服务器以外的人看到。 + +写过前端的同学一定知道,在 JS 脚本里可以用 document.cookie 来读写 Cookie 数据,这就带来了安全隐患,有可能会导致“跨站脚本”(XSS)攻击窃取数据。 + +属性“HttpOnly”会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。 + +另一个属性“SameSite”可以防范“跨站请求伪造”(XSRF)攻击,设置成“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送,而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。 + +还有一个属性叫“Secure”,表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。 + +Chrome 开发者工具是查看 Cookie 的有力工具,在“Network-Cookies”里可以看到单个页面 Cookie 的各种属性,另一个“Application”面板里则能够方便地看到全站的所有 Cookie。 + + + + + +Cookie 的应用 + +现在回到我们最开始的话题,有了 Cookie,服务器就有了“记忆能力”,能够保存“状态”,那么应该如何使用 Cookie 呢? + +Cookie 最基本的一个用途就是身份识别,保存用户的登录信息,实现会话事务。 + +比如,你用账号和密码登录某电商,登录成功后网站服务器就会发给浏览器一个 Cookie,内容大概是“name=yourid”,这样就成功地把身份标签贴在了你身上。 + +之后你在网站里随便访问哪件商品的页面,浏览器都会自动把身份 Cookie 发给服务器,所以服务器总会知道你的身份,一方面免去了重复登录的麻烦,另一方面也能够自动记录你的浏览记录和购物下单(在后台数据库或者也用 Cookie),实现了“状态保持”。 + +Cookie 的另一个常见用途是广告跟踪。 + +你上网的时候肯定看过很多的广告图片,这些图片背后都是广告商网站(例如 Google),它会“偷偷地”给你贴上 Cookie 小纸条,这样你上其他的网站,别的广告就能用 Cookie 读出你的身份,然后做行为分析,再推给你广告。 + +这种 Cookie 不是由访问的主站存储的,所以又叫“第三方 Cookie”(third-party cookie)。如果广告商势力很大,广告到处都是,那么就比较“恐怖”了,无论你走到哪里它都会通过 Cookie 认出你来,实现广告“精准打击”。 + +为了防止滥用 Cookie 搜集用户隐私,互联网组织相继提出了 DNT(Do Not Track)和 P3P(Platform for Privacy Preferences Project),但实际作用不大。 + +小结 + +今天我们学习了 HTTP 里的 Cookie 知识。虽然现在已经出现了多种 Local Web Storage 技术,能够比 Cookie 存储更多的数据,但 Cookie 仍然是最通用、兼容性最强的客户端数据存储手段。 + +简单小结一下今天的内容: + + +Cookie 是服务器委托浏览器存储的一些数据,让服务器有了“记忆能力”; +响应报文使用 Set-Cookie 字段发送“key=value”形式的 Cookie 值; +请求报文里用 Cookie 字段发送多个 Cookie 值; +为了保护 Cookie,还要给它设置有效期、作用域等属性,常用的有 Max-Age、Expires、Domain、HttpOnly 等; +Cookie 最基本的用途是身份识别,实现有状态的会话事务。 + + +还要提醒你一点,因为 Cookie 并不属于 HTTP 标准(RFC6265,而不是 RFC2616/7230),所以语法上与其他字段不太一致,使用的分隔符是“;”,与 Accept 等字段的“,”不同,小心不要弄错了。 + +课下作业 + + +如果 Cookie 的 Max-Age 属性设置为 0,会有什么效果呢? +Cookie 的好处已经很清楚了,你觉得它有什么缺点呢? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/20生鲜速递:HTTP的缓存控制.md b/专栏/透视HTTP协议/20生鲜速递:HTTP的缓存控制.md new file mode 100644 index 0000000..de01bae --- /dev/null +++ b/专栏/透视HTTP协议/20生鲜速递:HTTP的缓存控制.md @@ -0,0 +1,176 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 20 生鲜速递:HTTP的缓存控制 + 缓存(Cache)是计算机领域里的一个重要概念,是优化系统性能的利器。 + +由于链路漫长,网络时延不可控,浏览器使用 HTTP 获取资源的成本较高。所以,非常有必要把“来之不易”的数据缓存起来,下次再请求的时候尽可能地复用。这样,就可以避免多次请求 - 应答的通信成本,节约网络带宽,也可以加快响应速度。 + +试想一下,如果有几十 K 甚至几十 M 的数据,不是从网络而是从本地磁盘获取,那将是多么大的一笔节省,免去多少等待的时间。 + +实际上,HTTP 传输的每一个环节基本上都会有缓存,非常复杂。 + +基于“请求 - 应答”模式的特点,可以大致分为客户端缓存和服务器端缓存,因为服务器端缓存经常与代理服务“混搭”在一起,所以今天我先讲客户端——也就是浏览器的缓存。 + +服务器的缓存控制 + +为了更好地说明缓存的运行机制,下面我用“生鲜速递”作为比喻,看看缓存是如何工作的。 + +夏天到了,天气很热。你想吃西瓜消暑,于是打开冰箱,但很不巧,冰箱是空的。不过没事,现在物流很发达,给生鲜超市打个电话,不一会儿,就给你送来一个 8 斤的沙瓤大西瓜,上面还贴着标签:“保鲜期 5 天”。好了,你把它放进冰箱,想吃的时候随时拿出来。 + +在这个场景里,“生鲜超市”就是 Web 服务器,“你”就是浏览器,“冰箱”就是浏览器内部的缓存。整个流程翻译成 HTTP 就是: + + +浏览器发现缓存无数据,于是发送请求,向服务器获取资源; +服务器响应请求,返回资源,同时标记资源的有效期; +浏览器缓存资源,等待下次重用。 + + + + +你可以访问实验环境的 URI “/20-1”,看看具体的请求 - 应答过程。 + + + +服务器标记资源有效期使用的头字段是“Cache-Control”,里面的值“max-age=30”就是资源的有效时间,相当于告诉浏览器,“这个页面只能缓存 30 秒,之后就算是过期,不能用。” + +你可能要问了,让浏览器直接缓存数据就好了,为什么要加个有效期呢? + +这是因为网络上的数据随时都在变化,不能保证它稍后的一段时间还是原来的样子。就像生鲜超市给你快递的西瓜,只有 5 天的保鲜期,过了这个期限最好还是别吃,不然可能会闹肚子。 + +“Cache-Control”字段里的“max-age”和上一讲里 Cookie 有点像,都是标记资源的有效期。 + +但我必须提醒你注意,这里的 max-age 是“生存时间”(又叫“新鲜度”“缓存寿命”,类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。 + +比如,服务器设定“max-age=5”,但因为网络质量很糟糕,等浏览器收到响应报文已经过去了 4 秒,那么这个资源在客户端就最多能够再存 1 秒钟,之后就会失效。 + +“max-age”是 HTTP 缓存控制最常用的属性,此外在响应报文里还可以用其他的属性来更精确地指示浏览器应该如何使用缓存: + + +no_store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面; +no_cache:它的字面含义容易与 no_store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本; +must-revalidate:又是一个和 no_cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。 + + +听的有点糊涂吧。没关系,我拿生鲜速递来举例说明一下: + + +no_store:买来的西瓜不允许放进冰箱,要么立刻吃,要么立刻扔掉; +no_cache:可以放进冰箱,但吃之前必须问超市有没有更新鲜的,有就吃超市里的; +must-revalidate:可以放进冰箱,保鲜期内可以吃,过期了就要问超市让不让吃。 + + +你看,这超市管的还真多啊,西瓜到了家里怎么吃还得听他。不过没办法,在 HTTP 协议里服务器就是这样的“霸气”。 + +我把服务器的缓存控制策略画了一个流程图,对照着它你就可以在今后的后台开发里明确“Cache-Control”的用法了。 + + + +客户端的缓存控制 + +现在冰箱里已经有了“缓存”的西瓜,是不是就可以直接开吃了呢? + +你可以在 Chrome 里点几次“刷新”按钮,估计你会失望,页面上的 ID 一直在变,根本不是缓存的结果,明明说缓存 30 秒,怎么就不起作用呢? + +其实不止服务器可以发“Cache-Control”头,浏览器也可以发“Cache-Control”,也就是说请求 - 应答的双方都可以用这个字段进行缓存控制,互相协商缓存的使用策略。 + +当你点“刷新”按钮的时候,浏览器会在请求头里加一个“Cache-Control: max-age=0”。因为 max-age 是“生存时间”,max-age=0 的意思就是“我要一个最最新鲜的西瓜”,而本地缓存里的数据至少保存了几秒钟,所以浏览器就不会使用缓存,而是向服务器发请求。服务器看到 max-age=0,也就会用一个最新生成的报文回应浏览器。 + +Ctrl+F5 的“强制刷新”又是什么样的呢? + +它其实是发了一个“Cache-Control: no-cache”,含义和“max-age=0”基本一样,就看后台的服务器怎么理解,通常两者的效果是相同的。 + + + +那么,浏览器的缓存究竟什么时候才能生效呢? + +别着急,试着点一下浏览器的“前进”“后退”按钮,再看开发者工具,你就会惊喜地发现“from disk cache”的字样,意思是没有发送网络请求,而是读取的磁盘上的缓存。 + +另外,如果用[第 18 讲]里的重定向跳转功能,也可以发现浏览器使用了缓存: + +http://www.chrono.com/18-1?dst=20-1 + + + + +这几个操作与刷新有什么区别呢? + +其实也很简单,在“前进”“后退”“跳转”这些重定向动作中浏览器不会“夹带私货”,只用最基本的请求头,没有“Cache-Control”,所以就会检查缓存,直接利用之前的资源,不再进行网络通信。 + +这个过程你也可以用 Wireshark 抓包,看看是否真的没有向服务器发请求。 + +条件请求 + +浏览器用“Cache-Control”做缓存控制只能是刷新数据,不能很好地利用缓存数据,又因为缓存会失效,使用前还必须要去服务器验证是否是最新版。 + +那么该怎么做呢? + +浏览器可以用两个连续的请求组成“验证动作”:先是一个 HEAD,获取资源的修改时间等元信息,然后与缓存数据比较,如果没有改动就使用缓存,节省网络流量,否则就再发一个 GET 请求,获取最新的版本。 + +但这样的两个请求网络成本太高了,所以 HTTP 协议就定义了一系列“If”开头的“条件请求”字段,专门用来检查验证资源是否过期,把两个请求才能完成的工作合并在一个请求里做。而且,验证的责任也交给服务器,浏览器只需“坐享其成”。 + +条件请求一共有 5 个头字段,我们最常用的是“if-Modified-Since”和“If-None-Match”这两个。需要第一次的响应报文预先提供“Last-modified”和“ETag”,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的。 + +如果资源没有变,服务器就回应一个“304 Not Modified”,表示缓存依然有效,浏览器就可以更新一下有效期,然后放心大胆地使用缓存了。 + + + +“Last-modified”很好理解,就是文件的最后修改时间。ETag 是什么呢? + +ETag 是“实体标签”(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。 + +比如,一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分。 + +再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。 + +使用 ETag 就可以精确地识别资源的变动情况,让浏览器能够更有效地利用缓存。 + +ETag 还有“强”“弱”之分。 + +强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。 + +还是拿生鲜速递做比喻最容易理解: + +你打电话给超市,“我这个西瓜是 3 天前买的,还有最新的吗?”。超市看了一下库存,说:“没有啊,我这里都是 3 天前的。”于是你就知道了,再让超市送货也没用,还是吃冰箱里的西瓜吧。这就是“if-Modified-Since”和“Last-modified”。 + +但你还是想要最新的,就又打电话:“有不是沙瓤的西瓜吗?”,超市告诉你都是沙瓤的(Match),于是你还是只能吃冰箱里的沙瓤西瓜。这就是“If-None-Match”和“弱 ETag”。 + +第三次打电话,你说“有不是 8 斤的沙瓤西瓜吗?”,这回超市给了你满意的答复:“有个 10 斤的沙瓤西瓜”。于是,你就扔掉了冰箱里的存货,让超市重新送了一个新的大西瓜。这就是“If-None-Match”和“强 ETag”。 + +再来看看实验环境的 URI “/20-2”。它为资源增加了 ETag 字段,刷新页面时浏览器就会同时发送缓存控制头“max-age=0”和条件请求头“If-None-Match”,如果缓存有效服务器就会返回 304: + + + +条件请求里其他的三个头字段是“If-Unmodified-Since”“If-Match”和“If-Range”,其实只要你掌握了“if-Modified-Since”和“If-None-Match”,可以轻易地“举一反三”。 + +小结 + +今天我们学习了 HTTP 的缓存控制和条件请求,用好它们可以减少响应时间、节约网络流量,一起小结一下今天的内容吧: + + +缓存是优化系统性能的重要手段,HTTP 传输的每一个环节中都可以有缓存; +服务器使用“Cache-Control”设置缓存策略,常用的是“max-age”,表示资源的有效期; +浏览器收到数据就会存入缓存,如果没过期就可以直接使用,过期就要去服务器验证是否仍然可用; +验证资源是否失效需要使用“条件请求”,常用的是“if-Modified-Since”和“If-None-Match”,收到 304 就可以复用缓存里的资源; +验证资源是否被修改的条件有两个:“Last-modified”和“ETag”,需要服务器预先在响应报文里设置,搭配条件请求使用; +浏览器也可以发送“Cache-Control”字段,使用“max-age=0”或“no_cache”刷新数据。 + + +HTTP 缓存看上去很复杂,但基本原理说白了就是一句话:“没有消息就是好消息”,“没有请求的请求,才是最快的请求。” + +课下作业 + + +Cache 和 Cookie 都是服务器发给客户端并存储的数据,你能比较一下两者的异同吗? +即使有“Last-modified”和“ETag”,强制刷新(Ctrl+F5)也能够从服务器获取最新数据(返回 200 而不是 304),请你在实验环境里试一下,观察请求头和响应头,解释原因。 + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/21良心中间商:HTTP的代理服务.md b/专栏/透视HTTP协议/21良心中间商:HTTP的代理服务.md new file mode 100644 index 0000000..140c2ac --- /dev/null +++ b/专栏/透视HTTP协议/21良心中间商:HTTP的代理服务.md @@ -0,0 +1,158 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 21 良心中间商:HTTP的代理服务 + 在前面讲 HTTP 协议的时候,我们严格遵循了 HTTP 的“请求 - 应答”模型,协议中只有两个互相通信的角色,分别是“请求方”浏览器(客户端)和“应答方”服务器。 + +今天,我们要在这个模型里引入一个新的角色,那就是HTTP 代理。 + +引入 HTTP 代理后,原来简单的双方通信就变复杂了一些,加入了一个或者多个中间人,但整体上来看,还是一个有顺序关系的链条,而且链条里相邻的两个角色仍然是简单的一对一通信,不会出现越级的情况。 + + + +链条的起点还是客户端(也就是浏览器),中间的角色被称为代理服务器(proxy server),链条的终点被称为源服务器(origin server),意思是数据的“源头”“起源”。 + +代理服务 + +“代理”这个词听起来好像很神秘,有点“高大上”的感觉。 + +但其实 HTTP 协议里对它并没有什么特别的描述,它就是在客户端和服务器原本的通信链路中插入的一个中间环节,也是一台服务器,但提供的是“代理服务”。 + +所谓的“代理服务”就是指服务本身不生产内容,而是处于中间位置转发上下游的请求和响应,具有双重身份:面向下游的用户时,表现为服务器,代表源服务器响应客户端的请求;而面向上游的源服务器时,又表现为客户端,代表客户端发送请求。 + +还是拿上一讲的“生鲜超市”来打个比方。 + +之前你都是从超市里买东西,现在楼底下新开了一家 24 小时便利店,由超市直接供货,于是你就可以在便利店里买到原本必须去超市才能买到的商品。 + +这样超市就不直接和你打交道了,成了“源服务器”,便利店就成了超市的“代理服务器”。 + +在[第 4 讲]中,我曾经说过,代理有很多的种类,例如匿名代理、透明代理、正向代理和反向代理。 + +今天我主要讲的是实际工作中最常见的反向代理,它在传输链路中更靠近源服务器,为源服务器提供代理服务。 + +代理的作用 + +为什么要有代理呢?换句话说,代理能干什么、带来什么好处呢? + +你也许听过这样一句至理名言:“计算机科学领域里的任何问题,都可以通过引入一个中间层来解决”(在这句话后面还可以再加上一句“如果一个中间层解决不了问题,那就再加一个中间层”)。TCP/IP 协议栈是这样,而代理也是这样。 + +由于代理处在 HTTP 通信过程的中间位置,相应地就对上屏蔽了真实客户端,对下屏蔽了真实服务器,简单的说就是“欺上瞒下”。在这个中间层的“小天地”里就可以做很多的事情,为 HTTP 协议增加更多的灵活性,实现客户端和服务器的“双赢”。 + +代理最基本的一个功能是负载均衡。因为在面向客户端时屏蔽了源服务器,客户端看到的只是代理服务器,源服务器究竟有多少台、是哪些 IP 地址都不知道。于是代理服务器就可以掌握请求分发的“大权”,决定由后面的哪台服务器来响应请求。 + + + +代理中常用的负载均衡算法你应该也有所耳闻吧,比如轮询、一致性哈希等等,这些算法的目标都是尽量把外部的流量合理地分散到多台源服务器,提高系统的整体资源利用率和性能。 + +在负载均衡的同时,代理服务还可以执行更多的功能,比如: + + +健康检查:使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用; +安全防护:保护被代理的后端服务器,限制 IP 地址或流量,抵御网络攻击和过载; +加密卸载:对外网使用 SSL/TLS 加密通信认证,而在安全的内网不加密,消除加解密成本; +数据过滤:拦截上下行的数据,任意指定策略修改请求或者响应; +内容缓存:暂存、复用服务器响应,这个与[第 20 讲]密切相关,我们稍后再说。 + + +接着拿刚才的便利店来举例说明。 + +因为便利店和超市之间是专车配送,所以有了便利店,以后你买东西就更省事了,打电话给便利店让它去帮你取货,不用关心超市是否停业休息、是否人满为患,而且总能买到最新鲜的。 + +便利店同时也方便了超市,不用额外加大店面就可以增加客源和销量,货物集中装卸也节省了物流成本,由于便利店直接面对客户,所以也可以把恶意骚扰电话挡在外面。 + +代理相关头字段 + +代理的好处很多,但因为它“欺上瞒下”的特点,隐藏了真实客户端和服务器,如果双方想要获得这些“丢失”的原始信息,该怎么办呢? + +首先,代理服务器需要用字段“Via”标明代理的身份。 + +Via 是一个通用字段,请求头或响应头里都可以出现。每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾,就像是经手人盖了一个章。 + +如果通信链路中有很多中间代理,就会在 Via 里形成一个链表,这样就可以知道报文究竟走过了多少个环节才到达了目的地。 + +例如下图中有两个代理:proxy1 和 proxy2,客户端发送请求会经过这两个代理,依次添加就是“Via: proxy1, proxy2”,等到服务器返回响应报文的时候就要反过来走,头字段就是“Via: proxy2, proxy1”。 + + + +Via 字段只解决了客户端和源服务器判断是否存在代理的问题,还不能知道对方的真实信息。 + +但服务器的 IP 地址应该是保密的,关系到企业的内网安全,所以一般不会让客户端知道。不过反过来,通常服务器需要知道客户端的真实 IP 地址,方便做访问控制、用户画像、统计分析。 + +可惜的是 HTTP 标准里并没有为此定义头字段,但已经出现了很多“事实上的标准”,最常用的两个头字段是“X-Forwarded-For”和“X-Real-IP”。 + +“X-Forwarded-For”的字面意思是“为谁而转发”,形式上和“Via”差不多,也是每经过一个代理节点就会在字段里追加一个信息。但“Via”追加的是代理主机名(或者域名),而“X-Forwarded-For”追加的是请求方的 IP 地址。所以,在字段里最左边的 IP 地址就客户端的地址。 + +“X-Real-IP”是另一种获取客户端真实 IP 的手段,它的作用很简单,就是记录客户端 IP 地址,没有中间的代理信息,相当于是“X-Forwarded-For”的简化版。如果客户端和源服务器之间只有一个代理,那么这两个字段的值就是相同的。 + +我们的实验环境实现了一个反向代理,访问“http://www.chrono.com/21-1”,它会转而访问“http://origin.io”。这里的“origin.io”就是源站,它会在响应报文里输出“Via”“X-Forwarded-For”等代理头字段信息: + + + +单从浏览器的页面上很难看出代理做了哪些工作,因为代理的转发都在后台不可见,所以我把这个过程用 Wireshark 抓了一个包: + + + +从抓包里就可以清晰地看出代理与客户端、源服务器的通信过程: + + +客户端 55061 先用三次握手连接到代理的 80 端口,然后发送 GET 请求; +代理不直接生产内容,所以就代表客户端,用 55063 端口连接到源服务器,也是三次握手; +代理成功连接源服务器后,发出了一个 HTTP/1.0 的 GET 请求; +因为 HTTP/1.0 默认是短连接,所以源服务器发送响应报文后立即用四次挥手关闭连接; +代理拿到响应报文后再发回给客户端,完成了一次代理服务。 + + +在这个实验中,你可以看到除了“X-Forwarded-For”和“X-Real-IP”,还出现了两个字段:“X-Forwarded-Host”和“X-Forwarded-Proto”,它们的作用与“X-Real-IP”类似,只记录客户端的信息,分别是客户端请求的原始域名和原始协议名。 + +代理协议 + +有了“X-Forwarded-For”等头字段,源服务器就可以拿到准确的客户端信息了。但对于代理服务器来说它并不是一个最佳的解决方案。 + +因为通过“X-Forwarded-For”操作代理信息必须要解析 HTTP 报文头,这对于代理来说成本比较高,原本只需要简单地转发消息就好,而现在却必须要费力解析数据再修改数据,会降低代理的转发性能。 + +另一个问题是“X-Forwarded-For”等头必须要修改原始报文,而有些情况下是不允许甚至不可能的(比如使用 HTTPS 通信被加密)。 + +所以就出现了一个专门的“代理协议”(The PROXY protocol),它由知名的代理软件 HAProxy 所定义,也是一个“事实标准”,被广泛采用(注意并不是 RFC)。 + +“代理协议”有 v1 和 v2 两个版本,v1 和 HTTP 差不多,也是明文,而 v2 是二进制格式。今天只介绍比较好理解的 v1,它在 HTTP 报文前增加了一行 ASCII 码文本,相当于又多了一个头。 + +这一行文本其实非常简单,开头必须是“PROXY”五个大写字母,然后是“TCP4”或者“TCP6”,表示客户端的 IP 地址类型,再后面是请求方地址、应答方地址、请求方端口号、应答方端口号,最后用一个回车换行(\r\n)结束。 + +例如下面的这个例子,在 GET 请求行前多出了 PROXY 信息行,客户端的真实 IP 地址是“1.1.1.1”,端口号是 55555。 + +PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n +GET / HTTP/1.1\r\n +Host: www.xxx.com\r\n +\r\n + + +服务器看到这样的报文,只要解析第一行就可以拿到客户端地址,不需要再去理会后面的 HTTP 数据,省了很多事情。 + +不过代理协议并不支持“X-Forwarded-For”的链式地址形式,所以拿到客户端地址后再如何处理就需要代理服务器与后端自行约定。 + +小结 + + +HTTP 代理就是客户端和服务器通信链路中的一个中间环节,为两端提供“代理服务”; +代理处于中间层,为 HTTP 处理增加了更多的灵活性,可以实现负载均衡、安全防护、数据过滤等功能; +代理服务器需要使用字段“Via”标记自己的身份,多个代理会形成一个列表; +如果想要知道客户端的真实 IP 地址,可以使用字段“X-Forwarded-For”和“X-Real-IP”; +专门的“代理协议”可以在不改动原始报文的情况下传递客户端的真实 IP。 + + +课下作业 + + +你觉得代理有什么缺点?实际应用时如何避免? +你知道多少反向代理中使用的负载均衡算法?它们有什么优缺点? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/22冷链周转:HTTP的缓存代理.md b/专栏/透视HTTP协议/22冷链周转:HTTP的缓存代理.md new file mode 100644 index 0000000..1f81d36 --- /dev/null +++ b/专栏/透视HTTP协议/22冷链周转:HTTP的缓存代理.md @@ -0,0 +1,147 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 22 冷链周转:HTTP的缓存代理 + 在[第 20 讲]中,我介绍了 HTTP 的缓存控制,[第 21 讲]我介绍了 HTTP 的代理服务。那么,把这两者结合起来就是这节课所要说的“缓存代理”,也就是支持缓存控制的代理服务。 + +之前谈到缓存时,主要讲了客户端(浏览器)上的缓存控制,它能够减少响应时间、节约带宽,提升客户端的用户体验。 + +但 HTTP 传输链路上,不只是客户端有缓存,服务器上的缓存也是非常有价值的,可以让请求不必走完整个后续处理流程,“就近”获得响应结果。 + +特别是对于那些“读多写少”的数据,例如突发热点新闻、爆款商品的详情页,一秒钟内可能有成千上万次的请求。即使仅仅缓存数秒钟,也能够把巨大的访问流量挡在外面,让 RPS(request per second)降低好几个数量级,减轻应用服务器的并发压力,对性能的改善是非常显著的。 + +HTTP 的服务器缓存功能主要由代理服务器来实现(即缓存代理),而源服务器系统内部虽然也经常有各种缓存(如 Memcache、Redis、Varnish 等),但与 HTTP 没有太多关系,所以这里暂且不说。 + +缓存代理服务 + +我还是沿用“生鲜速递 + 便利店”的比喻,看看缓存代理是怎么回事。 + +便利店作为超市的代理,生意非常红火,顾客和超市双方都对现状非常满意。但时间一长,超市发现还有进一步提升的空间,因为每次便利店接到顾客的请求后都要专车跑一趟超市,还是挺麻烦的。 + +干脆这样吧,给便利店配发一个大冰柜。水果海鲜什么的都可以放在冰柜里,只要产品在保鲜期内,就允许顾客直接从冰柜提货。这样便利店就可以一次进货多次出货,省去了超市之间的运输成本。 + + + +通过这个比喻,你可以看到:在没有缓存的时候,代理服务器每次都是直接转发客户端和服务器的报文,中间不会存储任何数据,只有最简单的中转功能。 + +加入了缓存后就不一样了。 + +代理服务收到源服务器发来的响应数据后需要做两件事。第一个当然是把报文转发给客户端,而第二个就是把报文存入自己的 Cache 里。 + +下一次再有相同的请求,代理服务器就可以直接发送 304 或者缓存数据,不必再从源服务器那里获取。这样就降低了客户端的等待时间,同时节约了源服务器的网络带宽。 + +在 HTTP 的缓存体系中,缓存代理的身份十分特殊,它“既是客户端,又是服务器”,同时也“既不是客户端,又不是服务器”。 + +说它“即是客户端又是服务器”,是因为它面向源服务器时是客户端,在面向客户端时又是服务器,所以它即可以用客户端的缓存控制策略也可以用服务器端的缓存控制策略,也就是说它可以同时使用第 20 讲的各种“Cache-Control”属性。 + +但缓存代理也“即不是客户端又不是服务器”,因为它只是一个数据的“中转站”,并不是真正的数据消费者和生产者,所以还需要有一些新的“Cache-Control”属性来对它做特别的约束。 + +源服务器的缓存控制 + +[第 20 讲]介绍了 4 种服务器端的“Cache-Control”属性:max-age、no_store、no_cache 和 must-revalidate,你应该还有印象吧? + +这 4 种缓存属性可以约束客户端,也可以约束代理。 + +但客户端和代理是不一样的,客户端的缓存只是用户自己使用,而代理的缓存可能会为非常多的客户端提供服务。所以,需要对它的缓存再多一些限制条件。 + +首先,我们要区分客户端上的缓存和代理上的缓存,可以使用两个新属性“private”和“public”。 + +“private”表示缓存只能在客户端保存,是用户“私有”的,不能放在代理上与别人共享。而“public”的意思就是缓存完全开放,谁都可以存,谁都可以用。 + +比如你登录论坛,返回的响应报文里用“Set-Cookie”添加了论坛 ID,这就属于私人数据,不能存在代理上。不然,别人访问代理获取了被缓存的响应就麻烦了。 + +其次,缓存失效后的重新验证也要区分开(即使用条件请求“Last-modified”和“ETag”),“must-revalidate”是只要过期就必须回源服务器验证,而新的“proxy-revalidate”只要求代理的缓存过期后必须验证,客户端不必回源,只验证到代理这个环节就行了。 + +再次,缓存的生存时间可以使用新的“s-maxage”(s 是 share 的意思,注意 maxage 中间没有“-”),只限定在代理上能够存多久,而客户端仍然使用“max_age”。 + +还有一个代理专用的属性“no-transform”。代理有时候会对缓存下来的数据做一些优化,比如把图片生成 png、webp 等几种格式,方便今后的请求处理,而“no-transform”就会禁止这样做,不许“偷偷摸摸搞小动作”。 + +这些新的缓存控制属性比较复杂,还是用“便利店冷柜”来举例好理解一些。 + +水果上贴着标签“private, max-age=5”。这就是说水果不能放进冷柜,必须直接给顾客,保鲜期 5 天,过期了还得去超市重新进货。 + +冻鱼上贴着标签“public, max-age=5, s-maxage=10”。这个的意思就是可以在冰柜里存 10 天,但顾客那里只能存 5 天,过期了可以来便利店取,只要在 10 天之内就不必再找超市。 + +排骨上贴着标签“max-age=30, proxy-revalidate, no-transform”。因为缓存默认是 public 的,那么它在便利店和顾客的冰箱里就都可以存 30 天,过期后便利店必须去超市进新货,而且不能擅自把“大排”改成“小排”。 + +下面的流程图是完整的服务器端缓存控制策略,可以同时控制客户端和代理。 + + + +我还要提醒你一点,源服务器在设置完“Cache-Control”后必须要为报文加上“Last-modified”或“ETag”字段。否则,客户端和代理后面就无法使用条件请求来验证缓存是否有效,也就不会有 304 缓存重定向。 + +客户端的缓存控制 + +说完了服务器端的缓存控制策略,稍微歇一口气,我们再来看看客户端。 + +客户端在 HTTP 缓存体系里要面对的是代理和源服务器,也必须区别对待,这里我就直接上图了,来个“看图说话”。 + + + +max-age、no_store、no_cache 这三个属性在[第 20 讲]已经介绍过了,它们也是同样作用于代理和源服务器。 + +关于缓存的生存时间,多了两个新属性“max-stale”和“min-fresh”。 + +“max-stale”的意思是如果代理上的缓存过期了也可以接受,但不能过期太多,超过 x 秒也会不要。“min-fresh”的意思是缓存必须有效,而且必须在 x 秒后依然有效。 + +比如,草莓上贴着标签“max-age=5”,现在已经在冰柜里存了 7 天。如果有请求“max-stale=2”,意思是过期两天也能接受,所以刚好能卖出去。 + +但要是“min-fresh=1”,这是绝对不允许过期的,就不会买走。这时如果有另外一个菠萝是“max-age=10”,那么“7+1 + +有的时候客户端还会发出一个特别的“only-if-cached”属性,表示只接受代理缓存的数据,不接受源服务器的响应。如果代理上没有缓存或者缓存过期,就应该给客户端返回一个 504(Gateway Timeout)。 + +实验环境 + +信息量有些大,到这里你是不是有点头疼了,好在我们还有实验环境,用 URI“/22-1”试一下吧。 + +它设置了“Cache-Control: public, max-age=10, s-maxage=30”,数据可以在浏览器里存 10 秒,在代理上存 30 秒,你可以反复刷新,看看代理和源服务器是怎么响应的,同样也可以配合 Wireshark 抓包。 + +代理在响应报文里还额外加了“X-Cache”“X-Hit”等自定义头字段,表示缓存是否命中和命中率,方便你观察缓存代理的工作情况。 + + + +其他问题 + +缓存代理的知识就快讲完了,下面再简单说两个相关的问题。 + +第一个是“Vary”字段,在[第 15 讲]曾经说过,它是内容协商的结果,相当于报文的一个版本标记。 + +同一个请求,经过内容协商后可能会有不同的字符集、编码、浏览器等版本。比如,“Vary: Accept-Encoding”“Vary: User-Agent”,缓存代理必须要存储这些不同的版本。 + +当再收到相同的请求时,代理就读取缓存里的“Vary”,对比请求头里相应的“ Accept-Encoding”“User-Agent”等字段,如果和上一个请求的完全匹配,比如都是“gzip”“Chrome”,就表示版本一致,可以返回缓存的数据。 + +另一个问题是“Purge”,也就是“缓存清理”,它对于代理也是非常重要的功能,例如: + + +过期的数据应该及时淘汰,避免占用空间; +源站的资源有更新,需要删除旧版本,主动换成最新版(即刷新); +有时候会缓存了一些本不该存储的信息,例如网络谣言或者危险链接,必须尽快把它们删除。 + + +清理缓存的方法有很多,比较常用的一种做法是使用自定义请求方法“PURGE”,发给代理服务器,要求删除 URI 对应的缓存数据。 + +小结 + + +计算机领域里最常用的性能优化手段是“时空转换”,也就是“时间换空间”或者“空间换时间”,HTTP 缓存属于后者; +缓存代理是增加了缓存功能的代理服务,缓存源服务器的数据,分发给下游的客户端; +“Cache-Control”字段也可以控制缓存代理,常用的有“private”“s-maxage”“no-transform”等,同样必须配合“Last-modified”“ETag”等字段才能使用; +缓存代理有时候也会带来负面影响,缓存不良数据,需要及时刷新或删除。 + + +课下作业 + + +加入了代理后 HTTP 的缓存复杂了很多,试着用自己的语言把这些知识再整理一下,画出有缓存代理时浏览器的工作流程图,加深理解。 +缓存的时间策略很重要,太大太小都不好,你觉得应该如何设置呢? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/23HTTPS是什么?SSLTLS又是什么?.md b/专栏/透视HTTP协议/23HTTPS是什么?SSLTLS又是什么?.md new file mode 100644 index 0000000..b21e496 --- /dev/null +++ b/专栏/透视HTTP协议/23HTTPS是什么?SSLTLS又是什么?.md @@ -0,0 +1,146 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 23 HTTPS是什么?SSLTLS又是什么? + 从今天开始,我们开始进入全新的“安全篇”,聊聊与安全相关的 HTTPS、SSL、TLS。 + +在[第 14 讲]中,我曾经谈到过 HTTP 的一些缺点,其中的“无状态”在加入 Cookie 后得到了解决,而另两个缺点——“明文”和“不安全”仅凭 HTTP 自身是无力解决的,需要引入新的 HTTPS 协议。 + +为什么要有 HTTPS? + +简单的回答是“因为 HTTP 不安全”。 + +由于 HTTP 天生“明文”的特点,整个传输过程完全透明,任何人都能够在链路中截获、修改或者伪造请求 / 响应报文,数据不具有可信性。 + +比如,前几讲中说过的“代理服务”。它作为 HTTP 通信的中间人,在数据上下行的时候可以添加或删除部分头字段,也可以使用黑白名单过滤 body 里的关键字,甚至直接发送虚假的请求、响应,而浏览器和源服务器都没有办法判断报文的真伪。 + +这对于网络购物、网上银行、证券交易等需要高度信任的应用场景来说是非常致命的。如果没有基本的安全保护,使用互联网进行各种电子商务、电子政务就根本无从谈起。 + +对于安全性要求不那么高的新闻、视频、搜索等网站来说,由于互联网上的恶意用户、恶意代理越来越多,也很容易遭到“流量劫持”的攻击,在页面里强行嵌入广告,或者分流用户,导致各种利益损失。 + +对于你我这样的普通网民来说,HTTP 不安全的隐患就更大了,上网的记录会被轻易截获,网站是否真实也无法验证,黑客可以伪装成银行网站,盗取真实姓名、密码、银行卡等敏感信息,威胁人身安全和财产安全。 + +总的来说,今天的互联网已经不再是早期的“田园牧歌”时代,而是进入了“黑暗森林”状态。上网的时候必须步步为营、处处小心,否则就会被不知道埋伏在哪里的黑客所“猎杀”。 + +什么是安全? + +既然 HTTP“不安全”,那什么样的通信过程才是安全的呢? + +通常认为,如果通信过程具备了四个特性,就可以认为是“安全”的,这四个特性是:机密性、完整性,身份认证和不可否认。 + +机密性(Secrecy/Confidentiality)是指对数据的“保密”,只能由可信的人访问,对其他人是不可见的“秘密”,简单来说就是不能让不相关的人看到不该看的东西。 + +比如小明和小红私下聊天,但“隔墙有耳”,被小强在旁边的房间里全偷听到了,这就是没有机密性。我们之前一直用的 Wireshark ,实际上也是利用了 HTTP 的这个特点,捕获了传输过程中的所有数据。 + +完整性(Integrity,也叫一致性)是指数据在传输过程中没有被窜改,不多也不少,“完完整整”地保持着原状。 + +机密性虽然可以让数据成为“秘密”,但不能防止黑客对数据的修改,黑客可以替换数据,调整数据的顺序,或者增加、删除部分数据,破坏通信过程。 + +比如,小明给小红写了张纸条:“明天公园见”。小强把“公园”划掉,模仿小明的笔迹把这句话改成了“明天广场见”。小红收到后无法验证完整性,信以为真,第二天的约会就告吹了。 + +身份认证(Authentication)是指确认对方的真实身份,也就是“证明你真的是你”,保证消息只能发送给可信的人。 + +如果通信时另一方是假冒的网站,那么数据再保密也没有用,黑客完全可以使用冒充的身份“套”出各种信息,加密和没加密一样。 + +比如,小明给小红写了封情书:“我喜欢你”,但不留心发给了小强。小强将错就错,假冒小红回复了一个“白日做梦”,小明不知道这其实是小强的话,误以为是小红的,后果可想而知。 + +第四个特性是不可否认(Non-repudiation/Undeniable),也叫不可抵赖,意思是不能否认已经发生过的行为,不能“说话不算数”“耍赖皮”。 + +使用前三个特性,可以解决安全通信的大部分问题,但如果缺了不可否认,那通信的事务真实性就得不到保证,有可能出现“老赖”。 + +比如,小明借了小红一千元,没写借条,第二天矢口否认,小红也确实拿不出借钱的证据,只能认倒霉。另一种情况是小明借钱后还了小红,但没写收条,小红于是不承认小明还钱的事,说根本没还,要小明再掏出一千元。 + +所以,只有同时具备了机密性、完整性、身份认证、不可否认这四个特性,通信双方的利益才能有保障,才能算得上是真正的安全。 + +什么是 HTTPS? + +说到这里,终于轮到今天的主角 HTTPS 出场了,它为 HTTP 增加了刚才所说的四大安全特性。 + +HTTPS 其实是一个“非常简单”的协议,RFC 文档很小,只有短短的 7 页,里面规定了新的协议名“https”,默认端口号 443,至于其他的什么请求 - 应答模式、报文结构、请求方法、URI、头字段、连接管理等等都完全沿用 HTTP,没有任何新的东西。 + +也就是说,除了协议名“http”和端口号 80 这两点不同,HTTPS 协议在语法、语义上和 HTTP 完全一样,优缺点也“照单全收”(当然要除去“明文”和“不安全”)。 + +不信你可以用 URI“https://www.chrono.com”访问之前 08 至 21 讲的所有示例,看看它的响应报文是否与 HTTP 一样。 + +https://www.chrono.com +https://www.chrono.com/11-1 +https://www.chrono.com/15-1?name=a.json +https://www.chrono.com/16-1 + + + + +你肯定已经注意到了,在用 HTTPS 访问实验环境时 Chrome 会有不安全提示,必须点击“高级 - 继续前往”才能顺利显示页面。而且如果用 Wireshark 抓包,也会发现与 HTTP 不一样,不再是简单可见的明文,多了“Client Hello”“Server Hello”等新的数据包。 + +这就是 HTTPS 与 HTTP 最大的区别,它能够鉴别危险的网站,并且尽最大可能保证你的上网安全,防御黑客对信息的窃听、窜改或者“钓鱼”、伪造。 + +你可能要问了,既然没有新东西,HTTPS 凭什么就能做到机密性、完整性这些安全特性呢? + +秘密就在于 HTTPS 名字里的“S”,它把 HTTP 下层的传输协议由 TCP/IP 换成了 SSL/TLS,由“HTTP over TCP/IP”变成了“HTTP over SSL/TLS”,让 HTTP 运行在了安全的 SSL/TLS 协议上(可参考第 4 讲和第 5 讲),收发报文不再使用 Socket API,而是调用专门的安全接口。 + + + +所以说,HTTPS 本身并没有什么“惊世骇俗”的本事,全是靠着后面的 SSL/TLS“撑腰”。只要学会了 SSL/TLS,HTTPS 自然就“手到擒来”。 + +SSL/TLS + +现在我们就来看看 SSL/TLS,它到底是个什么来历。 + +SSL 即安全套接层(Secure Sockets Layer),在 OSI 模型中处于第 5 层(会话层),由网景公司于 1994 年发明,有 v2 和 v3 两个版本,而 v1 因为有严重的缺陷从未公开过。 + +SSL 发展到 v3 时已经证明了它自身是一个非常好的安全通信协议,于是互联网工程组 IETF 在 1999 年把它改名为 TLS(传输层安全,Transport Layer Security),正式标准化,版本号从 1.0 重新算起,所以 TLS1.0 实际上就是 SSLv3.1。 + +到今天 TLS 已经发展出了三个版本,分别是 2006 年的 1.1、2008 年的 1.2 和去年(2018)的 1.3,每个新版本都紧跟密码学的发展和互联网的现状,持续强化安全和性能,已经成为了信息安全领域中的权威标准。 + +目前应用的最广泛的 TLS 是 1.2,而之前的协议(TLS1.1⁄1.0、SSLv3/v2)都已经被认为是不安全的,各大浏览器即将在 2020 年左右停止支持,所以接下来的讲解都针对的是 TLS1.2。 + +TLS 由记录协议、握手协议、警告协议、变更密码规范协议、扩展协议等几个子协议组成,综合使用了对称加密、非对称加密、身份认证等许多密码学前沿技术。 + +浏览器和服务器在使用 TLS 建立连接时需要选择一组恰当的加密算法来实现安全通信,这些算法的组合被称为“密码套件”(cipher suite,也叫加密套件)。 + +你可以访问实验环境的 URI“/23-1”,对 TLS 和密码套件有个感性的认识。 + + + +你可以看到,实验环境使用的 TLS 是 1.2,客户端和服务器都支持非常多的密码套件,而最后协商选定的是“ECDHE-RSA-AES256-GCM-SHA384”。 + +这么长的名字看着有点晕吧,不用怕,其实 TLS 的密码套件命名非常规范,格式很固定。基本的形式是“密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法”,比如刚才的密码套件的意思就是: + +“握手时使用 ECDHE 算法进行密钥交换,用 RSA 签名和身份认证,握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM,摘要算法 SHA384 用于消息认证和产生随机数。” + +OpenSSL + +说到 TLS,就不能不谈到 OpenSSL,它是一个著名的开源密码学程序库和工具包,几乎支持所有公开的加密算法和协议,已经成为了事实上的标准,许多应用软件都会使用它作为底层库来实现 TLS 功能,包括常用的 Web 服务器 Apache、Nginx 等。 + +OpenSSL 是从另一个开源库 SSLeay 发展出来的,曾经考虑命名为“OpenTLS”,但当时(1998 年)TLS 还未正式确立,而 SSL 早已广为人知,所以最终使用了“OpenSSL”的名字。 + +OpenSSL 目前有三个主要的分支,1.0.2 和 1.1.0 都将在今年(2019)年底不再维护,最新的长期支持版本是 1.1.1,我们的实验环境使用的 OpenSSL 是“1.1.0j”。 + +由于 OpenSSL 是开源的,所以它还有一些代码分支,比如 Google 的 BoringSSL、OpenBSD 的 LibreSSL,这些分支在 OpenSSL 的基础上删除了一些老旧代码,也增加了一些新特性,虽然背后有“大金主”,但离取代 OpenSSL 还差得很远。 + +小结 + + +因为 HTTP 是明文传输,所以不安全,容易被黑客窃听或窜改; +通信安全必须同时具备机密性、完整性,身份认证和不可否认这四个特性; +HTTPS 的语法、语义仍然是 HTTP,但把下层的协议由 TCP/IP 换成了 SSL/TLS; +SSL/TLS 是信息安全领域中的权威标准,采用多种先进的加密技术保证通信安全; +OpenSSL 是著名的开源密码学工具包,是 SSL/TLS 的具体实现。 + + +课下作业 + + +你能说出 HTTPS 与 HTTP 有哪些区别吗? +你知道有哪些方法能够实现机密性、完整性等安全特性呢? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/24固若金汤的根本(上):对称加密与非对称加密.md b/专栏/透视HTTP协议/24固若金汤的根本(上):对称加密与非对称加密.md new file mode 100644 index 0000000..1273a8c --- /dev/null +++ b/专栏/透视HTTP协议/24固若金汤的根本(上):对称加密与非对称加密.md @@ -0,0 +1,152 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 24 固若金汤的根本(上):对称加密与非对称加密 + 在上一讲中,我们初步学习了 HTTPS,知道 HTTPS 的安全性是由 TLS 来保证的。 + +你一定很好奇,它是怎么为 HTTP 增加了机密性、完整性,身份认证和不可否认等特性的呢? + +先说说机密性。它是信息安全的基础,缺乏机密性 TLS 就会成为“无水之源”“无根之木”。 + +实现机密性最常用的手段是“加密”(encrypt),就是把消息用某种方式转换成谁也看不懂的乱码,只有掌握特殊“钥匙”的人才能再转换出原始文本。 + +这里的“钥匙”就叫做“密钥”(key),加密前的消息叫“明文”(plain text/clear text),加密后的乱码叫“密文”(cipher text),使用密钥还原明文的过程叫“解密”(decrypt),是加密的反操作,加密解密的操作过程就是“加密算法”。 + +所有的加密算法都是公开的,任何人都可以去分析研究,而算法使用的“密钥”则必须保密。那么,这个关键的“密钥”又是什么呢? + +由于 HTTPS、TLS 都运行在计算机上,所以“密钥”就是一长串的数字,但约定俗成的度量单位是“位”(bit),而不是“字节”(byte)。比如,说密钥长度是 128,就是 16 字节的二进制串,密钥长度 1024,就是 128 字节的二进制串。 + +按照密钥的使用方式,加密可以分为两大类:对称加密和非对称加密。 + +对称加密 + +“对称加密”很好理解,就是指加密和解密时使用的密钥都是同一个,是“对称”的。只要保证了密钥的安全,那整个通信过程就可以说具有了机密性。 + +举个例子,你想要登录某网站,只要事先和它约定好使用一个对称密码,通信过程中传输的全是用密钥加密后的密文,只有你和网站才能解密。黑客即使能够窃听,看到的也只是乱码,因为没有密钥无法解出明文,所以就实现了机密性。 + + + +TLS 里有非常多的对称加密算法可供选择,比如 RC4、DES、3DES、AES、ChaCha20 等,但前三种算法都被认为是不安全的,通常都禁止使用,目前常用的只有 AES 和 ChaCha20。 + +AES 的意思是“高级加密标准”(Advanced Encryption Standard),密钥长度可以是 128、192 或 256。它是 DES 算法的替代者,安全强度很高,性能也很好,而且有的硬件还会做特殊优化,所以非常流行,是应用最广泛的对称加密算法。 + +ChaCha20 是 Google 设计的另一种加密算法,密钥长度固定为 256 位,纯软件运行性能要超过 AES,曾经在移动客户端上比较流行,但 ARMv8 之后也加入了 AES 硬件优化,所以现在不再具有明显的优势,但仍然算得上是一个不错算法。 + +加密分组模式 + +对称算法还有一个“分组模式”的概念,它可以让算法用固定长度的密钥加密任意长度的明文,把小秘密(即密钥)转化为大秘密(即密文)。 + +最早有 ECB、CBC、CFB、OFB 等几种分组模式,但都陆续被发现有安全漏洞,所以现在基本都不怎么用了。最新的分组模式被称为 AEAD(Authenticated Encryption with Associated Data),在加密的同时增加了认证的功能,常用的是 GCM、CCM 和 Poly1305。 + +把上面这些组合起来,就可以得到 TLS 密码套件中定义的对称加密算法。 + +比如,AES128-GCM,意思是密钥长度为 128 位的 AES 算法,使用的分组模式是 GCM;ChaCha20-Poly1305 的意思是 ChaCha20 算法,使用的分组模式是 Poly1305。 + +你可以用实验环境的 URI“/24-1”来测试 OpenSSL 里的 AES128-CBC,在 URI 后用参数“key”“plain”输入密钥和明文,服务器会在响应报文里输出加密解密的结果。 + +https://www.chrono.com/24-1?key=123456 + +algo = aes_128_cbc +plain = hello openssl +enc = 93a024a94083bc39fb2c2b9f5ce27c09 +dec = hello openssl + + +非对称加密 + +对称加密看上去好像完美地实现了机密性,但其中有一个很大的问题:如何把密钥安全地传递给对方,术语叫“密钥交换”。 + +因为在对称加密算法中只要持有密钥就可以解密。如果你和网站约定的密钥在传递途中被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言了。 + +这个问题该怎么解决呢? + +你或许会说:“把密钥再加密一下发过去就好了”,但传输“加密密钥的密钥”又成了新问题。这就像是“鸡生蛋、蛋生鸡”,可以无限递归下去。只用对称加密算法,是绝对无法解决密钥交换的问题的。 + +所以,就出现了非对称加密(也叫公钥加密算法)。 + +它有两个密钥,一个叫“公钥”(public key),一个叫“私钥”(private key)。两个密钥是不同的,“不对称”,公钥可以公开给任何人使用,而私钥必须严格保密。 + +公钥和私钥有个特别的“单向”性,虽然都可以用来加密解密,但公钥加密后只能用私钥解密,反过来,私钥加密后也只能用公钥解密。 + +非对称加密可以解决“密钥交换”的问题。网站秘密保管私钥,在网上任意分发公钥,你想要登录网站只要用公钥加密就行了,密文只能由私钥持有者才能解密。而黑客因为没有私钥,所以就无法破解密文。 + + + +非对称加密算法的设计要比对称算法难得多,在 TLS 里只有很少的几种,比如 DH、DSA、RSA、ECC 等。 + +RSA 可能是其中最著名的一个,几乎可以说是非对称加密的代名词,它的安全性基于“整数分解”的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的。 + +10 年前 RSA 密钥的推荐长度是 1024,但随着计算机运算能力的提高,现在 1024 已经不安全,普遍认为至少要 2048 位。 + +ECC(Elliptic Curve Cryptography)是非对称加密里的“后起之秀”,它基于“椭圆曲线离散对数”的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。 + +目前比较常用的两个曲线是 P-256(secp256r1,在 OpenSSL 称为 prime256v1)和 x25519。P-256 是 NIST(美国国家标准技术研究所)和 NSA(美国国家安全局)推荐使用的曲线,而 x25519 被认为是最安全、最快速的曲线。 + +ECC 名字里的“椭圆”经常会引起误解,其实它的曲线并不是椭圆形,只是因为方程很类似计算椭圆周长的公式,实际的形状更像抛物线,比如下面的图就展示了两个简单的椭圆曲线。 + +两个简单的椭圆曲线:y^2=x^3+7,y^2=x^3-x + +比起 RSA,ECC 在安全强度和性能上都有明显的优势。160 位的 ECC 相当于 1024 位的 RSA,而 224 位的 ECC 则相当于 2048 位的 RSA。因为密钥短,所以相应的计算量、消耗的内存和带宽也就少,加密解密的性能就上去了,对于现在的移动互联网非常有吸引力。 + +实验环境的 URI“/24-2”演示了 RSA1024,你在课后可以动手试一下。 + +混合加密 + +看到这里,你是不是认为可以抛弃对称加密,只用非对称加密来实现机密性呢? + +很遗憾,虽然非对称加密没有“密钥交换”的问题,但因为它们都是基于复杂的数学难题,运算速度很慢,即使是 ECC 也要比 AES 差上好几个数量级。如果仅用非对称加密,虽然保证了安全,但通信速度有如乌龟、蜗牛,实用性就变成了零。 + +实验环境的 URI“/24-3”对比了 AES 和 RSA 这两种算法的性能,下面列出了一次测试的结果: + +aes_128_cbc enc/dec 1000 times : 0.97ms, 13.11MB/s + +rsa_1024 enc/dec 1000 times : 138.59ms, 93.80KB/s +rsa_1024/aes ratio = 143.17 + +rsa_2048 enc/dec 1000 times : 840.35ms, 15.47KB/s +rsa_2048/aes ratio = 868.13 + + +可以看到,RSA 的运算速度是非常慢的,2048 位的加解密大约是 15KB/S(微秒或毫秒级),而 AES128 则是 13MB/S(纳秒级),差了几百倍。 + +那么,是不是能够把对称加密和非对称加密结合起来呢,两者互相取长补短,即能高效地加密解密,又能安全地密钥交换。 + +这就是现在 TLS 里使用的混合加密方式,其实说穿了也很简单: + +在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题。 + +然后用随机数产生对称算法使用的“会话密钥”(session key),再用公钥加密。因为会话密钥很短,通常只有 16 字节或 32 字节,所以慢一点也无所谓。 + +对方拿到密文后用私钥解密,取出会话密钥。这样,双方就实现了对称密钥的安全交换,后续就不再使用非对称加密,全都使用对称加密。 + + + +这样混合加密就解决了对称加密算法的密钥交换问题,而且安全和性能兼顾,完美地实现了机密性。 + +不过这只是“万里长征的第一步”,后面还有完整性、身份认证、不可否认等特性没有实现,所以现在的通信还不是绝对安全,我们下次再说。 + +小结 + + +加密算法的核心思想是“把一个小秘密(密钥)转化为一个大秘密(密文消息)”,守住了小秘密,也就守住了大秘密; +对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换,常用的有 AES 和 ChaCha20; +非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢,常用的有 RSA 和 ECC; +把对称加密和非对称加密结合起来就得到了“又好又快”的混合加密,也就是 TLS 里使用的加密方式。 + + +课下作业 + + +加密算法中“密钥”的名字很形象,你能试着用现实中的锁和钥匙来比喻一下吗? +在混合加密中用到了公钥加密,因为只能由私钥解密。那么反过来,私钥加密后任何人都可以用公钥解密,这有什么用呢? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/25固若金汤的根本(下):数字签名与证书.md b/专栏/透视HTTP协议/25固若金汤的根本(下):数字签名与证书.md new file mode 100644 index 0000000..0cc7baf --- /dev/null +++ b/专栏/透视HTTP协议/25固若金汤的根本(下):数字签名与证书.md @@ -0,0 +1,160 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 25 固若金汤的根本(下):数字签名与证书 + 上一讲中我们学习了对称加密和非对称加密,以及两者结合起来的混合加密,实现了机密性。 + +但仅有机密性,离安全还差的很远。 + +黑客虽然拿不到会话密钥,无法破解密文,但可以通过窃听收集到足够多的密文,再尝试着修改、重组后发给网站。因为没有完整性保证,服务器只能“照单全收”,然后他就可以通过服务器的响应获取进一步的线索,最终就会破解出明文。 + +另外,黑客也可以伪造身份发布公钥。如果你拿到了假的公钥,混合加密就完全失效了。你以为自己是在和“某宝”通信,实际上网线的另一端却是黑客,银行卡号、密码等敏感信息就在“安全”的通信过程中被窃取了。 + +所以,在机密性的基础上还必须加上完整性、身份认证等特性,才能实现真正的安全。 + +摘要算法 + +实现完整性的手段主要是摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数(Hash Function)。 + +你可以把摘要算法近似地理解成一种特殊的压缩算法,它能够把任意长度的数据“压缩”成固定长度、而且独一无二的“摘要”字符串,就好像是给这段数据生成了一个数字“指纹”。 + +换一个角度,也可以把摘要算法理解成特殊的“单向”加密算法,它只有算法,没有密钥,加密后的数据无法解密,不能从摘要逆推出原文。 + + + +摘要算法实际上是把数据从一个“大空间”映射到了“小空间”,所以就存在“冲突”(collision,也叫碰撞)的可能性,就如同现实中的指纹一样,可能会有两份不同的原文对应相同的摘要。好的摘要算法必须能够“抵抗冲突”,让这种可能性尽量地小。 + +因为摘要算法对输入具有“单向性”和“雪崩效应”,输入的微小不同会导致输出的剧烈变化,所以也被 TLS 用来生成伪随机数(PRF,pseudo random function)。 + +你一定在日常工作中听过、或者用过 MD5(Message-Digest 5)、SHA-1(Secure Hash Algorithm 1),它们就是最常用的两个摘要算法,能够生成 16 字节和 20 字节长度的数字摘要。但这两个算法的安全强度比较低,不够安全,在 TLS 里已经被禁止使用了。 + +目前 TLS 推荐使用的是 SHA-1 的后继者:SHA-2。 + +SHA-2 实际上是一系列摘要算法的统称,总共有 6 种,常用的有 SHA224、SHA256、SHA384,分别能够生成 28 字节、32 字节、48 字节的摘要。 + +你可以用实验环境的 URI“/25-1”来测试一下 TLS 里的各种摘要算法,包括 MD5、SHA-1 和 SHA-2。 + +https://www.chrono.com/25-1?algo=md5 +https://www.chrono.com/25-1?algo=sha1 +https://www.chrono.com/25-1?algo=sha256 + + +完整性 + +摘要算法保证了“数字摘要”和原文是完全等价的。所以,我们只要在原文后附上它的摘要,就能够保证数据的完整性。 + +比如,你发了条消息:“转账 1000 元”,然后再加上一个 SHA-2 的摘要。网站收到后也计算一下消息的摘要,把这两份“指纹”做个对比,如果一致,就说明消息是完整可信的,没有被修改。 + +如果黑客在中间哪怕改动了一个标点符号,摘要也会完全不同,网站计算比对就会发现消息被窜改,是不可信的。 + +不过摘要算法不具有机密性,如果明文传输,那么黑客可以修改消息后把摘要也一起改了,网站还是鉴别不出完整性。 + +所以,真正的完整性必须要建立在机密性之上,在混合加密系统里用会话密钥加密消息和摘要,这样黑客无法得知明文,也就没有办法动手脚了。 + +这有个术语,叫哈希消息认证码(HMAC)。 + + + +数字签名 + +加密算法结合摘要算法,我们的通信过程可以说是比较安全了。但这里还有漏洞,就是通信的两个端点(endpoint)。 + +就像一开始所说的,黑客可以伪装成网站来窃取信息。而反过来,他也可以伪装成你,向网站发送支付、转账等消息,网站没有办法确认你的身份,钱可能就这么被偷走了。 + +现实生活中,解决身份认证的手段是签名和印章,只要在纸上写下签名或者盖个章,就能够证明这份文件确实是由本人而不是其他人发出的。 + +你回想一下之前的课程,在 TLS 里有什么东西和现实中的签名、印章很像,只能由本人持有,而其他任何人都不会有呢?只要用这个东西,就能够在数字世界里证明你的身份。 + +没错,这个东西就是非对称加密里的“私钥”,使用私钥再加上摘要算法,就能够实现“数字签名”,同时实现“身份认证”和“不可否认”。 + +数字签名的原理其实很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密。 + +但又因为非对称加密效率太低,所以私钥只加密原文的摘要,这样运算量就小的多,而且得到的数字签名也很小,方便保管和传输。 + +签名和公钥一样完全公开,任何人都可以获取。但这个签名只有用私钥对应的公钥才能解开,拿到摘要后,再比对原文验证完整性,就可以像签署文件一样证明消息确实是你发的。 + + + +刚才的这两个行为也有专用术语,叫做“签名”和“验签”。 + +只要你和网站互相交换公钥,就可以用“签名”和“验签”来确认消息的真实性,因为私钥保密,黑客不能伪造签名,就能够保证通信双方的身份。 + +比如,你用自己的私钥签名一个消息“我是小明”。网站收到后用你的公钥验签,确认身份没问题,于是也用它的私钥签名消息“我是某宝”。你收到后再用它的公钥验一下,也没问题,这样你和网站就都知道对方不是假冒的,后面就可以用混合加密进行安全通信了。 + +实验环境的 URI“/25-2”演示了 TLS 里的数字签名,它使用的是 RSA1024。 + +数字证书和 CA + +到现在,综合使用对称加密、非对称加密和摘要算法,我们已经实现了安全的四大特性,是不是已经完美了呢? + +不是的,这里还有一个“公钥的信任”问题。因为谁都可以发布公钥,我们还缺少防止黑客伪造公钥的手段,也就是说,怎么来判断这个公钥就是你或者某宝的公钥呢? + +真是“按下葫芦又起了瓢”,安全还真是个麻烦事啊,“一环套一环”的。 + +我们可以用类似密钥交换的方法来解决公钥认证问题,用别的私钥来给公钥签名,显然,这又会陷入“无穷递归”。 + +但这次实在是“没招”了,要终结这个“死循环”,就必须引入“外力”,找一个公认的可信第三方,让它作为“信任的起点,递归的终点”,构建起公钥的信任链。 + +这个“第三方”就是我们常说的CA(Certificate Authority,证书认证机构)。它就像网络世界里的公安局、教育部、公证中心,具有极高的可信度,由它来给各个公钥签名,用自身的信誉来保证公钥无法伪造,是可信的。 + +CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate)。 + +知名的 CA 全世界就那么几家,比如 DigiCert、VeriSign、Entrust、Let’s Encrypt 等,它们签发的证书分 DV、OV、EV 三种,区别在于可信程度。 + +DV 是最低的,只是域名级别的可信,背后是谁不知道。EV 是最高的,经过了法律和审计的严格核查,可以证明网站拥有者的身份(在浏览器地址栏会显示出公司的名字,例如 Apple、GitHub 的网站)。 + +不过,CA 怎么证明自己呢? + +这还是信任链的问题。小一点的 CA 可以让大 CA 签名认证,但链条的最后,也就是Root CA,就只能自己证明自己了,这个就叫“自签名证书”(Self-Signed Certificate)或者“根证书”(Root Certificate)。你必须相信,否则整个证书信任链就走不下去了。 + + + +有了这个证书体系,操作系统和浏览器都内置了各大 CA 的根证书,上网的时候只要服务器发过来它的证书,就可以验证证书里的签名,顺着证书链(Certificate Chain)一层层地验证,直到找到根证书,就能够确定证书是可信的,从而里面的公钥也是可信的。 + +我们的实验环境里使用的证书是“野路子”的自签名证书(在 Linux 上用 OpenSSL 命令行签发),肯定是不会被浏览器所信任的,所以用 Chrome 访问时就会显示成红色,标记为不安全。但你只要把它安装进系统的根证书存储区里,让它作为信任链的根,就不会再有危险警告。 + + + +证书体系的弱点 + +证书体系(PKI,Public Key Infrastructure)虽然是目前整个网络世界的安全基础设施,但绝对的安全是不存在的,它也有弱点,还是关键的“信任”二字。 + +如果 CA 失误或者被欺骗,签发了错误的证书,虽然证书是真的,可它代表的网站却是假的。 + +还有一种更危险的情况,CA 被黑客攻陷,或者 CA 有恶意,因为它(即根证书)是信任的源头,整个信任链里的所有证书也就都不可信了。 + +这两种事情并不是“耸人听闻”,都曾经实际出现过。所以,需要再给证书体系打上一些补丁。 + +针对第一种,开发出了 CRL(证书吊销列表,Certificate revocation list)和 OCSP(在线证书状态协议,Online Certificate Status Protocol),及时废止有问题的证书。 + +对于第二种,因为涉及的证书太多,就只能操作系统或者浏览器从根上“下狠手”了,撤销对 CA 的信任,列入“黑名单”,这样它颁发的所有证书就都会被认为是不安全的。 + +小结 + +今天我们学习了数字签名和证书、CA,是不是有种“盗梦空间”一层套一层的感觉?你可以在课后再去各大网站,结合它们“小锁头”里的信息来加深理解。 + +今天的内容可以简单概括为四点: + + +摘要算法用来实现完整性,能够为数据生成独一无二的“指纹”,常用的算法是 SHA-2; +数字签名是私钥对摘要的加密,可以由公钥解密后验证,实现身份认证和不可否认; +公钥的分发需要使用数字证书,必须由 CA 的信任链来验证,否则就是不可信的; +作为信任链的源头 CA 有时也会不可信,解决办法有 CRL、OCSP,还有终止信任。 + + +课下作业 + + +为什么公钥能够建立信任链,用对称加密算法里的对称密钥行不行呢? +假设有一个三级的证书体系(Root CA=> 一级 CA=> 二级 CA),你能详细解释一下证书信任链的验证过程吗? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/26信任始于握手:TLS1.2连接过程解析.md b/专栏/透视HTTP协议/26信任始于握手:TLS1.2连接过程解析.md new file mode 100644 index 0000000..f3d3119 --- /dev/null +++ b/专栏/透视HTTP协议/26信任始于握手:TLS1.2连接过程解析.md @@ -0,0 +1,192 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 26 信任始于握手:TLS1.2连接过程解析 + 经过前几讲的介绍,你应该已经熟悉了对称加密与非对称加密、数字签名与证书等密码学知识。 + +有了这些知识“打底”,现在我们就可以正式开始研究 HTTPS 和 TLS 协议了。 + +HTTPS 建立连接 + +当你在浏览器地址栏里键入“https”开头的 URI,再按下回车,会发生什么呢? + +回忆一下[第 8 讲]的内容,你应该知道,浏览器首先要从 URI 里提取出协议名和域名。因为协议名是“https”,所以浏览器就知道了端口号是默认的 443,它再用 DNS 解析域名,得到目标的 IP 地址,然后就可以使用三次握手与网站建立 TCP 连接了。 + +在 HTTP 协议里,建立连接后,浏览器会立即发送请求报文。但现在是 HTTPS 协议,它需要再用另外一个“握手”过程,在 TCP 上建立安全连接,之后才是收发 HTTP 报文。 + +这个“握手”过程与 TCP 有些类似,是 HTTPS 和 TLS 协议里最重要、最核心的部分,懂了它,你就可以自豪地说自己“掌握了 HTTPS”。 + +TLS 协议的组成 + +在讲 TLS 握手之前,我先简单介绍一下 TLS 协议的组成。 + +TLS 包含几个子协议,你也可以理解为它是由几个不同职责的模块组成,比较常用的有记录协议、警报协议、握手协议、变更密码规范协议等。 + +记录协议(Record Protocol)规定了 TLS 收发数据的基本单位:记录(record)。它有点像是 TCP 里的 segment,所有的其他子协议都需要通过记录协议发出。但多个记录数据可以在一个 TCP 包里一次性发出,也并不需要像 TCP 那样返回 ACK。 + +警报协议(Alert Protocol)的职责是向对方发出警报信息,有点像是 HTTP 协议里的状态码。比如,protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题,收到警报后另一方可以选择继续,也可以立即终止连接。 + +握手协议(Handshake Protocol)是 TLS 里最复杂的子协议,要比 TCP 的 SYN/ACK 复杂的多,浏览器和服务器会在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统。 + +最后一个是变更密码规范协议(Change Cipher Spec Protocol),它非常简单,就是一个“通知”,告诉对方,后续的数据都将使用加密保护。那么反过来,在它之前,数据都是明文的。 + +下面的这张图简要地描述了 TLS 的握手过程,其中每一个“框”都是一个记录,多个记录组合成一个 TCP 包发送。所以,最多经过两次消息往返(4 个消息)就可以完成握手,然后就可以在安全的通信环境里发送 HTTP 报文,实现 HTTPS 协议。 + + + +抓包的准备工作 + +这次我们在实验环境里测试 TLS 握手的 URI 是“/26-1”,看了上面的图你就可以知道,TLS 握手的前几个消息都是明文的,能够在 Wireshark 里直接看。但只要出现了“Change Cipher Spec”,后面的数据就都是密文了,看到的也就会是乱码,不知道究竟是什么东西。 + +为了更好地分析 TLS 握手过程,你可以再对系统和 Wireshark 做一下设置,让浏览器导出握手过程中的秘密信息,这样 Wireshark 就可以把密文解密,还原出明文。 + +首先,你需要在 Windows 的设置里新增一个系统变量“SSLKEYLOGFILE”,设置浏览器日志文件的路径,比如“D:\http_study\www\temp\sslkey.log”(具体的设置过程就不详细说了,可以在设置里搜索“系统变量”)。 + + + +然后在 Wireshark 里设置“Protocols-TLS”(较早版本的 Wireshark 里是“SSL”),在“(Pre)-Master-Secret log filename”里填上刚才的日志文件。 + + + +设置好之后,过滤器选择“tcp port 443”,就可以抓到实验环境里的所有 HTTPS 数据了。 + +如果你觉得麻烦也没关系,GitHub 上有抓好的包和相应的日志,用 Wireshark 直接打开就行。 + +ECDHE 握手过程 + +刚才你看到的是握手过程的简要图,我又画了一个详细图,对应 Wireshark 的抓包,下面我就用这个图来仔细剖析 TLS 的握手过程。 + + + +在 TCP 建立连接之后,浏览器会首先发一个“Client Hello”消息,也就是跟服务器“打招呼”。里面有客户端的版本号、支持的密码套件,还有一个随机数(Client Random),用于后续生成会话密钥。 + +Handshake Protocol: Client Hello + Version: TLS 1.2 (0x0303) + Random: 1cbf803321fd2623408dfe… + Cipher Suites (17 suites) + Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) + Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) + + +这个的意思就是:“我这边有这些这些信息,你看看哪些是能用的,关键的随机数可得留着。” + +作为“礼尚往来”,服务器收到“Client Hello”后,会返回一个“Server Hello”消息。把版本号对一下,也给出一个随机数(Server Random),然后从客户端的列表里选一个作为本次通信使用的密码套件,在这里它选择了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”。 + +Handshake Protocol: Server Hello + Version: TLS 1.2 (0x0303) + Random: 0e6320f21bae50842e96… + Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) + + +这个的意思就是:“版本号对上了,可以加密,你的密码套件挺多,我选一个最合适的吧,用椭圆曲线加 RSA、AES、SHA384。我也给你一个随机数,你也得留着。” + +然后,服务器为了证明自己的身份,就把证书也发给了客户端(Server Certificate)。 + +接下来是一个关键的操作,因为服务器选择了 ECDHE 算法,所以它会在证书后发送“Server Key Exchange”消息,里面是椭圆曲线的公钥(Server Params),用来实现密钥交换算法,再加上自己的私钥签名认证。 + +Handshake Protocol: Server Key Exchange + EC Diffie-Hellman Server Params + Curve Type: named_curve (0x03) + Named Curve: x25519 (0x001d) + Pubkey: 3b39deaf00217894e... + Signature Algorithm: rsa_pkcs1_sha512 (0x0601) + Signature: 37141adac38ea4... + + +这相当于说:“刚才我选的密码套件有点复杂,所以再给你个算法的参数,和刚才的随机数一样有用,别丢了。为了防止别人冒充,我又盖了个章。” + +之后是“Server Hello Done”消息,服务器说:“我的信息就是这些,打招呼完毕。” + +这样第一个消息往返就结束了(两个 TCP 包),结果是客户端和服务器通过明文共享了三个信息:Client Random、Server Random 和 Server Params。 + +客户端这时也拿到了服务器的证书,那这个证书是不是真实有效的呢? + +这就要用到第 25 讲里的知识了,开始走证书链逐级验证,确认证书的真实性,再用证书公钥验证签名,就确认了服务器的身份:“刚才跟我打招呼的不是骗子,可以接着往下走。” + +然后,客户端按照密码套件的要求,也生成一个椭圆曲线的公钥(Client Params),用“Client Key Exchange”消息发给服务器。 + +Handshake Protocol: Client Key Exchange + EC Diffie-Hellman Client Params + Pubkey: 8c674d0e08dc27b5eaa… + + +现在客户端和服务器手里都拿到了密钥交换算法的两个参数(Client Params、Server Params),就用 ECDHE 算法一阵算,算出了一个新的东西,叫“Pre-Master”,其实也是一个随机数。 + +至于具体的计算原理和过程,因为太复杂就不细说了,但算法可以保证即使黑客截获了之前的参数,也是绝对算不出这个随机数的。 + +现在客户端和服务器手里有了三个随机数:Client Random、Server Random 和 Pre-Master。用这三个作为原始材料,就可以生成用于加密会 话的主密钥,叫“Master Secret”。而黑客因为拿不到“Pre-Master”,所以也就得不到主密钥。 + +为什么非得这么麻烦,非要三个随机数呢? + +这就必须说 TLS 的设计者考虑得非常周到了,他们不信任客户端或服务器伪随机数的可靠性,为了保证真正的“完全随机”“不可预测”,把三个不可靠的随机数混合起来,那么“随机”的程度就非常高了,足够让黑客难以猜测。 + +你一定很想知道“Master Secret”究竟是怎么算出来的吧,贴一下 RFC 里的公式: + +master_secret = PRF(pre_master_secret, "master secret", + ClientHello.random + ServerHello.random) + + +这里的“PRF”就是伪随机数函数,它基于密码套件里的最后一个参数,比如这次的 SHA384,通过摘要算法来再一次强化“Master Secret”的随机性。 + +主密钥有 48 字节,但它也不是最终用于通信的会话密钥,还会再用 PRF 扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)等等,避免只用一个密钥带来的安全隐患。 + +有了主密钥和派生的会话密钥,握手就快结束了。客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证。 + +意思就是告诉服务器:“后面都改用对称算法加密通信了啊,用的就是打招呼时说的 AES,加密对不对还得你测一下。” + +服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束,后面就收发被加密的 HTTP 请求和响应了。 + +RSA 握手过程 + +整个握手过程可真是够复杂的,但你可能会问了,好像这个过程和其他地方看到的不一样呢? + +刚才说的其实是如今主流的 TLS 握手过程,这与传统的握手有两点不同。 + +第一个,使用 ECDHE 实现密钥交换,而不是 RSA,所以会在服务器端发出“Server Key Exchange”消息。 + +第二个,因为使用了 ECDHE,客户端可以不用等到服务器发回“Finished”确认握手完毕,立即就发出 HTTP 报文,省去了一个消息往返的时间浪费。这个叫“TLS False Start”,意思就是“抢跑”,和“TCP Fast Open”有点像,都是不等连接完全建立就提前发应用数据,提高传输的效率。 + +实验环境在 440 端口(https://www.chrono.com:440⁄26-1)实现了传统的 RSA 密钥交换,没有“False Start”,你可以课后自己抓包看一下,这里我也画了个图。 + + + +大体的流程没有变,只是“Pre-Master”不再需要用算法生成,而是客户端直接生成随机数,然后用服务器的公钥加密,通过“Client Key Exchange”消息发给服务器。服务器再用私钥解密,这样双方也实现了共享三个随机数,就可以生成主密钥。 + +双向认证 + +到这里 TLS 握手就基本讲完了。 + +不过上面说的是“单向认证”握手过程,只认证了服务器的身份,而没有认证客户端的身份。这是因为通常单向认证通过后已经建立了安全通信,用账号、密码等简单的手段就能够确认用户的真实身份。 + +但为了防止账号、密码被盗,有的时候(比如网上银行)还会使用 U 盾给用户颁发客户端证书,实现“双向认证”,这样会更加安全。 + +双向认证的流程也没有太多变化,只是在“Server Hello Done”之后,“Client Key Exchange”之前,客户端要发送“Client Certificate”消息,服务器收到后也把证书链走一遍,验证客户端的身份。 + +小结 + +今天我们学习了 HTTPS/TLS 的握手,内容比较多、比较难,不过记住下面四点就可以。 + + +HTTPS 协议会先与服务器执行 TCP 握手,然后执行 TLS 握手,才能建立安全连接; +握手的目标是安全地交换对称密钥,需要三个随机数,第三个随机数“Pre-Master”必须加密传输,绝对不能让黑客破解; +“Hello”消息交换随机数,“Key Exchange”消息交换“Pre-Master”; +“Change Cipher Spec”之前传输的都是明文,之后都是对称密钥加密的密文。 + + +课下作业 + + +密码套件里的那些算法分别在握手过程中起了什么作用? +你能完整地描述一下 RSA 的握手过程吗? +你能画出双向认证的流程图吗? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/27更好更快的握手:TLS1.3特性解析.md b/专栏/透视HTTP协议/27更好更快的握手:TLS1.3特性解析.md new file mode 100644 index 0000000..d94237e --- /dev/null +++ b/专栏/透视HTTP协议/27更好更快的握手:TLS1.3特性解析.md @@ -0,0 +1,189 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 27 更好更快的握手:TLS1.3特性解析 + 上一讲中我讲了 TLS1.2 的握手过程,你是不是已经完全掌握了呢? + +不过 TLS1.2 已经是 10 年前(2008 年)的“老”协议了,虽然历经考验,但毕竟“岁月不饶人”,在安全、性能等方面已经跟不上如今的互联网了。 + +于是经过四年、近 30 个草案的反复打磨,TLS1.3 终于在去年(2018 年)“粉墨登场”,再次确立了信息安全领域的新标准。 + +在抓包分析握手之前,我们先来快速浏览一下 TLS1.3 的三个主要改进目标:兼容、安全与性能。 + +最大化兼容性 + +由于 1.1、1.2 等协议已经出现了很多年,很多应用软件、中间代理(官方称为“MiddleBox”)只认老的记录协议格式,更新改造很困难,甚至是不可行(设备僵化)。 + +在早期的试验中发现,一旦变更了记录头字段里的版本号,也就是由 0x303(TLS1.2)改为 0x304(TLS1.3)的话,大量的代理服务器、网关都无法正确处理,最终导致 TLS 握手失败。 + +为了保证这些被广泛部署的“老设备”能够继续使用,避免新协议带来的“冲击”,TLS1.3 不得不做出妥协,保持现有的记录格式不变,通过“伪装”来实现兼容,使得 TLS1.3 看上去“像是”TLS1.2。 + +那么,该怎么区分 1.2 和 1.3 呢? + +这要用到一个新的扩展协议(Extension Protocol),它有点“补充条款”的意思,通过在记录末尾添加一系列的“扩展字段”来增加新的功能,老版本的 TLS 不认识它可以直接忽略,这就实现了“后向兼容”。 + +在记录头的 Version 字段被兼容性“固定”的情况下,只要是 TLS1.3 协议,握手的“Hello”消息后面就必须有“supported_versions”扩展,它标记了 TLS 的版本号,使用它就能区分新旧协议。 + +其实上一讲 Chrome 在握手时发的就是 TLS1.3 协议,你可以看一下“Client Hello”消息后面的扩展,只是因为服务器不支持 1.3,所以就“后向兼容”降级成了 1.2。 + +Handshake Protocol: Client Hello + Version: TLS 1.2 (0x0303) + Extension: supported_versions (len=11) + Supported Version: TLS 1.3 (0x0304) + Supported Version: TLS 1.2 (0x0303) + + +TLS1.3 利用扩展实现了许多重要的功能,比如“supported_groups”“key_share”“signature_algorithms”“server_name”等,这些等后面用到的时候再说。 + +强化安全 + +TLS1.2 在十来年的应用中获得了许多宝贵的经验,陆续发现了很多的漏洞和加密算法的弱点,所以 TLS1.3 就在协议里修补了这些不安全因素。 + +比如: + + +伪随机数函数由 PRF 升级为 HKDF(HMAC-based Extract-and-Expand Key Derivation Function); +明确禁止在记录协议里使用压缩; +废除了 RC4、DES 对称加密算法; +废除了 ECB、CBC 等传统分组模式; +废除了 MD5、SHA1、SHA-224 摘要算法; +废除了 RSA、DH 密钥交换算法和许多命名曲线。 + + +经过这一番“减肥瘦身”之后,TLS1.3 里只保留了 AES、ChaCha20 对称加密算法,分组模式只能用 AEAD 的 GCM、CCM 和 Poly1305,摘要算法只能用 SHA256、SHA384,密钥交换算法只有 ECDHE 和 DHE,椭圆曲线也被“砍”到只剩 P-256 和 x25519 等 5 种。 + +减肥可以让人变得更轻巧灵活,TLS 也是这样。 + +算法精简后带来了一个意料之中的好处:原来众多的算法、参数组合导致密码套件非常复杂,难以选择,而现在的 TLS1.3 里只有 5 个套件,无论是客户端还是服务器都不会再犯“选择困难症”了。 + + + +这里还要特别说一下废除 RSA 和 DH 密钥交换算法的原因。 + +上一讲用 Wireshark 抓包时你一定看到了,浏览器默认会使用 ECDHE 而不是 RSA 做密钥交换,这是因为它不具有“前向安全”(Forward Secrecy)。 + +假设有这么一个很有耐心的黑客,一直在长期收集混合加密系统收发的所有报文。如果加密系统使用服务器证书里的 RSA 做密钥交换,一旦私钥泄露或被破解(使用社会工程学或者巨型计算机),那么黑客就能够使用私钥解密出之前所有报文的“Pre-Master”,再算出会话密钥,破解所有密文。 + +这就是所谓的“今日截获,明日破解”。 + +而 ECDHE 算法在每次握手时都会生成一对临时的公钥和私钥,每次通信的密钥对都是不同的,也就是“一次一密”,即使黑客花大力气破解了这一次的会话密钥,也只是这次通信被攻击,之前的历史消息不会受到影响,仍然是安全的。 + +所以现在主流的服务器和浏览器在握手阶段都已经不再使用 RSA,改用 ECDHE,而 TLS1.3 在协议里明确废除 RSA 和 DH 则在标准层面保证了“前向安全”。 + +提升性能 + +HTTPS 建立连接时除了要做 TCP 握手,还要做 TLS 握手,在 1.2 中会多花两个消息往返(2-RTT),可能导致几十毫秒甚至上百毫秒的延迟,在移动网络中延迟还会更严重。 + +现在因为密码套件大幅度简化,也就没有必要再像以前那样走复杂的协商流程了。TLS1.3 压缩了以前的“Hello”协商过程,删除了“Key Exchange”消息,把握手时间减少到了“1-RTT”,效率提高了一倍。 + +那么它是怎么做的呢? + +其实具体的做法还是利用了扩展。客户端在“Client Hello”消息里直接用“supported_groups”带上支持的曲线,比如 P-256、x25519,用“key_share”带上曲线对应的客户端公钥参数,用“signature_algorithms”带上签名算法。 + +服务器收到后在这些扩展里选定一个曲线和参数,再用“key_share”扩展返回服务器这边的公钥参数,就实现了双方的密钥交换,后面的流程就和 1.2 基本一样了。 + +我为 1.3 的握手过程画了一张图,你可以对比 1.2 看看区别在哪里。 + + + +除了标准的“1-RTT”握手,TLS1.3 还引入了“0-RTT”握手,用“pre_shared_key”和“early_data”扩展,在 TCP 连接后立即就建立安全连接发送加密消息,不过这需要有一些前提条件,今天暂且不说。 + +握手分析 + +目前 Nginx 等 Web 服务器都能够很好地支持 TLS1.3,但要求底层的 OpenSSL 必须是 1.1.1,而我们实验环境里用的 OpenSSL 是 1.1.0,所以暂时无法直接测试 TLS1.3。 + +不过我在 Linux 上用 OpenSSL1.1.1 编译了一个支持 TLS1.3 的 Nginx,用 Wireshark 抓包存到了 GitHub 上,用它就可以分析 TLS1.3 的握手过程。 + + + +在 TCP 建立连接之后,浏览器首先还是发一个“Client Hello”。 + +因为 1.3 的消息兼容 1.2,所以开头的版本号、支持的密码套件和随机数(Client Random)结构都是一样的(不过这时的随机数是 32 个字节)。 + +Handshake Protocol: Client Hello + Version: TLS 1.2 (0x0303) + Random: cebeb6c05403654d66c2329… + Cipher Suites (18 suites) + Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301) + Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303) + Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302) + Extension: supported_versions (len=9) + Supported Version: TLS 1.3 (0x0304) + Supported Version: TLS 1.2 (0x0303) + Extension: supported_groups (len=14) + Supported Groups (6 groups) + Supported Group: x25519 (0x001d) + Supported Group: secp256r1 (0x0017) + Extension: key_share (len=107) + Key Share extension + Client Key Share Length: 105 + Key Share Entry: Group: x25519 + Key Share Entry: Group: secp256r1 + + +注意“Client Hello”里的扩展,“supported_versions”表示这是 TLS1.3,“supported_groups”是支持的曲线,“key_share”是曲线对应的参数。 + +这就好像是说: + +“还是照老规矩打招呼,这边有这些这些信息。但我猜你可能会升级,所以再多给你一些东西,也许后面用的上,咱们有话尽量一口气说完。” + +服务器收到“Client Hello”同样返回“Server Hello”消息,还是要给出一个随机数(Server Random)和选定密码套件。 + +Handshake Protocol: Server Hello + Version: TLS 1.2 (0x0303) + Random: 12d2bce6568b063d3dee2… + Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301) + Extension: supported_versions (len=2) + Supported Version: TLS 1.3 (0x0304) + Extension: key_share (len=36) + Key Share extension + Key Share Entry: Group: x25519, Key Exchange length: 32 + + +表面上看和 TLS1.2 是一样的,重点是后面的扩展。“supported_versions”里确认使用的是 TLS1.3,然后在“key_share”扩展带上曲线和对应的公钥参数。 + +服务器的“Hello”消息大概是这个意思: + +“还真让你给猜对了,虽然还是按老规矩打招呼,但咱们来个‘旧瓶装新酒’。刚才你给的我都用上了,我再给几个你缺的参数,这次加密就这么定了。” + +这时只交换了两条消息,客户端和服务器就拿到了四个共享信息:Client Random和Server Random、Client Params和Server Params,两边就可以各自用 ECDHE 算出“Pre-Master”,再用 HKDF 生成主密钥“Master Secret”,效率比 TLS1.2 提高了一大截。 + +在算出主密钥后,服务器立刻发出“Change Cipher Spec”消息,比 TLS1.2 提早进入加密通信,后面的证书等就都是加密的了,减少了握手时的明文信息泄露。 + +这里 TLS1.3 还有一个安全强化措施,多了个“Certificate Verify”消息,用服务器的私钥把前面的曲线、套件、参数等握手数据加了签名,作用和“Finished”消息差不多。但由于是私钥签名,所以强化了身份认证和和防窜改。 + +这两个“Hello”消息之后,客户端验证服务器证书,再发“Finished”消息,就正式完成了握手,开始收发 HTTP 报文。 + +虽然我们的实验环境暂时不能抓包测试 TLS1.3,但互联网上很多网站都已经支持了 TLS1.3,比如Nginx、GitHub,你可以课后自己用 Wireshark 试试。 + +在 Chrome 的开发者工具里,可以看到这些网站的 TLS1.3 应用情况。 + + + +小结 + +今天我们一起学习了 TLS1.3 的新特性,用抓包研究了它的握手过程,不过 TLS1.3 里的内容很多,还有一些特性没有谈到,后面会继续讲。 + + +为了兼容 1.1、1.2 等“老”协议,TLS1.3 会“伪装”成 TLS1.2,新特性在“扩展”里实现; +1.1、1.2 在实践中发现了很多安全隐患,所以 TLS1.3 大幅度删减了加密算法,只保留了 ECDHE、AES、ChaCha20、SHA-2 等极少数算法,强化了安全; +TLS1.3 也简化了握手过程,完全握手只需要一个消息往返,提升了性能。 + + +课下作业 + + +TLS1.3 里的密码套件没有指定密钥交换算法和签名算法,那么在握手的时候会不会有问题呢? +结合上一讲的 RSA 握手过程,解释一下为什么 RSA 密钥交换不具有“前向安全”。 +TLS1.3 的握手过程与 TLS1.2 的“False Start”有什么异同? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/28连接太慢该怎么办:HTTPS的优化.md b/专栏/透视HTTP协议/28连接太慢该怎么办:HTTPS的优化.md new file mode 100644 index 0000000..e8475c7 --- /dev/null +++ b/专栏/透视HTTP协议/28连接太慢该怎么办:HTTPS的优化.md @@ -0,0 +1,174 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 28 连接太慢该怎么办:HTTPS的优化 + 你可能或多或少听别人说过,“HTTPS 的连接很慢”。那么“慢”的原因是什么呢? + +通过前两讲的学习,你可以看到,HTTPS 连接大致上可以划分为两个部分,第一个是建立连接时的非对称加密握手,第二个是握手后的对称加密报文传输。 + +由于目前流行的 AES、ChaCha20 性能都很好,还有硬件优化,报文传输的性能损耗可以说是非常地小,小到几乎可以忽略不计了。所以,通常所说的“HTTPS 连接慢”指的就是刚开始建立连接的那段时间。 + +在 TCP 建连之后,正式数据传输之前,HTTPS 比 HTTP 增加了一个 TLS 握手的步骤,这个步骤最长可以花费两个消息往返,也就是 2-RTT。而且在握手消息的网络耗时之外,还会有其他的一些“隐形”消耗,比如: + + +产生用于密钥交换的临时公私钥对(ECDHE); +验证证书时访问 CA 获取 CRL 或者 OCSP; +非对称加密解密处理“Pre-Master”。 + + +在最差的情况下,也就是不做任何的优化措施,HTTPS 建立连接可能会比 HTTP 慢上几百毫秒甚至几秒,这其中既有网络耗时,也有计算耗时,就会让人产生“打开一个 HTTPS 网站好慢啊”的感觉。 + +不过刚才说的情况早就是“过去时”了,现在已经有了很多行之有效的 HTTPS 优化手段,运用得好可以把连接的额外耗时降低到几十毫秒甚至是“零”。 + +我画了一张图,把 TLS 握手过程中影响性能的部分都标记了出来,对照着它就可以“有的放矢”地来优化 HTTPS。 + + + +硬件优化 + +在计算机世界里的“优化”可以分成“硬件优化”和“软件优化”两种方式,先来看看有哪些硬件的手段。 + +硬件优化,说白了就是“花钱”。但花钱也是有门道的,要“有钱用在刀刃上”,不能大把的银子撒出去“只听见响”。 + +HTTPS 连接是计算密集型,而不是 I/O 密集型。所以,如果你花大价钱去买网卡、带宽、SSD 存储就是“南辕北辙”了,起不到优化的效果。 + +那该用什么样的硬件来做优化呢? + +首先,你可以选择更快的 CPU,最好还内建 AES 优化,这样即可以加速握手,也可以加速传输。 + +其次,你可以选择“SSL 加速卡”,加解密时调用它的 API,让专门的硬件来做非对称加解密,分担 CPU 的计算压力。 + +不过“SSL 加速卡”也有一些缺点,比如升级慢、支持算法有限,不能灵活定制解决方案等。 + +所以,就出现了第三种硬件加速方式:“SSL 加速服务器”,用专门的服务器集群来彻底“卸载”TLS 握手时的加密解密计算,性能自然要比单纯的“加速卡”要强大的多。 + +软件优化 + +不过硬件优化方式中除了 CPU,其他的通常可不是靠简单花钱就能买到的,还要有一些开发适配工作,有一定的实施难度。比如,“加速服务器”中关键的一点是通信必须是“异步”的,不能阻塞应用服务器,否则加速就没有意义了。 + +所以,软件优化的方式相对来说更可行一些,性价比高,能够“少花钱,多办事”。 + +软件方面的优化还可以再分成两部分:一个是软件升级,一个是协议优化。 + +软件升级实施起来比较简单,就是把现在正在使用的软件尽量升级到最新版本,比如把 Linux 内核由 2.x 升级到 4.x,把 Nginx 由 1.6 升级到 1.16,把 OpenSSL 由 1.0.1 升级到 1.1.0/1.1.1。 + +由于这些软件在更新版本的时候都会做性能优化、修复错误,只要运维能够主动配合,这种软件优化是最容易做的,也是最容易达成优化效果的。 + +但对于很多大中型公司来说,硬件升级或软件升级都是个棘手的问题,有成千上万台各种型号的机器遍布各个机房,逐一升级不仅需要大量人手,而且有较高的风险,可能会影响正常的线上服务。 + +所以,在软硬件升级都不可行的情况下,我们最常用的优化方式就是在现有的环境下挖掘协议自身的潜力。 + +协议优化 + +从刚才的 TLS 握手图中你可以看到影响性能的一些环节,协议优化就要从这些方面着手,先来看看核心的密钥交换过程。 + +如果有可能,应当尽量采用 TLS1.3,它大幅度简化了握手的过程,完全握手只要 1-RTT,而且更加安全。 + +如果暂时不能升级到 1.3,只能用 1.2,那么握手时使用的密钥交换协议应当尽量选用椭圆曲线的 ECDHE 算法。它不仅运算速度快,安全性高,还支持“False Start”,能够把握手的消息往返由 2-RTT 减少到 1-RTT,达到与 TLS1.3 类似的效果。 + +另外,椭圆曲线也要选择高性能的曲线,最好是 x25519,次优选择是 P-256。对称加密算法方面,也可以选用“AES_128_GCM”,它能比“AES_256_GCM”略快一点点。 + +在 Nginx 里可以用“ssl_ciphers”“ssl_ecdh_curve”等指令配置服务器使用的密码套件和椭圆曲线,把优先使用的放在前面,例如: + +ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:EECDH+CHACHA20; +ssl_ecdh_curve X25519:P-256; + + +证书优化 + +除了密钥交换,握手过程中的证书验证也是一个比较耗时的操作,服务器需要把自己的证书链全发给客户端,然后客户端接收后再逐一验证。 + +这里就有两个优化点,一个是证书传输,一个是证书验证。 + +服务器的证书可以选择椭圆曲线(ECDSA)证书而不是 RSA 证书,因为 224 位的 ECC 相当于 2048 位的 RSA,所以椭圆曲线证书的“个头”要比 RSA 小很多,即能够节约带宽也能减少客户端的运算量,可谓“一举两得”。 + +客户端的证书验证其实是个很复杂的操作,除了要公钥解密验证多个证书签名外,因为证书还有可能会被撤销失效,客户端有时还会再去访问 CA,下载 CRL 或者 OCSP 数据,这又会产生 DNS 查询、建立连接、收发数据等一系列网络通信,增加好几个 RTT。 + +CRL(Certificate revocation list,证书吊销列表)由 CA 定期发布,里面是所有被撤销信任的证书序号,查询这个列表就可以知道证书是否有效。 + +但 CRL 因为是“定期”发布,就有“时间窗口”的安全隐患,而且随着吊销证书的增多,列表会越来越大,一个 CRL 经常会上 MB。想象一下,每次需要预先下载几 M 的“无用数据”才能连接网站,实用性实在是太低了。 + +所以,现在 CRL 基本上不用了,取而代之的是 OCSP(在线证书状态协议,Online Certificate Status Protocol),向 CA 发送查询请求,让 CA 返回证书的有效状态。 + +但 OCSP 也要多出一次网络请求的消耗,而且还依赖于 CA 服务器,如果 CA 服务器很忙,那响应延迟也是等不起的。 + +于是又出来了一个“补丁”,叫“OCSP Stapling”(OCSP 装订),它可以让服务器预先访问 CA 获取 OCSP 响应,然后在握手时随着证书一起发给客户端,免去了客户端连接 CA 服务器查询的时间。 + +会话复用 + +到这里,我们已经讨论了四种 HTTPS 优化手段(硬件优化、软件优化、协议优化、证书优化),那么,还有没有其他更好的方式呢? + +我们再回想一下 HTTPS 建立连接的过程:先是 TCP 三次握手,然后是 TLS 一次握手。这后一次握手的重点是算出主密钥“Master Secret”,而主密钥每次连接都要重新计算,未免有点太浪费了,如果能够把“辛辛苦苦”算出来的主密钥缓存一下“重用”,不就可以免去了握手和计算的成本了吗? + +这种做法就叫“会话复用”(TLS session resumption),和 HTTP Cache 一样,也是提高 HTTPS 性能的“大杀器”,被浏览器和服务器广泛应用。 + +会话复用分两种,第一种叫“Session ID”,就是客户端和服务器首次连接后各自保存一个会话的 ID 号,内存里存储主密钥和其他相关的信息。当客户端再次连接时发一个 ID 过来,服务器就在内存里找,找到就直接用主密钥恢复会话状态,跳过证书验证和密钥交换,只用一个消息往返就可以建立安全通信。 + +实验环境的端口 441 实现了“Session ID”的会话复用,你可以访问 URI +“https://www.chrono.com:441⁄28-1”,刷新几次,用 Wireshark 抓包看看实际的效果。 + +Handshake Protocol: Client Hello + Version: TLS 1.2 (0x0303) + Session ID: 13564734eeec0a658830cd… + Cipher Suites Length: 34 + + +Handshake Protocol: Server Hello + Version: TLS 1.2 (0x0303) + Session ID: 13564734eeec0a658830cd… + Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) + + +通过抓包可以看到,服务器在“ServerHello”消息后直接发送了“Change Cipher Spec”和“Finished”消息,复用会话完成了握手。 + + + +会话票证 + +“Session ID”是最早出现的会话复用技术,也是应用最广的,但它也有缺点,服务器必须保存每一个客户端的会话数据,对于拥有百万、千万级别用户的网站来说存储量就成了大问题,加重了服务器的负担。 + +于是,又出现了第二种“Session Ticket”方案。 + +它有点类似 HTTP 的 Cookie,存储的责任由服务器转移到了客户端,服务器加密会话信息,用“New Session Ticket”消息发给客户端,让客户端保存。 + +重连的时候,客户端使用扩展“session_ticket”发送“Ticket”而不是“Session ID”,服务器解密后验证有效期,就可以恢复会话,开始加密通信。 + +这个过程也可以在实验环境里测试,端口号是 442,URI 是“https://www.chrono.com:442⁄28-1”。 + +不过“Session Ticket”方案需要使用一个固定的密钥文件(ticket_key)来加密 Ticket,为了防止密钥被破解,保证“前向安全”,密钥文件需要定期轮换,比如设置为一小时或者一天。 + +预共享密钥 + +“False Start”“Session ID”“Session Ticket”等方式只能实现 1-RTT,而 TLS1.3 更进一步实现了“0-RTT”,原理和“Session Ticket”差不多,但在发送 Ticket 的同时会带上应用数据(Early Data),免去了 1.2 里的服务器确认步骤,这种方式叫“Pre-shared Key”,简称为“PSK”。 + + + +但“PSK”也不是完美的,它为了追求效率而牺牲了一点安全性,容易受到“重放攻击”(Replay attack)的威胁。黑客可以截获“PSK”的数据,像复读机那样反复向服务器发送。 + +解决的办法是只允许安全的 GET/HEAD 方法(参见[第 10 讲]),在消息里加入时间戳、“nonce”验证,或者“一次性票证”限制重放。 + +小结 + + +可以有多种硬件和软件手段减少网络耗时和计算耗时,让 HTTPS 变得和 HTTP 一样快,最可行的是软件优化; +应当尽量使用 ECDHE 椭圆曲线密码套件,节约带宽和计算量,还能实现“False Start”; +服务器端应当开启“OCSP Stapling”功能,避免客户端访问 CA 去验证证书; +会话复用的效果类似 Cache,前提是客户端必须之前成功建立连接,后面就可以用“Session ID”“Session Ticket”等凭据跳过密钥交换、证书验证等步骤,直接开始加密通信。 + + +课下作业 + + +你能比较一下“Session ID”“Session Ticket”“PSK”这三种会话复用手段的异同吗? +你觉得哪些优化手段是你在实际工作中能用到的?应该怎样去用? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/29我应该迁移到HTTPS吗?.md b/专栏/透视HTTP协议/29我应该迁移到HTTPS吗?.md new file mode 100644 index 0000000..b549232 --- /dev/null +++ b/专栏/透视HTTP协议/29我应该迁移到HTTPS吗?.md @@ -0,0 +1,183 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 29 我应该迁移到HTTPS吗? + 今天是“安全篇”的最后一讲,我们已经学完了 HTTPS、TLS 相关的大部分知识。不过,或许你心里还会有一些困惑: + +“HTTPS 这么复杂,我是否应该迁移到 HTTPS 呢?它能带来哪些好处呢?具体又应该怎么实施迁移呢?” + +这些问题不单是你,也是其他很多人,还有当初的我的真实想法,所以今天我就来跟你聊聊这方面的事情。 + +迁移的必要性 + +如果你做移动应用开发的话,那么就一定知道,Apple、Android、某信等开发平台在 2017 年就相继发出通知,要求所有的应用必须使用 HTTPS 连接,禁止不安全的 HTTP。 + +在台式机上,主流的浏览器 Chrome、Firefox 等也早就开始“强推”HTTPS,把 HTTP 站点打上“不安全”的标签,给用户以“心理压力”。 + +Google 等搜索巨头还利用自身的“话语权”优势,降低 HTTP 站点的排名,而给 HTTPS 更大的权重,力图让网民只访问到 HTTPS 网站。 + +这些手段都逐渐“挤压”了纯明文 HTTP 的生存空间,“迁移到 HTTPS”已经不是“要不要做”的问题,而是“要怎么做”的问题了。HTTPS 的大潮无法阻挡,如果还是死守着 HTTP,那么无疑会被冲刷到互联网的角落里。 + +目前国内外的许多知名大站都已经实现了“全站 HTTPS”,打开常用的某宝、某东、某浪, + +都可以在浏览器的地址栏里看到“小锁头”,如果你正在维护的网站还没有实施 HTTPS,那可要抓点紧了。 + +迁移的顾虑 + +据我观察,阻碍 HTTPS 实施的因素还有一些这样、那样的顾虑,我总结出了三个比较流行的观点:“慢、贵、难”。 + +所谓“慢”,是指惯性思维,拿以前的数据来评估 HTTPS 的性能,认为 HTTPS 会增加服务器的成本,增加客户端的时延,影响用户体验。 + +其实现在服务器和客户端的运算能力都已经有了很大的提升,性能方面完全没有担心的必要,而且还可以应用很多的优化解决方案(参见[第 28 讲])。根据 Google 等公司的评估,在经过适当优化之后,HTTPS 的额外 CPU 成本小于 1%,额外的网络成本小于 2%,可以说是与无加密的 HTTP 相差无几。 + +所谓“贵”,主要是指证书申请和维护的成本太高,网站难以承担。 + +这也属于惯性思维,在早几年的确是个问题,向 CA 申请证书的过程不仅麻烦,而且价格昂贵,每年要交几千甚至几万元。 + +但现在就不一样了,为了推广 HTTPS,很多云服务厂商都提供了一键申请、价格低廉的证书,而且还出现了专门颁发免费证书的 CA,其中最著名的就是“Let’s Encrypt”。 + +所谓的“难”,是指 HTTPS 涉及的知识点太多、太复杂,有一定的技术门槛,不能很快上手。 + +这第三个顾虑比较现实,HTTPS 背后关联到了密码学、TLS、PKI 等许多领域,不是短短几周、几个月就能够精通的。但实施 HTTPS 也并不需要把这些完全掌握,只要抓住少数几个要点就好,下面我就来帮你逐个解决一些关键的“难点”。 + +申请证书 + +要把网站从 HTTP 切换到 HTTPS,首先要做的就是为网站申请一张证书。 + +大型网站出于信誉、公司形象的考虑,通常会选择向传统的 CA 申请证书,例如 DigiCert、GlobalSign,而中小型网站完全可以选择使用“Let’s Encrypt”这样的免费证书,效果也完全不输于那些收费的证书。 + +“Let’s Encrypt”一直在推动证书的自动化部署,为此还实现了专门的 ACME 协议(RFC8555)。有很多的客户端软件可以完成申请、验证、下载、更新的“一条龙”操作,比如 Certbot、acme.sh 等等,都可以在“Let’s Encrypt”网站上找到,用法很简单,相关的文档也很详细,几分钟就能完成申请,所以我在这里就不细说了。 + +不过我必须提醒你几个注意事项。 + +第一,申请证书时应当同时申请 RSA 和 ECDSA 两种证书,在 Nginx 里配置成双证书验证,这样服务器可以自动选择快速的椭圆曲线证书,同时也兼容只支持 RSA 的客户端。 + +第二,如果申请 RSA 证书,私钥至少要 2048 位,摘要算法应该选用 SHA-2,例如 SHA256、SHA384 等。 + +第三,出于安全的考虑,“Let’s Encrypt”证书的有效期很短,只有 90 天,时间一到就会过期失效,所以必须要定期更新。你可以在 crontab 里加个每周或每月任务,发送更新请求,不过很多 ACME 客户端会自动添加这样的定期任务,完全不用你操心。 + +配置 HTTPS + +搞定了证书,接下来就是配置 Web 服务器,在 443 端口上开启 HTTPS 服务了。 + +这在 Nginx 上非常简单,只要在“listen”指令后面加上参数“ssl”,再配上刚才的证书文件就可以实现最基本的 HTTPS。 + +listen 443 ssl; + +ssl_certificate xxx_rsa.crt; #rsa2048 cert +ssl_certificate_key xxx_rsa.key; #rsa2048 private key + +ssl_certificate xxx_ecc.crt; #ecdsa cert +ssl_certificate_key xxx_ecc.key; #ecdsa private ke + + +为了提高 HTTPS 的安全系数和性能,你还可以强制 Nginx 只支持 TLS1.2 以上的协议,打开“Session Ticket”会话复用: + +ssl_protocols TLSv1.2 TLSv1.3; + +ssl_session_timeout 5m; +ssl_session_tickets on; +ssl_session_ticket_key ticket.key; + + +密码套件的选择方面,我给你的建议是以服务器的套件优先。这样可以避免恶意客户端故意选择较弱的套件、降低安全等级,然后密码套件向 TLS1.3“看齐”,只使用 ECDHE、AES 和 ChaCha20,支持“False Start”。 + +ssl_prefer_server_ciphers on; + + +ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE+AES128:!MD5:!SHA1; + + +如果你的服务器上使用了 OpenSSL 的分支 BorringSSL,那么还可以使用一个特殊的“等价密码组”(Equal preference cipher groups)特性,它可以让服务器配置一组“等价”的密码套件,在这些套件里允许客户端优先选择,比如这么配置: + +ssl_ciphers +[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]; + + +如果客户端硬件没有 AES 优化,服务器就会顺着客户端的意思,优先选择与 AES“等价”的 ChaCha20 算法,让客户端能够快一点。 + +全部配置完成后,你可以访问“SSLLabs”网站,测试网站的安全程度,它会模拟多种客户端发起测试,打出一个综合的评分。 + +下图就是 GitHub 网站的评分结果: + + + +服务器名称指示 + +配置 HTTPS 服务时还有一个“虚拟主机”的问题需要解决。 + +在 HTTP 协议里,多个域名可以同时在一个 IP 地址上运行,这就是“虚拟主机”,Web 服务器会使用请求头里的 Host 字段(参见[第 9 讲])来选择。 + +但在 HTTPS 里,因为请求头只有在 TLS 握手之后才能发送,在握手时就必须选择“虚拟主机”对应的证书,TLS 无法得知域名的信息,就只能用 IP 地址来区分。所以,最早的时候每个 HTTPS 域名必须使用独立的 IP 地址,非常不方便。 + +那么怎么解决这个问题呢? + +这还是得用到 TLS 的“扩展”,给协议加个SNI(Server Name Indication)的“补充条款”。它的作用和 Host 字段差不多,客户端会在“Client Hello”时带上域名信息,这样服务器就可以根据名字而不是 IP 地址来选择证书。 + +Extension: server_name (len=19) + Server Name Indication extension + Server Name Type: host_name (0) + Server Name: www.chrono.com + + +Nginx 很早就基于 SNI 特性支持了 HTTPS 的虚拟主机,但在 OpenResty 里可还以编写 Lua 脚本,利用 Redis、MySQL 等数据库更灵活快速地加载证书。 + +重定向跳转 + +现在有了 HTTPS 服务,但原来的 HTTP 站点也不能马上弃用,还是会有很多网民习惯在地址栏里直接敲域名(或者是旧的书签、超链接),默认使用 HTTP 协议访问。 + +所以,我们就需要用到第 18 讲里的“重定向跳转”技术了,把不安全的 HTTP 网址用 301 或 302“重定向”到新的 HTTPS 网站,这在 Nginx 里也很容易做到,使用“return”或“rewrite”都可以。 + +return 301 https://$host$request_uri; # 永久重定向 +rewrite ^ https://$host$request_uri permanent; # 永久重定向 + + +但这种方式有两个问题。一个是重定向增加了网络成本,多出了一次请求;另一个是存在安全隐患,重定向的响应可能会被“中间人”窜改,实现“会话劫持”,跳转到恶意网站。 + +不过有一种叫“HSTS”(HTTP 严格传输安全,HTTP Strict Transport Security)的技术可以消除这种安全隐患。HTTPS 服务器需要在发出的响应头里添加一个“Strict-Transport-Security”的字段,再设定一个有效期,例如: + +Strict-Transport-Security: max-age=15768000; includeSubDomains + + +这相当于告诉浏览器:我这个网站必须严格使用 HTTPS 协议,在半年之内(182.5 天)都不允许用 HTTP,你以后就自己做转换吧,不要再来麻烦我了。 + +有了“HSTS”的指示,以后浏览器再访问同样的域名的时候就会自动把 URI 里的“http”改成“https”,直接访问安全的 HTTPS 网站。这样“中间人”就失去了攻击的机会,而且对于客户端来说也免去了一次跳转,加快了连接速度。 + +比如,如果在实验环境的配置文件里用“add_header”指令添加“HSTS”字段: + +add_header Strict-Transport-Security max-age=15768000; #182.5days + + +那么 Chrome 浏览器只会在第一次连接时使用 HTTP 协议,之后就会都走 HTTPS 协议。 + +小结 + +今天我介绍了一些 HTTPS 迁移的技术要点,掌握了它们你就可以搭建出一个完整的 HTTPS 站点了。 + +但想要实现大型网站的“全站 HTTPS”还是需要有很多的细枝末节的工作要做,比如使用 CSP(Content Security Policy)的各种指令和标签来配置安全策略,使用反向代理来集中“卸载”SSL,话题太大,以后有机会再细谈吧。 + +简单小结一下今天的内容: + + +从 HTTP 迁移到 HTTPS 是“大势所趋”,能做就应该尽早做; +升级 HTTPS 首先要申请数字证书,可以选择免费好用的“Let’s Encrypt”; +配置 HTTPS 时需要注意选择恰当的 TLS 版本和密码套件,强化安全; +原有的 HTTP 站点可以保留作为过渡,使用 301 重定向到 HTTPS。 + + +课下作业 + + +结合你的实际工作,分析一下迁移 HTTPS 的难点有哪些,应该如何克服? +参考上一讲,你觉得配置 HTTPS 时还应该加上哪些部分? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/30时代之风(上):HTTP2特性概览.md b/专栏/透视HTTP协议/30时代之风(上):HTTP2特性概览.md new file mode 100644 index 0000000..12d87aa --- /dev/null +++ b/专栏/透视HTTP协议/30时代之风(上):HTTP2特性概览.md @@ -0,0 +1,142 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 30 时代之风(上):HTTP2特性概览 + 在[第 14 讲]里,我们看到 HTTP 有两个主要的缺点:安全不足和性能不高。 + +刚结束的“安全篇”里的 HTTPS,通过引入 SSL/TLS 在安全上达到了“极致”,但在性能提升方面却是乏善可陈,只优化了握手加密的环节,对于整体的数据传输没有提出更好的改进方案,还只能依赖于“长连接”这种“落后”的技术(参见[第 17 讲])。 + +所以,在 HTTPS 逐渐成熟之后,HTTP 就向着性能方面开始“发力”,走出了另一条进化的道路。 + +在[第 1 讲]的 HTTP 历史中你也看到了,“秦失其鹿,天下共逐之”,Google 率先发明了 SPDY 协议,并应用于自家的浏览器 Chrome,打响了 HTTP 性能优化的“第一枪”。 + +随后互联网标准化组织 IETF 以 SPDY 为基础,综合其他多方的意见,终于推出了 HTTP/1 的继任者,也就是今天的主角“HTTP/2”,在性能方面有了一个大的飞跃。 + +为什么不是 HTTP/2.0 + +你一定很想知道,为什么 HTTP/2 不像之前的“1.0”“1.1”那样叫“2.0”呢? + +这个也是很多初次接触 HTTP/2 的人问的最多的一个问题,对此 HTTP/2 工作组特别给出了解释。 + +他们认为以前的“1.0”“1.1”造成了很多的混乱和误解,让人在实际的使用中难以区分差异,所以就决定 HTTP 协议不再使用小版本号(minor version),只使用大版本号(major version),从今往后 HTTP 协议不会出现 HTTP/2.0、2.1,只会有“HTTP/2”“HTTP/3”…… + +这样就可以明确无误地辨别出协议版本的“跃进程度”,让协议在一段较长的时期内保持稳定,每当发布新版本的 HTTP 协议都会有本质的不同,绝不会有“零敲碎打”的小改良。 + +兼容 HTTP/1 + +由于 HTTPS 已经在安全方面做的非常好了,所以 HTTP/2 的唯一目标就是改进性能。 + +但它不仅背负着众多的期待,同时还背负着 HTTP/1 庞大的历史包袱,所以协议的修改必须小心谨慎,兼容性是首要考虑的目标,否则就会破坏互联网上无数现有的资产,这方面 TLS 已经有了先例(为了兼容 TLS1.2 不得不进行“伪装”)。 + +那么,HTTP/2 是怎么做的呢? + +因为必须要保持功能上的兼容,所以 HTTP/2 把 HTTP 分解成了“语义”和“语法”两个部分,“语义”层不做改动,与 HTTP/1 完全一致(即 RFC7231)。比如请求方法、URI、状态码、头字段等概念都保留不变,这样就消除了再学习的成本,基于 HTTP 的上层应用也不需要做任何修改,可以无缝转换到 HTTP/2。 + +特别要说的是,与 HTTPS 不同,HTTP/2 没有在 URI 里引入新的协议名,仍然用“http”表示明文协议,用“https”表示加密协议。 + +这是一个非常了不起的决定,可以让浏览器或者服务器去自动升级或降级协议,免去了选择的麻烦,让用户在上网的时候都意识不到协议的切换,实现平滑过渡。 + +在“语义”保持稳定之后,HTTP/2 在“语法”层做了“天翻地覆”的改造,完全变更了 HTTP 报文的传输格式。 + +头部压缩 + +首先,HTTP/2 对报文的头部做了一个“大手术”。 + +通过“进阶篇”的学习你应该知道,HTTP/1 里可以用头字段“Content-Encoding”指定 Body 的编码方式,比如用 gzip 压缩来节约带宽,但报文的另一个组成部分——Header 却被无视了,没有针对它的优化手段。 + +由于报文 Header 一般会携带“User Agent”“Cookie”“Accept”“Server”等许多固定的头字段,多达几百字节甚至上千字节,但 Body 却经常只有几十字节(比如 GET 请求、204/301/304 响应),成了不折不扣的“大头儿子”。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费,“长尾效应”导致大量带宽消耗在了这些冗余度极高的数据上。 + +所以,HTTP/2 把“头部压缩”作为性能改进的一个重点,优化的方式你也肯定能想到,还是“压缩”。 + +不过 HTTP/2 并没有使用传统的压缩算法,而是开发了专门的“HPACK”算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还釆用哈夫曼编码来压缩整数和字符串,可以达到 50%~90% 的高压缩率。 + +二进制格式 + +你可能已经很习惯于 HTTP/1 里纯文本形式的报文了,它的优点是“一目了然”,用最简单的工具就可以开发调试,非常方便。 + +但 HTTP/2 在这方面没有“妥协”,决定改变延续了十多年的现状,不再使用肉眼可见的 ASCII 码,而是向下层的 TCP/IP 协议“靠拢”,全面采用二进制格式。 + +这样虽然对人不友好,但却大大方便了计算机的解析。原来使用纯文本的时候容易出现多义性,比如大小写、空白字符、回车换行、多字少字等等,程序在处理时必须用复杂的状态机,效率低,还麻烦。 + +而二进制里只有“0”和“1”,可以严格规定字段大小、顺序、标志位等格式,“对就是对,错就是错”,解析起来没有歧义,实现简单,而且体积小、速度快,做到“内部提效”。 + +以二进制格式为基础,HTTP/2 就开始了“大刀阔斧”的改革。 + +它把 TCP 协议的部分特性挪到了应用层,把原来的“Header+Body”的消息“打散”为数个小片的二进制“帧”(Frame),用“HEADERS”帧存放头数据、“DATA”帧存放实体数据。 + +这种做法有点像是“Chunked”分块编码的方式(参见[第 16 讲]),也是“化整为零”的思路,但 HTTP/2 数据分帧后“Header+Body”的报文结构就完全消失了,协议看到的只是一个个的“碎片”。 + + + +虚拟的“流” + +消息的“碎片”到达目的地后应该怎么组装起来呢? + +HTTP/2 为此定义了一个“流”(Stream)的概念,它是二进制帧的双向传输序列,同一个消息往返的帧会分配一个唯一的流 ID。你可以想象把它成是一个虚拟的“数据流”,在里面流动的是一串有先后顺序的数据帧,这些数据帧按照次序组装起来就是 HTTP/1 里的请求报文和响应报文。 + +因为“流”是虚拟的,实际上并不存在,所以 HTTP/2 就可以在一个 TCP 连接上用“流”同时发送多个“碎片化”的消息,这就是常说的“多路复用”( Multiplexing)——多个往返通信都复用一个连接来处理。 + +在“流”的层面上看,消息是一些有序的“帧”序列,而在“连接”的层面上看,消息却是乱序收发的“帧”。多个请求 / 响应之间没有了顺序关系,不需要排队等待,也就不会再出现“队头阻塞”问题,降低了延迟,大幅度提高了连接的利用率。 + + + +为了更好地利用连接,加大吞吐量,HTTP/2 还添加了一些控制帧来管理虚拟的“流”,实现了优先级和流量控制,这些特性也和 TCP 协议非常相似。 + +HTTP/2 还在一定程度上改变了传统的“请求 - 应答”工作模式,服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求 HTML 的时候就提前把可能会用到的 JS、CSS 文件发给客户端,减少等待的延迟,这被称为“服务器推送”(Server Push,也叫 Cache Push)。 + +强化安全 + +出于兼容的考虑,HTTP/2 延续了 HTTP/1 的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。 + +但由于 HTTPS 已经是大势所趋,而且主流的浏览器 Chrome、Firefox 等都公开宣布只支持加密的 HTTP/2,所以“事实上”的 HTTP/2 是加密的。也就是说,互联网上通常所能见到的 HTTP/2 都是使用“https”协议名,跑在 TLS 上面。 + +为了区分“加密”和“明文”这两个不同的版本,HTTP/2 协议定义了两个字符串标识符:“h2”表示加密的 HTTP/2,“h2c”表示明文的 HTTP/2,多出的那个字母“c”的意思是“clear text”。 + +在 HTTP/2 标准制定的时候(2015 年)已经发现了很多 SSL/TLS 的弱点,而新的 TLS1.3 还未发布,所以加密版本的 HTTP/2 在安全方面做了强化,要求下层的通信协议必须是 TLS1.2 以上,还要支持前向安全和 SNI,并且把几百个弱密码套件列入了“黑名单”,比如 DES、RC4、CBC、SHA-1 都不能在 HTTP/2 里使用,相当于底层用的是“TLS1.25”。 + +协议栈 + +下面的这张图对比了 HTTP/1、HTTPS 和 HTTP/2 的协议栈,你可以清晰地看到,HTTP/2 是建立在“HPack”“Stream”“TLS1.2”基础之上的,比 HTTP/1、HTTPS 复杂了一些。 + + + +虽然 HTTP/2 的底层实现很复杂,但它的“语义”还是简单的 HTTP/1,之前学习的知识不会过时,仍然能够用得上。 + +我们的实验环境在新的域名“www.metroid.net”上启用了 HTTP/2 协议,你可以把之前“进阶篇”“安全篇”的测试用例都走一遍,再用 Wireshark 抓一下包,实际看看 HTTP/2 的效果和对老协议的兼容性(例如“http://www.metroid.net/11-1”)。 + +在今天这节课专用的 URI“/30-1”里,你还可以看到服务器输出了 HTTP 的版本号“2”和标识符“h2”,表示这是加密的 HTTP/2,如果改用“https://www.chrono.com/30-1”访问就会是“1.1”和空。 + + + +你可能还会注意到 URI 里的一个小变化,端口使用的是“8443”而不是“443”。这是因为 443 端口已经被“www.chrono.com”的 HTTPS 协议占用,Nginx 不允许在同一个端口上根据域名选择性开启 HTTP/2,所以就不得不改用了“8443”。 + +小结 + +今天我简略介绍了 HTTP/2 的一些重要特性,比较偏重理论,下一次我会用 Wireshark 抓包,具体讲解 HTTP/2 的头部压缩、二进制帧和流等特性。 + + +HTTP 协议取消了小版本号,所以 HTTP/2 的正式名字不是 2.0; +HTTP/2 在“语义”上兼容 HTTP/1,保留了请求方法、URI 等传统概念; +HTTP/2 使用“HPACK”算法压缩头部信息,消除冗余数据节约带宽; +HTTP/2 的消息不再是“Header+Body”的形式,而是分散为多个二进制“帧”; +HTTP/2 使用虚拟的“流”传输消息,解决了困扰多年的“队头阻塞”问题,同时实现了“多路复用”,提高连接的利用率; +HTTP/2 也增强了安全性,要求至少是 TLS1.2,而且禁用了很多不安全的密码套件。 + + +课下作业 + + +你觉得明文形式的 HTTP/2(h2c)有什么好处,应该如何使用呢? +你觉得应该怎样理解 HTTP/2 里的“流”,为什么它是“虚拟”的? +你能对比一下 HTTP/2 与 HTTP/1、HTTPS 的相同点和不同点吗? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/31时代之风(下):HTTP2内核剖析.md b/专栏/透视HTTP协议/31时代之风(下):HTTP2内核剖析.md new file mode 100644 index 0000000..1a4b81b --- /dev/null +++ b/专栏/透视HTTP协议/31时代之风(下):HTTP2内核剖析.md @@ -0,0 +1,187 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 31 时代之风(下):HTTP2内核剖析 + 今天我们继续上一讲的话题,深入 HTTP/2 协议的内部,看看它的实现细节。 + + + +这次实验环境的 URI 是“/31-1”,我用 Wireshark 把请求响应的过程抓包存了下来,文件放在 GitHub 的“wireshark”目录。今天我们就对照着抓包来实地讲解 HTTP/2 的头部压缩、二进制帧等特性。 + +连接前言 + +由于 HTTP/2“事实上”是基于 TLS,所以在正式收发数据之前,会有 TCP 握手和 TLS 握手,这两个步骤相信你一定已经很熟悉了,所以这里就略过去不再细说。 + +TLS 握手成功之后,客户端必须要发送一个“连接前言”(connection preface),用来确认建立 HTTP/2 连接。 + +这个“连接前言”是标准的 HTTP/1 请求报文,使用纯文本的 ASCII 码格式,请求方法是特别注册的一个关键字“PRI”,全文只有 24 个字节: + +PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n + + +在 Wireshark 里,HTTP/2 的“连接前言”被称为“Magic”,意思就是“不可知的魔法”。 + +所以,就不要问“为什么会是这样”了,只要服务器收到这个“有魔力的字符串”,就知道客户端在 TLS 上想要的是 HTTP/2 协议,而不是其他别的协议,后面就会都使用 HTTP/2 的数据格式。 + +头部压缩 + +确立了连接之后,HTTP/2 就开始准备请求报文。 + +因为语义上它与 HTTP/1 兼容,所以报文还是由“Header+Body”构成的,但在请求发送前,必须要用“HPACK”算法来压缩头部数据。 + +“HPACK”算法是专门为压缩 HTTP 头部定制的算法,与 gzip、zlib 等压缩算法不同,它是一个“有状态”的算法,需要客户端和服务器各自维护一份“索引表”,也可以说是“字典”(这有点类似 brotli),压缩和解压缩就是查表和更新表的操作。 + +为了方便管理和压缩,HTTP/2 废除了原有的起始行概念,把起始行里面的请求方法、URI、状态码等统一转换成了头字段的形式,并且给这些“不是头字段的头字段”起了个特别的名字——“伪头字段”(pseudo-header fields)。而起始行里的版本号和错误原因短语因为没什么大用,顺便也给废除了。 + +为了与“真头字段”区分开来,这些“伪头字段”会在名字前加一个“:”,比如“:authority” “:method” “:status”,分别表示的是域名、请求方法和状态码。 + +现在 HTTP 报文头就简单了,全都是“Key-Value”形式的字段,于是 HTTP/2 就为一些最常用的头字段定义了一个只读的“静态表”(Static Table)。 + +下面的这个表格列出了“静态表”的一部分,这样只要查表就可以知道字段名和对应的值,比如数字“2”代表“GET”,数字“8”代表状态码 200。 + + + +但如果表里只有 Key 没有 Value,或者是自定义字段根本找不到该怎么办呢? + +这就要用到“动态表”(Dynamic Table),它添加在静态表后面,结构相同,但会在编码解码的时候随时更新。 + +比如说,第一次发送请求时的“user-agent”字段长是一百多个字节,用哈夫曼压缩编码发送之后,客户端和服务器都更新自己的动态表,添加一个新的索引号“65”。那么下一次发送的时候就不用再重复发那么多字节了,只要用一个字节发送编号就好。 + + + +你可以想象得出来,随着在 HTTP/2 连接上发送的报文越来越多,两边的“字典”也会越来越丰富,最终每次的头部字段都会变成一两个字节的代码,原来上千字节的头用几十个字节就可以表示了,压缩效果比 gzip 要好得多。 + +二进制帧 + +头部数据压缩之后,HTTP/2 就要把报文拆成二进制的帧准备发送。 + +HTTP/2 的帧结构有点类似 TCP 的段或者 TLS 里的记录,但报头很小,只有 9 字节,非常地节省(可以对比一下 TCP 头,它最少是 20 个字节)。 + +二进制的格式也保证了不会有歧义,而且使用位运算能够非常简单高效地解析。 + + + +帧开头是 3 个字节的长度(但不包括头的 9 个字节),默认上限是 2^14,最大是 2^24,也就是说 HTTP/2 的帧通常不超过 16K,最大是 16M。 + +长度后面的一个字节是帧类型,大致可以分成数据帧和控制帧两类,HEADERS 帧和 DATA 帧属于数据帧,存放的是 HTTP 报文,而 SETTINGS、PING、PRIORITY 等则是用来管理流的控制帧。 + +HTTP/2 总共定义了 10 种类型的帧,但一个字节可以表示最多 256 种,所以也允许在标准之外定义其他类型实现功能扩展。这就有点像 TLS 里扩展协议的意思了,比如 Google 的 gRPC 就利用了这个特点,定义了几种自用的新帧类型。 + +第 5 个字节是非常重要的帧标志信息,可以保存 8 个标志位,携带简单的控制信息。常用的标志位有END_HEADERS表示头数据结束,相当于 HTTP/1 里头后的空行(“\r\n”),END_STREAM表示单方向数据发送结束(即 EOS,End of Stream),相当于 HTTP/1 里 Chunked 分块结束标志(“0\r\n\r\n”)。 + +报文头里最后 4 个字节是流标识符,也就是帧所属的“流”,接收方使用它就可以从乱序的帧里识别出具有相同流 ID 的帧序列,按顺序组装起来就实现了虚拟的“流”。 + +流标识符虽然有 4 个字节,但最高位被保留不用,所以只有 31 位可以使用,也就是说,流标识符的上限是 2^31,大约是 21 亿。 + +好了,把二进制头理清楚后,我们来看一下 Wireshark 抓包的帧实例: + + + +在这个帧里,开头的三个字节是“00010a”,表示数据长度是 266 字节。 + +帧类型是 1,表示 HEADERS 帧,负载(payload)里面存放的是被 HPACK 算法压缩的头部信息。 + +标志位是 0x25,转换成二进制有 3 个位被置 1。PRIORITY 表示设置了流的优先级,END_HEADERS 表示这一个帧就是完整的头数据,END_STREAM 表示单方向数据发送结束,后续再不会有数据帧(即请求报文完毕,不会再有 DATA 帧 /Body 数据)。 + +最后 4 个字节的流标识符是整数 1,表示这是客户端发起的第一个流,后面的响应数据帧也会是这个 ID,也就是说在 stream[1] 里完成这个请求响应。 + +流与多路复用 + +弄清楚了帧结构后我们就来看 HTTP/2 的流与多路复用,它是 HTTP/2 最核心的部分。 + +在上一讲里我简单介绍了流的概念,不知道你“悟”得怎么样了?这里我再重复一遍:流是二进制帧的双向传输序列。 + +要搞明白流,关键是要理解帧头里的流 ID。 + +在 HTTP/2 连接上,虽然帧是乱序收发的,但只要它们都拥有相同的流 ID,就都属于一个流,而且在这个流里帧不是无序的,而是有着严格的先后顺序。 + +比如在这次的 Wireshark 抓包里,就有“0、1、3”一共三个流,实际上就是分配了三个流 ID 号,把这些帧按编号分组,再排一下队,就成了流。 + + + +在概念上,一个 HTTP/2 的流就等同于一个 HTTP/1 里的“请求 - 应答”。在 HTTP/1 里一个“请求 - 响应”报文来回是一次 HTTP 通信,在 HTTP/2 里一个流也承载了相同的功能。 + +你还可以对照着 TCP 来理解。TCP 运行在 IP 之上,其实从 MAC 层、IP 层的角度来看,TCP 的“连接”概念也是“虚拟”的。但从功能上看,无论是 HTTP/2 的流,还是 TCP 的连接,都是实际存在的,所以你以后大可不必再纠结于流的“虚拟”性,把它当做是一个真实存在的实体来理解就好。 + +HTTP/2 的流有哪些特点呢?我给你简单列了一下: + + +流是可并发的,一个 HTTP/2 连接上可以同时发出多个流传输数据,也就是并发多请求,实现“多路复用”; +客户端和服务器都可以创建流,双方互不干扰; +流是双向的,一个流里面客户端和服务器都可以发送或接收数据帧,也就是一个“请求 - 应答”来回; +流之间没有固定关系,彼此独立,但流内部的帧是有严格顺序的; +流可以设置优先级,让服务器优先处理,比如先传 HTML/CSS,后传图片,优化用户体验; +流 ID 不能重用,只能顺序递增,客户端发起的 ID 是奇数,服务器端发起的 ID 是偶数; +在流上发送“RST_STREAM”帧可以随时终止流,取消接收或发送; +第 0 号流比较特殊,不能关闭,也不能发送数据帧,只能发送控制帧,用于流量控制。 + + +这里我又画了一张图,把上次的图略改了一下,显示了连接中无序的帧是如何依据流 ID 重组成流的。 + + + +从这些特性中,我们还可以推理出一些深层次的知识点。 + +比如说,HTTP/2 在一个连接上使用多个流收发数据,那么它本身默认就会是长连接,所以永远不需要“Connection”头字段(keepalive 或 close)。 + +你可以再看一下 Wireshark 的抓包,里面发送了两个请求“/31-1”和“/favicon.ico”,始终用的是“56095<->8443”这个连接,对比一下[第 8 讲],你就能够看出差异了。 + +又比如,下载大文件的时候想取消接收,在 HTTP/1 里只能断开 TCP 连接重新“三次握手”,成本很高,而在 HTTP/2 里就可以简单地发送一个“RST_STREAM”中断流,而长连接会继续保持。 + +再比如,因为客户端和服务器两端都可以创建流,而流 ID 有奇数偶数和上限的区分,所以大多数的流 ID 都会是奇数,而且客户端在一个连接里最多只能发出 2^30,也就是 10 亿个请求。 + +所以就要问了:ID 用完了该怎么办呢?这个时候可以再发一个控制帧“GOAWAY”,真正关闭 TCP 连接。 + +流状态转换 + +流很重要,也很复杂。为了更好地描述运行机制,HTTP/2 借鉴了 TCP,根据帧的标志位实现流状态转换。当然,这些状态也是虚拟的,只是为了辅助理解。 + +HTTP/2 的流也有一个状态转换图,虽然比 TCP 要简单一点,但也不那么好懂,所以今天我只画了一个简化的图,对应到一个标准的 HTTP“请求 - 应答”。 + + + +最开始的时候流都是“空闲”(idle)状态,也就是“不存在”,可以理解成是待分配的“号段资源”。 + +当客户端发送 HEADERS 帧后,有了流 ID,流就进入了“打开”状态,两端都可以收发数据,然后客户端发送一个带“END_STREAM”标志位的帧,流就进入了“半关闭”状态。 + +这个“半关闭”状态很重要,意味着客户端的请求数据已经发送完了,需要接受响应数据,而服务器端也知道请求数据接收完毕,之后就要内部处理,再发送响应数据。 + +响应数据发完了之后,也要带上“END_STREAM”标志位,表示数据发送完毕,这样流两端就都进入了“关闭”状态,流就结束了。 + +刚才也说过,流 ID 不能重用,所以流的生命周期就是 HTTP/1 里的一次完整的“请求 - 应答”,流关闭就是一次通信结束。 + +下一次再发请求就要开一个新流(而不是新连接),流 ID 不断增加,直到到达上限,发送“GOAWAY”帧开一个新的 TCP 连接,流 ID 就又可以重头计数。 + +你再看看这张图,是不是和 HTTP/1 里的标准“请求 - 应答”过程很像,只不过这是发生在虚拟的“流”上,而不是实际的 TCP 连接,又因为流可以并发,所以 HTTP/2 就可以实现无阻塞的多路复用。 + +小结 + +HTTP/2 的内容实在是太多了,为了方便学习,我砍掉了一些特性,比如流的优先级、依赖关系、流量控制等。 + +但只要你掌握了今天的这些内容,以后再看 RFC 文档都不会有难度了。 + + +HTTP/2 必须先发送一个“连接前言”字符串,然后才能建立正式连接; +HTTP/2 废除了起始行,统一使用头字段,在两端维护字段“Key-Value”的索引表,使用“HPACK”算法压缩头部; +HTTP/2 把报文切分为多种类型的二进制帧,报头里最重要的字段是流标识符,标记帧属于哪个流; +流是 HTTP/2 虚拟的概念,是帧的双向传输序列,相当于 HTTP/1 里的一次“请求 - 应答”; +在一个 HTTP/2 连接上可以并发多个流,也就是多个“请求 - 响应”报文,这就是“多路复用”。 + + +课下作业 + + +HTTP/2 的动态表维护、流状态转换很复杂,你认为 HTTP/2 还是“无状态”的吗? +HTTP/2 的帧最大可以达到 16M,你觉得大帧好还是小帧好? +结合这两讲,谈谈 HTTP/2 是如何解决“队头阻塞”问题的。 + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/32未来之路:HTTP3展望.md b/专栏/透视HTTP协议/32未来之路:HTTP3展望.md new file mode 100644 index 0000000..823e6e8 --- /dev/null +++ b/专栏/透视HTTP协议/32未来之路:HTTP3展望.md @@ -0,0 +1,150 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 32 未来之路:HTTP3展望 + 在前面的两讲里,我们一起学习了 HTTP/2,你也应该看到了 HTTP/2 做出的许多努力,比如头部压缩、二进制分帧、虚拟的“流”与多路复用,性能方面比 HTTP/1 有了很大的提升,“基本上”解决了“队头阻塞”这个“老大难”问题。 + +HTTP/2 的“队头阻塞” + +等等,你可能要发出疑问了:为什么说是“基本上”,而不是“完全”解决了呢? + +这是因为 HTTP/2 虽然使用“帧”“流”“多路复用”,没有了“队头阻塞”,但这些手段都是在应用层里,而在下层,也就是 TCP 协议里,还是会发生“队头阻塞”。 + +这是怎么回事呢? + +让我们从协议栈的角度来仔细看一下。在 HTTP/2 把多个“请求 - 响应”分解成流,交给 TCP 后,TCP 会再拆成更小的包依次发送(其实在 TCP 里应该叫 segment,也就是“段”)。 + +在网络良好的情况下,包可以很快送达目的地。但如果网络质量比较差,像手机上网的时候,就有可能会丢包。而 TCP 为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,其他的包即使已经收到了,也只能放在缓冲区里,上层的应用拿不出来,只能“干着急”。 + +我举个简单的例子: + +客户端用 TCP 发送了三个包,但服务器所在的操作系统只收到了后两个包,第一个包丢了。那么内核里的 TCP 协议栈就只能把已经收到的包暂存起来,“停下”等着客户端重传那个丢失的包,这样就又出现了“队头阻塞”。 + +由于这种“队头阻塞”是 TCP 协议固有的,所以 HTTP/2 即使设计出再多的“花样”也无法解决。 + +Google 在推 SPDY 的时候就已经意识到了这个问题,于是就又发明了一个新的“QUIC”协议,让 HTTP 跑在 QUIC 上而不是 TCP 上。 + +而这个“HTTP over QUIC”就是 HTTP 协议的下一个大版本,HTTP/3。它在 HTTP/2 的基础上又实现了质的飞跃,真正“完美”地解决了“队头阻塞”问题。 + +不过 HTTP/3 目前还处于草案阶段,正式发布前可能会有变动,所以今天我尽量不谈那些不稳定的细节。 + +这里先贴一下 HTTP/3 的协议栈图,让你对它有个大概的了解。 + + + +QUIC 协议 + +从这张图里,你可以看到 HTTP/3 有一个关键的改变,那就是它把下层的 TCP“抽掉”了,换成了 UDP。因为 UDP 是无序的,包之间没有依赖关系,所以就从根本上解决了“队头阻塞”。 + +你一定知道,UDP 是一个简单、不可靠的传输协议,只是对 IP 协议的一层很薄的包装,和 TCP 相比,它实际应用的较少。 + +不过正是因为它简单,不需要建连和断连,通信成本低,也就非常灵活、高效,“可塑性”很强。 + +所以,QUIC 就选定了 UDP,在它之上把 TCP 的那一套连接管理、拥塞窗口、流量控制等“搬”了过来,“去其糟粕,取其精华”,打造出了一个全新的可靠传输协议,可以认为是“新时代的 TCP”。 + + + +QUIC 最早是由 Google 发明的,被称为 gQUIC。而当前正在由 IETF 标准化的 QUIC 被称为 iQUIC。两者的差异非常大,甚至比当年的 SPDY 与 HTTP/2 的差异还要大。 + +gQUIC 混合了 UDP、TLS、HTTP,是一个应用层的协议。而 IETF 则对 gQUIC 做了“清理”,把应用部分分离出来,形成了 HTTP/3,原来的 UDP 部分“下放”到了传输层,所以 iQUIC 有时候也叫“QUIC-transport”。 + +接下来要说的 QUIC 都是指 iQUIC,要记住,它与早期的 gQUIC 不同,是一个传输层的协议,和 TCP 是平级的。 + +QUIC 的特点 + +QUIC 基于 UDP,而 UDP 是“无连接”的,根本就不需要“握手”和“挥手”,所以天生就要比 TCP 快。 + +就像 TCP 在 IP 的基础上实现了可靠传输一样,QUIC 也基于 UDP 实现了可靠传输,保证数据一定能够抵达目的地。它还引入了类似 HTTP/2 的“流”和“多路复用”,单个“流”是有序的,可能会因为丢包而阻塞,但其他“流”不会受到影响。 + +为了防止网络上的中间设备(Middle Box)识别协议的细节,QUIC 全面采用加密通信,可以很好地抵御窜改和“协议僵化”(ossification)。 + +而且,因为 TLS1.3 已经在去年(2018)正式发布,所以 QUIC 就直接应用了 TLS1.3,顺便也就获得了 0-RTT、1-RTT 连接的好处。 + +但 QUIC 并不是建立在 TLS 之上,而是内部“包含”了 TLS。它使用自己的帧“接管”了 TLS 里的“记录”,握手消息、警报消息都不使用 TLS 记录,直接封装成 QUIC 的帧发送,省掉了一次开销。 + +QUIC 内部细节 + +由于 QUIC 在协议栈里比较偏底层,所以我只简略介绍两个内部的关键知识点。 + +QUIC 的基本数据传输单位是包(packet)和帧(frame),一个包由多个帧组成,包面向的是“连接”,帧面向的是“流”。 + +QUIC 使用不透明的“连接 ID”来标记通信的两个端点,客户端和服务器可以自行选择一组 ID 来标记自己,这样就解除了 TCP 里连接对“IP 地址 + 端口”(即常说的四元组)的强绑定,支持“连接迁移”(Connection Migration)。 + + + +比如你下班回家,手机会自动由 4G 切换到 WiFi。这时 IP 地址会发生变化,TCP 就必须重新建立连接。而 QUIC 连接里的两端连接 ID 不会变,所以连接在“逻辑上”没有中断,它就可以在新的 IP 地址上继续使用之前的连接,消除重连的成本,实现连接的无缝迁移。 + +QUIC 的帧里有多种类型,PING、ACK 等帧用于管理连接,而 STREAM 帧专门用来实现流。 + +QUIC 里的流与 HTTP/2 的流非常相似,也是帧的序列,你可以对比着来理解。但 HTTP/2 里的流都是双向的,而 QUIC 则分为双向流和单向流。 + + + +QUIC 帧普遍采用变长编码,最少只要 1 个字节,最多有 8 个字节。流 ID 的最大可用位数是 62,数量上比 HTTP/2 的 2^31 大大增加。 + +流 ID 还保留了最低两位用作标志,第 1 位标记流的发起者,0 表示客户端,1 表示服务器;第 2 位标记流的方向,0 表示双向流,1 表示单向流。 + +所以 QUIC 流 ID 的奇偶性质和 HTTP/2 刚好相反,客户端的 ID 是偶数,从 0 开始计数。 + +HTTP/3 协议 + +了解了 QUIC 之后,再来看 HTTP/3 就容易多了。 + +因为 QUIC 本身就已经支持了加密、流和多路复用,所以 HTTP/3 的工作减轻了很多,把流控制都交给 QUIC 去做。调用的不再是 TLS 的安全接口,也不是 Socket API,而是专门的 QUIC 函数。不过这个“QUIC 函数”还没有形成标准,必须要绑定到某一个具体的实现库。 + +HTTP/3 里仍然使用流来发送“请求 - 响应”,但它自身不需要像 HTTP/2 那样再去定义流,而是直接使用 QUIC 的流,相当于做了一个“概念映射”。 + +HTTP/3 里的“双向流”可以完全对应到 HTTP/2 的流,而“单向流”在 HTTP/3 里用来实现控制和推送,近似地对应 HTTP/2 的 0 号流。 + +由于流管理被“下放”到了 QUIC,所以 HTTP/3 里帧的结构也变简单了。 + +帧头只有两个字段:类型和长度,而且同样都采用变长编码,最小只需要两个字节。 + + + +HTTP/3 里的帧仍然分成数据帧和控制帧两类,HEADERS 帧和 DATA 帧传输数据,但其他一些帧因为在下层的 QUIC 里有了替代,所以在 HTTP/3 里就都消失了,比如 RST_STREAM、WINDOW_UPDATE、PING 等。 + +头部压缩算法在 HTTP/3 里升级成了“QPACK”,使用方式上也做了改变。虽然也分成静态表和动态表,但在流上发送 HEADERS 帧时不能更新字段,只能引用,索引表的更新需要在专门的单向流上发送指令来管理,解决了 HPACK 的“队头阻塞”问题。 + +另外,QPACK 的字典也做了优化,静态表由之前的 61 个增加到了 98 个,而且序号从 0 开始,也就是说“:authority”的编号是 0。 + +HTTP/3 服务发现 + +讲了这么多,不知道你注意到了没有:HTTP/3 没有指定默认的端口号,也就是说不一定非要在 UDP 的 80 或者 443 上提供 HTTP/3 服务。 + +那么,该怎么“发现”HTTP/3 呢? + +这就要用到 HTTP/2 里的“扩展帧”了。浏览器需要先用 HTTP/2 协议连接服务器,然后服务器可以在启动 HTTP/2 连接后发送一个“Alt-Svc”帧,包含一个“h3=host:port”的字符串,告诉浏览器在另一个端点上提供等价的 HTTP/3 服务。 + +浏览器收到“Alt-Svc”帧,会使用 QUIC 异步连接指定的端口,如果连接成功,就会断开 HTTP/2 连接,改用新的 HTTP/3 收发数据。 + +小结 + +HTTP/3 综合了我们之前讲的所有技术(HTTP/1、SSL/TLS、HTTP/2),包含知识点很多,比如队头阻塞、0-RTT 握手、虚拟的“流”、多路复用,算得上是“集大成之作”,需要多下些功夫好好体会。 + + +HTTP/3 基于 QUIC 协议,完全解决了“队头阻塞”问题,弱网环境下的表现会优于 HTTP/2; +QUIC 是一个新的传输层协议,建立在 UDP 之上,实现了可靠传输; +QUIC 内含了 TLS1.3,只能加密通信,支持 0-RTT 快速建连; +QUIC 的连接使用“不透明”的连接 ID,不绑定在“IP 地址 + 端口”上,支持“连接迁移”; +QUIC 的流与 HTTP/2 的流很相似,但分为双向流和单向流; +HTTP/3 没有指定默认端口号,需要用 HTTP/2 的扩展帧“Alt-Svc”来发现。 + + +课下作业 + + +IP 协议要比 UDP 协议省去 8 个字节的成本,也更通用,QUIC 为什么不构建在 IP 协议之上呢? +说一说你理解的 QUIC、HTTP/3 的好处。 +对比一下 HTTP/3 和 HTTP/2 各自的流、帧,有什么相同点和不同点。 + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/33我应该迁移到HTTP2吗?.md b/专栏/透视HTTP协议/33我应该迁移到HTTP2吗?.md new file mode 100644 index 0000000..63ac7ab --- /dev/null +++ b/专栏/透视HTTP协议/33我应该迁移到HTTP2吗?.md @@ -0,0 +1,157 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 33 我应该迁移到HTTP2吗? + 这一讲是“飞翔篇”的最后一讲,而 HTTP 的所有知识也差不多快学完了。 + +前面你已经看到了新的 HTTP/2 和 HTTP/3 协议,了解了它们的特点和工作原理,如果再联系上前几天“安全篇”的 HTTPS,你可能又会发出疑问: + +“刚费了好大的力气升级到 HTTPS,这又出了一个 HTTP/2,还有再次升级的必要吗?” + +与各大浏览器“强推”HTTPS 的待遇不一样,HTTP/2 的公布可谓是“波澜不惊”。虽然它是 HTTP 协议的一个重大升级,但 Apple、Google 等科技巨头并没有像 HTTPS 那样给予大量资源的支持。 + +直到今天,HTTP/2 在互联网上还是处于“不温不火”的状态,虽然已经有了不少的网站改造升级到了 HTTP/2,但普及的速度远不及 HTTPS。 + +所以,你有这样的疑问也是很自然的,升级到 HTTP/2 究竟能给我们带来多少好处呢?到底“值不值”呢? + +HTTP/2 的优点 + +前面的几讲主要关注了 HTTP/2 的内部实现,今天我们就来看看它有哪些优点和缺点。 + +首先要说的是,HTTP/2 最大的一个优点是完全保持了与 HTTP/1 的兼容,在语义上没有任何变化,之前在 HTTP 上的所有投入都不会浪费。 + +因为兼容 HTTP/1,所以 HTTP/2 也具有 HTTP/1 的所有优点,并且“基本”解决了 HTTP/1 的所有缺点,安全与性能兼顾,可以认为是“更安全的 HTTP、更快的 HTTPS”。 + +在安全上,HTTP/2 对 HTTPS 在各方面都做了强化。下层的 TLS 至少是 1.2,而且只能使用前向安全的密码套件(即 ECDHE),这同时也就默认实现了“TLS False Start”,支持 1-RTT 握手,所以不需要再加额外的配置就可以自动实现 HTTPS 加速。 + +安全有了保障,再来看 HTTP/2 在性能方面的改进。 + +你应该知道,影响网络速度的两个关键因素是“带宽”和“延迟”,HTTP/2 的头部压缩、多路复用、流优先级、服务器推送等手段其实都是针对这两个要点。 + +所谓的“带宽”就是网络的传输速度。从最早的 56K/s,到如今的 100M/s,虽然网速已经是“今非昔比”,比从前快了几十倍、几百倍,但仍然是“稀缺资源”,图片、视频这样的多媒体数据很容易会把带宽用尽。 + +节约带宽的基本手段就是压缩,在 HTTP/1 里只能压缩 body,而 HTTP/2 则可以用 HPACK 算法压缩 header,这对高流量的网站非常有价值,有数据表明能节省大概 5%~10% 的流量,这是实实在在的“真金白银”。 + +与 HTTP/1“并发多个连接”不同,HTTP/2 的“多路复用”特性要求对一个域名(或者 IP)只用一个 TCP 连接,所有的数据都在这一个连接上传输,这样不仅节约了客户端、服务器和网络的资源,还可以把带宽跑满,让 TCP 充分“吃饱”。 + +这是为什么呢? + +我们来看一下在 HTTP/1 里的长连接,虽然是双向通信,但任意一个时间点实际上还是单向的:上行请求时下行空闲,下行响应时上行空闲,再加上“队头阻塞”,实际的带宽打了个“对折”还不止(可参考[第 17 讲])。 + +而在 HTTP/2 里,“多路复用”则让 TCP 开足了马力,“全速狂奔”,多个请求响应并发,每时每刻上下行方向上都有流在传输数据,没有空闲的时候,带宽的利用率能够接近 100%。所以,HTTP/2 只使用一个连接,就能抵得过 HTTP/1 里的五六个连接。 + +不过流也可能会有依赖关系,可能会存在等待导致的阻塞,这就是“延迟”,所以 HTTP/2 的其他特性就派上了用场。 + +“优先级”可以让客户端告诉服务器,哪个文件更重要,更需要优先传输,服务器就可以调高流的优先级,合理地分配有限的带宽资源,让高优先级的 HTML、图片更快地到达客户端,尽早加载显示。 + +“服务器推送”也是降低延迟的有效手段,它不需要客户端预先请求,服务器直接就发给客户端,这就省去了客户端解析 HTML 再请求的时间。 + +HTTP/2 的缺点 + +说了一大堆 HTTP/2 的优点,再来看看它有什么缺点吧。 + +听过上一讲 HTTP/3 的介绍,你就知道 HTTP/2 在 TCP 级别还是存在“队头阻塞”的问题。所以,如果网络连接质量差,发生丢包,那么 TCP 会等待重传,传输速度就会降低。 + +另外,在移动网络中发生 IP 地址切换的时候,下层的 TCP 必须重新建连,要再次“握手”,经历“慢启动”,而且之前连接里积累的 HPACK 字典也都消失了,必须重头开始计算,导致带宽浪费和时延。 + +刚才也说了,HTTP/2 对一个域名只开一个连接,所以一旦这个连接出问题,那么整个网站的体验也就变差了。 + +而这些情况下 HTTP/1 反而不会受到影响,因为它“本来就慢”,而且还会对一个域名开 6~8 个连接,顶多其中的一两个连接会“更慢”,其他的连接不会受到影响。 + +应该迁移到 HTTP/2 吗? + +说到这里,你对迁移到 HTTP/2 是否已经有了自己的判断呢? + +在我看来,HTTP/2 处于一个略“尴尬”的位置,前面有“老前辈”HTTP/1,后面有“新来者”HTTP/3,即有“老前辈”的“打压”,又有“新来者”的“追赶”,也就难怪没有获得市场的大力“吹捧”了。 + +但这绝不是说 HTTP/2“一无是处”,实际上 HTTP/2 的性能改进效果是非常明显的,Top 1000 的网站中已经有超过 40% 运行在了 HTTP/2 上,包括知名的 Apple、Facebook、Google、Twitter 等等。仅用了四年的时间,HTTP/2 就拥有了这么大的市场份额和巨头的认可,足以证明它的价值。 + +因为 HTTP/2 的侧重点是“性能”,所以“是否迁移”就需要在这方面进行评估。如果网站的流量很大,那么 HTTP/2 就可以带来可观的收益;反之,如果网站流量比较小,那么升级到 HTTP/2 就没有太多必要了,只要利用现有的 HTTP 再优化就足矣。 + +不过如果你是新建网站,我觉得完全可以跳过 HTTP/1、HTTPS,直接“一步到位”,上 HTTP/2,这样不仅可以获得性能提升,还免去了老旧的“历史包袱”,日后也不会再有迁移的烦恼。 + +顺便再多嘴一句,HTTP/2 毕竟是“下一代”HTTP 协议,它的很多特性也延续到了 HTTP/3,提早升级到 HTTP/2 还可以让你在 HTTP/3 到来时有更多的技术积累和储备,不至于落后于时代。 + +配置 HTTP/2 + +假设你已经决定要使用 HTTP/2,应该如何搭建服务呢? + +因为 HTTP/2“事实上”是加密的,所以如果你已经在“安全篇”里成功迁移到了 HTTPS,那么在 Nginx 里启用 HTTP/2 简直可以说是“不费吹灰之力”,只需要在 server 配置里再多加一个参数就可以搞定了。 + +server { + listen 443 ssl http2; + + + server_name www.xxx.net; + + + ssl_certificate xxx.crt; + ssl_certificate_key xxx.key; + + +注意“listen”指令,在“ssl”后面多了一个“http2”,这就表示在 443 端口上开启了 SSL 加密,然后再启用 HTTP/2。 + +配置服务器推送特性可以使用指令“http2_push”和“http2_push_preload”: + +http2_push /style/xxx.css; +http2_push_preload on; + + +不过如何合理地配置推送是个难题,如果推送给浏览器不需要的资源,反而浪费了带宽。 + +这方面暂时没有一般性的原则指导,你必须根据自己网站的实际情况去“猜测”客户端最需要的数据。 + +优化方面,HTTPS 的一些策略依然适用,比如精简密码套件、ECC 证书、会话复用、HSTS 减少重定向跳转等等。 + +但还有一些优化手段在 HTTP/2 里是不适用的,而且还会有反效果,比如说常见的精灵图(Spriting)、资源内联(inlining)、域名分片(Sharding)等,至于原因是什么,我把它留给你自己去思考(提示,与缓存有关)。 + +还要注意一点,HTTP/2 默认启用 header 压缩(HPACK),但并没有默认启用 body 压缩,所以不要忘了在 Nginx 配置文件里加上“gzip”指令,压缩 HTML、JS 等文本数据。 + +应用层协议协商(ALPN) + +最后说一下 HTTP/2 的“服务发现”吧。 + +你有没有想过,在 URI 里用的都是 HTTPS 协议名,没有版本标记,浏览器怎么知道服务器支持 HTTP/2 呢?为什么上来就能用 HTTP/2,而不是用 HTTP/1 通信呢? + +答案在 TLS 的扩展里,有一个叫“ALPN”(Application Layer Protocol Negotiation)的东西,用来与服务器就 TLS 上跑的应用协议进行“协商”。 + +客户端在发起“Client Hello”握手的时候,后面会带上一个“ALPN”扩展,里面按照优先顺序列出客户端支持的应用协议。 + +就像下图这样,最优先的是“h2”,其次是“http/1.1”,以前还有“spdy”,以后还可能会有“h3”。 + + + +服务器看到 ALPN 扩展以后就可以从列表里选择一种应用协议,在“Server Hello”里也带上“ALPN”扩展,告诉客户端服务器决定使用的是哪一种。因为我们在 Nginx 配置里使用了 HTTP/2 协议,所以在这里它选择的就是“h2”。 + + + +这样在 TLS 握手结束后,客户端和服务器就通过“ALPN”完成了应用层的协议协商,后面就可以使用 HTTP/2 通信了。 + +小结 + +今天我们讨论了是否应该迁移到 HTTP/2,还有应该如何迁移到 HTTP/2。 + + +HTTP/2 完全兼容 HTTP/1,是“更安全的 HTTP、更快的 HTTPS”,头部压缩、多路复用等技术可以充分利用带宽,降低延迟,从而大幅度提高上网体验; +TCP 协议存在“队头阻塞”,所以 HTTP/2 在弱网或者移动网络下的性能表现会不如 HTTP/1; +迁移到 HTTP/2 肯定会有性能提升,但高流量网站效果会更显著; +如果已经升级到了 HTTPS,那么再升级到 HTTP/2 会很简单; +TLS 协议提供“ALPN”扩展,让客户端和服务器协商使用的应用层协议,“发现”HTTP/2 服务。 + + +课下作业 + + +和“安全篇”的第 29 讲类似,结合自己的实际情况,分析一下是否应该迁移到 HTTP/2,有没有难点? +精灵图(Spriting)、资源内联(inlining)、域名分片(Sharding)这些手段为什么会对 HTTP/2 的性能优化造成反效果呢? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/34Nginx:高性能的Web服务器.md b/专栏/透视HTTP协议/34Nginx:高性能的Web服务器.md new file mode 100644 index 0000000..d431716 --- /dev/null +++ b/专栏/透视HTTP协议/34Nginx:高性能的Web服务器.md @@ -0,0 +1,139 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 34 Nginx:高性能的Web服务器 + 经过前面几大模块的学习,你已经完全掌握了 HTTP 的所有知识,那么接下来请收拾一下行囊,整理一下装备,跟我一起去探索 HTTP 之外的广阔天地。 + +现在的互联网非常发达,用户越来越多,网速越来越快,HTTPS 的安全加密、HTTP/2 的多路复用等特性都对 Web 服务器提出了非常高的要求。一个好的 Web 服务器必须要具备稳定、快速、易扩展、易维护等特性,才能够让网站“立于不败之地”。 + +那么,在搭建网站的时候,应该选择什么样的服务器软件呢? + +在开头的几讲里我也提到过,Web 服务器就那么几款,目前市面上主流的只有两个:Apache 和 Nginx,两者合计占据了近 90% 的市场份额。 + +今天我要说的就是其中的 Nginx,它是 Web 服务器的“后起之秀”,虽然比 Apache 小了 10 岁,但增长速度十分迅猛,已经达到了与 Apache“平起平坐”的地位,而在“Top Million”网站中更是超过了 Apache,拥有超过 50% 的用户(参考数据)。 + + + +在这里必须要说一下 Nginx 的正确发音,它应该读成“Engine X”,但我个人感觉“X”念起来太“拗口”,还是比较倾向于读做“Engine ks”,这也与 UNIX、Linux 的发音一致。 + +作为一个 Web 服务器,Nginx 的功能非常完善,完美支持 HTTP/1、HTTPS 和 HTTP/2,而且还在不断进步。当前的主线版本已经发展到了 1.17,正在进行 HTTP/3 的研发,或许一年之后就能在 Nginx 上跑 HTTP/3 了。 + +Nginx 也是我个人的主要研究领域,我也写过相关的书,按理来说今天的课程应该是“手拿把攥”,但真正动笔的时候还是有些犹豫的:很多要点都已经在书里写过了,这次的专栏如果再重复相同的内容就不免有“骗稿费”的嫌疑,应该有些“不一样的东西”。 + +所以我决定抛开书本,换个角度,结合 HTTP 协议来讲 Nginx,带你窥视一下 HTTP 处理的内幕,看看 Web 服务器的工作原理。 + +进程池 + +你也许听说过,Nginx 是个“轻量级”的 Web 服务器,那么这个所谓的“轻量级”是什么意思呢? + +“轻量级”是相对于“重量级”而言的。“重量级”就是指服务器进程很“重”,占用很多资源,当处理 HTTP 请求时会消耗大量的 CPU 和内存,受到这些资源的限制很难提高性能。 + +而 Nginx 作为“轻量级”的服务器,它的 CPU、内存占用都非常少,同样的资源配置下就能够为更多的用户提供服务,其奥秘在于它独特的工作模式。 + + + +在 Nginx 之前,Web 服务器的工作模式大多是“Per-Process”或者“Per-Thread”,对每一个请求使用单独的进程或者线程处理。这就存在创建进程或线程的成本,还会有进程、线程“上下文切换”的额外开销。如果请求数量很多,CPU 就会在多个进程、线程之间切换时“疲于奔命”,平白地浪费了计算时间。 + +Nginx 则完全不同,“一反惯例”地没有使用多线程,而是使用了“进程池 + 单线程”的工作模式。 + +Nginx 在启动的时候会预先创建好固定数量的 worker 进程,在之后的运行过程中不会再 fork 出新进程,这就是进程池,而且可以自动把进程“绑定”到独立的 CPU 上,这样就完全消除了进程创建和切换的成本,能够充分利用多核 CPU 的计算能力。 + +在进程池之上,还有一个“master”进程,专门用来管理进程池。它的作用有点像是 supervisor(一个用 Python 编写的进程管理工具),用来监控进程,自动恢复发生异常的 worker,保持进程池的稳定和服务能力。 + +不过 master 进程完全是 Nginx 自行用 C 语言实现的,这就摆脱了外部的依赖,简化了 Nginx 的部署和配置。 + +I/O 多路复用 + +如果你用 Java、C 等语言写过程序,一定很熟悉“多线程”的概念,使用多线程能够很容易实现并发处理。 + +但多线程也有一些缺点,除了刚才说到的“上下文切换”成本,还有编程模型复杂、数据竞争、同步等问题,写出正确、快速的多线程程序并不是一件容易的事情。 + +所以 Nginx 就选择了单线程的方式,带来的好处就是开发简单,没有互斥锁的成本,减少系统消耗。 + +那么,疑问也就产生了:为什么单线程的 Nginx,处理能力却能够超越其他多线程的服务器呢? + +这要归功于 Nginx 利用了 Linux 内核里的一件“神兵利器”,I/O 多路复用接口,“大名鼎鼎”的 epoll。 + +“多路复用”这个词我们已经在之前的 HTTP/2、HTTP/3 里遇到过好几次,如果你理解了那里的“多路复用”,那么面对 Nginx 的 epoll“多路复用”也就好办了。 + +Web 服务器从根本上来说是“I/O 密集型”而不是“CPU 密集型”,处理能力的关键在于网络收发而不是 CPU 计算(这里暂时不考虑 HTTPS 的加解密),而网络 I/O 会因为各式各样的原因不得不等待,比如数据还没到达、对端没有响应、缓冲区满发不出去等等。 + +这种情形就有点像是 HTTP 里的“队头阻塞”。对于一般的单线程来说 CPU 就会“停下来”,造成浪费。而多线程的解决思路有点类似“并发连接”,虽然有的线程可能阻塞,但由于多个线程并行,总体上看阻塞的情况就不会太严重了。 + +Nginx 里使用的 epoll,就好像是 HTTP/2 里的“多路复用”技术,它把多个 HTTP 请求处理打散成碎片,都“复用”到一个单线程里,不按照先来后到的顺序处理,而是只当连接上真正可读、可写的时候才处理,如果可能发生阻塞就立刻切换出去,处理其他的请求。 + +通过这种方式,Nginx 就完全消除了 I/O 阻塞,把 CPU 利用得“满满当当”,又因为网络收发并不会消耗太多 CPU 计算能力,也不需要切换进程、线程,所以整体的 CPU 负载是相当低的。 + +这里我画了一张 Nginx“I/O 多路复用”的示意图,你可以看到,它的形式与 HTTP/2 的流非常相似,每个请求处理单独来看是分散、阻塞的,但因为都复用到了一个线程里,所以资源的利用率非常高。 + + + +epoll 还有一个特点,大量的连接管理工作都是在操作系统内核里做的,这就减轻了应用程序的负担,所以 Nginx 可以为每个连接只分配很小的内存维护状态,即使有几万、几十万的并发连接也只会消耗几百 M 内存,而其他的 Web 服务器这个时候早就“Memory not enough”了。 + +多阶段处理 + +有了“进程池”和“I/O 多路复用”,Nginx 是如何处理 HTTP 请求的呢? + +Nginx 在内部也采用的是“化整为零”的思路,把整个 Web 服务器分解成了多个“功能模块”,就好像是乐高积木,可以在配置文件里任意拼接搭建,从而实现了高度的灵活性和扩展性。 + +Nginx 的 HTTP 处理有四大类模块: + + +handler 模块:直接处理 HTTP 请求; +filter 模块:不直接处理请求,而是加工过滤响应报文; +upstream 模块:实现反向代理功能,转发请求到其他服务器; +balance 模块:实现反向代理时的负载均衡算法。 + + +因为 upstream 模块和 balance 模块实现的是代理功能,Nginx 作为“中间人”,运行机制比较复杂,所以我今天只讲 handler 模块和 filter 模块。 + +不知道你有没有了解过“设计模式”这方面的知识,其中有一个非常有用的模式叫做“职责链”。它就好像是工厂里的流水线,原料从一头流入,线上有许多工人会进行各种加工处理,最后从另一头出来的就是完整的产品。 + +Nginx 里的 handler 模块和 filter 模块就是按照“职责链”模式设计和组织的,HTTP 请求报文就是“原材料”,各种模块就是工厂里的工人,走完模块构成的“流水线”,出来的就是处理完成的响应报文。 + +下面的这张图显示了 Nginx 的“流水线”,在 Nginx 里的术语叫“阶段式处理”(Phases),一共有 11 个阶段,每个阶段里又有许多各司其职的模块。 + + + +我简单列几个与我们的课程相关的模块吧: + + +charset 模块实现了字符集编码转换;([第 15 讲]) +chunked 模块实现了响应数据的分块传输;([第 16 讲]) +range 模块实现了范围请求,只返回数据的一部分;([第 16 讲]) +rewrite 模块实现了重定向和跳转,还可以使用内置变量自定义跳转的 URI;([第 18 讲]) +not_modified 模块检查头字段“if-Modified-Since”和“If-None-Match”,处理条件请求;([第 20 讲]) +realip 模块处理“X-Real-IP”“X-Forwarded-For”等字段,获取客户端的真实 IP 地址;([第 21 讲]) +ssl 模块实现了 SSL/TLS 协议支持,读取磁盘上的证书和私钥,实现 TLS 握手和 SNI、ALPN 等扩展功能;([安全篇]) +http_v2 模块实现了完整的 HTTP/2 协议。([飞翔篇]) + + +在这张图里,你还可以看到 limit_conn、limit_req、access、log 等其他模块,它们实现的是限流限速、访问控制、日志等功能,不在 HTTP 协议规定之内,但对于运行在现实世界的 Web 服务器却是必备的。 + +如果你有 C 语言基础,感兴趣的话可以下载 Nginx 的源码,在代码级别仔细看看 HTTP 的处理过程。 + +小结 + + +Nginx 是一个高性能的 Web 服务器,它非常的轻量级,消耗的 CPU、内存很少; +Nginx 采用“master/workers”进程池架构,不使用多线程,消除了进程、线程切换的成本; +Nginx 基于 epoll 实现了“I/O 多路复用”,不会阻塞,所以性能很高; +Nginx 使用了“职责链”模式,多个模块分工合作,自由组合,以流水线的方式处理 HTTP 请求。 + + +课下作业 + + +你是怎么理解进程、线程上下文切换时的成本的,为什么 Nginx 要尽量避免? +试着自己描述一下 Nginx 用进程、epoll、模块流水线处理 HTTP 请求的过程。 + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/35OpenResty:更灵活的Web服务器.md b/专栏/透视HTTP协议/35OpenResty:更灵活的Web服务器.md new file mode 100644 index 0000000..937e722 --- /dev/null +++ b/专栏/透视HTTP协议/35OpenResty:更灵活的Web服务器.md @@ -0,0 +1,138 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 35 OpenResty:更灵活的Web服务器 + 在上一讲里,我们看到了高性能的 Web 服务器 Nginx,它资源占用少,处理能力高,是搭建网站的首选。 + +虽然 Nginx 成为了 Web 服务器领域无可争议的“王者”,但它也并不是没有缺点的,毕竟它已经 15 岁了。 + +“一个人很难超越时代,而时代却可以轻易超越所有人”,Nginx 当初设计时针对的应用场景已经发生了变化,它的一些缺点也就暴露出来了。 + +Nginx 的服务管理思路延续了当时的流行做法,使用磁盘上的静态配置文件,所以每次修改后必须重启才能生效。 + +这在业务频繁变动的时候是非常致命的(例如流行的微服务架构),特别是对于拥有成千上万台服务器的网站来说,仅仅增加或者删除一行配置就要分发、重启所有的机器,对运维是一个非常大的挑战,要耗费很多的时间和精力,成本很高,很不灵活,难以“随需应变”。 + +那么,有没有这样的一个 Web 服务器,它有 Nginx 的优点却没有 Nginx 的缺点,既轻量级、高性能,又灵活、可动态配置呢? + +这就是我今天要说的 OpenResty,它是一个“更好更灵活的 Nginx”。 + +OpenResty 是什么? + +其实你对 OpenResty 并不陌生,这个专栏的实验环境就是用 OpenResty 搭建的,这么多节课程下来,你应该或多或少对它有了一些印象吧。 + +OpenResty 诞生于 2009 年,到现在刚好满 10 周岁。它的创造者是当时就职于某宝的“神级”程序员章亦春,网名叫“agentzh”。 + +OpenResty 并不是一个全新的 Web 服务器,而是基于 Nginx,它利用了 Nginx 模块化、可扩展的特性,开发了一系列的增强模块,并把它们打包整合,形成了一个“一站式”的 Web 开发平台。 + +虽然 OpenResty 的核心是 Nginx,但它又超越了 Nginx,关键就在于其中的 ngx_lua 模块,把小巧灵活的 Lua 语言嵌入了 Nginx,可以用脚本的方式操作 Nginx 内部的进程、多路复用、阶段式处理等各种构件。 + +脚本语言的好处你一定知道,它不需要编译,随写随执行,这就免去了 C 语言编写模块漫长的开发周期。而且 OpenResty 还把 Lua 自身的协程与 Nginx 的事件机制完美结合在一起,优雅地实现了许多其他语言所没有的“同步非阻塞”编程范式,能够轻松开发出高性能的 Web 应用。 + +目前 OpenResty 有两个分支,分别是开源、免费的“OpenResty”和闭源、商业产品的“OpenResty+”,运作方式有社区支持、OpenResty 基金会、OpenResty.Inc 公司,还有其他的一些外界赞助(例如 Kong、CloudFlare),正在蓬勃发展。 + + + +顺便说一下 OpenResty 的官方 logo,是一只展翅飞翔的海鸥,选择海鸥是因为“鸥”与 OpenResty 的发音相同。另外,这个 logo 的形状也像是左手比出的一个“OK”姿势,正好也是一个“O”。 + +动态的 Lua + +刚才说了,OpenResty 里的一个关键模块是 ngx_lua,它为 Nginx 引入了脚本语言 Lua。 + +Lua 是一个比较“小众”的语言,虽然历史比较悠久,但名气却没有 PHP、Python、JavaScript 大,这主要与它的自身定位有关。 + + + +Lua 的设计目标是嵌入到其他应用程序里运行,为其他编程语言带来“脚本化”能力,所以它的“个头”比较小,功能集有限,不追求“大而全”,而是“小而美”,大多数时间都“隐匿”在其他应用程序的后面,是“无名英雄”。 + +你或许玩过或者听说过《魔兽世界》《愤怒的小鸟》吧,它们就在内部嵌入了 Lua,使用 Lua 来调用底层接口,充当“胶水语言”(glue language),编写游戏逻辑脚本,提高开发效率。 + +OpenResty 选择 Lua 作为“工作语言”也是基于同样的考虑。因为 Nginx C 开发实在是太麻烦了,限制了 Nginx 的真正实力。而 Lua 作为“最快的脚本语言”恰好可以成为 Nginx 的完美搭档,既可以简化开发,性能上又不会有太多的损耗。 + +作为脚本语言,Lua 还有一个重要的“代码热加载”特性,不需要重启进程,就能够从磁盘、Redis 或者任何其他地方加载数据,随时替换内存里的代码片段。这就带来了“动态配置”,让 OpenResty 能够永不停机,在微秒、毫秒级别实现配置和业务逻辑的实时更新,比起 Nginx 秒级的重启是一个极大的进步。 + +你可以看一下实验环境的“www/lua”目录,里面存放了我写的一些测试 HTTP 特性的 Lua 脚本,代码都非常简单易懂,就像是普通的英语“阅读理解”,这也是 Lua 的另一个优势:易学习、易上手。 + +高效率的 Lua + +OpenResty 能够高效运行的一大“秘技”是它的“同步非阻塞”编程范式,如果你要开发 OpenResty 应用就必须时刻铭记于心。 + +“同步非阻塞”本质上还是一种“多路复用”,我拿上一讲的 Nginx epoll 来对比解释一下。 + +epoll 是操作系统级别的“多路复用”,运行在内核空间。而 OpenResty 的“同步非阻塞”则是基于 Lua 内建的“协程”,是应用程序级别的“多路复用”,运行在用户空间,所以它的资源消耗要更少。 + +OpenResty 里每一段 Lua 程序都由协程来调度运行。和 Linux 的 epoll 一样,每当可能发生阻塞的时候“协程”就会立刻切换出去,执行其他的程序。这样单个处理流程是“阻塞”的,但整个 OpenResty 却是“非阻塞的”,多个程序都“复用”在一个 Lua 虚拟机里运行。 + + + +下面的代码是一个简单的例子,读取 POST 发送的 body 数据,然后再发回客户端: + +ngx.req.read_body() -- 同步非阻塞 (1) + +local data = ngx.req.get_body_data() +if data then + ngx.print("body: ", data) -- 同步非阻塞 (2) +end + + +代码中的“ngx.req.read_body”和“ngx.print”分别是数据的收发动作,只有收到数据才能发送数据,所以是“同步”的。 + +但即使因为网络原因没收到或者发不出去,OpenResty 也不会在这里阻塞“干等着”,而是做个“记号”,把等待的这段 CPU 时间用来处理其他的请求,等网络可读或者可写时再“回来”接着运行。 + +假设收发数据的等待时间是 10 毫秒,而真正 CPU 处理的时间是 0.1 毫秒,那么 OpenResty 就可以在这 10 毫秒内同时处理 100 个请求,而不是把这 100 个请求阻塞排队,用 1000 毫秒来处理。 + +除了“同步非阻塞”,OpenResty 还选用了LuaJIT作为 Lua 语言的“运行时(Runtime)”,进一步“挖潜增效”。 + +LuaJIT 是一个高效的 Lua 虚拟机,支持 JIT(Just In Time)技术,可以把 Lua 代码即时编译成“本地机器码”,这样就消除了脚本语言解释运行的劣势,让 Lua 脚本跑得和原生 C 代码一样快。 + +另外,LuaJIT 还为 Lua 语言添加了一些特别的增强,比如二进制位运算库 bit,内存优化库 table,还有 FFI(Foreign Function Interface),让 Lua 直接调用底层 C 函数,比原生的压栈调用快很多。 + +阶段式处理 + +和 Nginx 一样,OpenResty 也使用“流水线”来处理 HTTP 请求,底层的运行基础是 Nginx 的“阶段式处理”,但它又有自己的特色。 + +Nginx 的“流水线”是由一个个 C 模块组成的,只能在静态文件里配置,开发困难,配置麻烦(相对而言)。而 OpenResty 的“流水线”则是由一个个的 Lua 脚本组成的,不仅可以从磁盘上加载,也可以从 Redis、MySQL 里加载,而且编写、调试的过程非常方便快捷。 + +下面我画了一张图,列出了 OpenResty 的阶段,比起 Nginx,OpenResty 的阶段更注重对 HTTP 请求响应报文的加工和处理。 + + + +OpenResty 里有几个阶段与 Nginx 是相同的,比如 rewrite、access、content、filter,这些都是标准的 HTTP 处理。 + +在这几个阶段里可以用“xxx_by_lua”指令嵌入 Lua 代码,执行重定向跳转、访问控制、产生响应、负载均衡、过滤报文等功能。因为 Lua 的脚本语言特性,不用考虑内存分配、资源回收释放等底层的细节问题,可以专注于编写非常复杂的业务逻辑,比 C 模块的开发效率高很多,即易于扩展又易于维护。 + +OpenResty 里还有两个不同于 Nginx 的特殊阶段。 + +一个是“init 阶段”,它又分成“master init”和“worker init”,在 master 进程和 worker 进程启动的时候运行。这个阶段还没有开始提供服务,所以慢一点也没关系,可以调用一些阻塞的接口初始化服务器,比如读取磁盘、MySQL,加载黑白名单或者数据模型,然后放进共享内存里供运行时使用。 + +另一个是“ssl 阶段”,这算得上是 OpenResty 的一大创举,可以在 TLS 握手时动态加载证书,或者发送“OCSP Stapling”。 + +还记得[第 29 讲]里说的“SNI 扩展”吗?Nginx 可以依据“服务器名称指示”来选择证书实现 HTTPS 虚拟主机,但静态配置很不灵活,要编写很多雷同的配置块。虽然后来 Nginx 增加了变量支持,但它每次握手都要读磁盘,效率很低。 + +而在 OpenResty 里就可以使用指令“ssl_certificate_by_lua”,编写 Lua 脚本,读取 SNI 名字后,直接从共享内存或者 Redis 里获取证书。不仅没有读盘阻塞,而且证书也是完全动态可配置的,无需修改配置文件就能够轻松支持大量的 HTTPS 虚拟主机。 + +小结 + + +Nginx 依赖于磁盘上的静态配置文件,修改后必须重启才能生效,缺乏灵活性; +OpenResty 基于 Nginx,打包了很多有用的模块和库,是一个高性能的 Web 开发平台; +OpenResty 的工作语言是 Lua,它小巧灵活,执行效率高,支持“代码热加载”; +OpenResty 的核心编程范式是“同步非阻塞”,使用协程,不需要异步回调函数; +OpenResty 也使用“阶段式处理”的工作模式,但因为在阶段里执行的都是 Lua 代码,所以非常灵活,配合 Redis 等外部数据库能够实现各种动态配置。 + + +课下作业 + + +谈一下这些天你对实验环境里 OpenResty 的感想和认识。 +你觉得 Nginx 和 OpenResty 的“阶段式处理”有什么好处?对你的实际工作有没有启发? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/36WAF:保护我们的网络服务.md b/专栏/透视HTTP协议/36WAF:保护我们的网络服务.md new file mode 100644 index 0000000..77c3515 --- /dev/null +++ b/专栏/透视HTTP协议/36WAF:保护我们的网络服务.md @@ -0,0 +1,176 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 36 WAF:保护我们的网络服务 + 在前些天的“安全篇”里,我谈到了 HTTPS,它使用了 SSL/TLS 协议,加密整个通信过程,能够防止恶意窃听和窜改,保护我们的数据安全。 + +但 HTTPS 只是网络安全中很小的一部分,仅仅保证了“通信链路安全”,让第三方无法得知传输的内容。在通信链路的两端,也就是客户端和服务器,它是无法提供保护的。 + +因为 HTTP 是一个开放的协议,Web 服务都运行在公网上,任何人都可以访问,所以天然就会成为黑客的攻击目标。 + +而且黑客的本领比我们想象的还要大得多。虽然不能在传输过程中做手脚,但他们还可以“假扮”成合法的用户访问系统,然后伺机搞破坏。 + +Web 服务遇到的威胁 + +黑客都有哪些手段来攻击 Web 服务呢?我给你大概列出几种常见的方式。 + +第一种叫“DDoS”攻击(distributed denial-of-service attack),有时候也叫“洪水攻击”。 + +黑客会控制许多“僵尸”计算机,向目标服务器发起大量无效请求。因为服务器无法区分正常用户和黑客,只能“照单全收”,这样就挤占了正常用户所应有的资源。如果黑客的攻击强度很大,就会像“洪水”一样对网站的服务能力造成冲击,耗尽带宽、CPU 和内存,导致网站完全无法提供正常服务。 + +“DDoS”攻击方式比较“简单粗暴”,虽然很有效,但不涉及 HTTP 协议内部的细节,“技术含量”比较低,不过下面要说的几种手段就不一样了。 + +网站后台的 Web 服务经常会提取出 HTTP 报文里的各种信息,应用于业务,有时会缺乏严格的检查。因为 HTTP 报文在语义结构上非常松散、灵活,URI 里的 query 字符串、头字段、body 数据都可以任意设置,这就带来了安全隐患,给了黑客“代码注入”的可能性。 + +黑客可以精心编制 HTTP 请求报文,发送给服务器,服务程序如果没有做防备,就会“上当受骗”,执行黑客设定的代码。 + +“SQL 注入”(SQL injection)应该算是最著名的一种“代码注入”攻击了,它利用了服务器字符串拼接形成 SQL 语句的漏洞,构造出非正常的 SQL 语句,获取数据库内部的敏感信息。 + +另一种“HTTP 头注入”攻击的方式也是类似的原理,它在“Host”“User-Agent”“X-Forwarded-For”等字段里加入了恶意数据或代码,服务端程序如果解析不当,就会执行预设的恶意代码。 + +在之前的[第 19 讲]里,也说过一种利用 Cookie 的攻击手段,“跨站脚本”(XSS)攻击,它属于“JS 代码注入”,利用 JavaScript 脚本获取未设防的 Cookie。 + +网络应用防火墙 + +面对这么多的黑客攻击手段,我们应该怎么防御呢? + +这就要用到“网络应用防火墙”(Web Application Firewall)了,简称为“WAF”。 + +你可能对传统的“防火墙”比较熟悉。传统“防火墙”工作在三层或者四层,隔离了外网和内网,使用预设的规则,只允许某些特定 IP 地址和端口号的数据包通过,拒绝不符合条件的数据流入或流出内网,实质上是一种网络数据过滤设备。 + +WAF 也是一种“防火墙”,但它工作在七层,看到的不仅是 IP 地址和端口号,还能看到整个 HTTP 报文,所以就能够对报文内容做更深入细致的审核,使用更复杂的条件、规则来过滤数据。 + +说白了,WAF 就是一种“HTTP 入侵检测和防御系统”。 + + + +WAF 都能干什么呢? + +通常一款产品能够称为 WAF,要具备下面的一些功能: + + +IP 黑名单和白名单,拒绝黑名单上地址的访问,或者只允许白名单上的用户访问; +URI 黑名单和白名单,与 IP 黑白名单类似,允许或禁止对某些 URI 的访问; +防护 DDoS 攻击,对特定的 IP 地址限连限速; +过滤请求报文,防御“代码注入”攻击; +过滤响应报文,防御敏感信息外泄; +审计日志,记录所有检测到的入侵操作。 + + +听起来 WAF 好像很高深,但如果你理解了它的工作原理,其实也不难。 + +它就像是平时编写程序时必须要做的函数入口参数检查,拿到 HTTP 请求、响应报文,用字符串处理函数看看有没有关键字、敏感词,或者用正则表达式做一下模式匹配,命中了规则就执行对应的动作,比如返回 403/404。 + +如果你比较熟悉 Apache、Nginx、OpenResty,可以自己改改配置文件,写点 JS 或者 Lua 代码,就能够实现基本的 WAF 功能。 + +比如说,在 Nginx 里实现 IP 地址黑名单,可以利用“map”指令,从变量 $remote_addr 获取 IP 地址,在黑名单上就映射为值 1,然后在“if”指令里判断: + +map $remote_addr $blocked { + default 0; + "1.2.3.4" 1; + "5.6.7.8" 1; +} + + +if ($blocked) { + return 403 "you are blocked."; +} + + +Nginx 的配置文件只能静态加载,改名单必须重启,比较麻烦。如果换成 OpenResty 就会非常方便,在 access 阶段进行判断,IP 地址列表可以使用 cosocket 连接外部的 Redis、MySQL 等数据库,实现动态更新: + +local ip_addr = ngx.var.remote_addr + +local rds = redis:new() +if rds:get(ip_addr) == 1 then + ngx.exit(403) +end + + +看了上面的两个例子,你是不是有种“跃跃欲试”的冲动了,想自己动手开发一个 WAF? + +不过我必须要提醒你,在网络安全领域必须时刻记得“木桶效应”(也叫“短板效应”)。网站的整体安全不在于你加固的最强的那个方向,而是在于你可能都没有意识到的“短板”。黑客往往会“避重就轻”,只要发现了网站的一个弱点,就可以“一点突破”,其他方面的安全措施也就都成了“无用功”。 + +所以,使用 WAF 最好“不要重新发明轮子”,而是使用现有的、比较成熟的、经过实际考验的 WAF 产品。 + +全面的 WAF 解决方案 + +这里我就要“隆重”介绍一下 WAF 领域里的最顶级产品了:ModSecurity,它可以说是 WAF 界“事实上的标准”。 + +ModSecurity 是一个开源的、生产级的 WAF 工具包,历史很悠久,比 Nginx 还要大几岁。它开始于一个私人项目,后来被商业公司 Breach Security 收购,现在则是由 TrustWave 公司的 SpiderLabs 团队负责维护。 + +ModSecurity 最早是 Apache 的一个模块,只能运行在 Apache 上。因为其品质出众,大受欢迎,后来的 2.x 版添加了 Nginx 和 IIS 支持,但因为底层架构存在差异,不够稳定。 + +所以,这两年 SpiderLabs 团队就开发了全新的 3.0 版本,移除了对 Apache 架构的依赖,使用新的“连接器”来集成进 Apache 或者 Nginx,比 2.x 版更加稳定和快速,误报率也更低。 + +ModSecurity 有两个核心组件。第一个是“规则引擎”,它实现了自定义的“SecRule”语言,有自己特定的语法。但“SecRule”主要基于正则表达式,还是不够灵活,所以后来也引入了 Lua,实现了脚本化配置。 + +ModSecurity 的规则引擎使用 C++11 实现,可以从GitHub上下载源码,然后集成进 Nginx。因为它比较庞大,编译很费时间,所以最好编译成动态模块,在配置文件里用指令“load_module”加载: + +load_module modules/ngx_http_modsecurity_module.so; + + +只有引擎还不够,要让引擎运转起来,还需要完善的防御规则,所以 ModSecurity 的第二个核心组件就是它的“规则集”。 + +ModSecurity 源码提供一个基本的规则配置文件“modsecurity.conf-recommended”,使用前要把它的后缀改成“conf”。 + +有了规则集,就可以在 Nginx 配置文件里加载,然后启动规则引擎: + +modsecurity on; +modsecurity_rules_file /path/to/modsecurity.conf; + + +“modsecurity.conf”文件默认只有检测功能,不提供入侵阻断,这是为了防止误杀误报,把“SecRuleEngine”后面改成“On”就可以开启完全的防护: + +#SecRuleEngine DetectionOnly +SecRuleEngine On + + +基本的规则集之外,ModSecurity 还额外提供一个更完善的规则集,为网站提供全面可靠的保护。这个规则集的全名叫“OWASP ModSecurity 核心规则集”(Open Web Application Security Project ModSecurity Core Rule Set),因为名字太长了,所以有时候会简称为“核心规则集”或者“CRS”。 + +CRS 也是完全开源、免费的,可以从 GitHub 上下载: + +git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git + + +其中有一个“crs-setup.conf.example”的文件,它是 CRS 的基本配置,可以用“Include”命令添加到“modsecurity.conf”里,然后再添加“rules”里的各种规则。 + +Include /path/to/crs-setup.conf +Include /path/to/rules/*.conf + + +你如果有兴趣可以看一下这些配置文件,里面用“SecRule”定义了很多的规则,基本的形式是“SecRule 变量 运算符 动作”。不过 ModSecurity 的这套语法“自成一体”,比较复杂,要完全掌握不是一朝一夕的事情,我就不详细解释了。 + +另外,ModSecurity 还有强大的审计日志(Audit Log)功能,记录任何可疑的数据,供事后离线分析。但在生产环境中会遇到大量的攻击,日志会快速增长,消耗磁盘空间,而且写磁盘也会影响 Nginx 的性能,所以一般建议把它关闭: + +SecAuditEngine off #RelevantOnly +SecAuditLog /var/log/modsec_audit.log + + +小结 + +今天我们一起学习了“网络应用防火墙”,也就是 WAF,使用它可以加固 Web 服务。 + + +Web 服务通常都运行在公网上,容易受到“DDoS”、“代码注入”等各种黑客攻击,影响正常的服务,所以必须要采取措施加以保护; +WAF 是一种“HTTP 入侵检测和防御系统”,工作在七层,为 Web 服务提供全面的防护; +ModSecurity 是一个开源的、生产级的 WAF 产品,核心组成部分是“规则引擎”和“规则集”,两者的关系有点像杀毒引擎和病毒特征库; +WAF 实质上是模式匹配与数据过滤,所以会消耗 CPU,增加一些计算成本,降低服务能力,使用时需要在安全与性能之间找到一个“平衡点”。 + + +课下作业 + + +HTTPS 为什么不能防御 DDoS、代码注入等攻击呢? +你还知道有哪些手段能够抵御网络攻击吗? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/37CDN:加速我们的网络服务.md b/专栏/透视HTTP协议/37CDN:加速我们的网络服务.md new file mode 100644 index 0000000..07a5c41 --- /dev/null +++ b/专栏/透视HTTP协议/37CDN:加速我们的网络服务.md @@ -0,0 +1,132 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 37 CDN:加速我们的网络服务 + 在正式开讲前,我们先来看看到现在为止 HTTP 手头都有了哪些“武器”。 + +协议方面,HTTPS 强化通信链路安全、HTTP/2 优化传输效率;应用方面,Nginx/OpenResty 提升网站服务能力,WAF 抵御网站入侵攻击,讲到这里,你是不是感觉还少了点什么? + +没错,在应用领域,还缺一个在外部加速 HTTP 协议的服务,这个就是我们今天要说的 CDN(Content Delivery Network 或 Content Distribution Network),中文名叫“内容分发网络”。 + +为什么要有网络加速? + +你可能要问了,HTTP 的传输速度也不算差啊,而且还有更好的 HTTP/2,为什么还要再有一个额外的 CDN 来加速呢?是不是有点“多此一举”呢? + +这里我们就必须要考虑现实中会遇到的问题了。你一定知道,光速是有限的,虽然每秒 30 万公里,但这只是真空中的上限,在实际的电缆、光缆中的速度会下降到原本的三分之二左右,也就是 20 万公里 / 秒,这样一来,地理位置的距离导致的传输延迟就会变得比较明显了。 + +比如,北京到广州直线距离大约是 2000 公里,按照刚才的 20 万公里 / 秒来算的话,发送一个请求单程就要 10 毫秒,往返要 20 毫秒,即使什么都不干,这个“硬性”的时延也是躲不过的。 + +另外不要忘了, 互联网从逻辑上看是一张大网,但实际上是由许多小网络组成的,这其中就有小网络“互连互通”的问题,典型的就是各个电信运营商的网络,比如国内的电信、联通、移动三大家。 + + + +这些小网络内部的沟通很顺畅,但网络之间却只有很少的联通点。如果你在 A 网络,而网站在 C 网络,那么就必须“跨网”传输,和成千上万的其他用户一起去“挤”连接点的“独木桥”。而带宽终究是有限的,能抢到多少只能看你的运气。 + +还有,网络中还存在许多的路由器、网关,数据每经过一个节点,都要停顿一下,在二层、三层解析转发,这也会消耗一定的时间,带来延迟。 + +把这些因素再放到全球来看,地理距离、运营商网络、路由转发的影响就会成倍增加。想象一下,你在北京,访问旧金山的网站,要跨越半个地球,中间会有多少环节,会增加多少时延? + +最终结果就是,如果仅用现有的 HTTP 传输方式,大多数网站都会访问速度缓慢、用户体验糟糕。 + +什么是 CDN? + +这个时候 CDN 就出现了,它就是专门为解决“长距离”上网络访问速度慢而诞生的一种网络应用服务。 + +从名字上看,CDN 有三个关键词:“内容”“分发”和“网络”。 + +先看一下“网络”的含义。CDN 的最核心原则是“就近访问”,如果用户能够在本地几十公里的距离之内获取到数据,那么时延就基本上变成 0 了。 + +所以 CDN 投入了大笔资金,在全国、乃至全球的各个大枢纽城市都建立了机房,部署了大量拥有高存储高带宽的节点,构建了一个专用网络。这个网络是跨运营商、跨地域的,虽然内部也划分成多个小网络,但它们之间用高速专有线路连接,是真正的“信息高速公路”,基本上可以认为不存在网络拥堵。 + +有了这个高速的专用网之后,CDN 就要“分发”源站的“内容”了,用到的就是在[第 22 讲]说过的“缓存代理”技术。使用“推”或者“拉”的手段,把源站的内容逐级缓存到网络的每一个节点上。 + +于是,用户在上网的时候就不直接访问源站,而是访问离他“最近的”一个 CDN 节点,术语叫“边缘节点”(edge node),其实就是缓存了源站内容的代理服务器,这样一来就省去了“长途跋涉”的时间成本,实现了“网络加速”。 + + + +那么,CDN 都能加速什么样的“内容”呢? + +在 CDN 领域里,“内容”其实就是 HTTP 协议里的“资源”,比如超文本、图片、视频、应用程序安装包等等。 + +资源按照是否可缓存又分为“静态资源”和“动态资源”。所谓的“静态资源”是指数据内容“静态不变”,任何时候来访问都是一样的,比如图片、音频。所谓的“动态资源”是指数据内容是“动态变化”的,也就是由后台服务计算生成的,每次访问都不一样,比如商品的库存、微博的粉丝数等。 + +很显然,只有静态资源才能够被缓存加速、就近访问,而动态资源只能由源站实时生成,即使缓存了也没有意义。不过,如果动态资源指定了“Cache-Control”,允许缓存短暂的时间,那它在这段时间里也就变成了“静态资源”,可以被 CDN 缓存加速。 + +套用一句广告词来形容 CDN 吧,我觉得非常恰当:“我们不生产内容,我们只是内容的搬运工。” + +CDN,正是把“数据传输”这件看似简单的事情“做大做强”“做专做精”,就像专门的快递公司一样,在互联网世界里实现了它的价值。 + +CDN 的负载均衡 + +我们再来看看 CDN 是具体怎么运行的,它有两个关键组成部分:全局负载均衡和缓存系统,对应的是 DNS([第 6 讲])和缓存代理([第 21 讲]、[第 22 讲])技术。 + +全局负载均衡(Global Sever Load Balance)一般简称为 GSLB,它是 CDN 的“大脑”,主要的职责是当用户接入网络的时候在 CDN 专网中挑选出一个“最佳”节点提供服务,解决的是用户如何找到“最近的”边缘节点,对整个 CDN 网络进行“负载均衡”。 + + + +GSLB 最常见的实现方式是“DNS 负载均衡”,这个在[第 6 讲]里也说过,不过 GSLB 的方式要略微复杂一些。 + +原来没有 CDN 的时候,权威 DNS 返回的是网站自己服务器的实际 IP 地址,浏览器收到 DNS 解析结果后直连网站。 + +但加入 CDN 后就不一样了,权威 DNS 返回的不是 IP 地址,而是一个 CNAME( Canonical Name ) 别名记录,指向的就是 CDN 的 GSLB。它有点像是 HTTP/2 里“Alt-Svc”的意思,告诉外面:“我这里暂时没法给你真正的地址,你去另外一个地方再查查看吧。” + +因为没拿到 IP 地址,于是本地 DNS 就会向 GSLB 再发起请求,这样就进入了 CDN 的全局负载均衡系统,开始“智能调度”,主要的依据有这么几个: + + +看用户的 IP 地址,查表得知地理位置,找相对最近的边缘节点; +看用户所在的运营商网络,找相同网络的边缘节点; +检查边缘节点的负载情况,找负载较轻的节点; +其他,比如节点的“健康状况”、服务能力、带宽、响应时间等。 + + +GSLB 把这些因素综合起来,用一个复杂的算法,最后找出一台“最合适”的边缘节点,把这个节点的 IP 地址返回给用户,用户就可以“就近”访问 CDN 的缓存代理了。 + +CDN 的缓存代理 + +缓存系统是 CDN 的另一个关键组成部分,相当于 CDN 的“心脏”。如果缓存系统的服务能力不够,不能很好地满足用户的需求,那 GSLB 调度算法再优秀也没有用。 + +但互联网上的资源是无穷无尽的,不管 CDN 厂商有多大的实力,也不可能把所有资源都缓存起来。所以,缓存系统只能有选择地缓存那些最常用的那些资源。 + +这里就有两个 CDN 的关键概念:“命中”和“回源”。 + +“命中”就是指用户访问的资源恰好在缓存系统里,可以直接返回给用户;“回源”则正相反,缓存里没有,必须用代理的方式回源站取。 + +相应地,也就有了两个衡量 CDN 服务质量的指标:“命中率”和“回源率”。命中率就是命中次数与所有访问次数之比,回源率是回源次数与所有访问次数之比。显然,好的 CDN 应该是命中率越高越好,回源率越低越好。现在的商业 CDN 命中率都在 90% 以上,相当于把源站的服务能力放大了 10 倍以上。 + +怎么样才能尽可能地提高命中率、降低回源率呢? + +首先,最基本的方式就是在存储系统上下功夫,硬件用高速 CPU、大内存、万兆网卡,再搭配 TB 级别的硬盘和快速的 SSD。软件方面则不断“求新求变”,各种新的存储软件都会拿来尝试,比如 Memcache、Redis、Ceph,尽可能地高效利用存储,存下更多的内容。 + +其次,缓存系统也可以划分出层次,分成一级缓存节点和二级缓存节点。一级缓存配置高一些,直连源站,二级缓存配置低一些,直连用户。回源的时候二级缓存只找一级缓存,一级缓存没有才回源站,这样最终“扇入度”就缩小了,可以有效地减少真正的回源。 + +第三个就是使用高性能的缓存服务,据我所知,目前国内的 CDN 厂商内部都是基于开源软件定制的。最常用的是专门的缓存代理软件 Squid、Varnish,还有新兴的 ATS(Apache Traffic Server),而 Nginx 和 OpenResty 作为 Web 服务器领域的“多面手”,凭借着强大的反向代理能力和模块化、易于扩展的优点,也在 CDN 里占据了不少的份额。 + +小结 + +CDN 发展到现在已经有二十来年的历史了,早期的 CDN 功能比较简单,只能加速静态资源。随着这些年 Web 2.0、HTTPS、视频、直播等新技术、新业务的崛起,它也在不断进步,增加了很多的新功能,比如 SSL 加速、内容优化(数据压缩、图片格式转换、视频转码)、资源防盗链、WAF 安全防护等等。 + +现在,再说 CDN 是“搬运工”已经不太准确了,它更像是一个“无微不至”的“网站保姆”,让网站只安心生产优质的内容,其他的“杂事”都由它去代劳。 + + +由于客观地理距离的存在,直连网站访问速度会很慢,所以就出现了 CDN; +CDN 构建了全国、全球级别的专网,让用户就近访问专网里的边缘节点,降低了传输延迟,实现了网站加速; +GSLB 是 CDN 的“大脑”,使用 DNS 负载均衡技术,智能调度边缘节点提供服务; +缓存系统是 CDN 的“心脏”,使用 HTTP 缓存代理技术,缓存命中就返回给用户,否则就要回源。 + + +课下作业 + + +网站也可以自建同城、异地多处机房,构建集群来提高服务能力,为什么非要选择 CDN 呢? +对于无法缓存的动态资源,你觉得 CDN 也能有加速效果吗? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/38WebSocket:沙盒里的TCP.md b/专栏/透视HTTP协议/38WebSocket:沙盒里的TCP.md new file mode 100644 index 0000000..e2e21da --- /dev/null +++ b/专栏/透视HTTP协议/38WebSocket:沙盒里的TCP.md @@ -0,0 +1,161 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 38 WebSocket:沙盒里的TCP + 在之前讲 TCP/IP 协议栈的时候,我说过有“TCP Socket”,它实际上是一种功能接口,通过这些接口就可以使用 TCP/IP 协议栈在传输层收发数据。 + +那么,你知道还有一种东西叫“WebSocket”吗? + +单从名字上看,“Web”指的是 HTTP,“Socket”是套接字调用,那么这两个连起来又是什么意思呢? + +所谓“望文生义”,大概你也能猜出来,“WebSocket”就是运行在“Web”,也就是 HTTP 上的 Socket 通信规范,提供与“TCP Socket”类似的功能,使用它就可以像“TCP Socket”一样调用下层协议栈,任意地收发数据。 + + + +更准确地说,“WebSocket”是一种基于 TCP 的轻量级网络通信协议,在地位上是与 HTTP“平级”的。 + +为什么要有 WebSocket + +不过,已经有了被广泛应用的 HTTP 协议,为什么要再出一个 WebSocket 呢?它有哪些好处呢? + +其实 WebSocket 与 HTTP/2 一样,都是为了解决 HTTP 某方面的缺陷而诞生的。HTTP/2 针对的是“队头阻塞”,而 WebSocket 针对的是“请求 - 应答”通信模式。 + +那么,“请求 - 应答”有什么不好的地方呢? + +“请求 - 应答”是一种“半双工”的通信模式,虽然可以双向收发数据,但同一时刻只能一个方向上有动作,传输效率低。更关键的一点,它是一种“被动”通信模式,服务器只能“被动”响应客户端的请求,无法主动向客户端发送数据。 + +虽然后来的 HTTP/2、HTTP/3 新增了 Stream、Server Push 等特性,但“请求 - 应答”依然是主要的工作方式。这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏等要求“实时通信”的领域。 + +在 WebSocket 出现之前,在浏览器环境里用 JavaScript 开发实时 Web 应用很麻烦。因为浏览器是一个“受限的沙盒”,不能用 TCP,只有 HTTP 协议可用,所以就出现了很多“变通”的技术,“轮询”(polling)就是比较常用的的一种。 + +简单地说,轮询就是不停地向服务器发送 HTTP 请求,问有没有数据,有数据的话服务器就用响应报文回应。如果轮询的频率比较高,那么就可以近似地实现“实时通信”的效果。 + +但轮询的缺点也很明显,反复发送无效查询请求耗费了大量的带宽和 CPU 资源,非常不经济。 + +所以,为了克服 HTTP“请求 - 应答”模式的缺点,WebSocket 就“应运而生”了。它原来是 HTML5 的一部分,后来“自立门户”,形成了一个单独的标准,RFC 文档编号是 6455。 + +WebSocket 的特点 + +WebSocket 是一个真正“全双工”的通信协议,与 TCP 一样,客户端和服务器都可以随时向对方发送数据,而不用像 HTTP“你拍一,我拍一”那么“客套”。于是,服务器就可以变得更加“主动”了。一旦后台有新的数据,就可以立即“推送”给客户端,不需要客户端轮询,“实时通信”的效率也就提高了。 + +WebSocket 采用了二进制帧结构,语法、语义与 HTTP 完全不兼容,但因为它的主要运行环境是浏览器,为了便于推广和应用,就不得不“搭便车”,在使用习惯上尽量向 HTTP 靠拢,这就是它名字里“Web”的含义。 + +服务发现方面,WebSocket 没有使用 TCP 的“IP 地址 + 端口号”,而是延用了 HTTP 的 URI 格式,但开头的协议名不是“http”,引入的是两个新的名字:“ws”和“wss”,分别表示明文和加密的 WebSocket 协议。 + +WebSocket 的默认端口也选择了 80 和 443,因为现在互联网上的防火墙屏蔽了绝大多数的端口,只对 HTTP 的 80、443 端口“放行”,所以 WebSocket 就可以“伪装”成 HTTP 协议,比较容易地“穿透”防火墙,与服务器建立连接。具体是怎么“伪装”的,我稍后再讲。 + +下面我举几个 WebSocket 服务的例子,你看看,是不是和 HTTP 几乎一模一样: + +ws://www.chrono.com +ws://www.chrono.com:8080/srv +wss://www.chrono.com:445/im?user_id=xxx + + +要注意的一点是,WebSocket 的名字容易让人产生误解,虽然大多数情况下我们会在浏览器里调用 API 来使用 WebSocket,但它不是一个“调用接口的集合”,而是一个通信协议,所以我觉得把它理解成“TCP over Web”会更恰当一些。 + +WebSocket 的帧结构 + +刚才说了,WebSocket 用的也是二进制帧,有之前 HTTP/2、HTTP/3 的经验,相信你这次也能很快掌握 WebSocket 的报文结构。 + +不过 WebSocket 和 HTTP/2 的关注点不同,WebSocket 更侧重于“实时通信”,而 HTTP/2 更侧重于提高传输效率,所以两者的帧结构也有很大的区别。 + +WebSocket 虽然有“帧”,但却没有像 HTTP/2 那样定义“流”,也就不存在“多路复用”“优先级”等复杂的特性,而它自身就是“全双工”的,也就不需要“服务器推送”。所以综合起来,WebSocket 的帧学习起来会简单一些。 + +下图就是 WebSocket 的帧结构定义,长度不固定,最少 2 个字节,最多 14 字节,看着好像很复杂,实际非常简单。 + + + +开头的两个字节是必须的,也是最关键的。 + +第一个字节的第一位“FIN”是消息结束的标志位,相当于 HTTP/2 里的“END_STREAM”,表示数据发送完毕。一个消息可以拆成多个帧,接收方看到“FIN”后,就可以把前面的帧拼起来,组成完整的消息。 + +“FIN”后面的三个位是保留位,目前没有任何意义,但必须是 0。 + +第一个字节的后 4 位很重要,叫“Opcode”,操作码,其实就是帧类型,比如 1 表示帧内容是纯文本,2 表示帧内容是二进制数据,8 是关闭连接,9 和 10 分别是连接保活的 PING 和 PONG。 + +第二个字节第一位是掩码标志位“MASK”,表示帧内容是否使用异或操作(xor)做简单的加密。目前的 WebSocket 标准规定,客户端发送数据必须使用掩码,而服务器发送则必须不使用掩码。 + +第二个字节后 7 位是“Payload len”,表示帧内容的长度。它是另一种变长编码,最少 7 位,最多是 7+64 位,也就是额外增加 8 个字节,所以一个 WebSocket 帧最大是 2^64。 + +长度字段后面是“Masking-key”,掩码密钥,它是由上面的标志位“MASK”决定的,如果使用掩码就是 4 个字节的随机数,否则就不存在。 + +这么分析下来,其实 WebSocket 的帧头就四个部分:“结束标志位 + 操作码 + 帧长度 + 掩码”,只是使用了变长编码的“小花招”,不像 HTTP/2 定长报文头那么简单明了。 + +我们的实验环境利用 OpenResty 的“lua-resty-websocket”库,实现了一个简单的 WebSocket 通信,你可以访问 URI“/38-1”,它会连接后端的 WebSocket 服务“ws://127.0.0.1⁄38-0”,用 Wireshark 抓包就可以看到 WebSocket 的整个通信过程。 + +下面的截图是其中的一个文本帧,因为它是客户端发出的,所以需要掩码,报文头就在两个字节之外多了四个字节的“Masking-key”,总共是 6 个字节。 + + + +而报文内容经过掩码,不是直接可见的明文,但掩码的安全强度几乎是零,用“Masking-key”简单地异或一下就可以转换出明文。 + +WebSocket 的握手 + +和 TCP、TLS 一样,WebSocket 也要有一个握手过程,然后才能正式收发数据。 + +这里它还是搭上了 HTTP 的“便车”,利用了 HTTP 本身的“协议升级”特性,“伪装”成 HTTP,这样就能绕过浏览器沙盒、网络防火墙等等限制,这也是 WebSocket 与 HTTP 的另一个重要关联点。 + +WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字段: + + +“Connection: Upgrade”,表示要求协议“升级”; +“Upgrade: websocket”,表示要“升级”成 WebSocket 协议。 + + +另外,为了防止普通的 HTTP 消息被“意外”识别成 WebSocket,握手消息还增加了两个额外的认证用头字段(所谓的“挑战”,Challenge): + + +Sec-WebSocket-Key:一个 Base64 编码的 16 字节随机数,作为简单的认证密钥; +Sec-WebSocket-Version:协议的版本号,当前必须是 13。 + + + + +服务器收到 HTTP 请求报文,看到上面的四个字段,就知道这不是一个普通的 GET 请求,而是 WebSocket 的升级请求,于是就不走普通的 HTTP 处理流程,而是构造一个特殊的“101 Switching Protocols”响应报文,通知客户端,接下来就不用 HTTP 了,全改用 WebSocket 协议通信。(有点像 TLS 的“Change Cipher Spec”) + +WebSocket 的握手响应报文也是有特殊格式的,要用字段“Sec-WebSocket-Accept”验证客户端请求报文,同样也是为了防止误连接。 + +具体的做法是把请求头里“Sec-WebSocket-Key”的值,加上一个专用的 UUID “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,再计算 SHA-1 摘要。 + +encode_base64( + sha1( + Sec-WebSocket-Key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' )) + + +客户端收到响应报文,就可以用同样的算法,比对值是否相等,如果相等,就说明返回的报文确实是刚才握手时连接的服务器,认证成功。 + +握手完成,后续传输的数据就不再是 HTTP 报文,而是 WebSocket 格式的二进制帧了。 + + + +小结 + +浏览器是一个“沙盒”环境,有很多的限制,不允许建立 TCP 连接收发数据,而有了 WebSocket,我们就可以在浏览器里与服务器直接建立“TCP 连接”,获得更多的自由。 + +不过自由也是有代价的,WebSocket 虽然是在应用层,但使用方式却与“TCP Socket”差不多,过于“原始”,用户必须自己管理连接、缓存、状态,开发上比 HTTP 复杂的多,所以是否要在项目中引入 WebSocket 必须慎重考虑。 + + +HTTP 的“请求 - 应答”模式不适合开发“实时通信”应用,效率低,难以实现动态页面,所以出现了 WebSocket; +WebSocket 是一个“全双工”的通信协议,相当于对 TCP 做了一层“薄薄的包装”,让它运行在浏览器环境里; +WebSocket 使用兼容 HTTP 的 URI 来发现服务,但定义了新的协议名“ws”和“wss”,端口号也沿用了 80 和 443; +WebSocket 使用二进制帧,结构比较简单,特殊的地方是有个“掩码”操作,客户端发数据必须掩码,服务器则不用; +WebSocket 利用 HTTP 协议实现连接握手,发送 GET 请求要求“协议升级”,握手过程中有个非常简单的认证机制,目的是防止误连接。 + + +课下作业 + + +WebSocket 与 HTTP/2 有很多相似点,比如都可以从 HTTP/1 升级,都采用二进制帧结构,你能比较一下这两个协议吗? +试着自己解释一下 WebSocket 里的”Web“和”Socket“的含义。 +结合自己的实际工作,你觉得 WebSocket 适合用在哪些场景里? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/39HTTP性能优化面面观(上).md b/专栏/透视HTTP协议/39HTTP性能优化面面观(上).md new file mode 100644 index 0000000..6979cc3 --- /dev/null +++ b/专栏/透视HTTP协议/39HTTP性能优化面面观(上).md @@ -0,0 +1,136 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 39 HTTP性能优化面面观(上) + “透视 HTTP 协议”这个专栏已经陪伴了你近三个月的时间,在最后的这两讲里,我将把散落在前面各个章节的零散知识点整合起来,做一个总结,和你一起聊聊 HTTP 的性能优化。 + +由于 HTTPS(SSL/TLS)的优化已经在[第 28 讲]里介绍的比较详细了,所以这次就暂时略过不谈,你可以课后再找机会复习。 + +既然要做性能优化,那么,我们就需要知道:什么是性能?它都有哪些指标,又应该如何度量,进而采取哪些手段去优化? + +“性能”其实是一个复杂的概念。不同的人、不同的应用场景都会对它有不同的定义。对于 HTTP 来说,它又是一个非常复杂的系统,里面有非常多的角色,所以很难用一两个简单的词就能把性能描述清楚。 + +还是从 HTTP 最基本的“请求 - 应答”模型来着手吧。在这个模型里有两个角色:客户端和服务器,还有中间的传输链路,考查性能就可以看这三个部分。 + + + +HTTP 服务器 + +我们先来看看服务器,它一般运行在 Linux 操作系统上,用 Apache、Nginx 等 Web 服务器软件对外提供服务,所以,性能的含义就是它的服务能力,也就是尽可能多、尽可能快地处理用户的请求。 + +衡量服务器性能的主要指标有三个:吞吐量(requests per second)、并发数(concurrency)和响应时间(time per request)。 + +吞吐量就是我们常说的 RPS,每秒的请求次数,也有叫 TPS、QPS,它是服务器最基本的性能指标,RPS 越高就说明服务器的性能越好。 + +并发数反映的是服务器的负载能力,也就是服务器能够同时支持的客户端数量,当然也是越多越好,能够服务更多的用户。 + +响应时间反映的是服务器的处理能力,也就是快慢程度,响应时间越短,单位时间内服务器就能够给越多的用户提供服务,提高吞吐量和并发数。 + +除了上面的三个基本性能指标,服务器还要考虑 CPU、内存、硬盘和网卡等系统资源的占用程度,利用率过高或者过低都可能有问题。 + +在 HTTP 多年的发展过程中,已经出现了很多成熟的工具来测量这些服务器的性能指标,开源的、商业的、命令行的、图形化的都有。 + +在 Linux 上,最常用的性能测试工具可能就是 ab(Apache Bench)了,比如,下面的命令指定了并发数 100,总共发送 10000 个请求: + +ab -c 100 -n 10000 'http://www.xxx.com' + + +系统资源监控方面,Linux 自带的工具也非常多,常用的有 uptime、top、vmstat、netstat、sar 等等,可能你比我还要熟悉,我就列几个简单的例子吧: + +top # 查看 CPU 和内存占用情况 +vmstat 2 # 每 2 秒检查一次系统状态 +sar -n DEV 2 # 看所有网卡的流量,定时 2 秒检查 + + +理解了这些性能指标,我们就知道了服务器的性能优化方向:合理利用系统资源,提高服务器的吞吐量和并发数,降低响应时间。 + +HTTP 客户端 + +看完了服务器的性能指标,我们再来看看如何度量客户端的性能。 + +客户端是信息的消费者,一切数据都要通过网络从服务器获取,所以它最基本的性能指标就是“延迟”(latency)。 + +之前在讲 HTTP/2 的时候就简单介绍过延迟。所谓的“延迟”其实就是“等待”,等待数据到达客户端时所花费的时间。但因为 HTTP 的传输链路很复杂,所以延迟的原因也就多种多样。 + +首先,我们必须谨记有一个“不可逾越”的障碍——光速,因为地理距离而导致的延迟是无法克服的,访问数千公里外的网站显然会有更大的延迟。 + +其次,第二个因素是带宽,它又包括接入互联网时的电缆、WiFi、4G 和运营商内部网络、运营商之间网络的各种带宽,每一处都有可能成为数据传输的瓶颈,降低传输速度,增加延迟。 + +第三个因素是 DNS 查询,如果域名在本地没有缓存,就必须向 DNS 系统发起查询,引发一连串的网络通信成本,而在获取 IP 地址之前客户端只能等待,无法访问网站, + +第四个因素是 TCP 握手,你应该对它比较熟悉了吧,必须要经过 SYN、SYN/ACK、ACK 三个包之后才能建立连接,它带来的延迟由光速和带宽共同决定。 + +建立 TCP 连接之后,就是正常的数据收发了,后面还有解析 HTML、执行 JavaScript、排版渲染等等,这些也会耗费一些时间。不过它们已经不属于 HTTP 了,所以不在今天的讨论范围之内。 + +之前讲 HTTPS 时介绍过一个专门的网站“SSLLabs”,而对于 HTTP 性能优化,也有一个专门的测试网站“WebPageTest”。它的特点是在世界各地建立了很多的测试点,可以任意选择地理位置、机型、操作系统和浏览器发起测试,非常方便,用法也很简单。 + +网站测试的最终结果是一个直观的“瀑布图”(Waterfall Chart),清晰地列出了页面中所有资源加载的先后顺序和时间消耗,比如下图就是对 GitHub 首页的一次测试。 + + + +Chrome 等浏览器自带的开发者工具也可以很好地观察客户端延迟指标,面板左边有每个 URI 具体消耗的时间,面板的右边也是类似的瀑布图。 + +点击某个 URI,在 Timing 页里会显示出一个小型的“瀑布图”,是这个资源消耗时间的详细分解,延迟的原因都列的清清楚楚,比如下面的这张图: + + + +图里面的这些指标都是什么含义呢?我给你解释一下: + + +因为有“队头阻塞”,浏览器对每个域名最多开 6 个并发连接(HTTP/1.1),当页面里链接很多的时候就必须排队等待(Queued、Queueing),这里它就等待了 1.62 秒,然后才被浏览器正式处理; +浏览器要预先分配资源,调度连接,花费了 11.56 毫秒(Stalled); +连接前必须要解析域名,这里因为有本地缓存,所以只消耗了 0.41 毫秒(DNS Lookup); +与网站服务器建立连接的成本很高,总共花费了 270.87 毫秒,其中有 134.89 毫秒用于 TLS 握手,那么 TCP 握手的时间就是 135.98 毫秒(Initial connection、SSL); +实际发送数据非常快,只用了 0.11 毫秒(Request sent); +之后就是等待服务器的响应,专有名词叫 TTFB(Time To First Byte),也就是“首字节响应时间”,里面包括了服务器的处理时间和网络传输时间,花了 124.2 毫秒; +接收数据也是非常快的,用了 3.58 毫秒(Content Dowload)。 + + +从这张图你可以看到,一次 HTTP“请求 - 响应”的过程中延迟的时间是非常惊人的,总时间 415.04 毫秒里占了差不多 99%。 + +所以,客户端 HTTP 性能优化的关键就是:降低延迟。 + +HTTP 传输链路 + +以 HTTP 基本的“请求 - 应答”模型为出发点,刚才我们得到了 HTTP 性能优化的一些指标,现在,我们来把视角放大到“真实的世界”,看看客户端和服务器之间的传输链路,它也是影响 HTTP 性能的关键。 + +还记得[第 8 讲])里的互联网示意图吗?我把它略微改了一下,划分出了几个区域,这就是所谓的“第一公里”“中间一公里”和“最后一公里”(在英语原文中是 mile,英里)。 + + + +“第一公里”是指网站的出口,也就是服务器接入互联网的传输线路,它的带宽直接决定了网站对外的服务能力,也就是吞吐量等指标。显然,优化性能应该在这“第一公里”加大投入,尽量购买大带宽,接入更多的运营商网络。 + +“中间一公里”就是由许多小网络组成的实际的互联网,其实它远不止“一公里”,而是非常非常庞大和复杂的网络,地理距离、网络互通都严重影响了传输速度。好在这里面有一个 HTTP 的“好帮手”——CDN,它可以帮助网站跨越“千山万水”,让这段距离看起来真的就好像只有“一公里”。 + +“最后一公里”是用户访问互联网的入口,对于固网用户就是光纤、网线,对于移动用户就是 WiFi、基站。以前它是客户端性能的主要瓶颈,延迟大带宽小,但随着近几年 4G 和高速宽带的普及,“最后一公里”的情况已经好了很多,不再是制约性能的主要因素了。 + +除了这“三公里”,我个人认为还有一个“第零公里”, 就是网站内部的 Web 服务系统。它其实也是一个小型的网络(当然也可能会非常大),中间的数据处理、传输会导致延迟,增加服务器的响应时间,也是一个不可忽视的优化点。 + +在上面整个互联网传输链路中,末端的“最后一公里”我们是无法控制的,所以我们只能在“第零公里”“第一公里”和“中间一公里”这几个部分下功夫,增加带宽降低延迟,优化传输速度。 + +小结 + + +性能优化是一个复杂的概念,在 HTTP 里可以分解为服务器性能优化、客户端性能优化和传输链路优化; +服务器有三个主要的性能指标:吞吐量、并发数和响应时间,此外还需要考虑资源利用率; +客户端的基本性能指标是延迟,影响因素有地理距离、带宽、DNS 查询、TCP 握手等; +从服务器到客户端的传输链路可以分为三个部分,我们能够优化的是前两个部分,也就是“第一公里”和“中间一公里”; +有很多工具可以测量这些指标,服务器端有 ab、top、sar 等,客户端可以使用测试网站,浏览器的开发者工具。 + + +课下作业 + + +你有 HTTP 性能优化的经验吗?常用的有哪些方法? +你是怎么理解客户端的“延迟”的?应该怎样降低延迟? + + +欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。 + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/40HTTP性能优化面面观(下).md b/专栏/透视HTTP协议/40HTTP性能优化面面观(下).md new file mode 100644 index 0000000..c8bb6e0 --- /dev/null +++ b/专栏/透视HTTP协议/40HTTP性能优化面面观(下).md @@ -0,0 +1,129 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 40 HTTP性能优化面面观(下) + 今天我们继续上次的话题,看看 HTTP 性能优化有哪些行之有效的手段。 + +上一讲里我说到了,在整个 HTTP 系统里有三个可优化的环节,分别是服务器、客户端和传输链路(“第一公里”和“中间一公里”)。但因为我们是无法完全控制客户端的,所以实际上的优化工作通常是在服务器端。这里又可以细分为后端和前端,后端是指网站的后台服务,而前端就是 HTML、CSS、图片等展现在客户端的代码和数据。 + +知道了大致的方向,HTTP 性能优化具体应该怎么做呢? + +总的来说,任何计算机系统的优化都可以分成这么几类:硬件软件、内部外部、花钱不花钱。 + +投资购买现成的硬件最简单的优化方式,比如换上更强的 CPU、更快的网卡、更大的带宽、更多的服务器,效果也会“立竿见影”,直接提升网站的服务能力,也就实现了 HTTP 优化。 + +另外,花钱购买外部的软件或者服务也是一种行之有效的优化方式,最“物有所值”的应该算是 CDN 了(参见[第 37 讲])。CDN 专注于网络内容交付,帮助网站解决“中间一公里”的问题,还有很多其他非常专业的优化功能。把网站交给 CDN 运营,就好像是“让网站坐上了喷气飞机”,能够直达用户,几乎不需要费什么力气就能够达成很好的优化效果。 + +不过这些“花钱”的手段实在是太没有“技术含量”了,属于“懒人”(无贬义)的做法,所以我就不再细说,接下来重点就讲讲在网站内部、“不花钱”的软件优化。 + +我把这方面的 HTTP 性能优化概括为三个关键词:开源、节流、缓存。 + +开源 + +这个“开源”可不是 Open Source,而是指抓“源头”,开发网站服务器自身的潜力,在现有条件不变的情况下尽量挖掘出更多的服务能力。 + +首先,我们应该选用高性能的 Web 服务器,最佳选择当然就是 Nginx/OpenResty 了,尽量不要选择基于 Java、Python、Ruby 的其他服务器,它们用来做后面的业务逻辑服务器更好。利用 Nginx 强大的反向代理能力实现“动静分离”,动态页面交给 Tomcat、Django、Rails,图片、样式表等静态资源交给 Nginx。 + +Nginx 或者 OpenResty 自身也有很多配置参数可以用来进一步调优,举几个例子,比如说禁用负载均衡锁、增大连接池,绑定 CPU 等等,相关的资料有很多。 + +特别要说的是,对于 HTTP 协议一定要启用长连接。在[第 39 讲]里你也看到了,TCP 和 SSL 建立新连接的成本是非常高的,有可能会占到客户端总延迟的一半以上。长连接虽然不能优化连接握手,但可以把成本“均摊”到多次请求里,这样只有第一次请求会有延迟,之后的请求就不会有连接延迟,总体的延迟也就降低了。 + +另外,在现代操作系统上都已经支持 TCP 的新特性“TCP Fast Open”(Win10、iOS9、Linux 4.1),它的效果类似 TLS 的“False Start”,可以在初次握手的时候就传输数据,也就是 0-RTT,所以我们应该尽可能在操作系统和 Nginx 里开启这个特性,减少外网和内网里的握手延迟。 + +下面给出一个简短的 Nginx 配置示例,启用了长连接等优化参数,实现了动静分离: + +server { + listen 80 deferred reuseport backlog=4096 fastopen=1024; + + + keepalive_timeout 60; + keepalive_requests 10000; + + location ~* \.(png)$ { + root /var/images/png/; + } + + location ~* \.(php)$ { + proxy_pass http://php_back_end; + } +} + + +节流 + +“节流”是指减少客户端和服务器之间收发的数据量,在有限的带宽里传输更多的内容。 + +“节流”最基本的做法就是使用 HTTP 协议内置的“数据压缩”编码,不仅可以选择标准的 gzip,还可以积极尝试新的压缩算法 br,它有更好的压缩效果。 + +不过在数据压缩的时候应当注意选择适当的压缩率,不要追求最高压缩比,否则会耗费服务器的计算资源,增加响应时间,降低服务能力,反而会“得不偿失”。 + +gzip 和 br 是通用的压缩算法,对于 HTTP 协议传输的各种格式数据,我们还可以有针对性地采用特殊的压缩方式。 + +HTML/CSS/JS 属于纯文本,就可以采用特殊的“压缩”,去掉源码里多余的空格、换行、注释等元素。这样“压缩”之后的文本虽然看起来很混乱,对“人类”不友好,但计算机仍然能够毫无障碍地阅读,不影响浏览器上的运行效果。 + +图片在 HTTP 传输里占有非常高的比例,虽然它本身已经被压缩过了,不能被 gzip、br 处理,但仍然有优化的空间。比如说,去除图片里的拍摄时间、地点、机型等元数据,适当降低分辨率,缩小尺寸。图片的格式也很关键,尽量选择高压缩率的格式,有损格式应该用 JPEG,无损格式应该用 Webp 格式。 + +对于小文本或者小图片,还有一种叫做“资源合并”(Concatenation)的优化方式,就是把许多小资源合并成一个大资源,用一个请求全下载到客户端,然后客户端再用 JS、CSS 切分后使用,好处是节省了请求次数,但缺点是处理比较麻烦。 + +刚才说的几种数据压缩都是针对的 HTTP 报文里的 body,在 HTTP/1 里没有办法可以压缩 header,但我们也可以采取一些手段来减少 header 的大小,不必要的字段就尽量不发(例如 Server、X-Powered-By)。 + +网站经常会使用 Cookie 来记录用户的数据,浏览器访问网站时每次都会带上 Cookie,冗余度很高。所以应当少使用 Cookie,减少 Cookie 记录的数据量,总使用 domain 和 path 属性限定 Cookie 的作用域,尽可能减少 Cookie 的传输。如果客户端是现代浏览器,还可以使用 HTML5 里定义的 Web Local Storage,避免使用 Cookie。 + +压缩之外,“节流”还有两个优化点,就是域名和重定向。 + +DNS 解析域名会耗费不少的时间,如果网站拥有多个域名,那么域名解析获取 IP 地址就是一个不小的成本,所以应当适当“收缩”域名,限制在两三个左右,减少解析完整域名所需的时间,让客户端尽快从系统缓存里获取解析结果。 + +重定向引发的客户端延迟也很高,它不仅增加了一次请求往返,还有可能导致新域名的 DNS 解析,是 HTTP 前端性能优化的“大忌”。除非必要,应当尽量不使用重定向,或者使用 Web 服务器的“内部重定向”。 + +缓存 + +在[第 20 讲]里,我就说到了“缓存”,它不仅是 HTTP,也是任何计算机系统性能优化的“法宝”,把它和上面的“开源”“节流”搭配起来应用于传输链路,就能够让 HTTP 的性能再上一个台阶。 + +在“第零公里”,也就是网站系统内部,可以使用 Memcache、Redis、Varnish 等专门的缓存服务,把计算的中间结果和资源存储在内存或者硬盘里,Web 服务器首先检查缓存系统,如果有数据就立即返回给客户端,省去了访问后台服务的时间。 + +在“中间一公里”,缓存更是性能优化的重要手段,CDN 的网络加速功能就是建立在缓存的基础之上的,可以这么说,如果没有缓存,那就没有 CDN。 + +利用好缓存功能的关键是理解它的工作原理(参见[第 20 讲]和[第 22 讲]),为每个资源都添加 ETag 和 Last-modified 字段,再用 Cache-Control、Expires 设置好缓存控制属性。 + +其中最基本的是 max-age 有效期,标记资源可缓存的时间。对于图片、CSS 等静态资源可以设置较长的时间,比如一天或者一个月,对于动态资源,除非是实时性非常高,也可以设置一个较短的时间,比如 1 秒或者 5 秒。 + +这样一旦资源到达客户端,就会被缓存起来,在有效期内都不会再向服务器发送请求,也就是:“没有请求的请求,才是最快的请求。” + +HTTP/2 + +在“开源”“节流”和“缓存”这三大策略之外,HTTP 性能优化还有一个选择,那就是把协议由 HTTP/1 升级到 HTTP/2。 + +通过“飞翔篇”的学习,你已经知道了 HTTP/2 的很多优点,它消除了应用层的队头阻塞,拥有头部压缩、二进制帧、多路复用、流量控制、服务器推送等许多新特性,大幅度提升了 HTTP 的传输效率。 + +实际上这些特性也是在“开源”和“节流”这两点上做文章,但因为这些都已经内置在了协议内,所以只要换上 HTTP/2,网站就能够立刻获得显著的性能提升。 + +不过你要注意,一些在 HTTP/1 里的优化手段到了 HTTP/2 里会有“反效果”。 + +对于 HTTP/2 来说,一个域名使用一个 TCP 连接才能够获得最佳性能,如果开多个域名,就会浪费带宽和服务器资源,也会降低 HTTP/2 的效率,所以“域名收缩”在 HTTP/2 里是必须要做的。 + +“资源合并”在 HTTP/1 里减少了多次请求的成本,但在 HTTP/2 里因为有头部压缩和多路复用,传输小文件的成本很低,所以合并就失去了意义。而且“资源合并”还有一个缺点,就是降低了缓存的可用性,只要一个小文件更新,整个缓存就完全失效,必须重新下载。 + +所以在现在的大带宽和 CDN 应用场景下,应当尽量少用资源合并(JS、CSS 图片合并,数据内嵌),让资源的粒度尽可能地小,才能更好地发挥缓存的作用。 + +小结 + + +花钱购买硬件、软件或者服务可以直接提升网站的服务能力,其中最有价值的是 CDN; +不花钱也可以优化 HTTP,三个关键词是“开源”“节流”和“缓存”; +后端应该选用高性能的 Web 服务器,开启长连接,提升 TCP 的传输效率; +前端应该启用 gzip、br 压缩,减小文本、图片的体积,尽量少传不必要的头字段; +缓存是无论何时都不能忘记的性能优化利器,应该总使用 Etag 或 Last-modified 字段标记资源; +升级到 HTTP/2 能够直接获得许多方面的性能提升,但要留意一些 HTTP/1 的“反模式”。 + + +到这里,专栏的全部课程就学完了,在这三个月的时间里你是否有了很多的收获呢? + +接下来,就请在广阔的网络世界里去实践这些知识吧,祝你成功! + + + + + + \ No newline at end of file diff --git a/专栏/透视HTTP协议/结束语做兴趣使然的Hero.md b/专栏/透视HTTP协议/结束语做兴趣使然的Hero.md new file mode 100644 index 0000000..c810479 --- /dev/null +++ b/专栏/透视HTTP协议/结束语做兴趣使然的Hero.md @@ -0,0 +1,69 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 结束语 做兴趣使然的Hero + 从今年年初与极客时间编辑的初次接触开始,到这个月底专栏的正式结束,经过了差不多 7 个月的时间。这段历程有痛苦也有欢乐,有迷惘也有清朗,有困惑也有顿悟,有挫折也有奋进,各种感受五味杂陈,一言难尽。 + +无论如何,首先要感谢的,就是你——感谢你对我和这个专栏的支持,Many thanks to you。 + +写文章都讲究“首尾呼应”,所以在这篇“结束语”里,我就顺着“开篇词”,和你聊些轻松的话题,唠唠家常、说说心里话。 + +我是怎么写专栏的 + +咱们都是吃计算机这碗饭的,应该知道通信协议这个东西很不好学,更是很难讲,因为它真的是太“虚”了。不像编程语言、算法、数据结构、开发框架、操作系统那样,有实实在在的代码,协议只是一个文本规范,是一个动态的过程,而不是在计算机系统里真实存在的东西,你不能用 GDB 直接去调试,内存里也看不见。 + +所以,讲协议的书本、课程实在是少之又少。 + +落到 HTTP 协议,就如同我在专栏一开始时所说的,它“既简单又不简单”,而且历史悠久,涉及的范围很广,关联的技能点很多、很杂。当我接下写专栏的任务时,甚至有点“懵”的感觉,千头万绪不知从何谈起。 + +好在我一直有写学习笔记的习惯,最早是用“原始”的 word 文档,近几年改用云端笔记工具,随时记录、整理散乱的知识碎片。 + +既然暂时“无从下手”,那就先读文章、记笔记好了。 + +于是,我开始大量地粗读、精读现有资料,在阅读的过程中慢慢搜集思维中闪现的“火花”,即使是一两个零星的词汇也不放过。一个多月的辛苦整理过后,这才逐渐理清了脉络,有了模糊的写作思路,全程有点“垃圾堆里筛金子”的感觉。 + +虽然我有写书的经验,但写专栏则是完全不同的体验,在正式动笔写作的时候(严格来说应该是“敲键盘码字”),我才意识到,它与传统的技术类书籍有很大的不同。 + +书籍的阅读场景通常会比较安静、放松,读者会有比较长的思考时间,可以翻来覆去地看,再时不时拿起铅笔画个重点、做个记号,一段时间的阅读下来可以关注很多的知识点,然后再慢慢思索,总结串联。 + +而专栏的阅读场景则更可能是在地铁、公交车里,周围人挤人、人挨人,拿着手机,或看或听,还要时刻当心别坐过站。在这种情况下,读者很难有足够的思考时间和精力,更希望能够高效率、便捷地在短短几分钟的碎片时间里吸取知识,如果有太多的知识点就难以接受,一两个略有深度的点会更好。 + +所以,这次的专栏写作我就改换了风格,开始“口语化写作”,不再像写书那样斟词酌句,为一两句话的用词反复思量、咬文嚼字,而是完全“放飞自我”,定下每篇文章的主旨、要点后就笔随心动,把头脑里的思绪完全“dump”出来。 + +这样虽然在一定程度上降低了文字的信息密度,却会让文章形式更流畅、更易理解,做为补充,文章的末尾我再用小结的形式集中强化一下要点,实现了“浅入深出”。 + +现在看来效果似乎还算不错,不知道你以为如何呢? + +兴趣使然的 HERO + +虽然风格定了,但专栏写作过程中的困难程度还是我当初没有预计到的,写书是一个“慢功夫”,可以慢慢思考,有想法了就写一点,没有灵感可能十天半个月都动不了笔。 + +而写专栏却有“硬性”的时间限制,和编辑确定了写作大纲后就开始了“奴隶”一样的日子:每周固定要交两、三篇,每篇三四千字,相当于毎天要产出至少一千的有效文字,这简直成了“夺命连环 call”,同时还有构思、画图、编码、试验、核查等其他工作,压力非常大,真是一次“触及灵魂”之旅。 + +记得有一句名言:“兴趣是最好的老师”,支撑着我把这个专栏按时交付下去的最大动力,可能就是对学习计算机知识的兴趣和探索欲了。每当 get 到一个以前没有注意的知识点,每当成功领会了协议背后的设计意图,我的心底都会产生由衷的喜悦,前面钻研过程中的苦恼和烦躁也就瞬间“烟消云散”了。 + +所以,只要发自内心地对一件事情产生兴趣和喜爱,那么即使有再多的困难,也会想办法去克服、去解决。 + +说到这里,我联想到了《一拳超人》里的主角埼玉,他可以算得上是典型的“兴趣使然的 Hero”,纯粹是因为自己的“兴趣”而走上了“打怪升级”的道路,不图名不图利,不在意排名,也不在意奖励。单纯而快乐的生活,也许正是我们很多人想要追求的目标。 + +当然,除了兴趣,更重要的是恒心、毅力和坚持。埼玉之所以成为“无敌的存在”,就是因为他每天坚持做 100 个俯卧撑、100 个仰卧起坐、100 个下蹲,天天如此,从不间断。 + +我在这几个月的专栏写作过程中,遇到的困难和烦恼是以前写书的好几倍,经常是坐在电脑前,脑子里有很多乱麻一样想法,却无法“落地”转化成合适的词语,有时候会就这么干坐上一两个小时,焦灼的心情可想而知。 + +幸运的是最终我在“兴趣”这个原动力的支撑下坚持到了最后,另外还有了一个意外的收获。每天夜里码字没有思路的时候,我会走出家门,在小区里慢跑两三圈,呼吸新鲜空气顺便“放空”大脑。到专栏结束的这个时间点,居然减掉了差不多 8 斤的体重。 + +所以你看,“兴趣”给我带来的好处还真是不少呢。 + +相濡以沫,不如相忘于江湖 + +“透视 HTTP”这个专栏马上就要结束了,但 HTTP 协议的学习还远没有结束。 + +这有点像是调查兵团历经磨难和牺牲,终于看到了大海,但在海的另一头,还会更多更大的挑战等待着他们(看过《进击的巨人》的朋友一定能领会这种情景吧)。 + +在这篇“结束语”的留言区里,希望大家都能“冒个泡”,看看当时定下的“小目标”有没有达成,一起分享一下在这个专栏中的收获和心路历程,还有将来的打算。也欢迎你访问专栏的 GitHub 主页,提 issue 和 PR,把 HTTP 的学习、实践继续下去。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/00开篇词为什么大厂面试必考操作系统?.md b/专栏/重学操作系统-完/00开篇词为什么大厂面试必考操作系统?.md new file mode 100644 index 0000000..30318f1 --- /dev/null +++ b/专栏/重学操作系统-完/00开篇词为什么大厂面试必考操作系统?.md @@ -0,0 +1,144 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 开篇词 为什么大厂面试必考操作系统? + 你好,发现求知的乐趣,我是林䭽。 + +我在阿里巴巴做架构的多年时间里,每天都在和复杂的业务场景斗争着,比如如何应对高并发场景?如何解决系统间的数据一致性问题?如何带给用户更快更爽的体验? + +在处理一个又一个业务架构、系统架构问题的过程中,我愈发地意识到有一块知识非常重要,也就是我们这门课的主题——操作系统。操作系统可以作为一个完整的知识框架,把复杂如高并发、数据一致性的问题,基础到编程语言、计算框架、业务框架的问题都串联起来。 + +为什么要学习操作系统? + +操作系统(Operating System)作为一门计算机专业大学必修课,如今已经成为程序员跳槽、涨薪、过面试的必考内容。像面试中高频的考点,比如 Linux 指令、中断、多线程、并发、性能、内存管理、系统稳定性、文件系统、容器和虚拟化等,知识都来源于操作系统。学了操作系统: + + +你不懂 Java 多线程,也可以回答好 Java 多线程的面试题; +你不熟悉 Docker,也可以回答出容器化应该如何做。 + + + + +操作系统已不仅仅是一门大学的必修课那么简单,更是计算机领域的本源知识,任何编程语言学下去都会碰到操作系统知识,比如 Java 的虚拟机、Go 语言的协程与通道、Node.js 的 I/O 模型等。任何研发工具学下去也都会碰到操作系统,比如: + + +MySQL 深入学下去会碰到 InnoDB 文件系统; +HBase 深入学下去会有 Hadoop 文件系统(HDFS); +Redis 深入学下去会碰到 Linux 的I/O模型; +Docker 深入学下去有 Linux 的命名空间等; +甚至 Spring 框架,也需要用到线程池和调度算法。 + + +因此,作为面试官,我们需要通过操作系统知识判断求职者的综合能力,你可以将这些语言、开发工具用到什么层次?是能够使用还是理解原理,甚至具备系统改造的能力? + +为什么你要学习我这门操作系统课? + +互联网领域有一个非常重要的分析方法,那就是一件事情如果可以成功,无非就是合适的人,在合适的时间,做合适的事情。接下来,我将结合课程设计思路和自己的人生经历来分析, 你为什么要学习我的这门操作系统课。 + +我们可以先从“事到人”的角度入手。从事情上说,我希望做一门学了就能用的课程,所以本课程学习目标有两个:一是帮助你顺利通过面试、跳槽涨薪;另一个是帮助你提升应对实际工作场景的能力,具体包括以下几点。 + + +提升学习和理解能力:比如学习 Redis 可以理解到日志文件系统层面;学习 Java/Python/Node 等语言可以理解到语言最底层。 +提升应用架构能力:比如可以将操作系统的微内核架构迁移到自己设计的系统中。 +提升系统稳定性架构能力:比如在多线程设计上更出色,可以帮助同事找到设计漏洞。 +提升运维能力:做到可以方便地管理集群和分析日志。 + + +下面我再来结合我自身背景和互联网行业特点与你具体聊一聊。 + +首先,帮你面试涨薪这块我非常有底气,为什么我敢这样说? + +我曾在 3 家互联网大厂任职架构师(技术专家),而且是技术委员会成员;另一方面,我做了 10 年的面试官,每年都会到拉勾网筛选简历,保守估计按每年面试 100 个人来算,我至少已经面试过上千人了;还有一方面,我有很多朋友在大厂做技术 Leader,也有很多学生在大厂工作,因此我有一个精准的面试圈子。 + +我们在面试题、面试技巧方面交流非常多,而且我已经出过几门技术类的在线课程,我的讲课风格也受到了很多学员的认可,他们很喜欢和我交流问题、探讨技术,所以我可以自信地说:我是一个非常懂面试和公司用人标准的老师。 + +再者,我有丰富的实际应用场景经历。 + +中国互联网系统最主要的设计约束:并发高、数据量大(毕竟中国互联网是以人口红利起家的)。比较巧的是,海量用户的 C 端场景和大数据商业分析场景,我刚好都负责过。而高并发、大数据中的很多知识,又需要从操作系统中获取,加上我本身操作系统方面的知识也比较扎实,所以在实际场景这块我也有丰富的经验。 + +接下来再从我的角度来看看“现在要不要学操作系统”,我觉得现在的时机刚刚好。 + +首先,目前是一个在线教育的风口,我结合自身背景以及拉勾网在线招聘求职方向的优势,给你带来一门针对工作场景的就业提升类操作系统课程,符合平台调性。 + + + +再者,云原生架构出现之后,越来越强调“谁开发谁运维”,因此业内对操作系统的需求度在提升、要求也在提高,所以我设计这门针对工作场景的就业提升类操作系统课程符合市场需求。 + + +操作系统的需求量也是急剧增长。 +随着我国用户量和数据量的爆发式增长,也让互联网的运营成本变高。因此,公司很希望自己的员工熟悉操作系统,可以从系统底层去帮助公司节约成本。 + + +此外,随着我国用户量和数据量的爆发式增长,也让互联网的运营成本变高,一个普普通通千万级用户量的网站,就需要每年上亿的运维成本去 Cover。在这种情况下,公司很希望自己的员工熟悉操作系统,可以从系统底层去帮助公司节约成本。所以市场对于操作系统的需求量也是急剧增长的。 + +课程介绍 + +接下来我们聊聊课程内容,这门课程对标的是架构师层级的基础能力,看个人的接受程度,学完之后大概会在阿里的 P7 及以上层级。 + +课程共分为 8 个模块,合计 39 个课时。具体每个模块的介绍,我将在下一课时“课前必读”中详细讲解。在这里,我先跟你分享一下课程的整体设计思路。 + +这门操作系统课程将帮助你系统地解决面试中可能遇到的计算机原理和操作系统类问题,并以大厂面试题作为切入点,引出很多你在实际工作中会遇到的问题和技术难点。同时,每一个模块聚焦操作系统知识的一个方向,每节课的标题就是这个方向最需要掌握的,也是真实出现过的大厂面试题,同时它也代表着一类知识点。而且,我还将结合实战场景帮助你打牢基础知识,向架构师的方向努力。 + +说到工作场景,我认为有两个非常重要的问题需要解决。 + +第 1 个问题是提高大家在实际工作场景中的实战能力。 除了讲解操作系统的知识结构,还会结合以下 6 个场景深入分析: + + +架构师必备的高并发、多线程编程技巧; +团队 Leader 如何掌握必备的 Linux 运维技巧; +程序语言(Java/Go/Node)的内存分配和回收; +数据库底层(MySQL/Hadoop)的文件系统; +计算机网络和操作系统 I/O 模型结合有哪些注意事项; +如何从操作系统底层理看容器化技术:K8s 和 Docker。 + + +这些都是目前国内大厂研发岗位最为关心的问题,也是大厂面试的热门问题,学会和理解这些问题背后的原理,将对你面试和晋升有很大的帮助。 + +第 2 个问题是解决面试难题,让你顺利拿到 Offer。我精选了 80 道左右的大厂面试题: + + +其中 40 道题目会作为课程标题或者课前的提问,在课时中会带着大家分析,以此引出该问题背后的知识点,从点到面一步步把知识点和原理讲清楚。 +另外 40 道题目将作为课后练习题,帮助你巩固本课时的内容,检测自己的学习成果。此外,这部分问题还会作为精讲习题,在每个模块单独设置一个课时带你梳理解题思路、分析底层原理,结合实战场景帮你分析工作中可能遇到的瓶颈。 + + +学完之后,希望你可以举一反三,这非常有利于提升你的面试成功率。 + +其实我在做课程设计之前,花了大量的时间进行市场调研,发现目前市场上专门针对互联网方向,帮助你提升系统架构能力、应对日常使用和高并发场景的操作系统课程是缺失的。 + +这也是我做这门课程的初衷:课程中没有大量的源代码,也没有特别的语言依赖。我会把面试要点和工作场景中的重难点内容都覆盖到,彻彻底底、由内而外地给你讲清楚,帮你应对面试、解决工作难题。 + +说到这里,我想你可能还有些疑问,比如课程的难度怎么样,适不适合新手学习?或者是课程深度能否满足进阶大厂架构师? + + +第一个问题,适不适合新手学习? + + +我的答案是:适合。这门课程对大家的基础知识没做任何假设,比如操作系统唯一依赖的知识——计算机组成原理,我也把它们做成了前置课程,利用模块一的 4 个课时把这块内容讲清楚。 + +另一方面,我会从设计者角度出发来给你讲问题。比如讲多线程算法的时候,我会先讲遇到了什么困难,再诱导大家思考如何解决这些困难,然后和你一起分析出几个解题方向,最后循序渐进地给出一个合理的方案。所以这门课程非常适合新手学习。 + + +第二个问题,课程内容的深度能否满足进阶大厂架构师? + + +我的答案依然是:满足,这门课程的深度足够培养一名架构师。课程内容会涉及多线程编程中 AQS 的原理;数据同步中的乐观锁;Java 新生代用了什么算法、老生代用了什么算法;还有比如高并发时有哪些提升性能的方法?这些都是架构师需要掌握的,也是课程的重点。 + +所以,操作系统这门课程既适合新手入门,也适合有经验的开发人员进阶学习,这并不受经验影响,不同的是你们的学习目标和学习收获。 + +寄语 + +最后,我还想和你说点关于职业发展相关的事情。 + + + +中国有超过 1000 万程序员,大部分人的年薪小于 30 万。我观察到一个这样的现象:一方面求职者们抱怨市场竞争激烈,大家争抢一两个岗位;另一方面很多优秀团队的高薪岗位招人难,闲置多个空位,求职者很多但是符合岗位要求的却很少。到底是什么原因造成企业招人难,求职者求职难的情况呢? + +其实,拉开个人薪资和团队整体水平差异的分水岭,根本原因就是计算机基础知识的掌握程度。基础好的程序员,学习速度快,愿意花时间去积累知识,提高自身能力,因此涨薪快、跳槽更容易;而基础不好的,学习相对较慢,知识输入少,因此涨薪慢、跳槽难。个人能力的高低决定了收入的水平。 + +这个事情很现实,也很不公平。但是反过来想,为什么基础不好的同学,不把时间精力拿出来去填补自己的知识空缺呢?如果你的操作系统知识还是一盘散沙,那么请你现在就开始行动,跟着我一起重学操作系统,把这块知识捡起来。愿正在看这篇文章的你,能通过自己的努力去到更好的团队,拿更高的薪水,进而得到更广阔的发展空间。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/00课前必读构建知识体系,可以这样做!.md b/专栏/重学操作系统-完/00课前必读构建知识体系,可以这样做!.md new file mode 100644 index 0000000..613ed90 --- /dev/null +++ b/专栏/重学操作系统-完/00课前必读构建知识体系,可以这样做!.md @@ -0,0 +1,124 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 00 课前必读 构建知识体系,可以这样做! + 我认为,在学习中有一件非常重要的事情,那就是梳理知识体系,所以在进入操作系统课程的学习之前,我想先给你一份这门课程的知识体系(也是一份学习路径),然后再介绍一套我自己梳理知识体系的方法,帮助你更轻松地学好这门课。 + +课程内容&知识体系 + +我们先来看下这门课程的知识体系结构,分为 8 个模块,39 个课时,具体如下。 + + +模块一:(前置知识)计算机组成原理。 如果你对计算机的组成原理中涉及的比如内存、寄存器工作原理、CPU 指令、总线都是怎么工作的这些基本问题,没有搞清楚,大概率会影响你后续对操作系统的学习。因此,在课程开始前,我先来给你一份操作系统的前置知识,帮助你更好地理解后续内容。 +模块二:(初探)Linux 指令入门。 这个模块将介绍一些实用的知识,带你入门 Bash 编程,并通过日志分析、性能监控、集群管理等实战场景深入学习 Linux 指令。这些对于日常开发和运维人员来说,都会非常有帮助。 +模块三:(总纲)操作系统概述。 这部分帮助你了解操作系统的整体设计,介绍内核、用户空间等基本概念,还会介绍操作系统的分类,以及对比一下市面上的操作系统(如 Windows、Linux、Unix、Android 等),让你对整个操作系统生态能有一个整体的认识。 + + +总的来说,模块四 ~ 模块七是我们这门课程的核心内容,也是面试的重点考区。设置这块内容的目的是借助操作系统的知识,帮你思考如何解决实战问题,比如我们反复提及的高并发、数据一致性、大数据存储和网络问题等。 + + +模块四:(面试重点)进程和线程。 我会针对大家在面试和工作中最常见的并发和数据同步问题,从进程原理、多线程编程、互斥和乐观锁、死锁和饥饿、调度算法、进程通信等多个方面,同时结合一些语言特性(比如 Java 的语言特性)讲解原理、思考方案及对策。 +模块五:(面试重点)内存管理。 这部分我们是从页表和 MMU、虚拟化、内存的分配和回收、缓存置换、逃逸分析、三色算法、生代算法等方面入手,帮助你了解内存的工作原理,应对高并发带来的内存使用问题。 +模块六:(面试重点)文件系统。 这部分内容我们将从两个方面入手,一方面是通过学习 Linux 的文件目录结构,了解 Linux 下不同的文件目录的功能和作用,帮助你把 Linux 用好;另一个方面,从文件系统的底层设计入手,帮助你了解文件系统的设计思路和原理,并且通过讲解数据库的文件系统,比如 MySQL 的 InnoDb、B+Tree 以及 Hadoop 的 HDFS,帮你把文件系统的知识应用到处理海量数据的领域。 +模块七:(面试重点)网络与安全。 这部分讲解面试中常见的互联网协议群、TCP 和 UDP 协议、Linux 的 I/O 模型、公私钥加密体系,以及一些最基本的计算机网络安全知识,帮助你理解操作系统和网络之间的交互,从而更好地利用操作系统知识设计业务系统的网络架构。 +模块八:(知识拓展)虚拟化和其他。 最后这部分,我们将从操作系统的角度学习容器化应用(比如 Kubernetes 和 Docker),还会深入讨论 Linux 架构及商业操作系统。这些知识一方面能够帮你和面试官产生更多的共鸣,另一方面还能帮你开拓视野、打开思路,看到未来的发展趋势。 + + +接下来,我给大家梳理一下操作系统整体的知识框架,帮你扫除知识盲区。 + +从知识结构上来看,操作系统最核心的部分是进程,因为操作系统自己不能提供服务,它要想实现价值,就必须通过安装在系统中的应用程序。而安装好的应用程序,启动后就成了进程,所以说进程处在操作系统知识体系的核心。 + +了解了以上内容后,我们围绕进程继续梳理,可以发现: + + +进程往往要同时做很多事情,比如浏览器同时要处理网络、又要处理鼠标、还要展示内容,因此有了多线程的概念。 +进程需要执行用的存储空间,比如需要存程序指令、需要堆栈存执行数据,因此需要内存。 +进程需要将一部分数据持久的存储下来,因此需要文件系统。 +进程需要和外界通信,其中一种途径就是网络。 +开发过程中我们希望进程可以单独部署,于是需要容器。 +操作系统内核本身也是一个程序,可以理解成一个进程,它同样是需要单独研究的。 + + +所以,进程是核心,内核、多线程、内存、文件系统、网络、容器和虚拟是配套的能力。我们要想展开操作系统知识的学习,就要先从它的核心——进程入手,通过进程将操作系统的知识串联起来,然后逐一击破。 + +到这里,请你思考一个问题:进程本身是做什么的? + +给你一些提示,进程是程序的执行副本,操作系统用进程来分配资源。这里说的资源,就是 CPU 的计算资源、内存和磁盘的存储资源、I/O 设备的使用权等等。所以我们为了更好地学习操作系统,就需要知道计算机是怎么工作的,因此就需要一门前置课——计算机组成原理,我将在模块一把这部分内容给你讲解清楚。 + +最后,操作系统通常提供 3 类用户接口: + + +给程序员用的 API,比如用 C 语言去调用; +给运维人员和管理员用的 Shell; +给大众用户的图形界面。 + + +通常用 API 是为了定制操作系统的能力,如果你从事云计算、运维开发、嵌入式方向,那么一定会用到 API 。因为用户界面制作成本最高,能力也最少,所以大部分运维人员和管理人员都用 Shell。从这个角度分析,认真学习 Shell 有助于你快速掌握操作系统的基本功能,而且又不会太难。 + +以上就是操作系统课程的知识体系结构,你也可以通过目录,快速了解这个课程的内容设置。 + + + +我是如何梳理知识体系的? + +介绍完操作系统的知识体系,我还想借此再和你聊一聊我梳理知识体系的方法。 + +这是一套我运用多年的学习方法,它可以帮你节省时间成本,快速精准地查询到你需要的资料。这个学习方法有点像顺藤摸瓜,我自己称之为:“追溯源头、回归本质”。经过这样的思考,可以帮助你把知识学扎实,从而逐渐形成自己的知识体系。 + +我记得有一个技术大牛曾经说过,程序员最重要的是搜索知识的能力,我非常赞同这个说法,此外,我认为如果你想要长远发展,还应同时具备用结构化的思维去构建知识体系的能力。因为知识成体系后,会形成关联记忆和整体的理解,这种经过深度思考和梳理过的知识才能转化为自己的储备。 + +下面请你跟我一起进入到场景中,跟着我的思路把你的大脑运转起来。假设,在工作的过程中,我遇到了一块不懂的知识,其中有一个技术名词我不了解它的作用,比如 ReentrantLockLock,那么我该如何解决自己的问题呢? + + +注意: 你也可以把它替换成任意一个陌生的或者你不理解的技术名词。 + + +首先我会去查阅它的官方文档,然后发现了以下这些线索: + + +构造函数上有个参数在配置锁的公平性; +ReentrantLockLock 是可重入的; +功能类似 synchronized 关键字,但是更灵活; +支持 lock、unlock、tryLock 等方法; +底层是 AbstractQueuedSynchronizer。 + + +接着,根据我获得的知识,追溯 synchronized 关键字,发现 ReentrantLockLock 都说自己的底层是 AbstractQueuedSynchronizer(AQS),我感觉到 AQS 应该是一个重要的东西。 + +然后我会去查资料验证我的猜测。这时候,我又得到了一个新的信息:发现AQS是用来实现信号量、条件变量以及其他锁的一个编程框架。 + +假设我还不知道信号量、条件变量和锁是什么,于是我通过搜索资料,发现这些名词通通指向一门科学,也就是操作系统。 + +接下来,我会去挑选一门讲操作系统的在线课程或者买一本书来查阅,经过查阅发现这些名词出现在进程和多线程这个部分。然后我翻阅了这两个章节的内容,发现了更多我不知道的知识,比如死锁和饥饿、信号量、竞争条件和临界区、互斥的实现,以及最底层的 CPU 指令。 + +经过以上过程的推导,我开始在脑海中梳理这些知识点,然后动笔画出了一幅基于思考过程的思维导图,将这些知识点串联起来,如下图所示: + + + +注意,上图梳理出来的知识关系不一定对,但是你一定要敢于去画,这个梳理和探索的过程能够带动你主动思考,锻炼主动解决问题的能力。 + +输出思维导图后,我将开始学习上面那些超出我现阶段知识储备的内容,然后进行归类和整理。 + +这时候,我发现公平锁、可重入锁其实都是锁的一种实现,而 Java 中实现锁这个机制用的是 AQS,而 AQS 最基本的问题是要解决资源竞争的问题。 + +通过学习,我发现资源竞争的问题在操作系统里叫作竞争条件,解决方案是让临界区互斥。让临界区互斥可以用算法的实现,但是为了执行效率,更多的情况是利用 CPU 指令。Java 里用于实现互斥的原子操作 CAS,也是基于 CPU 指令的。 + +操作系统在解决了互斥问题的基础上,还提供了解决更复杂问题的数据结构,比如说信号量、竞争条件等;而程序语言也提供了数据结构,比如说可重入锁、公平锁。 + +经过一番探索,我终于弄明白了,原来实际应用场景中对锁有各种各样的需求,因此不仅仅需要信号量等数据结构,甚至还需要一个快速实现这种数据结构的框架,这个框架就是 AQS。我们可以用 AQS 实现 ReentrantLockLock 的功能。 + + + +通过上面的方法,我不仅仅可以把 ReentrantLockLockt 学透,而且顺藤摸瓜找到了所有关联的知识点,比如 AQS 和 CAS。比起理解最初的知识点,更重要的是我通过这种方法形成了自己的一个知识体系;而且,我会发现在这个知识体系中,操作系统是起到支撑作用的骨架。 + +与此同时,我还认识到了计算机语言和操作系统之间的联系非常紧密,操作系统知识是学习计算机语言的根基。于是我开始制定学习计划,投入时间学习操作系统。我更偏爱做一次性的时间投入,以防止日后碎片化学习做多次投入,陷入时间黑洞,而这个嗜好让我受益良多。 + +寄语 + +最后,希望今天的课程和学习方法可以帮助到你;也希望你平时遇到未知的问题,尝试使用我今天介绍的方法,学会构建自己的知识体系,提高自己的学商。你可以在留言区给自己立下 Flag,比如给自己定一个具体的目标,或者是打卡你的学习天数,总之希望你不要一看而过,留下你的思考。经过长期的积累,相信你会得到意想不到的收获。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/01计算机是什么:“如何把程序写好”这个问题是可计算的吗?.md b/专栏/重学操作系统-完/01计算机是什么:“如何把程序写好”这个问题是可计算的吗?.md new file mode 100644 index 0000000..c51b392 --- /dev/null +++ b/专栏/重学操作系统-完/01计算机是什么:“如何把程序写好”这个问题是可计算的吗?.md @@ -0,0 +1,147 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 01 计算机是什么:“如何把程序写好”这个问题是可计算的吗? + 我记得自己在面试中遇到过这样一个问题:“可不可以计算一个人程序写得好不好?” + +当时我也没有想明白“计算”这个词是什么意思。但事后分析来看,“计算”不就是写程序吗? + +其实简单理解这个问题就是“可不可以用机器来判断人的程序写得好不好?”如果从这个角度考虑,我是可以和面试官论述一番的。 + +后面我查阅了资料,历史上有一个对计算机领域影响颇深的可计算理论,面试官说的“计算”应该就来源于这里。其实继续深挖还能找出很多涉及计算机本源的有趣的知识,比如图灵机、冯诺依曼模型;再比如说 CPU 的构成、程序如何执行、缓存的分级、总线的作用等。 + +上面提到的这些内容其实都属于操作系统的前置课程,我会利用第一章 4 个课时和大家探讨一下计算机组成原理,然后我们再正式进入操作系统的学习。其实学习就是这样,追溯源头,回到本质,才能挖掘兴趣、激发思考,否则就变成了死记硬背。接下来我们将从计算能源的角度入手,来展开今天的课程学习。 + +芯片:计算能源 + +我们知道第一次工业革命出现了蒸汽机,能源是煤炭。第二次工业革命出现了发电机,能源是电。20 世纪四五十年代,又发生了第三次科技革命,革命产物是计算机。而第四次科技革命,就发生在当下,出现了人工智能,能源是数据。 + +说到这里,你可能会有个疑问:第三次科技革命的能源是什么呢? + +你的第一反应可能是电,但是细想又觉得不对。前两次工业革命都有带来能源变革,为什么第三次科技革命就没有了能源变革?其实,第三次科技革命的能源是一种数字能量,本质是计算。 + +下面我们来看一看这种数字能量是如何产生的。电能供给给芯片,芯片中的一种电子元件晶振(也就是石英晶体)通电后产生震荡,震荡会产生频率稳定的脉冲信号。通常这是一种高频的脉冲信号,每秒可达百万次。然后,我们通过谐振效应发放这个信号,形成方波。再通过电子元件调整这种脉冲的频率,把脉冲信号转换为我们需要的频率,这就形成了驱动芯片工作的时钟信号。这种信号的频率,我们也称作芯片的时钟频率。最后,时钟信号驱动着芯片工作,就像人体的脉搏一样,每一次脉冲到来,都让芯片的状态发生一次变化,用这种方法,最终存储器中的指令被一行行执行。指令被执行,其实就是数据被计算,这就是我说的计算能量。 + +芯片普及后,不仅给计算机和手机提供支持,它们还被安装到了航天设备、能源设备、医疗设备及通信设备中,甚至小到电灯、微波炉、咖啡机、热水器里面都有了芯片。有了芯片,设备通电后才可以计算,有了计算,这些设备才能够实现更加复杂而精确的功能。 + +摩尔定律:计算能力的发展 + +值得一提的是,历史上是先有计算机,后有的芯片。世界上第一个芯片,也被称作集成电路, 1958 年由美国德州仪器公司的工程师杰克·基尔比发明。而世界上第一台通用计算机 ENIAC 则是在 1946 年诞生于美国陆军弹道研究实验室。 + +看到这里你可能会有疑问,为什么是先发明计算机再发明芯片呢? + +其实,这个道理就好比很多程序员先实现产品功能,再考虑封装和复用。ENIAC 中负责计算的模块和后来的芯片原理是一样的,都是利用电路实现逻辑运算。只不过在 20 世纪 40 年代人们还没有将这种能力抽象成一个独立的产品,而且也没有办法解决电路体积的问题,ENIAC的体积看上去就像一所学校那么大。 + +芯片的计算能力来源于芯片内部的集成电路,集成电路大大减小了电路的体积,所有的元件都是用同一块半导体材料制作而成,也就是把所有的电路都集成到了一个单一的硅片上。为了提高计算性能,集成电路越来越复杂,里面的电子元件也越来越多。从最早拥有 100 个左右晶体管的小型集成电路,发展到 21 世纪初,拥有上亿电子元件的巨大规模集成电路。 + +芯片的发展,带来了计算能力的飞跃,ENIAC 只能每秒计算 5000 次加法和 400 次乘法,到 1978 年 8086 芯片已经可以每秒计算百万次了。而今天随便一个芯片不但可以轻轻松松每秒计算数亿次,而且不只有一个核心,是多个核心都能达到这一量级的计算能力。 + +在当时那个年代,Intel 的创始人之一摩尔就观察到了这个现象,并提出了摩尔定律:当价格不变时,集成电路中可容纳的晶体管数目约每隔 18~24 个月就会增加一倍,性能也将提升一倍。这一定律揭示了信息技术发展的速度,但到今天,摩尔定律失效了。因为随着芯片越来越小,在尺寸和散热等方面已经挑战了人类的极限,芯片中无法再放入更多的电子元件了。 + +但是计算能力又开始以另一种方式发展,比如一个普普通通的 NVIDA 显卡中就拥有了几百个核心,这样就可以进行大量的并发计算;另外,一个分布式的大数据集群,里面就可能有上千个核心。 + +展望未来,计算能力还有更多的增长点,不仅有可以无限提高计算能力的量子计算机,还有利用光学元件替代晶体元件的光电集成电路。 + +可计算理论:图灵机 + +当然,在科学家们尝试发明计算机和芯片之前,他们必须回答一个问题,那就是计算或者程序可以用来做什么?比如:计算可不可以用来做饭?换一个更专业的说法,做饭可不可以被计算? + +生活在数字时代的我们,用着导航、玩着游戏,本能地知道很多问题是可以被计算的,但是生活在 20 世纪初的科学家们,需要在没有计算机和芯片的时代就想清楚这些问题,并不是一件容易的事情。 + +公理化体系和不完备性定理 + +最早在 19 世纪初,德国著名数学家希尔伯特提出:这个世界可以建立一套完善的公理体系,由少数几个公理出发,推导出所有的定理和推论。这样就可以逐渐通过这种方法将世界上的万事万物都统一到一个体系中。 + +当然,这只是一个非常美好的设想,如果万事万物都可以用形式化(简单理解就是程序化规范化)的手段统一到一套体系中,也就意味着计算能力将被无限扩展,只要给定足够的时间和空间,计算机就可以完成任何工作。 + +但在不久后,美籍数学家哥德尔就提出了哥德尔不完备性定理,内容是:即便在完善的公理体系中仍然可以找到不能被证明也不能被证伪的命题。 + +这让我联想到,一说谎,鼻子就会变长的匹诺曹。如果他说“我说谎了”,那么他的鼻子应该变长还是变短呢?对于人类而言,这个问题可以理解,但是对于计算机来说这个问题是不可以被计算的。 + +正是因为世界上存在着大量的这种“公说公有理,婆说婆有理”的问题,才让大家认识到计算不能解决所有问题,所以:计算机能力也是有边界的。哥德尔的不完备性定理,让大家看到了世界上还有大量不可计算的问题。 + +图灵机和可计算理论 + +于是人们意识到了需要一个理论,专门回答这样的问题:哪些问题可以被计算,哪些不可以被计算,这就是可计算性理论,该理论是计算机科学的理论基础之一。 + +1936 年,被誉为人工智能之父的阿兰·图灵提出了图灵机,它是一种不断执行指令的抽象计算机。之所以说抽象,是因为图灵并没有真的造出这台机器,而是把它当成理论去和大家探讨可计算问题。 + +图灵发现如果一个问题是可计算的,那么它的解决方案就必须可以被具化成一条条的指令,也就是可以使用图灵机处理。因此,不能使用图灵机处理的问题,都是不可计算的问题。 + +比如一个马达的控制程序是可计算的,因为控制过程是可以被抽象成一条条指令的(即可以写程序实现)。比如程序可以先读入传感器的数据,然后根据数据计算出下面要进行加速还是减速。 + +不可计算问题 + +但当图灵机遇到“素数是不是有无穷多个?”这样的问题时,事情就变得复杂了。虽然,我们可以通过有限的步骤计算出下一个素数。比如可以每次尝试一个更大的数字,然后通过一系列计算过程判断该数字是不是素数,直到找到一个更大的素数。古希腊数学家埃拉托斯特尼就发明了筛选出给定范围内所有素数的方法。 + + + +如上图所示,我们利用埃拉托斯特尼筛法找到的素数越来越多。但是,我们还是不能回答“素数是不是有无穷多个”这样的问题。因为要回答这样的问题,我们会不停地寻找下一个素数。如果素数是无穷的,那么我们的计算就是无穷无尽的,所以这样的问题不可计算。 + +停机问题 + +我们也无法实现用一个通用程序去判断另一个程序是否会停止。比如你用运行这段程序来检查一个程序是否会停止时,你会发现不能因为这个程序执行了 1 天,就判定它不会停止,也不能因为这个程序执行了 10 年,从而得出它不会停止的结论。这个问题放到图灵机领域,叫作停机问题,我们无法给出一个判断图灵机是否会停机的通用方法,因此停机问题是一个经典的不可计算问题。 + +计算能力的边界在哪里? + +我们可以把世界上想解决的事情都称作问题,解决问题往往需要消耗芯片的计算能力,这通常称作时间开销,另外解决问题还需要消耗内存,称作空间开销。 + +问题的分类 + +世界上有一类问题,无论我们消耗多少时间和空间也无法解决,这类问题就包括“停机问题”,称作不可计算问题,我们无法用计算机精确地解决这类问题。世界上不可计算问题多,还是可计算问题多,也是一个不可计算问题,但直觉告诉我们一定是不可计算问题更多。 + +另外在可计算的问题中,有困难问题,也有简单问题,我们通常用复杂度来衡量,比如: + + +“求数组第 10 个元素”,计算这种问题,时间开销、空间开销都不会随着问题规模增长,我们记为 O(1); +“求数组中的最大值”,计算这种问题,时间开销会随着数组规模线性增大,记作 O(N),N 是问题的规模; +还有像“求一个n*n矩阵的和”,如果n是规模,那么时间开销会随着问题规模的平方增长,我们称作 O(N2); +当然也有更加复杂的数学模型,比如说O(N3)、O(N4)、O(N100)等。 + + +P 问题 vs NP 问题 + +按照摩尔定律所说,人类的计算能力每 18~24 个月翻一倍,我们的计算能力在呈指数形式上升。因此,在所有可以计算的问题中,像 O(N1000)的问题,虽然现在的计算能力不够,但是相信在遥远的未来,我们会拥有能力解决。这种我们有能力解决的问题,统称为多项式时间( Polynomial time)问题。我们今天能解决的问题,都是多项式时间的问题,下面记为 P 类型的问题。 + + + +另外,还有一类问题复杂度本身也是指数形式的问题,比如 O(2N)的问题。这类型的问题随着规模 N 上升,时间开销的增长速度和人类计算能力增长速度持平甚至更快。因此虽然这类问题可以计算,但是当 N 较大时,因为计算能力不足,最终结果依然无法被解决。 + +由此可见,不是所有可以计算的问题都可以被解决,问题如果不能在多项式时间内找到答案,我们记为 NP 问题。 + +有一部分 NP 问题可以被转化为 P 问题,比如斐波那契数列求第 N 项,可以用缓存、动态规划等方式转化为 O(N) 的问题。但还有更多的 NP 问题,比如一个集合,找出和为零的子集,就没能找到一个合适的转换方法。其实说这么多,就是想告诉大家:如今还有很多问题无法解决,它的数量远远大于我们可以解决的问题,科学家、工程师们也只能望洋兴叹了。 + +人工智能 + +此外,包括停机问题、包括 NP 问题在内的很多问题,虽然不能解决,但可以努力让计算机的解决方案超过人类的水平,这就是人工智能。 + +比如下围棋,围棋盘是 19*19 的,共有 361!种情况,如果遍历 361!种情况,并进行打分,共有 10 的 170 次方种可能,因此,我们的计算能力是远远不足的。但是如果使用人工智能方法对可能出现的部分情况进行概率判断,在不追求绝对精确的情况下,人工智能就可以超过人类选手。 + +AlphaGo 战胜李世石就是利用了基于概率的不完全解法,这种解法已经可以超过部分人类职业选手了,也就是说计算机的解法已经超过了人类。当然,人类的强项在于理解和分析,人有两种思维,归纳和假设,这两种思维都是计算机无法计算的。机器用概率理解围棋,局部来说机器下得更好,但是人可以制造机器,因此,人的感悟更有意义,谈不上孰优孰劣。 + +针对这种解决问题的方法,20 世纪中人工智能之父图灵,提出图灵测试,就是在一次人机对话中,随机抽样一部分的实验者和机器对话,如果这部分实验者有较大的百分比判断对面是人而不是机器,那这台机器就通过了图灵测试。在围棋领域,可以说,AI 通过了图灵测试。但围棋的 AI 不能下象棋,这也是 AI 的一个劣势。所以广义的 AI 还没有出现,现在出现的是在某个专业领域的 AI。 + +总结 + +下面我们进行总结。本课时是一个理解操作系统知识必不可少的计算机原理引导课。 + + +我们学习了芯片,芯片将电能转化为计算能量,计算能量推动程序执行; +接着提到了摩尔定律,了解到我们的计算能力仍在飞速发展; +还花了篇幅讲了图灵机,从而进一步认识了人工智能之父阿兰·图灵,图灵机具体的设计和构造,这将在02 课时程序的执行部分进一步讨论。 +最后普及了图灵测试和人工智能的基本概念,带你了解了计算机的能力边界。 + + +下面我们回到最初的问题:“可不可以计算一个人程序写得好不好?” + +这个问题可以这样来思考,如果把问题降级,变成:“可不可以计算一个人写的程序会不会停机?” + +这个问题就如同停机问题,无法计算,因此这是一个不可计算的问题。但是我们通过设立规则,比如检查缩进、检查函数的复用情况、检查类的命名情况,给写程序的人更好的建议。另外,我们也可以通过 AI 技术,让机器在“程序写得好不好”这个问题的判定能力上,达到人类的水平,通过图灵测试。 + +综上,从绝对的对错角度去看,这是一个不可计算问题,因为它没有办法被完全解决;但是从图灵测试层面来看,虽然目前无法解决这个问题,但是我们有理由相信,在未来,计算机对这个问题的解决方案,是可以超过人类的。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/02程序的执行:相比32位,64位的优势是什么?(上).md b/专栏/重学操作系统-完/02程序的执行:相比32位,64位的优势是什么?(上).md new file mode 100644 index 0000000..cd7076c --- /dev/null +++ b/专栏/重学操作系统-完/02程序的执行:相比32位,64位的优势是什么?(上).md @@ -0,0 +1,192 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 02 程序的执行:相比 32 位,64 位的优势是什么?(上) + 本节课给你讲学习操作系统之前的一个前置知识:程序是如何执行的? + +我们先来看一道常规的面试题:相比 32 位,64 位的优势是什么? + +面试官考察这种类型的问题,主要是想看求职者是否有扎实的计算机基础,同时想知道求职者在工作中是否充满好奇,会主动学习、寻根问底,毕竟 32、64 位是经常出现在程序员视野的词汇,常见的东西都弄明白了,那说明这个人学习能力强。 + +其实 ,面试官在这里给你挖了一个陷阱,因为他没有说清楚 32、64 位指的是操作系统、是软件、还是 CPU? + + +如果是软件,那么我们的数据库有 32 位和 64 位版本; +如果是操作系统,那么在阿里云上选择 Centos 和 Debian 版本的时候,也会有 32⁄64 版本; +如果是 CPU,那么有 32 位 CPU,也有 64 位 CPU。 + + +接下来请你带着问题开始今天的课程学习,本课时的重点是带你学懂程序执行的原理。 + +图灵机的构造 + +想要学懂程序执行的原理,就要从图灵机说起了。它在计算机科学方面有两个巨大的贡献: + +第一,它清楚地定义了计算机能力的边界,也就是可计算理论; + +第二,它定义了计算机由哪些部分组成,程序又是如何执行的。 + + + +我们先来看一看图灵机的内部构造: + + +图灵机拥有一条无限长的纸带,纸带上是一个格子挨着一个格子,格子中可以写字符,你可以把纸带看作内存,而这些字符可以看作是内存中的数据或者程序。 +图灵机有一个读写头,读写头可以读取任意格子上的字符,也可以改写任意格子的字符。 +读写头上面的盒子里是一些精密的零件,包括图灵机的存储、控制单元和运算单元。 + + +图灵机如何执行程序 + +下面我们来举一个例子,让大家弄清楚图灵机是如何工作的,比如我们要计算 11 + 15 的值,具体的运算步骤如下: + + +首先,我们将“11、15、+” 分别写入纸带上的 3 个格子(现在纸带上的字符串是11、15、 +),然后将读写头先停在 11 对应的格子上。 + + + + + +接下来,图灵机通过读写头读入 11 到它的存储设备中(这个存储设备也叫作图灵机的状态)。图灵机没有说读写头为什么可以识别纸带上的字符,而是假定读写头可以做到这点。 + + + + + +然后读写头向右移动一个格,用同样的方法将 15 读入图灵机的状态中。现在图灵机的状态中有两个连续的数字,11 和 15。 + + + + + +接下来重复上面的过程,会读到一个+号。下面我详细说一下这个运算流程: + + +读写头读到一个 + 号 ; +然后将 + 号传输给控制单元 ; +控制单元发现是一个 + 号,所以没有存入状态中。因为 + 号是一个我们预设的控制符(指令),它的作用是加和目前状态。因此,控制单元识别出是控制符,并通知运算单元工作; +运算单元从状态中读入 11、15 并进行计算,将结果 26 存储到状态; +运算单元将结果回传给控制单元; +控制单元将结果传输给读写头。 + + + + + + +读写头向右移动,将结果 26 写入纸带。 + + + + +这样,我们就通过图灵机计算出了 11+15 的值。不知道你有没有发现,图灵机构造的这一台机器,主要功能就是读写纸带然后计算;纸带中有数据、也有控制字符(也就是指令),这个设计和我们今天的计算机是一样的。 + +图灵通过数学证明了,一个问题如果可以拆解成图灵机的可执行步骤,那问题就是可计算的。另一方面,图灵机定义了计算机的组成以及工作原理,但是没有给出具体的实现。 + +冯诺依曼模型 + + + +具体的实现是 1945 年冯诺依曼和其他几位科学家在著名的 101 页报告中提出的。报告遵循了图灵机的设计,并提出用电子元件构造计算机,约定了用二进制进行计算和存储,并且将计算机结构分成以下 5 个部分: + + +输入设备; +输出设备; +内存; +中央处理器; +总线。 + + +这个模型也被称为冯诺依曼模型,下面我们具体来看看这 5 部分的作用。 + +内存 + +在冯诺依曼模型中,程序和数据被存储在一个被称作内存的线性排列存储区域。存储的数据单位是一个二进制位,英文是 bit。最小的存储单位叫作字节,也就是 8 位,英文是 byte,每一个字节都对应一个内存地址。内存地址由 0 开始编号,比如第 1 个地址是 0,第 2 个地址是 1, 然后自增排列,最后一个地址是内存中的字节数减 1。 + +我们通常说的内存都是随机存取器,也就是读取任何一个地址数据的速度是一样的,写入任何一个地址数据的速度也是一样的。 + +CPU + +冯诺依曼模型中 CPU 负责控制和计算。为了方便计算较大的数值,CPU 每次可以计算多个字节的数据。 + + +如果 CPU 每次可以计算 4 个 byte,那么我们称作 32 位 CPU; +如果 CPU 每次可以计算 8 个 byte,那么我们称作 64 位 CPU。 + + +这里的 32 和 64,称作 CPU 的位宽。 + +为什么 CPU 要这样设计呢? 因为一个 byte 最大的表示范围就是 0~255。比如要计算 20000*50,就超出了byte 最大的表示范围了。因此,CPU 需要支持多个 byte 一起计算。当然,CPU 位数越大,可以计算的数值就越大。但是在现实生活中不一定需要计算这么大的数值。比如说 32 位 CPU 能计算的最大整数是 4294967295,这已经非常大了。 + +控制单元和逻辑运算单元 + +CPU 中有一个控制单元专门负责控制 CPU 工作;还有逻辑运算单元专门负责计算。具体的工作原理我们在指令部分给大家分析。 + +寄存器 + +CPU 要进行计算,比如最简单的加和两个数字时,因为 CPU 离内存太远,所以需要一种离自己近的存储来存储将要被计算的数字。这种存储就是寄存器。寄存器就在 CPU 里,控制单元和逻辑运算单元非常近,因此速度很快。 + + +寄存器中有一部分是可供用户编程用的,比如用来存加和指令的两个参数,是通用寄存器。 +还有一部分寄存器有特殊的用途,叫作特殊寄存器。比如程序指针,就是一个特殊寄存器。它存储了 CPU 要执行的下一条指令所在的内存地址。注意,程序指针不是存储了下一条要执行的指令,此时指令还在内存中,程序指针只是存储了下一条指令的地址。 +下一条要执行的指令,会从内存读入到另一个特殊的寄存器中,这个寄存器叫作指令寄存器。指令被执行完成之前,指令都存储在这里。 + + +总线 + +CPU 和内存以及其他设备之间,也需要通信,因此我们用一种特殊的设备进行控制,就是总线。总线分成 3 种: + + +一种是地址总线,专门用来指定 CPU 将要操作的内存地址。 +还有一种是数据总线,用来读写内存中的数据。 + + +当 CPU 需要读写内存的时候,先要通过地址总线来指定内存地址,再通过数据总线来传输数据。 + + +最后一种总线叫作控制总线,用来发送和接收关键信号,比如后面我们会学到的中断信号,还有设备复位、就绪等信号,都是通过控制总线传输。同样的,CPU 需要对这些信号进行响应,这也需要控制总线。 + + +输入、输出设备 + +输入设备向计算机输入数据,计算机经过计算,将结果通过输出设备向外界传达。如果输入设备、输出设备想要和 CPU 进行交互,比如说用户按键需要 CPU 响应,这时候就需要用到控制总线。 + +到这里,相信你已经对冯诺依曼模型的构造有了一定的了解。这里我再强调几个问题: + +1. 线路位宽问题 + +第一个问题是,你可能会好奇数据如何通过线路传递。其实是通过操作电压,低电压是 0,高电压是 1。 + +如果只有一条线路,每次只能传递 1 个信号,因为你必须在 0,1 中选一个。比如你构造高高低低这样的信号,其实就是 1100,相当于你传了一个数字 10 过去。大家注意,这种传递是相当慢的,因为你需要传递 4 次。 + +这种一个 bit 一个 bit 发送的方式,我们叫作串行。如果希望每次多传一些数据,就需要增加线路,也就是需要并行。 + +如果只有 1 条地址总线,那每次只能表示 0-1 两种情况,所以只能操作 2 个内存地址;如果有 10 条地址总线,一次就可以表示 210 种情况,也就是可以操作 1024 个内存地址;如果你希望操作 4G 的内存,那么就需要 32 条线,因为 232 是 4G。 + +到这里,你可能会问,那我串行发送行不行?当然也不是不行,只是速度会很慢,因为每多增加一条线路速度就会翻倍。 + +2. 64 位和 32 位的计算 + +第二个问题是,CPU 的位宽会对计算造成什么影响? + +我们来看一个具体场景:要用 32 位宽的 CPU,加和两个 64 位的数字。 + +32 位宽的 CPU 控制 40 位宽的地址总线、数据总线工作会非常麻烦,需要双方制定协议。 因此通常 32 位宽 CPU 最多操作 32 位宽的地址总线和数据总线。 + +因此必须把两个 64 位数字拆成 2 个 32 位数字来计算,这样就需要一个算法,比如用像小时候做加法竖式一样,先加和两个低位的 32 位数字,算出进位,然后加和两个高位的 32 位数字,最后再加上进位。 + +而 64 位的 CPU 就可以一次读入 64 位的数字,同时 64 位的 CPU 内部的逻辑计算单元,也支持 64 位的数字进行计算。但是你千万不要仅仅因为位宽的区别,就认为 64 位 CPU 性能比 32 位高很多。 + +要知道大部分应用不需要计算超过 32 位的数字,比如你做一个电商网站,用户的金额通常是 10 万以下的,而 32 位有符号整数,最大可以到 20 亿。所以这样的计算在 32 位还是 64 位中没有什么区别。 + +还有一点要注意,32 位宽的 CPU 没办法控制超过 32 位的地址总线、数据总线工作。比如说你有一条 40 位的地址总线(其实就是 40 条线),32 位的 CPU 没有办法一次给 40 个信号,因为它最多只有 32 位的寄存器。因此 32 位宽的 CPU 最多操作 232 个内存地址,也就是 4G 内存地址。 + +总结 + +关于计算机组成和指令部分,我们就先学到这里。这节课我们通过图灵机和冯诺依曼模型学习了计算机的组成、CPU 的工作原理等。此外,我们还顺带讨论了 32 位和 64 位的区别,现在,你可以回答 64 位和 32 位比较有哪些优势了吗? + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/03程序的执行:相比32位,64位的优势是什么?(下).md b/专栏/重学操作系统-完/03程序的执行:相比32位,64位的优势是什么?(下).md new file mode 100644 index 0000000..f1b7229 --- /dev/null +++ b/专栏/重学操作系统-完/03程序的执行:相比32位,64位的优势是什么?(下).md @@ -0,0 +1,202 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 03 程序的执行:相比 32 位,64 位的优势是什么?(下) + 在 02 课时中我们学习了计算机的组成原理,还分析了一些你在工作中可能会遇到的问题。本课时,我们继续深入学习程序执行部分,进一步讨论程序在冯诺依曼模型上如何执行。 + +程序的执行过程 + +当 CPU 执行程序的时候: + +1.首先,CPU 读取 PC 指针指向的指令,将它导入指令寄存器。具体来说,完成读取指令这件事情有 3 个步骤: + +步骤 1:CPU 的控制单元操作地址总线指定需要访问的内存地址(简单理解,就是把 PC 指针中的值拷贝到地址总线中)。 + +步骤 2:CPU 通知内存设备准备数据(内存设备准备好了,就通过数据总线将数据传送给 CPU)。 + +步骤 3:CPU 收到内存传来的数据后,将这个数据存入指令寄存器。 + +完成以上 3 步,CPU 成功读取了 PC 指针指向指令,存入了指令寄存器。 + +2.然后,CPU 分析指令寄存器中的指令,确定指令的类型和参数。 +3.如果是计算类型的指令,那么就交给逻辑运算单元计算;如果是存储类型的指令,那么由控制单元执行。 +4.PC 指针自增,并准备获取下一条指令。 + + +比如在 32 位的机器上,指令是 32 位 4 个字节,需要 4 个内存地址存储,因此 PC 指针会自增 4。 + + + + +了解了程序的执行过程后,我还有一些问题想和大家一起讨论: + + +内存虽然是一个随机存取器,但是我们通常不会把指令和数据存在一起,这是为了安全起见。具体的原因我会在模块四进程部分展开讲解,欢迎大家在本课时的留言区讨论起来,我会结合你们留言的内容做后续的课程设计。 +程序指针也是一个寄存器,64 位的 CPU 会提供 64 位的寄存器,这样就可以使用更多内存地址。特别要说明的是,64 位的寄存器可以寻址的范围非常大,但是也会受到地址总线条数的限制。比如和 64 位 CPU 配套工作的地址总线只有 40 条,那么可以寻址的范围就只有 1T,也就是 240。 +从 PC 指针读取指令、到执行、再到下一条指令,构成了一个循环,这个不断循环的过程叫作CPU 的指令周期,下面我们会详细讲解这个概念。 + + +详解 a = 11 + 15 的执行过程 + +上面我们了解了基本的程序执行过程,接下来我们来看看如果用冯诺依曼模型执行a=11+15是一个怎样的过程。 + +我们再 Review 下这个问题:程序员写的程序a=11+15是字符串,CPU 不能执行字符串,只能执行指令。所以这里需要用到一种特殊的程序——编译器。编译器的核心能力是翻译,它把一种程序翻译成另一种程序语言。 + +这里,我们需要编译器将程序员写的程序翻译成 CPU 认识的指令(指令我们认为是一种低级语言,我们平时书写的是高级语言)。你可以先跟我完整地学完操作系统,再去深入了解编译原理的内容。 + +下面我们来详细阐述 a=11+15 的执行过程: + +1.编译器通过分析,发现 11 和 15 是数据,因此编译好的程序启动时,会在内存中开辟出一个专门的区域存这样的常数,这个专门用来存储常数的区域,就是数据段,如下图所示: + + +11 被存储到了地址 0x100; +15 被存储到了地址 0x104; + + + + +2.编译器将a=11+15转换成了 4 条指令,程序启动后,这些指令被导入了一个专门用来存储指令的区域,也就是正文段。如上图所示,这 4 条指令被存储到了 0x200-0x20c 的区域中: + +0x200 位置的 load 指令将地址 0x100 中的数据 11 导入寄存器 R0; + +0x204 位置的 load 指令将地址 0x104 中的数据 15 导入寄存器 R1; + +0x208 位置的 add 指令将寄存器 R0 和 R1 中的值相加,存入寄存器 R2; + +0x20c 位置的 store 指令将寄存器 R2 中的值存回数据区域中的 0x1108 位置。 + +3.具体执行的时候,PC 指针先指向 0x200 位置,然后依次执行这 4 条指令。 + +这里还有几个问题要说明一下: + + +变量 a 实际上是内存中的一个地址,a 是给程序员的助记符。 +为什么 0x200 中代表加载数据到寄存器的指令是 0x8c000100,我们会在下面详细讨论。 +不知道细心的同学是否发现,在上面的例子中,我们每次操作 4 个地址,也就是 32 位,这是因为我们在用 32 位宽的 CPU 举例。在 32 位宽的 CPU 中,指令也是 32 位的。但是数据可以小于 32 位,比如可以加和两个 8 位的字节。 +关于数据段和正文段的内容,会在模块四进程和线程部分继续讲解。 + + +指令 + +接下来我会带你具体分析指令的执行过程。 + +在上面的例子中,load 指令将内存中的数据导入寄存器,我们写成了 16 进制:0x8c000100,拆分成二进制就是: + + +这里大家还是看下图,需要看一下才能明白。 + + + + + +最左边的 6 位,叫作操作码,英文是 OpCode,100011 代表 load 指令; +中间的 4 位 0000是寄存器的编号,这里代表寄存器 R0; +后面的 22 位代表要读取的地址,也就是 0x100。 + + +所以我们是把操作码、寄存器的编号、要读取的地址合并到了一个 32 位的指令中。 + +我们再来看一条求加法运算的 add 指令,16 进制表示是 0x08048000,换算成二进制就是: + + + + +最左边的 6 位是指令编码,代表指令 add; +紧接着的 4 位 0000 代表寄存器 R0; +然后再接着的 4 位 0001 代表寄存器 R1; +再接着的 4 位 0010 代表寄存器 R2; +最后剩下的 14 位没有被使用。 + + +构造指令的过程,叫作指令的编码,通常由编译器完成;解析指令的过程,叫作指令的解码,由 CPU 完成。由此可见 CPU 内部有一个循环: + + +首先 CPU 通过 PC 指针读取对应内存地址的指令,我们将这个步骤叫作 Fetch,就是获取的意思。 +CPU 对指令进行解码,我们将这个部分叫作 Decode。 +CPU 执行指令,我们将这个部分叫作 Execution。 +CPU 将结果存回寄存器或者将寄存器存入内存,我们将这个步骤叫作 Store。 + + + + +上面 4 个步骤,我们叫作 CPU 的指令周期。CPU 的工作就是一个周期接着一个周期,周而复始。 + +指令的类型 + +通过上面的例子,你会发现不同类型(不同 OpCode)的指令、参数个数、每个参数的位宽,都不一样。而参数可以是以下这三种类型: + + +寄存器; +内存地址; +数值(一般是整数和浮点)。 + + +当然,无论是寄存器、内存地址还是数值,它们都是数字。 + +指令从功能角度来划分,大概有以下 5 类: + + +I/O 类型的指令,比如处理和内存间数据交换的指令 store/load 等;再比如将一个内存地址的数据转移到另一个内存地址的 mov 指令。 +计算类型的指令,最多只能处理两个寄存器,比如加减乘除、位运算、比较大小等。 +跳转类型的指令,用处就是修改 PC 指针。比如编程中大家经常会遇到需要条件判断+跳转的逻辑,比如 if-else,swtich-case、函数调用等。 +信号类型的指令,比如发送中断的指令 trap。 +闲置 CPU 的指令 nop,一般 CPU 都有这样一条指令,执行后 CPU 会空转一个周期。 + + +指令还有一个分法,就是寻址模式,比如同样是求和指令,可能会有 2 个版本: + + +将两个寄存器的值相加的 add 指令。 +将一个寄存器和一个整数相加的 addi 指令。 + + +另外,同样是加载内存中的数据到寄存器的 load 指令也有不同的寻址模式: + + +比如直接加载一个内存地址中的数据到寄存器的指令la,叫作直接寻址。 +直接将一个数值导入寄存器的指令li,叫作寄存器寻址。 +将一个寄存器中的数值作为地址,然后再去加载这个地址中数据的指令lw,叫作间接寻址。 + + +因此寻址模式是从指令如何获取数据的角度,对指令的一种分类,目的是给编写指令的人更多选择。 + +了解了指令的类型后,我再强调几个细节问题: + + +关于寻址模式和所有的指令,只要你不是嵌入式开发人员,就不需要记忆,理解即可。 +不同 CPU 的指令和寄存器名称都不一样,因此这些名称也不需要你记忆。 +有几个寄存器在所有 CPU 里名字都一样,比如 PC 指针、指令寄存器等。 + + +指令的执行速度 + +之前我们提到过 CPU 是用石英晶体产生的脉冲转化为时钟信号驱动的,每一次时钟信号高低电平的转换就是一个周期,我们称为时钟周期。CPU 的主频,说的就是时钟信号的频率。比如一个 1GHz 的 CPU,说的是时钟信号的频率是 1G。 + +到这里你可能会有疑问:是不是每个时钟周期都可以执行一条指令?其实,不是的,多数指令不能在一个时钟周期完成,通常需要 2 个、4 个、6 个时钟周期。 + +总结 + +接下来我们来做一个总结。这节课我们深入讨论了指令和指令的分类。接下来,我们来看一看在 02 课时中留下的问题:64 位和 32 位比较有哪些优势? + +还是老规矩,请你先自己思考这个问题的答案,写在留言区,然后再来看我接下来的分析。 + +【解析】 其实,这个问题需要分类讨论。 + + +如果说的是 64 位宽 CPU,那么有 2 个优势。 + + +优势 1:64 位 CPU 可以执行更大数字的运算,这个优势在普通应用上不明显,但是对于数值计算较多的应用就非常明显。 + +优势 2:64 位 CPU 可以寻址更大的内存空间 + + +如果 32 位/64 位说的是程序,那么说的是指令是 64 位还是 32 位的。32 位指令在 64 位机器上执行,困难不大,可以兼容。 如果是 64 位指令,在 32 位机器上执行就困难了。因为 32 位指令在 64 位机器执行的时候,需要的是一套兼容机制;但是 64 位指令在 32 位机器上执行,32 位的寄存器都存不下指令的参数。 +操作系统也是一种程序,如果是 64 位操作系统,也就是操作系统中程序的指令都是 64 位指令,因此不能安装在 32 位机器上。 + + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/04构造复杂的程序:将一个递归函数转成非递归函数的通用方法.md b/专栏/重学操作系统-完/04构造复杂的程序:将一个递归函数转成非递归函数的通用方法.md new file mode 100644 index 0000000..5108b2b --- /dev/null +++ b/专栏/重学操作系统-完/04构造复杂的程序:将一个递归函数转成非递归函数的通用方法.md @@ -0,0 +1,449 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 04 构造复杂的程序:将一个递归函数转成非递归函数的通用方法 + 我看到过一道非常不错的面试题:不支持递归的程序语言如何实现递归程序? + +之所以说这道题好,是因为: + + +首先,它不是纯粹考概念和死记硬背,求职者在回答问题之前需要进行一定的思考; +其次,这道题目可以继续深挖,比如可以让求职者具体写一个程序,就变成了一道编程题; +最后,这道题目有实战意义,它背后考察的是求职者的编程功底。 + + +为了弄清楚这道题目,你需要对程序有一个更深层次的认识,不仅仅停留在指令的执行层面,而是要灵活使用指令,去实现更加复杂的功能。 + +for 循环如何被执行 + +首先,我们来看 for 循环是如何实现的。 + +下面是一个求 1 加到 100 的 Java 程序,请你思考如何将它转换为指令: + +var i = 1, s = 0; + +for(; i <= 100; i++) { + + s+=i; + +} + + +指令是简单的,像积木一样,程序是复杂的,像房子一样。我们将简单的事情组合,然后去完成复杂的事情,这就是程序员每天在做的。在这个过程中,你会产生思考,比如如何排列组合,如何搭积木,才能更快更准地完成项目?所以这也是训练思维的一个过程。 + +经过思考,如果按照顺序执行上面的程序,则需要很多指令,因为 for 循环可以执行 1 次,也可以执行 100W 次,还可以执行无数次。因此,指令的设计者提供了一种 jump 类型的指令,让你可以在程序间跳跃,比如: + +loop: + + jump loop + + +这就实现了一个无限循环,程序执行到 jumploop 的时候,就会跳回 loop 标签。 + +用这种方法,我们可以将 for 循环用底层的指令实现: + +# var i = 1, s = 0 + +# 对应 Java 代码,我们首先将 1 和 0 存储到两个地址 + +# 这两个地址我们用 $i 和 $s 表示 + +store #1 -> $i // 将数字 1 存入i的地址 + +store #0 -> $s // 将数字 0 存入 s 的地址 + +# 接下来循环要开始了,我们在这里预留一个 loop 标签 + +# loop 是一个自定义标签,它代表指令的相对位置 + +# 后续我们可以用 jump 指令跳转回这个位置实现循环 + +loop: # 循环标签 + +# for ... i <= 100 + +# 接下来我们开始实现循环控制 + +# 我们先首先 i <= 100的比较 + +# 我们先将变量 i 的地址,也就是 $i 导入寄存器 R0 + +load $i -> R0 + +# 然后我们用 cmp 比较指令 R0 和数字 100 + +cmp R0 #100 // 比较 R0 和数字 100 + +# 注意指令不会有返回值,它会进行计算,然后改变机器的状态(也就是寄存器) + +# 比较后,有几个特殊的寄存器会保存比较结果 + +# 然后我们用 ja(jump above), 如果比较结果 R0 比 100 大 + +# 那么我们就跳转到 end 标签,实现循环的跳出 + +ja end + +nop + +# 如果 R0<=100,那么ja end 没有生效,这时我们处理 s+=i + +# 首先我们把变量 s 所在地址的数据导入寄存器 R1 + +load $s -> R1 + +# 然后我们把寄存器R0和R1加和,把结果存储寄存器 R2 + +add R0 R1 R2 + +# 这时,我们把寄存器 R2 的值存入变量 s 所在的地址 + +store R2 -> $s + +# 刚才我们完成了一次循环 + +# 我们还需要维护变量 i 的自增 + +# 现在 i 的值在 R0 中,我们首先将整数 1 叠加到 R0 上 + +add R0 #1 R0 + +# 再把 R0 的值存入i所在的内存地址 + +store R0 -> $i + +# 这时我们的循环体已经全部执行完成,我们需要调转回上面 loop 标签所在的位置 + +# 继续循环 + +jump loop + +nop + +end: + + +通过上面的方法,我们成功将 for 循环的程序转换成了指令,然后再将它们编码成二进制,就可以存储到内存中了。 + +讲到这里,我要强调几个事情: + + +jump 指令直接操作 PC 指针,但是很多 CPU 会抢先执行下一条指令,因此通常我们在 jump 后面要跟随一条 nop 指令,让 CPU 空转一个周期,避免 jump 下面的指令被执行。是不是到了微观世界,和你所认识的程序还不太一样? +上面我写指令的时候用到了 add/store 这些指令,它们叫作助记符,是帮助你记忆的。整体这段程序,我们就称作汇编程序。 +因为不同的机器助记符也不一样,所以你不用太关注我用的是什么汇编语言,也不用去记忆这些指令。当你拿到指定芯片的时候,直接去查阅芯片的说明书就可以了。 +虽然不同 CPU 的指令不一样,但也是有行业标准的。现在使用比较多的是 RISC(精简指令集)和 CISC(复杂指令集)。比如目前Inte 和 AMD 家族主要使用 CISC 指令集,ARM 和 MIPS 等主要使用RISC 指令集。 + + +条件控制程序 + +条件控制程序有两种典型代表,一种是 if-else ,另一种是 switch-case 。 总体来说, if-else 翻译成指令,是比较简单的,你需要用跳转指令和比较指令处理它的跳转逻辑。 + +当然,它们的使用场景不同,这块我不展开了。在这里我主要想跟你说说,它们的内部实现是不一样的。if-else 是一个自上向下的执行逻辑, switch-case是一种精确匹配算法。比如你有 1000 个 case,如果用 if-else 你需要一个个比较,最坏情况下需要比较 999 次;而如果用 switch-case ,就不需要一个个比较,通过算法就可以直接定位到对应的case 。 + +举个具体的例子,比如一个根据数字返回星期的程序。如果用if-else,那么你需要这样做: + +if(week == 1) { + + return "周一"; + +} else if(week == 2) { + + return "周二"; + +} + +…… + + +如果用 switch-case 的逻辑,你可能会这样计算: + +跳转位置=当前PC + 4*(week * 2 - 1) + + +你不用太关心上面的数学关系,我只是举一个例子告诉你, switch-case 实现更多是依赖数学关系,直接算出 case 所在指令的位置,而不是一行行执行和比较。 + +函数 + +了解了循环和条件判断,我们再来看看函数是如何被执行的。函数的执行过程必须深入到底层,也会涉及一种叫作栈的数据结构。 + +下面是一段 C 程序,传入两个参数,然后返回两个参数的和: + +int add(int a, int b){ + + return a + b; + +} + + +这里我先不说具体的解决方案,希望你可以先自己思考。其实到这里,你已经学了不少知识了。下面我们一起分析一下,一种思考的方向是: + + +通过观察,我们发现函数的参数 a,b 本质是内存中的数据,因此需要给它们分配内存地址。 +函数返回值也是内存中的数据,也就是返回值也需要分配内存地址。 +调用函数其实就是跳转到函数体对应的指令所在的位置,因此函数名可以用一个标签,调用时,就用 jump 指令跟这个标签。 + + +比如上面函数进行了a+b的运算,我们可以这样构造指令: + +# 首先我们定义一个叫作add的标签 + +add: + +# 然后我们将a和b所在地址中的数据都导入寄存器 + +load $a -> R0 + +load $b -> R1 + +# 然后我们将寄存器求和,并将结果回写到返回地址 + +add R0 R1 R2 + +store R2 -> $r + + +当我们需要调用这个函数的时候,我们就构造下面这样的指令: + +jump add + + +细心的同学可能已经发现,这里有 2 个问题还没有解决: + + +参数如何传递给函数? +返回值如何传递给调用者? + + +为了解决这 2 个问题,我们就需要用到前面提到的一个叫作栈的数据结构。栈的英文是 Stack,意思是码放整齐的一堆东西。首先在调用方,我们将参数传递给栈;然后在函数执行过程中,我们从栈中取出参数。 + + + +函数执行过程中,先将执行结果写入栈中,然后在返回前把之前压入的参数出栈,调用方再从栈中取出执行结果。 + + + +将参数传递给 Stack 的过程,叫作压栈。取出结果的过程,叫作出栈。栈就好像你书桌上的一摞书,压栈就是把参数放到书上面,出栈就是把顶部的书拿下来。 + +因为栈中的每个数据大小都一样,所以在函数执行的过程中,我们可以通过参数的个数和参数的序号去计算参数在栈中的位置。 + +接下来我们来看看函数执行的整体过程:假设要计算 11 和 15 的和,我们首先在内存中开辟一块单独的空间,也就是栈。 + + + +就如前面所讲,栈的使用方法是不断往上堆数据,所以需要一个栈指针(Stack Pointer, SP)指向栈顶(也就是下一个可以写入的位置)。每次将数据写入栈时,就把数据写到栈指针指向的位置,然后将 SP 的值增加。 + +为了提高效率,我们通常会用一个特殊的寄存器来存储栈指针,这个寄存器就叫作 Stack Pointer,在大多数芯片中都有这个特殊的寄存器。一开始,SP 指向 0x100 位置,而 0x100 位置还没有数据。 + + +压栈参数11 + + +接下来我们开始传参,我们先将 11 压栈,之所以称作压栈( Push),就好像我们把数据 11 堆在内存中一样。模拟压栈的过程是下面两条指令: + +store #11 -> $SP // 将11存入SP指向的地址0x100 + +add SP, 4, SP // 栈指针增加4(32位机器) + + +第一条 store 指令将 SP 寄存器指向的内存地址设置为常数 11。 + +第二条指令将栈指针自增 4。 + +这里用美元符号代表将 11 存入的是 SP 寄存器指向的内存地址,这是一次间接寻址。存入后,栈指针不是自增 1 而是自增了 4,因为我在这里给你讲解时,用的是一个 32 位宽的 CPU 。如果是 64 位宽的 CPU,那么栈指针就需要自增 8。 + +压栈完成后,内存变成下图中所示的样子。11 被写入内存,并且栈指针指向了 0x104 位置。 + + + + +压栈参数15 + + +然后我们用同样的方法将参数 15 压栈。 + + + +压栈后,11 和 15 都被放入了对应的内存位置,并且栈指针指向了 0x108。 + + +将返回值压栈 + + +接下来,我们将返回值压栈。到这里你可能会问,返回值还没有计算呢,怎么就压栈了?其实这相当于一个占位,后面我们会改写这个地址。 + + + + +调用函数 + + +当我们完成了上面的压栈后,就开始调用函数,一种简单的做法是用 jump 指令直接跳转到函数的标签,比如: + +jump add + + +这个时候,要加和在栈中的数据 11 和 15,我们可以利用 SP 指针寻找数据。11 距离当前 SP 指针差 3 个位置,15 距离 SP 指针差 2 个位置。这种寻址方式是一种复合的寻址方式,是间接 + 偏移量寻址。 + +我们可以用下面的代码完成将 11 和 15 导入寄存器的过程: + +load $(SP - 12) -> R0 + +load $(SP - 8) -> R1 + + +然后进行加和,将结果存入 R2。 + +load R0 R1 R2 + + +最后我们可以再次利用数学关系将结果写入返回值所在的位置。 + +store R2 -> $(SP-4) + + +上面我们用到了一种间接寻址的方式来进行加和运算,也就是利用 SP 中的地址做加减法操作内存。 + +经过函数调用的结果如下图所示,运算结果 26 已经被写入了返回值的位置: + + + + +发现-解决问题 + + +一个好的解决方案,也会面临问题。现在我们就遇到了麻烦: + + +函数计算完成,这时应该跳转回去。可是我们没有记录函数调用前 PC 指针的位置,因此这里需要改进,我们需要存储函数调用前的 PC 指针方便调用后恢复。 +栈不可以被无限使用,11和 15 作为参数,计算出了结果 26,那么它们就可以清空了。如果用调整栈指针的方式去清空,我们就会先清空 26。此时就会出现顺序问题,因此我们需要调整压栈的顺序。 + + +具体顺序你可以看下图。首先,我们将函数参数和返回值换位,这样在清空数据的时候,就会先清空参数,再清空返回值。 + + + +然后我们在调用函数前,还需要将返回地址压栈。这样在函数计算完成前,就能跳转回对应的返回地址。翻译成指令,就是下面这样: + +## 压栈返回值 + +add SP, 4 -> SP + +# 计算返回地址 + +# 我们需要跳转到清理堆栈那行,也就是16行 + +MOV PC+4*(参数个数*2+1) -> SP + +# 压栈参数的程序 + +…… + +# 执行函数,计算返回值 + +call function + +# 清理堆栈 + +add SP, -(参数个数+1)*4, SP + + +递归函数如何被执行 + +我们刚刚使用了栈解决了函数的调用问题。但是这个方案究竟合不合理,还需要用更复杂的情况来验证。 + +如下所示,我们给出一个递归函数,请你判断是否可以用上面的方法执行: + +int sum(int n){ + + if(n == 1) {return 1;} + + return n + sum(n-1); + +} + + +递归的时候,我们每次执行函数都形成一个如下所示的栈结构: + + + +比如执行 sum(100),我们就会形成一个复杂的栈,第一次调用 n = 100,第二次递归调用 n = 99: + + + +它们堆在了一起,就形成了一个很大的栈,简化一下就是这样的一个模型,如下所示: + + + +到这里,递归消耗了更多空间,但是也保证了中间计算的独立性。当递归执行到 100 次的时候,就会执行下面的语句: + + if(n == 1) {return 1;} + + +于是触发第 99 次递归执行: + +return 2 + sum(1) // sum(1) = 1 + + +上面程序等价于return 3,接着再触发第 98 次递归的执行,然后是第 97 次,最终触发到第一次函数调用返回结果。 + +由此可见,栈这种结构同样适合递归的计算。事实上,计算机编程语言就是用这种结构来实现递归函数。 + +类型(class)如何实现 + +按照我们之前已经学习到的知识: + + +变量是一个内存地址,所以只需要分配内存就好了; +循环控制可以用跳转加判断实现; +条件控制也可以用跳转加判断实现,只不过如果是 switch-case 还需要一定的数学计算; +函数调用需要压栈参数、返回值和返回地址。 + + +最后,我们来说说类型是如何实现的,也就是很多语言都支持的 class 如何被翻译成指令。其实 class 实现非常简单,首先一个 class 会分成两个部分,一部分是数据(也称作属性),另一部分是函数(也称作方法)。 + + + +class 有一个特殊的方法叫作构造函数,它会为 class 分配内存。构造函数执行的时候,开始扫描类型定义中所有的属性和方法。 + + +如果遇到属性,就为属性分配内存地址; +如果遇到方法,方法本身需要存到正文段(也就是程序所在的内存区域),再将方法的值设置为方法指令所在的内存地址。 + + +当我们调用一个 class 方法的时候,本质上是执行了一个函数,因此和函数调用是一致的: + + +首先把返回值和返回地址压栈; +然后压栈参数; +最后执行跳转。 + + +这里有一个小问题,有时候 class 的方法会用到this ,这其实并不复杂,你仔细想想, this指针不就是构造函数创建的一个指向 class 实例的地址吗?那么,有一种简单的实现,就是我们可以把 this 作为函数的第一个参数压栈。这样,类型的函数就可以访问类型的成员了,而类型也就可以翻译成指令了。 + +总结 + +下面我们做一个简单的总结: + + +我们写的程序需要翻译成指令才能被执行,在 03 课时中我们提到过,这个翻译工具叫作编译器。 +平时你编程做的事情,用机器指令也能做,所以从计算能力上来说它们是等价的,最终这种计算能力又和图灵机是等价的。如果一个语言的能力和图灵机等价,我们就说这个语言是图灵完备的语言。现在市面上的绝大多数语言都是图灵完备的语言,但也有一些不是,比如 HTML、正则表达式和 SQL 等。 +我们通过汇编语言构造高级程序;通过高级程序构造自己的业务逻辑,这些都是工程能力的一种体现。 + + +那么通过这节课的学习,你现在可以来回答本节关联的面试题目:一个程序语言如果不支持递归函数的话,该如何实现递归算法? + +老规矩,请你先在脑海里思考问题的答案,并把你的思考写在留言区,然后再来看我接下来的分析。 + +【解析】 思路如下: + + +我们需要用到一个栈(其实用数组就可以); +我们还需要一个栈指针,支持寄存器的编程语言能够直接用寄存器,而不支持直接用寄存器的编程语言,比如 Java,我们可以用一个变量; +然后我们可以实现压栈、出栈的操作,并按照上面学习的函数调用方法操作我们的栈。 + + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/05(1)加餐练习题详解(一).md b/专栏/重学操作系统-完/05(1)加餐练习题详解(一).md new file mode 100644 index 0000000..c66a5f7 --- /dev/null +++ b/专栏/重学操作系统-完/05(1)加餐练习题详解(一).md @@ -0,0 +1,182 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 (1) 加餐 练习题详解(一) + 今天我会带你把《模块一:计算机组成原理》中涉及的课后练习题,逐一讲解,并给出每个课时练习题的解题思路和答案。 + +练习题详解 + +01 | 计算机是什么:“如何把程序写好”这个问题是可计算的吗? + +【问题】 可不可以构造一段程序证明停机问题无解?如果可以,请用自己熟悉的语言写出这段程序。 + +【解析】拿到这道题,我们可以先从问题的抽象入手。 + + +判断一段程序是否会停机的方法可以抽象成一个函数。 +一段程序,也可以抽象成一个函数。 + + +因此,问题可以转换为:存不存在一个通用函数判断另一个函数是否会停止? + +接下来,再来构造冲突。 + +假设存在一个函数 willStop,它只有一个参数 func,willStop 可以判断任意函数 func 是否会停止: + + +如果会停止,返回 true; +如果不会停止返回 false。 + + +willStop 具体如何实现我们无法给出,这里只是做一个假设。 + +func willStop(func){ + + //... + +} + + +下面我们构造一组冲突,构造一个叫作wrappedWillStop函数,它调用willStop构造冲突。 + +function wrappedWillStop(){ + + if( willStop(wrappedWillStop) ) { + + while(true){} + + } else { + + return + + } + +} + +wrappedWillStop() + + +wrapped版本构造冲突方法如下:调用willStop并把自己传进去。如果willStop认为wrapped会停止,那么就执行一个死循环。 如果willStop认为wrapped不会停止,就直接返回。 + +通过上述的方法,我们就知道willStop这样的函数肯定是无法被实现的;也就是停机问题无解。 + +03 | 程序的执行:相比 32 位 64 位的优势是什么? + +【问题】 CPU 中有没有求对数的指令?如果没有那么程序如何去计算? + +【解析】 CPU 中求一个数字的 2 倍,可以通过左移指令。比如 10 代表数字 2,左移 1 位变成 100 就代表数字 4。CPU 提供了乘法指令,所以如果求一个数字的幂,比如 33,可以拿 3*3 再乘以 3,需要计算 2 次。 + +但是如果求 3100 次方,就不会去计算 100 次。比如你可以先计算出 325,然后再求 (350)2,就是 3100。所以这样就节省了 1 倍的运算。 + +我举例主要是想告诉大家,CPU 没有提供很复杂的指令,但是这里有很多算法可以降低我们的时间开销。 + +然后我们来说说求对数,求对数也是没有指令的。因为对数是指数的逆运算,当然我们可以利用乘法运算一点点尝试。比如计算 log_210,我们可以先尝试 32,再尝试 3.12 等等,一直找到以 2 为底 10 的对数。这其实是个近似算法。 + +另外,在这个问题上聪明的数学家提出了很多近似算法,提升了计算效率。具体这里比较超纲,面试通常只考到有没有求对数的指令,感兴趣的同学可以学习泰勒级数、牛顿迭代法等。 + +比如下面这个泰勒级数可以用来求以e为底的对数,可以进行相似运算。 + + + +【补充内容】1 位的 CPU 能操作多大的内存空间? + +在 03 课时程序的执行中,有个问题我讲的不是很明白,在这里我们再讨论一下。 + +之前提到过 32 位机器只能操作小于 32 位的地址总线,这里其实讲的不太清晰,历史上出现过 32 位操作 40 位地址总线的情况。 + +接下来再和你探讨一个极端情况,1 位的 CPU 能操作多大的内存空间。 + +答案是:无限大。 + +比如说,地址总线 40 位,说明 CPU 上有 40 个引脚接了地址总线。CPU 只有 1 位,因此操作这 40 个引脚可以分成 40 步。每次设置 1 根引脚的电平是 0 还是 1。所以本身 CPU 多少位和能操作多少位地址总线,没有本质联系。但是如果需要分步操作,效率会低,需要多次操作,不如一次完成来得划算。 因此我们今天的设计通常不拿 32 位 CPU 操作 40 位地址总线,而是用 64 位 CPU 操作。 + +04 | 构造复杂的程序 : 将一个递归函数转成非递归函数的通用方法? + +【问题】 假设你使用的程序语言不支持递归程序,如果要求用栈来模拟下面这个斐波那契求第 n 项的程序,应该如何转换成等价的基于栈的非递归实现? + +int fib(int n) { + + if(n == 1 || n == 2) { return n; } + return fib(n-1) + fib(n-2) + + +【解析】其实这道题目等同于递归的函数如何非递归表达?改写斐波那契数列第 N 项目。 + +下面是我的一个伪代码,需要实现一个 Stack。 + +fib(n) { + + stack = new Stack(); + + // 构造Stack + + // stack中每一项是一个Record + + // Record第一项是数据(参数或者返回值) + + // Record第二项是递归方向(down=1代表向下,up=2代表向上) + + stack.push((n, down)); + + // stack中只有一项的时候递归停止 + + while(stack.size() > 1) { + + (n, phase) = stack.pop(); + + if(phase == down) { + + if(n == 1 || n == 2) { + + stack.push((1, -)) + + continue + + } + + stack.push((n-1, down)) + + stack.push((n-1, up)) + + } + + else { + + last1 = stack.pop() + + last2 = stack.pop() + + stack.push((last1[0] + last2[0], up)) + + } + + } + + return stack.pop()[0]; + +} + + +05 | 存储器分级 :SSD、内存和 L1 Cache 相比速度差多少倍? + +【问题】 假设有一个二维数组,总共有 1M 个条目,如果我们要遍历这个二维数组,应该逐行遍历还是逐列遍历? + +【解析】 二维数组本质还是 1 维数组。只不过进行了脚标运算。比如说一个 N 行 M 列的数组,第 y 行第 x 列的坐标是: x + y*M。因此当行坐标增加时,内存空间是跳跃的。列坐标增加时,内存空间是连续的。 + + + +当 CPU 遍历二维数组的时候,会先从 CPU 缓存中取数据。 + +关键因素在于现在的 CPU 设计不是每次读取一个内存地址,而是读取每次读取相邻的多个内存地址(内存速度 200~300 CPU 周期,预读提升效率)。所以这相当于机器和人的约定,如果程序员不按照这个约定,就无法利用预读的优势。 + +另一方面当读取内存地址跳跃较大的时候,会触发内存的页面置换,这个知识在“模块五:内存管理”中学习。 + +总结 + +以上这些练习题你做得怎么样呢?我看到很多同学在留言区写下了练习题答案、思考过程以及课后总结,当然还有很多同学提出了问题。有问题是好事,说明你在认真思考,这也是构建知识体系的一部分。经过长期的积累,相信你会得到意想不到的收获。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/05存储器分级:L1Cache比内存和SSD快多少倍?.md b/专栏/重学操作系统-完/05存储器分级:L1Cache比内存和SSD快多少倍?.md new file mode 100644 index 0000000..bca5cc9 --- /dev/null +++ b/专栏/重学操作系统-完/05存储器分级:L1Cache比内存和SSD快多少倍?.md @@ -0,0 +1,181 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 05 存储器分级:L1 Cache 比内存和 SSD 快多少倍? + 近两年我在面试求职者的时候,喜欢问这样一道面试题:SSD、内存和 L1 Cache 相比速度差多少倍? + +其实比起复杂的技术问题,我更喜欢在面试中提问这种像生活常识一样的简单问题。因为我觉得,复杂的问题是由简单的问题组成的,如果你把简单的问题学扎实了,那么复杂问题也是可以自己推导的。 + +如果你不知道 L1 Cache,可能会错误地判断内存执行速度。我们写程序,会用寄存器、内存以及硬盘,所以按照墨菲定律,如果这里有一个认知是错误的,那么最终的结果就会产生问题。 + +下面,回到我们今天的问题,这个问题关联的知识点是存储器分级策略。接下来,请你带着问题开始学习今天的内容。 + +为什么会有存储器分级策略? + +要想弄清楚存储器分级策略。 + +首先,你要弄清楚,“我们希望存储器是什么样子的”,也就是“我们的需求是什么”? + +然后,你要弄清楚,我们的需求有哪些“实现约束”。 + +从需求上讲,我们希望存储器速度快、体积小、空间大、能耗低、散热好、断电数据不丢失。但在现实中,我们往往无法把所有需求都实现。 + +下面我们举几个例子,带你深入体会一下,比如: + + +如果一个存储器的体积小,那它存储空间就会受到制约。 +如果一个存储器电子元件密度很大,那散热就会有问题。因为电子元件都会产生热能,所以电子元件非常集中的 CPU,就需要单独的风扇或者水冷帮助电子元件降温。 +如果一个存储器离 CPU 较远,那么在传输过程中必然会有延迟,因此传输速度也会下降。 + + +这里你可能会有疑问,因为在大多数人的认知里,光速是很快的,而信号又是以光速传输的。既然光速这么快,那信号的延迟应该很小才对。但事实并不是这样,比如时钟信号是 1GHz 的 CPU,1G 代表 10 个亿,因此时钟信号的一个周期是 1⁄10 亿秒。而光的速度是 3×10 的 8 次方米每秒,就是 3 亿米每秒。所以在一个周期内,光只能前进 30 厘米。 + +你看!虽然在宏观世界里光速非常快,但是到计算机世界里,光速并没有像我们认知中的那么快。所以即使元件离 CPU 的距离稍微远了一点,运行速度也会下降得非常明显。 + +你可能还会问,那干吗不把内存放到 CPU 里? + +如果你这么做的话,除了整个电路散热和体积会出现问题,服务器也没有办法做定制内存了。也就是说 CPU 在出厂时就决定了它的内存大小,如果你想换更大的内存,就要换 CPU,而组装定制化是你非常重要的诉求,这肯定是不能接受的。 + +此外,在相同价格下,一个存储器的速度越快,那么它的能耗通常越高。能耗越高,发热量越大。 + +因此,我们上面提到的需求是不可能被全部满足的,除非将来哪天存储技术有颠覆性的突破。 + +存储器分级策略 + +既然我们不能用一块存储器来解决所有的需求,那就必须把需求分级。 + +一种可行的方案,就是根据数据的使用频率使用不同的存储器:高频使用的数据,读写越快越好,因此用最贵的材料,放到离 CPU 最近的位置;使用频率越低的数据,我们放到离 CPU 越远的位置,用越便宜的材料。 + + + +具体来说,通常我们把存储器分成这么几个级别: + + +寄存器; +L1-Cache; +L2-Cache; +L3-Cahce; +内存; +硬盘/SSD。 + + +寄存器(Register) + +寄存器紧挨着 CPU 的控制单元和逻辑计算单元,它所使用的材料速度也是最快的。就像我们前面讲到的,存储器的速度越快、能耗越高、产热越大,而且花费也是最贵的,因此数量不能很多。 + +寄存器的数量通常在几十到几百之间,每个寄存器可以用来存储一定字节(byte)的数据。比如: + + +32 位 CPU 中大多数寄存器可以存储 4 个字节; +64 位 CPU 中大多数寄存器可以存储 8 个字节。 + + +寄存机的访问速度非常快,一般要求在半个 CPU 时钟周期内完成读写。比如一条要在 4 个周期内完成的指令,除了读写寄存器,还需要解码指令、控制指令执行和计算。如果寄存器的速度太慢,那 4 个周期就可能无法完成这条指令了。 + +L1-Cache + +L1- 缓存在 CPU 中,相比寄存器,虽然它的位置距离 CPU 核心更远,但造价更低。通常 L1-Cache 大小在几十 Kb 到几百 Kb 不等,读写速度在 2~4 个 CPU 时钟周期。 + +L2-Cache + +L2- 缓存也在 CPU 中,位置比 L1- 缓存距离 CPU 核心更远。它的大小比 L1-Cache 更大,具体大小要看 CPU 型号,有 2M 的,也有更小或者更大的,速度在 10~20 个 CPU 周期。 + +L3-Cache + +L3- 缓存同样在 CPU 中,位置比 L2- 缓存距离 CPU 核心更远。大小通常比 L2-Cache 更大,读写速度在 20~60 个 CPU 周期。L3 缓存大小也是看型号的,比如 i9 CPU 有 512KB L1 Cache;有 2MB L2 Cache; 有16MB L3 Cache。 + +内存 + +内存的主要材料是半导体硅,是插在主板上工作的。因为它的位置距离 CPU 有一段距离,所以需要用总线和 CPU 连接。因为内存有了独立的空间,所以体积更大,造价也比上面提到的存储器低得多。现在有的个人电脑上的内存是 16G,但有些服务器的内存可以到几个 T。内存速度大概在 200~300 个 CPU 周期之间。 + +SSD 和硬盘 + +SSD 也叫固态硬盘,结构和内存类似,但是它的优点在于断电后数据还在。内存、寄存器、缓存断电后数据就消失了。内存的读写速度比 SSD 大概快 10~1000 倍。以前还有一种物理读写的磁盘,我们也叫作硬盘,它的速度比内存慢 100W 倍左右。因为它的速度太慢,现在已经逐渐被 SSD 替代。 + + + +当 CPU 需要内存中某个数据的时候,如果寄存器中有这个数据,我们可以直接使用;如果寄存器中没有这个数据,我们就要先查询 L1 缓存;L1 中没有,再查询 L2 缓存;L2 中没有再查询 L3 缓存;L3 中没有,再去内存中拿。 + +缓存条目结构 + +上面我们介绍了存储器分级结构大概有哪些存储以及它们的特点,接下来还有一些缓存算法和数据结构的设计困难要和你讨论。比如 CPU 想访问一个内存地址,那么如何检查这个数据是否在 L1- 缓存中?换句话说,缓存中的数据结构和算法是怎样的? + +无论是缓存,还是内存,它们都是一个线性存储器,也就是数据一个挨着一个的存储。如果我们把内存想象成一个只有 1 列的表格,那么缓存就是一个多列的表格,这个表格中的每一行叫作一个缓存条目。 + +方案 1 + +缓存本质上是一个 Key-Value 的存储,它的 Key 是内存地址,值是缓存时刻内存地址中的值。我们先思考一种简单的方案,一个缓存条目设计 2 列: + + +内存的地址; +缓存的值。 + + +CPU 读取到一个内存地址,我们就增加一个条目。当我们要查询一个内存地址的数据在不在 L1- 缓存中的时候,可以遍历每个条目,看条目中的内存地址是否和查询的内存地址相同。如果相同,我们就取出条目中缓存的值。 + +这个方法需要遍历缓存中的每个条目,因此计算速度会非常慢,在最坏情况下,算法需要检查所有的条目,所以这不是一个可行的方案。 + +方案 2 + +其实很多优秀的方案,往往是从最笨的方案改造而来的。现在我们已经拥有了一个方案,但是这个方案无法快速确定一个内存地址缓存在哪一行。因此我们想要找到一个更好的方法,让我们看到一个内存地址,就能够快速知道它在哪一行。 + +这里,我们可以用一个数学的方法。比如有 1000 个内存地址,但只有 10 个缓存条目。内存地址的编号是 0、1、2、3,…,999,缓存条目的编号是 0~9。我们思考一个内存编号,比如 701,然后用数学方法把它映射到一个缓存条目,比如 701 整除 10,得到缓存条目 1。 + +用这种方法,我们每次拿到一个内存地址,都可以快速确定它的缓存条目;然后再比较缓存条目中的第一列内存地址和查询的内存地址是否相同,就可以确定内存地址有没有被缓存。 + +延伸一下,这里用到了一种类似哈希表的方法:地址 % 10,其实就构成了一个简单的哈希函数。 + +指令的预读 + +接下来我们讨论下指令预读的问题。 + +之前我们学过,CPU 顺序执行内存中的指令,CPU 执行指令的速度是非常快的,一般是 2~6 个 CPU 时钟周期;这节课,我们学习了存储器分级策略,发现内存的读写速度其实是非常慢的,大概有 200~300 个时钟周期。 + +不知道你发现没有?这也产生了一个非常麻烦的问题:CPU 其实是不能从内存中一条条读取指令再执行的,如果是这样做,那每执行一条指令就需要 200~300 个时钟周期了。 + +那么,这个问题如何处理呢? + +这里我再多说一句,你在做业务开发 RPC 调用的时候,其实也会经常碰到这种情况,远程调用拖慢了整体执行效率,下面我们一起讨论这类问题的解决方案。 + +一个解决办法就是 CPU 把内存中的指令预读几十条或者上百条到读写速度较快的 L1- 缓存中,因为 L1- 缓存的读写速度只有 2~4 个时钟周期,是可以跟上 CPU 的执行速度的。 + +这里又产生了另一个问题:如果数据和指令都存储在 L1- 缓存中,如果数据缓存覆盖了指令缓存,就会产生非常严重的后果。因此,L1- 缓存通常会分成两个区域,一个是指令区,一个是数据区。 + +与此同时,又出现了一个问题,L1- 缓存分成了指令区和数据区,那么 L2/L3 需不需要这样分呢?其实,是不需要的。因为 L2 和 L3,不需要协助处理指令预读的问题。 + +缓存的命中率 + +接下来,还有一个重要的问题需要解决。就是 L1/L2/L3 加起来,缓存的命中率有多少? + +所谓命中就是指在缓存中找到需要的数据。和命中相反的是穿透,也叫 miss,就是一次读取操作没有从缓存中找到对应的数据。 + +据统计,L1 缓存的命中率在 80% 左右,L1/L2/L3 加起来的命中率在 95% 左右。因此,CPU 缓存的设计还是相当合理的。只有 5% 的内存读取会穿透到内存,95% 都能读取到缓存。 这也是为什么程序语言逐渐取消了让程序员操作寄存器的语法,因为缓存保证了很高的命中率,多余的优化意义不大,而且很容易出错。 + +缓存置换问题 + +最后的一个问题,比如现在 L1- 缓存条目已经存满了,接下来 CPU 又读了内存,需要把一个新的条目存到 L1- 缓存中,既然有一个新的条目要进来,那就有一个旧的条目要出去。所以,这个时候我们就需要用一个算法去计算哪个条目应该被置换出去。这个问题叫作缓存置换问题。有关缓存置换问题,我会在 “21 | 进程的调度:进程调度都有哪些方法?”中和你讨论。 + +总结 + +这节课我们讲到了存储器分级策略,讨论了 L1/L2/L3 缓存的工作原理。本课时学习的内容,是所有缓存知识的源头。所有缓存系统的设计,都是存储资源的分级。我们在设计缓存的时候,除了要关心整体架构外,还需要注意细节,比如: + + +条目怎么设计? +算法怎么设计? +命中率怎么统计? +缓存怎么置换等? + + +现在我们来说一下课前提出的问题:SSD、内存和 L1 Cache 相比速度差多少倍? + +还是老规矩,请你先自己思考这个问题的答案,写在留言区,然后再来看我接下来的分析。 + +【解析】 因为内存比 SSD 快 10~1000 倍,L1 Cache 比内存快 100 倍左右。因此 L1 Cache 比 SSD 快了 1000~100000 倍。所以你有没有发现 SSD 的潜力很大,好的 SSD 已经接近内存了,只不过造价还略高。 + +这个问题告诉我们,不同的存储器之间性能差距很大,构造存储器分级很有意义,分级的目的是要构造缓存体系。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/06目录结构和文件管理指令:rm-rf指令的作用是?.md b/专栏/重学操作系统-完/06目录结构和文件管理指令:rm-rf指令的作用是?.md new file mode 100644 index 0000000..2df913a --- /dev/null +++ b/专栏/重学操作系统-完/06目录结构和文件管理指令:rm-rf指令的作用是?.md @@ -0,0 +1,274 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 06 目录结构和文件管理指令:rm -rf 指令的作用是? + 通过模块一的学习,你应该掌握了计算机组成原理的重点知识,到了模块二,我们开始学习 Linux 指令,它是操作系统的前端,学好这部分内容一方面可以帮助你应对工作场景,另一方面可以让你在学习操作系统底层知识前,对 Linux 有一个大概的了解。 + +接下来,我们依然通过一道常见的高频面试题,引出今天的主要内容。面试题如下:请你说说rm / -rf的作用? + +相信 90% 的同学是知道这个指令的。这里先预警一下,你千万不要轻易在服务器上尝试。要想知道这条指令是做什么的,能够帮助我们解决哪些问题,那就请你认真学习今天的内容。在本课时的最后我会公布这道题目的分析过程和答案。 + +什么是 Shell + +在我们学习 Linux 指令之前,先来说一下什么是 Shell?Shell 把我们输入的指令,传递给操作系统去执行,所以 Shell 是一个命令行的用户界面。 + +早期程序员没有图形界面用,就用 Shell。而且图形界面制作成本较高,不能实现所有功能,因此今天的程序员依然在用 Shell。 + +你平时还经常会看到一个词叫作bash(Bourne Again Shell),它是用 Shell 组成的程序。这里的 Bourne 是一个人名,Steve Bourne 是 bash 的发明者。 + +我们今天学习的所有指令,不是写死在操作系统中的,而是一个个程序。比如rm指令,你可以用which指令查看它所在的目录。如下图所示,你会发现rm指令在/usr/bin/rm目录中。 + + + +如上图所示,ramroll是我的英文名字,ubuntu 是我这台机器的名字。我输入了which rm,然后获得了/usr/bin/rm的结果,最终执行这条指令的是操作系统,连接我和操作系统的程序就是 Shell。 + +Linux 对文件目录操作的指令就工作在 Shell 上,接下来我们讲讲文件目录操作指令。 + +Linux 对文件目录的抽象 + +Linux 对文件进行了一个树状的抽象。/代表根目录,每一节目录也用/分开,所以在上图所展示的/usr/bin/rm中,第一级目录是/根目录,第二级目录是usr目录,第三级是bin目录。最后的rm是一个文件。 + +路径(path) + +像/usr/bin/rm称为可执行文件rm的路径。路径就是一个文件在文件系统中的地址。如果文件系统是树形结构,那么通常一个文件只有一个地址(路径)。 + +目标文件的绝对路径(Absolute path),也叫作完全路径(full path),是从/开始,接下来每一层都是一级子目录,直到定位到目标文件为止。 + +如上图所示的例子中,/usr/bin/rm就是一个绝对路径。 + +工作目录 + +为了方便你工作,Shell 还抽象出了工作目录。当用户打开 Shell 的时候,Shell 就会给用户安排一个工作目录。因此也就产生了相对路径。 + +相对路径(Relative path)是以工作目录为基点的路径。比如: + + +当用户在/usr目录下的时候,rm文件的相对路径就是bin/rm; +如果用户在/usr/bin目录下的时候,rm文件的路径就是./rm或者rm,这里用.代表当前目录; +如果用户在/usr/bin/somedir下,那么rm的相对路径就是../rm,这里用..代表上一级目录。 + + +我们使用cd(change directory)指令切换工作目录,既可以用绝对路径,也可以用相对路径。 这里我要强调几个注意事项: + + +输入cd,不带任何参数会切换到用户的家目录,Linux 中通常是/home/{用户名}。以我自己为例,我的家目录是/home/ramroll; +输入cd .什么都不会发生,因为.代表当前目录; +输入cd..会回退一级目录,因为..代表上级目录。 + + +利用上面这 3 种能力,你就可以方便的构造相对路径了。 + +Linux提供了一个指令pwd(Print Working Directory)查看工作目录。下图是我输入pwd的结果。 + + + +你可以看到我正在/home/ramroll/Documents目录下工作。 + +几种常见的文件类型 + +另一方面,Linux 下的目录也是一种文件;但是文件也不只有目录和可执行文件两种。常见的文件类型有以下 7 种: + + +普通文件(比如一个文本文件); +目录文件(目录也是一个特殊的文件,它用来存储文件清单,比如/也是一个文件); +可执行文件(上面的rm就是一个可执行文件); +管道文件(我们会在 07 课时讨论管道文件); +Socket 文件(我们会在模块七网络部分讨论 Socket 文件); +软链接文件(相当于指向另一个文件所在路径的符号); +硬链接文件(相当于指向另一个文件的指针,关于软硬链接我们将在模块六文件系统部分讨论)。 + + +你如果使用ls -F就可以看到当前目录下的文件和它的类型。比如下面这种图: + + +* 结尾的是可执行文件; += 结尾的是 Socket 文件; +@ 结尾的是软链接; +| 结尾的管道文件; +没有符号结尾的是普通文件; +/ 结尾的是目录。 + + + + +设备文件 + +Socket 是网络插座,是客户端和服务器之间同步数据的接口。其实,Linux 不只把 Socket 抽象成了文件,设备基本也都被抽象成了文件。因为设备需要不断和操作系统交换数据。而交换方式只有两种——读和写。所以设备是可以抽象成文件的,因为文件也支持这两种操作。 + +Linux 把所有的设备都抽象成了文件,比如说打印机、USB、显卡等。这让整体的系统设计变得高度统一。 + +至此,我们了解了 Linux 对文件目录的抽象,接下来我们看看具体的增删改查指令。 + +文件的增删改查 + +增加 + +创建一个普通文件的方法有很多,最常见的有touch指令。比如下面我们创建了一个 a.txt 文件。 + + + +touch指令本来是用来更改文件的时间戳的,但是如果文件不存在touch也会帮助创建一个空文件。 + +如果你拿到一个指令不知道该怎么用,比如touch,你可以用man touch去获得帮助。man意思是 manual,就是说明书的意思,这里指的是系统的手册。如果你不知道man是什么,也可以使用man man。下图是使用man man的结果: + + + +另外如果我们需要增加一个目录,就需要用到mkdir指令( make directory),比如我们创建一个hello目录,如下图所示: + + + +查看 + +创建之后我们可以用ls指令看到这个文件,ls是 list 的缩写。下面是指令 ‘ls’ 的执行结果。 + + + +我们看到在当前的目录下有一个a.txt文件,还有一个hello目录。如果你知道当前的工作目录,就可以使用pwd指令。 + +如果想看到a.txt更完善的信息,还可以使用ls -l。-l是ls指令的可选参数。下图是ls -l的结果,你可以看到a.txt更详细的描述。 + + + +如上图所示,我们看到两个ramroll,它们是a.txt所属的用户和所属的用户分组,刚好重名了。Sep 13是日期。 中间有一个0是a.txt的文件大小,目前a.txt中还没有写入内容,因此大小是0。 + +另外虽然hello是空的目录,但是目录文件 Linux 上来就分配了4096字节的空间。这是因为目录内需要保存很多文件的描述信息。 + +删除 + +如果我们想要删除a.txt可以用rm a.txt;如我们要删除hello目录,可以用rm hello。rm是 remove 的缩写。 + + + +但是当我们输入rm hello的时候,会提示hello是一个目录,不可以删除。因此我们需要增加一个可选项,比如-r即 recursive(递归)。目录是一个递归结构,所以需要用递归删除。最后,你会发现rm hello -r删除了hello目录。 + +接下来我们尝试在 hello 目录下新增一个文件,比如相对路径是hello/world/os.txt。需要先创建 hello/world 目录。这种情况会用到mkdir的-p参数,这个参数控制mkdir当发现目标目录的父级目录不存在的时候会递归的创建。以下是我们的执行结果: + + + +修改 + +如果需要修改一个文件,可以使用nano或者vi编辑器。类似的工具还有很多,但是nano和vi一般是linux自带的。 + +这里我不展开讲解了,你可以自己去尝试。在尝试的过程中如果遇到什么问题,可以写在留言区,我会逐一为你解答。 + +查阅文件内容 + +在了解了文件的增删改查操作后,下面我们来学习查阅文件内容。我们知道,Linux 下查阅文件内容,可以根据不同场景选择不同的指令。 + +当文件较小时,比如一个配置文件,想要快速浏览这个文件,可以用cat指令。下面 cat 指令帮助我们快速查看/etc/hosts文件。cat指令将文件连接到标准输出流并打印到屏幕上。 + + + +标准输出流(Standard Output)也是一种文件,进程可以将要输出的内容写入标准输出流文件,这样就可以在屏幕中打印。 + +如果用cat查看大文件,比如一个线上的日志文件,因为动辄有几个 G,控制台打印出所有的内容就要非常久,而且刷屏显示看不到东西。 + +而且如果在线上进行查看大文件的操作,会带来不必要的麻烦: + +首先因为我们需要把文件拷贝到输入输出流,这需要花费很长时间,这个过程会占用机器资源; + +其次,本身文件会读取到内存中,这时内存被大量占用,很危险,这可能导致其他应用内存不足。因此我们需要一些不用加载整个文件,就能查看文件内容的指令。 + +more + +more可以帮助我们读取文件,但不需要读取整个文件到内存中。本身more的定位是一个阅读过滤器,比如你在more里除了可以向下翻页,还可以输入一段文本进行搜索。 + + + +如上图所示,我在more查看一个 nginx 日志后,先输入一个/,然后输入192.168看到的结果。more帮我找到了192.168所在的位置,然后又帮我定位到了这个位置。整个过程 more 指令只读取我们需要的部分到内存中。 + +less + +less是一个和more功能差不多的工具,打开man能够看到less的介绍上写着自己是more的反义词(opposite of more)。这样你可以看出linux生态其实也是很自由的一个生态,在这里创造工具也可以按照自己的喜好写文档。less支持向上翻页,这个功能more是做不到的。所以现在less用得更多一些。 + +head/tail + +head和tail是一组,它们用来读取一个文件的头部 N 行或者尾部 N 行。比如一个线上的大日志文件,当线上出了 bug,服务暂停的时候,我们就可以用tail -n 1000去查看最后的 1000 行日志文件,寻找导致服务异常的原因。 + +另一个比较重要的用法是,如果你想看一个实时的nginx日志,可以使用tail -f 文件名,这样你会看到用户的请求不断进来。查一下man,你会发现-f是 follow 的意思,就是文件追加的内容会跟随输出到标准输出流。 + +grep + +有时候你需要查看一个指定ip的nginx日志,或者查看一段时间内的nginx日志。如果不想用less和more进入文件中去查看,就可以用grep命令。Linux 的文件命名风格都很短,所以也影响了很多人,比如之前我看到过一个大牛的程序,变量名从来不超过 5 个字母,而且都有意义。 + +grep 这个词,我们分成三段来看,是 g|re|p。 + + +g 就是 global,全局; +re 就是 regular expression,正则表达式; +p 就是 pattern,模式。 + + +所以这个指令的作用是通过正则表达式全局搜索一个文件找到匹配的模式。我觉得这种命名真的很牛,软件命名也是一个世纪难题,grep这个名字不但发音不错,而且很有含义,又避免了名字过长,方便记忆。 + +下面我们举两个例子看看 grep 的用法: + + +例 1:查找 ip 地址 + + +我们可以通过grep命令定位某个ip地址的用户都做了什么事情,如下图所示: + + + + +例 2:查找时间段的日志 + + +我们可以通过 grep 命令查找某个时间段内用户都做了什么事情。如下图所示,你可以看到在某个 5 分钟内所有用户的访问情况。 + + + +查找文件 + +用户经常还会有一种诉求,就是查找文件。 + +之前我们使用过一个which指令,这个指令可以查询一个指令文件所在的位置,比如which grep会,你会看到grep指令被安装的位置是/usr/bin。但是我们还需要一个更加通用的指令查找文件,也就是 find 指令。 + +find + +find 指令帮助我们在文件系统中查找文件。 比如我们如果想要查找所有.txt 扩展名的文件,可以使用find / -iname "*.txt",-iname这个参数是用来匹配查找的,i 字母代表忽略大小写,这里也可以用-name替代。输入这条指令,你会看到不断查找文件,如下图所示: + + + +总结 + +这节课我们学习了很多指令,不知道你记住了多少?最后,我们再一起复习一下。 + + +pwd指令查看工作目录。 +cd指令切换工作目录。 +which指令查找一个执行文件所在的路径。 +ls显示文件信息。 +rm删除文件。 +touch修改一个文件的时间戳,如果文件不存在会触发创建文件。 +vi和nano可以用来编辑文件。 +cat查看完成的文件适合小型文件。 +moreless查看一个文件但是只读取用户看到的内容到内存,因此消耗资源较少,适合在服务器上看日志。 +headtail可以用来看文件的头和尾。 +grep指令搜索文件内容。 +find指令全局查找文件。 + + +在这里,我再强调一个指令,即man指令,它是所有指令的手册,所以你一定要多多运用,熟练掌握。另外,一个指令通常有非常多的参数,但都需要用man指令去仔细研究。 + +那么通过这节课的学习,你现在可以来回答本节关联的面试题目:rm / -rf的作用是? + +老规矩,请你先在脑海里先思考你的答案,并把你的思考写在留言区,然后再来看我接下来的分析。 + +【解析】 + + +/是文件系统根目录; +rm是删除指令; +-r是 recursive(递归); +-f是 force(强制),遇到只读文件也不提示,直接删除。 + + +所以rm -rf /就是删除整个文件系统上的所有文件,而且不用给用户提示。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/07进程、重定向和管道指令:xargs指令的作用是?.md b/专栏/重学操作系统-完/07进程、重定向和管道指令:xargs指令的作用是?.md new file mode 100644 index 0000000..70fc78d --- /dev/null +++ b/专栏/重学操作系统-完/07进程、重定向和管道指令:xargs指令的作用是?.md @@ -0,0 +1,273 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 07 进程、重定向和管道指令:xargs 指令的作用是? + 在面试中,我们经常会遇到面试官询问 Linux 指令,06 课时中讲到的rm -rf /属于比较简单的题目,相当于小学难度。这节课给你带来一道初中难度的题目:xargs指令的作用是什么? + +通常这个指令是和管道一起使用,因此就引出了这节课的主题:管道。为了理解管道,和学习管道相关的内容,还有一些概念需要你理解,比如:进程、标准流和重定向。好的,接下来请和我一起,把这块知识一网打尽! + +进程 + +为了弄清楚这节课程的内容,也就是管道,我们先来讨论一下进程。 + +我们知道,应用的可执行文件是放在文件系统里,把可执行文件启动,就会在操作系统里(具体来说是内存中)形成一个应用的副本,这个副本就是进程。 + +插一个小知识,以后你再遇到面试题:什么是进程? + +可以回答:进程是应用的执行副本;而不要回答进程是操作系统分配资源的最小单位。前者是定义,后者是作用。 + +ps + +如果你要看当前的进程,可以用ps指令。p 代表 processes,也就是进程;s 代表 snapshot,也就是快照。所谓快照,就是像拍照一样。 + + + +如上图所示,我启动了两个进程,ps和bash。ps 就是我刚刚启动的,被ps自己捕捉到了;bash是因为我开了这个控制台,执行的shell是bash。 + +当然操作系统也不可能只有这么几个进程,这是因为不带任何参数的ps指令显示的是同一个电传打字机(TTY上)的进程。TTY 这个概念是一个历史的概念,过去用来传递信息,现在已经被传真、邮件、微信等取代。 + +操作系统上的 TTY 是一个输入输出终端的概念,比如用户打开 bash,操作系统就为用户分配了一个输入输出终端。没有加任何参数的ps只显示在同一个 TTY 的进程。 + +如果想看到所有的进程,可以用ps -e,-e没有特殊含义,只是为了和-A区分开。我们通常不直接用ps -e而是用ps -ef,这是因为-f可以带上更多的描述字段,如下图所示: + + + + +UID 指进程的所有者; +PID 是进程的唯一标识; +PPID 是进程的父进程 ID; +C 是 CPU 的利用率(就是 CPU 占用); +STIME 是开始时间; +TTY 是进程所在的 TTY,如果没有 TTY 就是 ?号; +TIME; +CMD 是进程启动时的命令,如果不是一个 Shell 命令,而是用方括号括起来,那就是系统进程或者内核过程。 + + +另外一个用得比较多的是ps aux,它和ps -ef能力差不多,但是是 BSD 风格的。就是加州伯克利分校研发的 Unix 分支版本的衍生风格,这种风格其实不太好描述,我截了一张图,你可以体会一下: + + + +在 BSD 风格中有些字段的叫法和含义变了,如果你感兴趣,可以作为课后延伸学习的内容。 + +top + +另外还有一个和ps能力差不多,但是显示的不是快照而是实时更新数据的top指令。因为自带的top显示的内容有点少, 所以我喜欢用一个叫作htop的指令,具体的安装全方法我会在 10 | 软件的安装: 编译安装和包管理器安装有什么优势和劣势?中给你介绍。本课时,我们先看一下使用效果,如下图所示: + + + +以上,我们一起把进程学了一个皮毛,更多关于进程的内容我们会在模块四:进程和线程中讨论。 + +管道(Pipeline) + +现在你已经掌握了一点点进程的基础,下面我们来学习管道,管道(Pipeline)的作用是在命令和命令之间,传递数据。比如说一个命令的结果,就可以作为另一个命令的输入。我们了解了进程,所以这里说的命令就是进程。更准确地说,管道在进程间传递数据。 + +输入输出流 + +每个进程拥有自己的标准输入流、标准输出流、标准错误流。 + +这几个标准流说起来很复杂,但其实都是文件。 + + +标准输入流(用 0 表示)可以作为进程执行的上下文(进程执行可以从输入流中获取数据)。 +标准输出流(用 1 表示)中写入的结果会被打印到屏幕上。 +如果进程在执行过程中发生异常,那么异常信息会被记录到标准错误流(用 2 表示)中。 + + +重定向 + +我们执行一个指令,比如ls -l,结果会写入标准输出流,进而被打印。这时可以用重定向符将结果重定向到一个文件,比如说ls -l > out,这样out文件就会有ls -l的结果;而屏幕上也不会再打印ls -l的结果。 + + + +具体来说>符号叫作覆盖重定向;>>叫作追加重定向。>每次都会把目标文件覆盖,>>会在目标文件中追加。比如你每次启动一个程序日志都写入/var/log/somelogfile中,可以这样操作,如下所示: + +start.sh >> /var/log/somelogfile + + +经过这样的操作后,每次执行程序日志就不会被覆盖了。 + +另外还有一种情况,比如我们输入: + +ls1 > out + + +结果并不会存入out文件,因为ls1指令是不存在的。结果会输出到标准错误流中,仍然在屏幕上。这里我们可以把标准错误流也重定向到标准输出流,然后再重定向到文件。 + +ls1 &> out + + +这个写法等价于: + +ls1 > out 2>&1 + + + + +相当于把ls1的标准输出流重定向到out,因为ls1 > out出错了,所以标准错误流被定向到了标准输出流。&代表一种引用关系,具体代表的是ls1 >out的标准输出流。 + +管道的作用和分类 + +有了进程和重定向的知识,接下来我们梳理下管道的作用。管道(Pipeline)将一个进程的输出流定向到另一个进程的输入流,就像水管一样,作用就是把这两个文件接起来。如果一个进程输出了一个字符 X,那么另一个进程就会获得 X 这个输入。 + +管道和重定向很像,但是管道是一个连接一个进行计算,重定向是将一个文件的内容定向到另一个文件,这二者经常会结合使用。 + +Linux 中的管道也是文件,有两种类型的管道: + + +匿名管道(Unnamed Pipeline),这种管道也在文件系统中,但是它只是一个存储节点,不属于任何一个目录。说白了,就是没有路径。 +命名管道(Named Pipeline),这种管道就是一个文件,有自己的路径。 + + +FIFO + +管道具有 FIFO(First In First Out),FIFO 和排队场景一样,先排到的先获得。所以先流入管道文件的数据,也会先流出去传递给管道下游的进程。 + +使用场景分析 + +接下来我们以多个场景举例帮助你深入学习管道。 + +排序 + +比如我们用ls,希望按照文件名排序倒序,可以使用匿名管道,将ls的结果传递给sort指令去排序。你看,这样ls的开发者就不用关心排序问题了。 + + + +去重 + +另一个比较常见的场景是去重,比如有一个字典文件,里面都是词语。如下所示: + +Apple + +Banana + +Apple + +Banana + +…… + + +如果我们想要去重可以使用uniq指令,uniq指令能够找到文件中相邻的重复行,然后去重。但是我们上面的文件重复行是交替的,所以不可以直接用uniq,因此可以先sort这个文件,然后利用管道将sort的结果重定向到uniq指令。指令如下: + + + +筛选 + +有时候我们想根据正则模式筛选对应的内容。比如说我们想找到项目文件下所有文件名中含有Spring的文件。就可以利用grep指令,操作如下: + +find ./ | grep Spring + + +find ./递归列出当前目录下所有目录中的文件。grep从find的输出流中找出含有Spring关键字的行。 + +如果我们希望包含Spring但不包含MyBatis就可以这样操作: + +find ./ | grep Spring | grep -v MyBatis + + +grep -v是匹配不包含 MyBatis 的结果。 + +数行数 + +还有一个比较常见的场景是数行数。比如你写了一个 Java 文件想知道里面有多少行,就可以使用wc -l指令,如下所示: + + + +但是如果你想知道当前目录下有多少个文件,可以用ls | wc -l,如下所示: + + + +接下来请你思考一个问题:我们如何知道当前java的项目目录下有多少行代码? + +提示一下。你可以使用下面这个指令: + +find -i ".java" ./ | wc -l + + +快去自己动手写一写吧,你在尝试的过程中如果遇到什么问题,也可以写在留言区,我会逐一为你解答。 + +中间结果 + +管道一个接着一个,是一个计算逻辑。有时候我们想要把中间的结果保存下来,这就需要用到tee指令。tee指令从标准输入流中读取数据到标准输出流。 + +这时候,你可能会问: 老师, 这不是什么都没做吗? + +别急,tee还有一个能力,就是自己利用这个过程把输入流中读取到的数据存到文件中。比如下面这条指令: + +find ./ -i "*.java" | tee JavaList | grep Spring + + +这句指令的意思是从当前目录中找到所有含有 Spring 关键字的 Java 文件。tee 本身不影响指令的执行,但是 tee 会把 find 指令的结果保存到 JavaList 文件中。 + +tee这个执行就像英文字母中的 T 一样,连通管道两端,下面又开了口。这个开口,在函数式编程里面叫作副作用。 + +xargs + +上面我们学习的内容难度,已经由小学 1 年级攀升到了小学 6 年级,最后我们来看看初中难度的xargs指令。 + +xargs指令从标准数据流中构造并执行一行行的指令。xargs从输入流获取字符串,然后利用空白、换行符等切割字符串,在这些字符串的基础上构造指令,最后一行行执行这些指令。 + +举个例子,如果我们重命名当前目录下的所有 .a 的文件,想在这些文件前面加一个前缀prefix_。比如说x.a文件需要重命名成prefix_x.a,我们就可以用xargs指令构造模块化的指令。 + +现在我们有x.ay.az.a三个文件,如下图所示: + + + +然后使用下图中的指令构造我们需要的指令: + + + + +我们用ls找到所有的文件; +-I参数是查找替换符,这里我们用GG替代ls找到的结果;-I GG后面的字符串 GG 会被替换为x.ax.b或x.z; +echo是一个在命令行打印字符串的指令。使用echo主要是为了安全,帮助我们检查指令是否有错误。 + + +我们用xargs构造了 3 条指令。这里我再多讲一个词,叫作样板代码。如果你没有用xargs指令,而是用一条条mv指令去敲,这样就构成了样板代码。 + +最后去掉 echo,就是我们想要的结果,如下所示: + + + +管道文件 + +上面我们花了较长的一段时间讨论匿名管道,用|就可以创造和使用。匿名管道也是利用了文件系统的能力,是一种文件结构。当你学到模块六文件系统的内容,会知道匿名管道拥有一个自己的inode,但不属于任何一个文件夹。 + +还有一种管道叫作命名管道(Named Pipeline)。命名管道是要挂到文件夹中的,因此需要创建。用mkfifo指令可以创建一个命名管道,下面我们来创建一个叫作pipe1的命名管道,如下图所示: + + + +命名管道和匿名管道能力类似,可以连接一个输出流到另一个输入流,也是 First In First Out。 + +当执行cat pipe1的时候,你可以观察到,当前的终端处于等待状态。因为我们cat pipe1的时候pipe1中没有内容。 + +如果这个时候我们再找一个终端去写一点东西到pipe中,比如说: + +echo "XXX" > pipe1 + + +这个时候,cat pipe1就会返回,并打印出xxx,如下所示: + + + +我们可以像上图那样演示这段程序,在cat pipe1后面增加了一个&符号。这个&符号代表指令在后台执行,不会阻塞用户继续输入。然后我们通过echo指令往pipe1中写入东西,接着就会看到xxx被打印出来。 + +总结 + +这节课我们为了学习管道,先简单接触了进程的概念,然后学习了重定向。之后我们学习了匿名管道的应用场景,匿名管道帮助我们把 Linux 指令串联起来形成很强的计算能力。特别是xargs指令支持模板化的生成指令,拓展了指令的能力。最后我们还学习了命名管道,命名管道让我们可以真实拿到一个管道文件,让多个程序之间可以方便地进行通信。 + +那么通过这节课的学习,你现在可以来回答本节关联的面试题目:xargs 的作用了吗? + +老规矩,请你先在脑海里构思下给面试官的表述,并把你的思考写在留言区,然后再来看我接下来的分析。 + +【解析】 xargs 将标准输入流中的字符串分割成一条条子字符串,然后再按照我们自己想要的方式构建成一条条指令,大大拓展了 Linux 指令的能力。 + +比如我们可以用来按照某种特定的方式逐个处理一个目录下所有的文件;根据一个 IP 地址列表逐个 ping 这些 IP,收集到每个 IP 地址的延迟等。 + +思考题 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/08用户和权限管理指令:请简述Linux权限划分的原则?.md b/专栏/重学操作系统-完/08用户和权限管理指令:请简述Linux权限划分的原则?.md new file mode 100644 index 0000000..d5fee54 --- /dev/null +++ b/专栏/重学操作系统-完/08用户和权限管理指令:请简述Linux权限划分的原则?.md @@ -0,0 +1,279 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 08 用户和权限管理指令: 请简述 Linux 权限划分的原则? + 我看到过这样一道面试题:请简述 Linux 权限划分的原则? + +这种类型的面试题也是我比较喜欢的一种题目,因为它考察的不仅是一个具体的指令,还考察了候选人技术层面的认知。 + +如果你对 Linux 权限有较深的认知和理解,那么完全可以通过查资料去完成具体指令的执行。更重要的是,认知清晰的程序员可以把 Linux 权限管理的知识迁移到其他的系统设计中。而且我认为,能够对某个技术形成认知的人, 同样也会热爱思考,善于总结,这样的程序员是所有团队梦寐以求的。 + +因此,这次我们就把这道面试题作为引子,开启今天的学习。 + +权限抽象 + +一个完整的权限管理体系,要有合理的抽象。这里就包括对用户、进程、文件、内存、系统调用等抽象。下面我将带你一一了解。 + +首先,我们先来说说用户和组。Linux 是一个多用户平台,允许多个用户同时登录系统工作。Linux 将用户抽象成了账户,账户可以登录系统,比如通过输入登录名 + 密码的方式登录;也可以通过证书的方式登录。 + +但为了方便分配每个用户的权限,Linux 还支持组 (Group)账户。组账户是多个账户的集合,组可以为成员们分配某一类权限。每个用户可以在多个组,这样就可以利用组给用户快速分配权限。 + +组的概念有点像微信群。一个用户可以在多个群中。比如某个组中分配了 10 个目录的权限,那么新建用户的时候可以将这个用户增加到这个组中,这样新增的用户就不必再去一个个目录分配权限。 + +而每一个微信群都有一个群主,Root 账户也叫作超级管理员,就相当于微信群主,它对系统有着完全的掌控。一个超级管理员可以使用系统提供的全部能力。 + +此外,Linux 还对文件进行了权限抽象(注意目录也是一种文件)。Linux 中一个文件可以设置下面 3 种权限: + + +读权限(r):控制读取文件。 +写权限(w):控制写入文件。 +执行权限(x):控制将文件执行,比如脚本、应用程序等。 + + + + +然后每个文件又可以从 3 个维度去配置上述的 3 种权限: + + +用户维度。每个文件可以所属 1 个用户,用户维度配置的 rwx 在用户维度生效; +组维度。每个文件可以所属 1 个分组,组维度配置的 rwx 在组维度生效; +全部用户维度。设置对所有用户的权限。 + + + + +因此 Linux 中文件的权限可以用 9 个字符,3 组rwx描述:第一组是用户权限,第二组是组权限,第三组是所有用户的权限。然后用-代表没有权限。比如rwxrwxrwx代表所有维度可以读写执行。rw--wxr-x代表用户维度不可以执行,组维度不可以读取,所有用户维度不可以写入。 + +通常情况下,如果用ls -l查看一个文件的权限,会有 10 个字符,这是因为第一个字符代表的是文件类型。我们在 06 课时讲解“几种常见的文件类型”时提到过,有管道文件、目录文件、链接文件等等。-代表普通文件、d代表目录、p代表管道。 + +学习了这套机制之后,请你跟着我的节奏一起思考以下 4 个问题。 + + +文件被创建后,初始的权限如何设置? +需要全部用户都可以执行的指令,比如ls,它们的权限如何分配? +给一个文本文件分配了可执行权限会怎么样? +可不可以多个用户都登录root,然后只用root账户? + + +你可以把以上 4 个问题作为本课时的小测验,把你的思考或者答案写在留言区,然后再来看我接下来的分析。 + +问题一:初始权限问题 + +一个文件创建后,文件的所属用户会被设置成创建文件的用户。谁创建谁拥有,这个逻辑很顺理成章。但是文件的组又是如何分配的呢? + +这里 Linux 想到了一个很好的办法,就是为每个用户创建一个同名分组。 + +比如说zhang这个账户创建时,会创建一个叫作zhang的分组。zhang登录之后,工作分组就会默认使用它的同名分组zhang。如果zhang想要切换工作分组,可以使用newgrp指令切换到另一个工作分组。因此,被创建文件所属的分组是当时用户所在的工作分组,如果没有特别设置,那么就属于用户所在的同名分组。 + +再说下文件的权限如何?文件被创建后的权限通常是: + +rw-rw-r-- + + +也就是用户、组维度不可以执行,所有用户可读。 + +问题二:公共执行文件的权限 + +前面提到过可以用which指令查看ls指令所在的目录,我们发现在/usr/bin中。然后用ls -l查看ls的权限,可以看到下图所示: + + + +第一个-代表这是一个普通文件,后面的 rwx 代表用户维度可读写和执行;第二个r-x代表组维度不可以写;第三个r-x代表所有用户可以读和执行。后面的两个root,第一个是所属用户,第二个是所属分组。 + +到这里你可能会有一个疑问:如果一个文件设置为不可读,但是可以执行,那么结果会怎样? + +答案当然是不可以执行,无法读取文件内容自然不可以执行。 + +问题三:执行文件 + +在 Linux 中,如果一个文件可以被执行,则可以直接通过输入文件路径(相对路径或绝对路径)的方式执行。如果想执行一个不可以执行的文件,Linux 则会报错。 + +当用户输入一个文件名,如果没有指定完整路径,Linux 就会在一部分目录中查找这个文件。你可以通过echo $PATH看到 Linux 会在哪些目录中查找可执行文件,PATH是 Linux 的环境变量,关于环境变量,我将在 “12 | 高级技巧之集群部署中”和你详细讨论。 + + + +问题四:可不可以都 root + +最后一个问题是,可不可以都root? + +答案当然是不行!这里先给你留个悬念,具体原因我们会在本课时最后来讨论。 + +到这里,用户和组相关权限就介绍完了。接下来说说内核和系统调用权限。 内核是操作系统连接硬件、提供最核心能力的程序。今天我们先简单了解一下,关于内核的详细知识,会在“14 |用户态和内核态:用户态线程和内核态线程有什么区别?”中介绍。 + +内核提供操作硬件、磁盘、内存分页、进程等最核心的能力,并拥有直接操作全部内存的权限,因此内核不能把自己的全部能力都提供给用户,而且也不能允许用户通过shell指令进行系统调用。Linux 下内核把部分进程需要的系统调用以 C 语言 API 的形式提供出来。部分系统调用会有权限检查,比如说设置系统时间的系统调用。 + +以上我们看到了 Linux 对系统权限的抽象。接下来我们再说说权限架构的思想。 + +权限架构思想 + +优秀的权限架构主要目标是让系统安全、稳定且用户、程序之间相互制约、相互隔离。这要求权限系统中的权限划分足够清晰,分配权限的成本足够低。 + +因此,优秀的架构,应该遵循最小权限原则(Least Privilege)。权限设计需要保证系统的安全和稳定。比如:每一个成员拥有的权限应该足够的小,每一段特权程序执行的过程应该足够的短。对于安全级别较高的时候,还需要成员权限互相牵制。比如金融领域通常登录线上数据库需要两次登录,也就是需要两个密码,分别掌握在两个角色手中。这样即便一个成员出了问题,也可以保证整个系统安全。 + +同样的,每个程序也应该减少权限,比如说只拥有少量的目录读写权限,只可以进行少量的系统调用。 + +权限划分 + +此外,权限架构思想还应遵循一个原则,权限划分边界应该足够清晰,尽量做到相互隔离。Linux 提供了用户和分组。当然 Linux 没有强迫你如何划分权限,这是为了应对更多的场景。通常我们服务器上重要的应用,会由不同的账户执行。比如说 Nginx、Web 服务器、数据库不会执行在一个账户下。现在随着容器化技术的发展,我们甚至希望每个应用独享一个虚拟的空间,就好像运行在一个单独的操作系统中一样,让它们互相不用干扰。 + +到这里,你可能会问:为什么不用 root 账户执行程序? 下面我们就来说说 root 的危害。 + +举个例子,你有一个 Mysql 进程执行在 root(最大权限)账户上,如果有黑客攻破了你的 Mysql 服务,获得了在 Mysql 上执行 Sql 的权限,那么,你的整个系统就都暴露在黑客眼前了。这会导致非常严重的后果。 + +黑客可以利用 Mysql 的 Copy From Prgram 指令为所欲为,比如先备份你的关键文件,然后再删除他们,并要挟你通过指定账户打款。如果执行最小权限原则,那么黑客即便攻破我们的 Mysql 服务,他也只能获得最小的权限。当然,黑客拿到 Mysql 权限也是非常可怕的,但是相比拿到所有权限,这个损失就小多了。 + +分级保护 + +因为内核可以直接操作内存和 CPU,因此非常危险。驱动程序可以直接控制摄像头、显示屏等核心设备,也需要采取安全措施,比如防止恶意应用开启摄像头盗用隐私。通常操作系统都采取一种环状的保护模式。 + + + +如上图所示,内核在最里面,也就是 Ring 0。 应用在最外面也就是Ring 3。驱动在中间,也就是 Ring 1 和 Ring 2。对于相邻的两个 Ring,内层 Ring 会拥有较高的权限,可以改变外层的 Ring;而外层的 Ring 想要使用内层 Ring 的资源时,会有专门的程序(或者硬件)进行保护。 + +比如说一个 Ring3 的应用需要使用内核,就需要发送一个系统调用给内核。这个系统调用会由内核进行验证,比如验证用户有没有足够的权限,以及这个行为是否安全等等。 + +权限包围(Privilege Bracking) + +之前我们讨论过,当 Mysql 跑在 root 权限时,如果 Mysql 被攻破,整个机器就被攻破了。因此我们所有应用都不要跑在 root 上。如果所有应用都跑在普通账户下,那么就会有临时提升权限的场景。比如说安装程序可能需要临时拥有管理员权限,将应用装到/usr/bin目录下。 + +Linux 提供了权限包围的能力。比如一个应用,临时需要高级权限,可以利用交互界面(比如让用户输入 root 账户密码)验证身份,然后执行需要高级权限的操作,然后马上恢复到普通权限工作。这样做可以减少应用在高级权限的时间,并做到专权专用,防止被恶意程序利用。 + +用户分组指令 + +上面我们讨论了 Linux 权限的架构,接下来我们学习一些具体的指令。 + +查看 + +如果想查看当前用户的分组可以使用groups指令。 + + + +上面指令列出当前用户的所有分组。第一个是同名的主要分组,后面从adm开始是次级分组。 + +我先给你介绍两个分组,其他分组你可以去查资料: + + +adm 分组用于系统监控,比如/var/log中的部分日志就是 adm 分组。 +sudo 分组用户可以通过 sudo 指令提升权限。 + + +如果想查看当前用户,可以使用id指令,如下所示: + + + + +uid 是用户 id; +gid 是组 id; +groups 后面是每个分组和分组的 id。 + + +如果想查看所有的用户,可以直接看/etc/passwd。 + + + +/etc/passwd这个文件存储了所有的用户信息,如下图所示: + + + +创建用户 + +创建用户用useradd指令。 + +sudo useradd foo + + +sudo 原意是 superuser do,后来演变成用另一个用户的身份去执行某个指令。如果没有指定需要 sudo 的用户,就像上面那样,就是以超级管理员的身份。因为 useradd 需要管理员身份。这句话执行后,会进行权限提升,并弹出输入管理员密码的输入界面。 + +创建分组 + +创建分组用groupadd指令。下面指令创建一个叫作hello的分组。 + +sudo groupadd hello + + +为用户增加次级分组 + +组分成主要分组(Primary Group)和次级分组(Secondary Group)。主要分组只有 1 个,次级分组可以有多个。如果想为用户添加一个次级分组,可以用usermod指令。下面指令将用户foo添加到sudo分组,从而foo拥有了sudo的权限。 + +sudo usermod -a -G sudo foo + + +-a代表append,-G代表一个次级分组的清单, 最后一个foo是账户名。 + +修改用户主要分组 + +修改主要分组还是使用usermod指令。只不过参数是小写的-g。 + +sudo usermod -g somegroup foo + + +文件权限管理指令 + +接下来我们学习文件管理相关的指令。 + +查看 + +我们可以用ls -l查看文件的权限,相关内容在本课时前面已经介绍过了。 + +修改文件权限 + +可以用chmod修改文件权限,chmod( change file mode bits),也就是我们之前学习的 rwx,只不过 rwx 在 Linux 中是用三个连在一起的二进制位来表示。 + +# 设置foo可以执行 + +chmod +x ./foo + +# 不允许foo执行 + +chmod -x ./foo + +# 也可以同时设置多个权限 + +chmod +rwx ./foo + + +因为rwx在 Linux 中用相邻的 3 个位来表示。比如说111代表rwx,101代表r-x。而rwx总共有三组,分别是用户权限、组权限和全部用户权限。也就是可以用1111111119 个 1 代表rwxrwxrwx。又因为11110 进制是 7,因此当需要一次性设置用户权限、组权限和所有用户权限的时候,我们经常用数字表示。 + +# 设置rwxrwxrwx (111111111 -> 777) + +chmod 777 ./foo + +# 设置rw-rw-rw-(110110110 -> 666) + +chmod 666 ./foo + + +修改文件所属用户 + +有时候我们需要修改文件所属用户,这个时候会使用chown指令。 下面指令修改foo文件所属的用户为bar。 + +chown bar ./foo + + +还有一些情况下,我们需要同时修改文件所属的用户和分组,比如我们想修改foo的分组位g,用户为u,可以使用: + +chown g.u ./foo + + +总结 + +这节课我们学习 Linux 的权限管理的抽象和架构思想。Linux 对用户、组、文件、系统调用等都进行了完善的抽象。之后,我们讨论了最小权限原则。最后我们对用户分组管理和文件权限管理两部分重要的指令进行了系统学习。 + +那么通过这节课的学习,你现在可以来回答本节关联的面试题目:请简述 Linux 权限划分的原则? + +老规矩,请你先在脑海里构思下给面试官的表述,并把你的思考写在留言区,然后再来看我接下来的分析。 + +【解析】 Linux 遵循最小权限原则。 + + +每个用户掌握的权限应该足够小,每个组掌握的权限也足够小。实际生产过程中,最好管理员权限可以拆分,互相牵制防止问题。 +每个应用应当尽可能小的使用权限。最理想的是每个应用单独占用一个容器(比如 Docker),这样就不存在互相影响的问题。即便应用被攻破,也无法攻破 Docker 的保护层。 +尽可能少的root。如果一个用户需要root能力,那么应当进行权限包围——马上提升权限(比如 sudo),处理后马上释放权限。 +系统层面实现权限分级保护,将系统的权限分成一个个 Ring,外层 Ring 调用内层 Ring 时需要内层 Ring 进行权限校验。 + + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/09Linux中的网络指令:如何查看一个域名有哪些NS记录?.md b/专栏/重学操作系统-完/09Linux中的网络指令:如何查看一个域名有哪些NS记录?.md new file mode 100644 index 0000000..a777751 --- /dev/null +++ b/专栏/重学操作系统-完/09Linux中的网络指令:如何查看一个域名有哪些NS记录?.md @@ -0,0 +1,199 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 09 Linux 中的网络指令:如何查看一个域名有哪些 NS 记录? + 我看到过一道关于 Linux 指令的面试题:如何查看一个域名有哪些 NS 记录? + +这类题目是根据一个场景,考察一件具体的事情如何处理。虽然你可以通过查资料找到解决方案,但是,这类问题在面试中还是有必要穿插一下,用于确定求职者技能是否熟练、经验是否丰富。特别是计算机网络相关的指令,平时在远程操作、开发、联调、Debug 线上问题的时候,会经常用到。 + +Linux 中提供了不少网络相关的指令,因为网络指令比较分散,本课时会从下面几个维度给你介绍,帮助你梳理常用的网络指令: + + +远程操作; +查看本地网络状态; +网络测试; +DNS 查询; +HTTP。 + + +这块知识从体系上属于 Linux 指令,同时也关联了很多计算机网络的知识,比如说 TCP/IP 协议、UDP 协议,我会在“模块七”为你简要介绍。 + +如果你对这部分指令背后的网络原理有什么困惑,可以在评论区提问。另外,你也可以关注我将在拉勾教育推出的《计算机网络》课程。下面我们开始学习今天的内容。 + +远程操作指令 + +远程操作指令用得最多的是ssh,ssh指令允许远程登录到目标计算机并进行远程操作和管理。还有一个比较常用的远程指令是scp,scp帮助我们远程传送文件。 + +ssh(Secure Shell) + +有一种场景需要远程登录一个 Linux 系统,这时我们会用到ssh指令。比如你想远程登录一台机器,可以使用ssh user@ip的方式,如下图所示: + + + +上图中,我在使用ssh指令从机器u1登录我的另一台虚拟机u2。这里u1和u2对应着 IP 地址,是我在/etc/hosts中设置的,如下图所示: + + + +/etc/hosts这个文件可以设置 IP 地址对应的域名。我这里是一个小集群,总共有两台机器,因此我设置了方便记忆和操作的名字。 + +scp + +另一种场景是我需要拷贝一个文件到远程,这时可以使用scp指令,如下图,我使用scp指令将本地计算机的一个文件拷贝到了 ubuntu 虚拟机用户的家目录中。 + +比如从u1拷贝家目录下的文件a.txt到u2。家目录有一个简写,就是用~。具体指令见下图: + + + +输入 scp 指令之后会弹出一个提示,要求输入密码,系统验证通过后文件会被成功拷贝。 + +查看本地网络状态 + +如果你想要了解本地的网络状态,比较常用的网络指令是ifconfig和netstat。 + +ifconfig + +当你想知道本地ip以及本地有哪些网络接口时,就可以使用ifconfig指令。你可以把一个网络接口理解成一个网卡,有时候虚拟机会装虚拟网卡,虚拟网卡是用软件模拟的网卡。 + +比如:VMware 为每个虚拟机创造一个虚拟网卡,通过虚拟网卡接入虚拟网络。当然物理机也可以接入虚拟网络,它可以通过虚拟网络向虚拟机的虚拟网卡上发送信息。 + +下图是我的 ubuntu 虚拟机用 ifconfig 查看网络接口信息。 + + + +可以看到我的这台 ubuntu 虚拟机一共有 2 个网卡,ens33 和 lo。lo是本地回路(local lookback),发送给lo就相当于发送给本机。ens33是一块连接着真实网络的虚拟网卡。 + +netstat + +另一个查看网络状态的场景是想看目前本机的网络使用情况,这个时候可以用netstat。 + +默认行为 + +不传任何参数的netstat帮助查询所有的本地 socket,下图是netstat | less的结果。 + + + +如上图,我们看到的是 socket 文件。socket 是网络插槽被抽象成了文件,负责在客户端、服务器之间收发数据。当客户端和服务端发生连接时,客户端和服务端会同时各自生成一个 socket 文件,用于管理这个连接。这里,可以用wc -l数一下有多少个socket。 + + + +你可以看到一共有 615 个 socket 文件,因为有很多 socket 在解决进程间的通信。就是将两个进程一个想象成客户端,一个想象成服务端。并不是真的有 600 多个连接着互联网的请求。 + +查看 TCP 连接 + +如果想看有哪些 TCP 连接,可以使用netstat -t。比如下面我通过netstat -t看tcp协议的网络情况: + + + +这里没有找到连接中的tcp,因为我们这台虚拟机当时没有发生任何的网络连接。因此我们尝试从机器u2(另一台机器)ssh 登录进u1,再看一次: + + + +如上图所示,可以看到有一个 TCP 连接了。 + +查看端口占用 + +还有一种非常常见的情形,我们想知道某个端口是哪个应用在占用。如下图所示: + + + +这里我们看到 22 端口被 sshd,也就是远程登录模块被占用了。-n是将一些特殊的端口号用数字显示,-t是指看 TCP 协议,-l是只显示连接中的连接,-p是显示程序名称。 + +网络测试 + +当我们需要测试网络延迟、测试服务是否可用时,可能会用到ping和telnet指令。 + +ping + +想知道本机到某个网站的网络延迟,就可以使用ping指令。如下图所示: + + + +ping一个网站需要使用 ICMP 协议。因此你可以在上图中看到 icmp 序号。 这里的时间time是往返一次的时间。ttl叫作 time to live,是封包的生存时间。就是说,一个封包从发出就开始倒计时,如果途中超过 128ms,这个包就会被丢弃。如果包被丢弃,就会被算进丢包率。 + +另外ping还可以帮助我们看到一个网址的 IP 地址。 通过网址获得 IP 地址的过程叫作 DNS Lookup(DNS 查询)。ping利用了 DNS 查询,但是没有显示全部的 DNS 查询结果。 + +telnet + +有时候我们想知道本机到某个 IP + 端口的网络是否通畅,也就是想知道对方服务器是否在这个端口上提供了服务。这个时候可以用telnet指令。 如下图所示: + + + +telnet 执行后会进入一个交互式的界面,比如这个时候,我们输入下图中的文字就可以发送 HTTP 请求了。如果你对 HTTP 协议还不太了解,建议自学一下 HTTP 协议。如果希望和林老师一起学习,可以等待下我之后的《计算机网络》专栏。 + + + +如上图所示,第 5 行的GET 和第 6 行的HOST是我输入的。 拉勾网返回了一个 301 永久跳转。这是因为拉勾网尝试把http协议链接重定向到https。 + +DNS 查询 + +我们排查网络故障时想要进行一次 DNS Lookup,想知道一个网址 DNS 的解析过程。这个时候有多个指令可以用。 + +host + +host 就是一个 DNS 查询工具。比如我们查询拉勾网的 DNS,如下图所示: + + + +我们看到拉勾网 www.lagou.com 是一个别名,它的原名是 lgmain 开头的一个域名,这说明拉勾网有可能在用 CDN 分发主页(关于 CDN,我们《计算机网络》专栏见)。 + +上图中,可以找到 3 个域名对应的 IP 地址。 + +如果想追查某种类型的记录,可以使用host -t。比如下图我们追查拉勾的 AAAA 记录,因为拉勾网还没有部署 IPv6,所以没有找到。 + + + +dig + +dig指令也是一个做 DNS 查询的。不过dig指令显示的内容更详细。下图是dig拉勾网的结果。 + + + +从结果可以看到www.lagou.com 有一个别名,用 CNAME 记录定义 lgmain 开头的一个域名,然后有 3 条 A 记录,通常这种情况是为了均衡负载或者分发内容。 + +HTTP 相关 + +最后我们来说说http协议相关的指令。 + +curl + +如果要在命令行请求一个网页,或者请求一个接口,可以用curl指令。curl支持很多种协议,比如 LDAP、SMTP、FTP、HTTP 等。 + +我们可以直接使用 curl 请求一个网址,获取资源,比如我用 curl 直接获取了拉勾网的主页,如下图所示: + + + +如果只想看 HTTP 返回头,可以使用curl -I。 + +另外curl还可以执行 POST 请求,比如下面这个语句: + +curl -d '{"x" : 1}' -H "Content-Type: application/json" -X POST http://localhost:3000/api + + +curl在向localhost:3000发送 POST 请求。-d后面跟着要发送的数据, -X后面是用到的 HTTP 方法,-H是指定自定义的请求头。 + +总结 + +这节课我们学习了不少网络相关的 Linux 指令,这些指令是将来开发和调试的强大工具。这里再给你复习一下这些指令: + + +远程登录的 ssh 指令; +远程拷贝文件的 scp 指令; +查看网络接口的 ifconfig 指令; +查看网络状态的 netstat 指令; +测试网络延迟的 ping 指令; +可以交互式调试和服务端的 telnet 指令; +两个 DNS 查询指令 host 和 dig; +可以发送各种请求包括 HTTPS 的 curl 指令。 + + +那么通过这节课的学习,你现在可以来回答本节关联的面试题目:如何查看一个域名有哪些 NS 记录了吗? + +老规矩,请你先在脑海里构思下给面试官的表述,并把你的思考写在留言区,然后再来看我接下来的分析。 + +【解析】 host 指令提供了一个-t参数指定需要查找的记录类型。我们可以使用host -t ns {网址}。另外 dig 也提供了同样的能力。如果你感兴趣,还可以使用man对系统进行操作。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/10软件的安装:编译安装和包管理器安装有什么优势和劣势?.md b/专栏/重学操作系统-完/10软件的安装:编译安装和包管理器安装有什么优势和劣势?.md new file mode 100644 index 0000000..8ef0a3c --- /dev/null +++ b/专栏/重学操作系统-完/10软件的安装:编译安装和包管理器安装有什么优势和劣势?.md @@ -0,0 +1,249 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 10 软件的安装: 编译安装和包管理器安装有什么优势和劣势? + 今天给你带来的面试题是:编译安装和包管理器安装有什么优势和劣势?为了搞清楚这个问题,就引出了今天的话题,在 Linux 上如何安装程序。 + +在 Linux 上安装程序大概有 2 种思路: + + +直接编译源代码; +使用包管理器。 + + +受开源运动影响,Linux 上很多软件都可以拿到源代码,这也是 Linux 能取得成功的一个重要原因。接下来我们先尝试用包管理器安装应用,然后再用一个实战的例子,教你如何编译安装nginx。 + +包管理器使用 + +Linux 下的应用程序多数以软件包的形式发布,用户拿到对应的包之后,使用包管理器进行安装。说到包管理器,就要提到dpkg和rpm。 + +我们先说说包。 Linux 下两大主流的包就是rpm和dpkg。 + +dpkg(debian package),是linux一个主流的社区分支开发出来的。社区就是开源社区,有很多世界顶级的程序员会在社区贡献代码,比如 github。一般衍生于debian的 Linux 版本都支持dpkg,比如ubuntu。 + +rpm(redhatpackage manager)。在正式讲解之前,我们先来聊聊 RedHat 这家公司。 + +RedHat 是一个做 Linux 的公司,你可以把它理解成一家“保险公司”。 很多公司购买红帽的服务,是为了给自己的业务上一个保险。以防万一哪天公司内部搞不定 Linux 底层,或者底层有 Bug,再或者底层不适合当下的业务发展,需要修改等问题,红帽的工程师都可以帮企业解决。 + +再比如,RedHat 收购了JBoss,把 JBoss 改名为 WildFly。 像 WildFly 这种工具更多是面向企业级,比如没有大量研发团队的企业会更倾向使用成熟的技术。RedHat 公司也有自己的 Linux,就叫作 RedHat。RedHat 系比较重要的 Linux 有 RedHat/Fedora 等。 + +无论是dpkg还是rpm都抽象了自己的包格式,就是以.dpkg或者.rpm结尾的文件。 + +dpkg和rpm也都提供了类似的能力: + + +查询是否已经安装了某个软件包; +查询目前安装了什么软件包; +给定一个软件包,进行安装; +删除一个安装好的软件包。 + + +关于dpkg和rpm的具体用法,你可以用man进行学习。接下来我们聊聊yum和apt。 + +自动依赖管理 + +Linux 是一个开源生态,因此工具非常多。工具在给用户使用之前,需要先打成dpkg或者rpm包。 有的时候一个包会依赖很多其他的包,而dpkg和rpm不会对这种情况进行管理,有时候为了装一个包需要先装十几个依赖的包,过程非常艰辛!因此现在多数情况都在用yum和apt。 + +yum + +你可能会说,我不用yum也不用apt,我只用docker。首先给你一个连击 666,然后我还是要告诉你,如果你做docker镜像,那么还是要用到yum和apt,因此还是有必要学一下。 + +yum的全名是 Yellodog Updator,Modified。 看名字就知道它是基于Yellodog Updator这款软件修改而来的一个工具。yum是 Python 开发的,提供的是rpm包,因此只有redhat系的 Linux,比如 Fedora,Centos 支持yum。yum的主要能力就是帮你解决下载和依赖两个问题。 + +下载之所以是问题,是因为 Linux 生态非常庞大,有时候用户不知道该去哪里下载一款工具。比如用户想安装vim,只需要输入sudo yum install vim就可以安装了。yum的服务器收集了很多linux软件,因此yum会帮助用户找到vim的包。 + +另一方面,yum帮助用户解决了很多依赖,比如用户安装一个软件依赖了 10 个其他的软件,yum会把这 11 个软件一次性的装好。 + +关于yum的具体用法,你可以使用man工具进行学习。 + +apt + +接下来我们来重点说说apt,然后再一起尝试使用。因为我这次是用ubuntuLinux 给你教学,所以我以 apt 为例子,yum 的用法是差不多的,你可以自己 man 一下。 + +apt全名是 Advanced Packaging Tools,是一个debian及其衍生 Linux 系统下的包管理器。由于advanced(先进)是相对于dpkg而言的,因此它也能够提供和yum类似的下载和依赖管理能力。比如在没有vim的机器上,我们可以用下面的指令安装vim。如下图所示: + + + +然后用dpkg指令查看 vim 的状态是ii。第一个i代表期望状态是已安装,第二个i代表实际状态是已安装。 + +下面我们卸载vim,再通过dpkg查看,如下图所示: + + + + + +我们看到 vim 的状态从ii变成了rc,r是期望删除,c是实际上还有配置文件遗留。 如果我们想彻底删除配置文件,可以使用apt purge,就是彻底清除的意思,如下图所示: + + + +再使用dpkg -l时,vim已经清除了。 + + + +期待结果是u就是 unkonw(未知)说明已经没有了。实际结果是n,就是 not-installed(未安装)。 + +如果想查询mysql相关的包,可以使用apt serach mysql,这样会看到很多和mysql相关的包,如下图所示: + + + +如果我们想精确查找一个叫作mysql-server的包,可以用apt list。 + + + +这里我们找到了mysql-server包。 + +另外有时候国内的apt服务器速度比较慢,你可以尝试使用阿里云的镜像服务器。具体可参考我下面的操作: + +cat /etc/apt/sources.list + +--以下是文件内容-- + +deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse + +deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse + +deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse + +deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse + +deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse + +deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse + +deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse + +deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse + +deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse + +deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse + + +镜像地址可以通过/etc/apt/sources.list配置,注意focal是我用的ubuntu版本,你可以使用sudo lsb_release查看自己的 Ubuntu 版本。如果你想用我上面给出的内容覆盖你的sources.list,只需把版本号改成你自己的。注意,每个ubuntu版本都有自己的代号。 + + + +通过上面的学习,相信你已经逐渐了解了包管理器的基本概念和使用。如果你是centos或者fedora,需要自己man一下yum。 + +编译安装 Nginx + +接下来我们说说编译安装 Nginx(发音是 engine X),是一个家喻户晓的 Web 服务器。 它的发明者是俄国的伊戈尔·赛索耶夫。赛索耶夫 2002 年开始写 Nginx,主要目的是解决同一个互联网节点同时进入大量并发请求的问题。注意,大量并发请求不是大量 QPS 的意思,QPS 是吞吐量大,需要快速响应,而高并发时则需要合理安排任务调度。 + +后来塞索耶夫成立了 Nginx 公司, 2018 年估值到达到 4.3 亿美金。现在基本上国内大厂的 Web 服务器都是基于 Nginx,只不过进行了特殊的修改,比如淘宝用 Tengine。 + +下面我们再来看看源码安装,在 Linux 上获取nginx源码,可以去搜索 Nginx 官方网站,一般都会提供源码包。 + + + +如上图所示,可以看到 nginx-1.18.0 的网址是:http://nginx.org/download/nginx-1.19.2.tar.gz。然后我们用 wget 去下载这个包。 wget 是 GNU 项目下的下载工具,GNU 是早期unix项目的一个变种。linux下很多工具都是从unix继承来的,这就是开源的好处,很多工具不用再次开发了。你可能很难想象windows下的命令工具可以在linux下用,但是linux下的工具却可以在任何系统中用。 因此,linux下面的工具发展速度很快,如今已成为最受欢迎的服务器操作系统。 + +当然也有同学的机器上没有wget,那么你可以用apt安装一下。 + + +第一步:下载源码。我们使用wget下载nginx源码包: + + + + +可以像我这样使用cd先切换到家目录。 + + +第二步:解压。我们解压下载好的nginx源码包。 + + + + +用ls发现包已经存在了,然后使用tar命令解压。 + +tar是用来打包和解压用的。之所以叫作tar是有一些历史原因:t代表tape(磁带);ar是 archive(档案)。因为早期的存储介质很小,人们习惯把文件打包然后存储到磁带上,那时候unix用的命令就是tar。因为linux是个开源生态,所以就沿袭下来继续使用tar。 + +-x代表 extract(提取)。-z代表gzip,也就是解压gz类型的文件。-v代表 verbose(显示细节),如果你不输入-v,就不会打印解压过程了。-f代表 file,这里指的是要操作文件,而不是磁带。 所以tar解压通常带有x和f,打包通常是c就是 create 的意思。 + + +第三步:配置和解决依赖。解压完,我们进入nginx的目录看一看。 如下图所示: + + + + +可以看到一个叫作configure的文件是绿色的,也就是可执行文件。然后我们执行 configure 文件进行配置,这个配置文件来自一款叫作autoconf的工具,也是 GNU 项目下的,说白了就是bash(Bourne Shell)下的安装打包工具(就是个安装程序)。这个安装程序支持很多配置,你可以用./configure --help看到所有的配置项,如下图所示: + + + +这里有几个非常重要的配置项,叫作prefix。prefix配置项决定了软件的安装目录。如果不配置这个配置项,就会使用默认的安装目录。sbin-path决定了nginx的可执行文件的位置。conf-path决定了nginx配置文件的位置。我们都使用默认,然后执行./configure,如下图所示: + + + +autoconf进行依赖检查的时候,报了一个错误,cc 没有找到。这是因为机器上没有安装gcc工具,gcc 是家喻户晓的工具套件,全名是 GNU Compiler Collection——里面涵盖了包括 c/c++ 在内的多门语言的编译器。 + +我们用包管理器,安装gcc,如下图所示。安装gcc通常是安装build-essential这个包。 + + + +安装完成之后,再执行./configure,如下图所示: + + + +我们看到配置程序开始执行。但是最终报了一个错误,如下图所示: + + + +报错的内容是,nginx的HTTP rewrite模块,需要PCRE库。 PCRE 是perl语言的兼容正则表达式库。perl语言一直以支持原生正则表达式,而受到广大编程爱好者的喜爱。我曾经看到过一个 IBM 的朋友用perl加上wget就实现了一个简单的爬虫。接下来,我们开始安装PCRE。 + +一般这种依赖库,会叫pcre-dev或者libpcre。用apt查询了一下,然后grep。 + + + +我们看到有pcre2也有pcre3。这个时候可以考虑试试pcre3。 + + + +安装完成之后再试试./configure,提示还需要zlib。然后我们用类似的方法解决zlib依赖。 + + + +zlib包的名字叫zlib1g不太好找,需要查资料才能确定是这个名字。 + +我们再尝试配置,终于配置成功了。 + + + + +第四步:编译和安装。 + + +通常配置完之后,我们输入make && sudo make install进行编译和安装。make是linux下面一个强大的构建工具。autoconf也就是./configure会在当前目录下生成一个 MakeFile 文件。make会根据MakeFile文件编译整个项目。编译完成后,能够形成和当前操作系统以及 CPU 指令集兼容的二进制可执行文件。然后再用make install安装。&&符号代表执行完make再去执行make installl。 + + + +你可以看到编译是个非常慢的活。等待了差不多 1 分钟,终于结束了。nginx被安装到了/usr/local/nginx中,如果需要让nginx全局执行,可以设置一个软连接到/usr/local/bin,具体如下: + +ln -sf /usr/local/nginx/sbin/nginx /usr/local/sbin/nginx + + +为什么会有编译安装? + +学完整个编译安装 Ngnix 过程后,你可能会问,为什么会有编译安装这么复杂的事情。 + +原来使用 C/C++ 写的程序存在一个交叉编译的问题。就是写一次程序,在很多个平台执行。而不同指令集的 CPU 指令,还有操作系统的可执行文件格式是不同的。因此,这里有非常多的现实问题需要解决。一般是由操作系统的提供方,比如 RedHat 来牵头解决这些问题。你可以用apt等工具提供给用户已经编译好的包。apt会自动根据用户的平台类型选择不同的包。 + +但如果某个包没有在平台侧注册,也没有提供某个 Linux 平台的软件包,我们就需要回退到编译安装,通过源代码直接在某个平台安装。 + +总结 + +这节课我们学习了在 Linux 上安装软件,简要介绍了dpkg和rpm,然后介绍了能够解决依赖和帮助用户下载的yum和apt。重点带你使用了apt,在这个过程中看到了强大的包管理机制,今天的maven、npm、pip都继承了这样一个特性。最后我们还尝试了一件高难度的事情,就是编译安装nginx。 + +那么通过这节课的学习,你现在可以来回答本节关联的面试题目:编译安装和包管理安装有什么优势和劣势了吗? + +老规矩,请你先在脑海里构思下给面试官的表述,并把你的思考写在留言区,然后再来看我接下来的分析。 + +【解析】 包管理安装很方便,但是有两点劣势。 + +第一点是需要提前将包编译好,因此有一个发布的过程,如果某个包没有发布版本,或者在某个平台上找不到对应的发布版本,就需要编译安装。 + +第二点就是如果一个软件的定制程度很高,可能会在编译阶段传入参数,比如利用configure传入配置参数,这种时候就需要编译安装。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/11高级技巧之日志分析:利用Linux指令分析Web日志.md b/专栏/重学操作系统-完/11高级技巧之日志分析:利用Linux指令分析Web日志.md new file mode 100644 index 0000000..06f280b --- /dev/null +++ b/专栏/重学操作系统-完/11高级技巧之日志分析:利用Linux指令分析Web日志.md @@ -0,0 +1,124 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 11 高级技巧之日志分析:利用 Linux 指令分析 Web 日志 + 著名的黑客、自由软件运动的先驱理查德.斯托曼说过,“编程不是科学,编程是手艺”。可见,要想真正搞好编程,除了学习理论知识,还需要在实际的工作场景中进行反复的锤炼。 + +所以今天我们将结合实际的工作场景,带你利用 Linux 指令分析 Web 日志,这其中包含很多小技巧,掌握了本课时的内容,将对你将来分析线上日志、了解用户行为和查找问题有非常大地帮助。 + +本课时将用到一个大概有 5W 多条记录的nginx日志文件,你可以在 GitHub上下载。 下面就请你和我一起,通过分析这个nginx日志文件,去锤炼我们的手艺。 + +第一步:能不能这样做? + +当我们想要分析一个线上文件的时候,首先要思考,能不能这样做? 这里你可以先用htop指令看一下当前的负载。如果你的机器上没有htop,可以考虑用yum或者apt去安装。 + + + +如上图所示,我的机器上 8 个 CPU 都是 0 负载,2G的内存用了一半多,还有富余。 我们用wget将目标文件下载到本地(如果你没有 wget,可以用yum或者apt安装)。 + +wget 某网址(自己替代) + + +然后我们用ls查看文件大小。发现这只是一个 7M 的文件,因此对线上的影响可以忽略不计。如果文件太大,建议你用scp指令将文件拷贝到闲置服务器再分析。下图中我使用了--block-size让ls以M为单位显示文件大小。 + + + +确定了当前机器的CPU和内存允许我进行分析后,我们就可以开始第二步操作了。 + +第二步:LESS 日志文件 + +在分析日志前,给你提个醒,记得要less一下,看看日志里面的内容。之前我们说过,尽量使用less这种不需要读取全部文件的指令,因为在线上执行cat是一件非常危险的事情,这可能导致线上服务器资源不足。 + + + +如上图所示,我们看到nginx的access_log每一行都是一次用户的访问,从左到右依次是: + + +IP 地址; +时间; +HTTP 请求的方法、路径和协议版本、返回的状态码; +User Agent。 + + +第三步:PV 分析 + +PV(Page View),用户每访问一个页面就是一次Page View。对于nginx的acess_log来说,分析 PV 非常简单,我们直接使用wc -l就可以看到整体的PV。 + + + +如上图所示:我们看到了一共有 51462 条 PV。 + +第四步:PV 分组 + +通常一个日志中可能有几天的 PV,为了得到更加直观的数据,有时候需要按天进行分组。为了简化这个问题,我们先来看看日志中都有哪些天的日志。 + +使用awk '{print $4}' access.log | less可以看到如下结果。awk是一个处理文本的领域专有语言。这里就牵扯到领域专有语言这个概念,英文是Domain Specific Language。领域专有语言,就是为了处理某个领域专门设计的语言。比如awk是用来分析处理文本的DSL,html是专门用来描述网页的DSL,SQL是专门用来查询数据的DSL……大家还可以根据自己的业务设计某种针对业务的DSL。 + +你可以看到我们用$4代表文本的第 4 列,也就是时间所在的这一列,如下图所示: + + + +我们想要按天统计,可以利用 awk提供的字符串截取的能力。 + + + +上图中,我们使用awk的substr函数,数字2代表从第 2 个字符开始,数字11代表截取 11 个字符。 + +接下来我们就可以分组统计每天的日志条数了。 + + + +上图中,使用sort进行排序,然后使用uniq -c进行统计。你可以看到从 2015 年 5 月 17 号一直到 6 月 4 号的日志,还可以看到每天的 PV 量大概是在 2000~3000 之间。 + +第五步:分析 UV + +接下来我们分析 UV。UV(Uniq Visitor),也就是统计访问人数。通常确定用户的身份是一个复杂的事情,但是我们可以用 IP 访问来近似统计 UV。 + + + +上图中,我们使用 awk 去打印$1也就是第一列,接着sort排序,然后用uniq去重,最后用wc -l查看条数。 这样我们就知道日志文件中一共有2660个 IP,也就是2660个 UV。 + +第六步:分组分析 UV + +接下来我们尝试按天分组分析每天的 UV 情况。这个情况比较复杂,需要较多的指令,我们先创建一个叫作sum.sh的bash脚本文件,写入如下内容: + +#!/usr/bin/bash + +awk '{print substr($4, 2, 11) " " $1}' access.log |\ + + sort | uniq |\ + + awk '{uv[$1]++;next}END{for (ip in uv) print ip, uv[ip]}' + + +具体分析如下。 + + +文件首部我们使用#!,表示我们将使用后面的/usr/bin/bash执行这个文件。 +第一次awk我们将第 4 列的日期和第 1 列的ip地址拼接在一起。 +下面的sort是把整个文件进行一次字典序排序,相当于先根据日期排序,再根据 IP 排序。 +接下来我们用uniq去重,日期 +IP 相同的行就只保留一个。 +最后的awk我们再根据第 1 列的时间和第 2 列的 IP 进行统计。 + + +为了理解最后这一行描述,我们先来简单了解下awk的原理。 + +awk本身是逐行进行处理的。因此我们的next关键字是提醒awk跳转到下一行输入。 对每一行输入,awk会根据第 1 列的字符串(也就是日期)进行累加。之后的END关键字代表一个触发器,就是 END 后面用 {} 括起来的语句会在所有输入都处理完之后执行——当所有输入都执行完,结果被累加到uv中后,通过foreach遍历uv中所有的key,去打印ip和ip对应的数量。 + +编写完上面的脚本之后,我们保存退出编辑器。接着执行chmod +x ./sum.sh,给sum.sh增加执行权限。然后我们可以像下图这样执行,获得结果: + + + +如上图,IP地址已经按天进行统计好了。 + +总结 + +今天我们结合一个简单的实战场景——Web 日志分析与统计练习了之前学过的指令,提高熟练程度。此外,我们还一起学习了新知识——功能强大的awk文本处理语言。在实战中,我们对一个nginx的access_log进行了简单的数据分析,直观地获得了这个网站的访问情况。 + +我们在日常的工作中会遇到各种各样的日志,除了 nginx 的日志,还有应用日志、前端日志、监控日志等等。你都可以利用今天学习的方法,去做数据分析,然后从中得出结论。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/12(1)加餐练习题详解(二).md b/专栏/重学操作系统-完/12(1)加餐练习题详解(二).md new file mode 100644 index 0000000..cc18964 --- /dev/null +++ b/专栏/重学操作系统-完/12(1)加餐练习题详解(二).md @@ -0,0 +1,157 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 (1)加餐 练习题详解(二) + 今天我会带你把《模块二:Linux 指令》中涉及的课后练习题,逐一讲解,并给出每个课时练习题的解题思路和答案。 + +练习题详解 + +06 | 目录结构和文件管理指令:rm / -rf 指令的作用是? + +【问题】 搜索文件系统中所有以包含 std字符串且以.h扩展名结尾的文件。 + +【解析】 这道题目比较简单,大家也比较活跃,我自己只写了一种方法,没想到留言中有挺多不错的方案,那我们一起来看下。 + +下面是我的方案,你学完模块二的内容后,应该知道查看全部文件需要sudo,以管理员身份: + +sudo find / -name "*std*.h" + + +我在留言中看到有的同学用的是-iname,这样也是可以的,只是忽略了大小写。 + +也可以结合 grep 语句, 用管道实现,比如: + +sudo find / -name "*.h" |grep std + + +07 | 进程、重定向和管道指令:xargs 指令的作用是? + +【问题】 请问下面这段 Shell 程序的作用是什么? + +mkfifo pipe1 + +mkfifo pipe2 + +echo -n run | cat - pipe1 > pipe2 & + +cat < pipe2 > pipe1 + + +【解析】 这个题目是我在网上看到的一个比较有趣的问题。 + +前 2 行代码创建了两个管道文件。 + +从第 3 行开始,代码变得复杂。echo -n run就是向输出流中写入一个run字符串(不带回车,所以用-n)。通过管道,将这个结果传递给了cat。cat是 concatenate 的缩写,意思是把文件粘在一起。 + + +当cat用>重定向输出到一个管道文件时,如果没有其他进程从管道文件中读取内容,cat会阻塞。 +当cat用<读取一个管道内容时,如果管道中没有输入,也会阻塞。 + + +从这个角度来看,总共有 3 次重定向: + + +将-也就是输入流的内容和pipe1内容合并重定向到pipe2; +将pipe2内容重定向到cat; +将cat的内容重定向到pipe1。 + + +仔细观察下路径:pipe1->pipe2->pipe1,构成了一个循环。 这样导致管道pipe1管道pipe2中总是有数据(没有数据的时间太短)。于是,就构成了一个无限循环。我们打开执行这个程序后,可以用htop查看当前的 CPU 使用情况,会发现 CPU 占用率很高。 + +08 | 用户和权限管理指令: 请简述 Linux 权限划分的原则? + +【问题】 如果一个目录是只读权限,那么这个目录下面的文件还可写吗? + +【解析】 这类问题,你一定要去尝试,观察现象再得到结果。 + + + +你可以看到上图中,foo 目录不可读了,下面的foo/bar文件还可以写。 即便它不可写了,下面的foo/bar文件还是可以写。 + + + +但是想要创建新文件就会出现报错,因为创建新文件也需要改目录文件。这个例子说明 Linux 中的文件内容并没有存在目录中,目录中却有文件清单。 + +09 | Linux 中的网络指令:如何查看一个域名有哪些 NS 记录? + +【问题】 如何查看正在 TIME_WAIT 状态的连接数量? + +【解析】 注意,这里有个小坑,就是 netstat 会有两行表头,这两行可以用 tail 过滤掉,下面tail -n +3就是告诉你 tail 从第 3 行开始显示。-a代表显示所有的 socket。 + +netstat -a | tail -n +3 | wc -l + + +10 | 软件的安装: 编译安装和包管理器安装有什么优势和劣势? + +【问题】 如果你在编译安装 MySQL 时,发现找不到libcrypt.so ,应该如何处理? + +【解析】 遇到这类问题,首先应该去查资料。 比如查 StackOverflow,搜索关键词:libcrypt.so not found,或者带上自己的操作系统ubuntu。下图是关于 Stackoverflow 的一个解答: + + + +在这里我再多说两句,程序员成长最需要的是学习时间,如果在这前面加一个形容词,那就是大量的学习时间;而程序员最需要掌握的技能就是搜索和学习知识的能力。如果你看到今天的这篇内容,说明已经学完了《重学操作系统》专栏两个模块的知识,希望你可以坚持下去! + +11 | 高级技巧之日志分析:利用 Linux 指令分析 Web 日志 + +【问题 1 】 根据今天的 access_log 分析出有哪些终端访问了这个网站,并给出分组统计结果。 + +【解析】access_log中有Debian和Ubuntu等等。我们可以利用下面的指令看到,第 12 列是终端,如下图所示: + + + +我们还可以使用sort和uniq查看有哪些终端,如下图所示: + + + +最后需要写一个脚本,进行统计: + +cat nginx_logs.txt |\ + +awk '{tms[$12]++;next}END{for (t in tms) print t, tms[t]}' + + +结果如下: + + + +【问题 2】 根据今天的 access_log 分析出访问量 Top 前三的网页。 + +如果不需要 Substring 等复杂的处理,也可以使用sort和uniq的组合。如下图所示: + + + +12 | 高级技巧之集群部署:利用 Linux 指令同时在多台机器部署程序 + +【问题】~/.bashrc ~/.bash_profile, ~/.profile 和 /etc/profile 的区别是什么? + +【解析】 执行一个 shell 的时候分成login shell和non-login shell。顾名思义我们使用了sudosu切换到某个用户身份执行 shell,也就是login shell。还有 ssh 远程执行指令也是 login shell,也就是伴随登录的意思——login shell 会触发很多文件执行,路径如下: + + + +如果以当前用户身份正常执行一个 shell,比如说./a.sh,就是一个non-login的模式。 这时候不会触发上述的完整逻辑。 + +另外shell还有另一种分法,就是interactive和non-interactive。interactive 是交互式的意思,当用户打开一个终端命令行工具后,会进入一个输入命令得到结果的交互界面,这个时候,就是interactive shell。 + +baserc文件通常只在interactive模式下才会执行,这是因为~/.bashrc文件中通常有这样的语句,如下图所示: + + + +这个语句通过$-看到当前shell的执行环境,如下图所示: + + + +带 i 字符的就是interactive,没有带i字符就不是。 + +因此, 如果你需要通过 ssh 远程 shell 执行一个文件,你就不是在 interactive 模式下,bashrc 不会触发。但是因为登录的原因,login shell 都会触发,也就是说 profile 文件依然会执行。 + +总结 + +这个模块我们学习了 Linux 指令。我带大家入了个门,也和你一起感受了一次 Linux 指令的博大精深。Linux 虽然没有上下五千年的历史,但每次使用,依然让我感受到了它浓郁的历史气息,悠久的文化传承,自由的创造精神。希望这块知识可以陪伴大家,鼓励你成为优秀的程序员。虽然我们已经学了几十个指令,但还是沧海一粟。后续就需要你多查资料,多用man手册,继续深造了。 + +好的,Linux 指令部分就告一段落。下一节课,我们将开启操作系统核心知识学习,请和我一起来学习“模块三:操作系统基础知识”吧。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/12高级技巧之集群部署:利用Linux指令同时在多台机器部署程序.md b/专栏/重学操作系统-完/12高级技巧之集群部署:利用Linux指令同时在多台机器部署程序.md new file mode 100644 index 0000000..28ef359 --- /dev/null +++ b/专栏/重学操作系统-完/12高级技巧之集群部署:利用Linux指令同时在多台机器部署程序.md @@ -0,0 +1,308 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 12 高级技巧之集群部署:利用 Linux 指令同时在多台机器部署程序 + Linux 指令是由很多顶级程序员共同设计的,使用 Linux 指令解决问题的过程,就好像在体验一款优秀的产品。每次通过查资料使用 Linux 指令解决问题后,都会让我感到收获满满。在这个过程中,我不仅学会了一条指令,还从中体会到了软件设计的魅力:彼此独立,又互成一体。这就像每个 Linux 指令一样,专注、高效。回想起来,在我第一次看到管道、第一次使用 awk、第一次使用 sort,都曾有过这种感受。 + +通过前面的学习,相信你已经掌握了一些基础指令的使用方法,今天我们继续挑战一个更复杂的问题——用 Linux 指令管理一个集群。这属于 Linux 指令的高级技巧,所谓高级技巧并不是我们要学习更多的指令,而是要把之前所学的指令进行排列组合。当你从最初只能写几条指令、执行然后看结果,成长到具备书写一个拥有几十行、甚至上百行的 bash 脚本的能力时,就意味着你具备了解决复杂问题的能力。而最终的目标,是提升你对指令的熟练程度,锻炼工程能力。 + +本课时,我将带你朝着这个目标努力,通过把简单的指令组合起来,分层组织成最终的多个脚本文件,解决一个复杂的工程问题:在成百上千的集群中安装一个 Java 环境。接下来,请你带着这个目标,开启今天的学习。 + +第一步:搭建学习用的集群 + +第一步我们先搭建一个学习用的集群。这里简化一下模型。我在自己的电脑上装一个ubuntu桌面版的虚拟机,然后再装两个ubuntu服务器版的虚拟机。 + +相对于桌面版,服务器版对资源的消耗会少很多。我将教学材料中桌面版的ubuntu命名为u1,两个用来被管理的服务器版ubuntu叫作v1和v2。 + +用桌面版的原因是:我喜欢ubuntu漂亮的开源字体,这样会让我在给你准备素材的时候拥有一个好心情。如果你对此感兴趣,可以搜索ubuntu mono,尝试把这个字体安装到自己的文本编辑器中。不过我还是觉得在ubuntu中敲代码更有感觉。 + +注意,我在这里只用了 3 台服务器,但是接下来我们要写的脚本是可以在很多台服务器之间复用的。 + +第二步:循环遍历 IP 列表 + +你可以想象一个局域网中有很多服务器需要管理,它们彼此之间网络互通,我们通过一台主服务器对它们进行操作,即通过u1操作v1和v2。 + +在主服务器上我们维护一个ip地址的列表,保存成一个文件,如下图所示: + + + +目前iplist中只有两项,但是如果我们有足够的机器,可以在里面放成百上千项。接下来,请你思考shell如何遍历这些ip? + +你可以先尝试实现一个最简单的程序,从文件iplist中读出这些ip并尝试用for循环遍历这些ip,具体程序如下: + +#!/usr/bin/bash + +readarray -t ips < iplist + +for ip in ${ips[@]} + +do + + echo $ip + +done + + +首行的#!叫作 Shebang。Linux 的程序加载器会分析 Shebang 的内容,决定执行脚本的程序。这里我们希望用bash来执行这段程序,因为我们用到的 readarray 指令是bash 4.0后才增加的能力。 + +readarray指令将 iplist 文件中的每一行读取到变量ips中。ips是一个数组,可以用echo ${ips[@]}打印其中全部的内容:@代表取数组中的全部内容;$符号是一个求值符号。不带$的话,ips[@]会被认为是一个字符串,而不是表达式。 + +for循环遍历数组中的每个ip地址,echo把地址打印到屏幕上。 + +如果用shell执行上面的程序会报错,因为readarray是bash 4.0后支持的能力,因此我们用chomd为foreach.sh增加执行权限,然后直接利用shebang的能力用bash执行,如下图所示: + + + +第三步:创建集群管理账户 + +为了方便集群管理,通常使用统一的用户名管理集群。这个账号在所有的集群中都需要保持命名一致。比如这个集群账号的名字就叫作lagou。 + +接下来我们探索一下如何创建这个账户lagou,如下图所示: + + + +上面我们创建了lagou账号,然后把lagou加入sudo分组。这样lagou就有了sudo成为root的能力,如下图所示: + + + +接下来,我们设置lagou用户的初始化shell是bash,如下图所示: + + + +这个时候如果使用命令su lagou,可以切换到lagou账号,但是你会发现命令行没有了颜色。因此我们可以将原来用户下面的.bashrc文件拷贝到/home/lagou目录下,如下图所示: + + + +这样,我们就把一些自己平时用的设置拷贝了过去,包括终端颜色的设置。.bashrc是启动bash的时候会默认执行的一个脚本文件。 + +接下来,我们编辑一下/etc/sudoers文件,增加一行lagou ALL=(ALL) NOPASSWD:ALL表示lagou账号 sudo 时可以免去密码输入环节,如下图所示: + + + +我们可以把上面的完整过程整理成指令文件,create_lagou.sh: + +sudo useradd -m -d /home/lagou lagou + +sudo passwd lagou + +sudo usermod -G sudo lagou + +sudo usermod --shell /bin/bash lagou + +sudo cp ~/.bashrc /home/lagou/ + +sudo chown lagou.lagou /home/lagou/.bashrc + +sduo sh -c 'echo "lagou ALL=(ALL) NOPASSWD:ALL">>/etc/sudoers' + + +你可以删除用户lagou,并清理/etc/sudoers文件最后一行。用指令userdel lagou删除账户,然后执行create_lagou.sh重新创建回lagou账户。如果发现结果一致,就代表create_lagou.sh功能没有问题。 + +最后我们想在v1v2上都执行create_logou.sh这个脚本。但是你不要忘记,我们的目标是让程序在成百上千台机器上传播,因此还需要一个脚本将create_lagou.sh拷贝到需要执行的机器上去。 + +这里,可以对foreach.sh稍做修改,然后分发create_lagou.sh文件。 + +foreach.sh + +#!/usr/bin/bash + +readarray -t ips < iplist + +for ip in ${ips[@]} + +do + + scp ~/remote/create_lagou.sh ramroll@$ip:~/create_lagou.sh + +done + + +这里,我们在循环中用scp进行文件拷贝,然后分别去每台机器上执行create_lagou.sh。 + +如果你的机器非常多,上述过程会变得非常烦琐。你可以先带着这个问题学习下面的Step 4,然后再返回来重新思考这个问题,当然你也可以远程执行脚本。另外,还有一个叫作sshpass的工具,可以帮你把密码传递给要远程执行的指令,如果你对这块内容感兴趣,可以自己研究下这个工具。 + +第四步: 打通集群权限 + +接下来我们需要打通从主服务器到v1和v2的权限。当然也可以每次都用ssh输入用户名密码的方式登录,但这并不是长久之计。 如果我们有成百上千台服务器,输入用户名密码就成为一件繁重的工作。 + +这时候,你可以考虑利用主服务器的公钥在各个服务器间登录,避免输入密码。接下来我们聊聊具体的操作步骤: + +首先,需要在u1上用ssh-keygen生成一个公私钥对,然后把公钥写入需要管理的每一台机器的authorized_keys文件中。如下图所示:我们使用ssh-keygen在主服务器u1中生成公私钥对。 + + + +然后使用mkdir -p创建~/.ssh目录,-p的优势是当目录不存在时,才需要创建,且不会报错。~代表当前家目录。 如果文件和目录名前面带有一个.,就代表该文件或目录是一个需要隐藏的文件。平时用ls的时候,并不会查看到该文件,通常这种文件拥有特别的含义,比如~/.ssh目录下是对ssh的配置。 + +我们用cd切换到.ssh目录,然后执行ssh-keygen。这样会在~/.ssh目录中生成两个文件,id_rsa.pub公钥文件和is_rsa私钥文件。 如下图所示: + + + +可以看到id_rsa.pub文件中是加密的字符串,我们可以把这些字符串拷贝到其他机器对应用户的~/.ssh/authorized_keys文件中,当ssh登录其他机器的时候,就不用重新输入密码了。 这个传播公钥的能力,可以用一个shell脚本执行,这里我用transfer_key.sh实现。 + +我们修改一下foreach.sh,并写一个transfer_key.sh配合foreach.sh的工作。transfer_key.sh内容如下: + +foreach.sh + +#!/usr/bin/bash + +readarray -t ips < iplist + +for ip in ${ips[@]} + +do + + sh ./transfer_key.sh $ip + +done + + +tranfer_key.sh + +ip=$1 + +pubkey=$(cat ~/.ssh/id_rsa.pub) + +echo "execute on .. $ip" + +ssh lagou@$ip " + +mkdir -p ~/.ssh + +echo $pubkey >> ~/.ssh/authorized_keys + +chmod 700 ~/.ssh + +chmod 600 ~/.ssh/authorized_keys + +" + + +在foreach.sh中我们执行 transfer_key.sh,并且将 IP 地址通过参数传递过去。在 transfer_key.sh 中,用$1读出 IP 地址参数, 再将公钥写入变量pubkey,然后登录到对应的服务器,执行多行指令。用mkdir指令检查.ssh目录,如不存在就创建这个目录。最后我们将公钥追加写入目标机器的~/.ssh/authorized_keys中。 + +chmod 700和chmod 600是因为某些特定的linux版本需要.ssh的目录为可读写执行,authorized_keys文件的权限为只可读写。而为了保证安全性,组用户、所有用户都不可以访问这个文件。 + +此前,我们执行foreach.sh需要输入两次密码。完成上述操作后,我们再登录这两台服务器就不需要输入密码了。 + + + +接下来,我们尝试一下免密登录,如下图所示: + + + +可以发现,我们登录任何一台机器,都不再需要输入用户名和密码了。 + +第五步:单机安装 Java 环境 + +在远程部署 Java 环境之前,我们先单机完成以下 Java 环境的安装,用来收集需要执行的脚本。 + +在ubuntu上安装java环境可以直接用apt。 + +我们通过下面几个步骤脚本配置 Java 环境: + +sudo apt install openjdk-11-jdk + + +经过一番等待我们已经安装好了java,然后执行下面的脚本确认java安装。 + +which java + +java --version + + + + +根据最小权限原则,执行 Java 程序我们考虑再创建一个用户ujava。 + +sudo useradd -m -d /opt/ujava ujava + +sudo usermod --shell /bin/bash lagou + + +这个用户可以不设置密码,因为我们不会真的登录到这个用户下去做任何事情。接下来我们为用户配置 Java 环境变量,如下图所示: + + + +通过两次 ls 追查,可以发现java可执行文件软连接到/etc/alternatives/java然后再次软连接到/usr/lib/jvm/java-11-openjdk-amd64下。 + +这样我们就可以通过下面的语句设置 JAVA_HOME 环境变量了。 + +export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/ + + +Linux 的环境变量就好比全局可见的数据,这里我们使用 export 设置JAVA_HOME环境变量的指向。如果你想看所有的环境变量的指向,可以使用env指令。 + + + +其中有一个环境变量比较重要,就是PATH。 + + + +如上图,我们可以使用shell查看PATH的值,PATH中用:分割,每一个目录都是linux查找执行文件的目录。当用户在命令行输入一个命令,Linux 就会在PATH中寻找对应的执行文件。 + +当然我们不希望JAVA_HOME配置后重启一次电脑就消失,因此可以把这个环境变量加入ujava用户的profile中。这样只要发生用户登录,就有这个环境变量。 + +sudo sh -c 'echo "export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/" >> /opt/ujava/.bash_profile' + + +将JAVA_HOME加入bash_profile,这样后续远程执行java指令时就可以使用JAVA_HOME环境变量了。 + +最后,我们将上面所有的指令整理起来,形成一个install_java.sh。 + +sudo apt -y install openjdk-11-jdk + +sudo useradd -m -d /opt/ujava ujava + +sudo usermod --shell /bin/bash ujava + +sudo sh -c 'echo "export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/" >> /opt/ujava/.bash_profile' + + +apt后面增了一个-y是为了让执行过程不弹出确认提示。 + +第六步:远程安装 Java 环境 + +终于到了远程安装 Java 环境这一步,我们又需要用到foreach.sh。为了避免每次修改,你可以考虑允许foreach.sh带一个文件参数,指定需要远程执行的脚本。 + +foreach.sh + +#!/usr/bin/bash + +readarray -t ips < iplist + +script=$1 + +for ip in ${ips[@]} + +do + + ssh $ip 'bash -s' < $script + +done + + +改写后的foreach会读取第一个执行参数作为远程执行的脚本文件。 而bash -s会提示使用标准输入流作为命令的输入;< $script负责将脚本文件内容重定向到远程bash的标准输入流。 + +然后我们执行foreach.sh install_java.sh,机器等待 1 分钟左右,在执行结束后,可以用下面这个脚本检测两个机器中的安装情况。 + +check.sh + +sudo -u ujava -i /bin/bash -c 'echo $JAVA_HOME' + +sudo -u ujava -i java --version + + +check.sh中我们切换到ujava用户去检查JAVA_HOME环境变量和 Java 版本。执行的结果如下图所示: + + + +总结 + +这节课我们所讲的场景是自动化运维的一些皮毛。通过这样的场景练习,我们复习了很多之前学过的 Linux 指令。在尝试用脚本文件构建一个又一个小工具的过程中,可以发现复用很重要。 + +在工作中,优秀的工程师,总是善于积累和复用,而shell脚本就是积累和复用的利器。如果你第一次安装java环境,可以把今天的安装脚本保存在自己的笔记本中,下次再安装就能自动化完成了。除了积累和总结,另一个非常重要的就是你要尝试自己去查资料,包括使用man工具熟悉各种指令的使用方法,用搜索引擎查阅资料等。 + + + + \ No newline at end of file diff --git a/专栏/重学操作系统-完/13操作系统内核:Linux内核和Windows内核有什么区别?.md b/专栏/重学操作系统-完/13操作系统内核:Linux内核和Windows内核有什么区别?.md new file mode 100644 index 0000000..91b2ce3 --- /dev/null +++ b/专栏/重学操作系统-完/13操作系统内核:Linux内核和Windows内核有什么区别?.md @@ -0,0 +1,141 @@ + + + 因收到Google相关通知,网站将会择期关闭。相关通知内容 + + + 13 操作系统内核:Linux 内核和 Windows 内核有什么区别? + Windows 和 Linux 是当今两款最主流的服务器操作系统产品,都拥有广泛的用户和信徒。Windows 通过强大的商业运作,驱动了大量优秀人才加盟到它的开发团队中;Linux 通过社区产品的魅力吸引着世界上大量的顶级程序员为它贡献源代码、解答问题。两者在服务器市场上竞争激烈,不分伯仲,但也存在互相扶持的关系。 + +我觉得,两个操作系统各有千秋。每次学习两个操作系统的技术知识,都让我切实地感受到编程真的是一门艺术,而学习编程就像是在探索艺术。 + +今天我们继续从一道面试题目“ Linux 内核和 Windows 内核有什么区别?”入手,去了解这两个操作系统内核的设计,帮助你学习操作系统中最核心的一个概念——内核,并希望这些知识可以伴随你日后的每个系统设计。 + +什么是内核? + +说到操作系统,就必须说内核。内核是操作系统中应用连接硬件设备的桥梁。 + +内核的能力 + +对于一个现代的操作系统来说,它的内核至少应该提供以下 4 种基本能力: + + +管理进程、线程(决定哪个进程、线程使用 CPU); +管理内存(决定内存用来做什么); +连接硬件设备(为进程、和设备间提供通信能力); +提供系统调用(接收进程发送来的系统调用)。 + + +操作系统分层 + +从上面 4 种能力来看操作系统和内核之间的关系,通常可以把操作系统分成 3 层,最底层的硬件设备抽象、中间的内核和最上层的应用。 + + + +内核是如何工作的? + +为了帮助你理解什么是内核,请你先思考一个问题:进程和内核的关系,是不是像浏览器请求服务端服务?你可以先自己思考,然后在留言区写下你此时此刻对这个问题的认知,等学完“模块三”再反过头来回顾这个知识,相信你定会产生新的理解。 + +接下来,我们先一起分析一下这个问题。 + +内核权限非常高,它可以管理进程、可以直接访问所有的内存,因此确实需要和进程之间有一定的隔离。这个隔离用类似请求/响应的模型,非常符合常理。 + + + +但不同的是在浏览器、服务端模型中,浏览器和服务端是用不同的机器在执行,因此不需要共享一个 CPU。但是在进程调用内核的过程中,这里是存在资源共享的。 + + +比如,一个机器有 4 个 CPU,不可能让内核用一个 CPU,其他进程用剩下的 CPU。这样太浪费资源了。 +再比如,进程向内核请求 100M 的内存,内核把 100M 的数据传回去。 这个模型不可行,因为传输太慢了。 + + +所以,这里多数操作系统的设计都遵循一个原则:进程向内核发起一个请求,然后将 CPU 执行权限让出给内核。内核接手 CPU 执行权限,然后完成请求,再转让出 CPU 执行权限给调用进程。 + +关于这块知识,我们会在“ 14 |户态和内核态:用户态线程和内核态线程有什么区别?”中详细讨论。 + +Linux 的设计 + +Linux 操作系统第一版是1991 年林纳斯托·瓦兹(一个芬兰的小伙子,当时 22 岁)用 C 语音写的。 写完之后他在网络上发布了 Linux 内核的源代码。又经过了 3 年的努力,在 1994 年发布了完整的核心 Version 1.0。 + +说到 Linux 内核设计,这里有很多有意思的名词。大多数听起来复杂、专业,但是理解起来其实很简单。接下来我们一一讨论。 + + +Multitask and SMP(Symmetric multiprocessing) + + +MultiTask 指多任务,Linux 是一个多任务的操作系统。多任务就是多个任务可以同时执行,这里的“同时”并不是要求并发,而是在一段时间内可以执行多个任务。当然 Linux 支持并发。 + +SMP 指对称多处理。其实是说 Linux 下每个处理器的地位是相等的,内存对多个处理器来说是共享的,每个处理器都可以访问完整的内存和硬件资源。 这个特点决定了在 Linux 上不会存在一个特定的处理器处理用户程序或者内核程序,它们可以被分配到任何一个处理器上执行。 + + +ELF(Executable and Linkable Format) + + + + +这个名词翻译过来叫作可执行文件链接格式。这是一种从 Unix 继承而来的可执行文件的存储格式。我们可以看到 ELF 中把文件分成了一个个分段(Segment),每个段都有自己的作用。如果想要深入了解这块知识,会涉及部分编译原理的知识,如果你感兴趣可以去网上多查些资料或者去留言区我们一起讨论。 + + +Monolithic Kernel + + +这个名词翻译过来就是宏内核,宏内核反义词就是 Microkernel ,微内核的意思。Linux 是宏内核架构,这说明 Linux 的内核是一个完整的可执行程序,且内核用最高权限来运行。宏内核的特点就是有很多程序会打包在内核中,比如,文件系统、驱动、内存管理等。当然这并不是说,每次安装驱动都需要重新编译内核,现在 Linux 也可以动态加载内核模块。所以哪些模块在内核层,哪些模块在用户层,这是一种系统层的拆分,并不是很强的物理隔离。 + +与宏内核对应,接下来说说微内核,内核只保留最基本的能力。比如进程调度、虚拟内存、中断。多数应用,甚至包括驱动程序、文件系统,是在用户空间管理的。 + + + +学到这里,你可能会问:在内核层和在用户层有什么区别吗? + +感觉分层其实差不多。 我这里说一个很大的区别,比如说驱动程序是需要频繁调用底层能力的,如果在内核中,性能肯定会好很多。对于微内核设计,驱动在内核外,驱动和硬件设备交互就需要频繁做内核态的切换。 + +当然微内核也有它的好处,比如说微内核体积更小、可移植性更强。不过我认为,随着计算能力、存储技术越来越发达,体积小、安装快已经不能算是一个很大的优势了。现在更重要的是如何有效利用硬件设备的性能。 + +之所以这么思考,也可能因为我是带着现代的目光回望当时人们对内核的评判,事实上,当时 Linux 团队也因此争论过很长一段时间。 但是我觉得历史往往是螺旋上升的,说不定将来性能发展到了一个新的阶段,像微内核的灵活性、可以提供强大的抽象能力这样的特点,又重新受到人们的重视。 + +还有一种就是混合类型内核。 混合类型的特点就是架构像微内核,内核中会有一个最小版本的内核,其他功能会在这个能力上搭建。但是实现的时候,是用宏内核的方式实现的,就是内核被做成了一个完整的程序,大部分功能都包含在内核中。就是在宏内核之内有抽象出了一个微内核。 + +上面我们大体介绍了内核几个重要的特性,有关进程、内存、虚拟化等特性,我们会在后面几个模块中逐步讨论。 + +Window 设计 + +接下来我们说说 Windows 的设计,Windows 和 Linux 的设计有很大程度的相似性。Windows也有内核,它的内核是 C/C++ 写的。准确地说,Windows 有两个内核版本。一个是早期的Windows 9x 内核,早期的 Win95, Win98 都是这个内核。我们今天用的 Windows 7, Windows 10 是另一个内核,叫作 Windows NT。NT 指的是 New Technology。接下来我们讨论的都是 NT 版本的内核。 + +下面我找到一张 Windows 内核架构的图片给你一个直观感受。 + + + +Windows 同样支持 Multitask 和 SMP(对称多处理)。Windows 的内核设计属于混合类型。你可以看到内核中有一个 Microkernel 模块。而整个内核实现又像宏内核一样,含有的能力非常多,是一个完整的整体。 + +Windows 下也有自己的可执行文件格式,这个格式叫作 Portable Executable(PE),也就是可移植执行文件,扩展名通常是.exe、.dll、.sys等。 + +PE 文件的结构和 ELF 结构有很多相通的地方,我找到了一张图片帮助你更直观地理解。 因为这部分知识涉及编译原理,我这里就不详细介绍了,感兴趣同学可以在留言区和大家一起讨论,或者查阅更多资料。 + + + +Windows 还有很多独特的能力,比如 Hyper-V 虚拟化技术,有关虚拟化技术我们将在“模块八:虚拟化和其他”中详细讲解。 + +总结 + +这一讲我们学习了内核的基础知识,包括内核的作用、整体架构以及 3 种内核类型(宏内核、微内核和混合类型内核)。内核很小(微内核)方便移植,因为体积小、安装快;内核大(宏内核),方便优化性能,毕竟内核更了解计算机中的资源。我们还学习了操作系统对执行文件的抽象,但是没有很深入讨论,内核部分有很多知识是需要在后面的几个模块中体现的,比如进程、文件、内存相关的能力等。 + +那么通过这一讲的学习,你现在可以来回答本节关联的面试题目:Linux 内核和 Windows 内核有什么区别? + +老规矩,请你先在脑海里构思下给面试官的表述,并把你的思考写在留言区,然后再来看我接下来的分析。 + +【解析】 Windows 有两个内核,最新的是 NT 内核,目前主流的 Windows 产品都是 NT 内核。NT 内核和 Linux 内核非常相似,没有太大的结构化差异。 + +从整体设计上来看,Linux 是宏内核,NT 内核属于混合型内核。和微内核不同,宏内核和混合类型内核从实现上来看是一个完整的程序。只不过混合类型内核内部也抽象出了微内核的概念,从内核内部看混合型内核的架构更像微内核。 + +另外 NT 内核和 Linux 内核还存在着许多其他的差异,比如: + + +Linux 内核是一个开源的内核; +它们支持的可执行文件格式不同; +它们用到的虚拟化技术不同。 + + +关于这块知识就不展开说了, 我们会在后续的“进程、内存、虚拟化”等模块中仔细讨论。 + + + + \ No newline at end of file