使用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
// 記錄讀庫的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("當前DataSource的key為: " + key);
}
return key;
}
Object key = getSlaveKey();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("當前DataSource的key為: " + 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() {
//得到的下標為:0、1、2、3……
Integer index = counter.incrementAndGet() %slaveCount;
if (counter.get() > 9999) {// 以免超出Integer範圍
counter.set(-1);// 還原
}
return slaveDataSources.get(index);
}
}