├── QueueController.php ├── views └── default │ ├── create.php │ ├── update.php │ ├── stat.php │ ├── view.php │ ├── _search.php │ ├── _form.php │ ├── report.php │ └── index.php ├── composer.json ├── models ├── generated │ ├── QueueManagerQuery.php │ └── QueueManager.php ├── QueueManager.php └── search │ └── QueueManagerSearch.php ├── Job.php ├── assets └── QueueManagerAsset.php ├── LICENSE.md ├── messages ├── config.php ├── en │ └── queuemanager.php └── ru │ └── queuemanager.php ├── migrations └── m171020_204247_init.php ├── README.md ├── QueueManager.php ├── behaviors └── QueueManagerBehavior.php ├── helpers └── QueueManagerHelper.php ├── js └── queuemanager.js └── controllers └── DefaultController.php /QueueController.php: -------------------------------------------------------------------------------- 1 | modify('-7 days'); 15 | 16 | \ignatenkovnikita\queuemanager\models\QueueManager::deleteAll(['<','created_at', $dateTime->getTimestamp()]); 17 | 18 | } 19 | 20 | 21 | } -------------------------------------------------------------------------------- /views/default/create.php: -------------------------------------------------------------------------------- 1 | title = Yii::t('queuemanager', 'Create {modelClass}', [ 10 | 'modelClass' => 'Queue Manager', 11 | ]); 12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('queuemanager', 'Queue Managers'), 'url' => ['index']]; 13 | $this->params['breadcrumbs'][] = $this->title; 14 | ?> 15 |
16 | 17 | render('_form', [ 18 | 'model' => $model, 19 | ]) ?> 20 | 21 |
22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ignatenkovnikita/yii2-queuemanager", 3 | "description": "Yii2 Queue Manager", 4 | "type": "yii2-extension", 5 | "keywords": ["yii2","extension","queue","manager"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Ignatenkov Nikita", 10 | "email": "ignatenkov.n@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "yiisoft/yii2": "~2.0.0", 15 | "yiisoft/yii2-jui": "~2.0.0", 16 | "2amigos/yii2-chartjs-widget": "~2.0" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "ignatenkovnikita\\queuemanager\\": "" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /views/default/update.php: -------------------------------------------------------------------------------- 1 | title = Yii::t('queuemanager', 'Update {modelClass}: ', [ 9 | 'modelClass' => 'Queue Manager', 10 | ]) . ' ' . $model->name; 11 | $this->params['breadcrumbs'][] = ['label' => Yii::t('queuemanager', 'Queue Managers'), 'url' => ['index']]; 12 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]]; 13 | $this->params['breadcrumbs'][] = Yii::t('queuemanager', 'Update'); 14 | ?> 15 |
16 | 17 | render('_form', [ 18 | 'model' => $model, 19 | ]) ?> 20 | 21 |
22 | -------------------------------------------------------------------------------- /models/generated/QueueManagerQuery.php: -------------------------------------------------------------------------------- 1 | andWhere('[[status]]=1'); 15 | }*/ 16 | 17 | /** 18 | * @inheritdoc 19 | * @return QueueManager[]|array 20 | */ 21 | public function all($db = null) 22 | { 23 | return parent::all($db); 24 | } 25 | 26 | /** 27 | * @inheritdoc 28 | * @return QueueManager|array|null 29 | */ 30 | public function one($db = null) 31 | { 32 | return parent::one($db); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Job.php: -------------------------------------------------------------------------------- 1 | 6 | * @file Job.php 7 | * @author ignatenkovnikita 8 | * @date $date$ 9 | */ 10 | 11 | /** 12 | * Created by PhpStorm. 13 | * User: ignatenkovnikita 14 | * Web Site: http://IgnatenkovNikita.ru 15 | */ 16 | 17 | namespace ignatenkovnikita\queuemanager; 18 | 19 | 20 | use yii\base\BaseObject; 21 | use yii\base\InvalidConfigException; 22 | 23 | class Job extends BaseObject 24 | { 25 | public $name; 26 | 27 | public function init() 28 | { 29 | if (empty($this->name)) { 30 | throw new InvalidConfigException('Property name not empty, please set value'); 31 | } 32 | parent::init(); // TODO: Change the autogenerated stub 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /views/default/stat.php: -------------------------------------------------------------------------------- 1 | 6 | * @file stat.php 7 | * @author ignatenkovnikita 8 | * @date $date$ 9 | */ 10 | 11 | use yii\helpers\Html; 12 | use yii\widgets\DetailView; 13 | 14 | /* @var $this yii\web\View */ 15 | /* @var $model ignatenkovnikita\queuemanager\models\QueueManager */ 16 | 17 | $this->title = 'Statistic in realtime'; 18 | $this->params['breadcrumbs'][] = ['label' => Yii::t('queuemanager', 'Queue Managers'), 'url' => ['index']]; 19 | $this->params['breadcrumbs'][] = $this->title; 20 | \ignatenkovnikita\queuemanager\assets\QueueManagerAsset::register($this); 21 | ?> 22 | 23 | 24 | 25 | 26 | 27 |

Активные workers

28 |
29 | 30 |
-------------------------------------------------------------------------------- /assets/QueueManagerAsset.php: -------------------------------------------------------------------------------- 1 | 8 | * @file QueueManagerAsset.php 9 | * @author ignatenkovnikita 10 | * @date $date$ 11 | */ 12 | 13 | /** 14 | * Created by PhpStorm. 15 | * User: ignatenkovnikita 16 | * Web Site: http://IgnatenkovNikita.ru 17 | */ 18 | class QueueManagerAsset extends \yii\web\AssetBundle 19 | { 20 | // public $depends = [ 21 | // 'yii\web\JqueryAsset', 22 | // 'yii\bootstrap\BootstrapAsset', 23 | // 'trntv\filekit\widget\BlueimpFileuploadAsset' 24 | // ]; 25 | 26 | public function init() 27 | { 28 | $this->sourcePath = __DIR__ . "/../"; 29 | // $this->css = [ 30 | // YII_DEBUG ? 'css/upload-kit.css' : 'css/upload-kit.min.css' 31 | // ]; 32 | 33 | $this->js = [ 34 | 'js/queuemanager.js' 35 | ]; 36 | parent::init(); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nikita Ignatenkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /messages/config.php: -------------------------------------------------------------------------------- 1 | null, 14 | 'interactive' => true, 15 | 'help' => null, 16 | 'sourcePath' => '@vendor/ignatenkovnikita/yii2-queuemanager', 17 | 'messagePath' => 'vendor/ignatenkovnikita/yii2-queuemanager/messages/', 18 | 'languages' => ['en', 'ru'], 19 | 'translator' => 'Yii::t', 20 | 'sort' => false, 21 | 'overwrite' => true, 22 | 'removeUnused' => false, 23 | 'markUnused' => true, 24 | 'except' => [ 25 | '.svn', 26 | '.git', 27 | '.gitignore', 28 | '.gitkeep', 29 | '.hgignore', 30 | '.hgkeep', 31 | '/messages', 32 | '/BaseYii.php', 33 | ], 34 | 'only' => [ 35 | '*.php', 36 | ], 37 | 'format' => 'php', 38 | 'catalog' => 'messages', 39 | 'ignoreCategories' => [], 40 | 'phpFileHeader' => '', 41 | 'phpDocBlock' => null, 42 | ]; 43 | -------------------------------------------------------------------------------- /views/default/view.php: -------------------------------------------------------------------------------- 1 | title = $model->name; 10 | $this->params['breadcrumbs'][] = ['label' => Yii::t('queuemanager', 'Queue Managers'), 'url' => ['index']]; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 | 15 |

16 | $model->id], ['class' => 'btn btn-primary']) ?> 17 | $model->id], [ 18 | 'class' => 'btn btn-danger', 19 | 'data' => [ 20 | 'confirm' => Yii::t('queuemanager', 'Are you sure you want to delete this item?'), 21 | 'method' => 'post', 22 | ], 23 | ]) ?> 24 |

25 | 26 | $model, 28 | 'attributes' => [ 29 | 'id', 30 | 'name', 31 | 'sender', 32 | 'ttr', 33 | 'delay', 34 | 'priority', 35 | 'status', 36 | 'class', 37 | 'properties:ntext', 38 | 'data:ntext', 39 | 'result_id', 40 | 'result:ntext', 41 | 'created_at:datetime', 42 | 'updated_at:datetime', 43 | 'start_execute:datetime', 44 | 'end_execute:datetime', 45 | ], 46 | ]) ?> 47 | 48 |
49 | -------------------------------------------------------------------------------- /migrations/m171020_204247_init.php: -------------------------------------------------------------------------------- 1 | db->driverName === 'mysql') { 13 | $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; 14 | } 15 | $this->createTable(self::TABLE_NAME, [ 16 | 'id' => $this->primaryKey(), 17 | 'name' => $this->string(), 18 | 'sender' => $this->string(), 19 | 'ttr' => $this->integer(), 20 | 'delay' => $this->integer(), 21 | 'priority' => $this->integer(), 22 | 'status' => $this->integer(), 23 | 'class' => $this->string(), 24 | 'properties' => $this->text(), 25 | 'data' => $this->text(), 26 | 'result_id' => $this->integer(), 27 | 'result' => $this->text(), 28 | 'created_at' => $this->bigInteger(), 29 | 'updated_at' => $this->bigInteger(), 30 | 'start_execute' => $this->bigInteger(), 31 | 'end_execute' => $this->bigInteger(), 32 | ], $tableOptions); 33 | 34 | } 35 | 36 | public function safeDown() 37 | { 38 | $this->dropTable(self::TABLE_NAME); 39 | } 40 | 41 | /* 42 | // Use up()/down() to run migration code without a transaction. 43 | public function up() 44 | { 45 | 46 | } 47 | 48 | public function down() 49 | { 50 | echo "m171020_204247_init cannot be reverted.\n"; 51 | 52 | return false; 53 | } 54 | */ 55 | } 56 | -------------------------------------------------------------------------------- /messages/en/queuemanager.php: -------------------------------------------------------------------------------- 1 | '', 21 | 'Are you sure you want to delete this item?' => '', 22 | 'Class' => '', 23 | 'Create' => '', 24 | 'Create {modelClass}' => '', 25 | 'Created At' => '', 26 | 'Data' => '', 27 | 'Delay' => '', 28 | 'Delete' => '', 29 | 'End Execute' => '', 30 | 'ID' => '', 31 | 'Name' => '', 32 | 'Priority' => '', 33 | 'Properties' => '', 34 | 'Queue Managers' => '', 35 | 'Repeat' => '', 36 | 'Reset' => '', 37 | 'Result' => '', 38 | 'Result ID' => '', 39 | 'Search' => '', 40 | 'Sender' => '', 41 | 'Start Execute' => '', 42 | 'Status' => '', 43 | 'Status Done' => '', 44 | 'Status Error' => '', 45 | 'Status Reserved' => '', 46 | 'Status Waiting' => '', 47 | 'Time Execute' => '', 48 | 'Ttr' => '', 49 | 'Update' => '', 50 | 'Update {modelClass}: ' => '', 51 | 'Updated At' => '', 52 | ]; 53 | -------------------------------------------------------------------------------- /views/default/_search.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | 58 | -------------------------------------------------------------------------------- /models/QueueManager.php: -------------------------------------------------------------------------------- 1 | Yii::t('queuemanager', 'Status Waiting'), 42 | self::STATUS_RESERVED => Yii::t('queuemanager', 'Status Reserved'), 43 | self::STATUS_DONE => Yii::t('queuemanager', 'Status Done'), 44 | self::STATUS_ERROR => Yii::t('queuemanager', 'Status Error'), 45 | ]; 46 | 47 | return $status ? ArrayHelper::getValue($statuses, $status) : $statuses; 48 | } 49 | 50 | public function behaviors() 51 | { 52 | return ArrayHelper::merge(parent::behaviors(), [ 53 | 'timestamp' => \yii\behaviors\TimestampBehavior::class, 54 | ]); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /messages/ru/queuemanager.php: -------------------------------------------------------------------------------- 1 | 'Отчет', 21 | 'Statistics' => 'Статистика', 22 | 'Enter date' => 'Укажите дату', 23 | 'Are you sure to repeat this item?' => '', 24 | 'Are you sure you want to delete this item?' => '', 25 | 'Class' => '', 26 | 'Create' => '', 27 | 'Create {modelClass}' => '', 28 | 'Created At' => '', 29 | 'Data' => '', 30 | 'Delay' => '', 31 | 'Delete' => '', 32 | 'End Execute' => '', 33 | 'ID' => '', 34 | 'Name' => '', 35 | 'Priority' => '', 36 | 'Properties' => '', 37 | 'Queue Managers' => '', 38 | 'Repeat' => '', 39 | 'Reset' => '', 40 | 'Result' => '', 41 | 'Result ID' => '', 42 | 'Search' => '', 43 | 'Sender' => '', 44 | 'Start Execute' => '', 45 | 'Status' => '', 46 | 'Status Done' => '', 47 | 'Status Error' => '', 48 | 'Status Reserved' => '', 49 | 'Status Waiting' => '', 50 | 'Time Execute' => '', 51 | 'Ttr' => '', 52 | 'Update' => '', 53 | 'Update {modelClass}: ' => '', 54 | 'Updated At' => '', 55 | ]; 56 | -------------------------------------------------------------------------------- /views/default/_form.php: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 | 14 | 15 | errorSummary($model); ?> 16 | 17 | field($model, 'name')->textInput(['maxlength' => true]) ?> 18 | 19 | field($model, 'sender')->textInput(['maxlength' => true]) ?> 20 | 21 | field($model, 'ttr')->textInput() ?> 22 | 23 | field($model, 'delay')->textInput() ?> 24 | 25 | field($model, 'priority')->textInput() ?> 26 | 27 | field($model, 'status')->textInput(['maxlength' => true]) ?> 28 | 29 | field($model, 'class')->textInput(['maxlength' => true]) ?> 30 | 31 | field($model, 'properties')->textarea(['rows' => 6]) ?> 32 | 33 | field($model, 'data')->textarea(['rows' => 6]) ?> 34 | 35 | field($model, 'result_id')->textInput() ?> 36 | 37 | field($model, 'result')->textarea(['rows' => 6]) ?> 38 | 39 | field($model, 'created_at')->textInput() ?> 40 | 41 | field($model, 'update_at')->textInput() ?> 42 | 43 | field($model, 'start_execute')->textInput() ?> 44 | 45 | field($model, 'end_execute')->textInput() ?> 46 | 47 |
48 | isNewRecord ? Yii::t('queuemanager', 'Create') : Yii::t('queuemanager', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> 49 |
50 | 51 | 52 | 53 |
54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yii2 Queue Manager 2 | ================== 3 | 4 | [![Latest Stable Version](https://poser.pugx.org/ignatenkovnikita/yii2-queuemanager/v/stable)](https://packagist.org/packages/ignatenkovnikita/yii2-queuemanager) [![Total Downloads](https://poser.pugx.org/ignatenkovnikita/yii2-queuemanager/downloads)](https://packagist.org/packages/ignatenkovnikita/yii2-queuemanager) [![Latest Unstable Version](https://poser.pugx.org/ignatenkovnikita/yii2-queuemanager/v/unstable)](https://packagist.org/packages/ignatenkovnikita/yii2-queuemanager) [![License](https://poser.pugx.org/ignatenkovnikita/yii2-queuemanager/license)](https://packagist.org/packages/ignatenkovnikita/yii2-queuemanager) 5 | 6 | 7 | Yii2 Queue Manager 8 | 9 | ![2017-10-21_13-55-13](https://user-images.githubusercontent.com/4436320/31851112-b75f26e8-b667-11e7-8f54-d907daeb26bb.png) 10 | 11 | 12 | Installation 13 | ------------ 14 | 15 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 16 | 17 | Either run 18 | 19 | ``` 20 | php composer.phar require --prefer-dist ignatenkovnikita/yii2-queuemanager "*" 21 | ``` 22 | 23 | or add 24 | 25 | ``` 26 | "ignatenkovnikita/yii2-queuemanager": "*" 27 | ``` 28 | 29 | to the require section of your `composer.json` file. 30 | 31 | 32 | Usage 33 | ----- 34 | Apply migrations 35 | 36 | ```bash 37 | ./console/yii migrate --migrationPath=vendor/ignatenkovnikita/yii2-queuemanager/migrations/ 38 | 39 | ``` 40 | 41 | Once the extension is installed, simply use it in your code by : 42 | 43 | ```php 44 | 'modules' => [ 45 | 'queuemanager' => [ 46 | 'class' => \ignatenkovnikita\queuemanager\QueueManager::class 47 | ] 48 | ], 49 | 'components' => [ 50 | 'queue' => [ 51 | 'class' => \yii\queue\redis\Queue::class, 52 | 'as log' => \yii\queue\LogBehavior::class, 53 | 'as quuemanager' => \ignatenkovnikita\queuemanager\behaviors\QueueManagerBehavior::class 54 | // Other driver options 55 | ], 56 | ]``` 57 | -------------------------------------------------------------------------------- /QueueManager.php: -------------------------------------------------------------------------------- 1 | 6 | * @file QueueManager.php 7 | * @author ignatenkovnikita 8 | * @date $date$ 9 | */ 10 | 11 | /** 12 | * Created by PhpStorm. 13 | * User: ignatenkovnikita 14 | * Web Site: http://IgnatenkovNikita.ru 15 | */ 16 | 17 | namespace ignatenkovnikita\queuemanager; 18 | 19 | use Yii; 20 | use yii\base\Application; 21 | use yii\base\BootstrapInterface; 22 | use yii\base\Event; 23 | use yii\base\Module; 24 | use yii\filters\AccessControl; 25 | use yii\helpers\ArrayHelper; 26 | use yii\queue\Queue; 27 | 28 | class QueueManager extends Module implements BootstrapInterface 29 | { 30 | public $adminPermission = 'queuemanager-module'; 31 | 32 | public function behaviors() 33 | { 34 | return ArrayHelper::merge( 35 | parent::behaviors(), 36 | [ 37 | 'access' => [ 38 | 'class' => AccessControl::className(), 39 | 'rules' => [ 40 | [ 41 | 'allow' => true, 42 | 'roles' => [$this->adminPermission] 43 | ] 44 | ] 45 | ] 46 | ] 47 | ); 48 | } 49 | 50 | public function init() 51 | { 52 | parent::init(); // TODO: Change the autogenerated stub 53 | $this->registerTranslations(); 54 | } 55 | 56 | public function registerTranslations() 57 | { 58 | Yii::$app->i18n->translations['queuemanager'] = [ 59 | 'class' => 'yii\i18n\PhpMessageSource', 60 | 'sourceLanguage' => 'en-US', 61 | 'basePath' => '@vendor/ignatenkovnikita/yii2-queuemanager/messages', 62 | ]; 63 | 64 | } 65 | 66 | public function bootstrap($app) 67 | { 68 | if ($app instanceof \yii\console\Application) { 69 | $app->controllerMap[$this->id] = [ 70 | 'class' => 'ignatenkovnikita\queuemanager\QueueController', 71 | ]; 72 | 73 | 74 | } 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /behaviors/QueueManagerBehavior.php: -------------------------------------------------------------------------------- 1 | 'afterPush', 33 | Queue::EVENT_BEFORE_EXEC => 'beforeExec', 34 | Queue::EVENT_AFTER_EXEC => 'afterExec', 35 | Queue::EVENT_AFTER_ERROR => 'afterError', 36 | ]; 37 | } 38 | 39 | public function afterPush(PushEvent $event) 40 | { 41 | QueueManagerHelper::create($event); 42 | } 43 | 44 | public function beforeExec(ExecEvent $event) 45 | { 46 | QueueManagerHelper::startExec($event); 47 | } 48 | 49 | public function afterExec(ExecEvent $event) 50 | { 51 | QueueManagerHelper::afterExec($event); 52 | } 53 | 54 | public function afterError(ExecEvent $event) 55 | { 56 | QueueManagerHelper::afterError($event); 57 | 58 | Yii::endProfile($this->getEventTitle($event), Queue::class); 59 | Yii::error($this->getEventTitle($event) . ' error ' . $event->error, Queue::class); 60 | if ($this->autoFlush) { 61 | Yii::getLogger()->flush(true); 62 | } 63 | } 64 | 65 | protected function getEventTitle(JobEvent $event) 66 | { 67 | $title = strtr('[id] name', [ 68 | 'id' => $event->id, 69 | 'name' => $event->job instanceof Job ? get_class($event->job) : 'mixed data', 70 | ]); 71 | if ($event instanceof ExecEvent) { 72 | $title .= " (attempt: $event->attempt)"; 73 | } 74 | 75 | return $title; 76 | } 77 | } -------------------------------------------------------------------------------- /models/generated/QueueManager.php: -------------------------------------------------------------------------------- 1 | 255], 46 | ]; 47 | } 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | public function attributeLabels() 53 | { 54 | return [ 55 | 'id' => Yii::t('queuemanager', 'ID'), 56 | 'name' => Yii::t('queuemanager', 'Name'), 57 | 'sender' => Yii::t('queuemanager', 'Sender'), 58 | 'ttr' => Yii::t('queuemanager', 'Ttr'), 59 | 'delay' => Yii::t('queuemanager', 'Delay'), 60 | 'priority' => Yii::t('queuemanager', 'Priority'), 61 | 'status' => Yii::t('queuemanager', 'Status'), 62 | 'class' => Yii::t('queuemanager', 'Class'), 63 | 'properties' => Yii::t('queuemanager', 'Properties'), 64 | 'data' => Yii::t('queuemanager', 'Data'), 65 | 'result_id' => Yii::t('queuemanager', 'Result ID'), 66 | 'result' => Yii::t('queuemanager', 'Result'), 67 | 'created_at' => Yii::t('queuemanager', 'Created At'), 68 | 'updated_at' => Yii::t('queuemanager', 'Updated At'), 69 | 'start_execute' => Yii::t('queuemanager', 'Start Execute'), 70 | 'end_execute' => Yii::t('queuemanager', 'End Execute'), 71 | ]; 72 | } 73 | 74 | /** 75 | * @inheritdoc 76 | * @return QueueManagerQuery the active query used by this AR class. 77 | */ 78 | public static function find() 79 | { 80 | return new QueueManagerQuery(get_called_class()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /models/search/QueueManagerSearch.php: -------------------------------------------------------------------------------- 1 | orderBy('id desc'); 46 | 47 | $dataProvider = new ActiveDataProvider([ 48 | 'query' => $query, 49 | ]); 50 | 51 | if (!($this->load($params) && $this->validate())) { 52 | return $dataProvider; 53 | } 54 | 55 | $query->andFilterWhere([ 56 | 'id' => $this->id, 57 | 'ttr' => $this->ttr, 58 | 'delay' => $this->delay, 59 | 'priority' => $this->priority, 60 | 'result_id' => $this->result_id, 61 | // 'created_at' => $this->created_at, 62 | // 'updated_at' => $this->updated_at, 63 | 'start_execute' => $this->start_execute, 64 | 'end_execute' => $this->end_execute, 65 | ]); 66 | 67 | $this->dayCondition($query, 'created_at'); 68 | $this->dayCondition($query, 'updated_at'); 69 | 70 | $query->andFilterWhere(['like', 'name', $this->name]) 71 | ->andFilterWhere(['like', 'sender', $this->sender]) 72 | ->andFilterWhere(['like', 'status', $this->status]) 73 | ->andFilterWhere(['like', 'class', $this->class]) 74 | ->andFilterWhere(['like', 'properties', $this->properties]) 75 | ->andFilterWhere(['like', 'data', $this->data]) 76 | ->andFilterWhere(['like', 'result', $this->result]); 77 | 78 | 79 | return $dataProvider; 80 | } 81 | 82 | 83 | /** 84 | * @param ActiveQuery $query 85 | * @param $attribute 86 | * @return bool 87 | */ 88 | protected function dayCondition(ActiveQuery &$query, $attribute) 89 | { 90 | if(empty($this->{$attribute})) { 91 | return false; 92 | } 93 | $start = strtotime('midnight', strtotime($this->{$attribute})); 94 | $query->andFilterWhere( ['BETWEEN', $attribute, $start, strtotime('+1day - 1sec', $start)]); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /helpers/QueueManagerHelper.php: -------------------------------------------------------------------------------- 1 | getComponents(false) as $id => $component) { 26 | if ($component === $event->sender) { 27 | $data['sender'] = $id; 28 | break; 29 | } 30 | } 31 | $data['id'] = $event->id; 32 | $data['ttr'] = isset($event->ttr) ? $event->ttr : null; 33 | $data['delay'] = isset($event->delay) ? $event->delay : null; 34 | $data['priority'] = isset($event->priority) ? $event->priority : null; 35 | // $data['status'] = self::getStatus($data); 36 | if ($event->job instanceof Job) { 37 | $data['class'] = get_class($event->job); 38 | $data['name'] = $event->job->name; 39 | $data['properties'] = []; 40 | foreach (get_object_vars($event->job) as $property => $value) { 41 | $data['properties'][$property] = VarDumper::dumpAsString($value); 42 | } 43 | } else { 44 | $data['data'] = VarDumper::dumpAsString($event->job); 45 | } 46 | 47 | return $data; 48 | } 49 | 50 | public static function create($event) 51 | { 52 | $data = self::parseDate($event); 53 | $model = QueueManager::findOne($data['id']); 54 | if (!$model) { 55 | $model = new QueueManager(); 56 | $model->load($data, ''); 57 | $model->id = $data['id']; 58 | $model->status = self::getStatus($model); 59 | $model->properties = json_encode($model->properties); 60 | $model->data = json_encode($model->data); 61 | $model->save(); 62 | } 63 | 64 | return $model; 65 | } 66 | 67 | public static function startExec(ExecEvent $event) 68 | { 69 | $model = self::create($event); 70 | $model->start_execute = time(); 71 | $model->status = self::getStatus($model); 72 | $r = $model->updateAttributes(['start_execute', 'status']); 73 | return $r; 74 | } 75 | 76 | public static function afterExec(ExecEvent $event) 77 | { 78 | $model = self::create($event); 79 | $model->end_execute = time(); 80 | $model->status = QueueManager::STATUS_DONE; 81 | $r = $model->updateAttributes(['end_execute', 'status']); 82 | return $r; 83 | } 84 | 85 | public static function afterError(ExecEvent $event) 86 | { 87 | $model = self::create($event); 88 | $model->end_execute = time(); 89 | $model->status = QueueManager::STATUS_ERROR; 90 | $model->result = $event->error; 91 | $r = $model->updateAttributes(['end_execute', 'status', 'result']); 92 | return $r; 93 | } 94 | 95 | private static function getStatus($model) 96 | { 97 | $status = null; 98 | if ($queue = Yii::$app->get($model->sender, false)) { 99 | try { 100 | if ($queue->isWaiting($model->id)) { 101 | $status = QueueManager::STATUS_WAITING; 102 | } elseif ($queue->isReserved($model->id)) { 103 | $status = QueueManager::STATUS_RESERVED; 104 | } elseif ($queue->isDone($model->id)) { 105 | $status = QueueManager::STATUS_DONE; 106 | } 107 | } catch (NotSupportedException $e) { 108 | } catch (\Exception $e) { 109 | $status = $e->getMessage(); 110 | } 111 | } 112 | return $status; 113 | } 114 | } -------------------------------------------------------------------------------- /js/queuemanager.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) $user$, Inc - All Rights Reserved 3 | * 4 | * 5 | * @file queuemanager.js 6 | * @author ignatenkovnikita 7 | * @date $date$ 8 | */ 9 | 10 | 11 | Highcharts.setOptions({ 12 | global: { 13 | useUTC: false 14 | } 15 | }); 16 | 17 | // Create the chart 18 | Highcharts.stockChart('container', { 19 | chart: { 20 | events: { 21 | load: function () { 22 | 23 | // set up the updating of the chart each second 24 | var series_1 = this.series[0]; 25 | var series_2 = this.series[1]; 26 | var series_3 = this.series[2]; 27 | setInterval(function () { 28 | $.ajax({ 29 | url: "ajax", 30 | method: "GET", 31 | // data: {"id": id}, 32 | dataType: "json", 33 | 34 | success: function (data) { 35 | console.log(data.time); 36 | console.log(data.series_1); 37 | series_1.addPoint([(new Date()).getTime(), data.series_1], true, true); 38 | series_2.addPoint([(new Date()).getTime(), data.series_2], true, true); 39 | series_3.addPoint([(new Date()).getTime(), data.series_3], true, true); 40 | 41 | $('#workers').html(data.workers.join("
")); 42 | } 43 | 44 | }); 45 | 46 | var x = (new Date()).getTime(), // current time 47 | y = Math.round(Math.random() * 100); 48 | console.log(x); 49 | // series.addPoint([x, y], true, true); 50 | }, 1000); 51 | } 52 | } 53 | }, 54 | 55 | rangeSelector: { 56 | buttons: [{ 57 | count: 1, 58 | type: 'minute', 59 | text: '1M' 60 | }, { 61 | count: 5, 62 | type: 'minute', 63 | text: '5M' 64 | }, { 65 | type: 'all', 66 | text: 'All' 67 | }], 68 | inputEnabled: 69 | false, 70 | selected: 71 | 0 72 | }, 73 | 74 | title: { 75 | text: 'Queue Stat' 76 | } 77 | , 78 | 79 | exporting: { 80 | enabled: false 81 | } 82 | , 83 | 84 | series: [{ 85 | name: 'Waiting', 86 | data: (function () { 87 | // generate an array of random data 88 | var data = [], 89 | time = (new Date()).getTime(), 90 | i; 91 | 92 | for (i = -5; i <= 0; i += 1) { 93 | data.push([ 94 | time + i * 1000, 95 | Math.round(0) 96 | ]); 97 | } 98 | return data; 99 | }()) 100 | }, 101 | { 102 | name: 'Delayed', 103 | data: (function () { 104 | // generate an array of random data 105 | var data = [], 106 | time = (new Date()).getTime(), 107 | i; 108 | 109 | for (i = -5; i <= 0; i += 1) { 110 | data.push([ 111 | time + i * 1000, 112 | Math.round(0) 113 | ]); 114 | } 115 | return data; 116 | }()) 117 | }, 118 | { 119 | name: 'Reserved', 120 | data: (function () { 121 | // generate an array of random data 122 | var data = [], 123 | time = (new Date()).getTime(), 124 | i; 125 | 126 | for (i = -5; i <= 0; i += 1) { 127 | data.push([ 128 | time + i * 1000, 129 | Math.round(0) 130 | ]); 131 | } 132 | return data; 133 | }()) 134 | } 135 | ] 136 | }) 137 | ; 138 | 139 | -------------------------------------------------------------------------------- /views/default/report.php: -------------------------------------------------------------------------------- 1 | 6 | * @file report.php 7 | * @author ignatenkovnikita 8 | * @date $date$ 9 | */ 10 | 11 | use dosamigos\chartjs\ChartJs; 12 | use ignatenkovnikita\queuemanager\models\QueueManager; 13 | 14 | /** 15 | * Created by PhpStorm. 16 | * User: ignatenkovnikita 17 | * Web Site: http://IgnatenkovNikita.ru 18 | */ 19 | 20 | $this->title = 'Report status job'; 21 | $this->params['breadcrumbs'][] = ['label' => Yii::t('queuemanager', 'Queue Managers'), 'url' => ['index']]; 22 | $this->params['breadcrumbs'][] = $this->title; 23 | 24 | 25 | // status 26 | $db = Yii::$app->db; 27 | switch ($db->driverName) { 28 | case 'pgsql': 29 | $selectExpression = "to_char(to_timestamp(created_at), 'YYYY-MM-DD')"; 30 | break; 31 | default: 32 | case 'mysql': 33 | $selectExpression = "DATE_FORMAT(FROM_UNIXTIME(created_at), '%Y-%m-%d')"; 34 | break; 35 | } 36 | $subQuery = (new \yii\db\Query()) 37 | ->select([ 38 | 'dd' => new \yii\db\Expression($selectExpression), 39 | 'status', 40 | ])->from('queue_manager'); 41 | $data = (new \yii\db\Query()) 42 | ->select([ 43 | 'dd', 44 | 'status', 45 | 'cnt' => 'COUNT(*)', 46 | ]) 47 | ->from(['q' => $subQuery]) 48 | ->groupBy(['status', 'dd']) 49 | ->orderBy('dd') 50 | ->all(); 51 | 52 | $labels = []; 53 | foreach ($data as $item) { 54 | $dd = Yii::$app->formatter->asDate($item['dd']); 55 | if (!in_array($dd, $labels)) { 56 | $labels[] = $dd; 57 | } 58 | } 59 | $status1 = array_fill_keys($labels, '0'); 60 | $status2 = array_fill_keys($labels, '0'); 61 | $status3 = array_fill_keys($labels, '0'); 62 | $status4 = array_fill_keys($labels, '0'); 63 | foreach ($data as $item) { 64 | $dd = Yii::$app->formatter->asDate($item['dd']); 65 | if ($item['status'] == QueueManager::STATUS_WAITING) { 66 | $status1[$dd] = $item['cnt']; 67 | } 68 | if ($item['status'] == QueueManager::STATUS_RESERVED) { 69 | $status2[$dd] = $item['cnt']; 70 | } 71 | if ($item['status'] == QueueManager::STATUS_DONE) { 72 | $status3[$dd] = $item['cnt']; 73 | } 74 | if ($item['status'] == QueueManager::STATUS_ERROR) { 75 | $status4[$dd] = $item['cnt']; 76 | } 77 | } 78 | $status1 = array_values($status1); 79 | $status2 = array_values($status2); 80 | $status3 = array_values($status3); 81 | $status4 = array_values($status4); 82 | 83 | 84 | echo ChartJs::widget([ 85 | 'type' => 'line', 86 | 'options' => [ 87 | // 'height' => 400, 88 | // 'width' => 400 89 | ], 90 | 'data' => [ 91 | 'labels' => $labels, 92 | 'datasets' => [ 93 | [ 94 | 'label' => "Status Waiting", 95 | // 'backgroundColor' => "rgba(179,181,198,0.2)", 96 | 'borderColor' => "blue", 97 | // 'pointBackgroundColor' => "rgba(179,181,198,1)", 98 | // 'pointBorderColor' => "#fff", 99 | // 'pointHoverBackgroundColor' => "#fff", 100 | // 'pointHoverBorderColor' => "rgba(179,181,198,1)", 101 | 'data' => $status1 102 | ], 103 | [ 104 | 'label' => "Status Reserved", 105 | // 'backgroundColor' => "rgba(255,99,132,0.2)", 106 | 'borderColor' => "green", 107 | // 'pointBackgroundColor' => "rgba(255,99,132,1)", 108 | // 'pointBorderColor' => "#fff", 109 | // 'pointHoverBackgroundColor' => "#fff", 110 | // 'pointHoverBorderColor' => "rgba(255,99,132,1)", 111 | 'data' => $status2 112 | ], 113 | [ 114 | 'label' => "Status Done", 115 | // 'backgroundColor' => "rgba(255,99,132,0.2)", 116 | 'borderColor' => "green", 117 | // 'pointBackgroundColor' => "rgba(255,99,132,1)", 118 | // 'pointBorderColor' => "#fff", 119 | // 'pointHoverBackgroundColor' => "#fff", 120 | // 'pointHoverBorderColor' => "rgba(255,99,132,1)", 121 | 'data' => $status3 122 | ], 123 | [ 124 | 'label' => "Status Error", 125 | // 'backgroundColor' => "rgba(255,99,132,0.2)", 126 | 'borderColor' => "red", 127 | // 'pointBackgroundColor' => "rgba(255,99,132,1)", 128 | // 'pointBorderColor' => "#fff", 129 | // 'pointHoverBackgroundColor' => "#fff", 130 | // 'pointHoverBorderColor' => "rgba(255,99,132,1)", 131 | 'data' => $status4 132 | ] 133 | ] 134 | ] 135 | ]); -------------------------------------------------------------------------------- /views/default/index.php: -------------------------------------------------------------------------------- 1 | title = Yii::t('queuemanager', 'Queue Managers'); 13 | $this->params['breadcrumbs'][] = $this->title; 14 | ?> 15 |
16 | 17 | render('_search', ['model' => $searchModel]); ?> 18 | 19 | 20 | 21 | 22 | $dataProvider, 27 | 'filterModel' => $searchModel, 28 | 'rowOptions' => function ($model) { 29 | if ($model->status == QueueManager::STATUS_DONE) { 30 | return ['class' => 'success']; 31 | } 32 | if ($model->status == QueueManager::STATUS_RESERVED) { 33 | return ['class' => 'warning']; 34 | } 35 | if ($model->status == QueueManager::STATUS_WAITING) { 36 | return ['class' => 'info']; 37 | } 38 | if ($model->status == QueueManager::STATUS_ERROR) { 39 | return ['class' => 'danger']; 40 | } 41 | }, 42 | 'columns' => [ 43 | // ['class' => 'yii\grid\SerialColumn'], 44 | 45 | 'id', 46 | [ 47 | 'attribute' => 'status', 48 | 'value' => function ($data) { 49 | return QueueManager::getStatuses($data->status); 50 | }, 51 | 'filter' => QueueManager::getStatuses() 52 | ], 53 | 'name', 54 | 'sender', 55 | // 'ttr', 56 | // 'delay', 57 | // 'priority', 58 | // 'status', 59 | // 'class', 60 | // 'properties:ntext', 61 | // 'data:ntext', 62 | // 'result_id', 63 | // 'result:ntext', 64 | [ 65 | 'headerOptions' => ['width' => '140'], 66 | 'attribute' => 'created_at', 67 | 'value' => function ($model) { 68 | 69 | return empty($model->created_at) ? null : date('d.m.Y H:i:s', $model->created_at); 70 | }, 71 | 'filter' => DatePicker::widget([ 72 | 'model' => $searchModel, 73 | 'attribute' => 'created_at', 74 | 'dateFormat' => 'php:d.m.Y', 75 | 'options' => ['class' => 'form-control', 'placeholder' => Yii::t('queuemanager', 'Enter date')], 76 | ]), 77 | 'format' => 'html', 78 | ], 79 | [ 80 | 'headerOptions' => ['width' => '140'], 81 | 'attribute' => 'updated_at', 82 | 'value' => function ($model) { 83 | 84 | return empty($model->created_at) ? null : date('d.m.Y H:i:s', $model->created_at); 85 | }, 86 | 'filter' => DatePicker::widget([ 87 | 'model' => $searchModel, 88 | 'attribute' => 'updated_at', 89 | 'dateFormat' => 'php:d.m.Y', 90 | 'options' => ['class' => 'form-control', 'placeholder' => Yii::t('queuemanager', 'Enter date')], 91 | ]), 92 | 'format' => 'html', 93 | ], 94 | // 'updated_at', 95 | 'start_execute:datetime', 96 | 'end_execute:datetime', 97 | [ 98 | 'label' => Yii::t('queuemanager', 'Time Execute'), 99 | 'value' => function (\ignatenkovnikita\queuemanager\models\QueueManager $data) { 100 | return Yii::$app->formatter->asTimestamp($data->end_execute - $data->start_execute); 101 | } 102 | ], 103 | 104 | [ 105 | 'class' => 'yii\grid\ActionColumn', 106 | 'template' => '{view} {repeat} {delete}', 107 | 'buttons' => [ 108 | 'repeat' => function ($url, $model) { 109 | $customurl = Yii::$app->getUrlManager()->createUrl(['default/repeat', 'id' => $model['id']]); //$model->id для AR 110 | // return \yii\helpers\Html::a('', $customurl, 111 | // ['title' => Yii::t('queuemanager', 'Repeat'), 'data-pjax' => '0']); 112 | return Html::a('', $url, [ 113 | 'title' => Yii::t('queuemanager', 'Repeat'), 114 | 'data-confirm' => Yii::t('queuemanager', 'Are you sure to repeat this item?'), 115 | 'data-method' => 'post', 116 | ]); 117 | } 118 | 119 | 120 | ] 121 | ], 122 | ], 123 | ]); 124 | \yii\widgets\Pjax::end(); 125 | ?> 126 | 127 |
128 | 129 | registerJs(' 132 | setInterval(function(){ 133 | $.pjax.reload({container:"#p0"}); 134 | }, 3000);', \yii\web\VIEW::POS_HEAD); 135 | ?> 136 | -------------------------------------------------------------------------------- /controllers/DefaultController.php: -------------------------------------------------------------------------------- 1 | [ 26 | 'class' => VerbFilter::className(), 27 | 'actions' => [ 28 | 'delete' => ['post'], 29 | 'repeat' => ['post'], 30 | ], 31 | ], 32 | ]; 33 | } 34 | 35 | /** 36 | * Lists all QueueManager models. 37 | * @return mixed 38 | */ 39 | public function actionIndex() 40 | { 41 | $searchModel = new QueueManagerSearch(); 42 | $dataProvider = $searchModel->search(Yii::$app->request->queryParams); 43 | 44 | return $this->render('index', [ 45 | 'searchModel' => $searchModel, 46 | 'dataProvider' => $dataProvider, 47 | ]); 48 | } 49 | 50 | /** 51 | * @return array 52 | */ 53 | protected function getWorkersInfo() 54 | { 55 | $queue = Yii::$app->queue; 56 | 57 | $workers = []; 58 | $data = $queue->redis->clientList(); 59 | foreach (explode("\n", trim($data)) as $line) { 60 | $client = []; 61 | foreach (explode(' ', trim($line)) as $pair) { 62 | list($key, $value) = explode('=', $pair, 2); 63 | $client[$key] = $value; 64 | } 65 | if (isset($client['name']) && strpos($client['name'], $queue->channel . '.worker') === 0) { 66 | $workers[$client['name']] = $client; 67 | } 68 | } 69 | 70 | return $workers; 71 | } 72 | 73 | 74 | /** 75 | * @return array 76 | */ 77 | public function actionAjax() 78 | { 79 | $workers = []; 80 | if ($workersInfo = $this->getWorkersInfo()) { 81 | foreach ($workersInfo as $name => $info) { 82 | $workers[] = $name . ' ' . $info['addr']; 83 | // Console::stdout($this->format("- $name: ", Console::FG_YELLOW)); 84 | // Console::output($info['addr']); 85 | } 86 | } 87 | 88 | $queue = Yii::$app->queue; 89 | $prefix = $queue->channel; 90 | $waiting = $queue->redis->llen("$prefix.waiting"); 91 | $delayed = $queue->redis->zcount("$prefix.delayed", '-inf', '+inf'); 92 | $reserved = $queue->redis->zcount("$prefix.reserved", '-inf', '+inf'); 93 | $total = $queue->redis->get("$prefix.message_id"); 94 | $done = $total - $waiting - $delayed - $reserved; 95 | 96 | Yii::$app->response->format = Response::FORMAT_JSON; 97 | 98 | return [ 99 | 'time' => time(), 100 | 'series_1' => (int)$waiting, 101 | 'series_2' => (int)$delayed, 102 | 'series_3' => (int)$reserved, 103 | 'workers' => $workers 104 | ]; 105 | } 106 | 107 | 108 | public function actionStat() 109 | { 110 | return $this->render('stat'); 111 | } 112 | 113 | public function actionReport() 114 | { 115 | return $this->render('report'); 116 | } 117 | 118 | /** 119 | * Displays a single QueueManager model. 120 | * @param integer $id 121 | * @return mixed 122 | */ 123 | public function actionView($id) 124 | { 125 | return $this->render('view', [ 126 | 'model' => $this->findModel($id), 127 | ]); 128 | } 129 | 130 | /** 131 | * Creates a new QueueManager model. 132 | * If creation is successful, the browser will be redirected to the 'view' page. 133 | * @return mixed 134 | */ 135 | public function actionCreate() 136 | { 137 | $model = new QueueManager(); 138 | 139 | if ($model->load(Yii::$app->request->post()) && $model->save()) { 140 | return $this->redirect(['view', 'id' => $model->id]); 141 | } else { 142 | return $this->render('create', [ 143 | 'model' => $model, 144 | ]); 145 | } 146 | } 147 | 148 | /** 149 | * Updates an existing QueueManager model. 150 | * If update is successful, the browser will be redirected to the 'view' page. 151 | * @param integer $id 152 | * @return mixed 153 | */ 154 | public function actionUpdate($id) 155 | { 156 | $model = $this->findModel($id); 157 | 158 | if ($model->load(Yii::$app->request->post()) && $model->save()) { 159 | return $this->redirect(['view', 'id' => $model->id]); 160 | } else { 161 | return $this->render('update', [ 162 | 'model' => $model, 163 | ]); 164 | } 165 | } 166 | 167 | /** 168 | * Deletes an existing QueueManager model. 169 | * If deletion is successful, the browser will be redirected to the 'index' page. 170 | * @param integer $id 171 | * @return mixed 172 | */ 173 | public function actionDelete($id) 174 | { 175 | $this->findModel($id)->delete(); 176 | 177 | return $this->redirect(['index']); 178 | } 179 | 180 | public function actionRepeat($id) 181 | { 182 | $model = $this->findModel($id); 183 | 184 | 185 | Yii::$app->{$model->sender}->push(new $model->class(json_decode($model->properties))); 186 | return $this->redirect(Yii::$app->request->referrer); 187 | // Yii::$app->components->->push() 188 | // print_r($model); 189 | } 190 | 191 | /** 192 | * Finds the QueueManager model based on its primary key value. 193 | * If the model is not found, a 404 HTTP exception will be thrown. 194 | * @param integer $id 195 | * @return QueueManager the loaded model 196 | * @throws NotFoundHttpException if the model cannot be found 197 | */ 198 | protected function findModel($id) 199 | { 200 | if (($model = QueueManager::findOne($id)) !== null) { 201 | return $model; 202 | } else { 203 | throw new NotFoundHttpException('The requested page does not exist.'); 204 | } 205 | } 206 | } 207 | --------------------------------------------------------------------------------