package com.sina.util.dnscache;
import android.annotation.SuppressLint;
import android.content.Context;
import android.net.NetworkInfo;
import android.text.TextUtils;
import com.sina.util.dnscache.cache.DnsCacheManager;
import com.sina.util.dnscache.cache.IDnsCache;
import com.sina.util.dnscache.dnsp.DnsManager;
import com.sina.util.dnscache.dnsp.IDns;
import com.sina.util.dnscache.log.HttpDnsLogManager;
import com.sina.util.dnscache.model.DomainModel;
import com.sina.util.dnscache.model.HttpDnsPack;
import com.sina.util.dnscache.model.IpModel;
import com.sina.util.dnscache.net.ApacheHttpClientNetworkRequests;
import com.sina.util.dnscache.net.networktype.Constants;
import com.sina.util.dnscache.net.networktype.NetworkManager;
import com.sina.util.dnscache.net.networktype.NetworkStateReceiver;
import com.sina.util.dnscache.query.IQuery;
import com.sina.util.dnscache.query.QueryManager;
import com.sina.util.dnscache.score.IScore;
import com.sina.util.dnscache.score.ScoreManager;
import com.sina.util.dnscache.speedtest.ISpeedtest;
import com.sina.util.dnscache.speedtest.SpeedtestManager;
import com.sina.util.dnscache.thread.RealTimeThreadPool;
import java.io.File;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* 项目名称: DNSCache <br>
* 类名称: DNSCache <br>
* 类描述: Lib库全局 对外实例对象 <br>
* 创建人: fenglei <br>
* 创建时间: 2015-3-26 下午5:26:04 <br>
*
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
*
* @version V1.0
*/
@SuppressLint("NewApi")
public class DNSCache {
// ///////////////////////////////////////////////////////////////////////////////////
public static boolean isEnable = true;
public static int timer_interval = 60 * 1000;
private static DNSCache Instance = null;
private static Context sContext;
private static Object lock = new Object();
public IDnsCache dnsCacheManager = null;
public IQuery queryManager = null;
public IScore scoreManager = null;
public IDns dnsManager = null;
public ISpeedtest speedtestManager = null;
public DNSCache(Context ctx) {
dnsCacheManager = new DnsCacheManager(ctx);
queryManager = new QueryManager(dnsCacheManager);
scoreManager = new ScoreManager();
dnsManager = new DnsManager();
speedtestManager = new SpeedtestManager();
startTimer();
}
public static DNSCache getInstance() {
if (null == Instance) {
synchronized (lock) {
if (Instance == null) {
Instance = new DNSCache(sContext);
}
}
}
return Instance;
}
public static void Init(Context ctx) {
if (null == ctx){
throw new RuntimeException("DNSCache Init; context can not be null!!!");
}
sContext = ctx.getApplicationContext();
// 根据配置文件 初始化策略
DNSCacheConfig.InitCfg(sContext);
NetworkManager.CreateInstance(sContext);
AppConfigUtil.init(sContext);
NetworkStateReceiver.register(sContext);
Instance = null;
}
/**
* 预加载逻辑
*
* @param domains
*/
public void preLoadDomains(final String[] domains) {
for (String domain : domains) {
checkUpdates(domain, true);
}
}
public IDnsCache getDnsCacheManager() {
return dnsCacheManager;
}
// ///////////////////////////////////////////////////////////////////////////////////
/**
* 获取 HttpDNS信息
*
* @param url
* 传入的Url
* @return 返回排序后的可直接使用接口
*/
public DomainInfo[] getDomainServerIp(String url) {
String host = Tools.getHostName(url);
if (isEnable) {
if (!TextUtils.isEmpty(host) && Tools.isIPV4(host)) {
DomainInfo[] info = new DomainInfo[1];
info[0] = new DomainInfo("", url, "");
return info;
}
// 查询domain对应的server ip数组
final DomainModel domainModel = queryManager.queryDomainIp(String.valueOf(NetworkManager.getInstance().getSPID()), host);
// 如果本地cache 和 内置数据都没有 返回null,然后马上查询数据
if (null == domainModel || domainModel.id == -1) {
this.checkUpdates(host, true);
if (null == domainModel) {
return null;
}
}
HttpDnsLogManager.getInstance().writeLog(HttpDnsLogManager.TYPE_INFO, HttpDnsLogManager.ACTION_INFO_DOMAIN,domainModel.tojson(), true);
ArrayList<IpModel> result = filterInvalidIp(domainModel.ipModelArr);
String[] scoreIpArray = scoreManager.ListToArr(result);
if (scoreIpArray == null || scoreIpArray.length == 0) {
return null; // 排序错误 终端后续流程
}
// 转换成需要返回的数据模型
DomainInfo[] domainInfoList = DomainInfo.DomainInfoFactory(scoreIpArray, url, host);
return domainInfoList;
} else {
return null;
}
}
/**
* 过滤无效ip数据
* @param ipModelArr
* @return
*/
private ArrayList<IpModel> filterInvalidIp(ArrayList<IpModel> ipModelArr) {
ArrayList<IpModel> result = new ArrayList<IpModel>();
for (int i = 0; i < ipModelArr.size(); i++) {
IpModel ipModel = ipModelArr.get(i);
if (!("" + SpeedtestManager.MAX_OVERTIME_RTT).equals(ipModel.rtt)) {
result.add(ipModel);
}
}
return result;
}
private boolean isSupport(String host) {
return (DNSCacheConfig.domainSupportList.size() == 0) || (DNSCacheConfig.domainSupportList.contains(host));
}
// ///////////////////////////////////////////////////////////////////////////////////
private ConcurrentHashMap<String, UpdateTask> mRunningTasks = new ConcurrentHashMap<String, UpdateTask>();
/**
* 从httpdns 服务器重新拉取数据
*
* @param domain
*/
private void checkUpdates(String domain, boolean speedTest) {
if (isSupport(domain)) {
final String host = domain;
final boolean needSpeedTest = speedTest;
UpdateTask task = mRunningTasks.get(host);
if (null == task) {
UpdateTask updateTask = new UpdateTask(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName("Get Http Dns Data");
getHttpDnsData(host);
mRunningTasks.remove(host);
if (needSpeedTest) {
RealTimeThreadPool.getInstance().execute(new SpeedTestTask());
}
}
});
mRunningTasks.put(host, updateTask);
updateTask.start();
} else {
long beginTime = task.getBeginTime();
long now = System.currentTimeMillis();
// 上次拉取超时,这次再开一个线程继续
if (now - beginTime > 30 * 1000) {
task.start();
}
}
}
}
static class UpdateTask {
public Runnable runnable;
public long beginTime;
public UpdateTask(Runnable runnable) {
super();
this.runnable = runnable;
this.beginTime = System.currentTimeMillis();
}
public void start() {
Thread thread = new Thread(runnable);
thread.start();
}
public long getBeginTime() {
return beginTime;
}
}
/**
* 根据 host 更新数据
*
* @param host
*/
private final DomainModel getHttpDnsData(String host) {
// 获取 httpdns 数据
HttpDnsPack httpDnsPack = dnsManager.requestDns(host);
if (httpDnsPack == null) {
return null; // 没有从htppdns服务器获取正确的数据。必须中断下面流程
}
HttpDnsLogManager.getInstance().writeLog(HttpDnsLogManager.TYPE_INFO, HttpDnsLogManager.ACTION_INFO_DOMAIN, httpDnsPack.toJson(),
true);
// 插入本地 cache
DomainModel domainModel = dnsCacheManager.insertDnsCache(httpDnsPack);
return domainModel;
}
// ///////////////////////////////////////////////////////////////////////////////////
/**
* 定时器休眠时间
*/
public final int sleepTime = timer_interval;
/**
* 启动定时器
*/
private void startTimer() {
timer = new Timer();
timer.schedule(task, 0, sleepTime);
}
/**
* 定时器Obj
*/
private Timer timer = null;
/**
* TimerTask 运行时间
*/
public long TimerTaskOldRunTime = 0;
/**
* 上次测速时间
*/
private long lastSpeedTime;
/**
* 上次日志上传时间
*/
private long lastLogTime;
/**
* 定时器还多久启动
*/
public long getTimerDelayedStartTime() {
return (sleepTime - (System.currentTimeMillis() - TimerTaskOldRunTime)) / 1000;
}
/**
* 定时器任务
*/
private TimerTask task = new TimerTask() {
@Override
public void run() {
TimerTaskOldRunTime = System.currentTimeMillis();
//无网络情况下不执行任何后台任务操作
if (NetworkManager.Util.getNetworkType() == Constants.NETWORK_TYPE_UNCONNECTED || NetworkManager.Util.getNetworkType() == Constants.MOBILE_UNKNOWN) {
return;
}
/************************* 更新过期数据 ********************************/
Thread.currentThread().setName("HTTP DNS TimerTask");
final ArrayList<DomainModel> list = dnsCacheManager.getExpireDnsCache();
for (DomainModel model : list) {
checkUpdates(model.domain, false);
}
long now = System.currentTimeMillis();
/************************* 测速逻辑 ********************************/
if (now - lastSpeedTime > SpeedtestManager.time_interval - 3) {
lastSpeedTime = now;
RealTimeThreadPool.getInstance().execute(new SpeedTestTask());
}
/************************* 日志上报相关 ********************************/
now = System.currentTimeMillis();
if (HttpDnsLogManager.LOG_UPLOAD_SWITCH && now - lastLogTime > HttpDnsLogManager.time_interval) {
lastLogTime = now;
// 判断当前是wifi网络才能上传
if (NetworkManager.Util.getNetworkType() == Constants.NETWORK_TYPE_WIFI) {
RealTimeThreadPool.getInstance().execute(new LogUpLoadTask());
}
}
}
};
class SpeedTestTask implements Runnable {
public void run() {
ArrayList<DomainModel> list = dnsCacheManager.getAllMemoryCache();
updateSpeedInfo(list);
}
private void updateSpeedInfo(ArrayList<DomainModel> list) {
for (int m = 0; m < list.size(); m++) {
DomainModel domainModel = list.get(m);
if (domainModel == null) {
continue;
}
ArrayList<IpModel> ipArray = domainModel.ipModelArr;
if (ipArray == null || ipArray.size() < 1) {
continue;
}
for (int i = 0; i < ipArray.size(); i++) {
IpModel ipModel = ipArray.get(i);
int rtt = speedtestManager.speedTest(ipModel.ip, domainModel.domain);
boolean succ = rtt > SpeedtestManager.OCUR_ERROR;
if (succ) {
ipModel.rtt = String.valueOf(rtt);
ipModel.success_num = String.valueOf((Integer.valueOf(ipModel.success_num) + 1));
ipModel.finally_success_time = String.valueOf(System.currentTimeMillis());
} else {
ipModel.rtt = String.valueOf(SpeedtestManager.MAX_OVERTIME_RTT);
ipModel.err_num = String.valueOf((Integer.valueOf(ipModel.err_num) + 1));
ipModel.finally_fail_time = String.valueOf(System.currentTimeMillis());
}
}
scoreManager.serverIpScore(domainModel);
try {
dnsCacheManager.setSpeedInfo(ipArray);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
class LogUpLoadTask implements Runnable {
public void run() {
File logFile = HttpDnsLogManager.getInstance().getLogFile();
if (null == logFile || !logFile.exists()) {
return;
}
boolean succ = false;
// upload
try {
succ = ApacheHttpClientNetworkRequests.upLoadFile(HttpDnsLogManager.LOG_UPLOAD_API, logFile);
} catch (Exception e) {
e.printStackTrace();
}
// succ
if (succ) {
logFile.delete();
}
}
}
// ///////////////////////////////////////////////////////////////////////////////////
/**
* 网络环境发生变化 刷新缓存数据 暂时先不需要 预处理逻辑。 用户直接请求的时候会更新数据。 (会有一次走本地dns ,
* 后期优化这个方法,主动请求缓存的数据)
*
* @param networkInfo
*/
public void onNetworkStatusChanged(NetworkInfo networkInfo) {
if (null != dnsCacheManager) {
dnsCacheManager.clearMemoryCache();
}
}
// ///////////////////////////////////////////////////////////////////////////////////
}