【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` 没有任何关系。

相关推荐
Elasticsearch3 小时前
Elastic AI agent builder 介绍
elasticsearch
Elasticsearch5 小时前
MCP 工具:自主代理的攻击向量与防御建议
elasticsearch
和光同尘20237 小时前
CentOS7搭建ELK日志分析系统
运维·elasticsearch·云原生·kubernetes·centos·kibana·logstash
Elastic 中国社区官方博客9 小时前
理解 Elasticsearch 中的分块策略
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
企鹅侠客11 小时前
ElasticSearch-提高篇
大数据·elasticsearch·jenkins
llf_cloud20 小时前
Elasticsearch8容器化部署
elasticsearch·docker·kibana
小样还想跑1 天前
UniApp ConnectSocket连接websocket
websocket·elasticsearch·uni-app
whltaoin1 天前
SpringCloud 项目阶段十:kafka实现双端信息同步以及ElasticSearch容器搭建示例
elasticsearch·spring cloud·kafka
Elasticsearch1 天前
Elastic 获得 2025 年最佳 AI 辅助支持应用奖
elasticsearch