在架构中,适当的异步是必须的,否则服务之间的交互就会缓慢,迟缓,架构的灵活性,扩展性会很差。随着微服务,分布式及云原生等的流行,异步在架构中将会变得越来越重要。
而响应式架构,强调架构中都以异步为主,有着它独特的优势与魅力,在一些业务需求场景下,它可能是你最佳的选择。
本篇,我将继续阐述我对响应式架构的思考,这一篇我着重讨论,选择一个响应式架构,究竟能带来什么样的收益,又需要团队付出什么样的成本?
只有明白收益与成本,做为架构师,我们才能做出正确的选择。
收益
业界认为,响应式架构的特点应该是:
- • Responsive (即时响应性)
- • Resilient (回弹性)
- • Elastic (弹性)
- • Event-driven (事件驱动)
按照响应式编程的特点,很显然,响应式架构的最大收益是在:架构的性能
所以,如果是一些类似时实会议,在线游戏等,在架构上选用这种模式是非常合适的。
成本
但如我所说,没有任何东西是没有成本的,响应式架构要做到上面这样的效果,是需要付出代价的。
那构建一个响应式架构,你需要付出的成本是:
降低代码的可读性以及可维护性
如我在前面一篇文章已经详细阐述了,异步编程虽然优势是高效,但它的确一定程序上降低了代码的可读性。可读性的降低也自然会降低代码的可维护性。
如果代码都很难读与理解,如何谈的上维护性?
学习曲线增加
相比较于同步式编程的各种语言,框架来说,响应式编程的各种语言,框架,都学习曲线是明显偏高的。
同步式编程是与人的思维一致的,我们理解一个事情就是从上到下,一步一步;而异步的回调也好,流式编程风格也好,都与我们的思维并不一致。
因而这显著的增加了学习曲线。
调试与TDD变得困难
编码中,调试与TDD都是编码中非常重要的行为;调试有助于我们事后定位与发现程序员的代码错误,而TDD则更是可维护软件的必要前提。
但在响应式编程中,无论是调试还是TDD,都变得更加困难。
过往的一步一步的断点式调试并不是很适合异步执行的响应式编程,而TDD单元测试,对于异步的一些实现,也不是非常好实现。特别是Mock或Stub等在异步编程中,很难模拟。
人员培养难度增加
软件行业不断发展与繁荣,对程序员的需求也越来越多,今天开发一个软件是一个团队的事情,并非是单打独斗的行为。
IT行业人员流动本来就非常大,团队人员不稳固,人员培养的困难性几乎是很多团队及公司都普遍存在的困扰。
而使用一些响应式框架及其生态,显著的在这边面增加了更多的难度,第一由于本身就难且小众,懂的程序员本身就非常少;第二使用并不多,很多程序员基于职业生涯的考虑,也并不愿意参与这样的技术中。
生态不够丰富与稳固
Java的世界中,架构师经常听到的一句忠告就是:不要重复制造轮子
这是因为Java的生态太丰富了,并且质量可靠。
但同样的事情放在响应式架构上,可能并不妥当,由于并非主流,相对于Java同步式的生态世界来说,其生态表现为可选项并不多,且很多未有被大规模使用与验证。
选择响应式架构,可能需要在技术基础设施上,花费相当大的精力。
软件的世界没有银弹
响应式构建的宣言非常美好,Responsive,Resilient,Elastic,Event-driven;
似乎只要你选择它,就能构建一个成熟完美可靠的架构与系统。
但事实并非如此。
做为技术人员,我们要明白,响应式架构并非软件世界的银弹,它有许多问题仍有待解决,而且它也压根无法取代非响应式架构。
其它的不说,就以它最具优势的性能而言,不使用这种架构,不代表你在性能上就会出问题,这种假设是完全不成立的。
同步式编程仍然是当前的主流,如果性能都有问题,那同步式编程就不应该存在了。但我们都知道,通过集群,微服务,实例复制等方式,一样能具有非常好的性能。无非是以硬件换性能而已。
对于极少数对响应时间要求极高的业务来说,考虑使用响应式编程是恰当的。而大多数业务场景下,选择响应式编程,可能是得不偿失的行为。
同步 轻量级线程
其实,就性能这一块,除了响应式架构倡导的这种异步编程来说,现在有一些新的方案可供我们考量。
- • Go语言以高效而著名,但它的机制是Goroutines,Goroutines是一种轻量级线程。
- • Kotlin则有协程,也是Kotlin Coroutines
- • Java正在孵化自己的Project Loom,也叫Virtual Thread(虚拟线程)
这些都是使用类似线程暂停的机制与实现,可以称之为“轻量级线程”。
它们一方面性能上高效,不需要使用大量线程来处理并发,而又仍然是同步式编程,岂不是更值得我们考虑的选择。
代码语言:javascript复制Kotlin协程
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L)
println("World!")
}
println("Hello,") // 主线程中的代码会立即执行
runBlocking { // 但是这个表达式阻塞了主线程
delay(2000L) // ……我们延迟 2 秒来保证 JVM 的存活
}
}
协程的代码仍然是同步的,但它并不需要很多线程来支撑。这样的代码没有阅读上的困难性,并且支持所有同步的生态。
END
关于响应式架构,我并未说它没有价值,我只是希望架构师们在选择它时,一定要知道它的成本,切勿陷入技术情节中,被异步流式及函数式编程风格吸引,响应式架构试图构建的软件看似完美,但背后却意味着要付出极大的成本。
也许你以及你的团队无法承受这种代价。这就是你做为一个架构师应当认真思考的。