天天看點

開源 | 在 Spring Boot 中內建 SOFABoot 類隔離能力

原創聲明:本文系作者原創,謝絕個人、媒體、公衆号或網站未經授權轉載,違者追究其法律責任。

SOFABoot 是螞蟻金服中間件團隊開源的基于 Spring Boot 的一個開發架構,其在 Spring Boot 基礎能力之上,增加了類隔離能力。螞蟻金服内部豐富的實踐場景表明,類隔離能力對解決類沖突、版本管控有其特殊的優勢。

SOFABoot 的類隔離能力由單獨的元件 SOFAArk 實作,相比業界遵循 OSGi(

https://www.osgi.org/

) 規範的 Equinox 或者 Felix,SOFAArk 專注于類隔離,簡化了類加載模型,是一款更加輕量的類隔離架構。

本文将介紹 SOFABoot 類隔離能力的背景及其使用方式。

1. 背景

在 Java 世界中,依賴的 JAR 包之間互相沖突永遠是一個痛,Spring Boot 采用統一的依賴管理機制規避了大部分依賴沖突問題。理想很美好,現實卻很骨感,作為螞蟻金服這類大體量的公司,各業務線紛繁複雜、基礎服務元件繁多,很難做到對所有 JAR 包做統一管控,尤其涉及到多個跨團隊子產品元件互相依賴時,因為各自技術棧曆史包袱的存在,難以有效統一沖突包版本。

假設如下場景,工程需要引入兩個三方元件:A 和 B,元件 A 需要依賴 Hessian 3,元件 B 需要依賴 Hessian 4,因為 Hessian 3 和 Hessian 4 是不相容的。作為開發者,遇到這種包沖突問題,如果不借助類隔離架構,隻能耗費精力更新到統一版本。

開源 | 在 Spring Boot 中內建 SOFABoot 類隔離能力

為了徹底解決類似的包沖突問題,我們需要借助類隔離機制,使用不同的類加載器加載沖突的三方依賴包,進而做到在同一個應用運作時共存。

基于此背景,SOFABoot 提供了一個輕量級的類隔離架構,也是本文的主角,SOFAArk。

2. 基本原理

在介紹 SOFAArk 類隔離架構使用之前,我們簡單了解下其背後的實作原理。正如前文中描述,SOFAArk 是通過獨立的類加載器加載互相沖突的三方依賴包,進而做到隔離包沖突。那麼我們不禁要問,SOFAArk 是如何區分應用中哪些依賴包是需要單獨的類加載器加載呢?原因是 Ark Plugin,它是 SOFAArk 架構定義的一種特殊的 JAR 封包件格式,SOFAArk 架構會自動識别 Ark Plugin 這種特殊依賴。

何為 Ark Plugin ? Ark Plugin 本質上是一個 FatJar,借助 SOFABoot 官方提供的 maven 打包插件,開發者可以把若幹普通的 JAR 包打包成 Ark Plugin 供應用依賴或者把普通的 Java 子產品改造成 Ark Plugin。通常來說,如果把一個普通 JAR 打包成 Ark Plugin,那麼該 JAR 包依賴的其他三方包也會被打入同一個 Ark Plugin,預設情況下 SOFABoot 官方打包插件會自動把間接依賴也打入到 Ark Plugin。

應用使用添加 maven 依賴的方式引入 Ark Plugin,運作時,SOFAArk 架構會自動識别應用的三方依賴包中是否含有 Ark Plugin,進而使用單獨的類加載器加載。為了更加直覺,下圖是應用運作時邏輯分層圖:

開源 | 在 Spring Boot 中內建 SOFABoot 類隔離能力

可以看到,在應用運作時,SOFAArk 容器處于最底層,負責啟動應用。應用依賴的所有 Ark Plugin 處于中間層,每個 Ark Plugin 都由 SOFAArk 容器使用獨立的類加載器加載,互相隔離。應用業務代碼及其他非 Ark Plugin 的普通三方依賴包,為了描述簡單,統稱為 Ark Biz,它運作在最上層,需要依賴中間層的 Ark Plugin。

一個标準 Ark Plugin 會包含一些配置檔案,主要包含導出類和導入類配置。導出類即把 Ark Plugin 中的類導出給 Ark Biz 和其他 Ark Plugin 可見。預設情況下,所有 Ark Plugin 的導出類對于 Ark Biz 來說都是可見的,即 Ark Biz 可以使用 Ark Plugin 的導出類。對于 Ark Plugin 來說,如果需要使用其他 Ark Plugin 的導出類,必須聲明為自身的導入類。關于 Ark Plugin 詳細說明可以參考文末相關連結。

下面我們來示範如何開發一個簡單的 Ark Plugin。

3. Java 子產品改造成 Ark Plugin

3.1 建立工程

Demo 工程參見:

https://github.com/QilongZhang/ark-plugin-demo

運作需要 JDK 6 及以上、 Maven 3.2.5 以上。

首先我們在 IDE 裡建立一個普通 Maven 工程,并建立三個普通的 Java 子產品。以前文描述的 Hessian 沖突為例,在示範工程中定義了三個模快:

pojo-module: 定義了一個簡單的 PoJo 類 SamplePoJo,并設定為導出類,打包成 pojo-ark-plugin 。

hessian3-module:定義了一個服務類 Hessian3Service,實作了簡單的序列化和反序列邏輯,使用的版本是 Hessian 3,并導入了 SamplePoJo,打包成 hessian3-ark-plugin 。

hessian4-module:定義了一個服務類 Hessian4Service,和 Hessian3Service 功能類似,使用的版本是 Hessian 4,并導入了 SamplePoJo,打包成 hessian4-ark-plugin 。

該用例是為了示範如何将普通的 Java 子產品及其三方依賴包打包成 Ark Plugin,以 hessian3-module 子產品為例來講解打包流程。

3.2 編寫服務類

在 hessian3-module 中,提供了一個簡單的序列化和反序列化功能類 Hessian3Service:

package com.alipay.sofa.demo.hessian3;

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class Hessian3Service {
    public byte[] serialize(Object obj) throws IOException {
        if(obj==null) throw new NullPointerException();

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        HessianOutput ho = new HessianOutput(os);
        ho.writeObject(obj);
        return os.toByteArray();
    }

    public Object deserialize(byte[] by) throws IOException {
        if(by==null) throw new NullPointerException();

        ByteArrayInputStream is = new ByteArrayInputStream(by);
        HessianInput hi = new HessianInput(is);
        return hi.readObject();
    }
 }           

該功能類非常簡單,提供了兩個方法調用,分别實作對象的序列化和發序列化。

3.3 Ark Plugin 配置

因為 Ark Plugin 的配置大同小異,在這裡以普通的 Java 子產品 hessian3-module 打包成 hessian3-ark-plugin 為例,介紹 Ark Plugin 的一般配置。

首先,我們需要添加 SOFABoot 官方提供的 maven 打包插件:

<plugin>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>sofa-ark-plugin-maven-plugin</artifactId>
    <version>0.2.0<version>
</plugin>           

然後在hessian3-module 示範用例中,隻需要在子產品 pom.xml 檔案中添加一個導出類和一個導入類配置,配置如下:

<build>
    <plugins>
        <plugin>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>sofa-ark-plugin-maven-plugin</artifactId>
            <version>0.2.0</version>
            <executions>
                <execution>
                    <id>default-cli</id>
                    <goals>
                        <goal>ark-plugin</goal>
                    </goals>

                    <configuration>
                        <!-- configure imported class -->
                        <imported>
                            <!-- configure class-level imported class -->
                            <classes>
                                <class>com.alipay.sofa.demo.pojo.SamplePoJo</class>
                            </classes>
                        </imported>

                        <!-- configure exported class -->
                        <exported>
                            <!-- configure class-level exported class -->
                            <classes>
                                <class>com.alipay.sofa.demo.hessian3.Hessian3Service</class>
                            </classes>
                        </exported>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>           

可以看到,hessian3-ark-plugin 導入了基礎實體類 com.alipay.sofa.demo.pojo.SamplePoJo,導出服務類 com.alipay.sofa.demo.hessian3.Hessian3Service 供 Ark Biz 使用。

使用 maven 打包指令 maven package 即可将 hessian3-module 打包成 Ark Plugin,該插件包含子產品代碼及其依賴包,其 maven 坐标為:

<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>hessian3-ark-plugin</artifactId>
    <classifier>ark-plugin</classifier>
    <version>1.0.0</version>
</dependency>           

關于 Ark Plugin 的導入、導出類,這裡強調一下,在實際開發中,和業務本身無關的實體類或者基礎服務類,如果多個 Ark Plugin 和 Ark Biz 都需要統一版本時,可以把這類基礎 Jar 統一打包成 Ark Plugin 設定為導出類。在該示範用例中,我們假設 SamplePoJo 是一個基礎實體類,和業務邏輯無關,而 hessian3-ark-plugin 和 hessian4-ark-plugin 都需要使用,于是單獨打包成了一個 Ark Plugin。

到此為止,這就是一個簡單的 Ark Plugin 從開發的步驟,看起來是不是很簡單呢?

下面,我們示範下如何在 Spring Boot 工程中,快速內建 SOFABoot 的類隔離能力,并使用這三個 Ark Plugin。

4. Spring Boot 工程內建 SOFAArk 元件

https://github.com/QilongZhang/springboot-ark-demo

運作需要 JDK 6 及以上、 Maven 3.2.5 以上。Spring Boot 版本要求 1.4.2.RELEASE 以上,目前還不相容 2.0.0 及以上版本。

該用例工程主要為了示範如何在 Spring Boot 工程中內建 SOFABoot 類隔離能力并使用 Ark Plugin。這裡以前文提到的 Ark Plugin 為例,示例工程将會引入三個 Ark Plugin:pojo-ark-plugin,hessian3-ark-plugin 和 hessian4-ark-plugin,并使用後兩者的導出類 Hessian3Service 和 Hessian4Service。

4.1 建立工程

在 Spring Boot 官網

https://start.spring.io/

建立一個 web 工程,并設定 Spring Boot 版本号為 1.4.2.RELEASE。在 Spring Boot 工程中內建 SOFABoot 類隔離能力,隻需要添加 SOFABoot 提供的類隔離架構 starter。修改 maven 項目的配置檔案 pom.xml,将

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.2.RELEASE</version>
    <relativePath/> 
</parent>           

替換為:

<parent>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>sofaboot-dependencies</artifactId>
    <version>2.3.1</version>
</parent>           

并添加如下 SOFAArk Starter 依賴:

<!--ark spring boot starter-->
<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>sofa-ark-springboot-starter</artifactId>
</dependency>           

如此,一個 Spring Boot 工程便內建了 SOFABoot 提供的類隔離能力。

4.2 添加 Ark Plugin 依賴

前文中提到,将普通 Java 子產品打包成 Ark Plugin,其 maven 坐标不會發生變化,但是需要添加 classifier=ark-plugin 标志,是以如下添加三個 Ark Plugin 的依賴:

<!--ark plugin-->
<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>pojo-ark-plugin</artifactId>
    <classifier>ark-plugin</classifier>
    <version>1.0.0</version>
</dependency>
 
<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>hessian3-ark-plugin</artifactId>
    <classifier>ark-plugin</classifier>
    <version>1.0.0</version>
</dependency>
 
<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>hessian4-ark-plugin</artifactId>
    <classifier>ark-plugin</classifier>
    <version>1.0.0</version>
 </dependency>           

需要指出的是,因為 ark plugin 是一個 FatJar,為了讓應用編譯期通過,需要在工程主 pom.xml 中增加如下依賴:

<!--just for compile success-->
<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>pojo-ark-plugin</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
 </dependency>
 
 <dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>hessian3-ark-plugin</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
 </dependency>
 
 <dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>hessian4-ark-plugin</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
</dependency>           

4.3 編寫 Rest 接口

為了示範更加直覺,編寫如下 Rest 接口:

package com.alipay.sofa.springbootarkdemo.controller;

import com.alipay.sofa.demo.hessian3.Hessian3Service;
import com.alipay.sofa.demo.hessian4.Hessian4Service;
import com.alipay.sofa.demo.pojo.SamplePoJo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

 @RestController
 public class HelloController {

    @RequestMapping("/hello-hessian3")
    public String hessian3() throws IOException {
        SamplePoJo samplePoJo = new SamplePoJo("Hello, hessian3.");
        Hessian3Service hessian3Service = new Hessian3Service();
        byte[] bytes = hessian3Service.serialize(samplePoJo);
        Object pojo = hessian3Service.deserialize(bytes);
        return pojo.toString();
    }

    @RequestMapping("/hello-hessian4")
    public String hessian4() throws IOException {
        SamplePoJo samplePoJo = new SamplePoJo("Hello, hessian4.");
        Hessian4Service hessian4Service = new Hessian4Service();
        byte[] bytes = hessian4Service.serialize(samplePoJo);
        Object pojo = hessian4Service.deserialize(bytes);
        return pojo.toString();
    }
}           

該 Rest 接口主要為了本地啟動應用後,能夠直覺示範 Ark Plugin 使用結果。啟動該 Web Spring 工程,可以看到控制台列印結果:

...
2018-05-14 10:04:06.573  INFO 85393 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-05-14 10:04:06.577  INFO 85393 --- [           main] c.a.s.s.SpringbootArkDemoApplication     : Started SpringbootArkDemoApplication in 2.111 seconds (JVM running for 3.562)
2018-05-14 10:04:06,578 INFO  main                             - Finish to start biz: Startup In IDE
2018-05-14 10:04:06,578 INFO  main                             - Finish to process pipeline stage: com.alipay.sofa.ark.container.pipeline.DeployBizStage
Ark container started in 2720 ms.           

可以看到類似 Ark container started in XXXXms 字樣,即表示該工程運作在 SOFABoot 類隔離架構之上。通路如下兩個請求即可看到工程調用 Hessian3Service 和 Hessian4Service 的調用結果

http://localhost:8080/hello-hessian3

http://localhost:8080/hello-hessian4           

以上就是 Spring Boot 內建 SOFABoot 類隔離架構的一般步驟。可以看到,作為開發者,基本上無需過多關心自身應用是否使用類隔離架構,隻需要引入 SOFABoot 提供的類隔離架構 Starter,即可快速完成類隔離能力的內建。

5. 總結

本文主要介紹了 SOFABoot 基于 Spring Boot 開發的類隔離架構元件 SOFAArk。通過兩個簡單的用例工程,分别介紹了如何開發一個自己的 Ark Plugin 以及在 Spring Boot 快速內建 SOFABoot 的類隔離能力。SOFABoot 官方提供了 SOFARPC 打包的 Ark Plugin,開發者可以按需隔離 SOFARPC 和自身應用。另外,在 SOFAArk 類隔離容器之上,SOFABoot 提供了一整套相容 JUnit 和 TestNG 測試架構的方案,感興趣的讀者可以下載下傳文末給出的 Demo 工程,并按照官方文檔學習。

相關資源

ark-plugin-demo:

springboot-ark-demo:

SOFABoot:

https://github.com/alipay/sofa-boot

SOFARPC:

https://github.com/alipay/sofa-rpc

SOFARPC Ark Plugin:

https://github.com/alipay/sofa-rpc-boot-projects

SOFAArk:

https://github.com/alipay/sofa-ark

Ark Plugin 目錄結構及其打包插件的使用

https://alipay.github.io/sofastack.github.io/docs/ark-plugin.html