<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Arthas 是 Alibaba開源的Java診斷工具,具有實時檢視系統的執行狀況,檢視函數呼叫引數、返回值和異常,線上熱更新程式碼,秒解決類衝突問題、定位類載入路徑,生成熱點圖,通過網頁診斷線上應用。 如今在各大廠都有廣泛應用,也延伸出很多產品。
這裡將介紹如何將Arthas整合進SpringBoot監控平臺中。
為了方便SpringBoot Admin 簡稱為SBA
版本:1.5.x
1.5版本的SBA如果要開發外掛比較麻煩,需要下載SBA的原始碼包,再按照spring-boot-admin-server-ui-hystrix的形式copy一份,由於JS使用的是Angular,本人放棄了
版本:2.x
2.x版本的SBA外掛開發,官網有介紹如何開發,JS使用Vue,方便很多,由於我們專案還在使用1.5,所以並沒有使用該版本,請讀者自行嘗試
不能使用SBA的外掛進行整合,那還有什麼辦法呢?????
鄙人的辦法是將Arthas的相關檔案直接copy到admin服務中
arthas包該包下存放的是所有arthas的Java檔案
@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會在啟動的時候載入該目錄下的檔案
index.html 覆蓋SBA原來的首頁,在其中新增一個導航,首頁會是這樣
<!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; }
copy過來的js
popper-1.14.6.min.js
web-console.js
xterm.css
xterm.js
# 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中偵錯應用了,看看最後的頁面
偵錯流程
開啟Arthas
在Select Application中選擇應用
這種整合方式有一些缺陷:
以上就是SpringBoot Admin整合診斷利器Arthas範例實現的詳細內容,更多關於SpringBoot Admin整合Arthas的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45