一、消息通知

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

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高级进阶(二)的更多相关文章

  1. Redis高级进阶(一)

    一.redis中的事务 在关系型数据库中事务是必不可少的一个核心功能,生活中也是处处可见,比如我们去银行转账,首先需要将A账户的钱划走,然后存到B账户上,这两个步骤必须在同一事务中,要么都执行,要么都 ...

  2. Redis高级进阶

    目录 本章目标 Redis配置文件 Redis存储 Redis事务 Redis发布订阅 Redis安全 本章目标 Redis配置文件 Redis的存储 Redis的事务 Redis发布订阅 Redis ...

  3. Python高级进阶(二)Python框架之Django写图书管理系统(LMS)

    正式写项目准备前的工作 Django是一个Web框架,我们使用它就是因为它能够把前后端解耦合而且能够与数据库建立ORM,这样,一个Python开发工程师只需要干自己开发的事情就可以了,而在使用之前就我 ...

  4. Redis学习第八课:Redis高级实用特性(二)

    Redis高级实用特性 4.持久化机制 Redis是一个支持持久化的内存数据库,也就是说Redis需要经常将内存中的数据同步到硬盘来保证持久化.Redis支持两种持久化方式:(1).snapshott ...

  5. 【Redis】二、Redis高级特性

    (三) Redis高级特性   前面我们介绍了Redis的五种基本的数据类型,灵活运用这五种数据类型是使用Redis的基础,除此之外,Redis还有一些特性,掌握这些特性能对Redis有进一步的了解, ...

  6. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  7. NoSQL之Redis高级实用命令详解--安全和主从复制

    Android IOS JavaScript HTML5 CSS jQuery Python PHP NodeJS Java Spring MySQL MongoDB Redis NOSQL Vim ...

  8. MEF高级进阶

    MEF高级进阶   好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四 ...

  9. .Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?

    本文将通过场景例子演示,来通俗易懂的讲解在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码. 通过一系列优化最终达到两个效果,1.通过代码块来控制事务(分布式事务),2.通过委托优化Tran ...

随机推荐

  1. apktool反编译时各种问题汇总

    问题1:apktool d -d 时出现错误Error occured while disassembling class办法:这不是你的错误,这是apktool本身的错误,目前正式release的1 ...

  2. 阿里巴巴 DevOps 转型后的运维平台建设

    原文:http://www.sohu.com/a/156724220_262549 本文转载自公众号「DevOps 时代」,高效运维社区致力于陪伴您的职业生涯,与您一起愉快的成长. 作者简介: 陈喻( ...

  3. mysql用merge合并表

    merge合并表的要求 1.合并的表使用的必须是MyISAM引擎 2.表的结构必须一致,包括索引.字段类型.引擎和字符集 实例: create table if not exists user1( i ...

  4. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  5. Android 动画分析学习笔记

    一:分类: Android动画分三种:view动画(对场景中的对象不断做图像变换<平移,缩放,旋转,透明度>).帧动画(顺序播放一系列图像产生动画效果).属性动画(动态改变对象属性). 二 ...

  6. sublime livereload插件

    1.首先在chrome商店下载livereload 安装之后记得在 chrome 的 扩展程序 里面 勾上 允许访问文件地址 2.sublime text 3 中下载插件 Livereload 3.配 ...

  7. Windows下静态库、动态库的创建和调用过程

    静态库和动态库的使用包括两个方面,1是使用已有的库(调用过程),2是编写一个库供别人使用(创建过程).这里不讲述过多的原理,只说明如何编写,以及不正确编写时会遇见的问题. //注:本文先从简单到复杂, ...

  8. mysql命令行导入和导出数据

    首先打开命令窗口,输入命令:mysql -h localhost -u selffabu -p 连接成功后,进行下面的操作 MySQL中导出CSV格式数据的SQL语句样本如下: select * fr ...

  9. html中keydown事件

    实现在输入框按回车按钮进行查询的功能: 1.<input type="text" id="inputChannel" onkeydown="ke ...

  10. [译]GLUT教程 - 键盘高级特性

    Lighthouse3d.com >> GLUT Tutorial >> Input >> Advanced Keyboard 本节我们会介绍另外4个处理键盘事件的 ...