目录:
1.概述
2.JedisAdapter
3.EventModel
4.EventHandler
5.LikeHandler
6.EventProducer
7.EventConsumer
1.概述
在一个网站中,一个业务发生的同时,还有一些后续业务需要发生。
比如点赞,除了完成点赞功能外,还有一系列,比如提醒被点赞的用户等等,为了能够实现这些操作并且不拖慢单纯点赞功能的实现,我们将这些使用异步队列实现。
处理流程如下图:
Biz为业务部门,理解为点赞的实现,也就是在实现点赞的同时通过EventProducer发送一个事件;
这个事件进入队列等待,队列另一头,有一个EventConsumer,不断消费事件;
EventConsumer下面有很多EventHandler,只要EventHandler发现自己需要处理的事件类型,就会进行相应的操作。
优点:①后续业务的实现,不会拖慢主业务。②如果后续业务的服务器挂掉,只要重启,继续从优先队列消费事件即可。
2.JedisAdapter
在jedisAdapter中添加如下方法
public void setObject(String key, Object obj) {//将一个对象转换为一个jsoon串存入set中
set(key, JSON.toJSONString(obj));
}
public <T> T getObject(String key, Class<T> clazz) {//从set中取出json串,并转换为相应object
String value = get(key);
if (value != null) {
return JSON.parseObject(value, clazz);
}
return null;
}
3.EventModel
即发送的队列的事件模型,只有一些基本属性和get、set方法。
其中一些set的return 设置为this,是因为方便连续set多个属性。
package com.nowcoder.async;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
/**
* Created by nowcoder on 2016/7/14.
*/
public class EventModel {
private EventType type;
private int actorId;
private int entityId;
private int entityType;
private int entityOwnerId;
private Map<String, String> exts = new HashMap<>();
public Map<String, String> getExts() {
return exts;
}
public EventModel() {
}
public EventModel(EventType type) {
this.type = type;
}
public String getExt(String name) {
return exts.get(name);
}
public EventModel setExt(String name, String value) {
exts.put(name, value);
return this;
}
public EventType getType() {
return type;
}
public EventModel setType(EventType type) {
this.type = type;
return this;
}
public int getActorId() {
return actorId;
}
public EventModel setActorId(int actorId) {
this.actorId = actorId;
return this;
}
public int getEntityId() {
return entityId;
}
public EventModel setEntityId(int entityId) {
this.entityId = entityId;
return this;
}
public int getEntityType() {
return entityType;
}
public EventModel setEntityType(int entityType) {
this.entityType = entityType;
return this;
}
public int getEntityOwnerId() {
return entityOwnerId;
}
public EventModel setEntityOwnerId(int entityOwnerId) {
this.entityOwnerId = entityOwnerId;
return this;
}
}
EventType
package com.nowcoder.async;
/**
* Created by nowcoder on 2016/7/14.
*/
public enum EventType {//枚举类
LIKE(0),
COMMENT(1),
LOGIN(2),
MAIL(3);
private int value;
EventType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
4.EventHandler
设计为一个接口,handler都实现此接口。
package com.nowcoder.async;
import java.util.List;
/**
* Created by nowcoder on 2016/7/14.
*/
public interface EventHandler {
void doHandle(EventModel model);//处理此事件
List<EventType> getSupportEventTypes();//添加监视的事件类型
}
5.LikeHandler
实现EventHandler接口。
package com.nowcoder.async.handler;
import com.nowcoder.async.EventHandler;
import com.nowcoder.async.EventModel;
import com.nowcoder.async.EventType;
import com.nowcoder.model.Message;
import com.nowcoder.model.User;
import com.nowcoder.service.MessageService;
import com.nowcoder.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* Created by nowcoder on 2016/7/14.
*/
@Component
public class LikeHandler implements EventHandler {
@Autowired
MessageService messageService;
@Autowired
UserService userService;
@Override
public void doHandle(EventModel model) {
Message message = new Message();
User user = userService.getUser(model.getActorId());
message.setToId(model.getEntityOwnerId());
message.setContent("用户" + user.getName() +
" 赞了你的资讯,http://127.0.0.1:8080/news/"
+ String.valueOf(model.getEntityId()));
// SYSTEM ACCOUNT
message.setFromId(3);
message.setCreatedDate(new Date());
messageService.addMessage(message);
}
@Override
public List<EventType> getSupportEventTypes() {//只关注“like”类型的事件
return Arrays.asList(EventType.LIKE);
}
}
6.EventProducer
负责将事件添加到队列中。
package com.nowcoder.async;
import com.alibaba.fastjson.JSONObject;
import com.nowcoder.util.JedisAdapter;
import com.nowcoder.util.RedisKeyUtil;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* Created by nowcoder on 2016/7/14.
*/
@Service
public class EventProducer {
@Autowired
JedisAdapter jedisAdapter;
public boolean fireEvent(EventModel eventModel) {
try {
String json = JSONObject.toJSONString(eventModel);//生成value
String key = RedisKeyUtil.getEventQueueKey();//生成key
jedisAdapter.lpush(key, json);//将事件添加到队列中
return true;
} catch (Exception e) {
return false;
}
}
}
7.EventConsumer
负责从队列中消费事件。
package com.nowcoder.async;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.nowcoder.async.handler.LoginExceptionHandler;
import com.nowcoder.util.JedisAdapter;
import com.nowcoder.util.RedisKeyUtil;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by nowcoder on 2016/7/14.
*/
@Service
public class EventConsumer implements InitializingBean, ApplicationContextAware {
//InitializingBean 通过实现此接口的afterPropertiesSet()方法记录哪些Event需要哪些handler来处理
//ApplicationContextAware 通过实现此接口的setApplicationContext方法获取上下文
private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);
//用来存储一个事件类型对应的所有的eventhandler,下次有该事件产生时,即可直接调用对应的list
private Map<EventType, List<EventHandler>> config = new HashMap<>();
private ApplicationContext applicationContext;
@Autowired
private JedisAdapter jedisAdapter;
@Override
public void afterPropertiesSet() throws Exception {//记录哪些Event需要哪些handler来处理
//找出上下文中所有实现了EventHandler接口的类,存入beans
Map<String, EventHandler> beans = applicationContext.getBeansOfType(EventHandler.class);
if (beans != null) {
//遍历所有的handler,将他们存入他们所监听的eventType对应的list中
for (Map.Entry<String, EventHandler> entry : beans.entrySet()) {
List<EventType> eventTypes = entry.getValue().getSupportEventTypes();//查看事件的监视事件
for (EventType type : eventTypes) {
if (!config.containsKey(type)) {
config.put(type, new ArrayList<EventHandler>());
}
// 注册每个事件的处理函数
config.get(type).add(entry.getValue());
}
}
}
// 启动线程去消费事件
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 从队列一直消费
while (true) {
String key = RedisKeyUtil.getEventQueueKey();
List<String> messages = jedisAdapter.brpop(0, key);//从redis的队列中取出事件,并存入list中
// 第一个元素是队列名字,跳过
for (String message : messages) {
if (message.equals(key)) {
continue;
}
EventModel eventModel = JSON.parseObject(message, EventModel.class);
// 找到这个事件的处理handler列表
if (!config.containsKey(eventModel.getType())) {
logger.error("不能识别的事件");
continue;
}
for (EventHandler handler : config.get(eventModel.getType())) {//处理他的所有的handler
handler.doHandle(eventModel);
}
}
}
}
});
thread.start();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}