反叛之冰:ZeroC ICE基礎使用

yanhang0610發表於2017-09-10

1        系統環境

系統:Win7。

版本:JDK 1.8.0_91,ZeroC ICE 3.6.3。

2        基礎

官網:https://zeroc.com

視訊教程:https://www.chuanke.com/v6242976-184547-1010808.html

書籍:ZeroC Ice權威指南 高清 帶索引書籤目錄 leader-us(著)

2.1 ICE基礎

   

2.2 Slice

2.2.1   基本資料型別

型別

定義及範圍

長度

bool

true of false

≥ 1bit

byte

[-128, 127] or [0, 255]

≥ 8bit

short

[-2^15, 2^15-1]

≥ 16bit

int

[-2^31, 2^31-1]

≥ 32bit

long

[-2^63, 2^63-1]

≥ 64bit

float

 

≥ 32bit

double

 

≥ 64bit

string

 

variable-length

2.2.2   常量定義

用const修飾,如:

const bool trueOrFalse = true;
const byte b = 0x0f;
const string msg = “hello”;
const short s = 56;
const double PI = 3.1416;

enum Fruit {Apple, Orange};  (enum型別其實等價於int)
const Fruit favoriteFruit = Orange;

2.2.3   複合資料結構定義

型別

含義說明

enum

列舉,如:

enum Fruit {Apple, Orange}

或:

enum Fruit {Apple = 5, Orange = 1}

實際上enum型別等價於int

struct

結構體,保護多個屬性資料,類似與JavaBean。

struct Student {

   int id;

   string name;

}

sequence

複合型別,支援 基本型別的集合 或者 複合型別的集合,如:

sequence<Fruit> FruitPlatter;

sequence<FruitPlatter> FruitBanquet; (集合的集合)

dictionary

Map型別,類似於Java HashMap,如:

dictionary<long, Student> StudentMap

例:

struct TimeOfDay {
  shor hour; // 0 - 23 
  shor minute;  // 0 - 59
  shor second;  // 0 - 59
};

2.2.4   異常定義

exception Error {};  // 可定義空異常
exception RangeError {
	TimeOfDay errorTime;
	TimeOfDay minTime;
	TimeOfDay maxTime;
};

2.2.5   slice檔案複用

使用#include關鍵字可引用其他slice檔案:

#include common.slice

2.2.6   介面和方法定義

使用interface來申明介面(語法上跟java定義介面語法類似,只是沒有public關鍵字),如:

interface Clock {
  TimeOfDay getTime();
  void setTime(TimeOfDay time);
}

Idempotent關鍵詞:用該關鍵詞修飾方法,指明該方法是冪等的,即呼叫1次和呼叫2次其結果是一樣的。新增Idempotent修飾的方法,可以讓ICE更好地實現“自動恢復錯誤”機制,即在某個Ice Object呼叫失敗的情況下,ICE會再次呼叫有Idempotent修飾的方法,透明恢復故障,而在客戶端看來則呼叫正常,沒有感覺到ICE做了自動故障恢復操作。

3        例項

參考:http://blog.csdn.net/xuzheng_java/article/details/24459181

所需jar包:

ICE安裝目錄下lib目錄裡的ice-3.6.3.jar

3.1 Slice指令碼

HelloWorldIDL.ice

[["java:package:myice.demo"]]

HelloWorldIDL.ice
[["java:package:myice.demo"]]
module test {
	interface HelloWorldIDL {
		string sayHello(string username);
	};
};

說明:

Ø        slice檔案必須以ice為字尾。

Ø        [["java:package:myice.demo"]]定義java父包路徑,module表示模組名,真正生成的包路徑為myice.demo.test。

Ø        module定義不能缺少。

Ø        在定義語句的結尾(如右花括號)需要以分號結尾。

3.2 生成通用服務類

slice2java--output-dir D:\iceoutput D:\slice\HelloWorldIDL.ice

3.3 Server端編寫服務實現類

Ø        HelloWorldHandler.java

package server;

import Ice.Current;
import myice.demo.test._HelloWorldIDLDisp;

/**
 * 介面處理類,繼承生成的_HelloWorldIDLDisp類
 */
public class HelloWorldHandler extends _HelloWorldIDLDisp {

	private static final long serialVersionUID = 1L;

	/*
	 * 在__current.ctx裡可獲取到客戶端額外上送的引數
	 */
	@Override
	public String sayHello(String username, Current __current) {
		return "Hello ZeroC ICE, [" + username + "]";
	}

}

Ø        HelloIceHandler.java

package server;

import Ice.Current;
import myice.demo.test._HelloWorldIDLDisp;

/**
 * 介面處理類,繼承生成的_HelloWorldIDLDisp類
 */
public class HelloIceHandler extends _HelloWorldIDLDisp {

	private static final long serialVersionUID = 1L;

	/*
	 * 在__current.ctx裡可獲取到客戶端額外上送的引數
	 */
	@Override
	public String sayHello(String username, Current __current) {
		return "Hello ICE ASDFSDF, [" + username + "]";
	}

}

3.4 Server端註冊服務並監聽請求

package server;

public class HelloWorldServer {

	public static void main(String[] args) {
		Ice.Communicator communicator = null;
		
		try {
			// 初始化ICE Communicator物件,args可以傳一些初始化引數,如連線超時、初始化客戶端連線池數量等
			communicator = Ice.Util.initialize(args);
			
			// 建立ObjectAdapter(名稱為helloWorldAdapter),使用預設的通訊協議(TCP/IP),埠為7890,用於監聽請求
			Ice.ObjectAdapter adapter = communicator.createObjectAdapterWithEndpoints("helloWorldAdapter", "default -p 7890");
			
			// 建立服務端介面處理handler例項(ice裡稱為servant)
			HelloWorldHandler helloWorldHandler = new HelloWorldHandler();
			
			/*
			 * 將helloWorldHandler新增到ObjectAdapter中,並將helloWorldHandler關聯到ID為"helloWorldHandler"的Ice Object
			 * (此處ID相當於介面名稱,全域性唯一,client端通過該名稱連線上來)
			 */
			adapter.add(helloWorldHandler, Ice.Util.stringToIdentity("helloWorldHandler"));
			adapter.activate();  // 啟用ObjectAdapter
			
			// 在服務退出前,一直監聽請求
			communicator.waitForShutdown();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (communicator != null) {
				communicator.destroy();
			}
		}
	}
	
}

3.5 Client傳送請求並得到響應

package client;

import myice.demo.test.HelloWorldIDLPrx;
import myice.demo.test.HelloWorldIDLPrxHelper;

public class HelloWorldClient {

	public static void main(String[] args) {
		Ice.Communicator communicator = null;
		
		try {
			// 初始化ICE Communicator物件,args可以傳一些初始化引數,如連線超時、初始化客戶端連線池數量等
			communicator = Ice.Util.initialize(args);
			
			// 傳入遠端服務介面的名稱、網路協議、IP和埠,建立一個Proxy物件
			Ice.ObjectPrx base = communicator.stringToProxy("helloWorldHandler:default -p 7890");
			
			/*
			 * 通過checkedCast向下轉型,獲取HelloWorld介面的代理類(客戶端)
			 * 其中:HelloWorldIDLPrx和HelloWorldIDLPrxHelper為生成的類
			 */
			HelloWorldIDLPrx helloWorldClient = HelloWorldIDLPrxHelper.checkedCast(base);
			if (helloWorldClient != null) {
				String result = helloWorldClient.sayHello("ZEROC ICE");
				
				// 可額外傳參到服務端,服務端在Current的ctx屬性裡可獲取到傳參值。如:
//				Map<String, String> params = new HashMap<String, String>();
//				params.put("param1", "aaaaaaa");
//				params.put("param2", "bbbbbbb");
//				String result = helloWorldClient.sayHello("ZEROC ICE", params);
				
				System.out.println(result);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (communicator != null) {
				communicator.destroy();
			}
		}
	}
	
}

3.6 執行

執行HelloWorldServer,啟動服務。

執行HelloWorldClient,呼叫遠端服務介面,控制檯列印如下:

Hello ZeroC ICE, [ZEROC ICE]

成功呼叫遠端服務。

4        IceBox

IceBox類似於Tomcat,裡面可以載入多個伺服器例項(應用)。本章節示範一個IceBox容器的開發過程。

所需jar包:icebox-3.6.3.jar

4.1 伺服器例項編寫

一個IceBox伺服器例項繼承自IceBox.Service介面:

package icebox;

import Ice.Communicator;
import IceBox.Service;
import server.HelloIceHandler;
import server.HelloWorldHandler;

/**
 * IceBox伺服器例項,實現IceBox.Service介面
 */
public class HelloWorldServer2 implements Service {

	private Ice.ObjectAdapter adapter = null;

	@Override
	public void start(String name, Communicator communicator, String[] arg2) {
		System.out.println("ice box helloworld starting...");
		
		adapter = communicator.createObjectAdapter(name);
		
		// 建立服務端介面處理handler例項(ice裡稱為servant)
		HelloWorldHandler helloWorldHandler = new HelloWorldHandler();
		HelloIceHandler helloIceHandler = new HelloIceHandler();
		
		// 將Handler新增到ObjectAdapter中
		adapter.add(helloWorldHandler, Ice.Util.stringToIdentity("helloWorldHandler2"));
		adapter.add(helloIceHandler, Ice.Util.stringToIdentity("helloIceHandler2"));
		adapter.activate();  // 啟用ObjectAdapter
		
		System.out.println("ice box helloworld started.");
	}

	@Override
	public void stop() {
		adapter.destroy();
		System.out.println("ice box helloworld stoped.");
	}

}

4.2 配置檔案編寫

編寫icebox.properties配置檔案,建議放在classpath裡,示例配置如下:

#icebox instance properties begin
IceBox.InstanceName=MyIceBox 1
IceBox.InheritProperties=1
IceBox.PrintServicesReady=MyAppIceBox 1
#icebox instance properties end

#log define begin
Ice.Trace.Network=1
Ice.Trace.ThreadPool=1
Ice.Trace.Locator=1
#log define end

#server define begin
IceBox.Service.HelloWorldServer2=icebox.HelloWorldServer2 prop1=1 prop2=2 prop3=3
IceBox.UseSharedCommunicator.HelloWorldServer2=1
HelloWorldServer2.Endpoints=tcp -h localhost -p 7890
#server define end

4.3 執行

4.3.1   啟動IceBox容器

Ø        在eclipse裡除錯

執行IceBox容器,可通過java命令執行,也可在eclipse裡除錯:

在專案上右擊->Debug As->Debug configurations,新建一個debugconfiguration,配置資訊如下:

Main->Name:根據需要填寫,如IceBoxTest

Main->MainClass:IceBox.Server

Arguments->program arguments:--Ice.Config=icebox.properties

點選debug按鈕,即可啟動MainClass進行除錯。

Ø        通過程式碼啟動

package icebox;

public class StartIceBox {

	public static void main(String[] args) {
		IceBox.Server icebox = new IceBox.Server();
		icebox.main(new String[] {"--Ice.Config=icebox.properties"});
	}
	
}

4.3.2   執行客戶端

執行章節3.5即可。

5        IceGrid

IceGrid為ICE提供的分散式系統架構,由一個Ice Registry + N個Ice Grid Node組成。

5.1 Ice Registry

Ice Registry(登錄檔)元件用於儲存服務的Endpoint資訊,支援主從同步,從節點可以分擔查詢請求,類似MySQL的讀寫分離,並防止單點故障。Registry以二進位制檔案形式儲存執行期Ice服務註冊資訊。

IceBox可以註冊到Registry上,客戶端通過向Registry傳送請求,得到具體的服務的Endpoint資訊,然後建立起服務之間的直連TCP通道,此後的資料互動就在這個直連通道上完成。

本節示例配置一個Registry元件,並註冊一個IceBox服務到Registry上,編寫客戶端通過該Registry來訪問服務,此處Registry僅提供查詢服務的功能。

5.1.1   啟動Registry

Ø        配置檔案

編寫配置檔案:icegridregistry.cfg

IceGrid.InstanceName=MyIceGrid  #預設名稱是IceGrid
IceGrid.Registry.Client.Endpoints=tcp -h 192.168.1.7 -p 4061
IceGrid.Registry.Server.Endpoints=tcp -h 192.168.1.7
IceGrid.Registry.Internal.Endpoints=tcp -h 192.168.1.7
IceGrid.Registry.Data=D:\Program Files (x86)\ZeroC\data\registry  #路徑需手動建立好
IceGrid.Registry.PermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.AdminPermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.DynamicRegistration=1

Ø        啟動

進入ice安裝目錄下的bin子目錄,啟動registry,如:

D:\Program Files (x86)\ZeroC\Ice-3.6.3\bin>

icegridregistry --Ice.Config=D:\ZerocICE\publish\rsrc\icegridregistry.cfg

啟動後,可在IceGrid.Registry.Data目錄下看到生成的一堆二進位制檔案。

5.1.2   註冊IceBox到Registry

註冊IceBox到Registry,只需在原有的IceBox配置檔案(4.1)上新增如下配置,啟動IceBox即可:

#iceboxregistry define begin

Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061

HelloWorldServer2.AdapterId=HelloWorldServer2

#iceboxregistry define end

其中:

MyIceGrid為registry裡定義的IceGrid.InstanceName屬性值,其後的ip port為 registry的ipport。

HelloWorldServer2.AdapterId=HelloWorldServer2格式為:{serviceName}.AdapterId={adapterName},本例serviceName和adapterName均為IceBox配置裡IceBox.Service.{serviceName}上的serviceName值。

5.1.3   Client傳送請求並獲得響應

修改3.5裡的獲取代理類的程式碼即可。完整程式碼如下:

package client;

import myice.demo.test.HelloWorldIDLPrx;
import myice.demo.test.HelloWorldIDLPrxHelper;

public class HelloWorldClient4IceGrid {

	public static void main(String[] args) {
		Ice.Communicator communicator = null;
		
		try {
			communicator = Ice.Util.initialize(new String[] {"--Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061"});
			Ice.ObjectPrx base = communicator.stringToProxy("helloWorldHandler2@HelloWorldServer2");
			
			HelloWorldIDLPrx helloWorldClient = HelloWorldIDLPrxHelper.checkedCast(base);
			if (helloWorldClient != null) {
				String result = helloWorldClient.sayHello("fffkkk");
				System.out.println(result);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (communicator != null) {
				communicator.destroy();
			}
		}
	}
	
}

5.1.4   執行

啟動IceBox服務,註冊HelloWorldServer2到Registry。

執行HelloWorldClient4IceGrid,呼叫遠端服務介面,控制檯列印如下:

Hello ZeroC ICE, [fffkkk]

成功呼叫遠端服務。

5.2 IceGrid Node

IceGrid Node為IceGrid叢集裡的節點,負責IceBox的裝載和啟停、採集主機的負載資訊和執行控制器IceGrid Admin的指令。

IceGrid Node是IceBox的容器,IceBox是服務(如HelloWorldServer2)的容器。一個IceGrid Node可以部署多個IceBox例項,這些例項可以是同一個IceBox的多個例項也可以是多個不同IceBox的例項組合。IceGrid Node連同其上執行的IceBox例項的資訊同步到了Registry中。

每個IceGrid Node都有唯一的名字,用來在IceGrid中標識其身份,可以在用一個伺服器上部署多個IceGrid Node,只要確保其都有唯一的名字即可。每個IceGrid Node都有一個配置檔案,定義了其啟動引數。

5.2.1   啟動IceGrid Node

Ø        配置檔案

編寫配置檔案:icegridnode.cfg

Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061 # 指定Ice Registry資訊
IceGrid.Node.Name=node1
IceGrid.Node.Endpoints=tcp -h 192.168.1.7
IceGrid.Node.Data=D:\Program Files (x86)\ZeroC\data\node1 # 所有目錄需預先建立好
IceGrid.Node.Output=D:\Program Files (x86)\ZeroC\data\node1
Ice.StdErr=D:\Program Files (x86)\ZeroC\data\node1\stderr.log
Ice.StdOut=D:\Program Files (x86)\ZeroC\data\node1\stdout.log

Ø        啟動

進入ice安裝目錄下的bin子目錄,啟動registry,如:

D:\Program Files (x86)\ZeroC\Ice-3.6.3\bin>

icegridnode --Ice.Config=D:\ZerocICE\publish\rsrc\icegridnode.cfg

啟動後,可在IceGrid.Node.Data目錄下看到生成的一堆檔案。

5.3 IceGrid叢集

Ø        系統環境

CentOS 7,jdk1.8.0_91,zerocice 3.6.3

Ø        系統拓撲

伺服器

節點型別

服務

192.168.1.210

Ice Registry

1個Ice Registry、1個IceBox

192.168.1.212

IceGrid Node

1個IceBox

192.168.1.213

IceGrid Node

1個IceBox

 

Ø        伺服器安裝ice

CentOS下可安裝Red Hat版本ICE:

# cd /etc/yum.repos.d

# wget https://zeroc.com/download/rpm/zeroc-ice-el7.repo

# yum install ice-all-runtime ice-all-devel

5.3.1   icegridadmin使用

icegridadmin為IceGrid叢集管理工具,用於部署或升級IceGrid、檢視IceGrid節點狀態、啟停Node中的Server例項等。執行icegridadmin,需要先啟動Registry和Node。

Ø       啟動icegridadmin

進入ice安裝目錄下的bin子目錄,執行如下命令:

D:\Program Files (x86)\ZeroC\Ice-3.6.3\bin>

icegridadmin -u test -p test--Ice.Default.Locator="MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061"

即可進入icegridadmin操作符模式。由於Registry在本地且沒開啟許可權驗證,所以可用test/test使用者名稱及密碼來訪問 Registry登錄檔。

5.3.2   叢集搭建(暫單伺服器)

首先將程式包分發到各個伺服器上,根據配置檔案建立必要的目錄。

Ø       IceBox程式碼

參照第3章和第4.1章節,完成IceBox伺服器程式碼的編寫。

此時不需要IceBox的配置檔案,將由IceGrid自動生成。

Ø       啟動Ice Registry

參考5.1.1章節,在192.168.2.10上啟動icegridregistry服務:

# icegridregistry --daemon--Ice.Config=/root/iceserver/rsrc/icegridregistry.cfg

啟動後可使用一下命令檢視啟動後執行在後臺的程式:

# ps aux | grep ice

配置檔案示例:

[root@nn rsrc]# more icegridregistry.cfg 
IceGrid.InstanceName=MyIceGrid
IceGrid.Registry.Client.Endpoints=tcp -h 192.168.1.210 -p 4061
IceGrid.Registry.Server.Endpoints=tcp -h 192.168.1.210
IceGrid.Registry.Internal.Endpoints=tcp -h 192.168.1.210
IceGrid.Registry.Data=/root/iceserver/data/registry
IceGrid.Registry.PermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.AdminPermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.DynamicRegistration=1

Ø       啟動IceGrid Node

參考5.2.1章節,啟動icegridnode服務:

# icegridnode --daemon--Ice.Config=/root/iceserver/rsrc/icegridnode.cfg

各個服務的配置可根據需要做微調,如節點目錄。

配置檔案示例:

[root@nn rsrc]# more icegridnode.cfg 
# 指定Ice Registry資訊
Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.210 -p 4061
IceGrid.Node.Name=node1
IceGrid.Node.Endpoints=tcp -h 192.168.1.210
IceGrid.Node.Data=/root/iceserver/data/node1
IceGrid.Node.Output=/root/iceserver/data/node1
Ice.StdErr=/root/iceserver/data/node1/stderr.log
Ice.StdOut=/root/iceserver/data/node1/stdout.log

Ø       啟動IceGrid叢集

編寫叢集配置檔案:

myicegrid.xml

<icegrid>
	<application name="MyApplication">
	
		<!-- IceBox等配置檔案裡的配置項 -->
		<properties id="cfg_icebox">
			<property name="IceBox.InheritProperties" value="1" />
			<property name="Ice.PrintStackTraces" value="1"/>
			<property name="Ice.Trace.Network" value="1" />
			<property name="Ice.Trace.ThreadPool" value="1" />
			<property name="Ice.Trace.Locator" value="1" />
		</properties>
		
		<!-- 負載均衡和服務容錯 -->
		<replica-group id="rep_HelloWorldServer2Rep">
			<!-- type: 負載均衡策略, n-replicas: 參與負載均衡的節點數,0應該表示自動獲取所有節點數 -->
			<load-balancing type="round-robin" n-replicas="0"/>
			<!-- 定義服務, identity: 新增到adapter裡時指定的ice object id, type: slice檔案定義的路徑(module名稱和interface名稱),或在ice生成的介面的抽象類中找到id -->
			<object identity="helloWorldHandler2" type="::test::HelloWorldIDL" />
			<object identity="helloIceHandler2" type="::test::HelloWorldIDL" />
		</replica-group>
		
		<server-template id="tpl_MyServer">
			<!-- 定義模版裡的引數名稱 -->
			<parameter name="id" />
			
			<!-- 定義icebox容器, icebox節點可定義多個, id需要唯一; exe: 服務啟動命令, activation: 啟動方式 -->
			<icebox id="MyServer-${id}" exe="java" activation="on-demand">
				<!-- IceBox的配置檔案 -->
				<properties>
					<!-- 引用配置檔案 -->
					<properties refid="cfg_icebox" />
				</properties>
				
				<!-- option: exe執行命令的引數 -->
				<option>IceBox.Server</option>
				<env>CLASSPATH=/usr/java/jdk1.8.0_91/lib/tools.jar;/root/iceserver/lib/*</env>
				
				<!-- 服務定義,可定義多個service節點 -->
				<service name="HelloWorldServer2" entry="icebox.HelloWorldServer2">
					<adapter name="HelloWorldServer2" id="HelloWorldServer2-${id}" endpoints="tcp" replica-group="rep_HelloWorldServer2Rep" />
				</service>
			</icebox>
			
			<!-- 直接定義服務 -->
			<!-- <server id="MyServer-${id}" exe="" activation="on-demand">
				<adapter name="Hello" endpoints="tcp" replica-group="ReplicatedHelloAdapter" />
			</server> -->
		</server-template>

		<node name="node1">
			<server-instance template="tpl_MyServer" id="1" />
		</node>
		<!-- <node name="node2">
			<server-instance template="tpl_MyServer" id="2" />
		</node> -->
		
	</application>
</icegrid>

           進入icegridadmin命令列:

# icegridadmin -u test -p test--Ice.Default.Locator="MyIceGrid/Locator:tcp -h 192.168.1.210 -p4061"

    載入應用:

    >>> application add/root/iceserver/rsrc/myicegrid.xml

    更新/重新載入:

    >>> application update/root/iceserver/rsrc/myicegrid.xml

    啟動服務(啟動服務出錯,待解決)

    >>> server start MyServer-1

    常用命令:

    server list 檢視啟動的伺服器

    service list MyServer-1  檢視MyServer-1上的服務

    service describe MyServer-1HelloWorldServer2  檢視MyServer-1下HelloWorldServer2服務的描述

    adapter list 檢視所有Adapter

    node list 檢視所有node

6        常見問題

 

相關文章