改进:这是gateway的老版本遗留,我以前是2.04,后升级到2.17后bug已经被解决
老版本或者无法升级的可以尝试一下方案
前提:这里只针对304处理,302等其他请使用其他方案,如果有小伙伴有更好的方案请提出讨论
首先我们必须知道今静态资源304状态,是有两个http 头部信息决定的,Last-Modified 和If-Modified-Since
Last-Modified 是由服务器发送给客户端的HTTP请求头标签
If-Modified-Since 则是由客户端发送给服务器的HTTP请求头标签
第一次浏览器请求的时候是没有If-Modified-Since,当请求成功服务器会把该静态资源的Last-Modified(最后一次修改时间)返回
而后浏览器再次请求静态资源的时候会携带该头部信息的If-Modified-Since ,服务器会把If-Modified-Since和Last-Modified 进行比对,如果一致,则返回304状态码告诉浏览器,你本地已经缓存,直接使用即可;
问题就在于返回304后,服务端是没有body内容的,而gateway 在处理熔断的时候是查看后台是否有body进行判断的。且熔断机制在gateway中优先级非常高;从而导致gateway在对304进行处理的时候以为服务器熔断而降级;使得这种类型的静态资源 无法加载;
解决路程:
1.我暂时关闭了熔断,使用全局异常处理方式获得后台返回,如果为空,则设置状态码为304;并设置body 为随意,(浏览器会根据状态码来进行业务处理,而不会根据body内容:(切记:后端服务请进行全局异常处理,非304状态下的异常最好不要返回null,否则会会增加gateway的逻辑复杂度))代码如下:
/**
*
* @author Administrator
* @version $Id: JsonExceptionHandler, v 0.1 2020/3/16 22:48 Administrator Exp$
*/
public class JsonExceptionHandler implements ErrorWebExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(JsonExceptionHandler.class);
/**
* MessageReader
*/
private List> messageReaders = Collections.emptyList();
/**
* MessageWriter
*/
private List> messageWriters = Collections.emptyList();
/**
* ViewResolvers
*/
private List viewResolvers = Collections.emptyList();
/**
* 存储处理异常后的信息
*/
private ThreadLocal
/**
* 在启动类中加入
* 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可
*
* @param viewResolversProvider
* @param serverCodecConfigurer
* @return
*/
@Primary
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
JsonExceptionHandler jsonExceptionHandler = new JsonExceptionHandler();
jsonExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));
jsonExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
jsonExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
return jsonExceptionHandler;
}
这样当304返回空body的时候就直接对body进行赋值,从而使得浏览器正常304;但是如果加上了熔断后(熔断的优先级高于全局异常),会导致全局异常不起作用直接熔断,于是我在熔断代码里加了以下代码:
@RequestMapping("/fallbackSimple")
@ResponseBody
public LinkedHashMap fallbackSimple(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
DataBufferFactory bufferFactory = response.bufferFactory();
bufferFactory.allocateBuffer().read();//读取返回流的时候会异常,从而进入全局异常,全局异常根据文件后缀设置状态码
log.info("服务熔断了----------fallbackSimple");
return fallback;
}
这样既能使用全局异常,又能使用熔断;
以上方案为一个取巧方案;
还有另一个方案就是修改gateway的源代码重新打包(这种方案本人没有实际测试过,debug调试下看了可以获取到304状态码)
状态码那一行提前,然后进行状态码判断,理论上这种方案更科学;