The last packet successfully received from the server was 5,627,630 mi
1.堕落信息The last packet successfully received from the server was 5,627,630 milliseconds ago. The last packet sent successfully to the server was 5,627,630 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
2.缘故原由分析
客户端应用的连接池中的连接最后一次收到MySQL server的相应是xxx毫秒之前;已经超出了MySQL server端配置的wait_timeout的巨细。
客户端连接池中的连接与MySQL server端连接池的连接是一一对应的,超出wait_timeout配置的时间未连接过,则服务端将对应的连接销毁,这时间客户端是不知道的。
下次客户端再去进行连接的时间,就会出现上面的错误
3.解决思路
1.提前销毁
服务端到期销毁连接之前,客户端就先销毁对应的连接,也就是客户端销毁连接的时间要小于wait_timeout配置的时间
MySQL默认的wait_timeout配置 默认8小时
https://i-blog.csdnimg.cn/direct/e275c92bad0f45e9a3bdc129e25d895d.png
Druid
连接池默认连接最大空闲时间:默认7小时
com.alibaba.druid.pool.DruidAbstractDataSource
https://i-blog.csdnimg.cn/direct/d2e4dc0035a646b1a065aa65b2d00a71.png
hikari
com.zaxxer.hikari.HikariConfig
maxLifetime配置连接最大的生命时长,默认30分钟;超过生命时长的,空闲的直接驱逐,非空闲的标记,下次获取时驱逐 https://i-blog.csdnimg.cn/direct/b71d0f92e6a04d9c8579a366c8c9a897.png
2.每次连接时做检测
连接上增长autoReconnect=true
4.连接池源码
Druid在初始化连接池是启动一个默认每分钟执行的定时任务
public class DestroyTask implements Runnable {
@Override
public void run() {
shrink(true);
if (isRemoveAbandoned()) {
removeAbandoned();
}
}
} public void shrink(boolean checkTime) {
final List<DruidConnectionHolder> evictList = new ArrayList<DruidConnectionHolder>();
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
return;
}
try {
//当前连接数-最小活跃连接数=要销毁的数量
final int checkCount = poolingCount - minIdle;
final long currentTimeMillis = System.currentTimeMillis();
for (int i = 0; i < poolingCount; ++i) {
DruidConnectionHolder connection = connections;
if (checkTime) {
//phyTimeoutMillis默认-1,配置了则计算是否超时,所以phyTimeoutMillis决定了连接的生命时长
if (phyTimeoutMillis > 0) {
long phyConnectTimeMillis = currentTimeMillis - connection.getTimeMillis();
if (phyConnectTimeMillis > phyTimeoutMillis) {
evictList.add(connection);
continue;
}
}
//空闲时长
long idleMillis = currentTimeMillis - connection.getLastActiveTimeMillis();
if (idleMillis < minEvictableIdleTimeMillis) {
break;
}
if (checkTime && i < checkCount) {//大于最小连接数的连接销毁
evictList.add(connection);
} else if (idleMillis > maxEvictableIdleTimeMillis) {
//超过最大空闲时间的销毁
evictList.add(connection);
}
} else {
if (i < checkCount) { //大于最小连接数的连接销毁
evictList.add(connection);
} else {
break;
}
}
}
int removeCount = evictList.size();
if (removeCount > 0) {
System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
poolingCount -= removeCount;
}
} finally {
lock.unlock();
}
//关闭连接
for (DruidConnectionHolder item : evictList) {
Connection connection = item.getConnection();
JdbcUtils.close(connection);
destroyCount.incrementAndGet();
}
} HikariPool每30秒执行定时任务
private final class HouseKeeper implements Runnable
{
private volatile long previous = plusMillis(currentTime(), -housekeepingPeriodMs);
@Override
public void run()
{
try {
// refresh values in case they changed via MBean
connectionTimeout = config.getConnectionTimeout();
validationTimeout = config.getValidationTimeout();
leakTaskFactory.updateLeakDetectionThreshold(config.getLeakDetectionThreshold());
catalog = (config.getCatalog() != null && !config.getCatalog().equals(catalog)) ? config.getCatalog() : catalog;
final long idleTimeout = config.getIdleTimeout();
final long now = currentTime();
// Detect retrograde time, allowing +128ms as per NTP spec.
if (plusMillis(now, 128) < plusMillis(previous, housekeepingPeriodMs)) {
logger.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.",
poolName, elapsedDisplayString(previous, now));
previous = now;
softEvictConnections();
return;
}
else if (now > plusMillis(previous, (3 * housekeepingPeriodMs) / 2)) {
// No point evicting for forward clock motion, this merely accelerates connection retirement anyway
logger.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", poolName, elapsedDisplayString(previous, now));
}
previous = now;
String afterPrefix = "Pool ";
if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) {
logPoolState("Before cleanup ");
afterPrefix = "After cleanup";
final List<PoolEntry> notInUse = connectionBag.values(STATE_NOT_IN_USE);
int toRemove = notInUse.size() - config.getMinimumIdle();
for (PoolEntry entry : notInUse) {
if (toRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) {
closeConnection(entry, "(connection has passed idleTimeout)");
toRemove--;
}
}
}
logPoolState(afterPrefix);
fillPool(); // Try to maintain minimum connections
}
catch (Exception e) {
logger.error("Unexpected exception in housekeeping task", e);
}
} private PoolEntry createPoolEntry()
{
final PoolEntry poolEntry = newPoolEntry();
final long maxLifetime = config.getMaxLifetime();
if (maxLifetime > 0) {
// variance up to 2.5% of the maxlifetime
final long variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0;
final long lifetime = maxLifetime - variance;
poolEntry.setFutureEol(houseKeepingExecutorService.schedule(
() -> {
if (softEvictConnection(poolEntry, "(connection has passed maxLifetime)", false /* not owner */)) {
addBagItem(connectionBag.getWaitingThreadCount());
}
},
lifetime, MILLISECONDS));
}
return poolEntry;
}
private boolean softEvictConnection(final PoolEntry poolEntry, final String reason, final boolean owner)
{
poolEntry.markEvicted();
if (owner || connectionBag.reserve(poolEntry)) {
closeConnection(poolEntry, reason);
return true;
}
return false;
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]