├── .gitignore
├── README.md
├── pom.xml
├── screenshots
├── all_secrets.png
├── config_secrets.png
├── hive_query.png
├── login.png
└── user_config.png
└── src
├── main
├── assembly
│ ├── bin
│ │ ├── restart.sh
│ │ ├── startup.sh
│ │ └── stop.sh
│ └── conf
│ │ └── application.properties
├── java
│ └── com
│ │ └── prophet
│ │ ├── Application.java
│ │ ├── common
│ │ ├── Encryptor.java
│ │ ├── HQLParser.java
│ │ ├── QueryHistoryStatusEnum.java
│ │ └── ThreadPool.java
│ │ ├── config
│ │ ├── HiveDataSourceConfig.java
│ │ ├── HiveResultTextConfig.java
│ │ └── MySQLDataSourceConfig.java
│ │ ├── dao
│ │ ├── AdminDao.java
│ │ ├── EmailUtil.java
│ │ ├── HiveMetaStoreDao.java
│ │ ├── HiveSecretTableDao.java
│ │ ├── HiveSecretUserPrivsDao.java
│ │ ├── HiveServerDao.java
│ │ ├── QueryHistoryDao.java
│ │ ├── UserAuthLdapDao.java
│ │ ├── UserAuthProphetDao.java
│ │ └── task
│ │ │ ├── HiveQueryAsyncTask.java
│ │ │ └── HiveResultSendmailRunnableTask.java
│ │ ├── domain
│ │ ├── HiveSecretTable.java
│ │ └── QueryHistory.java
│ │ ├── filter
│ │ └── LoginFilter.java
│ │ ├── interfaces
│ │ └── UserAuthDaoInterface.java
│ │ ├── service
│ │ ├── BaseService.java
│ │ ├── HiveMetaStoreService.java
│ │ ├── HiveSecretDataService.java
│ │ ├── HiveServerService.java
│ │ ├── QueryHistoryService.java
│ │ └── UserAuthService.java
│ │ ├── util
│ │ └── DateTimeUtil.java
│ │ └── web
│ │ ├── BaseController.java
│ │ ├── HiveQueryController.java
│ │ ├── HiveSecretDataController.java
│ │ ├── UserController.java
│ │ └── postparameters
│ │ └── HiveQueryCommand.java
├── resources
│ ├── application.properties
│ ├── application.properties.github
│ ├── assembly.xml
│ └── logging.xml
└── webapp
│ └── WEB-INF
│ └── web.xml
└── test
└── java
├── junit
└── EmailTest01.java
└── prophet
├── HQLParserTest01.java
└── HiveTest01.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | .classpath
3 | .project
4 | .settings/
5 | target/
6 | .DS_Store
7 | **/cobertura
8 | logs/
9 | tmp/
10 | data/
11 | **/.springBeans
12 | .idea
13 | *.iml
14 | *.jar
15 | /.swp
16 | .git
17 | src/main/resources/application.properties.*
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Prophet是什么?
2 | * 一个优秀的大数据查询平台,提供hive异步任务查询、LDAP用户、表级查询权限控制、历史查询任务与结果存储、邮件通知、excel下载等功能。
3 | * 具有查询性能快、查询方便的特点
4 |
5 | ## 开发环境
6 | * java 8
7 | * springboot
8 | * VUE + iview
9 |
10 | ## 准备工作
11 | * 搭建hadoop集群、hive集群(强烈推荐hive-server2-2.x版本)、metastore
12 | * 搭建prophet会用到的mysql,推荐mysql 5.6及以上版本
13 |
14 | ## 安装步骤
15 | * 1.安装jdk,强烈推荐使用jdk 1.8
16 | * 安装jdk
17 | * 修改PATH
18 | * 2.下载文件(非编译安装)
19 | * 二进制文件下载地址:https://pan.baidu.com/s/1eSgx4fo
20 | * 或者下载ZIP包并解压,解压后会看到prophet_server、prophet_fe、prophet_sql三个目录
21 | * 3.后端服务部署
22 | * prophet_sql目录:连接到prophet会用到的mysql里source prophet.sql这个文件将库表建好
23 | * prophet_server目录:后端服务,请部署在后端服务器适当目录下
24 | * 修改主配置文件:prophet_server/conf/application.properties
25 | * 启动服务:./bin/startup.sh
26 | * 检查日志:./logs/prophet.log
27 | * 4.前端服务部署
28 | * prophet_fe目录:前端页面,请部署在nginx服务器或某个web服务器目录下例如/static/prophet_fe/,并参照下一步nginx配置
29 | * 5.前端服务nginx配置
30 | ```javascript
31 | upstream prophet{
32 | ip_hash;
33 | server 192.168.1.11:8090;
34 | #server 192.168.1.12:8090;
35 | }
36 |
37 | server {
38 | listen 80;
39 | server_name prophet.xxx.com;
40 |
41 | gzip on;
42 | gzip_min_length 1k;
43 | gzip_proxied expired no-cache no-store private auth;
44 | gzip_types text/plain text/css application/xml application/json application/javascript application/xhtml+xml;
45 |
46 | client_max_body_size 300M;
47 | index index.php index.html index.htm;
48 |
49 | access_log /log/nginx/prophet.access.log main;
50 | error_log /log/nginx/prophet.error.log;
51 |
52 | location ~ \.json$ {
53 | proxy_pass http://prophet;
54 | proxy_set_header Host $host;
55 | proxy_set_header X-Real-IP $remote_addr;
56 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57 | client_max_body_size 200m;
58 | client_body_buffer_size 128k;
59 | proxy_connect_timeout 86400;
60 | #因为后端hive任务执行时间较长,因此该项应该设置无限大,单位秒
61 | proxy_read_timeout 259200;
62 | proxy_buffer_size 4k;
63 | }
64 |
65 | location / {
66 | root "/static/prophet_fe/";
67 | }
68 | }
69 | ```
70 | 配置完重启nginx即可
71 | * 6.配置域名解析prophet.xxx.com到该nginx所在ip
72 | * 7.打开浏览器,访问http://prophet.xxx.com/,输入用户名和密码进行登录。
73 | * 如果配置了LDAP:则填写LDAP账号,prophet内置用户系统不生效。
74 | * 如果配置了prophet内置用户系统:则默认初始化管理账号为admin1,密码为admin1
75 | * 8.开始使用吧!
76 |
77 | ## 系统截图
78 | * 1.登录页面
79 | * 
80 | * 2.主查询界面
81 | * 
82 | * 3.所有机密表展示
83 | * 
84 | * 4.标记哪些表成为机密表
85 | * 
86 | * 5.内置用户系统管理
87 | * 
88 |
89 | ## 性能调优
90 | * prophet JVM能容纳的最大并发线程数NThreads = CPU核心数 * 总CPU利用率 * (1 + CPU等待时间/CPU处理时间)
91 | * 如果一个任务CPU处理时间为100ms,99ms是IO等待时间,系统8核心,CPU利用率50%,则NThreads = 8 * 50% * (1 + 99/100) = 7.96 ~ 8
92 | * 该指标可用于估算单进程prophet最大可运行的并发任务数
93 | * 如果指标不够则需要扩容
94 |
95 | ## 联系方式:
96 | QQ群:669833720
97 |
98 | 加群请注明来历
99 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | org.springframework.boot
5 | spring-boot-starter-parent
6 | 1.3.3.RELEASE
7 |
8 | 4.0.0
9 | com.prophet
10 | prophet
11 | jar
12 | 1.0
13 | prophet Maven Webapp
14 | http://maven.apache.org
15 |
16 | 1.8
17 |
18 |
19 |
20 | junit
21 | junit
22 | test
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-test
27 | test
28 |
29 |
30 | jdk.tools
31 | jdk.tools
32 | 1.8
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-web
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-jdbc
41 |
42 |
43 | mysql
44 | mysql-connector-java
45 |
46 |
47 | org.apache.tomcat.embed
48 | tomcat-embed-jasper
49 | provided
50 |
51 |
52 | javax.servlet
53 | jstl
54 |
55 |
56 | net.sf.json-lib
57 | json-lib-ext-spring
58 | 1.0.2
59 |
60 |
61 | org.apache.hive
62 | hive-jdbc
63 | 2.3.2
64 |
65 |
66 | org.eclipse.jetty.aggregate
67 | jetty-all
68 |
69 |
70 | org.apache.hive
71 | hive-shims
72 |
73 |
74 | org.slf4j
75 | slf4j-log4j12
76 |
77 |
78 |
79 |
80 | org.apache.hadoop
81 | hadoop-common
82 | 2.5.1
83 |
84 |
85 | org.slf4j
86 | slf4j-log4j12
87 |
88 |
89 |
90 |
91 | commons-io
92 | commons-io
93 | 2.4
94 |
95 |
96 | org.springframework.boot
97 | spring-boot-starter-mail
98 |
99 |
100 | org.apache.hive
101 | hive-exec
102 | 2.3.2
103 |
104 |
105 | org.slf4j
106 | slf4j-log4j12
107 |
108 |
109 |
110 |
111 | org.pentaho
112 | pentaho-aggdesigner-algorithm
113 | 5.1.5
114 |
115 |
116 |
117 |
118 |
131 |
132 | maven-assembly-plugin
133 |
134 |
135 | src/main/resources/assembly.xml
136 |
137 |
138 |
139 |
140 | make-assembly
141 | package
142 |
143 | single
144 |
145 |
146 |
147 |
148 |
149 | prophet
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/screenshots/all_secrets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jetbinliu/prophet/b80f85ae88d93e5f931dc6d344f78d989161348b/screenshots/all_secrets.png
--------------------------------------------------------------------------------
/screenshots/config_secrets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jetbinliu/prophet/b80f85ae88d93e5f931dc6d344f78d989161348b/screenshots/config_secrets.png
--------------------------------------------------------------------------------
/screenshots/hive_query.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jetbinliu/prophet/b80f85ae88d93e5f931dc6d344f78d989161348b/screenshots/hive_query.png
--------------------------------------------------------------------------------
/screenshots/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jetbinliu/prophet/b80f85ae88d93e5f931dc6d344f78d989161348b/screenshots/login.png
--------------------------------------------------------------------------------
/screenshots/user_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jetbinliu/prophet/b80f85ae88d93e5f931dc6d344f78d989161348b/screenshots/user_config.png
--------------------------------------------------------------------------------
/src/main/assembly/bin/restart.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PROPHET_HOME="$( dirname "$( cd "$( dirname "$0" )" && pwd ) " )"
4 |
5 | ${PROPHET_HOME}/bin/stop.sh
6 |
7 | ${PROPHET_HOME}/bin/startup.sh
8 |
--------------------------------------------------------------------------------
/src/main/assembly/bin/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PROPHET_HOME="$( dirname "$( cd "$( dirname "$0" )" && pwd ) " )"
4 |
5 | export JAVA_HOME=/apps/srv/jdk/bin
6 |
7 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -Xmx1500m"
8 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:+UseG1GC -verbose:gc"
9 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:+PrintGCDetails"
10 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:+PrintGCTimeStamps"
11 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:+PrintGCDateStamps"
12 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -Xloggc:${PROPHET_HOME}/logs/prophet-gc.log"
13 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:+HeapDumpOnOutOfMemoryError"
14 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:HeapDumpPath=${PROPHET_HOME}/logs/heapdump.hprof"
15 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:+UseGCLogFileRotation"
16 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:GCLogFileSize=128M -XX:NumberOfGCLogFiles=4"
17 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -XX:+HeapDumpOnOutOfMemoryError"
18 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -Dcom.sun.management.jmxremote=true"
19 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -Dcom.sun.management.jmxremote.port=30005"
20 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -Dcom.sun.management.jmxremote.ssl=false"
21 | JAVA_CMD_OPTS="${JAVA_CMD_OPTS} -Dcom.sun.management.jmxremote.authenticate=false"
22 |
23 | MAIN_CLASS="com.prophet.Application"
24 |
25 | pid=`ps -ef |grep "java"|grep "prophet" |grep -v "grep" |awk '{print $2}'`
26 | if [ -f ${PROPHET_HOME}/logs/prophet.pid ];then
27 | echo "Error! Prophet is running and pid is ${pid}, please stop it first."
28 | exit 1
29 | else
30 | #set classpath
31 | for j in ${PROPHET_HOME}/lib/*.jar;do
32 | CLASSPATH=${j}:"${CLASSPATH}"
33 | done
34 | CLASSPATH="${PROPHET_HOME}/conf:${CLASSPATH}"
35 |
36 | #nohup java -jar
37 | nohup ${JAVA_HOME}/java ${JAVA_CMD_OPTS} -classpath .:${CLASSPATH} ${MAIN_CLASS} -Dglobal.config.path=${PROPHET_HOME}/conf/ --spring.config.location=${PROPHET_HOME}/conf/application.properties &>>${PROPHET_HOME}/logs/prophet.log &
38 |
39 | sleep 2
40 | pid=`ps -ef |grep "java"|grep "prophet" |grep -v "grep" |awk '{print $2}'`
41 | if [ ${pid} ];then
42 | echo "Prophet started successfully."
43 | echo ${pid} > ${PROPHET_HOME}/logs/prophet.pid
44 | else
45 | echo "Error! Prophet failed to start... please check the logs."
46 | exit 1
47 | fi
48 | fi
49 | exit 0
50 |
--------------------------------------------------------------------------------
/src/main/assembly/bin/stop.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PROPHET_HOME="$( dirname "$( cd "$( dirname "$0" )" && pwd ) " )"
4 |
5 | pid=`ps -ef |grep "java"|grep "prophet" |grep -v "grep" |awk '{print $2}'`
6 | if [ ${pid} ];then
7 | kill -9 ${pid}
8 | sleep 1
9 | if [[ $? -eq 0 ]];then
10 | echo "Prophet stopped successfully."
11 | rm -f ${PROPHET_HOME}/logs/prophet.pid &>/dev/null
12 | else
13 | echo "Error! Prophet failed to stop..."
14 | fi
15 | else
16 | echo "Prophet is not running, no need to stop."
17 | fi
18 | exit 0
19 |
--------------------------------------------------------------------------------
/src/main/assembly/conf/application.properties:
--------------------------------------------------------------------------------
1 | #########################################datasource###########################################
2 | spring.ds_prophet.url=jdbc:mysql://192.168.5.10:5621/prophet
3 | spring.ds_prophet.username=prophet
4 | spring.ds_prophet.password=prophet
5 | spring.ds_prophet.driver-class-name=com.mysql.jdbc.Driver
6 | spring.ds_prophet.type=org.apache.commons.dbcp2.BasicDataSource
7 | #spring.ds_prophet.max-wait=300000
8 | #spring.ds_prophet.max-active=50
9 | #spring.ds_prophet.max-idle=10
10 | spring.ds_prophet.min-idle=4
11 | spring.ds_prophet.validation-query=select 123
12 | spring.ds_prophet.test-while-idle=true
13 |
14 | spring.ds_hive_metastore.url = jdbc:mysql://192.168.5.7:3306/hive_metadata
15 | spring.ds_hive_metastore.username = hiveuser
16 | spring.ds_hive_metastore.password = hivepass
17 | spring.ds_hive_metastore.driver-class-name=com.mysql.jdbc.Driver
18 | spring.ds_hive_metastore.type=org.apache.commons.dbcp2.BasicDataSource
19 | spring.ds_hive_metastore.min-idle=4
20 | spring.ds_hive_metastore.validation-query=select 123
21 | spring.ds_hive_metastore.test-while-idle=true
22 |
23 | spring.ds_hive_server.url = jdbc:hive2://192.168.1.25:10000/default
24 | spring.ds_hive_server.username = hadoop
25 | spring.ds_hive_server.password =
26 | spring.ds_hive_server.driver-class-name=org.apache.hive.jdbc.HiveDriver
27 | spring.ds_hive_server.type=org.apache.commons.dbcp2.BasicDataSource
28 | spring.ds_hive_server.min-idle=10
29 | spring.ds_hive_server.test-while-idle=true
30 | #########################################datasource###########################################
31 |
32 | #############################################jsp##############################################
33 | spring.mvc.view.prefix=/WEB-INF/jsp/
34 | spring.mvc.view.suffix=.jsp
35 | #############################################jsp##############################################
36 |
37 | #######################################prophet server#########################################
38 | server.port=8090
39 | #session max alive seconds, default 30 mins
40 | server.session-timeout=2592000
41 | server.tomcat.uri-encoding=UTF-8
42 | server.tomcat.max-threads=800
43 | server.tomcat.basedir=logs/
44 | server.tomcat.access-log-enabled=true
45 | server.tomcat.accesslog.directory=tomcat_access_logs/
46 |
47 | #######################################prophet server#########################################
48 |
49 | #######################################logging################################################
50 | #logging.config=/home/prophet_server/conf/logging.xml
51 | #######################################logging################################################
52 |
53 | #####################################user auth################################################
54 | #user authentication system, available values(case insensitive): 1.LDAP 2.prophet
55 | authentication.system=LDAP
56 |
57 | #if choose ldap, then following info is mandatory. Otherwise ignored.
58 | authentication.ldap.url=ldap://xxx.prophet.com/
59 | authentication.ldap.base-dn=CN=mycn,OU=Users,DC=prophet,DC=com
60 | authentication.ldap.user-search-dn=OU=myou,DC=prophet,DC=com
61 | authentication.ldap.user-search-column=username
62 | authentication.ldap.factory=com.sun.jndi.ldap.LdapCtxFactory
63 | authentication.ldap.security-authentication=simple
64 | authentication.ldap.security-credenticials=abc123456790
65 | #####################################user auth################################################
66 |
67 | #####################################email####################################################
68 | spring.mail.host=smtp.prophet.com
69 | spring.mail.port=25
70 | #spring.mail.username=123
71 | #spring.mail.password=123
72 | spring.mail.from=sender@prophet.com
73 | spring.mail.properties.mail.smtp.auth=false
74 | spring.mail.properties.mail.smtp.starttls.enable=false
75 | spring.mail.properties.mail.smtp.starttls.required=false
76 | #suffix used within your company for everyone
77 | spring.mail.company.suffix=@prophet.com
78 | #####################################email####################################################
--------------------------------------------------------------------------------
/src/main/java/com/prophet/Application.java:
--------------------------------------------------------------------------------
1 | package com.prophet;
2 | import org.springframework.boot.SpringApplication;
3 | import org.springframework.boot.autoconfigure.SpringBootApplication;
4 | import org.springframework.transaction.annotation.EnableTransactionManagement;
5 | import org.springframework.boot.context.web.SpringBootServletInitializer;
6 | import org.springframework.boot.web.servlet.ServletComponentScan;
7 | import org.springframework.boot.builder.SpringApplicationBuilder;
8 |
9 |
10 | @SpringBootApplication
11 | @EnableTransactionManagement
12 | @ServletComponentScan
13 | //@ComponentScan
14 | public class Application extends SpringBootServletInitializer{
15 |
16 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
17 | return application.sources(Application.class);
18 | }
19 |
20 | public static void main(String[] args) throws Exception{
21 | SpringApplication.run(Application.class, args);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/prophet/common/Encryptor.java:
--------------------------------------------------------------------------------
1 | package com.prophet.common;
2 | import java.math.BigInteger;
3 | import java.security.MessageDigest;
4 | import java.security.NoSuchAlgorithmException;
5 |
6 |
7 | /**
8 | * 加密算法基础工具类
9 | *
10 | */
11 | public abstract class Encryptor {
12 | public static final String KEY_SHA = "SHA";
13 | public static final String KEY_MD5 = "MD5";
14 |
15 | /**
16 | * MAC算法可选以下多种算法
17 | *
18 | * HmacMD5
19 | * HmacSHA1
20 | * HmacSHA256
21 | * HmacSHA384
22 | * HmacSHA512
23 | */
24 | public static final String KEY_MAC = "HmacMD5";
25 |
26 | /**
27 | * MD5加密
28 | *
29 | * @param data
30 | * @return
31 | * @throws Exception
32 | */
33 | public static String encryptMD5(String data){
34 | MessageDigest md5 = null;
35 | try {
36 | md5 = MessageDigest.getInstance(KEY_MD5);
37 | } catch (NoSuchAlgorithmException e) {
38 | e.printStackTrace();
39 | }
40 | md5.update(data.getBytes());
41 | BigInteger bi = new BigInteger(md5.digest());
42 | return bi.toString(16);
43 | }
44 |
45 | /**
46 | * SHA加密
47 | *
48 | * @param data
49 | * @return
50 | * @throws Exception
51 | */
52 | public static String encryptSHA(String data){
53 | MessageDigest sha = null;
54 | try {
55 | sha = MessageDigest.getInstance(KEY_SHA);
56 | } catch (NoSuchAlgorithmException e) {
57 | e.printStackTrace();
58 | }
59 | sha.update(data.getBytes());
60 | BigInteger bi = new BigInteger(sha.digest());
61 | return bi.toString(32);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/prophet/common/HQLParser.java:
--------------------------------------------------------------------------------
1 | package com.prophet.common;
2 | import java.util.HashMap;
3 | import java.util.HashSet;
4 | import java.util.Map;
5 | import java.util.Set;
6 | import java.util.Stack;
7 | import java.util.TreeMap;
8 |
9 | import org.apache.hadoop.hive.ql.parse.ASTNode;
10 | import org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer;
11 | import org.apache.hadoop.hive.ql.parse.HiveParser;
12 | import org.apache.hadoop.hive.ql.parse.ParseDriver;
13 | import org.apache.hadoop.hive.ql.parse.ParseException;
14 |
15 | public class HQLParser {
16 | /**
17 | * 该方法供外部调用
18 | * @param sql
19 | * @throws Exception
20 | */
21 | public void parseHQL(String sql) throws ParseException, org.antlr.runtime.NoViableAltException {
22 | ParseDriver pd = new ParseDriver();
23 | ASTNode ast = pd.parse(sql);
24 | this.parse(ast);
25 | }
26 |
27 | private static final String UNKNOWN = "UNKNOWN";
28 | private Map alias = new HashMap();
29 | private Map cols = new TreeMap();
30 | private Map colAlais = new TreeMap();
31 | private Set tables = new HashSet();
32 | private Stack tableNameStack = new Stack();
33 | private Stack operStack = new Stack();
34 | private String nowQueryTable = "";//定义及处理不清晰,修改为query或from节点对应的table集合或许好点。目前正在查询处理的表可能不止一个。
35 | private Oper oper ;
36 | private boolean joinClause = false;
37 |
38 | public Map getCols() {
39 | return cols;
40 | }
41 | public Set getTables() {
42 | Set newTables = new HashSet();
43 | //去掉default12.`user`里的``
44 | for (String t : this.tables) {
45 | newTables.add(t.replaceAll("`", ""));
46 | }
47 | return newTables;
48 | }
49 | public String getOper() {
50 | return oper.toString();
51 | }
52 |
53 | private enum Oper {
54 | SELECT, INSERT, DROP, TRUNCATE, LOAD, CREATETABLE, ALTER, CREATEDATABASE, DROPDATABASE;
55 | }
56 | public Set parseIteral(ASTNode ast) {
57 | Set set= new HashSet();//当前查询所对应到的表集合
58 | prepareToParseCurrentNodeAndChilds(ast);
59 | set.addAll(parseChildNodes(ast));
60 | set.addAll(parseCurrentNode(ast ,set));
61 | endParseCurrentNode(ast);
62 | return set;
63 | }
64 | private void endParseCurrentNode(ASTNode ast){
65 | if (ast.getToken() != null) {
66 | switch (ast.getToken().getType()) {//join 从句结束,跳出join
67 | case HiveParser.TOK_RIGHTOUTERJOIN:
68 | case HiveParser.TOK_LEFTOUTERJOIN:
69 | case HiveParser.TOK_JOIN:
70 | joinClause = false;
71 | break;
72 | case HiveParser.TOK_QUERY:
73 | break;
74 | case HiveParser.TOK_INSERT:
75 | case HiveParser.TOK_SELECT:
76 | nowQueryTable = tableNameStack.pop();
77 | oper = operStack.pop();
78 | break;
79 | }
80 | }
81 | }
82 | private Set parseCurrentNode(ASTNode ast, Set set){
83 | if (ast.getToken() != null) {
84 | switch (ast.getToken().getType()) {
85 | case HiveParser.TOK_TABLE_PARTITION:
86 | if (ast.getChildCount() != 2) {
87 | String table = BaseSemanticAnalyzer
88 | .getUnescapedName((ASTNode) ast.getChild(0));
89 | if (oper == Oper.SELECT) {
90 | nowQueryTable = table;
91 | }
92 | //tables.add(table + "\t" + oper);
93 | tables.add(table);
94 | }
95 | break;
96 |
97 | case HiveParser.TOK_TAB:// outputTable
98 | String tableTab = BaseSemanticAnalyzer
99 | .getUnescapedName((ASTNode) ast.getChild(0));
100 | if (oper == Oper.SELECT) {
101 | nowQueryTable = tableTab;
102 | }
103 | //tables.add(tableTab + "\t" + oper);
104 | tables.add(tableTab);
105 | break;
106 | case HiveParser.TOK_TABREF:// inputTable
107 | ASTNode tabTree = (ASTNode) ast.getChild(0);
108 | String tableName = (tabTree.getChildCount() == 1) ? BaseSemanticAnalyzer
109 | .getUnescapedName((ASTNode) tabTree.getChild(0))
110 | : BaseSemanticAnalyzer
111 | .getUnescapedName((ASTNode) tabTree.getChild(0))
112 | + "." + tabTree.getChild(1);
113 | if (oper == Oper.SELECT) {
114 | if(joinClause && !"".equals(nowQueryTable) ){
115 | nowQueryTable += "&"+tableName;//
116 | }else{
117 | nowQueryTable = tableName;
118 | }
119 | set.add(tableName);
120 | }
121 | //tables.add(tableName + "\t" + oper);
122 | tables.add(tableName);
123 | if (ast.getChild(1) != null) {
124 | String alia = ast.getChild(1).getText().toLowerCase();
125 | alias.put(alia, tableName);
126 | }
127 | break;
128 | case HiveParser.TOK_TABLE_OR_COL:
129 | if (ast.getParent().getType() != HiveParser.DOT) {
130 | String col = ast.getChild(0).getText().toLowerCase();
131 | if (alias.get(col) == null
132 | && colAlais.get(nowQueryTable + "." + col) == null) {
133 | if(nowQueryTable.indexOf("&") > 0){//sql23
134 | cols.put(UNKNOWN + "." + col, "");
135 | }else{
136 | cols.put(nowQueryTable + "." + col, "");
137 | }
138 | }
139 | }
140 | break;
141 | case HiveParser.TOK_ALLCOLREF:
142 | cols.put(nowQueryTable + ".*", "");
143 | break;
144 | case HiveParser.TOK_SUBQUERY:
145 | if (ast.getChildCount() == 2) {
146 | String tableAlias = unescapeIdentifier(ast.getChild(1)
147 | .getText());
148 | String aliaReal = "";
149 | for(String table : set){
150 | aliaReal+=table+"&";
151 | }
152 | if(aliaReal.length() !=0){
153 | aliaReal = aliaReal.substring(0, aliaReal.length()-1);
154 | }
155 | alias.put(tableAlias, aliaReal);
156 | }
157 | break;
158 |
159 | case HiveParser.TOK_SELEXPR:
160 | if (ast.getChild(0).getType() == HiveParser.TOK_TABLE_OR_COL) {
161 | String column = ast.getChild(0).getChild(0).getText()
162 | .toLowerCase();
163 | if(nowQueryTable.indexOf("&") > 0){
164 | cols.put(UNKNOWN + "." + column, "");
165 | }else if (colAlais.get(nowQueryTable + "." + column) == null) {
166 | cols.put(nowQueryTable + "." + column, "");
167 | }
168 | } else if (ast.getChild(1) != null) {// TOK_SELEXPR (+
169 | // (TOK_TABLE_OR_COL id)
170 | // 1) dd
171 | String columnAlia = ast.getChild(1).getText().toLowerCase();
172 | colAlais.put(nowQueryTable + "." + columnAlia, "");
173 | }
174 | break;
175 | case HiveParser.DOT:
176 | if (ast.getType() == HiveParser.DOT) {
177 | if (ast.getChildCount() == 2) {
178 | if (ast.getChild(0).getType() == HiveParser.TOK_TABLE_OR_COL
179 | && ast.getChild(0).getChildCount() == 1
180 | && ast.getChild(1).getType() == HiveParser.Identifier) {
181 | String alia = BaseSemanticAnalyzer
182 | .unescapeIdentifier(ast.getChild(0)
183 | .getChild(0).getText()
184 | .toLowerCase());
185 | String column = BaseSemanticAnalyzer
186 | .unescapeIdentifier(ast.getChild(1)
187 | .getText().toLowerCase());
188 | String realTable = null;
189 | if (!tables.contains(alia + "\t" + oper)
190 | && alias.get(alia) == null) {// [b SELECT, a
191 | // SELECT]
192 | alias.put(alia, nowQueryTable);
193 | }
194 | if (tables.contains(alia + "\t" + oper)) {
195 | realTable = alia;
196 | } else if (alias.get(alia) != null) {
197 | realTable = alias.get(alia);
198 | }
199 | if (realTable == null || realTable.length() == 0 || realTable.indexOf("&") > 0) {
200 | realTable = UNKNOWN;
201 | }
202 | cols.put(realTable + "." + column, "");
203 |
204 | }
205 | }
206 | }
207 | break;
208 | case HiveParser.TOK_ALTERTABLE_ADDPARTS:
209 | case HiveParser.TOK_ALTERTABLE_RENAME:
210 | case HiveParser.TOK_ALTERTABLE_ADDCOLS:
211 | ASTNode alterTableName = (ASTNode) ast.getChild(0);
212 | //tables.add(alterTableName.getText() + "\t" + oper);
213 | tables.add(alterTableName.getText());
214 | break;
215 | }
216 | }
217 | return set;
218 | }
219 |
220 | private Set parseChildNodes(ASTNode ast){
221 | Set set= new HashSet();
222 | int numCh = ast.getChildCount();
223 | if (numCh > 0) {
224 | for (int num = 0; num < numCh; num++) {
225 | ASTNode child = (ASTNode) ast.getChild(num);
226 | set.addAll(parseIteral(child));
227 | }
228 | }
229 | return set;
230 | }
231 |
232 | private void prepareToParseCurrentNodeAndChilds(ASTNode ast){
233 | if (ast.getToken() != null) {
234 | switch (ast.getToken().getType()) {
235 | //join 从句开始
236 | case HiveParser.TOK_RIGHTOUTERJOIN:
237 | case HiveParser.TOK_LEFTOUTERJOIN:
238 | case HiveParser.TOK_JOIN:
239 | joinClause = true;
240 | break;
241 | case HiveParser.TOK_QUERY:
242 | tableNameStack.push(nowQueryTable);
243 | operStack.push(oper);
244 | nowQueryTable = "";//sql22
245 | oper = Oper.SELECT;
246 | break;
247 | case HiveParser.TOK_INSERT:
248 | tableNameStack.push(nowQueryTable);
249 | operStack.push(oper);
250 | oper = Oper.INSERT;
251 | break;
252 | case HiveParser.TOK_SELECT:
253 | tableNameStack.push(nowQueryTable);
254 | operStack.push(oper);
255 | // nowQueryTable = "";//语法树join
256 | oper = Oper.SELECT;
257 | break;
258 | case HiveParser.TOK_DROPTABLE:
259 | oper = Oper.DROP;
260 | break;
261 | case HiveParser.TOK_TRUNCATETABLE:
262 | oper = Oper.TRUNCATE;
263 | break;
264 | case HiveParser.TOK_LOAD:
265 | oper = Oper.LOAD;
266 | break;
267 | case HiveParser.TOK_CREATETABLE:
268 | oper = Oper.CREATETABLE;
269 | break;
270 | case HiveParser.TOK_CREATEDATABASE:
271 | oper = Oper.CREATETABLE;
272 | break;
273 | case HiveParser.TOK_DROPDATABASE:
274 | oper = Oper.DROPDATABASE;
275 | break;
276 | }
277 | if (ast.getToken() != null
278 | && ast.getToken().getType() >= HiveParser.TOK_ALTERDATABASE_PROPERTIES
279 | && ast.getToken().getType() <= HiveParser.TOK_ALTERVIEW_RENAME) {
280 | oper = Oper.ALTER;
281 | }
282 | }
283 | }
284 | public static String unescapeIdentifier(String val) {
285 | if (val == null) {
286 | return null;
287 | }
288 | if (val.charAt(0) == '`' && val.charAt(val.length() - 1) == '`') {
289 | val = val.substring(1, val.length() - 1);
290 | }
291 | return val;
292 | }
293 |
294 | public void parse(ASTNode ast) {
295 | parseIteral(ast);
296 | }
297 |
298 | }
299 |
--------------------------------------------------------------------------------
/src/main/java/com/prophet/common/QueryHistoryStatusEnum.java:
--------------------------------------------------------------------------------
1 | package com.prophet.common;
2 |
3 | /**
4 | * query_history表status字段的枚举值
5 | *
6 | */
7 | public enum QueryHistoryStatusEnum {
8 | FINISHED(0, "已运行完毕"), RUNNING(1, "任务执行中"), ABORTED(2, "已经被取消"), ERROR(3, "运行出现错误");
9 |
10 | private int index;
11 | private String name;
12 |
13 | private QueryHistoryStatusEnum(int index, String name) {
14 | this.index = index;
15 | this.name = name;
16 | }
17 |
18 | public String getName() {
19 | return this.name;
20 | }
21 |
22 | public void setName(String name) {
23 | this.name = name;
24 | }
25 |
26 | public int getIndex() {
27 | return this.index;
28 | }
29 |
30 | public void setIndex(int index) {
31 | this.index = index;
32 | }
33 |
34 | /**
35 | * 根据键值获取对应的状态名的方法
36 | * @param index
37 | * @return
38 | */
39 | public static String getNameByIndex(int index) throws Exception {
40 | String name = "";
41 | for (QueryHistoryStatusEnum q : QueryHistoryStatusEnum.values()) {
42 | if (q.getIndex() == index) {
43 | name = q.getName();
44 | }
45 | }
46 | //如果遍历完了都没有找到对应的index,则抛出异常
47 | if (name.equals("")) {
48 | throw new Exception(String.format("根据状态值%d无法找到对应的QueryHistoryStatus枚举值描述!", index));
49 | }
50 | return name;
51 | }
52 |
53 | /**
54 | * 根据状态值描述获取对应的状态值的方法
55 | * @param name
56 | * @return
57 | * @throws Exception
58 | */
59 | public static int getIndexByName(String name) throws Exception {
60 | int index = -1;
61 | for (QueryHistoryStatusEnum q : QueryHistoryStatusEnum.values()) {
62 | if (q.getName().equals(name)) {
63 | index = q.getIndex();
64 | }
65 | }
66 | //如果遍历完了都没有找到对应的index,则抛出异常
67 | if (index == -1) {
68 | throw new Exception(String.format("根据状态值描述%s无法找到对应的QueryHistoryStatus枚举值!", name));
69 | }
70 | return index;
71 | }
72 | }
--------------------------------------------------------------------------------
/src/main/java/com/prophet/common/ThreadPool.java:
--------------------------------------------------------------------------------
1 | package com.prophet.common;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.Callable;
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.Future;
8 | import java.util.concurrent.ThreadFactory;
9 | import java.util.concurrent.ConcurrentHashMap;
10 |
11 | import com.prophet.dao.task.HiveQueryAsyncTask;
12 |
13 | /**
14 | * 线程池基础类
15 | */
16 | public class ThreadPool {
17 | private final static int POOL_SIZE = 64;
18 | private static ExecutorService executorService;
19 | public static Map> activeThreadsMap = new ConcurrentHashMap>(); //String为线程名,Future为线程结果
20 |
21 | public final static String HIVE_QUERY_THREAD_NAME = "HiveQueryAsyncTaskThread-";
22 | public final static String HIVE_EMAIL_THREAD_NAME = "HiveResultMailThread-";
23 |
24 | private static ExecutorService getExecutor() {
25 | if (executorService == null || executorService.isShutdown()) {
26 | synchronized (ThreadFactory.class) {
27 | if (executorService == null || executorService.isShutdown()) {
28 | executorService = Executors.newFixedThreadPool(POOL_SIZE);
29 | activeThreadsMap = new ConcurrentHashMap>();
30 | }
31 | }
32 | }
33 | return executorService;
34 | }
35 |
36 | /**
37 | * 手动终止某个线程,并将Future从活跃列表移除
38 | * @param queryHistId
39 | */
40 | public static void stopThread(long queryHistId) {
41 | if (activeThreadsMap != null) {
42 | Future> result = activeThreadsMap.get(HIVE_QUERY_THREAD_NAME + queryHistId);
43 | if (result != null) {
44 | result.cancel(true);
45 | }
46 | activeThreadsMap.remove(HIVE_QUERY_THREAD_NAME + queryHistId);
47 | }
48 | }
49 |
50 | /**
51 | * 不返回结果的execute方法
52 | * @param thread
53 | */
54 | public static void execute(T thread) {
55 | getExecutor().execute(thread);
56 | }
57 |
58 | /**
59 | * 提交hive query任务
60 | * @param hiveTask
61 | * @return Future
62 | */
63 | public static void executeHiveQuery(HiveQueryAsyncTask hiveTask) {
64 | Future> result = getExecutor().submit(hiveTask);
65 |
66 | //加入活跃线程列表,方便后面取消任务
67 | if (hiveTask != null) {
68 | activeThreadsMap.put(HIVE_QUERY_THREAD_NAME + hiveTask.getQueryHistId(), result);
69 |
70 | }
71 | }
72 |
73 | /**
74 | * 可以返回结果的提交方法
75 | * @param Callable task
76 | * @return 一个Future对象
77 | */
78 | public static Future submit(Callable task) {
79 | return getExecutor().submit(task);
80 | }
81 |
82 | /**
83 | * 不再使用线程池时,调用该方法关闭线程池即可
84 | */
85 | public static final void shutdown() {
86 | getExecutor().shutdown();
87 | activeThreadsMap = null;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/com/prophet/config/HiveDataSourceConfig.java:
--------------------------------------------------------------------------------
1 | package com.prophet.config;
2 |
3 | import javax.sql.DataSource;
4 | import org.springframework.beans.factory.annotation.Qualifier;
5 | import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
6 | import org.springframework.boot.context.properties.ConfigurationProperties;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 | //import org.springframework.context.annotation.Primary;
10 | import org.springframework.jdbc.core.JdbcTemplate;
11 |
12 | @Configuration
13 | public class HiveDataSourceConfig {
14 | @Bean(name="hiveServerDS")
15 | @ConfigurationProperties(prefix="spring.ds_hive_server")
16 | public DataSource prophetHiveServerDataSource() {
17 | return DataSourceBuilder.create().build();
18 | }
19 |
20 | @Bean(name="hiveServerJdbcTemplate")
21 | public JdbcTemplate getHiveServerJdbcTemplate(@Qualifier("hiveServerDS") DataSource dsHiveServer) {
22 | return new JdbcTemplate(dsHiveServer);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/prophet/config/HiveResultTextConfig.java:
--------------------------------------------------------------------------------
1 | package com.prophet.config;
2 |
3 | public class HiveResultTextConfig {
4 |
5 | private final static String HIVE_RESULT_FILE_DIR = "data/";
6 | //public final static String HIVE_RESULT_FIELD_DELIMITER = "##@@#";
7 | public final static String HIVE_RESULT_FIELD_DELIMITER = "\001\001"; //hive默认列分隔符^A的八进制编码,这里是两个^A
8 |
9 | /**
10 | * 获取数据文件绝对路径
11 | * @param username
12 | * @param queryHistId
13 | * @return
14 | */
15 | public final static String getDataFileName(String username, long queryHistId) {
16 | return String.format("%s%s-%d.txt", HIVE_RESULT_FILE_DIR, username, queryHistId);
17 | }
18 |
19 | /**
20 | * 获取meta文件绝对路径
21 | * @param username
22 | * @param queryHistId
23 | * @return
24 | */
25 | public final static String getMetaFileName(String username, long queryHistId) {
26 | return String.format("%s%s-%d.meta", HIVE_RESULT_FILE_DIR, username, queryHistId);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/prophet/config/MySQLDataSourceConfig.java:
--------------------------------------------------------------------------------
1 | package com.prophet.config;
2 |
3 | import javax.sql.DataSource;
4 | import org.springframework.beans.factory.annotation.Qualifier;
5 | import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
6 | import org.springframework.boot.context.properties.ConfigurationProperties;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 | import org.springframework.context.annotation.Primary;
10 | import org.springframework.jdbc.core.JdbcTemplate;
11 |
12 | @Configuration
13 | public class MySQLDataSourceConfig {
14 | @Bean(name="prophetDS")
15 | @Primary
16 | @ConfigurationProperties(prefix="spring.ds_prophet")
17 | public DataSource prophetMysqlDataSource() {
18 | return DataSourceBuilder.create().build();
19 | }
20 |
21 | @Bean(name="prophetJdbcTemplate")
22 | public JdbcTemplate getProphetJdbcTemplate(@Qualifier("prophetDS") DataSource dsProphet) {
23 | return new JdbcTemplate(dsProphet);
24 | }
25 |
26 | @Bean(name="hiveMetaStoreDS")
27 | @ConfigurationProperties(prefix="spring.ds_hive_metastore")
28 | public DataSource hiveMetaStoreMysqlDataSource() {
29 | return DataSourceBuilder.create().build();
30 | }
31 |
32 | @Bean(name="hiveMetaStoreJdbcTemplate")
33 | public JdbcTemplate getHiveMetaStoreJdbcTemplate(@Qualifier("hiveMetaStoreDS") DataSource dsHive) {
34 | return new JdbcTemplate(dsHive);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/prophet/dao/AdminDao.java:
--------------------------------------------------------------------------------
1 | package com.prophet.dao;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.beans.factory.annotation.Qualifier;
8 | import org.springframework.jdbc.core.JdbcTemplate;
9 | import org.springframework.stereotype.Repository;
10 |
11 | @Repository
12 | public class AdminDao {
13 | @Autowired
14 | @Qualifier("prophetJdbcTemplate")
15 | private JdbcTemplate jdbcTemplate;
16 |
17 | public List