TransportClient详细说一说
好的,我们来详细说一说 Elasticsearch 的 **TransportClient
**。
核心定位:历史功臣,现已退休
TransportClient
是 Elasticsearch 官方提供的、早期的 Java 客户端,用于连接到 Elasticsearch 集群并执行操作(索引、搜索、管理数据等)。它曾经是 Java 应用集成 Elasticsearch 的推荐方式,但在 Elasticsearch 5.x 版本后被标记为废弃,并在 7.0 版本中完全移除。
为什么需要了解它?
- 维护旧代码库: 如果你在维护运行在 ES 5.x 或更早版本上的应用代码,你肯定会遇到它。
- 理解历史文档/教程: 网上有很多基于它的旧资料。
- 了解演进过程: 理解为什么它被替代有助于理解现代客户端的设计哲学。
TransportClient 的核心特性和工作方式
基于传输层(Transport Layer):
- 它不使用 HTTP/REST API。
- 它直接与 Elasticsearch 集群中的节点建立二进制 TCP 连接。
- 通信协议是Elasticsearch原生的传输协议(基于 Netty 实现),而不是 HTTP。这通常在相同的硬件和网络条件下可以提供比 HTTP 更高的性能和更低的延迟(尤其是在大量操作时),因为协议更紧凑,开销更小。
节点发现与连接管理:
- 初始连接: 你需要提供一个或多个种子节点的地址(通常是集群中几个节点的主机名/IP 和端口,默认是
9300
)。端口9300
是节点间内部通信的默认传输端口。 - 集群感知:
TransportClient
启动后会连接到这些种子节点。 - 获取集群状态: 通过这些种子节点,它可以获取集群完整的拓扑信息(包括所有数据节点、主节点等的地址)。
- 维护连接池: 它会对已知的数据节点维护一个连接池,以便后续请求使用。
- 动态感知: 集群发生变化时(节点加入/离开),
TransportClient
能感知到(虽然有时会有短暂的延迟)并调整其连接池。
- 初始连接: 你需要提供一个或多个种子节点的地址(通常是集群中几个节点的主机名/IP 和端口,默认是
请求分发 (Load Balancing/Failover):
- 当你的应用代码通过
TransportClient
发出请求(例如client.prepareIndex(...).execute()
或client.prepareSearch(...).execute()
)时,客户端会负责:- 根据请求的类型(写操作通常路由到主分片所在节点,读操作可以路由到任意拥有分片副本的节点)和配置的负载均衡策略(如 round-robin、基于 ping 响应时间的策略等)。
- 从连接池中选择一个合适的节点发送请求。
- 如果请求失败,它会自动尝试发送到另一个合适的节点(Failover)。
- 对应用代码屏蔽集群内部节点通信的复杂性。开发者无需手动选择将请求发送给哪个节点。
- 当你的应用代码通过
异步与非阻塞:
- 执行方法通常返回一个
ActionFuture
(支持.actionGet()
同步等待结果)。 - 更现代的方式是通过添加
ActionListener
参数来提供回调函数,实现完全的异步非阻塞调用。
- 执行方法通常返回一个
序列化:
- 使用高效的二进制序列化(如 Thrift, MessagePack),而不是 JSON over HTTP。这减少了数据大小和解析开销。
模块化与扩展:
- 你可以添加插件(如
x-pack
的安全模块)来扩展其功能(如认证、加密)。 - 支持配置连接池大小、超时、SSL/TLS、嗅探器行为等。
- 你可以添加插件(如
一个简单的工作流程示例(索引文档)
// 1. 创建客户端实例 (旧方式)
TransportClient client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("node1.mydomain.com"), 9300)) // 种子节点 1.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("node2.mydomain.com"), 9300)); // 种子节点 2// 2. 准备索引请求
IndexResponse response = client.prepareIndex("my_index", "_doc", "1") // 索引名,类型名(6.x后基本弃用),文档ID.setSource(jsonBuilder().startObject().field("user", "kimchy").field("message", "trying out Elasticsearch").endObject()).get(); // .get() 是同步等待响应,也可以使用 .execute() + Listener 异步// 3. 处理响应(检查状态等)
if (response.status() == RestStatus.CREATED) {// ...
}// 4. 关闭客户端
client.close();
为什么它被废弃和移除?(关键原因)
版本绑定紧密 (Biggest Issue):
TransportClient
必须与目标 Elasticsearch 集群的主版本号严格匹配(例如,5.6 的客户端只能连 5.x 集群,不能连 6.x 或 7.x)。这给集群升级带来了巨大的痛苦,要求应用及其依赖的客户端库必须同时升级。- 服务器端协议的任何微小改动都可能破坏客户端的兼容性。维护 ES 团队需要为每个版本的服务器维护对应的各个语言的客户端。
双重维护负担:
- Elasticsearch 团队需要维护两套暴露给用户的 API:传输层协议和 HTTP/REST API。移除
TransportClient
允许他们将精力集中在改进和维护更通用的、跨语言的 HTTP/REST API 上。
- Elasticsearch 团队需要维护两套暴露给用户的 API:传输层协议和 HTTP/REST API。移除
HTTP/REST API 的成熟与优化:
- 随着 HTTP/2 的普及、更好的序列化技术和网络栈优化(Elasticsearch 现在使用 Netty for HTTP too),HTTP 协议的性能差距已经显著缩小,很多场景下可以接受或差异不大。
- HTTP 协议具有无与伦比的通用性和可调试性(
curl
, Kibana Console, 浏览器插件),开发者友好,跨语言支持完美。
安全性:
- HTTP 协议上配置 TLS 加密和认证(Basic Auth, PKI, Token)是标准、成熟的做法。
- 原生传输协议上的安全配置(X-Pack Security)不如 HTTP 标准化和广泛支持。
部署灵活性:
- 在云/Kubernetes 环境中,将集群暴露给外部应用通常通过 HTTP LB/Ingress 进行。让应用通过传输协议直接连接后端节点,绕过这些负载均衡器,会引入部署和维护的复杂性,并可能破坏 LB 的健康检查等功能。
- HTTP API 可以轻松地放在负载均衡器之后。
集群内部的强耦合:
TransportClient
需要知道集群内部节点的地址。在某些网络配置下(如 VPC peering, different security groups)可能会增加配置难度。
TransportClient 的现代替代品
官方推荐并积极维护的替代客户端是:
- Java High Level REST Client: (在 ES 7.15 及更早版本是主流推荐)
- 通过 HTTP (端口
9200
) 与集群交互。 - 接受并返回领域对象(Request/Response objects)。
- 支持异步 (
async()
+ActionListener
回调)。 - 屏蔽了 HTTP 连接的细节(像
TransportClient
屏蔽传输连接一样)。 - 版本要求更宽松: 客户端版本可以连接到等于或低于其版本的 ES 集群(例如,7.15 的客户端可以连 7.x 的所有版本)。升级更平滑。
- 通过 HTTP (端口
- Java API Client (New, preferred in 8.x+): (从 ES 7.15 开始提供,8.x 后更成熟)
- 同样基于 HTTP。
- 使用强类型、面向对象的方法来构建请求和解析响应(
IndexRequest.withJson()
取代了很多字符串拼接)。 - 更好地利用了 Java 的语言特性(泛型、流式 API),提供了更好的类型安全和 IDE 支持。
- 进一步抽象和简化了接口。
- 使用 JSON 编组库(Jackson)进行高效的(反)序列化。
- 同样遵循主版本号一致规则(8.x客户端连8.x集群),但内部机制通过HTTP,版本管理和维护成本显著低于旧的TransportClient。
总结
TransportClient
是一个时代的产品,它通过高效的二进制协议和集群感知能力为早期Java应用接入Elasticsearch提供了强大的解决方案。然而,其与ES服务器版本的紧密绑定、维护负担、以及HTTP/REST API技术栈的成熟,最终导致了它被废弃并移除。现代应用开发应避免使用TransportClient
,转而使用基于HTTP/REST API的Java High Level REST Client (兼容旧项目) 或更新的、面向对象的 Java API Client (推荐用于新项目)。它们提供了几乎相同的易用性(甚至更优的API设计)、良好的性能、更高的安全性、跨语言兼容性和更平滑的升级路径。