开发者

How to execute a function until it succeeds?

I am writing a function to send a mail to specific user?

What I am trying to do is, if error occurs I want to keep on sending the mail until the mail is sent.

    function sendmail() {
        $name = mysql_escape_string($name);
        $email = mysql_escape_string($email);
        $phone = mysql_escape_string($phone);
        $content = nl2br(mysql_escape_string($content));
        $subject = "mail from ".$name." (".$email.", Ph: ".$phone.")";
        $mail = @mail($feedback,$subject,$content,$headers);
        if($mail) { echo "Your mailis send"; }
        else { echo "We are sorry for the inconvienience, but we could not send your mailnow."; }
}   

the above function displays error message but instead of giving the error, I want to try to send the mail until the mail is finally se开发者_StackOverflownt.


Design for Error Handling:

What if the page cannot send? The Daemon stopped, the user entered malformed data, the connection was lost, could write disk, database is down for mainenance, etc. What do you want the server to do with the user request? I advise setting a queue up on failure to retry later. There are a lot of good queue systems out there. Cron the retry if you have to (that way it is not hanging on a client request). Log tries and if certain error conditions occur then notify the administrator.

This is what an operations/infrastructure team is built for, but you can start with some good tooling and a well designed strategy for error and exception handling. The people who inherit your code will thank you.

I have been reading the Pragmatic Programmer and I have to highly recommend it for dealing specifically with this question. It sets a standard in the way you think about error/exception handling and for me definitely cleared the fog regarding what an error is, vs an exception, vs an assertion condition (or impossible condition). In particular Chapter 4 Pragmatic Paranoia:

"When everyone actually is out to get you, paranoia is just good thinking" -Woody Allen


So, what could possibly have gone wrong if mail() fails?

  • If your system is configured to hand mail to a local daemon, the daemon may be dead or misconfigured.
  • If you're talking to an SMTP server directly, the server is most likely dead, or something is misconfigured.

In both cases, how would you know when the responsible parties become responsive again? How long do you want to keep retrying?
The local mail daemon shouldn't ever die, or at least it should be resurrected immediately by the system. If that doesn't happen, you should fix the server instead of handling this situation in code.
If you're talking to an SMTP server, the only legitimate case where retrying a second or so later may actually succeed is if the server is being overloaded or DDoS'd and you're lucky enough to get through. That case is probably rare enough not to worry about though.

Either way, if mail() returns false, there's usually no point in trying again until you fix some other problem. Therefore, just fail gracefully, keep a complete log of things with a copy of the unsent mail and send it again later manually.

Anything beyond that point is out of your control, i.e. even if mail() succeeds, the email may or may not arrive at its destination. It's your job as server admin to catch bounced or error'd mails and act on them manually/try to figure out what's wrong.


You realize that if your mail server is out of business for more than 30 second it will just exhaust the time it has (which usually is 30 seconds) with that while loop?

The error is a better way probably, it will not wait to re-send it will just send over and over, possibly 782428424 times before those 30 seconds are over, wasting bandwith and probabl annoying some people in the processor.

What you would better do is on failure write the contents to some database which you flush and try to send every 5 minutes or so until success.


I would recommend putting in a counter and/or sleep statement.

The counter is to ensure that the script eventually quits and doesn't just hang around and keeps executing a failing statement.

The sleep statement is for "dealing" with momentary load issues.

Something like:

$remainingTries = 5;
while ($remainingTries > 0 && !sendmail())
{
  $remainingTries--; // we used a try, decrement
  sleep(5);          // wait five seconds before next try
}


function sendmail() {
        $name = mysql_escape_string($name);
        $email = mysql_escape_string($email);
        $phone = mysql_escape_string($phone);
        $content = nl2br(mysql_escape_string($content));
        $subject = "mail from ".$name." (".$email.", Ph: ".$phone.")";

        $retries = 0;
        while (!@mail($feedback,$subject,$content,$headers))
        {
            $retries++;

            if ($retries >= MAX_RETRIES)
            {
                break;
            }

            sleep(WAITING_PERIOD);
        }

        if ($retries < MAX_RETRIES) { echo "Your mail has been sent."; }
        else { echo "We are sorry for the inconvienience, but we could not send your mail now." ; }
}

Set WAITING_PERIOD and MAX_RETRIES to something that works for you, just don't set MAX_RETRIES too long or you may get stuck for a long wait, nor set WAITING_PERIOD too low, or you may flood your mail agent.


Try something like this:

function doSomething($arg1, $arg2, $retry = 10)
{
    $result = false;

    /* do stuff */

    if ($result === false)
    {
        static $original = null;

        if (is_null($original))
        {
            set_time_limit(0);
            $original = $retry;
        }

        sleep(pow(2, $original - $retry));

        while (--$retry > 0)
        {
            return doSomething($arg1, $arg2, $retry);
        }
    }

    return $result;
}

If it fails, it retry $retry times and wait:

1 second before the 1st retry
2 seconds before the 2nd retry
4 seconds before the 3rd retry
8 seconds before the 4th retry
16 seconds before the 5th retry
32 seconds before the 6th retry
64 seconds before the 7th retry
128 seconds before the 8th retry
256 seconds before the 9th retry
512 seconds before the 10th retry
...
2 ^ ($retry - 1) seconds before the ($retry)th retry

By doing this you ensure you don't get your server overloaded.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜