伺服器端軟體的複雜度相對比較高。30000多行代碼的中介軟體軟體的主線程程式又是怎樣的呢?
小雞射手在實際工作中設計的網路服務的主線程代碼如下:
IContext& context = ContextManager::GetContext();
IEventHandler& handler = EventHandlerManager::GetEventHandler();
while(!IsStopped())
...{
IEvent* event = context.eventQueue.GetEvent(context.configSetting.eventQueueTimeout);
if(event==0) continue;
...{
const auto_ptr<IEvent> pEvent(event);
try
...{
event->Handle(handler, context);
}
catch(const exception& ex)
...{
context.logger.Error("Dispatcher中發現異常:[%s]。", ex.what());
}
catch(...)
...{
context.logger.Error("Dispatcher中發現未知異常。EventID is [%s]", event->GetInfo());
}
}
}
這段代碼應該還是比較好理解的,eventQueue採用stl queue加上線程同步機制實現。其中涉及到的兩個關鍵介面定義如下:
class IEvent ...{
public:
virtual ~IEvent() ...{}
virtual void Handle(IEventHandler& handler, IContext& context)=0;
virtual const char* GetInfo()=0;
};
class IEventHandler ...{
public:
virtual ~IEventHandler() ...{}
virtual void Handle(ThisEvent& event, IContext& context)...{}
virtual void Handle(ThatEvent& event, IContext& context)...{}
virtual void Handle(OtherEvent& event, IContext& context)...{}
}
設計的靈感來自於:
- Windows的訊息機制。早期的Windows開發主程式就是GetMessage和switch迴圈,所以以上程式只是對此思想的Object Oriented版本;
- Think in Java對垃圾分類的討論,垃圾分類問題可以通過Visitor模式解決。下面就是採用該模式實現的具體event類的Handle方法。應用邏輯則是在IEventHandler的子類中實現。
void ThisEvent::Handle(IEventHandler& handler, IContext& context)
...{
handler.Handle(*this, context);
}
該設計的優點是:
- 儘管伺服器端的軟體往往是多線程的,需要線程間同步。但是由於該設計的處理均在主線程上完成,故eventHandler中不需要(或者減少了大量)同步;
- 軟體擴充容易,只要加event和對應的eventhandler;
- code on interface,代碼介面和實現分離,易於維護。
當然,該設計也有需要注意的地方:
- eventHandler中的操作不允許blocking,所以類似寫日誌這樣的操作實際上是放到後台線程完成的。 其中的原因和介面處理常式中長時間操作會導致介面沒有反應一樣;
- 理論上,該設計在多CPU的機器上,可能不能完全發揮硬體的潛力。
總之,這還是不錯的設計。小雞射手已經成功將它運用到公司的兩大產品中。