Spring-Boot-Admin集成arthas环境部署-服务端

目录

1.环境及版本使用

2.SBA环境搭建

2.1 SBA服务搭建

2.2 application.yml

2.3 SBA启动

3. SBA集成Arthas

3.1 引入完整依赖

3.2 arthas源代码拷贝到SBA中

3.3 application.yml完整版

3.4 SBA服务改造

3.5 Arthas外链设置 

3.6 重新启动SBA并访问Arthas Console

3.7 日志收集

3.7.1 slf4j方式

3.7.2 logback方式


1.环境及版本使用

Spring-Boot-Admin(SBA)和Arthas集成部署到rancher环境,监控节点状态、jvm性能、日志收集等工作,留下本文记录搭建过程。

版本选择:

  • Spring Boot:2.3.12.RELEASE
  • SBA:2.3.1
  • Arthas:3.6.4

SBA版本跟随Spring Boot大版本一致,否则容易出一些奇葩问题

2.SBA环境搭建

2.1 SBA服务搭建

使用Spring initializer创建spring boot项目,选择ops下spring boot admin server

Spring-Boot-Admin集成arthas环境部署-服务端_第1张图片

Spring-Boot-Admin集成arthas环境部署-服务端_第2张图片

2.2 application.yml

server:
  port: 7000
spring:
  application:
    name: sba_arthas

2.3 SBA启动

在@SpringBootApplication入口类上添加注解@EnableAdminServer以启用SBA

Spring-Boot-Admin集成arthas环境部署-服务端_第3张图片

此时没有服务注册进来 

3. SBA集成Arthas

3.1 引入完整依赖

4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.12.RELEASE
         
    

    com.tsit
    spring-boot-admin
    0.0.1-SNAPSHOT

    
        1.8
        2.3.1
        3.6.4
    
    
        
            org.projectlombok
            lombok
            1.16.14
            provided
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            de.codecentric
            spring-boot-admin-starter-server
        
        
            de.codecentric
            spring-boot-admin-server-ui
        
        
            org.springframework.boot
            spring-boot-starter-security
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
        
            com.taobao.arthas
            arthas-common
            ${arthas.version}
        
        
            com.taobao.arthas
            arthas-tunnel-common
            ${arthas.version}
        
        
            it.ozimov
            embedded-redis
            0.7.3
            
                
                    org.slf4j
                    slf4j-simple
                
            
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            com.github.ben-manes.caffeine
            caffeine
        
        
            org.apache.commons
            commons-pool2
        
        
            org.apache.commons
            commons-lang3
        
    

3.2 arthas源代码拷贝到SBA中

  • 下载: Arthas源码地址:Gitee版本
  • 拷贝tunnel-server下代码到SBA中,Arthas目录结构如下

Spring-Boot-Admin集成arthas环境部署-服务端_第4张图片

  • 拷贝后SBA目录结构如下

Spring-Boot-Admin集成arthas环境部署-服务端_第5张图片

3.3 application.yml完整版

server:
  port: 7000

spring:
  application:
    name: sba_arthas
  ## 集成了spring security安全组件,定义登录SBA的账号密码,
  ## 后期注册到SBA的客户端也要设置此权限才能注册进来
  security:
    user:
      name: admin
      password: admin
  boot:
    admin:
      # SBA添加外链扩展页面,此处外链跳转Arthas控制台
      ui:
        external-views:
          - label: "Arthas Console"
            url: "./extensions/arthas/arthas.html"
            order: 1900
  # Arthas的缓存策略
  cache:
    type: caffeine
    cache-names: inMemoryClusterCache
    caffeine:
      spec: maximumSize=3000,expireAfterAccess=3600s

# 监控所有页面
management:
  endpoints:
    web:
      exposure:
        include: '*'
  metrics:
    tags:
      application: ${spring.application.name}
  ## 关闭rabbitmq,redis,es 健康检查
  health:
    redis:
      enabled: false
    rabbit:
      enabled: false
    elasticsearch:
      enabled: false
  # 总是显示服务健康细节
  endpoint:
    health:
      show-details: always
# arthas tunnel-server监听地址端口
arthas:
  server:
    host: 0.0.0.0
    port: ${PORT:7777}
  enableDetailPages: true

3.4 SBA服务改造

  • 添加ArthasController类,以获取所有注册到tunnel-server的服务agentId
  • 注释ArthasTunnelApplication,从SBA的application类启动,并添加@EnableCaching启用缓存策略
  • 注释WebSecurityConfig,添加新的SecurityConfig,兼容SBA的权限过滤设置
  • 修改ProxyController类,以支持arthas火焰图文件地址查看
  • static文件改造添加arthas.html和arthas.js
  • 修改web-console.js中updateArthasOutputLink方法

完整代码

package com.example.sba_arthas.arthas.app.web;

import com.example.sba_arthas.arthas.AgentInfo;
import com.example.sba_arthas.arthas.TunnelServer;
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;

import java.util.Map;
import java.util.Set;

/**
 * 获取所有注册到 Arthas 的客户端 
* * @date: 2022年8月24日11:30:30
* @author: yzg
* @since: 1.0
* @version: 1.0
*/ @RequestMapping("/api/arthas") @RestController public class ArthasController { @Autowired private TunnelServer tunnelServer; @RequestMapping(value = "/clients", method = RequestMethod.GET) public Set getClients() { Map agentInfoMap = tunnelServer.getAgentInfoMap(); return agentInfoMap.keySet(); } }
package com.example.sba_arthas.config;

import com.example.sba_arthas.arthas.app.configuration.ArthasProperties;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

/**
 * @author :yzg
 * @date :Created in 2022/8/22 14:56
 * @description:
 * @modified By:
 * @version: $
 */

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	private final String adminContextPath;
	
	@Autowired
	private ArthasProperties arthasProperties;

	public SecurityConfig(AdminServerProperties adminServerProperties) {
		this.adminContextPath = adminServerProperties.getContextPath();
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		// @formatter:off
		SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
		successHandler.setTargetUrlParameter("redirectTo");
		successHandler.setDefaultTargetUrl(adminContextPath + "/");
		
		// allow iframe
		if (arthasProperties.isEnableIframeSupport()) {
			http.headers().frameOptions().disable();
		}
		
		http.authorizeRequests()
				.antMatchers(adminContextPath + "/assets/**").permitAll()//Grants public access to all static assets and the login page.
				.antMatchers(adminContextPath + "/login").permitAll()
				.anyRequest().authenticated()//	Every other request must be authenticated.
				.and()
				.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()//Configures login and logout.
				.logout().logoutUrl(adminContextPath + "/logout").and()
				.httpBasic().and()//Enables HTTP-Basic support. This is needed for the Spring Boot Admin Client to register.
				.csrf()
				.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())//	Enables CSRF-Protection using Cookies
				.ignoringAntMatchers(
						adminContextPath + "/instances",//	Disables CRSF-Protection the endpoint the Spring Boot Admin Client uses to register.
						adminContextPath + "/actuator/**"//Disables CRSF-Protection for the actuator endpoints.
				);
	}
}
package com.tsit.springbootadmin.arthas.app.web;

import com.alibaba.arthas.tunnel.common.MethodConstants;
import com.alibaba.arthas.tunnel.common.SimpleHttpResponse;
import com.alibaba.arthas.tunnel.common.URIConstans;
import com.tsit.springbootadmin.arthas.AgentInfo;
import com.tsit.springbootadmin.arthas.TunnelServer;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 代理http请求到具体的 arthas agent里
 * 
 * @author hengyunabc 2020-10-22
 *
 */
@RequestMapping(value = {"/extensions/arthas", "/"})
@Controller
public class ProxyController {
    private final static Logger logger = LoggerFactory.getLogger(ProxyController.class);

    @Autowired
	TunnelServer tunnelServer;

    @RequestMapping(value = "/proxy/{agentId}/**")
    @ResponseBody
    public ResponseEntity execute(@PathVariable(name = "agentId", required = true) String agentId,
            HttpServletRequest request) throws InterruptedException, ExecutionException, TimeoutException {

        String fullPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
		fullPath = StringUtils.replace(fullPath, "/extensions/arthas", "");
		logger.info("fullPath:{}", fullPath);
        String targetUrl = fullPath.substring("/proxy/".length() + agentId.length());

        logger.info("http proxy, agentId: {}, targetUrl: {}", agentId, targetUrl);

        Optional findAgent = tunnelServer.findAgent(agentId);

        if (findAgent.isPresent()) {
            String requestId = RandomStringUtils.random(20, true, true).toUpperCase();

            ChannelHandlerContext agentCtx = findAgent.get().getChannelHandlerContext();

            Promise httpResponsePromise = GlobalEventExecutor.INSTANCE.newPromise();

            tunnelServer.addProxyRequestPromise(requestId, httpResponsePromise);

            URI uri = UriComponentsBuilder.newInstance().scheme(URIConstans.RESPONSE).path("/")
                    .queryParam(URIConstans.METHOD, MethodConstants.HTTP_PROXY).queryParam(URIConstans.ID, agentId)
                    .queryParam(URIConstans.TARGET_URL, targetUrl).queryParam(URIConstans.PROXY_REQUEST_ID, requestId)
                    .build().toUri();

            agentCtx.channel().writeAndFlush(new TextWebSocketFrame(uri.toString()));
            logger.info("waitting for arthas agent http proxy, agentId: {}, targetUrl: {}", agentId, targetUrl);

            SimpleHttpResponse simpleHttpResponse = httpResponsePromise.get(15, TimeUnit.SECONDS);

            BodyBuilder bodyBuilder = ResponseEntity.status(simpleHttpResponse.getStatus());
            for (Entry entry : simpleHttpResponse.getHeaders().entrySet()) {
                bodyBuilder.header(entry.getKey(), entry.getValue());
            }
            ResponseEntity responseEntity = bodyBuilder.body(simpleHttpResponse.getContent());
            return responseEntity;
        } else {
            logger.error("can not find agent by agentId: {}", agentId);
        }

        return ResponseEntity.notFound().build();
    }
}

arthas.html是拷贝的index.html,可以比较一下两个不同





    
    
    

    
    
    

    
    

    
    
    
    
    
    
    
    
    


    

    Arthas Console



    

    
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("");
        server = list[i].toLowerCase();
        $(uiSelect).append("");
    }
}

/**
 * 重置配置文件
 */
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;
}
function updateArthasOutputLink() {
    $('#arthasOutputA').prop("href", "proxy/" + $("#selectServer").val() + "/arthas-output/")
}

3.5 Arthas外链设置 

yml文件中定义的Arthas控制台外链地址如何定义,此处是重点

SBA启动后访问的页面是spring-boot-admin-server-ui依赖的页面,外链指向的地址是希望通过maven打包的方式将static静态资源打入到该目录下。

Spring-Boot-Admin集成arthas环境部署-服务端_第6张图片

引入pom打包模块


        ${project.artifactId}
        
            
            
                src/main/resources
                ${project.build.directory}/classes
                
                    **/*
                
                true
            
            
            
                src/main/resources/static
                ${project.build.directory}/classes/META-INF/spring-boot-admin-server-ui/extensions/arthas
                
                false
            
        
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    

3.6 重新启动SBA并访问Arthas Console

Spring-Boot-Admin集成arthas环境部署-服务端_第7张图片Spring-Boot-Admin集成arthas环境部署-服务端_第8张图片

3.7 日志收集(客户端设置)

3.7.1 slf4j方式

logging:
  file:
    ## 日志路径,默认文件名spring.log
    path: /user/iot/manage
  pattern:
    file: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%thread]){faint} %clr(%logger{50}){cyan} %clr(LN:%L){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}'

Spring-Boot-Admin集成arthas环境部署-服务端_第9张图片

3.7.2 logback方式

添加logback-spring.xml文件 



    
  ${APP_Name}
    
    

    
    
    
    
    
    

    
    
        
            ${CONSOLE_LOG_PATTERN}
            utf8
        
    

    
    
        ${LOG_HOME}/iot-work.log
        
            
            ${LOG_HOME}/iot-work-%d{yyyy-MM-dd}.log
            
            3
        
        
            
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n
        
        
        
            10MB
        
    



    
    
        
        
    

添加application.yml中logging模块

management:
  endpoint:
    logfile:
      ## logback-spring.xml中定义的日志文件路径
      external-file: /user/iot/work/logs/iot-work.log

logging:
  config: classpath:logback-spring.xml

至此SBA集成Arthas搭建完成,下一章搭建客户端注册SBA,后续添加上DockerFile后打包部署到rancher上。

参考资料:https://blog.csdn.net/xiaoll880214/article/details/120191476?spm=1001.2014.3001.5502 

你可能感兴趣的:(服务监控,spring,boot,jvm)