微服務設計筆記(8)—— RPC 調用方式

RPC(Remote Procedure Call)—遠程過程調用,它是一種不須要了解底層網絡技術的協議,就能夠經過網絡,請求遠程服務器上的服務。java

咱們能夠調用本地的一個 RPC , 實際上,響應結果是由遠程服務器返回的 。RPC 有不少種類型 , 好比 SOAP、Thrift、 protocol buffers 等等 )。不一樣的技術棧,能夠經過其接口定義,很方便地生成客戶端或服務端的樁代碼 。編程

  • SOAP(Simple Object Access Protocol),即簡單對象訪問協議。它是交換數據的一種協議規範,是一種輕量的、簡單的、基於XML的協議,它被設計成可在 WEB 上交換結構化或固化的信息。
  • Thrift 是一種接口描述語言和二進制通信協議,它被用來定義和建立跨語言服務 。它是由 Facebook 爲支持 「 大規模跨語言服務」 而開發的 。
  • protocol buffer 是 google 的一個開源項目。它可串行化結構化的數據。就像 XML ,但它比 XML 更小 、 更快 、 也更簡單 。 咱們能夠定義本身的數據結構,而後使用代碼生成器所生成的代碼來讀寫這個數據結構 。甚至能夠在無需從新部署程序的狀況下更新自定義的數據結構。

好比, 咱們可讓一個 Java 服務對外表現爲一個 SOAP 服務接口 , 調用方能夠依據使用 WSDL( Web Service Definition Language,Web 服務描述語言 ) 接口定義內容,來生成基於 .NET 的客戶端代碼 。 這些技術都有一個共同點 , 那就是使用本地調用的方式和遠程服務器進行交互。bash

Java RMI、 Thrift、 protocol buffers 是以二進制做爲消息格式;而 SOAP 用的是 XML,並且綁定特定的網絡協議(HTTP)。不一樣的網絡協議,特性也不一樣。好比, TCP 協議可以保證消息送達對端;而 UDP 雖然會丟包,但開銷較小。 因此咱們能夠根據實際應用場景來選擇不一樣的技術棧。服務器

這些 RPC 實現通常會提供工具,快速生成服務端或客戶端的樁代碼 , 這樣咱們就能夠直接開始編碼 。網絡

實際應用中,RPC 調用方式並無那麼好。一開始,問題還不那麼明顯 , 但慢慢就會暴露出來 , 其帶來的負面影響要遠遠大於一開始快速編碼所帶來的好處。數據結構

(1)耦合編程語言

好比 Java RMI(Remote Method Invocation), 會致使服務端和客戶端緊密耦合 , 由於雙方都必須使用相同的 Java 技術棧。而 Thrift 和 protocol buffers 能夠支持不一樣編程語言 , 從而在必定程度上緩解了這個問題 。工具

(2)遠程調用的複雜性性能

RPC 的原意是隱藏遠程調用的複雜性 。但遠程調用特定涉及網絡通訊時間、對傳輸對象的序列化與反序列化,這樣都會影響性能。ui

還有網絡自己並不可靠。因此即便客戶端和服務端都正常,也會由於網絡問題,致使服務調用失敗。還有黑客攻擊狀況也要予以考慮。

(3)脆弱性

假設咱們使用 Java RMI 定義了一個服務接口:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface CustomerRemote extends Remote {
    /**
     * 查找客戶
     *
     * @param id
     * @return
     * @throws RemoteException
     */
    Customer find(String id) throws RemoteException;

    /**
     * 建立客戶
     * @param firstname
     * @param surname
     * @param email
     * @return
     * @throws RemoteException
     */
    Customer create(String firstname, String surname, String email) throws RemoteException;

}
複製代碼

在這個接口定義中 , 「建立客戶」 方法,接受姓名及電子郵件做爲入參 。 若是客戶端但願只經過電子郵件就能夠建立客戶,咱們能夠在這個接口中,新定義一個方法 , 以下所示:

Customer create( String email) throws RemoteException;
複製代碼

由於從新定義了接口,因此全部的客戶端都須要從新生成樁,即便某些客戶端根本不須要這個新方法 。這是一個廣泛現象,因此認爲RPC 調用方式是脆弱的。

此外,還有一種形式的脆弱。 如今讓咱們來看看 Customer 對象:

import java.io.Serializable;

public class Customer implements Serializable {
    private String firstName;
    private String surName;
    private String email;
    private String age;
}

複製代碼

這裏的 Customer 客戶對象,除了以前在接口中所看到的 firstName、surName 和 age 以外,還定義了 age 屬性。後來發現這個屬性,徹底沒有任何客戶端在使用它,是一個冗餘字段。但不能直接在服務端刪除它,由於會影響各個調用者的 Customer 客戶對象,即便是基於二進制消息格式的 RPC 也存在一樣的問題,即服務端和客戶端沒法實現部署分離。


若是必定要選用 RPC 調用方式,那麼注意不要對遠程調用過分抽象,讓客戶端留意網絡調用的影響。還要確保咱們能夠獨立地升級服務端接口,而不是強迫客戶端升級。

相關文章
相關標籤/搜索