引入核心 Jar 包 (Maven 坐標示例):
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.2</version> <!-- 建議升級至 3.x/4.x 版本 -->
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
配置 ShardedJedisPool,支援主從架構,透過 JedisPoolConfig 精細調優連接池參數。
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg index="0" ref="jedisPoolConfig"/>
<constructor-arg index="1">
<list>
<bean name="master" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis.master.host}"/>
<constructor-arg index="1" value="${redis.master.port}" type="int"/>
</bean>
<bean name="slaver" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis.slaver.host}"/>
<constructor-arg index="1" value="${redis.slaver.port}" type="int"/>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="2048"/> <!-- 最大連接數 -->
<property name="maxIdle" value="200"/> <!-- 最大閒置連接 -->
<property name="minIdle" value="50"/> <!-- 最小閒置連接 -->
<property name="maxWaitMillis" value="1500"/> <!-- 獲取連接最大等待時間(ms) -->
<property name="testOnBorrow" value="true"/> <!-- 借出時驗證有效性 -->
<property name="testWhileIdle" value="true"/> <!-- 閒置驗證 -->
<property name="testOnReturn" value="false"/> <!-- 歸還時驗證 (通常關閉) -->
<property name="timeBetweenEvictionRunsMillis" value="30000"/>
<property name="numTestsPerEvictionRun" value="1024"/>
<property name="minEvictableIdleTimeMillis" value="60000"/>
<property name="softMinEvictableIdleTimeMillis" value="10000"/>
<property name="blockWhenExhausted" value="false"/> <!-- 連接耗盡時不阻塞,直接拋異常 -->
<property name="jmxEnabled" value="true"/>
<property name="jmxNamePrefix" value="myapp"/>
</bean>
屬性檔 redis.properties 中定義 redis.master.host、redis.master.port 等佔位符值。
注入 ShardedJedisPool,封裝 Key 過期、String、List、Set、Hash、SortedSet (ZSet) 等核心資料結構操作,**統一資源獲取/歸還/異常處理邏輯**,保證連接池不洩漏。
@Service
public class RedisService {
@Resource private ShardedJedisPool shardedJedisPool;
private static final Logger logger = LoggerFactory.getLogger(RedisService.class);
// 通用資源處理模板
private ShardedJedis getResource() { return shardedJedisPool.getResource(); }
private void returnResource(ShardedJedis jedis) {
if (jedis != null) shardedJedisPool.returnResource(jedis);
}
private void returnBroken(ShardedJedis jedis) {
if (jedis != null) shardedJedisPool.returnBrokenResource(jedis);
}
// --- Key 過期 ---
public long expire(String key, int seconds) {
if (key == null || key.isEmpty()) return 0;
ShardedJedis jedis = null;
try {
jedis = getResource();
return jedis.expire(key, seconds);
} catch (Exception e) {
logger.error("EXPIRE error[key={}, seconds={}]", key, seconds, e);
returnBroken(jedis); return 0;
} finally { returnResource(jedis); }
}
// --- String 操作 ---
public boolean set(String key, String value) { /* 同模板 */ }
public boolean set(String key, String value, int seconds) { /* setex */ }
public String get(String key, String defVal) { /* 同模板 */ }
public boolean del(String key) { /* 同模板 */ }
public long incr(String key) { /* 同模板 */ }
public long decr(String key) { /* 同模板 */ }
// --- List 操作 (LPUSH/LRANGE/LTRIM/LLEN/LREM) ---
public boolean addList(String key, String... values) { /* lpush */ }
public boolean addList(String key, int seconds, String... values) { /* add + expire */ }
public List<String> getList(String key) { /* lrange 0 -1 */ }
public List<String> rangeList(String key, long start, long end) { /* lrange */ }
public long countList(String key) { /* llen */ }
public String trimList(String key, long start, long end) { /* ltrim */ }
public int removeListValue(String key, long count, String value) { /* lrem */ }
// --- Set 操作 (SADD/SREM/SISMEMBER/SMEMBERS/SCARD) ---
public boolean addSet(String key, String... values) { /* sadd */ }
public boolean addSet(String key, int seconds, String... values) { /* add + expire */ }
public boolean removeSetValue(String key, String... values) { /* srem */ }
public boolean containsInSet(String key, String value) { /* sismember */ }
public Set<String> getSet(String key) { /* smembers */ }
public long countSet(String key) { /* scard */ }
// --- Hash 操作 (HSET/HGET/HDEL/HEXISTS/HKEYS/HVALS/HLEN/HSCAN) ---
public boolean setHSet(String domain, String key, String value) { /* hset */ }
public String getHSet(String domain, String key) { /* hget */ }
public long delHSet(String domain, String... keys) { /* hdel */ }
public boolean existsHSet(String domain, String key) { /* hexists */ }
public List<Map.Entry<String,String>> scanHSet(String domain, String match) { /* hscan 迭代 */ }
public List<String> hvals(String domain) { /* hvals */ }
public Set<String> hkeys(String domain) { /* hkeys */ }
public long lenHset(String domain) { /* hlen */ }
// --- SortedSet (ZSet) 操作 (ZADD/ZRANGE/ZREVRANGE/ZRANGEBYSCORE/ZCOUNT/ZREM/ZSCORE) ---
public boolean setSortedSet(String key, long score, String value) { /* zadd */ }
public Set<String> getSoredSet(String key, long startScore, long endScore, boolean desc) { /* zrangeByScore */ }
public Set<String> getSoredSetByRange(String key, int start, int end, boolean desc) { /* zrange/zrevrange */ }
public long countSoredSet(String key, long startScore, long endScore) { /* zcount */ }
public boolean delSortedSet(String key, String value) { /* zrem */ }
public Double getScore(String key, String member) { /* zscore */ }
}
try { jedis = pool.getResource(); ... } catch { returnBroken(jedis); } finally { returnResource(jedis); } —— 異常時歸還「壞連接」,正常歸還「好連接」,防止連接池耗盡。ShardedJedis 客戶端按 Key 一致性哈希路由至對應分片,適合海量 Key、單機記憶體不足場景。注意:跨分片操作 (如 KEYS *, FLUSHALL, 事務、Lua 腳本) 不支援或行為異常。maxTotal 擬設為 (業務峰值 QPS × 平均處理時間 ms / 1000) + 閒置緩衝;testOnBorrow=true 確保借出連接可用;啟用 testWhileIdle 定期清理殭屍連接。spring-boot-starter-data-redis (底層默認 Lettuce,可切換 Jedis)。