目录:
1. SpringBoot集成Redis
2. 封装服务RedisService.java
3. 单元测试RedisServiceTest.java
4. Redis读写功能调用
5. Redis使用技巧:控制REST接口访问频率
6. 封装Annotation注解,灵活控制REST接口访问频率
7. 代码优化:ExceptionHandler全局处理异常
Redis是一个高性能的key-value数据库,常用于搭建缓存系统,提高并发响应速度。SpringBoot集成Redis只需简单配置,本文分享封装的RedisService服务,进一步介绍使用注解和Redis,灵活控制REST接口访问频率的实现方法。
典型的缓存系统数据读取流程:
项目代码:https://github.com/jextop/StarterApi/
示例代码:https://github.com/rickding/HelloJava/tree/master/HelloAnnotation
一,SpringBoot集成Redis
代码文件 |
功能要点 |
|
SpringBoot集成Redis |
pom.xml |
引入Redis依赖:spring-boot-starter-data-redis |
application.yml |
配置Redis服务器:host, port |
|
封装RedisService服务 |
RedisService.java |
封装Redis调用:RedisTemplate, ValueOperations, ListOperations, HashOperations, SetOperations |
单元测试 |
RedisServiceTest.java |
测试封装的Redis功能函数 |
Redis读写功能调用 |
CheckController.java |
增加REST接口/chk/cache,调用Redis读写功能 |
1. 新建SpringBoot项目时,选中Redis,将自动添加Redis依赖。
2. 已有SpringBoot项目,可以在pom.xml中直接引用Redis Starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3. 在application.yml中配置Redis服务器信息:
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
password:
timeout: 100
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 10
min-idle: 0
二,封装服务RedisService.java,调用Redis功能函数
1. 使用StringRedisTemplate对String类型的key和value操作
2. 使用ValueOperations<String, String>对String操作
a) setStr()
b) getStr()
3. ValueOperations<Object, Object>对Object操作
a) set()
b) get()
4. ListOperations<Object, Object>对列表操作
a) lSet()
b) lGet()
5. HashOperations<Object, Object, Object>对哈希表操作
a) hSet()
b) hGet()
6. SetOperations<Object, Object>对集合操作
a) sSet()
b) sGet()
三,单元测试RedisServiceTest.java
四,Redis读写功能调用
1. 增加RestController:CheckController.java
2. 增加REST接口/chk/cache,调用Redis读写功能
@GetMapping(value = "/chk/cache")
public Object cache(@RequestAttribute(required = false) String ip) {
// Get a unique key
String key = String.format("cache_test_%s_%s_缓存", ip, CodeUtil.getCode());
// Set cache
redisService.setStr(key, key, 3);
// Get cache
String str = redisService.getStr(key);
// Delete key
redisService.delStr(key);
return new HashMap<String, Object>() {{
put("chk", "cache");
put("msg", str);
put("status", key.equals(str));
}};
}
3. REST接口调用RedisService示例
五,Redis使用技巧:控制REST接口访问频率
1,功能设计:统计API在指定时间段内的访问次数,进行频率控制。
2,实现要点:将接口访问频率控制逻辑实现在解释器和注解中。
代码文件 |
功能 |
AccessInterceptor.java |
定义解释器 |
AccessLimitException.java |
声明访问频率异常 |
WebConfig.java |
注册解释器 |
AccessLimited.java |
定义注解@AccessLimited |
ExceptionController.java |
ExceptionHandler处理访问频率异常 |
代码下载:https://github.com/jextop/StarterApi/tree/master/src/main/java/com/starter/
├── interceptor/AccessInterceptor.java
├── exception/AccessLimitException.java
├── config/WebConfig.java
├── annotation/AccessLimited.java
├── controller/ExceptionController.java
3,处理流程:
- Redis统计指定时间段内的访问次数,根据HttpRequest生成key
- Interceptor解释器拦截,实现访问频率控制逻辑
- 抛出异常AccessLimitException
String key = String.format("%s_%s:%s",
request.getSession().getId(),
request.getMethod(),
request.getRequestURI()
);
try {
long count = redisService.incr(key);
if (count <= 5)) {
if (count == 1) {
redisService.expire(key, 1);
}
return true;
}
} catch (RedisConnectionFailureException e) {
return true;
}
throw new AccessLimitException();
六,封装Annotation注解,实现灵活的控制规则
1. 增加注解@AccessLimited
声明REST接口期望的访问频率控制规则:多长时间内允许访问多少次。然后更新代码,判断时使用accessLimited.count()和accessLimited.seconds()
@Order(Ordered.HIGHEST_PRECEDENCE)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimited {
int count() default 5;
int seconds() default 1;
}
2. REST接口引用@AccessLimited,代码优雅的只需一个注解。
3. 调用示例:
七,ExceptionHandler全局处理异常
异常发生时,返回给客户端不同的状态和信息。
l 处理AccessLimitException
l 处理Redis数据读取异常:PersistenceException, DataAccessException
@RestControllerAdvice
public class ExceptionController {
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
@ExceptionHandler(value = AccessLimitException.class)
public Object accessLimitExceptionHandler(AccessLimitException e) {
return new HashMap<String, Object>() {{
put("msg", e.getMessage());
}};
}
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
@ExceptionHandler(value = {PersistenceException.class, DataAccessException.class, MessagingException.class})
public Object dataExceptionHandler(RuntimeException e) {
return new HashMap<String, Object>() {{
put("msg", e.getMessage());
}};
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(value = Exception.class)
public Object exceptionHandler(Exception e) {
return new HashMap<String, Object>() {{
put("msg", e.getMessage());
}};
}
}