ICE通信之IceBox服务器

 

一、 简介

IceBox服务器用于配置服务、并把对它们的管理集中在一起,服务被开发成可动态加载的组件,通过属性为IceBox服务配置它负责加载和管理的应用特有的服务。要把多个服务组合成一个应用,可以通过配置、而不是编译和链接来完成。这解除了服务和服务器的耦合,允许你按照需要组合服务或分离服务。

二、 服务管理器

除了应用服务所支持的对象,IceBox服务器还支持一个实现了IceBox.ServiceManager接口的管理对象。这个对象负责加载和初始化服务,并执行客户所要求的管理动作。为了保护这个对象的端点,不让它们受到未经授权的访问,IceBox服务器还会为这个对象创建一个对象适配器。目前,ServiceManager接口的管理能力相当有限:该接口只支持shutdown,用于终止服务、关闭IceBox服务器。

三、 端点配置

服务管理器的对象适配器的端点是用IceBox.ServiceManager.Endpoints配置属性定义的:IceBox.ServiceManager.Endpoints=tcp -p 10000因为有恶意的客户可能会向服务管理器发动 “拒绝服务”攻击,明智的做法是使用SSL端点,或用适当的防火墙配置来保护这些端点,或者两者都使用。

四、 客户配置

需要访问服务管理器的管理功能的客户可以使用属性所定义的端点来创建代理。服务管理器对象的缺省标识是ServiceManager,但你可以使用IceBox.ServiceManager.Identity属性来改变它。因此,使用缺省标识和端点的代理可以构造如下:

ServiceManager.Proxy=ServiceManager:tcp -p 10000

五、 开发服务

要编写IceBox服务,需要实现某个IceBox服务接口。下面给出的Java例子实现了IceBox.Service

module IceBox {
	local interface ServiceBase {
		void stop();
	};
	local interface Service extends ServiceBase {
		void start(string name,Ice.Communicator communicator,Ice.StringSeq args)throws FailureException;
	};
};

你可以看到,服务只需实现两个操作:startstop。这些操作由服务管理器调用;start在服务加载后被调用,而stop在服务关闭时被调用。服务可以在start操作中初始化自身;这通常包括创建对象适配器和servantname和args参数提供了来自服务的配置的信息,而communicator参数是服务管理器为供服务使用而创建的Ice.Communicator对象。取决于服务的配置,这个通信器实例可能会由同一个IceBox服务器中的其他服务共享,因此,你需要注意确保像对象适配器这样的对象的名字是唯一的。

stop操作必须回收服务所使用的任何资源。一般而言,服务会解除其对象适配器的激活,可能还需要调用对象适配器的waitForDeactivate,以确保在继续进行清理之前,所有待处理的请求都已完成。服务管理器会负责销毁通信器缺省的行为是为每个服务创建一个新通信器。

由于下面的原因,这些接口被声明为local接口:它们代表的是服务管理器和服务之间的合约,而不是要供远地客户使用。服务与远地客户进行的任何交互都要通过服务所创建的servant来完成。

myeclipse中创建一个java工程IceboxDemo,在工程下创建包icebox.client(存放客户端文件)、icebox.demo.slice2java(存放由slice2java命令编译生成的文件)、icebox.servant(存放servant文件)、icebox.service(存放服务)

编写Hello.ice文件,将Hello.ice文件直接存放src目录下。

#ifndef HELLO_ICE
#define HELLO_ICE
[["java:package:icebox.demo"]]
module slice2java
{
	interface Hello
	{
		void sayHello();
	};
};
#endif

打开终端,进入Hello.ice文件所在的目录,使用命令slice2java编译Hello.ice文件。

编译之后的生成的文件存放到IceboxDemo项目下的包icebox.demo.slice2java中。

六、 编写servant

Servant类名为HelloI,只是简单的实现“Hello World!”的打印功能。

package icebox.servant;

import icebox.demo.slice2java._HelloDisp;

public class HelloI extends _HelloDisp
{
    public void sayHello(Ice.Current current)
    {
        System.out.println("Hello World!");
    }
}

七、 Java实现服务

我们的服务的类定义如下,存放在icebox.service包中。

package icebox.service;

import icebox.servant.HelloI;

public class HelloServiceI extends Ice.LocalObjectImpl implements
		IceBox.Service {
	public void start(String name, Ice.Communicator communicator, String[] args) {
		for(int i = 0;i<args.length;i++){
			System.out.println("args["+i+"]="+args[i]);
		}
		_adapter = communicator.createObjectAdapter(name);
		_adapter.add(new HelloI(), communicator.stringToIdentity("Hello"));
		_adapter.activate();
	}

	public void stop() {
		_adapter.deactivate();
	}

	private Ice.ObjectAdapter _adapter;
}

首先要注意,我们的类扩展了Ice.LocalObjectImpl。这是Ice开发者必须实现本地接口的极少数情况之一(其他常见的情况是对象工厂和servant定位器)。start方法用和服务名相同的名字创建一个对象适配器,激活一个类型为HelloIservant,并激活对象适配器。stop简单地解除对象适配器的激活。

服务管理器要求服务的实现拥有缺省构造器。这是Java IceBox服务的进入点;也就是说,服务管理器会动态加载服务实现类,并调用构造器来获取服务的实例。

八、 编写客户端

客户端类名为Client,存放在icebox.client包中。

package icebox.client;

import icebox.demo.slice2java.*;

public class Client {
    public static void main(String[] args) {
        int status = 0;
        Ice.Communicator ic = null;
        try {
            //初使化
            ic = Ice.Util.initialize(args);
            //传入远程服务单元的名称、网络协议、IP及端口,获取Hello的远程代理,这里使用的stringToProxy方式
            Ice.ObjectPrx base = ic.stringToProxy("Hello:tcp -p 10000");
            //通过checkedCast向下转换,获取Hello接口的远程,并同时检测根据传入的名称获取的服务单元是否Hello的代理接口,如果不是则返回null对象
            HelloPrx printer = HelloPrxHelper.checkedCast(base);
            if (printer == null) throw new Error("Invalid proxy");
            //调用服务端HelloI打印出来,因为这个方法最终会在服务端上执行
            printer.sayHello();
        } catch (Ice.LocalException e) {
            e.printStackTrace();
            status = 1;
        } catch (Exception e) {
            System.err.println(e.getMessage());
            status = 1;
        }
        if (ic != null) {
            // Clean up
            //
            try {
                ic.destroy();
            } catch (Exception e) {
                System.err.println(e.getMessage());
                status = 1;
            }
        }
        System.exit(status);
    }
}


九、 配置服务器

要把服务配置进IceBox服务器中,只需使用一个属性。这个属性的用途有好几个:它定义服务的名字,它向服务管理器提供服务进入点,它还定义用于服务的属性和参数。下面是这个属性的格式:

IceBox.Service.name=entry_point [args]

属性键的name部分是服务名。这个名字会传给服务的start操作,在配置进同一个IceBox服务器的所有服务中必须是唯一的。用不同的名字加载同一个服务的两个或多个实例是可能的,尽管你很少需要这么做。属性值的第一个参数用于指定进入点。

对于Java服务,进入点就是服务实现类的完整类名 (包括任何package)。这个类必须位于IceBox服务器的类路径上。跟在entry_point后面的任何参数都会被检查。如果某个参数的形式是--name=value,它就会被解释为属性定义,将会出现在传给服务的start操作的通信器的属性集中。这些参数将被移除,剩下的参数会放在args参数中传给start操作。

将配置文件直接存放在src目录下,文件名为config.icebox,完整的配置文件内容如下:

IceBox.InstanceName=DemoIceBox

# The IceBox server endpoint configuration
IceBox.ServiceManager.Endpoints=tcp -p 9998

# The hello service

#创建一个名为Hello的服务,这个服务应当位于icebox.service包中的HelloServiceI类

IceBox.Service.Hello=icebox.service.HelloServiceI --Ice.Trace.Network=1 hello there

# The server creates one single object adapter with the name of
# the service. The following line sets the endpoints for this
# adapter.
Hello.Endpoints=tcp -p 10000:udp -p 10000
# Warn about connection exceptions
#
Ice.Warn.Connections=1

IceBox.Service.Hello=icebox.service.HelloServiceI --Ice.Trace.Network=1 hello there使用这个配置,将会创建一个名为Hello的服务。这个服务应当位于icebox.service.HelloServiceI类中。参数--Ice.Trace.Network=1(网络跟踪级别,1代表跟踪连接的建立和关闭)被转换成属性定义,交给start方法中的Communicator通信器处理,参数hellothere则会成为传给start方法的args序列参数中的两个元素,参数在此文章的例子中没有作用。

完成的目录结构如下:

十、 共享一个通信器

你可以用下面的属性对服务管理器进行配置,让它的所有服务共享一个

通信器实例:IceBox.UseSharedCommunicator=1

如果没有定义这个属性,缺省的行为是为每个服务创建一个新通信器。但是,如果你想要对服务进行并置优化,就必须使用一个共享的通信器实例。

十一、 加载服务

在缺省情况下,服务管理器加载服务的次序是不确定的,也就是说,同一个IceBox服务器中的各个服务不应该相互依赖。如果多个服务必须按照特定的次序加载,可以使用IceBox.LoadOrder属性:

IceBox.LoadOrder=Service1,Service2

在这个例子中,Service1首先加载,接着是Service2。剩下的其他服务在Service2之后以不确定的次序加载。在IceBox.LoadOrder中提到的每个服务都必须有相匹配的IceBox.Service属性。

十二、 运行客户和服务器

1.1. 运行IceBox服务器

把我们在前面讨论的内容结合在一起,我们现在可以配置并启动IceBox服务器了。

进入点的指定:

IceBox.ServiceManager.Endpoints=tcp -p 9998

IceBox.Service.Hello= icebox.service.HelloServiceI

Hello.Endpoints=tcp -p 10000

我们已经将这些属性写入config.icebox的配置文件中,我们可以使用类似这样命令启动Java IceBox服务器:

$ java IceBox.Server --Ice.Config= config.icebox

IceBox.Server是IceBox服务器的主类,该类位于Ice.jar包中,所以执行该命令时,classpath环境变量必须指定Ice.jar包所在位置。

config.icebox是启动IceBox服务器所需要的配置,我们已经存放在java工程IceboxDemo的src目录下。

1、首先打开一个命令行终端,进入config.icebox配置文件存放的目录下,如下图:

此处的路径并不是我们前面提到IceboxDemo工程的src目录下,而是myeclipse编译源文件之后的class文件所在的bin目录下,bin目录下存放的是项目所有的class文件和配置文件。

2、执行以下命令

java -classpath .;F:\lhy_workspace\IceboxDemo\bin\Ice.jar IceBox.Server --Ice.Config=config.icebox

执行完成以后,如下图所示:

以上大部分信息都是网络跟踪日志信息。

1.2. 运行客户端

myeclipse中运行Client客户端,

终端打印出“Hello World!信息。

 

    参考《Ice分布式程序设计》马维达 译.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值