Tornado項目結構
之前答應過羣裏幾個同學要曬下我們的Tornado項目結構,後來就忘了。。。今天曬出來。
無論是Tornado項目還是Django的項目,大體結構都是一樣的。最外層是工程結構,包含了配置、文檔、打包等信息,當然還有源碼。
項目結構大體都是這樣:
project
- conf/
- docs/
- src/
- package1/
- __init__.py
- models.py
- manager.py
- server.py
- setup.py
- MANIFEST.in
對於項目來說,只考慮兩個問題,第一:本地開發是否方便;第二:線上部署是否方便。
開發方便
Django的./manage.py runserver
的方式對於本地開發調試就很方便,所以對於Tornado項目來說,也需要有一個類似的機制可以方便的在開發環境中啓動項目。
部署方便
因爲我們是採用標準的PyPi包分發的方式部署的項目,所有項目文件最終都會落到site-packages
中,所以包目錄的規劃就是個問題。
比如像Django那樣,把所有的App作爲獨立的包分散到site-packages
中,還是把源碼目錄"src"作爲獨立的包放到site-packages
中。
兩種不同的方式,在啓動時也有所差別,因爲包的路徑是不一樣的。這裏不討論哪種方式更合理,我們只說實際的使用情況。
所以部署方便的點在於,我把包放到site-packages
中後是否能方便的啓動項目。這意味着包的結構需要兼容本地啓動和線上啓動。
本地和線上的差別
所以就扯到另外一個問題,本地啓動項目時,你當前腳本所在的目錄就是默認的包根目錄,也就是在sys.path
中會加入當前文件所在目錄,也就是上面結構中的project/src
。你要引用package1下的模塊,直接from package1.models import xxxx
即可。
而在線上的情況是,源碼目錄是作爲獨立包放在site-packages
中的。你要引用的話需要from src.package1.models import xxx
。
這種本地和線上不同引用的問題在Django中是沒有的,除非你調整了Django的結構。
問題解決
包的依賴路徑問題,基本上都可以通過sys.path.insert(<path>)
來解決。
兩種解決的方式,一個是改線上的sys.path
,一個是改本地的。線上的改動只需要在項目加載時把src目錄先insert
到sys.path中,作爲一個新的根路徑。
另外一個就是改本地的啓動命令,把src所在的目錄加到sys.path
中。
從個人的習慣來說,能調整本地邏輯的就不去修改部署環境的邏輯,讓其按照默認的方式處理是最穩當的方式。畢竟線上掛了影響很大。
所以我會在本地啓動項目是做上面的處理,同時所有包的引用觸發是同包的情況下,否則都需要從src開始,也就是:from src.package1.models import xx
。
最終目錄結構
project
├── MANIFEST.in
├── README.md
├── conf
│ └── supervisord.conf
├── fabfile
│ ├── __init__.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── project_src
├── __init__.py
├── handlers
│ ├── __init__.py
│ ├── base.py
│ ├── index.py
├── server.py
├── settings
│ ├── __init__.py
│ ├── base.py
│ └── develop.py
├── templates
│ └── base.html
├── url.py
└── models
├── __init__.py
├── model1.py
└── model2.py
其中一server.py的示例代碼如下:
#!/usr/bin/env python
import os
import sys
from logging.config import dictConfig
from importlib import import_module
import click
import tornado.ioloop
import tornado.web
from raven.contrib.tornado import AsyncSentryClient
def make_app(debug=None):
from project_src.url import urls
assert isinstance(urls, (list, tuple)), 'urls must be list or tuple'
return tornado.web.Application(urls, debug=debug)
def init_log():
dictConfig(tornado.settings.LOGGING)
@click.command()
@click.option('--port', default=9090)
@click.option('--profile', default='develop')
def serve(port, profile):
settings = import_module(f'project_src.settings.{profile}')
tornado.settings = settings
init_log()
app = make_app(settings.DEBUG)
app.listen(port)
app.sentry_client = AsyncSentryClient(
'<sentry>'
)
sys.stdout.write(f"Start server at:http://0.0.0.0:{port} \nProfile: {profile}\n")
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
current_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, current_path)
serve()
最後
其實無論哪種方式,只要能夠保證大家保有共識就可以,比如the5fire不去修改線上的加載路徑,目的是避免有人去按照常識去修改包目錄之後產生不可預知的後果。
硬廣
目前《Django源碼解析視頻版》穩步更新中,已經更新12期,主體內容:
- 第一章 Django項目結構
- 1.1 Django功能概覽
- 1.2 搭建源碼閱讀環境
- 1.3 Django源碼目錄介紹
- 第二章 安裝和創建Django項目
- 2.1 安裝Django時發生了什麼?
- 2.2 創建一個Django項目
- 2.3 bash上的自動補全
- 第三章 步入Django的世界
- 3.1 runserver時發生了什麼-概述