Web项目(四)————异步队列的实现

目录:

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;
    }
}

猜你喜欢

转载自my.oschina.net/u/3786691/blog/1799784
今日推荐