SpringBoot整合Redis及Redis简介

/ Java / 没有评论 / 1809浏览

前言

由于版本原因,SpringBoot2.0整合Redis和低版本的SpringBoot不太一样,此方案基于Srping Boot 2.x

Linux下Redis安装

整合

maven 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 下面的不用加,用于分布式共享session的配置 -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

分布式共享session参考:参考

配置文件application.properties

# Redis 数据库索引
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=maxzhao
# 连接池最大连接数
spring.redis.jedis.pool.max-active=1000
# 连接池最大阻塞等待时间,负值没有限制
spring.redis.jedis.pool.max-wait=-1
# 连接池中最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=2
# 超时时间 毫秒
spring.redis.timeout=1000

注入

@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
// StringRedisTemplate是继承RedisTemplate的,
// RedisTemplate在StringRedisTemplate中泛型定义为String

StringRedisTemplate 源码

public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

StringRedisTemplate 测试

这里用的是Springboot Test

@RunWith(SpringRunner.class)
@SpringBootTest(classes = IttestApplication.class)
public class RedisTest {


    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void testRedisTemplate() {
        String prefix = "maxzhao:redisTemplate:";
        HashOperations hashOperations = redisTemplate.opsForHash();
        ValueOperations valueOps = redisTemplate.opsForValue();
        ListOperations listOps = redisTemplate.opsForList();
        SetOperations setOps = redisTemplate.opsForSet();
        ZSetOperations zSetOps = redisTemplate.opsForZSet();
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        ClusterOperations clusterOperations = redisTemplate.opsForCluster();
        Map map = Arrays.stream(new String[]{"a", "B"}).collect(Collectors.toMap(Function.identity(), Function.identity()));
        hashOperations.putAll(prefix + "hash", map);
    }

    @Test
    public void testStringRedisTemplate() {
        String prefix = "maxzhao:stringRedisTemplate:";
        HashOperations hashOperations = stringRedisTemplate.opsForHash();
        hashOperations.putAll(prefix + "hash", Arrays.stream(new String[]{"a", "b"}).collect(Collectors.toMap(Function.identity(), Function.identity())));
        hashOperations.putAll(prefix + "hash", Arrays.stream(new String[]{"c", "d"}).collect(Collectors.toMap(Function.identity(), Function.identity())));
        hashOperations.putAll(prefix + "hash", Arrays.stream(new String[]{"e", "f"}).collect(Collectors.toMap(Function.identity(), Function.identity())));
        hashOperations.put(prefix + "hash", "max", "maxvalue");// a b c d e f max
        hashOperations.get(prefix + "hash", "max");// maxvalue
        hashOperations.delete(prefix + "hash", "f");// return 1  database:a b c d e max
        hashOperations.delete(prefix + "hash", "c", "d");// return 2  database:a b e max
        hashOperations.hasKey(prefix + "hash", "max");//return true
        hashOperations.values(prefix + "hash");// return map object
        ValueOperations valueOperations = stringRedisTemplate.opsForValue();
        valueOperations.set(prefix + "value", "value");
        valueOperations.set(prefix + "value", "valueTest");//value 被覆盖
        valueOperations.get(prefix + "value");// return valueTest

        ListOperations listOps = stringRedisTemplate.opsForList();
//        listOps.remove(prefix + "list", listOps.size(prefix + "list"), 1);
        listOps.leftPush(prefix + "list", "A");
        listOps.leftPush(prefix + "list", "B");
        listOps.rightPush(prefix + "list", "C", "D");//只有 1:B  2:A
        listOps.leftPush(prefix + "list", "C");
        listOps.leftPush(prefix + "list", "D");//return 3  1:D 2:C 3:A
        listOps.range(prefix + "list", 0, listOps.size(prefix + "list"));//return 3  0:D 1:C 2:A   list下标从0开始
        listOps.leftPop(prefix + "list");//只有   1:A 返回的为B
        listOps.leftPush(prefix + "list2", "A");
        listOps.leftPush(prefix + "list2", "B");//只有 1:B  2:A
        listOps.rightPush(prefix + "list2", "C");//只有 1:B  2:A 3 C

        // set 是无序的,所有pop等获取value的操作,得到结果可能不同
        SetOperations setOps = stringRedisTemplate.opsForSet();
        setOps.add(prefix + "set", "A");//return 1
        setOps.add(prefix + "set", "A");//return 0
        setOps.add(prefix + "set", "B");//return 1
        setOps.difference(prefix + "set", "A");//return HashSet  A,B
        setOps.isMember(prefix + "set", "A");//return true
        setOps.isMember(prefix + "set", "C");//return false
        setOps.members(prefix + "set");//return HashSet  A,B
        setOps.pop(prefix + "set");// 出序列并删除 1个
        setOps.add(prefix + "set", "A","B", "C", "D", "E");//return 5
        setOps.pop(prefix + "set", 2);// 出序列并删除 2个
        setOps.add(prefix + "set", "A","B", "C", "D", "E");
        setOps.move(prefix + "set", "D", "A");//return  true  database=BCE
        // 把当前key=set的c值,move到 key=set1
        setOps.move(prefix + "set", "C", prefix + "set1");//return  true
        setOps.remove(prefix + "set", "C", "D");//删除
        // 有序的set
        ZSetOperations zSetOps = stringRedisTemplate.opsForZSet();

        GeoOperations geoOperations = stringRedisTemplate.opsForGeo();
        // 只有jedis 和 lettuce 支持Redis Cluster。
        ClusterOperations clusterOperations = stringRedisTemplate.opsForCluster();
        System.out.println("====================");
    }
}

Redis Cluster

Enabling Redis Cluster

集群的支持是基于非集群通讯构建的。RedisClusterConnection 是RedisConnection 的一个扩展,用来处理和Redis Cluster的通讯,转换错误信息到Spring DAO异常层。RedisClusterConnection 是通过RedisConnectionFactory 创建的,该工程的创建要依据于RedisClusterConfiguration配置。

Example 1. Sample RedisConnectionFactory Configuration for Redis Cluster

@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class ClusterConfigurationProperties {

    /*
     * spring.redis.cluster.nodes[0] = 127.0.0.1:7379
     * spring.redis.cluster.nodes[1] = 127.0.0.1:7380
     * ...
     */
    List<String> nodes;

    /**
     * Get initial collection of known cluster nodes in format {@code host:port}.
     *
     * @return
     */
    public List<String> getNodes() {
        return nodes;
    }

    public void setNodes(List<String> nodes) {
        this.nodes = nodes;
    }
}

@Configuration
public class AppConfig {

    /**
     * Type safe representation of application.properties
     */
    @Autowired ClusterConfigurationProperties clusterProperties;

    public @Bean RedisConnectionFactory connectionFactory() {

        return new JedisConnectionFactory(
            new RedisClusterConfiguration(clusterProperties.getNodes()));
    }
}

通过配置文件配置:

# 这个初始化的配置为驱动库指定了一组初始化集群节点。集群可以在线修改配置,但修改结果只会保存在本驱动的内存中,不会写入到配置文件中。
spring.redis.cluster.nodes: Comma delimited list of host:port pairs.
spring.redis.cluster.max-redirects: Number of allowed cluster redirections.

RedisTemplate源码

public class RedisTemplate<K, V> 
    extends RedisAccessor 
    implements RedisOperations<K, V>, BeanClassLoaderAware {
    // 是否支持事物,必须通过设置setEnableTransactionSupport(true)显式地为使用中的每个RedisTemplate启用事务支持这将强制将正在使用的重断开绑定到当前线程触发MULTI。如果事务完成时没有错误,则调用EXEC,否则丢弃。一旦在MULTI中,RedisConnection将对写操作进行排队,所有只读操作(例如键)都将通过管道传输到一个新的(非线程绑定的)RedisConnection。
    private boolean enableTransactionSupport = false;
    // 有一个this.createRedisConnectionProxy代理
    private boolean exposeConnection = false;
    // afterPropertiesSet()方法用到,此方法允许bean实例在设置了所有bean属性后执行总体配置的验证和最终初始化。
    private boolean initialized = false;
    // 默认序列化
    private boolean enableDefaultSerializer = true;
    // 默认使用JdkSerializationRedisSerializer序列化
    @Nullable
    private RedisSerializer<?> defaultSerializer;
    @Nullable
    private ClassLoader classLoader;
    // 默认使用 this.defaultSerializer 序列化
    @Nullable
    private RedisSerializer keySerializer = null;
    // 默认使用 this.defaultSerializer 序列化
    @Nullable
    private RedisSerializer valueSerializer = null;
    // 默认使用 this.defaultSerializer 序列化
    @Nullable
    private RedisSerializer hashKeySerializer = null;
    // 默认使用 this.defaultSerializer 序列化
    @Nullable
    private RedisSerializer hashValueSerializer = null;
    // 默认使用 new StringRedisSerializer(StandardCharsets.UTF_8)
    private RedisSerializer<String> stringSerializer = RedisSerializer.string();
    // 用于执行Redis脚本的ScriptExecutor
    @Nullable
    private ScriptExecutor<K> scriptExecutor;
    @Nullable
    private ValueOperations<K, V> valueOps;
    @Nullable
    private ListOperations<K, V> listOps;
    @Nullable
    private SetOperations<K, V> setOps;
    @Nullable
    private ZSetOperations<K, V> zSetOps;
    @Nullable
    private GeoOperations<K, V> geoOps;
    @Nullable
    private HyperLogLogOperations<K, V> hllOps;

    public RedisTemplate() {
    }

    public void afterPropertiesSet() ;
    // 在连接中执行给定的操作对象
    @Nullable
    public <T> T execute(RedisCallback<T> action) ;
    // 在连接中执行给定的操作对象,该对象可以公开,也可以不公开。
    @Nullable
    public <T> T execute(RedisCallback<T> action, boolean exposeConnection) ;
    // 在可公开或不可公开的连接中执行给定的操作对象。
    // 此外,可以对连接进行流水线操作。注意,管道的结果被丢弃(使其适合只写的场景)。有道翻译
    @Nullable
    public <T> T execute(RedisCallback<T> action, 
    	boolean exposeConnection, boolean pipeline);
    // 执行一个Redis会话。允许在同一个会话中执行多个操作,通过RedisOperations.multi()和RedisOperations.watch(Collection)操作启用“事务”功能。
    public <T> T execute(SessionCallback<T> session) ;
    //在管道连接上执行给定的Redis会话。允许流水线处理事务。注意,回调函数不能返回非空值,因为它被管道覆盖。
    public List<Object> executePipelined(SessionCallback<?> session) ;
    // 自定义序列化
    public List<Object> executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer) ;
    // 在管道连接上执行给定的操作对象,返回结果。注意,回调函数不能返回非空值,因为它被管道覆盖。此方法将使用默认的序列化器反序列化结果
    public List<Object> executePipelined(RedisCallback<?> action) ;
    // 自定义序列化
    public List<Object> executePipelined(RedisCallback<?> action, 
    		@Nullable RedisSerializer<?> resultSerializer) ;
    public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) ;
    public <T> T execute(RedisScript<T> script, 
    		RedisSerializer<?> argsSerializer, 
    		RedisSerializer<T> resultSerializer, 
    		List<K> keys, Object... args) ;
    public <T extends Closeable> T executeWithStickyConnection(RedisCallback<T> callback) ;
    private Object executeSession(SessionCallback<?> session) {
        return session.execute(this);
    }
    protected RedisConnection createRedisConnectionProxy(RedisConnection pm) ;
    protected RedisConnection preProcessConnection(RedisConnection connection, 
    		boolean existingConnection) ;
    @Nullable
    protected <T> T postProcessResult(@Nullable T result, 
    		RedisConnection conn, boolean existingConnection) ;
    public boolean isExposeConnection();
    public void setExposeConnection(boolean exposeConnection) ;
    public boolean isEnableDefaultSerializer() ;
    public void setEnableDefaultSerializer(boolean enableDefaultSerializer) ;
    @Nullable
    public RedisSerializer<?> getDefaultSerializer() ;
    public void setDefaultSerializer(RedisSerializer<?> serializer) ;
    public void setKeySerializer(RedisSerializer<?> serializer) ;
    public RedisSerializer<?> getKeySerializer() ;
    public void setValueSerializer(RedisSerializer<?> serializer) ;
    public RedisSerializer<?> getValueSerializer() ;
    public RedisSerializer<?> getHashKeySerializer() ;
    public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) ;
    public RedisSerializer<?> getHashValueSerializer() ;
    public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) ;
    public RedisSerializer<String> getStringSerializer() ;
    public void setStringSerializer(RedisSerializer<String> stringSerializer) ;
    public void setScriptExecutor(ScriptExecutor<K> scriptExecutor) ;
    private byte[] rawKey(Object key) ;
    private byte[] rawString(String key) ;
    private byte[] rawValue(Object value) ;
    private byte[][] rawKeys(Collection<K> keys) ; 
    private K deserializeKey(byte[] value) ;
    @Nullable
    private List<Object> deserializeMixedResults(@Nullable List<Object> rawValues, 
        @Nullable RedisSerializer valueSerializer, 
        @Nullable RedisSerializer hashKeySerializer,
        @Nullable RedisSerializer hashValueSerializer) ;
    private Set<?> deserializeSet(Set rawSet, 
    			@Nullable RedisSerializer valueSerializer) ;
    private Set<TypedTuple<V>> convertTupleValues(Set<Tuple> rawValues, 
    			@Nullable RedisSerializer valueSerializer) ;
    public List<Object> exec() ;
    public List<Object> exec(RedisSerializer<?> valueSerializer) ;
    protected List<Object> execRaw() ;
    public Boolean delete(K key) ;
    public Long delete(Collection<K> keys) ;
    public Boolean unlink(K key) ;
    public Long unlink(Collection<K> keys) ;
    public Boolean hasKey(K key) ;
    public Long countExistingKeys(Collection<K> keys) ;
    public Boolean expire(K key, long timeout, TimeUnit unit) ;
    public Boolean expireAt(K key, Date date) ;
    public void convertAndSend(String channel, Object message);
    public Long getExpire(K key) ;
    public Long getExpire(K key, TimeUnit timeUnit) ;
    public Set<K> keys(K pattern) ;
    public Boolean persist(K key) ;
    public Boolean move(K key, int dbIndex) ;
    public K randomKey() ;
    public void rename(K oldKey, K newKey) ;
    public Boolean renameIfAbsent(K oldKey, K newKey) ;
    public DataType type(K key);
    public byte[] dump(K key) ;
    public void restore(K key, byte[] value, long timeToLive, 
    				TimeUnit unit, boolean replace) ;
    public void multi() ;
    public void discard() ;
    public void watch(K key) ;
    public void watch(Collection<K> keys) ;
    public void unwatch() ;
    public List<V> sort(SortQuery<K> query) ;
    public <T> List<T> sort(SortQuery<K> query, @Nullable RedisSerializer<T> resultSerializer) ;
    public <T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper) ;
    public <T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, @Nullable RedisSerializer<S> resultSerializer) ;
    public Long sort(SortQuery<K> query, K storeKey) ;
    public void killClient(String host, int port);
    public List<RedisClientInfo> getClientList() ;
    public void slaveOf(String host, int port) ;
    public void slaveOfNoOne() ;
}