实现基本的mysql主数据写入,从数据库查询,从数据库简单负载均衡(均匀分配请求数)
1.1配置两个数据源参数
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.2 配置applicationContext-datasource.xml
<!-- 主库master 数据连接池 -->
<bean id="dataSource-master" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${jdbc.master.url}" />
<property name="username" value="${jdbc.master.username}" />
<property name="password" value="${jdbc.master.password}" />
<property name="driverClassName" value="${jdbc.master.driverClassName}" />
<property name="maxActive" value="200" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="20" />
<property name="testWhileIdle" value="true" />
<property name="validationQuery" value="select 1+1 from dual" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="300" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="numTestsPerEvictionRun" value="20" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="poolPreparedStatements" value="true" />
<property name="defaultAutoCommit" value="true" />
</bean>
<!-- 从库slave1 数据连接池 -->
<bean id="dataSource-slave1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${jdbc.slave1.url}" />
<property name="username" value="${jdbc.slave1.username}" />
<property name="password" value="${jdbc.slave1.password}" />
<property name="driverClassName" value="${jdbc.slave1.driverClassName}" />
<property name="maxActive" value="200" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="20" />
<property name="testWhileIdle" value="true" />
<property name="validationQuery" value="select 1+1 from dual" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="300" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="numTestsPerEvictionRun" value="20" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="poolPreparedStatements" value="true" />
<property name="defaultAutoCommit" value="true" />
</bean>
<!--主从库选择 -->
<bean id="dynamicDataSource" class="com.flame.common.dataSource.DynamicDataSource">
<property name="master" ref="dataSource-master" />
<property name="slaves">
<list>
<ref bean="dataSource-slave1" />
</list>
</property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource" />
</bean>
<!-- 注解式事务配置 -->
<tx:annotation-driven transaction-manager="transactionManager" />
1.3配置applicationContext-datasource.xml
<!-- MyBatis会话工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<property name="databaseIdProvider" ref="databaseIdProvider" />
<property name="configLocation" value="classpath:/META-INF/mybatis/mybatis-config.xml" />
<property name="mapperLocations">
<array>
<value>classpath:/META-INF/mybatis/mybatis-pager.xml</value>
<value>classpath*:com/flame/dao/mapper/*.xml</value>
<value>classpath*:com/flame/dao/custom/*.xml</value>
</array>
</property>
</bean>
1.4DynamicDataSource 动态切换数据源代码
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<DataSource> slaves; // 从库,允许有多个
private AtomicLong slaveCount = new AtomicLong();
private int slaveSize = 0;
private Map<Object, Object> dataSources = new HashMap<Object, Object>();
private static final String DEFAULT = "master";
private static final String SLAVE = "slave";
private static final ThreadLocal<LinkedList<String>> datasourceHolder = new ThreadLocal<LinkedList<String>>() {
@Override
protected LinkedList<String> initialValue() {
return new LinkedList<String>();
}
};
/**
* 初始化
*/
@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<String> m = datasourceHolder.get();
m.offerFirst(DEFAULT);
}
/**
* 选择使用从库,并把选择放到当前ThreadLocal的栈顶
*/
public static void useSlave() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("use datasource :" + datasourceHolder.get());
}
LinkedList<String> m = datasourceHolder.get();
m.offerFirst(SLAVE);
}
/**
* 重置当前栈
*/
public static void reset() {
LinkedList<String> m = datasourceHolder.get();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("reset datasource {}", m);
}
if (m.size() > 0) {
m.poll();
}
}
/**
* 如果是选择使用从库,且从库的数量大于1,则通过取模来控制从库的负载, 计算结果返回AbstractRoutingDataSource
*/
@Override
protected Object determineCurrentLookupKey() {
LinkedList<String> m = datasourceHolder.get();
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<DataSource> getSlaves() {
return slaves;
}
public void setMaster(DataSource master) {
this.master = master;
}
public void setSlaves(List<DataSource> slaves) {
this.slaves = slaves;
}
}
1.5 注解DataSourceChange
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.6配置AOP
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
<context:component-scan base-package="com.flame">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />
</context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
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<Article, Long> implements ArticleService {
@Resource
private ArticleDao articleDao;
@Override
protected BaseDao<Article, Long> getBaseDao() {
return this.articleDao;
}
@Override
public List<Article> getResourceByDeviceId(String deviceId) {
// TODO Auto-generated method stub
return articleDao.getResourceByDeviceId(deviceId);
}
@Override
@DataSourceChange(slave = true)
public DataGrid<ArticleDto> paging(Params params, Page page) {
// 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