События сервера Flask, работающие в разработке, но не в производстве

#python #python-3.x #nginx #flask #gunicorn

#питон #python-3.x #nginx #фляжка #gunicorn #flask

Вопрос:

У меня есть маршрут flask, который должен генерировать серверные события в браузере. В основном функция выполняет следующее: 1. загрузите csv-файл 2. для каждой строки csv-файла 3. сохраните имя пользователя и адрес электронной почты в базе данных sql (используя sqlalchemy) 4. счетчик обновлений (для статуса выполнения) 5. отправить событие в браузер

Дело в том, что функция работает хорошо, когда я нахожусь в режиме разработки (используя встроенный сервер flask), но в производственном режиме (используя NginX и gunicorn) функция останавливается через несколько секунд, так как счетчик никогда не достигает 100, это приводит к повторному вызову функции браузером, и этот цикл никогда не заканчивается, потому что событие никогда не получает инструкцию close. Итак, главный вопрос заключается в том, почему это работает в разработке, а не в производстве? Вот mi code:

 # Update or construct database if a csv file was submitted
@app.route("/constructDatabase/<string:filename>", methods=["GET","POST"])
def constructDatabase(filename):
    # context manager to open csv file
    csvFile = open(os.path.join(os.getcwd(), filename), newline='')
    # get lines count of csv file
    totalLines = len(csvFile.readlines())
    # reset reader pointer
    csvFile.seek(0)
    # current percent status
    current_status = 0
    def generate(file, counter):
        # unpack and iterate over the csv file to get all the names and emails
    for Company,Address,City,State,Zip,County,Phone,Website,Contact,Title,Direct_Phone,Email,Sales,Employees,SIC_Code,Industry in csv.reader(file, delimiter=','):
        yield ':keep connection alivenn'
        counter  = 1
        # if a user has not contact name or email then not useful
        if Email == None or Email == '':
            yield f'id: {counter}nevent: messagendata: {round(counter / totalLines * 100, 1)}nn'
            continue
        if Contact == None or Contact == '':
            yield f'id: {counter}nevent: messagendata: {round(counter / totalLines * 100, 1)}nn'
            continue
        # Create user as instance of User class
        user = Users(company=Company, address=Address, city=City, state=State, 
        zip=Zip, country=County, phone=Phone, website=Website, contact=Contact, 
        title=Title, direct_phone = Direct_Phone, email=Email, sales=Sales, 
        employees=Employees, sic_code=SIC_Code, industry=Industry)
        # Add user to database
        db.session.add(user)
        # get current percent status of building database
        yield f'id: {counter}nevent: messagendata: {round(counter / totalLines * 100, 1)}nn'
    # Save changes in database
    db.session.commit()
    print("SAVING DATABASE .......")
    # close file
    file.close()
return Response(generate(csvFile, current_status), mimetype='text/event-stream')
  

Код Java script прямо сейчас:
javascript

 // create Event source connection with the server to listen for incoming msg
var source = new EventSource(`/constructDatabase/${filename}`);
// if new msg was received
source.onmessage = function(msg) {
// update progress bar
$('.progress-bar').css('width', msg.data '%').attr('aria-valuenow', msg.data);  
// if is 100 percent close connection to the server
if (msg.data == 100) {
    source.close();
    // Hide label
    $('.prog-bar-label').addClass('d-none');
    // Hide CSV progress bar
    $('.csvProgressBar').addClass('d-none');
    // reset progress bar
    $('.csvProgressBar').find('.progress-bar').css('width', 0 '%').attr('aria-valuenow', 0);
           } 
     };
     source.onerror = function(error){
           console.log(error.data);
     };
  

Комментарии:

1. Проверьте журналы nginx и gunicorn, чтобы увидеть, какой из них может завершать запрос и возвращать результаты.

2. У меня возникает эта ошибка при каждом сбросе соединения: [error] 14186#14186: *13628 connect() failed (111: Connection refused) while connecting to upstream

Ответ №1:

Я понимаю, что здесь происходит. Дело в том, что сервер разработчика flask, когда он запускается, не объявляет никакого тайм-аута, поэтому функция может долго ожидать ответа. Но на производственном сервере с gunicorn время ожидания по умолчанию очень короткое, поэтому, если функции требуется много времени, чтобы получить ответ, и время ожидания истекло, gunicorn завершает процесс и выходит из функции, поэтому потоковый процесс на стороне браузера думает, что это ошибка на сервере, когда на самом деле была просто отложенная функция. Решение состоит в том, чтобы увеличить время ожидания в gunicorn с помощью —timeout (желаемое значение времени ожидания здесь) и будет хорошим. Не используйте очень большие значения таймаута, просто попытайтесь выяснить, сколько времени требуется вашей функции, чтобы получить ответ, и используйте это значение. Помните, что это значение теперь будет одинаковым для всех ваших функций.