0%

最近在给自己的服务进行解耦,就需要用到消息队列,虽然Redis能够实现消息队列,但是对于实时性要求没有太高,于是就想到了各种MQ产品,目前市面上有很多,比如ActiveMQ,RabbitMQ,Kafka,ZeroMQ,RocketMQ等等。
对比了一下各家的MQ产品,最终看中了RabbitMQ。RabbitMQ是用Erlang作为开发语言进行开发的开源消息队列,所以在使用RabbitMQ之前,得先配好Erlang的环境,当然本身支持很多协议包括:AMQP,XMPP,SMTP等。

一、三个角色

对于MQ产品而言,一般包含三种角色:

  1. 生产者(Producer)
  2. 队列(queue)
  3. 消费者(consumer)

一个大致的流程就是生产者产生消息,消息包括两个部分:有效载荷(payload)标签(label),并将消息放入队列中,这个时候生产者的任务就完成了,队列服务器会根据标签信息发送给对应的消费者,等待消费者去消费就行了,当消费者消费消息的时候只会得到消息的有效载荷。
另外生产者、队列以及消费者可以不放在同一台机器上,在某种程度上也是一种
解耦

二、信道

在消费者或者生产者与RabbitMQ服务器之间除了创建了一条TCP连接,还需要在这条连接上建立一条虚拟的信道(channel),每一条信道都会有被指派一个唯一ID保存在服务器上,所有的发送、传送和接收消息都是在这条信道上完成的。
这样做的好处就是在消息量大的时候,相对于每一个线程创建一个TCP连接,信道的模式能够减少创建TCP连接带来的开销,保障了时效性。

三、消息确认

为了保障消息队列的可靠性,MQ产品设计了一个消息消费确认机制,当生产者将消息放在队列上等待消费者消费时,消费者每接收到一条消息,都必须进行确认,这里有两种情况,一种是手动确认,一种是自动确认。

  1. 手动确认是指当消费者接收到消息时,显示地向RabbitMQ服务发送一个确认,这样做的好处是,当应用程序有BUG忘记确认消息或者出现格式错误导致无法处理而崩溃时,RabbitMQ则不会继续发送更多的消息给消费者,防止消息的丢失
  2. 自动确认是指一旦消费者接收到消息之后,RabbitMQ服务器将会自动确认已经正确接收到消息了,这样做对于一些简单的消息来说,提高了效率。
  3. 当然如果程序出现问题时也可以主动拒收消息,这个时候消费者可以向RabbitMQ服务器发送一个reject命令,当服务器接收到后会将这条信息放入“死信”(dead letter)队列,这个队列专门存放那些被拒绝又不重新入队的消息,这样通过监控死信队列可以及时发现问题。

四、几种工作模式

1. 简单队列

一个生产者,一条队列,一个消费者。
简单队列

2. Work模式

两个消费者,一条消息只能被一个消费者消费,分摊消费压力,有点负载均衡的意思。
Work模式

3. 发布/订阅模式

可以有多个消费者,而每个消费者都有自己的队列,每个队列要绑定到交换器,发送者将消息交给交换器,交换器根据绑定时指定的binding key,将消息放进对应的队列
发布/订阅模式

交换器有4种类型:

  • direct
  • fanout
  • topic
  • headers

4. 路由模式

交换器为direct,交换器根据不同的路由键(routing key),放进不同的队列中,可以有个路由键对应同一条队列

路由模式

5. 主题模式

主题模式也就是通配符模式。同一个消息被多个消费者获取。一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费到消息。
主题模式

1. 简单工厂模式

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
41
42
43
44
45
46
<?php

interface Math
{
function calc($num1, $num2);
}

class Add implements Math
{
public function calc($num1, $num2)
{
// TODO: Implement calc() method.
return $num1 + $num2;
}
}

class Sub implements Math
{
public function calc($num1, $num2)
{
// TODO: Implement calc() method.
return $num1 - $num2;
}
}

class Cal
{
public static function getMath($type)
{
switch ($type) {
case "+" :
return new Add();
break;
case "-" :
return new Sub();
break;
default :
die("不支持其他计算方法");
}
}
}

$math = Cal::getMath("+");
var_dump($math->calc(1,2));
$math2 = Cal::getMath("-");
var_dump($math2->calc(3,2));

好处就是,只需要管理getMath里面的类型就行了,不需要管如何实现的,但是缺点就是,当需要实现的方法较多时就比较麻烦,每增加一种,就需要增加一个类型。

2. 工厂模式

定义工厂的接口,让工厂的子类来确定实例化哪一个具体的产品类,延迟了类的实例化。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
namespace FactoryMethod2;

//父类
abstract class Math
{
abstract public function create();
}
//操作类
abstract class Operation
{
protected $num1 = 0;

protected $num2 = 0;

public function setNum1($num1){
$this->num1 = $num1;
}

public function setNum2($num2){
$this->num2 = $num2;
}

abstract public function calc();
}

class Add extends Operation
{

public function calc(){
return $this->num1 + $this->num2;
}
}

class Sub extends Operation
{
public function calc(){
return $this->num1 - $this->num2;
}
}
//工厂类
class AddFactory extends Math
{
public function create()
{
// TODO: Implement create() method.
return new Add();
}
}
//工厂类
class SubFactory extends Math
{
public function create()
{
// TODO: Implement create() method.
return new Sub();
}
}

$factory1 = new AddFactory();
$math1 = $factory1->create();
$math1->setNum1(1);
$math1->setNum2(2);
var_dump($math1->calc());

$factory2 = new SubFactory();
$math2 = $factory2->create();
$math2->setNum1(2);
$math2->setNum2(1);
var_dump($math2->calc());
?>
Read more »

今天来学习一下几种简单常用的设计模式,更深的还看不懂,只能从简单的开始。

单例模式

单例模式不管是哪种语言,都是比较常用的设计模式,因为程序中经常会用到全局变量,如果全局变量没有得到好的保护,就会造成一些难以想象的后果。所以当我们在设计类的时候就要考虑到这一点,而我们要做的就是程序中同一个类的所有对象都使用的是同一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class Preference {
//创建私有静态变量来保存类对象
private static $instance;

//防止被创建对象
private function __construct(){}

//防止克隆对象
private function __clone(){}

//判断是否有创建对象
public static function getInstance(){
if (!self::$instance instanceof self) {
self::$instance = new Preference();
}
return self::$instance;
}
}