### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 題目描述 - 難度:中等 - 給定一個不含重覆數字的數 ...
一、前言
Zeroc ICE在簡中互聯網的資料十分匱乏,以至於大家線上使用時可能會有所顧慮。其實大家盡可放心,ZerocICE是一款性能和穩定性都非常優秀的RPC組件,這也是我當時選擇ZerocICE作為XL-LightHouse的RPC組件的唯一原因。為便於大家快速瞭解ZerocICE,本文以v3.7版本為例介紹其部署和使用方式。
二、特性
- 跨語言通信
- 高性能RPC
- 安全通信
- 實時壓縮
- 註冊中心支持主從備份
- 節點負載均衡,可動態調整
- 支持同步調用和非同步調用
三、名詞解釋
1、Slice語義
ZerocICE目前支持的開發語言有:C++、Java、C#、JavaScript、Python、Ruby、Swift、Objective-C、PHP。通過Slice語義,不同語言開發的客戶端和服務端可以完全互通。Slice語義可以理解成一種描述介面構成(介面名稱、介面參數類型、返回值類型)的標準。ZerocICE提供了Slice轉化工具,可以在各種系統平臺將Slice語義轉化成對應的介面的代碼。比如相同的Slice文件,在X86平臺可以通過Slice2Java轉化成相應的Java介面代碼,在ARM平臺通過Slice2C++可以轉化成對應的C++介面代碼。
2、Ice.Object
Ice.Object是RPC介面的抽象,ICE中一個Object對象可以包含多個介面,每一個介面對應一個響應客戶端請求的Object實體。ICE使用(Object Identity)來區分不同的Object對象,identity在集群中必須全局唯一。
3、Proxy
Ice.Object是相對於ICE服務端來說的,而Proxy則是相對於ICE客戶端來說的.客戶端要想調用某個介面,必須持有該對象的代理,Proxy就是Object對象在客戶端的代理者。
4、Adapter
Adapter是對象適配器,用於把客戶端請求映射到某個Object對象的特定方法上。
5、IceBox
IceBox是應用容器,每個IceBox實例叫做一個Server,對應一個獨立的進程,系統為每個server分配一個獨立的server id,ICE通過對管理IceBox進程來實現負載均衡等操作。
6、IceNode
IceBox對應的獨立進程,而IceNode則對應的獨立伺服器節點,一個伺服器節點可以啟動多個IceBox進程實例。
7、IceGrid
ICE可以同時管理多個IceGrid,每個IceGrid可以理解成是一套服務的整體拓撲結構,Locator是用於定位IceGrid的定址信息。
8、Registry
服務的註冊中心,客戶端在調用介面時,需要指定主從註冊中心的地址,介面處理時首先請求註冊中心,註冊中心為其分配處理當前請求的adapter,adapter再將請求定位到具體的指定server進程中的特定object對象處理。
9、部署及運行結構
很多朋友覺得ICE複雜,可能是因為ICE提出了很多新的概念,而不清每個概念的包含關係,以至於不能理解它的運行方式,再加上資料匱乏所以難以上手。我覺得只要從兩個角度去看這個問題可能會比較清晰,一個是物理結構(伺服器節點),另一個是軟體結構(服務運行結構)。
- 從物理節點的角度
一個IceGrid集群,包括至少一個註冊中心節點(建議線上使用主從註冊中心節點),並且一個IceGrid集群管理多個Node節點。
- 從服務運行的角度
為什麼IceGrid部署時明明已經啟動了對應的Node節點,但application的配置文件還需要加Node配置?
其實這個問題很好理解,Grid可以管理多個節點,所以部署時被其管理的每個節點都要啟動一個icegridnode進程。而Grid也可以包含多個application,每個application可以根據需要占用Grid所有節點中的一個或多個。
service和adapter區別?
ICE中的service對應的是介面,一個server中可以包含多個介面。
adapter是特定對象上的介面,用來將客戶端請求分配到特定server上去處理。可能有些拗口,不過我拿XL-LightHouse的進程舉一個例子就很明白了。
如上圖:
server list,將集群所有IceBox進程羅列出來。
service list,serverID將當前server中的介面名稱羅列出來。
adapter list,ICE將所有server中的每個介面實例都分配了一個adapter句柄並將其羅列出來。
四、Ice安裝
部署前請確保系統已安裝JDK、GCC等依賴包!
不同發行版的安裝方式略有不同,以下命令只適合RHEL、CentOS、Rocky和Almalinux,如果您是其他的Linux發行版,請按照官網描述的方式安裝(也可以參考XL-LightHouse源碼中的/bin/install/install_ice.sh文件)。
官網安裝參考:https://doc.zeroc.com/ice/3.7/release-notes/using-the-linux-binary-distributions
下麵以AlmaLinux安裝為例:
#註意:rpm安裝需要根據系統版本指定el版本,使用uname命令可以查看系統el版本,不能配錯
[root@VM-6-22-rockylinux ~]# uname -a
Linux VM-6-22-rockylinux 5.14.0-162.6.1.el9_1.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Nov 18 02:06:38 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
我用的Alma9.1部署時需要指定el9,安裝命令即為:
sudo yum install https://zeroc.com/download/ice/3.7/el9/ice-repo-3.7.el9.noarch.rpm
sudo yum install ice-all-runtime ice-all-devel
安裝成功後,可以查看到相關的命令:
[root@VM-6-37-almalinux soft]# ice
icebox iceboxadmin icegridadmin icegridgui icegridregistry icepatch2client icestormadmin
icebox++11 icebridge icegriddb icegridnode icepatch2calc icepatch2server icestormdb
五、Slice文件生成
完整代碼請查閱:https://github.com/xl-xueling/ZerocICEDemo.git
(1)、創建slice文件printer.ice
[["java:package:com.dtstep.test.ice"]]
module PrinterServer {
interface Printer{
void printStr(string str);
};
};
(2)、生成介面代碼
#進入到Linux環境,執行以下命令
slice2java printer.ice
以上命令會在當前目錄生成介面代碼,因為我這裡的部署客戶端和服務端都是使用的Java語言,所以該介面代碼客戶端和服務端都需要用,如果客戶端和服務端是跨語言的需要用相應的命令生成不同的介面代碼。
(3)、創建工程並引入依賴包
<dependencies>
<dependency>
<groupId>com.zeroc</groupId>
<artifactId>ice</artifactId>
<version>3.7.9</version>
</dependency>
<dependency>
<groupId>com.zeroc</groupId>
<artifactId>icebox</artifactId>
<version>3.7.9</version>
</dependency>
<dependency>
<groupId>com.zeroc</groupId>
<artifactId>icegrid</artifactId>
<version>3.7.9</version>
</dependency>
<dependency>
<groupId>com.zeroc</groupId>
<artifactId>ice-compat</artifactId>
<version>3.7.9</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.zeroc</groupId>
<artifactId>glacier2</artifactId>
<version>3.7.9</version>
</dependency>
</dependencies>
(4)、將上面生成的介面代碼拷貝到工程中。
六、本地運行模式
完整代碼請查閱:https://github.com/xl-xueling/ZerocICEDemo.git
(1)、創建服務端實現類
public class PrinterServer {
public static void main(String[] args) {
Communicator ic = null;
try {
ic = Util.initialize();
ObjectAdapter adapter = ic.
createObjectAdapterWithEndpoints("PrinterServiceAdapter", "default -p 10000");
LocalPrinterServiceImpl servant = new LocalPrinterServiceImpl();
adapter.add(servant, Util.stringToIdentity("PrinterService"));
adapter.activate();
System.out.println("The server starts listening ...");
ic.waitForShutdown();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ic != null) {
ic.destroy();
}
}
}
}
(2)、創建客戶端端實現類
public class PrinterClient {
public static void main(String[] args) {
Communicator ic = null;
try {
ic = Util.initialize();
ObjectPrx base = ic.stringToProxy("PrinterService:default -p 10000");
PrinterPrx proxy = PrinterPrx.checkedCast(base);
for(int i=0;i<500;i++){
String msg = "Hello World_" + i;
proxy.printStr(msg);
System.out.println("client send message,msg:" + msg);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ic != null) {
ic.destroy();
}
}
}
}
(3)、同時本地運行客戶端和服務端可以正常發送和接收消息
七、集群模式配置
註意:
1、以下操作不要在root賬號下執行,如果在root賬號下啟動icegridnode,ice啟動服務為了安全起見使用的是nobody用戶,而該用戶沒有目錄訪問許可權,所以啟動會報錯!
2、完整代碼請查閱:https://github.com/xl-xueling/ZerocICEDemo.git
3、以下內容我直接基於【XL-LightHouse開源通用型流式數據統計項目】的結構進行修改,XL-LightHouse是對程式員日常開發很有幫助的輔助工具,歡迎GitHub搜索瞭解。
(1)、集群規劃
以下操作我在三台AlmaLinux節點上部署該集群,IP分別為:
10.206.6.37 #作為註冊中心主節點和Node運算節點
10.206.6.39 #作為註冊中心從節點和Node運算節點
10.206.6.25 #作為Node運算節點
(2)、創建工程所需目錄
在三台伺服器上依次創建以下目錄結構。
#該路徑用於存儲相關配置文件
mkdir -p /opt/soft/PrinterProject/conf
#該路徑用於存儲註冊中心的數據文件及日誌信息
mkdir -p /opt/soft/PrinterProject/data/registdata
#該路徑用於存儲Node節點的運行數據文件
mkdir -p /opt/soft/PrinterProject/data/nodedata
#該路徑用於存儲Node節點的輸出日誌信息
mkdir -p /opt/soft/PrinterProject/data/nodeoutput
#創建依賴包路徑,包含工程jar包和ICE依賴包
mkdir -p /opt/soft/PrinterProject/lib
#賦予目錄讀寫許可權
chmod -R 755 /opt/soft/PrinterProject
(3)、創建註冊中心主節點配置文件 master_registry.cfg
IceGrid.InstanceName=PrinterIceGrid
IceGrid.Registry.Client.Endpoints=tcp -p 4061
IceGrid.Registry.Server.Endpoints=tcp
IceGrid.Registry.Internal.Endpoints=tcp
IceGrid.Registry.PermissionsVerifier=PrinterIceGrid/NullPermissionsVerifier
IceGrid.Registry.AdminPermissionsVerifier=PrinterIceGrid/NullPermissionsVerifier
IceGrid.Registry.LMDB.Path=/opt/soft/PrinterProject/data/registdata
IceGrid.Registry.DynamicRegistration=1
Ice.Admin.InstanceName=AdminInstance
Ice.LogFile=/opt/soft/PrinterProject/data/registdata/ice-regist.log
(4)、創建註冊中心從節點配置文件 slave_registry.cfg
Ice.Default.Locator=PrinterIceGrid/Locator:tcp -h 10.206.6.37 -p 4061
IceGrid.Registry.Client.Endpoints=tcp -p 4061
IceGrid.Registry.Server.Endpoints=tcp
IceGrid.Registry.Internal.Endpoints=tcp
IceGrid.Registry.PermissionsVerifier=PrinterIceGrid/NullPermissionsVerifier
IceGrid.Registry.AdminPermissionsVerifier=PrinterIceGrid/NullPermissionsVerifier
IceGrid.Registry.LMDB.Path=/opt/soft/PrinterProject/data/registdata
IceGrid.Registry.DynamicRegistration=1
IceGrid.Registry.ReplicaName=Replica1
Ice.LogFile=/opt/soft/PrinterProject/data/registdata/ice-regist.log
(5)、創建每台節點對應的node.cfg文件
註意:每個節點的Node.Name不可相同,而且需要與application.xml文件中的節點名稱一致。
Ice.Default.Locator=PrinterIceGrid/Locator:tcp -h 10.206.6.37 -p 4061:tcp -h 10.206.6.39 -p 4061
IceGrid.Node.Name=node1
IceGrid.Node.Endpoints=tcp
#配置文件中的路徑必須首先創建出來,並且目錄需要有寫入數據的許可權
IceGrid.Node.Data=/opt/soft/PrinterProject/data/nodedata
IceGrid.Node.Output=/opt/soft/PrinterProject/data/nodeoutput
IceGrid.Node.CollocateRegistry=0
Ice.StdErr=/opt/soft/PrinterProject/data/nodeoutput/ice_stderr.log
(6)、創建應用配置文件application.xml
<icegrid>
<application name="PrinterServiceApplication">
<properties id="MultiThreaded">
<property name="Ice.PrintStackTraces" value="1"/>
<property name="IceSSL.Trace.Security" value="2"/>
<property name="Ice.ThreadPool.Client.Size" value="50"/>
<property name="Ice.ThreadPool.Client.SizeMax" value="500"/>
<property name="Ice.ThreadPool.Server.Size" value="50"/>
<property name="Ice.ThreadPool.Serxver.SizeMax" value="500"/>
<property name="IceBox.InheritProperties" value="1"/>
<property name="Ice.Override.ConnectTimeout" value="2000"/>
<property name="Ice.Override.Timeout" value="2000" />
<property name="IceBox.Trace.ServiceObserver" value="1"/>
<property name="Ice.Default.Timeout" value="2000"/>
<property name="Ice.Default.LocatorCacheTimeout" value="1200" />
<property name="Ice.BackgroundLocatorCacheUpdates" value="1"/>
</properties>
<server-template id="PrinterBoxTemplate">
<parameter name="index" default="0"/>
<icebox id="printer-icebox${index}" exe="java" activation="on-demand" >
<properties>
<properties refid="MultiThreaded" />
</properties>
<option>-Xmx4000M</option>
<option>-Xms4000m</option>
<option>-XX:+UseCompressedOops</option>
<option>-XX:+HeapDumpOnOutOfMemoryError</option>
<option>-XX:+UseG1GC</option>
<option>-XX:-UseBiasedLocking</option>
<option>-XX:G1HeapRegionSize=4M</option>
<option>com.zeroc.IceBox.Server</option>
<env>CLASSPATH=.:/opt/soft/PrinterProject/lib/*</env>
<!--該處配置server端的介面類地址-->
<service name="PrinterService" entry="com.dtstep.test.ice.PrinterServer.cluster.server.PrinterService">
<adapter name="PrinterService" id="PrinterService${index}" endpoints="default" replica-group="PrinterServiceRep" />
</service>
</icebox>
</server-template>
<replica-group id="PrinterServiceRep">
<load-balancing type="adaptive" n-replicas="0" />
<!--該處的type配置slice文件的module名稱和interface名稱-->
<object identity="PrinterServiceIdentity" type="::PrinterServer::Printer"/>
</replica-group>
<!--node名稱,必須與node.cfg中的名稱對應-->
<node name="node1">
<!--註意template名稱需要與上面的server-template保持一致 -->
<server-instance template="PrinterBoxTemplate" index="11"/>
<server-instance template="PrinterBoxTemplate" index="12"/>
</node>
<node name="node2">
<server-instance template="PrinterBoxTemplate" index="21"/>
<server-instance template="PrinterBoxTemplate" index="22"/>
</node>
<node name="node3">
<server-instance template="PrinterBoxTemplate" index="31"/>
<server-instance template="PrinterBoxTemplate" index="32"/>
</node>
</application>
</icegrid>
(7)、創建集群Server端實現
public class PrinterService implements Service {
@Override
public void start(String s, Communicator communicator, String[] strings) {
ObjectAdapter adapter = communicator.createObjectAdapter(s);
communicator.getProperties().setProperty("Ice.MessageSizeMax", "1409600");
ClusterPrinterServiceImpl servant = new ClusterPrinterServiceImpl();
adapter.add(servant, Util.stringToIdentity("PrinterServiceIdentity"));
adapter.activate();
System.out.println("printer server start success!");
}
@Override
public void stop() {
}
}
(8)、創建集群Client端實現
public class ClientTest {
public static void main(String[] args) {
Communicator ic = null;
try {
String initParams = "--Ice.Default.Locator=PrinterIceGrid/Locator:tcp -h 10.206.6.39 -p 4061:tcp -h 10.206.6.37 -p 4061 -z";
String [] params = new String[]{initParams};
ic = Util.initialize(params);
//註意該處配置與介面對應的replica-group的identity名稱保持一致
ObjectPrx objectPrx = ic.stringToProxy("PrinterServiceIdentity").ice_connectionId(UUID.randomUUID().toString()).ice_locatorCacheTimeout(1200);
PrinterPrx proxy = PrinterPrx.checkedCast(objectPrx);
for(int i=0;i<500;i++){
String msg = "Hello World_" + i;
proxy.printStr(msg);
System.out.println("client send message,msg:" + msg);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ic != null) {
ic.destroy();
}
}
}
}
(9)、將上述配置文件上傳到conf目錄。
(10)、將工程打包和maven依賴包上傳到lib目錄下
(11)、工程的完整文件及路徑信息如下
[root@VM-6-37-almalinux PrinterProject]# pwd
/opt/soft/PrinterProject
[root@VM-6-37-almalinux PrinterProject]# tree -a ./
./
├── conf
│ ├── application.xml
│ ├── master_registry.cfg
│ ├── node1.cfg
│ ├── node2.cfg
│ ├── node3.cfg
│ ├── printer.ice
│ └── slave_registry.cfg
├── data
│ ├── nodedata
│ ├── nodeoutput
│ └── registdata
└── lib
├── glacier2-3.7.9.jar
├── ice-3.7.9.jar
├── icebox-3.7.9.jar
├── ice-compat-3.7.9.jar
├── icegrid-3.7.9.jar
└── TestICE-1.0-SNAPSHOT.jar
5 directories, 13 files
(12)、將以上工程文件夾同步到集群各個節點。
七、集群模式啟動
(1)、在第一臺伺服器上啟動註冊中心主節點
#註意:如果執行重啟操作,請檢查一下icegridregistry進程是否仍存在,如果存在先kill掉,然後務必刪除registdata目錄下的文件後再重啟。
icegridregistry --Ice.Config=/opt/soft/PrinterProject/conf/master_registry.cfg &
執行完後,查看/opt/soft/PrinterProject/data/registdata/ice-regist.log是否有異常。
(2)、在第二台伺服器上啟動註冊中心主節點,註意與主節點配置文件不同
#註意:如果重啟請檢查一下icegridregistry進程是否仍存在,如果存在kill掉,然後務必刪除registdata目錄下的文件後再重啟。
icegridregistry --Ice.Config=/opt/soft/PrinterProject/conf/slave_registry.cfg &
執行完後,查看/opt/soft/PrinterProject/data/registdata/ice-regist.log是否有異常。
(3)、在三台伺服器依次啟動節點,註意配置文件各不相同
#註意:如果重啟請檢查一下icegridnode進程是否仍存在,如果存在kill掉,並同時將nodedata目錄下文件刪除後再重啟。
icegridnode --Ice.Config=/opt/soft/PrinterProject/conf/node1.cfg &
icegridnode --Ice.Config=/opt/soft/PrinterProject/conf/node2.cfg &
icegridnode --Ice.Config=/opt/soft/PrinterProject/conf/node3.cfg &
(4)、進程檢查
執行完以上操作後,在註冊中心主節點執行
icegridadmin命令,user id和password隨便輸入進入到ICE控制台。
執行help命令,可查看所有組件,執行 node help,service help ...可查看每個組件的幫助信息。
執行node list命令,查看當前節點是否啟動正常,如果正常進行下一步。
八、啟動應用Application
執行icegridamin命令進入ICE控制台。
# 啟動應用
>> application add /opt/soft/PrinterProject/conf/application.xml
# 啟動應用後,預設不會立即啟動server,當有客戶端請求時會自動啟動server
# 查看server 列表
>> server list
# 手動啟動server,進入nodeout目錄查看日誌是否正常。
>> server start printer-icebox11
# 查看應用列表
>> application list
# 移除應用
>> application remove PrinterServiceApplication
執行cluster目錄內的ClientTest.java發送消息到服務端,服務端的nodeoutput目錄可以正常輸出日誌信息(註意:由於集群在三台伺服器上啟動了6個進程,所以需要具體看下請求被分發到了哪個進程,輸出的日誌文件是不同的,線上使用的時候可以配置log4j讓節點內的多個進程都輸出到一個日誌文件)。
[work@VM-6-25-almalinux nodeoutput]$ tail -f printer-icebox32.out
server receive message,msg:Hello World_25
server receive message,msg:Hello World_26
server receive message,msg:Hello World_27
server receive message,msg:Hello World_28
server receive message,msg:Hello World_29
server receive message,msg:Hello World_30
server receive message,msg:Hello World_31
server receive message,msg:Hello World_32
server receive message,msg:Hello World_33
server receive message,msg:Hello World_34
server receive message,msg:Hello World_35
完整代碼及配置信息請查閱:https://github.com/xl-xueling/ZerocICEDemo.git
九、解鎖新技能、打開新世界
上述內容向大家介紹了一款超高性能的RPC服務框架ZerocICE的集群部署方式和初步使用。接下來向大家推薦一款更具有實用價值的開發好幫手XL-LightHouse。XL-LightHouse是一款通用型流式大數據統計工具,對於程式員來說,它有幾個特性:
1、世界範圍獨一無二;
2、它對各種工種的程式員都具有很大的實用價值;
3、你只要用上它,就會離不開它;
建議您花一兩分鐘的時間GitHub搜索XL-LightHouse或訪問dtstep.com瞭解更多!