Commit ab30bc41 authored by Mickael Pastor's avatar Mickael Pastor

fusion

parent 414db195
Pipeline #4769 passed with stage
in 2 minutes and 28 seconds
vendor/
composer.lock
.idea/
config/app.php
gitlab-ci_local.sh
gitlab-ci_local/
.php_cs.cache
\ No newline at end of file
image: lganee/fatapache:a4
cache:
key: build-cache
paths:
- vendor/
variables:
COMPOSER_ALLOW_SUPERUSER: "1"
test:
script:
- composer update
- vendor/phpunit/phpunit/phpunit --coverage-text --colors=never
# Beanstalk
[![License](https://img.shields.io/badge/licence-CeCILL%20v2-blue.svg)](http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html)
[![build status](https://gitlab.libriciel.fr/CakePHP/cakephp-beanstalk/badges/master/build.svg)](https://gitlab.libriciel.fr/CakePHP/cakephp-beanstalk/pipelines)
[![coverage report](https://gitlab.libriciel.fr/CakePHP/cakephp-beanstalk/badges/master/coverage.svg)](https://gitlab.libriciel.fr/CakePHP/cakephp-beanstalk/pipelines)
Plugin Cakephp 3.
Interface entre le service __beanstalkd__, votre application et vos workers.
## Prérequis
Un serveur Beanstalkd est indispensable.
```bash
sudo apt-get install beanstalkd
```
Si vous voulez conserver les jobs après le redémarrage du service, il vous faut
modifier la conf.
```bash
sudo nano /etc/default/beanstalkd
```
Décommettez la dernière ligne :
`BEANSTALKD_EXTRA="-b /var/lib/beanstalkd"`
Autre méthode : lancez __beanstalkd__ avec l'option __-d__
## Installation
```bash
composer config repositories.libriciel/cakephp-beanstalk git https://gitlab.libriciel.fr/CakePHP/cakephp-beanstalk.git
composer require libriciel/cakephp-beanstalk ^2.0
```
Ajoutez le plugin dans votre Application.php
```php
$this->addPlugin('Beanstalk');
```
### Base de données
Vous pouvez au choix, utiliser le SQL (postgres) fourni dans **config/schema/beanstalk.sql**, ou bien utiliser le plugin migration :
```bash
bin/cake migrations migrate --plugin Beanstalk
```
## Utilisation
Maintenant que vous disposez de votre serveur Beanstalkd, il va vous falloir
un ou plusieurs workers selon le besoin.
Exemple sur vendor/libriciel/cakephp-beanstalk/exemple/DefaultWorker.php
Copiez ce fichier vers src/Shell/Worker
Ajoutez à votre configuration :
```php
Configure::write([
'Beanstalk' => [
/**
* Connection au serveur Beanstalkd
*/
'host' => '127.0.0.1',
'port' => 11300,
'timeout' => null,
'persistant' => false,
/**
* Chemin des classes de workers
*/
'workerPaths' => [
APP . 'Shell' . DS . 'Worker'
],
/**
* Configurations liées aux tests des workers
*
* - timeout: durée maximum en secondes entre l'émission du test et la
* consultation du résultat
*/
'tests' => [
'timeout' => 10,
],
/**
* Configuration optionnelle
*/
'table_jobs' => 'Beanstalk.BeanstalkJobs',
'table_workers' => 'Beanstalk.BeanstalkWorkers',
'classname', \Beanstalk\Utility\Beanstalk::class,
'PheanstalkClassname' => \Pheanstalk\Pheanstalk::class
]
]);
```
### Lancer un worker
__Options:__
- __--dir__ Dossier de la classe du worker
- __--one-job__ N'effectue qu'un seul job - utile pour un worker sous docker
- __--suffix__ Permet de spécifier le dossier worker (default: Worker)
- __--table-workers__ Permet de spécifier la table utilisée pour stocker les informations sur le worker lancé (default: Beanstalk.BeanstalkWorkers)
- __--tube__ Nom du tube (par défaut: _nom du worker_)
- __--unique__ Lance le worker seulement s'il est seul sur son tube
__Arguments:__
- __worker__ Nom du worker (optional)
Lancez votre shell :
```bash
bin/cake worker default_worker
```
On peut lancer plusieurs workers en même temps. Attention car un worker peut
occuper un thread à 100% jusqu’à la fin de la file de _job_. Si le nombre de
workers dépasse celui du nombre de cœur la charge sera équitablement répartie.
Si les workers sont sur le même serveur que la page web, l’expérience
utilisateur peut s'en retrouver affectée.
## L'utilitaire Beanstalk
Permet de communiquer avec le service __Beanstalkd__ tout en manipulant la base de données de l'application afin de conserver certaines informations.
### La methode __\_\_construct()__
Prends en paramètre le tube sur lequel va travailler le worker. Il est bon
d'avoir un tube par worker.
Les autres paramètres sont pour la connexion au serveur __beanstalkd__.
### emit()
Ajoute un Job dans le tube, avec le data passé en paramètres.
### getNext()
Utile pour boucler dessus, il se charge de réserver le prochain job et l'affecter
à `Worker->job`
Si aucun job n'est à faire, il attend tranquillement qu'un nouveau job arrive
sans prendre de la charge serveur.
### getData()
Attention, peut renvoyer faux si la signature des données n'est pas validée.
Cette particularité a pour but d'éviter les conflits si une autre application
utilise le même tube.
Sinon, la donnée renvoyée peut être de tous types grâce à une conversion json.
### done()
Supprime le job car celui-ci est terminé
### exemple:
```php
$Beanstalk = new \Beanstalk\Utility\Beanstalk('mon-tube');
$Beanstalk->emit('foo'); // Envoi un message dans le tube
while ($Beanstalk->getNext()) {
$data = $Beanstalk->getData();
$Beanstalk->done();
if ($data === 'foo') {
break;
}
}
```
## Faire un worker
Créez une classe ___\<NomDu\>Worker___ (suffixe _Worker_ par défaut) qui implémente __WorkerInterface__.
Vous pouvez étendre la classe __AbstractWorker__ qui possède déjà la mécanique de base d'un worker (il vous faudra juste définir la méthode principale: work()).
### exemple:
```php
<?php
namespace App\Shell\Worker;
use Beanstalk\Shell\AbstractWorker;
use Beanstalk\Shell\WorkerInterface;
class ExempleWorker extends AbstractWorker implements WorkerInterface
{
public function work($data)
{
echo $data;
}
}
```
{
"name": "libriciel/cakephp-beanstalk",
"description": "Permet d'utiliser beanstalk pour lancer des background processes.",
"type": "cakephp-plugin",
"authors": [
{
"name": "lganee",
"email": "ludovic.ganee@libriciel.coop"
}
],
"require": {
"php": ">=5.6",
"ext-json": "*",
"pda/pheanstalk": "~3.1",
"cakephp/cakephp": "^3.7"
},
"suggest": {
"cakephp/migrations": "Pour une installation via le plugin migration (voir config/Migrations/)"
},
"autoload": {
"psr-4": {
"Beanstalk\\": "src",
"Beanstalk\\Test\\": "tests"
}
},
"require-dev": {
"phpunit/phpunit": "^6.3"
}
}
<?php
use Migrations\AbstractMigration;
class Initial extends AbstractMigration
{
public function up()
{
$this->table('beanstalk_jobs')
->addColumn('jobid', 'integer', [
'default' => null,
'limit' => 10,
'null' => false,
])
->addColumn('tube', 'string', [
'default' => 'default',
'limit' => 255,
'null' => true,
])
->addColumn('priority', 'integer', [
'default' => '1024',
'limit' => 10,
'null' => true,
])
->addColumn('delay', 'integer', [
'default' => 0,
'limit' => 11,
'null' => true,
])
->addColumn('ttr', 'integer', [
'default' => 60,
'limit' => 11,
'null' => true,
])
->addColumn('last_status', 'string', [
'default' => 'ready',
'limit' => 255,
'null' => true,
])
->addColumn('created', 'timestamp', [
'default' => null,
'limit' => null,
'null' => false,
])
->addColumn('user_id', 'integer', [
'default' => null,
'limit' => 10,
'null' => true,
])
->addIndex(
[
'user_id',
]
)
->create();
$this->table('beanstalk_workers')
->addColumn('name', 'string', [
'default' => null,
'limit' => 255,
'null' => true,
])
->addColumn('tube', 'string', [
'default' => null,
'limit' => 255,
'null' => true,
])
->addColumn('path', 'string', [
'default' => null,
'limit' => 255,
'null' => true,
])
->addColumn('pid', 'integer', [
'default' => null,
'limit' => 10,
'null' => true,
])
->addColumn('last_launch', 'timestamp', [
'default' => null,
'limit' => null,
'null' => true,
])
->addIndex(
[
'name',
],
['unique' => true]
)
->create();
$this->table('beanstalk_jobs')
->addForeignKey(
'user_id',
'users',
'id',
[
'update' => 'CASCADE',
'delete' => 'SET_NULL'
]
)
->update();
}
public function down()
{
$this->table('beanstalk_jobs')
->dropForeignKey(
'user_id'
);
$this->dropTable('beanstalk_jobs');
$this->dropTable('beanstalk_workers');
}
}
<?php
use Migrations\AbstractMigration;
class AddHostnameToBeanstalkWorkers extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-change-method
* @return void
*/
public function change()
{
$table = $this->table('beanstalk_workers');
$table->addColumn('hostname', 'string', [
'default' => null,
'limit' => 255,
'null' => false,
]);
$table->update();
}
}
<?php
use Migrations\AbstractMigration;
class DropUniqueFromBeanstalkWorkers extends AbstractMigration
{
public function up()
{
$table = $this->table('beanstalk_workers');
$table->removeIndex(['name']);
$table->save();
}
public function down()
{
$table = $this->table('beanstalk_workers');
$table->addIndex(['name'], ['unique' => true]);
$table->save();
}
}
<?php
use Migrations\AbstractMigration;
class AddErrorsToBeanstalkJobs extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-change-method
* @return void
*/
public function change()
{
$table = $this->table('beanstalk_jobs');
$table->addColumn('errors', 'text', [
'default' => null,
'null' => true,
]);
$table->update();
}
}
BEGIN;
CREATE TABLE beanstalk_jobs (
id SERIAL NOT NULL PRIMARY KEY,
jobid INTEGER NOT NULL,
tube VARCHAR(255) DEFAULT 'default',
priority INTEGER DEFAULT 1024,
delay INTEGER DEFAULT 0,
ttr INTEGER DEFAULT 60,
last_status VARCHAR(255) DEFAULT 'ready',
errors TEXT DEFAULT null,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE beanstalk_workers (
id SERIAL NOT NULL PRIMARY KEY,
name VARCHAR(255),
tube VARCHAR(255),
path VARCHAR(255),
pid INTEGER,
last_launch TIMESTAMP,
hostname VARCHAR(255)
);
COMMIT;
\ No newline at end of file
<?php
/**
* Exemple d'un worker
*
* Donne un exemple d'utilisation de worker. Celui-ci calculera la quantitée
* de nombre premiers en fonction d'un message Beanstalk qui indique un nombre.
* Le worker calculera les nombres premiers inferieur au nombre indiqué.
* ex: si 10 est envoyé en message, [2,3,5,7] donc 4 nombre premiers trouvé.
*
* @category Beanstalk
*
* @author Ludovic Ganée <ludovic.ganee@libriciel.coop>
* @copyright (c) 2017, Libriciel
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
*/
namespace App\Shell\Worker;
use Beanstalk\Shell\AbstractWorker;
use Beanstalk\Utility\Beanstalk;
/**
* Classe d'exemple d'un worker.
*
* Possède les fonction <b>work()</b> et <b>keepWorking()</b> obligatoire.
* <b>work()</b> execute le travail à accomplir. Les exception qu'il envoi sont capturées
* par AbstractWorker qui emet un message d'erreur via Beanstalk.
*
* <b>keepWorking()</b> permet de continuer ou pas un nouveau cicle de travail.
* Par défaut, il dépendra de la conf sur l'attribut 'keep_working' mais on peut
* décider d'un fonctionnement different.
*
* <b>nb_premiers()</b> permet bien evidement de calculer les nombre premiers,
* et est une fonction qui ne dépend que de ce worker.
*/
class DefaultWorker extends AbstractWorker
{
/**
* {@inheritdoc}
*/
public function work($data)
{
if (is_numeric($data)) {
$nombres = $this->nbPremiers($data);
$count = count($nombres);
$result = [
'worker' => __CLASS__,
'message' => sprintf("Nombre premiers calculé avec succès. "
. "Il y a %d nombres premiers", $count),
'pid' => getmypid(),
'raw' => $nombres
];
var_dump($result);
} else {
throw new \Exception("Le message doit être numérique !");
}
}
/**
* {@inheritdoc}
*/
public function keepWorking($params)
{
return parent::keepWorking($params);
}
/**
* Donne une liste de nombre premiers
*
* @param integer $limit nombre premiers de 1 à $limit
* @return array
*/
protected function nbPremiers($limit = 100)
{
$premiers = [];
for ($i = 2; $i < $limit; $i++) {
$isPremier = true;
for ($j = $i-1; $j > 1; $j--) {
if (round($i / $j) == $i / $j) {
$isPremier = false;
}
}
if ($isPremier) {
$premiers[] = $i;
}
}
return $premiers;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="CakephpBeanstalk Test Suite">
<directory>./tests/TestCase</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
</phpunit>
<?php
/**
* Beanstalk\Exception\CantWorkException
*/
namespace Beanstalk\Exception;
use Exception;
/**
* Exception qui est lancée lorsqu'un job doit être considéré fait,
* sans avoir pu aboutir.
* Contrairement à une autre exception, le worker appellera la fonction
* afterWork et supprimera le job.
* Une autre exception "enterre" le job (état buried) mais ne le supprime pas.
*
* @category Beanstalk
*
* @author Ludovic Ganée <ludovic.ganee@libriciel.coop>
* @copyright (c) 2017, Libriciel
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
*/
class CantWorkException extends Exception
{
}
<?php
/**
* Entité de la table beanstalk_jobs
*
* Défini des champs virtuels afin d'exploiter les informations détenues par le
* serveur Beanstalk.
*
* @category Beanstalk
*
* @author Ludovic Ganée <ludovic.ganee@libriciel.coop>
* @copyright (c) 2017, Libriciel
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
*/
namespace Beanstalk\Model\Entity;
use Beanstalk\Utility\Beanstalk;
use Cake\Core\Configure;
use Cake\ORM\Entity;
/**
* Classe de l'entité
*
* <b>beanstalkStats()</b> permet de récupérer et de stocker les informations
* beanstalk sur le job de l'entité.
* Cette information est récupérée par les méthodes des champs virtuels.
*
* <b>getServerBeanstalkStatus()</b> permet de récupérer les informations du
* serveur beanstalk du job.
*/
class BeanstalkJob extends Entity
{
/**
* @var array Champs virtuels
*/
protected $_virtual = ['state', 'age', 'data', 'statetrad', 'related_id'];
/**
* @var array Stock le résultat de la function beanstalkStats
*/