Spring Data Redis 實戰

## Spring Data Redis 實戰

redis 介紹

redis是使用ANSI C 編寫的NOSQL數據庫,並提供領域專用語言DSL。其採用客戶端/服務器端模式,監聽TCP端口並接收命令。客戶端發送命令:

SET foo bar

LPUSH mylist val

命令在一個key上執行原子操作,讀寫一個或多個key值。沒有任何競爭條件發生,不需要任何鎖機制。redis所有基於key的操作,值都保存在內存中。

redis應用場景

基於內存數據,提升數據庫處理效率,特別是有大量寫操作,實時統計分析。
實現與memcached一樣的緩存功能,但可以修改緩存數據,而不僅是緩存失效。
實現消息功能,redis list可作爲隊列,實現發佈/訂閱消息。
計算單元,在redis實例中實現複雜的計算操作,如實現推薦算法引擎。

Spring Data Redis 及 redis 連接器

Spring Data 集成Jedis、JRedis、srp以及Lettuce連接器。在不同連接器之間僅有一組接口保持行爲一致性。RedisConnection 和 RedisConnectionFactory 用於從redis中獲取活動連接。同時RedisConnectionFactory也處理和後端redis之間的通訊以及把庫異常翻譯爲Spring一致的Dao異常層級,方便在不同連接器間可以不修改代碼進行切換。

依賴

Spring data Redis可以支持不同連接器,但好處是隻要你的代碼基於Spring data Redis api,則與你是用那個連接器無關。你可以通過依賴自由切換連接器。

示例使用jedis連接器:

plugins {
    id 'org.springframework.boot' version '2.1.2.RELEASE'
    id 'java'
}
apply plugin: 'io.spring.dependency-management'

repositories {
    mavenCentral()
}

dependencies {
    compile 'redis.clients:jedis:2.9.0'
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'

    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8'
}

其中 jackson-databind 實現對象序列化功能。

jedis 連接器基本配置示例

Spring Boot java 配置:

    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        RedisStandaloneConfiguration redisCfg= new RedisStandaloneConfiguration();
        redisCfg.setHostName("localhost");
        redisCfg.setPort(6379);
        redisCfg.setPassword("8888");

        return new JedisConnectionFactory(redisCfg);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory());
        Jackson2JsonRedisSerializer jacksonSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 設置value的序列化規則和 key的序列化規則
        template.setValueSerializer(jacksonSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();

        return template;
    }

這裏指定jackson的ObjectMapper作爲序列化工具,實現對象自動轉成json字符串進行傳輸和存儲。

基本類型交互

// ValueOperations, BoundValueOperations
stringRedisTemplate.opsForValue().set(key, value); 
stringRedisTemplate.boundValueOps(key).set(value); 

// HashOperations, BoundHashOperations
stringRedisTemplate.opsForHash().put(key, "hashKey", value); 
stringRedisTemplate.boundHashOps(key).put("hashKey", value); 

// ListOperations, BoundListOperations
stringRedisTemplate.opsForList().leftPush(key, value); 
stringRedisTemplate.opsForList().rightPush(key, value); 
stringRedisTemplate.opsForList().rightPop(key, 1, TimeUnit.SECONDS); 
stringRedisTemplate.opsForList().leftPop(key, 1, TimeUnit.SECONDS); 
stringRedisTemplate.boundListOps(key).leftPush(value); 
stringRedisTemplate.boundListOps(key).rightPush(value); 
stringRedisTemplate.boundListOps(key).rightPop(1, TimeUnit.SECONDS); 
stringRedisTemplate.boundListOps(key).leftPop(1, TimeUnit.SECONDS); 

// ZSetOperations, BoundZSetOperations
stringRedisTemplate.opsForZSet().add(key, "player1", 12.0d); 
stringRedisTemplate.opsForZSet().add(key, "player2", 11.0d); 
stringRedisTemplate.boundZSetOps(key).add("player1", 12.0d); 
stringRedisTemplate.boundZSetOps(key).add("player2", 11.0d);

自定義類型操作

自定義類型Student類:

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student implements Serializable {

    public enum Gender {
        /**
         * male
         */
        MALE,
        /**
         * female
         */
        FEMALE
    }

    private String id;
    private String name;
    private Gender gender;
    private int grade;
}

存儲獲取student對象:

    Student student = new Student("Eng2015002", "John Doe", Student.Gender.MALE, 1);

    redisTemplate.opsForValue().set(student.getId(),student);
    Object stu = redisTemplate.opsForValue().get(student.getId());
    log.info("Eng2015002:{}", stu);

jackson自動進行序列化和反序列化。

內置類

Spring Data Redis 提供了幾個可重用組件,如原子計數器和集合。原子計數器可以方便實現Redis自增key,而集合可以實現redis key的管理:

原子計數器示例

@Bean 
public RedisAtomicInteger init(RedisConnectionFactory factory) { 
    return new RedisAtomicInteger("myCounter", factory); 
} 
public class Example { 
    @Autowired 
    private RedisAtomicInteger atomicInteger; 

    public void increment() { 
        atomicInteger.incrementAndGet(); 
    } 
} 

Redis 集合示例

@Bean 
public Deque<String> queue(StringRedisTemplate redisTemplate) { 
    return new DefaultRedisList<>(
               redisTemplate.boundListOps("MY_KEY_FOR_QUEUE")); 
}
public class DequeExample { 

    @Resource(name = "queue") 
    private Deque<String> queue; 

    public void add(String value) { 
        queue.add(value); 
    } 

    public String getFirst() { 
        return queue.getFirst(); 
    } 

    public String getLast() { 
        return queue.getLast(); 
    } 
} 

發佈/訂閱模型

Spring Data Redis 支持基於主題的發佈訂閱模型:

監聽器及主題配置:

@Configuration
public class RedisMessageConfig {
    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;

    @Bean
    RedisMessageListenerContainer redisContainer() {
        RedisMessageListenerContainer container
            = new RedisMessageListenerContainer();
        container.setConnectionFactory(jedisConnectionFactory);
        container.addMessageListener(messageListener(), topic());
        return container;
    }

    @Bean
    MessageListenerAdapter messageListener() {
        return new MessageListenerAdapter(new RedisMessageSubscriber());
    }

    @Bean
    ChannelTopic topic() {
        return new ChannelTopic("messageQueue");
    }
}

實際處理訂閱邏輯代碼:

@Service
public class RedisMessageSubscriber implements MessageListener {

    public static List<String> messageList = new ArrayList<>();

    @Override
    public void onMessage(Message message, byte[] pattern) {
        messageList.add(message.toString());
        System.out.println("Message received: " + message.toString());
    }
}

發送消息類:

@Service
public class RedisMessagePublisher implements MessagePublisher {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ChannelTopic topic;

    public RedisMessagePublisher(RedisTemplate<String, Object> redisTemplate, ChannelTopic topic) {
        this.redisTemplate = redisTemplate;
        this.topic = topic;
    }

    @Override
    public void publish(Object message) {
        redisTemplate.convertAndSend(topic.getTopic(), message);
    }
}

總結

本文介紹了redis 及 spring data redis。通過示例展示spring data redis和redis 交互,同時也介紹常用內置組件,實現原子計數器、隊列以及訂閱/發佈模型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章