Spring Boot 3响应式场景下唯一ID生成的代码实现咨询
问题描述
基于Spring Boot 3响应式框架开发,需求如下:
- 根据请求传入的格式生成唯一ID;
- 校验该ID在数据库中是否已存在;
- 若ID已存在则重新生成;
- 最多尝试10次,全部失败则抛出异常。
现有代码中无法实现非阻塞的ID唯一性校验,调用block()会触发“阻塞操作不被支持”的异常,需要修改代码完成非阻塞的ID生成与校验逻辑。
代码片段:
// DB CRUD operation service for Group table // Group record is unique by groupId and type @Autowired final GroupService groupService; public String newUniqueId(String format, String groupId) { // try 10 times to generate a unique id return IntStream.range(0, 10) .mapToObj(i -> newIdByFormat(format)) .filter(newIdByFormat -> isIdUnique(newIdByFormat, groupId)) // Need some guidence here .findFirst() .orElseThrow(() -> new RuntimeException("Could not generate unique id")); } private String newIdByFormat(String format) { // Map<String, Supplier<String>> map; return map.get(format).get(); } private Predicate<String> isIdUnique(String groupId) { // This call returns, Mono<Group> group = groupService.query(id, groupId) return id -> groupService.query(id, groupId) .filter(Objects::isNull); // How to return boolen value here? // tried calling .block() here, but fails with exception // block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-4 } public Mono<Group> createGroup(Request request) { return transformRequest(request); } private Mono<Group> transformRequest(Request request) { String uniqueId = newUniqueId(request.format, request.groupId); return dbService.save(new Group(uniqueId)); }
解决方案
核心问题是混用了阻塞式集合操作(IntStream)和响应式异步操作(Mono),必须把整个ID生成逻辑改成纯响应式流的形式,绝对不能用block()这类阻塞调用。以下是具体改造步骤:
1. 重构唯一性校验方法
把isIdUnique改成返回Mono<Boolean>,用响应式操作判断ID是否唯一:
private Mono<Boolean> isIdUnique(String id, String groupId) { // 查询数据库,判断ID是否存在;存在则返回false,不存在返回true return groupService.query(id, groupId) .hasElement() // 检查是否有查询结果,存在则返回Mono.just(true) .map(exists -> !exists); // 取反得到“是否唯一”的结果 }
2. 重构唯一ID生成方法
将newUniqueId改为返回Mono<String>,用Flux.range实现10次尝试的响应式循环:
public Mono<String> newUniqueId(String format, String groupId) { return Flux.range(0, 10) // 每次尝试生成一个ID .map(i -> newIdByFormat(format)) // 异步校验ID唯一性,只保留唯一的ID .flatMap(id -> isIdUnique(id, groupId) .filter(isUnique -> isUnique) .map(isUnique -> id)) // 取第一个符合要求的ID .next() // 10次尝试都失败时抛出异常 .switchIfEmpty(Mono.error(new RuntimeException("Could not generate unique id"))); }
3. 重构请求转换方法
因为newUniqueId现在返回Mono<String>,所以transformRequest必须用响应式链处理,不能直接同步获取结果:
private Mono<Group> transformRequest(Request request) { return newUniqueId(request.format, request.groupId) .map(uniqueId -> new Group(uniqueId)) .flatMap(dbService::save); }
关键注意点
- 全程使用响应式操作链,严格避免任何阻塞调用,符合Spring WebFlux的非阻塞模型;
Flux.range(0,10)替代原IntStream,实现10次尝试的异步循环;flatMap用于把ID生成和异步校验绑定,过滤出唯一ID后通过next()拿到第一个有效值;switchIfEmpty在10次尝试全部失败时抛出指定异常。
内容的提问来源于stack exchange,提问作者Entity Unknown
相关产品推荐
相关产品推荐

