├── Procfile ├── README.md ├── monitor-spring ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── runcoding │ │ │ └── monitor │ │ │ ├── config │ │ │ ├── MonitorConfiguration.java │ │ │ ├── SentinelWebConfig.java │ │ │ └── sqlite │ │ │ │ ├── DBInitializeConfig.java │ │ │ │ └── MonitorDataSourceConfiguration.java │ │ │ ├── dto │ │ │ ├── Resp.java │ │ │ └── RespStatus.java │ │ │ ├── exception │ │ │ ├── BusinessException.java │ │ │ └── SentinelBlockException.java │ │ │ ├── support │ │ │ ├── jvm │ │ │ │ └── JvmProcessor.java │ │ │ ├── metric │ │ │ │ ├── MetricProcessor.java │ │ │ │ └── SentinelMetricProcessor.java │ │ │ ├── monitor │ │ │ │ ├── MonitorProcessor.java │ │ │ │ ├── function │ │ │ │ │ ├── MonitorDegradeRuleFunction.java │ │ │ │ │ ├── MonitorFlowRuleFunction.java │ │ │ │ │ └── MonitorSendFunction.java │ │ │ │ └── sentinel │ │ │ │ │ ├── SentinelProcessor.java │ │ │ │ │ └── SentinelRuleProcessor.java │ │ │ ├── server │ │ │ │ ├── DynamicServerProcessor.java │ │ │ │ └── MonitorSessionService.java │ │ │ └── webhook │ │ │ │ └── dingtalk │ │ │ │ ├── DTWebHookProcessor.java │ │ │ │ └── model │ │ │ │ ├── DTAt.java │ │ │ │ ├── DTMessage.java │ │ │ │ ├── DTMessageType.java │ │ │ │ ├── DTResult.java │ │ │ │ └── content │ │ │ │ ├── DTActionCardContent.java │ │ │ │ ├── DTLinkContent.java │ │ │ │ ├── DTMarkdownContent.java │ │ │ │ └── DTTextContent.java │ │ │ └── web │ │ │ ├── controller │ │ │ └── monitor │ │ │ │ ├── MonitorController.java │ │ │ │ ├── MonitorRegistrationController.java │ │ │ │ └── MonitorSentinelController.java │ │ │ ├── dao │ │ │ ├── MetricInfoMapper.java │ │ │ └── MetricInfoMapper.xml │ │ │ ├── job │ │ │ └── MonitorJob.java │ │ │ ├── model │ │ │ ├── MonitorConstants.java │ │ │ ├── container │ │ │ │ ├── ContainerCpuInfo.java │ │ │ │ ├── ContainerMethodMonitorInfo.java │ │ │ │ ├── ContainerRunningInfo.java │ │ │ │ ├── ContainerThreadInfo.java │ │ │ │ ├── GarbageCollectorInfo.java │ │ │ │ ├── GroupThreadInfo.java │ │ │ │ ├── MemoryInfo.java │ │ │ │ └── OperatingSystemInfo.java │ │ │ ├── metrics │ │ │ │ └── MethodMetricInfo.java │ │ │ ├── monitor │ │ │ │ └── MonitorUserInfo.java │ │ │ └── sentinel │ │ │ │ └── SentinelRuleInfo.java │ │ │ └── utils │ │ │ ├── ErrorMessageUtils.java │ │ │ ├── IPAddressAnalysor.java │ │ │ ├── IpUtils.java │ │ │ ├── MethodUtils.java │ │ │ ├── PasswordEncoder.java │ │ │ ├── RestTemplateUtils.java │ │ │ ├── ThreadUtil.java │ │ │ └── date │ │ │ ├── DatePattern.java │ │ │ └── LocalDateUtil.java │ └── resources │ │ ├── META-INF │ │ ├── resources │ │ │ ├── dist │ │ │ │ ├── base64.min.js │ │ │ │ ├── clipboard │ │ │ │ │ └── clipboard.js │ │ │ │ ├── css │ │ │ │ │ └── login.css │ │ │ │ ├── docsify │ │ │ │ │ ├── docsify.js │ │ │ │ │ ├── plugins │ │ │ │ │ │ ├── codesponsor.js │ │ │ │ │ │ ├── codesponsor.min.js │ │ │ │ │ │ ├── disqus.js │ │ │ │ │ │ ├── disqus.min.js │ │ │ │ │ │ ├── emoji.js │ │ │ │ │ │ ├── emoji.min.js │ │ │ │ │ │ ├── external-script.js │ │ │ │ │ │ ├── external-script.min.js │ │ │ │ │ │ ├── front-matter.js │ │ │ │ │ │ ├── front-matter.min.js │ │ │ │ │ │ ├── ga.js │ │ │ │ │ │ ├── ga.min.js │ │ │ │ │ │ ├── gitalk.js │ │ │ │ │ │ ├── gitalk.min.js │ │ │ │ │ │ ├── google-analytics.js │ │ │ │ │ │ ├── prismjs │ │ │ │ │ │ │ └── components │ │ │ │ │ │ │ │ ├── prism-asciidoc.min.js │ │ │ │ │ │ │ │ ├── prism-autohotkey.min.js │ │ │ │ │ │ │ │ ├── prism-bash.min.js │ │ │ │ │ │ │ │ ├── prism-basic.min.js │ │ │ │ │ │ │ │ ├── prism-c.min.js │ │ │ │ │ │ │ │ ├── prism-coffeescript.min.js │ │ │ │ │ │ │ │ ├── prism-core.min.js │ │ │ │ │ │ │ │ ├── prism-css.min.js │ │ │ │ │ │ │ │ ├── prism-django.min.js │ │ │ │ │ │ │ │ ├── prism-docker.min.js │ │ │ │ │ │ │ │ ├── prism-erlang.min.js │ │ │ │ │ │ │ │ ├── prism-go.min.js │ │ │ │ │ │ │ │ ├── prism-groovy.min.js │ │ │ │ │ │ │ │ ├── prism-http.min.js │ │ │ │ │ │ │ │ ├── prism-icon.min.js │ │ │ │ │ │ │ │ ├── prism-java.min.js │ │ │ │ │ │ │ │ ├── prism-javascript.min.js │ │ │ │ │ │ │ │ ├── prism-json.min.js │ │ │ │ │ │ │ │ ├── prism-jsx.min.js │ │ │ │ │ │ │ │ ├── prism-keyman.min.js │ │ │ │ │ │ │ │ ├── prism-kotlin.min.js │ │ │ │ │ │ │ │ ├── prism-less.min.js │ │ │ │ │ │ │ │ ├── prism-makefile.min.js │ │ │ │ │ │ │ │ ├── prism-markdown.min.js │ │ │ │ │ │ │ │ ├── prism-nginx.min.js │ │ │ │ │ │ │ │ ├── prism-objectivec.min.js │ │ │ │ │ │ │ │ ├── prism-php.min.js │ │ │ │ │ │ │ │ ├── prism-ruby.min.js │ │ │ │ │ │ │ │ ├── prism-scala.min.js │ │ │ │ │ │ │ │ ├── prism-sql.min.js │ │ │ │ │ │ │ │ ├── prism-stylus.min.js │ │ │ │ │ │ │ │ ├── prism-swift.min.js │ │ │ │ │ │ │ │ ├── prism-vim.min.js │ │ │ │ │ │ │ │ ├── prism-wiki.min.js │ │ │ │ │ │ │ │ └── prism-yaml.min.js │ │ │ │ │ │ ├── search.js │ │ │ │ │ │ ├── search.min.js │ │ │ │ │ │ ├── zoom-image.js │ │ │ │ │ │ └── zoom-image.min.js │ │ │ │ │ ├── sw.js │ │ │ │ │ └── themes │ │ │ │ │ │ ├── buble.css │ │ │ │ │ │ └── vue.css │ │ │ │ ├── fetch │ │ │ │ │ └── fetch.js │ │ │ │ ├── json │ │ │ │ │ ├── json-to-table.js │ │ │ │ │ ├── json.html │ │ │ │ │ ├── jsoneditor.min.css │ │ │ │ │ └── jsoneditor.min.js │ │ │ │ ├── mermaid │ │ │ │ │ └── mermaid.js │ │ │ │ ├── mo │ │ │ │ │ └── mo.min.js │ │ │ │ ├── nest │ │ │ │ │ └── canvas-nest.js │ │ │ │ ├── system │ │ │ │ │ └── system.js │ │ │ │ ├── wiki.css │ │ │ │ └── zepto │ │ │ │ │ └── zepto.min.js │ │ │ └── monitor │ │ │ │ ├── index.html │ │ │ │ ├── login.html │ │ │ │ ├── view │ │ │ │ ├── current │ │ │ │ │ ├── cpu.html │ │ │ │ │ ├── cpu.js │ │ │ │ │ ├── current.html │ │ │ │ │ └── current.js │ │ │ │ ├── metric │ │ │ │ │ ├── metric.html │ │ │ │ │ └── metric.js │ │ │ │ ├── refresh │ │ │ │ │ ├── refresh.html │ │ │ │ │ └── refresh.js │ │ │ │ └── sentinel │ │ │ │ │ ├── sentinel.html │ │ │ │ │ └── sentinel.js │ │ │ │ └── wiki │ │ │ │ ├── 404.md │ │ │ │ ├── home.md │ │ │ │ ├── monitor │ │ │ │ └── sentinel_config.md │ │ │ │ └── sidebar.md │ │ └── spring.factories │ │ ├── monitor_config.properties │ │ └── store │ │ └── schema.sql │ └── test │ ├── java │ └── com │ │ └── runcoding │ │ └── monitor │ │ ├── ExceptionResolver.java │ │ ├── MonitorApplication.java │ │ ├── e2etest │ │ ├── MockHttpClient.java │ │ └── SysLoadTest.java │ │ ├── sentinel │ │ ├── SentinelAuthorityTest.java │ │ ├── SentinelDegradeTest.java │ │ ├── SentinelSystemTest.java │ │ └── flow │ │ │ ├── SentinelFlowQpsTest.java │ │ │ └── SentinelFlowThreadTest.java │ │ └── test │ │ ├── LoadAverage.java │ │ ├── MonitorTest.java │ │ ├── MonitorTestController.java │ │ ├── MonitorTestService.java │ │ └── TreadTest.java │ └── resources │ ├── application.yml │ └── bootstrap.yml └── pom.xml /Procfile: -------------------------------------------------------------------------------- 1 | web: java -jar -Dserver.port=$PORT monitor-spring/target/monitor-spring-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 轻量级流量控制 2 | 基于[Sentinel](https://github.com/alibaba/Sentinel/wiki)实现服务手自一体限流。 3 | 4 | ### 使用效果图 5 | 6 | [演示视频](https://raw.githubusercontent.com/runcoding/static/master/wiki/pic/monitor.mov) 7 | 8 | ### 如何使用 9 | 步骤一: 10 | ```xml 11 | 12 | com.runcoding 13 | monitor-spring 14 | 1.0.0-SNAPSHOT 15 | 16 | ``` 17 | 18 | 步骤二: 19 | ```yml 20 | spring: 21 | application: 22 | name: monitor-center # 应用名称 23 | profiles: 24 | active: dev # 运行环境(显示在钉钉消息中) 25 | 26 | server: 27 | tomcat: 28 | max-threads: 100 # Reactor线程池最大线程数(目前使用的是tomcat) 29 | 30 | # 哨兵限流配置支持与spring config结合的热更新方式。通过Eureka服务发现节点。注意刷新时会重启Eureka注册节点。 31 | csp: 32 | sentinel: 33 | rules: 34 | system: # 配置哨兵平台规则 35 | systemLoad: 10.0 # 配置最高系统加载平均值,是排队到可用处理器的可运行实体数目与可用处理器上可运行实体数目的总和在某一段时间进行平均的结果 36 | avgRt: 10000 # 平均RT时间(ms) 37 | qps: 1000 # 每秒接受处理的请求数 38 | maxThread: 200 # 最高并行执行线程数 39 | # http://tool.chinaz.com/Tools/unixtime.aspx 40 | authorityWhite: MonitorController.job(0)|1536595200000,OrderProducerService.send(2)|-1 #监控方法白名单(Unix时间单位ms,-1不设置过期时间,Unix时间 1536595200000 = 2018-09-11 00:00:00) 41 | authorityBlock: MonitorController.degrade(0)|-1 #监控黑名单方法 42 | isAutoRule: true # 是否自动开启限流 43 | maxLoadAverageRate: 0.95 # 最大的负载比例(当前正在运行的线程/容器最大数量),这里的容器默认指tomcat。超过后会进入打断执行线程判断 44 | isAutoInterrupt: true # 是否自动打断执行线程(如果设置为false,maxRunTimeoutMillis和maxBlockTimeMillis配置将失效) 45 | maxRunTimeoutMillis: 10000 # 最长的方法执行时长(ms,默认10s),超过这个时间执行线程会被打断,并且加入到黑名单中 46 | maxBlockTimeMillis: 180000 # 加入黑名单时长(ms,默认3分钟) 47 | warnTimeoutMillis: 3000 # 调用请求超时3s,输出服务当前运行日志 48 | maxTardinessMillis: 500 # 超过这个时间(ms,默认0.5s)的请求,将在每小时被统计 49 | api: 50 | # port: 8099 # 服务向外暴露端口,供dashboard请求(不填不暴露),目前不建议使用 51 | dashboard: 52 | # server: localhost:8090 # dashboard 监控台地址,目前不建议使用 53 | webHook: 54 | # 钉钉文档 https://open-doc.dingtalk.com/docs/doc.htm?treeId=257&articleId=105735&docType=1 55 | dingTalk: 56 | atMobiles: 1580000000,158000001 #接受钉钉,通知需要被@的人 57 | accessToken: 84b2a8576d5193637 #钉钉通知机器人 58 | 59 | eureka: # 多节点部署需配置eureka,用做哨兵变更规则 60 | 61 | ``` 62 | > 具体可查看: SentinelWebConfig.java 63 | 64 | ```java 65 | @Resource 66 | private MonitorProcessor monitorProcessor; 67 | 68 | monitorProcessor.setWarnChatBot(chatBotSendLog()); 69 | 70 | /**自定义异常报警*/ 71 | public MonitorSendFunction chatBotSendLog(){ 72 | return ((methodName, t, args) -> { 73 | if(!(t instanceof RuntimeException)){ 74 | return; 75 | } 76 | /**处理RuntimeException异常*/ 77 | String throwablePackageName = t.getClass().getPackage().getName(); 78 | if(StringUtils.startsWith(throwablePackageName,"java.lang") || 79 | StringUtils.startsWith(throwablePackageName,"org.springframework") ){ 80 | boolean checkEnableSend = DTWebHookProcessor.checkEnableSend(methodName + t.getClass().getSimpleName()); 81 | if(!checkEnableSend){ 82 | return; 83 | } 84 | /**是否死锁*/ 85 | boolean isDeadlock = t instanceof DeadlockLoserDataAccessException; 86 | /**运行时异常*/ 87 | DTWebHookProcessor.chatbotSendByMarkdown("运行时异常报警", 88 | "## 服务在运行时出现了异常,请即时处理 \n - 服务:"+appName+"-"+ appEnv 89 | +"\n - ip:"+HostNameUtil.getIp() 90 | +"\n - 方法:"+methodName 91 | +"\n - 参数:"+JSON.toJSONString(args) 92 | + (isDeadlock ? "\n - 死锁: 当前执行出现了死锁" : "") 93 | +"\n - error:"+t.toString()+"\n"+t.getMessage() 94 | +"\n - 问题处理人:", 95 | false); 96 | } 97 | }); 98 | } 99 | 100 | ``` 101 | 102 | ## 自动流控规则与实现 103 | > SentinelRuleProcessor.java 104 | 105 | - 开启自动流控 isAutoRule = true 106 | - 自动流控:当处理线程大于最大线程池95%时,该运行方法将只有50%线程数的线程可以运行该方法。具体查看哨兵的FlowRule线程控制 107 | - 自动降级: 当方法执行时间>3s时,通过响应时间3s,阻塞2s的窗口时间。 108 | 109 | - 是否自动打断执行时间超过10s的线程,打断后加入黑名单三分钟。isAutoInterrupt = true 110 | 111 | -------------------------------------------------------------------------------- /monitor-spring/README.md: -------------------------------------------------------------------------------- 1 | # metrics-spring-boot 2 | Metrics Spring-boot Sqlite Aop method tps 3 | 4 | ## 服务接口监控 5 | - 查看当前服务内存、线程运行情况。 6 | - 每小时定时统计接口调用(count、tps)情况。 7 | 8 | ## 使用方式 9 | 业务系统中直接嵌入。(目前版本暂未提供权限功能,需定制开发) 10 | ```xml 11 | 12 | com.runcoding 13 | monitor-spring 14 | 0.0.1-SNAPSHOT 15 | 16 | ``` 17 | ## 动态配置参数 18 | ```text 19 | runcoding.monitor.sqlite.storePath = ## 默认 当前用户目录 + /data/sqlite/monitor 20 | spring.application.name = ## 数据文件名称,默认 monitor.db 21 | ``` 22 | 23 | ### 实时查看JVM内存、线程、接口或服务调用次数 24 | - 地址: https://metrics-spring-boot.herokuapp.com/monitor/index.html 25 | ![](snapshot/monitor_index.png) 26 | 27 | ### 查看每分钟接口调用速率均值(每小时统计一次) 28 | - 地址: https://metrics-spring-boot.herokuapp.com//monitor/metric.html 29 | ![](snapshot/monitor_metric.png) 30 | 31 | 32 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/config/MonitorConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.PropertySource; 6 | 7 | /** 8 | * @author: runcoding 9 | * @email: runcoding@163.com 10 | * @created Time: 2019/07/21 15:07 11 | * @description 启用系统监控 12 | * Copyright (C), 13 | **/ 14 | @Configuration 15 | @ComponentScan({"com.runcoding.monitor"}) 16 | @PropertySource("classpath:/monitor_config.properties") 17 | public class MonitorConfiguration { 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/config/sqlite/DBInitializeConfig.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.config.sqlite; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.util.StringUtils; 10 | 11 | import javax.annotation.PostConstruct; 12 | import javax.sql.DataSource; 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.InputStreamReader; 17 | import java.nio.charset.StandardCharsets; 18 | import java.sql.Connection; 19 | import java.sql.SQLException; 20 | import java.sql.Statement; 21 | /** 22 | * @author: runcoding 23 | * @email: runcoding@163.com 24 | * @created Time: 2019/07/25 21:07 25 | * @description sqlite初始化 26 | * Copyright (C), 27 | **/ 28 | @Configuration 29 | public class DBInitializeConfig { 30 | 31 | private Logger logger = LoggerFactory.getLogger(DBInitializeConfig.class); 32 | 33 | /**初始化脚步存放路径*/ 34 | @Value("${runcoding.monitor.sqlite.store_schema_path:/store/schema.sql}") 35 | private String store_schema_path ; 36 | 37 | @Autowired 38 | @Qualifier("sqliteMonitorDataSource") 39 | private DataSource dataSource; 40 | 41 | @PostConstruct 42 | public void initialize(){ 43 | Connection connection = null; 44 | Statement statement = null; 45 | try { 46 | connection = dataSource.getConnection(); 47 | statement = connection.createStatement(); 48 | statement.executeUpdate("BEGIN IMMEDIATE;"); 49 | initSqliteSchema(statement,store_schema_path); 50 | statement.executeUpdate("COMMIT;"); 51 | } catch (SQLException e) { 52 | logger.error("初始化统计数据异常:",e); 53 | } finally { 54 | try { 55 | if(connection != null && !connection.isClosed()){ 56 | connection.close(); 57 | } 58 | if (statement != null && !statement.isClosed()){ 59 | statement.close(); 60 | } 61 | } catch (SQLException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | 67 | public static void initSqliteSchema(Statement statement ,String localSqlitePath) throws SQLException { 68 | String sql = null; 69 | try { 70 | sql = readResourceFileContent(localSqlitePath); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | } 74 | String[] sqlArr = sql.split(";"); 75 | for (String sqlStr : sqlArr) { 76 | if(!StringUtils.isEmpty(sqlStr.trim())){ 77 | statement.execute(sqlStr.trim()); 78 | } 79 | } 80 | } 81 | 82 | public static String readResourceFileContent(String resourceFilePath) throws IOException { 83 | StringBuilder sb = new StringBuilder(8192); 84 | 85 | try (InputStream inputStream = DBInitializeConfig.class.getResourceAsStream(resourceFilePath)) { 86 | try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { 87 | BufferedReader r = new BufferedReader(reader); 88 | String str = null; 89 | 90 | while ((str = r.readLine()) != null) { 91 | sb.append(str); 92 | sb.append("\n"); 93 | } 94 | } 95 | } 96 | return sb.toString(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/config/sqlite/MonitorDataSourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.config.sqlite; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import org.apache.ibatis.session.SqlSessionFactory; 5 | import org.mybatis.spring.SqlSessionFactoryBean; 6 | import org.mybatis.spring.annotation.MapperScan; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 13 | import org.springframework.util.StringUtils; 14 | 15 | import java.io.File; 16 | 17 | /** 18 | * @module 主数据源配置 19 | * @author runcoding 20 | * @date: 2017年10月27日 21 | */ 22 | @Configuration 23 | @MapperScan(basePackages = "com.runcoding.monitor.web.dao", 24 | sqlSessionFactoryRef = "sqliteMonitorSqlSessionFactory") 25 | public class MonitorDataSourceConfiguration { 26 | 27 | private Logger logger = LoggerFactory.getLogger(MonitorDataSourceConfiguration.class); 28 | 29 | /** sqlite文件保持路径(默认当前用户home下) */ 30 | @Value("${runcoding.monitor.sqlite.storePath:}") 31 | private String storePath; 32 | 33 | @Value("${spring.application.name:}") 34 | private String appId; 35 | 36 | private DruidDataSource dataSource; 37 | 38 | 39 | @Bean(name = "sqliteMonitorDataSource") 40 | public DruidDataSource sqliteMonitorDataSource() { 41 | appId = StringUtils.isEmpty(appId) ? "monitor.db": appId+".db"; 42 | if(StringUtils.isEmpty(storePath)){ 43 | String userHome = System.getenv("HOME"); 44 | storePath = userHome + "/data/sqlite/monitor"; 45 | } 46 | File file = new File(storePath); 47 | if(!file.exists()){ 48 | file.mkdirs(); 49 | } 50 | String storeUrl = storePath + "/" + appId ; 51 | logger.info("monitor统计数据存储路径:"+storeUrl); 52 | dataSource = new DruidDataSource(); 53 | dataSource.setUrl("jdbc:sqlite:"+storeUrl); 54 | dataSource.setDriverClassName("org.sqlite.JDBC"); 55 | dataSource.setValidationQuery("SELECT 1"); 56 | return dataSource; 57 | } 58 | 59 | @Bean(name = "sqliteMonitorTransactionManager") 60 | public DataSourceTransactionManager sqliteMonitorTransactionManager() { 61 | return new DataSourceTransactionManager(dataSource); 62 | } 63 | 64 | @Bean(name = "sqliteMonitorSqlSessionFactory") 65 | public SqlSessionFactory sqliteMonitorSqlSessionFactory() throws Exception { 66 | SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); 67 | sessionFactory.setDataSource(dataSource); 68 | return sessionFactory.getObject(); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/dto/Resp.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.dto; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import com.runcoding.monitor.exception.BusinessException; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 返回实体包装类 12 | * @author runcoding 13 | * @date 2017年9月3日 下午8:19:10 14 | */ 15 | public class Resp implements Serializable { 16 | 17 | @ApiModelProperty("请求响应状态") 18 | private String status; 19 | 20 | @ApiModelProperty("消息提示") 21 | private String message; 22 | 23 | @ApiModelProperty("返回结果") 24 | private E data; 25 | 26 | public Resp(String status, String message, E data) { 27 | this.status = status; 28 | this.message = message; 29 | this.data = data; 30 | } 31 | 32 | public Resp() { 33 | } 34 | 35 | public E getData() { 36 | return data; 37 | } 38 | 39 | public void setData(E data) { 40 | this.data = data; 41 | } 42 | 43 | public String getStatus() { 44 | return status; 45 | } 46 | 47 | public void setStatus(String status) { 48 | this.status = status; 49 | } 50 | 51 | public String getMessage() { 52 | return message; 53 | } 54 | 55 | public void setMessage(String message) { 56 | this.message = message; 57 | } 58 | 59 | @JSONField(serialize = false) 60 | public boolean isSuccess() { 61 | return StringUtils.equals(RespStatus.SUCCESS.code,status); 62 | } 63 | 64 | @JSONField(serialize = false) 65 | public boolean isFailure() { 66 | return !StringUtils.equals(RespStatus.SUCCESS.code,status); 67 | } 68 | 69 | /** 业务处理成功 */ 70 | public static Resp success(String message) { 71 | return success(message, null); 72 | } 73 | 74 | public static Resp success() { 75 | return success(null, null); 76 | } 77 | 78 | public static Resp success(T data) { 79 | return success(null, data); 80 | } 81 | 82 | public static Resp success(String message, T data) { 83 | return new Resp(RespStatus.SUCCESS.code, message, data); 84 | } 85 | 86 | /** 业务处理失败 */ 87 | public static Resp failure(String message) { 88 | return failure(message, null); 89 | } 90 | 91 | public static Resp failure(String message, T data) { 92 | return new Resp(RespStatus.FAILURE.code, message, data); 93 | } 94 | 95 | /**抛出异常*/ 96 | public static void throwFailure(String message) { 97 | throw new BusinessException(failure(message, null)); 98 | } 99 | 100 | /** 系统异常 */ 101 | public static Resp error(String message) { 102 | return new Resp(RespStatus.INTERNAL_SERVER_ERROR.code, message, null); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/dto/RespStatus.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.dto; 2 | 3 | /** 4 | * @author runcoding 5 | * @desc 6 | */ 7 | public enum RespStatus { 8 | 9 | /**业务处理成功*/ 10 | SUCCESS("200", "业务处理成功"), 11 | 12 | /**默认的业务处理异常,特殊情况需要业务系统自定义*/ 13 | FAILURE("400", "业务处理失败"), 14 | 15 | SESSION_NOT_AVAILABLE("401", "会话过期请重新登录"), 16 | 17 | FORBIDDEN("403", "无权限访问"), 18 | 19 | METHOD_NOT_ALLOWED("405", "请求方法不支持"), 20 | 21 | /**签名验证不通过*/ 22 | PRECONDITION_FAILED("412", "签名验证不通过"), 23 | 24 | COMPULSORYRENEWAL("436","强制更新"), 25 | 26 | /**系统异常*/ 27 | INTERNAL_SERVER_ERROR("500", "服务器内部异常") 28 | ; 29 | 30 | public final String code; 31 | 32 | public final String name; 33 | 34 | RespStatus(String code, String name) { 35 | this.code = code; 36 | this.name = name; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.exception; 2 | 3 | import com.runcoding.monitor.dto.Resp; 4 | 5 | /** 6 | * @author: runcoding 7 | * @Date: 2019/07/06 上午13:22 8 | * @Description: 业务处理异常 9 | */ 10 | public class BusinessException extends RuntimeException { 11 | 12 | private Resp resp; 13 | 14 | public BusinessException(Resp resp) { 15 | this.resp = resp; 16 | } 17 | 18 | public Resp getResp() { 19 | return resp; 20 | } 21 | 22 | public void setResp(Resp resp) { 23 | this.resp = resp; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/exception/SentinelBlockException.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.exception; 2 | 3 | import com.runcoding.monitor.dto.Resp; 4 | 5 | /** 6 | * @author: runcoding 7 | * @Date: 2019/07/06 上午13:22 8 | * @Description: 哨兵限流异常 9 | */ 10 | public class SentinelBlockException extends BusinessException { 11 | 12 | 13 | public SentinelBlockException(Resp resp) { 14 | super(resp); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/metric/SentinelMetricProcessor.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.metric; 2 | 3 | import com.alibaba.csp.sentinel.command.CommandHandler; 4 | import com.alibaba.csp.sentinel.command.CommandRequest; 5 | import com.alibaba.csp.sentinel.command.CommandResponse; 6 | import com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter; 7 | import com.alibaba.csp.sentinel.transport.util.HttpCommandUtils; 8 | import com.google.common.collect.Lists; 9 | import com.runcoding.monitor.web.utils.date.DatePattern; 10 | import com.runcoding.monitor.web.utils.date.LocalDateUtil; 11 | import com.runcoding.monitor.web.model.metrics.MethodMetricInfo; 12 | import com.runcoding.monitor.web.model.MonitorConstants; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * 18 | * @author: runcoding@163.com 19 | * @date: 2019/07/29 11:51 20 | * @describe: 监控哨兵统计信息 21 | **/ 22 | public class SentinelMetricProcessor { 23 | 24 | 25 | /** 26 | * 指定的开始时间(startTime), 27 | * 结束时间(endTime) {endTime ==null时,可以设置maxLines最大行数为} 28 | * 指定资源(identity)的值 29 | * @param request 30 | * @return 31 | */ 32 | public static List getMethodMetricInfo(CommandRequest request) { 33 | List methodApis = Lists.newArrayList(); 34 | request.addMetadata(HttpCommandUtils.REQUEST_TARGET,"metric"); 35 | CommandHandler commandHandler = SimpleHttpCommandCenter.getHandler("metric"); 36 | if (commandHandler == null) { 37 | return methodApis; 38 | } 39 | 40 | CommandResponse response = (CommandResponse) commandHandler.handle(request); 41 | if(!response.isSuccess()){ 42 | return methodApis; 43 | } 44 | String result = response.getResult(); 45 | String[] metricArr = result.split("\n"); 46 | if(metricArr == null ){ 47 | return methodApis; 48 | } 49 | for (int i = 0; i < metricArr.length ; i++) { 50 | String metricLine = metricArr[i]; 51 | String[] metric = metricLine.split("\\|"); 52 | if(metric == null || metric.length < 7){ 53 | continue; 54 | } 55 | /**1535513607000|MonitorController.containerMetrics(1)|63|0|64|0|4512*/ 56 | MethodMetricInfo methodMetricInfo = new MethodMetricInfo(); 57 | String refDate = LocalDateUtil.secondToStr(Long.parseLong(metric[0]) / MonitorConstants.millisecond, DatePattern.LONG); 58 | methodMetricInfo.setRefDate(refDate); 59 | methodMetricInfo.setName(metric[1]); 60 | methodMetricInfo.setCntPassRequest(Long.parseLong(metric[2])); 61 | methodMetricInfo.setCntBlockRequest(Long.parseLong(metric[3])); 62 | methodMetricInfo.setCntRequest(Long.parseLong(metric[2])+Long.parseLong(metric[3])); 63 | methodMetricInfo.setCntSuccessRequest(Long.parseLong(metric[4])); 64 | methodMetricInfo.setCntExceptionRequest(Long.parseLong(metric[5])); 65 | methodMetricInfo.setAvgRt(Long.parseLong(metric[6])); 66 | methodApis.add(methodMetricInfo); 67 | } 68 | return methodApis; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/monitor/function/MonitorDegradeRuleFunction.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.monitor.function; 2 | 3 | /** 4 | * 5 | * @author: runcoding@163.com 6 | * @date: 2019/07/10 16:08 7 | * @describe: 自动降级规则配置 8 | **/ 9 | @FunctionalInterface 10 | public interface MonitorDegradeRuleFunction { 11 | 12 | 13 | /** 14 | * @param methodName 执行方法 15 | * @param duration 执行时间 16 | * */ 17 | void autoRule(String methodName, long duration); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/monitor/function/MonitorFlowRuleFunction.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.monitor.function; 2 | 3 | /** 4 | * 5 | * @author: runcoding@163.com 6 | * @date: 2019/07/10 16:08 7 | * @describe: 自动限流规则配置 8 | **/ 9 | @FunctionalInterface 10 | public interface MonitorFlowRuleFunction { 11 | 12 | 13 | void autoRule(String methodName); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/monitor/function/MonitorSendFunction.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.monitor.function; 2 | 3 | /** 4 | * 5 | * @author: runcoding@163.com 6 | * @date: 2019/07/04 16:08 7 | * @describe: 警告机器人 8 | **/ 9 | @FunctionalInterface 10 | public interface MonitorSendFunction { 11 | 12 | /**发送异常机器人处理业务*/ 13 | void chatBotSendLog(String methodName, Throwable t, Object[] args); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/monitor/sentinel/SentinelProcessor.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.monitor.sentinel; 2 | 3 | import com.runcoding.monitor.support.metric.MetricProcessor; 4 | import com.runcoding.monitor.web.model.MonitorConstants; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.lang.management.ManagementFactory; 9 | import java.lang.management.OperatingSystemMXBean; 10 | import java.util.concurrent.atomic.AtomicLong; 11 | 12 | /** 13 | * 14 | * @author: runcoding@163.com 15 | * @date: 2019/07/31 11:24 16 | * @describe: 哨兵监控处理器 17 | **/ 18 | public class SentinelProcessor { 19 | 20 | private static Logger logger =LoggerFactory.getLogger(SentinelProcessor.class); 21 | 22 | /**正在处理的线程数*/ 23 | private static AtomicLong processThreads = new AtomicLong(0); 24 | 25 | /**高负载流控*/ 26 | public static void highLoadSentinelAutoRule(boolean isWhiteMethod ,boolean isThreadRunningMethod, String methodName){ 27 | if(!isThreadRunningMethod){ 28 | return; 29 | } 30 | long currProcessThreads = processThreads.getAndIncrement(); 31 | if( isWhiteMethod || (currProcessThreads / MonitorConstants.serverMaxThreads) < MonitorConstants.maxLoadAverageRate){ 32 | return; 33 | } 34 | /**正常处理线程大于95%时,自动进入流控*/ 35 | SentinelRuleProcessor.highLoadAverageRate(methodName); 36 | } 37 | 38 | /***执行时间大于等于3s,自动限流 */ 39 | public static void timeOutSentinelAutoRule(boolean isWhiteMethod ,boolean isThreadRunningMethod , 40 | String methodName , Object[] args, long duration ){ 41 | try{ 42 | /**记录最长执行时间*/ 43 | MetricProcessor.signTardiness(methodName,duration); 44 | if(duration < MonitorConstants.warnTimeoutMillis){ 45 | return; 46 | } 47 | warnLogger(methodName,duration); 48 | 49 | if(isWhiteMethod){ 50 | return; 51 | } 52 | 53 | if(isThreadRunningMethod){ 54 | /**当前方法为执行记录初始方法*/ 55 | SentinelRuleProcessor.methodLimiter(methodName,args,duration); 56 | } 57 | }catch (Exception e){ 58 | logger.error("记录执行时间错误:",e); 59 | } 60 | } 61 | 62 | public static void destroy(){ 63 | processThreads.getAndDecrement(); 64 | } 65 | 66 | /***打印日志*/ 67 | private static void warnLogger(String methodName, long duration) { 68 | 69 | OperatingSystemMXBean system = ManagementFactory.getOperatingSystemMXBean(); 70 | 71 | /**后一分钟内系统加载平均值。*/ 72 | double systemLoadAverage = system.getSystemLoadAverage(); 73 | 74 | logger.warn("【执行】[method]{},当前处理时长:{},系统负载:{}",methodName,duration/MonitorConstants.millisecond,systemLoadAverage); 75 | } 76 | 77 | /**是否高负载*/ 78 | public static boolean isHighLoad(){ 79 | return (processThreads.get() / MonitorConstants.serverMaxThreads) > MonitorConstants.maxLoadAverageRate; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/server/DynamicServerProcessor.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.server; 2 | 3 | import com.runcoding.monitor.web.model.MonitorConstants; 4 | import com.runcoding.monitor.web.utils.IpUtils; 5 | import com.netflix.loadbalancer.ILoadBalancer; 6 | import com.netflix.loadbalancer.Server; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.BeansException; 10 | import org.springframework.cloud.netflix.ribbon.SpringClientFactory; 11 | import org.springframework.context.ApplicationContext; 12 | import org.springframework.context.ApplicationContextAware; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.util.CollectionUtils; 15 | 16 | import java.util.HashSet; 17 | import java.util.List; 18 | import java.util.Set; 19 | 20 | /** 21 | * 22 | * @author: runcoding@163.com 23 | * @date: 2019/07/04 10:33 24 | * @describe: 服务信息 25 | **/ 26 | @Service 27 | public class DynamicServerProcessor implements ApplicationContextAware { 28 | 29 | private Logger logger = LoggerFactory.getLogger(DynamicServerProcessor.class); 30 | 31 | /**上下文对象实例 */ 32 | private ApplicationContext applicationContext; 33 | 34 | /** 获取服务注册在eureka */ 35 | public Set getServerHostPorts(){ 36 | SpringClientFactory springClientFactory = applicationContext.getBean(SpringClientFactory.class); 37 | Set hostPorts = new HashSet<>(); 38 | ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(MonitorConstants.applicationName); 39 | if(loadBalancer != null){ 40 | List reachableServers = loadBalancer.getReachableServers(); 41 | reachableServers.forEach(e->{ 42 | /**如果服务有设置zone,此处获取的可能并不是所有的实例 43 | * http://www.cnblogs.com/flying607/p/9560159.html 44 | * @EnableDiscoveryClient 开启(EurekaRibbonClientConfiguration)或关闭 45 | * */ 46 | hostPorts.add(e.getHostPort()); 47 | }); 48 | } 49 | if(CollectionUtils.isEmpty(hostPorts)){ 50 | hostPorts.add(IpUtils.getIp()+":"+MonitorConstants.applicationPort); 51 | } 52 | return hostPorts; 53 | } 54 | 55 | /**获取bean*/ 56 | public T getBean(Class requiredType) throws BeansException{ 57 | return applicationContext.getBean(requiredType); 58 | } 59 | 60 | @Override 61 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 62 | this.applicationContext = applicationContext; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/server/MonitorSessionService.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.server; 2 | 3 | import com.alibaba.csp.sentinel.util.HostNameUtil; 4 | import com.runcoding.monitor.exception.BusinessException; 5 | import com.runcoding.monitor.dto.Resp; 6 | import com.runcoding.monitor.dto.RespStatus; 7 | import com.runcoding.monitor.web.model.monitor.MonitorUserInfo; 8 | import com.runcoding.monitor.web.utils.PasswordEncoder; 9 | import org.apache.commons.lang.StringUtils; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.web.context.request.RequestContextHolder; 13 | import org.springframework.web.context.request.ServletRequestAttributes; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | 17 | /** 18 | * @author runcoding 19 | * @date 2019-07-16 20 | * @desc: monitor监控会话管理 21 | */ 22 | @Service 23 | public class MonitorSessionService { 24 | 25 | @Value("${spring.profiles.active:local}") 26 | private String appEnv; 27 | 28 | @Value("${runcoding.monitor.username:admin}") 29 | private String username; 30 | 31 | @Value("${runcoding.monitor.password:admin}") 32 | private String password; 33 | 34 | private static String OPEN_ENV = "local-dev-test-stresstest"; 35 | 36 | private static String OPEN_SERVER_NAME = "localhost-127.0.0.1"; 37 | 38 | public String login( MonitorUserInfo user){ 39 | if(username.equals(user.getUsername()) && password.equals(user.getPassword())){ 40 | return getToken() ; 41 | } 42 | throw new BusinessException(Resp.failure("用户名或密码错误")); 43 | } 44 | 45 | public void isLogin(){ 46 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) 47 | .getRequest(); 48 | String token = request.getHeader("Monitor-Authorization"); 49 | if(StringUtils.isNotBlank(token)){ 50 | if(PasswordEncoder.matches(password,token)){ 51 | return ; 52 | } 53 | } 54 | 55 | /**生产和预发环境需要登录*/ 56 | if(StringUtils.containsAny(OPEN_ENV,appEnv)){ 57 | return ; 58 | } 59 | 60 | /**本服务调用不需要登录*/ 61 | String serverName = request.getServerName(); 62 | if(StringUtils.containsAny(OPEN_SERVER_NAME,serverName) || 63 | StringUtils.equalsIgnoreCase(serverName, HostNameUtil.getIp())){ 64 | return ; 65 | } 66 | //throw new BusinessException(new Resp(RespStatus.SESSION_NOT_AVAILABLE.code,"需要登录",null)); 67 | } 68 | 69 | public String getToken() { 70 | return PasswordEncoder.encode(password); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/DTAt.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model; 2 | 3 | import java.util.Set; 4 | 5 | public class DTAt { 6 | /** 7 | * atMobiles : ["1825718XXXX"] 8 | * isAtAll : false 9 | */ 10 | 11 | private boolean isAtAll; 12 | 13 | 14 | public DTAt() { 15 | } 16 | 17 | public DTAt(boolean isAtAll, Set atMobiles) { 18 | this.isAtAll = isAtAll; 19 | this.atMobiles = isAtAll ? null : atMobiles; 20 | } 21 | 22 | private Set atMobiles; 23 | 24 | public boolean isIsAtAll() { 25 | return isAtAll; 26 | } 27 | 28 | public boolean isAtAll() { 29 | return isAtAll; 30 | } 31 | 32 | public void setAtAll(boolean atAll) { 33 | isAtAll = atAll; 34 | } 35 | 36 | public Set getAtMobiles() { 37 | return atMobiles; 38 | } 39 | 40 | public void setAtMobiles(Set atMobiles) { 41 | this.atMobiles = atMobiles; 42 | } 43 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/DTMessage.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import com.runcoding.monitor.support.webhook.dingtalk.model.content.DTActionCardContent; 5 | import com.runcoding.monitor.support.webhook.dingtalk.model.content.DTMarkdownContent; 6 | import com.runcoding.monitor.support.webhook.dingtalk.model.content.DTLinkContent; 7 | import com.runcoding.monitor.support.webhook.dingtalk.model.content.DTTextContent; 8 | 9 | /** 10 | * 11 | * @author: runcoding@163.com 12 | * @date: 2019/07/03 14:17 13 | * @describe: 钉钉消息 14 | * * msgtype : text 15 | * * text : {"content":"我就是我, @1825718XXXX 是不一样的烟火"} 16 | * * link : {"text":"群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。例如:通过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;通过聚合Trello,JIRA等项目协调服务,实现项目信息同步。不仅如此,群机器人支持Webhook协议的自定义接入,支持更多可能性,例如:你可将运维报警提醒通过自定义机器人聚合到钉钉群。","title":"自定义机器人协议","picUrl":"","messageUrl":"https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.Rqyvqo&treeId=257&articleId=105735&docType=1"} 17 | * * markdown : {"title":"杭州天气","text":"##杭州天气 \n > 9度,@1825718XXXX 西北风1级,空气良89,相对温度73%\n\n > ![screenshot](http://i01.lw.aliimg.com/media/lALPBbCc1ZhJGIvNAkzNBLA_1200_588.png)\n > ####10点20分发布 [天气](http://www.thinkpage.cn/) "} 18 | * * actionCard : {"title":"乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身","text":"![screenshot](@lADOpwk3K80C0M0FoA) \n ##乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划","hideAvatar":"0","btnOrientation":"0","singleTitle":"阅读全文","singleURL":"https://www.dingtalk.com/"} 19 | * * at : {"atMobiles":["1825718XXXX"],"isAtAll":false} 20 | **/ 21 | public class DTMessage { 22 | 23 | @JSONField(name = "msgtype") 24 | private DTMessageType msgType; 25 | 26 | private DTTextContent text; 27 | 28 | private DTLinkContent link; 29 | 30 | private DTMarkdownContent markdown; 31 | 32 | private DTActionCardContent actionCard; 33 | 34 | private DTAt at; 35 | 36 | public DTMessage() { 37 | } 38 | 39 | 40 | public DTMessage(DTMessageType msgType) { 41 | this.msgType = msgType; 42 | } 43 | 44 | public DTMessageType getMsgType() { 45 | return msgType; 46 | } 47 | 48 | public void setMsgType(DTMessageType msgType) { 49 | this.msgType = msgType; 50 | } 51 | 52 | public DTTextContent getText() { 53 | return text; 54 | } 55 | 56 | public void setText(DTTextContent text) { 57 | this.text = text; 58 | } 59 | 60 | public DTLinkContent getLink() { 61 | return link; 62 | } 63 | 64 | public void setLink(DTLinkContent link) { 65 | this.link = link; 66 | } 67 | 68 | public DTMarkdownContent getMarkdown() { 69 | return markdown; 70 | } 71 | 72 | public void setMarkdown(DTMarkdownContent markdown) { 73 | this.markdown = markdown; 74 | } 75 | 76 | public DTActionCardContent getActionCard() { 77 | return actionCard; 78 | } 79 | 80 | public void setActionCard(DTActionCardContent actionCard) { 81 | this.actionCard = actionCard; 82 | } 83 | 84 | public DTAt getAt() { 85 | return at; 86 | } 87 | 88 | public void setAt(DTAt at) { 89 | this.at = at; 90 | } 91 | 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/DTMessageType.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model; 2 | 3 | /** 4 | * 5 | * @author: runcoding@163.com 6 | * @date: 2019/07/03 14:24 7 | * @describe: 钉钉消息类型 8 | * https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.1BIP18&treeId=257&articleId=105735&docType=1 9 | **/ 10 | public enum DTMessageType { 11 | 12 | /**text*/ 13 | text, 14 | 15 | /**link*/ 16 | link, 17 | 18 | /**markdown*/ 19 | markdown, 20 | 21 | /**独立跳转*/ 22 | feedCard, 23 | 24 | /**整体跳转*/ 25 | actionCard; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/DTResult.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model; 2 | 3 | /** 4 | * 5 | * @author: runcoding@163.com 6 | * @date: 2019/07/03 14:44 7 | * @describe: 8 | **/ 9 | public class DTResult { 10 | 11 | 12 | /** 13 | * errmsg : ok 14 | * errcode : 0 15 | */ 16 | 17 | private String errmsg; 18 | 19 | private int errcode; 20 | 21 | public String getErrmsg() { 22 | return errmsg; 23 | } 24 | 25 | public void setErrmsg(String errmsg) { 26 | this.errmsg = errmsg; 27 | } 28 | 29 | public int getErrcode() { 30 | return errcode; 31 | } 32 | 33 | public void setErrcode(int errcode) { 34 | this.errcode = errcode; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/content/DTActionCardContent.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model.content; 2 | 3 | /** 4 | * title : 乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身 5 | * text : ![screenshot](@lADOpwk3K80C0M0FoA) 6 | ##乔布斯 20 年前想打造的苹果咖啡厅 7 | 8 | Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划 9 | * hideAvatar : 0 10 | * btnOrientation : 0 11 | * singleTitle : 阅读全文 12 | * singleURL : https://www.dingtalk.com/ 13 | */ 14 | public class DTActionCardContent { 15 | 16 | private String title; 17 | 18 | private String text; 19 | 20 | private String hideAvatar; 21 | 22 | private String btnOrientation; 23 | 24 | private String singleTitle; 25 | 26 | private String singleURL; 27 | 28 | public String getTitle() { 29 | return title; 30 | } 31 | 32 | public void setTitle(String title) { 33 | this.title = title; 34 | } 35 | 36 | public String getText() { 37 | return text; 38 | } 39 | 40 | public void setText(String text) { 41 | this.text = text; 42 | } 43 | 44 | public String getHideAvatar() { 45 | return hideAvatar; 46 | } 47 | 48 | public void setHideAvatar(String hideAvatar) { 49 | this.hideAvatar = hideAvatar; 50 | } 51 | 52 | public String getBtnOrientation() { 53 | return btnOrientation; 54 | } 55 | 56 | public void setBtnOrientation(String btnOrientation) { 57 | this.btnOrientation = btnOrientation; 58 | } 59 | 60 | public String getSingleTitle() { 61 | return singleTitle; 62 | } 63 | 64 | public void setSingleTitle(String singleTitle) { 65 | this.singleTitle = singleTitle; 66 | } 67 | 68 | public String getSingleURL() { 69 | return singleURL; 70 | } 71 | 72 | public void setSingleURL(String singleURL) { 73 | this.singleURL = singleURL; 74 | } 75 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/content/DTLinkContent.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model.content; 2 | /** 3 | * text : 群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。 4 | * 例如:通过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;通过聚合Trello,JIRA等项目协调服务,实现项目信息同步。 5 | * 不仅如此,群机器人支持Webhook协议的自定义接入,支持更多可能性,例如:你可将运维报警提醒通过自定义机器人聚合到钉钉群。 6 | * title : 自定义机器人协议 7 | * picUrl : 8 | * messageUrl : https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.Rqyvqo&treeId=257&articleId=105735&docType=1 9 | */ 10 | public class DTLinkContent { 11 | 12 | private String text; 13 | 14 | private String title; 15 | 16 | private String picUrl; 17 | 18 | private String messageUrl; 19 | 20 | public String getText() { 21 | return text; 22 | } 23 | 24 | public void setText(String text) { 25 | this.text = text; 26 | } 27 | 28 | public String getTitle() { 29 | return title; 30 | } 31 | 32 | public void setTitle(String title) { 33 | this.title = title; 34 | } 35 | 36 | public String getPicUrl() { 37 | return picUrl; 38 | } 39 | 40 | public void setPicUrl(String picUrl) { 41 | this.picUrl = picUrl; 42 | } 43 | 44 | public String getMessageUrl() { 45 | return messageUrl; 46 | } 47 | 48 | public void setMessageUrl(String messageUrl) { 49 | this.messageUrl = messageUrl; 50 | } 51 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/content/DTMarkdownContent.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model.content; 2 | /** 3 | * title : 杭州天气 4 | * text : ##杭州天气 5 | > 9度,@1825718XXXX 西北风1级,空气良89,相对温度73% 6 | 7 | > ![screenshot](http://i01.lw.aliimg.com/media/lALPBbCc1ZhJGIvNAkzNBLA_1200_588.png) 8 | > ####10点20分发布 [天气](http://www.thinkpage.cn/) 9 | */ 10 | public class DTMarkdownContent { 11 | 12 | private String title; 13 | 14 | private String text; 15 | 16 | public DTMarkdownContent() { 17 | } 18 | 19 | public DTMarkdownContent(String title, String text) { 20 | this.title = title; 21 | this.text = text; 22 | } 23 | 24 | public String getTitle() { 25 | return title; 26 | } 27 | 28 | public void setTitle(String title) { 29 | this.title = title; 30 | } 31 | 32 | public String getText() { 33 | return text; 34 | } 35 | 36 | public void setText(String text) { 37 | this.text = text; 38 | } 39 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/support/webhook/dingtalk/model/content/DTTextContent.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.support.webhook.dingtalk.model.content; 2 | /** 3 | * content : 我就是我, @1825718XXXX 是不一样的烟火 4 | */ 5 | public class DTTextContent { 6 | 7 | private String content; 8 | 9 | public String getContent() { 10 | return content; 11 | } 12 | 13 | public void setContent(String content) { 14 | this.content = content; 15 | } 16 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/controller/monitor/MonitorRegistrationController.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.controller.monitor; 2 | 3 | import com.runcoding.monitor.dto.Resp; 4 | import com.runcoding.monitor.support.server.MonitorSessionService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.cloud.client.serviceregistry.Registration; 8 | import org.springframework.cloud.client.serviceregistry.ServiceRegistry; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | /** 12 | * Created by runcoding on 2019/07/16. 13 | * @author runcoding 14 | * @desc 服务注册中心管理 15 | */ 16 | @Slf4j 17 | @RestController 18 | @RequestMapping("admin/monitor") 19 | public class MonitorRegistrationController { 20 | 21 | @Autowired 22 | private MonitorSessionService sessionService; 23 | 24 | @Autowired(required = false) 25 | private ServiceRegistry serviceRegistry; 26 | 27 | @Autowired(required = false) 28 | private Registration registration; 29 | 30 | 31 | /**获取服务(Eureka)当前状态*/ 32 | @GetMapping(value = "/register") 33 | public Resp getStatus(){ 34 | sessionService.isLogin(); 35 | Object status = serviceRegistry.getStatus(registration); 36 | return Resp.success(status); 37 | } 38 | 39 | /**变更服务(Eureka)当前状态 40 | * enum InstanceStatus { 41 | * CANCEL_OVERRIDE,// Eureka 特有状态 42 | * UP, // Ready to receive traffic(上线) 43 | * DOWN, // Do not send traffic- healthcheck callback failed (下线) 44 | * STARTING, // Just about starting- initializations to be done - do not send traffic 45 | * OUT_OF_SERVICE, // Intentionally shutdown for traffic(一般会在发版部署时使用,让服务下线关闭流量) 46 | * UNKNOWN; 47 | * */ 48 | @PutMapping(value = "/register") 49 | public Resp setStatus(String status){ 50 | sessionService.isLogin(); 51 | serviceRegistry.setStatus(registration,status); 52 | return Resp.success("变更Eureka服务status="+status); 53 | } 54 | 55 | /**服务(Eureka)注册上线*/ 56 | @PostMapping("/register") 57 | public Resp register(){ 58 | sessionService.isLogin(); 59 | serviceRegistry.register(registration); 60 | return Resp.success("服务成功注册上线"); 61 | } 62 | 63 | /** 64 | * 服务(Eureka)注销下线 65 | * https://www.cnblogs.com/trust-freedom/p/10744683.html 66 | * 此处可能有坑,调用注销下线,可能会导致再次调用上线无效(无法成功注册到Eureka Server) 67 | * */ 68 | @PostMapping(value = "/deregister") 69 | public Resp deregister(){ 70 | sessionService.isLogin(); 71 | serviceRegistry.deregister(registration); 72 | return Resp.success("服务成功注销下线(无法重新恢复注册)"); 73 | } 74 | 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/controller/monitor/MonitorSentinelController.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.controller.monitor; 2 | 3 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; 4 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; 5 | import com.alibaba.csp.sentinel.slots.system.SystemRuleManager; 6 | import com.alibaba.fastjson.JSON; 7 | import com.google.common.collect.Lists; 8 | import com.runcoding.monitor.dto.Resp; 9 | import com.runcoding.monitor.support.monitor.sentinel.SentinelRuleProcessor; 10 | import com.runcoding.monitor.support.server.DynamicServerProcessor; 11 | import com.runcoding.monitor.support.server.MonitorSessionService; 12 | import com.runcoding.monitor.web.model.MonitorConstants; 13 | import com.runcoding.monitor.web.model.sentinel.SentinelRuleInfo; 14 | import com.runcoding.monitor.web.utils.RestTemplateUtils; 15 | import lombok.extern.slf4j.Slf4j; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Controller; 18 | import org.springframework.util.CollectionUtils; 19 | import org.springframework.util.StringUtils; 20 | import org.springframework.web.bind.annotation.*; 21 | 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | /** 26 | * Created by runcoding on 2019/07/16. 27 | * @author runcoding 28 | */ 29 | @Slf4j 30 | @Controller 31 | @RequestMapping("admin/monitor") 32 | public class MonitorSentinelController { 33 | 34 | @Autowired 35 | private MonitorSessionService sessionService; 36 | 37 | @Autowired 38 | private DynamicServerProcessor dynamicServerProcessor; 39 | 40 | /**获取Sentinel配置的规则信息*/ 41 | @GetMapping(value = "/sentinel/rules") 42 | @ResponseBody 43 | public Resp getSentinelRules() { 44 | sessionService.isLogin(); 45 | SentinelRuleInfo ruleInfo = SentinelRuleInfo.builder() 46 | .flowRules(FlowRuleManager.getRules()) 47 | .degradeRules(DegradeRuleManager.getRules()) 48 | .systemRules(SystemRuleManager.getRules()).build(); 49 | 50 | ruleInfo.setAutoRule(MonitorConstants.isAutoRule); 51 | ruleInfo.setAuthorityWhite(SentinelRuleProcessor.getAuthorityWhite()); 52 | ruleInfo.setAuthorityBlock(SentinelRuleProcessor.getAuthorityBlock()); 53 | return Resp.success(ruleInfo); 54 | } 55 | 56 | /**编辑Sentinel配置的规则信息*/ 57 | @PutMapping(value = "/sentinel/rule") 58 | @ResponseBody 59 | public Resp editSentinelRule(@RequestParam(value = "ruleType",defaultValue = "") String ruleType , 60 | @RequestParam(value = "slave",defaultValue = "false")boolean slave , 61 | @RequestBody SentinelRuleInfo ruleInfo) { 62 | sessionService.isLogin(); 63 | if(StringUtils.isEmpty(ruleType)){ 64 | return Resp.failure("ruleType 为null"); 65 | } 66 | if(!slave){ 67 | Set serverHostPorts = dynamicServerProcessor.getServerHostPorts(); 68 | if(!CollectionUtils.isEmpty(serverHostPorts)){ 69 | String token = sessionService.getToken(); 70 | Set failHostPorts = new HashSet<>(); 71 | serverHostPorts.forEach(hostPort->{ 72 | try { 73 | String url = "http://"+hostPort+"/admin/monitor/sentinel/rule?ruleType="+ruleType+"&slave=true"; 74 | Resp result = RestTemplateUtils.putForEntity(url,token, JSON.toJSONString(ruleInfo)); 75 | if(result.isFailure()){ 76 | failHostPorts.add(hostPort); 77 | log.error("更新Sentinel配置的规则信息失败,可能{}服务已经下线。{}",hostPort,JSON.toJSONString(result)); 78 | } 79 | }catch (Exception e){ 80 | failHostPorts.add(hostPort); 81 | log.error("更新Sentinel配置的规则信息失败,可能{}服务已经下线",hostPort,e); 82 | } 83 | }); 84 | serverHostPorts.removeAll(failHostPorts); 85 | return Resp.success(Lists.newArrayList(serverHostPorts,failHostPorts)); 86 | } 87 | } 88 | boolean isOpsAll = StringUtils.endsWithIgnoreCase(ruleType,"all"); 89 | /**修改系统规则*/ 90 | if(StringUtils.endsWithIgnoreCase(ruleType,"systemRules") || isOpsAll ){ 91 | SystemRuleManager.loadRules(ruleInfo.getSystemRules()); 92 | } 93 | /**修改流控规则*/ 94 | if(StringUtils.endsWithIgnoreCase(ruleType,"flowRules") || isOpsAll){ 95 | SentinelRuleProcessor.loadFlowRules(ruleInfo.getFlowRules()); 96 | } 97 | /**修改流控规则*/ 98 | if(StringUtils.endsWithIgnoreCase(ruleType,"degradeRules") || isOpsAll){ 99 | SentinelRuleProcessor.loadDegradeRules(ruleInfo.getDegradeRules()); 100 | } 101 | /**是否自动开启自动限流*/ 102 | if(StringUtils.endsWithIgnoreCase(ruleType,"timeOutSentinelAutoRule")){ 103 | MonitorConstants.isAutoRule = ruleInfo.isAutoRule(); 104 | } 105 | /**修改方法白名单*/ 106 | if(StringUtils.endsWithIgnoreCase(ruleType,"authorityWhite")){ 107 | SentinelRuleProcessor.setAuthorityWhite(ruleInfo.getAuthorityWhite()); 108 | } 109 | /**修改方法白名单*/ 110 | if(StringUtils.endsWithIgnoreCase(ruleType,"authorityBlock")){ 111 | SentinelRuleProcessor.setAuthorityBlock(ruleInfo.getAuthorityBlock()); 112 | } 113 | return Resp.success(); 114 | } 115 | 116 | 117 | 118 | 119 | 120 | } 121 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/dao/MetricInfoMapper.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.dao; 2 | 3 | 4 | import com.runcoding.monitor.web.model.metrics.MethodMetricInfo; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author: runcoding 11 | * @email: runcoding@163.com 12 | * @created Time: 2019/07/21 15:07 13 | * @description 启用系统监控 14 | * Copyright (C), 15 | **/ 16 | public interface MetricInfoMapper { 17 | 18 | List findMetricInfo(@Param("orderType") Integer orderType); 19 | 20 | int insert(MethodMetricInfo methodMetricInfo); 21 | 22 | int delete(String delDate); 23 | 24 | int vacuum(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/dao/MetricInfoMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 34 | 35 | 37 | INSERT INTO "metric_info" ( 38 | refDate, 39 | name, 40 | cntRequest, 41 | cntPassRequest, 42 | cntSuccessRequest, 43 | cntExceptionRequest, 44 | cntBlockRequest, 45 | avgRt, 46 | tardiness 47 | ) 48 | VALUES 49 | ( 50 | #{refDate}, 51 | #{name}, 52 | #{cntRequest}, 53 | #{cntPassRequest}, 54 | #{cntSuccessRequest}, 55 | #{cntExceptionRequest}, 56 | #{cntBlockRequest}, 57 | #{avgRt}, 58 | #{tardiness} 59 | ) 60 | 61 | 62 | 63 | DELETE FROM metric_info where refDate 64 | 65 | 66 | VACUUM 67 | 68 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/job/MonitorJob.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.job; 2 | 3 | import com.alibaba.csp.sentinel.command.CommandRequest; 4 | import com.runcoding.monitor.web.model.metrics.MethodMetricInfo; 5 | import com.runcoding.monitor.support.metric.MetricProcessor; 6 | import com.runcoding.monitor.support.metric.SentinelMetricProcessor; 7 | import com.runcoding.monitor.web.dao.MetricInfoMapper; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.scheduling.annotation.Scheduled; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.time.LocalDateTime; 15 | import java.time.format.DateTimeFormatter; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.stream.Collectors; 20 | 21 | /** 22 | * @author: runcoding 23 | * @email: runcoding@163.com 24 | * @created Time: 2019/07/29 10:47 25 | * @description 服务监控数据job 26 | * Copyright (C), 2017-2018, runcoding 27 | **/ 28 | @Component 29 | public class MonitorJob { 30 | 31 | private int circleSize = 6; 32 | 33 | private Logger logger = LoggerFactory.getLogger(MonitorJob.class); 34 | 35 | @Autowired 36 | private MetricInfoMapper metricInfoMapper; 37 | 38 | /**每分钟监控 上一次执行完毕时间点之后30秒再执行*/ 39 | @Scheduled(fixedDelay = 30000) 40 | public void monitorMinute(){ 41 | MetricProcessor.interruptRunningTimeout(); 42 | } 43 | 44 | /**获取近一小时的统计数据,上一次执行完毕时间点之后1小时再执行*/ 45 | @Scheduled(fixedDelay = 3600000) 46 | public void monitorHour(){ 47 | try{ 48 | long currentTimeMillis = System.currentTimeMillis(); 49 | String refDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); 50 | Map metricInfoMap = new HashMap<>(64); 51 | for (int i = 0; i < circleSize; i++) { 52 | CommandRequest request = new CommandRequest(); 53 | request.addParam("endTime",String.valueOf(currentTimeMillis-1000)); 54 | /**十分以内的数据*/ 55 | currentTimeMillis -= 600000; 56 | request.addParam("startTime",String.valueOf(currentTimeMillis)); 57 | List metricInfoList = SentinelMetricProcessor.getMethodMetricInfo(request); 58 | 59 | Map tmpMetricMap = metricInfoList.stream(). 60 | collect(Collectors.toMap(m -> m.getName(), m -> m, this::collectMethodMetricInfo)); 61 | if(metricInfoMap.isEmpty()){ 62 | metricInfoMap.putAll(tmpMetricMap); 63 | continue; 64 | } 65 | tmpMetricMap.forEach((methodName,newMethodMetricInfo)-> { 66 | MethodMetricInfo methodMetricInfo = metricInfoMap.get(methodName); 67 | if(methodMetricInfo == null){ 68 | metricInfoMap.put(methodName,newMethodMetricInfo); 69 | return; 70 | } 71 | collectMethodMetricInfo(methodMetricInfo,newMethodMetricInfo); 72 | }); 73 | } 74 | metricInfoMap.forEach((methodName,methodMetricInfo)-> { 75 | /**最长执行时长(ms)*/ 76 | Long tardiness = MetricProcessor.removeTardinessMethod(methodName); 77 | methodMetricInfo.setTardiness(tardiness == null ? 0L : tardiness); 78 | methodMetricInfo.setRefDate(refDate); 79 | metricInfoMapper.insert(methodMetricInfo); 80 | }); 81 | /**清空7天之前的数据*/ 82 | int deleteNum = metricInfoMapper.delete(LocalDateTime.now().plusDays(-7). 83 | format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 84 | if(deleteNum>0){ 85 | /**释放磁盘空间*/ 86 | metricInfoMapper.vacuum(); 87 | } 88 | }catch (Exception e){ 89 | logger.warn("每小时统计一次服务监控出错",e); 90 | } 91 | 92 | } 93 | 94 | /**聚合多个结果*/ 95 | public MethodMetricInfo collectMethodMetricInfo(MethodMetricInfo oldVal, MethodMetricInfo newVal){ 96 | if(newVal == null){ 97 | return oldVal; 98 | } 99 | oldVal.setCntPassRequest(oldVal.getCntPassRequest() + newVal.getCntPassRequest()); 100 | oldVal.setCntBlockRequest(oldVal.getCntBlockRequest() + newVal.getCntBlockRequest()); 101 | oldVal.setCntRequest(oldVal.getCntRequest() + newVal.getCntRequest()); 102 | oldVal.setCntSuccessRequest(oldVal.getCntSuccessRequest() + newVal.getCntSuccessRequest()); 103 | oldVal.setCntExceptionRequest(oldVal.getCntExceptionRequest() + newVal.getCntExceptionRequest()); 104 | long oldValAvgRt = oldVal.getAvgRt(); 105 | long newValAvgRt = newVal.getAvgRt(); 106 | oldVal.setAvgRt(oldValAvgRt > newValAvgRt ? oldValAvgRt : newValAvgRt); 107 | return oldVal; 108 | }; 109 | 110 | } 111 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/MonitorConstants.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model; 2 | 3 | /** 4 | * 5 | * @author: runcoding@163.com 6 | * @date: 2019/07/31 17:10 7 | * @describe: 监控系统系统常量 8 | **/ 9 | public class MonitorConstants { 10 | 11 | /**应用名称*/ 12 | public static String applicationName; 13 | 14 | /**应用环境*/ 15 | public static String applicationEnv; 16 | 17 | /**应用运行的端口*/ 18 | public static String applicationPort; 19 | 20 | /** 21 | * tomcat最大处理http请求数 22 | * org.apache.tomcat.util.threads.ThreadPoolExecutor 23 | * */ 24 | public static long serverMaxThreads = 200; 25 | 26 | /**最大的负载比例(当前正在运行的线程/容器最大数量),这里的容器默认指tomcat*/ 27 | public static double maxLoadAverageRate = 0.95d; 28 | 29 | /**是否开启自动限流(默认开启,超过1s)*/ 30 | public static boolean isAutoRule = true; 31 | 32 | /**调用请求超时3s,输出服务当前运行日志*/ 33 | public static long warnTimeoutMillis = 3000L; 34 | 35 | /**是否自动打断执行线程*/ 36 | public static boolean isAutoInterrupt = true; 37 | 38 | /**最长的方法执行时长(ms,默认10s)*/ 39 | public static long maxRunTimeoutMillis = 10000L; 40 | 41 | /**加入黑名单时长(ms,默认3分钟)*/ 42 | public static long maxBlockTimeMillis = 180000L; 43 | 44 | /** 超过这个时间(ms,默认0.5s)的请求,将被统计*/ 45 | public static Long maxTardinessMillis = 500L; 46 | 47 | /**1s = 1000ms */ 48 | public static Long millisecond = 1000L; 49 | 50 | /**当前节点是否正在刷新*/ 51 | public static volatile boolean isRefresh = false; 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/ContainerCpuInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | 9 | /** 10 | * Created by runcoding on 2017/7/28. 11 | * @desc 当前服务器cpu 12 | */ 13 | @Builder 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class ContainerCpuInfo { 18 | 19 | /**多核cpu时,编号*/ 20 | private int cpuNum; 21 | 22 | /**CPU的总量MHz*/ 23 | private int mhz ; 24 | 25 | /**获得CPU的卖主,如:Intel*/ 26 | private String vendor ; 27 | 28 | /**获得CPU的类别,如:Celeron*/ 29 | private String model ; 30 | 31 | /**缓冲存储器数量*/ 32 | private long cacheSize; 33 | 34 | /**CPU用户使用率*/ 35 | private double user; 36 | 37 | /**系统使用率*/ 38 | private double sys; 39 | 40 | /**当前空闲率*/ 41 | private double idle; 42 | 43 | /**当前等待率*/ 44 | private double wait; 45 | 46 | /***/ 47 | private double stolen; 48 | 49 | /**总的使用率*/ 50 | private double combined; 51 | 52 | 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/ContainerMethodMonitorInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import com.runcoding.monitor.web.model.metrics.MethodMetricInfo; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | /** 13 | * @author: runcoding 14 | * @email: runcoding@163.com 15 | * @created Time: 2018/4/27 14:13 16 | * @description 方法监控执行信息 17 | * Copyright (C), 2017-2018, 18 | **/ 19 | @Builder 20 | @Data 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | public class ContainerMethodMonitorInfo { 24 | 25 | /**被监控了的方法*/ 26 | private Set methodNames; 27 | 28 | /**监控的方法*/ 29 | private String proposalMethodName; 30 | 31 | /**监控方法执行指标信息*/ 32 | private List proposalMetricList; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/ContainerRunningInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import com.alibaba.csp.sentinel.Constants; 4 | import com.alibaba.csp.sentinel.command.CommandRequest; 5 | import com.runcoding.monitor.support.jvm.JvmProcessor; 6 | import com.runcoding.monitor.support.metric.MetricProcessor; 7 | import com.runcoding.monitor.support.metric.SentinelMetricProcessor; 8 | import com.runcoding.monitor.web.model.metrics.MethodMetricInfo; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | import org.apache.commons.lang.StringUtils; 14 | import org.springframework.util.CollectionUtils; 15 | 16 | import java.lang.management.ManagementFactory; 17 | import java.util.*; 18 | import java.util.stream.Collectors; 19 | 20 | /** 21 | * Created by runcoding on 2017/7/27. 22 | * @desc 当前容器jvm信息 23 | */ 24 | @Builder 25 | @Data 26 | @NoArgsConstructor 27 | @AllArgsConstructor 28 | public class ContainerRunningInfo { 29 | 30 | /**当前操作系统信息*/ 31 | private OperatingSystemInfo operatingSystemInfo; 32 | 33 | /**当前服务线程信息*/ 34 | private ContainerThreadInfo containerThreadInfo; 35 | 36 | /**内存信息*/ 37 | private List containerMemoryInfo; 38 | 39 | /**当前方法被多少个线程正在执行*/ 40 | private Map methodThreadRunningCnt; 41 | 42 | /**方法执行指标信息*/ 43 | private ContainerMethodMonitorInfo methodMetricInfo; 44 | 45 | /**GC 运行次数*/ 46 | private List garbageCollectorInfoList; 47 | 48 | /**构建容器运行信息*/ 49 | public static ContainerRunningInfo buildContainerRunningInfo(boolean isDumpAllThread , String proposalMethodName){ 50 | ContainerRunningInfo runningInfo = new ContainerRunningInfo(); 51 | runningInfo.setOperatingSystemInfo(JvmProcessor.getOperatingSystemInfo()); 52 | runningInfo.setContainerMemoryInfo(JvmProcessor.getContainerMemoryInfo()); 53 | /**获取限制的线程信息(获取堆栈信息相当于jstack,注意会影响服务运行的性能)*/ 54 | runningInfo.setContainerThreadInfo(JvmProcessor.getContainerThreadInfo(isDumpAllThread)); 55 | runningInfo.setMethodThreadRunningCnt(MetricProcessor.methodThreadRunningCnt()); 56 | 57 | /**获取一分钟的数据*/ 58 | CommandRequest request = new CommandRequest(); 59 | long currentTimeMillis = System.currentTimeMillis(); 60 | request.addParam("startTime",String.valueOf(currentTimeMillis-60000)); 61 | List metricInfoList = SentinelMetricProcessor.getMethodMetricInfo(request); 62 | ContainerMethodMonitorInfo methodMetricInfo = new ContainerMethodMonitorInfo(); 63 | runningInfo.setMethodMetricInfo(methodMetricInfo); 64 | if(CollectionUtils.isEmpty(metricInfoList)){ 65 | return runningInfo; 66 | } 67 | Comparator byReducePassRequest = Comparator.comparing(m ->m.getCntPassRequest()); 68 | Set methodNames = new HashSet<>(); 69 | String maxPassRequestMethodName = metricInfoList.stream() 70 | .filter(f -> 71 | /**忽略掉sentinel监控信息*/ 72 | !org.apache.commons.lang3.StringUtils.containsAny(f.getName(), 73 | Constants.TOTAL_IN_RESOURCE_NAME, 74 | Constants.CPU_USAGE_RESOURCE_NAME, 75 | Constants.SYSTEM_LOAD_RESOURCE_NAME 76 | )).map(m -> {methodNames.add(m.getName());return m; }) 77 | .sorted(byReducePassRequest.reversed()).findFirst().get().getName(); 78 | 79 | if(StringUtils.isEmpty(proposalMethodName)){ 80 | proposalMethodName = maxPassRequestMethodName ; 81 | } 82 | 83 | String finalProposalMethodName = proposalMethodName; 84 | List proposalMetricList = metricInfoList.stream(). 85 | filter(m -> StringUtils.endsWithIgnoreCase(m.getName(), finalProposalMethodName)).collect(Collectors.toList()); 86 | 87 | methodMetricInfo.setMethodNames(methodNames); 88 | methodMetricInfo.setProposalMethodName(proposalMethodName); 89 | methodMetricInfo.setProposalMetricList(proposalMetricList); 90 | 91 | runningInfo.setMethodMetricInfo(methodMetricInfo); 92 | 93 | /**gc 信息*/ 94 | List gcList = ManagementFactory.getGarbageCollectorMXBeans().stream().map(gc -> 95 | GarbageCollectorInfo.builder().gcName(gc.getName()).gcCount(gc.getCollectionCount()).gcTime(gc.getCollectionTime()).build() 96 | ).collect(Collectors.toList()); 97 | runningInfo.setGarbageCollectorInfoList(gcList); 98 | return runningInfo; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/ContainerThreadInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Created by runcoding on 2017/7/27. 13 | * 当前服务线程信息 14 | */ 15 | @Builder 16 | @Data 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class ContainerThreadInfo { 20 | 21 | /**仍活动的线程总数*/ 22 | private long threadCount; 23 | 24 | /**峰值*/ 25 | private long peakThreadCount; 26 | 27 | /**线程总数(被创建并执行过的线程总数)*/ 28 | private long totalStartedThreadCount; 29 | 30 | /**当初仍活动的守护线程(daemonThread)总数*/ 31 | private long daemonThreadCount; 32 | 33 | /**当前容器,线程分组后的信息*/ 34 | private Map> threadGroupInfo; 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/GarbageCollectorInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author runcoding 10 | * @date 2019-07-19 11 | * @desc: GC信息 12 | */ 13 | @Builder 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class GarbageCollectorInfo { 18 | 19 | private String gcName; 20 | 21 | private Long gcCount; 22 | 23 | /**总gc耗时毫秒*/ 24 | private Long gcTime; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/GroupThreadInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Created by runcoding on 2017/7/27. 10 | * 线程信息 11 | */ 12 | @Builder 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class GroupThreadInfo { 17 | 18 | /**线程名称*/ 19 | private String threadName; 20 | 21 | /**线程id*/ 22 | private long threadId; 23 | 24 | /**线程的状态*/ 25 | private Thread.State threadState; 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/MemoryInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Created by runcoding on 2017/7/27. 10 | * 内存信息 11 | */ 12 | @Builder 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MemoryInfo { 17 | 18 | private String name; 19 | 20 | /**初始(M)*/ 21 | private long init; 22 | 23 | /**最大(上限)(M)*/ 24 | private long max; 25 | 26 | /**当前(已使用)(M)*/ 27 | private long used; 28 | 29 | /**提交的内存(已申请)(M)*/ 30 | private long committed; 31 | 32 | /**使用率(%)*/ 33 | private long usedRate; 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/container/OperatingSystemInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.container; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Created by runcoding on 2017/7/27. 10 | * container 获取当前操作系统信息 11 | */ 12 | @Builder 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class OperatingSystemInfo { 17 | 18 | /**运行用户名字*/ 19 | private String runName; 20 | 21 | /**运行的pid*/ 22 | private String runPid; 23 | 24 | /**系统名称*/ 25 | private String name; 26 | 27 | /**系统版本*/ 28 | private String version ; 29 | 30 | /**操作系统的架构*/ 31 | private String arch; 32 | 33 | /**当前系统内网ip*/ 34 | private String ip; 35 | 36 | /**可用的内核数*/ 37 | private long availableProcessors; 38 | 39 | /** 40 | * 最后一分钟内系统加载平均值。 41 | * 系统加载平均值是排队到可用处理器的可运行实体数目与可用处理器上可运行实体数目的总和在某一段时间进行平均的结果。 42 | * 计算加载平均值的方式是特定于操作系统的,但通常是衰减的与时间相关的平均值。 43 | * */ 44 | private double systemLoadAverage; 45 | 46 | private long totalMemory ; 47 | 48 | private long freeMemory ; 49 | 50 | private long maxMemory ; 51 | 52 | private long usedMemory ; 53 | 54 | private long availableMemory ; 55 | 56 | /**总物理内存(M) jdk9 已经需要添加jar才能使用,作用不大就移除了*/ 57 | @Deprecated 58 | private long totalPhysicalMemory; 59 | 60 | /**已用物理内存(M)*/ 61 | @Deprecated 62 | private long usedPhysicalMemorySize; 63 | 64 | /**剩余物理内存(M)*/ 65 | @Deprecated 66 | private long freePhysicalMemory; 67 | 68 | /**总交换空间(M)*/ 69 | @Deprecated 70 | private long totalSwapSpaceSize; 71 | 72 | /**已用交换空间(M)*/ 73 | @Deprecated 74 | private long usedSwapSpaceSize; 75 | 76 | /**剩余交换空间(M)*/ 77 | @Deprecated 78 | private long freeSwapSpaceSize; 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/metrics/MethodMetricInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.metrics; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author runcoding 10 | * @time 2017/10/26. 11 | */ 12 | @Builder 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MethodMetricInfo { 17 | 18 | private Integer id; 19 | 20 | /**统计日期*/ 21 | private String refDate; 22 | 23 | /**名称*/ 24 | private String name; 25 | 26 | /**总执行次数*/ 27 | private long cntRequest; 28 | 29 | /**总执行通过次数*/ 30 | private long cntPassRequest; 31 | 32 | /**总执行成功次数*/ 33 | private long cntSuccessRequest; 34 | 35 | /**总执行异常次数*/ 36 | private long cntExceptionRequest; 37 | 38 | /**总执行被阻塞次数*/ 39 | private long cntBlockRequest; 40 | 41 | /**平均相应时间(毫秒)*/ 42 | private long avgRt; 43 | 44 | /**最慢耗时长(毫秒)*/ 45 | private long tardiness; 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/monitor/MonitorUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.monitor; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @author: runcoding 11 | * @email: runcoding@163.com 12 | * @created Time: 2018/6/2 10:31 13 | * @description 统计用户信息 14 | * Copyright (C), 2017-2018, 15 | **/ 16 | @Builder 17 | @Data 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class MonitorUserInfo { 21 | 22 | 23 | private String username; 24 | 25 | @JSONField(serialize = false) 26 | private String password; 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/model/sentinel/SentinelRuleInfo.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.model.sentinel; 2 | 3 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; 4 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; 5 | import com.alibaba.csp.sentinel.slots.system.SystemRule; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @user: runcoding 16 | * @author: runcoding@163.com 17 | * @date: 2018/08/24 17:46 18 | * @describe: Sentinel规则配置 19 | **/ 20 | @Builder 21 | @Data 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | public class SentinelRuleInfo { 25 | 26 | 27 | /**是否自动开启限流*/ 28 | private boolean autoRule ; 29 | 30 | /**执行方法白名单{key:执行方法,value:规则截止时间,-1不限制(单位ms)}*/ 31 | private Map authorityWhite; 32 | 33 | /**执行方法黑名单{key:执行方法,value:规则截止时间,-1不限制(单位ms)}*/ 34 | private Map authorityBlock; 35 | 36 | /**修改流控规则*/ 37 | private List flowRules; 38 | 39 | /**修改降级规则*/ 40 | private List degradeRules; 41 | 42 | /**修改系统规则*/ 43 | private List systemRules; 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/utils/ErrorMessageUtils.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.utils; 2 | 3 | /** 4 | * 5 | * @author: runcoding@163.com 6 | * @date: 2019/07/29 18:31 7 | * @describe: 异常信息 8 | **/ 9 | public class ErrorMessageUtils { 10 | 11 | 12 | public static String getMessage( Throwable t){ 13 | StringBuilder str = new StringBuilder(t.toString()); 14 | str.append("\n StackTrace:"); 15 | StackTraceElement[] stackTrace = t.getStackTrace(); 16 | if(stackTrace != null){ 17 | str.append(stackTrace.length >1 ? stackTrace[0] : ""); 18 | } 19 | str.append("\n message:"); 20 | str.append(t.getMessage()); 21 | return str.toString(); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/utils/IPAddressAnalysor.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.utils; 2 | 3 | import javax.servlet.ServletRequest; 4 | import javax.servlet.http.HttpServletRequest; 5 | import java.net.InetAddress; 6 | import java.net.UnknownHostException; 7 | 8 | /** 9 | * 10 | * @Description: ip地址解析工具 11 | * @author runcoding 12 | * @date 2017年9月5日 13 | */ 14 | public class IPAddressAnalysor { 15 | 16 | /** 17 | * 18 | * @param request 19 | * @return 20 | */ 21 | public static String getIPAddress(ServletRequest request) { 22 | HttpServletRequest httpServletRequest = (HttpServletRequest) request; 23 | String ipAddress = httpServletRequest.getHeader("x-forwarded-for"); 24 | if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { 25 | ipAddress = httpServletRequest.getHeader("Proxy-Client-IP"); 26 | } 27 | if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { 28 | ipAddress = httpServletRequest.getHeader("WL-Proxy-Client-IP"); 29 | } 30 | if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { 31 | ipAddress = httpServletRequest.getRemoteAddr(); 32 | if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) { 33 | // 根据网卡取本机配置的IP 34 | InetAddress inet = null; 35 | try { 36 | inet = InetAddress.getLocalHost(); 37 | } catch (UnknownHostException e) { 38 | e.printStackTrace(); 39 | } 40 | ipAddress = inet.getHostAddress(); 41 | } 42 | } 43 | // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 44 | if (ipAddress != null && ipAddress.length() > 15) { 45 | if (ipAddress.indexOf(",") > 0) { 46 | ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); 47 | } 48 | } 49 | return ipAddress; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/utils/IpUtils.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.utils; 2 | 3 | import com.alibaba.csp.sentinel.util.HostNameUtil; 4 | import org.apache.commons.lang.StringUtils; 5 | 6 | /** 7 | * 8 | * @author: runcoding@163.com 9 | * @date: 2019/07/17 16:17 10 | * @describe: 获取服务器运行ip 11 | **/ 12 | public class IpUtils { 13 | 14 | private static String ip; 15 | 16 | static { 17 | ip = HostNameUtil.getIp(); 18 | } 19 | 20 | public static String getIp() { 21 | return ip; 22 | } 23 | 24 | public static void setIp(String ip) { 25 | if(StringUtils.isBlank(ip)){ 26 | return; 27 | } 28 | IpUtils.ip = ip; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/utils/MethodUtils.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.utils; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.serializer.SerializerFeature; 5 | import org.apache.commons.lang.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * 11 | * @author: runcoding@163.com 12 | * @date: 2019/07/12 10:32 13 | * @describe: 14 | **/ 15 | public class MethodUtils { 16 | 17 | private static Logger logger = LoggerFactory.getLogger(MethodUtils.class); 18 | 19 | /**方法参数*/ 20 | public static String methodArgs(Object[] args){ 21 | if(args == null){ 22 | return ""; 23 | } 24 | try{ 25 | String argJsonStr = JSON.toJSONString(args,SerializerFeature.IgnoreNonFieldGetter); 26 | return StringUtils.length(argJsonStr) > 200 ? StringUtils.substring(argJsonStr,0,200) : argJsonStr; 27 | }catch (Exception e){ 28 | logger.warn(e.getMessage()); 29 | } 30 | return ""; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/utils/PasswordEncoder.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.utils; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 5 | 6 | /** 7 | * @author runcoding 8 | * @date 2019-08-09 9 | * @desc: 密码编码类 10 | */ 11 | public class PasswordEncoder { 12 | 13 | private static BCryptPasswordEncoder passwordEncoder; 14 | 15 | /**密码编码*/ 16 | public static String encode(String password){ 17 | if(StringUtils.isBlank(password)){ 18 | return ""; 19 | } 20 | return getPasswordEncoder().encode(password); 21 | } 22 | 23 | /** 24 | * 密码匹配 25 | * @param rawPassword 原始密码 26 | * @param encodedPassword 加密编号(含有盐) 27 | */ 28 | public static boolean matches(String rawPassword, String encodedPassword){ 29 | return getPasswordEncoder().matches(rawPassword,encodedPassword); 30 | } 31 | 32 | private static BCryptPasswordEncoder getPasswordEncoder() { 33 | if(passwordEncoder != null){ 34 | return passwordEncoder ; 35 | } 36 | synchronized (PasswordEncoder.class){ 37 | if(passwordEncoder != null){ 38 | return passwordEncoder ; 39 | } 40 | passwordEncoder = new BCryptPasswordEncoder(); 41 | } 42 | return passwordEncoder; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/utils/RestTemplateUtils.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.utils; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.runcoding.monitor.dto.Resp; 5 | import com.runcoding.monitor.support.webhook.dingtalk.model.DTResult; 6 | import org.apache.commons.lang.StringUtils; 7 | import org.springframework.http.*; 8 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 9 | import org.springframework.web.client.RestTemplate; 10 | 11 | /** 12 | * 13 | * @author: runcoding@163.com 14 | * @date: 2019/07/04 10:30 15 | * @describe: http utils 16 | **/ 17 | public class RestTemplateUtils { 18 | 19 | 20 | private static RestTemplate restTemplate; 21 | 22 | static { 23 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 24 | factory.setReadTimeout(30000); 25 | factory.setConnectTimeout(3000); 26 | restTemplate = new RestTemplate(factory); 27 | } 28 | 29 | 30 | /**post json请求*/ 31 | public static DTResult postForEntity(String url , String requestBody){ 32 | HttpHeaders headers = new HttpHeaders(); 33 | headers.setContentType(MediaType.APPLICATION_JSON_UTF8); 34 | HttpEntity entity = new HttpEntity(requestBody, headers); 35 | ResponseEntity response = restTemplate.postForEntity(url, entity, String.class); 36 | DTResult resp = JSON.parseObject(response.getBody(), DTResult.class); 37 | return resp; 38 | } 39 | 40 | /**put json请求*/ 41 | public static Resp putForEntity(String url , String authorization, String requestBody){ 42 | HttpHeaders headers = new HttpHeaders(); 43 | headers.setContentType(MediaType.APPLICATION_JSON_UTF8); 44 | if(StringUtils.isNotBlank(authorization)){ 45 | headers.set("Monitor-Authorization", authorization); 46 | } 47 | HttpEntity entity = new HttpEntity(requestBody, headers); 48 | ResponseEntity response = restTemplate.exchange(url,HttpMethod.PUT, entity, String.class); 49 | Resp resp = JSON.parseObject(response.getBody(), Resp.class); 50 | return resp; 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /monitor-spring/src/main/java/com/runcoding/monitor/web/utils/date/DatePattern.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.web.utils.date; 2 | 3 | /** 4 | * @author runcoding 5 | * @desc 常用日期格式 6 | */ 7 | public enum DatePattern { 8 | 9 | SHORT("yyyy-MM-dd"), 10 | 11 | LONG("yyyy-MM-dd HH:mm:ss"), 12 | 13 | FULL("yyyy-MM-dd HH:mm:ss.S"), 14 | 15 | VIRGULE_SHORT("yyyy/MM/dd"), 16 | 17 | VIRGULE_LONG("yyyy/MM/dd HH:mm:ss"), 18 | 19 | VIRGULE_FULL("yyyy/MM/dd HH:mm:ss.S"), 20 | 21 | INT_MINITE("yyyyMMddHHmmss"), 22 | 23 | INT_DATE("yyyyMMdd"), 24 | 25 | YEAR_MONTH_CN("yyyy年MM月"), 26 | 27 | SHORT_CN("yyyy年MM月dd日"), 28 | 29 | LONG_CN("yyyy年MM月dd日 HH时mm分ss秒"), 30 | 31 | FULL_CN("yyyy年MM月dd日 HH时mm分ss秒SSS毫秒"), 32 | 33 | SPOT_DATE("yyyy.MM.dd"), 34 | 35 | YEAR("YYYY"), 36 | 37 | MONTH("MM"), 38 | 39 | DAY("dd"); 40 | 41 | public final String pattern; 42 | 43 | DatePattern(String pattern) { 44 | this.pattern = pattern; 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/base64.min.js: -------------------------------------------------------------------------------- 1 | (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory(global):typeof define==="function"&&define.amd?define(factory):factory(global)})(typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:this,function(global){"use strict";var _Base64=global.Base64;var version="2.4.8";var buffer;if(typeof module!=="undefined"&&module.exports){if(typeof navigator!="undefined"&&navigator.product=="ReactNative"){}else{try{buffer=require("buffer").Buffer}catch(err){}}}var b64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64tab=function(bin){var t={};for(var i=0,l=bin.length;i>>6)+fromCharCode(128|cc&63):fromCharCode(224|cc>>>12&15)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}else{var cc=65536+(c.charCodeAt(0)-55296)*1024+(c.charCodeAt(1)-56320);return fromCharCode(240|cc>>>18&7)+fromCharCode(128|cc>>>12&63)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}};var re_utob=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;var utob=function(u){return u.replace(re_utob,cb_utob)};var cb_encode=function(ccc){var padlen=[0,2,1][ccc.length%3],ord=ccc.charCodeAt(0)<<16|(ccc.length>1?ccc.charCodeAt(1):0)<<8|(ccc.length>2?ccc.charCodeAt(2):0),chars=[b64chars.charAt(ord>>>18),b64chars.charAt(ord>>>12&63),padlen>=2?"=":b64chars.charAt(ord>>>6&63),padlen>=1?"=":b64chars.charAt(ord&63)];return chars.join("")};var btoa=global.btoa?function(b){return global.btoa(b)}:function(b){return b.replace(/[\s\S]{1,3}/g,cb_encode)};var _encode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(u){return(u.constructor===buffer.constructor?u:buffer.from(u)).toString("base64")}:function(u){return(u.constructor===buffer.constructor?u:new buffer(u)).toString("base64")}:function(u){return btoa(utob(u))};var encode=function(u,urisafe){return!urisafe?_encode(String(u)):_encode(String(u)).replace(/[+\/]/g,function(m0){return m0=="+"?"-":"_"}).replace(/=/g,"")};var encodeURI=function(u){return encode(u,true)};var re_btou=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g");var cb_btou=function(cccc){switch(cccc.length){case 4:var cp=(7&cccc.charCodeAt(0))<<18|(63&cccc.charCodeAt(1))<<12|(63&cccc.charCodeAt(2))<<6|63&cccc.charCodeAt(3),offset=cp-65536;return fromCharCode((offset>>>10)+55296)+fromCharCode((offset&1023)+56320);case 3:return fromCharCode((15&cccc.charCodeAt(0))<<12|(63&cccc.charCodeAt(1))<<6|63&cccc.charCodeAt(2));default:return fromCharCode((31&cccc.charCodeAt(0))<<6|63&cccc.charCodeAt(1))}};var btou=function(b){return b.replace(re_btou,cb_btou)};var cb_decode=function(cccc){var len=cccc.length,padlen=len%4,n=(len>0?b64tab[cccc.charAt(0)]<<18:0)|(len>1?b64tab[cccc.charAt(1)]<<12:0)|(len>2?b64tab[cccc.charAt(2)]<<6:0)|(len>3?b64tab[cccc.charAt(3)]:0),chars=[fromCharCode(n>>>16),fromCharCode(n>>>8&255),fromCharCode(n&255)];chars.length-=[0,0,2,1][padlen];return chars.join("")};var atob=global.atob?function(a){return global.atob(a)}:function(a){return a.replace(/[\s\S]{1,4}/g,cb_decode)};var _decode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(a){return(a.constructor===buffer.constructor?a:buffer.from(a,"base64")).toString()}:function(a){return(a.constructor===buffer.constructor?a:new buffer(a,"base64")).toString()}:function(a){return btou(atob(a))};var decode=function(a){return _decode(String(a).replace(/[-_]/g,function(m0){return m0=="-"?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))};var noConflict=function(){var Base64=global.Base64;global.Base64=_Base64;return Base64};global.Base64={VERSION:version,atob:atob,btoa:btoa,fromBase64:decode,toBase64:encode,utob:utob,encode:encode,encodeURI:encodeURI,btou:btou,decode:decode,noConflict:noConflict};if(typeof Object.defineProperty==="function"){var noEnum=function(v){return{value:v,enumerable:false,writable:true,configurable:true}};global.Base64.extendString=function(){Object.defineProperty(String.prototype,"fromBase64",noEnum(function(){return decode(this)}));Object.defineProperty(String.prototype,"toBase64",noEnum(function(urisafe){return encode(this,urisafe)}));Object.defineProperty(String.prototype,"toBase64URI",noEnum(function(){return encode(this,true)}))}}if(global["Meteor"]){Base64=global.Base64}if(typeof module!=="undefined"&&module.exports){module.exports.Base64=global.Base64}else if(typeof define==="function"&&define.amd){define([],function(){return global.Base64})}return{Base64:global.Base64}}); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/codesponsor.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var DEFAULT_OPTIONS = { 5 | theme: 'light', 6 | image: 'show' 7 | }; 8 | 9 | function tpl (id, options) { 10 | var qs = []; 11 | 12 | for (var key in options) { 13 | qs.push((key + "=" + (options[key]))); 14 | } 15 | 16 | var div = Docsify.dom.create('div'); 17 | 18 | Docsify.dom.toggleClass(div, 'codesponsor'); 19 | div.innerHTML = "\n "; 20 | 21 | return div 22 | } 23 | 24 | function appIframe (id, opts) { 25 | var html = tpl(id, opts); 26 | 27 | Docsify.dom.before(Docsify.dom.find('section.content'), html); 28 | } 29 | 30 | function appendStyle () { 31 | Docsify.dom.style("\n .codesponsor {\n position: relative;\n float: right;\n right: 10px;\n top: 10px;\n }\n\n @media screen and (min-width: 1600px) {\n body.sticky .codesponsor {\n position: fixed;\n }\n\n .codesponsor {\n position: absolute;\n bottom: 10px;\n top: auto;\n float: none;\n }\n }\n "); 32 | } 33 | 34 | var install = function (hook, vm) { 35 | var config = vm.config.codesponsor; 36 | var id; 37 | 38 | if (typeof config === 'string') { 39 | id = config; 40 | config = {}; 41 | } else { 42 | id = config.id; 43 | } 44 | 45 | var opts = Docsify.util.merge(DEFAULT_OPTIONS, config); 46 | 47 | if (!id) { 48 | throw Error('codesponsor plugin need id') 49 | } 50 | 51 | if (Docsify.util.isMobile) { 52 | return 53 | } 54 | 55 | // Append style 56 | hook.ready(function () { 57 | appendStyle(); 58 | appIframe(id, opts); 59 | }); 60 | }; 61 | 62 | window.$docsify.plugins = [].concat(install, window.$docsify.plugins); 63 | 64 | }()); 65 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/codesponsor.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function o(o,n){var i=[];for(var e in n)i.push(e+"="+n[e]);var s=Docsify.dom.create("div");return Docsify.dom.toggleClass(s,"codesponsor"),s.innerHTML='\n ',s}function n(n,i){var e=o(n,i);Docsify.dom.before(Docsify.dom.find("section.content"),e)}function i(){Docsify.dom.style("\n .codesponsor {\n position: relative;\n float: right;\n right: 10px;\n top: 10px;\n }\n\n @media screen and (min-width: 1600px) {\n body.sticky .codesponsor {\n position: fixed;\n }\n\n .codesponsor {\n position: absolute;\n bottom: 10px;\n top: auto;\n float: none;\n }\n }\n ")}var e={theme:"light",image:"show"},s=function(o,s){var t,r=s.config.codesponsor;"string"==typeof r?(t=r,r={}):t=r.id;var c=Docsify.util.merge(e,r);if(!t)throw Error("codesponsor plugin need id");Docsify.util.isMobile||o.ready(function(){i(),n(t,c)})};window.$docsify.plugins=[].concat(s,window.$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/disqus.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var fixedPath = location.href.replace('/-/', '/#/'); 5 | if (fixedPath !== location.href) { 6 | location.href = fixedPath; 7 | } 8 | 9 | function install (hook, vm) { 10 | var dom = Docsify.dom; 11 | var disqus = vm.config.disqus; 12 | if (!disqus) { 13 | throw Error('$docsify.disqus is required') 14 | } 15 | 16 | hook.init(function (_) { 17 | var script = dom.create('script'); 18 | 19 | script.async = true; 20 | script.src = "https://" + disqus + ".disqus.com/embed.js"; 21 | script.setAttribute('data-timestamp', +new Date()); 22 | dom.appendTo(dom.body, script); 23 | }); 24 | 25 | hook.mounted(function (_) { 26 | var div = dom.create('div'); 27 | div.id = 'disqus_thread'; 28 | var main = dom.getNode('#main'); 29 | div.style = "width: " + (main.clientWidth) + "px; margin: 0 auto 20px;"; 30 | dom.appendTo(dom.find('.content'), div); 31 | 32 | // eslint-disable-next-line 33 | window.disqus_config = function() { 34 | this.page.url = location.origin + '/-' + vm.route.path; 35 | this.page.identifier = vm.route.path; 36 | this.page.title = document.title; 37 | }; 38 | }); 39 | 40 | hook.doneEach(function (_) { 41 | if (typeof window.DISQUS !== 'undefined') { 42 | window.DISQUS.reset({ 43 | reload: true, 44 | config: function () { 45 | this.page.url = location.origin + '/-' + vm.route.path; 46 | this.page.identifier = vm.route.path; 47 | this.page.title = document.title; 48 | } 49 | }); 50 | } 51 | }); 52 | } 53 | 54 | $docsify.plugins = [].concat(install, $docsify.plugins); 55 | 56 | }()); 57 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/disqus.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function i(i,t){var e=Docsify.dom,o=t.config.disqus;if(!o)throw Error("$docsify.disqus is required");i.init(function(i){var t=e.create("script");t.async=!0,t.src="https://"+o+".disqus.com/embed.js",t.setAttribute("data-timestamp",+new Date),e.appendTo(e.body,t)}),i.mounted(function(i){var o=e.create("div");o.id="disqus_thread";var n=e.getNode("#main");o.style="width: "+n.clientWidth+"px; margin: 0 auto 20px;",e.appendTo(e.find(".content"),o),window.disqus_config=function(){this.page.url=location.origin+"/-"+t.route.path,this.page.identifier=t.route.path,this.page.title=document.title}}),i.doneEach(function(i){void 0!==window.DISQUS&&window.DISQUS.reset({reload:!0,config:function(){this.page.url=location.origin+"/-"+t.route.path,this.page.identifier=t.route.path,this.page.title=document.title}})})}var t=location.href.replace("/-/","/#/");t!==location.href&&(location.href=t),$docsify.plugins=[].concat(i,$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/external-script.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | function handleExternalScript () { 5 | var container = Docsify.dom.getNode('#main'); 6 | var scripts = Docsify.dom.findAll(container, 'script'); 7 | 8 | for (var i = scripts.length; i--;) { 9 | var script = scripts[i]; 10 | 11 | if (script && script.src) { 12 | var newScript = document.createElement('script'); 13 | 14 | Array.prototype.slice.call(script.attributes).forEach(function (attribute) { 15 | newScript[attribute.name] = attribute.value; 16 | }); 17 | 18 | script.parentNode.insertBefore(newScript, script); 19 | script.parentNode.removeChild(script); 20 | } 21 | } 22 | } 23 | 24 | var install = function (hook) { 25 | hook.doneEach(handleExternalScript); 26 | }; 27 | 28 | window.$docsify.plugins = [].concat(install, window.$docsify.plugins); 29 | 30 | }()); 31 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/external-script.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function e(){for(var e=Docsify.dom.getNode("#main"),o=Docsify.dom.findAll(e,"script"),n=o.length;n--;){var i=o[n];if(i&&i.src){var t=document.createElement("script");Array.prototype.slice.call(i.attributes).forEach(function(e){t[e.name]=e.value}),i.parentNode.insertBefore(t,i),i.parentNode.removeChild(i)}}}var o=function(o){o.doneEach(e)};window.$docsify.plugins=[].concat(o,window.$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/front-matter.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function e(e){return{parent:null,length:0,level:e,lines:[],children:[],addChild:function(e){this.children.push(e),e.parent=this,++this.length}}}function n(n){var t,r=g.regLevel,i=g.invalidLine,l=n.split("\n"),u=0,a=0,s=[],f=new e(-1),h=new e(0);f.addChild(h);var c=[],p="";s.push(h),c.push(u);for(var v=0,d=l.length;va){var m=h;h=new e(u),m.addChild(h),s.push(h),c.push(u)}else if(u=0;--E)if(c[E]==u){h=new e(u),s.push(h),c.push(u),null!=s[E].parent&&s[E].parent.addChild(h),w=!0;break}if(!w)return void o.push("Error: Invalid indentation at line "+v+": "+p)}h.lines.push(p.replace(g.trim,"")),a=u}return f}function t(e){e=e.replace(g.trim,"");var n=null;if("true"==e)return!0;if("false"==e)return!1;if(".NaN"==e)return Number.NaN;if("null"==e)return null;if(".inf"==e)return Number.POSITIVE_INFINITY;if("-.inf"==e)return Number.NEGATIVE_INFINITY;if(n=e.match(g.dashesString))return n[1];if(n=e.match(g.quotesString))return n[1];if(n=e.match(g.float))return parseFloat(n[0]);if(n=e.match(g.integer))return parseInt(n[0]);if(isNaN(n=Date.parse(e))){if(n=e.match(g.single_key_value)){var r={};return r[n[1]]=t(n[2]),r}if(n=e.match(g.array)){for(var i=0,l=" ",r=[],u="",a=!1,s=0,f=n[1].length;s0&&r.push(t(u)),r}if(n=e.match(g.map)){for(var i=0,l=" ",r=[],u="",a=!1,s=0,f=n[1].length;s0&&r.push(u);for(var h={},s=0,f=r.length;s"==b[0]?null!=f?f[y]=r(s.shift()):u[y]=r(s.shift()):null!=f?f[y]=t(b):u[y]=t(b)}else null!=f?f[y]=l(s):u[y]=l(s)}else{if(x.match(/^-\s*$/)){v&&(v=!1,void 0===u.length&&(u=[])),null!=f&&u.push(f),f={},v=!0;continue}if(n=x.match(/^-\s*(.*)/)){null!=f?f.push(t(n[1])):(v&&(v=!1,void 0===u.length&&(u=[])),u.push(t(n[1])));continue}}}null!=f&&(v&&(v=!1,void 0===u.length&&(u=[])),u.push(f))}for(var d=p.length-1;d>=0;--d)e.splice.call(e,p[d],1);return u}function u(e){return l(e.children)}function a(e){var n,t=e.split("\n"),r=g.comment;for(var i in t)(n=t[i].match(r))&&void 0!==n[3]&&(t[i]=n[0].substr(0,n[0].length-n[3].length));return t.join("\n")}function s(e){o=[],c=[],p=(new Date).getTime();var t=a(e),r=n(t),i=u(r);return p=(new Date).getTime()-p,i}function f(e){e=e||"";var n=e.split(/(\r?\n)/);return n[0]&&/= yaml =|---/.test(n[0])?h(e):{attributes:{},body:e}}function h(e){var n=v.exec(e);if(!n)return{attributes:{},body:e};var t=n[n.length-1].replace(/^\s+|\s+$/g,"");return{attributes:s(t)||{},body:e.replace(n[0],""),frontmatter:t}}var o=[],c=[],p=0,g={regLevel:new RegExp("^([\\s\\-]+)"),invalidLine:new RegExp("^\\-\\-\\-|^\\.\\.\\.|^\\s*#.*|^\\s*$"),dashesString:new RegExp('^\\s*\\"([^\\"]*)\\"\\s*$'),quotesString:new RegExp("^\\s*\\'([^\\']*)\\'\\s*$"),float:new RegExp("^[+-]?[0-9]+\\.[0-9]+(e[+-]?[0-9]+(\\.[0-9]+)?)?$"),integer:new RegExp("^[+-]?[0-9]+$"),array:new RegExp("\\[\\s*(.*)\\s*\\]"),map:new RegExp("\\{\\s*(.*)\\s*\\}"),key_value:new RegExp("([a-z0-9_-][ a-z0-9_-]*):( .+)","i"),single_key_value:new RegExp("^([a-z0-9_-][ a-z0-9_-]*):( .+?)$","i"),key:new RegExp("([a-z0-9_-][ a-z0-9_-]+):( .+)?","i"),item:new RegExp("^-\\s+"),trim:new RegExp("^\\s+|\\s+$"),comment:new RegExp("([^\\'\\\"#]+([\\'\\\"][^\\'\\\"]*[\\'\\\"])*)*(#.*)?")},v=new RegExp("^(\\ufeff?(= yaml =|---)$([\\s\\S]*?)(?:\\2|\\.\\.\\.)$(?:\\n)?)","m"),d=function(e,n){e.beforeEach(function(e){var t=f(e),r=t.attributes,i=t.body;return Docsify.util.merge(n.config,r.config),i})};$docsify.plugins=[].concat(d,$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/ga.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // From https://github.com/egoist/vue-ga/blob/master/src/index.js 5 | function appendScript () { 6 | var script = document.createElement('script'); 7 | script.async = true; 8 | script.src = 'https://www.google-analytics.com/analytics.js'; 9 | document.body.appendChild(script); 10 | } 11 | 12 | function init (id) { 13 | appendScript(); 14 | window.ga = 15 | window.ga || 16 | function () { 17 | (window.ga.q = window.ga.q || []).push(arguments); 18 | }; 19 | window.ga.l = Number(new Date()); 20 | window.ga('create', id, 'auto'); 21 | } 22 | 23 | function collect () { 24 | if (!window.ga) { 25 | init($docsify.ga); 26 | } 27 | 28 | window.ga('set', 'page', location.hash); 29 | window.ga('send', 'pageview'); 30 | } 31 | 32 | var install = function (hook) { 33 | if (!$docsify.ga) { 34 | console.error('[Docsify] ga is required.'); 35 | return 36 | } 37 | 38 | hook.beforeEach(collect); 39 | }; 40 | 41 | $docsify.plugins = [].concat(install, $docsify.plugins); 42 | 43 | }()); 44 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/ga.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function n(){var n=document.createElement("script");n.async=!0,n.src="https://www.google-analytics.com/analytics.js",document.body.appendChild(n)}function o(o){n(),window.ga=window.ga||function(){(window.ga.q=window.ga.q||[]).push(arguments)},window.ga.l=Number(new Date),window.ga("create",o,"auto")}function i(){window.ga||o($docsify.ga),window.ga("set","page",location.hash),window.ga("send","pageview")}var a=function(n){if(!$docsify.ga)return void console.error("[Docsify] ga is required.");n.beforeEach(i)};$docsify.plugins=[].concat(a,$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/gitalk.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | function install (hook, vm) { 5 | var dom = Docsify.dom; 6 | 7 | hook.mounted(function (_) { 8 | var div = dom.create('div'); 9 | div.id = 'gitalk-container'; 10 | var main = dom.getNode('#main'); 11 | div.style = "width: " + (main.clientWidth) + "px; margin: 0 auto 20px;"; 12 | dom.appendTo(dom.find('.content'), div); 13 | var script = dom.create('script'); 14 | var content = "gitalk.render('gitalk-container')"; 15 | script.textContent = content; 16 | dom.appendTo(dom.body, script); 17 | }); 18 | } 19 | 20 | $docsify.plugins = [].concat(install, $docsify.plugins); 21 | 22 | }()); 23 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/gitalk.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function t(t,n){var i=Docsify.dom;t.mounted(function(t){var n=i.create("div");n.id="gitalk-container";var e=i.getNode("#main");n.style="width: "+e.clientWidth+"px; margin: 0 auto 20px;",i.appendTo(i.find(".content"),n);var o=i.create("script");o.textContent="gitalk.render('gitalk-container')",i.appendTo(i.body,o)})}$docsify.plugins=[].concat(t,$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/google-analytics.js: -------------------------------------------------------------------------------- 1 | this.D = this.D || {}; 2 | (function () { 3 | 'use strict'; 4 | 5 | // From https://github.com/egoist/vue-ga/blob/master/src/index.js 6 | function appendScript () { 7 | var script = document.createElement('script'); 8 | script.async = true; 9 | script.src = 'https://www.google-analytics.com/analytics.js'; 10 | document.body.appendChild(script); 11 | } 12 | 13 | function init (id) { 14 | appendScript(); 15 | window.ga = window.ga || function () { 16 | (window.ga.q = window.ga.q || []).push(arguments); 17 | }; 18 | window.ga.l = Number(new Date()); 19 | window.ga('create', id, 'auto'); 20 | } 21 | 22 | function collect () { 23 | if (!window.ga) { 24 | init($docsify.ga); 25 | } 26 | 27 | window.ga('set', 'page', location.hash); 28 | window.ga('send', 'pageview'); 29 | } 30 | 31 | var install = function (hook) { 32 | if (!$docsify.ga) { 33 | console.error('[Docsify] ga is required.'); 34 | return 35 | } 36 | 37 | hook.beforeEach(collect); 38 | }; 39 | 40 | $docsify.plugins = [].concat(install, $docsify.plugins); 41 | 42 | }()); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-bash.min.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b-?(?:0x[\dA-Fa-f]+|\d*\.?\d+(?:[Ee]-?\d+)?)\b/,operator:/--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\([^)]+\)|`[^`]+`/,inside:{variable:/^\$\(|^`|\)$|`$/}},/\$(?:[a-z0-9_#\?\*!@]+|\{[^}]+\})/i]};e.languages.bash={shebang:{pattern:/^#!\s*\/bin\/bash|^#!\s*\/bin\/sh/,alias:"important"},comment:{pattern:/(^|[^"{\\])#.*/,lookbehind:!0},string:[{pattern:/((?:^|[^<])<<\s*)(?:"|')?(\w+?)(?:"|')?\s*\r?\n(?:[\s\S])*?\r?\n\2/g,lookbehind:!0,greedy:!0,inside:t},{pattern:/(["'])(?:\\\\|\\?[^\\])*?\1/g,greedy:!0,inside:t}],variable:t.variable,"function":{pattern:/(^|\s|;|\||&)(?:alias|apropos|apt-get|aptitude|aspell|awk|basename|bash|bc|bg|builtin|bzip2|cal|cat|cd|cfdisk|chgrp|chmod|chown|chroot|chkconfig|cksum|clear|cmp|comm|command|cp|cron|crontab|csplit|cut|date|dc|dd|ddrescue|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|enable|env|ethtool|eval|exec|expand|expect|export|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|getopts|git|grep|groupadd|groupdel|groupmod|groups|gzip|hash|head|help|hg|history|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|jobs|join|kill|killall|less|link|ln|locate|logname|logout|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|make|man|mkdir|mkfifo|mkisofs|mknod|more|most|mount|mtools|mtr|mv|mmv|nano|netstat|nice|nl|nohup|notify-send|npm|nslookup|open|op|passwd|paste|pathchk|ping|pkill|popd|pr|printcap|printenv|printf|ps|pushd|pv|pwd|quota|quotacheck|quotactl|ram|rar|rcp|read|readarray|readonly|reboot|rename|renice|remsync|rev|rm|rmdir|rsync|screen|scp|sdiff|sed|seq|service|sftp|shift|shopt|shutdown|sleep|slocate|sort|source|split|ssh|stat|strace|su|sudo|sum|suspend|sync|tail|tar|tee|test|time|timeout|times|touch|top|traceroute|trap|tr|tsort|tty|type|ulimit|umask|umount|unalias|uname|unexpand|uniq|units|unrar|unshar|uptime|useradd|userdel|usermod|users|uuencode|uudecode|v|vdir|vi|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yes|zip)(?=$|\s|;|\||&)/,lookbehind:!0},keyword:{pattern:/(^|\s|;|\||&)(?:let|:|\.|if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)(?=$|\s|;|\||&)/,lookbehind:!0},"boolean":{pattern:/(^|\s|;|\||&)(?:true|false)(?=$|\s|;|\||&)/,lookbehind:!0},operator:/&&?|\|\|?|==?|!=?|<<>|<=?|>=?|=~/,punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];]/};var a=t.variable[1].inside;a["function"]=e.languages.bash["function"],a.keyword=e.languages.bash.keyword,a.boolean=e.languages.bash.boolean,a.operator=e.languages.bash.operator,a.punctuation=e.languages.bash.punctuation}(Prism); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-basic.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.basic={string:/"(?:""|[!#$%&'()*,\/:;<=>?^_ +\-.A-Z\d])*"/i,comment:{pattern:/(?:!|REM\b).+/i,inside:{keyword:/^REM/i}},number:/(?:\b|\B[.-])(?:\d+\.?\d*)(?:E[+-]?\d+)?/i,keyword:/\b(?:AS|BEEP|BLOAD|BSAVE|CALL(?: ABSOLUTE)?|CASE|CHAIN|CHDIR|CLEAR|CLOSE|CLS|COM|COMMON|CONST|DATA|DECLARE|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DIM|DO|DOUBLE|ELSE|ELSEIF|END|ENVIRON|ERASE|ERROR|EXIT|FIELD|FILES|FOR|FUNCTION|GET|GOSUB|GOTO|IF|INPUT|INTEGER|IOCTL|KEY|KILL|LINE INPUT|LOCATE|LOCK|LONG|LOOP|LSET|MKDIR|NAME|NEXT|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPEN|OPTION BASE|OUT|POKE|PUT|READ|REDIM|REM|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SHARED|SINGLE|SELECT CASE|SHELL|SLEEP|STATIC|STEP|STOP|STRING|SUB|SWAP|SYSTEM|THEN|TIMER|TO|TROFF|TRON|TYPE|UNLOCK|UNTIL|USING|VIEW PRINT|WAIT|WEND|WHILE|WRITE)(?:\$|\b)/i,"function":/\b(?:ABS|ACCESS|ACOS|ANGLE|AREA|ARITHMETIC|ARRAY|ASIN|ASK|AT|ATN|BASE|BEGIN|BREAK|CAUSE|CEIL|CHR|CLIP|COLLATE|COLOR|CON|COS|COSH|COT|CSC|DATE|DATUM|DEBUG|DECIMAL|DEF|DEG|DEGREES|DELETE|DET|DEVICE|DISPLAY|DOT|ELAPSED|EPS|ERASABLE|EXLINE|EXP|EXTERNAL|EXTYPE|FILETYPE|FIXED|FP|GO|GRAPH|HANDLER|IDN|IMAGE|IN|INT|INTERNAL|IP|IS|KEYED|LBOUND|LCASE|LEFT|LEN|LENGTH|LET|LINE|LINES|LOG|LOG10|LOG2|LTRIM|MARGIN|MAT|MAX|MAXNUM|MID|MIN|MISSING|MOD|NATIVE|NUL|NUMERIC|OF|OPTION|ORD|ORGANIZATION|OUTIN|OUTPUT|PI|POINT|POINTER|POINTS|POS|PRINT|PROGRAM|PROMPT|RAD|RADIANS|RANDOMIZE|RECORD|RECSIZE|RECTYPE|RELATIVE|REMAINDER|REPEAT|REST|RETRY|REWRITE|RIGHT|RND|ROUND|RTRIM|SAME|SEC|SELECT|SEQUENTIAL|SET|SETTER|SGN|SIN|SINH|SIZE|SKIP|SQR|STANDARD|STATUS|STR|STREAM|STYLE|TAB|TAN|TANH|TEMPLATE|TEXT|THERE|TIME|TIMEOUT|TRACE|TRANSFORM|TRUNCATE|UBOUND|UCASE|USE|VAL|VARIABLE|VIEWPORT|WHEN|WINDOW|WITH|ZER|ZONEWIDTH)(?:\$|\b)/i,operator:/<[=>]?|>=?|[+\-*\/^=&]|\b(?:AND|EQV|IMP|NOT|OR|XOR)\b/i,punctuation:/[,;:()]/}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-c.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.c=Prism.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0},directive:{pattern:/(#\s*)\b(define|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/,lookbehind:!0,alias:"keyword"}}},constant:/\b(__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|stdin|stdout|stderr)\b/}),delete Prism.languages.c["class-name"],delete Prism.languages.c["boolean"]; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-coffeescript.min.js: -------------------------------------------------------------------------------- 1 | !function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\?[^\\])*?'/,greedy:!0},{pattern:/"(?:\\?[^\\])*?"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\?[\s\S])*?`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},rest:e.languages.javascript}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"]}(Prism); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-css.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:{pattern:/("|')(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-django.min.js: -------------------------------------------------------------------------------- 1 | var _django_template={property:{pattern:/(?:{{|{%)[\s\S]*?(?:%}|}})/g,greedy:!0,inside:{string:{pattern:/("|')(?:\\\\|\\?[^\\\r\n])*?\1/,greedy:!0},keyword:/\b(?:\||load|verbatim|widthratio|ssi|firstof|for|url|ifchanged|csrf_token|lorem|ifnotequal|autoescape|now|templatetag|debug|cycle|ifequal|regroup|comment|filter|endfilter|if|spaceless|with|extends|block|include|else|empty|endif|endfor|as|endblock|endautoescape|endverbatim|trans|endtrans|[Tt]rue|[Ff]alse|[Nn]one|in|is|static|macro|endmacro|call|endcall|set|endset|raw|endraw)\b/,operator:/[-+=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/,"function":/\b(?:_|abs|add|addslashes|attr|batch|callable|capfirst|capitalize|center|count|cut|d|date|default|default_if_none|defined|dictsort|dictsortreversed|divisibleby|e|equalto|escape|escaped|escapejs|even|filesizeformat|first|float|floatformat|force_escape|forceescape|format|get_digit|groupby|indent|int|iriencode|iterable|join|last|length|length_is|linebreaks|linebreaksbr|linenumbers|list|ljust|lower|make_list|map|mapping|number|odd|phone2numeric|pluralize|pprint|random|reject|rejectattr|removetags|replace|reverse|rjust|round|safe|safeseq|sameas|select|selectattr|sequence|slice|slugify|sort|string|stringformat|striptags|sum|time|timesince|timeuntil|title|trim|truncate|truncatechars|truncatechars_html|truncatewords|truncatewords_html|undefined|unordered_list|upper|urlencode|urlize|urlizetrunc|wordcount|wordwrap|xmlattr|yesno)\b/,important:/\b-?\d+(?:\.\d+)?\b/,variable:/\b\w+?\b/,punctuation:/[[\];(),.:]/}}};Prism.languages.django=Prism.languages.extend("markup",{comment:/(?:)/}),Prism.languages.django.tag.pattern=/<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\s\S])*\1|[^>=]+))?)*\s*\/?>/i,Prism.languages.insertBefore("django","entity",_django_template),Prism.languages.insertBefore("inside","tag",_django_template,Prism.languages.django.tag),Prism.languages.javascript&&(Prism.languages.insertBefore("inside","string",_django_template,Prism.languages.django.script),Prism.languages.django.script.inside.string.inside=_django_template),Prism.languages.css&&(Prism.languages.insertBefore("inside","atrule",{tag:_django_template.property},Prism.languages.django.style),Prism.languages.django.style.inside.string.inside=_django_template),Prism.languages.jinja2=Prism.languages.django; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-docker.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.docker={keyword:{pattern:/(^\s*)(?:ONBUILD|FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|COPY|VOLUME|USER|WORKDIR|CMD|LABEL|ENTRYPOINT)(?=\s)/im,lookbehind:!0},string:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*?\1/,comment:/#.*/,punctuation:/---|\.\.\.|[:[\]{}\-,|>?]/}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-erlang.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.erlang={comment:/%.+/,string:{pattern:/"(?:\\?.)*?"/,greedy:!0},"quoted-function":{pattern:/'(?:\\.|[^'\\])+'(?=\()/,alias:"function"},"quoted-atom":{pattern:/'(?:\\.|[^'\\])+'/,alias:"atom"},"boolean":/\b(?:true|false)\b/,keyword:/\b(?:fun|when|case|of|end|if|receive|after|try|catch)\b/,number:[/\$\\?./,/\d+#[a-z0-9]+/i,/(?:\b|-)\d*\.?\d+([Ee][+-]?\d+)?\b/],"function":/\b[a-z][\w@]*(?=\()/,variable:{pattern:/(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,lookbehind:!0},operator:[/[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:bnot|div|rem|band|bor|bxor|bsl|bsr|not|and|or|xor|orelse|andalso)\b/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],atom:/\b[a-z][\w@]*/,punctuation:/[()[\]{}:;,.#|]|<<|>>/}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-go.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.go=Prism.languages.extend("clike",{keyword:/\b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,builtin:/\b(bool|byte|complex(64|128)|error|float(32|64)|rune|string|u?int(8|16|32|64|)|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(ln)?|real|recover)\b/,"boolean":/\b(_|iota|nil|true|false)\b/,operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,number:/\b(-?(0x[a-f\d]+|(\d+\.?\d*|\.\d+)(e[-+]?\d+)?)i?)\b/i,string:{pattern:/("|'|`)(\\?.|\r|\n)*?\1/,greedy:!0}}),delete Prism.languages.go["class-name"]; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-groovy.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.groovy=Prism.languages.extend("clike",{keyword:/\b(as|def|in|abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|trait|transient|try|void|volatile|while)\b/,string:[{pattern:/("""|''')[\s\S]*?\1|(\$\/)(\$\/\$|[\s\S])*?\/\$/,greedy:!0},{pattern:/("|'|\/)(?:\\?.)*?\1/,greedy:!0}],number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?[\d]+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.{1,2}(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),Prism.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment"}}),Prism.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(setup|given|when|then|and|cleanup|expect|where):/}),Prism.languages.insertBefore("groovy","function",{annotation:{alias:"punctuation",pattern:/(^|[^.])@\w+/,lookbehind:!0}}),Prism.hooks.add("wrap",function(e){if("groovy"===e.language&&"string"===e.type){var t=e.content[0];if("'"!=t){var n=/([^\\])(\$(\{.*?\}|[\w\.]+))/;"$"===t&&(n=/([^\$])(\$(\{.*?\}|[\w\.]+))/),e.content=e.content.replace(/</g,"<").replace(/&/g,"&"),e.content=Prism.highlight(e.content,{expression:{pattern:n,lookbehind:!0,inside:Prism.languages.groovy}}),e.classes.push("/"===t?"regex":"gstring")}}}); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-http.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.http={"request-line":{pattern:/^(POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\b\shttps?:\/\/\S+\sHTTP\/[0-9.]+/m,inside:{property:/^(POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\b/,"attr-name":/:\w+/}},"response-status":{pattern:/^HTTP\/1.[01] \d+.*/m,inside:{property:{pattern:/(^HTTP\/1.[01] )\d+.*/i,lookbehind:!0}}},"header-name":{pattern:/^[\w-]+:(?=.)/m,alias:"keyword"}};var httpLanguages={"application/json":Prism.languages.javascript,"application/xml":Prism.languages.markup,"text/xml":Prism.languages.markup,"text/html":Prism.languages.markup};for(var contentType in httpLanguages)if(httpLanguages[contentType]){var options={};options[contentType]={pattern:new RegExp("(content-type:\\s*"+contentType+"[\\w\\W]*?)(?:\\r?\\n|\\r){2}[\\w\\W]*","i"),lookbehind:!0,inside:{rest:httpLanguages[contentType]}},Prism.languages.insertBefore("http","header-name",options)} -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-icon.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.icon={comment:/#.*/,string:{pattern:/(["'])(?:(?!\1)[^\\\r\n]|\\.|_(?:\r?\n|\r))*\1/,greedy:!0},number:/\b(?:\d+r[a-z\d]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)\b|\.\d+\b/i,"builtin-keyword":{pattern:/&(?:allocated|ascii|clock|collections|cset|current|date|dateline|digits|dump|e|error(?:number|text|value)?|errout|fail|features|file|host|input|lcase|letters|level|line|main|null|output|phi|pi|pos|progname|random|regions|source|storage|subject|time|trace|ucase|version)\b/,alias:"variable"},directive:{pattern:/\$\w+/,alias:"builtin"},keyword:/\b(?:break|by|case|create|default|do|else|end|every|fail|global|if|initial|invocable|link|local|next|not|of|procedure|record|repeat|return|static|suspend|then|to|until|while)\b/,"function":/(?!\d)\w+(?=\s*[({]|\s*!\s*\[)/,operator:/[+-]:(?!=)|(?:[\/?@^%&]|\+\+?|--?|==?=?|~==?=?|\*\*?|\|\|\|?|<(?:->?|>?=?)(?::=)?|:(?:=:?)?|[!.\\|~]/,punctuation:/[\[\](){},;]/}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-java.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.java=Prism.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i,operator:{pattern:/(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,lookbehind:!0}}),Prism.languages.insertBefore("java","function",{annotation:{alias:"punctuation",pattern:/(^|[^.])@\w+/,lookbehind:!0}}); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-javascript.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0}}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\\\|\\?[^\\])*?`/,greedy:!0,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-json.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.json={property:/"(?:\\.|[^\\"])*"(?=\s*:)/gi,string:/"(?!:)(?:\\.|[^\\"])*"(?!:)/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?)\b/g,punctuation:/[{}[\]);,]/g,operator:/:/g,"boolean":/\b(true|false)\b/gi,"null":/\bnull\b/gi},Prism.languages.jsonp=Prism.languages.json; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-jsx.min.js: -------------------------------------------------------------------------------- 1 | !function(a){var e=a.util.clone(a.languages.javascript);a.languages.jsx=a.languages.extend("markup",e),a.languages.jsx.tag.pattern=/<\/?[\w\.:-]+\s*(?:\s+(?:[\w\.:-]+(?:=(?:("|')(\\?[\s\S])*?\1|[^\s'">=]+|(\{[\s\S]*?\})))?|\{\.{3}\w+\})\s*)*\/?>/i,a.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:('|")[\s\S]*?(\1)|[^\s>]+)/i,a.languages.insertBefore("inside","attr-name",{spread:{pattern:/\{\.{3}\w+\}/,inside:{punctuation:/\{|\}|\./,"attr-value":/\w+/}}},a.languages.jsx.tag);var s=a.util.clone(a.languages.jsx);delete s.punctuation,s=a.languages.insertBefore("jsx","operator",{punctuation:/=(?={)|[{}[\];(),.:]/},{jsx:s}),a.languages.insertBefore("inside","attr-value",{script:{pattern:/=(\{(?:\{[^}]*\}|[^}])+\})/i,inside:s,alias:"language-javascript"}},a.languages.jsx.tag)}(Prism); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-keyman.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.keyman={comment:/\bc\s.*/i,"function":/\[\s*((CTRL|SHIFT|ALT|LCTRL|RCTRL|LALT|RALT|CAPS|NCAPS)\s+)*([TKU]_[a-z0-9_?]+|".+?"|'.+?')\s*\]/i,string:/("|')((?!\1).)*\1/,bold:[/&(baselayout|bitmap|capsononly|capsalwaysoff|shiftfreescaps|copyright|ethnologuecode|hotkey|includecodes|keyboardversion|kmw_embedcss|kmw_embedjs|kmw_helpfile|kmw_helptext|kmw_rtl|language|layer|layoutfile|message|mnemoniclayout|name|oldcharposmatching|platform|targets|version|visualkeyboard|windowslanguages)\b/i,/\b(bitmap|bitmaps|caps on only|caps always off|shift frees caps|copyright|hotkey|language|layout|message|name|version)\b/i],keyword:/\b(any|baselayout|beep|call|context|deadkey|dk|if|index|layer|notany|nul|outs|platform|return|reset|save|set|store|use)\b/i,atrule:/\b(ansi|begin|unicode|group|using keys|match|nomatch)\b/i,number:/\b(U\+[\dA-F]+|d\d+|x[\da-f]+|\d+)\b/i,operator:/[+>\\,()]/,tag:/\$(keyman|kmfl|weaver|keymanweb|keymanonly):/i}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-kotlin.min.js: -------------------------------------------------------------------------------- 1 | !function(n){n.languages.kotlin=n.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|else|enum|final|finally|for|fun|get|if|import|in|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|out|override|package|private|protected|public|reified|return|sealed|set|super|tailrec|this|throw|to|try|val|var|when|where|while)\b/,lookbehind:!0},"function":[/\w+(?=\s*\()/,{pattern:/(\.)\w+(?=\s*\{)/,lookbehind:!0}],number:/\b(?:0[bx][\da-fA-F]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete n.languages.kotlin["class-name"],n.languages.insertBefore("kotlin","string",{"raw-string":{pattern:/(["'])\1\1[\s\S]*?\1{3}/,alias:"string"}}),n.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),n.languages.insertBefore("kotlin","function",{label:{pattern:/\w+@|@\w+/,alias:"symbol"}});var e=[{pattern:/\$\{[^}]+\}/,inside:{delimiter:{pattern:/^\$\{|\}$/,alias:"variable"},rest:n.util.clone(n.languages.kotlin)}},{pattern:/\$\w+/,alias:"variable"}];n.languages.kotlin.string.inside=n.languages.kotlin["raw-string"].inside={interpolation:e}}(Prism); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-less.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.less=Prism.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-]+?(?:\([^{}]+\)|[^(){};])*?(?=\s*\{)/i,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\([^{}]*\)|[^{};@])*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/i,punctuation:/[{}();:,]/,operator:/[+\-*\/]/}),Prism.languages.insertBefore("less","punctuation",{"function":Prism.languages.less.function}),Prism.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-]+.*?(?=[(;])/,lookbehind:!0,alias:"function"}}); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-makefile.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|.)*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},builtin:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,symbol:{pattern:/^[^:=\r\n]+(?=\s*:(?!=))/m,inside:{variable:/\$+(?:[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:[/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,{pattern:/(\()(?:addsuffix|abspath|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:s|list)?)(?=[ \t])/,lookbehind:!0}],operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-markdown.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"}],title:[{pattern:/\w+.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^[*_]|[*_]$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.italic.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.bold.inside.italic=Prism.util.clone(Prism.languages.markdown.italic),Prism.languages.markdown.italic.inside.bold=Prism.util.clone(Prism.languages.markdown.bold); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-nginx.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.nginx=Prism.languages.extend("clike",{comment:{pattern:/(^|[^"{\\])#.*/,lookbehind:!0},keyword:/\b(?:CONTENT_|DOCUMENT_|GATEWAY_|HTTP_|HTTPS|if_not_empty|PATH_|QUERY_|REDIRECT_|REMOTE_|REQUEST_|SCGI|SCRIPT_|SERVER_|http|server|events|location|include|accept_mutex|accept_mutex_delay|access_log|add_after_body|add_before_body|add_header|addition_types|aio|alias|allow|ancient_browser|ancient_browser_value|auth|auth_basic|auth_basic_user_file|auth_http|auth_http_header|auth_http_timeout|autoindex|autoindex_exact_size|autoindex_localtime|break|charset|charset_map|charset_types|chunked_transfer_encoding|client_body_buffer_size|client_body_in_file_only|client_body_in_single_buffer|client_body_temp_path|client_body_timeout|client_header_buffer_size|client_header_timeout|client_max_body_size|connection_pool_size|create_full_put_path|daemon|dav_access|dav_methods|debug_connection|debug_points|default_type|deny|devpoll_changes|devpoll_events|directio|directio_alignment|disable_symlinks|empty_gif|env|epoll_events|error_log|error_page|expires|fastcgi_buffer_size|fastcgi_buffers|fastcgi_busy_buffers_size|fastcgi_cache|fastcgi_cache_bypass|fastcgi_cache_key|fastcgi_cache_lock|fastcgi_cache_lock_timeout|fastcgi_cache_methods|fastcgi_cache_min_uses|fastcgi_cache_path|fastcgi_cache_purge|fastcgi_cache_use_stale|fastcgi_cache_valid|fastcgi_connect_timeout|fastcgi_hide_header|fastcgi_ignore_client_abort|fastcgi_ignore_headers|fastcgi_index|fastcgi_intercept_errors|fastcgi_keep_conn|fastcgi_max_temp_file_size|fastcgi_next_upstream|fastcgi_no_cache|fastcgi_param|fastcgi_pass|fastcgi_pass_header|fastcgi_read_timeout|fastcgi_redirect_errors|fastcgi_send_timeout|fastcgi_split_path_info|fastcgi_store|fastcgi_store_access|fastcgi_temp_file_write_size|fastcgi_temp_path|flv|geo|geoip_city|geoip_country|google_perftools_profiles|gzip|gzip_buffers|gzip_comp_level|gzip_disable|gzip_http_version|gzip_min_length|gzip_proxied|gzip_static|gzip_types|gzip_vary|if|if_modified_since|ignore_invalid_headers|image_filter|image_filter_buffer|image_filter_jpeg_quality|image_filter_sharpen|image_filter_transparency|imap_capabilities|imap_client_buffer|include|index|internal|ip_hash|keepalive|keepalive_disable|keepalive_requests|keepalive_timeout|kqueue_changes|kqueue_events|large_client_header_buffers|limit_conn|limit_conn_log_level|limit_conn_zone|limit_except|limit_rate|limit_rate_after|limit_req|limit_req_log_level|limit_req_zone|limit_zone|lingering_close|lingering_time|lingering_timeout|listen|location|lock_file|log_format|log_format_combined|log_not_found|log_subrequest|map|map_hash_bucket_size|map_hash_max_size|master_process|max_ranges|memcached_buffer_size|memcached_connect_timeout|memcached_next_upstream|memcached_pass|memcached_read_timeout|memcached_send_timeout|merge_slashes|min_delete_depth|modern_browser|modern_browser_value|mp4|mp4_buffer_size|mp4_max_buffer_size|msie_padding|msie_refresh|multi_accept|open_file_cache|open_file_cache_errors|open_file_cache_min_uses|open_file_cache_valid|open_log_file_cache|optimize_server_names|override_charset|pcre_jit|perl|perl_modules|perl_require|perl_set|pid|pop3_auth|pop3_capabilities|port_in_redirect|post_action|postpone_output|protocol|proxy|proxy_buffer|proxy_buffer_size|proxy_buffering|proxy_buffers|proxy_busy_buffers_size|proxy_cache|proxy_cache_bypass|proxy_cache_key|proxy_cache_lock|proxy_cache_lock_timeout|proxy_cache_methods|proxy_cache_min_uses|proxy_cache_path|proxy_cache_use_stale|proxy_cache_valid|proxy_connect_timeout|proxy_cookie_domain|proxy_cookie_path|proxy_headers_hash_bucket_size|proxy_headers_hash_max_size|proxy_hide_header|proxy_http_version|proxy_ignore_client_abort|proxy_ignore_headers|proxy_intercept_errors|proxy_max_temp_file_size|proxy_method|proxy_next_upstream|proxy_no_cache|proxy_pass|proxy_pass_error_message|proxy_pass_header|proxy_pass_request_body|proxy_pass_request_headers|proxy_read_timeout|proxy_redirect|proxy_redirect_errors|proxy_send_lowat|proxy_send_timeout|proxy_set_body|proxy_set_header|proxy_ssl_session_reuse|proxy_store|proxy_store_access|proxy_temp_file_write_size|proxy_temp_path|proxy_timeout|proxy_upstream_fail_timeout|proxy_upstream_max_fails|random_index|read_ahead|real_ip_header|recursive_error_pages|request_pool_size|reset_timedout_connection|resolver|resolver_timeout|return|rewrite|root|rtsig_overflow_events|rtsig_overflow_test|rtsig_overflow_threshold|rtsig_signo|satisfy|satisfy_any|secure_link_secret|send_lowat|send_timeout|sendfile|sendfile_max_chunk|server|server_name|server_name_in_redirect|server_names_hash_bucket_size|server_names_hash_max_size|server_tokens|set|set_real_ip_from|smtp_auth|smtp_capabilities|so_keepalive|source_charset|split_clients|ssi|ssi_silent_errors|ssi_types|ssi_value_length|ssl|ssl_certificate|ssl_certificate_key|ssl_ciphers|ssl_client_certificate|ssl_crl|ssl_dhparam|ssl_engine|ssl_prefer_server_ciphers|ssl_protocols|ssl_session_cache|ssl_session_timeout|ssl_verify_client|ssl_verify_depth|starttls|stub_status|sub_filter|sub_filter_once|sub_filter_types|tcp_nodelay|tcp_nopush|timeout|timer_resolution|try_files|types|types_hash_bucket_size|types_hash_max_size|underscores_in_headers|uninitialized_variable_warn|upstream|use|user|userid|userid_domain|userid_expires|userid_name|userid_p3p|userid_path|userid_service|valid_referers|variables_hash_bucket_size|variables_hash_max_size|worker_connections|worker_cpu_affinity|worker_priority|worker_processes|worker_rlimit_core|worker_rlimit_nofile|worker_rlimit_sigpending|working_directory|xclient|xml_entities|xslt_entities|xslt_stylesheet|xslt_types)\b/i}),Prism.languages.insertBefore("nginx","keyword",{variable:/\$[a-z_]+/i}); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-objectivec.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.objectivec=Prism.languages.extend("c",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while|in|self|super)\b|(@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,string:/("|')(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|@"(\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-php.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.php=Prism.languages.extend("clike",{keyword:/\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i,constant:/\b[A-Z0-9_]{2,}\b/,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0}}),Prism.languages.insertBefore("php","class-name",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),Prism.languages.insertBefore("php","keyword",{delimiter:{pattern:/\?>|<\?(?:php|=)?/i,alias:"important"},variable:/\$\w+\b/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),Prism.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}}),Prism.languages.markup&&(Prism.hooks.add("before-highlight",function(e){"php"===e.language&&/(?:<\?php|<\?)/gi.test(e.code)&&(e.tokenStack=[],e.backupCode=e.code,e.code=e.code.replace(/(?:<\?php|<\?)[\s\S]*?(?:\?>|$)/gi,function(a){for(var n=e.tokenStack.length;-1!==e.backupCode.indexOf("___PHP"+n+"___");)++n;return e.tokenStack[n]=a,"___PHP"+n+"___"}),e.grammar=Prism.languages.markup)}),Prism.hooks.add("before-insert",function(e){"php"===e.language&&e.backupCode&&(e.code=e.backupCode,delete e.backupCode)}),Prism.hooks.add("after-highlight",function(e){if("php"===e.language&&e.tokenStack){e.grammar=Prism.languages.php;for(var a=0,n=Object.keys(e.tokenStack);a'+Prism.highlight(r,e.grammar,"php").replace(/\$/g,"$$$$")+"")}e.element.innerHTML=e.highlightedCode}})); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-ruby.min.js: -------------------------------------------------------------------------------- 1 | !function(e){e.languages.ruby=e.languages.extend("clike",{comment:[/#(?!\{[^\r\n]*?\}).*/,/^=begin(?:\r?\n|\r)(?:.*(?:\r?\n|\r))*?=end/m],keyword:/\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/});var n={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:e.util.clone(e.languages.ruby)}};e.languages.insertBefore("ruby","keyword",{regex:[{pattern:/%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/,greedy:!0,inside:{interpolation:n}},{pattern:/%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,greedy:!0,inside:{interpolation:n}},{pattern:/%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,greedy:!0,inside:{interpolation:n}},{pattern:/%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,greedy:!0,inside:{interpolation:n}},{pattern:/%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,greedy:!0,inside:{interpolation:n}},{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0}],variable:/[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/,symbol:/:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/}),e.languages.insertBefore("ruby","number",{builtin:/\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/}),e.languages.ruby.string=[{pattern:/%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,greedy:!0,inside:{interpolation:n}},{pattern:/%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,greedy:!0,inside:{interpolation:n}},{pattern:/("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/,greedy:!0,inside:{interpolation:n}}]}(Prism); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-scala.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.scala=Prism.languages.extend("java",{keyword:/<-|=>|\b(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|null|object|override|package|private|protected|return|sealed|self|super|this|throw|trait|try|type|val|var|while|with|yield)\b/,string:[{pattern:/"""[\s\S]*?"""/,greedy:!0},{pattern:/("|')(?:\\\\|\\?[^\\\r\n])*?\1/,greedy:!0}],builtin:/\b(?:String|Int|Long|Short|Byte|Boolean|Double|Float|Char|Any|AnyRef|AnyVal|Unit|Nothing)\b/,number:/\b(?:0x[\da-f]*\.?[\da-f]+|\d*\.?\d+e?\d*[dfl]?)\b/i,symbol:/'[^\d\s\\]\w*/}),delete Prism.languages.scala["class-name"],delete Prism.languages.scala["function"]; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-sql.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},string:{pattern:/(^|[^@\\])("|')(?:\\?[\s\S])*?\2/,greedy:!0,lookbehind:!0},variable:/@[\w.$]+|@("|'|`)(?:\\?[\s\S])+?\1/,"function":/\b(?:COUNT|SUM|AVG|MIN|MAX|FIRST|LAST|UCASE|LCASE|MID|LEN|ROUND|NOW|FORMAT)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR VARYING|CHARACTER (?:SET|VARYING)|CHARSET|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|DATA(?:BASES?)?|DATE(?:TIME)?|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITER(?:S)?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE(?: PRECISION)?|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE KEY|ELSE|ENABLE|ENCLOSED BY|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPE(?:D BY)?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTO|INVOKER|ISOLATION LEVEL|JOIN|KEYS?|KILL|LANGUAGE SQL|LAST|LEFT|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MODIFIES SQL DATA|MODIFY|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL(?: CHAR VARYING| CHARACTER(?: VARYING)?| VARCHAR)?|NATURAL|NCHAR(?: VARCHAR)?|NEXT|NO(?: SQL|CHECK|CYCLE)?|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READ(?:S SQL DATA|TEXT)?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEATABLE|REPLICATION|REQUIRE|RESTORE|RESTRICT|RETURNS?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE MODE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|START(?:ING BY)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED BY|TEXT(?:SIZE)?|THEN|TIMESTAMP|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNPIVOT|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?)\b/i,"boolean":/\b(?:TRUE|FALSE|NULL)\b/i,number:/\b-?(?:0x)?\d*\.?[\da-f]+\b/,operator:/[-+*\/=%^~]|&&?|\|?\||!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-stylus.min.js: -------------------------------------------------------------------------------- 1 | !function(n){var t={url:/url\((["']?).*?\1\)/i,string:{pattern:/("|')(?:[^\\\r\n]|\\(?:\r\n|[\s\S]))*?\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:if|else|for|return|unless)(?=\s+|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,number:/\b\d+(?:\.\d+)?%?/,"boolean":/\b(?:true|false)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.+|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],punctuation:/[{}()\[\];:,]/};t.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:n.util.clone(t)},t.func={pattern:/[\w-]+\([^)]*\).*/,inside:{"function":/^[^(]+/,rest:n.util.clone(t)}},n.languages.stylus={comment:{pattern:/(^|[^\\])(\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},"atrule-declaration":{pattern:/(^\s*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:t}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:(?:\{[^}]*\}|.+)|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:t}},statement:{pattern:/(^[ \t]*)(?:if|else|for|return|unless)[ \t]+.+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:t}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)[^{\r\n]*(?:;|[^{\r\n,](?=$)(?!(\r?\n|\r)(?:\{|\2[ \t]+)))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:t.interpolation}},rest:t}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\))?|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\))?|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t]+)))/m,lookbehind:!0,inside:{interpolation:t.interpolation,punctuation:/[{},]/}},func:t.func,string:t.string,interpolation:t.interpolation,punctuation:/[{}()\[\];:.]/}}(Prism); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-swift.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.swift=Prism.languages.extend("clike",{string:{pattern:/("|')(\\(?:\((?:[^()]|\([^)]+\))+\)|\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{interpolation:{pattern:/\\\((?:[^()]|\([^)]+\))+\)/,inside:{delimiter:{pattern:/^\\\(|\)$/,alias:"variable"}}}}},keyword:/\b(as|associativity|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic(?:Type)?|else|enum|extension|fallthrough|final|for|func|get|guard|if|import|in|infix|init|inout|internal|is|lazy|left|let|mutating|new|none|nonmutating|operator|optional|override|postfix|precedence|prefix|private|Protocol|public|repeat|required|rethrows|return|right|safe|self|Self|set|static|struct|subscript|super|switch|throws?|try|Type|typealias|unowned|unsafe|var|weak|where|while|willSet|__(?:COLUMN__|FILE__|FUNCTION__|LINE__))\b/,number:/\b([\d_]+(\.[\de_]+)?|0x[a-f0-9_]+(\.[a-f0-9p_]+)?|0b[01_]+|0o[0-7_]+)\b/i,constant:/\b(nil|[A-Z_]{2,}|k[A-Z][A-Za-z_]+)\b/,atrule:/@\b(IB(?:Outlet|Designable|Action|Inspectable)|class_protocol|exported|noreturn|NS(?:Copying|Managed)|objc|UIApplicationMain|auto_closure)\b/,builtin:/\b([A-Z]\S+|abs|advance|alignof(?:Value)?|assert|contains|count(?:Elements)?|debugPrint(?:ln)?|distance|drop(?:First|Last)|dump|enumerate|equal|filter|find|first|getVaList|indices|isEmpty|join|last|lexicographicalCompare|map|max(?:Element)?|min(?:Element)?|numericCast|overlaps|partition|print(?:ln)?|reduce|reflect|reverse|sizeof(?:Value)?|sort(?:ed)?|split|startsWith|stride(?:of(?:Value)?)?|suffix|swap|toDebugString|toString|transcode|underestimateCount|unsafeBitCast|with(?:ExtendedLifetime|Unsafe(?:MutablePointers?|Pointers?)|VaList))\b/}),Prism.languages.swift.string.inside.interpolation.inside.rest=Prism.util.clone(Prism.languages.swift); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-wiki.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.wiki=Prism.languages.extend("markup",{"block-comment":{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0,alias:"comment"},heading:{pattern:/^(=+).+?\1/m,inside:{punctuation:/^=+|=+$/,important:/.+/}},emphasis:{pattern:/('{2,5}).+?\1/,inside:{"bold italic":{pattern:/(''''').+?(?=\1)/,lookbehind:!0},bold:{pattern:/(''')[^'](?:.*?[^'])?(?=\1)/,lookbehind:!0},italic:{pattern:/('')[^'](?:.*?[^'])?(?=\1)/,lookbehind:!0},punctuation:/^''+|''+$/}},hr:{pattern:/^-{4,}/m,alias:"punctuation"},url:[/ISBN +(?:97[89][ -]?)?(?:\d[ -]?){9}[\dx]\b|(?:RFC|PMID) +\d+/i,/\[\[.+?\]\]|\[.+?\]/],variable:[/__[A-Z]+__/,/\{{3}.+?\}{3}/,/\{\{.+?}}/],symbol:[/^#redirect/im,/~{3,5}/],"table-tag":{pattern:/((?:^|[|!])[|!])[^|\r\n]+\|(?!\|)/m,lookbehind:!0,inside:{"table-bar":{pattern:/\|$/,alias:"punctuation"},rest:Prism.languages.markup.tag.inside}},punctuation:/^(?:\{\||\|\}|\|-|[*#:;!|])|\|\||!!/m}),Prism.languages.insertBefore("wiki","tag",{nowiki:{pattern:/<(nowiki|pre|source)\b[\s\S]*?>[\s\S]*?<\/\1>/i,inside:{tag:{pattern:/<(?:nowiki|pre|source)\b[\s\S]*?>|<\/(?:nowiki|pre|source)>/i,inside:Prism.languages.markup.tag.inside}}}}); -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/prismjs/components/prism-yaml.min.js: -------------------------------------------------------------------------------- 1 | Prism.languages.yaml={scalar:{pattern:/([\-:]\s*(![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\3[^\r\n]+)*)/,lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:/(\s*(?:^|[:\-,[{\r\n?])[ \t]*(![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/,lookbehind:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:/([:\-,[{]\s*(![^\s]+)?[ \t]*)(\d{4}-\d\d?-\d\d?([tT]|[ \t]+)\d\d?:\d{2}:\d{2}(\.\d*)?[ \t]*(Z|[-+]\d\d?(:\d{2})?)?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(:\d{2}(\.\d*)?)?)(?=[ \t]*($|,|]|}))/m,lookbehind:!0,alias:"number"},"boolean":{pattern:/([:\-,[{]\s*(![^\s]+)?[ \t]*)(true|false)[ \t]*(?=$|,|]|})/im,lookbehind:!0,alias:"important"},"null":{pattern:/([:\-,[{]\s*(![^\s]+)?[ \t]*)(null|~)[ \t]*(?=$|,|]|})/im,lookbehind:!0,alias:"important"},string:{pattern:/([:\-,[{]\s*(![^\s]+)?[ \t]*)("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?=[ \t]*($|,|]|}))/m,lookbehind:!0,greedy:!0},number:{pattern:/([:\-,[{]\s*(![^\s]+)?[ \t]*)[+\-]?(0x[\da-f]+|0o[0-7]+|(\d+\.?\d*|\.?\d+)(e[\+\-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im,lookbehind:!0},tag:/![^\s]+/,important:/[&*][\w]+/,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./}; -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/plugins/search.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function e(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return String(e).replace(/[&<>"'\/]/g,function(e){return n[e]})}function n(e){var n=[];return h.dom.findAll("a:not([data-nosearch])").map(function(t){var o=t.href,i=t.getAttribute("href"),r=e.parse(o).path;r&&-1===n.indexOf(r)&&!Docsify.util.isAbsolutePath(i)&&n.push(r)}),n}function t(e){localStorage.setItem("docsify.search.expires",Date.now()+e),localStorage.setItem("docsify.search.index",JSON.stringify(g))}function o(e,n,t,o){void 0===n&&(n="");var i,r=window.marked.lexer(n),a=window.Docsify.slugify,s={};return r.forEach(function(n){if("heading"===n.type&&n.depth<=o)i=t.toURL(e,{id:a(n.text)}),s[i]={slug:i,title:n.text,body:""};else{if(!i)return;s[i]?s[i].body?s[i].body+="\n"+(n.text||""):s[i].body=n.text:s[i]={slug:i,title:"",body:""}}}),a.clear(),s}function i(n){var t=[],o=[];Object.keys(g).forEach(function(e){o=o.concat(Object.keys(g[e]).map(function(n){return g[e][n]}))}),n=n.trim();var i=n.split(/[\s\-\,\\\/]+/);1!==i.length&&(i=[].concat(n,i));for(var r=0;rl.length&&(d=l.length);var p="..."+e(l).substring(f,d).replace(o,''+n+"")+"...";s+=p}}),a)){var d={title:e(c),content:s,url:f};t.push(d)}}(r);return t}function r(e,i){h=Docsify;var r="auto"===e.paths,a=localStorage.getItem("docsify.search.expires")
',o=Docsify.dom.create("div",t),i=Docsify.dom.find("aside");Docsify.dom.toggleClass(o,"search"),Docsify.dom.before(i,o)}function c(e){var n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,".results-panel");if(!e)return t.classList.remove("show"),void(t.innerHTML="");var o=i(e),r="";o.forEach(function(e){r+='
\n \n

'+e.title+"

\n

"+e.content+"

\n
\n
"}),t.classList.add("show"),t.innerHTML=r||'

'+y+"

"}function l(){var e,n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,"input");Docsify.dom.on(n,"click",function(e){return"A"!==e.target.tagName&&e.stopPropagation()}),Docsify.dom.on(t,"input",function(n){clearTimeout(e),e=setTimeout(function(e){return c(n.target.value.trim())},100)})}function f(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var o=Object.keys(e).filter(function(e){return n.indexOf(e)>-1})[0];t.placeholder=e[o]}}function d(e,n){if("string"==typeof e)y=e;else{var t=Object.keys(e).filter(function(e){return n.indexOf(e)>-1})[0];y=e[t]}}function p(e,n){var t=n.router.parse().query.s;a(),s(e,t),l(),t&&setTimeout(function(e){return c(t)},500)}function u(e,n){f(e.placeholder,n.route.path),d(e.noData,n.route.path)}var h,g={},y="",m={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5},v=function(e,n){var t=Docsify.util,o=n.config.search||m;Array.isArray(o)?m.paths=o:"object"==typeof o&&(m.paths=Array.isArray(o.paths)?o.paths:"auto",m.maxAge=t.isPrimitive(o.maxAge)?o.maxAge:m.maxAge,m.placeholder=o.placeholder||m.placeholder,m.noData=o.noData||m.noData,m.depth=o.depth||m.depth);var i="auto"===m.paths;e.mounted(function(e){p(m,n),!i&&r(m,n)}),e.doneEach(function(e){u(m,n),i&&r(m,n)})};$docsify.plugins=[].concat(v,$docsify.plugins)}(); 2 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/docsify/sw.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * docsify sw.js 3 | * =========================================================== 4 | * Copyright 2016 @huxpro 5 | * Licensed under Apache 2.0 6 | * Register service worker. 7 | * ========================================================== */ 8 | 9 | const RUNTIME = 'docsify' 10 | const HOSTNAME_WHITELIST = [ 11 | self.location.hostname, 12 | 'fonts.gstatic.com', 13 | 'fonts.googleapis.com', 14 | 'unpkg.com' 15 | ] 16 | 17 | // The Util Function to hack URLs of intercepted requests 18 | const getFixedUrl = (req) => { 19 | var now = Date.now() 20 | var url = new URL(req.url) 21 | 22 | // 1. fixed http URL 23 | // Just keep syncing with location.protocol 24 | // fetch(httpURL) belongs to active mixed content. 25 | // And fetch(httpRequest) is not supported yet. 26 | url.protocol = self.location.protocol 27 | 28 | // 2. add query for caching-busting. 29 | // Github Pages served with Cache-Control: max-age=600 30 | // max-age on mutable content is error-prone, with SW life of bugs can even extend. 31 | // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. 32 | // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 33 | if (url.hostname === self.location.hostname) { 34 | url.search += (url.search ? '&' : '?') + 'cache-bust=' + now 35 | } 36 | return url.href 37 | } 38 | 39 | /** 40 | * @Lifecycle Activate 41 | * New one activated when old isnt being used. 42 | * 43 | * waitUntil(): activating ====> activated 44 | */ 45 | self.addEventListener('activate', event => { 46 | event.waitUntil(self.clients.claim()) 47 | }) 48 | 49 | /** 50 | * @Functional Fetch 51 | * All network requests are being intercepted here. 52 | * 53 | * void respondWith(Promise r) 54 | */ 55 | self.addEventListener('fetch', event => { 56 | // Skip some of cross-origin requests, like those for Google Analytics. 57 | if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { 58 | // Stale-while-revalidate 59 | // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale 60 | // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 61 | const cached = caches.match(event.request) 62 | const fixedUrl = getFixedUrl(event.request) 63 | const fetched = fetch(fixedUrl, { cache: 'no-store' }) 64 | const fetchedCopy = fetched.then(resp => resp.clone()) 65 | 66 | // Call respondWith() with whatever we get first. 67 | // If the fetch fails (e.g disconnected), wait for the cache. 68 | // If there’s nothing in cache, wait for the fetch. 69 | // If neither yields a response, return offline pages. 70 | event.respondWith( 71 | Promise.race([fetched.catch(_ => cached), cached]) 72 | .then(resp => resp || fetched) 73 | .catch(_ => { /* eat any errors */ }) 74 | ) 75 | 76 | // Update the cache with the version we fetched (only for ok status) 77 | event.waitUntil( 78 | Promise.all([fetchedCopy, caches.open(RUNTIME)]) 79 | .then(([response, cache]) => response.ok && cache.put(event.request, response)) 80 | .catch(_ => { /* eat any errors */ }) 81 | ) 82 | } 83 | }) 84 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/json/json-to-table.js: -------------------------------------------------------------------------------- 1 | var json2table = function (json, classes) { 2 | var cols = Object.keys(json[0]); 3 | 4 | var headerRow = ''; 5 | var bodyRows = ''; 6 | 7 | classes = classes || ''; 8 | 9 | function capitalizeFirstLetter(string) { 10 | return string.charAt(0).toUpperCase() + string.slice(1); 11 | } 12 | 13 | cols.map(function(col) { 14 | headerRow += '' + capitalizeFirstLetter(col) + ''; 15 | }); 16 | 17 | json.map(function(row) { 18 | bodyRows += ''; 19 | 20 | cols.map(function(colName) { 21 | var rowInfo ; 22 | var rowVal = row[colName]; 23 | /**值是数组*/ 24 | if( rowVal instanceof Array){ 25 | rowInfo = "
    "; 26 | rowVal.forEach(function(innerVal,index,array){ 27 | var innerRowInfo; 28 | if (innerVal instanceof Object){ 29 | for(innerKey in innerVal){ 30 | innerRowInfo = ": "+innerVal[innerKey]+""; 31 | } 32 | } 33 | rowInfo += "
  • "+(innerRowInfo || innerVal)+"
  • "; 34 | }); 35 | rowInfo += "
"; 36 | } 37 | rowInfo = (rowInfo || row[colName]) || ''; 38 | bodyRows += '' + rowInfo + ''; 39 | }) 40 | 41 | bodyRows += ''; 42 | }); 43 | 44 | return '' + 45 | '' +'' + marked(headerRow) + '' + '' + 46 | '' + marked(bodyRows)+ '' + 47 | '
'; 48 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/json/json.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 28 | 29 | 30 |
31 | 32 | 71 | 72 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/dist/wiki.css: -------------------------------------------------------------------------------- 1 | .markdown-section { 2 | padding: 5px 15px 40px; 3 | } 4 | .markdown-section ul { 5 | margin: 0.1em 0; 6 | } 7 | .markdown-section td, .markdown-section th { 8 | border: 1px solid #ddd; 9 | padding: 1px 10px; 10 | } 11 | .markdown-section a { 12 | color: #3658b9; 13 | font-weight: 500; 14 | text-decoration:none; 15 | } 16 | .markdown-section p { 17 | 18 | } 19 | .markdown-section figure, .markdown-section ol, .markdown-section p, .markdown-section ul { 20 | margin: 0.1em 0; 21 | } 22 | 23 | .text-success { 24 | color: #09b92b; 25 | } 26 | 27 | .text-danger { 28 | color: #dd100b; 29 | } 30 | 31 | .text-warning { 32 | color: #f38905; 33 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/current/cpu.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 |
Cpu(%)线程堆栈信息
22 |
23 |
24 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/current/cpu.js: -------------------------------------------------------------------------------- 1 | var getCpuTop = function(){ 2 | $('#tbody_metric').empty(); 3 | requestByJson({ 4 | url:"admin/monitor/thread/cpu/top", 5 | method:"GET" 6 | },{ 7 | success:function(req,data){ 8 | var result = data.data || {}; 9 | for( var i in result){ 10 | var obj = result[i]; 11 | var tplSysApiTr = $('#tpl_metric_tr').text(); 12 | tplSysApiTr = tplSysApiTr.replaceAll("{{cpu}}",i); 13 | tplSysApiTr = tplSysApiTr.replaceAll("{{threadStack}}",obj); 14 | $('#tbody_metric').append(tplSysApiTr); 15 | } 16 | },fail: function(req,data){ 17 | alert("请求出错"); 18 | return; 19 | } 20 | }) 21 | } 22 | getCpuTop(); 23 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/current/current.html: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 | 监控: 11 | 12 | 13 |
14 | 15 | 哨兵(Sentinel)监控: 16 | 17 | 18 |
19 | 获取当前运行线程状态: 20 | 21 |
22 |
23 |
24 | 视图显示管理: 25 | 27 | 28 | 30 | 31 | 33 | 34 | 36 | 37 |
38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 |
服务信息
53 |
54 |
55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
内存区域初始(M)上限(M)已使用(M)已申请(M)使用率(%)
68 |
69 |
70 | 71 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/metric/metric.html: -------------------------------------------------------------------------------- 1 |
2 | 8 | 9 | 10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 43 |
统计近一小时调用接口最慢耗时(大于0.5秒)RT时间(秒)请求次数通过次数成功次数异常次数被阻塞次数
44 |
45 |
46 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/metric/metric.js: -------------------------------------------------------------------------------- 1 | var getMetric = function(){ 2 | $('#tbody_metric').empty(); 3 | requestByJson({ 4 | url:"admin/monitor/method-metric?orderType="+$('#orderType').val(), 5 | method:"GET" 6 | },{ 7 | success:function(req,data){ 8 | var result = data.data || {}; 9 | for( var i in result){ 10 | var obj = result[i]; 11 | var tplSysApiTr = $('#tpl_metric_tr').text(); 12 | tplSysApiTr = tplSysApiTr.replaceAll("{{sequence}}",i); 13 | tplSysApiTr = tplSysApiTr.replaceAll("{{refDate}}",obj.refDate); 14 | tplSysApiTr = tplSysApiTr.replaceAll("{{name}}",obj.name); 15 | tplSysApiTr = tplSysApiTr.replaceAll("{{cntRequest}}",obj.cntRequest); 16 | tplSysApiTr = tplSysApiTr.replaceAll("{{cntPassRequest}}",obj.cntPassRequest); 17 | tplSysApiTr = tplSysApiTr.replaceAll("{{cntSuccessRequest}}",obj.cntSuccessRequest); 18 | tplSysApiTr = tplSysApiTr.replaceAll("{{cntExceptionRequest}}",obj.cntExceptionRequest); 19 | tplSysApiTr = tplSysApiTr.replaceAll("{{cntBlockRequest}}",obj.cntBlockRequest); 20 | var tardiness = obj.tardiness /1000 ; 21 | if(tardiness >3 ){ 22 | tardiness = ''; 23 | }else if(tardiness == 0){ 24 | tardiness = '<0.5s'; 25 | } 26 | tplSysApiTr = tplSysApiTr.replaceAll("{{tardiness}}",tardiness); 27 | 28 | var avgRt = obj.avgRt /1000 ; 29 | avgRt = avgRt >3 ? '' : avgRt; 30 | tplSysApiTr = tplSysApiTr.replaceAll("{{avgRt}}",avgRt); 31 | $('#tbody_metric').append(tplSysApiTr); 32 | } 33 | },fail: function(req,data){ 34 | alert("请求出错"); 35 | return; 36 | } 37 | }) 38 | } 39 | getMetric(); 40 | 41 | var manualControlMetric = function () { 42 | requestByJson({ 43 | url:"admin/monitor/job", 44 | method:"GET" 45 | },{ 46 | success:function(req,data){ 47 | getMetric(); 48 | },fail: function(req,data){ 49 | alert("请求出错"); 50 | return; 51 | } 52 | }) 53 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/refresh/refresh.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 24 |
服务实例状态更新信息
25 |
26 |
27 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/refresh/refresh.js: -------------------------------------------------------------------------------- 1 | var successHostStatus = ' '; 2 | var failHostStatus = ' '; 3 | var runStatus = false; 4 | 5 | var refresh = function(){ 6 | if(runStatus){ 7 | alert("请求正在处理中……"); 8 | return; 9 | } 10 | runStatus = true; 11 | $('#tbody_metric').empty(); 12 | requestByJson({ 13 | url:"admin/monitor/refresh", 14 | method:"PUT" 15 | },{ 16 | success:function(req,res){ 17 | runStatus = false; 18 | var data = res.data || {}; 19 | for(var i in data){ 20 | var dataInfo = data[i]; 21 | var isSuccess = (i == 0); 22 | for( var hostPort in dataInfo){ 23 | var successKey = dataInfo[hostPort]; 24 | successKey = successKey && successKey.length == 0 ? "无更新" :""; 25 | var tplSysApiTr = $('#tpl_metric_tr').text(); 26 | tplSysApiTr = tplSysApiTr.replaceAll("{{hostPort}}",hostPort); 27 | tplSysApiTr = tplSysApiTr.replaceAll("{{hostStatus}}",isSuccess?successHostStatus:failHostStatus); 28 | 29 | tplSysApiTr = tplSysApiTr.replaceAll("{{changeKey}}",successKey); 30 | $('#tbody_metric').append(tplSysApiTr); 31 | } 32 | } 33 | },fail: function(req,data){ 34 | runStatus = false; 35 | alert("请求出错"); 36 | return; 37 | } 38 | }) 39 | } -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/view/sentinel/sentinel.html: -------------------------------------------------------------------------------- 1 | 8 |
9 |

10 | 11 | 12 | 13 |

14 | { 15 | "BasicErrorController": -1, //规则保持永久生效 16 | "BasicErrorController": "2019-07-20 00:00:00" //规则生效截止时间 17 | } 18 |

19 |
20 | 21 |
22 |
23 | 24 |
25 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/wiki/404.md: -------------------------------------------------------------------------------- 1 | ## **页面不存在,返回[主页]({{baseDomain}})** -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/wiki/home.md: -------------------------------------------------------------------------------- 1 | ## 轻量级流量控制 2 | 基于[Sentinel](https://github.com/alibaba/Sentinel/wiki)实现 3 | 4 | ### 如何使用 5 | 步骤一: 6 | ```xml 7 | 8 | com.runcoding 9 | monitor-spring 10 | 1.0.0-SNAPSHOT 11 | 12 | ``` 13 | 步骤二: 14 | ```yml 15 | spring: 16 | application: 17 | name: monitor-center # 应用名称 18 | profiles: 19 | active: dev # 运行环境(显示在钉钉消息中) 20 | 21 | server: 22 | tomcat: 23 | max-threads: 100 # Reactor线程池最大线程数(目前使用的是tomcat) 24 | 25 | # 哨兵限流配置支持与spring config结合的热更新方式。通过Eureka服务发现节点。注意刷新时会重启Eureka注册节点。 26 | csp: 27 | sentinel: 28 | rules: 29 | system: # 配置哨兵平台规则 30 | systemLoad: 10.0 # 配置最高系统加载平均值,是排队到可用处理器的可运行实体数目与可用处理器上可运行实体数目的总和在某一段时间进行平均的结果 31 | avgRt: 10000 # 平均RT时间(ms) 32 | qps: 1000 # 每秒接受处理的请求数 33 | maxThread: 200 # 最高并行执行线程数 34 | # http://tool.chinaz.com/Tools/unixtime.aspx 35 | authorityWhite: MonitorController.job(0)|1536595200000,OrderProducerService.send(2)|-1 #监控方法白名单(Unix时间单位ms,-1不设置过期时间,Unix时间 1536595200000 = 2018-09-11 00:00:00) 36 | authorityBlock: MonitorController.degrade(0)|-1 #监控黑名单方法 37 | isAutoRule: true # 是否自动开启限流 38 | maxLoadAverageRate: 0.95 # 最大的负载比例(当前正在运行的线程/容器最大数量),这里的容器默认指tomcat。超过后会进入打断执行线程判断 39 | isAutoInterrupt: true # 是否自动打断执行线程(如果设置为false,maxRunTimeoutMillis和maxBlockTimeMillis配置将失效) 40 | maxRunTimeoutMillis: 10000 # 最长的方法执行时长(ms,默认10s),超过这个时间执行线程会被打断,并且加入到黑名单中 41 | maxBlockTimeMillis: 180000 # 加入黑名单时长(ms,默认3分钟) 42 | warnTimeoutMillis: 3000 # 调用请求超时3s,输出服务当前运行日志 43 | maxTardinessMillis: 500 # 超过这个时间(ms,默认0.5s)的请求,将在每小时被统计 44 | api: 45 | # port: 8099 # 服务向外暴露端口,供dashboard请求(不填不暴露),目前不建议使用 46 | dashboard: 47 | # server: localhost:8090 # dashboard 监控台地址,目前不建议使用 48 | webHook: 49 | # 钉钉文档 https://open-doc.dingtalk.com/docs/doc.htm?treeId=257&articleId=105735&docType=1 50 | dingTalk: 51 | atMobiles: 15869111000,15869111001 #接受钉钉,通知需要被@的人 52 | accessToken: 84b2a8576d514dfc59e86038b72d3f0bd9362461f5a9d2267a10a64e98f93637 #钉钉通知机器人 53 | 54 | eureka: # 多节点部署需配置eureka,用做哨兵变更规则 55 | 56 | ``` 57 | > 具体可查看: SentinelWebConfig.java 58 | 59 | ```java 60 | @Resource 61 | private MonitorProcessor monitorProcessor; 62 | 63 | monitorProcessor.setWarnChatBot(chatBotSendLog()); 64 | 65 | /**自定义异常报警*/ 66 | public MonitorSendFunction chatBotSendLog(){ 67 | return ((methodName, t, args) -> { 68 | if(!(t instanceof RuntimeException)){ 69 | return; 70 | } 71 | /**处理RuntimeException异常*/ 72 | String throwablePackageName = t.getClass().getPackage().getName(); 73 | if(StringUtils.startsWith(throwablePackageName,"java.lang") || 74 | StringUtils.startsWith(throwablePackageName,"org.springframework") ){ 75 | boolean checkEnableSend = DTWebHookProcessor.checkEnableSend(methodName + t.getClass().getSimpleName()); 76 | if(!checkEnableSend){ 77 | return; 78 | } 79 | /**是否死锁*/ 80 | boolean isDeadlock = t instanceof DeadlockLoserDataAccessException; 81 | /**运行时异常*/ 82 | DTWebHookProcessor.chatbotSendByMarkdown("运行时异常报警", 83 | "## 服务在运行时出现了异常,请即时处理 \n - 服务:"+appName+"-"+ appEnv 84 | +"\n - ip:"+HostNameUtil.getIp() 85 | +"\n - 方法:"+methodName 86 | +"\n - 参数:"+JSON.toJSONString(args) 87 | + (isDeadlock ? "\n - 死锁: 当前执行出现了死锁" : "") 88 | +"\n - error:"+t.toString()+"\n"+t.getMessage() 89 | +"\n - 问题处理人:", 90 | false); 91 | } 92 | }); 93 | } 94 | 95 | ``` 96 | 97 | ## 自动流控规则与实现 98 | > SentinelRuleProcessor.java 99 | 100 | - 开启自动流控 isAutoRule = true 101 | - 自动流控:当处理线程大于最大线程池95%时,该运行方法将只有50%线程数的线程可以运行该方法。具体查看哨兵的FlowRule线程控制 102 | - 自动降级: 当方法执行时间>3s时,通过响应时间3s,阻塞2s的窗口时间。 103 | 104 | - 是否自动打断执行时间超过10s的线程,打断后加入黑名单三分钟。isAutoInterrupt = true 105 | 106 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/resources/monitor/wiki/sidebar.md: -------------------------------------------------------------------------------- 1 | - [首页]({{baseDomain}}/monitor/index.html) 2 | - 监控 3 | - [实时监控]({{baseDomain}}/monitor/index.html?module=current/current#/) 4 | - [CPU TOP 10]({{baseDomain}}/monitor/index.html?module=current/cpu#/) 5 | - [过去每小时]({{baseDomain}}/monitor/index.html?module=metric/metric#/) 6 | - [Sentinel配置]({{baseDomain}}/monitor/index.html?#/monitor/wiki/monitor/sentinel_config) 7 | - [系统规则]({{baseDomain}}/monitor/index.html?module=sentinel/sentinel&sentinelRule=systemRules#/) 8 | - [流控规则]({{baseDomain}}/monitor/index.html?module=sentinel/sentinel&sentinelRule=flowRules#/) 9 | - [降级规则]({{baseDomain}}/monitor/index.html?module=sentinel/sentinel&sentinelRule=degradeRules#/) 10 | - 方法黑白名单 11 | - [白名单]({{baseDomain}}/monitor/index.html?module=sentinel/sentinel&sentinelRule=authorityWhite#/) 12 | - [黑名单]({{baseDomain}}/monitor/index.html?module=sentinel/sentinel&sentinelRule=authorityBlock#/) 13 | - [刷新配置]({{baseDomain}}/monitor/index.html?module=refresh/refresh#/) 14 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | ## 启用系统监控 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.runcoding.monitor.config.MonitorConfiguration -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/monitor_config.properties: -------------------------------------------------------------------------------- 1 | #https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html 2 | 3 | #\u542F\u7528shutdown 4 | endpoints.shutdown.enabled=true 5 | 6 | #shutdown\u7981\u7528\u5BC6\u7801\u9A8C\u8BC1 7 | endpoints.shutdown.sensitive=false 8 | 9 | #shutdown \u8BF7\u6C42\u5730\u5740 10 | endpoints.shutdown.path=/admin/actuator/shutdown 11 | 12 | #\u5173\u95EDinfo\u63A5\u53E3 13 | endpoints.info.enabled=false 14 | 15 | #health \u8BF7\u6C42\u5730\u5740 16 | endpoints.health.path=/admin/actuator/health 17 | 18 | management.server.address=127.0.0.1 19 | -------------------------------------------------------------------------------- /monitor-spring/src/main/resources/store/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS "metric_info"; 2 | 3 | CREATE TABLE IF NOT EXISTS "metric_info" ( 4 | "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 5 | "refDate" text, 6 | "name" text, 7 | "cntRequest" integer, 8 | "cntPassRequest" integer, 9 | "cntSuccessRequest" integer, 10 | "cntExceptionRequest" integer, 11 | "cntBlockRequest" integer, 12 | "avgRt" integer, 13 | "tardiness" integer 14 | ); 15 | 16 | CREATE UNIQUE INDEX IF NOT EXISTS "unq_name" 17 | ON "metric_info" ( 18 | "refDate" COLLATE BINARY ASC, 19 | "name" COLLATE BINARY ASC 20 | ); -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/ExceptionResolver.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor; 2 | 3 | import com.alibaba.csp.sentinel.slots.block.BlockException; 4 | import com.runcoding.monitor.dto.Resp; 5 | import com.runcoding.monitor.exception.SentinelBlockException; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.bind.annotation.ControllerAdvice; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | 13 | @ControllerAdvice 14 | @Component 15 | public class ExceptionResolver { 16 | 17 | private Logger logger = LoggerFactory.getLogger(ExceptionResolver.class); 18 | 19 | @ExceptionHandler(value = SentinelBlockException.class) 20 | @ResponseBody 21 | public Resp jsonErrorHandler(Exception e) { 22 | logger.error("出错了:",e); 23 | if (BlockException.isBlockException(e) || e instanceof SentinelBlockException){ 24 | return Resp.failure("服务繁忙,请稍后重试……"); 25 | } 26 | return Resp.failure("服务异常:"+e.getMessage()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/MonitorApplication.java: -------------------------------------------------------------------------------- 1 | 2 | package com.runcoding.monitor; 3 | 4 | 5 | import com.runcoding.monitor.web.model.MonitorConstants; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 9 | import org.springframework.context.ConfigurableApplicationContext; 10 | import org.springframework.scheduling.annotation.EnableScheduling; 11 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | 13 | @SpringBootApplication 14 | @EnableDiscoveryClient(autoRegister = false) 15 | @EnableScheduling 16 | /** 17 | * http://localhost:8080/swagger-ui.html 18 | */ 19 | @EnableSwagger2 20 | public class MonitorApplication { 21 | 22 | 23 | public static void main(String[] args) { 24 | ConfigurableApplicationContext context = SpringApplication.run(MonitorApplication.class, args); 25 | System.out.println("http://localhost:"+ MonitorConstants.applicationPort+"/monitor/index.html?module=current/current#/"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/e2etest/MockHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.e2etest; 2 | 3 | import com.alibaba.fastjson.util.TypeUtils; 4 | import org.assertj.core.util.Lists; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.http.*; 8 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 9 | import org.springframework.web.client.RestTemplate; 10 | import org.springframework.web.util.UriComponentsBuilder; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author: runcoding 16 | * @email: runcoding@163.com 17 | * @created Time: 2019/07/9 10:26 18 | * @description 模拟用户请求http 19 | * Copyright (C), 2017-2018, runcoding 20 | **/ 21 | public class MockHttpClient { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(MockHttpClient.class); 24 | 25 | private static RestTemplate restTemplate; 26 | 27 | static { 28 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 29 | factory.setReadTimeout(60000); 30 | factory.setConnectTimeout(60000); 31 | restTemplate = new RestTemplate(factory); 32 | } 33 | 34 | /**post json请求*/ 35 | public static T postForEntity(String url ,String requestBody, Class responseType){ 36 | HttpHeaders headers = new HttpHeaders(); 37 | List mediaTypes = Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8); 38 | headers.setAccept(mediaTypes); 39 | headers.setContentType(MediaType.APPLICATION_JSON_UTF8); 40 | 41 | HttpEntity entity = new HttpEntity(requestBody, headers); 42 | ResponseEntity response = restTemplate.postForEntity(url, entity, String.class); 43 | return TypeUtils.castToJavaBean(response.getBody(),responseType); 44 | } 45 | 46 | /**post json请求*/ 47 | public static T putForEntity(String url ,String requestBody, Class responseType){ 48 | UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); 49 | HttpHeaders headers = new HttpHeaders(); 50 | List mediaTypes = Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8); 51 | headers.setAccept(mediaTypes); 52 | headers.setContentType(MediaType.APPLICATION_JSON_UTF8); 53 | 54 | HttpEntity entity = new HttpEntity(requestBody, headers); 55 | ResponseEntity response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.PUT, entity, String.class); 56 | return TypeUtils.castToJavaBean(response.getBody(),responseType); 57 | } 58 | 59 | /**get json请求*/ 60 | public static T getForEntity(String url ,String requestBody, Class responseType){ 61 | UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); 62 | HttpHeaders headers = new HttpHeaders(); 63 | List mediaTypes = Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8); 64 | headers.setAccept(mediaTypes); 65 | headers.setContentType(MediaType.APPLICATION_JSON_UTF8); 66 | HttpEntity entity = new HttpEntity(requestBody, headers); 67 | ResponseEntity response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, entity, String.class); 68 | return TypeUtils.castToJavaBean(response.getBody(),responseType); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/e2etest/SysLoadTest.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.e2etest; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.atomic.AtomicLong; 9 | 10 | /** 11 | * 12 | * @author: runcoding@163.com 13 | * @date: 2019/07/30 17:17 14 | * @describe: 系统负载测试 15 | **/ 16 | public class SysLoadTest { 17 | 18 | /**模拟请求次数*/ 19 | private static int latchCnt = 1; 20 | 21 | private CountDownLatch latch = new CountDownLatch(latchCnt); 22 | 23 | private AtomicLong errCnt = new AtomicLong(0); 24 | 25 | /**模拟人数*/ 26 | private static ExecutorService executorService = Executors.newFixedThreadPool(15); 27 | 28 | 29 | @Test 30 | public void loadAverage() { 31 | for (int i = 0; i <= latchCnt; i++) { 32 | executorService.execute(()->{ 33 | try{ 34 | String res = MockHttpClient.getForEntity("http://localhost:8080/test/degrade", "", String.class); 35 | System.out.println(String.format("threadId=%s,res=%s",Thread.currentThread().getId(),res)); 36 | }catch (Exception e){ 37 | errCnt.getAndIncrement(); 38 | e.printStackTrace(); 39 | System.out.println(String.format("threadId=%s,res=%s",Thread.currentThread().getId(),"请求错误")); 40 | }finally { 41 | latch.countDown(); 42 | } 43 | }); 44 | } 45 | try { 46 | latch.await(); 47 | } catch (InterruptedException e) { 48 | e.printStackTrace(); 49 | } 50 | System.out.println("系统负载测试结束:latchCnt="+latchCnt+",errCnt="+errCnt); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/sentinel/SentinelAuthorityTest.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.sentinel; 2 | 3 | import com.alibaba.csp.sentinel.Entry; 4 | import com.alibaba.csp.sentinel.SphU; 5 | import com.alibaba.csp.sentinel.context.ContextUtil; 6 | import com.alibaba.csp.sentinel.slots.block.BlockException; 7 | import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; 8 | import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.springframework.test.context.junit4.SpringRunner; 12 | 13 | import java.util.Collections; 14 | 15 | /** 16 | * 根据黑白名单,来做黑白名单控制 17 | */ 18 | @RunWith(SpringRunner.class) 19 | public class SentinelAuthorityTest { 20 | 21 | private static final String RESOURCE_NAME = "testABC"; 22 | 23 | /**黑名单测试*/ 24 | @Test 25 | public void caseBlack() { 26 | initBlackRules(); 27 | testFor(RESOURCE_NAME, "appA"); 28 | testFor(RESOURCE_NAME, "appB"); 29 | testFor(RESOURCE_NAME, "appC"); 30 | testFor(RESOURCE_NAME, "appE"); 31 | } 32 | 33 | /**白名单测试*/ 34 | @Test 35 | public void caseWhite() { 36 | initWhiteRules(); 37 | testFor(RESOURCE_NAME, "appA"); 38 | testFor(RESOURCE_NAME, "appB"); 39 | testFor(RESOURCE_NAME, "appC"); 40 | testFor(RESOURCE_NAME, "appE"); 41 | } 42 | 43 | private static void testFor( String resource, String origin) { 44 | ContextUtil.enter(resource, origin); 45 | Entry entry = null; 46 | try { 47 | entry = SphU.entry(resource); 48 | System.out.println(String.format("Passed for store %s, origin is %s", resource, origin)); 49 | } catch (BlockException ex) { 50 | System.err.println(String.format("Blocked for store %s, origin is %s", resource, origin)); 51 | } finally { 52 | if (entry != null) { 53 | entry.exit(); 54 | } 55 | ContextUtil.exit(); 56 | } 57 | } 58 | 59 | private static void initWhiteRules() { 60 | AuthorityRule rule = new AuthorityRule(); 61 | rule.setResource(RESOURCE_NAME); 62 | rule.setStrategy(0); 63 | rule.setLimitApp("appA,appE"); 64 | AuthorityRuleManager.loadRules(Collections.singletonList(rule)); 65 | } 66 | 67 | private static void initBlackRules() { 68 | AuthorityRule rule = new AuthorityRule(); 69 | rule.setResource(RESOURCE_NAME); 70 | rule.setStrategy(1); 71 | rule.setLimitApp("appA,appB"); 72 | AuthorityRuleManager.loadRules(Collections.singletonList(rule)); 73 | } 74 | } -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/sentinel/SentinelSystemTest.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.sentinel; 2 | 3 | import com.alibaba.csp.sentinel.*; 4 | import com.alibaba.csp.sentinel.node.Node; 5 | import com.alibaba.csp.sentinel.slots.block.BlockException; 6 | import com.alibaba.csp.sentinel.slots.block.RuleConstant; 7 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; 8 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; 9 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; 10 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; 11 | import com.alibaba.csp.sentinel.slots.system.SystemRule; 12 | import com.alibaba.csp.sentinel.slots.system.SystemRuleManager; 13 | import com.alibaba.fastjson.JSON; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import java.time.LocalDateTime; 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.List; 22 | import java.util.concurrent.CountDownLatch; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | /** 29 | * 30 | * @author: runcoding@163.com 31 | * @date: 2019/07/23 14:47 32 | * @describe: 通过系统的状态,例如 load1 等,来控制总的入口流量 33 | *这个 slot 会根据对于当前系统的整体情况,对入口的资源进行调配。其原理是让入口的 API 和当前系统的 API 达到一个动态平衡。 34 | * 35 | * 注意这个功能的两个限制: 36 | * 37 | * 只对入口流量起作用(调用类型为EntryType.IN),对出口流量无效。可通过SphU.entry()指定调用类型,如果不指定,默认是EntryType.OUT。 38 | * Entry entry = SphU.entry("resourceName",EntryType.IN); 39 | * 只在 Unix-like 的操作系统上生效 40 | **/ 41 | @RunWith(SpringRunner.class) 42 | public class SentinelSystemTest { 43 | 44 | private static String sentinelResource = "order:add"; 45 | 46 | private static AtomicInteger pass = new AtomicInteger(); 47 | 48 | private static AtomicInteger block = new AtomicInteger(); 49 | 50 | private static AtomicInteger total = new AtomicInteger(); 51 | 52 | private static int latchCnt = 100; 53 | 54 | private CountDownLatch latch = new CountDownLatch(latchCnt); 55 | 56 | private static ExecutorService executorService = Executors.newFixedThreadPool(10); 57 | 58 | 59 | private static void initSystemRule() { 60 | List rules = new ArrayList<>(); 61 | SystemRule rule = new SystemRule(); 62 | // max load is 3 63 | rule.setHighestSystemLoad(3.0); 64 | // max avg rt of all request is 10 ms 65 | rule.setAvgRt(10); 66 | // max total qps is 20 67 | rule.setQps(20); 68 | // max parallel working thread is 10 69 | rule.setMaxThread(10); 70 | 71 | rules.add(rule); 72 | SystemRuleManager.loadRules(Collections.singletonList(rule)); 73 | } 74 | @Test 75 | public void caseDegrade() throws Exception { 76 | initSystemRule(); 77 | for (int i = 0; i < latchCnt; i++) { 78 | executorService.execute(()->{ 79 | long start = System.currentTimeMillis(); 80 | Entry entry = null; 81 | try { 82 | entry = SphU.entry("methodA", EntryType.IN); 83 | Node curNode = entry.getCurNode(); 84 | Node originNode = entry.getOriginNode(); 85 | System.out.println("curNode="+JSON.toJSONString(curNode)); 86 | System.out.println("originNode="+JSON.toJSONString(originNode)); 87 | // token acquired 88 | int passIncr = pass.incrementAndGet(); 89 | System.out.println(Thread.currentThread().getName()+",在"+LocalDateTime.now().toString()+"s,thread正常业务处理……pass="+passIncr); 90 | TimeUnit.MILLISECONDS.sleep(20); 91 | } catch (Exception e) { 92 | e.printStackTrace(); 93 | int blockIncr = block.incrementAndGet(); 94 | System.err.println(Thread.currentThread().getName()+",在"+LocalDateTime.now().toString()+"s,block!blockIncr="+blockIncr); 95 | } finally { 96 | total.incrementAndGet(); 97 | if (entry != null) { 98 | System.out.println(Thread.currentThread().getName()+",执行结束:"+(System.currentTimeMillis()-start)+"ms"); 99 | entry.exit(); 100 | } 101 | latch.countDown(); 102 | } 103 | }); 104 | } 105 | latch.await(); 106 | } 107 | 108 | 109 | 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/sentinel/flow/SentinelFlowQpsTest.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.sentinel.flow; 2 | 3 | import com.alibaba.csp.sentinel.*; 4 | import com.alibaba.csp.sentinel.slots.block.BlockException; 5 | import com.alibaba.csp.sentinel.slots.block.RuleConstant; 6 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; 7 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; 8 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; 9 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.springframework.test.context.junit4.SpringRunner; 13 | 14 | import java.time.LocalDateTime; 15 | import java.util.ArrayList; 16 | import java.util.Collections; 17 | import java.util.List; 18 | import java.util.concurrent.CountDownLatch; 19 | import java.util.concurrent.ExecutorService; 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | 23 | /** 24 | * 25 | * @author: runcoding@163.com 26 | * @date: 2019/07/23 14:47 27 | * @describe: 阿里监控哨兵 28 | **/ 29 | @RunWith(SpringRunner.class) 30 | public class SentinelFlowQpsTest { 31 | 32 | private static String sentinelResource = "order:add"; 33 | 34 | private static int latchCnt = 100; 35 | 36 | private CountDownLatch latch = new CountDownLatch(latchCnt); 37 | 38 | private static ExecutorService executorService = Executors.newFixedThreadPool(20); 39 | 40 | 41 | /**控制qps*/ 42 | private void initFlowQpsRule() { 43 | FlowRule rule = new FlowRule(); 44 | rule.setResource(sentinelResource); 45 | rule.setCount(5); 46 | rule.setGrade(RuleConstant.FLOW_GRADE_QPS); 47 | rule.setLimitApp("default"); 48 | 49 | // 匀速器模式下,设置了 QPS 为 5,则请求每 200 ms 允许通过 1 个 (发生拦截后是直接拒绝,还是排队等待,还是慢启动模式) 50 | rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER); 51 | // 如果更多的请求到达,这些请求会被置于虚拟的等待队列中。等待队列有一个 max timeout,如果请求预计的等待时间超过这个时间会直接被 block 52 | // 在这里,timeout 为 1s 53 | rule.setMaxQueueingTimeMs(1 * 1000); 54 | FlowRuleManager.loadRules(Collections.singletonList(rule)); 55 | } 56 | 57 | @Test 58 | public void caseQps() throws InterruptedException { 59 | initFlowQpsRule(); 60 | for (int i = 0; i < latchCnt; i++) { 61 | executorService.execute(()->{ 62 | // 资源名可使用任意有业务语义的字符串 63 | if (SphO.entry(sentinelResource)) { 64 | // 务必保证finally会被执行 65 | try { 66 | System.out.println(Thread.currentThread().getName()+",在"+LocalDateTime.now().toString()+"s,正常业务处理……"); 67 | } finally { 68 | SphO.exit(); 69 | } 70 | } else { 71 | System.err.println(Thread.currentThread().getName()+",在"+LocalDateTime.now().toString()+"s,block!"); 72 | } 73 | latch.countDown(); 74 | }); 75 | } 76 | latch.await(); 77 | } 78 | 79 | 80 | 81 | 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/sentinel/flow/SentinelFlowThreadTest.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.sentinel.flow; 2 | 3 | import com.alibaba.csp.sentinel.*; 4 | import com.alibaba.csp.sentinel.slots.block.BlockException; 5 | import com.alibaba.csp.sentinel.slots.block.RuleConstant; 6 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; 7 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; 8 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; 9 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.springframework.test.context.junit4.SpringRunner; 13 | 14 | import java.time.LocalDateTime; 15 | import java.util.ArrayList; 16 | import java.util.Collections; 17 | import java.util.List; 18 | import java.util.concurrent.CountDownLatch; 19 | import java.util.concurrent.ExecutorService; 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | 23 | /** 24 | * 25 | * @author: runcoding@163.com 26 | * @date: 2019/07/23 14:47 27 | * @describe: 阿里监控哨兵 28 | **/ 29 | @RunWith(SpringRunner.class) 30 | public class SentinelFlowThreadTest { 31 | 32 | private static String sentinelResource = "order:add"; 33 | 34 | private static int latchCnt = 100; 35 | 36 | private CountDownLatch latch = new CountDownLatch(latchCnt); 37 | 38 | private static ExecutorService executorService = Executors.newFixedThreadPool(20); 39 | 40 | /** 41 | * 每秒运行1个线程执行一次 42 | * */ 43 | private void initFlowThreadRule() { 44 | FlowRule rule = new FlowRule(); 45 | rule.setResource(sentinelResource); 46 | rule.setCount(1); 47 | rule.setGrade(RuleConstant.FLOW_GRADE_THREAD); 48 | rule.setLimitApp("default"); 49 | FlowRuleManager.loadRules(Collections.singletonList(rule)); 50 | } 51 | /** 52 | * 53 | * 服务处理http 请求4个,每秒处理1个 54 | */ 55 | @Test 56 | public void caseThread() throws Exception { 57 | executorService = Executors.newFixedThreadPool(4); 58 | initFlowThreadRule(); 59 | for (int i = 0; i < latchCnt; i++) { 60 | executorService.execute(()->{ 61 | if (SphO.entry(sentinelResource)) { 62 | try { 63 | System.out.println(Thread.currentThread().getName()+",在"+LocalDateTime.now().toString()+"s,thread正常业务处理……"); 64 | } finally { 65 | SphO.exit(); 66 | } 67 | } else { 68 | System.err.println(Thread.currentThread().getName()+",在"+LocalDateTime.now().toString()+"s,block!"); 69 | } 70 | latch.countDown(); 71 | }); 72 | } 73 | latch.await(); 74 | } 75 | 76 | 77 | 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/test/LoadAverage.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.test; 2 | 3 | import com.alibaba.csp.sentinel.util.HostNameUtil; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | import java.lang.management.GarbageCollectorMXBean; 9 | import java.lang.management.ManagementFactory; 10 | import java.lang.management.OperatingSystemMXBean; 11 | import java.lang.management.ThreadInfo; 12 | import java.util.List; 13 | import java.util.concurrent.*; 14 | 15 | /** 16 | * 17 | * @author: runcoding@163.com 18 | * @date: 2019/07/24 14:59 19 | * @describe: 系统负载测试 20 | **/ 21 | @RunWith(SpringRunner.class) 22 | public class LoadAverage { 23 | 24 | private static int latchCnt = 2048; 25 | 26 | private CountDownLatch latch = new CountDownLatch(latchCnt); 27 | 28 | 29 | private static ExecutorService executorService = Executors.newFixedThreadPool(20); 30 | 31 | @Test 32 | public void loadAverage() throws InterruptedException { 33 | OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean(); 34 | 35 | double cpu = os.getSystemLoadAverage()/ os.getAvailableProcessors(); 36 | System.out.println("Load Average: " + os.getSystemLoadAverage()+",="+cpu); 37 | for (int i = 0; i <= latchCnt; i++) { 38 | executorService.execute(()->{ 39 | ThreadInfo[] threadInfos = ManagementFactory.getThreadMXBean().dumpAllThreads(false, false); 40 | latch.countDown(); 41 | }); 42 | } 43 | latch.await(); 44 | os = ManagementFactory.getOperatingSystemMXBean(); 45 | cpu = os.getSystemLoadAverage()/ os.getAvailableProcessors(); 46 | System.out.println("Load Average: " + os.getSystemLoadAverage()+",="+cpu+"="+HostNameUtil.getConfigString()); 47 | 48 | 49 | //获取young GC 和full GC 次数 50 | List list1=ManagementFactory.getGarbageCollectorMXBeans(); 51 | for(GarbageCollectorMXBean e:list1){ 52 | System.out.println(String.format("name=%s,count=%s,time=%s",e.getName(),e.getCollectionCount(),e.getCollectionTime())); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/test/MonitorTest.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.test; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.runcoding.monitor.MonitorApplication; 5 | import com.runcoding.monitor.web.dao.MetricInfoMapper; 6 | import com.runcoding.monitor.web.job.MonitorJob; 7 | import com.runcoding.monitor.web.model.metrics.MethodMetricInfo; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.context.annotation.Import; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | import java.time.LocalDateTime; 18 | import java.time.format.DateTimeFormatter; 19 | import java.util.List; 20 | 21 | /** 22 | * @desc 服务 23 | * @author runcoding 24 | * @date: 2018年01月31日 25 | */ 26 | @RunWith(SpringRunner.class) 27 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 28 | @Import(MonitorApplication.class) 29 | public class MonitorTest { 30 | 31 | private final Logger logger = LoggerFactory.getLogger(MonitorTest.class); 32 | 33 | @Autowired 34 | private MetricInfoMapper metricInfoMapper; 35 | 36 | @Autowired 37 | private MonitorJob monitorJob; 38 | 39 | @Test 40 | public void exec(){ 41 | monitorJob.monitorHour(); 42 | } 43 | 44 | @Test 45 | public void findMetricInfo() { 46 | List list = metricInfoMapper.findMetricInfo(0); 47 | logger.info(JSON.toJSONString(list)); 48 | 49 | } 50 | 51 | @Test 52 | public void insert(){ 53 | for (int i = 0; i < 1000 ; i++) { 54 | try{ 55 | MethodMetricInfo serviceAnalysis = new MethodMetricInfo(); 56 | serviceAnalysis.setName("insert"); 57 | serviceAnalysis.setRefDate("2018-04-12 00:00:00"+i); 58 | serviceAnalysis.setCntRequest(1L); 59 | metricInfoMapper.insert(serviceAnalysis); 60 | }catch (Exception e){ 61 | e.printStackTrace(); 62 | } 63 | } 64 | /**清空7天之前的数据*/ 65 | int deleteNum = metricInfoMapper.delete(LocalDateTime.now().plusDays(-7). 66 | format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 67 | if(deleteNum>0){ 68 | /**释放磁盘空间*/ 69 | int vacuum = metricInfoMapper.vacuum(); 70 | System.out.println("vacuum="+vacuum); 71 | } 72 | } 73 | 74 | @Test 75 | public void delete(){ 76 | metricInfoMapper.delete("2018-03-30 00:00:00"); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/test/MonitorTestController.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.test; 2 | 3 | import com.runcoding.monitor.dto.Resp; 4 | import com.runcoding.monitor.web.utils.IPAddressAnalysor; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.cloud.context.config.annotation.RefreshScope; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestParam; 14 | import org.springframework.web.bind.annotation.ResponseBody; 15 | 16 | import javax.servlet.http.HttpServletRequest; 17 | 18 | /** 19 | * 20 | * @author: runcoding@163.com 21 | * @date: 2019/07/11 17:10 22 | * @describe: 23 | **/ 24 | @Controller 25 | @RequestMapping("test") 26 | @RefreshScope 27 | public class MonitorTestController { 28 | 29 | private Logger logger = LoggerFactory.getLogger(MonitorTestController.class); 30 | 31 | @Autowired 32 | private MonitorTestService monitorTestService; 33 | 34 | 35 | /**配置最高系统加载平均值,是排队到可用处理器的可运行实体数目与可用处理器上可运行实体数目的总和在某一段时间进行平均的结果*/ 36 | @Value("${csp.sentinel.rules.system.systemLoad:-1}") 37 | private double highestSystemLoad ; 38 | 39 | /** 40 | * 测试降级方法 41 | * siege -c 100 -r 10000 "http://localhost:8080/test/degrade?degrade=10" 42 | * */ 43 | @GetMapping(value = "/degrade" ) 44 | @ResponseBody 45 | public Resp degrade(boolean isNull, @RequestParam(value = "degrade",defaultValue = "200")Long degrade, 46 | HttpServletRequest request) throws InterruptedException { 47 | if(isNull){ 48 | throw new NullPointerException("aa"); 49 | } 50 | String ipAddress = IPAddressAnalysor.getIPAddress(request); 51 | logger.info("ip={}",ipAddress); 52 | monitorTestService.degrade(degrade); 53 | return Resp.success(); 54 | } 55 | 56 | 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/test/MonitorTestService.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.test; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | /** 6 | * 7 | * @author: runcoding@163.com 8 | * @date: 2019/07/11 17:08 9 | * @describe: 1 10 | **/ 11 | @Service 12 | public class MonitorTestService { 13 | 14 | 15 | public void degrade(Long degrade) throws InterruptedException { 16 | Thread.sleep(degrade); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /monitor-spring/src/test/java/com/runcoding/monitor/test/TreadTest.java: -------------------------------------------------------------------------------- 1 | package com.runcoding.monitor.test; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.serializer.SerializerFeature; 5 | import org.junit.Test; 6 | 7 | import java.util.Collection; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.concurrent.*; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | 14 | /** 15 | * 16 | * @author: runcoding@163.com 17 | * @date: 2019/07/30 17:17 18 | * @describe: 系统负载测试 19 | **/ 20 | public class TreadTest { 21 | 22 | private static int latchCnt = 100; 23 | 24 | private CountDownLatch latch = new CountDownLatch(latchCnt); 25 | 26 | private Map threadMap = new HashMap<>(); 27 | 28 | private static AtomicLong processThreads = new AtomicLong(); 29 | 30 | private static ExecutorService executorService = new ThreadPoolExecutor( 31 | 10, 32 | /**最大3个线程*/ 33 | 10, 34 | 5000L, TimeUnit.MILLISECONDS, 35 | new ArrayBlockingQueue<>(16), 36 | /**拒绝策略使用当前运行线程执行,放弃异步执行使用同步*/ 37 | new ThreadPoolExecutor.CallerRunsPolicy()); 38 | 39 | @Test 40 | public void loadAverage() throws InterruptedException { 41 | for (int i = 0; i <= latchCnt; i++) { 42 | executorService.execute(()->{ 43 | Thread thread = Thread.currentThread(); 44 | try{ 45 | System.out.println(String.format(" exec threadId=%s",thread.getId())); 46 | long andIncrement = processThreads.getAndIncrement(); 47 | if( andIncrement%2 == 1){ 48 | threadMap.put(thread.getId(),thread); 49 | Thread.sleep(2000); 50 | } 51 | if( andIncrement%2 == 0){ 52 | if(!threadMap.values().isEmpty()){ 53 | Thread thread1 = threadMap.values().iterator().next(); 54 | System.out.println(String.format("interrupt andIncrement=%s threadId=%s",andIncrement,thread1.getId())); 55 | threadMap.remove(thread1.getId()); 56 | thread1.interrupt(); 57 | } 58 | } 59 | }catch (Exception e){ 60 | System.err.println(String.format("error threadId=%s ,e=%s",thread.getId(),e.getMessage())); 61 | } finally{ 62 | latch.countDown(); 63 | } 64 | }); 65 | } 66 | latch.await(); 67 | System.out.println("threadMap="+JSON.toJSONString(threadMap,SerializerFeature.DisableCircularReferenceDetect)); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /monitor-spring/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | #debug: true 2 | spring: 3 | profiles: 4 | active: dev 5 | 6 | server: 7 | port: 8080 8 | tomcat: 9 | uri-encoding: UTF-8 10 | max-threads: 10 11 | 12 | logging: 13 | level: 14 | org.springframework: info 15 | 16 | eureka: 17 | instance: 18 | hostname: localhost 19 | client: 20 | register-with-eureka: false 21 | fetch-registry: false -------------------------------------------------------------------------------- /monitor-spring/src/test/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: monitor-center 4 | alias: Monitor中心 5 | 6 | 7 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | public 5 | 6 | com.runcoding 7 | runcoding 8 | 1.0.0-SNAPSHOT 9 | pom 10 | 11 | 12 | monitor-spring 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------