1.前言

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

由于是基于Spring Boot 开发,故与其有着版本对应关系,SOFABoot 3.x 系列版本将构建在 Spring Boot 2.x 基础之上,SOFABoot 4.x 系列版本将构建在 Spring Boot 3.x 基础之上。

注:本文以SOFABoot 3.17.0 和 Spring Boot 2.7.8 (maven 3.8.8)为例进行说明!

2.快速开始

为了能顺利拉取jar包,需要先配置本地maven,在setting.xml 文件增加如下 profile 配置:

<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>maven-snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>maven-snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</pluginRepository>
</pluginRepositories>
</profile>

首先新建一个SpringBoot的项目,我以阿里云源进行创建,创建后在依赖管理中用 SOFABoot 替换SpringBoot的版本

<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofaboot-dependencies</artifactId>
<version>${sofa.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

其中sofa版本如下

接着引入SOFABoot 健康检查扩展能力的依赖及 Web 依赖(方便查看健康检查结果)

<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>healthcheck-sofa-boot-starter</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

在配置目录中新增配置文件 application.properties 文件,添加 SOFABoot 工程常用的参数配置,其中 spring.application.name 是必需的参数,用于标示当前应用的名称;logging path 用于指定日志的输出目录。

spring.application.name=SOFABoot Demo
logging.path=./logs

最后直接在启动类上运行main方法,启动成功后在浏览器访问 http://localhost:8080/actuator/versions 来查看当前 SOFABoot 中使用 Maven 插件生成的版本信息汇总。如果是SOFABoot 4.x 则需要把路径替换为  /sofaboot/versions

在浏览器输入 http://localhost:8080/actuator/readiness 查看应用 Readiness Check 的状况。如果是SOFABoot 4.x 则需要把路径替换为  /health/readiness

status: "UP" 表示应用 Readiness Check 健康的。

另外也可以查看日志,由于上述配置了日志文件的目录是项目目录下,故打开可以看到有个log目录,里面的结构如下

自此SOFABoot 集成和启动就完成了。

但是,它具体有何用,目前还未体现出来,下面进行说明。

3.启动加速

在实际使用 Spring/Spring Boot 开发中,会有一些 Bean 在初始化过程中执行准备操作,如拉取远程配置、初始化数据源等等。在应用启动期间,这类 Bean 会增加 Spring 上下文刷新时间,导致应用启动耗时变长。

为了加速应用启动,SOFABoot 通过配置可选项,将 Bean 的初始化方法(init-method) 使用单独线程异步执行,加快 Spring 上下文加载过程,提高应用启动速度。那么是如何去做的?

先来demo

首先是两个类,分别有两个初始化的方法(有延迟),然后手动方式注入到Spring

启动后查看启动时间

这是正常逻辑下的bean注入,没有问题对吧。下面就模拟在启动过程中有耗时操作(这里就指定初始化的方法)

重启后再次查看日志,有初始化的打印日志,并且时间都9S多,这是在情理之中的,因为 Bean 的初始化动作是串行的,3S+5S,再加系统自带的启动时间

如果一个大型项目启动过程中有大量的耗时操作,那么按照这种方式是不是启动非常缓慢。而Sofa Boot就解决了这个问题,其实逻辑很简单,就是将上述Bean 串行初始化改为并行异步。

如果使用呢?在注入bean时添加注解@SofaAsyncInit,再次启动观察日志

是不是很神奇,只是简单加了个注解,就减少了启动的时间。

再看初始化方法的日志,发现并不是使用的main线程,而是其他线程执行的,且是并行

看起来是不是非常简单,在实际场景中可有效的加快启动时间。但是,它也有弊端,就像上面的两个类的init方法,需要手动在这种初始化耗时的方法上添加注解@SofaAsyncInit,也就是说需要我们开发人员去判断哪些类是可以异步初始化的。

用法讲完了,下一步就是啃原理了,进入注解 @SofaAsyncInit  ,下载源码

com.alipay.sofa.runtime.api.annotation.SofaAsyncInit

这个注解也只有一个value属性,默认值是true,根据注释是说为true时才对初始化的方法进行异步调用

通过点击这个value(),可以看到只有一个地方调用

进入这个类,

com.alipay.sofa.runtime.spring.AsyncInitBeanFactoryPostProcessor#registerAsyncInitBea

可以看出在异步初始化的方法中只有value属性为true时才调用 registerAsyncInitBean()  ,和上面的注释不谋而合。

那么这个方法 registerAsyncInitBean() 是干啥的?

com.alipay.sofa.runtime.spring.async.AsyncInitBeanHolder

这个类中有一个map和两个方法

其中map看起来应该是存储bean的,那么究竟存储的数据是什么格式?打断点后重启项目

原来它是以模块进行区分,内层的map即为bean名称和初始化的方法名。也就是说这个方法就是把模块与bean名称和初始化的方法名放入map。方法 registerAsyncInitBean() 是放入,那么什么时候来取这个对应关系?那就是 getAsyncInitMethodName() 的作用,稍后再看。

那么如何知道哪些类的方法在初始化时需要异步?那肯定是标记了 @SofaAsyncInit 注解的方法,既然如此,我们来看有哪些类引入了此注解。可以看出,除了我们自己使用外,只有 AsyncInitBeanFactoryPostProcessor  这个类引入了

进入这个类

com.alipay.sofa.runtime.spring.AsyncInitBeanFactoryPostProcessor

其他代码无需关注,只从L45开始阅读,是走if分支还是else?不知道就打断点看执行过程,可以看出都会执行L145的if的分支,而在if分支里面就是获取@SofaAsyncInit 注解,然后调用 registerAsyncInitBean() ,而这个方法是不是在哪见过

这就和上述的value判断串起来了。

下面来看 getAsyncInitMethodName() 的作用,通过调用可以看到在AsyncProxyBeanPostProcessor类中

com.alipay.sofa.runtime.spring.AsyncProxyBeanPostProcessor

会从map中获取方法名并传入类AsyncInitializeBeanMethodInvoker

那么AsyncInitializeBeanMethodInvoker是干啥的,继续看

此类定义了几个参数,重要看下面三个

  • initCountDownLatch:CountDownLatch 对象,其中 count 初始化为 1
  • isAsyncCalling:表示是否正在异步执行 init 方法。
  • isAsyncCalled:表示是否已经异步执行过 init 方法。

那么是否可以推测通过这几个字段就可以判断一个bean是否已经执行或正在执行初始化方法?由于此类实现了MethodInterceptor接口,我们看起invoke(),

其中L121~L143的if分支,逻辑是什么?即没有异步执行初始化bean的init方法并且是异步的方法,那么就会放入线程池去执行。

如果不满足if,那就是后续的逻辑。不满足的条件是什么?可能是正在初始化或者已经初始化过了,那么这种情况下也不会重复把初始化丢入线程池去执行,也就是一个异步执行bean的初始化方法只会执行一次。

如此来看,加速bean初始化是不是还是比较简单。

其他功能待续

参考https://www.cnblogs.com/thisiswhy/p/17457499.html

SOFABoot 入门及基本使用的更多相关文章

  1. Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求

    上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...

  2. ABP入门系列(1)——学习Abp框架之实操演练

    作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...

  3. Oracle分析函数入门

    一.Oracle分析函数入门 分析函数是什么?分析函数是Oracle专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据中进行分组然后计算基于组的某种统计值,并且每一组的每一行都可以返回一个统计 ...

  4. Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数

    上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...

  5. Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数

    上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...

  6. Angular2入门系列教程4-服务

    上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...

  7. wepack+sass+vue 入门教程(三)

    十一.安装sass文件转换为css需要的相关依赖包 npm install --save-dev sass-loader style-loader css-loader loader的作用是辅助web ...

  8. wepack+sass+vue 入门教程(二)

    六.新建webpack配置文件 webpack.config.js 文件整体框架内容如下,后续会详细说明每个配置项的配置 webpack.config.js直接放在项目demo目录下 module.e ...

  9. wepack+sass+vue 入门教程(一)

    一.安装node.js node.js是基础,必须先安装.而且最新版的node.js,已经集成了npm. 下载地址 node安装,一路按默认即可. 二.全局安装webpack npm install ...

  10. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

随机推荐

  1. ETL的数据挖掘方式

    ETL的基本概念 数据抽取(Extraction):从不同源头系统中获取所需数据的步骤.比如从mysql中拿取数据就是一种简单的抽取动作,从API接口拿取数据也是. 数据转换(Transformati ...

  2. spring项目run起来的最小依赖

    spring项目跑起来,只需要spring-context这1个依赖项就行,参考下面: 一.pom.xml 1 <?xml version="1.0" encoding=&q ...

  3. GStreamer开发笔记(九):gst-rtcp-server安装和部署实现简单的rtsp-server服务器推流Demo

    前言   Gstreamer还有一个重要的功能就是充当rtsp流媒体服务器.   注意   本ubuntu是虚拟机,对延迟可能影响较大,延迟可作为参考,物理机可能更快.   Demo      RTP ...

  4. fantasy-talking:实现图片加音频生成对嘴数字人

    引言:一张图也能"说话"? 你有没有想过,一张静态的照片,配上一段音频,就能变成一段"对嘴"的视频?不是简单的口型同步,而是让图片中的人物"活过来&q ...

  5. 【STL和泛型编程】4. hashtable、unordered_set、unordered_map

    1. hashtable 前置知识:[数据结构]3.跳表和散列 基本原理: 将Key计算成一个数值,然后取余数得到它在表头中的位置 table(篮子)里每个指针都指向一个链表(桶)来存储余数相同的值 ...

  6. CH5 用神经网络解决线性问题

    了解非线性问题.分类问题 掌握神经网络解决二分类问题 掌握随机生成训练数据的方法 非线性问题 三好学生问题:家长们再次凑到一起,但这次情况不一样了,孩子们的总分不知道,仅知道是否评选上三好学生的结果. ...

  7. 建筑矿山设备工厂与 MyEMS 能源管理系统深度应用白皮书

    一.建筑矿山设备工厂:全球矿业装备的技术革新引领者 企业沿革与行业定位 作为拥有百年工业基因的跨国集团,建筑矿山设备工厂起源于欧洲压缩空气技术领域,历经百年发展已成为全球采矿与基础设施技术解决方案的头 ...

  8. Java面向对象基础——2.构造方法

    目录 Java构造方法 构造方法的基本概念与作用 为什么需要构造方法? 构造方法的定义与使用 基本语法 示例:为Person类定义构造方法 默认构造方法 注意事项 构造方法的重载 示例:重载构造方法 ...

  9. 动态设置移动端rem自适应

    1.平常我们全局设置rem是直接再html标签选择器上面设置的,如:html{fontsize:10px}    那么1rem = 10px 今天我们来动态获取以及设置html标签的rem值  如下 ...

  10. 扣子Coze智能体实战:1分钟自动仿写公众号爆文,一键自动发布

    大家好,我是汤师爷,专注AI智能体分享,致力于帮助100W人用智能体创富~ 你有没有遇到过这样的情况: 看到一篇爆文,想改成自己的风格却不知从何下手? 每天为公众号内容发愁,想发又怕质量不高? 文章改 ...