Spring Cloud Gateway 拦截响应问题分析(数据截断问题)
作者:起风哥 发布时间:2022-06-20 07:30:27
Spring Cloud Gateway是Spring 官方基于Spring 5.0、Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单有效的、统一的 API 路由管理方式。
Spring Cloud Gateway 作为 Spring Cloud 生态系中的网关,其目标是替代 Netflix Zuul,它不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,如:安全、监控/埋点和限流等。
Spring Cloud Gateway依赖Spring Boot和Spring WebFlux,基于Netty 运行。不能在传统的 servlet 容器中工作也不能构建成war包。
关于Spring Cloud Gateway 核心概念
1、Route
Route 是网关的基础元素,由 ID、目标 URI、断言、过滤器组成。当请求到达网关时,由 Gateway HandlerMapping 通过断言进行路由匹配(Mapping),断言为真时匹配到路由。
2、Predicate
Predicate 是 Java 8 中提供的一个函数。输入类型是 Spring Framework ServerWebExchange。它允许开发人员匹配来自 HTTP 的请求,例如请求头或者请求参数。简单来说它就是匹配条件。
3、Filter
Filter是Gateway 中的过滤器,可以在请求发出前后进行一些业务上的处理。
Spring Cloud Gateway 拦截响应
最近因为上链路追踪后发现如果在服务层将异常拦截掉,在链路追踪界面上就不会显示异常链路信息,除了服务异常意外,系统的异常不会触发链路error,所以对服务层做了个变更,将所有未处理异常直接捕获后统一封装完抛出,这个时候就需要在网关层统一处理那么网关需要对响应数据进行拦截如果是 9999错误码,则封装后返回,如果是其它响应码或者其它数据直接返回。
起初写法测试后发现数据过长时被截断。
return super.writeWith(fluxBody.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
// 释放掉内存
DataBufferUtils.release(dataBuffer);
String str = new String(content, Charset.forName("UTF-8"));
originalResponse.getHeaders().setContentLength(str.getBytes().length);
log.error("gateway catch service exception error:"+ str);
JsonResult result = new JsonResult();
result.setCode(ErrorCode.SYS_EXCEPTION.getCode());
result.setMessage(ErrorCode.SYS_EXCEPTION.getMsg());
return bufferFactory.wrap(str.getBytes());
}));
查询api后发现存在一个DataBufferFactory可以一次性join完所有数据后拼接就不会产生截断问题。
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
于是修改代码如下
package com.server.gateway.filters;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.http.protocol.HTTP;
import org.reactivestreams.Publisher;
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.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.framework.common.enums.ErrorCode;
import com.framework.common.web.JsonResult;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered{
@Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -2;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
AtomicReference<String> bodyRef = new AtomicReference<>();
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
// 释放掉内存
DataBufferUtils.release(join);
String str = new String(content, Charset.forName("UTF-8"));
originalResponse.getHeaders().setContentLength(str.getBytes().length);
log.error("gateway catch service exception error:"+ str);
JsonResult result = new JsonResult();
result.setCode(ErrorCode.SYS_EXCEPTION.getCode());
result.setMessage(ErrorCode.SYS_EXCEPTION.getMsg());
return bufferFactory.wrap(str.getBytes());
}));
}
// if body is not a flux. never got there.
return super.writeWith(body);
}
};
// replace response with decorator
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
}
经过如上修改后链路追踪可以实现哪个服务出错就立马出现链路异常马上可以定位到哪个服务出现了未处理异常
来源:https://blog.csdn.net/a807719447/article/details/102823340
猜你喜欢
- 使用java语言用集合存储数据实现学生信息管理系统,在控制台上编译执行可以实现基本的学生信息增加、删除、修改、查询功能IO版可以参考我的另外
- 效果图片重写DataGridView的OnRowPostPaint方法或者直接在DataGridView的RowPostPaint事件里写,
- 前言在上一篇中,我们初步了解了Sentinel的基本概念,以及其有关限流方面的基础理论,本篇将通过简单的与框架进行整合,看看Sentinel
- Android 消息机制1.概述Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列(MessageQu
- 导读本文主体为单项链表和双向链表的反转以及简单的测试,以便于理解链表相关的算法题目。链表特点便于增删数据,不便于寻址在内存中属于跳转结构单链
- 概述模板方法模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。那么什么是模板方法呢?我们看下模板方法的定义。一个具体方法而非
- using System.Collections.Generic;using System.Linq;using System.Refle
- 1. 使用try-with-resources简化文件读取操作:修改前:FileInputStream fis = null;try { &
- 这里使用 Maven 项目管理工具构建项目初始化项目打开 Intellij IDEA,点击 Create New Project选择 Mav
- 一、简介随着 Apple 发布 iPhone X 之后,各大手机厂商也开始模仿这种刘海屏的设计,而且刘海屏手机的用户也是越来越大,前段时间将
- 先给出网页地址:https://wall.alphacoders.com/featured.php?lang=Chinese主要步骤:利用J
- 在新建Java项目时,run运行main方法时,报错 “java: 错误: 无效的源发行版:16”,
- 一、layui.use1、LayUI的官方使用文档:https://www.layui.com/doc/2、layui的内置模块不是默认就加
- 前情提要本文中提供了九种方式获取resources目录下文件的方式。其中打印文件的方法如下: /**
- 自从SEOTcs系统11月份24日更新了一下SEO得分算法以来,一直困扰我的一个问题出现了,java的数据job任务,在执行过程中会经常报以
- 一.什么是SemaphoreSemaphore,俗称信号量,它是操作系统中PV操作的原语在java的实现,它也是基于AbstractQueu
- Redisson分布式锁之前的基于注解的锁有一种锁是基本redis的分布式锁,锁的实现我是基于redisson组件提供的RLock,这篇来看
- 前言本文学习MP中的更新操作方法,带大家一起查看源码,了解更新操作的方法。学会熟练地去运用更新方法解决自己在项目中的问题。一、通过id更新1
- 本文实例为大家分享了java实现简单俄罗斯方块的具体代码,供大家参考,具体内容如下结合网上的资料刚做完课程设计,具体代码如下:public
- 对于自定义注解这里就不唠叨了,百度一大堆,这里有我一个自定义注解@Retention(RetentionPolicy.RUNTIME)@Ta