redis 高级应用–使用 redis 消息队列完成秒杀过期订单处理(2)
一、springDataRedis 中定义消息的监听器
1、在 JAVA 程序中监听 redis 消息:配置监听 redis 的消息。
如果要在 java 代码中监听 redis 的主题消息,需要自定义处理消息的监听器类。
2、在工程 spring-data-redis-test (模块)中,创建消息的监听器 类 RedisMessageListener.java
/**
* spring-data-redis-test\src\main\java\djh\it\listener\RedisMessageListener.java
*
* 2024-7-24 创建消息的监听器 类 RedisMessageListener.java
*
* 配置 redis 消息的监听器:获取 redis 中的消息并进行处理。
*/
package djh.it.listener;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
public class RedisMessageListener implements MessageListener {
/**
* onMessage : 处理消息。
* @param message :完整的消息(频道的信息,以及消息的具体内容)
* @param bytes:获取的频道信息
*/
@Override
public void onMessage( Message message, byte[] bytes ) {
System.out.println("从channel为:" + new String(message.getChannel())
+"获取了一条新的消息,消息内容为:" + new String(message.getBody()));
}
}
二、springDataRedis 中配置消息的监听器
1、配置消息的监听器:
1)监听器的适配器:(配置自定义的监听器)
2)配置监听器容器:(对监听器进行统一管理)
2、在工程 spring-data-redis-test (模块)中,修改 配置文件 applicationContext-redis.xml 添加消息监听。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd ">
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<!-- 设置key序列化方式 -->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
<!-- 设置value序列化方式 -->
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
<!-- 注入连接工厂 -->
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
<!-- 创建connectionFactory交给spring容器管理 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="127.0.0.1"></property>
<property name="port" value="6379"></property>
<property name="database" value="0"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
<!-- 创建连接池的基本配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="MaxTotal" value="10"></property>
</bean>
<!-- 配置消息的监听器:
1)监听器的适配器:(配置自定义的监听器)
2)配置监听器容器:(对监听器进行统一管理)
-->
<!-- 配置处理消息的消息监听适配器 -->
<bean class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter" id="messageListener">
<constructor-arg>
<!-- 注入自定义的监听器 -->
<bean class="djh.it.listener.RedisMessageListener"></bean>
</constructor-arg>
</bean>
<!-- 配置消息监听器容器 -->
<bean class="org.springframework.data.redis.listener.RedisMessageListenerContainer" id="redisContainer">
<!--
由于需要和 redis 进行交互,注入连接工厂
1)订阅的消息主题
2)接收到消息之后处理的监听器
-->
<property name="connectionFactory" ref="connectionFactory"></property>
<property name="messageListeners">
<map>
<entry key-ref="messageListener">
<!-- topic 的集合:订阅的主题
订阅了一个主题消息:主题的名称(dzs168)
接收到一条消息之后,交由自定义的消息监听器来进行处理
-->
<list>
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="dzs168"></constructor-arg>
</bean>
</list>
</entry>
</map>
</property>
</bean>
</beans>
三、springDataRedis 中测试消息通知
1、在工程 spring-data-redis-test (模块)中,创建 测试类 RedisTest02.java
/**
* spring-data-redis-test\src\test\java\djh\it\redis\test\RedisTest02.java
*
* 2024-7-24 测试类 RedisTest02.java
*/
package djh.it.redis.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RedisTest02 {
public static void main( String[] args ) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-redis.xml");
}
}
2、运行测试类 RedisTest02.java 进行消息监听,同时启动一客户端,连接 redis 服务端,
进行消息发送,观察测试类控制台输出结果:
“ 从channel为:dzs168获取了一条新的消息,消息内容为:very good ”
3、测试消息说明
1)配置好消息监听,已经订阅的主题之后就可以启动程序进行测试了。由于有监听程序在,只需要已 java 代码的形式启动,创建 spring 容器(当 spring 容器加载之后,会创建监听器一直监听对应的消息)。
2)当程序启动之后,会一直保持运行状态。即订阅了 dzs168 频道的消息,这个时候通过 redis 的客户端程序(redis-cli)发布一条消息。
3)publish topic名称 消息内容 : 向指定频道发送一条消息。
发送消息之后,我们在来看 java 控制台输出验证是否获取到了此消息。
4)至此已经完成了在 java 代码中获取 redis 的消息通知。
四、失效问题处理:业务分析以及资料介绍
1、结合 redis 的 key 失效机制和消息完成过期优惠券处理,解决过期优惠券的问题:
1)在 redis 的内部当一个 key 失效时,也会向固定的频道中发送一条消息,只需要监听到此消息获取数据库中的 id,修改对应的优惠券状态就可以了。
2)这也带来了一些繁琐的操作:用户获取到优惠券之后需要将优惠券存入 redis 服务器并设置超时时间。
3)由于要借助redis的key失效通知,有两个注意事项各位需要注意:
-
- 事件通过 Redis 的订阅与发布功能(pub/sub)来进行分发,故需要订阅(__keyevent@0__:expired)频道 0表示db0 根据自己的dbindex选择合适的数字。
-
- 修改 redis.conf 文件
# 添加
notify-keyspace-events Ex
2、模拟过期代金卷案例介绍:
1)使用 redis 的消息通知结合 springDataRedis 完成一个过期优惠券的处理。
2)需要两套程序,一个模拟 用户获取优惠券(coupon-achieve),一个模拟 过期优惠券的处理(coupon-expired )。
3)第一套程序:coupon-achieve
用户获取优惠券
- 手动创建一个优惠券保存到数据库中。
- 同时保存到 redis 服务器中,同时设置失效时间。
- 由于 redis 中 key 的失效机制(只会发送key的失效消息)
key(coupon:优惠券ID)-value(优惠券ID)。
4)第二套程序:coupon-expired
过期优惠券的处理
- 订阅的主题: keyevent@0:expired
- 配置消息监听器
获取到失效消息(coupon:优惠券id),从失效消息中分离出优惠券id
查询数据库,修改优惠券状态。
3、mysql 数据库表创建。
# 创建数据库
CREATE DATABASE `coupon` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
# 使用数据库
use coupon;
# 创建数据表
DROP TABLE IF EXISTS `t_coupon`;
CREATE TABLE `t_coupon` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(60) DEFAULT NULL COMMENT '优惠券名称',
`money` decimal(10,0) DEFAULT NULL COMMENT '金额',
`coupon_desc` varchar(128) DEFAULT NULL COMMENT '优惠券说明',
`create_time` datetime DEFAULT NULL COMMENT '获取时间',
`expire_time` datetime DEFAULT NULL COMMENT '失效时间',
`state` int(1) DEFAULT NULL COMMENT '状态,0-有效,1-已失效,2-已使用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
五、失效问题处理:模拟获取优惠券
1、在工程 spring-data-redis-test (模块)中,创建 测试类 RedisTest02.java
/**
* spring-data-redis-test\src\test\java\djh\it\redis\test\RedisTest02.java
*
* 2024-7-24 测试类 RedisTest02.java
*/
package djh.it.redis.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RedisTest02 {
public static void main( String[] args ) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-redis.xml");
}
}
2、运行测试类 RedisTest02.java 进行消息监听,同时启动一客户端,连接 redis 服务端,
进行消息发送,观察测试类控制台输出结果:
从channel为:dzs168获取了一条新的消息,消息内容为:very good
6-springDataRedis中测试消息通知.png
3、测试消息说明
1)配置好消息监听,已经订阅的主题之后就可以启动程序进行测试了。由于有监听程序在,只需要已 java 代码的形式启动,创建 spring 容器(当 spring 容器加载之后,会创建监听器一直监听对应的消息)。
2)当程序启动之后,会一直保持运行状态。即订阅了 dzs168 频道的消息,这个时候通过 redis 的客户端程序(redis-cli)发布一条消息。
3)publish topic名称 消息内容 : 向指定频道发送一条消息。
发送消息之后,我们在来看 java 控制台输出验证是否获取到了此消息。
4)至此已经完成了在 java 代码中获取 redis 的消息通知。
12-失效问题处理:业务分析以及资料介绍
redis 高级应用–使用 redis 消息队列完成秒杀过期订单处理(2)
四、失效问题处理:业务分析以及资料介绍
1、结合 redis 的 key 失效机制和消息完成过期优惠券处理,解决过期优惠券的问题:
1)在 redis 的内部当一个 key 失效时,也会向固定的频道中发送一条消息,只需要监听到此消息获取数据库中的 id,修改对应的优惠券状态就可以了。
2)这也带来了一些繁琐的操作:用户获取到优惠券之后需要将优惠券存入 redis 服务器并设置超时时间。
3)由于要借助redis的key失效通知,有两个注意事项各位需要注意:
-
- 事件通过 Redis 的订阅与发布功能(pub/sub)来进行分发,故需要订阅(__keyevent@0__:expired)频道 0表示db0 根据自己的dbindex选择合适的数字。
-
- 修改 redis.conf 文件
# 添加
notify-keyspace-events Ex
2、模拟过期代金卷案例介绍:
1)使用 redis 的消息通知结合 springDataRedis 完成一个过期优惠券的处理。
2)需要两套程序,一个模拟 用户获取优惠券,一个模拟 过期优惠券的处理。
3)第一套程序:coupon-achieve
用户获取优惠券
- 手动创建一个优惠券保存到数据库中。
- 同时保存到 redis 服务器中,同时设置失效时间。
- 由于 redis 中 key 的失效机制(只会发送key的失效消息)
key(coupon:优惠券ID)-value(优惠券ID)。
4)第二套程序:coupon-expired
过期优惠券的处理
- 订阅的主题: keyevent@0:expired
- 配置消息监听器
获取到失效消息(coupon:优惠券id),从失效消息中分离出优惠券id
查询数据库,修改优惠券状态。
3、mysql 数据库表创建。
# 创建数据库
CREATE DATABASE `coupon` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
# 使用数据库
use coupon;
# 创建数据表
DROP TABLE IF EXISTS `t_coupon`;
CREATE TABLE `t_coupon` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(60) DEFAULT NULL COMMENT '优惠券名称',
`money` decimal(10,0) DEFAULT NULL COMMENT '金额',
`coupon_desc` varchar(128) DEFAULT NULL COMMENT '优惠券说明',
`create_time` datetime DEFAULT NULL COMMENT '获取时间',
`expire_time` datetime DEFAULT NULL COMMENT '失效时间',
`state` int(1) DEFAULT NULL COMMENT '状态,0-有效,1-已失效,2-已使用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
13-失效问题处理:模拟获取优惠券
redis 高级应用–使用 redis 消息队列完成秒杀过期订单处理(2)
五、失效问题处理:模拟获取优惠券
1、打开 idea,创建 coupon-achieve 的 maven 工程。
--> idea --> File
--> New --> Project
--> Maven
Project SDK: ( 1.8(java version "1.8.0_131" )
--> Next
--> Groupld : ( djh.it )
Artifactld : ( coupon-achieve )
Version : 1.0-SNAPSHOT
--> Name: ( coupon-achieve )
Location: ( ...\coupon-achieve\ )
--> Finish
2、在工程 coupon-achieve (模块)中的 pom.xml 中导入依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast</groupId>
<artifactId>coupon-achieve</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<springdataredis.version>1.7.4.RELEASE</springdataredis.version>
<!-- <mysql.version>5.1.6</mysql.version>-->
<mysql.version>8.0.23</mysql.version>
</properties>
<dependencies>
<!-- spring data redis相关坐标 beg -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<!-- <version>2.2</version>-->
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${springdataredis.version}</version>
</dependency>
<!-- spring data redis相关坐标 end -->
<!-- spring 相关坐标 beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring 相关坐标 end -->
<!-- 日志相关坐标 beg -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 日志相关坐标 end -->
<!-- 单元测试相关坐标 beg -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!-- 单元测试相关坐标 end -->
<!-- druid数据库连接池 beg -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<!-- druid数据库连接池 end -->
<!-- mysql驱动 beg -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- mysql驱动 end -->
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mybatis整合spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
<build>
<finalName>coupon-achieve</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
3、在工程 coupon-achieve (模块)中,创建实体类 Coupon.java
/**
* redis-coupon\coupon-achieve2\src\main\java\cn\itcast\entity\Coupon.java
*
* 2024-7-24 创建实体类 Coupon.java
*/
package cn.itcast.entity;
import java.math.BigDecimal;
import java.util.Date;
/**
* 优惠券实体类
*/
public class Coupon {
private Long id;//主键id
private String name;//优惠券名称
private BigDecimal money;//优惠券金额
private String couponDesc;//说明
private Date createTime;//获取时间
private Date expireTime;//失效时间
private Integer state;//状态 0:可用 1:已失效 2:已使用
public Coupon() {}
public Coupon(String name, BigDecimal money, String couponDesc, Date createTime, Date expireTime,Integer state) {
this.name = name;
this.money = money;
this.couponDesc = couponDesc;
this.createTime = createTime;
this.expireTime = expireTime;
this.state = state;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
public String getCouponDesc() {
return couponDesc;
}
public void setCouponDesc(String couponDesc) {
this.couponDesc = couponDesc;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getExpireTime() {
return expireTime;
}
public void setExpireTime(Date expireTime) {
this.expireTime = expireTime;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
@Override
public String toString() {
return "Coupon [id=" + id + ", name=" + name + ", money=" + money + ", couponDesc=" + couponDesc
+ ", createTime=" + createTime + ", expireTime=" + expireTime + ", state=" + state + "]";
}
}
4、在工程 coupon-achieve (模块)中,创建映射类 CouponMapper.java
/**
* redis-coupon\coupon-achieve2\src\main\java\cn\itcast\mapper\CouponMapper.java
*
* 2024-7-24 创建映射类 CouponMapper.java
*/
package cn.itcast.mapper;
import cn.itcast.entity.Coupon;
public interface CouponMapper {
//保存优惠券信息
public void saveCoupon(Coupon coupon);
//根据id更新优惠券信息
public void updateCoupon(Coupon coupon);
//根据id查询优惠券信息
public Coupon selectCouponById(long id);
}
5、在工程 coupon-achieve (模块)中,创建工具类 CouponMapper.java
/**
* redis-coupon.coupon-achieve2.src.main.java.cn.itcast.utils.DataUtils.java
*
* 2024-7-24 创建工具类 CouponMapper.java
*/
package cn.itcast.utils;
import java.util.Calendar;
import java.util.Date;
public class DataUtils {
public static Date addTime(Date date,int minute) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, minute);
return calendar.getTime();
}
}
6、在工程 coupon-achieve (模块)中,创建 配置文件 CouponMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.itcast.mapper.CouponMapper">
<resultMap id="BaseResultMap" type="cn.itcast.entity.Coupon">
<id column="id" property="id" jdbcType="BIGINT" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="money" property="money" jdbcType="DECIMAL" />
<result column="coupon_desc" property="couponDesc" jdbcType="VARCHAR" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="expire_time" property="expireTime" jdbcType="TIMESTAMP" />
<result column="state" property="state" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List">
id,name,money,coupon_desc,create_time,expire_time,state
</sql>
<sql id="table">
t_coupon
</sql>
<select id="selectCouponById" resultMap="BaseResultMap" parameterType="long">
select <include refid="Base_Column_List" /> from <include refid="table" /> where id =#{id,jdbcType=BIGINT}
</select>
<insert id="saveCoupon" parameterType="cn.itcast.entity.Coupon" useGeneratedKeys="true" keyProperty="id">
insert into <include refid="table" /> (name,money,coupon_desc,create_time,expire_time,state)
values (#{name}, #{money},#{couponDesc},#{createTime}, #{expireTime}, #{state})
</insert>
<update id="updateCoupon" parameterType="cn.itcast.entity.Coupon">
update <include refid="table" />
set name = #{name},
money = #{money},
coupon_desc = #{couponDesc},
create_time = #{createTime},
expire_time = #{expireTime},
state = #{state,jdbcType=INTEGER}
where id = #{id,jdbcType=BIGINT}
</update>
</mapper>
<!-- redis-coupon\coupon-achieve2\src\main\resources\cn\itcast\mapper\CouponMapper.xml -->
7、在工程 coupon-achieve (模块)中,创建 applicationContext.xml 配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<context:component-scan base-package="cn.itcast"></context:component-scan>
<!-- 1.数据源,连接数据库 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.工厂 生产sqlSession -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="cn.itcast.entity"></property>
<property name="configLocation" value="classpath:sqlMapConfig.xml"></property>
</bean>
<!-- 3.扫描接口,接口代理开发 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.mapper"></property>
</bean>
<!-- 4. 创建事物管理平台(对象) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.事物管理策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 6.事物切面 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.service.*.*(..))"/>
</aop:config>
<import resource="classpath:applicationContext-data-redis.xml"/>
</beans>
<!-- redis-coupon\coupon-achieve2\src\main\resources\applicationContext.xml -->
8、在工程 coupon-achieve (模块)中,创建 applicationContext-data-redis.xml 配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">
<description>spring-data整合jedis</description>
<!-- springData Redis 核心api-->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory"></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>
</bean>
<!-- jedis连接工厂 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="127.0.0.1"></property>
<property name="port" value="6379"></property>
<property name="database" value="0"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
<!-- jedis连接池的配置信息 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="5"></property>
<property name="maxTotal" value="10"></property>
<property name="testOnBorrow" value="true"></property>
</bean>
</beans>
<!-- redis-coupon\coupon-achieve2\src\main\resources\applicationContext-data-redis.xml -->
9、在工程 coupon-achieve (模块)中,创建 jdbc.properties 配置文件。
# redis-coupon\coupon-achieve2\src\main\resources\jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/coupon
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=12311
10、在工程 coupon-achieve (模块)中,创建 sqlMapConfig.xml 配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
11、在工程 coupon-achieve (模块)中,创建 log4j.properties 日志文件。
# redis-coupon\coupon-achieve2\src\main\resources\log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
12、在工程 coupon-achieve (模块)中,创建测试类 CouponTest.java
/**
* D:\java-test\idea2019\redis-coupon\coupon-achieve2\src\test\java\cn\itcast\coupon\test\CouponTest.java
*
* 2024-7-24 创建测试类 CouponTest.java
*/
package cn.itcast.coupon.test;
import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.entity.Coupon;
import cn.itcast.mapper.CouponMapper;
import cn.itcast.utils.DataUtils;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class CouponTest {
@Autowired
private CouponMapper couponMapper;
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
* 1.创建一个优惠券
* 2.保存到数据库中
* 3.保存到redis服务器中,设置超时时间
*/
@Test
public void testGetCoupon() {
Date now = new Date();
int timeOut = 1;// 优惠券的超时时间:1分钟
//自定义一个优惠券
Coupon coupon = new Coupon();
coupon.setName("段子手168优惠券");
coupon.setMoney(BigDecimal.ONE);//设置金额
coupon.setCouponDesc("全品类优惠券50元");
coupon.setCreateTime(now);
coupon.setExpireTime(DataUtils.addTime(now, timeOut));//设置优惠券的失效时间,一份后失效
coupon.setState(0);
//将优惠券保存到数据库中
couponMapper.saveCoupon(coupon);
//将优惠券设置到redis缓存中,并且设置超时件
redisTemplate.opsForValue().set("coupon:"+coupon.getId(), coupon.getId()+"", timeOut, TimeUnit.MINUTES);
}
}
13、启动 redis 服务端和客户端,运行测试类 CouponTest.java 进行测试,如下图,
在数据库中查询到插入的记录,redis 客户端也查询到数据,但 1 分钟后失效。
7-失效问题处理-模拟获取优惠券.png
六、失效问题处理:配置消息监听,借助 redis 的 key 失效通知设置优惠券状态
1、打开 idea,创建 coupon-expired 的 maven 工程。
--> idea --> File
--> New --> Project
--> Maven
Project SDK: ( 1.8(java version "1.8.0_131" )
--> Next
--> Groupld : ( djh.it )
Artifactld : ( coupon-expired )
Version : 1.0-SNAPSHOT
--> Name: ( coupon-expired )
Location: ( ...\coupon-expired\ )
--> Finish
2、 在工程 coupon-expired (模块)中的 pom.xml 中导入依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast</groupId>
<artifactId>coupon-expired</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<springdataredis.version>1.7.4.RELEASE</springdataredis.version>
<!-- <mysql.version>5.1.6</mysql.version>-->
<mysql.version>8.0.23</mysql.version>
</properties>
<dependencies>
<!-- spring data redis相关坐标 beg -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<!-- <version>2.2</version>-->
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${springdataredis.version}</version>
</dependency>
<!-- spring data redis相关坐标 end -->
<!-- spring 相关坐标 beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring 相关坐标 end -->
<!-- 日志相关坐标 beg -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 日志相关坐标 end -->
<!-- 单元测试相关坐标 beg -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!-- 单元测试相关坐标 end -->
<!-- druid数据库连接池 beg -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<!-- druid数据库连接池 end -->
<!-- mysql驱动 beg -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- mysql驱动 end -->
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- mybatis整合spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
<build>
<finalName>coupon-expired</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
<!-- redis-coupon\coupon-expired2\pom.xml -->
3、在工程 coupon-expired (模块)中,创建监听器类 RedisMessageListener.java
/**
* redis-coupon\coupon-expired2\src\main\java\cn\itcast\redis\listener\RedisMessageListener.java
*
* 2024-7-24 创建监听器类 RedisMessageListener.java
*/
package cn.itcast.redis.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import cn.itcast.entity.Coupon;
import cn.itcast.mapper.CouponMapper;
public class RedisMessageListener implements MessageListener {
@Autowired
private CouponMapper couponMapper;
//处理消息
/**
* 获取的时失效key通知:
* 发送的消息:key(coupon:优惠券id)
*
* 1.接收到消息获取消息(验证是否时优惠券失效的key)
* 2.从消息中分离出优惠券id
* 3.查询数据库获取优惠券对象
* 4.设置失效状态,更新数据库
*
*/
public void onMessage(Message message, byte[] pattern) {
String key = new String (message.getBody());
if(key.startsWith("coupon")) {
String id = key.split(":")[1];
Coupon coupon = couponMapper.selectCouponById(Long.parseLong(id));
coupon.setState(1);
couponMapper.updateCoupon(coupon);
}
}
}
4、在工程 coupon-expired (模块)中,创建实体类 Coupon.java
/**
* redis-coupon\coupon-expired2\src\main\java\cn\itcast\entity\Coupon.java
*
* 2024-7-24 创建实体类 Coupon.java
*/
package cn.itcast.entity;
import java.math.BigDecimal;
import java.util.Date;
/**
* 优惠券实体类
*/
public class Coupon {
private Long id;//主键id
private String name;//优惠券名称
private BigDecimal money;//优惠券金额
private String couponDesc;//说明
private Date createTime;//获取时间
private Date expireTime;//失效时间
private Integer state;//状态 0:可用 1:已失效 2:已使用
public Coupon() {}
public Coupon(String name, BigDecimal money, String couponDesc, Date createTime, Date expireTime,Integer state) {
this.name = name;
this.money = money;
this.couponDesc = couponDesc;
this.createTime = createTime;
this.expireTime = expireTime;
this.state = state;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
public String getCouponDesc() {
return couponDesc;
}
public void setCouponDesc(String couponDesc) {
this.couponDesc = couponDesc;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getExpireTime() {
return expireTime;
}
public void setExpireTime(Date expireTime) {
this.expireTime = expireTime;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
@Override
public String toString() {
return "Coupon [id=" + id + ", name=" + name + ", money=" + money + ", couponDesc=" + couponDesc
+ ", createTime=" + createTime + ", expireTime=" + expireTime + ", state=" + state + "]";
}
}
5、在工程 coupon-expired (模块)中,创建映射类 CouponMapper.java
/**
* redis-coupon\coupon-expired2\src\main\java\cn\itcast\mapper\CouponMapper.java
*
* 2024-7-24 创建映射类 CouponMapper.java
*/
package cn.itcast.mapper;
import cn.itcast.entity.Coupon;
public interface CouponMapper {
//保存优惠券信息
public void saveCoupon(Coupon coupon);
//根据id更新优惠券信息
public void updateCoupon(Coupon coupon);
//根据id查询优惠券信息
public Coupon selectCouponById(long id);
}
6、在工程 coupon-expired (模块)中,创建工具类 DataUtils.java
/**
* redis-coupon.coupon-expired2.src.main.java.cn.itcast.utils.DataUtils.java
*
* 2024-7-24 创建工具类 DataUtils.java
*/
package cn.itcast.utils;
import java.util.Calendar;
import java.util.Date;
public class DataUtils {
public static Date addTime(Date date,int minute) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, 10);
return calendar.getTime();
}
}
7、在工程 coupon-expired (模块)中,创建配置文件 CouponMapper.xml 。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.itcast.mapper.CouponMapper">
<resultMap id="BaseResultMap" type="cn.itcast.entity.Coupon">
<id column="id" property="id" jdbcType="BIGINT" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="money" property="money" jdbcType="DECIMAL" />
<result column="coupon_desc" property="couponDesc" jdbcType="VARCHAR" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="expire_time" property="expireTime" jdbcType="TIMESTAMP" />
<result column="state" property="state" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List">
id,name,money,coupon_desc,create_time,expire_time,state
</sql>
<sql id="table">
t_coupon
</sql>
<select id="selectCouponById" resultMap="BaseResultMap" parameterType="long">
select <include refid="Base_Column_List" /> from <include refid="table" /> where id =#{id,jdbcType=BIGINT}
</select>
<insert id="saveCoupon" parameterType="cn.itcast.entity.Coupon" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into <include refid="table" /> (name,money,coupon_desc,create_time,expire_time,state)
values (#{name}, #{money},#{couponDesc},#{createTime}, #{expireTime}, #{state})
</insert>
<update id="updateCoupon" parameterType="cn.itcast.entity.Coupon">
update <include refid="table" />
set name = #{name},
money = #{money},
coupon_desc = #{couponDesc},
create_time = #{createTime},
expire_time = #{expireTime},
state = #{state,jdbcType=INTEGER}
where id = #{id,jdbcType=BIGINT}
</update>
</mapper>
<!-- redis-coupon\coupon-expired2\src\main\resources\cn\itcast\mapper\CouponMapper.xml -->
8、在工程 coupon-expired (模块)中,创建配置文件 applicationContext.xml 。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<context:component-scan base-package="cn.itcast"></context:component-scan>
<!-- 1.数据源,连接数据库 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.工厂 生产sqlSession -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="cn.itcast.entity"></property>
<property name="configLocation" value="classpath:sqlMapConfig.xml"></property>
</bean>
<!-- 3.扫描接口,接口代理开发 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.mapper"></property>
</bean>
<!-- 4. 创建事物管理平台(对象) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.事物管理策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 6.事物切面 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.service.*.*(..))"/>
</aop:config>
<import resource="classpath:applicationContext-data-redis.xml"/>
</beans>
<!-- redis-coupon\coupon-expired2\src\main\resources\applicationContext.xml -->
9、在工程 coupon-expired (模块)中,创建配置文件 applicationContext-data-redis.xml。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">
<description>spring-data整合jedis</description>
<!-- springData Redis的核心API -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory"></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>
</bean>
<!-- 连接工厂 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="127.0.0.1"></property>
<property name="port" value="6379"></property>
<property name="database" value="0"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
<!-- 连接池基本配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="5"></property>
<property name="maxTotal" value="10"></property>
<property name="testOnBorrow" value="true"></property>
</bean>
<!-- 配置监听 -->
<bean class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter" id="messageListener">
<constructor-arg>
<bean class="cn.itcast.redis.listener.RedisMessageListener"/>
</constructor-arg>
</bean>
<!-- 监听容器 -->
<bean class="org.springframework.data.redis.listener.RedisMessageListenerContainer" id="redisContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="messageListeners">
<map>
<entry key-ref="messageListener">
<list>
<!-- __keyevent@0__:expired 配置订阅的主题名称
此名称时redis提供的名称,标识过期key消息通知
0表示db0 根据自己的dbindex选择合适的数字
-->
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="__keyevent@0__:expired"></constructor-arg>
</bean>
</list>
</entry>
</map>
</property>
</bean>
</beans>
<!-- redis-coupon\coupon-expired2\src\main\resources\applicationContext-data-redis.xml -->
10、在工程 coupon-expired (模块)中,创建配置文件 jdbc.properties。
# redis-coupon\coupon-expired2\src\main\resources\jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/coupon
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=12311
11、在工程 coupon-expired (模块)中,创建配置文件 sqlMapConfig.xml。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
12、在工程 coupon-expired (模块)中,创建日志文件 log4j.properties
# redis-coupon\coupon-expired2\src\main\resources\log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
13、在工程 coupon-expired (模块)中,创建测试类 CouponTest.java
/**
* redis-coupon\coupon-expired2\src\test\java\cn\itcast\coupon\test\CouponTest.java
*
* 2024-7-24 创建测试类 CouponTest.java
*/
package cn.itcast.coupon.test;
import java.math.BigDecimal;
import java.util.Date;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.entity.Coupon;
import cn.itcast.mapper.CouponMapper;
import cn.itcast.utils.DataUtils;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class CouponTest {
@Autowired
private CouponMapper couponMapper;
@Test
public void testSaveCoupon() {
Date now = new Date();
Coupon coupon = new Coupon("测试优惠券", BigDecimal.ONE,
"全品类优惠10元", now, DataUtils.addTime(now, 1),0);
couponMapper.saveCoupon(coupon );
}
@Test
public void testUpdateCoupon() {
Coupon coupon = couponMapper.selectCouponById(1l);
coupon.setState(1);
couponMapper.updateCoupon(coupon);
}
@Test
public void selectCouponById() {
Coupon coupon = couponMapper.selectCouponById(1l);
System.out.println(coupon);
}
}
14、在工程 coupon-expired (模块)中,创建测试类 RedisTest.java
/**
* redis-coupon\coupon-expired2\src\test\java\cn\itcast\redis\RedisTest.java
*
* 2024-7-24 创建测试类 RedisTest.java
*/
package cn.itcast.redis;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext-data-redis.xml")
public class RedisTest {
private static Logger log = Logger.getLogger(RedisTest.class);
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Test
public void testSend() {
redisTemplate.opsForValue().set("itcast","very good!");
String value = redisTemplate.opsForValue().get("itcast");
log.info("从redis中获取的数据:"+value);
}
}
15、在工程 coupon-expired (模块)中,创建测试类 RedisTest2.java
*/
/**
* redis-coupon\coupon-expired2\src\test\java\cn\itcast\redis\RedisTest2.java
*
* 2024-7-24 创建测试类 RedisTest2.java
*/
package cn.itcast.redis;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RedisTest2 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
16、启动 redis 服务端和客户端,
1)在工程 coupon-expired (模块)中,运行 测试类 RedisTest2.java 进行监听。
2)在工程 coupon-achieve (模块)中,运行测试类 CouponTest.java 进行测试,
在数据库中查询到插入的记录,redis 客户端也查询到数据,但 1 分钟后失效。
同时,mysql 数据库中的状态,由 “0” 变为 “1”。
七、总结
1、- 对于过期代金卷,红包,订单解决方案的分析
2、- redis消息队列的介绍及入门
3、- redis整合SpringData redis开发
4、- 在Java程序中监听redis消息通知
5、- 结合redis的key失效机制和消息通知完成过期订单处理
上一节关联链接请点击
# redis 高级应用–使用 redis 消息队列完成秒杀过期订单处理(1)