代码改变世界

Jemter利用BeanShall连接远程数据库(SSH方式)

2022-08-06 08:07  第二个卿老师  阅读(679)  评论(0编辑  收藏  举报

背景

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)。

这时你应该还知道

  1. mysql的远程地址、端口号、数据库登录用户名及用户密码
  2. SSH服务器的远程地址、SSH端口号、服务器登录用户名及用户密码

接着

  1. 打开jmeter
  2. 添加一个jsr223取样器,选择beanshall
  3. 然后开始可以写代码调试了

我的完整代码如下,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端口了,就不会报错。

那本地端口绑定的连接就不会关闭了吗?也不是:

  1. TIME_WAIT状态是TCP连接的一个状态,大概3分钟系统会自动清除连接
  2. 重启jmeter也会自动关闭连接

所以,只能把ssh代理脚本设置为循环期间仅一次执行或放入setUp线程组中,才能实现业务的循环操作。如果你有更优雅关闭端口绑定的办法,欢迎留言。