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 的代码让其可以支持多个服务器之间的自动切换,尚未对其彻底自动化,咱们指望的前景是可以配置一个让开发人员彻底不须要进行任何代码开发的第三方服务器来存储而且代理实现全部的配置命令,而且给用户一个能够配置的界面,让用户对其进行新的命令和执行方式的配置 (例如是否能够进行自动化的时间配置)。
实现自动化遍历服务器,动态读取错误日志可以大大缩短开发测试人员分析问题的时间,提升解决问题的效率。但遇到复杂的错误时,单从错误日志并不能彻底地查出根本所在,所以咱们须要不断地扩展其余功能以应对各类复杂的状况。咱们将会在后续的文章中对功能进一步深刻细化。