python-socketio не всегда выдает при отслеживании загрузки файла на флеш-сервер
Я использую флеш-сервер для веб-сервисов RESTful и python-socketio для достижения двунаправленной связи между сервером и клиентом, чтобы отслеживать ход загрузки на серверной части.
Я беру переменную sio, объявленную в файле server.py, и передаю ее в качестве параметра в новый объект, который будет использовать ее для передачи клиенту определенных сообщений о том, как происходит загрузка файла на сервер.
sio = socketio.Server(async_mode='threading')
omics_env = None
@sio.on('init', namespace='/guardiome')
def init(sid, data):
global omics_env
if omics_env == None:
omics_env = Environment(socket=sio)
omics_env.conda.download_conda()
omics_env.data_management.download_omics_data()
Проблема в том, что когда файл загружается на сервер Python, он отправляет клиенту сообщение каждый раз, когда записывает 1% данных в файл. Но он не всегда посылает клиенту каждый раз, когда он загружает / записывает 1 процент данных в файл.
Обычно он сообщает о прогрессе до 18%, задерживает на некоторое время, а затем отчитывается о 40%, пропуская выбросы между 18% и 40%.
Кто-то может сказать, что интернет, возможно, отстает, но я печатал операторы в функции загрузки поверх функции emit, которая показывает, что она записывает / загружает каждый 1 процент данных.
Я также проверил онлайн для другого ресурса. Некоторые упоминали использование eventlet и делали что-то подобное на самом высоком уровне кода сервера.
import eventlet
evenlet.monkey_patch()
Но это не приводит к генерации кода вообще.
Другие упоминали об использовании очереди сообщений, такой как redis, но я не могу использовать redis, и я планирую превратить весь код python в двоичный исполняемый файл, чтобы он был полностью переносимым на платформе linux для связи с локальным клиентом.
Вот мой server.py
import socketio
import eventlet.wsgi
from environment import Environment
from flask import Flask, jsonify, request, send_file
from flask_cors import CORS
omics_env = None
sio = socketio.Server(async_mode='threading')
app = Flask(__name__)
CORS(app)
@sio.on('init', namespace='/guardiome')
def init(sid, data):
global omics_env
if omics_env == None:
omics_env = Environment(socket=sio)
omics_env.conda.download_conda()
omics_env.data_management.download_omics_data()
omics_env.logger.info('_is_ready()')
sio.emit(
event='init',
data={'status': True, 'information': None},
namespace='/guardiome')
try:
# wrap Flask application with engineio's middleware
app.wsgi_app = socketio.Middleware(sio, app.wsgi_app)
# Launch the server with socket integration
app.run(port=8008, debug=False, threaded=True)
finally:
pass
# LOGGER.info('Exiting ...')
Вот функция download_w_progress, в которую я передаю sio в качестве параметра reporter
def download_w_progress(url , path, reporter=None):
ssl._create_default_https_context = ssl._create_unverified_context
r = requests.get(url, stream=True)
# Helper lambda functions
progress_report = lambda current, total: int((current/total)*100)
raw_percent = lambda current, total: (current/total)*100
# TODO(mak3): Write lambda function for reporting amount of file downloaded
# in MB, KB, GB, or whatever
with open(path, 'wb') as f:
total_length = int(r.headers.get('content-length'))
progress_count = 0
chunk_size = 1024
# Used to cut down on emit the same rounded percentage number
previous_percent = -1
# Read and write the file in chunks to its destination
for chunk in r.iter_content(chunk_size=1024):
progress_dict = {
"percent": progress_report(progress_count, total_length)
}
if reporter != None:
# Limit the number of emits sent to prevent
# to socket from overworking
if progress_dict["percent"] != previous_percent:
reporter.emit(event="environment", namespace="/guardiome", data=progress_dict)
# TODO(mak3): Remove or uncomment in production
if progress_dict["percent"] != previous_percent:
print(progress_dict["percent"], end='\r')
progress_count += chunk_size
previous_percent = progress_dict["percent"]
if chunk:
f.write(chunk)
f.flush()
0 ответов
Извините, я пропустил этот вопрос, когда вы его опубликовали.
В вашем коде есть пара проблем. Вы выбираете async_mode='threading
, В общем случае лучше не указывать этот аргумент и позволить серверу выбирать лучший асинхронный режим в зависимости от сервера, который вы используете. Например, когда вы добавляете eventlet, режим работы с потоками не работает, на самом деле для Eventlet существует специальный асинхронный режим.
Поэтому я рекомендую:
- удалить
async_mode
аргумент вsocketio.Server()
конструктор - установить eventlet в вашей виртуальной среде
- заменить
app.run()
раздел в вашем скрипте с кодом, который запускает сервер событий или если вы используете Flask, используйте расширение Flask-SocketIO, в которое уже встроен этот код. - добавить
sio.sleep(0)
вызов внутри цикла, где вы читаете свой файл. Это даст eventlet возможность гладко выполнять все задачи.