tunnel server add pages about apps/agents, support embedded redis #1563

pull/1585/head^2
hengyunabc 4 years ago
parent 1eafe43b0a
commit 5c0a1c7c22

@ -70,6 +70,18 @@
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.7.3</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

@ -17,6 +17,13 @@ public class ArthasProperties {
private Server server;
private EmbeddedRedis embeddedRedis;
/**
* supoort apps.html/agents.html
*/
private boolean enableDetatilPages = false;
public Server getServer() {
return server;
}
@ -25,6 +32,22 @@ public class ArthasProperties {
this.server = server;
}
public EmbeddedRedis getEmbeddedRedis() {
return embeddedRedis;
}
public void setEmbeddedRedis(EmbeddedRedis embeddedRedis) {
this.embeddedRedis = embeddedRedis;
}
public boolean isEnableDetatilPages() {
return enableDetatilPages;
}
public void setEnableDetatilPages(boolean enableDetatilPages) {
this.enableDetatilPages = enableDetatilPages;
}
public static class Server {
/**
* tunnel server listen host
@ -81,4 +104,40 @@ public class ArthasProperties {
}
/**
* for test
*
* @author hengyunabc 2020-11-03
*
*/
public static class EmbeddedRedis {
private boolean enabled = false;
private String host = "127.0.0.1";
private int port = 6379;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
}

@ -0,0 +1,38 @@
package com.alibaba.arthas.tunnel.server.app.configuration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.arthas.tunnel.server.app.configuration.ArthasProperties.EmbeddedRedis;
import redis.embedded.RedisServer;
/**
*
* @author hengyunabc 2020-11-03
*
*/
@Configuration
@AutoConfigureBefore(TunnelClusterStoreConfiguration.class)
public class EmbeddedRedisConfiguration {
@Bean(initMethod = "start", destroyMethod = "stop")
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "arthas", name = { "embedded-redis.enabled" })
public RedisServer embeddedRedisServer(ArthasProperties arthasProperties) {
EmbeddedRedis embeddedRedis = arthasProperties.getEmbeddedRedis();
RedisServer redisServer = RedisServer.builder().port(embeddedRedis.getPort()).bind(embeddedRedis.getHost())
.build();
return redisServer;
}
public static void main(String[] args) {
RedisServer redisServer = new RedisServer();
redisServer.start();
}
}

@ -0,0 +1,91 @@
package com.alibaba.arthas.tunnel.server.app.web;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.arthas.tunnel.server.AgentClusterInfo;
import com.alibaba.arthas.tunnel.server.app.configuration.ArthasProperties;
import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore;
/**
*
* @author hengyunabc 2020-11-03
*
*/
@Controller
public class DetailAPIController {
private final static Logger logger = LoggerFactory.getLogger(DetailAPIController.class);
@Autowired
ArthasProperties arthasProperties;
@Autowired(required = false)
private TunnelClusterStore tunnelClusterStore;
@RequestMapping("/api/tunnelApps")
@ResponseBody
public Set<String> tunnelApps(HttpServletRequest request, Model model) {
if (!arthasProperties.isEnableDetatilPages()) {
throw new IllegalAccessError("not allow");
}
Set<String> result = new HashSet<String>();
if (tunnelClusterStore != null) {
Collection<String> agentIds = tunnelClusterStore.allAgentIds();
for (String id : agentIds) {
String appName = findAppNameFromAgentId(id);
if (appName != null) {
result.add(appName);
} else {
logger.warn("illegal agentId: " + id);
}
}
}
return result;
}
@RequestMapping("/api/tunnelAgentInfo")
@ResponseBody
public Map<String, AgentClusterInfo> tunnelAgentIds(@RequestParam(value = "app", required = true) String appName,
HttpServletRequest request, Model model) {
if (!arthasProperties.isEnableDetatilPages()) {
throw new IllegalAccessError("not allow");
}
if (tunnelClusterStore != null) {
Map<String, AgentClusterInfo> agentInfos = tunnelClusterStore.agentInfo(appName);
return agentInfos;
}
return Collections.emptyMap();
}
private static String findAppNameFromAgentId(String id) {
int index = id.indexOf('_');
if (index < 0 || index >= id.length()) {
return null;
}
return id.substring(0, index);
}
}

@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
*
* arthas agent
* @author hengyunabc 2019-09-24
*
*/

@ -7,4 +7,9 @@ arthas.server.port=7777
management.endpoints.web.exposure.include=*
# default user name
spring.security.user.name=arthas
spring.security.user.name=arthas
#arthas.enable-detatil-pages=true
#arthas.embedded-redis.enabled=true
#spring.redis.host=127.0.0.1

@ -0,0 +1,95 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://g.alicdn.com/code/lib/twitter-bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
crossorigin="anonymous">
<script src="https://g.alicdn.com/code/lib/vue/2.6.4/vue.min.js" integrity="sha256-isEQDc5Dw7wea1s5iMZjBvPuYzjzMrvtlPwE6LtavFA=" crossorigin="anonymous"></script>
<script src="https://g.alicdn.com/code/lib/axios/0.18.0/axios.min.js"></script>
<title>Arthas Tutorials</title>
<style>
/* This is all that's required */
.dropdown-item-checked::before {
position: absolute;
left: .4rem;
content: '✓';
font-weight: 600;
}
</style>
</head>
<body>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://g.alicdn.com/code/lib/jquery/3.3.1/jquery.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://g.alicdn.com/code/lib/popper.js/1.14.7/umd/popper.min.js" integrity="sha512-5WvZa4N7Jq3TVNCp4rjcBMlc6pT3lZ7gVxjtI6IkKW+uItSa+rFgtFljvZnCxQGj8SUX5DHraKE6Mn/4smK1Cg==" crossorigin="anonymous"></script>
<script src="https://g.alicdn.com/code/lib/twitter-bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
crossorigin="anonymous"></script>
<div id="app">
<table class="table table-hover issue-tracker">
<thead>
<tr>
<th>IP</th>
<th>AgentId</th>
<th>Version</th>
</tr>
</thead>
<tbody>
<tr v-for="(agentInfo, agentId) in agentInfos">
<td>
<a class="btn btn-primary btn-xs" v-bind:href="tunnelWebConsoleLink(agentId, agentInfo.clientConnectHost)">{{agentInfo.host}}</a>
</td>
<td>
<span class="label label-default">{{agentId}}</span>
</td>
<td>
<span class="label label-default">{{agentInfo.arthasVersion}}</span>
</td>
</tr>
</tbody>
</table>
</div>
</body>
<script>
var app = new Vue({
el: '#app',
data: {
agentInfos: [],
},
methods: {
fetchMyApps: function () {
var vm = this;
var params = new window.URLSearchParams(window.location.search);
var appName = params.get('app');
axios.get('/api/tunnelAgentInfo?app=' + appName)
.then(function (response) {
vm.agentInfos = response.data
})
.catch(function (error) {
console.log('api error ' + error)
})
},
tunnelWebConsoleLink: function (agentId, targetServer) {
return "/?targetServer=" + targetServer + "&agentId=" + agentId;
}
},
mounted: function() {
this.fetchMyApps()
}
})
</script>
</html>

@ -0,0 +1,85 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://g.alicdn.com/code/lib/twitter-bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
crossorigin="anonymous">
<script src="https://g.alicdn.com/code/lib/vue/2.6.4/vue.min.js" integrity="sha256-isEQDc5Dw7wea1s5iMZjBvPuYzjzMrvtlPwE6LtavFA=" crossorigin="anonymous"></script>
<script src="https://g.alicdn.com/code/lib/axios/0.18.0/axios.min.js"></script>
<title>Arthas Tutorials</title>
<style>
/* This is all that's required */
.dropdown-item-checked::before {
position: absolute;
left: .4rem;
content: '✓';
font-weight: 600;
}
</style>
</head>
<body>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://g.alicdn.com/code/lib/jquery/3.3.1/jquery.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://g.alicdn.com/code/lib/popper.js/1.14.7/umd/popper.min.js" integrity="sha512-5WvZa4N7Jq3TVNCp4rjcBMlc6pT3lZ7gVxjtI6IkKW+uItSa+rFgtFljvZnCxQGj8SUX5DHraKE6Mn/4smK1Cg==" crossorigin="anonymous"></script>
<script src="https://g.alicdn.com/code/lib/twitter-bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
crossorigin="anonymous"></script>
<div id="app">
<table class="table table-hover issue-tracker">
<thead>
<tr>
<th>应用名</th>
</tr>
</thead>
<tbody>
<tr v-for="myApp in myApps">
<td>
<a class="btn btn-primary btn-xs" v-bind:href="detailLink(myApp)">{{myApp}}</a>
</td>
</tr>
</tbody>
</table>
</div>
</body>
<script>
var app = new Vue({
el: '#app',
data: {
myApps: {},
},
methods: {
fetchMyApps: function () {
var vm = this
axios.get('/api/tunnelApps')
.then(function (response) {
vm.myApps = response.data
})
.catch(function (error) {
console.log('api error ' + error)
})
},
detailLink: function (appName) {
return "agents.html?app=" + appName;
}
},
mounted: function() {
this.fetchMyApps()
}
})
</script>
</html>
Loading…
Cancel
Save