我們的Tornado項目結構

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時發生了什麼-概述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章