中期答辩

38 阅读5分钟

基础业务与项目进度

12306 铁路购票服务是与大家生活和出行相关的关键系统,包括会员、购票、订单、支付和网关等服务。 image.png

目前实现了已经实现了前端界面网关服务模块会员服务模块车票查询以及部分车票购买订单管理 等功能。

项目架构

image.png

image.png

实现思路

网关服务

在用户登录时执行 身份认证黑白名单检查

1. 认证用户身份

  • 用户通过 用户名/邮箱/手机号 + 密码 登录。
  • 服务器调用 认证服务 进行 校验(JWT Token)。
  • 如果登录成功,则返回 Token 并设置到 Cookie 或前端存储。

2. 进行黑白名单检测

  • 黑名单

    • 存储方式:Redis、MySQL。

    • 判断逻辑

      • IP 黑名单:检测请求 IP 是否在黑名单中,如发现则直接拒绝请求(如 403)。
      • 用户黑名单:检测 userIdusername 是否在黑名单中,阻止登录或访问关键接口。
  • 白名单

    • 对于某些 受限接口(如管理员后台、API 访问),只有 白名单用户 才能使用。

具体实现

(1)网关拦截器 在 API 网关层添加一个拦截器:

  • 校验 Token:检查 Authorization 头,是否是有效 Token。
  • 黑名单检查:在 Redis/数据库中查找该用户是否在黑名单。
  • 白名单检查:如果该接口需要白名单,检查用户是否符合要求。
const checkBlacklist = async (userId, ip) => {
  const isBlacklisted = await redis.sismember("blacklist:users", userId);
  const isIPBlacklisted = await redis.sismember("blacklist:ips", ip);

  if (isBlacklisted || isIPBlacklisted) {
    throw new Error("您的账号/IP 被禁止访问");
  }
};

const loginHandler = async (req, res) => {
  const { username, password } = req.body;

  // 验证用户身份
  const user = await authenticateUser(username, password);
  if (!user) return res.status(401).json({ message: "用户名或密码错误" });

  // 黑名单检测
  await checkBlacklist(user.id, req.ip);

  // 生成 Token 并返回
  const token = generateJWT(user);
  res.json({ token, user });
};

(2)前端登录逻辑增强

实现了 fetchLogin 方法。

fetchLogin({
  ...formState
}).then((res) => {
  if (res.success) {
    Cookies.set('token', res.data?.accessToken);
    router.push('/ticketSearch');
  } else if (res.code === 403) {
    message.error("您的账号已被封禁,请联系管理员");
  } else {
    message.error(res.message);
  }
});

车票查询功能

12306 车票搜索用的是 Redis

12306的搜索条件

image.png

搜索条件如下:

  • 单程或者往返

  • 出发地

  • 目的地

  • 出发日或者返程日

  • 普通或者学生

  • 车次类型

  • 出发车站

  • 到达车站

  • 车次席别

  • 发车时间

  • 显示积分兑换车次

  • 显示全部可预订车次

为什么使用 Redis 缓存存储列车数据

  1. 实时性: Redis 以内存为基础,具有极低的读取延迟,可以快速响应实时查询请求,这对于需要即时更新的列车数据非常重要。单程或往返、出发日期等条件可以通过快速的 Redis 查询来满足。
  2. 缓存: Redis 是一个出色的缓存数据库,可以用于缓存热门的列车路线和查询结果,从而减轻后端数据库的负载。对于需要被查询的路线,可以将其结果缓存在 Redis 中,以提高响应速度。
  3. 简单性: Redis 的数据模型相对简单,适合存储简单的键值对或一些常规数据结构。这使得 Redis 适合存储一些搜索条件,如出发地、目的地、车次类型等,以便快速筛选结果。
  4. 部署成本: Redis 是一款轻量级的数据库,易于部署和维护。它的内存占用相对较低,可以在相对较小的硬件配置上运行,从而减少了部署成本。
  5. 只需后端查询一次: 在实际操作中,页面上的搜索条件大多是前端筛选,而只有在点击查询按钮时才会发起后端请求。因此,Redis 可以用于快速存储和检索列车数据。

车票购票

初步完成车票购票以及订单管理功能, 目前出现部分错误。

  • 购买车票

目前还没实现 订单提交部分。

image.png

  • 车票付款

image.png

订单管理

  • 订单管理

image.png

乘车人信息

  • 乘车人

image.png

并发场景

用户请求高峰

场景:

  • 12306 可能在短时间内涌入大量用户同时抢购车票。
  • 特别是春运、五一、国庆等节假日放票时,瞬间并发请求可达数百万 QPS(查询次数/秒)。
  • 用户在搜索余票、提交订单、在线支付等环节,都会产生高并发请求。

技术挑战:

  • 流量削峰:通过如虚拟队列防止瞬间请求压垮服务器 -> 消息队列MQ
  • 高效缓存:热点数据(如车次信息、余票查询)-> 使用 Redis 等缓存技术减少数据库压力。
  • CDN 分流:静态资源(如网页、图片等)-> 通过 CDN 缓存减少服务器压力。

订单提交与支付

场景:

  • 订单提交时,需要多个操作:锁定座位、扣除余票、生成订单、引导支付
  • 高并发下,可能会出现超卖订单丢失等问题。

技术挑战:

  • 分布式锁:确保同一张票不会被多个用户同时购买 -> 使用 Redis 分布式锁
  • 幂等性控制:防止用户因网络原因重复提交订单。
  • 消息队列:下单请求进入消息队列,后台异步处理并生成订单,避免数据库写入过载。 -> MQ

秒杀抢票

场景:

  • 放票瞬间,大量用户并发请求导致服务器负载飙升。
  • 恶意黄牛可能会利用脚本自动抢票,加剧竞争压力。

技术挑战:

  • 验证码 & 人机验证:防止黄牛脚本自动抢票,提高抢票公平性。-> 图形/点选/滑动/行为验证码

  • 限流策略:限制单个 IP 或单个用户的请求频率,防止恶意刷票。-> Nginx限流 or Redis计数器

  • 排队系统:用户进入排队队列,按顺序放行购买请求,避免系统崩溃。-> MQ 处理异步购票请求

并发量测试

使用 Jmeter 对系统并发能力进行测试

image.png

测试结果如下 :

线程数有线程池
20001ms内
40001s内
60002s内
80003s ~ 6s
100003s ~ 7s