如何在Spring WebFlux中动态选择同一请求对应的控制器?
实现Spring WebFlux动态路由权重分配的方案
在Spring WebFlux中实现基于自定义规则(如权重比例)的动态路由,可以通过自定义HandlerFunction结合可动态调整的配置来实现,核心思路是在统一路由入口处根据规则选择目标处理器,以下是具体实现步骤:
1. 定义动态权重配置类
创建线程安全的配置类,存储并允许修改控制器A、B的权重比例,适配WebFlux异步场景的并发需求:
import java.util.concurrent.atomic.AtomicReference; import org.springframework.stereotype.Component; @Component public class RouteWeightConfig { // 数组存储[控制器A权重, 控制器B权重],总和建议设为100方便计算 private final AtomicReference<int[]> weightRef = new AtomicReference<>(new int[]{60, 40}); public int[] getWeights() { return weightRef.get(); } public void updateWeights(int weightA, int weightB) { if (weightA < 0 || weightB < 0 || weightA + weightB != 100) { throw new IllegalArgumentException("权重必须非负且总和为100"); } weightRef.set(new int[]{weightA, weightB}); } }
2. 实现动态路由转发的HandlerFunction
编写自定义HandlerFunction,注入权重配置和两个目标控制器的Handler,在请求处理时根据权重随机选择执行逻辑:
import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Mono; import java.util.concurrent.ThreadLocalRandom; @Component public class DynamicRouteHandler implements HandlerFunction<ServerResponse> { private final RouteWeightConfig weightConfig; private final HandlerFunction<ServerResponse> controllerAHandler; private final HandlerFunction<ServerResponse> controllerBHandler; // 构造注入两个目标控制器的HandlerFunction public DynamicRouteHandler(RouteWeightConfig weightConfig, HandlerFunction<ServerResponse> controllerAHandler, HandlerFunction<ServerResponse> controllerBHandler) { this.weightConfig = weightConfig; this.controllerAHandler = controllerAHandler; this.controllerBHandler = controllerBHandler; } @Override public Mono<ServerResponse> handle(ServerRequest request) { int[] weights = weightConfig.getWeights(); int random = ThreadLocalRandom.current().nextInt(100); // 根据随机数区间判断执行哪个控制器 if (random < weights[0]) { return controllerAHandler.handle(request); } else { return controllerBHandler.handle(request); } } }
3. 注册路由规则
在RouterFunctions配置中,将/v1/xyz路径绑定到自定义的DynamicRouteHandler:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; @Configuration public class RouterConfig { @Bean public RouterFunction<ServerResponse> dynamicRoute(DynamicRouteHandler dynamicRouteHandler) { return RouterFunctions.route() .GET("/v1/xyz", dynamicRouteHandler) .POST("/v1/xyz", dynamicRouteHandler) .build(); } // 定义控制器A的处理逻辑 @Bean public HandlerFunction<ServerResponse> controllerAHandler() { return request -> ServerResponse.ok().bodyValue("Response from Controller A"); } // 定义控制器B的处理逻辑 @Bean public HandlerFunction<ServerResponse> controllerBHandler() { return request -> ServerResponse.ok().bodyValue("Response from Controller B"); } }
4. 提供权重动态调整接口
添加接口用于运行时修改权重比例,无需重启服务:
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class WeightAdjustController { private final RouteWeightConfig weightConfig; public WeightAdjustController(RouteWeightConfig weightConfig) { this.weightConfig = weightConfig; } @PostMapping("/adjust-weight") public String adjustWeight(@RequestParam int weightA, @RequestParam int weightB) { try { weightConfig.updateWeights(weightA, weightB); return "权重已更新:控制器A=" + weightA + "%,控制器B=" + weightB + "%"; } catch (IllegalArgumentException e) { return "更新失败:" + e.getMessage(); } } }
关键注意事项
- 线程安全:使用
AtomicReference存储权重,避免并发修改导致的不一致问题; - 随机数生成:使用
ThreadLocalRandom而非Random,在WebFlux异步场景下更高效且线程安全; - 扩展性:如果需要更复杂的路由规则(如基于用户标签、请求参数),只需修改
DynamicRouteHandler的判断逻辑即可; - 参数校验:权重修改接口添加参数校验,避免非法配置导致路由异常。
内容的提问来源于stack exchange,提问作者Ray Chase
相关产品推荐
相关产品推荐

