构建高性能微服务架构

传统架构之痛

当前的时代称为互联网的时代,互联网应用的特点往往是,新型的应用迅速出现颠覆旧的商业模式,一旦商业模式稍有起色便会有大量的厂商蜂拥而至,使得蓝海变成红海,经过短时间的残酷竞争,热度往往持续较短时间后,大量厂商退出市场,仅仅前二名到三名能够存活下来,最终这一波浪潮被下一波取代。我们回想视频网站,团购网站,社交网站,微博,打车,直播等,全都呈现这种模式。

所以互联网市场只有一招,天下武功,唯快不破。

当一种商业模式出现的时候,为了迅速切入市场,占领商业献祭,快速验证商业模式,往往软件的设计会采取传统的单体架构。

传统的单体架构往往分三层,最下面一层是数据库,中间是应用程序层,所有的商业逻辑都会在这一层,最上面是页面。

如果商业模式比较成功,则应用会添加新的功能,一种方式是全部添加到原有的商业逻辑中,另一种方式是开发一个全新的三层结构来支撑新的功能。

 

由于是单体结构,一方面应用层里面的功能越来越多,如图中一个功能变为三个功能,将来可能三十个功能,另一方面很多代码和逻辑都不能复用,如图中功能 2,每个应用都有,这种功能常见的有认证模块,消息的编码和解码模块等。

这种单体结构会带来三方面灵活性比较差。

第一,时间灵活性:应用快速迭代,缩短客户需求到产品上线的时间。

互联网应用的需求随时改变,因而应用开发的迭代速度要求会比较快,传统的软件可能半年或者一年发布一个功能,而互联网应用则可能每周都发布新的功能。而且互联网产品会时常搞活动,比如双十一,每次活动不可能提前很长时间策划,从而给开发充分的产品周期。

然而单体结构的应用如果有了 30 个模块,每个模块由两到三个人负责,则修改的成本会非常的大,从开发人员看来,整个架构牵一发动全身,每次修改必须要做好良好的前期设计,并且让整个团队评审,如果新的需求要改多个模块,则代码的管理和合并就成为很大的问题。

而且无论测试,联调,上线,扩展,缩减,升级,回滚都需要重新搭建环境,需要配置软件,需要进行回归测试,运维人员需要反复的部署环境,而且无法保证环境的一致性,任何一个环境配置的小问题,都有可能导致软件使用有问题。

第二,空间灵活性:应用弹性伸缩,应对业务量突然增长后较短时间恢复。

互联网应用往往是针对终端用户的,终端用户的行为往往不如企业用户那样容易预测,终端用户可能因为促销,过节等因素导致访问量的迅速的增长,当访问遭遇峰值的时候,我们希望应用可以快速扩展。

然而对于单体架构,应用扩展的过程如万丈高楼平地起,一层一层慢慢盖。

如果部署在物理机上面,则还需要采购新的物理设备,如果有虚拟化平台,则需要申请新的虚拟机,并且配置好网络和存储。然而仅仅一个空的虚拟机是没有用的,上面什么环境都没有,接下来需要安装应用环境,比如 Tomcat, Apache 等,然后就是将应用,页面配置到应用环境中,还没有结束,新启动的是一个独立的系统,还需要将这个系统加入到当前的系统中,才能一起承担访问量,例如加入到负载均衡器的配置里面。这样每部署一套都需要从头来一遍,实在是运维人员的噩梦。

第三,管理灵活性:易部署,易迁移,服务发现,依赖管理,自动修复,负载均衡。

现在很多互联网应用都需要多地,多机房部署,有时候会从一个机房迁移到另一个机房,如果每次变动都如上面一样从底层到顶层都做一遍,成本比较大,时间比较长。

当一个应用依赖于另一个应用,被依赖的应该宕机之后,修复需要手动进行,从底层到顶层配置一遍,而且修复好的系统往往 IP 地址也变了,则依赖于此应用的所有应用都需要修改配置。

 

微服务化之路

要解决上面所述的三个问题,对应的有三个步骤。

第一步,去状态化,从而实现程序的可扩展。

单体架构的程序往往很多数据是保存在内存里面的,或者是本地文件系统的,例如用户访问的 session 数据,例如用户上传的照片。所谓的去状态化,就是使得应用程序仅仅运行商业逻辑,而将数据的保存全部交给外部的存储服务。内存里面的数据可以放在缓存 redis 里面,结构化数据放在统一的数据库服务里面,文件存放在对象存储里面。这样应用程序就变成了一个只有商业逻辑的应用,可以随时扩展。

这里面有一个问题就是应用程序的状态外置化了,放在统一的缓存,数据库,对象存储里面了,可是应用程序宕机了是没有问题了,再启动一个就可以,如果缓存,数据库,对象存储宕机了,数据不也是没有了么?

其实主流的开源的缓存 Redis,数据库 MySQL,对象存储 swift 等,他们设计的时候就是考虑了高可用和容灾的情况的,所以数据存储的工作就应该让专业的模块来做这件事情,而应用程序应该关注在商业逻辑的实现,从而加速开发的速度。当然这些存储模块的维护则是另外的专业人士在做的,这部分人士是缓存的专家,数据库的专家,对象存储的专家,不需要懂商业逻辑。

第二步,容器化,可编排。

与传统 IaaS 架构不同,容器提供的不仅仅是基础资源,而是将操作系统,应用运行环境以及代码打包成一个不可修改的镜像进行交付,容器的机制可以保证无论在哪里,什么时候运行,都能保持环境的一致性,也就是 Docker 所宣称的“Build, Ship, and Run Any App, Anywhere(一次构建,随处运行)”。

容器特别适合部署无状态的服务,上一步的无状态化,给容器化奠定了良好的基础。

一个服务往往会包含多个组件,因而会封装成为多个容器,容器之间会有相互的依赖,相互的调用,Kubernetes 可以实现服务的编排、自发现、自修复,使得复杂服务的部署变成了一次 API 调用。

如图所示,Kubernetes 管理了相互依赖的四个服务,全部部署在容器里面,分别运行在不同的机器上面。服务之间的调用通过服务名称进行,而非固定 IP 进行,而服务名称 Kubernetes 会管理起来。

当一台服务器宕机的时候,服务 B 和服务 C 会被自动调度到另外两台机器上,由于服务是无状态的,所以没有问题。然而服务 A 和服务 D 如何再找到服务 B 和服务 C 能,这两个服务的物理主机变了,很可能 IP 也变了。其实是没有问题的,Kubernetes 会自动将服务名和对应的 IP 地址关联起来,服务之间只要配置的是服务名,而非 IP 地址,就依然能够相互访问。

有了容器和 Kubernetes 这两个工具后,解决了管理复杂性的问题,但是需要专门的团队和技术力量,去玩转 Kubernetes。

第三步,DevOps,可迭代。

从前面的一张图中,Dev 和 Ops,开发和运维之间隔着长长的流程,导致迭代速度很慢。DevOps 就是可以加快迭代速度的一种方法。

然而如何让开发直接进入到运维流程中呢?容器的镜像不可改变性提供了方案。

Docker 可以保证容器中的运行环境,业务代码无论在哪个环境都是一致的,唯一不同的是不同环境的不同配置,可以通过环境变量注入的方式设置。有了这个模式,开发人员可以从很早就使用容器镜像的方式进行开发,并且以容器镜像的方式交付给测试,测试使用同样的镜像得到同样的环境进行测试用例的执行,当决定发布的时候,也确定真正到了生产环境的时候,同测试环境是一样的。这样避免了环境不断重复的部署过程。

容器镜像可以手动维护和交付,但是也可以借助 CICD 持续集成的工具,来监控代码库的更改,当有程序员提交代码的时候,会触发一个 hook,这个 hook 会调用 CI 工具,告知他代码已经有更新了,可以根据最新的代码打成最新的镜像,CI 工具根据配置好的 Dockerfile,将代码打包成镜像,上传到镜像库,每次打镜像都应该有新的版本,而不应该总使用 latest。

镜像打好了以后,接下来 CD 的工具会将镜像部署到测试环境,测试人员可以就这个新的测试环境进行一轮测试,如果测试成功,则可以告知线上管理人员,可以更新新的版本。

线上管理人员在恰当的时间,使用编排工具,将容器镜像的版本改为最新的版本,从而生产环境也就更新了。如果发现生产环境有问题,新的版本有 Bug,没有问题,只要将镜像改为上个版本的镜像即可,可以保证原来那个能用的版本,所有的配置和原来一样,从而功能也一样,实现了升级和回滚功能。

当然这套持续集成的工具和流程,需要开发人员和开发流程进行改进,才可以顺利使用。

小结

只需要逐步做到以下三步,就可以实现微服务和快速交付:

  1. 去状态化:将内存数据写入缓存,将持久化数据写入数据库,将文件写入对象存储。
  2. 容器化:可弹性伸缩,自我修复,动态迁移。
  3. 微服务化:可快速迭代,持续集成。

posted on 2019-01-31 15:18  小夏coding  阅读(191)  评论(0编辑  收藏  举报

导航