Gateway网关和Feign调用开启gzip压缩

本文介绍了一种提高数据响应速度的方法,通过在网关层实现数据压缩,并利用Feign进行服务间调用来进一步优化。文章详细展示了如何在Spring Cloud Gateway中添加自定义过滤器以实现响应体压缩,同时介绍了如何在YAML配置文件中设置Feign的gzip压缩。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

总监:咳咳咳…小王啊,最近挺清闲啊!

我:没有没有,还有好多bug没改呢。

总监:有个需求需要你弄一下。

我:好的好的。(摸鱼被抓不敢反抗啊!!!)

总监:现在的数据响应我觉得还可以提升一下,你把所有的响应在响应前都压缩一遍。还有服务之间feign调用的形式。

我:好的好的,现在就干。

总监:晚上给我个demo!


一:GateWay网关实现数据压缩

思路:
  1. 要网关吐出压缩后的数据,则要实现一个网关拦截器,在最后一层的拦截器当中修改响应的数据。
  2. 查看浏览器请求头中的Accept-Encoding: gzip, deflate一项,这里使用gzip压缩。

好啦,既然没有那么复杂,就开始干吧。


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.log4j.Log4j2;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.context.annotation.Configuration;
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.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.zip.GZIPOutputStream;


@Configuration
@Log4j2
public class ResponseFilter implements GlobalFilter, Ordered {

    /**
     * 约定的压缩格式
     */
    private final static String GZIP = "gzip";


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取响应体
        ServerHttpResponse originalResponse = exchange.getResponse();
        //获取请求体
        ServerHttpRequest request = exchange.getRequest();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                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 responseString = new String(content, Charset.forName("UTF-8"));
                        //判断该浏览器是否支持gzip解码,如果支持gzip解码,则进行压缩
                        String acceptEncoding = request.getHeaders().getFirst("Accept-Encoding");
                        if(!StrUtil.hasBlank(acceptEncoding)){
                            assert acceptEncoding != null;
                            //是否支持压缩
                            if(acceptEncoding.contains(GZIP)){
                                //支持压缩
                                ByteArrayOutputStream bout = new ByteArrayOutputStream();
                                //压缩输出流中的数据
                                GZIPOutputStream gout;
                                try {
                                    gout = new GZIPOutputStream(bout);
                                    gout.write(content);
                                    gout.close();
                                    content = bout.toByteArray();
                                    originalResponse.getHeaders().setContentLength(content.length);
                                    originalResponse.getHeaders().set("content-encoding", GZIP);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }else{
                                originalResponse.getHeaders().setContentLength(responseString.getBytes().length);
                            }
                        }
                        return bufferFactory.wrap(content);
                    }));
                }
                return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Override
    public int getOrder() {
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 2;
    }

}

好啦,代码撸完啦!试一下通过网关请求下游服务是否返回了压缩的数据。

ok!网关搞定啦,接下来feign就简单了!

二:Feign之间调用实现gzip压缩

只需要在yaml配置文件中增加如下配置:

#feign开启gzip压缩配置
feign:
  compression:
    request:
      enabled: true
      mime-types: ["text/xml,application/xml,application/json"]
      min-request-size: 2048
    response:
      enabled: true

开启debug级别的日志,服务之间调用一下可以看到已经开启了gzip。


我:嘿嘿,全部搞定。晚上才提交,又可以开心的划水了。

<think>首先,用户的问题是:“我想确认Feign调用是否经过网关 请问Feign调用是否需要经过API网关”。这是一个关于Spring Cloud Feign调用是否通过API网关的问题。 我需要基于提供的引用信息来回答。引用内容是关于Feign网关服务调用的。 - 引用[1]:解释了Feign如何简化HTTP API调用,类似于Dubbo,消费者通过接口调用远程服务,而不需要拼接URL。它还提到Feign支持SpringMVC注解(OpenFeign)。 - 引用[2]:讨论了区分网关请求服务间请求的方法。建议在请求头中添加参数来区分,而不是改变请求参数。这暗示服务间调用(如Feign)可能直接发生,而不经过网关。 - 引用[3]:展示了一个Feign接口的例子,其中有一个@RequestHeader注解,可能用于传递如FROM之类的头信息。 - 引用[4]:介绍了Spring Cloud Gateway作为API网关,用于路由管理。 - 引用[5]:提到在Spring Cloud中使用Feign客户端,并启用Feign功能。 关键点是:Feign调用通常是服务间直接调用,而不是通过API网关。API网关(如Spring Cloud Gateway或Zuul)通常用于外部客户端访问微服务,而服务间调用使用Feign或类似工具直接进行。 从引用[2]中:“来自网关的请求进行鉴权,而服务间的请求则直接调用。”这明确说明服务间调用(如Feign)是直接的,不经过网关。 引用[5]展示了在消费者服务中使用@EnableFeignClients来启用Feign,这允许服务直接调用其他服务,而不通过网关。 因此,回答应该是:Feign调用通常不经过API网关;它是服务间直接调用。 现在,系统指令要求: - 所有行内数学表达式用$...$格式(但在这个问题中,没有数学内容,所以可能不适用)。 - 独立公式用$$...$$格式(同样,可能不适用)。 - LaTeX语法正确(但问题不涉及数学)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾添加引用标识,如[^1]。 - 回答结构清晰,逐步解决问题。 - 保证回答真实可靠。 结构回答: 1. **直接回答问题**:Feign调用是否经过网关?答案是否定的。 2. **解释原因**:基于引用,Feign是用于服务间直接调用的工具,而API网关用于外部访问。 3. **引用支持**:在相关部分添加引用标识。 4. **生成相关问题**:在最后添加相关问题部分。 最终回答大纲: - 开头:直接回答用户问题。 - 解释:简要说明Feign的工作原理网关的角色。 - 引用支持:引用[2]中的关键部分。 - 相关问题:生成3-5个相关问题。 相关问题应该基于主题:Feign网关、微服务调用。 例如: 1. 如何配置Feign进行服务调用? 2. API网关在微服务架构中的作用是什么? 3. 如何区分网关请求服务间调用? 4. FeignRestTemplate有什么区别? 现在,撰写回答。</think>### Feign调用是否经过API网关Feign调用通常**不经过API网关**。在Spring Cloud微服务架构中,Feign是一种用于服务间直接调用的工具,它通过声明式接口简化HTTP API的调用,而API网关(如Spring Cloud Gateway或Zuul)主要用于管理外部客户端(如Web或移动端)到微服务的入口路由请求处理。服务间调用(例如一个微服务调用另一个微服务)使用Feign时,是直接在服务之间进行的,无需经过网关层。这有助于减少延迟提高效率,同时避免不必要的网关负载[^2]。 #### 为什么Feign调用不经过网关? - **Feign的工作原理**:Feign基于接口注解实现远程调用。服务消费者定义一个接口(如使用`@FeignClient`注解),并像调用本地方法一样发起请求。Feign在后台自动处理HTTP请求的构建发送,直接连接到目标服务的URL(例如通过服务注册中心如Eureka发现地址)。这类似于Dubbo的RPC调用方式,但专注于HTTP协议[^1][^5]。 - **网关的角色**:API网关作为统一入口,处理外部请求的路由、鉴权、限流等。例如,来自浏览器的请求会先经过网关,再由网关转发到具体服务。而服务间调用(如Feign)被视为内部通信,通常直接通过服务发现机制完成,无需网关介入[^4][^2]。 - **实践中的区分**:在微服务架构中,为了区分网关请求(需鉴权)服务间请求(直接调用),开发者可能在请求头中添加特定标识(如`SecurityConstants.FROM`),但这并不改变Feign调用的直接性。引用[2]中提到:“来自网关的请求进行鉴权,而服务间的请求则直接调用”,这强调了服务间调用(包括Feign)的独立性[^2][^3]。 #### 如何验证配置? - **验证方法**:在Feign接口中,可以通过日志或调试查看请求路径。Feign的URL通常是目标服务的直接地址(如`http://service-name/api`),而不是网关地址。 - **配置示例**:在Spring Cloud中,启用Feign只需在消费者服务中添加`@EnableFeignClients`注解,并定义接口: ```java @FeignClient(name = "target-service") public interface MyFeignClient { @PostMapping("/test") String callService(@RequestParam("id") String id, @RequestHeader(SecurityConstants.FROM) String from); } ``` 这里,请求直接发送到`target-service`,不经过网关[^3][^5]。 总之,Feign调用是服务间直接通信,不依赖API网关。如果您需要将某些请求通过网关(例如统一入口),需显式配置网关路由规则,但这不属于Feign的默认行为。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值