基于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;
    }
}
相关推荐
码蜂窝编程官方25 分钟前
【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现
java·vue.js·spring boot·后端·spring·旅游
AuroraI'ncoding1 小时前
时间请求参数、响应
java·后端·spring
计算机毕设指导63 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
煎饼小狗4 小时前
Redis五大基本类型——Zset有序集合命令详解(命令用法详解+思维导图详解)
数据库·redis·缓存
ExiFengs5 小时前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
wenyue11215 小时前
Revolutionize Your Kubernetes Experience with Easegress: Kubernetes Gateway API
容器·kubernetes·gateway
瓜牛_gn5 小时前
依赖注入注解
java·后端·spring
木子_lishk6 小时前
gateway 支持同时使用 https 和 http访问
https·gateway
一元咖啡6 小时前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
雯0609~7 小时前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存