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.Chain 与 RealInterceptorChain:

    • 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-Type 和 Content-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 数据的抽象,如 Http1Codec 或 Http2Codec)。
      • 将这个 RealConnection 和 HttpCodec 传递给后续的拦截器链。
    • 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 方法)。
      • 跟踪关联的 RealConnection 和 HttpCodec。
      • 管理连接的生命周期引用计数(通过 acquire 和 release 方法,更新 RealConnection.allocations 列表)。当计数降为 0 且连接空闲时,连接可能被放回连接池或关闭。
      • 处理连接失败后的清理和重试逻辑(与 RetryAndFollowUpInterceptor 协作)。
  10. HttpCodec:

    • 角色: 抽象层,定义了读写 HTTP 请求和响应消息的接口。
    • 实现:
      • Http1Codec: 处理 HTTP/1.1 协议。封装了 BufferedSource 和 BufferedSink,实现请求行、状态行、头部的读写以及 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: gzip 且 BridgeInterceptor 已处理)。

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

  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: 通过 StreamAllocation 从 ConnectionPool 获取/创建 RealConnection 和 HttpCodec,传递给下一级。
    • CallServerInterceptor: 使用 HttpCodec 发送请求数据,接收响应数据,构建 Response 对象。
  7. 响应逐级返回: Response 对象从 CallServerInterceptor 开始,逆序向上返回,经过每个拦截器(拦截器有机会修改响应)。
  8. 最终响应返回: 最外层的 getResponseWithInterceptorChain() 返回最终的 Response。
  9. 分发器清理: Dispatcher 移除已完成的同步调用 (runningSyncCalls.remove(call))。
  10. 资源处理: 使用者读取 ResponseBody 后,需要关闭它 (response.close()) 或消费完所有内容以释放底层资源(连接引用计数减少,可能回池或关闭)。StreamAllocation 的 release 方法会被调用。

关键点理解

  • 拦截器链是灵魂: 理解 Chain.proceed() 的递归/责任链调用机制是理解 OkHttp 扩展性和功能模块化的核心。
  • 连接复用是性能关键: ConnectionPool 和 StreamAllocation 协同工作,通过复用 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. 查看连接池清理: 查看 ConnectionPool 的 executor 运行的清理任务 (cleanupRunnable),理解其清理逻辑。

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/diannao/86544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

洛谷P12170 [蓝桥杯 2025 省 Python B] 攻击次数

题目传送门 思路 首先定义一个数 n n n ,初值为 2025 2025 2025,从第一回合开始,三个英雄持续攻击,攻击方式为: 第一个英雄: 每回合攻击 5 5 5

百度之星2021——BD202104 萌新

输入格式&#xff1a; 本题有多组测试数据。 第一行一个数 T (1 ≤ T ≤ 1000) 表示一共有 T 组数据。对于每一组数据&#xff0c;输入一行两个数 a,b (1 ≤ a,b ≤ 1000000000)。 输出格式&#xff1a; 对每组数据&#xff0c;输出一行两个数分别表示最小与最大的 c&#xff0…

R语言ICU患者死亡率预测实战

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) 预测ICU患者死亡率对比较药物的疗效、比较护理的有效性、比较手术的有效性有重要意义&#xff0c;利用机…

leetcode240-搜索二维矩阵

leetcode 240 思路 1. 矩阵特性 首先&#xff0c;利用矩阵的特性是解题的关键&#xff1a; 每行的元素递增每列的元素递增 这意味着&#xff0c;如果我们在矩阵中从右上角或左下角开始搜索&#xff0c;可以有效缩小搜索范围 2. 从右上角开始搜索 将搜索的起点定位在矩阵…

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…

【web应用】若依框架:若依框架中的页面跳转简介

文章目录 ⭐前言1. 后端控制器跳转2. 前端路由跳转3. 菜单配置跳转4. 权限控制跳转5. 常见跳转路径 ⭐一、主目录页面跳转⭐二、菜单目录跳转⭐总结 标题详情作者JosieBook头衔CSDN博客专家资格、阿里云社区专家博主、软件设计工程师博客内容开源、框架、软件工程、全栈&#x…

MS9292+MS9332 HD/DVI转VGA转换器+HD环出带音频

MS9292MS9332是一款HD/DVI转VGA转换器HD环出带音频的视频转换方案。支持HD/DVI输入&#xff0c;VGA输出和HD环出&#xff0c;HD/DVI输入最高支持1920120060Hz&#xff0c;VGA输出最高支持1920120060Hz&#xff0c;HD环出最高支持4K30Hz。该方案已实现量产&#xff0c;并提供完善…

C++初阶-list的模拟实现(难度较高)

1.list&#xff08;容器&#xff0c;不是类&#xff09;模拟实现基本结构 这个实现是和源码的实现不同的&#xff0c;不要进行强制类型转换了&#xff0c;很麻烦。我们把定义放在.h文件中&#xff0c;在.cpp中放测试代码。 注&#xff1a;这个基本结构之后会改变&#xff0c;…

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…

【iOS】cell的复用以及自定义cell

【iOS】cell的复用以及自定义cell 文章目录 【iOS】cell的复用以及自定义cell前言cell的复用手动&#xff08;非注册&#xff09;自动&#xff08;注册&#xff09; 自定义cell 前言 cell的复用及自定义cell是UITableView或UICollectionView的一个重要优化机制,当用户滚动视图…

深度学习之模型压缩三驾马车:基于ResNet18的模型剪枝实战(2)

前言 《深度学习之模型压缩三驾马车&#xff1a;基于ResNet18的模型剪枝实战&#xff08;1&#xff09;》里面我只是提到了对conv1层进行剪枝&#xff0c;只是为了验证这个剪枝的整个过程&#xff0c;但是后面也有提到&#xff1a;仅裁剪 conv1层的影响极大&#xff0c;原因如…

传输层协议:UDP

目录 1、概念 2、报文结构 3、核心特性 3.1 无连接 3.2 不可靠交付 3.3 面向数据报 3.4 轻量级&高效 3.5 支持广播和组播 4、典型应用场景 5、优缺点分析 6、与TCP的区别 1、概念 UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09…

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…

【汇编逆向系列】七、函数调用包含多个参数之浮点型- XMM0-3寄存器

目录 1. 汇编代码 1.1 debug编译 1.2 release编译 2. 汇编分析 2.1 浮点参数传递规则 2.2 栈帧rsp的变化时序 2.3 参数的访问逻辑 2.4 返回值XMM0寄存器 3. 汇编转化 3.1 Debug编译 3.2 Release 编译 3.3 C语言转化 1. 汇编代码 上一节介绍了整型的函数传参&#x…

华为云Flexus+DeepSeek征文 | 从零到一:用Flexus云服务打造低延迟联网搜索Agent

作者简介 我是摘星&#xff0c;一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型&#xff0c;将实际使用经验分享给大家&#xff0c;希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 作者简介 前言 1. 项目背景与技术选型 1.1 项目…

【多智能体】受木偶戏启发实现多智能体协作编排

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本专栏《人工智能》旨在记录最新的科研前沿&#xff0c;包括大模型、具身智能、智能体等相关领域&#xff0c;期待与你一同探索、学习、进步&#xff0c;一起卷起来叭&#xff…

Java八股文——Spring篇

文章目录 Java八股文专栏其它文章Java八股文——Spring篇SpringSpring的IoC和AOPSpring IoC实现机制Spring AOP实现机制 动态代理JDK ProxyCGLIBByteBuddy Spring框架中的单例Bean是线程安全的吗&#xff1f;什么是AOP&#xff0c;你们项目中有没有使用到AOPSpring中的事务是如…

NineData数据库DevOps功能全面支持百度智能云向量数据库 VectorDB,助力企业 AI 应用高效落地

NineData 的数据库 DevOps 解决方案已完成对百度智能云向量数据库 VectorDB 的全链路适配&#xff0c;成为国内首批提供 VectorDB 原生操作能力的服务商。此次合作聚焦 AI 开发核心场景&#xff0c;通过标准化 SQL 工作台与细粒度权限管控两大能力&#xff0c;助力企业安全高效…

开源技术驱动下的上市公司财务主数据管理实践

开源技术驱动下的上市公司财务主数据管理实践 —— 以人造板制造业为例 引言&#xff1a;财务主数据的战略价值与行业挑战 在资本市场监管日益严格与企业数字化转型的双重驱动下&#xff0c;财务主数据已成为上市公司财务治理的核心基础设施。对于人造板制造业而言&#xff0…