方法调用栈
write:771, AbstractChannelHandlerContext (io.netty.channel)
writeAndFlush:757, AbstractChannelHandlerContext (io.netty.channel)
writeAndFlush:812, AbstractChannelHandlerContext (io.netty.channel)
writeAndFlush:1036, DefaultChannelPipeline (io.netty.channel)
writeAndFlush:305, AbstractChannel (io.netty.channel) //lettuce的通信最底层是基于netty
channelWriteAndFlush:342, DefaultEndpoint (io.lettuce.core.protocol) //写数据到服务器
writeToChannelAndFlush:282, DefaultEndpoint (io.lettuce.core.protocol)
write:142, DefaultEndpoint (io.lettuce.core.protocol)
write:112, CommandExpiryWriter (io.lettuce.core.protocol)
dispatch:187, RedisChannelHandler (io.lettuce.core)
dispatch:152, StatefulRedisConnectionImpl (io.lettuce.core)
dispatch:467, AbstractRedisAsyncCommands (io.lettuce.core)
set:1203, AbstractRedisAsyncCommands (io.lettuce.core) //写数据
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:62, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:566, Method (java.lang.reflect)
handleInvocation:57, FutureSyncInvocationHandler (io.lettuce.core)
invoke:80, AbstractInvocationHandler (io.lettuce.core.internal) //redis客户端-lettuce
set:-1, $Proxy67 (com.sun.proxy)
set:146, LettuceStringCommands (org.springframework.data.redis.connection.lettuce) //调用底层redis客户端
lettuce
set:274, DefaultedRedisConnection (org.springframework.data.redis.connection)
inRedis:240, DefaultValueOperations$3 (org.springframework.data.redis.core)
doInRedis:59, AbstractOperations$ValueDeserializingRedisCallback (org.springframework.data.redis.core)
execute:224, RedisTemplate (org.springframework.data.redis.core)
execute:184, RedisTemplate (org.springframework.data.redis.core)
execute:95, AbstractOperations (org.springframework.data.redis.core)
set:236, DefaultValueOperations (org.springframework.data.redis.core) //spring-data-redis
set:112, RedisUtil (com.example.redis.utils) //应用程序-工具类
getdata:28, TestController (com.example.redis.controller) //应用程序入口
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:62, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:566, Method (java.lang.reflect)
doInvoke:190, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:138, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:104, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:892, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:797, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1039, DispatcherServlet (org.springframework.web.servlet)
doService:942, DispatcherServlet (org.springframework.web.servlet)
processRequest:1005, FrameworkServlet (org.springframework.web.servlet)
doGet:897, FrameworkServlet (org.springframework.web.servlet)
service:634, HttpServlet (javax.servlet.http)
service:882, FrameworkServlet (org.springframework.web.servlet)
service:741, HttpServlet (javax.servlet.http)
internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:99, RequestContextFilter (org.springframework.web.filter)
doFilter:109, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:92, FormContentFilter (org.springframework.web.filter)
doFilter:109, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, HiddenHttpMethodFilter (org.springframework.web.filter)
doFilter:109, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:200, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:109, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:202, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:490, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:139, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:343, CoyoteAdapter (org.apache.catalina.connector)
service:408, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:853, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1587, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1128, ThreadPoolExecutor (java.util.concurrent)
run:628, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:834, Thread (java.lang)
应用程序入口
控制器
package com.example.redis.controller;
import com.example.redis.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
RedisUtil redisUtil;
@Autowired
private RedisTemplate<String, String> redisTemplate;// 通过构造方法注入该对象
@GetMapping("/getdata")
public Object getdata(){
//测试redis
redisUtil.set("name","小猫"); //写数据
System.out.println(redisUtil.getExpire("name")); //获取过期时间
return redisUtil.get("name"); //读数据
}
}
封装的工具类
主要是封装spring-data-redis的RedisTemplate
@Component //工具类本身也要变为spring的bean
public final class RedisUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate; //往工具类注入RedisTemplate数据
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
声明bean-RedisTemplate
package com.example.redis.config;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate") //基于注解方式声明bean-RedisTemplate
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); //使用fastjson序列化
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
核心作用
1.基于注解方式声明bean-RedisTemplate。
2.配置各种东西,比如key value用什么来序列化,这里的key是用自带的字符串序列化器来序列化,本质是在写读数据的时候,把key的字符串值和二进制值互相转换。value是用阿里的fastjson来序列化的,本质是在写读的时候,把value的对象值和二进制值互相转换。
spring-data-redis
class DefaultValueOperations<K, V> extends AbstractOperations<K, V> implements ValueOperations<K, V> {
public void set(K key, V value) {
final byte[] rawValue = this.rawValue(value);
this.execute(new AbstractOperations<K, V>.ValueDeserializingRedisCallback(key) {
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
connection.set(rawKey, rawValue); //写数据
return null;
}
}, true);
}
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
abstract class AbstractOperations<K, V> {
@Nullable
<T> T execute(RedisCallback<T> callback, boolean b) {
return this.template.execute(callback, b);
}
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = this.getRequiredConnectionFactory();
RedisConnection conn = null;
Object var11;
try {
if (this.enableTransactionSupport) {
conn = RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);
} else {
conn = RedisConnectionUtils.getConnection(factory);
}
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}
RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
T result = action.doInRedis(connToExpose);
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}
var11 = this.postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
return var11;
}
public final V doInRedis(RedisConnection connection) {
byte[] result = this.inRedis(AbstractOperations.this.rawKey(this.key), connection);
return AbstractOperations.this.deserializeValue(result);
}
DefaultedRedisConnection
public interface DefaultedRedisConnection extends RedisConnection {
/** @deprecated */
@Deprecated
default Boolean set(byte[] key, byte[] value) {
return this.stringCommands().set(key, value); //1.获取命令 2.写数据
}
这一层是spring-data-redis,相当于是一个中介,再往下就是决定要使用具体的哪个redis客户端,是jedis?还是lettuce?
LettuceStringCommands
class LettuceStringCommands implements RedisStringCommands {
public Boolean set(byte[] key, byte[] value) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
try {
if (this.isPipelined()) {
this.pipeline(this.connection.newLettuceResult(this.getAsyncConnection().set(key, value), Converters.stringToBooleanConverter()));
return null;
} else if (this.isQueueing()) {
this.transaction(this.connection.newLettuceResult(this.getAsyncConnection().set(key, value), Converters.stringToBooleanConverter()));
return null;
} else {
return Converters.stringToBoolean(this.getConnection().set(key, value)); //写数据
}
} catch (Exception var4) {
throw this.convertLettuceAccessException(var4);
}
}
具体的redis客户端是lettuce,因为springboot现在默认是lettuce。
redis客户端-lettuce
AbstractInvocationHandler
public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (args == null) {
args = NO_ARGS;
}
if (args.length == 0 && method.getName().equals("hashCode")) {
return this.hashCode();
} else if (args.length == 1 && method.getName().equals("equals") && method.getParameterTypes()[0] == Object.class) {
Object arg = args[0];
if (arg == null) {
return false;
} else {
return proxy == arg ? true : isProxyOfSameInterfaces(arg, proxy.getClass()) && this.equals(Proxy.getInvocationHandler(arg));
}
} else {
return args.length == 0 && method.getName().equals("toString") ? this.toString() : this.handleInvocation(proxy, method, args); //调用
}
}
FutureSyncInvocationHandler
protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
try {
Method targetMethod = this.translator.get(method);
Object result = targetMethod.invoke(this.asyncApi, args); //调用
if (result instanceof RedisFuture) {
RedisFuture<?> command = (RedisFuture)result;
if (isNonTxControlMethod(method.getName()) && isTransactionActive(this.connection)) {
return null;
} else {
long timeout = this.getTimeoutNs(command);
return LettuceFutures.awaitOrCancel(command, timeout, TimeUnit.NANOSECONDS);
}
} else {
return result;
}
} catch (InvocationTargetException var9) {
throw var9.getTargetException();
}
}
到了命令这一层
public abstract class AbstractRedisAsyncCommands<K, V> implements RedisHashAsyncCommands<K, V>, RedisKeyAsyncCommands<K, V>, RedisStringAsyncCommands<K, V>, RedisListAsyncCommands<K, V>, RedisSetAsyncCommands<K, V>, RedisSortedSetAsyncCommands<K, V>, RedisScriptingAsyncCommands<K, V>, RedisServerAsyncCommands<K, V>, RedisHLLAsyncCommands<K, V>, BaseRedisAsyncCommands<K, V>, RedisTransactionalAsyncCommands<K, V>, RedisGeoAsyncCommands<K, V>, RedisClusterAsyncCommands<K, V> {
public <T> AsyncCommand<K, V, T> dispatch(RedisCommand<K, V, T> cmd) {
AsyncCommand<K, V, T> asyncCommand = new AsyncCommand(cmd);
RedisCommand<K, V, T> dispatched = this.connection.dispatch(asyncCommand); //
return dispatched instanceof AsyncCommand ? (AsyncCommand)dispatched : asyncCommand;
}
public class StatefulRedisConnectionImpl<K, V> extends RedisChannelHandler<K, V> implements StatefulRedisConnection<K, V> {
public <T> RedisCommand<K, V, T> dispatch(RedisCommand<K, V, T> command) {
RedisCommand toSend = this.preProcessCommand(command);
RedisCommand var3;
try {
var3 = super.dispatch(toSend); //
} finally {
if (command.getType().name().equals(CommandType.MULTI.name())) {
this.multi = this.multi == null ? new MultiOutput(this.codec) : this.multi;
}
}
return var3;
}
public abstract class RedisChannelHandler<K, V> implements Closeable, ConnectionFacade {
protected <T> RedisCommand<K, V, T> dispatch(RedisCommand<K, V, T> cmd) {
if (this.debugEnabled) {
logger.debug("dispatching command {}", cmd);
}
if (this.tracingEnabled) {
RedisCommand<K, V, T> commandToSend = cmd;
TraceContextProvider provider = (TraceContextProvider)CommandWrapper.unwrap(cmd, TraceContextProvider.class);
if (provider == null) {
commandToSend = new TracedCommand(cmd, this.clientResources.tracing().initialTraceContextProvider().getTraceContext());
}
return this.channelWriter.write((RedisCommand)commandToSend);
} else {
return this.channelWriter.write(cmd); //写数据
}
}
public class CommandExpiryWriter implements RedisChannelWriter {
public <K, V, T> RedisCommand<K, V, T> write(RedisCommand<K, V, T> command) {
this.potentiallyExpire(command, this.getExecutorService());
return this.writer.write(command); //写数据
}
public class DefaultEndpoint implements RedisChannelWriter, Endpoint {
public <K, V, T> RedisCommand<K, V, T> write(RedisCommand<K, V, T> command) {
LettuceAssert.notNull(command, "Command must not be null");
try {
this.sharedLock.incrementWriters();
this.validateWrite(1);
if (this.autoFlushCommands) {
if (this.isConnected()) {
this.writeToChannelAndFlush(command); //写数据
} else {
this.writeToDisconnectedBuffer(command);
}
} else {
this.writeToBuffer(command);
}
} finally {
this.sharedLock.decrementWriters();
if (this.debugEnabled) {
logger.debug("{} write() done", this.logPrefix());
}
}
return command;
}
private ChannelFuture channelWriteAndFlush(RedisCommand<?, ?, ?> command) {
if (this.debugEnabled) {
logger.debug("{} write() writeAndFlush command {}", this.logPrefix(), command);
}
return this.channel.writeAndFlush(command); //写数据
}
netty
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
public ChannelFuture writeAndFlush(Object msg) {
return this.pipeline.writeAndFlush(msg); //写数据
}
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
private void write(Object msg, boolean flush, ChannelPromise promise) {
ObjectUtil.checkNotNull(msg, "msg");
try {
if (this.isNotValidPromise(promise, true)) {
ReferenceCountUtil.release(msg);
return;
}
} catch (RuntimeException var8) {
ReferenceCountUtil.release(msg);
throw var8;
}
AbstractChannelHandlerContext next = this.findContextOutbound(flush ? 98304 : '耀');
Object m = this.pipeline.touch(msg, next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
if (flush) {
next.invokeWriteAndFlush(m, promise);
} else {
next.invokeWrite(m, promise);
}
} else {
Object task;
if (flush) {
task = AbstractChannelHandlerContext.WriteAndFlushTask.newInstance(next, m, promise);
} else {
task = AbstractChannelHandlerContext.WriteTask.newInstance(next, m, promise);
}
if (!safeExecute(executor, (Runnable)task, promise, m)) {
((AbstractChannelHandlerContext.AbstractWriteTask)task).cancel();
}
}
}
作用
RedisTemplate的作用?为什么spring要封装为一个jar?
1.中介
用哪个redis客户端,jedis还是lettuce?springboot默认是lettuce,但是也可以配置为jedis。
2.配置各种东西
比如key value用哪个序列化器来序列化?
其实就是类似于spring的各种中介模板类,比如jdbc的模板类JDBCTemplate。
总结
set get,还有获取过期时间命令,流程都一样。