软件编程
位置:首页>> 软件编程>> java编程>> java秒杀之redis限流操作详解

java秒杀之redis限流操作详解

作者:四颗石头  发布时间:2022-07-08 09:26:57 

标签:java,秒杀,限流

最近写到了一个秒杀的功能模块,为了保证高并 * 况下不会宕机,要从多方面去考虑,当前的限流操作只是其中的一个方面,具体操作如下。

导入所需依赖


<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <maven.compiler.source>1.8</maven.compiler.source>
   <maven.compiler.target>1.8</maven.compiler.target>
   <spring.version>5.0.2.RELEASE</spring.version>
   <slf4j.version>1.6.6</slf4j.version>
   <log4j.version>1.2.12</log4j.version>
   <mysql.version>5.1.6</mysql.version>
   <mybatis.version>3.4.5</mybatis.version>
 </properties>

<dependencies>

<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-web</artifactId>
     <version>${spring.version}</version>
   </dependency>

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>${spring.version}</version>
   </dependency>

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-test</artifactId>
     <version>${spring.version}</version>
   </dependency>

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-tx</artifactId>
     <version>${spring.version}</version>
   </dependency>

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>${spring.version}</version>
   </dependency>

<dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.12</version>
     <scope>compile</scope>
   </dependency>

<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>${mysql.version}</version>
   </dependency>

<dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>servlet-api</artifactId>
     <version>2.5</version>
     <scope>provided</scope>
   </dependency>

<dependency>
     <groupId>javax.servlet.jsp</groupId>
     <artifactId>jsp-api</artifactId>
     <version>2.0</version>
     <scope>provided</scope>
   </dependency>

<dependency>
     <groupId>jstl</groupId>
     <artifactId>jstl</artifactId>
     <version>1.2</version>
   </dependency>

<!-- log start -->
   <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>

<!-- log end -->
   <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis</artifactId>
     <version>${mybatis.version}</version>
   </dependency>

<dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis-spring</artifactId>
     <version>1.3.0</version>
   </dependency>

<dependency>
     <groupId>c3p0</groupId>
     <artifactId>c3p0</artifactId>
     <version>0.9.1.2</version>
     <type>jar</type>
     <scope>compile</scope>
   </dependency>

<dependency>
     <groupId>org.springframework.data</groupId>
     <artifactId>spring-data-redis</artifactId>
     <version>1.7.2.RELEASE</version>
   </dependency>

<dependency>
     <groupId>redis.clients</groupId>
     <artifactId>jedis</artifactId>
     <version>2.8.1</version>
   </dependency>
</dependencies>

编写注解


@Retention(RUNTIME)//运行时有效
@Target(ElementType.METHOD)//用在方法上
public @interface AccessLimit {
    int seconds();//时间范围(单位:秒)
    int maxCount();//在这个时间范围内最大访问次数
}

编写 *


public class AcessLimiitInterceptor implements HandlerInterceptor {
//注入redisTemplate
   @Autowired
   private RedisTemplate<String,String> redisTemplate;

@Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    //设置redisTemplate的序列化方式(必须设置为这种方式,因为要用到incr)
       redisTemplate.setKeySerializer(new StringRedisSerializer());
       redisTemplate.setValueSerializer(new StringRedisSerializer());

if(handler instanceof HandlerMethod){
        //查看该方法上是否有@AcessLimit注解
           HandlerMethod hm= (HandlerMethod) handler;
           AccessLimit accessLimit=hm.getMethodAnnotation(AccessLimit.class);
           //没有@AcessLimit注解,证明无限流操作,直接放行
           if(accessLimit==null){
               return true;
           }
            //获取注解的参数值
           int seconds=accessLimit.seconds();//时间范围
           int maxCount=accessLimit.maxCount();//时间范围内的最大访问次数
           //该请求的路径
           String key=request.getRequestURI();
           //在该时间范围内已经访问的次数
           String countStr=redisTemplate.opsForValue().get(key);
           Integer count=null;
           //如果不是第一次访问,则把访问次数转换为integer类型
           if(countStr!=null){
               count= Integer.valueOf(redisTemplate.opsForValue().get(key));
           }
  //拿到访问次数的过期时间
           Long keySeconds=redisTemplate.getExpire(key);
           //该时间范围内没有访问
           if(count==null){
            //第一次访问,设置key为访问路径,值为访问次数1
               redisTemplate.opsForValue().set(key,1+"");
               //设置过期时间
               redisTemplate.expire(key,600, TimeUnit.SECONDS);
           }else if(count<maxCount){//在该时间范围内已经有访问记录,但访问没有达到最大次数
            //访问次数+1
               redisTemplate.opsForValue().increment(key,1);
               //设置剩余过期时间(修改完该key的value值后,对应的过期时间会失效,需重新设置)
               redisTemplate.expire(key,keySeconds, TimeUnit.SECONDS);
           }else{//在该时间范围内已经超过最大的访问次数
               return false;
           }
       }
       //放行
       return true;
   }
}

编写application.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"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns:tx="http://www.springframework.org/schema/tx"
      xmlns:p="http://www.springframework.org/schema/p"
      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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

<!--开启注解的扫描,希望处理service和dao,controller不需要Spring框架去处理-->
   <context:component-scan base-package="cn.itcast" >
       <!--配置哪些注解不扫描-->
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
   </context:component-scan>
   <!--Spring整合MyBatis框架-->
   <!--配置连接池-->
   <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="driverClass" value="com.mysql.jdbc.Driver"/>
       <property name="jdbcUrl" value="jdbc:mysql:///ssm"/>
       <property name="user" value="root"/>
       <property name="password" value="root"/>
   </bean>

<!--配置SqlSessionFactory工厂-->
   <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
       <property name="dataSource" ref="dataSource" />
   </bean>

<!--配置AccountDao接口所在包-->
   <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="cn.itcast.dao"/>
   </bean>

<!--配置Spring框架声明式事务管理-->
   <!--配置事务管理器-->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
   </bean>

<!--配置事务通知-->
   <tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
           <tx:method name="find*" read-only="true"/>
           <tx:method name="*" isolation="DEFAULT"/>
       </tx:attributes>
   </tx:advice>

<!--配置AOP增强-->
   <aop:config>
       <aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.service.impl.*ServiceImpl.*(..))"/>
   </aop:config>

<context:property-placeholder location="classpath*:*.properties"></context:property-placeholder>
   <!-- <context:property-placeholder location="classpath*:properties/*.properties" />   -->

<!-- redis 相关配置 -->
   <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
       <property name="maxIdle" value="${redis.maxIdle}" />
       <property name="maxWaitMillis" value="${redis.maxWait}" />
       <property name="testOnBorrow" value="${redis.testOnBorrow}" />
   </bean>

<bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
         p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
       <property name="connectionFactory" ref="JedisConnectionFactory" />
   </bean>

</beans>

配置web.xml


<web-app>
 <display-name>Archetype Created Web Application</display-name>

<!--配置Spring的 * ,默认只加载WEB-INF目录下的applicationContext.xml配置文件-->
 <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <!--设置配置文件的路径-->
 <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:applicationContext.xml</param-value>
 </context-param>

<!--配置前端控制器-->
 <servlet>
   <servlet-name>dispatcherServlet</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <!--加载springmvc.xml配置文件-->
   <init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>classpath:springmvc.xml</param-value>
   </init-param>
   <!--启动服务器,创建该servlet-->
   <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
   <servlet-name>dispatcherServlet</servlet-name>
   <url-pattern>/</url-pattern>
 </servlet-mapping>

<!--解决中文乱码的过滤器-->
 <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>
 </filter>
 <filter-mapping>
   <filter-name>characterEncodingFilter</filter-name>
   <url-pattern>/*</url-pattern>
 </filter-mapping>

</web-app>

注解应用


@AccessLimit(seconds = 500,maxCount = 3)
   @RequestMapping(value = "/findAll")
   public String findAll(Model model){
       System.out.println("csl");
       return "list";
   }

总 结

以上操作就完成了java后台使用redisTemplate的限流操作,这里还需各位自己开启一个redis服务端并且把配置文件中的地址改成对应的ip,希望以上内容对大家有帮助,多提宝贵意见。

来源:https://blog.csdn.net/qq_35375529/article/details/90295311

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com