如何通过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
相关产品推荐
相关产品推荐

