美文网首页Android进阶安卓网络
基于OkHttp3 加入HttpDns功能

基于OkHttp3 加入HttpDns功能

作者: GhostInMatrix | 来源:发表于2018-01-30 18:52 被阅读486次

什么是HttpDns

HttpDns是通过网络请求的方式,获取即将发送的业务请求所需要的ip地址。

为什么要用HttpDns

在使用HttpDns时,android发送网络请求时会请求本地dns或本地运营商 的dns服务获取目标ip,但是一旦你使用的这个默认的dns不靠谱,不受信任,则请求稳定性将会降低,甚至可能被劫持。
因此,如果能够使用自己信任的dns服务器做dns域名解析,将大大降低这种风险。

基于OkHttp3如何定制?

image.png

OkHttp3一大亮点在其强大的Interceptor机制。因此HttpDns在整个request发射过程中就有了两个结合点:

  1. 使用Interceptor,直接将域名替换为ip地址
  2. 使用OkHttp提供的dns接口,新建Dns子类,实现lookup()方法。

使用Interceptor做ip直连,则会存在以下优点:

  • 对Dns的控制偏上层,可更加细化,控制灵活。
  • 容灾处理更容易
    但也会存在比较致命的缺点,一切跟域名有关的处理全部失效,具体有:
  • 在Https下处理SSL证书会出现校验问题
  • ip访问时出现Cookie校验问题。

若使用OkHttp自带的dns(),优点在于:

  • Https下不会存在证书校验问题,保证流程正常执行
  • 种Cookie时不会存在问题
    缺点在于:
  • 时机过于底层,容灾控制都不方便。
  • okhttp自身存在缓存,一旦dns自身ttl过期,okhttp缓存有可能还在使用,会存在一定的风险。

综上来看,使用OkHttp原生Dns接口更加科学。除非不要求Cookie,不使用Https,使用Interceptor做简单的场景才比较合适。

实现

image.png
setDNS(new Dns() {
     @Override
      public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
List<InetAddress> result = DnsManager.getInstance().getIps(hostname);
            if (result == null)
            result = new ArrayList<>();
            return result;
          }
     })

OkHttp内使用RouteDatabase进行每次使用ip的监控和反馈:

public final class RouteDatabase {
  private final Set<Route> failedRoutes = new LinkedHashSet<>();

  /** Records a failure connecting to {@code failedRoute}. */
  public synchronized void failed(Route failedRoute) {
    failedRoutes.add(failedRoute);
  }

  /** Records success connecting to {@code route}. */
  public synchronized void connected(Route route) {
    failedRoutes.remove(route);
  }

  /** Returns true if {@code route} has failed recently and should be avoided. */
  public synchronized boolean shouldPostpone(Route route) {
    return failedRoutes.contains(route);
  }
}

为了能够复用shouldPostpone()获取okhttp自身对ip可用性的判断结果,使得自定义manager对ip的可用性的判断与okhttp一致,从而更新Manager自身的ip名单,可以通过
Internal.instance.routeDatabase(getConnectionPool());得到RouteDatabase对象。

Internal.instance在OkHttpClient实例化之后就被赋值,事实上,Internal.instance就是OkHttpClient。因此可以使用NetworkInterceptor获取RouteDatabase。即:

addNetworkInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        try {

                            Connection connection = chain.connection();
                            RouteDatabase routeDatabase = Internal.instance.routeDatabase(getConnectionPool());
                            Route route = connection.route();
                            Log.d("GI", "Route:" + route + "=======Can Trust?:" + !routeDatabase.shouldPostpone(route));
                            if (routeDatabase.shouldPostpone(route)) {
                                DnsManager.getInstance().putToBlackList(route.socketAddress().getAddress().getHostName(), route.socketAddress().getAddress().getHostAddress());
                                DnsManager.getInstance().removeFromWhiteList(route.socketAddress().getAddress().getHostName(), route.socketAddress().getAddress().getHostAddress());
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return chain.proceed(request);
                    }
                })

Manager内维护两个名单Map,

  • 白名单:存储Dns下发的ip及经过检验可靠的ip
  • 黑名单:存储使用过程中不可靠的ip
    根据每次的dns请求和RouteDatabase进行反馈更新。

附:OkHttp 链式调用原理

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

interceptors.addAll(client.interceptors())是将addInterceptor时的所有Interceptor加入列表,然后再加入OkHttpCore核心处理Interceptor。
如果本次请求是一个需要走网络的请求,还会继续添加addNetInterceptor时所有的Interceptor加入列表。最后才加入CallServerInterceptor用来处理真正的网络请求。
这个顺序能够保证在递归调用过程中,自定义拦截器只会影响到OkHttpCore处理流程之前或者之后,而Core内的核心流程不会受到影响。

image.png

相关文章

  • 基于OkHttp3 加入HttpDns功能

    什么是HttpDns HttpDns是通过网络请求的方式,获取即将发送的业务请求所需要的ip地址。 为什么要用Ht...

  • Android框架のOkhttp-com.zhousf.lib

    OkHttp3 基于OkHttp3封装的网络请求库 功能点 支持Http/Https协议/自定义Https证书认证...

  • 🔑 也谈 HTTPS - 如何内测

    在上篇文章 ? 也谈 HTTPS - HTTPDNS + HTTPS 中,我们谈了如何基于 HTTPDNS 来部署...

  • HttpDNS功能说明及实现

    功能说明 HTTPDNS使用HTTP协议进行域名解析,代替现有基于UDP的DNS协议,域名解析请求直接发送到阿里云...

  • Okhttp3源码分析

    okhttp3源码分析基于okhttp3.10.0。 关于okhttp的特点及相关功能的介绍可以查看官网的介绍: ...

  • Retrofit2源码解析(一)

    本文基于Retrofit2的2.4.0版本 Retrofit2底层基于OkHttp3,是对利用OkHttp3请求网...

  • OkHttp3源码解析(一)之请求流程

    本文基于OkHttp3的3.11.0版本 OkHttp3发起请求方式 我们用OkHttp3发起一个网络请求一般是这...

  • 移动端网页的优化

    1 网络节点优化(HTTPDNS) 移动解析HttpDNS CloudXNS HttpDNS 【鹅厂网事】全局精确...

  • Retrofit2源码解析

    Retrofit网络请求的功能实现是okhttp3,由okhttp3做真正的网络请求。Retrofit2框架和其他...

  • okhttp3的拦截器机制

    okhttp3是目前使用广泛的http调用工具,其基于拦截器的请求处理机制为开发者扩展功能提供了方便,比如通过自定...

网友评论

    本文标题:基于OkHttp3 加入HttpDns功能

    本文链接:https://www.haomeiwen.com/subject/uymczxtx.html