Hive-0.12.0的Web接口HWI安装、配置、改造及使用

简介:

使用Hive的HWI接口,可以通过在Web页面上提交HQL查询操作,并浏览查询结果数据。默认情况下,HWI只支持浏览结果数据,不能够下载查询结果文件(当然,HWI可能也是考虑通过Web下载大量的结果数据,对服务器造成压力,或者处于安全方面的考虑)。我们对HWI进行了简单的改造,改造内容主要是增加了一个内置的文件服务器,可以通过页面进行查询,然后下载结果文件。

HWI安装配置

首先,要保证Hadoop集群正常运行,Hive能够正常使用。
先要安装Ant,如下所示:

1 wget http://mirrors.hust.edu.cn/apache//ant/binaries/apache-ant-1.9.4-bin.tar.gz
2 tar xvzf apache-ant-1.9.4-bin.tar.gz
3 ln -s /usr/local/apache-ant-1.9.4-bin /usr/local/ant

修改Hive的环境配置文件hive-env.sh,增加如下配置内容:

1 export ANT_LIB=/usr/local/ant

将如下JAR文件拷贝到${HIVE_HOME}/lib目录下面:

1 // 用于编译JSP文件
2 jasper-compiler-5.5.23.jar
3 jasper-runtime-5.5.23.jar
4
5 // 替换默认的servlet-api-2.5-20081211.jar,我使用的是apache-tomcat-7.0.53/lib下面的servlet-api.jar文件
6 servlet-api.jar

另外,由于Hive 0.12.0版本的HWI的问题,需要将你的${JAVA_HOME}/lib/tools.jar文件也加入到${HIVE_HOME}/lib目录下。这个算是一个Bug吧,大概要到Hive 0.13.0版本能够解决。
接下来,需要在${HIVE_HOME}/conf/hive-site.xml配置文件中,加入如下配置内容:

01 <!-- HWI Configuration -->
02 <property>
03 <name>hive.hwi.listen.host</name>
04 <value>0.0.0.0</value>
05 <description>This is the host address the Hive Web Interface will listen on.</description>
06 </property>
07 <property>
08 <name>hive.hwi.listen.port</name>
09 <value>9999</value>
10 <description>This is the port the Hive Web Interface will listen on.</description>
11 </property>
12 <property>
13 <name>hive.hwi.war.file</name>
14 <value>/lib/hwi.war</value>
15 <description>This is the WAR file with the jsp content for Hive Web Interface.</description>
16 </property>

其中,属性hive.hwi.war.file对应的hwi.war文件,我们会在后面改造之后重新打包为该文件,并拷贝到${HIVE_HOME}/lib目录下面。

HWI改造

默认使用HWI是没有查询结果文件下载功能的,可以增加一个文件服务器,用来存放并提供下载查询结果文件。这里,我发现HWI虽然也提供一个Web容器,但是由于封装的太深,所以放弃修改内部源码,而是直接通过我熟悉的方式,使用Jetty实现了一个文件服务,需要用到下面的一些JAR文件:

01 // 用于文件服务器
02 jetty-continuation-8.1.0.RC5.jar
03 jetty-io-8.1.0.RC5.jar
04 jetty-server-8.1.0.RC5.jar
05 jetty-util-8.1.0.RC5.jar
06 jetty-xml-8.1.0.RC5.jar
07 jetty-http-8.1.0.RC5.jar
08 jetty-security-8.1.0.RC5.jar
09 jetty-servlet-8.1.0.RC5.jar
10 jetty-webapp-8.1.0.RC5.jar
  • 实现文件服务器

首先,定义一个服务器接口,用来控制文件服务器的启停操作等,如下所示:

01 package org.shirdrn.hadoop.hive.jetty;
02
03 public interface JettyServer {
04
05 int getPort();
06
07 /** Start this server. */
08 void start();
09
10 /** Stop this server. */
11 void close();
12
13 /** Wait for this server to exit. */
14 void join() throws InterruptedException;
15
16 }

然后,实现一个文件服务器,代码如下所示:

01 package org.shirdrn.hadoop.hive.jetty;
02
03 import java.util.HashMap;
04 import java.util.Iterator;
05 import java.util.Map;
06 import java.util.Map.Entry;
07
08 import org.apache.commons.logging.Log;
09 import org.apache.commons.logging.LogFactory;
10 import org.eclipse.jetty.server.Server;
11 import org.eclipse.jetty.server.handler.DefaultHandler;
12 import org.eclipse.jetty.server.handler.HandlerList;
13 import org.eclipse.jetty.server.handler.ResourceHandler;
14 import org.eclipse.jetty.server.nio.SelectChannelConnector;
15
16 /**
17 * Jetty file server for handling hvie query result file downloading.
18 *
19 * @author yanjun
20 */
21 public class JettyFileServer implements JettyServer {
22
23 private static final Log LOG = LogFactory.getLog(JettyFileServer.class);
24 private final Server server;
25 private final int port = 9722;
26
27 static Map<String, String> users = new HashMap<String, String>();
28 static Map<String, String> resourceBases = new HashMap<String, String>();
29 static {
30 users.put("user", "user");
31 resourceBases.put("user", "/download");
32 }
33
34 public JettyFileServer() {
35 server = new Server(port);
36 SelectChannelConnector connector = new SelectChannelConnector();
37 server.addConnector(connector);
38 // create file context for each authorized user
39 HandlerList handlers = new HandlerList();
40 createFileServerContexts(handlers);
41
42 handlers.addHandler(new DefaultHandler());
43
44 server.setHandler(handlers);
45 }
46
47 private void createFileServerContexts(HandlerList handlers) {
48 Iterator<Entry<String, String>> iter = users.entrySet().iterator();
49 while(iter.hasNext()) {
50 Entry<String, String> entry = iter.next();
51 String user = entry.getKey();
52 String fileBase = resourceBases.get(user);
53 ResourceHandler resourceHandler = createResourceHandler(user, fileBase);
54 handlers.addHandler(resourceHandler);
55 }
56 }
57
58 private ResourceHandler createResourceHandler(String user, String fileBase) {
59 ResourceHandler resourceHandler = new ResourceHandler();
60 resourceHandler.setDirectoriesListed(true);
61 resourceHandler.setWelcomeFiles(new String[]{ "index.html" });
62 resourceHandler.setResourceBase(fileBase);
63 return resourceHandler;
64 }
65
66 @Override
67 public int getPort() {
68 return port;
69 }
70
71 @Override
72 public void start() {
73 try {
74 server.start();
75 } catch (InterruptedException e) {
76 } catch (Exception e) {
77 throw new RuntimeException("Fail to start Jetty file server!", e);
78 }
79 }
80
81 @Override
82 public void close() {
83 try {
84 server.stop();
85 } catch (Exception e) {
86 throw new RuntimeException("Fail to stop Jetty file server!", e);
87 }
88 }
89
90 @Override
91 public void join() throws InterruptedException {
92 server.join();
93 }
94
95 }

这里,为了简化,我没有使用配置的方式,配置文件服务器的resource base目录,直接写死路径在代码里面,默认是/download目录,主要的是,这个目录需要设置一下权限,可以允许任何人写,但是不能覆盖别人已经写的文件(执行查询生成的结果文件):

1 chmod 1777 /download
  • 增加一个ServletContextListener

主要用来启动文件服务器,实现类FileServerContextListener的代码如下所示:

01 package org.shirdrn.hadoop.hive.jetty;
02
03 import javax.servlet.ServletContextEvent;
04
05 import org.apache.commons.logging.Log;
06 import org.apache.commons.logging.LogFactory;
07
08 public class FileServerContextListener implements javax.servlet.ServletContextListener {
09
10 protected static final Log l4j = LogFactory.getLog(FileServerContextListener.class
11 .getName());
12 private JettyServer fileServer;
13
14 public void contextInitialized(ServletContextEvent sce) {
15 // start Jetty file server
16 l4j.info("Start Jetty file server...");
17 JettyServer server = new JettyFileServer();
18 server.start();
19 l4j.info("Jetty file server started!");
20 }
21
22 public void contextDestroyed(ServletContextEvent sce) {
23 // stop Jetty file server
24 fileServer.close();
25 l4j.info("Jetty file server stopped!");
26 }
27 }

然后,需要在web.xml文件中增加如下配置:

1 <listener>
2 <listener-class>org.shirdrn.hadoop.hive.jetty.FileServerContextListener</listener-class>
3 </listener>
  • 增加下载链接,设置DDL、DML操作限制

因为我们可能需要将HWI暴露给组织内部的其他项目团队使用,只允许他们查询,就应该限制Hive的DDL、DML操作,不运行他们建库建表、
这里,需要修改页面session_manage.jsp,修改后的内容,如下所示:

001 <%@page import="org.apache.hadoop.hive.hwi.*" %>
002 <%@page import="java.util.Arrays" %>
003 <%@page import="java.util.List" %>
004 <%@page errorPage="error_page.jsp" %>
005 <% HWISessionManager hs = (HWISessionManager) application.getAttribute("hs");; %>
006
007 <% HWIAuth auth = (HWIAuth) session.getAttribute("auth"); %>
008 <% if (auth==null) { %>
009 <jsp:forward page="/authorize.jsp" />
010 <% } %>
011 <% String sessionName=request.getParameter("sessionName"); %>
012 <% HWISessionItem sess = hs.findSessionItemByName(auth,sessionName); %>
013 <% String message=null; %>
014 <%
015 String randomFile = String.valueOf(System.currentTimeMillis()) + ".txt"; // 生成随机文件名称
016 String errorFile=request.getParameter("errorFile");
017 String resultFile=request.getParameter("resultFile");
018 resultFile = "/user/download/" + resultFile; // 结果文件存储的路径
019 String query = request.getParameter("query");
020 String silent = request.getParameter("silent");
021 String start = request.getParameter("start");
022
023 // 简单的HQL操作过滤,限制DDL、DML操作
024 String[] stoppedKeywords = new String[] {
025 "INSERT", "DELETE", "TRUNCATE",
026 "CREATE", "DROP", "ALTER",
027 "GRANT", "REVOKE", "LOAD"
028 };
029 List<String> list = Arrays.asList(stoppedKeywords);
030 if(query != null) {
031 String q = query.toUpperCase();
032 String[] a = q.split("\\s+");
033 for(String w : a) {
034 if(list.contains(w)) { // 如果包含上述关键词,直接抛出异常,限制执行操作
035 String err = "Permission denied! Excludes operations: " + list;
036 throw new HWIException(err);
037 }
038 }
039 }
040 %>
041 <%
042 if (request.getParameter("start")!=null ){
043 if ( sess.getStatus()==HWISessionItem.WebSessionItemStatus.READY){
044 sess.setErrorFile(errorFile);
045 sess.setResultFile(resultFile);
046 sess.clearQueries();
047
048 for (String q : query.split(";") ){
049 sess.addQuery(q);
050 }
051 if (query.length()==0){
052 message="You did not specify a query";
053 start="NO";
054 }
055 if (silent.equalsIgnoreCase("YES") )
056 sess.setSSIsSilent(true);
057 else
058 sess.setSSIsSilent(false);
059
060 message="Changes accepted.";
061 if (start.equalsIgnoreCase("YES") ){
062 sess.clientStart();
063 message="Session is set to start.";
064 }
065 }
066 }
067 %>
068 <!DOCTYPE html>
069 <html>
070 <head>
071 <title>Manage Session <%=sessionName%></title>
072 <link href="css/bootstrap.min.css" rel="stylesheet">
073 </head>
074 <body style="padding-top: 60px;">
075 <jsp:include page="/navbar.jsp"></jsp:include>
076 <div class="container">
077 <div class="row">
078 <div class="span4">
079 <jsp:include page="/left_navigation.jsp" />
080 </div><!-- span4 -->
081 <div class="span8">
082 <h2>
083 Manage Session
084 <%=sessionName%></h2>
085
086 <% if (message != null) { %>
087 <div class="alert alert-info"><%=message %></div>
088 <% } %>
089
090 <% if (sess.getStatus()==HWISessionItem.WebSessionItemStatus.QUERY_RUNNING) { %>
091 <div class="alert alert-warning">Session is in QUERY_RUNNING
092 state. Changes are not possible!</div>
093 <% } %>
094
095 <% if (sess.getStatus()==HWISessionItem.WebSessionItemStatus.QUERY_RUNNING){ %>
096 <%--
097 View JobTracker: <a href="<%= sess.getJobTrackerURI() %>">View Job</a><br>
098 Kill Command: <%= sess.getKillCommand() %>
099 Session Kill: <a href="/hwi/session_kill.jsp?sessionName=<%=sessionName%>"><%=sessionName%></a><br>
100 --%>
101 <% } %>
102
103 <div class="btn-group">
104 <a class="btn" href="/hwi/session_history.jsp?sessionName=<%=sessionName%>"><i class="icon-book"></i> History</a>
105 <a class="btn" href="/hwi/session_diagnostics.jsp?sessionName=<%=sessionName%>"><i class="icon-cog"></i> Diagnostics</a>
106 <a class="btn"href="/hwi/session_remove.jsp?sessionName=<%=sessionName%>"><i class="icon-remove"></i> Remove</a>
107 <a class="btn"href="/hwi/session_result.jsp?sessionName=<%=sessionName%>"><i class=" icon-download-alt"></i> Result Bucket</a>
108 </div>
109
110 <form action="session_manage.jsp" class="form-horizontal">
111 <input type="hidden" name="sessionName" value="<%=sessionName %>">
112
113 <fieldset>
114 <legend>Session Details </legend>
115 <div class="control-group">
116 <label class="control-label"for="fldresfile">Result File</label>
117 <div class="controls">
118 <input id="fldresfile" type="text"name="resultFile"
119 readonly value="<%=randomFile%>">
120 <% if (sess.getResultFile()!=null) { %>
121 <a href="/hwi/view_file.jsp?sessionName=<%=sessionName%>">View File</a>
122 <%
123 String[] a = sess.getResultFile().split("/");
124 String file = a[a.length - 1];
125 %>
126 <!-- 增加下载文件的链接 -->
127 <a target="_blank"href="http://192.168.1.105:9722/<%=file%>">Download File</a>
128 <% } %>
129 </div>
130 </div>
131
132 <div class="control-group">
133 <label class="control-label" for="flderrfile">Error File</label>
134 <div class="controls">
135 <input id="flderrfile" type="text"name="errorFile"
136 value="<%
137 if (sess.getErrorFile()==null) { out.print(""); } else { out.print(sess.getErrorFile()); }
138 %>">
139 </div>
140 </div>
141
142 <div class="control-group">
143 <label class="control-label"for="fldquery">Query</label>
144 <div class="controls">
145 <textarea id="fldquery" name="query" rows="8"cols="70">
146 <%
147 if (sess.getQueries()==null) {
148 out.print("");
149 } else {
150 for (String qu: sess.getQueries() ) {
151 out.print(qu); out.print(" ; ");
152 }
153 }
154 %>
155 </textarea>
156 </div>
157 </div>
158
159
160 <div class="control-group">
161 <label class="control-label" for="fldsilent">Silent Mode</label>
162 <div class="controls">
163 <select id="fldsilent" name="silent">
164 <option value="YES"
165 <% if (sess.getSSIsSilent()==true) { out.print("SELECTED=\"TRUE\""); } %>>YES</option>
166 <option value="NO"
167 <% if (sess.getSSIsSilent()==false) { out.print("SELECTED=\"TRUE\""); } %>>NO</option>
168 </select>
169 </div>
170 </div>
171
172 <div class="control-group">
173 <label class="control-label" for="fldstart">Start Query</label>
174 <div class="controls">
175 <select id="fldstart" name="start">
176 <option value="YES"SELECTED="TRUE">YES</option>
177 <option value="NO">NO</option>
178 </select>
179 </div>
180 </div>
181
182 </fieldset>
183
184 <h3>Query Return Codes</h3>
185 <p>
186 <% for (int i=0; i< sess.getQueryRet().size();++i ){ %>
187 <%=i%>
188 :
189 <%=sess.getQueryRet().get(i)%><br>
190 <% } %>
191 </p>
192
193 <% if (sess.getStatus()!=HWISessionItem.WebSessionItemStatus.QUERY_RUNNING) { %>
194 <div class="form-actions">
195 <button type="submit" class="btn btn-primary">Submit</button>
196 </div>
197
198 <% } %>
199 </form>
200 </div><!-- span8 -->
201 </div><!-- row -->
202 </div><!-- container -->
203 </body>
204 </html>

上面注释的地方,说明了修改的内容,可以查看。

HWI重新构建

我们只需要将我们自己实现的代码部分,打包到WAR文件里面就可以,因为默认的HWI的Java实现部分,可以在Hive的软件包中找到,可以查看${HIVE_HOME}/lib/hive-hwi-0.12.0.jar。上面我们改造过程中实现了一个Jetty文件服务器,编译完成后,需要删除默认目录${HWI_HOME}\target\WEB-INF\classes下面,不是我们改造后增加的类的类文件,然后就可以执行如下命令构建:

1 cd ${HWI_HOME}\target
2 jar cvf hwi.war *

然后,将生成的hwi文件,拷贝到部署Hive的对应目录${HIVE_HOME}/lib/下面即可,启动HWI服务:

1 hive --service hwi >> /tmp/hwi/hwi.log &

然后,就可以通过Web页面访问。

HWI使用

访问页面,例如我的是http://10.10.2.245:9999/hwi,然后,可以看到HWI首页,执行如下操作进行查询:

  1. 点击“Authorize”,填写“User”和“Groups”的内容,例如都是hadoop,然后点击提交按钮“Submit”;
  2. 点击“Create Session”,填写“Session name”,例如MYSESSION[hadoop],然后点击提交按钮“Submit”;
  3. 这时,进入到Manage Session MYSESSION[hadoop]页面,可以在“Query”中输入HQL查询语句,“Start Query”选择“YES”,然后击提交按钮“Submit”;
  4. 可以通过“View File”和“Result Bucket”查看结果内容;
  5. 可以通过我们改造后的“Download File”链接,进行查询结果文件的下载。
目录
相关文章
|
4天前
|
SQL HIVE
Hive的安装
Hive的安装
19 1
|
4天前
|
网络协议 网络虚拟化
网工配置命令总结(1)---Web访问及vlan配置
网工配置命令总结(1)---Web访问及vlan配置
5 0
|
4天前
|
SQL 分布式计算 关系型数据库
Ubuntu上安装MySQL以及hive
Ubuntu上安装MySQL以及hive
17 1
|
4天前
|
JSON Go 数据格式
golang学习7,glang的web的restful接口结构体传参
golang学习7,glang的web的restful接口结构体传参
|
4天前
|
JSON Go 数据格式
golang学习6,glang的web的restful接口传参
golang学习6,glang的web的restful接口传参
|
4天前
|
JSON Go 数据格式
golang学习5,glang的web的restful接口
golang学习5,glang的web的restful接口
|
4天前
|
Go
golang学习4,glang的web接口
golang学习4,glang的web接口
|
4天前
|
Go
golang学习3,golang 项目中配置gin的web框架
golang学习3,golang 项目中配置gin的web框架
|
4天前
|
开发框架 JSON .NET
.Net4.0 Web.config 配置实践
.Net4.0 Web.config 配置实践
|
4天前
|
SQL 存储 分布式计算
Hive详解、配置、数据结构、Hive CLI
Hive详解、配置、数据结构、Hive CLI
29 0
Hive详解、配置、数据结构、Hive CLI