周煦辰 2016年8月31日

本文介绍了一下本人在开发过程中遇到“定时推送提醒”的需求的时候所思考的三种解决方案。

明确问题

首先明确一下这个需求可能包含的几个“坑”:

  1. 系统内的用户量是否很大?所涉及的提醒任务是否会很多?
  2. 该提醒是否是用户自己设置的?中途是否会修改?
  3. 推送的时间是否固定(如每天固定时间推送或者每隔一个小时推送等)?还是用户自定义推送时间?

所需工具

  • Redis
  • crontab
  • 任何一种Linux上可以运行的脚本语言(Python、PHP等)

解决方案一:使用Redis队列(普通入队出队)

针对第一个问题,我们可以将需要推送的任务作为一个消息队列,这样可以减轻数据库的压力。因此这就引出第一种解决思路:使用Redis的队列命令实现一个简单的消息队列。

基本思路为,在一天中的某个时间(例如早上五点这种服务器不会遇到什么压力的时间段),通过crontab运行脚本,将推送任务整理完成并逐条插入Redis的队列中。基本的代码思路如下:

import redis
import json # 这里是你的数据库查询代码
# TODO # 这里将你需要推送的用户ID、内容等整合为一个字典
reminder = {
'id': 27149,
'content': 'The meaning of life is 42'
}
# 将字典编码为json字符串
reminder_str = json.dumps(reminder) # 连接redis并将数据插入redis中
r = redis.StrictRedis(hostname='localhost', port=6379, db=0)
print r.lpush('test_list', reminder_str) # 如果所有数据已入队
# 可以在最后插入一个空数据作为结束的标志
r.lpush('test_list', '{}')

到需要推送消息的时间(例如早上十点),通过crontab运行如下的出队命令,进行消息的推送。

import redis
import json r = redis.StrictRedis(hostname='localhost', port=6379, db=0)
while True:
item = r.rpop('test_list')
data = json.loads(item)
if not data:
break
# 这里是自定义的推送代码

以上这种方法的优点在于:

  1. 可以利用Redis实现不同服务端应用间的数据,例如你的服务端应用是用PHP写的,而入队的脚本希望使用Python,则可以采用这种方法。
  2. 可以减轻对数据库的压力,且Redis的查询效率非常高,可以提升该功能上的性能。

不足之处:

  • 假如推送的消息是用户自定义的,且中间会有修改,那这种方案就会遇到推送时的消息错误。

针对不足,我们来看方案二。

解决方案二:还是使用Redis队列,但是我们使用阻塞模式(Blocking)

这个方案我们还是会用到Redis的队列,不同的是我们会使用到Redis提供的阻塞出队接口(blpop、brpop)。阻塞出队简单来说,就是在出队命令在没有接收到队列内的数据前,会挂起,直到在设置的阻塞时间内队列中有新的数据入队,则弹出数据,命令结束;如果在设置的阻塞时间内没有数据入队,则返回空;如果阻塞时间被设置为0,则进程将一直被挂起直到队列中有数据入队。

若使用阻塞模式,假设我们的推送时间为早上的十点,那么在9:58左右,可以运行入队命令,将需要推送的数据加入Redis队列中。代码示例如方案一,这里就不放了。

到了十点整,运行如下代码进行出队。

import redis
import json r = redis.StrictRedis(hostname='localhost', port=6379, db=0)
while True:
item = r.brpop('test_list', 0)
data = json.loads(item)
if not data:
break
# 这里是自定义的推送代码

注意第6行代码item = r.brpop('test_list', 0),第二个参数即为阻塞的等待时间。

使用这种方法基本可以保证推送消息的准确性,一边生成消息塞进队列,一边从队列里拿即可。但是似乎这种方式并没有发挥到“缓存”的优势。

解决方案三:偷鸡摸狗,用Redis的Hash实现一个“队列”

这个方案中我们会使用到Redis中的Hash功能。简单来说Redis的Hash就是一个类似Python中的字典,不同的是Redis的一个Hash-Field只能对应一个字符串。

r.hset(hash_key, hask_field, value)

使用该方案可以在用户修改计划任务时快速找到需要推送的消息并修改。出队的时候使用HAVLS并且遍历即可。代码就不放了,毕竟是个偷鸡摸狗的思路。

后端利用Redis队列及哈希实现定时推送提醒的三个思路的更多相关文章

  1. (七)RabbitMQ消息队列-通过fanout模式将消息推送到多个Queue中

    原文:(七)RabbitMQ消息队列-通过fanout模式将消息推送到多个Queue中 前面第六章我们使用的是direct直连模式来进行消息投递和分发.本章将介绍如何使用fanout模式将消息推送到多 ...

  2. 使用redis进行消息推送

    Redis支持这样一种特性,你可以将数据推到某个信息管道中,然后其它客户端可以通过订阅这些管道来获取推送过来的信息.使用Redis的Pub/Sub,接收方在某个channel注册为一个订阅者,然后监听 ...

  3. Springboot21 整合redis、利用redis实现消息队列

    1 前提准备 1.1 创建一个springboot项目 技巧01:本博文基于springboot2.0创建 1.2 安装redis 1.2.1 linux版本 参考博文 1.2.2 windows版本 ...

  4. Java利用Redis实现消息队列

    应用场景 为什么要用redis?二进制存储.java序列化传输.IO连接数高.连接频繁 一.序列化 这里编写了一个java序列化的工具,主要是将对象转化为byte数组,和根据byte数组反序列化成ja ...

  5. 我心中的核心组件~MSMQ与Redis队列

    回到目录 这个文章其实是我心中的核心组件的第七回,确实在时间上有些滞后了,但内容并不滞后!本文MSMQ只是个引题,我确实不太想说它,它是微软自己集成的一套消息队列,寄宿在Window服务里,稳定性十在 ...

  6. (3)redis队列功能

    Redis队列功能介绍 List 常用命令: Blpop删除,并获得该列表中的第一元素,或阻塞,直到有一个可用 Brpop删除,并获得该列表中的最后一个元素,或阻塞,直到有一个可用 Brpoplpus ...

  7. 利用Redis keyspace notification(键空间通知)实现过期提醒

    一.序言: 本文所说的定时任务或者说计划任务并不是很多人想象中的那样,比如说每天凌晨三点自动运行起来跑一个脚本.这种都已经烂大街了,随便一个 Crontab 就能搞定了. 这里所说的定时任务可以说是计 ...

  8. 如何利用redis key过期事件实现过期提醒

    https://blog.csdn.net/zhu_tianwei/article/details/80169900 redis自2.8.0之后版本提供Keyspace Notifications功能 ...

  9. 第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型

    第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型 Hash操作,redis中Hash在内存中的存储格式如下图: hset(name, key, value)name ...

随机推荐

  1. Oracle自动备份脚本的实现

    问题描述: Oracle自动备份脚本的实现. 错误提示1: Message file RMAN.msb not found Verify that Oracle_HOME is set properl ...

  2. 网络费用流-最小k路径覆盖

    多校联赛第一场(hdu4862) Jump Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Ot ...

  3. Java项目工程化之项目构建工具Maven

    欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系.本系列教程希望您能站在上帝 ...

  4. 170717、springboot编程之mybatis数据库开发和aop拦截

    一.springboot整合mybaits (1)新建maven project; 新建一个maven project,取名为:spring-boot-mybatis (2)在pom.xml文件中引入 ...

  5. string unicode utf8 ascii in python and js

    http://www.jb51.net/article/62155.htm http://www.cnblogs.com/dkblog/archive/2011/03/02/1980644.html ...

  6. scrapy爬虫系列之开头--scrapy知识点

    介绍:Scrapy是一个为了爬取网站数据.提取结构性数据而编写的应用框架,我们只需要实现少量的代码,就能够快速抓取.Scrapy使用了Twisted异步网络框架,可以加快我们的下载速度. 0.说明: ...

  7. nginx:正向代理和反向代理

    一.正向代理 原理:正向代理是一个位于客户端和目标服务器之间的代理服务器(中间服务器).为了从目标服务器取得内容,客户端向代理服务器发送一个请求,并且指定目标服务器,之后代理向目标服务器转交并且将获得 ...

  8. android 控件加圆角

    1.新建一个radius_border.xml <shape xmlns:android="http://schemas.android.com/apk/res/android&quo ...

  9. git-【四】撤销修改和删除文件操作

    一:撤销修改: 比如我现在在readme.txt文件里面增加一行 内容为555555555555,我们先通过命令查看如下: 在未提交之前,发现添加5555555555555内容有误,所以得马上恢复以前 ...

  10. maven-eclipse 中index.html页面乱码

    maven-eclipse 中index.html页面乱码: pox.xml修改: <project> -- <properties> <argLine>-Dfil ...