使用 HttpDns 与 传统DNS 不同的是,HttpDns 是使用 Http协议去进行 dns 解析请求,将服务器返回的结果,也就是域名对应的服务器ip 作为我们发起请求的地址,替换使用域名
LocalDNS劫持: 由于HttpDNS是通过ip直接请求http获取服务器A记录地址,不存在向本地运营商询问domain解析过程,所以从根本避免了劫持问题。 (对于http内容tcp/ip层劫持,可以使用验证因子或者数据加密等方式来保证传输数据的可信度)
平均访问延迟下降: 由于是ip直接访问省掉了一次domain解析过程,(即使系统有缓存速度也会稍快一些‘毫秒级’)通过智能算法排序后找到最快节点进行访问。
用户连接失败率下降: 通过算法降低以往失败率过高的服务器排序,通过时间近期访问过的数据提高服务器排序,通过历史访问成功记录提高服务器排序。如果ip(a)访问错误,在下一次返回ip(b)或者ip(c) 排序后的记录。(LocalDNS很可能在一个ttl时间内(或多个ttl)都是返回记录
使用OkHttp 简单配置 HttpDns服务
方式一
在 **HttpInterceptor** 中对 host域名进行ip的替换
//工具类
public class HttpDNSUtil {
/**
* 转换url 主机头为ip地址
*
* @param url 原url
* @param host 主机头
* @param ip 服务器ip
* @return
*/
public static String getIpUrl(String url, String host, String ip) {
if (url == null) {
Log.e("TAG", "URL NULL");
}
if (host == null) {
Log.e("TAG", "host NULL");
}
if (ip == null) {
Log.e("TAG", "ip NULL");
}
if (url == null || host == null || ip == null) return url;
String ipUrl = url.replaceFirst(host, ip);
return ipUrl;
}
/**
* 根据url获得ip,此方法只是最简单的模拟,实际情况很复杂,需要做缓存处理
*
* @param host
* @return
*/
public static String getIPByHost(String host) {
HttpUrl httpUrl = new HttpUrl.Builder()
.scheme("http")
.host("203.107.1.1")
.addPathSegment("d")
.addQueryParameter("host", host)
.build();
//与我们正式请求独立,所以这里新建一个OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(httpUrl)
.get()
.build();
try {
String result = null;
/**
* 子线程中同步去获取
*/
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
String body = response.body().string();
JSONObject jsonObject = new JSONObject(body);
JSONArray ips = jsonObject.optJSONArray("ips");
if (ips != null) {
result = ips.optString(0);
}
}
return result;
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
//拦截器
public class HttpDNSInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originRequest = chain.request();
HttpUrl httpUrl = originRequest.url();
String url = httpUrl.toString();
String host = httpUrl.host();
Log.e("HttpDNS", "origin url:" + url);
Log.e("HttpDNS", "origin host:" + host);
String hostIP = HttpDNSUtil.getIPByHost(host);
Request.Builder builder = originRequest.newBuilder();
if (hostIP != null) {
builder.url(HttpDNSUtil.getIpUrl(url, host, hostIP));
builder.header("host", hostIP);
Log.e("HttpDNS", "the host has replaced with ip " + hostIP);
} else {
Log.e("HttpDNS", "can't get the ip , can't replace the host");
}
Request newRequest = builder.build();
Log.e("HttpDNS", "newUrl:" + newRequest.url());
Response newResponse = chain.proceed(newRequest);
return newResponse;
}
}
// 为okHttpClient 添加拦截器
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.interceptors().add(new HttpDNSInterceptor());
OkHttpClient okHttpClient = builder.build();
方式二
使用 OkHttpClient 的 setDns(Dns)方法
(腾讯有免费的 HttpDns 服务,可以去它的网站查看相关资料)
//重载 Dns的lookup 方法,根据host 返回 InetAddress
@Override
public List<InetAddress> lookup(String host) throws UnknownHostException {
if(host == null) {
throw new UnknownHostException("host == null");
} else {
// HttpDNS 是自己编写的一个 帮助类,根据host 返回ips
String[] ips = HttpDNS.getAddressesByName(host);
if(Utils.isArrayEmpty(ips)) {
return Arrays.asList(InetAddress.getAllByName(host));
}
compareWithHistory(host, ips);
List<InetAddress> addresses = new ArrayList<InetAddress>();
for (int i = 0; i < ips.length; i++) {
addresses.add(InetAddress.getByName(ips[i]));
}
return addresses;
}
}