基于spring gateway 的静态资源缓存实现

由于子项目比较多,子项目都是通过嵌套的方式实现的。就会导致子页面加载比较慢,影响客户体验

实现思路(AI搜的--!):

1、通过spring boot缓存实现静态资源缓存

2、在gateway过滤器,对静态资源进行缓存

直接上代码:

XML 复制代码
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
java 复制代码
package com.xxx.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Wang
 * 创建时间: 2023/11/15 10:19
 * 功能描述:静态资源缓存
 */
@Slf4j
@Component
public class StaticResourceFilter implements GlobalFilter, Ordered {


    private static final String STATIC_RESOURCE_PATTERN = "\\.(html|css|js|png|jpg|jpeg|gif|woff2|woff)$";
    private final WebClient webClient;
    private final CacheManager cacheManager;
    List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());

    public StaticResourceFilter(WebClient webClient, CacheManager cacheManager) {
        this.webClient = webClient;
        this.cacheManager = cacheManager;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uriInfo = request.getURI();
        String staticResourcePath = getUrl(uriInfo);
        if (isStaticResource(staticResourcePath) && !synchronizedList.contains(staticResourcePath)) {
            //Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            String cacheKey = request.getURI().toString();
            Cache cache = cacheManager.getCache("staticResources");

            // 尝试从缓存中获取静态资源
            Object cachedResource = cache.get(cacheKey);
            if (cachedResource != null) {
                if (cachedResource instanceof SimpleValueWrapper) {
                    cachedResource = ((SimpleValueWrapper) cachedResource).get();
                }

                // 如果缓存中存在,直接返回缓存的资源
                ServerHttpResponse response = exchange.getResponse();
                HttpHeaders headers = response.getHeaders();

                String fileSuffix = staticResourcePath.replaceAll(".*(\\.[a-zA-Z0-9]+)$", "$1");
                // 根据文件后缀设置MIME类型
                switch (fileSuffix) {
                    case ".html":
                        headers.setContentType(MediaType.TEXT_HTML);
                        break;
                    case ".js":
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/javascript");
                        break;
                    case ".css":
                        headers.set(HttpHeaders.CONTENT_TYPE, "text/css");
                        break;
                    case ".png":
                        headers.setContentType(MediaType.IMAGE_PNG);
                        break;
                    case ".jpg":
                    case ".jpeg":
                        headers.setContentType(MediaType.IMAGE_JPEG);
                        break;
                    case ".woff":
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/font-woff");
                        break;
                    case ".woff2":
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/font-woff2");
                        break;
                    case ".ttf":
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/x-font-ttf");
                        break;
                    case ".eot":
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/vnd.ms-fontobject");
                        break;
                    default:
                        headers.setContentType(MediaType.ALL);
                        break;
                }

                // 这里假设缓存的内容是字节数组,您可以根据实际情况进行调整
                DataBuffer dataBuffer = response.bufferFactory().wrap((byte[]) cachedResource);
                return response.writeWith(Mono.just(dataBuffer));
            }
            // 如果缓存不存在,则继续请求下游服务获取资源,并将其缓存起来
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                getResourceFromDownstream(staticResourcePath, cacheKey, cache);
            }));
        }

        // 继续处理其他过滤器或请求
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    /**
     * 根据请求路径判断是否为静态资源请求
     *
     * @param staticResourcePath 请求路径
     */
    private boolean isStaticResource(String staticResourcePath) {
        Pattern pattern = Pattern.compile(STATIC_RESOURCE_PATTERN);
        Matcher matcher = pattern.matcher(staticResourcePath);
        return matcher.find();
    }

    /**
     * 请求下游服务静态资源的方法,这里只是一个示例,您需要根据实际情况实现此方法
     *
     * @param cache              缓存
     * @param staticResourcePath URL
     * @param cacheKey           缓存Key
     */
    private void getResourceFromDownstream(String staticResourcePath, String cacheKey, Cache cache) {

        synchronizedList.add(staticResourcePath);
        Mono<byte[]> mono = webClient.get().uri(staticResourcePath).retrieve().bodyToMono(byte[].class);
        // 处理响应数据
        mono.subscribe(res -> {
            synchronizedList.remove(staticResourcePath);
            cache.put(cacheKey, res);
        }, error -> {
            log.error("请求下游服务静态资源失败:{},\n错误详情:{}", staticResourcePath, error.toString());
        });
    }

    /**
     * 获取静态资源地址
     *
     * @param uri uri
     * @return 静态资源地址
     */
    private String getUrl(URI uri) {
        String path = uri.getPath();
        String host = uri.getHost();
        int port = uri.getPort();

        // 下游服务的地址是
        String downstreamUrl = String.format("http://%s:%s%s", host, port, path);
        return downstreamUrl;
    }
}
相关推荐
格子先生Lab10 小时前
Spring Boot 本地缓存工具类设计与实现
spring boot·后端·缓存
m0_7482400210 小时前
Gateway Timeout504 网关超时的完美解决方法
gateway
sky丶Mamba11 小时前
Spring框架中的单例Bean是线程安全的吗
java·安全·spring
stayhungerstayflush14 小时前
spring webclient介绍
java·python·spring
ashane131415 小时前
Spring 构造器注入和setter注入的比较
java·spring·log4j
努力学计算机的小白一枚16 小时前
146.LRU缓存
java·数据结构·缓存
雾喔16 小时前
Java的缓存
java·缓存·mybatis
SchneeDuan16 小时前
力扣146 - LRU缓存
缓存·lru
JLiuli16 小时前
Redis网络模型
数据库·redis·缓存
Justice link16 小时前
Docker参数,以及仓库搭建
后端·spring·spring cloud