c#选取文件夹路径_关于ICE-3.5.1部署及生成 C#|Java|PHP|Python|Ruby等开发语言的使用教程...

71b80bed4da0fdc381f39ef3d443f041.png

目录

一、 什么是ICE

二、 安装

三、环境变量配置

四、来一个Demo,小试牛刀

1、编写Slice接口

2、 编写用于C# 的Slice 定义

3、 编写和编译服务器

4、 编写和编译客户

5、 效果

五、正式项目应用

1、前期准备

2、 添加ICE客户端类库

3、 选择路径

4、 开始生成CS文件,出错

5、 再次生成,成功

6、 使用

六、 扩展知识

1、JAVA

2、PHP

3、Python

4、Ruby

七、 概念和原理

1、Ice Object

2、ObjectAdapter

3、Ice Proxy

4、Location Service


什么是ICE

ICE(Internet Communications Engine)是出自ZeroC之下一种面向对象的RPC中间件平台,主要用于网络通讯。它为面向对象的“客户端-服务器”模型的应用提供了一组很好的工具和API接口。目前在全世界被应用于很多项目之中。ICE中间件号称标准统一,开源,跨平台,跨语言,分布式,安全,服务透明,负载均衡,面向对象,性能优越,防火期穿透,通讯屏蔽。因此相比Corba,DCOM,SOAP,J2EE等的中间件技术,自然是集众多优点于一身,而却没有他们的缺点。

安装

在官网下载https://zeroc.com/downloads/ice/3.5; 但在官网下载一般需要商业许可,在此我提供了我网盘内资源可供大家学习使用,地址如下:

719581a632cb8e2c7411c0e3b9eb8e8f.png
如果有需要的小伙伴,请留言或私信

下载后根据提示进行安装即可,在安装的过程注意下自己选择的安装路径,便于后期配置环境变量时使用。

安装后,进入到我们安装路径后,可以得到如下图所示:

f9f817381cccd0ac77bce892e89a3cc4.png
我们可以看到,Ice可能支持的开发语言等信息。

三、环境变量配置

配置ICE_HOME ice的安装目录

在path中配置 %ICE_HOME%bin

然后以 slice2java -v 验证

四、来一个Demo,小试牛刀

此文档我们以C#为例子来进行讲解;通过以上目录一“什么是ICE”我们知道面向对象的“客户端-服务器”模型... 我们需要搭建服务端和客户端来进行效果一演示和验证。

1、编写Slice接口

首先我们第一步我们要编写一个Ice Object在服务端具体化为一个Servant实例,即我们用某种具体编程语言实现的一个Slice接口,如下图:

e27601487d20525f509f886a2c491e46.png

源码

module Family
{
   interface print
    {
       void printString(string s);
    };
};

我们把这段文本保存为Printer.ice 的文件,为了便于演示我本地直接放在了Users文件夹下。

2b530addc6352d0ced102dddca424c05.png

编写用于C# 的Slice 定义

要创建我们的C# 应用,第一步是要编译我们的Slice 定义,生成C#代理和骨架。在Windows上,你可以这样编译定义:

slice2cs.exe Printer.ice

slice2cs 编译器根据这个定义生成一些.cs 源文件。我们目前无需关注这些文件的确切内容——它们包含的是编译器生成的代码,与我们在Printer.ice 中定义的Printer 接口相对应。

开始-->运行-->cmd-->slice2cs.exe Printer.ice(记得将你写的ICE文件拖入运行cmd看到的对应文件夹)-->回车

52c60cc56c6970a8ce2b549e02cddd34.png

下图为刚生成的.cs文件:

1436c43f463ef77fc7a40f6302cf3199.png

编写和编译服务器

首先,我们要添加对ICE的引用,右键添加引用,选择ICE安装目录下的Ice.dll,例如我是(D:Program Files (x86)ZeroCIce-3.5.1Assemblies...)

a50f82903fec965893a885b217ed326b.png

其次,要实现我们的Printer 接口,我们必须创建一个servant 类。按照惯例,servant 类的名字是它们的接口的名字加上一个I 后缀,所以我们的servant类叫作PrinterI

2711341f4d4fc93193a5c02747f62516.png

PrinterI 类继承自叫作PrinterDisp_ 的基类。继承基类后我们可以简单地在PrinterDisp_ 上点击右键实现抽象类,将会自动出现对这个基类里面函数的重写(其实就是我们在ICE文件中定义的接口)。这个基类由slice2cs 编译器生成,是一个抽象类,其中含有一个printString方法,其参数是打印机要打印的串,以及类型为Ice.Current 的对象.我们的printString 方法的实现会简单地把它的参数写到终端。

88bf290146cb32038e7271eab8d5c215.png

源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Family;
using Ice;
namespace ice_3._5._1_test
{
    class PrinterI : printDisp_
    {
        public PrinterI()
        {

        }
        public override void printString(string s, Current current__)
        {
            Console.WriteLine(s);
            Console.ReadLine();
        }
    }
}

服务器代码的其余部分在一个叫作Server.cs 的源文件中,下面给出了其完整代码:

5afac547c502e86fe53b2e02638c34fe.png

源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ice_3._5._1_test
{
    class Program
    {
        static void Main(string[] args)
        {
            int status = 0;
            //创建通信器
            Ice.Communicator ic = null;
            try
            {               
                //初始化运行时
                ic = Ice.Util.initialize(ref args);
                //创建适配器对象,参数为(适配器名称,缺省TCP/IP,端口号)
                Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinter", "default -p 10000");
                //服务器端run time 已经初始化,再实例化PrinterI赋给接口(得到一个可以完成具体方法的对象)//PrinterI p = new PrinterI();Ice.Object o = p;
                Ice.Object obj = new PrinterI();
                //在适配器中传入对象,以及适配器对象名称(接收者可能有多个)
                adapter.add(obj, Ice.Util.stringToIdentity("SimplePrinter"));
                //激活适配器(适配器创建完毕之后处于holding状态)
                adapter.activate();
                //挂起发出调用的线程,直到服务器实现终止
                ic.waitForShutdown();
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e);
                status = 1;
            }
            finally
            {
                if (ic != null)
                {
                    ic.destroy();
                }
            }
            //终止此进程并为基础操作系统提供指定的退出代码 status 为 0 代表已经完成。
            Environment.Exit(status);
        }
    }
}

Main 的主体含有一个try 块,我们把所有的服务器代码都放在其中;然后是两个catch 处理器。第一个处理器捕捉Ice run time 可能抛出的所有异常,其意图是,如果代码在任何地方遇到意料之外的Ice 运行时异常,栈会一直退回到Main,打印出异常,然后把失败返回给操作系统。第二个处理器捕捉串常量,其意图是,如果我们在代码某处遇到致命错误,我们可以简单地抛出带有出错消息的串文本。这也会使栈退回到main,打印出出错消息,然后把失败返回给操作系统。

这段代码会在退出之前销毁通信器(如果曾经成功创建过)。要使Ice run time 正常结束,这样做是必需的:程序必须调用它所创建的任何通信器的destroy ;否则就会产生不确定的行为。我们把对destroy 的调用放进finally 块,这样,不管前面的try 块中发生什么异常,通信器都保证会正确销毁。

编写和编译客户

客户代码在Client.cs 中,看起来与服务器非常类似。同样是要添加slice2cs.exe Printer.ice生成的类文件到项目中,添加对Ice.dll的引用。此时不用override基类中的方法,其实很简单,因为我们的目的是让客户端向服务器发送一条指令,让服务器去完成而已。

0de5290e5595009d5ece19ab0aa2480d.png

下面是完整的代码:

源码:

using Family;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICE_3._5._1_Client_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int status = 0;
            //创建通信器
            Ice.Communicator ic = null;
            try
            {
                //初始化运行时
                ic = Ice.Util.initialize(ref args);
                //调用通信器的stringToProxy创建一个代理(这个串包含的是对象标识和服务器所用的端口号)
                Ice.ObjectPrx obj = ic.stringToProxy("SimplePrinter:default -p 10000");
                printPrx printer = printPrxHelper.checkedCast(obj);
                if (printer == null)
                {
                    throw new ApplicationException("Invalid proxy");
                }
                printer.printString("Hello World!");
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e);
                status = 1;
            }
            finally
            {
                if (ic != null)
                {
                    ic.destroy();
                }
            }
            //终止此进程并为基础操作系统提供指定的退出代码 status 为 0 代表已经完成。
            Environment.Exit(status);
        }
    }
}

效果

先运行(F5)服务端

再运行(F5)客户端

我们可以看到服务端的控制台会打印出我们在客户端预设好的信息。

d2f05743e0c098901ab69ec95d12dc6a.png

五、正式项目应用

1、前期准备

首先我们先介绍下我们目前的状况。我们需要用开发语言C#(C Shap)调用服务器(Liunx系统) 使用C++开发语言编写的程序,C++程序主要的功能就是访问mysql数据库(像这种你要问为什么不直接使用C#访问数据库,干嘛还绕那么大一圈的问题,我是不会回答的,那样又要扯一堆,如果你已经用了此技术,相信你也不需要问这么基础的问题,如果你没有用到只是想简单了解,此文档已经可以满足你啦),C++端给我们提供了已经使用ICE生成的ice文件的文件夹,我们暂时放在了桌面,文件夹里大概就是这个样子:

fb925c9e0a8d00b6c2b4a876c5ec66ff.png

文件内容大概这样子

43233f59cc3525a149c0727f723ee27c.png

以上就是我们准备在C#的开发环境下使用的文件;

添加ICE客户端类库

我们想使用服务端提供的slice接口程序,首先要在项目中创建客户端来响应,因为我们要使用的文件很多,为了便于管理最好的方式就是创建一个类库,如下图:

9428b734748b85167d365df155a8d0ae.png

选择路径

根据“目录三”我们配置的环境变量,我们需要找到安装目录,【D:Program Files (x86)ZeroCIce-3.5.1...】因为我们ICE使用的是Slice(Specification Language for Ice)语言,为了方便查找我们选择 slice 文件夹。进入文件后我们把ices文件复制过来。如下图:

b54dcbbdf25d2c92a9acbe737a2c7c42.png

开始生成CS文件,出错

开始菜单-->右键-->Windows PowerShell(管理员)(A)-->定位到我们Ices文件夹上级目录(我本地为D:Program Files (x86)ZeroCIce-3.5.1slice)-->键入命令(slice2cs.exe xxx.ice)本次我们以PrePlanOP.ice文件来进行演示;我们发现报错了,如下图:

77ff17abd27c761fa199646fca8f4a72.png

提示告诉我们说不能打开”Ice/BuiltinSequences.ice”文件,也就是没有找到文件,说明我们缺少这个文件,从文件名我们大概能猜出大概是构建序列许可的文件,应该是ice3.5.1安装程序自带的文件,说明不是我们缺少文件,而是我们路径有问题,那我们先找到此文件,

通过系统检索我们发现在sliceice的文件夹下

e9036cd301806728350c5303028a1e3f.png

我们发现跟我们所在的文件是所在同一个目录下

a36ae19954f7f1ab346a914e778b0906.png

再次生成,成功

调整路径,我们在重新生成,根据提示我们需要在Ices文件夹下创建一个ice文件夹然后把BuiltinSequences.ice文件放进去,但是这样可能会缺少其他文件,索性把整个文件夹都复制到ices文件夹下;如下图

9bad86fa765a10f0fab50892cc8f8480.png

然后我们在根据以上的指令进行生成,看效果:

72c5b2ff788906b9e7793d02aea64884.png

我们看到没有提示报错信息啦,然后我们在回到 slice 文件夹下看看,发现这时候多了一个cs文件

317e30464bb8d26b7bcd995bf30335fb.png

文件名称和我们生成前的ice文件相同,说明我们已经我们已经生成成功啦。

PS:为了效率我们可以一次生成多个文件,只需要文件和文件用空格隔开即可,如下图:

d16b48ff3a56dd41431b41a9231379b0.png

使用

将生成的文件复制到我们刚刚新建的类库内即可使用,我本地是根据自己的业务放到了anync文件夹内,你可以根据自己的业务自行调整。

0a047d620e39b9f871252ebffe6c5aa1.png

至此,演示完毕。

扩展知识

以上我们使用的C#(C shap)开发语言,下面简单说说其他语言的使用,

1、JAVA

命令:slice2java xxx.ice

如图示:

ad1b7df67b2e98cc76beef12c7a1e0f4.png

Java生成的不是单纯的java文件而是根据命名空间创建一个文件夹并生成许多辅助文件

如图:

b76221b3620bdc2648b547038daa7e2c.png

2、PHP

命令:slice2php xxx.ice

如图:

df726577238e199e65288cfc81dadca6.png

3、Python

命令:slice2py xxx.ice

如图:

bff0851a009d09241df262de3cf53b85.png

4、Ruby

命令:slice2rb xxx.ice

如图:

5dcab01abd326631ed9b181141ab4ad7.png

大家从以上的扩展可以看出,用法基本与C#用法类似,所以不过多在此赘述,如果有机会可能会在其他文档进行详细说明。

概念和原理

来稍微了解下Ice概念和原理,前面已经跑通过一个小例子,再回过来看概念和原理,可能会容易理解一些。作为一个复杂的RPC平台,Ice也创造了很多概念和术语,其中一个名词就是Slice,这个前面已经学习过了,但现在也只是会用,具体生成那一坨文件还没有细看。下面我们从几个基本概念来看下。

1、Ice Object

关于Ice Object,截取《ZeroC Ice权威指南》中的一句话:

拥有一个对象标识符Object Identity来区别与其他类型对象,Ice对象模型中要求对象标识符是全局唯一的,即没有任何对象的标识符相同。一个Ice Object在服务端具体化为一个Servant实例,即我们用某种具体编程语言实现的一个Slice接口并新建的某个对象就是一个Servant。

看一段代码就清楚了,我们先来截取Server.java中的一段代码:

// 创建提供服务的站点,缺省协议为TCP/IP,端口为10000

Ice.ObjectAdapter adapter = ic.
createObjectAdapterWithEndpoints("MyServiceAdapter", "default -p 10000");

// 创建具体提供服务的实例

HelloWorldServiceImpl servant = new HelloWorldServiceImpl();

// 将服务实例添加到提供服务的站点

adapter.add(servant, Ice.Util.stringToIdentity("HelloService"));
adapter.activate();

再看ObjectAdapter的add()方法申明:Ice.ObjectPrx add(Ice.Object servant, Identity id);,可见第一个参数就是Ice.Object类型,Identity也能从第二个参数体现出来。这么看来,是否可以理解成HelloWorldServiceImpl就是一个Ice Object;而new HelloWorldServiceImpl()就是一个Servant实例?这个只是我个人的理解,不能确定。

2、ObjectAdapter

从Ice Object中引用的代码来看,Object Adapter像是Ice Object的宿主,提供了Ice Object的访问地址(Endpoint),并且负责完成请求处理转发的流程。下面来看下Object Adapter具有的功能:

提供一个或多个通信端点(Endpoints),客户端通过这些断点中的某个端点,连接到一个具体的Ice Object对象,一个Endpoint由服务端所使用的通信协议、IP地址、端口等信息组成,例如default -h 192.168.0.1 -p 10000,表示采用默认协议(TCP),绑定在192.168.0.1的10000端口上。

绑定一个或多个Servant,每个Servant与一个Ice Object映射,将客户端针对某个Ice Object的请求委派到对应的Servant上,并完成整个请求流程的处理过程,包括底层通信。从代码看就是adapter.add()方法实现的功能。

3、Ice Proxy

先引用下客户端调用的代码:

// 通过名称、协议、端口,构造通用Proxy对象

Ice.ObjectPrx base = ic.stringToProxy("HelloService:default -p 10000");

// 将通用Proxy对象转为真实的服务(uncheckedCast(base)可以减少一次远程调用)

HelloWorldServicePrx prxy = HelloWorldServicePrxHelper.checkedCast(base);
prxy.hello()

Ice Proxy是Ice Object在客户端的代表,简单来说,Proxy是客户端用来访问远程某个Ice Object的本地代理。Proxy存在于客户端的进程地址空间,当客户端调用远程对象的某个方法时,Ice运行时期的客户端代码库会完成如下工作:

  • 定位远程对象Ice Object
  • 如果Ice Object所在的Server处于关闭状态,则自动激活此Server,并激活远程对象。注:这一点暂时没有试出来
  • 将方法的入参通过Socket传输到远程对象
  • 等待调用完成
  • 将方法的出参返回给客户端,若发生异常则抛出调用异常

作为远程Ice Object的本地代理,Proxy还持有如下重要信息:

  • 远程Server的地址信息,用来初始化通信,可以通过ic.stringToProxy()方法初始化
  • 用来定位Ice Object的对象标识符:object identity,从代码来看该信息封装在checkedCast()方法中
  • 可选的Facet标识符,用来确定引用Ice Object的哪个接口
  • 一个具体的Proxy可以用一个包括Endpoint信息的字符串描述,比如HelloService:default -p 10000,表示在远端的TCP
  • 端口10000上绑定的一个HelloService对象的Proxy。

目前直观的感觉是一个Proxy对应一个Endpoint,也就是一个访问地址。Proxy有Direct Proxy与Indirect Proxy两种。前者是直接绑定某个远端Object的访问地址,就像上面引用的代码一样,这种情况下地址是写死的。后者则不绑定远程Object的某个具体通信地址,但是Endpoint的名称一定是有的,由于没有远程对象的地址信息,因此,需要借助寻址服务Location Service来获取对应的Ice Object的通信地址。如果要真的使用该产品的话,我想客户端肯定是采用Indirect Proxy这种方式来发现服务。

4、Location Service

Location Service其实就是利用了Ice的注册表来实现Object Identity到Endpoint的查询服务,通俗的讲也就是服务的发现。Ice的注册表中保持了Ice Object、Object Proxy、Server等相关信息,属于IceGrid体系的重要组成部分,这种注册表组件也是大多数分布式系统的关键组件之一。
理解Indirect Proxy对于理解和掌握Ice来说非常重要,因为在IceGrid这种分布式框架中,所有Proxy都是Indirect的,从而实现了复杂的负载均衡和故障恢复机制。其调用过程如下:

b47f9fb73c1af1c078b92051aaf06ed4.png
  • 客户端发起对远程Ice Object的方法InitialOp的调用
  • 客户端寻址,Indirect Proxy向Location Service发出查询命令,Location Service收到请求后从注册表Registry中获取对应Object的地址信息并返回
  • 客户端Ice Proxy直接跟远程对象建立通信连接,实现调用

5、总结

最后总结下上面学到的术语,并加以补充。在Ice中,Ice Object、Object Adapter、Servant属于服务端的概念,它们存在于一个Ice Server中,这个Server容器通常是一个IceBox进程,暂且可以理解为一个Tomcat。ASM(Active Servant Map)是一个对象标识符(Object Identity)到对应Servant的查询表,也就是Ice Object到Servant的映射表,当一个客户端在某个Endpoint上发起对某个Ice Object的请求访问时,ASM用来快速定位到具体的Servant上,以便高效地委派请求。下面给出一张示意图:

e1aa9f958b350dd4013f6cc5e9a49090.png

再看下Ice中另外一个重要概念–Replication,即让Object Adapter拥有多个访问地址,其目标是在多个机器上部署相同的Server来实现服务的负载均衡和容错机制,具体如何使用,我们后面再说。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值