Elasticsearch(ES)

image-20230902191216423

  • image-20230902191547411


入门

正向、倒排索引

image-20230902192259971

image-20230902192309881



文档

image-20230902192407961



索引(Index)

  • 类似表,根据结果进行分类

image-20230902192528798



与MySQL对比

image-20230902192759259



架构

image-20230902192914181



安装

1、elasticsearch

  • 先创建一个网络:
docker network create es-net
  • 官网pull
docker pull elasticsearch:8.9.0
  • 部署单点
docker run -d \
--name es \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=true" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
--privileged \
elasticsearch:8.9.0

  • -e "cluster.name=es-docker-cluster":设置集群名称
  • -e "http.host=0.0.0.0":监听的地址,可以外网访问
  • -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":内存大小
  • -e "discovery.type=single-node":非集群模式
  • -v es-data:/usr/share/elasticsearch/data:挂载逻辑卷,绑定es的数据目录
  • -v es-logs:/usr/share/elasticsearch/logs:挂载逻辑卷,绑定es的日志目录
  • -v es-plugins:/usr/share/elasticsearch/plugins:挂载逻辑卷,绑定es的插件目录【自己创建】
  • --privileged:授予逻辑卷访问权
  • --network es-net :加入一个名为es-net的网络中
  • -p 9200:9200:端口映射配置
  • 访问 http:// IPIPIP:9200 即可看到elasticsearch的响应结果:JASON字符串


  • 进入容器
docker exec -it es /bin/bash
  • 自动生成密码
#超级用户名 elastic
bin/elasticsearch-reset-password -u elastic
#创建 服务账户
elasticsearch-users useradd mykibana -p <esSales..> -r kibana_system
elasticsearch-users useradd mykibana -p esSales.. -r kibana_system

2、kibana(ES_Ui界面)

Kibana 8.9.0 | Elastic

  • 官网pull
docker pull kibana:版本和kibana一致
  • 部署
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
-e ELASTICSEARCH_USERNAME=mykibana \
-e ELASTICSEARCH_PASSWORD=esSales.. \
--network=es-net \
-p 5601:5601 \
kibana:8.9.1
  • --network es-net :加入一个名为es-net的网络中,与elasticsearch在同一个网络中
  • -e ELASTICSEARCH_HOSTS=http://es:9200":设置elasticsearch的地址,因为kibana已经与elasticsearch在一个网络,因此可以用容器名直接访问elasticsearch
  • -p 5601:5601:端口映射配置
  • 查看日志
docker logs -f kibana
  • 访问

http:// IPIPIPIPIP:5601


  • 本地运行
#1.下载解压

#2.【配置文件】设置远程Es的地址
elasticsearch.hosts: ["http://URL:9200"]

#3.【配置文件】设置ES服务客户账户密码
elasticsearch.username: "服务账户"
elasticsearch.password: "密码.."
  • kibana登录访问– es超级用户账户密码


3、安装分词器

# 查看es的数据卷目录es-plugins
docker volume inspect es-plugins

{
"CreatedAt": "2023-12-08T17:23:15+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/es-plugins/_data",
"Name": "es-plugins",
"Options": null,
"Scope": "local"
}

#进入 Mountpoint

#复制ik压缩包进去 -- 解压
unzip 压缩包

#删除压缩包
#重启容器
docker restart es

# 查看es日志
docker logs -f es
  • http:// IPIPIPIPIP:5601

  • 测试

IK分词器包含两种模式:

  • ik_smart:最少切分

  • ik_max_word:最细切分

#ik分词器  ik_max_word:最大切分
GET /_analyze
{
"text": "辰呀正在学习java,太棒了吧",
"analyzer": "ik_smart"
}

image-20230903005603637



4、安装拼音分词器

  • 下载[ Pinyin
  • 打包成对应的Es版本插件:依赖选择对应的Es版本,然后Maven生命周期-》打包Install

解决拼音分词器不分词情况:先使用ik分词器再使用拼音分词器

  • 要在创建索引库的时候设置

image-20230922214219698

  • 测试
POST /_analyze
{
"text": ["如家如家酒店"],
"analyzer": "pinyin"
}


索引库操作

image-20230903010214534

  • text : 分词
  • keyword:不分词
  • index:是否参与搜索

创建

# 创建索引库
PUT /chen
{
"mappings": {
"properties": {
"info": {
"type": "text",
"analyzer": "ik_smart"
},
"email": {
"type": "keyword",
"index": false
},
"name": {
"type": "object",
"properties": {
"firstName": {
"type": "keyword"
},
"lastName": {
"type": "keyword"
}
}
}
}
}
}

删除

#删除
DELETE /chen

修改

image-20230903013250624


查询

#查询
GET /chen


文档操作


新增

image-20230903155748312

#插入文法
POST /chen/_doc/1
{
"info":"辰在学习Java",
"email":"3160@qq.com",
"name":{
"firstName":"陈",
"lastNmae":"李"
}
}

删除

image-20230903160437964

#删除文档
DELETE /chenp/_doc/1

查看

image-20230903160453505

#查询文档
GET /chenp/_doc/2

更改

  • 全修改

#修改文档  --  PUT   --   替换内容 -id 文档不存在则 新增
PUT /chenp/_doc/2
{
"info":"辰在学习Java",
"email":"chenCCCCCCC@qq.com",
"name":{
"firstName":"陈",
"lastNmae":"李"
}
}
  • 局部修改

#局部修改文档字段
POST /chenp/_update/2
{
"doc":{
"email": "PC@qq.com"
}
}


DSL查询语法

image-20230904005529844



全文检索查询

image-20230904005623284

#查询所有   GET /索引库名/_search
GET /hotel/_search
{
"query": {
"match_all": {}
}
}

  • match:根据一个字段
# match查询
GET /hotel/_search
{
"query": {
"match": {
"all": "如家"
}
}
}

  • multi_match:根据多个字段查询,参与查询字段越多,查询性能越差
# match查询
GET /hotel/_search
{
"query": {
"multi_match": {
"query": "如家",
"fields": ["brand","name"]
}
}
}


精确检索查询

  • term:精确检索:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
# term语句
GET /hotel/_search
{
"query": {
"term": {
"city": {
"value": "北京"
}
}
}
}

  • range:范围查询,可以是数值、日期的范围
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gte": 1100,
"lte": 3000
}
}
}
}

  • 地理范围查询 distance:范围,location:中心点
# distance查询
GET /hotel/_search
{
"query": {
"geo_distance":{
"distance": "2km",
"location": "31.21,121.5"
}
}
}


# 按照我的位置坐标的距离进行升序排序 121.533514,31.375558
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 31.375558,
"lon": 121.533514
},
"order": "asc",
"unit": "km"
}
}
]
}

复合查询

算分规则

  • 在平常的查询条件上,实现过滤加分

  • 默认BM25算法

image-20230922141347163

image-20230904102449700

# function score 查询
GET /hotel/_search
{
"query": {

"function_score": {

"query": { "match": { "all" : "外滩" } }, #查询条件

"functions": [
{
"filter": { #过滤条件 : 符合进行算分
"term": {"brand": "如家"} #词条精确检索
},
"weight": 10 #常量作为函数结果
}

],
"boost_mode": "sum" #加权模式:相加
}
}
}


布尔查询

  • 多条件查询

image-20230904103639997

  • must参与算分 : 算分条件越多,性能

image-20230904104419913

#######    bool 布尔查询   ########
GET /hotel/_search
{
"query": {
"bool": {
"must": [ #参与算分
{
"match": {"name": "如家"}
}
],

"must_not": [
{
"range": {"price": {"gte": 400} }
}
],

"filter": [
{
"geo_distance": {
"distance": "10km",
"location": {"lat": 31.21,"lon": 121.5}
}
}
]

}
}
}


搜索结果处理

  • 搜索全部
  • 排序,按照sort的条件进行排序,可多个排序条件,满足第一个则开始执行第二个
# sort排序
# 同等 score 按照 price排序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"score": "desc"
},
{
"price": "asc"
}
]
}

# 按照我的位置坐标的距离进行升序排序  121.533514,31.375558
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"location": {"lat": 31.375558,"lon": 121.533514},
"order": "asc",
"unit": "km"
}
}
]
}

分页

  • 默认10条
# 分页查询
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": "desc"
}
],
"from": 0, #起始
"size": 2
}

image-20230904110855361



高亮

image-20230904111420544

  • 默认em标签
# 高亮查询
#默认请求下,搜索字段和高亮字段必须一致 : require_field_match(字段匹配)默认true
GET /hotel/_search
{
"query": {
"match": {
"all": "如家"
}
},
"highlight": {
"fields": { #高亮的字段
"name": { #和搜索字段不一致,需要取消字段匹配
"require_field_match": "false" #需要取消字段匹配
}
}
}
}
}




DSL聚合

  • 统计

image-20230904162751501

image-20230904162915551

聚合

image-20230922204000091

Bucket聚合会统计Bucket内的文档数量,记为**_count**,并且按照_count降序排序

  • aggs 代表聚合 与 query 同级
# =====全索引库=====  聚合功能 , 自定义排序规则
GET /hotel/_search
{
"size": 0,
"aggs": {
"barndAgg": {
"terms": {
"field": "brand",
"size": 20,
"order": { #排序
"_count": "desc"
}
}
}
}
}


# =====防止全索引库实现聚合===== 设置范围,先查询后聚合
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200
}
}
},
"size": 0,
"aggs": {
"brandAgg":{
"terms": {
"field": "brand",
"size": 20
}}}}

image-20230904171518981



嵌套聚合

  • 获取每个品牌的用户评分的min、max、avg的值

image-20230922205254667

#嵌套聚合metric
POST /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg":{
"terms": {
"field": "brand",
"size": 20,
"order": {
"scoreAgg.avg": "desc"
}
},
"aggs":{ #是brands聚合的子聚合,也就是分组后对每组分别计算
"scoreAgg":{ #聚合名称
"stats": { #聚合类型,这里stats可以计算min、max、等
"field": "score" #聚合字段,这里是score
}
}}}}}





自动补全

使用

  • 拼音分词器

解决拼音分词器不分词情况:先使用ik分词器再使用拼音分词器

  • 要在创建索引库的时候设置
  • image-20230922215719171

image-20230922214219698


# 自定义拼音分词器
PUT /test
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "ik_max_word",
"filter": "py"
}
},
"filter": {
"py": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},


#索引库 -- 词条分词使用ik+py,搜索使用ik
"mappings": {
"properties": {
"name":{ #分词字段
"type": "text",
"analyzer": "my_analyzer",
"search_analyzer": "ik_smart"
}
}
}
}

  • 测试
POST /test/_analyze
{
"text": ["如家如家酒店"],
"analyzer": "my_analyzer"
}

总结:

image-20230922220156743



语法

  • 创建索引库字段的时候要求类型为 completion

image-20230922220357195

#自动补全的索引库
PUT test
{
"mappings": {
"properties": {
"title":{
"type": "completion" # 补全字段
}
}
}
}

  • 查询

image-20230922220533988

#示例数据
POST test/_doc
{
"title": ["Sony", "WH-1000XM3"]
}
POST test/_doc
{
"title": ["SK-II", "PITERA"]
}
POST test/_doc
{
"title": ["Nintendo", "switch"]
}



#自动补全查询
POST /test/_search
{
"suggest": {
"title_suggest": {
"text": "s", # 关键字
"completion": {
"field": "title", # 补全字段
"skip_duplicates": true, # 跳过重复的
"size": 10 # 获取前10条结果
}
}
}
}





RestClient(ES)

ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。官方文档地址:https:/Www.elastic.co/quide/en/elasticsearch/client/index.html


1、依赖

<!--springboot自带默认版本是7.6.2,需要指定版本要 注明 -->
<properties>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

<!--es的RestHighLevelClient依赖-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>

2、初始化 RestHighLevelClient

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.238.3:9200")
));


索引库操作

image-20230903194609576

创建

  • 请求:CreateIndexRequest
  • create
//1、创建Request对象 hotel
CreateIndexRequest request = new CreateIndexRequest("hotel");

//2、准备请求的参数:(DSL语句,语句类型)
request.source(MAPPING_TEMPLATE, XContentType.JSON);

//3、发起请求
client.indices().create(request, RequestOptions.DEFAULT);



删除

  • 请求:DeleteIndexRequest
  • delete
//1、创建Request对象
DeleteIndexRequest request = new DeleteIndexRequest("hotel");

//2、发起请求
client.indices().delete(request, RequestOptions.DEFAULT);

查询

  • 请求:GetIndexRequest
  • exists
//1、创建Request对象
GetIndexRequest request = new GetIndexRequest("hotel");

//2、发起请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);


文档操作

创建

//根据id查询数据
Hotel hotel = hotelService.getById(395434);
//转换自定义文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);

//1、创建Request对象 hotel
IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());

//2、准备JSON文档
request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);

//3、发起请求
client.index(request,RequestOptions.DEFAULT);



/** 数据库批量导入 **/
//批量查询数据
List<Hotel> hotels = hotelService.list();

//1、创建Request
BulkRequest request = new BulkRequest();

//2、准备参数,添加多个新增的Request

for (Hotel hotel : hotels) {
//转换文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
//新增文档对象
request.add(new IndexRequest("hotel")
.id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc),XContentType.JSON));
}

//3、发送请求
client.bulk(request,RequestOptions.DEFAULT);

GET /hotel/_doc/395434


查询

image-20230903230820413

//1、准备Request
GetRequest request = new GetRequest("hotel","395434");
//2、发送请求,得到响应
GetResponse response = client.get(request,RequestOptions.DEFAULT);
//3、解析响应结果
String json = response.getSourceAsString();

HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);

更新

image-20230904001801180

//1、准备Request
UpdateRequest request = new UpdateRequest("hotel", "395434");

//2、准备请求参数
request.doc(
"price","992",
"starName","满天星级"
);

//3、发送请求
client.update(request,RequestOptions.DEFAULT);

删除

//1、准备Request
DeleteRequest request = new DeleteRequest("hotel", "395434");

//3、发起请求
client.delete(request,RequestOptions.DEFAULT);

批量导入

//批处理导入数据
@Test
void testBulkRequest() throws IOException {
//批量查询数据
List<Hotel> hotels = hotelService.list();

//1、创建Request
BulkRequest request = new BulkRequest();

//2、准备参数,添加多个新增的Request

for (Hotel hotel : hotels) {
//转换文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
//新增文档对象
request.add(new IndexRequest("hotel")
.id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc),XContentType.JSON));
}

//3、发送请求
client.bulk(request,RequestOptions.DEFAULT);




文档查询与结果处理

QueryBuilders

准备:

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.238.3:9200")
));

//1、准备Request
SearchRequest request = new SearchRequest("hotel");

检索

image-20230904113447120

image-20230904114421501

//2、准备DSL

//全部查询
request.source().query(QueryBuilders.matchAllQuery());

//单字段
request.source().query(QueryBuilders.matchQuery("all","如家"));

//多字段
request.source().query(QueryBuilders.multiMatchQuery("如家","name","business"));

//精确查询
request.source().query(QueryBuilders.termQuery("city","上海"));

//范围查询
request.source().query(QueryBuilders.rangeQuery("price").lte(1500).gte(100));

//复合查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//布尔查询
boolQuery
.must(QueryBuilders.termQuery("city","上海"))
.mustNot(QueryBuilders.rangeQuery("price").lte(100))
.filter(QueryBuilders.rangeQuery("price").gte(1000));
request.source().query(boolQuery); //添加布尔查询到request



高亮

//2、准备DSL

//高亮 -- 查询时一次性链式编程
request.source()
.query(QueryBuilders.matchQuery("all","如家"))
.highlighter(new HighlightBuilder().field("name") //高亮字段
.requireFieldMatch(false)); //字段映射

//额外添加高亮要求 编程
request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));

  • 结果解析

image-20230904143107301

if (!highlightFields.isEmpty()){
HighlightField highlightField = highlightFields.get("name");
if (highlightField!=null){
//获取高亮的值
String name = highlightField.getFragments()[0].toString();
//覆盖高亮结果
hotelDoc.setName(name);
}
}


距离排序

image-20230904154248449

String location  = params.getLocation();
if (location != null && !location.equals("")){
request.source().sort(SortBuilders
.geoDistanceSort("location",new GeoPoint(location))
.order(SortOrder.ASC)
.unit(DistanceUnit.KILOMETERS)
);
}

//获取距离
Object[] sortValues = hit.getSortValues();


算分控制

image-20230922203020674


//2、算分控制
FunctionScoreQueryBuilder functionScoreQuery =
QueryBuilders.functionScoreQuery(
//添加原始的查询,相关性算分的查询
boolQuery,
//function score的数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
//其中的一个function score元素
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
//过滤条件
QueryBuilders.termQuery("isAd",true), //判断当前文档是否要加分
//算分函数
ScoreFunctionBuilders.weightFactorFunction(10)
)
}
);
request.source().query(functionScoreQuery);//添加到查询中


结果与解析

//3、发起请求
SearchResponse response = client.search(request,RequestOptions.DEFAULT);
//4、解析结果
SearchHits searchHits = response.getHits();
//4.1 获取总条数
long value = searchHits.getTotalHits().value;
//4.2获取文档数组
SearchHit[] hits = searchHits.getHits();

//4.3遍历
for (SearchHit hit : hits) {
//获取文档source
String json= hit.getSourceAsString();

//反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);

//获取高亮结果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (!highlightFields.isEmpty()){
HighlightField highlightField = highlightFields.get("name");
if (highlightField!=null){
//获取高亮的值
String name = highlightField.getFragments()[0].toString();
//覆盖高亮结果
hotelDoc.setName(name);
}
}

//获取距离
Object[] sortValues = hit.getSortValues();
if (sortValues.length>0){
Object sortValue = sortValues[0];//距离
}

System.out.println("hotelDoc" + hotelDoc);
}





聚合

image-20230922205646201

image-20230922210144008

//1、准备Request
SearchRequest request = new SearchRequest("hotel");

//2、准备DSL
//2.1 设置size
request.source().size(0);
//2.2 聚合
request.source().aggregation(AggregationBuilders
.terms("brandAgg") //聚合名称
.field("brand") //聚合字段
.size(10)
);

//3、发出请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);

//4、解析请求
Aggregations aggregations = response.getAggregations();
//4.1根据聚合名称获取聚合结果 -- Terms
Terms brandTerms = aggregations.get("brandAgg");
//4.2 获取buckets
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
//4.3 遍历
for (Terms.Bucket bucket:buckets){

String key = bucket.getKeyAsString();
int count = bucket.getDocCount()

}


自动补全

image-20230923010047661

SearchRequest request = new SearchRequest("hotel");

request.source().suggest(new SuggestBuilder().addSuggestion(
"suggestions",
SuggestBuilders.completionSuggestion("suggestion")
.prefix("pg")
.skipDuplicates(true)
.size(10)
));

SearchResponse response = client.search(request,RequestOptions.DEFAULT);

image-20230923013422170

Suggest suggest = response.getSuggest();
CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
for (CompletionSuggestion.Entry.Option option : options) {

System.out.println(option.getText().string());
}


Mq&Es数据同步

  • 同步调用 : 业务耦合

优点:实现简单,粗暴

缺点:业务耦合度高

image-20230923135745996



  • 主从关系

优点:完全解除服务间耦合

缺点:开启binlog增加数据库负担、实现复杂度高

image-20230923135926128



  • 消息队列

优点:低耦合,实现难度一般

缺点:依赖mq的可靠性

image-20230923135847406


实践

  • 对于数据库的CRUD:,MQ中对Es的操作只有,删除或者插入,es中如果对于的文档存在则为修改,不存在则新增

image-20230923141434336


常量类

  • 声明交换机、队列、对应的BindKey

【消费者、发布者】

public class MqConstants {
/**
* 交换机
*/
public final static String HOTEL_EXCHANGE = "hotel.topic";

/**
* 监听新增和修改的队列
*/
public final static String HOTEL_INSERT_QUEUE = "hotel.insert.queue";

/**
* 监听删除的队列
*/
public final static String HOTEL_DELETE_QUEUE = "hotel.delete.queue";

/**
* 新增或修改的RoutingKey
*/
public final static String HOTEL_INSERT_KEY = "hotel.insert";

/**
* 新增或修改的RoutingKey
*/
public final static String HOTEL_DELETE_KEY = "hotel.delete";

}

发布

  • 配置
spring:

rabbitmq:
host: 198.168.238.3
port: 5672
username: itcast
password: 123321
virtual-host: /
@Autowired
private RabbitTemplate rabbitTemplate;

//发送(交换机,队列的BindKey,消息)
rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE,MqConstants.HOTEL_INSERT_KEY,hotel.getId());

接收

  • Bean配置类
@Configuration
public class MqConfig {

//定义交换机
@Bean
public TopicExchange topicExchange(){
return new TopicExchange(MqConstants.HOTEL_EXCHANGE,true,false); //durable:持久化
}

//队列1
@Bean
public Queue insertQueue(){
return new Queue(MqConstants.HOTEL_INSERT_QUEUE,true);
}

//队列2
@Bean
public Queue deleteQueue(){
return new Queue(MqConstants.HOTEL_DELETE_QUEUE,true);
}

//绑定关系:把队列1绑定到交换机中和设置指定 routingKey :发送消息设置只有对应的队列才能获取信息
@Bean
public Binding insertQueueBinding(){
return BindingBuilder.bind(insertQueue()).to(topicExchange()).with(MqConstants.HOTEL_INSERT_KEY);
}

//绑定关系:把队列2绑定到交换机中和设置指定 routingKey :发送消息设置只有对应的队列才能获取信息
@Bean
public Binding deleteQueueBinding(){
return BindingBuilder.bind(deleteQueue()).to(topicExchange()).with(MqConstants.HOTEL_DELETE_KEY);
}
}


  • 监听类
@Component
public class HotelListener {

@Autowired
private IHotelService hotelService;

/**
* 监听酒店新增或修改的业务
* @param id 酒店的id
*/
@RabbitListener(queues = MqConstants.HOTEL_INSERT_QUEUE)
public void listenHotelInsertOrUpdate(Long id){
hotelService.insertById(id);

//往es插入文档
IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());

request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);

client.index(request, RequestOptions.DEFAULT);
}

/**
* 监听酒店删除业务
* @param id 酒店的id
*/
@RabbitListener(queues = MqConstants.HOTEL_DELETE_QUEUE)
public void listenHotelDelete(Long id){
hotelService.deleteById(id); //往es删除文档

DeleteRequest request = new DeleteRequest("hotel",id.toString());

client.delete(request,RequestOptions.DEFAULT);
}


}





ES集群

  • 将单点的Es索引库分成主副分片,主副分片不存放在同一节点上

image-20230923190311069


集群搭建

https://chen-1317386995.cos.ap-guangzhou.myqcloud.com/Java/Utils/%E5%AE%89%E8%A3%85elasticsearch.md



集群职责

image-20230923194922810


image-20230923195225833


image-20230923195303947



集群数据同步

image-20230923195623242

image-20230923195750559

image-20230923195910221

image-20230923195938643



故障转移

image-20230923200044694

image-20230923200225215