摘要:我要學(xué)好分布式通信框架技術(shù)我要學(xué)好分布式分布式框架是最近幾年的熱門。先寫個(gè)測(cè)試用的遠(yuǎn)程接口,注意接口要拋異常實(shí)現(xiàn)遠(yuǎn)程接口,并且繼承創(chuàng)建服務(wù)器程序方法注冊(cè)遠(yuǎn)程對(duì)象創(chuàng)建客戶端程序調(diào)用過程流程去注冊(cè)中心注冊(cè),端啟動(dòng)服務(wù)。
title: 我要學(xué)好分布式-RMI通信框架
date: 2018-07-26 19:28:30
分布式框架是最近幾年的熱門??墒且肜斫夥植际娇蚣苤鴮?shí)不易,為了努力跟上時(shí)代潮流,特此開了一個(gè)專題,起名“我要學(xué)好分布式”,通過博客來分享一下我的學(xué)習(xí)過程,加深我對(duì)分布式整體框架的理解。
想要解鎖更多新姿勢(shì)?請(qǐng)?jiān)L問我的博客
什么是RPC英文就不說了。中文名遠(yuǎn)程進(jìn)程調(diào)用協(xié)議。顧名思義,客戶端在不知道細(xì)節(jié)的情況下,可以調(diào)用遠(yuǎn)程計(jì)算機(jī)的api,就像是調(diào)用本地方法一樣。
RPC協(xié)議是一個(gè)規(guī)范。主流的PRC協(xié)議有Dubbo、Thrif、RMI、Webservice、Hessain
他又一個(gè)非常大的特點(diǎn),網(wǎng)絡(luò)協(xié)議和網(wǎng)絡(luò)IO對(duì)于調(diào)用端和服務(wù)端來說是透明的(動(dòng)態(tài)代理)
一個(gè)RPC框架包含的要素:
RMIRMI(remote method invocation) ?, 可以認(rèn)為是RPC的java版本
RMI使用的是JRMP(Java Remote Messageing Protocol), JRMP是專門為java定制的通信協(xié)議,所以他是純java的分布式解決方案 。注意,這個(gè)RMI已經(jīng)老舊過時(shí)了。
RMI Demo先寫個(gè)測(cè)試用的遠(yuǎn)程接口,注意接口要拋異常
public interface ISayHello extends Remote { public String satHello(String name) throws RemoteException; }
? 2.實(shí)現(xiàn)遠(yuǎn)程接口,并且繼承:UnicastRemoteObject
public class SayHelloImpl extends UnicastRemoteObject implements ISayHello{ protected SayHelloImpl() throws RemoteException { } public String satHello(String name) throws RemoteException { return "hello," + name; } }
? 3.創(chuàng)建服務(wù)器程序: createRegistry方法注冊(cè)遠(yuǎn)程對(duì)象
import java.net.MalformedURLException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; public class HelloServer { public static void main(String[] args) { try { ISayHello sayHello =new SayHelloImpl(); LocateRegistry.createRegistry(8888); Naming.bind("rmi://localhost:8888/sayhello",sayHello); System.out.println("server start success"); } catch (RemoteException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } }
? 4.創(chuàng)建客戶端程序
public class HelloClient { public static void main(String[] args) { try { ISayHello iSayHello = (ISayHello) Naming.lookup("rmi://localhost:8888/sayhello"); System.out.println("hello"); } catch (NotBoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }RMI調(diào)用過程
流程:
1.去注冊(cè)中心注冊(cè),server端啟動(dòng)服務(wù)。
2.注冊(cè)中心聯(lián)系stub(存根)。stub用于客戶端 ,在j2ee中是這么說的:為屏蔽客戶調(diào)用遠(yuǎn)程主機(jī)上的對(duì)象,必須提供某種方式來模擬本地對(duì)象,這種本地對(duì)象稱為存根(stub),存根負(fù)責(zé)接收本地方法調(diào)用,并將它們委派給各自的具體實(shí)現(xiàn)對(duì)象
3.server注冊(cè)對(duì)象,然后返回注冊(cè)對(duì)象
4.客戶端訪問注冊(cè)中心,(動(dòng)態(tài)代理)返回stub對(duì)象
5.stub(存根)遠(yuǎn)程調(diào)用skeleton (骨架 )
6.skeleton 調(diào)用相應(yīng)接口
源碼讓我看看核心的注冊(cè)服務(wù)的源碼實(shí)現(xiàn)
public RegistryImpl(final int var1) throws RemoteException { this.bindings = new Hashtable(101); //安全認(rèn)證 if (var1 == 1099 && System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Void run() throws RemoteException { LiveRef var1x = new LiveRef(RegistryImpl.id, var1); RegistryImpl.this.setup(new UnicastServerRef(var1x, (var0) -> { return RegistryImpl.registryFilter(var0); })); return null; } }, (AccessControlContext)null, new SocketPermission("localhost:" + var1, "listen,accept")); } catch (PrivilegedActionException var3) { throw (RemoteException)var3.getException(); } } else { //初始化遠(yuǎn)程引用UnicastServerRef對(duì)象 LiveRef var2 = new LiveRef(id, var1);//《-------------------------- this.setup(new UnicastServerRef(var2, RegistryImpl::registryFilter)); } }
點(diǎn)進(jìn)UnicastServerRef,找出實(shí)現(xiàn)的關(guān)系~
點(diǎn)進(jìn)setup方法,用idea反編碼
public Remote exportObject(Remote var1, Object var2, boolean var3) throws RemoteException { Class var4 = var1.getClass(); Remote var5; try { var5 = Util.createProxy(var4, this.getClientRef(), this.forceStubUse);//《-------------------- } catch (IllegalArgumentException var7) { throw new ExportException("remote object implements illegal remote interface", var7); } if (var5 instanceof RemoteStub) {//《-------------------------- this.setSkeleton(var1); } Target var6 = new Target(var1, this, var5, this.ref.getObjID(), var3);//《------------------------ this.ref.exportObject(var6); this.hashToMethod_Map = (Map)hashToMethod_Maps.get(var4); return var5; }
發(fā)現(xiàn)在創(chuàng)建代理,判斷當(dāng)前的var是不是遠(yuǎn)程stub,如果是就設(shè)置骨架。如果不是,就構(gòu)建target對(duì)象。點(diǎn)開代理
public static Remote createProxy(Class> var0, RemoteRef var1, boolean var2) throws StubNotFoundException { Class var3; try { var3 = getRemoteClass(var0);//《-------------------------- } catch (ClassNotFoundException var9) { throw new StubNotFoundException("object does not implement a remote interface: " + var0.getName()); } if (var2 || !ignoreStubClasses && stubClassExists(var3)) { return createStub(var3, var1);//《-------------------------- } else { final ClassLoader var4 = var0.getClassLoader(); final Class[] var5 = getRemoteInterfaces(var0); final RemoteObjectInvocationHandler var6 = new RemoteObjectInvocationHandler(var1); try { return (Remote)AccessController.doPrivileged(new PrivilegedAction() { public Remote run() { return (Remote)Proxy.newProxyInstance(var4, var5, var6); } }); } catch (IllegalArgumentException var8) { throw new StubNotFoundException("unable to create proxy", var8); } } }
發(fā)現(xiàn)在調(diào)用遠(yuǎn)程服務(wù),然后創(chuàng)建了stub。繼續(xù)點(diǎn)開getRemoteClass()方法
private static Class> getRemoteClass(Class> var0) throws ClassNotFoundException { while(var0 != null) { Class[] var1 = var0.getInterfaces();//《-------------------------- for(int var2 = var1.length - 1; var2 >= 0; --var2) { if (Remote.class.isAssignableFrom(var1[var2])) { return var0; } } var0 = var0.getSuperclass(); } throw new ClassNotFoundException("class does not implement java.rmi.Remote"); }
發(fā)現(xiàn)現(xiàn)在在創(chuàng)建實(shí)例
好吧,回到createProxy方法,再看看順著往下走,看看Target var6 = new Target(var1, this, var5, this.ref.getObjID(), var3);
`this.ref.exportObject(var6);`的出口對(duì)象方法
public void exportObject(Target var1) throws RemoteException { this.ep.exportObject(var1); }
public interface Endpoint { Channel getChannel(); void exportObject(Target var1) throws RemoteException; Transport getInboundTransport(); Transport getOutboundTransport(); }
public void exportObject(Target var1) throws RemoteException { this.transport.exportObject(var1); }
一路點(diǎn)下去,找到了tcp出口的方法。這是屬于協(xié)議層的玩意。
public void exportObject(Target var1) throws RemoteException { synchronized(this) { this.listen(); ++this.exportCount; }
一路點(diǎn)下去,發(fā)現(xiàn)listen。
private void listen() throws RemoteException { assert Thread.holdsLock(this); TCPEndpoint var1 = this.getEndpoint(); int var2 = var1.getPort(); if (this.server == null) { if (tcpLog.isLoggable(Log.BRIEF)) { tcpLog.log(Log.BRIEF, "(port " + var2 + ") create server socket"); } try { this.server = var1.newServerSocket();//《-------------------------- Thread var3 = (Thread)AccessController.doPrivileged(new NewThreadAction(new TCPTransport.AcceptLoop(this.server), "TCP Accept-" + var2, true)); var3.start(); } catch (BindException var4) { throw new ExportException("Port already in use: " + var2, var4); } catch (IOException var5) { throw new ExportException("Listen failed on port: " + var2, var5); } } else { SecurityManager var6 = System.getSecurityManager(); if (var6 != null) { var6.checkListen(var2); } }
發(fā)現(xiàn)newServerSocket?。?!
綜上,總體流程和上圖一樣。
RMI缺陷1.基于java,支持語言單一
2.服務(wù)注冊(cè)只能注冊(cè)到我上面分析的那個(gè)源碼。注冊(cè)中心掛了以后就完了
3.序列化是用java原生那個(gè)方法,效率不好
4.服務(wù)端底層是bio方式,性能不好
手寫RMI步驟:
編寫服務(wù)器程序,暴露一個(gè)監(jiān)聽, 可以使用socket
編寫客戶端程序,通過ip和端口連接到指定的服務(wù)器,并且將數(shù)據(jù)做封裝(序列化)
服務(wù)器端收到請(qǐng)求,先反序列化。再進(jìn)行業(yè)務(wù)邏輯處理。把返回結(jié)果序列化返回
源碼:https://github.com/tengshe789...
把源碼發(fā)布到GitHub了,在把源碼粘貼太麻煩了。
結(jié)束
此片完了~ 想要了解更多精彩新姿勢(shì)?請(qǐng)?jiān)L問我的個(gè)人博客 .
本篇為原創(chuàng)內(nèi)容,已在個(gè)人博客率先發(fā)表,隨后CSDN,segmentfault,juejin同步發(fā)出。如有雷同,緣分呢兄弟。趕快加個(gè)好友~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76629.html
摘要:微軟的雖然引入了事件機(jī)制,可以在隊(duì)列收到消息時(shí)觸發(fā)事件,通知訂閱者。由微軟作為主要貢獻(xiàn)者的,則對(duì)以及做了進(jìn)一層包裝,并能夠很好地實(shí)現(xiàn)這一模式。 在分布式服務(wù)框架中,一個(gè)最基礎(chǔ)的問題就是遠(yuǎn)程服務(wù)是怎么通訊的,在Java領(lǐng)域中有很多可實(shí)現(xiàn)遠(yuǎn)程通訊的技術(shù),例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,這些名詞之間到底是些什么關(guān)系呢,它們背后到底是基...
摘要:微軟的雖然引入了事件機(jī)制,可以在隊(duì)列收到消息時(shí)觸發(fā)事件,通知訂閱者。由微軟作為主要貢獻(xiàn)者的,則對(duì)以及做了進(jìn)一層包裝,并能夠很好地實(shí)現(xiàn)這一模式。 在分布式服務(wù)框架中,一個(gè)最基礎(chǔ)的問題就是遠(yuǎn)程服務(wù)是怎么通訊的,在Java領(lǐng)域中有很多可實(shí)現(xiàn)遠(yuǎn)程通訊的技術(shù),例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,這些名詞之間到底是些什么關(guān)系呢,它們背后到底是基...
摘要:對(duì)于與而言,則可以看做是消息傳遞技術(shù)的一種衍生或封裝。在生產(chǎn)者通知消費(fèi)者時(shí),傳遞的往往是消息或事件,而非生產(chǎn)者自身。通過消息路由,我們可以配置路由規(guī)則指定消息傳遞的路徑,以及指定具體的消費(fèi)者消費(fèi)對(duì)應(yīng)的生產(chǎn)者。采用和來進(jìn)行遠(yuǎn)程對(duì)象的通訊。 消息模式 歸根結(jié)底,企業(yè)應(yīng)用系統(tǒng)就是對(duì)數(shù)據(jù)的處理,而對(duì)于一個(gè)擁有多個(gè)子系統(tǒng)的企業(yè)應(yīng)用系統(tǒng)而言,它的基礎(chǔ)支撐無疑就是對(duì)消息的處理。與對(duì)象不同,消息本質(zhì)上...
摘要:具體可以參考消息隊(duì)列之具體可以參考實(shí)戰(zhàn)之快速入門十分鐘入門阿里中間件團(tuán)隊(duì)博客是一個(gè)分布式的可分區(qū)的可復(fù)制的基于發(fā)布訂閱的消息系統(tǒng)主要用于大數(shù)據(jù)領(lǐng)域當(dāng)然在分布式系統(tǒng)中也有應(yīng)用。目前市面上流行的消息隊(duì)列就是阿里借鑒的原理用開發(fā)而得。 我自己總結(jié)的Java學(xué)習(xí)的系統(tǒng)知識(shí)點(diǎn)以及面試問題,目前已經(jīng)開源,會(huì)一直完善下去,歡迎建議和指導(dǎo)歡迎Star: https://github.com/Snail...
摘要:都是分開部署,單獨(dú)上線的。序列化畢竟是遠(yuǎn)程通信,需要將對(duì)象轉(zhuǎn)化成二進(jìn)制流進(jìn)行傳輸。服務(wù)化架構(gòu)的演進(jìn)架構(gòu)當(dāng)業(yè)務(wù)規(guī)模很小時(shí),將所有功能都不熟在同一個(gè)進(jìn)程中,通過雙機(jī)或者負(fù)載均衡器實(shí)現(xiàn)負(fù)債分流此時(shí),分離前后臺(tái)邏輯的架構(gòu)是關(guān)鍵。 showImg(https://segmentfault.com/img/bVbiI2F?w=2250&h=1500); 前言 為什么需要RPC,而不是簡(jiǎn)單的HTTP...
閱讀 740·2021-11-17 09:33
閱讀 3771·2021-09-01 10:46
閱讀 1762·2019-08-30 11:02
閱讀 3290·2019-08-29 15:05
閱讀 1407·2019-08-26 11:39
閱讀 2283·2019-08-23 17:04
閱讀 1982·2019-08-23 15:43
閱讀 1379·2019-08-23 14:12