Deployment
- Resource Management
- Example settings
- Using Multiple Processes
- Deploying A Lona Script
- Lona
- Apache2
- Systemd
- Deploying A Lona Project
- Lona
- Apache2
- Systemd
Resource Management
Lona is based on multi-threading (more information: Asynchronous Code). That means that every hook of a lona.LonaView like handle_request() or handle_input_event() block one thread each until your business logic finishes.
When planing resources for your application keep in mind that every view that runs can use up to three threads at a time (one for handle_request(), one for handle_input_event() and one for messaging between server and client).
Lona splits threading up in two pools: the view_runtime_pool and the worker_pool, configured through settings.MAX_RUNTIME_THREADS and settings.MAX_WORKER_THREADS.
LonaView.handle_request(), the main entry point for view business logic, runs in view_runtime_pool separated from all other Lona tasks because these are expected to run for a very long time potentially (up to days or weeks).
All other hooks and messaging runs in worker_pool. Business logic that runs there is expected to don't run that long, but with a much higher rate.
Example settings
These are example settings for an application that servers 50 concurrent users with one running view for each user. 50 views can run at a time. Because a view can use up to two worker threads the worker_pool should be at least twice as big as view_runtime_pool.
When running as a Lona script, static files like CSS, Javascript etc. get served by Lona itself. settings.MAX_STATIC_THREADS configures how many threads handle static file serving.
MAX_RUNTIME_THREADS = 50
MAX_WORKER_THREADS = 100
MAX_STATIC_THREADS = 20
Using Multiple Processes
Because of the Python Gil only one python thread can run at a time. Depending on your application and hardware it can make sense to start the same Lona project or script on multiple ports and use a load balancer like Apache2 to distribute the load to multiple Python processes.
All Apache configs in this article use Apaches load balancing feature to implement reverse proxying. To expand the balancing pool just add more than one member on port 8080.
Deploying A Lona Script
In this section we will deploy a simple Lona Script using systemd and Apache2 as reverse proxy
Lona
# installing the necessary debian/ubuntu packages
$ sudo apt install apache2 python3 python3-venv
# setup /srv/lona and python virtualenv
$ sudo mkdir -p /srv/lona
$ sudo python3 -m venv /srv/lona/env
$ source /srv/lona/env/bin/activate
$ (env) pip install lona
# setup lona script
$ sudo touch /srv/lona/my-script.py
# change ownership of /srv/lona to www-data
$ sudo chown -R www-data:www-data /srv/lona
# /srv/lona/my-script.py
from lona.html import HTML, Button, Div, H1
from lona import LonaApp, LonaView
app = LonaApp(__file__)
app.settings.MAX_RUNTIME_THREADS = 50
app.settings.MAX_WORKER_THREADS = 100
app.settings.MAX_STATIC_THREADS = 20
@app.route('/')
class MyView(LonaView):
def handle_request(self, request):
message = Div('Button not clicked')
button = Button('Click me!')
html = HTML(
H1('Click the button!'),
message,
button,
)
self.show(html)
# this call blocks until the button was clicked
input_event = self.await_click(button)
if input_event.node == button:
message.set_text('Button clicked')
return html
app.run(port=8080)
Apache2
$ sudo a2enmod headers rewrite proxy proxy_wstunnel proxy_http proxy_balancer lbmethod_byrequests
# /etc/apache2/sites-available/lona.conf
# needed modules: headers rewrite proxy proxy_wstunnel proxy_http proxy_balancer lbmethod_byrequests
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# application
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<Proxy balancer://httprewrite>
BalancerMember http://localhost:8080 route=1
ProxySet stickysession=ROUTEID
</Proxy>
<Proxy balancer://wsrewrite>
BalancerMember ws://localhost:8080 route=1
ProxySet stickysession=ROUTEID
</Proxy>
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule (.*) balancer://wsrewrite%{REQUEST_URI} [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule (.*) balancer://httprewrite%{REQUEST_URI} [P,L]
</VirtualHost>
$ sudo a2ensite lona.conf
Systemd
# /etc/systemd/system/lona-server.service
[Unit]
Description=Lona Server
[Service]
User=www-data
Type=simple
WorkingDirectory=/srv/lona/
ExecStart=/srv/lona/env/bin/python /srv/lona/my-script.py
[Install]
WantedBy=multi-user.target
$ sudo systemctl enable lona-server.service
$ sudo systemctl start lona-server.service
Deploying A Lona Project
In this section we will deploy lona-project-template using Apache2 and systemd.
Lona collects all static file at startup once and places them in /srv/lona/static to make them available for Apache.
The Lona server service sets up a Lona Shell Server using a unix domain socket in /srv/lona/lona-project-template/lona_project for debugging and monitoring.
When the project is deployed like defined here, you can attach a Lona Shell when the server is running.
$ sudo /srv/lona/lona-project-template/env/bin/rlpython file:///srv/lona/lona-project-template/lona_project/socket
Lona
# installing the necessary debian/ubuntu packages
$ sudo apt install apache2 build-essential python3 python3-venv
# setup /srv/lona and python virtualenv
$ sudo mkdir -p /srv/lona
$ sudo mkdir -p /srv/lona/static
$ sudo git clone https://github.com/lona-web-org/lona-project-template /srv/lona/lona-project-template
$ cd /srv/lona/lona-project-template
$ sudo make env
# change ownership of /srv/lona to www-data
$ sudo chown -R www-data:www-data /srv/lona
Apache2
$ sudo a2enmod headers rewrite proxy proxy_wstunnel proxy_http proxy_balancer lbmethod_byrequests
# /etc/apache2/sites-available/lona.conf
# needed modules: headers rewrite proxy proxy_wstunnel proxy_http proxy_balancer lbmethod_byrequests
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# static files
DocumentRoot /srv/lona/static
Alias /static /srv/lona/static
<Directory /srv/lona/static>
Require all granted
</Directory>
# application
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<Proxy balancer://httprewrite>
BalancerMember http://localhost:8080 route=1
ProxySet stickysession=ROUTEID
</Proxy>
<Proxy balancer://wsrewrite>
BalancerMember ws://localhost:8080 route=1
ProxySet stickysession=ROUTEID
</Proxy>
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule !^static($|/) balancer://wsrewrite%{REQUEST_URI} [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule !^static($|/) balancer://httprewrite%{REQUEST_URI} [P,L]
</VirtualHost>
$ sudo a2ensite lona.conf
Systemd
# /etc/systemd/system/lona-server.service
[Unit]
Description=Lona Server
[Service]
User=www-data
Type=simple
WorkingDirectory=/srv/lona/lona-project-template/lona_project
ExecStartPre=/srv/lona/lona-project-template/env/bin/lona collect-static /srv/lona/static/ -s settings.py deployment_settings.py --clean
ExecStart=/srv/lona/lona-project-template/env/bin/lona run-server --host=localhost --port=8080 -s settings.py deployment_settings.py --shell-server-url=file://socket
[Install]
WantedBy=multi-user.target
$ sudo systemctl enable lona-server.service
$ sudo systemctl start lona-server.service