Redis实现简单的消息队列

下面是自己实现的消息队列, 非常粗糙, 欢迎大家指点.

注: 该实现比较简单, 请谨慎用于生产环境.
"Don't talk, show the code"

1. Redis管理类:

package pk.api.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by cliff on 17/9/18.
 */
public class RedisKit {

    /**
     * 连接池.
     */
    private JedisPool jedisPool;

    /**
     * 默认的redis管理器.
     */
    private static final RedisKit me = new RedisKit("localhost", 6379);

    /**
     * 获取默认的管理器.
     *
     * @return 默认的redis管理器
     */
    public static RedisKit me() {
        return me;
    }

    /**
     * 构造器.
     *
     * @param ip   主机
     * @param port 端口
     */
    public RedisKit(String ip, int port) {
        jedisPool = new JedisPool(ip, port);
    }

    /**
     * 在队列右侧添加.
     *
     * @param key    键
     * @param values 值
     * @return 保存的数量
     */
    public Long rpush(String key, Object... values) {
        Jedis jedis = jedisPool.getResource();
        try {
            List<byte[]> list = new ArrayList<>();
            for (Object object : values) {
                list.add(serialize(object));
            }
            return jedis.rpush(key.getBytes(), list.toArray(new byte[values.length][]));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
        return 0l;
    }

    /**
     * 从队列左侧取出 @count数量的元素.
     *
     * @param key   键
     * @param start 索引的开始
     * @param count 数量
     * @param <T>   对象类型
     * @return 列表
     */
    public <T> List<T> lrange(String key, int start, int count) {
        Jedis jedis = jedisPool.getResource();
        List<T> resultList = new ArrayList<>();
        try {
            List<byte[]> lrange = jedis.lrange(key.getBytes(), start, start + count - 1);
            for (byte[] bytes : lrange) {
                resultList.add((T) deserialize(bytes));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
        return resultList;
    }

    /**
     * 从队列左侧删除元素.
     *
     * @param key   键
     * @param count 要删除的等于 @value的个数.大于0 表示从左侧开始, 小于0表示从右侧开始.
     * @param value 值
     * @return 删除的个数
     */
    public long lrem(String key, long count, Object value) {
        Jedis jedis = jedisPool.getResource();
        try {
            return jedis.lrem(key.getBytes(), count, serialize(value));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
        return 0l;
    }


    //序列化对象
    private byte[] serialize(Object object) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutput objectOutput = new ObjectOutputStream(out);
        objectOutput.writeObject(object);
        return out.toByteArray();
    }

    //反序列化
    private Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream);
        return inputStream.readObject();
    }

}

2. 消息队列:

package pk.api.redis;

import java.util.List;

/**
 * redis实现的队列.
 */
public class RedisQueue {
    /**
     * 队列名称.
     */
    private String queueName;

    public RedisQueue(String queueName) {
        this.queueName = queueName;
    }

    /**
     * 放入消息.
     */
    public void pushMessage(final Message message) {
        RedisKit.me().rpush(queueName, message);
    }

    /**
     * 取出消息,却不删除.
     */
    List<Message> batchPopMessage(int count) {
        return RedisKit.me().lrange(queueName, 0, count - 1);
    }

    /**
     * 删除消息.
     */
    void delMsg(Message message, int count) {
        RedisKit.me().lrem(queueName, count, message);
    }
}

3. 消息对象

package pk.api.redis;

import pk.api.uuid.UUIDKit;

import java.io.Serializable;

/**
 * Created by cliff on 17/9/14.
 */
public final class Message implements Serializable {
    /**
     * 序列化id.
     */
    private static final long serialVersionUID = -1725153377631087068L;

    /**
     * 消息id, 唯一标识.
     */
    private String id;
    /**
     * 消息体, json格式的字符串.
     */
    private String messageBody;
    /**
     * 延迟执行时间.
     */
    private long delaySeconds;

    /**
     * 创建时间.
     */
    private long createTime;

    /**
     * 生命时长,单位秒,默认5天.
     */
    private long lifespan = 3600 * 24 * 5l;

    /**
     * 构造函数.
     */
    public Message() {
        this.id = UUIDKit.generateUUID();
        this.createTime = System.currentTimeMillis();
    }

    /**
     * 获取消息体.
     *
     * @return 消息体
     */
    public String getMessageBody() {
        return messageBody;
    }

    public String getId() {
        return id;
    }

    /**
     * 设置消息体.
     *
     * @param messageBody 消息体
     */
    public void setMessageBody(String messageBody) {
        if (messageBody == null) {
            throw new IllegalArgumentException("message body can not be null");
        }
        this.messageBody = messageBody;
    }

    /**
     * 拿到延迟秒数.
     *
     * @return
     */
    public long getDelaySeconds() {
        return delaySeconds;
    }

    /**
     * 设置延迟秒数.
     *
     * @param delaySeconds 单位秒
     */
    public void setDelaySeconds(long delaySeconds) {
        this.delaySeconds = delaySeconds;
    }

    /**
     * 获取创建时间.
     *
     * @return 创建时间
     */
    public long getCreateTime() {
        return createTime;
    }

    /**
     * 获取生命时长.
     *
     * @return 生命时长
     */
    public long getLifespan() {
        return lifespan;
    }

    /**
     * 设置生命时长.
     *
     * @param lifespan 单位秒
     */
    public void setLifespan(long lifespan) {
        this.lifespan = lifespan;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Message)) {
            return false;
        }
        Message another = (Message) obj;
        return this.id.equals(another.getId());
    }

    @Override
    public int hashCode() {
        return getId().hashCode();
    }
}

4. 处理消息的线程抽象类, 客户端可继承此类, 实现具体逻辑

package pk.api.redis;

import java.util.List;

/**
 * Created by cliff on 17/9/15.
 */
public abstract class RedisQueueProcessThread extends Thread {
//    public static final Log LOGGER = Log.getLog(RedisQueueProcessThread.class);

    /**
     * 消息队列.
     */
    private RedisQueue queue;
    /**
     * 每次请求获取的消息数量.
     */
    private int msgCountPerRequest;

    /**
     * 构造器.
     *
     * @param queue              消息队列
     * @param msgCountPerRequest 每次请求获取的消息数量
     */
    public RedisQueueProcessThread(final RedisQueue queue, final int msgCountPerRequest) {
        this.queue = queue;
        this.msgCountPerRequest = msgCountPerRequest;
    }

    @Override
    public void run() {
        //1.取出消息
        //2.处理消息
        //3.处理成功: 删除消息; 失败,跳出
        while (true) {
            List<Message> messages;
            try {
                messages = queue.batchPopMessage(msgCountPerRequest);
            } catch (Exception e) {
//                LOGGER.error("批量获取消息失败", e);
                System.out.println("批量获取消息失败");
                continue;
            }
            for (Message message : messages) {
                boolean success = true;
                try {
                    //1.计算消息的执行时间
                    long executionTime = message.getCreateTime() + message.getDelaySeconds() * 1000;
                    if (System.currentTimeMillis() < executionTime) {
                        continue;
                    }
                    //2.如果生命时长过期, 直接删除
                    long lifeEndTime = message.getCreateTime() + message.getLifespan() * 1000;
                    if (System.currentTimeMillis() > lifeEndTime) {
                        delMsg(queue, message);
                        continue;
                    }
                    //3.准备执行
                    processMessage(message);
                } catch (Exception e) {
                    //消息处理失败, 不做处理
//                    LOGGER.error("消息处理失败" + message.getMessageBody(), e);
                    System.out.println("消息处理失败" + message.getMessageBody());
                    success = false;
                }
                if (success) {
                    //4.执行成功后,删除消息
                    delMsg(queue, message);
                }
            }
        }
    }


    /**
     * 删除消息, catch异常.
     */
    private void delMsg(final RedisQueue queue, final Message message) {
        try {
            queue.delMsg(message, 1);
        } catch (Exception e) {
//            LOGGER.error("消息删除失败" + message.getMessageBody(), e);
            System.out.println("消息删除失败" + message.getMessageBody());
        }
    }

    /**
     * 具体的处理消息逻辑.
     *
     * @param message 消息
     */
    protected abstract void processMessage(Message message);
}

 

posted on 2017-09-18 15:42  老实的程序员  阅读(390)  评论(1编辑  收藏  举报

导航