使用 @restcontrolleradvice 配合 @exceptionhandler 可以统一捕获 elasticsearch 查询异常。核心做法是拦截 elasticsearchexception 或其子类(如 responseexception),在处理器中解析错误信息并返回标准化的 json 响应,从而避免将原始堆栈直接暴露给前端。

RestHighLevelClient 抛出 ResponseException 时如何区分 HTTP 状态码?
当底层 HTTP 请求失败时,客户端会抛出 ResponseException。通过调用 getResponse().getStatusLine().getStatusCode() 可以获取具体的 HTTP 状态码(如 404 或 500),进而决定返回业务错误还是系统错误。

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalElasticsearchExceptionHandler {
@ExceptionHandler(ElasticsearchException.class)
public ResponseEntity<string> handleEsException(ElasticsearchException ex) {
if (ex instanceof ResponseException) {
int statusCode = ((ResponseException) ex).getResponse()
.getStatusLine().getStatusCode();
if (statusCode == 404) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("索引或文档未找到");
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("ES 服务内部错误: " + ex.getMessage());
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("查询参数错误: " + ex.getMessage());
}
}</string>
上述代码针对 ResponseException 进行了特化处理。如果输入导致 404 错误,返回 404 状态码;如果是其他 ES 异常,则根据具体类型返回相应提示。这种处理方式确保了 API 响应的规范性。
如何处理 SearchPhaseExecutionException 导致的分片查询失败?
当部分分片查询失败但整体搜索仍可继续时,ES 可能会抛出 SearchPhaseExecutionException。此时需要检查异常链中的 causedBy 字段,判断是数据不一致还是网络超时,通常建议记录日志后返回部分成功结果或降级处理。

import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.ElasticsearchException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalElasticsearchExceptionHandler {
@ExceptionHandler(SearchPhaseExecutionException.class)
public ResponseEntity<string> handlePhaseException(SearchPhaseExecutionException ex) {
// 检查是否有更具体的底层原因
Throwable cause = ex.getCause();
if (cause != null) {
return ResponseEntity.status(503)
.body("部分分片查询失败,请稍后重试: " + cause.getMessage());
}
return ResponseEntity.status(500)
.body("搜索执行阶段发生未知错误");
}
}</string>
该段代码专门处理分片级别的执行异常。通过提取 getCause(),开发者可以获取更详细的失败原因。在实际生产中,这类异常往往伴随着部分数据的缺失,因此返回 503 状态码比 500 更符合语义,提示客户端进行重试或降级。
全局异常处理器中如何确保 ThreadContext 不被污染?
在 Spring Boot 的全局异常处理中,如果直接使用 RestHighLevelClient,必须注意 ThreadContext 的上下文隔离。每次请求结束后,应确保清理或重置 ThreadContext,防止上一个请求的 Header 或元数据影响到下一个请求,尤其是在异步或非阻塞场景下。
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseListener;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class EsClientWrapper {
@Autowired
private RestHighLevelClient client;
public void safeQuery() throws Exception {
ThreadContext context = client.getLowLevelClient().getThreadContext();
try {
// 模拟保存上下文
context.putHeader("request_id", "unique-id-123");
// 执行查询逻辑...
} finally {
// 关键:恢复上下文,防止污染后续请求
context.restore();
}
}
}
这段代码展示了如何在操作 ES 客户端时保护 ThreadContext。通过在 finally 块中调用 restore(),可以确保当前请求的自定义 Header 不会泄露到线程池中的其他任务中。这是构建高可用 ES 集成层的关键细节,避免了因上下文残留导致的认证失败或路由错误。











