Kotlin HTTP客户端:正确发起请求——高级篇
在开发现代Kotlin应用时,HTTP请求的处理远不止简单调用URL.readText()。在高级层面,需要考虑连接管理、超时、重试、日志记录、异步操作以及正确的错误处理。本文将讲解如何为生产环境配置Kotlin HTTP客户端,使用两个流行的库:OkHttp和Ktor Client。本文面向已熟悉Kotlin和HTTP基础的开发者。
1. 库的选择:OkHttp vs Ktor Client
在编写代码之前,理解两种方法的区别很重要。OkHttp是Square公司开发的成熟、久经考验的库,与Retrofit配合使用效果极佳。Ktor Client是JetBrains开发的本地跨平台库,与Kotlin协程完全集成。对于基于Spring Boot的服务端项目,通常选择OkHttp;而对于Ktor应用或跨平台项目,则选择Ktor Client。
我们将讨论这两种方案,以便您能为自己的技术栈选择合适的一个。
2. OkHttp的高级配置
OkHttp的基本配置很简单,但生产环境需要精细调优。下面介绍如何创建带有自定义参数的Singleton客户端。
import okhttp3.*import java.util.concurrent.TimeUnit
object HttpClientProvider {
private val client: OkHttpClient by lazy { OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .addInterceptor(LoggingInterceptor()) .addInterceptor(RetryInterceptor(maxRetries = 3)) .connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES)) .build() }
fun getClient(): OkHttpClient = client}2.1 自定义拦截器(Interceptors)
拦截器是修改请求和响应的强大机制。下面是一个日志记录拦截器和重试拦截器的示例。
class LoggingInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() println("--> ${request.method} ${request.url}") val startTime = System.nanoTime() val response = chain.proceed(request) val duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) println("<-- ${response.code} ${response.message} (${duration}ms)") return response }}
class RetryInterceptor(private val maxRetries: Int) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { var retryCount = 0 var response: Response? = null while (retryCount < maxRetries) { try { response = chain.proceed(chain.request()) if (response.isSuccessful) return response } catch (e: Exception) { println("重试 #${retryCount + 1} 失败: ${e.message}") } retryCount++ if (retryCount < maxRetries) Thread.sleep(1000L * retryCount) } return response ?: throw IOException("超出最大重试次数") }}2.2 使用协程的异步请求
OkHttp支持同步和异步(基于回调)请求。为了与Kotlin协程集成,我们使用suspendCancellableCoroutine。
suspend fun makeRequest(url: String): String { return suspendCancellableCoroutine { continuation -> val request = Request.Builder().url(url).build() val call = HttpClientProvider.getClient().newC