Django 服务器崩溃,退出代码为 139、77
Django server crashes with exit codes 139, 77
前言
好的,我有一个非常复杂的性能问题。我正在构建一个内容管理系统,其中一个功能应该是使用不同的模板生成大量 .docx
文件。我从 Webodt + Abiword. But then templates got too complex, so I had to swith my backend to Templated-docs + LibreOffice 开始。所以这就是我的问题开始的地方。
我使用:
- Python 2.7.12
- Django==1.8.2
- 模板文档==0.2.9
- LibreOffice 5.1.5.2
- Ubuntu 16.04
实际问题
我有一个 API 可以处理 .docx
渲染。我将展示其中一个视图,作为示例,它们非常相似:
@permission_classes((permissions.IsAdminUser,))
class BookDocxViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
def retrieve(self, request, *args, **kwargs):
queryset = Pupils.objects.get(id=kwargs['pk'])
serializer = StudentSerializer(queryset)
context = dict(serializer.data)
doc = fill_template('crm/docs/book.ott', context, output_format='docx')
p = u'docs/books/%s/%s_%s_%s.doc' % (datetime.now().date(), context[u'surname'], context[u'name'], datetime.now().date())
with open(doc, 'rb') as f:
content = f.read()
path = default_storage.save(p, ContentFile(content))
f.close()
return response.Response(u'/media/' + path)
当我第一次调用它时,它会创建一个 .docx
文件,将其保存到我的 default_storage
,然后 returns 我下载 link。但是当我尝试再次执行此操作时,使用另一种方法(使用另一个模板和上下文)执行此操作时,我的服务器只是崩溃而没有任何日志。我最后看到的是
Process finished with exit code 77
如果我稍微延迟调用它(超过一秒)
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
如果立即第二次调用我的方法(不到一秒)
我尝试使用调试器 -- 它说我的服务器在这一行崩溃:
doc = fill_template('crm/docs/book.ott', context, output_format='docx')
我敢打赌会发生什么:
- 当我第一次调用我的方法时
templated_docs
启动 LibreOffice 后端,然后 没有停止它
- 当我第二次调用我的方法时
templated_docs
尝试再次启动 LibreOffice 后端,但它已经很忙了。
问题
我如何调试 LibreOffice 来证明/反驳我的理论? (我想我需要调试 templated_docs
)
- 为什么我会根据延迟获得不同的退出代码?
- GitHub 上 issue 的基础是否足够?
- 我该如何解决?
UPD
这不是 REST Framework 或未使用 FileResponce()
的问题。
我已经尝试用常规视图对其进行测试。
def get_document(request, *args, **kwargs):
context = Pupils.objects.get(id=kwargs['pk']).__dict__
doc = fill_template('crm/docs/book.ott', context, output_format='docx')
p = u'%s_%s_%s' % (context[u'surname'], context[u'name'], datetime.now().date())
return FileResponse(doc, p)
同样的问题
更新 2
好的。此行正在刷新我的服务器:
# pylokit/lokit.py
self.lokit = lo.libreofficekit_hook(six.b(lo_path))
好的,这是 templated_docs
中的一个错误。我是对的,这是因为 templated_docs
试图启动 LibreOffice 两次。正如pylokit
documentation中所说:
The use of _exit() instead of default exit() is required because in
some circumstances LibreOffice segfaults on process exit.
这意味着使用pylockt
的进程应该在之后被杀死。但是我们不能杀死 Django 服务器。所以我决定使用多处理:
# templated_docs/__init__.py
if source_extension[1:] != output_format:
lo_path = getattr(
settings,
'TEMPLATED_DOCS_LIBREOFFICE_PATH',
'/usr/lib/libreoffice/program/')
def f(conn):
with Office(lo_path) as lo:
conv_file = NamedTemporaryFile(delete=False,
suffix='.%s' % output_format)
with lo.documentLoad(str(dest_file.name)) as doc:
doc.saveAs(conv_file.name)
os.unlink(dest_file.name)
conn.send(conv_file.name)
conn.close()
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
conv_file_name = parent_conn.recv()
p.join()
return conv_file_name
else:
return dest_file.name
我打开了 issue and made a pull request。
前言
好的,我有一个非常复杂的性能问题。我正在构建一个内容管理系统,其中一个功能应该是使用不同的模板生成大量 .docx
文件。我从 Webodt + Abiword. But then templates got too complex, so I had to swith my backend to Templated-docs + LibreOffice 开始。所以这就是我的问题开始的地方。
我使用:
- Python 2.7.12
- Django==1.8.2
- 模板文档==0.2.9
- LibreOffice 5.1.5.2
- Ubuntu 16.04
实际问题
我有一个 API 可以处理 .docx
渲染。我将展示其中一个视图,作为示例,它们非常相似:
@permission_classes((permissions.IsAdminUser,))
class BookDocxViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
def retrieve(self, request, *args, **kwargs):
queryset = Pupils.objects.get(id=kwargs['pk'])
serializer = StudentSerializer(queryset)
context = dict(serializer.data)
doc = fill_template('crm/docs/book.ott', context, output_format='docx')
p = u'docs/books/%s/%s_%s_%s.doc' % (datetime.now().date(), context[u'surname'], context[u'name'], datetime.now().date())
with open(doc, 'rb') as f:
content = f.read()
path = default_storage.save(p, ContentFile(content))
f.close()
return response.Response(u'/media/' + path)
当我第一次调用它时,它会创建一个 .docx
文件,将其保存到我的 default_storage
,然后 returns 我下载 link。但是当我尝试再次执行此操作时,使用另一种方法(使用另一个模板和上下文)执行此操作时,我的服务器只是崩溃而没有任何日志。我最后看到的是
Process finished with exit code 77
如果我稍微延迟调用它(超过一秒)Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
如果立即第二次调用我的方法(不到一秒)
我尝试使用调试器 -- 它说我的服务器在这一行崩溃:
doc = fill_template('crm/docs/book.ott', context, output_format='docx')
我敢打赌会发生什么:
- 当我第一次调用我的方法时
templated_docs
启动 LibreOffice 后端,然后 没有停止它 - 当我第二次调用我的方法时
templated_docs
尝试再次启动 LibreOffice 后端,但它已经很忙了。
问题
我如何调试 LibreOffice 来证明/反驳我的理论?(我想我需要调试templated_docs
)- 为什么我会根据延迟获得不同的退出代码?
- GitHub 上 issue 的基础是否足够?
- 我该如何解决?
UPD
这不是 REST Framework 或未使用 FileResponce()
的问题。
我已经尝试用常规视图对其进行测试。
def get_document(request, *args, **kwargs):
context = Pupils.objects.get(id=kwargs['pk']).__dict__
doc = fill_template('crm/docs/book.ott', context, output_format='docx')
p = u'%s_%s_%s' % (context[u'surname'], context[u'name'], datetime.now().date())
return FileResponse(doc, p)
同样的问题
更新 2
好的。此行正在刷新我的服务器:
# pylokit/lokit.py
self.lokit = lo.libreofficekit_hook(six.b(lo_path))
好的,这是 templated_docs
中的一个错误。我是对的,这是因为 templated_docs
试图启动 LibreOffice 两次。正如pylokit
documentation中所说:
The use of _exit() instead of default exit() is required because in some circumstances LibreOffice segfaults on process exit.
这意味着使用pylockt
的进程应该在之后被杀死。但是我们不能杀死 Django 服务器。所以我决定使用多处理:
# templated_docs/__init__.py
if source_extension[1:] != output_format:
lo_path = getattr(
settings,
'TEMPLATED_DOCS_LIBREOFFICE_PATH',
'/usr/lib/libreoffice/program/')
def f(conn):
with Office(lo_path) as lo:
conv_file = NamedTemporaryFile(delete=False,
suffix='.%s' % output_format)
with lo.documentLoad(str(dest_file.name)) as doc:
doc.saveAs(conv_file.name)
os.unlink(dest_file.name)
conn.send(conv_file.name)
conn.close()
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
conv_file_name = parent_conn.recv()
p.join()
return conv_file_name
else:
return dest_file.name
我打开了 issue and made a pull request。