记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码

这篇具有很好参考价值的文章主要介绍了记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

       基于公司的业务需求,在SpringCloud Gateway组件的基础上,写了一个转发服务,测试开发阶段运行正常,并实现初步使用。但三个月后,PostMan请求接口,返回异常,经排查,从日志中获取到转发响应的结果为乱码:

      记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

跟踪日志:

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

转发到目标接口,响应结果已乱码。一般排查的思路是,查看请求方和响应方的编码格式是否一致,打印请求方的编码格式为UTF-8,响应服务的编码格式也是UTF-8。

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

以上说明编码格式没有问题。上网去找“gateway响应结果乱码”的相关文章,大多数会提供解决方案:

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);
System.out.println(str);
return bufferFactory.wrap(str.getBytes());

这段关键代码,在我的响应结果包装过滤器是有的,如下:

    /**
     * 获取到解码方的response,验签--->重新封装--->加签
     * 通过 DataBufferFactory 解决响应体分段传输问题。
     */
    private ServerHttpResponseDecorator verifyRePackageSignatureResponse(ServerWebExchange exchange, String jmf_decode_url, String route_privateKey, String jmf_publicKey) {
        ServerHttpResponse response = exchange.getResponse();
        log.debug("R:给响应结果response设置编码格式----START----");
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
        log.debug("R:给响应结果response设置编码格式-----END-----");
        DataBufferFactory bufferFactory = response.bufferFactory();

        return new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {

                    // 获取响应类型,如果是 json 就打印
                    String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
                    log.debug("响应类型为originalResponseContentType:{}",originalResponseContentType);
                    if (RequestResponseUtil.isJson(originalResponseContentType)) {
                        Flux<? extends DataBuffer> fluxBody = Flux.from(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 rootData = new String(content, StandardCharsets.UTF_8);
                            log.debug("R:正常返回的数据rootData为:{}", rootData);


                            //使用枚举 + 工厂 + 策略模式(第二版)
                            String newQqtBodyJson = null;
                            try {
                                newQqtBodyJson = DecoderSignStrategyContext.zwmVerifySignResponse(rootData, jmf_decode_url, route_privateKey, jmf_publicKey);
                                log.debug("R:【码路由服务】经具体策略处理,得到结果newQqtBodyJson为:{}", newQqtBodyJson);
                            } catch (Exception e) {
                                log.error("R:【码路由服务】解码方返回结果验签异常:{}", e);
                                throw new VerifySignException(StaticVar.FAIL_10020015, "R:【码路由服务】解码方返回结果验签异常");
                            }
                            byte[] respData = newQqtBodyJson.getBytes();
                            //byte[] respData = newQqtBodyJson.getBytes(StandardCharsets.UTF_8);
                            byte[] uppedContent = new String(respData, Charset.forName("UTF-8")).getBytes();
                            return bufferFactory.wrap(uppedContent);

                        }));

                    } else {
                        log.error("响应结果异常");
                        throw new ProcessHandleException(StaticVar.FAIL_10020015, "R:【码路由服务】解码方返回结果异常,非法JSON");
                    }
                }

                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
    }

因此不是代码的问题。又找到了一篇文章,解决了PostMan请求的问题。

https://bbs.csdn.net/topics/399102026/close

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

如上所述,在PostMan请求的headers中去掉Accept-Encoding,请求成功:

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

       至此,PostMan请求乱码的问题已解决。但事情似乎没有那么简单,接下来,使用手机模拟扫码,请求码路由服务,又出现了乱码,扫码结果页面,返回结果:

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

 查看日志,乱码如下:

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

      排查问题进入瓶颈期,有些烦躁了,必须冷静下来,重新捋一下代码,同时也在想,既然和PostMan请求存在同样的问题,是不是在请求头中默认会有一个Accept-Encoding属性,从而导致了乱码,根据这个思路,在自定义的请求转发过滤器中,发现了以下代码:

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

由于在请求转发的逻辑中,重新构建了一个request请求的同时,重新创建了一个请求头headers,它在重新构建的时候默认会有一个accept-encoding属性,看到这一行代码时,正印证了以上我的猜想,问题已经找到了,将其置为空字符串即可(注意:不能使其为null,也不能remove这个属性,会报错)。

  将headers的accept-encoding属性置为空字符串,并添加注释如下:

// 定义新的消息头
        HttpHeaders headers = new HttpHeaders();
        //System.out.println("headers = " + headers); //查看重新定义消息头的内容
        headers.putAll(exchange.getRequest().getHeaders());

        // 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度
        int length = bodyStr.getBytes().length;
        log.debug("bodyStr长度为:{}",length);
        headers.remove(HttpHeaders.CONTENT_LENGTH);

        // 设置CONTENT_TYPE
        if (StringUtils.isNotBlank(contentType)) {
            headers.set(HttpHeaders.CONTENT_TYPE, contentType);
        }

        String jmf_decode_url = request.getURI().toString();
        String qqtBodyJsonStr = JSONUtils.toString(bizPackage);

        //request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(bodyStr.length())); //报错JSON parse error JsonEOFException,长度必须为字节的长度
        request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(length));//成功,解决JSON parse error JsonEOFException

        /**
         * 乱码现象:请求响应结果乱码
         * 解决过程分为PostMan请求和手机端App扫码请求:
         *   (1)、PostMan请求响应乱码解决:
         *             PostMan请求的headers中默认会有"Accept-Encoding"属性,值为"gzip, deflate, br",导致响应结果乱码
         *         去掉Accept-Encoding后请求正常。
         *
         *   (2)、手机端App扫码请求乱码解决:
         *             在HttpRequestSignForwardGatewayFilter中定义新的消息头,headers中默认会有"accept-encoding"属性,
         *         值为"gzip, deflate, br", 添加代码"request.mutate().header("accept-encoding",""); "解决乱码
         *
         *       注意: 创建headers对象默认会生成“accept-encoding=‘gzip, deflate, br‘ ”属性,此处必须将accept-encoding
         *       置为空字符串(置为null会报错),否则使用默认值会导致响应结果乱码。
         *       说明: 测试开发阶段未发生此问题,第三方检测时演示出现此问题,这个可能是gateway内部的问题,尚未可知。
         *
         *          经测试,这种方式已解决了:手机端APP扫码和PostMan响应结果乱码的问题(PostMan请求时可以不用刻意去掉Accept-Encoding,
         *       也可请求成功)
         *
         */
        request.mutate().header("accept-encoding","");

重启后,部署后问题得到解决。这个问题在测试开发阶段没有暴露出来,按理说应该早暴露了,但现实情况就是这么诡异,这可能是Gateway内部的bug,尚未可知。

记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码,Java,编程语言,spring cloud,gateway,响应结果乱码,路由转发,微服务

     希望此文对遇到同样问题的小伙伴有所帮助和启发,望了解gateway内部原理机制的大神,参与讨论。

    乱码问题已排查并处理结束,完结,撒花!

参考文章:

RestTemplate请求头accept-encoding导致乱码_resttemplate 乱码_AE86Jag的博客-CSDN博客

https://bbs.csdn.net/topics/399102026/close

在此感谢文章作者!文章来源地址https://www.toymoban.com/news/detail-758743.html

到了这里,关于记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记一次线上问题 → Deadlock 的分析与优化

    今天女朋友很生气 女朋友:我发现你们男的,都挺单纯的 我:这话怎么说 女朋友:脑袋里就只想三件事,搞钱,跟谁喝点,还有这娘们真好看 我:你错了,其实我们男人吧,每天只合计一件事 女朋友:啥事呀? 我:这娘们真好看,得搞钱跟她喝点   MySQL8. 0.30  ,隔离级别

    2024年02月15日
    浏览(48)
  • 记一次线上问题 → 偶尔的热情真的难顶呀!

    昨晚和媳妇坐在沙发上刷视频 我用手肘轻轻推了推媳妇:你看这渣男,玩完女的都不娶人家 媳妇:哎哟我天,哎呀妈,我这也没好哪去呀 我疑惑的看向媳妇:啥意思啊 媳妇看向自己的手机:啥意思啊,特么有些人,娶完了也不玩呀 我负责的系统需要同步上游系统的数据 同

    2024年02月03日
    浏览(44)
  • 记一次线上kafka重复消费的问题解决及思考

    线上ELK日志发现kafka消费者消费到重复消息 由于生产方本身就发送了重复的消息,导致消费到重复消息 消费方采用的是循环poll的模式,具体是在多线程分租户去批量处理的消息

    2024年02月10日
    浏览(53)
  • 记一次线上问题引发的对 Mysql 锁机制分析

    最近双十一开门红期间组内出现了一次因 Mysql 死锁导致的线上问题,当时从监控可以看到数据库活跃连接数飙升,导致应用层数据库连接池被打满,后续所有请求都因获取不到连接而失败 整体业务代码精简逻辑如下: 数据库实例监控: 当时通过分析上游问题流量限流解决后

    2024年02月05日
    浏览(56)
  • 记一次线上mysql出错:由于docker自动拉取最新mysql镜像导致mysql容器无法启动

    我随便写写,你们随便看看 环境背景:在docker中部署mysql镜像,通过portainer管理docker容器 简单说下过程:docker里mysql的时区没有设置,导致相差8小时,通过增加TZ=Asiz/Shanghai环境变量,然后重启容器来生效。结果重启的时候始终无法启动起来,后来发现是自动升级了mysql镜像版

    2024年02月07日
    浏览(56)
  • 记一次 Redisson 线上问题 → ERR unknown command 'WAIT' 的排查与分析

    昨晚和一个朋友聊天 我:处对象吗,咱俩试试? 朋友:我有对象 我:我不信,有对象不公开? 朋友:不好公开,我当的小三 程序在生产环境稳定的跑着 直到有一天,公司执行组件漏洞扫描,有漏洞的  jar  要进行升级修复 然后我就按着扫描报告将有漏洞的  jar  修复到指

    2024年02月09日
    浏览(57)
  • JAVA开发(记一次504 gateway timeout错误排查过程)

    一、问题与背景: 最近在发布一个web项目,在测试环境都是可以的,发布到生产环境通过IP访问也是可以的,但是通过域名访问就出现504 gateway timeout。通过postman去测试接口也是一样。ip和端口都可以通,域名却不行,百思不得其解。通过一顿百度搜索,解析说通过nginx配置文

    2024年02月11日
    浏览(43)
  • 通过一次线上问题,讲下Ribbon重试机制

    前段时间,产品经理在线上验证产品功能的时候,发现某个功能不符合需求预期,后来测试验证发现是服务端的一个接口大概率偶现超时,前端做了兜底处理,所以对线上用户么有太大影响。 由于服务端的接口偶现超时,并且网关设置了30s超时熔断,所以前端请求就直接报错

    2024年02月15日
    浏览(60)
  • 一次线上mysql 调优 ,join 的调优,索引优化(Block Nested Loop)

    原因: 某接口调用十分缓慢,通过 Explain 发现是SQL问题 可以看到,在Join连接时,出现了BNL查询,BNL出现是因为,JOIN连接时 dr表也就是 domian_redemption 被驱动的表上没出现可用的索引。 个人解决方法: 在对应的连接字段上,既dr的orderCode字段,内表加上索引,再次执行Explai

    2024年02月05日
    浏览(51)
  • 记一次内存泄漏排查

    最近某项目的服务突然告警,cpu超85%,随后就是服务宕机。交付重启服务后恢复正常但是随后不久又开始告警,特别是白天,严重影响客户业务进行。 1、分析日志 查看日志的过程中发现存在内存溢出(OOM),思考要么存在内存泄漏要么业务上触发了某个接口存在大对象,结

    2023年04月16日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包