SpringCloudAlibaba微服務實戰教程系列
第一部分第二部分:分布式架構基礎、Paxos算法、Raft算法、系統網絡通信
第三部分:分布式系統網絡通信
一、網絡通信基礎
計算機系統網絡通信的基本原理,在底層層面去看,網絡通信需要做的就是將流從一台計算機傳輸到另外一台計算機,基於傳輸協議和網絡IO來實現,其中傳輸協議包絡:tcp、udp等等,tcp、udp都是在基於Socket概念上為某類應用場景而擴展出的傳輸協議,網絡IO主要包括:BIO、NIO、AIO三種方式,所有的分布式應用通訊都基於這個原理而實現,只是為了應用的易用,各種語言通常都會提供一些更為貼近應用易用的應用層協議。
二、什么是RPC
RPC全稱為remote procedure call,即遠程過程調用。並非是一種通信協議,是一種思想或者說是一類通信協議的總稱。借助RPC可以做到像本地調用一樣調用遠程服務,是一種進程間的通信方式。無論是何種類型的數據,最終都需要轉換成二進制流在網絡上進行傳輸,數據的發送方需要將對象轉換為二進制流(也稱為序列化),而數據的接收方則需要把二進制流再恢復為對象(稱為反序列化)。在java中RPC框架比較多,常見的有Hessian、gRPC、Thrift、HSF (High Speed Service Framework)、Dubbo等,其實對於RPC框架而言,核心模塊就是通訊和序列化。
三、通信基礎RMI
Java RMI,即
遠程方法調用(
Remote Method Invocation),一種用於實現
遠程過程調用(RPC)
(Remote procedure call)的Java API, 能直接傳輸序列化后的Java對象和
分布式垃圾收集。它的實現依賴於
Java虛擬機(JVM),因此它僅支持從一個JVM到另一個JVM的調用。

客戶端:
1)存根/樁(Stub):遠程對象在客戶端上的代理;
2)遠程引用層(Remote Reference Layer):解析並執行遠程引用協議;
3)傳輸層(Transport):發送調用、傳遞遠程方法參數、接收遠程方法執行結果。
服務端:
1)骨架(Skeleton):讀取客戶端傳遞的方法參數,調用服務器方的實際對象方法,
並接收方法執行后的返回值;
2)遠程引用層(Remote Reference Layer):處理遠程引用后向骨架發送遠程方法調用;
3)傳輸層(Transport):監聽客戶端的入站連接,接收並轉發調用到遠程引用層。
1)客戶端從遠程服務器的注冊表中查詢並獲取遠程對象引用。 2)樁對象與遠程對象具有相同的接口和方法列表,當客戶端調用遠程對象時,實際上是由相應的樁對象代理完成的。 3)遠程引用層在將樁的本地引用轉換為服務器上對象的遠程引用后,再將調用傳遞給傳輸層(Transport),由傳輸層通 過TCP協議發送調用; 4)在服務器端,傳輸層監聽入站連接,它一旦接收到客戶端遠程調用后,就將這個引用轉發給其上層的遠程引用層; 5)服務器端的遠程引用層將客戶端發送的遠程應用轉換為本地虛擬機的引用后,再將請求傳遞給骨架(Skeleton); 6)骨架讀取參數,又將請求傳遞給服務器,最后由服務器進行實際的方法調用。
RMI代碼實現項目結構
1、公用實體類

package city.albert; import java.io.Serializable; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:16 PM */ public class UserInfo implements Serializable { private String name; private int age; public UserInfo(String name, int age) { this.name = name; this.age = age; } public UserInfo() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "UserInfo{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
2、公用接口
package city.albert; import java.rmi.Remote; import java.rmi.RemoteException; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:18 PM */ public interface UserService extends Remote { /** * rmi注意重點: * 第一:接口類上繼承 Remote接口 * 第二:實現類上繼承UnicastRemoteObject 類 * 第三:調用方法上拋出RemoteException異常 * 第四:返回對象實現序列化接口Serializable * * @param name * @return * @throws RemoteException */ UserInfo getUserInfo(String name) throws RemoteException; }
3、接口實現類

package city.albert; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:18 PM */ public class UserServiceImpl extends UnicastRemoteObject implements UserService { /** * 因為UnicastRemoteObject的構造方法拋出了RemoteException異常,因此這里默認的構造方法必須寫,必須 聲明拋出RemoteException異常 * * @throws RemoteException */ protected UserServiceImpl() throws RemoteException { } @Override public UserInfo getUserInfo(String name) throws RemoteException { System.out.println("name=" + name); return new UserInfo(name, 18); } }
4、服務端注冊啟動注冊

package city.albert; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:19 PM */ public class Application { public static void main(String[] args) throws RemoteException { //創建注冊實例 UserService service = new UserServiceImpl(); //創建注冊表對象綁定端口 Registry registry = LocateRegistry.createRegistry(8088); /** * //綁定的URL標准格式為:rmi://host:port/name(其中協議名可以省略,下面兩種寫法都是正確的) * 第一種:rmi://host:port/name * 第二種://host:port/name */ try { //綁定注冊實例 Naming.bind("rmi://127.0.0.1:8088/user", service); System.out.println("注冊成功"); } catch (Exception e) { e.printStackTrace(); } } }
5、客戶端啟動調用

package city.albert; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:26 PM */ public class CliApplication { public static void main(String[] args) { try { //獲取注冊的注冊對象 UserService service = (UserService) Naming.lookup("rmi://127.0.0.1:8088/user"); //通過動態代理對象調用方法 UserInfo info = service.getUserInfo("張三"); System.out.println(info); } catch (NotBoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }
客戶端結果打印
四、IO模型:BIO、AIO、NIO
1、同步異步:是指應用程序與內核之間的通信方式,同步是用戶進程出發IO操作或者輪詢方式判斷讀寫操作是否就緒,異步是通過事件通知是否就緒。
2、BIO同步阻塞IO模型,就是服務器模式為一個連接一個線程
BIO、AIO、NIO:https://www.imooc.com/article/265871
五、netty