admin管理员组

文章数量:1122846

I have a Springboot 3.4 web service with micrometer

The endpoint is straightforward, the controller accepts a list of messages to be sent to an external rest API I have no control over. The processing on their side takes very long.

The payload looks like, this is just an example, please imagine many many inner messages.

[
        "somefirst message",
        "some second message",
        "etc"
    ]

To handle this, I wrote the following spring rest controller:

    @PostMapping("/question2")
    public String question2(@RequestBody List<String> messages) {
        Observation parent = Observation.createNotStarted("parent", registry);
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (String message : messages) {
                parent.observe(() -> {
                    ContextExecutorService.wrap(executor,
                                    () -> ContextSnapshotFactory.builder().build().captureAll())
                            .submit(() -> {
                                Observation.createNotStarted("child" + message, registry).observe(() -> {
                                    messageProducer.sendRequest("topic-loom-micrometer",
                                            message + Thread.currentThread().getName());
                                });
                            });
                });
            }
            executor.shutdown();
        }
        return "it seems everything went fine";
    }

I also wrote another version with the new stream + gatherers map concurrent construct.

@PostMapping("/question3")
    public List<String> question3(@RequestBody List<String> messages) {
        Observation parent = Observation.createNotStarted("parent", registry);
        return messages.stream()
                .gather(Gatherers.mapConcurrent(messages.size() + 1, oneMessage -> sendRequestInParallel(oneMessage, parent)))
                .toList();
    }

    private String sendRequestInParallel(String oneMessage, Observation parent) {
        return parent.observe(() -> {
            return Observation.createNotStarted("child" + oneMessage, registry).observe(() -> {
                return restClient.post()
                        .uri("http://localhost:8081/justString?name=" + oneMessage)
                        .retrieve()
                        .body(String.class);
            });
        });
    }

However, it seems there is an issue for tracing. In a plain old for loop, we would see something like this, some sort of cascade

        for (String message : messages) {
            messageProducer.sendMessage("topic-loom", message);
        }

With project loom, the virtual thread (first construct) or the gatherers mapconcurent (second approach), I would have expected to see something like this (please correct me if I am wrong). Something like "not a cascade, but more parallel".

However, with both approaches, there seems to be an issue with the traces. It shows something very strange with the parent.

Question:

How to properly display something similar to the loop (first image) , where there are just two levels, the main task, and all the parallel runs?

本文标签: javaCorrelate traces for spans sent in parallel for Springboot micrometerStack Overflow