PageRenderTime 1922ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/console/controllers/ContentController.php

https://gitlab.com/jafari/resana
PHP | 171 lines | 105 code | 26 blank | 40 comment | 5 complexity | 64197a302befc32e6a7ebb315dbea84f MD5 | raw file
  1. <?php
  2. /**
  3. * @link http://www.noghteh.ir/
  4. * @copyright Copyright (c) 2015 Noghteh
  5. * @license http://www.noghteh.ir/license/
  6. */
  7. namespace console\controllers;
  8. use PhpAmqpLib\Wire\AMQPTable;
  9. use Yii;
  10. use Exception;
  11. use common\models\Channel;
  12. use common\models\Content;
  13. use yii\helpers\ArrayHelper;
  14. /**
  15. * Class ContentController
  16. * @package console\controllers
  17. * @author Ali Irani <ali@irani.im>
  18. */
  19. class ContentController extends \yii\console\Controller
  20. {
  21. /**
  22. * Get waiting contents to push and then one bye one push to channels
  23. */
  24. public function actionSendToChannels()
  25. {
  26. $contents = Content::find()
  27. ->where(['{{%content}}.status' => [Content::STATUS_IN_QUEUE, Content::STATUS_SCHEDULED]])
  28. ->joinWith(['channels'])
  29. ->andWhere(['{{%channel}}.type' => [Channel::TYPE_TELEGRAM_BOT, Channel::TYPE_TELEGRAM_CHANNEL, Channel::TYPE_TWITTER]])
  30. ->all();
  31. if ($contents) {
  32. $contentIds = ArrayHelper::getColumn($contents, 'id');
  33. Content::updateAll(['status' => Content::STATUS_SENDING], ['id' => $contentIds]);
  34. /* @var Content $content */
  35. foreach ($contents as $content) {
  36. if ($content->published_at > time()) {
  37. Content::updateAll(['status' => Content::STATUS_SCHEDULED], ['id' => $content->id]);
  38. continue;
  39. }
  40. try {
  41. $content->sendToChannels();
  42. } catch (Exception $e) {
  43. Yii::error(['message' => $e->getMessage(), 'file' => $e->getFile().':'.$e->getLine()], __METHOD__);
  44. }
  45. $content->status = $content->status === Content::STATUS_SENDING ? Content::STATUS_FAILED : $content->status;
  46. $content->update(false);
  47. }
  48. $this->stdout("Sent \"" . count($contents) . "\" contents to the channels.\n");
  49. }
  50. }
  51. /**
  52. * Beta worker (consumer) for contents queue in amqp
  53. * @todo: this action need to be better and optimizing
  54. */
  55. public function actionWorker()
  56. {
  57. $exchange = 'router';
  58. $queue = 'contents';
  59. $consumerTag = 'consumer';
  60. /* @var \PhpAmqpLib\Connection\AMQPStreamConnection $connection */
  61. $connection = Yii::$app->amqp->getConnection();
  62. $channel = $connection->channel();
  63. $channel->queue_declare($queue, false, true, false, false);
  64. $channel->exchange_declare($exchange, 'direct', false, true, false);
  65. $channel->queue_bind($queue, $exchange);
  66. $channel->basic_consume($queue, $consumerTag, false, false, false, false, [$this, 'processMessage']);
  67. register_shutdown_function([$this, 'shutdownConsumer'], $channel, $connection);
  68. // Loop as long as the channel has callbacks registered
  69. while (count($channel->callbacks)) {
  70. $channel->wait();
  71. }
  72. }
  73. /**
  74. * Beta worker (consumer) for delayed contents queue in amqp
  75. * @todo: this action need to be better and optimizing
  76. */
  77. public function actionDelayedWorker()
  78. {
  79. $exchange = 'delayed_router';
  80. $queue = 'delayed_contents';
  81. $consumerTag = 'delayed_consumer';
  82. $exchangeType = 'x-delayed-message';
  83. $exchangeArgs = new AMQPTable(['x-delayed-type' => 'fanout']);
  84. $queueArgs = new AMQPTable(['x-dead-letter-exchange' => 'delayed']);
  85. /* @var \PhpAmqpLib\Connection\AMQPStreamConnection $connection */
  86. $connection = Yii::$app->amqp->getConnection();
  87. $channel = $connection->channel();
  88. $channel->queue_declare($queue, false, true, false, false, false, $queueArgs);
  89. $channel->exchange_declare($exchange, $exchangeType, false, true, false, false, false, $exchangeArgs);
  90. $channel->queue_bind($queue, $exchange);
  91. $channel->basic_consume($queue, $consumerTag, false, false, false, false, [$this, 'processMessage']);
  92. register_shutdown_function([$this, 'shutdownConsumer'], $channel, $connection);
  93. // Loop as long as the channel has callbacks registered
  94. while (count($channel->callbacks)) {
  95. $channel->wait();
  96. }
  97. }
  98. /**
  99. * Proccess messages for amqp publisher
  100. * @param $message
  101. * @return bool
  102. */
  103. public function processMessage($message)
  104. {
  105. $contentId = $message->body;
  106. $startTime = date("Y-m-j H:i:s");
  107. echo "\n[$startTime] Start sending content $contentId to channels.";
  108. $content = Content::findOne($contentId);
  109. if ($content === null) {
  110. return false;
  111. }
  112. $content->changeStatus(Content::STATUS_SENDING);
  113. try {
  114. $content->sendToChannels();
  115. } catch (Exception $e) {
  116. Yii::error(['message' => $e->getMessage(), 'file' => $e->getFile().':'.$e->getLine()], __METHOD__);
  117. }
  118. $status = $content->status === Content::STATUS_SENDING ? Content::STATUS_FAILED : $content->status;
  119. $content->changeStatus($status);
  120. $message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
  121. $endTime = date("Y-m-j H:i:s");
  122. echo "\n[$endTime] Finish sending content $contentId to channels.";
  123. return true;
  124. }
  125. /**
  126. * Shutdown amqp connections
  127. * @param $channel
  128. * @param $connection
  129. */
  130. public function shutdownConsumer($channel, $connection)
  131. {
  132. $channel->close();
  133. $connection->close();
  134. }
  135. /**
  136. * Restart AMQP worker
  137. * @param string $user
  138. */
  139. public function actionRestartWorker($user = 'gituser')
  140. {
  141. shell_exec('ps ax | grep yii> /dev/null & echo $!');
  142. shell_exec("killall -w -v -u $user php");
  143. shell_exec('nohup php yii content/worker &>./console/runtime/logs/amqp.log &');
  144. }
  145. }