连接工厂创建连接,并放在连接池中?
一、Spring RedisTemplate的原理
1、不同类型数据结构定义了不同的操作器
private final ValueOperations<K, V> valueOps = new DefaultValueOperations<>(this); private final ListOperations<K, V> listOps = new DefaultListOperations<>(this); private final SetOperations<K, V> setOps = new DefaultSetOperations<>(this); private final StreamOperations<K, ?, ?> streamOps = new DefaultStreamOperations<>(this,ObjectHashMapper.getSharedInstance()); private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations<>(this); private final GeoOperations<K, V> geoOps = new DefaultGeoOperations<>(this); private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations<>(this); private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations<>(this);
| opsForValue() | 操作只有简单属性的数据 |
| opsForList() | 操作含有list的数据 |
| opsForSet() | 操作含有set的数据 |
| opsForZSet() | 操作含有ZSet(有序集合)的数据 |
| opsForHash() | 操作含有hash的数据 |
| opsForStream() | 操作Stream |
2、提供配置key和value的序列化方式,默认的序列化方式为JDK,可在实例化RedisTemplate的时候指定序列化方式
private @Nullable RedisSerializer<?> defaultSerializer; private RedisSerializer keySerializer = null; private RedisSerializer valueSerializer = null; private RedisSerializer hashKeySerializer = null; private RedisSerializer hashValueSerializer = null; private RedisSerializer<String> stringSerializer = RedisSerializer.string();
3、RedisTemplate继承自 RedisAccessor,RedisAccessor中包含了 RedisConnectFactory属性
因此,在实例化RedisTemplate可指定自定义的连接工厂
public class RedisAccessor implements InitializingBean { private @Nullable RedisConnectionFactory connectionFactory; }
4、提供实际的数据操作的执行
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) { return execute(action, exposeConnection, false); }
操作数据流程:
1、获取连接工厂
RedisConnectionFactory factory = getRequiredConnectionFactory();
2、根据连接工厂获取连接
RedisConnection conn = RedisConnectionUtils.getConnection(factory, enableTransactionSupport);
1)当前线程是否持有连接,若持有,则直接返回
2)否则,使用连接工厂创建一个连接并绑定到当前线程上
3、执行完成释放连接
finally { RedisConnectionUtils.releaseConnection(conn, factory, enableTransactionSupport); }
二、连接动态化
1、线程本地变量
public class RdmContext { /** * 保存RdmContext的线程本地变量 */ private static final ThreadLocal<RdmContext> RDM_CONTEXT_THREAD_LOCAL = ThreadLocal.withInitial(RdmContext::new); public static RdmContext currentContext() { return RDM_CONTEXT_THREAD_LOCAL.get(); } public static void remove() { RDM_CONTEXT_THREAD_LOCAL.remove(); } /** * redis操作模板对象 */ private RedisTemplate<String, Object> redisTemplate; /** * 请求参数 */ private Map<String, String> data; /** * 连接信息 */ private RedisProperties redisProperties; /** * 连接信息token */ private String token; /** * 处理器执行结果 */ private HandleResult handleResult = new HandleResult(); /** * 集群模式 */ private String mode; ...getter/setter }
2、HandleResult
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; public class HandleResult { private Integer code = 0; private String message = "success"; @JsonInclude(JsonInclude.Include.NON_EMPTY) private Object data; public HandleResult() { } public HandleResult(ResponseEnum response) { this.code = response.getCode(); this.message = response.getMessage(); } public HandleResult(Object data) { this.data = data; } public HandleResult fail(ResponseEnum response) { this.code = response.getCode(); this.message = response.getMessage(); return this; } @JsonIgnore public boolean isSuccess() { return ResponseEnum.SUCCESS.getCode().equals(code); } ...getter/setter }
3、定义前置处理器,在每个请求到大Controller之前先执行前置处理器
1)定义前置处理器接口
/** * 处理器接口 */ public interface RdmHandler { /** * 是否执行 * * @return true:执行,false:不执行 */ boolean boolHandle(); /** * 执行,并处理异常 */ void handle(); }
2)定义参数解析处理器
用于解析请求中的Redis连接信息参数和其他参数,并将请求参数保存在当前线程的ThreadLoca中
/** * 参数处理器 */ @Handler(order = 2) public class ParamParseHandler implements RdmHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ParamParseHandler.class); @Override public boolean boolHandle() { return true; } @Override public void handle() { RdmContext rdmContext = RdmContext.currentContext(); try { // 将data从base64转为json明文 String originData = RequestUtil.getParam("data"); String data = Base64Util.decode(originData); // 解析其中的连接信息 Map<String, String> dataMap = JacksonUtil.ofMap(data, String.class, String.class); if (dataMap == null) { rdmContext.getHandleResult().fail(ResponseEnum.PARAM_ERROR); return; } rdmContext.setData(dataMap); // 单机还是集群 String mode = dataMap.get("mode"); String host = dataMap.get("host"); String password = dataMap.get("password"); String username = dataMap.get("username"); String database = dataMap.get("database"); rdmContext.setMode(mode); RedisProperties redisProperties = new RedisProperties(); if (StringUtil.equals(CommonConstant.STANDALONE, mode)) { // 解析域名端口 String[] hostPort = host.split(":"); redisProperties.setHost(hostPort[0]); redisProperties.setPort(Integer.parseInt(hostPort[1])); // 如果数据库不为空 if (StringUtil.isNotEmpty(database)) { redisProperties.setDatabase(Integer.parseInt(database)); } } else if (StringUtil.equals(CommonConstant.CLUSTER, mode)) { String[] hosts = host.split(","); redisProperties.setNodes(Arrays.asList(hosts)); } else { rdmContext.getHandleResult().fail(ResponseEnum.PARAM_ERROR); return; } redisProperties.setUsername(username); redisProperties.setPassword(password); rdmContext.setRedisProperties(redisProperties); String token = Md5Util.getMd5(JacksonUtil.toString(redisProperties)); rdmContext.setToken(token); // 放入延时队列中,半小时后清理 RedisDelayQueue.putDelayQueue(token); } catch (Exception e) { LOGGER.error("参数处理失败:" + e.getMessage(), e); rdmContext.getHandleResult().fail(ResponseEnum.PARAM_ERROR); } } }