深入解析 Druid 连接池:连接有效性检测与 Keep-Alive 机制
背景
在 Java 程序中,下面是一个经常会碰到的错误。
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
The last packet successfully received from the server was 30,027 milliseconds ago. The last packet sent successfully to the server was 30,028 milliseconds ago.
该错误通常是由于 MySQL 连接意外断开导致的,常见原因包括:
客户端连接池(如 HikariCP、Druid)配置不当,包括:
- 空闲连接超时时间超过 MySQL
wait_timeout
(默认是 28800 秒,即 8 小时),导致连接被 MySQL 服务端关闭。 - 未配置适当的 Keep-Alive 机制,导致连接长时间未使用而被 MySQL 服务器关闭。
- 未进行连接有效性检查,可能导致客户端获取到失效连接。
连接未及时释放。
长时间未释放的连接无法通过连接池的 Keep-Alive 机制保持活跃,更容易因空闲超时被 MySQL 服务端或中间件关闭。
中间层组件的超时限制。
如果客户端与 MySQL 之间存在代理(如 ProxySQL)或负载均衡器(LB),这些组件可能会有独立的空闲连接超时设置,导致连接被提前断开。
网络问题,包括高延迟、丢包或短暂网络中断都会影响数据库连接的稳定性。
连接被 MySQL 服务器主动断开,如 DBA 手动执行
KILL
操作终止连接。
本文将深入解析 Druid 连接池的连接有效性检测机制,重点探讨以下内容:
- Druid 在哪些情况下会检查连接是否可用?
- Druid 如何保持连接的活跃状态(Keep-Alive 机制)?
- Druid 连接池中常见参数的具体含义及其作用。
- 为什么 MySQL 的 general log 看不到
validationQuery
定义的检测语句执行?
希望通过本篇分析,帮助大家更深入理解 Druid 连接池的运行机制。
什么场景下会检测连接的有效性
Druid 连接池在以下四种场景下会检测连接的有效性:
- 申请连接。
- 归还连接。
- 创建新的物理连接。
- 定期检测。
下面我们看看这四种场景的具体实现逻辑。
1. 申请连接
当应用从连接池申请空闲连接时,会检查连接的有效性,与之相关的参数有两个:testOnBorrow 和 testWhileIdle。
申请连接是在getConnectionDirect
方法中实现的,下面我们看看该方法的具体实现细节。
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
int notFullTimeoutRetryCnt = 0;
for (; ; ) {
DruidPooledConnection poolableConnection;
try {
// 从连接池中获取空闲连接
poolableConnection = getConnectionInternal(maxWaitMillis);
} catch (GetConnectionTimeoutException ex) {
...
}
// 如果testOnBorrow为true,则会调用testConnectionInternal检测连接的有效性
if (testOnBorrow) {
boolean validated = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validated) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validated connection.");
}
// 如果连接无效,则会调用discardConnection丢弃该连接,并继续从连接池中获取新的空闲连接。
discardConnection(poolableConnection.holder);
continue;
}
} else {
...
// 如果testOnBorrow不为true,且testWhileIdle为true,则判断连接的空闲时间是否超过timeBetweenEvictionRunsMillis,如果超过,也会调用testConnectionInternal检测连接的有效性
if (testWhileIdle) {
final DruidConnectionHolder holder = poolableConnection.holder;
long currentTimeMillis = System.currentTimeMillis();
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
...
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
if (idleMillis >= timeBetweenEvictionRunsMillis
|| idleMillis < 0 // unexcepted branch
) {
boolean validated = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validated) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validated connection.");
}
// 如果连接无效,则会调用discardConnection丢弃该连接,并继续从连接池中获取新的空闲连接。
discardConnection(poolableConnection.holder);
continue;
}
}
}
}
...
return poolableConnection;
}
}
该方法的实现逻辑如下:
- 首先从连接池中获取一个空闲连接。
- 如果
testOnBorrow
为 true,则调用 testConnectionInternal 检测连接的有效性。若连接有效,则直接返回;若无效,则丢弃该连接并重新获取新的空闲连接。 - 如果
testOnBorrow
为 false 且testWhileIdle
为 true,则判断连接的空闲时间是否超过 timeBetweenEvictionRunsMillis。若超过,则调用 testConnectionInternal 进行检测;若未超过或检测通过,则直接返回连接。
testOnBorrow、testWhileIdle、timeBetweenEvictionRunsMillis 这三个参数的默认值分别为 false、true 和 60000(即 60 秒)。
这就意味着,在默认配置下,当从连接池申请一个连接时,如果该连接空闲时间超过 60 秒,系统会对该连接的有效性进行检查。这样的逻辑适用于大多数场景,因为在大多数情况下,连接在 60 秒内被中断的概率较小。
如果应用对连接的可用性要求极高(例如金融、支付等场景),可以考虑将 testOnBorrow 设置为 true,以确保每次获取的连接都是可用的。但需要注意,这会有一定的性能开销。
2. 归还连接
当应用调用connection.close()
关闭连接时,连接并不会被立即销毁,而是被归还到连接池中,以便后续复用。
如果 testOnReturn(默认为 false) 为 true,会在连接归还时验证其有效性,确保不会将无效连接放回给连接池。
// DruidDataSource.java
protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
final DruidConnectionHolder holder = pooledConnection.holder;
...
if (testOnReturn) {
boolean validated = testConnectionInternal(holder, physicalConnection);
if (!validated) {
JdbcUtils.close(physicalConnection);
...
}
}
...
}
3. 创建新的物理连接
创建新的物理连接是在createPhysicalConnection
方法中实现的。
public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
String url = this.getUrl();
Properties connectProperties = getConnectProperties();
...
try {
// 这里会调用驱动的 connect 方法来建立连接
conn = createPhysicalConnection(url, physicalConnectProperties);
connectedNanos = System.nanoTime();
if (conn == null) {
throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass);
}
...
if (!initSqls(conn, variables, globalVariables)) {
validateConnection(conn);
}
...
}
...
return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos, variables, globalVariables);
}
在连接建立后,如果initSqls(conn, variables, globalVariables)
为 false,则会调用validateConnection
来验证连接的有效性。
以下是 initSqls(conn, variables, globalVariables) 为 false 需要满足的条件:
- connectionInitSqls 为空(默认值)。connectionInitSqls 常用来设置一些连接初始化语句,如
set NAMES 'utf8mb4'
。 - initVariants 为 false(默认值)。如果该参数为 true,则会执行 show variables 获取连接的会话变量。
- initGlobalVariants 为 false(默认值)。如果该参数为 true,则会执行 show global variables 获取全局变量。
4. 定期检测
Druid 在初始化连接池时,会启动一个后台守护线程 (DestroyConnectionThread
),用于定期销毁连接池中的过期连接。
该线程按照一定的时间间隔(由 timeBetweenEvictionRunsMillis 参数决定,默认为 60秒)调用shrink(true, keepAlive)
方法,执行具体的连接销毁操作。
下面,我们看看该方法的具体实现细节。
public void shrink(boolean checkTime, boolean keepAlive) {
...
boolean needFill = false; // 是否需要填充连接池中的空闲连接
int evictCount = 0; // 需要销毁的连接数量
int keepAliveCount = 0; // 需要保持活跃的连接数量
...
try {
...
final int checkCount = poolingCount - minIdle; // 计算连接池中可以回收的连接数量(总连接数减去最小空闲连接数)
final long currentTimeMillis = System.currentTimeMillis(); // 获取系统当前时间
int remaining = 0;
int i = 0;
for (; i < poolingCount; ++i) { // 遍历连接池中的连接
DruidConnectionHolder connection = connections[i];
...
if (checkTime) { // shrink被DestroyConnectionThread调用时,checkTime默认为true。
if (phyTimeoutMillis > 0) { // 物理超时检查
long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis;
// 如果连接的存活时间超过了物理超时时间,则将该连接加入销毁列表。
if (phyConnectTimeMillis > phyTimeoutMillis) {
evictConnections[evictCount++] = connection;
continue;
}
}
// 计算连接的空闲时间
long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;
// 如果连接的空闲时间小于 minEvictableIdleTimeMillis 和 keepAliveBetweenTimeMillis,则退出当前循环
if (idleMillis < minEvictableIdleTimeMillis
&& idleMillis < keepAliveBetweenTimeMillis) {
break;
}
// 如果连接的空闲时间大于maxEvictableIdleTimeMillis,
// 或者连接的空闲时间大于等于 minEvictableIdleTimeMillis 且连接的序号小于可以回收的连接数量
// 才将该连接加入销毁列表。
if (idleMillis >= minEvictableIdleTimeMillis) {
if (i < checkCount) {
evictConnections[evictCount++] = connection;
continue;
} else if (idleMillis > maxEvictableIdleTimeMillis) {
evictConnections[evictCount++] = connection;
continue;
}
}
// 如果 keepAlive 启用,并且连接空闲时间达到了 keepAliveBetweenTimeMillis,则将其加入 keepAliveConnections,后续会检测连接的有效性
if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis
&& currentTimeMillis - connection.lastKeepTimeMillis >= keepAliveBetweenTimeMillis) {
keepAliveConnections[keepAliveCount++] = connection;
} else {
if (i != remaining) { // 将不需要销毁的连接移到新的位置
connections[remaining] = connection;
}
remaining++;
}
}
...
}
// 计算需要移除的连接数
int removeCount = evictCount + keepAliveCount;
// 将未被检查的连接移动到 remaining 之后的位置,确保有效连接的连续性。
if (removeCount > 0) {
int breakedCount = poolingCount - i;
if (breakedCount > 0) {
System.arraycopy(connections, i, connections, remaining, breakedCount);
remaining += breakedCount;
}
System.arraycopy(nullConnections, 0, connections, remaining, removeCount);
poolingCount -= removeCount;
}
keepAliveCheckCount += keepAliveCount;
if (keepAlive && poolingCount + activeCount < minIdle) {
needFill = true;
}
} finally {
lock.unlock();
}
// 关闭需要销毁的连接
if (evictCount > 0) {
for (int i = 0; i < evictCount; ++i) {
DruidConnectionHolder item = evictConnections[i];
Connection connection = item.getConnection();
JdbcUtils.close(connection);
destroyCountUpdater.incrementAndGet(this);
}
System.arraycopy(nullConnections, 0, evictConnections, 0, evictConnections.length);
}
// 检测 keepAliveConnections 中连接的有效性,若有效,将其放回连接池。如果校验失败,则丢弃连接。
if (keepAliveCount > 0) {
for (int i = keepAliveCount - 1; i >= 0; --i) {
DruidConnectionHolder holder = keepAliveConnections[i];
Connection connection = holder.getConnection();
holder.incrementKeepAliveCheckCount();
boolean validate = false;
try {
this.validateConnection(connection);
validate = true;
} catch (Throwable error) {
...
}
boolean discard = !validate;
if (validate) {
holder.lastKeepTimeMillis = System.currentTimeMillis();
boolean putOk = put(holder, 0L, true);
if (!putOk) {
discard = true;
}
}
...
}
this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
System.arraycopy(nullConnections, 0, keepAliveConnections, 0, keepAliveConnections.length);
}
if (needFill) {
lock.lock();
try {
// 如果连接池中的总连接数 (activeCount + poolingCount + createTaskCount) 小于 minIdle,则补充新的连接。
int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
emptySignal(fillCount);
} finally {
lock.unlock();
}
} else if (fatalErrorIncrement > 0) {
lock.lock();
try {
emptySignal();
} finally {
lock.unlock();
}
}
}
该方法的处理流程如下:
通过 checkCount = poolingCount - minIdle 计算当前池中可以回收的连接数量。其中
minIdle
是 Druid 参数,用于指定连接池需保留的最小空闲连接数。遍历连接池中的连接,并执行以下检查:
物理连接存活时间检查:如果 phyTimeoutMillis > 0,检查物理连接的存活时长是否超过
phyTimeoutMillis
,如果超过,则将该连接加入销毁列表(evictConnections)。空闲时间检查:如果连接的空闲时间大于 maxEvictableIdleTimeMillis,或空闲时间大于等于 minEvictableIdleTimeMillis 且连接的序号小于 checkCount(可回收连接数),则将该连接加入销毁列表。
保持连接活跃:若连接需要保持活跃(
keepAlive
开启)且空闲时间超过keepAliveBetweenTimeMillis
,则将该连接加入 keepAliveConnections 列表。将未被检查的连接移动到 remaining 之后的位置,确保有效连接的连续性。
如果 evictCount 大于 0,表示有连接需要销毁,遍历销毁列表(evictConnections)关闭这些连接。
如果 keepAliveCount 大于 0,表示有连接需要保持活跃,遍历 keepAliveConnections 列表,检查连接有效性,若有效,将其放回连接池。如果校验失败,则丢弃连接。
如果 needFill 为 true,表示连接池中空闲连接不足,触发填充信号以创建新连接。
所以,Druid 连接池默认情况下,每 60 秒(由 timeBetweenEvictionRunsMillis
参数控制)执行一次连接回收和维护操作,并保持一定数量的空闲连接。其核心逻辑包括:
回收超时或多余的空闲连接:
- 连接的空闲时间超过 maxEvictableIdleTimeMillis 或 phyConnectTimeMillis,将被回收。
- 当连接池的数量超过最小空闲连接数 minIdle 时,如果连接的空闲时间超过 minEvictableIdleTimeMillis,也会被回收。
维护 Keep-Alive 机制(如果
keepAlive
开启):- 当连接的空闲时间超过 keepAliveBetweenTimeMillis,且距离上次 Keep-Alive 检测时间超过 keepAliveBetweenTimeMillis 时,执行有效性检测。
- 通过 validateConnection 进行检测,合格的连接重新加入池中,不合格的连接被销毁。
必要时补充新的连接:
若当前连接数(activeCount + poolingCount)低于 minIdle,则触发连接补充机制,创建新的连接。
需要注意的是,即使连接开启了定期探活检测,若发生超时,仍会被回收。
接下来,我们看看上述参数的默认值:
- timeBetweenEvictionRunsMillis:默认 60000 毫秒(60 秒)。
- minEvictableIdleTimeMillis:默认 1800000 毫秒(30 分钟)。
- maxEvictableIdleTimeMillis:默认 25200000 毫秒(7 小时)。
- phyTimeoutMillis:默认 -1。
- keepAlive:默认为 false。
- keepAliveBetweenTimeMillis:默认 120000 毫秒(120 秒)。
为什么设置的 validationQuery 没有效果?
在 Druid 连接池中,判断连接是否有效时,通常调用 testConnectionInternal
或 validateConnection
方法。这两个方法的核心逻辑基本相同,具体如下:
优先使用 validConnectionChecker 进行连接校验:
- validConnectionChecker 是一个接口,定义了 isValidConnection 方法,用于检测数据库连接的有效性。
- 具体的数据库有对应的实现类,例如:MySQL 由
MySqlValidConnectionChecker
实现,Oracle 由 OracleValidConnectionChecker 实现。 - validConnectionChecker 在 initValidConnectionChecker 方法中初始化,并根据数据库驱动类型选择合适的实现类。
如果 validConnectionChecker 未初始化,则执行默认检查:
通过 validationQuery 执行 SQL 语句,验证连接是否有效。
该方法适用于所有数据库,但会带来一定的性能开销。
以下是 MySQL 实现类(MySqlValidConnectionChecker)中isValidConnection
方法的具体实现。
// druid-1.2.24/core/src/main/java/com/alibaba/druid/pool/vendor/MySqlValidConnectionChecker.java
public boolean isValidConnection(Connection conn,
String validateQuery,
int validationQueryTimeout) throws Exception {
if (conn.isClosed()) {
return false;
}
if (usePingMethod || StringUtils.isEmpty(validateQuery)) {
validateQuery = DEFAULT_VALIDATION_QUERY;
}
return ValidConnectionCheckerAdapter.execValidQuery(conn, validateQuery, validationQueryTimeout);
}
方法中的 usePingMethod 受druid.mysql.usePingMethod
参数控制,其默认值为 true。
当 usePingMethod 等于 true 时,validateQuery 将被设置为 DEFAULT_VALIDATION_QUERY,即/* ping */ SELECT 1
,而非用户自定义的 validationQuery。
execValidQuery() 方法执行 validateQuery 时,如果查询语句以/* ping */
开头,MySQL JDBC 驱动会进行特殊处理。
具体来说,MySQL JDBC 在解析 SQL 语句时,会判断它是否以 PING_MARKER(即/* ping */
)开头,如果是,则不会执行 SQL 语句,而是调用doPingInstead()
,直接向 MySQL 服务器发送 COM_PING 命令,这样可以减少 SQL 解析和执行的开销,提高性能。
// mysql-connector-j-8.0.33/src/main/user-impl/java/com/mysql/cj/jdbc/StatementImpl.java
public java.sql.ResultSet executeQuery(String sql) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
JdbcConnection locallyScopedConn = this.connection;
...
if (sql.charAt(0) == '/') {
if (sql.startsWith(PING_MARKER)) {
doPingInstead(); // 直接发送 COM_PING 命令
return this.results;
}
}
...
}
}
关于参数设置的几点建议
minEvictableIdleTimeMillis
,maxEvictableIdleTimeMillis
不宜设置过小,因为频繁销毁和创建连接会带来额外的性能开销。建议开启 keepAlive 机制,尤其是在客户端与 MySQL 之间存在代理的情况下,这些组件可能会有独立的空闲连接超时设置,导致连接被提前断开。
在连接申请时检测连接的有效性(通过设置 testOnBorrow 为 true)是最有效的方式,可以确保每次获取的连接都是可用的。但这种方式会对应用性能产生一定影响,尤其是在高并发场景下。
因此,建议根据业务需求权衡性能与可靠性,选择合适的检测策略。
考虑到网络可能的故障,即使 Druid 连接池定期检测连接的有效性,也无法 100% 保证所有连接都可用,所以应用端一定要做好容错处理。
对于代码中未及时归还使用过的连接,一方面可能导致连接泄漏,使连接池耗尽可用连接。另一方面,未释放的连接无法通过 Druid 的 Keep-Alive 机制保持活跃状态,更容易因空闲超时被 MySQL 服务器或中间件关闭。
为了避免这些问题,建议在应用的测试环境开启以下参数来识别长时间未归还的连接:
logAbandoned
,removeAbandoned
,removeAbandonedTimeoutMillis
。
总结
Druid 连接池在以下四种场景下会检测连接的有效性:申请连接、归还连接、创建新物理连接以及定期检测。
Druid 通过开启 keepAlive 参数,定期对空闲连接进行有效性检测,确保连接保持活跃状态。
当连接的空闲时间超过 keepAliveBetweenTimeMillis 时,Druid 会触发 Keep-Alive 检测,验证连接的有效性。如果连接有效,则重新放回连接池;如果无效,则将其销毁。
Druid 默认使用 MySQL 的 COM_PING 命令进行连接有效性检测,这种方式比执行 SQL 语句更高效。
由于 COM_PING 的优先级高于用户自定义的 validationQuery,因此在默认配置下,validationQuery 不会被执行。
如果用户希望使用自定义的 validationQuery 进行连接检测,可将
druid.mysql.usePingMethod
参数设置为 false 来实现。
深入解析 Druid 连接池:连接有效性检测与 Keep-Alive 机制的更多相关文章
- spring boot配置druid连接池连接mysql
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- node 连接MySQL及其分装, 连接池连接
const mysql = require('mysql') const config = require('./../../config/config.default') var connectio ...
- JNDI连接池连接Oracle数据库
今天做了一个评论的小功能,要求用JNDI连接池连接Oracle数据库,以前只是测试了是否连接的上,现在没想到一个JNDI连接池连接Oracle数据库,纠结了好久,原来都是Oracle数据库的问题,这是 ...
- python通过连接池连接redis,操作redis队列
在每次使用redis都进行连接的话会拉低redis的效率,都知道redis是基于内存的数据库,效率贼高,所以每次进行连接比真正使用消耗的资源和时间还多.所以为了节省资源,减少多次连接损耗,连接池的作用 ...
- Mybatis 搭配 阿里druid连接池 连接 oracle 或 mysql
DRUID介绍 DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针 ...
- springboot使用druid连接池连接Oracle数据库的基本配置
#阿里连接池配置 #spring.datasource.druid.driver-class-name=oracle.jdbc.driver.OracleDriver #可配可不配,阿里的数据库连接池 ...
- springboot druid 数据库连接池连接失败后一直重连
在使用个人阿里云测试机,在查询实时输出日志时,看到数据库连接失败后,服务器一直在重连服务器.开始以为是遭受重复攻击,后面把服务重启后,就没有出现一直重连的情况.看以下输出日志: 2022-02-09 ...
- JavaEE连接池泄漏问题检测Oracle数据库
1.项目环境 项目是典型的轻量级JavaEE项目,使用SSH框架构建,数据源使用DBCP管理,和Spring进行了整合. 项目数据库使用Oracle数据库. 项目DBCP配置内容如下 ###### D ...
- FIREDAC FDConnection 连接池 连接串
一.FDConnection 连接池 http://docs.embarcadero.com/products/rad_studio/firedac/frames.html?frmname=topic ...
- 记录关于使用ADO.NET 连接池连接Oracle时Session信息不更新的坑
最近的一个项目中,由于界面查询的数据量比较大,关联的表比较多,有些数据查出来需要临时保存起来供后面的查询使用,于是想到了用oracle的临时表来实现这个需求.大家都知道,oracle的临时表有两种:事 ...
随机推荐
- Qt编写可视化大屏电子看板系统21-数据转曲线
一.前言 数据转曲线,这个用的非常多,比如串口或者网络收到的数据,对特定的字节数据绘制实时的曲线,或者对历史记录存储的数据进行曲线绘制,按照约定的规则,数据转曲线绘制必须提供规则,没有规则只能对所有数 ...
- [转]fatal: remote error: The unauthenticated git protocol on port 9418 is no longer support问题解决
背景 因为居家办公,把代码从远程clone下来之后,发现使用npm install一直失败. 提示的错误:fatal: remote error: The unauthenticated git pr ...
- JS端实现图片、视频时直接下载而不是打开预览
项目中的附件列表,通常情况都需要提供下载.删除的功能,功能本身没有什么要说的,都是基本功能,使用浏览器的的下载功能,也都是用window.open(url),或者window.location.hre ...
- win10 bat文件命令大全-快捷键
具体介绍: 1. echo 和 @回显命令 @ #关闭单行回显 echo off #从下一行开始关闭回显 @echo off ...
- 6种微服务RPC框架-copy
一类是跟某种特定语言平台绑定的,另一类是与语言无关即跨语言平台的. 跟语言平台绑定的开源 RPC 框架主要有下面几种. Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并于 2011 年末 ...
- springboot集成webService开发详解
https://blog.csdn.net/m0_51111980/article/details/124581559https://blog.csdn.net/qq_43842093/article ...
- nginx平台初探-2
handler模块简介 相信大家在看了前一章的模块概述以后,都对nginx的模块有了一个基本的认识.基本上作为第三方开发者最可能开发的就是三种类型的模块,即handler,filter和load-ba ...
- Codeblocks 显示所创建工程的文件夹
问题: 有时创建完工程后没有默认打开文件夹: 方法: 使用F2 +shift view->manager
- 云辅助隐私集合求交(Server-Aided PSI)协议介绍:学习
原文来自:云辅助隐私集合求交(Server-Aided PSI)协议介绍,下面学习一波,并记录一些笔记. 背景 总结: 1.PSI-CA和PSI相比,前者在乎的是交集的大小,后者在乎的是交集本身.另外 ...
- 反射:获取Class 类的实例(四种方法)
Class 类 对象照镜子后可以得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象.一个 Class 对象包含了 ...