vlambda博客
学习文章列表

领域驱动设计+ Reactive:软件开发最新趋势解析


Photo @https://blog.xkcd.com/page/1/


脚踏实地“敲代码”,同时,不忘偶尔仰望下星空。

本文根据阿里云原生团队资深技术专家陈立兵 (花名:雷卷)的直播整理而成,从领域驱动设计(DDD)、Reactive、Service Mesh 和代码智能等几个方面展开,聊一聊他眼里的软件开发的发展趋势。(阿里巴巴研发效能峰会即将开始,欢迎点击文末阅读原文了解)。


今年 4 月份的时候,InfoQ 发布了软件架构与设计的趋势报告。InfoQ 在技术趋势报告中将软件架构分为了 4 类,如下图所示,从左到右依次是创新者(Innovators)、早期采用者(Early Adopters)、早期大众(Early Majority)和晚期大众(Late Majority)。在报告中可以看出,很多技术如微服务(Microservice)、领域驱动设计(Domain-driven Design)等已经非常流行,并成为如今软件开发行业的主流了。对于早期大众和晚期大众部分的架构设计而言,很多部分与领域驱动设计相关,比如微服务、CQRS、事件驱动等。

领域驱动设计+ Reactive:软件开发最新趋势解析


在本次的分享中,也会介绍为什么领域驱动设计相关的技术会在InfoQ技术报告中占据如此重要的位置。并且也将会根据领域驱动设计展开到其他相关技术,比如 Service Mesh 等。

 

领域驱动设计(Domain-drivenDesign)


GitHub 上有一个 DDD 实践指南,从下图中可以看到领域驱动设计所解决的问题从架构设计一直到代码层级,所以对于整个软件研发周期来说,DDD 方法论还是比较流行的。而指导微服务划分的一个重要理论基础就是领域驱动设计。之前谈到领域驱动设计,大部分时间总会探讨如何识别限界上下文,如何使用战术或者战略方法等,而本次分享中则会介绍一个主流的趋势,那就是 DDD + Reactive 。


领域驱动设计+ Reactive:软件开发最新趋势解析


之前的时候,领域驱动设计有分层结构、事件驱动等概念,但是也存在一个很大的问题就是将单体应用划分成多个应用的时候,如何解决应用之间的通信问题。领域驱动设计中有限界上下文映射(BoundedContext Map)的概念,来解决两个限界上下文之间的通信。之前 DDD 谈到的解耦合方式主要是基于消息实现异步化的理论支撑,但是具体如何去做却没有具体的技术栈帮助大家实施。当然了,针对于每一种语言而言,具体实现可能不同,因为一些语言是纯异步化支持的,可能已经内置了协程支持,因此在不同的技术栈里面会选择不同的解决方案。本次分享中比较偏向于 Java 的技术栈,但是需要强调的是 Reactive 并不止针对 Java 。


领域驱动设计+ Reactive:软件开发最新趋势解析


Reactive 的好处在于使得两个限界上下文之间的通信变成一个技术栈,这样就可以使用Reactive 实施了。JDDD 是一个新的项目,其基于 Spring Data 实现。JDDD 也是一个开发包,其主要思想是让开发者通过开发包以代码的方式来呈现 DDD 要表达的思想。前面所提到的微服务则非常依赖限界上下文,借助它来确定边界并进行划分。此外,在 Could Native 和 FaaS 方案都会提到是面向事件驱动的,但是事件(Event)这一概念在领域驱动设计中很早的时候就已经提出来了,如果涉及到事件或者异步化相关的事情,就需要关注事件和领域驱动设计的关系了,当然目前来说,这一点已经非常成熟了。还有一点就是 DDD + CQRS , CQRS 简单理解就是读写分离,比如 MySQL 数据库使用 Master-Slave 机制使得读数据和写数据分开, Master 主要负责写入, Slave 主要负责读取,而 CQRS 也会在 DDD 中发挥很大的作用。目前来看,在整个架构设计里面,领域驱动设计是非常主流的,能够帮助我们解决一些设计上面的问题。

 

Reactive


DDD 将应用划分成多个小的限界上下文,接下来需要解决如何通信的问题了。而 Reactive 就提供了不同应用之间进行通信的指导方案,当然了 Reactive 也提供了对应的技术解决方案,比如 Async 能够提供全异步化支持,当然这里的异步化只是说基于异步化技术。此外,还有 Observable 模式,可以说这是一个架构和设计的模式,这一模式应用很广泛,最早是由微软提出的,比如一个表格发生变化,其他表格也会根据这个表格发生相应的变化,这就是 Observable 观察者模式。针对 Java 程序员而言,可以发现 Spring 在主推 Spring Reactive ,很多技术方案都是和 Reactive 相关的。此外,还有 Message 和 Event Driven,FaaS 和 CouldNative 在做通讯的时候都需要根据 Message 和 EventDriven 进行设计。

 

领域驱动设计+ Reactive:软件开发最新趋势解析



RSocket


在InfoQ的软件架构和设计趋势图中,在早期采用者阶段有一个Function Programming ,在早期大众阶段则有一个 ReactiveProgramming ,而在创新者部分则有一个 RSocket & Reactive Streams ,大家对于这个技术可能了解比较少,因此在本部分简单介绍一下。如果大家关注 Spring 官方站点,那么可能了解 Spring 5.2 版本已经内置了 RSocket 的支持,但是并没有很多相关文档来介绍相关技术。前面提到, Reactive 是让 DDD 的通讯模型有了理论基础,所以这个 RSocket 协议就是为了实现 Reactive 理论基础设计一个通讯模型。


如果大家了解 Reactive 语义,就会知道有一个背压模式。当然,背压模式与断路保护模式类似,两者的机制都是为了解决限流问题,因为消费方或者服务提供方因为突发流量不能支撑导致应用雪崩或者宕机。传统模式最早使用消息队列,是消息提供方主动将消息推送给消息消费方,此时存在的问题就是消费方消费不了,但是消息提供方还会继续推送消息,使得消息消费方无法支撑导致系统不稳定。


后来出现了 Kafka ,其使用 Pull 模式,也就是需要多少条消息就去拉取多少条消息,因为 Kafka 支持消息堆积。但是使用Pull模式则需要设置时间间隔,此时出现的问题就是消息轮空怎么办?也就是当发送消息时发现消息没有就会轮空,此时就会不停地向 Broker 拉数据,这样就会造成网络浪费。所以在 Reactive 里面的背压是将原来的推送模型增加了开关,当消息到达一定数量之后就不要推送了。这种主动推送机制的好处在于性能特别高,不需要去拉取,也不需要保存消息位点。总之,背压机制能够设置所能够支持的最大拉取数量,如果在阈值的范围内能够做到性能的增高,同时也能保护消息的消费方。


领域驱动设计+ Reactive:软件开发最新趋势解析


在通讯方面,RSocket 有四个模型,比如 Request 和 Response、发布-订阅模型、日志采集等。在使用 DDD 实现应用划分之后,两个应用之间需要进行通信,而 RSocket 就提供了四个模型,基本涵盖了应用之间通信的场景,此外还提供了 Metadata Push ,比如所有的集群变化或者需要做限流都可以通过 MetadataPush 来实现。


RSocket 还可以做对等通讯,传统的通讯模型都是 Client-Server 模式,但是在 RSocket下通信都是对等的,没有 Client 和 Server 的分别,相互之间都可以进行消息发送。前面所提到的 RSocket 的设计模型和理念都是为了解决基于 Reactive 通信应用的各种问题。虽然 RSocket 这个技术方案并没有得到很好的推广,但是已经有很多技术社区提供支持了,比如 Spring 在 5.2 版本中就增加了对于 RSocket 的支持。因为推出的通信协议是 Reactive 的,但是背后需要涉及到数据库操作或者其他等如果不是异步化的,就需要进行处理, Spring 在今年发布 Spring Boot 2.3 的时候就完善了对于 Reactive 的支持。举例而言,从最前端的网关层通过 Spring 的 Web Flask 就能够保证是 Reactive 的。



中间件、通讯模型以及 NoSQL 产品都是支持异步化操作的,能够解决高并发问题,这些特性在很早的时候就已经添加到 Spring 里面了。此外,在开发的时候还有一点比较关键的就是数据库,每个Java程序员在访问数据库的时候首先要设置数据库连接池,需要考虑数据库设计方案以及考虑哪一个连接池性能最高等问题。这是因为数据库不属于异步化的,需要通过创建多个连接来解决并发的问题。因此,在 Spring 的 R2DBC 里面增加了对于数据库异步化的支持。目前Spring能够支持数据库的异步化了,但是还需要一段时间的等待,因为 R2DBC 和 JDBC 一样,也是一个规范+驱动,但是基于 JDBC 的上层框架非常多,比如 Hibernate、MyBatis 等,而基于 R2DBC 的框架并不多,目前只有一个 Spring Data R2DBC 。因此,国内很多的开发者首选还是 MyBatis 。如果存在实现异步化的意向,那么基本上是从最开始接入的网关层协议一直到 NoSQL 数据库,包括存储文件系统,全部都实现异步化,这些目前也比较成熟了。

 

Service Mesh


再回到最开始 InfoQ 的软件架构与设计趋势,可以看到大多数与 Cloud Native 相关,比如 Serverless、Service Mesh、HTTP/2 和 gRPC 等,这些概念非常火爆,因此可以说 Service Mesh 在目前的技术趋势中占据很高的地位。典型的 Service Mesh 架构还是基于 Istio+Envoy 的 Sidecar 经典架构。后来出现了 Dapr,它与 Isito+Envoy 架构的区别在于 Envoy 的 Sidecar 可以理解为代理,其目的在于将服务连接起来, Dapr 也会有一个 Sidecar ,这个 Sidecar 不只是做代理那么简单,而是可以帮助开发者做非常多的事情,比如使用新的开发语言实现应用,但是 Kafka 或者 NoSQL 数据库并没有提供对应语言的 SDK ,或者即便提供了对应语言的 SDK ,但是这些 SDK 并不稳定,此时选择一些新的技术就会受到一些约束。举个例子,如果大家做一些大数据相关的工作,比如 HBase、Hadoop 等都是 Java 相关的 SDK 最稳定。而 Dapr 的 Runtime 可以很好地与外部系统交互,比如应用 gRPC 与 Dapr 的 Sidecar 通信,也可以和 Kafka 通信,将从 Kafka 中获取的数据传递给 gRPC ,并且 Dapr 的 Runtime 使得开发者不需要了解通信协议背后的细节。


领域驱动设计+ Reactive:软件开发最新趋势解析


这样的设计比 Isito+Envoy 架构要更好,因为如果只做代理,那么在客户端还需要做大量的数据处理工作,而如果放到 Proxy 上来实现,就会更加复杂,此时就会变成 Dapr 的 Runtime。此外,阿里巴巴最近在做一些尝试就是 RSocket Broker ,其基于 Reactive Mesh 实现,其与 Sidecar 结构不同,因为大多数基于基于消息或者事件驱动的都只需要发送消息即可,而 RSocket Broker 提供了应用之间的一揽子通信协议,不需要再选择其他通信协议了。对于以上三种技术方案应该如何选择,大家应该根据自身的实际情况进行判断,因为技术选型没有绝对的正确,但是目前来说是 Isito+Envoy 架构收到了大多数人的欢迎,因为这与 Kurbernates 整合比较方便,并且目前已经提供了比较完善的基础设施。


FaaS


对于企业而言,可能核心系统只有几个,但是总会有一些长尾的应用。如果之前没有很好的 FaaS 方案,那么可能会做一个大而全的系统将这些小功能融合在一起,此时造成的问题就是修改代码的困难,造成很多的麻烦,也不利于技术革新。FaaS 的好处在于可以将函数部署到边缘,比如 CDN 边缘或者 Edge 端。在通信部分, FaaS 主要是 Message+EventDriven 的,而目前很多的通信协议会用到 gRPC ,因为 FaaS 本身要求响应速度比较快,因此对于通信协议具有一定的要求。gRPC 的接口相对比较底层一些,在此之上还做成了 Reactive gRPC 能够更方便大家通过 Reactive 来操纵 gRPC ,但是在写代码的时候可能并不知道是底层是基于 gRPC 的。在技术趋势里面还有一个叫做 AsyncAPI ,其实在大家工作中,很多框架都支持从代码直接生成 OpenAPI ,比如 SpringDoc 等。


领域驱动设计+ Reactive:软件开发最新趋势解析


目前还有一个技术趋势是 FaaSWebAssembly ,其也是和 FaaS 结合的。众所周知,如果 FaaS 不是高频使用的话,会出现容器拉起时出现一定等待时间的问题,但是在某些场景下,对于等待时间存在比较严格的限制。那么此时如果使用传统容器的解决方案往往难以实现,而 WebAssembly 这种技术解决方案就能够充当函数的钩子,能够满足上述需求。同时,由于 WebAssembly 也是 W3C 新推出的规范,和之前的 HTML、CSS、JS 网页三剑客结合成为了网页四剑客。此外,在 FaaS 部分,还有最近推出的 Deno 可以很好地支持 WebAssembly 机制。而因为很多开发同学会关心 Rust 这一编程语言,而 Deno 对于 Rust 具有良好的支持,因此很多人会选择使用 Deno 作为 FaaS 的 Runtime 来支持 FaaS 的运行。

 

代码智能


目前还有一个技术趋势就是代码智能,通过前面所提到的技术架构,能够满足企业的架构选型,那么接下来就肯定会涉及到代码。对于代码而言,主要有两点,一个是代码生成,另一点是手写代码。代码生成主要是脚手架,能够快速将代码的框架生成,而不需要自己写配置项等,目前这项技术已经非常成熟了。这部分的工具主要有 IntelliCode 、Codota、Kite 以及各种 Cloud IDE 等。在写代码的时候会遇到的问题就是技术往往变化很快,每间隔 4、5 个月就会推出新的框架或者技术,而因为开发者关注的是开发效率,所以即使有 Code AI 能力,但是在某种程度上还是会依赖 Cloud IDE 特性。因为 Cloud IDE 在跟进某些技术上面的速度比较快,它往往能够很快知道客户新的需求是什么。

 

领域驱动设计+ Reactive:软件开发最新趋势解析

Code Generation +IDE


目前有很多的代码生成工具,帮助大家解决框架生成问题。比如Scaffold,start.spring.io是可扩展的代码框架生成器。Code Generation是Java自带的 Annotation 机制,这一点可能大多数同学没有关注,但是在很多的场景下也会使用到,其生成的代码能够做到性能更高。此外,目前很多的 IDE ,比如 JetBrains+VS Code 都有集成的工具,帮助开发者快速生成代码框架结构。



往期推荐








技术琐话 


以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。本号由坐馆老司机技术团队维护。