谈谈我对SOFA模块化的理解

21次阅读

共计 2496 个字符,预计需要花费 7 分钟才能阅读完成。

今天我们谈谈 SOFA 模块化,首先看一段 SOFA 的介绍:

SOFABoot 是蚂蚁金服开源的基于 Spring Boot 的研发框架,它在 Spring Boot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 Spring Boot 的同时,SOFABoot 提供了让用户可以在 Spring Boot 中非常方便地使用 SOFA 中间件的能力。

在接触 SOFA 的模块化概念之前,我对服务端开发的模块化认知停留在“模块化”这个层面,我通常会按照下图所示的结构组织自己负责的应用的代码。

在 Spring 体系中的模块(Module),就是通过不同的 Spring 上下文来管理各自模块中的 bean,在开发和编译期实现模块化,但是在运行时整个应用还是在一个 Spring 上下文中,这些代码还都是被一个类加载器加载的。

在图 1 中,我在 manager 模块中定义的 bean,在 service 中可以随时引用,而不需要关注这个引用是否合理;这样的开发模式在应用还小的时候没什么问题,不过,随着业务的发展和应用的升级,这些模块之间的引用关系会越来越复杂而无法管理(这时候应用也就成为了一个“大泥球”),当某一天需要对应用进行服务化拆分的时候,就需要花很大的精力去理清不同模块之间的耦合和引用关系。SOFA 的模块化特性就是为了解决这个问题而出现的,这种特性可以强制开发者在增加两个模块之间的引用关系的时候进行仔细的设计和思考。

SOFA 的模块化

SOFA 的模块,是一种可运行的模块;普通的 Spring 项目中的模块,则是普通的 Jar。一个完整的 SOFA 模块和一个普通的 Jar 包有两点区别:

  • SOFA 模块包含一份 sofa-module.properties 文件,这份文件里面定义了 SOFA 模块的名称以及模块之间的依赖关系。
  • SOFA 模块的 META-INF/spring 目录下,可以放置任意多的 Spring 配置文件,SOFA 会自动把它们作为本模块的 Spring 配置加载起来。

使用 SOFA 模块化特性后,两个模块之间的 bean 无法直接通信,需要使用 SOFA 的通信协议进行通信,SOFA 支持两种通信协议:

  • jvm 调用,两个模块是在同一个 JVM 虚拟机中运行,无需经过网络调用。
  • rpc 调用,两个模块不是在同一个 JVM 虚拟机中运行,甚至不是在一台机器上,需要经过网络调用。

SOFA 为开发人员提供了三种形式的发布和引用服务的方式:xml 方式、注解方式、编程方式。我现在用的比较多的还是 xml 方式,原因在于 SofaService 和 SofaReference 都是只支持 jvm 服务的发布和引用,而 xml 方式则可以支持两种形式的调用。

SOFA 模块化对开发模式的影响

SOFA 提供的模块化解决方案,既实现了真正的、运行时的模块化,又没有过度引入 OSGI 的复杂度,是一种有效的折衷方案。接触 SOFA 到熟悉 SOFA 的过程对我的开发思路影响很大,但是在熟悉了 SOFA 的模块化思想之后,我发现这个特性对于我平常的开发工作有几个好处。

安全的近远端架构

为了降低服务端的压力,我们需要提供一个 SDK 供业务方使用,在之前 Spring 体系下,这种架构也是可以执行的,但是有个弊端——SDK 中应用的类和 JAR 包对于业务方的应用来说也是可见的,在某些情况下会出现冲突或引入潜在的 BUG。

使用 SOFA 的模块化特性,我们提供的近端包虽然还是跟业务方的应用共享一个 JVM,但是在类加载器层面实现了隔离,对于业务方来说,他们只需要知道是使用了我们的哪些接口,而不需要关注我们这个 SDK 引入了多少三方 JAR 包。

更好的代码共享

利用 SOFA 模块化,要实现代码共享,可没有 Spring 体系下那么简单——直接引用给一个模块就可以。在 SOFA 中,要进行代码共享,通常有两种情况:(1)近远端代码共享(2)管理时和运行时代码共享。

以第(1)种情况为例,某个组件近远端都是一样的,为了避免代码重复,我们如何实现代码共享呢?我现在的做法是:在一个公共的 test-core 模块中定义需要共享的接口和实现类;这个 test-core 模块不能定义为 SOFA 的模块,只能定义为一个普通的 JAR 包;然后在需要使用该接口的 SOFA 模块(近端模块和远端模块)中,分别声明和引用那个共享的 bean,示例图如下所示。

这里的重点在于:

  • javaadu-core 模块中是一个普通的 JAR 模块,只有接口的定义和实现,在这个模块中没有对 bean 的声明
  • javaadu-remote 模块中是一个 SOFA 模块,在这里引用了 javaadu-core 中的接口和实现,这里需要对 bean 进行声明(bean 声明)和发布(sofa-service),当前应用的其他模块要想使用该接口,只需要引用 javaadu-core 和 javaadu-remote,然后使用 sofa-reference 引用该接口就可以,这里一般是 jvm 调用;其他应用如果想使用该接口,并且没有近端需求的话,则需要引用 javaadu-core 和 javaadu-remote,并使用 sofa-reference 引用。可以看出,SOFA 模糊了跨应用调用和应用内调用的概念,模块化做得很彻底
  • javaadu-client 模块是一个 SOFA 模块,也是近端模块,在这里也需要自己定义和使用 javaadu-core 中的接口和实现。

总结

本文主要介绍了 SOFA 开发框架与 Spring 体系区别最明显的一个特性:SOFA 模块化,通过每个模块一个 Spring 上下文的形式,实现了真正的运行时隔离。

基于我个人的使用经验,SOFA 模块化对服务端开发的影响优大于劣:在维护代码中的过程中会仔细斟酌当前应用的模块依赖结构是否合理;可以更安全得提供 SDK 给业务方使用;在实现代码共享的时候,也需要仔细考虑哪些代码值得共享,哪些不需要。

参考资料

  1. Sofaboot- 模块化开发概述
  2. 蚂蚁金服的业务系统模块化之模块化隔离方案
  3. 阿里巴巴 Java 开发手册(华山版).pdf
  4. JVM 服务发布与引用

本号专注于后端技术、JVM 问题排查和优化、Java 面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

正文完
 0