簡易RPC框架-代理

来源:http://www.cnblogs.com/ASPNET2008/archive/2017/11/12/7821756.html
-Advertisement-
Play Games

代理 代理的意思就是請求者不直接與終端交互,而是通過一個中間者來做請求轉發,舉幾個生活中的代理案例: 翻牆上網 國外有很多網站在國內不能訪問,所以就需要利用一個代理中轉發請求,達到瞞天過海的目的。 用戶炒股 要想投資股票,你只能通過在證券商那開通賬戶然後,通過在證券商那提供的功能才能實現在上海證券交 ...


代理

代理的意思就是請求者不直接與終端交互,而是通過一個中間者來做請求轉發,舉幾個生活中的代理案例:

  • FQ上網

國外有很多網站在國內不能訪問,所以就需要利用一個代理中轉發請求,達到瞞天過海的目的。

  • 用戶炒股

要想投資股票,你只能通過在證券商那開通賬戶然後,通過在證券商那提供的功能才能實現在上海證券交易所交易股票。

  • 經濟人

下麵的看圖就明白了,不需要多做解釋。

RPC

RPC的意思是遠程過程調用,使客戶端調用遠程方法像調用本地方法一樣簡單,而不需要去關心如何與遠程伺服器通信相關問題:

  • 具體的通信協議選擇

比如是TCP通信還是基於HTTP通信,像dubbo預設是基於TCP的,噹噹在此基礎上擴展了劫持HTTP通信的擴展,spring cloud也是基於HTTP。

  • 具體的編碼方式

電腦之間通信時最需要按一定格式的數據進行傳輸,比如TCP通信時就需要將JAVA對象通過編碼轉換成位元組流,比如這兩對象:
MessageToByteEncoder與ByteToMessageDecoder

  • 具體數據傳輸

比如TCP傳輸時,各類問題:半包,粘包,延遲,超時,重連等處理。

  • ......

為了不在客戶端調用服務端時處理上述邏輯,就需要有一個專門處理上述問題的框架來協助,這裡可以利用JAVA提供的動態代理業完成。將請求委托給一個代理,這個代理去專門解決通信問題。

動態代理基礎

InvocationHandler

要想寫一個代理實現類,最簡單的方法就是實現InvocationHandler介面,它只包含一個介面方法:

Object invoke(Object proxy, Method method, Object[] args)

包含三個參數:

  • proxy

是指被代理的真實對象

  • method

是指我們需要執行的真實對象的方法

  • args

是指我們需要執行的真實對象的方法所需要的參數

此類需要配合下麵的Proxy類來創建動態代理,自身只是一個代理類的實現。

Proxy

這個類是用來創建真實對象代理類的,我這裡應用Proxy.newProxyInstance構建Rpc客戶端代理,它也有三個參數:

  • ClassLoader loader

是指由哪一個類載入器來載入生成的代理對象,一般我們就用真實對象所用的載入器即可。

  • Class<?>[] interfaces

是指真實對象都實現了哪些介面,介面確認之後才能調用其中的方法。

  • InvocationHandler h

是指產生的代理類在執行方法時所關聯的一個代理對象,即我們第一步提到的實現了InvocationHandler介面的實例,代理對象在執行方法時委托給這個關聯的handle去處理。

RPC客戶端代理實現

RpcProxy

編寫一個代理類RpcProxy,它用來處理TCP通信相關的問題,主要流程如下:

  • 組裝參數

從method以及args參數中獲取相應的值,填充到私有協議棧所需要的數據對象中(RpcRequest)。

  • 找一個可用的連接進行數據通信

從連接管理器(RpcClientInvokerManager)中獲取可用連接,構建處理請求鏈最後調用執行方法。

  • 返回結果

根據客戶端請求的方式,如果是需要返回值則返回一個Future對象供非同步回調,如果不關心返回值則直接返回空。

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        RpcRequest request = new RpcRequest();
        request.setRequestId(UUID.randomUUID().toString());
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setParameterTypes(method.getParameterTypes());
        request.setParameters(args);

        if (this.reference != null) {
            request.setMaxExecutesCount(this.reference.maxExecutesCount());
        }

        request.setContextParameters(RpcContext.getContext().getContextParameters());

        RpcClientInvoker invoker = RpcClientInvokerManager.getInstance(this.referenceConfig).getInvoker();
        invoker.setRpcRequest(request);

        RpcInvoker rpcInvoker=invoker.buildInvokerChain(invoker);
        ResponseFuture response=(ResponseFuture) rpcInvoker.invoke(invoker.buildRpcInvocation(request));

        if(isSync){
            return response.get();
        }
        else {
            RpcContext.getContext().setResponseFuture(response);
            return null;
        }
    }

RPC客戶端初始化遠程介面

  • 在RpcClient類中封裝一個創建代理的方法:
public <T> T createProxy(Class<T> interfaceClass,RpcReference reference) {
    return (T) Proxy.newProxyInstance(
            interfaceClass.getClassLoader(),
            new Class<?>[]{interfaceClass},
            new RpcProxy<T>(interfaceClass,this.referenceConfig,reference)
    );
}

通過上面代碼產後的代理,是在JVM運行時產生的,它即不是我們上面所提到的代理對象RpcProxy也不是我們的真實對象,它的主要作用就是在調用介面時,將invoke方法的執行委托給我們的代理對象RpcProxy,起到一個轉發的效果。

  • 通過註解自動生成代理

要想實現調用遠程介面與調用本地介面一樣簡單,思路就是在系統初始化時,掃描特殊註解的變數從而為變數創建代理對象。這裡可以藉助於BeanPostProcessor對象,它有一個初始化的方法:

public Object postProcessBeforeInitialization(Object bean, String beanName)

我們可以在這個函數中為特殊的變數調用RpcClient.createProxy生成代理,比如下麵的代碼會遍歷所有的欄位,如果欄位上標記了RpcReference註解,說明這是一個遠程介面,所以調用RpcClient調用createProxy生成代理對象。

public Object initRpcReferenceBean(Object bean, String beanName){
    Class<?> clazz = bean.getClass();
    if(isProxyBean(bean)){
        clazz = AopUtils.getTargetClass(bean);
    }

    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        try {
            if (! field.isAccessible()) {
                field.setAccessible(true);
            }
            RpcReference reference = field.getAnnotation(RpcReference.class);
            if (reference != null) {
                Object value=this.rpcClient.createProxy(field.getType(),reference);
                if (value != null) {
                    field.set(bean, value);
                }
            }
        } catch (Exception e) {
            throw new BeanInitializationException("Failed to init remote service reference at filed " + field.getName() + " in class " + bean.getClass().getName(), e);
        }
    }
    return bean;
}

RPC客戶端引用遠程介面

以下代碼是一個服務中依賴的變數,增加上@rpcreference之後說明介面是一個遠程介面。

@RpcReference(isSync = false)
private ProductService productServiceAsync;

方法中調用遠程介面:

public Product getById(Long productId){
   return this.productService.getById(productId);
}

業務代碼中,直接調用productServiceAsync中包含的方法即可,不需要去寫任何寫通信相關的代碼,實現了典型的遠程過程調用。

本文源碼

https://github.com/jiangmin168168/jim-framework

文中代碼是依賴上述項目的,如果有不明白的可下載源碼


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.查找文件find / -name filename.txt 根據名稱查找/目錄下的filename.txt文件。find . -name “*.xml” 遞歸查找所有的xml文件2.查看一個程式是否運行ps –ef|grep tomcat 查看所有有關tomcat的進程3.終止線程kill -9 ...
  • 在項目開發裡面,我遇到了這麼一個需求,就是對於node的title欄位,編輯內容的角色不允許對title進行編輯。title欄位是創建內容類型時自動生成的欄位,不能在drupal8後臺直接配置許可權,所以我需要用代碼自定義一個許可權。 1.在/modules/custom下自定義一個模塊,我的模塊名為o ...
  • Hadoop生態大數據系統分為Yam、 HDFS、MapReduce計算框架。TensorFlow分散式相當於MapReduce計算框架,Kubernetes相當於Yam調度系統。TensorFlowOnSpark,利用遠程直接記憶體訪問(Remote Direct Memory Access,RDM ...
  • PHP5以上提供了一個simpleXML對象來操作XML,把XML的節點轉換成對象和數組去操作。 ...
  • 在學習bootstrap的路上,需要使用roots主題,而roots是屬於wordpress的一個主題,那也開始了wordpress的探索~ 首先,使用wordpress我們需要一個必要的環境:PHP+Apache+Mysql。這裡我用的是集成環境,沒必要非獨立安裝。根據自己的操作系統自行下載即可~ ...
  • 1、初步認識 觀察者模式的定義: 在對象之間定義了一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象會收到通知並自動更新。 大白話: 其實就是發佈訂閱模式,發佈者發佈信息,訂閱者獲取信息,訂閱了就能收到信息,沒訂閱就收不到信息。 2、這個模式的結構圖 3、可以看到,該模式包含四個角色 抽象被觀 ...
  • 【為了方便獨立成文,原諒在內容排版上的一點點個人強迫症】 【本文內容由上一篇擴展論述(詳見:商城系統下單庫存管控系列雜記(一) http://www.cnblogs.com/bsfz/p/7801980.html)】 四、闡述關於併發環境中庫存管控的一些案例問題,以及涉及到的相關技術實現細節 ... ...
  • 一、概念 將一個類的介面轉換成客戶希望的另外一個介面,適配器模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。 二、模式動機 適配器的本質就是轉換類型(源介面的功能和目標介面的功能相同或者相近,如此轉換才有意義),目的就是復用已有的功能。 三、模式的結構 適配器跟據實現方式可以分為類適配 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...