1. 程式人生 > >使用spring實現讀寫分離(mysql主從複製)五:一主多從的實現

使用spring實現讀寫分離(mysql主從複製)五:一主多從的實現

很多實際使用場景下都是採用“一主多從”的架構的,使用輪詢演算法實現目前只需要修改DynamicDataSource即可。

 

1.1. 實現

import java.lang.reflect.Field;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.concurrent.atomic.AtomicInteger;

import javax.sql.DataSource;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import org.springframework.util.ReflectionUtils;

/**

 * 定義動態資料來源,實現通過整合Spring提供的AbstractRoutingDataSource,只需要實現determineCurrentLookupKey方法即可

 *

 * 由於DynamicDataSource是單例的,執行緒不安全的,所以採用ThreadLocal保證執行緒安全,由DynamicDataSourceHolder

完成。

 *

 * @authorzhijun

 *

 */

public class DynamicDataSourceextends AbstractRoutingDataSource {

    private static final LoggerLOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    private IntegerslaveCount;

    // 輪詢計數,初始為-1,AtomicInteger是執行緒安全的

    private AtomicIntegercounter = new

 AtomicInteger(-1);

    // 記錄讀庫的key

    private List<Object>slaveDataSources = new ArrayList<Object>(0);

    @Override

    protected Object determineCurrentLookupKey() {

        //使用DynamicDataSourceHolder保證執行緒安全,並且得到當前執行緒中的資料來源key

        if (DynamicDataSourceHolder.isMaster()) {

            Object key = DynamicDataSourceHolder.getDataSourceKey();

            if (LOGGER.isDebugEnabled()) {

                LOGGER.debug("當前DataSourcekey: " + key);

            }

            return key;

        }

        Object key = getSlaveKey();

        if (LOGGER.isDebugEnabled()) {

            LOGGER.debug("當前DataSourcekey: " + key);

        }

        return key;

    }

    @SuppressWarnings("unchecked")

    @Override

    public void afterPropertiesSet() {

        super.afterPropertiesSet();

        //由於父類的resolvedDataSources屬性是私有的子類獲取不到,需要使用反射獲取

        Field field = ReflectionUtils.findField(AbstractRoutingDataSource.class,"resolvedDataSources");

        field.setAccessible(true);// 設定可訪問

        try {

            Map<Object, DataSource> resolvedDataSources = (Map<Object, DataSource>) field.get(this);

            //讀庫的資料量等於資料來源總數減去寫庫的數量

            this.slaveCount =resolvedDataSources.size() - 1;

            for (Map.Entry<Object, DataSource>entry : resolvedDataSources.entrySet()) {

                if (DynamicDataSourceHolder.MASTER.equals(entry.getKey())) {

                    continue;

                }

                slaveDataSources.add(entry.getKey());

            }

        } catch (Exceptione) {

            LOGGER.error("afterPropertiesSet error! ",e);

        }

    }

    /**

     * 輪詢演算法實現

     *

     * @return

     */

    public Object getSlaveKey() {

        //得到的下標為:0123……

        Integer index = counter.incrementAndGet() %slaveCount;

        if (counter.get() > 9999) {// 以免超出Integer範圍

            counter.set(-1);// 還原

        }

        return slaveDataSources.get(index);

    }

}