package com.jackson.db.service;
import com.jackson.db.SqlUtil;
import com.jackson.db.dao.ProxyDao;
import com.jackson.db.po.Proxy;
import com.jackson.executor.DBSingleExecutor;
import com.jackson.utils.ListUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by Jackson on 2017/1/13.
*/
public class ProxyService implements IDaoService<Proxy>{
private static Logger logger = LogManager.getLogger(ProxyService.class.getName());
private final SqlSessionFactory factory;
private final String tableName;
private final HashSet insertProxysSet;
private final LinkedList insertProxysList;
private final HashSet updateProxysSet;
private final LinkedList updateProxysList;
private final TakeMethod takeMethod;
private int proxySize = 100;
private int minProxyCatch = 20;
private final LinkedBlockingQueue<Proxy> queue;
/**
* 设置每次从数据库读取条数
* @param proxySize 每次读取条数
* @return
*/
public ProxyService setProxySize(int proxySize) {
this.proxySize = proxySize;
return this;
}
/**
* 设置最小缓存,如果低于这个值,就去数据库取数据
* @param minProxyCatch
* @return
*/
public ProxyService setMinProxyCatch(int minProxyCatch) {
this.minProxyCatch = minProxyCatch;
return this;
}
/**
*
* @param tableName 创建的表名
* @param takeMethod 根据什么条件取出数据
*/
public ProxyService(String tableName,TakeMethod takeMethod) {
this.tableName = tableName;
this.takeMethod = takeMethod;
queue = new LinkedBlockingQueue<>();
factory = SqlUtil.getFactory();
insertProxysSet = new HashSet<Proxy>();
insertProxysList = new LinkedList<Proxy>();
updateProxysSet = new HashSet<>();
updateProxysList = new LinkedList<>();
init();
}
public String getTableName(){
return this.tableName;
}
/**
* 添加到数据库
* @param proxys
*/
public void insert(List<Proxy> proxys){
if(ListUtil.isEmpty(proxys))return;
synchronized(insertProxysSet){
//这里的作用是为了唤醒操作queue的线程
if(queue.size()==0){
Proxy proxy = proxys.remove(proxys.size() - 1);
proxy.setState(Proxy.STATE_TAKEN_OUT);
queue.offer(proxy);
}
insertProxysSet.addAll(proxys);
logger.info("表{} 添加新Proxy size:{} insertProxysSet size{}",tableName,proxys.size(),insertProxysSet.size());
DBSingleExecutor.instance.execute(new InsertTask());
}
}
/**
* 添加到数据库
* @param proxy
*/
public void insert(Proxy proxy){
synchronized(insertProxysSet){
if(queue.size()==0){
//这里的作用是为了唤醒操作queue的线程
proxy.setState(Proxy.STATE_TAKEN_OUT);
queue.offer(proxy);
}
insertProxysSet.add(proxy);
logger.debug("表{} 添加新ProxyinsertProxysSet size{}",tableName,insertProxysSet.size());
DBSingleExecutor.instance.execute(new InsertTask());
}
}
private void init(){
createTable();
setTask2Free();
}
private void createTable() {
SqlSession sqlSession = null;
try {
sqlSession = factory.openSession();
ProxyDao mapper = sqlSession.getMapper(ProxyDao.class);
mapper.createTable(tableName);
sqlSession.commit();
} catch (Exception e){
logger.error("表{} {}",tableName,e.toString());
} finally{
sqlSession.close();
}
}
private void setTask2Free() {
SqlSession sqlSession = null;
try {
sqlSession = factory.openSession();
ProxyDao mapper = sqlSession.getMapper(ProxyDao.class);
mapper.setTakeState2Free(tableName);
sqlSession.commit();
} catch (Exception e){
logger.error("表{} {}",tableName,e.toString());
} finally{
sqlSession.close();
}
}
/**
* 存入数据库
* @param Proxys
*/
private void insertProxysToDatabase(List<Proxy> Proxys) {
logger.info("表{} Proxy存入数据库 insertProxysSet size:{}",tableName, Proxys.size());
SqlSession sqlSession = null;
try {
sqlSession = factory.openSession();
ProxyDao mapper = sqlSession.getMapper(ProxyDao.class);
mapper.insertsIgnore(tableName,Proxys);
sqlSession.commit();
} catch (Exception e) {
logger.error("表{} {}",tableName,e.toString());
} finally {
sqlSession.close();
}
}
private void updateProxysToDatabase(List<Proxy> Proxys) {
logger.info("表{} 更新Proxy size is {}",tableName, updateProxysSet.size());
SqlSession sqlSession = null;
try {
sqlSession = factory.openSession();
ProxyDao mapper = sqlSession.getMapper(ProxyDao.class);
for (Proxy ProxyTemp : Proxys) {
mapper.update(tableName,ProxyTemp);
}
sqlSession.commit();
} catch (Exception e) {
logger.error("表{} {}",tableName,e.toString());
} finally {
sqlSession.close();
}
}
/**
* 带有id的proxy才能被更新,也就是说只有从数据库里读取出来的proxy调用此方法才有效,不建议手动设置id
* @param Proxy
*/
public void updateProxy(Proxy Proxy) {
logger.debug("表{} 更新Proxy Proxy:{}:{}",tableName,Proxy.getHost(),Proxy.getPort());
synchronized (updateProxysSet){
updateProxysSet.add(Proxy);
}
DBSingleExecutor.instance.execute(new UpdateTask());
}
//task线程执行
private void updateProxysToDatabase(){
synchronized (updateProxysSet){
if(updateProxysSet.size()>0){
updateProxysList.addAll(updateProxysSet);
updateProxysToDatabase(updateProxysList);
updateProxysSet.clear();//存入数据库方法成功后才会清空
updateProxysList.clear();
}
}
}
//task线程执行
private void insertProxysToDatabase(){
synchronized (insertProxysSet){
if(insertProxysSet.size()>0){
insertProxysList.addAll(insertProxysSet);
insertProxysToDatabase(insertProxysList);
insertProxysSet.clear();//存入数据库方法成功后才会清空
insertProxysList.clear();
}
}
}
/**
* 非阻塞
* @return
*/
public Proxy take() {
return poll();
}
private Proxy poll(){
if (queue.size() < minProxyCatch) {
synchronized (this){
if(queue.size()<minProxyCatch){
DBSingleExecutor.instance.execute(new Database2Queue(takeMethod));
}
}
}
logger.info("表{} queue size:{}",tableName, queue.size());
return queue.poll();
}
private class UpdateTask extends DBSingleExecutor.DBTask{
@Override
public void run() {
updateProxysToDatabase();
}
}
private class InsertTask extends DBSingleExecutor.DBTask {
@Override
public void run() {
insertProxysToDatabase();
}
}
private class Database2Queue extends DBSingleExecutor.DBTask {
private final TakeMethod method;
public Database2Queue(TakeMethod method) {
this.method = method;
}
@Override
public void run() {
databaseToQueue(method);
}
}
private DatabaseToQueueHandler handler;
/**
* 当从数据库里取出数据后的回调,这个回调在数据存入queue之前
* @param handler
*/
public void setDatabaseToQueueHandler(DatabaseToQueueHandler handler){
this.handler = handler;
}
public interface DatabaseToQueueHandler{
void handle(List<Proxy> list,LinkedBlockingQueue queue);
}
/**
* 从数据库读取proxy 到测试队列中
* @param method
*/
private synchronized void databaseToQueue(TakeMethod method) {
SqlSession sqlSession = null;
try {
sqlSession = factory.openSession();
ProxyDao mapper = sqlSession.getMapper(ProxyDao.class);
List<Proxy> proxys = method.getProxyFromDatabase(tableName,mapper,proxySize);
logger.debug("表{} 数据库取出的 Proxys size:{}",tableName, proxys.size());
if(handler!=null)handler.handle(proxys,queue);
if (proxys.size() > 0) {
mapper.update2TakenOut(tableName,proxys);
sqlSession.commit();
queue.addAll(proxys);
}
logger.debug("表{} 当前缓存proxys queue size:{}",tableName, queue.size());
} catch (Exception e) {
logger.error("表{} error:{}",tableName,e.toString());
} finally {
if (sqlSession != null)
sqlSession.close();
}
}
public enum TakeMethod{
/**
* 取出测试次数最少的
*/
MIN_TEST_TIME {
@Override
public List<Proxy> getProxyFromDatabase(String tableName, ProxyDao mapper, int size) {
return mapper.findFreeMinTestTime(tableName, size);
}
},
/**
* 取出速度最快的
*/
MAX_SPEED{
@Override
public List<Proxy> getProxyFromDatabase(String tableName, ProxyDao mapper, int size) {
return mapper.findFreeMaxSpeed(tableName,size);
}
};
abstract List<Proxy> getProxyFromDatabase(String tableName, ProxyDao mapper, int size);
}
}