0.学习目标
- 了解集群和分布式概念
- 会部署elasticsearch集群
- 学习HighLevelRestClient操作ES
- 掌握SpringDataElasticsearch操作ES
java代码取操作ES有三种方式
1、es提供的原生操作模式 在es 8.0后会移除
2、es提供的rest操作风格
3、SpringDataElasticSearch的方式
1. 集群和分布式
我们来看看集群和分布式的概念。
1.1 单点服务问题
如图所示,是我们以前项目使用的架构模式,单点服务架构。整个服务部署在一个完整的项目中tomcat,使用一个mysql数据库:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-MEmYdI4g-1656170326622)(assets/1543286552773.png)]
本服务器处理所有请求,风险很大:
A:并发处理能力有限。由于单个服务器的性能有限。所以单台Tomcat最大连接数有限, B:容错率低,一旦服务器出现故障,整个服务就无法访问。 eBay于 1999年6月停机22小时的事故中断了约230万的拍卖eBay股票下跌了9.2个百分点。 C:单台服务器计算能力低,无法完成复杂的海量数据计算。
如何解决这样的问题?需要使用计算机集群。
1.2 集群
下维基百科全书介绍集群:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-I81UlqxW-1656170326623)(assets/1543286678122.png)]
集群是一组计算机高度密切合作,完成计算工作。每台计算机都被称为一台节点。我们将集群分为三类:
- 高可用集群
- 负载均衡集群
- 科学计算集群(分布式处理)
上述集群方法不需要独立使用,我们经常在系统架构中使用。
1.2.1 高可用集群
High availability Cluster高可用群集,简称HAC。其设计理念是避免单点故障问题,故障时可以快速恢复,快速继续提供服务。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-0ENELHoA-1656170326624)(assets/1543287433625.png)]
如图所示,集群中有两台计算机node01和node02,两者共享资源,处理业务也基本一致,互为主从。当node01工作时,node02处于待命状态。所有业务都在。Node如果01运行,故障服务和资源将转移到Node02上。
这种结构保证了服务的高可用性,但闲置节点是对资源的浪费。
1.2.2 负载均衡集群
Load Balancing负载平衡,集群中的每台计算机都完成相同的业务,无论主次。当用户请求到达时,请求平衡地分发到集群中的每个节点,并充分利用每个节点的资源。如图所示:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-3iaCoeTG-1656170326625)(assets/1543287806342.png)]
由于每个节点的业务相同,如果某个节点出现故障,只需将请求分发给其他节点即可。
1.2.3 科学计算集群
由于硬件设备的限制,单台计算机的处理性能有上限。如果计算所需的资源超过了单台计算机的能力,我们该怎么办?此时,可以使用科学的计算集群。
我们将复杂的任务分成小的子任务,然后分配到集群中的不同节点,最后总结计算结果。这么便宜PC机器互联,形成一个"超级计算机"解决复杂的计算任务。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-GAoJHGBb-1656170326626)(assets/1543288401040.png)]
这种方式也叫==,集群中的每个节点都完成了==。
1.3 分布式web应用
在任何领域都可以使用上述计算机合作的集群模式web开发也是如此,但有一些细节是不同的。以电子商务网站为例,我们来看看几种架构方法:
1.3.1 单体应用
在一个系统中完成所有业务:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-dICvmfKJ-1656170326627)(assets/1543288684507.png)]
问题:
- 系统庞大,功能耦合,维护困难
- 并发能力差,容易出现单点故障
- 不同的功能无法优化
1.3.2 分布式架构
根据上述分布式集群概念,集群中的每个节点完成不同的业务。web在开发中也是如此。我们拆分完整的系统,形成独立的系统,然后部署到不同的系统tomcat通过网络通信,不同节点相互配合。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-pB9OmNUi-1656170326628)(assets/1543288817235.png)]
这样,复杂的系统被细分,但另一个问题是单个节点故障会导致整个系统不完整。
1.3.3 高可用分布式集群架构
为了解决上述单点故障问题,我们可以部署负载平衡节点,即每个业务系统都有一个负载平衡的小集群。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-WCHzDaMM-1656170326629)(assets/1543289444226.png)]
2.Elasticsearch集群
在以前的课程中,我们都使用单点elasticsearch,接下来,我们将学习如何构建它Elasticsearch的集群。
2.1.单点的问题
单点的elasticsearch可能出现的问题有哪些?
- 单台机器存储容量有限
- 单个服务器容易出现单点故障,无法实现高可用性
- 单一服务并发处理能力有限
因此,我们需要处理这些问题elasticsearch搭建集群
2.2.集群结构
那怎样才能建立集群呢?
2.2.1.数据分片
首先,我们面临的第一个问题是数据量过大,单点存储量有限。
你认为应该如何解决?
是的,我们可以将数据分成多份,每份存储在不同的机器节点中(node),为了减少每个节点的数据量。这是数据的分布式存储,也称为:数据分片(Shard)
。
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-U6dgDnBr-1656170326630)(assets/1553071443011.png)]
2.2.2.数据备份
数据分片解决了大量数据存储的问题,但如果出现单点故障,分片数据将不再完整。如何解决这个问题?
是的,就像每个人都会在移动硬盘上存储一个额外的数据来备份手机数据一样。我们可以备份每个子数据,存储到其他节点,以防止数据丢失。这就是数据备份,也被称为数据副本(replica)
。
数据备份可以保证高可用性,但每个分片备份所需的节点数量会翻倍,成本太高!
为了在高可用性和成本之间寻求平衡,我们可以这样做:
- 首先,将数据分片存储到不同的节点
- 然后备份每个片段,放在对方的节点,完成相互备份
这样可以大大减少所需服务节点的数量,如图所示,我们以3分片为例,每分片备份一份:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-4Mxp52U3-1656170326631)(assets/1553072275126.png)]
在这个集群中,如果出现单节点故障,不会导致数据丢失,从而保证了集群的高可用性,减少了节点中的数据存储量。而且由于数据存储在多个节点,用户请求也会分发到不同的服务器,并发能力也有所提高。
2.3.搭建集群
集群需要多台机器,我们这里用一台机器来模拟,因此我们需要在一台虚拟机中部署多个elasticsearch节点,每个elasticsearch的端口都必须不一样。
我们计划集群名称为:leyou-elastic,部署3个elasticsearch节点,分别是:
node-01:http端口9201,TCP端口9301
node-02:http端口9202,TCP端口9302
node-03:http端口9203,TCP端口9303
先把目前电脑上的es和kibana关闭
第一步:把昨天安装的ES软件中复制一份,把复制出来软件中的data文件夹的数据删除()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uxnyLtSO-1656170326632)(assets/1575467048005.png)]
第二步:复制es软件粘贴3次,分别改名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjmnpi5N-1656170326633)(assets/1575467083227.png)]
第三步:修改每一个节点的配置文件 config下的elasticsearch.yml,下面已第一份配置文件为例
#允许跨域名访问
http.cors.enabled: true
http.cors.allow-origin: "*"
network.host: 127.0.0.1
# 集群的名称
cluster.name: leyou-elastic
#当前节点名称 每个节点不一样
node.name: node-01
#数据的存放路径 每个节点不一样
path.data: d:\class\sorfware\elasticsearch-9201\data
#日志的存放路径 每个节点不一样
path.logs: d:\class\sorfware\elasticsearch-9201\logs
# http协议的对外端口 每个节点不一样
http.port: 9201
# TCP协议对外端口 每个节点不一样
transport.tcp.port: 9301
#三个节点相互发现
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
#声明大于几个的投票主节点有效,请设置为(nodes / 2) + 1
discovery.zen.minimum_master_nodes: 2
# 是否为主节点
node.master: true
按照上面的内容修改其他两个配置文件,注意:保存时一定确保文件的保存编码是utf-8
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6H6WsA4p-1656170326633)(assets/1575467250968.png)]
第四步:启动集群
把三个节点分别启动,启动时不要着急,要一个一个地启动
使用head插件查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8fiF5OTp-1656170326635)(assets/1575467279027.png)]
2.4.测试集群中创建索引库
配置kibana,再重启
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HwFM1vOH-1656170326636)(assets/1588141227308.png)]
搭建集群以后就要创建索引库了,那么问题来了,当我们创建一个索引库后,数据会保存到哪个服务节点上呢?如果我们对索引库分片,那么每个片会在哪个节点呢?
这个要亲自尝试才知道。
还记得创建索引库的API吗?
-
请求方式:PUT
-
请求路径:/索引库名
-
请求参数:json格式:
{ "settings": { "属性名": "属性值" } }
settings:就是索引库设置,其中可以定义索引库的各种属性,目前我们可以不设置,都走默认。
这里给搭建看看集群中分片和备份的设置方式,示例:
PUT /heima
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
这里有两个配置:
- number_of_shards:分片数量,这里设置为3
- number_of_replicas:副本数量,这里设置为1,每个分片一个备份,一个原始数据,共2份。
通过chrome浏览器的head查看,我们可以查看到分片的存储结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MWSF5ySV-1656170326637)(assets/1575467330712.png)]
可以看到,heima这个索引库,有三个分片,分别是0、1、2,每个分片有1个副本,共6份。
- node-01上保存了0号分片和1号分片的副本
- node-02上保存了1号分片和2号分片的副本
- node-03上保存了0号分片和2号分片的副本
3.Elasticsearch客户端
3.1.客户端介绍
在elasticsearch官网中提供了各种语言的客户端:https://www.elastic.co/guide/en/elasticsearch/client/index.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkYijWwm-1656170326638)(assets/1596177415223.png)]
我们接下来要学习的是JavaRestClient的客户端。
注意点击进入后,选择版本到6.2
,因为我们之前按照的都是6.2.4
版本:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6MquywsV-1656170326639)(assets/1553651744294.png)]
然后选择Java High Level Rest Client
版本:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RT8eAJL5-1656170326640)(assets/1553651841987.png)]
3.2.创建Demo工程
3.2.1.初始化项目
选择用maven创建:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j0qbW2pH-1656170326642)(assets/1553657415587.png)]
选择目录:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pL5c8s8b-1656170326643)(assets/1553652153727.png)]
3.2.2.pom文件
注意,这里我们直接导入了SpringBoot的启动器,方便后续讲解。不过还需要手动引入elasticsearch的High-level-Rest-Client的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast.demo</groupId>
<artifactId>es-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.4.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2.3.配置文件
我们在resource下引入application.yml: 先空着,明天用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F73vweFf-1656170326644)(assets/1554383466912.png)]
3.3.索引库及映射
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAvJhAAo-1656170326645)(assets/1596177823927.png)]
创建索引库的同时,我们也会创建type及其映射关系,但是这些操作不建议使用java客户端完成,原因如下:
-
索引库和映射往往是初始化时完成,不需要频繁操作,不如提前配置好
-
官方提供的创建索引库及映射API非常繁琐,需要通过字符串拼接json结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-20FQFH9N-1656170326646)(assets/1553654415047.png)]
因此,这些操作建议还是使用我们昨天学习的Rest风格API去实现。
我们接下来以这样一个商品数据为例来创建索引库:
package cn.itcast.es.pojo;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Item {
private Long id;
private String title; //标题
private String category;// 分类
private String brand; // 品牌
private Double price; // 价格
private String images; // 图片地址
}
分析一下数据结构:
- id:可以认为是主键,将来判断数据是否重复的标示,不分词,可以使用keyword类型
- title:搜索字段,需要分词,可以用text类型
- category:商品分类,这个是整体,不分词,可以使用keyword类型
- brand:品牌,与分类类似,不分词,可以使用keyword类型
- price:价格,这个是double类型
- images:图片,用来展示的字段,不搜索,index为false,不分词,可以使用keyword类型
我们可以编写这样的映射配置:
PUT /leyou
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"item": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"category": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"images": {
"type": "keyword",
"index": false
},
"price": {
"type": "double"
}
}
}
}
}
3.4.索引数据操作
有了索引库,我们接下来看看如何新增索引数据
3.4.0.初始化客户端
完成任何操作都需要通过HighLevelRestClient客户端,看下如何创建。
我们先编写一个测试类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MTjF9ZAl-1656170326647)(assets/1553658784911.png)]
然后再@Before的方法中编写client初始化:
/** * @author syl */
public class ElasticSearchTest {
private RestHighLevelClient client;
// Json工具
private Gson gson = new Gson();
@Before
public void init(){
// 初始化HighLevel客户端
client = new RestHighLevelClient(
RestClient.builder(
HttpHost.create("http://127.0.0.1:9201"),
HttpHost.create("http://127.0.0.1:9202"),
HttpHost.create("http://127.0.0.1:9203")
)
);
}
@After
public void close() throws IOException {
// 关闭客户端
client.close();
}
}
3.4.1.新增文档
示例:
@Test
public void testAddIndex() throws IOException {
// 准备文档数据:
Item item = new Item(1L, "小米手机9", " 手机",
"小米", 3499.00, "http://image.leyou.com/13123.jpg");
// 转为Json格式:
String source = gson.toJson(item);
// 创建一个新增索引的请求
IndexRequest request = new IndexRequest("leyou", "item", item.getId().toString())
//添加数据,并指定是JSON格式
.source(source, XContentType.JSON);
// 发起请求
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
System.out.println("response = " + response);
}
看下响应:
response = IndexResponse[
index=item,
type=docs,
id=1,
version=2,
result=created,
seqNo=1,
primaryTerm=1,
shards={"total":2,"successful":2,"failed":0}
]
3.4.2.查看文档
根据rest风格,查看应该是根据id进行get查询,难点是对结果的解析:
@Test
public void testFindIndex() throws IOException {
// 创建get请求,并指定id
GetRequest request = new GetRequest("leyou", "item", "1");
// 是否进行结果source过滤
// request.fetchSourceContext(new FetchSourceContext(true, null, null));
// 查询,得到响应
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 解析响应,应该是json
String source = response.getSourceAsString();
// 转换json数据
Item item = gson.fromJson(source, Item.class);
System.out.println(item);
}
结果:
Item{id=1, title='小米手机9', category=' 手机', brand='小米', price=3499.0, images='http://image.leyou.com/13123.jpg'}
3.4.3.修改文档
新增时,如果传递的id是已经存在的,则会完成修改操作,如果不存在,则是新增。
3.4.4.删除文档
根据id删除:
@Test
public void testDeleteIndex() throws IOException {
// 准备删除的请求,参数为id
DeleteRequest request = new DeleteRequest("leyou", "item", "1");
// 发起请求
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
System.out.println("response = " + response);
}
3.4.5.批量新增
@Test public void testBulkIndex() throws IOException { // 准备文档数据: List<Item> list = new ArrayList<>(); list.add(new Item(1L, "小米手机7", "手机", "小米", 3299.00, "http://image.leyou.com/13123.jpg")); list.add(new Item(2L, "坚果手机R1", "手机", "
锤子", 3699.00, "http://image.leyou.com/13123.jpg")); list.add(new Item(3L, "华为META10", "手机", "华为", 4499.00, "http://image.leyou.com/13123.jpg")); list.add(new Item(4L, "小米Mix2S", "手机", "小米", 4299.00, "http://image.leyou.com/13123.jpg")); list.add(new Item(5L, "荣耀V10", "手机", "华为", 2799.00, "http://image.leyou.com/13123.jpg")); // 创建批量新增请求 BulkRequest request = new BulkRequest(); for (Item item : list) { // 构建单个IndexRequest,并add到BulkRequest中去 request.add(new IndexRequest("leyou", "item", item.getId().toString()) .source(gson.toJson(item), XContentType.JSON)); } // 发起请求 BulkResponse response = client.bulk(request, RequestOptions.DEFAULT); System.out.println("response = " + response); }
关键点:
- BulkRequest:批量请求,可以添加多个IndexRequest对象,完成批处理
3.5.搜索数据
3.5.1.查询所有match_all
标签:
40针前置连接器6es7