Analysis of distributed heartbeat detection module of DBLE

Posted by SystemOverload on Wed, 08 Apr 2020 11:24:57 +0200

Author: Lu Lu Those who love technology and are willing to share are mainly engaged in the research of database related technology. Source: original contribution *Aikesheng is produced by the open source community. The original content cannot be used without authorization. Please contact the editor for reprint and indicate the source.

Summary

This paper mainly introduces DBLE heartbeat detection module, including heartbeat detection function and heartbeat detection module source code analysis.

Heartbeat detection function

The functions of DBLE center hop detection are as follows:

1. Control high availability switching of multiple write nodes;

2. To control the load balance of read operation, the read load balance will be controlled according to the latest heartbeat state and master-slave delay (if slaveThreshold is configured);

3. Control the number of idle connections and close redundant idle connections. The PING packet is sent here, which needs to be matched with the dataNodeIdleCheckPeriod parameter. Idle connections exceeding this parameter will be checked by sending the PING packet.

Generally speaking, it is to judge the status of MySQL instance.

In this paper, the first two points are mainly related to heartbeat detection. The third point is more suitable for connection management, which is not involved in this paper.

Source code analysis of heartbeat module

In the scheduler ා init method, the start entry of the heartbeat detection timing task uses the dataNodeHeartbeatPeriod interval to detect the heartbeat periodically. The default value is 10 seconds:

scheduler.scheduleAtFixedRate(dataSourceHeartbeat(), 0L, system.getDataNodeHeartbeatPeriod(), TimeUnit.MILLISECONDS);

The scheduler ා datasourceheartbeat method returns the Runnable task:

private Runnable dataSourceHeartbeat() {
        return new Runnable() {
            @Override
            public void run() {
                timerExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                    //Here's a judgment. If there are no read and write nodes, there is no need for heartbeat detection
                        if (!DbleServer.getInstance().getConfig().isDataHostWithoutWR()) {
                            Map<String, AbstractPhysicalDBPool> hosts = DbleServer.getInstance().getConfig().getDataHosts();
                            for (AbstractPhysicalDBPool host : hosts.values()) {
                            //doHeartbeat() method of AbstractPhysicalDBPool called
                                host.doHeartbeat();
                            }
                        }
                    }
                });
            }
        };
    }

Abstractphysicaldbpool ා doheartbeat is an abstract method. There are two implementations in the classes PhysicalDNPoolSingleWH and PhysicalDBPool. The difference between these two classes can be seen from the name. One is that there is only one WriteHost, and the other has multiple writehosts, which will be initialized according to the specific configuration in your schema.xml.

For heartbeat detection, the basic implementation is the same, so it doesn't affect which class to see.

Let's take a look at the physicaldnpoolsinglewh method

public void doHeartbeat() {
        for (PhysicalDatasource source : allSourceMap.values()) {
            if (source != null) {
                source.doHeartbeat();
            } else {
                LOGGER.warn(hostName + " current dataSource is null!");
            }
        }
    }

The above method is to loop through all data sources, and then detect the heartbeat of each data source.

Continue to see the PhysicalDatasource ᦇ doheartbeat method, and add that PhysicalDatasource is also an abstract class, but in DBLE, there is only one implementation of MySQL datasource, because the DBLE backend only supports mysql, and the MySQL datasource ᦇ doheartbeat method also directly inherits the implementation of the abstract class:

public void doHeartbeat() {
        if (TimeUtil.currentTimeMillis() < heartbeatRecoveryTime) {
            return;
        }
        if (!heartbeat.isStop()) {
            //Here we call the mysqleartbeatාheartbeat method directly
            heartbeat.heartbeat();
        }
    }

Before continuing to look at the mysqleartbeat ා heartbeat method, let's first look at the relationship between the MySQLDatasource and the mysqleartbeat class:

The relationship between them is very simple, that is, MySQL data source will create MySQL search bit, and there is a one-to-one relationship between them.

Simply put, a MySQL datasource object has a MySQL heartbeat object responsible for its heartbeat detection.

Let's take a closer look at mysqleartbeat ා heartbeat method:

public void heartbeat() {
        final ReentrantLock reentrantLock = this.lock;
        reentrantLock.lock();
        try {
            if (isChecking.compareAndSet(false, true)) {
                if (detector == null || detector.isQuit()) {
                    try {
                        detector = new MySQLDetector(this);
                        detector.heartbeat();
                    } catch (Exception e) {
                        LOGGER.info(source.getConfig().toString(), e);
                        setResult(ERROR_STATUS);
                    }
                } else {
                    detector.heartbeat();
                }
            } else {
                if (detector != null) {
                    if (detector.isQuit()) {
                        isChecking.compareAndSet(true, false);
                    } else if (detector.isHeartbeatTimeout()) {
                        setResult(TIMEOUT_STATUS);
                    }
                }
            }
        } finally {
            reentrantLock.unlock();
        }
    }

The above method mainly calls the MySQL detector ා heartbeat method, and the call chain is really deep :

public void heartbeat() {
        if (con == null || con.isClosed()) {
            heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS);
            return;
        }
        //Set the time to send heartbeat detection
        lastSendQryTime = System.currentTimeMillis();


        String[] fetchCols = {};
        if (heartbeat.getSource().getHostConfig().isShowSlaveSql()) {
            fetchCols = MYSQL_SLAVE_STATUS_COLS;
        } else if (heartbeat.getSource().getHostConfig().isShowClusterSql()) {
            fetchCols = MYSQL_CLUSTER_STATUS_COLS;
        } else if (heartbeat.getSource().getHostConfig().isSelectReadOnlySql()) {
            fetchCols = MYSQL_READ_ONLY_COLS;
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("do heartbeat,conn is " + con);
        }
        OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(fetchCols, this);
        sqlJob = new HeartbeatSQLJob(heartbeat.getHeartbeatSQL(), con, resultHandler);
        //Perform heartbeat detection task
        sqlJob.execute();
    }

Simply put, this method will actually detect the backend MySQL status and store the corresponding data according to the heartbeat statement you configured. This involves asynchronous calls. After the detection is completed, it will call back to the MySQL detector ා onresult method:

public void onResult(SQLQueryResult<Map<String, String>> result) {
        //Set the time after heartbeat detection
        lastReceivedQryTime = System.currentTimeMillis();
        heartbeat.getRecorder().set((lastReceivedQryTime - lastSendQryTime));
        if (result.isSuccess()) {
            PhysicalDatasource source = heartbeat.getSource();
            Map<String, String> resultResult = result.getResult();
            if (source.getHostConfig().isShowSlaveSql()) {
                setStatusBySlave(source, resultResult);
            } else if (source.getHostConfig().isShowClusterSql()) {
                setStatusByCluster(resultResult);
            } else if (source.getHostConfig().isSelectReadOnlySql()) {
                setStatusByReadOnly(source, resultResult);
            } else {
                setStatusForNormalHeartbeat(source);
            }
        } else {
            heartbeat.setResult(MySQLHeartbeat.ERROR_STATUS);
        }
    }

The above method is based on the heartbeat detection results to set various variables in the mysqleartbeat class that represent the heartbeat state, such as the status variable and slaveBehindMaster master master master slave delay time variable.

The whole process above completes the heartbeat detection of the data source. The use of the detection results is mainly through the getStatus and getSlaveBehindMaster methods in the mysqleartbeat class. Through these two methods, we can determine whether the heartbeat is successful and how much the master-slave delay is, thus affecting the data source switching and the separation logic of reading and writing, respectively corresponding to the first and second points of heartbeat detection.

summary

This article mainly introduces the heartbeat detection module of DBLE, including the heartbeat detection function and the corresponding source code analysis. I hope this article can help you further understand the heartbeat detection module.

Topics: Database MySQL Load Balance xml