适用场景

  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 字符串方法isdigit()

    python isdigit() 方法检测字符串是否只有数字组成. 语法: isdigit()方法语法: str.isdigit() 参数:无 返回值: 如果字符串中只含有数字则返回True,否则返回 ...

  2. [C++] 检查随机内存溢出

    C++程序的随机内存溢出是非常难处理的,windows提供了一些工具来缓解这个问题. windows debuger提供的Global Flags可以设置"enable heap tail ...

  3. 安装node-sass时出现的错误解决方案(Mac自用,也可以借鉴)

    安装node-sass时出现一下错误: gyp ERR! configure error gyp ERR! stack Error: EACCES: permission denied, mkdir ...

  4. Codeforces Round #564 (Div. 2)

    传送门 参考资料 [1]: the Chinese Editoria A. Nauuo and Votes •题意 x个人投赞同票,y人投反对票,z人不确定: 这 z 个人由你来决定是投赞同票还是反对 ...

  5. tensorflow在文本处理中的使用——skip-gram模型

    代码来源于:tensorflow机器学习实战指南(曾益强 译,2017年9月)——第七章:自然语言处理 代码地址:https://github.com/nfmcclure/tensorflow-coo ...

  6. Canvas学习实践:一款简单的动画游戏

    最近学习了下Canvas绘图...突发奇想就有了下面这款简单的小游戏,纯属娱乐~ 废话不多说,直接上代码: <!DOCTYPE html> <html lang="zh&q ...

  7. Spring Security 学习笔记-信道过滤器

    信道过滤器主要职责是拦截不合规则的http请求,比如规定只能通过https访问资源,那么信道拦截器做相应的拦截处理,把http请求重定向为https请求,https请求则不做任何处理. 配置方式参照: ...

  8. XSS攻击及防范

    1.什么是XSS攻击 跨站脚本攻击(Cross Site Scripting),攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到 ...

  9. 【25.93%】【676D】Theseus and labyrinth

    time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  10. CMD操纵Mysql命令大全

    连接:mysql -h主机地址 -u用户名 -p用户密码 (注:u与root可以不用加空格,其它也一样)断开:exit (回车) 创建授权:grant select on 数据库.* to 用户名@登 ...