【前五篇】系列文章傳送門:html
上一節咱們瞭解 RPC 的經典模型和設計要點,並用最先期的 ONC RPC 爲例子,詳述了具體的實現。而時代在進步,ONC RPC 逐漸由於各類問題被替代,SOAP 協議就是替代者之一。網絡
ONC RPC 將客戶端要發送的參數,以及服務端要發送的回覆,都壓縮爲一個二進制串,這樣當然可以解決雙方的協議約定問題,可是存在必定的不方便。app
首先,須要雙方的壓縮格式徹底一致,一點都不能差。一旦有少量的差錯,多一位,少一位或者錯一位,均可能形成沒法解壓縮。固然,咱們能夠用傳輸層的可靠性以及加入校驗值等方式,來減小傳輸過程當中的差錯。框架
其次,協議修改不靈活。若是不是傳輸過程當中形成的差錯,而是客戶端由於業務邏輯的改變,添加或者刪除了字段,或者服務端添加或者刪除了字段,而雙方沒有及時通知,或者線上系統沒有及時升級,就會形成解壓縮不成功。函數
於是,當業務發生改變,須要多傳輸一些參數或者少傳輸一些參數的時候,都須要及時通知對方,而且根據約定好的協議文件從新生成雙方的 Stub 程序。天然,這樣靈活性比較差。工具
若是僅僅是溝通的問題也還好解決,其實更難弄的還有版本的問題。好比在服務端提供一個服務,參數的格式是版本一的,已經有 50 個客戶端在線上調用了。如今有一個客戶端有個需求,要加一個字段,怎麼辦呢?這但是一個大工程,全部的客戶端都要適配這個,須要從新寫程序,加上這個字段,可是傳輸值是 0,不須要這個字段的客戶端很「冤」,原本沒我啥事兒,爲啥讓我也忙活?網站
最後,ONC RPC 的設計明顯是面向函數的,而非面向對象。而當前面向對象的業務邏輯設計與實現方式已經成爲主流。spa
這一切的根源就在於壓縮。這就像平時咱們愛用縮略語。若是是籃球愛好者,你直接說 NBA,他立刻就知道什麼意思,可是若是你給一個大媽說 NBA,她可能就不知所云。設計
因此,這種 RPC 框架只能用於客戶端和服務端全由一撥人開發的場景,或者至少客戶端和服務端的開發人員要密切溝通,相互合做,有大量的共同語言,才能按照既定的協議順暢地進行工做。調試
可是,通常狀況下,咱們作一個服務,都是要提供給陌生人用的,你和客戶不會常常溝通,也沒有什麼共同語言。就像你給別人介紹 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 描述文件,發佈到這個註冊中心,註冊完畢後,服務使用方能夠查找到服務的描述,封裝爲本地的客戶端進行調用。