1

PDF 添付ファイルを使用して、foreach ループで一度に 1 つずつ、サブスクライバーのリストに電子メールを送信する cron ジョブがあります。cron スクリプトから次のメッセージを受け取りました。

Fatal error: Allowed memory size of 94371840 bytes exhausted (tried to allocate 78643193 bytes)

このエラーを防ぐにはどうすればよいですか?

また、すべてのサブスクライバーへの送信が完了していないことは確かですが、全員に送信されなかった場合にどこで再度受信するかを知るには、どうすればこれを追跡できますか?

アップデーター:コード サンプルは次のとおりです: (ちなみに Zend Framework を使用しています)

public function send(Default_Model_MyEmail $myEmail)
{
    if (null != ($id = $myEmail->attachmentId)) {
        $file = new Default_Model_File();
        $file->find($id);
        $filepath = APPLICATION_UPLOADS_DIR . '/' . $file->getActualFilename();

        $attachment = new Zend_Mime_Part(file_get_contents($filepath));
        $attachment->type = $file->getMimeType();
        $attachment->disposition = Zend_Mime::DISPOSITION_ATTACHMENT;
        $attachment->encoding = Zend_Mime::ENCODING_BASE64;
        $attachment->filename = $file->getDisplayFilename();    
    }
    $transport = new Zend_Mail_Transport_Smtp('localhost');

    $mail = new Zend_Mail('utf-8');
    $mail->setFrom('from@address', 'From Name');
    $mail->setReplyTo('replyto@address');
    $mail->setSubject($myEmail->subject);
    if (isset($attachment)) {
        $mail->addAttachment($attachment);
    }

    $subscribers = $this->getSubscribers();
    foreach ($subscribers as $subscriber) {
        $mail->addTo($subscriber->email);
        $bodyText = $myEmail->body
            . "\r\n\r\nIf for any reason you would like to be removed from this mailing list, "
            . "please visit \r\nhttp://myapp.com/myemail/unsubscribe/email/"
            . $subscriber->email;
        $mail->setBodyText($bodyText);
        $mail->send($transport);
        $mail->clearRecipients();
    }
}

更新:$transport変数を再利用しています。これが複数のサブスクライバーに送信する正しい方法であるという印象を受けましたが、これが原因でしょうか? どう思いますか?

更新:メモリ使用量ステートメントを出力する一連のログ ステートメントを追加しましたが、今何をすべきか本当にわかりません。メールを送信するたびにメモリ使用量が増加します。サブスクライバ リストが 200 の場合、160 になり、メモリが不足します。私は何をすべきか?

4

4 に答える 4

2

エラー メッセージに基づいて、コードが 78 MB のチャンクを割り当てようとしているようです。

非常に大きなメモリ チャンクを一度に割り当てようとする可能性のあるものがないかコードを確認してください。失敗した割り当てブロックが大きいため、これはおそらく小さいオブジェクトの解放の失敗が原因ではありません。

この問題の原因となっているコードのスニペットを投稿していただければ、喜んで確認し、より詳細な返信をお待ちしております。

全員に送信したかどうかは、メールサーバー(SMTPサーバー)のチェックで送信メッセージのログが書き込まれます。その場合、メールを受信した人のリストを取得できる場合があります。一般に、将来クラッシュが発生した場合に備えて、送信された各電子メールをファイルまたはデータベースに記録するように PHP コードを変更することをお勧めします。

コードを見た後に編集:

表面的には、電子メールと添付ファイルを表すオブジェクトが一度作成され、再利用されるように見えます。

コードをローカルでデバッグすることをお勧めします。

まず、本番環境と同じメモリ制限を設定します。このリソースを試して、その方法を理解してください。

次に、ループ内にデバッグ出力を追加して、各反復後に使用可能なメモリ量を確認します。

最後に、コードをローカルで実行します。ただし、できれば自分のドメインの電子メール アドレスまたは既知の不正な電子メール アドレスに置き換えます (スパムを送信しないようにするため)。送信の進行に合わせてメモリ使用量を監視します。

これにより、エラーの原因を絞り込むことができます。

編集2:

OK、メモリ使用量が増え続けていることを確認した後、Google のちょっとした魔法のように、これは既知の問題であり、回避策が報告されていることがわかりました。

于 2009-12-31T19:40:10.830 に答える
1

@ Malfist の回答に加えて、次のことができます。

  • ループの反復ごとに新しい変数を追加するのではなく、変数を再利用していることを確認してください。
  • 電子メールが正常に送信されたときにシステムに記録させ、一定数の電子メールの後に送信を停止します。次回 cron が実行されると、送信先が残っている人が特定され、再開されます。
于 2009-12-31T19:41:37.770 に答える
0

メモリ使用量を減らすか、メモリ制限を増やしてください。

于 2009-12-31T19:39:17.523 に答える