如何在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:自定义可序列化异常+服务端异常映射配置
- 定义实现
Serializable的自定义业务异常(JDK自带异常如IOException本身已实现序列化,也可直接使用,但自定义异常需满足序列化要求):
class BusinessIOException(message: String) : IOException(message), Serializable class BusinessIllegalArgException(message: String) : IllegalArgumentException(message), Serializable
- 修改服务实现类,抛出自定义异常:
class ExceptionServiceImpl : ExceptionService { override fun doSmth(flag: Boolean) { if (flag) { throw BusinessIOException("my IO exception") } else { throw BusinessIllegalArgException("my IllegalArgument exception") } } }
- 服务端配置自定义
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
相关产品推荐
相关产品推荐

