├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── com └── github └── netty └── monitor └── agent ├── Constant.java ├── LogUtils.java ├── NettyElementMatcher.java ├── NettyInterceptor.java ├── NettyMonitor.java ├── NettyMonitorAgent.java ├── NettyMonitorCache.java ├── data ├── ChannelMonitorData.java ├── EventLoopMonitorData.java └── GlobalMonitorData.java ├── display ├── LogMonitorOutService.java └── MonitorOutService.java ├── monitor ├── BootstrapMonitor.java ├── ChannelStateMonitor.java ├── InputDataMonitor.java └── OutputDataMonitor.java └── task ├── ChannelIntervalStatisticsTask.java ├── GlobalIntervalStatisticsTask.java └── MonitorDataIntervalTask.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | 25 | # Operating System Files 26 | 27 | *.DS_Store 28 | Thumbs.db 29 | *.sw? 30 | .#* 31 | *# 32 | *~ 33 | *.sublime-* 34 | 35 | # Build Artifacts 36 | 37 | .gradle/ 38 | build/ 39 | target/ 40 | bin/ 41 | dependency-reduced-pom.xml 42 | 43 | # Eclipse Project Files 44 | 45 | .classpath 46 | .project 47 | .settings/ 48 | 49 | # IntelliJ IDEA Files 50 | 51 | *.iml 52 | *.ipr 53 | *.iws 54 | *.idea 55 | .idea 56 | out 57 | 58 | README.html 59 | /LOGS/ 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 xzc-coder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netty-monitor-agent 2 | # 1. 介绍 3 | 4 | ## 1.1 基本概况 5 | 6 | 该项目是基于Java Agent的静态加载实现的Netty无侵入监控,创建目的是为了学习和使用Java Agent技术及Netty数据监控 7 | 8 | ## 1.2 技术栈 9 | 10 | Java Agent + bytebuddy + Netty 11 | 12 | ## 1.3 使用方式 13 | 14 | ### 项目中 15 | 16 | 将代码打包为jar包,或者从标签处下载,在jar包运行时添加参数 -javaagent:agen包路径 17 | 18 | 例如项目包test.jar和agent包netty-monitor-agent-1.0.jar在同一目录,那么启动命令则如下: 19 | 20 | ``` 21 | java -javaagent:./netty-monitor-agent-1.0.jar.jar -jar test.jar 22 | ``` 23 | 24 | ### IDEA中 25 | 26 | 点击 Add VM options,然后在该行中填入 -javaagent:agent包路径,如下: 27 | 28 | ``` 29 | -javaagent:./netty-monitor-agent-1.0.jar.jar 30 | ``` 31 | 32 | 如果需要Debug,则把该agent包的依赖导入即可 33 | 34 | ## 2. 其它 35 | 36 | ### 2.1 自定义输出 37 | 38 | #### 侵入式(不推荐) 39 | 40 | 在项目包中引入agent依赖,实现接口MonitorOutService,在该接口中进行监控数据输出,然后通过添加启动参数:-DmonitorOutServiceClass=实现类的全类名,如下: 41 | 42 | ``` 43 | public class TestMonitorOutService implements MonitorOutService { 44 | @Override 45 | public void outputMonitorData(GlobalMonitorData globalMonitorData, List eventLoopMonitorDataList, List channelMonitorDataList) { 46 | System.out.println("测试输出"); 47 | } 48 | } 49 | 50 | 启动参数: 51 | -DmonitorOutServiceClass=com.github.netty.monitor.agent.test.TestMonitorOutService 52 | ``` 53 | 54 | #### 非侵入式 55 | 56 | 自己修改源码,实现接口MonitorOutService,然后添加启动参数:-DmonitorOutServiceClass=实现类的全类名,之后再打jar去做无侵入监控,可以把监控数据持久化记录到Redis、MySQL等数据库中 57 | 58 | ### 2.2 监控数据 59 | 60 | #### 全局数据 61 | 62 | | 字段 | 说明 | 63 | | --------------- | ------------------------------------------------------------ | 64 | | ActiveChannel | 激活的连接数 | 65 | | TotalInput | 总接受字节量,单位B、KB、MB、GB、TB自动变化 | 66 | | TotalOutput | 总输出字节量,单位B、KB、MB、GB、TB自动变化 | 67 | | InputRate | 总输入速率,单位B/s、KB/s、MB/s、GB/s、TB/s自动变化 | 68 | | OutputRate | 总输出速率,单位B/s、KB/s、MB/s、GB/s、TB/s自动变化 | 69 | | PoolUseHeap | 池化堆内内存使用总量,单位B、KB、MB、GB、TB自动变化 | 70 | | PoolUseDirect | 池化直接内存使用总量,单位B、KB、MB、GB、TB自动变化 | 71 | | unpoolUseHeap | 非池化堆内内存使用总量,单位B、KB、MB、GB、TB自动变化 | 72 | | unpoolUseDirect | 非池化直接内存使用总量,单位B、KB、MB、GB、TB自动变化 | 73 | | useDirect | 直接内存使用总量(池化+非池化),单位B、KB、MB、GB、TB自动变化 | 74 | 75 | #### EventLoop数据 76 | 77 | | 字段 | 说明 | 78 | | ------------- | ----------------------------- | 79 | | EventLoopName | 当前EventLoop的名称 | 80 | | ChannelCount | 当前EventLoop下的活跃连接总数 | 81 | | PendingTask | 当前EventLoop下的任务总数 | 82 | | State | 当前EventLoop的状态 | 83 | 84 | 每个EventLoop都会打印一份 85 | 86 | #### Channel数据 87 | 88 | | 字段 | 说明 | 89 | | -------------- | --------------------------------------------------- | 90 | | LocalAddress | 本地地址(IP:端口) | 91 | | RemoteAddress | 远端地址(IP:端口) | 92 | | TotalInput | 接受字节量,单位B、KB、MB、GB、TB自动变化 | 93 | | TotalOutput | 输出字节量,单位B、KB、MB、GB、TB自动变化 | 94 | | InputRate | 输入速率,单位B/s、KB/s、MB/s、GB/s、TB/s自动变化 | 95 | | OutputRate | 输出速率,单位B/s、KB/s、MB/s、GB/s、TB/s自动变化 | 96 | | activeDate | 激活时间,格式:MM-dd HH:mm:ss | 97 | | inactiveDate | 断开时间,格式:MM-dd HH:mm:ss | 98 | | LastInputDate | 最后一次接受数据时间,格式:MM-dd HH:mm:ss | 99 | | LastOutputDate | 最后一次输出数据的时间,格式:MM-dd HH:mm:ss | 100 | | LastEventDate | 最后一次发生事件的时间,格式:MM-dd HH:mm:ss | 101 | | LastExceptDate | 最后一次Channel发生异常的时间,格式:MM-dd HH:mm:ss | 102 | | LastExceptMsg | 最后一次Channel发生异常的信息 | 103 | 104 | ### 2.3 注意事项 105 | 106 | 1.该项目主要是学习为主,代码可供参考 107 | 108 | 2.支持JDK版本为1.8以上(其它版本需要自测) 109 | 110 | 3.暂不支持Netty版本为4.0.45.Final以下的,4.0.0至4.0.44.Final的版本需要自行通过反射去PlatformDepedent类中获取池化和非池化的数据 111 | 112 | 4.默认是控制台输出,如果导入了日志框架,则会打印日志,默认是每5秒打印一次,对于离线的Channel,离线的30秒后该Channel数据才会消失 113 | 114 | 5.最好自己修改源码,实现持久化数据存储 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.xy 8 | netty-monitor-agent 9 | 1.0 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | 19 | 20 | org.apache.maven.plugins 21 | maven-source-plugin 22 | 3.2.1 23 | 24 | 25 | attach-sources 26 | package 27 | 28 | jar 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-assembly-plugin 36 | 2.4 37 | 38 | false 39 | 40 | jar-with-dependencies 41 | 42 | 43 | 44 | 45 | true 46 | 47 | true 48 | 49 | 50 | 51 | 52 | com.github.netty.monitor.agent.NettyMonitorAgent 53 | true 54 | true 55 | 56 | 57 | 58 | 59 | 60 | 61 | make-assembly 62 | package 63 | 64 | single 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | net.bytebuddy 74 | byte-buddy 75 | 1.10.18 76 | 77 | 78 | net.bytebuddy 79 | byte-buddy-agent 80 | 1.10.18 81 | 82 | 83 | io.netty 84 | netty-all 85 | 4.1.109.Final 86 | provided 87 | 88 | 89 | org.projectlombok 90 | lombok 91 | 1.18.4 92 | provided 93 | 94 | 95 | org.slf4j 96 | slf4j-api 97 | 1.7.21 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/Constant.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent; 2 | 3 | 4 | /** 5 | * @Date: 2022/6/11 10:52 6 | * @Description: 常量类 7 | * @author: xzc-coder 8 | */ 9 | public class Constant { 10 | 11 | private Constant() { 12 | 13 | } 14 | 15 | /** 16 | * 统计间隔,每5秒统计一次 17 | */ 18 | public static int STATISTICS_INTERVAL_SECOND = 5; 19 | 20 | /** 21 | * Channel断开连接后延迟多少秒移除 22 | */ 23 | 24 | public static int CHANNEL_MONITOR_REMOVE_DELAY_SECOND = 30; 25 | 26 | public static String SPACE = " "; 27 | 28 | public static String EMPTY = ""; 29 | 30 | public static final String B = "B"; 31 | 32 | public static final String KB = "KB"; 33 | 34 | public static final String MB = "MB"; 35 | 36 | public static final String GB = "GB"; 37 | 38 | public static final String TB = "TB"; 39 | 40 | public static final String RATE = "/s"; 41 | 42 | public static final long CARRY = 1024L; 43 | 44 | public static final long KB_CARRY = CARRY; 45 | 46 | public static final long MB_CARRY = KB_CARRY * CARRY; 47 | 48 | public static final long GB_CARRY = MB_CARRY * CARRY; 49 | 50 | public static final long TB_CARRY = GB_CARRY * CARRY; 51 | 52 | public static final int LOG_MEMORY_SCALE = 6; 53 | 54 | public static final int LOG_SCALE = 3; 55 | 56 | public static final int LOG_SCALE_B = 0; 57 | 58 | public static final String MONITOR_OUT_SERVICE_CLASS = "monitorOutServiceClass"; 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/LogUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.text.DateFormat; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Arrays; 9 | import java.util.Date; 10 | import java.util.List; 11 | 12 | /** 13 | * @Date: 2022/6/18 20:05 14 | * @Description: 日志工具 15 | * @author: xzc-coder 16 | */ 17 | public class LogUtils { 18 | 19 | private volatile static boolean hasLoggingFramework; 20 | 21 | private static final List LOGGING_FRAMEWORKS = Arrays.asList( 22 | "org.apache.logging.log4j.core.Logger", 23 | "ch.qos.logback.classic.Logger", 24 | "org.apache.log4j.Logger" 25 | ); 26 | 27 | static { 28 | hasLoggingFramework = isLoggingFrameworkAvailable(); 29 | } 30 | 31 | private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; 32 | private static final String DEBUG = "DEBUG"; 33 | private static final String INFO = "INFO"; 34 | private static final String WARN = "WARN"; 35 | private static final String ERROR = "ERROR"; 36 | 37 | private static boolean isLoggingFrameworkAvailable() { 38 | boolean result = false; 39 | for(String className : LOGGING_FRAMEWORKS) { 40 | boolean loggingFrameworkAvailable = isLoggingFrameworkAvailable(className); 41 | if(loggingFrameworkAvailable) { 42 | result = true; 43 | break; 44 | } 45 | } 46 | return result; 47 | } 48 | 49 | private static boolean isLoggingFrameworkAvailable(String className) { 50 | try { 51 | Class.forName(className); 52 | return true; 53 | } catch (ClassNotFoundException e) { 54 | return false; 55 | } 56 | } 57 | 58 | public static boolean isLoggingFramework() { 59 | return hasLoggingFramework; 60 | } 61 | 62 | public static void debug(Class clazz,String message) { 63 | if(hasLoggingFramework) { 64 | Logger logger = LoggerFactory.getLogger(clazz); 65 | logger.debug(message); 66 | }else { 67 | System.out.println(getPrintContent(clazz,message,DEBUG)); 68 | } 69 | } 70 | 71 | 72 | public static void info(Class clazz,String message) { 73 | if(hasLoggingFramework) { 74 | Logger logger = LoggerFactory.getLogger(clazz); 75 | logger.info(message); 76 | }else { 77 | System.out.println(getPrintContent(clazz,message,INFO)); 78 | } 79 | } 80 | 81 | 82 | public static void warn(Class clazz,String message) { 83 | if(hasLoggingFramework) { 84 | Logger logger = LoggerFactory.getLogger(clazz); 85 | logger.warn(message); 86 | }else { 87 | System.out.println(getPrintContent(clazz,message,WARN)); 88 | } 89 | } 90 | 91 | public static void error(Class clazz,String message) { 92 | if(hasLoggingFramework) { 93 | Logger logger = LoggerFactory.getLogger(clazz); 94 | logger.error(message); 95 | }else { 96 | System.out.println(getPrintContent(clazz,message,ERROR)); 97 | } 98 | } 99 | 100 | public static void error(Class clazz,String message,Throwable throwable) { 101 | if(hasLoggingFramework) { 102 | Logger logger = LoggerFactory.getLogger(clazz); 103 | logger.error(message,throwable); 104 | }else { 105 | System.out.println(getPrintContent(clazz,message,ERROR)); 106 | throwable.printStackTrace(); 107 | } 108 | } 109 | 110 | 111 | private static String getDate() { 112 | DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); 113 | return dateFormat.format(new Date()); 114 | } 115 | 116 | private static String getThreadName() { 117 | return "["+Thread.currentThread().getName()+"]"; 118 | } 119 | 120 | private static String getPrintContent(Class clazz, String message,String level) { 121 | StringBuilder content = new StringBuilder(); 122 | content.append(getDate()) 123 | .append(Constant.SPACE) 124 | .append(getThreadName()) 125 | .append(Constant.SPACE) 126 | .append(level) 127 | .append(Constant.SPACE) 128 | .append(clazz.getName()) 129 | .append(Constant.SPACE) 130 | .append("-") 131 | .append(Constant.SPACE) 132 | .append(message); 133 | return content.toString(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/NettyElementMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.ChannelPromise; 5 | import net.bytebuddy.matcher.ElementMatcher; 6 | import net.bytebuddy.matcher.ElementMatchers; 7 | 8 | import java.net.SocketAddress; 9 | 10 | 11 | /** 12 | * @Date: 2022/6/11 10:58 13 | * @Description: Netty元素匹配器,既匹配一些拦截点 14 | * @author: xzc-coder 15 | */ 16 | public class NettyElementMatcher { 17 | 18 | private NettyElementMatcher() { 19 | } 20 | 21 | 22 | public static final String NETTY = "netty"; 23 | 24 | public static final String CLASS_HEAD_CONTEXT = "io.netty.channel.DefaultChannelPipeline$HeadContext"; 25 | 26 | public static final String CLASS_SERVER_BOOTSTRAP = "io.netty.bootstrap.ServerBootstrap"; 27 | 28 | public static final String CLASS_BOOTSTRAP = "io.netty.bootstrap.Bootstrap"; 29 | 30 | public static final String METHOD_CHANNEL_READ = "channelRead"; 31 | 32 | public static final String METHOD_WRITE = "write"; 33 | 34 | public static final String METHOD_CHANNEL_ACTIVE = "channelActive"; 35 | 36 | public static final String METHOD_CHANNEL_INACTIVE = "channelInactive"; 37 | 38 | public static final String METHOD_USER_EVENT_TRIGGERED = "userEventTriggered"; 39 | 40 | public static final String METHOD_EXCEPTION_CAUGHT = "exceptionCaught"; 41 | 42 | public static final String METHOD_CONNECT = "connect"; 43 | 44 | public static final String METHOD_BIND = "bind"; 45 | 46 | public static final ElementMatcher.Junction CLASS_HEAD_CONTEXT_MATCHER = ElementMatchers.named(CLASS_HEAD_CONTEXT); 47 | 48 | public static final ElementMatcher.Junction CLASS_SERVER_BOOTSTRAP_MATCHER = ElementMatchers.named(CLASS_SERVER_BOOTSTRAP); 49 | 50 | public static final ElementMatcher.Junction CLASS_BOOTSTRAP_MATCHER = ElementMatchers.named(CLASS_BOOTSTRAP); 51 | 52 | public static final ElementMatcher.Junction HEAD_CONTEXT_METHOD_CHANNEL_READ_MATCHER = ElementMatchers.named(METHOD_CHANNEL_READ).and(ElementMatchers.takesArguments(ChannelHandlerContext.class, Object.class)); 53 | 54 | public static final ElementMatcher.Junction HEAD_CONTEXT_METHOD_WRITE_MATCHER = ElementMatchers.named(METHOD_WRITE).and(ElementMatchers.takesArguments(ChannelHandlerContext.class, Object.class, ChannelPromise.class)); 55 | 56 | public static final ElementMatcher.Junction HEAD_CONTEXT_METHOD_CHANNEL_ACTIVE_MATCHER = ElementMatchers.named(METHOD_CHANNEL_ACTIVE).and(ElementMatchers.takesArguments(ChannelHandlerContext.class)); 57 | 58 | public static final ElementMatcher.Junction HEAD_CONTEXT_METHOD_CHANNEL_INACTIVE_MATCHER = ElementMatchers.named(METHOD_CHANNEL_INACTIVE).and(ElementMatchers.takesArguments(ChannelHandlerContext.class)); 59 | 60 | public static final ElementMatcher.Junction HEAD_CONTEXT_METHOD_USER_EVENT_TRIGGERED_MATCHER = ElementMatchers.named(METHOD_USER_EVENT_TRIGGERED).and(ElementMatchers.takesArguments(ChannelHandlerContext.class, Object.class)); 61 | 62 | public static final ElementMatcher.Junction HEAD_CONTEXT_METHOD_EXCEPTION_CAUGHT_MATCHER = ElementMatchers.named(METHOD_EXCEPTION_CAUGHT).and(ElementMatchers.takesArguments(Throwable.class)); 63 | 64 | public static final ElementMatcher.Junction SERVER_BOOTSTRAP_METHOD_BIND_MATCHER = (ElementMatchers.named(METHOD_BIND).and(ElementMatchers.takesArguments(SocketAddress.class))) 65 | .or(ElementMatchers.named(METHOD_BIND).and(ElementMatchers.takesNoArguments())); 66 | 67 | public static final ElementMatcher.Junction BOOTSTRAP_METHOD_CONNECT_MATCHER = (ElementMatchers.named(METHOD_CONNECT).and(ElementMatchers.takesArguments(SocketAddress.class, SocketAddress.class))) 68 | .or(ElementMatchers.named(METHOD_CONNECT).and(ElementMatchers.takesArguments(SocketAddress.class))) 69 | .or(ElementMatchers.named(METHOD_CONNECT).and(ElementMatchers.takesNoArguments())); 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/NettyInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent; 2 | 3 | import com.github.netty.monitor.agent.monitor.BootstrapMonitor; 4 | import com.github.netty.monitor.agent.monitor.ChannelStateMonitor; 5 | import com.github.netty.monitor.agent.monitor.InputDataMonitor; 6 | import com.github.netty.monitor.agent.monitor.OutputDataMonitor; 7 | import net.bytebuddy.implementation.bind.annotation.*; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.concurrent.Callable; 15 | 16 | 17 | /** 18 | * @Date: 2022/6/11 9:58 19 | * @Description: Netty拦截器 20 | * @author: xzc-coder 21 | */ 22 | public class NettyInterceptor { 23 | 24 | private static List nettyMonitorList = new ArrayList<>(); 25 | 26 | static { 27 | nettyMonitorList.add(new InputDataMonitor()); 28 | nettyMonitorList.add(new OutputDataMonitor()); 29 | nettyMonitorList.add(new ChannelStateMonitor()); 30 | nettyMonitorList.add(new BootstrapMonitor()); 31 | } 32 | 33 | @RuntimeType 34 | public static Object intercept(@This Object target, @Origin Method method, @AllArguments Object[] args, @Pipe @SuperCall Callable callable) throws Exception { 35 | System.out.println("-----intercept-----------"); 36 | Map contextMap = new HashMap<>(); 37 | beforeIntercept(target, method, args, contextMap); 38 | Object result = callable.call(); 39 | afterIntercept(target, method, args, result, contextMap); 40 | return result; 41 | } 42 | 43 | private static void beforeIntercept(Object target, Method method, Object[] args, Map contextMap) throws Exception{ 44 | for (NettyMonitor nettyMonitor : nettyMonitorList) { 45 | nettyMonitor.beforeIntercept(target, method, args, contextMap); 46 | } 47 | } 48 | 49 | private static void afterIntercept(Object target, Method method, Object[] args, Object result, Map contextMap) throws Exception{ 50 | for (NettyMonitor nettyMonitor : nettyMonitorList) { 51 | nettyMonitor.afterIntercept(target, method, args, result, contextMap); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/NettyMonitor.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent; 2 | 3 | import net.bytebuddy.implementation.bind.annotation.*; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.Map; 7 | import java.util.concurrent.Callable; 8 | 9 | 10 | /** 11 | * @Date: 2022/6/11 10:02 12 | * @Description: Netty监控器接口 13 | * @author: xzc-coder 14 | */ 15 | public interface NettyMonitor { 16 | 17 | /** 18 | * 拦截的方法调用前拦截 19 | * 20 | * @param target 目标对象 21 | * @param method 方法 22 | * @param args 参数 23 | * @param contextMap 上下文 24 | */ 25 | default void beforeIntercept(Object target, Method method, Object[] args, Map contextMap) throws Exception{ 26 | 27 | } 28 | 29 | /** 30 | * 拦截的方法调用后拦截 31 | * 32 | * @param target 目标对象 33 | * @param method 方法 34 | * @param args 参数 35 | * @param result 返回值 36 | * @param contextMap 上下文 37 | */ 38 | default void afterIntercept(Object target, Method method, Object[] args, Object result, Map contextMap) throws Exception{ 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/NettyMonitorAgent.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import net.bytebuddy.agent.builder.AgentBuilder; 5 | import net.bytebuddy.description.type.TypeDescription; 6 | import net.bytebuddy.implementation.MethodDelegation; 7 | 8 | import java.lang.instrument.Instrumentation; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | 13 | /** 14 | * @Date: 2022/6/11 10:26 15 | * @Description: Netty监控器Agent类 16 | * @author: xzc-coder 17 | */ 18 | @Slf4j 19 | public class NettyMonitorAgent { 20 | 21 | /** 22 | * 启动时重新加载class 23 | * 24 | * @param agentArgs 25 | * @param inst 插桩器 26 | * @throws Exception 27 | */ 28 | public static void premain(String agentArgs, Instrumentation inst) throws Exception { 29 | LogUtils.info(NettyMonitorAgent.class, "-----------netty agent start-----------"); 30 | Set transformedClasses = new HashSet<>(); 31 | //按类添加拦截点匹配器 32 | inst.addTransformer(new AgentBuilder.Default() 33 | .type(NettyElementMatcher.CLASS_HEAD_CONTEXT_MATCHER) 34 | .transform((builder, type, classLoader, module) -> { 35 | if (!transformedClasses.contains(type)) { 36 | transformedClasses.add(type); 37 | return builder 38 | .method(NettyElementMatcher.HEAD_CONTEXT_METHOD_CHANNEL_READ_MATCHER 39 | .or(NettyElementMatcher.HEAD_CONTEXT_METHOD_WRITE_MATCHER) 40 | .or(NettyElementMatcher.HEAD_CONTEXT_METHOD_CHANNEL_ACTIVE_MATCHER) 41 | .or(NettyElementMatcher.HEAD_CONTEXT_METHOD_CHANNEL_INACTIVE_MATCHER) 42 | .or(NettyElementMatcher.HEAD_CONTEXT_METHOD_USER_EVENT_TRIGGERED_MATCHER) 43 | .or(NettyElementMatcher.HEAD_CONTEXT_METHOD_EXCEPTION_CAUGHT_MATCHER)) 44 | .intercept(MethodDelegation.to(NettyInterceptor.class)); 45 | } 46 | return builder; 47 | }).installOn(inst)); 48 | 49 | inst.addTransformer(new AgentBuilder.Default() 50 | .type(NettyElementMatcher.CLASS_SERVER_BOOTSTRAP_MATCHER) 51 | .transform((builder, type, classLoader, module) -> { 52 | if (!transformedClasses.contains(type)) { 53 | transformedClasses.add(type); 54 | return builder 55 | .method(NettyElementMatcher.SERVER_BOOTSTRAP_METHOD_BIND_MATCHER) 56 | .intercept(MethodDelegation.to(NettyInterceptor.class)); 57 | } 58 | return builder; 59 | }).installOn(inst)); 60 | 61 | inst.addTransformer(new AgentBuilder.Default() 62 | .type(NettyElementMatcher.CLASS_BOOTSTRAP_MATCHER) 63 | .transform((builder, type, classLoader, module) -> { 64 | if (!transformedClasses.contains(type)) { 65 | transformedClasses.add(type); 66 | return builder 67 | .method(NettyElementMatcher.BOOTSTRAP_METHOD_CONNECT_MATCHER) 68 | .intercept(MethodDelegation.to(NettyInterceptor.class)); 69 | } 70 | return builder; 71 | }).installOn(inst)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/NettyMonitorCache.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent; 2 | 3 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 4 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 5 | import io.netty.channel.*; 6 | import io.netty.util.concurrent.SingleThreadEventExecutor; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.CopyOnWriteArraySet; 14 | 15 | /** 16 | * @Date: 2022/6/11 11:48 17 | * @Description: Netty监控器缓存 18 | * @author: xzc-coder 19 | */ 20 | public class NettyMonitorCache { 21 | 22 | private NettyMonitorCache() { 23 | } 24 | 25 | /** 26 | * 统计Channel相关的数据缓存 27 | */ 28 | private static final Map channelMonitorDataMap = new ConcurrentHashMap<>(); 29 | /** 30 | * EventLoopGroup的缓存,不存数据是因为通过定时任务去从EventLoopGroup中获取数据并统计 31 | */ 32 | private static final Set eventLoopGroupSet = new CopyOnWriteArraySet<>(); 33 | /** 34 | * 统计某个线程下的Channel数量缓存 35 | */ 36 | private static final Map eventLoopChannelCountMap = new ConcurrentHashMap<>(); 37 | 38 | public static ChannelMonitorData removeNettyMonitorData(Channel channel) { 39 | return removeNettyMonitorData(channel.id()); 40 | } 41 | 42 | public static ChannelMonitorData removeNettyMonitorData(ChannelId channelId) { 43 | return channelMonitorDataMap.remove(channelId); 44 | } 45 | 46 | public static ChannelMonitorData getNettyMonitorData(Channel channel) { 47 | ChannelId channelId = channel.id(); 48 | ChannelMonitorData channelMonitorData = channelMonitorDataMap.get(channelId); 49 | if (channelMonitorData == null) { 50 | channelMonitorData = new ChannelMonitorData(channel); 51 | EventLoop eventLoop = channel.eventLoop(); 52 | if (eventLoop instanceof SingleThreadEventExecutor) { 53 | SingleThreadEventExecutor singleThreadEventExecutor = (SingleThreadEventExecutor) eventLoop; 54 | String name = singleThreadEventExecutor.threadProperties().name(); 55 | channelMonitorData.setEventLoopName(name); 56 | } 57 | channelMonitorDataMap.put(channelId, channelMonitorData); 58 | } 59 | return channelMonitorData; 60 | } 61 | 62 | public static List getChannelMonitorDataList() { 63 | return new ArrayList<>(channelMonitorDataMap.values()); 64 | } 65 | 66 | 67 | public static GlobalMonitorData getGlobalMonitorData() { 68 | return GlobalMonitorData.getInstance(); 69 | } 70 | 71 | public static Set getEventLoopGroupSet() { 72 | return eventLoopGroupSet; 73 | } 74 | 75 | public static void addEventLoopGroup(EventLoopGroup eventLoopGroup) { 76 | eventLoopGroupSet.add(eventLoopGroup); 77 | } 78 | 79 | public static void removeEventLoopGroup(EventLoopGroup eventLoopGroup) { 80 | eventLoopGroupSet.remove(eventLoopGroup); 81 | } 82 | 83 | public static void channelCountDecrement(long threadId) { 84 | Integer channelCount = eventLoopChannelCountMap.get(threadId); 85 | if (channelCount != null) { 86 | --channelCount; 87 | eventLoopChannelCountMap.put(threadId, channelCount); 88 | } 89 | } 90 | 91 | public static void channelCountIncrement(long threadId) { 92 | Integer channelCount = eventLoopChannelCountMap.get(threadId); 93 | if (channelCount == null) { 94 | channelCount = 0; 95 | } 96 | ++channelCount; 97 | eventLoopChannelCountMap.put(threadId, channelCount); 98 | } 99 | 100 | public static Integer getChannelCount(long threadId) { 101 | return eventLoopChannelCountMap.get(threadId); 102 | } 103 | 104 | public static Integer removeChannelCount(long threadId) { 105 | return eventLoopChannelCountMap.remove(threadId); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/data/ChannelMonitorData.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.data; 2 | 3 | import io.netty.channel.Channel; 4 | import lombok.Data; 5 | 6 | import java.net.InetSocketAddress; 7 | import java.util.Date; 8 | 9 | 10 | /** 11 | * @Date: 2022/6/11 16:02 12 | * @Description: Channel的监控数据 13 | * @author: xzc-coder 14 | */ 15 | @Data 16 | public class ChannelMonitorData { 17 | 18 | /** 19 | * channel的ID,短的ID,长的显示不了 20 | */ 21 | private final String channelId; 22 | 23 | /** 24 | * Channel 25 | */ 26 | private final Channel channel; 27 | 28 | /** 29 | * Channel的类型,class简写 30 | */ 31 | private final String channelType; 32 | 33 | private InetSocketAddress remoteAddress; 34 | 35 | private InetSocketAddress localAddress; 36 | 37 | /** 38 | * Channel绑定的eventLoop名 39 | */ 40 | private String eventLoopName; 41 | 42 | /** 43 | * channel间隔期内读取的字节数 44 | */ 45 | private long intervalInputByte = 0; 46 | 47 | /** 48 | * channel间隔期内写出的字节数 49 | */ 50 | private long intervalOutputByte = 0; 51 | /** 52 | * channel累计读取的字节数 53 | */ 54 | private long totalInputByte = 0; 55 | 56 | /** 57 | * channel累计写出的字节数 58 | */ 59 | private long totalOutputByte = 0; 60 | 61 | /** 62 | * 读取速率 63 | */ 64 | private int inputRateByte = 0; 65 | 66 | /** 67 | * 写出速率 68 | */ 69 | private int outputRateByte = 0; 70 | 71 | /** 72 | * 事件触发总数 73 | */ 74 | private int eventTriggeredCount = 0; 75 | 76 | /** 77 | * 最后一次异常发生的时间 78 | */ 79 | private Date lastExceptionDateTime; 80 | 81 | /** 82 | * 最后一次异常信息 83 | */ 84 | private String lastExceptionMessage; 85 | 86 | /** 87 | * 最后一次事件触发时间 88 | */ 89 | private Date lastEventTriggeredDateTime; 90 | 91 | /** 92 | * Channel激活时间 93 | */ 94 | private Date activeDateTime; 95 | 96 | /** 97 | * Channel失效时间 98 | */ 99 | private Date inactiveDateTime; 100 | 101 | /** 102 | * 最后一次读取数据时间 103 | */ 104 | private Date lastInputDateTime; 105 | 106 | /** 107 | * 最后一次写出数据时间 108 | */ 109 | private Date lastOutputDateTime; 110 | 111 | 112 | public ChannelMonitorData(Channel channel) { 113 | this.channel = channel; 114 | this.channelId = channel.id().asShortText(); 115 | this.channelType = channel.getClass().getSimpleName(); 116 | this.remoteAddress = (InetSocketAddress) channel.remoteAddress(); 117 | this.localAddress = (InetSocketAddress) channel.localAddress(); 118 | 119 | } 120 | 121 | public void addIntervalOutputByte(int outputByte) { 122 | this.intervalOutputByte += outputByte; 123 | } 124 | 125 | public void addTotalOutputByte(int outputByte) { 126 | this.totalOutputByte += outputByte; 127 | } 128 | 129 | public void addIntervalInputByte(int inputByte) { 130 | this.intervalInputByte += inputByte; 131 | } 132 | 133 | public void addTotalInputByte(int inputByte) { 134 | this.totalInputByte += inputByte; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/data/EventLoopMonitorData.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.data; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Date: 2022/6/11 16:32 7 | * @Description: EventLoop的监控数据 8 | * @author: xzc-coder 9 | */ 10 | @Data 11 | public class EventLoopMonitorData { 12 | 13 | /** 14 | * eventLoop名 15 | */ 16 | private String eventLoopName; 17 | /** 18 | * eventLoop下的Channel总数(活跃的) 19 | */ 20 | private Integer channelCount; 21 | /** 22 | * eventLoop中的任务总数 23 | */ 24 | private Integer pendingTaskCount; 25 | 26 | /** 27 | * eventLoop所绑定的线程的状态 28 | */ 29 | private Thread.State state; 30 | 31 | 32 | public EventLoopMonitorData(String eventLoopName) { 33 | this.eventLoopName = eventLoopName; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/data/GlobalMonitorData.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.data; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | import java.util.concurrent.atomic.AtomicLong; 7 | 8 | 9 | /** 10 | * @Date: 2022/6/11 18:22 11 | * @Description: 全局的监控数据 12 | * @author: xzc-coder 13 | */ 14 | @Data 15 | public class GlobalMonitorData { 16 | 17 | private GlobalMonitorData() { 18 | } 19 | 20 | /** 21 | * 监控间隔期内读取的字节数 22 | */ 23 | private AtomicLong intervalInputByte = new AtomicLong(0); 24 | /** 25 | * 全局所有channel累计读取的字节数 26 | */ 27 | private AtomicLong totalInputByte = new AtomicLong(0); 28 | 29 | /** 30 | * 监控间隔期内写出的字节数 31 | */ 32 | private AtomicLong intervalOutputByte = new AtomicLong(0); 33 | /** 34 | * 全局所有channel累计写出的字节数 35 | */ 36 | private AtomicLong totalOutputByte = new AtomicLong(0); 37 | 38 | /** 39 | * 激活的Channel数量 40 | */ 41 | private AtomicLong activeChannelCount = new AtomicLong(0); 42 | 43 | /** 44 | * 读取速率 45 | */ 46 | private AtomicInteger inputRateByte = new AtomicInteger(0); 47 | 48 | /** 49 | * 写出速率 50 | */ 51 | private AtomicInteger outputRateByte = new AtomicInteger(0); 52 | 53 | /** 54 | * 累计激活的Channel数量 55 | */ 56 | private AtomicLong totalActiveChannelCount = new AtomicLong(0); 57 | 58 | /** 59 | * 累计失效的Channel数量 60 | */ 61 | private AtomicLong totalInactiveChannelCount = new AtomicLong(0); 62 | 63 | /** 64 | * 池化使用的堆内存 65 | */ 66 | private AtomicLong pooledUsedHeapMemory = new AtomicLong(0); 67 | 68 | /** 69 | * 池化使用的直接内存 70 | */ 71 | private AtomicLong pooledUsedDirectMemory = new AtomicLong(0); 72 | /** 73 | * 未池化使用的堆内存 74 | */ 75 | private AtomicLong unpooledUsedHeapMemory = new AtomicLong(0); 76 | /** 77 | * 未池化使用的直接内存 78 | */ 79 | private AtomicLong unpooledUsedDirectMemory = new AtomicLong(0); 80 | 81 | /** 82 | * 直接内存总数 83 | */ 84 | private AtomicLong usedDirectMemory = new AtomicLong(0); 85 | 86 | public void addIntervalInputByte(int inputByte) { 87 | this.intervalInputByte.addAndGet(inputByte); 88 | } 89 | 90 | public void addTotalInputByte(int inputByte) { 91 | this.totalInputByte.addAndGet(inputByte); 92 | } 93 | 94 | public void addIntervalOutputByte(int outputByte) { 95 | this.intervalOutputByte.addAndGet(outputByte); 96 | } 97 | 98 | public void addTotalOutputByte(int outputByte) { 99 | this.totalOutputByte.addAndGet(outputByte); 100 | } 101 | 102 | public long activeChannelCountIncrement() { 103 | return this.activeChannelCount.incrementAndGet(); 104 | } 105 | 106 | public long activeChannelCountDecrement() { 107 | return this.activeChannelCount.decrementAndGet(); 108 | } 109 | 110 | public long totalActiveChannelCountIncrement() { 111 | return this.totalActiveChannelCount.incrementAndGet(); 112 | } 113 | 114 | public long totalInactiveChannelCountIncrement() { 115 | return this.totalInactiveChannelCount.incrementAndGet(); 116 | } 117 | 118 | public static GlobalMonitorData getInstance() { 119 | return GlobalMonitorDataHolder.INSTANCE.globalMonitorData; 120 | } 121 | 122 | 123 | private enum GlobalMonitorDataHolder { 124 | INSTANCE; 125 | private GlobalMonitorData globalMonitorData; 126 | 127 | private GlobalMonitorDataHolder() { 128 | this.globalMonitorData = new GlobalMonitorData(); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/display/LogMonitorOutService.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.display; 2 | 3 | import com.github.netty.monitor.agent.Constant; 4 | import com.github.netty.monitor.agent.LogUtils; 5 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 6 | import com.github.netty.monitor.agent.data.EventLoopMonitorData; 7 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 8 | 9 | import java.math.BigDecimal; 10 | import java.text.DateFormat; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Date; 13 | import java.util.List; 14 | 15 | /** 16 | * @Date: 2022/6/20 11:09 17 | * @Description: 日志的形式输出监控数据(没有日志则控制台打印) 18 | * @author: xzc-coder 19 | */ 20 | public class LogMonitorOutService implements MonitorOutService { 21 | 22 | private static final String CHANNEL_DATE_FORMAT = "MM-dd HH:mm:ss"; 23 | 24 | @Override 25 | public void outputMonitorData(GlobalMonitorData globalMonitorData, List eventLoopMonitorDataList, List channelMonitorDataList) { 26 | //输出全局监控数据 27 | outputGlobalMonitorData(globalMonitorData); 28 | //输出EventLoop监控数据 29 | outputEventLoopMonitorData(eventLoopMonitorDataList); 30 | //输出Channel监控数据 31 | outputChannelMonitorData(channelMonitorDataList); 32 | } 33 | 34 | 35 | private void outputChannelMonitorData(List channelMonitorDataList) { 36 | if (!channelMonitorDataList.isEmpty()) { 37 | for (ChannelMonitorData channelMonitorData : channelMonitorDataList) { 38 | final int longContentCount = 21; 39 | final int midContentCount = 16; 40 | final int shortContentCount = 14; 41 | final int tinyContent = 12; 42 | String localAddress = channelMonitorData.getLocalAddress().getAddress().getHostAddress() + ":" + channelMonitorData.getLocalAddress().getPort(); 43 | String remoteAddress = Constant.EMPTY; 44 | if (channelMonitorData.getRemoteAddress() != null) { 45 | remoteAddress = channelMonitorData.getRemoteAddress().getAddress().getHostAddress() + ":" + channelMonitorData.getRemoteAddress().getPort(); 46 | } 47 | String adaptionTotalInput = selfAdaptionValue(channelMonitorData.getTotalInputByte(), false); 48 | String adaptionTotalOutput = selfAdaptionValue(channelMonitorData.getTotalOutputByte(), false); 49 | String adaptionInputRate = selfAdaptionValue(channelMonitorData.getInputRateByte(), true); 50 | String adaptionOutputRate = selfAdaptionValue(channelMonitorData.getOutputRateByte(), true); 51 | String activeDate = toDateStr(channelMonitorData.getActiveDateTime()); 52 | String inactiveDate = toDateStr(channelMonitorData.getInactiveDateTime()); 53 | String lastInputDate = toDateStr(channelMonitorData.getLastInputDateTime()); 54 | String lastOutputDate = toDateStr(channelMonitorData.getLastOutputDateTime()); 55 | String lastEventDate = toDateStr(channelMonitorData.getLastEventTriggeredDateTime()); 56 | String lastExceptDate = toDateStr(channelMonitorData.getLastExceptionDateTime()); 57 | String lastExceptMsg = channelMonitorData.getLastExceptionMessage(); 58 | 59 | String localAddressStr = fillContent(localAddress, longContentCount); 60 | String remoteAddressStr = fillContent(remoteAddress, longContentCount); 61 | String totalInputStr = fillContent(adaptionTotalInput, tinyContent); 62 | String totalOutputStr = fillContent(adaptionTotalOutput, tinyContent); 63 | String inputRateStr = fillContent(adaptionInputRate, shortContentCount); 64 | String outputRateStr = fillContent(adaptionOutputRate, shortContentCount); 65 | String activeDateStr = fillContent(activeDate, midContentCount); 66 | String inactiveDateStr = fillContent(inactiveDate, midContentCount); 67 | String lastInputDateStr = fillContent(lastInputDate, midContentCount); 68 | String lastOutputDateStr = fillContent(lastOutputDate, midContentCount); 69 | String lastEventDateStr = fillContent(lastEventDate, midContentCount); 70 | String lastExceptDateStr = fillContent(lastExceptDate, midContentCount); 71 | String lastExceptMsgStr = fillContent(lastExceptMsg, midContentCount); 72 | 73 | LogUtils.debug(this.getClass(), "+-----------------------------------------------------------NettyChannelMonitorData----------------------------------------------------------------------------------------------------------------------------------------+"); 74 | LogUtils.debug(this.getClass(), "| LocalAddress | RemoteAddress | TotalInput | TotalOutput| InputRate | OutputRate | activeDate | inactiveDate | LastInputDate | LastOutputDate | LastEventDate | LastExceptDate | LastExceptMsg |"); 75 | LogUtils.debug(this.getClass(), "+---------------------+---------------------+------------+------------+--------------+--------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+"); 76 | LogUtils.debug(this.getClass(), String.format("|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|", localAddressStr, remoteAddressStr, 77 | totalInputStr, totalOutputStr, inputRateStr, outputRateStr, 78 | activeDateStr, inactiveDateStr, lastInputDateStr, lastOutputDateStr, 79 | lastEventDateStr, lastExceptDateStr, lastExceptMsgStr)); 80 | LogUtils.debug(this.getClass(), "+-----------------------------------------------------------NettyChannelMonitorData----------------------------------------------------------------------------------------------------------------------------------------+"); 81 | } 82 | } 83 | } 84 | 85 | 86 | private void outputEventLoopMonitorData(List eventLoopMonitorDataList) { 87 | if (!eventLoopMonitorDataList.isEmpty()) { 88 | final int firstContentCount = 26; 89 | final int otherContentCount = 16; 90 | for (EventLoopMonitorData eventLoopMonitorData : eventLoopMonitorDataList) { 91 | Integer channelCount = eventLoopMonitorData.getChannelCount(); 92 | if (channelCount == null) { 93 | channelCount = 0; 94 | } 95 | String eventLoopName = fillContent(eventLoopMonitorData.getEventLoopName(), firstContentCount); 96 | String channelCountStr = fillContent(channelCount, otherContentCount); 97 | String pendingTaskStr = fillContent(eventLoopMonitorData.getPendingTaskCount(), otherContentCount); 98 | String state = fillContent(eventLoopMonitorData.getState(), otherContentCount); 99 | LogUtils.debug(this.getClass(), "+------------------------NettyEventLoopMonitorData----------------------------+"); 100 | LogUtils.debug(this.getClass(), "| EventLoopName | ChannelCount | PendingTask | State |"); 101 | LogUtils.debug(this.getClass(), "+--------------------------+----------------+----------------+----------------+"); 102 | LogUtils.debug(this.getClass(), String.format("|%s|%s|%s|%s|", eventLoopName, channelCountStr, pendingTaskStr, state)); 103 | LogUtils.debug(this.getClass(), "+-------------------------NettyEventLoopMonitorData---------------------------+"); 104 | } 105 | } 106 | } 107 | 108 | private void outputGlobalMonitorData(GlobalMonitorData globalMonitorData) { 109 | final int longContentCount = 15; 110 | final int shortContentCount = 13; 111 | long activeChannel = globalMonitorData.getActiveChannelCount().get(); 112 | String adaptionTotalInput = selfAdaptionValue(globalMonitorData.getTotalInputByte().get(), false); 113 | String adaptionTotalOutput = selfAdaptionValue(globalMonitorData.getTotalOutputByte().get(), false); 114 | String adaptionInputRate = selfAdaptionValue(globalMonitorData.getInputRateByte().get(), true); 115 | String adaptionOutputRate = selfAdaptionValue(globalMonitorData.getOutputRateByte().get(), true); 116 | String adaptionPooledUsedHeap = selfAdaptionValue(globalMonitorData.getPooledUsedHeapMemory().get(), false, Constant.LOG_MEMORY_SCALE); 117 | String adaptionPooledUsedDirect = selfAdaptionValue(globalMonitorData.getPooledUsedDirectMemory().get(), false, Constant.LOG_MEMORY_SCALE); 118 | String adaptionUnpooledUsedHeap = selfAdaptionValue(globalMonitorData.getUnpooledUsedHeapMemory().get(), false, Constant.LOG_MEMORY_SCALE); 119 | String adaptionUnpooledUsedDirect = selfAdaptionValue(globalMonitorData.getUnpooledUsedDirectMemory().get(), false, Constant.LOG_MEMORY_SCALE); 120 | String adaptionUsedDirect = selfAdaptionValue(globalMonitorData.getUsedDirectMemory().get(), false, Constant.LOG_MEMORY_SCALE); 121 | 122 | String activeChannelStr = fillContent(activeChannel, shortContentCount); 123 | String totalInputStr = fillContent(adaptionTotalInput, shortContentCount); 124 | String totalOutputStr = fillContent(adaptionTotalOutput, shortContentCount); 125 | String inputRateStr = fillContent(adaptionInputRate, shortContentCount); 126 | String outputRateStr = fillContent(adaptionOutputRate, shortContentCount); 127 | String adaptionPooledUsedHeapStr = fillContent(adaptionPooledUsedHeap, longContentCount); 128 | String adaptionPooledUsedDirectStr = fillContent(adaptionPooledUsedDirect, longContentCount); 129 | String adaptionUnpooledUsedHeapStr = fillContent(adaptionUnpooledUsedHeap, longContentCount); 130 | String adaptionUnpooledUsedDirectStr = fillContent(adaptionUnpooledUsedDirect, longContentCount); 131 | String adaptionUsedDirectStr = fillContent(adaptionUsedDirect, shortContentCount); 132 | LogUtils.info(this.getClass(), "+-------------------------NettyGlobalMonitorData--------------------------------+"); 133 | LogUtils.info(this.getClass(), "|ActiveChannel| TotalInput | TotalOutput | InputRate | OutputRate | PoolUseHeap | PoolUseDirect | unpoolUseHeap |unpoolUseDirect| useDirect |"); 134 | LogUtils.info(this.getClass(), "+-------------+-------------+-------------+-------------+-------------+---------------+---------------+---------------+---------------+"); 135 | LogUtils.info(this.getClass(), String.format("|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|", activeChannelStr, totalInputStr, totalOutputStr, inputRateStr, outputRateStr, 136 | adaptionPooledUsedHeapStr, adaptionPooledUsedDirectStr, adaptionUnpooledUsedHeapStr, adaptionUnpooledUsedDirectStr, adaptionUsedDirectStr)); 137 | LogUtils.info(this.getClass(), "+-------------------------NettyGlobalMonitorData--------------------------------+"); 138 | } 139 | 140 | private String fillContent(Object value, int count) { 141 | String str = Constant.EMPTY; 142 | if (value != null) { 143 | str = value.toString(); 144 | } 145 | if (str.length() <= count) { 146 | int difference = count - str.length(); 147 | int padNum = difference / 2; 148 | if (difference % 2 == 0) { 149 | //偶数 150 | String padContent = repeat(Constant.SPACE.charAt(0), padNum); 151 | str = padContent + str + padContent; 152 | } else { 153 | str = repeat(Constant.SPACE.charAt(0), padNum + 1) + str + repeat(Constant.SPACE.charAt(0), padNum); 154 | } 155 | } 156 | return str; 157 | } 158 | 159 | public String selfAdaptionValue(long content, boolean isRate) { 160 | return selfAdaptionValue(content, isRate, Constant.LOG_SCALE); 161 | } 162 | 163 | public String selfAdaptionValue(long content, boolean isRate, int scale) { 164 | String unit; 165 | int scaleNumber = scale; 166 | BigDecimal bigDecimal = new BigDecimal(content); 167 | if (content / Constant.TB_CARRY >= 1) { 168 | bigDecimal = bigDecimal.divide(BigDecimal.valueOf(Constant.TB_CARRY)); 169 | unit = Constant.TB; 170 | } else if (content / Constant.GB_CARRY >= 1) { 171 | bigDecimal = bigDecimal.divide(BigDecimal.valueOf(Constant.GB_CARRY)); 172 | unit = Constant.GB; 173 | } else if (content / Constant.MB_CARRY >= 1) { 174 | bigDecimal = bigDecimal.divide(BigDecimal.valueOf(Constant.MB_CARRY)); 175 | unit = Constant.MB; 176 | } else if (content / Constant.KB_CARRY >= 1) { 177 | bigDecimal = bigDecimal.divide(BigDecimal.valueOf(Constant.KB_CARRY)); 178 | unit = Constant.KB; 179 | } else { 180 | unit = Constant.B; 181 | scaleNumber = Constant.LOG_SCALE_B; 182 | } 183 | String value = bigDecimal.setScale(scaleNumber, BigDecimal.ROUND_DOWN).toString() + unit; 184 | if (isRate) { 185 | value = value + Constant.SPACE + Constant.RATE; 186 | } 187 | return value; 188 | } 189 | 190 | private String repeat(char c, int count) { 191 | if (count <= 0) { 192 | return Constant.EMPTY; 193 | } 194 | 195 | char[] result = new char[count]; 196 | for (int i = 0; i < count; i++) { 197 | result[i] = c; 198 | } 199 | return new String(result); 200 | } 201 | 202 | 203 | private String toDateStr(Date date) { 204 | String result = Constant.EMPTY; 205 | if (date != null) { 206 | DateFormat dateFormat = new SimpleDateFormat(CHANNEL_DATE_FORMAT); 207 | result = dateFormat.format(date); 208 | } 209 | return result; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/display/MonitorOutService.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.display; 2 | 3 | import com.github.netty.monitor.agent.Constant; 4 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 5 | import com.github.netty.monitor.agent.data.EventLoopMonitorData; 6 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 7 | 8 | import java.lang.reflect.Constructor; 9 | import java.util.List; 10 | 11 | public interface MonitorOutService { 12 | 13 | 14 | void outputMonitorData(GlobalMonitorData globalMonitorData, List eventLoopMonitorDataList, List channelMonitorDataList); 15 | 16 | /** 17 | * 获取监控数据输出实现类 18 | * @return 监控数据输出实现类 19 | * @throws Exception 20 | */ 21 | static MonitorOutService getInstance() throws Exception { 22 | MonitorOutService monitorOutService; 23 | String monitorOutServiceClass = System.getProperty(Constant.MONITOR_OUT_SERVICE_CLASS); 24 | if(monitorOutServiceClass == null || monitorOutServiceClass.length() == 0) { 25 | monitorOutService = new LogMonitorOutService(); 26 | }else { 27 | Class monitorOutServiceClazz = Class.forName(monitorOutServiceClass); 28 | if(MonitorOutService.class.isAssignableFrom(monitorOutServiceClazz)) { 29 | Constructor constructor = monitorOutServiceClazz.getConstructor(); 30 | monitorOutService = (MonitorOutService) constructor.newInstance(); 31 | }else { 32 | throw new IllegalArgumentException(monitorOutServiceClass + "不是 com.xy.agent.display.MonitorOutService 实现类"); 33 | } 34 | } 35 | return monitorOutService; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/monitor/BootstrapMonitor.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.monitor; 2 | 3 | import com.github.netty.monitor.agent.Constant; 4 | import com.github.netty.monitor.agent.NettyElementMatcher; 5 | import com.github.netty.monitor.agent.NettyMonitor; 6 | import com.github.netty.monitor.agent.NettyMonitorCache; 7 | import com.github.netty.monitor.agent.display.MonitorOutService; 8 | import com.github.netty.monitor.agent.task.GlobalIntervalStatisticsTask; 9 | import com.github.netty.monitor.agent.task.MonitorDataIntervalTask; 10 | import io.netty.bootstrap.Bootstrap; 11 | import io.netty.bootstrap.ServerBootstrap; 12 | import io.netty.channel.EventLoopGroup; 13 | 14 | import java.lang.reflect.Method; 15 | import java.util.Map; 16 | import java.util.concurrent.*; 17 | import java.util.concurrent.atomic.AtomicInteger; 18 | 19 | /** 20 | * @Date: 2022/6/19 18:22 21 | * @Description: 全局的监控数据 22 | * @author: xzc-coder 23 | */ 24 | public class BootstrapMonitor implements NettyMonitor { 25 | 26 | private static ScheduledExecutorService scheduledExecutorService; 27 | 28 | @Override 29 | public void afterIntercept(Object target, Method method, Object[] args, Object result, Map contextMap) throws Exception { 30 | String methodName = method.getName(); 31 | if(NettyElementMatcher.METHOD_CONNECT.equals(methodName)) { 32 | //客户端的启动监控 connect方法被调用时 33 | if(target instanceof Bootstrap) { 34 | enableEventLoopStatisticsScheduled(); 35 | Bootstrap bootstrap = (Bootstrap) target; 36 | EventLoopGroup eventLoopGroup = bootstrap.config().group(); 37 | NettyMonitorCache.addEventLoopGroup(eventLoopGroup); 38 | } 39 | }else if(NettyElementMatcher.METHOD_BIND.equals(methodName)) { 40 | //服务器的启动监控 bind方法被调用时 41 | if(target instanceof ServerBootstrap) { 42 | enableEventLoopStatisticsScheduled(); 43 | ServerBootstrap serverBootstrap = (ServerBootstrap) target; 44 | EventLoopGroup eventLoopGroup = serverBootstrap.config().group(); 45 | NettyMonitorCache.addEventLoopGroup(eventLoopGroup); 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * 开启EventLoop的定时任务监控 52 | * @throws Exception 53 | */ 54 | private static synchronized void enableEventLoopStatisticsScheduled() throws Exception { 55 | if(scheduledExecutorService == null) { 56 | scheduledExecutorService = Executors.newScheduledThreadPool(1, new ThreadFactory() { 57 | private final AtomicInteger threadNumber = new AtomicInteger(1); 58 | @Override 59 | public Thread newThread(Runnable r) { 60 | String name = "pool-thread-" + threadNumber.getAndIncrement(); 61 | Thread thread = new Thread(r); 62 | thread.setName(name); 63 | thread.setDaemon(true); 64 | return thread; 65 | } 66 | }); 67 | scheduledExecutorService.schedule(new GlobalIntervalStatisticsTask(scheduledExecutorService), Constant.STATISTICS_INTERVAL_SECOND,TimeUnit.SECONDS); 68 | scheduledExecutorService.schedule(new MonitorDataIntervalTask(scheduledExecutorService, MonitorOutService.getInstance()), Constant.STATISTICS_INTERVAL_SECOND,TimeUnit.SECONDS); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/monitor/ChannelStateMonitor.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.monitor; 2 | 3 | import com.github.netty.monitor.agent.Constant; 4 | import com.github.netty.monitor.agent.NettyElementMatcher; 5 | import com.github.netty.monitor.agent.NettyMonitor; 6 | import com.github.netty.monitor.agent.NettyMonitorCache; 7 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 8 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 9 | import com.github.netty.monitor.agent.task.ChannelIntervalStatisticsTask; 10 | import io.netty.channel.Channel; 11 | import io.netty.channel.ChannelHandlerContext; 12 | import io.netty.channel.EventLoop; 13 | import io.netty.channel.socket.ServerSocketChannel; 14 | import io.netty.util.concurrent.SingleThreadEventExecutor; 15 | 16 | import java.lang.reflect.Method; 17 | import java.util.Date; 18 | import java.util.Map; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | 22 | /** 23 | * @Date: 2022/6/19 19:52 24 | * @Description: Channel数据监控 25 | * @author: xzc-coder 26 | */ 27 | public class ChannelStateMonitor implements NettyMonitor { 28 | 29 | @Override 30 | public void beforeIntercept(Object target, Method method, Object[] args, Map contextMap) { 31 | String methodName = method.getName(); 32 | if (NettyElementMatcher.METHOD_CHANNEL_ACTIVE.equals(methodName)) { 33 | channelActive((ChannelHandlerContext) args[0]); 34 | } else if(NettyElementMatcher.METHOD_CHANNEL_INACTIVE.equals(methodName)) { 35 | channelInactive((ChannelHandlerContext) args[0]); 36 | } 37 | } 38 | 39 | private void channelActive(ChannelHandlerContext ctx) { 40 | Channel channel = ctx.channel(); 41 | //不算监听端口的Channel 42 | if(channel instanceof ServerSocketChannel) { 43 | return; 44 | } 45 | GlobalMonitorData globalMonitorData = GlobalMonitorData.getInstance(); 46 | globalMonitorData.activeChannelCountIncrement(); 47 | globalMonitorData.totalActiveChannelCountIncrement(); 48 | ChannelMonitorData channelMonitorData = NettyMonitorCache.getNettyMonitorData(channel); 49 | channelMonitorData.setActiveDateTime(new Date()); 50 | EventLoop eventLoop = channel.eventLoop(); 51 | if(eventLoop instanceof SingleThreadEventExecutor) { 52 | SingleThreadEventExecutor singleThreadEventExecutor = (SingleThreadEventExecutor) eventLoop; 53 | long threadId = singleThreadEventExecutor.threadProperties().id(); 54 | NettyMonitorCache.channelCountIncrement(threadId); 55 | } 56 | eventLoop.schedule(new ChannelIntervalStatisticsTask(channel), Constant.STATISTICS_INTERVAL_SECOND,TimeUnit.SECONDS); 57 | } 58 | 59 | private void channelInactive(ChannelHandlerContext ctx) { 60 | Channel channel = ctx.channel(); 61 | if(channel instanceof ServerSocketChannel) { 62 | return; 63 | } 64 | GlobalMonitorData globalMonitorData = GlobalMonitorData.getInstance(); 65 | globalMonitorData.activeChannelCountDecrement(); 66 | globalMonitorData.totalInactiveChannelCountIncrement(); 67 | ChannelMonitorData channelMonitorData = NettyMonitorCache.getNettyMonitorData(channel); 68 | channelMonitorData.setInactiveDateTime(new Date()); 69 | EventLoop eventLoop = channel.eventLoop(); 70 | if(eventLoop instanceof SingleThreadEventExecutor) { 71 | SingleThreadEventExecutor singleThreadEventExecutor = (SingleThreadEventExecutor) eventLoop; 72 | long threadId = singleThreadEventExecutor.threadProperties().id(); 73 | NettyMonitorCache.channelCountDecrement(threadId); 74 | } 75 | if(Constant.CHANNEL_MONITOR_REMOVE_DELAY_SECOND > 0) { 76 | channel.eventLoop().schedule(() -> NettyMonitorCache.removeNettyMonitorData(channel), Constant.CHANNEL_MONITOR_REMOVE_DELAY_SECOND, TimeUnit.SECONDS); 77 | }else { 78 | NettyMonitorCache.removeNettyMonitorData(channel); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/monitor/InputDataMonitor.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.monitor; 2 | 3 | import com.github.netty.monitor.agent.NettyElementMatcher; 4 | import com.github.netty.monitor.agent.NettyMonitor; 5 | import com.github.netty.monitor.agent.NettyMonitorCache; 6 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 7 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 8 | import io.netty.buffer.ByteBuf; 9 | import io.netty.channel.Channel; 10 | import io.netty.channel.ChannelHandlerContext; 11 | import io.netty.channel.socket.DatagramPacket; 12 | 13 | import java.lang.reflect.Method; 14 | import java.util.Date; 15 | import java.util.Map; 16 | 17 | 18 | /** 19 | * @Date: 2022/6/19 21:43 20 | * @Description: 输入的数据监控 21 | * @author: xzc-coder 22 | */ 23 | public class InputDataMonitor implements NettyMonitor { 24 | 25 | @Override 26 | public void beforeIntercept(Object target, Method method, Object[] args, Map contextMap) { 27 | String methodName = method.getName(); 28 | if(NettyElementMatcher.METHOD_CHANNEL_READ.equals(methodName)) { 29 | channelRead((ChannelHandlerContext) args[0],args[1]); 30 | } else if(NettyElementMatcher.METHOD_EXCEPTION_CAUGHT.equals(methodName)) { 31 | exceptionCaught((ChannelHandlerContext)args[0],(Throwable)args[1]); 32 | } else if(NettyElementMatcher.METHOD_USER_EVENT_TRIGGERED.equals(methodName)) { 33 | userEventTriggered((ChannelHandlerContext) args[0],args[1]); 34 | } 35 | } 36 | 37 | private void channelRead(ChannelHandlerContext ctx, Object msg) { 38 | if(msg instanceof ByteBuf) { 39 | ByteBuf byteBuf = (ByteBuf) msg; 40 | Channel channel = ctx.channel(); 41 | int readableBytes = byteBuf.readableBytes(); 42 | readStatistics(channel,readableBytes); 43 | }else if(msg instanceof DatagramPacket) { 44 | DatagramPacket datagramPacket = (DatagramPacket) msg; 45 | Channel channel = ctx.channel(); 46 | ByteBuf byteBuf = datagramPacket.content(); 47 | int readableBytes = byteBuf.readableBytes(); 48 | readStatistics(channel,readableBytes); 49 | } 50 | } 51 | 52 | private void readStatistics(Channel channel,int readableBytes) { 53 | ChannelMonitorData channelMonitorData = NettyMonitorCache.getNettyMonitorData(channel); 54 | channelMonitorData.addIntervalInputByte(readableBytes); 55 | channelMonitorData.addTotalInputByte(readableBytes); 56 | channelMonitorData.setLastInputDateTime(new Date()); 57 | GlobalMonitorData globalMonitorData = GlobalMonitorData.getInstance(); 58 | globalMonitorData.addTotalInputByte(readableBytes); 59 | globalMonitorData.addIntervalInputByte(readableBytes); 60 | } 61 | 62 | 63 | private void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 64 | Channel channel = ctx.channel(); 65 | ChannelMonitorData channelMonitorData = NettyMonitorCache.getNettyMonitorData(channel); 66 | channelMonitorData.setLastExceptionDateTime(new Date()); 67 | channelMonitorData.setLastExceptionMessage(cause.getMessage()); 68 | } 69 | 70 | private void userEventTriggered(ChannelHandlerContext ctx, Object evt) { 71 | Channel channel = ctx.channel(); 72 | ChannelMonitorData channelMonitorData = NettyMonitorCache.getNettyMonitorData(channel); 73 | channelMonitorData.setLastEventTriggeredDateTime(new Date()); 74 | channelMonitorData.setEventTriggeredCount(channelMonitorData.getEventTriggeredCount() + 1); 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/monitor/OutputDataMonitor.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.monitor; 2 | 3 | import com.github.netty.monitor.agent.NettyElementMatcher; 4 | import com.github.netty.monitor.agent.NettyMonitor; 5 | import com.github.netty.monitor.agent.NettyMonitorCache; 6 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 7 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 8 | import io.netty.buffer.ByteBuf; 9 | import io.netty.channel.Channel; 10 | import io.netty.channel.ChannelHandlerContext; 11 | import io.netty.channel.ChannelPromise; 12 | import io.netty.channel.socket.DatagramPacket; 13 | 14 | import java.lang.reflect.Method; 15 | import java.util.Date; 16 | import java.util.Map; 17 | 18 | 19 | /** 20 | * @Date: 2022/6/19 23:34 21 | * @Description: 输出的数据监控 22 | * @author: xzc-coder 23 | */ 24 | public class OutputDataMonitor implements NettyMonitor { 25 | 26 | 27 | @Override 28 | public void beforeIntercept(Object target, Method method, Object[] args, Map contextMap) { 29 | String methodName = method.getName(); 30 | if(NettyElementMatcher.METHOD_WRITE.equals(methodName)) { 31 | write((ChannelHandlerContext) args[0],args[1], (ChannelPromise) args[2]); 32 | } 33 | } 34 | 35 | private void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise){ 36 | if(msg instanceof ByteBuf) { 37 | ByteBuf byteBuf = (ByteBuf) msg; 38 | promise.addListener((future -> { 39 | int writableBytes = byteBuf.writableBytes(); 40 | Channel channel = ctx.channel(); 41 | writeStatistics(channel,writableBytes); 42 | })); 43 | }else if(msg instanceof DatagramPacket) { 44 | DatagramPacket datagramPacket = (DatagramPacket) msg; 45 | ByteBuf byteBuf = datagramPacket.content(); 46 | promise.addListener((future -> { 47 | int writableBytes = byteBuf.writableBytes(); 48 | Channel channel = ctx.channel(); 49 | writeStatistics(channel,writableBytes); 50 | })); 51 | 52 | } 53 | } 54 | 55 | private void writeStatistics(Channel channel,int writableBytes) { 56 | ChannelMonitorData channelMonitorData = NettyMonitorCache.getNettyMonitorData(channel); 57 | channelMonitorData.addTotalOutputByte(writableBytes); 58 | channelMonitorData.addIntervalOutputByte(writableBytes); 59 | channelMonitorData.setLastOutputDateTime(new Date()); 60 | GlobalMonitorData globalMonitorData = GlobalMonitorData.getInstance(); 61 | globalMonitorData.addTotalOutputByte(writableBytes); 62 | globalMonitorData.addIntervalOutputByte(writableBytes); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/task/ChannelIntervalStatisticsTask.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.task; 2 | 3 | import com.github.netty.monitor.agent.Constant; 4 | import com.github.netty.monitor.agent.NettyMonitorCache; 5 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 6 | import io.netty.channel.Channel; 7 | import io.netty.channel.EventLoop; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @Date: 2022/6/12 16:35 13 | * @Description: 常量类 14 | * @author: xzc-coder 15 | */ 16 | public class ChannelIntervalStatisticsTask implements Runnable { 17 | 18 | private Channel channel; 19 | 20 | public ChannelIntervalStatisticsTask(Channel channel) { 21 | this.channel = channel; 22 | } 23 | 24 | @Override 25 | public void run() { 26 | if(channel.isActive()) { 27 | EventLoop eventLoop = channel.eventLoop(); 28 | ChannelMonitorData channelMonitorData = NettyMonitorCache.getNettyMonitorData(channel); 29 | long intervalInputByte = channelMonitorData.getIntervalInputByte(); 30 | int inputRateByte = (int) (intervalInputByte / Constant.STATISTICS_INTERVAL_SECOND); 31 | channelMonitorData.setInputRateByte(inputRateByte); 32 | channelMonitorData.setIntervalInputByte(0); 33 | long intervalOutputByte = channelMonitorData.getIntervalOutputByte(); 34 | int outputRateByte = (int) (intervalOutputByte / Constant.STATISTICS_INTERVAL_SECOND); 35 | channelMonitorData.setOutputRateByte(outputRateByte); 36 | channelMonitorData.setIntervalOutputByte(0); 37 | String localAddress = channelMonitorData.getLocalAddress().toString(); 38 | String remoteAddress = ""; 39 | if(channelMonitorData.getRemoteAddress() != null) { 40 | remoteAddress = channelMonitorData.getRemoteAddress().toString(); 41 | } 42 | // System.out.println(String.format("通道输入信息,Channel:%s,local:%s,remote:%s,速率:%d/b,累计读取字节数:%d", channelMonitorData.getChannelId(), 43 | // localAddress, remoteAddress, inputRateByte, channelMonitorData.getAccumulateInputByte())); 44 | // System.out.println(String.format("通道输入出信息,Channel:%s,local:%s,remote:%s,速率:%d/b,累计输出字节数:%d", channelMonitorData.getChannelId(), 45 | // localAddress, remoteAddress, inputRateByte, channelMonitorData.getAccumulateOutputByte())); 46 | eventLoop.schedule(this,Constant.STATISTICS_INTERVAL_SECOND, TimeUnit.SECONDS); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/task/GlobalIntervalStatisticsTask.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.task; 2 | 3 | import com.github.netty.monitor.agent.Constant; 4 | import com.github.netty.monitor.agent.LogUtils; 5 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 6 | import io.netty.buffer.ByteBufAllocatorMetric; 7 | import io.netty.buffer.PooledByteBufAllocator; 8 | import io.netty.buffer.PooledByteBufAllocatorMetric; 9 | import io.netty.buffer.UnpooledByteBufAllocator; 10 | import io.netty.util.internal.PlatformDependent; 11 | 12 | import java.lang.reflect.Field; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | import java.util.concurrent.TimeUnit; 15 | import java.util.concurrent.atomic.AtomicLong; 16 | 17 | /** 18 | * @Date: 2022/6/12 13:21 19 | * @Description: 常量类 20 | * @author: xzc-coder 21 | */ 22 | public class GlobalIntervalStatisticsTask implements Runnable { 23 | 24 | private ScheduledExecutorService scheduledExecutorService; 25 | 26 | public GlobalIntervalStatisticsTask(ScheduledExecutorService scheduledExecutorService) { 27 | this.scheduledExecutorService = scheduledExecutorService; 28 | } 29 | 30 | 31 | @Override 32 | public void run() { 33 | try { 34 | GlobalMonitorData globalMonitorData = GlobalMonitorData.getInstance(); 35 | int globalInputRateByte = calculateInputRateByte(globalMonitorData); 36 | globalMonitorData.getInputRateByte().set(globalInputRateByte); 37 | globalMonitorData.getIntervalInputByte().set(0); 38 | int globalOutputRateByte = calculateOutputRateByte(globalMonitorData); 39 | globalMonitorData.getOutputRateByte().set(globalOutputRateByte); 40 | globalMonitorData.getIntervalOutputByte().set(0); 41 | //池化和非池化的低版本没有该类,低版本需要通过反射去获取(4.0.45.Final及之后的版本有) 42 | PooledByteBufAllocatorMetric pooledByteBufAllocatorMetric = PooledByteBufAllocator.DEFAULT.metric(); 43 | globalMonitorData.getPooledUsedDirectMemory().set(pooledByteBufAllocatorMetric.usedDirectMemory()); 44 | globalMonitorData.getPooledUsedHeapMemory().set(pooledByteBufAllocatorMetric.usedHeapMemory()); 45 | ByteBufAllocatorMetric unpooledByteBufAllocatorMetric = UnpooledByteBufAllocator.DEFAULT.metric(); 46 | globalMonitorData.getUnpooledUsedDirectMemory().set(unpooledByteBufAllocatorMetric.usedDirectMemory()); 47 | globalMonitorData.getUnpooledUsedHeapMemory().set(unpooledByteBufAllocatorMetric.usedHeapMemory()); 48 | Field field = PlatformDependent.class.getDeclaredField("DIRECT_MEMORY_COUNTER"); 49 | field.setAccessible(true); 50 | AtomicLong directMemoryCounter = (AtomicLong) field.get(PlatformDependent.class); 51 | globalMonitorData.setUsedDirectMemory(directMemoryCounter); 52 | }catch (Exception e) { 53 | LogUtils.error(this.getClass(),"全局数据统计任务异常",e); 54 | }finally { 55 | this.scheduledExecutorService.schedule(this, Constant.STATISTICS_INTERVAL_SECOND, TimeUnit.SECONDS); 56 | } 57 | } 58 | 59 | private int calculateInputRateByte(GlobalMonitorData globalMonitorData) { 60 | return (int) (globalMonitorData.getIntervalInputByte().get() / Constant.STATISTICS_INTERVAL_SECOND); 61 | } 62 | 63 | private int calculateOutputRateByte(GlobalMonitorData globalMonitorData) { 64 | return (int) (globalMonitorData.getIntervalOutputByte().get() / Constant.STATISTICS_INTERVAL_SECOND); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/github/netty/monitor/agent/task/MonitorDataIntervalTask.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.monitor.agent.task; 2 | 3 | import com.github.netty.monitor.agent.Constant; 4 | import com.github.netty.monitor.agent.LogUtils; 5 | import com.github.netty.monitor.agent.NettyMonitorCache; 6 | import com.github.netty.monitor.agent.data.ChannelMonitorData; 7 | import com.github.netty.monitor.agent.data.EventLoopMonitorData; 8 | import com.github.netty.monitor.agent.data.GlobalMonitorData; 9 | import com.github.netty.monitor.agent.display.MonitorOutService; 10 | import io.netty.channel.EventLoopGroup; 11 | import io.netty.util.concurrent.EventExecutor; 12 | import io.netty.util.concurrent.SingleThreadEventExecutor; 13 | import io.netty.util.concurrent.ThreadProperties; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Iterator; 17 | import java.util.List; 18 | import java.util.concurrent.*; 19 | 20 | /** 21 | * @Date: 2022/6/12 10:16 22 | * @Description: 总监控数据的定时任务 23 | * @author: xzc-coder 24 | */ 25 | public class MonitorDataIntervalTask implements Runnable { 26 | 27 | private ScheduledExecutorService scheduledExecutorService; 28 | private MonitorOutService monitorDataDisplayService; 29 | 30 | public MonitorDataIntervalTask(ScheduledExecutorService scheduledExecutorService, MonitorOutService monitorDataDisplayService) { 31 | this.scheduledExecutorService = scheduledExecutorService; 32 | this.monitorDataDisplayService = monitorDataDisplayService; 33 | } 34 | 35 | @Override 36 | public void run() { 37 | try { 38 | //eventLoop的监控 39 | List eventLoopMonitorDataList = eventLoopStatistics(); 40 | //全局数据监控 41 | GlobalMonitorData globalMonitorData = NettyMonitorCache.getGlobalMonitorData(); 42 | //channel数据监控 43 | List channelMonitorDataList = NettyMonitorCache.getChannelMonitorDataList(); 44 | //数据展示,默认就日志打印出来 45 | monitorDataDisplayService.outputMonitorData(globalMonitorData,eventLoopMonitorDataList,channelMonitorDataList); 46 | } catch (Exception e) { 47 | LogUtils.error(this.getClass(),"间隔数据统计任务异常",e); 48 | }finally { 49 | scheduledExecutorService.schedule(this, Constant.STATISTICS_INTERVAL_SECOND, TimeUnit.SECONDS); 50 | } 51 | } 52 | 53 | 54 | private List eventLoopStatistics() throws ExecutionException, InterruptedException { 55 | List result = new ArrayList<>(); 56 | Iterator iterator = NettyMonitorCache.getEventLoopGroupSet().iterator(); 57 | while (iterator.hasNext()) { 58 | EventLoopGroup eventLoopGroup = iterator.next(); 59 | if(eventLoopGroup.isShutdown() || eventLoopGroup.isShuttingDown()) { 60 | iterator.remove(); 61 | } 62 | for(EventExecutor eventExecutor : eventLoopGroup) { 63 | if(eventExecutor instanceof SingleThreadEventExecutor) { 64 | SingleThreadEventExecutor singleThreadEventExecutor = (SingleThreadEventExecutor) eventExecutor; 65 | ThreadProperties threadProperties = singleThreadEventExecutor.threadProperties(); 66 | long threadId = threadProperties.id(); 67 | if(singleThreadEventExecutor.isShutdown() || singleThreadEventExecutor.isShuttingDown()) { 68 | NettyMonitorCache.removeChannelCount(threadId); 69 | }else { 70 | EventLoopMonitorData eventLoopMonitorData = new EventLoopMonitorData(threadProperties.name()); 71 | int pendingTasks = singleThreadEventExecutor.pendingTasks(); 72 | eventLoopMonitorData.setPendingTaskCount(pendingTasks); 73 | Thread.State state = threadProperties.state(); 74 | eventLoopMonitorData.setState(state); 75 | Integer channelCount = NettyMonitorCache.getChannelCount(threadId); 76 | eventLoopMonitorData.setChannelCount(channelCount); 77 | result.add(eventLoopMonitorData); 78 | } 79 | } 80 | } 81 | } 82 | return result; 83 | } 84 | } 85 | --------------------------------------------------------------------------------