当前位置: 首页 > news >正文

OkHttp 3.0源码解析:从设计理念到核心实现

本文通过深入分析OkHttp 3.0源码,揭示其高效HTTP客户端的实现奥秘,包含核心设计理念、关键组件解析、完整工作流程及实用技巧。

一、引言:为什么选择OkHttp?

在Android和Java生态中,OkHttp已成为HTTP客户端的标准选择。相较于其他HTTP库,OkHttp具有以下优势:

特性OkHttpHttpURLConnectionApache HttpClient
连接池✅ 自动复用连接❌ 需手动管理✅ 支持
拦截器✅ 强大可扩展❌ 不支持⚠️ 有限支持
HTTP/2✅ 完整支持⚠️ Android 5+支持❌ 不支持
透明压缩✅ 自动处理❌ 需手动实现❌ 需手动实现
缓存机制✅ 符合RFC规范⚠️ 基础支持✅ 支持
API设计⭐ 简洁现代⚠️ 冗长复杂⚠️ 冗长复杂

二、环境搭建与基础使用

1. 添加依赖

dependencies {implementation 'com.squareup.okhttp3:okhttp:3.14.9' // 3.x最终稳定版
}

2. 同步请求示例

// 1. 创建客户端(推荐复用实例)
OkHttpClient client = new OkHttpClient();// 2. 构建请求
Request request = new Request.Builder().url("https://api.example.com/data").build();// 3. 执行请求并处理响应
try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) {throw new IOException("Unexpected code: " + response);}// 获取响应头Headers headers = response.headers();for (int i = 0; i < headers.size(); i++) {System.out.println(headers.name(i) + ": " + headers.value(i));}// 获取响应体String body = response.body().string();System.out.println(body);
}

3. 异步请求示例

// 1. 创建请求
Request request = new Request.Builder().url("https://api.example.com/async").build();// 2. 异步执行
client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {try (ResponseBody body = response.body()) {if (!response.isSuccessful()) {throw new IOException("Unexpected code: " + response);}System.out.println(body.string());}}
});

三、核心设计理念解析

1. 分层架构设计

OkHttp采用清晰的分层结构,各层职责分明:

+---------------------+
|      Application    |  ← 用户代码
+---------------------+
|      OkHttpClient   |  ← 配置中心
+---------------------+
|      Interceptors   |  ← 功能扩展点(核心!)
+---------------------+
|       Connection    |  ← 连接管理层
+---------------------+
|   Network Protocol  |  ← HTTP/1.1或HTTP/2实现
+---------------------+
|        Socket       |  ← 底层I/O
+---------------------+

2. 拦截器机制(责任链模式)

拦截器是OkHttp最核心的创新,将HTTP请求处理分解为可插拔的步骤:

public interface Interceptor {// 关键方法:处理请求并返回响应Response intercept(Chain chain) throws IOException;interface Chain {Request request();Response proceed(Request request) throws IOException;}
}

OkHttp 是一个广受欢迎的 Java/Android HTTP 客户端库,以其高效、灵活和易用性著称。分析 OkHttp 3.0 源码(虽然 3.0 已是较老版本,但其核心架构与后续版本基本一致)是理解其强大功能的关键。以下是对核心组件和流程的深入分析:

主要组件分析

  1. OkHttpClient:

    • 角色: 工厂和配置中心。通常作为单例使用。
    • 职责:
      • 持有所有全局配置:连接超时、读取超时、写入超时、拦截器列表 (interceptors, networkInterceptors)、连接池 (connectionPool)、代理、缓存 (cache)、认证器 (authenticator)、Cookie 管理器 (cookieJar)、DNS 解析器 (dns)、是否遵循重定向、是否重试连接失败等。
      • 通过 newCall(Request request) 方法,根据给定的 Request 创建一个 Call 对象。这是发起请求的入口。
  2. Request:

    • 角色: 描述一个 HTTP 请求。
    • 内容: URL (HttpUrl)、方法 (GET, POST 等)、Headers (Headers)、Body (RequestBody)、标签 (Tag) 等。
    • 构建: 通常使用 Request.Builder 模式构建。
  3. Call:

    • 角色: 表示一个准备执行的请求。它是连接 Request 和最终 Response 的桥梁。一个 Call 实例代表一次请求尝试(可能包含重试和重定向)。
    • 实现: RealCall (OkHttp 内部的真实实现)。
    • 关键方法:
      • execute(): 同步执行请求,阻塞当前线程直到响应返回(或出错),返回 Response
      • enqueue(Callback responseCallback): 异步执行请求。将请求放入队列,在后台线程执行,结果通过 Callback 回调到调用者线程(通常是主线程)。
      • cancel(): 取消请求(如果可能)。
    • 核心流程 (以 RealCall.execute() 为例):
      1. 检查是否已执行或已取消。
      2. 调用 client.dispatcher().executed(this) 通知分发器这是一个同步请求(用于统计和取消)。
      3. 关键: 调用 getResponseWithInterceptorChain() 方法。这是拦截器链执行的入口点。
  4. Dispatcher:

    • 角色: 请求的分发管理器,主要用于管理异步请求的线程池和运行状态。
    • 职责:
      • 维护两个线程池:executorService (用于执行异步网络请求) 和 executorServiceOrNull (内部实现细节)。
      • 维护三个队列:
        • readyAsyncCalls: 等待执行的异步请求队列(当正在运行的请求达到最大值时)。
        • runningAsyncCalls: 正在运行的异步请求队列。
        • runningSyncCalls: 正在运行的同步请求队列(仅用于统计和取消)。
      • 控制并发请求的最大数量(默认为 64)和单个主机最大并发数(默认为 5)。
      • 当异步请求完成或条件变化时,将 readyAsyncCalls 中的请求移入 runningAsyncCalls 并执行。
  5. Interceptor.ChainRealInterceptorChain:

    • Interceptor 接口: 定义单个拦截器的行为,核心方法是 Response intercept(Chain chain) throws IOException
    • Chain 接口: 代表拦截器链中的当前位置,提供:
      • request(): 获取当前请求。
      • proceed(Request request): 核心! 将(可能被当前拦截器修改后的)请求传递给链中的下一个拦截器,并接收其返回的响应。
    • RealInterceptorChain: Chain 的具体实现。它持有:
      • 当前拦截器列表。
      • 当前拦截器的索引 index
      • 原始的 Request
      • 其他必要上下文(如 StreamAllocation, HttpCodec, RealConnection - 这些通常在连接拦截器中创建并传递)。
    • 链的执行 (RealInterceptorChain.proceed()):
      1. 检查索引是否越界。
      2. 获取下一个拦截器 (interceptors.get(index))。
      3. 创建新的 RealInterceptorChain 实例,index 加 1,其他上下文复制或传递。
      4. 调用 nextInterceptor.intercept(nextChain)
      5. 返回 intercept 方法返回的 Response
      • 本质: 这是一个递归调用过程,每个拦截器在调用 chain.proceed(request) 时,就将控制权交给了下一个拦截器。当最后一个拦截器(通常是 CallServerInterceptor)执行完毕并返回响应时,这个响应会逐层向上(逆序)返回到前面的拦截器,每个拦截器都有机会修改最终的响应。
  6. 内置拦截器 (按链中顺序):

    • RetryAndFollowUpInterceptor: 处理失败重试和 HTTP 重定向 (3xx 响应码)。它会根据响应创建新的请求并重新发起调用(通过创建新的 RealInterceptorChain)。
    • BridgeInterceptor: 桥接应用层和网络层。
      • 请求方向: 添加必要的默认 Headers (如 User-Agent, Host, Connection: keep-alive, Accept-Encoding: gzip)。如果请求有 Body,添加 Content-TypeContent-Length。处理 Cookie。
      • 响应方向: 处理 Content-Encoding: gzip,自动解压缩响应体。保存 Cookie。
    • CacheInterceptor: 处理 HTTP 缓存。
      • 根据请求和缓存策略 (CacheControl) 查找可用的缓存响应。
      • 如果找到有效缓存且请求满足条件 (如 if-Modified-Since, if-None-Match),可能直接返回缓存或发送条件请求。
      • 处理网络响应的缓存写入(如果响应可缓存)。
    • ConnectInterceptor: 建立到目标服务器的连接。
      • 使用 StreamAllocation 对象(由 RetryAndFollowUpInterceptor 创建)从连接池 (ConnectionPool) 获取或新建一个到目标地址的 RealConnection
      • 建立 TCP/TLS 连接(如果必要),进行协议协商 (HTTP/1.1, HTTP/2)。
      • 获取一个 HttpCodec 对象 (用于实际读写 HTTP 数据的抽象,如 Http1CodecHttp2Codec)。
      • 将这个 RealConnectionHttpCodec 传递给后续的拦截器链。
    • CallServerInterceptor: 链的末端,执行实际的网络 I/O。
      • 使用 HttpCodec 将请求头和请求体写入网络。
      • 读取响应头和响应体。
      • 构造最终的 Response 对象并返回。
      • 这是唯一真正进行网络读写的拦截器。
  7. ConnectionPool:

    • 角色: 管理空闲的 HTTP 和 HTTP/2 连接以供重用。
    • 实现: 内部使用一个线程池 (Executor) 运行清理任务。
    • 核心方法:
      • put(RealConnection connection): 将空闲连接放入池中。
      • get(Address address, StreamAllocation streamAllocation): 根据地址 (Address - 包含 URL、代理、SSL 配置等) 查找匹配的空闲连接。找到后关联到 StreamAllocation
    • 清理机制:
      • 最大空闲连接数: 默认 5 个。
      • 最长空闲时间: 默认 5 分钟。清理线程定期扫描,移除空闲时间超过限制或空闲连接数超过限制的连接。
      • 对于 HTTP/2 连接,即使空闲连接数为 0,只要其关联的 StreamAllocation 计数为 0,也会被清理。
  8. RealConnection:

    • 角色: 表示一个到目标服务器的物理 Socket 连接。
    • 内容:
      • Socket / SSLSocket
      • 底层输入/输出流 (Source, Sink)。
      • 握手信息 (Handshake)。
      • 使用的协议 (Protocol: HTTP/1.1, HTTP/2, SPDY)。
      • HTTP/2 相关的 Http2Connection 对象(如果使用 HTTP/2)。
      • 一个 List<Reference<StreamAllocation>> (allocations),记录当前使用此连接的活跃请求 (StreamAllocation) 的弱引用列表。
    • 连接建立流程 (connect 方法):
      1. 解析 IP 地址(可能涉及 DNS)。
      2. 建立 TCP Socket 连接。
      3. 如果需要 TLS (HTTPS),进行 SSL/TLS 握手 (SSLSocket)。
      4. 如果是 HTTP/2,进行协议协商 (ALPN 或 NPN),建立 Http2Connection
      5. 将连接标记为成功。
  9. StreamAllocation:

    • 角色: 协调请求流 (Call)、连接 (Connection) 和流 (Stream / HttpCodec) 之间的关系。一个 Call 对应一个 StreamAllocation(即使在重试/重定向过程中)。
    • 职责:
      • 通过 ConnectionPool 查找或创建 RealConnection
      • 在找到的 RealConnection 上创建 HttpCodec (通过 newCodec 方法)。
      • 跟踪关联的 RealConnectionHttpCodec
      • 管理连接的生命周期引用计数(通过 acquirerelease 方法,更新 RealConnection.allocations 列表)。当计数降为 0 且连接空闲时,连接可能被放回连接池或关闭。
      • 处理连接失败后的清理和重试逻辑(与 RetryAndFollowUpInterceptor 协作)。
  10. HttpCodec:

    • 角色: 抽象层,定义了读写 HTTP 请求和响应消息的接口。
    • 实现:
      • Http1Codec: 处理 HTTP/1.1 协议。封装了 BufferedSourceBufferedSink,实现请求行、状态行、头部的读写以及 body 的流式读写。
      • Http2Codec: 处理 HTTP/2 协议。将 HTTP 语义映射到 HTTP/2 流。利用 Http2Connection 创建流 (FramingSource, FramingSink),读写帧数据。
  11. Response:

    • 角色: 表示 HTTP 响应。
    • 内容: 协议 (Protocol)、状态码 (int)、状态信息 (String)、Headers (Headers)、响应体 (ResponseBody)、网络响应 (networkResponse - 用于重定向/缓存)、缓存响应 (cacheResponse)、请求 (Request)、握手信息 (Handshake) 等。
    • ResponseBody:
      • 封装响应体内容。
      • 提供 source() 方法获取 BufferedSource 进行流式读取。
      • 提供 bytes(), string(), charStream(), byteStream() 等方法方便读取整个内容(注意大响应可能导致 OOM)。
      • 自动处理 GZIP 解压缩(如果响应头包含 Content-Encoding: gzipBridgeInterceptor 已处理)。

核心流程总结 (同步请求)

  1. 创建请求: Request request = new Request.Builder().url(...).build();
  2. 创建调用: Call call = okHttpClient.newCall(request);
  3. 执行调用: Response response = call.execute();
  4. 分发器登记: Dispatcher 记录此同步调用 (runningSyncCalls.add(call))。
  5. 启动拦截器链: RealCall.getResponseWithInterceptorChain()
    • 创建初始的拦截器列表 (包含应用拦截器、内置拦截器、网络拦截器)。
    • 创建初始的 RealInterceptorChain (index=0)。
    • 调用 chain.proceed(initialRequest)
  6. 拦截器链逐级执行:
    • 每个拦截器接收 Request,可以选择修改它,然后调用 chain.proceed(request) 交给下一个拦截器。
    • RetryAndFollowUpInterceptor: 处理重试/重定向(可能创建新链)。
    • BridgeInterceptor: 添加请求头、处理响应 GZIP。
    • CacheInterceptor: 检查缓存,可能直接返回缓存响应或发出条件请求。
    • ConnectInterceptor: 通过 StreamAllocationConnectionPool 获取/创建 RealConnectionHttpCodec,传递给下一级。
    • CallServerInterceptor: 使用 HttpCodec 发送请求数据,接收响应数据,构建 Response 对象。
  7. 响应逐级返回: Response 对象从 CallServerInterceptor 开始,逆序向上返回,经过每个拦截器(拦截器有机会修改响应)。
  8. 最终响应返回: 最外层的 getResponseWithInterceptorChain() 返回最终的 Response
  9. 分发器清理: Dispatcher 移除已完成的同步调用 (runningSyncCalls.remove(call))。
  10. 资源处理: 使用者读取 ResponseBody 后,需要关闭它 (response.close()) 或消费完所有内容以释放底层资源(连接引用计数减少,可能回池或关闭)。StreamAllocationrelease 方法会被调用。

关键点理解

  • 拦截器链是灵魂: 理解 Chain.proceed() 的递归/责任链调用机制是理解 OkHttp 扩展性和功能模块化的核心。
  • 连接复用是性能关键: ConnectionPoolStreamAllocation 协同工作,通过复用 TCP 连接大幅减少延迟。
  • 分层抽象: Call -> StreamAllocation -> RealConnection/HttpCodec 的分层管理清晰隔离了请求逻辑、连接管理和协议实现。
  • 资源管理: 正确关闭 ResponseBody 至关重要,以确保连接能被及时回收。StreamAllocation 的引用计数机制是连接生命周期管理的核心。

四、核心组件源码深度解析

1. 拦截器链执行流程

// RealCall.java
Response getResponseWithInterceptorChain() throws IOException {// 构建完整拦截器链(按顺序)List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());          // 应用拦截器interceptors.add(retryAndFollowUpInterceptor);      // 重试拦截器interceptors.add(new BridgeInterceptor(...));       // 桥接拦截器interceptors.add(new CacheInterceptor(...));        // 缓存拦截器interceptors.add(new ConnectInterceptor(...));      // 连接拦截器interceptors.addAll(client.networkInterceptors());  // 网络拦截器interceptors.add(new CallServerInterceptor(...));   // 服务调用拦截器// 创建初始责任链Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);// 启动拦截器链return chain.proceed(originalRequest);
}

2. 连接复用机制(关键源码)

// ConnectionPool.java
public final class ConnectionPool {// 空闲连接的最大数量private final int maxIdleConnections;// 连接的最大空闲时间(秒)private final long keepAliveDurationNs;// 连接池实际存储private final Deque<RealConnection> connections = new ArrayDeque<>();// 清理任务private Runnable cleanupRunnable = new Runnable() {@Override public void run() {while (true) {// 计算下次清理等待时间long waitNanos = cleanup(System.nanoTime());if (waitNanos == -1) return;if (waitNanos > 0) {synchronized (ConnectionPool.this) {try {// 等待指定时间或被唤醒ConnectionPool.this.wait(waitNanos);} catch (InterruptedException ignored) {}}}}}};// 获取可用连接RealConnection get(Address address, StreamAllocation streamAllocation) {for (RealConnection connection : connections) {// 1. 检查连接是否可用// 2. 检查地址是否匹配// 3. 检查协议兼容性if (connection.isEligible(address)) {streamAllocation.acquire(connection);return connection;}}return null;}
}

3. HTTP/2多路复用实现

// Http2Codec.java
public final class Http2Codec implements HttpCodec {@Override public void writeRequestHeaders(Request request) throws IOException {// 创建HTTP/2流stream = http2Connection.newStream(requestHeaders, hasRequestBody);// 发送请求头帧stream.getSink().headers(requestHeaders);}@Override public Response.Builder readResponseHeaders() throws IOException {// 读取响应头帧Headers headers = stream.takeHeaders();return new Response.Builder().protocol(Protocol.HTTP_2).code(parseStatus(headers.get(":status"))).message("").headers(headers);}
}

五、关键流程剖析

1. 完整请求生命周期

应用程序 分发器 拦截器链 服务器 RetryInterceptor BridgeInterceptor CacheInterceptor ConnectInterceptor CallServerInterceptor newCall(request) execute() 重试逻辑 添加头信息 缓存检查 获取连接 网络I/O loop [拦截器处理] 发送请求 返回响应 返回Response 返回结果 应用程序 分发器 拦截器链 服务器 RetryInterceptor BridgeInterceptor CacheInterceptor ConnectInterceptor CallServerInterceptor

2. 连接复用流程

请求发起
连接池中
是否有可用连接?
复用现有连接
创建新连接
执行TCP握手
是否为HTTPS?
执行TLS握手
发送HTTP请求
接收响应
连接是否
可复用?
归还连接池
关闭连接

六、高级特性实现

1. 自定义拦截器示例

public class LoggingInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();// 1. 请求前记录long startNs = System.nanoTime();logger.info(String.format("Sending request %s%n%s",request.url(), request.headers()));// 2. 继续处理请求Response response = chain.proceed(request);// 3. 响应后记录long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);ResponseBody body = response.body();logger.info(String.format("Received response in %dms%n%s",tookMs, response.headers()));return response;}
}// 使用自定义拦截器
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();

2. 连接调优参数

// 创建高性能调优的客户端
OkHttpClient client = new OkHttpClient.Builder().connectionPool(new ConnectionPool(100,                 // 最大空闲连接数5, TimeUnit.MINUTES  // 连接存活时间)).connectTimeout(10, TimeUnit.SECONDS)  // 连接超时.readTimeout(30, TimeUnit.SECONDS)     // 读取超时.writeTimeout(30, TimeUnit.SECONDS)    // 写入超时.retryOnConnectionFailure(true)        // 自动重试.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)) // 协议优先级.build();

七、关键点总结

  1. 拦截器链是核心机制

    • 采用责任链模式处理请求
    • 支持自定义拦截器扩展功能
    • 内置拦截器各司其职(重试、桥接、缓存、连接、网络)
  2. 连接池提升性能

    • 默认最大空闲连接数:5
    • 默认连接存活时间:5分钟
    • 支持HTTP/1.1 Keep-Alive和HTTP/2多路复用
  3. 高效的缓存策略

    • 遵循RFC 7234规范
    • 支持磁盘缓存(默认10MB)
    • 自动处理缓存验证(If-Modified-Since等)
  4. 智能的错误恢复

    • 自动重试连接失败
    • 自动处理重定向(最多20次)
    • 自动处理身份验证挑战
  5. 协议支持策略

    • 自动协商最佳协议(HTTP/2优先)
    • 透明支持TLS(SNI和ALPN扩展)
    • 自动回退到HTTP/1.1

八、性能优化建议

  1. 客户端复用

    // 错误做法:每次请求创建新客户端
    for (int i = 0; i < 100; i++) {OkHttpClient client = new OkHttpClient(); // 创建100个客户端!client.newCall(request).execute();
    }// 正确做法:复用客户端实例
    OkHttpClient client = new OkHttpClient(); // 单例
    for (int i = 0; i < 100; i++) {client.newCall(request).execute(); // 复用连接池
    }
    
  2. 响应体及时关闭

    // 危险做法:可能造成连接泄漏
    Response response = client.newCall(request).execute();
    String result = response.body().string();
    // 忘记关闭response!// 推荐做法1:try-with-resources
    try (Response response = client.newCall(request).execute()) {String result = response.body().string();
    }// 推荐做法2:手动关闭
    Response response = client.newCall(request).execute();
    try {String result = response.body().string();
    } finally {response.close(); // 确保关闭
    }
    
  3. 合理设置超时时间

    new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS)  // 连接超时.readTimeout(30, TimeUnit.SECONDS)     // 读取超时.writeTimeout(30, TimeUnit.SECONDS)    // 写入超时.callTimeout(60, TimeUnit.SECONDS)     // 整个调用超时.build();
    

九、结语

分析建议

  1. 从入口开始: OkHttpClient.newCall().execute()enqueue() -> RealCall
  2. 深入拦截器链: 重点追踪 getResponseWithInterceptorChain() 方法,单步调试每个内置拦截器的 intercept() 方法,观察 Chain.proceed() 的调用和返回。理解每个拦截器的职责。
  3. 理解连接获取:ConnectInterceptor 中,跟踪 StreamAllocation.connection() -> ConnectionPool.get() 以及新建连接的流程 (RealConnection.connect())。
  4. 跟踪网络读写:CallServerInterceptor 中,看 HttpCodec (特别是 Http1Codec) 如何读写请求行、头部、body 和响应行、头部、body。
  5. 观察缓存:CacheInterceptor 中,设置缓存目录后,观察缓存查找 (Cache.get()) 和存储 (Cache.put()) 的触发条件。
  6. 查看连接池清理: 查看 ConnectionPoolexecutor 运行的清理任务 (cleanupRunnable),理解其清理逻辑。

通过深入分析OkHttp 3.0源码,我们可以清晰地看到其卓越设计的实现细节:

  1. 拦截器链作为核心架构,实现了功能模块的高度解耦和灵活扩展
  2. 连接池机制通过TCP连接复用,大幅提升网络性能
  3. 分层设计使得各组件职责清晰,便于维护和扩展
  4. 严谨的资源管理确保系统稳定性和可靠性

OkHttp不仅是一个功能强大的HTTP客户端,其设计理念和实现方式更值得开发者深入学习。掌握这些底层原理,将有助于我们编写更高效、稳定的网络请求代码,并在复杂场景下进行有效的问题诊断和性能优化。

源码学习建议:从RealCall.execute()入口开始,逐步跟踪拦截器链的执行过程,重点关注ConnectInterceptor和CallServerInterceptor的实现细节,这是理解OkHttp网络处理机制的关键所在。

相关文章:

  • AI整合SEO关键词智能策略
  • 阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库
  • selenium自动化测试学习心得1
  • C++ 信息学奥赛总复习题答案解析(第一章)
  • 在Ubuntu22.04 系统中安装Docker详细教程
  • 【hadoop】相关集群开启命令
  • Vue3学习(4)- computed的使用
  • 基于Python学习《Head First设计模式》第八章 模板方法模式
  • 力扣HOT100之二分查找:33. 搜索旋转排序数组
  • 热电厂中控室无线集控:高清视频监测+PLC远程操控
  • 调试器基本原理
  • 高精度算法--四则运算模板(附例题)
  • 心理咨询技能竞赛流程方案
  • day 47
  • 知识改变命运?如何有规划的学好计算机专业?
  • Codeforces Round 509 (Div. 2) C. Coffee Break
  • 【AI论文】反思、重试、奖励:通过强化学习实现大型语言模型的自我提升
  • Go 标准库 encoding/gob 快速上手
  • Python项目中添加环境配置文件
  • 【Elasticsearch】映射:Join 类型、Flattened 类型、多表关联设计
  • wordpress手机移动版/关键词seo排名
  • centos wordpress 建站教程/谷歌搜索指数查询
  • 国际货代做网站/行业关键词搜索量排名
  • 网站建设参考网站的说明书/做推广app赚钱的项目
  • 微信网站建设热线/互联网销售平台有哪些
  • html5做图书馆网站/互联网营销专业