SSHxcute 是一個現存的基於 Java 的遠程執行類庫,容許工程師利用 Java 代碼經過 ssh 遠程執行 Linux/Unix 系統上的命令或者腳本,這種方式不論是針對軟件測試仍是系統部署,都簡化了自動化測試與系統部署的步驟。可是可是通過測試,現有的狀況只是支持對單個服務器的鏈接。本文介紹如何實現多服務器之間的切換而且根據配置文件來自動連接並讀取日誌或執行命令等操做。html
0 評論:java
包 向華, 軟件工程師, IBMlinux
2014 年 12 月 18 日web
在 IBM Bluemix 雲平臺上開發並部署您的下一個應用。服務器
開始您的試用session
系統的爆炸性增加讓應用的分佈式變的愈來愈廣泛。雖然現有的分佈式系統的應用,給予應用自己很大的可擴展性。可是相對來講,維護性就可能到其影響,讓維護人員的工做量加倍和工做效率下降。其中,最多見的麻煩之一即是錯誤處理問題的查找。當用戶須要根據錯誤日誌來診斷問題出現的時間和相對位置的時候,多個服務器之間的相互切換和命令的重複鍵入是沒有辦法被避免的,用戶須要重複各類簡單並且單調的工做模式,在不一樣的機器當中重複實現。app
回頁首負載均衡
對於軟件開發測試人員來講,大型的應用意味着對遠程主機依賴會達到更高的層次。從應用環境的部署到命令的遠程執行,都須要依賴各類客戶端工具。對於開發人員,當每次執行的命令不少時,通常是把其寫入到一個腳本里面,經過客戶端工具進行遠程登陸後執行。可是每次的執行都只是對一個特定的服務器,可是現有的分佈式系統讓工做可以均衡的負載到各個服務器上面,這也加大在相同工做內容上服務器上面進行部署的工做,並且當腳本部署到各個服務器上面時,當其中的一個腳本內容修改時,沒法達到全部腳本的一致性,這也會致使不少的平衡性的問題。框架
開發人員更加喜歡可智能化的集成開發環境,好比說,sshxcute 是一個現存的基於 Java 的遠程執行類庫,開發人員能夠經過運行一個 Java 類來部署環境,遠程執行命令或者腳本。他是一個框架,容許工程師利用 Java 代碼經過 ssh 遠程執行 Linux/Unix 系統上的命令或者腳本,這種方式不論是針對軟件測試仍是系統部署,都簡化了自動化測試與系統部署的步驟。
SSHXcute 的設置宗旨在於:
最小的系統需求
易用性
內置命令/
腳本任務執行功能
易擴展
可是不管是客戶端仍是先有的 Java 執行類庫,都有一個缺陷,那就是沒法實現相同任務分佈式服務器的自動切換。
測試人員在當下,若是發現問題後通常會執行以下步驟以得到錯誤日誌,只有得到更準確的錯誤日誌開發人員才能更快速的分析問題根本解決問題。
測試人員發現界面或後臺運行程序出現錯誤
打開 Putty,鏈接 1 號應用 app 服務器,打開相關日誌文檔查找錯誤日誌
鏈接 1 號數據庫服務器,打開相關日誌文檔查找錯誤日誌
鏈接 1 號後臺任務管理服務器,打開相關日誌文檔查找錯誤日誌
下載各類配置文件,已查詢錯誤
重複步驟 1-5,遍歷全部關聯服務器(Job,App,DB)查找日誌
SSHXcute 提供的 API 包含有關閉以及切換的操做,可是通過測試,現有的狀況只是支持對單個服務器的鏈接。不管是調用關閉仍是切換接口,都沒法影響現有的鏈接。
只有對現有的源代碼進行改進,纔可以自動實現多服務器之間的切換而且更夠根據配置文件來自動實現。
在開發測試人員的平常分析中,常常要經過遍歷大量不一樣的服務器以找到真正須要的日誌,所以實現自動遍歷全部服務器,可以大大縮短鏈接轉換服務器的時間,提升效率。
實現代碼以下:
//讀取服務器磁盤空間信息命令,讀取使用率大於 90%的 String cmd = "df -h | grep -b -E \\(9[1-9]\\%\\)\\|\\(100\\%\\)"; JSch sshSingleton = new JSch(); //從配置文件中加載用戶名和密碼 Properties userProp = new Properties(); userProp.load(new FileReader("user.properties")); String userName = userProp.getProperty("username"); String password = userProp.getProperty("password"); //從配置文件中加載服務器信息 Properties serversProp = new Properties(); serversProp.load(new FileReader("servers.properties")); for (Map.Entry<Object, Object> serverProp : serversProp.entrySet()) { String name = (String) serverProp.getKey(); String server = (String) serverProp.getValue(); System.out.println("Start working on: " + name); Session session = sshSingleton.getSession(userName, server); session.setPassword(password); Properties config = new Properties(); //設置 SSH 鏈接時不進行公鑰確認 config.put("StrictHostKeyChecking", "no"); session.setConfig(config); session.connect(); //打開命令執行管道 ChannelExec channel = (ChannelExec) session.openChannel("exec"); BufferedReader in = new BufferedReader(new InputStreamReader( channel.getInputStream())); channel.setCommand(cmd); channel.connect(); //讀取命令輸出信息 String msg; while ((msg = in.readLine()) != null) { System.out.println(msg); } channel.disconnect(); session.disconnect(); }
user.properties
用來存放用戶名和密碼:
username=admin password=xxxxxxxx
servers.propesties
用來存放 servers 的 IP 信息,例如:
#alias name = host name/IP server1=192.168.0.11 server2=192.168.0.12 server3=192.168.0.13 server4=192.168.0.14 server5=192.168.0.15
在多臺 job servers 中,因爲配置了負載均衡,所以 job 就會被分發到不一樣的 server 上,因此對應 job 的日誌可能會出如今不一樣的 server 上。一般咱們須要根據界面上的 ID 從一個日誌文件先找出相關的 JobId,而後再從其餘日誌文件中再找出具體的錯誤日誌信息。
實現代碼以下:
//日誌 1 中特殊字符信息 String uniqeStr = "RunID: 123"; //日誌 1 文件位置 String serverlogFilePath = "/opt/server/log/server.log"; //日誌 2 中特殊字符信息 String jobIdExpr = "id=[0-9]+"; //日誌 2 文件位置 String joblogFilePath = "/opt/server/log/job.log"; JSch sshSingleton = new JSch(); Properties userProp = new Properties(); userProp.load(new FileReader("user.properties")); String userName = userProp.getProperty("username"); String password = userProp.getProperty("password"); Properties serversProp = new Properties(); serversProp.load(new FileReader("servers.properties")); Pattern p = Pattern.compile(jobIdExpr); for (Map.Entry<Object, Object> serverProp : serversProp.entrySet()) { String name = (String) serverProp.getKey(); String server = (String) serverProp.getValue(); Session session = sshSingleton.getSession(userName, server); session.setPassword(password); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); session.connect(); //打開執行管道 ChannelExec channel = (ChannelExec) session.openChannel("exec"); BufferedReader in = new BufferedReader(new InputStreamReader( channel.getInputStream())); //設置命令,從日誌 1 中找出關鍵字符信息 channel.setCommand("cat " + serverlogFilePath + " |grep -w " + uniqeStr); channel.connect(); String msg; String jobId = null; while ((msg = in.readLine()) != null) { Matcher m = p.matcher(msg); if (m.find()) { jobId = m.group(); break; } } //關閉第一個執行管道 channel.disconnect(); if (jobId != null) { //日誌 1 中發現關鍵字符信息後,在同一服務器中日誌文件 2 中繼續查找 System.out.println("found log in jobServer: " + name); //另外再打開一個新的執行管道 channel = (ChannelExec) session.openChannel("exec"); in = new BufferedReader(new InputStreamReader( channel.getInputStream())); String cmd = "cat " + joblogFilePath + " |grep -A 10 " + jobId; channel.setCommand(cmd); channel.connect(); //輸出須要查找的日誌信息 while ((msg = in.readLine()) != null) { System.out.println(msg); } channel.disconnect(); } session.disconnect(); if (jobId != null) { //已經在這臺服務器中找到日誌,不須要繼續去其餘服務器中查找了 break; } }
在測試過程當中,咱們常常須要上傳更新一些配置文件或者下載服務器上的一些日誌文件,下面咱們就以一臺服務器作爲上傳下載的示例。
實現代碼以下:
String serverFile = "/opt/log/1.log"; String localFolder = "C:/tmp"; String localFile = "C:/tmp/user.xml"; String serverFolder = "/tmp/"; JSch sshSingleton = new JSch(); Properties userProp = new Properties(); userProp.load(new FileReader("user.properties")); String userName = userProp.getProperty("username"); String password = userProp.getProperty("password"); String server = "192.168.0.1"; //設置端口 int port = 22; Session session = sshSingleton.getSession(userName, server, port); session.setPassword(password); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); session.connect(); //打開 ftp 管道 Channel channel = session.openChannel("sftp"); channel.connect(); ChannelSftp c = (ChannelSftp) channel; //轉到根目錄,便於使用絕對路徑來進行文件傳輸 c.cd("/"); //從服務器上下載日誌文件 1.log 到本地目錄 C:/tmp c.get(serverFile, localFolder); //上傳配置文件 user.xml 到服務器上,若是服務器上已經存在該文件,則覆蓋它 c.put(localFile, serverFolder, ChannelSftp.OVERWRITE); session.disconnect();
咱們現有的改進方式不過是從新修改 SSHXcute 的代碼讓其可以支持多個服務器之間的自動切換,尚未對其徹底自動化,咱們指望的前景是可以配置一個讓開發人員徹底不須要進行任何代碼開發的第三方服務器來存儲而且代理實現全部的配置命令,而且給用戶一個能夠配置的界面,讓用戶對其進行新的命令和執行方式的配置 (例如是否能夠進行自動化的時間配置)。
實現自動化遍歷服務器,動態讀取錯誤日誌可以大大縮短開發測試人員分析問題的時間,提升解決問題的效率。但遇到複雜的錯誤時,單從錯誤日誌並不能徹底地查出根本所在,所以咱們須要不斷地擴展其餘功能以應對各類複雜的狀況。咱們將會在後續的文章中對功能進一步深刻細化。