Redis 分布式锁

众所周知,redis是一个高性能的分布式key-value存储系统,在NoSQL数据库市场上,redis自己就占据了将近半壁江山,足以见到其强大之处。同时,由于redis的单线程特性,我们可以将其用作为一个消息队列。本篇文章就来讲讲如何将redis整合到spring boot中,并用作消息队列的  (本人自己在线上使用后,感觉加分布式锁大大影响了效率,还没有单机效率高)

 

 

   

为什么会出现消息队列?

  1. 异步:常见的B/S架构下,客户端向服务器发送请求,但是服务器处理这个消息需要花费的时间很长的时间,如果客户端一直等待服务器处理完消息,会造成客户端的系统资源浪费;而使用消息队列后,服务器直接将消息推送到消息队列中,由专门的处理消息程序处理消息,这样客户端就不必花费大量时间等待服务器的响应了;

  2. 解耦:传统的软件开发模式,模块之间的调用是直接调用,这样的系统很不利于系统的扩展,同时,模块之间的相互调用,数据之间的共享问题也很大,每个模块都要时时刻刻考虑其他模块会不会挂了;使用消息队列以后,模块之间不直接调用,而是通过数据,且当某个模块挂了以后,数据仍旧会保存在消息队列中。最典型的就是生产者-消费者模式,本案例使用的就是该模式;
  3. 削峰填谷:某一时刻,系统的并发请求暴增,远远超过了系统的最大处理能力后,如果不做任何处理,系统会崩溃;使用消息队列以后,服务器把请求推送到消息队列中,由专门的处理消息程序以合理的速度消费消息,降低服务器的压力。

 

二、环境准备

Java环境:jdk1.8

spring boot版本:2.2.1.RELEASE

redis-server版本:3.2.100

三、相关依赖

这里只展示与redis相关的依赖,

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

<dependency>

    <groupId>org.springframework.integration</groupId>

    <artifactId>spring-integration-redis</artifactId>

</dependency>

 这里解释一下这两个依赖:

  • 第一个依赖是对redis NoSQL的支持

  • 第二个依赖是spring integration与redis的结合,这里添加这个代码主要是为了实现分布式锁

 

 

四、代码配置

redis用作消息队列,其在spring boot中的主要表现为一RedisTemplate.convertAndSend()方法和一个MessageListener接口。所以我们要在IOC容器中注入一个RedisTemplate和一个实现了MessageListener接口的类。话不多说,先看代码

配置RedisTemplate

配置RedisTemplate的主要目的是配置序列化方式以解决乱码问题,同时合理配置序列化方式还能降低一点性能开销。

 

/**

 * 配置RedisTemplate,解决乱码问题

 */

@Bean

public RedisTemplate&lt;String, Object&gt; redisTemplate(RedisConnectionFactory factory) {

    LOGGER.debug("redis序列化配置开始");

    RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();

    template.setConnectionFactory(factory);

    // string序列化方式

    RedisSerializer serializer = new GenericJackson2JsonRedisSerializer();

    // 设置默认序列化方式

    template.setDefaultSerializer(serializer);

    template.setKeySerializer(new StringRedisSerializer());

    template.setHashValueSerializer(serializer);

    LOGGER.debug("redis序列化配置结束");

    return template;

}

代码第12行,我们配置默认的序列化方式为GenericJackson2JsonRedisSerializer代码第13行,我们配置键的序列化方式为StringRedisSerializer代码第14行,我们配置哈希表的值的序列化方式为GenericJackson2JsonRedisSerializer

RedisTemplate几种序列化方式的简要介绍

 

 生产者:

 

      主要利用   redis的convertAndSend方法把数据生产。

       

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


     @Override

    public void sendMessage(String channel, String data) {
        stringRedisTemplate.convertAndSend(channel, data);
    }

 


 消费者:

        利用监听器监听某个方法,消费数据

/**
*通过这个方法消费数据
*
*
*
*/
@Override
    public void receiveMessage(String id) {
       //业务逻辑

        
    }

相关配置

package com.newtv.ucas.config;

import com.newtv.ucas.dao.cache.impl.RedisDaoImpl;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;


/**
 * @Classname SubscriberConfig
 * @Description 作为消息队列的消费者
 * @Date 2019/5/22 4:03 PM
 * @Author by pengasan
 */
@Configuration
@AutoConfigureAfter({RedisDaoImpl.class})
public class SubscriberConfig {



    /**
     * 消息监听适配器,注入接受消息方法,输入方法名字 反射方法
     *
     * @param redis
     * @return
     */
    @Bean
    public MessageListenerAdapter getMessageListenerAdapter(RedisDaoImpl redis) {

        //当没有继承MessageListener时需要写方法名字
        return new MessageListenerAdapter(redis,"receiveMessage");
    }

    /**
     * 创建消息监听容器
     *
     * @param redisConnectionFactory
     * @param messageListenerAdapter
     * @return
     */
    @Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter,new PatternTopic("MessageNotify"));

        return redisMessageListenerContainer;
    }
}


package com.newtv.ucas.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.integration.redis.util.RedisLockRegistry;


/**
* @Classname RedisCacheAutoConfiguration
* @Description 注册redisTemplate,作为消息队列的发布者
* @Date 2019/5/22 3:37 PM
* @Author by pengasan
*/
@Configuration
public class PublisherConfig extends RedisConfig{

@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Bean
public LettuceConnectionFactory secondConnectionFactory() {
//Duration time = new
return lettuceConnectionFactory( host, password, port, database);
}




@Bean(name = "redisTemplate")
public RedisTemplate firstRedisTemplate() {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(secondConnectionFactory());
//key序列化
template.setKeySerializer(new StringRedisSerializer());
//value序列化
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}

@Bean(name = "redisStringTemplate")
public StringRedisTemplate firstStringRedisTemplate() {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(secondConnectionFactory());
template.afterPropertiesSet();
return template;
}

/**
* redis分布书锁 bean 配置
* @Description 第一个参数是redis连接池,第二个参数是锁的前缀,即取出的锁,键名为“demo-lock:KEY_NAME”,第三个参数为锁的过期时间(秒),默认为60秒,当持有锁超过该时间后自动过期。
* @return org.springframework.integration.redis.util.RedisLockRegistry
* @date 2019/12/2 3:41 PM
* @auther lixin
*/
@Bean
public RedisLockRegistry redisLockRegistry() {
return new RedisLockRegistry(secondConnectionFactory(), "subscriber-lock",30);

}


}



 

 

 

我在使用中增加了分布式锁,效率没有单机快,于是不使用分布式锁了,继续单节点消费,你们是怎么处理的呢,欢迎交流

posted @ 2019-12-04 15:22  彭阿三  阅读(1236)  评论(0编辑  收藏  举报