基于 SpringBoot 2.7.x 使用最新的 Elasticsearch Java API Client 之 ElasticsearchClient

news2024/12/23 14:03:18

1. 从 RestHighLevelClient 到 ElasticsearchClient

从 Java Rest Client 7.15.0 版本开始,Elasticsearch 官方决定将 RestHighLevelClient 标记为废弃的,并推荐使用新的 Java API Client,即 ElasticsearchClient. 为什么要将 RestHighLevelClient 废弃,大概有以下几点:

  • 维护成本高:RestHighLevelClient 需要和 Elasticsearch APIs 的更新保持一致,而 Elasticsearch APIs 更新较为频繁,因此每次 Elasticsearch APIs 有新的迭代,RestHighLevelClient 也要跟着迭代,维护成本高。
  • 兼容性差: 由于 RestHighLevelClient 和 Elasticsearch 的内部数据结构紧耦合,而 Elasticsearch 不同版本的数据结构可能略有差异,因此很难跨不同版本的 Elasticsearch 保持兼容。
  • 灵活度低: RestHighLevelClient 的灵活性扩展性较差,很难去扩展或者自定义一些功能。

而 Spring 官方对 Elasticsearch 客户端也进行了封装,集成于 spring-boot-starter-data-elasticsearch 模块,Elasticsearch 官方决定废弃 RestHighLevelClient 而支持 ElasticsearchClient 这一举措,必然也导致 Spring 项目组对 data-elasticserach 模块进行同步更新,以下是 Spring 成员对相关内容的讨论:

  • https://github.com/spring-projects/spring-boot/issues/28597

大概内容就是在对 ElasticsearchClient 自动装配的支持会在 springboot 3.0.x 版本中体现,而在 2.7.x 版本中会将 RestHighLevelClient 标记为废弃的。

由于我们的项目是基于 springboot 2.7.10 版本开发的,而 2.7.x 作为最后一个 2.x 版本,springboot 下个版本为 3.x,恰逢项目已经规划在半年后将 JDK 升级为17版本,全面支持 springboot 3.x 版本的替换,因此现阶段需要封装一个能够跨 2.7.x 和 3.x 版本都可以使用的 Elasticsearch 客户端。

2. 自定义 starter 模块实现 ElasticsearchTemplate 的自动装配

在调研了 spring-boot 2.7.10 版本的源码后发现,其实 2.7.x 版本已经引入了 ElasticsearchClient,并封装了新的客户端 ElasticsearchTemplate,但是并没有为其做自动装配,如果想要使用基于ElasticsearchClient 的 ElasticsearchTemplate,需要用户自己装配。否则,直接使用 ElasticsearchTemplate 会出现以下提示:

Consider defining a bean of type 'org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate' in your configuration.

即由提示可以知道,无法创建一个 ElasticsearchTemplate 类型的 bean.

因此需要自己实现 ElasticsearchTemplate 的装配,才可以使用。为了能够一次装配多项目复用,决定自己构建一个starter,之后需要使用 ElasticsearchTemplate,可以通过引入依赖的方式完成自动装配。

自定义的 starter 项目目录结构如下图所示:
在这里插入图片描述

pom.xml 文件:

<?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>
    <description>自定义elasticsearch-client组件</description>
    <parent>
        <artifactId>xxx-spring-boot-starters</artifactId>
        <groupId>com.xxx.commons</groupId>
        <version>${revision}</version>
    </parent>
    <artifactId>xxx-elasticsearch-client-spring-boot-starter</artifactId>
    <packaging>jar</packaging>
    <name>xxx-elasticsearch-client-spring-boot-starter</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>jakarta.json</groupId>
                    <artifactId>jakarta.json-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.0.1</version>
        </dependency>
    </dependencies>
</project>

org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件

com.xxx.commons.springboot.elasticsearch.ElasticsearchTemplateAutoConfiguration
com.xxx.commons.springboot.elasticsearch.actuate.xxxElasticsearchHealthIndicatorAutoConfiguration

PackageInfo 接口:

package com.xxx.commons.springboot.elasticsearch;

/**
 * @author reader
 * Date: 2023/9/18 22:21
 **/
public interface PackageInfo {
}

RestClientBuilder 类:

package com.xxx.commons.springboot.elasticsearch;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;

import java.net.URI;
import java.net.URISyntaxException;

/**
 * @author reader
 * Date: 2023/9/20 15:16
 **/
public final class RestClientBuilder {

    private RestClientBuilder() {
    }

    public static RestClient buildWithProperties(ElasticsearchProperties properties) {
        HttpHost[] hosts = properties.getUris().stream().map(RestClientBuilder::createHttpHost).toArray((x$0) -> new HttpHost[x$0]);
        org.elasticsearch.client.RestClientBuilder builder = RestClient.builder(hosts);
        builder.setDefaultHeaders(new BasicHeader[]{new BasicHeader("Content-type", "application/json")});
        builder.setHttpClientConfigCallback((httpClientBuilder) -> {
            httpClientBuilder.addInterceptorLast((HttpResponseInterceptor) (response, context) -> response.addHeader("X-Elastic-Product", "Elasticsearch"));
            if (hasCredentials(properties.getUsername(), properties.getPassword())) {
                // 密码配置
                CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(properties.getUsername(), properties.getPassword()));
                httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }
            // httpClient配置
            return httpClientBuilder;
        });
        builder.setRequestConfigCallback((requestConfigBuilder) -> {
            // request配置
            requestConfigBuilder.setConnectionRequestTimeout((int)properties.getConnectionTimeout().getSeconds() * 1000);
            requestConfigBuilder.setSocketTimeout((int)properties.getSocketTimeout().getSeconds() * 1000);
            return requestConfigBuilder;
        });
        if (properties.getPathPrefix() != null) {
            builder.setPathPrefix(properties.getPathPrefix());
        }
        return builder.build();
    }

    private static boolean hasCredentials(String username, String password) {
        return StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password);
    }

    private static HttpHost createHttpHost(String uri) {
        try {
            return createHttpHost(URI.create(uri));
        } catch (IllegalArgumentException var2) {
            return HttpHost.create(uri);
        }
    }

    private static HttpHost createHttpHost(URI uri) {
        if (StringUtils.isBlank(uri.getUserInfo())) {
            return HttpHost.create(uri.toString());
        } else {
            try {
                return HttpHost.create((new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment())).toString());
            } catch (URISyntaxException var2) {
                throw new IllegalStateException(var2);
            }
        }
    }
}

ElasticsearchClientConfiguration 类:

package com.xxx.commons.springboot.elasticsearch;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author reader
 * Date: 2023/9/20 14:59
 **/
@Configuration
@EnableConfigurationProperties({ElasticsearchProperties.class})
@ConditionalOnClass({ElasticsearchClient.class, ElasticsearchTransport.class})
public class ElasticsearchClientConfiguration {

    protected static final Log LOGGER = LogFactory.getLog(ElasticsearchClientConfiguration.class);

    private ElasticsearchProperties elasticsearchProperties;

    public ElasticsearchClientConfiguration(ElasticsearchProperties elasticsearchProperties) {
        LOGGER.info("框架 elasticsearch-client-starter elasticsearchProperties 装载开始");
        this.elasticsearchProperties = elasticsearchProperties;
    }

    @Bean
    public ElasticsearchClient elasticsearchClient() {
        LOGGER.info("框架 elasticsearch-client-starter elasticsearchClient 装载开始");
        RestClient restClient = RestClientBuilder.buildWithProperties(elasticsearchProperties);
        RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }
}
package com.xxx.commons.springboot.elasticsearch;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import com.xxx.commons.springboot.elasticsearch.actuate.ElasticsearchInfoContributor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.info.ConditionalOnEnabledInfoContributor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScanner;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * @author reader
 * Date: 2023/9/19 16:35
 **/
@AutoConfiguration(before = {ElasticsearchRestClientAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class})
@ConditionalOnClass({ElasticsearchTemplate.class})
@EnableConfigurationProperties({ElasticsearchProperties.class})
@Import({ElasticsearchClientConfiguration.class})
public class ElasticsearchTemplateAutoConfiguration {

    protected static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplateAutoConfiguration.class);

    @Bean
    ElasticsearchCustomConversions elasticsearchCustomConversions() {
        return new ElasticsearchCustomConversions(Collections.emptyList());
    }

    @Bean
    public SimpleElasticsearchMappingContext elasticsearchMappingContext(ApplicationContext applicationContext,
                                                                         ElasticsearchCustomConversions elasticsearchCustomConversions) throws ClassNotFoundException {
        SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
        mappingContext.setInitialEntitySet(new EntityScanner(applicationContext).scan(Document.class));
        mappingContext.setSimpleTypeHolder(elasticsearchCustomConversions.getSimpleTypeHolder());
        return mappingContext;
    }

    @Bean
    ElasticsearchConverter elasticsearchConverter(SimpleElasticsearchMappingContext mappingContext,
                                                  ElasticsearchCustomConversions elasticsearchCustomConversions) {
        MappingElasticsearchConverter converter = new MappingElasticsearchConverter(mappingContext);
        converter.setConversions(elasticsearchCustomConversions);
        return converter;
    }

    @Bean
    ElasticsearchTemplate elasticsearchTemplate(ElasticsearchClient client, ElasticsearchConverter converter) {
        LOGGER.info("框架 elasticsearch-client-starter elasticsearchTemplate 装载开始");
        return new ElasticsearchTemplate(client, converter);
    }

    @Bean
    @ConditionalOnEnabledInfoContributor("elasticsearch")
    public ElasticsearchInfoContributor elasticsearchInfoContributor(ObjectProvider<ElasticsearchProperties> propertiesObjectProvider) {
        List<ElasticsearchProperties> properties = new ArrayList<>();
        propertiesObjectProvider.forEach(properties::add);
        return new ElasticsearchInfoContributor(properties);
    }
}

健康度指标相关的封装有:

  • ElasticsearchHealthIndicator 类:
package com.xxx.commons.springboot.elasticsearch.actuate;

import org.elasticsearch.client.Node;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author reader
 * Date: 2023/9/20 19:26
 **/
public class ElasticsearchHealthIndicator extends AbstractHealthIndicator {

    private final List<RestClient> clients;

    public ElasticsearchHealthIndicator(List<RestClient> clients) {
        this.clients = clients;
    }

    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
            boolean success = true;
            Map<String, Object> properties = new HashMap<>();

            for (RestClient client : clients) {
                List<Node> nodes = client.getNodes();
                if (null == nodes || nodes.isEmpty()){
                    continue;
                }
                String id = nodes.stream().map(Node::toString).collect(Collectors.joining(";"));
                boolean ps = client.isRunning();
                properties.put("ElasticsearchClient[" + id + "]", ps);
                if (!ps) {
                    success = false;
                }
            }

            if (success) {
                builder.up();
            } else {
                builder.withDetails(properties).down();
            }
    }
}
  • ElasticsearchInfoContributor 类:
package com.xxx.commons.springboot.elasticsearch.actuate;

import com.xxx.commons.springboot.elasticsearch.PackageInfo;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author reader
 * Date: 2023/9/20 19:32
 **/
public class ElasticsearchInfoContributor implements InfoContributor {

    private final List<ElasticsearchProperties> elasticsearchProperties;

    public ElasticsearchInfoContributor(List<ElasticsearchProperties> elasticsearchProperties) {
        this.elasticsearchProperties = elasticsearchProperties;
    }

    @Override
    public void contribute(Info.Builder builder) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("version", PackageInfo.class.getPackage().getImplementationVersion());
        properties.put("_title_", "ElasticsearchTemplate组件");
        elasticsearchProperties.forEach(p -> {
            Map<String, Object> sp = new HashMap<>();
            String id = String.join(";", p.getUris());
            properties.put(id, sp);
            sp.put("nodes", String.join(";", p.getUris()));
            sp.put("user", p.getUsername());
            sp.put("connectionTimeout[ms]", p.getConnectionTimeout().toMillis());
            sp.put("socketTimeout[ms]", p.getSocketTimeout().toMillis());
        });
        builder.withDetail("xxx-elasticsearch-client", properties);
    }
}
  • xxxElasticsearchHealthIndicatorAutoConfiguration 类:
package com.xxx.commons.springboot.elasticsearch.actuate;

import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

import java.util.ArrayList;
import java.util.List;

/**
 * @author reader
 * Date: 2023/9/20 19:45
 **/
@AutoConfiguration(before = {HealthContributorAutoConfiguration.class})
@ConditionalOnEnabledHealthIndicator("elasticsearch")
public class xxxElasticsearchHealthIndicatorAutoConfiguration {

    @Bean("elasticsearchHealthIndicator")
    @ConditionalOnMissingBean
    public ElasticsearchHealthIndicator xxxElasticHealthIndicator(ObjectProvider<RestClient> elasticsearchClientProvider) {
        List<RestClient> restClients = new ArrayList<>();
        elasticsearchClientProvider.forEach(restClients::add);
        return new ElasticsearchHealthIndicator(restClients);
    }
}

3. 使用自定义的 starter

1、在自己封装了一个 starter 工具模块之后,通过引入依赖的方式使用,引入的依赖为:

<dependency>
    <groupId>com.xxx.commons</groupId>
    <artifactId>xxx-elasticsearch-client-spring-boot-starter</artifactId>
    <version>${version}</version>
</dependency>

在 yaml 文件中配置的相关属性信息:

spring:
  elasticsearch:
    uris: http://127.0.0.1:9200         
    username: elastic
    password: password

注入并使用 ElasticsearchTemplate 对 ES 进行操作:

package com.xxx.xxx;

import com.xxx.commons.result.query.PaginationBuilder;
import com.xxx.commons.result.query.Query;
import com.xxx.commons.result.query.QueryBuilder;
import com.xxx.push.domain.AliPushRecordDO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author reader
 * Date: 2023/9/26 14:42
 **/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, properties = {"profile=dev", "debug=true"})
public class ElasticsearchTemplateTest {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Test
    public void testSearch() {
        Query query = QueryBuilder.page(1).pageSize(20).build();
        NativeQueryBuilder nativeQueryBuilder = new NativeQueryBuilder();
        nativeQueryBuilder.withPageable(PageRequest.of(query.getPage() - 1, query.getPageSize()));
        NativeQuery searchQuery = nativeQueryBuilder.build();
        // 查询总数
        long count = elasticsearchTemplate.count(searchQuery, AliPushRecordDO.class);
        PaginationBuilder<AliPushRecordDO> builder = PaginationBuilder.query(query);
        builder.amount((int) count);
        if (count > 0) {
            SearchHits<AliPushRecordDO> aliPushRecordDOSearchHits = elasticsearchTemplate.search(searchQuery, AliPushRecordDO.class);
            List<SearchHit<AliPushRecordDO>> searchHits = aliPushRecordDOSearchHits.getSearchHits();
            List<AliPushRecordDO> aliPushRecordDOList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(searchHits)) {
                searchHits.forEach(searchHit -> aliPushRecordDOList.add(searchHit.getContent()));
            }
            builder.result(aliPushRecordDOList);
        } else {
            builder.result(new ArrayList<>());
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1054637.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大喜国庆,聊聊我正式进入职场的这三个月...

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

Thymeleaf 内联语法使用教程

1 表达式内联 Thrmeleaf标准方言允许使用标签属性(th:)来实现很多的功能&#xff0c;但在有些场景之下&#xff0c;需要将表达式直接写入我们HTML 代码中和CSS代码中及JavaScript代码中【代码和html文件在一起&#xff0c;分能不开&#xff0c;待验证】&#xff0c;称为内联…

[Unity][VR]Oculus透视开发图文教程1-Passthrough应用XR项目设置

Oculus现在已向开发者公布了如何使用自己的设备Camera,本系列课程就来手把手地告诉你如何在Unity中使用这个特性。 第一步,既然用的是Quest的特性,那就需要先引入Quest的Unity开发SDK。并且完成基本的VR开发项目设置。 新建Unity项目后,在编辑器界面先点击Window,打开资…

【实验记录】一些小疑问

1.为什么要选择基于“外观”这一特性来作为回环检测的方案&#xff1f; 朴素思路复杂度高&#xff0c;不利于实时性&#xff1b;基于“里程计”的方案需要知道相机处于何位置下才能发生检测&#xff0c;这与我们需要知道的准确位置相矛盾 基于“外观”的方案与前端和后端均无关…

计算机图形学、贝塞尔曲线及绘制方法、反走样问题的解决(附完整代码)

贝塞尔曲线 1. 本次作业实现的函数及简单描述&#xff08;详细代码见后&#xff09;2. 与本次作业有关的基础知识整理3. 代码描述&#xff08;详细&#xff09;4. 完整代码5. 参考文献 &#xff08;本篇为作者学习计算机图形学时根据作业所撰写的笔记&#xff0c; 如有同课程请…

进程的状态与转换以及组织方式

1.进程的状态 三种基本状态&#xff1a;运行态&#xff0c;就绪态&#xff0c;阻塞态。 1.运行状态 如果一个进程此时在CPU上运行&#xff0c;那么这个进程处于“运行态”。 CPU会执行该进程对应的程序&#xff08;执行指令序列) 2.就绪状态 当进程创建完成后&#xff0c;…

【论文阅读】(CVPR2023)用于半监督医学图像分割的双向复制粘贴

目录 前言方法BCPMean-teacher and Traning StrategyPre-Training via Copy-PasteBidirectional Copy-Paste ImagesBidirectional Copy-Paste Supervisory Signals Loss FunctionTesting Phase 结论 先看这个图&#xff0c;感觉比较清晰。它整个的思路就是把有标签的图片和无标…

动态规划算法(1)--矩阵连乘

目录 一、动态数组 1、创建动态数组 2、添加元素 3、删除修改元素 4、访问元素 5、返回数组长度 6、for each遍历数组 二、输入多个数字 1、正则表达式 2、has.next()方法 三、矩阵连乘 1、什么是矩阵连乘&#xff1f; 2、动态规划思路 3、手推m和s矩阵 4、完…

AI伦理与机器道德:人工智能的道德挑战

文章目录 什么是AI伦理和机器道德&#xff1f;1. 隐私保护2. 歧视和不平等3. 透明度和解释性4. 安全性5. 社会影响 AI伦理和机器道德的重要性1. 保护个人权利2. 避免不平等和歧视3. 保持透明和责任4. 促进创新 AI伦理挑战和解决方案1. 隐私保护2. 歧视和不平等3. 透明度和解释性…

P1541 [NOIP2010 提高组] 乌龟棋(4维背包问题)

[NOIP2010 提高组] 乌龟棋 题目背景 小明过生日的时候&#xff0c;爸爸送给他一副乌龟棋当作礼物。 题目描述 乌龟棋的棋盘是一行 N N N 个格子&#xff0c;每个格子上一个分数&#xff08;非负整数&#xff09;。棋盘第 1 1 1 格是唯一的起点&#xff0c;第 N N N 格是…

Nginx简介与Docker Compose部署指南

Nginx是一款高性能的开源Web服务器和反向代理服务器&#xff0c;以其卓越的性能、可伸缩性和灵活性而闻名。它在全球范围内广泛用于托管Web应用程序、负载均衡、反向代理和更多场景中。在本文中&#xff0c;我们将首先介绍Nginx的基本概念&#xff0c;然后演示如何使用Docker C…

Apollo自动驾驶系统概述(文末参与活动赠送百度周边)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

数据结构之美:如何优化搜索和排序算法

文章目录 搜索算法的优化1. 二分搜索2. 哈希表 排序算法的优化1. 快速排序2. 归并排序 总结 &#x1f389;欢迎来到数据结构学习专栏~数据结构之美&#xff1a;如何优化搜索和排序算法 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#x…

阿里云ECS服务器无法发送邮件问题解决方案

这篇文章分享一下自己把项目部署在阿里云ECS上之后&#xff0c;登录邮件提醒时的邮件发送失败问题&#xff0c;无法连接发送邮箱的服务器。 博主使用的springboot提供的发送邮件服务&#xff0c;如下所示&#xff0c;为了实现异步的效果&#xff0c;新开了一个线程来发送邮件。…

【Vim 插件管理器】Vim-plug和Vim-vbundle的区别

- vundle是一款老款的插件管理工具 - vim-plug相对较新&#xff0c;特点是支持异步加载&#xff0c;相比vundle而言 Vim-plug 是一个自由、开源、速度非常快的、极简的 vim 插件管理器。它可以并行地安装或更新插件。你还可以回滚更新。它创建浅层克隆shallow clone最小化磁盘…

【数据结构初阶】六、线性表中的队列(C语言 -- 链式结构实现队列)

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 【数据结构初阶】五、线性表中的栈&#xff08;C语言 -- 顺序表实现栈&#xff09;_高高的胖子的博客-CSDN博客 1 . 队列&#xff08;Queue&#xff09; 队列的概念和结构&#xf…

如何利用niceGUI构建一个流式单轮对话界面

官方文档 参考文档 import asyncio import time import requests from fastapi import FastAPI from nicegui import app, uiclass ChatPage:temperature: ui.slider Nonetop_p: ui.slider Noneapi_key: ui.input Nonemodel_name: ui.input Noneprompt: ui.textarea None…

番外5:下载+安装+配置Linux

任务前期工作&#xff1a; 01. 电脑已安装好VMware Workstation软件&#xff1b; 02.提前下载好Rhel-8.iso映像文件&#xff08;文件较大一般在9.4GB&#xff0c;建议采用迅雷下载&#xff09;&#xff0c;本人使用的以下版本&#xff08;地址ed2k://|file|rhel-8.4-x86_64-dvd…

Tomcat启动后的日志输出为乱码

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

实现三栏布局的十种方式

本文节选自我的博客&#xff1a;实现三栏布局的十种方式 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是MilesChen&#xff0c;偏前端的全栈开发者。&#x1f4dd; CSDN主页&#xff1a;爱吃糖的猫&#x1f525;&#x1f4e3; 我的博客&#xff1a;爱吃糖的猫&…