周煦辰 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. MariaDB登陆

    设置root密码 “mariabd”是新密码 [root@master /]# mysqladmin -u root password mariadb [root@master /]# mysql - ...

  2. 判断手机访问还是pc访问

    function isMobile(){ // 如果有HTTP_X_WAP_PROFILE则一定是移动设备 if (isset ($_SERVER['HTTP_X_WAP_PROFILE'])) re ...

  3. poj3345 Bribing FIPA【树形DP】【背包】

    Bribing FIPA Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 5910   Accepted: 1850 Desc ...

  4. 在idea中为函数自动生成注释(解决注释无法出现形参的情况)

    1 点击“File”-->“Settings”-->“Live Templates”打开如下对话框,点击右边绿色的加号,创建一个自定义的Template Group,如“Java” 2.选 ...

  5. Tickets---hdu1260 (简单dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1260 题意就是有n个人排队买票,每个人需要的时间是a[i] (1=< i <=N),但是现 ...

  6. saltstack相关

    通过saltstack实现根据不同业务特性进行配置集中化管理,分发文件,采集服务器数据,操作系统基础及软件包管理等第一层为web交互层,采用django+mysql+bootstarp实现,服务端采用 ...

  7. 内核通信之Netlink源码分析-用户内核通信原理3

    2017-07-06 上节主讲了用户层通过netlink和内核交互的详细过程,本节分析下用户层接收数据的过程…… 有了之前基础知识的介绍,用户层接收数据只涉及到一个核心调用readmsg(), 其他的 ...

  8. Ubuntu18.04 英文系统下安装中文输入法

    今天尝试了Ubuntu18.04LTS(依旧装的英文版)发现按照之前的方法( http://www.cnblogs.com/asmer-stone/p/5227188.html)安装中文输入法不行了, ...

  9. 0702-spring cloud config-git仓库配置、用户授权

    一.概述 参看地址:https://cloud.spring.io/spring-cloud-static/Edgware.SR3/single/spring-cloud.html#_environm ...

  10. Java8新特性(转载)

    1.Lambda表达式 Lambda表达式(也称为闭包)是整个Java 8发行版中最受期待的在Java语言层面上的改变.使用 Lambda 表达式可以使代码变的更加简洁紧凑. Lambda允许把函数作 ...