sofa boot介绍
介绍
官网上的说明,相比springboot,增加的功能主要有三个:健康检查、模块化(多上下文)、类隔离(ark多classloader)。
- 扩展 Spring Boot 健康检查的能力:在 Spring Boot 健康检查能力基础上,提供了 Readiness Check 的能力,保证应用实例安全上线。
“服务注册上”并不代表“程序初始化完成”。是否初始化完成,需要业务上自己判断。在spirngboot中有HealthIndicator接口,也能提供类似的检查。但是好像没有现成的通过这接口控制流量的机制。
- 提供模块化开发的能力:基于 Spring 上下文隔离提供模块化开发能力,每个 SOFABoot 模块使用独立的 Spring 上下文,避免不同 SOFABoot 模块间的 BeanId 冲突。
- 增加模块并行加载和 Spring Bean 异步初始化能力,加速应用启动;
主要是解决什么场景下的问题?如果是server端,其实对启动时间并不敏感。微服务化后,启动都不会慢
- 增加日志空间隔离的能力:中间件框架自动发现应用的日志实现依赖并独立打印日志,避免中间件和应用日志实现绑定,通过 sofa-common-tools 实现。
不是很明白解决的问题场景。日志实现不一般通过SL4J进行解耦么。
- 增加类隔离的能力:基于 SOFAArk 框架提供类隔离能力,方便使用者解决各种类冲突问题。
这也是个常见的场景。
- 增加中间件集成管理的能力:统一管控、提供中间件统一易用的编程接口、每一个 SOFA 中间件都是独立可插拔的组件。
每个中间件可能完成的功能差别很大,比如打点的和rpc的,接口应该很难统一?
- 提供完全兼容 Spring Boot的能力:SOFABoot 基于 Spring Boot 的基础上进行构建,并且完全兼容 Spring Boot。
如何在Spring Boot的基础上构建?Spring Boot本身提供了很多可以扩展的地方,比如postProcessor、自定义的各种实现类,是强入侵的么?(应该不是,如果是Spring Boot升级版本就比较麻烦)如果不是对Spring boot的强入侵,那么是不是更像对Spirng Boot的扩展?
依赖管理
- 与springboot-dependencies一样,有对应的sofaboot-dependencies。这sofaboot-dependencies中有非常多包版本管理,什么activitymq、kafka、zkclient、jackson、gson等等
- 依赖分为三类,中间件(比如sofarpc)、扩展组件(sofaboot的对springboot扩展的功能如健康检查、模块化检查、类隔离)、ark插件(三方包打包成ark插件,单独的classloader,如rpc-sofa-boot-plugin)
- ark插件在没有包冲突的时候可以不用,比如rpc-sofa-boot-plugin,在sofabootdependencies中有rpc-sofa-boot-starter的版本管理
健康检查
- Readiness检查分为两种,一种流量来自中间件,由于中间件有对应的sofa-rpc比较容易控制,但是还有流量来在自负载均衡器,建议是需要负载均衡器进行Readiness Check来决定是否导入流量
- 通过扩展org.springframework.boot.actuate.health.HealthIndicator来增加检查项目,如果配置了检查项目,那么会根据配置策略检查服务是否可用,如果不可以用不会有流量进入,只是在启动时候,运营时候的叫Liveness Check,这两个概念来自k8s。
版本查看
- 通过api查看jar包版本,觉着这功能很实用,为啥springboot没有呢
启动加速
- 异步初始化 Bean 的原理是开启单独线程负责执行 Bean 的初始化方法(init-method),能加速比如拉去远程配置、初始化数据源等操作。
模块隔离
概述
- 必要性。java中模块划分,一般通过一个package的方式,但并不是真正的划分,可能多个模块使用了同样的上线文。这样有什么问题?一个典型的场景就是,如果一个模块A,用到了模块B中的一个本来不期望暴露的类(不满足迪米特法则),那么在后续的模块服务化过程中就很麻烦了。这里主要原因一是通过package的方式划分模块,根本就不是真正的模块,java中有OSGi的方式,但这种方式比较重,引入很多概念。
- 在C中如果导出个dll,一般需要额外声明个额外的标准的C的接口,在客户端使用的时候要么有源代码的.h文件,要么自己根据文档声明一个。这中通过显示的声明的方式与sofa模块化使用有些类似。
- 这样做的一个优势就是,在模块拆分为服务的时候,非常快,只需要修改下“协议类型”,为什么叫协议类型,因为本质上看,模块之间就是一种通信方式,通过Jvm调用可以理解为一种协议
- 这种划分模块的方式更接近于划分服务接口,不同的是更宽泛些,能暴露出一些类
- 在微服务建设的过程中,如果原来的程序是个小泥球,拆分微服务可能就是个噩梦,如果要拆分的服务本身模块化都已经非常好,那微服务只是换了种通信的方式。通过这种强制显示声明暴露的服务的方式,能够起到倒推的作用,强制在模块定义的时候就考虑好模块之间的划分。
- 与DDD的关系?在DDD的实践中,一个界限上下文一般对应一个微服务,一个界限上下文中有多个聚合,一个聚合根。那么这里的模块就可以和聚合对应上,DDD中聚合是有可能后续拆分微服务的,而sofa中的模块在微服务化的过程中是异常的简单的。如果聚合根对应这里的模块的概念,完美契合。如果个概念有写入出,从另外一个角度看这两者也是殊途同归的,都是为了模块化设计,后续进行为服务拆分打下基础。
JVM服务发布与引用
- 与spring注入Bean很类似,提供了xml、注解、api三种方式进行服务的发布与引用
- 发布的时候可以直接通过接口的方式发布,发布的时候指定接口和一个ref,这个ref只是一个新的服务的bean的id。但引用的时候,虽然也有个id,也是spring bean的id,但是一个新的bean,因为是两个不同的bean,这两个id并没有什么匹配的关系。
- 但在同一个接口有多个不同服务实现的时候,发布的时候就需要区分两个不同的实现,这个时候其实有两个选择,一种是制定发布服务中的beanId,在spring中就有这种制定beanId的方式。但是,sofa的服务设计之出就考虑了模块服务化的问题,如果服务化后一个服务要引入另外一个服务内的beanid就很奇怪,所以sofa的方案是在加个id——unique id。
Spring Bean 异步初始化&并行初始化
- 两个不同的概念,异步是对init-method方法而言,并行是对多个模块树
- “在实际使用 Spring/Spring Boot 开发中,会有一些 Bean 在初始化过程中执行准备操作,如拉取远程配置、初始化数据源等等;在应用启动期间,这类 Bean 会增加 Spring 上下文刷新时间,导致应用启动耗时变长。为了加速应用启动,SOFABoot 通过配置可选项,将 Bean 的初始化方法(init-method) 使用单独线程异步执行,加快 Spring 上下文加载过程,提高应用启动速度”,目的是加快启动进程,这对于server端的服务来说意义好像不是很明显。
- 异步之后如何同步?上下文如何知道异步初始化完成?如果没有init没有完成,上下文认为初始化完成,有流量进来可能有业务异常。
- 一个容器内可能有多个模块树,各个模块树是能够进行并行初始化的,这就可以并行地对模块进行初始化操作
SOFABoot 拓展点
- 三步,定义需要被扩展的对象(需要显示实现接口并且配置扩展点名字)、定义扩展点(有扩展点名字,无具体值)、定义扩展(需要被扩展的对象、扩展点名字、扩展的具体content)
- spring的Config中通过@Bean也能解决扩展的问题,但解决的问题不是一类问题,文档中说的:“SOFABoot 支持模块化隔离,在实际的使用场景中,一个模块中的 bean 有时候需要开放一些入口,供另外一个模块扩展。”是由于模块化的存在,A是高层模块,由于依赖导致,A定义一些基础的服务,需要B模块来实现(扩展),把“依赖倒置”这种方式给用个小的模块化框架约束起来。
- 但这机制有些让我对模块隔离这设计的初衷有疑问,开始理解是为了后续拆分微服务方便。这里有可以定义扩展点,这扩展点方式不方便进行微服务化,那么模块设计的初衷还有一层,并一定为了微服务化才模块隔离,而是升级到更清晰的职责划分,依赖导致的一个框建?微服务化框架+依赖导致框架
类隔离
- “在大型软件开发过程中,通常会推荐底层功能插件化,业务功能模块化的开发模式,以期达到低耦合、高内聚、功能复用的优点。“,这里底层功能是否包括一些基础服务?个人推测应该包括如帐号、加密等。
- ”ark的能力包括:定义类加载模型,运行时底层插件、业务应用(模块)之间均相互隔离,单一插件和应用(模块)由不同的 ClassLoader 加载,可以有效避免相互之间的包冲突,提升插件和模块功能复用能力;”
- ark plugin与ark biz的区别:从功能上引用官方文档:“Ark Plugin 优先 Ark Biz 被加载启动;Ark Plugin 之间是双向类索引关系,即可以相互委托对方加载所需的类和资源;Ark Plugin 和 Ark Biz 是单向类索引关系,即只允许 Ark Biz 索引 Ark Plugin 加载的类和资源,反之则不允许”。从这能看出,如果是业务相关的放在ark biz中比较合适,而plugin提供一些通用的类似中间件能力的东西,比如sofa rpc,其他的一些基础的通用的服务应该也在plugin中。
- 在ark配置中能够显示配置需要使用哪些biz、哪些plugin,这种显示的配置,类似maven中的包依赖,只不过是业务层面上的。
- ark扩展机制中,“这种可以完全将所有依赖都打在基座(宿主)应用中,模块中可以什么依赖都不带,完全是纯的业务代码;带来的好处是,一个模块最终打出的包大小会非常小,在动态操作模块时,可以极大的提高性能。”,这类似云原生的理念,除了业务代码意外的东西尽量由外部提供。
总结
- sofaboot解决的问题基本都是复杂的业务场景下开发的问题,如类隔离(多团队、多业务包冲突)、模块隔离(beanId冲突、方便微服务化、依赖导致)
- sofaboot还实现了一些工程理念(微服务化、模块依赖导致、业务模块化、基础功能插件化)框架化,通过框架的约束,反推开发人员提前考虑模块划分(后续是否可能微服务化(Jvm服务发布)、模块之间的依赖问题(扩展点机制反推进行依赖呆滞);通过ark的插件沉淀底层功能、通过ark插件模块化业务让开发人员提前考虑业务层的划分
- 综上,sofaboot提供的功能大多更贴近业务,可以理解为一个业务框架。
Q&A
- 一般的开发方式是如何的?简单的方式,直接使用sofaboot开发模块,定义模块之间的扩展点即可;复杂的方式,sofaboot定义ark的业务插件,ark的业务插件内进行模块化开发