通过工厂函数封装返回app对象

main.py #主文件

import os

from app import create_app

# 通过环境变量设置项目运行时使用的配置文件,这里就手动设置以下了,一般部署的时候通过脚本等设置。
# os.environ.setdefault("APP_ENV", "dev")

app = create_app(os.environ.get("APP_ENV", "dev"))


@app.route('/')
def all_route():
    """返回所有路由信息"""
    rules = app.url_map.iter_rules()
    print(os.environ.get("APP_ENV"))
    return [i.__repr__() for i in rules]

app/init.py

import os.path
import sys

import grpc
from flask import Flask
from flask_apscheduler.auth import HTTPBasicAuth
from flask_cors import CORS
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from redis.client import Redis
from redis.cluster import RedisCluster, ClusterNode
from sqlalchemy.exc import OperationalError

from app.settings import constants
from app.settings.configs import config_dict


try:
    import pymysql
    pymysql.install_as_MySQLdb()
except:
    pass

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 将common加入到模块搜索路径
sys.path.append(os.path.join(BASE_DIR, "common"))

from utils.db_read_write_separation import MYSession
# db对象
# 调用session_options传递自己的Session类,实现读写分离
# db = SQLAlchemy(session_options={"class_": MYSession, 'twophase': True})
# 开启二阶段提交,但是全部session都会启动xa,性能有所影响
db = SQLAlchemy(session_options={"class_": MYSession})

## REDIS
# 暴露redis引用
# 因为flask-app是延迟加载,而我们需要将redis的配置信息从flask-app的config中读取,所以先给None,
# redis_cli: Redis = None
redis_cluster: RedisCluster = None

## Flask-APScheduler
# Flask-APScheduler 别人基于APScheduler封装,方便Flask中使用
from flask_apscheduler import APScheduler
# 先初始化,然后在create app工厂函数里面再执行init_app() ,这点和Flask-SQLAlchemy一样...
scheduler = APScheduler()

## RPC调用相关
#### rpc  ####
recommend_rpc: grpc.Channel = None

# 创建flask app对象函数
def create_flask_app(env):
    """
    创建app对象并返回
    :param env: 加载指定环境的配置文件
    :return: app实例
    """
    app = Flask(__name__)
    # 根据传递进来的env获取对象类型的配置文件并加载
    app.config.from_object(config_dict[env])

    # 然后再从环境变量加载覆盖配置(可以用于覆盖配置、或则迷惑别人...比如SECRET_KEY,你并不想别人知道,你就可以用这种方式。)
    # 从环境变量指向的配置文件中读取的配置信息会覆盖掉从配置对象中加载的同名参数
    # slient : 如果是True则表示即便没有这个环境遍历,也不会抛出异常。默认为False
    app.config.from_envvar(constants.EXTRA_CONFIG_FROM_ENV_NAME,
                           silent=True)  # SECRET_CONFIG变量设置为一个配置文件的相对或者绝对路径,文件里面的配置项是k=v

    # # 返回json设置相关:
    # # 需要注意的是json默认是会对中文进行ascii编码,需要再flask的配置中设置
    # # flask 2.2之前:
    # # app.config["JSON_AS_ASCII"] = False
    # # flask 2.2之后。原因:2.2提供了一个JsonProvider : https://github.com/pallets/flask/pull/4692
    # # 拓展:使用orjson,这个json库比较好的样子?有空看看。https://www.jb51.net/article/250451.htm
    # app.json.ensure_ascii = False
    # 这里我再定义一个自定义配置项来设置
    app.json_provider_class.ensure_ascii = app.config.get("ADV_FLASK_ENSURE_ASCII", True)

    return app


def create_app(env):
    # 拿到flask的app对象
    app = create_flask_app(env)

    # 注册扩展组件
    register_extra(app)

    # 注册url参数转换器
    from utils.converters import register_converters
    register_converters(app)

    # 统一注册蓝图
    register_blueprint(app)

    # 统一异常处理
    from utils import exceptions
    app.errorhandler(exceptions.DBException)(exceptions.database_error)
    app.errorhandler(OperationalError)(exceptions.operational_error)

    return app


def register_extra(app):
    """
    将一些额外的初始化统一放到该方法中
    :param app:
    :return:
    """

    # 初始化数据库
    db.init_app(app)

    # 初始化redis
    # global redis_cli
    # redis_cli = Redis(
    #     host=app.config["REDIS_HOST"],
    #     port=app.config["REDIS_PORT"],
    #     db=app.config["REDIS_SELECT_DB"],
    #     decode_responses=True
    # )

    nodes_config = app.config["REDIS_CLUSTER_NODES"]
    # 现在新版要求传的是ClusterNode对象了,所以读取出配置的host和port,然后创建对象传参进去
    nodes = []
    for node in nodes_config:
        nodes.append(ClusterNode(node["host"], node["port"]))

    global redis_cluster
    redis_cluster = RedisCluster(startup_nodes=nodes, decode_responses=True, socket_connect_timeout=5)

    # 数据迁移
    Migrate(app, db)
    # 导入模型类【一定要记得!】
    from app.modules.users.models import User, Relation
    from app.modules.channels.models import UserChannel, Channel
    from app.modules.articles.models import Article, ArticleContent, Comment


    # 请求钩子注入
    # 每次请求进来之前,判断是否有token,并校验token
    # 将token中的用户id、是否刷新token标志位写入到g对象中,方便后续视图类、视图函数判断是否有登录。
    from utils.middlewares import get_userid
    app.before_request(get_userid)


    ### 跨域, flask-cors ###
    cors = CORS(app)

    # Flask-APScheduler
    scheduler.init_app(app)
    # https://github.com/viniciuschiele/flask-apscheduler/blob/master/examples/auth.py
    # scheduler.auth = HTTPBasicAuth()  # 进入flask-apscheduler提供的api管理视图认证方式类
    # scheduler.authenticate(lambda x: True)  # 授权方法,只要方法中返回true就是认证通过
    # 导入任务
    from .tasks import tasks
    # 为了防止任务中可能会需要用到FLASK上下文,所以还是用app.app_context()
    with app.app_context():
        scheduler.start()

    ### gRPC ###
    global recommend_rpc
    recommend_rpc = grpc.insecure_channel(app.config["RPC_SERVERS"]["recommend"])

    # 其他的rpc.....以此类推...
    # global video_rpc
    # video_rpc = grpc.insecure_channel(app.config["RPC_SERVERS"]["video"])



def register_blueprint(app):
    """将注册蓝图的操作统一方法这个函数中"""
    from app.modules.users import user_blueprint
    from app.modules.verify import verify_blueprint
    from app.modules.channels import channels_blueprint
    from app.modules.articles import article_blueprint
    # url_prefix可以在app注册蓝图的时候写,也可以在蓝图对象实例中指定,如:user_blueprint = Blueprint("users", __name__,url_prefix="/user")
    # 如果同时指定,以app.register_blueprint方法中指定的为准
    app.register_blueprint(user_blueprint, url_prefix="/user")
    app.register_blueprint(verify_blueprint, url_prefix="/verify")
    app.register_blueprint(channels_blueprint, url_prefix="/channels")
    app.register_blueprint(article_blueprint, url_prefix="/articles")

settings/configs.py

class DefaultConfig:
    # 使用flask中的session要配置SECRET_KEY
    SECRET_KEY = "sdDSFGy981^&*(-31wzz-c0546511"

    # 这个变量是自定义的,用来确保Flask默认的response返回jsop字符串确保不经过ascii编码
    ADV_FLASK_ENSURE_ASCII = False

    # restful-json返回中文不经过ascii编码
    RESTFUL_JSON = {
        "ensure_ascii": False
    }

    ### SQLALCHEMY配置相关 ###
    # app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
    SQLALCHEMY_DATABASE_URI = "mysql://root:root@127.0.0.1:3306/toutiao"

    SQLALCHEMY_BINDS = {
        # 一般主库和从库对外访问也是使用高可用的方案,所以一般会访问VIP虚拟IP,所以一般一个就够了
        "master": "mysql://root:root@127.0.0.1:3306/toutiao",
        "slave": "mysql://root:root@127.0.0.1:3306/toutiao",
    }
    # 显示ORM执行的SQL语句
    SQLALCHEMY_ECHO = False

    # 如果启用,将记录请求期间每个查询的信息。使用get_recorded_queries()获取请求期间发出的查询列表。
    # 用于分析慢查询
    SQLALCHEMY_RECORD_QUERIES = False

    # 跟踪,关闭它,不然有性能影响
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    ### JWT配置相关 ###
    # JWT SECRET KEY
    JWT_SECRET_KEY = 'xiuagTFxgx..ADFEaiuxdbjas$#$1%$$&8-12du91234562'


    ### 七牛云对象存储OSS ###
    QINIU_ACCESS_KEY = "PTXRkpavqT1AZVgyYDMXNqG0021DzZJwQsGEZVMM"
    QINIU_SECRET_KEY = "DhlG5zdhj0Cxkicaf4G_udkMY5wgqUYLHI-3kMFM"
    QINIU_BASEURL = "http://rvdfduarm.hn-bkt.clouddn.com/"
    QINIU_BUCKET_NAME = "juelian-bbs-img"  # 要上传的空间


    ### 跨越设置 ###
    # 让flask-cors直接读取配置文件。
    CORS_ORIGINS = [
        "http://127.0.0.1:8080",
        "https://127.0.0.1:8080",
        "http://127.0.0.1:5000",
        "https://127.0.0.1:5000",
    ]
    CORS_MAX_AGE = 86400
    CORS_SUPPORTS_CREDENTIALS = True


    #### APScheduler ####
    # 这个和flask中的jsonify()函数有关,但是不知道为啥默认应该就是True的,FLASK-APScheduler会找不到,所以手动设置一下。。
    JSONIFY_PRETTYPRINT_REGULAR = True

    # 开启API查询功能,给你提供了很多接口,是Flask-APScheduler额外提供的功能
    SCHEDULER_API_ENABLED = True
    # api接口的前缀,默认是/schedule
    # SCHEDULER_API_PREFIX: str(default: "/scheduler")
    SCHEDULER_API_PREFIX = "/apscheduler"
    # SCHEDULER_ENDPOINT_PREFIX: str(default: "scheduler.")
    # 允许访问的主机
    # SCHEDULER_ALLOWED_HOSTS: list(default: ["*"])



    #### RPC 服务器 ####
    RPC_SERVERS = {
        "recommend": "127.0.0.1:7888",
        "video": "127.0.0.1:8888",
        "news": "127.0.0.1:9888",
    }


class DevConfig(DefaultConfig):
    DEBUG = True

    # 显示ORM执行的SQL语句
    SQLALCHEMY_ECHO = True

    # 如果启用,将记录请求期间每个查询的信息。使用get_recorded_queries()获取请求期间发出的查询列表。
    # 用于分析慢查询
    SQLALCHEMY_RECORD_QUERIES = True

    # REDIS信息
    REDIS_HOST = "10.0.1.140"
    REDIS_PORT = 30817
    REDIS_SELECT_DB = 0

    # 定义redis集群的Node节点信息
    # 注意不一定要全部集群的节点信息都放在这里,因为RedisCluster只要求集群中其中一个节点即可!
    REDIS_CLUSTER_NODES = [
        {"host": "192.168.2.6", "port": 7000},
        {"host": "192.168.2.6", "port": 7001},
        {"host": "192.168.2.6", "port": 7002},
    ]


class ProductConfig(DefaultConfig):
    DEBUG = False

    # REDIS信息
    REDIS_HOST = "10.0.1.140"
    REDIS_PORT = 30817
    REDIS_SELECT_DB = 0
    pass


class TestConfig(DefaultConfig):
    DEBUG = True


config_dict = {
    "dev": DevConfig,
    "prod": ProductConfig,
    "test": TestConfig,
}

posted @ 2023-10-04 12:20  蕝戀  阅读(32)  评论(0编辑  收藏  举报