招聘平台的专场活动(线下招聘会、企业宣讲会、行业沙龙)需要完整的活动管理系统——创建活动、在线报名、审核、电子票、现场签到、数据统计。2023年做了专场活动系统的重构,今天把架构设计分享出来。
一、核心流程#
二、系统架构#
核心服务用 Go 写(高并发),通知等非核心服务用 PHP(快速开发)。
三、关键功能设计#
3.1 报名与限流#
热门活动报名瞬间流量大,用 Redis 限流 + 原子计数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| func (s *SignupService) Signup(activityId, userId int) error {
// 1. 幂等检查
if s.redis.SIsMember(ctx, "signup:users:"+itoa(activityId), userId).Val() {
return errors.New("已报名")
}
// 2. 名额检查(原子操作)
countKey := "signup:count:" + itoa(activityId)
count := s.redis.Incr(ctx, countKey).Val()
maxCount := s.getActivityMaxCount(activityId)
if count > maxCount {
s.redis.Decr(ctx, countKey)
return errors.New("名额已满")
}
// 3. 异步写数据库
s.queue.Publish(&SignupMessage{ActivityID: activityId, UserID: userId})
// 4. 标记已报名
s.redis.SAdd(ctx, "signup:users:"+itoa(activityId), userId)
return nil
}
|
3.2 电子票与二维码#
报名审核通过后生成电子票,用 JWT 编码票信息,二维码存 OSS:
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 生成票 Token
ticket := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"ticket_id": ticketId,
"activity_id": activityId,
"user_id": userId,
"exp": time.Now().Add(7 * 24 * time.Hour).Unix(),
})
token, _ := ticket.SignedString([]byte(secret))
// 生成二维码
qrCode := qrcode.New(token, qrcode.Medium)
qrFile, _ := qrCode.PNG(256)
oss.Upload("tickets/"+ticketId+".png", qrFile)
|
3.3 扫码签到#
现场扫码签到,用 Redis 做实时统计和防重复签到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| func (s *CheckinService) Checkin(ticketToken string) (*CheckinResult, error) {
// 1. 验证票 Token
claims, err := s.parseTicketToken(ticketToken)
if err != nil {
return nil, errors.New("无效票券")
}
// 2. 防重复签到(Redis SETNX)
checkinKey := "checkin:" + claims["ticket_id"]
if !s.redis.SetNX(ctx, checkinKey, "1", 24*time.Hour).Val() {
return nil, errors.New("已签到")
}
// 3. 异步写签到记录
s.queue.Publish(&CheckinMessage{TicketID: claims["ticket_id"]})
// 4. 实时统计+1
s.redis.Incr(ctx, "checkin:count:"+claims["activity_id"])
return &CheckinResult{Success: true, UserName: claims["user_name"]}, nil
}
|
四、性能优化#
- 活动详情页缓存:Redis 缓存活动信息,5分钟过期
- 报名异步化:报名请求先写 Redis,异步写数据库,响应时间 < 50ms
- 签到本地化:现场签到服务部署在活动现场附近,减少网络延迟
- 二维码预生成:审核通过后立即生成二维码,签到时不用实时生成
五、踩坑经验#
- 报名并发超卖:早期用"先查再写"的方式判断名额,并发下超卖。改成 Redis INCR 原子计数后解决。
- 二维码过期:票 Token 过期时间设太短,活动还没开始就过期了。改成活动结束后过期。
- 签到网络差:现场网络不稳定,签到超时。加了离线签到模式(本地记录,网络恢复后同步)。
六、总结#
专场活动系统的核心设计:
- 异步化:报名、通知、签到记录都异步处理,核心接口快速响应
- 原子操作:名额计数、防重复签到用 Redis 原子操作,防止并发问题
- 实时统计:签到数据实时更新,活动方可实时查看到场情况
- 高可用:核心服务 Go 开发,非核心 PHP,兼顾性能和开发效率
- 现场适配:考虑现场网络差的情况,支持离线签到
活动系统的特点是流量峰值高(报名开始和活动当天),平时流量低。架构设计要考虑峰值场景,用缓存、异步、原子操作扛住高并发,同时保证数据一致性。