背景
性能测试,使用分布式压力机进行规模较大的压测时,环境准备阶段需要将测试依赖的文件(测试数据文件、依赖jar包、依赖第三方程序等)拷贝到各个机器上
分布式压力机使用的是linux系统或或者是windows再或者是两者混用
linux机器之间cp文件有很多种,而且都很方便,不需要额外装软件,预装的就能实现文件拷贝比如scp、ftp等都能实现
windows之间就比较麻烦了,os中可能都没有启用ftp,也没有开足够的共享权限等。也有可能你完全配置好一台windows,制作模版,再装其他机器。(别抬杠,不排除这种方法)
使用ftp共享实现
不是我要介绍的重点,自己问问度娘,网上很大篇幅是介绍这种方式的(略)
文件中转实现分发
我倾向拿来主义,所以我借助第三的程序。废话少说来干的
准备
服务器准备
准备一台linux服务器:hostLinux
准备N台windows服务:hostWin1……hostWinN
软件准备
pscp和psexec
用法见附录或者去官网
需求
将hostWin1上的文件cp到hostWin2……hostWinN
实现
pscp能进行win与linux之间cp文件类似linux下的scp;
psexec可以在windows之间远程执行命令,更重要的是有一个-c参数,可以将执行的命令拷贝到远程机器上执行,这样要拷贝文件的机器就不需要提前将命令准备好(在这里是pscp命令,也就是hostWin2……hostWinN上不需要做任何准备)
检查linux和个windows之间的ping,windows之间的ping
连通性检查可选,最好还是检查一下
将待分发的文件上传至hostLinux上的指定目录
pscp -pw hostLinuxPasswd [-r] localFile root@hostLinux:/root/transFile/
在hostWin1上控制在hostWin2……hostWinN上执行pscp命令
psexec -u hostWin2user -p hostWin2passwd -c -f pscp -pw xxxxx [-r] root@hostLinux:/root/transFile/ localFile
重复上面的动作
验证hostWin2……hostWinN上文件的完整性
传完了验证文件的完整性(验证一遍比较放心),可以通过远程执命令统计文件的个数,大小,md5等
改进
服务器很多,敲上面的命令是不是很糟心,虽然省略了打个各个终端操作,这么多命令也很累,写脚本,自动生成命令
将win服务器列表写成配置文件
#ip::user::passwd
#x.x.x.x::yueling::yuelingp
x.x.x.x::yueling::yuelingp
x.x.x.x::yueling::yuelingp
将linux服务器列表写成配置文件
#ip::user::passwd
#x.x.x.x::yueling::yuelingp
x.x.x.x::yueling::yuelingp
#当文件比较多或者比较大时,可以配置多个linux服务器作为中转,减轻一台服务器的网络和io压力(自己把握)
将操作的文件写成配置文件
#type::path
#file
f::c:\wmsFile\a.txt
#folder
D::c:\wmsFile
文件存储的位置写成配置文件(这个一般不会用到)
比如使用常用的性能测试工具LR加载测试依赖jar包或者文件,多个机器上的文件位置肯定是固定,否则脚本就会报错,Jmeter依赖文件、参数化文件等也是一样的
核心代码
读取配置文件
package com.yueling.utils;
import(略)
/**
* @ClassName: ReadCfgFile
* @Description: TODO
* @author yueling
* @date 2017年9月27日 下午2:45:26
*
*/
public class ReadCfgFile {
private final static Logger logger = LoggerFactory.getLogger(ReadCfgFile.class);
/**
* @Title: readTxt
* @Description: 读取配置文件,将结果放入arrayList对象中
* @param @param filePath
* @param @return
* @return ArrayList<String>
* @throws
*/
public static ArrayList<String> readTxt(String filePath) {
ArrayList<String> arrayList = new ArrayList<String>();
try {
File file = new File(filePath);
if (file.isFile() && file.exists()) {
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "utf-8");
BufferedReader br = new BufferedReader(isr);
String lineTxt = null;
while ((lineTxt = br.readLine()) != null) {
if (lineTxt.trim().startsWith("#") || lineTxt.trim().equals("")) {
logger.info("忽略{}", lineTxt);
} else {
logger.info(lineTxt);
arrayList.add(lineTxt);
}
}
br.close();
isr.close();
} else {
System.out.println("!");
logger.error("【{}】文件不存在", filePath);
}
} catch (Exception e) {
logger.error("打开文件异常!");
} finally {
}
return arrayList;
}
/**
* @Title: processServerList
* @Description: 处理服务器列表,将异常剔除
* @param @return
* @return ArrayList<ServerListObject>
* @throws
*/
public static ArrayList<ServerListObject> processServerList(String filePath) {
ArrayList<ServerListObject> serverList = new ArrayList<ServerListObject>();
ArrayList<String> arrServerList = readTxt(filePath);
String pattern = ".*::.*::.*";
String[] tmp = null;
for (int serverIndex = 0; serverIndex < arrServerList.size(); serverIndex++) {
ServerListObject serverListObject = new ServerListObject();
String serverInfo = arrServerList.get(serverIndex);
boolean isMatch = Pattern.matches(pattern, serverInfo);
if (isMatch) {
logger.info(serverInfo);
tmp = serverInfo.split("::");
int lenght = tmp.length;
if (lenght != 3) {
logger.error("格式不正确!【{}】正确格式为【ip::user::passwd】", serverInfo);
} else {
serverListObject.setIp(tmp[0]);
serverListObject.setUser(tmp[1]);
serverListObject.setPasswd(tmp[2]);
serverList.add(serverListObject);
}
} else {
logger.error("格式不正确!【{}】正确格式为【ip::user::passwd】", serverInfo);
}
}
return serverList;
}
/**
* @Title: processFileList
* @Description: 处理文件列表,将异常的剔除
* @param @return
* @return ArrayList<FileListObject>
* @throws
*/
public static ArrayList<FileListObject> processFileList(String filePath) {
ArrayList<FileListObject> fileList = new ArrayList<FileListObject>();
ArrayList<String> arrFileList = readTxt(filePath);
String pattern = ".*::.*";
String[] tmp = null;
for (int fileIndex = 0; fileIndex < arrFileList.size(); fileIndex++) {
FileListObject fileListObject = new FileListObject();
String fileInfo = arrFileList.get(fileIndex);
boolean isMatch = Pattern.matches(pattern, fileInfo);
if (isMatch) {
logger.info(fileInfo);
tmp = fileInfo.split("::");
int lenght = tmp.length;
if (lenght != 2) {
logger.error("格式不正确!【{}】正确格式为【type::path】", fileInfo);
} else {
// 处理文件是否存在
if (tmp[0].toUpperCase().equals("f".toUpperCase())) {
if (isFileExists(new File(tmp[1]))) {
fileListObject.setType(tmp[0]);
fileListObject.setPath(tmp[1]);
fileList.add(fileListObject);
}
} else if (tmp[0].toUpperCase().equals("d".toUpperCase())) {
if (isDirExists(new File(tmp[1]))) {
fileListObject.setType(tmp[0]);
fileListObject.setPath(tmp[1]);
fileList.add(fileListObject);
}
}
}
} else {
logger.error("格式不正确!【{}】正确格式为【type:path】", fileInfo);
}
}
return fileList;
}
/**
* @Title: isFileExists
* @Description: 判断文件是否存在
* @param @param file
* @param @return
* @return boolean
* @throws
*/
public static boolean isFileExists(File file) {
boolean isExistsOrNotFlag = false;
if (file.exists()) {
logger.info("{} file exists", file);
isExistsOrNotFlag = true;
} else {
logger.info("{} file not exists", file);
isExistsOrNotFlag = false;
}
return isExistsOrNotFlag;
}
/**
* @Title: isDirExists
* @Description: 判断文件夹是否存在
* @param @param file
* @param @return
* @return boolean
* @throws
*/
public static boolean isDirExists(File file) {
boolean isExistsOrNotFlag = false;
if (file.exists()) {
if (file.isDirectory()) {
logger.info("{} dir exists", file);
isExistsOrNotFlag = true;
} else {
logger.info("{} the same name file exists, is not a dir", file);
isExistsOrNotFlag = false;
}
} else {
logger.info("{} is not exists", file);
isExistsOrNotFlag = false;
}
return isExistsOrNotFlag;
}
}
组装命令执行
通过上面的读取文件已经将合法的配置放入到相应的数组中,遍历组装命令即可(略)
取一段检查服务器连通性的代码为例
public static boolean winPingWinServer(ServerListObject serverListObject) {
boolean serverAlive = false;
Runtime rt = Runtime.getRuntime();
Process process;
// ping的返回中有ttl标识,通过指定的次数判断
String contains = "TTL";
int pingCount = 0;
try {
process = rt.exec("cmd.exe /c ping -n 2 " + serverListObject.getIp());
pingCount = PrintResponseOnConsole.print(process, contains);
if (pingCount == 2) {
serverAlive = true;
logger.info("localhost ping {} is alive",serverListObject.getIp());
}else{
logger.info("localhost ping {} is not alive",serverListObject.getIp());
}
} catch (IOException e) {
logger.error("localhost ping {} exception",serverListObject.getIp());
e.printStackTrace();
}
return serverAlive;
}
使用system执行命令
使用参数控制
比如一部分命令没有必要重新执行,比如从本地上传到linux服务器,只需做一次,所以用参数控制
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("至少需要一个参数");
System.out.println("参数需为:<init|upload|download>");
} else {
try {
filePath = directory.getCanonicalPath() + "\\config\\serverList.properties";
} catch (IOException e) {
e.printStackTrace();
}
processServerList = ReadCfgFile.processServerList(filePath);
try {
filePath = directory.getCanonicalPath() + "\\config\\puppetServer.properties";
} catch (IOException e) {
e.printStackTrace();
}
processPuppetServerList = ReadCfgFile.processServerList(filePath);
try {
filePath = directory.getCanonicalPath() + "\\config\\FileList.properties";
} catch (IOException e) {
e.printStackTrace();
}
processFileList = ReadCfgFile.processFileList(filePath);
try {
filePath = directory.getCanonicalPath() + "\\config\\output.bat";
} catch (IOException e) {
e.printStackTrace();
}
// 根据传参决定执行那些功能
if (args[0].toUpperCase().equals("init".toUpperCase())) {
// 检查服务器部分的测试
//testInit(directory, filePath, processServerList, processPuppetServerList, processFileList);
//testCountFiles(processServerList, processPuppetServerList);
//testRmFiles(processPuppetServerList);
testInitTxt(directory, filePath, processServerList, processPuppetServerList, processFileList);
} else if (args[0].toUpperCase().equals("upload".toUpperCase())) {
// 测试上传文件
testValidUploadFileToPuppetServer(directory, filePath, processServerList, processPuppetServerList,
processFileList);
} else if (args[0].toUpperCase().equals("download".toUpperCase())) {
// 测试下载文件到各个windows服务器
testValidDownloadFileFromPuppetServer(directory, filePath, processServerList, processPuppetServerList,
processFileList);
} else {
System.out.println("参数需为:<init|upload|download>");
}
}
}
打包执行
C:\Users\yueling.DANGDANG\Desktop>java -jar distribute.jar
至少需要一个参数
参数需为:<init|upload|download>
C:\Users\yueling.DANGDANG\Desktop>
附录
pscp:
PuTTY Secure Copy client
Release 0.62
Usage: pscp [options] [user@]host:source target
pscp [options] source [source...] [user@]host:target
pscp [options] -ls [user@]host:filespec
Options:
-V print version information and exit
-pgpfp print PGP key fingerprints and exit
-p preserve file attributes
-q quiet, don't show statistics
-r copy directories recursively
-v show verbose messages
-load sessname Load settings from saved session
-P port connect to specified port
-l user connect with specified username
-pw passw login with specified password
-1 -2 force use of particular SSH protocol version
-4 -6 force use of IPv4 or IPv6
-C enable compression
-i key private key file for authentication
-noagent disable use of Pageant
-agent enable use of Pageant
-batch disable all interactive prompts
-unsafe allow server-side wildcards (DANGEROUS)
-sftp force use of SFTP protocol
-scp force use of SCP protocol
PsExec v1.97 - Execute processes remotely
Copyright (C) 2001-2009 Mark Russinovich
Sysinternals - www.sysinternals.com
PsExec executes a program on a remote system, where remotely executed console
applications execute interactively.
Usage: psexec [\\computer[,computer2[,...] | @file]][-u user [-p psswd][-n s][-l][-s|-e][-x][-i [session]][-c [-f|-v]][-w directory][-d][-<priority>][-a n,n,...] cmd [arguments]
-a Separate processors on which the application can run with
commas where 1 is the lowest numbered CPU. For example,
to run the application on CPU 2 and CPU 4, enter:
"-a 2,4"
-c Copy the specified program to the remote system for
execution. If you omit this option the application
must be in the system path on the remote system.
-d Don't wait for process to terminate (non-interactive).
-e Does not load the specified account's profile.
-f Copy the specified program even if the file already
exists on the remote system.
-i Run the program so that it interacts with the desktop of the
specified session on the remote system. If no session is
specified the process runs in the console session.
-h If the target system is Vista or higher, has the process
run with the account's elevated token, if available.
-l Run process as limited user (strips the Administrators group
and allows only privileges assigned to the Users group).
On Windows Vista the process runs with Low Integrity.
-n Specifies timeout in seconds connecting to remote computers.
-p Specifies optional password for user name. If you omit this
you will be prompted to enter a hidden password.
-s Run the remote process in the System account.
-u Specifies optional user name for login to remote
computer.
-v Copy the specified file only if it has a higher version number
or is newer on than the one on the remote system.
-w Set the working directory of the process (relative to
remote computer).
-x Display the UI on the Winlogon secure desktop (local system
only).
-priority Specifies -low, -belownormal, -abovenormal, -high or
-realtime to run the process at a different priority. Use
-background to run at low memory and I/O priority on Vista.
computer Direct PsExec to run the application on the remote
computer or computers specified. If you omit the computer
name PsExec runs the application on the local system,
and if you specify a wildcard (\\*), PsExec runs the
command on all computers in the current domain.
@file PsExec will execute the command on each of the computers listed
in the file.
program Name of application to execute.
arguments Arguments to pass (note that file paths must be
absolute paths on the target system).
原文链接 http://blog.csdn.net/yue530tomtom/article/details/78207506