From e30851b4d8708e4cba7801f733d32107b87eacfe Mon Sep 17 00:00:00 2001
From: wulingxiao <1251605638@qqcom>
Date: Sat, 9 Jul 2022 19:35:28 +0800
Subject: [PATCH] support MetadataContext transfer in thread poll
---
.../metadata/concurrent/MetadataCallable.java | 100 +++++++++++++
.../metadata/concurrent/MetadataRunnable.java | 97 ++++++++++++
.../metadata/concurrent/MetadataWrap.java | 42 ++++++
.../concurrent/executor/MetadataExecutor.java | 71 +++++++++
.../executor/MetadataExecutorService.java | 141 ++++++++++++++++++
.../executor/MetadataExecutors.java | 105 +++++++++++++
.../MetadataScheduledExecutorService.java | 97 ++++++++++++
.../concurrent/MetadataCallableTest.java | 131 ++++++++++++++++
.../concurrent/MetadataRunnableTest.java | 133 +++++++++++++++++
.../metadata/concurrent/MetadataTestUtil.java | 49 ++++++
.../executor/MetadataExecutorServiceTest.java | 126 ++++++++++++++++
.../executor/MetadataExecutorTest.java | 102 +++++++++++++
.../executor/MetadataExecutorsTest.java | 64 ++++++++
.../MetadataScheduledExecutorServiceTest.java | 88 +++++++++++
14 files changed, 1346 insertions(+)
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataCallable.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataRunnable.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataWrap.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutor.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutorService.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutors.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataScheduledExecutorService.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/MetadataCallableTest.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/MetadataRunnableTest.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/MetadataTestUtil.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutorServiceTest.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutorTest.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutorsTest.java
create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/executor/MetadataScheduledExecutorServiceTest.java
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataCallable.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataCallable.java
new file mode 100644
index 000000000..fb45a08f0
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataCallable.java
@@ -0,0 +1,100 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * {@link MetadataCallable} decorate {@link Callable} to get {@link MetadataContext} value
+ * and transfer it to the time of {@link Callable} execution, needed when use {@link Callable} to thread pool.
+ *
+ * Use factory methods {@link #get} / {@link #gets} to create instance.
+ *
+ * @author wlx
+ * @date 2022/7/8 9:31 下午
+ */
+public final class MetadataCallable implements Callable,
+ MetadataWrap> {
+
+ private final Callable delegate;
+
+ private final AtomicReference metadataContextReference;
+
+ private MetadataCallable(Callable delegate) {
+ this.delegate = delegate;
+ this.metadataContextReference = new AtomicReference<>(MetadataContextHolder.get());
+ }
+
+ @Override
+ public V call() throws Exception {
+ MetadataContext metadataContext = metadataContextReference.get();
+ MetadataContext metadataContextBackup = MetadataContextHolder.get();
+ MetadataContextHolder.set(metadataContext);
+ try {
+ return delegate.call();
+ } finally {
+ MetadataContextHolder.set(metadataContextBackup);
+ }
+ }
+
+ /**
+ * Factory method to create {@link MetadataCallable} instance.
+ *
+ * @param delegate delegate
+ * @param MetadataCallable return type
+ * @return {@link MetadataCallable} instance
+ */
+ public static Callable get(Callable delegate) {
+ if (null == delegate || delegate instanceof MetadataCallable) {
+ return delegate;
+ } else {
+ return new MetadataCallable<>(delegate);
+ }
+ }
+
+
+ /**
+ * Factory method to create some {@link MetadataCallable} instance.
+ *
+ * @param delegates delegates
+ * @param MetadataCallable return type
+ * @return some {@link MetadataCallable} instance
+ */
+ public static List> gets(Collection extends Callable> delegates) {
+ if (delegates == null) {
+ return Collections.emptyList();
+ }
+ return delegates.stream().map(
+ MetadataCallable::get
+ ).collect(Collectors.toList());
+ }
+
+ @Override
+ public Callable unWrap() {
+ return this.delegate;
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataRunnable.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataRunnable.java
new file mode 100644
index 000000000..0a60ee4fb
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataRunnable.java
@@ -0,0 +1,97 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * {@link MetadataRunnable} decorate {@link Runnable} to get {@link MetadataContext} value
+ * and transfer it to the time of {@link Runnable} execution, needed when use {@link Runnable} to thread pool.
+ *
+ * Use factory methods {@link #get} / {@link #gets} to create instance.
+ *
+ *
+ * @author wlx
+ * @date 2022/7/8 9:16 下午
+ */
+public final class MetadataRunnable implements Runnable,
+ MetadataWrap {
+
+ private final Runnable delegate;
+
+ private final AtomicReference metadataContextReference;
+
+ private MetadataRunnable(Runnable delegate) {
+ this.delegate = delegate;
+ this.metadataContextReference = new AtomicReference<>(MetadataContextHolder.get());
+ }
+
+ @Override
+ public void run() {
+ MetadataContext metadataContext = metadataContextReference.get();
+ MetadataContext metadataContextBackup = MetadataContextHolder.get();
+ MetadataContextHolder.set(metadataContext);
+ try {
+ delegate.run();
+ } finally {
+ MetadataContextHolder.set(metadataContextBackup);
+ }
+ }
+
+ /**
+ * Factory method to create {@link MetadataRunnable} instance.
+ *
+ * @param delegate delegate
+ * @return MetadataRunnable instance
+ */
+ public static Runnable get(Runnable delegate) {
+ if (null == delegate || delegate instanceof MetadataRunnable) {
+ return delegate;
+ } else {
+ return new MetadataRunnable(delegate);
+ }
+ }
+
+ /**
+ * Factory method to create {@link MetadataRunnable} instance.
+ *
+ * @param delegates delegates
+ * @return MetadataRunnable instances
+ */
+ public static List gets(Collection delegates) {
+ if (delegates == null) {
+ return Collections.emptyList();
+ }
+ return delegates.stream().map(
+ MetadataRunnable::get
+ ).collect(Collectors.toList());
+ }
+
+ @Override
+ public Runnable unWrap() {
+ return this.delegate;
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataWrap.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataWrap.java
new file mode 100644
index 000000000..6b9b2077d
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/MetadataWrap.java
@@ -0,0 +1,42 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent;
+
+/**
+ * Metadata Wrapper interface.
+ *
+ * Used to mark wrapper types, for example:
+ *
+ *
+ * @author wlx
+ * @date 2022/7/9 9:17 上午
+ */
+
+public interface MetadataWrap {
+
+ /**
+ * unwrap to the original/underneath one.
+ *
+ * @return a unWrap instance
+ */
+ T unWrap();
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutor.java
new file mode 100644
index 000000000..62cab1a91
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutor.java
@@ -0,0 +1,71 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent.executor;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.metadata.concurrent.MetadataRunnable;
+import com.tencent.cloud.metadata.concurrent.MetadataWrap;
+import org.springframework.lang.NonNull;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link MetadataContext} Wrapper of {@link Executor},
+ * transfer the {@link MetadataContext} from the task submit time of {@link Runnable}
+ * to the execution time of {@link Runnable}.
+ *
+ * @author wlx
+ * * @date 2022/7/8 9:35 下午
+ */
+class MetadataExecutor implements Executor, MetadataWrap {
+
+ private final Executor delegate;
+
+ public MetadataExecutor(Executor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void execute(@NonNull Runnable command) {
+ delegate.execute(MetadataRunnable.get(command));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MetadataExecutor that = (MetadataExecutor) o;
+ return delegate.equals(that.delegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(delegate);
+ }
+
+ @Override
+ public Executor unWrap() {
+ return this.delegate;
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutorService.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutorService.java
new file mode 100644
index 000000000..e293e9503
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutorService.java
@@ -0,0 +1,141 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent.executor;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.metadata.concurrent.MetadataCallable;
+import com.tencent.cloud.metadata.concurrent.MetadataRunnable;
+import org.springframework.lang.NonNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * {@link MetadataContext} Wrapper of {@link ExecutorService},
+ * transfer the {@link MetadataContext} from the task submit time of {@link Runnable} or {@link Callable}
+ * to the execution time of {@link Runnable} or {@link Callable}.
+ *
+ * @author wlx
+ * @date 2022/7/8 9:36 下午
+ */
+class MetadataExecutorService extends MetadataExecutor implements ExecutorService {
+
+ private final ExecutorService delegate;
+
+ public MetadataExecutorService(ExecutorService delegate) {
+ super(delegate);
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void shutdown() {
+ this.delegate.shutdown();
+ }
+
+ @Override
+ @NonNull
+ public List shutdownNow() {
+ return this.delegate.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return this.delegate.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return this.delegate.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, @NonNull TimeUnit unit) throws InterruptedException {
+ return this.delegate.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ @NonNull
+ public Future submit(@NonNull Callable task) {
+ return this.delegate.submit(MetadataCallable.get(task));
+ }
+
+ @Override
+ @NonNull
+ public Future submit(@NonNull Runnable task, T result) {
+ return this.delegate.submit(MetadataRunnable.get(task), result);
+ }
+
+ @Override
+ @NonNull
+ public Future> submit(@NonNull Runnable task) {
+ return this.delegate.submit(MetadataRunnable.get(task));
+ }
+
+ @Override
+ @NonNull
+ public List> invokeAll(@NonNull Collection extends Callable> tasks) throws InterruptedException {
+ return this.delegate.invokeAll(MetadataCallable.gets(tasks));
+ }
+
+ @Override
+ @NonNull
+ public List> invokeAll(@NonNull Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+ return this.delegate.invokeAll(MetadataCallable.gets(tasks), timeout, unit);
+ }
+
+ @Override
+ @NonNull
+ public T invokeAny(@NonNull Collection extends Callable> tasks) throws InterruptedException, ExecutionException {
+ return this.delegate.invokeAny(MetadataCallable.gets(tasks));
+ }
+
+ @Override
+ public T invokeAny(@NonNull Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return this.delegate.invokeAny(MetadataCallable.gets(tasks), timeout, unit);
+ }
+
+ @Override
+ public ExecutorService unWrap() {
+ return this.delegate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MetadataExecutorService that = (MetadataExecutorService) o;
+ return delegate.equals(that.delegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(delegate);
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutors.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutors.java
new file mode 100644
index 000000000..2435b6fec
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataExecutors.java
@@ -0,0 +1,105 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent.executor;
+
+import com.tencent.cloud.metadata.concurrent.MetadataWrap;
+import org.springframework.lang.Nullable;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+
+/**
+ * Util methods for Metadata wrapper of jdk executors.
+ *
+ * @author wlx
+ * @date 2022/7/8 11:58 下午
+ */
+public class MetadataExecutors {
+
+ /**
+ * wrap Executor instance to MetadataExecutorService instance.
+ *
+ * @param executor executor
+ * @return MetadataExecutorService instance
+ */
+ public static Executor getMetadataExecutor(Executor executor) {
+ if (null == executor || isMetadataWrap(executor)) {
+ return executor;
+ }
+ return new MetadataExecutor(executor);
+ }
+
+ /**
+ * wrap ExecutorService instance to MetadataExecutorService instance.
+ *
+ * @param executorService executorService
+ * @return MetadataExecutorService instance
+ */
+ public static ExecutorService getMetadataExecutorService(ExecutorService executorService) {
+ if (null == executorService || isMetadataWrap(executorService)) {
+ return executorService;
+ }
+ return new MetadataExecutorService(executorService);
+ }
+
+ /**
+ * wrap ScheduledExecutorService instance to MetadataScheduledExecutorService instance.
+ *
+ * @param scheduledExecutorService scheduledExecutorService
+ * @return MetadataScheduledExecutorService instance
+ */
+ public static ScheduledExecutorService getMetadataScheduledExecutorService(ScheduledExecutorService
+ scheduledExecutorService) {
+ if (null == scheduledExecutorService || isMetadataWrap(scheduledExecutorService)) {
+ return scheduledExecutorService;
+ }
+ return new MetadataScheduledExecutorService(scheduledExecutorService);
+
+ }
+
+ /**
+ * unwrap to the original/underneath one.
+ *
+ * @param executor input executor
+ * @param Executor type
+ * @return original/underneath one instance.
+ */
+ @SuppressWarnings("unchecked")
+ public static T unwrap(@Nullable T executor) {
+ if (!isMetadataWrap(executor)) {
+ return executor;
+ }
+ return (T) ((MetadataExecutor) executor).unWrap();
+ }
+
+ /**
+ * check the executor is a MetadataExecutor wrapper or not.
+ *
+ * if the parameter executor is MetadataExecutor wrapper, return {@code true}, otherwise {@code false}.
+ *
+ * NOTE: if input executor is {@code null}, return {@code false}.
+ *
+ * @param executor input executor
+ * @param Executor type
+ */
+ public static boolean isMetadataWrap(@Nullable T executor) {
+ return executor instanceof MetadataWrap;
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataScheduledExecutorService.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataScheduledExecutorService.java
new file mode 100644
index 000000000..34c7560f7
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/concurrent/executor/MetadataScheduledExecutorService.java
@@ -0,0 +1,97 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent.executor;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.metadata.concurrent.MetadataCallable;
+import com.tencent.cloud.metadata.concurrent.MetadataRunnable;
+import org.springframework.lang.NonNull;
+
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link MetadataContext} Wrapper of {@link ScheduledExecutorService},
+ * transfer the {@link MetadataContext} from the task submit time of {@link Runnable} or {@link Callable}
+ * to the execution time of {@link Runnable} or {@link Callable}.
+ *
+ * @author wlx
+ * @date 2022/7/8 9:40 下午
+ */
+class MetadataScheduledExecutorService extends MetadataExecutorService
+ implements ScheduledExecutorService {
+
+ private final ScheduledExecutorService delegate;
+
+ public MetadataScheduledExecutorService(ScheduledExecutorService delegate) {
+ super(delegate);
+ this.delegate = delegate;
+ }
+
+ @Override
+ @NonNull
+ public ScheduledFuture> schedule(@NonNull Runnable command, long delay, @NonNull TimeUnit unit) {
+ return this.delegate.schedule(MetadataRunnable.get(command), delay, unit);
+ }
+
+ @Override
+ @NonNull
+ public ScheduledFuture schedule(@NonNull Callable callable, long delay, @NonNull TimeUnit unit) {
+ return this.delegate.schedule(MetadataCallable.get(callable), delay, unit);
+ }
+
+ @Override
+ @NonNull
+ public ScheduledFuture> scheduleAtFixedRate(@NonNull Runnable command, long initialDelay,
+ long period, @NonNull TimeUnit unit) {
+ return this.delegate.scheduleAtFixedRate(MetadataRunnable.get(command), initialDelay, period, unit);
+ }
+
+ @Override
+ @NonNull
+ public ScheduledFuture> scheduleWithFixedDelay(@NonNull Runnable command, long initialDelay,
+ long delay, @NonNull TimeUnit unit) {
+ return this.delegate.scheduleAtFixedRate(MetadataRunnable.get(command), initialDelay, delay, unit);
+ }
+
+ @Override
+ public ScheduledExecutorService unWrap() {
+ return this.delegate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MetadataScheduledExecutorService that = (MetadataScheduledExecutorService) o;
+ return delegate.equals(that.delegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(delegate);
+ }
+}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/MetadataCallableTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/MetadataCallableTest.java
new file mode 100644
index 000000000..2bf827656
--- /dev/null
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/concurrent/MetadataCallableTest.java
@@ -0,0 +1,131 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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 com.tencent.cloud.metadata.concurrent;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import org.assertj.core.api.Assertions;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+/**
+ * Test for {@link MetadataCallable}.
+ *
+ * @author wlx
+ * @date 2022/7/9 12:11 下午
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = RANDOM_PORT,
+ classes = MetadataCallableTest.TestApplication.class,
+ properties = {"spring.config.location = classpath:application-test.yml",
+ "spring.main.web-application-type = servlet",
+ "spring.cloud.gateway.enabled = false"})
+public class MetadataCallableTest {
+
+ private static final ExecutorService executor = Executors.newFixedThreadPool(1);
+
+ @Test
+ public void threadMultiplexingTest() throws InterruptedException, ExecutionException, TimeoutException {
+
+ Future