You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Micrometer Timer捕获ObjectMapper序列化反序列化延迟指标

优化方案

你提到的顶级对象包裹方案确实可行,但完全不需要为埋点额外解析对象,下面两种方案可以在复用现有序列化/反序列化逻辑的基础上优雅实现计时:

方案一:利用Jackson的BeanSerializerModifier和BeanDeserializerModifier

这种方式相当于给Jackson的序列化/反序列化流程加了一个"切面",不需要替换你已有的自定义序列化器,只在原有逻辑外层包裹计时逻辑,完全复用原生处理流程。

序列化计时Modifier实现

public class TimingBeanSerializerModifier extends BeanSerializerModifier {
    private final Timer serializationTimer;

    public TimingBeanSerializerModifier(Timer serializationTimer) {
        this.serializationTimer = serializationTimer;
    }

    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        // 只对我们关心的类添加计时,可根据需要调整范围
        if (Person.class.isAssignableFrom(beanDesc.getBeanClass()) || Address.class.isAssignableFrom(beanDesc.getBeanClass())) {
            return new TimingJsonSerializer(serializer, serializationTimer);
        }
        return serializer;
    }

    // 内部类实现计时包裹
    private static class TimingJsonSerializer extends JsonSerializer<Object> {
        private final JsonSerializer<Object> delegate;
        private final Timer timer;

        public TimingJsonSerializer(JsonSerializer<Object> delegate, Timer timer) {
            this.delegate = delegate;
            this.timer = timer;
        }

        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            try (Timer.Sample sample = Timer.start()) {
                // 调用原有序列化逻辑
                delegate.serialize(value, gen, serializers);
                // 停止计时并记录指标
                sample.stop(timer);
            }
        }
    }
}

反序列化计时Modifier实现

public class TimingBeanDeserializerModifier extends BeanDeserializerModifier {
    private final Timer deserializationTimer;

    public TimingBeanDeserializerModifier(Timer deserializationTimer) {
        this.deserializationTimer = deserializationTimer;
    }

    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        if (Person.class.isAssignableFrom(beanDesc.getBeanClass()) || Address.class.isAssignableFrom(beanDesc.getBeanClass())) {
            return new TimingJsonDeserializer(deserializer, deserializationTimer);
        }
        return deserializer;
    }

    private static class TimingJsonDeserializer extends JsonDeserializer<Object> {
        private final JsonDeserializer<Object> delegate;
        private final Timer timer;

        public TimingJsonDeserializer(JsonDeserializer<Object> delegate, Timer timer) {
            this.delegate = delegate;
            this.timer = timer;
        }

        @Override
        public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            try (Timer.Sample sample = Timer.start()) {
                Object result = delegate.deserialize(p, ctxt);
                sample.stop(timer);
                return result;
            }
        }
    }
}

更新ObjectMapper配置

@Bean
public ObjectMapper objectMapper(MeterRegistry meterRegistry) {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    objectMapper.configure(Deserialization.FAIL_ON_UNKNOWN_PROPERTIES, false);

    // 创建序列化和反序列化的Timer,可添加标签区分不同类
    Timer serializationTimer = Timer.builder("jackson.serialization.duration")
            .description("耗时:Jackson对象序列化")
            .tag("target_class", "Person,Address")
            .register(meterRegistry);

    Timer deserializationTimer = Timer.builder("jackson.deserialization.duration")
            .description("耗时:Jackson对象反序列化")
            .tag("target_class", "Person,Address")
            .register(meterRegistry);

    SimpleModule module = new SimpleModule();
    // 注册计时Modifier
    module.setSerializerModifier(new TimingBeanSerializerModifier(serializationTimer));
    module.setDeserializerModifier(new TimingBeanDeserializerModifier(deserializationTimer));
    // 保留你原有的自定义序列化/反序列化器
    module.addDeserializer(Address.class, new AddressDeserializer());
    module.addSerializer(Address.class, new AddressSerializer());
    
    objectMapper.registerModule(module);
    return objectMapper;
}

方案二:给现有自定义序列化器/反序列化器添加计时逻辑

如果你只需要针对特定类(比如Address)单独计时,可以直接修改你现有的自定义序列化器,让它实现ContextualSerializer(序列化侧)或ContextualDeserializer(反序列化侧)接口,在初始化时注入Timer:

修改AddressSerializer示例

public class AddressSerializer extends JsonSerializer<Address> implements ContextualSerializer {
    private Timer timer;

    // 无参构造,供Jackson默认初始化
    public AddressSerializer() {}

    // 带Timer的构造,用于上下文注入
    public AddressSerializer(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void serialize(Address value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        try (Timer.Sample sample = Timer.start()) {
            // 你原有的序列化逻辑
            gen.writeStartObject();
            gen.writeStringField("addLineOne", value.getAddLineOne());
            gen.writeStringField("addLineTwo", value.getAddLineTwo());
            gen.writeNumberField("zipCode", value.getZipCode());
            gen.writeEndObject();
            // 记录耗时
            sample.stop(timer);
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        // 从Spring上下文获取MeterRegistry(这里用工具类示例,你可以根据项目实际方式获取)
        MeterRegistry meterRegistry = SpringContextUtil.getBean(MeterRegistry.class);
        Timer timer = Timer.builder("jackson.serialization.address.duration")
                .description("耗时:Address对象序列化")
                .register(meterRegistry);
        return new AddressSerializer(timer);
    }
}

为什么原有方案不够优?

你提到的顶级对象包裹方案,会强制把整个对象的序列化逻辑重新实现一遍(为了埋点),不仅重复造轮子,还可能和你已有的子对象自定义序列化器冲突,甚至带来额外的解析开销。上面的两种方案都是在原有逻辑外层做轻量包裹,完全复用Jackson原生流程,既实现了指标捕获,又不会影响原有功能的性能和逻辑。


内容的提问来源于stack exchange,提问作者frpet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:51:36