需要回顾的技术:
redis热点缓存技术
springboot拦截器interceptor
自定义注解
解决问题,由于客户对查询的频繁操作,可以将查询的部分请求后的结果存入redis中,第二次查询是,就直接从redis中获取,减少sql的压力,从而提升性能.
配置类:
#是否开启数据缓存
tanhua.cache.enable=false
# Redis相关配置
spring.redis.jedis.pool.max-wait = 5000ms
spring.redis.jedis.pool.max-Idle = 100
spring.redis.jedis.pool.min-Idle = 10
spring.redis.timeout = 10s
spring.redis.cluster.nodes = 192.168.21.31:6379,192.168.81.85:6380,192.168.81.41:6381
spring.redis.cluster.max-redirects=5
配置类:
/** * 请求的拦截路径 */ @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private RedisCacheInterceptor redisCacheInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(this.redisCacheInterceptor).addPathPatterns("/**"); } }
采用拦截器进行缓存命中
package com.tanhua.server.interceptor; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.tanhua.common.utils.Cache; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; /** * 请求拦截器 */ @Component public class RedisCacheInterceptor implements HandlerInterceptor { @Value("${tanhua.cache.enable}") private boolean enable; private static final ObjectMapper mapper = new ObjectMapper(); @Autowired private RedisTemplate<String, String> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //开启全局缓存 if (!enable) { return true; } //controller中不是handlerMethod方法 if (!(handler instanceof HandlerMethod)) { return true; } //controller中的方法这不是get方法 if (!((HandlerMethod) handler).hasMethodAnnotation(GetMapping.class)) { return true; } //判断是否加了自定义注解Cache if (!((HandlerMethod) handler).hasMethodAnnotation(Cache.class)) { return true; } //命中缓存 String redisKey = createRedisKey(request); String CacheValue = this.redisTemplate.opsForValue().get(redisKey); if (StringUtils.isNotEmpty(CacheValue)) { //缓存未命中 return true; } //命中.响应出去 response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.getWriter().write(CacheValue); return false; } public static String createRedisKey(HttpServletRequest request) throws JsonProcessingException { //获取方法路径名 String url = request.getRequestURI(); //获取前端发过来的参数 Map<String, String[]> parameterMap = request.getParameterMap(); //转为json String params = mapper.writeValueAsString(parameterMap.getClass()); //token String token = request.getHeader("Authorization"); //拼接 String data = url + "_" + params + "_" + token; //转为md5处理 return "SERVER_CACHE_DATA_" + DigestUtils.md5Hex(data); } }
响应结果写入到缓存
package com.tanhua.server.interceptor; import com.fasterxml.jackson.databind.ObjectMapper; import com.tanhua.common.utils.Cache; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.MethodParameter; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import java.util.concurrent.TimeUnit; @ControllerAdvice //该类是个通知类 public class MyResponseBodyAdvice implements ResponseBodyAdvice { @Value("tanhua.cache.enable") private boolean enable; private static final ObjectMapper MAPPER = new ObjectMapper(); @Autowired private RedisTemplate<String, String> redisTemplate; @Override public boolean supports(MethodParameter methodParameter, Class aClass) { //开关为true 是getMapping请求 包含Cache注解 return enable && methodParameter.hasMethodAnnotation(GetMapping.class) && methodParameter.hasMethodAnnotation(Cache.class); } /** * 功能的增强 响应拦截器 * * @param body 需要被增强的对象 * @param methodParameter 被拦截的参数 * @param mediaType * @param aClass * @param serverHttpRequest * @param serverHttpResponse * @return */ @Override public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { //如果响应体为null if (body == null) { return null; } //响应体为String/duix String redisValue = null; try { //为string if (body instanceof String) { redisValue = (String) body; } else { //为对象 redisValue = MAPPER.writeValueAsString(body); } //获取路径名+参数params+token作为key String redisKey = RedisCacheInterceptor.createRedisKey(((ServletServerHttpRequest) serverHttpRequest).getServletRequest()); //存在redis中 Cache cache = methodParameter.getMethodAnnotation(Cache.class); this.redisTemplate.opsForValue().set(redisKey, redisValue, Long.valueOf(cache.time()), TimeUnit.SECONDS); } catch (Exception e) { e.printStackTrace(); } return body; } }
以上如果有问题,欢迎大家指出!!!