OkHttpClient连接池达150+时触发DataFormatException问题求助
我在服务中集成了OkHttpClient,当连接池中的连接数同时达到150+时,抛出了如下DataFormatException异常。请问是否有人了解该异常?如何修复此问题?是否与GZIP压缩有关?
错误信息
ResponseCode: 500 INTERNAL_SERVER_ERROR, ErrorMessage:ca.uhn.fhir.parser.DataFormatException: Failed to parse JSON encoded API content: Content does not appear to be API JSON, first non-whitespace character was: '<' (must be '{')
代码片段
private fun okhttpioConnection(): String { log.info("Initializing OKHttp-ioConnection") client = OkHttpClient() client.setConnectTimeout(600000, TimeUnit.MILLISECONDS) client.setReadTimeout(600000, TimeUnit.MILLISECONDS) client.connectionPool = ConnectionPool(300, 20000) client.retryOnConnectionFailure = true var request = createRequest(restParams) // GET or POST var response = client.newCall(request).execute() var responseCodeString = response.code().toString() return response.body()!!.string() } fun createRequest(restParams: RestParams): Request { if (restParams.method == "POST") { return Request.Builder() .post(RequestBody.create( MediaType.parse("application/json; charset=utf-8"), restParams.body.toString())) .header("Authorization", "xxxxxx") .addHeader("Content-Length",restParams.body.length.toString()) .addHeader("Accept", "application/json, text/json") .url(restParams.url) .build() } else { return Request.Builder() .header("Authorization", "xxxxxxx") .addHeader("Accept", "application/json, text/json") .url(restParams.url).get() .build() } }
异常原因
从错误信息判断,服务端在连接数过高时返回了500内部错误,同时返回的内容是HTML格式(以<开头,大概率是服务端的错误页面),但你的代码默认将所有响应体当作JSON解析,因此触发了DataFormatException。本质是高负载下服务端无法正常处理请求,返回了非预期格式的错误响应。
是否与GZIP压缩有关
目前来看和GZIP压缩无关。如果是GZIP解压问题,通常会出现乱码或解压失败的异常,而非解析JSON时遇到<字符。除非服务端错误返回了GZIP压缩的HTML,但这种情况概率极低,暂不考虑。
修复方案
先校验响应状态再解析
在获取响应体之前,先判断响应是否成功(200-299状态码),如果是错误状态,直接捕获处理,避免解析错误格式的内容:var response = client.newCall(request).execute() if (!response.isSuccessful) { val errorBody = response.body()?.string() ?: "Empty error body" log.error("Request failed, code: ${response.code()}, error content: $errorBody") // 抛出业务异常或返回错误标识,不要继续解析JSON throw RuntimeException("Server returned error: ${response.code()}") } return response.body()!!.string()优化连接池配置
当前设置的300个连接可能超出了服务端的承载上限,导致服务端过载。建议降低最大连接数(比如调整到100以内),同时缩短连接空闲超时时间,避免无效连接占用资源:client.connectionPool = ConnectionPool(100, 10000) // 示例配置,根据实际业务调整复用OkHttpClient实例
你的代码每次调用okhttpioConnection()都会创建新的OkHttpClient实例,导致多个连接池并存,无法有效复用连接。应该将OkHttpClient做成全局单例:// 全局单例初始化 private val client by lazy { OkHttpClient.Builder() .connectTimeout(600000, TimeUnit.MILLISECONDS) .readTimeout(600000, TimeUnit.MILLISECONDS) .connectionPool(ConnectionPool(100, 10000)) .retryOnConnectionFailure(true) .build() } private fun okhttpioConnection(): String { log.info("Initializing OKHttp-ioConnection") val request = createRequest(restParams) val response = client.newCall(request).execute() // 后续响应处理逻辑 }排查服务端负载瓶颈
连接数过高时服务端返回500,说明服务端在高负载下稳定性不足。需要配合服务端团队排查:是否存在接口性能瓶颈、数据库连接池不足、线程资源耗尽等问题,从根源解决高负载下的服务异常。
内容的提问来源于stack exchange,提问作者Kaushik KR

