周煦辰 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. MVC之路由

    1.路由规则理解: 在MVC项目中,路由是一个独立的模块,需要引入System.Web.Routing ,路由的作用就是确定控制器和行为,同时确定其他的参数,然后将这些信息传递个相应的控制器和行为. ...

  2. 解析导航栏的url

    前段时间做ui自动化测试的时候,导航栏菜单始终有点问题,最后只好直接获取到url,然后直接使用driver.get(url)进入页面: 包括做压测的时候,比如我要找出所有报表菜单的url,这样不可能手 ...

  3. Oracle安装部署之RAC安装环境配置脚本

    #!/bin/bash#Usage:Log on as the superuser('root'),and then execute the command:#./1preusers.sh group ...

  4. redhat 6安装详解

    备注:redhat 6 系列OS安装步骤一致. 此处安装redhat 6.5 64位版本,即rhel-server-6.5-x86_64-dvd.iso 一.安装RHEL 6.5 用光盘成功引导系统, ...

  5. Error:(12, 64) java: 未报告的异常错误java.io.IOException; 必须对其进行捕获或声明以便抛出

    Error:(12, 64) java: 未报告的异常错误java.io.IOException; 必须对其进行捕获或声明以便抛出 package com.test; import org.apach ...

  6. 新一代Java模板引擎Thymeleaf

    新一代Java模板引擎Thymeleaf-spring,thymeleaf,springboot,java 相关文章-天码营 https://www.tianmaying.com/tutorial/u ...

  7. Navigation Nightmare---poj1984(多关系并查集)

    题目链接:http://poj.org/problem?id=1984 给定n个城市,m条边告诉你城市间的相对距离,接下来q组询问,问你在第几条边添加后两城市的距离. #include <ios ...

  8. 奔小康赚大钱---hdu2255(最大带权匹配)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 带权匹配问题的模板: 运用KM算法: #include<stdio.h> #incl ...

  9. django的restfulapi

    Django Rest framework 的流程大概是这样的 建立 Models 依靠 Serialiers 将数据库取出的数据 Parse 为 API 的数据(可用于返回给客户端,也可用于浏览器显 ...

  10. Python总结篇——知识大全

    python基础 Python开发环境搭建 Python变量和基本数据类型 python基本数据类型之操作 python的语法规范及for和while python编码 python文件操作 pyth ...