找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1029

积分

0

好友

140

主题
发表于 前天 16:01 | 查看: 7| 回复: 0

在一次线上支付系统的告警中,我们收到了BigMoney java.lang.ArithmeticException的异常提示。该异常发生在处理批量付款流程时,日志打印操作意外中断了支付链路。

业务流程与问题背景
该模块负责调用支付中台接口,核心作用是将批次单在支付中台已冻结的总金额,按单笔拆解并进行“单笔解冻”,以便后续走正常单笔付款流程。然而,在日志打印序列化过程中,出现了异常。

异常根本原因
通过异常日志分析,发现序列化org.joda.money.Money对象时,FastJSON的ASM序列化器会调用getAmountMajorInt()方法。该方法内部使用BigDecimal.intValueExact()进行转换,当金额部分超过Integer.MAX_VALUE(即21亿)时,就会抛出ArithmeticException: Overflow

结合上下文日志,问题出现在处理53亿越南盾(VND)的场景中:

  • Java中,Integer.MAX_VALUE的值为2147483647(约21亿)。
  • 53亿远超此限制,导致整型溢出。

问题小结  

  1. org.joda.money.Money类定义了getAmountMajorInt()公开方法。  
  2. FastJSON生成的序列化器会遍历所有getter方法,执行到getAmountMajorInt()时触发溢出。  
  3. 这类日志打印操作绝不能影响核心支付链路的稳定性。

实验复现

通过以下测试代码可以复现该问题:

@Slf4j
public class MockTest extends TestBase {
    public void test() {
        // {"amount":5370000000,"currency":"VND"}
        BigDecimal amount = BigDecimal.valueOf(5370000000L);
        CurrencyUnit currencyUnit = CurrencyUnit.VND;
        Money money = Money.of(currencyUnit, amount);
        log.info("result: " + JSON.toJSONString(money));
        System.out.println("====> success");
    }
}

运行后,控制台会抛出ArithmeticException异常,与线上情况一致。

解决方案

解决该问题可以从两个方向入手:

1. 修改Money类的序列化行为

org.joda.money.Money类中,为getAmountMajorInt()方法添加FastJSON注解,忽略其序列化:

@JSONField(serialize = false)
public int getAmountMajorInt() {
    return getAmountMajor().intValueExact();
}

2. 更换JSON输出方式

升级到FastJSON 2.x版本并使用@JSONField(serializeUsing=...)定制序列化器,或者切换到Jackson并配合joda-money模块。以下是使用Jackson的示例:

添加依赖

<dependencies>
    <dependency>
        <groupId>org.joda</groupId>
        <artifactId>joda-money</artifactId>
        <version>1.0.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-joda-money</artifactId>
    </dependency>
</dependencies>

自定义日志打印工具类

public class LogConverter {
    private static final ObjectMapper objectMapper = newObjectMapper();

    public static ObjectMapper newObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JodaMoneyModule());
        return objectMapper;
    }

    public static String toJsonString(Object original) throws JsonProcessingException {
        return objectMapper.writeValueAsString(original);
    }
}

使用LogConverter.toJsonString()方法替代原有的JSON序列化,可以避免整型溢出问题,确保支付系统日志打印的稳定性。




上一篇:Zendesk数据迁移实践:重构长时间作业API实现无状态架构
下一篇:设计模式实战解析:以单例、工厂等5种模式为例的武侠编程心法
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 16:31 , Processed in 0.106607 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表