Route caching helps to speed things up. In Laravel 5, a caching mechanism was introduced to speed up the execution.
The example routes.php
is shown here:
Route::post('reserve-room', 'ReservationController@store'), Route::controllers([ 'auth' => 'AuthAuthController', 'password' => 'AuthPasswordController', ]); Route::post('/bookRoom','ReservationsController@reserve', ['middleware' => 'auth', 'domain'=>'booking.hotelwebsite.com']); Route::resource('rooms', 'RoomsController'), Route::group(['middleware' => ['auth','whitelist']], function() { Route::resource('accommodations', 'AccommodationsController'), Route::resource('accommodations.amenities', 'AccommodationsAmenitiesController'), Route::resource('accommodations.rooms', 'AccommodationsRoomsController'), Route::resource('accommodations.locations', 'AccommodationsLocationsController'), Route::resource('amenities', 'AmenitiesController'), Route::resource('locations', 'LocationsController'), });
By running the following command, Laravel will cache the routes:
$ php artisan route:cache
Then, place them into the following directory:
/vendor/routes.php
Here is a small portion of the resultant file:
<?php /* | Load The Cached Routes |-------------------------------------------------------------------------- | | Here we will decode and unserialize the RouteCollection instance that | holds all of the route information for an application. This allows | us to instantaneously load the entire route map into the router. | */ app('router')->setRoutes( unserialize(base64_decode('TzozNDoiSWxsdW1pbmF0ZVxSb3V0aW5nXF JvdXRlQ29sbGVjdGlvbiI6NDp7czo5OiIAKgByb3V0ZXMiO2E6Njp7czozOiJH RVQiO2E6NTA6e3M6MToiLyI7TzoyNDoiSWxsdW1pbmF0ZVxSb3V0aW5nXFJvdX RlIjo3OntzOjY6IgAqAHVyaSI7czoxOiIvIjtzOjEwOiIAKgBtZXRob2RzIjth OjI6e2k6MDtzOjM6IkdFVCI7aToxO3M6NDoiSEVBRCI7fX ... Db250cm9sbGVyc1xBbWVuaXRpZXNDb250cm9sbGVyQHVwZGF0ZSI7cjoxNDQx O3M6NTQ6Ik15Q29tcGFueVxIyb2xsZXJzXEhvdGVsQ29udHJvbGxlckBkZXN0c m95IjtyOjE2MzI7fX0=')) );
As the DocBlock states, the routes are encoded in base64 and then serialized:
unserialize(base64_decode( … ));
This performs some precompilation. If we base64 decode the contents of the file, we obtain the serialized data. The following code is an extract of the file:
O:34:"IlluminateRoutingRouteCollection":4:{s:9:"*routes"; a:6:{s:3:"GET";a:50:{s:1:"/";O:24:"IlluminateRoutingRoute": 7:{s:6:"*uri";s:1:"/";s:10:"*methods";a:2:{i:0;s:3:"GET";i:1; s:4:"HEAD";}s:9:"*action";a:5:{s:4:"uses";s:50:"MyCompany HttpControllersWelcomeController@index";s:10:"controller"; s:50:"MyCompanyHttpControllersWelcomeController@index"; s:9:"namespace";s:26:"MyCompanyHttpControllers";s:6:"prefix"; N;s:5:"where";a:0:{}}s:11:"*defaults";a:0:{}s:9:"*wheres"; a:0:{}s:13:"*parameters";N;s:17:"*parameterNames";N; }s:4:"home";O:24:"Illumin… "MyCompanyHttpControllersHotelController@destroy";r:1632;}}
If the /vendor/routes.php
file exists, it is used instead of the routes.php
file that is located at /app/Http/routes.php
. If at some point using the route caching file is no longer desired, use the following artisan
command:
$ php artisan route:clear
This command will delete the cached routes
file and Laravel will begin using the /app/Http/routes.php
file again.
It is important to note that if there are any closures used in the routes.php
file, then caching will fail. Here is an example of a closure in a route:
Route::get('room/{$id}', function(){ return Room::find($id); });
It is inadvisable to use closure in the routes.php
file for any reason. To be able to use route caching, relocate the code used within the closure into a controller.
All of this work speeds up an important part of the request life cycle, the routing. In Laravel, the routing class is located inside the illuminate/routing
namespace:
<?php namespace IlluminateRouting; use Closure; use LogicException; use ReflectionFunction; use IlluminateHttpRequest; use IlluminateContainerContainer; use IlluminateRoutingMatchingUriValidator; use IlluminateRoutingMatchingHostValidator; use IlluminateRoutingMatchingMethodValidator; use IlluminateRoutingMatchingSchemeValidator; use SymfonyComponentRoutingRoute as SymfonyRoute; use IlluminateHttpExceptionHttpResponseException; use SymfonyComponentHttpKernelExceptionNotFoundHttpException;
Examining the use
operators, it is clear that the routing mechanism consists of quite a few classes. The most important line is as follows:
use SymfonyComponentRoutingRoute as SymfonyRoute;
Laravel uses Symfony's routing class. However, a new routing package written by Nikita Popov has emerged. FastRoute
is a fast request router that is faster than other routing packages and addresses some of the issues of the existing routing packages. This component is one of the major advantages of the of the Lumen microframework.
In soda marketing terms, Lumen could be considered Laravel Light or Laravel Zero. In addition to using the FastRoute
routing package, many packages have been removed from Lumen, allowing it to be minimal and reduce its footprint.
The packages in Laravel and Lumen were compared and listed in the following table. These packages are installed when the following command is run:
$ composer update –-no-dev
The preceding command is used when the development is completed and the application is ready for deployment on the server. Tools such as PHPUnit and PHPSpec are obviously excluded at this stage.
The package names are aligned to illustrate where the packages are present in both Laravel and Lumen:
Laravel Packages |
Lumen Packages |
---|---|
- |
|
|
- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- |
|
|
|
- |
|
|
|
|
|
- |
|
- |
|
|
|
- |
|
|
|
- |
|
- |
|
|
|
|
|
|
|
|
|
|
|
- |
|
- |
|
|
|
|
|
- |
|
- |
|
- |
|
- |
|
- |
|
- |
|
- |
|
- |
|
|
|
|
|
- |
|
- |
|
|
|
|
|
- |
|
- |
|
- |
|
- |
|
- |
|
- |
At the time of writing, there are 51 packages (shown in the left column) that are installed in Laravel 5.0 using the nondevelopment configuration. Compare this number of packages to the number of packages installed in Lumen (shown in the right column)—there are just 24.
The aforementioned nikic/fast-route
package is the only package that Lumen has which Laravel does not. The symfony/routing
package is the complimentary package found in Laravel.
We will use an example, a simple public-facing RESTful API. This RESTful API displays the names and addresses of a list of accommodations in JSON format to any user via GET
:
ext/mcrypt
is not needed.nesbot/carbon
is not needed. Since there is no HTML interface, then the following libraries involved in testing the HTML of an application, symfony/css-selector
and symfony/dom-crawler
, will not be needed.illuminate/mail
nor swiftmailer/swiftmailer
is needed.league/flysystem
.symfony/console
is not needed.illuminate/redis
may be left out.vlucas/phpdotenv
is not needed.It is clear that the decision to remove certain packages has been done carefully so as to lighten up Lumen as needed with the simplest of applications in mind.
Laravel has another great mechanism for helping its performance in the enterprise: read and write. This is related to database performance, but the functionality is so easy to set up that any application can take advantage of its usefulness.
Regarding MySQL, the original MyISAM database engine required a lock on the entire table during inserts, updates, and deletes. This caused massive bottlenecks during large operations that modified the data and select queries waited to access these tables. With the introduction of InnoDB, UPDATE
, INSERT
, and DELETE
SQL statements required a lock only at the row-level. This tremendously impacted performance, since selects could read from various parts of a table where other operations were taking place.
MariaDB, a MySQL fork, claims faster performance than the traditional MySQL. Replacing the database engine with TokuDB will give even higher performance, especially in a big data setting.
Another mechanism to speed up performance of the database is the use of a master/slave configuration. In the following diagram, all of the operations are performed on a single table. The inserts and updates will lock down single rows and the select statements will be performed as allocated.
The master/slave configuration uses a master table that allows SELECT
, UPDATE
, and DELETE
statements. These statements modify the table or write to it. There may also be multiple master tables. Each master table is kept continually synchronized: changes made to any table needs to be communicated to the master table.
The slave table is a slave to the master. It depends on the master table for its changes. SQL clients can only perform read operations (SELECT
) from it. There may also be multiple slaves that depend on one or more multiple master tables. The master table communicates all of its changes to all the slaves. The following diagram shows the basic architecture of a master/slave setup:
This continual synchronization adds slight overhead to the database structure; however, it presents important advantages:
Since only SELECT
statements can be performed on the slave table while INSERT
, UPDATE
, and DELETE
statements can be performed on the master table, the slave table is free to accept many SELECT
statements freely, without having to "wait" for any operations involving the same rows to finish.
An example of this would be a currency exchange rate or stock price table. This table would be continually updated in real time with the latest values, possibly even many times per second. Obviously, a website that allows many users to access this information could potentially have thousands of visitors. Also, the web page used to display this data may make continual multiple requests per user.
Performing many SELECT
statements would be slightly slower when there are UPDATE
statements that need to access the same data at the same time.
By using a master/slave configuration, the SELECT
statements would be performed only on the slave table. This table receives only the data that has changed in an extremely optimized way.
In plain PHP using a library such as mysqli
, there could be two database connections configured:
$master=mysqli_connect('127.0.0.1:3306','dbuser','dbpassword','mydatabase'), $slave=mysqli_connect('127.0.0.1:3307','dbuser','dbpassword','mydatabase'),
In this simplified example, the slave is set up on the same server machine. In a real application, it would most likely be set up on another server machine to take advantage of separate hardware.
Then, all of the SQL statements which involve a write statement would be performed on the slave and read would be performed on the master.
This would add some overhead to the programming efforts, as a different connection would need to be passed into each SQL statement:
$result= mysqli_real_query($master,"UPDATE exchanges set rate='1.345' where exchange_id=2"); $result= mysqli_query($slave,"SELECT rate from exchanges where exchange_id=2");
In the preceding code example, it would be prudent to remember which SQL statements should be used for the master and which SQL statements should be used for the slave.
As stated before, code written in Eloquent is converted into fluent query-builder code. This code is then converted to PDO, which is a standard wrapper around the various database drivers.
Laravel provides the ability to manage master/slave configurations though its read/write configuration. This allows programmers to write Eloquent and fluent query-builder code without having to worry about whether the queries will be executed on the master or slave table. Also, a software project that starts out with a non-master/slave configuration and later needs to scale up to a master/slave setup will only need to change one aspect of the database configuration. The database configuration file is located at config/database.php
.
As an element of the connections
array, an entry with the key mysql
will be created with the following configuration:
'connections' => 'mysql' => [ 'read' => [ 'host' => '192.168.1.1',
'password' => 'slave-Passw0rd', ], 'write' => [ 'host' => '196.168.1.2',
'username' => 'dbhostusername' ], 'driver' => 'mysql', 'database' => 'database', 'username' => 'dbusername', 'password' => 's0methingSecure', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', ],
The read and write represent slave and master respectively. Since the parameters cascade, if the username, password, and database name are the same, then only the IP address of the host name needs to be listed. However, any values can be overridden. In this example, the read has a password that is different from that of the master and the write has a username that is different from the slave.