深入理解Link
文章目錄
- 一、問題的集合
- 1.1啟動服務提供者一直沒有響應?
- 1.2找不到ConsumerService對象
- 1.3輸入的集合為空(負載均衡時)
- 二、回顧Link
- 2.1 基于接口編程的方式(場景)
- 2.2 編碼的理解
- 2.3 對于負載均衡的理解
- 2.4 對方法調用的理解
- 2.4.1 本地調用
- 2.4.2 遠程調用
- 2.5 網絡的功能
- 2.6 談談對Socket 里面Io的理解?
- 三、對并發的理解
- 3.1 現在的模式:
- 3.2 就按照我們現在的代碼,能不能實現一個并發的訪問?
- 3.3 線程池的復習:
- 3.4 對提供者的改進
- 3.5 多線程的消費
- 四、我們系統存在的缺陷
- 4.1 單線程的輪詢模型
- 4.2 利用單線程的輪詢模型來設計我們的系統
- 五、我們怎么利用nio 來對系統進行改造
- 5.1 jdk 原生提供的nio有點難懂,一般不使用推薦使用Netty
- 5.2 改造的代碼:(參考)
- 六、Link 的資源的關閉的問題
一、問題的集合
1.1啟動服務提供者一直沒有響應?
和zk 相關!
這是因為你的zk 掛了,或者你的zookeeper 沒有連接成功!
因為超時時間是:
解決方案:
就是設置連接的超時時間,已經重啟你的zookeeper ,2181 的端口放行
1.2找不到ConsumerService對象
發生在啟動服務的消費者:
從ioc 容器里面無法找到該對象:
1 沒有包掃描
2 你沒有添加@Service 注解
1.3輸入的集合為空(負載均衡時)
怎么引起這個原因的?
服務發現失敗,沒有發現服務提供者
啟動的順序或者時間相關:
服務提供者還沒有來得及注冊,你就去服務的發現,肯定為null
服務提供者還沒有來得及注冊,你就去服務的發現,肯定為null
能服務發現成功的原因:服務已經注冊成功了,只有你看zk 上面有沒有你的機器
有同學發現沒有注冊成功:服務提供者啟動了,但是沒有注冊成功
沒有添加@ExposeService 或者沒有包掃描也不行
二、回顧Link
2.1 基于接口編程的方式(場景)
Mapper(mybatis/ plus) UserMapper-> 如何實現對數據庫操作的?
Public interface UserMapper{
int insert(User user) ;
}
代理對象:
想一想,Mybatis 底層是怎么實現的?
dubbo 也是如此,注入的也是代理對象
link 也是如此,注入的也是代理對象
基于接口的編程方法基本上都是動態代理完成的
2.2 編碼的理解
為什么需要寫一個編碼器?
因為socket(網絡IO) 不能直接發生一個對象,只能發送字節的數組
之前我們使用jdk的序列化實現了編碼器,那好不好?
Jdk的序列化效果一般,因為它序列化出來的字節數組比較大!
若能字節數組小,并且速度快,那就比jdk的序列化更好。
有: 在dubbo 里面,它使用的時Hession
大家在緩存里面:json
2.3 對于負載均衡的理解
從一個列表里面選擇一個出來!
我們寫的是:隨機的負載均衡!
但是dubbo:輪詢的,最小響應的!
2.4 對方法調用的理解
對于服務的提供者而言:它會某種方法,他具備某種功能,->有實現類->我就可以直接調用實現類對象(本地調用)
對于服務的消費者而言:他需要使用某種方法/功能(接口的定義),但是他不會。,只有接口->只能去求別人(遠程調用)
本地調用和遠程調用的相同點都是:調用某個方法,得到一個結果!
調用某個方法:必須的參數:
2.4.1 本地調用
我需要得到實現類的對象,就能完成調用
Object: 怎么得到該對象
Method:怎么得到該方法
method.invoker(object,args);
我們的所有對象都交給ioc 去管理了,得到對象,一般也就是從ioc 里面去獲取
Ioc.getBean(接口)
方法得到:只需要方法的名稱和參數就可以得到了
2.4.2 遠程調用
沒有實現類對象,所以無法完成調用,只能求別人調用
給別人發短信,說我需要調用你的那個方法,參數是那些,別人給你計算好了后,把答案發給你,這就是遠程調用:
你->Socket->Request->localMethodInvoke->Result->Socket->你
2.5 網絡的功能
網絡就是一個數據的通道。具備輸入和輸出
服務端和客戶端:服務端和客戶端寫數據的流程相同,但是獲取輸入流和輸出流有點不同:
我們的socket 現在有什么問題?
2.6 談談對Socket 里面Io的理解?
1 serverSocket.accept(); // 接受客戶端連接
2 inputStream.read(); // 從輸入流里面讀取數據
3 outputStream.write(); // 從輸入流里面寫數據
三、對并發的理解
3.1 現在的模式:
我們消費端只有一個線程去消費了提供者
While(true) 代表一直是在一個線程里面輪詢
那怎么變成多線程:
3.2 就按照我們現在的代碼,能不能實現一個并發的訪問?
我們現在服務的消費者去調用服務提供者的方法,服務提供者里面,只有一個線程能供我們使用!
我們的服務端只有一個線程:
所以我們的服務端,是個單線程的,阻塞的方法,只能讓一個服務的消費者消費完畢后,其他的消費者才能去執行!(Redis)
單線程模型并不好:因為沒有利用到多核的優勢,我們需要把他改進為多線程模型!
為什么redis 不改進:因為redis 是內存操作,他的速度非常快!
為什么我們需要改進,因為在我們的java 的大多數業務邏輯里面,都在數據庫的操作,數據庫是文件io的操作!
? 我們怎么實現一個多線程的rpc 框架?
3.3 線程池的復習:
面試的重點:(背會)
一個任務一來,放在任務的列表里面:假設任務的列表是有限制的,只能放10 個
1 任務列表的任務會直接交個一個線程去做
2 線程線程池為null ,會直接調用ThreadFactory 去創建線程,直到達到核心的線程數3個
4 現在任務列表里面還存在7 個任務,線程會不會繼續創建?
不會,因為任務列表沒有滿,7 個任務繼續在列表里面等待,直到把他處理完畢!
5 7 個任務 ,又來了4 個 = 7 +3 = 10 +1 = 10 ,但是任務列表里面只能放10 ,來了11 個,線程工廠會繼續創建線程,來消費
3 線程+1 = 4 線程
11 - 1 = 10
有12 個任務呢?
Max = 5-3 =2
12-2 =10
有13 個:
13-2 = 11 ,任務列表放不下,會走拒絕的策略
3.4 對提供者的改進
/*** serverSocket* ServerSocket 只需要一個就有可以了,不管有多少個客戶端,只需要一個ServerSocket 就可以了* 在服務端和客戶不同的點,是服務端需要監聽**/ public class ServerSocketNet extends SocketNet{private Coder coder = JDKCoder.INSTANCE;private ServerSocket serverSocket ;private MethodInvoker methodInvoker = LocalMethodInvoker.INSTANCE;/*** 線程池:* 7 個參數* 1 :*/private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,5,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {return new Thread(r);}},new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("任務來了"+r+"我已經有女朋友了");}});public ServerSocketNet(int port){try {this.serverSocket = new ServerSocket(port) ;// 服務端一啟動就需要監聽,而且是死循環的監聽while(true){listener();}} catch (IOException e) {e.printStackTrace();}}public void listener(){try {Socket socket = this.serverSocket.accept(); // 代表監聽 socket 客人threadPoolExecutor.submit(new Runnable() {@Overridepublic void run() {handlerSocket( socket) ; // 處理和接待客人}});} catch (IOException e) {e.printStackTrace();}}private void handlerSocket(Socket socket){System.out.println("正在處理------------------");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}try {// 該socket 就是客戶端 , 客戶端會被請求對象發送過來,我們在本地調用成功后,又把該結果發個clientThreadLocalUtil.set("client",socket); // 把該變量放在線程里面// ------------------------------- // 消費者1 進來,代碼走到 ,讀取數據,消費者2 進來,他是否能連接到serverSocket 上面? // 不行了,因為服務端只有一個線程,這個現在還在被使用讀取數據byte[] read = read(); // 調用父類里面的讀取數據的方法,因為服務的消費者要把請求對象發送過來Request request = (Request)coder.deCode(read);// 服務的提供者里面,需要本地調用Object result = methodInvoker.invoker(request);write(coder.code(result)); // 向服務的消費者寫答案過去// 一次調用結束ThreadLocalUtil.clear(); // 調用結束,釋放線程里面的數據} catch (Exception e) {e.printStackTrace();}finally {if(socket!=null){try {socket.close();} catch (IOException e) {e.printStackTrace();}finally {socket = null ;}}}}@Overrideprotected OutputStream getOutputStream() {Socket client =(Socket) ThreadLocalUtil.get("client");// 把該變量放在線程里面OutputStream outputStream = null;try {outputStream = client.getOutputStream();} catch (IOException e) {e.printStackTrace();}return outputStream;}/*** 獲取輸入流* @return*/@Overrideprotected InputStream getInputStream() {Socket client = (Socket)ThreadLocalUtil.get("client");InputStream inputStream = null;try {inputStream = client.getInputStream();} catch (IOException e) {e.printStackTrace();}return inputStream;} }3.5 多線程的消費
效果:
四、我們系統存在的缺陷
現在我們是多線程的并發消費模型吧,但是我們有個問題,socket 里面的io 阻塞問題
我們能不能在服務提供者提供少許的線程就能完成對對大并發的處理?
4.1 單線程的輪詢模型
4.2 利用單線程的輪詢模型來設計我們的系統
為什么設計,我們的系統就是在單線程輪詢:
但是和js的不同在于:我們的系統是單線程接受一事件/客人,該客人交個多線程去處理了
這也就是我們多線程的用處!若客人非常多的話,我們就需要很多的線程數!
1 假設有個空間:
Buff(緩存區)
Jdk 給我們提供的nio ,非常難用:但是有個3個概念大家需要了解:
1 通道(代替之前的socket)
2 選擇器(就是事件)
3 buff(緩沖區,數據直接放在里面)
Nio:核心思想在于:使用緩沖區,充分利用線程!
但是nio 也不是比io 非常完美:
1 若有多個緩沖區同時滿了,io 和nio 的速度,到底那個快?
Io 快,因為io 可以使用多個線程來同時寫
Nio:場景: 高并發少數據量(rpc)
Io 的場景:并發少,數據量大(文件的上傳和下載)
推薦一篇文章:
https://zhuanlan.zhihu.com/p/23488863
對nio 理解非常到位,可以看看
五、我們怎么利用nio 來對系統進行改造
5.1 jdk 原生提供的nio有點難懂,一般不使用推薦使用Netty
Jdk:提供的nio有點難懂,linux和window的實現策略都不一樣!(意味著我們需要寫2 套代碼)
Netty: 就是一個Socket,在Dubbo 里面默認使用Netty 來做網絡的傳輸功能! ,netty 可以很簡單的利用nio的能力
5.2 改造的代碼:(參考)
Netty非常的簡單,他寫起來和Controller->Service->Dao 很像,我們沒有講Netty,如果大家對Netty 有興趣,可以想參考我之前寫的rpc,把netty 添加進去!
https://github.com/NymphWeb/Link/tree/master/Link
如果你想學習Netty ?
有本書:Netty實戰
建議大家看一看,對面試有幫助
六、Link 的資源的關閉的問題
是否可以資源關閉?
將導致Socket 直接關閉,因為Socket 是個雙通道(全雙工),可以輸出也可以輸入
,如果你的Socket 關閉了,那你以后讀數據時,獲取輸入流就報錯,說你的socket 已經關閉了
但是你不能直接關閉Stream,我們可以這樣關閉:
因為現在socket 和線程綁定,socket 的生命周期,當有請求來,它的生命開始,響應后,它的生命結束:
我們可以在ThreadLocaUtil 里面關閉
因為服務端和客戶端一次調用結束后都會執行ThreadLocalUtil.clear() 方法,所以我們在里面關閉最好!
總結
- 上一篇: C++以及java学习方法和路线
- 下一篇: 实现客户机(Client)类声明字符型静