怎么设计开发一个巨无霸单体应用
欢迎转载,请支持原创,保留原文链接:blog.ilibrary.me
前言
一直想设计一个巨无霸单体应用,
- 什么功能都可以往里面塞,
- 功能都可以配置,
- 最好还可以定制,
- 新功能可插拔。
- 最好是单体的,
- 所有的功能都高度内聚,
- 流程都是可配置的。
为什么是单体的?
因为如果要同时满足功能可以配置,可以定制,可以插拔,那么必然会牵涉到自动代码生成。如果是微服务的,每次定制一个功能,要改动好多项目的代码,改动不好同步, 怕是会很痛苦。如果是单体应用,那么只需要敲一下命令行,所有需要修改的地方都自动修改好了。
说到这里,好像问题出来了。上面说的不像是一个单体应用,更像是一个单体应用模板,或者单体应用生成器. 维护这个生成器本身又会成为一个大的负担, 整个流程变长,系统变复杂了. 如果是一个一直在跑的成熟的单体应用,那会简单很多。
架构和微服务
先理解一下以下概念:
- 微服务架构
- 微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言,不同的数据存储技术,运行在不同的进程之中。服务采取轻量级的通信机制和自动化的部署机制实现通信与运维。
- 单体架构
- HTTP/REST, RPC和消息总线.
- 微服务对外应该通过无状态的REST API提供服务。微服务不能主动回call.
- 消息总线因为双向通信的关系,增加了状态的复杂性,增加了耦合性。
- SOLID原则 + DIP(依赖倒置) + CARP(合成服用原则)
- DDD(Domain-Driven Design领域驱动设计), 有点类似MVVM, 多加了一层领域层(VM)层。
- 这种设计好像很少见,可能太重了。
- 领域模型跟技术毫无关系,而是为了更有结构化的拆解和表达业务逻辑
- 六边形架构. 这个词一点也不形象,容易给人误解。我个人理解如下:
- 端口和适配器架构 (又称六边形架构)
- 六边形只是一个代号,没有特殊含义。
- 接口和实现分离.
- API: 应用对UI层提供一套服务接口, API, 供UI层调用.
- 驱动适配器: 应用对数据库层和其它底层(基础设施)提供一套服务接口定义,供它们适配。基础设施通过适配器注入到应用中来供应用调用。
- 前后端分离的Rails App可以理解为是六边形架构的经典实现。
- Rails Console也可以理解为六边形里面的UI(访问)的一种形式。
- Rails Active Record可以理解为一个通配的数据库驱动适配器。各个数据库通过适配Active Record来把自己注入到系统中来,供系统调用。
- MVC
- MVVM
简单一点理解,上面的4及以后的概念都是在单体架构这个框架内讨论的。
微服务和单体架构是服务架构模式,不是代码架构模式。服务是代码之上的一种概念,是一种业务逻辑的抽象,描述和约定了输入什么,什么情况下会输出什么。 具体的功能细节还是由代码实现。
记住软件架构设计里面的一条万金油法则: 没有任何是不能通过多加一层抽象来解决的,如果有,那就再加一层。
K8S和Spring Cloud
- Spring Cloud是应用层的微服务治理框架。对Spring深度绑定,并且很多概念太重,不建议学。
- K8S是基础设施层的服务治理技术. 可以有效屏蔽具体的语言实现,通过容器和强大的私有网络技术解决微服务治理的问题,把服务治理彻底从应用层剔除掉.
- Spring Cloud终究会被淘汰, K8S才是王者。
表 1-1 传统 Spring Cloud 与 Kubernetes 提供的解决方案对比
Kubernetes | Spring Cloud | |
---|---|---|
弹性伸缩 | Autoscaling | N/A |
服务发现 | KubeDNS / CoreDNS | Spring Cloud Eureka |
配置中心 | ConfigMap / Secret | Spring Cloud Config |
服务网关 | Ingress Controller | Spring Cloud Zuul |
负载均衡 | Load Balancer | Spring Cloud Ribbon |
服务安全 | RBAC API | Spring Cloud Security |
跟踪监控 | Metrics API / Dashboard | Spring Cloud Turbine |
降级熔断 | N/A | Spring Cloud Hystrix |
问题
当前软件开发领域面临的问题
- 社会分工越来越细, 软件需要集成的各种服务越来越多.这些第三方服务的继承往往都只需要通过修改配置,改极其少量的代码就可以完成。但是修改配置又是一件容易出错的事情。
- 软件需求越来越复杂。 社交都能玩出各种稀奇古怪的玩法, 电商的玩法也是千奇百怪,各种营销工具就更加不用说了。
- 软件技术层出不穷。每种软件技术都还有自己擅长的方面和不擅长的方面,没有哪种语言或者技术能一统天下. 这个开发者带来极大的学习成本。
- 轮子越来越多. 每个项目都把以前做过的功能重新做一遍,并且还质量不一定有以前做得好。严重浪费社会资源.
为什么会需要巨无霸应用?
因为现在重复的代码太多,轮子也太多。类似的功能反复做,反复造轮子, 浪费社会资源。现在非常流行的低代码,无代码就是非常好的一种避免反复造轮子的形式。
案例分析
挑选一些典型的应用来分析,方便从可扩展性, 可挂载性, 工作流引擎,自动代码生成, 项目模板几个方面来挑选项目分析.
- Redmine, 一个项目管理系统, 有插件扩展机制.
- Spree, 一种可挂载的完整的服务,还可以自定义扩展
- Apache/Airflow, Apache下面的一个开源工作流引擎。
- Apache isis tutorial, isis教程.
- Apache/isis, 一个自动生成UI和API的方案.
- Odoo, 一个开源ERP项目.
- Ofbiz, Apache开源ERP项目,是一个Java框架,包含一个ofbiz plugin.
- Azure, 研究Azure的整体架构,有助于理解怎么设计可扩展的系统。
附加项:
- Expo.
基础原则
必须有一些设计方面的基础原则,保证设计不会跑太偏。
- 限定范围。要明确哪些东西需要考虑,哪些不应该考虑.
- 选一个明确的架构方案, 保证设计沟通的时候有一个主线。
- 现在各种架构多如牛毛。如MVC, MVVM, DDD, 六边形.
- 网上有各种文章解释这些架构。非常方便不同的人理解学习学习这些思想。
- 这些架构属于能高效的传递设计思想,保证架构沟通的顺畅。
- 选用公开的架构有助于和第三方服务代码的集成。
- 要方便测试。
- 东西多了就难免会相互干扰。单元测试是代码质量和逻辑稳定性的第一堡垒。一定要方便测试,并且要有足够的测试覆盖。
- 代码重构也极度依赖于单元测试。
- 不方便测试的方案不能选用。
- 方便调试。
- 一旦系统上线,会有各种奇葩的问题出现。甚至会有很多乌龙出现。如果没有一种非常方便的调试机制,光处理各种线上问题都会把人搞死。
- 数据要好回放.
- 日志要清晰具体。还要方便检索定位.
- impersonate.
- 自我诊断
- 在线数据的自我诊断. 系统跑到一定程度,很多数据就慢慢开始不符合系统的规定了。可能是历史数据迁移的时候未迁移好。也可能是某些数据跑创建的时候抛异常,系统遗留了脏数据。还有可能是管理人员不太合规范的数据操作导致的。这些都应该能被识别出来,如果能自我修复,那是最好的。
- 数据有效性检查是最基础的。
- 要方便接入APM.
- APM可以高效诊断在线系统的性能。
- APM还能高效定位代码出错的位置。
- 方便的文档生成工具.
- API说明文档,swagger.
方案设计
- 巨无霸单体
- 微服务集合
- 低代码应用
- 项目模板/生成器
解藕
即便是单体应用,也要有很好的解藕性,保证在业务发展到一定程度以后可以扩展,拆分。
解藕的方法:
- 前后端分离. 前后端分离保证了业务的代码无关性。
- 前后端通过标准的HTTP请求来通信,只要都遵循HTTP标准, 前端可以随意替换技术方案,后端也可以随意替换技术方案。
- 并且,前端都可以整体替换,也都可以局部小范围替换。后端也是如此。彼此无感知。
- 可以方便整合第三方代码和服务。
- SSO/JWT. 通过SSO/JWT方案,可以打通不同的系统,可以做到服务级别的隔离,节藕程度更加高。
- 用K8S部署.
- K8S部署的好处是在方案替换的时候可以分流,新老方案共存,相互验证。保证平滑过渡。