# 前言
上一小節,我們介紹了 pheanstalk 的使用。
這一小節,我們使用 pheanstalk 來完成一個小功能。
# 需求設計

主要是一個注冊功能,當用戶注冊成功之后,會接收到兩條信息,分別是郵件和短信。
這里的郵件和短信,我們以消息隊列來實現,這樣可以加快服務端對用戶端的響應,提高體驗。
# 實現規劃
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 之后,三個窗口就分別會有輸出。
大致如下:


