发布时间:2025-12-09 11:44:09 浏览次数:1
这系列文章将会一步步教你如何部署一个ICE服务,如果你正在读这篇博客,我想你已经了解了什么是ICE(Internet Communications Engine),以及如何去实现ICE服务,并且了解什么是ICE对象、ICE对象标识符、ICE对象适配器、ICE服务实现servant、ICE通信器等概念,当然如果你连什么是ICE都不知道,我不建议你继续读下去。
先说一下ICE的基本组件:
(1) Slice工具:将Slice语言定义的接口编译成各种特定语言实现的代码,这属于开发环境的一部分
(2) ICE容器:IceBox、Ice Node、Ice Registry、Ice Grid、Ice Admin等组件,这些是用于部署和管理Ice服务,你可以在你的服务中选择其中的几项使用。
(3)ICE运行库:由ICE实现平台提供的一套API,不同的语言有不同的实现,如java使用的是jar包,c++使用的是.so文件,这套API需要被ICE服务的客户端和服务端调用
以实现底层通信。
我在写这系列博客时使用的开发环境如下:
eclipse
Ice-3.3.1
jdk-1.6
CentOS release 5.11 (Final)
我们这里实现一个查询雇员信息的服务,用于演示Ice服务的部署方式。
(一)我们先在eclipse中创建一个java项目,并在项目下创建一个名为slice的文件夹,然后使用Ice 的Slice语言定义Ice服务接口 并将文件命名为query.ice,并将文件放入项目中的slice文件夹中
[["java:package:com.yujie.ice"]]module info{struct EmployeeInfo{string name;int age;bool isLeave;double salary;string remark;};interface QueryEmployee{EmployeeInfo query(EmployeeInfo msg);};};query.ice文件中定义了报名com.yujie.ice并用info进行包隔离最后生成的包名就是com.yujie.ice.info。
文件中定了一个EmployeeInfo结构体作为员工的信息结构体,定义了一个接口QueryEmployee,我们需要在自己的服务端代码中实现这个接口。
(二) 使用Slice工具将query.ice定义的接口 生成java实现的具体代码,我的eclipse已经安装了ICE 编译插件,直接编译这个java项目就可以生成相应的接口客户端和服务端的stub代码。如果你没有安装Slice2java 编译插件你可以使用类似slice2java –output-dir ../src/main/java/ query.ice这样的命令生成代码,前提是你已经设置了ICE相应的环境变量。
编译之后的项目目录如下所示:
(三)实现ice服务端代码。
/** * 继承服务端stub代码_QueryEmployeeDisp类 并实现query接口 * @author yujie.wang * */public class QueryEmployeeImpl extends _QueryEmployeeDisp{@Overridepublic EmployeeInfo query(EmployeeInfo msg, Current __current) {// TODO Auto-generated method stubEmployeeInfo ei = new EmployeeInfo();ei.age = 23;ei.name = msg.name;ei.isLeave = false;ei.salary = 2000.0;ei.remark = "he is a good employee";return ei;}}我们这里要实现一个查询雇员信息的服务,返回一个雇员的的信息。
之后我们实现启动ICE服务端的代码,代码如下所示:
/** * 实现启动ICE服务端代码 * @author yujie.wang * */public class QueryEmployeeServer {public static void main(String [] args){int state = 0;Ice.Communicator communicator = null;try {//初始化ice通信器communicator,可以使用args传入一下ice初始化的参数如超时时间,线程池大小等communicator = Ice.Util.initialize(args);//创建一个名为queryEmployeeAdapter的适配器并且默认使用tcp协议 服务部署在10.4.30.81机器上 服务开启10006监听端口Ice.ObjectAdapter adapter = communicator.createObjectAdapterWithEndpoints("queryEmployeeAdapter","default -p 10006");// 创建服务端代码实现servantQueryEmployeeImpl servant = new QueryEmployeeImpl();// 将servant与ice对象标识符建立映射关系,并添加到ice对象适配器中adapter.add(servant, Ice.Util.stringToIdentity("queryServer"));// 激活对象适配器adapter.activate();System.out.println("QueryEmployeeServer adapter activate");// 服务在退出之前一直保持监听状态communicator.waitForShutdown();} catch (Exception e) {// TODO: handle exceptionstate = 1;System.out.println(e);} finally{if(communicator != null){communicator.destroy();}}System.out.println("state: "+ state);}}(四) 实现客户端代码
/** * 实现客户端调用接口代码 * @author yujie.wang * */public class QueryEmployeeClient {public static void main(String[] args) {// TODO Auto-generated method stubIce.Communicator communicator = null;try {//初始化ice通信器communicator,可以使用args传入一下ice初始化的参数如超时时间,线程池大小等communicator = Ice.Util.initialize(args);// 传入远程服务单元的 ice对象标识符 协议默认tcp 主机 已经服务监听端口Ice.ObjectPrx op = communicator.stringToProxy("queryServer:default -h 10.4.30.81 -p 10006");// 检查通用客户端代理op 是不是queryServer对象标识符所关联的ice对象的代理QueryEmployeePrx qp = QueryEmployeePrxHelper.checkedCast(op);if(qp == null){throw new Exception("qp == null");}// 构造传入参数EmployeeInfo ei = new EmployeeInfo();ei.name = "yujie.wang";// 调用接口EmployeeInfo result = qp.query(ei);if(result == null){throw new Exception("result == null");}// 输出服务端返回结果System.out.println(result.remark);} catch (Exception e) {// TODO: handle exceptionSystem.out.println(e);}}}(五) 服务部署
当然你可以直接在eclipse里运行服务端启动ice服务的main方法,但是为了延续后面的服务部署思路,我这里打算将代码打成jar包,通过一个简单的shell脚本来运行这个main方法。
接下来 我们将java项目进行编译,之后将编译后的class代码通过jar -cvf yujie-ice-test1.jar ./* 命令打成yujie-ice-test1.jar包。
我在服务器上写了一个非常简单的容器IceServer用于启动这个ice服务,该容器的目录结构如下所示:
bin目录下面有如下两个文件:
env.sh
#!/bin/shif [ -z "$JAVA_HOME" ]; then JAVA_HOME="/data/web/jdk"fiecho "JAVA_HOME:$JAVA_HOME"if [ -z "$SERVER_HOME" ]; then BIN_DIR=`readlink -f "$0"` echo "BIN_DIR:$BIN_DIR" BASE_DIR=`dirname "$BIN_DIR"` echo "BASE_DIR:$BASE_DIR" SERVER_HOME="`cd $BASE_DIR/.. && pwd`"else echo "SERVER_HOME IS NOT EMPTY"fiecho "SERVER_HOME:$SERVER_HOME"startIceServer.sh
#!/bin/sh. ./env.shAPP_HOME=$SERVER_HOME/libAPP_MAINCLASS=com.yujie.ice.server.QueryEmployeeServerCLASSPATH=$APP_HOMEecho $CLASSPATHfor i in "$APP_HOME"/*.jar;do CLASSPATH="$CLASSPATH":"$i";doneecho $CLASSPATH$JAVA_HOME/bin/java -Xms1000M -Xmx1000M -Xmn500M -XX:PermSize=128m -XX:MaxPermSize=256m -cp $CLASSPATH $APP_MAINCLASS lib目录下面分别是ICE 平台API jar包和我们自己服务的jar包。
现在我们用startIceServer.sh脚本来启动服务端代码
sh startIceserver.sh &
最后我们在本地运行客户端调用代码输出如下:
(六)该部署方式总结
优点:直接使用服务端main函数启动,没有引入ice其他容器组件,客户端和服务端直接通过tcp建立连接,是一个十分轻量级的服务。
确定:客户端直接写死了服务的Endpoint信息,及其不灵活;服务端部署方式简单粗暴 不易运维,无法实现负载均衡、故障恢复
这种部署方式只是简单粗暴的演示,完全不能作为工业级的部署方案。
下一节我们将实现另一种部署方案,该方案会解决该种部署方式的一些问题。
138731.html