Java REST API偶发未映射HTTP 100状态码问题排查求助
问题
我调用一个Java编写的REST API服务时,偶尔会出现未映射的HTTP状态码,定位不到问题。用Postman执行相同请求始终能得到预期状态码,但通过应用调用时偶尔失败。
相关代码
@Component public class FessCalculator implements Processor { private static Map<String, Object> headers; private static int responseStatusCode = 200; @Override public void process(Exchange exchange) throws Exception { String message = (String) exchange.getIn().getHeader("exception.message"); String supplierName = exchange.getProperty("supplierName",String.class); Map<String, companyFess> output = new HashMap<>(); companyFess companyFess = new companyFess(); if (StringUtils.isBlank(message)) { Map<String, Object> map = exchange.getIn().getBody(Map.class); String ebsResult = (String) map.get("x_msg_data"); if (StringUtils.isBlank(ebsResult)) { companyFess.setFees((List<TaxLineOut>) map.get("lines_tab")); output.put(supplierName, companyFess); } else { /* API executed with errors. Fees will be empty */ companyFess.setMessage(ebsResult); companyFess.setFees(new ArrayList<TaxLineOut>()); output.put(supplierName, companyFess); } } else { /* API could not be executed. Fees will be null */ companyFess.setMessage("API Could not\n be executed"); companyFess.setFees(new ArrayList<TaxLineOut>()); output.put(supplierName, companyFess); } companyFessResponse response = new companyFessResponse(); if (output == null || output.isEmpty()) { responseStatusCode = 400; } else { response.setCompanyFees(output); for (companyFess companyFess1 : output.values()) { if (StringUtils.isNotBlank(companyFess1.getMessage())) { response.setStatus(500); } } } exchange.getOut().setBody(response); headers = exchange.getIn().getHeaders(); headers.put(Exchange.HTTP_RESPONSE_CODE, responseStatusCode); exchange.getOut().setHeaders(headers); } }
调用方错误日志
8444-[2023-02-02 19:44:17] feesCalculator.CRITICAL: "feesCalculator API Exception: "{\n \"status\" : 200,\n \"companyTaxes\" : {\n \"Company\" : {\n \"message\" : \"\",\n \"taxes\" : [ {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"STATE\",\n \"tax_rate_id\" : 12500,\n \"tax_rate_code\" : \"STATE NGRP 13M\",\n \"tax_rate_name\" : \"VAT RAT BSAS\",\n \"operand\" : 1.1,\n \"amount\" : 11499.92\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USCF\",\n \"tax_rate_id\" : 13779,\n \"tax_rate_code\" : \"GRP07_IIBB_CF\",\n \"tax_rate_name\" : \"VAT RAT USA Res 364 AGIP 1%\",\n \"operand\" : 1,\n \"amount\" : 10454.49\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USMI\",\n \"tax_rate_id\" : 13881,\n \"tax_rate_code\" : \"USMI CONV\",\n \"tax_rate_name\" : \"VAT RAT USA\",\n \"operand\" : 3.31,\n \"amount\" : 0\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USA-VAT\",\n \"tax_rate_id\" : 13710,\n \"tax_rate_code\" : \"USA-VAT CM 2,5\",\n \"tax_rate_name\" : \"VAT RAT Tucum\",\n \"operand\" : 2.5,\n \"amount\" : 501.81\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USFO\",\n \"tax_rate_id\" : 12035,\n \"tax_rate_code\" : \"USFO CONV\",\n \"tax_rate_name\" : \"VAT RAT Formos\",\n \"operand\" : 1,\n \"amount\" : 0\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"VAT\",\n \"tax_rate_id\" : 11756,\n \"tax_rate_code\" : \"VAT 21% PP BA\",\n \"tax_rate_name\" : \"VAT 21%\",\n \"operand\" : 21,\n \"amount\" : 47326\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"USER\",\n \"tax_rate_id\" : 12200,\n \"tax_rate_code\" : \"USER 3%\",\n \"tax_rate_name\" : \"VAT Washington\",\n \"operand\" : 3,\n \"amount\" : 0\n }, {\n \"tax_regime_code\" : \"USA-VAT\",\n \"tax\" : \"VAT\",\n \"tax_rate_id\" : 11765,\n \"tax_rate_code\" : \"VAT 21% RVTA\",\n \"tax_rate_name\" : \"VAT 21%\",\n \"operand\" : 21,\n \"amount\" : 172218.1\n } ]\n }\n }\n}"" [] []}
调用feesCalculator处理器计算费用时,偶尔会抛出HTTP 100状态码,但响应内容已正常加载。请问哪里出错了?为什么会出现这种情况?我预期只返回已映射的HTTP状态码。
分析与解决方案
问题核心是静态变量的线程安全问题:
responseStatusCode和headers是类级别的静态变量,而Spring容器中FessCalculator是单例Bean,多并发请求会共享这两个变量。当多个请求同时进入process方法时,前一个请求的状态码还没写完,后一个请求就可能覆盖它的值,导致最终返回的状态码混乱(比如出现的100状态码,就是其他请求遗留的异常值)。- Postman测试时请求量小,并发概率低,所以很难复现;应用调用时并发请求多,就会触发这个问题。
修改后的代码
将静态变量改为方法内的局部变量,让每个请求独立处理自己的状态码和头信息:
@Component public class FessCalculator implements Processor { @Override public void process(Exchange exchange) throws Exception { String message = (String) exchange.getIn().getHeader("exception.message"); String supplierName = exchange.getProperty("supplierName",String.class); Map<String, companyFess> output = new HashMap<>(); companyFess companyFess = new companyFess(); if (StringUtils.isBlank(message)) { Map<String, Object> map = exchange.getIn().getBody(Map.class); String ebsResult = (String) map.get("x_msg_data"); if (StringUtils.isBlank(ebsResult)) { companyFess.setFees((List<TaxLineOut>) map.get("lines_tab")); output.put(supplierName, companyFess); } else { /* API executed with errors. Fees will be empty */ companyFess.setMessage(ebsResult); companyFess.setFees(new ArrayList<TaxLineOut>()); output.put(supplierName, companyFess); } } else { /* API could not be executed. Fees will be null */ companyFess.setMessage("API Could not\n be executed"); companyFess.setFees(new ArrayList<TaxLineOut>()); output.put(supplierName, companyFess); } companyFessResponse response = new companyFessResponse(); // 改为局部变量,默认值200 int responseStatusCode = 200; if (output == null || output.isEmpty()) { responseStatusCode = 400; } else { response.setCompanyFees(output); for (companyFess companyFess1 : output.values()) { if (StringUtils.isNotBlank(companyFess1.getMessage())) { response.setStatus(500); // 同步设置HTTP响应码为500 responseStatusCode = 500; } } } exchange.getOut().setBody(response); // 复制头信息,避免修改原请求头集合 Map<String, Object> headers = new HashMap<>(exchange.getIn().getHeaders()); headers.put(Exchange.HTTP_RESPONSE_CODE, responseStatusCode); exchange.getOut().setHeaders(headers); } }
额外说明
- 原代码存在逻辑漏洞:当
companyFess1.getMessage()不为空时,只设置了响应体的状态为500,但未同步修改HTTP响应码,导致响应体和HTTP状态码不一致,修改后的代码补上了这一点。 - 用
new HashMap<>(exchange.getIn().getHeaders())复制头信息,避免直接修改原请求的头集合,防止对后续流程产生意外影响。
内容的提问来源于stack exchange,提问作者InternetLover

