• Redis高级进阶(二)


    一、消息通知

    在一些网站上,经常会有一些发布/订阅或者邮件订阅的功能,尤其一些博客上。其实这种问题很常见,当页面需要进行如发送邮件、复杂的计算时会阻塞页面的渲染。为了避免用户等待太久,应该使用其他进程单独完成此类操作,这里邮件订阅可以用任务队列来实现,具体来说,当需要发送邮件时,将其存入队列中,另外一个进程监视该队列,一旦发现就读取信息进行发送邮件。

    1、使用redis实现任务队列

    在redis中我们很容易想到使用列表来实现队列是最好不过的了,这时生产者通过lpush往列表中添加邮件信息,另外消费者通过rpop进行读取邮件信息进而发送邮件。

    实现的伪代码如下:

    #无限循环
    loop
       $task = rpop queue
       if $task
          execute($task)
       else
           wait 1 second

    以上就简单的实现了一个任务队列,这里有点不足的地方就是:如果任务列表中没有通知任务,这时还是通过每秒执行rpop进行检查,如果能实现一旦有新任务就通知消费者来读取就最好不过了,BRPOP命令就可以很好的实现该需求,brpop和rpop命令类似,唯一区别在于brpop会在列表中没有元素时一直阻塞连接,直到有新元素加入,以上的代码可以修改为:

    loop
        $task = brpop queue,0
        execute($task)

    brpop语法:brpop Key[key...] timeout

    接受两个参数,第一个是key,可以有多个。第二个参数是超时(秒),超过这个时间后会返回nil。当设置为0表示没有时间限制,如果没有新元素加入就一直阻塞。

    为了测试brpop命令,我们打开两个session:

    session A:

    127.0.0.1:6379> brpop queue 0   #一直监视queue内的元素情况,一旦session B中加入一个元素后立马输出下面的信息
    1) "queue"
    2) "10"
    (27.40s)

    session B:

    127.0.0.1:6379> lpush queue 10
    (integer) 1

    这时再查看queue列表中的情况:

    127.0.0.1:6379> lrange queue 0 -1    #已经被取走
    (empty list or set)

    2、优先级队列

    假设某个博客有10000个邮件订阅者,那么当发布一篇新文章需要向任务队列中添加10000个任务,如果发一个邮件需要10秒,全部完成这些任务需要30个小时。问题来了,如果这时有个新的订阅者,需要发送确认邮件,它根本就不知道前面排了10000个任务呢,那么他不得不等30个小时完成确认,多么糟糕的用户体验!而另一方面发送文章通知邮件并不是紧急的,有时晚一天也可以接受的,所以可以得出结论,当二者同时出现时,应该优先执行确认邮件的任务,为了实现这个需求,我们必须完成一个优先级队列。

    幸福的是BRPOP命令是可以实现的,由于BRPOP可以接受多个key,如brpop queue1 queue2 0,意思是同时监控多个key,一旦有哪个键有新元素加入就弹出,如果多个键都有新元素加入,那么会按照从左到右的顺序取第一个键中的元素。下面进行测试:

    复制代码
    127.0.0.1:6379> lpush queue1 10
    (integer) 1
    127.0.0.1:6379> lpush queue2 20
    (integer) 1
    127.0.0.1:6379> lpush quequ3 30
    (integer) 1
    127.0.0.1:6379> lpush queue1 11
    (integer) 2
    127.0.0.1:6379> lpush queue1 12
    (integer) 3
    127.0.0.1:6379> brpop queue1 queue2 queue3 0
    1) "queue1"
    2) "10"
    127.0.0.1:6379> brpop queue1 queue2 queue3 0
    1) "queue1"
    2) "11"
    127.0.0.1:6379> brpop queue1 queue2 queue3 0   #到这里完全是按从左到右的顺序,将第一个key中元素全部取完才轮到下一个key
    1) "queue1"
    2) "12"
    127.0.0.1:6379> brpop queue1 queue2 queue3 0
    1) "que
    复制代码

    通过以上的特性,我们可以创建两个队列:分别是queue.confirm.email和queue.notify.email,下面是伪代码:

    loop  
       $task = brpop queue.confirm.email queue.notify.email 0
       execute($task[1])

    3、发布/订阅模式

    除了实现队列外,redis还提供一组命令可以让开发者实现发布/订阅模式。发布/订阅模式同样可以实现进程间信息通信。它的原理是这样的:

    发布/订阅包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或若干个频道,而发布者可以针对频道进行发送消息。

    发布者发布消息的命令是:publish channel message  返回值是订阅者的数量。

    订阅者订阅的命令是:subscribe channel [channel...]

    下面打开两个session进行测试:

    session A:订阅频道1.1

    复制代码
    127.0.0.1:6379> subscribe channel1.1
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "channel1.1"
    3) (integer) 1
    1) "message"
    2) "channel1.1"
    3) "helloworld"
    1) "message"
    2) "channel1.1"
    3) "darren"
    复制代码

    session B:发布者

    127.0.0.1:6379> publish channel1.1 helloworld
    (integer) 1
    127.0.0.1:6379> publish channel1.1 darren
    (integer) 1

    4、管道

    客户端和redis server使用TCP协议连接。不论是客户端发送命令到redis还是redis返回结果给客户端,都需要经过网络传输,这两部分总消耗称为往返时延。当执行命令很多时,各个执行的往返时延加起来还是对性能有一定影响的。因为在执行多条命令时,每条命令都要等到上一条命令执行完成并返回结果才能执行,所以redis提供管道功能,可以一次性

    发送多个命令,而且等都执行完成后一次性返回结果,这样就减少了每条命令都需要的往返时延了,可以节省大量的连接时间。

    5、节省空间

    redis是一个内存数据库,所有的数据都存储在内存中,所以如何优化存储,减少内存空间的占用对成本控制来说是一个重要的话题。

    1)精简键名和键值

    精简键名和键值是最直观的减少内存占用的方式。当然精简键名也要把握好一个度,不能为了减少内存占用而使用一些不易理解的键名,这样既不易维护也容易造成键名重复。再比如存储性别的male和female,我们可以用m和f表示,当然也可以用0和1表示性别。

    2)内部编码优化

  • 相关阅读:
    Redis学习之(一)
    SpringMVC之学习(0)
    Node.js之Express三
    Node.js之Express二
    Node.js之Express一
    Node.js进程管理之进程集群
    Node.js进程管理之子进程
    Node.js其他模块
    Node.js进程管理之Process模块
    Node.js HTTP Server对象及GET、POST请求
  • 原文地址:https://www.cnblogs.com/henrylinux/p/9926382.html
Copyright © 2020-2023  润新知