我们的Tornado项目结构

2019-03-01 14:28:43 浏览数 (1)

Tornado项目结构

之前答应过群里几个同学要晒下我们的Tornado项目结构,后来就忘了。。。今天晒出来。

无论是Tornado项目还是Django的项目,大体结构都是一样的。最外层是工程结构,包含了配置、文档、打包等信息,当然还有源码。

项目结构大体都是这样:

代码语言:javascript复制
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

最终目录结构

代码语言:javascript复制
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的示例代码如下:

代码语言:javascript复制
#!/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不去修改线上的加载路径,目的是避免有人去按照常识去修改包目录之后产生不可预知的后果。

0 人点赞