用到的技術以及第三方框架jar
spring 的自定義復雜bean
guava.jar
ioc ,aop ,動態(tài)代理? 反射,多線程
netty
知識點
spring 自定義復雜bean?
1.ClassPathXmlApplicationContext?
2.自定義xsd
3.配置spring.handlers和spring.schemal
4.在spring.handlers找到相應的NettyNameHandler,進行初始化
5.調(diào)相應的方法NettyServicePaser,NettyRegisterPaser和NettyRefence
6.通過初始化把相關的bean通過反射和動態(tài)代理實現(xiàn)Netty的通訊
7.通訊時把相關類的信息,方法,參數(shù),參數(shù)值通過encode,傳到服務端,服務端調(diào)解碼
得到相應的結(jié)果進行交互
客戶端定義復雜bean實現(xiàn)接口FactoryBean
8.guava
反射方法: Reflection.newProxy(PersonManage.class, new MessageSendProxy<T>());
? ExecutorCompletionService<Boolean> completionService = new ExecutorCompletionService<Boolean>(executor);
??????????????????????????? completionService.submit(new ApiEchoResolver(host, echoApiPort));
guava線程池.submit 會掉里面的call方法
? private static ListeningExecutorService threadPoolExecutor = MoreExecutors.listeningDecorator((ThreadPoolExecutor) RpcThreadPool.getExecutor(threadNums, queueNums));
10,有多少秒過期,重連機制
ListenableFuture的說明?
并發(fā)編程是一個難題,但是一個強大而簡單的抽象可以顯著的簡化并發(fā)的編寫。出于這樣的考慮,Guava 定義了 ListenableFuture接口并繼承了JDK concurrent包下的Future 接口,ListenableFuture?允許你注冊回調(diào)方法(callbacks),在運算(多線程執(zhí)行)完成的時候進行調(diào)用, ?或者在運算(多線程執(zhí)行)完成后立即執(zhí)行。這樣簡單的改進,使得可以明顯的支持更多的操作,這樣的功能在JDK concurrent中的Future是不支持的。?在高并發(fā)并且需要大量Future對象的情況下,推薦盡量使用ListenableFuture來代替..
ListenableFuture 中的基礎方法是addListener(Runnable, Executor), 該方法會在多線程運算完的時候,在Executor中執(zhí)行指定的Runnable。
ListenableFuture的創(chuàng)建和使用
對應JDK中的 ExecutorService.submit(Callable) 提交多線程異步運算的方式,Guava 提供了ListeningExecutorService 接口, 該接口返回 ListenableFuture, 而相應的ExecutorService 返回普通的 Future。將 ExecutorService 轉(zhuǎn)為 ListeningExecutorService,可以使用MoreExecutors.listeningDecorator(ExecutorService)進行裝飾。舉例說明:
??? static CountDownLatch Latch =null; //線程計數(shù)器?
?? //開始計時
??????? StopWatch sw = new StopWatch();
倒計時可以算執(zhí)行多少秒
netty 對accpet的channel進行多個預處理直接通過。addLast().addLast
1、定義RPC請求消息、應答消息結(jié)構(gòu),里面要包括RPC的接口定義模塊、包括遠程調(diào)用的類名、方法名稱、參數(shù)結(jié)構(gòu)、參數(shù)值等信息。
2、服務端初始化的時候通過容器加載RPC接口定義和RPC接口實現(xiàn)類對象的映射關系,然后等待客戶端發(fā)起調(diào)用請求。
3、客戶端發(fā)起的RPC消息里面包含,遠程調(diào)用的類名、方法名稱、參數(shù)結(jié)構(gòu)、參數(shù)值等信息,通過網(wǎng)絡,以字節(jié)流的方式送給RPC服務端,RPC服務端接收到字節(jié)流的請求之后,去對應的容器里面,查找客戶端接口映射的具體實現(xiàn)對象。
4、RPC服務端找到實現(xiàn)對象的參數(shù)信息,通過反射機制創(chuàng)建該對象的實例,并返回調(diào)用處理結(jié)果,最后封裝成RPC應答消息通知到客戶端。
5、客戶端通過網(wǎng)絡,收到字節(jié)流形式的RPC應答消息,進行拆包、解析之后,顯示遠程調(diào)用結(jié)果。
上面說的是很簡單,但是實現(xiàn)的時候,我們還要考慮如下的問題:
1、RPC服務器的傳輸層是基于TCP協(xié)議的,出現(xiàn)粘包咋辦?這樣客戶端的請求,服務端不是會解析失???好在Netty里面已經(jīng)提供了解決TCP粘包問題的解碼器:LengthFieldBasedFrameDecoder,可以靠它輕松搞定TCP粘包問題。
2、Netty服務端的線程模型是單線程、多線程(一個線程負責客戶端連接,連接成功之后,丟給后端IO的線程池處理)、還是主從模式(客戶端連接、后端IO處理都是基于線程池的實現(xiàn))。當然在這里,我出于性能考慮,使用了Netty主從線程池模型。
3、Netty的IO處理線程池,如果遇到非常耗時的業(yè)務,出現(xiàn)阻塞了咋辦?這樣不是很容易把后端的NIO線程給掛死、阻塞?本文的處理方式是,對于復雜的后端業(yè)務,分派到專門的業(yè)務線程池里面,進行異步回調(diào)處理。
4、RPC消息的傳輸是通過字節(jié)流在NIO的通道(Channel)之間傳輸,那具體如何實現(xiàn)呢?本文,是通過基于Java原生對象序列化機制的編碼、解碼器(ObjectEncoder、ObjectDecoder)進行實現(xiàn)的。當然出于性能考慮,這個可能不是最優(yōu)的方案。更優(yōu)的方案是把消息的編碼、解碼器,搞成可以配置實現(xiàn)的。具體比如可以通過:protobuf、JBoss?Marshalling方式進行解碼和編碼,以提高網(wǎng)絡消息的傳輸效率。
5、RPC服務器要考慮多線程、高并發(fā)的使用場景,所以線程安全是必須的。此外盡量不要使用synchronized進行加鎖,改用輕量級的ReentrantLock方式進行代碼塊的條件加鎖。比如本文中的RPC消息處理回調(diào),就有這方面的使用。
6、RPC服務端的服務接口對象和服務接口實現(xiàn)對象要能輕易的配置,輕松進行加載、卸載。在這里,本文是通過Spring容器進行統(tǒng)一的對象管理。
總體流程
Netty 流程