JVM Profiler Reporter介绍

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 开篇 JVM Profiler采集完数据后可以通过多种途径上报数据,对接Console,File,redis,kafka等,这篇文章会把源码罗列一下毕竟都很简单。

开篇

 JVM Profiler采集完数据后可以通过多种途径上报数据,对接Console,File,redis,kafka等,这篇文章会把源码罗列一下毕竟都很简单。
 JVM Profiler提供灵活的框架可以集成更多的Reporter,只要实现Reporter接口即可,看你个人意愿了,反正github上有源码,直接集成编译打包即可。


img_32f35386c75b58da37ff4cd050da4b01.png


ConsoleOutputReporter

  • 简单明了的通过Sytem.out.println来上报监控数据。
public class ConsoleOutputReporter implements Reporter {
    @Override
    public void report(String profilerName, Map<String, Object> metrics) {
        System.out.println(String.format("ConsoleOutputReporter - %s: %s", profilerName, JsonUtils.serialize(metrics)));
    }

    @Override
    public void close() {
    }
}


FileOutputReporter

  • 在指定的目录创建采集数据记录文件。
  • 通过FileWriter来往文件写入数据。
public class FileOutputReporter implements Reporter {
    private static final AgentLogger logger = AgentLogger.getLogger(FileOutputReporter.class.getName());
    
    private String directory;
    private ConcurrentHashMap<String, FileWriter> fileWriters = new ConcurrentHashMap<>();
    private volatile boolean closed = false;
    
    public FileOutputReporter() {
    }

    public String getDirectory() {
        return directory;
    }

    public void setDirectory(String directory) {
        synchronized (this) {
            if (this.directory == null || this.directory.isEmpty()) {
                this.directory = directory;
            } else {
                throw new RuntimeException(String.format("Cannot set directory to %s because it is already has value %s", directory, this.directory));
            }
        }
    }

    @Override
    public synchronized void report(String profilerName, Map<String, Object> metrics) {
        if (closed) {
            logger.info("Report already closed, do not report metrics");
            return;
        }
        
        FileWriter writer = ensureFile(profilerName);
        try {
            writer.write(JsonUtils.serialize(metrics));
            writer.write(System.lineSeparator());
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public synchronized void close() {
        closed = true;
        
        List<FileWriter> copy = new ArrayList<>(fileWriters.values());
        for (FileWriter entry : copy) {
            try {
                entry.flush();
                entry.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    private FileWriter ensureFile(String profilerName) {
        synchronized (this) {
            if (directory == null || directory.isEmpty()) {
                try {
                    directory = Files.createTempDirectory("jvm_profiler_").toString();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return fileWriters.computeIfAbsent(profilerName, t -> createFileWriter(t));
    }
    
    private FileWriter createFileWriter(String profilerName) {
        String path = Paths.get(directory, profilerName + ".json").toString();
        try {
            return new FileWriter(path, true);
        } catch (IOException e) {
            throw new RuntimeException("Failed to create file writer: " + path, e);
        }
    }
}


KafkaOutputReporter

  • 依赖kafka-client的jar包来构建KafkaProducer。
  • 通过producer.send来发送采集数据。
public class KafkaOutputReporter implements Reporter {
    private String brokerList = "localhost:9092";
    private boolean syncMode = false;
    
    private String topicPrefix;
    
    private ConcurrentHashMap<String, String> profilerTopics = new ConcurrentHashMap<>();

    private Producer<String, byte[]> producer;

    public KafkaOutputReporter() {
    }
    
    public KafkaOutputReporter(String brokerList, boolean syncMode, String topicPrefix) {
        this.brokerList = brokerList;
        this.syncMode = syncMode;
        this.topicPrefix = topicPrefix;
    }

    @Override
    public void report(String profilerName, Map<String, Object> metrics) {
        ensureProducer();

        String topicName = getTopic(profilerName);
        
        String str = JsonUtils.serialize(metrics);
        byte[] message = str.getBytes(StandardCharsets.UTF_8);

        Future<RecordMetadata> future = producer.send(
                new ProducerRecord<String, byte[]>(topicName, message));

        if (syncMode) {
            producer.flush();
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
    }

   // 省略一些非核心的代码
    private void ensureProducer() {
        synchronized (this) {
            if (producer != null) {
                return;
            }

            Properties props = new Properties();
            props.put("bootstrap.servers", brokerList);
            props.put("retries", 10);
            props.put("batch.size", 16384);
            props.put("linger.ms", 0);
            props.put("buffer.memory", 16384000);
            props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
            props.put("value.serializer", org.apache.kafka.common.serialization.ByteArraySerializer.class.getName());

            if (syncMode) {
                props.put("acks", "all");
            }

            producer = new KafkaProducer<>(props);
        }
    }
}


RedisOutputReporter

  • 依赖jedis包来实现redis的读写。
  • redis当中存储的采集数据的key是机器ip和时间戳的组合,value是采集的数据。
public class RedisOutputReporter implements Reporter {

    private static final AgentLogger logger = AgentLogger.getLogger(RedisOutputReporter.class.getName());
    private JedisPool redisConn = null;

    //JedisPool should always be used as it is thread safe.
    public void report(String profilerName, Map<String, Object> metrics) {
        ensureJedisConn();
        try {
            Jedis jedisClient = redisConn.getResource();
            jedisClient.set(createOriginStamp(profilerName), JsonUtils.serialize(metrics));
            redisConn.returnResource(jedisClient);
        } catch (Exception err) {
            logger.warn(err.toString());
        }
    }

    public String createOriginStamp(String profilerName) {
        try {
            return (profilerName + "-" + InetAddress.getLocalHost().getHostAddress() + "-" + System.currentTimeMillis());
        } catch (UnknownHostException err) {
            logger.warn("Address could not be determined and will be omitted!");
            return (profilerName + "-" + System.currentTimeMillis());
        }
    }

    public void close() {
        synchronized (this) {
            redisConn.close();
            redisConn = null;
        }
    }

    private void ensureJedisConn() {
        synchronized (this) {
            if (redisConn == null || redisConn.isClosed()) {
                redisConn = new JedisPool(System.getenv("JEDIS_PROFILER_CONNECTION"));
                return;
            }
        }
    }
}
目录
相关文章
|
分布式计算 监控 Java
Uber jvm profiler 使用
Uber jvm profiler 使用
279 0
Uber jvm profiler 使用
|
存储 SQL 分布式计算
如何用 Uber JVM Profiler 等可视化工具监控 Spark 应用程序?
  关键要点   持续可靠地运行 Spark 应用程序是一项具有挑战性的任务,而且需要一个良好的性能监控系统。   - 在设计性能监控系统时有三个目标——收集服务器和应用程序指标、在时序数据库中存储指标,并提供用于数据可视化的仪表盘。   Uber JVM Profiler 被用于监控 Spark 应用程序,用到的其他技术还有 InfluxDB(用于存储时序数据)和 Grafana(数据可视化工具)。性能监控系统可帮助 DevOps 团队有效地监控系统,用以满足应用程序的合规性和 SLA。
263 0
|
监控 Java 调度
JVM Profiler 启动过程分析
开篇  先来调侃一句,原来独角兽Uber的程序员写的代码也是看得懂的,而且还是比较容易看得懂的,所以有时候在设计模式和代码结构清晰以及可读性方面我还是更倾向于后者,宁可重复或浪费一部分代码也要整个代码的可读性更强一些。
1149 0
|
数据采集 Linux 索引
JVM Profiler IOProfiler
开篇  IOProfiler因为采集方法的限制,目前支持linux系统指标采集,但是不支持mac,windows等操作系统。  IOProfiler通过读取linux系统的/proc/self/io的当前线程的IO指标数据,该文件的内容如下图所示,通过解析成kv键值对完成采集。
1027 0
|
Java
JVM Profiler CpuAndMemoryProfiler
开篇   CpuAndMemoryProfiler主要用来采集cpu和memory相关的信息,采集核心方法都是由ManagementFactory提供的接口: getClassLoadingMXBean() 返回 Java 虚拟机的类加载系统的管理 Bean。
889 0
|
Java
JVM Profiler StacktraceCollectorProfiler
开篇  StacktraceCollectorProfiler主要用来采集线程的调用栈,原理是通过ManagementFactory.getThreadMXBean()返回的ThreadMXBean对象来实现。
813 0
|
监控 Java 编解码
JVM Profiler 方法耗时采集
开篇  JVM Profile的方法采集通过修改字节码在原来方法体的前置和后置增加采集耗时的代码。核心是基于基于java自带的instrument包和javassist包来实现的。
1169 0
|
消息中间件 分布式计算 Java
JVM Profiler介绍
开篇  过去的几周把java多线程相关部分的源码粗粗的看了一遍基本上也算告一段落了,后面应该会聚焦看下dubbo、mycat、datax以及剩下部分的mybatis。
1706 0
|
监控 Java 消息中间件
JVM Profiler 整体架构
开篇 整个JVM Profiler的组件类似于上图,抽象出来主要分为:Class File Transformer:负责转换被监控方法的字节码,在前后增加耗时统计。
1054 0
|
2月前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。