【elasticsearch】序列化与反序列化机制

Elasticsearch 几乎不用 Java 自带的 `java.io.Serializable` 机制,而是自己定义了一套"写-读"协议,核心就是两个接口方法:

  1. `void writeTo(StreamOutput out)`

  2. `static T readFrom(StreamInput in)`

只要一个类实现了这对方法,框架就能把它的字段按顺序写成字节、再按同样的顺序读回来;与 `Serializable` 没有任何关系。

`ClusterState` 以及 `DiscoveryNode`、`IndexMetaData`、`ShardRouting` 等所有集群级对象都遵循这套协议,所以它们虽然没实现 `Serializable`,照样可以在节点之间来回传输并完整重建。


一、ES 的网络层:基于 Netty 的 "Transport" 协议

  • 节点之间所有通信(包括集群状态发布、索引请求、搜索请求)统一走 TCP 传输层,称作 transport。

  • 传输的消息必须实现 `TransportRequest` 或 `TransportResponse` 接口,而这两个接口的唯一要求就是:

"你会把自己写到 `StreamOutput`,也能从 `StreamInput` 读回来"。

  • 因此,ES 里只要出现跨节点的对象,就一定会配套实现 `writeTo`/`readFrom`,否则连编译都过不了。

二、StreamOutput / StreamInput:定制的"高效字节流"

  • 位于 `org.elasticsearch.common.io.stream` 包,本质是包装了 Netty 的 `ByteBuf`。

  • 提供了一系列 `writeVInt`、`writeString`、`writeOptionalWriteable`、`writeMap` 等紧凑方法,支持变长 int、ZigZag 压缩、字典写字符串、版本号兼容等优化,比 Java 默认协议体积小、速度快。

  • 字段顺序一旦约定好就不能随意改动,增删字段时必须根据 `Version.CURRENT` 做兼容分支,以保证滚动升级时老节点也能解析。


三、以 ClusterState 为例:手工可控的"逐字段写读"

下面是极度简化后的伪代码,真实源码在 `ClusterState.java` 及其内部嵌套类:

```java

public class ClusterState implements Writeable { // 注意接口是 Writeable,不是 Serializable

private final long version;

private final String clusterName;

private final DiscoveryNodes nodes;

private final MetaData metaData;

private final RoutingTable routingTable;

...

@Override

public void writeTo(StreamOutput out) throws IOException {

out.writeVLong(version);

out.writeString(clusterName);

nodes.writeTo(out); // 递归调用

metaData.writeTo(out);

routingTable.writeTo(out);

...

}

public static ClusterState readFrom(StreamInput in, DiscoveryNodes nodes) throws IOException {

long version = in.readVLong();

String clusterName = in.readString();

// MetaData、RoutingTable 等同样调用各自的 readFrom

MetaData md = MetaData.readFrom(in);

RoutingTable rt = RoutingTable.readFrom(in);

...

return new ClusterState(version, clusterName, nodes, md, rt, ...);

}

}

```

Master 节点每次发布新集群状态时,就是把整个 `ClusterState` 按上面的顺序写进 `StreamOutput`,然后广播给所有节点;接收方用同样的顺序读回字段,再 `new` 一个新的 `ClusterState` 对象。全过程完全可控,没有反射、没有 Unsafe,也不需要 `Serializable`。


四、为什么要抛弃 Serializable

  1. 性能:Java 原生协议字段描述信息冗余、IO 量大;ES 的 `StreamOutput` 用数字常量标识字段,体积可缩小 35 倍。

  2. 可控:自己写读,可以针对版本号做向前向后兼容,滚动升级不会 break。

  3. 安全:反序列化时不会执行任何 `readObject` 魔术方法,杜绝了 "Java 原生反序列化 Gadget" 攻击面。

  4. 跨语言:虽然 ES 服务端只跑在 JVM,但 REST 层用 JSON,transport 层协议简单明了,方便以后出其他语言客户端。


结论

Elasticsearch 里的类(包括 `ClusterState`)不需要实现 `Serializable`,因为它们全部实现了 ES 自己的 `Writeable` 接口,用 `writeTo`/`readFrom` 这一套"手写"协议完成高效、紧凑、版本兼容的序列化与反序列化。Java 原生的 `ObjectOutputStream` 在 ES 内部根本不会被用到。

`org.elasticsearch.common.io.stream.Writeable` 是 Elasticsearch 8.x(以及 7.x、6.x)统一的序列化/反序列化契约接口,作用等价于 Java 的 `Serializable`,但完全走 ES 自己的 `StreamOutput/StreamInput` 协议。


接口源码(v8.1.0)只有两行核心定义:

```java

public interface Writeable {

void writeTo(StreamOutput out) throws IOException;

interface Reader<T> {

T read(StreamInput in) throws IOException;

}

}

```

  • 实现类负责把自己的字段按顺序写进 `StreamOutput`;

  • 对应的 `Reader<T>`(通常是 `static readFrom(StreamInput)` 方法)负责按同样的顺序读回来并重建对象。


为什么接口里只定义了 `writeTo`,而没有 `readFrom`?

  1. 构造函数/工厂方法签名各不相同,没法统一成接口方法;

  2. ES 约定"每个 `Writeable` 类必须提供一个 `static T readFrom(StreamInput in)`",调用处直接拿方法引用即可,例如:

```java

// Transport 层反序列化时统一调用

ClusterState state = new ClusterState.Reader().read(in);

// 或者更常见的写法

ClusterState state = ClusterState.readFrom(in, nodes);

```


总结

打开的这个 `Writeable.java` 就是 Elasticsearch 唯一且官方的"序列化接口"。

只要你在 ES 源码里看到 `implements Writeable`,就能肯定它一定配好了 `writeTo` 和对应的 `readFrom`,后续所有网络传输、持久化、集群状态广播都靠这对方法完成,与 `java.io.Serializable` 没有任何关系。

相关推荐
q***48413 小时前
SpringBoot整合easy-es
spring boot·后端·elasticsearch
q***766612 小时前
Java_ElasticSearch(ES)——分布式搜索引擎
java·elasticsearch·搜索引擎
Hello.Reader13 小时前
Flink CDC 用 PolarDB-X CDC 实时同步数据到 Elasticsearch
大数据·elasticsearch·flink
weixin_4569042720 小时前
Git大文件管理与版本回退
大数据·git·elasticsearch
天下无敌笨笨熊21 小时前
ES作为向量库研究
大数据·python·elasticsearch
Hello.Reader21 小时前
Flink CDC 用 SqlServer CDC 实时同步数据到 Elasticsearch
elasticsearch·sqlserver·flink
阿里云大数据AI技术1 天前
阿里云 Elasticsearch 的 AI 革新:高性能、低成本、智能化的搜索新纪元
人工智能·elasticsearch·阿里云
w***4241 天前
Springboot中使用Elasticsearch(部署+使用+讲解 最完整)
spring boot·elasticsearch·jenkins
利刃大大1 天前
【c++中间件】Elasticsearch介绍与安装 && 核心概念 && Kibana && 二次封装
c++·elasticsearch·中间件
q***54751 天前
springboot之集成Elasticsearch
spring boot·后端·elasticsearch