2

我正在做一个酒店预订项目。一项新功能要求客户可以对酒店发表评论。每条评论都有一个等级(0 --> 5)。在搜索酒店页面上,每家酒店都会显示其平均评分。

添加评论非常简单,可以发布一个 CommentAddedEvent:

class CommentAddedEvent {
    private String hotelId;
    private double rating;
    //other attributes omitted
} 

在查询方面,此事件上会保留一个注释行:

 create table t_comment {
    hotel_id number,
    rating   number(2,1)
}

但是我们在如何显示酒店的平均评分方面放慢了速度。以下是我们可能的解决方案:

a) 数据库集成

在搜索酒店查询端,使用 sql 函数获取平均值:

select h.id, h.name,....,select avg(c.rating) from t_commentc , t_hotel 
where.... group by c.hotel_id.....

这看起来很简单,但我们认为它会在测试中引入更多的精力,并且可能会导致一些性能问题。

b) EventHandler 计算平均值

添加一个订阅 CommentAddedEvent 的事件处理程序并计算平均值:

public void on(CommentAddedEvent event) {
    Hotel hotel = ....
    double total = hotel.rating * hotel.comments;
    double average = (total + event.rating)/(hotel.comments + 1)
    //update hotel
}

测试很简单,但这个解决方案似乎不是幂等的。当某些事情失败时,事件处理程序可能会处理重复事件。

c) 计划任务

添加定时任务,汇总每家酒店的意见。但这对于一些酒店来说效率很低,因为上次任务后没有评论。

d) 计划任务与事件处理程序混合

使用事件处理程序来标记自上次任务以来评论的酒店:

public void on(CommentAddedEvent event) {
    int count = uncalculatedCommentsOnHotel(...);
    count++;
    //update count
}

并根据计数大于零的酒店安排摘要任务。

解决方案 D 是幂等的,并且似乎更有效。我们是否存在一些缺陷或任何其他解决方案?

4

1 回答 1

3

我会选择实现一个计算平均值的事件处理程序(选项 b)。你有几个克服幂等性问题的策略。

  1. 通过使用相关标识符在处理程序中添加条件检查,使消息处理程序具有幂等性。在这种情况下,您可以跟踪已为每家酒店处理的评论的 ID,以防止并仅处理新评论。

    public void on(CommentAddedEvent event) 
    {       
        Hotel hotel = ....
    
        if (hotel.CommentIds.Contains(event.CommentId))
            return;  // comment has already been processed
    
        double total = hotel.rating * hotel.comments;
        double average = (total + event.rating)/(hotel.comments + 1)
    
        hotel.CommentIds.Add(event.CommentId);
        //update hotel
    }
    
  2. 在基础设施级别对消息进行重复数据删除。当接收到事件时,可以对照事件存储来检查它。如果消息已经出现在事件存储中,则可以安全地丢弃它。这将确保每个事件处理程序最多接收一次事件。

通过将重复数据删除职责移至基础架构,这意味着您不需要使每个事件处理程序具有幂等性。这将允许您原来的选项 b 处理程序按原样工作。

延伸阅读

于 2014-01-16T09:00:48.330 回答