配置解析
nsqd的主函数位于apps/nsqd.go
中的main
函数
首先main
函数调用nsqFlagset
和Parse
进行命令行参数集初始化,
然后判断version
参数是否存在,若存在,则打印版本号并退出程序
接下来钩住系统的syscall.SIGINT
和syscall.SIGTERM
消息,用来阻塞主goroutine防止退出
随后判断config
参数是否存在,若存在的话还需进行配置文件的读取,
nsq使用toml格式的配置文件,并通过github.com/BurntSushi/toml
库进行配置文件的读取和解析
如果配置文件存在并且符合toml格式,则调用cfg.Validate
对配置文件的各项进行进一步的合法性检查。
主要是检查配置文件中有关tls的选项(是否支持以及支持的版本)
配置文件检查通过后,创建默认配置opts
,并于命令行参数和配置文件进行合并。
合并时用到了github.com/mreiferson/go-options
库。
若出现冲突,则优先级从高到低排序依次是命令行、配置文件和默认配置
使用合并后的参数集初始化真正的nsqd对象
最后,nsqd对象进行初始化和检查后,启动nsqd包的主函数,程序从跳转apps/nsqd.go
到nsqd/nsqd.go
初始化
nsqd真正开始运行前需要执行nsqd/nsqd.go
中的LoadMetadata
和PersistMetadata
两个函数
LoadMetadata
初始化nsqd的LoadMetadata
函数使用atomic
包中的方法来保证方法执行前和执行后isLoading
值的改变
元数据以json
格式保存在nsqd可执行文件目录下的nsqd.%d.dat中。其中%d
为代表该程序的ID,
通过在启动时的命令行worker-id
或者配置文件中的id
指定。默认ID是通过对主机名散列后获得。
因此保证了同一台机器每次启动的ID相同。
解析元数据的文件得到系统中的存在的topic列表,遍历topic列表中的每个topic:
- 检查topic名称是否合法(长度在1-64之间,满足正则表达式
^[\.a-zA-Z0-9_-]+(#ephemeral)?$
)
,若不合法则忽略 - 使用
GetTopic
函数通过名字获得topic对象 - 判断当前topic对象是否处于暂停状态,是的话调用
Pause
函数暂停topic - 获取当前topic下所有的channel,并且遍历channel,执行的操作与topic基本一致
- 检查channel名称是否合法(长度在1-64之间,满足正则表达式
^[\.a-zA-Z0-9_-]+(#ephemeral)?$
)
,若不合法则忽略 - 使用
GetChannel
函数通过名字获得channel对象 - 判断当前channel对象是否处于暂停状态,是的话调用
Pause
函数暂停channel
- 检查channel名称是否合法(长度在1-64之间,满足正则表达式
至此,元数据的载入完成
PersistMetadata
PersistMetadata
将当前的topic和channel信息写入nsqd.%d.dat文件中,
主要步骤是忽略#ephemeral
结尾的topic和channel后将topic和channel列表json序列化后写回文件中
写入文件时先创建扩展名为tmp的临时文件,写入内容后并保存后再调用atomicRename
函数将tmp文件重命名为nsqd.%d.dat。
其中atomicRename
函数在windows和其他操作系统下实现方式不同,分别位于nsqd/rename_windows.go
和rename.go
中。在Linux下直接调用了os.Rename
函数,而Windows下则使用Win32 API实现了文件的重命名。
这是因为go的早期版本中Windows下调用os.Rename
函数时如果重命名后的文件已经存在则会失败。
这个bug在os: make Rename atomic on Windows中提到,
并且已经在os: windows Rename should overwrite destination file.提交中被修复,
因此,Golang1.5不存在这一bug
Main
在Main
函数中,nsqd真正开始运行。Main
监听tcp,https(如果设置了相关参数),http端口并通过WaitGroupWrapper
的Wrap
函数以goroutine方式启动主要的组件。
其中WaitGroupWrapper
是对sync.WaitGroup
的简单包装
执行完Main
函数后,配置和初始化工作全部完成,各个组件启动运行,而主goroutine会阻塞在<-signalChan
处,直到收到中断程序的信号,随后执行nsqd.Exit
函数。 Exit
函数将进行socket
关闭等清理工作,随后结束整个程序的运行。