ERR_HTTP2_PROTOCOL_ERROR

用Chrome浏览器访问后端某流媒体资源接口,发现媒体资源内容无法加载,F12打开Network工具调试,发现资源报错net::ERR_HTTP2_PROTOCOL_ERROR,状态码又是200,表示客户端到服务器的链接是正常的,能建立正常链接,但是就是资源请求不回来。

然后谷歌搜索How To Fix the ERR_HTTP2_PROTOCOL_ERROR

一种观点认为需要配置nginx的proxy_max_temp_file_size

在Nginx的反向代理配置中,将proxy_max_temp_file_size设置为0的含义是禁用临时文件的使用。默认情况下,Nginx会在反向代理过程中使用临时文件来存储大体积的响应数据。

当反向代理的响应数据超过proxy_max_temp_file_size指定的大小时,Nginx会将响应数据写入到一个临时文件中,然后再将该文件发送给客户端。这是为了避免占用过多的内存,特别是在处理大文件时。

proxy_max_temp_file_size设置为0意味着禁用临时文件的使用。这样,当反向代理的响应数据超过指定的大小时,Nginx将会使用内存来存储响应数据,而不是写入到临时文件中。

禁用临时文件的使用可能会导致Nginx消耗更多的内存资源。因此,如果你的反向代理处理的响应数据通常较大,或者希望减少对磁盘I/O的依赖,可以考虑将proxy_max_temp_file_size设置为0。但是,请注意确保Nginx服务器的内存足够支持处理大量的响应数据。

配置示例:

location / {
    proxy_pass http://backend;
    proxy_max_temp_file_size 0;
}

在上述示例中,将proxy_max_temp_file_size设置为0以禁用临时文件的使用。这样,Nginx将会使用内存来存储反向代理的响应数据。

参考链接:https://www.cnblogs.com/ami-miao/p/15175129.html

一种观点认为要关闭谷歌浏览器的QUIC协议

chrome://flags/#enable-quic

Now you’ll see a highlighted result labeled Experimental QUIC protocol. For this setting, change it to Disabled:

实际上谷歌QUIC(Quick UDP Internet Connections)协议和HTTP/2是两种不同的协议,但它们之间有一些关联和相互影响。

HTTP/2是一种新的网络协议,旨在改善HTTP/1.1的性能和效率。它引入了一些新的特性,如二进制分帧、头部压缩、多路复用等,以减少延迟、提高吞吐量和性能。

谷歌QUIC协议是一种基于UDP的传输协议,用于在网络上进行快速、安全的数据传输。QUIC协议结合了传输层和应用层的特性,旨在提供更低的延迟和更高的性能。它使用UDP协议而不是TCP协议来进行数据传输,并集成了一些与安全相关的功能,如TLS加密和身份验证。

HTTP/2和QUIC之间的关系在于,QUIC协议可以作为HTTP/2的底层传输协议。这意味着在使用QUIC协议时,可以在其上运行HTTP/2,从而获得HTTP/2的性能优势和功能。

试了一下也没什么作用。

参考链接:How To Fix the ERR_HTTP2_PROTOCOL_ERROR

最后想了一下代码变动的内容:

因为增加了LoggingFilter,拦截请求打印日志打印body里的数据,会出现不能再次读取的问题,servlet的requestbody以及response的body只能被读取一次,一旦流被读取了,就无法再次读取了。

推荐使用Spring本身提供的Wrapper类来解决此问题,当然也可以自己封装个Wrapper类,继承HttpServletRequestWrapper即可。

Spring ContentCachingRequestWrapper和ContentCachingResponseWrapper是用于缓存HTTP请求和响应内容的包装器类。

ContentCachingRequestWrapper用于包装HttpServletRequest对象,它可以缓存请求的内容,包括请求体和请求参数。通过使用ContentCachingRequestWrapper,可以在请求处理的任何时候获取请求的内容,例如日志记录或调试。

ContentCachingResponseWrapper用于包装HttpServletResponse对象,它可以缓存响应的内容,包括响应体和响应头。通过使用ContentCachingResponseWrapper,可以在响应返回到客户端之前获取响应的内容,例如记录响应的内容或修改响应的头部信息。

private static ContentCachingRequestWrapper wrapRequest(HttpServletRequest request) {
  if (request instanceof ContentCachingRequestWrapper) {
      return (ContentCachingRequestWrapper) request;
  } else {
      return new ContentCachingRequestWrapper(request);
  }
}

private static ContentCachingResponseWrapper wrapResponse(HttpServletResponse response) {
  if (response instanceof ContentCachingResponseWrapper) {
      return (ContentCachingResponseWrapper) response;
  } else {
      return new ContentCachingResponseWrapper(response);
  }
}

对了一下代码变更,然后查看了一下nginx报错的日志
upstream prematurely closed connection while reading upstream异常 返回ERR_CONTENT_LENGTH_MISMATCH

@ApiOperation(value = "获取资源媒体流", hidden = true)
@RequestMapping(value = "/getStream/*", method = RequestMethod.GET)
public ResponseEntity<StreamingResponseBody> getStream(@RequestHeader(required = false, value = "Range") String range,
                                                      @RequestParam("url") String url) {
  return new ResponseEntity<>(streamingResponseBody, responseEntity.getHeaders(), StringUtils.isBlank(range) ? HttpStatus.OK : HttpStatus.PARTIAL_CONTENT);
}

当请求getStream/xxx.jpg资源的时候,经过LoggingFilter拦截之后,ContentCachingResponseWrapper包装后,最后执行response.copyBodyToResponse时,异步请求返回流数据,导致连接提前中断,最终获取的ResponseBody里面内容为空,所以请求就会不定时报ERR_HTTP2_PROTOCOL_ERROR错误,最后判断如果是异步请求或者请求流媒体资源就跳过doFilterWrapped。

if (isAsyncDispatch(request) || request.getRequestURI().contains("/getStream/")) {
    filterChain.doFilter(request, response);
} else {
    doFilterWrapped(wrapRequest(request), wrapResponse(response), filterChain);
}

参考链接:Not getting response body when using ContentCachingResponseWrapper

当然在stackoverflow上看到这样的解决方案

responseToCache.copyBodyToResponse()

更改成下面的代码:the execution order: sync-filterChain(in)->Controller of DeferredResult<VO> method -> sync-filterChain(out) -> async-filterChain(in) ->async-filterChain(out). It seems that the response returns to the front-end just after sync-filterChain(out), not after async-filterChain(out), so of cause the sync-response-body is empty!

if (requestToCache.isAsyncStarted()) {
  requestToCache.getAsyncContext().addListener(new AsyncListener() {
      public void onComplete(AsyncEvent asyncEvent) throws IOException {
          responseToCache.copyBodyToResponse()
      }

      public void onTimeout(AsyncEvent asyncEvent) throws IOException {
      }

      public void onError(AsyncEvent asyncEvent) throws IOException {
      }

      public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
      }
  })
} else {
  responseToCache.copyBodyToResponse()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值