# 前言
上一小节,我们介绍了 pheanstalk 的使用。
这一小节,我们使用 pheanstalk 来完成一个小功能。
# 需求设计
![](https://box.kancloud.cn/6594c0ed8f55c8edd0e20eed93ef6726_542x666.png)
主要是一个注册功能,当用户注册成功之后,会接收到两条信息,分别是邮件和短信。
这里的邮件和短信,我们以消息队列来实现,这样可以加快服务端对用户端的响应,提高体验。
# 实现规划
1、以 php + mysql + beanstalkd 为基础架构
2、使用 pheanstalk 操作 beanstalkd、使用 medoo 操作 mysql
3、使用 composer 作为包管理工具
4、有三个文件:
- register.php 负责接收注册请求、用户信息入库、生成短信任务和邮件任务
- sms.php 短信任务消费者
- email.php 邮件任务消费者
# 代码实现
## 包安装
```
composer require pda/pheanstalk
composer require catfan/medoo
```
## demo.sql
```
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) COLLATE utf8_bin NOT NULL,
`email` varchar(100) COLLATE utf8_bin NOT NULL,
`phone` char(11) COLLATE utf8_bin NOT NULL,
`password` varchar(255) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
KEY `user_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
```
## register.php
```
<?php
// +----------------------------------------------------------------------
// | register.php
// +----------------------------------------------------------------------
// | Description: 注册
// +----------------------------------------------------------------------
// | Time: 2019/1/16 下午1:47
// +----------------------------------------------------------------------
// | Author: Object,半醒的狐狸<2252390865@qq.com>
// +----------------------------------------------------------------------
require_once "../vendor/autoload.php";
use Medoo\Medoo;
use Pheanstalk\Pheanstalk;
// demo 只为演示如何使用 pheanstalk ,所以不做精细化处理,如注入、加密、字段设计等,只能用于简单测试
$param = getopt('u:p:e:m:');
$username = $param['u'];
$password = $param['p'];
$email = $param['e'];
$phone = $param['m'];
$db = new Medoo([
'database_type' => 'mysql',
'database_name' => 'test',
'server' => 'mysql',
'username' => 'root',
'password' => 'root',
// [optional]
'charset' => 'utf8',
'port' => 3306,
]);
$alreadyIn = $db->has('user', [
'OR' => [
'username' => $username,
'email' => $email,
'phone' => $phone,
]
]);
if ($alreadyIn) {
echo '用户已存在';
die();
}
// 插入用户
$user = [
'username' => $username,
'email' => $email,
'phone' => $phone,
'password' => $password
];
$db->insert('user', $user);
$user['id'] = $db->id();
$jobData = json_encode($user);
$conn = Pheanstalk::create('beanstalkd', 11300, 10);
$conn->useTube('register_sms');
$smsJob = $conn->put($jobData);
$conn->useTube('register_email');
$emailJob = $conn->put($jobData);
echo '注册成功:<br>';
print_r($user);
print_r($smsJob);
print_r($emailJob);
```
## sms.php
```
<?php
// +----------------------------------------------------------------------
// | sms.php
// +----------------------------------------------------------------------
// | Description:
// +----------------------------------------------------------------------
// | Time: 2019/1/16 下午1:47
// +----------------------------------------------------------------------
// | Author: Object,半醒的狐狸<2252390865@qq.com>
// +----------------------------------------------------------------------
include_once "../vendor/autoload.php";
use Pheanstalk\Pheanstalk;
$conn = Pheanstalk::create('beanstalkd', 11300, 10);
$conn->watchOnly('register_sms');
function sendSms($user)
{
return random_int(0, 1); // 取随机数,模拟发送成功与失败
}
while (1) {
try {
$job = $conn->reserveWithTimeout(1);
if ($job === null) {
throw new Exception('没有任务');
}
// 发送邮件
if (sendSms($job->getData())) {
// 处理成功
$conn->delete($job);
} else {
// 处理失败
$conn->release();
}
} catch (Exception $e) {
print_r($e->getMessage());
die();
}
echo "欢迎短信发送成功<br>";
usleep(500000);
}
```
## email.php
```
<?php
// +----------------------------------------------------------------------
// | email.php
// +----------------------------------------------------------------------
// | Description:
// +----------------------------------------------------------------------
// | Time: 2019/1/16 下午1:47
// +----------------------------------------------------------------------
// | Author: Object,半醒的狐狸<2252390865@qq.com>
// +----------------------------------------------------------------------
include_once "../vendor/autoload.php";
use Pheanstalk\Pheanstalk;
$conn = Pheanstalk::create('beanstalkd', 11300, 10);
$conn->watchOnly('register_email');
function sendEmail($user)
{
return random_int(0, 1); // 取随机数,模拟发送成功与失败
}
while (1) {
try {
$job = $conn->reserve();
if ($job === null) {
throw new Exception('没有任务');
}
// 发送邮件
if (sendEmail($job->getData())) {
// 处理成功
$conn->delete($job);
} else {
// 处理失败
$conn->release();
}
} catch (Exception $e) {
print_r($e->getMessage());
die();
}
echo "欢迎邮件发送成功<br>";
usleep(500000); // 每 500ms 接收 job
}
```
# 测试
先确保,你的 php 脚本能正确地连接到 mysql 和 beanstalkd
然后,打开三个 shell 窗口,每个窗口按顺序运行一条命令:
```
php sms.php
```
```
php email.php
```
```
php register.php -u username -p password -e email@qq.com -m 15866668888
```
其中,sms.php 和 email.php 两个窗口,会阻塞运行。
每当有运行过 register.php 之后,三个窗口就分别会有输出。
大致如下:
![](https://box.kancloud.cn/6615a7ac3bd0488005f38895919f6d1c_1463x469.png)
![](https://box.kancloud.cn/5c7906227ac3b83f90e21f64405a7e53_932x121.png)
![](https://box.kancloud.cn/f1ec5c004d7393d705f33bb4c6722221_777x71.png)