论坛项目进展05

第5章 Kafka,构建TB级异步消息系统
5.1阻塞队列

image-20220620140847069

注意,解决线程通信的方式不只阻塞队列一种,像wait/notify方法也可以实现,但是太麻烦。

5.2 kafka入门

image-20220620141758922

5.3spring整合kafka

image-20220620143536089

 

5.4发送系统通知

image-20220620144134017

image-20220620150021824

在entity下建Event

public class Event {
   private String topic;//主题,就是事件类型
   private int userId;//事件是谁发出的。即事件的触发者
   private int entityType;//这个事件发生在哪个实体上,比如帖子,评论
   private int entityId;
   private int entityUserId;//以及这个实体的作者是谁
   private Map<String, Object> data = new HashMap<>();//可能需要一些额外的数据需要记录
   public String getTopic() {
       return topic;
  }
   public Event setTopic(String topic) {
       this.topic = topic;
       return this;
  }
   public int getUserId() {
       return userId;
  }
   public Event setUserId(int userId) {
       this.userId = userId;
       return this;
  }
   public int getEntityType() {
       return entityType;
  }
   public Event setEntityType(int entityType) {
       this.entityType = entityType;
       return this;
  }
   public int getEntityId() {
       return entityId;
  }
   public Event setEntityId(int entityId) {
       this.entityId = entityId;
       return this;
  }
   public int getEntityUserId() {
       return entityUserId;
  }
   public Event setEntityUserId(int entityUserId) {
       this.entityUserId = entityUserId;
       return this;
  }
   public Map<String, Object> getData() {
       return data;
  }
   public Event setData(String key, Object value) {
       this.data.put(key, value);
       return this;
  }
}

新建一个event包,包下面建EventConsumer和EventProducer

@Component
public class EventProducer {
   @Autowired
   private KafkaTemplate kafkaTemplate;
   // 处理事件
   public void fireEvent(Event event) {
       // 将事件发布到指定的主题
       kafkaTemplate.send(event.getTopic(), JSONObject.toJSONString(event));
  }
}
@Component
public class EventConsumer implements CommunityConstant {
   private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);
   @Autowired
   private MessageService messageService;
   @KafkaListener(topics = {TOPIC_COMMENT, TOPIC_LIKE, TOPIC_FOLLOW})
   public void handleCommentMessage(ConsumerRecord record) {
       if (record == null || record.value() == null) {
           logger.error("消息的内容为空!");
           return;
      }
       Event event = JSONObject.parseObject(record.value().toString(), Event.class);
       if (event == null) {
           logger.error("消息格式错误!");
           return;
      }
       // 发送站内通知
       Message message = new Message();
       message.setFromId(SYSTEM_USER_ID);
       message.setToId(event.getEntityUserId());
       message.setConversationId(event.getTopic());
       message.setCreateTime(new Date());
       Map<String, Object> content = new HashMap<>();
       content.put("userId", event.getUserId());
       content.put("entityType", event.getEntityType());
       content.put("entityId", event.getEntityId());
       if (!event.getData().isEmpty()) {
           for (Map.Entry<String, Object> entry : event.getData().entrySet()) {
               content.put(entry.getKey(), entry.getValue());
          }
      }
       message.setContent(JSONObject.toJSONString(content));
       messageService.addMessage(message);//把系统消息放到message表中
  }
}

修改CommentController:

 @RequestMapping(path = "/add/{discussPostId}", method = RequestMethod.POST)
   public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) {
       comment.setUserId(hostHolder.getUser().getId());
       comment.setStatus(0);
       comment.setCreateTime(new Date());
       commentService.addComment(comment);
       // 触发评论事件
       Event event = new Event()
              .setTopic(TOPIC_COMMENT)
              .setUserId(hostHolder.getUser().getId())
              .setEntityType(comment.getEntityType())
              .setEntityId(comment.getEntityId())
              .setData("postId", discussPostId);
       if (comment.getEntityType() == ENTITY_TYPE_POST) {//如果评论的是帖子
           DiscussPost target = discussPostService.findDiscussPostById(comment.getEntityId());
           event.setEntityUserId(target.getUserId());
      } else if (comment.getEntityType() == ENTITY_TYPE_COMMENT) {//如果评论的是评论
           Comment target = commentService.findCommentById(comment.getEntityId());
           event.setEntityUserId(target.getUserId());
      }
       eventProducer.fireEvent(event);//触发事件
       if (comment.getEntityType() == ENTITY_TYPE_POST) {
           // 触发发帖事件
           event = new Event()
                  .setTopic(TOPIC_PUBLISH)
                  .setUserId(comment.getUserId())
                  .setEntityType(ENTITY_TYPE_POST)
                  .setEntityId(discussPostId);
           eventProducer.fireEvent(event);
           // 计算帖子分数
           String redisKey = RedisKeyUtil.getPostScoreKey();
           redisTemplate.opsForSet().add(redisKey, discussPostId);
      }
       return "redirect:/discuss/detail/" + discussPostId;
  }

修改LikeController:

    @RequestMapping(path = "/like", method = RequestMethod.POST)
   @ResponseBody
   public String like(int entityType, int entityId, int entityUserId, int postId) {
       User user = hostHolder.getUser();
       // 点赞
       likeService.like(user.getId(), entityType, entityId, entityUserId);
       // 数量
       long likeCount = likeService.findEntityLikeCount(entityType, entityId);
       // 状态
       int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);
       // 返回的结果
       Map<String, Object> map = new HashMap<>();
       map.put("likeCount", likeCount);
       map.put("likeStatus", likeStatus);
       // 触发点赞事件
       if (likeStatus == 1) {
           Event event = new Event()
                  .setTopic(TOPIC_LIKE)
                  .setUserId(hostHolder.getUser().getId())
                  .setEntityType(entityType)
                  .setEntityId(entityId)
                  .setEntityUserId(entityUserId)
                  .setData("postId", postId);
           eventProducer.fireEvent(event);
      }
       if(entityType == ENTITY_TYPE_POST) {
           // 计算帖子分数
           String redisKey = RedisKeyUtil.getPostScoreKey();
           redisTemplate.opsForSet().add(redisKey, postId);
      }
       return CommunityUtil.getJSONString(0, null, map);
  }

修改FollowController:

@RequestMapping(path = "/follow", method = RequestMethod.POST)
   @ResponseBody
   public String follow(int entityType, int entityId) {
       User user = hostHolder.getUser();
       followService.follow(user.getId(), entityType, entityId);
       // 触发关注事件
       Event event = new Event()
              .setTopic(TOPIC_FOLLOW)
              .setUserId(hostHolder.getUser().getId())
              .setEntityType(entityType)
              .setEntityId(entityId)
              .setEntityUserId(entityId);
       eventProducer.fireEvent(event);

       return CommunityUtil.getJSONString(0, "已关注!");
  }
   @RequestMapping(path = "/unfollow", method = RequestMethod.POST)
   @ResponseBody
   public String unfollow(int entityType, int entityId) {
       User user = hostHolder.getUser();
       followService.unfollow(user.getId(), entityType, entityId);
       return CommunityUtil.getJSONString(0, "已取消关注!");
  }
5.5显示系统通知

image-20220620152827421image-20220620153756098

把上一章节存到数据库的系统消息显示在页面上。

在MessageMapper增加:

    // 查询某个主题下最新的通知
   Message selectLatestNotice(int userId, String topic);
   // 查询某个主题所包含的通知数量
   int selectNoticeCount(int userId, String topic);
   // 查询未读的通知的数量
   int selectNoticeUnreadCount(int userId, String topic);
   // 查询某个主题所包含的通知列表
   List<Message> selectNotices(int userId, String topic, int offset, int limit);

在MessageService增加:

    public Message findLatestNotice(int userId, String topic) {
       return messageMapper.selectLatestNotice(userId, topic);
  }
   public int findNoticeCount(int userId, String topic) {
       return messageMapper.selectNoticeCount(userId, topic);
  }
   public int findNoticeUnreadCount(int userId, String topic) {
       return messageMapper.selectNoticeUnreadCount(userId, topic);
  }
   public List<Message> findNotices(int userId, String topic, int offset, int limit) {
       return messageMapper.selectNotices(userId, topic, offset, limit);
  }

在MessageController增加:

    @RequestMapping(path = "/notice/list", method = RequestMethod.GET)
   public String getNoticeList(Model model) {
       User user = hostHolder.getUser();
       // 查询评论类通知
       Message message = messageService.findLatestNotice(user.getId(), TOPIC_COMMENT);
       if (message != null) {
           Map<String, Object> messageVO = new HashMap<>();
           messageVO.put("message", message);
           String content = HtmlUtils.htmlUnescape(message.getContent());
           Map<String, Object> data = JSONObject.parseObject(content, HashMap.class);
           messageVO.put("user", userService.findUserById((Integer) data.get("userId")));
           messageVO.put("entityType", data.get("entityType"));
           messageVO.put("entityId", data.get("entityId"));
           messageVO.put("postId", data.get("postId"));
           int count = messageService.findNoticeCount(user.getId(), TOPIC_COMMENT);
           messageVO.put("count", count);
           int unread = messageService.findNoticeUnreadCount(user.getId(), TOPIC_COMMENT);
           messageVO.put("unread", unread);
           model.addAttribute("commentNotice", messageVO);
      }
       // 查询点赞类通知
       message = messageService.findLatestNotice(user.getId(), TOPIC_LIKE);
       if (message != null) {
           Map<String, Object> messageVO = new HashMap<>();
           messageVO.put("message", message);
           String content = HtmlUtils.htmlUnescape(message.getContent());
           Map<String, Object> data = JSONObject.parseObject(content, HashMap.class);
           messageVO.put("user", userService.findUserById((Integer) data.get("userId")));
           messageVO.put("entityType", data.get("entityType"));
           messageVO.put("entityId", data.get("entityId"));
           messageVO.put("postId", data.get("postId"));
           int count = messageService.findNoticeCount(user.getId(), TOPIC_LIKE);
           messageVO.put("count", count);
           int unread = messageService.findNoticeUnreadCount(user.getId(), TOPIC_LIKE);
           messageVO.put("unread", unread);
           model.addAttribute("likeNotice", messageVO);
      }
       // 查询关注类通知
       message = messageService.findLatestNotice(user.getId(), TOPIC_FOLLOW);
       if (message != null) {
           Map<String, Object> messageVO = new HashMap<>();
           messageVO.put("message", message);
           String content = HtmlUtils.htmlUnescape(message.getContent());
           Map<String, Object> data = JSONObject.parseObject(content, HashMap.class);
           messageVO.put("user", userService.findUserById((Integer) data.get("userId")));
           messageVO.put("entityType", data.get("entityType"));
           messageVO.put("entityId", data.get("entityId"));
           int count = messageService.findNoticeCount(user.getId(), TOPIC_FOLLOW);
           messageVO.put("count", count);
           int unread = messageService.findNoticeUnreadCount(user.getId(), TOPIC_FOLLOW);
           messageVO.put("unread", unread);
           model.addAttribute("followNotice", messageVO);
      }
       // 查询未读消息数量
       int letterUnreadCount = messageService.findLetterUnreadCount(user.getId(), null);
       model.addAttribute("letterUnreadCount", letterUnreadCount);
       int noticeUnreadCount = messageService.findNoticeUnreadCount(user.getId(), null);
       model.addAttribute("noticeUnreadCount", noticeUnreadCount);
       return "/site/notice";
  }
   @RequestMapping(path = "/notice/detail/{topic}", method = RequestMethod.GET)
   public String getNoticeDetail(@PathVariable("topic") String topic, Page page, Model model) {
       User user = hostHolder.getUser();
       page.setLimit(5);
       page.setPath("/notice/detail/" + topic);
       page.setRows(messageService.findNoticeCount(user.getId(), topic));
       List<Message> noticeList = messageService.findNotices(user.getId(), topic, page.getOffset(), page.getLimit());
       List<Map<String, Object>> noticeVoList = new ArrayList<>();
       if (noticeList != null) {
           for (Message notice : noticeList) {
               Map<String, Object> map = new HashMap<>();
               // 通知
               map.put("notice", notice);
               // 内容
               String content = HtmlUtils.htmlUnescape(notice.getContent());
               Map<String, Object> data = JSONObject.parseObject(content, HashMap.class);
               map.put("user", userService.findUserById((Integer) data.get("userId")));
               map.put("entityType", data.get("entityType"));
               map.put("entityId", data.get("entityId"));
               map.put("postId", data.get("postId"));
               // 通知作者
               map.put("fromUser", userService.findUserById(notice.getFromId()));

               noticeVoList.add(map);
          }
      }
       model.addAttribute("notices", noticeVoList);
       // 设置已读
       List<Integer> ids = getLetterIds(noticeList);
       if (!ids.isEmpty()) {
           messageService.readMessage(ids);
      }
       return "/site/notice-detail";
  }


posted @ 2022-06-20 16:14  zhangshuai2496689659  阅读(27)  评论(0)    收藏  举报