Python3 HTTP Server作为Systemd服务运行时出现Broken Pipe错误
解决Python http.server作为Systemd服务时的BrokenPipeError问题
问题原因
BrokenPipeError本质是客户端(比如视频播放器)在服务器还未发送完数据时主动断开了连接,Python内置的http.server没有默认处理这类异常,因此会抛出错误。对于HLS视频服务来说,这种情况十分常见——用户暂停播放、切换视频或关闭页面,都会导致连接提前中断。
解决方案
1. 在请求处理中捕获BrokenPipeError
修改你的CORSRequestHandler类,重写相关方法捕获异常,避免单个请求的错误影响整个服务:
#!/usr/bin/env python3 from http.server import HTTPServer, SimpleHTTPRequestHandler import sys import shutil class CORSRequestHandler(SimpleHTTPRequestHandler): def end_headers(self): self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', '*') self.send_header('Access-Control-Allow-Headers', '*') self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate') return super(CORSRequestHandler, self).end_headers() def do_OPTIONS(self): self.send_response(200) self.end_headers() def do_GET(self): try: super().do_GET() except BrokenPipeError: self.log_message("客户端 {} 提前断开连接".format(self.client_address[0])) except Exception as e: self.log_error("处理请求时出错: %s", str(e)) def copyfile(self, source, outputfile): try: shutil.copyfileobj(source, outputfile) except BrokenPipeError: pass host = sys.argv[1] if len(sys.argv) > 2 else '0.0.0.0' port = int(sys.argv[len(sys.argv)-1]) if len(sys.argv) > 1 else 9999 print("Listening on {}:{}".format(host, port)) httpd = HTTPServer((host, port), CORSRequestHandler) httpd.serve_forever()
2. 优化Systemd服务配置
调整配置提升安全性和稳定性,避免用root用户运行服务:
[Unit] Description=HLS视频HTTP服务 After=network.target [Service] # 使用普通用户运行,降低权限风险 User=media WorkingDirectory=/home/media # 明确指定监听地址和端口,避免参数歧义 ExecStart=/usr/bin/python3 /home/media/httpserver.py 0.0.0.0 9999 # 仅在服务异常退出时重启,正常退出不触发重启 Restart=on-failure # 设置重启间隔,防止频繁重启 RestartSec=5 # 将日志输出到systemd journal,方便排查问题 StandardOutput=journal+console StandardError=journal+console [Install] WantedBy=multi-user.target
3. 生产环境替代方案:使用更稳定的HTTP服务器
Python内置http.server仅适合开发测试,生产环境建议用更可靠的服务器,比如aiohttp:
#!/usr/bin/env python3 from aiohttp import web import sys async def handle_static(request): path = request.match_info.get('path', '') return web.FileResponse(f'/home/media/{path}' if path else '/home/media/index.html') async def options_handler(request): return web.Response(headers={ 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': '*', 'Access-Control-Allow-Headers': '*' }) def main(): app = web.Application() app.add_routes([web.get('/{path:.*}', handle_static)]) app.add_routes([web.options('/{path:.*}', options_handler)]) @app.middleware async def cors_middleware(request, handler): response = await handler(request) response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate' return response host = sys.argv[1] if len(sys.argv) > 2 else '0.0.0.0' port = int(sys.argv[-1]) if len(sys.argv) > 1 else 9999 web.run_app(app, host=host, port=port) if __name__ == '__main__': main()
安装依赖:pip install aiohttp,然后更新Systemd的ExecStart为/usr/bin/python3 /home/media/aiohttp_server.py即可。
验证修改
- 重载Systemd配置并重启服务:
sudo systemctl daemon-reload sudo systemctl restart your-service-name
- 实时查看日志确认问题是否解决:
journalctl -u your-service-name -f
内容的提问来源于stack exchange,提问作者PETERSON MUNENE
相关产品推荐
相关产品推荐

