亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Classloader隔離技術(shù)在業(yè)務(wù)監(jiān)控中的應(yīng)用詳解

 更新時(shí)間:2022年08月25日 15:32:15   作者:得物技術(shù)  
這篇文章主要為大家介紹了Classloader隔離技術(shù)在業(yè)務(wù)監(jiān)控中的應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1. 背景&簡介

業(yè)務(wù)監(jiān)控平臺是得物自研的一款用于數(shù)據(jù)和狀態(tài)驗(yàn)證的平臺。能快速便捷發(fā)現(xiàn)線上業(yè)務(wù)臟數(shù)據(jù)和錯(cuò)誤邏輯,有效防止資產(chǎn)損失和保證系統(tǒng)穩(wěn)定性。

數(shù)據(jù)流向:

上圖的過濾和校驗(yàn)步驟的實(shí)際工作就是執(zhí)行一個(gè)用戶自定義的Groovy核對腳本。業(yè)務(wù)監(jiān)控內(nèi)部通過一個(gè)執(zhí)行腳本的模塊來實(shí)現(xiàn)。

本篇以腳本執(zhí)行模塊的一個(gè)技術(shù)問題為切入點(diǎn),給大家分享利用ClassLoader隔離技術(shù)實(shí)現(xiàn)腳本執(zhí)行隔離的經(jīng)驗(yàn)。

2. 業(yè)務(wù)監(jiān)控平臺腳本調(diào)試流程

業(yè)務(wù)監(jiān)控核心執(zhí)行邏輯是數(shù)據(jù)校驗(yàn)核對。不同域會有不同的數(shù)據(jù)校驗(yàn)核對規(guī)則。最初版本用戶編寫一個(gè)腳本進(jìn)行調(diào)試的步驟如下:

1.編寫數(shù)據(jù)校驗(yàn)?zāi)_本(在業(yè)務(wù)監(jiān)控平臺規(guī)則下),腳本demo:

@Service
public class DubboDemoScript implements DemoScript {
    @Resource
    private DemoService demoService;
    @Override
    public boolean filter(JSONObject jsonObject) {
        // 這里省略數(shù)據(jù)過濾邏輯 由業(yè)務(wù)使用方實(shí)現(xiàn)
        return true;
    }
    @Override
    public String check(JSONObject jsonObject) {
        Long id = jsonObject.getLong("id");
        // 數(shù)據(jù)校驗(yàn),由業(yè)務(wù)使用方實(shí)現(xiàn)
        Response responseResult = demoService.queryById(id);
        log.info("[DubboClassloaderTestDemo]返回結(jié)果={}", JsonUtils.serialize(responseResult));
        return JsonUtils.serialize(responseResult);
    }
}

其中DemoScript是業(yè)務(wù)監(jiān)控平臺定義的一個(gè)模板interface,  不同腳本實(shí)現(xiàn)此接口并重寫 filter和check兩個(gè)方法。filter方法是用來進(jìn)行數(shù)據(jù)過濾的,check方法是進(jìn)行數(shù)據(jù)核對校驗(yàn)的-用戶主要編寫這兩個(gè)方法中的邏輯。

2.在業(yè)務(wù)監(jiān)控平臺腳本調(diào)試頁面進(jìn)行調(diào)試腳本,當(dāng)腳本中有第三方團(tuán)隊(duì)Maven依賴時(shí)候,業(yè)務(wù)監(jiān)控平臺需要在pom.xml中添加Maven依賴并進(jìn)行發(fā)布,之后通知用戶再此進(jìn)行調(diào)試。

3.點(diǎn)擊腳本調(diào)試,查看腳本調(diào)試結(jié)果。

4.保存并上線腳本。

2.1 業(yè)務(wù)監(jiān)控的腳本開發(fā)調(diào)試流程圖

用戶想要調(diào)試一個(gè)腳本需要告知平臺開發(fā),平臺開發(fā)手動(dòng)將Maven依賴添加到project中并去發(fā)布平臺進(jìn)行發(fā)布。中間不僅特別耗時(shí),效率低,而且還要頻繁發(fā)布,嚴(yán)重影響了業(yè)務(wù)監(jiān)控平臺的用戶使用體驗(yàn)且增加平臺開發(fā)的維護(hù)成本。

為此,業(yè)務(wù)監(jiān)控平臺在新版本中使用了Classloader隔離技術(shù)來動(dòng)態(tài)加載腳本中依賴的業(yè)務(wù)方服務(wù)。業(yè)務(wù)監(jiān)控不需要再進(jìn)行特殊處理(添加Maven依賴再進(jìn)行發(fā)布),用戶在管控后臺直接上傳腳本以來的JAR文件就可以完成調(diào)試,大大降低了使用和維護(hù)成本,提高用戶體驗(yàn)。

3. 自定義Classloder | 打破雙親委派

3.1 什么是Classloader

ClassLoader是一個(gè)抽象類,我們用它的實(shí)例對象來裝載類 ,它負(fù)責(zé)將Java字節(jié)碼裝載到JVM中 , 并使其成為JVM一部分。JVM的類動(dòng)態(tài)加載技術(shù)能夠在運(yùn)行時(shí)刻動(dòng)態(tài)地加載或者替換系統(tǒng)的某些功能模塊,而不影響系統(tǒng)其他功能模塊的正常運(yùn)行。一般是通過類名讀入一個(gè)class文件來裝載這個(gè)類。

類裝載就是尋找一個(gè)類或是一個(gè)接口的字節(jié)碼文件并通過解析該字節(jié)碼來構(gòu)造代表這個(gè)類或是這個(gè)接口的class對象的過程 。在Java中,類裝載器把一個(gè)類裝入Java虛擬機(jī)中,要經(jīng)過三個(gè)步驟來完成:裝載、鏈接和初始化。

3.2 Classloader動(dòng)態(tài)加載依賴文件

利用Classloader實(shí)現(xiàn)類URLClassloader來實(shí)現(xiàn)依賴文件的動(dòng)態(tài)加載。示例代碼:

public class CustomClassLoader extends URLClassLoader {
/**
 * @param jarPath jar文件目錄地址
 * @return
 */
private CustomClassLoader createCustomClassloader(String jarPath) throws MalformedURLException {
    File file = new File(jarPath);
    URL url = file.toURI().toURL();
    List<URL> urlList = Lists.newArrayList(url);
    URL[] urls = new URL[urlList.size()];
    urls = urlList.toArray(urls);
    return new CustomJarClassLoader(urls, classLoader.getParent());
}
public CustomClassLoader(URL[] urls, ClassLoader parent) {
    super(urls, parent);
}

在新增依賴文件的時(shí)候,使用Classloader的addURL方法動(dòng)態(tài)添加來進(jìn)行實(shí)現(xiàn)。

如果所有腳本使用同一個(gè)類加載器,來進(jìn)行加載,就會出現(xiàn)問題,原因:同一個(gè)類(全限定名一樣)只會被類加載器加載一次(雙親委派)。但是不同腳本存在兩個(gè)全限定名一樣的情況,但是方法或者屬性不相同,因此加載一次就會導(dǎo)致其中一個(gè)腳本核對邏輯出錯(cuò)。

在理解了上面的情況下,我們就需要打破Java雙親委派機(jī)制,這里要知道一個(gè)知識點(diǎn):一個(gè)類的全限定名以及加載該類的加載器兩者共同形成了這個(gè)類在JVM中的唯一標(biāo)識,因此就需要自定義類加載器,讓腳本和Classloader一一對應(yīng)且各不相同。話不多說,直接上干貨:

3.3 自定義類加載器

public class CustomClassLoader extends URLClassLoader {
    public JarFile jarFile;
    public ClassLoader parent;
    public CustomClassLoader(URL[] urls, JarFile jarFile, ClassLoader parent) {
        super(urls, parent);
        this.jarFile = jarFile;
        this.parent = parent;
    }
    public CustomClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }
    private static String classNameToJarEntry(String name) {
        String classPath = name.replaceAll("\\.", "\\/");
        return new StringBuilder(classPath).append(".class").toString();
    }
    /**
     * 重寫loadClass方法,按照類包路徑規(guī)則拉進(jìn)行加載Class到j(luò)vm
     * @param name 類全限定名
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 這里定義類加載規(guī)則,和findClass方法一起組合打破雙親
        if (name.startsWith("com.xx") || name.startsWith("com.yyy")) {
           return this.findClass(name);
        }
        return super.loadClass(name, resolve);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        try {
            String jarEntryName = classNameToJarEntry(name);
            if (jarFile == null) {
                return clazz;
            }
            JarEntry jarEntry = jarFile.getJarEntry(jarEntryName);
            if (jarEntry != null) {
                InputStream inputStream = jarFile.getInputStream(jarEntry);
                byte[] bytes = IOUtils.toByteArray(inputStream);
                clazz = defineClass(name, bytes, 0, bytes.length);
            }
        } catch (IOException e) {
            log.info("Custom classloader load calss {} failed", name)
        }
        return clazz;
    }
}

說明:上述自定義類加載器的loadClass和findClass方法一起達(dá)到破壞雙親委派機(jī)制的關(guān)鍵。其中super.loadClass(name, resolve)方法是不符合自定義類加載器規(guī)則的情況下,讓其父加載器(這里的父加載器就是LanuchUrlClassloader)進(jìn)行類加載,自定義類加載器只關(guān)注自己要加載的類,并按照腳本維度進(jìn)行緩存對應(yīng)的Classloader。

3.4 業(yè)務(wù)監(jiān)控使用CustomClassloader

腳本或者調(diào)試腳本過程中和Classloader之間的創(chuàng)建關(guān)系:

一個(gè)腳本對應(yīng)多個(gè)依賴的JAR文件(JAR文件在腳本調(diào)試頁面上傳到HDFS),一個(gè)腳本對應(yīng)一個(gè)classloader(并進(jìn)行本地緩存)(完全相同的兩個(gè)類在不同的classloader中加載后兩個(gè)Class對象是不相等的)。

3.5 業(yè)務(wù)監(jiān)控動(dòng)態(tài)加載JAR和腳本的實(shí)現(xiàn)

在上述的操作中,相信大家對JAR怎么實(shí)現(xiàn)腳本加載的,和腳本中@Resource注解標(biāo)記的屬性DemoService類如何創(chuàng)建Bean和注入到Spring容器比較關(guān)注。貼張流程圖來講解:

流程圖中生成FeignClient對象的創(chuàng)建源碼:

/**
 * 
 * @param serverName 服務(wù)名 (@FeignClient主鍵中的name值)
 *  eg:@FeignClient("demo-interfaces") 
 * @param beanName feign對象名稱 eg: DemoFeignClient
 * @param targetClass feign的Class對象 
 * @param <T> FeignClient主鍵標(biāo)記的Object
 * @return
 */
public static <T> T build(String serverName, String beanName, Class<T> targetClass) {
    return buildClient(serverName, beanName, targetClass);
}
private static <T> T buildClient(String serverName, String beanName, Class<T> targetClass) {
    T t = (T) BEAN_CACHE.get(serverName + "-" + beanName);
    if (Objects.isNull(t)) {
        FeignClientBuilder.Builder<T> builder = new FeignClientBuilder(applicationContext).forType(targetClass, serverName);
        t = builder.build();
        BEAN_CACHE.put(serverName + "-" + beanName, t);
    }
    return t;
}

流程圖中生成注冊Dubbo consumer的源碼:

public void registerDubboBean(Class clazz, String beanName) {
        // 當(dāng)前應(yīng)用配置
    ApplicationConfig application = new ApplicationConfig();
    application.setName("demo-service");
    // 連接注冊中心配置
    RegistryConfig registry = new RegistryConfig();
    registry.setAddress(registryAddress);
    // ReferenceConfig為重對象,內(nèi)部封裝了與注冊中心的連接,以及與服務(wù)提供方的連接
    ReferenceConfig reference = new ReferenceConfig&lt;&gt;(); // 此實(shí)例很重,封裝了與注冊中心的連接以及與提供者的連接,請自行緩存,否則可能造成內(nèi)存和連接泄漏
    reference.setApplication(application);
    reference.setRegistry(registry); // 多個(gè)注冊中心可以用setRegistries()
    reference.setInterface(clazz);
    reference.setVersion("1.0");
    // 注意:此代理對象內(nèi)部封裝了所有通訊細(xì)節(jié),這里用dubbo2.4版本以后提供的緩存類ReferenceConfigCache
    ReferenceConfigCache cache = ReferenceConfigCache.getCache();
    Object dubboBean = cache.get(reference);    
    dubboBeanMap.put(beanName, dubboBean);
    // 注冊bean
    SpringContextUtils.registerBean(beanName, dubboBean);
    // 注入bean
    SpringContextUtils.autowireBean(dubboBean);
}

以上就是Classloader隔離技術(shù)在業(yè)務(wù)監(jiān)控平臺的實(shí)際運(yùn)用,當(dāng)然在開發(fā)中也遇到一些問題,下面列舉2個(gè)例子。

4. 問題&原因&方案

問題一: 多個(gè)團(tuán)隊(duì)的Check腳本運(yùn)行在一起,單個(gè)應(yīng)用的Metaspace空間占用會不會過大?

答:隨著業(yè)務(wù)的發(fā)展,JAR文件的不斷增多,確實(shí)會出現(xiàn)元數(shù)據(jù)區(qū)占用過大的情況,這也是做Classloader隔離的原因。在做了這一步之后,為后面進(jìn)行腳本拆分做了鋪墊,比如按照應(yīng)用、團(tuán)隊(duì)等維度單獨(dú)部署應(yīng)用來運(yùn)行其對應(yīng)check腳本。這樣腳本和業(yè)務(wù)監(jiān)控邏輯上也進(jìn)行了拆分,也會降低主應(yīng)用的發(fā)布頻率帶來的噪音。

問題二:Classloader隔離實(shí)現(xiàn)上有沒有遇到什么難題?

答:中間遇到了一些問題,就是同一個(gè)全限定名的類,出現(xiàn)了CastException異常,此類問題是最容易出現(xiàn)的,也最容易想到的。

原因:同一個(gè)類被2個(gè)不同的Classloader對象加載了2次。解決也很簡單,使用同一個(gè)類加載器。

5. 總結(jié)

該篇文章講解了自定義Classloader的實(shí)現(xiàn)和如何做到隔離,如何動(dòng)態(tài)加載JAR文件,如何手動(dòng)注冊入Dubbo和Feign服務(wù)。類加載器動(dòng)態(tài)加載腳本技術(shù),在業(yè)務(wù)監(jiān)控平臺運(yùn)用再適合不過了。當(dāng)然一些業(yè)務(wù)場景也是可以參考此項(xiàng)技術(shù),來解決一些技術(shù)問題。

以上就是Classloader隔離技術(shù)在業(yè)務(wù)監(jiān)控中的應(yīng)用詳解的詳細(xì)內(nèi)容,更多關(guān)于Classloader業(yè)務(wù)監(jiān)控隔離技術(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中的弗洛伊德(Floyd)算法

    Java中的弗洛伊德(Floyd)算法

    這篇文章主要介紹了Java中的弗洛伊德(Floyd)算法,Floyd算法又稱為插點(diǎn)法,是一種利用動(dòng)態(tài)規(guī)劃的思想尋找給定的加權(quán)圖中多源點(diǎn)之間最短路徑的算法,與Dijkstra算法類似,需要的朋友可以參考下
    2024-01-01
  • java實(shí)現(xiàn)2048小游戲

    java實(shí)現(xiàn)2048小游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)2048小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • 關(guān)于SpringMVC中控制器如何處理文件上傳的問題

    關(guān)于SpringMVC中控制器如何處理文件上傳的問題

    這篇文章主要介紹了關(guān)于SpringMVC中控制器如何處理文件上傳的問題,在 Web 應(yīng)用程序中,文件上傳是一個(gè)常見的需求,例如用戶上傳頭像、上傳文檔等,本文將介紹 Spring MVC 中的控制器如何處理文件上傳,并提供示例代碼,需要的朋友可以參考下
    2023-07-07
  • JVM調(diào)優(yōu)實(shí)戰(zhàn)

    JVM調(diào)優(yōu)實(shí)戰(zhàn)

    本文主要介紹了JVM調(diào)優(yōu)實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Java實(shí)現(xiàn)數(shù)獨(dú)小游戲

    Java實(shí)現(xiàn)數(shù)獨(dú)小游戲

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)數(shù)獨(dú)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • SpringCloud筆記(Hoxton)Netflix之Ribbon負(fù)載均衡示例代碼

    SpringCloud筆記(Hoxton)Netflix之Ribbon負(fù)載均衡示例代碼

    這篇文章主要介紹了SpringCloud筆記HoxtonNetflix之Ribbon負(fù)載均衡,Ribbon是管理HTTP和TCP服務(wù)客戶端的負(fù)載均衡器,Ribbon具有一系列帶有名稱的客戶端(Named?Client),對SpringCloud?Ribbon負(fù)載均衡相關(guān)知識感興趣的朋友一起看看吧
    2022-06-06
  • Java查詢MongoDB數(shù)據(jù)庫案例大全

    Java查詢MongoDB數(shù)據(jù)庫案例大全

    這篇文章主要給大家介紹了關(guān)于Java查詢MongoDB數(shù)據(jù)庫的一些相關(guān)案例,Java可以使用MongoDB的官方Java驅(qū)動(dòng)程序來連接和操作MongoDB數(shù)據(jù)庫,需要的朋友可以參考下
    2023-07-07
  • Java中數(shù)組與集合的相互轉(zhuǎn)換實(shí)現(xiàn)解析

    Java中數(shù)組與集合的相互轉(zhuǎn)換實(shí)現(xiàn)解析

    這篇文章主要介紹了Java中數(shù)組與集合的相互轉(zhuǎn)換實(shí)現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • java如何創(chuàng)建一個(gè)jdbc程序詳解

    java如何創(chuàng)建一個(gè)jdbc程序詳解

    使用Java程序來操作數(shù)據(jù)庫,后者更加直接的話就是使用Java程序來發(fā)送SQL語句的技術(shù)稱之為:JDBC。下面這篇文章主要給大家介紹了關(guān)于利用java如何創(chuàng)建一個(gè)jdbc程序的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-11-11
  • mybatis分頁及模糊查詢功能實(shí)現(xiàn)

    mybatis分頁及模糊查詢功能實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)為大家詳細(xì)介紹了mybatis實(shí)現(xiàn)分頁及模糊查詢功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06

最新評論