springboot应用redis实现分布式队列订阅监听服务

redis提供了redis列表这种轻量级的队列订阅监听服务,
redis队列的特点
1相对于kafka等队列消息服务,redis队列由于是基于内存操作,所以速度更快
2处理的数据量不如kafka大
3在一些异常场景下可能会丢失消息。

springboot实现redis队列订阅监听

pom文件

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

redis队列消息发布类RedisSender

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
/**
 * 生产者
 */
@Service
public class RedisSender {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //向通道发送消息的方法
    public void sendChannelMess(String channel, String message) {
        stringRedisTemplate.convertAndSend(channel, message);
    }
}

redis队列订阅监听类RedisSubListenerConfig

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

import java.util.concurrent.*;

@Configuration
public class RedisSubListenerConfig {
    //初始化监听器
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                            MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("seckill"));
        /**
         * 如果不定义线程池,每一次消费都会创建一个线程,如果业务层面不做限制,就会导致秒杀超卖
         */
        ThreadFactory factory = new ThreadFactoryBuilder()
                .setNameFormat("redis-listener-pool-%d").build();
        Executor executor = new ThreadPoolExecutor(
                1,
                1,
                5L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000),
                factory);
        container.setTaskExecutor(executor);
        container.setSubscriptionExecutor(Executors.newFixedThreadPool(1));
        return container;
    }
    //利用反射来创建监听到消息之后的执行方法
    @Bean
    MessageListenerAdapter listenerAdapter(RedisConsumer redisReceiver) {
        return new MessageListenerAdapter(redisReceiver, "receiveMessage");
    }
    //使用默认的工厂初始化redis操作模板
    @Bean
    StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }
}

redis队列消费者类RedisConsumer

import com.seckilldemo.entity.Result;
import com.seckilldemo.redis.RedisUtil;
import com.seckilldemo.service.ISeckillService;
import com.seckilldemo.util.SeckillStatEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 消费者
 */
@Service
public class RedisConsumer {
    private static final Logger logger = LoggerFactory.getLogger(RedisConsumer.class);
    @Autowired
    private ISeckillService seckillService;
    @Autowired
    private RedisUtil redisUtil;

    public void receiveMessage(String message) {
        Thread th=Thread.currentThread();
        System.out.println("Tread name:"+th.getName());
        //收到通道的消息之后执行秒杀操作(超卖)
        String[] array = message.split(";");
        if(redisUtil.getValue(array[0])==null){//control层已经判断了,其实这里不需要再判断了
            Result result = seckillService.startSeckil(Long.parseLong(array[0]), Long.parseLong(array[1]));
            if(result.equals(Result.ok(SeckillStatEnum.SUCCESS))){
                logger.info("秒杀成功");

            }else{
                logger.info("秒杀失败");
                redisUtil.cacheValue(array[0], "ok");//秒杀结束
            }
        }else{
            logger.info("秒杀失败");
        }
    }
}

controller

  @PostMapping("/startRedisQueue")
    public Result startRedisQueue(long seckillId,long userId){
        try {
            redisUtil.cacheValue(seckillId+"", null);//秒杀结束
            if(redisUtil.getValue(seckillId+"")==null){
                //思考如何返回给用户信息ws
                redisSender.sendChannelMess("seckill",seckillId+";"+userId);
                return Result.ok();
            }else{
                return Result.error();
            }

        }catch (Exception e){
            System.out.println(e);
        }
        return Result.error();
    }

配置文件application.properties

# Redis
# 数据库索引(默认为0)
spring.redis.database=0  
# 服务器地址 变更为自己的
spring.redis.host=111.229.94.187
# 服务器连接端口
spring.redis.port=6379
# 服务器连接密码(默认为空)如果有变更为自己的
spring.redis.password=redis123
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=-1
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=100
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=3000ms

你可能感兴趣的:(springboot,reids,消息队列)