2011-11-26

将任务一进行改编,写了一个基于ServiceI框架的服务,并提供Adapter,并用Cmake进行编译,部署,运行,具体分为以下几个方面:

1 设计和实现基于ServiceI服务的服务器端服务

撰写基于ServiceI服务的服务器的头文件和源文件,在/trunk/socialgraph/helloworld/src下面编写服务的头文件和服务实现文件,撰写过程中需要注意以下几点:

1.1 实现服务所需的完整的头文件及头文件直接的相对位置,一般的顺序如下:

首先是C++ STL相关的头文件,常见的有#include<string>, #include <vector>等;

然后是ICE框架的头文件,如#include <Ice/Ice.h>, #include<IceUtil/IceUtil.h>等;

再次是项目基础框架相关的头文件,如#include “Singleton.h”等;

最后是具体服务相关的头文件,如#include “HelloWorld.h”等。

1.2 通常名字空间的顺序与头文件的包含顺序相对应,一般如下:

using namespace std;

using MyUtil;

using namespace xce::socialgraph;

1.3 注意服务器端服务功能的验证,由于所实现的服务功能是在后端,所以基于终端的测试结果的验证不能实现,由ICE框架重定向到tmp目录下的临时文件中,具体的实现机制,听楠哥讲了下,不过尚未彻底明白。。。所以对于输出终端的功能测试只能由打印日志来验证

   基于ServiceI的服务由于封装了IceBox的一些重复工作,创建一个新的服务变得更为简单,只需在Myutil::initialize()中通过新建一个实例:

ServiceI& service = ServiceI::install(),然后调用下面的接口:

Service.getAdapter()->add();

即可新建一个新的服务

2 设计基于AdapterI的客户端代理

class TalkCacheAdapter : public AdapterI,
    public AdapterISingleton<MyUtil::TalkChannel, TalkCacheAdapter> {

public:
    TalkCacheAdapter() {
        _managersOneway.resize(cluster() > 0 ? cluster() : 1);
    }

protected:
    virtual string name() {
        return "TalkCache";
    }
    virtual string endpoints() {
        return "@TalkCache";
    }
    ;
    virtual size_t cluster() {
        return 5;
    }

........


    vector<CacheManagerPrx> _managersOneway;

};

每个客户端代理都要基础MyUtil::AdapterI和MyUtil::AdapterISingleton类,

代理主要包括实例化服务name, endpoints, 服务类型,cluser的数量.其中cluster的分发是通过下面几个数据结构来实现的:

首先定义多个代理序列:

std::vector<HelloWorldPrx> _managers;

然后在代理的构造函数中将每个cluster对应一个代理:

_managers.resize(cluster() > 0 ? cluster() : 1 );

客户端通过每个代理的请求就分别散发到这些cluser上

3 可执行文件的编写,来通过生成代理实例来调用服务器的服务:

4 在cmake中添加相应的服务和代理,然后在编译目录对服务进行编译,这里需要注意以下几点:

4.1 在对cmake中添加新的服务时,需选择正确的服务类型.例如,添加一个新的服务:

ADD_SERVICE("socialgraph/helloworld/src" "HelloWorld" ReplicaCluster OceSliceXceStorm OceSliceReplicaCluster

          OceSliceSubjectObserver IceExt DbCxxPool IceGrid  UtilCxx UtilSlice OceSlice)

添加一个可执行文件(功能为通过调用代理来验证服务器端的服务是否正常启动):

ADD_SERVICE_EXEC("socialgraph/helloworld_exe/src" "HelloWorldexe" OceCxxAdapterHelloWorldAdapter ReplicaCluster

  OceSliceXceStorm OceSliceReplicaCluster OceSliceSubjectObserver IceExt DbCxxPool IceGrid UtilCxx UtilSlice OceSlice)

4.2 注意添加静态库的顺序,例如,由于Ice框架中的模块需要调用数据库,因此,上面的DbCxxPoolIceGrid需要添加在IceExt静态库后面

5 将生成的静态库文件拷贝到测试机上,然后修改测试机上的模板文件进行部署,包括在项目配置文件中include相应的服务配置文件和添加服务的配置项

 

  在 2011-11-26,上午10:50, rong.tang 写道:

师傅:
对于AdapterI还有些疑问(昨天可能你讲过但是过后有些忘了:-[ ),我看好多代
码都是用的ServiceI,

1.那么什么时候该用AdapterI呢,
1、AdapterI和ServiceI是两个完全不同的东西,ServiceI是服务端用的,AdapterI是客户端用的。 2.“实现服务时用ServiceI,实现对外接口用AdapterI” 这样理解对嚒? 2、这个理解是对的
AdapterI的channle配置了object对应的endpoint,可以直接给客户提供ptr,而不需要客户知道相应的地址


3.ServiceI 封装了ICE run time 的初始化,而AdapterI可以将用户发来的任务分 派(cluster) 对吗? 3、需要特别注意的是:我们这里的AdapterI和ice里的Adapter是两个完全不同的概念。
AdapterI就是个客户端,Adapter是用来任务分派的,可以这么理解。

 

 

 

 

1 将任务二用ServiceI和Adapter进行了重写,并将客户端请求散列在10个不同的代理上,发现了以下问题:

1.1 Cmake服务添加时,对于客户端适配器功能的验证时,需要修改相应的adapter的库名称,例如OceCxxAdapterSortArrayAdapter

1.2 需要防止用户自定义函数名与与stl库函数冲突,并在调用STL库函数需要包含相应库函数的头文件,例如STL包含sort算法,如果用户自定义了名为 sort的函数,为了防止编译时相互引用的命名空间之间的冲突,那么对STL的sort算法引用时需要标明STL::sort防止编译错误,此外,还需要 添加相应的标准库头文件#include <algorithm>

1.3 Ice输入输出参数的区别: 对于定义在Ice slice文件中的的需要修改的输出参数需要用out关键字显式标明输出参数,因为ICE的slice文件将接口中的各种形式默认的编译为const类 型,导致接口函数无法作用于输出参数,而添加out关键字后编译后则去掉了const的属性

相应的,在实现客户端适配器的时候的输出参数也应该设置为可以在接口中进行保存修改的结果的引用

void sort(int userId, std::vector<int> inArray, std::vector<int>& outArray);

1.4 对于每一个基于AdapterI的服务,名称的拼接为:

name@endpoints

其中name由name()函数返回,endpoints由endpoints()和由socialgraph.xml文件中的Index拼接而 成,客户端代理首先将uerID mod cluser()之后生成的数字映射成Index值,然后根据ICE box找到相应的开启的代理服务。

对于每一个基于ServiceI的服务,名称的拼接为:

name@endpoints,

其中endpoints为一个具体的服务名称,由具体的business服务名称和一段随机数拼接而成

1.5 定义多个代理序列时,需要注意客户端代理的参数为代理类型:

std::vector<SortArrayPrx> _managers;

2 学习LRU的cache实现 和学习ObjectCache代码

2.1学习LRU的cache实现 /UtilCxx/src/ Evictor.h

Elivctor.h文件主要定义了一个SizedEvictor模板类,TimedEvictor

实现基于list的key-value形式的、分别以list的size和duration time为依据两种LRU算法,其中value为Ice服务器端的servent object,提供了以下接口函数:

SizedEvicor(int size=1000);主要设置list的size大小,即LRU算法的每个桶的容量,默认大小为1000条记录;

locate()函数:判断对象是否在LRU队列中,如果在,则将其重新加载到链表最前面,否则返回直接返回空;

find()函数,找出对象是否在cache中,如果存在则返回对象,否则返回空;

evict()函数,判断cache是否已满,如果满了则逐出最后的一个对象;

2.2 学习ObjectCache代码

trunk/IceExt/src/ObjectCacheI.cpp

trunk/IceExt/src/ObjectCacheI.h

/trunk/UtilSlice/slice/ObjectCache.ice

ObjectCacheI基于IceUtil::Shared和ObjectCache.ice定义了一些cache操作的接口,包括 size(),add(), remove(), preload(),reload(),create()等,提供cache相关的操作服务,首先定义了一个基于IceUtil::Shared的 ObjectBaseMap基类,然后分别定义了ObjectEvitor子类通过继承ObjectBaseMap和基于LRU算法 SizedEvictor模板类来实现cache的逐出(以及ObjectRentention的不逐出服务), 并对LRU的逐出实现又一层key-value封装,形成多个桶的LRU逐出策略。在ObjectCacheI中,还定义了一个”抽象”的 ServantFactory类通过它们的category,id和基于ObjectBaseMap基类指针来生成单个或批量的cache服务对象,各种 不同的应用通过继承此基类来创建具体的实例化对象,然后再调用registerObjectCache来实现cache服务

和楠哥讨论了LRU逐出算法中两层key-map实现中不同的locate的功能区别,为什么在第一次(底层)hash-map时,locate不 到一个对象不添加而在第二层(上层)hasp-map时却直接调用create函数将对象直接添加了进去,主要是考虑到底层的函数设计时,应尽量的保持功 能单一,这样有助于实现底层的功能解耦、支撑上层不同的逻辑实现,同时也有助于上层不同的应用的修改时不需要修改底层和与底层相关的应用,从而实现代码的 健壮性;此外,在ObjectCacheI中设计ObjectBaseMap 基类和ObjectEvitor及ObjectRentention子类的原因也是如此。

posted @ 2011-11-26 11:38  tangr206  阅读(458)  评论(0编辑  收藏  举报