/** * Alipay.com Inc. * Copyright (c) 2004-2012 All Rights Reserved. */ package com.alipay.zdal.client.jdbc.dbselector; import java.sql.SQLException; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import javax.sql.DataSource; import org.apache.log4j.Logger; import com.alipay.zdal.client.jdbc.DBSelector.DataSourceTryer; import com.alipay.zdal.client.jdbc.ZdalStatement.DB_OPERATION_TYPE; public class PriorityGroupsDataSources { private static final Logger logger = Logger .getLogger(PriorityGroupsDataSources.class); private final EquityDbManager equityDbManager; /** * �������������ڿ���ֻ����һ��ҵ���߳�ȥ���ԣ� */ private final ReentrantLock lock = new ReentrantLock(); /** * ����Դ�Ƿ���ã�Ĭ�Ͽ��� */ private volatile boolean isNotAvailable = false; /** * �ϴ�����ʱ�� */ private volatile long lastRetryTime = 0; /** * �쳣���� */ private volatile int exceptionTimes = 0; /** * ��һ�β����쳣��ʱ�䣬��λ���� */ private volatile long firstExceptionTime = 0; /** * ���Թ���db��ʱ������Ĭ��ֵ��Ϊ2s,��λ���� */ private final int retryBadDbInterval = 2000; /** * ��λʱ��Σ�Ĭ��Ϊ1���ӣ�����ͳ��ʱ�����ij��db���쳣�Ĵ�������λ���� */ private final int timeInterval = 60000; /** * ��λʱ���������쳣�Ĵ���������������ֵ�㽫����Դ��Ϊ������ */ private final int allowExceptionTimes = 20; PriorityGroupsDataSources(EquityDbManager equityDbManager) { this.equityDbManager = equityDbManager; } /** * �����ȼ����з��飬����p0��p1�ȣ����ij�����ȼ��ڵ�����Դ���������ˣ����׳�NoMoreDataSourceException * Ȼ����ݵ�λʱ�����׳����쳣�Ĵ����Ƿ񳬹�ij����ֵ�������Ƿ񽫸����ȼ�������Ϊ�����ã���isNotAvailable=true * @param <T> * @param failedDataSources * @param tryer * @param times * @param operationType * @param i * @param args * @return * @throws SQLException */ public <T> T tryExecute(Map<DataSource, SQLException> failedDataSources, DataSourceTryer<T> tryer, int times, DB_OPERATION_TYPE operationType, int i, Object... args) throws SQLException { T t = null; //��������� if (isNotAvailable) { t = tryOnFailedPriorityGroupDataSource(failedDataSources, tryer, times, operationType, i, args); } else { t = tryOnAvailablePriorityGroupDataSource(failedDataSources, tryer, times, operationType, i, args); } return t; } /** * �ڲ����õ����ȼ��Ͻ��в��� * ֻ�����߳̽��뵽�����ȼ��������ԣ�������Գɹ��򽫸����ȼ���Ϊ���ã����ʧ�ܾ��׳��쳣����Χ����ס�쳣���᳢����һ�����ȼ��� * ��������̲߳���������״̬���򲻽������ԣ�ֱ���׳��쳣����Χ�����쳣���漴������һ�����ȼ��� * * @param <T> * @param failedDataSources * @param tryer * @param times * @param operationType * @param i * @param args * @return * @throws SQLException * @throws SQLException */ public <T> T tryOnFailedPriorityGroupDataSource( Map<DataSource, SQLException> failedDataSources, DataSourceTryer<T> tryer, int times, DB_OPERATION_TYPE operationType, int i, Object... args) throws SQLException { T t = null; boolean toTry = System.currentTimeMillis() - lastRetryTime > retryBadDbInterval; //ÿ������룬ֻ����һ��ҵ���̼߳���ʹ��������ȼ�������Դ�� if (toTry && lock.tryLock()) { try { logger.warn("�߳�" + Thread.currentThread().getName() + "��" + getCurrentDateTime(null) + "�������ȼ�" + i + "�ĵ��߳�����״̬��"); Long beginTime = System.currentTimeMillis(); t = equityDbManager .tryExecute(failedDataSources, tryer, times, operationType, args); logger.warn("���߳�" + Thread.currentThread().getName() + "ȥ��ȡ�����ȼ�p" + i + "�ɹ�����ʱΪ��" + (System.currentTimeMillis() - beginTime)); this.isNotAvailable = false; this.exceptionTimes = 0; logger.warn("����Դ���ȼ�p" + i + "��" + getCurrentDateTime(null) + "�Ѿ��ָ������Ϊ���ã�"); } catch (NoMoreDataSourceException e) { logger.error("���߳��������ȼ� p" + i + "ʧ�ܣ�����ȥѰ������һ�����ȼ�������Դ��", e); throw e; } finally { lastRetryTime = System.currentTimeMillis(); lock.unlock(); } } else { //�����ϳ��Ը����ȼ���������ֱ���׳��쳣��������Χforѭ��ȥ������һ�����ȼ� logger.warn("���ȼ�p" + i + "���ڱ������̷߳��ʣ����߲�����2s��ʱ������"); throw new NoMoreDataSourceException("No more dataSource for p" + i); } return t; } /** * �ڿ��õ����ȼ��Ͻ��в����� * ��������ȼ������쳣�������ͳ�Ƶ�λʱ���ڵ��쳣�������������ָ������ֵ���򽫸����ȼ���Ϊ������ * ͳ�����쳣�������쳣�׳�����Χ�Ჶ����쳣���漴ѡ����һ�����ȼ�������Դ���в��� * @param <T> * @param failedDataSources * @param tryer * @param times * @param operationType * @param i * @param args * @return * @throws SQLException */ public <T> T tryOnAvailablePriorityGroupDataSource( Map<DataSource, SQLException> failedDataSources, DataSourceTryer<T> tryer, int times, DB_OPERATION_TYPE operationType, int i, Object... args) throws SQLException { T t = null; try { t = equityDbManager.tryExecute(failedDataSources, tryer, times, operationType, args); } catch (NoMoreDataSourceException e) { calcFailedDSExceptionTimes(i); throw e; } catch (IllegalStateException e) {//����ڱ������Ҳ��������׳�NoMoreDataSourceException�쳣�����ڴ���һ��EquityDbManager�м���ִ��. throw new NoMoreDataSourceException(e.getMessage()); } return t; } /** * ͳ�����ȼ�pi���쳣���� * С��ָ��ʱ�������ۼ��쳣����������쳣��������ij����ֵ�͸����ȼ�����Ϊ�����ã������������¼��� * @param i */ public synchronized void calcFailedDSExceptionTimes(int i) { //ͳ�Ƹ÷����ڵĵ�λʱ�����쳣�Ĵ��� if (this.exceptionTimes == 0) { this.firstExceptionTime = System.currentTimeMillis(); } long currentTime = System.currentTimeMillis(); if (currentTime - this.firstExceptionTime <= timeInterval) { ++exceptionTimes; logger.error("���ȼ�p" + i + "��λʱ���ڵ�" + exceptionTimes + "���쳣����ǰʱ�䣺" + getCurrentDateTime(currentTime) + "���״��쳣ʱ�䣺" + getCurrentDateTime(firstExceptionTime) + "��ʱ����Ϊ��" + (currentTime - firstExceptionTime) + "ms."); if (exceptionTimes >= allowExceptionTimes) { this.isNotAvailable = true; logger.error("���ȼ�p" + i + "��ʱ��" + getCurrentDateTime(null) + "���߳���"); } } else { logger.warn("ͳ���쳣����������λʱ����,�ϴε�λʱ�������쳣����Ϊ" + exceptionTimes + "��,���ڿ�ʼ���¼�����"); this.exceptionTimes = 0; } } /** * ��ȡ��ǰ��ʱ��ĸ�ʽ���ַ��� */ public String getCurrentDateTime(Long time) { java.util.Date now; if (time != null) { now = new java.util.Date(time); } else { now = new java.util.Date(); } java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.format(now); } public EquityDbManager getEquityDbManager() { return equityDbManager; } }