事件驱动架构实战 用Java和MySQL实现用户行为积分奖励系统

事件驱动架构设计

事件驱动架构(EDA)的核心是解耦生产者和消费者,通过事件总线传递消息。对于积分奖励系统,用户行为(如登录、下单)作为事件触发,积分服务作为消费者处理事件。

关键组件包括:

  • 事件生产者:用户服务、订单服务等业务模块
  • 事件总线:消息队列(如Kafka/RabbitMQ)
  • 事件消费者:积分计算服务
  • 存储层:MySQL数据库

Java实现事件生产者

使用Spring Boot框架创建事件发布者:

// 用户行为事件定义
public class UserBehaviorEvent {
    private Long userId;
    private String behaviorType; // LOGIN, PURCHASE等
    private LocalDateTime eventTime;
    // getters/setters
}

// 事件发布服务
@Service
public class EventPublisher {
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void publishUserBehavior(Long userId, String behaviorType) {
        UserBehaviorEvent event = new UserBehaviorEvent();
        event.setUserId(userId);
        event.setBehaviorType(behaviorType);
        event.setEventTime(LocalDateTime.now());
        eventPublisher.publishEvent(event);
    }
}

消息队列集成

使用Spring Cloud Stream连接RabbitMQ:

# application.yml
spring:
  cloud:
    stream:
      bindings:
        userBehavior-out-0:
          destination: user.behavior
          content-type: application/json
      rabbit:
        bindings:
          userBehavior-out-0:
            producer:
              routing-key-expression: "'user.behavior'"

@EnableBinding(Source.class)
public class MessageProducer {
    @Autowired
    private Source source;

    public void sendEvent(UserBehaviorEvent event) {
        source.output().send(MessageBuilder.withPayload(event).build());
    }
}

积分服务实现

MySQL表结构设计:

CREATE TABLE user_points (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    points INT DEFAULT 0,
    version INT DEFAULT 0, -- 乐观锁
    UNIQUE KEY (user_id)
);

CREATE TABLE points_transaction (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    points_change INT NOT NULL,
    behavior_type VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

积分计算逻辑:

@Service
public class PointsService {
    @Autowired
    private PointsRepository pointsRepository;

    @Transactional
    public void addPoints(Long userId, int points, String behaviorType) {
        pointsRepository.findByUserId(userId).ifPresentOrElse(
            userPoints -> {
                userPoints.setPoints(userPoints.getPoints() + points);
                pointsRepository.save(userPoints);
            },
            () -> {
                UserPoints newPoints = new UserPoints();
                newPoints.setUserId(userId);
                newPoints.setPoints(points);
                pointsRepository.save(newPoints);
            }
        );
        
        // 记录积分变动历史
        PointsTransaction transaction = new PointsTransaction();
        transaction.setUserId(userId);
        transaction.setPointsChange(points);
        transaction.setBehaviorType(behaviorType);
        transactionRepository.save(transaction);
    }
}

事件消费者实现

使用@StreamListener处理事件:

@Service
@EnableBinding(Sink.class)
public class PointsCalculator {
    @Autowired
    private PointsService pointsService;

    @StreamListener(Sink.INPUT)
    public void handleUserBehavior(UserBehaviorEvent event) {
        int points = switch(event.getBehaviorType()) {
            case "LOGIN" -> 10;
            case "PURCHASE" -> 100;
            default -> 0;
        };
        
        if(points > 0) {
            pointsService.addPoints(event.getUserId(), points, event.getBehaviorType());
        }
    }
}

事务一致性保证

采用最终一致性模式:

  1. 使用本地事务表记录事件状态
  2. 定时任务补偿未处理事件
  3. 幂等设计防止重复处理

补偿任务示例:

@Scheduled(fixedRate = 60000)
public void compensateFailedEvents() {
    eventRepository.findByStatus("PENDING").forEach(event -> {
        try {
            pointsCalculator.handleUserBehavior(event);
            event.setStatus("PROCESSED");
        } catch (Exception e) {
            event.setRetryCount(event.getRetryCount() + 1);
        }
        eventRepository.save(event);
    });
}

性能优化建议

  1. 批量处理:对高频事件(如浏览)采用批量积分计算
  2. 缓存层:使用Redis缓存用户当前积分
  3. 读写分离:将积分查询路由到只读副本
  4. 分表策略:按用户ID哈希分片存储积分记录

完整实现需根据具体业务场景调整积分规则和事件类型,事件驱动架构能有效支持系统的水平扩展和模块解耦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值