4.JMeter压测

1. JMeter入门

  • 官网:https://jmeter.apache.org/

  • 使用:点击bat文件即可用运行

  • 步骤:添加线程组、添加监听器(聚合报告)、线程组右键->添加sampler-》http请求

在本地对/goods/to_list这个简单的压力测试,其实这个接口里面就一个任务:

List goodsVoList = goodsService.getGoodsVoList();

那么,我以1000的并发,循环10次,尽快执行完。测试结果发现吞吐量最高大约是350。这个并发量比较小。

2. 自定义变量模拟多用户

模拟多个不同用户同时操作。其实就是建立一个文件,然后引用配置文件中变量即可。下面有示例。

  1. 测试计划->添加配置元件->CSV Data Set Config
  2. 引用变量${}

3. JMeter命令行使用

先在本地用软件生成一个jmx文件,将其上传到Liunx服务器上,这个服务器上现在跑当前程序的war包,如何生成这个war见下面介绍。

在linux上安装好jmeter.执行:

jmeter.sh -n -t xxx.jmx -l result.jtl

生成结果保存到result.jtl文件中。可以在图形化界面软件中打开这个结果进行查看。

在一台linux上进行测试,接口就上面提到的to_list。5000并发量,循环10次,在上面的测试结果大概是1267的QPS。记录此值,下面进行优化。

3.1 秒杀接口测试

我们的重点是对do_miaosha这个接口进行测试。但是呢,我们不能用一个user来测试,所以在压测之前,我们需要准备好数据:

整体思路是:先往数据库插入5000条数据,然后生成5000个token到一个txt文件中。

3.2 连接数据库的工具类:DBUtil

public class DBUtil {

    private static Properties props;

    static {
        try {
            InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
            props = new Properties();
            props.load(in);
            in.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConn() throws Exception{
        String url = props.getProperty("spring.datasource.url");
        String username = props.getProperty("spring.datasource.username");
        String password = props.getProperty("spring.datasource.password");
        String driver = props.getProperty("spring.datasource.driver-class-name");
        Class.forName(driver);
        return DriverManager.getConnection(url,username, password);
    }
}

3.3 db.properties文件

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat

3.4 执行程序,要先启动web程序

public class UserUtil {

    private static void createUser(int count) throws Exception{
        List users = new ArrayList(count);
        //生成用户
        for(int i=0;inew MiaoshaUser();
            user.setId(13000000000L+i);
            user.setLoginCount(1);
            user.setNickname("user"+i);
            user.setRegisterDate(new Date());
            user.setSalt("1a2b3c");
            user.setPassword(MD5Util.inputPassToDbPass("123456", user.getSalt()));
            users.add(user);
        }
        System.out.println("create user");
//      //插入数据库
        Connection conn = DBUtil.getConn();
        String sql = "insert into miaosha_user(login_count, nickname, register_date, salt, password, id)values(?,?,?,?,?,?)";
        PreparedStatement pstmt = conn.prepareStatement(sql);
        for(int i=0;i1, user.getLoginCount());
            pstmt.setString(2, user.getNickname());
            pstmt.setTimestamp(3, new Timestamp(user.getRegisterDate().getTime()));
            pstmt.setString(4, user.getSalt());
            pstmt.setString(5, user.getPassword());
            pstmt.setLong(6, user.getId());
            pstmt.addBatch();
        }
        pstmt.executeBatch();
        pstmt.close();
        conn.close();
        System.out.println("insert to db");
        //登录,生成token
        String urlString = "http://localhost:8080/login/do_login";
        File file = new File("D:/tokens.txt");
        if(file.exists()) {
            file.delete();
        }
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        file.createNewFile();
        raf.seek(0);
        for(int i=0;inew URL(urlString);
            HttpURLConnection co = (HttpURLConnection)url.openConnection();
            co.setRequestMethod("POST");
            co.setDoOutput(true);
            OutputStream out = co.getOutputStream();
            String params = "mobile="+user.getId()+"&password="+MD5Util.inputPassToFormPass("123456");
            out.write(params.getBytes());
            out.flush();
            InputStream inputStream = co.getInputStream();
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte buff[] = new byte[1024];
            int len = 0;
            while((len = inputStream.read(buff)) >= 0) {
                bout.write(buff, 0 ,len);
            }
            inputStream.close();
            bout.close();
            String response = new String(bout.toByteArray());
            JSONObject jo = JSON.parseObject(response);
            String token = jo.getString("data");
            System.out.println("create token : " + user.getId());

            String row = user.getId()+","+token;
            raf.seek(raf.length());
            raf.write(row.getBytes());
            raf.write("\r\n".getBytes());
            System.out.println("write to file : " + user.getId());
        }
        raf.close();

        System.out.println("over");
    }

    public static void main(String[] args)throws Exception {
        createUser(5000);
    }
}

最后查看数据库是否生成了5000条用户信息,以及是否在D盘下生成了相应的token文件。

我们的目标是生成userId和token的文件,所以我们需要对doLogin这个方法进行修改,原来是返回Result,现在返回Result,这个String就是生成的token。

如果顺利的话,生成的文件是这样的:

13000000000,3e9e716b555047f2af8ccdb3224da4f2
13000000001,53f55f4b1b3247669c5c2588548d8ee8
13000000002,87a313072df74b2d944c3227b14c2d4a
13000000003,77c7e4a834fd4986952a78c18c27d22c

下面,打开JMeter软件,首先是按照上面的步骤CSV Data Set Config,引入tokens.txt这个文件。在Variable Names这一项写上userId,token,这样,就可以获取到这两个参数。

然后配置好http请求:

用Aggregate Report来查看结果。这里用5000的并发来发请求。

我在数据库准备5个秒杀商品。

在测试中,发现数据库的秒杀商品数量竟然变成了负数。。这个时候出现了线程安全,我们的超卖现象。

有的时候也能根据预期执行完,我们会发现5000个用户只有5个人抢到了。数据库里只有五条记录。秒杀的压力测试效果我们已经达到了,下面就是线程安全和提高并发量的工作了。

4. redis压测工具redis-benchmark

redis-benchmark -h 127.0.0.1 p 6379 -c 100 -n 100000

100个并发连接,100000个请求。

redis-benchmark -h 127.0.0.1 p 6379 -q -d 100

存取大小为100字节的数据包

5. spring Boot打war包

添加spring-boot-starter-tomcat的provided依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-tomcatartifactId>
    <scope>providedscope>
dependency>

添加maven-war-plugin插件

<build>
    <finalName>${project.artifactId}finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
        plugin>
        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-war-pluginartifactId>
            <configuration>
                <failOnMissingWebXml>falsefailOnMissingWebXml>
            configuration>
        plugin>
    plugins>
build>

不要忘记上面的:

<packaging>jarpackaging>

改为:

<packaging>warpackaging>

最后,修改启动函数:

@EnableTransactionManagement
@SpringBootApplication
public class MiaoshaApplication extends SpringBootServletInitializer{

    public static void main(String[] args) {
        SpringApplication.run(MiaoshaApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(MiaoshaApplication.class);
    }
}

执行mvn clean package命令,执行成功,就可以看到war包了。

你可能感兴趣的:(秒杀,秒杀系统)