This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch virtual-threads in repository https://gitbox.apache.org/repos/asf/skywalking.git
commit aaa294b0dc381816b17b6e207d4ea0722ddad831 Author: Wu Sheng <[email protected]> AuthorDate: Mon Feb 16 16:08:08 2026 +0800 Add virtual thread support for Armeria HTTP servers on JDK 25+ Armeria's blockingTaskExecutor (default max 200 cached platform threads per server) runs all @Blocking handler methods. On JDK 25+, replace it with a fully virtual-thread-backed ScheduledExecutorService. VirtualThreadScheduledExecutor implements scheduling by sleeping in virtual threads (which does not block OS threads), requiring zero platform threads. VirtualScheduledFuture provides the ScheduledFuture contract backed by virtual thread futures. Each HTTP server gets a distinct name (core-http, receiver-http, promql-http, logql-http, zipkin-query-http, zipkin-http, firehose-http). Document thread pool changes in changes.md. Co-Authored-By: Claude Opus 4.6 <[email protected]> --- docs/en/changes/changes.md | 8 + .../oap/server/core/CoreModuleProvider.java | 1 + .../oap/server/library/server/http/HTTPServer.java | 15 ++ .../util/VirtualThreadScheduledExecutor.java | 261 +++++++++++++++++++++ .../oap/server/library/util/VirtualThreads.java | 30 +++ .../server/library/util/VirtualThreadsTest.java | 34 +++ .../skywalking/oap/query/logql/LogQLProvider.java | 1 + .../oap/query/promql/PromQLProvider.java | 1 + .../oap/query/zipkin/ZipkinQueryProvider.java | 1 + .../AWSFirehoseReceiverModuleProvider.java | 1 + .../server/SharingServerModuleProvider.java | 1 + .../receiver/zipkin/ZipkinReceiverProvider.java | 1 + 12 files changed, 355 insertions(+) diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md index ec5d97fd4c..6305beb123 100644 --- a/docs/en/changes/changes.md +++ b/docs/en/changes/changes.md @@ -12,6 +12,14 @@ * Add `CLAUDE.md` as AI assistant guide for the project. * Upgrade Groovy to 5.0.3 in OAP backend. * Bump up nodejs to v24.13.0 for the latest UI(booster-ui) compiling. +* Add virtual thread support (JDK 25+) for gRPC and Armeria HTTP server handler threads. + Set `SW_VIRTUAL_THREADS_ENABLED=false` to disable. + + | Pool | JDK < 25 | JDK 25+ | + |---|---|---| + | gRPC handler (`core-grpc`, `receiver-grpc`, `als-grpc`, `ebpf-grpc`) | Cached platform threads (unbounded) | Virtual threads (per-task) | + | HTTP blocking (`core-http`, `receiver-http`, `promql-http`, `logql-http`, `zipkin-query-http`, `zipkin-http`, `firehose-http`) | Cached platform threads (max 200/server) | Virtual threads (per-task) | + | Netty/Armeria I/O event loops | Platform threads (`cores*2` / `cores`) | Unchanged | #### OAP Server diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java index 507c346200..337208602d 100755 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java @@ -265,6 +265,7 @@ public class CoreModuleProvider extends ModuleProvider { setBootingParameter("oap.external.http.host", moduleConfig.getRestHost()); setBootingParameter("oap.external.http.port", moduleConfig.getRestPort()); httpServer = new HTTPServer(httpServerConfig); + httpServer.setBlockingTaskName("core-http"); httpServer.initialize(); this.registerServiceImplementation(ConfigService.class, new ConfigService(moduleConfig, this)); diff --git a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java index 0ab8b8925f..1a352818fa 100644 --- a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java +++ b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java @@ -35,10 +35,12 @@ import java.net.InetSocketAddress; import java.time.Duration; import java.util.List; import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; import lombok.extern.slf4j.Slf4j; import org.apache.skywalking.oap.server.library.server.Server; import org.apache.skywalking.oap.server.library.server.ssl.PrivateKeyUtil; +import org.apache.skywalking.oap.server.library.util.VirtualThreads; import static java.util.Objects.requireNonNull; @@ -48,11 +50,16 @@ public class HTTPServer implements Server { protected ServerBuilder sb; // Health check service, supports HEAD, GET method. protected final Set<HttpMethod> allowedMethods = Sets.newHashSet(HttpMethod.HEAD); + private String blockingTaskName = "http-blocking"; public HTTPServer(HTTPServerConfig config) { this.config = config; } + public void setBlockingTaskName(final String blockingTaskName) { + this.blockingTaskName = blockingTaskName; + } + @Override public void initialize() { sb = com.linecorp.armeria.server.Server @@ -93,6 +100,14 @@ public class HTTPServer implements Server { sb.absoluteUriTransformer(this::transformAbsoluteURI); } + if (VirtualThreads.isSupported()) { + final ScheduledExecutorService blockingExecutor = VirtualThreads.createScheduledExecutor( + blockingTaskName, () -> null); + if (blockingExecutor != null) { + sb.blockingTaskExecutor(blockingExecutor, true); + } + } + log.info("Server root context path: {}", config.getContextPath()); } diff --git a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/VirtualThreadScheduledExecutor.java b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/VirtualThreadScheduledExecutor.java new file mode 100644 index 0000000000..6392283cb9 --- /dev/null +++ b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/VirtualThreadScheduledExecutor.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.oap.server.library.util; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import lombok.extern.slf4j.Slf4j; + +/** + * A {@link ScheduledExecutorService} fully backed by virtual threads. + * + * <p>All methods — including {@code schedule()}, {@code scheduleAtFixedRate()}, + * and {@code scheduleWithFixedDelay()} — delegate to virtual threads. + * Scheduling is implemented by sleeping in a virtual thread (which does not + * block OS threads), eliminating the need for a platform timer thread. + * + * <p>This adapter bridges the gap between virtual thread executors (which return + * {@link ExecutorService}) and frameworks like Armeria that require a + * {@link ScheduledExecutorService} for their blocking task executor. + */ +@Slf4j +final class VirtualThreadScheduledExecutor implements ScheduledExecutorService { + + private final ExecutorService vtExecutor; + + VirtualThreadScheduledExecutor(final ExecutorService vtExecutor) { + this.vtExecutor = vtExecutor; + } + + // --- Core execution: delegate to virtual threads --- + + @Override + public void execute(final Runnable command) { + vtExecutor.execute(command); + } + + @Override + public Future<?> submit(final Runnable task) { + return vtExecutor.submit(task); + } + + @Override + public <T> Future<T> submit(final Runnable task, final T result) { + return vtExecutor.submit(task, result); + } + + @Override + public <T> Future<T> submit(final Callable<T> task) { + return vtExecutor.submit(task); + } + + @Override + public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks) + throws InterruptedException { + return vtExecutor.invokeAll(tasks); + } + + @Override + public <T> List<Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks, + final long timeout, final TimeUnit unit) + throws InterruptedException { + return vtExecutor.invokeAll(tasks, timeout, unit); + } + + @Override + public <T> T invokeAny(final Collection<? extends Callable<T>> tasks) + throws InterruptedException, ExecutionException { + return vtExecutor.invokeAny(tasks); + } + + @Override + public <T> T invokeAny(final Collection<? extends Callable<T>> tasks, + final long timeout, final TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return vtExecutor.invokeAny(tasks, timeout, unit); + } + + // --- Scheduling: sleep in virtual thread, then execute --- + + @Override + public ScheduledFuture<?> schedule(final Runnable command, final long delay, final TimeUnit unit) { + final long triggerNanos = System.nanoTime() + unit.toNanos(delay); + final VirtualScheduledFuture<Void> sf = new VirtualScheduledFuture<>(triggerNanos); + sf.setFuture(vtExecutor.submit(() -> { + sleepUntil(triggerNanos); + command.run(); + return null; + })); + return sf; + } + + @Override + public <V> ScheduledFuture<V> schedule(final Callable<V> callable, final long delay, + final TimeUnit unit) { + final long triggerNanos = System.nanoTime() + unit.toNanos(delay); + final VirtualScheduledFuture<V> sf = new VirtualScheduledFuture<>(triggerNanos); + sf.setFuture(vtExecutor.submit(() -> { + sleepUntil(triggerNanos); + return callable.call(); + })); + return sf; + } + + @Override + public ScheduledFuture<?> scheduleAtFixedRate(final Runnable command, final long initialDelay, + final long period, final TimeUnit unit) { + final long periodNanos = unit.toNanos(period); + final long firstTrigger = System.nanoTime() + unit.toNanos(initialDelay); + final VirtualScheduledFuture<Void> sf = new VirtualScheduledFuture<>(firstTrigger); + sf.setFuture(vtExecutor.submit(() -> { + long nextTrigger = firstTrigger; + sleepUntil(nextTrigger); + while (!Thread.currentThread().isInterrupted()) { + command.run(); + nextTrigger += periodNanos; + sf.updateTriggerNanos(nextTrigger); + sleepUntil(nextTrigger); + } + return null; + })); + return sf; + } + + @Override + public ScheduledFuture<?> scheduleWithFixedDelay(final Runnable command, final long initialDelay, + final long delay, final TimeUnit unit) { + final long delayNanos = unit.toNanos(delay); + final long firstTrigger = System.nanoTime() + unit.toNanos(initialDelay); + final VirtualScheduledFuture<Void> sf = new VirtualScheduledFuture<>(firstTrigger); + sf.setFuture(vtExecutor.submit(() -> { + sleepUntil(firstTrigger); + while (!Thread.currentThread().isInterrupted()) { + command.run(); + final long nextTrigger = System.nanoTime() + delayNanos; + sf.updateTriggerNanos(nextTrigger); + sleepUntil(nextTrigger); + } + return null; + })); + return sf; + } + + private static void sleepUntil(final long triggerNanos) throws InterruptedException { + long remaining = triggerNanos - System.nanoTime(); + while (remaining > 0) { + TimeUnit.NANOSECONDS.sleep(remaining); + remaining = triggerNanos - System.nanoTime(); + } + } + + // --- Lifecycle --- + + @Override + public void shutdown() { + vtExecutor.shutdown(); + } + + @Override + public List<Runnable> shutdownNow() { + return vtExecutor.shutdownNow(); + } + + @Override + public boolean isShutdown() { + return vtExecutor.isShutdown(); + } + + @Override + public boolean isTerminated() { + return vtExecutor.isTerminated(); + } + + @Override + public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException { + return vtExecutor.awaitTermination(timeout, unit); + } + + /** + * A {@link ScheduledFuture} backed by a virtual thread {@link Future}. + */ + static final class VirtualScheduledFuture<V> implements ScheduledFuture<V> { + private volatile Future<V> delegate; + private volatile long triggerNanos; + + VirtualScheduledFuture(final long triggerNanos) { + this.triggerNanos = triggerNanos; + } + + void setFuture(final Future<V> delegate) { + this.delegate = delegate; + } + + void updateTriggerNanos(final long triggerNanos) { + this.triggerNanos = triggerNanos; + } + + @Override + public long getDelay(final TimeUnit unit) { + return unit.convert(triggerNanos - System.nanoTime(), TimeUnit.NANOSECONDS); + } + + @Override + public int compareTo(final Delayed other) { + if (other == this) { + return 0; + } + return Long.compare(getDelay(TimeUnit.NANOSECONDS), other.getDelay(TimeUnit.NANOSECONDS)); + } + + @Override + public boolean cancel(final boolean mayInterruptIfRunning) { + return delegate.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return delegate.isCancelled(); + } + + @Override + public boolean isDone() { + return delegate.isDone(); + } + + @Override + public V get() throws InterruptedException, ExecutionException { + return delegate.get(); + } + + @Override + public V get(final long timeout, final TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate.get(timeout, unit); + } + } +} diff --git a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/VirtualThreads.java b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/VirtualThreads.java index d796ebec2c..4f558157a2 100644 --- a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/VirtualThreads.java +++ b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/VirtualThreads.java @@ -21,6 +21,7 @@ package org.apache.skywalking.oap.server.library.util; import java.lang.reflect.Method; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.function.Supplier; import lombok.extern.slf4j.Slf4j; @@ -156,6 +157,35 @@ public final class VirtualThreads { return platformExecutorSupplier.get(); } + /** + * Create a named scheduled executor service with virtual threads enabled by default. + * On JDK 25+, creates a virtual-thread-backed {@link ScheduledExecutorService}. + * On older JDKs, delegates to the provided {@code platformExecutorSupplier}. + * + * <p>This is designed for frameworks (e.g. Armeria) that require a + * {@link ScheduledExecutorService} for their blocking task executor. + * All methods — including scheduling — are fully backed by virtual threads. + * Scheduling is implemented by sleeping in a virtual thread. + * + * @param namePrefix prefix for virtual thread names + * @param platformExecutorSupplier supplies the platform-thread executor as fallback + * @return virtual thread scheduled executor on JDK 25+, or the supplier's executor otherwise + */ + public static ScheduledExecutorService createScheduledExecutor( + final String namePrefix, + final Supplier<ScheduledExecutorService> platformExecutorSupplier) { + if (SUPPORTED) { + try { + final ExecutorService vtExecutor = createVirtualThreadExecutor(namePrefix); + return new VirtualThreadScheduledExecutor(vtExecutor); + } catch (final ReflectiveOperationException e) { + log.warn("Failed to create virtual thread scheduled executor [{}], " + + "falling back to platform threads", namePrefix, e); + } + } + return platformExecutorSupplier.get(); + } + private static ExecutorService createVirtualThreadExecutor( final String namePrefix) throws ReflectiveOperationException { // Thread.ofVirtual().name("vt:" + namePrefix + "-", 0).factory() diff --git a/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/VirtualThreadsTest.java b/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/VirtualThreadsTest.java index d445915446..325b621fb9 100644 --- a/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/VirtualThreadsTest.java +++ b/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/VirtualThreadsTest.java @@ -21,6 +21,8 @@ import java.lang.reflect.Method; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -83,6 +85,38 @@ public class VirtualThreadsTest { } } + @Test + public void testScheduledExecutorUsesVirtualThreads() throws Exception { + if (!VirtualThreads.isSupported()) { + return; + } + final ScheduledExecutorService executor = VirtualThreads.createScheduledExecutor( + "sched-vt", () -> null); + assertNotNull(executor); + try { + // Test execute() runs on virtual threads + final ThreadCapture capture = submitAndCapture(executor); + assertTrue(capture.name.startsWith("vt:sched-vt-"), + "Scheduled executor virtual thread name should start with 'vt:sched-vt-', but was: " + + capture.name); + assertTrue(isVirtual(capture.thread), + "Scheduled executor should use virtual threads on JDK 25+"); + + // Test schedule() also dispatches to virtual threads + final AtomicReference<Thread> scheduledRef = new AtomicReference<>(); + final CountDownLatch scheduledLatch = new CountDownLatch(1); + final ScheduledFuture<?> future = executor.schedule(() -> { + scheduledRef.set(Thread.currentThread()); + scheduledLatch.countDown(); + }, 10, TimeUnit.MILLISECONDS); + assertTrue(scheduledLatch.await(5, TimeUnit.SECONDS), "Scheduled task did not complete"); + assertTrue(isVirtual(scheduledRef.get()), + "Scheduled task should run on a virtual thread"); + } finally { + executor.shutdown(); + } + } + @Test public void testFallbackUsedWhenNotSupported() { if (VirtualThreads.isSupported()) { diff --git a/oap-server/server-query-plugin/logql-plugin/src/main/java/org/apache/skywalking/oap/query/logql/LogQLProvider.java b/oap-server/server-query-plugin/logql-plugin/src/main/java/org/apache/skywalking/oap/query/logql/LogQLProvider.java index b23cd7d84e..7299b35149 100644 --- a/oap-server/server-query-plugin/logql-plugin/src/main/java/org/apache/skywalking/oap/query/logql/LogQLProvider.java +++ b/oap-server/server-query-plugin/logql-plugin/src/main/java/org/apache/skywalking/oap/query/logql/LogQLProvider.java @@ -76,6 +76,7 @@ public class LogQLProvider extends ModuleProvider { .build(); httpServer = new HTTPServer(httpServerConfig); + httpServer.setBlockingTaskName("logql-http"); httpServer.initialize(); httpServer.addHandler( new LogQLApiHandler(getManager()), diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/PromQLProvider.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/PromQLProvider.java index a41ce71a89..62c9fc0cb6 100644 --- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/PromQLProvider.java +++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/PromQLProvider.java @@ -77,6 +77,7 @@ public class PromQLProvider extends ModuleProvider { .build(); httpServer = new HTTPServer(httpServerConfig); + httpServer.setBlockingTaskName("promql-http"); httpServer.initialize(); httpServer.addHandler( new PromQLApiHandler(getManager(), config), diff --git a/oap-server/server-query-plugin/zipkin-query-plugin/src/main/java/org/apache/skywalking/oap/query/zipkin/ZipkinQueryProvider.java b/oap-server/server-query-plugin/zipkin-query-plugin/src/main/java/org/apache/skywalking/oap/query/zipkin/ZipkinQueryProvider.java index d5ab3a6be1..aa5ff0141e 100644 --- a/oap-server/server-query-plugin/zipkin-query-plugin/src/main/java/org/apache/skywalking/oap/query/zipkin/ZipkinQueryProvider.java +++ b/oap-server/server-query-plugin/zipkin-query-plugin/src/main/java/org/apache/skywalking/oap/query/zipkin/ZipkinQueryProvider.java @@ -77,6 +77,7 @@ public class ZipkinQueryProvider extends ModuleProvider { .build(); httpServer = new HTTPServer(httpServerConfig); + httpServer.setBlockingTaskName("zipkin-query-http"); httpServer.initialize(); httpServer.addHandler( new ZipkinQueryHandler(config, getManager()), diff --git a/oap-server/server-receiver-plugin/aws-firehose-receiver/src/main/java/org/apache/skywalking/oap/server/receiver/aws/firehose/AWSFirehoseReceiverModuleProvider.java b/oap-server/server-receiver-plugin/aws-firehose-receiver/src/main/java/org/apache/skywalking/oap/server/receiver/aws/firehose/AWSFirehoseReceiverModuleProvider.java index 3ab6608e20..41d8ea15ec 100644 --- a/oap-server/server-receiver-plugin/aws-firehose-receiver/src/main/java/org/apache/skywalking/oap/server/receiver/aws/firehose/AWSFirehoseReceiverModuleProvider.java +++ b/oap-server/server-receiver-plugin/aws-firehose-receiver/src/main/java/org/apache/skywalking/oap/server/receiver/aws/firehose/AWSFirehoseReceiverModuleProvider.java @@ -81,6 +81,7 @@ public class AWSFirehoseReceiverModuleProvider extends ModuleProvider { .tlsCertChainPath(moduleConfig.getTlsCertChainPath()) .build(); httpServer = new HTTPServer(httpServerConfig); + httpServer.setBlockingTaskName("firehose-http"); httpServer.initialize(); } diff --git a/oap-server/server-receiver-plugin/skywalking-sharing-server-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/sharing/server/SharingServerModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-sharing-server-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/sharing/server/SharingServerModuleProvider.java index a37e8093f0..7a67823372 100644 --- a/oap-server/server-receiver-plugin/skywalking-sharing-server-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/sharing/server/SharingServerModuleProvider.java +++ b/oap-server/server-receiver-plugin/skywalking-sharing-server-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/sharing/server/SharingServerModuleProvider.java @@ -90,6 +90,7 @@ public class SharingServerModuleProvider extends ModuleProvider { setBootingParameter("oap.external.http.port", config.getRestPort()); httpServer = new HTTPServer(httpServerConfig); + httpServer.setBlockingTaskName("receiver-http"); httpServer.initialize(); this.registerServiceImplementation(HTTPHandlerRegister.class, new HTTPHandlerRegisterImpl(httpServer)); diff --git a/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zipkin/ZipkinReceiverProvider.java b/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zipkin/ZipkinReceiverProvider.java index 441f9faaec..3878a6d7c0 100644 --- a/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zipkin/ZipkinReceiverProvider.java +++ b/oap-server/server-receiver-plugin/zipkin-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/zipkin/ZipkinReceiverProvider.java @@ -88,6 +88,7 @@ public class ZipkinReceiverProvider extends ModuleProvider { .build(); httpServer = new HTTPServer(httpServerConfig); + httpServer.setBlockingTaskName("zipkin-http"); httpServer.initialize(); httpServer.addHandler(
