You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Apache Ignite客户端获取服务端的原始异常?

Ignite客户端区分服务端抛出的不同异常类型方案

问题场景

服务定义与实现

服务接口:

interface ExceptionService : Service {
    fun doSmth(flag: Boolean)
}

服务实现类:

class ExceptionServiceImpl : ExceptionService {
    override fun doSmth(flag: Boolean) {
        if (flag) {
            throw IOException("my IO exception")
        } else {
            throw IllegalArgumentException("my IllegalArgument exception")
        }
    }
}

集群部署代码

Ignition.start(
    IgniteConfiguration()
        .setServiceConfiguration(
            ServiceConfiguration()
                .setService(ExceptionServiceImpl())
                .setName("service")
        )
)

客户端调用代码

val client = Ignition.startClient(ClientConfiguration().setAddresses("127.0.0.1"))
val service = client.services().serviceProxy("service", ExceptionService::class.java)
try {
    service.doSmth(true)
} catch (e: Exception) {
    println(e.message)
}
try {
    service.doSmth(false)
} catch (e: Exception) {
    println(e.message)
}

现象与问题

控制台输出:

Ignite failed to process request [1] my IO exception (server status code [1])
Ignite failed to process request [2] my IllegalArgument exception (server status code [1])

客户端捕获的异常始终是org.apache.ignite.client.ClientException,服务端抛出的原始异常类型丢失,仅保留包装后的消息,根源为org.apache.ignite.internal.client.thin.ClientServerError。需要在客户端区分处理服务抛出的不同类型异常。


可行解决方案

方法1:自定义可序列化异常+服务端异常映射配置

  1. 定义实现Serializable的自定义业务异常(JDK自带异常如IOException本身已实现序列化,也可直接使用,但自定义异常需满足序列化要求):
class BusinessIOException(message: String) : IOException(message), Serializable
class BusinessIllegalArgException(message: String) : IllegalArgumentException(message), Serializable
  1. 修改服务实现类,抛出自定义异常:
class ExceptionServiceImpl : ExceptionService {
    override fun doSmth(flag: Boolean) {
        if (flag) {
            throw BusinessIOException("my IO exception")
        } else {
            throw BusinessIllegalArgException("my IllegalArgument exception")
        }
    }
}
  1. 服务端配置自定义ClientExceptionMapper,避免原始异常被包装:
Ignition.start(
    IgniteConfiguration()
        .setServiceConfiguration(
            ServiceConfiguration()
                .setService(ExceptionServiceImpl())
                .setName("service")
        )
        .setClientExceptionMapper { ex ->
            // 直接返回原始异常,让客户端能解析到真实类型
            ex
        }
)

注:默认映射器会将所有服务端异常包装为ClientException,自定义映射器可保留原始异常类型,前提是异常类可序列化且客户端类路径存在该类。

方法2:解析ClientException的根源信息(临时方案)

若无法修改服务端异常,可通过解析ClientException的根源异常ClientServerError提取原始异常类名:

try {
    service.doSmth(true)
} catch (e: ClientException) {
    val rootError = e.cause as? ClientServerError
    rootError?.let {
        when (it.className) {
            "java.io.IOException" -> println("处理IO异常:${e.message}")
            "java.lang.IllegalArgumentException" -> println("处理参数异常:${e.message}")
        }
    }
}

注:该方式依赖字符串匹配类名,异常类名变更会导致逻辑失效,仅适合临时场景。

方法3:开启异常栈信息传递

服务端配置ClientConnector开启异常栈传递,客户端可从ClientException的栈信息中解析原始异常类型:

// 服务端配置
Ignition.start(
    IgniteConfiguration()
        .setServiceConfiguration(...)
        .setClientConnectorConfiguration(
            ClientConnectorConfiguration()
                .setSendExceptionStackTrace(true)
        )
)

开启后,客户端可通过ClientException的栈轨迹,匹配原始异常的类名进行区分处理。


关键注意事项

  • 只有可序列化的异常才能在客户端还原为原始类型,需保证服务端与客户端的异常类完全一致(包名、类名、序列化ID)。
  • 若使用JDK自带异常,需确保客户端类路径中存在对应类,否则仍会被包装为ClientException。

内容的提问来源于stack exchange,提问作者Viktor L

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:42:24