本文研究redis 来做缓存 搭建请看 博文使用Docker搭建Redis集群
加入依赖
org.springframework.boot spring-boot-starter-data-redis redis.clients jedis 2.9.0 commons-io commons-io 2.6 ## 编写配置文件 spring.redis.jedis.pool.max-wait = 500 spring.redis.jedis.pool.max-Idle = 100 spring.redis.jedis.pool.min-Idle = 10 spring.redis.timeout = 10 spring.redis.cluster.nodes = 192.168.0.10:6379,192.168.0.10:6380,192.168.0.10:6381 spring.redis.cluster.max-redirects=5 连接不上修改 timeout时间 ## 编写配置类@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
@Data
public class ClusterConfigurationProperties {
private List<String> nodes;
private Integer maxRedirects;
}
注册Redis连接工厂
@Configuration
public class RedisClusterConfig {
@Autowired
private ClusterConfigurationProperties clusterProperties;
@Bean
public RedisConnectionFactory connectionFactory() {
RedisClusterConfiguration configuration = new
RedisClusterConfiguration(clusterProperties.getNodes());
configuration.setMaxRedirects(clusterProperties.getMaxRedirects());
return new JedisConnectionFactory(configuration);
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory
redisConnectionfactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionfactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
编写测试用例
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRedis {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Test
public void testSave() {
for (int i = 0; i < 100; i++) {
this.redisTemplate.opsForValue().set("key" + i, "value" + i);
}
Set<String> keys = this.redisTemplate.keys("key_*");
for (String key : keys) {
String value = this.redisTemplate.opsForValue().get(key);
System.out.println("value :"+value);
this.redisTemplate.delete(key);
}
}
}
添加缓存逻辑
本文采用统一控制缓存逻辑
采用拦截器进行缓存命中
@Component
public class RedisCacheInterceptor implements HandlerInterceptor {
private static ObjectMapper mapper = new ObjectMapper();
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(StringUtils.equalsIgnoreCase(request.getMethod(), "OPTIONS")){
return true;
}
// 判断请求方式,get还是post还是其他。。。
if (!StringUtils.equalsIgnoreCase(request.getMethod(), "GET")) {
// 非get请求,如果不是graphql请求,放行
if (!StringUtils.equalsIgnoreCase(request.getRequestURI(), "/graphql")) {
return true;
}
}
// 通过缓存做命中,查询redis,redisKey ? 组成:md5(请求的url + 请求参数)
String redisKey = createRedisKey(request);
String data = this.redisTemplate.opsForValue().get(redisKey);
if (StringUtils.isEmpty(data)) {
// 缓存未命中
return true;
}
// 将data数据进行响应
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
// 支持跨域
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.getWriter().write(data);
return false;
}
public static String createRedisKey(HttpServletRequest request) throws
Exception {
String paramStr = request.getRequestURI();
Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap.isEmpty()) {
paramStr += IOUtils.toString(request.getInputStream(), "UTF-8");
} else {
paramStr += mapper.writeValueAsString(request.getParameterMap());
}
String redisKey = "WEB_DATA_" + DigestUtils.md5Hex(paramStr);
return redisKey;
}
}
注册拦截器到Spring
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private RedisCacheInterceptor redisCacheInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.redisCacheInterceptor).addPathPatterns("/**");
}
}
编写HttpServletRequest的包装类
public class MyServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
/**
* Construct a wrapper for the specified request.
*
* @param request The request to be wrapped
*/
public MyServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = IOUtils.toByteArray(super.getInputStream());
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new RequestBodyCachingInputStream(body);
}
private class RequestBodyCachingInputStream extends ServletInputStream {
private byte[] body;
private int lastIndexRetrieved = -1;
private ReadListener listener;
public RequestBodyCachingInputStream(byte[] body) {
this.body = body;
}
@Override
public int read() throws IOException {
if (isFinished()) {
return -1;
}
int i = body[lastIndexRetrieved + 1];
lastIndexRetrieved++;
if (isFinished() && listener != null) {
try {
listener.onAllDataRead();
} catch (IOException e) {
listener.onError(e);
throw e;
}
}
return i;
}
@Override
public boolean isFinished() {
return lastIndexRetrieved == body.length - 1;
}
@Override
public boolean isReady() {
// This implementation will never block
// We also never need to call the readListener from this method, as this method will never return false
return isFinished();
}
@Override
public void setReadListener(ReadListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener cann not be null");
}
if (this.listener != null) {
throw new IllegalArgumentException("listener has been set");
}
this.listener = listener;
if (!isFinished()) {
try {
listener.onAllDataRead();
} catch (IOException e) {
listener.onError(e);
}
} else {
try {
listener.onAllDataRead();
} catch (IOException e) {
listener.onError(e);
}
}
}
@Override
public int available() throws IOException {
return body.length - lastIndexRetrieved - 1;
}
@Override
public void close() throws IOException {
lastIndexRetrieved = body.length - 1;
body = null;
}
}
}
通过过滤器进行包装request对象:
```java
```java
@Component
public class RequestReplaceFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (!(request instanceof MyServletRequestWrapper)) {
request = new MyServletRequestWrapper(request);
}
filterChain.doFilter(request, response);
}
}
测试你的接口
在Controller中获取数据陈工