方案(一)Python3.6+pyinstaller+windows服务

一、Python3.6(64位)环境清单

  • Django==1.11.7
  • django-windows-tools==0.2
  • PyInstaller==3.3

主要的工具包就这些,如果其中还需其他依赖包,可再进行安装。

二、创建Helloworld

2.1、创建django工程示例

E:\>django-admin startproject helloworld
E:\helloworld>python manage.py startapp hello

  若还有数据库,请按django正常流程进行数据库操作,更多详细操作可参见自强学堂的Django 基本命令

  然后就是尝试启动,看是否启动成功。

E:\helloworld>python manage.py runserver 8800

  在浏览器中访问http://localhost:8800/,若可以显示django的服务接口清单,则表示服务启动正常,若不正常,请参考django的文档。

  当然,还可以查看端口是否正常启动【netstat –ano | find “8800”】,如果有监听,则应该不会有问题。

2.2、添加windows服务功能

2.2.1、安装与配置

  • 【pip install django-windows-tools】,可参见官方文档
  • 添加django配置

    在【E:\helloworld\hello\settings.py】文件中,向变量INSTALLED_APPS中添加'django_windows_tools'
  • 生成项目的service.*文件

    【E:\helloworld>python manage.py winservice_install】,此处可能与官方文档不一样,官方应该是错的。此时会自动生成service.py和service.ini两个文件
  • 配置service.ini文件

    官网有详细说明,大体为services是调用服务的入口,其中run的值为下文中设置的节点名称,如果run指令中含有多个节点命令,那么就会起多个线程来执行。本例中只会用到services、runserver、log这3个节点。其余节点可视情况配置。例如:
[services]
# Services to be run on all machines
run=runserver ## 表示会使用到下文的[runserver]中的命令,此名称可随意定
clean=d:\logs\service.log ## 要定期清理的日志路径,需与[log]中filename对应,否则在停止服务时会报找不到此日志文件 [runserver]
# Runs the debug server and listen on port 8000
# This one is just an example to show that any manage command can be used
command=runserver ## django的runserver命令
parameters=--noreload --insecure 0.0.0.0:8800 ## django的启动参数 [log]
filename=d:\logs\service.log
level=INFO
  • 修改service.py文件:

    为使服务更人性化,可在service.py文件的_svc_display_name_变量后面添加服务的描述【_svc_description_】:
_svc_display_name_ = "HelloWorldService"
_svc_description_ = "HelloWorldService" ## 建议处
_config_filename = "service.ini"

2.2.2、手工添加和启动服务

E:\helloworld>python service.py install
Installing service HelloWroldService
Service installed E:\helloworld>python service.py start
Starting service HelloWroldService

  此时,浏览器访问url能正常显示服务接口清单,且端口都正常,表示服务启动正常,windows服务搭建进行到一半了。一般此步骤之前不会存在问题。

2.3、打包工程Pyinstaller

如何下载安装就不讲解了,官网非常详细。

2.3.1、先打包成文件夹形式(方便排查问题)

E:\helloworld>pyinstaller -n hello -y --add-data "service.ini;." service.py
E:\helloworld>dist\hello\hello.exe install
E:\helloworld>dist\hello\hello.exe start

  上述会在当前目录下生成dist文件夹,下面是生成的最终打包的文件。

  -add-data表示将service.ini手工添加到打包程序中,若不添加,在运行时会提示找不到此文件。

  -y表示自动覆盖上次的打包程序。

  最后的参数service.py为windows服务的入口。

编译和运行过程中可能会存在如下报错:

  • 问题1:缺少win32timezone包

    直接在service.py中添加【import win32timezone】,pyinstaller就能自己找到加载包了
  • 问题2:服务install正常,但start异常:

    报【Error starting service: 服务没有及时响应启动或控制请求。】

    此报错可根据网上的解决方法(具体原理本人也不清楚,能解决问题就可以了):
import win32serviceutil
import traceback
import servicemanager
import winerror
......
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == 'test':
test_commands(base_path)
else:
if len(sys.argv) == 1:
try:evtsrc_dll = os.path.abspath(servicemanager.__file__)
servicemanager.PrepareToHostSingle(Service)
servicemanager.Initialize('HelloService', evtsrc_dll)
servicemanager.StartServiceCtrlDispatcher()
except Exception as exp:
print('ERROR : %s, Detail : %s' % (exp, traceback.format_exc()))
if exp.args[0] == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
win32serviceutil.usage()
else:
win32serviceutil.HandleCommandLine(Service)
  • 问题3:启动时不会报错,但端口是没有监听,windows事件查看器中会显示报错:
The instance's SvcRun() method failed
Traceback (most recent call last):
File "site-packages\win32\lib\win32serviceutil.py", line 835, in SvcRun
File "site-packages\django_windows_tools\service.py", line 269, in SvcDoRun
FileNotFoundError: [WinError 2] 'C:\\Windows\\system32\\service.ini'

 即找不到service.ini文件,虽然我们打包时添加了此文件,但此exe文件在实际运行时并没有查找找到当前目录的文件。原因与pyinstaller运行机制有关,详见文档,需修改service.py文件import后中配置文件路径:

base_path = os.path.dirname(os.path.abspath(__file__))
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS ## 表示在实际生产运行时让程序去此目录查找
if not base_path in sys.path:
sys.path.append(base_path)
  • 问题4:启动时不会报错,windows事件查看器中会显示报错(即问题3已经修改),日志文件中也无报错记录,但端口是没有监听,windows事件查看器中现象为:在打印出'starting'后就停止打印其他信息,未打印出'Starting command'。

    正常日志应显示'Starting command D:\...\site-pack..\service.py runserver --noreload --insecure 0.0.0.0:8800'。

    • 分析过程:

      手工在site-packages\django_windows_tools\service.py文件中添加日志分析,发现在调用链【self.start()】-【start_commands()】-【spawn_command()】-【start_django_command()】中,最后那次调用未生效,即spawn_command中生成Process多线程处理时未生效;经google后,找到了PyInstaller-built Windows EXE fails with multiprocessing ,里面提到

      无标题.png

      也就是说在windows平台中需要添加freeze_support()函数。

    • 解决方法:
if __name__ == "__main__":
multiprocessing.freeze_support()
  • 问题5:线程正常启动,事件查看器中显示报错:

    pyinstall少包.png

    说明我们在pyinstaller打包时少打了包。经过多次这种尝试,最终找出了所有django所需的包,需修改hello.spec文件,在【hiddenimports】数组中添加如下包:

hiddenimports=['django.contrib.admin.apps', 'django.contrib.auth.apps', 'django.contrib.contenttypes.apps', 'django.contrib.messages.apps', 'django.contrib.staticfiles.apps', 'django.contrib.sessions.models', 'django.contrib.sessions.apps', 'django.contrib.messages.middleware', 'django.contrib.auth.middleware', 'django.contrib.sessions.middleware', 'django.contrib.sessions.serializers']

注意,此时应执行:【pyinstaller -y hello.spec】命令

  • 问题6:Django参数命令不正确。
    • 事件查看器显示:
Exception occured : Traceback (most recent call last):
File "site-packages\django_windows_tools\service.py", line 156, in start_django_command
File "site-packages\django\core\management\__init__.py", line 364, in execute_from_command_line
File "site-packages\django\core\management\__init__.py", line 356, in execute
File "site-packages\django\core\management\base.py", line 277, in run_from_argv
File "site-packages\django\core\management\base.py", line 58, in parse_args
File "argparse.py", line 1733, in parse_args
File "site-packages\django\core\management\base.py", line 62, in error
File "argparse.py", line 2389, in error
File "argparse.py", line 2376, in exit
SystemExit: 2
    • d:\logs\service.log显示:
[INFO/Process-1] Starting command : service.py runserver --noreload --insecure 0.0.0.0:8800
[INFO/Process-1] usage: service.py runserver [-h] [--version] [-v {0,1,2,3}]
[--settings SETTINGS] [--pythonpath PYTHONPATH]
[--traceback] [--no-color] [--ipv6]
[--nothreading] [--noreload]
[addrport] [INFO/Process-1] service.py runserver: error: unrecognized arguments: --insecure
    • 即insecure参数不对,那就去掉吧。把service.ini的runserver节点下修改成【parameters=--noreload 0.0.0.0:8800】

小结:

本节主要介绍了使用django-windows-tools及pyinstaler打包过程中遇到的各种问题,在本文最后再贴出源码。

2.3.2、打包成单文件形式

打包生成单文件,可有2种方法:

  • 通过命令行倒推:

    先备份hello.spec文件,再运行命令【pyinstaller -n hello -y --add-data "service.ini;." –F service.py】添加-F参数,表示打包单文件;再打包生成新的hello.spec;然后把上述少的程序包添加到hiddenimports数组里;最后再进行打包。
  • 直接修改hello.spec文件:

    去掉【exclude_binaries=True,】,并添加【a.binaries, a.zipfiles, a.datas,runtime_tmpdir=None,】4行配置,再进行打包。

打包完后,最终运行时如:

E:\helloworld>dist\hello.exe install
E:\helloworld>dist\hello.exe start

比文件夹形式的,中间会少一层目录。

注意:运行单文件会比目录形式的时间长一点,它会首先解压至临时目录中,再运行,如果中间没有报错,就会立刻删除临时文件。因此,若项目中存在一些要时刻访问的配置文件,则需新建其他目录进行额外的管理。

2.4、其他可能遇到的问题

2.4.1、Error installing service: 指定的服务已标记为删除。 (1072)

一般为【服务】或【事件查看器】未关闭,关闭了即可解决,如果关闭了还出问题,可能就需要重启电脑,把与这个服务关联的进程给清理掉。

2.4.2、PermissionError: [WinError 5] 拒绝访问:'E:\helloworld\dist\hello\servicemanager.pyd'

一般为【事件查看器】未关闭,关闭了即可解决,如果关闭了还出问题,可能就需要重启电脑,把与这个服务关联的进程给清理掉。

三、完整源码

3.1、service.ini

[services]
# Services to be run on all machines
run=runserver
clean=APPLOGS\service.log [BEATSERVER]
# There should be only one machine with the celerybeat service
run=celeryd celerybeat
clean=APPLOGS\celerybeat.pid;APPLOGS\beat.log;APPLOGS\celery.log [celeryd]
command=celeryd
parameters=-f APPLOGS\celery.log -l info [celerybeat]
command=celerybeat
parameters=-f APPLOGS\beat.log -l info --pidfile=APPLOGS\celerybeat.pid [runserver]
# Runs the debug server and listen on port 8000
# This one is just an example to show that any manage command can be used
command=runserver
parameters=--noreload 0.0.0.0:18800 [log]
filename=APPLOGS\service.log
level=INFO

3.2、service.py

#!/usr/bin/env python
import os
import os.path
import sys
import win32serviceutil
import win32timezone
import traceback
import servicemanager
import winerror
import multiprocessing
import re # This is my base path
base_path = os.path.dirname(os.path.abspath(__file__))
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
if not base_path in sys.path:
sys.path.append(base_path) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hello.settings") from django_windows_tools.service import DjangoService,test_commands, error class HelloService(DjangoService):
_base_path = base_path
_svc_name_ = "HelloService"
_svc_display_name_ = "HelloService"
_svc_description_ = "XXXXXXX"
_config_filename = "service.ini" def __init__(self, args):
self.__replace_log_path()
DjangoService.__init__(self, args) ## 将service.ini手工移动到系统目录下的LOTS目录中
def __replace_log_path(self):
try:
file_dir = os.getenv('SYSTEMROOT') + '\\..\\LOTS'
if not os.path.exists(file_dir):
os.makedirs(file_dir)
old_file = os.path.join(HelloService._base_path, HelloService._config_filename)
new_file = old_file + '.run'
with open(old_file, 'r') as f:
old = f.read()
new = re.sub("APPLOGS", file_dir, old)
with open(new_file, 'w') as f:
f.write(new)
HelloService._config_filename = new_file
# win32file.CopyFile(new_file, old_file, 0)
except Exception as exp:
err = 'ERROR : %s, Detail : %s' % (exp, traceback.format_exc())
error(err) if __name__ == "__main__":
multiprocessing.freeze_support()
argv_len = len(sys.argv)
if argv_len > 1 and sys.argv[1] == 'test':
test_commands(base_path)
else:
if argv_len == 1:
try:
evtsrc_dll = os.path.abspath(servicemanager.__file__)
servicemanager.PrepareToHostSingle(HelloService)
servicemanager.Initialize('HelloService', evtsrc_dll)
servicemanager.StartServiceCtrlDispatcher()
except Exception as exp:
print('ERROR : %s, Detail : %s' % (exp, traceback.format_exc()))
if exp.args[0] == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
win32serviceutil.usage()
else:
win32serviceutil.HandleCommandLine(HelloService)

3.3、hello.spec——文件夹形式

# -*- mode: python -*-

block_cipher = None

a = Analysis(['service.py'],
pathex=['E:\\hello'],
binaries=[],
datas=[('service.ini', '.')],
hiddenimports=['django.contrib.admin.apps', 'django.contrib.auth.apps', 'django.contrib.contenttypes.apps', 'django.contrib.messages.apps', 'django.contrib.staticfiles.apps', 'django.contrib.sessions.models', 'django.contrib.sessions.apps', 'django.contrib.messages.middleware', 'django.contrib.auth.middleware', 'django.contrib.sessions.middleware', 'django.contrib.sessions.serializers'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
exclude_binaries=True,
name='hello',
debug=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='hello')

3.4、hello.spec——单文件形式

# -*- mode: python -*-

block_cipher = None

a = Analysis(['service.py'],
pathex=['E:\\hello'],
binaries=[],
datas=[('service.ini', '.')],
hiddenimports=['django.contrib.admin.apps', 'django.contrib.auth.apps', 'django.contrib.contenttypes.apps', 'django.contrib.messages.apps', 'django.contrib.staticfiles.apps', 'django.contrib.sessions.models', 'django.contrib.sessions.apps', 'django.contrib.messages.middleware', 'django.contrib.auth.middleware', 'django.contrib.sessions.middleware', 'django.contrib.sessions.serializers'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='hello',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )

转载:https://www.jianshu.com/p/a53b430b1410

Python3.6+pyinstaller+Django的更多相关文章

  1. 【爬坑】python3+pyqt5+pyinstaller 打包成exe的各种问题

    windows系统+python3+pyqt5+pyinstaller打包,经常会出现各种打包异常情况.如果代码没有特别异常,那么综合原因,大抵都是这四个元素之间的匹配问题,引起的.作者:一心狮链接: ...

  2. python3使用pyinstaller打包apscheduler出的错

    本来只是想用Python做一个定时任务小工具在服务器上运行,可是服务器在隔离区,各种禁止上外网,使用pip导出列表那种下载库的方法不管用,导致Python的各种库都下不到,官网离线下载又各种缺依赖,好 ...

  3. python3下安装Django

    1.下载python3 https://www.Python.org/ 我下载的是Python3.5.1 选的 Windows x86-64 executable installer 2. 打开cmd ...

  4. python3.4下django集成使用xadmin后台

    环境:window7 x64.python3.4.django1.10 一.pip install xadmin安装报错 1.使用pip install xadmin命令安装可能报如下错误: 2.解决 ...

  5. python3.5 中Django框架连接mysql

    ps:mysqldb目前还不支持3.0python唉,最近赶了个新潮,用起了Python3.4跟Django1.6,数据库依然是互联网企业常见的MySql.悲催的是在Python2.7时代连接MySq ...

  6. python3.6 pyinstaller 打包exe

    现在的pyinstaller 最新版本已经支持python3.6版本的打包了只需要进行如下的操作即可 1. pip install pyinstaller 2. pip install --upgra ...

  7. python3开发进阶-Django框架学习前的小项目(一个简单的学员管理系统)

    ''' 自己独立写一个学员管理系统 表结构: 班级表: -id -grade_name 学生表: -id -student_name -grade 关联外键班级表 老师表: -id -teacher_ ...

  8. python3开发进阶-Django框架的起飞加速一(ORM)

    阅读目录 ORM介绍 Django中的ORM ORM中的Model ORM的操作 一.ORM介绍 1.ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一 ...

  9. python3开发进阶-Django框架起飞前的准备

    阅读目录 安装 创建项目 运行 文件配置的说明 三个组件 一.安装(安装最新LTS版) Django官网下载页面 根据官方的图版本,我们下载1.11版本的,最好用! 有两种下载方式一种直接cmd里: ...

随机推荐

  1. 【转】Android实现伸缩弹力分布菜单效果

    本文介绍下在Android中实现伸缩弹力分布菜单效果.关于这种菜单效果在IPhone中比较常见,效果比较酷.那么在Android中实现只是一种简单的模仿. 这两天无意间看到一园友的博文实现Path2. ...

  2. vue 修饰符 整理

    事件修饰符 .stop <!-- 阻止单击事件继续传播 --> <a v-on:click.stop="doThis"></a> .preven ...

  3. Design Pattern ->Factory Method

    Layering & Contract Philosophy With additional indirection Factory Method The example code is as ...

  4. Android实现异步的几种方法

    在Android项目中,有经验的开发人员都知道,一些耗时的IO操作等都必须在子线程中去操作,那么可以有哪些方法来开启子线程呢,一般可以使用Java中自带的几种方法,也可以使用Andorid特有的一些类 ...

  5. 《ArcGIS Runtime SDK for Android开发笔记》——(6)、基于Android Studio的ArcGIS Android工程结构解析

    1.前言 Android Studio 是第一个Google官方的 Android 开发环境.其他工具,例如 Eclipse,在 Android Studio 发布之前已经有了大规模的使用.为了帮助开 ...

  6. react-native 视频播放器(很不错哦)

    第一步: npm i -S react-native-af-video-player(安装前:先安装: react-native-video.react-native-keep-awake.react ...

  7. GNU Nano编辑器

    ^表示 control建 ^X 退出 nano ^O 保存文件 ^R 插入其他文件内容至光标位置 ^W 查找字符串 ^Y 跳至前一屏 ^V 跳至后一屏 ^K 剪切光标所在行并保存到剪贴板,或剪切选中内 ...

  8. 【Tim Sweeney】Why C++ for Unreal 4?

    The first three generations of the Unreal Engine included a sandboxed scripting language, UnrealScri ...

  9. Git-进阶-远程仓库的使用

    一.远程仓库怎么玩 1. 自己搭建一个运行Git的服务器 Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上,但肯定有一台机器有着最原始的版本库,然后别的机器来克隆这个原始版本库,这 ...

  10. cesium 加载kml polygon和mark(贴地形terrain效果)

    key code: var options = { camera : viewer.scene.camera, canvas : viewer.scene.canvas, clampToGround: ...