Bernard the Tactician
Background processing in PHP
Márk Sági-Kazár
The Problem
Some ecommerce site
- reseller
- sell from multiple supplier
- customer order -> report back
- send orders to (multiple) remote APIs
Common issues...
- uploading an image fails or takes too long
- placing order fails or takes too long
- page loading takes too long
...and their causes
- improper error handling
- badly scaled application
- too much responsibilities on the frontend
Background processing...
- runs in the background
- runs without user interaction
- (does not block the "foreground" process)
Definition
Parallel execution
- run multiple actions in parallel
- might block the "foreground" process
- eg. multi-threading
Deferred execution
- run multiple actions
- different place/time/environment
- the result of execution is not important for the original process
- eg. job queues
Cron jobs
Deferred execution FTW
- removes responsibilities from the frontend
- may include retry strategy
- improves scalability
...in PHP
Parallel execution
Multi-threading: pthreads
- usual PHP builds are not thread-safe
- many PHP extensions are not thread-safe
Deferred execution
Job queues
- one or more queues
- one or more workers
- put the job in the queue and forget
Message Queues
- inter-process communication
- asynchronous
- operated by a message broker
Message Broker
- coordinates communication between parties
- validates/routes/transforms/stores messages
- ensures message delivery
- eg. Rabbit MQ, Apache ActiveMQ/Kafka
Publish/Subscribe pattern
- most common messaging pattern (in MQs)
- sender publishes the message
- message broker stores
- receiver subscribes for specific messages
Beanstalk
Golden rule
Replace the first character with "ph"
<?php
use Pheanstalk\Pheanstalk;
// Configuration
$pheanstalk = new Pheanstalk('127.0.0.1');
// Publisher
$pheanstalk
->useTube('order')
->put("order product #1234 from supplier #5");
// Subscriber
$job = $pheanstalk
->watch('order')
->reserve();
// Process order
$data = $job->getData();
// Delete job from the queue
$pheanstalk->delete($job);
Bernard
- MQ abstraction
- supports the major ones
- https://github.com/bernardphp/bernard
<?php
// Configuration
$producer = ...;
$consumer = ...;
$queue = ...;
// Publisher (== producer)
$producer->produce(
new Message\DefaultMessage('Order', [
'product_id' => 1234,
'supplier_id' => 5,
]),
$queue->getName()
);
// Subscriber (== consumer)
$consumer->consume($queue);
Command pattern
- message
- information about an action to be executed
- no return value, but can trigger events
- improves testability
Tactician
- simple command bus implementation
- uses middlewares internally
- maintained by The League (Ross Tuck)
- http://tactician.thephpleague.com
<?php
declare(strict_types=1);
final class Order
{
private $productId;
private $supplierId;
public function __construct(int $productId, int $supplierId)
{
$this->productId = $productId;
$this->supplierId = $supplierId;
}
public function getProductId() : int
{
return $this->productId;
}
public function getSupplierId() : int
{
return $this->supplierId;
}
}
final class OrderHandler
{
// some dependencies
public function handle(Order $command)
{
// do something
}
}
<?php
// Configuration
$commandBus = ...;
$consumer = ...;
$queue = ...;
// Producer
$command = new Order(1234, 5);
$commandBus->handle($command);
// Consumer
$consumer->consume($queue);
Demo
Questions?
Thank you!
Bernard the Tactician
By Márk Sági-Kazár
Bernard the Tactician
- 1,790