/*
* Copyright 1999-2012 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cobar.mysql;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.log4j.Logger;
import com.alibaba.cobar.config.Alarms;
import com.alibaba.cobar.config.model.DataSourceConfig;
import com.alibaba.cobar.heartbeat.MySQLHeartbeat;
import com.alibaba.cobar.mysql.bio.Channel;
import com.alibaba.cobar.mysql.bio.ChannelFactory;
import com.alibaba.cobar.mysql.bio.MySQLChannelFactory;
import com.alibaba.cobar.statistic.SQLRecorder;
import com.alibaba.cobar.util.TimeUtil;
/**
* @author xianmao.hexm 2011-4-26 上午11:12:13
*/
public final class MySQLDataSource {
private static final Logger LOGGER = Logger.getLogger(MySQLDataSource.class);
private static final Logger ALARM = Logger.getLogger("alarm");
private final MySQLDataNode node;
private final int index;
private final String name;
private final DataSourceConfig config;
private int activeCount;
private int idleCount;
private final int size;
private final Channel[] items;
private final ReentrantLock lock;
private final ChannelFactory factory;
private final MySQLHeartbeat heartbeat;
private final SQLRecorder sqlRecorder;
public MySQLDataSource(MySQLDataNode node, int index, DataSourceConfig config, int size) {
this.node = node;
this.index = index;
this.name = config.getName();
this.config = config;
this.size = size;
this.items = new Channel[size];
this.lock = new ReentrantLock();
this.factory = new MySQLChannelFactory();
this.heartbeat = new MySQLHeartbeat(this);
this.sqlRecorder = new SQLRecorder(config.getSqlRecordCount());
}
public MySQLDataNode getNode() {
return node;
}
public int getIndex() {
return index;
}
public String getName() {
return name;
}
public DataSourceConfig getConfig() {
return config;
}
public int size() {
return size;
}
public int getActiveCount() {
return activeCount;
}
public int getIdleCount() {
return idleCount;
}
public MySQLHeartbeat getHeartbeat() {
return heartbeat;
}
public SQLRecorder getSqlRecorder() {
return sqlRecorder;
}
public void startHeartbeat() {
heartbeat.start();
}
public void stopHeartbeat() {
heartbeat.stop();
}
public void doHeartbeat() {
if (!heartbeat.isStop()) {
try {
heartbeat.heartbeat();
} catch (Throwable e) {
LOGGER.error(name + " heartbeat error.", e);
}
}
}
/**
* @return never null
*/
public Channel getChannel() throws Exception {
// 尝试从池中取得可用资源
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 当活跃资源大于等于池大小时,记录告警信息。
if (activeCount >= size) {
StringBuilder s = new StringBuilder();
s.append(Alarms.DEFAULT).append("[name=").append(name).append(",active=");
s.append(activeCount).append(",size=").append(size).append(']');
ALARM.error(s.toString());
}
// 检查池中是否有可用资源
final Channel[] items = this.items;
for (int i = 0; idleCount > 0 && i < items.length; i++) {
if (items[i] != null) {
Channel c = items[i];
items[i] = null;
--idleCount;
if (c.isClosed()) {
continue;
} else {
++activeCount;
return c;
}
}
}
// 将创建新连接,在此先假设创建成功。
++activeCount;
} finally {
lock.unlock();
}
// 创建新的资源
Channel c = factory.make(this);
try {
c.connect(node.getConfig().getWaitTimeout());
} catch (Exception e) {
lock.lock();
try {
--activeCount;
} finally {
lock.unlock();
}
c.closeNoActive();
throw e;
}
return c;
}
public void releaseChannel(Channel c) {
// 状态检查
if (c == null || c.isClosed()) {
return;
}
// 释放资源
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Channel[] items = this.items;
for (int i = 0; i < items.length; i++) {
if (items[i] == null) {
++idleCount;
--activeCount;
c.setLastActiveTime(TimeUtil.currentTimeMillis());
items[i] = c;
return;
}
}
} finally {
lock.unlock();
}
// 关闭多余的资源
c.close();
}
public void deActive() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
--activeCount;
} finally {
lock.unlock();
}
}
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Channel[] items = this.items;
for (int i = 0; i < items.length; i++) {
Channel c = items[i];
if (c != null) {
c.closeNoActive();
--idleCount;
items[i] = null;
}
}
} finally {
lock.unlock();
}
}
public void idleCheck(long timeout) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Channel[] items = this.items;
long time = TimeUtil.currentTimeMillis() - timeout;
for (int i = 0; i < items.length; i++) {
Channel c = items[i];
if (c != null && time > c.getLastAcitveTime()) {
c.closeNoActive();
--idleCount;
items[i] = null;
}
}
} finally {
lock.unlock();
}
}
}