Mysql Connector/J 源码分析(Failover)

2023-10-27

文章目录


前言

本文讨论Connector/J 的failover模块。本文将以两个区分(区分构造阶段和使用阶段;区分通讯异常和数据异常)来分析failover的使用价值。

本次分析的版本为5.1.46。若通过maven下载,可添加以下依赖:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>

 我们获取连接的例子如下:

    Connection conn = null;
    URL =“jdbc:mysql://ip1:port1,ip2:port2,ip3:port3/dbname”;
    try{
        // 注册 JDBC 驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 打开链接
        conn = DriverManager.getConnection(DB_URL,USER,PASS);
    ....

 


一、什么是Failover?

官方用两段文字对Failover进行描述:

MySQL Connector/J supports server failover. A failover happens when connection-related errors occur for an underlying, active connection. The connection errors are, by default, propagated to the client, which has to handle them by, for example, recreating the working objects (Statement, ResultSet, etc.) and restarting the processes. Sometimes, the driver might eventually fall back to the original host automatically before the client application continues to run, in which case the host switch is transparent and the client application will not even notice it.

A connection using failover support works just like a standard connection: the client does not experience any disruptions in the failover process. This means the client can rely on the same connection instance even if two successive statements might be executed on two different physical hosts. However, this does not mean the client does not have to deal with the exception that triggered the server switch.

大意是说,通常情况下连接发生异常,调用方就得放充当前连接,获取新连接,重新一遍之前的操作。而有了failover特性后,它会在底层的连接出状况时候产生作用,它不会让调用方当前的操作崩掉而重新再来。在failover的作用下,调用方只需保持着一个连接,尽管前后两条SQL命令有可能在两台不同的设备上执行。

通过官方的描述,看起来调用方很轻松,拿看一个连接就可走天下了。但细想一下,如果当前的操作是带事务的,前后两条命令在不同设备上执行,事务还会生效?官方的最后一句是说有了failover并不意味着的调用方可以高枕无忧,那么到底调用方在什么情况下需要做什么处理呢?后续章节将一一解疑。


二、Failover的主要结构

 

我们以UML类图展示其内部结构:

主要组件功能如下:

 - MysqlIO:负责与Mysql服务器建立tcp链接。
- ConnectionImpl、JDBC4Connection:通过MysqlIO控制与Mysql服务器间的连接,实现数据操作的接口方法。并设定和记录各种连接时间。
- MultiHostMySQLConnection、JDBC4MultiHostMySQLConnection:它们之间是继承关系。通过代理对象FailoverConnectionProxy获取JDBC4Connection对象,并调用其对应的接口方法(从JDBC4MySQLConnection到java.sql.Connection声明的方法)。
- MultiHostConnectionProxy:作为各种动态类的父类,实现了各种动态类的公共方法,最常见的就是返回当前连接对象给到MultiHostMySQLConnection及其子类。它还是InvocationHandler接口的直接实现类,它重载了invoke方法,并声明了由其子类实现的虚方法invokeMore。invoke方法的实现使用了模板方法这种设计模式。通过invoke方法和子类的invokeMore方法,一起实现了代理模式,即在被代理方法执行之前和之后都添加了一些行为。
- FailoverConnectionProxy作为MultiHostConnectionProxy的子类重载了invokeMore方法。当发生异常时,在被代理方法执行前或者后,通过failover方法更换当前连接对象。


三、异常处理

从官网的描述可以理解到,failover就是要解决出现底层连接异常时造成调用方当前操作崩掉的问题。我们从连接的构造阶段和连接的使用阶段进行探讨,并且每个阶段我们都尝试分析通讯异常和数据异常发生后分别会有什么不同的处理。


3.1 构造连接阶段

从调用者发起DriverManager.getConnection命令开始到FailoverConnectionProxy#createProxyInstance方法,虽然期间所调用的命令会抛出异常,然后都已经及时处理,没有继续抛向调用者。这条调用链如下:

接下来我们看看FailoverConnectionProxy#createProxyInstance方法:

public static Connection createProxyInstance(List<String> hosts, Properties props) throws SQLException {
        FailoverConnectionProxy connProxy = new FailoverConnectionProxy(hosts, props);

        return (Connection) java.lang.reflect.Proxy.newProxyInstance(Connection.class.getClassLoader(), INTERFACES_TO_PROXY, connProxy);
    }

方法里只有两条命令,第一条命令构造FailoverConnectionProxy对象。根据方法声明,我们知道它会抛出SQLException。第二条命令构造动态代理,它有可能抛出IllegalArgumentException,这异常是RuntimeException的子类。因此我们只需要着重观察FailoverConnectionProxy对象的构造过程。

@Override
    synchronized void pickNewConnection() throws SQLException {
        if (this.isClosed && this.closedExplicitly) {
            return;
        }

        if (!isConnected() || readyToFallBackToPrimaryHost()) {
            try {
                connectTo(this.primaryHostIndex);
            } catch (SQLException e) {
                resetAutoFallBackCounters();
                failOver(this.primaryHostIndex);
            }
        } else {
            failOver();
        }
    }

从代码的结构以及方法的命名可大概猜出此方法要做的事情。因此当前处于构造阶段,所以肯定满足!isConnected()方法的判定。接下来就会尝试连接url里第一组的ip:port ,如果期间出现异常(在《Mysql Connector/J 源码分析(普通Connection)》一文里已经知道,通讯异常和数据异常都会统一被封装成SQLException),该异常将会被捕获,然后执行failOver方法。

private synchronized void failOver(int failedHostIdx) throws SQLException {
        
    ....

        do {
            try {
                firstConnOrPassedByPrimaryHost = firstConnOrPassedByPrimaryHost || isPrimaryHostIndex(nextHostIndex);

                connectTo(nextHostIndex);

                if (firstConnOrPassedByPrimaryHost && connectedToSecondaryHost()) {
                    resetAutoFallBackCounters();
                }
                gotConnection = true;

            } catch (SQLException e) {
                lastExceptionCaught = e;

                if (shouldExceptionTriggerConnectionSwitch(e)) {
                    
                    ....

                    nextHostIndex = newNextHostIndex;

                } else {
                    throw e;
                }
            }
        } while (attempts < this.retriesAllDown && !gotConnection);

        if (!gotConnection) {
            throw lastExceptionCaught;
        }
    }

方法里有一个大的do while循环体。循环条件是遍数未够并且还没建立底层连接。离开循环体后,如果还没建立底层连接就抛出最近一次的异常。

在循环体里有捕获异常的行为,然后通过shouldExceptionTriggerConnectionSwitch判断异常的性质,如果它不符合判定就直接抛出异常。我们继续看看shouldExceptionTriggerConnectionSwitch是如何判定异常的:

@Override
    boolean shouldExceptionTriggerConnectionSwitch(Throwable t) {
        if (!(t instanceof SQLException)) {
            return false;
        }

        String sqlState = ((SQLException) t).getSQLState();
        if (sqlState != null) {
            if (sqlState.startsWith("08")) {
                // connection error
                return true;
            }
        }

        // Always handle CommunicationsException
        if (t instanceof CommunicationsException) {
            return true;
        }

        return false;
    }

从代码里不难看出,通讯类异常符合方法的判定。那么我们可以判断出failOver方法抛出异常的场景:

  • 非通讯类异常,直接抛出异常
  • 通讯类异常,轮循的遍数已足够并且仍未建立连接情况下将被抛出。

小结

因此,我们知道在构造动态代理连接的时候,有可能发生数据异常和通讯异常。数据异常能够比较早地被调用者感知到,而通讯异常不一定会被调用者感知到;如果调用者感知到通讯异常时,说明url里配置的ip:port都未能够成功建立连接。总的来说,调用者能够感知到异常。

一个有趣的发现:如果我们设置错误的用户名与密码,通过调用链我们可知道,程序来到pickNewConnection的时候,会调用connectTo方法尝试建立连接。因为用户名与密码是错误的,所以底层会抛出SQLException。该异常被捕获后进入failover方法,然后再次connectTo方法,这次抛出来的异常才直接抛向调用者。所以这种情况会尝试两次建立连接。在在《Mysql Connector/J 源码分析(普通Connection)》中我们知道每次建立连接都耗时耗资源,所以在pickNewConnection方法里如果判断到异常属于数据异常时就将异常往调用者抛,这样可能会更好。

 


3.2 使用连接阶段

failover使用了动态代理技术,即执行任何一条指定接口的方法都会进入InvocationHandler接口实现类的invoke方法。在此处FailoverConnectionProxy的父类MultiHostConnectionProxy实现该接口并重载invoke方法,而方法体内最终会调用FailoverConnectionProxy的invokeMore方法:

@Override
    public synchronized Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        ....

        Object result = null;

        try {
            result = method.invoke(this.thisAsConnection, args);
            result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result);
        } catch (InvocationTargetException e) {
            dealWithInvocationException(e);
        }

        ....

        return result;
    }

(1)如果执行conn.setAutoCommit(false)命令时,出现了通讯异常或者数据异常,反射机制会抛出InvocationTargetException,在此处会进入MultiHostConnectionProxy#dealWithInvocationException方法:

void dealWithInvocationException(InvocationTargetException e) throws SQLException, Throwable, InvocationTargetException {
        Throwable t = e.getTargetException();

        if (t != null) {
            if (this.lastExceptionDealtWith != t && shouldExceptionTriggerConnectionSwitch(t)) {
                invalidateCurrentConnection();
                pickNewConnection();
                this.lastExceptionDealtWith = t;
            }
            throw t;
        }
        throw e;
    }

在该方法里我们又看到了shouldExceptionTriggerConnectionSwitch方法的调用。如果异常源于通讯异常,就会调用pickNewConnection方法,随后进入failover方法;如果异常源于数据异常,则直接抛出。这些方法前文已经介绍过,这里需要补充的是,如果此时failover抛出异常,那么invokeMore方法没有进一步捕获异常的操作,而父类MultiHostConnectionProxy#invoke方法里虽有捕获,但它做的事情是要不要对异常进行封装,最后依然是继续往上抛,所以异常会直接到调用方那:

public synchronized Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        ....

        try {
            return invokeMore(proxy, method, args);
        } catch (InvocationTargetException e) {
            throw e.getCause() != null ? e.getCause() : e;
        } catch (Exception e) {
            // Check if the captured exception must be wrapped by an unchecked exception.
            Class<?>[] declaredException = method.getExceptionTypes();
            for (Class<?> declEx : declaredException) {
                if (declEx.isAssignableFrom(e.getClass())) {
                    throw e;
                }
            }
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

(2)如果执行conn.createStatement(),会进入proxyIfReturnTypeIsJdbcInterface方法。该方法对于类路径以java.sql或者javax.sql为前缀的类使用FailoverJdbcInterfaceProxy(它是FailoverConnectionProxy的内部类)进行封装然后生成动态代理。所以,Statement的实现类就会被动态代理。FailoverJdbcInterfaceProxy的父类是JdbcInterfaceProxy(它是MulitHostConnectionProxy的内部类),JdbcInterfaceProxy实现了InvocationHandler接口和重载了invoke方法。使用JdbcInterfaceProxy进行封装有一种使用适匹器的味道,读者自行体会吧。但最终的目的就是如果这些类的实例在运行时产生异常也能够进入MulitHostConnectionProxy#dealWithInvocationException的方法进行判定,最终抛出异常。


小结

在使用过程中,调用者能够感知到异常的发生。而保证这一点的核心是MultiHostConnectionProxy#dealWithInvocationException方法,它一定会将异常往抛。通讯异常与数据异常唯一的差别在于,通讯异常会在更换底层连接后再抛,而数据异常直接抛出异常。


 

四、四元素判定

在调用Method#invoke前,有可能会进行一次更换底层连接。然而,这需要满足4个元素。

@Override
    public synchronized Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        ....

        if (this.isClosed && !allowedOnClosedConnection(method)) {
            if (this.autoReconnect && !this.closedExplicitly) {
                this.currentHostIndex = NO_CONNECTION_INDEX; // Act as if this is the first connection but let it sync with the previous one.
                pickNewConnection();
                this.isClosed = false;
                this.closedReason = null;
            } else {
                String reason = "No operations allowed after connection closed.";
                if (this.closedReason != null) {
                    reason += ("  " + this.closedReason);
                }
                throw SQLError.createSQLException(reason, SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null /* no access to a interceptor here... */);
            }
        }

        Object result = null;

        try {
            result = method.invoke(this.thisAsConnection, args);
            result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result);
        } catch (InvocationTargetException e) {
            dealWithInvocationException(e);
        }

        ....

        return result;
    }

 

这四个元素摘录如下:

  • this.isClose为真
  • !allowedOnClosedConnection(method)为真
  • this.autoReconnect为真
  • !this.closedExplicitly为真

对于isClose属性,我们观察它被设置为true的地方是在MultiHostConnectionProxy#invoke方法里。

public synchronized Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        ....

        if (METHOD_CLOSE.equals(methodName)) {
            doClose();
            this.isClosed = true;
            this.closedReason = "Connection explicitly closed.";
            this.closedExplicitly = true;
            return null;
        }

        if (METHOD_ABORT_INTERNAL.equals(methodName)) {
            doAbortInternal();
            this.currentConnection.abortInternal();
            this.isClosed = true;
            this.closedReason = "Connection explicitly closed.";
            return null;
        }

        if (METHOD_ABORT.equals(methodName) && args.length == 1) {
            doAbort((Executor) args[0]);
            this.isClosed = true;
            this.closedReason = "Connection explicitly closed.";
            return null;
        }

        ....
    }

调用方调用指定的方法时,isClosed属性就会被设置为true。对于被调用的方法整理如下:

常量 常量值 声明方法的接口
METHOD_CLOSE close java.sql.Connection
METHOD_ABORT_INTERNAL abortInternal com.mysql.jdbc.Connection
METHOD_ABORT abort java.sql.Connection

我们可以看到,这些方法被调用后,不仅isClose属性被设置为true,而且直接返回null。所以这里暗示出,在执行了这行方法之后,再调用其他的Connection接口方法时才进行前面四个元素的判定。比如:先执行conn.close()命令,然后再执行conn.createStatement()命令。


对于!allowedOnClosedConnection(method)为真的判断条件,其实是只要调用方不是调用连接以下方法就符合。现对于被调用的方法整理如下:

常量 常量值 声明方法的接口
METHOD_GET_AUTO_COMMIT getAutoCommit java.sql.Connection
METHOD_GET_CATALOG getCatalog java.sql.Connection
METHOD_GET_TRANSACTION getTransactionIsolation java.sql.Connection
METHOD_GET_SESSION_MAX_ROWS getSessionMaxRows com.mysql.jdbc.Connection

这些命令返回的数据已经在当前连接建立的时候,从Mysql获取到了,所以不需要实时地连接到Mysql获取。


对于autoReconnect,默认值为false。用户可以在url上添加autoReconnect=true或者autoReconnectForPools=true选项来更新。


对于!this.closedExplicitly,我们首先观察closedExplicitly设置为ture的地方。对于被调用的方法整理如下:

常量 常量值 声明方法的接口
METHOD_CLOSE close java.sql.Connection

所以,只要调用方调用的不是连接的close方法,即可满足。


小结

综上所述,只要调用方调用连接的abortInternal或者abort方法,并且在url上添加autoReconnect=true或者autoReconnectForPools=true选项,然后再调用比如createStatement方法  就会执行pinkNewConnection方法,更换连接。也就是说,此处提供给调用方主动提出更换连接的机会。


五、专有选项

failover模式有几个专有的选项,可添加到url,下面将从代码的角度来介绍它们的作用:

  • retriesAllDown

如果当前的连接出现通讯异常会进入failover方法,它的作用是控制url里的ip:port组集合的被轮循的遍数。理论上整个ip:port组集合每个组合都试过一次算1次。但是第一组ip:port作为主服务器,是否尝试建立连接,还取决于secondsBeforeRetryMaster或者queriesBeforeRetryMaster这两个选项值。

  • secondsBeforeRetryMaster和queriesBeforeRetryMaster

secondsBeforeRetryMaster是表示第一组ip:port对应的主服务器连不上后,要等待的毫秒数。queriesBeforeRetryMaster表示表示第一组ip:port对应的主服务器连不上后,隔了多少次SQL操作后才再尝试连接主服务器。

synchronized boolean readyToFallBackToPrimaryHost() {
        return this.enableFallBackToPrimaryHost && connectedToSecondaryHost() && (secondsBeforeRetryPrimaryHostIsMet() || queriesBeforeRetryPrimaryHostIsMet());
    }

//判断距离主服务器断开连接的时间长度
private synchronized boolean secondsBeforeRetryPrimaryHostIsMet() {
        return this.secondsBeforeRetryPrimaryHost > 0 && Util.secondsSinceMillis(this.primaryHostFailTimeMillis) >= this.secondsBeforeRetryPrimaryHost;
    }

//判断距离主服务器断开连接后执行的操作次数是否足够
private synchronized boolean queriesBeforeRetryPrimaryHostIsMet() {
        return this.queriesBeforeRetryPrimaryHost > 0 && this.queriesIssuedSinceFailover >= this.queriesBeforeRetryPrimaryHost;
    }

在FailoverJdbcInterfaceProxy#invoke方法里对执行execute为前缀的方法进行queriesIssuedSinceFailover属性累加。根据上面代码块,它被用于与queriesBeforeRetryPrimaryHost进行比较:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();

            boolean isExecute = methodName.startsWith("execute");

            if (FailoverConnectionProxy.this.connectedToSecondaryHost() && isExecute) {
                FailoverConnectionProxy.this.incrementQueriesIssuedSinceFailover();
            }

            Object result = super.invoke(proxy, method, args);

    ....
}


//FailoverConnectionProxy#incrementQueriesIssuedSinceFailover有个累加动作
synchronized void incrementQueriesIssuedSinceFailover() {
        this.queriesIssuedSinceFailover++;
    }

 

  • autoReconnect和autoReconnectForPools

当用户在url配置这两个选项后,在程序里会变成MultiHostConnectionProxy#autoReconnect属性值。该值在前文的“四元素判定”一节已经介绍了。

 

  • failOverReadOnly

官网的描述如下:

Sequence A, with failOverReadOnly=true:

  1. Connects to primary host in read/write mode

  2. Sets Connection.setReadOnly(true); primary host now in read-only mode

  3. Failover event; connects to secondary host in read-only mode

  4. Sets Connection.setReadOnly(false); secondary host remains in read-only mode

  5. Falls back to primary host; connection now in read/write mode

Sequence B, with failOverReadOnly=false

  1. Connects to primary host in read/write mode

  2. Sets Connection.setReadOnly(true); primary host now in read-only mode

  3. Failover event; connects to secondary host in read-only mode

  4. Set Connection.setReadOnly(false); connection to secondary host switches to read/write mode

  5. Falls back to primary host; connection now in read/write mode

核心意思就是url的第一对ip:port(主服务器的Mysql)默认具有读写能力,而其他服务器的Mysql只能是读操作。当在url添加failOverReadOnly=false选项,在主服务器连接异常而使用别的服务器的连接时,如果调用方执行 Connection.setReadOnly(false),后续就可以更新数据了。

代码上,在更换连接前,有一个计算readOnly的过程。该过程就是体现了官网对readOnly的描述。

private synchronized void switchCurrentConnectionTo(int hostIndex, MySQLConnection connection) throws SQLException {
        invalidateCurrentConnection();

        boolean readOnly;
        if (isPrimaryHostIndex(hostIndex)) {
            readOnly = this.explicitlyReadOnly == null ? false : this.explicitlyReadOnly;
        } else if (this.failoverReadOnly) {
            readOnly = true;
        } else if (this.explicitlyReadOnly != null) {
            readOnly = this.explicitlyReadOnly;
        } else if (this.currentConnection != null) {
            readOnly = this.currentConnection.isReadOnly();
        } else {
            readOnly = false;
        }
        syncSessionState(this.currentConnection, connection, readOnly);
        this.currentConnection = connection;
        this.currentHostIndex = hostIndex;
    }

当从主服务器切换到从服务器时,计算的元素为explicitlyReadOnly属性值。它的类型是Boolean,该属性需要显示赋值。该值可通过动态代理连接的setReadOnly方法设置。切换到从服务器时,以之前通过setReadOnly设置的值为准。

从服务器切换到其他服务器是主要的观察场景。failoverReadOnly的类型是boolean,即默认为false值。如果使用默认值,就以动态代理连接的setReadOnly方法的参数为准。

从实用性考虑,我们当然希望主服务器具有的读、写能力在切换到任何一台从服务器上也具有同样的能力。为了保证下一次切换连接时具有当前连接的读、写能力,调用者需要在感知切换了新的底层连接时就要调用setReadOnly(false),更直接的处理就是在捕获SQLException后判断异常类型,如果为通讯类异常就调用setReadOnly(false)方法。但这样无形中给开发人员添加精神压力和增加工作量。


六、官网的态度

对于自动更换连接,官方的态度是谨慎的:

Seamless Reconnection

Although not recommended, you can make the driver perform failovers without invalidating the active Statement or ResultSet instances by setting either the parameter autoReconnect or autoReconnectForPools to true. This allows the client to continue using the same object instances after a failover event, without taking any exceptional measures. This, however, may lead to unexpected results: for example, if the driver is connected to the primary host with read/write access mode and it fails-over to a secondary host in real-only mode, further attempts to issue data-changing queries will result in errors, and the client will not be aware of that. This limitation is particularly relevant when using data streaming: after the failover, the ResultSet looks to be alright, but the underlying connection may have changed already, and no backing cursor is available anymore.

在Failover模块下构造的Statement实例,它的connection属性值为JDBC4MultiHostMySQLConnection实例。当Statement实例需要通过connection做点什么事的时候,JDBC4MultiHostMySQLConnection实例就会找到FailoverConnectionProxy实例的currentConnection属性值。当发生failover的时候,该属性值会指向另一个底层连接,而该底层连接有可能是ReadOnly模式,因此官方会有上述的表态。

为避免这种情况的发生,就要确保每个底层的连接都是读、写模式。而为做到这一点,需要做到以下两点:

  • url里添加failoverReadOnly=false选项或者根本就不添加
  • 调用者需要在捕获SQLException后判断异常类型,如果为通讯类异常就调用setReadOnly(false)方法

总结

本文首先分析了Failover模块的结构,并通过两个“区分”来分析该模式的异常处理。当发生了异常后,调用者都能够感知得到。在动态代理连接使用过程中属性的通讯异常,它会尝试更换底层连接,然后再说将异常往上抛。所以调用者捕获异常后,下次再使用动态代理连接时,底层的连接可能已经是另外一个连接了。基于调用者能够感知到异常的发生,该模式具有一定的使用价值。

随后我们也了解到发生failover后读、写模式可能会发生变化,并且试图提供解决方案。为确保每个底层连接都具有读和写的能力,开发人员必须有专门的代理处理。因此,如果项目从使用单台Myql数据库升级为使用多台Mysql数据库,开发人员需要修改代码。那么,是否存在一个模式,为开发人员带来更方便的升级体验呢?请关注另一篇文章《Mysql Connector/J 源码分析(LoadBalance)》。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mysql Connector/J 源码分析(Failover) 的相关文章

  • 初步了解爆破(附Burp suite)

    huihui爆破通常是网站管理 运营人员由于安全意识不足 为了方便 避免忘记密码等 使用了非常容易记住的密码 或者是直接采用了系统的默认密码等 攻击者利用此漏洞可直接进入应用系统或者管理系统 从而进行系统 网页 数据的篡改与删除 非法获取系
  • 第十五章 Scala进阶——隐式转换与隐式参数

    考虑如下场景 假设编写了一个向量类MyVector 并且包含了一些向量的基本操作 因为向量可以与标量做数乘运算 所以需要一个计算数乘的方法 它应该接收一个类型为基本值类的参数 在向量对象myVec调用该方法时 可以写成诸如 myVec 2
  • python中time clock方法不可用的解决技巧

    Python time clock 函数以浮点数计算的秒数返回当前的CPU时间 用来衡量不同程序的耗时 比time time 更有用 这个需要注意 在不同的系统上含义不同 在UNIX系统上 它返回的是 进程时间 它是用秒表示的浮点数 时间戳
  • QT自定义信号与槽发生重载解决办法

    信号与槽作为qt中的核心机制 在qt应用开发中经常会用的 但是原生的信号与槽连接传参 只支持基本的数据类型 比如char int float double 如果想要在信号与槽之间传递自定义参数 比如结构体 类等 就必须特别注意 因为在编译的
  • shell中的常用通配符,字符类

    通配符是一种特殊语句 主要有星号 和问号 用来模糊搜索文件 当查找文件夹时 可以使用它来代替一个或多个真正字符 当不知道真正字符或者懒得输入完整名字时 常常使用通配符代替一个或多个真正的字符 shell 提供了特殊字符来帮助你快速指定一组文
  • Python TKinter 天气时钟

    用Python的tk做了一个可以用的小天气时钟软件 分享给大家啦 pageFrame from tkinter import import math time import threading as thd from getWeather
  • 免费智能机器人聊天API

    道翰天琼的免费智能机器人聊天API 认知智能CI机器人是杭州道翰天琼智能科技有限公司旗下产品 认知智能机器人是依托道翰天琼10年研发的认知智能CI体系为核心而打造的认知智能机器人大脑 是全球第一个认知智能机器人大脑 具有突破性 创新性 领航
  • 关于在VS+QtTools环境下使用QList<自定义类型>调试存在乱码问题的研究【记录】

    文章目录 前言 同事的解决方案 当结构体作用域改变时 这时我更加迷惑了但是我发现一个新的点 总结目前发现的俩种解决方案 当我不调试执行运行它它它它有又变了 初步个人结论 Qt测试结果 总结 前言 这个问题是在同事在从数据库读取数据时才发现的
  • Javaweb登录功能优化及注销登录

    登录功能优化 上篇登录功能代码在从数据库取出用户信息的代码中少写了取出用户姓名 现在将其加上user setName rs getString u name 在登录成功后右上角会有一个xx用户 我们希望把用户名字显示在xx上 所以对其adm

随机推荐

  • idea2021搭建SpringMVC项目

    打开IDEA 点击file gt close Project 点击完了之后跳转到idea的首页 点击New Project 左侧选择maven 在右侧勾选Create from archetype 并且选择结尾带webapp的选项的类型 然
  • Elasticsearch安装ik分词插件

    前置条件 如果发现问题请留言 如果有发现不正确的地方 欢迎留言指正 感激不尽 已安装好Elasticsearch 本次安装插件版本为7 3 1 需与elasticsearch版本一致 elasticsearch安装在 home elk el
  • Fluid主题出错解决方案

    Fluid主题排版出错 重装一次主题解决 一 存在问题 大半个月没有浏览Hexo博客 再次浏览的时候发现网页的排版布局竟然出错了 PS 导航栏排版出错 PS 内容板块的宽度失效 PS footer部分排版也出错了 打开 开发者模式 查看一下
  • k8s远程debug

    k8s远程debug 1 方案1 方案1是不行的 因为k8s的ingress走的7层协议 1 1 应用 启动debug端口 java agentlib jdwp transport dt socket server y suspend n
  • PCL点云处理之添加高斯噪点的两种方法(详细注释版)(一百八十一)

    PCL点云处理之添加高斯噪点的两种方法 详细注释版 一百八十一 一 实验效果 二 算法简介 三 具体流程 四 PCL自带函数实现 1 代码 2 结果 五 Boost函数实现 1 代码 2 结果 总结 一 实验效果 通过实验测试 效果如上所示
  • java usb camera_android4.0 USB Camera实例(一)HAL层

    一直想自己写一个从HAL层到应用层的Camera例子 android4 0上usb camera用不了 所以决定自己写一个 usb camera和coms原理都是一样的 基本v4l2 只不过源码数据格式不一样而已 下面我们就从HAL层开始
  • IDEA在包下右键new没有Servlet选项?解决办法

    在包下右键没有new一个Servlet 1 首先检查pom xml文件中Tomcat和Servlet的坐标有没有导入 Tomcat坐标
  • Ubuntu安装psycopg2小记

    作者 Wally Yu 在windows上和Linux上安装psycopg2都遇到了点小插曲 记录如下 Windows下 1 前往官网下载源代码 http www initd org psycopg 2 解压 3 运行python setu
  • Docker-容器

    Docker的应用场景 Web 应用的自动化打包和发布 自动化测试和持续集成 发布 在服务型环境中部署和调整数据库或其他的后台应用 Docker的架构 Docker 镜像 Images Docker 镜像是用于创建 Docker 容器的模板
  • taro 支付宝/微信小程序的chooseImage真机和开发工具上的区别

    支付宝小程序 微信小程序
  • 创建Win PE启动盘(小白都会装系统)

    第一步 下载启动盘制作软件 打开搜索引擎 搜索 电脑店 找到下图链接 打开电脑店网站如下图所示 然后点击 完整版下载 开始下载制作PE系统的软件 下载完成后解压到当前目录 如下图 打开后 找到DianNaoDian exe文件双击打开 第二
  • 关于IDEA在创建Maven子模块后的pom.xml文件没有parent标签的解决方法。

    关于IDEA在创建Maven子模块后的pom xml文件没有parent标签的解决方法 问题 我们在创建Maven子模块后的pom xml文件一开始是有parent标签的 然后加载完就直接消失了 解决方法 直接手打上去 具体格式网上都有说怎
  • SQL Server(MMS)开启代理服务器(agent)方法(本篇版本展示界面为SQLserver2014)

    第一步 在SQL Server Management Studio中连接到SQL Server实例后 会显示 SQL Server 代理 节点 如果当前该实例的Agent服务没有启动 SQL Server 代理 后边就会显示 已禁用代理XP
  • libcurl库及curl API的简介

    目录 一 libcurl简介 二 curl API简介 三 库安装编译方法 内容来源 Http协议之libcurl实现 谢呈勖 博客园 cnblogs com 一 libcurl简介 libcurl是一个跨平台的网络协议库 支持http h
  • JAVA Eclipse连接SQL Server 2019并从数据库中读取表中数据

    一 进入SQL Server 配置登录名和密码 这里有默认的sa 一开始是禁用的 选中sa 右键 属性 1 授予and启用 2 设置登录名和密码 点击确定 然后关闭SQL 重新进入的时候身份验证选择SQL Server身份验证 二 新建一个
  • maven 配置多镜像

    1 配置maven的setting xml
  • Activity之任务和返回栈

    一个应用程序中会有多个activity 每个activity一般都有自己独立的功能 我们可以用activity启动自己应用中的另一个activity 例如 从一个数据列表界面 跳转到一个数据详情界面 也可以用我们的activity去打开其他
  • 深入理解javascript对象

    理解对象 对象被定义为一组属性的无序集合 对象就是一组没有特定顺序的值 对象的每个value值都由一个key来标识 一个key映射一个value值 1 Object 创建对象 创建了一个名为 person 的对象 而且有三个属性 name
  • Android adb实现原理

    adb定义 adb Android Debug Bridge 安卓调试桥 包含adb client adb server和adbd三部分 adb client 运行在PC上 即DDMS或者在Windows dos下启动的adb shell
  • Mysql Connector/J 源码分析(Failover)

    文章目录 前言 一 什么是Failover 二 Failover的主要结构 三 异常处理 3 1 构造连接阶段 小结 3 2 使用连接阶段 小结 四 四元素判定 小结 五 专有选择项 六 官网的态度 总结 前言 本文讨论Connector