《SpringBoot2从入门到工程实战》第四篇:集成多实例Redis

作为一种高性能的缓存数据库,Redis以其简单、可靠、高性能、数据结构多等很多优点,收到越来越多开发者的喜爱,所以我们优先介绍在SpringBoot对于Redis的集成使用,并且是多Redis实例的使用实战。

本章示例工程名称:springboot_worker_multi_redis

代码地址:https://github.com/stamhe/SpringBoot-Work-Example

目录结构如下:

pom.xml内容:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.stamhe</groupId>
  <artifactId>springboot_worker_multi_redis</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>springboot_worker_multi_redis</name>
  <url>http://maven.apache.org</url>


	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
	</properties>
  
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

  	<dependencies>
    	<dependency>
        	<groupId>org.springframework.boot</groupId>
        	<artifactId>spring-boot-starter-test</artifactId>
        	<scope>test</scope>
    	</dependency>
    
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
	    
	    <!-- Spring Boot2.x 后底层不再是Jedis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency> 
        
        <dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.8</version>
		</dependency>
        
        <!-- 设置了连接池的相关参数,需要额外引入依赖 -->
        <dependency>
    		<groupId>org.apache.commons</groupId>
    		<artifactId>commons-pool2</artifactId>
		</dependency>
    
	    <dependency>
	    	<groupId>junit</groupId>
	      	<artifactId>junit</artifactId>
	      	<scope>test</scope>
	    </dependency>
	</dependencies>

	<!-- 
	使用Spring Boot微服务搭建框架,在eclipse和Idea下能正常运行,但是在打成jar包部署或者直接使用java -jar命令的时候,
	提示了xxxxxx.jar中没有主清单属性.
	添加 spring-boot-maven-plugin然后再执行mvn install 或者 mvn clean package 即可解决.
	-->
	<build>
	  <plugins>
	  	<plugin>
	  		<groupId>org.springframework.boot</groupId>
	 		<artifactId>spring-boot-maven-plugin</artifactId>
	  	</plugin>
	  </plugins>
 	</build>
</project>

新增了 spring-boot-starter-data-redis 以及 commons-pool2 两个依赖,以及存储redis所需要的序列化方案json库,这儿我们选用比较通用的jackon库。

App.java内容:

package com.stamhe.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication
@PropertySource({
	"classpath:/redis.properties"
})
public class App 
{
    public static void main( String[] args )
    {
    	SpringApplication.run(App.class, args);
    }
}

这儿我们使用了独立的 redis.properties 来放置 Redis 相关的配置信息,方便维护,同时为了方便,我们将其放置在了classpath 路径,真实项目中,应该放到 /data、/opt这样的目录去,这个我们在第二篇中有详细的说明。

redis.properties内容:

# REDIS (RedisProperties)
### 单机版
# Redis数据库索引(默认为0)
spring.redis1.database=1
# 连接URL,将覆盖主机,端口和密码(用户将被忽略),例如:redis://user:password@example.com:6379
#spring.redis.url=
# Redis服务器地址
spring.redis1.host=127.0.0.1
# Redis服务器连接端口
spring.redis1.port=6379
# Redis服务器连接密码(默认为空)
spring.redis1.password=
# 数据库连接超时时间,2.0 中该参数的类型为Duration,这里在配置的时候需要指明单位. 单位: 毫秒
spring.redis1.timeout=500
# 启用SSL支持。
#spring.redis.ssl=false


# REDIS (RedisProperties)
### 单机版
# Redis数据库索引(默认为0)
spring.redis2.database=2
# 连接URL,将覆盖主机,端口和密码(用户将被忽略),例如:redis://user:password@example.com:6379
#spring.redis.url=
# Redis服务器地址
spring.redis2.host=127.0.0.1
# Redis服务器连接端口
spring.redis2.port=6379
# Redis服务器连接密码(默认为空)
spring.redis2.password=
# 数据库连接超时时间,2.0 中该参数的类型为Duration,这里在配置的时候需要指明单位. 单位: 毫秒
spring.redis2.timeout=500
# 启用SSL支持。
#spring.redis.ssl=false


# 最大可用连接数(默认为8,负数表示无限)
spring.redisconfig.pool.max-active =  16
# 最大空闲连接数(默认为8,负数表示无限)
spring.redisconfig.pool.max-idle =  16
# 最小空闲连接数(默认为0,该值只有为正数才有作用)
spring.redisconfig.pool.min-idle =  8
# 连接分配在池被耗尽时抛出异常之前应该阻塞的最长时间量(以毫秒为单位)。使用负值可以无限期地阻止。 单位: 毫秒
spring.redisconfig.pool.max-wait =  100

UserModel.java内容:

package com.stamhe.springboot.model;

import java.io.Serializable;

public class UserModel implements Serializable {
	private static final long serialVersionUID = 8655851615465363473L;
    private Long id;
    private String username;
    private String password;
    
    
	public UserModel() {
		super();
	}

	public UserModel(Long id, String username, String password) {
		super();
		this.id = id;
		this.username = username;
		this.password = password;
	}
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
	@Override
	public String toString() {
		return "UserModel [id=" + id + ", username=" + username + ", password=" + password + "]";
	}
}

因为要通过序列化方式存储到Redis中,所以这儿的UserModel POJO需要继承自 java.io.Serializable 。

Redis1Config.java内容:

package com.stamhe.springboot.redis;

import java.time.Duration;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;


@Configuration
public class Redis1Config {
	@Value("${spring.redis1.host}")
    private String host;
	
	@Value("${spring.redis1.port}")
    private Integer port;
	
	@Value("${spring.redis1.password}")
    private String password;
	
	@Value("${spring.redis1.database}")
    private Integer database;

	@Value("${spring.redisconfig.pool.max-active}")
    private Integer maxActive;
	
    @Value("${spring.redisconfig.pool.max-idle}")
    private Integer maxIdle;
    
    @Value("${spring.redisconfig.pool.max-wait}")
    private Long maxWait;
    
    @Value("${spring.redisconfig.pool.min-idle}")
    private Integer minIdle;
    

    /*
     * 使用springdboot操作Redis时,发现key值出现 \xac\xed\x00\x05t\x00\tb,但不影响程序读写,
     * 查询资料发现redisTemplate 默认的序列化方式为 jdkSerializeable, StringRedisTemplate的默认序列化方式为StringRedisSerializer
     * 可以通过手动配置, 将redisTemplate的序列化方式进行更改
     */
    @Bean(name="redis1Template")
    public RedisTemplate<String, Object> redis1Template(@Qualifier("lettuce1Factory")LettuceConnectionFactory lettuce1Factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuce1Factory);
        
        ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		
		GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(om);
		
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());
		
		redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
		redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
		
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean(name="lettuce1Factory")
    @Primary
    public LettuceConnectionFactory lettuce1Factory(RedisStandaloneConfiguration redis1RedisConfig,
            GenericObjectPoolConfig redis1PoolConfig) {
        LettuceClientConfiguration clientConfig =
                LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(100))
                        .poolConfig(redis1PoolConfig).build();
        return new LettuceConnectionFactory(redis1RedisConfig, clientConfig);
    }

    
    @Bean(name="redis1PoolConfig")
    public GenericObjectPoolConfig poolConfig() {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxTotal(maxActive);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);
        config.setMaxWaitMillis(maxWait);
        return config;
    }

    @Bean(name="redis1RedisConfig")
    public RedisStandaloneConfiguration redisConfig() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPassword(RedisPassword.of(password));
        config.setPort(port);
        config.setDatabase(database);
        return config;
    }
}

Redis1Config 演示了使用@Value直接读取配置文件参数初始化 Redis 相关连接的示例。

Redis2Config.java内容:

package com.stamhe.springboot.redis;

import java.time.Duration;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonAutoDetect;


@Configuration
@ConfigurationProperties(prefix="spring.redis2")
public class Redis2Config {
    private String host;
    private Integer port;
    private String password;
    private Integer database;

	@Value("${spring.redisconfig.pool.max-active}")
    private Integer maxActive;
	
    @Value("${spring.redisconfig.pool.max-idle}")
    private Integer maxIdle;
    
    @Value("${spring.redisconfig.pool.max-wait}")
    private Long maxWait;
    
    @Value("${spring.redisconfig.pool.min-idle}")
    private Integer minIdle;
    
    /*
     * 使用springdboot操作Redis时,发现key值出现 \xac\xed\x00\x05t\x00\tb,但不影响程序读写,
     * 查询资料发现redisTemplate 默认的序列化方式为 jdkSerializeable, StringRedisTemplate的默认序列化方式为StringRedisSerializer
     * 可以通过手动配置, 将redisTemplate的序列化方式进行更改
     */
    @Bean(name="redis2Template")
    public RedisTemplate<String, Object> redis2Template(@Qualifier("lettuce2Factory")LettuceConnectionFactory lettuce2Factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuce2Factory);

        ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		
		GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(om);
		
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());
		
		redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
		redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
		
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean(name="lettuce2Factory")
    public LettuceConnectionFactory lettuce2Factory(RedisStandaloneConfiguration redis2RedisConfig,
            GenericObjectPoolConfig redis2PoolConfig) {
        LettuceClientConfiguration clientConfig =
                LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(100))
                        .poolConfig(redis2PoolConfig).build();
        return new LettuceConnectionFactory(redis2RedisConfig, clientConfig);
    }

    
    @Bean(name="redis2PoolConfig")
    public GenericObjectPoolConfig poolConfig() {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxTotal(maxActive);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);
        config.setMaxWaitMillis(maxWait);
        return config;
    }

    @Bean(name="redis2RedisConfig")
    public RedisStandaloneConfiguration redisConfig() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPassword(RedisPassword.of(password));
        config.setPort(port);
        config.setDatabase(database);
        return config;
    }

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public Integer getPort() {
		return port;
	}

	public void setPort(Integer port) {
		this.port = port;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Integer getDatabase() {
		return database;
	}

	public void setDatabase(Integer database) {
		this.database = database;
	}
}

Redis2Config 演示了通过 @ConfigurationProperties 结合 prefix以及getter、setter获取参数初始化 Redis 连接的示例。

HelloController.java内容:

package com.stamhe.springboot.model.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.stamhe.springboot.model.UserModel;

@Controller
@RequestMapping("/redis")
public class RedisController {
	@Autowired
	private RedisTemplate<String, Object> redis1Template;
	
	@Autowired
	private RedisTemplate<String, Object> redis2Template;

	/**
	 * http://127.0.0.1:8080/redis/ops
	 * redis的各种操作演示
	 * @return
	 */
	@RequestMapping("/ops")
	@ResponseBody
	public Map<String, Object> redisAction()
	{
		Map<String, Object> hashMap = new HashMap<String, Object>();
		hashMap.put("hello", "world");
		hashMap.put("k1", "v1");
		
		// kv
		String redisKey = "redis-k1";
		redis1Template.opsForValue().set(redisKey, "redis-v1");
		hashMap.put("redis-k1", redis1Template.opsForValue().get(redisKey));

		// hash
		String redisHashKey = "redis-hash-k1";
		redis1Template.opsForHash().put(redisHashKey, "rk1", "rv1");
		hashMap.put("redis-hash-k1", redis1Template.opsForHash().get(redisHashKey, "rk1"));
		
		// list
		String redisListKey = "redis-list-k1";
		redis1Template.opsForList().rightPush(redisListKey, "redis-list-v1");
		redis1Template.opsForList().rightPush(redisListKey, "redis-list-v2");
		redis1Template.opsForList().rightPush(redisListKey, "redis-list-v3");
		hashMap.put("redis-list-k1", redis1Template.opsForList().leftPop(redisListKey));
		Long redisListLen = redis1Template.opsForList().size(redisListKey);
		hashMap.put("redis-list-len", redisListLen.toString());
		
		// incr
		String redisIncrKey = "redis-incr-k1";
		redis1Template.opsForValue().set(redisIncrKey, 100L);
		Long redisIncrV1 = redis1Template.opsForValue().increment(redisIncrKey, 1L);
		hashMap.put("redis-incr-k1", redisIncrV1.toString());
		
		return hashMap;
	}
	
	/**
	 * http://127.0.0.1:8080/redis/object
	 * 演示了直接存储 POJO 对象的示例
	 * @return
	 */
	@RequestMapping("/object")
	@ResponseBody
	public Map<String, Object> redis2Action()
	{
		Map<String, Object> hashMap = new HashMap<String, Object>();
		
		String key = "user-1";
		redis2Template.opsForValue().set(key, new UserModel(1L, "u1", "pa"));
        final UserModel user = (UserModel) redis2Template.opsForValue().get(key);
        
        System.out.println("user = " + user);
        
        hashMap.put("1", user);
        
        return hashMap;
	}
}

这个 controller 演示了如何使用 Redis 对象操作 Redis的 kv、list、hash等数据的示例。

http://localhost:8080/redis/ops的输出:

http://localhost:8080/redis/object的输出:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据