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网络处理机制的关键所在。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…