package com.hengyu.ticket.common;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by lgf on 2016/4/3.
* 使用多台数据库服务区器负载
*/
public class CommonLoadBalancing {
private static final String SERVER_CONFIG = "SERVER.JSON";
public static String DB_DRIVER;
public static String DB_PORT;
public static String DB_USER_NAME;
public static String DB_PASSWORD;
public static String DB_URL;
//选择服务器类型(线程安全)
private static final ThreadLocal<String> SERVER_HOLDER = new ThreadLocal<>();
//服务器类型
private final static String SERVER_MASTER = "MASTER";
private final static String SERVER_SLAVE = "SLAVE";
//当前服务器的编号
private static Integer CURRENT_INDEX = -1;
//当前权重
private static Integer CURRENT_WEIGHT = 0;
//最大权重
private static Integer MAX_WEIGHT = 0;
//权重公约数
private static Integer GCD_WEIGHT = 1;
//最小公约数
private static Integer MIN_WEIGHT = 1;
//主服务器
private static Server MASTER;
//备用主服务器
private static Server MASTER_RESERVED;
//服务器集合
public static final List<Server> servers = Collections.synchronizedList(new ArrayList<Server>());
//脱机服务器集合
private static final List<Server> down_servers = Collections.synchronizedList(new ArrayList<Server>());
static {
DBConfig.init();
}
//获取主服务器,备用主服务器
public static Server getMaster() {
if (MASTER.isDown()) {
if (MASTER_RESERVED != null && !MASTER_RESERVED.isDown()) {
return MASTER_RESERVED;
} else {
throw new RuntimeException("error:tow master is down!");
}
}
return MASTER;
}
public static void setMaster(Server master) {
CommonLoadBalancing.MASTER = master;
}
//添加服务器
public static void addServer(Server server) {
addServer(server, false);
}
//添加服务器
public static void addServer(Server server, boolean isReload) {
if (server == null) {
throw new RuntimeException("error: server cant't not be null !");
}
int index = servers.size();
if (server.getType() != null && SERVER_MASTER.equals(server.getType())) {
MASTER = server;
} else if (server.getType() == null) {
server.setType(SERVER_SLAVE);
}
servers.add(server);
if (isReload) {
initOrReload();
}
}
//添加服务器
public static void addServer(List<Server> servers) {
for (int i = 0; i < servers.size(); i++) {
Server server = servers.get(i);
addServer(server);
}
CommonLoadBalancing.initOrReload();
}
//查找权重,计算权重公约数
public synchronized static void initOrReload() {
for (Server server : servers) {
if (server == null || server.isDown()) {
continue;
}
if (server.getWeight() > MAX_WEIGHT) {
MAX_WEIGHT = server.getWeight();
}
if (server.getWeight() < MIN_WEIGHT) {
MIN_WEIGHT = server.getWeight();
}
}
if (MASTER == null) {
MASTER = servers.get(0);
MASTER.setType(SERVER_MASTER);
}
GCD_WEIGHT = gcd(servers);
}
//获取权重公因数
public static int gcd(List<Server> servers) {
if (servers == null || servers.size() == 0) {
return 1;
}
int min = servers.get(0).getWeight();
for (int i = 0; i < servers.size(); i++) {
Server server = servers.get(i);
if (server.getWeight() < min) {
min = server.getWeight();
}
}
while (min >= 1) {
boolean isCommon = true;
for (int i = 0; i < servers.size(); i++) {
Server server = servers.get(i);
if (server.getWeight() % min != 0) {
isCommon = false;
break;
}
}
if (isCommon) {
break;
}
min--;
}
return min<1?1:min;
}
//轮询服务器
public static Server getServer() {
int count = 0;
int size = servers.size();
if (size == 0 || getServerType().equals(SERVER_MASTER)) {
return getMaster().addAccessCount();
}
clearServerHolder();
while (true) {
CURRENT_INDEX = (CURRENT_INDEX + 1) % size;
if (CURRENT_INDEX == 0) {
CURRENT_WEIGHT = CURRENT_WEIGHT - GCD_WEIGHT;
if (CURRENT_WEIGHT <= 0) {
CURRENT_WEIGHT = MAX_WEIGHT;
}
}
Server server = servers.get(CURRENT_INDEX);
if (server != null && server.getWeight() >= CURRENT_WEIGHT && !server.isDown) {
return server.addAccessCount();
}
if (count >= size) {
return getMaster().addAccessCount();
}
count++;
}
}
public static List<Server> getServers() {
return servers;
}
public static void setMasterReserved(Server masterReserved) {
CommonLoadBalancing.MASTER_RESERVED = masterReserved;
}
public static List<Server> getDown_servers() {
return down_servers;
}
public static void setServerType(boolean isReadOnly) {
if (isReadOnly) {
SERVER_HOLDER.set(SERVER_SLAVE);
} else {
SERVER_HOLDER.set(SERVER_MASTER);
}
}
public static void clearServerHolder() {
SERVER_HOLDER.set(SERVER_SLAVE);
}
public static String getServerType() {
String type = SERVER_HOLDER.get();
if (type == null || type.equals(SERVER_SLAVE)) {
return SERVER_SLAVE;
}
return SERVER_MASTER;
}
public static class Server {
//服务器编号
private String id;
//服务器索引
private Integer index;
///服务器ip
private String ip;
//权重
private int weight;
//类型,主从
private String type;
//用户名
private String username;
//密码
private String password;
//端口
private String port;
//url链接
private String url;
//访问数量
private Integer accessCount = 0;
//是否脱机
private boolean isDown;
public Server() {
}
@Override
public String toString() {
return "Server{type='" + type + '\'' + ", ip='" + ip + '\'' + ", weight=" + weight + ", accessCount=" + accessCount + '}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public Server(String ip, int weight) {
this.ip = ip;
this.weight = weight;
}
public int getAccessCount() {
return accessCount;
}
public void setAccessCount(int accessCount) {
this.accessCount = accessCount;
}
public Server addAccessCount() {
synchronized (this.accessCount) {
this.accessCount++;
return this;
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getWeight() {
if (weight < 1) {
weight = 1;
}
return weight;
}
public void setWeight(int weight) {
if (weight < 1) {
weight = 1;
}
this.weight = weight;
}
public boolean isDown() {
return isDown;
}
public void setDown(boolean down) {
isDown = down;
}
}
//数据库配置
public static class DBConfig {
/*
* { "username": "root", "password": "admin", "port": "3306", "driver": "com.mysql.jdbc.Driver"
* , "url": "jdbc:mysql://${host}:${prot}/ticket?characterEncoding=utf8", "master": [ "127.0.0.1" ],
* "slave": [ "192.168.0.10 -w100", "192.168.0.11 -w50" ] }
*
* */
//初始化
public static void init() {
InputStream in = null;
try {
in = Thread.currentThread().getContextClassLoader().getResourceAsStream(SERVER_CONFIG);
if (in == null) {
throw new RuntimeException("error:" + SERVER_CONFIG + " cat't not be null");
}
byte[] bs = new byte[in.available()];
in.read(bs);
JSONObject base = JSON.parseObject(new String(bs));
DB_USER_NAME = base.get("username") == null ? "" : base.get("username").toString();
DB_PASSWORD = base.get("password") == null ? "" : base.get("password").toString();
DB_URL = base.get("url") == null ? "" : base.get("url").toString();
DB_DRIVER = base.get("driver") == null ? "" : base.get("driver").toString();
DB_PORT = base.get("port") == null ? "" : base.get("port").toString();
List<String> masters = JSON.parseArray(base.get("master").toString(), String.class);
List<String> slaves = null;
if (base.get("slave") != null) {
slaves = JSON.parseArray(base.get("slave").toString(), String.class);
}
createServer(masters, slaves);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
}
}
}
//创建服务器
public synchronized static void createServer(List<String> masters, List<String> slave) {
List<Server> servers = new ArrayList<>();
int i = 0;
if (masters != null) {
//主服务器
for (String info : masters) {
Server server = new Server();
server.setId(String.valueOf(i));
createServer(SERVER_MASTER, server, info);
server.setIndex(servers.size());
servers.add(server);
CommonLoadBalancing.MASTER = server;
if (i == 1) {
CommonLoadBalancing.MASTER_RESERVED = server;
}
i++;
}
}
//从服务器
if (slave != null) {
for (String info : slave) {
Server sa = new Server();
sa.setId(String.valueOf(i));
createServer(SERVER_SLAVE, sa, info);
sa.setIndex(i);
servers.add(sa);
i++;
}
}
CommonLoadBalancing.addServer(servers);
}
//创建服务器
public static void createServer(String type, Server server, String info) {
server.setUrl(DB_URL);
server.setPort(DB_PORT);
server.setUsername(DB_USER_NAME);
server.setPassword(DB_PASSWORD);
server.setType(type);
String[] array = info.trim().split("\\s");
int i = 0;
for (String item : array) {
if (item.startsWith("-w")) {
server.setWeight(Integer.parseInt(getConfigString(item, array, i)));
} else if (item.startsWith("-u")) {
server.setUsername(getConfigString(item, array, i));
} else if (item.startsWith("-p")) {
server.setPassword(getConfigString(item, array, i));
} else if (item.startsWith("-P")) {
server.setPort(getConfigString(item, array, i));
} else if (item.startsWith("-U")) {
server.setUrl(getConfigString(item, array, i));
} else if (item.startsWith("-h")) {
server.setIp(getConfigString(item, array, i));
} else if (item.startsWith("-i")) {
server.setId(getConfigString(item, array, i));
} else if (i == 0) {
server.setIp(item);
}
i++;
}
String _url = server.getUrl();
server.setUrl(_url.replace("${host}", server.getIp()).replace("${port}", server.getPort()));
}
public static String getConfigString(String item, String[] array, int i) {
return (item.length() == 2 ? array[i + 1] : item.substring(2)).trim();
}
}
}
//数据源配置
package com.hengyu.ticket.common;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by lgf on 2016/4/3.
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static Map<Object, Object> targetDataSources = new HashMap<>();
//初始化数据源
public static void initDataSource(){
try {
List<CommonLoadBalancing.Server> servers = CommonLoadBalancing.getServers() ;
for (CommonLoadBalancing.Server server:servers){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setUsername(server.getUsername());
ds.setPassword(server.getPassword());
ds.setDriverClassName(CommonLoadBalancing.DB_DRIVER);
ds.setUrl(server.getUrl());
targetDataSources.put(String.valueOf(server.getId()),ds);
}
}catch (Exception e){
e.printStackTrace();
}
}
static {
initDataSource();
}
public DynamicDataSource() throws IOException {
super.setTargetDataSources(targetDataSources);
}
@Override
protected Object determineCurrentLookupKey() {
CommonLoadBalancing.Server server = CommonLoadBalancing.getServer();
System.out.println("=====>>获取数据源:" +server);
return server.getIndex().toString();
}
public Map<Object, Object> getTargetDataSources() {
return targetDataSources;
}
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this.targetDataSources = targetDataSources;
}
}
//spring配置
package com.hengyu.ticket.common;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.JoinPoint;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.web.filter.OncePerRequestFilter;
import java.lang.reflect.Method;
/**
* Created by lgf on 2016/4/4.
*/
public class MethodInterceptor implements org.aopalliance.intercept.MethodInterceptor {
@Autowired
private TransactionInterceptor transactionInterceptor;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Class targetClass = invocation.getThis() != null? AopUtils.getTargetClass(invocation.getThis()):null;
final TransactionAttribute txAttr = transactionInterceptor.getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
System.out.println("*******"+txAttr.isReadOnly());
CommonLoadBalancing.setServerType(txAttr.isReadOnly());
Object proceed = invocation.proceed();
return proceed;
}
}
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <!-- 支持注解 --> <mvc:annotation-driven /> <!-- 扫描控制器类 --> <context:component-scan base-package="com.hengyu.ticket.service" /> <!-- 加载配置文件 --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config.properties</value> </list> </property> </bean> <bean id="dynamicDataSource" class="com.hengyu.ticket.common.DynamicDataSource"> </bean> <!-- 配置 sqlSessionFactory --> <bean id="sqlSessionFactory" name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dynamicDataSource"/> <property name="plugins"> <array> <bean class="com.hengyu.ticket.common.SqlIntercept"> <property name="show_sql" value="false"></property> </bean> </array> </property> <property name="configurationProperties"> <props> <!-- <prop key="cacheEnabled">true</prop> --> </props> </property> <property name="mapperLocations" value="com.hengyu.ticket.dao.*.xml"/> <property name="typeAliasesPackage" value="com.hengyu.ticket.entity"/> </bean> <!-- 配置 mapperScannerConfigurer 扫描配置文件 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="basePackage" value="com.hengyu.ticket.dao"/> <!-- <property name="sqlSessionTemplateBeanName" value="SqlSessionTemplate"/> --> </bean> <!-- 事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dynamicDataSource"></property> </bean> <!-- 事物切面 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes > <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="edit*" propagation="REQUIRED" /> <tx:method name="del*" propagation="REQUIRED" /> <tx:method name="remove*" propagation="REQUIRED" /> <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <tx:method name="select*" propagation="SUPPORTS" read-only="true" /> <tx:method name="load*" propagation="SUPPORTS" read-only="true" /> <tx:method name="*" /> </tx:attributes> </tx:advice> <aop:config> <aop:advisor pointcut="execution(* com.hengyu.ticket.service.*.*(..))" advice-ref="txAdvice"/> <aop:advisor pointcut="execution(* com.hengyu.ticket.service.*.*(..))" advice-ref="methodInterceptor" order="1"/> <aop:aspect id="im" ref="methodInterceptor"> <aop:pointcut id="mi" expression="execution(* com.hengyu.ticket.service.*.*(..))"></aop:pointcut> </aop:aspect> </aop:config> <bean id="methodInterceptor" class="com.hengyu.ticket.common.MethodInterceptor"></bean> </beans>
//数据库配置SERVER.JSON
{ "username": "root", "password": "admin", "port": "3306", "driver": "com.mysql.jdbc.Driver", "url": "jdbc:mysql://${host}:${port}/ticket?characterEncoding=utf8", "master": [ "127.0.0.1" ], "slave": [ "localhost" ] }