✨ Introducing new features. #2207 适配兼容 Spring Cloud 2020 && Spring Boot 2.4

This commit is contained in:
lengleng committed 2020-12-30 11:31:51 +08:00
1 parent a25caeff01
commit bf2ce5cffd
11 files changed
+162 -100

No files matched your search

@@ -27,8 +27,8 @@ import org.springframework.cloud.client.SpringCloudApplication;
* @author lengleng
* @date 2020-02-08 认证授权中心
*/
@SpringCloudApplication
@EnablePigxFeignClients
@SpringCloudApplication
public class PigxAuthApplication {
public static void main(String[] args) {
@@ -33,7 +33,6 @@ import java.lang.annotation.*;
@Documented
@EnableFeignClients
@Import(PigxFeignClientsRegistrar.class)
// 排除 Ribbon (当然也可以排查 maven 依赖)
@EnableAutoConfiguration(excludeName =
"org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration")
public @interface EnablePigxFeignClients {
@@ -18,14 +18,12 @@
package com.pig4cloud.pigx.common.gray;
import com.pig4cloud.pigx.common.gray.feign.GrayFeignRequestInterceptor;
import com.pig4cloud.pigx.common.gray.rule.GrayRibbonLoadBalancerRule;
import com.pig4cloud.pigx.common.gray.rule.GrayLoadBalancerClientConfigurationRegistrar;
import feign.RequestInterceptor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.Import;
/**
* @author lengleng
@@ -33,14 +31,8 @@ import org.springframework.context.annotation.Scope;
*/
@Configuration
@ConditionalOnProperty(value = "gray.rule.enabled", havingValue = "true")
public class GrayRibbonLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public GrayRibbonLoadBalancerRule ribbonLoadBalancerRule() {
return new GrayRibbonLoadBalancerRule();
}
@Import(GrayLoadBalancerClientConfigurationRegistrar.class)
public class GrayLoadBalancerAutoConfiguration {
@Bean
public RequestInterceptor grayFeignRequestInterceptor() {
@@ -0,0 +1,26 @@
package com.pig4cloud.pigx.common.gray.rule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* @author lengleng
* @date 2020/12/29
*/
public class GrayLoadBalancerClientConfiguration extends LoadBalancerClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new GrayRoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
@@ -0,0 +1,39 @@
package com.pig4cloud.pigx.common.gray.rule;
import cn.hutool.core.util.ReflectUtil;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import java.lang.reflect.Field;
/**
* @author lengleng
* @date 2020/11/20
*/
public class GrayLoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(LoadBalancerClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());
}
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Field[] fields = ReflectUtil.getFields(ServiceNameConstants.class);
// 遍历服务名称,注入支持灰度策略的负载均衡器
for (Field field : fields) {
Object fieldValue = ReflectUtil.getFieldValue(ServiceNameConstants.class, field);
registerClientConfiguration(registry, fieldValue, GrayLoadBalancerClientConfiguration.class);
}
}
}
@@ -1,83 +0,0 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.gray.rule;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.util.WebUtils;
import com.pig4cloud.pigx.common.gray.support.NonWebVersionContextHolder;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
/**
* @author lengleng
* @date 2020/1/12
*/
@Slf4j
public class GrayRibbonLoadBalancerRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
public Server choose(ILoadBalancer lb, Object key) {
List<Server> reachableServers = lb.getReachableServers();
// 注册中心无可用实例 抛出异常
if (CollUtil.isEmpty(reachableServers)) {
log.warn("No instance available for {}", key);
return null;
}
// 获取请求version,无则随机返回可用实例
String reqVersion = WebUtils.getRequest() != null ? WebUtils.getRequest().getHeader(CommonConstants.VERSION)
: NonWebVersionContextHolder.getVersion();
if (StrUtil.isBlank(reqVersion)) {
return reachableServers.get(RandomUtil.randomInt(reachableServers.size()));
}
// 遍历可以实例元数据,若匹配则返回此实例
for (Server server : reachableServers) {
NacosServer nacosServer = (NacosServer) server;
Map<String, String> metadata = nacosServer.getMetadata();
String targetVersion = MapUtil.getStr(metadata, CommonConstants.VERSION);
if (reqVersion.equalsIgnoreCase(targetVersion)) {
log.debug("gray requst match success :{} {}", reqVersion, nacosServer);
return nacosServer;
}
}
return reachableServers.get(RandomUtil.randomInt(reachableServers.size()));
}
}
@@ -0,0 +1,84 @@
package com.pig4cloud.pigx.common.gray.rule;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.NacosServiceInstance;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
/**
* @author lengleng
* @date 2020/11/20
*/
@Slf4j
public class GrayRoundRobinLoadBalancer extends RoundRobinLoadBalancer {
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private String serviceId;
/**
* @param serviceInstanceListSupplierProvider a provider of
* {@link ServiceInstanceListSupplier} that will be used to get available instances
* @param serviceId id of the service for which to choose an instance
*/
public GrayRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
super(serviceInstanceListSupplierProvider, serviceId);
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.serviceId = serviceId;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map(serviceInstances -> getInstanceResponse(serviceInstances, request));
}
Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {
// 注册中心无可用实例 抛出异常
if (CollUtil.isEmpty(instances)) {
log.warn("No instance available ");
return new EmptyResponse();
}
DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
RequestData clientRequest = (RequestData) requestContext.getClientRequest();
HttpHeaders headers = clientRequest.getHeaders();
String reqVersion = headers.getFirst(CommonConstants.VERSION);
if (StrUtil.isBlank(reqVersion)) {
return super.choose(request).block();
}
// 遍历可以实例元数据,若匹配则返回此实例
for (ServiceInstance instance : instances) {
NacosServiceInstance nacosInstance = (NacosServiceInstance) instance;
Map<String, String> metadata = nacosInstance.getMetadata();
String targetVersion = MapUtil.getStr(metadata, CommonConstants.VERSION);
if (reqVersion.equalsIgnoreCase(targetVersion)) {
log.debug("gray requst match success :{} {}", reqVersion, nacosInstance);
return new DefaultResponse(nacosInstance);
}
}
// 降级策略,使用轮询策略
return super.choose(request).block();
}
}
@@ -1,2 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pig4cloud.pigx.common.gray.GrayRibbonLoadBalancerConfiguration
com.pig4cloud.pigx.common.gray.GrayLoadBalancerAutoConfiguration
@@ -32,8 +32,8 @@ import org.springframework.cloud.client.SpringCloudApplication;
* 用户统一管理系统
*/
@EnablePigxSwagger2
@SpringCloudApplication
@EnablePigxFeignClients
@SpringCloudApplication
@EnablePigxResourceServer
public class PigxAdminApplication {
@@ -30,8 +30,8 @@ import org.springframework.cloud.client.SpringCloudApplication;
* 支付模块
*/
@EnablePigxSwagger2
@SpringCloudApplication
@EnablePigxFeignClients
@SpringCloudApplication
@EnablePigxResourceServer
public class PigxPayPlatformApplication {
+5
View File
@@ -66,6 +66,11 @@
</properties>
<dependencies>
<!--bootstrap 启动器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--配置文件处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>