编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

    • 库的解读

      • README
      • LeakCanary内存收集
      • Glide图片加载设计
      • OkHttp网络框架设计
        • 01.整体概述介绍
          • 1.1 概述介绍
          • 1.2 核心特性说明
          • 1.3 技术架构概览
          • 1.4 问题思考
        • 02.核心架构设计
          • 2.1 整体架构设计
          • 2.2 整体设计思路
          • 2.3 核心组件关系图
        • 03.核心组件详解
          • 3.1 OkHttpClient
          • 3.2 Request请求封装
          • 3.3 Call请求执行接口
          • 3.4 Dispatcher调度器
          • 3.5 拦截器机制
          • 3.6 Response返回
        • 04.核心流程分析
          • 4.1 请求执行流程
          • 4.2 连接管理流程
          • 4.3 缓存处理流程
        • 05.关键技术特性
          • 5.1 连接池管理
          • 5.2 HTTP/2支持
          • 5.3 缓存机制设计
          • 5.4 安全特性设计
          • 5.5 SSL/TLS流程
        • 06.性能优化策略
          • 6.1 连接复用
          • 6.2 请求合并
          • 6.3 压缩传输
          • 6.4 内存管理
        • 07.使用示例说明
          • 7.1 基本使用
          • 7.2 高级配置
          • 7.3 统计请求耗时
        • 10.优秀代码设计深度解析
          • 10.1 责任链模式的极致运用
          • 10.2 不可变对象与建造者模式
          • 10.3 Dispatcher调度器的设计精髓
          • 10.4 连接池的引用计数设计
          • 10.5 Okio的Segment池化设计
          • 10.6 设计思想总结
      • EventBus事件总设计
      • ARouter路由实践设计
    • 专栏博客

  • iOS开发和进阶

  • Web开发和进阶

  • Linux应用开发

  • IoT智能硬件开发

  • Apps
  • Android提升进阶
  • 库的解读
杨充
2025-06-24
目录

OkHttp网络框架设计

# 03.OkHttp网络框架设计

# 目录介绍

  • 01.整体概述介绍
    • 1.1 概述介绍
    • 1.2 核心特性说明
    • 1.3 技术架构概览
    • 1.4 问题思考
  • 02.核心架构设计
    • 2.1 整体架构设计
    • 2.2 整体设计思路
    • 2.3 核心组件关系图
  • 03.核心组件详解
    • 3.1 OkHttpClient
    • 3.2 Request请求封装
    • 3.3 Call请求执行接口
    • 3.4 Dispatcher调度器
    • 3.5 拦截器机制
    • 3.6 Response返回
  • 04.核心流程分析
    • 4.1 请求执行流程
    • 4.2 连接管理流程
    • 4.3 缓存处理流程
  • 05.关键技术特性
    • 5.1 连接池管理
    • 5.2 HTTP/2支持
    • 5.3 缓存机制设计
    • 5.4 安全特性设计
    • 5.5 SSL/TLS流程
  • 06.性能优化策略
    • 6.1 连接复用
    • 6.2 请求合并
    • 6.3 压缩传输
    • 6.4 内存管理
  • 07.使用示例说明
    • 7.1 基本使用
    • 7.2 高级配置
    • 7.3 统计请求耗时
  • 08.最佳实践
    • 8.1 客户端复用
    • 8.2 请求取消
    • 8.3 错误处理
  • 09.架构优势
    • 9.1 设计模式应用
    • 9.2 SOLID原则体现
    • 9.3 性能优势
  • 10.优秀代码设计深度解析
    • 10.1 责任链模式的极致运用
    • 10.2 不可变对象与建造者模式
    • 10.3 Dispatcher调度器的设计精髓
    • 10.4 连接池的引用计数设计
    • 10.5 Okio的Segment池化设计
    • 10.6 设计思想总结

# 01.整体概述介绍

# 1.1 概述介绍

OkHttp是一个高效的HTTP客户端库,专为Android和Java应用程序设计。基于OkHttpLib模块的源码分析,详细阐述了OkHttp的架构设计、核心组件、工作流程和技术特性。避免陷入代码细节,着重理解设计思想!

# 1.2 核心特性说明

  • 高效的HTTP/2支持:完整支持HTTP/2协议,包括连接复用、服务器推送等特性
  • 连接池管理:自动管理连接池,减少连接建立的开销
  • 透明的GZIP压缩:自动处理响应压缩,减少网络传输量
  • 响应缓存:支持HTTP缓存,避免重复请求
  • 请求/响应拦截:灵活的拦截器机制,支持请求和响应的定制化处理
  • 故障恢复:自动重试机制,提高网络请求的可靠性

# 1.3 技术架构概览

# 1.4 问题思考

  • OkHttp设计:OkHttp整体设计思路是什么样的?request和respond分别如何设计?如何设计call请求操作?
  • OkHttp同步异步:如何设计同步和异步请求?同步操作做了什么?异步操作如何处理逻辑?
  • OkHttp拦截器:拦截器是如何设计的?为什么需要拦截器?拦截器如何处理拦截,和向下分发逻辑?如何做重试机制的设计?
  • OkHttp缓存:如何设计缓存?什么情况下会用到网络缓存?缓存拦截器的核心思想是什么?
  • OkHttp分发器:同步和异步请求的Dispatcher是如何调度的?设计的巧妙之处是什么?
  • OkHttp线程池:使用了什么线程池?是如何管理线程任务?跟普通线程池使用有何区别?

# 02.核心架构设计

# 2.1 整体架构设计

OkHttp采用分层架构设计,主要包括以下几个层次:

  1. 应用接口层:提供简洁的API接口,包括OkHttpClient、Request、Response等
  2. 调度管理层:Dispatcher负责请求的调度和线程池管理
  3. 拦截器链层:责任链模式实现的拦截器机制
  4. 连接管理层:ConnectionPool管理HTTP连接的复用
  5. 网络传输层:底层的Socket连接和数据传输

# 2.2 整体设计思路

网络请求到响应大概流程图如下所示

image

整体设计思路大概如下所示:

  • 第一步:创建OkHttpClient对象,由于创建这个对象十分复杂,因此采用builder设计模式构造
  • 第二步:包装Request请求体对象,主要是存放url,header,get请求,post请求等等属性
  • 第三步:通过newCall(request)去创建一个call请求
  • 第四步:开始执行同步execute或者enqueue请求,这里会使用到线程池
  • 第五步:添加各种拦截器,缓存拦截器,
  • 第六步:处理缓存拦截,数据复用的技术逻辑
  • 第七步:创建连接请求的操作,给服务端发送请求
  • 第八步:获取返回response数据,这里主要是处理code和body数据

# 2.3 核心组件关系图

# 03.核心组件详解

# 3.1 OkHttpClient

OkHttpClient是整个框架的入口点,采用建造者模式进行配置:

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
    final Dispatcher dispatcher;
    final @Nullable Proxy proxy;
    final List<Protocol> protocols;
    final List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors;
    final List<Interceptor> networkInterceptors;
    final EventListener.Factory eventListenerFactory;
    final ProxySelector proxySelector;
    final CookieJar cookieJar;
    final @Nullable Cache cache;
    final @Nullable InternalCache internalCache;
    final SocketFactory socketFactory;
    final SSLSocketFactory sslSocketFactory;
    final ConnectionPool connectionPool;
    // ... 更多配置项
}

创建OkHttpClient对象,主要是用于Api网络请求的对象。类似于初始化网络请求,可以设置超时时间,日志打印拦截器,代理,ssl校验,域名校验等等。

主要职责:

  • 管理全局配置(超时、代理、SSL等)
  • 创建Call对象
  • 管理拦截器链
  • 管理连接池和缓存

# 3.1.1 创建流程架构图

# 3.1.2 Builder模式原理

OkHttpClient采用Builder模式的主要原因:

  1. 参数众多:OkHttpClient有30+个配置参数
  2. 可选配置:大部分参数都有合理的默认值
  3. 不可变性:创建后的OkHttpClient实例不可修改
  4. 链式调用:提供流畅的API体验
  5. 参数验证:在build()时统一验证参数合法性

# 3.1.3 Builder配置流程图

# 3.1.4 超时配置关系图

在创建对象时,连接超时,读取超时,写入超时,完整请求超时,都可以灵活配置。那么这块是如何设计的呢?

# 3.2 Request请求封装

主要是封装一个Request请求体。

Request request = new Request.Builder()
        .url(url)
        .addHeader("cookie","yangchong")
        .get()
        .build();

Request类封装了HTTP请求的所有信息:

public final class Request {
    final HttpUrl url;
    final String method;
    final Headers headers;
    final @Nullable RequestBody body;
    final Map<Class<?>, Object> tags;
    
    public static class Builder {
        @Nullable HttpUrl url;
        String method;
        Headers.Builder headers;
        @Nullable RequestBody body;
        Map<Class<?>, Object> tags = Collections.emptyMap();
    }
}

Request包括Headers和RequestBody,而RequestBody是abstract的,他的子类是有FormBody(表单提交的)和MultipartBody(文件上传),分别对应了两种不同的MIME类型。

FormBody :"application/x-www-form-urlencoded"
MultipartBody:"multipart/"+xxx.

核心特性:

  • 不可变对象设计,线程安全
  • 建造者模式构建
  • 支持多种HTTP方法(GET、POST、PUT、DELETE等)
  • 灵活的Header管理

# 3.3 Call请求执行接口

如何设计Call请求基于接口开发,设计了Call接口,里面主要做同步请求execute,异步请求enqueue,取消请求cancel等等。

Call类详解:有道词典翻译该类注释:调用是准备执行的请求。call可以取消。由于此对象表示单个请求/响应对(流),因此不能执行两次。

主要是HTTP请求任务封装

  • 可以说我们能用到的操纵基本上都定义在这个接口里面了,可以通过Call对象来操作请求,同步请求execute,异步请求enqueue。
  • 而Call接口内部提供了Factory工厂方法模式(将对象的创建延迟到工厂类的子类去进行,从而实现动态配置)。

Call接口定义了请求执行的标准:

public interface Call extends Cloneable {
    Request request();
    Response execute() throws IOException;
    void enqueue(Callback responseCallback);
    void cancel();
    boolean isExecuted();
    boolean isCanceled();
    Timeout timeout();
    Call clone();
}

RealCall实现:

  • 同步执行:execute()方法直接在当前线程执行
  • 异步执行:enqueue()方法提交到线程池执行
  • 请求取消:支持请求的取消操作

# 3.3.1 核心方法对比

特性 execute() enqueue()
执行方式 同步阻塞 异步非阻塞
线程模型 当前线程执行 线程池执行
返回方式 直接返回Response 通过Callback回调
异常处理 抛出IOException 通过Callback.onFailure
调度管理 简单记录 复杂的队列调度

# 3.3.2 执行流程架构图

# 3.3.3 Execute执行时序图

# 3.3.4 Enqueue执行时序图

# 3.4 Dispatcher调度器

网络请求肯定涉及多线程,如何处理大量任务?采用Dispatcher作为调度,与线程池配合实现了高并发,低阻塞的的运行。针对请求任务,采用Deque作为集合,按照入队的顺序先进先出。

Dispatcher负责管理请求的执行调度:

public final class Dispatcher {
    private int maxRequests = 64;  // 最大并发请求数
    private int maxRequestsPerHost = 5;  // 单主机最大并发数
    
    // 等待执行的异步请求队列
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    // 正在执行的异步请求队列
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    // 正在执行的同步请求队列
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
}

调度策略:

  • 最大并发请求数限制(默认64个)
  • 单主机最大并发数限制(默认5个)
  • 使用双端队列管理等待和运行中的请求
  • 自动的线程池管理

# 3.4.1 Dispatcher架构图

# 3.4.2 异步调度架构

# 3.4.3 调度策略流程图

# 3.5 拦截器机制

OKHttp有两种调用方式,一种是阻塞的同步请求,一种是异步的非阻塞的请求。

但是无论同步还是异步都会调用下RealCall的 getResponseWithInterceptorChain方法来完成请求,同时将返回数据或者状态通过Callback来完成。

设计拦截器的核心原理

Interceptor 负责拦截和分发。先来看看含义:观察,修改以及可能短路的请求输出和响应请求的回来。通常情况下拦截器用来添加,移除或者转换请求或者回应的头部信息。

拦截器,就像水管一样,把一节一节的水管(拦截器)连起来,形成一个回路。实际上client到server也是如此,通过一个又一个的interceptor串起来,然后把数据发送到服务器,又能接受返回的数据,每一个拦截器(水管)都有自己的作用,分别处理不同东西,比如消毒,净化,去杂质,就像一层层过滤网一样。

接口是如何设计的

public interface Interceptor {
   //负责拦截
  Response intercept(Chain chain) throws IOException;
  interface Chain {
     //负责分发、前行
    Response proceed(Request request) throws IOException;
  }
}

# 3.5.1 拦截器时序图

OkHttp的拦截器采用责任链模式,每个拦截器负责特定的功能:

# 3.5.2 拦截器类关系图

# 3.5.3 内置拦截器详解

  1. 应用拦截器(Application Interceptors)
  • 用户自定义的拦截器
  • 在请求发送前和响应接收后执行
  • 可以修改请求和响应
  1. 重试和重定向拦截器(RetryAndFollowUpInterceptor)
  • 处理请求失败重试
  • 处理HTTP重定向
  • 管理连接的获取和释放
  1. 桥接拦截器(BridgeInterceptor)
  • 将用户请求转换为网络请求
  • 添加必要的HTTP头(Content-Type、Content-Length等)
  • 处理Cookie
  • 处理GZIP压缩
  1. 缓存拦截器(CacheInterceptor)
  • 实现HTTP缓存策略
  • 处理缓存的读取和写入
  • 支持强制缓存和协商缓存
  1. 连接拦截器(ConnectInterceptor)
  • 建立与服务器的连接
  • 管理连接池
  • 处理HTTP/2连接复用
  1. 网络拦截器(Network Interceptors)
  • 用户自定义的网络级拦截器
  • 在实际网络请求前后执行
  1. 调用服务器拦截器(CallServerInterceptor)
  • 实际的网络IO操作
  • 发送请求数据
  • 读取响应数据

# 3.5.4 重定向拦截器

核心功能

  • 异常恢复:处理网络异常和连接失败
  • 重定向处理:自动处理HTTP重定向响应
  • 重试逻辑:根据策略决定是否重试请求

# 3.5.6 缓存拦截器

核心功能

  • 缓存策略:根据HTTP缓存规范决定缓存行为
  • 缓存读取:从缓存中读取有效的响应
  • 缓存写入:将可缓存的响应写入缓存
  • 条件请求:发送If-None-Match、If-Modified-Since等条件请求

# 3.5.7 网络请求拦截器

CallServerInterceptor(网络请求拦截器)

  • 请求发送:将HTTP请求发送到服务器
  • 响应接收:接收服务器的HTTP响应
  • 流控制:管理请求和响应的数据流
  • 协议处理:处理HTTP/1.1和HTTP/2的具体协议细节

# 3.5.8 拦截器链状态管理

# 3.6 Response返回

Response核心职责

  • 响应封装:统一封装HTTP响应的所有信息
  • 流式处理:支持大文件的流式读取和处理
  • 内存管理:优化内存使用,避免大响应体的内存溢出
  • 协议适配:支持HTTP/1.1和HTTP/2协议的响应处理
  • 缓存集成:与缓存系统无缝集成

# 3.6.1 类层次结构图

# 3.6.2 创建流程架构图

Response创建流程架构图

# 3.6.3 状态行解析流程图

# 3.6.4 Headers解析流程

# 3.6.5 ResponseBody架构设计

# 3.6.6 ResponseBody数据读取

# 3.6.7 Response生命周期

# 3.6.8 响应数据处理

# 3.6.9 Response错误处理

# 3.6.10 Response缓存机制

# 04.核心流程分析

# 4.1 请求执行流程

# 4.2 连接管理流程

# 4.3 缓存处理流程

# 05.关键技术特性

# 5.1 连接池管理

OkHttp使用连接池来复用HTTP连接,提高性能:

public final class ConnectionPool {
    private static final Executor executor = new ThreadPoolExecutor(
        0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
        new SynchronousQueue<>(), 
        Util.threadFactory("OkHttp ConnectionPool", true)
    );
    
    private final RealConnectionPool delegate;
    
    public ConnectionPool() {
        this(5, 5, TimeUnit.MINUTES);
    }
    
    public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
        this.delegate = new RealConnectionPool(taskRunner, maxIdleConnections, keepAliveDuration, timeUnit);
    }
}

连接池特性:

  • 默认最大空闲连接数:5个
  • 默认连接保活时间:5分钟
  • 自动清理过期连接
  • 支持HTTP/1.1和HTTP/2连接复用

# 5.2 HTTP/2支持

OkHttp完整支持HTTP/2协议:

  • 多路复用:单个连接上并发处理多个请求
  • 服务器推送:服务器主动推送资源
  • 头部压缩:HPACK算法压缩HTTP头部
  • 流量控制:精确控制数据传输速率

# 5.2.1 HTTP/2连接架构

# 5.3 缓存机制设计

实现完整的HTTP缓存策略:

public final class CacheInterceptor implements Interceptor {
    @Override 
    public Response intercept(Chain chain) throws IOException {
        Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;
        
        long now = System.currentTimeMillis();
        CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse;
        
        // 缓存策略处理逻辑
        if (networkRequest == null && cacheResponse == null) {
            return new Response.Builder()
                .request(chain.request())
                .protocol(Protocol.HTTP_1_1)
                .code(504)
                .message("Unsatisfiable Request (only-if-cached)")
                .build();
        }
        
        if (networkRequest == null) {
            return cacheResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .build();
        }
        
        // 继续网络请求...
    }
}

# 5.3.1 缓存架构设计

# 5.3.2 缓存核心组件

# 5.3.3 缓存决策流程

# 5.3.4 磁盘缓存设计

DiskLruCache是OkHttp缓存的核心存储引擎,采用LRU(Least Recently Used)算法:

# 5.3.5 缓存拦截器思想

CacheInterceptor核心流程

# 5.4 安全特性设计

在安全性方面采用了多层防护策略,涵盖传输层安全(TLS)、证书验证、主机名校验、数据完整性保护等多个维度。

  • TLS支持:支持TLS 1.2和1.3
  • 证书锁定:Certificate Pinning防止中间人攻击
  • 主机名验证:严格的主机名验证
  • 协议降级保护:防止协议降级攻击

# 5.4.1 安全组件架构图

# 5.4.2 安全数据流图

# 5.4.3 证书验证机制

OkHttp使用CertificateChainCleaner来清理和验证证书链:

public abstract class CertificateChainCleaner {
  public abstract List<Certificate> clean(List<Certificate> chain, String hostname)
      throws SSLPeerUnverifiedException;

  public static CertificateChainCleaner get(X509TrustManager trustManager) {
    return Platform.get().buildCertificateChainCleaner(trustManager);
  }

  public static CertificateChainCleaner get(X509TrustManager trustManager, TrustRootIndex trustRootIndex) {
    return new BasicCertificateChainCleaner(trustRootIndex);
  }
}

证书链验证流程

# 5.4.4 主机名验证

OkHostnameVerifier实现,OkHttp实现了符合RFC 2818标准的主机名验证器

# 5.4.5 中间人攻击防护

OkHttp通过多层防护机制防止中间人攻击:

# 5.5 SSL/TLS流程

SSL配置层次图

# 5.5.1 TLS版本支持

OkHttp支持多个TLS版本,并提供了灵活的配置机制:

public enum TlsVersion {
  TLS_1_3("TLSv1.3"), // jdk11+, android10+
  TLS_1_2("TLSv1.2"), // jdk7+, android16+
  TLS_1_1("TLSv1.1"), // jdk7+, android16+
  TLS_1_0("TLSv1.0"), // jdk7+, android16+
  SSL_3_0("SSLv3");   // jdk6+, android9+

  public static final TlsVersion[] DEFAULT = {
    TLS_1_3, TLS_1_2, TLS_1_1, TLS_1_0
  };
}

# 5.5.2 TLS版本策略

# 5.5.3 TLS握手过程

RealConnection负责建立安全的TLS连接:

# 06.性能优化策略

# 6.1 连接复用

# 6.1.1 连接复用架构

# 6.2 请求合并

对于相同的请求,OkHttp会自动合并,避免重复请求。

# 6.3 压缩传输

  • 自动GZIP压缩响应体
  • 支持Brotli压缩算法
  • 透明的压缩/解压缩处理

# 6.4 内存管理

  • 使用Okio进行高效的IO操作
  • 智能的缓冲区管理
  • 及时释放不需要的资源

# 6.4.1 内存管理架构

# 6.4.2 IO操作架构

# 07.使用示例说明

# 7.1 基本使用

// 创建客户端
OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .build();

// 构建请求
Request request = new Request.Builder()
    .url("https://api.example.com/data")
    .header("Authorization", "Bearer token")
    .build();

// 同步执行
try (Response response = client.newCall(request).execute()) {
    if (response.isSuccessful()) {
        String result = response.body().string();
        // 处理响应数据
    }
}

// 异步执行
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 处理失败
    }
    
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
            String result = response.body().string();
            // 处理响应数据
        }
    }
});

# 7.2 高级配置

OkHttpClient client = new OkHttpClient.Builder()
    // 连接池配置
    .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
    // 缓存配置
    .cache(new Cache(cacheDir, 50 * 1024 * 1024)) // 50MB缓存
    // 拦截器
    .addInterceptor(new LoggingInterceptor())
    .addNetworkInterceptor(new NetworkInterceptor())
    // 重试配置
    .retryOnConnectionFailure(true)
    // 代理配置
    .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)))
    // SSL配置
    .sslSocketFactory(sslSocketFactory, trustManager)
    .hostnameVerifier(hostnameVerifier)
    .build();

# 7.3 统计请求耗时

OkHttp如何进行各个请求环节的耗时统计呢?

  • OkHttp 版本提供了EventListener接口,可以让调用者接收一系列网络请求过程中的事件,例如DNS解析、TSL/SSL连接、Response接收等。
  • 通过继承此接口,调用者可以监视整个应用中网络请求次数、流量大小、耗时(比如dns解析时间,请求时间,响应时间等等)情况。

如何消耗记录时间

  • 在OkHttp库中有一个EventListener类。该类是网络事件的侦听器。扩展这个类以监视应用程序的HTTP调用的数量、大小和持续时间。
  • 所有启动/连接/获取事件最终将接收到匹配的结束/释放事件,要么成功(非空参数),要么失败(非空可抛出)。
  • 比如,可以在开始链接记录时间;dns开始,结束等方法解析记录时间,可以计算dns的解析时间。
  • 比如,可以在开始请求记录时间,记录connectStart,connectEnd等方法时间,则可以计算出connect连接时间。

OkHttp性能监控架构

# 10.优秀代码设计深度解析

# 10.1 责任链模式的极致运用

OkHttp的拦截器是责任链模式最经典的应用之一,它的设计值得深入分析:

// RealInterceptorChain — 责任链的核心实现
// 精妙之处在于每次proceed()创建新的Chain,index+1
class RealInterceptorChain(
    val interceptors: List<Interceptor>,
    val index: Int,                    // 当前拦截器索引
    val exchange: Exchange?,
    val request: Request
) : Interceptor.Chain {

    override fun proceed(request: Request): Response {
        // 核心:创建新的Chain,index+1,传给下一个拦截器
        val next = RealInterceptorChain(
            interceptors, index + 1, exchange, request
        )
        val interceptor = interceptors[index]
        
        // 调用拦截器的intercept方法
        // 拦截器内部调用chain.proceed()时,
        // 实际上调用的是next(index+1)的proceed
        val response = interceptor.intercept(next)
        return response
    }
}

// 设计精髓分析:
// 1. 不可变链:每次proceed创建新Chain而非修改index
//    → 线程安全,无状态竞争
//    → 同一个拦截器可以多次调用proceed(重试场景)

// 2. 拦截器可以选择:
//    a. 修改request → chain.proceed(modifiedRequest)
//    b. 短路返回 → 直接return response(不调用proceed)
//    c. 修改response → 先proceed获取response,再修改
//    d. 重试 → 多次调用chain.proceed()

// 3. 应用拦截器 vs 网络拦截器的区别:
//    应用拦截器在最外层 → 只执行一次(即使有重试/重定向)
//    网络拦截器在内层 → 每次实际网络请求都会执行
责任链中拦截器的协作设计:

请求方向 →
App拦截器 → Retry → Bridge → Cache → Connect → Network → CallServer
                                                              ↓
响应方向 ←                                                  服务器
App拦截器 ← Retry ← Bridge ← Cache ← Connect ← Network ← CallServer

每个拦截器的职责:

RetryAndFollowUpInterceptor:
  → 请求:记录当前请求信息
  → 响应:检查是否需要重试/重定向
  → 重试时重新调用chain.proceed()(新一轮责任链)
  → 最大重试次数:20次

BridgeInterceptor:
  → 请求:添加Content-Type, Content-Length, Host, User-Agent等Header
  → 请求:添加Accept-Encoding: gzip(透明压缩)
  → 请求:添加Cookie
  → 响应:自动解压gzip(如果Accept-Encoding是自动添加的)
  → 响应:保存Cookie

CacheInterceptor:
  → 请求:查询缓存,计算缓存策略
  → 可能短路:缓存有效时直接返回,不调用proceed
  → 响应:将可缓存的响应写入磁盘缓存

ConnectInterceptor:
  → 请求:从连接池获取或创建连接
  → 执行TLS握手
  → 不处理响应(直接透传)

CallServerInterceptor(链尾):
  → 请求:通过Socket发送HTTP请求
  → 响应:从Socket读取HTTP响应
  → 不调用proceed(它是最后一个拦截器)

# 10.2 不可变对象与建造者模式

// Request — 不可变对象设计典范
public final class Request {
    final HttpUrl url;           // final字段
    final String method;         // 不可修改
    final Headers headers;       // Headers也是不可变的
    final @Nullable RequestBody body;
    final Map<Class<?>, Object> tags;
    
    // 构造函数私有,只能通过Builder创建
    Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build(); // build()返回不可变对象
        this.body = builder.body;
        this.tags = Util.immutableMap(builder.tags); // 不可变Map
    }
    
    // 如果需要修改,创建新的Builder
    public Builder newBuilder() {
        return new Builder(this); // 基于当前Request创建Builder
    }
}

// Headers — 高效的不可变头信息存储
public final class Headers {
    private final String[] namesAndValues; // 扁平数组存储,比Map更紧凑
    
    // 不使用Map<String, String>的原因:
    // 1. HTTP Header的Key可以重复(如Set-Cookie)
    // 2. 扁平数组内存更紧凑,无HashMap的开销
    // 3. 数组遍历比HashMap遍历更快(缓存友好)
    // 4. 数组是不可变的(创建后不能修改长度和内容)
    
    public String get(String name) {
        for (int i = namesAndValues.length - 2; i >= 0; i -= 2) {
            if (name.equalsIgnoreCase(namesAndValues[i])) {
                return namesAndValues[i + 1];
            }
        }
        return null;
        // 从后往前找:后设置的Header优先
    }
}

// 设计优势:
// 1. 线程安全:不可变对象天然线程安全,无需synchronized
// 2. 可共享:多个线程可以安全地共享同一个Request对象
// 3. 缓存友好:可以安全地用作Map的Key
// 4. 可预测:创建后状态不会改变,便于调试

# 10.3 Dispatcher调度器的设计精髓

// Dispatcher — 精妙的线程池设计
public final class Dispatcher {
    // 线程池使用SynchronousQueue而非LinkedBlockingQueue
    private @Nullable ExecutorService executorService;
    
    public synchronized ExecutorService executorService() {
        if (executorService == null) {
            executorService = new ThreadPoolExecutor(
                0,                          // 核心线程数为0
                Integer.MAX_VALUE,          // 最大线程数无限
                60, TimeUnit.SECONDS,       // 空闲60秒回收
                new SynchronousQueue<>(),   // 关键!
                Util.threadFactory("OkHttp Dispatcher", false)
            );
        }
        return executorService;
    }
}

// 设计分析 — 为什么用SynchronousQueue?
// 
// SynchronousQueue是零容量队列:
// → 不能存储任何任务
// → 每次put必须有对应的take(生产者直接交给消费者)
// → 如果没有空闲线程 → 创建新线程
// 
// 与LinkedBlockingQueue对比:
// LinkedBlockingQueue:任务先入队列 → 线程从队列取任务
// → 如果核心线程忙,任务堆积在队列中
// → 不会创建新线程(除非队列满了)
// 
// SynchronousQueue + maxPoolSize=MAX_VALUE:
// → 任务提交时,如果有空闲线程就用,没有就立即创建新线程
// → 永远不会排队等待(网络请求需要及时执行,排队会增加延迟)
// → 空闲60秒后线程自动回收(不浪费资源)
//
// 为什么不直接Executors.newCachedThreadPool()?
// 因为OkHttp需要在Dispatcher层面控制最大并发数(64)和单Host并发数(5)
// CachedThreadPool无法控制并发数上限

// 双端队列(Deque)设计:
// readyAsyncCalls + runningAsyncCalls 构成了二级调度
// ready → running 的转换由promoteAndExecute()控制
// 这种设计让OkHttp可以灵活控制并发策略
// 而不是将所有请求都提交给线程池

// promoteAndExecute()的精妙之处:
// 1. 遍历readyAsyncCalls
// 2. 检查runningAsyncCalls.size() < maxRequests
// 3. 检查同一Host的请求数 < maxRequestsPerHost
// 4. 满足条件 → 移到running队列 → 提交线程池
// 5. 请求完成 → finished() → 再次调用promoteAndExecute()
// → 形成自驱动的调度循环

# 10.4 连接池的引用计数设计

ConnectionPool的连接管理 — 引用计数 + 弱引用

连接池的核心挑战:
  何时判断一个连接可以被回收?
  → 一个连接可能被多个请求共享(HTTP/2多路复用)
  → 不能简单地用空闲时间判断

OkHttp的解决方案:引用计数

RealConnection中的引用管理:
  val calls = mutableListOf<Reference<RealCall>>()
  // 每个使用该连接的RealCall会添加到列表中(弱引用)
  // 当RealCall完成时,从列表中移除
  // 当calls为空 → 连接空闲

连接清理线程的工作逻辑:
fun cleanup(now: Long): Long {
    var longestIdleDurationNs = -1L
    var idleConnectionCount = 0
    var longestIdleConnection: RealConnection? = null
    
    for (connection in connections) {
        // 检查连接是否在使用中
        if (pruneAndGetAllocationCount(connection) > 0) {
            continue  // 还在使用,跳过
        }
        
        idleConnectionCount++
        val idleDurationNs = now - connection.idleAtNanos
        
        if (idleDurationNs > longestIdleDurationNs) {
            longestIdleDurationNs = idleDurationNs
            longestIdleConnection = connection
        }
    }
    
    when {
        longestIdleDurationNs >= keepAliveDurationNs 
            || idleConnectionCount > maxIdleConnections -> {
            // 空闲时间超限 或 空闲连接数超限 → 关闭最久空闲的连接
            connections.remove(longestIdleConnection)
        }
        idleConnectionCount > 0 -> {
            // 有空闲连接但未超限 → 等待到超限时间再检查
            return keepAliveDurationNs - longestIdleDurationNs
        }
        else -> {
            // 没有空闲连接 → 等待下次put时唤醒
            return -1
        }
    }
}

设计精髓:
1. 弱引用管理RealCall → 即使RealCall泄漏也不影响连接回收
2. 清理线程按需唤醒 → put新连接时唤醒,不空转
3. 双条件清理 → 时间超限或数量超限都触发
4. 返回下次清理的等待时间 → 精确的延迟清理

# 10.5 Okio的Segment池化设计

Okio — OkHttp的IO基础库,性能优化的底层支撑

Segment(段)设计:
  Okio不使用大的连续byte数组,而是使用Segment链表:
  
  Buffer = Segment1 ←→ Segment2 ←→ Segment3 ←→ ...
  
  每个Segment:
  ├── byte[] data = new byte[8192]  // 固定8KB
  ├── int pos    // 读取位置
  ├── int limit  // 写入位置
  ├── Segment next, prev  // 双向链表指针
  ├── boolean shared  // 是否共享
  └── boolean owner   // 是否拥有data数组

Segment池化(SegmentPool):
  static final SegmentPool INSTANCE = new SegmentPool();
  
  // 池容量:最多缓存64KB(8个Segment)
  // 使用单链表实现(next指针复用)
  
  Segment take() {
      // 从池中取出一个Segment
      // 如果池为空 → 创建新Segment
  }
  
  void recycle(Segment segment) {
      // 将用完的Segment归还到池中
      // 如果池满 → 丢弃(让GC回收)
      // 归还前重置pos和limit
  }

零拷贝操作:
  Okio支持Segment级别的移动而非字节拷贝:
  
  buffer1 → buffer2 传输数据:
  传统方式:从buffer1读byte[] → 写入buffer2 → 两次拷贝
  Okio方式:将Segment从buffer1的链表移到buffer2 → 零拷贝!
  
  前提:Segment的owner标记确保安全

ByteString — 不可变字节串:
  类似String但存储byte[]
  → 不可变 → 可安全共享
  → 内置SHA-256、MD5等计算方法
  → 支持Base64、Hex编解码
  → OkHttp的TLS握手、证书处理大量使用

设计启发:
1. 分段存储避免大内存分配 → 减少GC压力
2. 池化复用减少对象创建 → 更少的内存分配
3. 链表结构支持零拷贝 → 更高的IO效率
4. 这就是为什么OkHttp的IO性能优于HttpURLConnection

# 10.6 设计思想总结

OkHttp优秀设计思想汇总:

1. 分层架构(Layered Architecture)
   应用层 → 调度层 → 拦截器层 → 连接层 → IO层
   每层职责清晰,可独立测试和替换

2. 责任链模式(Chain of Responsibility)
   拦截器链是OkHttp最核心的设计
   → 每个拦截器关注一个方面
   → 添加新功能只需添加拦截器,不修改已有代码
   → 完美体现开闭原则

3. 不可变对象(Immutable Objects)
   Request、Response、Headers都是不可变的
   → 线程安全无需加锁
   → 可以安全地在拦截器链中传递

4. 建造者模式(Builder Pattern)
   OkHttpClient、Request、Response都使用Builder
   → 参数众多但创建简洁
   → newBuilder()支持基于已有对象微调

5. 连接复用(Connection Pooling)
   → 引用计数管理连接生命周期
   → 按需清理空闲连接
   → HTTP/2多路复用同一连接

6. 异步调度(Async Dispatch)
   → Dispatcher双队列调度
   → SynchronousQueue实现即时执行
   → 细粒度的并发控制

7. 透明处理(Transparent Processing)
   → 自动重试、重定向
   → 自动GZIP压缩/解压
   → 自动Cookie管理
   → 对上层透明,减少使用复杂度

8. 可扩展性(Extensibility)
   → 应用拦截器和网络拦截器
   → EventListener监听各阶段事件
   → 可替换的DNS解析器、SSL配置等

这些设计思想不仅适用于网络框架,
对任何复杂系统的架构设计都有参考价值。
上次更新: 2026/06/28, 17:55:19
Glide图片加载设计
EventBus事件总设计

← Glide图片加载设计 EventBus事件总设计→

最近更新
01
科学方法实践论法
06-28
02
辩证思维矛盾论法
06-28
03
毛选中的调查观念
06-28
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式