unitMap) {
+ String destUnitId = unitMap.get(TencentUnitContext.CLOUD_SPACE_TARGET_UNIT_ID);
+ String destServiceName = unitMap.get(TencentUnitContext.CLOUD_SPACE_TARGET_SERVICE);
+
+ if (StringUtils.isEmpty(destUnitId)) {
+ TencentUnitContext.putSystemTags(unitMap);
+ // 是否跨 cloud gdu 优先访问,此时没有目标单元
+ String grayUnitInfo = unitMap.get(TencentUnitContext.CLOUD_SPACE_GRAY_UNIT_INFO);
+ // 设置灰度上游
+ if (StringUtils.isNotEmpty(grayUnitInfo)) {
+ TencentUnitContext.putSourceTag(TencentUnitContext.CLOUD_SPACE_GRAY_UNIT_INFO, grayUnitInfo);
+ }
+ SpringCloudUnitUtils.preRequestRecordUnitContext(destServiceName);
+ }
+ else {
+ String destSystemName = unitMap.get(TencentUnitContext.CLOUD_SPACE_TARGET_SYSTEM);
+
+ // 在当前 cloud, check ns
+ UnitNamespace namespace = TencentUnitManager.getUnitNamespaceId(destUnitId, destSystemName);
+ if (namespace == null) {
+ String msg = String.format("[getProxyInfoFromUnitHeader] check ns error, destUnitId:%s, destServiceName:%s",
+ destUnitId, destServiceName);
+ LOGGER.error(msg);
+ throw new TencentUnitException(ErrorCode.COMMON_PARAMETER_ERROR, msg);
+ }
+ TencentUnitContext.putSystemTags(unitMap);
+ TencentUnitContext.putRouteTag(TencentUnitContext.CLOUD_SPACE_ROUTE_TARGET_NAMESPACE_ID, namespace.getId());
+ }
+ }
+
+ /**
+ * 【网关使用该方法】通过http直接调用网关,从header获取 cid 并解析.
+ */
+ public static void processFromCid(String cid, String path) {
+
+ //url:/groupContext/system/ms/api
+ String serviceName;
+ String systemName;
+ String[] pathSegments = path.split("/");
+ if (pathSegments.length >= 5) {
+ serviceName = pathSegments[3];
+ systemName = pathSegments[2];
+ if (StringUtils.isEmpty(serviceName) || StringUtils.isEmpty(systemName)) {
+ String msg = String.format("[checkUnitFromHttp] serviceName:%s or systemName:%s is empty", serviceName, systemName);
+ throw new TencentUnitException(ErrorCode.COMMON_PARAMETER_ERROR, msg);
+ }
+ }
+ else {
+ String msg = "[checkUnitFromHttp] path pattern is wrong, path:" + path;
+ throw new TencentUnitException(ErrorCode.COMMON_PARAMETER_ERROR, msg);
+ }
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_CUSTOMER_IDENTIFIER, cid);
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_SYSTEM, systemName);
+
+ SpringCloudUnitUtils.preRequestRecordUnitContext(serviceName);
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("[getProxyInfoFromCid] parse unit from cid. path:{}, unit context:{}",
+ path, TencentUnitContext.getOriginCompositeContextMap());
+ }
+ }
+
+ private static boolean checkUnitIdAndSetContext(RoutingUnit routingUnit, String system, String customerIdentifier, String serviceName) {
+ if (routingUnit == null || StringUtils.isEmpty(routingUnit.getUnitId())) {
+ String msg = String.format("[preRequestRecordUnitContext] routingUnit unit id not exist, customerIdentifier:%s, system:%s, serviceName:%s", customerIdentifier, system, serviceName);
+ LOGGER.error(msg);
+ throw new TencentUnitException(ErrorCode.COMMON_PARAMETER_ERROR, msg);
+ }
+
+ return checkUnitIdAndSetContext(routingUnit.getUnitId(), system, serviceName);
+ }
+
+ private static boolean checkUnitIdAndSetContext(String unitId, String system, String serviceName) {
+ if (TencentUnitUtils.checkLocalCloudSpace(unitId)) {
+ // 本地,检查 ns 是否存在
+ UnitNamespace namespace = TencentUnitManager.getUnitNamespaceId(unitId, system);
+ if (namespace != null) {
+ setUnitContext(unitId, namespace, serviceName);
+ // 设置治理 ns
+ MetadataContext metadataContext = MetadataContextHolder.get();
+ metadataContext.putFragmentContext(MetadataContext.FRAGMENT_APPLICATION_NONE,
+ MetadataConstant.POLARIS_TARGET_NAMESPACE, namespace.getId());
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else if (TencentUnitManager.checkUnitId(unitId)) {
+ // 异地,检查 unitId 是否存在
+ setUnitContext(unitId, serviceName);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ private static void setUnitContext(String unitId, String serviceName) {
+ setUnitContext(unitId, null, serviceName);
+ }
+
+ private static void setUnitContext(String unitId, UnitNamespace namespace, String serviceName) {
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_UNIT_ID, unitId);
+ if (namespace != null) {
+ TencentUnitContext.putRouteTag(TencentUnitContext.CLOUD_SPACE_ROUTE_TARGET_NAMESPACE_ID, namespace.getId());
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_NAMESPACE_ID, namespace.getId());
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_NAMESPACE_NAME, namespace.getName());
+ }
+
+ // 不同 cloud, 需要转到对应 gateway
+ if (!TencentUnitUtils.checkLocalCloudSpace(unitId)) {
+ Gateway gateway = getGateway(unitId);
+
+ TencentUnitContext.putRouteTag(TencentUnitContext.CLOUD_SPACE_ROUTE_GATEWAY, gateway);
+
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_SERVICE, serviceName);
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_CLOUD, TencentUnitManager.getUnitCloudMap()
+ .get(unitId));
+ }
+ }
+
+ /**
+ * 设置跨 cloud gdu 的上下文, 然后再 gdu first 访问,暂不确定目标单元.
+ */
+ private static void setCrossGduContext(String unitId, String serviceName) {
+ Gateway gateway = getGateway(unitId);
+ TencentUnitContext.putRouteTag(TencentUnitContext.CLOUD_SPACE_ROUTE_GATEWAY, gateway);
+
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_SERVICE, serviceName);
+ TencentUnitContext.putSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_CLOUD, TencentUnitManager.getUnitCloudMap()
+ .get(unitId));
+ }
+
+ private static Gateway getGateway(String unitId) {
+ return getGateway(unitId, TencentUnitContext.getSystemTag(TencentUnitContext.CLOUD_SPACE_TARGET_SYSTEM));
+ }
+
+ private static Gateway getGateway(String unitId, String system) {
+ String targetGatewayCloudId = TencentUnitManager.getUnitCloudMap().get(unitId);
+ if (!EnvironmentUtils.isGateway()) {
+ targetGatewayCloudId = TencentUnitManager.getLocalCloudSpaceId();
+ }
+ Gateway result = TencentUnitManager.getBusinessSystemGateway(targetGatewayCloudId, system);
+ if (result == null) {
+ String msg = String.format("[getGateway] gateway not exist, targetGatewayCloudId:%s, unitId:%s, system:%s", targetGatewayCloudId, unitId, system);
+ LOGGER.error(msg);
+ throw new TencentUnitException(ErrorCode.COMMON_PARAMETER_ERROR, msg);
+ }
+ return result;
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/EnableTsfUnit.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/EnableTsfUnit.java
similarity index 85%
rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/EnableTsfUnit.java
rename to spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/EnableTsfUnit.java
index cb89dfb6f..0da583fdc 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/EnableTsfUnit.java
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/EnableTsfUnit.java
@@ -25,17 +25,14 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Empty annotation. Compatible with old versions TSF SDK.
- *
- * Deprecated since 2.0.0.0.
- *
- * @author Haotian Zhang
- */
-@Deprecated
+import com.tencent.cloud.plugin.unit.config.UnitAutoConfiguration;
+
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
+@ImportAutoConfiguration({UnitAutoConfiguration.class})
public @interface EnableTsfUnit {
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCall.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCall.java
similarity index 89%
rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCall.java
rename to spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCall.java
index 4272d6b72..2dd0eb857 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCall.java
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCall.java
@@ -24,14 +24,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Empty annotation. Compatible with old versions TSF SDK.
- *
- * Deprecated since 2.0.0.0.
- *
- * @author Haotian Zhang
- */
-@Deprecated
+
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCustomerIdentifier.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCustomerIdentifier.java
similarity index 88%
rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCustomerIdentifier.java
rename to spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCustomerIdentifier.java
index 68ec64412..a18318bf7 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCustomerIdentifier.java
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitCustomerIdentifier.java
@@ -24,14 +24,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Empty annotation. Compatible with old versions TSF SDK.
- *
- * Deprecated since 2.0.0.0.
- *
- * @author Haotian Zhang
- */
-@Deprecated
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitLocalCall.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitLocalCall.java
similarity index 100%
rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitLocalCall.java
rename to spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/annotation/TsfUnitLocalCall.java
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/aspect/TsfUnitRouteAspect.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/aspect/TsfUnitRouteAspect.java
new file mode 100644
index 000000000..4c5be9318
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/aspect/TsfUnitRouteAspect.java
@@ -0,0 +1,108 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 Tencent. 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.tsf.unit.aspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import com.tencent.tsf.unit.annotation.TsfUnitCall;
+import com.tencent.tsf.unit.annotation.TsfUnitCustomerIdentifier;
+import com.tencent.tsf.unit.core.utils.TencentUnitUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 拦截单元化相关的注解.
+ */
+@Aspect
+public class TsfUnitRouteAspect {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TsfUnitRouteAspect.class);
+
+ public TsfUnitRouteAspect() {
+ LOG.info("init TsfUnitRouteAspect");
+ }
+
+ // 拦截@FeignClient+@TsfUnitCall的对象
+ @Pointcut("@within(com.tencent.tsf.unit.annotation.TsfUnitCall) &&" +
+ "@within(org.springframework.cloud.openfeign.FeignClient)")
+ public void unitRoutePointcut() {
+ }
+
+ @Around("unitRoutePointcut()")
+ public Object unitRouteProcess(ProceedingJoinPoint joinPoint) throws Throwable {
+ try {
+ // 获取拦截点(pointcut)的函数签名
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ // 获取拦截点的类名
+ String pointClassName = methodSignature.getDeclaringTypeName();
+ // 获取拦截点所属类名的Class
+ Class> pointCutClass = Class.forName(pointClassName);
+ // 获取拦截点所在类Class上的注解,获取本地调用注解@TsfUnitCall
+ TsfUnitCall tsfUnitCallClass = pointCutClass.getAnnotation(TsfUnitCall.class);
+
+ // 获取单元化注解上的systemName
+ String systemName = tsfUnitCallClass.systemName();
+ boolean global = tsfUnitCallClass.global();
+ // 获取在拦截点对象上执行的方法
+ Method pointCutMethod = methodSignature.getMethod();
+ // 获取方法上所有参数的注解,找到@TsfUnitUserTag的修饰的参数,作为用户标签(客户号)取出来
+ String cid = getCustomerIdentifier(pointCutMethod.getParameterAnnotations(), joinPoint.getArgs());
+
+ // 写入Context
+ TencentUnitUtils.putCustomerTags(systemName, cid, global);
+
+ return joinPoint.proceed();
+ }
+ catch (Throwable e) {
+ LOG.error("[TsfUnitRoute] aspect catch exception: " + e.getMessage());
+ throw e;
+ }
+ }
+
+ // 获取参数里面里有userTag相关注解的参数
+ private String getCustomerIdentifier(Annotation[][] annotations, Object[] args) {
+ // 找到TsfUnitUserTag注解的位置
+ int index = -1;
+ for (int i = 0; i < annotations.length; i++) {
+ for (Annotation annotation : annotations[i]) {
+ if (annotation.annotationType() == TsfUnitCustomerIdentifier.class) {
+ index = i;
+ break;
+ }
+ }
+ if (index >= 0) {
+ break;
+ }
+ }
+
+ if (index >= 0 && index < args.length) {
+ Object object = args[index];
+ if (object.getClass().equals(String.class)) {
+ return (String) object;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/Env.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/Env.java
new file mode 100644
index 000000000..ff9e97f0f
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/Env.java
@@ -0,0 +1,69 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 Tencent. 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.tsf.unit.core;
+
+import com.tencent.polaris.api.utils.IPAddressUtils;
+
+public final class Env {
+
+ private Env() {
+ }
+
+ private final static String consulToken;
+
+ private final static String consulHost;
+
+ private final static Integer consulPort;
+
+ private final static String namespaceId;
+
+ static {
+ // 只支持从环境变量取
+ consulHost = IPAddressUtils.getIpCompatible(getSystemProperty("tsf_consul_ip", "localhost"));
+ consulPort = Integer.parseInt(getSystemProperty("tsf_consul_port", "8500"));
+ consulToken = getSystemProperty("tsf_token", "");
+ namespaceId = getSystemProperty("tsf_namespace_id", "");
+ }
+
+ private static String getSystemProperty(String name, String defaultValue) {
+ String val = null;
+ if (System.getenv(name) != null) {
+ val = System.getenv(name);
+ }
+ if (System.getProperty(name) != null) {
+ val = System.getProperty(name);
+ }
+ return (val == null) ? defaultValue : val;
+ }
+
+ public static String getConsulToken() {
+ return consulToken;
+ }
+
+ public static String getConsulHost() {
+ return consulHost;
+ }
+
+ public static Integer getConsulPort() {
+ return consulPort;
+ }
+
+ public static String getNamespaceId() {
+ return namespaceId;
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/GatewayUnitArchCallback.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/GatewayUnitArchCallback.java
new file mode 100644
index 000000000..9ae9cf456
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/GatewayUnitArchCallback.java
@@ -0,0 +1,90 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 Tencent. 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.tsf.unit.core;
+
+import java.util.Map;
+import java.util.Objects;
+
+import com.tencent.polaris.api.utils.StringUtils;
+import com.tencent.tsf.unit.core.model.UnitArch.Gateway;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GatewayUnitArchCallback implements IUnitChangeCallback {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GatewayUnitArchCallback.class);
+ private final String applicationName;
+
+ public GatewayUnitArchCallback(String applicationName) {
+ this.applicationName = applicationName;
+ }
+
+ @Override
+ public void callback() {
+ boolean enableUnit = false;
+ for (Map.Entry entry : TencentUnitManager.getLocalBusinessSystemGwMap().entrySet()) {
+ Gateway gateway = entry.getValue();
+ if (StringUtils.equals(applicationName, gateway.getServiceName())
+ && StringUtils.equals(Env.getNamespaceId(), gateway.getNamespaceId())) {
+ enableUnit = true;
+ break;
+ }
+ }
+ if (!enableUnit) {
+ for (Map.Entry entry: TencentUnitManager.getLocalOnlyGwMap().entrySet()) {
+ Gateway gateway = entry.getValue();
+ if (StringUtils.equals(applicationName, gateway.getServiceName())
+ && StringUtils.equals(Env.getNamespaceId(), gateway.getNamespaceId())) {
+ enableUnit = true;
+ break;
+ }
+ }
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("[callback] enableUnit:{}, applicationName:{}, ns:{}, localBusinessSystemGwMap:{}, localOnlyGwMap:{}",
+ enableUnit, applicationName, Env.getNamespaceId(), TencentUnitManager.getLocalBusinessSystemGwMap(), TencentUnitManager.getLocalOnlyGwMap());
+ }
+ if (TencentUnitManager.isEnable() != enableUnit) {
+ LOGGER.info("[callback] unit enable change, new status:{}", enableUnit);
+ }
+ TencentUnitManager.setEnable(enableUnit);
+ }
+
+ @Override
+ public String getName() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof GatewayUnitArchCallback)) {
+ return false;
+ }
+ GatewayUnitArchCallback that = (GatewayUnitArchCallback) o;
+ return StringUtils.equals(applicationName, that.applicationName) && StringUtils.equals(getName(), that.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getName(), applicationName);
+ }
+
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/IUnitChangeCallback.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/IUnitChangeCallback.java
new file mode 100644
index 000000000..89db39e8d
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/IUnitChangeCallback.java
@@ -0,0 +1,26 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 Tencent. 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.tsf.unit.core;
+
+public interface IUnitChangeCallback {
+
+ void callback();
+
+ String getName();
+
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/MappingServiceLoader.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/MappingServiceLoader.java
new file mode 100644
index 000000000..8285d12a7
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/MappingServiceLoader.java
@@ -0,0 +1,54 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 Tencent. 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.tsf.unit.core;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import com.tencent.tsf.unit.core.mapping.api.IMappingService;
+import com.tencent.tsf.unit.core.mapping.impl.CustomerMappingService;
+
+/**
+ * 找到MappingService的实现.
+ */
+public final class MappingServiceLoader {
+
+ private MappingServiceLoader() {
+ }
+
+ private static IMappingService service;
+
+ static {
+ ServiceLoader mappingServices = ServiceLoader.load(IMappingService.class);
+ if (mappingServices != null) {
+ Iterator itr = mappingServices.iterator();
+ while (itr.hasNext()) {
+ service = itr.next();
+ }
+ }
+
+ // 默认实现
+ if (service == null) {
+ service = new CustomerMappingService();
+ }
+ }
+
+ public static IMappingService getService() {
+ return service;
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/TencentUnitContext.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/TencentUnitContext.java
new file mode 100644
index 000000000..034ac32ed
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-unit-plugin/src/main/java/com/tencent/tsf/unit/core/TencentUnitContext.java
@@ -0,0 +1,549 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 Tencent. 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.tsf.unit.core;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.common.util.MetadataContextUtils;
+import com.tencent.polaris.api.utils.StringUtils;
+import com.tencent.polaris.metadata.core.MetadataObjectValue;
+import com.tencent.polaris.metadata.core.MetadataType;
+import com.tencent.tsf.unit.core.model.UnitRouteInfo.GrayMatchRouteUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class TencentUnitContext {
+
+ /**
+ * 内部的 key 都以 CloudSpace 开头.
+ */
+ public static final String CLOUD_SPACE_TARGET_UNIT_ID = "CloudSpaceTargetUnitId";
+ /**
+ * 是否限定转发到 gdu 服务,true/false.
+ */
+ public static final String CLOUD_SPACE_GDU_FORWARD_ONLY = "CloudSpaceGduFrowardOnly";
+ /**
+ * 是否限定转发到 sdu 服务,true/false,内部变量,可能会调整.
+ */
+ public static final String CLOUD_SPACE_SDU_FORWARD_ONLY = "CloudSpaceSduFrowardOnly";
+ /**
+ * CLOUD_SPACE_GDU_INSTANCE_EXIST.
+ */
+ public static final String CLOUD_SPACE_GDU_INSTANCE_EXIST = "CloudSpaceGduInstanceExist";
+ /**
+ * 目标 cloud space,用于判断是否转发到下一个网关,网关时判断.
+ */
+ public static final String CLOUD_SPACE_TARGET_CLOUD = "CloudSpaceTargetCloud";
+ /**
+ * 目标 ns id,如果是 gdu forward only,这里的目标 ns 即 gdu ns.
+ */
+ public static final String CLOUD_SPACE_TARGET_NAMESPACE_ID = "CloudSpaceTargetNamespaceId";
+ /**
+ * CLOUD_SPACE_TARGET_NAMESPACE_NAME.
+ */
+ public static final String CLOUD_SPACE_TARGET_NAMESPACE_NAME = "CloudSpaceTargetNamespaceName";
+ /**
+ * 客户要素传递.
+ */
+ public static final String CLOUD_SPACE_CUSTOMER_IDENTIFIER = "CloudSpaceCustomerIdentifier";
+ /**
+ * 业务系统传递.
+ */
+ public static final String CLOUD_SPACE_TARGET_SYSTEM = "CloudSpaceTargetSystem";
+ /**
+ * gw转发时用, 目标 ms.
+ */
+ public static final String CLOUD_SPACE_TARGET_SERVICE = "CloudSpaceTargetService";
+ /**
+ * 灰度单元信息,进入了灰度单元,将 GrayMatchRouteUnit list 的 json 序列化设置进去.
+ */
+ public static final String CLOUD_SPACE_GRAY_UNIT_INFO = "CloudSpaceGrayUnitInfo";
+ /**
+ * GrayCloudSpaceGduUnitId.
+ */
+ public static final String GRAY_CLOUD_SPACE_GDU_UNIT_ID = "GrayCloudSpaceGduUnitId";
+ /**
+ * GrayCloudSpaceSduUnitId.
+ */
+ public static final String GRAY_CLOUD_SPACE_SDU_UNIT_ID = "GrayCloudSpaceSduUnitId";
+ /**
+ * 根据目标客户要素计算出的目标客户号.
+ */
+ public static final String CLOUD_SPACE_TARGET_CUSTOMER_NUMBER = "CloudSpaceTargetCustomerNumber";
+ /**
+ * 根据目标客户号计算出的ShardingKey,后续通过这个ShardingKey可以算出路由到目标单元号.
+ */
+ public static final String CLOUD_SPACE_TARGET_SHARDING_KEY = "CloudSpaceTargetShardingKey";
+ /**
+ * 路由上下文标签,路由网关,如果 value 不为空,则需要转发到网关.
+ */
+ public static final String CLOUD_SPACE_ROUTE_GATEWAY = "CloudSpaceRouteGateway";
+ /**
+ * CloudSpaceRouteTargetNamespaceId.
+ */
+ public static final String CLOUD_SPACE_ROUTE_TARGET_NAMESPACE_ID = "CloudSpaceRouteTargetNamespaceId";
+ /**
+ * 需要从 user context 转移到 system context 的 key.
+ */
+ public static final Set CLOUD_SPACE_SYSTEM_FROM_USER_KEYS = new HashSet<>(
+ Arrays.asList(CLOUD_SPACE_TARGET_SYSTEM, CLOUD_SPACE_CUSTOMER_IDENTIFIER, CLOUD_SPACE_TARGET_UNIT_ID));
+ /**
+ * SOURCE_PREFIX.
+ */
+ public static final String SOURCE_PREFIX = "source.";
+ /**
+ * GRAY_PREFIX.
+ */
+ public static final String GRAY_PREFIX = "gray.";
+ /**
+ * USER_CONTEXTS_KEY.
+ */
+ private static final String USER_CONTEXTS_KEY = "USER_CONTEXTS";
+ /**
+ * SYSTEM_CONTEXTS_KEY.
+ */
+ private static final String SYSTEM_CONTEXTS_KEY = "SYSTEM_CONTEXTS";
+ /**
+ * ROUTE_CONTEXTS_KEY.
+ */
+ private static final String ROUTE_CONTEXTS_KEY = "ROUTE_CONTEXTS";
+ /**
+ * SOURCE_CONTEXTS_KEY.
+ */
+ private static final String SOURCE_CONTEXTS_KEY = "SOURCE_CONTEXTS";
+ /**
+ * GRAY_USER_CONTEXTS_KEY.
+ */
+ private static final String GRAY_USER_CONTEXTS_KEY = "GRAY_USER_CONTEXTS";
+ /**
+ * GRAY_SYSTEM_CONTEXTS_KEY.
+ */
+ private static final String GRAY_SYSTEM_CONTEXTS_KEY = "GRAY_SYSTEM_CONTEXTS";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TencentUnitContext.class);
+
+ private static Map getUserContext() {
+ MetadataObjectValue