pyinstaller + flask + tensorflow(keras) 打包部署

文章目录

      • 一 、环境准备
      • 二、pyinstaller打包基于tensorflow 2.2.0的程序
      • 三、flask服务请求
      • 四、flask 实现多线程
          • 4.1 gunicorn + flask服务打包
          • 4.2 tornado+ flask服务打包
          • 4.2 gevent+ flask服务打包
      • 五、设置日志输出

一 、环境准备

打包部署环境:CentOS7 + Anaconda3.5.X
下载源后上传至~/目录配置系统源:

#!/usr/bin/bash
echo -e '\033[32m======= start ==========\033[0m'

cp CentOS7-Base-163.repo /etc/yum.repos.d/
cd /etc/yum.repos.d/
mv CentOS-Base.repo CentOS-Base.repo.bak
mv CentOS7-Base-163.repo CentOS-Base.repo
echo
echo "cp CentOS7-Base-163.repo /etc/yum.repos.d/"
echo "cd /etc/yum.repos.d/"
echo "mv CentOS-Base.repo CentOS-Base.repo.bak"
echo "mv CentOS7-Base-163.repo CentOS-Base.repo"
echo
echo -e '\033[32m======= doing...==========\033[0m'

echo "yum clean al"
echo "yum makecache"
echo "yum update"

yum clean all
sleep 1
yum makecache
sleep 1
yum update
sleep 1

echo -e '\033[32m======= done! ==========\033[0m'

关于Anaconda环境安装

#!/usr/bin/bash
echo -e '\033[32m======= first ==========\033[0m'
echo
echo "yum -y install swig gcc gcc-c++ kernel-devel"
echo

yum -y install swig gcc gcc-c++ kernel-devel


echo
echo -e '\033[32m======= second ==========\033[0m'
echo
echo "pip install oscrypto"
echo "pip install paramiko"
echo "pip install pyinstaller"
echo "pip install flask"
echo "pip install flask_restful"
echo "pip install pandas"
echo "pip install endesive"
echo "pip install opencv-python"
echo "pip install pdfplumber"
echo "pip install gunicorn"
echo "pip install gevent"
echo "pip install PyMuPDF==1.17.0"
echo

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple oscrypto
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple paramiko
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask_restful
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple endesive
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pdfplumber
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gunicorn
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gevent
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple PyMuPDF==1.17.0



echo -e '\033[32m======= third ==========\033[0m'
echo "pip install wrapt"
echo "pip install tensorflow & keras"

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade setuptools
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple wrapt --ignore-installed
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow==2.2.0
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple keras
echo
echo -e '\033[32m======== end ==========\033[0m'

二、pyinstaller打包基于tensorflow 2.2.0的程序

参考文章:【Flask作为Web App部署Keras模型】
准备完服务的代码执行 pyinstaller app.py 打包服务。执行服务:/dist/app/app
缺少依赖模块解决方法:
添加hook文件:hook-tensorflow.py

from PyInstaller.utils.hooks import collect_data_files, collect_submodules
hiddenimports = collect_submodules('tensorflow_core.core.framework')
hiddenimports += collect_submodules('tensorflow_core.core')
hiddenimports += collect_submodules('tensorflow.compiler.tf2tensorrt')
hiddenimports += collect_submodules('tensorflow_core')
hiddenimports += collect_submodules('tensorflow.python.keras.engine.base_layer_v1')
hiddenimports += collect_submodules('tensorflow_core.lite.experimental.microfrontend.python.ops')

缺少二进制文件模块解决方法:编辑app.spec文件,在binaries栏添加内容:

binaries = [("/home/`username`/anaconda3/lib/python3.6/site-packages/tensorflow/lite/experimental/microfrontend/python/ops/_audio_microfrontend_op.so",\
"./tensorflow_core/lite/experimental/microfrontend/python/ops")],

打包完,执行程序时可能还会报错:
无法找到动态库 libpython3.6m.so
无法找到动态库tensorflow/lite/experimental/microfrontend/python/ops/_audio_microfrontend_op.so

(可能)需要执行:

mv /home/`username`/path/dist/ocr_server/libpython3.6m.so.1.0 /home/wangsp/ocr_server_files/dist/ocr_server/libpython3.6m.so

mkdir -p /home/`username`/path/dist/ocr_server/tensorflow/lite/experimental/microfrontend/python/ops/
cp /home/`username`/anaconda3/lib/python3.6/site-packages/tensorflow/lite/experimental/microfrontend/python/ops/_audio_microfrontend_op.so /home/`username`/path/dist/ocr_server/tensorflow/lite/experimental/microfrontend/python/ops/_audio_microfrontend_op.so

三、flask服务请求

flask 服务端与客户端示例代码:http://www.suoniao.com/article/23907
flask 服务端支持正则的路由:https://www.cnblogs.com/felixwang2/p/9262464.html

示例:传入json字符串 --server 端

#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
from flask import Flask
from flask import request
from flask import redirect
from flask import jsonify
app = Flask(__name__)

@app.route("/" , methods=["GET", "POST"])
def index():
    if request.method == "POST":
        a = request.get_data()
        dict1 = json.loads(a)
        return json.dumps(dict1["data"])
    else:
        return "

Only post request

"
@app.route("/user/") def user(name): return"

hello, %s

"
% name if __name__ =="__main__": port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)

—client端

#coding=utf-8
import requests
import json


data={ "opr": "add", "data": { "userName": "98997", "disc": "hudihiudhu", "ip": ["10.10.11.1", "10.10.11.2"]}}
data = json.dumps(data)
r = requests.post("http://127.0.0.1:5000/", data)

print(r.status_code)
print(r.headers["content-type"])
print(r.encoding)
print(r.text)

pyinstaller打包后,运行可执行文件报错:TemplateNotFound: index.html
解决方法:在可执行文件中添加

import sys
sys.path.append(os.getcwd())

app\__init__.py文件中添加如下内容:

import sys
import os
from flask import Flask


if getattr(sys, 'frozen', False):
    template_folder = os.path.join(sys._MEIPASS, 'templates')
    app = Flask(__name__, template_folder=template_folder)
else:
    app = Flask(__name__)

【参考文章】

四、flask 实现多线程

在服务实现多线程中找寻了许多方法,这里我主要测试了gunicorntornadogevent 这三种方式。

不同部署方式方法:https://www.jianshu.com/p/e8ee1eed2e50

  • gunicorn方式部署
  • tornado方式部署
  • bjoern部署方式
  • cherrypy部署方式
  • meinheld 方式
  • gevent 部署方式

性能测试:

组合 成功率 总耗时 备注
cherrypy 48% 18s 单进程
tornado 76% 9.5s 单进程
tornado 84% 4.5s 4进程
gevent 84% 6s 单进程
meinheld 84% 3.7s 单进程
bjoern 84% 3.7s 单进程
gunicorn+gevent 84% 4.3s 9进程
gunicorn+meinheld 84% 3.6s 9进程
4.1 gunicorn + flask服务打包

【gunicorn 官网】
flask使用的是Werkzeug来作为它的WSGI server,但是性能很一般,生产环境一般会使用其他的WSGI server, 网上查到有以下WSGI server:

Gunicorn 独角兽,从Ruby的Unicorn移植过来的。
uWSGI 比较全能的一个WSGI server。
CherryPy CherryPy是Python的一个HTTP Framework,然后它也有WSGI server。
可能还有其他的一些WSGI server,对于gunicorn熟悉,那么要使用gunicorn,app.py需添加以下代码:
gunicorn + flask服务:https://www.jianshu.com/p/70a30944fade
使用该方法打包依赖处理,隐藏导入:

hiddenimports=[
    "tensorflow.python.keras.engine.base_layer_v1",\
    "gunicorn.glogging",\
    "gunicorn.workers.sync",\
    "gunicorn.workers.ggevent"],

重写Gunicorn基类服务 【Gunicorn文档】

import multiprocessing,os
import gunicorn.app.base
from flask import Flask, request

app = Flask(__name__)


def number_of_workers():
    return (multiprocessing.cpu_count() * 2) + 1

class StandaloneApplication(gunicorn.app.base.BaseApplication):
    def __init__(self, app, options=None):
        self.options = options or {}
        self.application = app
        super().__init__()

    def load_config(self):
        config = {key: value for key, value in self.options.items()
                  if key in self.cfg.settings and value is not None}
        for key, value in config.items():
            self.cfg.set(key.lower(), value)

    def load(self):
        return self.application


if __name__ == '__main__':
    gunicorn_config = {
    'bind': '%s:%s' % ('127.0.0.1', '8080'),
    "check_config": True,
    "worker_class": "gthread",
    "workers": number_of_workers(),
    "threads": 2,
    'timeout': 60,
    "loglevel": "info",
    "access_log_format": "gunicorn %(h)s - %(t)s - %(r)s - %(s)s - %(f)s",
    "backlog": 30,
    }
    StandaloneApplication(app, options=gunicorn_config).run()
4.2 tornado+ flask服务打包

【tornado官网文档】 【tornado中文文档】 \

在这里查看tornado详情:https://blog.csdn.net/ka_ka314/category_7820903.html
关于日志使用:https://www.cnblogs.com/shijingjing07/p/7670672.html

#!/usr/bin/python
# -*- coding:utf-8 -*-
import os,json
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="-1"

from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from flask import Flask
from flask import request

app = Flask(__name__)

@app.route("/" , methods=["GET", "POST"])
def index():
    if request.method == "POST":
        a = request.get_data()
        dict1 = json.loads(a)
        return json.dumps(dict1["data"])
    else:
        return "

Only post request

"
@app.route("/user/") def user(name): return"

hello, %s

"
% name if __name__ == '__main__': port = int(os.environ.get('PORT', 5000)) http_server = HTTPServer(WSGIContainer(app)) http_server.listen(port) IOLoop.instance().start()
4.2 gevent+ flask服务打包

使用协程的方式启动。【参考文章】
下载地址:pip install gevent https://pypi.org/project/gevent/

#!/usr/bin/python
# -*- coding:utf-8 -*-
import os,json
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="-1"
from gevent import monkey
monkey.patch_all()
from flask import Flask
from flask import request
from gevent.pywsgi import WSGIServer


app = Flask(__name__)


@app.route("/" , methods=["GET", "POST"])
def index():
    if request.method == "POST":
        a = request.get_data()
        dict1 = json.loads(a)
        return json.dumps(dict1["data"])
    else:
        return "

Only post request

"
@app.route("/user/") def user(name): return"

hello, %s

"
% name if __name__ =="__main__": port = int(os.environ.get('PORT', 5000)) http_server = WSGIServer(('0.0.0.0', port), app) http_server.serve_forever()

五、设置日志输出

Python 中的 logging 模块可以让你跟踪代码运行时的事件,当程序崩溃时可以查看日志并且发现是什么引发了错误。Log 信息有内置的层级——调试(debugging)、信息(informational)、警告(warnings)、错误(error)和严重错误(critical)。你也可以在 logging 中包含 traceback 信息。不管是小项目还是大项目,都推荐在 Python 程序中使用 logging。

  1. 基本使用
    配置logging基本的设置,然后在控制台输出日志
import logging
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 使用示例
logger.info("Start log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
  1. 将日志写入到文件
    设置logging,创建一个FileHandler,并对输出消息的格式进行设置,将其添加到logger,然后将日志写入到指定的文件中,
import logging

logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler("log.txt") # 文件路径
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)


# 使用示例
logger.info("Start log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
  1. 将日志同时输出到屏幕和日志文件
    logger中添加StreamHandler,可以将日志输出到屏幕上,
import logging

logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(handler)
logger.addHandler(console)


# 使用示例
logger.info("Start log")
logger.debug("Do something")
logger.warning("Something maybe fail.")

你可能感兴趣的:(python基础及相关,python)