工厂模式在实际中的应用

前言:为什么要使用设计模式?
设计模式并不是生来就有的,也是前人大量编码,最后总结出来的一套代码结构设计经验,根本目的也是为了增加代码的复用性和可维护性。
顺便说一下设计模式很看重一个原则->开闭原则,会在下面的示例中得到体现!简单的来说就是说对扩展开放,对修改关闭。

工厂模式
工厂模式是一种实例化对象的模式,是用工厂方法代替new操作的一种方式
工厂模式根据抽象的程度,可以分为简单工厂模式,工厂方法模式,抽象工厂模式。
Ps:对于一些功能相似的类,我们通常会提供一个抽象接口,把公共方法暴露出来,供调用者实现具体的逻辑。
示例讲解

以保险为例

1.简单工厂模式
由一个工厂类根据传入的参数决定创建哪一种产品的类,其实属于一种比较自然的再封装,我们通常也会在代码设计中不经意的使用

interface Create_product {
    public function getProducts();
}
//xyz的产品类
class Product implements Create_product
{
    public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
//anlian的产品类
class Product implements Creat_product
{
     public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
class Simple_facrtory {
    public static function createProduct($supp_name)
    {
        if ($supp_name=='Xyz') {
            return new  Xyz\Product;
        }
        if ($supp_name=='Anlian') {
            return new Anlian\Product;
        }
    }
}
$product = Simple_factory::createProduct('Xyz');
$product->getProducts();

优点:客户端免除了具体产品对象的创建,统一使用工厂去创建对象
缺点:违背了开闭原则,当需要有新的产品的时候,会在工厂类的基础上修改,很容易引发错误。

2.工厂方法模式
对简单工厂模式进行了抽象,有一个简单抽象工厂类负责制定以下规范,一般在这种模式中,工厂类会与某个具体的产品类一一对应。

//抽象产品类的方法
interface Create_product {
    public function getProducts();
}
//xyz的产品类
class Product implements Create_product
{
    public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
//anlian的产品类
class Product implements Creat_product
{
     public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
//将对象的创建抽象成一个接口
interface Create{
    public function product();
}
class FactoryXyz implements Create{
    public function product()
    {
        return new Xyz\Product;
    }
}
class FactoryAnlian implements Create{
    public function product()
    {
        return new Anlian\Product
    }
}
$factory = new FactoryAnlian();
$product = $factory->product();

优点:实现了开闭原则,对于新增的产品,只需要新增对应的产品类和工厂类,不需要对已有代码的修改,降低修改的风险
缺点:对于不同种类的产品,表示无能为力

3.抽象工厂模式
先举个例子,通过上面2个示例,我们制造这个产品了,但是我们有产品后,又需要去搞订单了,是不是又要创建另外的一个工厂类专门来处理订单呢?显然是不合适的,这时候,我们就会想,是不是可以把相关联的产品(产品和订单)放到一个工厂里,也就没必要再新开一个工厂了!

interface Create{
    public function product();
    public function order();
}
class FactoryXyz implements Create{
    public function product()
    {
        return new Xyz\Product;
    }
    public function order()
    {
        return new Xyz\Order;
    }
}
class FactoryAnlian implements Create{
    public function product()
    {
        return new Anlian\Product;
    }
    public function order()
    {
        return new Anlian\Order;
    }
}
$factory = new FactoryXyz();
$product = $factory->product();

优点:抽象工厂模式,核心在于'一系列', 单个工厂可以生产多个相关的产品。便于在业务中切换工厂。
缺点:显而易见,当出现新的产品的时候,改动开销就很大了,需要在每个工厂类中都加入对应的产品。比如说新增了一个用户,那么就需要在这每个工厂里都加一个用户的实现了,也就是无法支持新增产品,但可以支持多个不同的工厂!

总结:这三种模式并不是说哪个一定好,具体的使用要根据对应的环境来决定的!

CodeIgniter数据库操作


非原创,来源:https://pjf.name/post-383.html

  1. 查询数据
    $query = $this->db->query('select * from xxx');
    /**

    • 获取多条数据
      */
      $res = $query->result();//返回一个对象数组(多维)OR空数组,同$query->result_object();
      $res = $query->result_array();//返回一个关联数组(多维)OR空数组
      /**
    • 获取某条数据
      */
      $res = $query->row();//返回第一行数据(对象方式)
      $res = $query->row(n);//返回第n行数据,如果n大于数据的条数,则返回第一条的数据
      $res = $query->row_array();//同$query->row(),只是返回的数据为关联数组
      $res = $query->row_array(n);//同$query->row_array(),返回的是关联数组
      $res = $query->first_row();//第一条记录
      $res = $query->first_row('array');//返回第一条记录(用数组形式)
      $res = $query->last_row();//最后一条记录
      $res = $query->last_row('array');//最后一条记录(用数组形式)
      $res = $query->next_row();//下一条记录
      $res = $query->next_row(‘array');//下一条记录(用数组形式)
      $res = $query->previous_row();//上一条记录
      $res = $query->previous_row('array');//上一条记录(用数组形式)
  2. 辅助函数
    【查询】
    $query->num_rows(); //查询结果集的行数
    $query->num_fields(); //返回当前请求的字段数目(即列数)
    $query->free_result(); //释放结果集
    【其它】
    $this->db->insert_id();//执行数据插入时的ID
    $this->db->affected_rows();//影响的行数
    $this->db->count_all('table_name');//返回某个表的总行书
    $this->db->platform();//数据库平台(mysql,MS SQL...)
    $this->db->version();//数据库版本
    $this->db->last_query();//最近一次查询
    $this->db->insert_string('table_name' , $data); //生成标准的insert语句
    $this->db->update_string('table_name' , $data); //生成标准的update语句

  3. 原文更正:
    where条件里写大于、小于、不等等符号的时候,要留空格。
    误:$this->db->where('start_time!=','2013');
    正:$this->db->where('start_time !=','2013');

CI框架3.x部分新增特性

  1. CI框架从3.0开始,输入类Input开始支持用数组的方式去取变量值。例如:
    以前(2.x):
    $user_id = $this->input->post('user_id', true);
    $nickname = $this->input->post('nickname', true);
    $version = $this->input->post('version', true);
    ...
    $address = $this->input->post('address', true);
    改进(3.x):
    $keys = array('user_id', 'nickname', 'version');
    list($user_id, $nickname, $version, $address) = $this->input->post($keys, true);

  2. 使用 input_stream() 来获取php://input流数据,例如 PUT 请求
    以前:
    $rsv = file_get_contents("php://input");
    $data = json_decode($rsv,1);
    $user_id = intval($data['user_id']);
    $nickname = stripSql($data['nickname']);
    $version = stripSql($data['version']);
    改进:
    $keys = array('user_id', 'nickname', 'version');
    list($user_id, $nickname, $version, $address) = $this->input->input_stream($keys, true);

  3. 在http和https的兼容上面,$this->config->base_url() 添加了第二个参数 $protocol,可以传入http/https。如果传入【""】,会得到//开头的链接,表示自适应。
    示例:
    $CI =& get_instance();
    $url = $CI->config->base_url("yilucaifu/buy/1024", "");
    /**
    * window.location="//shop.ci123.com/flashsale/yilucaifu/buy/1024";
    */

  4. get(), post(), get_post(), cookie(), server(), user_agent()在没有取到数据的时候,返回NULL而非FALSE。
    意义:
    在以前(2.x),$p = $this->input->get('p', true); isset($p) 始终为真;到3.x后,如果没有p参数,isset($p)为假。

  5. BASEPATH, APPPATH 被定义为了绝对路径,在脚本里可以直接使用这些变量了。

  6. CI 3.x原生支持composer。新建composer.json,填写内容后update就可以使用了。

Js 中的数组和对象

示例一:
var x = [1,2];
x.k = 'v';
console.log(x);
console.log(x.length);
结果是:
[1, 2, k: "v"]
2

示例二:
var x = [1, 2];
x.get = function(){
alert('function get');
}
console.log(x);
console.log(x.length);
结果是:
[1,2] --注释1
2
注释1:展开后是:
0:1
1:2
get:function()

示例三:
var x = new Object();
x[0] = 1;
x[1] = 2;
x.get = function(){
alert('function get');
}
console.log(x);
console.log(x.length);
结果是:
Object {0: 1, 1: 2} --注释2
undefined
注释2:展开后是:
0:1
1:2
get:function()

示例四:
var m = [];
var n = new Object();
console.log(m instanceof Array, m instanceof Object);
console.log(n instanceof Array, n instanceof Object);
结果是:
true true
false true

【结论1】:从上面可以看出来,JS中的数组仅仅是一种特殊的对象,对象和数组都可以具有属性和方法。数组具有 length 属性,而对象没有。当数组具有属性或方法时,属性和方法不会让数组的 length 增加。

示例五:
var p = [];
console.log(p.length);
p[100] = 1; --注释3
console.log(p.length);
结果是:
0
101
注释3:或者p['100']

示例六:
var p = [1,2,3,4,5]
console.log(p, p.length);
p.length = 3;
console.log(p, p.length);
p.length = 5;
console.log(p, p.length);

结果是:
[1, 2, 3, 4, 5] 5
[1, 2, 3] 3
[1, 2, 3] 5

【结论2】:当值被分配给大于数组本身长度的索引时,length 会自动增加到新的长度;当 length 变小时,索引超过数组长度的元素将被删除。(恢复length也无法恢复数组元素)

Js小知识点——setTimeout

一:下面看一个腾讯面试题,主要知识是Js的线程问题。(有删减)
var cnt = 0;
for(var i = 0; i < 10; ++i) {
setTimeout(function() {
cnt += i;
}, 0);
}
setTimeout(function(){
console.log(cnt);
}, 0);

由于Js是单线程,遇到循环的时候setTimeout会在循环结束后才执行,所以cnt所加的i总是10,最后cnt的值是100。

var cnt = 0;
for(var i = 0; i < 10; ++i) {
    setTimeout(function() {
        cnt += i;
    }, 0);
}
console.log(cnt);

稍作改动,代码的运行结果是0,很容易理解。

二:向 setTimeout 传递字符串字面量

var s = 1;
function test()
{
    var s = 2;
    setTimeout('console.log(s)', 0);
}
function test2()
{
    var s = 2;
    setTimeout(function(){
        console.log(s);
    }, 0);
}
test();
test2();

传入字符串字面量时,字面量会在全局作用域内被解析,所以上面输出 1 和 2 ;

系统中接口调用的单例化(third_party中的一路财富接口)

场景介绍:
1、以前每次调用接口时,都需要new一个接口对象,再调用接口,这样不仅浪费资源,代码也比较繁琐;
2、理想的调用方法是:接口全静态。这样不仅运行效率高,而且调用方便;
思路整理:
1、首先静态方法无法调用非静态方法,如果把接口静态化,调用日志记录接口时会出现问题(CiLogs);
2、基于第一条,接口全静态的想法不可取。转而求其次,接口全部单例化,用静态类来调用;这样既可以记录日志,调用也非常方便。
改造:
1、首先看third_party代码结构(特指ylcf, xinyi类似):
com ----存放工具类,比如加密、字符串操作函数
entity ----未使用,可以删除
service ----存放了所有接口,按文件分类
config.php ----配置文件,主要是接口的转换
demo.php ----接口测试文件
ylcfClass.php ----引入service并实例化,对外提供统一的接口访问

PS:config.php担负接口管理的职责,但是在service中仍然出现了具体的接口,config没发挥好管理职责。
    更好的做法是,config.php中用键值对(可以是属性名-值,也可以是数组键名-值等等,属性名和键名都算“键”)保存接口,service中只出现config中提供的“键”,不接触接口。

2、把ylcfClass.php所有方法都静态化,用静态属性保存service的实例,保证每个service实例的单一性;

3、把com工具类静态化;

4、以前在 controller 或 model 里面调用接口是这样的:
        include_once("xxxx/ylcfClass.php");
        class A
        {
            ......
            $yc = new YlcfClass();
            $yc->xxx()->yyy();
            ......
        }
    为了美观,我们先创建一个 library: YlcfApiTool.php, 里面使用静态方法,调用 YlcfClass。
    然后在 controller 或 model 里这样调用:
        class A
        {
            public __construst()
            {
                $this->load->library('YlcfApiTool');
            }
            ......
            YlcfApiTool::xxx()->yyy();
            ......
        }
5、日志处理:
    整个过程没有修改日志处理流程,所以不受影响。

总结:
1、静态化可以提高代码效率,特别是在非脚本语言里;
2、用 library 包装了一遍接口,好处是可以用 load library 优雅地引入接口,调用也无需创建实例。

软件测试-大家都知道的

  1. 为什么要做测试(必答题)
    测试不能提高代码质量;
    自己写的代码自己一般检查不出来BUG;
    流程中复杂的数据结构,第三方模块和方法,内部细节不够详细(类似于黑盒),必须要测试才能发现潜在的bug;
    bug可能因为两块正常的代码耦合在一起而产生,所以通畅地测试整个流程很有必要;

  2. web开发的测试包括哪些
    宏观的测试内容包括:
    用户感受:页面样式正确、文案通顺、交互正常、流程逻辑合理;
    运营需求:流程(样式、文案、引导等等)和目的一致,数据能随时统计;
    开发人员:代码重用率、需求扩展是否容易、文档是否齐全;

  3. 测试需要遵循哪些规则

  4. 有哪些常规的测试方法
    代码评审
    黑盒测试

  5. 有哪些测试工具,自动化测试工具
    Chrome DevTools
    postman(模拟请求)
    fiddler(抓包)
    Selenium(模拟访问 & 结果存储分析)

  6. 如何编写测试文档(范例)
    用例:
    描述:
    前置条件:
    后置条件:
    预期结果:
    是否通过:
    附链接参考:http://www.ibm.com/support/knowledgecenter/zh/SSYMRC_4.0.0/com.ibm.rational.test.qm.doc/topics/t_testcase_template_ref.html

  7. 在以往项目开发过程中,测试的时候遇到的问题(必答题)
    调用api错误(调了别人的分支、调用了测试分支);
    数据解析错误(传参不一样数据结构不一样的接口);
    sql错误:表缺少字段,或者存储了一个null到非null字段;

spl_autoload_register 入门级讲解

标签(空格分隔): PHP自动加载


__autoload()

了解 spl_autoload_register() 前先了解一下 __autoload(),或者直接 跳过此节

function __autoload($c)
{
    var_dump($c);
}
new Say();
//输出
string(3) "Say"
Fatal error: Class 'Say' not found in E:\Dropbox\liubole\htdocs\test\index.php on line xx

当Say类不存在的时候,自动执行了 __autoload() 函数,并将类名当做参数传入了 __autoload()。

如果在 __autoload() 里面 include 一下包含 Say 类的文件,可以“亡羊补牢”,代码继续正常执行:

function __autoload($c)
{
    echo "not fund class \"{$c}\", auto loading...\n";

    //libs下的say.php,大小写无影响
    include_once __DIR__ . "/libs/{$c}.php";
}

$say = new Say();
$say->hello();

包含 Say 类(libs/say.php)的文件内容:

class Say
{
    function __construct()
    {
        echo "init class \"Say\"...\n";
    }

    function hello()
    {
        echo "say: hello~\n";
    }
}

再次执行,结果如下:

not fund class "Say", auto loading...
init class "Say"...
say: hello~

至此我们大概了解了 __autoload() 的实际执行情况。


spl_autoload_register()

PS:前缀 SPL 的全称是 Standard PHP Library。

当PHP找不到类文件会调用 __autoload() 方法,当注册了自己的函数或方法时,PHP不会调用 __autoload() 函数,而会调用自定义的函数。

如果在你的程序中已经实现了__autoload函数,它必须显式注册到__autoload栈中。因为spl_autoload_register()函数会将Zend Engine中的__autoload函数取代为spl_autoload()或spl_autoload_call()。

上面的话来自 php.net。OK,那怎样算 显式注册到__autoload栈中 呢?

◔ ‸◔ ... ◔ ‸◔ ..... ◔ ‸◔ ...... ◔ ‸◔ ........◔ ‸◔

答案是:spl_autoload_register("__autoload") 。以下是我找来的一点摘要,描述了为何需要这样做:

  • autoload机制的主要执行过程为:
    (1) 检查执行器全局变量函数指针autoload_func是否为NULL。
    (2) 如果autoload_func==NULL,则查找系统中是否定义有__autoload()函数,如果没有,则报告错误并退出。
    (3) 如果定义了__autoload()函数,则执行__autoload()尝试加载类,并返回加载结果。
    (4) 如果autoload_func不为NULL,则直接执行autoload_func指针指向的函数用来加载类。注意此时并不检查__autoload()函数是否定义。

  • 如果既实现了__autoload()函数,又实现了autoload_func(将autoload_func指向某一PHP函数),那么只执行autoload_func函数。

  • SPL autoload机制的实现是通过将函数指针autoload_func指向自己实现的具有自动装载功能的函数来实现的。SPL有两个不同的函数spl_autoload, spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的自动加载机制。spl_autoload的功能比较简单,而且它是在SPL扩展中实现的,我们无法扩充它的功能。所以我们需要spl_autoload_register将autoload_func指向spl_autoload_call的这个功能。

  • 在SPL模块内部,有一个全局变量autoload_functions,它本质上是一个HashTable,不过我们可以将其简单的看作一个链表,链表中的每一个元素都是一个函数指针,指向一个具有自动加载类功能的函数。spl_autoload_call按顺序执行这个链表中每个函数,在每个函数执行完成后都判断一次需要的类是否已经加载,如果加载成功就直接返回,不再继续执行链表中的其它函数。

  • 自动加载函数链表autoload_functions由spl_autoload_register函数维护,它可以将用户定义的自动加载函数注册到这个链表中,并将autoload_func函数指针指向spl_autoload_call函数。

欲知详情请移步 __autoload机制详解以及与spl_autoload_register的区别, 文章把原因讲的很详细,时间匆忙我仅囫囵吞枣瞄了眼。


本文涉及资料:
PHP: spl_autoload_register - Manual
__autoload机制详解以及与spl_autoload_register的区别


判断点是否在矩形内的算法


声明:想获取更多更详细的知识请移步:计算几何算法概览

需求描述:
有一个 4*4 的方格,用户可以选择起点和落点形成矩形,每次形成的矩形不能重叠,起点可以和落点坐标一样。现已知起点、未选点集合和已选点集合,求落点的集合。

算法重点:转换为判断点P是否在一个矩形内。


方法一:建立平面直角坐标系,根据横坐标和纵坐标判断点是否在矩形内。


/** * 判断点是否在矩形内(含边)。p1、p2是矩形的两个对角点。 * 该算法和平面直角坐标系怎么建立无关 * @param $rc * @param $p * @return bool */ function isInRect($rc, $p) { $xi = ($p->x - $rc->p1->x) * ($p->x - $rc->p2->x); $yi = ($p->y - $rc->p1->y) * ($p->y - $rc->p2->y); return ($xi <= 0) && ($yi <= 0); }

方法二:假设矩形左、右、上、下分别在直线 L1、L2、L3、L4 上,只需要证明点 P 同时在上下、左右直线之间即可。

过P作一条直线相交于 L1、L2 的 C1 、C2 ,向量PC1 、PC2 同向则P在 L1、L2 之外,反向则在 L1、L2 之内。同理可判断P是否在 L3、L4 之间。

$$ PC_1 = (C_1x - P_x , C_1y - P_y); PC_2 = (C_2x - P_x , C_2y - P_y);$$
$$ \frac{C_2x - P_x}{C_1x - P_x } > 0 或 \frac{C_2y - P_y}{C_1y - P_y } > 0 => PC_1、PC_2 同向 $$
$$(C_1x-P_x)^2 + (C_1y-P_y)^2 = 0 => PC_1为零向量 => PC_1、PC_2 同向$$
$$(C_2x-P_x)^2 + (C_2y-P_y)^2 = 0 => PC_2为零向量 => PC_1、PC_2 同向$$

如果:
$$ L_1: y = mx + t_1; $$
$$ L_2: y = mx + t_2; $$
$$ C_1C_2: y = (m+1)x; $$
$$ P_1:(P_1x, P_1y); P_2:(P_3x, P_3y) 是L_1上的两个矩形顶点;$$
$$ P_3:(P_3x, P_3y); P_4:(P_4x, P_4y) 是L_2上的两个矩形顶点;$$

则:

$$ 若点P和{P_1,P_2,P_3,P_4}中的某一点重合,则P在矩形内; $$

$$ 若 (t_2 - P_x)*(t_1 - P_x)>0, t_1=\frac{P_1yP_2x-P_1xP_2y}{P_2x-P_1x},t_2=\frac{P_3yP_4x-P_3xP_4y}{P_4x-P_3x} 则P在矩形内;$$

$$ 若 (t_2 - P_x)*(t_1 - P_x)>0, t_1=\frac{P_1yP_2x-P_1xP_2y}{P_2x-P_1x},t_2=\frac{P_3yP_4x-P_3xP_4y}{P_4x-P_3x} 则P在矩形内;$$

/**
 * 此处省略n行代码
 */

方法三:以点P为起点,向左作射线,如果射线和矩形的焦点个数是奇数,则P在矩形内。

PS:对以下特例不做考虑:1、对于矩形的水平边不作考虑;2、对于矩形的顶点和L相交的情况。

/**
 * 此处省略m行代码
 */

总结:方法一简单实用,方法三可以推广到判断点是否在任意多边形内。


RabbitMQ在项目中的使用方式+简略介绍

声明:文章写于2016-11-10,项目中RabbitMQ的使用方式在后面可能会发生变化。

消息队列(MessageQueue)的好处:

  • 简化业务流程
  • 异步处理消息
  • 减少组件耦合
  • 消减数据峰值

消息队列的应用场景:

  • 场景1:
    支付成功以后,给用户发送优惠券or红包;
  • 场景2:
    用户注册成功后发送通知邮件or短信;

  • 场景3:
    活动里面的站内信发送;
    . . . . . .
    总结起来就是,把单个业务简洁化,通过消息系统把不相关的业务连接起来,实现更复杂的业务链,降低整个系统的复杂度。

安装

安装方法自行google,如果要在生产环境中安装请先联系运维,涉及到安全问题。

项目中使用

//发布消息
use CI123\Shop\Tool\RabbitMq\Publisher;
Publisher::publish('routing_key', $message);

//监听消息
use Api\Tool\RabbitMq\Subscriber;
Subscriber::subscribe('routing_key', function ($msgObj)    {
            $message = $msgObj->body;
            Subscriber::ack($msgObj);
});

以上是对RabbitMQ极其简陋的介绍,下面是重点:

一、我们项目中怎么设置RabbitMQ的:

1. 持久化

真正的持久保存消息,需要设置三个地方:
1.1 将交换机设置成durable;
1.2 将队列设置成durable;
1.3 将消息的delivery_mode设置成2;

PS:
缺少任何一个设置,都无法真正持久化;
持久化会消耗额外的性能;

2. 自动删除

2.1 消息在队列里存在时间是5分钟;
2.2 队列本身存在时间是30分钟;
2.3 过期的消息会被移出到“死信队列”;
2.4 死信队列里的消息存在7天;
2.5 死信队列本身存在10天;

3. 消费者应答机制(ack/nack机制)

3.1 如果消息已经被正确消费了,你可以通过ack方法告诉rabbitmq,rabbitmq会删除这条消息;
3.2 反之,你可以调用nack告诉RabbitMQ消费失败,rabbitmq会立马将消息移到“死信队列”;
3.3 或者什么都不做,等待消息过期后被移动到“死信队列”。

4. 生产者确认机制(Confirm机制)

4.1 发布消息后,发送者可以通过异步的回调知道消息是否发送成功;

PS:目前项目中基本没有使用。

二、初步理解RabbitMQ

1. 四个比较重要的概念

  • 虚拟主机(vhost):每个vhost都有单独一套交换器、队列、绑定规则
  • 交换器(exchange):将消息发送到队列
    常见相关名词:
    + 名称(exchange):交换器名字,不能重复
    + 类型(type):fanout/direct/topic/headers
    + 持久化(durable):服务重启后是否要恢复
    + 自动删除(auto-delete):没有队列使用交换器时自动删除
  • 队列(queue):存取消息
    常见相关名词:
    + 名称(queue):队列名字,不能重复
    + 持久化(durable):服务重启后是否要恢复
    + 自动删除(auto-delete):没有消费者时自动删除
    + 声明(arguments): 消息存在时间(x-message-ttl)、队列存在时间(x-expires)
  • 绑定(binding):将队列和交换机绑定,以通过routing_key来收发消息;

2. 四种Exchange

  • fanout :广播
  • direct:routing_key全部匹配
  • topic:可以理解为在direct的基础上给routing_key增加了正则匹配,当然肯定不是正则;
  • headers:消息里包含headers,如果消息的headers和队列能匹配上就将消息投递到该headers,可以指定是完全匹配(all)还是只需要匹配单个(one);

3. 其他概念和结论

  • 消息的获取有主动拉取、被动接收,发布/订阅模式是被动接收;
  • 交换器、队列的属性一旦声明就不可更改,但是绑定是可以解绑的(unbind);
  • 当一个 message 被路由到多个 queue 中时,在某个 queue 中的某个 message 的“死亡”不会对相同 message 在其他 queue 中的生存状况;
  • 同时设置了队列生存时间T1和消息生存时间T2后,消息在队列中的存在时间是T1/T2之中的最小值;
  • 如果没有设置死信队列,即便你设置了durable,消息到期后也会被丢弃;

4.目前的使用状况

  • 按照普通的消息队列来使用:佣金、理财、保险;
  • 改造为延时队列:微信模板消息,通过延时队列来重发上次发送失败的消息;
  • 可以改造为任务队列使用:待定;