/** * <pre> * This program is free software; you can redistribute it and/or modify it under the terms of * the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU AFFERO GENERAL PUBLIC LICENSE for more details. * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program; * if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * </pre> */ package com.meidusa.amoeba.net.poolable; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.pool.PoolableObjectFactory; import com.meidusa.amoeba.heartbeat.HeartbeatManager; import com.meidusa.amoeba.heartbeat.Status; import com.meidusa.amoeba.util.Initialisable; import com.meidusa.amoeba.util.InitialisationException; /** * <pre> * ��Pool �ṩ���ؾ��⡢failover��HA���� * ����Load Balance ObjectPool����object ����ʵ��{@link PoolableObject} * Ĭ���ṩ2�ָ��ؾ��ⷽ���� * <li>��ѯ��������ѯ���䵽ÿ��pool��ÿ��pool������Ƚ�ƽ��</li> * <li>��æ�̶ȣ�������Pool��Active Num��һ��������С��Active Num�����ȷ�������</li> * @author <a href=mailto:piratebase@sina.com>Struct chen</a> * </pre> */ public class MultipleLoadBalanceObjectPool implements ObjectPool,Initialisable { public class ObjectPoolWrapper implements ObjectPool{ ObjectPool source; public ObjectPoolWrapper(ObjectPool objectPool){ this.source = objectPool; } public boolean isEnable() { return this.source.isEnable(); } public boolean isValid() { return this.source.isValid(); } public void setEnable(boolean isEnabled) { this.source.setEnable(isEnabled); } public void setValid(boolean valid) { this.source.setValid(valid); } public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException { this.source.addObject(); } public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException { return this.source.borrowObject(); } public void clear() throws Exception, UnsupportedOperationException { this.source.clear(); } public void close() throws Exception { this.source.close(); } public int getNumActive() throws UnsupportedOperationException { return this.source.getNumActive(); } public int getNumIdle() throws UnsupportedOperationException { return this.source.getNumIdle(); } public void invalidateObject(Object obj) throws Exception { this.source.invalidateObject(obj); } public void returnObject(Object obj) throws Exception { this.source.returnObject(obj); } public void setFactory(PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException { this.source.setFactory(factory); } @Override public boolean validate() { return source.validate(); } @Override public String getName() { return source.getName(); } @Override public void setName(String name) { source.setName(name); } } public static final int LOADBALANCING_ROUNDROBIN = 1; public static final int LOADBALANCING_WEIGHTBASED = 2; public static final int LOADBALANCING_HA = 3; private boolean enable; private String name; protected static class ActiveNumComparator implements Comparator<ObjectPool> { public int compare(ObjectPool o1, ObjectPool o2) { return o1.getNumActive() - o2.getNumActive(); } } /** * ��������㷨 */ private int loadbalance; private AtomicLong currentCount = new AtomicLong(0); private ObjectPool[] objectPools; private ObjectPool[] runtimeObjectPools; private int index = 0; private ActiveNumComparator comparator = new ActiveNumComparator(); private boolean valid; public MultipleLoadBalanceObjectPool(){ } public MultipleLoadBalanceObjectPool(int loadbalance, ObjectPool... objectPools){ setObjectPools(objectPools); this.loadbalance = loadbalance; } public void setLoadbalance(int loadbalance) { this.loadbalance = loadbalance; } public void setObjectPools(ObjectPool[] objectPools) { this.objectPools = new ObjectPool[objectPools.length]; for(int i=0;i<objectPools.length;i++){ this.objectPools[i] = new ObjectPoolWrapper(objectPools[i]); } this.objectPools = objectPools; this.runtimeObjectPools = objectPools.clone(); } public void addObject() throws Exception { throw new UnsupportedOperationException(); } public Object borrowObject() throws Exception { ObjectPool pool = null; ObjectPool[] poolsTemp = null; while(true){ poolsTemp = runtimeObjectPools; if (poolsTemp.length == 0) { throw new Exception("poolName="+name+", no valid pools"); } if (loadbalance == LOADBALANCING_ROUNDROBIN) { long current = currentCount.getAndIncrement(); pool = poolsTemp[(int) (current % poolsTemp.length)]; } else if (loadbalance == LOADBALANCING_WEIGHTBASED) { if (poolsTemp.length > 1) { ObjectPool[] objectPoolsCloned = poolsTemp.clone(); Arrays.sort(objectPoolsCloned, comparator); pool = objectPoolsCloned[0]; } else if (poolsTemp.length == 1) { pool = poolsTemp[0]; } } else if (loadbalance == LOADBALANCING_HA) { // HA,ֻҪ��Ч��pool if(index < poolsTemp.length){ pool = poolsTemp[index]; }else{ pool = poolsTemp[0]; } } else { throw new Exception("poolName="+name+" loadbalance parameter error,parameter loadbalance in [1,2,3]"); } if(!pool.isValid()){ validate(); continue; }else{ break; } } return pool.borrowObject(); } public void initAllPools() { /*for (ObjectPool pool : this.objectPools) { HeartbeatManager.addPooltoHeartbeat(new HeartbeatDelayed(2, TimeUnit.SECONDS, pool)); }*/ } public void clear() throws Exception, UnsupportedOperationException { //do not clear internal pools /*for (ObjectPool pool : objectPools) { pool.clear(); }*/ } public void close() throws Exception { //do not close internal pools /*for (ObjectPool pool : objectPools) { pool.close(); }*/ } public int getNumActive() throws UnsupportedOperationException { int active = 0; for (ObjectPool pool : objectPools) { active += pool.getNumActive(); } return active; } public int getNumIdle() throws UnsupportedOperationException { int idle = 0; for (ObjectPool pool : objectPools) { idle += pool.getNumIdle(); } return idle; } public void invalidateObject(Object obj) throws Exception { PoolableObject poolableObject = (PoolableObject) obj; ObjectPool pool = poolableObject.getObjectPool(); pool.invalidateObject(obj); } public void returnObject(Object obj) throws Exception { PoolableObject poolableObject = (PoolableObject) obj; ObjectPool pool = poolableObject.getObjectPool(); pool.returnObject(obj); } public void setFactory(PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException { throw new UnsupportedOperationException("setFactory is not supported in class="+ this.getClass().getName()); } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public boolean isEnable() { return enable; } public void setEnable(boolean isEnabled) { this.enable = isEnabled; } public boolean isValid() { return this.valid; } public void setValid(boolean valid) { this.valid = valid; } public static class MultipleHeartbeatDelayed extends ObjectPoolHeartbeatDelayed { public MultipleHeartbeatDelayed(long nsTime, TimeUnit timeUnit, MultipleLoadBalanceObjectPool pool) { super(nsTime, timeUnit, pool); } public boolean isCycle(){ return true; } public Status doCheck() { return super.doCheck(); } } @Override public void init() throws InitialisationException { HeartbeatManager.addHeartbeat(new MultipleHeartbeatDelayed(3, TimeUnit.SECONDS, this)); } @Override public boolean validate() { List<ObjectPool> poolList = new ArrayList<ObjectPool>(); for(ObjectPool object :this.objectPools){ if(object.isValid()){ poolList.add(object); } } ObjectPool[] poolsTemp = runtimeObjectPools = poolList.toArray(new ObjectPool[poolList.size()]); if (poolsTemp.length == 0) { return false; }else{ return true; } } @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name;; } }