Skip to content

Commit 38954ed

Browse files
GeorryHuangndimiduk
authored andcommitted
HBASE-23994: Add WebUI to Canary (#1292)
Signed-off-by: Duo Zhang <[email protected]> Signed-off-by: Nick Dimiduk <[email protected]>
1 parent cb60d23 commit 38954ed

File tree

6 files changed

+437
-2
lines changed

6 files changed

+437
-2
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
2+
<%doc>
3+
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing, software
15+
distributed under the License is distributed on an "AS IS" BASIS,
16+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
See the License for the specific language governing permissions and
18+
limitations under the License.
19+
</%doc>
20+
<%args>
21+
RegionStdOutSink sink;
22+
</%args>
23+
<%import>
24+
java.util.Map;
25+
java.util.concurrent.atomic.LongAdder;
26+
org.apache.hadoop.hbase.ServerName;
27+
org.apache.hadoop.hbase.tool.CanaryTool.RegionStdOutSink;
28+
</%import>
29+
30+
<!--[if IE]>
31+
<!DOCTYPE html>
32+
<![endif]-->
33+
<?xml version="1.0" encoding="UTF-8" ?>
34+
<html lang="en">
35+
<head>
36+
<meta charset="utf-8">
37+
<title>Canary</title>
38+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
39+
<meta name="description" content="">
40+
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
41+
<link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
42+
<link href="/static/css/hbase.css" rel="stylesheet">
43+
</head>
44+
45+
<body>
46+
47+
<div class="navbar navbar-fixed-top navbar-default">
48+
<div class="container">
49+
<div class="navbar-header">
50+
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
51+
<span class="icon-bar"></span>
52+
<span class="icon-bar"></span>
53+
<span class="icon-bar"></span>
54+
</button>
55+
<a class="navbar-brand" href="/canary-status"><img src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
56+
</div>
57+
</div>
58+
</div>
59+
60+
<div class="container">
61+
<section>
62+
<h2>Failed Servers</h2>
63+
<%java>
64+
Map<ServerName, LongAdder> perServerFailuresCount = sink.getPerServerFailuresCount();
65+
</%java>
66+
<table class="table table-striped">
67+
<tr>
68+
<th>Server</th>
69+
<th>Failures Count</th>
70+
</tr>
71+
<%if (perServerFailuresCount != null && perServerFailuresCount.size() > 0)%>
72+
<%for Map.Entry<ServerName, LongAdder> entry : perServerFailuresCount.entrySet() %>
73+
<tr>
74+
<td><& serverNameLink ; serverName = entry.getKey() &></td>
75+
<td><% entry.getValue() %></td>
76+
</tr>
77+
</%for>
78+
</%if>
79+
<tr><td>Total Failed Servers: <% (perServerFailuresCount != null) ? perServerFailuresCount.size() : 0 %></td></tr>
80+
</table>
81+
</section>
82+
<section>
83+
<h2>Failed Tables</h2>
84+
<%java>
85+
Map<String, LongAdder> perTableFailuresCount = sink.getPerTableFailuresCount();
86+
</%java>
87+
<table class="table table-striped">
88+
<tr>
89+
<th>Table</th>
90+
<th>Failures Count</th>
91+
</tr>
92+
<%if (perTableFailuresCount != null && perTableFailuresCount.size() > 0)%>
93+
<%for Map.Entry<String, LongAdder> entry : perTableFailuresCount.entrySet()%>
94+
<tr>
95+
<td><% entry.getKey() %></td>
96+
<td><% entry.getValue() %></td>
97+
</tr>
98+
</%for>
99+
</%if>
100+
<tr><td>Total Failed Tables: <% (perTableFailuresCount != null) ? perTableFailuresCount.size() : 0 %></td></tr>
101+
</table>
102+
</section>
103+
104+
<section>
105+
<h2>Software Attributes</h2>
106+
<table id="attributes_table" class="table table-striped">
107+
<tr>
108+
<th>Attribute Name</th>
109+
<th>Value</th>
110+
<th>Description</th>
111+
</tr>
112+
<tr>
113+
<td>HBase Version</td>
114+
<td><% org.apache.hadoop.hbase.util.VersionInfo.getVersion() %>, r<% org.apache.hadoop.hbase.util.VersionInfo.getRevision() %></td><td>HBase version and revision</td>
115+
</tr>
116+
<tr>
117+
<td>HBase Compiled</td>
118+
<td><% org.apache.hadoop.hbase.util.VersionInfo.getDate() %>, <% org.apache.hadoop.hbase.util.VersionInfo.getUser() %></td>
119+
<td>When HBase version was compiled and by whom</td>
120+
</tr>
121+
<tr>
122+
<td>Hadoop Version</td>
123+
<td><% org.apache.hadoop.util.VersionInfo.getVersion() %>, r<% org.apache.hadoop.util.VersionInfo.getRevision() %></td>
124+
<td>Hadoop version and revision</td>
125+
</tr>
126+
<tr>
127+
<td>Hadoop Compiled</td>
128+
<td><% org.apache.hadoop.util.VersionInfo.getDate() %>, <% org.apache.hadoop.util.VersionInfo.getUser() %></td>
129+
<td>When Hadoop version was compiled and by whom</td>
130+
</tr>
131+
</table>
132+
</section>
133+
</div>
134+
</div> <!-- /container -->
135+
136+
<script src="/static/js/jquery.min.js" type="text/javascript"></script>
137+
<script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
138+
<script src="/static/js/tab.js" type="text/javascript"></script>
139+
</body>
140+
</html>
141+
142+
<%def serverNameLink>
143+
<%args>
144+
ServerName serverName;
145+
</%args>
146+
<%java>
147+
int infoPort = serverName.getPort() + 1;
148+
String url = "//" + serverName.getHostname() + ":" + infoPort + "/";
149+
</%java>
150+
151+
<%if (infoPort > 0) %>
152+
<a href="<% url %>"><% serverName.getServerName() %></a>
153+
<%else>
154+
<% serverName.getServerName() %>
155+
</%if>
156+
</%def>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
*
3+
* Licensed to the Apache Software Foundation (ASF) under one
4+
* or more contributor license agreements. See the NOTICE file
5+
* distributed with this work for additional information
6+
* regarding copyright ownership. The ASF licenses this file
7+
* to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance
9+
* with the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.apache.hadoop.hbase.tool;
20+
21+
import java.io.IOException;
22+
import javax.servlet.ServletException;
23+
import javax.servlet.http.HttpServlet;
24+
import javax.servlet.http.HttpServletRequest;
25+
import javax.servlet.http.HttpServletResponse;
26+
import org.apache.hadoop.hbase.tmpl.tool.CanaryStatusTmpl;
27+
import org.apache.yetus.audience.InterfaceAudience;
28+
29+
30+
@InterfaceAudience.Private
31+
public class CanaryStatusServlet extends HttpServlet {
32+
@Override
33+
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
34+
throws ServletException, IOException {
35+
CanaryTool.RegionStdOutSink sink =
36+
(CanaryTool.RegionStdOutSink) getServletContext().getAttribute(
37+
"sink");
38+
if (sink == null) {
39+
throw new ServletException(
40+
"RegionStdOutSink is null! The CanaryTool's InfoServer is not initialized correctly");
41+
}
42+
43+
resp.setContentType("text/html");
44+
45+
CanaryStatusTmpl tmpl = new CanaryStatusTmpl();
46+
tmpl.render(resp.getWriter(), sink);
47+
}
48+
49+
}

hbase-server/src/main/java/org/apache/hadoop/hbase/tool/CanaryTool.java

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import java.io.Closeable;
2626
import java.io.IOException;
27+
import java.net.BindException;
2728
import java.net.InetSocketAddress;
2829
import java.util.ArrayList;
2930
import java.util.Arrays;
@@ -39,6 +40,7 @@
3940
import java.util.TreeSet;
4041
import java.util.concurrent.Callable;
4142
import java.util.concurrent.ConcurrentHashMap;
43+
import java.util.concurrent.ConcurrentMap;
4244
import java.util.concurrent.ExecutionException;
4345
import java.util.concurrent.ExecutorService;
4446
import java.util.concurrent.Future;
@@ -81,6 +83,7 @@
8183
import org.apache.hadoop.hbase.client.TableDescriptor;
8284
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
8385
import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
86+
import org.apache.hadoop.hbase.http.InfoServer;
8487
import org.apache.hadoop.hbase.tool.CanaryTool.RegionTask.TaskType;
8588
import org.apache.hadoop.hbase.util.Bytes;
8689
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
@@ -122,6 +125,37 @@
122125
*/
123126
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
124127
public class CanaryTool implements Tool, Canary {
128+
public static final String HBASE_CANARY_INFO_PORT = "hbase.canary.info.port";
129+
130+
public static final int DEFAULT_CANARY_INFOPORT = 16050;
131+
132+
public static final String HBASE_CANARY_INFO_BINDADDRESS = "hbase.canary.info.bindAddress";
133+
134+
private InfoServer infoServer;
135+
136+
private void putUpWebUI() throws IOException {
137+
int port = conf.getInt(HBASE_CANARY_INFO_PORT, DEFAULT_CANARY_INFOPORT);
138+
// -1 is for disabling info server
139+
if (port < 0) {
140+
return;
141+
}
142+
if (zookeeperMode) {
143+
LOG.info("WebUI is not supported in Zookeeper mode");
144+
} else if (regionServerMode) {
145+
LOG.info("WebUI is not supported in RegionServer mode");
146+
} else {
147+
String addr = conf.get(HBASE_CANARY_INFO_BINDADDRESS, "0.0.0.0");
148+
try {
149+
infoServer = new InfoServer("canary", addr, port, false, conf);
150+
infoServer.addUnprivilegedServlet("canary", "/canary-status", CanaryStatusServlet.class);
151+
infoServer.setAttribute("sink", this.sink);
152+
infoServer.start();
153+
LOG.info("Bind Canary http info server to {}:{} ", addr, port);
154+
} catch (BindException e) {
155+
LOG.warn("Failed binding Canary http info server to {}:{}", addr, port, e);
156+
}
157+
}
158+
}
125159

126160
@Override
127161
public int checkRegions(String[] targets) throws Exception {
@@ -273,17 +307,53 @@ public void publishReadTiming(String znode, String server, long msTime) {
273307
public static class RegionStdOutSink extends StdOutSink {
274308
private Map<String, LongAdder> perTableReadLatency = new HashMap<>();
275309
private LongAdder writeLatency = new LongAdder();
276-
private final Map<String, List<RegionTaskResult>> regionMap = new ConcurrentHashMap<>();
310+
private final ConcurrentMap<String, List<RegionTaskResult>> regionMap =
311+
new ConcurrentHashMap<>();
312+
private ConcurrentMap<ServerName, LongAdder> perServerFailuresCount =
313+
new ConcurrentHashMap<>();
314+
private ConcurrentMap<String, LongAdder> perTableFailuresCount = new ConcurrentHashMap<>();
315+
316+
public ConcurrentMap<ServerName, LongAdder> getPerServerFailuresCount() {
317+
return perServerFailuresCount;
318+
}
319+
320+
public ConcurrentMap<String, LongAdder> getPerTableFailuresCount() {
321+
return perTableFailuresCount;
322+
}
323+
324+
public void resetFailuresCountDetails() {
325+
perServerFailuresCount.clear();
326+
perTableFailuresCount.clear();
327+
}
328+
329+
private void incFailuresCountDetails(ServerName serverName, RegionInfo region) {
330+
perServerFailuresCount.compute(serverName, (server, count) -> {
331+
if (count == null) {
332+
count = new LongAdder();
333+
}
334+
count.increment();
335+
return count;
336+
});
337+
perTableFailuresCount.compute(region.getTable().getNameAsString(), (tableName, count) -> {
338+
if (count == null) {
339+
count = new LongAdder();
340+
}
341+
count.increment();
342+
return count;
343+
});
344+
}
277345

278346
public void publishReadFailure(ServerName serverName, RegionInfo region, Exception e) {
279347
incReadFailureCount();
348+
incFailuresCountDetails(serverName, region);
280349
LOG.error("Read from {} on serverName={} failed",
281350
region.getRegionNameAsString(), serverName, e);
282351
}
283352

284353
public void publishReadFailure(ServerName serverName, RegionInfo region,
285354
ColumnFamilyDescriptor column, Exception e) {
286355
incReadFailureCount();
356+
incFailuresCountDetails(serverName, region);
287357
LOG.error("Read from {} on serverName={}, columnFamily={} failed",
288358
region.getRegionNameAsString(), serverName,
289359
column.getNameAsString(), e);
@@ -304,12 +374,14 @@ public void publishReadTiming(ServerName serverName, RegionInfo region,
304374

305375
public void publishWriteFailure(ServerName serverName, RegionInfo region, Exception e) {
306376
incWriteFailureCount();
377+
incFailuresCountDetails(serverName, region);
307378
LOG.error("Write to {} on {} failed", region.getRegionNameAsString(), serverName, e);
308379
}
309380

310381
public void publishWriteFailure(ServerName serverName, RegionInfo region,
311382
ColumnFamilyDescriptor column, Exception e) {
312383
incWriteFailureCount();
384+
incFailuresCountDetails(serverName, region);
313385
LOG.error("Write to {} on {} {} failed", region.getRegionNameAsString(), serverName,
314386
column.getNameAsString(), e);
315387
}
@@ -345,7 +417,7 @@ public LongAdder getWriteLatency() {
345417
return this.writeLatency;
346418
}
347419

348-
public Map<String, List<RegionTaskResult>> getRegionMap() {
420+
public ConcurrentMap<String, List<RegionTaskResult>> getRegionMap() {
349421
return this.regionMap;
350422
}
351423

@@ -908,6 +980,7 @@ public int run(String[] args) throws Exception {
908980
System.arraycopy(args, index, monitorTargets, 0, length);
909981
}
910982

983+
putUpWebUI();
911984
if (zookeeperMode) {
912985
return checkZooKeeper();
913986
} else if (regionServerMode) {
@@ -1352,6 +1425,7 @@ public void run() {
13521425
try {
13531426
List<Future<Void>> taskFutures = new LinkedList<>();
13541427
RegionStdOutSink regionSink = this.getSink();
1428+
regionSink.resetFailuresCountDetails();
13551429
if (this.targets != null && this.targets.length > 0) {
13561430
String[] tables = generateMonitorTables(this.targets);
13571431
// Check to see that each table name passed in the -readTableTimeouts argument is also
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<%--
2+
/**
3+
* Licensed to the Apache Software Foundation (ASF) under one
4+
* or more contributor license agreements. See the NOTICE file
5+
* distributed with this work for additional information
6+
* regarding copyright ownership. The ASF licenses this file
7+
* to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance
9+
* with the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
--%>
20+
<meta HTTP-EQUIV="REFRESH" content="0;url=/canary-status"/>

0 commit comments

Comments
 (0)