1,简单介绍redis
Redis(Remote Dictionary Server)是一个开源的内存中数据结构存储系统。
主要特点:
-
内存存储: Redis 主要将数据存储在内存中,因此读写速度非常快,适合需要高性能的应用场景。
-
持久化: Redis 支持将内存中的数据持久化到磁盘,可以通过RDB快照(Snapshotting)或者日志(Append-only file, AOF)的方式实现数据持久化,保证数据在服务重启后不丢失。
-
数据结构多样性: Redis 不仅仅是键值存储(key-value store),还支持多种复杂的数据结构,每种数据结构都有专门的操作命令,使得开发者可以根据需求选择合适的数据结构存储数据。
-
原子操作: Redis 提供的命令支持原子操作,可以确保复杂操作的原子性,避免并发问题。
-
分布式: Redis 提供了主从复制(Replication)和分片(Sharding)等功能,支持构建高可用、高扩展性的分布式系统。
-
高性能: Redis 使用单线程模型来处理请求,避免了多线程间的锁竞争和上下文切换开销,因此能够达到很高的单实例性能。
-
丰富的扩展功能: Redis 提供了许多扩展功能,如事务(Transaction)、发布订阅(Pub/Sub)、Lua 脚本扩展、过期自动删除等,使其在不同场景下有更广泛的应用。
2,如何在实际项目中使用
正常项目中使用思路
- 使用redis目的是为了提高我们接口响应速度,所以经常用于查询的数据可以用redis
- 必须考虑保证redis与数据库的数据一致性
- 作为辅助提升响应的手段,redis故障不能影响服务自身的业务,也就是不能导致服务故障
- 主要是为了获取而用,所以redis数据的add,delete等操作应该由多线程进行
redis与数据库的数据一致性,后面会有出别的文章。这里主要展示如何使用
引入依赖
根据自己springBoot的版本选择合适的版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.5.4</version>
</dependency>
<!-- 使用连接池进行管理redis -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>
编写配置文件
# redis 连接信息
spring.redis.host=127.0.0.1
spring.redis.port=6379
# 这里为了信息安全使用了加密
spring.redis.password={decrypt}cm9qZXIxMjM0
# redis 连接池的配置信息
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=2
spring.redis.jedis.pool.max-wait=1500ms
关于如何在springboot中使用配置文件的加解密请查看
SpringBoot启动自动解密加密配置项_springboot environment 配置项解密-CSDN博客
编写config类
package com.luojie.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.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Value("${spring.redis.password}")
private String redisPassword;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxTotal;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private Duration maxWaitMillis;
/**
* Redis 连接工厂配置
* @return
*/
@Bean
public RedisConnectionFactory redisConnectionFactory() {
if (redisHost.contains(",")) {
// 配置集群 Redis 连接工厂
return clusterConnectionFactory();
} else {
// 配置单机 Redis 连接工厂
return standaloneConnectionFactory();
}
}
/**
* 当redis为单机情况时
* @return
*/
private RedisConnectionFactory standaloneConnectionFactory() {
// RedisStandaloneConfiguration 设置单机 Redis 配置
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(redisHost);
config.setPort(redisPort);
config.setPassword(redisPassword);
// JedisClientConfiguration 配置 Jedis 连接
JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
jedisClientConfiguration.connectTimeout(Duration.ofMillis(3500)); // connection timeout
// JedisPoolConfig 配置连接池参数
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(maxTotal);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxWaitMillis(maxWaitMillis.toMillis());
jedisClientConfiguration.usePooling().poolConfig(poolConfig);
// 使用 JedisConnectionFactory 创建连接工厂
return new JedisConnectionFactory(config, jedisClientConfiguration.build());
}
private RedisConnectionFactory clusterConnectionFactory() {
// RedisClusterConfiguration 设置集群 Redis 配置
RedisClusterConfiguration config = new RedisClusterConfiguration();
for (String node : redisHost.split(",")) {
String[] parts = node.split(":");
config.clusterNode(parts[0], Integer.parseInt(parts[1]));
}
config.setPassword(redisPassword);
// JedisClientConfiguration 配置 Jedis 连接
JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
jedisClientConfiguration.connectTimeout(Duration.ofMillis(3500)); // connection timeout
// JedisPoolConfig 配置连接池参数
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(maxTotal);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxWaitMillis(maxWaitMillis.toMillis());
jedisClientConfiguration.usePooling().poolConfig(poolConfig);
// 使用 JedisConnectionFactory 创建连接工厂
return new JedisConnectionFactory(config, jedisClientConfiguration.build());
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用StringRedisSerializer序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 使用GenericJackson2JsonRedisSerializer序列化和反序列化redis的value值
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
编写redisTemplateUtil工具类
package com.luojie.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* redis 工具类
*/
@Component
@Slf4j
public class RedisServiceUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* 普通string类型,设置值
*
* @param key
* @param value
*/
public void set(String key, String value) {
try {
redisTemplate.opsForValue().set(key, value);
} catch (Exception e) {
log.error("fail set redis key:{},value:{}, errorMsg:{}", key, value, e.getMessage());
}
}
/**
* 普通string类型,设置值并设置超时时间
*
* @param key
* @param value
*/
public void setWithExpire(String key, String value, int time, TimeUnit timeUnit) {
try {
redisTemplate.opsForValue().set(key, value, time, timeUnit);
} catch (Exception e) {
log.error("fail set redis key with expire:{},value:{}, errorMsg:{}", key,
value, e.getMessage());
}
}
/**
* 普通string类型,获取值
*
* @param key
*/
public String get(String key) {
try {
return (String) redisTemplate.opsForValue().get(key);
} catch (Exception e) {
log.error("fail get redis key:{}, errorMsg:{}", key, e.getMessage());
}
return null;
}
/**
* 普通string类型,删除key
*
* @param key
*/
public void delete(String key) {
try {
redisTemplate.delete(key);
} catch (Exception e) {
log.error("fail delete redis key:{}, errorMsg:{}", key, e.getMessage());
}
}
/**
* 普通string类型,判断key是否存在
*
* @param key
*/
public boolean exists(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
log.error("fail exists redis key:{}, errorMsg:{}", key, e.getMessage());
}
return false;
}
/**
* 普通string类型,为某个key单独设置超时时间
*
* @param key
*/
public void expire(String key, int seconds, TimeUnit timeUnit) {
try {
redisTemplate.expire(key, seconds, timeUnit);
} catch (Exception e) {
log.error("fail expire redis key:{}, errorMsg:{}", key, e.getMessage());
}
}
/**
* 普通string类型,获取key的超时时间
*
* @param key
*/
public Long getExpire(String key) {
try {
return redisTemplate.getExpire(key);
} catch (Exception e) {
log.error("fail getExpire redis key:{}, errorMsg:{}", key, e.getMessage());
}
return null;
}
/**
* hash类型,设置值
*
* @param key
* @param value
*/
public void setHash(String key, String field, String value) {
try {
redisTemplate.opsForHash().put(key, field, value);
} catch (Exception e) {
log.error("fail setHash redis key:{}, errorMsg:{}", key, e.getMessage());
}
}
/**
* hash类型,设置值
*
* @param key
* @param value
*/
public void setHashWithExpire(String key, String field, String value, int time, TimeUnit timeUnit) {
try {
redisTemplate.opsForHash().put(key, field, value);
redisTemplate.expire(key, time, timeUnit);
} catch (Exception e) {
log.error("fail setHash with expire redis key:{}, errorMsg:{}", key, e.getMessage());
}
}
/**
* hash类型,获取值
*
* @param key
*/
public String getHash(String key, String field) {
try {
return (String) redisTemplate.opsForHash().get(key, field);
} catch (Exception e) {
log.error("fail getHash redis key:{}, errorMsg:{}", key, e.getMessage());
}
return null;
}
/**
* hash类型,删除
*
* @param key
*/
public void deleteHash(String key, String field) {
try {
redisTemplate.opsForHash().delete(key, field);
} catch (Exception e) {
log.error("fail deleteHash redis key:{}, errorMsg:{}", key, e.getMessage());
}
}
}
------往上为基本功能线------
根据上面的,直接使用controller进行访问调用已经可以用了。往下这里做一下功能升级。
主要用到异步 + event
如果想详细了解异步,了解event监听器请参考
Spring的监听器使用(实用,直接拿去修改可用)-CSDN博客
创建数据module类
package com.luojie.moudle;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import java.util.concurrent.TimeUnit;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
public class RedisEventModule {
@NonNull
private String key;
private String value;
private String field;
private int timeout;
private TimeUnit timeoutUnit;
}
创建event类
BaseRedisEvent
package com.luojie.event;
import org.springframework.context.ApplicationEvent;
public abstract class BaseRedisEvent extends ApplicationEvent {
public BaseRedisEvent(Object source) {
super(source);
}
}
RedisAddEvent
package com.luojie.event;
import com.luojie.moudle.RedisEventModule;
import org.apache.commons.lang3.StringUtils;
public class RedisAddEvent extends BaseRedisEvent {
public RedisEventModule eventModule;
public RedisAddEvent(RedisEventModule source) {
super(source);
if (StringUtils.isBlank(source.getValue())) {
throw new IllegalArgumentException("redis value can't be empty");
}
this.eventModule = source;
}
}
RedisDeleteEvent
package com.luojie.event;
import com.luojie.moudle.RedisEventModule;
public class RedisDeleteEvent extends BaseRedisEvent{
public RedisEventModule eventModule;
public RedisDeleteEvent(RedisEventModule source) {
super(source);
this.eventModule = source;
}
}
创建监听类
package com.luojie.config.eventListener;
import com.luojie.event.BaseRedisEvent;
import com.luojie.event.RedisAddEvent;
import com.luojie.event.RedisDeleteEvent;
import com.luojie.moudle.RedisEventModule;
import com.luojie.util.RedisServiceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RedisListener implements ApplicationListener<BaseRedisEvent> {
@Autowired
private RedisServiceUtil redisServiceUtil;
@Override
@Async("asyncExecutor") // 使用异步线程池进行处理
public void onApplicationEvent(BaseRedisEvent redisEvent) {
if (redisEvent instanceof RedisAddEvent) {
handleAddEvent((RedisAddEvent) redisEvent);
} else if (redisEvent instanceof RedisDeleteEvent) {
handleDeleteEvent((RedisDeleteEvent) redisEvent);
}
}
private void handleAddEvent(RedisAddEvent redisEvent) {
log.info("RedisAddEvent:{}", redisEvent);
RedisEventModule module = redisEvent.eventModule;
if (StringUtils.isNotBlank(module.getField()) && module.getTimeout() != 0) {
redisServiceUtil.setHashWithExpire(module.getKey(), module.getField(), module.getValue(),
module.getTimeout(), module.getTimeoutUnit());
} else if (StringUtils.isNotBlank(module.getField())) {
redisServiceUtil.setHash(module.getKey(), module.getField(), module.getValue());
} else if (module.getTimeout() != 0){
redisServiceUtil.setWithExpire(module.getKey(), module.getValue(),
module.getTimeout(), module.getTimeoutUnit());
} else {
redisServiceUtil.set(module.getKey(), module.getValue());
}
}
private void handleDeleteEvent(RedisDeleteEvent redisEvent) {
log.info("RedisDeleteEvent:{}", redisEvent);
RedisEventModule module = redisEvent.eventModule;
if (StringUtils.isNotBlank(module.getField()) ) {
redisServiceUtil.deleteHash(module.getKey(), module.getField());
} else {
redisServiceUtil.delete(module.getKey());
}
}
}
异步线程池类
package com.luojie.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class AsyncPools {
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 设置核心线程数
executor.setMaxPoolSize(20); // 设置最大线程数
executor.setQueueCapacity(100); // 设置队列容量
executor.setThreadNamePrefix("async-executor-"); // 设置线程名前缀
executor.setKeepAliveSeconds(5000);// 设置线程最大等待时间
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 设置线程拒绝策略
executor.initialize();
return executor;
}
}
3,创建controller测试使用
controller
package com.luojie.controller;
import com.luojie.event.RedisAddEvent;
import com.luojie.event.RedisDeleteEvent;
import com.luojie.event.RojerEvent;
import com.luojie.moudle.RedisEventModule;
import com.luojie.util.RedisServiceUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisTestController {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private RedisServiceUtil redisServiceUtil;
@PostMapping("/redis/test1")
public Object redisTest1(@RequestBody RedisEventModule module) throws InterruptedException {
// 测试普通string
eventPublisher.publishEvent(new RedisAddEvent(module));
// 获取刚存入的value
Thread.sleep(3000);
String re = redisServiceUtil.get(module.getKey());
System.out.println(re);
// 测试删除
eventPublisher.publishEvent(new RedisDeleteEvent(module));
Thread.sleep(3000);
System.out.println("删除之后re的值: " + redisServiceUtil.get(module.getKey()));
return re;
}
@PostMapping("/redis/test2")
public Object redisTest2(@RequestBody RedisEventModule module) throws InterruptedException {
// 测试普通string
eventPublisher.publishEvent(new RedisAddEvent(module));
// 获取刚存入的value
Thread.sleep(3000);
String hash = redisServiceUtil.getHash(module.getKey(), module.getField());
System.out.println(hash);
eventPublisher.publishEvent(new RedisDeleteEvent(module));
Thread.sleep(3000);
System.out.println("删除之后re的值: " + redisServiceUtil.getHash(module.getKey(),
module.getField()));
return hash;
}
}
测试字符串存入删除情况
存入值已获取
已存入值删除
测试成功
测试hash测存入和删除
存入后正常获取
存入值删除成功
测试成功