自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試突擊:OkHttp 原理八連問(wèn)

移動(dòng)開(kāi)發(fā) Android
OkHttp 可以說(shuō)是 Android 開(kāi)發(fā)中最常見(jiàn)的網(wǎng)絡(luò)請(qǐng)求框架,OkHttp 使用方便,擴(kuò)展性強(qiáng),功能強(qiáng)大,OKHttp 源碼與原理也是面試中的???。

[[434054]]

OkHttp 可以說(shuō)是 Android 開(kāi)發(fā)中最常見(jiàn)的網(wǎng)絡(luò)請(qǐng)求框架,OkHttp 使用方便,擴(kuò)展性強(qiáng),功能強(qiáng)大,OKHttp 源碼與原理也是面試中的???。

但是 OKHttp 的源碼內(nèi)容比較多,想要學(xué)習(xí)它的源碼往往千頭萬(wàn)緒,一時(shí)抓不住重點(diǎn). 本文從幾個(gè)問(wèn)題出發(fā)梳理 OKHttp 相關(guān)知識(shí)點(diǎn),以便快速構(gòu)建 OKHttp 知識(shí)體,本文主要包括以下內(nèi)容

  1.   OKHttp 請(qǐng)求的整體流程是怎樣的?
  2.   OKHttp 分發(fā)器是怎樣工作的?
  3.   OKHttp 攔截器是如何工作的?
  4.   應(yīng)用攔截器和網(wǎng)絡(luò)攔截器有什么區(qū)別?
  5.   OKHttp 如何復(fù)用 TCP 連接?
  6.   OKHttp 空閑連接如何清除?
  7.   OKHttp 有哪些優(yōu)點(diǎn)?
  8.   OKHttp 框架中用到了哪些設(shè)計(jì)模式?

1. OKHttp請(qǐng)求整體流程介紹

首先來(lái)看一個(gè)最簡(jiǎn)單的 Http 請(qǐng)求是如何發(fā)送的。 

  1. val okHttpClient = OkHttpClient()  
  2. val request: RequestRequest = Request.Builder()  
  3.     .url("https://www.google.com/")  
  4.     .build()  
  5. okHttpClient.newCall(request).enqueue(object :Callback{  
  6.     override fun onFailure(call: Call, e: IOException) {  
  7.     }  
  8.     override fun onResponse(call: Call, response: Response) {  
  9.     }  
  10. }) 

這段代碼看起來(lái)比較簡(jiǎn)單,OkHttp 請(qǐng)求過(guò)程中最少只需要接觸 OkHttpClient、Request、Call、 Response,但是框架內(nèi)部會(huì)進(jìn)行大量的邏輯處理。

所有網(wǎng)絡(luò)請(qǐng)求的邏輯大部分集中在攔截器中,但是在進(jìn)入攔截器之前還需要依靠分發(fā)器來(lái)調(diào)配請(qǐng)求任務(wù)。關(guān)于分發(fā)器與攔截器,我們?cè)谶@里先簡(jiǎn)單介紹下,后續(xù)會(huì)有更加詳細(xì)的講解

  1.  分發(fā)器:內(nèi)部維護(hù)隊(duì)列與線(xiàn)程池,完成請(qǐng)求調(diào)配;
  2.  攔截器:五大默認(rèn)攔截器完成整個(gè)請(qǐng)求過(guò)程。

整個(gè)網(wǎng)絡(luò)請(qǐng)求過(guò)程大致如上所示

  1.  通過(guò)建造者模式構(gòu)建 OKHttpClient 與 Request
  2.  OKHttpClient 通過(guò) newCall 發(fā)起一個(gè)新的請(qǐng)求
  3.  通過(guò)分發(fā)器維護(hù)請(qǐng)求隊(duì)列與線(xiàn)程池,完成請(qǐng)求調(diào)配
  4.  通過(guò)五大默認(rèn)攔截器完成請(qǐng)求重試,緩存處理,建立連接等一系列操作
  5.  得到網(wǎng)絡(luò)請(qǐng)求結(jié)果

2. OKHttp分發(fā)器是怎樣工作的?

分發(fā)器的主要作用是維護(hù)請(qǐng)求隊(duì)列與線(xiàn)程池,比如我們有100個(gè)異步請(qǐng)求,肯定不能把它們同時(shí)請(qǐng)求,而是應(yīng)該把它們排隊(duì)分個(gè)類(lèi),分為正在請(qǐng)求中的列表和正在等待的列表, 等請(qǐng)求完成后,即可從等待中的列表中取出等待的請(qǐng)求,從而完成所有的請(qǐng)求

而這里同步請(qǐng)求各異步請(qǐng)求又略有不同

同步請(qǐng)求 

  1. synchronized void executed(RealCall call) {  
  2.  runningSyncCalls.add(call);  

因?yàn)橥秸?qǐng)求不需要線(xiàn)程池,也不存在任何限制。所以分發(fā)器僅做一下記錄。后續(xù)按照加入隊(duì)列的順序同步請(qǐng)求即可

異步請(qǐng)求 

  1. synchronized void enqueue(AsyncCall call) {  
  2.  //請(qǐng)求數(shù)最大不超過(guò)64,同一Host請(qǐng)求不能超過(guò)5個(gè)  
  3.  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost)    {  
  4.   runningAsyncCalls.add(call);  
  5.   executorService().execute(call);  
  6.  } else {  
  7.   readyAsyncCalls.add(call);  
  8.  }  

當(dāng)正在執(zhí)行的任務(wù)未超過(guò)最大限制64,同時(shí)同一 Host 的請(qǐng)求不超過(guò)5個(gè),則會(huì)添加到正在執(zhí)行隊(duì)列,同時(shí)提交給線(xiàn)程池。否則先加入等待隊(duì)列。每個(gè)任務(wù)完成后,都會(huì)調(diào)用分發(fā)器的 finished 方法,這里面會(huì)取出等待隊(duì)列中的任務(wù)繼續(xù)執(zhí)行

3. OKHttp攔截器是怎樣工作的?

經(jīng)過(guò)上面分發(fā)器的任務(wù)分發(fā),下面就要利用攔截器開(kāi)始一系列配置了 

  1. # RealCall  
  2.   override fun execute(): Response {  
  3.     try {  
  4.       client.dispatcher.executed(this)  
  5.       return getResponseWithInterceptorChain()  
  6.     } finally {  
  7.       client.dispatcher.finished(this) 
  8.     }  
  9.   } 

我們?cè)賮?lái)看下 RealCall的execute方法,可以看出,最后返回了 getResponseWithInterceptorChain ,責(zé)任鏈的構(gòu)建與處理其實(shí)就是在這個(gè)方法里面 

  1. internal fun getResponseWithInterceptorChain(): Response {  
  2.     // Build a full stack of interceptors.  
  3.     val interceptors = mutableListOf<Interceptor>()  
  4.     interceptors += client.interceptors 
  5.     interceptors += RetryAndFollowUpInterceptor(client)  
  6.     interceptors += BridgeInterceptor(client.cookieJar)  
  7.     interceptors += CacheInterceptor(client.cache) 
  8.     interceptors += ConnectInterceptor  
  9.     if (!forWebSocket) {  
  10.       interceptors += client.networkInterceptors  
  11.     }  
  12.     interceptors += CallServerInterceptor(forWebSocket)  
  13.     val chain = RealInterceptorChain 
  14.         call = this,interceptorsinterceptors = interceptors,index = 0  
  15.     )  
  16.     val response = chain.proceed(originalRequest)  
  17.   } 

如上所示,構(gòu)建了一個(gè) OkHttp 攔截器的責(zé)任鏈

責(zé)任鏈,顧名思義,就是用來(lái)處理相關(guān)事務(wù)責(zé)任的一條執(zhí)行鏈,執(zhí)行鏈上有多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都有機(jī)會(huì)(條件匹配)處理請(qǐng)求事務(wù),如果某個(gè)節(jié)點(diǎn)處理完了就可以根據(jù)實(shí)際業(yè)務(wù)需求傳遞給下一個(gè)節(jié)點(diǎn)繼續(xù)處理或者返回處理完畢。

如上所示責(zé)任鏈添加的順序及作用,如下表所示:

攔截器 作用
應(yīng)用攔截器 拿到的是原始請(qǐng)求,可以添加一些自定義 header、通用參數(shù)、參數(shù)加密、網(wǎng)關(guān)接入等等。
RetryAndFollowUpInterceptor 處理錯(cuò)誤重試和重定向
BridgeInterceptor 應(yīng)用層和網(wǎng)絡(luò)層的橋接攔截器,主要工作是為請(qǐng)求添加cookie、添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存響應(yīng)結(jié)果的cookie,如果響應(yīng)使用gzip壓縮過(guò),則還需要進(jìn)行解壓。
CacheInterceptor 緩存攔截器,如果命中緩存則不會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求。
ConnectInterceptor 連接攔截器,內(nèi)部會(huì)維護(hù)一個(gè)連接池,負(fù)責(zé)連接復(fù)用、創(chuàng)建連接(三次握手等等)、釋放連接以及創(chuàng)建連接上的socket流。
networkInterceptors(網(wǎng)絡(luò)攔截器) 用戶(hù)自定義攔截器,通常用于監(jiān)控網(wǎng)絡(luò)層的數(shù)據(jù)傳輸。
CallServerInterceptor 請(qǐng)求攔截器,在前置準(zhǔn)備工作完成后,真正發(fā)起了網(wǎng)絡(luò)請(qǐng)求。

我們的網(wǎng)絡(luò)請(qǐng)求就是這樣經(jīng)過(guò)責(zé)任鏈一級(jí)一級(jí)的遞推下去,最終會(huì)執(zhí)行到 CallServerInterceptor的intercept 方法,此方法會(huì)將網(wǎng)絡(luò)響應(yīng)的結(jié)果封裝成一個(gè) Response 對(duì)象并 return。之后沿著責(zé)任鏈一級(jí)一級(jí)的回溯,最終就回到 getResponseWithInterceptorChain 方法的返回,如下圖所示:

4. 應(yīng)用攔截器和網(wǎng)絡(luò)攔截器有什么區(qū)別?

從整個(gè)責(zé)任鏈路來(lái)看,應(yīng)用攔截器是最先執(zhí)行的攔截器,也就是用戶(hù)自己設(shè)置 request 屬性后的原始請(qǐng)求,而網(wǎng)絡(luò)攔截器位于 ConnectInterceptor 和 CallServerInterceptor 之間,此時(shí)網(wǎng)絡(luò)鏈路已經(jīng)準(zhǔn)備好,只等待發(fā)送請(qǐng)求數(shù)據(jù)。它們主要有以下區(qū)別

    1.  首先,應(yīng)用攔截器在 RetryAndFollowUpInterceptor 和 CacheInterceptor 之前,所以一旦發(fā)生錯(cuò)誤重試或者網(wǎng)絡(luò)重定向,網(wǎng)絡(luò)攔截器可能執(zhí)行多次,因?yàn)橄喈?dāng)于進(jìn)行了二次請(qǐng)求,但是應(yīng)用攔截器永遠(yuǎn)只會(huì)觸發(fā)一次。另外如果在 CacheInterceptor 中命中了緩存就不需要走網(wǎng)絡(luò)請(qǐng)求了,因此會(huì)存在短路網(wǎng)絡(luò)攔截器的情況。

    2.  其次,除了 CallServerInterceptor 之外,每個(gè)攔截器都應(yīng)該至少調(diào)用一次 realChain.proceed 方法。實(shí)際上在應(yīng)用攔截器這層可以多次調(diào)用 proceed 方法(本地異常重試)或者不調(diào)用 proceed 方法(中斷),但是網(wǎng)絡(luò)攔截器這層連接已經(jīng)準(zhǔn)備好,可且僅可調(diào)用一次 proceed 方法。

    3.  最后,從使用場(chǎng)景看,應(yīng)用攔截器因?yàn)橹粫?huì)調(diào)用一次,通常用于統(tǒng)計(jì)客戶(hù)端的網(wǎng)絡(luò)請(qǐng)求發(fā)起情況;而網(wǎng)絡(luò)攔截器一次調(diào)用代表了一定會(huì)發(fā)起一次網(wǎng)絡(luò)通信,因此通??捎糜诮y(tǒng)計(jì)網(wǎng)絡(luò)鏈路上傳輸?shù)臄?shù)據(jù)。

5. OKHttp如何復(fù)用TCP連接?

ConnectInterceptor 的主要工作就是負(fù)責(zé)建立 TCP 連接,建立 TCP 連接需要經(jīng)歷三次握手四次揮手等操作,如果每個(gè) HTTP 請(qǐng)求都要新建一個(gè) TCP 消耗資源比較多 而 Http1.1 已經(jīng)支持 keep-alive ,即多個(gè) Http 請(qǐng)求復(fù)用一個(gè) TCP 連接,OKHttp 也做了相應(yīng)的優(yōu)化,下面我們來(lái)看下 OKHttp 是怎么復(fù)用 TCP 連接的

ConnectInterceptor 中查找連接的代碼會(huì)最終會(huì)調(diào)用到 ExchangeFinder.findConnection 方法,具體如下: 

  1. # ExchangeFinder  
  2. //為承載新的數(shù)據(jù)流 尋找 連接。尋找順序是 已分配的連接、連接池、新建連接  
  3. private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,  
  4.     int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {  
  5.   synchronized (connectionPool) {  
  6.     // 1.嘗試使用 已給數(shù)據(jù)流分配的連接.(例如重定向請(qǐng)求時(shí),可以復(fù)用上次請(qǐng)求的連接)  
  7.     releasedConnection = transmitter.connection;  
  8.     result = transmitter.connection;  
  9.     if (result == null) {  
  10.       // 2. 沒(méi)有已分配的可用連接,就嘗試從連接池獲取。(連接池稍后詳細(xì)講解)  
  11.       if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {  
  12.         result = transmitter.connection;  
  13.       }  
  14.     }  
  15.   }  
  16.   synchronized (connectionPool) {  
  17.     if (newRouteSelection) {  
  18.       //3. 現(xiàn)在有了IP地址,再次嘗試從連接池獲取??赡軙?huì)因?yàn)檫B接合并而匹配。(這里傳入了routes,上面的傳的null)  
  19.       routes = routeSelection.getAll(); 
  20.      if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, false)) {  
  21.         foundPooledConnection = true 
  22.         result = transmitter.connection;  
  23.       }  
  24.     }  
  25.   // 4.第二次沒(méi)成功,就把新建的連接,進(jìn)行TCP + TLS 握手,與服務(wù)端建立連接. 是阻塞操作  
  26.   result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,  
  27.       connectionRetryEnabled, call, eventListener);  
  28.   synchronized (connectionPool) {  
  29.     // 5. 最后一次嘗試從連接池獲取,注意最后一個(gè)參數(shù)為true,即要求 多路復(fù)用(http2.0)  
  30.     //意思是,如果本次是http2.0,那么為了保證 多路復(fù)用性,(因?yàn)樯厦娴奈帐植僮鞑皇蔷€(xiàn)程安全)會(huì)再次確認(rèn)連接池中此時(shí)是否已有同樣連接  
  31.     if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {  
  32.       // 如果獲取到,就關(guān)閉我們創(chuàng)建里的連接,返回獲取的連接  
  33.       result = transmitter.connection;  
  34.     } else {  
  35.       //最后一次嘗試也沒(méi)有的話(huà),就把剛剛新建的連接存入連接池  
  36.       connectionPool.put(result);  
  37.     }  
  38.   }  
  39.   return result;  

上面精簡(jiǎn)了部分代碼,可以看出,連接攔截器使用了5種方法查找連接:

  1.  首先會(huì)嘗試使用 已給請(qǐng)求分配的連接。(已分配連接的情況例如重定向時(shí)的再次請(qǐng)求,說(shuō)明上次已經(jīng)有了連接)
  2.  若沒(méi)有已分配的可用連接,就嘗試從連接池中 匹配獲取。因?yàn)榇藭r(shí)沒(méi)有路由信息,所以匹配條件:address 一致—— host、port、代理等一致,且匹配的連接可以接受新的請(qǐng)求。
  3.  若從連接池沒(méi)有獲取到,則傳入 routes 再次嘗試獲取,這主要是針對(duì) Http2.0 的一個(gè)操作, Http2.0 可以復(fù)用 square.com 與 square.ca 的連接
  4.  若第二次也沒(méi)有獲取到,就創(chuàng)建 RealConnection 實(shí)例,進(jìn)行 TCP + TLS 握手,與服務(wù)端建立連接。
  5.  此時(shí)為了確保 Http2.0 連接的多路復(fù)用性,會(huì)第三次從連接池匹配。因?yàn)樾陆⒌倪B接的握手過(guò)程是非線(xiàn)程安全的,所以此時(shí)可能連接池新存入了相同的連接。
  6.  第三次若匹配到,就使用已有連接,釋放剛剛新建的連接;若未匹配到,則把新連接存入連接池并返回。

以上就是連接攔截器嘗試復(fù)用連接的操作,流程圖如下:

6. OKHttp空閑連接如何清除?

上面說(shuō)到我們會(huì)建立一個(gè) TCP 連接池,但如果沒(méi)有任務(wù)了,空閑的連接也應(yīng)該及時(shí)清除,OKHttp 是如何做到的呢? 

  1. # RealConnectionPool  
  2.  private val cleanupQueue: TaskQueue = taskRunner.newQueue()  
  3.  private val cleanupTask = object : Task("$okHttpName ConnectionPool") {  
  4.    override fun runOnce(): Long = cleanup(System.nanoTime())  
  5.  }  
  6.  long cleanup(long now) {  
  7.    int inUseConnectionCount = 0;//正在使用的連接數(shù)  
  8.    int idleConnectionCount = 0;//空閑連接數(shù)  
  9.    RealConnection longestIdleConnection = null;//空閑時(shí)間最長(zhǎng)的連接  
  10.    long longestIdleDurationNs = Long.MIN_VALUE;//最長(zhǎng)的空閑時(shí)間 
  11.    //遍歷連接:找到待清理的連接, 找到下一次要清理的時(shí)間(還未到最大空閑時(shí)間)  
  12.    synchronized (this) {  
  13.      for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {  
  14.        RealConnection connection = i.next();  
  15.        //若連接正在使用,continue,正在使用連接數(shù)+1  
  16.        if (pruneAndGetAllocationCount(connection, now) > 0) {  
  17.          inUseConnectionCount++;  
  18.          continue;  
  19.        }  
  20.  //空閑連接數(shù)+1  
  21.        idleConnectionCount++;  
  22.        // 賦值最長(zhǎng)的空閑時(shí)間和對(duì)應(yīng)連接  
  23.        long idleDurationNs = now - connection.idleAtNanos;  
  24.        if (idleDurationNs > longestIdleDurationNs) {  
  25.          longestIdleDurationNs = idleDurationNs 
  26.          longestIdleConnection = connection 
  27.        }  
  28.      }  
  29.   //若最長(zhǎng)的空閑時(shí)間大于5分鐘 或 空閑數(shù) 大于5,就移除并關(guān)閉這個(gè)連接  
  30.      if (longestIdleDurationNs >= this.keepAliveDurationNs  
  31.          || idleConnectionCount > this.maxIdleConnections) {  
  32.        connections.remove(longestIdleConnection);  
  33.      } else if (idleConnectionCount > 0) {  
  34.        // else,就返回 還剩多久到達(dá)5分鐘,然后wait這個(gè)時(shí)間再來(lái)清理  
  35.        return keepAliveDurationNs - longestIdleDurationNs;  
  36.      } else if (inUseConnectionCount > 0) {  
  37.        //連接沒(méi)有空閑的,就5分鐘后再?lài)L試清理.  
  38.        return keepAliveDurationNs;  
  39.      } else {  
  40.        // 沒(méi)有連接,不清理  
  41.        cleanupRunning = false 
  42.        return -1;  
  43.      }  
  44.    }  
  45. //關(guān)閉移除的連接  
  46.    closeQuietly(longestIdleConnection.socket());  
  47.    //關(guān)閉移除后 立刻 進(jìn)行下一次的 嘗試清理  
  48.    return 0;  
  49.  } 

思路還是很清晰的:

    1.  在將連接加入連接池時(shí)就會(huì)啟動(dòng)定時(shí)任務(wù)

    2.  有空閑連接的話(huà),如果最長(zhǎng)的空閑時(shí)間大于5分鐘 或 空閑數(shù) 大于5,就移除關(guān)閉這個(gè)最長(zhǎng)空閑連接;如果 空閑數(shù) 不大于5 且 最長(zhǎng)的空閑時(shí)間不大于5分鐘,就返回到5分鐘的剩余時(shí)間,然后等待這個(gè)時(shí)間再來(lái)清理。

    3.  沒(méi)有空閑連接就等5分鐘后再?lài)L試清理。

    4.  沒(méi)有連接不清理。

流程如下圖所示:

7. OKHttp有哪些優(yōu)點(diǎn)?

  •  使用簡(jiǎn)單,在設(shè)計(jì)時(shí)使用了外觀模式,將整個(gè)系統(tǒng)的復(fù)雜性給隱藏起來(lái),將子系統(tǒng)接口通過(guò)一個(gè)客戶(hù)端 OkHttpClient 統(tǒng)一暴露出來(lái)。
  •  擴(kuò)展性強(qiáng),可以通過(guò)自定義應(yīng)用攔截器與網(wǎng)絡(luò)攔截器,完成用戶(hù)各種自定義的需求
  •  功能強(qiáng)大,支持 Spdy、Http1.X、Http2、以及 WebSocket 等多種協(xié)議
  •  通過(guò)連接池復(fù)用底層 TCP(Socket),減少請(qǐng)求延時(shí)
  •  無(wú)縫的支持 GZIP 減少數(shù)據(jù)流量
  •  支持?jǐn)?shù)據(jù)緩存,減少重復(fù)的網(wǎng)絡(luò)請(qǐng)求
  •  支持請(qǐng)求失敗自動(dòng)重試主機(jī)的其他 ip,自動(dòng)重定向

8. OKHttp框架中用到了哪些設(shè)計(jì)模式?

  •  構(gòu)建者模式:OkHttpClient 與 Request 的構(gòu)建都用到了構(gòu)建者模式
  •  外觀模式:OkHttp使用了外觀模式,將整個(gè)系統(tǒng)的復(fù)雜性給隱藏起來(lái),將子系統(tǒng)接口通過(guò)一個(gè)客戶(hù)端 OkHttpClient 統(tǒng)一暴露出來(lái)
  •  責(zé)任鏈模式: OKHttp 的核心就是責(zé)任鏈模式,通過(guò)5個(gè)默認(rèn)攔截器構(gòu)成的責(zé)任鏈完成請(qǐng)求的配置
  •  享元模式: 享元模式的核心即池中復(fù)用, OKHttp 復(fù)用 TCP 連接時(shí)用到了連接池,同時(shí)在異步請(qǐng)求中也用到了線(xiàn)程池  

 

責(zé)任編輯:龐桂玉 來(lái)源: 安卓開(kāi)發(fā)精選
相關(guān)推薦

2022-02-14 08:25:50

Go語(yǔ)言面試

2010-05-13 10:40:56

富士康

2024-08-07 13:40:00

2021-07-12 07:08:52

TCP協(xié)議面試

2014-12-22 11:28:01

2020-09-30 18:19:27

RedisJava面試

2022-01-05 09:55:26

asynawait前端

2014-12-21 08:49:53

2022-05-14 21:19:22

ThreadLocaJDKsynchroniz

2019-12-19 09:23:45

Java多線(xiàn)程數(shù)據(jù)

2022-07-11 07:10:48

HTTP協(xié)議類(lèi)型

2022-04-11 07:40:45

synchroniz靜態(tài)方法程序

2022-02-28 07:01:22

線(xiàn)程中斷interrupt

2015-04-07 16:09:28

鋼七連華為

2020-07-28 00:58:20

IP地址子網(wǎng)TCP

2021-12-01 11:50:50

HashMap面試Java

2022-05-05 07:38:32

volatilJava并發(fā)

2022-06-06 07:35:26

MySQLInnoDBMyISAM

2022-07-27 07:36:01

TCP可靠性

2022-04-20 07:47:00

notify喚醒線(xiàn)程JVM
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)