nyatmeat/roadrunner-bundle

A RoadRunner worker as a Symfony Bundle

Fund package maintenance!
Baldinof

Installs: 21

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 44

Type:symfony-bundle

2.1.2 2021-11-29 20:50 UTC

README

RoadRunner is a high-performance PHP application server, load-balancer, and process manager written in Golang.

This bundle provides a RoadRunner Worker integrated in Symfony, it's easily configurable and extendable.

Installation

Run the following command:

composer require baldinof/roadrunner-bundle

If you don't use Symfony Flex:

  • register Baldinof\RoadRunnerBundle\BaldinofRoadRunnerBundle in your kernel
  • copy default RoadRunner configuration files: cp vendor/baldinof/roadrunner-bundle/.rr.* .

Usage

  • get the RoadRunner binary: vendor/bin/rr get --location bin/
  • run RoadRunner with bin/rr serve or bin/rr serve -c .rr.dev.yaml (watch mode)
  • visit your app at http://localhost:8080

Integrations

Depending on installed bundle & your configuration, this bundles add some integrations:

  • Sentry: configure the request context (if the SentryBundle is installed)
  • Sessions: add the session cookie to the Symfony response (if framework.sessions.enabled config is true)
  • Doctrine Mongo Bundle: clear opened managers after each requests (if DoctrineMongoDBBundle is installed)
  • Doctrine ORM Bundle: clear opened managers and check connection is still usable after each requests (if DoctrineBundle is installed)
  • Blackfire: enable the probe when a profile is requested (if the blackfire extension is installed)

Even if it is not recommended, you can disable default integrations:

baldinof_road_runner:
  default_integrations: false

Middlewares

You can use middlewares to manipulate request & responses. Middlewares must implements Baldinof\RoadRunnerBundle\Http\MiddlewareInterface.

Example configuration:

baldinof_road_runner:
    middlewares:
        - App\Middleware\YourMiddleware

Be aware that

  • middlewares are run outside of Symfony Kernel::handle()
  • the middleware stack is always resolved at worker start (can be a performance issue if your middleware initialization takes time)

Kernel reboots

The Symfony kernel and the dependency injection container are preserved between requests. If an exception is thrown during the request handling, the kernel is rebooted and a fresh container is used.

The goal is to prevent services to be in a non-recoverable state after an error.

To optimize your worker you can allow exceptions that does not put your app in an errored state:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
    kernel_reboot:
      strategy: on_exception
      allowed_exceptions:
        - Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
        - Symfony\Component\Serializer\Exception\ExceptionInterface
        - App\Exception\YourDomainException

If some of your services are stateful, you can implement Symfony\Contracts\Service\ResetInterface and your service will be resetted on each request.

If you are seeing issues and want to use a fresh container on each request you can use the always reboot strategy:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
    kernel_reboot:
      strategy: always

Events

The following events are dispatched throughout the worker lifecycle:

  • Baldinof\RoadRunnerBundle\Event\WorkerStartEvent: Dispatched right before the worker starts listening to requests.
  • Baldinof\RoadRunnerBundle\Event\WorkerStopEvent: Dispatched right before the worker closes.
  • Baldinof\RoadRunnerBundle\Event\WorkerExceptionEvent: Dispatched after encountering an uncaught exception during request handling.
  • Baldinof\RoadRunnerBundle\Event\WorkerKernelRebootedEvent: Dispatched after the symfony kernel was rebooted (see Kernel reboots).

Development mode

Copy the dev config file if it's not present: cp vendor/baldinof/roadrunner-bundle/.rr.dev.yaml .

Start RoadRunner with the dev config file:

bin/rr serve -c .rr.dev.yaml

Reference: https://roadrunner.dev/docs/beep-beep-reload

If you use the Symfony VarDumper, dumps will not be shown in the HTTP Response body. You can view dumps with bin/console server:dump or in the profiler.

Metrics

Roadrunner can collect application metrics, and expose a prometheus endpoint.

Example configuration:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
  metrics:
    enabled: true
    collect:
      user_login:
        type: counter
        help: "Number of logged in user"

And configure RoadRunner:

# .rr.yaml
rpc:
  listen: "tcp:127.0.0.1:6001"

metrics:
  address: "0.0.0.0:9180" # prometheus endpoint

Then simply inject Spiral\RoadRunner\MetricsInterface to record metrics:

class YouController
{
    public function index(MetricsInterface $metrics): Response
    {
        $metrics->add('user_login', 1);

        return new Response("...");
    }
}

Usage with Docker

# Dockerfile
FROM php:7.4-alpine

RUN apk add --no-cache autoconf openssl-dev g++ make pcre-dev icu-dev zlib-dev libzip-dev && \
    docker-php-ext-install bcmath intl opcache zip sockets && \
    apk del --purge autoconf g++ make

WORKDIR /usr/src/app

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

COPY composer.json composer.lock ./

RUN composer install --no-dev --no-scripts --no-plugins --prefer-dist --no-progress --no-interaction

RUN ./vendor/bin/rr get-binary --location /usr/local/bin

COPY . .

ENV APP_ENV=prod

RUN composer dump-autoload --optimize && \
    composer check-platform-reqs && \
    php bin/console cache:warmup

EXPOSE 8080

CMD ["rr", "serve"]