Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a new article about applications with multiple kernels #6840

Merged
merged 11 commits into from
Dec 15, 2016
182 changes: 182 additions & 0 deletions request/multiple_kernels.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
.. index::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be in the configuration/ directory?

single: kernel, performance

How To Create Symfony Applications with Multiple Kernels
========================================================

In most Symfony applications, incoming requests are processed by the
``web/app.php`` front controller, which instantiates the ``app/AppKernel.php``
class to create the application kernel that loads the bundles and handles the
request to generate the response.

This single kernel approach is a convenient default provided by the Symfony
Standard edition, but Symfony applications can define any number of kernels.
Whereas :doc:`environments </configuration/environments>` execute the same
application with different configurations, kernels can execute different parts
of the same application.

These are some of the common use cases for creating multiple kernels:

* An application that defines an API could define two kernels for performance
reasons. The first kernel would serve the regular application and the second
one would only respond to the API requests, loading less bundles and enabling
less features;
* A highly sensitive application could define two kernels. The first one would
only load the routes that match the parts of the application exposed publicly.
The second kernel would load the rest of the application and its access would
be protected by the web server;
* A micro-services oriented application could define several kernels to
enable/disable services selectively turning a traditional monolith application
into several micro-applications.

Adding a new Kernel to the Application
--------------------------------------

Creating a new kernel in a Symfony application is a three-step process:

1. Create a new front controller to load the new kernel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another way to do this (which is done on a BIG Symfony site I know of): keeping app.php and app_dev.php and using some URL matching to load different kernels. For example, if the URL starts with /admin, then load the AdminKernel, else AppKernel. I think it's worth mentioning both. So, what about:

A) Making step (1) actually step (3) (I think creating the kernel and hooking up the configuration makes sense to have first, then finally show how you can instantiate the now-created kernel)

B) Rename this step to Step 3) Executing the kernel from a front controller

C) Mention the if statement approach above (it could be mentioned first or second).

2. Create the new kernel class;
3. Define the configuration loaded by the new kernel.

The following example shows how to create a new kernel for the API of a given
Symfony application.

Step 1) Create a new Front Controller
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Instead of creating the new front controller from scratch, it's easier to
duplicate the existing ones. For example, create ``web/api_dev.php`` from
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.> ... from scratch, it's easier to duplicate the existing ones

(it just sounds a bit more relaxed)

``web/app_dev.php`` and ``web/api.php`` from ``web/app.php``.

Then, update the code of the new front controllers to instantiate the new kernel
class instead of the usual ``AppKernel`` class::

// web/api.php
// ...
$kernel = new ApiKernel('prod', false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you don't put a require call to load the file defining it, you already need to show the update of the autoload config

// ...

// web/api_dev.php
// ...
$kernel = new ApiKernel('dev', true);
// ...

Step 2) Create the new Kernel Class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now you need to define the ``ApiKernel`` class used by the new front controller.
The easiest way to do this is by duplicating the existing ``app/AppKernel.php``
file and make the needed changes.

In this example, the ``ApiKernel`` will load less bundles than AppKernel. Be
sure to also change the location of the cache, logs and configuration files so
they don't collide with the files from ``AppKernel``::

// app/ApiKernel.php
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class ApiKernel extends Kernel
{
public function registerBundles()
{
// load only the bundles strictly needed for the API...
}

public function getCacheDir()
{
return dirname(__DIR__).'/var/cache/api/'.$this->getEnvironment();
}

public function getLogDir()
{
return dirname(__DIR__).'/var/logs/api';
}

public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load($this->getRootDir().'/config/api/config_'.$this->getEnvironment().'.yml');
}
}

Step 3) Define the Kernel Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Finally, define the configuration files that the new ``ApiKernel`` will load.
According to the above code, this config will live in the ``app/config/api/``
directory.

The new configuration can be created from scratch when you load just a few
bundles, because it will be very simple. Otherwise, duplicate the existing
config files or better, import them and override the needed options:

.. code-block:: yaml

# app/config/api/config_dev.yml
imports:
- { resource: ../config_dev.yml }

# override option values ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is to share some configuration files, especially if both kernels use some of the same bundles that you want configured in the same ways. I'm haven't done this enough to settle on the best organization for this... but we could at least mention it. Perhaps a app/config/shared.yml file where we show just a little bit of config and mention how you could import this from the config.yml file of both kernels.

Executing Commands with a Different Kernel
------------------------------------------

The ``bin/console`` script used to run Symfony commands always uses the default
``AppKernel`` class to build the application and load the commands. If you need
to execute console commands using the new kernel, duplicate the ``bin/console``
script and rename it (e.g. ``bin/api``).

Then, replace the ``AppKernel`` instantiation by your own kernel instantiation
(e.g. ``ApiKernel``) and now you can execute commands using the new kernel
(e.g. ``php bin/api cache:clear``) Now you can use execute commands using the
new kernel.

.. note::

The commands available for each console script (e.g. ``bin/console`` and
``bin/api``) can differ because they depend on the bundles enabled for each
kernel, which could be different.

Rendering Templates Defined in a Different Kernel
-------------------------------------------------

If you follow the Symfony Best Practices, the templates of the default kernel
will be stored in ``app/Resources/views/``. Trying to render those templates in
a different kernel will result in a *There are no registered paths for
namespace "__main__"* error.

In order to solve this issue, add the following configuration to your kernel:

.. code-block:: yaml

# api/config/config.yml
twig:
paths:
# allows to use app/Resources/views/ templates in the ApiKernel
"%kernel.root_dir%/../app/Resources/views": ~

Adding more Kernels to the Application
--------------------------------------

If your application is very complex and you create several kernels, it's better
to store them in their own directories instead of messing with lots of files in
the default ``app/`` directory:

.. code-block:: text

project/
├─ app/
│ ├─ ...
│ ├─ config/
│ └─ AppKernel.php
├─ api/
│ ├─ ...
│ ├─ config/
│ └─ ApiKernel.php
├─ ...
└─ web/
├─ ...
├─ app.php
├─ app_dev.php
├─ api.php
└─ api_dev.php