案例:1.互联网秒杀 2.抢优惠卷 3.接口幂
引入pom文件
<packaging>war</packaging><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.8.RELEASE</version></dependency><!-- 实现分布式锁的工具类 --><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.17.7</version></dependency><!-- Spring操作Redis的工具类 --><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.3.2.RELEASE</version></dependency><!-- Redis客户端 --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.9.0</version></dependency><!-- JSON解析工具 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.4</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><configuration><port>8001</port><!-- /表示访问应用的路径,省略项目名 --><path>/</path><!-- 配置编码方式为UTF-8 --><uriEncoding>UTF-8</uriEncoding></configuration><executions><execution><!-- 打包完成后,运行服务 --><phase>package</phase><goals><goal>run</goal></goals></execution></executions></plugin></plugins></build>
我们在pom文件中配置了打包后运行的jar包,所以我们要改变启动方式
在resources下创还能spring.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:context="http://www.springframework.org/schema/context"xsi:schemaLocation=" http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.leq"/><!-- 配置Redis连接工厂对象--><bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"><property name="hostName" value="192.168.40.100"></property><property name="port" value="6379"></property></bean><!-- Spring框架为了简化Redis操作提供了一个Redis操作的模板类:RedisTemplate--><bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"><!--ref表示该属性的取值是引用类型,将ref取值设置为外部bean的id值--><property name="connectionFactory" ref="connectionFactory"></property></bean></beans>
配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/spring.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 配置解决中⽂乱码问题。此过滤器会进⾏:request.setCharacterEncoding("utf-8") --><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping> </web-app>
抢购的业务:
@Autowired//简化Redis操作的模板类private StringRedisTemplate stringRedisTemplate;@RequestMapping("seckill")public synchronized String secKill(){String phone_count = stringRedisTemplate.opsForValue().get("phone_count");int phoneCount = Integer.parseInt(phone_count);if(phoneCount>0){//给redis中的数据添加锁 setnx() 在key不存在时,设置值phoneCount--;stringRedisTemplate.opsForValue().set("phone_count",String.valueOf(phoneCount));System.out.println("当前库存数量:"+phoneCount);}else {System.out.println("手机库存不足...");}return "当前的手机数量:"+phoneCount;}
在一次性涌入大量数据的时候可能会出现-1或者同票的情况,这个时候就需要加锁
//RedissonClient实现分布式锁@Autowiredprivate RedissonClient redisson;@RequestMapping("seckill")public synchronized String secKill(){//获取开发者自定义的锁RLock redissonLock = redisson.getLock("phone");//加锁,添加过期时间 1过期时间 2具体的单位 后面的代码都是受保护的redissonLock.lock(30, TimeUnit.SECONDS);Integer phoneCount = null;try {//高并发抢购业务String phone_count = stringRedisTemplate.opsForValue().get("phone_count");phoneCount = Integer.parseInt(phone_count);if(phoneCount>0){//给redis中的数据添加锁 setnx() 在key不存在时,设置值phoneCount--;stringRedisTemplate.opsForValue().set("phone_count",String.valueOf(phoneCount));System.out.println("当前库存数量-1:"+phoneCount);}else {System.out.println("手机库存不足...");}}catch (Exception e){System.out.println("有异常");}finally {redissonLock.unlock();//释放锁 释放后代码不受保护}return "当前的手机数量:"+phoneCount;}
模拟一次性涌入大量数据:
1.打开ApiPost测试工具
2.新建一个接口,访问刚刚写的seckil接口,点击保存
3.保存后点击自动化测试,新建一个测试用例,点击最左边的HTTP请求接口,选择刚刚建的接口,在上面可以选择次数和时间,然后点击执行即可
4.执行的结果如下,库存的数量需要我们自己手动在redis库中进行设置 :set phone_count 10
解决高并发就需要一个配置类RedissonConfig
//分布式锁的配置类import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //分布式锁配置类 @Configuration public class RedissonConfig {//1.Redis单机模式下@Bean //RedissonClient 是Redisson客户端对象,使用该对象来添加锁public RedissonClient singletonModelRedisson(){Config config =new Config();//构建Redis结构的配置// Redis数据库默认放在字库1中,对应的下标为0config.useSingleServer().setAddress("redis://192.168.40.100:6379").setDatabase(0);return Redisson.create(config);}/* // 2.Redis集群@Beanpublic RedissonClient clusterModelRedisson() {Config config =new Config();//构建Redis结构的配置config.useClusterServers().setScanInterval(2000)//集群的扫描时间.addNodeAddress("redis://192.168.40.100:6379","redis://192.168.40.101:6379","redis://192.168.40.102:6379");return Redisson.create(config);}//3.Redis集群:主从架构@Beanpublic RedissonClient masterModelRedisson() {Config config =new Config();//构建Redis结构的配置config.useMasterSlaveServers().setMasterAddress("redis://192.168.40.100:6379").addSlaveAddress("redis://192.168.40.101:6379","redis://192.168.40.102:6379");return Redisson.create(config);}@Bean//4Redis集群:哨兵模式public RedissonClient sentinelModelRedisson() {Config config =new Config();//构建Redis结构的配置config.useSentinelServers().addSentinelAddress("redis://192.168.40.101:6379","redis://192.168.40.102:6379","redis://192.168.40.100:6379");return Redisson.create(config);} */ }