support spring cloud gateway routers (#230)
parent
30f57c527d
commit
68620d9f8f
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||||
|
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||||
|
import com.tencent.cloud.polaris.router.feign.PolarisCachingSpringLoadBalanceFactory;
|
||||||
|
import com.tencent.cloud.polaris.router.feign.RouterLabelFeignInterceptor;
|
||||||
|
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||||
|
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* configuration for feign singleton components.
|
||||||
|
* Feign-related components need to be loaded only in the feign environment.
|
||||||
|
*@author lepdou 2022-06-10
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnClass(name = {"org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer"})
|
||||||
|
@RibbonClients(defaultConfiguration = {FeignLoadBalancerConfiguration.class})
|
||||||
|
public class FeignAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List<RouterLabelResolver> routerLabelResolvers,
|
||||||
|
MetadataLocalProperties metadataLocalProperties,
|
||||||
|
RouterRuleLabelResolver routerRuleLabelResolver) {
|
||||||
|
return new RouterLabelFeignInterceptor(routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PolarisCachingSpringLoadBalanceFactory polarisCachingSpringLoadBalanceFactory(SpringClientFactory factory) {
|
||||||
|
return new PolarisCachingSpringLoadBalanceFactory(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* 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.scg;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||||
|
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||||
|
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||||
|
import com.tencent.cloud.common.util.ExpressionLabelUtils;
|
||||||
|
import com.tencent.cloud.polaris.router.PolarisRouterContext;
|
||||||
|
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||||
|
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
|
||||||
|
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
|
||||||
|
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the default LoadBalancerClientFilter implementation.
|
||||||
|
*@author lepdou 2022-06-10
|
||||||
|
*/
|
||||||
|
public class PolarisLoadBalancerClientFilter extends LoadBalancerClientFilter {
|
||||||
|
private final static Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancerClientFilter.class);
|
||||||
|
|
||||||
|
private final MetadataLocalProperties metadataLocalProperties;
|
||||||
|
private final RouterRuleLabelResolver routerRuleLabelResolver;
|
||||||
|
private final List<RouterLabelResolver> routerLabelResolvers;
|
||||||
|
|
||||||
|
private final boolean isRibbonLoadBalanceClient;
|
||||||
|
|
||||||
|
public PolarisLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties,
|
||||||
|
MetadataLocalProperties metadataLocalProperties,
|
||||||
|
RouterRuleLabelResolver routerRuleLabelResolver,
|
||||||
|
List<RouterLabelResolver> routerLabelResolvers) {
|
||||||
|
super(loadBalancer, properties);
|
||||||
|
this.metadataLocalProperties = metadataLocalProperties;
|
||||||
|
this.routerRuleLabelResolver = routerRuleLabelResolver;
|
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
|
||||||
|
routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
|
||||||
|
this.routerLabelResolvers = routerLabelResolvers;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.routerLabelResolvers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServiceInstance choose(ServerWebExchange exchange) {
|
||||||
|
String peerServiceName = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
|
||||||
|
|
||||||
|
if (isRibbonLoadBalanceClient) {
|
||||||
|
// Pass routing context to ribbon load balancer
|
||||||
|
PolarisRouterContext routerContext = genRouterContext(exchange, peerServiceName);
|
||||||
|
return ((RibbonLoadBalancerClient) loadBalancer).choose(peerServiceName, routerContext);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return loadBalancer.choose(peerServiceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PolarisRouterContext genRouterContext(ServerWebExchange exchange, String peerServiceName) {
|
||||||
|
// local service labels
|
||||||
|
Map<String, String> labels = new HashMap<>(metadataLocalProperties.getContent());
|
||||||
|
|
||||||
|
// labels from rule expression
|
||||||
|
Map<String, String> ruleExpressionLabels = getExpressionLabels(exchange, peerServiceName);
|
||||||
|
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
|
||||||
|
labels.putAll(ruleExpressionLabels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// labels from request
|
||||||
|
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
|
||||||
|
routerLabelResolvers.forEach(resolver -> {
|
||||||
|
try {
|
||||||
|
Map<String, String> customResolvedLabels = resolver.resolve(exchange);
|
||||||
|
if (!CollectionUtils.isEmpty(customResolvedLabels)) {
|
||||||
|
labels.putAll(customResolvedLabels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// labels from downstream
|
||||||
|
Map<String, String> transitiveLabels = MetadataContextHolder.get()
|
||||||
|
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
|
||||||
|
labels.putAll(transitiveLabels);
|
||||||
|
|
||||||
|
PolarisRouterContext routerContext = new PolarisRouterContext();
|
||||||
|
|
||||||
|
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels);
|
||||||
|
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels);
|
||||||
|
|
||||||
|
return routerContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getExpressionLabels(ServerWebExchange exchange, String peerServiceName) {
|
||||||
|
Set<String> labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
|
||||||
|
MetadataContext.LOCAL_SERVICE, peerServiceName);
|
||||||
|
|
||||||
|
if (CollectionUtils.isEmpty(labelKeys)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExpressionLabelUtils.resolve(exchange, labelKeys);
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
com.tencent.cloud.polaris.router.config.RouterAutoConfiguration
|
com.tencent.cloud.polaris.router.config.RouterAutoConfiguration,\
|
||||||
|
com.tencent.cloud.polaris.router.config.FeignAutoConfiguration
|
||||||
|
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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.scg;
|
||||||
|
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||||
|
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||||
|
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||||
|
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
|
||||||
|
import com.tencent.cloud.polaris.router.PolarisRouterContext;
|
||||||
|
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||||
|
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
|
||||||
|
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||||
|
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||||
|
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test for ${@link PolarisLoadBalancerClientFilter}
|
||||||
|
*@author lepdou 2022-06-13
|
||||||
|
*/
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class PolarisLoadBalancerClientFilterTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MetadataLocalProperties metadataLocalProperties;
|
||||||
|
@Mock
|
||||||
|
private RouterRuleLabelResolver routerRuleLabelResolver;
|
||||||
|
@Mock
|
||||||
|
private RouterLabelResolver routerLabelResolver;
|
||||||
|
@Mock
|
||||||
|
private LoadBalancerClient loadBalancerClient;
|
||||||
|
@Mock
|
||||||
|
private LoadBalancerProperties loadBalancerProperties;
|
||||||
|
|
||||||
|
private static final String callerService = "callerService";
|
||||||
|
private static final String calleeService = "calleeService";
|
||||||
|
|
||||||
|
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
|
||||||
|
private static MockedStatic<MetadataContextHolder> mockedMetadataContextHolder;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() {
|
||||||
|
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
|
||||||
|
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
|
||||||
|
.thenReturn(callerService);
|
||||||
|
|
||||||
|
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
|
||||||
|
|
||||||
|
// mock transitive metadata
|
||||||
|
Map<String, String> transitiveLabels = new HashMap<>();
|
||||||
|
transitiveLabels.put("t1", "v1");
|
||||||
|
transitiveLabels.put("t2", "v2");
|
||||||
|
when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
|
||||||
|
|
||||||
|
mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class);
|
||||||
|
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() {
|
||||||
|
mockedApplicationContextAwareUtils.close();
|
||||||
|
mockedMetadataContextHolder.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenRouterContext() {
|
||||||
|
PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter(
|
||||||
|
loadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver,
|
||||||
|
Lists.newArrayList(routerLabelResolver));
|
||||||
|
|
||||||
|
Map<String, String> localMetadata = new HashMap<>();
|
||||||
|
localMetadata.put("env", "blue");
|
||||||
|
when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
|
||||||
|
|
||||||
|
Set<String> expressionLabelKeys = Sets.newHashSet("${http.header.k1}", "${http.query.userid}");
|
||||||
|
when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString())).thenReturn(expressionLabelKeys);
|
||||||
|
|
||||||
|
MockServerHttpRequest request = MockServerHttpRequest.get("/" + calleeService + "/users")
|
||||||
|
.header("k1", "v1")
|
||||||
|
.queryParam("userid", "zhangsan")
|
||||||
|
.build();
|
||||||
|
MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
|
||||||
|
|
||||||
|
Map<String, String> customMetadata = new HashMap<>();
|
||||||
|
customMetadata.put("k2", "v2");
|
||||||
|
when(routerLabelResolver.resolve(webExchange)).thenReturn(customMetadata);
|
||||||
|
|
||||||
|
PolarisRouterContext routerContext = polarisLoadBalancerClientFilter.genRouterContext(webExchange, calleeService);
|
||||||
|
|
||||||
|
Map<String, String> routerLabels = routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS);
|
||||||
|
Assert.assertEquals("v1", routerLabels.get("${http.header.k1}"));
|
||||||
|
Assert.assertEquals("zhangsan", routerLabels.get("${http.query.userid}"));
|
||||||
|
Assert.assertEquals("blue", routerLabels.get("env"));
|
||||||
|
Assert.assertEquals("v1", routerLabels.get("t1"));
|
||||||
|
Assert.assertEquals("v2", routerLabels.get("t2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChooseInstanceWithoutRibbon() {
|
||||||
|
PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter(
|
||||||
|
loadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver,
|
||||||
|
Lists.newArrayList(routerLabelResolver));
|
||||||
|
|
||||||
|
String url = "/" + calleeService + "/users";
|
||||||
|
MockServerHttpRequest request = MockServerHttpRequest.get(url)
|
||||||
|
.header("k1", "v1")
|
||||||
|
.queryParam("userid", "zhangsan")
|
||||||
|
.build();
|
||||||
|
MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
|
||||||
|
webExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://" + calleeService + "/users"));
|
||||||
|
|
||||||
|
polarisLoadBalancerClientFilter.choose(webExchange);
|
||||||
|
|
||||||
|
verify(loadBalancerClient).choose(calleeService);
|
||||||
|
verify(metadataLocalProperties, times(0)).getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChooseInstanceWithRibbon() {
|
||||||
|
RibbonLoadBalancerClient ribbonLoadBalancerClient = Mockito.mock(RibbonLoadBalancerClient.class);
|
||||||
|
|
||||||
|
PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter(
|
||||||
|
ribbonLoadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver,
|
||||||
|
Lists.newArrayList(routerLabelResolver));
|
||||||
|
|
||||||
|
Map<String, String> localMetadata = new HashMap<>();
|
||||||
|
localMetadata.put("env", "blue");
|
||||||
|
when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
|
||||||
|
|
||||||
|
Set<String> expressionLabelKeys = Sets.newHashSet("${http.header.k1}", "${http.query.userid}");
|
||||||
|
when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString())).thenReturn(expressionLabelKeys);
|
||||||
|
|
||||||
|
String url = "/" + calleeService + "/users";
|
||||||
|
MockServerHttpRequest request = MockServerHttpRequest.get(url)
|
||||||
|
.header("k1", "v1")
|
||||||
|
.queryParam("userid", "zhangsan")
|
||||||
|
.build();
|
||||||
|
MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
|
||||||
|
webExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://" + calleeService + "/users"));
|
||||||
|
|
||||||
|
Map<String, String> customMetadata = new HashMap<>();
|
||||||
|
customMetadata.put("k2", "v2");
|
||||||
|
when(routerLabelResolver.resolve(webExchange)).thenReturn(customMetadata);
|
||||||
|
|
||||||
|
polarisLoadBalancerClientFilter.choose(webExchange);
|
||||||
|
|
||||||
|
verify(ribbonLoadBalancerClient).choose(anyString(), any());
|
||||||
|
verify(metadataLocalProperties, times(1)).getContent();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>polaris-gateway-example</artifactId>
|
||||||
|
<groupId>com.tencent.cloud</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>gateway-callee-service2</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tencent.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.gateway.example.callee;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gateway callee application.
|
||||||
|
*
|
||||||
|
* @author Haotian Zhang
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
public class GatewayCalleeApplication2 {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(GatewayCalleeApplication2.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.gateway.example.callee;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
|
||||||
|
import com.tencent.cloud.common.constant.MetadataConstant;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gateway callee controller.
|
||||||
|
*
|
||||||
|
* @author Haotian Zhang
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/gateway/example/callee")
|
||||||
|
public class GatewayCalleeController {
|
||||||
|
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(GatewayCalleeController.class);
|
||||||
|
|
||||||
|
@Value("${server.port:0}")
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information of callee.
|
||||||
|
* @return information of callee
|
||||||
|
*/
|
||||||
|
@RequestMapping("/info")
|
||||||
|
public String info() {
|
||||||
|
LOG.info("Gateway Example Callee [{}] is called.", port);
|
||||||
|
return String.format("Gateway Example Callee [%s] is called.", port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get metadata in HTTP header.
|
||||||
|
*
|
||||||
|
* @param metadataStr metadata string
|
||||||
|
* @return metadata in HTTP header
|
||||||
|
* @throws UnsupportedEncodingException encoding exception
|
||||||
|
*/
|
||||||
|
@RequestMapping("/echo")
|
||||||
|
public String echoHeader(
|
||||||
|
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String metadataStr)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
LOG.info(URLDecoder.decode(metadataStr, "UTF-8"));
|
||||||
|
return URLDecoder.decode(metadataStr, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
server:
|
||||||
|
session-timeout: 1800
|
||||||
|
port: 48082
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: GatewayCalleeService
|
||||||
|
cloud:
|
||||||
|
tencent:
|
||||||
|
metadata:
|
||||||
|
content:
|
||||||
|
env: green
|
||||||
|
polaris:
|
||||||
|
address: grpc://183.47.111.80:8091
|
||||||
|
namespace: default
|
Loading…
Reference in new issue