Lettuce同步命令原始碼分析
Lettuce同步原始碼分析
在上一篇分享中分享了單機模式非同步連線建立過程; 在本次分享內容主要介紹同步命令的處理過程.
Lettuce是基於Netty的Redis高階客戶端,對於非同步命令來說是天然的,那麼lettuce中是如何處理同步命令的呢?實際上同步連線還是對非同步命令的一次封裝;下面我們就透過原始碼進行分析看看Lettuce中的具體實現.
透過上一篇文章中可以知道在StatefulRedisConnectionImpl中建立 非同步模式,同步模式以及響應式模式命令處理模式,那麼我們就從 該處看起
public StatefulRedisConnectionImpl(RedisChannelWriter writer, RedisCodec super (writer, timeout); this .codec = codec; //建立非同步redis命令處理模式 this .async = newRedisAsyncCommandsImpl(); //建立redis命令同步處理模式 this .sync = newRedisSyncCommandsImpl(); //建立redis命令響應式處理模式 this .reactive = newRedisReactiveCommandsImpl(); }
|
透過這裡似乎看不出同步處理模式同非同步處理模式有什麼關聯,那麼我們在深入進去看一下
protected RedisCommands return syncHandler(async(), RedisCommands. class , RedisClusterCommands. class ); }
|
在這段程式碼中可以看到async(),這個就是redis命令非同步處理模式,那麼它是如何封裝的呢?
protected
//對非同步API建立呼叫處理器 FutureSyncInvocationHandler h = new FutureSyncInvocationHandler((StatefulConnection, ?>) this , asyncApi, interfaces); //建立動態代理 return (T) Proxy.newProxyInstance(AbstractRedisClient. class .getClassLoader(), interfaces, h); }
|
透過上面對原始碼可以發現原來是對非同步api建立了一個JDK動態代理;那麼關鍵的邏輯還是在FutureSyncInvocationHandler中,對於動態代理的知識就不在展開了.
在invoke處理是在AbstractInvocationHandler中完成的,它將一些基本公用的抽象在了基類中,將特殊的實現延遲到子類中實現.
public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果引數為null則 將args設定為"{}" if (args == null ) { args = NO_ARGS; } //如果引數長度為0同時方法名稱為hashCode則直接返回hashCode if (args.length == 0 && method.getName().equals( "hashCode" )) { return hashCode(); } //如果是equals if (args.length == 1 && method.getName().equals( "equals" ) && method.getParameterTypes()[ 0 ] == Object. class ) { Object arg = args[ 0 ]; if (arg == null ) { return false ; } if (proxy == arg) { return true ; } return isProxyOfSameInterfaces(arg, proxy.getClass()) && equals(Proxy.getInvocationHandler(arg)); } //如果是toString if (args.length == 0 && method.getName().equals( "toString" )) { return toString(); } return handleInvocation(proxy, method, args); }
|
在FutureSyncInvocationHandler中實現了同步命令處理過程,其原始碼如下:
protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable { try { //獲取當前method在asyncApi 中對應的方法 Method targetMethod = this .translator.get(method); //呼叫非同步介面 Object result = targetMethod.invoke(asyncApi, args); //如果返回結果是RedisFuture型別 if (result instanceof RedisFuture>) { //型別強轉 RedisFuture> command = (RedisFuture>) result; //如果不是事務控制方法 同時還在事務中則返回null if (isNonTxControlMethod(method.getName()) && isTransactionActive(connection)) { return null ; } //是事務控制方法,或不在事務中則進行如下處理 //等待超時或取消 LettuceFutures.awaitOrCancel(command, connection.getTimeout().toNanos(), TimeUnit.NANOSECONDS); //返回結果,這裡處理不是很好 上一步中就可以直接返回了 return command.get(); } //如果不是RedisFuture型別則直接返回 return result; } catch (InvocationTargetException e) { throw e.getTargetException(); } }
|
在上文中有一段是獲取獲取指定方法在delegate中對應方法的處理,下面就看看這個處理是如何實現的
protected static class MethodTranslator { private final static WeakHashMap new WeakHashMap( 32 ); //真實方法和代理類中方法對映表 private final Map private MethodTranslator(Class> delegate, Class>... methodSources) { map = createMethodMap(delegate, methodSources); }
public static MethodTranslator of(Class> delegate, Class>... methodSources) { //同步程式碼塊 synchronized (TRANSLATOR_MAP) { //如果翻譯器對映表中不存在delegate的翻譯器則建立一個新的 return TRANSLATOR_MAP.computeIfAbsent(delegate, key -> new MethodTranslator(key, methodSources)); } } private Map Map List new ArrayList(); //遍歷源類,找到所有public方法 for (Class> sourceClass : methodSources) { methods.addAll(getMethods(sourceClass)); } map = new HashMap(methods.size(), 1 .0f); //建立方法和代理類的方法的對映表 for (Method method : methods) { try { map.put(method, delegate.getMethod(method.getName(), method.getParameterTypes())); } catch (NoSuchMethodException ignore) { } } return map; } //獲取目標方法中的所有方法 private Collection extends Method> getMethods(Class> sourceClass) { //目標方法集合 Set new HashSet(); Class> searchType = sourceClass; while (searchType != null && searchType != Object. class ) { //將目標類中所有public方法新增到集合中 result.addAll(filterPublicMethods(Arrays.asList(sourceClass.getDeclaredMethods()))); //如果souceClass是介面型別 if (sourceClass.isInterface()) { //獲取souceClass的所有介面 Class>[] interfaces = sourceClass.getInterfaces(); //遍歷介面,將介面的public方法也新增到方法集合中 for (Class> interfaceClass : interfaces) { result.addAll(getMethods(interfaceClass)); } searchType = null ; } else { //如果不是介面則查詢父類 searchType = searchType.getSuperclass(); } } return result; } //獲取給定方法集合中所有public方法 private Collection extends Method> filterPublicMethods(List List new ArrayList(methods.size()); for (Method method : methods) { if (Modifier.isPublic(method.getModifiers())) { result.add(method); } } return result; } public Method get(Method key) { //從方法對映表中獲取目標方法 Method result = map.get(key); //如果目標方法不為null則返回,否則丟擲異常 if (result != null ) { return result; } throw new IllegalStateException( "Cannot find source method " + key); } } }
|
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2310/viewspace-2803522/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入OKHttp原始碼分析(一)----同步和非同步請求流程和原始碼分析HTTP原始碼非同步
- Okhttp同步請求原始碼分析HTTP原始碼
- 影片聊天原始碼,同步、非同步示例程式碼分析原始碼非同步
- Dubbo原始碼分析(十)同步呼叫與非同步呼叫原始碼非同步
- 【原始碼】Redis exists命令bug分析原始碼Redis
- 原始碼分析laravel artisan命令列原始碼Laravel命令列
- 原始碼分析:同步基礎框架——AbstractQueuedSynchronizer(AQS)原始碼框架AQS
- redis原始碼分析(一)複習redis命令、持久化方案、主從同步原理、配置Redis原始碼持久化主從同步
- redis原始碼分析(一)複習redis命令、持久化方案、主從同步原來、配置Redis原始碼持久化主從同步
- OkHttpClient原始碼分析(一)—— 同步、非同步請求分析和Dispatcher的任務排程HTTPclient原始碼非同步
- 原始碼分析:Phaser 之更靈活的同步屏障原始碼
- Java非同步程式設計——深入原始碼分析FutureTaskJava非同步程式設計原始碼
- 死磕以太坊原始碼分析之Fetcher同步原始碼
- 死磕以太坊原始碼分析之downloader同步原始碼
- MySQL·原始碼分析·InnoDB非同步IO工作流程MySql原始碼非同步
- Retrofit原始碼分析三 原始碼分析原始碼
- Giraph 原始碼分析(五)—— 載入資料+同步總結原始碼
- React Fiber原始碼分析 第二篇(同步模式)React原始碼模式
- 影片直播系統原始碼,非同步處理實現程式碼分析原始碼非同步
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- Swoole 原始碼分析——Async 非同步事件系統 Swoole_Event原始碼非同步事件
- Fabric 1.0原始碼分析(33) Peer #peer channel命令及子命令實現原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- React Fiber原始碼分析 第三篇(非同步狀態)React原始碼非同步
- Fabric 1.0原始碼分析(32)Peer #peer根命令入口及載入子命令原始碼
- Fabric 1.0原始碼分析(34) Peer #peer chaincode命令及子命令實現原始碼AI
- 深度 Mybatis 3 原始碼分析(一)SqlSessionFactoryBuilder原始碼分析MyBatis原始碼SQLSessionUI
- 以太坊原始碼分析(51)rpc原始碼分析原始碼RPC
- 【Android原始碼】Fragment 原始碼分析Android原始碼Fragment