feat:optimize project structure and checkstyle

pull/56/head
SkyeBeFreeman 3 years ago
parent efc8a7e555
commit baabb69be3

@ -0,0 +1,28 @@
root = true
[*.java]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.groovy]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.xml]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.yml]
indent_style = space
indent_size = 2
[*.yaml]
indent_style = space
indent_size = 2
[*.sh]
indent_style = space
indent_size = 4

@ -38,193 +38,233 @@
</scm> </scm>
<modules> <modules>
<module>spring-cloud-tencent-dependencies</module> <module>spring-cloud-tencent-polaris-context</module>
<module>spring-cloud-tencent-starters</module> <module>spring-cloud-tencent-commons</module>
<module>spring-cloud-tencent-examples</module> <module>spring-cloud-tencent-metadata</module>
<module>spring-cloud-tencent-coverage</module> <module>spring-cloud-starter-tencent-polaris-discovery</module>
</modules> <module>spring-cloud-starter-tencent-polaris-ratelimit</module>
<module>spring-cloud-starter-tencent-polaris-circuitbreaker</module>
<developers> <module>spring-cloud-starter-tencent-polaris-router</module>
<developer> <module>spring-cloud-tencent-dependencies</module>
<id>SkyeBeFreeman</id> <module>spring-cloud-tencent-examples</module>
<name>Haotian Zhang</name> <module>spring-cloud-tencent-coverage</module>
<email>928016560@qq.com</email> </modules>
<organization>Tencent</organization>
<url>https://github.com/SkyeBeFreeman/</url> <developers>
</developer> <developer>
<id>SkyeBeFreeman</id>
<developer> <name>Haotian Zhang</name>
<name>Andrew Shan</name> <email>928016560@qq.com</email>
<email>samshan08@126.com</email> <organization>Tencent</organization>
<organization>Tencent</organization> <url>https://github.com/SkyeBeFreeman/</url>
</developer> </developer>
<developer> <developer>
<id>xiaoyao1999hn</id> <name>Andrew Shan</name>
<name>Jie Cheng</name> <email>samshan08@126.com</email>
<email>348893717@qq.com</email> <organization>Tencent</organization>
<organization>Tencent</organization> </developer>
<url>https://github.com/xiaoyao1999hn/</url>
</developer> <developer>
</developers> <id>xiaoyao1999hn</id>
<name>Jie Cheng</name>
<properties> <email>348893717@qq.com</email>
<!-- Project revision --> <organization>Tencent</organization>
<revision>1.2.0.Hoxton.SR9-SNAPSHOT</revision> <url>https://github.com/xiaoyao1999hn/</url>
</developer>
<!-- Spring Cloud --> </developers>
<spring.cloud.version>Hoxton.SR9</spring.cloud.version>
<properties>
<!-- Dependencies --> <!-- Project revision -->
<logback.version>1.2.7</logback.version> <revision>1.2.0.Hoxton.SR9-SNAPSHOT</revision>
<!-- Maven Plugin Versions --> <!-- Spring Cloud -->
<jacoco.version>0.8.3</jacoco.version> <spring.cloud.version>Hoxton.SR9</spring.cloud.version>
<maven-source-plugin.version>3.2.0</maven-source-plugin.version>
<flatten-maven-plugin.version>1.2.7</flatten-maven-plugin.version> <!-- Dependencies -->
</properties> <logback.version>1.2.7</logback.version>
<dependencyManagement> <!-- Maven Plugin Versions -->
<dependencies> <jacoco.version>0.8.3</jacoco.version>
<!-- Spring Boot Dependencies --> <maven-source-plugin.version>3.2.0</maven-source-plugin.version>
<dependency> <flatten-maven-plugin.version>1.2.7</flatten-maven-plugin.version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId> <!-- Checkstyle -->
<version>${spring-boot.version}</version> <maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError>
<type>pom</type> <maven-checkstyle-plugin.failsOnViolation>true</maven-checkstyle-plugin.failsOnViolation>
<scope>import</scope> <maven-checkstyle-plugin.includeTestSourceDirectory>true
</dependency> </maven-checkstyle-plugin.includeTestSourceDirectory>
</properties>
<!-- Spring Cloud Dependencies -->
<dependency> <dependencyManagement>
<groupId>org.springframework.cloud</groupId> <dependencies>
<artifactId>spring-cloud-dependencies</artifactId> <!-- Spring Cloud Dependencies -->
<version>${spring.cloud.version}</version> <dependency>
<type>pom</type> <groupId>org.springframework.cloud</groupId>
<scope>import</scope> <artifactId>spring-cloud-dependencies</artifactId>
</dependency> <version>${spring.cloud.version}</version>
<type>pom</type>
<!-- Spring Cloud Tencent Dependencies --> <scope>import</scope>
<dependency> </dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-dependencies</artifactId> <!-- Spring Cloud Tencent Dependencies -->
<version>${revision}</version> <dependency>
<type>pom</type> <groupId>com.tencent.cloud</groupId>
<scope>import</scope> <artifactId>spring-cloud-tencent-dependencies</artifactId>
</dependency> <version>${revision}</version>
<type>pom</type>
<dependency> <scope>import</scope>
<groupId>ch.qos.logback</groupId> </dependency>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version> <dependency>
</dependency> <groupId>ch.qos.logback</groupId>
</dependencies> <artifactId>logback-classic</artifactId>
</dependencyManagement> <version>${logback.version}</version>
</dependency>
<build> </dependencies>
<pluginManagement> </dependencyManagement>
<plugins>
<plugin> <build>
<groupId>org.jacoco</groupId> <pluginManagement>
<artifactId>jacoco-maven-plugin</artifactId> <plugins>
<version>${jacoco.version}</version> <plugin>
</plugin> <groupId>org.jacoco</groupId>
</plugins> <artifactId>jacoco-maven-plugin</artifactId>
</pluginManagement> <version>${jacoco.version}</version>
<plugins> </plugin>
<plugin> </plugins>
<groupId>org.apache.maven.plugins</groupId> </pluginManagement>
<artifactId>maven-compiler-plugin</artifactId> <plugins>
<inherited>true</inherited> <plugin>
<configuration> <groupId>io.spring.javaformat</groupId>
<source>1.8</source> <artifactId>spring-javaformat-maven-plugin</artifactId>
<target>1.8</target> </plugin>
<parameters>true</parameters> <plugin>
</configuration> <groupId>org.apache.maven.plugins</groupId>
</plugin> <artifactId>maven-checkstyle-plugin</artifactId>
<plugin> </plugin>
<groupId>org.apache.maven.plugins</groupId> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <groupId>org.apache.maven.plugins</groupId>
<inherited>true</inherited> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <inherited>true</inherited>
<forkCount>1</forkCount> <configuration>
<reuseForks>false</reuseForks> <source>1.8</source>
</configuration> <target>1.8</target>
</plugin> <parameters>true</parameters>
<plugin> </configuration>
<groupId>org.codehaus.mojo</groupId> </plugin>
<artifactId>flatten-maven-plugin</artifactId> <plugin>
<version>${flatten-maven-plugin.version}</version> <groupId>org.jacoco</groupId>
<configuration> <artifactId>jacoco-maven-plugin</artifactId>
<updatePomFile>true</updatePomFile> <version>${jacoco.version}</version>
<flattenMode>resolveCiFriendliesOnly</flattenMode> <executions>
</configuration> <execution>
<executions> <id>jacoco-initialize</id>
<execution> <goals>
<id>flatten</id> <goal>prepare-agent</goal>
<phase>process-resources</phase> </goals>
<goals> </execution>
<goal>flatten</goal> <execution>
</goals> <id>jacoco-site</id>
</execution> <phase>test</phase>
<execution> <goals>
<id>flatten.clean</id> <goal>report</goal>
<phase>clean</phase> </goals>
<goals> </execution>
<goal>clean</goal> </executions>
</goals> </plugin>
</execution> <plugin>
</executions> <groupId>org.apache.maven.plugins</groupId>
</plugin> <artifactId>maven-surefire-plugin</artifactId>
</plugins> <inherited>true</inherited>
</build> <configuration>
<forkCount>1</forkCount>
<profiles> <reuseForks>false</reuseForks>
<profile> </configuration>
<id>release</id> </plugin>
<build> <plugin>
<plugins> <groupId>org.codehaus.mojo</groupId>
<plugin> <artifactId>flatten-maven-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId> <version>${flatten-maven-plugin.version}</version>
<artifactId>maven-javadoc-plugin</artifactId> <configuration>
<executions> <updatePomFile>true</updatePomFile>
<execution> <flattenMode>resolveCiFriendliesOnly</flattenMode>
<id>attach-javadocs</id> </configuration>
<goals> <executions>
<goal>jar</goal> <execution>
</goals> <id>flatten</id>
</execution> <phase>process-resources</phase>
</executions> <goals>
</plugin> <goal>flatten</goal>
</goals>
<plugin> </execution>
<groupId>org.apache.maven.plugins</groupId> <execution>
<artifactId>maven-source-plugin</artifactId> <id>flatten.clean</id>
<version>${maven-source-plugin.version}</version> <phase>clean</phase>
<executions> <goals>
<execution> <goal>clean</goal>
<phase>package</phase> </goals>
<goals> </execution>
<goal>jar-no-fork</goal> </executions>
</goals> </plugin>
</execution> </plugins>
</executions> </build>
</plugin>
<reporting>
<plugin> <plugins>
<groupId>org.apache.maven.plugins</groupId> <plugin>
<artifactId>maven-gpg-plugin</artifactId> <groupId>org.apache.maven.plugins</groupId>
<version>${maven-gpg-plugin.version}</version> <artifactId>maven-checkstyle-plugin</artifactId>
<executions> </plugin>
<execution> </plugins>
<phase>verify</phase> </reporting>
<goals>
<goal>sign</goal> <profiles>
</goals> <profile>
</execution> <id>release</id>
</executions> <build>
</plugin> <plugins>
</plugins> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build> </build>
<distributionManagement> <distributionManagement>

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-tencent-polaris-circuitbreaker</artifactId>
<name>Spring Cloud Starter Tencent Polaris Circuitbreaker</name>
<dependencies>
<!-- Spring Cloud Tencent dependencies start -->
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-commons</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-metadata</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-context</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-discovery-factory</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-circuitbreaker-factory</artifactId>
</dependency>
<!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -17,8 +17,6 @@
package com.tencent.cloud.polaris.circuitbreaker; package com.tencent.cloud.polaris.circuitbreaker;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
import com.tencent.cloud.common.constant.ContextConstant.ModifierOrder; import com.tencent.cloud.common.constant.ContextConstant.ModifierOrder;
import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor; import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor;
import com.tencent.cloud.polaris.context.PolarisConfigModifier; import com.tencent.cloud.polaris.context.PolarisConfigModifier;
@ -27,6 +25,7 @@ import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory; import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import com.tencent.polaris.factory.config.ConfigurationImpl; import com.tencent.polaris.factory.config.ConfigurationImpl;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -35,47 +34,51 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
/** /**
* Configuration for Polaris {@link feign.Feign} which can automatically bring in the call results for reporting * Configuration for Polaris {@link feign.Feign} which can automatically bring in the call
* results for reporting.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@ConditionalOnProperty( @ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled",
value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
havingValue = "true",
matchIfMissing = true)
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(PolarisContextConfiguration.class) @AutoConfigureAfter(PolarisContextConfiguration.class)
@AutoConfigureBefore(FeignAutoConfiguration.class) @AutoConfigureBefore(FeignAutoConfiguration.class)
public class PolarisFeignClientAutoConfiguration { public class PolarisFeignClientAutoConfiguration {
@Bean @Bean
public ConsumerAPI consumerAPI(SDKContext context) { public ConsumerAPI consumerAPI(SDKContext context) {
return DiscoveryAPIFactory.createConsumerAPIByContext(context); return DiscoveryAPIFactory.createConsumerAPIByContext(context);
} }
@Bean
@Order(HIGHEST_PRECEDENCE)
public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor(
ConsumerAPI consumerAPI) {
return new PolarisFeignBeanPostProcessor(consumerAPI);
}
@Bean
public CircuitBreakerConfigModifier circuitBreakerConfigModifier() {
return new CircuitBreakerConfigModifier();
}
@Bean public static class CircuitBreakerConfigModifier implements PolarisConfigModifier {
@Order(value = HIGHEST_PRECEDENCE)
public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) {
return new PolarisFeignBeanPostProcessor(consumerAPI);
}
@Bean @Override
public CircuitBreakerConfigModifier circuitBreakerConfigModifier() { public void modify(ConfigurationImpl configuration) {
return new CircuitBreakerConfigModifier(); // 开启熔断配置
} configuration.getConsumer().getCircuitBreaker().setEnable(true);
}
public static class CircuitBreakerConfigModifier implements PolarisConfigModifier { @Override
public int getOrder() {
return ModifierOrder.CIRCUIT_BREAKER_ORDER;
}
@Override }
public void modify(ConfigurationImpl configuration) {
//开启熔断配置
configuration.getConsumer().getCircuitBreaker().setEnable(true);
}
@Override
public int getOrder() {
return ModifierOrder.CIRCUIT_BREAKER_ORDER;
}
}
} }

@ -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.polaris.circuitbreaker.feign;
import com.tencent.polaris.api.core.ConsumerAPI;
import feign.Client;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
/**
* Wrap Spring Bean and decorating proxy for Feign Client.
*
* @author Haotian Zhang
*/
public class PolarisFeignBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
private final ConsumerAPI consumerAPI;
private BeanFactory factory;
public PolarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) {
this.consumerAPI = consumerAPI;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return wrapper(bean);
}
private Object wrapper(Object bean) {
if (isNeedWrap(bean)) {
if (bean instanceof LoadBalancerFeignClient) {
LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
return new PolarisLoadBalancerFeignClient(
createPolarisFeignClient(client.getDelegate()), factory(),
clientFactory());
}
if (bean instanceof FeignBlockingLoadBalancerClient) {
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) bean;
return new PolarisFeignBlockingLoadBalancerClient(
createPolarisFeignClient(client.getDelegate()),
factory.getBean(BlockingLoadBalancerClient.class));
}
return createPolarisFeignClient((Client) bean);
}
return bean;
}
private boolean isNeedWrap(Object bean) {
return bean instanceof Client && !(bean instanceof PolarisFeignClient)
&& !(bean instanceof PolarisFeignBlockingLoadBalancerClient)
&& !(bean instanceof PolarisLoadBalancerFeignClient);
}
private PolarisFeignClient createPolarisFeignClient(Client delegate) {
return new PolarisFeignClient(delegate, consumerAPI);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
CachingSpringLoadBalancerFactory factory() {
return this.factory.getBean(CachingSpringLoadBalancerFactory.class);
}
SpringClientFactory clientFactory() {
return this.factory.getBean(SpringClientFactory.class);
}
}

@ -18,19 +18,21 @@
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.polaris.circuitbreaker.feign;
import feign.Client; import feign.Client;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient; import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient; import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
/** /**
* Wrap for {@link FeignBlockingLoadBalancerClient} * Wrap for {@link FeignBlockingLoadBalancerClient}.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient { public class PolarisFeignBlockingLoadBalancerClient
extends FeignBlockingLoadBalancerClient {
public PolarisFeignBlockingLoadBalancerClient(Client delegate, public PolarisFeignBlockingLoadBalancerClient(Client delegate,
BlockingLoadBalancerClient loadBalancerClient) { BlockingLoadBalancerClient loadBalancerClient) {
super(delegate, loadBalancerClient); super(delegate, loadBalancerClient);
} }
} }

@ -0,0 +1,104 @@
/*
* 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.polaris.circuitbreaker.feign;
import java.io.IOException;
import java.net.URI;
import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey;
import com.tencent.cloud.metadata.context.MetadataContext;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.Client;
import feign.Request;
import feign.Request.Options;
import feign.Response;
import org.apache.commons.lang.StringUtils;
import static feign.Util.checkNotNull;
/**
* Wrap for {@link Client}.
*
* @author Haotian Zhang
*/
public class PolarisFeignClient implements Client {
private final Client delegate;
private final ConsumerAPI consumerAPI;
public PolarisFeignClient(Client target, ConsumerAPI consumerAPI) {
this.delegate = checkNotNull(target, "target");
this.consumerAPI = checkNotNull(consumerAPI, "CircuitBreakAPI");
}
@Override
public Response execute(Request request, Options options) throws IOException {
final ServiceCallResult resultRequest = createServiceCallResult(request);
try {
Response response = delegate.execute(request, options);
// HTTP code greater than 400 is an exception
if (response.status() >= 400) {
resultRequest.setRetStatus(RetStatus.RetFail);
}
return response;
}
catch (IOException origin) {
resultRequest.setRetStatus(RetStatus.RetFail);
throw origin;
}
finally {
consumerAPI.updateServiceCallResult(resultRequest);
}
}
private ServiceCallResult createServiceCallResult(final Request request) {
ServiceCallResult resultRequest = new ServiceCallResult();
MetadataContext metadataContext = MetadataContextHolder.get();
String namespace = metadataContext
.getSystemMetadata(SystemMetadataKey.PEER_NAMESPACE);
resultRequest.setNamespace(namespace);
String serviceName = metadataContext
.getSystemMetadata(SystemMetadataKey.PEER_SERVICE);
resultRequest.setService(serviceName);
String method = metadataContext.getSystemMetadata(SystemMetadataKey.PEER_PATH);
resultRequest.setMethod(method);
resultRequest.setRetStatus(RetStatus.RetSuccess);
String sourceNamespace = metadataContext
.getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE);
String sourceService = metadataContext
.getSystemMetadata(SystemMetadataKey.LOCAL_SERVICE);
if (StringUtils.isNotBlank(sourceNamespace)
&& StringUtils.isNotBlank(sourceService)) {
resultRequest
.setCallerService(new ServiceKey(sourceNamespace, sourceService));
}
URI uri = URI.create(request.url());
resultRequest.setHost(uri.getHost());
resultRequest.setPort(uri.getPort());
return resultRequest;
}
}

@ -18,20 +18,22 @@
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.polaris.circuitbreaker.feign;
import feign.Client; import feign.Client;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
/** /**
* Wrap for {@link LoadBalancerFeignClient} * Wrap for {@link LoadBalancerFeignClient}.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisLoadBalancerFeignClient extends LoadBalancerFeignClient { public class PolarisLoadBalancerFeignClient extends LoadBalancerFeignClient {
public PolarisLoadBalancerFeignClient(Client delegate, public PolarisLoadBalancerFeignClient(Client delegate,
CachingSpringLoadBalancerFactory lbClientFactory, CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) { SpringClientFactory clientFactory) {
super(delegate, lbClientFactory, clientFactory); super(delegate, lbClientFactory, clientFactory);
} }
} }

@ -1,2 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.circuitbreaker.PolarisFeignClientAutoConfiguration com.tencent.cloud.polaris.circuitbreaker.PolarisFeignClientAutoConfiguration

@ -22,6 +22,7 @@ import feign.Client;
import org.junit.Test; import org.junit.Test;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -33,30 +34,32 @@ import org.springframework.test.context.junit4.SpringRunner;
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = TestPolarisFeignApp.class) @SpringBootTest(classes = TestPolarisFeignApp.class)
@ContextConfiguration(classes = {PolarisFeignClientAutoConfiguration.class}) @ContextConfiguration(classes = { PolarisFeignClientAutoConfiguration.class })
public class PolarisFeignClientTest { public class PolarisFeignClientTest {
@Autowired @Autowired
private ApplicationContext springCtx; private ApplicationContext springCtx;
@Test @Test
public void testPolarisFeignBeanPostProcessor() { public void testPolarisFeignBeanPostProcessor() {
final PolarisFeignBeanPostProcessor postProcessor = springCtx.getBean(PolarisFeignBeanPostProcessor.class); final PolarisFeignBeanPostProcessor postProcessor = springCtx
Assertions.assertNotNull(postProcessor, "PolarisFeignBeanPostProcessor"); .getBean(PolarisFeignBeanPostProcessor.class);
} Assertions.assertNotNull(postProcessor, "PolarisFeignBeanPostProcessor");
}
@Test
public void testFeignClient() { @Test
final Client client = springCtx.getBean(Client.class); public void testFeignClient() {
if (client instanceof PolarisFeignClient) { final Client client = springCtx.getBean(Client.class);
return; if (client instanceof PolarisFeignClient) {
} return;
if (client instanceof PolarisLoadBalancerFeignClient) { }
return; if (client instanceof PolarisLoadBalancerFeignClient) {
} return;
if (client instanceof PolarisFeignBlockingLoadBalancerClient) { }
return; if (client instanceof PolarisFeignBlockingLoadBalancerClient) {
} return;
throw new IllegalStateException("Polaris burying failed"); }
} throw new IllegalStateException("Polaris burying failed");
}
} }

@ -17,7 +17,6 @@
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.polaris.circuitbreaker.feign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
@ -32,32 +31,30 @@ import org.springframework.web.bind.annotation.GetMapping;
@EnableFeignClients @EnableFeignClients
public class TestPolarisFeignApp { public class TestPolarisFeignApp {
@Autowired public static void main(String[] args) {
TestPolarisService service; SpringApplication.run(TestPolarisFeignApp.class);
}
public static void main(String[] args) { @FeignClient(name = "feign-service-polaris",
SpringApplication.run(TestPolarisFeignApp.class); fallback = TestPolarisServiceFallback.class)
} public interface TestPolarisService {
@FeignClient(name = "feign-service-polaris", fallback = TestPolarisServiceFallback.class) /**
public interface TestPolarisService { * Get info of service B.
*/
@GetMapping("/example/service/b/info")
String info();
/** }
* B
*
* @return
*/
@GetMapping("/example/service/b/info")
String info();
} @Component
public static class TestPolarisServiceFallback implements TestPolarisService {
@Component @Override
public static class TestPolarisServiceFallback implements TestPolarisService { public String info() {
return "trigger the refuse";
}
}
@Override
public String info() {
return "trigger the refuse";
}
}
} }

@ -12,4 +12,4 @@ feign:
mime-types: text/xml,application/xml,application/json #指定压缩的请求数据类型 mime-types: text/xml,application/xml,application/json #指定压缩的请求数据类型
min-request-size: 2048 #超过该大小的请求会被压缩 min-request-size: 2048 #超过该大小的请求会被压缩
response: response:
enabled: false #是否对响应进行GZIP压缩 enabled: false #是否对响应进行GZIP压缩

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>spring-cloud-tencent-starters</artifactId> <artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<version>${revision}</version> <version>${revision}</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
<name>Spring Cloud Starter Tencent Polaris Discovery</name> <name>Spring Cloud Starter Tencent Polaris Discovery</name>
<dependencies> <dependencies>
<!-- Spring Cloud Tencent dependencies start --> <!-- Spring Cloud Tencent dependencies start -->
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-commons</artifactId> <artifactId>spring-cloud-tencent-commons</artifactId>
</dependency> </dependency>
<dependency> <dependency>

@ -0,0 +1,231 @@
/*
* 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.polaris;
import javax.annotation.PostConstruct;
import org.apache.commons.lang.StringUtils;
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.Environment;
/**
* Properties for Polaris.
*
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
@ConfigurationProperties("spring.cloud.polaris.discovery")
public class PolarisProperties {
/**
* The polaris authentication token.
*/
private String token;
/**
* Namespace, separation registry of different environments.
*/
@Value("${spring.cloud.polaris.discovery.namespace:#{'default'}}")
private String namespace;
/**
* Service name to registry.
*/
@Value("${spring.cloud.polaris.discovery.service:${spring.application.name:}}")
private String service;
/**
* Load balance weight.
*/
@Value("${spring.cloud.polaris.discovery.weight:#{100}}")
private float weight;
/**
* Version number.
*/
private String version;
/**
* Protocol name such as http, https.
*/
@Value("${spring.cloud.polaris.discovery.protocol:http}")
private String protocol;
/**
* Port of instance.
*/
@Value("${server.port:}")
private int port;
/**
* Ip address to be registered.
*/
private String ipAddress;
/**
* If instance registered.
*/
@Value("${spring.cloud.polaris.discovery.register.enabled:#{true}}")
private Boolean registerEnabled;
/**
* If heartbeat enabled.
*/
@Value("${spring.cloud.polaris.discovery.heartbeat.enabled:#{true}}")
private Boolean heartbeatEnabled = true;
/**
* Custom health check url to override default.
*/
@Value("${spring.cloud.polaris.discovery.health-check-url:}")
private String healthCheckUrl;
@Autowired
private Environment environment;
public PolarisProperties(InetUtils inetUtils) {
if (inetUtils != null) {
this.ipAddress = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
}
}
/**
* Init properties.
*/
@PostConstruct
public void init() {
if (StringUtils.isEmpty(this.getNamespace())) {
this.setNamespace(environment
.resolvePlaceholders("${spring.cloud.polaris.discovery.namespace:}"));
}
if (StringUtils.isEmpty(this.getService())) {
this.setService(environment
.resolvePlaceholders("${spring.cloud.polaris.discovery.service:}"));
}
if (StringUtils.isEmpty(this.getToken())) {
this.setToken(environment
.resolvePlaceholders("${spring.cloud.polaris.discovery.token:}"));
}
}
public boolean isHeartbeatEnabled() {
if (null == heartbeatEnabled) {
return false;
}
return heartbeatEnabled;
}
public void setHeartbeatEnabled(Boolean heartbeatEnabled) {
this.heartbeatEnabled = heartbeatEnabled;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
public String getService() {
return service;
}
public void setService(String service) {
this.service = service;
}
public boolean isRegisterEnabled() {
return registerEnabled;
}
public void setRegisterEnabled(boolean registerEnabled) {
this.registerEnabled = registerEnabled;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getHealthCheckUrl() {
return healthCheckUrl;
}
public void setHealthCheckUrl(String healthCheckUrl) {
this.healthCheckUrl = healthCheckUrl;
}
@Override
public String toString() {
return "PolarisProperties{" + "token='" + token + '\'' + ", namespace='"
+ namespace + '\'' + ", service='" + service + '\'' + ", weight=" + weight
+ ", version='" + version + '\'' + ", protocol='" + protocol + '\''
+ ", port=" + port + ", ipAddress='" + ipAddress + '\''
+ ", registerEnabled=" + registerEnabled + ", heartbeatEnabled="
+ heartbeatEnabled + ", healthCheckUrl=" + healthCheckUrl
+ ", environment=" + environment + '}';
}
}

@ -35,18 +35,16 @@ import java.lang.annotation.Target;
@Inherited @Inherited
public @interface PolarisClient { public @interface PolarisClient {
/** /**
* service * Service name of instance.
* * @return service
* @return service */
*/ String service() default "";
String service() default "";
/** /**
* namespace * Namespace name of instance.
* * @return namespace
* @return namespace */
*/ String namespace() default "";
String namespace() default "";
} }

@ -21,6 +21,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled; import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
@ -28,9 +29,10 @@ import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
* @author Haotian Zhang, Andrew Shan, Jie Cheng * @author Haotian Zhang, Andrew Shan, Jie Cheng
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD}) @Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnDiscoveryEnabled @ConditionalOnDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.polaris.discovery.enabled", matchIfMissing = true) @ConditionalOnProperty(value = "spring.cloud.polaris.discovery.enabled",
matchIfMissing = true)
public @interface ConditionalOnPolarisDiscoveryEnabled { public @interface ConditionalOnPolarisDiscoveryEnabled {
} }

@ -24,6 +24,7 @@ import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory; import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -37,36 +38,41 @@ import org.springframework.context.annotation.Import;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisDiscoveryEnabled @ConditionalOnPolarisDiscoveryEnabled
@Import({PolarisDiscoveryClientConfiguration.class, PolarisReactiveDiscoveryClientConfiguration.class}) @Import({ PolarisDiscoveryClientConfiguration.class,
PolarisReactiveDiscoveryClientConfiguration.class })
public class PolarisDiscoveryAutoConfiguration { public class PolarisDiscoveryAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PolarisProperties polarisDiscoveryProperties(InetUtils inetUtils) { public PolarisProperties polarisDiscoveryProperties(InetUtils inetUtils) {
return new PolarisProperties(inetUtils); return new PolarisProperties(inetUtils);
} }
@Bean(name = "polarisProvider")
@ConditionalOnMissingBean
public ProviderAPI polarisProvider(SDKContext polarisContext)
throws PolarisException {
return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext);
}
@Bean(name = "polarisProvider") @Bean(name = "polarisConsumer")
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ProviderAPI polarisProvider(SDKContext polarisContext) throws PolarisException { public ConsumerAPI polarisConsumer(SDKContext polarisContext)
return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext); throws PolarisException {
} return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext);
}
@Bean(name = "polarisConsumer") @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ConsumerAPI polarisConsumer(SDKContext polarisContext) throws PolarisException { public PolarisDiscoveryHandler polarisDiscoveryHandler() {
return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext); return new PolarisDiscoveryHandler();
} }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PolarisDiscoveryHandler polarisDiscoveryHandler() { public PolarisServiceDiscovery polarisServiceDiscovery(
return new PolarisDiscoveryHandler(); PolarisDiscoveryHandler polarisDiscoveryHandler) {
} return new PolarisServiceDiscovery(polarisDiscoveryHandler);
}
@Bean
@ConditionalOnMissingBean
public PolarisServiceDiscovery polarisServiceDiscovery(PolarisDiscoveryHandler polarisDiscoveryHandler) {
return new PolarisServiceDiscovery(polarisDiscoveryHandler);
}
} }

@ -18,6 +18,7 @@
package com.tencent.cloud.polaris.discovery; package com.tencent.cloud.polaris.discovery;
import java.util.List; import java.util.List;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.DiscoveryClient;
@ -28,30 +29,30 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
*/ */
public class PolarisDiscoveryClient implements DiscoveryClient { public class PolarisDiscoveryClient implements DiscoveryClient {
/** /**
* Polaris Discovery Client Description. * Polaris Discovery Client Description.
*/ */
public final String description = "Spring Cloud Polaris Discovery Client"; public final String description = "Spring Cloud Polaris Discovery Client";
private final PolarisServiceDiscovery polarisServiceDiscovery; private final PolarisServiceDiscovery polarisServiceDiscovery;
public PolarisDiscoveryClient(PolarisServiceDiscovery polarisServiceDiscovery) { public PolarisDiscoveryClient(PolarisServiceDiscovery polarisServiceDiscovery) {
this.polarisServiceDiscovery = polarisServiceDiscovery; this.polarisServiceDiscovery = polarisServiceDiscovery;
} }
@Override @Override
public String description() { public String description() {
return description; return description;
} }
@Override @Override
public List<ServiceInstance> getInstances(String service) { public List<ServiceInstance> getInstances(String service) {
return polarisServiceDiscovery.getInstances(service); return polarisServiceDiscovery.getInstances(service);
} }
@Override @Override
public List<String> getServices() { public List<String> getServices() {
return polarisServiceDiscovery.getServices(); return polarisServiceDiscovery.getServices();
} }
} }

@ -33,14 +33,15 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnBlockingDiscoveryEnabled @ConditionalOnBlockingDiscoveryEnabled
@AutoConfigureBefore({SimpleDiscoveryClientAutoConfiguration.class, @AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class}) CommonsClientAutoConfiguration.class })
@AutoConfigureAfter(PolarisDiscoveryAutoConfiguration.class) @AutoConfigureAfter(PolarisDiscoveryAutoConfiguration.class)
public class PolarisDiscoveryClientConfiguration { public class PolarisDiscoveryClientConfiguration {
@Bean @Bean
public DiscoveryClient polarisDiscoveryClient(PolarisServiceDiscovery polarisServiceDiscovery) { public DiscoveryClient polarisDiscoveryClient(
return new PolarisDiscoveryClient(polarisServiceDiscovery); PolarisServiceDiscovery polarisServiceDiscovery) {
} return new PolarisDiscoveryClient(polarisServiceDiscovery);
}
} }

@ -0,0 +1,113 @@
/*
* 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.polaris.discovery;
import java.util.Map;
import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.PolarisProperties;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.pojo.ServiceInfo;
import com.tencent.polaris.api.rpc.GetAllInstancesRequest;
import com.tencent.polaris.api.rpc.GetInstancesRequest;
import com.tencent.polaris.api.rpc.GetServicesRequest;
import com.tencent.polaris.api.rpc.InstancesResponse;
import com.tencent.polaris.api.rpc.ServicesResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Discovery Handler for Polaris.
*
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
@Component
public class PolarisDiscoveryHandler {
@Autowired
private PolarisProperties polarisProperties;
@Autowired
private ProviderAPI providerAPI;
@Autowired
private ConsumerAPI polarisConsumer;
/**
* Get a list of instances after service routing.
* @param service service name
* @return list of instances
*/
public InstancesResponse getFilteredInstances(String service) {
String namespace = polarisProperties.getNamespace();
GetInstancesRequest getInstancesRequest = new GetInstancesRequest();
getInstancesRequest.setNamespace(namespace);
getInstancesRequest.setService(service);
String method = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.PEER_PATH);
getInstancesRequest.setMethod(method);
String localNamespace = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE);
String localService = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.LOCAL_SERVICE);
Map<String, String> allTransitiveCustomMetadata = MetadataContextHolder.get()
.getAllTransitiveCustomMetadata();
if (StringUtils.isNotBlank(localNamespace) || StringUtils.isNotBlank(localService)
|| null != allTransitiveCustomMetadata) {
ServiceInfo sourceService = new ServiceInfo();
sourceService.setNamespace(localNamespace);
sourceService.setService(localService);
sourceService.setMetadata(allTransitiveCustomMetadata);
getInstancesRequest.setServiceInfo(sourceService);
}
return polarisConsumer.getInstances(getInstancesRequest);
}
/**
* Return all instances for the given service.
* @param service serviceName
* @return list of instances
*/
public InstancesResponse getInstances(String service) {
String namespace = polarisProperties.getNamespace();
GetAllInstancesRequest request = new GetAllInstancesRequest();
request.setNamespace(namespace);
request.setService(service);
return polarisConsumer.getAllInstance(request);
}
public ProviderAPI getProviderAPI() {
return providerAPI;
}
/**
* Return all service for given namespace.
* @return service list
*/
public ServicesResponse GetServices() {
String namespace = polarisProperties.getNamespace();
GetServicesRequest request = new GetServicesRequest();
request.setNamespace(namespace);
return polarisConsumer.getServices(request);
}
}

@ -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.polaris.discovery;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import com.tencent.cloud.common.pojo.PolarisServiceInstance;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInfo;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.rpc.InstancesResponse;
import org.springframework.cloud.client.ServiceInstance;
/**
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
public class PolarisServiceDiscovery {
private final PolarisDiscoveryHandler polarisDiscoveryHandler;
public PolarisServiceDiscovery(PolarisDiscoveryHandler polarisDiscoveryHandler) {
this.polarisDiscoveryHandler = polarisDiscoveryHandler;
}
/**
* Return all instances for the given service.
* @param serviceId id of service
* @return list of instances
* @throws PolarisException polarisException
*/
public List<ServiceInstance> getInstances(String serviceId) throws PolarisException {
List<ServiceInstance> instances = new ArrayList<>();
InstancesResponse filteredInstances = polarisDiscoveryHandler
.getFilteredInstances(serviceId);
ServiceInstances serviceInstances = filteredInstances.toServiceInstances();
for (Instance instance : serviceInstances.getInstances()) {
instances.add(new PolarisServiceInstance(instance));
}
return instances;
}
/**
* Return the names of all services.
* @return list of service names
* @throws PolarisException polarisException
*/
public List<String> getServices() throws PolarisException {
return polarisDiscoveryHandler.GetServices().getServices().stream()
.map(ServiceInfo::getService).collect(Collectors.toList());
}
}

@ -0,0 +1,88 @@
/*
* 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.polaris.discovery.reactive;
import java.util.function.Function;
import com.tencent.cloud.polaris.discovery.PolarisServiceDiscovery;
import com.tencent.polaris.api.exception.PolarisException;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
/**
* Reactive Discovery Client for Polaris.
*
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
public class PolarisReactiveDiscoveryClient implements ReactiveDiscoveryClient {
private static final Logger log = LoggerFactory
.getLogger(PolarisReactiveDiscoveryClient.class);
private PolarisServiceDiscovery polarisServiceDiscovery;
public PolarisReactiveDiscoveryClient(
PolarisServiceDiscovery polarisServiceDiscovery) {
this.polarisServiceDiscovery = polarisServiceDiscovery;
}
@Override
public String description() {
return "Spring Cloud Polaris Reactive Discovery Client";
}
@Override
public Flux<ServiceInstance> getInstances(String serviceId) {
return Mono.justOrEmpty(serviceId).flatMapMany(loadInstancesFromPolaris())
.subscribeOn(Schedulers.boundedElastic());
}
private Function<String, Publisher<ServiceInstance>> loadInstancesFromPolaris() {
return serviceId -> {
try {
return Flux.fromIterable(polarisServiceDiscovery.getInstances(serviceId));
}
catch (PolarisException e) {
log.error("get service instance[{}] from polaris error!", serviceId, e);
return Flux.empty();
}
};
}
@Override
public Flux<String> getServices() {
return Flux.defer(() -> {
try {
return Flux.fromIterable(polarisServiceDiscovery.getServices());
}
catch (Exception e) {
log.error("get services from polaris server fail,", e);
return Flux.empty();
}
}).subscribeOn(Schedulers.boundedElastic());
}
}

@ -19,6 +19,7 @@ package com.tencent.cloud.polaris.discovery.reactive;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisServiceDiscovery; import com.tencent.cloud.polaris.discovery.PolarisServiceDiscovery;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -35,16 +36,16 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnReactiveDiscoveryEnabled @ConditionalOnReactiveDiscoveryEnabled
@AutoConfigureAfter({PolarisDiscoveryAutoConfiguration.class, @AutoConfigureAfter({ PolarisDiscoveryAutoConfiguration.class,
ReactiveCompositeDiscoveryClientAutoConfiguration.class}) ReactiveCompositeDiscoveryClientAutoConfiguration.class })
@AutoConfigureBefore({ReactiveCommonsClientAutoConfiguration.class}) @AutoConfigureBefore({ ReactiveCommonsClientAutoConfiguration.class })
public class PolarisReactiveDiscoveryClientConfiguration { public class PolarisReactiveDiscoveryClientConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PolarisReactiveDiscoveryClient polarisReactiveDiscoveryClient( public PolarisReactiveDiscoveryClient polarisReactiveDiscoveryClient(
PolarisServiceDiscovery polarisServiceDiscovery) { PolarisServiceDiscovery polarisServiceDiscovery) {
return new PolarisReactiveDiscoveryClient(polarisServiceDiscovery); return new PolarisReactiveDiscoveryClient(polarisServiceDiscovery);
} }
} }

@ -0,0 +1,98 @@
/*
* 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.polaris.registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils;
/**
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
public class PolarisAutoServiceRegistration
extends AbstractAutoServiceRegistration<Registration> {
private static final Logger log = LoggerFactory
.getLogger(PolarisAutoServiceRegistration.class);
private final PolarisRegistration registration;
public PolarisAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
PolarisRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
@Override
protected PolarisRegistration getRegistration() {
if (this.registration.getPort() <= 0) {
this.registration.setPort(this.getPort().get());
}
return this.registration;
}
@Override
protected PolarisRegistration getManagementRegistration() {
return null;
}
@Override
protected void register() {
if (!this.registration.getPolarisProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() <= 0) {
this.registration.setPort(getPort().get());
}
super.register();
}
@Override
protected void registerManagement() {
if (!this.registration.getPolarisProperties().isRegisterEnabled()) {
return;
}
super.registerManagement();
}
@Override
protected Object getConfiguration() {
return this.registration.getPolarisProperties();
}
@Override
protected boolean isEnabled() {
return this.registration.getPolarisProperties().isRegisterEnabled();
}
@Override
@SuppressWarnings("deprecation")
protected String getAppName() {
String appName = registration.getPolarisProperties().getService();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
}
}

@ -0,0 +1,93 @@
/*
* 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.polaris.registry;
import java.net.URI;
import java.util.Map;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.PolarisProperties;
import com.tencent.polaris.client.api.SDKContext;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
/**
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
public class PolarisRegistration implements Registration, ServiceInstance {
private final PolarisProperties polarisProperties;
private final SDKContext polarisContext;
public PolarisRegistration(PolarisProperties polarisProperties, SDKContext context) {
this.polarisProperties = polarisProperties;
this.polarisContext = context;
}
@Override
public String getServiceId() {
return polarisProperties.getService();
}
@Override
public String getHost() {
if (StringUtils.isNotBlank(polarisProperties.getIpAddress())) {
return polarisProperties.getIpAddress();
}
return polarisContext.getConfig().getGlobal().getAPI().getBindIP();
}
@Override
public int getPort() {
return polarisProperties.getPort();
}
public void setPort(int port) {
this.polarisProperties.setPort(port);
}
@Override
public boolean isSecure() {
return StringUtils.equalsIgnoreCase(polarisProperties.getProtocol(), "https");
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map<String, String> getMetadata() {
return MetadataContextHolder.get().getAllSystemMetadata();
}
public PolarisProperties getPolarisProperties() {
return polarisProperties;
}
@Override
public String toString() {
return "PolarisRegistration{" + "polarisProperties=" + polarisProperties
+ ", polarisContext=" + polarisContext + '}';
}
}

@ -0,0 +1,213 @@
/*
* 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.polaris.registry;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.tencent.cloud.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.polaris.PolarisProperties;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.rpc.InstanceDeregisterRequest;
import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest;
import com.tencent.polaris.api.rpc.InstanceRegisterRequest;
import com.tencent.polaris.api.rpc.InstancesResponse;
import com.tencent.polaris.client.util.NamedThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils;
import static org.springframework.util.ReflectionUtils.rethrowRuntimeException;
/**
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
private static final Logger log = LoggerFactory
.getLogger(PolarisServiceRegistry.class);
private static final int ttl = 5;
private final PolarisProperties polarisProperties;
private final PolarisDiscoveryHandler polarisDiscoveryHandler;
private final MetadataLocalProperties metadataLocalProperties;
private final ScheduledExecutorService heartbeatExecutor;
public PolarisServiceRegistry(PolarisProperties polarisProperties,
PolarisDiscoveryHandler polarisDiscoveryHandler,
MetadataLocalProperties metadataLocalProperties) {
this.polarisProperties = polarisProperties;
this.polarisDiscoveryHandler = polarisDiscoveryHandler;
this.metadataLocalProperties = metadataLocalProperties;
if (polarisProperties.isHeartbeatEnabled()) {
ScheduledThreadPoolExecutor heartbeatExecutor = new ScheduledThreadPoolExecutor(
0, new NamedThreadFactory("spring-cloud-heartbeat"));
heartbeatExecutor.setMaximumPoolSize(1);
this.heartbeatExecutor = heartbeatExecutor;
}
else {
this.heartbeatExecutor = null;
}
}
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for polaris client...");
return;
}
// 注册实例
InstanceRegisterRequest instanceRegisterRequest = new InstanceRegisterRequest();
instanceRegisterRequest.setNamespace(polarisProperties.getNamespace());
instanceRegisterRequest.setService(registration.getServiceId());
instanceRegisterRequest.setHost(registration.getHost());
instanceRegisterRequest.setPort(registration.getPort());
instanceRegisterRequest.setToken(polarisProperties.getToken());
if (null != heartbeatExecutor) {
instanceRegisterRequest.setTtl(ttl);
}
instanceRegisterRequest.setMetadata(metadataLocalProperties.getContent());
instanceRegisterRequest.setProtocol(polarisProperties.getProtocol());
instanceRegisterRequest.setVersion(polarisProperties.getVersion());
try {
ProviderAPI providerClient = polarisDiscoveryHandler.getProviderAPI();
providerClient.register(instanceRegisterRequest);
log.info("polaris registry, {} {} {}:{} {} register finished",
polarisProperties.getNamespace(), registration.getServiceId(),
registration.getHost(), registration.getPort(),
metadataLocalProperties.getContent());
if (null != heartbeatExecutor) {
InstanceHeartbeatRequest heartbeatRequest = new InstanceHeartbeatRequest();
BeanUtils.copyProperties(instanceRegisterRequest, heartbeatRequest);
// 注册成功后开始启动心跳线程
heartbeat(heartbeatRequest);
}
}
catch (Exception e) {
log.error("polaris registry, {} register failed...{},",
registration.getServiceId(), registration, e);
rethrowRuntimeException(e);
}
}
@Override
public void deregister(Registration registration) {
log.info("De-registering from Polaris Server now...");
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No dom to de-register for polaris client...");
return;
}
InstanceDeregisterRequest deRegisterRequest = new InstanceDeregisterRequest();
deRegisterRequest.setToken(polarisProperties.getToken());
deRegisterRequest.setNamespace(polarisProperties.getNamespace());
deRegisterRequest.setService(registration.getServiceId());
deRegisterRequest.setHost(registration.getHost());
deRegisterRequest.setPort(registration.getPort());
try {
ProviderAPI providerClient = polarisDiscoveryHandler.getProviderAPI();
providerClient.deRegister(deRegisterRequest);
}
catch (Exception e) {
log.error("ERR_POLARIS_DEREGISTER, de-register failed...{},", registration,
e);
}
finally {
if (null != heartbeatExecutor) {
heartbeatExecutor.shutdown();
}
}
log.info("De-registration finished.");
}
@Override
public void close() {
}
@Override
public void setStatus(Registration registration, String status) {
}
@Override
public Object getStatus(Registration registration) {
String serviceName = registration.getServiceId();
InstancesResponse instancesResponse = polarisDiscoveryHandler
.getInstances(serviceName);
Instance[] instances = instancesResponse.getInstances();
if (null == instances || instances.length == 0) {
return null;
}
for (Instance instance : instances) {
if (instance.getHost().equalsIgnoreCase(registration.getHost())
&& instance.getPort() == polarisProperties.getPort()) {
return instance.isHealthy() ? "UP" : "DOWN";
}
}
return null;
}
/**
* Start the heartbeat thread.
* @param heartbeatRequest heartbeat request
*/
public void heartbeat(InstanceHeartbeatRequest heartbeatRequest) {
heartbeatExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
// String healthCheckUrl = String.format("http://%s:%s%s",
// heartbeatRequest.getHost(), heartbeatRequest.getPort(),
// polarisProperties.getHealthCheckUrl());
// //先判断是否配置了health-check-url如果配置了需要先进行服务实例健康检查如果健康检查通过则进行心跳上报如果不通过则不上报心跳
// if (Strings.isNotEmpty(healthCheckUrl) &&
// !OkHttpUtil.get(healthCheckUrl, null)) {
// log.error("polaris health check failed");
// return;
// }
polarisDiscoveryHandler.getProviderAPI().heartbeat(heartbeatRequest);
}
catch (PolarisException e) {
log.error("polaris heartbeat[{}]", e.getCode(), e);
}
catch (Exception e) {
log.error("polaris heartbeat runtime error", e);
}
}
}, 0, ttl, TimeUnit.SECONDS);
}
}

@ -19,10 +19,11 @@ package com.tencent.cloud.polaris.registry;
import com.tencent.cloud.metadata.config.MetadataLocalProperties; import com.tencent.cloud.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.polaris.PolarisProperties; import com.tencent.cloud.polaris.PolarisProperties;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled; import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.polaris.client.api.SDKContext;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -39,32 +40,37 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties @EnableConfigurationProperties
@ConditionalOnPolarisDiscoveryEnabled @ConditionalOnPolarisDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, matchIfMissing = true)
AutoServiceRegistrationAutoConfiguration.class, @AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
PolarisDiscoveryAutoConfiguration.class}) AutoServiceRegistrationAutoConfiguration.class,
PolarisDiscoveryAutoConfiguration.class })
public class PolarisServiceRegistryAutoConfiguration { public class PolarisServiceRegistryAutoConfiguration {
@Bean @Bean
public PolarisServiceRegistry polarisServiceRegistry(PolarisProperties polarisProperties, public PolarisServiceRegistry polarisServiceRegistry(
PolarisDiscoveryHandler polarisDiscoveryHandler, PolarisProperties polarisProperties,
MetadataLocalProperties metadataLocalProperties) { PolarisDiscoveryHandler polarisDiscoveryHandler,
return new PolarisServiceRegistry(polarisProperties, polarisDiscoveryHandler, metadataLocalProperties); MetadataLocalProperties metadataLocalProperties) {
} return new PolarisServiceRegistry(polarisProperties, polarisDiscoveryHandler,
metadataLocalProperties);
}
@Bean @Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnBean(AutoServiceRegistrationProperties.class)
public PolarisRegistration polarisRegistration(PolarisProperties polarisProperties, public PolarisRegistration polarisRegistration(PolarisProperties polarisProperties,
SDKContext context) { SDKContext context) {
return new PolarisRegistration(polarisProperties, context); return new PolarisRegistration(polarisProperties, context);
} }
@Bean @Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnBean(AutoServiceRegistrationProperties.class)
public PolarisAutoServiceRegistration polarisAutoServiceRegistration(PolarisServiceRegistry registry, public PolarisAutoServiceRegistration polarisAutoServiceRegistration(
AutoServiceRegistrationProperties autoServiceRegistrationProperties, PolarisRegistration registration) { PolarisServiceRegistry registry,
return new PolarisAutoServiceRegistration(registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties,
autoServiceRegistrationProperties, registration); PolarisRegistration registration) {
} return new PolarisAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
} }

@ -18,6 +18,7 @@
package com.tencent.cloud.polaris.ribbon; package com.tencent.cloud.polaris.ribbon;
import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled; import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;

@ -21,6 +21,7 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList; import com.netflix.loadbalancer.ServerList;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -31,12 +32,14 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class PolarisRibbonServerListConfiguration { public class PolarisRibbonServerListConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ServerList<Server> ribbonServerList(PolarisDiscoveryHandler polarisDiscoveryHandler, public ServerList<Server> ribbonServerList(
IClientConfig iClientConfig) { PolarisDiscoveryHandler polarisDiscoveryHandler,
PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler); IClientConfig iClientConfig) {
serverList.initWithNiwsConfig(iClientConfig); PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler);
return serverList; serverList.initWithNiwsConfig(iClientConfig);
} return serverList;
}
} }

@ -17,58 +17,59 @@
package com.tencent.cloud.polaris.ribbon; package com.tencent.cloud.polaris.ribbon;
import java.util.ArrayList;
import java.util.List;
import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList; import com.netflix.loadbalancer.AbstractServerList;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
import com.tencent.cloud.polaris.pojo.PolarisServer; import com.tencent.cloud.common.pojo.PolarisServer;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.polaris.api.pojo.Instance; import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInstances; import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.rpc.InstancesResponse; import com.tencent.polaris.api.rpc.InstancesResponse;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import java.util.ArrayList;
import java.util.List;
/** /**
* @author Haotian Zhang, Andrew Shan, Jie Cheng * @author Haotian Zhang, Andrew Shan, Jie Cheng
*/ */
public class PolarisServerList extends AbstractServerList<Server> { public class PolarisServerList extends AbstractServerList<Server> {
private String serviceId; private String serviceId;
private PolarisDiscoveryHandler polarisDiscoveryHandler; private PolarisDiscoveryHandler polarisDiscoveryHandler;
public PolarisServerList(PolarisDiscoveryHandler polarisDiscoveryHandler) { public PolarisServerList(PolarisDiscoveryHandler polarisDiscoveryHandler) {
this.polarisDiscoveryHandler = polarisDiscoveryHandler; this.polarisDiscoveryHandler = polarisDiscoveryHandler;
} }
@Override @Override
public List<Server> getInitialListOfServers() { public List<Server> getInitialListOfServers() {
return getServers(); return getServers();
} }
@Override @Override
public List<Server> getUpdatedListOfServers() { public List<Server> getUpdatedListOfServers() {
return getServers(); return getServers();
} }
private List<Server> getServers() { private List<Server> getServers() {
InstancesResponse filteredInstances = polarisDiscoveryHandler.getFilteredInstances(serviceId); InstancesResponse filteredInstances = polarisDiscoveryHandler
ServiceInstances serviceInstances = filteredInstances.toServiceInstances(); .getFilteredInstances(serviceId);
List<Server> polarisServers = new ArrayList<>(); ServiceInstances serviceInstances = filteredInstances.toServiceInstances();
for (Instance instance : serviceInstances.getInstances()) { List<Server> polarisServers = new ArrayList<>();
polarisServers.add(new PolarisServer(serviceInstances, instance)); for (Instance instance : serviceInstances.getInstances()) {
} polarisServers.add(new PolarisServer(serviceInstances, instance));
return polarisServers; }
} return polarisServers;
}
public String getServiceId() { public String getServiceId() {
return serviceId; return serviceId;
} }
@Override @Override
public void initWithNiwsConfig(IClientConfig iClientConfig) { public void initWithNiwsConfig(IClientConfig iClientConfig) {
this.serviceId = iClientConfig.getClientName(); this.serviceId = iClientConfig.getClientName();
} }
} }

@ -0,0 +1,99 @@
/*
* 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.polaris.util;
import java.util.Map;
import java.util.Objects;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* okhttp util.
*
* @author kan peng
*/
public final class OkHttpUtil {
/**
* Logger.
*/
public final static Logger logger = LoggerFactory.getLogger(OkHttpUtil.class);
/**
* JSON format.
*/
public static final MediaType MEDIA_TYPE_JSON = MediaType
.parse("application/json; charset=utf-8");
/**
* client.
*/
private final static OkHttpClient HTTP_CLIENT = new OkHttpClient();
private OkHttpUtil() {
}
/**
* get request.
* @param url url
* @param headers headers
* @return response
*/
public static boolean get(String url, Map<String, String> headers) {
try {
Request.Builder builder = new Request.Builder();
buildHeader(builder, headers);
Request request = builder.url(url).build();
Response response = HTTP_CLIENT.newCall(request).execute();
if (response.isSuccessful() && Objects.nonNull(response.body())) {
String result = response.body().string();
logger.info("exec get request, url: {} successresponse data: {}", url,
result);
return true;
}
}
catch (Exception e) {
logger.error("exec get requesturl: {} failed!", url, e);
}
return false;
}
/**
* build header.
* @param builder builder
* @param headers headers
*/
private static void buildHeader(Request.Builder builder,
Map<String, String> headers) {
if (Objects.nonNull(headers) && headers.size() > 0) {
headers.forEach((k, v) -> {
if (Objects.nonNull(k) && Objects.nonNull(v)) {
builder.addHeader(k, v);
}
});
}
}
}

@ -2,4 +2,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration,\ com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration,\
com.tencent.cloud.polaris.ribbon.PolarisDiscoveryRibbonAutoConfiguration,\ com.tencent.cloud.polaris.ribbon.PolarisDiscoveryRibbonAutoConfiguration,\
com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration

@ -17,36 +17,44 @@
package com.tencent.cloud.polaris; package com.tencent.cloud.polaris;
import org.junit.Test;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtilsProperties;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import org.junit.Test; /**
import org.springframework.cloud.commons.util.InetUtils; * Test for {@link PolarisProperties}
import org.springframework.cloud.commons.util.InetUtilsProperties; *
* @author Haotian Zhang
*/
public class PolarisPropertiesTest { public class PolarisPropertiesTest {
@Test @Test
public void testInitAndGetSet() { public void testInitAndGetSet() {
PolarisProperties temp = new PolarisProperties(new InetUtils(new InetUtilsProperties())); PolarisProperties temp = new PolarisProperties(
try { new InetUtils(new InetUtilsProperties()));
temp.setNamespace(NAMESPACE_TEST); try {
temp.getNamespace(); temp.setNamespace(NAMESPACE_TEST);
temp.getNamespace();
temp.setService(SERVICE_PROVIDER);
temp.getService(); temp.setService(SERVICE_PROVIDER);
temp.getService();
temp.setToken("xxxxxx");
temp.getToken(); temp.setToken("xxxxxx");
temp.getToken();
temp.init();
assertThat(temp).isNotNull(); temp.init();
} catch (Exception e) { assertThat(temp).isNotNull();
fail(); }
e.printStackTrace(); catch (Exception e) {
} fail();
} e.printStackTrace();
}
}
} }

@ -17,10 +17,6 @@
package com.tencent.cloud.polaris.discovery; package com.tencent.cloud.polaris.discovery;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import com.tencent.cloud.polaris.PolarisProperties; import com.tencent.cloud.polaris.PolarisProperties;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration; import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.api.core.ConsumerAPI;
@ -29,52 +25,61 @@ import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisDiscoveryAutoConfiguration}
*
* @author Haotian Zhang
*/
public class PolarisDiscoveryAutoConfigurationTest { public class PolarisDiscoveryAutoConfigurationTest {
private static NamingServer namingServer; private static NamingServer namingServer;
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of( .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class,
PolarisContextConfiguration.class, PolarisDiscoveryAutoConfiguration.class,
PolarisDiscoveryAutoConfiguration.class, PolarisDiscoveryClientConfiguration.class))
PolarisDiscoveryClientConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT)
.withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
@BeforeClass @BeforeClass
public static void beforeClass() throws Exception { public static void beforeClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081); namingServer = NamingServer.startNamingServer(10081);
} }
@AfterClass @AfterClass
public static void afterClass() throws Exception { public static void afterClass() throws Exception {
if (null != namingServer) { if (null != namingServer) {
namingServer.terminate(); namingServer.terminate();
} }
} }
@Test @Test
public void testDefaultInitialization() { public void testDefaultInitialization() {
this.contextRunner.run(context -> { this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(ProviderAPI.class); assertThat(context).hasSingleBean(ProviderAPI.class);
assertThat(context).hasSingleBean(ConsumerAPI.class); assertThat(context).hasSingleBean(ConsumerAPI.class);
assertThat(context).hasSingleBean(PolarisProperties.class); assertThat(context).hasSingleBean(PolarisProperties.class);
assertThat(context).hasSingleBean(PolarisServiceDiscovery.class); assertThat(context).hasSingleBean(PolarisServiceDiscovery.class);
}); });
} }
@Configuration @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableDiscoveryClient @EnableDiscoveryClient
static class PolarisDiscoveryAutoConfiguration { static class PolarisDiscoveryAutoConfiguration {
} }
} }

@ -17,59 +17,69 @@
package com.tencent.cloud.polaris.discovery; package com.tencent.cloud.polaris.discovery;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration; import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.polaris.test.mock.discovery.NamingServer; import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisDiscoveryClientConfiguration}
*
* @author Haotian Zhang
*/
public class PolarisDiscoveryClientConfigurationTest { public class PolarisDiscoveryClientConfigurationTest {
private static NamingServer namingServer; private static NamingServer namingServer;
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class,
PolarisDiscoveryClientConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
@BeforeClass
public static void beforeClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081);
}
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() @AfterClass
.withConfiguration(AutoConfigurations.of( public static void afterClass() throws Exception {
PolarisContextConfiguration.class, if (null != namingServer) {
PolarisDiscoveryClientConfiguration.class)) namingServer.terminate();
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) }
.withPropertyValues("server.port=" + PORT) }
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
@BeforeClass @Test
public static void beforeClass() throws Exception { public void testDefaultInitialization() {
namingServer = NamingServer.startNamingServer(10081); this.contextRunner.run(context -> assertThat(context)
} .hasSingleBean(PolarisDiscoveryClient.class));
}
@AfterClass @Test
public static void afterClass() throws Exception { public void testDiscoveryBlockingDisabled() {
if (null != namingServer) { this.contextRunner
namingServer.terminate(); .withPropertyValues("spring.cloud.discovery.blocking.enabled=false")
} .run(context -> assertThat(context)
} .doesNotHaveBean(PolarisDiscoveryClient.class));
}
@Test @Configuration
public void testDefaultInitialization() { @EnableAutoConfiguration
this.contextRunner.run(context -> assertThat(context).hasSingleBean(PolarisDiscoveryClient.class)); @EnableDiscoveryClient
} static class PolarisDiscoveryClientConfiguration {
@Test }
public void testDiscoveryBlockingDisabled() {
this.contextRunner.withPropertyValues("spring.cloud.discovery.blocking.enabled=false")
.run(context -> assertThat(context).doesNotHaveBean(PolarisDiscoveryClient.class));
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class PolarisDiscoveryClientConfiguration {
}
} }

@ -17,52 +17,61 @@
package com.tencent.cloud.polaris.discovery; package com.tencent.cloud.polaris.discovery;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.List; import java.util.List;
import com.tencent.cloud.polaris.pojo.PolarisServiceInstance; import com.tencent.cloud.common.pojo.PolarisServiceInstance;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Test for {@link PolarisDiscoveryClient}
*
* @author Haotian Zhang
*/
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*") @PowerMockIgnore("javax.management.*")
public class PolarisDiscoveryClientTest { public class PolarisDiscoveryClientTest {
@Mock @Mock
private PolarisServiceDiscovery polarisServiceDiscovery; private PolarisServiceDiscovery polarisServiceDiscovery;
@InjectMocks
private PolarisDiscoveryClient client;
@InjectMocks @Test
private PolarisDiscoveryClient client; public void testGetInstances() {
@Test when(polarisServiceDiscovery.getInstances(anyString()))
public void testGetInstances() { .thenReturn(singletonList(mock(PolarisServiceInstance.class)));
when(polarisServiceDiscovery.getInstances(anyString())).thenReturn(singletonList(mock(PolarisServiceInstance.class))); List<ServiceInstance> serviceInstances = client.getInstances(SERVICE_PROVIDER);
List<ServiceInstance> serviceInstances = client.getInstances(SERVICE_PROVIDER); assertThat(serviceInstances).isNotEmpty();
}
assertThat(serviceInstances).isNotEmpty(); @Test
} public void testGetServices() {
@Test when(polarisServiceDiscovery.getServices())
public void testGetServices() { .thenReturn(singletonList(SERVICE_PROVIDER));
when(polarisServiceDiscovery.getServices()).thenReturn(singletonList(SERVICE_PROVIDER)); List<String> services = client.getServices();
List<String> services = client.getServices(); assertThat(services).contains(SERVICE_PROVIDER).size().isEqualTo(1);
assertThat(services).contains(SERVICE_PROVIDER).size().isEqualTo(1); }
}
} }

@ -0,0 +1,118 @@
/*
* 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.polaris.discovery;
import java.util.List;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import com.tencent.polaris.test.mock.discovery.NamingService;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisServiceDiscovery}
*
* @author Haotian Zhang
*/
public class PolarisServiceDiscoveryTest {
private static NamingServer namingServer;
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class,
PolarisServiceDiscoveryTest.PolarisPropertiesConfiguration.class,
PolarisDiscoveryClientConfiguration.class,
PolarisDiscoveryAutoConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
.withPropertyValues(
"spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST)
.withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx");
@BeforeClass
public static void beforeClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081);
// add service with 3 instances
NamingService.InstanceParameter instanceParameter = new NamingService.InstanceParameter();
instanceParameter.setHealthy(true);
instanceParameter.setIsolated(false);
instanceParameter.setWeight(100);
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER);
namingServer.getNamingService().batchAddInstances(serviceKey, PORT, 3,
instanceParameter);
}
@AfterClass
public static void afterClass() throws Exception {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void testGetInstances() {
this.contextRunner.run(context -> {
PolarisServiceDiscovery polarisServiceDiscovery = context
.getBean(PolarisServiceDiscovery.class);
List<ServiceInstance> serviceInstances = polarisServiceDiscovery
.getInstances(SERVICE_PROVIDER);
assertThat(serviceInstances.isEmpty()).isFalse();
assertThat(serviceInstances).hasSize(3);
assertThat(serviceInstances.get(0).getPort()).isEqualTo(PORT);
assertThat(serviceInstances.get(1).getPort()).isEqualTo(PORT + 1);
assertThat(serviceInstances.get(2).getPort()).isEqualTo(PORT + 2);
});
}
@Test
public void testGetServices() throws PolarisException {
this.contextRunner.run(context -> {
PolarisServiceDiscovery polarisServiceDiscovery = context
.getBean(PolarisServiceDiscovery.class);
List<String> services = polarisServiceDiscovery.getServices();
assertThat(services.size()).isEqualTo(1);
});
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class PolarisPropertiesConfiguration {
}
}

@ -17,57 +17,63 @@
package com.tencent.cloud.polaris.discovery.reactive; package com.tencent.cloud.polaris.discovery.reactive;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration; import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.polaris.test.mock.discovery.NamingServer; import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisReactiveDiscoveryClientConfiguration}
*
* @author Haotian Zhang
*/
public class PolarisReactiveDiscoveryClientConfigurationTest { public class PolarisReactiveDiscoveryClientConfigurationTest {
private static NamingServer namingServer; private static NamingServer namingServer;
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class,
PolarisReactiveDiscoveryClientConfiguration.class,
PolarisDiscoveryClientConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() @BeforeClass
.withConfiguration(AutoConfigurations.of( public static void beforeClass() throws Exception {
PolarisContextConfiguration.class, namingServer = NamingServer.startNamingServer(10081);
PolarisReactiveDiscoveryClientConfiguration.class, }
PolarisDiscoveryClientConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
@BeforeClass @AfterClass
public static void beforeClass() throws Exception { public static void afterClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081); if (null != namingServer) {
} namingServer.terminate();
}
}
@AfterClass @Test
public static void afterClass() throws Exception { public void testDefaultInitialization() {
if (null != namingServer) { this.contextRunner.run(context -> assertThat(context)
namingServer.terminate(); .hasSingleBean(PolarisReactiveDiscoveryClient.class));
} }
}
@Test @Configuration
public void testDefaultInitialization() { @EnableAutoConfiguration
this.contextRunner.run(context -> assertThat(context) @EnableDiscoveryClient
.hasSingleBean(PolarisReactiveDiscoveryClient.class)); static class PolarisReactiveDiscoveryClientConfiguration {
}
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class PolarisReactiveDiscoveryClientConfiguration {
}
} }

@ -17,57 +17,65 @@
package com.tencent.cloud.polaris.discovery.reactive; package com.tencent.cloud.polaris.discovery.reactive;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static java.util.Collections.singletonList;
import static org.mockito.Mockito.when;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.cloud.polaris.discovery.PolarisServiceDiscovery;
import java.util.Arrays; import java.util.Arrays;
import com.tencent.cloud.polaris.discovery.PolarisServiceDiscovery;
import com.tencent.polaris.api.exception.PolarisException;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.cloud.client.ServiceInstance;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
import org.springframework.cloud.client.ServiceInstance;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static java.util.Collections.singletonList;
import static org.mockito.Mockito.when;
/**
* Test for {@link PolarisReactiveDiscoveryClient}
*
* @author Haotian Zhang
*/
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*") @PowerMockIgnore("javax.management.*")
public class PolarisReactiveDiscoveryClientTest { public class PolarisReactiveDiscoveryClientTest {
@Mock @Mock
private PolarisServiceDiscovery serviceDiscovery; private PolarisServiceDiscovery serviceDiscovery;
@Mock @Mock
private ServiceInstance serviceInstance; private ServiceInstance serviceInstance;
@InjectMocks @InjectMocks
private PolarisReactiveDiscoveryClient client; private PolarisReactiveDiscoveryClient client;
@Test @Test
public void testGetInstances() throws PolarisException { public void testGetInstances() throws PolarisException {
when(serviceDiscovery.getInstances(SERVICE_PROVIDER)).thenReturn(singletonList(serviceInstance)); when(serviceDiscovery.getInstances(SERVICE_PROVIDER))
.thenReturn(singletonList(serviceInstance));
Flux<ServiceInstance> instances = this.client.getInstances(SERVICE_PROVIDER); Flux<ServiceInstance> instances = this.client.getInstances(SERVICE_PROVIDER);
StepVerifier.create(instances).expectNextCount(1).expectComplete().verify(); StepVerifier.create(instances).expectNextCount(1).expectComplete().verify();
} }
@Test @Test
public void testGetServices() throws PolarisException { public void testGetServices() throws PolarisException {
when(serviceDiscovery.getServices()).thenReturn(Arrays.asList(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2)); when(serviceDiscovery.getServices())
.thenReturn(Arrays.asList(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2));
Flux<String> services = this.client.getServices(); Flux<String> services = this.client.getServices();
StepVerifier.create(services).expectNext(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2) StepVerifier.create(services)
.expectComplete().verify(); .expectNext(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2).expectComplete()
} .verify();
}
} }

@ -19,15 +19,12 @@ package com.tencent.cloud.polaris.registry;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration; import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.polaris.test.mock.discovery.NamingServer; import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
@ -35,43 +32,53 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisServiceRegistryAutoConfiguration}
*
* @author Haotian Zhang
*/
public class PolarisServiceRegistryAutoConfigurationTest { public class PolarisServiceRegistryAutoConfigurationTest {
private static NamingServer namingServer; private static NamingServer namingServer;
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class,
PolarisServiceRegistryAutoConfiguration.class,
PolarisDiscoveryClientConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() @BeforeClass
.withConfiguration(AutoConfigurations.of( public static void beforeClass() throws Exception {
PolarisContextConfiguration.class, namingServer = NamingServer.startNamingServer(10081);
PolarisServiceRegistryAutoConfiguration.class, }
PolarisDiscoveryClientConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
@BeforeClass @AfterClass
public static void beforeClass() throws Exception { public static void afterClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081); if (null != namingServer) {
} namingServer.terminate();
}
}
@AfterClass @Test
public static void afterClass() throws Exception { public void testDefaultInitialization() {
if (null != namingServer) { this.contextRunner.run(context -> {
namingServer.terminate(); assertThat(context).hasSingleBean(PolarisDiscoveryAutoConfiguration.class);
} assertThat(context)
} .hasSingleBean(AutoServiceRegistrationAutoConfiguration.class);
});
}
@Test @Configuration
public void testDefaultInitialization() { @EnableAutoConfiguration
this.contextRunner.run(context -> { @EnableDiscoveryClient
assertThat(context).hasSingleBean(PolarisDiscoveryAutoConfiguration.class); static class PolarisServiceRegistryAutoConfiguration {
assertThat(context).hasSingleBean(AutoServiceRegistrationAutoConfiguration.class);
});
}
@Configuration }
@EnableAutoConfiguration
@EnableDiscoveryClient
static class PolarisServiceRegistryAutoConfiguration {
}
} }

@ -0,0 +1,120 @@
/*
* 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.polaris.registry;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
/**
* Test for {@link PolarisServiceRegistry}
*
* @author Haotian Zhang
*/
public class PolarisServiceRegistryTest {
private static NamingServer namingServer;
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class,
PolarisPropertiesConfiguration.class,
PolarisDiscoveryClientConfiguration.class,
PolarisDiscoveryAutoConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
.withPropertyValues(
"spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST)
.withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx");
@BeforeClass
public static void beforeClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081);
// add service
namingServer.getNamingService()
.addService(new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER));
}
@AfterClass
public static void afterClass() throws Exception {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void testRegister() {
this.contextRunner.run(context -> {
PolarisServiceRegistry registry = context
.getBean(PolarisServiceRegistry.class);
PolarisRegistration registration = Mockito.mock(PolarisRegistration.class);
when(registration.getHost()).thenReturn("127.0.0.1");
when(registration.getPort()).thenReturn(PORT);
when(registration.getServiceId()).thenReturn(SERVICE_PROVIDER);
try {
registry.register(registration);
}
catch (Exception e) {
fail();
}
try {
assertThat(registry.getStatus(registration)).isEqualTo("DOWN");
}
catch (Exception e) {
fail();
}
try {
registry.deregister(registration);
}
catch (Exception e) {
fail();
}
});
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class PolarisPropertiesConfiguration {
}
}

@ -17,16 +17,12 @@
package com.tencent.cloud.polaris.ribbon; package com.tencent.cloud.polaris.ribbon;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfig;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
@ -36,48 +32,59 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
public class PolarisRibbonServerListAutoConfigurationTest { import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisRibbonServerListConfiguration}
*
* @author Haotian Zhang
*/
public class PolarisRibbonServerListConfigurationTest {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of( .withConfiguration(AutoConfigurations.of(PolarisRibbonClientTest.class,
PolarisRibbonClientTest.class, PolarisDiscoveryClientConfiguration.class))
PolarisDiscoveryClientConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT)
.withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") .withPropertyValues(
.withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) "spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST)
.withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx");
@Test @Test
public void testProperties() { public void testProperties() {
this.contextRunner.run(context -> { this.contextRunner.run(context -> {
PolarisDiscoveryHandler discoveryHandler = context.getBean(PolarisDiscoveryHandler.class); PolarisDiscoveryHandler discoveryHandler = context
PolarisServerList serverList = new PolarisServerList(discoveryHandler); .getBean(PolarisDiscoveryHandler.class);
IClientConfig iClientConfig = context.getBean(IClientConfig.class); PolarisServerList serverList = new PolarisServerList(discoveryHandler);
serverList.initWithNiwsConfig(iClientConfig); IClientConfig iClientConfig = context.getBean(IClientConfig.class);
serverList.initWithNiwsConfig(iClientConfig);
assertThat(serverList.getServiceId()).isEqualTo(SERVICE_PROVIDER); assertThat(serverList.getServiceId()).isEqualTo(SERVICE_PROVIDER);
}); });
} }
@Configuration @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableDiscoveryClient @EnableDiscoveryClient
static class PolarisRibbonClientTest { static class PolarisRibbonClientTest {
@Bean @Bean
IClientConfig iClientConfig() { IClientConfig iClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl(); DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.setClientName(SERVICE_PROVIDER); config.setClientName(SERVICE_PROVIDER);
return config; return config;
} }
@Bean @Bean
@LoadBalanced @LoadBalanced
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
} }

@ -0,0 +1,145 @@
/*
* 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.polaris.ribbon;
import java.util.List;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.Server;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import com.tencent.polaris.test.mock.discovery.NamingService;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Test for {@link PolarisServerList}
*
* @author Haotian Zhang
*/
public class PolarisServerListTest {
private static NamingServer namingServer;
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class,
PolarisServerListTest.PolarisPropertiesConfiguration.class,
PolarisDiscoveryClientConfiguration.class,
PolarisDiscoveryAutoConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
.withPropertyValues(
"spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST)
.withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx");
@BeforeClass
public static void beforeClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081);
// add service
namingServer.getNamingService()
.addService(new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER));
}
@AfterClass
public static void afterClass() throws Exception {
if (null != namingServer) {
namingServer.terminate();
}
}
/**
* Test {@link PolarisServerList#getInitialListOfServers()} with empty server list.
*/
@Test
@SuppressWarnings("unchecked")
public void test1() {
this.contextRunner.run(context -> {
// mock
IClientConfig iClientConfig = mock(IClientConfig.class);
when(iClientConfig.getClientName()).thenReturn(SERVICE_PROVIDER);
PolarisDiscoveryHandler polarisDiscoveryHandler = context
.getBean(PolarisDiscoveryHandler.class);
PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler);
serverList.initWithNiwsConfig(iClientConfig);
List<Server> servers = serverList.getInitialListOfServers();
assertThat(servers).isEmpty();
});
}
/**
* Test {@link PolarisServerList#getUpdatedListOfServers()} with server list of size
* 3.
*/
@Test
@SuppressWarnings("unchecked")
public void test2() throws Exception {
this.contextRunner.run(context -> {
// mock
IClientConfig iClientConfig = mock(IClientConfig.class);
when(iClientConfig.getClientName()).thenReturn(SERVICE_PROVIDER);
PolarisDiscoveryHandler polarisDiscoveryHandler = context
.getBean(PolarisDiscoveryHandler.class);
PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler);
serverList.initWithNiwsConfig(iClientConfig);
// add service with 3 instances
NamingService.InstanceParameter instanceParameter = new NamingService.InstanceParameter();
instanceParameter.setHealthy(true);
instanceParameter.setIsolated(false);
instanceParameter.setWeight(100);
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER);
namingServer.getNamingService().batchAddInstances(serviceKey, PORT, 3,
instanceParameter);
List<Server> servers = serverList.getUpdatedListOfServers();
assertThat(servers).hasSize(3);
assertThat(servers.get(0).getPort()).isEqualTo(PORT);
assertThat(servers.get(1).getPort()).isEqualTo(PORT + 1);
assertThat(servers.get(2).getPort()).isEqualTo(PORT + 2);
});
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class PolarisPropertiesConfiguration {
}
}

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId>
<name>Spring Cloud Starter Tencent Polaris Ratelimit</name>
<dependencies>
<!-- Spring Cloud Tencent dependencies start -->
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-commons</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-context</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-commons</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-metadata</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-ratelimit-factory</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-common</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-mock-discovery</artifactId>
<scope>test</scope>
</dependency>
<!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -15,18 +15,15 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.ratelimit; package com.tencent.cloud.polaris.ratelimit.config;
import static javax.servlet.DispatcherType.ASYNC; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import static javax.servlet.DispatcherType.ERROR; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter;
import static javax.servlet.DispatcherType.FORWARD; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter;
import static javax.servlet.DispatcherType.INCLUDE;
import static javax.servlet.DispatcherType.REQUEST;
import com.tencent.cloud.polaris.ratelimit.callee.QuotaCheckFilter;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.ratelimit.api.core.LimitAPI; import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.factory.LimitAPIFactory; import com.tencent.polaris.ratelimit.factory.LimitAPIFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
@ -34,42 +31,64 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import static javax.servlet.DispatcherType.ASYNC;
import static javax.servlet.DispatcherType.ERROR;
import static javax.servlet.DispatcherType.FORWARD;
import static javax.servlet.DispatcherType.INCLUDE;
import static javax.servlet.DispatcherType.REQUEST;
/** /**
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@Configuration @Configuration
@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true) @ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled",
matchIfMissing = true)
public class RateLimitConfiguration { public class RateLimitConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public LimitAPI limitAPI(SDKContext polarisContext) { public LimitAPI limitAPI(SDKContext polarisContext) {
return LimitAPIFactory.createLimitAPIByContext(polarisContext); return LimitAPIFactory.createLimitAPIByContext(polarisContext);
} }
/**
* Create when web application type is SERVLET.
*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
static class QuotaCheckFilterConfig {
@Bean
@ConditionalOnMissingBean
public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI) {
return new QuotaCheckServletFilter(limitAPI);
}
@Bean
public FilterRegistrationBean<QuotaCheckServletFilter> quotaFilterRegistrationBean(
QuotaCheckServletFilter quotaCheckServletFilter) {
FilterRegistrationBean<QuotaCheckServletFilter> registrationBean = new FilterRegistrationBean<>(
quotaCheckServletFilter);
registrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
registrationBean.setName("quotaFilterRegistrationBean");
registrationBean.setOrder(RateLimitConstant.FILTER_ORDER);
return registrationBean;
}
/** }
*
*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
static class QuotaCheckFilterConfig {
@Bean /**
@ConditionalOnMissingBean * Create when web application type is REACTIVE.
public QuotaCheckFilter quotaCheckFilter(LimitAPI limitAPI) { */
return new QuotaCheckFilter(limitAPI); @Configuration
} @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
static class MetadataReactiveFilterConfig {
@Bean @Bean
public FilterRegistrationBean<QuotaCheckFilter> quotaFilterRegistrationBean( public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI) {
QuotaCheckFilter quotaCheckFilter) { return new QuotaCheckReactiveFilter(limitAPI);
FilterRegistrationBean<QuotaCheckFilter> registrationBean = new FilterRegistrationBean<>(quotaCheckFilter); }
registrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
registrationBean.setName("quotaFilterRegistrationBean");
registrationBean.setOrder(QuotaCheckFilter.ORDER);
return registrationBean;
}
}
}
} }

@ -15,35 +15,28 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.feign; package com.tencent.cloud.polaris.ratelimit.constant;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
/** /**
* Pre plugin used by PluggableFeign. * Constant for rate-limiter.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public interface PluggableFeignPlugin extends Ordered { public final class RateLimitConstant {
/** /**
* Get name of plugin * Order of filter.
* */
* @return public static final int FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
*/
String getName();
/** /**
* Get type of plugin * Info of rate limit.
* @see PluggableFeignPluginType */
* public static String QUOTA_LIMITED_INFO = "request blocked by polaris, reason is ";
* @return
*/
PluggableFeignPluginType getType();
/** private RateLimitConstant() {
* Run the plugin }
*/
void run(PluggableFeignContext context);
} }

@ -0,0 +1,106 @@
/*
* 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.polaris.ratelimit.filter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import com.tencent.cloud.metadata.constant.MetadataConstant;
import com.tencent.cloud.metadata.context.MetadataContext;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
/**
* Reactive filter to check quota.
*
* @author Haotian Zhang
*/
public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
private static final Logger LOG = LoggerFactory
.getLogger(QuotaCheckReactiveFilter.class);
private final LimitAPI limitAPI;
public QuotaCheckReactiveFilter(LimitAPI limitAPI) {
this.limitAPI = limitAPI;
}
@Override
public int getOrder() {
return RateLimitConstant.FILTER_ORDER;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
// get metadata of current thread
MetadataContext metadataContext = exchange
.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT);
String localNamespace = MetadataContextHolder.get()
.getSystemMetadata(MetadataConstant.SystemMetadataKey.LOCAL_NAMESPACE);
String localService = MetadataContextHolder.get()
.getSystemMetadata(MetadataConstant.SystemMetadataKey.LOCAL_SERVICE);
String method = MetadataContextHolder.get()
.getSystemMetadata(MetadataConstant.SystemMetadataKey.LOCAL_PATH);
Map<String, String> labels = null;
if (StringUtils.isNotBlank(method)) {
labels = new HashMap<>();
labels.put("method", method);
}
try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI,
localNamespace, localService, 1, labels, null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
DataBuffer dataBuffer = response.bufferFactory().allocateBuffer().write(
(RateLimitConstant.QUOTA_LIMITED_INFO + quotaResponse.getInfo())
.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
}
catch (Throwable t) {
// 限流API调用出现异常不应该影响业务流程的调用
LOG.error("fail to invoke getQuota, service is " + localService, t);
}
return chain.filter(exchange);
}
}

@ -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.polaris.ratelimit.filter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
/**
* Servlet filter to check quota.
*
* @author Haotian Zhang
*/
@Order(RateLimitConstant.FILTER_ORDER)
public class QuotaCheckServletFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory
.getLogger(QuotaCheckServletFilter.class);
private final LimitAPI limitAPI;
public QuotaCheckServletFilter(LimitAPI limitAPI) {
this.limitAPI = limitAPI;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String localNamespace = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE);
String localService = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.LOCAL_SERVICE);
String method = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.LOCAL_PATH);
Map<String, String> labels = null;
if (StringUtils.isNotBlank(method)) {
labels = new HashMap<>();
labels.put("method", method);
}
try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI,
localNamespace, localService, 1, labels, null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
response.setStatus(TOO_MANY_REQUESTS.value());
response.getWriter().write(
RateLimitConstant.QUOTA_LIMITED_INFO + quotaResponse.getInfo());
}
else {
filterChain.doFilter(request, response);
}
}
catch (Throwable t) {
// 限流API调用出现异常不应该影响业务流程的调用
LOG.error("fail to invoke getQuota, service is " + localService, t);
filterChain.doFilter(request, response);
}
}
}

@ -16,6 +16,8 @@
*/ */
package com.tencent.cloud.polaris.ratelimit.utils; package com.tencent.cloud.polaris.ratelimit.utils;
import java.util.Map;
import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult;
import com.tencent.polaris.ratelimit.api.core.LimitAPI; import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest;
@ -23,33 +25,37 @@ import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Map;
/** /**
* Utils for quota checking * Utils for quota checking.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class QuotaCheckUtils { public final class QuotaCheckUtils {
private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckUtils.class); private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckUtils.class);
public static QuotaResponse getQuota(LimitAPI limitAPI, String namespace, String service, int count, private QuotaCheckUtils() {
Map<String, String> labels, String method) { }
// build quota request
QuotaRequest quotaRequest = new QuotaRequest(); public static QuotaResponse getQuota(LimitAPI limitAPI, String namespace,
quotaRequest.setNamespace(namespace); String service, int count, Map<String, String> labels, String method) {
quotaRequest.setService(service); // build quota request
quotaRequest.setCount(count); QuotaRequest quotaRequest = new QuotaRequest();
quotaRequest.setLabels(labels); quotaRequest.setNamespace(namespace);
quotaRequest.setMethod(method); quotaRequest.setService(service);
quotaRequest.setCount(count);
try { quotaRequest.setLabels(labels);
return limitAPI.getQuota(quotaRequest); quotaRequest.setMethod(method);
} catch (Throwable throwable) {
LOG.error("fail to invoke getQuota of LimitAPI with QuotaRequest[{}].", quotaRequest, throwable); try {
return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 0, "get quota failed")); return limitAPI.getQuota(quotaRequest);
} }
} catch (Throwable throwable) {
LOG.error("fail to invoke getQuota of LimitAPI with QuotaRequest[{}].",
quotaRequest, throwable);
return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 0,
"get quota failed"));
}
}
} }

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.ratelimit.config.RateLimitConfiguration

@ -0,0 +1,150 @@
/*
* 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.polaris.ratelimit.controller;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import com.tencent.polaris.test.mock.discovery.NamingService;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.HttpClientErrorException.TooManyRequests;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Test for rate-limit.
*
* @author Haotian Zhang
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { CalleeControllerTests.Config.class, TestController.class },
properties = { "spring.application.name=java_provider_test",
"spring.cloud.polaris.discovery.namespace=Test",
"spring.cloud.polaris.address=grpc://127.0.0.1:10081" })
public class CalleeControllerTests {
private static NamingServer namingServer;
@LocalServerPort
private int port;
@Autowired
private RestTemplate restTemplate;
@MockBean
private LimitAPI limitAPI;
@BeforeClass
public static void beforeClass() throws Exception {
namingServer = NamingServer.startNamingServer(10081);
// add service with 3 instances
NamingService.InstanceParameter instanceParameter = new NamingService.InstanceParameter();
instanceParameter.setHealthy(true);
instanceParameter.setIsolated(false);
instanceParameter.setWeight(100);
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER);
namingServer.getNamingService().batchAddInstances(serviceKey, PORT, 3,
instanceParameter);
}
@AfterClass
public static void afterClass() throws Exception {
if (null != namingServer) {
namingServer.terminate();
}
}
@Before
public void setUp() {
QuotaResponse quotaResponse = mock(QuotaResponse.class);
when(quotaResponse.getCode()).thenReturn(QuotaResultCode.QuotaResultOk);
when(limitAPI.getQuota(any())).thenReturn(quotaResponse);
}
@Test
public void test1() {
String url = "http://localhost:" + port + "/test/info";
boolean hasPassed = false;
boolean hasLimited = false;
for (int i = 0; i < 30; i++) {
try {
if (i > 9) {
QuotaResponse quotaResponse = mock(QuotaResponse.class);
when(quotaResponse.getCode())
.thenReturn(QuotaResultCode.QuotaResultLimited);
when(quotaResponse.getInfo())
.thenReturn("Testing rate limit after 10 times success.");
when(limitAPI.getQuota(any())).thenReturn(quotaResponse);
}
String result = restTemplate.getForObject(url, String.class);
System.out.println(result + " [" + i + "]");
hasPassed = true;
}
catch (RestClientException e) {
if (e instanceof TooManyRequests) {
System.out.println(((TooManyRequests) e).getResponseBodyAsString());
hasLimited = true;
}
else {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
}
Assert.assertTrue(hasPassed);
Assert.assertTrue(hasLimited);
}
@Configuration
@EnableAutoConfiguration
public static class Config {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
}

@ -21,15 +21,18 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/**
* Test controller.
*
* @author Haotian Zhang
*/
@RestController @RestController
@RequestMapping("/test") @RequestMapping("/test")
public class TestController { public class TestController {
@GetMapping("/info") @GetMapping("/info")
public String info() throws Exception { public String info() throws Exception {
return "hello service info"; return "hello service info";
} }
} }

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>spring-cloud-tencent-starters</artifactId> <artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<version>${revision}</version> <version>${revision}</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
<name>Spring Cloud Starter Tencent Polaris Router</name> <name>Spring Cloud Starter Tencent Polaris Router</name>
<dependencies> <dependencies>
<!-- Spring Cloud Tencent dependencies start --> <!-- Spring Cloud Tencent dependencies start -->
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-commons</artifactId> <artifactId>spring-cloud-tencent-commons</artifactId>
</dependency> </dependency>
<dependency> <dependency>

@ -0,0 +1,138 @@
/*
* 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.polaris.router;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.PollingServerListUpdater;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.tencent.cloud.common.pojo.PolarisServer;
import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.polaris.api.pojo.DefaultInstance;
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInfo;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.router.api.core.RouterAPI;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;
/**
* Routing load balancer of polaris.
*
* @author Haotian Zhang
*/
public class PolarisRoutingLoadBalancer extends DynamicServerListLoadBalancer<Server> {
private final RouterAPI routerAPI;
public PolarisRoutingLoadBalancer(IClientConfig config, IRule rule, IPing ping,
ServerList<Server> serverList, RouterAPI routerAPI) {
super(config, rule, ping, serverList, null, new PollingServerListUpdater());
this.routerAPI = routerAPI;
}
@Override
public List<Server> getReachableServers() {
List<Server> allServers = super.getAllServers();
if (CollectionUtils.isEmpty(allServers)) {
return allServers;
}
ServiceInstances serviceInstances = null;
if (allServers.get(0) instanceof PolarisServer) {
serviceInstances = ((PolarisServer) allServers.get(0)).getServiceInstances();
}
else {
String serviceName;
// notice the difference between different service registries
if (StringUtils.isNotBlank(
allServers.get(0).getMetaInfo().getServiceIdForDiscovery())) {
serviceName = allServers.get(0).getMetaInfo().getServiceIdForDiscovery();
}
else {
serviceName = allServers.get(0).getMetaInfo().getAppName();
}
if (StringUtils.isBlank(serviceName)) {
throw new IllegalStateException(
"PolarisRoutingLoadBalancer only Server with AppName or ServiceIdForDiscovery attribute");
}
ServiceKey serviceKey = new ServiceKey(MetadataContextHolder.LOCAL_NAMESPACE,
serviceName);
List<Instance> instances = new ArrayList<>(8);
for (Server server : allServers) {
DefaultInstance instance = new DefaultInstance();
instance.setNamespace(MetadataContextHolder.LOCAL_NAMESPACE);
instance.setService(serviceName);
instance.setHealthy(server.isAlive());
instance.setProtocol(server.getScheme());
instance.setId(server.getId());
instance.setHost(server.getHost());
instance.setPort(server.getPort());
instance.setZone(server.getZone());
instance.setWeight(100);
instances.add(instance);
}
serviceInstances = new DefaultServiceInstances(serviceKey, instances);
}
ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest();
processRoutersRequest.setDstInstances(serviceInstances);
String srcNamespace = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE);
String srcService = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.LOCAL_SERVICE);
Map<String, String> transitiveCustomMetadata = MetadataContextHolder.get()
.getAllTransitiveCustomMetadata();
String method = MetadataContextHolder.get()
.getSystemMetadata(SystemMetadataKey.PEER_PATH);
processRoutersRequest.setMethod(method);
if (StringUtils.isNotBlank(srcNamespace) && StringUtils.isNotBlank(srcService)) {
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.setNamespace(srcNamespace);
serviceInfo.setService(srcService);
serviceInfo.setMetadata(transitiveCustomMetadata);
processRoutersRequest.setSourceService(serviceInfo);
}
ProcessRoutersResponse processRoutersResponse = routerAPI
.processRouters(processRoutersRequest);
ServiceInstances filteredServiceInstances = processRoutersResponse
.getServiceInstances();
List<Server> filteredInstances = new ArrayList<>();
for (Instance instance : filteredServiceInstances.getInstances()) {
filteredInstances.add(new PolarisServer(serviceInstances, instance));
}
return filteredInstances;
}
@Override
public List<Server> getAllServers() {
return getReachableServers();
}
}

@ -21,6 +21,7 @@ import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.RouterAPIFactory; import com.tencent.polaris.factory.api.RouterAPIFactory;
import com.tencent.polaris.router.api.core.RouterAPI; import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -37,20 +38,22 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties @EnableConfigurationProperties
@ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.enabled", matchIfMissing = true) @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.enabled",
matchIfMissing = true)
@AutoConfigureAfter(RibbonAutoConfiguration.class) @AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = PolarisRibbonClientConfiguration.class) @RibbonClients(defaultConfiguration = PolarisRibbonClientConfiguration.class)
public class PolarisRibbonAutoConfiguration { public class PolarisRibbonAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PolarisRibbonProperties polarisRibbonProperties() { public PolarisRibbonProperties polarisRibbonProperties() {
return new PolarisRibbonProperties(); return new PolarisRibbonProperties();
} }
@Bean(name = "polarisRoute")
@ConditionalOnMissingBean
public RouterAPI polarisRouter(SDKContext polarisContext) throws PolarisException {
return RouterAPIFactory.createRouterAPIByContext(polarisContext);
}
@Bean(name = "polarisRoute")
@ConditionalOnMissingBean
public RouterAPI polarisRouter(SDKContext polarisContext) throws PolarisException {
return RouterAPIFactory.createRouterAPIByContext(polarisContext);
}
} }

@ -27,6 +27,7 @@ import com.tencent.cloud.polaris.router.PolarisRoutingLoadBalancer;
import com.tencent.cloud.polaris.router.rule.PolarisLoadBalanceRule; import com.tencent.cloud.polaris.router.rule.PolarisLoadBalanceRule;
import com.tencent.cloud.polaris.router.rule.PolarisWeightedRandomRule; import com.tencent.cloud.polaris.router.rule.PolarisWeightedRandomRule;
import com.tencent.polaris.router.api.core.RouterAPI; import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -37,20 +38,24 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class PolarisRibbonClientConfiguration { public class PolarisRibbonClientConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public IRule polarisRibbonRule(PolarisRibbonProperties polarisRibbonProperties) { public IRule polarisRibbonRule(PolarisRibbonProperties polarisRibbonProperties) {
switch (PolarisLoadBalanceRule.fromStrategy(polarisRibbonProperties.getPolicy())) { switch (PolarisLoadBalanceRule
case WEIGHTED_RANDOM_RULE: .fromStrategy(polarisRibbonProperties.getStrategy())) {
default: case WEIGHTED_RANDOM_RULE:
return new PolarisWeightedRandomRule(); default:
} return new PolarisWeightedRandomRule();
} }
}
@Bean
@ConditionalOnMissingBean
public ILoadBalancer polarisRoutingLoadBalancer(IClientConfig iClientConfig,
IRule iRule, IPing iPing, ServerList<Server> serverList,
RouterAPI polarisRouter) {
return new PolarisRoutingLoadBalancer(iClientConfig, iRule, iPing, serverList,
polarisRouter);
}
@Bean
@ConditionalOnMissingBean
public ILoadBalancer polarisRoutingLoadBalancer(IClientConfig iClientConfig, IRule iRule, IPing iPing,
ServerList<Server> serverList, RouterAPI polarisRouter) {
return new PolarisRoutingLoadBalancer(iClientConfig, iRule, iPing, serverList, polarisRouter);
}
} }

@ -21,44 +21,45 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* Properties of Ribbon.
*
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@ConfigurationProperties("spring.cloud.polaris.ribbon") @ConfigurationProperties("spring.cloud.polaris.ribbon")
public class PolarisRibbonProperties { public class PolarisRibbonProperties {
/** /**
* * If load-balance enabled.
*/ */
@Value("${spring.cloud.polaris.loadbalancer.enabled:#{true}}") @Value("${spring.cloud.polaris.discovery.loadbalancer.enabled:#{true}}")
private Boolean loadbalancerEnabled; private Boolean loadbalancerEnabled;
/**
* Load balance strategy.
*/
@Value("${spring.cloud.polaris.loadbalancer.strategy:#{'weightedRandom'}}")
private String strategy;
/** public String getStrategy() {
* loadbalnce strategy return strategy;
*/ }
@Value("${spring.cloud.polaris.loadbalancer.strategy:#{'weightedRandom'}}")
private String policy;
public String getPolicy() { public void setStrategy(String strategy) {
return policy; this.strategy = strategy;
} }
public void setPolicy(String policy) { public Boolean getLoadbalancerEnabled() {
this.policy = policy; return loadbalancerEnabled;
} }
public Boolean getLoadbalancerEnabled() { public void setLoadbalancerEnabled(Boolean loadbalancerEnabled) {
return loadbalancerEnabled; this.loadbalancerEnabled = loadbalancerEnabled;
} }
public void setLoadbalancerEnabled(Boolean loadbalancerEnabled) { @Override
this.loadbalancerEnabled = loadbalancerEnabled; public String toString() {
} return "PolarisRibbonProperties{" + "loadbalancerEnabled=" + loadbalancerEnabled
+ ", strategy='" + strategy + '\'' + '}';
}
@Override
public String toString() {
return "PolarisRibbonProperties{" +
"loadbalancerEnabled=" + loadbalancerEnabled +
", policy='" + policy + '\'' +
'}';
}
} }

@ -20,34 +20,33 @@ package com.tencent.cloud.polaris.router.rule;
import java.util.Arrays; import java.util.Arrays;
/** /**
* Load balance rule.
*
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public enum PolarisLoadBalanceRule { public enum PolarisLoadBalanceRule {
/** /**
* * Weighted random load balance rule.
*/ */
WEIGHTED_RANDOM_RULE("weighted_random"); WEIGHTED_RANDOM_RULE("weighted_random");
/** /**
* * Load balance strategy.
*/ */
String policy; final String policy;
PolarisLoadBalanceRule(String strategy) { PolarisLoadBalanceRule(String strategy) {
this.policy = strategy; this.policy = strategy;
} }
public static PolarisLoadBalanceRule fromStrategy(String strategy) { public static PolarisLoadBalanceRule fromStrategy(String strategy) {
return Arrays.stream(values()).filter(t -> t.getPolicy().equals(strategy)).findAny() return Arrays.stream(values()).filter(t -> t.getPolicy().equals(strategy))
.orElse(WEIGHTED_RANDOM_RULE); .findAny().orElse(WEIGHTED_RANDOM_RULE);
} }
/** public String getPolicy() {
* {@link #policy}getter return policy;
*/ }
public String getPolicy() {
return policy;
}
} }

@ -17,51 +17,57 @@
package com.tencent.cloud.polaris.router.rule; package com.tencent.cloud.polaris.router.rule;
import java.util.List;
import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
import com.tencent.cloud.polaris.pojo.PolarisServer; import com.tencent.cloud.common.pojo.PolarisServer;
import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; import com.tencent.polaris.api.config.consumer.LoadBalanceConfig;
import com.tencent.polaris.api.pojo.Instance; import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.router.api.core.RouterAPI; import com.tencent.polaris.router.api.core.RouterAPI;
import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest;
import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.List;
/** /**
* Weighted random load balance strategy.
*
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisWeightedRandomRule extends AbstractLoadBalancerRule { public class PolarisWeightedRandomRule extends AbstractLoadBalancerRule {
private static final String POLICY = LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM; private static final String POLICY = LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM;
@Autowired
private RouterAPI polarisRouter;
@Autowired @Override
private RouterAPI polarisRouter; public void initWithNiwsConfig(IClientConfig clientConfig) {
@Override }
public void initWithNiwsConfig(IClientConfig clientConfig) {
} @Override
public Server choose(Object key) {
List<Server> allServers = getLoadBalancer().getReachableServers();
if (CollectionUtils.isEmpty(allServers)) {
return null;
}
Server server = allServers.get(0);
if (!(server instanceof PolarisServer)) {
throw new IllegalStateException(
"PolarisDiscoveryRule only support PolarisServer instances");
}
PolarisServer polarisServer = (PolarisServer) server;
ProcessLoadBalanceRequest request = new ProcessLoadBalanceRequest();
request.setDstInstances(polarisServer.getServiceInstances());
request.setLbPolicy(POLICY);
ProcessLoadBalanceResponse processLoadBalanceResponse = polarisRouter
.processLoadBalance(request);
Instance targetInstance = processLoadBalanceResponse.getTargetInstance();
return new PolarisServer(polarisServer.getServiceInstances(), targetInstance);
}
@Override
public Server choose(Object key) {
List<Server> allServers = getLoadBalancer().getReachableServers();
if (CollectionUtils.isEmpty(allServers)) {
return null;
}
Server server = allServers.get(0);
if (!(server instanceof PolarisServer)) {
throw new IllegalStateException("PolarisDiscoveryRule only support PolarisServer instances");
}
PolarisServer polarisServer = (PolarisServer) server;
ProcessLoadBalanceRequest request = new ProcessLoadBalanceRequest();
request.setDstInstances(polarisServer.getServiceInstances());
request.setLbPolicy(POLICY);
ProcessLoadBalanceResponse processLoadBalanceResponse = polarisRouter.processLoadBalance(request);
Instance targetInstance = processLoadBalanceResponse.getTargetInstance();
return new PolarisServer(polarisServer.getServiceInstances(), targetInstance);
}
} }

@ -19,6 +19,7 @@ package com.tencent.cloud.polaris.router.config;
import com.tencent.polaris.router.api.core.RouterAPI; import com.tencent.polaris.router.api.core.RouterAPI;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@ -29,29 +30,31 @@ import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* @author skyehtzhang * Test for {@link PolarisRibbonAutoConfiguration}
*
* @author Haotian Zhang
*/ */
public class PolarisRibbonAutoConfigurationTest { public class PolarisRibbonAutoConfigurationTest {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner() private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of( .withConfiguration(AutoConfigurations.of(PolarisRibbonTest.class,
PolarisRibbonTest.class, PolarisRibbonAutoConfiguration.class))
PolarisRibbonAutoConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT)
.withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
@Test
@Test public void testDefaultInitialization() {
public void testDefaultInitialization() { this.contextRunner.run(context -> {
this.contextRunner.run(context -> { assertThat(context).hasSingleBean(RouterAPI.class);
assertThat(context).hasSingleBean(RouterAPI.class); assertThat(context).hasSingleBean(PolarisRibbonProperties.class);
assertThat(context).hasSingleBean(PolarisRibbonProperties.class); });
}); }
}
@Configuration
@Configuration @EnableAutoConfiguration
@EnableAutoConfiguration static class PolarisRibbonTest {
static class PolarisRibbonTest {
}
}
} }

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-tencent-commons</artifactId>
<name>Spring Cloud Tencent Commons</name>
<properties>
<commons.collections.version>3.2.2</commons.collections.version>
<commons.lang.version>2.5</commons.lang.version>
<commons.io.version>2.7</commons.io.version>
</properties>
<dependencies>
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-model</artifactId>
</dependency>
<!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
</dependencies>
</project>

@ -22,26 +22,28 @@ package com.tencent.cloud.common.constant;
* *
* @author skyehtzhang * @author skyehtzhang
*/ */
public interface ContextConstant { public final class ContextConstant {
/** /**
* Order of configuration modifier. * Order of configuration modifier.
*/ */
interface ModifierOrder { public static final class ModifierOrder {
/** /**
* First modifier order. * First modifier order.
*/ */
Integer FIRST = Integer.MIN_VALUE; public static Integer FIRST = Integer.MIN_VALUE;
/** /**
* Last modifier order. * Last modifier order.
*/ */
Integer LAST = Integer.MAX_VALUE; public static Integer LAST = Integer.MAX_VALUE;
/** /**
* Order of circuit breaker configuration modifier. * Order of circuit breaker configuration modifier.
*/ */
Integer CIRCUIT_BREAKER_ORDER = 1; public static Integer CIRCUIT_BREAKER_ORDER = 1;
}
}
} }

@ -0,0 +1,111 @@
/*
* 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.common.pojo;
import java.util.Map;
import java.util.Objects;
import com.netflix.loadbalancer.Server;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInstances;
import org.apache.commons.lang.StringUtils;
/**
* Polaris' implementation of {@link Server}.
*
* @author Haotian Zhang
*/
public class PolarisServer extends Server {
private final ServiceInstances serviceInstances;
private final Instance instance;
private final MetaInfo metaInfo;
public PolarisServer(ServiceInstances serviceInstances, Instance instance) {
super(instance.getHost(), instance.getPort());
if (StringUtils.equalsIgnoreCase(instance.getProtocol(), "https")) {
setSchemea("https");
}
else {
setSchemea("http");
}
this.serviceInstances = serviceInstances;
this.instance = instance;
this.metaInfo = new MetaInfo() {
@Override
public String getAppName() {
return instance.getService();
}
@Override
public String getServerGroup() {
return null;
}
@Override
public String getServiceIdForDiscovery() {
return instance.getService();
}
@Override
public String getInstanceId() {
return instance.getId();
}
};
}
public Instance getInstance() {
return instance;
}
@Override
public MetaInfo getMetaInfo() {
return metaInfo;
}
public Map<String, String> getMetadata() {
return instance.getMetadata();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
PolarisServer that = (PolarisServer) o;
return Objects.equals(instance, that.instance);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), instance);
}
public ServiceInstances getServiceInstances() {
return serviceInstances;
}
}

@ -0,0 +1,93 @@
/*
* 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.common.pojo;
import java.net.URI;
import java.util.Map;
import com.tencent.polaris.api.pojo.Instance;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
/**
* Polaris's implementation of {@link ServiceInstance}.
*
* @author Haotian Zhang
*/
public class PolarisServiceInstance implements ServiceInstance {
private final Instance instance;
private final boolean isSecure;
private final String scheme;
public PolarisServiceInstance(Instance instance) {
this.instance = instance;
this.isSecure = StringUtils.equalsIgnoreCase(instance.getProtocol(), "https");
if (isSecure) {
scheme = "https";
}
else {
scheme = "http";
}
}
@Override
public String getInstanceId() {
return ServiceInstance.super.getInstanceId();
}
@Override
public String getServiceId() {
return instance.getService();
}
@Override
public String getHost() {
return instance.getHost();
}
@Override
public int getPort() {
return instance.getPort();
}
@Override
public boolean isSecure() {
return this.isSecure;
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map<String, String> getMetadata() {
return instance.getMetadata();
}
@Override
public String getScheme() {
return this.scheme;
}
}

@ -23,47 +23,45 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* Spring Context Util * Spring Context Util.
* *
* @author Hongwei Zhu * @author Hongwei Zhu
*/ */
@Component @Component
public class ApplicationContextAwareUtils implements ApplicationContextAware { public class ApplicationContextAwareUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext; private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { /**
ApplicationContextAwareUtils.applicationContext = applicationContext; * Get application context.
} * @return application context
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/** public void setApplicationContext(ApplicationContext applicationContext)
* throws BeansException {
* ApplicationContextAwareUtils.applicationContext = applicationContext;
* @return Spring }
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/** /**
* Spring * Get application property.
* * @param key property name
* @param key * @return property value
* @return */
*/ public static String getProperties(String key) {
public static String getProperties(String key) { return applicationContext.getEnvironment().getProperty(key);
return applicationContext.getEnvironment().getProperty(key); }
}
/**
* Get application property. If null, return default.
* @param key property name
* @param defaultValue default value
* @return property value
*/
public static String getProperties(String key, String defaultValue) {
return applicationContext.getEnvironment().getProperty(key, defaultValue);
}
/**
* Spring<br>
*
*
* @param key
* @param defaultValue
* @return
*/
public static String getProperties(String key, String defaultValue) {
return applicationContext.getEnvironment().getProperty(key, defaultValue);
}
} }

@ -0,0 +1,84 @@
/*
* 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.common.util;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
/**
* Utils for Jackson.
*
* @author Haotian Zhang
*/
public final class JacksonUtils {
/**
* Object Mapper.
*/
public static final ObjectMapper OM = new ObjectMapper();
private static final Logger LOG = LoggerFactory.getLogger(JacksonUtils.class);
private JacksonUtils() {
}
/**
* Object to Json.
* @param object object to be serialized
* @param <T> type of object
* @return Json String
*/
public static <T> String serialize2Json(T object) {
try {
return OM.writeValueAsString(object);
}
catch (JsonProcessingException e) {
LOG.error("Object to Json failed. {}", object, e);
throw new RuntimeException("Object to Json failed.", e);
}
}
/**
* Json to Map.
* @param jsonStr Json String
* @return Map
*/
public static Map<String, String> deserialize2Map(String jsonStr) {
try {
if (StringUtils.hasText(jsonStr)) {
return OM.readValue(jsonStr, Map.class);
}
return new HashMap<>();
}
catch (JsonProcessingException e) {
LOG.error(
"Json to map failed. check if the format of the json string[{}] is correct.",
jsonStr, e);
throw new RuntimeException("Json to map failed.", e);
}
}
}

@ -20,24 +20,31 @@ package com.tencent.cloud.common.util;
import java.lang.reflect.Field; import java.lang.reflect.Field;
/** /**
* Reflection Utils * Reflection Utils.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class ReflectionUtils { public final class ReflectionUtils {
public static Object getFieldValue(Object instance, String fieldName) { private ReflectionUtils() {
Field field = org.springframework.util.ReflectionUtils.findField(instance.getClass(), fieldName);
}
field.setAccessible(true);
try { public static Object getFieldValue(Object instance, String fieldName) {
return field.get(instance); Field field = org.springframework.util.ReflectionUtils
} catch (IllegalAccessException e) { .findField(instance.getClass(), fieldName);
// ignore
} finally { field.setAccessible(true);
field.setAccessible(false); try {
} return field.get(instance);
return null; }
} catch (IllegalAccessException e) {
// ignore
}
finally {
field.setAccessible(false);
}
return null;
}
} }

@ -1,3 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.common.util.ApplicationContextAwareUtils com.tencent.cloud.common.util.ApplicationContextAwareUtils

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>spring-cloud-tencent</artifactId> <artifactId>spring-cloud-tencent</artifactId>
@ -44,11 +44,6 @@
<artifactId>spring-cloud-tencent-metadata</artifactId> <artifactId>spring-cloud-tencent-metadata</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-feign</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>

@ -121,24 +121,12 @@
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-gateway</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-metadata</artifactId> <artifactId>spring-cloud-tencent-metadata</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-feign</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>

@ -25,15 +25,15 @@ import org.springframework.web.bind.annotation.GetMapping;
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@FeignClient(name = "polaris-circuitbreaker-example-b", fallback = ProviderBFallback.class) @FeignClient(name = "polaris-circuitbreaker-example-b",
fallback = ProviderBFallback.class)
public interface ProviderB { public interface ProviderB {
/** /**
* B * Get info of service B.
* * @return info of service B
* @return */
*/ @GetMapping("/example/service/b/info")
@GetMapping("/example/service/b/info") String info();
String info();
} }

@ -27,9 +27,9 @@ import org.springframework.stereotype.Component;
@Component @Component
public class ProviderBFallback implements ProviderB { public class ProviderBFallback implements ProviderB {
@Override @Override
public String info() { public String info() {
return "trigger the refuse for service b"; return "trigger the refuse for service b";
} }
} }

@ -34,13 +34,13 @@ import org.springframework.web.client.RestTemplate;
@EnableFeignClients @EnableFeignClients
public class ServiceA { public class ServiceA {
@Bean @Bean
public RestTemplate restTemplate() { public RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(ServiceA.class); SpringApplication.run(ServiceA.class);
} }
} }

@ -33,41 +33,39 @@ import org.springframework.web.client.RestTemplate;
@RequestMapping("/example/service/a") @RequestMapping("/example/service/a")
public class ServiceAController { public class ServiceAController {
private final ProviderB polarisServiceB; private final ProviderB polarisServiceB;
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
public ServiceAController(ProviderB polarisServiceB, RestTemplate restTemplate) { public ServiceAController(ProviderB polarisServiceB, RestTemplate restTemplate) {
this.polarisServiceB = polarisServiceB; this.polarisServiceB = polarisServiceB;
this.restTemplate = restTemplate; this.restTemplate = restTemplate;
} }
/** /**
* *
* * @return
* @return */
* @throws Exception @GetMapping("/info")
*/ public String info() {
@GetMapping("/info") return "hello world ! I'am a service";
public String info() throws Exception { }
return "hello world ! I'am a service";
}
/** /**
* B * B
* * @return B
* @return B */
* @throws Exception @GetMapping("/getBServiceInfo")
*/ public String getBServiceInfo() {
@GetMapping("/getBServiceInfo") return polarisServiceB.info();
public String getBServiceInfo() throws Exception { }
return polarisServiceB.info();
}
@RequestMapping(value = "/testRest", method = RequestMethod.GET) @RequestMapping(value = "/testRest", method = RequestMethod.GET)
public String testRest() { public String testRest() {
ResponseEntity<String> entity = restTemplate.getForEntity("http://polaris-circuitbreaker-example-b/example/service/b/info", String.class); ResponseEntity<String> entity = restTemplate.getForEntity(
return entity.getBody(); "http://polaris-circuitbreaker-example-b/example/service/b/info",
} String.class);
return entity.getBody();
}
} }

@ -37,4 +37,4 @@ ribbon:
enabled: on enabled: on
serivceB: serivceB:
url: http://localhost:48081 url: http://localhost:48081

@ -264,11 +264,8 @@ consumer:
metricStatTimeWindow: 100ms metricStatTimeWindow: 100ms
#描述:基于周期错误率的熔断策略配置 #描述:基于周期错误率的熔断策略配置
errorRate: errorRate:
#描述:触发错误率熔断的阈值 #描述:触发错误率熔断的阈值百分比
#类型:double errorRateThreshold: 50
#范围:(0:1]
#默认值:0.5
errorRateThreshold: 0.01
#描述:错误率熔断的最小统计单元数量 #描述:错误率熔断的最小统计单元数量
#类型:int #类型:int
#范围:[1:...] #范围:[1:...]

@ -25,15 +25,15 @@ import org.springframework.web.bind.annotation.GetMapping;
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@FeignClient(name = "polaris-circuitbreaker-example-a", fallback = ProviderAFallback.class) @FeignClient(name = "polaris-circuitbreaker-example-a",
fallback = ProviderAFallback.class)
public interface ProviderA { public interface ProviderA {
/** /**
* B * Get info of service A.
* * @return info of service A
* @return */
*/ @GetMapping("/example/service/a/info")
@GetMapping("/example/service/a/info") String info();
String info();
} }

@ -27,8 +27,9 @@ import org.springframework.stereotype.Component;
@Component @Component
public class ProviderAFallback implements ProviderA { public class ProviderAFallback implements ProviderA {
@Override @Override
public String info() { public String info() {
return "trigger the refuse for service a"; return "trigger the refuse for service a";
} }
} }

@ -34,13 +34,13 @@ import org.springframework.web.client.RestTemplate;
@EnableFeignClients @EnableFeignClients
public class ServiceB { public class ServiceB {
@Bean public static void main(String[] args) {
public RestTemplate restTemplate() { SpringApplication.run(ServiceB.class);
return new RestTemplate(); }
}
public static void main(String[] args) { @Bean
SpringApplication.run(ServiceB.class); public RestTemplate restTemplate() {
} return new RestTemplate();
}
} }

@ -33,43 +33,40 @@ import org.springframework.web.client.RestTemplate;
@RequestMapping("/example/service/b") @RequestMapping("/example/service/b")
public class ServiceBController { public class ServiceBController {
private final ProviderA polarisServiceA; private final ProviderA polarisServiceA;
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
public ServiceBController(ProviderA polarisServiceA, RestTemplate restTemplate) { public ServiceBController(ProviderA polarisServiceA, RestTemplate restTemplate) {
this.polarisServiceA = polarisServiceA; this.polarisServiceA = polarisServiceA;
this.restTemplate = restTemplate; this.restTemplate = restTemplate;
} }
/** /**
* *
* * @return
* @return */
* @throws Exception @GetMapping("/info")
*/ public String info() {
@GetMapping("/info") // return "hello world ! I'am a service";
public String info() throws Exception { throw new RuntimeException("failed for call my service");
// return "hello world ! I'am a service"; }
throw new RuntimeException("failed for call my service");
}
/** /**
* B * B
* * @return B
* @return B */
* @throws Exception @GetMapping("/getAServiceInfo")
*/ public String getAServiceInfo() {
@GetMapping("/getAServiceInfo") return polarisServiceA.info();
public String getAServiceInfo() throws Exception { }
return polarisServiceA.info();
}
@RequestMapping(value = "/testRest", method = RequestMethod.GET)
public String testRest() {
ResponseEntity<String> entity = restTemplate.getForEntity("http://polaris-circuitbreaker-example-a/example/service/b/info", String.class);
return entity.getBody();
}
@RequestMapping(value = "/testRest", method = RequestMethod.GET)
public String testRest() {
ResponseEntity<String> entity = restTemplate.getForEntity(
"http://polaris-circuitbreaker-example-a/example/service/b/info",
String.class);
return entity.getBody();
}
} }

@ -37,4 +37,4 @@ ribbon:
enabled: on enabled: on
serivceB: serivceB:
url: http://localhost:48081 url: http://localhost:48081

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>polaris-discovery-example</artifactId> <artifactId>polaris-discovery-example</artifactId>
@ -19,15 +19,15 @@
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
</dependency> </dependency>
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>--> <!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>--> <!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>-->
<!-- </dependency>--> <!-- </dependency>-->
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>--> <!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>--> <!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!-- </dependency>--> <!-- </dependency>-->
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

@ -17,6 +17,9 @@
package com.tencent.cloud.polaris.discovery.service.callee; package com.tencent.cloud.polaris.discovery.service.callee;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -29,26 +32,27 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/discovery/service/callee") @RequestMapping("/discovery/service/callee")
public class DiscoveryCalleeController { public class DiscoveryCalleeController {
/** private static Logger logger = LoggerFactory
* .getLogger(DiscoveryCalleeController.class);
*
* @return /**
*/ *
@GetMapping("/info") * @return
public String info() { */
return "Discovery Service Callee"; @GetMapping("/info")
} public String info() {
return "Discovery Service Callee";
/** }
*
* /**
* @param value1 1 *
* @param value2 2 * @param value1 1
* @return * @param value2 2
*/ * @return
@GetMapping("/sum") */
public int sum(@RequestParam int value1, @RequestParam int value2) { @GetMapping("/sum")
return value1 + value2; public int sum(@RequestParam int value1, @RequestParam int value2) {
} return value1 + value2;
}
} }

@ -19,14 +19,17 @@ package com.tencent.cloud.polaris.discovery.service.callee;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
/** /**
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@SpringBootApplication @SpringBootApplication
@RestController
public class DiscoveryCalleeService { public class DiscoveryCalleeService {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(DiscoveryCalleeService.class, args); SpringApplication.run(DiscoveryCalleeService.class, args);
} }
} }

@ -7,18 +7,20 @@ spring:
cloud: cloud:
polaris: polaris:
address: grpc://127.0.0.1:8091 address: grpc://127.0.0.1:8091
consul:
port: 8500
host: 127.0.0.1
enabled: true
discovery: discovery:
register: true ip-address: 127.0.0.1
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} # consul:
enabled: true # port: 8500
service-name: ${spring.application.name} # host: 127.0.0.1
ip-address: localhost # enabled: true
prefer-ip-address: true # discovery:
eureka: # register: true
client: # instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
serviceUrl: # enabled: true
defaultZone: http://127.0.0.1:7654/eureka/ # service-name: ${spring.application.name}
# ip-address: localhost
# prefer-ip-address: true
#eureka:
# client:
# serviceUrl:
# defaultZone: http://127.0.0.1:7654/eureka/

@ -12,4 +12,4 @@ log4j.appender.FILE.File=applog/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log
log4j.appender.FILE.Threshold=INFO log4j.appender.FILE.Threshold=INFO
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} [%5p] - %c -%F(%L) -%m%n log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.FILE.MaxFileSize=10MB log4j.appender.FILE.MaxFileSize=10MB

@ -3,4 +3,4 @@ global:
discoverCluster: discoverCluster:
sameAsBuiltin: true sameAsBuiltin: true
healthCheckCluster: healthCheckCluster:
sameAsBuiltin: true sameAsBuiltin: true

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>polaris-discovery-example</artifactId> <artifactId>polaris-discovery-example</artifactId>
@ -23,20 +23,20 @@
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
</dependency> </dependency>
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>--> <!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>--> <!-- <artifactId>spring-cloud-starter-consul-discovery</artifactId>-->
<!-- </dependency>--> <!-- </dependency>-->
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>--> <!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>--> <!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!-- </dependency>--> <!-- </dependency>-->
<dependency> <!-- <dependency>-->
<groupId>com.tencent.cloud</groupId> <!-- <groupId>com.tencent.cloud</groupId>-->
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId> <!-- <artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>-->
</dependency> <!-- </dependency>-->
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

@ -24,16 +24,17 @@ import org.springframework.web.bind.annotation.RequestParam;
/** /**
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@FeignClient(value = "DiscoveryCalleeService", fallback = DiscoveryCalleeServiceCallback.class) @FeignClient(value = "DiscoveryCalleeService",
fallback = DiscoveryCalleeServiceCallback.class)
public interface DiscoveryCalleeService { public interface DiscoveryCalleeService {
/** /**
* *
* * @param value1 1
* @param value1 1 * @param value2 2
* @param value2 2 * @return
* @return */
*/ @GetMapping("/discovery/service/callee/sum")
@GetMapping("/discovery/service/callee/sum") int sum(@RequestParam("value1") int value1, @RequestParam("value2") int value2);
int sum(@RequestParam("value1") final int value1, @RequestParam("value2") final int value2);
} }

@ -25,8 +25,9 @@ import org.springframework.stereotype.Component;
@Component @Component
public class DiscoveryCalleeServiceCallback implements DiscoveryCalleeService { public class DiscoveryCalleeServiceCallback implements DiscoveryCalleeService {
@Override @Override
public int sum(int value1, int value2) { public int sum(int value1, int value2) {
return 0; return 0;
} }
} }

@ -31,42 +31,41 @@ import org.springframework.web.client.RestTemplate;
@RequestMapping("/discovery/service/caller") @RequestMapping("/discovery/service/caller")
public class DiscoveryCallerController { public class DiscoveryCallerController {
@Autowired @Autowired
private RestTemplate restTemplate; private RestTemplate restTemplate;
@Autowired @Autowired
private DiscoveryCalleeService discoveryCalleeService; private DiscoveryCalleeService discoveryCalleeService;
/** /**
* *
* * @param value1 1
* @param value1 1 * @param value2 2
* @param value2 2 * @return
* @return */
*/ @GetMapping("/feign")
@GetMapping("/feign") public int feign(@RequestParam int value1, @RequestParam int value2) {
public int feign(@RequestParam int value1, @RequestParam int value2) { return discoveryCalleeService.sum(value1, value2);
return discoveryCalleeService.sum(value1, value2); }
}
/** /**
* *
* * @return
* @return */
*/ @GetMapping("/rest")
@GetMapping("/rest") public String rest() {
public String rest() { return restTemplate.getForObject(
return restTemplate.getForObject("http://DiscoveryCalleeService/discovery/service/callee/info", String.class); "http://DiscoveryCalleeService/discovery/service/callee/info",
} String.class);
}
/**
* health check
* @return
*/
@GetMapping("/healthCheck")
public String healthCheck() {
return "pk ok";
}
/**
* health check
*
* @return
*/
@GetMapping("/healthCheck")
public String healthCheck() {
return "pk ok";
}
} }

@ -33,14 +33,14 @@ import org.springframework.web.client.RestTemplate;
@EnableFeignClients @EnableFeignClients
public class DiscoveryCallerService { public class DiscoveryCallerService {
@Bean public static void main(String[] args) {
@LoadBalanced SpringApplication.run(DiscoveryCallerService.class, args);
public RestTemplate restTemplate() { }
return new RestTemplate();
}
public static void main(String[] args) { @Bean
SpringApplication.run(DiscoveryCallerService.class, args); @LoadBalanced
} public RestTemplate restTemplate() {
return new RestTemplate();
}
} }

@ -11,20 +11,20 @@ spring:
heartbeat: heartbeat:
enabled: true enabled: true
health-check-url: /discovery/service/caller/healthCheck health-check-url: /discovery/service/caller/healthCheck
consul: # consul:
port: 8500 # port: 8500
host: 127.0.0.1 # host: 127.0.0.1
enabled: true # enabled: true
discovery: # discovery:
register: true # register: true
health-check-path: /actuator/health # health-check-path: /actuator/health
health-check-interval: 10s # health-check-interval: 10s
instance-id: ${spring.application.name}:${server.port} # instance-id: ${spring.application.name}:${server.port}
enabled: true # enabled: true
service-name: ${spring.application.name} # service-name: ${spring.application.name}
ip-address: localhost # ip-address: localhost
prefer-ip-address: true # prefer-ip-address: true
eureka: #eureka:
client: # client:
serviceUrl: # serviceUrl:
defaultZone: http://127.0.0.1:7654/eureka/ # defaultZone: http://127.0.0.1:7654/eureka/

@ -12,4 +12,4 @@ log4j.appender.FILE.File=applog/%d{yyyy-MM-dd}/%d{yyyy-MM-dd}.log
log4j.appender.FILE.Threshold=INFO log4j.appender.FILE.Threshold=INFO
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} [%5p] - %c -%F(%L) -%m%n log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.FILE.MaxFileSize=10MB log4j.appender.FILE.MaxFileSize=10MB

@ -3,4 +3,4 @@ global:
discoverCluster: discoverCluster:
sameAsBuiltin: true sameAsBuiltin: true
healthCheckCluster: healthCheckCluster:
sameAsBuiltin: true sameAsBuiltin: true

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save