自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

動(dòng)態(tài)代理技術(shù)與RPC架構(gòu)解析

原創(chuàng) 精選
開發(fā) 架構(gòu)
動(dòng)態(tài)代理技術(shù)與RPC(Remote Procedure Call)架構(gòu)是現(xiàn)代分布式系統(tǒng)中常用的兩項(xiàng)關(guān)鍵技術(shù),二者結(jié)合可以極大地提高系統(tǒng)的靈活性和可擴(kuò)展性。

作者 | 張凱

審校 | 重樓

動(dòng)態(tài)代理技術(shù)與RPC(Remote Procedure Call)架構(gòu)是現(xiàn)代分布式系統(tǒng)中常用的兩項(xiàng)關(guān)鍵技術(shù),二者結(jié)合可以極大地提高系統(tǒng)的靈活性和可擴(kuò)展性將動(dòng)態(tài)代理技術(shù)與RPC架構(gòu)結(jié)合使用,可以實(shí)現(xiàn)自動(dòng)化的服務(wù)調(diào)用和增強(qiáng)功能。開發(fā)者可以專注于業(yè)務(wù)邏輯的實(shí)現(xiàn),而不必?fù)?dān)心底層細(xì)節(jié)。這種結(jié)合不僅提高了代碼的復(fù)用性和可維護(hù)性,也增強(qiáng)了系統(tǒng)的監(jiān)控和管理能力。本文將詳細(xì)解析這兩項(xiàng)技術(shù)的實(shí)現(xiàn)方式以及其背后的設(shè)計(jì)邏輯。

一、動(dòng)態(tài)代理

1. 代理模式

代理模式為其他對(duì)象提供了一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn),根據(jù)代理類的創(chuàng)建時(shí)機(jī)和創(chuàng)建方式的不同,可以將其分為靜態(tài)代理和動(dòng)態(tài)代理兩種形式:在程序運(yùn)行前就已經(jīng)存在的編譯好的代理類稱為靜態(tài)代理,在程序運(yùn)行期間根據(jù)需要?jiǎng)討B(tài)創(chuàng)建代理類及其實(shí)例來(lái)完成具體的功能稱為動(dòng)態(tài)代理。

代理模式的目的是為真實(shí)業(yè)務(wù)對(duì)象提供一個(gè)代理對(duì)象以控制對(duì)真實(shí)業(yè)務(wù)對(duì)象的訪問(wèn),代理對(duì)象的作用有:

  • 代理對(duì)象存在的價(jià)值主要用于攔截對(duì)真實(shí)業(yè)務(wù)對(duì)象的訪問(wèn);
  • 代理對(duì)象可以和目標(biāo)對(duì)象(真實(shí)業(yè)務(wù)對(duì)象)實(shí)現(xiàn)共同的接口或繼承于同一個(gè)類;
  • 代理對(duì)象是對(duì)目標(biāo)對(duì)象的增強(qiáng),以便對(duì)消息進(jìn)行預(yù)處理和后處理。

2. 反射

動(dòng)態(tài)代理的實(shí)現(xiàn)核心是反射,一切動(dòng)態(tài)代理的代理操作都是反射實(shí)現(xiàn)的。所以要先對(duì)反射知識(shí)有一定的了解。

2.1 反射實(shí)現(xiàn)步驟

2.1.1通過(guò)反射獲取對(duì)象的.class文件,也就是class對(duì)象(有三種方式)。

 //方式一:使用Class.forName方法進(jìn)行包名+類名的定位 
 Class c1=Class.forName("zk.reflect.Person"); 
 //方式二:采用類名.class方法獲取反射 
 Class c2=Person.class; 
 //方式三:采用對(duì)象名.class方法獲取反射(運(yùn)行過(guò)程中) 
 Person ps=new Person(); 
 Class c3 = ps.getClass(); 

2.1.2通過(guò)反射獲取的對(duì)象,獲取對(duì)象類中的方法,使用invoke([實(shí)例化對(duì)象],[方法對(duì)應(yīng)的參數(shù)])方法,進(jìn)行方法使用。

//獲取類的方法 
Method[] m =c.getMethods(); //獲得本類和其父類的全部public方法 
Method[] m2=c.getDeclaredMethods(); //僅獲取本類的全部方法(包括私有方法) 
//獲取指定方法,并使用 
 //通過(guò)實(shí)例對(duì)象獲取反射 
 //Person p =new Person(); 
 //獲取Person類對(duì)象 
 //Class c=p.getClass(); 
 Class c=Person.class; 
 Person p= (Person) c.newInstance(); 
 //獲取該類實(shí)例對(duì)象中的具體方法--第一個(gè)參數(shù)要寫Person類的方法名稱才能匹配上;后面參數(shù)是方法參數(shù)類型 
 Method m=c.getDeclaredMethod("eat",String.class); 
 //使用invoke方法對(duì)反射獲取的方法進(jìn)行激活---第一個(gè)參數(shù)是實(shí)例化對(duì)象,后面參數(shù)是方法對(duì)應(yīng)參數(shù)值 
 String s= (String) m.invoke(p,"zhangkai"); 
 System.out.println(s); 
//獲取指定方法,必須加入?yún)?shù),沒(méi)有加null;因?yàn)榇嬖谥剌d,只有方法名和參數(shù)個(gè)數(shù)兩個(gè)才能精確定位方法 
Method getid=c.getMethod("getid",null); 
Method setid=c.setMethod("setid",,int.class);    

3. 動(dòng)態(tài)代理原理

對(duì)代理模式而言,具體主題類與其代理類一般是一一對(duì)應(yīng)的,這也是靜態(tài)代理的特點(diǎn)。但是,也存在這樣的情況:有N個(gè)主題類,但是代理類中的“預(yù)處理、后處理”都是相同的,僅僅是調(diào)用主題不同。那么,若采用靜態(tài)代理,那么必然需要手動(dòng)創(chuàng)建N個(gè)代理類,這顯然讓人相當(dāng)不爽。動(dòng)態(tài)代理則可以簡(jiǎn)單地為各個(gè)主題類分別生成代理類,共享“預(yù)處理,后處理”功能,這樣可以大大減小程序規(guī)模,這也是動(dòng)態(tài)代理的一大亮點(diǎn)。(通俗上來(lái)說(shuō),動(dòng)態(tài)代理不再是一個(gè)代理類代理一個(gè)委托類,而是像個(gè)大管家,指定那個(gè)委托對(duì)象,就代理誰(shuí)的方法,只不過(guò)代理類中通用的邏輯會(huì)適用于每個(gè)委托類)

在動(dòng)態(tài)代理中,代理類是在運(yùn)行時(shí)期生成的。因此,相比靜態(tài)代理,動(dòng)態(tài)代理可以很方便地對(duì)委托類的相關(guān)方法進(jìn)行統(tǒng)一增強(qiáng)處理,如添加方法調(diào)用次數(shù)、添加日志功能等等。動(dòng)態(tài)代理主要分為JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理兩大類。

下面以一個(gè)模擬動(dòng)態(tài)代理案例來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理的實(shí)現(xiàn)思路:

設(shè)計(jì)兩個(gè)Service類:User和Order,使用ProxyUtils類動(dòng)態(tài)代理這兩類的抽象接口。程序在調(diào)用過(guò)程中,通過(guò)接口直接到代理類中,由代理類實(shí)現(xiàn)接口實(shí)現(xiàn)類的功能以及代理類自身的一些增強(qiáng)功能和通用功能。

3.1 接口類

//User的一些方法 
public interface UserService { 
 String login(String s,String p) throws InterruptedException; 
 void selectById(int id); 
 void delect(); 
} 
//Order的一些方法 
public interface OrderService { 
 String zhuyi(); 
 void select(); 
 String delectById(int id); 
} 

3.2 實(shí)現(xiàn)類

//UserServiceImpl實(shí)現(xiàn)類 
public class UserServiceImpl implements UserService { 

 @Override 
 public String login(String name,String password) { 
 String resword="用戶名或密碼錯(cuò)誤!"; 
 if(name.equals("admin") && password.equals("123456")){ 
 resword="登錄成功"; 
 } 
 try { 
 Thread.sleep(2000); 
 } catch (InterruptedException e) { 
 e.printStackTrace(); 
 } 
 return resword; 
 } 

 @Override 
 public void selectById(int id) { 
 try { 
 Thread.sleep(1500); 
 } catch (InterruptedException e) { 
 e.printStackTrace(); 
 } 
 System.out.println(id+"的信息如下:----"); 
 } 

 @Override 
 public void delect() { 
 try { 
 Thread.sleep(3000); 
 } catch (InterruptedException e) { 
 e.printStackTrace(); 
 } 
 System.out.println("信息正在刪除。。。。。"); 
 } 
} 
//OrderServiceImpl實(shí)現(xiàn)類 
public class OrderServiceImpl implements OrderService{ 

 @Override 
 public String zhuyi() { 
 return "訂單功能區(qū):---------"; 
 } 

 @Override 
 public void select() { 
 System.out.println("全部訂單如下所示--------"); 
 } 

 @Override 
 public String delectById(int id) { 
 String s=id+"的信息正在刪除。。。。。"; 
 return s; 
 } 
} 

3.3動(dòng)態(tài)代理類ProxyUtils

  • 該類中的proxyService方法使用泛型可以讓proxyService方法代理多種不同類的Service接口;
  • 返回一個(gè)Proxy通過(guò)newProxyInstance()提供的實(shí)例對(duì)象;
  • newProxyInstance()需要三部分的參數(shù)。第一部分是創(chuàng)建方法形參對(duì)象obj的類加載器對(duì)象;第二部分是創(chuàng)建方法形參對(duì)象obj的類接口對(duì)象(都為固定方法);第三部分是創(chuàng)建一個(gè)InvocationHandler()方法,意味調(diào)用類對(duì)象方法的方法;
  • 代理類中的增強(qiáng)功能就可以寫在InvocationHandler()方法中,本例是實(shí)現(xiàn)了一個(gè)方法運(yùn)行時(shí)間的檢測(cè)功能。
public class ProxyTest { 
 public static void main(String[] args) throws Exception { 
 //創(chuàng)建一個(gè)User類的代理對(duì)象,代理對(duì)象中也傳入了一個(gè)新建的User實(shí)現(xiàn)類對(duì)象 
 UserService userService=ProxyUtils.proxyService(new UserServiceImpl()); 
 String rs = userService.login("admin", "123456"); 
 System.out.println(rs); 
 userService.selectById(2); 
 userService.delect(); 
 //創(chuàng)建一個(gè)Order類的代理對(duì)象 
 OrderService orderService=ProxyUtils.proxyService(new OrderServiceImpl()); 
 String ss = orderService.delectById(9); 
 System.out.println(ss); 
 } 
} 

以上就是動(dòng)態(tài)代理案例的全部實(shí)現(xiàn),核心的調(diào)用邏輯就是流程圖中的調(diào)用邏輯,使用動(dòng)態(tài)代理類不僅可以大幅提高代碼的復(fù)用程度,而且還可以在被代理類的基礎(chǔ)上實(shí)現(xiàn)一些公共的增強(qiáng)功能,這其實(shí)就是Spring中的AOP的核心實(shí)現(xiàn)原理。例如本例就實(shí)現(xiàn)了所有被代理Service的方法運(yùn)行時(shí)間檢測(cè)的功能,對(duì)于多個(gè)Service都實(shí)現(xiàn)檢測(cè)功能,其實(shí)就是一種橫向編程的思路。

、RPC架構(gòu)

1.RPC的出現(xiàn)原因

不同的業(yè)務(wù)拆分到多個(gè)應(yīng)用中,讓不同的應(yīng)用分別承擔(dān)不同的功能是解決這些問(wèn)題的必殺技。將不同業(yè)務(wù)分拆到不同的應(yīng)用后,不但可以大幅度提升系統(tǒng)的穩(wěn)定性還有助于豐富技術(shù)選型,進(jìn)一步保證系統(tǒng)的性能。總的來(lái)說(shuō),從單體應(yīng)用到分布式多體應(yīng)用是系統(tǒng)升級(jí)必經(jīng)之路。

當(dāng)一個(gè)單體應(yīng)用演化成多體應(yīng)用后,遠(yuǎn)程調(diào)用就粉墨登場(chǎng)了。在一個(gè)應(yīng)用時(shí),相互通信直接通過(guò)本地調(diào)用就可完成,而變?yōu)槎囿w應(yīng)用時(shí),相互通信就得依賴遠(yuǎn)程調(diào)用了,這時(shí)一個(gè)高效穩(wěn)定的RPC框架就顯得非常必要了。簡(jiǎn)單的HTTP調(diào)用雖然也可以實(shí)現(xiàn)遠(yuǎn)程通信,但效果不夠理想,原因有二:

  • RPC遠(yuǎn)程調(diào)用像本地調(diào)用一樣干凈簡(jiǎn)潔,但其他方式對(duì)代碼的侵入性就比較強(qiáng);
  • 一般使用RPC框架實(shí)現(xiàn)遠(yuǎn)程通信效率比其他方式效率要高一些。

2. RPC框架介紹

對(duì)于多體應(yīng)用,由于各服務(wù)部署在不同機(jī)器,服務(wù)間的調(diào)用免不了網(wǎng)絡(luò)通信過(guò)程,服務(wù)消費(fèi)方每調(diào)用一個(gè)服務(wù)都要寫較多網(wǎng)絡(luò)通信相關(guān)的代碼,不僅復(fù)雜而且極易出錯(cuò)。若能實(shí)現(xiàn)一種機(jī)制,使得開發(fā)者能夠像調(diào)用本地服務(wù)一般便捷地調(diào)用遠(yuǎn)程服務(wù),同時(shí)對(duì)網(wǎng)絡(luò)通信等底層細(xì)節(jié)保持透明,那么這將極大地釋放程序員的開發(fā)負(fù)擔(dān),顯著提升開發(fā)效率與生產(chǎn)力。比如,服務(wù)消費(fèi)方在執(zhí)行helloService.hi(“Panda”)時(shí),實(shí)質(zhì)上調(diào)用的是遠(yuǎn)端的服務(wù)。這種方式其實(shí)就是RPC(Remote Procedure Call),在各大互聯(lián)網(wǎng)公司中被廣泛使用,如阿里巴巴的HSF、Dubbo(開源)、Facebook的Thrift(開源)、Google GRPC(開源)、Twitter的Finagle(開源)等。

RPC的主要功能目標(biāo)是讓構(gòu)建分布式計(jì)算(應(yīng)用)更容易,在提供強(qiáng)大的遠(yuǎn)程調(diào)用能力時(shí)不損失本地調(diào)用的語(yǔ)義簡(jiǎn)潔性。為實(shí)現(xiàn)該目標(biāo),RPC框架需提供一種透明調(diào)用機(jī)制讓使用者不必顯式的區(qū)分本地調(diào)用和遠(yuǎn)程調(diào)用。要讓網(wǎng)絡(luò)通信細(xì)節(jié)對(duì)使用者透明,我們需要對(duì)通信細(xì)節(jié)進(jìn)行封裝,下面是一個(gè)RPC的經(jīng)典調(diào)用的流程,并且反映了所涉及到的一些通信細(xì)節(jié):

(1)服務(wù)消費(fèi)方(client)以本地調(diào)用方式調(diào)用服務(wù);

(2).client stub接收到調(diào)用后負(fù)責(zé)將方法、參數(shù)等組裝成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;

(3)client stub找到服務(wù)地址,并將消息發(fā)送到服務(wù)端;

(4)server stub收到消息后進(jìn)行解碼;

(5)server stub根據(jù)解碼結(jié)果反射調(diào)用本地的服務(wù);

(6)本地服務(wù)執(zhí)行并將結(jié)果返回給server stub;

(7)server stub將返回結(jié)果打包成消息并發(fā)送至消費(fèi)方;

(8)client stub接收到消息,并進(jìn)行解碼;

(9)服務(wù)消費(fèi)方得到最終結(jié)果。

RPC框架就是要將2~8這些步驟封裝起來(lái),讓用戶對(duì)這些細(xì)節(jié)透明,使得遠(yuǎn)程方法調(diào)用看起來(lái)像調(diào)用本地方法一樣。

下面給出一個(gè)實(shí)現(xiàn)簡(jiǎn)易R(shí)PC架構(gòu)的代碼。

從該RPC框架的簡(jiǎn)易實(shí)現(xiàn)來(lái)看,RPC客戶端邏輯是:1.首先創(chuàng)建Socket客戶端并與服務(wù)端建立鏈接;2.然后使用Java原生的序列化/反序列化機(jī)制將調(diào)用請(qǐng)求發(fā)送給客戶端,包括所調(diào)用方法的名稱、參數(shù)列表將服務(wù)端的響應(yīng)返回給用戶即可。至此,一次簡(jiǎn)單PRC調(diào)用的客戶端流程執(zhí)行完畢。特別地,從代碼實(shí)現(xiàn)來(lái)看,實(shí)現(xiàn)透明的PRC調(diào)用的關(guān)鍵就是動(dòng)態(tài)代理,這是RPC框架實(shí)現(xiàn)的靈魂所在。

2.1.1服務(wù)端

服務(wù)端提供客戶端所期待的服務(wù),一般包括三個(gè)部分:服務(wù)接口,服務(wù)實(shí)現(xiàn)以及服務(wù)的注冊(cè)暴露三部分

//服務(wù)端接口 
public interface HelloService { 
 String hello(String name); 
 String hi(String msg); 
} 

//服務(wù)實(shí)現(xiàn)類 
public class HelloServiceImpl implements HelloService{ 
 @Override 
 public String hello(String name) { 
 return "Hello " + name; 
 } 

 @Override 
 public String hi(String msg) { 
 return "Hi, " + msg; 
 } 
} 

//(重要)服務(wù)暴露 
public class RpcProvider { 
 public static void main(String[] args) throws Exception { 
 HelloService service = new HelloServiceImpl(); 
 // 自實(shí)現(xiàn)的RpcFramework,RPC框架使用export()方法將服務(wù)暴露出來(lái),供客戶端消費(fèi) 
 RpcFramework.export(service, 1234); 
 } 
} 

2.1.2客戶端

客戶端消費(fèi)服務(wù)端所提供的服務(wù),一般包括兩個(gè)部分:服務(wù)引用和服務(wù)接口兩個(gè)部分。

//(服務(wù)引用)消費(fèi)端通過(guò)RPC框架進(jìn)行遠(yuǎn)程調(diào)用,這也是RPC框架功能之一 
public class RpcConsumer { 
 public static void main(String[] args) throws Exception { 
 // 由RpcFramework類中refer()方法生成的HelloService接口的代理 
 HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234); 
 //使用代理對(duì)象進(jìn)行實(shí)現(xiàn)類中hello()方法的調(diào)用 
 String hello = service.hello("World"); 
 System.out.println("客戶端收到遠(yuǎn)程調(diào)用的結(jié)果 : " + hello); 
 } 
} 
//與服務(wù)端共享同一個(gè)服務(wù)接口
public interface HelloService {
 String hello(String name);
 String hi(String msg);
}

2.1.3 RPC框架類RpcFramework

public class RpcFramework { 
 /** 
 * 暴露服務(wù) 
 * @param service 服務(wù)實(shí)現(xiàn) 
 * @param port 服務(wù)端口 
 * @throws Exception 
 */ 
 public static void export(final Object service, int port) throws Exception { 
 //如果服務(wù)和端口存在問(wèn)題,拋出異常 
 if (service == null) { 
 throw new IllegalArgumentException("service instance == null"); 
 } 
 if (port <= 0 || port > 65535) { 
 throw new IllegalArgumentException("Invalid port " + port); 
 } 
 System.out.println("Export service " + service.getClass().getName() + " on port " + port); 
 // 建立Socket服務(wù)端 
 ServerSocket server = new ServerSocket(port); 
 for (; ; ) { 
 try { 
 // 監(jiān)聽Socket請(qǐng)求 
 final Socket socket = server.accept(); 
 new Thread(new Runnable() { 
 @Override 
 public void run() { 
 try { 
 try { 
 /* 獲取請(qǐng)求流,Server解析并獲取請(qǐng)求*/ 
 // 構(gòu)建對(duì)象輸入流,從源中讀取對(duì)象到程序中 
 ObjectInputStream input = new ObjectInputStream( 
 socket.getInputStream()); 
 try { 

 System.out.println("\nServer解析請(qǐng)求 : "); 
 String methodName = input.readUTF(); 
 System.out.println("methodName : " + methodName); 
 // 泛型與數(shù)組是不兼容的,除了通配符作泛型參數(shù)以外 
 Class<?>[] parameterTypes = (Class<?>[])input.readObject(); 
 System.out.println( 
 "parameterTypes : " + Arrays.toString(parameterTypes)); 
 Object[] arguments = (Object[])input.readObject(); 
 System.out.println("arguments : " + Arrays.toString(arguments)); 


 /* Server 處理請(qǐng)求,進(jìn)行響應(yīng)*/ 
 ObjectOutputStream output = new ObjectOutputStream( 
 socket.getOutputStream()); 
 try { 
 // service類型為Object的(可以發(fā)布任何服務(wù)),故只能通過(guò)反射調(diào)用處理請(qǐng)求 
 // 反射調(diào)用,處理請(qǐng)求 
 Method method = service.getClass().getMethod(methodName, 
 parameterTypes); 
 //invoke方法調(diào)用對(duì)應(yīng)方法,得到處理返回值保存在result中 
 Object result = method.invoke(service, arguments); 
 System.out.println("\nServer 處理并生成響應(yīng) :"); 
 System.out.println("result : " + result); 
 //將請(qǐng)求處理結(jié)果寫回socket信息 
 output.writeObject(result); 
 } catch (Throwable t) { 
 output.writeObject(t); 
 } finally { 
 output.close(); 
 } 
 } finally { 
 input.close(); 
 } 
 } finally { 
 socket.close(); 
 } 
 } catch (Exception e) { 
 e.printStackTrace(); 
 } 
 } 
 }).start(); 
 } catch (Exception e) { 
 e.printStackTrace(); 
 } 
 } 
 } 


 /** 
 * 引用服務(wù) 
 * 
 * @param <T> 接口泛型 
 * @param interfaceClass 接口類型 
 * @param host 服務(wù)器主機(jī)名 
 * @param port 服務(wù)器端口 
 * @return 遠(yuǎn)程服務(wù),返回代理對(duì)象 
 * @throws Exception 
 */ 
 @SuppressWarnings("unchecked") 
 public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception { 
 if (interfaceClass == null) { 
 throw new IllegalArgumentException("Interface class == null"); 
 } 
 // JDK 動(dòng)態(tài)代理的約束,只能實(shí)現(xiàn)對(duì)接口的代理 
 if (!interfaceClass.isInterface()) { 
 throw new IllegalArgumentException( 
 "The " + interfaceClass.getName() + " must be interface class!"); 
 } 
 if (host == null || host.length() == 0) { 
 throw new IllegalArgumentException("Host == null!"); 
 } 
 if (port <= 0 || port > 65535) { 
 throw new IllegalArgumentException("Invalid port " + port); 
 } 
 System.out.println( 
 "Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port); 

 // JDK 動(dòng)態(tài)代理,使用泛型實(shí)現(xiàn)廣泛代理 
 T proxy = (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), 
 new Class<?>[] {interfaceClass}, new InvocationHandler() { 
 // invoke方法本意是對(duì)目標(biāo)方法的增強(qiáng),在這里用于發(fā)送RPC請(qǐng)求和接收響應(yīng) 
 @Override 
 public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { 
 // 創(chuàng)建Socket客戶端,并與服務(wù)端建立鏈接 
 Socket socket = new Socket(host, port); 
 try { 
 /* 客戶端向服務(wù)端進(jìn)行請(qǐng)求,并將請(qǐng)求參數(shù)寫入流中*/ 
 // 將對(duì)象寫入到對(duì)象輸出流,并將其發(fā)送到Socket流中去 
 ObjectOutputStream output = new ObjectOutputStream( 
 socket.getOutputStream()); 
 try { 
 // 發(fā)送請(qǐng)求 
 System.out.println("\nClient發(fā)送請(qǐng)求 : "); 
 output.writeUTF(method.getName()); 
 System.out.println("methodName : " + method.getName()); 
 output.writeObject(method.getParameterTypes()); 
 System.out.println("parameterTypes : " + Arrays.toString(method 
 .getParameterTypes())); 
 output.writeObject(arguments); 
 System.out.println("arguments : " + Arrays.toString(arguments)); 
 /* 客戶端讀取并返回服務(wù)端的響應(yīng)*/ 
 ObjectInputStream input = new ObjectInputStream( 
 socket.getInputStream()); 
 try { 
 Object result = input.readObject(); 
 //如果result是一個(gè)異常說(shuō)明服務(wù)端返回沒(méi)成功,客戶端只能拋出異常 
 if (result instanceof Throwable) { 
 throw (Throwable)result; 
 } 
 System.out.println("\nClient收到響應(yīng) : "); 
 System.out.println("result : " + result); 
 return result; 
 } finally { 
 input.close(); 
 } 
 } finally { 
 output.close(); 
 } 
 } finally { 
 socket.close(); 
 } 
 } 
 }); 
 //給消費(fèi)端返回代理對(duì)象,供使用 
 return proxy; 
 } 

2.2關(guān)于RPC框架的解析與說(shuō)明

(1)RPC框架如何做到透明化遠(yuǎn)程服務(wù)調(diào)用

在封裝通信細(xì)節(jié)以實(shí)現(xiàn)用戶能夠像本地調(diào)用服務(wù)一樣便捷地訪問(wèn)遠(yuǎn)程服務(wù)的方案中,Java的動(dòng)態(tài)代理技術(shù)無(wú)疑是一個(gè)有效的解決方案。具體而言,Java支持兩種動(dòng)態(tài)代理的實(shí)現(xiàn)方式:JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理。盡管基于字節(jié)碼生成的CGLIB代理方法在功能上更為強(qiáng)大和高效,但由于其代碼維護(hù)的復(fù)雜性,許多RPC框架的實(shí)現(xiàn)仍然優(yōu)先選擇使用JDK動(dòng)態(tài)代理。

在此背景下,RPCFramework的invoke方法有效地封裝了與遠(yuǎn)端服務(wù)之間的通信細(xì)節(jié)。當(dāng)消費(fèi)者通過(guò)RPCFramework獲取服務(wù)提供者的接口后,在調(diào)用helloService.hi("Panda")方法時(shí),將直接觸發(fā)該invoke方法,進(jìn)而執(zhí)行與遠(yuǎn)程服務(wù)的交互。此種設(shè)計(jì)極大地簡(jiǎn)化了遠(yuǎn)程服務(wù)調(diào)用的復(fù)雜性,為開發(fā)者提供了便捷的使用體驗(yàn)。

(2)如何發(fā)布自己的服務(wù)

如何讓用戶高效地調(diào)用我們的服務(wù)?是否僅僅通過(guò)在代碼中硬編碼服務(wù)的IP地址和端口即可實(shí)現(xiàn)?實(shí)際上,在生產(chǎn)環(huán)境中,這種做法并不可行,因?yàn)榉?wù)的部署節(jié)點(diǎn)可能會(huì)頻繁地上線和下線。舉例而言,若發(fā)現(xiàn)現(xiàn)有的服務(wù)實(shí)例不夠應(yīng)付需求,需要增加新的服務(wù)節(jié)點(diǎn)時(shí),調(diào)用者必須手動(dòng)更新其配置以包含新的IP地址,甚至可能需要在負(fù)載均衡的上下文中實(shí)現(xiàn)輪詢調(diào)用。與此同時(shí),當(dāng)某一臺(tái)機(jī)器出現(xiàn)故障,導(dǎo)致服務(wù)不可用時(shí),調(diào)用者又必須手動(dòng)刪除失效機(jī)器的IP。此過(guò)程顯然是相當(dāng)繁瑣和低效的。

為了解決這一問(wèn)題,應(yīng)當(dāng)采用一種能夠?qū)崿F(xiàn)自動(dòng)通知的機(jī)制,使得服務(wù)節(jié)點(diǎn)的變更對(duì)調(diào)用方保持透明,從而免去硬編碼服務(wù)提供者地址的需要。在實(shí)際生產(chǎn)中,許多RPC框架已經(jīng)實(shí)現(xiàn)了此類自動(dòng)告知功能。例如,阿里巴巴內(nèi)部使用的RPC框架HSF通過(guò)ConfigServer來(lái)管理服務(wù)的注冊(cè)與發(fā)現(xiàn)。此外,Zookeeper也廣泛應(yīng)用于實(shí)現(xiàn)服務(wù)的自動(dòng)注冊(cè)與發(fā)現(xiàn)。無(wú)論采用何種具體技術(shù),這些解決方案通?;诎l(fā)布/訂閱模式,以有效管理服務(wù)的可用性與負(fù)載均衡。

(3)序列化與反序列化

在Java中,對(duì)象無(wú)法直接在網(wǎng)絡(luò)中進(jìn)行傳輸,因此在進(jìn)行RPC請(qǐng)求時(shí),客戶端如何將請(qǐng)求發(fā)送給服務(wù)端,并如何接收服務(wù)端的響應(yīng),成為一個(gè)需要解決的問(wèn)題。解決方案在于對(duì)Java對(duì)象進(jìn)行序列化,以便將其轉(zhuǎn)換為可傳輸?shù)淖止?jié)流,并在接收端通過(guò)反序列化過(guò)程將字節(jié)流還原為原始對(duì)象,從而便于后續(xù)的處理。

實(shí)際上,序列化和反序列化的技術(shù)存在多種實(shí)現(xiàn)方式,包括Java的原生序列化、JSON格式、阿里巴巴的Hessian序列化以及ProtoBuf序列化等。這些不同的序列化方式在傳輸效率上各有差異,同時(shí)也具備各自的特點(diǎn)和優(yōu)勢(shì),適用于不同場(chǎng)景的需求。因此,選擇適當(dāng)?shù)男蛄谢桨笇?duì)于提升RPC框架的性能和可用性至關(guān)重要。

三、總結(jié)

動(dòng)態(tài)代理允許在運(yùn)行時(shí)創(chuàng)建對(duì)象的代理,從而對(duì)方法調(diào)用進(jìn)行攔截和處理。在Java等編程語(yǔ)言中,利用反射機(jī)制,可以動(dòng)態(tài)創(chuàng)建代理類。這種方式使得開發(fā)人員可以在不修改已有代碼的情況下,增強(qiáng)或改變方法的行為。本文通過(guò)構(gòu)建實(shí)例的方式詳細(xì)解釋了其工作原理。RPC(遠(yuǎn)程過(guò)程調(diào)用)是一種允許程序在不同的地址空間(通常是不同機(jī)器上的進(jìn)程)中調(diào)用子程序或服務(wù)的協(xié)議。通過(guò)RPC,開發(fā)者可以像調(diào)用本地方法一樣,方便地調(diào)用遠(yuǎn)程服務(wù),隱藏了網(wǎng)絡(luò)通信的復(fù)雜性。

通過(guò)動(dòng)態(tài)代理技術(shù)與RPC架構(gòu)的結(jié)合,可以實(shí)現(xiàn)以下優(yōu)勢(shì):

  1. 透明的遠(yuǎn)程調(diào)用:使用動(dòng)態(tài)代理,開發(fā)者在調(diào)用遠(yuǎn)程服務(wù)時(shí)無(wú)需關(guān)心具體的網(wǎng)絡(luò)請(qǐng)求和響應(yīng)處理,可以像調(diào)用本地方法一樣使用。
  2. 增強(qiáng)的功能:通過(guò)動(dòng)態(tài)代理,能夠在服務(wù)調(diào)用時(shí)加入額外的處理邏輯,如日志記錄、方法監(jiān)控、錯(cuò)誤處理等。
  3. 解耦合:RPC客戶端和服務(wù)端的實(shí)現(xiàn)可通過(guò)接口進(jìn)行解耦,更新和維護(hù)服務(wù)時(shí)不會(huì)影響到調(diào)用方的代碼。
  4. 優(yōu)化和擴(kuò)展:因?yàn)槭褂昧藙?dòng)態(tài)代理,增加新的功能或改變業(yè)務(wù)邏輯時(shí),只需更改代理的實(shí)現(xiàn)即可,而不需要修改每一個(gè)具體的服務(wù)調(diào)用。

動(dòng)態(tài)代理技術(shù)和RPC架構(gòu)的結(jié)合為分布式系統(tǒng)的開發(fā)提供了靈活和高效的解決方案。它們使得系統(tǒng)能夠在復(fù)雜的基礎(chǔ)設(shè)施中輕松處理遠(yuǎn)程服務(wù)的調(diào)用,同時(shí)還增強(qiáng)了代碼的可重用性和可維護(hù)性。這種技術(shù)的廣泛應(yīng)用,無(wú)疑對(duì)現(xiàn)代軟件架構(gòu)的演變產(chǎn)生了深遠(yuǎn)的影響。

作者介紹

張凱,中國(guó)農(nóng)業(yè)銀行股份有限公司研發(fā)中心軟件研發(fā)工程師,擅長(zhǎng)SpringBoot+Vue全棧式開發(fā),數(shù)據(jù)挖掘與建模,熱愛(ài)編程和學(xué)習(xí)前沿技術(shù)信息了解內(nèi)部的實(shí)現(xiàn)邏輯。


責(zé)任編輯:華軒 來(lái)源: 51CTO
相關(guān)推薦

2022-04-02 07:52:47

DubboRPC調(diào)用動(dòng)態(tài)代理

2023-12-06 08:23:44

代理模式設(shè)計(jì)模式

2017-01-13 10:51:13

RPC模型解析

2017-05-11 21:30:01

Android動(dòng)態(tài)代理ServiceHook

2011-04-06 11:41:25

Java動(dòng)態(tài)代理

2012-08-10 13:55:56

Java動(dòng)態(tài)代理

2017-02-24 17:24:16

Etcd架構(gòu)分布式系統(tǒng)

2012-08-28 10:59:26

JavaJava動(dòng)態(tài)代理Proxy

2009-06-22 09:56:00

J2EE web se

2011-07-03 19:45:32

SEO

2015-09-22 11:09:47

Java 8動(dòng)態(tài)代理

2011-03-23 10:40:51

java代理模式

2017-10-12 14:56:11

2025-02-27 00:32:35

2015-10-22 10:35:06

2020-12-28 07:47:35

動(dòng)態(tài)代理AOP

2022-01-26 00:05:00

AOPRPC遠(yuǎn)程調(diào)用

2009-05-13 09:10:59

Facebook存儲(chǔ)基礎(chǔ)架構(gòu)照片應(yīng)用程序

2017-02-21 13:16:49

微服務(wù)RPC技術(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)