歡迎訪問(wèn)我的GitHub
這里分類和匯總了欣宸的全部原創(chuàng)(含配套源碼):https://github.com/zq2599/blog_demos
本篇概覽
- 本篇是《java與es8實(shí)戰(zhàn)》系列的第三篇,將一些重要的知識(shí)點(diǎn)在這里梳理清楚,為后面的實(shí)踐奠定基礎(chǔ)
- 一共有七個(gè)與Java API Client有關(guān)的重要知識(shí)點(diǎn)
- 關(guān)于namespace:每個(gè)feature都有自己的package
- 命名規(guī)則:介紹Java API Client中對(duì)象的方法的命名規(guī)則
- 集合不為空:Java API Client中對(duì)象返回的集合,到底要不要做判空?
- variant type:繁多的場(chǎng)景和對(duì)象,可以通過(guò)variant type進(jìn)行簡(jiǎn)化
- 通過(guò)JSON字符串創(chuàng)建API對(duì)象:通過(guò)builder創(chuàng)建復(fù)雜的對(duì)象,會(huì)導(dǎo)致代碼也很復(fù)雜,這里提供了一種更簡(jiǎn)單的方式
- 關(guān)于異常:有哪些異常類型,各自會(huì)在什么場(chǎng)景拋出
- 接下來(lái)逐個(gè)去看
命名空間
-
在REST API文檔中,數(shù)量眾多API是按照特性(feature)來(lái)分組的,如下圖
-
在ES的Java庫(kù)Java API Client中,上圖中的各種feature被稱為namespace
-
在ES的Java庫(kù)Java API Client中,與REST API對(duì)應(yīng)的的類和接口都在統(tǒng)一的包名co.elastic.clients.elasticsearch之下,然后再通過(guò)下一級(jí)package進(jìn)行分類,這個(gè)分類與上圖的feature相對(duì)應(yīng),例如索引相關(guān)的,在REST API中的feature是Index APIs,那么在Java API Client中,完整的package就是co.elastic.clients.elasticsearch.indices,這里面有索引操作所需的請(qǐng)求、響應(yīng)、服務(wù)等各種類,如下圖
-
每一個(gè)namespace(也就是REST API中的feature),都有自己的client,例如索引相關(guān)的操作都有索引專用的client類負(fù)責(zé),實(shí)例代碼如下,client.indices()返回的是ElasticsearchIndicesClient對(duì)象,這是索引操作專用的實(shí)例
ElasticsearchClient client = ...
client.indices().create(c -> c.index("products"));
- 展開(kāi)上述代碼的indices()方法,看看其內(nèi)部實(shí)現(xiàn),如下所示,每次調(diào)用indices方法,都會(huì)創(chuàng)建一個(gè)ElasticsearchIndicesClient對(duì)象,對(duì)于其他namespace,例如ingest、license亦是如此,都會(huì)創(chuàng)建新的實(shí)例
- 看到這里,經(jīng)驗(yàn)豐富的您應(yīng)該發(fā)現(xiàn)了問(wèn)題:在大量并發(fā)頻繁執(zhí)行各種namespace操作時(shí),會(huì)創(chuàng)建大量client對(duì)象,這樣會(huì)影響系統(tǒng)性能嗎?
- es預(yù)判了咱們的預(yù)判,如下圖,官方說(shuō)這是輕量級(jí)對(duì)象(very lightweight),所以,理論上可以放心創(chuàng)建,不必?fù)?dān)心其對(duì)系統(tǒng)造成的壓力
- 盡管每個(gè)namespace都有自己的client,但也有例外,就是search和document,它們的代碼不在search或者document這樣的package下面,而是在core下面,而且可以通過(guò)ElasticsearchClient來(lái)操作,如下圖
命名規(guī)則
- Java API Client是個(gè)庫(kù),也是個(gè)java工程,工程里有自己的內(nèi)部設(shè)計(jì),這算是Java API Client自己的框架部分(framework),另一部分就是專門為使用者提供的大量API
- 對(duì)于API部分,方法的命名規(guī)則都是駝峰式(camelCaseNaming),例如查詢請(qǐng)求ElasticsearchClient.search()、查詢結(jié)果的最高評(píng)分SearchResponse.maxScore()
- 對(duì)于framework部分,方法命令是下劃線作為前綴,例如獲取查詢類型Query._kind()
五種對(duì)象
- 官方將Java API Client中的對(duì)象分為五種
- Object mapper:序列化和反序列化工具,這類對(duì)象是線程安全、無(wú)狀態(tài)的,通常是單例模式存在于應(yīng)用中,常在啟動(dòng)時(shí)創(chuàng)建
- Transport:傳輸工具,此類對(duì)象線程安全,借助底層HTTP客戶端工具維護(hù)著網(wǎng)絡(luò)資源,例如負(fù)責(zé)與es服務(wù)端建立連接,在需要關(guān)閉連接的時(shí)候負(fù)責(zé)釋放所有底層網(wǎng)絡(luò)資源
- Clients:實(shí)際處理每個(gè)namespace的客戶端類,例如負(fù)責(zé)索引的是ElasticsearchIndicesClient,它們的特點(diǎn):不可變對(duì)象、無(wú)狀態(tài)、線程安全、輕量級(jí)(類似于普通bean的資源開(kāi)銷),之所以輕量級(jí),是因?yàn)槠浣Y(jié)構(gòu)實(shí)際上就是對(duì)一些API endpoint的包裝
- Builders:這個(gè)在《開(kāi)篇》中已經(jīng)詳細(xì)說(shuō)明了,就不多贅述,用過(guò)builder的您應(yīng)該會(huì)發(fā)現(xiàn),builder當(dāng)然是可變類,至于是否線程安全似乎和builder沒(méi)什么關(guān)系,因?yàn)槊縿?chuàng)建一次實(shí)例時(shí),都要?jiǎng)?chuàng)建一個(gè)builder實(shí)例,而且,一旦執(zhí)行完build方法后,這個(gè)builder實(shí)例就沒(méi)用了
- Requests & other API objects:和請(qǐng)求相關(guān)的對(duì)象,都是不可變的、線程安全的
集合不會(huì)為空
- 對(duì)于單值屬性,我們?cè)谑褂玫臅r(shí)候判斷是否為空是個(gè)常規(guī)操作,這樣是為了避免直接使用時(shí)可能出現(xiàn)的空指針異常
- 而對(duì)于集合,Java API Client 已經(jīng)確保了API返回的集合非空,我們只需要檢查集合中是否有內(nèi)容,而不必?fù)?dān)心集合自身是否等于null的問(wèn)題,官方給出的演示代碼如下
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源碼,構(gòu)造方法如下,failures來(lái)自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返回的集合時(shí),集合對(duì)象自身始終非空
variant type
- variant type是Java API Client中常見(jiàn)的對(duì)象類型,這個(gè)該如何翻譯呢,個(gè)人覺(jué)得是不確定類型的意思,不專業(yè),期待您的指正
- 舉個(gè)例子,查詢是最常見(jiàn)的操作了,下面列舉三種查詢,第一個(gè)是普通的不分詞查詢
{
"query":{"term":{ "interests":"youyong"}}
}
- 分詞查詢
{
"query":{"match":{"interests": "changge"}}
}
- 以及復(fù)雜的全文本查詢
{
"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" } }
]
}
}
]
}
}
}
}
}
- 對(duì)查詢來(lái)說(shuō),在Java API Client中,有個(gè)Query對(duì)象代表了查詢行為,這就是個(gè)典型的variant type,至于對(duì)應(yīng)的真實(shí)query是哪種,可以在builder時(shí)指定,例如下面指定了類型是term
Query query = new Query.Builder()
.term(t -> t
.field("name")
.value(v -> v.stringValue("foo"))
)
.build();
- 上述query有對(duì)應(yīng)的方法返回其值,例如上面的value可以這樣獲取
query.term().value().stringValue()
-
如果在設(shè)置的時(shí)候,并非用stringValue方法,而是其他類型,那么上面的代碼在獲取String類型的值時(shí)會(huì)拋出IllegalStateException異常
-
variant type配有對(duì)應(yīng)的isXXX方法返回其是否屬于某個(gè)類型,例如Query就有query.isTerm()表示自己是不是term查詢
-
還可以用_kind()返回當(dāng)前類型,下面是示例文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-677169.html
switch(query._kind()) {
case Term:
doSomething(query.term());
break;
case Intervals:
doSomething(query.intervals());
break;
default:
doSomething(query._kind(), query._get());
}
- 可見(jiàn)有了variant type,在 queries, aggregations, field mappings, analyzers等多種場(chǎng)景下,我們不需要使用各種具體的類,只要用最抽象的variant type,再配置builder pattern即可,這對(duì)服務(wù)提供者和服務(wù)消費(fèi)者都是有效的簡(jiǎn)化
通過(guò)JSON字符串創(chuàng)建API對(duì)象
- 下面是kibana頁(yè)面上,用JSON創(chuàng)建索引的操作截圖
- 如果要在代碼中實(shí)現(xiàn)上述效果,該如何做呢?一層一層的創(chuàng)建mapping、proterties、field對(duì)象?那可真是麻煩...
- 在Java API Client中,可以通過(guò)json字符串反序列化為API對(duì)象,首先,將上述JSON放入名為some-index.json的文件中,然后執(zhí)行以下代碼,即可用json文件創(chuàng)建req對(duì)象
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();
- 再來(lái)段更復(fù)雜的,一個(gè)API對(duì)象,既通過(guò)JSON反序列化生成,同時(shí)又能調(diào)用對(duì)象的方法設(shè)置一些屬性
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();
關(guān)于異常
- 在Java API Client中一共有兩大類異常
- 第一類是由es服務(wù)端返回的錯(cuò)誤引發(fā)的,例如es服務(wù)端的校驗(yàn)未通過(guò),或者es服務(wù)端自己內(nèi)部出現(xiàn)異常等,這些情況下拋出的異常是ElasticsearchException
- 第二類是因?yàn)檎?qǐng)求未能成功到達(dá)es服務(wù)端而引發(fā)的,例如網(wǎng)絡(luò)故障,es服務(wù)不可用等,這些情況下拋出的異常是TransportException,這些是lower-level implementation拋出的,有個(gè)例外:如果這些問(wèn)題發(fā)生在RestClientTransport對(duì)象的方法中,那么拋出的異常類型是ResponseException
- 以上就是Java API Client相關(guān)的重要知識(shí)點(diǎn),在寫代碼之前先了解它們算是打好基礎(chǔ),然后,接下來(lái)精彩的實(shí)戰(zhàn)篇即將開(kāi)幕
歡迎關(guān)注博客園:程序員欣宸
學(xué)習(xí)路上,你不孤單,欣宸原創(chuàng)一路相伴...文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-677169.html
到了這里,關(guān)于java與es8實(shí)戰(zhàn)之三:Java API Client有關(guān)的知識(shí)點(diǎn)串講的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!