日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

OpenTelemetry agent 對 Spring Boot 應用的影響:一次 SPI 失效的調查

來源: 責編: 時間:2024-05-16 09:07:52 200觀看
導讀背景前段時間公司領導讓我排查一個關于在 JDK21 環境中使用 Spring Boot 配合一個 JDK18 新增的一個 SPI(java.net.spi.InetAddressResolverProvider) 不生效的問題。但這個不生效的前置條件有點多:JDK 的版本得在 18+

背景

前段時間公司領導讓我排查一個關于在 JDK21 環境中使用 Spring Boot 配合一個 JDK18 新增的一個 SPI(java.net.spi.InetAddressResolverProvider) 不生效的問題。ivH28資訊網——每日最新資訊28at.com

但這個不生效的前置條件有點多:ivH28資訊網——每日最新資訊28at.com

  • JDK 的版本得在 18+
  • SpringBoot3.x
  • 還在額外再配合使用 -javaagent:opentelemetry-javaagent.jar 使用,也就是 OpenTelemetry 提供的 agent。

才會導致自定義的 InetAddressResolverProvider 無法正常工作。ivH28資訊網——每日最新資訊28at.com

在復現這個問題之前先簡單介紹下 java.net.spi.InetAddressResolverProvider 這個 SPI;它是在 JDK18 之后才提供的,在這之前我們使用 InetAddress 的內置解析器來解析主機名和 IP 地址,但這個解析器之前是不可以自定義的。ivH28資訊網——每日最新資訊28at.com

在某些場景下會不太方便,比如我們需要請求 order.service 這個域名時希望可以請求到某一個具體 IP 地址上,我們可以自己配置 host ,或者使用服務發現機制來實現。ivH28資訊網——每日最新資訊28at.com

但現在通過 InetAddressResolverProvider 就可以定義在請求這個域名的時候返回一個我們預期的 IP 地址。ivH28資訊網——每日最新資訊28at.com

同時由于它是一個 SPI,所以我們只需要編寫一個第三方包,任何項目依賴它之后在發起網絡請求時都會按照我們預期的 IP 進行請求。ivH28資訊網——每日最新資訊28at.com

復現

要使用它也很簡單,主要是兩個類:ivH28資訊網——每日最新資訊28at.com

  • InetAddressResolverProvider:這是一個抽象類,我們可以繼承它之后重寫它的 get 函數返回一個 InetAddressResolver 對象
  • InetAddressResolver:一個接口,主要提供了兩個函數;一個用于傳入域名返回 IP 地址,另一個反之:傳入 IP 地址返回域名。
public class MyAddressResolverProvider extends InetAddressResolverProvider {    @Override    public InetAddressResolver get(Configuration configuration) {        return new MyAddressResolver();    }    @Override    public String name() {        return "MyAddressResolverProvider Internet Address Resolver Provider";    }}public class MyAddressResolver implements InetAddressResolver {    public MyAddressResolver() {        System.out.println("=====MyAddressResolver");    }    @Override    public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy)            throws UnknownHostException {        if (host.equals("fedora")) {            return Stream.of(InetAddress.getByAddress(new byte[] {127, 127, 10, 1}));        }        return Stream.of(InetAddress.getByAddress(new byte[] {127, 0, 0, 1}));    }    @Override    public String lookupByAddress(byte[] addr) {        System.out.println("++++++" + addr[0] + " " + addr[1] + " " + addr[2] + " " + addr[3]);        return  "fedora";    }}---```javaaddresses = InetAddress.getAllByName("fedora");// output: 127 127 10 1

這里我簡單實現了一個對域名 fedora 的解析,會直接返回 127.127.10.1。ivH28資訊網——每日最新資訊28at.com

如果使用 IP 地址進行查詢時:ivH28資訊網——每日最新資訊28at.com

InetAddress byAddress = InetAddress.getByAddress(new byte[]{127, 127, 10, 1});System.out.println("+++++" + byAddress.getHostName());// output: fedora

當然要要使得這個 SPI 生效的前提條件是我們需要新建一個文件:META-INF/services/java.net.spi.InetAddressResolverProvider里面的內容是我們自定義類的全限定名稱:ivH28資訊網——每日最新資訊28at.com

com.example.demo.MyAddressResolverProvider

這樣一個完整的 SPI 就實現完成了。ivH28資訊網——每日最新資訊28at.com

正常情況下我們將應用打包為一個 jar 之后運行:ivH28資訊網——每日最新資訊28at.com

java -jar target/demo-0.0.1-SNAPSHOT.jar

是可以看到輸出結果是符合預期的。ivH28資訊網——每日最新資訊28at.com

一旦我們使用配合上 spring boot 打包之后,也就是加上以下的依賴:ivH28資訊網——每日最新資訊28at.com

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>3.2.3</version>    <relativePath/> <!-- lookup parent from repository -->  </parent><build>    <plugins>     <plugin>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-maven-plugin</artifactId>     </plugin>    </plugins>  </build>

再次執行其實也沒啥問題,也能按照預期輸出結果。ivH28資訊網——每日最新資訊28at.com

但我們加上 OpenTelemetry 的 agent 時:ivH28資訊網——每日最新資訊28at.com

java  -javaagent:opentelemetry-javaagent.jar /      -jar target/demo-0.0.1-SNAPSHOT.jar

就會發現在執行解析的時候拋出了 java.net.UnknownHostException異常。ivH28資訊網——每日最新資訊28at.com

圖片圖片ivH28資訊網——每日最新資訊28at.com

從結果來看就是沒有進入我們自定義的解析器。ivH28資訊網——每日最新資訊28at.com

SPI 原理

在講排查過程之前還是要先預習下關于 Java SPI 的原理以及應用場景。ivH28資訊網——每日最新資訊28at.com

以前寫過一個 http 框架 cicada,其中有一個可拔插 IOC 容器的功能:ivH28資訊網——每日最新資訊28at.com

就是可以自定義實現自己的 IOC 容器,將自己實現的 IOC 容器打包為一個第三方包加入到依賴中,cicada 框架就會自動使用自定義的 IOC 實現。ivH28資訊網——每日最新資訊28at.com

要實現這個功能本質上就是要定義一個接口,然后根據依賴的不同實現創建接口的實例對象。ivH28資訊網——每日最新資訊28at.com

public interface CicadaBeanFactory {    /**     * Register into bean Factory     * @param object     */    void register(Object object);    /**     * Get bean from bean Factory     * @param name     * @return     * @throws Exception     */    Object getBean(String name) throws Exception;    /**     * get bean by class type     * @param clazz     * @param <T>     * @return bean     * @throws Exception     */    <T> T getBean(Class<T> clazz) throws Exception;    /**     * release all beans     */    void releaseBean() ;}

獲取具體的示例代碼時就只需要使用 JDK 內置的 ServiceLoader 進行加載即可:ivH28資訊網——每日最新資訊28at.com

public static CicadaBeanFactory getCicadaBeanFactory() {      ServiceLoader<CicadaBeanFactory> cicadaBeanFactories = ServiceLoader.load(CicadaBeanFactory.class);      if (cicadaBeanFactories.iterator().hasNext()){          return cicadaBeanFactories.iterator().next() ;      }      return new CicadaDefaultBean();  }

代碼也非常的簡潔,和剛才提到的 InetAddressResolverProvider 一樣我們需要新增一個 META-INF/services/top.crossoverjie.cicada.base.bean.CicadaBeanFactory 文件來配置我們的類名稱。ivH28資訊網——每日最新資訊28at.com

private boolean hasNextService() {    if (nextName != null) {        return true;    }    if (configs == null) {        try {         // PREFIX = META-INF/services/            String fullName = PREFIX + service.getName();            if (loader == null)                configs = ClassLoader.getSystemResources(fullName);            else                configs = loader.getResources(fullName);        } catch (IOException x) {            fail(service, "Error locating configuration files", x);        }    }    while ((pending == null) || !pending.hasNext()) {        if (!configs.hasMoreElements()) {            return false;        }        pending = parse(service, configs.nextElement());    }    nextName = pending.next();    return true;}

在 ServiceLoader 類中會會去查找 META-INF/services 的文件,然后解析其中的內容從而反射生成對應的接口對象。ivH28資訊網——每日最新資訊28at.com

這里還有一個關鍵是通常我們的代碼都會打包為一個 JAR 包,類加載器需要加載這個  JAR 包,同時需要在這個 JAR 包里找到我們之前定義的那個 spi 文件,如果這里查不到文件那就認為沒有定義 SPI。ivH28資訊網——每日最新資訊28at.com

這個是本次問題的重點,會在后文分析原因的時候用到。ivH28資訊網——每日最新資訊28at.com

排查

因為問題就出現在是否使用 opentelemetry-javaagent.jar 上,所以我需要知道在使用了 agent 之后有什么區別。ivH28資訊網——每日最新資訊28at.com

從剛才的對 SPI 的原理分析,加上 agent 出現異常,說明理論上就是沒有讀取到我們配置的文件: java.net.spi.InetAddressResolverProvider。ivH28資訊網——每日最新資訊28at.com

于是我便開始 debug,在 ServiceLoader 加載 jar 包的時候是可以看到具體使用的是什么 classLoader 。ivH28資訊網——每日最新資訊28at.com

這是不配置 agent 的時候使用的 classLoader:使用這個 loader 是可以通過文件路徑在 jar 包中查找到我們配置的文件。ivH28資訊網——每日最新資訊28at.com

而配置上 agent 之后使用的 classLoader:卻是一個 JarLoader,這樣是無法加載到在 springboot 格式下的配置文件的,至于為什么加載不到,那就要提一下 maven 打包后的文件目錄和 spring boot 打包后的文件目錄的區別了。ivH28資訊網——每日最新資訊28at.com

這里我截圖了同樣的一份代碼不同的打包方式:上面的是傳統 maven,下圖是 spring boot;其實主要的區別就是在 pom 中使用了一個構建插件:ivH28資訊網——每日最新資訊28at.com

<build>    <plugins>     <plugin>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-maven-plugin</artifactId>     </plugin>    </plugins>  </build>

或者使用 spring-boot 命令再次打包的效果也是一樣的。ivH28資訊網——每日最新資訊28at.com

會發現 spring boot 打包后會多出一層 BOOT-INF 的文件夾,然后會在 MANIFIST.MF 文件中定義 Main-Class 和 Start-Class.ivH28資訊網——每日最新資訊28at.com

通過上面的 debug 其實會發現 JarLoader 只能在加載 maven 打包后的文件,也就是說無法識別 BOOT-INF 這個目錄。ivH28資訊網——每日最新資訊28at.com

正常情況下 spring boot 中會有一個額外的 java.nio.file.spi.FileSystemProvider 實現:通過這個類的實現可以直接從 JAR 包中加載資源,比如我們自定義的 SPI 資源等。ivH28資訊網——每日最新資訊28at.com

初步判斷使用 opentelemetry-javaagent.jar的 agent 之后,它的類加載器優先于了 spring boot ,從而導致后續的加載失敗。ivH28資訊網——每日最新資訊28at.com

遠程 debug

這里穿插幾個 debug 小技巧,其中一個是遠程 debug,因為這里我是需要調試 javaagent,正常情況下是無法直接 debug 的。ivH28資訊網——每日最新資訊28at.com

所以我們可以使用以下命令啟動應用:ivH28資訊網——每日最新資訊28at.com

java -agentlib:jdwp="transport=dt_socket,server=y,suspend=y,address=5000" -javaagent:opentelemetry-javaagent.jar /      -jar target/demo-0.0.1-SNAPSHOT.jar

然后在 idea 中配置一個 remote 啟動。ivH28資訊網——每日最新資訊28at.com

注意這里的端口得和命令行中的保持一致。ivH28資訊網——每日最新資訊28at.com

當應用啟動之后便可以在 idea 中啟動這個 remote 了,這樣便可以正常 debug 了。ivH28資訊網——每日最新資訊28at.com

條件斷點

第二個是條件斷點也非常有用,有時候我們需要調試一個公共函數,調用的地方非常多。ivH28資訊網——每日最新資訊28at.com

而我們只需要關心某一類行為的調用,此時就可以對這個函數中的變量進行判斷,當他們滿足某些條件時再進入斷點,這樣可以極大的提高我們的調試效率:ivH28資訊網——每日最新資訊28at.com

配置也很簡單,只需要在斷點上右鍵就可以編輯條件了。ivH28資訊網——每日最新資訊28at.com

社區咨詢

雖然我根據現象初步可以猜測下原因,但依然不確定如何調整才能解決這個問題,于是便去社區提了一個 issue。ivH28資訊網——每日最新資訊28at.com

最后在社區大佬的幫助下發現我們需要禁用掉 OpenTelemetry agent 中的一個 resource 就可以了。ivH28資訊網——每日最新資訊28at.com

這個 resource 是由 agent 觸發的,它優先于 spring boot 之前進行 SPI 的加載。目的是為了給 metric 和 trace 新增兩個屬性:ivH28資訊網——每日最新資訊28at.com

加載的核心代碼在這里,只要禁用掉之后就不會再加載了。ivH28資訊網——每日最新資訊28at.com

禁用前:ivH28資訊網——每日最新資訊28at.com

禁用后:ivH28資訊網——每日最新資訊28at.com

當我們禁用掉之后就不會存在這兩個屬性了,不過我們目前并沒有使用這兩個屬性,所以為了使得 SPI 生效就只有先禁用掉了,后續再看看社區還有沒有其他的方案。ivH28資訊網——每日最新資訊28at.com

想要復現 debug 的可以在這里嘗試:https://github.com/crossoverJie/demoivH28資訊網——每日最新資訊28at.com

參考連接:ivH28資訊網——每日最新資訊28at.com

  • https://github.com/TogetherOS/cicada
  • https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/#packaging.repackage-goal
  • https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/10921
  • https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/resources/library/README.md#host

本文鏈接:http://m.www897cc.com/showinfo-26-88352-0.htmlOpenTelemetry agent 對 Spring Boot 應用的影響:一次 SPI 失效的調查

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 程序員為什么一定要去造幾個輪子

下一篇: C#事件:實現安全的發布/訂閱模型

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
欧美激情1区2区3区| 亚洲一区自拍| 国产日韩欧美电影在线观看| 国产精品午夜在线观看| 国产一级久久| 亚洲国产精品成人一区二区 | 国产一区免费视频| 国产一区香蕉久久| 亚洲精品色婷婷福利天堂| 亚洲一区二区免费在线| 久久久国产91| 欧美精品一区视频| 国产人成精品一区二区三| 亚洲国产cao| 亚洲午夜精品一区二区| 久久九九热免费视频| 欧美国产亚洲精品久久久8v| 国产精品观看| 精品96久久久久久中文字幕无| 亚洲欧洲在线观看| 欧美影院在线播放| 欧美精品在线极品| 国产真实精品久久二三区| 亚洲乱码久久| 久久久精品五月天| 欧美午夜在线观看| 影音先锋亚洲电影| 亚洲自拍偷拍一区| 欧美高清影院| 狠狠色噜噜狠狠色综合久| 在线综合欧美| 欧美成人国产一区二区| 国产欧美婷婷中文| 一本色道久久综合精品竹菊| 久久久久一区二区三区四区| 国产精品爱久久久久久久| 在线播放亚洲| 欧美专区福利在线| 欧美视频中文字幕| 亚洲国产精品尤物yw在线观看| 亚洲伊人久久综合| 欧美激情一区二区三级高清视频| 国产精品视频九色porn| 亚洲国产小视频| 久久精品日韩欧美| 国产精品日韩欧美一区| 亚洲剧情一区二区| 久久视频这里只有精品| 国产毛片一区二区| 夜夜嗨av色一区二区不卡| 乱中年女人伦av一区二区| 国产女精品视频网站免费 | 国产精品视频一二三| 亚洲人午夜精品免费| 久久裸体视频| 国产欧美日韩另类一区| 亚洲视频第一页| 欧美激情视频一区二区三区免费| 依依成人综合视频| 欧美在线关看| 国产精品一区二区三区四区五区| 99综合精品| 欧美精品一区二区精品网| 亚洲大胆人体视频| 久久免费的精品国产v∧| 国产日韩av高清| 午夜国产欧美理论在线播放| 欧美视频在线免费| 在线一区免费观看| 欧美涩涩视频| 一区二区三区精品视频| 欧美精品免费在线| 亚洲欧洲日韩综合二区| 老司机aⅴ在线精品导航| 狠狠干综合网| 久久久噜噜噜久噜久久| 含羞草久久爱69一区| 欧美专区在线| 国产在线成人| 久久久美女艺术照精彩视频福利播放 | 国产三级欧美三级| 翔田千里一区二区| 国产日韩欧美三区| 欧美一级久久久久久久大片| 国产精品久久久久一区二区三区共 | 国产精品视频免费一区| 亚洲一区视频在线观看视频| 欧美视频在线免费看| 中文亚洲欧美| 国产精品成人在线| 亚洲影院在线| 国产热re99久久6国产精品| 性欧美大战久久久久久久久| 国产欧美日本一区二区三区| 欧美在线免费视频| 国产亚洲欧美aaaa| 久久久久国产一区二区| 精品成人乱色一区二区| 免费在线视频一区| 亚洲精品乱码久久久久久黑人 | 激情一区二区| 美女精品一区| 亚洲久久一区| 欧美日韩一区二区在线视频| 在线亚洲美日韩| 国产精品揄拍500视频| 欧美一区深夜视频| 在线观看欧美黄色| 你懂的成人av| 99综合在线| 国产精品久久久久一区二区| 久久er精品视频| 亚洲承认在线| 欧美精品一区二区在线观看| 亚洲午夜精品国产| 国产欧美综合一区二区三区| 久久久精品久久久久| 最新日韩中文字幕| 欧美性事在线| 久久岛国电影| 亚洲国产精品久久久久| 欧美日韩国产综合一区二区| 亚洲一级网站| 狠狠入ady亚洲精品经典电影| 男女激情视频一区| 亚洲图片在区色| 国产亚洲va综合人人澡精品| 噜噜爱69成人精品| 亚洲午夜精品| 在线观看亚洲视频| 欧美午夜激情小视频| 久久久99久久精品女同性| 91久久久久久久久| 国产精品日本一区二区| 久久在线播放| 亚洲天堂视频在线观看| 狠狠入ady亚洲精品| 欧美日韩国产电影| 久久精品一区二区三区中文字幕| 91久久久久久国产精品| 国产精品一区一区三区| 欧美成人亚洲成人| 亚洲欧美大片| 亚洲国产日韩美| 国产精品羞羞答答| 欧美国产日韩亚洲一区| 欧美在线电影| 中文一区二区| 亚洲大片在线| 国产精品羞羞答答xxdd| 欧美国产一区在线| 久久精品国产91精品亚洲| 99视频+国产日韩欧美| 狠狠干综合网| 国产精品免费小视频| 欧美成人69av| 久久精品一区中文字幕| 亚洲香蕉伊综合在人在线视看| 亚洲第一级黄色片| 国产日产欧美精品| 欧美日韩综合在线免费观看| 蜜臀久久99精品久久久画质超高清 | 欧美一级淫片aaaaaaa视频| 亚洲精品在线二区| 一区二区三区在线视频免费观看| 国产精品久久久久久久7电影| 免费日韩成人| 久久精品视频免费播放| 亚洲欧美日韩精品久久久| 亚洲毛片av在线| 原创国产精品91| 国产亚洲人成网站在线观看| 国产精品福利在线观看| 欧美精品一区二区精品网| 美女精品网站| 久久午夜国产精品| 久久不见久久见免费视频1| 亚洲欧美激情视频在线观看一区二区三区| 亚洲青色在线| 亚洲国产成人av| 一区在线视频| 国产在线高清精品| 国产欧美综合在线| 国产精品美女在线| 国产精品高潮呻吟久久| 欧美日韩一区不卡| 欧美美女操人视频| 欧美经典一区二区| 欧美电影在线免费观看网站| 乱码第一页成人| 久久婷婷国产综合精品青草| 欧美一区激情视频在线观看| 亚洲综合色噜噜狠狠| 亚洲一区二区精品| 亚洲午夜日本在线观看| 一区二区三区国产| 一区二区三区三区在线| 一本久久知道综合久久| 一本大道av伊人久久综合| 一本色道久久综合亚洲精品婷婷| 亚洲伦理精品| 一区二区激情视频| 亚洲婷婷在线| 午夜精品久久久久|