乐趣区

关于微服务:微服务开发系列认识到序列化的重要性

1 先说论断

在该框架中,不再应用 fastjson 作为序列化工具,全面应用 jackson。

作为对 fastjson 灵活性的弥补,在 framework:cn/framework/common/jackson 门路下,提供了 JacksonJacksonObjectJacksonArray 三个类作为代替,根本保留的 fastjson 的操作习惯,不必本人新建 ObjectMapper,而且比原先的 fastjson 提供的类更为灵便,性能也更加弱小。

2 为什么不应用 fastjson

序列化可能在单个我的项目中被认为不是如许重要的事件,这也造成很多开发人员被 fastjson 蛊惑了,认为序列化不就是一个简略的通过 get set 办法去解决 json 数据的形式吗,最多多一个简单类嵌套的解决。

然而当你应用解决过 spring boot 对日期解决的类型问题时,你会发现 fastjson 中的配置是不失效的。

当你应用了一个枚举类在外面加上一些简单的构造函数时,你会发现 fastjson 蹩脚的应用体验。

如果你心愿应用 fastjson 代替 spring boot 到 cloud 架构中的所有须要应用序列化的中央,我只能说基本上不太可能,即便勉强替换上了,也不晓得哪天会呈现问题,具体的状况在文章《为什么不应该再应用 FastJson》中。

因而,在该架构中,禁止应用除了 jackson 以外的序列化工具。

就算在 Alibaba 的我的项目 nacos 外面也是应用的 jackson。

3 序列化在微服务框架中的一致性

作为微服务,服务多是不可避免的,那么服务与服务之间通信数据的一致性尤为必要,总不能 A 服务说法语,B 服务说德语,通信还要叫上一个翻译官。

你必定心愿看到一个数据在 A 服务序列化结束之后,在 B 服务可能顺利的反序列化成指标对象。

这仅仅是服务与服务之间,还有内存与 redis 之间,还有内存 > spring security > redis,还有内存 > redis > rpc > 内存,等等状况。

所以请意识到序列化一致性的重要性,不要给开发减少多余的累赘。

为了做到一致性,框架内的三个类就能解决大多数问题

  1. framework:cn.framework.config.jackson.JacksonConfig
  2. framework:cn.framework.config.jackson.RedisSessionConfig
  3. framework:cn.framework.config.redisson.RedissonConfig

3.1 JacksonConfig

JacksonConfig 利用了 spring boot 框架中的 Jackson2ObjectMapperBuilder,用过 jackson 的都晓得,应用 jackson 须要生成 ObjectMapper,这个类就是帮忙生成的工厂类。

spring cloud gateway 刚好反对 Jackson2ObjectMapperBuilder,所以节俭了一部分代码。

在这个类外面,还配置了 spring.jackson.date-format,作为对立的工夫格局配置,默认为 yyyy-MM-dd HH:mm:ss

针对工夫的解决,在解决 elasticsearch 的多工夫格局反对启发,还引申出了多工夫格局解决类 MultipleLocalDateTimeSerializer

它的作用是可能配置多个工夫格局,可能将工夫格局的字符串,对格局进行解析,如果第一个格局失败了,就尝试下一个。

如果有什么非凡的类须要做反序列化配置,能够在这里减少。

3.2 RedisSessionConfig

RedisSessionConfig 配置了 spring security 保留 session 到 redis 的序列化类 RedisSerializer,所用到的 ObjectMapper 也是来自于 JacksonConfig 配置的 Jackson2ObjectMapperBuilder 生成的。

在这里你能够看到应用了 SecurityJackson2Modules 这个类,这是 spring security 默认提供的,反对将 spring security 中的一些安全类反序列化的模块,很不便。

RedisSessionConfig 也注册了 UserDeserializer 这个反序列化类,反序列化了 User,扩大自 spring security User 类,参考自 org.springframework.security.jackson2.UserDeserializer,如果还须要扩大用户属性,在 User 上扩大,并且在 UserDeserializer 中做相应的设置即可。

3.3 RedissonConfig

此类是对 redisson 的配置类。

对序列化的配置是这一行代码 Codec codecIns = new JsonJacksonCodec(jackson2ObjectMapperBuilder.build());,目标是应用零碎中配置的 ObjectMapper 进行序列化的操作,这样就可能放弃对立。

4 序列化泛型

因为 java 中泛型擦除的问题存在,在解决嵌套的简单的类型对象时,个别的伎俩都会生效。

例如上面这段代码,如果将 test 转换为 json string,再转换回 List<Map<String, User>> 就会遇到擦除的问题。

    data class User(
        val name: String,
        val age: Int
    )
    val test = mutableListOf<Map<String, User>>()
    test.add(mapOf("a" to User("a", 10)))

对此 fastjson 和 jackson,都有着类似的解决办法,那就是利用 TypeReference 来让泛型在生成时就被固定下来,框架中封装的 Jackson 提供了这种办法。

Jackson.parseJavaObject(test.jsonString(), object : TypeReference<List<Map<String, User>>>() {})

只能通过这种抽象类的形式,在生成时确定泛型的类型。

然而 jackson 除此之外,还提供了一种更加灵便的形式,JavaType

val mapType: JavaType = TypeFactory.defaultInstance().constructParametricType(Map::class.java, String::class.java, User::class.java)
    val type: JavaType = TypeFactory.defaultInstance().constructParametricType(List::class.java, mapType)
    Jackson.parseJavaObject(test.jsonString(), type)

它可能让你动静生成简单泛型类型,这也是 jackson 比 fastjson 弱小的中央之一。

现在因为应用了 kotlin,泛型有了更好的解决形式。

Jackson 类中,扩大了 convert 的办法,该办法利用了 kotlin 的 reified 关键字,来解决泛型擦除的问题

inline fun <reified T> Any.convert() = Jackson.convert(this, object : TypeReference<T>() {})

fun <T> convert(value: Any, typeReference: TypeReference<T>): T {if (value is String) {return OBJECT_MAPPER.readValue(value, typeReference)
            }
            return OBJECT_MAPPER.convertValue(value, typeReference)
        }

这个办法可能极大的不便泛型解决,今后的类型转换,只须要一行简略的代码

val result: List<Map<String, User>> = test.jsonString().convert()

本文参加了思否技术征文,欢送正在浏览的你也退出。

退出移动版