Develop microservices
in PHP


by Enrico Zimuel
Principal Software Engineer @ Elastic


PHP Conference Brazil, 7th December 2019

About me

Microservice

...the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API

- Martin Fowler

Source: Introduction to microservices

Source: Introduction to microservices

Pros

  • Separation of concerns
    • Modularity
    • Encapsulation
  • Scalability
    • Horizontally scaling
    • Workload partitioning

Cons

  • Service latency (Docker, Kubernetes)
  • Debugging
  • New architecture challenges:
    • Autodiscovery
    • Telemetry
    • Everything needs to be automated

HTTP

HTTP & microservices

  • HTTP is used everywhere
  • REST API with JSON
  • Error management is critical, use a standard (e.g. application/problem+json)
  • Authentication & authorization (e.g. OAuth2)

HTTP in PHP

HTTP message in PHP

Global variables:

  • $_SERVER
  • $_POST
  • $_GET
  • $_FILES
  • $_COOKIE

HTTP functions in PHP

  • http_response_code()
  • header(), header_remove(), headers_list(), headers_sent()
  • setcookie(), setrawcookie()
  • gethostname(), etc
  • It's a mess!

PSR-7

PHP FIG

  • PHP Framework Interop Group (PHP FIG)
  • PHP Standards Recommendations (PSR)
  • PSR-7 are common interfaces for representing HTTP messages (RFC 7230, 7231, 3986)

PSR-7 interfaces

  • github: psr/http-message
  • Psr\Http\Message\MessageInterface
  • Psr\Http\Message\RequestInterface
  • Psr\Http\Message\ResponseInterface
  • Psr\Http\Message\ServerRequestInterface
  • Psr\Http\Message\StreamInterface
  • Psr\Http\Message\UploadedFileInterface
  • Psr\Http\Message\UriInterface

Example


// Returns an empty array if not found:
$header = $request->getHeader('Accept');

// Testing an header
if ($request->hasHeader('Accept')) {
    /* ... */
}

// Get query string parameters
$query = $request->getQueryParams();

Swoole

  • Swoole is an async programming framework for PHP 7
  • PHP extension, install:
    
    pecl install swoole
    
  • Released under Apache license 2.0
  • More info at swoole.co.uk

Features

  • Event-driven, asynchronous programming for PHP
  • Async TCP / UDP / HTTP / Websocket / HTTP2 client/server side API
  • IPv4 / IPv6 / Unixsocket / TCP/ UDP and SSL / TLS support
  • High performance and scalable
  • Fast serializer / unserializer
  • Milliseconds task scheduler

Swoole: how it works

HTTP Server


use Swoole\Http\Server;

$http = new Server("127.0.0.1", 9501);

$http->on("start", function ($server) {
    echo "Started at http://127.0.0.1:9501\n";
});
$http->on("request", function ($request, $response) {
    $response->header("Content-Type", "text/plain");
    $response->end("Hello World\n");
});

$http->start();

Test: 16K req/sec on CPU i5-2500, 16 GB RAM, PHP 7.2.12, Swoole 4.2.9

Event loop

Middleware

Middleware

A function that gets a request and generates a response


function ($request)
{
    // do something with $request
    return $response;
}

Delegating middleware


function ($request, callable $delegate)
{
    // delegating $request to another middleware
    $response = $delegate($request);
    return $response;
}

Middleware onion

Example: cache


function ($request, callable $delegate) use ($cache)
{
    if ($cache->has($request)) {
        return $cache->get($request);
    }
    $response = $delegate($request);
    $cache->set($request, $response);
    return $response;
}

PSR-15

Common interfaces for HTTP server request handlers and HTTP server middleware components that use HTTP messages as described by PSR-7

Expressive

  • Expressive is part of Laminas project (ex Zend Framework)
  • PHP framework for middleware applications
  • PSR-7 and PSR-15 support
  • Features: routing, dependency injection, templating, error handling

Example


$app->get('/api/ping', function ($request) {
    return JsonResponse(['ack' => time()])
});

// or implement a RequestHandlerInterface
$app->get('/api/ping', App\Handler\PingHandler::class);

PingHandler


namespace App\Handler;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\JsonResponse;

class PingHandler implements RequestHandlerInterface
{
    public function handle(
        ServerRequestInterface $request
    ) : ResponseInterface {
        return new JsonResponse(['ack' => time()]);
    }
}

Route a REST API


$app->route('/api/users[/{id}]', [
    Authentication\AuthenticationMiddleware::class,
    Authorization\AuthorizationMiddleware::class,
    Api\Action\UserAction::class
], ['GET', 'POST', 'PATCH', 'DELETE'], 'api.users');

// or route each HTTP method
$app->get('/api/users[/{id}]', ..., 'api.users.get');
$app->post('/api/users', ..., 'api.users.post');
$app->patch('/api/users/{id}', ..., 'api.users.patch');
$app->delete('/api/users/{id}', ..., 'api.users.delete');

Expressive with Swoole

Install:


composer require zendframework/zend-expressive-swoole

Usage:


vendor/bin/zend-expressive-swoole start

Open your browser at localhost:8080

PHP + Expressive + Swoole

Run a web application from CLI

Simplify the deploy (only PHP no web server)

Benchmark

2-4x faster than Nginx and Apache

Req/sec (mean)
Nginx 1418.23
Apache 1915.62
Swoole 4864.34

Testing environment:
Ubuntu 18.04, Expressive Skeleton 3.2.3, PHP 7.2.12, Nginx 1.14 + FPM,
Apache 2.4.29 + mod_php, Swoole 4.2.9, CPU i5-2500, 16 GB RAM, HD SSD

A microservice
is more than just an API

Microservices needs

  • Logging: aggregation
  • Monitoring: telemetry, metrics
  • Debugging: APM, distributed tracing

3 pillars of observability

  • Elasticsearch is a highly scalable open-source full-text search and analytics engine
  • Near Realtime (NRT)
  • Designed for scale (horizontally)
  • REST API

Example in PHP

Using elastic/elasticsearch-php:


use Elasticsearch\ClientBuilder;

$client = ClientBuilder::create()->build();
$params = [
    'index' => 'my_index',
    'type'  => 'my_type',
    'id'    => 'my_id',
    'body'  => ['testField' => 'abc']
];
$response = $client->index($params);
print_r($response);

Kibana

Your window into the Elastic Stack

Machine learning

Detect the anomalies hiding in your Elasticsearch data

ELK stack

APM

APM & PHP

Thanks!

Follow me: @ezimuel