## 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 交互,同時也介紹常用內置組件,實現原子計數器、隊列以及訂閱/發佈模型。