打包部署环境: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'
参考文章:【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 服务端与客户端示例代码: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__)
【参考文章】
在服务实现多线程中找寻了许多方法,这里我主要测试了gunicorn
,tornado
,gevent
这三种方式。
不同部署方式方法:https://www.jianshu.com/p/e8ee1eed2e50
性能测试:
组合 | 成功率 | 总耗时 | 备注 |
---|---|---|---|
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进程 |
【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()
【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()
使用协程的方式启动。【参考文章】
下载地址: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。
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.")
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.")
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.")