溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

JavaAgent如何實(shí)現(xiàn)http接口發(fā)布

發(fā)布時(shí)間:2023-03-02 14:13:20 來(lái)源:億速云 閱讀:106 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“JavaAgent如何實(shí)現(xiàn)http接口發(fā)布”,在日常操作中,相信很多人在JavaAgent如何實(shí)現(xiàn)http接口發(fā)布問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”JavaAgent如何實(shí)現(xiàn)http接口發(fā)布”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

    需求

    公司運(yùn)維系統(tǒng)想要監(jiān)控服務(wù)是否正常啟動(dòng),這些服務(wù)是k8s部署的,運(yùn)維人員的要求業(yè)務(wù)服務(wù)提供一個(gè)http接口用于監(jiān)控服務(wù)健康監(jiān)測(cè),要求所有的接口請(qǐng)求的URL,參數(shù)等都是相同的,這么做的目的是不需要通過(guò)規(guī)范來(lái)約束開(kāi)發(fā)人員去開(kāi)一個(gè)服務(wù)健康監(jiān)測(cè)的接口。

    使用服務(wù)接口來(lái)檢測(cè)服務(wù)我覺(jué)得相比較監(jiān)控進(jìn)程啟動(dòng),端口監(jiān)聽(tīng)等方式更準(zhǔn)確一些。所以,為了滿(mǎn)足運(yùn)維同學(xué)的要求,起初想到的方案是提供一個(gè)jar,專(zhuān)門(mén)集成到項(xiàng)目中用于發(fā)布監(jiān)控接口,但是想了一下,這么做需要涉及到服務(wù)的改造,我理想的方式是對(duì)應(yīng)用無(wú)侵入的方式實(shí)現(xiàn)。

    初步方案

    說(shuō)到對(duì)應(yīng)用無(wú)入侵,首先想到的就是javaagent技術(shù),此前使用該技術(shù)實(shí)現(xiàn)了無(wú)入侵增強(qiáng)程序日志的工具,所以對(duì)使用javaagent已經(jīng)沒(méi)有問(wèn)題,此時(shí)需要考慮的是如何發(fā)布接口了。

    基礎(chǔ)技術(shù) JavaAgent

    支持的技術(shù) SpringBoot和DubboX發(fā)布的rest服務(wù)

    公司服務(wù)大致分為兩類(lèi),一個(gè)是使用springboot發(fā)布的Spring MVC rest接口,另一種是基于DubboX發(fā)布的rest接口,因?yàn)楣驹谙蚍?wù)網(wǎng)格轉(zhuǎn),所以按要求是去dubbo化的,沒(méi)辦法還是有其他小組由于一些其他原因沒(méi)有或者說(shuō)短期內(nèi)不想進(jìn)行服務(wù)改造的項(xiàng)目,這些項(xiàng)目比較老,不是springboot的,是使用spring+DubboX發(fā)布的rest服務(wù)。所以這個(gè)agent要至少能支持這兩種技術(shù)。

    支持SpringBoot

    想要支持SpringBoot很簡(jiǎn)單,因?yàn)镾pringBoot支持自動(dòng)裝配,所以,我要寫(xiě)一個(gè)spring.factories來(lái)進(jìn)行自動(dòng)裝配。

    支持DubboX

    業(yè)務(wù)系統(tǒng)是傳統(tǒng)spring+DubboX實(shí)現(xiàn)的,并不支持自動(dòng)裝配,這是個(gè)問(wèn)題點(diǎn),還有個(gè)問(wèn)題點(diǎn)就是如何也發(fā)布一個(gè)DubboX的rest接口,這兩個(gè)問(wèn)題實(shí)際上就需要對(duì)SpringBean生命周期和Dubbo接口發(fā)布的流程有一定的了解了,這個(gè)一會(huì)兒再說(shuō)。

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

    pom文件依賴(lài)

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.3.6.RELEASE</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
                <version>2.3.6.RELEASE</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <version>2.3.6.RELEASE</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.70</version>
            </dependency>
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.4.6</version>
                <exclusions>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>com.101tec</groupId>
                <artifactId>zkclient</artifactId>
                <version>0.7</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.8.4</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>javax.ws.rs</groupId>
                <artifactId>javax.ws.rs-api</artifactId>
                <version>2.0.1</version>
            </dependency>
        </dependencies>

    實(shí)現(xiàn)一個(gè)JavaAgent

    實(shí)現(xiàn)一個(gè)JavaAgent很容易,以下三步就可以了,這里不細(xì)說(shuō)了。

    定義JavaAgent入口

    public class PreAgent {
        public static void premain(String args, Instrumentation inst) {
            System.out.println("輸入?yún)?shù):" + args);
            // 通過(guò)參數(shù)控制,發(fā)布的接口是DubboX還是SpringMVC
            Args.EXPORT_DUBBOX = args;
        }
    }

    Maven打包配置

            <plugins>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>1.4</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
                                <promoteTransitiveDependencies>false</promoteTransitiveDependencies>
                                <createDependencyReducedPom>true</createDependencyReducedPom>
                                <minimizeJar>false</minimizeJar>
                                <createSourcesJar>true</createSourcesJar>
                                <transformers>
                                    <transformer
                                            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                        <manifestEntries>
                                            <Premain-Class>com.ruubypay.agent.PreAgent</Premain-Class>
                                        </manifestEntries>
                                    </transformer>
                                </transformers>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>

    MANIFEST.MF編寫(xiě)

    注:該文件在resource/META-INF/目錄下

    Manifest-Version: 1.0
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    Premain-Class: com.ruubypay.agent.PreAgent

    支持SpringBoot發(fā)布的Http接口

    編寫(xiě)Controller

    接口很簡(jiǎn)單就發(fā)布一個(gè)get接口,響應(yīng)pong即可。

    @RestController
    public class PingServiceController {
        @GetMapping(value = "/agentServer/ping")
        public String ping() {
            return "pong";
        }
    }

    創(chuàng)建spring.factories

    通過(guò)這個(gè)配置文件可以實(shí)現(xiàn)SpringBoot自動(dòng)裝配,這里不細(xì)說(shuō)SpringBoot自動(dòng)裝配的原理了,該文件的配置內(nèi)容就是要自動(dòng)裝配的Bean的全路徑,代碼如下:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.ruubypay.config.WebConfiguration

    WebConfiguration配置類(lèi)

    這個(gè)配置配置類(lèi)很簡(jiǎn)單,@Configuration聲明這是個(gè)配置類(lèi),@ComponentScan掃描包。

    @Configuration
    @ComponentScan(value = "com.ruubypay")
    public class WebConfiguration {
    }

    支持DubboX發(fā)布的rest接口

    定義API

    使用的是DubboX發(fā)布rest接口需要javax.ws.rs包的注解,@Produces({ContentType.APPLICATION_JSON_UTF_8})聲明序列化方式,@Pathrest接口的路徑,@GET聲明為get接口。

    @Produces({ContentType.APPLICATION_JSON_UTF_8})
    @Path("/agentServer")
    public interface IPingService {
        /**
         * ping接口
         * @return
         */
        @GET
        @Path("/ping")
        String ping();
    }

    編寫(xiě)API實(shí)現(xiàn)類(lèi)

    @Component("IPingService")
    public class IPingServiceImpl implements IPingService {
        @Override
        public String ping() {
            return "pong";
        }
    }

    實(shí)現(xiàn)發(fā)布Dubbo接口

    如何實(shí)現(xiàn)發(fā)布接口是實(shí)現(xiàn)的難點(diǎn);首先程序并不支持自動(dòng)裝配了,我們就要考慮如何獲取到Spring上下文,如果能夠注冊(cè)BeanSpring容器中,如何觸發(fā)發(fā)布Dubbo接口等問(wèn)題。

    Spring上下文獲取及注冊(cè)Bean到Spring容器中

    觸發(fā)Bean注冊(cè),獲取Spring上下文我們通過(guò)Spring的Aware接口可以實(shí)現(xiàn),我這里使用的是ApplicationContextAware;注冊(cè)BeanSpring容器中可以使用BeanDefinition先創(chuàng)建Bean然后使用DefaultListableBeanFactoryregisterBeanDefinitionBeanDefinition注冊(cè)到Spring上下文中。

    @Component
    public class AgentAware implements ApplicationContextAware {
        private static final String DUBBOX = "1";
        private ApplicationContext applicationContext;
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
            // 如果不是DubboX,不用發(fā)布接口
            if (DUBBOX.equals(Args.EXPORT_DUBBOX)) {
                // 注冊(cè)配置Bean WebConfiguration
                webConfiguration();
                // 發(fā)布DubboX接口
                exportDubboxService();
            }
        }
        public void webConfiguration() {
            System.out.println("創(chuàng)建WebConfiguration的bean");
            ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
            DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getAutowireCapableBeanFactory();
            // 創(chuàng)建WebConfiguration的bean
            BeanDefinition webConfigurationBeanDefinition = new RootBeanDefinition(WebConfiguration.class);
            // 注冊(cè)到集合beanFactory中
            System.out.println("注冊(cè)到集合beanFactory中");
            listableBeanFactory.registerBeanDefinition(WebConfiguration.class.getName(), webConfigurationBeanDefinition);
        }
    }

    發(fā)布Dubbo接口

    通過(guò)ApplicationContextAware我們已經(jīng)能夠獲取Spring上下文了,也就是說(shuō)應(yīng)用程序的Dubbo注冊(cè)中心,發(fā)布接口協(xié)議,Dubbo Application等配置都已經(jīng)存在Spring容器中了,我們只要拿過(guò)來(lái)使用即可,拿過(guò)來(lái)使用沒(méi)問(wèn)題,我們接下來(lái)就需要考慮,如何發(fā)布接口,這需要對(duì)Dubbo服務(wù)發(fā)布的流程有一定的了解,這里我不細(xì)說(shuō)了,感興趣的可以自己了解下,或者看我以前發(fā)布的文章;

    首先Dubbo接口的Provider端的核心Bean是com.alibaba.dubbo.config.spring.ServiceBean,使用Spring配置文件中的標(biāo)簽<dubbo:service標(biāo)簽生成的Bean就是ServiceBean,所以,這里我們只需要?jiǎng)?chuàng)建ServiceBean對(duì)象并且初始化對(duì)象中的必要數(shù)據(jù),然后調(diào)用ServiceBean#export()方法就可以發(fā)布Dubbo服務(wù)了。

    這里需要的對(duì)象直接通過(guò)依賴(lài)查找的方式從Spring容器獲取就可以了 ApplicationConfig,ProtocolConfig,RegistryConfig,IPingService

        public void exportDubboxService() {
            try {
                System.out.println("開(kāi)始發(fā)布dubbo接口");
                // 獲取ApplicationConfig
                ApplicationConfig applicationConfig = applicationContext.getBean(ApplicationConfig.class);
                // 獲取ProtocolConfig
                ProtocolConfig protocolConfig = applicationContext.getBean(ProtocolConfig.class);
                // 獲取RegistryConfig
                RegistryConfig registryConfig = applicationContext.getBean(RegistryConfig.class);
                // 獲取IPingService接口
                IPingService iPingService = applicationContext.getBean(IPingService.class);
                // 創(chuàng)建ServiceBean
                ServiceBean<IPingService> serviceBean = new ServiceBean<>();
                serviceBean.setApplicationContext(applicationContext);
                serviceBean.setInterface("com.ruubypay.api.IPingService");
                serviceBean.setApplication(applicationConfig);
                serviceBean.setProtocol(protocolConfig);
                serviceBean.setRegistry(registryConfig);
                serviceBean.setRef(iPingService);
                serviceBean.setTimeout(12000);
                serviceBean.setVersion("1.0.0");
                serviceBean.setOwner("rubby");
                // 發(fā)布dubbo接口
                serviceBean.export();
                System.out.println("dubbo接口發(fā)布完畢");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    使用方式

    • DubboX: java -javaagent:ruubypay-ping-agent.jar=1 -jar 服務(wù)jar包

    • springboot的http接口:java -javaagent:ruubypay-ping-agent.jar -jar 服務(wù)jar包

    到此,關(guān)于“JavaAgent如何實(shí)現(xiàn)http接口發(fā)布”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI