OKHttp源码解析-连接池

Android网络编程(八)源码解析OkHttp后篇[复用连接池] 上一篇我们根据OkHttp的源码讲解了OkHttp的请求网络流程,这一讲我们接着根据源码了解OkHttp的复用连接池。 阅读详情

前面分析Retrofit的源码,Retrofit底层使用了OkHttp来做网络请求操作。在介绍ConnectInterceptor时有设计到OkHttp的连接池ConnectionPool。本篇文章将详细介绍其实现。

核心类

OkHttp的连接池主要涉及几个核心类,ConnectionPool.java、RealConnection.java、StreamAllocation.java、Internal.java

  • ConnectionPool.java
    这个类主要负责管理HTTP HTTP/2连接的重用以减少网络延时。它实现了连接重用、清理的的策略。

  • Internal.java
    外部对连接池不直接操作,而是通过这个类来完成。这个类是一个抽象类,只有一个唯一的实现,就在OKHttpClient中。

  • RealConnection.java
    这是真正处理物理连接的类。普通Socket的连接、SSL Socket的连接、IO的读写都是在这个类完成的。

  • StreamAllocation.java
    这个类主要用于处理物理Sokect连接、Streams(逻辑上的Http Request流和Response流)以及Calls(请求数据,包括原始请求以及重定向的请求或者需要证书认证的请求)之间的关系。每个连接有它们自己的分配上限,这决定了每个连接能够承载多少路并发流。HTTP 1.x一次最多有一路流,HTTP/2可以有多路。
    这个类提供了每一个连接以及每个连接里的流的释放的API。
    这个类还可以提供取消异步请求的API。

初始化

连接池的初始化在OKHttpClient的Builder函数里面。

  public static final class Builder {
	  ...
    ConnectionPool connectionPool;
	  ...

    public Builder() {
	  ...
      connectionPool = new ConnectionPool();
	  ...
    }
}

我们知道Java类的初始化顺序

再看下ConnectionPool的构造函数。

  public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);
  }
  public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.maxIdleConnections = maxIdleConnections;
    this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

    // Put a floor on the keep alive duration, otherwise cleanup will spin loop.
    if (keepAliveDuration <= 0) {
      throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
    }
  }

初始化连接池的时候主要传入了2个参数。一个是maxIdleConnections,表示连接池中最大的空闲连接数,最大是5。keepAliveDuration表示空闲连接与服务器保持连接的时间,最大是5分钟。

put

put方法是在StreamAllocation类的findConnection方法里面调用的。findConnection已经在Android-Retrofit源码解析(一)调用流程(下)里分析过了。我们只看下相关的代码。

  private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
...
    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");

      if (newRouteSelection) {
      //1. 获取到IP地址的集合,然后根据IP地址去连接池里查找是否有可复用的连接。
        // Now that we have a set of IP addresses, make another attempt at getting a connection from
        // the pool. This could match due to connection coalescing.
        List<Route> routes = routeSelection.getAll();
        for (int i = 0, size = routes.size(); i < size; i++) {
          Route route = routes.get(i);
          Internal.instance.get(connectionPool, address, this, route);
          if (connection != null) {
            foundPooledConnection = true;
            result = connection;
            this.route = route;
            break;
          }
        }
      }

//2-1.如果连接池中没有找到可复用的连接,就创建一个连接
      if (!foundPooledConnection) {
        if (selectedRoute == null) {
          selectedRoute = routeSelection.next();
        }

        // Create a connection and assign it to this allocation immediately. This makes it possible
        // for an asynchronous cancel() to interrupt the handshake we're about to do.
        route = selectedRoute;
        refusedStreamCount = 0;
        result = new RealConnection(connectionPool, selectedRoute);
        acquire(result, false);
      }
    }
    
//2-2.如果连接池中找到了可复用的连接,就直接返回
    // If we found a pooled connection on the 2nd time around, we're done.
    if (foundPooledConnection) {
      eventListener.connectionAcquired(call, result);
      return result;
    }

	//3. 创建连接对象后,调用其connect方法,与服务器建立物理上的连接
    // Do TCP + TLS handshakes. This is a blocking operation.
    result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());

    Socket socket = null;
    synchronized (connectionPool) {
      reportedAcquired = true;

	//4. 将新创建的连接放入连接池。
      // Pool the connection.
      Internal.instance.put(connectionPool, result);

	//4. 如果是HTTP2的连接,且有多路,就释放当前连接,使用其它路的连接。
      // If another multiplexed connection to the same address was created concurrently, then
      // release this connection and acquire that one.
      if (result.isMultiplexed()) {
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);
...
}

接下来看下put进连接池里面做了什么操作。

  void put(RealConnection connection) {
  // 1. 确保当前线程持有ConnectionPool对象的锁。
    assert (Thread.holdsLock(this));
    // 2. 默认cleanupRunning=false,所以进来会执行一遍clean
    if (!cleanupRunning) {
      cleanupRunning = true;
      executor.execute(cleanupRunnable);
    }
    // 3. 把当前connection加入到了connections中。
    connections.add(connection);
  }

clean的内容我们后面讲。先来看下这个connections。connections是一个ArrayDeque。ArrayQueue的内部是一个动态扩展的循环数组,通过head和tail维护数组的开始和结尾,其插入删除的效率非常高,都是O(N)。
OkHttpClient的Dispatcher类里面有三个Call队列,readyAsyncCalls、runningAsyncCalls、runningSyncCalls,他们也是用ArrayQueue实现的。

get

get方法和put方法一样,也是只在StreamAllocation类的findConnection方法里面调用。一共有2处调用,都在findConnection方法中,来看下代码。

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
      ...
	Socket toClose;
	// 1. 获取到connectionPool对象锁
    synchronized (connectionPool) {
      if (released) throw new IllegalStateException("released");
      if (codec != null) throw new IllegalStateException("codec != null");
      if (canceled) throw new IOException("Canceled");

      // Attempt to use an already-allocated connection. We need to be careful here because our
      // already-allocated connection may have been restricted from creating new streams.
      releasedConnection = this.connection;
      // 2. 如果这个连接不允许创建新的流,就释放这个连接。释放后this.connection被置为null。
      toClose = releaseIfNoNewStreams();
      if (this.connection != null) {
      // 3. 如果这个连接已经分配了并且是可用的,把这个连接赋值给result,后续直接返回这个result。
        // We had an already-allocated connection and it's good.
        result = this.connection;
        releasedConnection = null;
      }
      if (!reportedAcquired) {
        // If the connection was never reported acquired, don't report it as released!
        releasedConnection = null;
      }

      if (result == null) {
      // 4. 如果现有的连接是不可用的。尝试从连接池中获取一个可用连接。
        // Attempt to get a connection from the pool.
        Internal.instance.get(connectionPool, address, this, null);
        if (connection != null) {
        // 5-1. 在连接池中找到了可用连接
          foundPooledConnection = true;
          result = connection;
        } else {
        // 5-2. 连接池中没有找到可用连接。
          selectedRoute = route;
        }
      }
    }
    closeQuietly(toClose);

    if (releasedConnection != null) {
      eventListener.connectionReleased(call, releasedConnection);
    }
    if (foundPooledConnection) {
      eventListener.connectionAcquired(call, result);
    }
    if (result != null) {
    // 6. 如果 现有连接可用,或者现有连接不可用但是在连接池中找到了可用连接。直接返回。
      // If we found an already-allocated or pooled connection, we're done.
      return result;
    }

	//7. 如果没有可复用连接,并且如果可选路由和路由集合为空,就创建一个新的路由列表。
    // If we need a route selection, make one. This is a blocking operation.
    boolean newRouteSelection = false;
    if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
      newRouteSelection = true;
      routeSelection = routeSelector.next();
    }

    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");

      if (newRouteSelection) {
      // 8. 获取到了一组路由列表之后,可以尝试从连接池中获取每个路由上是否有可用连接。
      //这是有可能找到的,因为存在连接合并的情况。
        // Now that we have a set of IP addresses, make another attempt at getting a connection from
        // the pool. This could match due to connection coalescing.
        List<Route> routes = routeSelection.getAll();
        for (int i = 0, size = routes.size(); i < size; i++) {
          Route route = routes.get(i);
          // 9. 根据路由,查找可复用连接。
          Internal.instance.get(connectionPool, address, this, route);
          if (connection != null) {
            foundPooledConnection = true;
            result = connection;
            this.route = route;
            break;
          }
        }
      }

      if (!foundPooledConnection) {
      // 10. 如果还是没找到,就新建一个连接。每个连接都会至少有一个路由。
        if (selectedRoute == null) {
          selectedRoute = routeSelection.next();
        }

        // Create a connection and assign it to this allocation immediately. This makes it possible
        // for an asynchronous cancel() to interrupt the handshake we're about to do.
        route = selectedRoute;
        refusedStreamCount = 0;
        result = new RealConnection(connectionPool, selectedRoute);
        acquire(result, false);
      }
    }
    ...
}

两处调用分别在上述代码的第4个和第9个注释的。这两处调用唯一的区别就是一个没有传route,一个传了route。只有HTTP/2的请求才有这个route值。来看下get方法的实现。

  @Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
   // 1. 检查是否持有锁。
    assert (Thread.holdsLock(this));
    for (RealConnection connection : connections) {
      if (connection.isEligible(address, route)) {
      // 2. Eligible的中文意思是资格,如果这个连接可复用,就取出返回。
        streamAllocation.acquire(connection, true);
        return connection;
      }
    }
    // 3.如果遍历连接池仍没有找到可复用连接,返回null。
    return null;
  }

这里有两处关键点,一个是connection.isEligible方法,一个是streamAllocation.acquire方法。先看下isEligible的实现。

  public boolean isEligible(Address address, @Nullable Route route) {
    //如果这个连接已经分配的流达到了最大可分配的流或者被限制了不能创建新的流,直接返回。
    //HTTP/1 最多支持一路流,HTTP/2支持多路,一般建议不小于100.  具体可看https://stackoverflow.com/questions/39759054/how-many-concurrent-requests-should-we-multiplex-in-http-2
    // If this connection is not accepting new streams, we're done.
    if (allocations.size() >= allocationLimit || noNewStreams) return false;

    // 如果除了主机,其它的变量有一个不同,说明这个连接不是同一个,直接返回。
    // If the non-host fields of the address don't overlap, we're done.
    if (!Internal.instance.equalsNonHost(this.route.address(), address)) return false;

    // 如果连接池中的Address和目标Address完全一致,由于上面已经判断了这个还可以分配流。所以找到直接返回。
    // If the host exactly matches, we're done: this connection can carry the address.
    if (address.url().host().equals(this.route().address().url().host())) {
      return true; // This connection is a perfect match.
    }

    //如果只是主机名不一样,我们要看下是否存在合并连接的情况。
    // At this point we don't have a hostname match. But we still be able to carry the request if
    // our connection coalescing requirements are met. See also:
    // https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding
    // https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/

    // 1. 这个连接必须是HTTP/2的连接
    // 1. This connection must be HTTP/2.
    if (http2Connection == null) return false;

	//2.这些路由必须有同一个IP地址。IP地址和域名的映射通过DNS服务来实现。
    // 2. The routes must share an IP address. This requires us to have a DNS address for both
    // hosts, which only happens after route planning. We can't coalesce connections that use a
    // proxy, since proxies don't tell us the origin server's IP address.
    if (route == null) return false;
    if (route.proxy().type() != Proxy.Type.DIRECT) return false;
    if (this.route.proxy().type() != Proxy.Type.DIRECT) return false;
    if (!this.route.socketAddress().equals(route.socketAddress())) return false;

	// 3.这个连接的服务器证书必须要包含新的主机
    // 3. This connection's server certificate's must cover the new host.
    if (route.address().hostnameVerifier() != OkHostnameVerifier.INSTANCE) return false;
    if (!supportsUrl(address.url())) return false;

	// 4. 证书页的域名必须匹配。
    // 4. Certificate pinning must match the host.
    try {
      address.certificatePinner().check(address.url().host(), handshake().peerCertificates());
    } catch (SSLPeerUnverifiedException e) {
      return false;
    }

    return true; // The caller's address can be carried by this connection.
  }

如果连接池中有可用连接,调用streamAllocation.acquire(connection, true);。

  public void acquire(RealConnection connection, boolean reportedAcquired) {
    assert (Thread.holdsLock(connectionPool));
    if (this.connection != null) throw new IllegalStateException();

	// 将连接池的连接复制给当前连接
    this.connection = connection;
    this.reportedAcquired = reportedAcquired;
    //为该连接添加一个StreamAllocationReference引用。
    connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
  }

get方法的实现就分析完了,它先判断了连接池中是否有和该请求地址一样的可用连接,如果有就将其取出返回,并向该连接的引用计数List<Reference<StreamAllocation>> allocations里添加一个引用。

clean

在put方法里,执行了一个cleanupRunnable任务。

  void put(RealConnection connection) {
    assert (Thread.holdsLock(this));
    if (!cleanupRunning) {
      cleanupRunning = true;
      executor.execute(cleanupRunnable);
    }
    connections.add(connection);
  }

这个任务就是用来完成清理工作的。

  private final Runnable cleanupRunnable = new Runnable() {
    @Override public void run() {
      while (true) {
      //执行一次清理操作,并返回下一次清理的等待时间。
        long waitNanos = cleanup(System.nanoTime());
        if (waitNanos == -1) return;
        
        if (waitNanos > 0) {
          long waitMillis = waitNanos / 1000000L;
          waitNanos -= (waitMillis * 1000000L);
          synchronized (ConnectionPool.this) {
            try {
            //调用wait方法,表示等待
              ConnectionPool.this.wait(waitMillis, (int) waitNanos);
            } catch (InterruptedException ignored) {
            }
          }
        }
      }
    }
  };

wait方法会释放调当前持有的锁,并把任务加入到条件等待队列中,当前线程的状态会变为WATING或者TIME_WATING。如果等待时间到了,或者别的线程调用了notify/nofityAll方法,这个时候任务会从等待队列中移除,重新竞争锁。如果竞争到了,会从wait中返回,由于cleanupRunnable里面是一个死循环,因此会执行下一次清理工作。如果没有竞争到,任务会加入到对象锁等待队列中,线程的状态会变为BLOCKED,只有获得了锁后才会从wait调用中返回。

下面看一下cleanup的原理。

  long cleanup(long now) {
    int inUseConnectionCount = 0;
    int idleConnectionCount = 0;
    RealConnection longestIdleConnection = null;
    long longestIdleDurationNs = Long.MIN_VALUE;

	// 1. 获取锁对象
    // Find either a connection to evict, or the time that the next eviction is due.
    synchronized (this) {
    // 2. 遍历连接池,查找可以清理的连接
      for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
        RealConnection connection = i.next();

		// 3. 如果当前连接正在使用,则查找下一个。
		//判断当前是否在使用的依据就是连接池的List<Reference<StreamAllocation>> allocations列表的大小。
        // If the connection is in use, keep searching.
        if (pruneAndGetAllocationCount(connection, now) > 0) {
          inUseConnectionCount++;
          continue;
        }
		// 4. 如果当前连接没有正在使用,那么空闲数加1
        idleConnectionCount++;

		// 5. 接下来查找空闲时长最长的那个连接
        // If the connection is ready to be evicted, we're done.
        long idleDurationNs = now - connection.idleAtNanos;
        if (idleDurationNs > longestIdleDurationNs) {
          longestIdleDurationNs = idleDurationNs;
          longestIdleConnection = connection;
        }
      }

     // 6 -1. 遍历完成后,如果最长的空闲时间超过了设置的keepAlive时间,一般是5分钟,
     //或者空闲的连接超过了最大空闲数,一般是5个,就把这个连接从连接池中移除并关闭。
      if (longestIdleDurationNs >= this.keepAliveDurationNs
          || idleConnectionCount > this.maxIdleConnections) {
        // We've found a connection to evict. Remove it from the list, then close it below (outside
        // of the synchronized block).
        connections.remove(longestIdleConnection);
      } else if (idleConnectionCount > 0) {
        //  6-2. 如果存在空闲连接数,那么返回的等待时间就是距离保活时间的剩余时间
        // A connection will be ready to evict soon.
        return keepAliveDurationNs - longestIdleDurationNs;
      } else if (inUseConnectionCount > 0) {
      //如果所有的连接都在使用,那么等待5分钟再清理
        // All connections are in use. It'll be at least the keep alive duration 'til we run again.
        return keepAliveDurationNs;
      } else {
      //如果连接池中没有对象,不需要清理。恢复cleanupRunning标志位。
        // No connections, idle or in use.
        cleanupRunning = false;
        return -1;
      }
    }

    closeQuietly(longestIdleConnection.socket());

    // Cleanup again immediately.
    return 0;
  }

清理的原理总结一下:

  • 遍历连接池,计算正在使用的连接数、空闲连接数和查找空闲时间最长的连接。

  • 遍历完成后,如果最长的空闲时间超过了设置的keepAlive时间,一般是5分钟,
    或者空闲的连接超过了最大空闲数,一般是5个,就把这个连接从连接池中移除并关闭。

  • 如果还不满足清理条件,且存在空闲连接数,等待空闲连接已经空闲的时间到距离保活时间后,发起下一次清理。

  • 如果所有的连接都在使用,那么等待5分钟再清理。

  • 如果连接池中没有对象,不需要清理。恢复cleanupRunning标志位。

OkHttp源码解析(三)——连接池复用 1.StreamAllocation#newStream这个方法是在连接是返回stream对象的。我们看这个方法里的这段代码。 RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, 阅读详情

相关推荐

okhttp的应用详解与源码解析--连接池复用机制

文章目录连接的创建连接池 连接的创建是在StreamAllocation对象统筹下完成的,我们前面也说过它早在RetryAndFollowUpInterceptor就被创建了,StreamAllocation对象主要用来管理两个关键角色: RealConnection:真正建立连接的对象,利用Socket建立连接。 ConnectionPool:连接池,用来管理和复用连接。 在里初始化了一个StreamAllocation对象,我们说在这个StreamAllocation对象里初始化了一个Socket对

门心叼龙的专栏 1446

okhttp3连接池清理

okhttp3连接池清理okhttp3连接池RealConnectionPool清理在什么时候触发cleanUp方法cleanUp方法的下半部分cleanUp方法的上半部份pruneAndGetAllocationCount使用okhttp注意点 okhttp3连接池RealConnectionPool 连接池是用来存储连接实例对象RealConnection。存储RealConnection是为了实现连接复用,提高网络连接工作效率,减少内存开支。 class RealConnectionPool( t

zhekecong的博客 2829

鼎捷T100—二次开发:客制化联络对象维护作业 aooi350

鼎捷T100二次开发,客制化

weixin_52342654的博客 1133

Spring Cloud OkHttp设计原理

Spring Cloud 框架最底层核心的组件就是服务调用方式,一般Spring Cloud框架采用的是HTTP的调用框架,本文将在 Spring Cloud应用场景下,介绍组件OkHttp3的设计原理。 1. Spring Cloud的接口调用工作模式 Spring Cloud作为组合式的分布式微服务解决方案,再服务调用上,至少需要解决如下几个环节: 面向接口的编程形式 接口调用过程,除了拼...

我的程序人生(亦山札记) 3359

ur3手眼标定+realsenseL515

知乎 csdn

AI_future的博客 978

网工、运维利器:MobaXterm,一款全能的远程工具

你好,这里是网络技术联盟站,我是瑞哥。是一款由Mobatek开发的多功能远程计算机管理工具,集成了多种网络协议和工具,专为网络工程师和系统管理员设计。它提供了丰富的远程连接功能,包括SSH、RDP、VNC、X11转发等,支持文件传输和多标签界面,内置了文本编辑器和多种网络工具,是一款全能的远程管理软件。MobaXterm的开发起始于2008年,最初只是一个简单的X服务器,但随着用户需求的不断增加和软件的逐步完善,MobaXterm逐渐发展成了一款功能全面、界面友好的远程管理工具。

网络技术联盟站 5230

【JDBC】JDBC和连接池的关系

1. 前言 一直以为连接池是对JDBC的封装,今天再次回顾Java基础的时,才发现并非完全如此。连接池只是包装/操作了JDBC API 接口,然后再实际运行的时候,这些接口的具体实现则 下面是知乎上的一个图:JDBC和连接池的关系 2. JDBC和连接池的关系 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。简单说可以通过JDBC来访问数据库, 而连

pangpd的博客 2639

【DRAM存储器六十一】LPDDR5介绍--LPDDR5/5X/4/4X IO结构

简述LPDDR5/5X/4/4X IO结构及等效电路下的VREF理论值计算

highman110的博客 484

OkHttp3连接池复用原理分析

OkHttp3连接池原理:OkHttp3使用ConnectionPool连接池来复用链接,其原理是:当用户发起请求是,首先在链接池中检查是否有符合要求的链接(复用就在这里发生),如果有就用该链接发起网络请求,如果没有就创建一个链接发起请求。这种复用机制可以极大的减少网络延时并加快网络的请求和响应速度。 源码分析 // 最多保存 5个 处于空闲状态的连接,连接的默认保活时间为 5分钟 public ConnectionPool() { this(5, 5, TimeUnit

u012845099的博客 7109

【赛题解析&命令脚本配置】2023年全国职业院校技能大赛GZ073网络系统管理赛项赛题第1套模块A:网络构建 & (五)无线网络配置+(六)出口网络配置-命令配置

【赛题解析&命令脚本配置】2023年全国职业院校技能大赛GZ073网络系统管理赛项赛题第1套模块A:网络构建 & (五)无线网络配置+(六)出口网络配置-命令配置

君逍遥o 408

Nuxt - 项目打包部署及上线到服务器流程(SSR 服务端渲染)

Nuxt-项目打包部署及上线到服务器流程,详细介绍Nuxt项目上线部署,服务端渲染(ssr)项目及nuxt项目部署过程,nuxt项目打包上线,Nuxt项目的部署教程,NuxtJS项目部署方案,Nuxt如何部署到线上,nuxt项目部署篇,Nuxt项目搭建到Nuxt项目部署,Nuxt项目从开始到部署,详细教程。服务器部署nuxt项目,nuxt服务端部署上线,在服务器上部署nuxt项目,NuxtJS项目——命令和部署。......

🏆 前端领域优质创作者 1万+

okhttp连接池_okhttp源码解析

OkHttp 源码解析OkHttp是一个高效的客户端HTTP请求框架,在Android客户端开发被广泛使用。最近学习了HTTP协议的相关知识,学习一下OKHttp是如何使用HTTP协议请求网络的。学习OKhttp源码就要先看全局,然后根据使用需要再研究细节,体会其精妙之处。了解它的原理,并加以实践,可以实现一个简单的HTTP请求客户端。实现HTTPS请求-响应思路根据TCP/IP五层网络模型自下而...

weixin_39531688的博客 490

Codex入门00-初识神器(小白入门:到底什么是Codex CLI,和ChatGPT写代码有什么不同?)

OpenAI Codex CLI 是 OpenAI 于2025年推出的终端原生AI编程代理,不是在网页里聊天写代码,而是直接在你的终端里自主读代码、改文件、跑命令。它拥有三种安全模式、/goal 持久化任务、云端沙箱执行等独特能力。本文从零介绍 Codex CLI 的核心概念、与 ChatGPT 写代码的本质区别、典型使用场景和费用说明,让你全面认识这个改变开发方式的神器。

yueyeguzhuo的专栏 302

OkHttp源码彻底解析(五)OkHttp连接池

本系列文章: OkHttp源码彻底解析(一)OkHttp请求流程 OkHttp源码彻底解析(二)OkHttp架构及API源码 OkHttp源码彻底解析(三)OkHttp3.0拦截器原理——责任链模式 OkHttp源码彻底解析(四)OkHttp拦截器的作用 OkHttp源码彻底解析(五)OkHttp连接池 目录 OkHttp连接池 连接池的意义——KeepAl...

qq_39037047的博客 2836

【Matlab光伏功率预测】基于GRU门控循环单元的光伏功率时间序列预测(附MATLAB代码)

【Matlab光伏功率预测】基于GRU门控循环单元的光伏功率时间序列预测(附MATLAB代码)

k8291121的博客 1100

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

深入解析OkHttp核心架构:从责任链到连接池的源码级实践

在现代网络编程中,高效、可靠的HTTP客户端是构建高性能应用的关键组件。其核心原理通常围绕请求/响应模型、连接复用和异步处理展开。通过责任链模式,可以将复杂的网络处理流程分解为一系列职责单一的拦截器,实现高度可扩展的请求处理管道。连接池技术则通过复用TCP连接,显著减少握手开销,成为提升性能的基石。这些技术价值在于,它们不仅优化了资源利用,降低了延迟,还为复杂网络场景(如重试、缓存、监控)提供了统一的扩展点。在Android和Java后端开发中,深入理解这些机制对于诊断超时、内存泄漏等生产问题,以及定制化网

weixin_30730053的博客 604

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

OKHttp源码解析 (复用连接池)

原文地址:https://www.jianshu.com/p/6166d28983a2 复用连接池 相关的主要三个类 RealConnection ConnectionPool StreamAllocation 对流的处理使用Okio,Okio是okhttp中用于处理io的模块,没接触过的读者可以参考这篇文章:https://www.jianshu.com/p/f5941bcf3a2d RealConnection 要点 代表着链接socket的链路,如果拥有了一个RealConnection.

许佳佳的博客 1013

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

OkHttp3源码解析(三)——连接池复用

本文基于OkHttp3的3.11.0版本 implementation 'com.squareup.okhttp3:okhttp:3.11.0' 我们已经分析了OkHttp3的拦截器链和缓存策略,今天我们再来看看OkHttp3的连接池复用。 客户端和服务器建立socket连接需要经历TCP的三次握手和四次挥手,是一种比较消耗资源的动作。Http中有一种keepAlive connections的...

AntDream的程序世界 677

OkHttp3源码解析05-连接池

我们都知道HTTP协议采用请求-应答模式,为了解决TCP握手和挥手效率的问题,HTTP有一个keepalive模式。当使用普通模式(非KeppAlive模式)时,每个请求-应答都要新建一个连接,完成后立即断开连接(HTTP是无连接的协议) 当使用KeepAlive模式(又称持久连接、连接重用)时,KeepAlive功能使客户端到服务器连接持续有效,避免了频繁地重新建立连接。 http 1.

朱豪凯的博客 1596

okhttp网络框架源码解析十四okhttp框架连接池put()方法

1、ConnectionPool 不管是okhttp协议1.1也好,还是2.0也好,它们的keep-alive机制,或者2.0的多路复用机制,他们都需要引入一个连接池的概念,来维护整个okhttp的网络连接。okhttp会将客户端和服务端抽象为一个connection类,而realConnection就是它的实现类。为了管理所有的实现类,okhttp为我们提供了connectionPool这个类...

nicolelili1的专栏 316

OkHttp 源码解析(三):连接池

简介 上一篇文章(OkHttp 源码解析(二):建立连接)分析了 OkHttp 建立连接的过程,主要涉及到的几个类包括 StreamAllocation、RealConnection 以及 HttpCodec,其中 RealConnection 封装了底层的 Socket。Socket 建立了 TCP 连接,这是需要消耗时间和资源的,而 ...

weixin_33950035的博客 207

okhttp网络框架源码解析十五okhttp框架连接池connection回收

1、 connection如何在指定的时间内失效,对它进行有效的连接以及清除,这些作用都是非常重要的 连接池的回收利用了Gc的回收算法 streamAllocation的数量会间接地变为0,当它变为0的时候会被线程池检测到,然后回收 在connectionPool当中,有一个独立的线程会开启cleanUpRunnable 2、 cleanupRunnable是一个死循环,...

nicolelili1的专栏 468

android http 线程池,OkHttp 源码解析(三):连接池

简介上一篇文章(OkHttp 源码解析(二):建立连接)分析了 OkHttp 建立连接的过程,主要涉及到的几个类包括 StreamAllocation、RealConnection 以及 HttpCodec,其中 RealConnection 封装了底层的 Socket。Socket 建立了 TCP 连接,这是需要消耗时间和资源的,而 OkHttp 则使用连接池来管理这里连接,进行连接的重用,提高...

weixin_39644611的博客 333

Okhttp链接池的使用

连接池也就是链接的复用,可以比作成《超人总动员中》冰冻超人Frozone的滑道。当他想要从起点到达终点的时候,会建立一条滑道(RealConnection)),滑道会因温差而融化,但显然每次都从新创建会消耗很多资源,于是其更新技术,让滑道能够保持一段时间。其中每条滑道又分为了不同的干道,类似我们的公路。一条路上可以并行跑好多车辆,也就是链接中的流。

2401_89284596的博客 1180
上一篇: Android-Retrofit源码解析(一)调用流程(下)
下一篇: 解决-IntelliJ IDEA:源值1.5已过时, 将在未来所有发行版中删除
Huang-Lin
博客等级 码龄10年 20粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值