前言.
最近一直在赶项目,心里可是百感交集。一方面是由于学而知不足,另一方面更是感叹互联网的发展日新月异。有时候你不进步就是等于在退步。小侠最近心血来潮想用好好地在项目中用下队列。尽管没什么高并发的存在。可是对于一些业务解耦队列还是一个不错的解决方案。
什么是消息队列?消息队列可以用在什么地方?
#消息队列
“消息队列”是指消息的传输过程中保存消息的容器。
“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。
#应用场景
以下介绍消息队列在实际应用中常用的使用场景。异步处理,应用解耦,流量削锋和消息通讯四个场景。
异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种:
1.串行的方式。
2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。 因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。 小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢? 引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:
传统模式的缺点:
1) 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;
2) 订单系统与库存系统耦合;
如何解决以上问题呢?引入应用消息队列后的方案,如下图:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
流量削锋
流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
- 可以控制活动的人数;
- 可以缓解短时间内高流量压垮应用
消息通讯
消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。 点对点通讯
点对点通讯:
客户端A和客户端B使用同一队列,进行消息通讯。
聊天室通讯:
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。 以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图
以上就是就是对于队列的理解和应用。如果你想了解更多。你可以参考进入以下网址。了解更多关于队列的应用www.365jz.com/article/239…上面小侠的说明就是引用了这部分文章的。
----------------------------------------------------------------------------------------------------------
实战部分
本次我们基于演示的是邮件队列如何操作。
本次实现队列的方式我们采用了mysql。如果你想了解更多或者配置其他方式的队列记得好好查看laravel的官方手册。
第一步:修改.evn文件中的队列方式。
QUEUE_DRIVER=database复制代码
第二步:要使用 database
队列驱动,你需要数据表保存任务信息。要生成创建这些表的迁移,可以运行 Artisan 命令 queue:table
,迁移被创建之后,可以使用 migrate
命令生成这些表:
php artisan queue:table
php artisan migrate复制代码
第三步:为对应的队列生成一个队列的操作类
php artisan make:job SendPostEmail
#以上命令我们就可以在laravel里生成一个任务操作类,其代码位置在:app/Jobs复制代码
第四步:我们需要构建一个测试发送邮件的方法
<?php
namespace App\Http\Controllers\Queue;
use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Mail;
//引入需要使用的队列
use App\Jobs\SendPostEmail;
class IndexController extends Controller
{
public function sendemail(){
//Post表是本次发送内容的信息表
$post = new Post();
$post->title = "队列测试邮件";
$post->body = "这是测试邮件";
$post->save();
//此操作是说明信息内容写入成功后,我们把写入的内容进行入队列的操作
$this->dispatch(new SendPostEmail($post)); // 队列
}
}
复制代码
第五步:编写的队列操作类
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Mail;
use App\Post;
class SendPostEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $post;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Post $post)
{
$this->post = $post;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
$data= array(
'title'=> $this->post->title,
'body'=> $this->post->body,
);
Mail::send('test', $data, function($message){
$to = 'fivetong@163.com';
$message->to($to)->subject('测试邮件');
});
}
}
#从上面可以看到,构造函数中我们直接存入了对应的POST实例复制代码
第六步:通过命令执行开启队列应用
php artisan queue:work
#注意了,队列启动后如果有任何的数据修改记得重启队列。
php artisan queue:restart复制代码