Method call stack

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)
Copy the code

Application entry

The controller

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"," cat "); Println (redisutil.getexpire ("name")); // Write data system.out.println (redisutil.getexpire ("name")); Return redisUtil. Get ("name"); // Read data}}Copy the code

Encapsulated utility classes

The main package is the RedisTemplate of Spring-data-redis

Public Final class RedisUtil {@resource private RedisTemplate<String, Object> redisTemplate; // Add RedisTemplate data to the utility class /** * Add RedisTemplate data to the normal cache ** @param key * @param value * @return true success false failure */ public Boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; Public Object get(String key) {return key == null? null : redisTemplate.opsForValue().get(key); }Copy the code

Bean – RedisTemplate declaration

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") // Declare bean-redistemplate public based on annotations RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); / / use fastjson serialization / / the value of serialized using fastJsonRedisSerializer template. SetValueSerializer (fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer); / / key serialized using 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; }}Copy the code

1. Declare bean-redistemplate based on annotations. 2. Configure various things, such as how to serialize the key value. In this case, the key is serialized by the built-in string serializer. Value is serialized with Ali’s Fastjson. In essence, the object value of value and binary value are converted to each other during the writing and reading.

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); // Write data return null; } }, true); }Copy the code
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);
  }
  
Copy the code
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; }Copy the code
public final V doInRedis(RedisConnection connection) {
      byte[] result = this.inRedis(AbstractOperations.this.rawKey(this.key), connection);
      return AbstractOperations.this.deserializeValue(result);
    }
Copy the code

DefaultedRedisConnection

public interface DefaultedRedisConnection extends RedisConnection { /** @deprecated */ @Deprecated default Boolean set(byte[] key, byte[] value) { return this.stringCommands().set(key, value); //1. Get command 2. Write data}Copy the code

This layer is spring-data-Redis, which acts as a mediator, and then down below it is deciding which redis client to use, jedis? Or 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)); / / write data}} the catch (Exception var4) {throw this. ConvertLettuceAccessException (var4); }}Copy the code

The specific Redis client is lettuce, because Springboot is now the default.

Redis client -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); // call}}Copy the code

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); // call 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(); }}Copy the code

We’re at the command level

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;
}
Copy the code
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;
}
Copy the code
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); // Write data}}Copy the code

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); // Write data}Copy the code
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); / / write data} else {enclosing 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); // Write data}Copy the code

netty

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel { public ChannelFuture writeAndFlush(Object msg) { return this.pipeline.writeAndFlush(msg); // Write data}Copy the code
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: 'yao '); 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(); }}}Copy the code

role

What does the RedisTemplate do? Why is Spring packaged as a JAR? 1. Which redis client should the mediation use, Jedis or lettuce? Springboot is lettuce by default, but can also be configured as jedis. 2. Configure various things such as which serializer to use for key values?

These are the various spring-like mediation template classes, such as the JDBC template class JDBCTemplate.

conclusion

Set get, get expiration time, same process.

code

Github.com/13851809588…