3.3 应用架构设计
上面已经介绍了用户业务上云时如何进行网络设计、运维管理环境规划,本章将重点介绍如何基于阿里云产品和服务设计应用系统架构。
3.3.1 负载均衡
阿里云负载均衡(Server Load Balancer,SLB)是将访问流量根据转发策略分发到后端多台ECS的流量分发控制服务。用户可以通过负载均衡的流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。
阿里云负载均衡主要功能:
负载均衡服务通过设置虚拟服务地址(IP),将多台云服务器ECS实例虚拟成一个高性能、高可用的应用服务池。根据应用指定的方式,将来自客户端的网络请求分发到云服务器池中。
负载均衡服务会检查云服务器池中ECS的健康状态,自动隔离异常状态的ECS,从而解决单台ECS的单点问题,同时提高应用的整体服务能力。
在标准的负载均衡功能之外,负载均衡服务还具备TCP与HTTP抗DDoS攻击的特性,增强应用服务器的防护能力。
负载均衡服务是ECS面向多机方案的一个配套服务,需要同ECS结合使用。
在阿里云平台之上,用户应用可以使用SLB软负载均衡产品实现Web服务器和应用服务器的自动负载均衡和故障检测。阿里云SLB负载均衡产品可支持四层TCP协议和七层HTTPHTTPS协议。
3.3.2 可扩展性
前面介绍了阿里云上应用系统接入层的设计,本节将介绍应用系统的可扩展性设计。应用系统可扩展性设计主要包含应用程序层的可扩展性设计和数据存储层的可扩展性设计。
1 . 应用程序可扩展性设计
采用纵向扩展时,无须担心如何处理用户请求,这些请求都将汇集到单个服务器的网络接口卡上,并且得到处理,对它们的恢复也是通过网卡发送的。在不同处理器之间分配负载的具体任务,就交给操作系统调度程序来完成。如果应用程序是多进程的,可以给每个处理器分配一个子进程,然后同时处理多个请求。
但在横向扩展时,新问题出现了。我们有多个处理器,却没有操作系统分配请求。我们将在同一IP地址收到多个请求,而且希望在多台机器上服务这些请求。
最简单解决方案是引入应用层的负载均衡方案。一种方式是在DNS中为应用程序的域名创建一条以上的记录。通过DNS轮询随机将业务请求发送给实际提供服务的主机进行处理,以扩充应用服务器的处理能力。但此方案存在一个问题,即DNS服务无法自动发现后端出现异常的应用服务器,并自动屏蔽故障应用服务器对业务的影响。另外一种方式是使用阿里云SLB产品,作为统一接入层对外提供服务,SLB可自动转发业务流量到不同的后端服务器进行处理,并自动检测后端应用服务器是否正常,主动发现和处理异常应用服务器。
2 . 数据存储层可扩展性设计
阿里云上数据存储层的常用产品有RDS关系数据库服务、OSS对象存储服务和Redis数据缓存服务。OSS对象存储服务可动态扩展存储容量和访问量,无须用户在业务层考虑容量的可扩展性设计。下面将重点介绍RDS关系数据库服务和Redis数据缓存服务应如何实现可扩展性设计。
Redis数据缓存服务可支持主从版和集群版两种实例,如果用户业务在存储量或访问量急剧增加,已超过之前设计容量时,用户可以考虑通过扩展Redis实例规格或从主从版实例升级为集群版实例,来实现Redis数据缓存在存储容量和访问量方面的能力提升。用户使用阿里云Redis服务时,如果业务遇到实例访问量QPS的CPU瓶颈,需要通过扩充Redis实例节点数目来解决,如考虑从主从版Redis实例升级为集群版Redis实例,或从低规格的集群版Redis实例升级高规格集群版Redis实例。
RDS关系数据库服务的扩展能力,主要从以下几个维度考虑:
升级RDS实例规格:可在一定程度上扩展RDS读写请求的处理能力和存储容量。
引入RDS只读实例:使用DRDS实现自动读写分离,可扩展RDS数据库的读流量,适用于读多写少的业务场景。
引入Redis进行热点数据缓存:这同样适用于读多写少和高性能、低延时的场景,需要业务代码能维护好Redis缓存和RDS关系数据库的数据一致性。
使用DRDS分库分表功能实现RDS数据库存储容量和读写访问量的可扩展性设计:这适用于只读实例、缓存、实例规格升级无法解决的场景。由于DRDS引入了分布式存储技术,需要对业务进行合理规划和设计尽量避免分布式问题。
3.3.3 微服务
1 . 微服务与传统的集中化开发方式的差异
在传统的开发模式下,所有的功能打包在一个WAR包里,基本没有外部依赖,部署在一个J2EE容器(Tomcat、JBoss、WebLogic)里,包含DO/DAO、Service、UI等所有逻辑。所有的逻辑组合成一个庞大的运维单元,独立进行开发、升级、测试、部署等。
而微服务架构主张互联网应用由一系列细粒度的职责单一的服务组成,每个服务运行在各自独立的进程中,服务之间通过轻量机制(如HTTP/JSON)进行通信。这些服务建立在业务领域之上,可通过全自动方式独立部署。这些服务基本没有中央式的管理,服务可以用不同的编程语言编写,也可以使用不同的数据存储技术。微服务的目的是有效地对拆分的应用进行服务治理,简化部署,实现敏捷开发和部署。
然而,微服务不是免费的午餐。微服务系统复杂度高,运维开销大,需要完善的服务框架、交付、监控、安全隔离和规模扩展等必要的基础设施的支撑,对于DevOps技能要求高。对于初创和中小规模公司来说,借助于阿里云的基础设施来实现微服务的治理框架和持续交付,不失为最佳选择。
2 . 服务治理框架的选择
(1)使用Spring Cloud构造微服务
Spring Cloud是Springframework里的一个项目,提供了开发分布式系统时比较常见的一些模式能力。其中,配置管理(Config Server)、服务发现(Eureka)、服务熔断(Hystrix)、智能路由(Zuul)等是基于Netflix OSS的一个封装,利用Java注解(annotation)声明,可以在Spring Boot应用里便捷地使用Netflix的开源产品构建生产级可用的微服务应用。而且,可以通过Docker镜像的方式快速地在阿里云容器服务上部署。
(2)使用EDAS来构造微服务
EDAS产品充分利用阿里云的资源管理和服务体系,引入阿里巴巴中间件整套成熟的分布式产品,全面兼容Apache Tomcat的Java容器,提供高性能的分布式服务框架以及秒级推送的分布式配置管理服务。此外,EDAS还创新性地提供了分布式系统链路追踪、容量规划、数据化运营等多款组件,可以帮助企业级客户轻松构建大型分布式应用服务系统。
在复杂的云环境下,应用发布与管理变得十分复杂。本地开发完成的应用需要逐个部署到服务器,然后登录每一台服务器终端进行应用的发布和部署;后续还会有应用的重启、扩容等工作需要完成。服务器的不断增加对于运维人员是一个极大的挑战。针对这个场景,EDAS 提供了一个可视化的应用发布与管理平台,无论集群规模多大,都可以在 Web 控制台上轻松地进行应用生命周期管理。
当从集中式应用转变成分布式系统的时候,系统之间的可靠调用一直都是分布式架构的难题,比如需要确定网络通信、序列化协议设计等很多技术细节。EDAS 提供了一个高性能的 RPC 框架,能够构建高可用的分布式系统,系统地解决各个应用之间的分布式服务发现、服务路由、服务调用以及服务安全等细节。
应用开发完毕部署到生产环境之后,通常需要对应用运行时状态进行监控,比如监控CPU使用率、机器负载、内存使用率和网路流量等。但此类基础监控通常满足不了业务需求,比如系统运行变慢却无法定位瓶颈所在,或者页面打开出错但是无法排查具体调用错误等。对此,EDAS提供了一系列系统数据化运营组件,针对分布式系统的每一个组件和每一个服务进行精细化的监控和跟踪,建立数字化分析系统,从而精准地找到系统瓶颈。
3 . 如何通过各种构建物实现持续交付方案
(1)使用阿里云持续交付平台CRP系统来进行代码持续交付
阿里云持续交付平台CRP支持以鼠标在白屏上拖拽节点的方式定义项目发布工作流,每个节点可以加入多个任务,用以完成自动化更新代码、编译、运行单元测试、自动化发布到ECS机器上的工作。
如果需要基于代码库,执行代码扫描(安全检查)→自动化编译→测试→自动化部署到服务器的工作时,可以在持续交付平台CRP上定制一条持续发布线。持续发布线定制完成后,当代码更新后,CRP会监听到分支更新了代码,自动创建1条新的发布线开始运行,自动进行编译、测试、部署等工作。并且出现问题时,可以发邮件通知项目成员。
不同的技术栈有不同的构建物类型,同时也有相关的工具来创建和安装这些构建物,例如,Ruby中有gem、Java中有WAR包、Python中有egg。我们可以通过CRP等工具部署并且启动这些构建物,但是需要安装和配置一些基础环境软件。类似于Puppet、Chef、ansible等自动化配置管理工具,可以很好地解决这个问题。通过这些工具可对不同构建物的底层部署机制进行屏蔽。
(2)ECS定制化镜像
使用类似Puppet、Chef及Ansible这些自动化配置管理工具的一个问题是,需要花费大量时间在机器上运行这些脚本。假设服务器在ECS上,使用的是标准的CentOS镜像,为了运行Java应用程序,需要做的第一件事情就是安装Oracle JVM,接下来需要安装Tomcat容器。随着集群规模的扩展,同样的工具被一遍一遍地重复安装,对技术人员而言也是一种煎熬。
减少扩容成本的一种方法就是创建一个虚拟机镜像,镜像是云服务器 ECS 实例运行环境的模板,一般包括操作系统和预装的软件。可以使用镜像创建新的 ECS 实例和更换 ECS 实例的系统盘。使用这种功能之后,事情就变得简单了。现在可以把公共的工具安装在镜像上,然后在部署软件时,只需要根据镜像创建一个实例,然后在其上安装最新的服务版本即可。也就是说,只需要构建一次镜像,然后根据这些镜像启动ECS,就不需要再花费时间来安装相应的依赖,因为它们已经在镜像中安装好了,这样可以节省很多时间。如果核心依赖没有改变,那么新版本的服务就可以继续使用相同的基础镜像。
当然,既然已经做到了使用包含依赖的虚拟机镜像来加速交付,那么还可以更进一步,把服务本身也包含在镜像中,直接把镜像作为构建物进行交付,通过阿里云ECS的创建自定义镜像功能,利用自定义镜像安装ECS,当虚拟机启动镜像时,服务就已经就绪了。
通过镜像交付可以屏蔽操作系统和软件的差异性,但是如果在一次交付流程中,从开发测试、预生产、生产环境,每个环节都要制作自定义镜像、重新安装操作系统,对于迭代速度非常频繁的应用,那么虚拟机镜像交付这种略显笨重方式显然是难以接受的。
如果想既交付服务代码又要保证应用环境和操作系统的差异性,同时希望以敏捷、轻量级的方式交付,那么阿里云容器服务能很好地满足这个需求。利用容器技术把应用及其依赖做成一个标准的镜像,从开发到测试到生产环境都用同样的容器镜像,极大弥补了应用开发过程中开发和运维之间的交付鸿沟。
(3)容器服务定制化docker镜像
阿里云容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过Docker容器来进行应用生命周期管理。容器服务极大简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,从而实现云端优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,可帮助用户无缝上云并进行跨云管理。
传统的开发过程开发者的代码里有逻辑、应用以及代码依赖包,通过使用阿里云容器服务,代码中会加入Dockerfile、Docker Compose,用来制作集装箱和搬运集装箱。代码提交成功后,代码服务器会通知持续集成服务(比如Jenkins),持续集成服务会拉取代码进行代码打包,打包后进行单元测试。如果单元测试没有通过,就会马上向开发者反馈。如果通过测试,则会根据代码中的Dockerfile制作镜像,并把镜像推送到阿里云容器仓库服务,并用于应用交付。在Docker容器镜像中可以支持不同类型的应用,无论是PHP应用还是Java应用,也就是说,不同种类的应用可以用相同的命令或API进行管理,规避了不同软件之间的差别,标准化了软件的构建、交付和运维方式。
3.3.4 异步化
在高并发量下,微服务中如果采用同步调用方式,在服务单元处理时间变长的情况下,会导致服务线程消耗尽,遇到无法再创建线程处理服务请求的情况。对于这种情况的优化,除了在程序上不断调优(数据库调优、算法调优、缓存等),可以考虑在架构上做些调整,先返回结果给客户端,让用户可以继续使用客户端的其他操作,再把服务端的复杂逻辑处理模块做异步化处理。
比如,一个下单系统的展现端的逻辑强依赖后端10个服务,包括减库存、生成快照、运查询费、优惠查询等,其中9个都执行成功了,最后一个却执行失败,那么是不是前面9个都要回滚掉?如果这样做,成本还是非常高的。所以在拆分大的流程为多个小的本地事务的前提下,对于非实时、非强一致性的关联业务,在本地事务执行成功后,我们选择通过消息机制将关联事务异步化执行,从而保障整个业务流程的高效运转。
在阿里云上,可以使用阿里云提供的消息队列服务(Message Queue,MQ),帮助业务开发人员完成业务流程异步化。MQ消息队列服务是企业级互联网架构的核心产品,基于高可用分布式集群技术,搭建了包括发布订阅、消息轨迹、资源统计、定时(延时)、监控报警等一套完整的消息云服务。MQ可以帮助用户实现分布式计算场景中服务的异步化调用,帮助服务解耦并进行异步化改造。MQ目前提供TCP、HTTP、MQTT三种协议层面的接入方式,支持Java、C++以及.NET等编程语言,方便用不同编程语言开发的应用能快速接入MQ消息云服务。用户可以将应用部署在阿里云ECS、企业自建云,或者嵌入到移动端、物联网设备中,与MQ建立连接并进行消息收发,同时,本地开发者也可以通过公网接入MQ服务进行消息收发。
3.3.5 高可用
建设应用系统时,特别是建设一些核心和关键的业务系统时,我们需要考虑和评估一些极端异常的场景或故障发生时对整个应用系统和业务的影响。例如,单个机房出现网络或电力故障导致机房无法对外提供服务、某个城市地震、台风等自然灾害等情况。
针对这些极端场景或问题,阿里云提供“同城双机房”和“两地三中心”的灾备高可用解决方案,通过在同城或者异地建立两套或多套功能相同的应用系统,并集成健康监测和灾备切换功能,当一处系统因意外(如火灾、地震等)停止工作时,整个应用系统可切换到另一处备用系统,继续对外提供服务。
1 . 同城双机房高可用设计
图3-4给出了同城双机房架构。其中包括SLB网络接入层、ECS云服务器Web层和应用层、RDS数据库层以及OSS文件存储层。
1)SLB网络接入层:用户通过Internet访问BGP SLB,SLB集群根据用户来源IP段,将流量分配到不同机房的SLB节点上,实现机房间SLB双活。
2)ECS云服务器Web层和应用层:在不同机房分配ECS资源,在不同机房完成对等的Web和应用服务器部署,所有的Web服务器分别挂载到对应机房的SLB上。
3)RDS数据库层:RDS采用主备库模式双机房部署,数据实现近实时同步。Web & APP服务器正常情况下只访问RDS的Active的主库。
4)OSS文件存储层:非结构化数据将存入OSS,如ECS的系统快照和RDS的数据库备份等静态数据会统一上传到OSS存储集群上。
2 . 两地三中心高可用方案
图3-5给出了两地三中心架构。其中,最上层是DNS SLB网络接入层,用户通过外网DNS访问在线集群的SLB,DNS引流到活动的主站点。中间是由ECS集群组成的Web层和应用层,该层在不同地域、不同机房分配ECS资源,并完成Web和应用服务器部署。不同地域的Web和应用服务器分别挂载到相同地域的SLB实例之上。最下面是RDS数据库层,同一地域RDS采用主备库模式双机房部署,数据同步采用RDS主备复制方案,不同地域的RDS之间的数据同步使用阿里云数据传输产品实现秒级复制。Web和APP服务器正常情况下只访问活跃的RDS主库。
OSS文件存储层负责快照文件、备份文件等非结构化数据存储,如ECS操作系统的快照、RDS数据库的备份文件等静态数据,存储到OSS存储服务上。不同地域OSS数据容灾采用OSS数据迁移服务完成。
当机房发生故障时,会优先切换到本地域的不同机房。当因主干网络或其他原因导致整个地域业务受影响时,会将业务切换到不同地域的机房。
3.3.6 云数据库架构最佳实践
前面几节分别从应用负载均衡、可扩展性、微服务架构、异步化、高可用等技术层面做了介绍,本节将结合典型应用场景介绍整体技术架构的设计与实现。
1 . 小型OLTP系统
小型OLTP系统是指数据量小(小于500GB)、单表数据量级别(千万级)、访问量小、操作简单的系统,这类系统主要用于在线处理业务、简单实时数据统计和定期的数据报表(统计的数据量小(10w级)、确定性强的查询)。
针对这类系统,可采用单机RDS+DRDS方案。当遇到数据库读压力过大,可以通过增加RDS只读实例,采用DRDS将部分读流量切到只读实例来解决问题。
这类系统的典型应用场景包括财务、OA办公类系统。图3-6给出了小型OLTP的架构。
2 . 中型OLTP系统
中型OLTP系统是指数据量中等规模(大于500GB但小于2TB)、单表数据量级别(千万以上到1~2亿内)、写访问量较小但读访问量较大、较复杂的系统,主要用于在线处理业务、实时数据统计和定期的数据报表(统计的数据量小(50w内)、确定性较强的查询)。
对于这类系统,通常采用DRDS+单节点RDS+只读RDS方案,DRDS用于对大表进行分表和自动读写分离。
该类系统的典型应用场景包括市级政务平台、互联网金融平台等。图3-7给出了中型OLTP系统架构。
3 . 大型OLTP系统
大型OLTP系统是指数据量大(大于2TB)、单表数据量级别(亿级以上)、写访问量超过单机RDS容量、较复杂的系统,主要用于在线处理业务、实时数据统计和定期的数据报表(统计的数据量小(100w内)、确定性较强的查询)。
大型OLTP系统多采用DRDS+多个RDS实例的方案,DRDS进行分库分表和自动读写分离。
大型OLTP系统的典型应用场景包括部委级业务平台、企业总部级应用系统。图3-8给出了大型OTLP系统架构。
4 . 在线、离线一体化架构
综合型业务系统中的数据量大(超过RDS实例存储容量2TB)、单表数据量极大(达到亿级、十亿级甚至百亿级),需要处理各种复杂的实时、定时大数据报表需求。
综合型业务系统通常包括在线交易、准实时大数据分析和离线大数据分析三个部分。其中在线交易部分采用DRDS+多个RDS实例的方案,准实时大数据分析部分采用AnalyticDB,离线大数据分析部分采用MaxCompute。综合型业务系统的典型应用场景包括各类国家级综合业务平台。图3-9给出了在线、离线一体化架构。