OpenRTMFP/Cumulus Primer(5)CumulusServer啟動流程分析(續1)

鍾超發表於2012-04-14

OpenRTMFP/Cumulus Primer(5)CumulusServer啟動流程分析(續1)

  • 作者:柳大·Poechant(鍾超)
  • 部落格:Blog.CSDN.net/Poechant
  • 郵箱:zhongchao.ustc#gmail.com (# -> @)
  • 日期:April 14th, 2012

1 CumulusServer 是 ServerApplication 的子類

ServerApplication 對其子類有如下要求:

  • Subsystems must be registered in the constructor.
  • All non-trivial initializations must be made in the initialize() method.
  • At the end of the main() method, waitForTerminationRequest() should be called.

2 ServerApplication 是 Application 的子類

Application 對其子類的要求是,如下這些成員函式必須被覆蓋:

  • initialize() (the one-argument, protected variant):上一篇已介紹過。
  • uninitialize():下面會介紹,Application 的 run() 函式會在呼叫 main() 函式後呼叫 uninitialize() 函式。
  • reinitialize()
  • defineOptions():定義命令列啟動選項。
  • handleOption():響應相應的命令列選項。
  • main():

3 反初始化

CumulusServer是繼承ServerApplication的,ServerApplication是繼承Application的。Applicationrun()函式會先呼叫initialize(),然後呼叫main(),最後呼叫uninitialize。最後這個反初始化過程,在CumulusServer就是直接呼叫父類的uninitialize函式。

void uninitialize() {
    ServerApplication::uninitialize();
}

4 命令列選項設定

CumulusServer的命令列選項有:log(l)、dump(d)、cirrus(c)、middle(m)、help(h)。

void defineOptions(OptionSet& options) {
    ServerApplication::defineOptions(options);

設定日誌級別(0 - 8,預設是 6,表示 info 級別)。

    options.addOption(
        Option("log", "l", "Log level argument, must be beetween 0 and 8 : \
            nothing, fatal, critic, error, warn, note, info, debug, trace. \
            Default value is 6 (info), all logs until info level are displayed.")
                .required(false)
                .argument("level")
                .repeatable(false));

其他一些選項:

    options.addOption(
        Option("dump", "d", "Enables packet traces in logs. Optional arguments \
            are 'middle' or 'all' respectively to displays just middle packet \
            process or all packet process. If no argument is given, just outside \
            packet process will be dumped.",false,"middle|all",false)
                .repeatable(false));

    options.addOption(
        Option("cirrus", "c", "Cirrus address to activate a 'man-in-the-middle' \
            developer mode in bypassing flash packets to the official cirrus \
            server of your choice, it's a instable mode to help Cumulus developers, \
            \"p2p.rtmfp.net:10000\" for example. By adding the 'dump' argument, \
            you will able to display Cirrus/Flash packet exchange in your logs \
            (see 'dump' argument).",false,"address",true)
                .repeatable(false));

    options.addOption(
        Option("middle", "m","Enables a 'man-in-the-middle' developer mode \
            between two peers. It's a instable mode to help Cumulus developers. \
            By adding the 'dump' argument, you will able to display Flash/Flash \
            packet exchange in your logs (see 'dump' argument).")
                .repeatable(false));

顯示幫助資訊的選項:

    options.addOption(
        Option("help", "h", "Displays help information about command-line usage.")
            .required(false)
            .repeatable(false));
}

OptionSetPoco::Util::OptionSet,呼叫addOption可以向其中增加選項Option。其中requiredrepeatable表示:

  • Sets whether the option is required (flag == true) or optional (flag == false).
  • Returns true if the option can be specified more than once, or false if at most once.

當需要顯示幫助資訊時,呼叫如下函式:

void displayHelp() {
    HelpFormatter helpFormatter(options());
    helpFormatter.setCommand(commandName());
    helpFormatter.setUsage("OPTIONS");
    helpFormatter.setHeader("CumulusServer, open source RTMFP server");
    helpFormatter.format(cout);
}
  • setCommand(): Sets the command name.
  • setUsage(): Sets the usage string.
  • setHeader(): Sets the header string.
  • format(): Writes the formatted help text to the given stream.

5 處理命令列選項

引數是選項名和選項值。

void handleOption(const std::string& name, const std::string& value) {
    ServerApplication::handleOption(name, value);

如果選項是幫助:

    if (name == "help")
        _helpRequested = true;

如果是cirrus,即該服務的 IP 和埠號,Poco::URI 中有協議名(Scheme)、IP 地址(Host)、埠號(Port)、查詢串(Query)等等。

    else if (name == "cirrus") {
        try {
            URI uri("rtmfp://"+value);
            _pCirrus = new SocketAddress(uri.getHost(),uri.getPort());
            NOTE("Mode 'man in the middle' : the exchange will bypass to '%s'",value.c_str());
        } catch(Exception& ex) {
            ERROR("Mode 'man in the middle' error : %s",ex.message().c_str());
        }

如果選項是dump日誌:

    } else if (name == "dump") {
        if(value == "all")
            Logs::SetDump(Logs::ALL);
        else if(value == "middle")
            Logs::SetDump(Logs::MIDDLE);
        else
            Logs::SetDump(Logs::EXTERNAL);

如果選項是middle

    } else if (name == "middle")
        _middle = true;

如果選項是log,表示設定日誌級別:

    else if (name == "log")
        Logs::SetLevel(atoi(value.c_str()));
}

6 Dump logs

先加一個作用域鎖,然後再向日誌流寫資料。

void dumpHandler(const UInt8* data,UInt32 size) {
    ScopedLock<FastMutex> lock(_logMutex);
    cout.write((const char*)data, size);
    _logStream.write((const char*)data,size);
    manageLogFile();
}

呼叫 manageLogFile,主要做一些日誌大小超出限制的處理。

void manageLogFile() {

先判斷是否超過日誌檔案的大小上線,LOG_SIZE1000000位元組(即約 1 MB)。

    if(_pLogFile->getSize() > LOG_SIZE) {
        _logStream.close();
        int num = 10;

開啟新日誌檔案:

        File file(_logPath + "10");

如果該檔案已經存在,則先刪除:

        if (file.exists())
            file.remove();

        while (--num >= 0) {
            file = _logPath + NumberFormatter::format(num);
            if (file.exists())
                file.renameTo(_logPath + NumberFormatter::format(num + 1));
        }
        _logStream.open(_pLogFile->path(), ios::in | ios::ate);
    }   
}

7 停止執行

CumulusServer繼承了ApplicationKiller,該類中有純虛擬函式kill()需要被實現,於是有:

void kill() {
    terminate();
}

ApplicationKiller的定義在ApplicationKiller.h中,如下:

class ApplicationKiller {
public:
    ApplicationKiller(){}
    virtual ~ApplicationKiller(){}

    virtual void kill()=0;
};

8 載入配置

initialize()函式中呼叫,上一篇已提到過。

void loadConfiguration(const string& path) {
    try {
        ServerApplication::loadConfiguration(path);
    } catch(...) {
    }
}

9 處理日誌

void logHandler(Thread::TID threadId,
                const std::string& threadName,
                Priority priority,
                const char *filePath,
                long line, 
                const char *text) {

作用域鎖:

    ScopedLock<FastMutex> lock(_logMutex);

    Path path(filePath);
    string file;
    if (path.getExtension() == "lua")
        file += path.directory(path.depth()-1) + "/";

如果是命令列互動模式(即不是 daemon 模式):

    if (_isInteractive)
        printf("%s  %s[%ld] %s\n",
            g_logPriorities[priority - 1],
            (file + path.getBaseName()).c_str(),
            line,
            text);

向日志流輸出一句日誌:

    _logStream << DateTimeFormatter::format(LocalDateTime(),"%d/%m %H:%M:%S.%c  ")
            << g_logPriorities[priority-1] 
            << '\t' << threadName 
            << '(' << threadId << ")\t"
            << (file + path.getFileName()) 
            << '[' << line << "]  " 
            << text << std::endl;

    _logStream.flush();

日誌檔案的善後處理(主要處理檔案大小限制可能產生的問題):

    manageLogFile();
}

-

轉載請註明來自柳大的CSDN部落格:Blog.CSDN.net/Poechant

-

相關文章