diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index 13dc4f598..326931d42 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -33,6 +33,7 @@ 1.1.0 1.1.8.6 1.1.1 + 1.0.5 @@ -385,6 +386,11 @@ ${project.version} + + com.alibaba.spring + spring-context-support + ${spring.context.support.version} + @@ -422,6 +428,13 @@ false + + sonatype-nexus + https://oss.sonatype.org/content/repositories/releases + + true + + diff --git a/spring-cloud-alibaba-nacos-config/pom.xml b/spring-cloud-alibaba-nacos-config/pom.xml index cf06182e1..b51f6d9d7 100644 --- a/spring-cloud-alibaba-nacos-config/pom.xml +++ b/spring-cloud-alibaba-nacos-config/pom.xml @@ -19,6 +19,11 @@ nacos-client + + com.alibaba.spring + spring-context-support + + org.springframework.cloud diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java index 0478ac887..0a5005d3a 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java @@ -17,8 +17,11 @@ package com.alibaba.cloud.nacos; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.annotation.PostConstruct; @@ -26,11 +29,13 @@ import com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureExcept import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.spring.util.PropertySourcesUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; @@ -64,6 +69,8 @@ public class NacosConfigProperties { */ public static final String PREFIX = "spring.cloud.nacos.config"; + private static final Pattern PATTERN = Pattern.compile("-(\\w)"); + private static final Logger log = LoggerFactory .getLogger(NacosConfigProperties.class); @@ -80,8 +87,8 @@ public class NacosConfigProperties { String serverAddr = environment .resolvePlaceholders("${spring.cloud.nacos.config.server-addr:}"); if (StringUtils.isEmpty(serverAddr)) { - serverAddr = environment - .resolvePlaceholders("${spring.cloud.nacos.server-addr:}"); + serverAddr = environment.resolvePlaceholders( + "${spring.cloud.nacos.server-addr:localhost:8848}"); } this.setServerAddr(serverAddr); } @@ -357,6 +364,14 @@ public class NacosConfigProperties { this.name = name; } + public Environment getEnvironment() { + return environment; + } + + public void setEnvironment(Environment environment) { + this.environment = environment; + } + /** * @return ConfigService */ @@ -399,9 +414,28 @@ public class NacosConfigProperties { else { properties.put(ENDPOINT, endpoint); } + + enrichNacosConfigProperties(properties); return properties; } + private void enrichNacosConfigProperties(Properties nacosConfigProperties) { + Map properties = PropertySourcesUtils + .getSubProperties((ConfigurableEnvironment) environment, PREFIX); + properties.forEach((k, v) -> nacosConfigProperties.putIfAbsent(resolveKey(k), + String.valueOf(v))); + } + + private String resolveKey(String key) { + Matcher matcher = PATTERN.matcher(key); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(sb, matcher.group(1).toUpperCase()); + } + matcher.appendTail(sb); + return sb.toString(); + } + @Override public String toString() { return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\'' diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java index 560545a28..af51b3aab 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java @@ -62,7 +62,7 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { @Override public PropertySource locate(Environment env) { - + nacosConfigProperties.setEnvironment(env); ConfigService configService = nacosConfigProperties.configServiceInstance(); if (null == configService) { diff --git a/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json index c9c872616..3d322070e 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -3,6 +3,7 @@ { "name": "spring.cloud.nacos.server-addr", "type": "java.lang.String", + "defaultValue": "localhost:8848", "description": "nacos server address." }, { diff --git a/spring-cloud-alibaba-nacos-discovery/pom.xml b/spring-cloud-alibaba-nacos-discovery/pom.xml index 968267718..c87b1793e 100644 --- a/spring-cloud-alibaba-nacos-discovery/pom.xml +++ b/spring-cloud-alibaba-nacos-discovery/pom.xml @@ -19,6 +19,11 @@ nacos-client + + com.alibaba.spring + spring-context-support + + org.springframework.cloud spring-cloud-commons diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java index 7e419ec6f..3fb9dfec1 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java @@ -25,6 +25,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.annotation.PostConstruct; @@ -34,6 +36,7 @@ import com.alibaba.nacos.api.naming.NamingMaintainService; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.client.naming.utils.UtilAndComs; +import com.alibaba.spring.util.PropertySourcesUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.commons.util.InetUtils; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; @@ -66,6 +70,13 @@ public class NacosDiscoveryProperties { private static final Logger log = LoggerFactory .getLogger(NacosDiscoveryProperties.class); + /** + * Prefix of {@link NacosDiscoveryProperties}. + */ + public static final String PREFIX = "spring.cloud.nacos.discovery"; + + private static final Pattern PATTERN = Pattern.compile("-(\\w)"); + /** * nacos discovery server address. */ @@ -430,8 +441,8 @@ public class NacosDiscoveryProperties { String serverAddr = env .resolvePlaceholders("${spring.cloud.nacos.discovery.server-addr:}"); if (StringUtils.isEmpty(serverAddr)) { - serverAddr = env - .resolvePlaceholders("${spring.cloud.nacos.server-addr:}"); + serverAddr = env.resolvePlaceholders( + "${spring.cloud.nacos.server-addr:localhost:8848}"); } this.setServerAddr(serverAddr); } @@ -518,7 +529,26 @@ public class NacosDiscoveryProperties { properties.put(SECRET_KEY, secretKey); properties.put(CLUSTER_NAME, clusterName); properties.put(NAMING_LOAD_CACHE_AT_START, namingLoadCacheAtStart); + + enrichNacosDiscoveryProperties(properties); return properties; } + private void enrichNacosDiscoveryProperties(Properties nacosDiscoveryProperties) { + Map properties = PropertySourcesUtils + .getSubProperties((ConfigurableEnvironment) environment, PREFIX); + properties.forEach((k, v) -> nacosDiscoveryProperties.putIfAbsent(resolveKey(k), + String.valueOf(v))); + } + + private String resolveKey(String key) { + Matcher matcher = PATTERN.matcher(key); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(sb, matcher.group(1).toUpperCase()); + } + matcher.appendTail(sb); + return sb.toString(); + } + } diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 2f0718f95..b3dc069b9 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -2,6 +2,7 @@ { "name": "spring.cloud.nacos.server-addr", "type": "java.lang.String", + "defaultValue": "localhost:8848", "description": "nacos server address." }, { diff --git a/spring-cloud-alibaba-seata/pom.xml b/spring-cloud-alibaba-seata/pom.xml index 5f4a2802d..216008cc0 100644 --- a/spring-cloud-alibaba-seata/pom.xml +++ b/spring-cloud-alibaba-seata/pom.xml @@ -38,6 +38,12 @@ true + + org.springframework.cloud + spring-cloud-loadbalancer + true + + org.springframework.cloud spring-cloud-starter-netflix-ribbon diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataBeanPostProcessor.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataBeanPostProcessor.java index e815a9ec8..e737c0e05 100644 --- a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataBeanPostProcessor.java +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataBeanPostProcessor.java @@ -22,7 +22,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; /** * @author xiaojing */ -final class SeataBeanPostProcessor implements BeanPostProcessor { +public class SeataBeanPostProcessor implements BeanPostProcessor { private final SeataFeignObjectWrapper seataFeignObjectWrapper; diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignBlockingLoadBalancerClient.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignBlockingLoadBalancerClient.java new file mode 100644 index 000000000..3ad4a09ae --- /dev/null +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignBlockingLoadBalancerClient.java @@ -0,0 +1,95 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed 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 + * + * https://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 com.alibaba.cloud.seata.feign; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import feign.Client; +import feign.Request; +import feign.Response; +import io.seata.core.context.RootContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient; +import org.springframework.http.HttpStatus; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * @author yuhuangbin + */ +public class SeataFeignBlockingLoadBalancerClient implements Client { + + private static final Log LOG = LogFactory + .getLog(SeataFeignBlockingLoadBalancerClient.class); + + private final Client delegate; + + private final BlockingLoadBalancerClient loadBalancerClient; + + SeataFeignBlockingLoadBalancerClient(Client delegate, + BlockingLoadBalancerClient loadBalancerClient) { + this.delegate = delegate; + this.loadBalancerClient = loadBalancerClient; + } + + @Override + public Response execute(Request request, Request.Options options) throws IOException { + final URI originalUri = URI.create(request.url()); + String serviceId = originalUri.getHost(); + Assert.state(serviceId != null, + "Request URI does not contain a valid hostname: " + originalUri); + ServiceInstance instance = loadBalancerClient.choose(serviceId); + if (instance == null) { + String message = "Load balancer does not contain an instance for the service " + + serviceId; + if (LOG.isWarnEnabled()) { + LOG.warn(message); + } + return Response.builder().request(request) + .status(HttpStatus.SERVICE_UNAVAILABLE.value()) + .body(message, StandardCharsets.UTF_8).build(); + } + String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri) + .toString(); + Request newRequest = Request.create(request.httpMethod(), reconstructedUrl, + enrichRequstHeader(request.headers()), request.requestBody()); + + return delegate.execute(newRequest, options); + } + + private Map> enrichRequstHeader( + Map> headers) { + String xid = RootContext.getXID(); + if (!StringUtils.isEmpty(xid)) { + Map> newHeaders = new HashMap<>(); + newHeaders.putAll(headers); + newHeaders.put(RootContext.KEY_XID, Arrays.asList(xid)); + return newHeaders; + } + return headers; + } + +} diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java index 95c2a3f8e..58b9ef2c7 100644 --- a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignObjectWrapper.java @@ -16,9 +16,14 @@ package com.alibaba.cloud.seata.feign; +import java.lang.reflect.Field; + import feign.Client; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; @@ -28,12 +33,16 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; */ public class SeataFeignObjectWrapper { + private static final Log LOG = LogFactory.getLog(SeataFeignObjectWrapper.class); + private final BeanFactory beanFactory; private CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory; private SpringClientFactory springClientFactory; + private BlockingLoadBalancerClient loadBalancerClient; + SeataFeignObjectWrapper(BeanFactory beanFactory) { this.beanFactory = beanFactory; } @@ -45,11 +54,41 @@ public class SeataFeignObjectWrapper { return new SeataLoadBalancerFeignClient(client.getDelegate(), factory(), clientFactory(), this.beanFactory); } + if (bean.getClass().getName().equals( + "org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient")) { + return new SeataFeignBlockingLoadBalancerClient(getClient(bean), + loadBalancerClient()); + } return new SeataFeignClient(this.beanFactory, (Client) bean); } return bean; } + private Client getClient(Object bean) { + Field client = null; + boolean oldAccessible = false; + try { + client = bean.getClass().getDeclaredField("delegate"); + oldAccessible = client.isAccessible(); + client.setAccessible(true); + return (Client) client.get(bean); + } + catch (Exception e) { + LOG.error("get delegate client error", e); + } + finally { + client.setAccessible(oldAccessible); + } + return null; + } + + private BlockingLoadBalancerClient loadBalancerClient() { + if (this.loadBalancerClient != null) { + return this.loadBalancerClient; + } + return beanFactory.getBean(BlockingLoadBalancerClient.class); + } + CachingSpringLoadBalancerFactory factory() { if (this.cachingSpringLoadBalancerFactory == null) { this.cachingSpringLoadBalancerFactory = this.beanFactory diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java index cbb63e8ea..e0aa9e741 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java @@ -54,11 +54,8 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties { public void preCheck(String dataSourceName) { if (StringUtils.isEmpty(serverAddr)) { serverAddr = this.getEnv().getProperty( - "spring.cloud.sentinel.datasource.nacos.server-addr", ""); - if (StringUtils.isEmpty(serverAddr)) { - throw new IllegalArgumentException( - "NacosDataSource server-addr is empty"); - } + "spring.cloud.sentinel.datasource.nacos.server-addr", + "localhost:8848"); } }