APM - 零侵入监控Http服务

本文涉及的产品
云拨测,每月3000次拨测额度
简介: APM - 零侵入监控Http服务

20201008114455965.png


Pre

APM - 零侵入监控Service服务


HTTP采集入口

  1. DispacherServlet .doServer() ?
  1. @Control ?
  2. javax.servlet.http.HttpServlet ?

很显然第三种更具有通用性,不管是DispacherServlet 还是@Control 都会是基于HttpServlet#service

20201008121038867.png


Code

package com.artisan.collects;
import com.artisan.ApmContext;
import com.artisan.intf.ICollect;
import com.artisan.model.HttpStatistics;
import javassist.*;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.Map;
/**
 * Http采集器
 */
public class HttpCollect extends AbstractByteTransformCollect implements ICollect {
    // 采集目标
    // 1.DispatchServlet
    // 2.@control 下的方法
    // 3.采集 javax.servlet.service()  ---- 采用这种通用的方式
    private static final String TARGET_CLASS = "javax.servlet.http.HttpServlet";
    private static final String TARGET_METHOD = "service";
    private static ApmContext context;
    public HttpCollect(ApmContext context, Instrumentation instrumentation) {
        super(instrumentation);
        this.context = context;
    }
    public byte[] buildClass(ClassLoader loader) throws Exception {
        ClassPool pool = new ClassPool();
        pool.insertClassPath(new LoaderClassPath(loader));
        CtClass ctClass = pool.get(TARGET_CLASS);
        CtMethod oldMethod = ctClass.getDeclaredMethod(TARGET_METHOD);
        CtMethod newMethod = CtNewMethod.copy(oldMethod, ctClass, null);
        oldMethod.setName(oldMethod.getName() + "$agent");
        //HttpServlet.service()'
        String beginSrc = "Object stat=com.artisan.collects.HttpCollect.begin($args);";
        String errorSrc = "com.artisan.collects.HttpCollect.error(e,stat);";
        String endSrc = "com.artisan.collects.HttpCollect.end(stat);";
        newMethod.setBody(String.format(voidSource, beginSrc, TARGET_METHOD, errorSrc, endSrc));
        ctClass.addMethod(newMethod);
        return ctClass.toBytecode();
    }
    // url,client IP
    public static HttpStatistics begin(Object args[]) {
        HttpStatistics httpStatistics = new HttpStatistics();
        httpStatistics.setBeginTime(System.currentTimeMillis());
        // 采用适配器的方式 ,避免在tomcat下  agent【appLauncher装载】无法获取到HttpServletRequest【common加载】(classLoader机制)
        HttpServletRequestAdapter adapter = new HttpServletRequestAdapter(args[0]);
        httpStatistics.setUrl(adapter.getRequestURI());
        httpStatistics.setClientIp(adapter.getClientIp());
        return httpStatistics;
    }
    public static void end(Object obj) {
        HttpStatistics stat = (HttpStatistics) obj;
        ((HttpStatistics) obj).setUseTime(System.currentTimeMillis() - stat.getBeginTime());
        context.submitCollectResult(stat);
    }
    public static void error(Throwable error, Object obj) {
        HttpStatistics stat = (HttpStatistics) obj;
        stat.setError(error.getMessage());
        System.out.println(stat);
    }
    final static String voidSource = "{\n"
            + "%s"
            + "        try {\n"
            + "            %s$agent($$);\n"
            + "        } catch (Throwable e) {\n"
            + "%s"
            + "            throw e;\n"
            + "        }finally{\n"
            + "%s"
            + "        }\n"
            + "}\n";
    @Override
    public byte[] transform(ClassLoader loader, String className) throws Exception {
        if (!TARGET_CLASS.equals(className)) {
            return null;
        }
        try {
            return buildClass(loader);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    private static class HttpServletRequestAdapter {
        private final Object target;
        private final Method _getRequestURI;
        private final Method _getRequestURL;
        private final Method _getParameterMap;
        private final Method _getMethod;
        private final Method _getHeader;
        private final Method _getRemoteAddr;
        private final static String targetClassName = "javax.servlet.http.HttpServletRequest";
        public HttpServletRequestAdapter(Object target) {
            this.target = target;
            try {
                Class<?> targetClass = target.getClass().getClassLoader().loadClass(targetClassName);
                _getRequestURI = targetClass.getMethod("getRequestURI");
                _getParameterMap = targetClass.getMethod("getParameterMap");
                _getMethod = targetClass.getMethod("getMethod");
                _getHeader = targetClass.getMethod("getHeader", String.class);
                _getRemoteAddr = targetClass.getMethod("getRemoteAddr");
                _getRequestURL = targetClass.getMethod("getRequestURL");
            } catch (NoSuchMethodException e) {
                throw new IllegalArgumentException("error :" + e.getMessage() + ". probable cause the target is not belong javax.servlet.http.HttpServletRequest ");
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("error :" + e.getMessage() + ". probable cause the target is not belong javax.servlet.http.HttpServletRequest ");
            }
        }
        public String getRequestURI() {
            try {
                return (String) _getRequestURI.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        public String getRequestURL() {
            try {
                return _getRequestURL.invoke(target).toString();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        public Map<String, String[]> getParameterMap() {
            try {
                return (Map<String, String[]>) _getParameterMap.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        public String getMethod() {
            try {
                return (String) _getMethod.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        public String getHeader(String name) {
            try {
                return (String) _getHeader.invoke(target, name);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        public String getRemoteAddr() {
            try {
                return (String) _getRemoteAddr.invoke(target);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        public String getClientIp() {
            String ip = getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = getRemoteAddr();
            }
            return ip;
        }
    }
}


相关文章
|
1月前
|
Web App开发 监控 Java
|
2月前
|
监控 Java 索引
|
3月前
|
移动开发 JSON 监控
网络协议解析:在员工上网监控软件中实现HTTP流量分析
随着企业对员工网络活动的监控需求不断增加,开发一套能够实现HTTP流量分析的网络协议解析系统变得愈发重要。本文将深入探讨如何在员工上网监控软件中实现HTTP流量分析,通过代码示例演示关键步骤。
221 0
|
2月前
|
Kubernetes 监控 安全
Kustomize 生产实战 - 注入监控 APM Agent
Kustomize 生产实战 - 注入监控 APM Agent
|
14天前
|
域名解析 网络协议 应用服务中间件
阿里云服务器配置免费https服务
阿里云服务器配置免费https服务
|
1月前
|
关系型数据库 MySQL Serverless
Serverless 应用引擎常见问题之新发布的服务 arms 没了如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
26 3
|
1月前
|
Windows
node搭建本地https和wss服务
node搭建本地https和wss服务
25 0
|
2月前
|
XML 自然语言处理 前端开发
NLP自学习平台提供了API接口调用服务,这些接口可以通过HTTP GET请求进行调用
【2月更文挑战第7天】NLP自学习平台提供了API接口调用服务,这些接口可以通过HTTP GET请求进行调用
18 2
|
2月前
|
XML JSON 中间件
快速入门Gin框架搭建HTTP服务
快速入门Gin框架搭建HTTP服务
20 0
|
2月前
|
JSON Go 数据格式
一文搞懂Go快速搭建HTTP服务
一文搞懂Go快速搭建HTTP服务
24 0