tornado配置解析

tornado的options模块是一个命令行解析模块,此模块不仅可以从命令行解析传递的选项,也能从配置文件解析。以下边的事例来进行说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from tornado.options import options, define
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


define("port", default=8000, type=int, help="listen port")


class MainHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('hello')


if __name__ == '__main__':
app = Application(
[
(r'/', MainHandler)
], debug=True
)
app.listen(port=options.port, address='0.0.0.0')
IOLoop.current().start()

要使用tornado的配置解析功能需要从options模块中引入optionsdefine两个对象,define函数定义全局命名空间的选项,比如上边定义的port选项,后边就可以使用tornado.options.options.port来调用,运行上边的程序可以发现,程序监听在了8000端口,如下:

1
2
3
neal@neal-System-Product-Name:~$ ss -tnlp | grep 8000
LISTEN 0 128 *:8000 *:* users:(("python",pid=18033,fd=6))
neal@neal-System-Product-Name:~$

如果要让程序可以接受命令行传递的参数,那可以这样来改造程序,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tornado.options import options, define
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


define("port", default=8000, type=int, help="listen port")


class MainHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('hello')


if __name__ == '__main__':
options.parse_command_line()
app = Application(
[
(r'/', MainHandler)
], debug=True
)
app.listen(port=options.port, address='0.0.0.0')
IOLoop.current().start()

上边加入了options.parse_command_line()这一行代码,表示解析命令行传递的参数,参数使用_–name=value_的格式传递,试着运行上边的代码,在命令行传递相应的选项,如下:

1
2
3
4
5
6
7
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py --port=8080
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python)
LISTEN 0 128 *:8080 *:* users:(("python",pid=20086,fd=6))

(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python)
LISTEN 0 128 *:8000 *:* users:(("python",pid=20034,fd=6))

程序监听在了8080端口,是从命令行传递过来的,所以可以知道,在define函数中定义的选项,又定义了options.parse_command_line()从命令行捕获选项,那程序运行时传递了相应的选项,那程序就使用这个在命令行传递选项的值,如果程序运行时没有传递相应的选项,那就采用define中定义的默认值。

options模块不仅可以从define和命令行获取选项的值,还可以读取指定一个配置文件,从配置文件读取选项和值,请看下边代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tornado.options import options, define
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


define("port", default=8000, type=int, help="listen port")


class MainHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('hello')


if __name__ == '__main__':
options.parse_config_file('./application.conf')
app = Application(
[
(r'/', MainHandler)
], debug=True
)
app.listen(port=options.port, address='0.0.0.0')
IOLoop.current().start()

applications.conf格式如下:

1
2
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ cat application.conf 
port = 8080

程序运行后可发现程序监听在8080端口,即是从配置文件里读取的配置信息,如下:

1
2
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8080 *:* users:(("python",pid=22126,fd=6))

如果同时定义了从配置文件读取和从命令行读取,那生效的是哪个呢?做如下测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from tornado.options import options, define
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


define("port", default=8000, type=int, help="listen port")


class MainHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('hello')


if __name__ == '__main__':
options.parse_config_file('./application.conf')
app = Application(
[
(r'/', MainHandler)
], debug=True
)
options.parse_command_line()
app.listen(port=options.port, address='0.0.0.0')
IOLoop.current().start()

程序如下运行后查看监听在哪个端口:

1
2
3
4
5
6
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ cat application.conf 
port = 8080

(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py --port=8005
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8005 *:* users:(("python",pid=23810,fd=6))

程序监听在8005端口,如果不在命令行传递参数,那程序会监听在哪个端口?如下:

1
2
3
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python)
LISTEN 0 128 *:8080 *:* users:(("python",pid=24093,fd=6))

程序监听在了8080这个配置在配置文件中的端口。如果把options.parse_command_line()放在options.parse_config_file('./application.conf')之前呢?如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from tornado.options import options, define
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


define("port", default=8000, type=int, help="listen port")


class MainHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('hello')


if __name__ == '__main__':
options.parse_command_line()
options.parse_config_file('./application.conf')
app = Application(
[
(r'/', MainHandler)
], debug=True
)
app.listen(port=options.port, address='0.0.0.0')
IOLoop.current().start()

再运行程序试试,如下:

1
2
3
4
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py --port=8005

(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8080 *:* users:(("python",pid=8777,fd=6))

程序监听在8080这个定义在配置文件中的端口,如果不传递命令行参数呢?

1
2
3
4
gscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py

(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8080 *:* users:(("python",pid=8937,fd=6))

同样监听在8080端口,依然是配置文件中定义的参数,如果把配置文件中的信息清除呢?

1
2
3
4
5
6
7
8
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ echo "" > application.conf 
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py --port=8005
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8005 *:* users:(("python",pid=25126,fd=6))

(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8000 *:* users:(("python",pid=25384,fd=6))

在上边的程序中,当把配置文件中的配置项去除后,如果命令行传递相应选项,那程序会使用命令行传递的参数,如果命令行没有传递相应的选项,那程序会使用defie中定义的默认选项。经过以上的不断测试,可以总结出tornado在使用配置选项时是按照如下规则:

  1. 配置项的读取是根据代码的定义从上往下读取,如:先定义从文件读取,再定义从命令行读取,那程序就会先从文件读取,再从命令行读取;
  2. 如果相同的选项在配置文件中有定义,再在命令行也传递相同的选项,那后一个读取到的选项的值会覆盖前一个值,即最后读取到的值是生效值;
  3. 如果配置文件和命令行都未定义选项的值,那采用define定义的默认值。

而在实际的编程中,从配置文件读取和从命令行读取会结合起来,并且一般会从两个地方读取配置文件,一个一般存放在/etc目录下作为全局配置文件,另一个存放在工程的根目录下,最后再接收命令行传递的参数以便在程序调试时使用。举一个例子来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from tornado.options import options, define
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


define("port", default=8000, type=int, help="listen port")


class MainHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('hello')


if __name__ == '__main__':
import os
if os.path.exists('/etc/app.conf'):
options.parse_config_file('/etc/app.conf')
if os.path.exists('./application.conf'):
options.parse_config_file('./application.conf')
options.parse_command_line()
app = Application(
[
(r'/', MainHandler)
], debug=True
)
app.listen(port=options.port, address='0.0.0.0')
IOLoop.current().start()
1
2
3
4
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ cat /etc/app.conf 
port = 8888
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ cat application.conf
port = 8080

运行程序测试:

1
2
3
4
5
6
7
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py --port=8005
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8005 *:* users:(("python",pid=10291,fd=6))

(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ python options_test.py
(logscan-api) neal@neal-System-Product-Name:~/private/sync/temp/logscan-api/test_scripts$ ss -tnlp | grep python
LISTEN 0 128 *:8080 *:* users:(("python",pid=10404,fd=6))

这也很好的验证了是最后读取到的那个选项生效。

文章目录
|