├── 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 | 
26 |
27 | ### 查看每分钟接口调用速率均值(每小时统计一次)
28 | - 地址: https://metrics-spring-boot.herokuapp.com//monitor/metric.html
29 | 
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 > \n > ####10点20分发布 [天气](http://www.thinkpage.cn/) "}
18 | * * actionCard : {"title":"乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身","text":" \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 : 
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 | > 
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 = "";
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='',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:/(
28 |
29 |
30 |
31 |
32 |
71 |
72 |