基于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;
    }
}
相关推荐
小兔兔吃萝卜1 小时前
Spring 创建 Bean 的 8 种主要方式
java·后端·spring
AAA修煤气灶刘哥2 小时前
面试官: SpringBoot自动配置的原理是什么?从启动到生效,一文讲透
后端·spring·面试
qq_三哥啊4 小时前
【IDEA】设置Debug调试时调试器不进入特定类(Spring框架、Mybatis框架)
spring·intellij-idea·mybatis
别惹CC5 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
寒士obj5 小时前
Spring事物
java·spring
IT毕设实战小研14 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
甄超锋15 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
Java小白程序员18 小时前
Spring Framework:Java 开发的基石与 Spring 生态的起点
java·数据库·spring
甄超锋19 小时前
Java Maven更换国内源
java·开发语言·spring boot·spring·spring cloud·tomcat·maven
还是鼠鼠20 小时前
tlias智能学习辅助系统--Maven 高级-私服介绍与资源上传下载
java·spring boot·后端·spring·maven