├── .gitignore ├── LICENSE ├── RCT-Analyze ├── docker │ ├── Dockerfile │ └── start.sh ├── package.xml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── nesc │ │ │ └── ecbd │ │ │ ├── Analyzer.java │ │ │ ├── cache │ │ │ └── AppCache.java │ │ │ ├── config │ │ │ └── RestTemplateConfig.java │ │ │ ├── controller │ │ │ └── RDBAnalyzeController.java │ │ │ ├── entity │ │ │ └── RDBAnalyzeInfo.java │ │ │ ├── service │ │ │ ├── RDBAnalyzeService.java │ │ │ ├── ReportService.java │ │ │ ├── SimpleAnalyzerManager.java │ │ │ └── rdbanalyze │ │ │ │ ├── AbstractAnalyzer.java │ │ │ │ ├── DataTypeAnalyzer.java │ │ │ │ ├── ExportKeyByFilterAnalyzer.java │ │ │ │ ├── ExportKeyByPrefixAnalyzer.java │ │ │ │ ├── PrefixAnalyzer.java │ │ │ │ ├── TTLAnalyzer.java │ │ │ │ └── TopKeyAnalyzer.java │ │ │ ├── utils │ │ │ ├── RedisObjectEstimate.java │ │ │ └── Report.java │ │ │ └── worker │ │ │ ├── AnalyzerWorker.java │ │ │ └── AsyncSender.java │ └── resources │ │ ├── application.properties │ │ └── logback.xml │ └── test │ └── java │ └── org │ └── nesc │ └── ecbd │ ├── RedisTest.java │ └── test │ └── StringReplaceTest.java ├── RCT-Base ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── nesc │ └── ecbd │ ├── entity │ ├── AnalyzeInstance.java │ ├── AnalyzeStatus.java │ ├── AnalyzerConstant.java │ ├── ScheduleDetail.java │ └── ScheduleInfo.java │ └── utils │ ├── BytesUtil.java │ ├── ElasticSearchUtil.java │ ├── FileCopyUtil.java │ ├── Jemalloc.java │ ├── RedisInfoTool.java │ ├── RedisMemCalculate.java │ └── RedisUtil.java ├── RCT-Dashboard ├── .gitignore ├── db │ └── data.db ├── docker │ ├── Dockerfile │ └── start.sh ├── package.xml ├── pom.xml ├── src │ ├── main │ │ ├── frontend │ │ │ ├── .editorconfig │ │ │ ├── .eslintignore │ │ │ ├── .eslintrc │ │ │ ├── .gitignore │ │ │ ├── .webpackrc.js │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.png │ │ │ │ ├── images │ │ │ │ │ ├── chart.png │ │ │ │ │ ├── client.png │ │ │ │ │ ├── fail.jpg │ │ │ │ │ ├── list.png │ │ │ │ │ ├── statistical.png │ │ │ │ │ ├── success.jpg │ │ │ │ │ └── time.png │ │ │ │ └── index.html │ │ │ └── src │ │ │ │ ├── axios.js │ │ │ │ ├── components │ │ │ │ ├── CustomBreadcrumb │ │ │ │ │ ├── CustomBreadcrumb.jsx │ │ │ │ │ └── index.js │ │ │ │ ├── CustomTable │ │ │ │ │ ├── CustomTable.jsx │ │ │ │ │ ├── CustomTable.scss │ │ │ │ │ └── index.js │ │ │ │ └── NotFound │ │ │ │ │ ├── NotFound.jsx │ │ │ │ │ ├── NotFound.scss │ │ │ │ │ ├── images │ │ │ │ │ └── notFound.png │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ ├── layouts │ │ │ │ ├── BasicLayout │ │ │ │ │ ├── MainRoutes.jsx │ │ │ │ │ ├── components │ │ │ │ │ │ ├── Footer │ │ │ │ │ │ │ ├── Footer.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── Header │ │ │ │ │ │ │ ├── Header.jsx │ │ │ │ │ │ │ ├── Header.scss │ │ │ │ │ │ │ ├── images │ │ │ │ │ │ │ │ └── avatar.png │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── Logo │ │ │ │ │ │ │ ├── Logo.jsx │ │ │ │ │ │ │ ├── images │ │ │ │ │ │ │ └── logo.png │ │ │ │ │ │ │ └── index.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── DashLayout │ │ │ │ │ ├── MainRoutes.jsx │ │ │ │ │ ├── components │ │ │ │ │ │ ├── Footer │ │ │ │ │ │ │ ├── Footer.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── Header │ │ │ │ │ │ │ ├── Header.jsx │ │ │ │ │ │ │ ├── Header.scss │ │ │ │ │ │ │ ├── images │ │ │ │ │ │ │ │ └── avatar.png │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── Logo │ │ │ │ │ │ │ ├── Logo.jsx │ │ │ │ │ │ │ ├── images │ │ │ │ │ │ │ └── logo.png │ │ │ │ │ │ │ └── index.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ └── UserLayout │ │ │ │ │ ├── components │ │ │ │ │ ├── Footer │ │ │ │ │ │ └── index.js │ │ │ │ │ ├── Header │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── index.scss │ │ │ │ │ └── Intro │ │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ │ ├── menuConfig.js │ │ │ │ ├── pages │ │ │ │ ├── ClientList │ │ │ │ │ ├── ClientList.jsx │ │ │ │ │ ├── components │ │ │ │ │ │ ├── ClientList │ │ │ │ │ │ │ ├── ClientList.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── ClientMain │ │ │ │ │ │ │ ├── ClientMain.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── Monitors │ │ │ │ │ │ │ ├── Monitors.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── Statistics │ │ │ │ │ │ │ ├── Statistics.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ │ ├── Dashboard │ │ │ │ │ ├── Dashboard.jsx │ │ │ │ │ ├── components │ │ │ │ │ │ ├── CountRateLineChart │ │ │ │ │ │ │ ├── CountRateLineChart.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── KeyCountByType │ │ │ │ │ │ │ ├── KeyCountByType.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── KeyMemoryByType │ │ │ │ │ │ │ ├── KeyMemoryByType.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── LineChart │ │ │ │ │ │ │ ├── LineChart.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── MemoryRateLineChart │ │ │ │ │ │ │ ├── MemoryRateLineChart.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── PrefixKeyTable │ │ │ │ │ │ │ ├── KeyCountTable.jsx │ │ │ │ │ │ │ ├── PrefixKeyTable.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── PrefixTTL.scss │ │ │ │ │ │ ├── PrefixTTLTable │ │ │ │ │ │ │ ├── PrefixTTLTable.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── TopKeyTable │ │ │ │ │ │ │ ├── HashTopKey.jsx │ │ │ │ │ │ │ ├── ListTopKey.jsx │ │ │ │ │ │ │ ├── SetTopKey.jsx │ │ │ │ │ │ │ ├── StringTopKey.jsx │ │ │ │ │ │ │ ├── TopKeyTable.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ │ ├── RDBAnalyze │ │ │ │ │ ├── RDBAnalyze.jsx │ │ │ │ │ ├── components │ │ │ │ │ │ ├── RDBAnalyzeList │ │ │ │ │ │ │ ├── RDBAnalyzeList.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── RDBMain │ │ │ │ │ │ │ ├── RDBMain.jsx │ │ │ │ │ │ │ ├── RDBMainPage.jsx │ │ │ │ │ │ │ ├── StartAnalyze.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── RDBNodeList │ │ │ │ │ │ │ ├── RDBNodeList.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ │ ├── RedisInfo │ │ │ │ │ ├── components │ │ │ │ │ │ └── rct │ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ │ └── rct.jsx │ │ │ │ │ ├── index.js │ │ │ │ │ └── redisInfo.jsx │ │ │ │ ├── Slowlog │ │ │ │ │ ├── Slowlog.jsx │ │ │ │ │ ├── components │ │ │ │ │ │ ├── SlowlogList │ │ │ │ │ │ │ ├── SlowlogList.jsx │ │ │ │ │ │ │ ├── SlowlogList.scss │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── SlowlogMain │ │ │ │ │ │ │ ├── SlowlogMain.jsx │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── SlowlogMonitors │ │ │ │ │ │ │ ├── SlowlogMonitors.jsx │ │ │ │ │ │ │ ├── SlowlogMonitors.scss │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── SlowlogStatis │ │ │ │ │ │ │ ├── SlowlogStatis.jsx │ │ │ │ │ │ │ ├── SlowlogStatis.scss │ │ │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ │ └── UserLogin │ │ │ │ │ ├── UserLogin.jsx │ │ │ │ │ └── index.js │ │ │ │ ├── router.jsx │ │ │ │ └── routerConfig.js │ │ ├── java │ │ │ └── org │ │ │ │ └── nesc │ │ │ │ └── ecbd │ │ │ │ ├── StartApp.java │ │ │ │ ├── cache │ │ │ │ └── AppCache.java │ │ │ │ ├── common │ │ │ │ ├── BaseController.java │ │ │ │ ├── JobFactory.java │ │ │ │ ├── RestResponse.java │ │ │ │ └── SuperMapper.java │ │ │ │ ├── config │ │ │ │ ├── CorsConfig.java │ │ │ │ ├── InitConfig.java │ │ │ │ ├── InitServer.java │ │ │ │ ├── MybatisPlusConfig.java │ │ │ │ ├── QuartzSchedule.java │ │ │ │ └── ResponseConfig.java │ │ │ │ ├── controller │ │ │ │ ├── ClientListController.java │ │ │ │ ├── IndexController.java │ │ │ │ ├── RDBAnalyzeController.java │ │ │ │ ├── RedisInfoController.java │ │ │ │ └── SlowlogController.java │ │ │ │ ├── entity │ │ │ │ ├── AnalyzeInstance.java │ │ │ │ ├── ExcelData.java │ │ │ │ ├── RDBAnalyze.java │ │ │ │ ├── RDBAnalyzeResult.java │ │ │ │ ├── RedisInfo.java │ │ │ │ ├── ReportData.java │ │ │ │ ├── SlowlogEntity.java │ │ │ │ ├── SlowlogInfo.java │ │ │ │ └── TopKeyReportData.java │ │ │ │ ├── mapper │ │ │ │ ├── RDBAnalyzeMapper.java │ │ │ │ ├── RDBAnalyzeProvider.java │ │ │ │ ├── RDBAnalyzeResultMapper.java │ │ │ │ ├── RedisInfoMapper.java │ │ │ │ └── SlowlogMapper.java │ │ │ │ ├── report │ │ │ │ ├── EmailSendReport.java │ │ │ │ ├── IAnalyzeDataConverse.java │ │ │ │ ├── converseFactory │ │ │ │ │ └── ReportDataConverseFacotry.java │ │ │ │ └── impl │ │ │ │ │ ├── DataTypeDataConverse.java │ │ │ │ │ ├── PrefixDataConverse.java │ │ │ │ │ ├── TTLDataConverse.java │ │ │ │ │ └── TopKeyDataConverse.java │ │ │ │ ├── service │ │ │ │ ├── ClientListService.java │ │ │ │ ├── RDBAnalyzeResultService.java │ │ │ │ ├── RDBAnalyzeService.java │ │ │ │ ├── RDBScheduleJob.java │ │ │ │ ├── RedisInfoService.java │ │ │ │ ├── ScheduleTaskService.java │ │ │ │ ├── SlowlogCollectorSchedule.java │ │ │ │ ├── SlowlogScheduleJob.java │ │ │ │ └── SlowlogService.java │ │ │ │ ├── thread │ │ │ │ └── AnalyzerStatusThread.java │ │ │ │ └── util │ │ │ │ ├── BytesConverseUtil.java │ │ │ │ ├── EurekaUtil.java │ │ │ │ ├── ExcelUtil.java │ │ │ │ ├── FileUtil.java │ │ │ │ ├── ListSortUtil.java │ │ │ │ ├── SpringContextHolder.java │ │ │ │ └── SqliteUtil.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── banner.txt │ │ │ ├── logback.xml │ │ │ └── static │ │ │ ├── css │ │ │ └── index.css │ │ │ ├── favicon.png │ │ │ ├── index.html │ │ │ ├── js │ │ │ └── index.js │ │ │ └── public │ │ │ └── images │ │ │ ├── avatar.png │ │ │ ├── chart.png │ │ │ ├── client.png │ │ │ ├── fail.jpg │ │ │ ├── list.png │ │ │ ├── notFound.png │ │ │ ├── statistical.png │ │ │ ├── success.jpg │ │ │ └── time.png │ └── test │ │ └── java │ │ └── org │ │ └── nesc │ │ └── ecbd │ │ ├── RedisTest.java │ │ ├── SqliteTest.java │ │ └── test │ │ └── RedisDataTypeTest.java └── template │ └── RCT_Report_Template.xlsx ├── README.md ├── README_zh.md ├── doc ├── Chart_module.md ├── Code_structure_introduction.md ├── Design_scheme.md ├── Introduction_to_clientList_module.md ├── Introduction_to_rdb_analysis_module.md ├── SlowLog_module_introduction.md ├── TODO.md ├── Use_rdb_analysis_tool_for_quick_analysis.md ├── add_an_instance_of_redis.md ├── screenshots │ ├── chart模块可视化功能介绍.jpg │ ├── rct.jpg │ ├── 数据流.png │ └── 系统架构.jpg └── zh-CN │ ├── Chart模块介绍.md │ ├── TODO.md │ ├── 代码结构介绍.md │ ├── 如何使用RDB分析工具.md │ ├── 如何使用clientList分析工具.md │ ├── 如何使用slowlog分析工具.md │ ├── 如何增加redis实例.md │ ├── 如何快速使用RDB分析工具进行分析.md │ └── 设计方案.md ├── mvnw ├── mvnw.cmd └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .project 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | nbproject/private/ 22 | build/ 23 | nbbuild/ 24 | dist/ 25 | nbdist/ 26 | .nb-gradle/ 27 | logs/ 28 | 29 | 30 | # See https://help.github.com/ignore-files/ for more about ignoring files. 31 | 32 | # dependencies 33 | FrontEnd/node_modules 34 | 35 | # production 36 | FrontEnd/build 37 | FrontEnd/dist 38 | 39 | # misc 40 | .idea/ 41 | .happypack 42 | .DS_Store 43 | 44 | npm-debug.log* 45 | yarn-debug.log* 46 | yarn-error.log* 47 | 48 | *.db 49 | 50 | */rdbtemp/dump.rdb 51 | .vscode/ 52 | RCT-Dashboard/src/main/frontend/package-lock.json 53 | -------------------------------------------------------------------------------- /RCT-Analyze/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ##################### 2 | #version: RCT 1.0 3 | #describe rct-analyze 4 | #truman create 5 | # 6 | FROM openjdk:8u181-jdk-alpine3.8 7 | LABEL author="Truman.p.Du" 8 | COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas 9 | RUN apk upgrade --update && \ 10 | apk add --update curl tar bash 11 | ENV BASE_DIR /opt/app/rct/rct-analyze 12 | WORKDIR ${BASE_DIR} 13 | ENV RCT_NAME RCT-Analyze 14 | ENV VERSION 2.1.1 15 | RUN apk upgrade --update && \ 16 | apk add --update curl bash 17 | RUN cd ${BASE_DIR} && \ 18 | curl -fsSL -o ${RCT_NAME}-${VERSION}-release.tar.gz https://github.com/xaecbd/RCT/releases/download/v${VERSION}/${RCT_NAME}-${VERSION}-release.tar.gz && \ 19 | tar xvf ${RCT_NAME}-${VERSION}-release.tar.gz && \ 20 | rm -rf ${RCT_NAME}-${VERSION}-release.tar.gz 21 | ADD start.sh ${BASE_DIR} 22 | CMD ["sh","start.sh"] 23 | -------------------------------------------------------------------------------- /RCT-Analyze/docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ ! -d "config" ];then 3 | mkdir config 4 | fi 5 | 6 | appName=`ls |grep jar|grep RCT` 7 | echo start to run $appName 8 | 9 | if [ -n "$JAVA_OPTIONS" ];then 10 | java $JAVA_OPTIONS -jar $appName $option 11 | else 12 | java -jar $appName $option 13 | fi 14 | -------------------------------------------------------------------------------- /RCT-Analyze/package.xml: -------------------------------------------------------------------------------- 1 | 2 | release 3 | 4 | tar.gz 5 | 6 | false 7 | 8 | 9 | ${project.build.directory} 10 | 11 | 12 | *.jar 13 | 14 | 15 | 16 | ${project.basedir}/src/main/resources 17 | config 18 | 19 | application.properties 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RCT-Analyze/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | RCT 8 | org.nesc.ecbd 9 | 2.1.2 10 | 11 | 12 | RCT-Analyze 13 | jar 14 | 15 | RCT-Analyze 16 | http://maven.apache.org 17 | 18 | 19 | UTF-8 20 | 21 | 22 | 23 | 24 | org.springframework.cloud 25 | spring-cloud-starter-eureka 26 | 1.4.4.RELEASE 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.nesc.ecbd 35 | RCT-Base 36 | 2.1.2 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | true 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-assembly-plugin 52 | 53 | 54 | package.xml 55 | 56 | 57 | 58 | 59 | make-assembly 60 | package 61 | 62 | single 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/Analyzer.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd; 2 | 3 | import org.nesc.ecbd.entity.AnalyzeStatus; 4 | import org.nesc.ecbd.service.RDBAnalyzeService; 5 | import org.nesc.ecbd.utils.ElasticSearchUtil; 6 | import org.nesc.ecbd.utils.Report; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.boot.CommandLineRunner; 11 | import org.springframework.boot.SpringApplication; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 14 | 15 | /** 16 | * @author:Truman.P.Du 17 | * @createDate: 2018年3月21日 下午3:59:19 18 | * @version:1.0 19 | * @description: 程序入口 20 | */ 21 | @SpringBootApplication 22 | @EnableDiscoveryClient 23 | public class Analyzer implements CommandLineRunner { 24 | private final static Logger LOG = LoggerFactory.getLogger(Analyzer.class); 25 | 26 | @Value("${rct.elasticsearch.rest.urls}") 27 | private String esUrls; 28 | 29 | @Value("${rct.elasticsearch.index}") 30 | private String esIndex; 31 | @Value("${rct.elasticsearch.enable:false}") 32 | private boolean elasticsearchEnable; 33 | 34 | @Value("${rct.filter.bytes:512}") 35 | private Long filterBytes; 36 | 37 | @Value("${rct.filter.item.count:10}") 38 | private Integer filterItemCount; 39 | 40 | @Value("${rct.max.es.thread.num:10}") 41 | private Integer maxSenderThread; 42 | 43 | @Value("${eureka.client.serviceUrl.defaultZone}") 44 | private String serviceUrl; 45 | 46 | @Value("${rct.analyze.key.prefix.index.location:last}") 47 | private String keyPrefixIndexLocation; 48 | 49 | @Value("${rct.analyze.key.prefix.separators}") 50 | private String keyPrefixSeparators; 51 | 52 | 53 | public static String ESINDEX = null; 54 | public static String REGISTER_ROOT = null; 55 | public static Boolean USE_Custom_Algo = true; 56 | public static Boolean INCLUDE_NoMatchPrefixKey = false; 57 | public static Long FILTER_Bytes; 58 | public static Integer FILTER_ItemCount; 59 | public static Integer MAX_EsSenderThreadNum; 60 | public static Integer MAX_QUEUE_SIZE; 61 | public static String KEY_PREFIX_INDEX_LOCATION; 62 | public static String KEY_PREFIX_SEPARATORS; 63 | 64 | public static void main(String[] args) { 65 | SpringApplication.run(Analyzer.class, args); 66 | LOG.info("RCT analyzer start success!"); 67 | } 68 | 69 | @Override 70 | public void run(String... args) throws Exception { 71 | RDBAnalyzeService.setStatus(AnalyzeStatus.NOTINIT); 72 | if(elasticsearchEnable) { 73 | ElasticSearchUtil.init(30000, 30000, esUrls); 74 | } 75 | 76 | Report.init(30000, 30000, serviceUrl); 77 | ESINDEX = this.esIndex; 78 | FILTER_Bytes = this.filterBytes; 79 | FILTER_ItemCount = this.filterItemCount; 80 | MAX_EsSenderThreadNum = this.maxSenderThread; 81 | MAX_QUEUE_SIZE = 1000; 82 | KEY_PREFIX_INDEX_LOCATION = this.keyPrefixIndexLocation; 83 | KEY_PREFIX_SEPARATORS = this.keyPrefixSeparators; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/cache/AppCache.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.cache; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * @author:Truman.P.Du 11 | * @createDate: 2018年10月23日 下午2:16:32 12 | * @version:1.0 13 | * @description: 应用缓存 14 | */ 15 | public class AppCache { 16 | 17 | public static Map> reportCacheMap = new ConcurrentHashMap>(); 18 | public static List redisUsedMems = new ArrayList(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.nesc.ecbd.config; 5 | 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.client.ClientHttpRequestFactory; 9 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 10 | import org.springframework.web.client.RestTemplate; 11 | 12 | /** 13 | * @author Hulva Luva.H 14 | * @since 2018年4月12日 15 | * 16 | */ 17 | @Configuration 18 | public class RestTemplateConfig { 19 | @Bean 20 | public RestTemplate buildRestTemplate() { 21 | return new RestTemplate(simpleClientHttpRequestFactory()); 22 | } 23 | 24 | public ClientHttpRequestFactory simpleClientHttpRequestFactory() { 25 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 26 | factory.setReadTimeout(10000); 27 | factory.setConnectTimeout(15000); 28 | return factory; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/entity/RDBAnalyzeInfo.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.entity; 2 | 3 | import com.moilioncircle.redis.replicator.rdb.datatype.KeyValuePair; 4 | 5 | /** 6 | * @author:Truman.P.Du 7 | * @createDate: 2019年2月21日 下午1:33:05 8 | * @version:1.0 9 | * @param 10 | * @param 11 | * @description: 12 | */ 13 | public class RDBAnalyzeInfo { 14 | 15 | private Long bytesSize; 16 | 17 | private KeyValuePair kv; 18 | 19 | public RDBAnalyzeInfo(KeyValuePair kv, Long bytesSize) { 20 | this.bytesSize = bytesSize; 21 | this.kv = kv; 22 | } 23 | 24 | public Long getBytesSize() { 25 | return bytesSize; 26 | } 27 | 28 | public void setBytesSize(Long bytesSize) { 29 | this.bytesSize = bytesSize; 30 | } 31 | 32 | public KeyValuePair getKv() { 33 | return kv; 34 | } 35 | 36 | public void setKv(KeyValuePair kv) { 37 | this.kv = kv; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/service/ReportService.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.service; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.HashSet; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | import org.nesc.ecbd.cache.AppCache; 9 | import org.nesc.ecbd.entity.AnalyzerConstant; 10 | import org.springframework.stereotype.Service; 11 | 12 | import com.alibaba.fastjson.JSONObject; 13 | 14 | /** 15 | * @author Truman.P.Du 16 | * @date Aug 1, 2019 3:12:05 PM 17 | * @version 1.0 18 | */ 19 | @Service 20 | public class ReportService { 21 | 22 | public String fixReportData() { 23 | Map> reportCacheMap = AppCache.reportCacheMap; 24 | long usedMem = 0l; 25 | for (Long mem : AppCache.redisUsedMems) { 26 | usedMem = mem + usedMem; 27 | } 28 | if (usedMem == 0) { 29 | String report = JSONObject.toJSONString(reportCacheMap); 30 | return report; 31 | } 32 | Set dataTypeAnalyzeResult = AppCache.reportCacheMap.get(AnalyzerConstant.DATA_TYPE_ANALYZER + ""); 33 | Set newDataTypeAnalyzeResult = new HashSet<>(); 34 | long estimateMem = 0l; 35 | for (String str : dataTypeAnalyzeResult) { 36 | JSONObject jsonObject = JSONObject.parseObject(str); 37 | estimateMem = estimateMem + jsonObject.getLong("bytes"); 38 | } 39 | 40 | BigDecimal precisionRate = new BigDecimal(usedMem).divide(new BigDecimal(estimateMem),4, BigDecimal.ROUND_HALF_UP); 41 | 42 | for (String str : dataTypeAnalyzeResult) { 43 | JSONObject jsonObject = JSONObject.parseObject(str); 44 | long bytes = jsonObject.getLong("bytes"); 45 | long fixBytes = new BigDecimal(bytes).multiply(precisionRate).longValue(); 46 | jsonObject.put("bytes", fixBytes); 47 | newDataTypeAnalyzeResult.add(jsonObject.toJSONString()); 48 | } 49 | reportCacheMap.put(AnalyzerConstant.DATA_TYPE_ANALYZER + "", newDataTypeAnalyzeResult); 50 | 51 | 52 | Set prefixAnalyzeResult = AppCache.reportCacheMap.get(AnalyzerConstant.PREFIX_ANALYZER + ""); 53 | Set newPrefixAnalyzeResult = new HashSet<>(); 54 | for (String str : prefixAnalyzeResult) { 55 | JSONObject jsonObject = JSONObject.parseObject(str); 56 | long bytes = jsonObject.getLong("bytes"); 57 | long fixBytes = new BigDecimal(bytes).multiply(precisionRate).longValue(); 58 | jsonObject.put("bytes", fixBytes); 59 | newPrefixAnalyzeResult.add(jsonObject.toJSONString()); 60 | } 61 | reportCacheMap.put(AnalyzerConstant.PREFIX_ANALYZER + "", newPrefixAnalyzeResult); 62 | 63 | Set topAnalyzeResult = AppCache.reportCacheMap.get(AnalyzerConstant.TOP_KEY_ANALYZER + ""); 64 | Set newTopAnalyzeResult = new HashSet<>(); 65 | for (String str : topAnalyzeResult) { 66 | JSONObject jsonObject = JSONObject.parseObject(str); 67 | long bytes = jsonObject.getLong("bytes"); 68 | long fixBytes = new BigDecimal(bytes).multiply(precisionRate).longValue(); 69 | jsonObject.put("bytes", fixBytes); 70 | newTopAnalyzeResult.add(jsonObject.toJSONString()); 71 | } 72 | reportCacheMap.put(AnalyzerConstant.TOP_KEY_ANALYZER + "", newTopAnalyzeResult); 73 | 74 | String report = JSONObject.toJSONString(reportCacheMap); 75 | return report; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/service/rdbanalyze/ExportKeyByFilterAnalyzer.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.service.rdbanalyze; 2 | 3 | import java.util.Map; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | import org.nesc.ecbd.Analyzer; 8 | import org.nesc.ecbd.entity.AnalyzerConstant; 9 | import org.nesc.ecbd.entity.RDBAnalyzeInfo; 10 | import org.nesc.ecbd.service.RDBAnalyzeService; 11 | 12 | import com.alibaba.fastjson.JSONObject; 13 | import com.moilioncircle.redis.replicator.rdb.datatype.KeyValuePair; 14 | 15 | /** 16 | * @author:Truman.P.Du 17 | * @createDate: 2018年10月12日 下午1:41:26 18 | * @version:1.0 19 | * @description:按过滤器导出相应的key到ES分析器 20 | */ 21 | public class ExportKeyByFilterAnalyzer extends AbstractAnalyzer { 22 | private String customPrefix; 23 | private final static Pattern PATTERN = Pattern.compile("\\{\\w+\\}"); 24 | 25 | public ExportKeyByFilterAnalyzer() { 26 | this.setName("prefix"); 27 | } 28 | 29 | @Override 30 | public void init(Map params) { 31 | String analyzePort = params.get("port"); 32 | customPrefix = params.get("customPrefix"); 33 | this.setPort(Integer.parseInt(analyzePort)); 34 | } 35 | 36 | @Override 37 | public void execute(RDBAnalyzeInfo rdbAnalyzeInfo) { 38 | KeyValuePair theKV = rdbAnalyzeInfo.getKv(); 39 | String key = theKV.getKey(); 40 | String prefix = key; 41 | boolean flag = true; 42 | // Prefix 规则 43 | // 1. 配置指定的 44 | if (customPrefix != null) { 45 | String[] prefixes = this.customPrefix.trim().split(","); 46 | for (int i = 0, length = prefixes.length; i < length; i++) { 47 | if (flag && prefixes[i].length() > 0 && key.lastIndexOf(prefixes[i]) != -1) { 48 | prefix = prefixes[i] + "*"; 49 | flag = false; 50 | } 51 | } 52 | } 53 | // 2. {} 54 | Matcher matcher = PATTERN.matcher(key); 55 | if (flag && matcher.find()) { 56 | prefix = matcher.group(0) + "*"; 57 | flag = false; 58 | } 59 | // 3. : 60 | if (flag && key.lastIndexOf(":") != -1) { 61 | prefix = key.substring(0, key.lastIndexOf(":") + 1) + "*"; 62 | flag = false; 63 | } 64 | // 4. | 65 | if (flag && key.lastIndexOf("|") != -1) { 66 | prefix = key.substring(0, key.lastIndexOf("|") + 1) + "*"; 67 | flag = false; 68 | } 69 | // 5. _ 70 | if (flag && key.lastIndexOf("_") != -1) { 71 | prefix = key.substring(0, key.lastIndexOf("_") + 1) + "*"; 72 | flag = false; 73 | } 74 | // 6. - 75 | if (flag && key.lastIndexOf("-") != -1) { 76 | prefix = key.substring(0, key.lastIndexOf("-") + 1) + "*"; 77 | flag = false; 78 | } 79 | // no matches, 直接使用完整key 80 | // 对数据进行筛除 81 | if (flag && !Analyzer.INCLUDE_NoMatchPrefixKey) { 82 | return; 83 | } 84 | if (!flag && keepItOrNot(theKV)) { 85 | try { 86 | String message = this.generateResult(prefix, rdbAnalyzeInfo); 87 | this.async2ES(message); 88 | } catch (Exception e) { 89 | e.printStackTrace(); 90 | } 91 | } 92 | 93 | } 94 | 95 | private String generateResult(String prefix, RDBAnalyzeInfo rdbAnalyzeInfo) throws Exception { 96 | JSONObject item = new JSONObject(); 97 | KeyValuePair kv = rdbAnalyzeInfo.getKv(); 98 | item.put("scheduleId", RDBAnalyzeService.getScheduleInfo().getScheduleID()); 99 | item.put("bytes", rdbAnalyzeInfo.getBytesSize()); 100 | item.put("itemCount", this.calcuItemCount(kv)); 101 | item.put("key", kv.getKey()); 102 | item.put("prefix", prefix); 103 | item.put("time", System.currentTimeMillis()); 104 | item.put("analyzeType", AnalyzerConstant.EXPORT_KEY_BY_FILTER_ANALYZER); 105 | 106 | return item.toJSONString(); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/service/rdbanalyze/ExportKeyByPrefixAnalyzer.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.service.rdbanalyze; 2 | 3 | import java.util.HashSet; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | import org.nesc.ecbd.entity.AnalyzerConstant; 8 | import org.nesc.ecbd.entity.RDBAnalyzeInfo; 9 | import org.nesc.ecbd.service.RDBAnalyzeService; 10 | 11 | import com.alibaba.fastjson.JSONObject; 12 | import com.moilioncircle.redis.replicator.rdb.datatype.KeyValuePair; 13 | 14 | /** 15 | * @author:Truman.P.Du 16 | * @createDate: 2018年10月12日 下午1:40:05 17 | * @version:1.0 18 | * @description:按前缀导出相应的key到ES分析器 19 | */ 20 | public class ExportKeyByPrefixAnalyzer extends AbstractAnalyzer { 21 | 22 | private String customPrefix; 23 | Set batchData = new HashSet<>(); 24 | 25 | public ExportKeyByPrefixAnalyzer() { 26 | this.setName("prefix"); 27 | } 28 | 29 | @Override 30 | public void init(Map params) { 31 | String analyzePort = params.get("port"); 32 | customPrefix = params.get("customPrefix"); 33 | this.setPort(Integer.parseInt(analyzePort)); 34 | 35 | } 36 | 37 | @Override 38 | public void execute(RDBAnalyzeInfo rdbAnalyzeInfo) { 39 | KeyValuePair theKV = rdbAnalyzeInfo.getKv(); 40 | String key = theKV.getKey(); 41 | String prefix = key; 42 | boolean flag = true; 43 | // Prefix 规则 44 | // 1. 配置指定的 45 | if (customPrefix != null) { 46 | String[] prefixes = this.customPrefix.trim().split(","); 47 | for (int i = 0, length = prefixes.length; i < length; i++) { 48 | if (flag && prefixes[i].length() > 0 && key.lastIndexOf(prefixes[i]) != -1) { 49 | prefix = prefixes[i] + "*"; 50 | flag = false; 51 | } 52 | } 53 | } 54 | 55 | if (!flag) { 56 | try { 57 | String message = this.generateResult(prefix, rdbAnalyzeInfo); 58 | this.async2ES(message); 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | } 65 | 66 | private String generateResult(String prefix, RDBAnalyzeInfo rdbAnalyzeInfo) throws Exception { 67 | JSONObject item = new JSONObject(); 68 | KeyValuePair kv = rdbAnalyzeInfo.getKv(); 69 | item.put("scheduleId", RDBAnalyzeService.getScheduleInfo().getScheduleID()); 70 | item.put("bytes",rdbAnalyzeInfo.getBytesSize()); 71 | item.put("itemCount", this.calcuItemCount(kv)); 72 | item.put("key", kv.getKey()); 73 | item.put("time", System.currentTimeMillis()); 74 | item.put("analyzeType", AnalyzerConstant.EXPORT_KEY_BY_PREFIX_ANALYZER); 75 | return item.toJSONString(); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/worker/AnalyzerWorker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.nesc.ecbd.worker; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.concurrent.BlockingQueue; 9 | import java.util.concurrent.LinkedBlockingQueue; 10 | 11 | import org.nesc.ecbd.Analyzer; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * @author Hulva Luva.H 17 | * @since 2018年4月18日 18 | * 19 | */ 20 | public class AnalyzerWorker { 21 | protected final static Logger LOG = LoggerFactory.getLogger(AnalyzerWorker.class); 22 | 23 | public static BlockingQueue cache = new LinkedBlockingQueue<>(Analyzer.MAX_QUEUE_SIZE); 24 | private static final Map workers = new HashMap(); 25 | 26 | public static void startWorker() { 27 | AsyncSender sender = null; 28 | for (int i = 0; i < Analyzer.MAX_EsSenderThreadNum; i++) { 29 | sender = new AsyncSender(); 30 | Thread thread = new Thread(sender); 31 | thread.setName("worker" + i); 32 | thread.start(); 33 | workers.put("worker" + i, sender); 34 | } 35 | } 36 | 37 | public static void stopWorker() { 38 | for (int i = 0; i < workers.size(); i++) { 39 | AsyncSender sender = workers.get("worker" + i); 40 | sender.exit(); 41 | } 42 | } 43 | 44 | public static void workerNumChanged() { 45 | if (Analyzer.MAX_EsSenderThreadNum < 0) { 46 | LOG.info("InValid > MAX_EsSenderThreadNum: {}", Analyzer.MAX_EsSenderThreadNum); 47 | return; 48 | } 49 | AsyncSender sender = null; 50 | if (Analyzer.MAX_EsSenderThreadNum > workers.size()) { 51 | for (int i = workers.size(); i < Analyzer.MAX_EsSenderThreadNum; i++) { 52 | sender = new AsyncSender(); 53 | Thread thread = new Thread(sender); 54 | thread.setName("worker" + i); 55 | thread.start(); 56 | workers.put("worker" + i, sender); 57 | } 58 | } 59 | if (Analyzer.MAX_EsSenderThreadNum < workers.size()) { 60 | for (int i = Analyzer.MAX_EsSenderThreadNum, outsider = workers.size(); i < outsider; i++) { 61 | sender = workers.get("worker" + i); 62 | sender.exit(); 63 | workers.remove("worker" + i); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/java/org/nesc/ecbd/worker/AsyncSender.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.nesc.ecbd.worker; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import org.nesc.ecbd.Analyzer; 11 | import org.nesc.ecbd.utils.ElasticSearchUtil; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * @author Hulva Luva.H 17 | * @since 2018年4月18日 18 | * 19 | */ 20 | public class AsyncSender implements Runnable { 21 | protected final static Logger LOG = LoggerFactory.getLogger(AsyncSender.class); 22 | private boolean exit = false; 23 | 24 | /* 25 | * (non-Javadoc) 26 | * 27 | * @see java.lang.Runnable#run() 28 | */ 29 | @Override 30 | public void run() { 31 | this.doSend(); 32 | } 33 | 34 | private void doSend() { 35 | Set batchData = new HashSet<>(); 36 | long lastSendTime = System.currentTimeMillis(); 37 | while (!exit) { 38 | try { 39 | String dataToSend = AnalyzerWorker.cache.poll(100, TimeUnit.MILLISECONDS); 40 | // 以500批次/或者10s发送一次数据到ES 41 | if (dataToSend != null) { 42 | if (batchData.size() >= 500 || ((System.currentTimeMillis() - lastSendTime) > 1000 * 10)) { 43 | if (!batchData.isEmpty()) { 44 | ElasticSearchUtil.bulkIndexDocument1(Analyzer.ESINDEX, "analyze", batchData); 45 | lastSendTime = System.currentTimeMillis(); 46 | batchData.clear(); 47 | } 48 | } else { 49 | batchData.add(dataToSend); 50 | } 51 | 52 | } 53 | 54 | } catch (Exception e) { 55 | LOG.error("Send to es failed !", e); 56 | } 57 | } 58 | 59 | AnalyzerWorker.cache.drainTo(batchData); 60 | if (!batchData.isEmpty()) { 61 | try { 62 | ElasticSearchUtil.bulkIndexDocument1(Analyzer.ESINDEX, "analyze", batchData); 63 | } catch (Exception e) { 64 | LOG.error("last send to es failed !", e); 65 | } 66 | } 67 | } 68 | 69 | public void exit() { 70 | this.exit = true; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /RCT-Analyze/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${SERVER_PORT:8082} 2 | rct.max.es.thread.num=10 3 | rct.filter.item.count=10 4 | rct.filter.bytes=512 5 | 6 | # 是否开启elasticsearch 保存监控数据 7 | rct.elasticsearch.enable=false 8 | rct.elasticsearch.rest.urls= 9 | rct.elasticsearch.index=rct-analyze 10 | # 前缀分隔符 11 | rct.analyze.key.prefix.separators=${KEY_PREFIX_SEPARATORS::,|,-,_} 12 | # first/last 13 | # 对应java字符串查找indexOf/lastIndexOf 14 | rct.analyze.key.prefix.index.location=${KEY_PREFIX_INDEX_LOCATION:last} 15 | 16 | eureka.client.serviceUrl.defaultZone=${REDIS_MANAGER_URL:http://localhost:8080/eureka/} 17 | eureka.instance.preferIpAddress=true 18 | #发送时间间隔 19 | eureka.instance.lease-renewal-interval-in-seconds=10 20 | #多长时间过期 21 | eureka.instance.lease-expiration-duration-in-seconds=30 22 | logging.level.com.netflix.eureka=OFF 23 | logging.level.com.netflix.discovery=OFF 24 | spring.application.name=analyze -------------------------------------------------------------------------------- /RCT-Analyze/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %d{ISO8601} %-5level [%thread] %logger{0}: %msg%n 8 | 9 | 10 | 11 | 12 | 14 | 15 | logs/info.%d{yyyy-MM-dd}.log 16 | 7 17 | 18 | 19 | 20 | %d{ISO8601} %-5level [%thread] %logger{0}: %msg%n 21 | 22 | 23 | 24 | 25 | 27 | 28 | logs/warn.%d{yyyy-MM-dd}.log 29 | 7 30 | 31 | 32 | 33 | %d{ISO8601} %-5level [%thread] %logger{0}: %msg%n 34 | 35 | 36 | 37 | WARN 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /RCT-Analyze/src/test/java/org/nesc/ecbd/RedisTest.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd; 2 | 3 | import redis.clients.jedis.Jedis; 4 | 5 | /** 6 | * @author:Truman.P.Du 7 | * @createDate: 2018年3月30日 上午7:49:48 8 | * @version:1.0 9 | * @description: 10 | */ 11 | 12 | public class RedisTest { 13 | 14 | public static void main(String[] args) throws Exception { 15 | Jedis jedis = new Jedis("192.168.0.1", 6379); 16 | Long m1 = Long.valueOf(getMemory(jedis)); 17 | insertData(jedis); 18 | Long m2 = Long.valueOf(getMemory(jedis)); 19 | System.out.println(m2 - m1); 20 | } 21 | 22 | public static void insertData(Jedis jedis) { 23 | for (int i = 10000; i < 100000; i++) { 24 | jedis.set( "aa"+i, "bb"+i); // key和value长度都是7字节,且不是整数 25 | } 26 | } 27 | 28 | public static String getMemory(Jedis jedis) { 29 | String memoryAllLine = jedis.info("memory"); 30 | String usedMemoryLine = memoryAllLine.split("\r\n")[1]; 31 | String memory = usedMemoryLine.substring(usedMemoryLine.indexOf(':') + 1); 32 | return memory; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /RCT-Analyze/src/test/java/org/nesc/ecbd/test/StringReplaceTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.nesc.ecbd.test; 5 | 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * @author Hulva Luva.H 11 | * @since 2018年4月12日 12 | * 13 | */ 14 | public class StringReplaceTest { 15 | 16 | private static final String patternString = "\\{\\w+\\}"; 17 | /** 18 | * @param args 19 | */ 20 | public static void main(String[] args) { 21 | // String a = "dsasd\\dsd\\sds\\"; 22 | // System.out.println(a); 23 | // 24 | // System.out.println(a.split("\\\\")[0]); 25 | // a.replaceAll("\\\\", "/"); 26 | // System.out.println(a); 27 | 28 | Pattern pattern = Pattern.compile(patternString); 29 | Matcher matcher = pattern.matcher("eda{dsadsadasdasd}dsd"); 30 | matcher.find(); 31 | System.out.println(matcher.group(0)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /RCT-Base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | RCT 7 | org.nesc.ecbd 8 | 2.1.2 9 | 10 | 11 | RCT-Base 12 | jar 13 | 14 | RCT-Base 15 | http://maven.apache.org 16 | 17 | 18 | UTF-8 19 | 20 | 21 | 22 | 23 | 24 | com.alibaba 25 | fastjson 26 | 1.2.31 27 | 28 | 29 | org.apache.httpcomponents 30 | httpclient 31 | 32 | 33 | org.apache.commons 34 | commons-lang3 35 | 36 | 37 | com.moilioncircle 38 | redis-replicator 39 | 2.6.2 40 | 41 | 42 | 43 | redis.clients 44 | jedis 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/entity/AnalyzeInstance.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.entity; 2 | 3 | /** 4 | * @author:Truman.P.Du 5 | * @createDate: 2018年4月7日 下午4:41:16 6 | * @version:1.0 7 | * @description: 8 | */ 9 | public class AnalyzeInstance { 10 | private String host; 11 | private int port; 12 | 13 | public AnalyzeInstance(String host, int port) { 14 | super(); 15 | this.host = host; 16 | this.port = port; 17 | } 18 | 19 | public String getHost() { 20 | return host; 21 | } 22 | 23 | public void setHost(String host) { 24 | this.host = host; 25 | } 26 | 27 | public int getPort() { 28 | return port; 29 | } 30 | 31 | public void setPort(int port) { 32 | this.port = port; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "AnalyzeInstance [host=" + host + ", port=" + port + "]"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/entity/AnalyzeStatus.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.entity; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author Hulva Luva.H 8 | * @since 2018年4月7日 9 | * 10 | */ 11 | public enum AnalyzeStatus { 12 | 13 | /** 14 | * 程序启动,等待接受主节点 init 指令 15 | */ 16 | NOTINIT, 17 | 18 | /** 19 | * 接收到主节点 init 指令,对接收到的配置内容进行检查中 20 | */ 21 | CHECKING, 22 | 23 | /** 24 | * 程序初始化完毕并已检查完从主节点接收到的配置信息,等待主节点 execute 指令 25 | */ 26 | READY, 27 | 28 | /** 29 | * 已接收 execute 指令,正在拷贝 rdb 文件 30 | */ 31 | COPY_RDB, 32 | 33 | /** 34 | * 接收到主节点 execute 指令,并处于对 RDB 的分析中 35 | */ 36 | RUNNING, 37 | 38 | /** 39 | * 分析任务完成 40 | */ 41 | DONE, 42 | 43 | /** 44 | * 最近一次的分析任务被手动终止 45 | */ 46 | CANCELED, 47 | 48 | ERROR, 49 | 50 | NOT_START, 51 | 52 | RESETED; 53 | 54 | private static final Map stringToEnum = new HashMap(); 55 | static { 56 | // Initialize map from constant name to enum constant 57 | for (AnalyzeStatus analyzeStatus : values()) { 58 | stringToEnum.put(analyzeStatus.toString(), analyzeStatus); 59 | } 60 | } 61 | 62 | public static AnalyzeStatus fromString(String symbol) { 63 | return stringToEnum.get(symbol); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/entity/AnalyzerConstant.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.entity; 2 | 3 | /** 4 | * @author:Truman.P.Du 5 | * @createDate: 2018年10月16日 上午10:30:32 6 | * @version:1.0 7 | * @description: 分析器对照关系 8 | */ 9 | public class AnalyzerConstant { 10 | 11 | public static int DEFAULT_ANALYZER = 0; 12 | public static int DATA_TYPE_ANALYZER = 1; 13 | public static int PREFIX_ANALYZER = 2; 14 | public static int TOP_KEY_ANALYZER = 3; 15 | public static int TTL_ANALYZER = 4; 16 | public static int EXPORT_KEY_BY_PREFIX_ANALYZER = 5; 17 | public static int EXPORT_KEY_BY_FILTER_ANALYZER = 6; 18 | } 19 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/entity/ScheduleDetail.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.entity; 2 | 3 | /** 4 | * @author:Truman.P.Du 5 | * @createDate: 2018年4月13日 上午8:19:47 6 | * @version:1.0 7 | * @description: 调度任务进度实体 8 | */ 9 | public class ScheduleDetail { 10 | private long scheduleID; 11 | /** 12 | * 分析redis节点ip:port 13 | */ 14 | private String instance; 15 | 16 | private AnalyzeStatus status; 17 | /** 18 | * 调度是否成功 19 | */ 20 | private boolean scheduleStatus; 21 | private int process = 0; 22 | 23 | public ScheduleDetail() { 24 | 25 | } 26 | 27 | public ScheduleDetail(long scheduleID, String instance, boolean scheduleStatus, AnalyzeStatus status) { 28 | this.scheduleID = scheduleID; 29 | this.instance = instance; 30 | this.process = 0; 31 | this.scheduleStatus = scheduleStatus; 32 | this.status = status; 33 | } 34 | 35 | public ScheduleDetail(long scheduleID, String instance, int process, boolean scheduleStatus, AnalyzeStatus status) { 36 | this.scheduleID = scheduleID; 37 | this.instance = instance; 38 | this.process = process; 39 | this.scheduleStatus = scheduleStatus; 40 | this.status = status; 41 | } 42 | 43 | public long getScheduleID() { 44 | return scheduleID; 45 | } 46 | 47 | public void setScheduleID(long scheduleID) { 48 | this.scheduleID = scheduleID; 49 | } 50 | 51 | public String getInstance() { 52 | return instance; 53 | } 54 | 55 | public void setInstance(String instance) { 56 | this.instance = instance; 57 | } 58 | 59 | public AnalyzeStatus getStatus() { 60 | return status; 61 | } 62 | 63 | public void setStatus(AnalyzeStatus status) { 64 | this.status = status; 65 | } 66 | 67 | public int getProcess() { 68 | return process; 69 | } 70 | 71 | public void setProcess(int process) { 72 | this.process = process; 73 | } 74 | 75 | public boolean isScheduleStatus() { 76 | return scheduleStatus; 77 | } 78 | 79 | public void setScheduleStatus(boolean scheduleStatus) { 80 | this.scheduleStatus = scheduleStatus; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/entity/ScheduleInfo.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.entity; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | /** 8 | * @author:Truman.P.Du 9 | * @createDate: 2018年4月11日 下午1:59:17 10 | * @version:1.0 11 | * @description: 12 | */ 13 | public class ScheduleInfo { 14 | private long scheduleID; 15 | private String dataPath; 16 | private String prefixes; 17 | private int[] analyzerTypes; 18 | 19 | private Set ports = new HashSet<>(); 20 | 21 | public ScheduleInfo() { 22 | 23 | } 24 | 25 | public ScheduleInfo(long scheduleID, String dataPath, String prefixes, Set ports,int[] analyzerTypes) { 26 | this.scheduleID = scheduleID; 27 | this.dataPath = dataPath; 28 | this.prefixes = prefixes; 29 | this.ports = ports; 30 | this.analyzerTypes = analyzerTypes; 31 | } 32 | 33 | public long getScheduleID() { 34 | return scheduleID; 35 | } 36 | 37 | public void setScheduleID(long scheduleID) { 38 | this.scheduleID = scheduleID; 39 | } 40 | 41 | public String getDataPath() { 42 | return dataPath; 43 | } 44 | 45 | public void setDataPath(String dataPath) { 46 | this.dataPath = dataPath; 47 | } 48 | 49 | public Set getPorts() { 50 | return ports; 51 | } 52 | 53 | public void setPorts(Set ports) { 54 | this.ports = ports; 55 | } 56 | 57 | public String getPrefixes() { 58 | return prefixes; 59 | } 60 | 61 | public void setPrefixes(String prefixes) { 62 | this.prefixes = prefixes; 63 | } 64 | 65 | public int[] getAnalyzerTypes() { 66 | return analyzerTypes; 67 | } 68 | 69 | public void setAnalyzerTypes(int[] analyzerTypes) { 70 | this.analyzerTypes = analyzerTypes; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "ScheduleInfo [scheduleID=" + scheduleID + ", dataPath=" + dataPath + ", prefixes=" + prefixes 76 | + ", analyzerTypes=" + Arrays.toString(analyzerTypes) + ", ports=" + ports + "]"; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/utils/BytesUtil.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.utils; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | 9 | /** 10 | * @author:Truman.P.Du 11 | * @createDate: 2018年4月20日 下午3:01:06 12 | * @version:1.0 13 | * @description: 14 | */ 15 | public class BytesUtil { 16 | /** 17 | * 对象转数组 18 | */ 19 | public static byte[] toByteArray(Object obj) throws IOException { 20 | if (obj == null) { 21 | return null; 22 | } 23 | byte[] bytes = null; 24 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 25 | ObjectOutputStream oos = new ObjectOutputStream(bos); 26 | oos.writeObject(obj); 27 | oos.flush(); 28 | bytes = bos.toByteArray(); 29 | oos.close(); 30 | bos.close(); 31 | return bytes; 32 | } 33 | 34 | /** 35 | * 数组转对象 36 | */ 37 | public static Object toObject(byte[] bytes) throws IOException, ClassNotFoundException { 38 | if (bytes == null || bytes.length == 0) { 39 | return null; 40 | } 41 | Object obj = null; 42 | ByteArrayInputStream bis = new ByteArrayInputStream(bytes); 43 | ObjectInputStream ois = new ObjectInputStream(bis); 44 | obj = ois.readObject(); 45 | ois.close(); 46 | bis.close(); 47 | return obj; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/utils/FileCopyUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.nesc.ecbd.utils; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStream; 12 | 13 | /** 14 | * @author Hulva Luva.H 15 | * @since 2018年4月11日 16 | * 17 | */ 18 | public class FileCopyUtil { 19 | 20 | /** 21 | * 文件拷贝 22 | * 23 | * @param source 源文件 24 | * @param dest 25 | * @throws IOException 26 | */ 27 | public static void copyFileUsingStream(File source, File dest) throws IOException { 28 | InputStream is = null; 29 | try (OutputStream os = new FileOutputStream(dest);) { 30 | is = new FileInputStream(source); 31 | byte[] buffer = new byte[1024]; 32 | int length; 33 | while ((length = is.read(buffer)) > 0) { 34 | os.write(buffer, 0, length); 35 | } 36 | } finally { 37 | if (is != null) { 38 | is.close(); 39 | } 40 | } 41 | } 42 | 43 | public static void main(String[] args) { 44 | try { 45 | // check destination folder exist 46 | File des = new File("rdbtemp/dump.rdb"); 47 | System.out.println(des.getAbsolutePath()); 48 | FileCopyUtil.copyFileUsingStream(new File("D:\\temp\\redis\\prd\\dump.rdb"), des); 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /RCT-Base/src/main/java/org/nesc/ecbd/utils/RedisInfoTool.java: -------------------------------------------------------------------------------- 1 | package org.nesc.ecbd.utils; 2 | 3 | import java.util.Collections; 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.Map.Entry; 12 | 13 | /** 14 | * @author:Truman.P.Du 15 | * @createDate: 2018年10月11日 下午1:28:10 16 | * @version:1.0 17 | * @description:redis 相关信息计算工具 18 | */ 19 | public class RedisInfoTool { 20 | 21 | /** 22 | * 根据获取执行分析任务ports规则 23 | * 即获取其中一个slave,尽量保持均衡在不同机器上 24 | * 25 | * @param clusterNodesMap 26 | * @return 27 | */ 28 | public static Map> generateAnalyzeRule(Map> clusterNodesMap) { 29 | 30 | // 通过该map存储不同IP分配的数量,按照规则,优先分配数量最小的IP 31 | Map staticsResult = new HashMap<>(); 32 | Map> generateRule = new HashMap<>(); 33 | 34 | // 此处排序是为了将slave数量最小的优先分配 35 | List>> sortList = new LinkedList<>(clusterNodesMap.entrySet()); 36 | Collections.sort(sortList, new Comparator>>() { 37 | @Override 38 | public int compare(Entry> o1, Entry> o2) { 39 | return o1.getValue().size() - o2.getValue().size(); 40 | } 41 | }); 42 | 43 | for (Entry> entry : sortList) { 44 | List slaves = entry.getValue(); 45 | boolean isSelected = false; 46 | String tempPort = null; 47 | String tempIP = null; 48 | int num = 0; 49 | for (String slave : slaves) { 50 | String ip = slave.split(":")[0]; 51 | String port = slave.split(":")[1]; 52 | // 统计组里面不存在的IP优先分配 53 | if (!staticsResult.containsKey(ip)) { 54 | staticsResult.put(ip, 1); 55 | Set generatePorts = generateRule.get(ip); 56 | if (generatePorts == null) { 57 | generatePorts = new HashSet<>(); 58 | } 59 | generatePorts.add(port); 60 | generateRule.put(ip, generatePorts); 61 | isSelected = true; 62 | break; 63 | } else { 64 | // 此处是为了求出被使用最少的IP 65 | Integer staticsNum = staticsResult.get(ip); 66 | if (num == 0) { 67 | num = staticsNum; 68 | tempPort = port; 69 | tempIP = ip; 70 | continue; 71 | } 72 | if (staticsNum < num) { 73 | tempPort = port; 74 | tempIP = ip; 75 | num = staticsNum; 76 | } 77 | } 78 | 79 | } 80 | 81 | // 如果上面未分配,则选择staticsResult中数值最小的那个slave 82 | if (!isSelected) { 83 | if (slaves != null && slaves.size() > 0) { 84 | if (tempPort != null) { 85 | Set generatePorts = generateRule.get(tempIP); 86 | if (generatePorts == null) { 87 | generatePorts = new HashSet<>(); 88 | } 89 | generatePorts.add(tempPort); 90 | generateRule.put(tempIP, generatePorts); 91 | staticsResult.put(tempIP, staticsResult.get(tempIP) + 1); 92 | } 93 | } 94 | } 95 | } 96 | return generateRule; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /RCT-Dashboard/.gitignore: -------------------------------------------------------------------------------- 1 | /db/ 2 | -------------------------------------------------------------------------------- /RCT-Dashboard/db/data.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/db/data.db -------------------------------------------------------------------------------- /RCT-Dashboard/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ##################### 2 | #version: RCT 1.0 3 | #describe rct-dashboard 4 | #truman create 5 | # 6 | FROM openjdk:8u181-jdk-alpine3.8 7 | LABEL author="Truman.p.Du" 8 | COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas 9 | 10 | ENV BASE_DIR /opt/app/rct/rct-dashboard 11 | WORKDIR ${BASE_DIR} 12 | ENV RCT_NAME RCT-Dashboard 13 | ENV VERSION 2.1.1 14 | RUN apk upgrade --update && \ 15 | apk add --update curl bash 16 | RUN cd ${BASE_DIR} && \ 17 | curl -fsSL -o ${RCT_NAME}-${VERSION}-release.tar.gz https://github.com/xaecbd/RCT/releases/download/v${VERSION}/${RCT_NAME}-${VERSION}-release.tar.gz && \ 18 | tar xvf ${RCT_NAME}-${VERSION}-release.tar.gz && \ 19 | rm -rf ${RCT_NAME}-${VERSION}-release.tar.gz 20 | ADD start.sh ${BASE_DIR} 21 | CMD ["sh","start.sh"] 22 | -------------------------------------------------------------------------------- /RCT-Dashboard/docker/start.sh: -------------------------------------------------------------------------------- 1 | appName=`ls |grep jar|grep RCT` 2 | echo start to run $appName 3 | 4 | if [ -n "$JAVA_OPTIONS" ];then 5 | java $JAVA_OPTIONS -jar $appName $option 6 | else 7 | java -jar $appName $option 8 | fi -------------------------------------------------------------------------------- /RCT-Dashboard/package.xml: -------------------------------------------------------------------------------- 1 | 2 | release 3 | 4 | tar.gz 5 | 6 | false 7 | 8 | 9 | 10 | ${basedir}/db 11 | db 12 | 13 | *.db 14 | 15 | 16 | 17 | 18 | ${project.basedir}/src/main/resources 19 | config 20 | 21 | application.properties 22 | 23 | 24 | 25 | 26 | 27 | ${project.build.directory} 28 | 29 | 30 | *.jar 31 | 32 | 33 | 34 | 35 | ${basedir}/template 36 | template 37 | 38 | *.xlsx 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | # 忽略目录 2 | build/ 3 | tests/ 4 | demo/ 5 | 6 | # node 覆盖率文件 7 | coverage/ 8 | 9 | # 忽略文件 10 | **/*-min.js 11 | **/*.min.js 12 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "extends": "eslint-config-airbnb", 5 | "parserOptions": { 6 | "ecmaVersion": 6, 7 | "ecmaFeatures": { 8 | "jsx": true, 9 | "experimentalObjectRestSpread": true 10 | } 11 | }, 12 | "env": { 13 | "browser": true, 14 | "mocha": true 15 | }, 16 | "plugins": ["react", "babel"], 17 | "rules": { 18 | "react/prefer-stateless-function": 0, 19 | "no-console": 0, 20 | "no-use-before-define": 0, 21 | "jsx-a11y/label-has-for": 0, 22 | "jsx-a11y/no-static-element-interactions": 0, 23 | "jsx-a11y/anchor-has-content": 0, 24 | "jsx-a11y/click-events-have-key-events": 0, 25 | "jsx-a11y/anchor-is-valid": 0, 26 | "react/no-array-index-key": 0, 27 | "func-names": 0, 28 | "arrow-body-style": 0, 29 | "react/sort-comp": 0, 30 | "react/prop-types": 0, 31 | "react/jsx-first-prop-new-line": 0, 32 | "react/jsx-filename-extension": [ 33 | 1, 34 | { 35 | "extensions": [".js", ".jsx"] 36 | } 37 | ], 38 | "import/extensions": 0, 39 | "import/no-unresolved": 0, 40 | "import/no-extraneous-dependencies": 0, 41 | "prefer-destructuring": 0, 42 | "no-param-reassign": 0, 43 | "no-return-assign": 0, 44 | "max-len": 0, 45 | "consistent-return": 0, 46 | "no-redeclare": 0, 47 | "react/require-extension": 0, 48 | "react/no-danger": 0, 49 | "comma-dangle": ["error", "always-multiline"], 50 | "function-paren-newline": 0, 51 | "object-curly-newline": 0, 52 | "no-restricted-globals": 0, 53 | "global-require": 0, 54 | "no-mixed-operators": 0, 55 | "react/forbid-prop-types": 0 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # production 7 | /build 8 | /dist 9 | 10 | # misc 11 | .idea/ 12 | .happypack 13 | .DS_Store 14 | 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/.webpackrc.js: -------------------------------------------------------------------------------- 1 | const { 2 | resolve 3 | } = require('path'); 4 | 5 | module.exports = { 6 | output: { 7 | path: resolve('../resources/static'), 8 | }, 9 | module: { 10 | rules: [ { 11 | test: /\.(png|jpg|gif)$/, 12 | use: [ 13 | { 14 | loader: 'file-loader', 15 | options: { 16 | name: '[name].[ext]', 17 | outputPath: 'public/images/' 18 | } 19 | } 20 | ] 21 | }] 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/README.md: -------------------------------------------------------------------------------- 1 | # RCT 2 | 3 | RCT (Redis 内存分析工具) 4 | 5 | ## 使用 6 | 7 | - 启动调试服务: `npm start` 8 | - 构建: `npm run build` 9 | 10 | ## 目录结构 11 | 12 | - react-router @4.x 默认采用 hashHistory 的单页应用 13 | - 入口文件: `src/index.js` 14 | - 导航配置: `src/menuConfig.js` 15 | - 路由配置: `src/routerConfig.js` 16 | - 路由入口: `src/router.jsx` 17 | - 布局文件: `src/layouts` 18 | - 通用组件: `src/components` 19 | - 页面文件: `src/pages` 20 | 21 | ## 配色 22 | 23 | - 主色:#5e83fb 24 | - 功能主色:#5e83fb、#f7da47、#58ca9a、#ee706d 25 | - 字体颜色:#333、#666 26 | 27 | ## 效果图 28 | 29 | ![screenshot](https://img.alicdn.com/tfs/TB16CTXx7voK1RjSZFNXXcxMVXa-2872-1580.png) 30 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@icedesign/builder-platfrom-scaffold", 3 | "version": "1.0.0", 4 | "description": "RCT (Redis 内存分析工具)", 5 | "files": [ 6 | "src/", 7 | "build/", 8 | "public/", 9 | "tests/", 10 | "_gitignore", 11 | ".editorconfig", 12 | ".eslintignore", 13 | ".eslintrc" 14 | ], 15 | "keywords": [ 16 | "ice-scaffold" 17 | ], 18 | "scripts": { 19 | "start": "ice dev", 20 | "build": "ice build", 21 | "lint": "eslint . --ext '.js,.jsx' --fix" 22 | }, 23 | "publishConfig": { 24 | "registry": "http://registry.npmjs.com", 25 | "access": "public" 26 | }, 27 | "dependencies": { 28 | "@icedesign/base": "^0.2.0", 29 | "@icedesign/container": "^0.1.4", 30 | "@icedesign/dynamic-icon": "^0.1.6", 31 | "@icedesign/form-binder": "^0.1.4", 32 | "@icedesign/img": "^0.1.1", 33 | "@icedesign/layout": "^0.1.2", 34 | "@icedesign/menu": "^0.1.1", 35 | "@icedesign/skin": "^0.1.0", 36 | "axios": "^0.18.0" 37 | }, 38 | "devDependencies": { 39 | "babel-eslint": "^8.0.3", 40 | "classnames": "^2.2.5", 41 | "eslint": "^4.13.1", 42 | "eslint-config-airbnb": "^16.1.0", 43 | "eslint-plugin-babel": "^4.1.1", 44 | "eslint-plugin-import": "^2.8.0", 45 | "eslint-plugin-jsx-a11y": "^6.0.3", 46 | "eslint-plugin-react": "^7.5.1", 47 | "file-loader": "^3.0.1", 48 | "foundation-symbol": "^0.1.3", 49 | "ice-scripts": "^1.8.7", 50 | "prop-types": "^15.5.8", 51 | "react": "^16.4.1", 52 | "react-dom": "^16.4.1", 53 | "react-highcharts": "^16.0.2", 54 | "react-router-dom": "^4.3.1" 55 | }, 56 | "buildConfig": { 57 | "theme": "@icedesign/skin", 58 | "entry": "src/index.js", 59 | "localization": false, 60 | "output": { 61 | "publicPath": "./" 62 | } 63 | }, 64 | "themeConfig": { 65 | "primaryColor": "#5e83fb" 66 | }, 67 | "scaffoldConfig": { 68 | "builder": "ice-scripts", 69 | "name": "ice-builder-platform", 70 | "title": "云构建平台", 71 | "categories": [ 72 | "行业领域" 73 | ], 74 | "screenshot": "https://img.alicdn.com/tfs/TB16CTXx7voK1RjSZFNXXcxMVXa-2872-1580.png" 75 | }, 76 | "title": "frontend" 77 | } 78 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/favicon.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/images/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/images/chart.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/images/client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/images/client.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/images/fail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/images/fail.jpg -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/images/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/images/list.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/images/statistical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/images/statistical.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/images/success.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/images/success.jpg -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/images/time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/public/images/time.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | RCT 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | // 使用由库提供的配置的默认值来创建实例 此时超时配置的默认值是 `0` 3 | 4 | const axiosInstance = axios.create(); 5 | // 覆写库的超时默认值 现在,在超时前,所有请求都会等待 2分钟 6 | axiosInstance.defaults.timeout = 120000; 7 | 8 | //axiosInstance.defaults.baseURL = 'http://localhost:8080'; 9 | 10 | export default axiosInstance; 11 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/CustomBreadcrumb/CustomBreadcrumb.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Breadcrumb } from '@icedesign/base'; 4 | import IceContainer from '@icedesign/container'; 5 | 6 | export default class CustomBreadcrumb extends Component { 7 | static displayName = 'CustomBreadcrumb'; 8 | 9 | static defaultProps = { 10 | dataSource: [], 11 | }; 12 | 13 | static propTypes = { 14 | dataSource: PropTypes.array, 15 | }; 16 | 17 | render() { 18 | const { dataSource } = this.props; 19 | return ( 20 | 21 | 22 | {dataSource.map((item, index) => { 23 | return ( 24 | 25 | {item.text} 26 | 27 | ); 28 | })} 29 | 30 | 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/CustomBreadcrumb/index.js: -------------------------------------------------------------------------------- 1 | import CustomBreadcrumb from './CustomBreadcrumb'; 2 | 3 | export default CustomBreadcrumb; 4 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/CustomTable/CustomTable.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Table, Pagination } from '@icedesign/base'; 3 | import PropTypes from 'prop-types'; 4 | import './CustomTable.scss'; 5 | 6 | export default class Home extends Component { 7 | static displayName = 'Home'; 8 | 9 | static defaultProps = { 10 | isLoading: false, 11 | columns: [], 12 | dataSource: [], 13 | }; 14 | 15 | static propTypes = { 16 | isLoading: PropTypes.bool, 17 | columns: PropTypes.array, 18 | dataSource: PropTypes.array, 19 | }; 20 | 21 | constructor(props) { 22 | super(props); 23 | this.state = { 24 | current: 1, 25 | }; 26 | } 27 | 28 | handlePagination = (current) => { 29 | this.setState( 30 | { 31 | current, 32 | }, 33 | () => { 34 | this.props.onChange(); 35 | } 36 | ); 37 | }; 38 | 39 | render() { 40 | const { isLoading, dataSource, columns } = this.props; 41 | 42 | return ( 43 |
44 | 50 | {columns.map((item) => { 51 | return ( 52 | value)} 58 | /> 59 | ); 60 | })} 61 |
62 | 67 |
68 | ); 69 | } 70 | } 71 | 72 | const styles = { 73 | pagination: { 74 | margin: '20px 0', 75 | textAlign: 'center', 76 | }, 77 | }; 78 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/CustomTable/CustomTable.scss: -------------------------------------------------------------------------------- 1 | .custom-table { 2 | .next-table-header { 3 | table th { 4 | background: #f4f4f4; 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/CustomTable/index.js: -------------------------------------------------------------------------------- 1 | import CustomTable from './CustomTable'; 2 | 3 | export default CustomTable; 4 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/NotFound/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import IceContainer from '@icedesign/container'; 4 | import './NotFound.scss'; 5 | 6 | export default class NotFound extends Component { 7 | static displayName = 'NotFound'; 8 | 9 | render() { 10 | return ( 11 |
12 | 13 |
14 | 页面不存在 20 |
21 |

22 | 抱歉,你访问的页面不存在 23 |

24 |

25 | 您要找的页面没有找到,请返回首页继续浏览 26 |

27 |
28 |
29 |
30 |
31 | ); 32 | } 33 | } 34 | 35 | const styles = { 36 | exceptionContent: { 37 | display: 'flex', 38 | justifyContent: 'center', 39 | alignItems: 'center', 40 | }, 41 | title: { 42 | color: '#333', 43 | }, 44 | description: { 45 | color: '#666', 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/NotFound/NotFound.scss: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 720px) { 2 | .exception-content { 3 | min-height: 200px; 4 | .imgException { 5 | max-width: 100px; 6 | margin-right: 10px; 7 | } 8 | .title { 9 | font-size: 14px; 10 | margin: 10px 0; 11 | } 12 | .description { 13 | font-size: 12px; 14 | } 15 | } 16 | } 17 | 18 | @media screen and (min-width: 721px) and (max-width: 1199px) { 19 | .exception-content { 20 | min-height: 300px; 21 | .imgException { 22 | max-width: 180px; 23 | margin-right: 30px; 24 | } 25 | .title { 26 | font-size: 20px; 27 | margin: 10px 0; 28 | } 29 | .description { 30 | font-size: 14px; 31 | } 32 | } 33 | } 34 | 35 | @media screen and (min-width: 1200px) { 36 | .exception-content { 37 | min-height: 500px; 38 | .imgException { 39 | max-width: 260px; 40 | margin-right: 50px; 41 | } 42 | .title { 43 | font-size: 24px; 44 | margin: 20px 0; 45 | } 46 | .description { 47 | font-size: 16px; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/NotFound/images/notFound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/src/components/NotFound/images/notFound.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/components/NotFound/index.js: -------------------------------------------------------------------------------- 1 | import NotFound from './NotFound'; 2 | 3 | export default NotFound; 4 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | 3 | // 载入默认全局样式 normalize 、.clearfix 和一些 mixin 方法等 4 | import '@icedesign/base/reset.scss'; 5 | import '../public/images/chart.png'; 6 | import '../public/images/client.png'; 7 | import '../public/images/fail.jpg'; 8 | import '../public/images/statistical.png'; 9 | import '../public/images/success.jpg'; 10 | import '../public/images/time.png'; 11 | import '../public/images/list.png'; 12 | 13 | import router from './router'; 14 | 15 | const ICE_CONTAINER = document.getElementById('ice-container'); 16 | 17 | if (!ICE_CONTAINER) { 18 | throw new Error('当前页面不存在
节点.'); 19 | } 20 | 21 | ReactDOM.render(router, ICE_CONTAINER); 22 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/MainRoutes.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Switch, Route, Redirect } from 'react-router-dom'; 3 | import NotFound from '../../components/NotFound'; 4 | import routerData from '../../routerConfig'; 5 | 6 | class MainRoutes extends Component { 7 | /** 8 | * 渲染路由组件 9 | */ 10 | renderNormalRoute = (item, index) => { 11 | if (sessionStorage.getItem('user')) { 12 | return item.component ? ( 13 | 19 | ) : null; 20 | } 21 | return ; 22 | }; 23 | 24 | render() { 25 | return ( 26 | 27 | {/* 渲染路由表 */} 28 | {routerData.map(this.renderNormalRoute)} 29 | 30 | {/* 未匹配到的路由重定向到 NotFound */} 31 | 32 | 33 | ); 34 | } 35 | } 36 | 37 | export default MainRoutes; 38 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Footer/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Logo from '../Logo'; 3 | 4 | export default () => { 5 | return ( 6 |
22 |
23 | 24 |
25 |
33 | EC-BigData Team 34 |
35 | © 2019 版权所有 36 |
37 |
38 | ); 39 | }; 40 | 41 | 42 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | export default from './Footer'; 2 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Header/Header.scss: -------------------------------------------------------------------------------- 1 | .header-container { 2 | position: fixed; 3 | left: 0; 4 | right: 0; 5 | z-index: 999; 6 | background: #fff; 7 | min-width: 1280px; 8 | box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); 9 | .header-content { 10 | width: 100%; 11 | height: 62px; 12 | padding: 0 20px; 13 | display: flex; 14 | align-items: center; 15 | justify-content: space-between; 16 | } 17 | 18 | .header-navbar { 19 | display: flex; 20 | flex-direction: row; 21 | .ice-menu { 22 | background: transparent; 23 | .ice-menu-submenu-title, 24 | .ice-menu-item { 25 | font-size: 14px; 26 | a:hover { 27 | color: #5e83fb; 28 | } 29 | } 30 | } 31 | } 32 | 33 | .user-avatar { 34 | margin-right: 12px; 35 | border-radius: 4px; 36 | } 37 | 38 | .user-department { 39 | font-size: 12px; 40 | color: #333; 41 | } 42 | 43 | .ice-design-header-userpannel { 44 | margin-left: 20px; 45 | cursor: pointer; 46 | .user-profile { 47 | display: inline-block; 48 | text-align: center; 49 | margin-bottom: 2px; 50 | color: #333; 51 | } 52 | .icon-down { 53 | margin-left: 2px; 54 | color: #333; 55 | } 56 | } 57 | } 58 | 59 | .user-profile-menu { 60 | width: 130px; 61 | border-radius: 6px; 62 | padding: 0 16px; 63 | .user-profile-menu-item { 64 | height: 40px; 65 | line-height: 40px; 66 | font-size: 12px; 67 | color: #666; 68 | cursor: pointer; 69 | a:hover { 70 | color: #2077ff; 71 | } 72 | i { 73 | margin-right: 5px; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Header/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Header/images/avatar.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Logo/Logo.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export default class Logo extends Component { 4 | render() { 5 | return ( 6 |
7 | 8 | RCT 9 | 10 |
11 | ); 12 | } 13 | } 14 | 15 | const styles = { 16 | container: { 17 | display: 'flex', 18 | alignItems: 'center', 19 | marginRight: '20px', 20 | }, 21 | logoText: { 22 | display: 'block', 23 | maxWidth: '120px', 24 | overflow: 'hidden', 25 | textOverflow: 'ellipsis', 26 | whiteSpace: 'nowrap', 27 | marginLeft: '10px', 28 | fontSize: '22px', 29 | color: '#333', 30 | fontWeight: 'bold', 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Logo/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Logo/images/logo.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/components/Logo/index.js: -------------------------------------------------------------------------------- 1 | export default from './Logo'; 2 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Layout from '@icedesign/layout'; 3 | import Header from './components/Header'; 4 | import Footer from './components/Footer'; 5 | import MainRoutes from './MainRoutes'; 6 | import './index.scss'; 7 | 8 | export default class BasicLayout extends Component { 9 | render() { 10 | return ( 11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | const styles = { 25 | mainContent: { 26 | marginTop: '82px', 27 | padding: '0 20px', 28 | paddingBottom: '50px', 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/BasicLayout/index.scss: -------------------------------------------------------------------------------- 1 | // Global RESET 2 | .ice-layout { 3 | &.basic-layout { 4 | background: #f7f7f7; 5 | min-width: 1280px; 6 | min-height: 100vh; 7 | .next-btn { 8 | border-radius: 4px; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/MainRoutes.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Switch, Route, Redirect } from 'react-router-dom'; 3 | import NotFound from '../../components/NotFound'; 4 | import routerData from '../../routerConfig'; 5 | 6 | class MainRoutes extends Component { 7 | /** 8 | * 渲染路由组件 9 | */ 10 | renderNormalRoute = (item, index) => { 11 | if (sessionStorage.getItem('user')) { 12 | return item.component ? ( 13 | 19 | ) : null; 20 | } 21 | return ; 22 | }; 23 | 24 | render() { 25 | return ( 26 | 27 | {/* 渲染路由表 */} 28 | {routerData.map(this.renderNormalRoute)} 29 | 30 | {/* 根路由默认重定向到 /dashboard */} 31 | 32 | 33 | {/* 未匹配到的路由重定向到 NotFound */} 34 | 35 | 36 | ); 37 | } 38 | } 39 | 40 | export default MainRoutes; 41 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Footer/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Logo from '../Logo'; 3 | 4 | export default () => { 5 | return ( 6 |
20 |
21 | 22 |
23 |
31 | EC-BigData Team 32 |
33 | © 2019 版权所有 34 |
35 |
36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | export default from './Footer'; 2 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link, withRouter } from 'react-router-dom'; 3 | import { Balloon, Icon } from '@icedesign/base'; 4 | import FoundationSymbol from 'foundation-symbol'; 5 | import IceImg from '@icedesign/img'; 6 | import Logo from '../Logo'; 7 | import './Header.scss'; 8 | 9 | const data = () => { 10 | if (JSON.parse(sessionStorage.getItem('user'))) { 11 | return JSON.parse(sessionStorage.getItem('user')).userName; 12 | } 13 | }; 14 | 15 | @withRouter 16 | export default class Header extends Component { 17 | static propTypes = {}; 18 | 19 | static defaultProps = {}; 20 | 21 | constructor(props) { 22 | super(props); 23 | this.state = {}; 24 | } 25 | backup = () =>{ 26 | sessionStorage.clear(); 27 | } 28 | 29 | render() { 30 | const { location = {} } = this.props; 31 | const { pathname } = location; 32 | return ( 33 |
34 |
35 |
36 | 37 |
38 | 39 | 49 | 55 |
56 | 57 | {data()} 58 | 59 |
60 | 65 |
66 | } 67 | closable={false} 68 | className="user-profile-menu" 69 | > 70 |
    71 |
  • {this.backup(e)}}> 72 | 73 | 74 | 退出 75 | 76 |
  • 77 |
78 | 79 |
80 | 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Header/Header.scss: -------------------------------------------------------------------------------- 1 | .header-container { 2 | position: fixed; 3 | left: 0; 4 | right: 0; 5 | z-index: 999; 6 | background: #fff; 7 | min-width: 1280px; 8 | box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); 9 | .header-content { 10 | width: 100%; 11 | height: 62px; 12 | padding: 0 20px; 13 | display: flex; 14 | align-items: center; 15 | justify-content: space-between; 16 | } 17 | 18 | .header-navbar { 19 | display: flex; 20 | flex-direction: row; 21 | .ice-menu { 22 | background: transparent; 23 | .ice-menu-submenu-title, 24 | .ice-menu-item { 25 | font-size: 14px; 26 | a:hover { 27 | color: #5e83fb; 28 | } 29 | } 30 | } 31 | } 32 | 33 | .user-avatar { 34 | margin-right: 12px; 35 | border-radius: 4px; 36 | } 37 | 38 | .user-department { 39 | font-size: 12px; 40 | color: #333; 41 | } 42 | 43 | .ice-design-header-userpannel { 44 | margin-left: 20px; 45 | cursor: pointer; 46 | .user-profile { 47 | display: inline-block; 48 | text-align: center; 49 | margin-bottom: 2px; 50 | color: #333; 51 | } 52 | .icon-down { 53 | margin-left: 2px; 54 | color: #333; 55 | } 56 | } 57 | } 58 | 59 | .user-profile-menu { 60 | width: 130px; 61 | border-radius: 6px; 62 | padding: 0 16px; 63 | .user-profile-menu-item { 64 | height: 40px; 65 | line-height: 40px; 66 | font-size: 12px; 67 | color: #666; 68 | cursor: pointer; 69 | a:hover { 70 | color: #2077ff; 71 | } 72 | i { 73 | margin-right: 5px; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Header/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Header/images/avatar.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Logo/Logo.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export default class Logo extends Component { 4 | render() { 5 | return ( 6 |
7 | 8 | RCT 9 | 10 |
11 | ); 12 | } 13 | } 14 | 15 | const styles = { 16 | container: { 17 | display: 'flex', 18 | alignItems: 'center', 19 | marginRight: '20px', 20 | }, 21 | logoText: { 22 | display: 'block', 23 | maxWidth: '120px', 24 | overflow: 'hidden', 25 | textOverflow: 'ellipsis', 26 | whiteSpace: 'nowrap', 27 | marginLeft: '10px', 28 | fontSize: '22px', 29 | color: '#333', 30 | fontWeight: 'bold', 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Logo/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xaecbd/RCT/19e97c4c2c33e0af50712fedd8c1c933e7b8044d/RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Logo/images/logo.png -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/components/Logo/index.js: -------------------------------------------------------------------------------- 1 | export default from './Logo'; 2 | -------------------------------------------------------------------------------- /RCT-Dashboard/src/main/frontend/src/layouts/DashLayout/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Layout from '@icedesign/layout'; 3 | import Header from './components/Header'; 4 | import Footer from './components/Footer'; 5 | import MainRoutes from './MainRoutes'; 6 | import './index.scss'; 7 | 8 | export default class BasicLayout extends Component { 9 | render() { 10 | return ( 11 | 12 |
13 |
14 | 15 |
16 |