java秒殺之redis限流操作詳解
最近寫到了一個(gè)秒殺的功能模塊,為了保證高并發(fā)情況下不會(huì)宕機(jī),要從多方面去考慮,當(dāng)前的限流操作只是其中的一個(gè)方面,具體操作如下。
導(dǎo)入所需依賴
<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)//運(yùn)行時(shí)有效
@Target(ElementType.METHOD)//用在方法上
public @interface AccessLimit {
int seconds();//時(shí)間范圍(單位:秒)
int maxCount();//在這個(gè)時(shí)間范圍內(nèi)最大訪問次數(shù)
}
編寫攔截器
public class AcessLimiitInterceptor implements HandlerInterceptor {
//注入redisTemplate
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//設(shè)置redisTemplate的序列化方式(必須設(shè)置為這種方式,因?yàn)橐玫絠ncr)
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;
}
//獲取注解的參數(shù)值
int seconds=accessLimit.seconds();//時(shí)間范圍
int maxCount=accessLimit.maxCount();//時(shí)間范圍內(nèi)的最大訪問次數(shù)
//該請(qǐng)求的路徑
String key=request.getRequestURI();
//在該時(shí)間范圍內(nèi)已經(jīng)訪問的次數(shù)
String countStr=redisTemplate.opsForValue().get(key);
Integer count=null;
//如果不是第一次訪問,則把訪問次數(shù)轉(zhuǎn)換為integer類型
if(countStr!=null){
count= Integer.valueOf(redisTemplate.opsForValue().get(key));
}
//拿到訪問次數(shù)的過期時(shí)間
Long keySeconds=redisTemplate.getExpire(key);
//該時(shí)間范圍內(nèi)沒有訪問
if(count==null){
//第一次訪問,設(shè)置key為訪問路徑,值為訪問次數(shù)1
redisTemplate.opsForValue().set(key,1+"");
//設(shè)置過期時(shí)間
redisTemplate.expire(key,600, TimeUnit.SECONDS);
}else if(count<maxCount){//在該時(shí)間范圍內(nèi)已經(jīng)有訪問記錄,但訪問沒有達(dá)到最大次數(shù)
//訪問次數(shù)+1
redisTemplate.opsForValue().increment(key,1);
//設(shè)置剩余過期時(shí)間(修改完該key的value值后,對(duì)應(yīng)的過期時(shí)間會(huì)失效,需重新設(shè)置)
redisTemplate.expire(key,keySeconds, TimeUnit.SECONDS);
}else{//在該時(shí)間范圍內(nèi)已經(jīng)超過最大的訪問次數(shù)
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框架聲明式事務(wù)管理-->
<!--配置事務(wù)管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事務(wù)通知-->
<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增強(qiáng)-->
<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 相關(guān)配置 -->
<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的監(jiān)聽器,默認(rèn)只加載WEB-INF目錄下的applicationContext.xml配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--設(shè)置配置文件的路徑-->
<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>
<!--啟動(dòng)服務(wù)器,創(chuàng)建該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>
注解應(yīng)用
@AccessLimit(seconds = 500,maxCount = 3)
@RequestMapping(value = "/findAll")
public String findAll(Model model){
System.out.println("csl");
return "list";
}
總 結(jié)
以上操作就完成了java后臺(tái)使用redisTemplate的限流操作,這里還需各位自己開啟一個(gè)redis服務(wù)端并且把配置文件中的地址改成對(duì)應(yīng)的ip,希望以上內(nèi)容對(duì)大家有幫助,多提寶貴意見。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中ReentrantLock和ReentrantReadWriteLock的原理
這篇文章主要介紹了Java中ReentrantLock和ReentrantReadWriteLock的原理,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-09-09
圖解Eclipse j2ee開發(fā)環(huán)境的搭建過程
這篇文章以圖文結(jié)合的方式介紹了Eclipse j2ee開發(fā)環(huán)境的搭建過程,內(nèi)容很詳細(xì),每一個(gè)步驟都有對(duì)應(yīng)的操作截圖,需要的朋友可以參考下2015-08-08
MyBatis-Plus如何使用枚舉自動(dòng)關(guān)聯(lián)注入詳解
這篇文章主要給大家介紹了關(guān)于MyBatis-Plus如何使用枚舉自動(dòng)關(guān)聯(lián)注入的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用MyBatis-Plus具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03
SpringBoot項(xiàng)目中使用騰訊云發(fā)送短信的實(shí)現(xiàn)
本文主要介紹了SpringBoot項(xiàng)目中使用騰訊云發(fā)送短信的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
詳解SpringCloud服務(wù)認(rèn)證(JWT)
本篇文章主要介紹了SpringCloud服務(wù)認(rèn)證(JWT),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01
SpringBoot整合Kaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能
這篇文章主要介紹了SpringBoot整合Kaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
springboot+vue實(shí)現(xiàn)登錄功能
這篇文章主要為大家詳細(xì)介紹了springboot+vue實(shí)現(xiàn)登錄功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
IntelliJ IDEA配置Tomcat(完整版圖文教程)
這篇文章主要介紹了IntelliJ IDEA配置Tomcat(完整版圖文教程),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05

