背景
Jmeter连接数据库的方式,网上有很多,包括使用配置元件等等,这里就不说了。
如果你的mysql部署在远程服务器,而且还需要SSH登录连接时,那么使用Jmeter如何连接呢?
相信我,网上资料很少,这里是使用beanshall方式。
准备
首先你得下载两个jar包,一个是mysql的,一个是java的搭建ssh代理的包
mysql包下载:官网(我使用的是mysql8,所以下载的是8.0.25)
java的ssh代理包下载:官网(我下载的是jsch-0.1.55.jar)
然后把包放在apache-jmeter-5.4.3\lib\ext目录下,再次重启jmeter即可(如果测试计划添底部引入jar包目录,可以不用重启jmeter)。
这时你应该还知道
- mysql的远程地址、端口号、数据库登录用户名及用户密码
- SSH服务器的远程地址、SSH端口号、服务器登录用户名及用户密码
接着
- 打开jmeter
- 添加一个jsr223取样器,选择beanshall
- 然后开始可以写代码调试了
我的完整代码如下,startSSH是开启SSH代理,testSSH是测试执行,可以把要执行的sql放这里面:
import java.sql.Connection;
import java.sql.DriverManager;
import java.net.DatagramSocket;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Date;
import java.text.SimpleDateFormat;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
static int localPort = 3306;// 本地端口
static String remoteHost = "xxx";// 远程MySQL服务器
static int remotePort = 3306;// 远程MySQL服务端口
public static void startSSH() throws JSchException {
// SSH连接用户名
String sshUser = "xxx";
// SSH连接密码
String sshPassword = "xxx";
// SSH服务器地址
String sshHost = "xxx";
// SSH服务器访问端口
int sshPort = 22;
JSch jsch = new JSch();
Session session = jsch.getSession(sshUser, sshHost, sshPort);
session.setPassword(sshPassword);
// 设置第一次登录的时候提示,可选值:(ask | yes | no)
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
// 打印SSH服务器版本信息
System.out.println(session.getServerVersion());
try{
DatagramSocket ds=new DatagramSocket(3306);
// 设置SSH本地端口转发,本地转发到远程
int assinged_port = session.setPortForwardingL(localPort, remoteHost, remotePort);
// 设置SSH远程端口转发,远程转发到本地
// session.setPortForwardingR(remotePort, remoteHost, localPort);
log.info("localhost:" + assinged_port + " -> " + remoteHost + ":" + remotePort);
testSSH();
}catch (SocketException e){
System.out.println("busy port:"+i);
}
finally {
// 删除本地端口的转发
session.delPortForwardingL(localPort);
// 断开SSH链接
session.disconnect();
}
// System.out.println("localhost:" + assinged_port + " -> " + remoteHost + ":" + remotePort);
}
public static void testSSH() throws Exception {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver"); // mysql8是这个,8以下用???
// 设置SSH本地端口转发后,访问本地ip+port就可以访问到远程的ip+port
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbname?useSSL=false&serverTimezone=GMT", "用户名", "用户密码");
st = conn.createStatement();
String userId = vars.get("userId");
String superior = vars.get("userName");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(System.currentTimeMillis());
String time = formatter.format(date);
String value = "('" + userName + "','" + userId + "'," + "1,'" + time +"')";
log.info("----" + value);
// String value = "('111','111',1,'2022-07-26 17:32:32')";
// 定义sql
String sql = "INSERT INTO t_user (userName,userId,status,time) VALUES " + value;
// String sql2 = "SELECT * FROM t_invitation_relationship LIMIT 1;";
// rs = st.executeQuery(sql2); // select语句用
st.execute(sql); // insert语句用
// log.info("-------re:" + rs);
// while (rs.next())
// System.out.println(rs.getString(1));
//
} catch (Exception e) {
throw e;
} finally {
// if (rs!=null) {rs.close();rs=null;}
if (st!=null) {st.close();st=null;}
if (conn!=null) {conn.close();conn=null;}
}
}
startSSH();
最后
开始验证,运行jmeter,就可以看到已经连接成功了,如果有数据库报错,检查一下sql语句。
后记
我是在windows下运行的,当我尝试循环执行2次时,出现如下报错,报错提示为3306端口未注册。
而单次执行不会报错。。。
明明session连接已经关闭释放了,为什么会出现上述情况?查看端口情况,发现下图,期间执行了多次脚本。
原来每次 jmeter执行测试计划时会申请一个端口(如第二列的56751)去跟本地3306建立一个连接,虽然session连接释放只是关闭了tcp连接(对应图中的TIME_WAIT状态),未释放端口,所以第二次循环用同样的端口(56751)就绑定不了3306端口,就会报错。
而再次执行测试计划,会重新申请一个端口(如第二列的56735),这时就能绑定3306端口了,就不会报错。
那本地端口绑定的连接就不会关闭了吗?也不是:
- TIME_WAIT状态是TCP连接的一个状态,大概3分钟系统会自动清除连接
- 重启jmeter也会自动关闭连接
所以,只能把ssh代理脚本设置为循环期间仅一次执行或放入setUp线程组中,才能实现业务的循环操作。如果你有更优雅关闭端口绑定的办法,欢迎留言。