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

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 paramsprivate 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 propertiesdataSourceProperties.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 paramsprivate 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 propertiesif (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");}...}......@Overridepublic 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);}}@Overridepublic 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;}@Overridepublic 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*/@Beanpublic DataSource dataSource(DataSourceProperties dataSourceProperties) throws SQLException {HikariConfig config = new HikariConfig();//kerberos configconfig.setAuthenticationType(authenticationType);config.setKrb5FilePath(krb5FilePath);config.setPrincipal(principal);config.setKeytabPath(keytabPath);//jdbc and pool configconfig.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());//等待来自池的连接的最大毫秒数 30000config.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.rhkb.cn/news/118527.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023 AZ900备考

文章目录 如何学习最近准备考AZ900考试&#xff0c;找了一圈文档&#xff0c;结果发现看那么多文档&#xff0c;不如直接看官方的教程https://learn.microsoft.com/zh-cn/certifications/exams/az-900/ &#xff0c;简单直接&#xff0c;突然想到纳瓦尔宝典中提到多花时间进行思…

博客系统自动化测试项目实战(测试系列9)

目录 前言&#xff1a; 1.博客前端页面测试用例图 2.测试用例的代码实现 2.1登录页面的测试 2.2博客列表页面的测试 2.3写博客测试 2.4博客详情页面的测试 2.5已发布博客的标题和时间的测试 2.6注销用户的测试 结束语&#xff1a; 前言&#xff1a; 之前小编给大家讲…

Java学习笔记之----I/O(输入/输出)一

在变量、数组和对象中存储的数据是暂时存在的&#xff0c;程序结束后它们就会丢失。想要永久地存储程序创建的数据&#xff0c;就需要将其保存在磁盘文件中(就是保存在电脑的C盘或D盘中&#xff09;&#xff0c;而只有数据存储起来才可以在其他程序中使用它们。Java的I/O技术可…

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

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

Flutter 安装教程 + 运行教程

1.下载依赖 https://flutter.cn/docs/get-started/install/windows 解压完后根据自己的位置放置&#xff0c;如&#xff08;D:\flutter&#xff09; 注意 请勿将 Flutter 有特殊字符或空格的路径下。 请勿将 Flutter 安装在需要高权限的文件夹内&#xff0c;例如 C:\Program …

Elasticsearch终端命令行用法大全

API作用使用场景curl localhost:9200/_cluster/health?pretty查看ES健康状态curl localhost:9200/_cluster/settings?pretty查看ES集群的设置其中persistent为永久设置&#xff0c;重启仍然有效&#xff1b;trainsient为临时设置&#xff0c;重启失效curl localhost:9200/_ca…

锐捷盒式交换机S5760C版本U盘升级

1.确认设备当前版本信息 2.将升级文件包放置U盘文件夹中&#xff0c; U盘名称123 &#xff0c; 文件夹名称A 3.查看到升级包后&#xff0c;进行U盘升级 #upgrade usb0:/A/S5760X_RGOS12.5(4)B0702P4_install.bin 4.升级成功后 reload交换机 5.等交换机重启完毕&#xff0c;再次…

【Dart】学习使用(二):基本类型

前言 基本类型是语言的基础。 Dart 语言支持以下基础类型&#xff1a;Numbers(int、double)&#xff0c; 整形Strings(String), 字符串Booleans(bool) , 布尔型Records((value1,value2)) 记录Lists(List ) 数组Sets(Set) 集合Maps(Map) 映射Runes(Runes,通常由 characters AP…

支付宝使用OceanBase的历史库实践分享

为解决因业务增长引发的数据库存储空间问题&#xff0c;支付宝基于 OceanBase 数据库启动了历史库项目&#xff0c;通过历史数据归档、过期数据清理、异常数据回滚&#xff0c;实现了总成本降低 80%。 历史数据归档&#xff1a;将在线库&#xff08;SSD 磁盘&#xff09;数据归…

基于Citespace、vosviewer、R语言的文献计量学可视化分析技术及全流程文献可视化SCI论文高效写作

文献计量学是指用数学和统计学的方法&#xff0c;定量地分析一切知识载体的交叉科学。它是集数学、统计学、文献学为一体&#xff0c;注重量化的综合性知识体系。特别是&#xff0c;信息可视化技术手段和方法的运用&#xff0c;可直观的展示主题的研究发展历程、研究现状、研究…

ELK原理和介绍

为什么用到ELK&#xff1a; 一般我们需要进行日志分析场景&#xff1a;直接在日志文件中 grep、awk 就可以获得自己想要的信息。但在规模较大的场景中&#xff0c;此方法效率低下&#xff0c;面临问题包括日志量太大如何归档、文本搜索太慢怎么办、如何多维度查询。需要集中化…

Ansible学习笔记11

Command和Shell模块&#xff1a; 两个模块都是用于执行Linux命令的&#xff0c;这个对于命令熟悉的工程师来说&#xff0c;用起来非常high。 Shell模块跟Command模块差不多&#xff08;Command模块不能执行一类$HOME、> 、<、| 等符号&#xff0c;但是Shell是可以的。&…

大学物理 之 安培环路定理

文章目录 前言什么是安培环路定理安培环路定理有什么作用 深入了解深入学习 前言 什么是安培环路定理 安培环路定理的物理意义在于描述了电流和磁场之间的相互作用&#xff0c;以及如何在一个封闭的回路中分析这种相互作用。 简单的来说 , 用环路定理来解决在磁场中B对任意封…

java基础-----第九篇

系列文章目录 文章目录 系列文章目录前言一、GC如何判断对象可以被回收前言 一、GC如何判断对象可以被回收 引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计 数为0时可以回收, 可达性分析法:从 GC Roots 开始向下搜索,搜索所走过的…

TCP Header都有啥?

分析&回答 源端口号&#xff08;Source Port&#xff09; &#xff1a;16位&#xff0c;标识主机上发起传送的应用程序&#xff1b; 目的端口&#xff08;Destonation Port&#xff09; &#xff1a;16位&#xff0c;标识主机上传送要到达的应用程序。 源端&#xff0c;目…

第 3 章 栈和队列 (循环队列)

1. 背景说明 和顺序栈相类似&#xff0c;在队列的顺序存储结构中&#xff0c;除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外&#xff0c; 尚需附设两个指针 front 和 rear 分别指示队列头元素及队列尾元素的位置。约定&#xff1a;初始化建空队列时&#x…

全网都在用的nnUNet V2版本改进了啥,怎么安装?(一)

nnUNet&#xff0c;这个医学领域的分割巨无霸!在论文和比赛中随处可见他的身影。大家对于nnUNet v1版本的教程都赞不绝口&#xff0c;因为它简单易懂、详细全面&#xff0c;让很多朋友都轻松掌握了使用方法。 最近&#xff0c;我也抽出时间仔细研究了nnUNet v2&#xff0c;并全…

英国一大学宣布:严禁使用AI生成个人陈述

8月29日&#xff0c;伦敦都会大学&#xff08;London Met Uni&#xff09;在发给合作伙伴的邮件中表示&#xff1a;“我们知道人工智能技术已经被用来生成个人陈述&#xff08;personal statements&#xff0c;即文书&#xff09;。请注意&#xff0c;本大学不接受任何由人工智…

说说大表关联小表

分析&回答 Hive 大表和小表的关联 优先选择将小表放在内存中。小表不足以放到内存中&#xff0c;可以通过bucket-map-join(不清楚的话看底部文章)来实现&#xff0c;效果很明显。 两个表join的时候&#xff0c;其方法是两个join表在join key上都做hash bucket&#xff0c…

系统架构技能之设计模式-抽象工厂模式

一、上篇回顾 上篇我们主要讲述了简单工厂模式和工厂模式。并且分析了每种模式的应用场景和一些优缺点&#xff0c;我们现在来回顾一下&#xff1a; 简单工厂模式&#xff1a;一个工厂负责所有类型对象的创建&#xff0c;不支持无缝的新增新的类型对象的创建。 工厂模式&…