目录
为什么使用kafka?
kafka是一个吞吐量分布式的发布订阅消息系统。可以处理消费者在网站上所有动作流的数据。
用户在给其他人点赞,关注,评论的时候,kafka可以记录下来。将点赞,关注,评论看成一个事件的发生。用户 点赞了以后,需要有消息通知,所以可以用kafka来发送消息,用户在点赞之后,kafka将这个事件发送到消息队列中,然后由生产者自行取数据。
Springboot整合Kafka
在项目中使用kafka,首先我们的Springboot先得整合kafka
关于如何整合Kafka可以看这个
整合以后我们就需要进行代码编写了。
创建发送事件实体Event
我们要向队列传送的数据可以封装在Event实体中
Map类型的data是为了防止以后业务有变,我们需要增加数据类型,这样就可以将他们封装在map中。
!set方法 进行修改,本来没有返回值的,但是我们可以写一个返回值
这样可以提高代码的灵活性,因为我们set了一个值以后可以返回这个对象,然后可以继续set其他属性。
为什么不用构造器呢?因为我们的属性是不确定的,而我们修改属性的时候不一定会修改哪几个属性,这样我们就需要写很多个构造器,灵活性降低。
package com.nowcoder.community.entity;
import java.util.HashMap;
import java.util.Map;
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 = data;
return this;
}
}
创建生产者
当我们使用KafkaTemplate的实体kafkaTemplate发送事件的时候,我们需要发送的是一个事件对象,但是需要传输的是一个字符串类型,怎么做呢,只需要将事件类型的对象通过JSON转换为一个JSON字符串。
@Component
public class EventProducer {
@Autowired
private KafkaTemplate kafkaTemplate;
//处理事件 将事件发送到指定主题
public void fireEvent(Event event){
kafkaTemplate.send(event.getTopic(), JSONObject.toJSONString(event));
}
}
创建消费者
消费者需要对消息进行消费,然后将内容包装到message中(所有数据,包括额外数据)
package com.nowcoder.community.event;
import com.alibaba.fastjson.JSONObject;
import com.nowcoder.community.entity.Event;
import com.nowcoder.community.entity.Message;
import com.nowcoder.community.service.MessageService;
import com.nowcoder.community.util.CommunityConstant;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@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 TOPIC_FOLLOW(ConsumerRecord record){
if (record == null || record.value() == null){
logger.error("消息的内容为空!");
return;
}
//将事件从json转为对象
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中
message.setContent(JSONObject.toJSONString(content));
messageService.addMessage(message);
}
}
最后在controller层
在这一层中加入逻辑就好了。
就像这样的
// 触发关注事件
Event event = new Event()
.setTopic(TOPIC_FOLLOW)
.setUserId(hostHolder.getUser().getId())
.setEntityType(entityType)
.setEntityId(entityId)
.setEntityUserId(entityId);
eventProducer.fireEvent(event);