package com.taobao.tddl.group.config;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>
* 数据库权重配置,权重越大,被选中的机率越大.
* <p>
* 权重配置模式:
* <p>
* [r|R](\\d*) [w|W](\\d*) [p|P](\\d*) [q|Q](\\d*) [i|I](\\d*)
* <p>
* 字母r或R表示可以对数据库进行读操作, 后面跟一个数字表示读操作的权重,如果字母r或R后面没有数字,则默认是10;
* <p>
* 字母w或W表示可以对数据库进行写操作, 后面跟一个数字表示写操作的权重,如果字母w或W后面没有数字,则默认是10;<
* <p>
* 字母p或P表示读操作的优先级, 数字越大优先级越高,读操作优先从优先级最高的数据库中读数据, 如果字母p或P后面没有数字,则默认优先级是0;
* <p>
* 字母q或Q表示写操作的优先级, 数字越大优先级越高,写操作优先从优先级最高的数据库中写数据, 如果字母q或Q后面没有数字,则默认优先级是0.
* <p>
* 字母i或I表示动态DBIndex, 和用户通过threadLocal指定的dbIndex结合,实现rw之上更灵活的路由
* 一个db可以同时配置多个i;不同的db可以配置相同的i,例如 db0:i0i2,db1:i1,db2:i1,db3:i2则
* 用户指定dbIndex=0,路由到db0;(只有db0有i0) 用户指定dbIndex=1,随机路由到db1和db2;(db1和db2都有i1)
* 用户指定dbIndex=2,随机路由到db0和db3;(db0和db3都有i2)
* <p>
* 如:db1: r10w10p2, db2: r20p2, db3: rp3,则对应如下三个Weight: db1: Weight(r10w10p2)
* db2: Weight(r20p2) db3: Weight(rp3)
* <p>
* 在这个例子中,对db1, db2,db3这三个数据库的读操作分成了两个优先级: p3->[db3] p2->[db1, db2]
* 当进行读操作时,因为db3的优先级最高,所以优先从db3读, 如果db3无法进行读操作,再从db1,
* db2中随机选一个,因为db2的读权重是20,而db1是10,所以db2被选中的机率比db1更大。
* <p>
* 如果在数据库名后面没有设置权重字符串,就认为权重字符串是null, 如: db1: r10w10, db2, db3,则对应如下三个Weight:
* db1: Weight(r10w10) db2: Weight(null) db3: Weight(null)
* <p>
* <b>为了兼容2.4之前的老版本,当权重字符串是null时,相当于"r10w10p0q0", 对于上面的例子,实际的数据库权重配置是:db1:
* r10w10p0q0, db2: r10w10p0q0, db3: r10w10p0q0。<b>
*
* @author yangzhu
* @author linxuan add indexes i/I at 2011/01/21
*/
public class Weight {
private static final Pattern weightPattern_r = Pattern.compile("[R](\\d*)");
private static final Pattern weightPattern_w = Pattern.compile("[W](\\d*)");
private static final Pattern weightPattern_p = Pattern.compile("[P](\\d*)");
private static final Pattern weightPattern_q = Pattern.compile("[Q](\\d*)");
private static final Pattern weightPattern_i = Pattern.compile("[I](\\d*)");
/**
* 读权重,默认是10
*/
public final int r;
/**
* 写权重,默认是10
*/
public final int w;
/**
* 读优先级,默认是0
*/
public final int p;
/**
* 写优先级,默认是0
*/
public final int q;
public final Set<Integer> indexes;
public Weight(String weightStr){
// 兼容2.4之前的老版本,当权重字符串是null时,相当于"r10w10p0q0",
if (weightStr == null) {
r = 10;
w = 10;
p = 0;
q = 0;
indexes = null;
} else {
weightStr = weightStr.trim().toUpperCase();
// 如果字母'R'在weightStr中找不到,则读权重是0,
// 如果字母'R'在weightStr中已找到了,但是在字母'R'后面没有数字,是读权重是10
r = getUnitWeight(weightStr, 'R', weightPattern_r, 0, 10);
w = getUnitWeight(weightStr, 'W', weightPattern_w, 0, 10);
p = getUnitWeight(weightStr, 'P', weightPattern_p, 0, 0);
q = getUnitWeight(weightStr, 'Q', weightPattern_q, 0, 0);
indexes = getUnitWeights(weightStr, 'I', weightPattern_i);
}
}
public String toString() {
return "Weight[r=" + r + ", w=" + w + ", p=" + p + ", q=" + q + ", indexes=" + indexes + "]";
}
// 如果字符c在weightStr中找不到,则返回defaultValue1,
// 如果字符c在weightStr中已经找到了,但是在字母c后面没有数字,则返回defaultValue2,
// 否则返回字母c后面 的数字.
private static int getUnitWeight(String weightStr, char c, Pattern p, int defaultValue1, int defaultValue2) {
if (weightStr.indexOf(c) == -1) {
return defaultValue1;
} else {
Matcher m = p.matcher(weightStr);
m.find();
if (m.group(1).length() == 0) {
return defaultValue2;
} else {
return Integer.parseInt(m.group(1));
}
}
}
private static Set<Integer> getUnitWeights(String weightStr, char c, Pattern p) {
if (weightStr.indexOf(c) == -1) {
return null;
}
Set<Integer> is = new HashSet<Integer>();
int start = 0;
Matcher m = p.matcher(weightStr);
while (m.find(start)) {
if (m.group(1).length() != 0) {
is.add(Integer.valueOf(m.group(1)));
}
start = m.end();
}
return is;
}
}