欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本篇是《java与es8实战》系列的第三篇,将一些重要的知识点在这里梳理清楚,为后面的实践奠定基础
  • 一共有七个与Java API Client有关的重要知识点
  1. 关于namespace:每个feature都有自己的package
  2. 命名规则:介绍Java API Client中对象的方法的命名规则
  3. 集合不为空:Java API Client中对象返回的集合,到底要不要做判空?
  4. variant type:繁多的场景和对象,可以通过variant type进行简化
  5. 通过JSON字符串创建API对象:通过builder创建复杂的对象,会导致代码也很复杂,这里提供了一种更简单的方式
  6. 关于异常:有哪些异常类型,各自会在什么场景抛出
  • 接下来逐个去看

命名空间

  • 在REST API文档中,数量众多API是按照特性(feature)来分组的,如下图

  • 在ES的Java库Java API Client中,上图中的各种feature被称为namespace

  • 在ES的Java库Java API Client中,与REST API对应的的类和接口都在统一的包名co.elastic.clients.elasticsearch之下,然后再通过下一级package进行分类,这个分类与上图的feature相对应,例如索引相关的,在REST API中的feature是Index APIs,那么在Java API Client中,完整的package就是co.elastic.clients.elasticsearch.indices,这里面有索引操作所需的请求、响应、服务等各种类,如下图

  • 每一个namespace(也就是REST API中的feature),都有自己的client,例如索引相关的操作都有索引专用的client类负责,实例代码如下,client.indices()返回的是ElasticsearchIndicesClient对象,这是索引操作专用的实例

ElasticsearchClient client = ...
client.indices().create(c -> c.index("products"));
  • 展开上述代码的indices()方法,看看其内部实现,如下所示,每次调用indices方法,都会创建一个ElasticsearchIndicesClient对象,对于其他namespace,例如ingest、license亦是如此,都会创建新的实例

  • 看到这里,经验丰富的您应该发现了问题:在大量并发频繁执行各种namespace操作时,会创建大量client对象,这样会影响系统性能吗?
  • es预判了咱们的预判,如下图,官方说这是轻量级对象(very lightweight),所以,理论上可以放心创建,不必担心其对系统造成的压力

  • 尽管每个namespace都有自己的client,但也有例外,就是search和document,它们的代码不在search或者document这样的package下面,而是在core下面,而且可以通过ElasticsearchClient来操作,如下图

命名规则

  • Java API Client是个库,也是个java工程,工程里有自己的内部设计,这算是Java API Client自己的框架部分(framework),另一部分就是专门为使用者提供的大量API
  • 对于API部分,方法的命名规则都是驼峰式(camelCaseNaming),例如查询请求ElasticsearchClient.search()、查询结果的最高评分SearchResponse.maxScore()
  • 对于framework部分,方法命令是下划线作为前缀,例如获取查询类型Query._kind()

五种对象

  • 官方将Java API Client中的对象分为五种
  1. Object mapper:序列化和反序列化工具,这类对象是线程安全、无状态的,通常是单例模式存在于应用中,常在启动时创建
  2. Transport:传输工具,此类对象线程安全,借助底层HTTP客户端工具维护着网络资源,例如负责与es服务端建立连接,在需要关闭连接的时候负责释放所有底层网络资源
  3. Clients:实际处理每个namespace的客户端类,例如负责索引的是ElasticsearchIndicesClient,它们的特点:不可变对象、无状态、线程安全、轻量级(类似于普通bean的资源开销),之所以轻量级,是因为其结构实际上就是对一些API endpoint的包装
  4. Builders:这个在《开篇》中已经详细说明了,就不多赘述,用过builder的您应该会发现,builder当然是可变类,至于是否线程安全似乎和builder没什么关系,因为每创建一次实例时,都要创建一个builder实例,而且,一旦执行完build方法后,这个builder实例就没用了
  5. Requests & other API objects:和请求相关的对象,都是不可变的、线程安全的

集合不会为空

  • 对于单值属性,我们在使用的时候判断是否为空是个常规操作,这样是为了避免直接使用时可能出现的空指针异常
  • 而对于集合,Java API Client 已经确保了API返回的集合非空,我们只需要检查集合中是否有内容,而不必担心集合自身是否等于null的问题,官方给出的演示代码如下
NodeStatistics stats = NodeStatistics.of(b -> b
.total(1)
.failed(0)
.successful(1)
); // The `failures` list was not provided.
// - it's not null
assertNotNull(stats.failures());
  • 出于好奇,去看看NodeStatistics源码,构造方法如下,failures来自ApiTypeHelper.unmodifiable
	private NodeStatistics(Builder builder) {

		this.failures = ApiTypeHelper.unmodifiable(builder.failures);
this.total = ApiTypeHelper.requireNonNull(builder.total, this, "total");
this.successful = ApiTypeHelper.requireNonNull(builder.successful, this, "successful");
this.failed = ApiTypeHelper.requireNonNull(builder.failed, this, "failed"); }
  • 再去看ApiTypeHelper.unmodifiable,如下,已确保了failures不为空
    public static <T> List<T> unmodifiable(@Nullable List<T> list) {
if (list == null) {
return undefinedList();
}
if (list == UNDEFINED_LIST) {
return list;
}
return Collections.unmodifiableList(list);
}
  • 因此,再使用API返回的集合时,集合对象自身始终非空

variant type

  • variant type是Java API Client中常见的对象类型,这个该如何翻译呢,个人觉得是不确定类型的意思,不专业,期待您的指正
  • 举个例子,查询是最常见的操作了,下面列举三种查询,第一个是普通的不分词查询
{
"query":{"term":{ "interests":"youyong"}}
}
  • 分词查询
{
"query":{"match":{"interests": "changge"}}
}
  • 以及复杂的全文本查询
{
"query": {
"intervals" : {
"my_text" : {
"all_of" : {
"ordered" : false,
"intervals" : [
{
"match" : {
"query" : "my favorite books",
"max_gaps" : 0,
"ordered" : true
}
},
{
"any_of" : {
"intervals" : [
{ "match" : { "query" : "java tutorials" } },
{ "match" : { "query" : "cold porridge" } }
]
}
}
]
}
}
}
}
}
  • 对查询来说,在Java API Client中,有个Query对象代表了查询行为,这就是个典型的variant type,至于对应的真实query是哪种,可以在builder时指定,例如下面指定了类型是term
Query query = new Query.Builder()
.term(t -> t
.field("name")
.value(v -> v.stringValue("foo"))
)
.build();
  • 上述query有对应的方法返回其值,例如上面的value可以这样获取
query.term().value().stringValue()
  • 如果在设置的时候,并非用stringValue方法,而是其他类型,那么上面的代码在获取String类型的值时会抛出IllegalStateException异常

  • variant type配有对应的isXXX方法返回其是否属于某个类型,例如Query就有query.isTerm()表示自己是不是term查询

  • 还可以用_kind()返回当前类型,下面是示例

switch(query._kind()) {
case Term:
doSomething(query.term());
break;
case Intervals:
doSomething(query.intervals());
break;
default:
doSomething(query._kind(), query._get());
}
  • 可见有了variant type,在 queries, aggregations, field mappings, analyzers等多种场景下,我们不需要使用各种具体的类,只要用最抽象的variant type,再配置builder pattern即可,这对服务提供者和服务消费者都是有效的简化

通过JSON字符串创建API对象

  • 下面是kibana页面上,用JSON创建索引的操作截图

  • 如果要在代码中实现上述效果,该如何做呢?一层一层的创建mapping、proterties、field对象?那可真是麻烦...
  • 在Java API Client中,可以通过json字符串反序列化为API对象,首先,将上述JSON放入名为some-index.json的文件中,然后执行以下代码,即可用json文件创建req对象
InputStream input = this.getClass()
.getResourceAsStream("some-index.json"); CreateIndexRequest req = CreateIndexRequest.of(b -> b
.index("some-index")
.withJson(input)
); boolean created = client.indices().create(req).acknowledged();
  • 再来段更复杂的,一个API对象,既通过JSON反序列化生成,同时又能调用对象的方法设置一些属性
Reader queryJson = new StringReader(
"{" +
" \"query\": {" +
" \"range\": {" +
" \"@timestamp\": {" +
" \"gt\": \"now-1w\"" +
" }" +
" }" +
" }," +
" \"size\": 100" +
"}"); Reader aggregationJson = new StringReader(
"{" +
" \"size\": 0, " +
" \"aggregations\": {" +
" \"hours\": {" +
" \"date_histogram\": {" +
" \"field\": \"@timestamp\"," +
" \"interval\": \"hour\"" +
" }," +
" \"aggregations\": {" +
" \"max-cpu\": {" +
" \"max\": {" +
" \"field\": \"host.cpu.usage\"" +
" }" +
" }" +
" }" +
" }" +
" }" +
"}"); SearchRequest aggRequest = SearchRequest.of(b -> b
.withJson(queryJson)
.withJson(aggregationJson)
.ignoreUnavailable(true)
); Map<String, Aggregate> aggs = client
.search(aggRequest, Void.class)
.aggregations();

关于异常

  • 在Java API Client中一共有两大类异常
  1. 第一类是由es服务端返回的错误引发的,例如es服务端的校验未通过,或者es服务端自己内部出现异常等,这些情况下抛出的异常是ElasticsearchException
  2. 第二类是因为请求未能成功到达es服务端而引发的,例如网络故障,es服务不可用等,这些情况下抛出的异常是TransportException,这些是lower-level implementation抛出的,有个例外:如果这些问题发生在RestClientTransport对象的方法中,那么抛出的异常类型是ResponseException
  • 以上就是Java API Client相关的重要知识点,在写代码之前先了解它们算是打好基础,然后,接下来精彩的实战篇即将开幕

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

java与es8实战之三:Java API Client有关的知识点串讲的更多相关文章

  1. java版gRPC实战之三:服务端流

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. 《Java Web开发实战》——Java工程师必备干货教材

    一年一度毕业季,又到了简历.offer漫天飞,失望与希望并存的时节.在IT行业,高校毕业生求职时,面临的第一道门槛就是技能与经验的考验,但学校往往更注重学生的理论知识,忽略了对学生实践能力的培养,因而 ...

  3. spark使用idea向yarn提交报错:Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jersey/api/client/config/ClientConfig

    解决方法: 找到1.19版本放到spark的jars目录下

  4. java并发编程实战(java concurrency in practice)

    第一章   线程共享进程范围内的资源,但每个线程都有各自的程序计数器.栈以及局部变量等. 多个线程可以同时调度到多个CPU上运行.   线程的优势? 在服务应用程序中,可以提升资源利用率以及系统吞吐率 ...

  5. [Java聊天室server]实战之三 接收循环

    前言 学习不论什么一个稍有难度的技术,要对其有充分理性的分析,之后果断做出决定---->也就是人们常说的"多谋善断":本系列尽管涉及的是socket相关的知识.但学习之前,更 ...

  6. Java 9 揭秘(14. HTTP/2 Client API)

    Tips 做一个终身学习的人. 在此章中,主要介绍以下内容: 什么是HTTP/2 Client API 如何创建HTTP客户端 如何使HTTP请求 如何接收HTTP响应 如何创建WebSocket的e ...

  7. springCloud 服务注册启动报错<com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect>

    报错:com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: ...

  8. springCloud com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect

    1.com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: c ...

  9. Eureka服务注册中心相关错误com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect

    启动项目报错如下 原因: 在默认设置下,Eureka服务注册中心也会将自己作为客户端来尝试注册它自己,所以会出现 com.sun.jersey.api.client.ClientHandlerExce ...

  10. 【Hadoop】HA 场景下访问 HDFS JAVA API Client

    客户端需要指定ns名称,节点配置,ConfiguredFailoverProxyProvider等信息. 代码示例: package cn.itacst.hadoop.hdfs; import jav ...

随机推荐

  1. vue全家桶进阶之路50:Vue3 环境变量+跨域设置实例

    使用.env加后缀的方式来建立某个模式下的环境变量, 例如:项目根目录新建两个环境变量文件(development开发环境和production生产环境): .env.development .env ...

  2. 安装vue cli3以及配置环境 镜像下载

    安装vue cli3以及配置环境 镜像下载 1.下载安装Node.js 安装vue cli3之前需要先安装Node.js,方便对vue进行下载 node.js的下载与安装方法在隔壁哟 地址:https ...

  3. 【GiraKoo】Java Native Interface(JNI)的空间(引用)管理

    Java Native Interface(JNI)的空间(引用)管理 Java是通过垃圾回收机制回收内存,C/C++是通过malloc,free,new,delete手动管理空间.那么在JNI层,同 ...

  4. 2023 5.14 虚拟环境安装Linux

    1.安装配置VM虚拟机 vmare workstation 虚拟机是一款桌面计算机虚拟软件 让用户能够在单一主机上同事运行多个操作系统 1.每个虚拟操作系统的硬盘与数据都是独立 2.多台虚拟机可以构建 ...

  5. Doris(五) -- 数据的导入导出

    数据导入 使用 Insert 方式同步数据 用户可以通过 MySQL 协议,使用 INSERT 语句进行数据导入 INSERT 语句的使用方式和 MySQL 等数据库中 INSERT 语句的使用方式类 ...

  6. 编码器 | 基于 Transformers 的编码器-解码器模型

    基于 transformer 的编码器-解码器模型是 表征学习 和 模型架构 这两个领域多年研究成果的结晶.本文简要介绍了神经编码器-解码器模型的历史,更多背景知识,建议读者阅读由 Sebastion ...

  7. 20个Golang片段让我不再健忘

    前言 本文使用代码片段的形式来解释在 go 语言开发中经常遇到的小功能点,由于本人主要使用 java 开发,因此会与其作比较,希望对大家有所帮助. 1. hello world 新手村的第一课,毋庸置 ...

  8. 300行代码模拟cdn

    这一生听过许多道理,但还是过不好这一生,这是因为缺少真正的动手实践,光听道理,缺少动手实践的过程,学习难免会让人觉得味同嚼蜡,所以我的分享都比较倾向于实践,在一次次动手实践的过程中感受知识原本纯真的模 ...

  9. memcached使用中踩的一些坑

    背景 线上启用memcached(以下简称mc)作为热点缓存组件已经多年,其稳定性和性能都经历住了考验,这里记录一下踩过的几个坑. 大key存储 某年某月某日,观察mysql的读库CPU占比有些异常偏 ...

  10. 企业级logstash简单使用(ELK)

    企业级logstash简单使用(ELK) 要使用logstash收集到Elasticsearch的方式,需确保logstash版本与es版本一致. 由于我也是刚刚研究使用,所以本文暂不会出现原理性的东 ...