您的位置:首页 > 运维架构

OpenRTMFP/Cumulus Primer(5)CumulusServer启动流程分析(续1)

2012-09-29 17:32 716 查看
作者:柳大·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
的。
Application
run()
函数会先调用
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));
}


OptionSet
Poco::Util::OptionSet
,调用
addOption
可以向其中增加选项
Option
。其中
required
repeatable
表示:

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_SIZE
1000000
字节(即约
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

-
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: