为什么REST端点被访问两次,第二次访问是否可以消除?

问题描述 投票:0回答:1

代码“有效”,因为它返回了预期的信息(DemoPOJO对象的列表)。但是,如下面的控制台输出所示,在localhost:8080 / v2 / DemoPOJO处对REST服务进行了两次调用。我感觉第二个调用是由于缺乏对反应式编程的了解,但是我看不到在此REST API上进行第二个调用的位置,并希望消除它,因为可能会产生冗余部署“真实事物”时出现性能问题。

在提供的代码中,对localhost:8080 / v3 / DemoClient(在DemoClientHandler]中实现)进行了调用,然后使用WebClient对象访问位于localhost:8080 / v2 / DemoPOJO的相应REST服务(在DemoPOJOHandler中实现。

((我已将代码剥离为仅与REST端点/ v3 / DemoClient相关的那些语句)

2019-09-26 12:30:23.389  INFO 4260 --- [           main] com.test.demo.DemoApplication            : Starting DemoApplication on M7730-LFR with PID 4260 (D:\sandbox\DemoReactive\build\classes\java\main started by LesR in D:\sandbox\DemoReactive)
2019-09-26 12:30:23.391  INFO 4260 --- [           main] com.test.demo.DemoApplication            : No active profile set, falling back to default profiles: default
2019-09-26 12:30:24.570  INFO 4260 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2019-09-26 12:30:24.573  INFO 4260 --- [           main] com.test.demo.DemoApplication            : Started DemoApplication in 1.41 seconds (JVM running for 1.975)
2019-09-26 12:30:28.796  INFO 4260 --- [ctor-http-nio-3] m.n.d.accesslogger.ServiceRequestLogger  : 
    *****  Begin, Access Request Log
    Service request    -> GET @ http://localhost:8080/v3/DemoClient
    Service handled by -> com.test.demo.democlient.DemoClientHandler.getAll()
    *****  End, Access Request Log
2019-09-26 12:30:28.823  INFO 4260 --- [ctor-http-nio-8] m.n.d.accesslogger.ServiceRequestLogger  : 
    *****  Begin, Access Request Log
    Service request    -> GET @ http://localhost:8080/v2/DemoPOJO
    Service handled by -> com.test.demo.demopojo.DemoPOJOHandler.getAll()
    *****  End, Access Request Log
2019-09-26 12:30:28.911  INFO 4260 --- [ctor-http-nio-9] m.n.d.accesslogger.ServiceRequestLogger  : 
    *****  Begin, Access Request Log
    Service request    -> GET @ http://localhost:8080/v2/DemoPOJO
    Service handled by -> com.test.demo.demopojo.DemoPOJOHandler.getAll()
    *****  End, Access Request Log

“第二级”处理程序,通过WebClient(DemoClient

)访问“第一级” REST API
@Component
public class DemoClientHandler {

    public static final String PATH_VAR_ID = "id";

    @Autowired
    ServiceRequestLogger svcRequestLogger;

    @Autowired
    DemoClient demoClient;

    public Mono<ServerResponse> getAll(ServerRequest request) {
        Flux<DemoPOJO> fluxDemoPOJO = demoClient.getAll();

        svcRequestLogger.logServiceRequest(this.getClass(), "getAll()", request);

        return fluxDemoPOJO.hasElements().flatMap(hasElement -> {
            return hasElement ? ServerResponse.ok()
                                              .contentType(MediaType.APPLICATION_JSON)
                                              .body(fluxDemoPOJO, DemoPOJO.class)
                              : ServerResponse.noContent().build();
        });
    }
}

使用WebClient

访问“第一级” REST API ...
@Component
public class DemoClient {

    private final WebClient client;

    public DemoClient() {
        client = WebClient.create();
    }

    public Flux<DemoPOJO> getAll() {
        return client.get().uri("http://localhost:8080/v2/DemoPOJO")
                           .accept(MediaType.APPLICATION_JSON)
                           .exchange()
                           .flatMapMany(response -> response.bodyToFlux(DemoPOJO.class));
    }
)

““第一级”处理程序...

@Component
public class DemoPOJOHandler {

    @Autowired
    private ServiceRequestLogger svcRequestLogger;

    @Autowired
    private DemoPOJOService service;

    public Mono<ServerResponse> getAll(ServerRequest request) {
        Flux<DemoPOJO> fluxDemoPOJO = service.getAll();

        svcRequestLogger.logServiceRequest(this.getClass(), "getAll()", request);

        return fluxDemoPOJO.hasElements().flatMap(hasElement -> {
            return hasElement ? ServerResponse.ok()
                                              .contentType(MediaType.APPLICATION_JSON)
                                              .body(fluxDemoPOJO, DemoPOJO.class)
                              : ServerResponse.noContent().build();
        });
    }
}

用于第二级(WebClient

)REST API的路由器...
@Configuration
public class DemoClientRouter {

    @Bean
    public RouterFunction<ServerResponse> clientRoutes(DemoClientHandler requestHandler) {
        return nest(path("/v3"),
                nest(accept(APPLICATION_JSON),
                        RouterFunctions.route(RequestPredicates.GET("/DemoClient"), requestHandler::getAll)));
    }
}

一级REST API的路由器...

@Configuration
public class DemoPOJORouter {

    @Bean
    public RouterFunction<ServerResponse> demoPOJORoute(DemoPOJOHandler requestHandler) {
        return nest(path("/v2"),
                nest(accept(APPLICATION_JSON),
                        RouterFunctions.route(RequestPredicates.GET("/DemoPOJO"), requestHandler::getAll)));
    }
}

为了示例的完整性添加了以下代码,但是我怀疑与我要隔离并删除的行为有关。

服务层,支持DemoPOJO操作...

@Component
public class DemoPOJOService {

    @Autowired
    private DemoPOJORepo demoPOJORepo;

    public Flux<DemoPOJO> getAll() {
        return Flux.fromArray(demoPOJORepo.getAll());
    }
}

POJO /数据的简单模型以支持探索...

@Component
public class DemoPOJORepo {

    private static final int NUM_OBJS = 5;

    private static DemoPOJORepo demoRepo = null;

    private Map<Integer, DemoPOJO> demoPOJOMap;

    private DemoPOJORepo() {
        initMap();
    }

    public static DemoPOJORepo getInstance() {
        if (demoRepo == null) {
            demoRepo = new DemoPOJORepo();
        }
        return demoRepo;
    }

    public DemoPOJO[] getAll() {
        return demoPOJOMap.values().toArray(new DemoPOJO[demoPOJOMap.size()]);
    }

    private void initMap() {
        demoPOJOMap = new TreeMap<Integer, DemoPOJO>();

        for (int ndx = 1; ndx < (NUM_OBJS + 1); ndx++) {
            demoPOJOMap.put(ndx, new DemoPOJO(ndx, "foo_" + ndx, ndx + 100));
        }
    }
}

日志(SLF4J

/(Logback *)REST服务的客户端访问应用程序日志...
@Component
public class ServiceRequestLogger {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    public void logServiceRequest(Class serviceHandler, String serviceMethod, ServerRequest request) {
        logger.info(buildLogMessage(serviceHandler, serviceMethod, request));
    }

    private String buildLogMessage(Class serviceHandler, String serviceMethod, ServerRequest request) {
        StringBuilder logMessage = new StringBuilder();

        /* <housekeeping code to build message to log */
        return logMessage.toString();
    }
}

代码“有效”,因为它返回了预期的信息(DemoPOJO对象的列表)。但是,如下面所示的控制台输出所示,正在...

rest reactive-programming spring-webflux
1个回答
0
投票

可能违反了某些规则,但是我尝试改写问题并在Why is the handler for a REST endpoint being accessed twice, when accessed from a WebClient?处收紧代码示例。

对于mod,我正在尝试避免对原始问题进行过多的编辑,因为这会使注释显得非主题。

© www.soinside.com 2019 - 2024. All rights reserved.