JavaAgent实现http接口发布方式浅析

目录
  • 需求
  • 初步方案
  • 技术实现
    • pom文件依赖
    • 实现一个JavaAgent
    • 支持SpringBoot发布的Http接口
    • 支持DubboX发布的rest接口
    • 使用方式
  • 总结

需求

公司运维系统想要监控服务是否正常启动,这些服务是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包

总结

这个工具实现起来不复杂,总也就六个类和一个接口,但其实实现其能力所涉及的支持还是比较考验对框架的理解的,比如Spring生命周期,DubboX发布接口的流程以及实现一个最简单的JavaAgent的方式。

到此这篇关于JavaAgent实现http接口发布方式浅析的文章就介绍到这了,更多相关JavaAgent http接口内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java Http接口加签、验签操作方法

    1.业务背景 最近接触了一些电商业务,发现在处理电商业务接口时,比如淘宝.支付类接口,接口双方为了确保数据参数在传输过程中未经过篡改,都需要对接口数据进行加签,然后在接口服务器端对接口参数进行验签,确保两个签名是一样的,验签通过之后再进行业务逻辑处理.我们这里主要介绍一下处理思路,至于签名算法我不做过多介绍,网上一大堆. 2.处理思路 双方约定好,参数按特定顺序排列,比如按首字母的顺序排列,如url:http://xxx/xxx.do?a=wersd&b=sd2354&c=4&si

  • Java实现调用对方http接口得到返回数据

    目录 Java 用对方http接口得到返回数据 java后台工具类调用api接口,解析数据 一 . 引入jar包 二. httpclient请求接口工具类 Java 用对方http接口得到返回数据 如图所示我们这里自己写一个接口作为访问地址,返回的是json字符串 首先我们在浏览器访问这个接口的地址,会在浏览器打印出如图所示的内容, 然后我们写一个方法访问刚刚的接口地址,使用HttpURLConnextion进行访问,通过BufferedReader获取流,已得到返回的值 运行这个方法,会在控制

  • Java如何实现http接口参数和返回值加密

    目录 参数和返回值得加密目的 具体实现方式 大致思路 代码实现 身份检验 参数和返回值得加密目的 为了保证接口不被人拦截下来恶意请求,保证程序的稳定性,我们可以使用接口加密的方法来保证参数和返回值的保密性. 具体实现方式 因为本人是写Java 的,所以这里就直接以Java代码为例.思想都是一样的,只是不同的语言都不同的实现方式. 大致思路 我们的参数和返回值只需要定义两个参数:ak,ct,ak存放堆成加密的秘钥,ct存放加密之后的请求内容. 加密方式用到AES对称加密和RSA非对称加密,将请求参

  • Java调用第三方http接口的常用方式总结

    目录 1.概述 在Java项目中调用第三方接口的常用方式有 2.Java调用第三方http接口的方式 2.1 通过JDK网络类Java.net.HttpURLConnection 2.2 通过apache common封装好的HttpClient 2.3 通过Apache封装好的CloseableHttpClient 2.4 通过OkHttp 2.5 通过Spring的RestTemplate 2.6通过hutool的HttpUtil 3.总结 1.概述 在实际开发过程中,我们经常需要调用对方提

  • JavaAgent实现http接口发布方式浅析

    目录 需求 初步方案 技术实现 pom文件依赖 实现一个JavaAgent 支持SpringBoot发布的Http接口 支持DubboX发布的rest接口 使用方式 总结 需求 公司运维系统想要监控服务是否正常启动,这些服务是k8s部署的,运维人员的要求业务服务提供一个http接口用于监控服务健康监测,要求所有的接口请求的URL,参数等都是相同的,这么做的目的是不需要通过规范来约束开发人员去开一个服务健康监测的接口. 使用服务接口来检测服务我觉得相比较监控进程启动,端口监听等方式更准确一些.所以

  • 浅谈java调用Restful API接口的方式

    摘要:最近有一个需求,为客户提供一些RestfulAPI接口,QA使用postman进行测试,但是postman的测试接口与java调用的相似但并不相同,于是想自己写一个程序去测试RestfulAPI接口,由于使用的是HTTPS,所以还要考虑到对于HTTPS的处理.由于我也是首次使用Java调用restful接口,所以还要研究一番,自然也是查阅了一些资料. 分析:这个问题与模块之间的调用不同,比如我有两个模块frontend和backend,frontend提供前台展示,backend提供数据支

  • Linux采用双网卡bond、起子接口的方式

    什么是bond 网卡bond是通过多张网卡绑定为一个逻辑网卡,实现本地网卡的冗余,带宽扩容和负载均衡,在生产场景中是一种常用的技术. 适用场景 服务器两张网卡需要做bond,并且bond后网卡需配置不同网段的地址,用于走不同流量,这个时候就可以采用起子接口的方式. 实验场景 设备 服务器:Server_A 核心交换机:Switch_A.Switch_B 交换机连接方式:堆叠 服务器网卡:enp176s0f0.enp176s0f1做bond IP段划分 业务段     VLAN 201:10.10

  • angularjs $http调用接口的方式详解

    如下所示: $http.get("/merchantmall/merchant.json") .success(function(data, status, headers, config) { console.log(arguments); }) .error(function(data, status, headers, config) { console.log(arguments); }) $http({url: "/merchantmall/merchant.jso

  • python的Jenkins接口调用方式

    本来非常喜欢偷懒 最好就是不干活那种 所以最近在研究把Jenkins模块集成起来 做成傻瓜界面这样就给他们用 本人Python搓望大神不要喷,多多指导 jenkins的Python模块模块安装 pip: pip install python-jenkins easy_install: easy_install python-jenkins 使用: class jenkins_tools(): def __init__(self): cf = get_conf() self.username =

  • Python接口自动化之浅析requests模块post请求

    在上一篇Python接口自动化测试系列文章:Python接口自动化之浅析requests模块get请求,介绍了requests模块.get请求及响应结果详解.接下来介绍requests模块中的post请求的使用. 一.源码解析 def post(url, data=None, json=None, **kwargs): r"""Sends a POST request. :param url: URL for the new :class:`Request` object.

  • Python接口自动化之浅析requests模块get请求

    一.requests模块说明 介绍 Requests是Python语言的第三方的库,专门用于发送HTTP请求. 特点 1.Requests支持HTTP连接保持和连接池,支持使用cookie保持会话,支持文件上传,支持自动响应内容的编码,支持国际化的URL和POST数据自动编码. 2.在python内置模块的基础上进行了高度的封装,从而使得python进行网络请求时,变得人性化,使用Requests可以轻而易举的完成浏览器可有的任何操作. 3.Requests会自动实现持久连接keep-alive

  • java的接口解耦方式

    目录 java的接口解耦方式 举例子 java接口解耦效果的理解 先看一段代码 java的接口解耦方式 我只想把抽象的东西说的具体,或者说,听起来简单些,明白些... 学过java的人都知道,java是单继承的,也就是说一个class只能继承一个类. 例如我们想制作一台有播放器的手机,那么我们先得制作一个播放器吧,再把播放器放进手机里.在java会怎么实现呢?如果使用继承,我们会先创建一个播放器类,播放器类里面含有播放歌曲功能(方法),创建一个手机类继承播放器类,重写播放器的播歌功能(不重写的话

  • python模块导入方式浅析步骤

    目录 1.模块的使用 2.导入模块并使用 3.模块的导入方式之from-import 语句 4.__all__变量用来控制* 5.总结 首先啊,在python中我们熟知的py文件就是一个模块,也就是换言之以py结尾的Python源代码文件都是一个模块我就简单概括一下了直接上代码 1.模块的使用 使用模块里的工具前需要导入模块, 模块的导入方式之import······· 单个:import 模块名多个:import 模块1import 模块2import 模块1,模块2(一般不推荐使用)(多个)

  • 微信小程序 wx.request(接口调用方式)详解及实例

    微信小程序 wx.request----接口调用方式 最近开发了一个微信小程序版的任务管理系统,在向Java后台发送接口时遇到了一些问题,在这里做一个简单的总结. 官方接口 官方给出的接口叫做wx.request,请求方式比较简单,下面是官网给出的请求实例. wx.request({ url: 'test.php', //仅为示例,并非真实的接口地址 data: { x: '' , y: '' }, header: { 'content-type': 'application/json' },

随机推荐