亿级流量点赞系统-第七章

1. 前期回顾

项目源码地址:https://github.com/ruogu-coder/ruogu-like

在前六章,做了很多优化,目前点赞方式有:

  • Mysql 点赞 + Redis 缓存
  • Mysql +Redis Lua 脚本实现
  • Mysql + Redis + Caffine 实现多级缓存
  • Mysql + redis + Pulsar

Mysql 可替换成 TiDB 分布式数据库

2. 本期目标

使用 Jmeter 进行各个接口的压力测试

3. 环境搭建

3.1. 下载 Jmeter

官网地址:https://jmeter.apache.org/download_jmeter.cgi

下载完成后解压目录,如下图

进入 bin 目录修改jmeter.properties

将语言改为中文

java
复制代码
language=zh_CN

然后双击 jmeter.bat 脚本

打开后如下图

3.2. 创建线程组

3.3. 创建 HTTP 信息头管理

3.4. 添加 HTTP 请求头信息

plain
复制代码
Content-Type:application/json

3.5. 配置压测结果展示


3.6. 获取更多用户登录 token


在 test 目录下的创建 test 类

java
复制代码
package com.ruogu.thumb; import cn.hutool.core.util.RandomUtil; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.ruogu.thumb.common.exception.enums.GlobalErrorCodeConstants; import com.ruogu.thumb.common.pojo.CommonResult; import com.ruogu.thumb.model.dto.user.UserLoginReqDTO; import com.ruogu.thumb.model.dto.user.UserRegisterReqDTO; import com.ruogu.thumb.model.entity.User; import com.ruogu.thumb.service.UserService; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /*** *@author ruogu *@create 2025/4/25 12:57 **/ @SpringBootTest @AutoConfigureMockMvc class RuoguLikeApplicationTest { @Resource private UserService userService; @Resource private MockMvc mockMvc; }
  1. 批量添加用户
java
复制代码
@Test void addUser() throws Exception { int initCount = 5000; Set<String> hashSet = new HashSet<>(5000); for (int i = 0; i < initCount; i++) { // 生成随机用户账号 String userAccount = RandomUtil.randomString(8); while (hashSet.contains(userAccount)){ userAccount = RandomUtil.randomString(8); } hashSet.add(userAccount); // 固定密码和确认密码 String userPassword = "123456789"; // 构建请求体 UserRegisterReqDTO userRegisterReqDTO = new UserRegisterReqDTO(); userRegisterReqDTO.setUserAccount(userAccount); userRegisterReqDTO.setUserPassword(userPassword); userRegisterReqDTO.setCheckPassword(userPassword); // 将请求体转换为 JSON 字符串 String requestBody = new ObjectMapper().writeValueAsString(userRegisterReqDTO); // 构建 MockHttpServletRequestBuilder MockHttpServletRequestBuilder requestBuilder = post("/user/register") .contentType(MediaType.APPLICATION_JSON) .content(requestBody); // 执行请求并获取响应 ResultActions resultActions = mockMvc.perform(requestBuilder); // 解析响应体 MvcResult mvcResult = resultActions.andReturn(); String responseBody = mvcResult.getResponse().getContentAsString(); CommonResult<Long> commonResult = new ObjectMapper().readValue(responseBody, new TypeReference<CommonResult<Long>>() {}); // 断言响应结果为成功,并且返回值不为空 assertThat(commonResult.getCode()).isEqualTo(GlobalErrorCodeConstants.SUCCESS.getCode()); assertThat(commonResult.getData()).isNotNull(); } }

  1. 批量获取用户登录的 token 并保存成文件
java
复制代码
@Test void testLoginAndExportSessionToCsv() throws Exception { // 获取所有用户列表 List<User> list = userService.list(); // 确保用户列表不为空 assertThat(list).isNotEmpty(); // 使用 try-with-resources 确保资源正确关闭 try (PrintWriter writer = new PrintWriter(new FileWriter("token_output.csv", true))) { // 判断文件是否为空,如果是第一次写入,则添加表头 if (new File("token_output.csv").length() == 0) { writer.println("userId,token,timestamp"); } // 遍历用户列表 for (User user : list) { long testUserId = user.getId(); UserLoginReqDTO userLoginReqDTO = new UserLoginReqDTO(); userLoginReqDTO.setUserAccount(user.getUserAccount()); userLoginReqDTO.setUserPassword("123456789"); if(user.getUserAccount().equals("admin")){ userLoginReqDTO.setUserPassword("12345678"); } // 将请求体转换为 JSON 字符串 String requestBody = new ObjectMapper().writeValueAsString(userLoginReqDTO); // 构建登录请求 MvcResult result = mockMvc.perform(post("/user/login") .content(requestBody) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) // 断言响应状态码为 200 .andReturn(); // 获取 Set-Cookie 头 List<String> setCookieHeaders = result.getResponse().getHeaders("Set-Cookie"); assertThat(setCookieHeaders).isNotEmpty(); // 断言 Set-Cookie 头不为空 Optional<String> tokenValue = extractCookieValue(setCookieHeaders, "ruogu-like-token"); // 确保提取到值 String token = tokenValue.orElseThrow(() -> new RuntimeException("No ruogu-like-token found in response")); // 写入 CSV 文件 writer.printf("%d,%s,%s%n", testUserId, token, LocalDateTime.now()); // 输出日志 System.out.println("✅ 写入 CSV:" + testUserId + " -> " + token); } } } public static Optional<String> extractCookieValue(List<String> setCookieHeaders, String cookieName) { return setCookieHeaders.stream() .filter(cookie -> cookie.startsWith(cookieName + "=")) // 过滤包含指定 Cookie 名称的项 .map(cookie -> cookie.split(";")[0]) // 提取 Cookie 名称=值 部分 .map(cookie -> cookie.split("=")[1]) // 提取 Cookie 值部分 .findFirst(); // 返回第一个匹配项,如果未找到则返回 Optional.empty() }

4. 测试实现

4.1. Mysql 点赞 + Redis 缓存


  1. 回退版本

我的基础实现,是在增加前面这个一起已经实现,不过增加 Redis 支持是将登录完善,所以直接退回到这一期

重新执行添加用户和 token 的 csv 文件 这里使用的是单机 mysql 而不是 TiDB

  1. 添加 cookie 管理器

  1. 添加动态 csv 变量

测试结果:

平均值:8916

最慢响应:19239

TPS/s:478/sec

异常:0.42%

4.2. Mysql +Redis Lua 脚本 + 定时任务

将点赞数据通过定时任务写入,点赞信息全部维护在 Redis 中

清空 redis 点赞数据和 mysql 的点赞数据再次测试

平均值:1277

最慢响应:1864

TPS/s:2659/sec

异常:0%

4.3. TIDB + Redis Lua 脚本 + Pulsar

这里需要重新生成下 Token

平均值:14427

最慢响应:66158

TPS/s:275/sec

异常:1.5%

我这个测试出来还没有 Redis + 定时任务 的 TPS 高,Redis 和 mysql 都是本地安装的而 TiDB 和 Pulsar 都是虚拟机中 docker 搭建,感觉和这个应该是有关系的

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP