天天看點

接觸 CORBA 内幕: IOR、GIOP 和 IIOP

Technorati 标簽: corba

原文位址:http://www.ibm.com/developerworks/cn/java/underhood/index.html

級别: 初級

Dave Bartlett ([email protected]), 顧問、作家和講師

2000 年 8 月 01 日

7 月,我們建立了一個簡單示例 -- SimpleCalc。這個示例不值得多說;它是單一方法 add() ,接受兩個 IDL long 型變量,傳回一個 long 型變量。講授與學習 CORBA 的一個難題是:已知它是基于分布的客戶機和伺服器,從一開始它就變得很複雜。必須立即與網絡打交道。是以,讓我們現在就來談談網絡。

網絡

如果将話題深入一點,将發現許多都值得探讨,但又很簡單。我們簡單的電腦伺服器設計成被遠端調用,CORBA 專門確定讓我們不必擔心客戶機環境和伺服器環境之間的差異。客戶機對伺服器的遠端調用是根據遠端過程調用 (RPC) 協定生成的,該協定自 20 世紀 80 年代就存在。RPC 是由各種通信模型經過多年測試得到的結果 -- 這是已經在産品環境中測試過的可靠且真實的技術。我們現在使用的 CORBA 模型就基于該模型。

1. 網絡
接觸 CORBA 内幕: IOR、GIOP 和 IIOP
可互操作對象引用 (IOR)

讓我們跟蹤方法調用。客戶機必須首先獲得電腦的執行個體。它通過使用 calculatorHelper.java narrow() 方法來達到這一目的。 ior 是可互操作對象引用 (IOR) 的字元串表示,是從檔案 calcref.ior 中檢索到的。這個檔案是由伺服器寫的,以便客戶機可以定位并連接配接到它。對 orb string_to_object() 的方法調用隻取得 ior 字元串,并将它轉換成對象引用。以下是客戶機中的代碼, SimpleCalcClient.java :

calculator calc = calculatorHelper.narrow(orb.string_to_object(ior));

System.out.println("Calling into the server");

System.out.println( calc.add(2,3) );

IOR 中有什麼?IOR 是一個資料結構,它提供了關于類型、協定支援和可用 ORB 服務的資訊。ORB 建立、使用并維護該 IOR。許多 ORB 供應商提供了一個實用程式來窺視 IOR 的内部。OOC (Object Oriented Concepts, Inc.) 的 Orbacus(請參閱 參考資料) 附帶 IORDump.exe,如果您使用 Visibroker,它為您提供了 PrintIOR.exe。也有一些網站為您分析 IOR;可在 Xerox Parc 站點(請參閱 參考資料)上找到我使用的一個實用程式。因為正在使用 Orbacus,我将對在 SimpleCalc 示例中建立的 IOR 運作 IORDump。得到以下輸出:

C:/_work/corbasem/_sources/calcsimpl>iordump -f calcref.ior

IOR #1:

byteorder: big endian

type_id: IDL:corbasem/gen/calcsimpl/calculator:1.0

IIOP profile #1:

iiop_version: 1.2host: 192.168.0.10

port: 4545

object_key: (36)

171 172 171  49  57  54  49  48 "1/2 1/4 1/2 9610"

 48  53  56  49  54   0  95  82 "05816._R"

111 111 116  80  79  65   0   0 "ootPOA.."

202 254 186 190  57  71 200 248 ".? .9G .."

  0   0   0   0                 "...."

Native char codeset:

  "ISO 8859-1:1987; Latin Alphabet No. 1"

Char conversion codesets:

  "X/Open UTF-8; UCS Transformation Format 8 (UTF-8)"

  "ISO 646:1991 IRV (International Reference Version)"

Native wchar codeset:

 "ISO/IEC 10646-1:1993; UTF-16, UCS Transformation Format 16-bit form"

Wchar conversion codesets:

  "ISO/IEC 10646-1:1993; UCS-2, Level 1"

  "ISO 8859-1:1987; Latin Alphabet No. 1"

  "X/Open UTF-8; UCS Transformation Format 8 (UTF-8)"

  "ISO 646:1991 IRV (International Reference Version)"

IOR 中嵌入的是 type_id、IIOP 版本、主機位址和端口号,以及對象鍵。type_id 字元串是接口類型,衆所周知,它是 資源庫辨別格式。基本上,資源庫辨別是接口唯一的辨別。這個辨別可以是 DCE UUID 格式(COM 程式員比較熟悉它)或者是您指定的本地格式。IIOP 版本将幫助 IOR 閱讀器(通常是 ORB)正确了解 IOR 是哪種格式,因為 OMG 總是改進規範,每個版本的閱讀方法都與以前版本略有不同 ;-)。主機位址和端口号将讓我們接觸到與期望的對象通信的 ORB。對象鍵和許多其它資料都是按特定于服務的資訊的 OMG 标準建構的。這是幫助 OTB 支援伺服器的特定于服務的資料。例如,這些專用 IOR 元件可以編碼 ORB 類型和版本,或者幫助支援 OMG 安全服務的 ORB 實作。以上大多數資訊指定了字元代碼集轉換,這樣客戶機和伺服器就能夠互相了解。

如果通過 Xerox Parc IOR 文法分析器運作 IOR,将得到以下輸出:

IIOP_ParseCDR: byte order BigEndian,

                repository id,

                1 profile

_IIOP_ParseCDR: profile 1 is 124 bytes,

                 tag 0 (INTERNET),

                 BigEndian byte order

(iiop.c:parse_IIOP_Profile): bo=BigEndian,

                              version=1.2,

                              hostname=192.168.165.142,

                              port=4545,

   object_key=<...1961005816._RootPOA......9G......>

(iiop.c:parse_IIOP_Profile): encoded object key is

   <

(iiop.c:parse_IIOP_Profile):?non-native cinfo is object key is

   <#AB#AC#AB196100

   5816#00_RootPOA

   #00#00#CA#FE#BA

   #BE 9G #C8#F8#00#

   00#00#00>;

  no trustworthy most-specific-type info;

  unrecognized ORB;

  reachable with IIOP 1.2 at host "192.168.165.142", port 4545

IOR 中最主要的部分是幫助客戶機連接配接到伺服器的那些部分。可以在 Xerox Parc IOR 閱讀器(請參閱 參考資料)的輸出中看到這些部分。但是,其它許多資訊是 Orbacus 專有的,其它 IOR 閱讀器不能解釋它。這些專用部分是作為附加到 IOR 的資料序列出現的,并且隻有建構 IOR 的 ORB 才懂得這些資料。

存根

現在,我們知道 IOR 帶來了什麼功能。IOR 的目的就是使客戶機能夠連接配接到伺服器,以便它能夠完成方法調用。客戶機必須用 Add 方法将 IOR 轉換成它可以調用的實際對象。這是通過使用從 IDL 編譯器中生成的兩個 Java 檔案來完成的。客戶機将首先使用 calculatorHelper 對象将 IOR 的範圍縮小到 _calculatorStub 代理對象。

以下是 Orbacus 附帶的 jidl 編譯器生成的 narrow() 方法:

public static calculator narrow(org.omg.CORBA.Object _ob_v) {

  if(_ob_v != null) {

   try {

          return (calculator)_ob_v;

      } catch(ClassCastException ex) {

      }

      if(_ob_v._is_a(id())) {

          org.omg.CORBA.portable.ObjectImpl _ob_impl;

          _calculatorStub _ob_stub = new _calculatorStub();

          _ob_impl = (org.omg.CORBA.portable.ObjectImpl)_ob_v;

          _ob_stub._set_delegate(_ob_impl._get_delegate());

          return _ob_stub;

      }

      throw new org.omg.CORBA.BAD_PARAM();

  }

  return null;

}

可以看到,它最主要的任務是建立一個新的 _calculatorStub 對象。 _calculatorStub 充當駐留在伺服器上的實際電腦對象的代理對象。如果您不了解代理模式,我将非常樂意向您介紹“四人組” Design Patterns一書(請參閱 參考資料)。實際上,代理模式無非是建立一個代表或充當另一個實際對象的替身的對象,另一個對象将最終将調用或執行服務。代理模式是一種重要且常用的模式。在所有分布式設計中都會用到它。我敢打賭,您肯定用過這種模式,隻不過從沒有稱您的設計為代理模式。

一旦建立了 _calculatorStub ,它就代表客戶機的電腦接口。add 方法在伺服器中實作,而該伺服器在 IOR 中定義的位址上的電腦空間中運作。至此,這就所調用的 add() 方法。這裡,需要注意兩點:首先,我們必須以 _calculatorStub 的形式調用 add 方法。其次,請注意客戶機将中斷直到調用傳回,就像其它同步方法調用一樣。這是一種請求響應協定,它模仿單程序應用程式。程式設計客戶機,然後使用該請求響應協定執行客戶機就像用庫和 API 調用建立的常用程式設計開發環境一樣普通自然。這并不表示您不能使用異步調用;您當然可以生成那種類型的調用。我将在以後的專欄文章中讨論那些話題。

打包: GIOP CDR

至此,在體系結構中,我們已成功欺騙了客戶機,使它相信服務與它在一起。但事實并為如此,并且在以後幾步中,我們必須将資料和方法調用鑄造成一種形式,它允許在網絡上繼續該調用,并且可以在另一端使用該調用。這并不是無關重要的,且這種模型已經問世好幾年了。您也許已經多次見過 OSI 模型了,在圖 2 中,您将看到 OSI 模型,旁邊就是 OMG 所使用的模型。

2. OSI 的結構 vs. GIOP 協定堆棧
接觸 CORBA 内幕: IOR、GIOP 和 IIOP

客戶機調用接口操作時,它必須将操作資料(in 和 inout 參數)發送到伺服器。此時的困難在于将資料轉換成公共格式,這樣伺服器抽取操作資料時不會誤解或錯誤對齊資料。因為伺服器可以是任意數量不同的平台,我們應該預計到客戶機和伺服器之間的體系結構差異。CORBA 通過嚴格定義如何将資料轉換或打包成公共格式來處理這種問題。然後在連接配接的另一端重新組成或解包資料。這是通過用最基本的結構表示資料來完成的,最基本的結構就是位元組流,也就是八位元流。

CORBA 規範将八位元流定義成“一種抽象表示法,通常對應于要通過 IPC 機制或網絡傳輸來發送到另一個程序或另一台機器的記憶體緩沖區”。IDL 八位元準确映射成 Java 位元組。它們都是 8 位值,客戶機或伺服器都不打包這種值。将這些參數轉換成八位元序列的根本目的是産生用于資訊交換的基本結構。

現在,我們應當窺視 _calculatorStub 生成的代碼的内部資訊。請記住這不是由我編寫的 -- 它是由 Orbacus 附帶的 IDL-到-Java 編譯器 jidl 生成的。

//

// IDL:corbasem/gen/calcsimpl/calculator/add:1.0

//

public int add(int _ob_a0, int _ob_a1) {

  System.out.println("Inside _calculatorStub.add()");

  while(true) {

    if(!this._is_local()) {

      org.omg.CORBA.portable.OutputStream out = null;

      org.omg.CORBA.portable.InputStream in = null;

   try {

          out = _request("add", true);

          out.write_long(_ob_a0);

          out.write_long(_ob_a1);

          in = _invoke(out);

          int _ob_r = in.read_long();

          return _ob_r;

      } catch(org.omg.CORBA.portable.RemarshalException _ob_ex) {

                     continue;

      } catch(org.omg.CORBA.portable.ApplicationException _ob_aex) {

          final String _ob_id = _ob_aex.getId();

          in = _ob_aex.getInputStream();

          throw new org.omg.CORBA.UNKNOWN("Unexpected User Exception: " + _ob_id);

      } finally {

          _releaseReply(in);

      }

  } else {

      org.omg.CORBA.portable.ServantObject _ob_so = _servant_preinvoke

("add", _ob_opsClass);

      if(_ob_so == null)

                     continue;

      calculatorOperations _ob_self = (calculatorOperations)_ob_so.servant;

   try {

          return _ob_self.add(_ob_a0, _ob_a1);

      } finally {

          _servant_postinvoke(_ob_so);

      }

    }

  }

}

<>

要注意的部分是包含 _request() 、 write_long() 調用,和 _invoke() 及随後的 read_long() 。對 _request() 的調用使用要調用的方法名稱,和顯示是否需要響應的布爾 (boolean) 值。它傳回 CORBA 規範指定的 org.omg.CORBA.portable.OutputStream 對象。對于可移植性,這是必要的,因為 Java 經常被下載下傳,并且依賴于它運作的機器上的公共庫。對于 ORB 是這樣,對于 IO 也是這樣。是以,CORBA 規範為 Java 語言定義了比其它語言更廣泛的可移植類型集合。

通用 ORB 間協定 (GIOP)

通用 ORB 間協定 (GIOP) 用來為這個由不同計算機及其各種體系結構組成的淩亂世界中傳送消息定義結構和格式。如果使用 GIOP 的結構和格式,并将它們應用于 TCP/IP,那麼就得到 IIOP。GIOP 有兩個版本:1.0 和 1.1。這就意味着我們的消息根據其符合的 GIOP 版本可能有不同的格式。

至此,我們必須看一下 GIOP 以了解請求在變成正确格式化的 CORBA 請求時所要經曆的操作。盡管我們将仔細研究請求,響應隻是請求的鏡像圖像。如果您知道請求的工作原理,那麼您就能了解響應。

GIOP 請求消息分成三部分:GIOP 消息頭、GIOP 請求頭和請求主體。GIOP 消息頭表示這就是一條 GIOP 消息。它包含 GIOP 版本、消息類型、消息大小,然後根據您是使用 1.0、1.1 還是 1.2,包含位元組次序 (GIOP 1.0) 或一個位标志字段,該字段包括位元組次序以及一些保留位标志。GIOP 1.1 添加了對消息存儲碎片的支援,GIOP 1.2 添加了雙向通信支援。更新的版本都是向下相容的。

公共資料表示 (CDR)

公共資料表示 (CDR) 是 CORBA 調用中将使用的資料類型的正式映射。客戶機生成請求時,它不必知道請求要發送到什麼地方,或者哪一台伺服器将響應該請求。CORBA(作為規範)和 GIOP(作為規範的一部分,定義消息結構和傳送)被設計成允許實作一個接口的可能的多種不同伺服器之一來響應請求。規範必須定義如何打包操作中的資料,這樣所有可能的伺服器都可以抽取參數并調用遠端操作,并且資料轉換不會産生多義性。這種轉換問題的典型示例就是指針。客戶機中的指針對于在另一台機器上運作另一個程序的伺服器意味着什麼?毫無意義。或者,變量如何在使用不同尋址方案(大尾數法,小尾數法)的機器間發送?這些資料類型必須轉換成伺服器能夠了解并使用的流。顯然,CORBA 規範在公共資料表示方面是十分詳細的。這是我們不必涉足的細節層次,但如果您想要了解詳細資訊,請閱讀規範或 Ruh、Herron 和 Klinkeron 合著的 IIOP Complete一書(請參閱 參考資料)。

一旦包裝了所有資料,就将使用 IOR 中的資訊來建立連接配接。您可以差別 IOR 的結構,通常必須使用 TCP 作為傳送機制。但是,也可以使用其它傳送(再次提醒,請參閱 CORBA 規範以擷取詳細資訊)。ORB 守護程式負責查找 IOR 指定的對象實作,以及建立客戶機和伺服器之間的連接配接。一旦建立了連接配接,GIOP 将定義一組由客戶機用于請求或伺服器用于響應的消息。客戶機将發送消息類型 Request、LocateRequest、CancelRequest、Fragment 和 MessageError。伺服器可以發送消息類型 Reply、LocateReply、CloseConnection、Fragment 和 MessageError。

如果我們扯開 GIOP 消息,它看上去就像:

0x47 0x49 0x 4f 0x50 -> GIOP, the key

0x01 0x00           -> GIOP_version

0x00                -> Byte order (big endian)

0x00                -> Message type (Request message)

0x00 0x00 0x00 0x 2c -> Message size (44)

0x00 0x00 0x00 0x00 -> Service context

0x00 0x00 0x00 0x01 -> Request ID

0x01                -> Response expected

0x00 0x00 0x00 0x24 -> Object key length in octets (36)

0xab 0xac 0xab 0x31 0x39 0x36 0x31 0x30

0x30 0x35 0x38 0x31 0x36 0x00 0x 5f 0x52

0x 6f 0x 6f 0x74 0x50 0x 4f 0x41 0x00 0x00

0xca 0xfe 0xba 0xbe 0x39 0x47 0xc8 0xf8

0x00 0x00 0x00 0x00 -> Object key defined by vendor

0x00 0x00 0x00 0x04 -> Operation name length (4 octets long)

0x61 0x64 0x64 0x00 -> Value of operation name ("add")

0x20                -> Padding bytes to align next value

您應該了解大概情況了。這種消息流是高度結構化的。它也必須是,為了客戶機可以建立伺服器可以轉換成實作的消息 -- 不管實作如何運作,或在哪裡運作。伺服器也必須為在響應客戶機時使用的傳回值和參數執行相同操作。此消息格式在 OMG 成就中非常重要,因為它可以實作可移植性和互操作性目标。這種可移植性将給予您我們在第一篇專欄文章中談到的自由。您無需關心硬體、資料庫或程式設計語言。隻要關心您的資訊就行了。

IIOP

我們還沒有徹底結束。GIOP 是 CORBA 方法調用的核心部分。GIOP 不基于任何特别的網絡協定,如 IPX 或 TCP/IP。為了確定互操作性,OMG 必須将 GIOP 定義在所有供應商都支援的特定傳輸之上。如果有詳細和簡潔的消息規範,則不會提供互操作性,因為所有供應商使用不同的傳送機制來實作這個互操作性。是以,OMG 在最廣泛使用的通信傳輸平台 -- TCP/IP 上标準化 GIOP。GIOP 加 TCP/IP 等于 IIOP!就這麼簡單。

需要使用已釋出對象服務的客戶機将使用 IOR 中的值來啟動與對象的連接配接。我們已經繞了一個圈子,又回到了 IOR。IOR 對于 IIOP 是至關重要的,任何要對某個對象調用方法的客戶機都要将“請求”消息發送到 IOR 中詳細說明的主機和端口位址。在主機上,伺服器程序在請求進入時會偵聽端口,并将那些消息發送到對象。這就要求伺服器主動偵聽請求。

生活有陰陽兩面,每件事都有缺點,互操作性和 IIOP 也不例外。OMG 推出和運作了 IIOP,對比 ORB 供應商它們自己的伺服器上實作此功能,并且沒有伺服器方可移植性的年代,這是一大改進。但如果要求伺服器是位置無關的,我們應該做什麼?如果主機和端口值嵌入 IOR 中,每當您将對象從一個伺服器移到另一個伺服器,以均衡負載時,這個問題就會突然出現。可喜的是這個問題已經解決了;但又有一條壞消息,每家供應商的解決方法都不同。

結束語

現在,将負載均衡話題留到将來讨論。如果您在幾年前有 CORBA“經驗”(也就是說在一段時期内),而現在從事另一項研究,我相信您将感到驚喜。CORBA 規範已經獲得很大進步,可以確定您為一個 ORB 編寫的伺服器代碼可以移植到運作另一個 ORB 的另一台伺服器上。解決方案非常簡單,并且以經典的協定為基礎。客戶機和伺服器間的标準交換文法基于某些 OMG 詳細說明的需求。OMG 通過使用網絡尋址協定 (IIOP) 獨立于消息傳遞協定 (GIOP),為其規範建立了更多功能。這也確定随着資訊工業的變動(的确發展很快),CORBA 仍能跟上它的步伐。我最喜歡的是,對于我們剛讨論的關于如何使之成功完成的 CORBA 對象調用,我不必編寫代碼!在 ORB 中和用 IDL 标準化接口的功能中已經概括了網絡測量和打包的詳細資訊。下個月,我們将讨論 IDL。

參考資料
  • 您可以參閱本文在 developerWorks 全球站點上的 英文原文.
  • 從 OMG 網站了解更多關于 CORBA的資訊。
  • 請閱讀 Michi Henning 和 Steve Vinoski 的優秀著作 Advanced CORBA Programming with C++ 。
  • 在 William Ruh、Thomas Herron 和 Paul Klinker 合著的 IIOP Complete: Understanding CORBA and Middleware Interoperability 中查找關于 GIOP、CDR 和 IIOP 的參考資料。
  • 從 Object Oriented Concepts, Inc.下載下傳 Orbacus。
  • 在 Xerox PARC站點上找到 IOR 文法分析器。
  • 通路“四人組”(Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides) Design Patterns: Elements of Reusable Object-Oriented Software,擷取代理模式的詳細資訊。
  • 從 Bruce Eckel 的 Mindview.net和 Thinking in Java 第二版中學習 Java 程式設計知識
關于作者
接觸 CORBA 内幕: IOR、GIOP 和 IIOP
接觸 CORBA 内幕: IOR、GIOP 和 IIOP
接觸 CORBA 内幕: IOR、GIOP 和 IIOP
Dave Bartlett 居住在美國賓夕法尼亞州的 Berwyn,他是顧問、作家和講師。他是 Hands-On CORBA with Java的作者,這是适用于公共課程或企業内部教育訓練的 5 日教程。目前,Dave 正将課程資料編成書 Thinking in CORBA with Java。Dave 擁有賓州大學的工程和商業碩士學位。如果您對某個主題還有疑問或感興趣,可通過 [email protected]與 Dave 聯系。