【整理】系统可扩展性的设计与实现

发布时间 2023-04-17 13:02:12作者: 琴水玉

系统可扩展性是指能够低成本、高质量地在现有系统中添加新功能和优化现有功能。 可扩展通常涉及到全局结构的设计。包括:

  • 选择合适的架构
  • 使用消息系统进行服务解耦
  • 使用代理和负载均衡来确保服务可用性
  • 使用组件和配置化来提升功能的扩展性
  • 使用柔性编程来实现代码的可扩展性
  • 通过灰度发布和蓝绿发布来提升发布的扩展性

架构设计

使用可扩展的架构设计:

  • 建模存储:数据模型、数据 Schema 、完整性和一致性约束定义。
  • 领域驱动:DDD 设计,稳定的精炼的可持续演进的领域模型,六边形架构,聚合根,充血模型。
  • 架构模式:分层、微内核+插件、PipeLine 、事件驱动、订阅-推送、Actor、AKF 等。

微服务

  • 单体应用的问题:直接通过访问 DB 来共用数据库,数据管理容易缺乏明确 owner 而混乱,DB 设计变更难度高;缺乏明确的域的划分,功能边界不清晰,重复功能实现和重复代码难以维护,业务耦合,依赖关系混乱;协作成本高,系统整体稳定性差。
  • 微服务:将单体应用分解为多个具有明确领域定义的业务子域,将每个相对独立的业务子域实现成单独的微服务,微服务独立管理各自子域的问题,采用不同的架构和方案来适配自身领域的问题,最终所有微服务集成起来完成整体应用功能。实现独立自治和发展、模块化、分工协作等。
  • 微服务要解决的问题是服务治理。主要包括:限流/熔断降级、配置管理、日志中心、监控预警、链路跟踪、故障隔离、动态扩容、分流发布、全链路压测、中间件支撑、团队组织架构适配与管理。基本解决方案: RPC 框架和统一应用框架接入。
  • 一个较为棘手的问题:应用一致性。解决方案有:分布式事务、消息补偿、对账和自动修复。
  • 分布式事务解决方案:2PC,TCC,本地消息表(消息解耦)、消息事务。

消息系统

代理

  • 代理的目标是性能、路由、安全、透明、迟加载、隐藏复杂实现细节。
  • 代理技术:四层代理(IP+Port,LVS)、七层代理(IP+Port+Application,Ngnix)。四层代理性能更高,七层代理更灵活。
  • 代理模式:静态代理、动态代理。

代理模式

  • 参与者: 目标实例、代理实例、代理逻辑。 目标实例是已知的,需要代理的逻辑需要指定,代理实例需要生成。
  • 静态代理: 编写和目标实例具有相同行为的代理实例,并将对目标实例的请求转发给这个代理实例上。由于总是需要为目标对象手动编写代理实例,因此称为静态代理。静态代理容易理解,但不够灵活。
  • 动态代理:动态生成和目标实例具有相同行为的代理实例,并将对目标实例的请求转发给这个代理实例上。动态体现在可以为不同行为的目标对象生成相应的代理实例,而不是手动去编写代理实现。常用动态代理有 JDK 代理和 CGBLIB 代理。
  • JDK 代理:通过 java.lang.reflect.Proxy.newProxyInstance + 反射机制实现。适合对接口代理。代理逻辑通过 InvocationHandler 接口定义,Proxy 将实现 InvocationHandler 的实例传入构造器,生成动态代理实例。代理实例的类继承自 Proxy 。Proxy 通过proxyClassCache 来管理 ProxyClass 和 ProxyFactory ,并在 getProxyClass0 的时候去缓存 ProxyClass 的信息。

HTTP代理

  • HTTP 代理就像客户端与服务器之间的拦截器。既充当客户端的角色,又充当服务器的角色。代理可以级联,组合使用。可以通过 Trace 方法和 响应头的 Via 首部来追踪报文途径的网关和代理(Via 有安全与隐私问题)。
  • HTTP 代理的作用:过滤(不宜内容)、访问控制与审计追踪(安全)、安全防火墙(安全)、流量监控(安全)、缓存(性能,降低网络开销和拥塞)、反向代理(性能)、内容路由器(增值服务)、转码与压缩(国际化与性能)、匿名(安全与隐私)、路由与负载均衡(稳定性)。
  • HTTP 代理的部署: 出口(LAN 出网点,过滤、安全)、入口(缓存与性能)、边缘(反向代理)、对等交换点(缓存与安全)。
  • 使用 HTTP 代理的方式: 浏览器配置、交换或路由设备拦截、修改 DNS 、重定向。客户端代理配置 -- PAC 文件(提供一个URI, 指向用 JS 写的代理自动配置文件,会动态计算适合的代理配置);自动代理发现 -- WPAD ,按顺序尝试 DHCP(动态主机配置协议)、 SLP(服务定位协议)、DNS Known Hosts、DNS SRV 等技术,自动发现合适的 PAC 文件。
  • HTTP 代理的问题及方案:客户端发给代理的 HTTP 请求报文里应当是包含主机名的完整 URI。但客户端并不总是知道对方是代理,或者并不知道代理的存在。因此通用代理需要进行“缺失主机名的部分 URI 补全”,拿到主机名拼成完整的 URI(没有代理时浏览器也会做类似的事情)。某些代理会对 URI 做细微修改,影响互操作性。代理的容错机制(解析到的主机是已停用服务器时)。
  • 代理认证:客户端发送请求,代理发现没有认证,会返回 407 响应码,客户端拿到 407 后搜索和拿到证书,重发请求,代理认证通过。

组件和配置化

  • 组件化: 配置化的基本前提。组件需要定义良好的行为规范和接口规范。
  • 流程编排:需要将整个流程划分为若干阶段,定义每个阶段的行为和目标、阶段之间的连接。
  • 动态语言脚本。比如订单导出使用 Groovy 脚本配置报表字段逻辑。 脚本注意做成缓存对象,避免可能的内存泄漏。
  • 选项参数。选项参数的原型是命令行参数。用户可以通过选项参数来选择策略、调节性能等。
  • 规则引擎。 将业务逻辑表达为若干条规则,然后用工作流将规则集合串联起来。

柔性编程

落实到编程层面,即是:

  • 组件化编程:将系统功能分组、分类、模块化,提炼成可复用的组件。
  • 基于接口编程:抽象对象行为,通过接口来交互,而不通过具体实现。
  • 设计原则与模式:应用设计原则(SOLID, KISS)指导,使用设计模式实现。
  • 占位符思想:规范、识别、注册、使用。
  • 持续小幅重构。

发布

  • 分流发布:灰度发布、蓝绿发布。小批量验证。分流系数可动态配置和生效。
  • 容器化部署。