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: 封装发布订阅方法 ...
随机推荐
- Python--day38---进程间通信--初识队列(multiprocess.Queue)
初识队列: 进程间通信IPC(Inter-Process Communication) 1,队列的方法: q = Queue(5)1,q.put(1) #把1放进队列 2,print(q.full() ...
- ubuntu中桌面图标的配置
在网上随处可以找到怎么样把应用程序的图标放到桌面上,我刚用ubuntu时也是按照网上的做法,一步一步的做的,现将网上的做法复制下来: 桌面配置文件简述\label{sec:desktop file} ...
- H3C IPv6地址构成
- ASP.NET MVC4.0+EF+LINQ+bui+bootstrap+网站+角色权限管理系统(开篇)
系统预览: 源码下载:http://www.yealuo.com/Home/Detail?KeyValue=2f926407-f80b-4bff-a729-949a53efed7b 创建项目,新手按步 ...
- Numpy 返回数组大小
import numpy as np a = [[1, 2], [3, 4], [5, 6]] b = np.array(a) len(a) # 3 len(b) # 3 np.size(a) # 3 ...
- vue-learning:13 - js - vue作用域概念:全局和局部
目录 全局作用域:Vue对象 全局api 局部作用域: 实例对象vm 实例api 组件component 组件配置选项 在引入Vue文件时,就相当于拥有了一个全局Vue对象. 在var vm = ne ...
- IntPtr、Struct 相互转换
一般写c#代码基本用不到 相互转换 只有调用c++中的dll动态库的时候才用的到 struct转intptr public static IntPtr StructToIntPtr<T>( ...
- 22.BASE_DIR,os,sys
原文: BASE_DIR演示 想在bin里调用main里的方法.需要找到目录. import sys,os BASE_DIR = os.path.dirname(os.path.dirname(os. ...
- 关于java继承的哪些事
引言 本文结合一个例子来说明继承实现的基本原理. 基类Base代码如下所示: public class Base { public static int s; private int a; stati ...
- HTML基础常识
什么是HTML? 超文本标记语言,用来制作网页 浏览器常识: 常见浏览器: 谷歌(Chrome).苹果(Safari) . IE(Edge).欧朋(Opera).火狐(Firefox) 浏览器内核:浏 ...