分佈式邏輯管理平臺XXL-GLUE 原 薦

《分佈式邏輯管理平臺XXL-GLUE》

Maven Central GitHub release License

一、簡介

1.1 概述

XXL-GLUE 是一個分佈式環境下的 "可執行邏輯單元" 管理平臺, 學習簡單,擴展JVM的動態語言支持。現已開放源代碼並接入多家公司線上產品線,開箱即用。

GLUE:即"可執行邏輯",本質上是一段可執行的代碼。GLUE可以方便的嵌入業務代碼中, GLUE中邏輯代碼支持在線開發、動態推送更新、實時編譯生效。 可以節省部分因爲項目編譯、打包、部署和重啓線上機器所帶來的時間和人工消耗, 提高開發效率。

可以參考 “配置管理系統,如disconf xxl-conf等” 概念來幫助我們來理解XXL-GLUE。 前者維護 "配置信息",而且支持數據類型有限。XXL-GLUE功能更強大, 支持維護"可執行邏輯代碼"。 XXL-GLUE在功能上完全可以替代前者,只需要在可執行代碼塊中返回配置即可,支持返回任意類型配置數據。XXL-GLUE主要作用是託管"可執行邏輯代碼",將會爲開發者代碼不一樣的開發體驗。

1.2 特性

  • 1、動態:"可執行邏輯單元(GLUE)"支持在線開發、動態推送更新、實時編譯生效, 擴展JVM的動態語言支持;
  • 2、Wed IDE:提供WedIDE,支持在線開發GLUE代碼;
  • 3、推送更新:GLUE代碼修改後,開發人員可手動觸發GLUE更新廣播,廣播組件將會實時推送GLUE到接入方項目,從而實時更新GLUE;
  • 4、兼容Spring:GLUE代碼中支持@Resource和@Autowired兩種方式注入Spring容器中服務;
  • 5、版本:支持30個歷史版本的版本回溯;
  • 6、調試: 在開發階段可開啓本地模式, 該模式下將會加載本地GlueHandler文件, 支持Debug, 可以方便的進行本地調試;
  • 7、項目分組:支持設置項目分組,以項目爲維度進行GLUE分組管理;

1.3 下載

文檔地址

源碼倉庫地址

源碼倉庫地址Release Download
https://github.com/xuxueli/xxl-glueDownload
https://gitee.com/xuxueli0323/xxl-glueDownload
技術交流羣 (僅作技術交流)

1.4 環境

  • JDK:1.7+
  • Servlet/JSP Spec:3.1/2.3
  • Tomcat:8.5.x
  • Mysql:5.6+
  • Maven:3+

二、快速入門

源碼目錄介紹

/db                : 數據庫交表腳本位置
/xxl-glue-admin    : GLUE管理中心
/xxl-glue-core     : 公共依賴
/xxl-glue-core-example : GLUE接入Example項目, 可以參考它來學習如何在項目中接入並使用GLUE

1、初始化數據庫

執行數據庫建表腳本: /xxl-glue/doc/db/mysql_xxl_glue.sql

2、部署部署"GLUE管理中心"(xxl-glue-admin)

配置文件位置:xxl-glue-admin/resources/xxl-glue-admin.properties

### JDBC 配置
xxl.glue.db.driverClass=com.mysql.jdbc.Driver
xxl.glue.db.url=jdbc:mysql://localhost:3306/xxl-glue?useUnicode=true&characterEncoding=UTF-8
xxl.glue.db.user=root
xxl.glue.db.password=root_pwd

### zookeeper 地址配置:例如 "127.0.0.1:2181" 或 "127.0.0.1:2181,127.0.0.1:2182"
xxl.glue.zkserver=127.0.0.1:2181

### 登錄賬號密碼
xxl.glue.login.username=admin
xxl.glue.login.password=123456

編譯War包部署即可。

3、部署部署 "GLUE接入Example項目"(xxl-glue-core-example)

客戶端maven依賴

<!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-glue-core -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-glue-core</artifactId>
    <version>1.3.1</version>
</dependency>

客戶端配置文件

配置文件位置:xxl-glue-core-example/resources/xxl-glue.properties

### JDBC 配置
xxl.glue.db.driverClass=com.mysql.jdbc.Driver
xxl.glue.db.url=jdbc:mysql://localhost:3306/xxl-glue?useUnicode=true&characterEncoding=UTF-8
xxl.glue.db.user=root
xxl.glue.db.password=root_pwd

### zookeeper 地址配置:例如 "127.0.0.1:2181" 或 "127.0.0.1:2181,127.0.0.1:2182"
xxl.glue.zkserver=127.0.0.1:2181

編譯War包部署即可。

4、開發第一個Glue (Hello World)

登陸 "GLUE管理中心" 並點擊右上角 "新增GLUE" 按鈕,填寫 “GLUE名稱”(該名稱是該GLUE項的唯一標示)和簡介,確定後即新增一條GLUE。

點擊GLUE右側 “Web IDE”按鈕,即可進入GLUE的代碼開發界面,可在該界面開發GLUE代碼,也可以在IDE中開發完成後粘貼進來,默認已經初始化Demo代碼。 (每個Glue必須是實現統一父接口GlueHandler的子類;詳情可參考章節 "5.1" )

初始化數據局之後,系統默認生成了三個典型場景的GLUE示例,可以參考GLUE示例開發第一個GLUE(GLUE的三種經典使用場景,可參考 "章節四")。

輸入圖片說明

5、調用Glue

業務中調用Glue只需要執行以下一行代碼即可:

	Object result = GlueFactory.glue("glue名稱", "glue入參,Map類型");

"GLUE接入Example項目(xxl-glue-core-example)" 中,針對 "初始化數據局之後,系統默認生成了三個典型場景的GLUE示例" 提供了相應的示例調用代碼,代碼位置是:

xxl-glue-core-example/com.xxl.glue.example.controller.IndexController.index

部署啓動 "GLUE接入Example項目(xxl-glue-core-example)",假設項目部署在 "/xxl-glue-core-example" 路徑,訪問以下鏈接可執行測試邏輯。

http://localhost:8080/xxl-glue-core-example/

輸入圖片說明

三、操作詳解

1、登陸GLUE

輸入圖片說明

2、項目列表

系統以項目爲維度進行GLUE分組管理;可以在 "項目管理" 模塊查看系統中的項目列表,默認已經提供了一個 "示例項目";

輸入圖片說明

3、新增項目

在 "項目管理" 界面,點擊右上角 "新增項目" 可以新增項目,項目屬性說明如下:

項目AppName:項目AppName爲項目分組標識,在廣播刷新GLUE時可指定AppName實現灰度刷新指定AppNamd項目中的GLUE示例。正確格式爲:長度4-20位的小寫字母、數字和下劃線
項目名稱:項目中文名稱

輸入圖片說明

4、:GLUE列表

輸入圖片說明

5、新建GLUE

點擊右上角 "新建GLUE" 按鈕,彈框填寫GLUE信息即可新建GLUE,屬性介紹如下:

項目:GLUE所屬的項目,
GLUE:Glue名稱,每個GLUE的唯一標示,新建GLUE時將會自動將所項目的AppName作爲名稱前綴。正確格式爲:長度4-20位的大小寫字母、數字和下劃線
描述:GLUE的描述介紹信息

輸入圖片說明

6、開發GLUE代碼

找到新建的GLUE,點擊右側的 “Web IDE” 按鈕進入GLUE開發的Wed IDE界面。默認已經初始化示例代碼,如需開發業務代碼,只需要在handle方法中開發即可。

輸入圖片說明

7、一句話執行GlueHandler

首先確定項目中已經接入GLUE(參考上文 “GLUE接入Example項目(xxl-glue-core-example)”,接入非常方便);

業務中調用Glue只需要執行以下一行代碼即可:

	Object result = GlueFactory.glue("glue名稱", "glue入參,Map類型");

8、推送更新

Glue在第一次加載之後將會緩存在內存中,點擊右側 “清除緩存” 按鈕可以推送刷新GLUE緩存。 清除緩存彈框中有一個輸入框 "Witch APP", 輸入接入方項目的AppName, 即可精確的灰度刷新該項目中的相應Glue。如果不輸入, 則廣播刷新所有項目中響應的Glue。

輸入圖片說明

9、如何Debug測試GlueHandler

首先,需要了解源碼加載器配置,見項目"xxl-glue-core-example"的"applicationcontext-glue.xml"配置中"GlueFactory"實例的"glueLoader"屬性;

  • dbGlueLoader(數據庫加載器,不支持Debug,源碼線上維護): 默認XXL-GLUE在接入方通過加載數據庫中GLUE源碼進而實例化並執行, 需要配置一個"dbGlueLoader"。
  • FileGlueLoader(本地加載器,支持Debug): XXL-GLUE支持配置本地加載器, 只是掉上述配置中的"glueLoader"屬性即可,則使用默認的文件加載器;擁有以下特點:
    • 默認加載項目"resources/config/glue"目錄下的groovy文件,文件名稱必須和配置的GlueHandler名稱一致;
    • 會循環遍歷該目錄下子目錄文件進行匹配,因此文件可以在該目錄下自由存放;
    • FileGlueLoader方式使用XXL-GLUE,支持GlueHandler的debug斷點;

當源碼加載器選擇 "FileGlueLoader" 時,將會加載本地GLUE腳本文件,此時可以進行代碼Debug操作。

四、GlueHandler的三種經典使用場景,

示例場景01:託管 “配置信息”

尤其適用於數據結構比較複雜的配置項

package com.xxl.glue.example.handler;

import com.xxl.glue.core.handler.GlueHandler;

import java.util.HashSet;
import java.util.Map;

/**
 * 示例場景01:託管 “配置信息”
 *
 * 優點:
 * 		1、在線編輯;推送更新;
 * 		2、該場景下,相較於同類型配置管理系統,支持數據類型更加豐富,不僅支持基礎類型,甚至支持複雜對象;
 * 		3、該場景下,配置信息的操作和展示,更加直觀;
 *
 * @author xuxueli 2016-4-14 15:36:37
 */
public class DemoGlueHandler01 implements GlueHandler {

	@Override
	public Object handle(Map<String, Object> params) {
		
		/*
		// 【基礎類型配置】,例如:活動開關、短信發送次數閥值、redis地址等;
		boolean activitySwitch = true;													// 活動開關:true=開、false=關
		int smsLimitCount = 3;															// 短信發送次數閥值
		String brokerURL = "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.2:61616)";	// redis地址等

		// 【對象類型配置……】
		*/

		// 【列表配置】
		HashSet<String> blackTelephones = new HashSet<String>();						// 手機號碼黑名單列表
		blackTelephones.add("15000000000");
		blackTelephones.add("15000000001");
		blackTelephones.add("15000000002");

		return blackTelephones;
	}
	
}

場景02:託管 “靜態方法”

可以將配置解析邏輯一併託管,只關注返回結果即可

package com.xxl.glue.example.handler;

import com.xxl.glue.core.handler.GlueHandler;

import java.util.HashSet;
import java.util.Map;

/**
 * 示例場景02:託管 “靜態方法”
 *
 * 優點:
 * 		1、在線編輯;推送更新;
 * 		2、該場景下,託管公共組件,方便組件統一維護和升級;
 *
 * @author xuxueli 2016-4-14 16:07:03
 */
public class DemoGlueHandler02 implements GlueHandler {

	// 手機號碼黑名單列表
	private static HashSet<String> blackTelephones = new HashSet<String>();
	static {
		blackTelephones.add("15000000000");
		blackTelephones.add("15000000001");
		blackTelephones.add("15000000002");
	}

	/**
	 * 手機號碼黑名單校驗Util
	 *
	 * @param telephone
	 * @return
	 */
	private boolean isBlackTelephone(String telephone) {
		if (telephone!=null && blackTelephones.contains(telephone)) {
			return true;
		}
		return false;
	}
	
	@Override
	public Object handle(Map<String, Object> params) {
		String telephone = (params!=null)? (String) params.get("telephone") :null;
		return isBlackTelephone(telephone);
	}
	
}

場景C:示例場景03:託管 “動態服務”

可以靈活組裝接口和服務,擴展服務的動態特性,作爲公共服務。

package com.xxl.glue.example.handler;

import com.xxl.glue.core.handler.GlueHandler;

import java.util.Map;

/**
 * 示例場景03:託管 “動態服務”
 *
 * 優點:
 * 		1、在線編輯;推送更新;
 * 		2、該場景下,服務內部可以靈活組裝和調用其他Service服務, 擴展服務的動態特性,作爲公共服務。
 *
 * @author xuxueli 2016-4-14 16:07:03
 */
public class DemoGlueHandler03 implements GlueHandler {
	private static final String SHOPID = "shopid";
	
	/*
	@Resource
	private UserPhoneService userPhoneService;	// 手機號碼黑名單Service,此處僅作爲示例
	*/
	
	/**
	 * 商戶黑名單判斷
	 */
	@Override
	public Object handle(Map<String, Object> params) {
		
		/*
		String telephone = (params!=null)? (String) params.get("telephone") :null;

		boolean isBlackTelephone = userPhoneService.isBlackTelephone(telephone);
		return isBlackTelephone;
		*/
		
		return true;
	}
	
}

五、系統設計

架構圖

輸入圖片說明

角色組成
  • GlueHandler: "可執行邏輯"GLUE的代碼實現, 本質上是實現統一父接口的子類, 約定了公共方法以及公共的輸入輸出以便於與業務代碼交互。
  • Broadcase: 廣播組件, 當GlueHandler更新時, 將會觸發廣播消息;
  • GLUE管理中心: 管理中心, 提供對GlueHandler的管理功能, 同時提供 WebIDE 支持在線開發GlueHandler, 並藉助 "Broadcase" 組件提供邏輯單元"GlueHandler"實時推送功能;
  • GLUE服務: Glue源碼加載的RPC服務, 提供GlueHandler對應源碼的加載服務;
  • Client: GLUE的接入方, 可以動態的使用託管的GlueHandler, 並享受實時推送功能。避免了邏輯變更帶來的一系列編譯、打包、部署和重啓線上機器等流程;

5.1、GlueHandler 剖析

GlueHandler是 "可執行邏輯"GLUE 的代碼實現,本質上是實現統一父接口的子類, 約定了公共方法以及公共的輸入輸出以便於與業務代碼交互。

其源碼維護在數據庫表中, 接入方通過GroovyClassLoader加載相應源碼並實例化爲 "GlueHandler對象", 調用時將會執行父類公共方法。

統一父接口:

package com.xxl.glue.core.handler;

import java.util.Map;

/**
 * default glue iface, it could be use in your biz service
 * @author xuxueli 2016-1-2 21:31:56
 */
public interface GlueHandler {
	
	/**
	 * defaule method
	 * @param params
	 * @return
	 */
	public Object handle(Map<String, Object> params);
	
}

Groovy簡介 : 用於 Java 虛擬機的一種敏捷的動態語言;

  • 1、以強大的Java爲基礎;
  • 2、包含Python、Ruby、Smalltalk等語言強帶附加功能,例如動態類型轉換、閉包和元編程支持;
  • 3、一種成熟的面嚮對象語言,同時可用作純粹的校驗語言;
  • 4、適合與Spring動態語言支持一起使用,因爲它專門爲JVM設計,充分考慮Java繼承;
  • 5、與Java代碼互操作很容易;

5.2、GlueHandler 執行步驟

輸入圖片說明

接入方,執行託管在GLUE平臺上的一個GlueHandler中的代碼邏輯時, 執行步驟如下:

  • 1、緩存命中: 首先, 在本地緩存中匹配 "GlueHandler實例" , 匹配成功則調用統一公共方法即可; 否則執行步驟2;
  • 2、實例化: RPC方式加載相應GlueHandler對應源碼, 並通過 "GroovyClassLoader" 最終實例化爲 "GlueHandler實例" ;
  • 3、依賴注入: 執行 "injectService", 反射方式爲 "GlueHandler實例" 注入依賴的Spring服務;
  • 4、加入緩存: 將 "GlueHandler實例" 加入本地緩存;
  • 5、註冊監聽: 註冊對該 "GlueHandler" 的廣播消息監聽;
  • 6、invoke: 執行 "GlueHandler實例" 的公共方法, 返回執行結果;
  • 7、Finish;

5.3、Spring服務注入

支持 “Resource.class” 和 “Autowired.class” 兩種方式爲GlueHandler輸入Spring服務,實現邏輯如下:

  • 1、 反射獲取GlueHandler的Field數組;
  • 2、 遍歷Field數組,根據其註解 “@Resource” 和 “@Autowired” 在Spring容器匹配服務(注入規則同Spring默認規則:@Autowired按類型注入;@Resource按照首先名稱注入,失敗則按照類型注入;);
  • 3、將匹配到的Spring服務注入到該Field中。

5.4、廣播組件

Glue中通過ZK實現了一套廣播機制, 採用廣播的方式進行觸發主動更新。 系統在ZK中持久化一個node節點, 當GLUE需要廣播更新時將會將廣播消息(包含:GLUE名稱、灰度項目、版本號等)序列化後賦值給該節點。該GLUE的接入方項目將會監聽到事件通知並及時刷新緩存;

5.5、緩存更新策略(異步 + 覆蓋)

輸入圖片說明

緩存對象

Glue中緩存的對象是“groovyClassLoader”解析生成的GlueHandler實例。

Timeout

GlueHandler緩存支持設置Timeout時間,單位毫秒,緩存失效時將會實例化加載新的GLueHander實例,Timeout設置爲-1時將永不失效。

避免緩存雪崩

常規緩存更新,通常是通過remove(key)的方式進行緩存清理,然後在下次請求時將會以懶加載的方式進行緩存初始化,但是這樣在併發環境中有雪崩的隱患。 爲避免緩存雪崩情況,GlueHandler採用 “異步(queue + thread)”+“覆蓋”的方式進行GlueHandler更新,步驟如下:

1、在接收到緩存更新的廣播消息時,首先會將待更新的GlueHandler的名稱push到待更新隊列中;
2、異步線程監控待更新隊列,獲取待更新GlueHandler名稱,加載並實例化新GlueHandler實例;
3、將新的GlueHandler實例,覆蓋緩存中舊的GlueHandler實例,後續調用將會執行新的業務邏輯。
避免冗餘的緩存刷新

常規緩存刷新,通常流程是:每點擊一次刷新,生成一個新value值,覆蓋舊的value值。但是,當新value值和舊value值相等時,這種邏輯是冗餘甚至會降低性能的,特別是生成新value比較複雜時。 爲避免冗餘緩存刷新情況,底層對每個GLUE記錄一個version,當監聽到GLUE廣播刷新消息時會對比version是否一致,相同版本的GLUE不會觸發GlueLoader的刷新流程。

5.6、灰度更新

GlueHandler通過廣播的方式進行推送更新,在推送廣播消息時支持輸入待刷新該GlueHandler的項目AppName列表,只有匹配到的項目纔會對本項目中GlueHandler進行覆蓋更新,否則忽視該條廣播消息。爲空則全站廣播。 因此,可通過上述機制只刷新集羣中一臺機器上的某個GlueHandler,從而實現灰度功能。

5.7、版本回溯

GlueHandler的每次更新都會進行歷史版本源碼備份,默認支持記錄最近的30個版本。 同時,在Web IDE界面上,可以查看到所有的備份記錄,並且可以方便的進行版本回退。

5.8、預熱

GlueHandler創建時和項目關聯起來,這樣在項目啓動時會主動加載關聯到的GlueHandler,避免懶加載引起的併發問題。

5.9、避免因Permanet Generation空間被佔滿引起的Full GC

PermanetGeneration中存放的爲一些class的信息等,當系統中要加載的類、反射的類和調用的方法較多時,Permanet Generation可能會被佔滿。

GLUE底層基於Groovy實現,Groovy之前使用時曾經出現過頻繁Full GC的問題,原因如下:

系統在執行 “groovy.lang.GroovyClassLoader.parseClass(groovyScript)” 進行groovy代碼解析時,Groovy爲了保證解析後執行的都是最新的腳本內容,每進行一次解析都會生成一次新命名的Class文件,如下圖:

輸入圖片說明

因此,如果Groovy類加載器設置爲靜態,當對同一段腳本均多次執行該方法時,會導致 “GroovyClassLoader” 裝載的Class越來越多,從而導致PermGen被用滿。

爲了避免出現類似問題,GLUE做了以下幾點優化。

  • 1、針對每個GlueHandler解析後生成的Java對象實例做緩存,而不是代碼本身做緩存;
  • 2、僅僅在接收到清除緩存的廣播時解析生成新的GlueHandler實例對象,避免groovy的頻繁解析,減少Class裝載頻率;
  • 3、週期性的異步刷新類加載器,避免因全局類加載器裝載Class過多且不及時卸載導致的PermGen被用滿。

5.10、調試

由於GlueHandler源碼存儲在雲端, 調試不便。因此係統提供了 "com.xxl.glue.core.loader.impl.FileGlueLoader" ,用於開啓 "本地模式", 該模式系統將會從項目資源目錄的子目錄 "config/glue" 中加載源碼, 同時支持debug, 可以方便的進行業務代碼調試。

六、歷史版本

版本1.0.0

  • 1、動態(groovy):託管在平臺中的GlueHandler以 "groovy" 的方式進行加載實例化, 擴展JVM的動態語言支持;
  • 2、在線(Wed IDE):提供WedIDE,支持在線管理和開發GlueHandler;
  • 3、推送更新:當GlueHandler變動時, 將會通過廣播組件, 實時推送接入方對應的GlueHandler進行reload更新, 保證GlueHandler中業務邏輯的實時性;
  • 4、兼容Spring:無縫兼容Spring, 支持@Resource和@Autowired兩種方式注入Spring容器中服務;
  • 5、版本:支持50個歷史版本的版本回溯;

版本1.1.0

  • 1、系統重構;
  • 2、調試: 在開發階段可開啓本地模式, 該模式下將會加載本地GlueHandler文件, 支持Debug, 可以方便的進行本地調試;

版本1.2.0

  • 1、廣播組件由Activemq改爲自主實現的基於ZK的廣播組件, 減少系統第三方依賴;
  • 2、新增Local模式,提供GLUE本地加載器, 支持加載本地GlueHandler, 方便進行Debug調試;
  • 3、異步刷新緩存邏輯更新,新實例正常則覆蓋,否則remove掉舊實例;
  • 4、修復一處因ReentrantLock導致可能死鎖的問題;
  • 5、導航菜單更新;
  • 6、底層代碼重構, 結構優化;

版本1.3.0

  • 1、項目分組:支持設置項目分組,以項目爲維度進行GLUE分組管理;
  • 2、UI和交互升級;
  • 3、GLUE依賴注入邏輯優化,支持別名注入;
  • 4、廣播機制優化:接收到GLUE刷新廣播時校驗GLUE源碼version,避免冗餘的刷新邏輯;

版本1.3.1

  • 1、項目推送maven中央倉庫,groupId遷移至 "com.xuxueli";
  • 2、規範maven依賴版本;

TODO LIST

  • 4、用戶管理;
  • 5、權限管理;

七、其他

7.1 項目貢獻

歡迎參與項目貢獻!比如提交PR修復一個bug,或者新建 Issue 討論新特性或者變更。

7.2 用戶接入登記

更多接入的公司,歡迎在 登記地址 登記,登記僅僅爲了產品推廣。

7.3 開源協議和版權

產品開源免費,並且將持續提供免費的社區技術支持。個人或企業內部可自由的接入和使用。

  • Licensed under the GNU General Public License (GPL) v3.
  • Copyright (c) 2015-present, xuxueli.

捐贈

無論金額多少都足夠表達您這份心意,非常感謝 :) 前往捐贈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章