服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - SpringBoot Admin集成诊断利器Arthas示例实现

SpringBoot Admin集成诊断利器Arthas示例实现

2023-02-20 15:32横云断岭 Java教程

这篇文章主要为大家介绍了SpringBoot Admin集成诊断利器Arthas示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

Arthas 是 Alibaba开源的Java诊断工具,具有实时查看系统的运行状况,查看函数调用参数、返回值和异常,在线热更新代码,秒解决类冲突问题、定位类加载路径,生成热点图,通过网页诊断线上应用。 如今在各大厂都有广泛应用,也延伸出很多产品。

这里将介绍如何将Arthas集成进SpringBoot监控平台中。

 

SpringBoot Admin

为了方便SpringBoot Admin 简称为SBA

版本:1.5.x

1.5版本的SBA如果要开发插件比较麻烦,需要下载SBA的源码包,再按照spring-boot-admin-server-ui-hystrix的形式copy一份,由于JS使用的是Angular,本人放弃了

SpringBoot Admin集成诊断利器Arthas示例实现

版本:2.x

2.x版本的SBA插件开发,官网有介绍如何开发,JS使用Vue,方便很多,由于我们项目还在使用1.5,所以并没有使用该版本,请读者自行尝试

不能使用SBA的插件进行集成,那还有什么办法呢?????

 

SBA 集成

鄙人的办法是将Arthas的相关文件直接copy到admin服务中

SpringBoot Admin集成诊断利器Arthas示例实现

arthas包该包下存放的是所有arthas的Java文件

  • endpoint包下的文件可以都注释掉,没多大用
  • ArthasController这个文件是我自己新建的,用来获取所有注册到Arthas的客户端,这在后面是有用的
  • 其他文件直接copy过来就行
@RequestMapping("/api/arthas")
@RestController
public class ArthasController {
@Autowired
private TunnelServer tunnelServer;
@RequestMapping(value = "/clients", method = RequestMethod.GET)
public Set<String> getClients() {
Map<String, AgentInfo> agentInfoMap = tunnelServer.getAgentInfoMap();
return agentInfoMap.keySet();
}
}

spring-boot-admin-server-ui

该文件建在resources.META-INF下,admin会在启动的时候加载该目录下的文件

SpringBoot Admin集成诊断利器Arthas示例实现

index.html 覆盖SBA原来的首页,在其中添加一个导航,首页会是这样

SpringBoot Admin集成诊断利器Arthas示例实现

<!DOCTYPE html>
<html class="no-js">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Spring Boot Admin</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width">
  <link rel="shortcut icon" type="image/x-icon" href="img/favicon.png" rel="external nofollow" />
  <link rel="stylesheet" type="text/css" href="core.css" rel="external nofollow" />
  <link rel="stylesheet" type="text/css" href="all-modules.css" rel="external nofollow" />
</head>
<body>
<header class="navbar header--navbar desktop-only">
  <div class="navbar-inner">
      <div class="container-fluid">
          <div class="spring-logo--container">
              <a class="spring-logo" href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" ><span></span></a>
          </div>
          <div class="spring-logo--container">
              <a class="spring-boot-logo" href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" ><span></span></a>
          </div>
          <ul class="nav pull-right">
            <!--增加Arthas导航-->
              <li class="navbar-link ng-scope">
                  <a  class="ng-binding" href="arthas/arthas.html" rel="external nofollow" >Arthas</a>
              </li>
              <li ng-repeat="view in mainViews" class="navbar-link" ng-class="{active: $state.includes(view.state)}">
                  <a ui-sref="{{view.state}}" ng-bind-html="view.title"></a>
              </li>
          </ul>
      </div>
  </div>
</header>
<div ui-view></div>
<footer class="footer">
  <ul class="inline">
      <li><a href="https://codecentric.github.io/spring-boot-admin/@project.version@" rel="external nofollow"  target="_blank">Reference
          Guide</a></li>
      <li>-</li>
      <li><a href="https://github.com/codecentric/spring-boot-admin" rel="external nofollow"  target="_blank">Sources</a></li>
      <li>-</li>
      <li>Code licensed under <a href="http://www.apache.org/licenses/LICENSE-2.0" rel="external nofollow"  target="_blank">Apache License
          2.0</a></li>
  </ul>
</footer>
<script src="dependencies.js" type="text/javascript"></script>
<script type="text/javascript">
sbaModules = [];
</script>
<script src="core.js" type="text/javascript"></script>
<script src="all-modules.js" type="text/javascript"></script>
<script type="text/javascript">
angular.element(document).ready(function () {
  angular.bootstrap(document, sbaModules.slice(0), {
    strictDi: true
  });
});
</script>
</body>
</html>

arthas.html 新建页面,用于显示arthas控制台页面

<!DOCTYPE html>
<html class="no-js">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Spring Boot Admin</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width">
  <link rel="shortcut icon" type="image/x-icon" href="../img/favicon.png" rel="external nofollow" />
  <link rel="stylesheet" type="text/css" href="../core.css" rel="external nofollow" />
  <link rel="stylesheet" type="text/css" href="../all-modules.css" rel="external nofollow" />
  <script src="js/jquery-3.3.1.min.js"></script>
  <script src="js/popper-1.14.6.min.js"></script>
  <script src="js/xterm.js"></script>
  <script src="js/web-console.js"></script>
  <script src="js/arthas.js"></script>
  <link href="js/xterm.css" rel="external nofollow"  rel="stylesheet" />
  <script type="text/javascript">
      window.addEventListener('resize', function () {
          var terminalSize = getTerminalSize();
          ws.send(JSON.stringify({ action: 'resize', cols: terminalSize.cols, rows: terminalSize.rows }));
          xterm.resize(terminalSize.cols, terminalSize.rows);
      });
  </script>
</head>
<body>
<header class="navbar header--navbar desktop-only">
  <div class="navbar-inner">
      <div class="container-fluid">
          <div class="spring-logo--container">
              <a class="spring-logo" href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" ><span></span></a>
          </div>
          <div class="spring-logo--container">
              <a class="spring-boot-logo" href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" ><span></span></a>
          </div>
          <ul class="nav pull-right">
              <li class="navbar-link ng-scope">
                  <a  class="ng-binding" href="arthas.html" rel="external nofollow" >Arthas</a>
              </li>
              <li class="navbar-link ng-scope">
                  <a  class="ng-binding" href="../" rel="external nofollow" >Applications</a>
              </li>
              <li class="navbar-link ng-scope">
                  <a  class="ng-binding" href="../#/turbine" rel="external nofollow" >Turbine</a>
              </li>
              <li class="navbar-link ng-scope">
                  <a  class="ng-binding" href="../#/events" rel="external nofollow" >Journal</a>
              </li>
              <li class="navbar-link ng-scope">
                  <a  class="ng-binding" href="../#/about" rel="external nofollow" >About</a>
              </li>
              <li class="navbar-link ng-scope">
                  <a  class="ng-binding" href="../#/logout" rel="external nofollow" ><i class="fa fa-2x fa-sign-out" aria-hidden="true"></i></a>
              </li>
          </ul>
      </div>
  </div>
</header>
<div ui-view>
  <div class="container-fluid">
      <form class="form-inline">
          <input type="hidden" id="ip" name="ip" value="127.0.0.1">
          <input type="hidden" id="port" name="port" value="19898">
          Select Application:
          <select id="selectServer"></select>
          <button class="btn" onclick="startConnect()" type="button"><i class="fa fa-connectdevelop"></i> Connect</button>
          <button class="btn" onclick="disconnect()" type="button"><i class="fa fa-search-minus"></i> Disconnect</button>
          <button class="btn" onclick="release()" type="button"><i class="fa fa-search-minus"></i> Release</button>
      </form>
      <div id="terminal-card">
          <div id="terminal"></div>
      </div>
  </div>
</div>
</body>
</html>

arthas.js 存储页面控制的js

var registerApplications = null;
var applications = null;
$(document).ready(function () {
  reloadRegisterApplications();
  reloadApplications();
});
/**
* 获取注册的arthas客户端
*/
function reloadRegisterApplications() {
  var result = reqSync("/api/arthas/clients", "get");
  registerApplications = result;
  initSelect("#selectServer", registerApplications, "");
}
/**
* 获取注册的应用
*/
function reloadApplications() {
  applications = reqSync("/api/applications", "get");
  console.log(applications)
}
/**
* 初始化下拉选择框
*/
function initSelect(uiSelect, list, key) {
  $(uiSelect).html('');
  var server;
  for (var i = 0; i < list.length; i++) {
      server = list[i].toLowerCase().split("@");
      if ("phantom-admin" === server[0]) continue;
      $(uiSelect).append("<option value=" + list[i].toLowerCase() + ">" + server[0] + "</option>");
  }
}
/**
* 重置配置文件
*/
function release() {
  var currentServer = $("#selectServer").text();
  for (var i = 0; i < applications.length; i++) {
      serverId = applications[i].id;
      serverName = applications[i].name.toLowerCase();
      console.log(serverId + "/" + serverName);
      if (currentServer === serverName) {
          var result = reqSync("/api/applications/" +serverId+ "/env/reset", "post");
          alert("env reset success");
      }
  }
}
function reqSync(url, method) {
  var result = null;
  $.ajax({
      url: url,
      type: method,
      async: false, //使用同步的方式,true为异步方式
      headers: {
          'Content-Type': 'application/json;charset=utf8;',
      },
      success: function (data) {
          // console.log(data);
          result = data;
      },
      error: function (data) {
          console.log("error");
      }
  });
  return result;
}
  • 其他文件 jquery-3.3.1.min.js 新加Js

copy过来的js

popper-1.14.6.min.js

web-console.js

xterm.css

xterm.js

  • bootstrap.yml
# arthas端口
arthas:
server:
  port: 9898

这样子,admin端的配置完成了

 

客户端配置

在配置中心加入配置

#arthas服务端域名
arthas.tunnel-server = ws://admin域名/ws
#客户端id,应用名@随机值,js会截取前面的应用名
arthas.agent-id = ${spring.application.name}@${random.value}
#arthas开关,可以在需要调试的时候开启,不需要的时候关闭
spring.arthas.enabled = false

需要自动Attach的应用中引入arthas-spring-boot-starter 需要对starter进行部分修改,要将注册arthas的部分移除,下面是修改后的文件。

我这里是将修改后的文件重新打包成jar包,上传到私服,但有些应用会有无法加载arthasConfigMap的情况,可以将这两个文件单独放到项目的公共包中

@EnableConfigurationProperties({ ArthasProperties.class })
public class ArthasConfiguration {
private static final Logger logger = LoggerFactory.getLogger(ArthasConfiguration.class);
@ConfigurationProperties(prefix = "arthas")
@ConditionalOnMissingBean
@Bean
public HashMap<String, String> arthasConfigMap() {
return new HashMap<String, String>();
}
}
@ConfigurationProperties(prefix = "arthas")
public class ArthasProperties {
private String ip;
private int telnetPort;
private int httpPort;
private String tunnelServer;
private String agentId;
/**
* report executed command
*/
private String statUrl;
/**
* session timeout seconds
*/
private long sessionTimeout;
private String home;
/**
* when arthas agent init error will throw exception by default.
*/
private boolean slientInit = false;
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
public boolean isSlientInit() {
return slientInit;
}
public void setSlientInit(boolean slientInit) {
this.slientInit = slientInit;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getTelnetPort() {
return telnetPort;
}
public void setTelnetPort(int telnetPort) {
this.telnetPort = telnetPort;
}
public int getHttpPort() {
return httpPort;
}
public void setHttpPort(int httpPort) {
this.httpPort = httpPort;
}
public String getTunnelServer() {
return tunnelServer;
}
public void setTunnelServer(String tunnelServer) {
this.tunnelServer = tunnelServer;
}
public String getAgentId() {
return agentId;
}
public void setAgentId(String agentId) {
this.agentId = agentId;
}
public String getStatUrl() {
return statUrl;
}
public void setStatUrl(String statUrl) {
this.statUrl = statUrl;
}
public long getSessionTimeout() {
return sessionTimeout;
}
public void setSessionTimeout(long sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
}

实现开关效果

为了实现开关效果,还需要一个文件用来监听配置文件的改变
我这里使用的是在SBA中改变环境变量,对应服务监听到变量改变,当监听spring.arthas.enabled为true的时候,注册arthas, 到下面是代码

@Component
public class EnvironmentChangeListener implements ApplicationListener<EnvironmentChangeEvent> {
  @Autowired
  private Environment env;
  @Autowired
  private Map<String, String> arthasConfigMap;
  @Autowired
  private ArthasProperties arthasProperties;
  @Autowired
  private ApplicationContext applicationContext;
  @Override
  public void onApplicationEvent(EnvironmentChangeEvent event) {
      Set<String> keys = event.getKeys();
      for (String key : keys) {
          if ("spring.arthas.enabled".equals(key)) {
              if ("true".equals(env.getProperty(key))) {
                  registerArthas();
              }
          }
      }
  }
  private void registerArthas() {
      DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
      String bean = "arthasAgent";
      if (defaultListableBeanFactory.containsBean(bean)) {
          ((ArthasAgent)defaultListableBeanFactory.getBean(bean)).init();
          return;
      }
      defaultListableBeanFactory.registerSingleton(bean, arthasAgentInit());
  }
  private ArthasAgent arthasAgentInit() {
      arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap);
      // 给配置全加上前缀
      Map<String, String> mapWithPrefix = new HashMap<String, String>(arthasConfigMap.size());
      for (Map.Entry<String, String> entry : arthasConfigMap.entrySet()) {
          mapWithPrefix.put("arthas." + entry.getKey(), entry.getValue());
      }
      final ArthasAgent arthasAgent = new ArthasAgent(mapWithPrefix, arthasProperties.getHome(),
              arthasProperties.isSlientInit(), null);
      arthasAgent.init();
      return arthasAgent;
  }
}

 

结束

到此可以愉快的在SBA中调试应用了,看看最后的页面

SpringBoot Admin集成诊断利器Arthas示例实现

调试流程

开启Arthas

SpringBoot Admin集成诊断利器Arthas示例实现

在Select Application中选择应用

  • Connect 连接应用
  • DisConnect 断开应用
  • Release 释放配置文件

这种集成方式有一些缺陷:

  • 使用jar包的方式引入应用,具有一定的侵略性,如果arthas无法启动,会导致应用也无法启动
  • 如果使用docker,需要适当调整JVM内存,防止开启arthas的时候,内存炸了
  • 没有使用SBA插件的方式集成
  • 如上集成仅供参考,还是需要根据自己企业的情况来做

以上就是SpringBoot Admin集成诊断利器Arthas示例实现的详细内容,更多关于SpringBoot Admin集成Arthas的资料请关注服务器之家其它相关文章!

原文链接:https://blog.csdn.net/hengyunabc/article/details/110604991

延伸 · 阅读

精彩推荐