实现基本的mysql主数据写入,从数据库查询,从数据库简单负载均衡(均匀分配请求数)
jdbc.master.type=mysql
jdbc.master.driverClassName=com.mysql.jdbc.Driver
jdbc.master.url=jdbc:mysql://192.168.113.11:3306/showbox?useUnicode=true&characterEncoding=utf-8
jdbc.master.username=root
jdbc.master.password=zhd123456
jdbc.slave1.type=mysql
jdbc.slave1.driverClassName=com.mysql.jdbc.Driver
jdbc.slave1.url=jdbc:mysql://192.168.113.200:3306/showbox?useUnicode=true&characterEncoding=utf-8
jdbc.slave1.username=root
jdbc.slave1.password=zhd123456
1.3配置applicationContext-datasource.xml
package com.flame.common.dataSource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
private DataSource master; // 主库,只允许有一个
private List
private AtomicLong slaveCount = new AtomicLong();
private int slaveSize = 0;
private Map
private static final String DEFAULT = "master";
private static final String SLAVE = "slave";
private static final ThreadLocal
@Override
protected LinkedList
return new LinkedList
}
};
/**
* 初始化
*/
@Override
public void afterPropertiesSet() {
if (null == master) {
throw new IllegalArgumentException("Property 'master' is required");
}
dataSources.put(DEFAULT, master);
if (null != slaves && slaves.size() > 0) {
for (int i = 0; i < slaves.size(); i++) {
dataSources.put(SLAVE + (i + 1), slaves.get(i));
}
slaveSize = slaves.size();
}
this.setDefaultTargetDataSource(master);
this.setTargetDataSources(dataSources);
super.afterPropertiesSet();
}
/**
* 选择使用主库,并把选择放到当前ThreadLocal的栈顶
*/
public static void useMaster() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("use datasource :" + datasourceHolder.get());
}
LinkedList
m.offerFirst(DEFAULT);
}
/**
* 选择使用从库,并把选择放到当前ThreadLocal的栈顶
*/
public static void useSlave() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("use datasource :" + datasourceHolder.get());
}
LinkedList
m.offerFirst(SLAVE);
}
/**
* 重置当前栈
*/
public static void reset() {
LinkedList
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("reset datasource {}", m);
}
if (m.size() > 0) {
m.poll();
}
}
/**
* 如果是选择使用从库,且从库的数量大于1,则通过取模来控制从库的负载, 计算结果返回AbstractRoutingDataSource
*/
@Override
protected Object determineCurrentLookupKey() {
LinkedList
String key = m.peekFirst() == null ? DEFAULT : m.peekFirst();
if (null != key) {
if (DEFAULT.equals(key)) {
LOGGER.debug("current datasource (default):" + key);
return key;
} else if (SLAVE.equals(key)) {
if (slaveSize > 1) {// Slave loadBalance
long c = slaveCount.incrementAndGet();
c = c % slaveSize;
LOGGER.debug("current datasource (slaves>1) :" + SLAVE + (c + 1));
return SLAVE + (c + 1);
} else {
LOGGER.debug("current datasource (slaves=1):" + SLAVE + "1");
return SLAVE + "1";
}
}
return null;
} else {
return null;
}
}
public DataSource getMaster() {
return master;
}
public List
return slaves;
}
public void setMaster(DataSource master) {
this.master = master;
}
public void setSlaves(List
this.slaves = slaves;
}
}
package com.flame.common.annotation;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Inherited
@Retention(RUNTIME)
@Target(METHOD)
public @interface DataSourceChange {
boolean slave() default false;
}
1. DataSourceAspect切面
package com.flame.common.dataSource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.flame.common.annotation.DataSourceChange;
import com.flame.common.exception.DataSourceAspectException;
/**
* 有{@link com.wangzhixuan.commons.annotation.DataSourceChange}注解的方法,调用时会切换到指定的数据源
*
* @author tanghd
*/
@Aspect
@Component
public class DataSourceAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceAspect.class);
@Around("@annotation(dataSourceChange)")
public Object doAround(ProceedingJoinPoint pjp, DataSourceChange dataSourceChange) {
LOGGER.debug("AOP切换dataSource");
Object retVal = null;
boolean selectedDataSource = false;
try {
if (null != dataSourceChange) {
selectedDataSource = true;
if (dataSourceChange.slave()) {
DynamicDataSource.useSlave();
} else {
DynamicDataSource.useMaster();
}
}
retVal = pjp.proceed();
} catch (Throwable e) {
LOGGER.warn("数据源切换错误", e);
throw new DataSourceAspectException("数据源切换错误", e);
} finally {
if (selectedDataSource) {
DynamicDataSource.reset();
}
}
return retVal;
}
}
2.修改applicationContext.xml
3、service上添加注解
package com.flame.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.flame.base.dao.BaseDao;
import com.flame.base.model.DataGrid;
import com.flame.base.model.Page;
import com.flame.base.model.Params;
import com.flame.base.service.BaseServiceImpl;
import com.flame.common.annotation.DataSourceChange;
import com.flame.dao.ArticleDao;
import com.flame.dto.ArticleDto;
import com.flame.entity.Article;
import com.flame.service.ArticleService;
@Service("articleService")
public class ArticleServiceImpl extends BaseServiceImpl
@Resource
private ArticleDao articleDao;
@Override
protected BaseDao
return this.articleDao;
}
@Override
public List
// TODO Auto-generated method stub
return articleDao.getResourceByDeviceId(deviceId);
}
@Override
@DataSourceChange(slave = true)
public DataGrid
// TODO 自动生成的方法存根
return articleDao.paging(params, page);
}
@Override
@Transactional
public void deleteBatchDo(Long[] id) {
// TODO 自动生成的方法存根
for (Long i : id) {
articleDao.delete(i);
}
}
}
详细配置下载:https://download.csdn.net/download/jiangshanmnaa/10829634