web-dev-qa-db-ja.com

PHPのmail()を非同期にする

私はPHPのmail()を使用しています ssmtp を使用しています。これにはキュー/スプールがなく、AWS SESと同期しています。

SwiftMail を使用してスプールを提供できると聞きましたが、mail()で現在使用しているような簡単なレシピを使用してそれを使用することはできませんでした。

最小限のコードで非同期メールを提供したい。メールの送信に失敗したかどうかは気にしませんが、ログがあればいいでしょう。

簡単なヒントやコツはありますか?本格的なメールサーバーを実行するのに不足ですか? sendmailラッパーが答えになるかもしれないと思っていましたが、Nohupを計算できませんでした。

14
hendry

php-fpm

fastcgi_finish_request を使用するには、php-fpmを実行する必要があります。

echo "I get output instantly";
fastcgi_finish_request(); // Close and flush the connection.
sleep(10); // For illustrative purposes. Delete me.
mail("[email protected]", "lol", "Hi");

ユーザーへのリクエストの終了後に処理する任意のコードをキューに入れるのは非常に簡単です。

$post_processing = [];
/* your code */
$email = "[email protected]";
$subject = "lol";
$message = "Hi";

$post_processing[] = function() use ($email, $subject, $message) {
  mail($email, $subject, $message);
};

echo "Stuff is going to happen.";

/* end */

fastcgi_finish_request();

foreach($post_processing as $function) {
  $function();
}

流行に敏感なバックグラウンドワーカー

カールを瞬時にタイムアウトして、新しいリクエストに対応させます。クールになる前に、共有ホストでこれを行っていました。 (それはクールではありません)

if(!empty($_POST)) {
  sleep(10);
  mail($_POST['email'], $_POST['subject'], $_POST['message']);
  exit(); // Stop so we don't self DDOS.
}

$ch = curl_init("http://" . $_SERVER['HTTP_Host'] . $_SERVER['REQUEST_URI']);

curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
  'email' => '[email protected]',
  'subject' => 'foo',
  'message' => 'bar'
]);

curl_exec($ch);
curl_close($ch);

echo "Expect an email in 10 seconds.";
9
Kit Sunde

これを行う方法はたくさんありますが、スレッドの処理は必ずしも正しい選択ではありません。

  • register_shutdown_function:シャットダウン関数は、応答の送信後に呼び出されます。実際には非同期ではありませんが、少なくともリクエストが遅くなることはありません。実装については、例を参照してください。
  • Swift pool:symfonyを使用すると、スプールを簡単に使用できます。
  • Queue:送信するメールをキューシステムに登録し(RabbitMQ、MySQL、redisなどで実行できます)、消費するcronを実行します待ち行列。 fromtomessagesent(メールを送信したときにブール値をtrueに設定)のようなフィールドを持つMySQLテーブルのような単純なもので実行できます。

Register_shutdown_functionの例

_<?php
class MailSpool
{
  public static $mails = [];

  public static function addMail($subject, $to, $message)
  {
    self::$mails[] = [ 'subject' => $subject, 'to' => $to, 'message' => $message ];
  }

  public static function send() 
  {
    foreach(self::$mails as $mail) {
      mail($mail['to'], $mail['subject'], $mail['message']);
    }
  }
}

//In your script you can call anywhere
MailSpool::addMail('Hello', '[email protected]', 'Hello from the spool');


register_shutdown_function('MailSpool::send');

exit(); // You need to call this to send the response immediately
_
16
magnetik

PHPMailerを指定してAWS SESを使用します。

この方法は非常に高速で(1秒あたり数百のメッセージ)、必要なコードはそれほど多くありません。

$mail = new PHPMailer;
$mail->isSMTP();                                      // Set mailer to use SMTP
$mail->Host = 'ssl://email-smtp.us-west-2.amazonaws.com';  // Specify main and backup SMTP servers

$mail->SMTPAuth = true;                               // Enable SMTP authentication

$mail->Username = 'blah';                 // SMTP username
$mail->Password = 'blahblah';                           // SMTP password


$mail->SMTPSecure = 'tls';                            // Enable TLS encryption, `ssl` also accepted
$mail->Port = 443; 

私があなたの質問を正しく解釈したかどうかわかりませんが、これが役に立てば幸いです。

5

Pthreads はあなたの友達です:)
これは、本番アプリケーションで作成した方法のサンプルです

class AsynchMail extends Thread{
    private $_mail_from;
    private $_mail_to;
    private $_subject;

    public function __construct($subject, $mail_to, ...) {
        $this->_subject = $subject;
        $this->_mail_to = $mail_to;
        // ... 
    }
    // ...
    // you must redefine run() method, and to execute it we must call start() method
    public function run() {
        // here put your mail() function
        mail($this->_mail_to, ...);
    }
}

テストスクリプトの例

$mail_to_list = array('[email protected]', '[email protected]',...);
foreach($mail_to_list as $mail_to) {
    $asynchMail = new AsynchMail($mail_to);
    $asynchMail->start();
}

PHPでのスレッドのインストールと使用についてさらにヘルプが必要な場合はお知らせください
ロギングシステムでは、使用することを強くお勧めします Log4PHP :パワフルで使いやすく、設定も簡単
メールを送信する場合は、 PHPMailer を使用することも強くお勧めします

2
Halayem Anis

beanstalkd を使用して非同期のphp実行を使用しています。
これはシンプルなメッセージキューで、非常に軽量で統合が簡単です。

次のphpラッパーを使用してphp https://github.com/pda/pheanstalk を実行すると、次のようにしてメールワーカーを実装できます。

use Beanstalk\Client;
$msg="dest_email##email_subject##from_email##email_body";

$beanstalk = new Client(); 
$beanstalk->connect();
$beanstalk->useTube('flux'); // Begin to use tube `'flux'`.
$beanstalk->put(
    23,  // Give the job a priority of 23.
    0,   // Do not wait to put job into the ready queue.
    60,  // Give the job 1 minute to run.
    $msg // job body
);
$beanstalk->disconnect();

次に、別のphpファイルに配置されたコードでジョブが実行されます。
何かのようなもの:

use Beanstalk\Client;
$do=true;

try {
    $beanstalk = new Client();
    $beanstalk->connect();
    $beanstalk->watch('flux');

} catch (Exception $e ) {
    echo $e->getMessage();
    echo $e->getTraceAsString();
    $do = false;
}

while ($do) {
    $job = $beanstalk->reserve(); // Block until job is available.
    $emailParts = explode("##", $job['body'] );

    // Use your SendMail function here

    if ($i_am_ok) {
        $beanstalk->delete($job['id']);
    } else {
        $beanstalk->bury($job['id'], 20);
    }
}
$beanstalk->disconnect();

このphpファイルは、独立したphpプロセスとして個別に実行できます。これをsender.phpとして保存すると、Unixでは次のように実行されます。

php /path/to/sender/sender.php & && disown

このコマンドはファイルを実行し、プロセスを停止せずにコンソールを閉じるか、現在のユーザーをログアウトすることもできます。
Webサーバーがphpコマンドラインインタープリターと同じphp.iniファイルを使用していることも確認してください。 (お気に入りのphp.iniへのリンクを使用して解決される可能性があります

お役に立てば幸いです。

1
Evhz

AsyncへようこそPHP https://github.com/shuchkin/react-smtp-client

$loop = \React\EventLoop\Factory::create();

$smtp = new \Shuchkin\ReactSMTP\Client( $loop, 'tls://smtp.google.com:465', '[email protected]','password' );

$smtp->send('[email protected]', '[email protected]', 'Test ReactPHP mailer', 'Hello, Sergey!')->then(
    function() {
        echo 'Message sent via Google SMTP'.PHP_EOL;
    },
    function ( \Exception $ex ) {
        echo 'SMTP error '.$ex->getCode().' '.$ex->getMessage().PHP_EOL;
    }
);

$loop->run();
0
Sergey Shuchkin

あなたの最善の策は、スタックまたはスプールのパターンです。これはかなり単純で、2つのステップで説明できます。

  • 現在のスレッドで送信済みフラグのあるテーブルにメールを保存します。
  • Cronまたはajaxを使用してメール処理のphpファイルを繰り返し呼び出し、データベースから上位10または20件の未送信メールを取得し、送信済みとしてフラグを付け、実際にお気に入りのメール方法で送信します。
0
catbadger

これを行う簡単な方法は、メールを非同期で処理するコードを呼び出すことです。

たとえば、次のコードを含むemail.phpというファイルがあるとします。

// Example array with e-mailaddresses
$emailaddresses = ['[email protected]', '[email protected]', '[email protected]'];

// Call your mail function
mailer::sendMail($emailaddresses);

次に、これを通常のリクエストで非同期に呼び出すことができます

exec('Nice -n 20 php email.php > /dev/null & echo $!');

そして、email.phpが電子メールの送信を完了するのを待たずに、リクエストは完了します。電子メールを送信するファイルにログを追加することもできます。

変数は、呼び出されたファイル名と> /dev/nullの間でexecに渡すことができます。

exec('Nice -n 20 php email.php '.$var1.' '.$var2.' > /dev/null & echo $!');

escapeshellarg() を使用して、これらの変数が安全であることを確認してください。呼び出されたファイルでは、これらの変数は $ argv で使用できます

0
PWD