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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 00:50:33