专栏名称: 刘超的通俗云计算
刘超,网易云解决方案首席架构师,代码级略懂OpenStack、Hadoop、Docker、Lucene、Mesos等开源软件,曾出版《Lucene应用开发揭秘》,个人博客可搜索popsuper1982。
目录
相关文章推荐
51好读  ›  专栏  ›  刘超的通俗云计算

微服务化的基石——持续集成

刘超的通俗云计算  · 公众号  · 架构  · 2018-01-07 00:55

正文

一、持续集成对于微服务的意义:拆之前要先解决合的问题


在很多微服务化的文章中,很少会把持续集成放在第一篇,因为大多数的文章都会将如何拆的问题,例如拆的粒度,拆的时机,拆的方式。


为什么需要拆呢?因为这是人类处理问题的本质方式:将一个大的复杂问题,变成很多个小问题解决。


所以当一个系统复杂到一定程度,当维护一个系统的人数多到一定程度,解决问题的难度和沟通成本大大提高,因而需要拆成很多个工程,拆成很多个团队,分而治之。


然而当每个子团队将子问题解决了,整个系统的问题就解决了么?你可以想象你将一辆整车拆成零件,然后再组装起来的过程,你就可以想象拆虽然不容易,合则更难,需要各种标准,各种流水线,才能将零件组装称为车。


我们先来回顾一下拆的过程。


最初的应用大多数是一个单体应用



一个Java后端,后面跟一个数据库,基本上就搞定了。


随着系统复杂度的增加,首先Java程序需要做的是纵向的拆分。



首先最外面是一个负载均衡,接着是接入的nginx,做不同服务的路由。


不同的服务拆成独立的进程,独立部署,每个服务使用自己的数据库和缓存,解决数据库和缓存的单点瓶颈。


数据库使用一主多从的模式,进行读写分离,主要针对读多写少的场景。


为了承载更多的请求,设置缓存层,将数据缓存到Memcached或者Redis中,增加命中率。


当然还有些跨服务的查询,或者非结构化数据的查询,引入搜索引擎,比关系型数据库的查询速度快很多。



在高并发情况下,仅仅纵向拆分还不够,因而需要做真正的服务化。


一个服务化的架构如图所示。


首先是接入层,这一层主要实现API网关和动态资源和静态资源的分离及缓存,并且可以在这一层做整个系统的限流。


接下来是Web层,也就是controller,提供最外层的API,是对外提供服务的一层。


下面是组合服务层,有时候被称为编排层,compose层,是实现复杂逻辑的一层。


下面是基础服务层,是提供原子性的基本的逻辑的一层,他下面是缓存,数据库。


服务之间需要治理,需要相互发现,所以一般会有dubbo或者springcloud一样的框架。


对所有的服务,都应该有监控告警,及时发现异常,并自动修复或者告警运维手动修复。


对于所有的服务的日志,应该有相同的格式,收集到一起,称为日志中心,方便发现错误的时候,在统一的一个地方可以debug。


对于所有的服务的配置,有统一的管理的地方,称为配置中心,可以通过修改配置中心,下发配置,对于整个集群进行配置的修改,例如打开熔断或者降级开关等。


通过简单的描述,大家可以发现,从一个简单的单体应用,变成如此复杂的微服务架构,除了关心怎么拆的问题,还必须关注:


  • 如何控制拆的风险

  • 如何保证代码质量

  • 如何保证功能不变,不引入新的Bug


答案当然就是集成,从一开始就集成,并且不断的集成,反复的将拆分的模块重新组合,看看是否能够顺利组合起来,并且保证功能的不变。


要是不没事儿就组合一下,天知道几个月以后还能不能合的起来。


别忘了程序是人写的,你和你媳妇长时间不沟通都对不上默契,别说两个程序员了。


二、持续集成就是不断的尝试在一起


集成就是在一起。



为什么需要一个统一的代码仓库Git来做代码管理呢?是为了代码集成在一起。


为什么需要进行构建build呢?就是代码逻辑需要集成在一起,编译不出错


为什么要单元测试呢?一个模块的功能集成在一起能够正确工作。


为什么需要联调测试Staging环境呢?需要将不同模块之间集成在一起,在一个类生产的环境中进行测试。


最终才是部署到生产环境中,将所有人分开做的工作才算真正的合在了一起。



持续集成就是制定一系列流程,或者一个系列规则,将需要在一起的各个层次规范起来,方便大家在一起,强迫大家在一起。


三、持续集成,持续交付,持续部署,敏捷开发,DevOps都啥关系?


这些概念都容易混淆,他们之间是什么关系呢?



敏捷开发Agile是一种开发流程,是一种快速迭代的开发流程,每个开发流程非常短,长到一个月,短到两个星期,就会是一个周期,在这个周期中,每天都要开会同步,每天都要集成。正是因为周期短,才需要持续的做这件事情,如果一个开发周期长达几个月,则不需要持续的集成,最后留几个星期的集成时间一起做也是可以的,但是这样就不能达到互联网公司的快速迭代,也是我们常常看到传统公司的做法。


持续集成往往指对代码的提交,构建,测试的过程,也就是上述的在一起的过程。


持续交付是指将集成好的交付物,例如war,jar,或者容器镜像,部署在联调环境,或者预发环境的过程。


持续部署是指将交付物持续部署在生产环境的过程。


我们常说CICD,CD有时候指的是Delivery交付,有的是指Deployment部署,对于非生产环境,自动部署是没有问题的,对于生产环境,往往还是需要有专人来进行更为严肃的部署过程,不会完全的自动化。


接下来就是DevOps,DevOps不只是CICD,除了技术和流程,还包含文化。例如容器化带来的一个巨大的转变是,原来只有运维关心环境的部署,无论是测试环境,还是生产环境,都是运维搞定的,而容器化之后,需要开发自己写Dockerfile,自己关心环境的部署。因为微服务之后,模块太多了,让少数的运维能够很好的管理所有的服务,压力大,易出错,然而开发往往分成很多的团队,每个模块自己关心自己的部署,则不易出错,这就需要运维一部分的工作让研发来做,需要研发和运维的打通,如果公司没有这个文化,研发的老大说我们不写Dockerfile,则DevOps是搞不定的。


四、从一个持续集成的日常,看上述的几个概念如何实践



这是一个持续集成的流程,但是运行起来更加的复杂。


首先,项目开发的流程使用的是Agile,用常见的scrum为例子。



每天早上第一件事情,就是开站会standup meeting,为什么要站着呢?因为时间不能太长,微服务的一个模块,大概需要5-9人的团队规模,如果团队规模太大了,说明服务应该进行拆分了,这个团队规模,是能够保证比较短的时间之内过完昨天的状态的。


一定要大家一起开,而不要线下去更新Jira,虽然看起来一样,但是执行起来完全不一样。只有大家一起开,一起看燃尽图,一起说我昨天做了什么,今天打算做什么,有什么阻碍,才能够让大家都了解情况,不要期望大家会去看别人的Jira,经验告诉你,不会的。


而且这个站会对于开发是比较大的压力,例如你的一个功能block了依赖方的开发,在会议上会暴露出来,大家都知道这件事情了,一天block,两天block,第三天你都不好意思去说了,这会强迫你将大任务,比如原来写1周干一件什么事情,写成小时级别,这样每天你都有的说,昨天完成了一个task,而不是周只在那里说干同样一件事情,而且一旦有了block,team lead会知道这件事情,会帮你赶紧解决这个事情,推进整个项目的进展。让一个技术人员在团队面前承认这件事情我尝试了几天,的确搞不定了,也是一种压力。


站会中的内容其实在前一天晚上就要开始准备了。


持续集成要求每天都提交代码,这样才能降低代码集成的风险,不能埋头写一周一起提交,这样往往集成不成功。怎么样才能鼓励每天都提交代码呢?一个就是第二天的站会,你这个功能代码提交了,单元测试通过了,第二天才能说做完了,否则不算,这就逼得你,将大任务拆成小任务,每天都多次提交。


而且Git的提交方式,是后提交者有责任去merge,保证代码的编译通过和测试通过,你会发现,如果你不及时提交,等你改了一大片代码,别人都提交完了,这一大片的冲突都是你来merge,测试用例不通过的你来fix,所以逼的你有一个小的功能的改动,就尽早提交,pull一下发现没有人提交,赶紧提交。


提交不是马上进入主库,而是需要代码审核,这是把控代码质量的重要的环节。


代码质量的控制往往每个公司都有文档,甚至你可以从网上下载一篇很长很长的Java代码规范。但是我们常常看到的例子是,规范是有,但是虱子多了不咬人,规范太多的,谁也记不住,等于没有规范。


所以建议将复杂的规范通过项目组内部的讨论,简化为简单的10几条军规,深入人心,大家都容易记住,并且容器执行。


代码审核往往需要注意下面的几方面

  • 代码结构:整个项目组应该规定统一的代码组织结构,使得每个开发拿到另一个人的代码,都能看的熟悉的面孔。这也是scrum中提倡的每个开发之间是可替代的,当一个模块有了阻碍,其他人是可以帮上忙的。至于核心的逻辑,估计审核人员也来不及细看,这不要紧,核心逻辑是否通过,不能靠眼睛,要靠测试。

  • 有没有注释,尤其是对外的接口,应该有完善的注释,方便自动生成接口文档。

  • 异常的处理,是否抛出太过宽泛的异常,是否吞掉异常,是否吞掉异常的日志等。

  • 对于pom是否有修改,引入了新的jar。

  • 对于配置文件是否有修改,对外访问是否设置超时

  • 对于数据库是否有修改,是否经过DBA审核







请到「今天看啥」查看全文