编解码器和序列化器的关系

  • Serializer:序列化器,包含序列化和反序列化操作。
  • Codec:编解码器,包含编码器和解码器。编解码器是一种术语,指的是可以表示数据的任何格式,或者将数据转化成另外一种格式的过程。所以,可以将Serializer序列化器也理解成Codec编码器的一种。

Codec编解码器通用接口定义如下:

  1. // Encoder writes objects to a serialized form
  2. type Encoder interface {
  3. // Encode writes an object to a stream. Implementations may return errors if the versions are
  4. // incompatible, or if no conversion is defined.
  5. Encode(obj Object, w io.Writer) error
  6. // Identifier returns an identifier of the encoder.
  7. // Identifiers of two different encoders should be equal if and only if for every input
  8. // object it will be encoded to the same representation by both of them.
  9. //
  10. // Identifier is intended for use with CacheableObject#CacheEncode method. In order to
  11. // correctly handle CacheableObject, Encode() method should look similar to below, where
  12. // doEncode() is the encoding logic of implemented encoder:
  13. // func (e *MyEncoder) Encode(obj Object, w io.Writer) error {
  14. // if co, ok := obj.(CacheableObject); ok {
  15. // return co.CacheEncode(e.Identifier(), e.doEncode, w)
  16. // }
  17. // return e.doEncode(obj, w)
  18. // }
  19. Identifier() Identifier
  20. }
  21. // Decoder attempts to load an object from data.
  22. type Decoder interface {
  23. // Decode attempts to deserialize the provided data using either the innate typing of the scheme or the
  24. // default kind, group, and version provided. It returns a decoded object as well as the kind, group, and
  25. // version from the serialized data, or an error. If into is non-nil, it will be used as the target type
  26. // and implementations may choose to use it rather than reallocating an object. However, the object is not
  27. // guaranteed to be populated. The returned object is not guaranteed to match into. If defaults are
  28. // provided, they are applied to the data by default. If no defaults or partial defaults are provided, the
  29. // type of the into may be used to guide conversion decisions.
  30. Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error)
  31. }
  32. // Serializer is the core interface for transforming objects into a serialized format and back.
  33. // Implementations may choose to perform conversion of the object, but no assumptions should be made.
  34. type Serializer interface {
  35. Encoder
  36. Decoder
  37. }
  38. // Codec is a Serializer that deals with the details of versioning objects. It offers the same
  39. // interface as Serializer, so this is a marker to consumers that care about the version of the objects
  40. // they receive.
  41. type Codec Serializer

从定义可以看出,只要实现了Encoder和Decoder数据结构都是序列化器。
每种序列化器都对资源对象的metav1.TypeMeta(即APIVersion和Kind字段)进行验证,如果资源对象没有提供这些字段,就会返回错误。每种序列化器分别实现了Encode序列化方法和Decode反序列化方法:

  • jsonSerializer:Json格式的序列化和反序列化器。ContentType: application/json
  • yamlSerializer:Yaml格式的序列化和反序列化器。ContentType:application/yaml
  • protobufSerializer:Protobuf格式序列化和反序列化器。ContentType:application/vnd.kubernetes.protobuf

三种ContentType定义在下面的文件中:
代码路径:k8s.io/apimachinery/pkg/runtime/types.go

  1. const (
  2. ContentTypeJSON string = "application/json"
  3. ContentTypeYAML string = "application/yaml"
  4. ContentTypeProtobuf string = "application/vnd.kubernetes.protobuf"
  5. )

Codec编解码实例化

Codec编解码器通过NewCodeFctory函数实例化,实例化过程中会将jsonSerializer、yamlSerializer和protobufSerializer都实例化。
NewCodeFctory —> newSerializerForScheme

  1. func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory, options CodecFactoryOptions) []serializerType {
  2. jsonSerializer := json.NewSerializerWithOptions(
  3. mf, scheme, scheme,
  4. json.SerializerOptions{Yaml: false, Pretty: false, Strict: options.Strict},
  5. )
  6. jsonSerializerType := serializerType{
  7. AcceptContentTypes: []string{runtime.ContentTypeJSON},
  8. ContentType: runtime.ContentTypeJSON,
  9. FileExtensions: []string{"json"},
  10. EncodesAsText: true,
  11. Serializer: jsonSerializer,
  12. Framer: json.Framer,
  13. StreamSerializer: jsonSerializer,
  14. }
  15. if options.Pretty {
  16. jsonSerializerType.PrettySerializer = json.NewSerializerWithOptions(
  17. mf, scheme, scheme,
  18. json.SerializerOptions{Yaml: false, Pretty: true, Strict: options.Strict},
  19. )
  20. }
  21. yamlSerializer := json.NewSerializerWithOptions(
  22. mf, scheme, scheme,
  23. json.SerializerOptions{Yaml: true, Pretty: false, Strict: options.Strict},
  24. )
  25. protoSerializer := protobuf.NewSerializer(scheme, scheme)
  26. protoRawSerializer := protobuf.NewRawSerializer(scheme, scheme)
  27. serializers := []serializerType{
  28. jsonSerializerType,
  29. {
  30. AcceptContentTypes: []string{runtime.ContentTypeYAML},
  31. ContentType: runtime.ContentTypeYAML,
  32. FileExtensions: []string{"yaml"},
  33. EncodesAsText: true,
  34. Serializer: yamlSerializer,
  35. },
  36. {
  37. AcceptContentTypes: []string{runtime.ContentTypeProtobuf},
  38. ContentType: runtime.ContentTypeProtobuf,
  39. FileExtensions: []string{"pb"},
  40. Serializer: protoSerializer,
  41. Framer: protobuf.LengthDelimitedFramer,
  42. StreamSerializer: protoRawSerializer,
  43. },
  44. }
  45. for _, fn := range serializerExtensions {
  46. if serializer, ok := fn(scheme); ok {
  47. serializers = append(serializers, serializer)
  48. }
  49. }
  50. return serializers
  51. }

jsonSerializer和yamlSerializer序列化器

jsonSerializer使用Go语言标准库encoding/json来实现序列化和反序列化。
yamlSerializer使用第三方库gopkg.in/yaml.v2来实现序列化和反序列化。

序列化操作

代码路径:vendor/k8s.io/apimachinery/pkg/runtime/serializer/json/json/go
Encode函数支持两种序列化操作,分别是YAML和JOSN。

  1. // Encode serializes the provided object to the given writer.
  2. func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
  3. if co, ok := obj.(runtime.CacheableObject); ok {
  4. return co.CacheEncode(s.Identifier(), s.doEncode, w)
  5. }
  6. return s.doEncode(obj, w)
  7. }
  8. func (s *Serializer) doEncode(obj runtime.Object, w io.Writer) error {
  9. if s.options.Yaml { //如果是YAML格式
  10. json, err := caseSensitiveJSONIterator.Marshal(obj) // 先将对象转化为JSON格式
  11. if err != nil {
  12. return err
  13. }
  14. data, err := yaml.JSONToYAML(json) //在将JSON格式转化为YAML格式
  15. if err != nil {
  16. return err
  17. }
  18. _, err = w.Write(data)
  19. return err
  20. }
  21. // 如果是JSON格式
  22. if s.options.Pretty { //如果开启了Pretty
  23. data, err := caseSensitiveJSONIterator.MarshalIndent(obj, "", " ") // 优化格式
  24. if err != nil {
  25. return err
  26. }
  27. _, err = w.Write(data)
  28. return err
  29. }
  30. encoder := json.NewEncoder(w) // 通过Go语言标准库的json.Encode函数将资源对象转化为JSON格式。
  31. return encoder.Encode(obj)
  32. }

Kubernetes在jsonSerializer序列化器上做了一些优化,caseSensitiveJsonIterator函数实际封装了github.com/json-iterator/go第三方库,json-iterator有以下几个好处:

  • json-iterator支持区分大小写,encoding/json不支持。
  • json-iterator性能更优,编码可达到837ns/op,解码可达到5623ns/op。
  • json-iterator 100%兼容Go语言标准库encoding/json,可随时切换两种编码方式。

反序列化操作

代码路径:vendor/k8s.io/apimachinery/pkg/runtime/serializer/json/json/go
同样Decode函数也支持两种序列化操作,分别是YAML和JOSN

  1. // Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then
  2. // load that data into an object matching the desired schema kind or the provided into.
  3. // If into is *runtime.Unknown, the raw data will be extracted and no decoding will be performed.
  4. // If into is not registered with the typer, then the object will be straight decoded using normal JSON/YAML unmarshalling.
  5. // If into is provided and the original data is not fully qualified with kind/version/group, the type of the into will be used to alter the returned gvk.
  6. // If into is nil or data's gvk different from into's gvk, it will generate a new Object with ObjectCreater.New(gvk)
  7. // On success or most errors, the method will return the calculated schema kind.
  8. // The gvk calculate priority will be originalData > default gvk > into
  9. func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
  10. data := originalData
  11. if s.options.Yaml { // 如果是YAML格式的数据
  12. altered, err := yaml.YAMLToJSON(data) // 先转化成JSON格式-
  13. if err != nil {
  14. return nil, nil, err
  15. }
  16. data = altered // 之后处理的格式都是JSON
  17. }
  18. actual, err := s.meta.Interpret(data) // actual 是JSON解析出来的GVK类型
  19. if err != nil {
  20. return nil, nil, err
  21. }
  22. if gvk != nil {
  23. *actual = gvkWithDefaults(*actual, *gvk) // 如果actual version和kind等字段为空,则使用默认值
  24. }
  25. if unk, ok := into.(*runtime.Unknown); ok && unk != nil { // 如果into是unknown类型,则返回unknown类型
  26. unk.Raw = originalData
  27. unk.ContentType = runtime.ContentTypeJSON
  28. unk.GetObjectKind().SetGroupVersionKind(*actual)
  29. return unk, actual, nil
  30. }
  31. if into != nil {
  32. _, isUnstructured := into.(runtime.Unstructured)
  33. types, _, err := s.typer.ObjectKinds(into)
  34. switch {
  35. case runtime.IsNotRegisteredError(err), isUnstructured:
  36. if err := caseSensitiveJSONIterator.Unmarshal(data, into); err != nil {
  37. return nil, actual, err
  38. }
  39. return into, actual, nil
  40. case err != nil:
  41. return nil, actual, err
  42. default:
  43. *actual = gvkWithDefaults(*actual, types[0])
  44. }
  45. }
  46. if len(actual.Kind) == 0 {
  47. return nil, actual, runtime.NewMissingKindErr(string(originalData))
  48. }
  49. if len(actual.Version) == 0 {
  50. return nil, actual, runtime.NewMissingVersionErr(string(originalData))
  51. }
  52. // use the target if necessary
  53. obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
  54. if err != nil {
  55. return nil, actual, err
  56. }
  57. if err := caseSensitiveJSONIterator.Unmarshal(data, obj); err != nil {
  58. return nil, actual, err
  59. }
  60. // If the deserializer is non-strict, return successfully here.
  61. if !s.options.Strict {
  62. return obj, actual, nil
  63. }
  64. // In strict mode pass the data trough the YAMLToJSONStrict converter.
  65. // This is done to catch duplicate fields regardless of encoding (JSON or YAML). For JSON data,
  66. // the output would equal the input, unless there is a parsing error such as duplicate fields.
  67. // As we know this was successful in the non-strict case, the only error that may be returned here
  68. // is because of the newly-added strictness. hence we know we can return the typed strictDecoderError
  69. // the actual error is that the object contains duplicate fields.
  70. altered, err := yaml.YAMLToJSONStrict(originalData)
  71. if err != nil {
  72. return nil, actual, runtime.NewStrictDecodingError(err.Error(), string(originalData))
  73. }
  74. // As performance is not an issue for now for the strict deserializer (one has regardless to do
  75. // the unmarshal twice), we take the sanitized, altered data that is guaranteed to have no duplicated
  76. // fields, and unmarshal this into a copy of the already-populated obj. Any error that occurs here is
  77. // due to that a matching field doesn't exist in the object. hence we can return a typed strictDecoderError,
  78. // the actual error is that the object contains unknown field.
  79. strictObj := obj.DeepCopyObject()
  80. if err := strictCaseSensitiveJSONIterator.Unmarshal(altered, strictObj); err != nil {
  81. return nil, actual, runtime.NewStrictDecodingError(err.Error(), string(originalData))
  82. }
  83. // Always return the same object as the non-strict serializer to avoid any deviations.
  84. return obj, actual, nil
  85. }