springboot使用redis队列作为后台任务处理队列

springboot使用redis队列作为后台任务处理队列

1.maven依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2. java config

    @Bean
    public JedisPoolConfig getJedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        //最大空闲连接数, 默认8个
        jedisPoolConfig.setMaxIdle(4);
        //最大连接数, 默认8个
        jedisPoolConfig.setMaxTotal(8);
        //最小空闲连接数, 默认0
        jedisPoolConfig.setMinIdle(1);
        //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
        jedisPoolConfig.setMaxWaitMillis(15000);
        //逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
        jedisPoolConfig.setMinEvictableIdleTimeMillis(300000);
        //每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
        jedisPoolConfig.setNumTestsPerEvictionRun(3);
        //一个连接在池中最小生存的时间
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(60000);
        //连接超时时是否阻塞,false时报异常,ture阻塞直到超时, 默认true
        jedisPoolConfig.setBlockWhenExhausted(true);
        return jedisPoolConfig;
    }

    @Bean(name = "jedisConnectionFactory")
    public JedisConnectionFactory getJedisConnectionFactory(JedisPoolConfig jedisPoolConfig){
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setPoolConfig(jedisPoolConfig);
        jedisConnectionFactory.setHostName(redisHost);
        jedisConnectionFactory.setPort(redisPort);
        jedisConnectionFactory.setPassword(redisPassword);
        jedisConnectionFactory.setTimeout(redisProperties.getTimeout());
        return jedisConnectionFactory;
    }

    @Bean
    public RedisTemplate getRedisTemplate(@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory){
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        return redisTemplate;
    }

3.建立一个队列

public class RedisQueue<T> {

    private BoundListOperations<String, T> listOperations;//noblocking

    private static Lock lock = new ReentrantLock();//基于底层IO阻塞考虑

    private RedisTemplate redisTemplate;

    private byte[] rawKey;

    public RedisQueue(RedisTemplate redisTemplate, String key){
        this.redisTemplate = redisTemplate;
        rawKey = redisTemplate.getKeySerializer().serialize(key);
        listOperations = redisTemplate.boundListOps(key);
    }

    /**
     * blocking 一直阻塞直到队列里边有数据
     * remove and get last item from queue:BRPOP
     * @return
     */
    public T takeFromTail(int timeout) throws InterruptedException{
        lock.lockInterruptibly();
        RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
        RedisConnection connection = connectionFactory.getConnection();
        try{
            List<byte[]> results = connection.bRPop(timeout, rawKey);
            if(CollectionUtils.isEmpty(results)){
                return null;
            }
            return (T)redisTemplate.getValueSerializer().deserialize(results.get(1));
        }finally{
            lock.unlock();
            RedisConnectionUtils.releaseConnection(connection, connectionFactory);
        }
    }

    public T takeFromTail() throws InterruptedException{
        return takeFromTail(0);
    }

    /**
     * 从队列的头,插入
     */
    public void pushFromHead(T value){
        listOperations.leftPush(value);
    }

    public void pushFromTail(T value){
        listOperations.rightPush(value);
    }

    /**
     * noblocking
     * @return null if no item in queue
     */
    public T removeFromHead(){
        return listOperations.leftPop();
    }

    public T removeFromTail(){
        return listOperations.rightPop();
    }

    /**
     * blocking 一直阻塞直到队列里边有数据
     * remove and get first item from queue:BLPOP
     * @return
     */
    public T takeFromHead(int timeout) throws InterruptedException{
        lock.lockInterruptibly();
        RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
        RedisConnection connection = connectionFactory.getConnection();
        try{
            List<byte[]> results = connection.bLPop(timeout, rawKey);
            if(CollectionUtils.isEmpty(results)){
                return null;
            }
            return (T)redisTemplate.getValueSerializer().deserialize(results.get(1));
        }finally{
            lock.unlock();
            RedisConnectionUtils.releaseConnection(connection, connectionFactory);
        }
    }

    public T takeFromHead() throws InterruptedException{
        return takeFromHead(0);
    }

}

4.容器类

一般容器类的设计最为重要, 作用是作为spring bean 拿到spring管理的相关bean, 同时可以管理多个队列等.
@Component
public class RedisTaskContainer {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private static int runTaskThreadNum = 2;//Runtime.getRuntime().availableProcessors()

    //使用一个统一维护的线程池来管理隔离线程
    private static ExecutorService es = Executors.newFixedThreadPool(runTaskThreadNum);

    @Resource
    private RedisTemplate redisTemplate;

    public static String ORDER_SEND_REDIS_QUEQUE = "order:send:redis:queue";

    //队列里边的数据泛型可以根据实际情况调整, 可以定义多个类似的队列
    private RedisQueue<Map<String, List<OrderSendBO>>> redisQueue = null;

    @PostConstruct
    private void init(){
        redisQueue = new RedisQueue(redisTemplate, ORDER_SEND_REDIS_QUEQUE);

        Consumer<Map<String, List<OrderSendBO>>> consumer = (data) -> {
            // do something
        }

        //提交线程
        for (int i = 0; i < runTaskThreadNum; i++) {
            es.execute(
                    new OrderSendRedisConsumer(this, consumer)
            );
        }
    }

    public RedisQueue<Map<String, List<OrderSendBO>>> getRedisQueue() {
        return redisQueue;
    }

}

5.消费线程类

public class OrderSendRedisConsumer extends Thread {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private RedisTaskContainer container;

    private Consumer<Map<String, List<OrderSendBO>>> consumer;

    public OrderSendRedisConsumer(RedisTaskContainer container, Consumer<Map<String, List<OrderSendBO>>> consumer){
        this.container = container;
        this.consumer = consumer;
    }

    @Override
    public void run() {
        try{
            while(true){
                Map<String, List<OrderSendBO>> value = container.getRedisQueue().takeFromTail();//cast exception? you should check.
                //逐个执行
                if(value != null){
                    try{
                        consumer.accept(value);
                    }catch(Exception e){
                        logger.error("调用失败", e);
                    }
                }
            }
        }catch(Exception e){
            logger.error("轮循线程异常退出", e);
        }
    }
}

​ 如果需要给前端页面以视觉反馈, 可以给一个任务一个标识, 存入队列之前, 也放入redis; 任务被队列消费之后, 删除这个标识; 前端页面定时轮询这个标识是否存在即可;

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Java抽奖系统后台使用Spring Boot、MyBatis和Redis队列可以有效地处理高并发情况。 首先,Spring Boot提供了一个轻量级的开发框架,可以简化Java后台开发的流程。它内置了Tomcat服务器,提供了自动配置和快速构建的功能,可快速搭建开发环境。此外,Spring Boot还具有良好的扩展性和灵活性,可以方便地集成其他框架和技术。 MyBatis是一款优秀的持久层框架,可以大幅简化数据库操作的代码。它提供了灵活的SQL映射配置,可以通过注解或XML编写SQL语句,同时也支持动态SQL。MyBatis还支持多种数据库连接池,能够提高数据库连接的效率和并发处理能力。 Redis是一款高性能的内存数据库,可作为缓存或消息队列使用。在抽奖系统中,可以将中奖结果存储在Redis中,以提高中奖查询的性能。此外,Redis还提供了发布-订阅(Publish-Subscribe)机制,可用于实现消息队列。当用户进行抽奖时,可以将请求放入Redis队列中,后台程序可以通过订阅该队列处理请求,实现并发处理使用Redis队列处理高并发可以有效地降低系统的负载和响应时间。通过将请求放入队列中,可以使请求在后台异步处理,减少前端请求等待的时间。同时,通过控制队列的长度和处理速度,还可以防止系统负载过高。 综上所述,Java抽奖系统后台使用Spring Boot、MyBatis和Redis队列可以实现高并发的处理能力,提高抽奖系统的性能和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值