小赖子的英国生活和资讯

通过 Crontab 后台定时发邮件

阅读 桌面完整版

很多人设计 WEB 应用的时候会需要这么一个给用户 发邮件的功能. 这时候就可以通过 类似 PHPMailer 这种开源的类来即时的发邮件. 这有个不好的地方就是 因为是同步发送方式, 页面在邮件发出去之前 会卡住 而且会造成服务器的负担 可扩展性并不强 (如果一天数以万计的邮件需要发 那么高峰期服务器定不能胜任).

最好的设计方法是后台异步的发邮件, 那么我们首先需要一个SQL表格来存放待发送的邮件:

-- phpMyAdmin SQL Dump
-- version 4.2.6
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jul 05, 2015 at 01:24 PM
-- Server version: 5.5.40-0ubuntu0.14.04.1
-- PHP Version: 5.5.9-1ubuntu4.5

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

-- --------------------------------------------------------

--
-- Table structure for table `mails`
--

CREATE TABLE IF NOT EXISTS `mails` (
`id` bigint(32) unsigned NOT NULL,
  `sender` varchar(255) NOT NULL,
  `dest` varchar(255) NOT NULL,
  `subject` varchar(255) NOT NULL,
  `content` text NOT NULL,
  `sent` tinyint(1) NOT NULL,
  `ts` datetime NOT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

--
-- Indexes for dumped tables
--

--
-- Indexes for table `mails`
--
ALTER TABLE `mails`
 ADD PRIMARY KEY (`id`), ADD KEY `sent` (`sent`), ADD KEY `ts` (`ts`);

--
-- AUTO_INCREMENT for dumped tables
--

--
-- AUTO_INCREMENT for table `mails`
--
ALTER TABLE `mails`
MODIFY `id` bigint(32) unsigned NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

sql-mails-table

然后 在即时发邮件的代码就能被以下代码取代, 简单的来说就是要发邮件的时候就向数据库这个表里写一行数据.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    $ts = date('Y-m-d h:i:s');
    $sender = mysql_real_escape_string($_POST['user']); 
    $subject = mysql_real_escape_string($_POST['subject']);
    $content = mysql_real_escape_string($_POST["content"]);    
    $query = "
      insert into `mails` set
          `sender` = '$sender',
          `dest` = 'im@helloacm.com',
          `subject` = '$subject',
          `content` = '$content',
          `ts` = '$ts',
          `sent` = 0  
    ";
    // 异步要发的邮件
    $result = mysql_query($query);
    $ts = date('Y-m-d h:i:s');
    $sender = mysql_real_escape_string($_POST['user']);	
    $subject = mysql_real_escape_string($_POST['subject']);
    $content = mysql_real_escape_string($_POST["content"]);    
    $query = "
      insert into `mails` set
          `sender` = '$sender',
          `dest` = 'im@helloacm.com',
          `subject` = '$subject',
          `content` = '$content',
          `ts` = '$ts',
          `sent` = 0  
    ";
    // 异步要发的邮件
    $result = mysql_query($query);

然后 我们需要创建一个 process_email.php 文件用来把还未发送的邮件列表取出来并通过 PHPMailer 发送.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  set_time_limit(600);
  $link = mysql_connect("localhost", "user", "password") or die(mysql_error());
                              
  mysql_select_db('database') or die(mysql_error());
  
  mysql_query("SET NAMES 'utf8'");
  mysql_query("SET CHARACTER SET utf8");
  mysql_set_charset("utf-8", $link);
    
  // 获得即将要发送的邮件列表 limit 100 设置每次处理 100封邮件 可以去掉或者改其它数字
  $query = "select * from `mails` where `sent` = 0 limit 100";
  
  $result = mysql_query($query) or die(mysql_error());
  require("PHPMailerAutoload.php");
  $mailer = new PHPMailer();
  $mailer->CharSet = "UTF-8";
  $mailer->IsHTML(true);
  
  // 一封一封的发
  while ($row = mysql_fetch_array($result)) {
    $sender = $row['sender'];
    $dest = $row['dest'];
    $subject = $row['subject'];
    $content = $row['content'];
    $id = $row['id']; 
    $mailer->From = $sender;
    $mailer->FromName = $sender;
    $mailer->ClearAllRecipients();
    $mailer->AddAddress($dest, $dest);
    $mailer->Subject = $subject;
    $mailer->Body = $content;
    if ($mailer->Send()) {
      echo "$sender - OK! <br />";
      // 标记为已经发送
      $query = "update `mails` set `sent` = 1 where `id` = $id";
      mysql_query($query) or die(mysql_error());
    } else {
      echo "$sender - failed! $mailer->ErrorInfo <br />";
    } 
  }
  set_time_limit(600);
  $link = mysql_connect("localhost", "user", "password") or die(mysql_error());
                              
  mysql_select_db('database') or die(mysql_error());
  
  mysql_query("SET NAMES 'utf8'");
  mysql_query("SET CHARACTER SET utf8");
  mysql_set_charset("utf-8", $link);
    
  // 获得即将要发送的邮件列表 limit 100 设置每次处理 100封邮件 可以去掉或者改其它数字
  $query = "select * from `mails` where `sent` = 0 limit 100";
  
  $result = mysql_query($query) or die(mysql_error());
  require("PHPMailerAutoload.php");
  $mailer = new PHPMailer();
  $mailer->CharSet = "UTF-8";
  $mailer->IsHTML(true);
  
  // 一封一封的发
  while ($row = mysql_fetch_array($result)) {
    $sender = $row['sender'];
    $dest = $row['dest'];
    $subject = $row['subject'];
    $content = $row['content'];
    $id = $row['id']; 
    $mailer->From = $sender;
    $mailer->FromName = $sender;
    $mailer->ClearAllRecipients();
    $mailer->AddAddress($dest, $dest);
    $mailer->Subject = $subject;
    $mailer->Body = $content;
    if ($mailer->Send()) {
      echo "$sender - OK! <br />";
      // 标记为已经发送
      $query = "update `mails` set `sent` = 1 where `id` = $id";
      mysql_query($query) or die(mysql_error());
    } else {
      echo "$sender - failed! $mailer->ErrorInfo <br />";
    } 
  }

最后 我们需要 del-email.php 用来清理已经发送过的邮件 可以是一天一次.

1
2
3
4
5
6
7
8
9
10
11
12
  set_time_limit(600);
  mysql_connect("localhost", "user", "password") or die(mysql_error());
                              
  function run_query($query) {
    echo $query;
    mysql_query($query) or die(mysql_error());
    echo " ***OK!*** \n";
  }         
  
  mysql_select_db('database') or die(mysql_error());  
  $query = 'delete from `mails` where `sent` = 1';  
  run_query($query); 
  set_time_limit(600);
  mysql_connect("localhost", "user", "password") or die(mysql_error());
                              
  function run_query($query) {
    echo $query;
    mysql_query($query) or die(mysql_error());
    echo " ***OK!*** \n";
  }         
  
  mysql_select_db('database') or die(mysql_error());  
  $query = 'delete from `mails` where `sent` = 1';  
  run_query($query); 

然后我们就可以通过 crontab -e 加入两个任务 比如一个一分钟检查邮件列表并发送 另一个一天检查一次清理已经发送的邮件 这样的话服务器可扩展性就大大增强 (可以避免短时间大量邮件的发送)

英文: https://helloacm.com/send-emails-using-crontab-for-your-web-applications/

强烈推荐

微信公众号: 小赖子的英国生活和资讯 JustYYUK

阅读 桌面完整版
Exit mobile version