面向对象,服务器架构,设计模式探讨

来源:互联网 发布:域名所有者查询 编辑:程序博客网 时间:2024/04/29 20:48

        面向对象,服务器架构,设计模式探讨

鹦其鸣声,求其友矣

原著:吕跃强          版权申明:完全属于吕跃强,任何转摘和引用必须指明出处

 

   

n       1 服务器本身的架构

n       2 设计模式,面向对象在服务器开发中的应用

参考:

网络的基础架构:  ACE 

高效的并发服务器结构: apachesession独立性较高)

特殊应用服务器 : darwin 流媒体服务器,

 

1 服务器架构的探讨

1.1迭代vs并发vs混合

迭代:那些对每个事件的处理时间短暂、简单。Session处理不复杂,并且可以分解为多个短暂步骤 ,并且分开处理

并发:session复杂。Session中的阻塞不确定,session上下文复杂。如:对某个中转的session处理,收到read请求,然后与数据库交互,然后与加密机交互,然后向服务器提交请求,读取服务器应答,再加密机交互,数据库,然后向客户端回应。像这种建议用多线程。

混合:平时迭代处理,对消耗大的请求并发处理

 

1.2 多线程VS多进程

n       多线程:效率高,占用资源小。共享地址空间,线程间影响大

n       多进程:效率低,占用资源大。但是安全,稳定性高。

n       Apache 是多进程下面的多线程,整合了效率和安全。

 

1.3 并发分类

n       1.3.1 每个连接pthread_create一次

   就是针对每个事件发生时开一个线程处理,处理完了线程退出。这种模式其实效率在有的系统中还是相当可观的,也就是采用原生线程的平台中。

 

这种模式在一些压力不大的系统中应用还是挺广泛的。而且编成简单。

1.3.2 预创建线程池模式

n       1.3.2.1预创建半同步/半异步(生产者/消费者)模式

    由主线程(生产者线程)倾听事件,然后把事件和参数完一个队列里丢。消费者线程读出队列数据进行处理

    一般说来主线程处理i/o事件并解析然后再往队列丢数据,然后消费者读出数据进行应用逻辑处理。当然,主线程也可以不处理i/o事件,而把句柄等直接往队列里丢,让消费者来处理i/o和应用逻辑。

优点:简化编程将低层的异步I/O和高层同步应用服务分离,且没有降低低层服务性能。集中层间通信。另外,适合相互交互的session

缺点 :需要线程间传输数据,因此而带来的动态内存分配,数据拷贝,语境切换带来开销。高层服务不可能从底层异步服务效率中 获益。

 

n       线程池的调度:可以用操作系统信号量或者条件变量的来,也可以自己管理线程对象,然后由程序具体激活某个线程。

这种模式在很多系统中应用非常广泛

半同步半异步框图

1.3.2.2 预创建领跑者/跟随者模式

n       每次允许一个线程leader等待在事件源集合上。同时其他线程follower 排队等候成为领导者的机会。Leader检测到事件后,马上转入follower并提升一个followerleader,然后直接处理事件这时处于processer角色,处理完成后成为follower

缺点: 实现复杂性和缺乏灵活性

优点:增强了CPU高速缓存相似性,消除了动态内存分配和线程间的数据交换

       需要注意的问题

n       确定句柄类型。UDP并发句柄,TCP socket 为迭代句柄。

n       确定句柄集类型。WaitForMultipleObjects() 是并发的,而select是迭代的

n       事件分离和处理,角色转换。底层操作系统可以将事件在内部排队,直到有leader

 

n       OLAP系统中应用非常广。

1.4 内存池的管理

n       重载new/delete

   譬如在我的CTcpWrRdSocket中,重载了new实现静态内存池

n       参考STL 配置器allocator 建立动态CMemory类。这个工作还没有做

 

1.5 服务器几个部分

n       配置文件处理

Apache的配置文件处理系统非常值得深究。 在我的框架中就一个简单类。建议自己继承一个以支持XML格式的配置

n       日志处理

    (目前我的日志系统是由C库函数实现的。如果你习惯用<<的话也可以重载<<操作符。需要注意的是endl, hex, showbase等流操作子其实函数。没试过带参数的操作子)

n       边界处理socket (类图在包装器模式一节)

n       事件分离 select epoll_wait (类图在包装器模式一节)

n       线程池 (类图在最后)

n       应用逻辑处理

n       另外服务器的某些子系统可进行动态加载  ace apache

 

 

2 面向对象

 

2.1 设计方法:面向对象 vs 面向过程

    一个程序最重要的要求就是能满足需求并且健壮的跑。对于此无论是面向对象和过程都能很好的满足。

 误区:面向对象方法设计出来的软件一定比面向过程的具有更高质量。

误区: 只要学好vc++ 就能精通面向对象的设计方法

    当然以后面对类似的需求,我们希望快速的高质量的搭建新系统。这样对软件的扩张性和维护性就提出了相当的要求,这时面向对象的设计方法就大行其道了。而面向过程就显得吃力。

误区:只要用了类(有的仅仅是类,就连继承都没有,更不用说多态了),就是面向对象了。

其实用了类只是基于对象编程

 

n       面向过程一般按照功能进行分解,最后分解成函数。这样在非常大的软件系统中存在极大的耦合(外部变量,函数耦合),并且难于管理,且扩张性和维护性不高。就是变量名和函数名管理就很头痛。而现实世界中的物体往往是属性和行为的复合体。所以面向过程适合那些以功能操作为主,对扩张性要求不高的系统。

n       Rational统一过程(RUP):起始,细化,构建,移交的不断重复过程。敏捷开发(XP):其实也就是跌代。这里对象技术至关重要,否则原来的代码无法跌代,得打掉重写。上一个周期的跌代仅是验证了可行性。

n       RupXP不是培训几周就可以实现的。

2.2 面向对象的原则

n       开闭原则:一个模块对扩张开放,对修改关闭

n       完全替换原则:派生类可完全替换基类

n       非循环依赖原则:包与包直接切忌循环依赖

n       依赖倒置原则:依赖抽象,而不要依赖与具象

n       优先使用聚合,而不是继承

n       为人写代码:代码风格要统一,简洁

开闭原则和依赖倒置原则解释

 

 

2.3 面向对象设计

2.3.1  类的粒度:共性 VS 个性

n       日志:日志类的分类根据使用的要求,日志可以远程本地, 滚动 按条,有前缀 无前缀分。远程的还可以TCP UDP分。然后除UDP外所有的类都可以再继承实现一个线程安全的。其实还可以再分。就是这样从类图看就已经非常累赘了。

 

n       继承的粒度和减少使用者的分枝代码矛盾把握

n       使用聚合减少类的种类(较大不同时)

n       就使用(属性)开关量减少

共性 VS 个性 类粒度的把握

 

2.3.2 类职责分配

n       通用职责分配软件模式(GRASP):设计原则的总结和概括。

n       9种职责分配模式:专家  创建者 低耦合 高内聚 控制者 多态 纯虚构 中介者 不要和陌生人讲话

2.3.2.1 不要和陌生人讲话

       Class  CInerDataWkThread

       {

       private: 

              Creator * m_ptReactor;

       public:

              ThreadFunc(void)

                {

              if( false==m_bIsStatic )

n         {

n                if( !SetAynCancel() )

n                { return -1; }

n         }

n         while(1)

n         {

n                Wait();

n             //和陌生人CObserver说话了,错误

n             m_ptReactor->GetObserver()->EventProcess(&m_tEventPara);

n            // 只和m_ptReactor说话,由m_ptReactor负责和CObserver说话,正确

n            m_ptReactor->EventNotify();

n         }

n         return 0;

n             }

n       }

2.3.2.2  专家:与封装特性一致

这里m_tQueueTData 暴露在两个类中,导致到处是 Lock UnLock代码

根据专家原则,应该对此单独做一个类使其专门完成此职责

2.4 设计模式:变化之道 vs 永恒之美

n       模式:每个模式描述了一个在我们的环境中不断发生的问题,并且描述了解决该问题的核心方案,这样你就可以多次使用这一解决方案,不必重新劳动。

n       模式的组成:包括名称,解决的问题,方案和使用模式的结果。

n       模式的精髓:1 适应变化 2 针对接口编程,而不是实现编程(其实就是virtual + 继承)3 优先使用聚合而不是继承

2.4.1 模式的分类

n       架构模式:1.1 分层模式 1.2 管道和过滤器 1.3 黑板  2.1 C/S p2p  B/S  3. MVC

n       设计模式:

n       类模式:工厂方法,适配器,模版方法,解释器

n       对象模式: 抽象工厂,原型模式,单件,桥接 复合 装饰 外观 享元 代理 命令 跌代器 中介者 观察者 职责链 策略

2.4.2 包装器模式: 通一接口VS暴露细节

n       也叫适配器(adapter

n       定义: 将一个类的接口或一组API转换成用户希望的另外一个接口。

n       标准的可以以继承或聚合的方式实现,推荐聚合

 

2.4.3  桥接模式

n       一个抽象有多个实现时,通常用继承协调。抽象类定义接口,具体子类则用不同的方式实现,此方法有时不够灵活。继承机制将抽象和实现部分固定在一起。

n       感觉把一一列举变成排列组合,一次一点修改相关全改(举个例子:9个十位数和10个个位数组成90个两位数,不用烈举90个了。我用了10 个数,原来是10-19,现在变为20-29 ,只需1变为2 就可以了)

n       意图:把抽象部分和实现部分分离,使他们可以独立的变化

 

2.5 观察者模式:间接依赖VS直接依赖

n       谁或何时触发更新,目标的删除,观察多个目标,参数推拉模型

n       观察者在服务器中的变体:显示的指定感兴趣的改变 反应器模式

 

直接依赖变成间接依赖

 

2.6单件模式:隐式全局VS显式全局

n       不用释放内存

n       模式介绍:

n       全局变量的头痛

n       自创的多件模式

Class A

{

Public:

     Static A *Instance();

     ~A();

Protected:

     A();

Private:

         Static A* m_pThis;

}

A *A::m_pThis=NULL;

A *A::Instance()

{

If( NULL==m_pThis)

{  m_pThis = new A(); return m_pThis; }

Else { return m_pThis; }

}

自创的多件模式

class CAnonymSem

{

public:

       CAnonymSem();

       ~CAnonymSem();

       static CAnonymSem  *GetFirstSingleton(void);

protected:

       static CAnonymSem *m_pFirst;

       sem_t m_tSem;

};

CAnonymSem *CAnonymSem::m_pFirst=NULL;

CAnonymSem *CAnonymSem::GetFirstSingleton(void)

{

       if( NULL==m_pFirst )

       {

              m_pFirst=new CAnonymSem();

              return m_pFirst;

       }

       else {return m_pFirst; }

}

 

2.7 模版方法:依赖倒置vs正向依赖

好莱坞原则

class CAppServer

{

public:

       void Run(void)

       {

              Daemon();

              SignalInit();

              RealRun();

       }

protected:

       int Daemon();

       int SignalInit();

       virtual int RealRun()

       { cout<<"hello, in CAppServer::RealRun()"<<endl; }

}

class CMyServer:public CAppServer

{

protected:

       int RealRun()

       { cout<<"hello, in CMyServer::RealRun()"<<endl; }

}

main()

{

CAppServer *pMyApp = new CMyServer;

pMyApp->Run();

}

 

3 我的服务器框架类图

 

家,如有建议请发email到以下信箱:

dellme99@163.comdellme99@126.com

 

类图贴不上来,请问如何才能把类图贴上来。

另外,如何才能把源代码贴上来