适用场景

  1. 业务流程遇到大量异步操作,并且业务不是很复杂
  2. 业务的健壮型要求不高
  3. 对即时场景要求不高

原理介绍

redis官网文档:https://redis.io/topics/notifications#configuration

spring集成订阅发布:https://docs.spring.io/spring-data/redis/docs/1.7.1.RELEASE/reference/html/#redis:pubsub:subscribe

相关demo

业务发布.java

 //        异步通知邮件
String expiredEmail = RedisConstants.REDIS_EXPIRE_Email_Send.getExpired()+ uuid;
ValueOperations<Serializable, Object> operations = redisTemplate3.opsForValue();
//由于使用的org.springframework.data.redis.core.StringRedisTemplate,所以value必须是String类型
operations.set(expiredEmail, "1", email_expire_time, TimeUnit.SECONDS);

EmailSyncEventListener.java

package com.redis.listeners;

import com.carapi.services.order.FxjTCarInvoiceOrderService;
import com.common.constant.RedisConstants;
import com.exception.ErrorException;
import com.model.fxjTCarInvoiceOrder.FxjTCarInvoiceOrder;
import com.model.fxjTCarOrderList.FxjTCarOrderList;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer; import java.nio.charset.StandardCharsets;
import java.util.List; /**
* 邮件发送
*/
public class EmailSyncEventListener extends KeyExpirationEventMessageListener {
private static final Logger log = LoggerFactory.getLogger(EmailSyncEventListener.class);
  //可以使用自动注入,或者xml配置
public EmailSyncEventListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
} @Autowired
private FxjTCarInvoiceOrderService tCarInvoiceOrderService;
// @Autowired
// public EmailSyncEventListener(RedisMessageListenerContainer listenerContainer) {
// super(listenerContainer);
// }
@Override
public void onMessage(Message message, byte[] pattern) { String channel = new String(message.getChannel(), StandardCharsets.UTF_8);
//过期的key
String key = new String(message.getBody(),StandardCharsets.UTF_8);
if(StringUtils.isNotEmpty(key) && key.indexOf(RedisConstants.REDIS_EXPIRE_Email_Send.getExpired()) != -1){
System.out.println(key);
key = key.substring(key.indexOf(RedisConstants.REDIS_EXPIRE_Email_Send.getExpired())+RedisConstants.REDIS_EXPIRE_Email_Send.getExpired().length());
log.info(key);
try { FxjTCarInvoiceOrder invoiceOrder = tCarInvoiceOrderService.selectByPrimaryKey(key);
if(invoiceOrder!=null){ tCarInvoiceOrderService.resendEmail(invoiceOrder.getEmail(),invoiceOrder.getInvoiceReqSerialNo());
}
} catch (ErrorException e) {
log.info("异步发送邮寄失败,验证失败" );
} catch (Exception e) {
log.info("异步发送邮件失败");
e.printStackTrace();
}
log.info("异步发送邮寄成功");
log.info("redis key 过期:pattern={},channel={},key={}",new String(pattern),channel,key);
}
}
}

  

spring-cache.xml

    <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory"></property>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<property name="stringSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
</bean> <bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.password}" />
<property name="database" value="2" />
<property name="timeout" value="${redis.timeout}" />
<property name="poolConfig" ref="jedisPoolConfig" />
<property name="usePool" value="true" />
</bean> <!--去掉redis client的CONFIG-->
<util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/> <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="3600" />
</bean> <!-- 将监听实现类注册到spring容器中 -->
<bean id="emailSyncEventListener" class="com.redis.listeners.EmailSyncEventListener">
<constructor-arg ref="redisMessageListenerContainer"></constructor-arg>
</bean>

出现的问题

  1. 用户的session存入redis后,redis的负载不平衡,出现了ttl为0的key删除延迟较长

Redis 并不保证生存时间(TTL)变为 0 的键会立即被删除:如果程序没有访问这个过期键, 或者带有生存时间的键非常多的话,那么在键的生存时间变为 0 , 直到键真正被删除这中间,可能会有一段比较显著的时间间隔。

因此,Redis 产生 expired 通知的时间为过期键被删除的时候,而不是键的生存时间变为 0 的时候。如果 Redis 正确配置且负载合理的,延时不会超超过 1s。

RedisExpiredQuartz.java

package com.redis.quart;

import com.common.constant.RedisConstants;
import com.redis.listeners.EmailSyncEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate; import javax.annotation.Resource;
import java.util.Set; /**
* Redis 并不保证生存时间(TTL)变为 0 的键会立即被删除:
* 如果程序没有访问这个过期键, 或者带有生存时间的键非常多的话,
* 那么在键的生存时间变为 0 ,
* 直到键真正被删除这中间,可能会有一段比较显著的时间间隔。
*
* so加个定时器
*/
public class RedisExpiredQuartz {
private static final Logger log = LoggerFactory.getLogger(EmailSyncEventListener.class);
@Resource
private RedisTemplate redisTemplate3; public synchronized void onRedisExpiredQuartz(){
log.trace("------------------------------------------");
for (RedisConstants value : RedisConstants.values()) {
Set keys = redisTemplate3.keys(value.getExpired() + "*");
log.debug("业务需要正常通知的keys:{}",keys);
}
}
}

RedisConstants.java

package com.common.constant;

public enum  RedisConstants {
/**
* 月卡过期取消key前缀
*/
REDIS_EXPIRE_Sub_Card("redisExpiredSubCard_"),
/**
* 延时邮件发送key前缀
*/
REDIS_EXPIRE_Email_Send("redisExpiredEmail_Send_"),
; private String expired; RedisConstants(String expired) {
this.expired = expired;
} public String getExpired() {
return expired;
} public void setExpired(String expired) {
this.expired = expired;
} // 获得 enum 对象
public static RedisConstants get(String expired) {
for (RedisConstants item : values()) {
if (expired == item.getExpired()) {
return item;
}
}
return null;
}
}

  

redis订阅发布简单实现的更多相关文章

  1. ServiceStack.Redis订阅发布服务的调用(Z)

      1.Redis订阅发布介绍Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式.发布者和订阅者之间使用频 ...

  2. ServiceStack.Redis订阅发布服务的调用

    1.Redis订阅发布介绍 Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式. 发布者和订阅者之间使用频 ...

  3. Redis 订阅发布 - Jedis实现

    Redis 订阅发布 - Jedis实现 我想到使用Redis的订阅发布模式是用来解决推送问题的-. 对于概念性的叙述,多多少少还是要提一下的: ​ 什么是Redis发布订阅?Redis发布订阅是一种 ...

  4. ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现

    ASP.NET MVC 学习笔记-2.Razor语法   1.         表达式 表达式必须跟在“@”符号之后, 2.         代码块 代码块必须位于“@{}”中,并且每行代码必须以“: ...

  5. 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用

    责任链模式的具体应用   1.业务场景 生产车间中使用的条码扫描,往往一把扫描枪需要扫描不同的条码来处理不同的业务逻辑,比如,扫描投入料工位条码.扫描投入料条码.扫描产出工装条码等,每种类型的条码位数 ...

  6. python 实现redis订阅发布功能

    redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...

  7. 2016022611 - redis订阅发布命令集合

    redis消息订阅发布命令 参考地址:http://www.yiibai.com/redis/redis_pub_sub.html 消息发送者发送消息,通过redis的channal,消息接收者获取消 ...

  8. Linux--6 redis订阅发布、持久化、集群cluster、nginx入门

    一.redis发布订阅 Redis 通过 PUBLISH .SUBSCRIBE 等命令实现了订阅与发布模式. 其实从Pub/Sub的机制来看,它更像是一个广播系统,多个Subscriber可以订阅多个 ...

  9. redis 订阅&发布(转载)

    https://segmentfault.com/a/1190000016898228?utm_source=coffeephp.com 方法一: redis_helper.py: 封装发布订阅方法 ...

随机推荐

  1. Python--day47--mysql索引注意事项

  2. [转载] 使用StAX解析xml

    StAX 概述 从一开始,Java API for XML Processing (JAXP) 就提供了两种方法来处理 XML:文档对象模型(DOM)方法是用标准的对象模型表示 XML 文档:Simp ...

  3. H3C ACL包过滤的局限性

  4. Python--day60--建立第一个Djiango项目

  5. linux版本依赖

    记住, 你的模块代码一定要为每个它要连接的内核版本重新编译 -- 至少, 在缺乏 modversions 时, 这里不涉及因为它们更多的是给内核发布制作者, 而不是开发者. 模块 是紧密结合到一个特殊 ...

  6. 基于小米即时消息云服务(MIMC)的Web IM

    michat 一个基于小米即时消息云服务(MIMC)的Web IM. 源码地址github和gitee同步. 截图展示 如何使用 请先双击目录"需要安装的jars"的install ...

  7. git 安装及基本配置

    git 基本上来说是开发者必备工具了,在服务器里没有 git 实在不太能说得过去.何况,没有 git 的话,面向github编程 从何说起,如同一个程序员断了左膀右臂. 你对流程熟悉后,只需要一分钟便 ...

  8. 【Bad Blood】翻译0

    (一)作者注 本书基于对超过150人以上的上百个采访,包括60多个Theranos的职工而著成.叙述中出现的人物大多数都是他们的真实姓名,也有些人希望我隐藏身份信息,他们有些害怕公司的惩罚,有些担心会 ...

  9. 面试中常考的字符串操作方法大全,包含ES6

    原文链接:http://caibaojian.com/js-string.html 一.charAt() 返回在指定位置的字符. var str="abc" console.log ...

  10. Linux 内核即插即用规范

    一些新 ISA 设备板遵循特殊的设计规范并且需要一个特别的初始化顺序, 对增加接口板 的简单安装和配置的扩展. 这些板的设计规范称为即插即用, 由一个麻烦的规则集组成, 来建立和配置无跳线的 ISA ...