当前位置: 首页 > news >正文

一次简单的 Http 请求异常处理 (请求的 url 太长, Nginx 直接返回 400, 导致请求服务异常)

1 结论

按照惯例直接说结论。

后台服务 A 有一个 Http 接口, 代码如下:

@RequestMapping(value = "/user", method = RequestMethod.GET)
public List<UserInfoVo> getUserInfoByUserIds(@RequestParam(value = "userIds") List<String> userIds) {// ...
}

没错, 一个 Get 请求, 入参是一个 List

同时有另一个后台服务 B, 里面有段逻辑会通过 RestTemplate 调用服务 A 的这个接口, 代码如下:

public List<UserInfoVo> batchGetUserInfo(Collection<String> userIds) {String url = String.format("%s/user?userIds=%s", remoteHost, StringUtils.join(userIds, ","));String returnResult = restTemplate.getForObject(url, String.class);// ...
}

在服务 B 中, 通过 batchGetUserInfo 方法请求服务 A 时, 传入了一个长度为 122 的 List, List 中每一项是一个 32 位的 UUID。
结果导致调用服务 A 的 url 长度太长, Nginx 认为这时一个异常的请求格式, 直接返回状态码 400, 结构导致服务 B 逻辑异常

2 过程

2.1 反馈

下午, 突然收到用户反馈: 进入某个页面后, 直接白屏。

2.2 定位到直接原因

直接通过 Nginx 请求日志, 发现用户反馈的操作时间段内, 有一个接口一直返回 400 的错误。
根据客户端反馈这个错误的确会导致页面白屏。

知道了直接原因了, 但是没有解决, 还是需要定位到根本原因。

2.3 Arthas 排查

通过错误的 url, 定位到对应的代码, 然后通过 Kibana 查看日志, 发现只有一个简单的异常提示, 没输出任何堆栈信息。

因为是一个查询接口, 所以本地通过拼接参数, 尝试请求这个接口, 发现是逻辑正常的, 应该是数据问题, 这就尴尬了。

通过分析代码逻辑, 看不出什么异常的。
在没有日志, 复现不出反馈情况, 代码逻辑分析不出异常时, 决定通过 Arthas 协助排查了。

在生产环境中, 启动了一个预发版本, 通过 url 模拟用户请求。

同时启动 Arthas, watch 对应的接口

watch com.aaa.bbb.TestController testMethod "{params,returnObj,throwExp}" -x 4

定位到以下异常:

org.springframework.web.client.HttpClientErrorException: 400 at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]at com.aaa.bbb..service.impl.RemoteServiceImpl.batchGetUserInfo(RemoteServiceImpl.java:206) ~[classes/:?]......

根据堆栈信息定位到代码

public List<StudentVo> batchGetUserInfo(Collection<String> userIds) {String url = String.format("%s/user?userIds=%s", remoteHost, StringUtils.join(userIds, ","));String returnResult = restTemplate.getForObject(url, String.class);// ...
}

看逻辑没多大的异常, 唯一比较惊讶的就是 Get 请求, 入参确实是一个 List, 不过 Http 本身就支持这样操作。

继续通过 Arthas, watch 对应的接口, 不过这次 watch 上面的代码:

watch com.aaa.bbb..service.impl.RemoteServiceImpl batchGetUserInfo "{params,returnObj,throwExp}" -x 4

继续通过 url 模拟用户请求。

发现堆栈信息同样的 HttpClientErrorException: 400 异常, 但是通过打印的参数列表发现, 入参竟然是 123 个的 String。

第一时间感觉到: 参数太多, 拼接的 url 太长, 导致请求失败。
但是转念一下, Get 请求 url 的长度限制是浏览器的行为, Http 协议没有对传输的数据大小进行限制
现在是 2 个后台服务的 Http 请求, 没有经过任什么浏览器, 理论上是这个长度无限制的。

2.4 Nginx 直接返回 400 错误码

请求 url 感觉没什么问题?
既然这样, 会不会是结果响应方处理有什么异常吗?

同样通过 Kibana 查看日志, 发现对应的接口, 没有当前用户的请求日志。也就是说, 对应的请求没有到达服务 A。

不是被调用方的问题, 那么会不会是 RestTemplate 这个框架内部做了限制呢?

restTemplate.getForObject 出发, 进入到源码, 发现内部也是没有对 url 长度做限制的, 同时定位到抛出异常的位置如下

public class DefaultResponseErrorHandler implements ResponseErrorHandler {@Overridepublic void handleError(ClientHttpResponse response) throws IOException {// 从响应里面获取到状态码HttpStatus statusCode = getHttpStatusCode(response);switch (statusCode.series()) {// 状态码 4xxcase CLIENT_ERROR:throw new HttpClientErrorException(statusCode, response.getStatusText(),response.getHeaders(), getResponseBody(response), getCharset(response));// 状态码 5xx						case SERVER_ERROR:throw new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response));default:throw new RestClientException("Unknown status code [" + statusCode + "]");}}
}

抛出异常的结果是根据请求返回的状态码来决定的。 也就是服务 B 有发起请求, 同时收到了一个 400 的错误码, restTemplate 将其封装为一个 HttpClientErrorException。

调用方有发起请求, 被调用方没有请求日志, 2 者之间通过通过 Http 请求, 那么有问题的的地方应该就是 2 者中间的 Nginx 了。

2.5 验证

public List<StudentVo> batchGetUserInfo(Collection<String> userIds) {String url = String.format("%s/user?userIds=%s", remoteHost, StringUtils.join(userIds, ","));String returnResult = restTemplate.getForObject(url, String.class);// ...
}

将上面的 remoteHost 替换为一个具体的 ip 地址, 直接请求对应的容器, 绕过 Nginx。
重新部署, 通过 url 模拟用户请求, 正常响应。

3 总结

服务 A 的请求先经过 Nginx, 再由 Nginx 转发到 B。
而异常的用户的请求到了 Nginx, Nginx 直接返回了 400, 从而导致用户请求异常。

通过查询资料, Nginx 报 400 的场景如下

  1. request_uri 过长超过 nginx 配置大小
  2. cookie 或者 header 过大超过 nginx 配置大小
  3. 空 HOST 头
  4. content_length 和 body 长度不一致

我遇到的情况就是第一种。
Nginx 处理时认为客户端请求格式错误, 于是直接返回 400, 不会向 upstream server (也就是下游服务) 转发请求, 因而 upstream server 对这些错误请求其实完全是无感知的。

至此结束。

碎碎念:
其实对 Http 响应码有一点了解, 结合上面获取到的请求参数太多和堆栈的信息的 400, 基本可以推导出问题了, 不用像我一样, 一步步猜测验证。
而本身通过这次, 对 Http 的响应码和 Nginx 也算是多了一点了解。

相关文章:

一次简单的 Http 请求异常处理 (请求的 url 太长, Nginx 直接返回 400, 导致请求服务异常)

1 结论 按照惯例直接说结论。 后台服务 A 有一个 Http 接口, 代码如下: RequestMapping(value "/user", method RequestMethod.GET) public List<UserInfoVo> getUserInfoByUserIds(RequestParam(value "userIds") List<String> userIds…...

spring Cloud在代码中如何应用,erueka 客户端配置 和 服务端配置,Feign 和 Hystrix做高可用配置

文章目录 Eureka一、erueka 客户端配置二、eureka 服务端配置 三、高可用配置FeignHystrix 通过这篇文章来看看spring Cloud在代码中的具体应用&#xff0c;以及配置和注解&#xff1b; Eureka 一、erueka 客户端配置 1、Eureka 启禁用 eureka.client.enabledtrue 2、Eurek…...

C#8.0中新语法“is {}“的介绍及使用

一&#xff64;C#7.0及之前is的使用 is操作符检查表达式的结果是否与给定类型兼容&#xff0c;或者(从c# 7.0开始)根据模式测试表达式。有关类型测试is操作符的信息&#xff0c;请参阅类型测试和类型转换操作符文章的is操作符部分。 1&#xff64;is 模式匹配 从C&#xff0…...

编译器设计01-入门概述

编译器作用概述 源代码 → 编译器 目标代码 源代码\xrightarrow{\ \ \ 编译器\ \ \ }目标代码 源代码 编译器 ​目标代码 编译阶段概述 编译处理包括两个阶段&#xff1a;前端处理和后端处理&#xff0c;中间过程生成语法树。 编译处理&#xff1a;源代码 → 语法树 …...

SpringBoot封装Elasticsearch搜索引擎实现全文检索

一、前言 注&#xff1a;本文实现了Java对Elasticseach的分页检索/不分页检索的封装 ES就不用过多介绍了&#xff0c;直接上代码&#xff1a; 二、实现步骤&#xff1a; 创建Store类&#xff08;与ES字段对应&#xff0c;用于接收ES数据&#xff09; import com.alibaba.f…...

(C)一些题4

1. 以下叙述中正确的是( )。 A.C程序中的注释只能出现在程序的开始位置和语句的后面 B.C程序书写格式严格&#xff0c;要求行内只能写一个语句 C,C程序书写格式自由&#xff0c;一个语句可以写在多行上 D.用C语言编写的程序只能放在一个程序文件中 2.设有如下程序段 char …...

ChatGPT初体验:注册、API Key获取与ChatAPI调用详解

自从2022年10月&#xff0c;ChatGPT诞生以后&#xff0c;实际上已经改变了很多&#xff01;其火爆程度简直超乎想象&#xff0c;一周的时间用户过百万&#xff0c;两个月的时间用户过亿。 目前ChatGPT4已经把2023年4月以前的人类的知识都学习到了&#xff0c;在软件工程里面&am…...

TCP/IP协议、三次握手、四次挥手

TCP/IP TCP/IP协议分层TCP头部三次握手TCP四次挥手常见问题1、什么是TCP网络分层2、TCP为什么是三次握手&#xff0c;不是两次或者四次&#xff1f;3、TCP为什么是四次挥手&#xff0c;为什么不能是三次挥手将第二次挥手和第三次挥手合并&#xff1f;4、四次挥手时为什么TIME_W…...

Android U 匹配不到APN,无法发起数据建立的问题分析

问题 打开数据开关后&#xff0c;没有data PDN请求发起&#xff0c;因此无法上网。 根据日志确定是没有找到合适的data profile&#xff0c;原因一般有&#xff1a; 1、APN 没有配置 2、APN 类型/网络能力不满足——APN type或bearer 3、APN 配置了但被disable了——APN p…...

如何打造“面向体验”的音视频能力——对话火山引擎王悦

编者按&#xff1a;随着全行业视频化的演进&#xff0c;我们置身于一个充满创新与变革的时代。在这个数字化的浪潮中&#xff0c;视频已经不再只是传递信息的媒介&#xff0c;更是重塑了我们的交互方式和体验感知。作为字节跳动的“能力溢出”&#xff0c;火山引擎正在飞速奔跑…...

什么是NoOps

过去几年&#xff0c;自动化一直在推动整个 IT 行业向前发展。通过自动化某些任务&#xff0c;开发团队可以提高其能力&#xff0c;而无需感受到雇用新团队成员的预算压力。自动化还保证了更高的效率&#xff0c;特别是在操作和维护方面。 传统的软件开发工作流程涉及开发团队…...

Unity - Graphic解析

Gpahic 的作用 Graphic 是 Unity最基础的图形基类。主要负责UGUI的显示部分。 由上图可以看你出我们经常使用的Image&#xff0c;Text&#xff0c;都是继承自Graphic。 Graphic的渲染流程 在Graphic的源码中有以下属性 [NonSerialized] private CanvasRenderer m_CanvasRend…...

哈希思想的应用

目录 1.位图 位图的实现 题目变形一 题目变形二 题目变形三 总结&#xff1a; 2.布隆过滤器 概念 布隆过滤器的实现 3.哈希切割的思想 1.位图 哈希表和位图是数据结构中常用的两种技术。哈希表是一种数据结构&#xff0c;通过哈希函数把数据和位置进行映射&#xff0c…...

React入门使用 (官方文档向 Part1)

文章目录 React组件:万物皆组件 JSX: 将标签引入 JavaScriptJSX 规则1. 只能返回一个根元素2. 标签必须闭合3. 使用驼峰式命名法给 ~~所有~~ 大部分属性命名&#xff01;高级提示&#xff1a;使用 JSX 转化器 在 JSX 中通过大括号使用 JavaScript使用引号传递字符串使用大括号&…...

87基于matlab的双卡尔曼滤波算法

基于matlab的双卡尔曼滤波算法。第一步使用了卡尔曼滤波算法&#xff0c;用电池电压来修正SOC&#xff0c;然后将修正后的SOC作为第二个卡尔曼滤波算法的输入&#xff0c;对安时积分法得到的SOC进行修正&#xff0c;最终得到双卡尔曼滤波算法SOC估计值。结合EKF算法和安时积分法…...

Jacobi迭代与SOR迭代求解希尔伯特矩阵

给出线性方程组 Hn*x b&#xff0c;其中系数矩阵Hn为希尔伯特矩阵&#xff1a; 假设 x ∗ (1, 1, . . . , 1)T&#xff0c;b Hnx ∗。若取 n 6,8, 10&#xff0c;分别用 Jacobi 迭代法及 SOR迭代&#xff08;ω 1, 1:25,1:5&#xff09;求解&#xff0c;比较计算结果。…...

【云备份】配置加载文件模块

文章目录 配置信息设计配置文件加载cloud.conf配置文件单例模式的使用ReadConfigFile —— 读取配置文件GetInstance —— 创建对象其他函数的实现 具体实现cloud.confconfig.hpp 配置信息设计 使用文件配置加载一些程序运行的关键信息 可以让程序的运行更加灵活 配置信息&am…...

sqlserver写入中文乱码问题

sqlserver写入中文乱码问题解决方案 首先查看sqlserver数据库编码 首先查看sqlserver数据库编码 查询语句&#xff1a;SELECT COLLATIONPROPERTY(Chinese_PRC_Stroke_CI_AI_KS_WS, CodePage)&#xff1b; 对应的编码&#xff1a; 936 简体中文GBK 950 繁体中文BIG5 437 美国/加…...

【亚马逊云】基于EC2以 All-in-One 模式快速部署 KubeSphere 和 Kubernetes

文章目录 1. 云实例配置说明2. SSH连接云实例3. 查看系统版本4. 修改主机名5. 安装依赖项6. 安全组和DNS修改7. 下载KubeKey8. 同时安装Kubesphere和Kubernetes[可选]单独安装Kubernetes[可选]单独安装KubeSphere9. 验证KubeSphere安装结果10. 登录KubeSphere控制台[可选]安装K…...

使用 ChatGPT 创建 Makefile 构建系统:从 Docker 开始

使用 Docker 搭配 ChatGPT 创建 Makefile 构建系统 Makefile 构建系统是嵌入式软件团队实现其开发流程现代化的基础。构建系统不仅允许开发人员选择各种构建目标&#xff0c;还可以将这些构建集成到持续集成/持续部署 (CI/CD) 流程中。使用诸如 ChatGPT 这样的人工智能 (AI) 工…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...