Connect with us

Разработка

Как OkHttpClient повышает сетевую производительность

Замечательные абстракции, предоставляемые сетевой библиотекой OkHttp от компании Square, как правило, заслоняют собой оптимизацию, которая часто остается незамеченной разработчиками.

Фото аватара

Опубликовано

/

     
     

Замечательные абстракции, предоставляемые сетевой библиотекой OkHttp от компании Square, как правило, заслоняют собой оптимизацию, которая часто остается незамеченной разработчиками. А ведь библиотека использует стандартное соединение на основе TCP handshake для новых запросов и предлагает пул соединений по умолчанию из 5 соединений (каждое активно в течение 5 минут) для повторного использования ранее установленного TCP-соединения для последующих запросов.

Как OkHttpClient повышает сетевую производительность

class ConnectionPool internal constructor(
  internal val delegate: RealConnectionPool
) {

  constructor(
    maxIdleConnections: Int,
    keepAliveDuration: Long,
    timeUnit: TimeUnit
  ) : this(RealConnectionPool(
      taskRunner = TaskRunner.INSTANCE,
      maxIdleConnections = maxIdleConnections,
      keepAliveDuration = keepAliveDuration,
      timeUnit = timeUnit
  ))

  constructor() : this(5, 5, TimeUnit.MINUTES)

   ...
}

Под капотом

Как пул соединений повышает производительность сети? Это можно проверить с помощью специфического EventListener, связанного с вызовом OkHttp.

private class OkHttpEventListenerFactory: EventListener.Factory {
    override fun create(call: Call): EventListener {
        return object: EventListener() {

            override fun callStart(call: Call) {
                Log.d("OkHttp", "CallStart")
            }

            override fun callEnd(call: Call) {
                Log.d("OkHttp", "CallEnd")
            }

            override fun dnsStart(call: Call, domainName: String) {
                Log.d("OkHttp", "dnsStart")
            }

            override fun dnsEnd(
                call: Call,
                domainName: String,
                inetAddressList: List<InetAddress>
            ) {
                Log.d("OkHttp", "dnsEnd")
            }

            override fun secureConnectStart(call: Call) {
                Log.d("OkHttp", "secureConnectStart")
            }

            override fun secureConnectEnd(call: Call, handshake: Handshake?) {
                Log.d("OkHttp", "secureConnectEnd")
            }

            override fun connectStart(
                call: Call,
                inetSocketAddress: InetSocketAddress,
                proxy: Proxy
            ) {
                Log.d("OkHttp", "connectStart")
            }

            override fun connectEnd(
                call: Call,
                inetSocketAddress: InetSocketAddress,
                proxy: Proxy,
                protocol: Protocol?
            ) {
                Log.d("OkHttp", "connectEnd")
            }

            override fun connectionAcquired(call: Call, connection: Connection) {
                Log.d("OkHttp", "connectionAcquired")
            }

            override fun connectionReleased(call: Call, connection: Connection) {
                Log.d("OkHttp", "connectionReleased")
            }

            override fun requestHeadersStart(call: Call) {
                Log.d("OkHttp", "requestHeadersStart")
            }

            override fun requestHeadersEnd(call: Call, request: Request) {
                Log.d("OkHttp", "requestHeadersEnd")
            }

            override fun requestBodyStart(call: Call) {
                Log.d("OkHttp", "requestBodyStart")
            }

            override fun requestBodyEnd(call: Call, byteCount: Long) {
                Log.d("OkHttp", "requestBodyEnd")
            }

            override fun responseHeadersStart(call: Call) {
                Log.d("OkHttp", "responseHeadersStart")
            }

            override fun responseHeadersEnd(call: Call, response: Response) {
                Log.d("OkHttp", "responseHeadersEnd")
            }

            override fun responseBodyStart(call: Call) {
                Log.d("OkHttp", "responseBodyStart")
            }

            override fun responseBodyEnd(call: Call, byteCount: Long) {
                Log.d("OkHttp", "responseBodyEnd")
            }
        }
    }
}

Вышеуказанный слушатель событий регистрируется клиентом. Кэш отключен, чтобы исключить возможность попадания в кэш при последующих запросах.

private val client = OkHttpClient.Builder()
    .cache(null)
    .eventListenerFactory(OkHttpEventListenerFactory())
    .build()

Этот клиент используется для того, чтобы обратиться к конечной точке несколько раз.

    val request = Request.Builder()
        .url("https://www.google.com")
        .build()

    client.newCall(request)

Первое обращение занимает 676 миллисекунд и включает установку нового TCP-соединения, включая разрешение DNS, SSL Handshake и HTTP-запрос и ответ. Теперь это новое соединение добавляется в пул соединений, и, следовательно, connectionReleased не вызывается.

19:57:03.488  D  CallStart
19:57:03.524  D  dnsStart
19:57:03.554  D  dnsEnd
19:57:03.555  D  connectStart
19:57:03.581  D  secureConnectStart
19:57:03.640  D  secureConnectEnd
19:57:03.646  D  connectEnd
19:57:03.646  D  connectionAcquired
19:57:03.646  D  requestHeadersStart
19:57:03.648  D  requestHeadersEnd
19:57:04.114  D  responseHeadersStart
19:57:04.114  D  responseHeadersEnd
19:57:04.115  D  responseBodyStart
19:57:04.115  D  responseBodyEnd
19:57:04.116  D  requestHeadersStart
19:57:04.116  D  requestHeadersEnd
19:57:04.164  D  responseHeadersStart
19:57:04.164  D  responseHeadersEnd

Однако последующее обращение обрабатывается всего за 506 миллисекунд (минуя поиск DNS) за счет использования существующего TCP-соединения из пула соединений.

19:58:20.041  D  CallStart
19:58:20.043  D  connectionAcquired
19:58:20.043  D  requestHeadersStart
19:58:20.044  D  requestHeadersEnd
19:58:20.502  D  responseHeadersStart
19:58:20.502  D  responseHeadersEnd
19:58:20.503  D  responseBodyStart
19:58:20.503  D  responseBodyEnd
19:58:20.503  D  requestHeadersStart
19:58:20.504  D  requestHeadersEnd
19:58:20.547  D  responseHeadersStart
19:58:20.547  D  responseHeadersEnd

Заключение

Хотя стандартный пул подключений достаточно хорош для большинства приложений, приложениям, использующим один и тот же OkHttpClient для нескольких конечных точек (через Retrofit), стоит подумать либо об увеличении пула подключений, либо об использовании специфических OkHttpClient для конечных точек (Service).

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Advertisement

Наши партнеры:

LEGALBET

Мобильные приложения для ставок на спорт
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: