網絡協議 20 - RPC 協議(上)- 基於XML的SOAP協議

【前五篇】系列文章傳送門:html

  1. 網絡協議 15 - P2P 協議:小種子大學問
  2. 網絡協議 16 - DNS 協議:網絡世界的地址簿
  3. 網絡協議 17 - HTTPDNS:私人定製的 DNS 服務
  4. 網絡協議 18 - CDN:家門口的小賣鋪
  5. 網絡協議 19 - RPC 協議綜述:遠在天邊,近在眼前

    上一節咱們瞭解 RPC 的經典模型和設計要點,並用最先期的 ONC RPC 爲例子,詳述了具體的實現。而時代在進步,ONC RPC 逐漸由於各類問題被替代,SOAP 協議就是替代者之一。網絡

ONC RPC 存在的問題

    ONC RPC 將客戶端要發送的參數,以及服務端要發送的回覆,都壓縮爲一個二進制串,這樣當然可以解決雙方的協議約定問題,可是存在必定的不方便。app

    首先,須要雙方的壓縮格式徹底一致,一點都不能差。一旦有少量的差錯,多一位,少一位或者錯一位,均可能形成沒法解壓縮。固然,咱們能夠用傳輸層的可靠性以及加入校驗值等方式,來減小傳輸過程當中的差錯。框架

    其次,協議修改不靈活。若是不是傳輸過程當中形成的差錯,而是客戶端由於業務邏輯的改變,添加或者刪除了字段,或者服務端添加或者刪除了字段,而雙方沒有及時通知,或者線上系統沒有及時升級,就會形成解壓縮不成功。函數

    於是,當業務發生改變,須要多傳輸一些參數或者少傳輸一些參數的時候,都須要及時通知對方,而且根據約定好的協議文件從新生成雙方的 Stub 程序。天然,這樣靈活性比較差。工具

    若是僅僅是溝通的問題也還好解決,其實更難弄的還有版本的問題。好比在服務端提供一個服務,參數的格式是版本一的,已經有 50 個客戶端在線上調用了。如今有一個客戶端有個需求,要加一個字段,怎麼辦呢?這但是一個大工程,全部的客戶端都要適配這個,須要從新寫程序,加上這個字段,可是傳輸值是 0,不須要這個字段的客戶端很「冤」,原本沒我啥事兒,爲啥讓我也忙活?網站

    最後,ONC RPC 的設計明顯是面向函數的,而非面向對象。而當前面向對象的業務邏輯設計與實現方式已經成爲主流。spa

    這一切的根源就在於壓縮。這就像平時咱們愛用縮略語。若是是籃球愛好者,你直接說 NBA,他立刻就知道什麼意思,可是若是你給一個大媽說 NBA,她可能就不知所云。設計

    因此,這種 RPC 框架只能用於客戶端和服務端全由一撥人開發的場景,或者至少客戶端和服務端的開發人員要密切溝通,相互合做,有大量的共同語言,才能按照既定的協議順暢地進行工做。調試

XML 與 SOAP

    可是,通常狀況下,咱們作一個服務,都是要提供給陌生人用的,你和客戶不會常常溝通,也沒有什麼共同語言。就像你給別人介紹 NBA,你要說美國職業籃球賽,這樣無論他是幹啥的,都能聽得懂。

    放到咱們的場景中,對應的就是用文本類的方式進行傳輸。不管哪一個客戶端得到這個文本,都可以知道它的意義。

    一種常見的文本類格式是 XML。咱們這裏舉個例子來看。

<?xml version="1.0" encoding="UTF-8"?>
<cnblog:purchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cnblog="http://www.example.com">
    <order>
        <date>2019-01-08</date>
        <className> 板栗燜雞 </className>
        <price>58</price>
    </order>
</cnblog:purchaseOrder>

    我這裏不許備詳細講述 XML 的語法規則,可是你相信我,看完下面的內容,即使你沒有學過 XML,也能一看就懂,這段 XML 描述的是什麼,不像全面的二進制,你看到的都是 010101,不知所云。

    有了這個,剛纔咱們說的那幾個問題就都不是問題了。

    首先,格式不必徹底一致。好比若是咱們把 price 和 author 換個位置,並不影響客戶端和服務端解析這個文本,也根本不會誤會,說這個做者的名字叫 68。

    若是有的客戶端想增長一個字段,例如添加一個推薦人字段,只須要在上面的文件中加一行:

<recommended> Gary </recommended>

    對於不須要這個字段的客戶端,只要不解析這一行就是了。只要用簡單的處理,就不會出現錯誤。

    另外,這種表述方式顯然是描述一個訂單對象的,是一種面向對象的、更加接近用戶場景的表示方式。

    既然 XML 這麼好,接下來咱們來看看怎麼把它用在 RPC 中。

傳輸協議問題

    咱們先解決第一個,傳輸協議的問題。

    基於 XML 的最著名的通訊協議就是SOAP了,全稱簡單對象訪問協議(Simple Object Access Protocol)。它使用 XML 編寫簡單的請求和回覆消息,並用 HTTP 協議進行傳輸。

    SOAP 將請求和回覆放在一個信封裏面,就像傳遞一個郵件同樣。信封裏面的信分擡頭正文

POST /purchaseOrder HTTP/1.1
Host: www.cnblog.com
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
    <soap:Header>
        <m:Trans xmlns:m="http://www.w3schools.com/transaction/"
          soap:mustUnderstand="1">1234
        </m:Trans>
    </soap:Header>
    <soap:Body xmlns:m="http://www.cnblog.com/perchaseOrder">
        <m:purchaseOrder">
            <order>
                <date>2019-01-08</date>
                <className> 板栗燜雞 </className>
                <price>88</price>
            </order>
        </m:purchaseOrder>
    </soap:Body>
</soap:Envelope>

    HTTP 協議咱們學過,這個請求使用 POST 方法,發送一個格式爲 application/soap + xml 的 XML 正文給 www.geektime.com ,從而下一個單,這個訂單封裝在 SOAP 的信封裏面,而且代表這是一筆交易(transaction),並且訂單的詳情都已經寫明瞭。

協議約定問題

    接下來咱們解決第二個問題,就是雙方的協議約定是什麼樣的?

    由於服務開發出來是給陌生人用的,就像上面下單的那個 XML 文件,對於客戶端來講,它如何知道應該拼裝成上面的格式呢?這就須要對於服務進行描述,由於調用的人不認識你,因此沒辦法找到你,問你的服務應該如何調用。

    固然你能夠寫文檔,而後放在官方網站上,可是你的文檔不必定更新得那麼及時,並且你也寫的文檔也不必定那麼嚴謹,因此經常會有調試不成功的狀況。於是,咱們須要一種相對比較嚴謹的Web 服務描述語言,WSDL(Web Service Description Languages)。它也是一個 XML 文件。

    在這個文件中,要定義一個類型 order,與上面的 XML 對應起來。

<wsdl:types>
  <xsd:schema targetNamespace="http://www.example.org/cnblog">
   <xsd:complexType name="order">
    <xsd:element name="date" type="xsd:string"></xsd:element>
<xsd:element name="className" type="xsd:string"></xsd:element>
<xsd:element name="Author" type="xsd:string"></xsd:element>
    <xsd:element name="price" type="xsd:int"></xsd:element>
   </xsd:complexType>
  </xsd:schema>
 </wsdl:types>

    接下來,須要定義一個 message 的結構。

<wsdl:message name="purchase">
  <wsdl:part name="purchaseOrder" element="tns:order"></wsdl:part>
 </wsdl:message>

    接下來,應該暴露一個端口。

<wsdl:portType name="PurchaseOrderService">
  <wsdl:operation name="purchase">
   <wsdl:input message="tns:purchase"></wsdl:input>
   <wsdl:output message="......"></wsdl:output>
  </wsdl:operation>
 </wsdl:portType>

    而後,咱們來編寫一個 binding,將上面定義的信息綁定到 SOAP 請求的 body 裏面。

<wsdl:binding name="purchaseOrderServiceSOAP" type="tns:PurchaseOrderService">
  <soap:binding style="rpc"
   transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="purchase">
   <wsdl:input>
    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal" />
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>

    最後,咱們須要編寫 service。

<wsdl:service name="PurchaseOrderServiceImplService">
  <wsdl:port binding="tns:purchaseOrderServiceSOAP" name="PurchaseOrderServiceImplPort">
   <soap:address location="http://www.cnblog.com:8080/purchaseOrder" />
  </wsdl:port>
 </wsdl:service>

    WSDL 仍是有些複雜的,不過好在有工具能夠生成。

    對於某個服務,哪怕是一個陌生人,均可以經過在服務地址後面加上「?wsdl」來獲取到這個文件,可是這個文件仍是比較複雜,比較難以看懂。不過好在也有工具能夠根據 WSDL 生成客戶端 Stub,讓客戶端經過 Stub 進行遠程調用,就跟調用本地的方法同樣。

服務發現問題

    最後解決第三個問題,服務發現問題。

    這裏有一個UDDI(Universal Description, Discovery, and Integration),也即統一描述、發現和集成協議。它實際上是一個註冊中心,服務提供方能夠將上面的 WSDL 描述文件,發佈到這個註冊中心,註冊完畢後,服務使用方能夠查找到服務的描述,封裝爲本地的客戶端進行調用。

小結

  • 原來的二進制 RPC 有不少缺點,格式要求嚴格,修改過於複雜,不面向對象,因而產生了基於文本的調用方式——基於 XML 的 SOAP;
  • SOAP 有三大要素:協議約定用 WSDL、傳輸協議用 HTTP、服務發現用 UDDL。
相關文章
相關標籤/搜索