Spring Boot集成Redis和Jedis客户端使用Redis有以下主要特点:
👉简单易用只需要添加相关依赖和简单配置,就可以直接在Spring Boot应用中使用Jedis客户端操作Redis。这大大简化了使用Redis的难度。
👉自动配置Spring Boot会根据类路径中的Jedis版本自动配置JedisConnectionFactory。我们只需要注入JedisConnectionFactory即可获取Jedis客户端实例。
👉功能丰富通过Jedis或者Lettuce客户端,我们可以使用Redis的全部功能,如字符串、列表、集合、排序集合、哈希等数据结构,以及事务、管道等功能。
👉高扩展性我们可以很容易切换不同的Redis客户端,如Jedis、Lettuce。也可以根据需要自定义RedisTemplate或添加RedisCacheManager等来改变缓存行为。
👉与Spring Cache集成Spring Boot将Redis Cache Manager与Spring Cache深度集成,我们可以直接使用Spring Cache注解来注解方法和类,实现缓存功能,而不用直接依赖Redis API。
👉与Spring Data Redis集成Spring Data Redis在Jedis和Lettuce基础上提供了更高级的抽象,我们可以调用其Repository接口来操作Redis,这提供了类似JPA的编程体验。
👉多种缓存支持Spring Boot默认集成了多种缓存技术,如Redis、Ehcache、Hazelcast等。我们只需要一行配置即可切换缓存技术,不需要更改业务逻辑代码。
👉健康监控Spring Boot Actuator可以监控RedisConnectionFactory中的连接池状态、等待连接的线程数等信息,方便我们监控Redis的运行状况。
下面我们从以下几个步骤来应用Jedis:
🌵依赖
org.springframework.boot
spring-boot-starter-parent
3.0.6
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-redis
com.alibaba
fastjson
2.0.22
org.projectlombok
lombok
1.18.22
true
redis.clients
jedis
2.9.0
🌵配置文件:
server:
port: 8080 # 设置访问端口
spring:
redis:
enable: true
jedis:
host: localhost
port: 6379
password: 123456
database: 0
maxIdle: 100
minIdle: 0
maxTotal: 100
maxWaitMillis: 500
testOnBorrow: false
testOnReturn: true
🌵RedisTestController
@RestController
@RequestMapping("/redis")
public class RedisTestController {
@Autowired
RedisComponent redisComponent;
@ResponseBody
@GetMapping("/test")
public String test(){
String val = "aa";
String key = "testRedisKey";
redisComponent.set(key, val);
String retVal = redisComponent.get(key);
return retVal;
}
}
🌵JedisConfig
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Slf4j
@Configuration
public class JedisConfig {
@Value("${spring.redis.jedis.host}")
private String redisHost;
@Value("${spring.redis.jedis.port}")
private Integer redisPort;
@Value("${spring.redis.jedis.password}")
private String redisPassword;
@Value("${spring.redis.jedis.database}")
private Integer database;
@Value("${spring.redis.jedis.maxIdle}")
private Integer maxIdle;
@Value("${spring.redis.jedis.minIdle}")
private Integer minIde;
@Value("${spring.redis.jedis.maxTotal}")
private Integer maxTotal;
@Value("${spring.redis.jedis.maxWaitMillis}")
private Long maxWaitMillis;
@Value("${spring.redis.jedis.testOnBorrow}")
private Boolean testOnBorrow;
@Value("${spring.redis.jedis.testOnReturn}")
private Boolean testOnReturn;
@Bean
public JedisPool jedisPool(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIde);
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
jedisPoolConfig.setTestOnBorrow(testOnBorrow);
jedisPoolConfig.setTestOnReturn(testOnReturn);
log.info("redis properties {}, jedisPoolConfig={}" , this.toString(),JSONObject.toJSONString(jedisPoolConfig));
JedisPool jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort,0, redisPassword, database);
return jedisPool;
}
@Override
public String toString() {
return "JedisConfig{" +
"redisHost='" + redisHost + ''' +
", redisPort=" + redisPort +
", redisPassword='" + redisPassword + ''' +
", database=" + database +
'}';
}
}
🌵RedisComponent (Redis工具类)
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.Tuple;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Created on 2019/8/19.
*/
@Slf4j
// 是否开启redis
@ConditionalOnProperty(prefix = "spring", name = "redis.enable", havingValue = "true")
@Component
public class RedisComponent {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long RELEASE_SUCCESS = 1L;
private static final Long retryMill = 1000 * 1000 * 1L;
@Autowired
private JedisPool jedisPool;
/**
* 获取分布式锁
*
* @param lockKey
* @param requestId
* @param expireTime 毫秒
*/
public Boolean tryDistributedLock(String lockKey, String requestId, int expireTime) {
log.info("tryGetDistributedLock-->>lockKey = {}, requestId = {}, expireTime = {}", lockKey, requestId, expireTime);
String result = this.setNxxxExpx(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
log.info("tryGetDistributedLock-->>lockKey = {}, requestId = {}, result = {}", lockKey, requestId, result);
return LOCK_SUCCESS.equals(result);
}
/**
* @param lockKey 上锁的key
* @param expireTime 锁的超时时间
* @param retryTimeNano 最多尝试多久 纳秒单位
* @return
* @throws InterruptedException
*/
public String lock(String lockKey, int expireTime, long retryTimeNano) {
int rt = 0;
String key = null;
try {
while (true) {
key = String.valueOf(System.nanoTime());
String result = this.setNxxxExpx(lockKey, key, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return key;
}
// 获取锁失败
rt += retryMill;//每隔1 ms执行一次
if (rt >, lockKey = {}, requestId = {}", lockKey, requestId);
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = this.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
log.info("releaseDistributedLock, lockKey = {}, requestId = {}, result = {}", lockKey, requestId, result);
if (RELEASE_SUCCESS.equals(result)) {
log.info("releaselock>>>>>>>>>>>>>>>> lockKey = {}, requestId = {}", lockKey, requestId);
return true;
}
log.info("锁已释放, lockKey = {}, requestId = {}", lockKey, requestId);
return false;
}
public final OUT redisExec(Function function) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return function.apply(jedis);
} catch (Exception e) {
e.printStackTrace();
log.info("redisExec error, errorMsg = {}", e.getMessage());
throw e;
} finally {
Optional.ofNullable(jedis).ifPresent(j -> j.close());
}
}
public String setNxxxExpx(final String key, final String value, final String nxxx, final String expx,
final int time) {
return redisExec(jedis -> jedis.set(key, value, nxxx, expx, time));
}
public Object eval(String script, List keys, List args) {
return redisExec(jedis -> jedis.eval(script, keys, args));
}
public String get(String key) {
return this.redisExec(redis -> redis.get(key));
}
public byte[] getBytes(byte[] key) {
return this.redisExec(redis -> redis.get(key));
}
public void set(String key, String val) {
this.redisExec(redis -> redis.set(key, val));
}
public Long incr(String key) {
return this.redisExec(redis -> redis.incr(key));
}
public Long append(String key, String value) {
return this.redisExec(redis->redis.append(key, value));
}
public void set(String key, String val, int expireSecond) {
this.redisTxExec(tx -> {
tx.set(key, val);
tx.expire(key, expireSecond);
});
}
public Long rpush(String key, String val) {
return this.redisExec(tx-> tx.rpush(key, val));
}
public String lpop(String key) {
return this.redisExec(tx->tx.lpop(key));
}
public void setBytes(byte[] key, byte[] val, int expireSecond) {
this.redisTxExec(tx -> {
tx.set(key, val);
tx.expire(key, expireSecond);
});
}
public Long SAdd(String key, final String... field) {
return this.redisExec((redis -> redis.sadd(key, field)));
}
public Long ZAdd(String key, double score, String mem) {
return this.redisExec((redis -> redis.zadd(key, score, mem)));
}
public Long SRem(String key, final String... field) {
return this.redisExec((redis -> redis.srem(key, field)));
}
public Long IncBy(String key, Long val) {
return this.redisExec((redis -> redis.incrBy(key, val)));
}
public Long del(String key) {
return this.redisExec(redis -> redis.del(key));
}
public Long del(byte[] key) {
return this.redisExec(redis -> redis.del(key));
}
public Long delByte(byte[] key) {
return this.redisExec(redis -> redis.del(key));
}
public Long hdel(String key, String fileds) {
return this.redisExec(redis -> redis.hdel(key, fileds));
}
public Boolean exists(String key) {
return this.redisExec(redis -> redis.exists(key));
}
public Long scard(String key) {
return this.redisExec(redis -> redis.scard(key));
}
public String srandmember(String key) {
return this.redisExec(redis -> redis.srandmember(key));
}
public Set smembers(String key) {
return this.redisExec(redis -> redis.smembers(key));
}
public boolean sismember(String key, String value) {
return this.redisExec(redis -> redis.sismember(key, value));
}
public Boolean hexists(String key, String ele) {
return this.redisExec(redis -> redis.hexists(key, ele));
}
public String hget(String key, String filed) {
return this.redisExec(redis -> redis.hget(key, filed));
}
public Map hgetall(String key) {
return redisExec(redis -> redis.hgetAll(key));
}
public Set zrange(String key, Long start, Long end) {
return this.redisExec(redis -> redis.zrange(key, start, end));
}
public Long zadd(String key, double score, String member) {
return this.redisExec(redis->redis.zadd(key, score, member));
}
public Long zrem(String key, String... mem) {
return this.redisExec(r -> r.zrem(key, mem));
}
public Set zrevrange(String key, Long start, Long end) {
return this.redisExec(redis -> redis.zrevrange(key, start, end));
}
public Set zrevrangeWithScores(String key, Long start, Long end) {
return this.redisExec(redis -> redis.zrevrangeWithScores(key, start, end));
}
public Set zrangeWithScores(String key, Long start, Long end) {
return this.redisExec(redis -> redis.zrangeWithScores(key, start, end));
}
public Long zrank(String key, String member) {
return this.redisExec(redis -> redis.zrank(key, member));
}
public Set hkeys(String key) {
return this.redisExec(redis -> redis.hkeys(key));
}
public List lrange(String key, Long start, Long end) {
return this.redisExec(redis -> redis.lrange(key, start, end));
}
public Long expire(String key, int expireSecond) {
return this.redisExec(redis -> redis.expire(key, expireSecond));
}
public Long TTL(String key) {
return this.redisExec(redis -> redis.ttl(key));
}
public void redisTxExec(Consumer consumer) {
List
简而言之,Spring Boot与Redis和Jedis的深度集成,让Redis变得异常易用和高效。凭借Spring Boot的“自动配置、起步依赖、产生型API”等特性,我们可以快速上手Redis,开发基于Redis的应用,从而享受Redis强大的功能。这大大提高了开发效率和项目的重用性。
掌握Spring Boot与Redis的集成技术,对我们成为一名专业的后端开发者来说是非常有必要的。这是一项值得长期研究与实践的技能。