欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?熱更新?Groovy?實踐及踩坑指南(推薦)

 更新時間:2022年09月13日 10:40:46   作者:是咕咕雞  
Apache的Groovy是Java平臺上設計的面向?qū)ο缶幊陶Z言,這門動態(tài)語言擁有類似Python、Ruby和Smalltalk中的一些特性,可以作為Java平臺的腳本語言使用,這篇文章主要介紹了Java?熱更新?Groovy?實踐及踩坑指南,需要的朋友可以參考下

Groovy 是什么?

Apache的Groovy是Java平臺上設計的面向?qū)ο缶幊陶Z言。這門動態(tài)語言擁有類似Python、Ruby和Smalltalk中的一些特性,可以作為Java平臺的腳本語言使用,Groovy代碼動態(tài)地編譯成運行于Java虛擬機(JVM)上的Java字節(jié)碼,并與其他Java代碼和庫進行互操作。

由于其運行在JVM上的特性,Groovy可以使用其他Java語言編寫的庫。Groovy的語法與Java非常相似,大多數(shù)Java代碼也符合Groovy的語法規(guī)則,盡管可能語義不同。 Groovy 1.0于2007年1月2日發(fā)布,并于2012年7月發(fā)布了Groovy 2.0。從版本2開始,Groovy也可以靜態(tài)編譯,提供類型推論和Java相近的性能。Groovy 2.4是Pivotal軟件贊助的最后一個主要版本,截止于2015年3月。Groovy已經(jīng)將其治理結(jié)構(gòu)更改為Apache軟件基金會的項目管理委員會(PMC)[1]。

Java 為何需要 Groovy ?

Groovy 特性如下:

  • 語法上支持動態(tài)類型,閉包等新一代語言特性
  • 無縫集成所有已經(jīng)存在的Java類庫
  • 既支持面向?qū)ο缶幊桃仓С置嫦蜻^程編程
  • 執(zhí)行方式可以將groovy編寫的源文件編譯成class字節(jié)碼文件,然后交給JVM去執(zhí)行,也可以直接將groovy源文件解釋執(zhí)行。
  • Groovy可以與Java完美結(jié)合,而且可以使用java所有的庫

Groovy 優(yōu)勢如下:

  • 敏捷
    • groovy 在語法上加入了很多語法糖,很多 Java 嚴格的書寫語法,在 Groovy 中只需要少量的語法糖即可實現(xiàn)
  • Groovy 的靈活性是的它既可以作為變成語言,亦可作為腳本語言
  • 0成本學習 Groovy,完美適配 Java 語法

熱部署技術(shù)設計及實現(xiàn)

使用場景

我將介紹如下幾種常用的適合 Groovy 腳本熱更新的場景,供您學習

風控安全——規(guī)則引擎

風控的規(guī)則引擎非常適合用 groovy 來實現(xiàn),對抗黑產(chǎn),策略人員每天都都會產(chǎn)出攔截規(guī)則,如果每次都需要發(fā)版,可能發(fā)完觀測完后,該薅的羊毛都被黑產(chǎn)薅沒了。

所以利用 groovy 腳本引擎的動態(tài)解析執(zhí)行,使用規(guī)則腳本將查攔截規(guī)則抽象出來,快速部署,提升效率。

監(jiān)控中心

大型互聯(lián)網(wǎng)系統(tǒng),伴隨著海量數(shù)據(jù)進入,各個層級的人員需要時時刻刻關(guān)注業(yè)務的各個維度指標,此時某個指標異常光靠人肉是沒辦法實現(xiàn)的。此時需要監(jiān)控中心介入,提前部署好異動規(guī)則,當異常發(fā)生時,監(jiān)控中心發(fā)出告警通知到對應的規(guī)則創(chuàng)建人員,從而盡快查明原因,挽回資損。

此時要保證監(jiān)控中心異常靈活,可以隨時隨地滿足業(yè)務人員或者研發(fā)人員配置監(jiān)控指標,測試我們可以使用 Groovy 條件表達式,滿足靈活監(jiān)控規(guī)則配置需求。

活動營銷

營銷活動配置是我個人覺得最復雜的業(yè)務之一?;顒幽0宥鄻?,千人千面,不同人群看到的活動樣式或者“獎品”不一。且活動上線要快,效果回收,投入產(chǎn)出比等要能立即觀測。

此時需要工程側(cè)抽象出整個活動模板,在需要變化的地方嵌入 Groovy 腳本,這樣就減少了測試和發(fā)版的時間,做到活動可線上配置化。

技術(shù)實現(xiàn)

腳本加載/更新

代碼實現(xiàn)展示:

/**
 * 加載腳本
 * @param script
 * @return
 */
public static GroovyObject buildScript(String script) {
    if (StringUtils.isEmpty(script)) {
        throw new RuntimeException("script is empty");
    }

    String cacheKey = DigestUtils.md5DigestAsHex(script.getBytes());
    if (groovyObjectCache.containsKey(cacheKey)) {
        log.debug("groovyObjectCache hit");
        return groovyObjectCache.get(cacheKey);
    }

    GroovyClassLoader classLoader = new GroovyClassLoader();
    try {
        Class<?> groovyClass = classLoader.parseClass(script);
        GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
        classLoader.clearCache();

        groovyObjectCache.put(cacheKey, groovyObject);
        log.info("groovy buildScript success: {}", groovyObject);
        return groovyObject;
    } catch (Exception e) {
        throw new RuntimeException("buildScript error", e);
    } finally {
        try {
            classLoader.close();
        } catch (IOException e) {
            log.error("close GroovyClassLoader error", e);
        }
    }
}

重點關(guān)注:

  • 腳本開啟緩存處理:否則多次會更新可能會導致 Metaspace OutOfMemery

腳本執(zhí)行

// 程序內(nèi)部需要關(guān)聯(lián)出待執(zhí)行的腳本即可
try {
    Map<String, Object> singleMap = GroovyUtils.invokeMethod2Map(s.getScriptObject(), s.getInvokeMethod(), params);
    data.putAll(singleMap);
} catch (Throwable e) {
    log.error(String.format("RcpEventMsgCleanScriptGroovyHandle groovy error, guid: %d eventCode: %s",
            s.getGuid(), s.getEventCode()), e);
}

// 三種執(zhí)行方式,看 腳本內(nèi)部返回的結(jié)果是什么
public static Map<String, Object> invokeMethod2Map(GroovyObject scriptObject, String invokeMethod, Object[] params) {
    return (Map<String, Object>) scriptObject.invokeMethod(invokeMethod, params);
}

public static boolean invokeMethod2Boolean(GroovyObject scriptObject, String invokeMethod, Object[] params) {
    return (Boolean) scriptObject.invokeMethod(invokeMethod, params);
}

public static String invokeMethod2String(GroovyObject scriptObject, String invokeMethod, Object[] params) {
    log.debug("GroovyObject class: {}", scriptObject.getClass().getSimpleName());
    return (String) scriptObject.invokeMethod(invokeMethod, params);
}

生產(chǎn)踩坑指南

Java8 lambda 與 Groovy 語法問題

都說 Groovy 能完美兼容 Java 語法,即直接復制 Java 代碼到 Groovy 文件內(nèi),亦能編譯成功。

事實真的如此么,我們看如下執(zhí)行的代碼:

Set<String> demo = new HashSet<>();
demo.add("111");
demo.add("222");
for (String s : demo) {
    executor.submit({ -> 
        println "submit: " + s;                 
    });
}

for (String s in demo) {
    executor.submit({ -> 
        println "sp submit: " + s;                 
    });
}
// 輸出結(jié)果
// submit: 222
// sp submit: 222
// submit: 222
// sp submit: 222

此時代碼并沒有按照預期的結(jié)果輸出 111, 222,這是為什么呢?

答:lambda 語法在 Groovy 中語義和在Java 中不一致,雖然編譯不出錯,但表達的語義不一致

在 Groovy 中表示閉包概念,此處不熟悉的可以 Google 詳細了解 Groovy 語法。

GroovyClassLoader 加載機制導致頻繁gc問題

通常加載 Groovy 類代碼如下:

GroovyClassLoader groovyLoader = new GroovyClassLoader();
Class<Script> groovyClass = (Class<Script>) groovyLoader.parseClass(groovyScript);
Script groovyScript = groovyClass.newInstance();

每次執(zhí)行 groovyLoader.parseClass(groovyScript),Groovy 為了保證每次執(zhí)行的都是新的腳本內(nèi)容,會每次生成一個新名字的Class文件,這個點已經(jīng)在前文中說明過。當對同一段腳本每次都執(zhí)行這個方法時,會導致的現(xiàn)象就是裝載的Class會越來越多,從而導致PermGen被用滿。

同時這里也存在性能瓶頸問題,如果去分析這段代碼會發(fā)現(xiàn)90%的耗時占用在Class。

如上實戰(zhàn)過程中,已經(jīng)給出了解決辦法:

  • 對于 parseClass 后生成的 Class 對象進行cache,key 為 groovyScript 腳本的md5值

腳本首次執(zhí)行耗時高

在初期方案上線時,壓測后顯示,首次加載腳本性能較慢,后續(xù)腳本執(zhí)行速度非???,猜測可能是 Groovy 內(nèi)部在首次腳在腳本時還做了其他的校驗(本人還沒跟進這塊,如果有讀者感興趣,可以斷點詳細看下鏈路耗時在哪里)

正對首次加載緩慢問題,解決方法如下:

// 1.加載腳本,并緩存
GroovyObject object = loadClass(classSeq);
cacheMap.put(md5(classSeq), object);
// 2.預熱
// 模擬方法調(diào)用
cacheMap.get(md5(classSeq)).invoke();

// 3.開放給線上流量使用

到此這篇關(guān)于Java 熱更新 Groovy 實踐及踩坑指南的文章就介紹到這了,更多相關(guān)Java 熱更新 Groovy內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot+mybatis plus實現(xiàn)樹形結(jié)構(gòu)查詢

    springboot+mybatis plus實現(xiàn)樹形結(jié)構(gòu)查詢

    實際開發(fā)過程中經(jīng)常需要查詢節(jié)點樹,根據(jù)指定節(jié)點獲取子節(jié)點列表,本文主要介紹了springboot+mybatis plus實現(xiàn)樹形結(jié)構(gòu)查詢,感興趣的可以了解一下
    2021-07-07
  • java實現(xiàn)發(fā)送短信驗證碼

    java實現(xiàn)發(fā)送短信驗證碼

    這篇文章主要為大家詳細介紹了java實現(xiàn)發(fā)送短信驗證碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • Java提取2個集合中的相同和不同元素代碼示例

    Java提取2個集合中的相同和不同元素代碼示例

    這篇文章主要介紹了Java提取2個集合中的相同和不同元素代碼示例,涉及對removeall方法的簡單介紹,然后分享了主要的示例代碼,具有一定借鑒價值,需要的朋友可以參考下。
    2017-11-11
  • Java常用類庫Apache Commons工具類說明及使用實例詳解

    Java常用類庫Apache Commons工具類說明及使用實例詳解

    這篇文章主要介紹了Java常用類庫Apache Commons工具類說明及使用實例詳解,需要的朋友可以參考下
    2020-02-02
  • Java實現(xiàn)權(quán)重隨機算法詳解

    Java實現(xiàn)權(quán)重隨機算法詳解

    平時,經(jīng)常會遇到權(quán)重隨機算法,從不同權(quán)重的N個元素中隨機選擇一個,并使得總體選擇結(jié)果是按照權(quán)重分布的。本文就詳細來介紹如何實現(xiàn),感興趣的可以了解一下
    2021-07-07
  • Java字符串拼接+和StringBuilder的比較與選擇

    Java字符串拼接+和StringBuilder的比較與選擇

    Java 提供了兩種主要的方式:使用 "+" 運算符和使用 StringBuilder 類,本文主要介紹了Java字符串拼接+和StringBuilder的比較與選擇,感興趣的可以了解一下
    2023-10-10
  • springboot項目部署在linux上運行的兩種方式小結(jié)

    springboot項目部署在linux上運行的兩種方式小結(jié)

    這篇文章主要介紹了springboot項目部署在linux上運行的兩種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java數(shù)據(jù)結(jié)構(gòu)實現(xiàn)機器人行走

    java數(shù)據(jù)結(jié)構(gòu)實現(xiàn)機器人行走

    這篇文章主要為大家詳細介紹了java數(shù)據(jù)結(jié)構(gòu)實現(xiàn)機器人行走,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • SpringBoot3集成iText實現(xiàn)PDF導出功能

    SpringBoot3集成iText實現(xiàn)PDF導出功能

    不知道小伙伴們在項目中有沒有遇到過導出 PDF 的需求,小編在之前的 tienchin 項目中有一個合同導出的功能,需要將文檔導出為PDF,將文檔導出為 PDF 有很多方案,不同方案的優(yōu)缺點也各不相同,今天小編就和大家演示一個,感興趣的小伙伴跟著小編一起來看看吧
    2024-10-10
  • 18個Java8日期處理的實踐(太有用了)

    18個Java8日期處理的實踐(太有用了)

    這篇文章主要介紹了18個Java8日期處理的實踐(太有用了),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-01-01

最新評論