一、工程师们超越时代的应用架构设计之美
记得自己在2003年把握住了宝贵的面试机会,成为了一名Java程序员,那个时候我参与的项目还在使用JSP,Servlet,JavaBeans和JDBC所组合的最原始的Web应用框架,Spring framework的流行还要等到两年以后。
作为原始的应用框架,如果是一位牛逼的资深级程序员,一定能写出最整齐、简洁与极致高性能的代码,但是对于需要更多人参与进来的软件工程来讲,原始的框架很容易导致混乱,因此各种Web架构总是被顶尖的工程师们不断更新迭代,推陈出新,引导大众工程师们去关注业务本身而非琐碎的技术问题。
然而在那个IT时代,奔腾CPU尚在,还是32位操作系统的天下,内存普遍不超过2GB,局域网的带宽还是10M/100M自适应的水平,若要工程师参与的项目不去关注琐碎的技术,不去操心底层服务,只关注业务本身,在当时其实有些不可思议的事情。
但是工程师们对科技生产力提升的尝试远远超出了我们的想象,EJB(Enterprise Java Beans)技术作为超越时代的分布式应用架构设计,就在我们入行的时候被广泛应用与尝试,尽管2.0版本的尝试最终失败了,还催生出了更伟大与流行的Spring框架。
但是我们重新翻看那一段历史,只是为了欣赏那种企业级应用的分布式架构之美,钦佩天才般的科学家和IT工程师们的创造力以及对于未知领域的勇敢尝试。
什么是EJB呢? 构件用于封装业务逻辑,使开发人员无须再操心数据库访问、分布式事务处理、安全性、多线程并发问题等琐碎任务的编程,这是在1998年提出了来的架构设计,在2001年2.0版本的发布后,已经在全世界的范围进行了广泛尝试。这种目标理念就算放到现在也一点都不过时,有时候我们不得不感慨,后人只是不断实践与优化前人所提出的设想。
而我在2004年也在使用EJB2.1版本曾参与开发了ERP系统,实际上并不成功,EJB自身在部署方面的臃肿,实体Bean在分布式环境下的极低性能,导致了工程最终不能如期交付,而且项目中解决技术实施复杂性所花费的精力远远超过了解决业务自身的问题。
最终在《Expert one on one J2EE development without EJB》,Spring之父那本去EJB的经典之作出版后,随着Spring IOC、AOP技术配合Hibernate形成了的巨大冲击下,EJB所构建的分布式理想彻底垮塌。
如下图1 EJB3应用架构示例所示:
图1 EJB3应用架构示例
这是我在2010年参与的一个电商项目所抽离出了分布式应用架构的一部分应用案例,这时候已经开始应用EJB 3.0,部署在云端跨区域的两台AWS EC2虚拟节点,分别管理着各自的Amazon RDS数据库。
用户有可能使用独立安装在操作系统上的软件,也可能使用浏览器通过Web访问,例如:订单逻辑进行业务逻辑组件化以后通过远程(Remote)或本地(Local)的发布,就轻松应对了不同客户端的需求,这种RMI(远程方法调用)与目前流行的Http协议的Restful风格架构走了完全不同的两种风格。
订单在调用支付组件的过程实际上是进行了一次中间件支持的分布式事务的操作(JTA事务),通过这种手段,实现了不同网络区域的双库数据的两阶段事务提交与回滚。
如果从架构图中看,这种分布式架构中每个EJB业务组件都是独立部署在一个上下文容器中运行,通过网络互相通讯与协作,难道它不够优雅吗?但为什么最终还是无法成为行业主流而逐渐没落呢?这其实是我们思考的关键。
分布式架构的复杂性在当时是非常重要的原因,但我认为更关键的原因在于中间件服务的依赖性远大于Spring生态,这些中间件服务其实就是一些厂商根据JavaEE规范所提供的中间件服务产品,例如:JBoss、Glassfish等。
我们可以看到Spring框架IOC、AOP技术,后来发展出SpringBoot、SpringCloud,本质上都是在简化应用所依托的中间件服务。
这样对于开发者来讲,在整个开发过程会更为灵活与自由,输出的软件系统也更加与平台无关。
从这里就能看到一种是流行的趋势,那就是无服务化(serverless)的趋势,本质上就是在不断解放开发者的生产力,然而基于JavaEE规范的EJB尽管经历了三代的变革,但其本质仍然是重度依赖有服务的环境,因此尽管EJB的分布式架构设计超前了二十年,但仍然因其不适应技术潮流而被逐渐淘汰掉。
二、当今微服务、分布式和容器技术的演进融合
随着以Amazon为首的云计算平台在IT基础架构中扮演着越来越重要的角色,工程师们主要谈论的Web应用架构也逐步提升为云服务架构,这时候我们已经进入到了云时代,那么曾经叱咤风云的JavaEE规范生态与大多数中间件供应商逐渐被云平台所替代,在云平台之上则是更为轻量化的微服务架构与容器化技术承载了主流的云应用项目的运行。
但是分布式架构并没有停止脚步,而且在软件架构中变得越来越核心,为什么分布式会越来越重要呢?其实在上面我们提到的EJB架构已经在二十年前就开始了分布式的尝试,只不过那个时候摩尔定律依然有效,软件系统在单机部署和运行会更加的稳固,分布式更多是以双机热备的形态出现,再加上还没有千兆甚至万兆带宽的普及,分布式的需求并不强烈,性能拉胯也非常明显。
然而在当下,随着互联网的用户规模化,导致了高并发、大规模数据引发的问题频生,往往单点故障导致的高可用性会更致命,多路并行提升性能的需求很强烈。另外项目中软件系统的开发规模也在不断膨胀,单体架构的软件,其工程化组织管理必然会随着长期维护而走向臃肿与混乱。
因此才有了微服务架构的用武之地,微服务具有哪些特点呢?
微服务专注小的个体问题,形成服务,通过松耦合的通讯机制协作起来,解决更大的问题
微服务倾向于拆分,也就是将单体应用尽量拆分到一个适当的粒度,形成个人或小团队去关注独立的服务个体;
微服务的实施模式是自底向上型:不同的小团队分配不同的微服务进行开发、构建、部署、发布。
因此面对上述的难题,微服务架构为开发者打开了一个更为适度、自由、灵活的新局面,在微服务项目具体实施的过程中,它又与分布式架构紧密结合在一起。
在前几年的时候,我有机会负责架构设计了互联网医疗平台的项目,这个项目就是典型的微服务架构案例。它的特点在于将医疗信息化的灵魂装进互联网的外壳中,因此医疗业务的复杂性与互联网的技术平台性需要同时兼容。
那么大量的外部系统需要与平台对接,甚至频繁地升级,因此就必须寻求一种架构上的分解,所谓的分而治之,才能保证主体系统不会因为局部的频繁变化而变化,而且不同服务具有不同的发布策略,并由不同的团队适当地协作完成。
这种多维度的复杂需求叠加,对于单应用架构必然会导致臃肿与迟缓。
如下图 2 互联网医疗微服务与分布式架构示例所示:
图2 互联网医疗微服务与分布式架构示例
我们开发的互联网医疗平台被切分成了医生端、患者端、平台端等N多个微服务实例所组成。由网关对互联网的不同用户访问重定向到不同版本的微服务实例之上,网关与微服务之间、微服务与微服务之间、微服务与各个数据分库之间就形成了一种分布式的拓扑结构,另外承载高并发的关键微服务也可以由多实例副本形成均衡负载。
从上图中的实例我们可以看到微服务模型单元要比过去EJB模型单元更粗粒度,是以服务为基准而非组件,那么就不会像过去经典JavaEE架构所引发的一种强制性措施,要求更细粒度的组件必须分布,而是给予架构师更为灵活的自由度,去划分适合大小的服务粒度并进行适当的分布。
另外微服务是一种架构指导方法,而不是JavaEE那种强制规范,那么就不会对开发者硬性规定底层需要依赖什么样的服务,这就保证了输出的软件具有平台无关性,也就促进了软件服务在云平台的快速普及。
在这些微服务实例的幕后,其实就有了不同开发团队、QA人员、运维工程师的共同协作实现系统的发布升级,这个过程就是对DevOps过程方法的最好实践。
如下图3 互联网医疗平台微服务的版本升级案例所示:
图3 互联网医疗平台微服务的版本升级案例
说到这里,我们就会觉察到应用软件架构的发展其实就是不断追求系统的独立性与平台无关性,并且通过分布式不断增加与均匀算力,通过功能模块拆解不同提升分工效率。
软件应用架构发展到了微服务这个阶段看起来应该很不错吧,可是并没有停止工程师们继续优化的脚步,现在又面临一个问题,那就是采用更多机器组成的分布式网络节点,如何才能优化计算资源的使用?
例如:我们在一台服务器上想要部署两个Tomcat服务,其实就等同于操作系统运行了两个进程,这两个进程共享系统资源,就会存在资源争用问题,也会导致一些资源配置上的冲突,尤其是起到相同作用的多组程序。
这时候容器化技术就应运而生,例如:Docker,主要机制之一就是通过Linux的namespace机制实现了资源隔离,你就能在有限的云资源上跑出很多服务,而且容器作为操作系统上的一个进程,容器之间互不影响,并且可以针对容器的CPU、内存等计算资源进行预先分配,简单一条命令就能部署和运行一个服务。
这样就极大简化了服务在云服务器上的部署难度,提升了更高的效率,甚至我们可以同时部署多个版本的服务形成生产与测试环境的并存。
如下图 4 计算节点多容器部署示例所示:
图4 计算节点多容器部署示例
我们在一台AWS EC2服务器部署了12个Docker容器,其中有8个是可运行的容器,这就包括了互联网医疗两组不同的微服务:生产级与测试级,整个分布式网络的计算节点算下来,大概有50多个容器服务在运行。
为什么要将生产与测试尽量结合在一起运行呢?
因为我们仔细统计过我们在做互联网医疗平台的时候,对接了:分布式对象存储、短信服务、地图服务、推送服务、多种银行支付、公众号与小程序、医院HIS、Pacs、Lis、...。
若单独搭建线下的测试环境,那么测试组最终测出结果与实际生产环境有偏差,总是因为各种外部因素而无法弥合,因此尽量让开发测试与部署发布结合的更自然,这也是DevOps逐渐混淆开发、测试、部署与发布之间界限的精髓所在!
就是因为容器技术的支撑,让生产环境与测试环境可以无缝结合成为了可能。
三、展望云服务架构的未来
通过上述各章节的描述,我们可以看到应用服务架构从二十年前JavaEE标准需要重度依赖有服务的时代,逐渐演进到如今Amazon引领的Serverless的时代,这是无数工程师、开源社区和商业公司的共同努力。
那么我们畅想一下云服务架构的未来,我认为基于云平台的无服务架构应该会成为主流模式,无服务这是让开发者更加关注业务本身,而非基础设施与琐碎的基础技术。
但是对于开发者来讲,无服务的优势在于集群化的分布式计算资源的自动调度,那么分布式架构必然会成为绕不过去的复杂问题,对于分布式架构的复杂特性又会让很多开发者变得理解艰难,例如:CAP定理、副本机制、分区、多路并行、单点故障等等。
因此必然云服务厂商会在这个方面的基础设施层进行发力,例如Amazon EKS就是在创建和运行K8s集群提供了一整套分布式环境下的容器编排的解决方案,目标就是让开发者不用关注太复杂的分布式问题。
我相信这种去分布式复杂化的基础设施必将对应用软件架构起到越来越重要的作用,也会变得越来越流行。