diff --git a/pom.xml b/pom.xml index b4a9829a6..5f3320a22 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,6 @@ yudao-module-system yudao-module-infra - yudao-spring-boot-starter-env diff --git a/yudao-framework/pom.xml b/yudao-framework/pom.xml index 422a432cb..cd703719c 100644 --- a/yudao-framework/pom.xml +++ b/yudao-framework/pom.xml @@ -11,6 +11,7 @@ pom yudao-common + yudao-spring-boot-starter-env yudao-spring-boot-starter-banner yudao-spring-boot-starter-mybatis yudao-spring-boot-starter-redis diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/grey/GrayLoadBalancer.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/grey/GrayLoadBalancer.java index 1ef2d01ab..452e5fdb0 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/grey/GrayLoadBalancer.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/grey/GrayLoadBalancer.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.gateway.filter.grey; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.gateway.util.EnvUtils; import com.alibaba.cloud.nacos.balancer.NacosBalancer; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -62,7 +63,7 @@ public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer { return new EmptyResponse(); } - // 筛选满足条件的实例列表 + // 筛选满足 version 条件的实例列表 String version = headers.getFirst(VERSION); List chooseInstances; if (StrUtil.isEmpty(version)) { @@ -70,12 +71,41 @@ public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer { } else { chooseInstances = CollectionUtils.filterList(instances, instance -> version.equals(instance.getMetadata().get("version"))); if (CollUtil.isEmpty(chooseInstances)) { - log.warn("[getInstanceResponse][serviceId({}) 没有满足版本的服务实例列表,直接使用所有服务实例列表]", serviceId); + log.warn("[getInstanceResponse][serviceId({}) 没有满足版本({})的服务实例列表,直接使用所有服务实例列表]", serviceId, version); chooseInstances = instances; } } + // 基于 tag 过滤实例列表 + chooseInstances = filterTagServiceInstances(chooseInstances, headers); + // 随机 + 权重获取实例列表 TODO 芋艿:目前直接使用 Nacos 提供的方法,如果替换注册中心,需要重新失败该方法 return new DefaultResponse(NacosBalancer.getHostByRandomWeight3(chooseInstances)); } + + /** + * 基于 tag 请求头,过滤匹配 tag 的服务实例列表 + * + * copy from EnvLoadBalancerClient + * + * @param instances 服务实例列表 + * @param headers 请求头 + * @return 服务实例列表 + */ + private List filterTagServiceInstances(List instances, HttpHeaders headers) { + // 情况一,没有 tag 时,直接返回 + String tag = EnvUtils.getTag(headers); + if (StrUtil.isEmpty(tag)) { + return instances; + } + + // 情况二,有 tag 时,使用 tag 匹配服务实例 + List chooseInstances = CollectionUtils.filterList(instances, instance -> tag.equals(EnvUtils.getTag(instance))); + if (CollUtil.isEmpty(chooseInstances)) { + log.warn("[filterTagServiceInstances][serviceId({}) 没有满足 tag({}) 的服务实例列表,直接使用所有服务实例列表]", serviceId, tag); + chooseInstances = instances; + } + return chooseInstances; + } + } diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/EnvUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/EnvUtils.java new file mode 100644 index 000000000..5f690460b --- /dev/null +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/EnvUtils.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.gateway.util; + +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.http.HttpHeaders; + +import java.util.Objects; + +/** + * 环境 Utils + * + * copy from yudao-spring-boot-starter-env 的 EnvUtils 类 + * + * @author 芋道源码 + */ +public class EnvUtils { + + private static final String HEADER_TAG = "tag"; + + public static final String HOST_NAME_VALUE = "${HOSTNAME}"; + + public static String getTag(HttpHeaders headers) { + String tag = headers.getFirst(HEADER_TAG); + // 如果请求的是 "${HOSTNAME}",则解析成对应的本地主机名 + // 目的:特殊逻辑,解决 IDEA Rest Client 不支持环境变量的读取,所以就服务器来做 + return Objects.equals(tag, HOST_NAME_VALUE) ? getHostName() : tag; + } + + public static String getTag(ServiceInstance instance) { + return instance.getMetadata().get(HEADER_TAG); + } + + public static String getHostName() { + return StrUtil.blankToDefault(NetUtil.getLocalHostName(), IdUtil.fastSimpleUUID()); + } + +} diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java index 031c9a8c1..0553cc3f6 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/WebFrameworkUtils.java @@ -26,9 +26,10 @@ import reactor.core.publisher.Mono; @Slf4j public class WebFrameworkUtils { - @SuppressWarnings("UastIncorrectHttpHeaderInspection") private static final String HEADER_TENANT_ID = "tenant-id"; + private static final String HEADER_TAG = "tag"; + private WebFrameworkUtils() {} /**