19、秒杀子模块搭建、商家维护秒杀商品列表实现、springTesk的demo、定时任务将秒杀添加到redis、商品从redis中获取、前端秒杀倒计时、秒杀下单保存到数据库、子线程保存数据库、超卖问题

秒杀子模块搭建

在这里插入图片描述
搭建interface,不使用骨架
略。
搭建service,使用骨架
1、在resources中添加配置文件
applicationContext-service.xml
在这里插入图片描述
applicationContext-service.xml全文如下:(包括dubbo配置,线程池配置和)

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 访问dubbo要占用的当前主机端口,默认端口不写是20880 -->
	<dubbo:protocol name="dubbo" port="20889"></dubbo:protocol>
	<dubbo:application name="myApplication-seckill-service" />
	<dubbo:registry address="zookeeper://192.168.25.128:2181" />
	<dubbo:annotation package="com.myApplication.seckill.service.impl" />

	<!-- 线程池配置 -->
	<bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<!-- 核心线程数,默认为1 -->
		<property name="corePoolSize" value="10" />
		<!--最大线程数,默认为Integer.MAX_VALUE -->
		<property name="maxPoolSize" value="50" />
		<!--队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
		<property name="queueCapacity" value="10000" />
		<!--线程池维护线程所允许的空闲时间,默认为60s -->
		<property name="keepAliveSeconds" value="300" />
		<!--线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 
		1. CallerRunsPolicy :这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
		2. AbortPolicy :对拒绝任务抛弃处理,并且抛出异常。
		-->
		<property name="rejectedExecutionHandler">
			<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
		</property>
	</bean>


	<!--雪花算法,解决多表之间的id问题,是自增唯一,但不是连续-->
	<!--后边的两个value,第一个表示第几个数据中心,第二个表示第几台工作机器-->
	<bean id="idWorker" class="util.IdWorker">
		<!-- 工作机器ID:值范围是0-31  数据中心ID:值范围是0-31,两个参数可以不写 -->
		<constructor-arg index="0" value="1"></constructor-arg>
		<constructor-arg index="1" value="0"></constructor-arg>
	</bean>

</beans>

2、在WEB-INF中添加web.xml配置文件web.xml
在这里插入图片描述
web.xml全文如下:(配置文件加载、监听器)

注意:在加载配置文件时,classpath*代表将依赖中的对应配置文件也加载;

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">	
	
	<!-- 加载spring容器 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:spring/applicationContext*.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	
</web-app>

3、在pom文件中,添加tomcat设置。

 <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <!-- 指定端口 -->
                    <port>9009</port>
                    <!-- 请求路径 -->
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>

注意bug:
如果在seckillOrderServiceImpl启动时出现:
java.io.IOException: invalid constant type: 15
解决办法:
在本类的pom文件中添加依赖:

org.javassist
javassist
3.18.2-GA

搭建web,使用骨架
1、在webapp中引入静态原型
在这里插入图片描述
2、在web.xml中添加配置文件扫描的设置
解决post乱码问题。
servlet核心控制器
security和过滤器链
全文如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<!-- 解决post乱码 -->
	<filter>
		<filter-name>CharacterEncodingFilter</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>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/springmvc.xml</param-value>
		</init-param>
	</servlet>

	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/spring-security.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<welcome-file-list>
		<welcome-file>seckill-index.html</welcome-file>
	</welcome-file-list>

</web-app>

3、在resources中添加配置文件spring-security.xml和springmvc.xml
spring-security.xml中配置的是security框架和cas单点登录配置。在springmvc中配置的是dubbo服务的引用,和mvc注解支持。
spring-security.xml全文如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" 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.xsd
						http://www.springframework.org/schema/security 
						http://www.springframework.org/schema/security/spring-security.xsd">
	
	<http pattern="/css/**" security="none"></http>
	<http pattern="/js/**" security="none"></http>
	<http pattern="/img/**" security="none"></http>
	<http pattern="/plugins/**" security="none"></http>
	<http pattern="/seckill*.html" security="none"></http>	
	<!-- 添加购物车可以不用登录,存到cookie中 -->
	
	<!--   entry-point-ref  入口点引用 -->
	<http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint"> 
		<intercept-url pattern="/seckillOrder/add.do" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <intercept-url pattern="/seckillGoods/findOne.do" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <intercept-url pattern="/seckillGoods/findSeckillList.do" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <intercept-url pattern="/**" access="ROLE_USER"/>   
        <csrf disabled="true"/>  
        <!-- custom-filter为过滤器, position 表示将过滤器放在指定的位置上,before表示放在指定位置之前  ,after表示放在指定的位置之后  -->           
        <custom-filter ref="casAuthenticationFilter"  position="CAS_FILTER" />      
        <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>  
        <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>  
    </http>
    
  	<!-- CAS入口点 开始 -->
    <beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">  
        <!-- cas登录服务器登录URL -->  
        <beans:property name="loginUrl" value="http://localhost:8080/cas/login"/>
        <beans:property name="serviceProperties" ref="serviceProperties"/>  
    </beans:bean>      
    <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">  
        <!--service 配置自身工程的根地址+/login/cas   -->  
        <beans:property name="service" value="http://localhost:9109/login/cas"/>
    </beans:bean>  
    <!-- CAS入口点 结束 -->

    
    <!-- 认证过滤器 开始 -->
    <beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">  
        <beans:property name="authenticationManager" ref="authenticationManager"/>  
    </beans:bean>  
		<!-- 认证管理器 -->
	<authentication-manager alias="authenticationManager">
		<authentication-provider  ref="casAuthenticationProvider">
		</authentication-provider>
	</authentication-manager>
		<!-- 认证提供者 -->
	<beans:bean id="casAuthenticationProvider"     class="org.springframework.security.cas.authentication.CasAuthenticationProvider">  
        <beans:property name="authenticationUserDetailsService">  
            <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">  
                <beans:constructor-arg ref="userDetailService" />  
            </beans:bean>  
        </beans:property>  
        <beans:property name="serviceProperties" ref="serviceProperties"/>  
        <!-- ticketValidator 为票据验证器 -->
        <beans:property name="ticketValidator">  
            <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">  
                <!-- 设置cas票据验证地址 -->
                <beans:constructor-arg index="0" value="http://localhost:8080/cas"/>
            </beans:bean>  
        </beans:property>  
        <beans:property name="key" value="an_id_for_this_auth_provider_only"/> 
    </beans:bean>        
     <!-- springScurity认证类 -->
	<beans:bean id="userDetailService" class="com.pinyougou.user.service.UserDetailServiceImpl"/>  
	
	<!-- 认证过滤器 结束 -->
	
	
	<!-- 单点登出  开始  -->     
    <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>          
    <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">  
        <beans:constructor-arg value="http://localhost:8080/cas/logout?service=http://localhost:9109/"/>
        <beans:constructor-arg>  
            <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>  
        </beans:constructor-arg> 
        <!-- 将本地/logout/cas和 64行退出后的部分进行绑定 -->     
        <beans:property name="filterProcessesUrl" value="/logout/cas"/>  
    </beans:bean>  
    <!-- 单点登出  结束 -->  
	
</beans:beans>

springmvc.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<mvc:annotation-driven>
		<mvc:message-converters register-defaults="true">
			<bean
				class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
				<property name="supportedMediaTypes" value="application/json" />
				<property name="features">
					<array>
						<value>WriteMapNullValue</value>
						<value>WriteDateUseDateFormat</value>
					</array>
				</property>
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>

	<!-- 引用dubbo 服务 -->
	<dubbo:application name="pinyougou-seckill-web" />
	<dubbo:registry address="zookeeper://192.168.25.128:2181" />
	<dubbo:annotation package="com.pinyougou.seckill.controller" />

</beans>

商家维护秒杀商品列表实现

思路说明:
前端需要后端返回数据,显示在商家后台(shop-web)的秒杀商品管理中。
在商家后台管理shop-web模块中引用search.do的查询方法,在后端控制器controller中改造search方法,传入sellerid(商家名称),sellerid通过springsecurity来获取。在实现类中的对应search的方法中,将seckillGoods的sellerId设置进去。然后将模糊查询条件改为准确查询(去掉两个百分号)。这样就实现了查询当前商家下的所有秒杀商品列表。

springTesk的demo

定时任务demo演示。
在这里插入图片描述
1、在pom文件中设置tomcat。

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <!-- 指定端口 -->
                    <port>9010</port>
                    <!-- 请求路径 -->
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>

2、在resources配置文件中添加配置文件
在这里插入图片描述
applicationContext-task.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	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/context 
       				 	http://www.springframework.org/schema/context/spring-context.xsd
        				http://www.springframework.org/schema/task
        				http://www.springframework.org/schema/task/spring-task.xsd">

	 <!-- 包扫描 -->
     <context:component-scan base-package="com.pinyougou.seckill.task"></context:component-scan>
     <!-- 开启注解驱动 -->
     <task:annotation-driven/>
</beans>

3、在web.xml文件中配置了加载配置文件来生成spring容器的配置和监听器。
在这里插入图片描述
注意:在classpath后有*号,代表将pom文件中的所有依赖中的配置文件都加载。

4、在实现类中创建方法,使用Scheduled注解,cron表达式也叫七子域表达式
在这里插入图片描述

定时任务将秒杀添加到redis

@Component
public class SeckillTask {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TbSeckillGoodsMapper seckillGoodsMapper;

    /**
     * 定时查询数据库将秒杀商品添加到redis中
     * Scheduled定时任务 cron表达式(七子域表达式)
     * <p>
     * 秒 分 时 日 月 周   (quartz中有 年 域)
     * <p>
     * quartz也可以省略年 写六位
     */
    @Scheduled(cron = "0/3 * * * * ?")
    public void seckillGoodsToRedis() {
       /* SimpleDateFormat simpleDateFormat  = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
        System.out.println(simpleDateFormat.format(new Date()));*/

        //定时查询数据库中符合条件的商品加入数据库中
       // String name = SecurityContextHolder.getContext().getAuthentication().getName();
        TbSeckillGoodsExample example = new TbSeckillGoodsExample();
        example.createCriteria().andEndTimeGreaterThan(new Date())
                .andStartTimeLessThanOrEqualTo(new Date())
                .andStatusEqualTo("1")
                .andStockCountGreaterThanOrEqualTo(0)
                ;

        List<TbSeckillGoods> seckillGoods = seckillGoodsMapper.selectByExample(example);
        //将所有商品加入到redis中
        for (TbSeckillGoods seckillGood : seckillGoods) {

            System.out.println("存入redis的商品是:"+seckillGood.getId());

            redisTemplate.boundHashOps("seckill_goods").put(seckillGood.getId(),seckillGood);

            System.out.println("从redis取出的商品是:"+((TbSeckillGoods)redisTemplate.boundHashOps("seckill_goods").get(seckillGood.getId())).getId());

            //为了解决超卖问题,将每个商品添加到redis的list中(让什么无所谓,可以是id,我们只要判断不是null即可)
            for (Integer i = 0; i < seckillGood.getStockCount(); i++) {
                redisTemplate.boundListOps("seckill_goods_queue_"+seckillGood.getId()).leftPush(seckillGood.getId());
            }
        }
        System.out.println("=====导入商品数:"+seckillGoods.size());
    }
}

商品从redis中获取显示在页面上

1、在seckill-web的controller中添加查询方法findSeckillList
在这里插入图片描述
2、在对应的impl实现类实现方法,这里需要获取所有商家的所有秒杀商品显示在页面上,所以获取values即可。
在这里插入图片描述

前端秒杀倒计时

app.controller('seckillController',function($scope,$location,seckillService,$interval){
	
	$scope.findSeckillList = function(){
		seckillService.findSeckillList().success(
				function(response){
					$scope.seckillList = response;
				}
		)
	}
	
	$scope.findOne = function(){
		var id = $location.search()['goodsId'];
		if(id!=null){
			seckillService.findOne(id).success(
					function(response){
						$scope.entity = response;
						
						 //计算出剩余时间
			            var endTime = new Date($scope.entity.endTime).getTime();
			            var nowTime = new Date().getTime();

			            //剩余时间
			            $scope.secondes =Math.floor( (endTime-nowTime)/1000 );

			            var time =$interval(function () {
			                if($scope.secondes>0){
			                    //时间递减
			                    $scope.secondes--;
			                    //时间格式化
			                    $scope.timeString=convertTimeString($scope.secondes);
			                }else{
			                    //结束时间递减
			                    $interval.cancel(time);
			                }
			            },1000);
					}
			)
		}
		
	}
	
	//距离结束时间   
	convertTimeString=function (allseconds) {
	    //计算天数(所有秒数除每天的秒数) 通过Math.floor向下取整
	    var days = Math.floor(allseconds/(60*60*24));

	    //小时      (所有秒数- 天数的所有秒数,剩下的秒数除以 一小时有多少秒) 通过Math.floor向下取整
	    var hours =Math.floor( (allseconds-(days*60*60*24))/(60*60) );

	    //分钟     (所有秒数 - 天数的秒数 - 小时的秒数 ,最后的秒数除以 一分钟有多少秒)
	    var minutes = Math.floor( (allseconds-(days*60*60*24)-(hours*60*60))/60 );

	    //秒
	    var seconds = allseconds-(days*60*60*24)-(hours*60*60)-(minutes*60);

	    //拼接时间
	    var timString="";
	    if(days>0){
	        timString=days+"天:";
	    }
	    if(hours<10){
	    	hours = "0" + hours;
	    }
	    if(minutes<10){
	    	minutes = "0" + minutes;
	    }
	    if(seconds<10){
	    	seconds = "0" + seconds;
	    }
	    
	    return timString+=hours+":"+minutes+":"+seconds;
	}
	
	//秒杀下单saveSeckillOrder
	$scope.saveSeckillOrder = function(){
		//保存订单后->跳转到支付
		seckillService.saveSeckillOrder($scope.entity.id).success(
				function(response){
					if(response.success){
						alert("下单成功,进行支付操作")
					}else{
						alert(response.message);
					}
				}
		)
	}
})

秒杀下单保存到数据库

秒杀模块并发量肯定会非常高,所以,在处理秒杀下单操作时,不能在本类中insert操作。需要开子线程,在子线程中保存数据库。
1、在创建添加新的订单时,需要传入一个参数,即具体商品的id(即sku的id,即生成的秒杀商品的自增id(数据库中有,第一列),注意不是goodsId)

一会还会修改这个方法传入的参数,需要添加一个userId,后边会说到。
(粘贴的是添加userId后的方法)
在这里插入图片描述
2、需要根据传入的id,从redis中的商品数量-1,然后再存入到redis中。
在这里插入图片描述
在这里插入图片描述
3、根据从redis中取出的goods来创建order订单。(id使用了雪花算法)
在这里插入图片描述
4、为了保证秒杀执行效率,所以不在本类中进行插入操作。
在redis中新建list对象,通过leftPush和在子线程中的rightPop顺次取出order对象,保存数据库。
先将子线程类引入,将线程池ThreadPoolTaskExecutor引入
在这里插入图片描述
在这里插入图片描述

子线程保存数据库

在这个service实现类中创建子线程类,实现Runnable接口


@Component
public class CreateOrder implements Runnable {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TbSeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private TbSeckillOrderMapper seckillOrderMapper;

    @Override
    public void run() {
        //从redis中取出order
        TbSeckillOrder order = (TbSeckillOrder) redisTemplate.boundListOps("seckill_order").rightPop();
        //保存订单
        seckillOrderMapper.insert(order);

        //将seckill_goods的库存量-1 保存到数据库(在主线程中的-1操作是操作的redis中的库存量)
        TbSeckillGoods goods = seckillGoodsMapper.selectByPrimaryKey(order.getSeckillId());
        goods.setStockCount(goods.getStockCount()-1);
        //更新goods信息
        seckillGoodsMapper.updateByPrimaryKey(goods);
    }
}

超卖问题

1、在redis存入秒杀商品时,根据id将所有的商品每一个都添加到redis中的list中,存入的数据是什么无所谓,这里传入的是seckillgoodsId。
(具体代码在上边写了)
2、在创建订单的seckillOrderServiceImpl类中,根据id取出每一个存入的商品数据,只要判断不为空即可,代表还有商品。
在这里插入图片描述

抢购过商品之后的用户不可以重复抢购

如果redis中的商品数量-1时代表已经购买成功,将此时的用户id存入redis的set(不可重复)中。然后在本类的最上边(在redis弹出商品之前)进行判断,如果已经存在该用户的id,抛出已经购买的异常。
在这里插入图片描述

在这里插入图片描述

验证秒杀订单保存数据库减少秒杀商品数量的演示

如果从redis中取出的goods是null或者goods的库存数小于1,抛出异常:秒杀活动结束。
在这里插入图片描述
如果商品的库存数位0,从redis中移除商品
在这里插入图片描述

在controller中,判断是否登录,如果未登录,提示去登录。

注意:在这里需要在security配置文件中注意不能将所有的方法放行,将需要的方法放行即可。我们需要让security拦截一部分,让我们登录。
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现Redis商品进行秒杀,需要考虑以下几个步骤: 1. 连接Redis数据库 2. 初始化商品库存和秒杀活动时间 3. 用户秒杀请求处理 4. 商品库存更新 下面是一个简单的Python代码实现: ```python import redis import time # 连接Redis数据库 r = redis.StrictRedis(host='localhost', port=6379, db=0) # 初始化商品库存和秒杀活动时间 r.set('product_stock', 10) r.set('seckill_start_time', '2021-05-01 00:00:00') r.set('seckill_end_time', '2021-05-01 23:59:59') # 用户秒杀请求处理 def handle_seckill_request(user_id): # 判断秒杀活动是否开始 seckill_start_time = time.mktime(time.strptime(r.get('seckill_start_time'), '%Y-%m-%d %H:%M:%S')) if time.time() < seckill_start_time: return '秒杀活动还未开始' # 判断秒杀活动是否结束 seckill_end_time = time.mktime(time.strptime(r.get('seckill_end_time'), '%Y-%m-%d %H:%M:%S')) if time.time() > seckill_end_time: return '秒杀活动已结束' # 判断用户是否已经秒杀过 seckill_record_key = 'seckill_record:' + str(user_id) if r.get(seckill_record_key) is not None: return '您已经参与过秒杀活动' # 判断商品库存是否足够 product_stock = int(r.get('product_stock')) if product_stock <= 0: return '商品已售罄' # 商品库存更新 with r.pipeline() as pipe: while True: try: pipe.watch('product_stock') product_stock = int(pipe.get('product_stock')) if product_stock <= 0: return '商品已售罄' pipe.multi() pipe.decr('product_stock') pipe.set(seckill_record_key, 1) pipe.execute() return '秒杀成功' except redis.WatchError: continue ``` 这个代码,我们使用Redis的`watch`命令实现了乐观锁,在多个用户同时秒杀同一件商品时,能够保证库存减少的正确性。同时,我们使用了Redis的事务处理机制,保证了商品库存和秒杀记录的原性更新。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值