aprendizaje springCloud [6] motor de búsqueda distribuido (3)

Directorio de artículos

prefacio

  • Este artículo se aprende de un caballo oscuro, y después de un estudio cuidadoso, se clasifica y se resume, ¡no se copia! !
  • Con respecto a la parte de la operación real, se recomienda que los alumnos hagan más prácticas y analicen con cuidado. ! !
  • Esta parte tiene requisitos ligeramente más altos en la computadora, ¡preste atención para actualizar la configuración de la computadora! ! !

- agregación de datos

inserte la descripción de la imagen aquí

  • Las agregaciones pueden realizar las estadísticas, el análisis y la operación de los datos del documento.
  • Hay tres tipos comunes de agregación:
    • Agregación de cubos: se utiliza para agrupar documentos
      • TermAggregation: agrupar por valor de campo de documento
      • Histograma de fechas: agrupar por escala de fechas, por ejemplo, una semana como grupo o un mes como grupo
  • Agregación de métricas: se utiliza para calcular algunos valores, como: valor máximo, valor mínimo, valor promedio, etc.
    • Promedio: Promedio
    • Max: encontrar el valor máximo
    • Min: encontrar el valor mínimo
    • Estadísticas: busque simultáneamente máximo, mínimo, promedio, suma, etc.
  • Pipeline (pipeline) agregación: agregación basada en los resultados de otras agregaciones
  • **Nota:** Los campos que participan en la agregación deben ser palabra clave, fecha, valor y valor booleano.

1.1 DSL realiza la agregación

1.1.1 Sintaxis de agregación de cubos

GET /hotel/_search
{
    
    
 "size": 0,  // 设置size为0,结果中不包含文档,只包含聚合结果
 "aggs": {
    
     // 定义聚合
   "brandAgg": {
    
     //给聚合起个名字
     "terms": {
    
     // 聚合的类型,按照品牌值聚合,所以选择term
       "field": "brand", // 参与聚合的字段
       "size": 20 // 希望获取的聚合结果数量
     }
   }
 }
}
  • Ejemplo:
    #桶排序
    GET /hotel/_search
    {
          
          
      "size": 0,
      "aggs": {
          
          
        "brandAgg": {
          
          
          "terms": {
          
          
            "field": "brand",
            "size": 20
          }
        }
      }
    }
    
  • resultado:
    inserte la descripción de la imagen aquí

1.1.2 Clasificación de resultados de agregación

  • De forma predeterminada, la agregación del depósito contará el número de documentos en el depósito, lo registrará como _count y ordenará en orden descendente de _count.
  • Especifique el atributo de orden para personalizar el método de clasificación de agregación
  • Manifestación:
    #自定义聚合的排序方式
    # 按照_count升序排列
    GET /hotel/_search
    {
          
          
      "size": 0,
      "aggs": {
          
          
        "brandAgg": {
          
          
          "terms": {
          
          
            "field": "brand",
            "order": {
          
          
              "_count": "asc"
            },
            "size": 20
          }
        }
      }
    }
    
    • resultado:
      inserte la descripción de la imagen aquí

1.1.3 Limitar el alcance de la agregación

  • De forma predeterminada, la agregación de depósitos agrega todos los documentos en la biblioteca de índices, pero en escenarios reales, los usuarios ingresarán las condiciones de búsqueda, por lo que la agregación debe ser la agregación de los resultados de la búsqueda. Entonces la agregación tiene que ser calificada.
  • Para limitar el rango de documentos que se agregarán, simplemente agregue condiciones de consulta:
  • Manifestación:
    # 限定聚合范围
    # 只对200元以下的文档聚合
    GET /hotel/_search
    {
          
          
      "query": {
          
          
        "range": {
          
          
          "price": {
          
          
            "lte": 200 
          }
        }
      },
      "size": 0,
      "aggs": {
          
          
        "brandAgg": {
          
          
          "terms": {
          
          
            "field": "brand",
            "size": 20
          }
        }
      }
    }
    
  • resultado:
    inserte la descripción de la imagen aquí

1.2 Sintaxis de agregación de métricas

  • Agregación de métricas: se utiliza para calcular algunos valores, como: valor máximo, valor mínimo, valor promedio, etc.
    • Promedio: Promedio
    • Max: encontrar el valor máximo
    • Min: encontrar el valor mínimo
    • Estadísticas: busque simultáneamente máximo, mínimo, promedio, suma, etc.
  • Manifestación:
GET /hotel/_search
{
    
    
  "size": 0, 
  "aggs": {
    
    
    "brandAgg": {
    
     
      "terms": {
    
     
        "field": "brand", 
        "size": 20
      },
      "aggs": {
    
     // 是brands聚合的子聚合,也就是分组后对每组分别计算
        "score_stats": {
    
     // 聚合名称
          "stats": {
    
     // 聚合类型,这里stats可以计算min、max、avg等
            "field": "score" // 聚合字段,这里是score
          }
        }
      }
    }
  }
}
  • resultado:
    inserte la descripción de la imagen aquí

1.3 Resumen

  • aggs significa agregación, que está al mismo nivel que consulta ¿Cuál es la función de consulta en este momento?

    • Alcance de los documentos agregados
  • Los tres elementos necesarios para la agregación:

    • nombre agregado
    • tipo de agregación
    • campo agregado
  • Las propiedades configurables agregadas son:

    • tamaño: especifique el número de resultados de agregación
    • order: especifique el método de clasificación de los resultados de agregación
    • campo: especifique el campo de agregación

1.4 RestAPI realiza la agregación

1.5 Sintaxis de la API

  • Las condiciones de agregación están al mismo nivel que las condiciones de consulta, por lo que se debe usar request.source() para especificar las condiciones de agregación.
  • Sintaxis para condiciones agregadas:
    inserte la descripción de la imagen aquí
  • El resultado de la agregación también es diferente del resultado de la consulta y la API también es especial. Sin embargo, JSON también se analiza capa por capa:
    inserte la descripción de la imagen aquí

1,7 caso

  • Requisito: la marca, la ciudad y otra información de la página de búsqueda no deben codificarse en la página, sino obtenerse mediante datos agregados del hotel en la base de datos del índice:
    inserte la descripción de la imagen aquí
  • analizar:
    • Use la función de agregación y la agregación de cubos para agrupar los documentos en los resultados de búsqueda según la marca y la ciudad, y conozca las marcas y ciudades incluidas.
    • Debido a que es una agregación de resultados de búsqueda, la agregación es una agregación de rango limitado , es decir, las condiciones limitantes de la agregación son consistentes con las condiciones del documento de búsqueda.
  • El tipo de valor de retorno es el resultado final que se mostrará en la página:
    inserte la descripción de la imagen aquí
  • El resultado es una estructura Map:
    • la clave es una cadena, ciudad, estrella, marca, precio
    • el valor es una colección, como los nombres de varias ciudades

  • Código lógico importante implementado

  • Agregue un método en Controllerlos siguientes requisitos:

    • Método de solicitud:POST
    • Solicitud de ruta:/hotel/filters
    • Parámetros de la solicitud: RequestParams, consistentes con los parámetros del documento de búsqueda
    • Tipo de valor de retorno:Map<String, List<String>>
     @PostMapping("filters")
        public Map<String, List<String>> getFilters(@RequestBody RequestParams params){
          
          
            return hotelService.filters(params);
        }
    
  • ServiceDefina el nuevo método en :

    Map<String, List<String>> filters(RequestParams params);
    
  • Implemente este método en HotelServicela clase de implementación de

    @Override
    public Map<String, List<String>> filters(RequestParams params) {
          
          
        try {
          
          
            // 1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            // 2.准备DSL
            // 2.1.query
            buildBasicQuery(params, request);
            // 2.2.设置size
            request.source().size(0);
            // 2.3.聚合
            buildAggregation(request);
            // 3.发出请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 4.解析结果
            Map<String, List<String>> result = new HashMap<>();
            Aggregations aggregations = response.getAggregations();
            // 4.1.根据品牌名称,获取品牌结果
            List<String> brandList = getAggByName(aggregations, "brandAgg");
            result.put("品牌", brandList);
            // 4.2.根据品牌名称,获取品牌结果
            List<String> cityList = getAggByName(aggregations, "cityAgg");
            result.put("城市", cityList);
            // 4.3.根据品牌名称,获取品牌结果
            List<String> starList = getAggByName(aggregations, "starAgg");
            result.put("星级", starList);
    
            return result;
        } catch (IOException e) {
          
          
            throw new RuntimeException(e);
        }
    }
    
    private void buildAggregation(SearchRequest request) {
          
          
        request.source().aggregation(AggregationBuilders
                                     .terms("brandAgg")
                                     .field("brand")
                                     .size(100)
                                    );
        request.source().aggregation(AggregationBuilders
                                     .terms("cityAgg")
                                     .field("city")
                                     .size(100)
                                    );
        request.source().aggregation(AggregationBuilders
                                     .terms("starAgg")
                                     .field("starName")
                                     .size(100)
                                    );
    }
    
    private List<String> getAggByName(Aggregations aggregations, String aggName) {
          
          
        // 4.1.根据聚合名称获取聚合结果
        Terms brandTerms = aggregations.get(aggName);
        // 4.2.获取buckets
        List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
        // 4.3.遍历
        List<String> brandList = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
          
          
            // 4.4.获取key
            String key = bucket.getKeyAsString();
            brandList.add(key);
        }
        return brandList;
    }
    
  • Aviso:

    • En la parte del caso, el código no necesariamente debe personalizarse manualmente uno por uno, ¡pero debe operarlo usted mismo para verificar el resultado final! ! !
    • ¡Al mismo tiempo, trabaje duro para lidiar con los problemas que encuentre! ! !

Dos autocompletado

  • El efecto es como se muestra en la figura:
    inserte la descripción de la imagen aquí
    • De acuerdo con las letras ingresadas por el usuario, la función de solicitar entradas completas es la finalización automática
    • Debido a que debe inferirse en función de las letras pinyin, se utiliza la función de segmentación de palabras pinyin

2.1 Instalación del separador de palabras Pinyin

  • Para lograr la finalización basada en letras, es necesario segmentar el documento según pinyin. Resulta que hay un complemento de segmentación de palabras pinyin para elasticsearch en GitHub. DIRECCIÓN
  1. descargar y descomprimir
    inserte la descripción de la imagen aquí

  2. Subir a la máquina virtual, el directorio de complementos de elasticsearch

    • Para instalar el complemento, debe conocer la ubicación del directorio de complementos de elasticsearch, y usamos el montaje de volumen de datos, por lo que debemos ver el directorio de volumen de datos de elasticsearch y verificarlo con el siguiente comando:
    docker volume inspect es-plugins
    

    inserte la descripción de la imagen aquí

    • Luego cargue el archivo descomprimido a este directorio, pycomo el separador de palabras pinyin después de la descompresión y el cambio de nombre
      inserte la descripción de la imagen aquí
  3. reiniciar búsqueda elástica

    docker restart es
    
  4. prueba

    POST /_analyze
    {
          
          
      "text": "如家酒店还不错",
      "analyzer": "pinyin"
    }
    

    inserte la descripción de la imagen aquí

2.2 Tokenizador personalizado

  • El separador de palabras pinyin predeterminado divide cada carácter chino en pinyin, pero queremos que cada entrada forme un conjunto de pinyin, por lo que debemos personalizar el separador de palabras pinyin para formar un separador de palabras personalizado.

  • La composición del analizador en elasticsearch consta de tres partes:

    • filtros de caracteres : procesa el texto antes del tokenizador. por ejemplo, eliminar caracteres, reemplazar caracteres
    • tokenizer : Cortar el texto en términos de acuerdo con ciertas reglas. Por ejemplo, palabra clave no es participio; también existe ik_smart
    • Filtro tokenizador : procesa aún más las entradas generadas por el tokenizador. Por ejemplo, conversión de casos, procesamiento de sinónimos, procesamiento de pinyin, etc.
  • Cuando la segmentación de palabras del documento, el documento será procesado por estas tres partes a su vez:
    inserte la descripción de la imagen aquí

  • manifestación

#拼音分词器
DELETE /test

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
        }
      }
    }
  },
  "mappings": {
    
    
    "properties": {
    
    
      "name": {
    
    
        "type": "text",
        "analyzer": "my_analyzer",
        "search_analyzer": "ik_smart"
      }
    }
  }
}


POST /test/_analyze
{
    
    
  "text": ["如家酒店还不错"],
  "analyzer": "my_analyzer"
}
  • resultado:
    inserte la descripción de la imagen aquí
  • Precauciones para el separador de palabras pinyin
    • Para evitar buscar homófonos, no utilice el separador de palabras pinyin cuando busque

2.3 Consulta de autocompletar

  • Elasticsearch proporciona una consulta Sugerente de finalización para lograr la finalización automática. Esta consulta hará coincidir los términos que comienzan con la entrada del usuario y los devolverá. Para mejorar la eficiencia de la consulta de finalización, existen algunas restricciones en los tipos de campos en el documento:

    • Los campos que participan en la consulta de cumplimentación deben ser de tipo cumplimentación.
    • El contenido del campo es generalmente una matriz formada por múltiples entradas para completar.
  • Manifestación:

# 自动补全查询
DELETE /test02
## 创建索引库
PUT /test02
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "title": {
    
    
        "type": "completion"
      }
    }
  }
}
## 示例数据
POST test02/_doc
{
    
    
  "title": ["Sony", "WH-1000XM3"]
}
POST test02/_doc
{
    
    
  "title": ["SK-II", "PITERA"]
}
POST test02/_doc
{
    
    
  "title": ["Nintendo", "switch"]
}
## 自动补全查询
GET /test02/_search
{
    
    
  "suggest": {
    
    
    "title_suggest": {
    
    
      "text": "s", # 关键字
      "completion": {
    
    
        "field": "title", #补全查询的字段
        "skip_duplicates": true, #跳过重复的
        "size": 10 #获取前10条结果
      }
    }
  }
}

2.4 API de Java para consultas de finalización automática

  • Primero mire la API construida por el parámetro de solicitud:
    inserte la descripción de la imagen aquí
  • Veamos el análisis de resultados:
    inserte la descripción de la imagen aquí
  • Código importante:
    @Override
    public List<String> getSuggestions(String prefix) {
          
          
        try {
          
          
            // 1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            // 2.准备DSL
            request.source().suggest(new SuggestBuilder().addSuggestion(
                "suggestions",
                SuggestBuilders.completionSuggestion("suggestion")
                .prefix(prefix)
                .skipDuplicates(true)
                .size(10)
            ));
            // 3.发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 4.解析结果
            Suggest suggest = response.getSuggest();
            // 4.1.根据补全查询名称,获取补全结果
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
            // 4.2.获取options
            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            // 4.3.遍历
            List<String> list = new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option : options) {
          
          
                String text = option.getText().toString();
                list.add(text);
            }
            return list;
        } catch (IOException e) {
          
          
            throw new RuntimeException(e);
        }
    }
    

Sincronización de tres datos

  • Introducir:
    inserte la descripción de la imagen aquí

3.1 Análisis del pensamiento

  • Hay tres esquemas comunes de sincronización de datos:
    • Solución 1: llamada síncrona
    • Solución 2: notificación asíncrona
    • Solución 3: monitorear binlog

3.2 Solución 1: llamada síncrona

inserte la descripción de la imagen aquí

  • Los pasos básicos son los siguientes:
    • hotel-demo proporciona una interfaz para modificar los datos en elasticsearch
    • Una vez que el servicio de administración del hotel completa la operación de la base de datos, llama directamente a la interfaz proporcionada por hotel-demo

3.3 Solución 2: notificación asíncrona

inserte la descripción de la imagen aquí

  • El proceso es el siguiente:
    • El administrador del hotel envía un mensaje MQ después de agregar, eliminar y modificar los datos de la base de datos mysql
    • Hotel-demo escucha MQ y completa la modificación de datos de elasticsearch después de recibir el mensaje

3.4 Supervisar binlog

inserte la descripción de la imagen aquí

  • El proceso es el siguiente:
    • Habilite la función binlog para mysql
    • Las operaciones de adición, eliminación y modificación de mysql quedarán registradas en el binlog
    • Hotel-demo monitorea los cambios de binlog según el canal y actualiza el contenido en elasticsearch en tiempo real

3.5 Comparación y resumen de tres esquemas

plan llamada síncrona llamada asíncrona registro de binlog del monitor
ventaja Darse cuenta simple, crudo Acoplamiento bajo, generalmente difícil de implementar Desacoplamiento completo entre servicios
defecto Alto grado de acoplamiento empresarial Confíe en la fiabilidad de mq Habilitar binlog aumenta la carga de la base de datos y hace que la implementación sea más compleja

3.6 Resumen de casos de sincronización de datos

  • Cuando se agregan, eliminan o modifican los datos del hotel, se requiere la misma operación para los datos en elasticsearch.

  • paso:

    • Importe el proyecto hotel-admin proporcionado por los datos, inicie y pruebe el CRUD de los datos del hotel
    • Complete el envío de mensajes en agregar, eliminar y cambiar negocios en hotel-admin
    • Use anotaciones para declarar intercambio, cola y RoutingKey en hotel-demo, monitoreo completo de mensajes y actualización de datos en elasticsearch
    • Iniciar y probar la función de sincronización de datos
  • La estructura MQ se muestra en la figura:
    inserte la descripción de la imagen aquí

  • confiar:

    <!--amqp-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    
  • Declarar el nombre de intercambio de la cola

    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";
    }
    
  • Enviar mensaje MQ

     @PostMapping
        public void saveHotel(@RequestBody Hotel hotel){
          
          
            hotelService.save(hotel);
            rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE,MqConstants.HOTEL_INSERT_KEY,hotel.getId());
        }
    
        @PutMapping()
        public void updateById(@RequestBody Hotel hotel){
          
          
            if (hotel.getId() == null) {
          
          
                throw new InvalidParameterException("id不能为空");
            }
            hotelService.updateById(hotel);
    
            rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE,MqConstants.HOTEL_INSERT_KEY,hotel.getId());
        }
    
        @DeleteMapping("/{id}")
        public void deleteById(@PathVariable("id") Long id) {
          
          
            hotelService.removeById(id);
            rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE,MqConstants.HOTEL_DELETE_KEY,id);
        }
    
  • Recibir mensaje MQ

    • Las cosas que hacer cuando hotel-demo recibe mensajes MQ incluyen:
      • Nuevo mensaje: consulte la información del hotel de acuerdo con la identificación del hotel pasada y luego agregue un dato a la biblioteca de índice
    • Eliminar mensaje: elimine una parte de los datos en la biblioteca de índice de acuerdo con la identificación del hotel pasada
    • serviceDefinir servicios nuevos y eliminados en
    void deleteById(Long id);
    
    void insertById(Long id);
    
    • En su clase de implementación, implementa el negocio
    @Override
    public void deleteById(Long id) {
          
          
        try {
          
          
            // 1.准备Request
            DeleteRequest request = new DeleteRequest("hotel", id.toString());
            // 2.发送请求
            client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
          
          
            throw new RuntimeException(e);
        }
    }
    
    @Override
    public void insertById(Long id) {
          
          
        try {
          
          
            // 0.根据id查询酒店数据
            Hotel hotel = getById(id);
            // 转换为文档类型
            HotelDoc hotelDoc = new HotelDoc(hotel);
    
            // 1.准备Request对象
            IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
            // 2.准备Json文档
            request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
            // 3.发送请求
            client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
          
          
            throw new RuntimeException(e);
        }
    }
    
  • Escribe un oyente

    import cn.itcast.hotel.constants.MqConstants;
    import cn.itcast.hotel.service.IHotelService;
    import org.springframework.amqp.core.ExchangeTypes;
    import org.springframework.amqp.rabbit.annotation.Exchange;
    import org.springframework.amqp.rabbit.annotation.Queue;
    import org.springframework.amqp.rabbit.annotation.QueueBinding;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class HotelListener {
          
          
        @Autowired
        private IHotelService hotelService;
    
        /**
         * 监听酒店新增或修改的业务
         * @param id 酒店id
         *           queues =
         */
        @RabbitListener(bindings = @QueueBinding(value = @Queue(name =MqConstants.HOTEL_INSERT_QUEUE),
                        exchange = @Exchange(name = MqConstants.HOTEL_EXCHANGE,type = ExchangeTypes.TOPIC, autoDelete="false",durable = "true"),
                        key = {
          
          MqConstants.HOTEL_INSERT_KEY}
                        )
        )
        public void listenHotelInsertOrUpdate(Long id){
          
          
            hotelService.insertById(id);
        }
    
        /**
         * 监听酒店删除的业务
         * @param id 酒店id
         */
        @RabbitListener(bindings = @QueueBinding(value = @Queue(name =MqConstants.HOTEL_DELETE_QUEUE),
                exchange = @Exchange(name = MqConstants.HOTEL_EXCHANGE,type = ExchangeTypes.TOPIC, autoDelete="false",durable = "true"),
                key = {
          
          MqConstants.HOTEL_DELETE_KEY}
        )
        )
        public void listenHotelDelete(Long id){
          
          
            hotelService.deleteById(id);
        }
    }
    

3.7 Caso de prueba de sincronización de datos

  • En rabbitMq, puede ver que el registro de la cola está completo
    inserte la descripción de la imagen aquí
  • Confirme el funcionamiento normal del proyecto a través del enlace del interruptor.
    inserte la descripción de la imagen aquí

  • 上海希尔顿酒店precio revisado
    inserte la descripción de la imagen aquí
  • El ID del documento visto por vue devtoolsla herramienta de complemento上海希尔顿酒店
    inserte la descripción de la imagen aquí
  • Luego, edita su precio.
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí
  • Ver, el registro de mensajes de la cola
    inserte la descripción de la imagen aquí
  • consultar precios
    inserte la descripción de la imagen aquí

3.8 Suplemento: Instalación del complemento vue Devtools

  • ¡No recomiendo a todos, descargue el código fuente usted mismo, compílelo e instálelo manualmente! ! ! ¡Encontrarás muchos errores, y tus esfuerzos son ingratos! ! !

3.8.1 Método de instalación del navegador Edge

  • En la tienda de extensiones de Edge, busque e instale
    • La versión actual es la versión estable 6.5.0
      inserte la descripción de la imagen aquí

3.8.2 Cómo instalar el navegador Chrome

  1. Descarga Vue Devtools y descomprímelo
  2. Active el modo desarrollador de Chrome: Configuración->Extensiones->Modo desarrollador
    inserte la descripción de la imagen aquí
  3. Arrastre los archivos en la carpeta descomprimida .crxa la página del programa de extensión del navegador Chrome y haga clic en Agregar extensión.

3.9 Explicación de la herramienta vue devtools

  • Pruebas posteriores a la instalación
    • Esta herramienta, solo cuando la página frontal de vue se ejecuta localmente, la consola mostrará el complemento y no aparecerá en otras páginas web.
    • Entonces: la forma correcta de verificar si la instalación fue exitosa es: inicie el proyecto local de vue, abra la consola para ver las herramientas de desarrollo de vue
      inserte la descripción de la imagen aquí
  • El valor en el archivo de configuración predeterminado del complemento persistentes: true, por lo que no se requiere ninguna modificación.

Cuatro clústeres de búsqueda elástica

  • La búsqueda elástica independiente para el almacenamiento de datos inevitablemente enfrentará dos problemas: el almacenamiento masivo de datos y el punto único de falla.
    • Problema de almacenamiento masivo de datos: divida lógicamente la biblioteca de índices en N fragmentos (shards) y almacénelos en varios nodos
    • Problema de punto único de falla: copia de seguridad de datos fragmentados en diferentes nodos (réplica)
      inserte la descripción de la imagen aquí

4.1 Conceptos relacionados con el clúster ES

  • Clúster (clúster): Un grupo de nodos con un nombre de clúster común.
  • Nodo (nodo) : una instancia de Elasticearch en el clúster
  • Fragmento : los índices se pueden dividir en diferentes partes para el almacenamiento, llamados fragmentos. En un entorno de clúster, diferentes fragmentos de un índice se pueden dividir en diferentes nodos
  • Resuelva el problema: la cantidad de datos es demasiado grande y la capacidad de almacenamiento de un solo punto es limitada.
    inserte la descripción de la imagen aquí
    • Fragmento primario (Primary shard): relativo a la definición de fragmentos de réplica.
    • Fragmento de réplica (fragmento de réplica) Cada fragmento principal puede tener una o más copias, y los datos son los mismos que los del fragmento principal.

  • Para encontrar un equilibrio entre alta disponibilidad y costo, podemos hacer esto:
    • Primero fragmente los datos y guárdelos en diferentes nodos
    • Luego haga una copia de seguridad de cada fragmento y colóquelo en el otro nodo para completar la copia de seguridad mutua
      inserte la descripción de la imagen aquí
  • Ahora, cada fragmento tiene 1 copia de seguridad, almacenada en 3 nodos:
    • node0: contiene fragmentos 0 y 1
    • node1: contiene fragmentos 0 y 2
    • nodo2: fragmentos guardados 1 y 2

4.2 Crear un clúster ES

  • Usamos contenedores docker para ejecutar varias instancias de es en una sola máquina para simular clústeres de es. Sin embargo, en el entorno de producción, se recomienda que solo implemente una instancia de es en cada nodo de servicio.

4.3.1 Crear clúster es

  1. Primero escriba un archivo docker-compose.yml con el siguiente contenido:
    version: '2.2'
    services:
      es01:
        image: elasticsearch:7.12.1
        container_name: es01
        environment:
          - node.name=es01
          - cluster.name=es-docker-cluster
          - discovery.seed_hosts=es02,es03
          - cluster.initial_master_nodes=es01,es02,es03
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        volumes:
          - data01:/usr/share/elasticsearch/data
        ports:
          - 9200:9200
        networks:
          - elastic
      es02:
        image: elasticsearch:7.12.1
        container_name: es02
        environment:
          - node.name=es02
          - cluster.name=es-docker-cluster
          - discovery.seed_hosts=es01,es03
          - cluster.initial_master_nodes=es01,es02,es03
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        volumes:
          - data02:/usr/share/elasticsearch/data
        ports:
          - 9201:9200
        networks:
          - elastic
      es03:
        image: elasticsearch:7.12.1
        container_name: es03
        environment:
          - node.name=es03
          - cluster.name=es-docker-cluster
          - discovery.seed_hosts=es01,es02
          - cluster.initial_master_nodes=es01,es02,es03
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        volumes:
          - data03:/usr/share/elasticsearch/data
        networks:
          - elastic
        ports:
          - 9202:9200
    volumes:
      data01:
        driver: local
      data02:
        driver: local
      data03:
        driver: local
    
    networks:
      elastic:
        driver: bridge
    
  2. la operación es necesita modificar algunos permisos del sistema linux, modificar /etc/sysctl.confarchivos
    vi /etc/sysctl.conf
    
    inserte la descripción de la imagen aquí
  3. Luego ejecute el comando para que la configuración surta efecto:
    sysctl -p
    
  4. Inicie el clúster a través de docker-compose
    docker-compose up -d
    
    [root@kongyue tmp]# docker-compose up -d
    Starting es01 ... done
    Creating es03 ... done
    Creating es02 ... done
    

4.4 Supervisión del estado del clúster

4.4.1 Win instalación cerebro [no recomendado]

  • Kibana puede monitorear clústeres de es, pero la nueva versión necesita confiar en la función x-pack de es, y la configuración es más complicada.

  • Se recomienda usar cerebro para monitorear el estado del clúster es. El directorio descomprimido del sitio web oficial es el siguiente:
    inserte la descripción de la imagen aquí

  • Introduzca el directorio bin correspondiente:
    inserte la descripción de la imagen aquí

  • Haga doble clic en el archivo cerebro.bat para iniciar el servicio.

4.4.2 Problema de retroceso [sin resolver]

Oops, cannot start the server.
com.google.common.util.concurrent.UncheckedExecutionException: java.lang.IllegalStateException: Unable to load cache item
        at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2051)
        at com.google.common.cache.LocalCache.get(LocalCache.java:3951)
        at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3974)
        at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4958)
        at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4964)
        at com.google.inject.internal.FailableCache.get(FailableCache.java:54)
        at com.google.inject.internal.ConstructorInjectorStore.get(ConstructorInjectorStore.java:49)
        at com.google.inject.internal.ConstructorBindingImpl.initialize(ConstructorBindingImpl.java:155)
        at com.google.inject.internal.InjectorImpl.initializeBinding(InjectorImpl.java:592)
        at com.google.inject.internal.AbstractBindingProcessor$Processor.initializeBinding(AbstractBindingProcessor.java:173)
        at com.google.inject.internal.AbstractBindingProcessor$Processor.lambda$scheduleInitialization$0(AbstractBindingProcessor.java:160)
        at com.google.inject.internal.ProcessedBindingData.initializeBindings(ProcessedBindingData.java:49)
        at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:124)
        at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:108)
        at com.google.inject.Guice.createInjector(Guice.java:87)
        at com.google.inject.Guice.createInjector(Guice.java:78)
        at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:200)
        at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:155)
        at play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
        at play.core.server.ProdServerStart$.start(ProdServerStart.scala:54)
        at play.core.server.ProdServerStart$.main(ProdServerStart.scala:30)
        at play.core.server.ProdServerStart.main(ProdServerStart.scala)
Caused by: java.lang.IllegalStateException: Unable to load cache item
        at com.google.inject.internal.cglib.core.internal.$LoadingCache.createEntry(LoadingCache.java:79)
        at com.google.inject.internal.cglib.core.internal.$LoadingCache.get(LoadingCache.java:34)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator.create(AbstractClassGenerator.java:294)
        at com.google.inject.internal.cglib.reflect.$FastClass$Generator.create(FastClass.java:65)
        at com.google.inject.internal.BytecodeGen.newFastClassForMember(BytecodeGen.java:258)
        at com.google.inject.internal.BytecodeGen.newFastClassForMember(BytecodeGen.java:207)
        at com.google.inject.internal.DefaultConstructionProxyFactory.create(DefaultConstructionProxyFactory.java:49)
        at com.google.inject.internal.ProxyFactory.create(ProxyFactory.java:156)
        at com.google.inject.internal.ConstructorInjectorStore.createConstructor(ConstructorInjectorStore.java:94)
        at com.google.inject.internal.ConstructorInjectorStore.access$000(ConstructorInjectorStore.java:30)
        at com.google.inject.internal.ConstructorInjectorStore$1.create(ConstructorInjectorStore.java:38)
        at com.google.inject.internal.ConstructorInjectorStore$1.create(ConstructorInjectorStore.java:34)
        at com.google.inject.internal.FailableCache$1.load(FailableCache.java:43)
        at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3529)
        at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2278)
        at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2155)
        at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2045)
        ... 21 more
Caused by: java.lang.ExceptionInInitializerError
        at com.google.inject.internal.cglib.core.$DuplicatesPredicate.evaluate(DuplicatesPredicate.java:104)
        at com.google.inject.internal.cglib.core.$CollectionUtils.filter(CollectionUtils.java:52)
        at com.google.inject.internal.cglib.reflect.$FastClassEmitter.<init>(FastClassEmitter.java:69)
        at com.google.inject.internal.cglib.reflect.$FastClass$Generator.generateClass(FastClass.java:77)
        at com.google.inject.internal.cglib.core.$DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
        at com.google.inject.internal.cglib.core.internal.$LoadingCache$2.call(LoadingCache.java:54)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
        at com.google.inject.internal.cglib.core.internal.$LoadingCache.createEntry(LoadingCache.java:61)
        ... 38 more
Caused by: com.google.inject.internal.cglib.core.$CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @6a988392
        at com.google.inject.internal.cglib.core.$ReflectUtils.defineClass(ReflectUtils.java:464)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
        at com.google.inject.internal.cglib.core.internal.$LoadingCache$2.call(LoadingCache.java:54)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
        at com.google.inject.internal.cglib.core.internal.$LoadingCache.createEntry(LoadingCache.java:61)
        at com.google.inject.internal.cglib.core.internal.$LoadingCache.get(LoadingCache.java:34)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
        at com.google.inject.internal.cglib.core.$AbstractClassGenerator.create(AbstractClassGenerator.java:294)
        at com.google.inject.internal.cglib.core.$KeyFactory$Generator.create(KeyFactory.java:221)
        at com.google.inject.internal.cglib.core.$KeyFactory.create(KeyFactory.java:174)
        at com.google.inject.internal.cglib.core.$KeyFactory.create(KeyFactory.java:157)
        at com.google.inject.internal.cglib.core.$KeyFactory.create(KeyFactory.java:149)
        at com.google.inject.internal.cglib.core.$KeyFactory.create(KeyFactory.java:145)
        at com.google.inject.internal.cglib.core.$MethodWrapper.<clinit>(MethodWrapper.java:23)
        ... 49 more
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @6a988392
        at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:387)
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:363)
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:311)
        at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:201)
        at java.base/java.lang.reflect.Method.setAccessible(Method.java:195)
        at com.google.inject.internal.cglib.core.$ReflectUtils$1.run(ReflectUtils.java:61)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
        at com.google.inject.internal.cglib.core.$ReflectUtils.<clinit>(ReflectUtils.java:52)
        at com.google.inject.internal.cglib.reflect.$FastClassEmitter.<init>(FastClassEmitter.java:67)
        ... 46 more
  • Debido a que la versión jdk es demasiado alta, no es compatible con cerebo. ¡Parece que no hay solución en este momento! ! !
  • ¡Por supuesto, el nivel del autor es limitado! Si hay una solución, por favor hágamelo saber, muchas gracias

4.4.3 instalar cerebo en linux

  1. descargar cerebro
    inserte la descripción de la imagen aquí
    • Sugerencia: instale el complemento del acelerador github en el navegador [omita si tiene una escalera]
  2. Luego suba el paquete comprimido a Linux para su instalación
    rpm -ivh cerebro-0.9.4-1.noarch.rpm
    
    inserte la descripción de la imagen aquí
  3. Modificar el archivo de configuración
    vim /usr/share/cerebro/conf/application.conf
    
    inserte la descripción de la imagen aquí
  4. Comprobar y cerrar el estado de inicio de cerebro: [no recomendado]
    • No se puede acceder a este método de arranque desde dispositivos externos
    # 停止
    systemctl stop cerebro  
    # 开启
    systemctl start cerebro
    # 查看状态
    systemctl status cerebro
    
    inserte la descripción de la imagen aquí
  5. Comando de inicio:
    • Para facilitar la resolución de problemas, puede usar directamente el comando para iniciar cerebro
    /usr/share/cerebro/bin/cerebro
    
    • Iniciado por defecto:
    [info] play.api.Play - Application started (Prod) (no global state)
    [info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000
    
  6. visita ip:9000:
    inserte la descripción de la imagen aquí
  7. Ingrese la dirección y el puerto de cualquier nodo de elasticsearch, haga clic en conectar
    inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí
- Una barra verde significa que el clúster es verde (saludable)

4.4.5 Crear una biblioteca de índices

4.4.6 Uso de DevTools de Kibana para crear una biblioteca de índices [operación no práctica]

  • Ingrese el comando en DevTools:
    PUT /itcast
    {
          
          
      "settings": {
          
          
        "number_of_shards": 3, // 分片数量
        "number_of_replicas": 1 // 副本数量
      },
      "mappings": {
          
          
        "properties": {
          
          
          // mapping映射定义 ...
        }
      }
    }
    

4.4.7 Uso de cerebro para crear una biblioteca de índices [operación práctica]

  • También puede crear una biblioteca de índices con cerebro:

inserte la descripción de la imagen aquí

  • Complete la información de la biblioteca del índice:
    inserte la descripción de la imagen aquí

  • Haga clic en el botón Crear en la esquina inferior derecha:
    inserte la descripción de la imagen aquí

  • Haga clic en el botón Crear en la esquina inferior derecha:
    inserte la descripción de la imagen aquí

    inserte la descripción de la imagen aquí

4.4.8 Ver efecto de fragmentación

  • Regrese a la página de inicio y podrá ver el efecto de fragmentación de la biblioteca de índices:
    inserte la descripción de la imagen aquí

4.9 Problema de cerebro dividido en racimo

4.9.1 División de responsabilidades del clúster

  • Los nodos de clúster en elasticsearch tienen diferentes responsabilidades:
    inserte la descripción de la imagen aquí
  • El clúster debe separar las responsabilidades del clúster:
    • nodo maestro: altos requisitos de CPU, pero requisitos de memoria
    • nodo de datos: altos requisitos de CPU y memoria
    • Nodo de coordinación: altos requisitos de ancho de banda de red y CPU
  • La separación de tareas puede asignar hardware diferente para la implementación de acuerdo con las necesidades de los diferentes nodos. Y evitar la interferencia mutua entre servicios.
  • Cada rol de nodo en elasticsearch tiene sus propias responsabilidades diferentes, por lo que se recomienda que cada nodo tenga un rol independiente durante la implementación del clúster.
    inserte la descripción de la imagen aquí

4.9.2 Problema del cerebro dividido

  • De forma predeterminada, cada nodo es un nodo principal elegible, por lo que una vez que el nodo principal deja de funcionar, otros nodos candidatos elegirán uno para convertirse en el nodo principal. Puede ocurrir un problema de cerebro dividido cuando falla la red entre el nodo maestro y otros nodos.
    inserte la descripción de la imagen aquí

  • Después de que se elige el nodo 3, el clúster continúa brindando servicios externos. El nodo 2 y el nodo 3 forman un clúster por sí mismos, y el nodo 1 forma un clúster por sí mismos. Los datos de los dos clústeres no están sincronizados, lo que resulta en discrepancias de datos y situaciones de cerebro dividido.

  • Para evitar el cerebro dividido, es necesario exigir que los votos excedan (número de nodos elegibles + 1)/2 para ser elegido maestro, por lo que el número de nodos elegibles es preferiblemente un número impar.

  • El elemento de configuración correspondiente es discovery.zen.minimum_master_nodes, que se ha convertido en la configuración predeterminada después de es7.0, por lo que el problema del cerebro dividido generalmente no ocurre.

4.9.3 Resumen

  • El rol del nodo maestro elegible:

    • Participar en la elección del grupo.
    • El nodo maestro puede administrar el estado del clúster, administrar información de fragmentación y procesar solicitudes para crear y eliminar bibliotecas de índices.
  • El papel del nodo de datos:

    • CRUD de datos
  • El papel del nodo coordinador:

    • Enrutar solicitudes a otros nodos
    • Combine los resultados de la consulta y devuélvalos al usuario

4.10 Almacenamiento distribuido en clúster

  • Cuando se agregan nuevos documentos, deben guardarse en diferentes fragmentos para garantizar el equilibrio de los datos, entonces, ¿cómo determina el nodo coordinador en qué fragmento se deben almacenar los datos?

4.10.1 Prueba de almacenamiento de fragmentos

  • La herramienta utilizada para la prueba insomnia, Insomnia, como cartero, es una aplicación de escritorio de prueba de interfaz multiplataforma gratuita.
  • Sitio web oficial de Insomnia , si desea descargar, se recomienda descargar desde otros sitios web, el sitio web oficial es demasiado lento
  • Aquí el autor proporciona la última versión de "Insomnia.Core-2023.1.0.exe"
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí
  • Puede ver en la prueba que los tres datos están en fragmentos diferentes:
    inserte la descripción de la imagen aquí

4.10.2 Principio de almacenamiento de fragmentos

  • Elasticsearch utilizará el algoritmo hash para calcular en qué fragmento se debe almacenar el documento:
    inserte la descripción de la imagen aquí
  • ilustrar:
    • _routing por defecto es el id del documento
    • El algoritmo está relacionado con la cantidad de fragmentos, por lo que una vez que se crea la biblioteca de índice, la cantidad de fragmentos no se puede modificar.

  • El proceso de agregar un nuevo documento es el siguiente
    inserte la descripción de la imagen aquí
  • Interpretación:
    • 1) Agregar un documento con id=1
    • 2) Realice una operación hash en la identificación, si el resultado es 2, debe almacenarse en shard-2
    • 3) El fragmento principal del fragmento 2 está en el nodo 3 y los datos se enrutan al nodo 3.
    • 4) Guardar el documento
    • 5) Sincronizar con la réplica 2 del fragmento 2, en el nodo node2
    • 6) Devolver el resultado al nodo del nodo de coordinación

4.10.3 Consulta distribuida de clúster

  • La consulta de elasticsearch se divide en dos etapas:
    • fase de dispersión: en la fase de dispersión, el nodo coordinador distribuirá la solicitud a cada fragmento
    • fase de recopilación: la fase de recopilación, el nodo de coordinación resume los resultados de búsqueda del nodo de datos, lo procesa como el conjunto de resultados final y lo devuelve al usuario

inserte la descripción de la imagen aquí

4.10.4 Conmutación por error del clúster

  • Conmutación por error: el nodo maestro del clúster monitoreará el estado de los nodos en el clúster.Si se encuentra que un nodo está inactivo, migrará inmediatamente los datos fragmentados del nodo inactivo a otros nodos para garantizar la seguridad de los datos.
  • Por ejemplo, en la figura se muestra una estructura de clúster: el nodo 1 es el nodo maestro y los otros dos nodos son nodos esclavos.
    inserte la descripción de la imagen aquí
  • nodo1 ha fallado
    inserte la descripción de la imagen aquí
  • Lo primero después del tiempo de inactividad es volver a elegir el maestro, por ejemplo, seleccione el nodo2
    inserte la descripción de la imagen aquí
  • Después de que el nodo 2 se convierta en el nodo maestro, verificará el estado de monitoreo del clúster y encontrará que: el fragmento 1 y el fragmento 0 no tienen nodos de réplica. Por lo tanto, los datos del nodo 1 deben migrarse al nodo 2 y al nodo 3.
    inserte la descripción de la imagen aquí
  • Demostración de animación:
    inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/yang2330648064/article/details/129870103
Recomendado
Clasificación