can run with granian by setting REFLEX_USE_GRANIAN var (#3919)
* can run with granian by setting REFLEX_USE_GRANIAN var * granian also useable for prod mode * adjust reload paths for granian * move uvicorn / granian logic to their own function * fix prod mode
This commit is contained in:
parent
d0ad5cd15e
commit
001b8c4222
@ -172,6 +172,33 @@ def run_frontend_prod(root: Path, port: str, backend_present=True):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def should_use_granian():
|
||||||
|
"""Whether to use Granian for backend.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if Granian should be used.
|
||||||
|
"""
|
||||||
|
return os.getenv("REFLEX_USE_GRANIAN", "0") == "1"
|
||||||
|
|
||||||
|
|
||||||
|
def get_app_module():
|
||||||
|
"""Get the app module for the backend.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The app module for the backend.
|
||||||
|
"""
|
||||||
|
return f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_granian_target():
|
||||||
|
"""Get the Granian target for the backend.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The Granian target for the backend.
|
||||||
|
"""
|
||||||
|
return get_app_module() + f".{constants.CompileVars.API}"
|
||||||
|
|
||||||
|
|
||||||
def run_backend(
|
def run_backend(
|
||||||
host: str,
|
host: str,
|
||||||
port: int,
|
port: int,
|
||||||
@ -184,24 +211,76 @@ def run_backend(
|
|||||||
port: The app port
|
port: The app port
|
||||||
loglevel: The log level.
|
loglevel: The log level.
|
||||||
"""
|
"""
|
||||||
import uvicorn
|
|
||||||
|
|
||||||
config = get_config()
|
|
||||||
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
|
||||||
|
|
||||||
web_dir = get_web_dir()
|
web_dir = get_web_dir()
|
||||||
# Create a .nocompile file to skip compile for backend.
|
# Create a .nocompile file to skip compile for backend.
|
||||||
if web_dir.exists():
|
if web_dir.exists():
|
||||||
(web_dir / constants.NOCOMPILE_FILE).touch()
|
(web_dir / constants.NOCOMPILE_FILE).touch()
|
||||||
|
|
||||||
# Run the backend in development mode.
|
# Run the backend in development mode.
|
||||||
|
if should_use_granian():
|
||||||
|
run_granian_backend(host, port, loglevel)
|
||||||
|
else:
|
||||||
|
run_uvicorn_backend(host, port, loglevel)
|
||||||
|
|
||||||
|
|
||||||
|
def run_uvicorn_backend(host, port, loglevel):
|
||||||
|
"""Run the backend in development mode using Uvicorn.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
host: The app host
|
||||||
|
port: The app port
|
||||||
|
loglevel: The log level.
|
||||||
|
"""
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
app=f"{app_module}.{constants.CompileVars.API}",
|
app=f"{get_app_module()}.{constants.CompileVars.API}",
|
||||||
host=host,
|
host=host,
|
||||||
port=port,
|
port=port,
|
||||||
log_level=loglevel.value,
|
log_level=loglevel.value,
|
||||||
reload=True,
|
reload=True,
|
||||||
reload_dirs=[config.app_name],
|
reload_dirs=[get_config().app_name],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_granian_backend(host, port, loglevel):
|
||||||
|
"""Run the backend in development mode using Granian.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
host: The app host
|
||||||
|
port: The app port
|
||||||
|
loglevel: The log level.
|
||||||
|
"""
|
||||||
|
console.debug("Using Granian for backend")
|
||||||
|
try:
|
||||||
|
from granian import Granian # type: ignore
|
||||||
|
from granian.constants import Interfaces # type: ignore
|
||||||
|
from granian.log import LogLevels # type: ignore
|
||||||
|
|
||||||
|
Granian(
|
||||||
|
target=get_granian_target(),
|
||||||
|
address=host,
|
||||||
|
port=port,
|
||||||
|
interface=Interfaces.ASGI,
|
||||||
|
log_level=LogLevels(loglevel.value),
|
||||||
|
reload=True,
|
||||||
|
reload_paths=[Path(get_config().app_name)],
|
||||||
|
reload_ignore_dirs=[".web"],
|
||||||
|
).serve()
|
||||||
|
except ImportError:
|
||||||
|
console.error(
|
||||||
|
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
|
||||||
|
)
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_backend_workers():
|
||||||
|
from reflex.utils import processes
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
return (
|
||||||
|
processes.get_num_workers()
|
||||||
|
if not config.gunicorn_workers
|
||||||
|
else config.gunicorn_workers
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -212,6 +291,20 @@ def run_backend_prod(
|
|||||||
):
|
):
|
||||||
"""Run the backend.
|
"""Run the backend.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
host: The app host
|
||||||
|
port: The app port
|
||||||
|
loglevel: The log level.
|
||||||
|
"""
|
||||||
|
if should_use_granian():
|
||||||
|
run_granian_backend_prod(host, port, loglevel)
|
||||||
|
else:
|
||||||
|
run_uvicorn_backend_prod(host, port, loglevel)
|
||||||
|
|
||||||
|
|
||||||
|
def run_uvicorn_backend_prod(host, port, loglevel):
|
||||||
|
"""Run the backend in production mode using Uvicorn.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
host: The app host
|
host: The app host
|
||||||
port: The app port
|
port: The app port
|
||||||
@ -220,14 +313,11 @@ def run_backend_prod(
|
|||||||
from reflex.utils import processes
|
from reflex.utils import processes
|
||||||
|
|
||||||
config = get_config()
|
config = get_config()
|
||||||
num_workers = (
|
|
||||||
processes.get_num_workers()
|
app_module = get_app_module()
|
||||||
if not config.gunicorn_workers
|
|
||||||
else config.gunicorn_workers
|
|
||||||
)
|
|
||||||
RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --preload --timeout {config.timeout} --log-level critical".split()
|
RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --preload --timeout {config.timeout} --log-level critical".split()
|
||||||
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
|
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
|
||||||
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
|
||||||
command = (
|
command = (
|
||||||
[
|
[
|
||||||
*RUN_BACKEND_PROD_WINDOWS,
|
*RUN_BACKEND_PROD_WINDOWS,
|
||||||
@ -243,7 +333,7 @@ def run_backend_prod(
|
|||||||
"--bind",
|
"--bind",
|
||||||
f"{host}:{port}",
|
f"{host}:{port}",
|
||||||
"--threads",
|
"--threads",
|
||||||
str(num_workers),
|
str(_get_backend_workers()),
|
||||||
f"{app_module}()",
|
f"{app_module}()",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -252,7 +342,7 @@ def run_backend_prod(
|
|||||||
"--log-level",
|
"--log-level",
|
||||||
loglevel.value,
|
loglevel.value,
|
||||||
"--workers",
|
"--workers",
|
||||||
str(num_workers),
|
str(_get_backend_workers()),
|
||||||
]
|
]
|
||||||
processes.new_process(
|
processes.new_process(
|
||||||
command,
|
command,
|
||||||
@ -262,6 +352,47 @@ def run_backend_prod(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_granian_backend_prod(host, port, loglevel):
|
||||||
|
"""Run the backend in production mode using Granian.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
host: The app host
|
||||||
|
port: The app port
|
||||||
|
loglevel: The log level.
|
||||||
|
"""
|
||||||
|
from reflex.utils import processes
|
||||||
|
|
||||||
|
try:
|
||||||
|
from granian.constants import Interfaces # type: ignore
|
||||||
|
|
||||||
|
command = [
|
||||||
|
"granian",
|
||||||
|
"--workers",
|
||||||
|
str(_get_backend_workers()),
|
||||||
|
"--log-level",
|
||||||
|
"critical",
|
||||||
|
"--host",
|
||||||
|
host,
|
||||||
|
"--port",
|
||||||
|
str(port),
|
||||||
|
"--interface",
|
||||||
|
str(Interfaces.ASGI),
|
||||||
|
get_granian_target(),
|
||||||
|
]
|
||||||
|
processes.new_process(
|
||||||
|
command,
|
||||||
|
run=True,
|
||||||
|
show_logs=True,
|
||||||
|
env={
|
||||||
|
constants.SKIP_COMPILE_ENV_VAR: "yes"
|
||||||
|
}, # skip compile for prod backend
|
||||||
|
)
|
||||||
|
except ImportError:
|
||||||
|
console.error(
|
||||||
|
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def output_system_info():
|
def output_system_info():
|
||||||
"""Show system information if the loglevel is in DEBUG."""
|
"""Show system information if the loglevel is in DEBUG."""
|
||||||
if console._LOG_LEVEL > constants.LogLevel.DEBUG:
|
if console._LOG_LEVEL > constants.LogLevel.DEBUG:
|
||||||
|
Loading…
Reference in New Issue
Block a user