自定义HikariCP连接池


一、简介

1、概述

官方解释:
快速、简单、可靠。HikariCP 是一个“零开销”的生产就绪 JDBC 连接池。大约 130Kb,库非常轻。

2、地址

Github

https://github.com/brettwooldridge/HikariCP

二、配置参数

1、Hikari原生参数

1. 重要

  • driverClassName

  • jdbcUrl

  • username

  • password

2. 经常使用

  • autoCommit
    此属性控制从池返回的连接的默认自动提交行为。 它是一个布尔值。 默认值:true

  • connectionTimeout
    此属性控制客户端(即您)将等待来自池的连接的最大毫秒数。 如果超过此时间而连接不可用,则会抛出 SQLException。 可接受的最低连接超时为 250 毫秒。 默认值:30000(30 秒)

  • idleTimeout
    此属性控制允许连接在池中闲置的最长时间。 此设置仅在 minimumIdle 定义为小于 maximumPoolSize 时适用。 一旦池达到 minimumIdle 连接数,空闲连接将不会被淘汰。 连接是否因空闲而退出取决于最大 +30 秒的变化,以及 +15 秒的平均变化。 在此超时之前,连接永远不会因空闲而退出。 值为 0 表示永远不会从池中删除空闲连接。 允许的最小值为 10000 毫秒(10 秒)。 默认值:600000(10 分钟)

  • keepaliveTime
    此属性控制 HikariCP 尝试保持连接活动的频率,以防止它被数据库或网络基础设施超时。 该值必须小于 maxLifetime 值。 “keepalive”只会发生在空闲连接上。 当针对给定连接的“keepalive”时间到达时,该连接将从池中删除,“ping”,然后返回到池中。 “ping”是以下之一:调用 JDBC4 isValid() 方法,或执行 connectionTestQuery。 通常,池外持续时间应以个位数毫秒甚至亚毫秒为单位进行测量,因此对性能的影响很小或没有明显影响。 允许的最小值为 30000 毫秒(30 秒),但最好是分钟范围内的值。 默认值:0(禁用)

  • maxLifetime
    此属性控制池中连接的最长生命周期。 一个正在使用的连接永远不会被淘汰,只有当它关闭时才会被删除。 在逐个连接的基础上,应用较小的负衰减以避免池中的质量灭绝。 我们强烈建议设置此值,它应该比任何数据库或基础设施强加的连接时间限制短几秒。 值为 0 表示没有最大生命周期(无限生命周期),当然要服从 idleTimeout 设置。 允许的最小值为 30000 毫秒(30 秒)。 默认值:1800000(30 分钟)

  • connectionTestQuery
    如果您的驱动程序支持 JDBC4,我们强烈建议您不要设置此属性。 这是针对不支持 JDBC4 Connection.isValid() API 的“传统”驱动程序。 这是将在从池中为您提供连接之前执行的查询,以验证与数据库的连接是否仍然有效。 再次尝试在没有此属性的情况下运行池,如果您的驱动程序不兼容 JDBC4HikariCP 将记录错误以通知您。 默认值:无

  • minimumIdle
    此属性控制 HikariCP 尝试在池中维护的最小空闲连接数。 如果空闲连接低于这个值并且池中的总连接小于 maximumPoolSizeHikariCP 将尽最大努力快速有效地添加额外的连接。 但是,为了获得最大性能和对峰值需求的响应,我们建议不要设置此值,而是允许 HikariCP 充当固定大小的连接池。 默认值:与 maximumPoolSize 相同

  • maximumPoolSize
    此属性控制允许池达到的最大大小,包括空闲和使用中的连接。 基本上这个值将决定到数据库后端的最大实际连接数。 一个合理的值最好由您的执行环境决定。 当池达到此大小时,并且没有空闲连接可用时,对 getConnection() 的调用将在超时前阻塞最多 connectionTimeout 毫秒。 请阅读有关池大小的信息。 默认值:10

  • metricRegistry
    此属性仅可通过编程配置或 IoC 容器使用。 此属性允许您指定 Codahale/Dropwizard MetricRegistry 的实例,供池使用以记录各种指标。 有关详细信息,请参阅指标 wiki 页面。 默认值:无

  • healthCheckRegistry
    此属性仅可通过编程配置或 IoC 容器使用。 此属性允许您指定 Codahale/Dropwizard HealthCheckRegistry 的实例,供池使用以报告当前健康信息。 有关详细信息,请参阅健康检查 wiki 页面。 默认值:无

  • poolName
    此属性表示连接池的用户定义名称,主要出现在日志记录和 JMX 管理控制台中以识别池和池配置。 默认值:自动生成

3. 不经常使用

  • initializationFailTimeout
    此属性控制如果池无法成功地使用初始连接播种,池是否将“快速失败”。 任何正数都被视为尝试获取初始连接的毫秒数; 在此期间应用程序线程将被阻塞。 如果在此超时发生之前无法获取连接,则会抛出异常。 此超时在 connectionTimeout 期限之后应用。 如果值为零 (0),HikariCP 将尝试获取并验证连接。 如果获得连接,但验证失败,将抛出异常并且池不会启动。 但是,如果无法获得连接,池将启动,但稍后获得连接的努力可能会失败。 小于零的值将绕过任何初始连接尝试,并且池将在尝试在后台获取连接时立即启动。 因此,以后获得连接的努力可能会失败。 默认值:1

  • isolateInternalQueries
    此属性确定 HikariCP 是否在其自己的事务中隔离内部池查询,例如连接存活测试。 由于这些通常是只读查询,因此很少需要将它们封装在自己的事务中。 此属性仅在禁用 autoCommit 时适用。 默认值:false

  • allowPoolSuspension
    此属性控制是否可以通过 JMX 暂停和恢复池。 这对于某些故障转移自动化场景很有用。 当池暂停时,对 getConnection() 的调用不会超时,并将一直保持到池恢复。 默认值:false

  • readOnly
    该属性控制从池中获取的连接是否默认为只读模式。 请注意,某些数据库不支持只读模式的概念,而其他数据库则在 Connection 设置为只读时提供查询优化。 您是否需要此属性在很大程度上取决于您的应用程序和数据库。 默认值:false

  • registerMbeans
    此属性控制是否注册 JMX 管理 Bean(“MBean”)。 默认值:false

  • catalog
    此属性为支持目录概念的数据库设置默认目录。 如果未指定此属性,则使用 JDBC 驱动程序定义的默认目录。 默认值:驱动程序默认值

  • connectionInitSql
    此属性设置一个 SQL 语句,该语句将在每次创建新连接后执行,然后再将其添加到池中。 如果此 SQL 无效或抛出异常,将被视为连接失败并遵循标准重试逻辑。 默认值:无

  • driverClassName
    HikariCP 将尝试通过仅基于 jdbcUrl 的 DriverManager 解析驱动程序,但对于一些较旧的驱动程序,还必须指定 driverClassName。 除非您收到指示未找到驱动程序的明显错误消息,否则请忽略此属性。 默认值:无

  • transactionIsolation
    此属性控制从池返回的连接的默认事务隔离级别。 如果未指定此属性,则使用 JDBC 驱动程序定义的默认事务隔离级别。 仅当您有对所有查询通用的特定隔离要求时才使用此属性。 此属性的值是来自连接类的常量名称,例如 TRANSACTION_READ_COMMITTEDTRANSACTION_REPEATABLE_READ 等。默认值:驱动程序默认值

  • validationTimeout
    此属性控制将测试连接的活动性的最长时间。 该值必须小于 connectionTimeout。 可接受的最低验证超时为 250 毫秒。 默认值:5000

  • leakDetectionThreshold
    此属性控制在记录指示可能的连接泄漏的消息之前连接可以离开池的时间量。 值为 0 表示禁用泄漏检测。 启用泄漏检测的最低可接受值为 2000(2 秒)。 默认值:0

  • dataSource
    此属性仅可通过编程配置或 IoC 容器使用。 这个属性允许你直接设置 DataSource 的实例被池包装,而不是让 HikariCP 通过反射来构造它。 这在某些依赖注入框架中很有用。 指定此属性时,将忽略 dataSourceClassName 属性和所有特定于数据源的属性。 默认值:无

  • schema
    此属性为支持模式概念的数据库设置默认模式。 如果未指定此属性,则使用 JDBC 驱动程序定义的默认模式。 默认值:驱动程序默认值

  • threadFactory
    此属性仅可通过编程配置或 IoC 容器使用。 此属性允许您设置 java.util.concurrent.ThreadFactory 的实例,该实例将用于创建池使用的所有线程。 在某些受限的执行环境中需要它,在这些环境中线程只能通过应用程序容器提供的 ThreadFactory 创建。 默认值:无

  • scheduledExecutor 此属性仅可通过编程配置或 IoC 容器使用。 此属性允许您设置将用于各种内部计划任务的 java.util.concurrent.ScheduledExecutorService的实例。 如果为HikariCP 提供 ScheduledThreadPoolExecutor实例,建议使用setRemoveOnCancelPolicy(true)`。 默认值:无

2、Springboot中参数

  • spring.datasource.hikari.data-source-class-name:驱动类
  • spring.datasource.hikari.jdbc-url:url地址
  • spring.datasource.hikari.username:用户名
  • spring.datasource.hikari.password:密码
  • spring.datasource.hikari.pool-name:连接池的用户定义名称
  • spring.datasource.hikari.auto-commit:获取连接最大时长(用于从池获取毫秒数)
  • spring.datasource.hikari.minimum-idle:最小空闲连接数
  • spring.datasource.hikari.maximum-pool-size:最大连接数
  • spring.datasource.hikari.connection-timeout:获取连接最大时长(用于从池获取毫秒数)
  • spring.datasource.hikari.max-lifetime:池中连接的最大生存周期(从创建开始计算)(30000ms=30s)
  • spring.datasource.hikari.keepalive-time:连接活跃度检查时间
  • spring.datasource.hikari.connection-test-query:连接检查语句
  • spring.datasource.hikari.idle-timeout:连接在池中处于空闲状态的最长时间(空闲开始计算)
  • spring.datasource.hikari.allow-pool-suspension:此属性控制是否可以通过JMX挂起和恢复池
  • spring.datasource.hikari.catalog:此属性为支持目录概念的数据库设置默认目录
  • spring.datasource.hikari.connection-init-sql:此属性设置一个SQL语句,该语句将在每次创建新连接后执行,然后再将其添加到池中
  • spring.datasource.hikari.data-source-j-n-d-i
  • spring.datasource.hikari.data-source-properties
  • spring.datasource.hikari.driver-class-name:HikariCP将尝试通过仅基于的DriverManager解析驱动程序,但对于一些较旧的驱动程序,还必须指定
  • spring.datasource.hikari.exception-override-class-name
  • spring.datasource.hikari.health-check-properties
  • spring.datasource.hikari.initialization-fail-timeout:此属性控制如果无法成功为池设定初始连接的种子,则池是否会“快速故障”
  • spring.datasource.hikari.isolate-internal-queries:此属性确定HikariCP是否隔离内部池查询
  • spring.datasource.hikari.leak-detection-threshold:此属性控制在记录指示可能存在连接泄漏的消息之前,连接可以离开池的时间
  • spring.datasource.hikari.login-timeout
  • spring.datasource.hikari.metrics-tracker-factory
  • spring.datasource.hikari.read-only:此属性控制默认情况下从池中获取的连接是否处于只读模式。
  • spring.datasource.hikari.register-mbeans:此属性控制是否注册了JMX管理Bean
  • spring.datasource.hikari.scheduled-executor
  • spring.datasource.hikari.schema:此属性为支持模式概念的数据库设置默认模式
  • spring.datasource.hikari.transaction-isolation:此属性控制从池返回的连接的默认事务隔离级别
  • spring.datasource.hikari.validation-timeout:此属性控制测试连接是否有效的最长时间

三、springboot中使用

springboot中默认内置的默认数据库连接池为HikariPool。所以直接使用进行连接即可。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

日志

2023-05-23 23:01:34.734 DEBUG 42836 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet       1131 : Completed 200 OK
2023-05-23 23:01:34.844 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@4774440d
2023-05-23 23:01:34.974 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@638743e8
2023-05-23 23:01:35.070 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@122910ce
2023-05-23 23:01:35.173 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7fb4c201
2023-05-23 23:01:35.270 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@71c0f332
2023-05-23 23:01:35.364 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@34a64c99
2023-05-23 23:01:35.462 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@609524be
2023-05-23 23:01:35.568 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       729 : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7f6f167e
2023-05-23 23:01:35.569 DEBUG 42836 --- [onnection adder] com.zaxxer.hikari.pool.HikariPool       421 : HikariPool-1 - After adding stats (total=10, active=0, idle=10, waiting=0)

四、自定义数据源

1、各模块

需要使用Jdbc自己实现查询,并且查询过程中,使用数据库连接池进行数据源管理。

获取数据源

public HikariDataSource getDataSource() {
    
    
    HikariDataSource config = new HikariDataSource();
    config.setDriverClassName("");
    config.setJdbcUrl("");
    config.setUsername("");
    config.setPassword("");
    config.setPoolName("");
    config.setAutoCommit(true);
    config.setMinimumIdle(1);
    config.setMaximumPoolSize(10);
    config.setConnectionTimeout(111);
    config.setMaxLifetime(1);
    config.setKeepaliveTime(111);
    config.setConnectionTestQuery("SELECT 1");
    config.setIdleTimeout(1);
    config.setAllowPoolSuspension(false);
    return config;
}

从数据源获取链接

/**
 * 获取数据库连接
 *
 * @param driverClassName 驱动类(com.mysql.cj.jdbc.Driver)
 * @param jdbcUrl         jdbcUrl(jdbc:mysql://12.13.39.17:3306)
 * @param username        用户名
 * @param password        密码
 * @return
 */
public HikariDataSource test(String driverClassName, String jdbcUrl, String username, String password) {
    
    
    if (HIKARI_DATA_SOURCE == null) {
    
    
        synchronized (DataSourcePool.class) {
    
    
            if (HIKARI_DATA_SOURCE == null) {
    
    
                HIKARI_DATA_SOURCE = getDataSource(driverClassName, jdbcUrl, username, password);
            }
        }
    }
    return HIKARI_DATA_SOURCE;
}

使用:

String driverClassName = "com.mysql.cj.jdbc.Driver";
String jdbcUrl = "jdbc:mysql://12.13.39.17:3306/datasource";
String username = "root";
String password = "root";
String sql = "select * from datasource";
//  获取链接
HikariDataSource dataSource = dataSourcePool.getDataSource(driverClassName, jdbcUrl, username, password);
//  执行SQL
Connection connection = null;
connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement(sql);
ps.execute();
//	关闭连接
connection.close();

2、完整代码

DataSourcePool

package com.lydms.demohikari.client;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.stereotype.Component;

@Component
public class DataSourcePool {
    
    

    private static volatile HikariDataSource HIKARI_DATA_SOURCE = null;

    /**
     * 获取数据库连接
     *
     * @param driverClassName 驱动类(com.mysql.cj.jdbc.Driver)
     * @param jdbcUrl         jdbcUrl(jdbc:mysql://12.13.39.17:3306)
     * @param username        用户名
     * @param password        密码
     * @return
     */
    public HikariDataSource test(String driverClassName, String jdbcUrl, String username, String password) {
    
    
        if (HIKARI_DATA_SOURCE == null) {
    
    
            synchronized (DataSourcePool.class) {
    
    
                if (HIKARI_DATA_SOURCE == null) {
    
    
                    HIKARI_DATA_SOURCE = getDataSource(driverClassName, jdbcUrl, username, password);
                }
            }
        }
        return HIKARI_DATA_SOURCE;
    }

    public HikariDataSource getDataSource(String driverClassName, String jdbcUrl, String username, String password) {
    
    
        HikariDataSource config = new HikariDataSource();
        config.setDriverClassName(driverClassName);
        config.setJdbcUrl(jdbcUrl);
        config.setUsername(username);
        config.setPassword(password);

        config.setPoolName("");
        config.setAutoCommit(true);
        config.setMinimumIdle(1);
        config.setMaximumPoolSize(10);
        config.setConnectionTimeout(111);
        config.setMaxLifetime(1);
        config.setKeepaliveTime(111);
        config.setConnectionTestQuery("SELECT 1");
        config.setIdleTimeout(1);
        config.setAllowPoolSuspension(false);
        return config;
    }
}

DddServiceImpl

import com.lydms.demohikari.client.DataSourcePool;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

@Service
@Slf4j
public class DddServiceImpl {
    
    

    @Autowired
    private DataSourcePool dataSourcePool;

    public void get() {
    
    
        String driverClassName = "com.mysql.cj.jdbc.Driver";
        String jdbcUrl = "jdbc:mysql://12.13.39.17:3306/datasource";
        String username = "root";
        String password = "root";
        String sql = "select * from datasource";
        //  获取链接
        HikariDataSource dataSource = dataSourcePool.getDataSource(driverClassName, jdbcUrl, username, password);
        //  执行SQL
        Connection connection = null;
        try {
    
    
            connection = dataSource.getConnection();
            PreparedStatement ps = connection.prepareStatement(sql);
            ps.execute();
        } catch (SQLException e) {
    
    
            throw new RuntimeException(e);
        } finally {
    
    
          //	关闭连接
            try {
    
    
                if (connection != null) {
    
    
                    connection.close();
                }
            } catch (SQLException e) {
    
    
                log.error("关闭连接失败");
            }
        }
    }
}

3、多数据源

private static final HashMap<String, HikariDataSource> HIKARI_DATA_SOURCE_HASH_MAP = new HashMap<>();
/**
 * 获取数据库连接
 *
 * @param driverClassName 驱动类(com.mysql.cj.jdbc.Driver)
 * @param jdbcUrl         jdbcUrl(jdbc:mysql://12.13.39.17:3306)
 * @param username        用户名
 * @param password        密码
 * @return
 */
public HikariDataSource test(String driverClassName, String jdbcUrl, String username, String password) {
    
    
    String key = driverClassName + jdbcUrl + username + password;
    HikariDataSource hikariDataSource = HIKARI_DATA_SOURCE_HASH_MAP.get(key);
    if (hikariDataSource == null) {
    
    
        synchronized (DataSourcePool.class) {
    
    
            hikariDataSource = HIKARI_DATA_SOURCE_HASH_MAP.get(key);
            if (hikariDataSource == null) {
    
    
                hikariDataSource = getDataSource(driverClassName, jdbcUrl, username, password);
            }
        }
    }
    return hikariDataSource;
}

五、多数据源dynamic中使用

1、简介

文档地址:

https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611

多数据源既动态数据源,项目开发逐渐扩大,单个数据源、单一数据源已经无法满足需求项目的支撑需求。由此延伸了多数据源的扩展。

dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。

其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x

  • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  • 支持数据库敏感配置信息 加密 ENC()。
  • 支持每个数据库独立初始化表结构schema和数据库database。
  • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
  • 支持 自定义注解 ,需继承DS(3.2.0+)。
  • 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
  • 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
  • 提供 自定义数据源来源 方案(如全从数据库加载)。
  • 提供项目启动后 动态增加移除数据源 方案。
  • 提供Mybatis环境下的 纯读写分离 方案。
  • 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
  • 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
  • 提供 **基于seata的分布式事务方案。
  • 提供 本地多数据源事务方案。

2、引入依赖

1、引入dynamic-datasource-spring-boot-starter。

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>

2、项目引入``HikariCP`依赖

pringBoot2.x.x默认引入了HikariCP,除非对版本有要求无需再次引入。

SpringBoot 1.5.x需手动引入,对应的版本请根据自己环境和HikariCP官方文档自行选择。

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>${
    
    version}</version>
</dependency>

3、参数配置

  1. 如果参数都未配置,则保持原组件默认值。
  2. 如果配置了全局参数,则每一个数据源都会继承对应参数。
  3. 每一个数据源可以单独设置参数覆盖全局参数。

特别注意,hikaricp原生设置某些字段名和本组件不一致,本组件是根据参数反射设置,而原生hikaricp字段名称和set名称不一致。 所以大家理解,以本组件字段名称为准。

spring:
  datasource:
    dynamic:
      hikari:  # 全局hikariCP参数,所有值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
        catalog:
        connection-timeout:
        validation-timeout:
        idle-timeout:
        leak-detection-threshold:
        max-lifetime:
        max-pool-size:
        min-idle:
        initialization-fail-timeout:
        connection-init-sql:
        connection-test-query:
        dataSource-class-name:
        dataSource-jndi-name:
        schema:
        transaction-isolation-name:
        is-auto-commit:
        is-read-only:
        is-isolate-internal-queries:
        is-register-mbeans:
        is-allow-pool-suspension:
        data-source-properties: 
        health-check-properties:
      datasource:
        master:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic?characterEncoding=utf8&useSSL=false
          hikari: # 以下参数针对每个库可以重新设置hikari参数
            max-pool-size:
            idle-timeout:
#           ......

六、XMind整理

https://download.csdn.net/download/weixin_44624117/87816645

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44624117/article/details/130856966