JavaAgent如何实现http接口发布

这篇文章主要介绍“JavaAgent如何实现http接口发布”,在日常操作中,相信很多人在JavaAgent如何实现http接口发布问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JavaAgent如何实现http接口发布”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    需求

    公司运维系统想要监控服务是否正常启动,这些服务是k8s部署的,运维人员的要求业务服务提供一个http接口用于监控服务健康监测,要求所有的接口请求的URL,参数等都是相同的,这么做的目的是不需要通过规范来约束开发人员去开一个服务健康监测的接口。

    使用服务接口来检测服务我觉得相比较监控进程启动,端口监听等方式更准确一些。所以,为了满足运维同学的要求,起初想到的方案是提供一个jar,专门集成到项目中用于发布监控接口,但是想了一下,这么做需要涉及到服务的改造,我理想的方式是对应用无侵入的方式实现。

    初步方案

    说到对应用无入侵,首先想到的就是javaagent技术,此前使用该技术实现了无入侵增强程序日志的工具,所以对使用javaagent已经没有问题,此时需要考虑的是如何发布接口了。

    基础技术 JavaAgent

    支持的技术 SpringBoot和DubboX发布的rest服务

    公司服务大致分为两类,一个是使用springboot发布的Spring MVC rest接口,另一种是基于DubboX发布的rest接口,因为公司在向服务网格转,所以按要求是去dubbo化的,没办法还是有其他小组由于一些其他原因没有或者说短期内不想进行服务改造的项目,这些项目比较老,不是springboot的,是使用spring+DubboX发布的rest服务。所以这个agent要至少能支持这两种技术。

    支持SpringBoot

    想要支持SpringBoot很简单,因为SpringBoot支持自动装配,所以,我要写一个spring.factories来进行自动装配。

    支持DubboX

    业务系统是传统spring+DubboX实现的,并不支持自动装配,这是个问题点,还有个问题点就是如何也发布一个DubboX的rest接口,这两个问题实际上就需要对SpringBean生命周期和Dubbo接口发布的流程有一定的了解了,这个一会儿再说。

    技术实现

    pom文件依赖

        <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>

    实现一个JavaAgent

    实现一个JavaAgent很容易,以下三步就可以了,这里不细说了。

    定义JavaAgent入口

    public class PreAgent {
        public static void premain(String args, Instrumentation inst) {
            System.out.println("输入参数:" + args);
            // 通过参数控制,发布的接口是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编写

    注:该文件在resource/META-INF/目录下

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

    支持SpringBoot发布的Http接口

    编写Controller

    接口很简单就发布一个get接口,响应pong即可。

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

    创建spring.factories

    通过这个配置文件可以实现SpringBoot自动装配,这里不细说SpringBoot自动装配的原理了,该文件的配置内容就是要自动装配的Bean的全路径,代码如下:

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

    WebConfiguration配置类

    这个配置配置类很简单,@Configuration声明这是个配置类,@ComponentScan扫描包。

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

    支持DubboX发布的rest接口

    定义API

    使用的是DubboX发布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();
    }

    编写API实现类

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

    实现发布Dubbo接口

    如何实现发布接口是实现的难点;首先程序并不支持自动装配了,我们就要考虑如何获取到Spring上下文,如果能够注册BeanSpring容器中,如何触发发布Dubbo接口等问题。

    Spring上下文获取及注册Bean到Spring容器中

    触发Bean注册,获取Spring上下文我们通过Spring的Aware接口可以实现,我这里使用的是ApplicationContextAware;注册BeanSpring容器中可以使用BeanDefinition先创建Bean然后使用DefaultListableBeanFactoryregisterBeanDefinitionBeanDefinition注册到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,不用发布接口
            if (DUBBOX.equals(Args.EXPORT_DUBBOX)) {
                // 注册配置Bean WebConfiguration
                webConfiguration();
                // 发布DubboX接口
                exportDubboxService();
            }
        }
        public void webConfiguration() {
            System.out.println("创建WebConfiguration的bean");
            ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
            DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getAutowireCapableBeanFactory();
            // 创建WebConfiguration的bean
            BeanDefinition webConfigurationBeanDefinition = new RootBeanDefinition(WebConfiguration.class);
            // 注册到集合beanFactory中
            System.out.println("注册到集合beanFactory中");
            listableBeanFactory.registerBeanDefinition(WebConfiguration.class.getName(), webConfigurationBeanDefinition);
        }
    }

    发布Dubbo接口

    通过ApplicationContextAware我们已经能够获取Spring上下文了,也就是说应用程序的Dubbo注册中心,发布接口协议,Dubbo Application等配置都已经存在Spring容器中了,我们只要拿过来使用即可,拿过来使用没问题,我们接下来就需要考虑,如何发布接口,这需要对Dubbo服务发布的流程有一定的了解,这里我不细说了,感兴趣的可以自己了解下,或者看我以前发布的文章;

    首先Dubbo接口的Provider端的核心Bean是com.alibaba.dubbo.config.spring.ServiceBean,使用Spring配置文件中的标签<dubbo:service标签生成的Bean就是ServiceBean,所以,这里我们只需要创建ServiceBean对象并且初始化对象中的必要数据,然后调用ServiceBean#export()方法就可以发布Dubbo服务了。

    这里需要的对象直接通过依赖查找的方式从Spring容器获取就可以了 ApplicationConfig,ProtocolConfig,RegistryConfig,IPingService

        public void exportDubboxService() {
            try {
                System.out.println("开始发布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);
                // 创建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");
                // 发布dubbo接口
                serviceBean.export();
                System.out.println("dubbo接口发布完毕");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    使用方式

    • DubboX: java -javaagent:ruubypay-ping-agent.jar=1 -jar 服务jar包

    • springboot的http接口:java -javaagent:ruubypay-ping-agent.jar -jar 服务jar包

    到此,关于“JavaAgent如何实现http接口发布”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程之家网站,小编会继续努力为大家带来更多实用的文章!

    版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

    相关推荐


    本文从从Bitcask存储模型讲起,谈轻量级KV系统设计与实现。从来没有最好的K-V系统,只有最适合应用业务实际场景的系统,做任何的方案选择,要结合业务当前的实际情况综合权衡,有所取有所舍。
    内部的放到gitlab pages的博客,需要统计PV,不蒜子不能准确统计,原因在于gitlab的host设置了strict-origin-when-cross-origin, 导致不蒜子不能正确获取referer,从而PV只能统计到网站的PV。 为了方便统计页面的PV,这里简单的写了一个java程
    PCM 自然界中的声音非常复杂,波形极其复杂,通常我们采用的是脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。 采样率 采样频率,也称为采样速度或者采样率,定义了每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。采样频率的倒数
    本文介绍如何离线生成sst并在线加载,提供一种用rocksdb建立分布式kv系统替换mongodb的思路
    验证用户输入是否正确是我们应用程序中的常见功能。Spring提供了`@Valid`和@`Validated`两个注解来实现验证功能,本文详细介绍 [@Valid]和[@Validated]注解的区别 。
    引入pdf2dom &lt;dependency&gt; &lt;groupId&gt;net.sf.cssbox&lt;/groupId&gt; &lt;artifactId&gt;pdf2dom&lt;/artifactId&gt; &lt;version&gt;1.8&lt;/version&
    grafana 是一款非常优秀的可视化报表工具,有设计精良的可视化工具,今天来聊一聊如何将grafana集成到自己的应用中。 原理是: grafana允许iframe访问,开启auth.proxy, java 后端鉴权后代理grafana 前端通过iframe访问后端代理过的grafana graf
    介绍 Call Graph是一款IDEA插件,用于可视化基于IntelliJ平台的IDE的函数调用图。 这个插件的目标是让代码更容易理解,有助于读懂和调试代码。当前只支持Java。针对Typescript、Javascript或Python工具,可以使用作者的另外一款工具Codemap(https:
    原理 通过线程安全findAndModify 实现锁 实现 定义锁存储对象: /** * mongodb 分布式锁 */ @Data @NoArgsConstructor @AllArgsConstructor @Document(collection = &quot;distributed-loc
    Singleton 单例模式 单例模式是确保每个应用程序只存在一个实例的机制。默认情况下,Spring将所有bean创建为单例。 你用@Autowired获取的bean,全局唯一。 @RestController public class LibraryController { @Autowired
    pipeline 分布式任务调度器 目标: 基于docker的布式任务调度器, 比quartzs,xxl-job 更强大的分布式任务调度器。 可以将要执行的任务打包为docker镜像,或者选择已有镜像,自定义脚本程序,通过pipeline框架来实现调度。 开源地址: https://github.c
    python训练的模型,转换为onnx模型后,用python代码可以方便进行推理,但是java代码如何实现呢? 首先ONNX 推理,可以使用`onnxruntime` ```xml com.microsoft.onnxruntime onnxruntime 1.15.1 ``` 另外,训练的模型需要
    要获取内网地址,可以尝试连接到10.255.255.255:1。如果连接成功,获取本地套接字的地址信息就是当前的内网IP。 python实现: ```python import socket def extract_ip(): st = socket.socket(socket.AF_INET, s
    为什么要有索引 gremlin 其实是一个逐级过滤的运行机制,比如下面的一个简单的gremlin查询语句: g.V().hasLabel(&quot;label&quot;).has(&quot;prop&quot;,&quot;value&quot;) 运行原理就是: 找出所有的顶点V 然后过滤出
    最近在分析一个应用中的某个接口的耗时情况时,发现一个看起来极其普通的对象创建操作,竟然每次需要消耗 8ms 左右时间,分析后发现这个对象可以通过对象池模式进行优化,优化后此步耗时仅有 0.01ms。
    点赞再看,动力无限。Hello world : ) 微信搜「 程序猿阿朗 」。 本文 Github.com/niumoo/JavaNotes 和 未读代码网站 已经收录,有很多知识点和系列文章。 此篇文章介绍 Java JMX 技术的相关概念和具体的使用方式。 当前文章属于Java 性能分析优化系列
    如何将Java JAR 转化为 win/mac/linux 独立可执行程序?不需要预装 JRE 运行?
    点赞再看,动力无限。 微信搜「 程序猿阿朗 」。 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章。 Java 19 在2022 年 9 月 20 日正式发布,Java 19 不是一个长期支持版本,直到 2023 年 3 月它将被 JD
    点赞再看,动力无限。Hello world : ) 微信搜「 程序猿阿朗 」。 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章。 前言 Java 反编译,一听可能觉得高深莫测,其实反编译并不是什么特别高级的操作,Java 对于 Cla
    JSON 对于开发者并不陌生,如今的 WEB 服务、移动应用、甚至物联网大多都是以 **JSON** 作为数据交换的格式。学习 JSON 格式的操作工具对开发者来说是必不可少的。这篇文章将介绍如何使用 **Jackson** 开源工具库对 JSON 进行常见操作。