Flask 项目相关汇总

Flask 项目相关技术点汇总

gunicorn方式与apscheduler定时任务若干问题

1. 构建镜像

多进程方式运行python程序,实际只需要在requirements中添加gunicorn模块
在通过dockerfile构建镜像时,通过gunicorn启动项目即可

FROM python:3.6
ENV BUILD_PACKAGES="curl build-essential python3-dev ca-certificates libssl-dev libffi-dev"

ADD . /code
WORKDIR /code

RUN apt-get update && apt-get install -y $BUILD_PACKAGES \
    && pip3 install --no-cache-dir -U -I -r /code/requirements.txt -i https://pypi.douban.com/simple\
    && rm -rf /var/lib/apt/lists/*

EXPOSE 5000

CMD ["python3", "main.py"]
#CMD ["gunicorn", "-w", "3", "-b", "0.0.0.0:5000", "main:app"]

gunicorn下多个apscheduler

但是在flask中,多进程模式下(以及debug模式,会启动一个副进程),如果定义了定时调度任务,那么在项目运行时,定时调度任务会被多个进程重复执行
通过引入socket锁的方式,处理多线程下,多个flask_apscheduler任务问题

try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(("127.0.0.1", 5002))
except socket.error:
    print("!!!scheduler already started, DO NOTHING")
else:
    do something init app 

DEBUG模式下,多个apscheduler处理
这里”WERKZEUG_RUN_MAIN”这个变量如果没有,可能会导致debug模式仍然启动多个apscheduler,需要结合具体环境进行处理,思路就是根据对应的DEBUG特有的值进行判断,只初始化一次apscheduler

if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
    scheduler.init_app(app)

apscheduler继承app上下文

定时任务中,需要继承上下文时,需要把app传入定时任务(该场景主要用于在定时任务中,操作数据库等操作,需要获取app上下文)
在添加job时,将app作为参数传递给定时任务xxx

 scheduler.add_job(func=xxx trigger='interval',id='xx',args=[app], seconds=660)

注意,在项目中引用cache模块时,由于gunicorn内存块是相互隔离的,会导致cache存储的数据是分散的

数据库模型引用

项目中,除了多线程下的调度问题,另外一块就是orm
我的项目中,目录结构:

code
├── 项目名
│   ├── 项目入口文件.py
│   ├── bluepoint.py
│   ├── cache.py
│   ├── autotask.py
│   ├── __init__.py
│   ├── tasks.py
│   └── util.py
├── config.py
├── db
├── db.py
├── dockerfile
├── _init_.py
├── main.py
├── models.py
├── rds.py
├── readme.md
├── requirements.txt
├── static
└── templates

因为项目中引用了redis,mysql以及cache模块,其中db.py定义orm模型
db.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

因为redis使用时,用了多个分区,这里初始化了3个实例,配置文件中用REDIS_URL, REDIS2_URL来定义每个实例的连接
rds.py

from flask_redis import FlaskRedis
redis_client = FlaskRedis(decode_responses=True)
redis_client2 = FlaskRedis(decode_responses=True, config_prefix='REDIS2')

cache模块是写在项目目录内的,这里在多进程时,发现cache使用的内存块是独立的,会导致写入的cache分散在三个进程,读取cache不一致
没有继续研究这个问题,是不是要在初始化应用时写在外层init,最后cache用了外置的redis替代
(当时把gunicorn改回单进程也是ok的)
cache.py

from cacheout import Cache
cache = Cache(maxsize=1024000, ttl=600)

其他

前后端交互

 <body>
    <div class="panel panel-default">
        <!--后端传入的值,通过进行接收,前端可以加入一些逻辑判断 -->
        
          <form action="/demo/view22?arg2=" method="POST">
              <div class="form-group">
                  <label class="col-sm-2 control-label">提示</label>
                  <div class="col-sm-10">
                    <p class="form-control-static"></p>
                  </div>
              </div>
              <div class="form-group">
                <label class="col-sm-2 control-label">选择框</label>
                <!-- 这里选择框和输入框的内容都会通过name,以request.from传到后端,后端通过request.from['name1']获取这些输入的值-->
                <div class="col-sm-10">
                  <select  name='name1'>
                    <option>1</option>
                    <option>2</option>
                    <option>3</option>
                  </select>
                </div>
              </div>
              <div class="form-group">
                <label class="col-sm-2 control-label">输入框</label>
                <div class="col-sm-10">
                  <input type="text" class="form-control" placeholder="Inputxxx" name='name2'>
                </div>
              </div>            
              <button type="submit" class="btn btn-default">提交</button>
          </form>
        

    </div>
    <script src="/static/jquery-2.1.4.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
  </body>

前端submit 与后端的处理

前端button等提交submit调用后端相关视图后,后端处理完成刷新前端页面,引入redirect

return redirect(request.referrer)

后端传参到前端

  return render_template('index.html',
                          A1=a1,
                          B1=b1,
                          C1=c1
                          )

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦