import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
public class LoadBalanceUtil {
private boolean failover = true;
private String[] servers;
private Integer[] weights;
private Integer totalWeight = 0;
private TreeMap<Long, String> consistentBuckets;
private static ThreadLocal<MessageDigest> MD5 = new ThreadLocal<MessageDigest>() {
@Override
protected final MessageDigest initialValue() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("++++ no md5 algorythm found");
}
}
};
public LoadBalanceUtil(String[] servers) {
super();
this.servers = servers;
init();
}
public LoadBalanceUtil(String[] servers, Integer[] weights) {
super();
this.servers = servers;
this.weights = weights;
init();
}
private void init() {
// store buckets in tree map
consistentBuckets = new TreeMap<Long, String>();
MessageDigest md5 = MD5.get();
if (this.totalWeight <= 0 && this.weights != null) {
for (int i = 0; i < this.weights.length; i++)
this.totalWeight += (this.weights[i] == null) ? 1 : this.weights[i];
} else if (this.weights == null) {
this.totalWeight = this.servers.length;
}
for (int i = 0; i < servers.length; i++) {
int thisWeight = 1;
if (this.weights != null && this.weights[i] != null)
thisWeight = this.weights[i];
double factor = Math.floor(((double) (40 * this.servers.length * thisWeight)) / (double) this.totalWeight);
for (long j = 0; j < factor; j++) {
byte[] d = md5.digest((servers[i] + "-" + j).getBytes());
for (int h = 0; h < 4; h++) {
Long k = ((long) (d[3 + h * 4] & 0xFF) << 24) | ((long) (d[2 + h * 4] & 0xFF) << 16)
| ((long) (d[1 + h * 4] & 0xFF) << 8) | ((long) (d[0 + h * 4] & 0xFF));
consistentBuckets.put(k, servers[i]);
}
}
}
}
public String getServer(){
int size = consistentBuckets.size();
if(size==0){
return null;
}else if (size == 1) {
return consistentBuckets.get(consistentBuckets.firstKey());
}
String key = UUID.randomUUID().toString();
// from here on, we are working w/ multiple servers
// keep trying different servers until we find one
// making sure we only try each server one time
Set<String> tryServers = new HashSet<String>(Arrays.asList(servers));
// get initial bucket
long bucket = getBucket(key);
String server = consistentBuckets.get(bucket);
while (!tryServers.isEmpty()) {
//server可连接
boolean flag = ifCanConnect(server);
if(flag){
return server;
}
// if we do not want to failover, then bail here
if (!failover)
return null;
// log that we tried
tryServers.remove(server);
if (tryServers.isEmpty())
break;
// if we failed to get a socket from this server
// then we try again by adding an incrementer to the
// current key and then rehashing
int rehashTries = 0;
while (!tryServers.contains(server)) {
String newKey = new StringBuffer().append(rehashTries).append(key).toString();
// String.format( "%s%s", rehashTries, key );
bucket = getBucket(newKey);
server = consistentBuckets.get(bucket);
rehashTries++;
}
}
return null;
}
private static boolean ifCanConnect(String server){
HttpURLConnection urlCon = null;
try {
URL url = new URL("http://"+server+"/testIpIsActive");
urlCon = (HttpURLConnection)url.openConnection();
urlCon.setConnectTimeout(1000);
urlCon.setReadTimeout(1000);
urlCon.setUseCaches(false);
urlCon.setInstanceFollowRedirects(false);
urlCon.connect();
int resCode = urlCon.getResponseCode();
if(resCode != 0){
return true;
}
} catch (IOException e) {
// e.printStackTrace();
}finally{
if(urlCon!=null)
urlCon.disconnect();
}
return false;
}
private final long getBucket(String key) {
long hc = md5HashingAlg(key);
return findPointFor(hc);
}
private static long md5HashingAlg(String key) {
MessageDigest md5 = MD5.get();
md5.reset();
md5.update(key.getBytes());
byte[] bKey = md5.digest();
long res = ((long) (bKey[3] & 0xFF) << 24) | ((long) (bKey[2] & 0xFF) << 16) | ((long) (bKey[1] & 0xFF) << 8)
| (long) (bKey[0] & 0xFF);
return res;
}
private final Long findPointFor(Long hv) {
// this works in java 6, but still want to release support for java5
// Long k = this.consistentBuckets.ceilingKey( hv );
// return ( k == null ) ? this.consistentBuckets.firstKey() : k;
SortedMap<Long, String> tmap = this.consistentBuckets.tailMap(hv);
return (tmap.isEmpty()) ? this.consistentBuckets.firstKey() : tmap.firstKey();
}
public static void main(String[] args) {
String[] servers = {"106.39.164.160","180.149.134.142"};
Integer[] weights = {1,1};
LoadBalanceUtil util = new LoadBalanceUtil(servers,weights);
for(int i=0;i<20;i++){
String server = util.getServer();
System.out.println(server);
}
}
public String[] getServers() {
return servers;
}
public void setServers(String[] servers) {
this.servers = servers;
}
public Integer[] getWeights() {
return weights;
}
public void setWeights(Integer[] weights) {
this.weights = weights;
}
public boolean isFailover() {
return failover;
}
public void setFailover(boolean failover) {
this.failover = failover;
}
}
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
public class LoadBalanceUtil {
private boolean failover = true;
private String[] servers;
private Integer[] weights;
private Integer totalWeight = 0;
private TreeMap<Long, String> consistentBuckets;
private static ThreadLocal<MessageDigest> MD5 = new ThreadLocal<MessageDigest>() {
@Override
protected final MessageDigest initialValue() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("++++ no md5 algorythm found");
}
}
};
public LoadBalanceUtil(String[] servers) {
super();
this.servers = servers;
init();
}
public LoadBalanceUtil(String[] servers, Integer[] weights) {
super();
this.servers = servers;
this.weights = weights;
init();
}
private void init() {
// store buckets in tree map
consistentBuckets = new TreeMap<Long, String>();
MessageDigest md5 = MD5.get();
if (this.totalWeight <= 0 && this.weights != null) {
for (int i = 0; i < this.weights.length; i++)
this.totalWeight += (this.weights[i] == null) ? 1 : this.weights[i];
} else if (this.weights == null) {
this.totalWeight = this.servers.length;
}
for (int i = 0; i < servers.length; i++) {
int thisWeight = 1;
if (this.weights != null && this.weights[i] != null)
thisWeight = this.weights[i];
double factor = Math.floor(((double) (40 * this.servers.length * thisWeight)) / (double) this.totalWeight);
for (long j = 0; j < factor; j++) {
byte[] d = md5.digest((servers[i] + "-" + j).getBytes());
for (int h = 0; h < 4; h++) {
Long k = ((long) (d[3 + h * 4] & 0xFF) << 24) | ((long) (d[2 + h * 4] & 0xFF) << 16)
| ((long) (d[1 + h * 4] & 0xFF) << 8) | ((long) (d[0 + h * 4] & 0xFF));
consistentBuckets.put(k, servers[i]);
}
}
}
}
public String getServer(){
int size = consistentBuckets.size();
if(size==0){
return null;
}else if (size == 1) {
return consistentBuckets.get(consistentBuckets.firstKey());
}
String key = UUID.randomUUID().toString();
// from here on, we are working w/ multiple servers
// keep trying different servers until we find one
// making sure we only try each server one time
Set<String> tryServers = new HashSet<String>(Arrays.asList(servers));
// get initial bucket
long bucket = getBucket(key);
String server = consistentBuckets.get(bucket);
while (!tryServers.isEmpty()) {
//server可连接
boolean flag = ifCanConnect(server);
if(flag){
return server;
}
// if we do not want to failover, then bail here
if (!failover)
return null;
// log that we tried
tryServers.remove(server);
if (tryServers.isEmpty())
break;
// if we failed to get a socket from this server
// then we try again by adding an incrementer to the
// current key and then rehashing
int rehashTries = 0;
while (!tryServers.contains(server)) {
String newKey = new StringBuffer().append(rehashTries).append(key).toString();
// String.format( "%s%s", rehashTries, key );
bucket = getBucket(newKey);
server = consistentBuckets.get(bucket);
rehashTries++;
}
}
return null;
}
private static boolean ifCanConnect(String server){
HttpURLConnection urlCon = null;
try {
URL url = new URL("http://"+server+"/testIpIsActive");
urlCon = (HttpURLConnection)url.openConnection();
urlCon.setConnectTimeout(1000);
urlCon.setReadTimeout(1000);
urlCon.setUseCaches(false);
urlCon.setInstanceFollowRedirects(false);
urlCon.connect();
int resCode = urlCon.getResponseCode();
if(resCode != 0){
return true;
}
} catch (IOException e) {
// e.printStackTrace();
}finally{
if(urlCon!=null)
urlCon.disconnect();
}
return false;
}
private final long getBucket(String key) {
long hc = md5HashingAlg(key);
return findPointFor(hc);
}
private static long md5HashingAlg(String key) {
MessageDigest md5 = MD5.get();
md5.reset();
md5.update(key.getBytes());
byte[] bKey = md5.digest();
long res = ((long) (bKey[3] & 0xFF) << 24) | ((long) (bKey[2] & 0xFF) << 16) | ((long) (bKey[1] & 0xFF) << 8)
| (long) (bKey[0] & 0xFF);
return res;
}
private final Long findPointFor(Long hv) {
// this works in java 6, but still want to release support for java5
// Long k = this.consistentBuckets.ceilingKey( hv );
// return ( k == null ) ? this.consistentBuckets.firstKey() : k;
SortedMap<Long, String> tmap = this.consistentBuckets.tailMap(hv);
return (tmap.isEmpty()) ? this.consistentBuckets.firstKey() : tmap.firstKey();
}
public static void main(String[] args) {
String[] servers = {"106.39.164.160","180.149.134.142"};
Integer[] weights = {1,1};
LoadBalanceUtil util = new LoadBalanceUtil(servers,weights);
for(int i=0;i<20;i++){
String server = util.getServer();
System.out.println(server);
}
}
public String[] getServers() {
return servers;
}
public void setServers(String[] servers) {
this.servers = servers;
}
public Integer[] getWeights() {
return weights;
}
public void setWeights(Integer[] weights) {
this.weights = weights;
}
public boolean isFailover() {
return failover;
}
public void setFailover(boolean failover) {
this.failover = failover;
}
}