XXL-GLUE 是一個分佈式環境下的 "可執行邏輯單元" 管理平臺, 學習簡單,擴展JVM的動態語言支持。現已開放源代碼並接入多家公司線上產品線,開箱即用。html
GLUE:即"可執行邏輯",本質上是一段可執行的代碼。GLUE能夠方便的嵌入業務代碼中, GLUE中邏輯代碼支持在線開發、動態推送更新、實時編譯生效。 能夠節省部分由於項目編譯、打包、部署和重啓線上機器所帶來的時間和人工消耗, 提升開發效率。java
能夠參考 「配置管理系統,如disconf xxl-conf等」 概念來幫助咱們來理解XXL-GLUE。 前者維護 "配置信息",並且支持數據類型有限。XXL-GLUE功能更強大, 支持維護"可執行邏輯代碼"。 XXL-GLUE在功能上徹底能夠替代前者,只須要在可執行代碼塊中返回配置便可,支持返回任意類型配置數據。XXL-GLUE主要做用是託管"可執行邏輯代碼",將會爲開發者代碼不同的開發體驗。node
源碼倉庫地址 | Release Download |
---|---|
https://github.com/xuxueli/xxl-glue | Download |
https://gitee.com/xuxueli0323/xxl-glue | Download |
/db : 數據庫交表腳本位置 /xxl-glue-admin : GLUE管理中心 /xxl-glue-core : 公共依賴 /xxl-glue-core-example : GLUE接入Example項目, 能夠參考它來學習如何在項目中接入並使用GLUE
執行數據庫建表腳本: /xxl-glue/doc/db/mysql_xxl_glue.sqlmysql
配置文件位置:xxl-glue-admin/resources/xxl-glue-admin.propertiesgit
### 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包部署便可。github
<!-- 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.propertiesredis
### 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包部署便可。sql
登錄 "GLUE管理中心" 並點擊右上角 "新增GLUE" 按鈕,填寫 「GLUE名稱」(該名稱是該GLUE項的惟一標示)和簡介,肯定後即新增一條GLUE。數據庫
點擊GLUE右側 「Web IDE」按鈕,便可進入GLUE的代碼開發界面,可在該界面開發GLUE代碼,也能夠在IDE中開發完成後粘貼進來,默認已經初始化Demo代碼。 (每一個Glue必須是實現統一父接口GlueHandler的子類;詳情可參考章節 "5.1" )編程
初始化數據局以後,系統默認生成了三個典型場景的GLUE示例,能夠參考GLUE示例開發第一個GLUE(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/
系統以項目爲維度進行GLUE分組管理;能夠在 "項目管理" 模塊查看系統中的項目列表,默認已經提供了一個 "示例項目";
在 "項目管理" 界面,點擊右上角 "新增項目" 能夠新增項目,項目屬性說明以下:
項目AppName:項目AppName爲項目分組標識,在廣播刷新GLUE時可指定AppName實現灰度刷新指定AppNamd項目中的GLUE示例。正確格式爲:長度4-20位的小寫字母、數字和下劃線 項目名稱:項目中文名稱
點擊右上角 "新建GLUE" 按鈕,彈框填寫GLUE信息便可新建GLUE,屬性介紹以下:
項目:GLUE所屬的項目, GLUE:Glue名稱,每一個GLUE的惟一標示,新建GLUE時將會自動將所項目的AppName做爲名稱前綴。正確格式爲:長度4-20位的大小寫字母、數字和下劃線 描述:GLUE的描述介紹信息
找到新建的GLUE,點擊右側的 「Web IDE」 按鈕進入GLUE開發的Wed IDE界面。默認已經初始化示例代碼,如需開發業務代碼,只須要在handle方法中開發便可。
首先肯定項目中已經接入GLUE(參考上文 「GLUE接入Example項目(xxl-glue-core-example)」,接入很是方便);
業務中調用Glue只須要執行如下一行代碼便可:
Object result = GlueFactory.glue("glue名稱", "glue入參,Map類型");
Glue在第一次加載以後將會緩存在內存中,點擊右側 「清除緩存」 按鈕能夠推送刷新GLUE緩存。 清除緩存彈框中有一個輸入框 "Witch APP", 輸入接入方項目的AppName, 便可精確的灰度刷新該項目中的相應Glue。若是不輸入, 則廣播刷新全部項目中響應的Glue。
首先,須要瞭解源碼加載器配置,見項目"xxl-glue-core-example"的"applicationcontext-glue.xml"配置中"GlueFactory"實例的"glueLoader"屬性;
當源碼加載器選擇 "FileGlueLoader" 時,將會加載本地GLUE腳本文件,此時能夠進行代碼Debug操做。
尤爲適用於數據結構比較複雜的配置項
package com.xxl.glue.example.handler; import com.xxl.glue.core.handler.GlueHandler; import java.util.HashSet; import java.util.Map; /** * 示例場景01:託管 「配置信息」 * * 優勢: * 一、在線編輯;推送更新; * 二、該場景下,相較於同類型配置管理系統,支持數據類型更加豐富,不只支持基礎類型,甚至支持複雜對象; * 三、該場景下,配置信息的操做和展現,更加直觀; * * @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; } }
能夠將配置解析邏輯一併託管,只關注返回結果便可
package com.xxl.glue.example.handler; import com.xxl.glue.core.handler.GlueHandler; import java.util.HashSet; import java.util.Map; /** * 示例場景02:託管 「靜態方法」 * * 優勢: * 一、在線編輯;推送更新; * 二、該場景下,託管公共組件,方便組件統一維護和升級; * * @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); } }
能夠靈活組裝接口和服務,擴展服務的動態特性,做爲公共服務。
package com.xxl.glue.example.handler; import com.xxl.glue.core.handler.GlueHandler; import java.util.Map; /** * 示例場景03:託管 「動態服務」 * * 優勢: * 一、在線編輯;推送更新; * 二、該場景下,服務內部能夠靈活組裝和調用其餘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 的代碼實現,本質上是實現統一父接口的子類, 約定了公共方法以及公共的輸入輸出以便於與業務代碼交互。
其源碼維護在數據庫表中, 接入方經過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 虛擬機的一種敏捷的動態語言;
接入方,執行託管在GLUE平臺上的一個GlueHandler中的代碼邏輯時, 執行步驟以下:
支持 「Resource.class」 和 「Autowired.class」 兩種方式爲GlueHandler輸入Spring服務,實現邏輯以下:
Glue中經過ZK實現了一套廣播機制, 採用廣播的方式進行觸發主動更新。 系統在ZK中持久化一個node節點, 當GLUE須要廣播更新時將會將廣播消息(包含:GLUE名稱、灰度項目、版本號等)序列化後賦值給該節點。該GLUE的接入方項目將會監聽到事件通知並及時刷新緩存;
Glue中緩存的對象是「groovyClassLoader」解析生成的GlueHandler實例。
GlueHandler緩存支持設置Timeout時間,單位毫秒,緩存失效時將會實例化加載新的GLueHander實例,Timeout設置爲-1時將永不失效。
常規緩存更新,一般是經過remove(key)的方式進行緩存清理,而後在下次請求時將會以懶加載的方式進行緩存初始化,可是這樣在併發環境中有雪崩的隱患。 爲避免緩存雪崩狀況,GlueHandler採用 「異步(queue + thread)」+「覆蓋」的方式進行GlueHandler更新,步驟以下:
一、在接收到緩存更新的廣播消息時,首先會將待更新的GlueHandler的名稱push到待更新隊列中; 二、異步線程監控待更新隊列,獲取待更新GlueHandler名稱,加載並實例化新GlueHandler實例; 三、將新的GlueHandler實例,覆蓋緩存中舊的GlueHandler實例,後續調用將會執行新的業務邏輯。
常規緩存刷新,一般流程是:每點擊一次刷新,生成一個新value值,覆蓋舊的value值。可是,當新value值和舊value值相等時,這種邏輯是冗餘甚至會下降性能的,特別是生成新value比較複雜時。 爲避免冗餘緩存刷新狀況,底層對每一個GLUE記錄一個version,當監聽到GLUE廣播刷新消息時會對比version是否一致,相同版本的GLUE不會觸發GlueLoader的刷新流程。
GlueHandler經過廣播的方式進行推送更新,在推送廣播消息時支持輸入待刷新該GlueHandler的項目AppName列表,只有匹配到的項目纔會對本項目中GlueHandler進行覆蓋更新,不然忽視該條廣播消息。爲空則全站廣播。 所以,可經過上述機制只刷新集羣中一臺機器上的某個GlueHandler,從而實現灰度功能。
GlueHandler的每次更新都會進行歷史版本源碼備份,默認支持記錄最近的30個版本。 同時,在Web IDE界面上,能夠查看到全部的備份記錄,而且能夠方便的進行版本回退。
GlueHandler建立時和項目關聯起來,這樣在項目啓動時會主動加載關聯到的GlueHandler,避免懶加載引發的併發問題。
PermanetGeneration中存放的爲一些class的信息等,當系統中要加載的類、反射的類和調用的方法較多時,Permanet Generation可能會被佔滿。
GLUE底層基於Groovy實現,Groovy以前使用時曾經出現過頻繁Full GC的問題,緣由以下:
系統在執行 「groovy.lang.GroovyClassLoader.parseClass(groovyScript)」 進行groovy代碼解析時,Groovy爲了保證解析後執行的都是最新的腳本內容,每進行一次解析都會生成一次新命名的Class文件,以下圖:
所以,若是Groovy類加載器設置爲靜態,當對同一段腳本均屢次執行該方法時,會致使 「GroovyClassLoader」 裝載的Class愈來愈多,從而致使PermGen被用滿。
爲了不出現相似問題,GLUE作了如下幾點優化。
因爲GlueHandler源碼存儲在雲端, 調試不便。所以系統提供了 "com.xxl.glue.core.loader.impl.FileGlueLoader" ,用於開啓 "本地模式", 該模式系統將會從項目資源目錄的子目錄 "config/glue" 中加載源碼, 同時支持debug, 能夠方便的進行業務代碼調試。
歡迎參與項目貢獻!好比提交PR修復一個bug,或者新建 Issue 討論新特性或者變動。
更多接入的公司,歡迎在 登記地址 登記,登記僅僅爲了產品推廣。
產品開源免費,而且將持續提供免費的社區技術支持。我的或企業內部可自由的接入和使用。
不管金額多少都足夠表達您這份心意,很是感謝 :) 前往捐贈