Lychee-Rerank与Java微服务集成指南SpringBoot实战案例如果你正在用SpringBoot做Java后端开发最近又对RAG检索增强生成里的重排序技术感兴趣那今天这篇文章就是为你准备的。咱们不聊那些复杂的算法原理直接上手看看怎么把一个叫Lychee-Rerank的服务干净利落地集成到你的SpringBoot微服务里。你可能听说过在RAG系统里光靠向量检索把相关文档找出来还不够返回的结果顺序可能不是最优的。这时候就需要一个“裁判”来重新给这些结果打分、排序让最相关的信息排到最前面。Lychee-Rerank就是干这个的。今天我就带你从零开始在SpringBoot项目里把它用起来包括怎么设计API、怎么优雅地调用服务、怎么处理各种异常情况最后还会聊聊性能上能怎么优化。代码都是可以直接复制粘贴运行的咱们边看边做。1. 项目初始化与环境准备在开始写代码之前得先把“舞台”搭好。这里假设你已经有一个正在开发的SpringBoot项目了版本2.7.x或3.x都行。如果没有用Spring Initializr快速生成一个也很方便。1.1 添加必要的Maven依赖打开你的pom.xml文件在dependencies部分加入下面这些依赖。这些都是我们后续开发会用到的“工具”。dependencies !-- Spring Boot Web Starter (提供RESTful API能力) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring Boot Validation (用于API参数校验) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency !-- Lombok (减少Getter/Setter等样板代码) -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- Jackson (JSON序列化/反序列化) -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId /dependency !-- OkHttp (一个轻量级的HTTP客户端用于调用Lychee-Rerank服务) -- dependency groupIdcom.squareup.okhttp3/groupId artifactIdokhttp/artifactId version4.12.0/version !-- 请使用最新稳定版 -- /dependency !-- Spring Boot Configuration Processor (让自定义配置有提示) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-configuration-processor/artifactId optionaltrue/optional /dependency /dependencies加完依赖记得刷新一下你的Maven项目让这些“工具包”下载到本地。1.2 配置Lychee-Rerank服务连接信息我们肯定不希望把服务地址、API密钥这些敏感信息硬编码在代码里。SpringBoot的application.yml或application.properties文件就是用来管理这些配置的。这里我用YAML格式来写看起来更清晰。在你的src/main/resources/application.yml文件中添加下面这段配置# Lychee-Rerank 服务配置 lychee: rerank: # Lychee-Rerank服务的API基础地址根据你的实际部署情况修改 base-url: http://localhost:8000 # 调用重排序接口的路径 rerank-path: /rerank # 你的API密钥如果服务需要认证的话 api-key: your_lychee_api_key_here # 连接超时时间毫秒 connect-timeout: 5000 # 读取超时时间毫秒 read-timeout: 30000 # 是否启用该功能 enabled: true这里我定义了一个前缀为lychee.rerank的配置组。base-url是关键它指向你部署的Lychee-Rerank服务。如果你用的是别人提供的云端服务就把这里的地址换成对应的公网地址。api-key也记得换成你自己的。2. 核心模型与配置类封装配置写好了接下来我们得在Java代码里能读到它们并且定义好我们和Lychee-Rerank服务“对话”时用的“语言”也就是数据格式。2.1 定义配置属性类我们创建一个类专门用来绑定配置文件里的那些属性。这样在代码里用起来就非常方便了。package com.yourcompany.demo.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import javax.validation.constraints.NotBlank; /** * Lychee-Rerank服务的配置属性 * 会自动从application.yml中读取lychee.rerank下的配置 */ Data Component ConfigurationProperties(prefix lychee.rerank) public class LycheeRerankProperties { /** * 服务基础地址 (例如: http://localhost:8000) */ NotBlank(message Lychee-Rerank服务地址不能为空) private String baseUrl; /** * 重排序接口路径 */ private String rerankPath /rerank; /** * API密钥 */ private String apiKey; /** * 连接超时时间(毫秒) */ private int connectTimeout 5000; /** * 读取超时时间(毫秒) */ private int readTimeout 30000; /** * 是否启用该功能 */ private boolean enabled true; /** * 获取完整的重排序接口URL */ public String getRerankUrl() { return baseUrl rerankPath; } }这个类用了ConfigurationProperties注解SpringBoot启动时会自动把application.yml里lychee.rerank下面的值填充到这个类的对应字段里。Data是Lombok的注解帮我们自动生成了getter、setter等方法。2.2 定义请求与响应模型调用API无非就是发送一个结构化的请求然后接收一个结构化的响应。我们来定义这两个“数据结构”。请求模型 (LycheeRerankRequest.java):这个对象代表我们要发给Lychee-Rerank服务的内容一个问题query和一堆待排序的文档documents。package com.yourcompany.demo.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import java.util.List; /** * 调用Lychee-Rerank服务的请求体 */ Data NoArgsConstructor AllArgsConstructor public class LycheeRerankRequest { /** * 用户查询的问题 */ NotBlank(message 查询问题不能为空) private String query; /** * 待排序的文档列表 */ NotEmpty(message 待排序文档列表不能为空) private ListString documents; /** * 返回的Top K个结果数量 (可选参数根据服务API决定) */ private Integer topK; // 这里还可以根据Lychee-Rerank服务的实际API添加其他可选参数比如模型名称、返回格式等 // private String model; // private Boolean returnDocuments; }响应模型 (LycheeRerankResponse.java):这个对象代表服务返回给我们的结果。通常重排序服务会返回每个文档的新分数和排名。package com.yourcompany.demo.model; import lombok.Data; import java.util.List; /** * Lychee-Rerank服务的标准响应体 */ Data public class LycheeRerankResponse { /** * 请求是否成功 */ private Boolean success; /** * 错误信息如果success为false */ private String error; /** * 重排序后的结果列表 */ private ListRerankResult results; /** * 本次请求消耗的token数如果服务提供 */ private Integer usage; /** * 单个重排序结果 */ Data public static class RerankResult { /** * 文档在原始列表中的索引 */ private Integer index; /** * 文档内容 */ private String document; /** * 重排序后的相关性分数 */ private Double score; /** * 重排序后的排名从1开始 */ private Integer rank; } }模型定义好了相当于我们和Lychee-Rerank服务之间的“通信协议”就确定了。3. 服务层封装优雅地调用API现在进入核心部分写一个服务类去真正地调用Lychee-Rerank的HTTP接口。好的封装应该让调用方感觉不到网络请求的细节就像调用本地方法一样简单。3.1 创建HTTP客户端首先我们利用前面配置里的超时时间创建一个OkHttpClient实例。Spring的Configuration注解让它成为一个被管理的Bean。package com.yourcompany.demo.config; import okhttp3.OkHttpClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; Configuration public class HttpClientConfig { Bean public OkHttpClient okHttpClient(LycheeRerankProperties properties) { return new OkHttpClient.Builder() .connectTimeout(properties.getConnectTimeout(), TimeUnit.MILLISECONDS) .readTimeout(properties.getReadTimeout(), TimeUnit.MILLISECONDS) .build(); } }3.2 实现核心服务类接下来是重头戏实现LycheeRerankService。这里我会把关键步骤都写上注释你可以清晰地看到一次完整的HTTP调用是如何组织的。package com.yourcompany.demo.service; import com.fasterxml.jackson.databind.ObjectMapper; import com.yourcompany.demo.config.LycheeRerankProperties; import com.yourcompany.demo.model.LycheeRerankRequest; import com.yourcompany.demo.model.LycheeRerankResponse; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.io.IOException; import java.util.Collections; /** * Lychee-Rerank服务封装 */ Slf4j Service public class LycheeRerankService { private final OkHttpClient httpClient; private final LycheeRerankProperties properties; private final ObjectMapper objectMapper; private final MediaType JSON MediaType.get(application/json; charsetutf-8); // 通过构造器注入依赖 public LycheeRerankService(OkHttpClient httpClient, LycheeRerankProperties properties, ObjectMapper objectMapper) { this.httpClient httpClient; this.properties properties; this.objectMapper objectMapper; } PostConstruct public void init() { if (!properties.isEnabled()) { log.warn(Lychee-Rerank服务未启用相关功能将不可用。); } else { log.info(Lychee-Rerank服务已初始化基础URL: {}, properties.getBaseUrl()); } } /** * 同步调用重排序服务 * * param request 重排序请求 * return 重排序响应 * throws IOException 当网络或服务异常时抛出 */ public LycheeRerankResponse rerankSync(LycheeRerankRequest request) throws IOException { if (!properties.isEnabled()) { log.warn(尝试调用未启用的Lychee-Rerank服务。); return createDisabledResponse(); } // 1. 将请求对象序列化为JSON字符串 String requestBodyJson objectMapper.writeValueAsString(request); RequestBody body RequestBody.create(requestBodyJson, JSON); // 2. 构建HTTP请求 Request httpRequest new Request.Builder() .url(properties.getRerankUrl()) .post(body) .addHeader(Content-Type, application/json) .addHeader(Accept, application/json) .build(); // 3. 添加API Key认证头如果配置了的话 if (properties.getApiKey() ! null !properties.getApiKey().trim().isEmpty()) { httpRequest httpRequest.newBuilder() .addHeader(Authorization, Bearer properties.getApiKey()) .build(); } log.debug(调用Lychee-Rerank服务URL: {}, 请求体: {}, properties.getRerankUrl(), requestBodyJson); // 4. 执行同步调用并获取响应 try (Response response httpClient.newCall(httpRequest).execute()) { if (!response.isSuccessful()) { String errorBody response.body() ! null ? response.body().string() : null; log.error(Lychee-Rerank服务调用失败状态码: {}, 响应体: {}, response.code(), errorBody); throw new IOException(HTTP请求失败状态码: response.code() , 详情: errorBody); } // 5. 解析响应体 if (response.body() ! null) { String responseBody response.body().string(); log.debug(Lychee-Rerank服务响应: {}, responseBody); return objectMapper.readValue(responseBody, LycheeRerankResponse.class); } else { throw new IOException(响应体为空); } } } /** * 创建一个服务未启用时的默认响应 */ private LycheeRerankResponse createDisabledResponse() { LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(Lychee-Rerank服务未启用); response.setResults(Collections.emptyList()); return response; } }这个服务类做了几件重要的事参数校验与状态检查首先检查服务是否启用。请求构建把Java对象转成JSON设置正确的HTTP头和认证信息。同步调用使用OkHttpClient发送请求。响应处理检查HTTP状态码成功则解析JSON返回Java对象失败则抛出异常并记录日志。日志记录关键步骤都打了日志方便出问题时排查。这样业务代码里只需要注入这个LycheeRerankService然后调用rerankSync方法就行了是不是清爽多了4. 控制器层提供RESTful API服务层封装好了我们还需要一个“窗口”对外提供服务。在SpringBoot里这个“窗口”就是Controller。4.1 设计API接口我们来设计一个简单清晰的REST API接收前端或其他服务传来的查询和文档列表返回重排序后的结果。package com.yourcompany.demo.controller; import com.yourcompany.demo.model.LycheeRerankRequest; import com.yourcompany.demo.model.LycheeRerankResponse; import com.yourcompany.demo.service.LycheeRerankService; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.io.IOException; /** * 对外提供重排序功能的REST API控制器 */ Slf4j Validated RestController RequestMapping(/api/v1/rerank) public class RerankController { private final LycheeRerankService rerankService; public RerankController(LycheeRerankService rerankService) { this.rerankService rerankService; } /** * 对文档列表进行重排序 * POST /api/v1/rerank * * param request 包含查询语句和文档列表的请求体 * return 重排序后的结果 */ PostMapping public ResponseEntityLycheeRerankResponse rerankDocuments(Valid RequestBody LycheeRerankRequest request) { log.info(收到重排序请求查询: [{}], 文档数量: {}, request.getQuery(), request.getDocuments().size()); try { LycheeRerankResponse response rerankService.rerankSync(request); return ResponseEntity.ok(response); } catch (IOException e) { log.error(调用重排序服务时发生IO异常, e); LycheeRerankResponse errorResponse new LycheeRerankResponse(); errorResponse.setSuccess(false); errorResponse.setError(服务调用失败: e.getMessage()); // 根据实际情况这里可以返回500或者定义更具体的业务异常状态码 return ResponseEntity.internalServerError().body(errorResponse); } catch (Exception e) { log.error(处理重排序请求时发生未知异常, e); LycheeRerankResponse errorResponse new LycheeRerankResponse(); errorResponse.setSuccess(false); errorResponse.setError(内部服务错误: e.getMessage()); return ResponseEntity.internalServerError().body(errorResponse); } } /** * 健康检查端点 * GET /api/v1/rerank/health * * return 服务健康状态 */ GetMapping(/health) public ResponseEntityString healthCheck() { // 这里可以添加更复杂的健康检查逻辑比如测试一下到Lychee-Rerank服务的连通性 return ResponseEntity.ok(Rerank Service is UP); } }这个控制器提供了两个接口POST /api/v1/rerank: 核心的重排序接口。它接收JSON格式的请求体交给服务层处理并统一处理异常返回结构化的响应。GET /api/v1/rerank/health: 一个简单的健康检查接口用于监控服务状态。注意Valid注解它会自动校验LycheeRerankRequest对象里我们之前用NotBlank、NotEmpty定义的规则如果校验失败Spring会直接返回400错误不用我们在代码里手动判断。5. 进阶优化异步处理与全局异常基本的跑通之后我们来看看怎么让它变得更健壮、性能更好。这对于生产环境至关重要。5.1 实现异步非阻塞调用同步调用会阻塞当前线程直到收到响应。如果Lychee-Rerank服务响应慢或者并发请求多我们的服务线程很快就会被耗光。改成异步调用可以极大提升吞吐量。我们先改造服务类增加一个异步方法// 在 LycheeRerankService 类中添加以下方法 /** * 异步调用重排序服务 * * param request 重排序请求 * return 包含响应结果的CompletableFuture */ public CompletableFutureLycheeRerankResponse rerankAsync(LycheeRerankRequest request) { if (!properties.isEnabled()) { log.warn(尝试调用未启用的Lychee-Rerank服务。); return CompletableFuture.completedFuture(createDisabledResponse()); } CompletableFutureLycheeRerankResponse future new CompletableFuture(); try { String requestBodyJson objectMapper.writeValueAsString(request); RequestBody body RequestBody.create(requestBodyJson, JSON); Request.Builder requestBuilder new Request.Builder() .url(properties.getRerankUrl()) .post(body) .addHeader(Content-Type, application/json); if (properties.getApiKey() ! null !properties.getApiKey().trim().isEmpty()) { requestBuilder.addHeader(Authorization, Bearer properties.getApiKey()); } Request httpRequest requestBuilder.build(); log.debug(异步调用Lychee-Rerank服务URL: {}, properties.getRerankUrl()); // 使用OkHttp的异步调用 httpClient.newCall(httpRequest).enqueue(new Callback() { Override public void onFailure(Call call, IOException e) { log.error(异步调用Lychee-Rerank服务失败, e); future.completeExceptionally(e); } Override public void onResponse(Call call, Response response) throws IOException { try { if (!response.isSuccessful()) { String errorBody response.body() ! null ? response.body().string() : null; log.error(Lychee-Rerank服务异步调用失败状态码: {}, response.code()); future.completeExceptionally(new IOException(HTTP response.code() : errorBody)); return; } if (response.body() ! null) { String responseBody response.body().string(); LycheeRerankResponse rerankResponse objectMapper.readValue(responseBody, LycheeRerankResponse.class); future.complete(rerankResponse); } else { future.completeExceptionally(new IOException(响应体为空)); } } catch (Exception e) { future.completeExceptionally(e); } } }); } catch (Exception e) { future.completeExceptionally(e); } return future; }然后在控制器里我们可以提供一个异步的API端点。SpringBoot对CompletableFuture的返回类型有很好的支持。// 在 RerankController 类中添加以下方法 /** * 异步对文档列表进行重排序 * POST /api/v1/rerank/async * * param request 包含查询语句和文档列表的请求体 * return 一个Future最终包含重排序后的结果 */ PostMapping(/async) public CompletableFutureResponseEntityLycheeRerankResponse rerankDocumentsAsync( Valid RequestBody LycheeRerankRequest request) { log.info(收到异步重排序请求查询: [{}], request.getQuery()); return rerankService.rerankAsync(request) .thenApply(ResponseEntity::ok) // 成功时包装成ResponseEntity .exceptionally(e - { // 异常处理 log.error(异步重排序处理失败, e); LycheeRerankResponse errorResponse new LycheeRerankResponse(); errorResponse.setSuccess(false); errorResponse.setError(异步处理失败: e.getMessage()); return ResponseEntity.internalServerError().body(errorResponse); }); }这样调用/api/v1/rerank/async接口的线程会立即返回不会阻塞。当Lychee-Rerank服务返回结果后Spring会处理这个CompletableFuture并最终将响应返回给客户端。5.2 添加全局异常处理我们之前在控制器里用了try-catch来捕获异常。但如果有多个控制器方法每个都写一遍会很麻烦。Spring提供了ControllerAdvice来集中处理异常。package com.yourcompany.demo.handler; import com.yourcompany.demo.model.LycheeRerankResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; import java.io.IOException; import java.util.stream.Collectors; /** * 全局异常处理器 */ Slf4j RestControllerAdvice public class GlobalExceptionHandler { /** * 处理参数校验失败异常Valid触发的 */ ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntityLycheeRerankResponse handleValidationException(MethodArgumentNotValidException ex) { String errorMessage ex.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(; )); log.warn(参数校验失败: {}, errorMessage); LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(请求参数无效: errorMessage); return ResponseEntity.badRequest().body(response); } /** * 处理服务调用IO异常如网络超时、连接失败 */ ExceptionHandler(IOException.class) public ResponseEntityLycheeRerankResponse handleIOException(IOException ex) { log.error(服务调用发生IO异常, ex); LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(外部服务通信失败: ex.getMessage()); return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response); } /** * 处理其他所有未捕获的异常 */ ExceptionHandler(Exception.class) public ResponseEntityLycheeRerankResponse handleGenericException(Exception ex) { log.error(系统内部发生未预期异常, ex); LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(系统内部错误请稍后重试); // 生产环境中这里不应该把详细的异常信息返回给客户端 return ResponseEntity.internalServerError().body(response); } }有了这个全局处理器控制器里的代码就可以更干净只关注业务逻辑异常处理交给它。而且它能保证所有接口返回的异常格式都是一致的。6. 总结与最佳实践建议走完上面这些步骤一个具备基本生产可用性的Lychee-Rerank集成服务就搭建起来了。我们来回顾一下关键点并聊聊在实际项目中还能做哪些优化。整个集成的核心思路很清晰配置化、模型化、服务化、API化。通过配置文件管理可变参数通过Java对象定义数据契约通过Service类封装外部调用细节最后通过REST Controller对外提供标准接口。这种分层结构让代码职责清晰也易于测试和维护。异步改造是提升性能的关键一步特别是对于这种需要调用外部、可能比较耗时的服务。使用CompletableFuture可以让我们的主线程快速释放去处理更多请求。全局异常处理则保证了服务的健壮性给客户端始终返回友好、统一的错误格式。在实际项目中你还可以考虑下面这些点连接池与超时优化OkHttpClient默认就有连接池但你可以根据压测结果调整maxIdleConnections和keepAliveDuration。超时时间connectTimeout,readTimeout,writeTimeout也要根据网络环境和下游服务的SLA来仔细设定。重试机制网络请求难免失败。可以考虑加入带退避策略的重试机制比如指数退避对于瞬时的网络抖动或下游服务短暂不可用的情况很有帮助。可以用Spring Retry库或者Resilience4j来实现。熔断与降级如果Lychee-Rerank服务长时间不可用持续的失败调用会消耗资源。引入熔断器如Resilience4j或Sentinel在失败率达到阈值时快速失败并提供一个降级策略比如直接返回原始排序结果或返回一个缓存的结果。监控与指标使用Micrometer等工具暴露关键指标比如调用次数、成功/失败率、响应时间分布P50, P95, P99。这对了解服务健康状况和性能瓶颈至关重要。缓存如果某些查询和文档组合被频繁请求可以考虑在调用Lychee-Rerank之前加一层缓存比如用Caffeine或Redis直接返回之前的排序结果能显著降低延迟和外部服务负载。最后记得为你的LycheeRerankService和RerankController编写单元测试和集成测试确保代码的稳定性和正确性。集成测试可以启动一个测试用的Lychee-Rerank服务或者用Mock Server来验证整个调用链路。希望这个实战指南能帮你顺利地把智能重排序能力集成到你的SpringBoot应用中。从简单的同步调用开始逐步引入异步、异常处理、熔断等机制你的服务会变得越来越可靠和高效。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。
Lychee-Rerank与Java微服务集成指南:SpringBoot实战案例
Lychee-Rerank与Java微服务集成指南SpringBoot实战案例如果你正在用SpringBoot做Java后端开发最近又对RAG检索增强生成里的重排序技术感兴趣那今天这篇文章就是为你准备的。咱们不聊那些复杂的算法原理直接上手看看怎么把一个叫Lychee-Rerank的服务干净利落地集成到你的SpringBoot微服务里。你可能听说过在RAG系统里光靠向量检索把相关文档找出来还不够返回的结果顺序可能不是最优的。这时候就需要一个“裁判”来重新给这些结果打分、排序让最相关的信息排到最前面。Lychee-Rerank就是干这个的。今天我就带你从零开始在SpringBoot项目里把它用起来包括怎么设计API、怎么优雅地调用服务、怎么处理各种异常情况最后还会聊聊性能上能怎么优化。代码都是可以直接复制粘贴运行的咱们边看边做。1. 项目初始化与环境准备在开始写代码之前得先把“舞台”搭好。这里假设你已经有一个正在开发的SpringBoot项目了版本2.7.x或3.x都行。如果没有用Spring Initializr快速生成一个也很方便。1.1 添加必要的Maven依赖打开你的pom.xml文件在dependencies部分加入下面这些依赖。这些都是我们后续开发会用到的“工具”。dependencies !-- Spring Boot Web Starter (提供RESTful API能力) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring Boot Validation (用于API参数校验) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency !-- Lombok (减少Getter/Setter等样板代码) -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- Jackson (JSON序列化/反序列化) -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId /dependency !-- OkHttp (一个轻量级的HTTP客户端用于调用Lychee-Rerank服务) -- dependency groupIdcom.squareup.okhttp3/groupId artifactIdokhttp/artifactId version4.12.0/version !-- 请使用最新稳定版 -- /dependency !-- Spring Boot Configuration Processor (让自定义配置有提示) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-configuration-processor/artifactId optionaltrue/optional /dependency /dependencies加完依赖记得刷新一下你的Maven项目让这些“工具包”下载到本地。1.2 配置Lychee-Rerank服务连接信息我们肯定不希望把服务地址、API密钥这些敏感信息硬编码在代码里。SpringBoot的application.yml或application.properties文件就是用来管理这些配置的。这里我用YAML格式来写看起来更清晰。在你的src/main/resources/application.yml文件中添加下面这段配置# Lychee-Rerank 服务配置 lychee: rerank: # Lychee-Rerank服务的API基础地址根据你的实际部署情况修改 base-url: http://localhost:8000 # 调用重排序接口的路径 rerank-path: /rerank # 你的API密钥如果服务需要认证的话 api-key: your_lychee_api_key_here # 连接超时时间毫秒 connect-timeout: 5000 # 读取超时时间毫秒 read-timeout: 30000 # 是否启用该功能 enabled: true这里我定义了一个前缀为lychee.rerank的配置组。base-url是关键它指向你部署的Lychee-Rerank服务。如果你用的是别人提供的云端服务就把这里的地址换成对应的公网地址。api-key也记得换成你自己的。2. 核心模型与配置类封装配置写好了接下来我们得在Java代码里能读到它们并且定义好我们和Lychee-Rerank服务“对话”时用的“语言”也就是数据格式。2.1 定义配置属性类我们创建一个类专门用来绑定配置文件里的那些属性。这样在代码里用起来就非常方便了。package com.yourcompany.demo.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import javax.validation.constraints.NotBlank; /** * Lychee-Rerank服务的配置属性 * 会自动从application.yml中读取lychee.rerank下的配置 */ Data Component ConfigurationProperties(prefix lychee.rerank) public class LycheeRerankProperties { /** * 服务基础地址 (例如: http://localhost:8000) */ NotBlank(message Lychee-Rerank服务地址不能为空) private String baseUrl; /** * 重排序接口路径 */ private String rerankPath /rerank; /** * API密钥 */ private String apiKey; /** * 连接超时时间(毫秒) */ private int connectTimeout 5000; /** * 读取超时时间(毫秒) */ private int readTimeout 30000; /** * 是否启用该功能 */ private boolean enabled true; /** * 获取完整的重排序接口URL */ public String getRerankUrl() { return baseUrl rerankPath; } }这个类用了ConfigurationProperties注解SpringBoot启动时会自动把application.yml里lychee.rerank下面的值填充到这个类的对应字段里。Data是Lombok的注解帮我们自动生成了getter、setter等方法。2.2 定义请求与响应模型调用API无非就是发送一个结构化的请求然后接收一个结构化的响应。我们来定义这两个“数据结构”。请求模型 (LycheeRerankRequest.java):这个对象代表我们要发给Lychee-Rerank服务的内容一个问题query和一堆待排序的文档documents。package com.yourcompany.demo.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import java.util.List; /** * 调用Lychee-Rerank服务的请求体 */ Data NoArgsConstructor AllArgsConstructor public class LycheeRerankRequest { /** * 用户查询的问题 */ NotBlank(message 查询问题不能为空) private String query; /** * 待排序的文档列表 */ NotEmpty(message 待排序文档列表不能为空) private ListString documents; /** * 返回的Top K个结果数量 (可选参数根据服务API决定) */ private Integer topK; // 这里还可以根据Lychee-Rerank服务的实际API添加其他可选参数比如模型名称、返回格式等 // private String model; // private Boolean returnDocuments; }响应模型 (LycheeRerankResponse.java):这个对象代表服务返回给我们的结果。通常重排序服务会返回每个文档的新分数和排名。package com.yourcompany.demo.model; import lombok.Data; import java.util.List; /** * Lychee-Rerank服务的标准响应体 */ Data public class LycheeRerankResponse { /** * 请求是否成功 */ private Boolean success; /** * 错误信息如果success为false */ private String error; /** * 重排序后的结果列表 */ private ListRerankResult results; /** * 本次请求消耗的token数如果服务提供 */ private Integer usage; /** * 单个重排序结果 */ Data public static class RerankResult { /** * 文档在原始列表中的索引 */ private Integer index; /** * 文档内容 */ private String document; /** * 重排序后的相关性分数 */ private Double score; /** * 重排序后的排名从1开始 */ private Integer rank; } }模型定义好了相当于我们和Lychee-Rerank服务之间的“通信协议”就确定了。3. 服务层封装优雅地调用API现在进入核心部分写一个服务类去真正地调用Lychee-Rerank的HTTP接口。好的封装应该让调用方感觉不到网络请求的细节就像调用本地方法一样简单。3.1 创建HTTP客户端首先我们利用前面配置里的超时时间创建一个OkHttpClient实例。Spring的Configuration注解让它成为一个被管理的Bean。package com.yourcompany.demo.config; import okhttp3.OkHttpClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; Configuration public class HttpClientConfig { Bean public OkHttpClient okHttpClient(LycheeRerankProperties properties) { return new OkHttpClient.Builder() .connectTimeout(properties.getConnectTimeout(), TimeUnit.MILLISECONDS) .readTimeout(properties.getReadTimeout(), TimeUnit.MILLISECONDS) .build(); } }3.2 实现核心服务类接下来是重头戏实现LycheeRerankService。这里我会把关键步骤都写上注释你可以清晰地看到一次完整的HTTP调用是如何组织的。package com.yourcompany.demo.service; import com.fasterxml.jackson.databind.ObjectMapper; import com.yourcompany.demo.config.LycheeRerankProperties; import com.yourcompany.demo.model.LycheeRerankRequest; import com.yourcompany.demo.model.LycheeRerankResponse; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.io.IOException; import java.util.Collections; /** * Lychee-Rerank服务封装 */ Slf4j Service public class LycheeRerankService { private final OkHttpClient httpClient; private final LycheeRerankProperties properties; private final ObjectMapper objectMapper; private final MediaType JSON MediaType.get(application/json; charsetutf-8); // 通过构造器注入依赖 public LycheeRerankService(OkHttpClient httpClient, LycheeRerankProperties properties, ObjectMapper objectMapper) { this.httpClient httpClient; this.properties properties; this.objectMapper objectMapper; } PostConstruct public void init() { if (!properties.isEnabled()) { log.warn(Lychee-Rerank服务未启用相关功能将不可用。); } else { log.info(Lychee-Rerank服务已初始化基础URL: {}, properties.getBaseUrl()); } } /** * 同步调用重排序服务 * * param request 重排序请求 * return 重排序响应 * throws IOException 当网络或服务异常时抛出 */ public LycheeRerankResponse rerankSync(LycheeRerankRequest request) throws IOException { if (!properties.isEnabled()) { log.warn(尝试调用未启用的Lychee-Rerank服务。); return createDisabledResponse(); } // 1. 将请求对象序列化为JSON字符串 String requestBodyJson objectMapper.writeValueAsString(request); RequestBody body RequestBody.create(requestBodyJson, JSON); // 2. 构建HTTP请求 Request httpRequest new Request.Builder() .url(properties.getRerankUrl()) .post(body) .addHeader(Content-Type, application/json) .addHeader(Accept, application/json) .build(); // 3. 添加API Key认证头如果配置了的话 if (properties.getApiKey() ! null !properties.getApiKey().trim().isEmpty()) { httpRequest httpRequest.newBuilder() .addHeader(Authorization, Bearer properties.getApiKey()) .build(); } log.debug(调用Lychee-Rerank服务URL: {}, 请求体: {}, properties.getRerankUrl(), requestBodyJson); // 4. 执行同步调用并获取响应 try (Response response httpClient.newCall(httpRequest).execute()) { if (!response.isSuccessful()) { String errorBody response.body() ! null ? response.body().string() : null; log.error(Lychee-Rerank服务调用失败状态码: {}, 响应体: {}, response.code(), errorBody); throw new IOException(HTTP请求失败状态码: response.code() , 详情: errorBody); } // 5. 解析响应体 if (response.body() ! null) { String responseBody response.body().string(); log.debug(Lychee-Rerank服务响应: {}, responseBody); return objectMapper.readValue(responseBody, LycheeRerankResponse.class); } else { throw new IOException(响应体为空); } } } /** * 创建一个服务未启用时的默认响应 */ private LycheeRerankResponse createDisabledResponse() { LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(Lychee-Rerank服务未启用); response.setResults(Collections.emptyList()); return response; } }这个服务类做了几件重要的事参数校验与状态检查首先检查服务是否启用。请求构建把Java对象转成JSON设置正确的HTTP头和认证信息。同步调用使用OkHttpClient发送请求。响应处理检查HTTP状态码成功则解析JSON返回Java对象失败则抛出异常并记录日志。日志记录关键步骤都打了日志方便出问题时排查。这样业务代码里只需要注入这个LycheeRerankService然后调用rerankSync方法就行了是不是清爽多了4. 控制器层提供RESTful API服务层封装好了我们还需要一个“窗口”对外提供服务。在SpringBoot里这个“窗口”就是Controller。4.1 设计API接口我们来设计一个简单清晰的REST API接收前端或其他服务传来的查询和文档列表返回重排序后的结果。package com.yourcompany.demo.controller; import com.yourcompany.demo.model.LycheeRerankRequest; import com.yourcompany.demo.model.LycheeRerankResponse; import com.yourcompany.demo.service.LycheeRerankService; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.io.IOException; /** * 对外提供重排序功能的REST API控制器 */ Slf4j Validated RestController RequestMapping(/api/v1/rerank) public class RerankController { private final LycheeRerankService rerankService; public RerankController(LycheeRerankService rerankService) { this.rerankService rerankService; } /** * 对文档列表进行重排序 * POST /api/v1/rerank * * param request 包含查询语句和文档列表的请求体 * return 重排序后的结果 */ PostMapping public ResponseEntityLycheeRerankResponse rerankDocuments(Valid RequestBody LycheeRerankRequest request) { log.info(收到重排序请求查询: [{}], 文档数量: {}, request.getQuery(), request.getDocuments().size()); try { LycheeRerankResponse response rerankService.rerankSync(request); return ResponseEntity.ok(response); } catch (IOException e) { log.error(调用重排序服务时发生IO异常, e); LycheeRerankResponse errorResponse new LycheeRerankResponse(); errorResponse.setSuccess(false); errorResponse.setError(服务调用失败: e.getMessage()); // 根据实际情况这里可以返回500或者定义更具体的业务异常状态码 return ResponseEntity.internalServerError().body(errorResponse); } catch (Exception e) { log.error(处理重排序请求时发生未知异常, e); LycheeRerankResponse errorResponse new LycheeRerankResponse(); errorResponse.setSuccess(false); errorResponse.setError(内部服务错误: e.getMessage()); return ResponseEntity.internalServerError().body(errorResponse); } } /** * 健康检查端点 * GET /api/v1/rerank/health * * return 服务健康状态 */ GetMapping(/health) public ResponseEntityString healthCheck() { // 这里可以添加更复杂的健康检查逻辑比如测试一下到Lychee-Rerank服务的连通性 return ResponseEntity.ok(Rerank Service is UP); } }这个控制器提供了两个接口POST /api/v1/rerank: 核心的重排序接口。它接收JSON格式的请求体交给服务层处理并统一处理异常返回结构化的响应。GET /api/v1/rerank/health: 一个简单的健康检查接口用于监控服务状态。注意Valid注解它会自动校验LycheeRerankRequest对象里我们之前用NotBlank、NotEmpty定义的规则如果校验失败Spring会直接返回400错误不用我们在代码里手动判断。5. 进阶优化异步处理与全局异常基本的跑通之后我们来看看怎么让它变得更健壮、性能更好。这对于生产环境至关重要。5.1 实现异步非阻塞调用同步调用会阻塞当前线程直到收到响应。如果Lychee-Rerank服务响应慢或者并发请求多我们的服务线程很快就会被耗光。改成异步调用可以极大提升吞吐量。我们先改造服务类增加一个异步方法// 在 LycheeRerankService 类中添加以下方法 /** * 异步调用重排序服务 * * param request 重排序请求 * return 包含响应结果的CompletableFuture */ public CompletableFutureLycheeRerankResponse rerankAsync(LycheeRerankRequest request) { if (!properties.isEnabled()) { log.warn(尝试调用未启用的Lychee-Rerank服务。); return CompletableFuture.completedFuture(createDisabledResponse()); } CompletableFutureLycheeRerankResponse future new CompletableFuture(); try { String requestBodyJson objectMapper.writeValueAsString(request); RequestBody body RequestBody.create(requestBodyJson, JSON); Request.Builder requestBuilder new Request.Builder() .url(properties.getRerankUrl()) .post(body) .addHeader(Content-Type, application/json); if (properties.getApiKey() ! null !properties.getApiKey().trim().isEmpty()) { requestBuilder.addHeader(Authorization, Bearer properties.getApiKey()); } Request httpRequest requestBuilder.build(); log.debug(异步调用Lychee-Rerank服务URL: {}, properties.getRerankUrl()); // 使用OkHttp的异步调用 httpClient.newCall(httpRequest).enqueue(new Callback() { Override public void onFailure(Call call, IOException e) { log.error(异步调用Lychee-Rerank服务失败, e); future.completeExceptionally(e); } Override public void onResponse(Call call, Response response) throws IOException { try { if (!response.isSuccessful()) { String errorBody response.body() ! null ? response.body().string() : null; log.error(Lychee-Rerank服务异步调用失败状态码: {}, response.code()); future.completeExceptionally(new IOException(HTTP response.code() : errorBody)); return; } if (response.body() ! null) { String responseBody response.body().string(); LycheeRerankResponse rerankResponse objectMapper.readValue(responseBody, LycheeRerankResponse.class); future.complete(rerankResponse); } else { future.completeExceptionally(new IOException(响应体为空)); } } catch (Exception e) { future.completeExceptionally(e); } } }); } catch (Exception e) { future.completeExceptionally(e); } return future; }然后在控制器里我们可以提供一个异步的API端点。SpringBoot对CompletableFuture的返回类型有很好的支持。// 在 RerankController 类中添加以下方法 /** * 异步对文档列表进行重排序 * POST /api/v1/rerank/async * * param request 包含查询语句和文档列表的请求体 * return 一个Future最终包含重排序后的结果 */ PostMapping(/async) public CompletableFutureResponseEntityLycheeRerankResponse rerankDocumentsAsync( Valid RequestBody LycheeRerankRequest request) { log.info(收到异步重排序请求查询: [{}], request.getQuery()); return rerankService.rerankAsync(request) .thenApply(ResponseEntity::ok) // 成功时包装成ResponseEntity .exceptionally(e - { // 异常处理 log.error(异步重排序处理失败, e); LycheeRerankResponse errorResponse new LycheeRerankResponse(); errorResponse.setSuccess(false); errorResponse.setError(异步处理失败: e.getMessage()); return ResponseEntity.internalServerError().body(errorResponse); }); }这样调用/api/v1/rerank/async接口的线程会立即返回不会阻塞。当Lychee-Rerank服务返回结果后Spring会处理这个CompletableFuture并最终将响应返回给客户端。5.2 添加全局异常处理我们之前在控制器里用了try-catch来捕获异常。但如果有多个控制器方法每个都写一遍会很麻烦。Spring提供了ControllerAdvice来集中处理异常。package com.yourcompany.demo.handler; import com.yourcompany.demo.model.LycheeRerankResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; import java.io.IOException; import java.util.stream.Collectors; /** * 全局异常处理器 */ Slf4j RestControllerAdvice public class GlobalExceptionHandler { /** * 处理参数校验失败异常Valid触发的 */ ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntityLycheeRerankResponse handleValidationException(MethodArgumentNotValidException ex) { String errorMessage ex.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(; )); log.warn(参数校验失败: {}, errorMessage); LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(请求参数无效: errorMessage); return ResponseEntity.badRequest().body(response); } /** * 处理服务调用IO异常如网络超时、连接失败 */ ExceptionHandler(IOException.class) public ResponseEntityLycheeRerankResponse handleIOException(IOException ex) { log.error(服务调用发生IO异常, ex); LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(外部服务通信失败: ex.getMessage()); return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response); } /** * 处理其他所有未捕获的异常 */ ExceptionHandler(Exception.class) public ResponseEntityLycheeRerankResponse handleGenericException(Exception ex) { log.error(系统内部发生未预期异常, ex); LycheeRerankResponse response new LycheeRerankResponse(); response.setSuccess(false); response.setError(系统内部错误请稍后重试); // 生产环境中这里不应该把详细的异常信息返回给客户端 return ResponseEntity.internalServerError().body(response); } }有了这个全局处理器控制器里的代码就可以更干净只关注业务逻辑异常处理交给它。而且它能保证所有接口返回的异常格式都是一致的。6. 总结与最佳实践建议走完上面这些步骤一个具备基本生产可用性的Lychee-Rerank集成服务就搭建起来了。我们来回顾一下关键点并聊聊在实际项目中还能做哪些优化。整个集成的核心思路很清晰配置化、模型化、服务化、API化。通过配置文件管理可变参数通过Java对象定义数据契约通过Service类封装外部调用细节最后通过REST Controller对外提供标准接口。这种分层结构让代码职责清晰也易于测试和维护。异步改造是提升性能的关键一步特别是对于这种需要调用外部、可能比较耗时的服务。使用CompletableFuture可以让我们的主线程快速释放去处理更多请求。全局异常处理则保证了服务的健壮性给客户端始终返回友好、统一的错误格式。在实际项目中你还可以考虑下面这些点连接池与超时优化OkHttpClient默认就有连接池但你可以根据压测结果调整maxIdleConnections和keepAliveDuration。超时时间connectTimeout,readTimeout,writeTimeout也要根据网络环境和下游服务的SLA来仔细设定。重试机制网络请求难免失败。可以考虑加入带退避策略的重试机制比如指数退避对于瞬时的网络抖动或下游服务短暂不可用的情况很有帮助。可以用Spring Retry库或者Resilience4j来实现。熔断与降级如果Lychee-Rerank服务长时间不可用持续的失败调用会消耗资源。引入熔断器如Resilience4j或Sentinel在失败率达到阈值时快速失败并提供一个降级策略比如直接返回原始排序结果或返回一个缓存的结果。监控与指标使用Micrometer等工具暴露关键指标比如调用次数、成功/失败率、响应时间分布P50, P95, P99。这对了解服务健康状况和性能瓶颈至关重要。缓存如果某些查询和文档组合被频繁请求可以考虑在调用Lychee-Rerank之前加一层缓存比如用Caffeine或Redis直接返回之前的排序结果能显著降低延迟和外部服务负载。最后记得为你的LycheeRerankService和RerankController编写单元测试和集成测试确保代码的稳定性和正确性。集成测试可以启动一个测试用的Lychee-Rerank服务或者用Mock Server来验证整个调用链路。希望这个实战指南能帮你顺利地把智能重排序能力集成到你的SpringBoot应用中。从简单的同步调用开始逐步引入异步、异常处理、熔断等机制你的服务会变得越来越可靠和高效。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。