redis订阅发布简单实现
适用场景
- 业务流程遇到大量异步操作,并且业务不是很复杂
- 业务的健壮型要求不高
- 对即时场景要求不高
原理介绍
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>
出现的问题
- 用户的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订阅发布简单实现的更多相关文章
- ServiceStack.Redis订阅发布服务的调用(Z)
1.Redis订阅发布介绍Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式.发布者和订阅者之间使用频 ...
- ServiceStack.Redis订阅发布服务的调用
1.Redis订阅发布介绍 Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式. 发布者和订阅者之间使用频 ...
- Redis 订阅发布 - Jedis实现
Redis 订阅发布 - Jedis实现 我想到使用Redis的订阅发布模式是用来解决推送问题的-. 对于概念性的叙述,多多少少还是要提一下的: 什么是Redis发布订阅?Redis发布订阅是一种 ...
- ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现
ASP.NET MVC 学习笔记-2.Razor语法 1. 表达式 表达式必须跟在“@”符号之后, 2. 代码块 代码块必须位于“@{}”中,并且每行代码必须以“: ...
- 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用
责任链模式的具体应用 1.业务场景 生产车间中使用的条码扫描,往往一把扫描枪需要扫描不同的条码来处理不同的业务逻辑,比如,扫描投入料工位条码.扫描投入料条码.扫描产出工装条码等,每种类型的条码位数 ...
- python 实现redis订阅发布功能
redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...
- 2016022611 - redis订阅发布命令集合
redis消息订阅发布命令 参考地址:http://www.yiibai.com/redis/redis_pub_sub.html 消息发送者发送消息,通过redis的channal,消息接收者获取消 ...
- Linux--6 redis订阅发布、持久化、集群cluster、nginx入门
一.redis发布订阅 Redis 通过 PUBLISH .SUBSCRIBE 等命令实现了订阅与发布模式. 其实从Pub/Sub的机制来看,它更像是一个广播系统,多个Subscriber可以订阅多个 ...
- redis 订阅&发布(转载)
https://segmentfault.com/a/1190000016898228?utm_source=coffeephp.com 方法一: redis_helper.py: 封装发布订阅方法 ...
随机推荐
- 详解PhpStudy集成环境升级MySQL数据库版本
http://phpstudy.php.cn/jishu-php-2967.html phpstudy里没有地方可以设置mysql数据库,很多人都疑惑在phpstudy里怎么升级mysql数据库版本, ...
- linux 定时器 API
内核提供给驱动许多函数来声明, 注册, 以及去除内核定时器. 下列的引用展示了基本的 代码块: #include <linux/timer.h> struct timer_list { / ...
- 节点列表和HTML集合
getElementsByName()和getElementByTagName()返回的都是NodeList集合. 而document.images和document0.forms的属性为HTMLCo ...
- 【15.93%】【codeforces 672D】Robin Hood
time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...
- Tomcat 类加载器打破双亲委派模型
我们分为4个部分来探讨: 1. 什么是类加载机制? 2. 什么是双亲委任模型? 3. 如何破坏双亲委任模型? 4. Tomcat 的类加载器是怎么设计的? 我想,在研究tomcat 类加载之前,我们复 ...
- Android2_分析项目的结构
一.项目结构 成功运行第一个AS项目HelloWorld之后,我们开始试着分析一下这个项目.毕竟知其然也要知其所以然. 这是一个安卓的项目结构(实际上这是安卓模式的项目结构) 我们可以切换成Proje ...
- Python5_学习方法论
学习路线: 1.对基础知识的全面掌握,熟悉IDE和基本概念,注重知识体系构建: 2.开发实战.在该过程中掌握各类框架知识.熟悉模块的使用.并根据问题深入学习: ==================== ...
- Avram Joel Spolsky给计算机系学生的建议
Avram Joel Spolsky给计算机系的学生给了如下建议: (1)毕业前练好写作 (2)毕业前学好C语言 (3)毕业前学好微观经济学 (4)不要因为枯燥就不选修 ...
- VUE事件修饰符.passive、.capture、.once实现原理——重新认识addEventListener方法
https://www.jianshu.com/p/b12d0d3ad4c1 .passive的作用与使用场景 https://juejin.im/post/5ad804c1f265da504547f ...
- http、https、SSL、TLS的区别
一.HTTP和HTTPS之间的区别 HTTP是一种协议,全称叫作:超文本传输协议(HTTP,HyperText Transfer Protocol),是互联网上应用最为广泛的一种网络协议.所有的WWW ...