SOFABoot-09-模块隔离

news/2025/3/25 5:32:36/

前言

大家好,我是老马。

sofastack 其实出来很久了,第一次应该是在 2022 年左右开始关注,但是一直没有深入研究。

最近想学习一下 SOFA 对于生态的设计和思考。

sofaboot 系列

SOFABoot-00-sofaboot 概览

SOFABoot-01-蚂蚁金服开源的 sofaboot 是什么黑科技?

SOFABoot-02-模块化隔离方案

SOFABoot-03-sofaboot 介绍

SOFABoot-04-快速开始

SOFABoot-05-依赖管理

SOFABoot-06-健康检查

SOFABoot-07-版本查看

SOFABoot-08-启动加速

SOFABoot-09-模块隔离

SOFABoot-10-聊一聊 sofatboot 的十个问题

模块化开发概述

SOFABoot 从 2.4.0 版本开始支持基于 Spring 上下文隔离的模块化开发能力。为了更好的理解 SOFABoot 模块化开发的概念,我们来区分几个常见的模块化形式:

基于代码组织上的模块化:这是最常见的形式,在开发期,将不同功能的代码放在不同 Java 工程下,在编译期被打进不同 jar 包,在运行期,所有 Java 类都在一个 classpath 下,没做任何隔离;

基于 Spring 上下文隔离的模块化:借用 Spring 上下文来做不同功能模块的隔离,在开发期和编译期,代码和配置也会分在不同 Java 工程中,但在运行期,不同模块间的 Spring Bean 相互不可见,DI 只在同一个上下文内部发生,但是所有的 Java 类还是在同一个 ClassLoader 下;

基于 ClassLoader 隔离的模块化:借用 ClassLoader 来做隔离,每个模块都有独立的 ClassLoader,模块与模块之间的 classpath 不同,SOFAArk 就是这种模块化的实践方式。

SOFABoot 模块化开发属于第二种模块化形式 —— 基于 Spring 上下文隔离的模块化。

每个 SOFABoot 模块使用独立的 Spring 上下文,避免不同 SOFABoot 模块间的 BeanId 冲突,有效降低企业级多模块开发时团队间的沟通成本。

关于 SOFABoot 模块化产生的背景,可参考文章 《蚂蚁金服的业务系统模块化 —- 模块化隔离方案》

功能简介

功能简介

JVM 服务发布与引用

SOFABoot 提供三种方式给开发人员发布和引用 JVM 服务

  • XML 方式

  • Annotation 方式

  • 编程 API 方式

XML 方式

服务发布

首先需要定义一个 Bean:

<bean id="sampleService" class="com.alipay.sofa.runtime.test.service.SampleServiceImpl">

然后通过 SOFA 提供的 Spring 扩展标签来将上面的 Bean 发布成一个 SOFA JVM 服务。

<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService"><sofa:binding.jvm/>
</sofa:service>

上面的配置中的 interface 指的是需要发布成服务的接口,ref 指向的是需要发布成 JVM 服务的 Bean,至此,我们就已经完成了一个 JVM 服务的发布。

服务引用

使用 SOFA 提供的 Spring 扩展标签引用服务:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleServiceRef"><sofa:binding.jvm/>
</sofa:reference>

上面的配置中的 interface 是服务的接口,需要和发布服务时配置的 interface 一致。

id 属性的含义同 Spring BeanId。

上面的配置会生成一个 id 为 sampleServiceRef 的 Spring Bean,你可以将 sampleServiceRef 这个 Bean 注入到当前 SOFABoot 模块 Spring 上下文的任意地方。

service/reference 标签还支持 RPC 服务发布,相关文档: RPC 服务发布与引用

Annotation 方式

如果一个服务已经被加上了 @SofaService 的注解,它就不能再用 XML 的方式去发布服务了,选择一种方式发布服务,而不是两种混用。

除了通过 XML 方式发布 JVM 服务和引用之外,SOFABoot 还提供了 Annotation 的方式来发布和引用 JVM 服务。

通过 Annotation 方式发布 JVM 服务,只需要在实现类上加一个 @SofaService 注解即可,如下:

@SofaService
public class SampleImpl implements SampleInterface {public void test() {}
}
  • 提示

@SofaService 的作用是将一个 Bean 发布成一个 JVM 服务,这意味着虽然你可以不用再写 <sofa:service/> 的配置,但是还是需要事先将 @SofaService 所注解的类配置成一个 Spring Bean。

在使用 XML 配置 <sofa:service/> 的时候,我们配置了一个 interface 属性,但是在使用 @SofaService 注解的时候,却没有看到有配置服务接口的地方。

这是因为当被 @SofaService 注解的类只有一个接口的时候,框架会直接采用这个接口作为服务的接口。

当被 @SofaService 注解的类实现了多个接口时,可以设置 @SofaService 的 interfaceType 字段来指定服务接口,比如下面这样:

@SofaService(interfaceType=SampleInterface.class)
public class SampleImpl implements SampleInterface, Serializable {public void test() {}
}

和 @SofaService 对应,Sofa 提供了 @SofaReference 来引用一个 JVM 服务。

假设我们需要在一个 Spring Bean 中使用 SampleJvmService 这个 JVM 服务,那么只需要在字段上加上一个 @SofaReference 的注解即可:

public class SampleServiceRef {@SofaReferenceprivate SampleService sampleService;
}

和 @SofaService 类似,我们也没有在 @SofaReference 上指定服务接口,这是因为 @SofaReference 在不指定服务接口的时候,会采用被注解字段的类型作为服务接口,你也可以通过设定 @SofaReference 的 interfaceType 属性来指定:

public class SampleServiceRef {@SofaReference(interfaceType=SampleService.class)private SampleService sampleService;
}

使用 @SofaService 注解发布服务时,需要在实现类上打上 @SofaService 注解;在 Spring Boot 使用 Bean Method 创建 Bean 时,会导致 @Bean 和 @SofaService 分散在两处,而且无法对同一个实现类使用不同的 unique id。

因此自 SOFABoot v2.6.0 及 v3.1.0 版本起,支持 @SofaService 作用在 Bean Method 之上,例如:

@Configuration
public class SampleSofaServiceConfiguration {@Bean("sampleSofaService")@SofaService(uniqueId = "service1")SampleService service() {return new SampleServiceImpl("");}
}

同样为了方便在 Spring Boot Bean Method 使用注解 @SofaReference 引用服务,自 SOFABoot v2.6.0 及 v3.1.0 版本起,支持在 Bean Method 参数上使用 @SofaReference 注解引用 JVM 服务,例如:

@Configuration
public class MultiSofaReferenceConfiguration {@Bean("sampleReference")TestService service(@Value("$spring.application.name") String appName,@SofaReference(uniqueId = "service") SampleService service) {return new TestService(service);}
}

编程 API 方式

SOFABoot 为 JVM 服务的发布和引用提供了一套编程 API 方式,方便直接在代码中发布和引用 JVM 服务,与 Spring 的 ApplicationContextAware 类似,为使用编程 API 方式,首先需要实现 ClientFactoryAware 接口获取编程组件 API:

public class ClientFactoryBean implements ClientFactoryAware {private ClientFactory clientFactory;@Overridepublic void setClientFactory(ClientFactory clientFactory) {this.clientFactory = clientFactory;}
}

以 SampleService 为例,看下如何使用 clientFactory 通过编程 API 方式发布 JVM 服务:

ServiceClient serviceClient = clientFactory.getClient(ServiceClient.class);ServiceParam serviceParam = new ServiceParam();
serviceParam.setInstance(new SampleServiceImpl());
serviceParam.setInterfaceType(SampleService.class);
serviceClient.service(serviceParam);

上面的代码中

  • 首先通过 clientFactory 获得 ServiceClient 对象

  • 然后构造 ServiceParam 对象,ServiceParam 对象包含发布服务所需参数,通过 setInstance 方法来设置需要被发布成 JVM 服务的对象,setInterfaceType- 来
    设置服务的接口

  • 最后,调用 ServiceClient 的 service 方法,发布一个 JVM 服务

通过编程 API 方式引用 JVM 服务的代码也是类似的:

ReferenceClient referenceClient = clientFactory.getClient(ReferenceClient.class);ReferenceParam<SampleService> referenceParam = new ReferenceParam<SampleService>();
referenceParam.setInterfaceType(SampleService.class);
SampleService proxy = referenceClient.reference(referenceParam);

同样,引用一个 JVM 服务只需从 ClientFactory 中获取一个 ReferenceClient ,然后和发布一个服务类似,构造出一个 ReferenceParam,然后设置好服务的接口,最后调用 ReferenceClient 的 reference 方法即可。

通过动态客户端创建的 Reference 对象是一个非常重的对象,请大家在使用的时候不要频繁创建,自行做好缓存,否则可能存在内存溢出的风险。

除了实现 ClientFactoryAware 接口用于获取 ServiceClient 和 ReferenceClient 对象,还可以使用简便的注解 @SofaClientFactory 获取编程 API,例如

public class ClientBean {@SofaClientFactoryprivate ReferenceClient referenceClient;@SofaClientFactoryprivate ServiceClient serviceClient;
}

uniqueId

有些时候,针对一个接口,我们会需要发布两个服务出来,分别对应到不同的实现。

继续前面的 sampleService 的例子,我们可能有两个 SampleService 的实现,这两个实现我们都需要发布成 SOFA 的 JVM Service,按照前面的教程,采用 XML 的方式,我们就可能用下面这种方式进行配置:

<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService1">
</sofa:service>
<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService2">
</sofa:service>

上面的服务发布没有什么问题,但是当需要引用服务的时候,就出现了问题了,例如我们使用以下配置:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleService">
</sofa:reference>

这个 JVM 引用到底引用的是哪个 JVM 服务呢,我们无从知晓。

为了解决上面的这种问题,SOFABoot 引入了 uniqueId 的概念,针对服务接口一样的 JVM 服务,可以通过 uniqueId 来进行区分,上面的服务发布的代码我们加入 uniqueId 后,我们可以改成下面这样:

<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService1" unique-id="ss1">
</sofa:service>
<sofa:service interface="com.alipay.sofa.runtime.test.service.SampleService" ref="sampleService2" unique-id="ss2">
</sofa:service>

然后,在引用服务的时候,如果我们要使用 sampleService1 的服务,可以指定 unique-id 为 ss1,比如:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleService" unique-id="ss1">
</sofa:reference>

如果要使用 sampleService2 的服务,可以指定 unique-id 为 ss2,比如:

<sofa:reference interface="com.alipay.sofa.runtime.test.service.SampleService" id="sampleService" unique-id="ss2">
</sofa:reference>

上面说的是在 XML 的方式中使用 uniqueId。当你用 Annotation 的方式发布 JVM 服务和引用的时候,可以通过设置 @SofaService 和 @SofaReference 的 uniqueId 属性来设置 uniqueId。

当你用编程 API 的方式发布或者引用 JVM 服务的时候,可以通过 ServiceParam 和 ReferenceParam 的 setUniqueId 方法来设置 uniqueId。

小结

希望本文对你有所帮助,如果喜欢,欢迎点赞收藏转发一波。

我是老马,期待与你的下次相遇。


http://www.ppmy.cn/news/1582430.html

相关文章

Spring IOC 详解:基于 XML 配置与注解的依赖注入

Spring 框架是 Java 开发中最常用的框架之一&#xff0c;其核心思想之一是 IOC&#xff08;Inversion of Control&#xff0c;控制反转&#xff09;。IOC 的核心思想是将对象的创建和依赖关系的管理交给 Spring 容器来处理&#xff0c;从而降低代码的耦合度。本文将深入探讨 Sp…

git管理时keil项目忽略文件列表

在使用 Git 管理 Keil MDK&#xff08;μVision 5&#xff09;工程时&#xff0c;需要忽略编译生成的临时文件、调试文件、用户配置等非必要内容。以下是忽略文件的详细列表及说明&#xff0c;可直接保存为 .gitignore 文件&#xff1a; Keil MDK 工程的 .gitignore 文件 giti…

Java学习总结-List的集合

List集合的特有方法: ArrayList的底层原理 第一次创建集合对象&#xff0c;size是10&#xff0c;之后扩容的时候按1.5倍扩容。 ArrayList底层基于数组存储数据的。查的的快&#xff0c;节省空间。 LinkedList底层基于链表存储数据的。增删快。

如何开发搭建交易所系统:全面指南

在数字资产日益普及的今天&#xff0c;交易所系统作为连接买家与卖家的桥梁&#xff0c;扮演着至关重要的角色。开发一个高效、安全、用户友好的交易所系统不仅是对技术的挑战&#xff0c;更是对市场理解、用户体验、合规运营等多方面能力的综合考验。本文将详细探讨如何开发搭…

Stable Diffusion lora训练(一)

一、不同维度的LoRA训练步数建议 2D风格训练 数据规模&#xff1a;建议20-50张高质量图片&#xff08;分辨率≥10241024&#xff09;&#xff0c;覆盖多角度、多表情的平面风格。步数范围&#xff1a;总步数控制在1000-2000步&#xff0c;公式为 总步数 Repeat Image Epoch …

【前端】Visual Studio Code安装配置教程:下载、汉化、常用组件、基本操作

文章目录 一、Visual Studio Code下载二、汉化三、常用组件1、Auto Rename Tag2、view-in-browser3、Live Server 四、基本操作五、感谢观看&#xff01; 一、Visual Studio Code下载 下载官网&#xff1a;https://code.visualstudio.com/ 进入官网后点击右上角的Download &…

spring boot 登入权限RBAC模式

首先准备好5张表 user_info表&#xff0c;用户的信息表 role表&#xff0c;角色表&#xff08;比如超级管理员、管理员、审核员、采购......&#xff09; 创建user_role表&#xff0c;user_info表&#xff0c;role表的中间表 注意了&#xff0c;role_id和user_id是 u…

用 pytorch 从零开始创建大语言模型(零):汇总

用 pytorch 从零开始创建大语言模型&#xff08;零&#xff09;&#xff1a;汇总 本系列官方代码库&#xff1a;https://github.com/rasbt/LLMs-from-scratch/tree/main 官方书籍&#xff1a;Build a Large Language Model (From Scratch) 本系列文章&#xff1a; 用 pytorc…