001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.components.cache.redis; 017 018import com.jfinal.plugin.ehcache.IDataLoader; 019import io.jboot.Jboot; 020import io.jboot.components.cache.JbootCacheBase; 021import io.jboot.components.cache.JbootCacheConfig; 022import io.jboot.exception.JbootIllegalConfigException; 023import io.jboot.support.redis.JbootRedis; 024import io.jboot.support.redis.JbootRedisManager; 025import io.jboot.support.redis.RedisScanResult; 026import io.jboot.utils.StrUtil; 027 028import java.util.ArrayList; 029import java.util.List; 030import java.util.Set; 031 032 033public class JbootRedisCacheImpl extends JbootCacheBase { 034 035 036 private JbootRedis redis; 037 private JbootRedisCacheConfig cacheConfig; 038 private String redisCacheNamesKey = "jboot_cache_names"; 039 private String globalKeyPrefix = ""; 040 041 042 public JbootRedisCacheImpl(JbootCacheConfig config) { 043 super(config); 044 045 cacheConfig = Jboot.config(JbootRedisCacheConfig.class); 046 047 if (StrUtil.isNotBlank(cacheConfig.getGlobalKeyPrefix())) { 048 globalKeyPrefix = cacheConfig.getGlobalKeyPrefix() + ":"; 049 redisCacheNamesKey = globalKeyPrefix + redisCacheNamesKey; 050 } 051 052 053 if (cacheConfig.isConfigOk()) { 054 redis = JbootRedisManager.me().getRedis(cacheConfig); 055 } else { 056 redis = Jboot.getRedis(); 057 } 058 059 if (redis == null) { 060 throw new JbootIllegalConfigException("Can not get redis component in JbootRedisCacheImpl, Please check your jboot.properties " + 061 "and config jboot.cache.redis.host or jboot.redis.host correct."); 062 } 063 } 064 065 066 @Override 067 public <T> T get(String cacheName, Object key) { 068 T value = redis.get(buildKey(cacheName, key)); 069 070 if (config.isDevMode()) { 071 println("RedisCache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); 072 } 073 return value; 074 } 075 076 @Override 077 public void put(String cacheName, Object key, Object value) { 078 if (value == null) { 079 remove(cacheName, key); 080 return; 081 } 082 redis.set(buildKey(cacheName, key), value); 083 redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); 084 085 if (config.isDevMode()) { 086 println("RedisCache PUT: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); 087 } 088 } 089 090 @Override 091 public void put(String cacheName, Object key, Object value, int liveSeconds) { 092 if (value == null) { 093 remove(cacheName, key); 094 return; 095 } 096 if (liveSeconds <= 0) { 097 put(cacheName, key, value); 098 return; 099 } 100 101 redis.setex(buildKey(cacheName, key), liveSeconds, value); 102 redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); 103 104 if (config.isDevMode()) { 105 println("RedisCache PUT: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); 106 } 107 } 108 109 110 @Override 111 public void remove(String cacheName, Object key) { 112 redis.del(buildKey(cacheName, key)); 113 114 if (config.isDevMode()) { 115 println("RedisCache REMOVE: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"]"); 116 } 117 } 118 119 120 @Override 121 public void removeAll(String cacheName) { 122 String cursor = "0"; 123 int scanCount = 1000; 124 boolean continueState = true; 125 String scanName = globalKeyPrefix + buildCacheName(cacheName); 126 do { 127 RedisScanResult<String> redisScanResult = redis.scan(scanName + ":*", cursor, scanCount); 128 List<String> scanKeys = redisScanResult.getResults(); 129 cursor = redisScanResult.getCursor(); 130 131 if (scanKeys != null && scanKeys.size() > 0) { 132 redis.del(scanKeys.toArray(new String[scanKeys.size()])); 133 } 134 135 if (redisScanResult.isCompleteIteration()) { 136 continueState = false; 137 } 138 } while (continueState); 139 140 redis.srem(buildCacheName(redisCacheNamesKey), cacheName); 141 142 if (config.isDevMode()) { 143 println("RedisCache REMOVEALL: cacheName[" +buildCacheName(cacheName)+ "]"); 144 } 145 } 146 147 148 @Override 149 public <T> T get(String cacheName, Object key, IDataLoader dataLoader) { 150 Object data = get(cacheName, key); 151 if (data == null) { 152 data = dataLoader.load(); 153 put(cacheName, key, data); 154 } 155 156 return (T) data; 157 } 158 159 160 private String buildKey(String cacheName, Object key) { 161 cacheName = buildCacheName(cacheName); 162 StringBuilder keyBuilder = new StringBuilder(globalKeyPrefix) 163 .append(cacheName).append(":"); 164 165 if (key instanceof String) { 166 keyBuilder.append("S"); 167 } else if (key instanceof Number) { 168 keyBuilder.append("I"); 169 } else if (key == null) { 170 keyBuilder.append("S"); 171 key = "null"; 172 } else { 173 keyBuilder.append("O"); 174 } 175 return keyBuilder.append(":").append(key).toString(); 176 } 177 178 @Override 179 public <T> T get(String cacheName, Object key, IDataLoader dataLoader, int liveSeconds) { 180 if (liveSeconds <= 0) { 181 return get(cacheName, key, dataLoader); 182 } 183 184 Object data = get(cacheName, key); 185 if (data == null) { 186 data = dataLoader.load(); 187 put(cacheName, key, data, liveSeconds); 188 } 189 190 return (T) data; 191 } 192 193 194 @Override 195 public Integer getTtl(String cacheName, Object key) { 196 Long ttl = redis.ttl(buildKey(cacheName, key)); 197 return ttl != null ? ttl.intValue() : null; 198 } 199 200 201 @Override 202 public void setTtl(String cacheName, Object key, int seconds) { 203 redis.expire(buildKey(cacheName, key), seconds); 204 205 if (config.isDevMode()) { 206 println("RedisCache SETTTL: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] seconds:" + seconds); 207 } 208 } 209 210 211 @Override 212 public List getNames() { 213 String key = buildCacheName(redisCacheNamesKey); 214 Set set = redis.smembers(key); 215 return set == null ? null : new ArrayList(set); 216 } 217 218 219 @Override 220 public List getKeys(String cacheName) { 221 cacheName = globalKeyPrefix + buildCacheName(cacheName); 222 List<String> keys = new ArrayList<>(); 223 String cursor = "0"; 224 int scanCount = 1000; 225 boolean continueState = true; 226 do { 227 RedisScanResult<String> redisScanResult = redis.scan(cacheName + ":*", cursor, scanCount); 228 List<String> scanKeys = redisScanResult.getResults(); 229 cursor = redisScanResult.getCursor(); 230 231 if (scanKeys != null && scanKeys.size() > 0) { 232 for (String key : scanKeys) { 233 keys.add(key.substring(cacheName.length() + 3)); 234 } 235 } 236 237 if (redisScanResult.isCompleteIteration()) { 238 continueState = false; 239 } 240 } while (continueState); 241 242 return keys; 243 } 244 245 public JbootRedis getRedis() { 246 return redis; 247 } 248 249}