从 0 到 1 工程实践 (3)

叁叁肆2018-11-13 11:26

欢迎访问网易云社区,了解更多网易技术产品运营经验。


2.1.3 工程化构建


有了基础的代码框架后,开发人员要做的就是根据产品需求来填充代码。功能开发完 毕后,最重要的就是构建、打包和部署。规范化,甚至自动化这一流程对于工程的整体开 发测试流程来说都是很有必要的,它可以减少错误或者不必要的麻烦。并且随着产品的发 展,快速迭代、快速发布会成为产品研发的日常需要,如果没有一个规范或者自动化的流 程,那么对于运维人员来说,产品发布就会成为梦魇。本节以 Maven 为例,对工程的依赖 管理及开发完成后的构建部署流程进行简单的介绍。


1. 基于 Maven 构建


依赖管理(Dependencies) 

一般的工程都需要依赖很多组件去完成功能,比如说依赖 Web 开发框架,依赖数据访 问框架、日志框架等。对一个简单工程来说,依赖管理可以很简单,但对一个多人维护的 大型项目来说,依赖管理就会很复杂。之前提到 Maven 时说过,Maven 会提供一种标准化 的依赖管理机制,让大型项目的依赖管理很简单。Maven 的依赖管理既可以管理项目对于 外部框架的依赖,也可以管理工程本身内部各模块之间的依赖。以下是一个简单的示例。


其中 dependency 元素有一个 scope 的配置,scope 控制哪些依赖在什么环境下可用或者 哪些依赖在打包时需要(或者不需要)。下面介绍一下 Maven 里常用的几种 scope。


compile:默认的 scope,这种 scope 的依赖在所有场景下都可以,打包时也会打包 进应用。

provided:预期由 JDK 或者运行时的容器来提供的依赖,可以使用此 scope,比如 上面示例中的 servlet-api 就由运行的 Servlet 容器提供,打包时不需要提供这个依 赖(但是编译时需要)。

runtime:在测试或者运行的时候需要依赖,但在编译的时候不需要。

test:只在编译或者运行与测试相关的代码时需要。


Maven 会在构建的时候从远程 Maven 仓库下载依赖到本地(如果已经下载到本地, 则直接从本地加载依赖),并完成构建。从网络上自动下载依赖革命性地改变了开发软件的 方式。


构建生命周期(Lifecycle) 

Maven 的构建动作是由构建目标(goal)与构建目标的不同阶段(phase)组成的。构 建生命周期就是指构建过程中的不同阶段。Maven 中的构建工程都需要遵循生命周期,它 的默认生命周期包含常用的工程构建阶段,如编译、测试、打包等。以下是 Maven 默认生 命周期常用各个阶段的简要说明。


compile:对工程的源代码进行编译。

test:基于单元测试框架运行所有的单元测试。

package:对编译后的代码进行打包,比如 JAR、WAR 等。

install:将打包好的文件安装到本地 Maven 仓库。

deploy:将打包好的文件安装到远程 Maven 仓库。


构建 Profile 

为了解决程序可移植性的问题,Maven 2.0 引入了 Profile 的概念。基于 Profile,Maven 才有了可以针对不同环境进行不同构建的能力。比如对于线上线下环境,一般都会访问不 同的数据库,通过不同的 Profile 来完成:针对线上环境配置线上数据库地址,针对线下环 境配置线下数据库地址,在构建时指定是针对线上环境构建还是线下环境构建。Profile 也 可以配置成基于操作系统或者安装的 JDK 版本触发不同的构建 Profile,这样在不同环境下 构建出来的应用就不会存在兼容性问题。


2. 构建示例


有了上面对 Maven 构建相关内容的了解,我们就可以基于 Maven 进行构建,例如针对 下面的配置。

online 和 test 两个 Profle 访问的数据库地址不同,我们可以通过如下的命令针对线上环 境打包应用。


mvn package -Ponline 

如果开发一个框架性的项目,就可以通过 mvn install 将生成的依赖安装到本地 Maven仓库,如果有其他团队依赖这个框架,就可以通过 mvn deploy 将依赖包部署到远程 Maven 仓库。


多模块 Maven 工程的构建与单模块的差不多,只不过多模块工程在构建时,需要分析 各个模块之间的依赖,比如电商网站示例里,web 依赖其他几个模块提供接口,构建时就 先构建其他几个模块,然后再构建 web 模块。


2.1.4 代码规范及检查


1. 代码规范 

代码规范有两方面的内容,一方面是代码风格,如使用 Tab 还是空格,缩进 2 格还是 4 格,对变量或者函数命名时,是使用驼峰法还是使用下画线命名法;另一方面是代码实 现层面的一些基础规范,比如异常怎么处理、日志怎么处理。


有些读者会纳闷,只要我完成功能就好了,为什么要花时间在这些事情上?难道差 2 个空格差别就这么大?或者按自己的想法处理一下异常就好了,又会有什么影响?


对于一个产品来说,一般都由一个团队来完成,如果没有统一的代码规范,那么每个 人的代码必定风格迥异。如果多个人同时开发同一模块,或者即使是模块分工明确,合并 代码的时候也会碰到问题。即使你没改任何代码,但是格式不一致(或者开发工具做了格 式化),合并后还是会发现有代码更新,而实际可能只是因为 Tab 变成了空格。一般情况下, 理解代码困难并非是因为代码中有复杂的算法或者逻辑,而是不习惯阅读风格不一致的代 码,统一的风格使得代码可读性提高。而没有规范的异常处理,没有规范的日志处理等, 就可能在排查问题时很难找到引起问题的原因或者效率低下。基于经过实践验证的代码规 范进行开发,不但可以有效减少问题,查找问题也会更有效率。


随着项目的推进,维护成本成为项目的主要问题。而开发过程中的代码质量直接影响 着维护的成本。上面提到,规范的代码大大提高了程序的可读性,而可读性高的代码维护 成本相对更低。维护工作不仅要读懂原有代码,还需要在原有代码基础上做修改,统一的 风格有利于长期的维护。


所以,规范的代码在团队的合作开发中是非常有益而且必要的,它的主要目的是代码 可读性和可维护性。每个团队都应该有自己的代码规范,参考一些大公司的代码规范,在 其基础上整合团队的特殊性,并持续改进,以提高团队整体的代码质量。


2. 代码检查 

如果每个团队都要求你在写出一行代码的时候注意风格,那就得不偿失。基本上主流 语言的开发工具都提供了代码格式化的工具,通过定义规则,开发工具可以在保存时自动 (或者手动)对代码进行格式化。


当然,除了代码格式化工具,也可以在代码提交时(或者合并前),通过一些工具(如 Java 中的 Checkstyle)对代码进行强制代码风格检查,即使就算人为原因没做格式化,代 码也会因为检查出问题而无法提交或合并。


代码风格检查只保证了代码风格的一致,但实现是否合理,是不是有哪些隐藏的问题 没有发现,我们往往需要一些工具来进行更严格的检查。这就是静态代码检查工具想达到 的目标。静态代码检查会分析所有代码,来发现所有可能的问题,比如潜在的空指针,资 源没有关闭或者回收,可以优化的代码等。Java 可以通过 Findbugs 或者 PMD 来做静态代 码检查。 


文章节选自《云原生应用架构实践》 网易云基础服务架构团队 著


网易云计算基础服务深度整合了 IaaSPaaS 及容器技术,提供弹性计算、DevOps 工具链及微服务基础设施等服务,帮助企业解决 IT、架构及运维等问题,使企业更聚焦于业务,是新一代的云计算平台。点击可免费试用