聊聊druid的borrow行为

本文主要研究一下druid的borrow行为

getConnection

com/alibaba/druid/pool/DruidDataSource.java

    public DruidPooledConnection getConnection() throws SQLException {return getConnection(maxWait);}public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {init();if (filters.size() > 0) {FilterChainImpl filterChain = new FilterChainImpl(this);return filterChain.dataSource_connect(this, maxWaitMillis);} else {return getConnectionDirect(maxWaitMillis);}}

DruidDataSource的getConnection方法内部调用的是getConnectionDirect(maxWaitMillis)

getConnectionDirect

com/alibaba/druid/pool/DruidDataSource.java

    public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {int notFullTimeoutRetryCnt = 0;for (; ; ) {// handle notFullTimeoutRetryDruidPooledConnection poolableConnection;try {poolableConnection = getConnectionInternal(maxWaitMillis);} catch (GetConnectionTimeoutException ex) {if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {notFullTimeoutRetryCnt++;if (LOG.isWarnEnabled()) {LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);}continue;}throw ex;}if (testOnBorrow) {boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}discardConnection(poolableConnection.holder);continue;}} else {if (poolableConnection.conn.isClosed()) {discardConnection(poolableConnection.holder); // 传入null,避免重复关闭continue;}if (testWhileIdle) {final DruidConnectionHolder holder = poolableConnection.holder;long currentTimeMillis = System.currentTimeMillis();long lastActiveTimeMillis = holder.lastActiveTimeMillis;long lastExecTimeMillis = holder.lastExecTimeMillis;long lastKeepTimeMillis = holder.lastKeepTimeMillis;if (checkExecuteTime&& lastExecTimeMillis != lastActiveTimeMillis) {lastActiveTimeMillis = lastExecTimeMillis;}if (lastKeepTimeMillis > lastActiveTimeMillis) {lastActiveTimeMillis = lastKeepTimeMillis;}long idleMillis = currentTimeMillis - lastActiveTimeMillis;long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;if (timeBetweenEvictionRunsMillis <= 0) {timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;}if (idleMillis >= timeBetweenEvictionRunsMillis|| idleMillis < 0 // unexcepted branch) {boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}discardConnection(poolableConnection.holder);continue;}}}}if (removeAbandoned) {StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();poolableConnection.connectStackTrace = stackTrace;poolableConnection.setConnectedTimeNano();poolableConnection.traceEnable = true;activeConnectionLock.lock();try {activeConnections.put(poolableConnection, PRESENT);} finally {activeConnectionLock.unlock();}}if (!this.defaultAutoCommit) {poolableConnection.setAutoCommit(false);}return poolableConnection;}}public boolean isFull() {lock.lock();try {return this.poolingCount + this.activeCount >= this.maxActive;} finally {lock.unlock();}}

getConnectionDirect在一个for循环里头进行获取连接,首先执行getConnectionInternal(maxWaitMillis),若出现GetConnectionTimeoutException异常,则在notFull且notFullTimeoutRetryCnt小于等于this.notFullTimeoutRetryCount时会递增notFullTimeoutRetryCnt,然后continue继续循环,否则直接抛出GetConnectionTimeoutException跳出循环

获取到连接之后,判断是否是testOnBorrow,如果是则执行testConnectionInternal,若校验不成功则执行discardConnection,然后继续循环;若非testOnBorrow则判断conn是否closed,若是则执行discardConnection,然后继续循环,若非closed则进入testWhileIdle的逻辑(druid直接在getConnection的时候执行testWhileIdle有点令人匪夷所思)

最后是removeAbandoned,维护connectedTimeNano,将当前连接放到activeConnections中

getConnectionInternal

com/alibaba/druid/pool/DruidDataSource.java

    private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {if (closed) {connectErrorCountUpdater.incrementAndGet(this);throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis));}if (!enable) {connectErrorCountUpdater.incrementAndGet(this);if (disableException != null) {throw disableException;}throw new DataSourceDisableException();}final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait);final int maxWaitThreadCount = this.maxWaitThreadCount;DruidConnectionHolder holder;for (boolean createDirect = false; ; ) {if (createDirect) {createStartNanosUpdater.set(this, System.nanoTime());if (creatingCountUpdater.compareAndSet(this, 0, 1)) {PhysicalConnectionInfo pyConnInfo = DruidDataSource.this.createPhysicalConnection();holder = new DruidConnectionHolder(this, pyConnInfo);holder.lastActiveTimeMillis = System.currentTimeMillis();creatingCountUpdater.decrementAndGet(this);directCreateCountUpdater.incrementAndGet(this);if (LOG.isDebugEnabled()) {LOG.debug("conn-direct_create ");}boolean discard;lock.lock();try {if (activeCount < maxActive) {activeCount++;holder.active = true;if (activeCount > activePeak) {activePeak = activeCount;activePeakTime = System.currentTimeMillis();}break;} else {discard = true;}} finally {lock.unlock();}if (discard) {JdbcUtils.close(pyConnInfo.getPhysicalConnection());}}}try {lock.lockInterruptibly();} catch (InterruptedException e) {connectErrorCountUpdater.incrementAndGet(this);throw new SQLException("interrupt", e);}try {if (maxWaitThreadCount > 0&& notEmptyWaitThreadCount >= maxWaitThreadCount) {connectErrorCountUpdater.incrementAndGet(this);throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "+ lock.getQueueLength());}if (onFatalError&& onFatalErrorMaxActive > 0&& activeCount >= onFatalErrorMaxActive) {connectErrorCountUpdater.incrementAndGet(this);StringBuilder errorMsg = new StringBuilder();errorMsg.append("onFatalError, activeCount ").append(activeCount).append(", onFatalErrorMaxActive ").append(onFatalErrorMaxActive);if (lastFatalErrorTimeMillis > 0) {errorMsg.append(", time '").append(StringUtils.formatDateTime19(lastFatalErrorTimeMillis, TimeZone.getDefault())).append("'");}if (lastFatalErrorSql != null) {errorMsg.append(", sql \n").append(lastFatalErrorSql);}throw new SQLException(errorMsg.toString(), lastFatalError);}connectCount++;if (createScheduler != null&& poolingCount == 0&& activeCount < maxActive&& creatingCountUpdater.get(this) == 0&& createScheduler instanceof ScheduledThreadPoolExecutor) {ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) createScheduler;if (executor.getQueue().size() > 0) {createDirect = true;continue;}}if (maxWait > 0) {holder = pollLast(nanos);} else {holder = takeLast();}if (holder != null) {if (holder.discard) {continue;}activeCount++;holder.active = true;if (activeCount > activePeak) {activePeak = activeCount;activePeakTime = System.currentTimeMillis();}}} catch (InterruptedException e) {connectErrorCountUpdater.incrementAndGet(this);throw new SQLException(e.getMessage(), e);} catch (SQLException e) {connectErrorCountUpdater.incrementAndGet(this);throw e;} finally {lock.unlock();}break;}if (holder == null) {long waitNanos = waitNanosLocal.get();final long activeCount;final long maxActive;final long creatingCount;final long createStartNanos;final long createErrorCount;final Throwable createError;try {lock.lock();activeCount = this.activeCount;maxActive = this.maxActive;creatingCount = this.creatingCount;createStartNanos = this.createStartNanos;createErrorCount = this.createErrorCount;createError = this.createError;} finally {lock.unlock();}StringBuilder buf = new StringBuilder(128);buf.append("wait millis ").append(waitNanos / (1000 * 1000)).append(", active ").append(activeCount).append(", maxActive ").append(maxActive).append(", creating ").append(creatingCount);if (creatingCount > 0 && createStartNanos > 0) {long createElapseMillis = (System.nanoTime() - createStartNanos) / (1000 * 1000);if (createElapseMillis > 0) {buf.append(", createElapseMillis ").append(createElapseMillis);}}if (createErrorCount > 0) {buf.append(", createErrorCount ").append(createErrorCount);}List<JdbcSqlStatValue> sqlList = this.getDataSourceStat().getRuningSqlList();for (int i = 0; i < sqlList.size(); ++i) {if (i != 0) {buf.append('\n');} else {buf.append(", ");}JdbcSqlStatValue sql = sqlList.get(i);buf.append("runningSqlCount ").append(sql.getRunningCount());buf.append(" : ");buf.append(sql.getSql());}String errorMessage = buf.toString();if (createError != null) {throw new GetConnectionTimeoutException(errorMessage, createError);} else {throw new GetConnectionTimeoutException(errorMessage);}}holder.incrementUseCount();DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);return poolalbeConnection;}

getConnectionInternal方法先判断是否closed,如果是则抛出DataSourceClosedException,接着判断是否enable,如果不是则抛出DataSourceDisableException,紧接着for循环,它主要根据createDirect来执行不同逻辑,第一次默认createDirect为false;

createDirect为false,对于notEmptyWaitThreadCount大于等于maxWaitThreadCount则抛出SQLException,对于poolingCount为0且activeCount小于maxActive,createScheduler的queue大小大于0的,则设置createDirect为true;否则对于maxWait大于0的,执行pollLast(nanos),否则执行takeLast()

createDirect为true,会通过DruidDataSource.this.createPhysicalConnection()创建物理连接,对于activeCount小于maxActive的,则维护activeCount跳出循环,否则标记discard为true,通过JdbcUtils.close(pyConnInfo.getPhysicalConnection())关闭连接

pollLast

    private DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException {long estimate = nanos;for (; ; ) {if (poolingCount == 0) {emptySignal(); // send signal to CreateThread create connectionif (failFast && isFailContinuous()) {throw new DataSourceNotAvailableException(createError);}if (estimate <= 0) {waitNanosLocal.set(nanos - estimate);return null;}notEmptyWaitThreadCount++;if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {notEmptyWaitThreadPeak = notEmptyWaitThreadCount;}try {long startEstimate = estimate;estimate = notEmpty.awaitNanos(estimate); // signal by// recycle or// creatornotEmptyWaitCount++;notEmptyWaitNanos += (startEstimate - estimate);if (!enable) {connectErrorCountUpdater.incrementAndGet(this);if (disableException != null) {throw disableException;}throw new DataSourceDisableException();}} catch (InterruptedException ie) {notEmpty.signal(); // propagate to non-interrupted threadnotEmptySignalCount++;throw ie;} finally {notEmptyWaitThreadCount--;}if (poolingCount == 0) {if (estimate > 0) {continue;}waitNanosLocal.set(nanos - estimate);return null;}}decrementPoolingCount();DruidConnectionHolder last = connections[poolingCount];connections[poolingCount] = null;long waitNanos = nanos - estimate;last.setLastNotEmptyWaitNanos(waitNanos);return last;}}

pollLast方法在poolingCount为0时执行emptySignal,另外主要是处理notEmpty这个condition,然后取connections[poolingCount]

takeLast

    DruidConnectionHolder takeLast() throws InterruptedException, SQLException {try {while (poolingCount == 0) {emptySignal(); // send signal to CreateThread create connectionif (failFast && isFailContinuous()) {throw new DataSourceNotAvailableException(createError);}notEmptyWaitThreadCount++;if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {notEmptyWaitThreadPeak = notEmptyWaitThreadCount;}try {notEmpty.await(); // signal by recycle or creator} finally {notEmptyWaitThreadCount--;}notEmptyWaitCount++;if (!enable) {connectErrorCountUpdater.incrementAndGet(this);if (disableException != null) {throw disableException;}throw new DataSourceDisableException();}}} catch (InterruptedException ie) {notEmpty.signal(); // propagate to non-interrupted threadnotEmptySignalCount++;throw ie;}decrementPoolingCount();DruidConnectionHolder last = connections[poolingCount];connections[poolingCount] = null;return last;}

takeLast方法在poolingCount为0的时候执行emptySignal,然后通过notEmpty.await()进行阻塞等待,最后返回connections[poolingCount]

emptySignal

    private void emptySignal() {if (createScheduler == null) {empty.signal();return;}if (createTaskCount >= maxCreateTaskCount) {return;}if (activeCount + poolingCount + createTaskCount >= maxActive) {return;}submitCreateTask(false);}

emptySignal方法,对于createScheduler为null的执行empty.signal(),之后判断task数量即maxActive判断,最后执行submitCreateTask(false)

submitCreateTask

    private void submitCreateTask(boolean initTask) {createTaskCount++;CreateConnectionTask task = new CreateConnectionTask(initTask);if (createTasks == null) {createTasks = new long[8];}boolean putted = false;for (int i = 0; i < createTasks.length; ++i) {if (createTasks[i] == 0) {createTasks[i] = task.taskId;putted = true;break;}}if (!putted) {long[] array = new long[createTasks.length * 3 / 2];System.arraycopy(createTasks, 0, array, 0, createTasks.length);array[createTasks.length] = task.taskId;createTasks = array;}this.createSchedulerFuture = createScheduler.submit(task);}

submitCreateTask会创建CreateConnectionTask,然后提交到createScheduler执行

CreateConnectionTask

com/alibaba/druid/pool/DruidDataSource.java

    public class CreateConnectionTask implements Runnable {private int errorCount;private boolean initTask;private final long taskId;public CreateConnectionTask() {taskId = createTaskIdSeedUpdater.getAndIncrement(DruidDataSource.this);}public CreateConnectionTask(boolean initTask) {taskId = createTaskIdSeedUpdater.getAndIncrement(DruidDataSource.this);this.initTask = initTask;}@Overridepublic void run() {runInternal();}private void runInternal() {for (; ; ) {// addLastlock.lock();try {if (closed || closing) {clearCreateTask(taskId);return;}boolean emptyWait = true;if (createError != null && poolingCount == 0) {emptyWait = false;}if (emptyWait) {// 必须存在线程等待,才创建连接if (poolingCount >= notEmptyWaitThreadCount //&& (!(keepAlive && activeCount + poolingCount < minIdle)) // 在keepAlive场景不能放弃创建&& (!initTask) // 线程池初始化时的任务不能放弃创建&& !isFailContinuous() // failContinuous时不能放弃创建,否则会无法创建线程&& !isOnFatalError() // onFatalError时不能放弃创建,否则会无法创建线程) {clearCreateTask(taskId);return;}// 防止创建超过maxActive数量的连接if (activeCount + poolingCount >= maxActive) {clearCreateTask(taskId);return;}}} finally {lock.unlock();}PhysicalConnectionInfo physicalConnection = null;try {physicalConnection = createPhysicalConnection();} catch (OutOfMemoryError e) {LOG.error("create connection OutOfMemoryError, out memory. ", e);errorCount++;if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {// fail over retry attemptssetFailContinuous(true);if (failFast) {lock.lock();try {notEmpty.signalAll();} finally {lock.unlock();}}if (breakAfterAcquireFailure) {lock.lock();try {clearCreateTask(taskId);} finally {lock.unlock();}return;}this.errorCount = 0; // reset errorCountif (closing || closed) {lock.lock();try {clearCreateTask(taskId);} finally {lock.unlock();}return;}createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);return;}} catch (SQLException e) {LOG.error("create connection SQLException, url: " + jdbcUrl, e);errorCount++;if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {// fail over retry attemptssetFailContinuous(true);if (failFast) {lock.lock();try {notEmpty.signalAll();} finally {lock.unlock();}}if (breakAfterAcquireFailure) {lock.lock();try {clearCreateTask(taskId);} finally {lock.unlock();}return;}this.errorCount = 0; // reset errorCountif (closing || closed) {lock.lock();try {clearCreateTask(taskId);} finally {lock.unlock();}return;}createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);return;}} catch (RuntimeException e) {LOG.error("create connection RuntimeException", e);// unknow fatal exceptionsetFailContinuous(true);continue;} catch (Error e) {lock.lock();try {clearCreateTask(taskId);} finally {lock.unlock();}LOG.error("create connection Error", e);// unknow fatal exceptionsetFailContinuous(true);break;} catch (Throwable e) {lock.lock();try {clearCreateTask(taskId);} finally {lock.unlock();}LOG.error("create connection unexecpted error.", e);break;}if (physicalConnection == null) {continue;}physicalConnection.createTaskId = taskId;boolean result = put(physicalConnection);if (!result) {JdbcUtils.close(physicalConnection.getPhysicalConnection());LOG.info("put physical connection to pool failed.");}break;}}}

CreateConnectionTask通过for循环,然后加锁处理minIdle及maxActive,最后通过createPhysicalConnection创建物理连接

createPhysicalConnection

com/alibaba/druid/pool/DruidAbstractDataSource.java

    public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {String url = this.getUrl();Properties connectProperties = getConnectProperties();String user;if (getUserCallback() != null) {user = getUserCallback().getName();} else {user = getUsername();}String password = getPassword();PasswordCallback passwordCallback = getPasswordCallback();if (passwordCallback != null) {if (passwordCallback instanceof DruidPasswordCallback) {DruidPasswordCallback druidPasswordCallback = (DruidPasswordCallback) passwordCallback;druidPasswordCallback.setUrl(url);druidPasswordCallback.setProperties(connectProperties);}char[] chars = passwordCallback.getPassword();if (chars != null) {password = new String(chars);}}Properties physicalConnectProperties = new Properties();if (connectProperties != null) {physicalConnectProperties.putAll(connectProperties);}if (user != null && user.length() != 0) {physicalConnectProperties.put("user", user);}if (password != null && password.length() != 0) {physicalConnectProperties.put("password", password);}Connection conn = null;long connectStartNanos = System.nanoTime();long connectedNanos, initedNanos, validatedNanos;Map<String, Object> variables = initVariants? new HashMap<String, Object>(): null;Map<String, Object> globalVariables = initGlobalVariants? new HashMap<String, Object>(): null;createStartNanosUpdater.set(this, connectStartNanos);creatingCountUpdater.incrementAndGet(this);try {conn = createPhysicalConnection(url, physicalConnectProperties);connectedNanos = System.nanoTime();if (conn == null) {throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass);}initPhysicalConnection(conn, variables, globalVariables);initedNanos = System.nanoTime();validateConnection(conn);validatedNanos = System.nanoTime();setFailContinuous(false);setCreateError(null);} catch (SQLException ex) {setCreateError(ex);JdbcUtils.close(conn);throw ex;} catch (RuntimeException ex) {setCreateError(ex);JdbcUtils.close(conn);throw ex;} catch (Error ex) {createErrorCountUpdater.incrementAndGet(this);setCreateError(ex);JdbcUtils.close(conn);throw ex;} finally {long nano = System.nanoTime() - connectStartNanos;createTimespan += nano;creatingCountUpdater.decrementAndGet(this);}return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos, variables, globalVariables);}

createPhysicalConnection通过try catch去创建物理连接,若有异常则会通过JdbcUtils.close(conn)去关闭连接

testConnectionInternal

    protected boolean testConnectionInternal(DruidConnectionHolder holder, Connection conn) {String sqlFile = JdbcSqlStat.getContextSqlFile();String sqlName = JdbcSqlStat.getContextSqlName();if (sqlFile != null) {JdbcSqlStat.setContextSqlFile(null);}if (sqlName != null) {JdbcSqlStat.setContextSqlName(null);}try {if (validConnectionChecker != null) {boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);long currentTimeMillis = System.currentTimeMillis();if (holder != null) {holder.lastValidTimeMillis = currentTimeMillis;holder.lastExecTimeMillis = currentTimeMillis;}if (valid && isMySql) { // unexcepted branchlong lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);if (lastPacketReceivedTimeMs > 0) {long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;if (lastPacketReceivedTimeMs > 0 //&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {discardConnection(holder);String errorMsg = "discard long time none received connection. "+ ", jdbcUrl : " + jdbcUrl+ ", version : " + VERSION.getVersionNumber()+ ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;LOG.warn(errorMsg);return false;}}}if (valid && onFatalError) {lock.lock();try {if (onFatalError) {onFatalError = false;}} finally {lock.unlock();}}return valid;}if (conn.isClosed()) {return false;}if (null == validationQuery) {return true;}Statement stmt = null;ResultSet rset = null;try {stmt = conn.createStatement();if (getValidationQueryTimeout() > 0) {stmt.setQueryTimeout(validationQueryTimeout);}rset = stmt.executeQuery(validationQuery);if (!rset.next()) {return false;}} finally {JdbcUtils.close(rset);JdbcUtils.close(stmt);}if (onFatalError) {lock.lock();try {if (onFatalError) {onFatalError = false;}} finally {lock.unlock();}}return true;} catch (Throwable ex) {// skipreturn false;} finally {if (sqlFile != null) {JdbcSqlStat.setContextSqlFile(sqlFile);}if (sqlName != null) {JdbcSqlStat.setContextSqlName(sqlName);}}}

testConnectionInternal主要通过validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout)来校验连接,如果validConnectionChecker为null则通过jdbc执行validationQuery进行校验

discardConnection

    public void discardConnection(DruidConnectionHolder holder) {if (holder == null) {return;}Connection conn = holder.getConnection();if (conn != null) {JdbcUtils.close(conn);}lock.lock();try {if (holder.discard) {return;}if (holder.active) {activeCount--;holder.active = false;}discardCount++;holder.discard = true;if (activeCount <= minIdle) {emptySignal();}} finally {lock.unlock();}}

discardConnection方法主要是关闭connection,之后枷锁处理一些统计标记

小结

DruidDataSource的getConnection方法内部调用的是getConnectionDirect(maxWaitMillis)

getConnectionDirect在一个for循环里头进行获取连接,首先执行getConnectionInternal(maxWaitMillis),若出现GetConnectionTimeoutException异常,则在notFull且notFullTimeoutRetryCnt小于等于this.notFullTimeoutRetryCount时会递增notFullTimeoutRetryCnt,然后continue继续循环,否则直接抛出GetConnectionTimeoutException跳出循环

获取到连接之后,判断是否是testOnBorrow,如果是则执行testConnectionInternal,若校验不成功则执行discardConnection,然后继续循环;若非testOnBorrow则判断conn是否closed,若是则执行discardConnection,然后继续循环,若非closed则进入testWhileIdle的逻辑

最后是removeAbandoned,维护connectedTimeNano,将当前连接放到activeConnections中

整体代码看下来感觉跟commons-pool相比,druid代码的实现感觉有点粗糙,抽象层级不够高,代码充斥大量统计标记、状态位的处理,维护起来得很小心,另外druid直接在getConnection的时候执行testWhileIdle有点令人匪夷所思

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

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

相关文章

UniAccess Agent卸载

异常场景&#xff1a; UniAccess Agent导致系统中的好多设置打不开 例如:ipv4的协议,注册表,host等等 需要进行删除,亲测有效,及多家答案平凑的 借鉴了这位大神及他里面引用的大神的内容 https://blog.csdn.net/weixin_44476410/article/details/121605455 问题描述 这个进…

Redis 字符串操作实战(全)

目录 SET 存入键值对 SETNX SETEX SETBIT SETRANGE MSET 批量存入键值对 MSETNX PSETEX BITCOUNT 计算值中1的数量 BITOP 与或非异或操作 DECR 减1 DECRBY APPEND 追加 INCR 自增 INCRBY INCRBYFLOAT GET 取值 GETBIT GETRANGE GETSET 取旧值赋新值 MGET …

权限提升Linux篇

提权工具 https://github.com/liamg/traitor https://github.com/AlessandroZ/BeRoot https://github.com/rebootuser/LinEnum https://github.com/mzet-/linux-exploit-suggester https://github.com/sleventyeleven/linuxprivchecker https://github.com/jondonas/linux…

Git学习笔记4

GitHub是目前最火的开源项目代码托管平台。它是基于web的Git仓库&#xff0c;提供公有仓库和私有仓库&#xff0c;但私有仓库是需要付费的。 到Github上找类似的项目软件。 GitLab可以创建免费的私有仓库。 GitLab是利用 Ruby开发的一个开源的版本管理系统&#xff0c;实现一个…

如何使用Docker安装最新版本的Redis并设置远程访问(含免费可视化工具)

文章目录 安装Docker安装Redisredis.conf文件远程访问Redis免费可视化工具相关链接Docker是一种开源的应用容器引擎,使用Docker可以让我们快速部署应用环境,本文介绍如何使用Docker安装最新版本的Redis。 安装Docker 首先需要安装Docker,具体的安装方法可以参考Docker官方文…

使用 rtty 进行远程 Linux 维护和调试

rtty 是一个用于在终端上进行远程连接和数据传输的工具。它提供了一种简单的方式来与远程设备进行通信&#xff0c;使得在不同主机之间传输数据变得更加方便。 安装 rtty 是一个可执行程序&#xff0c;可以在 Linux、macOS 和 Windows 等平台上使用。 Linux/macOS 在终端中执…

开发者必备!如何将闲置iPad Pro打造为编程工具,使用VS Code编写代码

文章目录 前言1. 本地环境配置2. 内网穿透2.1 安装cpolar内网穿透(支持一键自动安装脚本)2.2 创建HTTP隧道 3. 测试远程访问4. 配置固定二级子域名4.1 保留二级子域名4.2 配置二级子域名 5. 测试使用固定二级子域名远程访问6. ipad pro通过软件远程vscode6.1 创建TCP隧道 7. ip…

Java-day17(反射)

Reflection(反射) 动态语言的关键 允许程序在执行期借助于Reflection API取得任何类的内部信息&#xff0c;并能直接操作任意对象的内部属性及方法提供的功能: 在运行时判断任意一个对象所属类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的成员变量和方法 在…

怒刷LeetCode的第15天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;哈希表双向链表 方法二&#xff1a;TreeMap 方法三&#xff1a;双哈希表 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;二分查找 方法二&#xff1a;线性搜索 方法三&#xff1a;Arrays类的b…

基于SpringBoot的在线文档管理系统

目录 前言 一、技术栈 二、系统功能介绍 管理员功能模块 员工功能模块 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势…

90行代码写一个视频播放器

上一篇文章视频播放器的技术组成写了视频播放器的整体结构 下面我们就来上伪代码&#xff1a; #include <iostream> #include <thread> #include <list> #include <string> using namespace std;//下文代码都需要考虑并发&#xff0c;并发代码没写是…

ArcGIS 10.3软件安装包下载及安装教程!

【软件名称】&#xff1a;ArcGIS 10.3 【安装环境】&#xff1a;Windows 【下载链接 】&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1K5ab7IHMYa23HpmuPkFa1A 提取码&#xff1a;oxbb 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 软件解压码点击原文…

java面试题-jvm基础知识

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&a…

代码随想录算法训练营day6| 哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和

目录 一、哈希表理论 hash function ​编辑hash collision 常见哈希结构 1&#xff09;set 2&#xff09;map 二、&#xff08;leetcode 242&#xff09;有效的字母异位词 三、&#xff08;leetcode 349&#xff09;两个数组的交集 四、&#xff08;leetcode 202&…

JAVA学习-全网最详细

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

Linux学习第19天:Linux并发与竞争实例: 没有规矩不成方圆

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 先说点题外话&#xff0c;上周参加行业年会&#xff0c;停更了一周。接下来的周五就要开启国庆中秋双节模式&#xff0c;所以有的时候&#xff0c;尤其是工作以后…

设计模式——备忘录模式

备忘录模式 备忘录模式是什么&#xff1f;备忘录模式解决什么问题&#xff1f;备忘录模式实现 备忘录模式是什么&#xff1f; 在不破坏封装性的前提下&#xff0c;捕获一个对的的内部状态&#xff0c;并在该对象之外保持这个状态。这样以后就可将该对象恢复到原先保持的状态 …

java面试题-设计模式基础

面试专题-设计模式 前言 在平时的开发中&#xff0c;涉及到设计模式的有两块内容&#xff0c;第一个是我们平时使用的框架&#xff08;比如spring、mybatis等&#xff09;&#xff0c;第二个是我们自己开发业务使用的设计模式。 面试官一般比较关心的是你在开发过程中&#…

游戏录屏软件推荐,教你录制高清游戏视频

“有没有好用的游戏录屏软件推荐呀&#xff0c;最近当上了游戏主播&#xff0c;平台要求每天都要发一个游戏视频&#xff0c;可是我的游戏录屏软件太拉胯了&#xff0c;录制出来的视频非常糊&#xff0c;导致平台审核不通过&#xff0c;所以想问问大家有没有游戏录屏软件推荐一…

SQL server 创建存储过程

SQL Server如何创建存储过程 存储过程&#xff1a; 可以理解为完成特定功能的一组 SQL 语句集&#xff0c;存储在数据库中&#xff0c;经过第一次编译&#xff0c;之后的运行不需要再次编译&#xff0c;用户通过指定存储过程的名字并给出参数&#xff08;如果该存储过程带有参数…