8. Bjoern

bjoern is an HTTP/1.1 WSGI Server for CPython2 and 3 written in C. It claims to be the fastest, smallest and most lightweight WSGI server.

Note

In a load test involving bjoern, cheroot, gunicorn, waitress and werkzeug bjoern (version: 3.0.0) was the clear speed winner against both a ZEO and a non-ZEO Zope instance.

8.1. Prerequisites

Bjoern uses libev and you will need to install both the library and the development header files on your box:

$ sudo apt install libev-dev

8.2. Use bjoern in our buildout

bjoern can be integrated using a shim package called dataflake.wsgi.bjoern.

You can use this package together with plone.recipe.zope2instance to build a bjoern based WSGI setup:

[instance]
recipe = plone.recipe.zope2instance
user = admin:admin
zeo-client = on
zeo-address = 8100
shared-blob = on
blob-storage = ${buildout:directory}/var/blobstorage
eggs =
    Plone
    Pillow
    wsgitraining.site
    dataflake.wsgi.bjoern
wsgi-ini-template = ${buildout:directory}/templates/bjoern.ini.in

In addition to adding dataflake.wsgi.bjoern to the eggs list we specify the location of our bjoern.ini configuration file. It is important to note that this file is not automatically created for us, we have to provide it ourself.

In addition to the PasteDeploy entry point and the p.r.zope2instance integration, dataflake.wsgi.bjoern provides facilities to create a set of Zope configuration files for bjoern with the included kbjoerninstance utility. We will however not use this option since it is easier for us to provide a custom template for the wsgi.ini file to plone.recipe.zope2instance. A suitable template is included in the buildout for the training (file bjoern.ini.in in the templates folder). It is basically a copy from the template contained in the buildout recipe with a slightly changed [server:main] section:

[server:main]
use = egg:dataflake.wsgi.bjoern#main
listen = %(http_address)s
reuse_port = True
...

Note

Let’s run some checks in order to verify that bin/instance actually invokes bjoern: Let’s first find the processes’ PID:

$ ps -ef | grep wsgi.ini
thomas   20009 20006  0 10:26 pts/1    00:00:22 /home/thomas/devel/plone/minimal52/bin/python /home/thomas/devel/plone/minimal52/parts/instance/bin/interpreter /home/thomas/.buildout/eggs/cp37m/Zope-4.1.1-py3.7.egg/Zope2/Startup/serve.py /home/thomas/devel/plone/minimal52/parts/instance/etc/wsgi.ini -d debug-mode=on
...

Using the above PID we can check the process map to see whether bjoern’s C extension has been loaded:

thomas@blake:~$ pmap 17245 | grep bjoern
17245:   /home/thomas/devel/plone/minimal52/bin/python /home/thomas/devel/plone/minimal52/parts/instance/bin/interpreter /home/thomas/.buildout/eggs/cp37m/Zope-4.1.1-py3.7.egg/Zope2/Startup/serve.py /home/thomas/devel/plone/minimal52/etc/bjoern.ini -d debug-mode=on
00007f7537fa5000     44K r-x-- _bjoern.cpython-37m-x86_64-linux-gnu.so
00007f7537fb0000   2048K ----- _bjoern.cpython-37m-x86_64-linux-gnu.so
00007f75381b0000      4K r---- _bjoern.cpython-37m-x86_64-linux-gnu.so
00007f75381b1000      4K rw--- _bjoern.cpython-37m-x86_64-linux-gnu.so

8.2.1. Exercise 1

Additional PasteDeploy entrypoints are also available for the werkzeug and cheroot WSGI servers. Pick one and use it to run Plone behind werkzeug or cheroot.

Solution

cheroot:

You will need to create two files, an .ini template and the buildout configuration. As a starting point, copy bjoern.cfg to cheroot.cfg and templates/bjoern.ini.in to templates/cheroot.ini.in in your buildout directory:

$ cp bjoern.cfg cheroot.cfg
$ cp templates/bjoern.ini.in templates/cheroot.ini.in

Then edit the files so they pull in cheroot as WSGI server rather than bjoern. cheroot.cfg:

...
[instance]
recipe = plone.recipe.zope2instance
user = admin:admin
zeo-client = on
zeo-address = 8100
shared-blob = on
blob-storage = ${buildout:directory}/var/blobstorage
eggs =
    Plone
    Pillow
    wsgitraining.site
    dataflake.wsgi.cheroot
wsgi-ini-template = ${buildout:directory}/templates/cheroot.ini.in

And templates/cheroot.ini.in:

[server:main]
use = egg:dataflake.wsgi.cheroot#main
host = localhost
port = 8080

[app:zope]
...

Note that the dataflake.wsgi.cheroot shim doesn’t understand either reuse_port nor listen. This means we cannot use the http-address parameter passed by plone.recipe.zope2instance. We resolve to specifying host and port in the template instead. dataflake.wsgi.cheroot accepts a couple of other options in the .ini file that we will not consider for this exercise.

Next run buildout with the new configuration:

(wsgitraining) $ buildout -c cheroot.cfg

You can now start your instance as usual:

(wsgitraining) $ bin/instance fg
...
2019-10-07 12:43:08,856 INFO    [Zope:45][MainThread] Ready to handle requests
Starting server in PID 3906.

werkzeug:

For werkzeug the steps are pretty much the same. Copy the configuration files:

$ cp bjoern.cfg werkzeug.cfg
$ cp templates/bjoern.ini.in templates/werkzeig.ini.in

Edit them. werkzeug.cfg:

...
[instance]
recipe = plone.recipe.zope2instance
user = admin:admin
zeo-client = on
zeo-address = 8100
shared-blob = on
blob-storage = ${buildout:directory}/var/blobstorage
eggs =
    Plone
    Pillow
    wsgitraining.site
    dataflake.wsgi.werkzeug
wsgi-ini-template = ${buildout:directory}/templates/werkzeug.ini.in

templates/werkzeug.ini.in:

[server:main]
use = egg:dataflake.wsgi.werkzeug#main
host = localhost
port = 8080

[app:zope]
...

After running buildout -c werkzeug.cfg you can start your Plone instance:

(wsgitraining) $ bin/instance fg
...
2019-10-07 12:58:54,660 INFO    [Zope:45][MainThread] Ready to handle requests
Starting server in PID 4337.
2019-10-07 12:58:54,661 INFO    [werkzeug:122][MainThread]  * Running on http://localhost:8080/ (Press CTRL+C to quit)

Just like the cheroot shim, dataflake.wsgi.werkzeug accepts a couple of additional options in the .ini file that we will not use here.