Spring Boot 整合——Redis实现简单的发布订阅以及配置序列化方式

设置序列化方式

为key和value配置特殊的序列化工具

下面的内容,使得key使用了StringRedisSerializer序列化工具。
使用value 使用了jackson2JsonRedisSerializer的序列化工具

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // json序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        template.setConnectionFactory(factory);
        // key序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        // value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // value hasHmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = 
                new Jackson2JsonRedisSerializer(Object.class);
        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 配置序列化(解决乱码的问题),过期时间30秒
        RedisCacheConfiguration config = 
                RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(30))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

要想检测配置是否成功,可以使用下面的测试内容

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RedisApplication.class)
@Slf4j
public class TestSerializer {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 启动RedisConfig配置的时候可以此测试可以通过,否则无法通过
     */
    @Test
    public void setUserByJackson2JsonRedisSerializer() {
        User user = new User();
        user.setAge(10);
        user.setName("user");
        user.setId(1L);
        redisTemplate.opsForValue().set("test",user);
        Object o = redisTemplate.opsForValue().get("test");
        Assert.assertTrue(o instanceof User);
        Assert.assertEquals("user",((User)o).getName());
    }

    /**
     * 启动RedisConfig配置的时候可以此测试可以无法通过,否则通过
     */
    @Test
    public void setIntKey() {
        redisTemplate.opsForValue().set(10L,"10");
        Object o = redisTemplate.opsForValue().get(10L);
        Assert.assertEquals("10",o);
    }

}

因为我们设置key使用了字符串序列化方式所以setIntKey使用long类型作为key会抛出异常,而setUserByJackson2JsonRedisSerializer则会通过。

setIntKey抛出的异常

java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String

	at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:35)
	at org.springframework.data.redis.core.AbstractOperations.rawKey(AbstractOperations.java:111)
	at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:59)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95)
	at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:198)
    ......

假如我们取消掉配置的内容,因为setUserByJackson2JsonRedisSerializer尝试保存对象而不是字符串,则会抛出下面的异常。

org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [dai.samples.core.entitiy.User]

	at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:96)
	at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:126)
	at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:197)
	at dai.samples.redis.base.service.impl.TestSerializer.setUserByJackson2JsonRedisSerializer(TestSerializer.java:35)
	......

使用redis实现消息队列

根据之前的API文档我们可以发现redisTemplate的convertAndSend方法,这个方法的作用
就是向指定通道发送消息,使用这个方法我们可以实现消息队列的操作

消息发送者

首先我们设置一个通道的名称

    /**
     * 通道名称
     */
    public static String CHANNEL = "dai.channel";

然后我们创建一个消息发送者

@Component
public class PubSend {

    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 发布消息到通道
     *
     * @param message
     */
    public void sendTopicMessage(String message) {
        redisTemplate.convertAndSend(RedisListenerConfig.CHANNEL,
                message);
    }
    
}

这样我们可以使用sendTopicMessage将消息发送至dai.channel通道中

消息监听者

要监听通道的消息,我们需要一个实现了org.springframework.data.redis.connection.MessageListener
的方法。然后我们可以在onMessage中处理接收到的消息。

@Slf4j
@Component
public class MyRedisMessageListener implements MessageListener {


    @Autowired
    private RedisTemplate <String, String> redisTemplate;
    /**
     * 接收消息的处理
     * Callback for processing received objects through Redis.
     *  @param message message must not be {@literal null}.
     * @param pattern pattern matching the channel (if specified) - can be {@literal null}.
     */
    @Override 
    public void onMessage(Message message, byte[] pattern) {
        byte[] body = message.getBody();
        byte[] channel = message.getChannel();
        String msgContent = 
                (String) redisTemplate
                        .getValueSerializer()
                        .deserialize(body);
        String topic = 
                redisTemplate
                        .getStringSerializer()
                        .deserialize(channel);
        // 设置输出内容
        log.info("redis--topic:" + topic + "  body:" + msgContent);
    }
}

配置监听

现在我们有了消息监听的类,现在只要我们将此监听器和通道进行绑定即可。这个时候需要使用
org.springframework.data.redis.listener.RedisMessageListenerContainer监听容器

@Configuration
public class RedisListenerConfig {
    /**
     * 通道名称
     */
    public static String CHANNEL = "dai.channel";

    @Bean 
    RedisMessageListenerContainer container(
            RedisConnectionFactory connectionFactory,
            MyRedisMessageListener listener) {
        // 新建监听对象
        RedisMessageListenerContainer container = 
                new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        // 订阅了一个通道
        container.addMessageListener(listener, new PatternTopic(CHANNEL));
        // 这个container 可以添加多个 messageListener
        return container;
    }
}

测试接口

现在我们配置一个测试的接口,进行消息请求的测试

@RestController
@RequestMapping("pub")
public class PubController {
    
    @Autowired
    private PubSend send;

    /**
     * 测试消息发送
     * @param message
     * @return
     */
    @RequestMapping(value = "send/{message}",method = RequestMethod.GET)
    public String send(@PathVariable("message") String message) {
        send.sendTopicMessage(message);
        return message;
    }
}

现在我们访问http://localhost:8000/pub/send//testMessage地址

在这里插入图片描述

现在我们查看控制台可以看到,通道的名称和消息内容已经成功输出

2019-08-07 21:24:19.060  INFO 19468 --- [    container-4] d.s.r.p.listener.MyRedisMessageListener  : redis--topic:dai.channel  body:testMessage

本篇文章涉及的源码下载地址:https://gitee.com/daifyutils/springboot-samples

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值