考古|ES 5.0 的 Java 客户端要重新依赖,transport client 单独成包

举报
岳涛 发表于 2026/11/10 08:35:41 2026/11/10
【摘要】 5.0 的 Java API 变更里,最容易被注意到的是三处……

5.0 的 Java API 变更里,最容易被注意到的是三处:transport client 从核心拆成独立 artifact(org.elasticsearch.client:transport),构造方式换成 PreBuiltTransportClient;count API 删除,改用 size(0) 的 search;suggest API 删除,改用 search 的 suggest 选项。同一页还有一批更琐碎但同样会编译失败的改动:SearchSourceBuilder 这类构建器去掉了接收 XContentBuilder、Map、bytes[] 的重载,写入请求的 setWriteConsistencyLevel 换成 setWaitForActiveShards,DocumentAlreadyExistsException 删除,以及 ES 不再自动探测日志实现。本文对照 v5.6.16 逐条列出。

说明

实测环境:本地 Elasticsearch 5.6.16 官方 tar 包(配 .eslab/jdk 下的 JDK 8),逐个用不同 -E 参数拉起独立单节点实例(独立 path.data 与端口 9761–9764),每次等 11 秒看进程是否还在,跑完进程已清理。

来源和边界先交代:本文的依据出自 v5.6.16 标签下的官方文件(java.asciidoc、PreBuiltTransportClient.java、SearchHits.java),文中引用的原文均来自这些文件。示例取自官方示例;哪些是文档写明、哪些是我的推断,正文里逐处标注。

01-java

图 1:官方文件 java.asciidoc(v5.6.16 标签)。来源:GitHub elastic/elasticsearch v5.6.16 java.asciidoc

Transport client 独立成模块

5.0 迁移文档(migrate_5_0/java.asciidoc 第 7–29 行):

The Java transport client has been moved to its own module which can be referenced using:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>5.0.0</version>
</dependency>

构造方式也变了:

TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host1"), 9300))
        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host2"), 9300));

以前引 elasticsearch 依赖就能用 transport client,5.0 起要单独引 transport。

PreBuiltTransportClient 这个名字的含义可以从源码确认(PreBuiltTransportClient 第 90–97 行):它预先装好一组插件,5.6.16 里是 Netty3Plugin、Netty4Plugin、ReindexPlugin、PercolatorPlugin、MustachePlugin、ParentJoinPlugin。所以"PreBuilt"指的是预装插件,调用方不用自己拼插件列表。它还提供了带额外插件的构造重载(第 106–118 行)。另外它的静态初始化会关掉 Netty 的若干默认行为(第 68–80 行):禁用 unsafe、不替换 selector key set、把 recycler 每线程容量设为 0,这些可以通过系统属性覆盖。

02-PreBuiltTransportClient

图 2:官方文件 PreBuiltTransportClient.java(v5.6.16 标签)。来源:GitHub elastic/elasticsearch v5.6.16 PreBuiltTransportClient.java

Count API 删除

The deprecated count api has been removed from the Java api, use the search api instead and set size to 0.

原来:

client.prepareCount(indices).setQuery(query).get;

改成:

client.prepareSearch(indices).setSource(new SearchSourceBuilder.size(0).query(query)).get;

这对应 5.0 里 search_type=count 的移除:count 的语义完全能用 size: 0 的 search 表达,独立 API 没有存在必要。

改完要注意响应解析的变化:count 响应读 getCount,search 响应读 getHits.getTotalHits。v5.6.16 里 SearchHits.totalHits 是 long(SearchHits.java 第 51 行),所以这里的类型对得上。后续大版本把它换成了带 relation 的对象,那是之后的事,本文没有逐版本核对。

03-SearchHits

图 3:官方文件 SearchHits.java(v5.6.16 标签)。来源:GitHub elastic/elasticsearch v5.6.16 SearchHits.java

Suggest API 删除

The suggest api has been removed from the Java api, use the suggest option in search api, it has been optimized for suggest-only request.

原来:

client.prepareSuggest(indices).addSuggestion("foo", SuggestBuilders.completionSuggestion("field").text("s")).get;

改成:

client.prepareSearch(indices).suggest(new SuggestBuilder.addSuggestion("foo",
        SuggestBuilders.completionSuggestion("field").text("s"))).get;

官方说这条路径"针对只有 suggest 的请求做过优化",所以不用担心走 search 会多算命中。

prepareCount、prepareSuggest 在 v5.6.16 的 Client 接口里确实没有了,本文按源码确认过这一点。

同一页里另外几条会编译失败的改动

这几条原文没写,但同样属于这一次迁移的范围(都在同一页):

  • 日志实现不再自动探测(第 68–76 行):ES 现在用 Log4j 2,以前 Log4j 不在 classpath 时会降级到 SLF4J 或 Java logging,现在不行——没有 Log4j 2 API 就跑不起来。用 Java 客户端时 log4j-over-slf4j 桥接应该可用,仍然依赖 Log4j 1 API 的第三方依赖用 log4j-1.2-api 桥接;
  • DocumentAlreadyExistsException 删除(第 89–93 行):换成 VersionConflictEngineException,报错描述更清楚。影响用 IndexRequest.opType 或 IndexRequest 做"不存在才写入"的代码;
  • setWriteConsistencyLevel 换成 setWaitForActiveShards(第 95–113 行):原因是写入一致性语义含糊,它只检查操作开始前有足够分片可用,不保证操作结束时数据已复制到那些副本。新方法接受总数以内的数值或 ActiveShardCount.ALL,默认只等主分片。影响 IndexRequest、BulkRequest、UpdateRequest、DeleteRequest 及各自的 Builder;
  • 构建器去掉 XContentBuilder / Map / bytes[] 重载(第 289–331 行):SearchSourceBuilder、SearchRequestBuilder、SearchRequest、AggregationBuilder 都改成只接收对应的 builder 对象(HighlightBuilder、AggregationBuilder、SuggestBuilder 等),好处是请求在调用时就能校验,报错更清楚。同一段还删掉了 setExtraSource、setTemplateSource、SearchRequest 上的 source 系列方法,模板查询改用 SearchTemplateRequest;
  • Query Builders 一批构造参数变必填(第 115–288 行):BoostingQueryBuilder 的 positive/negative、SpanContainingQueryBuilder 的 big/little、SpanNearQueryBuilder 的 slop 等改到构造函数里且不能为 null,配套的 QueryBuilders 静态方法也跟着改。SpanOrQueryBuilder 加子句的方法从 clause(...) 改名为 addClause(...);
  • SearchResponse 里 string 字段的排序值类型变了(第 321–324 行):从 org.elasticsearch.common.text.Text 变成 java.lang.String。

这一页的条目有几十项,上面列出的是改动面较大的几类。逐条核对请以迁移文档为准。

真正的决策是"还要不要用 transport client"

换 artifact、换构造方式、count 和 suggest 换写法,这些都不难,编译器会逐个列出来。需要想清楚的是另一件事:这个客户端你还打算用多久。

事实是 5.0 把它拆成独立模块,之后它被标记废弃,官方主推的方向变成 REST 客户端。transport client 在 7.0 标记废弃、8.0 移除,这是公开的版本演进事实(本文没有逐版本核对时间线)。所以这次迁移做完,几年后很可能还要再迁一次。

我的建议是:如果改动量不大,直接换成 REST 客户端;如果确实大,至少把 transport client 的调用收敛到单一模块,别散落在业务代码各处。判断的出发点是技术选型要看终点——一个在官方规划里要被取代的组件,继续在上面投入等于给自己排下一次迁移。

迁移步骤

  1. 构建里加 org.elasticsearch.client:transport,把 new TransportClient(...) 改成 new PreBuiltTransportClient(...);
  2. 全量替换 prepareCount(...),响应解析从 getCount 改成 getHits.getTotalHits;
  3. 全量替换 prepareSuggest(...),改用 prepareSearch(...).suggest(...);
  4. 处理上面几类改动:setWriteConsistencyLevel 换 setWaitForActiveShards、DocumentAlreadyExistsException 的 catch、构建器里传 XContentBuilder / Map / bytes[] 的调用、Query Builders 的构造参数;
  5. 确认 classpath 里有 Log4j 2 API,必要时配桥接;
  6. 确认客户端版本与集群版本一致;
  7. 项目里如果还留着内嵌节点那套写法(node.client),节点角色已经改成 node.master / node.data / node.ingest 分开配置,一并处理。

怎么找改动点

# transport client 构造(旧写法)
grep -rn "new TransportClient(" --include=*.java . | head -20

# count / suggest 的 Java API 调用
grep -rn "prepareCount\|prepareSuggest" --include=*.java . | head -20

# 写入请求的一致性设置
grep -rn "setWriteConsistencyLevel" --include=*.java . | head -20

# 依赖里的 client artifact
grep -rn "org.elasticsearch.client" --include=pom.xml --include=*.gradle . | head -20

前三条是文本匹配,命中后要人看上下文。验证方式很直接:编译一遍。这些都是编译期可见的改动(类没了、方法签名变了、构造参数不够),编译器会把漏改的地方列出来。改完跑一遍集成测试,确认总数和以前一致。

版本对齐

transport client 的实际约束是客户端与集群版本要对齐,集群升了客户端就要跟着升,否则可能连不上或行为异常。官方对"必须完全一致"的表述本文没有查到确切出处,落地时以你所用版本的 Java 客户端文档为准。

这也是我建议评估 REST 客户端的原因之一:通过 HTTP 访问对版本的要求宽松得多,升级时不用被迫同步发版。

如果暂时还得用 transport client,可以做的几件事:

  • 把客户端版本做成依赖管理的单一来源,别散落在各个服务的 pom / gradle 里;
  • 升级集群前先列出所有客户端,确认能一起升;
  • 在 CI 里加一条版本一致性检查,避免某个服务用了旧版本。

这几条能挡住"集群升完了、某个服务连不上"这类问题。

多服务一起升级时,先升一个非关键服务观察一段时间(连得通、查询正常、没有序列化异常),再批量铺开。旧版本的客户端 jar 保留一段时间:transport client 与集群版本强绑定,集群升完而客户端没跟上时,回滚往往只能把客户端版本降回去,升级前确认旧版本还能拿到。

在 5.6.16 上跑一遍

Java API 那几条要编译源码才能验,但正文末尾提到的节点角色设置可以放到启动期直接试。

04-node-client-removed

图 4:5.6.16 带 -Enode.client=true 启动,报 unknown setting [node.client] please check that any required plugins are installed, or check the breaking changes documentation for removed settings。本地实测输出。

05-new-role-settings

图 5:同一个 5.6.16 上,-Enode.master=false、-Enode.data=false、-Enode.ingest=true 三个设置都能让节点正常启动。本地实测输出。

JAVA_HOME=<jdk8> ES_JAVA_OPTS='-Xms512m -Xmx512m' ./bin/elasticsearch \
  -Ediscovery.type=single-node -Ehttp.port=9761 -Enode.client=true ...
# unknown setting [node.client] ...

curl -s '' | jq -r '.nodes[].roles | join(",")'
# 新角色设置下节点正常返回角色列表

一个是「未知设置、启动即失败」,另外三个是「收下并照常启动」。这跟正文里那句「节点角色已经改成 node.master / node.data / node.ingest 分开配置」对得上:旧的一体化客户端节点开关在 5.x 已经不存在,想配成不存数据、只做协调的节点,得用三个布尔设置组合出来。

结论是升级脚本里但凡还留着 node.client,节点连启动这一关都过不去,属于最容易发现的一类;真正容易漏的是依赖 transport client 的 Java 代码——编译期就报错,但没有运行期信号。我的建议是先把部署配置里的节点角色设置全量 grep 一遍,再处理 Java 依赖,两处一起改完再启动。

边界:

  • 图 4、图 5 出自 5.6.16 的启动期检查,只判断进程是否存活,没有读取节点的实际角色列表或验证请求路由;
  • 本文其余条目(PreBuiltTransportClient、count/suggest API 删除、构建器重载、setWaitForActiveShards、日志实现探测)是 Java 编译期改动,本地没有搭 Java 工程,均未实测;
  • 5.0 之前没有对应版本二进制,node.client 曾经生效的形态未做运行期对照。

写在最后

这次 Java API 的改动分成几层:transport client 独立成模块、count 和 suggest 两个入口删除、一批构建器和请求对象的方法签名变化、日志实现和异常类型的调整。前三层编译期可见,改起来快。

改动本身不是难点。值得借这次机会评估的是这个客户端还要不要用——它在此后的版本里被标记废弃并移除,继续投入等于给几年后再排一次迁移。如果暂时不换,至少把调用收敛到一处、把版本做成单一来源,这两步成本不高。

参考

本文对照 Elasticsearch v5.6.16 标签的官方文档核对,引文逐字取自原文。「在 5.6.16 上跑一遍」里已实际用启动参数验证节点角色设置:node.client=true 报 unknown setting [node.client] 并启动失败,node.master=false / node.data=false / node.ingest=true 三个都正常启动(见该节)。Java 客户端依赖、count/suggest API 删除、构建器重载等编译期改动需要 Java 工程,本机未搭建、未实测;5.0 之前的形态未做运行期对照。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。