Gunicorn Flask Restful API и Flask-Socketio аварийно завершают работу, когда я привязываю хост и порт к Gunicorn

Это немного странно, поскольку я развертываю свои приложения Flask уже более 2 лет, используя один и тот же способ с небольшими отличиями в зависимости от технического стека. Когда я запускаю свое приложение Flask без указания хоста: порт для пушки, как показано ниже:

web: gunicorn --worker-class eventlet -w 1 wsgi:app

как упоминалось в документах здесь, все работает нормально, но мои сокеты не 't, поскольку сервер Gunicorn каждый раз запускается почти с другого порта, что делает невозможным указание фиксированного порта во внешнем интерфейсе. Вот почему я указываю хост и порт следующим образом:

web: gunicorn --bind 127.0.0.1:8000 --worker-class eventlet -w 1 wsgi:app

Я должен отметить, что я также пробовал другие альтернативы, такие как:

web: gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker --bind 127.0.0.1:8000 -w 1 wsgi:app

Что происходит здесь, так это то, что приложение успешно развертывается также, когда я привязываю хост и адрес, но сбой без журналов или ошибок, когда это действительно расстраивает. Что еще более расстраивает, так это то, что он работает без проблем на локальном хосте, независимо от того, указываю ли я адрес. Вот мои журналы Heroku:

2020-05-30T10:32:09.146144+00:00 app[api]: Deploy 37f7668f by user [email protected]
2020-05-30T10:32:09.146144+00:00 app[api]: Release v39 created by user [email protected]
2020-05-30T10:32:20.484467+00:00 app[web.1]: [2020-05-30 10:32:20 +0000] [4] [INFO] Starting gunicorn 20.0.4
2020-05-30T10:32:20.485325+00:00 app[web.1]: [2020-05-30 10:32:20 +0000] [4] [INFO] Listening at: http://127.0.0.1:8000 (4)
2020-05-30T10:32:20.485476+00:00 app[web.1]: [2020-05-30 10:32:20 +0000] [4] [INFO] Using worker: eventlet
2020-05-30T10:32:20.490925+00:00 app[web.1]: [2020-05-30 10:32:20 +0000] [9] [INFO] Booting worker with pid: 9
2020-05-30T10:32:22.886718+00:00 app[web.1]: Server initialized for eventlet.
2020-05-30T10:32:26.000000+00:00 app[api]: Build succeeded
2020-05-30T10:33:17.674322+00:00 heroku[web.1]: State changed from starting to crashed
2020-05-30T10:33:18.184411+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=langcodex.herokuapp.com request_id=d0742a73-96a0-490e-860b-e0ccda544e0b fwd="41.92.113.84" dyno= connect= service= status=503 bytes= protocol=https
2020-05-30T10:33:19.352104+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=langcodex.herokuapp.com request_id=8f202689-ee4c-4871-bfe3-ef8f9c34b397 fwd="41.92.113.84" dyno= connect= service= status=503 bytes= protocol=https

Вот мой файл app/init.py:

import logging, os
from logging.handlers import SMTPHandler, RotatingFileHandler
from flask import Flask
from flask_socketio import SocketIO
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_mail import Mail
from flask_migrate import Migrate
from flask_seasurf import SeaSurf
from flask_cors import CORS
from config import Config, Development


db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()
mail = Mail()
seasurf = SeaSurf()
socket = SocketIO()
cors = CORS()


def create_app(config_class=Config):
    """Construct the core application."""
    app = Flask(__name__, static_folder='frontend/build/static', 
        instance_relative_config=True)
    # Application Configuration
    app.config.from_object(config_class)
    with app.app_context():
        # Initialize Plugins
        login_manager.init_app(app)
        db.init_app(app)
        mail.init_app(app)
        migrate.init_app(app, db)
        seasurf.init_app(app)
        cors.init_app(app, resources={r'/api/*': {'origins': '*'}})
        socket.init_app(app, cors_allowed_origins="*", 
            async_mode='eventlet', 
            engineio_logger=True, 
            logger=True)
        # Initialize Global db
        db.create_all()
        from app.serve_frontend_bp import serve_frontend_bp as serve_frontend_blueprint
        app.register_blueprint(serve_frontend_blueprint)
        from app.socket_bp import socket_bp as socket_blueprint
        app.register_blueprint(socket_blueprint)
        from app.nlp_bp import nlp_bp as nlp_blueprint
        app.register_blueprint(nlp_blueprint, url_prefix='/api/nlp')
        from app.main_bp import main_bp as main_blueprint
        app.register_blueprint(main_blueprint, url_prefix='/api')
        from app.errors_bp import errors_bp as errors_blueprint
        app.register_blueprint(errors_blueprint)
        # Configute Debugging
        if app.debug or app.testing:

            if app.config['LOG_TO_STDOUT']:
                stream_handler = logging.StreamHandler()
                stream_handler.setLevel(logging.INFO)
                app.logger.addHandler(stream_handler)
            else:
                if not os.path.exists('logs'):
                    os.mkdir('logs')
                file_handler = RotatingFileHandler('logs/langandcode.log',
                                                   maxBytes=20480, backupCount=20)
                file_handler.setFormatter(logging.Formatter(
                    '%(asctime)s %(levelname)s: %(message)s '
                    '[in %(pathname)s:%(lineno)d]'))
                file_handler.setLevel(logging.INFO)
                app.logger.addHandler(file_handler)
            app.logger.setLevel(logging.INFO)
            app.logger.info('LanguageandCode startup')
        return app

Вот структура моего приложения:

.
├── app
│   ├── auth_bp
│   ├── core
│   ├── decorators.py
│   ├── email.py
│   ├── errors_bp
│   ├── exceptions.py
│   ├── factory.py
│   ├── forms.py
│   ├── frontend
│   ├── functions.py
│   ├── __init__.py
│   ├── logs
│   ├── main
│   ├── main_bp
│   ├── models.py
│   ├── nlp_bp
│   ├── __pycache__
│   ├── serve_frontend_bp
│   ├── socket_bp
│   └── templates
├── config.py
├── latest.dump
├── logs
│   └── langandcode.log
├── migrations
│   ├── alembic.ini
│   ├── env.py
│   ├── __pycache__
│   ├── README
│   ├── script.py.mako
│   └── versions
├── Procfile
├── requirements.txt
├── runtime.txt
└── wsgi.py

Я надеюсь, что действительно найду ответ на этот вопрос, поскольку он не имеет для меня особого смысла, особенно без журналов или ошибок. Надеюсь, есть работа по исправлению порта, чтобы мои сокеты работали. Большое спасибо за ваше время и помощь.


person Murphy Adam    schedule 30.05.2020    source источник


Ответы (1)


Итак, то, что я добавил, чтобы сделать эту работу, по существу использует:

web: gunicorn --worker-class eventlet -w 1 wsgi:app

or

web: gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 wsgi:app

взаимозаменяемо в файле procfile с одной незначительной модификацией в моем приложении/init.py при инициализации socketio:

        socket.init_app(app, cors_allowed_origins="*", 
        async_mode='gevent', 
        engineio_logger=True, 
        logger=True, 
        port=8000) 

Я в основном добавил порт 8000 для сокетов для прокси-запросов вместо порта Gunicorn, который неизвестен внешнему интерфейсу, пока сервер не запустится, если это так. Я до сих пор не знаю, почему сервер падает, когда я указываю хост и имя, и я надеюсь, что найду ответ, но я рад, что мое приложение и сокеты теперь работают правильно.

person Murphy Adam    schedule 30.05.2020
comment
Нет, вы не правы. Аргумент port не существует, для init_app() он игнорируется. Heroku определяет, какой порт вы используете, и помещает его в переменную $PORT, поэтому каждый раз приложение запускается на другом порту. Это совершенно нормально, потому что из клиента вы будете подключаться к своему приложению через обратный прокси-сервер Heroku через порты 80 или 443, независимо от того, какой внутренний порт использует ваше приложение. - person Miguel; 30.05.2020
comment
@Miguel, спасибо за ответ. Это неловко, это было мое предположение, почему приложение, наконец, заработало, но после некоторых поисков оказалось, что это не так, как вы упомянули. - person Murphy Adam; 05.06.2020