Springboot Admin(SBA) + Nacos + Arthas 搭建你的在线性能分析和问题定位工具-服务端改造篇

本文背景介绍:

arthas有大牛提到sba 和arthas的集成,没有源码,自己磕磕绊绊,东拼西凑,打通任督二脉后,留下此文,一来做知识沉淀,二来分析给有需要的人
Arthas官方文档
参考博文1
参考博文2

环境和使用相关版本

SpringBoot Admin 2.3.1
Athas 3.4.5
Nacos 2.2.1.RELEASE(nacos注册&配置中心百度搜索搭建)

SBA + Arthas服务端集成

SBA 服务搭建

  1. pom.xml文件
<dependencies>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-server</artifactId>
      <version>2.3.1</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!--    <dependency>-->
    <!--      <groupId>org.springframework.boot</groupId>-->
    <!--      <artifactId>spring-boot-starter-mail</artifactId>-->
    <!--    </dependency>-->

    <dependency>
      <groupId>org.jolokia</groupId>
      <artifactId>jolokia-core</artifactId>
    </dependency>

    <!-- arthas 集成需要 -->
    <dependency>
      <groupId>com.taobao.arthas</groupId>
      <artifactId>arthas-common</artifactId>
      <version>${arthas.version}</version>
    </dependency>
    <dependency>
      <groupId>com.taobao.arthas</groupId>
      <artifactId>arthas-tunnel-common</artifactId>
      <version>${arthas.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
      <groupId>com.github.ben-manes.caffeine</groupId>
      <artifactId>caffeine</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</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>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
      <scope>runtime</scope>
    </dependency>

  </dependencies>

<build>
    <finalName>${project.artifactId}</finalName>
    <resources>
      <!-- 指定 src/main/resources下所有文件及文件夹为资源文件 -->
      <resource>
        <directory>src/main/resources</directory>
        <targetPath>${project.build.directory}/classes</targetPath>
        <includes>
          <include>**/*</include>
        </includes>
        <filtering>true</filtering>
      </resource>
      <!-- 通过 Maven Resource 的指定配置打入指定目录,实现 SBA 启动时的自定义加载 ,通过application配置 外链-->
      <resource>
        <directory>static</directory>
        <targetPath>${project.build.directory}/classes/META-INF/spring-boot-admin-server-ui/extensions/arthas
        </targetPath>
        <filtering>false</filtering>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <!--fork :  如果没有该项配置,肯呢个devtools不会起作用,即应用不会restart   这个要手动加进去 -->
          <fork>true</fork>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  1. bootstrap.yml
server:
  port: 7900

spring:
  main:
    ## 解决 xxx.FeignClientSpecification异常
    allow-bean-definition-overriding: true
  application:
    name: xiaogj-ms-admin-server
  profiles:
    active: ${SPRING_PROFILES:dev}
  cloud:
    nacos:
      config:
        file-extension: yml
        namespace: ${NACOS_NAMESPACE:group_name}
      username: ${NACOS_USERNAME:nacos}
      password: ${NACOS_PASSWORD:nacos}
      server-addr: ${NACOS_SERVER_ADDR:your nacos url}
      discovery:
        namespace: ${NACOS_NAMESPACE:group_name}


  1. application.yml配置
    主要用于配置arthas相关配置和admin自定义的页面
# 监控监控
management:
  endpoints:
    web:
      exposure:
        include: '*'
  metrics:
    tags:
      application: ${spring.application.name}
  ## 关闭rabbitmq 健康检查
  health:
    redis:
      enabled: false
    rabbit:
      enabled: false
    elasticsearch:
      enabled: false
  endpoint:
    health:
      show-details: ALWAYS

arthas:
  server:
    host: 0.0.0.0
    port: 7901
  enable-detatil-pages: true
  # redis模式缓存
  #embedded-redis:
    #enabled: true
    #settings: maxmemory 128M

spring:
  boot:
    # /META-INF/spring-boot-admin-server-ui/
    admin:
      ui:
        # 自定义网页header,默认值assets/img/icon-spring-boot-admin.svg,自定义地址
        #brand: <img src="custom/custom-icon.png">
        # 自定义logo图标,默认路径/META-INF/spring-boot-admin-server-ui/assets/img/
        #login-icon: assets/img/custom-login-icon.svg
        # 外链或扩展页面
        external-views:
          - label: "Arthas Console"
            url: ./extensions/arthas/arthas.html
            order: 1900
  #security:
    #user:
      #name: "admin"
      #password: "admin123"
  # caffeine缓存配置
  cache:
    type: caffeine
    cache-names: inMemoryClusterCache
    caffeine:
      spec: maximumSize=3000,expireAfterAccess=3600s
  #mail:
    #host: smtp.163.com
    #username: xiaolinlin
    #password:
  #boot:
    #admin:
      #notify:
        #mail:
          #to: 84226733@qq.com

SBA项目的搭建不多描述,仅描述核心部分

Arthas后端集成

  1. 参考博文大神文章,直接将tunnel-server的项目的代码拷贝到sba工程,工程目录结构如下
    Arthas服务端代码部分

  2. 新增一个ArthasController,主要用于前端获取所有注册的arthas 的客户端

import com.xiaogj.ms.admin.server.arthas.AgentInfo;
import com.xiaogj.ms.admin.server.arthas.TunnelServer;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * 获取所有注册到 Arthas 的客户端 <br>
 *
 * @date: 2021/8/17 <br>
 * @author: llxiao <br>
 * @since: 1.0 <br>
 * @version: 1.0 <br>
 */
@RequestMapping("/api/arthas")
@RestController
public class AthasController {

    @Autowired
    private TunnelServer tunnelServer;

    @RequestMapping(value = "/clients", method = RequestMethod.GET)
    public Set<String> getClients() {
        Map<String, AgentInfo> agentInfoMap = tunnelServer.getAgentInfoMap();
        return agentInfoMap.keySet();
    }


}
  1. 注释掉ArthasTunnelApplication类,通过sba的主类启动,注意该类上的一些注解使用

Arthas前端集成

  1. src目录同级创建static文件夹,然后拷贝tunnel-server项目的所有静态文件,格式如下
    前端改造
    针对图的1-4点分章节描述和贴源码
  2. arthas.html,主要在参考博文1的基础上微调
    该文件我这里其实是直接拷贝原来的index.html文件,仅做了部分调整,改动地方如下:
    新增引入js文件
    HTML修改部分
    源码如下:
<!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="bootstrap-4.2.1.min.css">
    <link rel="stylesheet" href="bootstrap-select.min.css" rel="stylesheet">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <link href="xterm.css" rel="stylesheet" />
    <link href="main.css" rel="stylesheet" />

    <script src="jquery-3.3.1.min.js"></script>
    <script src="popper-1.14.6.min.js"></script>
    <script src="bootstrap-4.2.1.min.js"></script>
    <script src="xterm.js"></script>
    <script src="web-console.js"></script>
    <script src="arthas.js"></script>
    <script src="bootstrap-select.min.js"></script>



    <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>

    <title>Arthas Console</title>
</head>

<body>
    <nav class="navbar navbar-expand navbar-light bg-light flex-column flex-md-row bd-navbar">
        <a href="https://github.com/alibaba/arthas" target="_blank" title="" class="navbar-brand"><img src="logo.png"
                alt="Arthas" title="Welcome to Arthas web console" style="height: 25px;" class="img-responsive"></a>

        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="https://arthas.aliyun.com/doc" target="_blank">Documentation
                        <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="https://arthas.aliyun.com/doc/arthas-tutorials.html" target="_blank">Online Tutorials</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="https://github.com/alibaba/arthas" target="_blank">Github</a>
                </li>
            </ul>
        </div>

        <form class="form-inline my-2 my-lg-0">
            <div class="col">
                <div class="input-group ">
                    <div class="input-group-prepend">
                        <span class="input-group-text" id="ip-addon">IP</span>
                    </div>
                    <input value="127.0.0.1" v-model="ip" type="text" class="form-control" name="ip" id="ip"
                        placeholder="please enter ip address" aria-label="ip" aria-describedby="ip-addon">
                </div>
            </div>

            <div class="col">
                <div class="input-group ">
                    <div class="input-group-prepend">
                        <span class="input-group-text" id="port-addon">Port</span>
                    </div>
                    <input value="7901" v-model="port" type="text" class="form-control" name="port" id="port"
                        placeholder="please enter port" aria-label="port" aria-describedby="port-addon">
                </div>
            </div>

            <div class="col">
                <select id="selectServer" data-style="btn-info"></select>
<!--                <div class="input-group ">-->
<!--                    <div class="input-group-prepend">-->
<!--                        <span class="input-group-text" id="agentId-addon">AgentId</span>-->
<!--                    </div>-->
<!--                    <input value="" v-model="agentId" type="text" class="form-control" name="agentId" id="agentId"-->
<!--                        placeholder="please enter agentId" aria-label="agentId" aria-describedby="agentId-addon">-->
<!--                </div>-->
            </div>

            <div class="col-inline">
                <button title="connect" type="button" class="btn btn-info form-control" onclick="startConnect()">连接Aarthas</button>
                <button title="disconnect" type="button" class="btn btn-info form-control" onclick="disconnect()">断开连接</button>
                <button title="release" type="button" class="btn btn-info form-control" onclick="reloadAgent()">重新加载服务</button>
<!--                <a id="arthasOutputA" target="_blank" href="arthas-output/" class="btn btn-info" role="button" οnclick="updateArthasOutputLink()">Arthas Output</a>-->
            </div>

        </form>

    </nav>

    <div class="container-fluid px-0">
        <div class="col px-0" id="terminal-card">
            <div id="terminal"></div>
        </div>
    </div>

    <div title="fullscreen" id="fullSc" class="fullSc">
        <button id="fullScBtn" onclick="xtermFullScreen()"><img src="fullsc.png"></button>
    </div>
</body>

</html>
  1. arthas.js 也是直接从参考博文1拷贝过来
    重点关注reloadRegisterApplicationsinitSelect方法,其中reloadRegisterApplications用于从上文编写的ArthasController中获取已注册的arthas客服端
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 reloadAgent(){
  reloadRegisterApplications();
  reloadApplications();
}

/**
 * 获取注册的应用
 */
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>");
    server = list[i].toLowerCase();
    $(uiSelect).append("<option value=" + server + ">" + server + "</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;
}
  1. web-console.js改造点说明:主要是注释掉自动连接,通过selectServer选择器选择
    注释掉自动加载
    在这里插入图片描述

改造后效果展示

  1. SBA首页新增Arthas链接
    SBA首页新增Arthas链接
    我曾经在这个外链折腾了好久,最早sba用2.2.1的版本,无论如何外链都不行,更改了2.3.1版本才生效
    这里的要点:

1.pom文件里面有一块打包的时候要将static文件打包到sba指定的目录

<resources>
      <!-- 指定 src/main/resources下所有文件及文件夹为资源文件 -->
      <resource>
        <directory>src/main/resources</directory>
        <targetPath>${project.build.directory}/classes</targetPath>
        <includes>
          <include>**/*</include>
        </includes>
        <filtering>true</filtering>
      </resource>
      <!-- 通过 Maven Resource 的指定配置打入指定目录,实现 SBA 启动时的自定义加载 ,通过application配置 外链-->
      <resource>
        <directory>static</directory>
        <targetPath>${project.build.directory}/classes/META-INF/spring-boot-admin-server-ui/extensions/arthas
        </targetPath>
        <filtering>false</filtering>
      </resource>
    </resources>

2.appliction配置要配对

spring:
  boot:
    # /META-INF/spring-boot-admin-server-ui/
    admin:
      ui:
        # 自定义网页header,默认值assets/img/icon-spring-boot-admin.svg,自定义地址
        #brand: <img src="custom/custom-icon.png">
        # 自定义logo图标,默认路径/META-INF/spring-boot-admin-server-ui/assets/img/
        #login-icon: assets/img/custom-login-icon.svg
        # 外链或扩展页面
        external-views:
          - label: "Arthas Console"
            url: ./extensions/arthas/arthas.html
            order: 1900
  1. 外链到Arthas Console效果
    在这里插入图片描述
    此处两点注意点:
  1. 外链的地址
  2. Port端口,这里一定是arthas.server.port的端口配置,如果是容器需要对应映射的端口
  1. 点击链接后进入到arthas页面,到这里就是arthas的能力圈了,就尽情享受
    arthas页面
    控制台如何使用复制粘贴:复制Ctrl+Insert/粘贴Shift+Insert 或Ctrl+Shift+c/Ctrl+Shift+v参考链接
    至此服务端已完成改造,以上都是核心点描述,源码待后续整理上传到github
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值