Symfony Swiftmailer停止发送进程以显示状态

时间:2017-11-21 20:59:16

标签: php symfony for-loop swiftmailer

在我们的应用程序中,对于某些操作,我们会向相当多的用户(数百到数千)发送通知和电子邮件。出于某些目的,我们必须使用foreach循环单独发送这些电子邮件/通知。 对于大约200-300个用户,这个工作正常,但是一旦有更多用户被通知,我们会在一段时间后暂停。

我现在想要做的是,重定向到新页面,例如创建一个文档,并通过发送20个电子邮件来处理发送到那里的电子邮件/通知,然后显示更新,例如“已发出20个xxx电子邮件/通知”,然后继续发送下一个20,显示更新等等。

是否有任何机构已经做过类似的事情或对此有想法? 到目前为止,这是我的代码:

       {
    document gets created...
              $em->persist($document);
              $em->flush();

              $this->addFlash(
                'success',
                'Your document has been created!'
                );

              return $this->redirectToRoute('documentBundle_document_send_notifications',  array('id' => strtok($document->getId(), '_')));

            }

      /**
    * @Route("/document/sendNotifications/{id}", name="documentBundle_document_send_notifications", requirements={"id" = "\d+"})
    */
    public function sendDocumentNotifications($id){
      $em = $this->getDoctrine()->getManager();
      $document = $em->getRepository('DocumentBundle:Document')->findOneById($id);
      $statusRepository = $em->getRepository('DocumentBundle:Status');

      /*
      * NOTIFICATION MANAGEMENT
      */

      //Users
      $user = $em->getRepository('UserBundle:User');
      $user = $user->findByDocumentAgency($user, $document);
      $users = array_unique($user->getResult());

 if(count($users)>0){
        /*
        * SEND NOTIFICATION
        */
        foreach ($users as $user){
          $manager = $this->get('mgilet.notification');
          $notif = $manager->generateNotification('A Document has been created!');
          $manager->addNotification($user, $notif);
        }

        /*
        * SEND EMAIL
        */

       foreach ($users as $user){
            $recipient = $user->getEmail();
            $this->get('MailerHelper')->sendMessage($recipient,...);
          }
        }
      }
        return $this->redirectToRoute('documentBundle_document_list');
    }

修改

添加了一个控制台命令以作为后台进程运行

/**
* @Route("/document/sendNotifications/{id}", name="documentBundle_document_send_notifications", requirements={"id" = "\d+"})
*/
public function sendDocumentNotifications($id){
  $em = $this->getDoctrine()->getManager();
  $document = $em->getRepository('DocumentBundle:Document')->findOneById($id);
  $process = new Process('php app/console app:send-notifications', $id );
  $process->run();

  dump($process);

  return $this->redirectToRoute('documentBundle_document_bulkDeactivate');


}

转储进程时,我得到了正确的commandLine,输入是正确的文档ID,但它说:

   Process {#1539 ▼
  -callback: null
  -commandline: "php app/console app:send-notifications"
  -cwd: "/srv/http/sp/web"
  -env: null
  -input: "320"
  -starttime: 1511378616.9664
  -lastOutputTime: 1511378616.9852
  -timeout: 60.0
  -idleTimeout: null
  -options: array:2 [▼
    "suppress_errors" => true
    "binary_pipes" => true
  ]
  -exitcode: 1
  -fallbackStatus: []
  -processInformation: array:8 [▼
    "command" => "php app/console app:send-notifications"
    "pid" => 29887
    "running" => false
    "signaled" => false
    "stopped" => false
    "exitcode" => 1
    "termsig" => 0
    "stopsig" => 0

然后我尝试了 $ process-> mustRun()并输出以下错误:

  
    

命令“php app / console app:send-notifications”失败。

  
     

退出代码:1(一般错误)

     

工作目录:/ srv / http / sp / web

     

输出:   ================无法打开输入文件:app / console

     

错误输出:

     

500内部服务器错误 - ProcessFailedException

1 个答案:

答案 0 :(得分:0)

TL; DR:为了防止超时,将您的流程卸载到后台进程。

第一步:使用假脱机

快速获胜将是to Spool Emails

  

当您使用SwiftmailerBundle发送电子邮件时   Symfony应用程序,它将默认立即发送电子邮件。   但是,您可能希望避免性能损失   Swift Mailer和电子邮件传输之间的通信   可能会导致用户在电子邮件中等待下一页加载   正在发送。

实际邮件将由命令行脚本发送: php bin/console swiftmailer:spool:send --env=prod

下一步:创建处理通知的命令

了解如何create a console command。像这样的东西(没有经过测试,只是作为一个例子):

class NotificationCommand extends ContainerAwareCommand
{

    protected function configure()
    {
        $this
            // the name of the command (the part after "bin/console")
            ->setName('app:send-notifications')

            // the short description shown while running "php bin/console list"
            ->setDescription('Send notifications to information users about a new document.')

            ->addArgument('document', InputArgument::REQUIRED, 'The ID of the new document.')

        ;
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
          $em = $this->getContainer()->get('doctrine')->getManager();
          $document = $em->getRepository('DocumentBundle:Document')->findOneById($input->getArgument('document'));
          $statusRepository = $em->getRepository('DocumentBundle:Status');

          /*
          * NOTIFICATION MANAGEMENT
          */

          //Users
          $user = $em->getRepository('UserBundle:User');
          $user = $user->findByDocumentAgency($user, $document);
          $users = array_unique($user->getResult());

        if(count($users)>0){
            /*
            * SEND NOTIFICATION
            */
            foreach ($users as $user){
              $manager = $this->get('mgilet.notification');
              $notif = $manager->generateNotification('A Document has been created!');
              $manager->addNotification($user, $notif);
            }

            /*
            * SEND EMAIL
            */

           foreach ($users as $user){
                $recipient = $user->getEmail();
                $this->get('MailerHelper')->sendMessage($recipient,...);
              }
            }
          }
    }
}

要执行此操作,请在命令行上运行bin/console app:send-notifications 1

安排你的命令

我想您不想手动登录服务器来发送通知,因此您可以创建一个可以由cron作业安排的常规“发送通知(如果需要)”命令。

下一步,您可以查看排队命令的方法。以JMSJobQueueBundle为例。

奖励提示:首先让您的控制器更薄

尝试让控制器更薄。阅读this旧的,但仍然有趣的博客。如果您创建服务并从控制器调用它们(而不是使用 fat <​​/ em>控制器),那么将代码从控制器移除到后台作业(反之亦然)的“重构”工作要容易得多。< / p>