HikariCP源码修改,使其连接池支持Kerberos认证

news2024/11/28 18:38:26

HikariCP-4.0.3

修改HikariCP源码,使其连接池支持Kerberos认证

修改后的Hikari源码地址:https://github.com/Raray-chuan/HikariCP-4.0.3

Springboot使用hikari连接池并进行Kerberos认证访问Impala的demo地址:https://github.com/Raray-chuan/springboot-kerberos-hikari-impala

1. Java连接impala的Kerberos认证

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;

import java.io.IOException;
import java.security.PrivilegedAction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
 * @Author Xichuan
 * @Date 2022/10/28 17:53
 * @Description
 */
public class TestKerberosImpala {
        public static final String KRB5_CONF = "D:\\development\\license_dll\\krb5.conf";
        public static final String PRINCIPAL = "xichuan/admin@XICHUAN.COM";
        public static final String KEYTAB = "D:\\development\\license_dll\\xichuan.keytab";
        public static String connectionUrl = "jdbc:impala://node01:21050/;AuthMech=1;KrbRealm=XICHUAN.COM;KrbHostFQDN=node01;KrbServiceName=impala";
        public static String jdbcDriverName = "com.cloudera.impala.jdbc41.Driver";

        public static void main(String[] args) throws Exception {
            UserGroupInformation loginUser = kerberosAuth(KRB5_CONF,KEYTAB,PRINCIPAL);

            int result = loginUser.doAs((PrivilegedAction<Integer>) () -> {
                int result1 = 0;
                try {
                    Class.forName(jdbcDriverName);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                try (Connection con = DriverManager.getConnection(connectionUrl)) {
                    Statement stmt = con.createStatement();
                    ResultSet rs = stmt.executeQuery("SELECT count(1) FROM test_dws.dws_test_id");
                    while (rs.next()) {
                        result1 = rs.getInt(1);
                    }
                    stmt.close();
                    con.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return result1;
            });
            System.out.println("count: "+ result);
        }

    /**
     * kerberos authentication
     * @param krb5ConfPath
     * @param keyTabPath
     * @param principle
     * @return
     * @throws IOException
     */
        public static UserGroupInformation kerberosAuth(String krb5ConfPath, String keyTabPath, String principle) throws IOException {
            System.setProperty("java.security.krb5.conf", krb5ConfPath);
            Configuration conf = new Configuration();
            conf.set("hadoop.security.authentication", "Kerberos");
            UserGroupInformation.setConfiguration(conf);
            UserGroupInformation loginInfo = UserGroupInformation.loginUserFromKeytabAndReturnUGI(principle, keyTabPath);


            if (loginInfo.hasKerberosCredentials()) {
                System.out.println("kerberos authentication success!");
                System.out.println("login user: "+loginInfo.getUserName());
            } else {
                System.out.println("kerberos authentication fail!");
            }

            return loginInfo;
        }
}

2. 解读源码,了解Hikari连接池如何保持Connection个数在一定数目上

1.在我们初始化Hikari Pool参数后,第一次调用com.zaxxer.hikari.HikariDataSource#getConnection()的时候,会进行初始化HikariPoolHikariPool正式管理Connection的类

2.进入com.zaxxer.hikari.pool.HikariPool#HikariPool类中,查看HikariPool如何初始化连接池的

HikariPool初始化的前面参数先不管,不是研究重点,看红框中,HikariPool会初始化一个HouseKeeper的线程,HouseKeeper的作用的是保持连接池的idle数据在一定的数目

3.进入com.zaxxer.hikari.pool.HikariPool.HouseKeeper,看它如何是保持Connection的数据在一定的数据

我们可以看到,HouseKeeper是一个内部类,继续往下看代码,有一个fillPool()方法,看注解,这个方法可以保持连接数在一定数据上

4.进入com.zaxxer.hikari.pool.HikariPool#fillPool方法

从上图我们可以看出,此方法会判断pool是否需要新添加Connection,如果需要,则在pool中添加Connection。添加Connection方式是提交一个线程,我们直接看PoolEntryCreator如何添加Connection即可。下面只会跟踪Hikari最终创建Connection的代码地方,不会解释每个方法以及类的作用

5.跟踪代码,找到Hikari最终创建Connection的代码地方

进入com.zaxxer.hikari.pool.HikariPool.PoolEntryCreator类,

可以看出该线程会创建一个PoolEntry类,PoolEntry类就是用来保存Connection的.

继续进入com.zaxxer.hikari.pool.HikariPool#createPoolEntry方法,看如何创建PoolEntry类的

可以看出,创建PoolEntry是通过newPoolEntry()方法进行创建的

继续进入com.zaxxer.hikari.pool.PoolBase#newPoolEntry方法,看如何创建PoolEntry

可以看出newPoolEntry()方法创建PoolEntry对象,是通过PoolEntry构造方法创建的,进入此构造方法,第一个参数就是Connection,那我们就需要进入newConnection()方法看此Connection是如何创建的

进入com.zaxxer.hikari.pool.PoolBase#newConnection方法

我们看出Connection的创建是通过dataSource.getConnection()来创建的,那这个dataSource的实现类是哪个?打断点可以看出是DriverDataSource

进入com.zaxxer.hikari.util.DriverDataSource#getConnection()方法,查看Connection是如何创建的

可以看出创建connection是通过调用impala、mysql、h2等驱动包的接口创建的

6.总结

通过上面的源码跟踪,可以发现,创建Connection是在HikariPool类中的HouseKeeper线程中进行的。所以我们在com.zaxxer.hikari.HikariDataSource#getConnection()中,在HikariPool初始化的时候进行Kerberos认证是行不通的,因为Kerberos默认24小时就失效了; 但Kerberos失效后,HouseKeeper创建Connection的时候并没有再次认证。

所以我们思路可以是,修改hikari的源码,在com.zaxxer.hikari.util.DriverDataSource#getConnection()方法调用 driver.connect(jdbcUrl, driverProperties)之前认证即可。并且hikari连接池的max-lifetime参数要小于Kerberos的过期时长

3. 修改Hikari源码,使其支持Kerberos认证

3.1 修改HikariConfig类,添加Kerberos的四个参数

四个参数分别是:

authenticationType:安全验证的类型,如果值是"kerberos",则进行Kerberos认证,如果为null,则不进行认证
krb5FilePath:krb5.conf文件的路径
principal:principal的名称
keytabPath:对应principal的keytab的路径
   //kerberos params
   private volatile String authenticationType;
   private volatile String krb5FilePath;
   private volatile String keytabPath;
   private volatile String principal;

   public String getAuthenticationType() {
      return authenticationType;
   }

   public void setAuthenticationType(String authenticationType) {
      this.authenticationType = authenticationType;
   }

   public String getKrb5FilePath() {
      return krb5FilePath;
   }

   public void setKrb5FilePath(String krb5FilePath) {
      this.krb5FilePath = krb5FilePath;
   }

   public String getKeytabPath() {
      return keytabPath;
   }

   public void setKeytabPath(String keytabPath) {
      this.keytabPath = keytabPath;
   }

   public String getPrincipal() {
      return principal;
   }

   public void setPrincipal(String principal) {
      this.principal = principal;
   }

3.2 在PoolBase类中初始化DriverDataSource的时候,添加Kerberos参数

  private void initializeDataSource()
   {
      final String jdbcUrl = config.getJdbcUrl();
      final String username = config.getUsername();
      final String password = config.getPassword();
      final String dsClassName = config.getDataSourceClassName();
      final String driverClassName = config.getDriverClassName();
      final String dataSourceJNDI = config.getDataSourceJNDI();
      final Properties dataSourceProperties = config.getDataSourceProperties();
      //add kerberos properties
      dataSourceProperties.setProperty("authenticationType",config.getAuthenticationType());
      dataSourceProperties.setProperty("keytabPath",config.getKeytabPath());
      dataSourceProperties.setProperty("krb5FilePath",config.getKrb5FilePath());
      dataSourceProperties.setProperty("principal",config.getPrincipal());

      DataSource ds = config.getDataSource();
      if (dsClassName != null && ds == null) {
         ds = createInstance(dsClassName, DataSource.class);
         PropertyElf.setTargetFromProperties(ds, dataSourceProperties);
      }
      else if (jdbcUrl != null && ds == null) {
         ds = new DriverDataSource(jdbcUrl, driverClassName, dataSourceProperties, username, password);
      }
      else if (dataSourceJNDI != null && ds == null) {
         try {
            InitialContext ic = new InitialContext();
            ds = (DataSource) ic.lookup(dataSourceJNDI);
         } catch (NamingException e) {
            throw new PoolInitializationException(e);
         }
      }

      if (ds != null) {
         setLoginTimeout(ds);
         createNetworkTimeoutExecutor(ds, dsClassName, jdbcUrl);
      }

      this.dataSource = ds;
   }

3.3 DriverDataSource类在getConnection()的时候进Kerberos认证

public final class DriverDataSource implements DataSource{
  ...
  ...
  //kerberos params
   private String authenticationType = "";
   private String krb5FilePath;
   private String keytabPath;
   private String principal;

   public DriverDataSource(String jdbcUrl, String driverClassName, Properties properties, String username, String password) {
      this.jdbcUrl = jdbcUrl;
      this.driverProperties = new Properties();

      //init kerberos properties
      if (properties.getProperty("authenticationType") != null && properties.getProperty("authenticationType").equals("kerberos")){
         authenticationType = properties.getProperty("authenticationType");
         krb5FilePath = properties.getProperty("krb5FilePath");
         keytabPath = properties.getProperty("keytabPath");
         principal = properties.getProperty("principal");
      }
     ...
   }

  ...
  ...
   @Override
   public Connection getConnection() throws SQLException {
      //if authenticationType=kerberos,it must be kerberos authentication first!
      if (authenticationType != null && authenticationType.equals("kerberos")){
         UserGroupInformation ugi = authentication();
         try {
            return ugi.doAs(new XichuanGenerateConnectionAction(jdbcUrl, driverProperties));
         } catch (IOException | InterruptedException e) {
            e.printStackTrace();
         }
         return null;
      }else{
         return driver.connect(jdbcUrl, driverProperties);
      }
   }

   @Override
   public Connection getConnection(final String username, final String password) throws SQLException
   {
      final Properties cloned = (Properties) driverProperties.clone();
      if (username != null) {
         cloned.put(USER, username);
         if (cloned.containsKey("username")) {
            cloned.put("username", username);
         }
      }
      if (password != null) {
         cloned.put(PASSWORD, password);
      }

      //if authenticationType=kerberos,it must be kerberos authentication first!
      if (authenticationType != null && authenticationType.equals("kerberos")){
         UserGroupInformation ugi = authentication();
         try {
            return ugi.doAs(new XichuanGenerateConnectionAction(jdbcUrl, cloned));
         } catch (IOException | InterruptedException e) {
            e.printStackTrace();
         }
         return null;
      }else{
         return driver.connect(jdbcUrl, cloned);
      }
   }

     /**
    * generate connection action
    */
   public class XichuanGenerateConnectionAction implements PrivilegedExceptionAction<Connection> {
      private String jdbcUrl;
      private Properties driverProperties;
      public XichuanGenerateConnectionAction(String jdbcUrl, Properties driverProperties){
         this.jdbcUrl = jdbcUrl;
         this.driverProperties = driverProperties;
      }

      @Override
      public Connection run() throws Exception {
         return driver.connect(jdbcUrl, driverProperties);
      }
   }

   /**
    * kerberos authentication
    */
   private UserGroupInformation authentication() {

      if(authenticationType != null && "kerberos".equalsIgnoreCase(authenticationType.trim())) {
         LOGGER.info("kerberos authentication is begin");
      } else {
         LOGGER.info("kerberos authentication is not open");
         return null;
      }


      System.setProperty("java.security.krb5.conf", krb5FilePath);
      org.apache.hadoop.conf.Configuration conf = new org.apache.hadoop.conf.Configuration();
      conf.set("hadoop.security.authentication", authenticationType);
      try {
         UserGroupInformation.setConfiguration(conf);
         UserGroupInformation userGroupInformation = UserGroupInformation.loginUserFromKeytabAndReturnUGI(principal, keytabPath);
         LOGGER.info("kerberos authentication success!, krb5FilePath:{}, principal:{}, keytab:{}", krb5FilePath, principal, keytabPath);
         LOGGER.info("login user::{}", userGroupInformation.getUserName());
         return userGroupInformation;
      } catch (IOException e1) {
         LOGGER.info("kerberos authentication fail!");
         LOGGER.error(e1.getMessage() + ", detail:{}", e1);
      }
      return null;
   }

  ...
}

4. 对修改后的源码打包

1.maven一定要用HikariCP的对应版本的maven版本

HikariCP-4.0.3要求的maven版本是3.3.9,必须使用apache-maven-3.3.9才能打包

2.添加toolchains.xml文档

toolchains.xml文件的内容:

<?xml version="1.0" encoding="UTF-8"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 http://maven.apache.org/xsd/toolchains-1.1.0.xsd">



 <toolchain>
	<type>jdk</type>
	<provides>
	  <version>1.8</version>
	  <vendor>sun</vendor>
	</provides>
	<configuration>
      <!--jdkHome是jdk的安装home路径-->
	  <jdkHome>D:\development tool\Java\jdk1.8.0_211</jdkHome>
	</configuration>
 </toolchain>

</toolchains>

将此文件放在apache-maven-3.3.9\conf目录中

如果打包的时候还是报缺失toolchains.xml文件,可以将此文件放到本地仓库的路径中,如下图:

3.进行package,然后在本地仓库中将HikariCP-4.0.3.jar替换即可

5.在springboot中使用hikari连接池并进行Kerberos认证

1. 在application.yml添加四个参数

# kerberos
# authenticationType:安全验证的类型,如果值是"kerberos",则进行Kerberos认证,如果为null,则不进行认证
authentication.type=kerberos
# krb5FilePath:krb5.conf文件的路径
authentication.kerberos.krb5FilePath=D:\\development\\license_dll\\krb5.conf
# principal:principal的名称
authentication.kerberos.principal=xichuan/admin@XICHUAN.COM
# keytabPath:对应principal的keytab的路径
authentication.kerberos.keytabPath=D:\\development\\license_dll\\xichuan.keytab


# datasource and pool
datasource.xichuan.url=jdbc:impala://node01:21050/;AuthMech=1;KrbRealm=XICHUAN.COM;KrbHostFQDN=node01;KrbServiceName=impala
datasource.xichuan.driver-class-name=com.cloudera.impala.jdbc41.Driver
datasource.xichuan.username=
datasource.xichuan.password=
datasource.xichuan.pool-name=xichuan-pool
datasource.xichuan.read-only=false
datasource.xichuan.auto-commit=true
datasource.xichuan.maximum-pool-size=3
#此值务必要小于Kerberos的过期时长(默认24小时)
datasource.xichuan.max-lifetime=35000
datasource.xichuan.idle-timeout=10000
datasource.xichuan.validation-timeout=5000

2.获取DataSourceProperties并封装成类

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @Author Xichuan
 * @Date 2022/11/1 17:44
 * @Description
 */
@Component
@ConfigurationProperties(prefix = "datasource.xichuan")
public class DataSourceProperties {

    private String url;
    private String driverClassName;
    private String username;
    private String password;
    private String poolName;
    private boolean readOnly;
    private boolean autoCommit;
    private int maximumPoolSize;
    private long maxLifetime;
    private long idleTimeout;
    private long validationTimeout;

    public String getPoolName() {
        return poolName;
    }

    public void setPoolName(String poolName) {
        this.poolName = poolName;
    }

    public boolean isReadOnly() {
        return readOnly;
    }

    public void setReadOnly(boolean readOnly) {
        this.readOnly = readOnly;
    }

    public boolean isAutoCommit() {
        return autoCommit;
    }

    public void setAutoCommit(boolean autoCommit) {
        this.autoCommit = autoCommit;
    }

    public int getMaximumPoolSize() {
        return maximumPoolSize;
    }

    public void setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }

    public long getMaxLifetime() {
        return maxLifetime;
    }

    public void setMaxLifetime(long maxLifetime) {
        this.maxLifetime = maxLifetime;
    }

    public long getIdleTimeout() {
        return idleTimeout;
    }

    public void setIdleTimeout(long idleTimeout) {
        this.idleTimeout = idleTimeout;
    }

    public long getValidationTimeout() {
        return validationTimeout;
    }

    public void setValidationTimeout(long validationTimeout) {
        this.validationTimeout = validationTimeout;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

3. 在配置文件中创建dataSource的bean

/**
 * @Author Xichuan
 * @Date 2022/11/1 15:15
 * @Description
 */
@Configuration
public class DataSourceConfig {
    private Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);

    @Value("${authentication.type}")
    private String authenticationType;

    @Value("${authentication.kerberos.krb5FilePath}")
    private String krb5FilePath;

    @Value("${authentication.kerberos.principal}")
    private String principal;

    @Value("${authentication.kerberos.keytabPath}")
    private String keytabPath;

    /**
     * inint datasource
     * @return
     */
    @Bean
    public DataSource dataSource(DataSourceProperties dataSourceProperties) throws SQLException {
        HikariConfig config = new HikariConfig();
        //kerberos config
        config.setAuthenticationType(authenticationType);
        config.setKrb5FilePath(krb5FilePath);
        config.setPrincipal(principal);
        config.setKeytabPath(keytabPath);

        //jdbc and pool config
        config.setJdbcUrl(dataSourceProperties.getUrl());
        config.setDriverClassName(dataSourceProperties.getDriverClassName());
        config.setUsername(dataSourceProperties.getUsername());
        config.setPassword(dataSourceProperties.getPassword());
        config.setPoolName(dataSourceProperties.getPoolName());
        config.setReadOnly(dataSourceProperties.isReadOnly());
        config.setAutoCommit(dataSourceProperties.isAutoCommit());
        config.setMaximumPoolSize(dataSourceProperties.getMaximumPoolSize());
        //maxLifetime 池中连接最长生命周期
        config.setMaxLifetime(dataSourceProperties.getMaxLifetime());
        //等待来自池的连接的最大毫秒数 30000
        config.setIdleTimeout(dataSourceProperties.getIdleTimeout());
        //连接将被测试活动的最大时间量
        config.setValidationTimeout(dataSourceProperties.getValidationTimeout());


        HikariDataSource dataSource = new HikariDataSource(config);
        logger.info("init new dataSource: {}", dataSource);
        return dataSource;
    }
}

4.使用

此时使用与其他数据库连接池的使用方式没什么区别了

详细的Springboot使用hikari连接池并进行Kerberos认证访问Impala的demo地址:https://github.com/Raray-chuan/springboot-kerberos-hikari-impala

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

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

相关文章

Lambda表达式第三版,从3个方面分析。(①抽象方法无参数无返回值,②抽象方法带参数无返回值,③抽象方法带参数有返回值)

1、函数式编程思想概述 在数学中&#xff0c;函数就是有输入量、输出量的一套计算方案&#xff0c;也就是”拿数据做操作“面向对象思想强调”必须通过对象的形式来做事情“函数式思想则尽量忽略面向对象的复杂语法&#xff1a;”强调做什么&#xff0c;而不是以什么形式去做&a…

【javaweb】学习日记Day8 - Mybatis入门 Mysql 多表查询 事务 索引

之前学习过的SQL语句笔记总结戳这里→【数据库原理与应用 - 第六章】T-SQL 在SQL Server的使用_Roye_ack的博客-CSDN博客 【数据库原理与应用 - 第八章】数据库的事务管理与并发控制_一级封锁协议_Roye_ack的博客-CSDN博客 目录 一、多表查询 1、概述 &#xff08;1&#…

性能测试(测试系列10)

目录 前言&#xff1a; 1.什么是性能测试 1.1生活中遇到的软件问题 1.2性能测试的定义 1.3性能测试和功能测试有什么区别 1.4性能的好坏的区分 1.5影响一个软件性能的因素 2.为什么要进行性能测试 3.性能测试常见的术语以及衡量指标 3.1并发 3.2用户数 3.3响应时间 …

Jmeter如何设置中文版

第一步&#xff1a;找到 apache-jmeter-5.4.3\bin目录下的 jmeter.properties 第二步:打开 三&#xff0c;ctrf 输入languageen&#xff0c;注释掉&#xff0c;增加以行修改如下 四&#xff0c;ctrs 保存修改内容&#xff0c;重新打开jmeter就可以了

golang-bufio 缓冲写

1. 缓冲写 在阅读这篇博客之前&#xff0c;请先阅读上一篇&#xff1a;golang-bufio 缓冲读 // buffered output// Writer implements buffering for an io.Writer object. // If an error occurs writing to a Writer, no more data will be // accepted and all subsequent…

5.0: Dubbo服务导出源码解析

#Dubbo# 文章内容 Dubbo服务导出基本原理分析Dubbo服务注册流程源码分析Dubbo服务暴露流程源码分析服务导出的入口方法为ServiceBean.export(),此方法会调用ServiceConfig.export()方法,进行真正的服务导出。 1. 服务导出大概原理 服务导出的入口方法为ServiceBean.export…

stm32之28.ADC

须看原理图&#xff08;引脚、电压值、ADC几号通道&#xff09;配置 。 若对比值0~4096 模拟电压/参考电压4096/x 假设模拟电压2.1V&#xff0c;参考电压3.3v&#xff0c;4096/x3.3/2.1 ->3.3x2.1x4096 ->x2,606.5 也可反推出模拟电压 ADC转换时间 ADC时钟来源于…

leetcode645. 错误的集合(java)

错误的集合 题目描述优化空间代码演示 题目描述 难度 - 简单 LC645 - 错误的集合 集合 s 包含从 1 到 n 的整数。不幸的是&#xff0c;因为数据错误&#xff0c;导致集合里面某一个数字复制了成了集合里面的另外一个数字的值&#xff0c;导致集合 丢失了一个数字 并且 有一个数…

时序预测 | MATLAB实现CNN-GRU卷积门控循环单元时间序列预测(风电功率预测)

时序预测 | MATLAB实现CNN-GRU卷积门控循环单元时间序列预测&#xff08;风电功率预测&#xff09; 目录 时序预测 | MATLAB实现CNN-GRU卷积门控循环单元时间序列预测&#xff08;风电功率预测&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.时序预测 | MA…

Python中的绝对和相对导入

在本文中&#xff0c;我们将看到Python中的绝对和相对导入。 Python中导入的工作 Python中的import类似于C/C中的#include header_file。Python模块可以通过使用import导入文件/函数来访问其他模块的代码。import语句是调用import机制的最常见方式&#xff0c;但它不是唯一的…

常见数学名词的物理意义(更新中)

BetterExplained – Math lessons that click 一、复数 i 的物理意义就是旋转&#xff0c;i 就是逆时针旋转90&#xff0c;i*i 就是逆时针旋转180 加法&#xff1a;实部相加&#xff0c;虚部相加 乘法&#xff1a; 复数zxyi控制了函数的放缩和旋转 ——x 放缩&#xff0c;…

Shell开发实践:服务器的磁盘、CPU、内存的占用监控

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

基于vue-cli创建后台管理系统前端页面——element-ui,axios,跨域配置,布局初步,导航栏

目录 引出安装npm install安装element-ui安装axios 进行配置main.js中引入添加jwt前端跨域配置 进行初始布局HomeView.vueApp.vue 新增页面和引入home页面导航栏总结 引出 1.vue-cli创建前端工程&#xff0c;安装element-ui&#xff0c;axios和配置&#xff1b; 2.前端跨域的配…

SQL查询本年每月的数据

--一、以一行数据的形式&#xff0c;显示本年的12月的数据&#xff0c;本示例以2017年为例&#xff0c;根据统计日期字段判断&#xff0c;计算总和&#xff0c;查询语句如下&#xff1a;selectsum(case when datepart(month,统计日期)1 then 支付金额 else 0 end) as 1月, sum…

UE4 春节鞭炮

先搞个基类&#xff0c;一个鞭炮的 搞个鞭炮类&#xff0c;存多个鞭炮 在构造函数的位置先生成对应的鞭炮数 将鞭炮绑定到绳子上&#xff0c;随绳子摆动而一起摆动 在基类里面写爆炸事件 最后用Timer去调用

一篇文章搞懂Redis缓存

目录 一、什么是缓存缓存的优缺点缓存的优点缓存的缺点 二、Redis缓存三、缓存的更新策略主动更新策略 四、缓存穿透解决方案 五、缓存雪崩解决方案 六、缓存击穿解决方案 一、什么是缓存 我们都知道在计算机中内存的速度比磁盘要快非常多&#xff0c;如果每次都要去磁盘获取数…

《Python魔法大冒险》004第一个魔法程序

在图书馆的一个安静的角落,魔法师和小鱼坐在一张巨大的桌子前。桌子上摆放着那台神秘的笔记本电脑。 魔法师: 小鱼,你已经学会了如何安装魔法解释器和代码编辑器。是时候开始编写你的第一个Python魔法程序了! 小鱼:(兴奋地两眼放光)我准备好了! 魔法师: 不用担心,…

Lesson6---案例:人脸案例

学习目标 了解opencv进行人脸检测的流程了解Haar特征分类器的内容 1 基础 我们使用机器学习的方法完成人脸检测&#xff0c;首先需要大量的正样本图像&#xff08;面部图像&#xff09;和负样本图像&#xff08;不含面部的图像&#xff09;来训练分类器。我们需要从其中提取特…

解决Debian系统通过cifs挂载smb后,中文目录乱码问题

解决Debian系统通过cifs挂载smb后&#xff0c;中文目录乱码问题 //$smb_server/share /mnt/nas_share cifs credentials/root/.smbcredentials,iocharsetutf8 0 0默认通过以上命令挂载smb&#xff0c;但是在查看文件目录时&#xff0c;中文乱码 解决问题方式&#xff1a; de…

BLDC无感方波控制

BLDC无感控制 反电动势过零检测反电动势检测方法比较器模式采样过零信号闭环的建立 BLDC 方波启动技术转子预定位电机的外同步加速电机运行状态的转换 程序部分 反电动势过零检测 它的主要核心就是通过检测定子绕组的反电动势过零点来判断转子当前的位置。 三相六状态 120通电…