Merge branch 'apache-3.2' into apache-3.3
# Conflicts: # dubbo-build-tools/pom.xml # dubbo-dependencies-bom/pom.xml # dubbo-dependencies/dubbo-dependencies-zookeeper-curator5/pom.xml # dubbo-kubernetes/pom.xml # dubbo-native-plugin/pom.xml # dubbo-xds/pom.xml # dubbo-xds/src/test/java/org/apache/dubbo/rpc/cluster/router/xds/XdsRouteTest.java # pom.xml
This commit is contained in:
commit
0fccd2ccae
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.common.constants;
|
||||
|
||||
/**
|
||||
* Provider Constants
|
||||
*/
|
||||
public interface ProviderConstants {
|
||||
|
||||
/**
|
||||
* Default prefer serialization,multiple separated by commas
|
||||
*/
|
||||
String DEFAULT_PREFER_SERIALIZATION = "fastjson2,hessian2";
|
||||
}
|
||||
|
|
@ -14,10 +14,8 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util;
|
||||
package org.apache.dubbo.common.serialization;
|
||||
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
public interface XdsListener {
|
||||
void process(DiscoveryResponse discoveryResponse);
|
||||
public interface PreferSerializationProvider {
|
||||
String getPreferSerialization();
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.dubbo.config;
|
||||
|
||||
import org.apache.dubbo.common.serialization.PreferSerializationProvider;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.config.support.Parameter;
|
||||
|
|
@ -30,7 +31,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.JSON_CHECK_LEVEL
|
|||
import static org.apache.dubbo.common.constants.CommonConstants.SSL_ENABLED_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.THREAD_POOL_EXHAUSTED_LISTENERS_KEY;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
|
||||
import static org.apache.dubbo.common.constants.ProviderConstants.DEFAULT_PREFER_SERIALIZATION;
|
||||
|
||||
/**
|
||||
* Configuration for the protocol.
|
||||
|
|
@ -270,7 +270,12 @@ public class ProtocolConfig extends AbstractConfig {
|
|||
}
|
||||
|
||||
if (StringUtils.isBlank(preferSerialization)) {
|
||||
preferSerialization = serialization != null ? serialization : DEFAULT_PREFER_SERIALIZATION;
|
||||
preferSerialization = serialization != null
|
||||
? serialization
|
||||
: getScopeModel()
|
||||
.getBeanFactory()
|
||||
.getBean(PreferSerializationProvider.class)
|
||||
.getPreferSerialization();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.dubbo.config.context;
|
||||
|
||||
import org.apache.dubbo.common.serialization.PreferSerializationProvider;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.config.ApplicationConfig;
|
||||
import org.apache.dubbo.config.ConfigCenterConfig;
|
||||
|
|
@ -27,6 +28,7 @@ import org.apache.dubbo.config.ProtocolConfig;
|
|||
import org.apache.dubbo.config.ProviderConfig;
|
||||
import org.apache.dubbo.config.RegistryConfig;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
|
@ -64,6 +66,7 @@ class ConfigManagerTest {
|
|||
ApplicationModel applicationModel = ApplicationModel.defaultModel();
|
||||
configManager = applicationModel.getApplicationConfigManager();
|
||||
moduleConfigManager = applicationModel.getDefaultModule().getConfigManager();
|
||||
FrameworkModel.defaultModel().getBeanFactory().registerBean(TestPreferSerializationProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -474,4 +477,11 @@ class ConfigManagerTest {
|
|||
Assertions.assertFalse(moduleConfigManager.getProviders().isEmpty());
|
||||
Assertions.assertFalse(moduleConfigManager.getConsumers().isEmpty());
|
||||
}
|
||||
|
||||
public static class TestPreferSerializationProvider implements PreferSerializationProvider {
|
||||
@Override
|
||||
public String getPreferSerialization() {
|
||||
return "fastjson2,hessian2";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,13 @@ import java.util.Map;
|
|||
|
||||
import com.alibaba.dubbo.config.ReferenceConfig;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class GenericServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
@BeforeAll
|
||||
public static void beforeAll() {
|
||||
DubboBootstrap.reset();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.apache.dubbo.common.constants.ProviderConstants.DEFAULT_PREFER_SERIALIZATION;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
|
|
@ -43,6 +42,7 @@ class ProtocolConfigTest {
|
|||
@BeforeEach
|
||||
public void setUp() {
|
||||
DubboBootstrap.reset();
|
||||
// FrameworkModel.defaultModel().getBeanFactory().registerBean(TestPreferSerializationProvider.class);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
|
@ -389,7 +389,7 @@ class ProtocolConfigTest {
|
|||
assertNull(protocolConfig.getPreferSerialization());
|
||||
|
||||
protocolConfig.checkDefault();
|
||||
assertThat(protocolConfig.getPreferSerialization(), equalTo(DEFAULT_PREFER_SERIALIZATION));
|
||||
assertThat(protocolConfig.getPreferSerialization(), equalTo("fastjson2,hessian2"));
|
||||
|
||||
protocolConfig = new ProtocolConfig();
|
||||
protocolConfig.setSerialization("x-serialization");
|
||||
|
|
@ -405,7 +405,7 @@ class ProtocolConfigTest {
|
|||
assertNull(protocolConfig.getPreferSerialization());
|
||||
|
||||
protocolConfig.refresh();
|
||||
assertThat(protocolConfig.getPreferSerialization(), equalTo(DEFAULT_PREFER_SERIALIZATION));
|
||||
assertThat(protocolConfig.getPreferSerialization(), equalTo("fastjson2,hessian2"));
|
||||
|
||||
protocolConfig = new ProtocolConfig();
|
||||
protocolConfig.setSerialization("x-serialization");
|
||||
|
|
|
|||
|
|
@ -14,15 +14,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds;
|
||||
package org.apache.dubbo.config.utils;
|
||||
|
||||
public final class XdsInitializationException extends Exception {
|
||||
import org.apache.dubbo.common.serialization.PreferSerializationProvider;
|
||||
|
||||
public XdsInitializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public XdsInitializationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
public class TestPreferSerializationProvider implements PreferSerializationProvider {
|
||||
@Override
|
||||
public String getPreferSerialization() {
|
||||
return "fastjson2,hessian2";
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
<description>The Apollo implementation of the configcenter api</description>
|
||||
<properties>
|
||||
<skip_maven_deploy>false</skip_maven_deploy>
|
||||
<apollo_mock_server_version>2.1.0</apollo_mock_server_version>
|
||||
<apollo_mock_server_version>2.2.0</apollo_mock_server_version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@
|
|||
<httpclient_version>4.5.14</httpclient_version>
|
||||
<httpcore_version>4.4.16</httpcore_version>
|
||||
<fastjson_version>1.2.83</fastjson_version>
|
||||
<fastjson2_version>2.0.42</fastjson2_version>
|
||||
<fastjson2_version>2.0.43</fastjson2_version>
|
||||
<zookeeper_version>3.7.2</zookeeper_version>
|
||||
<curator_version>5.1.0</curator_version>
|
||||
<curator_test_version>2.12.0</curator_test_version>
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
<consul_process_version>2.2.1</consul_process_version>
|
||||
<consul_client_version>1.5.3</consul_client_version>
|
||||
<xmemcached_version>1.4.3</xmemcached_version>
|
||||
<cxf_version>3.5.5</cxf_version>
|
||||
<cxf_version>3.6.2</cxf_version>
|
||||
<thrift_version>0.19.0</thrift_version>
|
||||
<hessian_version>4.0.66</hessian_version>
|
||||
<protobuf-java_version>3.25.1</protobuf-java_version>
|
||||
|
|
@ -126,10 +126,10 @@
|
|||
<kryo_version>4.0.3</kryo_version>
|
||||
<kryo_serializers_version>0.45</kryo_serializers_version>
|
||||
<fst_version>2.57</fst_version>
|
||||
<avro_version>1.11.1</avro_version>
|
||||
<avro_version>1.11.3</avro_version>
|
||||
<apollo_client_version>2.1.0</apollo_client_version>
|
||||
<snakeyaml_version>2.2</snakeyaml_version>
|
||||
<commons_lang3_version>3.12.0</commons_lang3_version>
|
||||
<commons_lang3_version>3.14.0</commons_lang3_version>
|
||||
<protostuff_version>1.8.0</protostuff_version>
|
||||
<envoy_api_version>0.1.35</envoy_api_version>
|
||||
<micrometer.version>1.12.0</micrometer.version>
|
||||
|
|
@ -146,12 +146,12 @@
|
|||
<rs_api_version>2.1.1</rs_api_version>
|
||||
<resteasy_version>3.15.6.Final</resteasy_version>
|
||||
<codehaus-jackson_version>1.9.13</codehaus-jackson_version>
|
||||
<tomcat_embed_version>8.5.87</tomcat_embed_version>
|
||||
<tomcat_embed_version>8.5.96</tomcat_embed_version>
|
||||
<jetcd_version>0.7.6</jetcd_version>
|
||||
<nacos_version>2.2.4</nacos_version>
|
||||
<sentinel.version>1.8.6</sentinel.version>
|
||||
<seata.version>1.6.1</seata.version>
|
||||
<grpc.version>1.59.0</grpc.version>
|
||||
<grpc.version>1.60.0</grpc.version>
|
||||
<grpc_contrib_verdion>0.8.1</grpc_contrib_verdion>
|
||||
<jprotoc_version>1.2.2</jprotoc_version>
|
||||
<mustache_version>0.9.10</mustache_version>
|
||||
|
|
@ -200,8 +200,8 @@
|
|||
<mortbay_jetty_version>6.1.26</mortbay_jetty_version>
|
||||
<portlet_version>2.0</portlet_version>
|
||||
<maven_flatten_version>1.5.0</maven_flatten_version>
|
||||
<commons_compress_version>1.23.0</commons_compress_version>
|
||||
<spotless-maven-plugin.version>2.41.0</spotless-maven-plugin.version>
|
||||
<commons_compress_version>1.25.0</commons_compress_version>
|
||||
<spotless-maven-plugin.version>2.41.1</spotless-maven-plugin.version>
|
||||
<spotless.action>check</spotless.action>
|
||||
<dubbo-shared-resources.version>1.0.0</dubbo-shared-resources.version>
|
||||
<palantirJavaFormat.version>2.38.0</palantirJavaFormat.version>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
<slf4j_version>1.7.36</slf4j_version>
|
||||
<curator5_version>5.1.0</curator5_version>
|
||||
<zookeeper_version>3.8.3</zookeeper_version>
|
||||
<spotless-maven-plugin.version>2.41.0</spotless-maven-plugin.version>
|
||||
<spotless-maven-plugin.version>2.41.1</spotless-maven-plugin.version>
|
||||
<spotless.action>check</spotless.action>
|
||||
<dubbo-shared-resources.version>1.0.0</dubbo-shared-resources.version>
|
||||
<palantirJavaFormat.version>2.38.0</palantirJavaFormat.version>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
<slf4j_version>1.7.36</slf4j_version>
|
||||
<curator_version>4.3.0</curator_version>
|
||||
<zookeeper_version>3.4.14</zookeeper_version>
|
||||
<spotless-maven-plugin.version>2.41.0</spotless-maven-plugin.version>
|
||||
<spotless-maven-plugin.version>2.41.1</spotless-maven-plugin.version>
|
||||
<spotless.action>check</spotless.action>
|
||||
<dubbo-shared-resources.version>1.0.0</dubbo-shared-resources.version>
|
||||
<palantirJavaFormat.version>2.38.0</palantirJavaFormat.version>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
</modules>
|
||||
|
||||
<properties>
|
||||
<spotless-maven-plugin.version>2.41.0</spotless-maven-plugin.version>
|
||||
<spotless-maven-plugin.version>2.41.1</spotless-maven-plugin.version>
|
||||
<spotless.action>check</spotless.action>
|
||||
<dubbo-shared-resources.version>1.0.0</dubbo-shared-resources.version>
|
||||
<palantirJavaFormat.version>2.38.0</palantirJavaFormat.version>
|
||||
|
|
|
|||
|
|
@ -120,15 +120,6 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- kubernetes -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-kubernetes</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- metadata -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
|
|
@ -591,15 +582,6 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- xds -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Transitive dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
|
|
@ -718,8 +700,6 @@
|
|||
<include>org.apache.dubbo:dubbo-serialization-hessian2</include>
|
||||
<include>org.apache.dubbo:dubbo-serialization-fastjson2</include>
|
||||
<include>org.apache.dubbo:dubbo-serialization-jdk</include>
|
||||
<include>org.apache.dubbo:dubbo-kubernetes</include>
|
||||
<include>org.apache.dubbo:dubbo-xds</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<transformers>
|
||||
|
|
@ -911,9 +891,6 @@
|
|||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor</resource>
|
||||
</transformer>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bin</id>
|
||||
|
|
|
|||
|
|
@ -180,13 +180,6 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- kubernetes -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-kubernetes</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- metadata -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
|
|
@ -741,12 +734,6 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- xds -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
|
|||
|
|
@ -510,12 +510,6 @@
|
|||
META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener
|
||||
</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>
|
||||
META-INF/dubbo/internal/org.apache.dubbo.registry.xds.XdsCertificateSigner
|
||||
</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<spotless-maven-plugin.version>2.41.0</spotless-maven-plugin.version>
|
||||
<spotless-maven-plugin.version>2.41.1</spotless-maven-plugin.version>
|
||||
<spotless.action>check</spotless.action>
|
||||
<dubbo-shared-resources.version>1.0.0</dubbo-shared-resources.version>
|
||||
<palantirJavaFormat.version>2.38.0</palantirJavaFormat.version>
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>dubbo-kubernetes</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>The Kubernetes Integration</description>
|
||||
<properties>
|
||||
<skip_maven_deploy>false</skip_maven_deploy>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-api</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-common</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-metadata-api</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-plugin-router-mesh</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-server-mock</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>3.12.4</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
|
||||
import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.Watch;
|
||||
import io.fabric8.kubernetes.client.Watcher;
|
||||
import io.fabric8.kubernetes.client.WatcherException;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_LISTEN_KUBERNETES;
|
||||
|
||||
public class KubernetesMeshEnvListener implements MeshEnvListener {
|
||||
public static final ErrorTypeAwareLogger logger =
|
||||
LoggerFactory.getErrorTypeAwareLogger(KubernetesMeshEnvListener.class);
|
||||
private static volatile boolean usingApiServer = false;
|
||||
private static volatile KubernetesClient kubernetesClient;
|
||||
private static volatile String namespace;
|
||||
|
||||
private final Map<String, MeshAppRuleListener> appRuleListenerMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Watch> vsAppWatch = new ConcurrentHashMap<>();
|
||||
private final Map<String, Watch> drAppWatch = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, String> vsAppCache = new ConcurrentHashMap<>();
|
||||
private final Map<String, String> drAppCache = new ConcurrentHashMap<>();
|
||||
|
||||
public static void injectKubernetesEnv(KubernetesClient client, String configuredNamespace) {
|
||||
usingApiServer = true;
|
||||
kubernetesClient = client;
|
||||
namespace = configuredNamespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnable() {
|
||||
return usingApiServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(String appName, MeshAppRuleListener listener) {
|
||||
appRuleListenerMap.put(appName, listener);
|
||||
logger.info("Subscribe Mesh Rule in Kubernetes. AppName: " + appName);
|
||||
|
||||
// subscribe VisualService
|
||||
subscribeVs(appName);
|
||||
|
||||
// subscribe DestinationRule
|
||||
subscribeDr(appName);
|
||||
|
||||
// notify for start
|
||||
notifyOnce(appName);
|
||||
}
|
||||
|
||||
private void subscribeVs(String appName) {
|
||||
if (vsAppWatch.containsKey(appName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Watch watch = kubernetesClient
|
||||
.genericKubernetesResources(MeshConstant.getVsDefinition())
|
||||
.inNamespace(namespace)
|
||||
.withName(appName)
|
||||
.watch(new Watcher<GenericKubernetesResource>() {
|
||||
@Override
|
||||
public void eventReceived(Action action, GenericKubernetesResource resource) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action
|
||||
+ " Resource:" + resource);
|
||||
}
|
||||
|
||||
if (action == Action.ADDED || action == Action.MODIFIED) {
|
||||
String vsRule = new Yaml(new SafeConstructor(new LoaderOptions())).dump(resource);
|
||||
vsAppCache.put(appName, vsRule);
|
||||
if (drAppCache.containsKey(appName)) {
|
||||
notifyListener(vsRule, appName, drAppCache.get(appName));
|
||||
}
|
||||
} else {
|
||||
appRuleListenerMap.get(appName).receiveConfigInfo("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(WatcherException cause) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
vsAppWatch.put(appName, watch);
|
||||
try {
|
||||
GenericKubernetesResource vsRule = kubernetesClient
|
||||
.genericKubernetesResources(MeshConstant.getVsDefinition())
|
||||
.inNamespace(namespace)
|
||||
.withName(appName)
|
||||
.get();
|
||||
vsAppCache.put(appName, new Yaml(new SafeConstructor(new LoaderOptions())).dump(vsRule));
|
||||
} catch (Throwable ignore) {
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(REGISTRY_ERROR_LISTEN_KUBERNETES, "", "", "Error occurred when listen kubernetes crd.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListener(String vsRule, String appName, String drRule) {
|
||||
String rule = vsRule + "\n---\n" + drRule;
|
||||
logger.info("Notify App Rule Listener. AppName: " + appName + " Rule:" + rule);
|
||||
|
||||
appRuleListenerMap.get(appName).receiveConfigInfo(rule);
|
||||
}
|
||||
|
||||
private void subscribeDr(String appName) {
|
||||
if (drAppWatch.containsKey(appName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Watch watch = kubernetesClient
|
||||
.genericKubernetesResources(MeshConstant.getDrDefinition())
|
||||
.inNamespace(namespace)
|
||||
.withName(appName)
|
||||
.watch(new Watcher<GenericKubernetesResource>() {
|
||||
@Override
|
||||
public void eventReceived(Action action, GenericKubernetesResource resource) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Received VS Rule notification. AppName: " + appName + " Action:" + action
|
||||
+ " Resource:" + resource);
|
||||
}
|
||||
|
||||
if (action == Action.ADDED || action == Action.MODIFIED) {
|
||||
String drRule = new Yaml(new SafeConstructor(new LoaderOptions())).dump(resource);
|
||||
|
||||
drAppCache.put(appName, drRule);
|
||||
if (vsAppCache.containsKey(appName)) {
|
||||
notifyListener(vsAppCache.get(appName), appName, drRule);
|
||||
}
|
||||
} else {
|
||||
appRuleListenerMap.get(appName).receiveConfigInfo("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(WatcherException cause) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
drAppWatch.put(appName, watch);
|
||||
try {
|
||||
GenericKubernetesResource drRule = kubernetesClient
|
||||
.genericKubernetesResources(MeshConstant.getDrDefinition())
|
||||
.inNamespace(namespace)
|
||||
.withName(appName)
|
||||
.get();
|
||||
drAppCache.put(appName, new Yaml(new SafeConstructor(new LoaderOptions())).dump(drRule));
|
||||
} catch (Throwable ignore) {
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(REGISTRY_ERROR_LISTEN_KUBERNETES, "", "", "Error occurred when listen kubernetes crd.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyOnce(String appName) {
|
||||
if (vsAppCache.containsKey(appName) && drAppCache.containsKey(appName)) {
|
||||
notifyListener(vsAppCache.get(appName), appName, drAppCache.get(appName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnSubscribe(String appName) {
|
||||
appRuleListenerMap.remove(appName);
|
||||
|
||||
if (vsAppWatch.containsKey(appName)) {
|
||||
vsAppWatch.remove(appName).close();
|
||||
}
|
||||
vsAppCache.remove(appName);
|
||||
|
||||
if (drAppWatch.containsKey(appName)) {
|
||||
drAppWatch.remove(appName).close();
|
||||
}
|
||||
drAppCache.remove(appName);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
|
||||
import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListenerFactory;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class KubernetesMeshEnvListenerFactory implements MeshEnvListenerFactory {
|
||||
public static final Logger logger = LoggerFactory.getLogger(KubernetesMeshEnvListenerFactory.class);
|
||||
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
private MeshEnvListener listener = null;
|
||||
|
||||
@Override
|
||||
public MeshEnvListener getListener() {
|
||||
try {
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
listener = new NopKubernetesMeshEnvListener();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.info("Current Env not support Kubernetes.");
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.registry.NotifyListener;
|
||||
import org.apache.dubbo.registry.support.FailbackRegistry;
|
||||
|
||||
/**
|
||||
* Empty implements for Kubernetes <br/>
|
||||
* Kubernetes only support `Service Discovery` mode register <br/>
|
||||
* Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
|
||||
* {@link KubernetesServiceDiscovery} is the real implementation of Kubernetes
|
||||
*/
|
||||
public class KubernetesRegistry extends FailbackRegistry {
|
||||
public KubernetesRegistry(URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRegister(URL url) {}
|
||||
|
||||
@Override
|
||||
public void doUnregister(URL url) {}
|
||||
|
||||
@Override
|
||||
public void doSubscribe(URL url, NotifyListener listener) {}
|
||||
|
||||
@Override
|
||||
public void doUnsubscribe(URL url, NotifyListener listener) {}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.registry.Registry;
|
||||
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
|
||||
|
||||
public class KubernetesRegistryFactory extends AbstractRegistryFactory {
|
||||
|
||||
@Override
|
||||
protected String createRegistryCacheKey(URL url) {
|
||||
return url.toFullString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Registry createRegistry(URL url) {
|
||||
return new KubernetesRegistry(url);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,451 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.JsonUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
|
||||
import org.apache.dubbo.registry.client.DefaultServiceInstance;
|
||||
import org.apache.dubbo.registry.client.ServiceInstance;
|
||||
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
|
||||
import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
|
||||
import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
|
||||
import org.apache.dubbo.registry.kubernetes.util.KubernetesConfigUtils;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModelUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EndpointAddress;
|
||||
import io.fabric8.kubernetes.api.model.EndpointPort;
|
||||
import io.fabric8.kubernetes.api.model.EndpointSubset;
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.PodBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
|
||||
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
|
||||
import io.fabric8.kubernetes.client.informers.SharedIndexInformer;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_UNABLE_ACCESS_KUBERNETES;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_UNABLE_FIND_SERVICE_KUBERNETES;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_UNABLE_MATCH_KUBERNETES;
|
||||
|
||||
public class KubernetesServiceDiscovery extends AbstractServiceDiscovery {
|
||||
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
|
||||
|
||||
private KubernetesClient kubernetesClient;
|
||||
|
||||
private String currentHostname;
|
||||
|
||||
private final URL registryURL;
|
||||
|
||||
private final String namespace;
|
||||
|
||||
private final boolean enableRegister;
|
||||
|
||||
public static final String KUBERNETES_PROPERTIES_KEY = "io.dubbo/metadata";
|
||||
|
||||
private static final ConcurrentHashMap<String, AtomicLong> SERVICE_UPDATE_TIME = new ConcurrentHashMap<>(64);
|
||||
|
||||
private static final ConcurrentHashMap<String, SharedIndexInformer<Service>> SERVICE_INFORMER =
|
||||
new ConcurrentHashMap<>(64);
|
||||
|
||||
private static final ConcurrentHashMap<String, SharedIndexInformer<Pod>> PODS_INFORMER =
|
||||
new ConcurrentHashMap<>(64);
|
||||
|
||||
private static final ConcurrentHashMap<String, SharedIndexInformer<Endpoints>> ENDPOINTS_INFORMER =
|
||||
new ConcurrentHashMap<>(64);
|
||||
|
||||
public KubernetesServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
|
||||
super(applicationModel, registryURL);
|
||||
Config config = KubernetesConfigUtils.createKubernetesConfig(registryURL);
|
||||
this.kubernetesClient = new KubernetesClientBuilder().withConfig(config).build();
|
||||
this.currentHostname = System.getenv("HOSTNAME");
|
||||
this.registryURL = registryURL;
|
||||
this.namespace = config.getNamespace();
|
||||
this.enableRegister = registryURL.getParameter(KubernetesClientConst.ENABLE_REGISTER, true);
|
||||
|
||||
boolean availableAccess;
|
||||
try {
|
||||
availableAccess = kubernetesClient.pods().withName(currentHostname).get() != null;
|
||||
} catch (Throwable e) {
|
||||
availableAccess = false;
|
||||
}
|
||||
if (!availableAccess) {
|
||||
String message = "Unable to access api server. " + "Please check your url config."
|
||||
+ " Master URL: "
|
||||
+ config.getMasterUrl() + " Hostname: "
|
||||
+ currentHostname;
|
||||
logger.error(REGISTRY_UNABLE_ACCESS_KUBERNETES, "", "", message);
|
||||
} else {
|
||||
KubernetesMeshEnvListener.injectKubernetesEnv(kubernetesClient, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDestroy() {
|
||||
SERVICE_INFORMER.forEach((k, v) -> v.close());
|
||||
SERVICE_INFORMER.clear();
|
||||
|
||||
PODS_INFORMER.forEach((k, v) -> v.close());
|
||||
PODS_INFORMER.clear();
|
||||
|
||||
ENDPOINTS_INFORMER.forEach((k, v) -> v.close());
|
||||
ENDPOINTS_INFORMER.clear();
|
||||
|
||||
kubernetesClient.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {
|
||||
if (enableRegister) {
|
||||
kubernetesClient
|
||||
.pods()
|
||||
.inNamespace(namespace)
|
||||
.withName(currentHostname)
|
||||
.edit(pod -> new PodBuilder(pod)
|
||||
.editOrNewMetadata()
|
||||
.addToAnnotations(
|
||||
KUBERNETES_PROPERTIES_KEY, JsonUtils.toJson(serviceInstance.getMetadata()))
|
||||
.endMetadata()
|
||||
.build());
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Write Current Service Instance Metadata to Kubernetes pod. " + "Current pod name: "
|
||||
+ currentHostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparing to {@link AbstractServiceDiscovery#doUpdate(ServiceInstance, ServiceInstance)}, unregister() is unnecessary here.
|
||||
*/
|
||||
@Override
|
||||
public void doUpdate(ServiceInstance oldServiceInstance, ServiceInstance newServiceInstance)
|
||||
throws RuntimeException {
|
||||
reportMetadata(newServiceInstance.getServiceMetadata());
|
||||
this.doRegister(newServiceInstance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {
|
||||
if (enableRegister) {
|
||||
kubernetesClient
|
||||
.pods()
|
||||
.inNamespace(namespace)
|
||||
.withName(currentHostname)
|
||||
.edit(pod -> new PodBuilder(pod)
|
||||
.editOrNewMetadata()
|
||||
.removeFromAnnotations(KUBERNETES_PROPERTIES_KEY)
|
||||
.endMetadata()
|
||||
.build());
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(
|
||||
"Remove Current Service Instance from Kubernetes pod. Current pod name: " + currentHostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getServices() {
|
||||
return kubernetesClient.services().inNamespace(namespace).list().getItems().stream()
|
||||
.map(service -> service.getMetadata().getName())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
|
||||
Endpoints endpoints = null;
|
||||
SharedIndexInformer<Endpoints> endInformer = ENDPOINTS_INFORMER.get(serviceName);
|
||||
if (endInformer != null) {
|
||||
// get endpoints directly from informer local store
|
||||
List<Endpoints> endpointsList = endInformer.getStore().list();
|
||||
if (endpointsList.size() > 0) {
|
||||
endpoints = endpointsList.get(0);
|
||||
}
|
||||
}
|
||||
if (endpoints == null) {
|
||||
endpoints = kubernetesClient
|
||||
.endpoints()
|
||||
.inNamespace(namespace)
|
||||
.withName(serviceName)
|
||||
.get();
|
||||
}
|
||||
|
||||
return toServiceInstance(endpoints, serviceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener)
|
||||
throws NullPointerException, IllegalArgumentException {
|
||||
listener.getServiceNames().forEach(serviceName -> {
|
||||
SERVICE_UPDATE_TIME.put(serviceName, new AtomicLong(0L));
|
||||
|
||||
// Watch Service Endpoint Modification
|
||||
watchEndpoints(listener, serviceName);
|
||||
|
||||
// Watch Pods Modification, happens when ServiceInstance updated
|
||||
watchPods(listener, serviceName);
|
||||
|
||||
// Watch Service Modification, happens when Service Selector updated, used to update pods watcher
|
||||
watchService(listener, serviceName);
|
||||
});
|
||||
}
|
||||
|
||||
private void watchEndpoints(ServiceInstancesChangedListener listener, String serviceName) {
|
||||
SharedIndexInformer<Endpoints> endInformer = kubernetesClient
|
||||
.endpoints()
|
||||
.inNamespace(namespace)
|
||||
.withName(serviceName)
|
||||
.inform(new ResourceEventHandler<Endpoints>() {
|
||||
@Override
|
||||
public void onAdd(Endpoints endpoints) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Endpoint Event. Event type: added. Current pod name: "
|
||||
+ currentHostname + ". Endpoints is: " + endpoints);
|
||||
}
|
||||
notifyServiceChanged(serviceName, listener, toServiceInstance(endpoints, serviceName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Endpoints oldEndpoints, Endpoints newEndpoints) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Endpoint Event. Event type: updated. Current pod name: "
|
||||
+ currentHostname + ". The new Endpoints is: " + newEndpoints);
|
||||
}
|
||||
notifyServiceChanged(serviceName, listener, toServiceInstance(newEndpoints, serviceName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(Endpoints endpoints, boolean deletedFinalStateUnknown) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Endpoint Event. Event type: deleted. Current pod name: "
|
||||
+ currentHostname + ". Endpoints is: " + endpoints);
|
||||
}
|
||||
notifyServiceChanged(serviceName, listener, toServiceInstance(endpoints, serviceName));
|
||||
}
|
||||
});
|
||||
|
||||
ENDPOINTS_INFORMER.put(serviceName, endInformer);
|
||||
}
|
||||
|
||||
private void watchPods(ServiceInstancesChangedListener listener, String serviceName) {
|
||||
Map<String, String> serviceSelector = getServiceSelector(serviceName);
|
||||
if (serviceSelector == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SharedIndexInformer<Pod> podInformer = kubernetesClient
|
||||
.pods()
|
||||
.inNamespace(namespace)
|
||||
.withLabels(serviceSelector)
|
||||
.inform(new ResourceEventHandler<Pod>() {
|
||||
@Override
|
||||
public void onAdd(Pod pod) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Pods Event. Event type: added. Current pod name: " + currentHostname
|
||||
+ ". Pod is: " + pod);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Pod oldPod, Pod newPod) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Pods Event. Event type: updated. Current pod name: "
|
||||
+ currentHostname + ". new Pod is: " + newPod);
|
||||
}
|
||||
|
||||
notifyServiceChanged(serviceName, listener, getInstances(serviceName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(Pod pod, boolean deletedFinalStateUnknown) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Pods Event. Event type: deleted. Current pod name: "
|
||||
+ currentHostname + ". Pod is: " + pod);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
PODS_INFORMER.put(serviceName, podInformer);
|
||||
}
|
||||
|
||||
private void watchService(ServiceInstancesChangedListener listener, String serviceName) {
|
||||
SharedIndexInformer<Service> serviceInformer = kubernetesClient
|
||||
.services()
|
||||
.inNamespace(namespace)
|
||||
.withName(serviceName)
|
||||
.inform(new ResourceEventHandler<Service>() {
|
||||
@Override
|
||||
public void onAdd(Service service) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Service Added Event. " + "Current pod name: " + currentHostname);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Service oldService, Service newService) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Service Update Event. Update Pods Watcher. Current pod name: "
|
||||
+ currentHostname + ". The new Service is: " + newService);
|
||||
}
|
||||
if (PODS_INFORMER.containsKey(serviceName)) {
|
||||
PODS_INFORMER.get(serviceName).close();
|
||||
PODS_INFORMER.remove(serviceName);
|
||||
}
|
||||
watchPods(listener, serviceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(Service service, boolean deletedFinalStateUnknown) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received Service Delete Event. " + "Current pod name: " + currentHostname);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
SERVICE_INFORMER.put(serviceName, serviceInformer);
|
||||
}
|
||||
|
||||
private void notifyServiceChanged(
|
||||
String serviceName, ServiceInstancesChangedListener listener, List<ServiceInstance> serviceInstanceList) {
|
||||
long receivedTime = System.nanoTime();
|
||||
|
||||
ServiceInstancesChangedEvent event;
|
||||
|
||||
event = new ServiceInstancesChangedEvent(serviceName, serviceInstanceList);
|
||||
|
||||
AtomicLong updateTime = SERVICE_UPDATE_TIME.get(serviceName);
|
||||
long lastUpdateTime = updateTime.get();
|
||||
|
||||
if (lastUpdateTime <= receivedTime) {
|
||||
if (updateTime.compareAndSet(lastUpdateTime, receivedTime)) {
|
||||
listener.onEvent(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Discard Service Instance Data. "
|
||||
+ "Possible Cause: Newer message has been processed or Failed to update time record by CAS. "
|
||||
+ "Current Data received time: "
|
||||
+ receivedTime + ". " + "Newer Data received time: "
|
||||
+ lastUpdateTime + ".");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return registryURL;
|
||||
}
|
||||
|
||||
private Map<String, String> getServiceSelector(String serviceName) {
|
||||
Service service = kubernetesClient
|
||||
.services()
|
||||
.inNamespace(namespace)
|
||||
.withName(serviceName)
|
||||
.get();
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
return service.getSpec().getSelector();
|
||||
}
|
||||
|
||||
private List<ServiceInstance> toServiceInstance(Endpoints endpoints, String serviceName) {
|
||||
Map<String, String> serviceSelector = getServiceSelector(serviceName);
|
||||
if (serviceSelector == null) {
|
||||
return new LinkedList<>();
|
||||
}
|
||||
Map<String, Pod> pods =
|
||||
kubernetesClient.pods().inNamespace(namespace).withLabels(serviceSelector).list().getItems().stream()
|
||||
.collect(Collectors.toMap(pod -> pod.getMetadata().getName(), pod -> pod));
|
||||
|
||||
List<ServiceInstance> instances = new LinkedList<>();
|
||||
Set<Integer> instancePorts = new HashSet<>();
|
||||
|
||||
for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
|
||||
instancePorts.addAll(endpointSubset.getPorts().stream()
|
||||
.map(EndpointPort::getPort)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
|
||||
for (EndpointAddress address : endpointSubset.getAddresses()) {
|
||||
Pod pod = pods.get(address.getTargetRef().getName());
|
||||
String ip = address.getIp();
|
||||
if (pod == null) {
|
||||
logger.warn(
|
||||
REGISTRY_UNABLE_MATCH_KUBERNETES,
|
||||
"",
|
||||
"",
|
||||
"Unable to match Kubernetes Endpoint address with Pod. " + "EndpointAddress Hostname: "
|
||||
+ address.getTargetRef().getName());
|
||||
continue;
|
||||
}
|
||||
instancePorts.forEach(port -> {
|
||||
ServiceInstance serviceInstance = new DefaultServiceInstance(
|
||||
serviceName, ip, port, ScopeModelUtil.getApplicationModel(getUrl().getScopeModel()));
|
||||
|
||||
String properties = pod.getMetadata().getAnnotations().get(KUBERNETES_PROPERTIES_KEY);
|
||||
if (StringUtils.isNotEmpty(properties)) {
|
||||
serviceInstance.getMetadata().putAll(JsonUtils.toJavaObject(properties, Map.class));
|
||||
instances.add(serviceInstance);
|
||||
} else {
|
||||
logger.warn(
|
||||
REGISTRY_UNABLE_FIND_SERVICE_KUBERNETES,
|
||||
"",
|
||||
"",
|
||||
"Unable to find Service Instance metadata in Pod Annotations. "
|
||||
+ "Possibly cause: provider has not been initialized successfully. "
|
||||
+ "EndpointAddress Hostname: "
|
||||
+ address.getTargetRef().getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* UT used only
|
||||
*/
|
||||
@Deprecated
|
||||
public void setCurrentHostname(String currentHostname) {
|
||||
this.currentHostname = currentHostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* UT used only
|
||||
*/
|
||||
@Deprecated
|
||||
public void setKubernetesClient(KubernetesClient kubernetesClient) {
|
||||
this.kubernetesClient = kubernetesClient;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
|
||||
import org.apache.dubbo.registry.client.ServiceDiscovery;
|
||||
|
||||
public class KubernetesServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
|
||||
@Override
|
||||
protected ServiceDiscovery createDiscovery(URL registryURL) {
|
||||
return new KubernetesServiceDiscovery(applicationModel, registryURL);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
|
||||
|
||||
public class MeshConstant {
|
||||
public static CustomResourceDefinitionContext getVsDefinition() {
|
||||
// TODO cache
|
||||
return new CustomResourceDefinitionContext.Builder()
|
||||
.withGroup("service.dubbo.apache.org")
|
||||
.withVersion("v1alpha1")
|
||||
.withScope("Namespaced")
|
||||
.withName("virtualservices.service.dubbo.apache.org")
|
||||
.withPlural("virtualservices")
|
||||
.withKind("VirtualService")
|
||||
.build();
|
||||
}
|
||||
|
||||
public static CustomResourceDefinitionContext getDrDefinition() {
|
||||
// TODO cache
|
||||
return new CustomResourceDefinitionContext.Builder()
|
||||
.withGroup("service.dubbo.apache.org")
|
||||
.withVersion("v1alpha1")
|
||||
.withScope("Namespaced")
|
||||
.withName("destinationrules.service.dubbo.apache.org")
|
||||
.withPlural("destinationrules")
|
||||
.withKind("DestinationRule")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshAppRuleListener;
|
||||
import org.apache.dubbo.rpc.cluster.router.mesh.route.MeshEnvListener;
|
||||
|
||||
public class NopKubernetesMeshEnvListener implements MeshEnvListener {
|
||||
|
||||
@Override
|
||||
public boolean isEnable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(String appName, MeshAppRuleListener listener) {}
|
||||
|
||||
@Override
|
||||
public void onUnSubscribe(String appName) {}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes.util;
|
||||
|
||||
public class KubernetesClientConst {
|
||||
public static final String DEFAULT_MASTER_PLACEHOLDER = "DEFAULT_MASTER_HOST";
|
||||
public static final String DEFAULT_MASTER_URL = "https://kubernetes.default.svc";
|
||||
|
||||
public static final String ENABLE_REGISTER = "enableRegister";
|
||||
|
||||
public static final String TRUST_CERTS = "trustCerts";
|
||||
|
||||
public static final String USE_HTTPS = "useHttps";
|
||||
|
||||
public static final String HTTP2_DISABLE = "http2Disable";
|
||||
|
||||
public static final String NAMESPACE = "namespace";
|
||||
|
||||
public static final String API_VERSION = "apiVersion";
|
||||
|
||||
public static final String CA_CERT_FILE = "caCertFile";
|
||||
|
||||
public static final String CA_CERT_DATA = "caCertData";
|
||||
|
||||
public static final String CLIENT_CERT_FILE = "clientCertFile";
|
||||
|
||||
public static final String CLIENT_CERT_DATA = "clientCertData";
|
||||
|
||||
public static final String CLIENT_KEY_FILE = "clientKeyFile";
|
||||
|
||||
public static final String CLIENT_KEY_DATA = "clientKeyData";
|
||||
|
||||
public static final String CLIENT_KEY_ALGO = "clientKeyAlgo";
|
||||
|
||||
public static final String CLIENT_KEY_PASSPHRASE = "clientKeyPassphrase";
|
||||
|
||||
public static final String OAUTH_TOKEN = "oauthToken";
|
||||
|
||||
public static final String USERNAME = "username";
|
||||
|
||||
public static final String PASSWORD = "password";
|
||||
|
||||
public static final String WATCH_RECONNECT_INTERVAL = "watchReconnectInterval";
|
||||
|
||||
public static final String WATCH_RECONNECT_LIMIT = "watchReconnectLimit";
|
||||
|
||||
public static final String CONNECTION_TIMEOUT = "connectionTimeout";
|
||||
|
||||
public static final String REQUEST_TIMEOUT = "requestTimeout";
|
||||
|
||||
public static final String ROLLING_TIMEOUT = "rollingTimeout";
|
||||
|
||||
public static final String LOGGING_INTERVAL = "loggingInterval";
|
||||
|
||||
public static final String HTTP_PROXY = "httpProxy";
|
||||
|
||||
public static final String HTTPS_PROXY = "httpsProxy";
|
||||
|
||||
public static final String PROXY_USERNAME = "proxyUsername";
|
||||
|
||||
public static final String PROXY_PASSWORD = "proxyPassword";
|
||||
|
||||
public static final String NO_PROXY = "noProxy";
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes.util;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.ConfigBuilder;
|
||||
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.API_VERSION;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_DATA;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CA_CERT_FILE;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_DATA;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_CERT_FILE;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_ALGO;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_DATA;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_FILE;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CLIENT_KEY_PASSPHRASE;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.CONNECTION_TIMEOUT;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_PLACEHOLDER;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.DEFAULT_MASTER_URL;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP2_DISABLE;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTPS_PROXY;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.HTTP_PROXY;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.LOGGING_INTERVAL;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NAMESPACE;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NO_PROXY;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.OAUTH_TOKEN;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PASSWORD;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_PASSWORD;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.PROXY_USERNAME;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.REQUEST_TIMEOUT;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.TRUST_CERTS;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USERNAME;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.USE_HTTPS;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_INTERVAL;
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.WATCH_RECONNECT_LIMIT;
|
||||
|
||||
public class KubernetesConfigUtils {
|
||||
|
||||
public static Config createKubernetesConfig(URL url) {
|
||||
// Init default config
|
||||
Config base = Config.autoConfigure(null);
|
||||
|
||||
// replace config with parameters if presents
|
||||
return new ConfigBuilder(base) //
|
||||
.withMasterUrl(buildMasterUrl(url)) //
|
||||
.withApiVersion(url.getParameter(API_VERSION, base.getApiVersion())) //
|
||||
.withNamespace(url.getParameter(NAMESPACE, base.getNamespace())) //
|
||||
.withUsername(url.getParameter(USERNAME, base.getUsername())) //
|
||||
.withPassword(url.getParameter(PASSWORD, base.getPassword())) //
|
||||
.withOauthToken(url.getParameter(OAUTH_TOKEN, base.getOauthToken())) //
|
||||
.withCaCertFile(url.getParameter(CA_CERT_FILE, base.getCaCertFile())) //
|
||||
.withCaCertData(url.getParameter(CA_CERT_DATA, decodeBase64(base.getCaCertData()))) //
|
||||
.withClientKeyFile(url.getParameter(CLIENT_KEY_FILE, base.getClientKeyFile())) //
|
||||
.withClientKeyData(url.getParameter(CLIENT_KEY_DATA, decodeBase64(base.getClientKeyData()))) //
|
||||
.withClientCertFile(url.getParameter(CLIENT_CERT_FILE, base.getClientCertFile())) //
|
||||
.withClientCertData(url.getParameter(CLIENT_CERT_DATA, decodeBase64(base.getClientCertData()))) //
|
||||
.withClientKeyAlgo(url.getParameter(CLIENT_KEY_ALGO, base.getClientKeyAlgo())) //
|
||||
.withClientKeyPassphrase(url.getParameter(CLIENT_KEY_PASSPHRASE, base.getClientKeyPassphrase())) //
|
||||
.withConnectionTimeout(url.getParameter(CONNECTION_TIMEOUT, base.getConnectionTimeout())) //
|
||||
.withRequestTimeout(url.getParameter(REQUEST_TIMEOUT, base.getRequestTimeout())) //
|
||||
.withWatchReconnectInterval(
|
||||
url.getParameter(WATCH_RECONNECT_INTERVAL, base.getWatchReconnectInterval())) //
|
||||
.withWatchReconnectLimit(url.getParameter(WATCH_RECONNECT_LIMIT, base.getWatchReconnectLimit())) //
|
||||
.withLoggingInterval(url.getParameter(LOGGING_INTERVAL, base.getLoggingInterval())) //
|
||||
.withTrustCerts(url.getParameter(TRUST_CERTS, base.isTrustCerts())) //
|
||||
.withHttp2Disable(url.getParameter(HTTP2_DISABLE, base.isHttp2Disable())) //
|
||||
.withHttpProxy(url.getParameter(HTTP_PROXY, base.getHttpProxy())) //
|
||||
.withHttpsProxy(url.getParameter(HTTPS_PROXY, base.getHttpsProxy())) //
|
||||
.withProxyUsername(url.getParameter(PROXY_USERNAME, base.getProxyUsername())) //
|
||||
.withProxyPassword(url.getParameter(PROXY_PASSWORD, base.getProxyPassword())) //
|
||||
.withNoProxy(url.getParameter(NO_PROXY, base.getNoProxy())) //
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String buildMasterUrl(URL url) {
|
||||
if (DEFAULT_MASTER_PLACEHOLDER.equalsIgnoreCase(url.getHost())) {
|
||||
return DEFAULT_MASTER_URL;
|
||||
}
|
||||
return (url.getParameter(USE_HTTPS, true) ? "https://" : "http://") + url.getHost() + ":" + url.getPort();
|
||||
}
|
||||
|
||||
private static String decodeBase64(String str) {
|
||||
return StringUtils.isNotEmpty(str) ? new String(Base64.getDecoder().decode(str)) : null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesRegistryFactory
|
||||
|
|
@ -1 +0,0 @@
|
|||
kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscoveryFactory
|
||||
|
|
@ -1 +0,0 @@
|
|||
kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesMeshEnvListenerFactory
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.config.ApplicationConfig;
|
||||
import org.apache.dubbo.registry.client.DefaultServiceInstance;
|
||||
import org.apache.dubbo.registry.client.ServiceInstance;
|
||||
import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
|
||||
import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
|
||||
import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModelUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.EndpointsBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.api.model.PodBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServiceBuilder;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
|
||||
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst.NAMESPACE;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
@ExtendWith({MockitoExtension.class})
|
||||
class KubernetesServiceDiscoveryTest {
|
||||
|
||||
private static final String SERVICE_NAME = "TestService";
|
||||
|
||||
private static final String POD_NAME = "TestServer";
|
||||
|
||||
public KubernetesServer mockServer = new KubernetesServer(false, true);
|
||||
|
||||
private NamespacedKubernetesClient mockClient;
|
||||
|
||||
private ServiceInstancesChangedListener mockListener = Mockito.mock(ServiceInstancesChangedListener.class);
|
||||
|
||||
private URL serverUrl;
|
||||
|
||||
private Map<String, String> selector;
|
||||
|
||||
private KubernetesServiceDiscovery serviceDiscovery;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
mockServer.before();
|
||||
mockClient = mockServer.getClient().inNamespace("dubbo-demo");
|
||||
|
||||
ApplicationModel applicationModel = ApplicationModel.defaultModel();
|
||||
applicationModel.getApplicationConfigManager().setApplication(new ApplicationConfig());
|
||||
|
||||
serverUrl = URL.valueOf(mockClient.getConfiguration().getMasterUrl())
|
||||
.setProtocol("kubernetes")
|
||||
.addParameter(NAMESPACE, "dubbo-demo")
|
||||
.addParameter(KubernetesClientConst.USE_HTTPS, "false")
|
||||
.addParameter(KubernetesClientConst.HTTP2_DISABLE, "true");
|
||||
serverUrl.setScopeModel(applicationModel);
|
||||
|
||||
this.serviceDiscovery = new KubernetesServiceDiscovery(applicationModel, serverUrl);
|
||||
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
|
||||
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
|
||||
|
||||
selector = new HashMap<>(4);
|
||||
selector.put("l", "v");
|
||||
Pod pod = new PodBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(POD_NAME)
|
||||
.withLabels(selector)
|
||||
.endMetadata()
|
||||
.build();
|
||||
|
||||
Service service = new ServiceBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(SERVICE_NAME)
|
||||
.endMetadata()
|
||||
.withNewSpec()
|
||||
.withSelector(selector)
|
||||
.endSpec()
|
||||
.build();
|
||||
|
||||
Endpoints endPoints = new EndpointsBuilder()
|
||||
.withNewMetadata()
|
||||
.withName(SERVICE_NAME)
|
||||
.endMetadata()
|
||||
.addNewSubset()
|
||||
.addNewAddress()
|
||||
.withIp("ip1")
|
||||
.withNewTargetRef()
|
||||
.withUid("uid1")
|
||||
.withName(POD_NAME)
|
||||
.endTargetRef()
|
||||
.endAddress()
|
||||
.addNewPort("Test", "Test", 12345, "TCP")
|
||||
.endSubset()
|
||||
.build();
|
||||
|
||||
mockClient.pods().resource(pod).create();
|
||||
mockClient.services().resource(service).create();
|
||||
mockClient.endpoints().resource(endPoints).create();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void destroy() throws Exception {
|
||||
serviceDiscovery.destroy();
|
||||
mockClient.close();
|
||||
mockServer.after();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEndpointsUpdate() {
|
||||
serviceDiscovery.setCurrentHostname(POD_NAME);
|
||||
serviceDiscovery.setKubernetesClient(mockClient);
|
||||
|
||||
ServiceInstance serviceInstance = new DefaultServiceInstance(
|
||||
SERVICE_NAME,
|
||||
"Test",
|
||||
12345,
|
||||
ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
|
||||
|
||||
serviceDiscovery.doRegister(serviceInstance);
|
||||
|
||||
HashSet<String> serviceList = new HashSet<>(4);
|
||||
serviceList.add(SERVICE_NAME);
|
||||
Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
|
||||
Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
|
||||
|
||||
serviceDiscovery.addServiceInstancesChangedListener(mockListener);
|
||||
mockClient.endpoints().withName(SERVICE_NAME).edit(endpoints -> new EndpointsBuilder(endpoints)
|
||||
.editFirstSubset()
|
||||
.addNewAddress()
|
||||
.withIp("ip2")
|
||||
.withNewTargetRef()
|
||||
.withUid("uid2")
|
||||
.withName(POD_NAME)
|
||||
.endTargetRef()
|
||||
.endAddress()
|
||||
.endSubset()
|
||||
.build());
|
||||
|
||||
await().until(() -> {
|
||||
ArgumentCaptor<ServiceInstancesChangedEvent> captor =
|
||||
ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
|
||||
Mockito.verify(mockListener, Mockito.atLeast(0)).onEvent(captor.capture());
|
||||
return captor.getValue().getServiceInstances().size() == 2;
|
||||
});
|
||||
ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
|
||||
ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
|
||||
Mockito.verify(mockListener, Mockito.times(2)).onEvent(eventArgumentCaptor.capture());
|
||||
Assertions.assertEquals(
|
||||
2, eventArgumentCaptor.getValue().getServiceInstances().size());
|
||||
|
||||
serviceDiscovery.doUnregister(serviceInstance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPodsUpdate() throws Exception {
|
||||
serviceDiscovery.setCurrentHostname(POD_NAME);
|
||||
serviceDiscovery.setKubernetesClient(mockClient);
|
||||
|
||||
ServiceInstance serviceInstance = new DefaultServiceInstance(
|
||||
SERVICE_NAME,
|
||||
"Test",
|
||||
12345,
|
||||
ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
|
||||
|
||||
serviceDiscovery.doRegister(serviceInstance);
|
||||
|
||||
HashSet<String> serviceList = new HashSet<>(4);
|
||||
serviceList.add(SERVICE_NAME);
|
||||
Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
|
||||
Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
|
||||
|
||||
serviceDiscovery.addServiceInstancesChangedListener(mockListener);
|
||||
|
||||
serviceInstance = new DefaultServiceInstance(
|
||||
SERVICE_NAME,
|
||||
"Test12345",
|
||||
12345,
|
||||
ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
|
||||
serviceDiscovery.doUpdate(serviceInstance, serviceInstance);
|
||||
|
||||
await().until(() -> {
|
||||
ArgumentCaptor<ServiceInstancesChangedEvent> captor =
|
||||
ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
|
||||
Mockito.verify(mockListener, Mockito.atLeast(0)).onEvent(captor.capture());
|
||||
return captor.getValue().getServiceInstances().size() == 1;
|
||||
});
|
||||
ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
|
||||
ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
|
||||
Mockito.verify(mockListener, Mockito.times(1)).onEvent(eventArgumentCaptor.capture());
|
||||
Assertions.assertEquals(
|
||||
1, eventArgumentCaptor.getValue().getServiceInstances().size());
|
||||
|
||||
serviceDiscovery.doUnregister(serviceInstance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testServiceUpdate() {
|
||||
serviceDiscovery.setCurrentHostname(POD_NAME);
|
||||
serviceDiscovery.setKubernetesClient(mockClient);
|
||||
|
||||
ServiceInstance serviceInstance = new DefaultServiceInstance(
|
||||
SERVICE_NAME,
|
||||
"Test",
|
||||
12345,
|
||||
ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
|
||||
|
||||
serviceDiscovery.doRegister(serviceInstance);
|
||||
|
||||
HashSet<String> serviceList = new HashSet<>(4);
|
||||
serviceList.add(SERVICE_NAME);
|
||||
Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
|
||||
Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
|
||||
|
||||
serviceDiscovery.addServiceInstancesChangedListener(mockListener);
|
||||
|
||||
selector.put("app", "test");
|
||||
mockClient.services().withName(SERVICE_NAME).edit(service -> new ServiceBuilder(service)
|
||||
.editSpec()
|
||||
.addToSelector(selector)
|
||||
.endSpec()
|
||||
.build());
|
||||
|
||||
await().until(() -> {
|
||||
ArgumentCaptor<ServiceInstancesChangedEvent> captor =
|
||||
ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
|
||||
Mockito.verify(mockListener, Mockito.atLeast(0)).onEvent(captor.capture());
|
||||
return captor.getValue().getServiceInstances().size() == 1;
|
||||
});
|
||||
ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
|
||||
ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
|
||||
Mockito.verify(mockListener, Mockito.times(1)).onEvent(eventArgumentCaptor.capture());
|
||||
Assertions.assertEquals(
|
||||
1, eventArgumentCaptor.getValue().getServiceInstances().size());
|
||||
|
||||
serviceDiscovery.doUnregister(serviceInstance);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInstance() {
|
||||
serviceDiscovery.setCurrentHostname(POD_NAME);
|
||||
serviceDiscovery.setKubernetesClient(mockClient);
|
||||
|
||||
ServiceInstance serviceInstance = new DefaultServiceInstance(
|
||||
SERVICE_NAME,
|
||||
"Test",
|
||||
12345,
|
||||
ScopeModelUtil.getApplicationModel(serviceDiscovery.getUrl().getScopeModel()));
|
||||
|
||||
serviceDiscovery.doRegister(serviceInstance);
|
||||
|
||||
serviceDiscovery.doUpdate(serviceInstance, serviceInstance);
|
||||
|
||||
Assertions.assertEquals(1, serviceDiscovery.getServices().size());
|
||||
Assertions.assertEquals(1, serviceDiscovery.getInstances(SERVICE_NAME).size());
|
||||
|
||||
serviceDiscovery.doUnregister(serviceInstance);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
mock-maker-inline
|
||||
|
|
@ -42,14 +42,14 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-core</artifactId>
|
||||
<version>3.9.1</version>
|
||||
<version>3.9.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugin-tools</groupId>
|
||||
<artifactId>maven-plugin-annotations</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>3.10.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-plugin-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>3.10.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-addPluginArtifactMetadata</id>
|
||||
|
|
|
|||
|
|
@ -105,8 +105,7 @@ public class SslServerTlsHandler extends ByteToMessageDecoder {
|
|||
url, channelHandlerContext.channel().remoteAddress());
|
||||
|
||||
if (providerConnectionConfig == null) {
|
||||
ChannelPipeline p = channelHandlerContext.pipeline();
|
||||
p.remove(this);
|
||||
channelHandlerContext.pipeline().remove(this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -117,8 +116,8 @@ public class SslServerTlsHandler extends ByteToMessageDecoder {
|
|||
}
|
||||
|
||||
if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.NONE) {
|
||||
ChannelPipeline p = channelHandlerContext.pipeline();
|
||||
p.remove(this);
|
||||
channelHandlerContext.pipeline().remove(this);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.error(INTERNAL_ERROR, "", "", "TLS negotiation failed when trying to accept new connection.");
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.stream.Stream;
|
||||
|
|
@ -73,18 +74,18 @@ public class ReflectionPackableMethod implements PackableMethod {
|
|||
case CLIENT_STREAM:
|
||||
case BI_STREAM:
|
||||
actualRequestTypes = new Class<?>[] {
|
||||
(Class<?>)
|
||||
((ParameterizedType) method.getMethod().getGenericReturnType()).getActualTypeArguments()[0]
|
||||
obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method.getMethod().getGenericReturnType()).getActualTypeArguments()[0])
|
||||
};
|
||||
actualResponseType =
|
||||
(Class<?>) ((ParameterizedType) method.getMethod().getGenericParameterTypes()[0])
|
||||
.getActualTypeArguments()[0];
|
||||
actualResponseType = obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method.getMethod().getGenericParameterTypes()[0])
|
||||
.getActualTypeArguments()[0]);
|
||||
break;
|
||||
case SERVER_STREAM:
|
||||
actualRequestTypes = method.getMethod().getParameterTypes();
|
||||
actualResponseType =
|
||||
(Class<?>) ((ParameterizedType) method.getMethod().getGenericParameterTypes()[1])
|
||||
.getActualTypeArguments()[0];
|
||||
actualResponseType = obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method.getMethod().getGenericParameterTypes()[1])
|
||||
.getActualTypeArguments()[0]);
|
||||
break;
|
||||
case UNARY:
|
||||
actualRequestTypes = method.getParameterClasses();
|
||||
|
|
@ -291,6 +292,13 @@ public class ReflectionPackableMethod implements PackableMethod {
|
|||
return serializeType;
|
||||
}
|
||||
|
||||
static Class<?> obtainActualTypeInStreamObserver(Type typeInStreamObserver) {
|
||||
return (Class<?>)
|
||||
(typeInStreamObserver instanceof ParameterizedType
|
||||
? ((ParameterizedType) typeInStreamObserver).getRawType()
|
||||
: typeInStreamObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pack getRequestPack() {
|
||||
return requestPack;
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds;
|
||||
package org.apache.dubbo.rpc.protocol.tri;
|
||||
|
||||
public interface XdsEnv {
|
||||
|
||||
String getCluster();
|
||||
public class DataWrapper<T> {
|
||||
public T data;
|
||||
}
|
||||
|
|
@ -68,6 +68,17 @@ public interface DescriptorService {
|
|||
|
||||
void sayHelloServerStream2(Object request, StreamObserver<Object> reply);
|
||||
|
||||
/**
|
||||
* obtain actual type in streamObserver
|
||||
*/
|
||||
void serverStream1(Object request, StreamObserver<String> streamObserver);
|
||||
|
||||
void serverStream2(Object request, StreamObserver<DataWrapper<String>> streamObserver);
|
||||
|
||||
StreamObserver<String> biStream1(StreamObserver<String> streamObserver);
|
||||
|
||||
StreamObserver<DataWrapper<String>> biStream2(StreamObserver<DataWrapper<String>> streamObserver);
|
||||
|
||||
/***********************grpc******************************/
|
||||
java.util.Iterator<HelloReply> iteratorServerStream(HelloReply request);
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,38 @@ class ReflectionPackableMethodTest {
|
|||
Assertions.assertTrue(needWrap(descriptor2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testObtainActualType() throws NoSuchMethodException {
|
||||
|
||||
Method method1 = DescriptorService.class.getMethod("serverStream1", Object.class, StreamObserver.class);
|
||||
Class<?> clazz1 = ReflectionPackableMethod.obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method1.getGenericParameterTypes()[1]).getActualTypeArguments()[0]);
|
||||
Assertions.assertEquals(clazz1.getName(), String.class.getName());
|
||||
|
||||
Method method2 = DescriptorService.class.getMethod("serverStream2", Object.class, StreamObserver.class);
|
||||
Class<?> clazz2 = ReflectionPackableMethod.obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method2.getGenericParameterTypes()[1]).getActualTypeArguments()[0]);
|
||||
Assertions.assertEquals(clazz2.getName(), DataWrapper.class.getName());
|
||||
|
||||
Method method3 = DescriptorService.class.getMethod("biStream1", StreamObserver.class);
|
||||
Class<?> clazz31 = ReflectionPackableMethod.obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method3.getGenericReturnType()).getActualTypeArguments()[0]);
|
||||
Assertions.assertEquals(clazz31.getName(), String.class.getName());
|
||||
|
||||
Class<?> clazz32 = ReflectionPackableMethod.obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method3.getGenericParameterTypes()[0]).getActualTypeArguments()[0]);
|
||||
Assertions.assertEquals(clazz32.getName(), String.class.getName());
|
||||
|
||||
Method method4 = DescriptorService.class.getMethod("biStream2", StreamObserver.class);
|
||||
Class<?> clazz41 = ReflectionPackableMethod.obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method4.getGenericReturnType()).getActualTypeArguments()[0]);
|
||||
Assertions.assertEquals(clazz41.getName(), DataWrapper.class.getName());
|
||||
|
||||
Class<?> clazz42 = ReflectionPackableMethod.obtainActualTypeInStreamObserver(
|
||||
((ParameterizedType) method4.getGenericParameterTypes()[0]).getActualTypeArguments()[0]);
|
||||
Assertions.assertEquals(clazz42.getName(), DataWrapper.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsNeedWrap() throws NoSuchMethodException {
|
||||
Method method = DescriptorService.class.getMethod("noParameterMethod");
|
||||
|
|
|
|||
|
|
@ -14,26 +14,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
package org.apache.dubbo.common.serialize;
|
||||
|
||||
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
|
||||
import org.apache.dubbo.common.serialize.support.PreferSerializationProviderImpl;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
import org.apache.dubbo.rpc.model.ModuleModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModelInitializer;
|
||||
|
||||
public class XdsScopeModelInitializer implements ScopeModelInitializer {
|
||||
|
||||
public class SerializationScopeModelInitializer implements ScopeModelInitializer {
|
||||
@Override
|
||||
public void initializeFrameworkModel(FrameworkModel frameworkModel) {}
|
||||
|
||||
@Override
|
||||
public void initializeApplicationModel(ApplicationModel applicationModel) {
|
||||
ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
|
||||
beanFactory.registerBean(RdsRouteRuleManager.class);
|
||||
beanFactory.registerBean(EdsEndpointManager.class);
|
||||
public void initializeFrameworkModel(FrameworkModel frameworkModel) {
|
||||
frameworkModel.getBeanFactory().registerBean(PreferSerializationProviderImpl.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeApplicationModel(ApplicationModel applicationModel) {}
|
||||
|
||||
@Override
|
||||
public void initializeModuleModel(ModuleModel moduleModel) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.common.serialize.support;
|
||||
|
||||
import org.apache.dubbo.common.serialization.PreferSerializationProvider;
|
||||
import org.apache.dubbo.common.serialize.Serialization;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PreferSerializationProviderImpl implements PreferSerializationProvider {
|
||||
private final String preferSerialization;
|
||||
|
||||
public PreferSerializationProviderImpl(FrameworkModel frameworkModel) {
|
||||
List<String> defaultSerializations = Arrays.asList("fastjson2", "hessian2");
|
||||
this.preferSerialization = defaultSerializations.stream()
|
||||
.filter(s ->
|
||||
frameworkModel.getExtensionLoader(Serialization.class).hasExtension(s))
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferSerialization() {
|
||||
return preferSerialization;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
serialization-api=org.apache.dubbo.common.serialize.SerializationScopeModelInitializer
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
package org.apache.dubbo.common.serialize.fastjson2;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.serialize.ObjectInput;
|
||||
import org.apache.dubbo.common.serialize.ObjectOutput;
|
||||
import org.apache.dubbo.common.serialize.Serialization;
|
||||
|
|
@ -37,6 +39,19 @@ import static org.apache.dubbo.common.serialize.Constants.FASTJSON2_SERIALIZATIO
|
|||
* </pre>
|
||||
*/
|
||||
public class FastJson2Serialization implements Serialization {
|
||||
private static final Logger logger = LoggerFactory.getLogger(FastJson2Serialization.class);
|
||||
|
||||
static {
|
||||
Class<?> aClass = null;
|
||||
try {
|
||||
aClass = com.alibaba.fastjson2.JSONB.class;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (aClass == null) {
|
||||
logger.info("Failed to load com.alibaba.fastjson2.JSONB, fastjson2 serialization will be disabled.");
|
||||
throw new IllegalStateException("The fastjson2 is not in classpath.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getContentTypeId() {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,17 @@ public class Fastjson2ScopeModelInitializer implements ScopeModelInitializer {
|
|||
|
||||
@Override
|
||||
public void initializeFrameworkModel(FrameworkModel frameworkModel) {
|
||||
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
|
||||
beanFactory.registerBean(Fastjson2CreatorManager.class);
|
||||
beanFactory.registerBean(Fastjson2SecurityManager.class);
|
||||
Class<?> aClass = null;
|
||||
try {
|
||||
aClass = com.alibaba.fastjson2.JSONB.class;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
if (aClass != null) {
|
||||
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
|
||||
beanFactory.registerBean(Fastjson2CreatorManager.class);
|
||||
beanFactory.registerBean(Fastjson2SecurityManager.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -25,10 +25,18 @@ import org.apache.dubbo.rpc.model.ScopeModelInitializer;
|
|||
public class Hessian2ScopeModelInitializer implements ScopeModelInitializer {
|
||||
@Override
|
||||
public void initializeFrameworkModel(FrameworkModel frameworkModel) {
|
||||
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
|
||||
beanFactory.registerBean(Hessian2FactoryManager.class);
|
||||
Class<?> aClass = null;
|
||||
try {
|
||||
aClass = com.alibaba.com.caucho.hessian.io.Hessian2Output.class;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
frameworkModel.addClassLoaderListener(new Hessian2ClassLoaderListener());
|
||||
if (aClass != null) {
|
||||
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
|
||||
beanFactory.registerBean(Hessian2FactoryManager.class);
|
||||
|
||||
frameworkModel.addClassLoaderListener(new Hessian2ClassLoaderListener());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
package org.apache.dubbo.common.serialize.hessian2;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.serialize.ObjectInput;
|
||||
import org.apache.dubbo.common.serialize.ObjectOutput;
|
||||
import org.apache.dubbo.common.serialize.Serialization;
|
||||
|
|
@ -37,6 +39,20 @@ import static org.apache.dubbo.common.serialize.Constants.HESSIAN2_SERIALIZATION
|
|||
* </pre>
|
||||
*/
|
||||
public class Hessian2Serialization implements Serialization {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Hessian2Serialization.class);
|
||||
|
||||
static {
|
||||
Class<?> aClass = null;
|
||||
try {
|
||||
aClass = com.alibaba.com.caucho.hessian.io.Hessian2Output.class;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (aClass == null) {
|
||||
logger.info(
|
||||
"Failed to load com.alibaba.com.caucho.hessian.io.Hessian2Output, hessian2 serialization will be disabled.");
|
||||
throw new IllegalStateException("The hessian2 is not in classpath.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getContentTypeId() {
|
||||
|
|
|
|||
|
|
@ -118,13 +118,6 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- kubernetes -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-kubernetes</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- metadata -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
|
|
@ -603,11 +596,5 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- xds -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
<curator.test.version>4.2.0</curator.test.version>
|
||||
<zookeeper.version>3.7.2</zookeeper.version>
|
||||
<curator5.version>4.2.0</curator5.version>
|
||||
<commons.compress.version>1.24.0</commons.compress.version>
|
||||
<commons.compress.version>1.25.0</commons.compress.version>
|
||||
<junit.platform.launcher.version>1.9.3</junit.platform.launcher.version>
|
||||
<commons.exec.version>1.3</commons.exec.version>
|
||||
<async.http.client.version>2.12.3</async.http.client.version>
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>The xDS Integration</description>
|
||||
<properties>
|
||||
<skip_maven_deploy>false</skip_maven_deploy>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty-shaded</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.envoyproxy.controlplane</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java-util</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-ext-jdk15on</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<version>${maven_protobuf_plugin_version}</version>
|
||||
<configuration>
|
||||
<protocArtifact>com.google.protobuf:protoc:${protobuf-protoc_version}:exe:${os.detected.classifier}</protocArtifact>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc_version}:exe:${os.detected.classifier}</pluginArtifact>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>compile-custom</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Override the maven-javadoc-plugin configuration that depends on the pass -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>${maven_os_plugin_version}</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Adaptive;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
|
||||
@SPI
|
||||
public interface XdsCertificateSigner {
|
||||
|
||||
@Adaptive(value = "signer")
|
||||
CertPair GenerateCert(URL url);
|
||||
|
||||
class CertPair {
|
||||
private final String privateKey;
|
||||
private final String publicKey;
|
||||
private final long createTime;
|
||||
private final long expireTime;
|
||||
|
||||
public CertPair(String privateKey, String publicKey, long createTime, long expireTime) {
|
||||
this.privateKey = privateKey;
|
||||
this.publicKey = publicKey;
|
||||
this.createTime = createTime;
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public String getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public String getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public boolean isExpire() {
|
||||
return System.currentTimeMillis() < expireTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.registry.NotifyListener;
|
||||
import org.apache.dubbo.registry.support.FailbackRegistry;
|
||||
|
||||
/**
|
||||
* Empty implements for xDS <br/>
|
||||
* xDS only support `Service Discovery` mode register <br/>
|
||||
* Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
|
||||
* {@link XdsServiceDiscovery} is the real implementation of xDS
|
||||
*/
|
||||
public class XdsRegistry extends FailbackRegistry {
|
||||
public XdsRegistry(URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRegister(URL url) {}
|
||||
|
||||
@Override
|
||||
public void doUnregister(URL url) {}
|
||||
|
||||
@Override
|
||||
public void doSubscribe(URL url, NotifyListener listener) {}
|
||||
|
||||
@Override
|
||||
public void doUnsubscribe(URL url, NotifyListener listener) {}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.registry.Registry;
|
||||
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
|
||||
|
||||
public class XdsRegistryFactory extends AbstractRegistryFactory {
|
||||
|
||||
@Override
|
||||
protected String createRegistryCacheKey(URL url) {
|
||||
return url.toFullString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Registry createRegistry(URL url) {
|
||||
return new XdsRegistry(url);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.registry.client.DefaultServiceInstance;
|
||||
import org.apache.dubbo.registry.client.ReflectionBasedServiceDiscovery;
|
||||
import org.apache.dubbo.registry.client.ServiceInstance;
|
||||
import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
|
||||
import org.apache.dubbo.registry.xds.util.PilotExchanger;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModelUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_INITIALIZE_XDS;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_PARSING_XDS;
|
||||
|
||||
public class XdsServiceDiscovery extends ReflectionBasedServiceDiscovery {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(XdsServiceDiscovery.class);
|
||||
|
||||
private PilotExchanger exchanger;
|
||||
|
||||
public XdsServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
|
||||
super(applicationModel, registryURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doInitialize(URL registryURL) {
|
||||
try {
|
||||
exchanger = PilotExchanger.initialize(registryURL);
|
||||
} catch (Throwable t) {
|
||||
logger.error(REGISTRY_ERROR_INITIALIZE_XDS, "", "", t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDestroy() {
|
||||
try {
|
||||
if (exchanger == null) {
|
||||
return;
|
||||
}
|
||||
exchanger.destroy();
|
||||
} catch (Throwable t) {
|
||||
logger.error(REGISTRY_ERROR_INITIALIZE_XDS, "", "", t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getServices() {
|
||||
return exchanger.getServices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
|
||||
Set<Endpoint> endpoints = exchanger.getEndpoints(serviceName);
|
||||
return changedToInstances(serviceName, endpoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener)
|
||||
throws NullPointerException, IllegalArgumentException {
|
||||
listener.getServiceNames()
|
||||
.forEach(serviceName -> exchanger.observeEndpoints(
|
||||
serviceName,
|
||||
(endpoints ->
|
||||
notifyListener(serviceName, listener, changedToInstances(serviceName, endpoints)))));
|
||||
}
|
||||
|
||||
private List<ServiceInstance> changedToInstances(String serviceName, Collection<Endpoint> endpoints) {
|
||||
List<ServiceInstance> instances = new LinkedList<>();
|
||||
endpoints.forEach(endpoint -> {
|
||||
try {
|
||||
DefaultServiceInstance serviceInstance = new DefaultServiceInstance(
|
||||
serviceName,
|
||||
endpoint.getAddress(),
|
||||
endpoint.getPortValue(),
|
||||
ScopeModelUtil.getApplicationModel(getUrl().getScopeModel()));
|
||||
// fill metadata by SelfHostMetaServiceDiscovery, will be fetched by RPC request
|
||||
serviceInstance.putExtendParam("clusterName", endpoint.getClusterName());
|
||||
fillServiceInstance(serviceInstance);
|
||||
instances.add(serviceInstance);
|
||||
} catch (Throwable t) {
|
||||
logger.error(
|
||||
REGISTRY_ERROR_PARSING_XDS,
|
||||
"",
|
||||
"",
|
||||
"Error occurred when parsing endpoints. Endpoints List:" + endpoints,
|
||||
t);
|
||||
}
|
||||
});
|
||||
instances.sort(Comparator.comparingInt(ServiceInstance::hashCode));
|
||||
return instances;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
|
||||
import org.apache.dubbo.registry.client.ServiceDiscovery;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_INITIALIZE_XDS;
|
||||
|
||||
public class XdsServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger =
|
||||
LoggerFactory.getErrorTypeAwareLogger(XdsServiceDiscoveryFactory.class);
|
||||
|
||||
@Override
|
||||
protected ServiceDiscovery createDiscovery(URL registryURL) {
|
||||
XdsServiceDiscovery xdsServiceDiscovery = new XdsServiceDiscovery(ApplicationModel.defaultModel(), registryURL);
|
||||
try {
|
||||
xdsServiceDiscovery.doInitialize(registryURL);
|
||||
} catch (Exception e) {
|
||||
logger.error(
|
||||
REGISTRY_ERROR_INITIALIZE_XDS,
|
||||
"",
|
||||
"",
|
||||
"Error occurred when initialize xDS service discovery impl.",
|
||||
e);
|
||||
}
|
||||
return xdsServiceDiscovery;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.istio;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.registry.xds.XdsCertificateSigner;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
|
||||
import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.grpc.stub.MetadataUtils;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import istio.v1.auth.IstioCertificateRequest;
|
||||
import istio.v1.auth.IstioCertificateResponse;
|
||||
import istio.v1.auth.IstioCertificateServiceGrpc;
|
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
|
||||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_GENERATE_CERT_ISTIO;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_GENERATE_KEY_ISTIO;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_RECEIVE_ERROR_MSG_ISTIO;
|
||||
|
||||
public class IstioCitadelCertificateSigner implements XdsCertificateSigner {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger =
|
||||
LoggerFactory.getErrorTypeAwareLogger(IstioCitadelCertificateSigner.class);
|
||||
|
||||
private final org.apache.dubbo.registry.xds.istio.IstioEnv istioEnv;
|
||||
|
||||
private CertPair certPair;
|
||||
|
||||
public IstioCitadelCertificateSigner() {
|
||||
// watch cert, Refresh every 30s
|
||||
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
|
||||
scheduledThreadPool.scheduleAtFixedRate(new GenerateCertTask(), 0, 30, TimeUnit.SECONDS);
|
||||
istioEnv = IstioEnv.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CertPair GenerateCert(URL url) {
|
||||
|
||||
if (certPair != null && !certPair.isExpire()) {
|
||||
return certPair;
|
||||
}
|
||||
return doGenerateCert();
|
||||
}
|
||||
|
||||
private class GenerateCertTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
doGenerateCert();
|
||||
}
|
||||
}
|
||||
|
||||
private CertPair doGenerateCert() {
|
||||
synchronized (this) {
|
||||
if (certPair == null || certPair.isExpire()) {
|
||||
try {
|
||||
certPair = createCert();
|
||||
} catch (IOException e) {
|
||||
logger.error(REGISTRY_FAILED_GENERATE_CERT_ISTIO, "", "", "Generate Cert from Istio failed.", e);
|
||||
throw new RpcException("Generate Cert from Istio failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return certPair;
|
||||
}
|
||||
|
||||
public CertPair createCert() throws IOException {
|
||||
PublicKey publicKey = null;
|
||||
PrivateKey privateKey = null;
|
||||
ContentSigner signer = null;
|
||||
|
||||
if (istioEnv.isECCFirst()) {
|
||||
try {
|
||||
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
|
||||
KeyPairGenerator g = KeyPairGenerator.getInstance("EC");
|
||||
g.initialize(ecSpec, new SecureRandom());
|
||||
KeyPair keypair = g.generateKeyPair();
|
||||
publicKey = keypair.getPublic();
|
||||
privateKey = keypair.getPrivate();
|
||||
signer = new JcaContentSignerBuilder("SHA256withECDSA").build(keypair.getPrivate());
|
||||
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | OperatorCreationException e) {
|
||||
logger.error(
|
||||
REGISTRY_FAILED_GENERATE_KEY_ISTIO,
|
||||
"",
|
||||
"",
|
||||
"Generate Key with secp256r1 algorithm failed. Please check if your system support. "
|
||||
+ "Will attempt to generate with RSA2048.",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
if (publicKey == null) {
|
||||
try {
|
||||
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
kpGenerator.initialize(istioEnv.getRasKeySize());
|
||||
KeyPair keypair = kpGenerator.generateKeyPair();
|
||||
publicKey = keypair.getPublic();
|
||||
privateKey = keypair.getPrivate();
|
||||
signer = new JcaContentSignerBuilder("SHA256WithRSA").build(keypair.getPrivate());
|
||||
} catch (NoSuchAlgorithmException | OperatorCreationException e) {
|
||||
logger.error(
|
||||
REGISTRY_FAILED_GENERATE_KEY_ISTIO,
|
||||
"",
|
||||
"",
|
||||
"Generate Key with SHA256WithRSA algorithm failed. Please check if your system support.",
|
||||
e);
|
||||
throw new RpcException(e);
|
||||
}
|
||||
}
|
||||
|
||||
String csr = generateCsr(publicKey, signer);
|
||||
String caCert = istioEnv.getCaCert();
|
||||
ManagedChannel channel;
|
||||
if (StringUtils.isNotEmpty(caCert)) {
|
||||
channel = NettyChannelBuilder.forTarget(istioEnv.getCaAddr())
|
||||
.sslContext(GrpcSslContexts.forClient()
|
||||
.trustManager(new ByteArrayInputStream(caCert.getBytes(StandardCharsets.UTF_8)))
|
||||
.build())
|
||||
.build();
|
||||
} else {
|
||||
channel = NettyChannelBuilder.forTarget(istioEnv.getCaAddr())
|
||||
.sslContext(GrpcSslContexts.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
Metadata header = new Metadata();
|
||||
Metadata.Key<String> key = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
|
||||
header.put(key, "Bearer " + istioEnv.getServiceAccount());
|
||||
|
||||
key = Metadata.Key.of("ClusterID", Metadata.ASCII_STRING_MARSHALLER);
|
||||
header.put(key, istioEnv.getIstioMetaClusterId());
|
||||
|
||||
IstioCertificateServiceGrpc.IstioCertificateServiceStub stub = IstioCertificateServiceGrpc.newStub(channel);
|
||||
|
||||
stub = stub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(header));
|
||||
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
StringBuffer publicKeyBuilder = new StringBuffer();
|
||||
AtomicBoolean failed = new AtomicBoolean(false);
|
||||
stub.createCertificate(
|
||||
generateRequest(csr), generateResponseObserver(countDownLatch, publicKeyBuilder, failed));
|
||||
|
||||
long expireTime =
|
||||
System.currentTimeMillis() + (long) (istioEnv.getSecretTTL() * istioEnv.getSecretGracePeriodRatio());
|
||||
|
||||
try {
|
||||
countDownLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RpcException("Generate Cert Failed. Wait for cert failed.", e);
|
||||
}
|
||||
|
||||
if (failed.get()) {
|
||||
throw new RpcException("Generate Cert Failed. Send csr request failed. Please check log above.");
|
||||
}
|
||||
|
||||
String privateKeyPem = generatePrivatePemKey(privateKey);
|
||||
CertPair certPair =
|
||||
new CertPair(privateKeyPem, publicKeyBuilder.toString(), System.currentTimeMillis(), expireTime);
|
||||
|
||||
channel.shutdown();
|
||||
return certPair;
|
||||
}
|
||||
|
||||
private IstioCertificateRequest generateRequest(String csr) {
|
||||
return IstioCertificateRequest.newBuilder()
|
||||
.setCsr(csr)
|
||||
.setValidityDuration(istioEnv.getSecretTTL())
|
||||
.build();
|
||||
}
|
||||
|
||||
private StreamObserver<IstioCertificateResponse> generateResponseObserver(
|
||||
CountDownLatch countDownLatch, StringBuffer publicKeyBuilder, AtomicBoolean failed) {
|
||||
return new StreamObserver<IstioCertificateResponse>() {
|
||||
@Override
|
||||
public void onNext(IstioCertificateResponse istioCertificateResponse) {
|
||||
for (int i = 0; i < istioCertificateResponse.getCertChainCount(); i++) {
|
||||
publicKeyBuilder.append(
|
||||
istioCertificateResponse.getCertChainBytes(i).toStringUtf8());
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Receive Cert chain from Istio Citadel. \n" + publicKeyBuilder);
|
||||
}
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
failed.set(true);
|
||||
logger.error(
|
||||
REGISTRY_RECEIVE_ERROR_MSG_ISTIO,
|
||||
"",
|
||||
"",
|
||||
"Receive error message from Istio Citadel grpc stub.",
|
||||
throwable);
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String generatePrivatePemKey(PrivateKey privateKey) throws IOException {
|
||||
String key = generatePemKey("RSA PRIVATE KEY", privateKey.getEncoded());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Generated Private Key. \n" + key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private String generatePemKey(String type, byte[] content) throws IOException {
|
||||
PemObject pemObject = new PemObject(type, content);
|
||||
StringWriter str = new StringWriter();
|
||||
JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(str);
|
||||
jcaPEMWriter.writeObject(pemObject);
|
||||
jcaPEMWriter.close();
|
||||
str.close();
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
private String generateCsr(PublicKey publicKey, ContentSigner signer) throws IOException {
|
||||
GeneralNames subjectAltNames = new GeneralNames(new GeneralName[] {new GeneralName(6, istioEnv.getCsrHost())});
|
||||
|
||||
ExtensionsGenerator extGen = new ExtensionsGenerator();
|
||||
extGen.addExtension(Extension.subjectAlternativeName, true, subjectAltNames);
|
||||
|
||||
PKCS10CertificationRequest request = new JcaPKCS10CertificationRequestBuilder(
|
||||
new X500Name("O=" + istioEnv.getTrustDomain()), publicKey)
|
||||
.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate())
|
||||
.build(signer);
|
||||
|
||||
String csr = generatePemKey("CERTIFICATE REQUEST", request.getEncoded());
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("CSR Request to Istio Citadel. \n" + csr);
|
||||
}
|
||||
return csr;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.istio;
|
||||
|
||||
public class IstioConstant {
|
||||
/**
|
||||
* Address of the spiffe certificate provider. Defaults to discoveryAddress
|
||||
*/
|
||||
public static final String CA_ADDR_KEY = "CA_ADDR";
|
||||
|
||||
/**
|
||||
* CA and xDS services
|
||||
*/
|
||||
public static final String DEFAULT_CA_ADDR = "istiod.istio-system.svc:15012";
|
||||
|
||||
/**
|
||||
* The trust domain for spiffe certificates
|
||||
*/
|
||||
public static final String TRUST_DOMAIN_KEY = "TRUST_DOMAIN";
|
||||
|
||||
/**
|
||||
* The trust domain for spiffe certificates default value
|
||||
*/
|
||||
public static final String DEFAULT_TRUST_DOMAIN = "cluster.local";
|
||||
|
||||
public static final String WORKLOAD_NAMESPACE_KEY = "WORKLOAD_NAMESPACE";
|
||||
|
||||
public static final String DEFAULT_WORKLOAD_NAMESPACE = "default";
|
||||
|
||||
/**
|
||||
* k8s jwt token
|
||||
*/
|
||||
public static final String KUBERNETES_SA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
|
||||
|
||||
public static final String KUBERNETES_CA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
|
||||
|
||||
public static final String ISTIO_SA_PATH = "/var/run/secrets/tokens/istio-token";
|
||||
|
||||
public static final String ISTIO_CA_PATH = "/var/run/secrets/istio/root-cert.pem";
|
||||
|
||||
public static final String KUBERNETES_NAMESPACE_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
|
||||
|
||||
public static final String RSA_KEY_SIZE_KEY = "RSA_KEY_SIZE";
|
||||
|
||||
public static final String DEFAULT_RSA_KEY_SIZE = "2048";
|
||||
|
||||
/**
|
||||
* The type of ECC signature algorithm to use when generating private keys
|
||||
*/
|
||||
public static final String ECC_SIG_ALG_KEY = "ECC_SIGNATURE_ALGORITHM";
|
||||
|
||||
public static final String DEFAULT_ECC_SIG_ALG = "ECDSA";
|
||||
|
||||
/**
|
||||
* The cert lifetime requested by istio agent
|
||||
*/
|
||||
public static final String SECRET_TTL_KEY = "SECRET_TTL";
|
||||
|
||||
/**
|
||||
* The cert lifetime default value 24h0m0s
|
||||
*/
|
||||
public static final String DEFAULT_SECRET_TTL = "86400"; // 24 * 60 * 60
|
||||
|
||||
/**
|
||||
* The grace period ratio for the cert rotation
|
||||
*/
|
||||
public static final String SECRET_GRACE_PERIOD_RATIO_KEY = "SECRET_GRACE_PERIOD_RATIO";
|
||||
|
||||
/**
|
||||
* The grace period ratio for the cert rotation, by default 0.5
|
||||
*/
|
||||
public static final String DEFAULT_SECRET_GRACE_PERIOD_RATIO = "0.5";
|
||||
|
||||
public static final String ISTIO_META_CLUSTER_ID_KEY = "ISTIO_META_CLUSTER_ID";
|
||||
|
||||
public static final String PILOT_CERT_PROVIDER_KEY = "PILOT_CERT_PROVIDER";
|
||||
|
||||
public static final String ISTIO_PILOT_CERT_PROVIDER = "istiod";
|
||||
|
||||
public static final String DEFAULT_ISTIO_META_CLUSTER_ID = "Kubernetes";
|
||||
|
||||
public static final String SPIFFE = "spiffe://";
|
||||
|
||||
public static final String NS = "/ns/";
|
||||
|
||||
public static final String SA = "/sa/";
|
||||
|
||||
public static final String JWT_POLICY = "JWT_POLICY";
|
||||
|
||||
public static final String DEFAULT_JWT_POLICY = "first-party-jwt";
|
||||
|
||||
public static final String FIRST_PARTY_JWT = "first-party-jwt";
|
||||
|
||||
public static final String THIRD_PARTY_JWT = "third-party-jwt";
|
||||
}
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.istio;
|
||||
|
||||
import org.apache.dubbo.common.constants.LoggerCodeConstants;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.registry.xds.XdsEnv;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_READ_FILE_ISTIO;
|
||||
import static org.apache.dubbo.registry.xds.istio.IstioConstant.NS;
|
||||
import static org.apache.dubbo.registry.xds.istio.IstioConstant.SA;
|
||||
import static org.apache.dubbo.registry.xds.istio.IstioConstant.SPIFFE;
|
||||
|
||||
public class IstioEnv implements XdsEnv {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(IstioEnv.class);
|
||||
|
||||
private static final IstioEnv INSTANCE = new IstioEnv();
|
||||
|
||||
private String podName;
|
||||
|
||||
private String caAddr;
|
||||
|
||||
private String jwtPolicy;
|
||||
|
||||
private String trustDomain;
|
||||
|
||||
private String workloadNameSpace;
|
||||
|
||||
private int rasKeySize;
|
||||
|
||||
private String eccSigAlg;
|
||||
|
||||
private int secretTTL;
|
||||
|
||||
private float secretGracePeriodRatio;
|
||||
|
||||
private String istioMetaClusterId;
|
||||
|
||||
private String pilotCertProvider;
|
||||
|
||||
private IstioEnv() {
|
||||
jwtPolicy =
|
||||
Optional.ofNullable(System.getenv(IstioConstant.JWT_POLICY)).orElse(IstioConstant.DEFAULT_JWT_POLICY);
|
||||
podName = Optional.ofNullable(System.getenv("POD_NAME")).orElse(System.getenv("HOSTNAME"));
|
||||
trustDomain = Optional.ofNullable(System.getenv(IstioConstant.TRUST_DOMAIN_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_TRUST_DOMAIN);
|
||||
workloadNameSpace = Optional.ofNullable(System.getenv(IstioConstant.WORKLOAD_NAMESPACE_KEY))
|
||||
.orElseGet(() -> {
|
||||
File namespaceFile = new File(IstioConstant.KUBERNETES_NAMESPACE_PATH);
|
||||
if (namespaceFile.canRead()) {
|
||||
try {
|
||||
return FileUtils.readFileToString(namespaceFile, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.error(REGISTRY_ERROR_READ_FILE_ISTIO, "", "", "read namespace file error", e);
|
||||
}
|
||||
}
|
||||
return IstioConstant.DEFAULT_WORKLOAD_NAMESPACE;
|
||||
});
|
||||
caAddr = Optional.ofNullable(System.getenv(IstioConstant.CA_ADDR_KEY)).orElse(IstioConstant.DEFAULT_CA_ADDR);
|
||||
rasKeySize = Integer.parseInt(Optional.ofNullable(System.getenv(IstioConstant.RSA_KEY_SIZE_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_RSA_KEY_SIZE));
|
||||
eccSigAlg = Optional.ofNullable(System.getenv(IstioConstant.ECC_SIG_ALG_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_ECC_SIG_ALG);
|
||||
secretTTL = Integer.parseInt(Optional.ofNullable(System.getenv(IstioConstant.SECRET_TTL_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_SECRET_TTL));
|
||||
secretGracePeriodRatio =
|
||||
Float.parseFloat(Optional.ofNullable(System.getenv(IstioConstant.SECRET_GRACE_PERIOD_RATIO_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_SECRET_GRACE_PERIOD_RATIO));
|
||||
istioMetaClusterId = Optional.ofNullable(System.getenv(IstioConstant.ISTIO_META_CLUSTER_ID_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_ISTIO_META_CLUSTER_ID);
|
||||
pilotCertProvider = Optional.ofNullable(System.getenv(IstioConstant.PILOT_CERT_PROVIDER_KEY))
|
||||
.orElse("");
|
||||
|
||||
if (getServiceAccount() == null) {
|
||||
throw new UnsupportedOperationException("Unable to found kubernetes service account token file. "
|
||||
+ "Please check if work in Kubernetes and mount service account token file correctly.");
|
||||
}
|
||||
}
|
||||
|
||||
public static IstioEnv getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public String getPodName() {
|
||||
return podName;
|
||||
}
|
||||
|
||||
public String getCaAddr() {
|
||||
return caAddr;
|
||||
}
|
||||
|
||||
public String getServiceAccount() {
|
||||
File saFile;
|
||||
switch (jwtPolicy) {
|
||||
case IstioConstant.FIRST_PARTY_JWT:
|
||||
saFile = new File(IstioConstant.KUBERNETES_SA_PATH);
|
||||
break;
|
||||
case IstioConstant.THIRD_PARTY_JWT:
|
||||
default:
|
||||
saFile = new File(IstioConstant.ISTIO_SA_PATH);
|
||||
}
|
||||
if (saFile.canRead()) {
|
||||
try {
|
||||
return FileUtils.readFileToString(saFile, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.error(
|
||||
LoggerCodeConstants.REGISTRY_ISTIO_EXCEPTION,
|
||||
"File Read Failed",
|
||||
"",
|
||||
"Unable to read token file.",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCsrHost() {
|
||||
// spiffe://<trust_domain>/ns/<namespace>/sa/<service_account>
|
||||
return SPIFFE + trustDomain + NS + workloadNameSpace + SA + getServiceAccount();
|
||||
}
|
||||
|
||||
public String getTrustDomain() {
|
||||
return trustDomain;
|
||||
}
|
||||
|
||||
public String getWorkloadNameSpace() {
|
||||
return workloadNameSpace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCluster() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getRasKeySize() {
|
||||
return rasKeySize;
|
||||
}
|
||||
|
||||
public boolean isECCFirst() {
|
||||
return IstioConstant.DEFAULT_ECC_SIG_ALG.equals(eccSigAlg);
|
||||
}
|
||||
|
||||
public int getSecretTTL() {
|
||||
return secretTTL;
|
||||
}
|
||||
|
||||
public float getSecretGracePeriodRatio() {
|
||||
return secretGracePeriodRatio;
|
||||
}
|
||||
|
||||
public String getIstioMetaClusterId() {
|
||||
return istioMetaClusterId;
|
||||
}
|
||||
|
||||
public String getCaCert() {
|
||||
File caFile;
|
||||
if (IstioConstant.ISTIO_PILOT_CERT_PROVIDER.equals(pilotCertProvider)) {
|
||||
caFile = new File(IstioConstant.ISTIO_CA_PATH);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (caFile.canRead()) {
|
||||
try {
|
||||
return FileUtils.readFileToString(caFile, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.error(
|
||||
LoggerCodeConstants.REGISTRY_ISTIO_EXCEPTION, "File Read Failed", "", "read ca file error", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.DeltaResource;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_REQUEST_XDS;
|
||||
|
||||
public class AdsObserver {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AdsObserver.class);
|
||||
private final ApplicationModel applicationModel;
|
||||
private final URL url;
|
||||
private final Node node;
|
||||
private volatile XdsChannel xdsChannel;
|
||||
|
||||
private final Map<String, XdsListener> listeners = new ConcurrentHashMap<>();
|
||||
|
||||
protected StreamObserver<DiscoveryRequest> requestObserver;
|
||||
|
||||
private final Map<String, DiscoveryRequest> observedResources = new ConcurrentHashMap<>();
|
||||
|
||||
public AdsObserver(URL url, Node node) {
|
||||
this.url = url;
|
||||
this.node = node;
|
||||
this.xdsChannel = new XdsChannel(url);
|
||||
this.applicationModel = url.getOrDefaultApplicationModel();
|
||||
}
|
||||
|
||||
public <T, S extends DeltaResource<T>> void addListener(AbstractProtocol<T, S> protocol) {
|
||||
listeners.put(protocol.getTypeUrl(), protocol);
|
||||
}
|
||||
|
||||
public void request(DiscoveryRequest discoveryRequest) {
|
||||
if (requestObserver == null) {
|
||||
requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(this));
|
||||
}
|
||||
requestObserver.onNext(discoveryRequest);
|
||||
observedResources.put(discoveryRequest.getTypeUrl(), discoveryRequest);
|
||||
}
|
||||
|
||||
private static class ResponseObserver implements StreamObserver<DiscoveryResponse> {
|
||||
private AdsObserver adsObserver;
|
||||
|
||||
public ResponseObserver(AdsObserver adsObserver) {
|
||||
this.adsObserver = adsObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(DiscoveryResponse discoveryResponse) {
|
||||
XdsListener xdsListener = adsObserver.listeners.get(discoveryResponse.getTypeUrl());
|
||||
xdsListener.process(discoveryResponse);
|
||||
adsObserver.requestObserver.onNext(buildAck(discoveryResponse));
|
||||
}
|
||||
|
||||
protected DiscoveryRequest buildAck(DiscoveryResponse response) {
|
||||
// for ACK
|
||||
return DiscoveryRequest.newBuilder()
|
||||
.setNode(adsObserver.node)
|
||||
.setTypeUrl(response.getTypeUrl())
|
||||
.setVersionInfo(response.getVersionInfo())
|
||||
.setResponseNonce(response.getNonce())
|
||||
.addAllResourceNames(adsObserver
|
||||
.observedResources
|
||||
.get(response.getTypeUrl())
|
||||
.getResourceNamesList())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
logger.error(REGISTRY_ERROR_REQUEST_XDS, "", "", "xDS Client received error message! detail:", throwable);
|
||||
adsObserver.triggerReConnectTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
logger.info("xDS Client completed");
|
||||
adsObserver.triggerReConnectTask();
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerReConnectTask() {
|
||||
ScheduledExecutorService scheduledFuture = applicationModel
|
||||
.getFrameworkModel()
|
||||
.getBeanFactory()
|
||||
.getBean(FrameworkExecutorRepository.class)
|
||||
.getSharedScheduledExecutor();
|
||||
scheduledFuture.schedule(this::recover, 3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void recover() {
|
||||
try {
|
||||
xdsChannel = new XdsChannel(url);
|
||||
if (xdsChannel.getChannel() != null) {
|
||||
requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(this));
|
||||
observedResources.values().forEach(requestObserver::onNext);
|
||||
return;
|
||||
} else {
|
||||
logger.error(
|
||||
REGISTRY_ERROR_REQUEST_XDS,
|
||||
"",
|
||||
"",
|
||||
"Recover failed for xDS connection. Will retry. Create channel failed.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(REGISTRY_ERROR_REQUEST_XDS, "", "", "Recover failed for xDS connection. Will retry.", e);
|
||||
}
|
||||
triggerReConnectTask();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util;
|
||||
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.registry.xds.istio.IstioEnv;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
|
||||
public class NodeBuilder {
|
||||
|
||||
private static final String SVC_CLUSTER_LOCAL = ".svc.cluster.local";
|
||||
|
||||
public static Node build() {
|
||||
// String podName = System.getenv("metadata.name");
|
||||
// String podNamespace = System.getenv("metadata.namespace");
|
||||
|
||||
String podName = IstioEnv.getInstance().getPodName();
|
||||
String podNamespace = IstioEnv.getInstance().getWorkloadNameSpace();
|
||||
String svcName = IstioEnv.getInstance().getIstioMetaClusterId();
|
||||
|
||||
// id -> sidecar~ip~{POD_NAME}~{NAMESPACE_NAME}.svc.cluster.local
|
||||
// cluster -> {SVC_NAME}
|
||||
return Node.newBuilder()
|
||||
.setId("sidecar~" + NetUtils.getLocalHost() + "~" + podName + "~" + podNamespace + SVC_CLUSTER_LOCAL)
|
||||
.setCluster(svcName)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.impl.EdsProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.impl.LdsProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.impl.RdsProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.EndpointResult;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.ListenerResult;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.RouteResult;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.RdsVirtualHostListener;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PilotExchanger {
|
||||
|
||||
protected final XdsChannel xdsChannel;
|
||||
|
||||
protected final LdsProtocol ldsProtocol;
|
||||
|
||||
protected final RdsProtocol rdsProtocol;
|
||||
|
||||
protected final EdsProtocol edsProtocol;
|
||||
|
||||
protected Map<String, ListenerResult> listenerResult;
|
||||
|
||||
protected Map<String, RouteResult> routeResult;
|
||||
|
||||
private final AtomicBoolean isRdsObserve = new AtomicBoolean(false);
|
||||
private final Set<String> domainObserveRequest = new ConcurrentHashSet<String>();
|
||||
|
||||
private final Map<String, Set<Consumer<Set<Endpoint>>>> domainObserveConsumer = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, Consumer<RdsVirtualHostListener>> rdsObserveConsumer = new ConcurrentHashMap<>();
|
||||
|
||||
private static PilotExchanger GLOBAL_PILOT_EXCHANGER = null;
|
||||
|
||||
private final ApplicationModel applicationModel;
|
||||
|
||||
protected PilotExchanger(URL url) {
|
||||
xdsChannel = new XdsChannel(url);
|
||||
int pollingTimeout = url.getParameter("pollingTimeout", 10);
|
||||
this.applicationModel = url.getOrDefaultApplicationModel();
|
||||
AdsObserver adsObserver = new AdsObserver(url, NodeBuilder.build());
|
||||
this.ldsProtocol = new LdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout);
|
||||
this.rdsProtocol = new RdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout);
|
||||
this.edsProtocol = new EdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout);
|
||||
|
||||
this.listenerResult = ldsProtocol.getListeners();
|
||||
this.routeResult = rdsProtocol.getResource(
|
||||
listenerResult.values().iterator().next().getRouteConfigNames());
|
||||
Set<String> ldsResourcesName = new HashSet<>();
|
||||
ldsResourcesName.add(AbstractProtocol.emptyResourceName);
|
||||
// Observer RDS update
|
||||
if (CollectionUtils.isNotEmpty(listenerResult.values().iterator().next().getRouteConfigNames())) {
|
||||
createRouteObserve();
|
||||
isRdsObserve.set(true);
|
||||
}
|
||||
// Observe LDS updated
|
||||
ldsProtocol.observeResource(
|
||||
ldsResourcesName,
|
||||
(newListener) -> {
|
||||
// update local cache
|
||||
if (!newListener.equals(listenerResult)) {
|
||||
this.listenerResult = newListener;
|
||||
// update RDS observation
|
||||
if (isRdsObserve.get()) {
|
||||
createRouteObserve();
|
||||
}
|
||||
}
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
private void createRouteObserve() {
|
||||
rdsProtocol.observeResource(
|
||||
listenerResult.values().iterator().next().getRouteConfigNames(),
|
||||
(newResult) -> {
|
||||
// check if observed domain update ( will update endpoint observation )
|
||||
List<String> domainsToUpdate = new LinkedList<>();
|
||||
domainObserveConsumer.forEach((domain, consumer) -> {
|
||||
newResult.values().forEach(o -> {
|
||||
Set<String> newRoute = o.searchDomain(domain);
|
||||
for (Map.Entry<String, RouteResult> entry : routeResult.entrySet()) {
|
||||
if (!entry.getValue().searchDomain(domain).equals(newRoute)) {
|
||||
// routers in observed domain has been updated
|
||||
// Long domainRequest = domainObserveRequest.get(domain);
|
||||
// router list is empty when observeEndpoints() called and domainRequest has not
|
||||
// been created yet
|
||||
// create new observation
|
||||
domainsToUpdate.add(domain);
|
||||
// doObserveEndpoints(domain);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
routeResult = newResult;
|
||||
ExecutorService executorService = applicationModel
|
||||
.getFrameworkModel()
|
||||
.getBeanFactory()
|
||||
.getBean(FrameworkExecutorRepository.class)
|
||||
.getSharedExecutor();
|
||||
executorService.submit(() -> domainsToUpdate.forEach(this::doObserveEndpoints));
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
public static PilotExchanger initialize(URL url) {
|
||||
synchronized (PilotExchanger.class) {
|
||||
if (GLOBAL_PILOT_EXCHANGER != null) {
|
||||
return GLOBAL_PILOT_EXCHANGER;
|
||||
}
|
||||
return (GLOBAL_PILOT_EXCHANGER = new PilotExchanger(url));
|
||||
}
|
||||
}
|
||||
|
||||
public static PilotExchanger getInstance() {
|
||||
synchronized (PilotExchanger.class) {
|
||||
return GLOBAL_PILOT_EXCHANGER;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return GLOBAL_PILOT_EXCHANGER != null;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
xdsChannel.destroy();
|
||||
}
|
||||
|
||||
public Set<String> getServices() {
|
||||
Set<String> domains = new HashSet<>();
|
||||
for (Map.Entry<String, RouteResult> entry : routeResult.entrySet()) {
|
||||
domains.addAll(entry.getValue().getDomains());
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
||||
public Set<Endpoint> getEndpoints(String domain) {
|
||||
Set<Endpoint> endpoints = new HashSet<>();
|
||||
for (Map.Entry<String, RouteResult> entry : routeResult.entrySet()) {
|
||||
Set<String> cluster = entry.getValue().searchDomain(domain);
|
||||
if (CollectionUtils.isNotEmpty(cluster)) {
|
||||
Map<String, EndpointResult> endpointResultList = edsProtocol.getResource(cluster);
|
||||
endpointResultList.forEach((k, v) -> endpoints.addAll(v.getEndpoints()));
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
public void observeEndpoints(String domain, Consumer<Set<Endpoint>> consumer) {
|
||||
// store Consumer
|
||||
domainObserveConsumer.compute(domain, (k, v) -> {
|
||||
if (v == null) {
|
||||
v = new ConcurrentHashSet<>();
|
||||
}
|
||||
// support multi-consumer
|
||||
v.add(consumer);
|
||||
return v;
|
||||
});
|
||||
if (!domainObserveRequest.contains(domain)) {
|
||||
doObserveEndpoints(domain);
|
||||
}
|
||||
}
|
||||
|
||||
private void doObserveEndpoints(String domain) {
|
||||
for (Map.Entry<String, RouteResult> entry : routeResult.entrySet()) {
|
||||
Set<String> router = entry.getValue().searchDomain(domain);
|
||||
// if router is empty, do nothing
|
||||
// observation will be created when RDS updates
|
||||
if (CollectionUtils.isNotEmpty(router)) {
|
||||
edsProtocol.observeResource(
|
||||
router,
|
||||
(endpointResultMap) -> {
|
||||
Set<Endpoint> endpoints = endpointResultMap.values().stream()
|
||||
.map(EndpointResult::getEndpoints)
|
||||
.flatMap(Set::stream)
|
||||
.collect(Collectors.toSet());
|
||||
for (Consumer<Set<Endpoint>> consumer : domainObserveConsumer.get(domain)) {
|
||||
consumer.accept(endpoints);
|
||||
}
|
||||
},
|
||||
false);
|
||||
domainObserveRequest.add(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unObserveEndpoints(String domain, Consumer<Set<Endpoint>> consumer) {
|
||||
domainObserveConsumer.get(domain).remove(consumer);
|
||||
domainObserveRequest.remove(domain);
|
||||
}
|
||||
|
||||
public void observeEds(Set<String> clusterNames, Consumer<Map<String, EndpointResult>> consumer) {
|
||||
edsProtocol.observeResource(clusterNames, consumer, false);
|
||||
}
|
||||
|
||||
public void unObserveEds(Set<String> clusterNames, Consumer<Map<String, EndpointResult>> consumer) {
|
||||
edsProtocol.unobserveResource(clusterNames, consumer);
|
||||
}
|
||||
|
||||
public void observeRds(Set<String> clusterNames, Consumer<Map<String, RouteResult>> consumer) {
|
||||
rdsProtocol.observeResource(clusterNames, consumer, false);
|
||||
}
|
||||
|
||||
public void unObserveRds(Set<String> clusterNames, Consumer<Map<String, RouteResult>> consumer) {
|
||||
rdsProtocol.unobserveResource(clusterNames, consumer);
|
||||
}
|
||||
|
||||
public void observeLds(Consumer<Map<String, ListenerResult>> consumer) {
|
||||
ldsProtocol.observeResource(Collections.singleton(AbstractProtocol.emptyResourceName), consumer, false);
|
||||
}
|
||||
|
||||
public void unObserveLds(Consumer<Map<String, ListenerResult>> consumer) {
|
||||
ldsProtocol.unobserveResource(Collections.singleton(AbstractProtocol.emptyResourceName), consumer);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.url.component.URLAddress;
|
||||
import org.apache.dubbo.registry.xds.XdsCertificateSigner;
|
||||
import org.apache.dubbo.registry.xds.util.bootstrap.Bootstrapper;
|
||||
import org.apache.dubbo.registry.xds.util.bootstrap.BootstrapperImpl;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DeltaDiscoveryRequest;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DeltaDiscoveryResponse;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
|
||||
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollDomainSocketChannel;
|
||||
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.grpc.netty.shaded.io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
|
||||
import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_CREATE_CHANNEL_XDS;
|
||||
|
||||
public class XdsChannel {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(XdsChannel.class);
|
||||
|
||||
private static final String USE_AGENT = "use-agent";
|
||||
|
||||
private URL url;
|
||||
|
||||
private static final String SECURE = "secure";
|
||||
|
||||
private static final String PLAINTEXT = "plaintext";
|
||||
|
||||
private final ManagedChannel channel;
|
||||
|
||||
public URL getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public ManagedChannel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public XdsChannel(URL url) {
|
||||
ManagedChannel managedChannel = null;
|
||||
this.url = url;
|
||||
try {
|
||||
if (!url.getParameter(USE_AGENT, false)) {
|
||||
if (PLAINTEXT.equals(url.getParameter(SECURE))) {
|
||||
managedChannel = NettyChannelBuilder.forAddress(url.getHost(), url.getPort())
|
||||
.usePlaintext()
|
||||
.build();
|
||||
} else {
|
||||
XdsCertificateSigner signer = url.getOrDefaultApplicationModel()
|
||||
.getExtensionLoader(XdsCertificateSigner.class)
|
||||
.getExtension(url.getParameter("signer", "istio"));
|
||||
XdsCertificateSigner.CertPair certPair = signer.GenerateCert(url);
|
||||
SslContext context = GrpcSslContexts.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.keyManager(
|
||||
new ByteArrayInputStream(
|
||||
certPair.getPublicKey().getBytes(StandardCharsets.UTF_8)),
|
||||
new ByteArrayInputStream(
|
||||
certPair.getPrivateKey().getBytes(StandardCharsets.UTF_8)))
|
||||
.build();
|
||||
managedChannel = NettyChannelBuilder.forAddress(url.getHost(), url.getPort())
|
||||
.sslContext(context)
|
||||
.build();
|
||||
}
|
||||
} else {
|
||||
BootstrapperImpl bootstrapper = new BootstrapperImpl();
|
||||
Bootstrapper.BootstrapInfo bootstrapInfo = bootstrapper.bootstrap();
|
||||
URLAddress address =
|
||||
URLAddress.parse(bootstrapInfo.servers().get(0).target(), null, false);
|
||||
EpollEventLoopGroup elg = new EpollEventLoopGroup();
|
||||
managedChannel = NettyChannelBuilder.forAddress(new DomainSocketAddress("/" + address.getPath()))
|
||||
.eventLoopGroup(elg)
|
||||
.channelType(EpollDomainSocketChannel.class)
|
||||
.usePlaintext()
|
||||
.build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(
|
||||
REGISTRY_ERROR_CREATE_CHANNEL_XDS,
|
||||
"",
|
||||
"",
|
||||
"Error occurred when creating gRPC channel to control panel.",
|
||||
e);
|
||||
}
|
||||
channel = managedChannel;
|
||||
}
|
||||
|
||||
public StreamObserver<DeltaDiscoveryRequest> observeDeltaDiscoveryRequest(
|
||||
StreamObserver<DeltaDiscoveryResponse> observer) {
|
||||
return AggregatedDiscoveryServiceGrpc.newStub(channel).deltaAggregatedResources(observer);
|
||||
}
|
||||
|
||||
public StreamObserver<DiscoveryRequest> createDeltaDiscoveryRequest(StreamObserver<DiscoveryResponse> observer) {
|
||||
return AggregatedDiscoveryServiceGrpc.newStub(channel).streamAggregatedResources(observer);
|
||||
}
|
||||
|
||||
public StreamObserver<io.envoyproxy.envoy.api.v2.DeltaDiscoveryRequest> observeDeltaDiscoveryRequestV2(
|
||||
StreamObserver<io.envoyproxy.envoy.api.v2.DeltaDiscoveryResponse> observer) {
|
||||
return io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.newStub(channel)
|
||||
.deltaAggregatedResources(observer);
|
||||
}
|
||||
|
||||
public StreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryRequest> createDeltaDiscoveryRequestV2(
|
||||
StreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryResponse> observer) {
|
||||
return io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.newStub(channel)
|
||||
.streamAggregatedResources(observer);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
channel.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.bootstrap;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
|
||||
public final class BootstrapInfoImpl extends Bootstrapper.BootstrapInfo {
|
||||
|
||||
private final List<Bootstrapper.ServerInfo> servers;
|
||||
|
||||
private final String serverListenerResourceNameTemplate;
|
||||
|
||||
private final Map<String, Bootstrapper.CertificateProviderInfo> certProviders;
|
||||
|
||||
private final Node node;
|
||||
|
||||
BootstrapInfoImpl(
|
||||
List<Bootstrapper.ServerInfo> servers,
|
||||
String serverListenerResourceNameTemplate,
|
||||
Map<String, Bootstrapper.CertificateProviderInfo> certProviders,
|
||||
Node node) {
|
||||
this.servers = servers;
|
||||
this.serverListenerResourceNameTemplate = serverListenerResourceNameTemplate;
|
||||
this.certProviders = certProviders;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Bootstrapper.ServerInfo> servers() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
public Map<String, Bootstrapper.CertificateProviderInfo> certProviders() {
|
||||
return certProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node node() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serverListenerResourceNameTemplate() {
|
||||
return serverListenerResourceNameTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BootstrapInfo{"
|
||||
+ "servers=" + servers + ", "
|
||||
+ "serverListenerResourceNameTemplate=" + serverListenerResourceNameTemplate + ", "
|
||||
+ "node=" + node + ", "
|
||||
+ "}";
|
||||
}
|
||||
|
||||
public static final class Builder extends Bootstrapper.BootstrapInfo.Builder {
|
||||
private List<Bootstrapper.ServerInfo> servers;
|
||||
private Node node;
|
||||
|
||||
private Map<String, Bootstrapper.CertificateProviderInfo> certProviders;
|
||||
|
||||
private String serverListenerResourceNameTemplate;
|
||||
|
||||
Builder() {}
|
||||
|
||||
@Override
|
||||
Bootstrapper.BootstrapInfo.Builder servers(List<Bootstrapper.ServerInfo> servers) {
|
||||
this.servers = new LinkedList<>(servers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
Bootstrapper.BootstrapInfo.Builder node(Node node) {
|
||||
if (node == null) {
|
||||
throw new NullPointerException("Null node");
|
||||
}
|
||||
this.node = node;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
Bootstrapper.BootstrapInfo.Builder certProviders(
|
||||
@Nullable Map<String, Bootstrapper.CertificateProviderInfo> certProviders) {
|
||||
this.certProviders = certProviders;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
Bootstrapper.BootstrapInfo.Builder serverListenerResourceNameTemplate(
|
||||
@Nullable String serverListenerResourceNameTemplate) {
|
||||
this.serverListenerResourceNameTemplate = serverListenerResourceNameTemplate;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
Bootstrapper.BootstrapInfo build() {
|
||||
if (this.servers == null || this.node == null) {
|
||||
StringBuilder missing = new StringBuilder();
|
||||
if (this.servers == null) {
|
||||
missing.append(" servers");
|
||||
}
|
||||
if (this.node == null) {
|
||||
missing.append(" node");
|
||||
}
|
||||
throw new IllegalStateException("Missing required properties:" + missing);
|
||||
}
|
||||
return new BootstrapInfoImpl(
|
||||
this.servers, this.serverListenerResourceNameTemplate, this.certProviders, this.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.bootstrap;
|
||||
|
||||
import org.apache.dubbo.registry.xds.XdsInitializationException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.grpc.ChannelCredentials;
|
||||
|
||||
public abstract class Bootstrapper {
|
||||
|
||||
public abstract BootstrapInfo bootstrap() throws XdsInitializationException;
|
||||
|
||||
BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public abstract static class ServerInfo {
|
||||
public abstract String target();
|
||||
|
||||
abstract ChannelCredentials channelCredentials();
|
||||
|
||||
abstract boolean useProtocolV3();
|
||||
|
||||
abstract boolean ignoreResourceDeletion();
|
||||
}
|
||||
|
||||
public abstract static class CertificateProviderInfo {
|
||||
public abstract String pluginName();
|
||||
|
||||
public abstract Map<String, ?> config();
|
||||
}
|
||||
|
||||
public abstract static class BootstrapInfo {
|
||||
public abstract List<ServerInfo> servers();
|
||||
|
||||
public abstract Map<String, CertificateProviderInfo> certProviders();
|
||||
|
||||
public abstract Node node();
|
||||
|
||||
public abstract String serverListenerResourceNameTemplate();
|
||||
|
||||
abstract static class Builder {
|
||||
|
||||
abstract Builder servers(List<ServerInfo> servers);
|
||||
|
||||
abstract Builder node(Node node);
|
||||
|
||||
abstract Builder certProviders(@Nullable Map<String, CertificateProviderInfo> certProviders);
|
||||
|
||||
abstract Builder serverListenerResourceNameTemplate(@Nullable String serverListenerResourceNameTemplate);
|
||||
|
||||
abstract BootstrapInfo build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.bootstrap;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.registry.xds.XdsInitializationException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.internal.JsonParser;
|
||||
import io.grpc.internal.JsonUtil;
|
||||
|
||||
public class BootstrapperImpl extends Bootstrapper {
|
||||
|
||||
static final String BOOTSTRAP_PATH_SYS_ENV_VAR = "GRPC_XDS_BOOTSTRAP";
|
||||
static String bootstrapPathFromEnvVar = System.getenv(BOOTSTRAP_PATH_SYS_ENV_VAR);
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BootstrapperImpl.class);
|
||||
private FileReader reader = LocalFileReader.INSTANCE;
|
||||
|
||||
private static final String SERVER_FEATURE_XDS_V3 = "xds_v3";
|
||||
private static final String SERVER_FEATURE_IGNORE_RESOURCE_DELETION = "ignore_resource_deletion";
|
||||
|
||||
public BootstrapInfo bootstrap() throws XdsInitializationException {
|
||||
String filePath = bootstrapPathFromEnvVar;
|
||||
String fileContent = null;
|
||||
if (filePath != null) {
|
||||
try {
|
||||
fileContent = reader.readFile(filePath);
|
||||
} catch (IOException e) {
|
||||
throw new XdsInitializationException("Fail to read bootstrap file", e);
|
||||
}
|
||||
}
|
||||
if (fileContent == null) throw new XdsInitializationException("Cannot find bootstrap configuration");
|
||||
|
||||
Map<String, ?> rawBootstrap;
|
||||
try {
|
||||
rawBootstrap = (Map<String, ?>) JsonParser.parse(fileContent);
|
||||
} catch (IOException e) {
|
||||
throw new XdsInitializationException("Failed to parse JSON", e);
|
||||
}
|
||||
return bootstrap(rawBootstrap);
|
||||
}
|
||||
|
||||
@Override
|
||||
BootstrapInfo bootstrap(Map<String, ?> rawData) throws XdsInitializationException {
|
||||
BootstrapInfo.Builder builder = new BootstrapInfoImpl.Builder();
|
||||
|
||||
List<?> rawServerConfigs = JsonUtil.getList(rawData, "xds_servers");
|
||||
if (rawServerConfigs == null) {
|
||||
throw new XdsInitializationException("Invalid bootstrap: 'xds_servers' does not exist.");
|
||||
}
|
||||
List<ServerInfo> servers = parseServerInfos(rawServerConfigs);
|
||||
builder.servers(servers);
|
||||
|
||||
Node.Builder nodeBuilder = Node.newBuilder();
|
||||
Map<String, ?> rawNode = JsonUtil.getObject(rawData, "node");
|
||||
if (rawNode != null) {
|
||||
String id = JsonUtil.getString(rawNode, "id");
|
||||
if (id != null) {
|
||||
nodeBuilder.setId(id);
|
||||
}
|
||||
String cluster = JsonUtil.getString(rawNode, "cluster");
|
||||
if (cluster != null) {
|
||||
nodeBuilder.setCluster(cluster);
|
||||
}
|
||||
Map<String, ?> metadata = JsonUtil.getObject(rawNode, "metadata");
|
||||
Map<String, ?> rawLocality = JsonUtil.getObject(rawNode, "locality");
|
||||
}
|
||||
builder.node(nodeBuilder.build());
|
||||
|
||||
Map<String, ?> certProvidersBlob = JsonUtil.getObject(rawData, "certificate_providers");
|
||||
if (certProvidersBlob != null) {
|
||||
Map<String, CertificateProviderInfo> certProviders = new HashMap<>(certProvidersBlob.size());
|
||||
for (String name : certProvidersBlob.keySet()) {
|
||||
Map<String, ?> valueMap = JsonUtil.getObject(certProvidersBlob, name);
|
||||
String pluginName = checkForNull(JsonUtil.getString(valueMap, "plugin_name"), "plugin_name");
|
||||
Map<String, ?> config = checkForNull(JsonUtil.getObject(valueMap, "config"), "config");
|
||||
CertificateProviderInfoImpl certificateProviderInfo =
|
||||
new CertificateProviderInfoImpl(pluginName, config);
|
||||
certProviders.put(name, certificateProviderInfo);
|
||||
}
|
||||
builder.certProviders(certProviders);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static List<ServerInfo> parseServerInfos(List<?> rawServerConfigs) throws XdsInitializationException {
|
||||
List<ServerInfo> servers = new LinkedList<>();
|
||||
List<Map<String, ?>> serverConfigList = JsonUtil.checkObjectList(rawServerConfigs);
|
||||
for (Map<String, ?> serverConfig : serverConfigList) {
|
||||
String serverUri = JsonUtil.getString(serverConfig, "server_uri");
|
||||
if (serverUri == null) {
|
||||
throw new XdsInitializationException("Invalid bootstrap: missing 'server_uri'");
|
||||
}
|
||||
List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
|
||||
if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
|
||||
throw new XdsInitializationException(
|
||||
"Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
|
||||
}
|
||||
ChannelCredentials channelCredentials =
|
||||
parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
|
||||
// if (channelCredentials == null) {
|
||||
// throw new XdsInitializationException(
|
||||
// "Server " + serverUri + ": no supported channel credentials found");
|
||||
// }
|
||||
|
||||
boolean useProtocolV3 = false;
|
||||
boolean ignoreResourceDeletion = false;
|
||||
List<String> serverFeatures = JsonUtil.getListOfStrings(serverConfig, "server_features");
|
||||
if (serverFeatures != null) {
|
||||
useProtocolV3 = serverFeatures.contains(SERVER_FEATURE_XDS_V3);
|
||||
ignoreResourceDeletion = serverFeatures.contains(SERVER_FEATURE_IGNORE_RESOURCE_DELETION);
|
||||
}
|
||||
servers.add(new ServerInfoImpl(serverUri, channelCredentials, useProtocolV3, ignoreResourceDeletion));
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
void setFileReader(FileReader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the content of the file with the given path in the file system.
|
||||
*/
|
||||
interface FileReader {
|
||||
String readFile(String path) throws IOException;
|
||||
}
|
||||
|
||||
private enum LocalFileReader implements FileReader {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String readFile(String path) throws IOException {
|
||||
return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T checkForNull(T value, String fieldName) throws XdsInitializationException {
|
||||
if (value == null) {
|
||||
throw new XdsInitializationException("Invalid bootstrap: '" + fieldName + "' does not exist.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList, String serverUri)
|
||||
throws XdsInitializationException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.bootstrap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
final class CertificateProviderInfoImpl extends Bootstrapper.CertificateProviderInfo {
|
||||
|
||||
private final String pluginName;
|
||||
private final Map<String, ?> config;
|
||||
|
||||
CertificateProviderInfoImpl(String pluginName, Map<String, ?> config) {
|
||||
this.pluginName = pluginName;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pluginName() {
|
||||
return pluginName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CertificateProviderInfo{" + "pluginName=" + pluginName + ", " + "config=" + config + "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.bootstrap;
|
||||
|
||||
import io.grpc.ChannelCredentials;
|
||||
|
||||
final class ServerInfoImpl extends Bootstrapper.ServerInfo {
|
||||
|
||||
private final String target;
|
||||
|
||||
private final ChannelCredentials channelCredentials;
|
||||
|
||||
private final boolean useProtocolV3;
|
||||
|
||||
private final boolean ignoreResourceDeletion;
|
||||
|
||||
ServerInfoImpl(
|
||||
String target,
|
||||
ChannelCredentials channelCredentials,
|
||||
boolean useProtocolV3,
|
||||
boolean ignoreResourceDeletion) {
|
||||
this.target = target;
|
||||
this.channelCredentials = channelCredentials;
|
||||
this.useProtocolV3 = useProtocolV3;
|
||||
this.ignoreResourceDeletion = ignoreResourceDeletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String target() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
ChannelCredentials channelCredentials() {
|
||||
return channelCredentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean useProtocolV3() {
|
||||
return useProtocolV3;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean ignoreResourceDeletion() {
|
||||
return ignoreResourceDeletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerInfo{"
|
||||
+ "target=" + target + ", "
|
||||
+ "channelCredentials=" + channelCredentials + ", "
|
||||
+ "useProtocolV3=" + useProtocolV3 + ", "
|
||||
+ "ignoreResourceDeletion=" + ignoreResourceDeletion
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.registry.xds.util.AdsObserver;
|
||||
import org.apache.dubbo.registry.xds.util.XdsListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_INTERRUPTED;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REQUEST;
|
||||
|
||||
public abstract class AbstractProtocol<T, S extends DeltaResource<T>> implements XdsProtocol<T>, XdsListener {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AbstractProtocol.class);
|
||||
|
||||
protected AdsObserver adsObserver;
|
||||
|
||||
protected final Node node;
|
||||
|
||||
private final int checkInterval;
|
||||
|
||||
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
protected final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
|
||||
|
||||
protected final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
|
||||
|
||||
protected Set<String> observeResourcesName;
|
||||
|
||||
public static final String emptyResourceName = "emptyResourcesName";
|
||||
private final ReentrantLock resourceLock = new ReentrantLock();
|
||||
|
||||
protected Map<Set<String>, List<Consumer<Map<String, T>>>> consumerObserveMap = new ConcurrentHashMap<>();
|
||||
|
||||
public Map<Set<String>, List<Consumer<Map<String, T>>>> getConsumerObserveMap() {
|
||||
return consumerObserveMap;
|
||||
}
|
||||
|
||||
protected Map<String, T> resourcesMap = new ConcurrentHashMap<>();
|
||||
|
||||
public AbstractProtocol(AdsObserver adsObserver, Node node, int checkInterval) {
|
||||
this.adsObserver = adsObserver;
|
||||
this.node = node;
|
||||
this.checkInterval = checkInterval;
|
||||
adsObserver.addListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method to obtain Type-URL from sub-class
|
||||
*
|
||||
* @return Type-URL of xDS
|
||||
*/
|
||||
public abstract String getTypeUrl();
|
||||
|
||||
public boolean isCacheExistResource(Set<String> resourceNames) {
|
||||
for (String resourceName : resourceNames) {
|
||||
if ("".equals(resourceName)) {
|
||||
continue;
|
||||
}
|
||||
if (!resourcesMap.containsKey(resourceName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public T getCacheResource(String resourceName) {
|
||||
if (resourceName == null || resourceName.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return resourcesMap.get(resourceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, T> getResource(Set<String> resourceNames) {
|
||||
resourceNames = resourceNames == null ? Collections.emptySet() : resourceNames;
|
||||
|
||||
if (!resourceNames.isEmpty() && isCacheExistResource(resourceNames)) {
|
||||
return getResourceFromCache(resourceNames);
|
||||
} else {
|
||||
return getResourceFromRemote(resourceNames);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, T> getResourceFromCache(Set<String> resourceNames) {
|
||||
return resourceNames.stream()
|
||||
.filter(o -> !StringUtils.isEmpty(o))
|
||||
.collect(Collectors.toMap(k -> k, this::getCacheResource));
|
||||
}
|
||||
|
||||
public Map<String, T> getResourceFromRemote(Set<String> resourceNames) {
|
||||
try {
|
||||
resourceLock.lock();
|
||||
CompletableFuture<Map<String, T>> future = new CompletableFuture<>();
|
||||
observeResourcesName = resourceNames;
|
||||
Set<String> consumerObserveResourceNames = new HashSet<>();
|
||||
if (resourceNames.isEmpty()) {
|
||||
consumerObserveResourceNames.add(emptyResourceName);
|
||||
} else {
|
||||
consumerObserveResourceNames = resourceNames;
|
||||
}
|
||||
|
||||
Consumer<Map<String, T>> futureConsumer = future::complete;
|
||||
try {
|
||||
writeLock.lock();
|
||||
ConcurrentHashMapUtils.computeIfAbsent(
|
||||
(ConcurrentHashMap<Set<String>, List<Consumer<Map<String, T>>>>) consumerObserveMap,
|
||||
consumerObserveResourceNames,
|
||||
key -> new ArrayList<>())
|
||||
.add(futureConsumer);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
|
||||
Set<String> resourceNamesToObserve = new HashSet<>(resourceNames);
|
||||
resourceNamesToObserve.addAll(resourcesMap.keySet());
|
||||
adsObserver.request(buildDiscoveryRequest(resourceNamesToObserve));
|
||||
logger.info("Send xDS Observe request to remote. Resource count: " + resourceNamesToObserve.size()
|
||||
+ ". Resource Type: " + getTypeUrl());
|
||||
|
||||
try {
|
||||
Map<String, T> result = future.get();
|
||||
|
||||
try {
|
||||
writeLock.lock();
|
||||
consumerObserveMap.get(consumerObserveResourceNames).removeIf(o -> o.equals(futureConsumer));
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (InterruptedException e) {
|
||||
logger.error(
|
||||
INTERNAL_INTERRUPTED,
|
||||
"",
|
||||
"",
|
||||
"InterruptedException occur when request control panel. error=",
|
||||
e);
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
logger.error(PROTOCOL_FAILED_REQUEST, "", "", "Error occur when request control panel. error=", e);
|
||||
}
|
||||
} finally {
|
||||
resourceLock.unlock();
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
public void observeResource(Set<String> resourceNames, Consumer<Map<String, T>> consumer, boolean isReConnect) {
|
||||
// call once for full data
|
||||
if (!isReConnect) {
|
||||
consumer.accept(getResource(resourceNames));
|
||||
try {
|
||||
writeLock.lock();
|
||||
consumerObserveMap.compute(resourceNames, (k, v) -> {
|
||||
if (v == null) {
|
||||
v = new ArrayList<>();
|
||||
}
|
||||
// support multi-consumer
|
||||
v.add(consumer);
|
||||
return v;
|
||||
});
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
try {
|
||||
writeLock.lock();
|
||||
this.observeResourcesName =
|
||||
consumerObserveMap.keySet().stream().flatMap(Set::stream).collect(Collectors.toSet());
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void unobserveResource(Set<String> resourceNames, Consumer<Map<String, T>> consumer) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
protected DiscoveryRequest buildDiscoveryRequest(Set<String> resourceNames) {
|
||||
return DiscoveryRequest.newBuilder()
|
||||
.setNode(node)
|
||||
.setTypeUrl(getTypeUrl())
|
||||
.addAllResourceNames(resourceNames)
|
||||
.build();
|
||||
}
|
||||
|
||||
protected abstract Map<String, T> decodeDiscoveryResponse(DiscoveryResponse response);
|
||||
|
||||
@Override
|
||||
public final void process(DiscoveryResponse discoveryResponse) {
|
||||
Map<String, T> newResult = decodeDiscoveryResponse(discoveryResponse);
|
||||
Map<String, T> oldResource = resourcesMap;
|
||||
discoveryResponseListener(oldResource, newResult);
|
||||
resourcesMap = newResult;
|
||||
}
|
||||
|
||||
private void discoveryResponseListener(Map<String, T> oldResult, Map<String, T> newResult) {
|
||||
Set<String> changedResourceNames = new HashSet<>();
|
||||
oldResult.forEach((key, origin) -> {
|
||||
if (!Objects.equals(origin, newResult.get(key))) {
|
||||
changedResourceNames.add(key);
|
||||
}
|
||||
});
|
||||
newResult.forEach((key, origin) -> {
|
||||
if (!Objects.equals(origin, oldResult.get(key))) {
|
||||
changedResourceNames.add(key);
|
||||
}
|
||||
});
|
||||
if (changedResourceNames.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Receive resource update notification from xds server. Change resource count: "
|
||||
+ changedResourceNames.stream() + ". Type: " + getTypeUrl());
|
||||
|
||||
// call once for full data
|
||||
try {
|
||||
readLock.lock();
|
||||
for (Map.Entry<Set<String>, List<Consumer<Map<String, T>>>> entry : consumerObserveMap.entrySet()) {
|
||||
if (entry.getKey().stream().noneMatch(changedResourceNames::contains)) {
|
||||
// none update
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, T> dsResultMap =
|
||||
entry.getKey().stream().collect(Collectors.toMap(k -> k, v -> newResult.get(v)));
|
||||
entry.getValue().forEach(o -> o.accept(dsResultMap));
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol;
|
||||
|
||||
/**
|
||||
* A interface for resources in xDS, which can be updated by ADS delta stream
|
||||
* <br/>
|
||||
* This interface is design to unify the way of fetching data in delta stream
|
||||
* in {@link org.apache.dubbo.registry.xds.util.PilotExchanger}
|
||||
*/
|
||||
public interface DeltaResource<T> {
|
||||
/**
|
||||
* Get resource from delta stream
|
||||
*
|
||||
* @return the newest resource from stream
|
||||
*/
|
||||
T getResource();
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface XdsProtocol<T> {
|
||||
/**
|
||||
* Gets all {@link T resource} by the specified resource name.
|
||||
* For LDS, the {@param resourceNames} is ignored
|
||||
*
|
||||
* @param resourceNames specified resource name
|
||||
* @return resources, null if request failed
|
||||
*/
|
||||
Map<String, T> getResource(Set<String> resourceNames);
|
||||
|
||||
/**
|
||||
* Add a observer resource with {@link Consumer}
|
||||
*
|
||||
* @param resourceNames specified resource name
|
||||
* @param consumer resource notifier, will be called when resource updated
|
||||
* @return requestId, used when resourceNames update with {@link XdsProtocol#updateObserve(long, Set)}
|
||||
*/
|
||||
void observeResource(Set<String> resourceNames, Consumer<Map<String, T>> consumer, boolean isReConnect);
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.delta;
|
||||
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.DeltaResource;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.EndpointResult;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DeltaEndpoint implements DeltaResource<EndpointResult> {
|
||||
private final Map<String, Set<Endpoint>> data = new ConcurrentHashMap<>();
|
||||
|
||||
public void addResource(String resourceName, Set<Endpoint> endpoints) {
|
||||
data.put(resourceName, endpoints);
|
||||
}
|
||||
|
||||
public void removeResource(Collection<String> resourceName) {
|
||||
if (CollectionUtils.isNotEmpty(resourceName)) {
|
||||
resourceName.forEach(data::remove);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndpointResult getResource() {
|
||||
Set<Endpoint> set = data.values().stream().flatMap(Set::stream).collect(Collectors.toSet());
|
||||
return new EndpointResult(set);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.delta;
|
||||
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.DeltaResource;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.ListenerResult;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DeltaListener implements DeltaResource<ListenerResult> {
|
||||
private final Map<String, Set<String>> data = new ConcurrentHashMap<>();
|
||||
|
||||
public void addResource(String resourceName, Set<String> listeners) {
|
||||
data.put(resourceName, listeners);
|
||||
}
|
||||
|
||||
public void removeResource(Collection<String> resourceName) {
|
||||
if (CollectionUtils.isNotEmpty(resourceName)) {
|
||||
resourceName.forEach(data::remove);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerResult getResource() {
|
||||
Set<String> set = data.values().stream().flatMap(Set::stream).collect(Collectors.toSet());
|
||||
return new ListenerResult(set);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.delta;
|
||||
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.DeltaResource;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.RouteResult;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class DeltaRoute implements DeltaResource<RouteResult> {
|
||||
private final Map<String, Map<String, Set<String>>> data = new ConcurrentHashMap<>();
|
||||
|
||||
public void addResource(String resourceName, Map<String, Set<String>> route) {
|
||||
data.put(resourceName, route);
|
||||
}
|
||||
|
||||
public void removeResource(Collection<String> resourceName) {
|
||||
if (CollectionUtils.isNotEmpty(resourceName)) {
|
||||
resourceName.forEach(data::remove);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouteResult getResource() {
|
||||
Map<String, Set<String>> result = new ConcurrentHashMap<>();
|
||||
data.values().forEach(result::putAll);
|
||||
return new RouteResult(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.impl;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.registry.xds.util.AdsObserver;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.delta.DeltaEndpoint;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.EndpointResult;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.core.v3.HealthStatus;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.config.core.v3.SocketAddress;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_RESPONSE_XDS;
|
||||
|
||||
public class EdsProtocol extends AbstractProtocol<EndpointResult, DeltaEndpoint> {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(EdsProtocol.class);
|
||||
|
||||
public EdsProtocol(AdsObserver adsObserver, Node node, int checkInterval) {
|
||||
super(adsObserver, node, checkInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, EndpointResult> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
return response.getResourcesList().stream()
|
||||
.map(EdsProtocol::unpackClusterLoadAssignment)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toConcurrentMap(
|
||||
ClusterLoadAssignment::getClusterName, this::decodeResourceToEndpoint));
|
||||
}
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
private EndpointResult decodeResourceToEndpoint(ClusterLoadAssignment resource) {
|
||||
Set<Endpoint> endpoints = resource.getEndpointsList().stream()
|
||||
.flatMap(e -> e.getLbEndpointsList().stream())
|
||||
.map(e -> decodeLbEndpointToEndpoint(resource.getClusterName(), e))
|
||||
.collect(Collectors.toSet());
|
||||
return new EndpointResult(endpoints);
|
||||
}
|
||||
|
||||
private static Endpoint decodeLbEndpointToEndpoint(String clusterName, LbEndpoint lbEndpoint) {
|
||||
Endpoint endpoint = new Endpoint();
|
||||
SocketAddress address = lbEndpoint.getEndpoint().getAddress().getSocketAddress();
|
||||
endpoint.setAddress(address.getAddress());
|
||||
endpoint.setPortValue(address.getPortValue());
|
||||
boolean healthy = HealthStatus.HEALTHY.equals(lbEndpoint.getHealthStatus())
|
||||
|| HealthStatus.UNKNOWN.equals(lbEndpoint.getHealthStatus());
|
||||
endpoint.setHealthy(healthy);
|
||||
endpoint.setWeight(lbEndpoint.getLoadBalancingWeight().getValue());
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
private static ClusterLoadAssignment unpackClusterLoadAssignment(Any any) {
|
||||
try {
|
||||
return any.unpack(ClusterLoadAssignment.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.error(REGISTRY_ERROR_RESPONSE_XDS, "", "", "Error occur when decode xDS response.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.impl;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.registry.xds.util.AdsObserver;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.delta.DeltaListener;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.ListenerResult;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Filter;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_RESPONSE_XDS;
|
||||
|
||||
public class LdsProtocol extends AbstractProtocol<ListenerResult, DeltaListener> {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(LdsProtocol.class);
|
||||
|
||||
public LdsProtocol(AdsObserver adsObserver, Node node, int checkInterval) {
|
||||
super(adsObserver, node, checkInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return "type.googleapis.com/envoy.config.listener.v3.Listener";
|
||||
}
|
||||
|
||||
public Map<String, ListenerResult> getListeners() {
|
||||
return getResource(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, ListenerResult> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
Set<String> set = response.getResourcesList().stream()
|
||||
.map(LdsProtocol::unpackListener)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(e -> decodeResourceToListener(e).stream())
|
||||
.collect(Collectors.toSet());
|
||||
Map<String, ListenerResult> listenerDecodeResult = new ConcurrentHashMap<>();
|
||||
listenerDecodeResult.put(emptyResourceName, new ListenerResult(set));
|
||||
return listenerDecodeResult;
|
||||
}
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
private Set<String> decodeResourceToListener(Listener resource) {
|
||||
return resource.getFilterChainsList().stream()
|
||||
.flatMap(e -> e.getFiltersList().stream())
|
||||
.map(Filter::getTypedConfig)
|
||||
.map(LdsProtocol::unpackHttpConnectionManager)
|
||||
.filter(Objects::nonNull)
|
||||
.map(HttpConnectionManager::getRds)
|
||||
.map(Rds::getRouteConfigName)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static Listener unpackListener(Any any) {
|
||||
try {
|
||||
return any.unpack(Listener.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.error(REGISTRY_ERROR_RESPONSE_XDS, "", "", "Error occur when decode xDS response.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpConnectionManager unpackHttpConnectionManager(Any any) {
|
||||
try {
|
||||
if (!any.is(HttpConnectionManager.class)) {
|
||||
return null;
|
||||
}
|
||||
return any.unpack(HttpConnectionManager.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.error(REGISTRY_ERROR_RESPONSE_XDS, "", "", "Error occur when decode xDS response.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.impl;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.registry.xds.util.AdsObserver;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.delta.DeltaRoute;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.RouteResult;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.config.route.v3.Route;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteAction;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_RESPONSE_XDS;
|
||||
|
||||
public class RdsProtocol extends AbstractProtocol<RouteResult, DeltaRoute> {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RdsProtocol.class);
|
||||
|
||||
public RdsProtocol(AdsObserver adsObserver, Node node, int checkInterval) {
|
||||
super(adsObserver, node, checkInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, RouteResult> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
return response.getResourcesList().stream()
|
||||
.map(RdsProtocol::unpackRouteConfiguration)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toConcurrentMap(RouteConfiguration::getName, this::decodeResourceToListener));
|
||||
}
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
private RouteResult decodeResourceToListener(RouteConfiguration resource) {
|
||||
Map<String, Set<String>> map = new HashMap<>();
|
||||
Map<String, VirtualHost> rdsVirtualhostMap = new ConcurrentHashMap<>();
|
||||
resource.getVirtualHostsList().forEach(virtualHost -> {
|
||||
Set<String> cluster = virtualHost.getRoutesList().stream()
|
||||
.map(Route::getRoute)
|
||||
.map(RouteAction::getCluster)
|
||||
.collect(Collectors.toSet());
|
||||
for (String domain : virtualHost.getDomainsList()) {
|
||||
map.put(domain, cluster);
|
||||
rdsVirtualhostMap.put(domain, virtualHost);
|
||||
}
|
||||
});
|
||||
return new RouteResult(map, rdsVirtualhostMap);
|
||||
}
|
||||
|
||||
private static RouteConfiguration unpackRouteConfiguration(Any any) {
|
||||
try {
|
||||
return any.unpack(RouteConfiguration.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.error(REGISTRY_ERROR_RESPONSE_XDS, "", "", "Error occur when decode xDS response.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.message;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Endpoint {
|
||||
private String clusterName;
|
||||
private String address;
|
||||
private int portValue;
|
||||
private boolean healthy;
|
||||
private int weight;
|
||||
|
||||
public String getClusterName() {
|
||||
return clusterName;
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public int getPortValue() {
|
||||
return portValue;
|
||||
}
|
||||
|
||||
public void setPortValue(int portValue) {
|
||||
this.portValue = portValue;
|
||||
}
|
||||
|
||||
public boolean isHealthy() {
|
||||
return healthy;
|
||||
}
|
||||
|
||||
public void setHealthy(boolean healthy) {
|
||||
this.healthy = healthy;
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public void setWeight(int weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Endpoint{" + "address='"
|
||||
+ address + '\'' + ", portValue='"
|
||||
+ portValue + '\'' + ", healthy="
|
||||
+ healthy + ", weight="
|
||||
+ weight + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Endpoint endpoint = (Endpoint) o;
|
||||
return healthy == endpoint.healthy
|
||||
&& weight == endpoint.weight
|
||||
&& Objects.equals(address, endpoint.address)
|
||||
&& Objects.equals(portValue, endpoint.portValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(address, portValue, healthy, weight);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.message;
|
||||
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class EndpointResult {
|
||||
private Set<Endpoint> endpoints;
|
||||
|
||||
public EndpointResult() {
|
||||
this.endpoints = new ConcurrentHashSet<>();
|
||||
}
|
||||
|
||||
public EndpointResult(Set<Endpoint> endpoints) {
|
||||
this.endpoints = endpoints;
|
||||
}
|
||||
|
||||
public Set<Endpoint> getEndpoints() {
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EndpointResult that = (EndpointResult) o;
|
||||
return Objects.equals(endpoints, that.endpoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(endpoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EndpointResult{" + "endpoints=" + endpoints + '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.message;
|
||||
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class ListenerResult {
|
||||
private Set<String> routeConfigNames;
|
||||
|
||||
public ListenerResult() {
|
||||
this.routeConfigNames = new ConcurrentHashSet<>();
|
||||
}
|
||||
|
||||
public ListenerResult(Set<String> routeConfigNames) {
|
||||
this.routeConfigNames = routeConfigNames;
|
||||
}
|
||||
|
||||
public Set<String> getRouteConfigNames() {
|
||||
return routeConfigNames;
|
||||
}
|
||||
|
||||
public void setRouteConfigNames(Set<String> routeConfigNames) {
|
||||
this.routeConfigNames = routeConfigNames;
|
||||
}
|
||||
|
||||
public void mergeRouteConfigNames(Set<String> names) {
|
||||
this.routeConfigNames.addAll(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ListenerResult listenerResult = (ListenerResult) o;
|
||||
return Objects.equals(routeConfigNames, listenerResult.routeConfigNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(routeConfigNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ListenerResult{" + "routeConfigNames=" + routeConfigNames + '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.registry.xds.util.protocol.message;
|
||||
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
|
||||
public class RouteResult {
|
||||
private final Map<String, Set<String>> domainMap;
|
||||
private Map<String, VirtualHost> virtualHostMap;
|
||||
|
||||
public RouteResult() {
|
||||
this.domainMap = new ConcurrentHashMap<>();
|
||||
this.virtualHostMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public RouteResult(Map<String, Set<String>> domainMap) {
|
||||
this.domainMap = domainMap;
|
||||
this.virtualHostMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public RouteResult(Map<String, Set<String>> domainMap, Map<String, VirtualHost> virtualHostMap) {
|
||||
this.domainMap = domainMap;
|
||||
this.virtualHostMap = virtualHostMap;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getDomainMap() {
|
||||
return domainMap;
|
||||
}
|
||||
|
||||
public boolean isNotEmpty() {
|
||||
return !domainMap.isEmpty();
|
||||
}
|
||||
|
||||
public Set<String> searchDomain(String domain) {
|
||||
return domainMap.getOrDefault(domain, new ConcurrentHashSet<>());
|
||||
}
|
||||
|
||||
public Set<String> getDomains() {
|
||||
return Collections.unmodifiableSet(domainMap.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
RouteResult that = (RouteResult) o;
|
||||
return Objects.equals(domainMap, that.domainMap) && Objects.equals(virtualHostMap, that.virtualHostMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(domainMap, virtualHostMap);
|
||||
}
|
||||
|
||||
public VirtualHost searchVirtualHost(String domain) {
|
||||
return virtualHostMap.get(domain);
|
||||
}
|
||||
|
||||
public void removeVirtualHost(String domain) {
|
||||
virtualHostMap.remove(domain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RouteResult{" + "domainMap=" + domainMap + ", virtualHostMap=" + virtualHostMap + '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface EdsEndpointListener {
|
||||
|
||||
void onEndPointChange(String cluster, Set<Endpoint> endpoints);
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
|
||||
import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.registry.xds.util.PilotExchanger;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.EndpointResult;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class EdsEndpointManager {
|
||||
|
||||
private static final ConcurrentHashMap<String, Set<EdsEndpointListener>> ENDPOINT_LISTENERS =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
private static final ConcurrentHashMap<String, Set<Endpoint>> ENDPOINT_DATA_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ConcurrentHashMap<String, Consumer<Map<String, EndpointResult>>> EDS_LISTENERS =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
public EdsEndpointManager() {}
|
||||
|
||||
public synchronized void subscribeEds(String cluster, EdsEndpointListener listener) {
|
||||
|
||||
Set<EdsEndpointListener> listeners =
|
||||
ConcurrentHashMapUtils.computeIfAbsent(ENDPOINT_LISTENERS, cluster, key -> new ConcurrentHashSet<>());
|
||||
if (CollectionUtils.isEmpty(listeners)) {
|
||||
doSubscribeEds(cluster);
|
||||
}
|
||||
listeners.add(listener);
|
||||
|
||||
if (ENDPOINT_DATA_CACHE.containsKey(cluster)) {
|
||||
listener.onEndPointChange(cluster, ENDPOINT_DATA_CACHE.get(cluster));
|
||||
}
|
||||
}
|
||||
|
||||
private void doSubscribeEds(String cluster) {
|
||||
ConcurrentHashMapUtils.computeIfAbsent(EDS_LISTENERS, cluster, key -> endpoints -> {
|
||||
Set<Endpoint> result = endpoints.values().stream()
|
||||
.map(EndpointResult::getEndpoints)
|
||||
.flatMap(Set::stream)
|
||||
.collect(Collectors.toSet());
|
||||
notifyEndpointChange(cluster, result);
|
||||
});
|
||||
Consumer<Map<String, EndpointResult>> consumer = EDS_LISTENERS.get(cluster);
|
||||
if (PilotExchanger.isEnabled()) {
|
||||
FrameworkModel.defaultModel()
|
||||
.getBeanFactory()
|
||||
.getBean(FrameworkExecutorRepository.class)
|
||||
.getSharedExecutor()
|
||||
.submit(() -> PilotExchanger.getInstance().observeEds(Collections.singleton(cluster), consumer));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void unSubscribeEds(String cluster, EdsEndpointListener listener) {
|
||||
Set<EdsEndpointListener> listeners = ENDPOINT_LISTENERS.get(cluster);
|
||||
if (CollectionUtils.isEmpty(listeners)) {
|
||||
return;
|
||||
}
|
||||
listeners.remove(listener);
|
||||
if (listeners.isEmpty()) {
|
||||
ENDPOINT_LISTENERS.remove(cluster);
|
||||
doUnsubscribeEds(cluster);
|
||||
}
|
||||
}
|
||||
|
||||
private void doUnsubscribeEds(String cluster) {
|
||||
Consumer<Map<String, EndpointResult>> consumer = EDS_LISTENERS.remove(cluster);
|
||||
|
||||
if (consumer != null && PilotExchanger.isEnabled()) {
|
||||
PilotExchanger.getInstance().unObserveEds(Collections.singleton(cluster), consumer);
|
||||
}
|
||||
ENDPOINT_DATA_CACHE.remove(cluster);
|
||||
}
|
||||
|
||||
public void notifyEndpointChange(String cluster, Set<Endpoint> endpoints) {
|
||||
|
||||
ENDPOINT_DATA_CACHE.put(cluster, endpoints);
|
||||
|
||||
Set<EdsEndpointListener> listeners = ENDPOINT_LISTENERS.get(cluster);
|
||||
if (CollectionUtils.isEmpty(listeners)) {
|
||||
return;
|
||||
}
|
||||
for (EdsEndpointListener listener : listeners) {
|
||||
listener.onEndPointChange(cluster, endpoints);
|
||||
}
|
||||
}
|
||||
|
||||
// for test
|
||||
static ConcurrentHashMap<String, Set<EdsEndpointListener>> getEndpointListeners() {
|
||||
return ENDPOINT_LISTENERS;
|
||||
}
|
||||
|
||||
// for test
|
||||
static ConcurrentHashMap<String, Set<Endpoint>> getEndpointDataCache() {
|
||||
return ENDPOINT_DATA_CACHE;
|
||||
}
|
||||
|
||||
// for test
|
||||
static ConcurrentHashMap<String, Consumer<Map<String, EndpointResult>>> getEdsListeners() {
|
||||
return EDS_LISTENERS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.registry.xds.util.PilotExchanger;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.ListenerResult;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.RouteResult;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.XdsRouteRule;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
|
||||
public class RdsRouteRuleManager {
|
||||
|
||||
private static final ConcurrentHashMap<String, Set<XdsRouteRuleListener>> RULE_LISTENERS =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
private static final ConcurrentHashMap<String, List<XdsRouteRule>> ROUTE_DATA_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ConcurrentMap<String, RdsVirtualHostListener> RDS_LISTENERS = new ConcurrentHashMap<>();
|
||||
|
||||
private static volatile Consumer<Map<String, ListenerResult>> LDS_LISTENER;
|
||||
|
||||
private static volatile Consumer<Map<String, RouteResult>> RDS_LISTENER;
|
||||
|
||||
private static Map<String, RouteResult> RDS_RESULT;
|
||||
|
||||
public RdsRouteRuleManager() {}
|
||||
|
||||
public synchronized void subscribeRds(String domain, XdsRouteRuleListener listener) {
|
||||
|
||||
Set<XdsRouteRuleListener> listeners =
|
||||
ConcurrentHashMapUtils.computeIfAbsent(RULE_LISTENERS, domain, key -> new ConcurrentHashSet<>());
|
||||
if (CollectionUtils.isEmpty(listeners)) {
|
||||
doSubscribeRds(domain);
|
||||
}
|
||||
listeners.add(listener);
|
||||
|
||||
if (ROUTE_DATA_CACHE.containsKey(domain)) {
|
||||
listener.onRuleChange(domain, ROUTE_DATA_CACHE.get(domain));
|
||||
}
|
||||
}
|
||||
|
||||
private void doSubscribeRds(String domain) {
|
||||
synchronized (RdsRouteRuleManager.class) {
|
||||
if (RDS_LISTENER == null) {
|
||||
RDS_LISTENER = rds -> {
|
||||
if (rds == null) {
|
||||
return;
|
||||
}
|
||||
for (RouteResult routeResult : rds.values()) {
|
||||
for (String domainToNotify : RDS_LISTENERS.keySet()) {
|
||||
VirtualHost virtualHost = routeResult.searchVirtualHost(domainToNotify);
|
||||
if (virtualHost != null) {
|
||||
RDS_LISTENERS.get(domainToNotify).parseVirtualHost(virtualHost);
|
||||
}
|
||||
}
|
||||
}
|
||||
RDS_RESULT = rds;
|
||||
};
|
||||
}
|
||||
if (LDS_LISTENER == null) {
|
||||
LDS_LISTENER = new Consumer<Map<String, ListenerResult>>() {
|
||||
private volatile Set<String> configNames = null;
|
||||
|
||||
@Override
|
||||
public void accept(Map<String, ListenerResult> listenerResults) {
|
||||
if (listenerResults.size() == 1) {
|
||||
for (ListenerResult listenerResult : listenerResults.values()) {
|
||||
Set<String> newConfigNames = listenerResult.getRouteConfigNames();
|
||||
if (configNames == null) {
|
||||
PilotExchanger.getInstance().observeRds(newConfigNames, RDS_LISTENER);
|
||||
} else if (!configNames.equals(newConfigNames)) {
|
||||
PilotExchanger.getInstance().unObserveRds(configNames, RDS_LISTENER);
|
||||
PilotExchanger.getInstance().observeRds(newConfigNames, RDS_LISTENER);
|
||||
}
|
||||
configNames = newConfigNames;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (PilotExchanger.isEnabled()) {
|
||||
PilotExchanger.getInstance().observeLds(LDS_LISTENER);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConcurrentHashMapUtils.computeIfAbsent(RDS_LISTENERS, domain, key -> new RdsVirtualHostListener(domain, this));
|
||||
RDS_LISTENER.accept(RDS_RESULT);
|
||||
}
|
||||
|
||||
public synchronized void unSubscribeRds(String domain, XdsRouteRuleListener listener) {
|
||||
Set<XdsRouteRuleListener> listeners = RULE_LISTENERS.get(domain);
|
||||
if (CollectionUtils.isEmpty(listeners)) {
|
||||
return;
|
||||
}
|
||||
listeners.remove(listener);
|
||||
if (listeners.isEmpty()) {
|
||||
RULE_LISTENERS.remove(domain);
|
||||
doUnsubscribeRds(domain);
|
||||
}
|
||||
}
|
||||
|
||||
private void doUnsubscribeRds(String domain) {
|
||||
RDS_LISTENERS.remove(domain);
|
||||
}
|
||||
|
||||
public void notifyRuleChange(String domain, List<XdsRouteRule> xdsRouteRules) {
|
||||
|
||||
ROUTE_DATA_CACHE.put(domain, xdsRouteRules);
|
||||
|
||||
Set<XdsRouteRuleListener> listeners = RULE_LISTENERS.get(domain);
|
||||
if (CollectionUtils.isEmpty(listeners)) {
|
||||
return;
|
||||
}
|
||||
boolean empty = CollectionUtils.isEmpty(xdsRouteRules);
|
||||
for (XdsRouteRuleListener listener : listeners) {
|
||||
if (empty) {
|
||||
listener.clearRule(domain);
|
||||
} else {
|
||||
listener.onRuleChange(domain, xdsRouteRules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for test
|
||||
static ConcurrentHashMap<String, Set<XdsRouteRuleListener>> getRuleListeners() {
|
||||
return RULE_LISTENERS;
|
||||
}
|
||||
|
||||
// for test
|
||||
static ConcurrentHashMap<String, List<XdsRouteRule>> getRouteDataCache() {
|
||||
return ROUTE_DATA_CACHE;
|
||||
}
|
||||
|
||||
// for test
|
||||
static Map<String, RdsVirtualHostListener> getRdsListeners() {
|
||||
return RDS_LISTENERS;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
|
||||
import org.apache.dubbo.common.constants.LoggerCodeConstants;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.ClusterWeight;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.HTTPRouteDestination;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.HeaderMatcher;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.HttpRequestMatch;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.LongRangeMatch;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.PathMatcher;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.XdsRouteRule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.envoyproxy.envoy.config.route.v3.Route;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteAction;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
|
||||
public class RdsVirtualHostListener {
|
||||
|
||||
private static final ErrorTypeAwareLogger LOGGER =
|
||||
LoggerFactory.getErrorTypeAwareLogger(RdsVirtualHostListener.class);
|
||||
|
||||
private final String domain;
|
||||
|
||||
private final RdsRouteRuleManager routeRuleManager;
|
||||
|
||||
public RdsVirtualHostListener(String domain, RdsRouteRuleManager routeRuleManager) {
|
||||
this.domain = domain;
|
||||
this.routeRuleManager = routeRuleManager;
|
||||
}
|
||||
|
||||
public void parseVirtualHost(VirtualHost virtualHost) {
|
||||
if (virtualHost == null || CollectionUtils.isEmpty(virtualHost.getRoutesList())) {
|
||||
// post empty
|
||||
routeRuleManager.notifyRuleChange(domain, new ArrayList<>());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
List<XdsRouteRule> xdsRouteRules = virtualHost.getRoutesList().stream()
|
||||
.map(route -> {
|
||||
if (route.getMatch().getQueryParametersCount() != 0) {
|
||||
return null;
|
||||
}
|
||||
HttpRequestMatch match = parseMatch(route.getMatch());
|
||||
HTTPRouteDestination action = parseAction(route);
|
||||
return new XdsRouteRule(match, action);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
// post rules
|
||||
routeRuleManager.notifyRuleChange(domain, xdsRouteRules);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(
|
||||
LoggerCodeConstants.INTERNAL_ERROR,
|
||||
"",
|
||||
"",
|
||||
"parse domain: " + domain + " xds VirtualHost error",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpRequestMatch parseMatch(RouteMatch match) {
|
||||
PathMatcher pathMatcher = parsePathMatch(match);
|
||||
List<HeaderMatcher> headerMatchers = parseHeadMatch(match);
|
||||
return new HttpRequestMatch(pathMatcher, headerMatchers);
|
||||
}
|
||||
|
||||
private PathMatcher parsePathMatch(RouteMatch match) {
|
||||
boolean caseSensitive = match.getCaseSensitive().getValue();
|
||||
PathMatcher pathMatcher = new PathMatcher();
|
||||
pathMatcher.setCaseSensitive(caseSensitive);
|
||||
switch (match.getPathSpecifierCase()) {
|
||||
case PREFIX:
|
||||
pathMatcher.setPrefix(match.getPrefix());
|
||||
return pathMatcher;
|
||||
case PATH:
|
||||
pathMatcher.setPath(match.getPath());
|
||||
return pathMatcher;
|
||||
case SAFE_REGEX:
|
||||
String regex = match.getSafeRegex().getRegex();
|
||||
pathMatcher.setRegex(regex);
|
||||
return pathMatcher;
|
||||
case PATHSPECIFIER_NOT_SET:
|
||||
return null;
|
||||
default:
|
||||
throw new IllegalArgumentException("Path specifier is not expect");
|
||||
}
|
||||
}
|
||||
|
||||
private List<HeaderMatcher> parseHeadMatch(RouteMatch routeMatch) {
|
||||
List<HeaderMatcher> headerMatchers = new ArrayList<>();
|
||||
List<io.envoyproxy.envoy.config.route.v3.HeaderMatcher> headersList = routeMatch.getHeadersList();
|
||||
for (io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher : headersList) {
|
||||
HeaderMatcher matcher = new HeaderMatcher();
|
||||
matcher.setName(headerMatcher.getName());
|
||||
matcher.setInverted(headerMatcher.getInvertMatch());
|
||||
switch (headerMatcher.getHeaderMatchSpecifierCase()) {
|
||||
case EXACT_MATCH:
|
||||
matcher.setExactValue(headerMatcher.getExactMatch());
|
||||
headerMatchers.add(matcher);
|
||||
break;
|
||||
case SAFE_REGEX_MATCH:
|
||||
matcher.setRegex(headerMatcher.getSafeRegexMatch().getRegex());
|
||||
headerMatchers.add(matcher);
|
||||
break;
|
||||
case RANGE_MATCH:
|
||||
LongRangeMatch rang = new LongRangeMatch();
|
||||
rang.setStart(headerMatcher.getRangeMatch().getStart());
|
||||
rang.setEnd(headerMatcher.getRangeMatch().getEnd());
|
||||
matcher.setRange(rang);
|
||||
headerMatchers.add(matcher);
|
||||
break;
|
||||
case PRESENT_MATCH:
|
||||
matcher.setPresent(headerMatcher.getPresentMatch());
|
||||
headerMatchers.add(matcher);
|
||||
break;
|
||||
case PREFIX_MATCH:
|
||||
matcher.setPrefix(headerMatcher.getPrefixMatch());
|
||||
headerMatchers.add(matcher);
|
||||
break;
|
||||
case SUFFIX_MATCH:
|
||||
matcher.setSuffix(headerMatcher.getSuffixMatch());
|
||||
headerMatchers.add(matcher);
|
||||
break;
|
||||
case HEADERMATCHSPECIFIER_NOT_SET:
|
||||
default:
|
||||
throw new IllegalArgumentException("Header specifier is not expect");
|
||||
}
|
||||
}
|
||||
return headerMatchers;
|
||||
}
|
||||
|
||||
private HTTPRouteDestination parseAction(Route route) {
|
||||
switch (route.getActionCase()) {
|
||||
case ROUTE:
|
||||
HTTPRouteDestination httpRouteDestination = new HTTPRouteDestination();
|
||||
// only support cluster and weight cluster
|
||||
RouteAction routeAction = route.getRoute();
|
||||
RouteAction.ClusterSpecifierCase clusterSpecifierCase = routeAction.getClusterSpecifierCase();
|
||||
if (clusterSpecifierCase == RouteAction.ClusterSpecifierCase.CLUSTER) {
|
||||
httpRouteDestination.setCluster(routeAction.getCluster());
|
||||
return httpRouteDestination;
|
||||
} else if (clusterSpecifierCase == RouteAction.ClusterSpecifierCase.WEIGHTED_CLUSTERS) {
|
||||
List<ClusterWeight> clusterWeights = routeAction.getWeightedClusters().getClustersList().stream()
|
||||
.map(c ->
|
||||
new ClusterWeight(c.getName(), c.getWeight().getValue()))
|
||||
.sorted(Comparator.comparing(ClusterWeight::getWeight))
|
||||
.collect(Collectors.toList());
|
||||
httpRouteDestination.setWeightedClusters(clusterWeights);
|
||||
return httpRouteDestination;
|
||||
}
|
||||
case REDIRECT:
|
||||
case DIRECT_RESPONSE:
|
||||
case FILTER_ACTION:
|
||||
case ACTION_NOT_SET:
|
||||
default:
|
||||
throw new IllegalArgumentException("Cluster specifier is not expect");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.XdsRouteRule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface XdsRouteRuleListener {
|
||||
|
||||
void onRuleChange(String appName, List<XdsRouteRule> xdsRouteRules);
|
||||
|
||||
void clearRule(String appName);
|
||||
}
|
||||
|
|
@ -1,391 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.common.utils.Holder;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.registry.xds.util.PilotExchanger;
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode;
|
||||
import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter;
|
||||
import org.apache.dubbo.rpc.cluster.router.state.BitList;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.ClusterWeight;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.DestinationSubset;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.HTTPRouteDestination;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.HeaderMatcher;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.HttpRequestMatch;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.PathMatcher;
|
||||
import org.apache.dubbo.rpc.cluster.router.xds.rule.XdsRouteRule;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class XdsRouter<T> extends AbstractStateRouter<T> implements XdsRouteRuleListener, EdsEndpointListener {
|
||||
|
||||
private Set<String> subscribeApplications;
|
||||
|
||||
private final ConcurrentHashMap<String, List<XdsRouteRule>> xdsRouteRuleMap;
|
||||
|
||||
private final ConcurrentHashMap<String, DestinationSubset<T>> destinationSubsetMap;
|
||||
|
||||
private final RdsRouteRuleManager rdsRouteRuleManager;
|
||||
|
||||
private final EdsEndpointManager edsEndpointManager;
|
||||
|
||||
private volatile BitList<Invoker<T>> currentInvokeList;
|
||||
|
||||
private static final String BINARY_HEADER_SUFFIX = "-bin";
|
||||
|
||||
private final boolean isEnable;
|
||||
|
||||
public XdsRouter(URL url) {
|
||||
super(url);
|
||||
isEnable = PilotExchanger.isEnabled();
|
||||
rdsRouteRuleManager =
|
||||
url.getOrDefaultApplicationModel().getBeanFactory().getBean(RdsRouteRuleManager.class);
|
||||
edsEndpointManager = url.getOrDefaultApplicationModel().getBeanFactory().getBean(EdsEndpointManager.class);
|
||||
subscribeApplications = new ConcurrentHashSet<>();
|
||||
destinationSubsetMap = new ConcurrentHashMap<>();
|
||||
xdsRouteRuleMap = new ConcurrentHashMap<>();
|
||||
currentInvokeList = new BitList<>(new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated only for uts
|
||||
*/
|
||||
protected XdsRouter(
|
||||
URL url, RdsRouteRuleManager rdsRouteRuleManager, EdsEndpointManager edsEndpointManager, boolean isEnable) {
|
||||
super(url);
|
||||
this.isEnable = isEnable;
|
||||
this.rdsRouteRuleManager = rdsRouteRuleManager;
|
||||
this.edsEndpointManager = edsEndpointManager;
|
||||
subscribeApplications = new ConcurrentHashSet<>();
|
||||
destinationSubsetMap = new ConcurrentHashMap<>();
|
||||
xdsRouteRuleMap = new ConcurrentHashMap<>();
|
||||
currentInvokeList = new BitList<>(new ArrayList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BitList<Invoker<T>> doRoute(
|
||||
BitList<Invoker<T>> invokers,
|
||||
URL url,
|
||||
Invocation invocation,
|
||||
boolean needToPrintMessage,
|
||||
Holder<RouterSnapshotNode<T>> nodeHolder,
|
||||
Holder<String> messageHolder)
|
||||
throws RpcException {
|
||||
if (!isEnable) {
|
||||
if (needToPrintMessage) {
|
||||
messageHolder.set(
|
||||
"Directly Return. Reason: Pilot exchanger has not been initialized, may not in mesh mode.");
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(invokers)) {
|
||||
if (needToPrintMessage) {
|
||||
messageHolder.set("Directly Return. Reason: Invokers from previous router is empty.");
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmptyMap(xdsRouteRuleMap)) {
|
||||
if (needToPrintMessage) {
|
||||
messageHolder.set("Directly Return. Reason: xds route rule is empty.");
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = needToPrintMessage ? new StringBuilder() : null;
|
||||
|
||||
// find match cluster
|
||||
String matchCluster = null;
|
||||
Set<String> appNames = subscribeApplications;
|
||||
for (String subscribeApplication : appNames) {
|
||||
List<XdsRouteRule> rules = xdsRouteRuleMap.get(subscribeApplication);
|
||||
if (CollectionUtils.isEmpty(rules)) {
|
||||
continue;
|
||||
}
|
||||
for (XdsRouteRule rule : rules) {
|
||||
String cluster = computeMatchCluster(invocation, rule);
|
||||
if (cluster != null) {
|
||||
matchCluster = cluster;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchCluster != null) {
|
||||
if (stringBuilder != null) {
|
||||
stringBuilder
|
||||
.append("Match App: ")
|
||||
.append(subscribeApplication)
|
||||
.append(" Cluster: ")
|
||||
.append(matchCluster)
|
||||
.append(' ');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// not match request just return
|
||||
if (matchCluster == null) {
|
||||
if (needToPrintMessage) {
|
||||
messageHolder.set("Directly Return. Reason: xds rule not match.");
|
||||
}
|
||||
return invokers;
|
||||
}
|
||||
DestinationSubset<T> destinationSubset = destinationSubsetMap.get(matchCluster);
|
||||
// cluster no target provider
|
||||
if (destinationSubset == null) {
|
||||
if (needToPrintMessage) {
|
||||
messageHolder.set(stringBuilder.append("no target subset").toString());
|
||||
}
|
||||
return BitList.emptyList();
|
||||
}
|
||||
if (needToPrintMessage) {
|
||||
messageHolder.set(stringBuilder.toString());
|
||||
}
|
||||
if (destinationSubset.getInvokers() == null) {
|
||||
return BitList.emptyList();
|
||||
}
|
||||
|
||||
return destinationSubset.getInvokers().and(invokers);
|
||||
}
|
||||
|
||||
private String computeMatchCluster(Invocation invocation, XdsRouteRule rule) {
|
||||
// compute request match cluster
|
||||
HttpRequestMatch requestMatch = rule.getMatch();
|
||||
if (requestMatch.getPathMatcher() == null && CollectionUtils.isEmpty(requestMatch.getHeaderMatcherList())) {
|
||||
return null;
|
||||
}
|
||||
PathMatcher pathMatcher = requestMatch.getPathMatcher();
|
||||
if (pathMatcher != null) {
|
||||
String path = "/" + invocation.getInvoker().getUrl().getPath() + "/" + RpcUtils.getMethodName(invocation);
|
||||
if (!pathMatcher.isMatch(path)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
List<HeaderMatcher> headerMatchers = requestMatch.getHeaderMatcherList();
|
||||
for (HeaderMatcher headerMatcher : headerMatchers) {
|
||||
String headerName = headerMatcher.getName();
|
||||
// not support byte
|
||||
if (headerName.endsWith(BINARY_HEADER_SUFFIX)) {
|
||||
return null;
|
||||
}
|
||||
String headValue = invocation.getAttachment(headerName);
|
||||
if (!headerMatcher.match(headValue)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
HTTPRouteDestination route = rule.getRoute();
|
||||
if (route.getCluster() != null) {
|
||||
return route.getCluster();
|
||||
}
|
||||
return computeWeightCluster(route.getWeightedClusters());
|
||||
}
|
||||
|
||||
private String computeWeightCluster(List<ClusterWeight> weightedClusters) {
|
||||
int totalWeight = Math.max(
|
||||
weightedClusters.stream().mapToInt(ClusterWeight::getWeight).sum(), 1);
|
||||
// target must greater than 0
|
||||
// if weight is 0, the destination will not receive any traffic.
|
||||
int target = ThreadLocalRandom.current().nextInt(1, totalWeight + 1);
|
||||
for (ClusterWeight weightedCluster : weightedClusters) {
|
||||
int weight = weightedCluster.getWeight();
|
||||
target -= weight;
|
||||
if (target <= 0) {
|
||||
return weightedCluster.getName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void notify(BitList<Invoker<T>> invokers) {
|
||||
BitList<Invoker<T>> invokerList = invokers == null ? BitList.emptyList() : invokers;
|
||||
currentInvokeList = invokerList.clone();
|
||||
|
||||
// compute need subscribe/unsubscribe rds application
|
||||
Set<String> currentApplications = new HashSet<>();
|
||||
for (Invoker<T> invoker : invokerList) {
|
||||
String applicationName = invoker.getUrl().getRemoteApplication();
|
||||
if (StringUtils.isNotEmpty(applicationName)) {
|
||||
currentApplications.add(applicationName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!subscribeApplications.equals(currentApplications)) {
|
||||
synchronized (this) {
|
||||
for (String currentApplication : currentApplications) {
|
||||
if (!subscribeApplications.contains(currentApplication)) {
|
||||
rdsRouteRuleManager.subscribeRds(currentApplication, this);
|
||||
}
|
||||
}
|
||||
for (String preApplication : subscribeApplications) {
|
||||
if (!currentApplications.contains(preApplication)) {
|
||||
rdsRouteRuleManager.unSubscribeRds(preApplication, this);
|
||||
}
|
||||
}
|
||||
subscribeApplications = currentApplications;
|
||||
}
|
||||
}
|
||||
|
||||
// update subset
|
||||
synchronized (this) {
|
||||
BitList<Invoker<T>> allInvokers = currentInvokeList.clone();
|
||||
for (DestinationSubset<T> subset : destinationSubsetMap.values()) {
|
||||
computeSubset(subset, allInvokers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void computeSubset(DestinationSubset<T> subset, BitList<Invoker<T>> invokers) {
|
||||
Set<Endpoint> endpoints = subset.getEndpoints();
|
||||
List<Invoker<T>> filterInvokers = invokers.stream()
|
||||
.filter(inv -> {
|
||||
String host = inv.getUrl().getHost();
|
||||
int port = inv.getUrl().getPort();
|
||||
Optional<Endpoint> any = endpoints.stream()
|
||||
.filter(end -> host.equals(end.getAddress()) && port == end.getPortValue())
|
||||
.findAny();
|
||||
return any.isPresent();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
subset.setInvokers(new BitList<>(filterInvokers));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onRuleChange(String appName, List<XdsRouteRule> xdsRouteRules) {
|
||||
if (CollectionUtils.isEmpty(xdsRouteRules)) {
|
||||
clearRule(appName);
|
||||
return;
|
||||
}
|
||||
Set<String> oldCluster = getAllCluster();
|
||||
xdsRouteRuleMap.put(appName, xdsRouteRules);
|
||||
Set<String> newCluster = getAllCluster();
|
||||
changeClusterSubscribe(oldCluster, newCluster);
|
||||
}
|
||||
|
||||
private Set<String> getAllCluster() {
|
||||
if (CollectionUtils.isEmptyMap(xdsRouteRuleMap)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Set<String> clusters = new HashSet<>();
|
||||
xdsRouteRuleMap.forEach((appName, rules) -> {
|
||||
for (XdsRouteRule rule : rules) {
|
||||
HTTPRouteDestination action = rule.getRoute();
|
||||
if (action.getCluster() != null) {
|
||||
clusters.add(action.getCluster());
|
||||
} else if (CollectionUtils.isNotEmpty(action.getWeightedClusters())) {
|
||||
for (ClusterWeight weightedCluster : action.getWeightedClusters()) {
|
||||
clusters.add(weightedCluster.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return clusters;
|
||||
}
|
||||
|
||||
private void changeClusterSubscribe(Set<String> oldCluster, Set<String> newCluster) {
|
||||
Set<String> removeSubscribe = new HashSet<>(oldCluster);
|
||||
Set<String> addSubscribe = new HashSet<>(newCluster);
|
||||
|
||||
removeSubscribe.removeAll(newCluster);
|
||||
addSubscribe.removeAll(oldCluster);
|
||||
// remove subscribe cluster
|
||||
for (String cluster : removeSubscribe) {
|
||||
edsEndpointManager.unSubscribeEds(cluster, this);
|
||||
destinationSubsetMap.remove(cluster);
|
||||
}
|
||||
// add subscribe cluster
|
||||
for (String cluster : addSubscribe) {
|
||||
destinationSubsetMap.put(cluster, new DestinationSubset<>(cluster));
|
||||
edsEndpointManager.subscribeEds(cluster, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clearRule(String appName) {
|
||||
Set<String> oldCluster = getAllCluster();
|
||||
List<XdsRouteRule> oldRules = xdsRouteRuleMap.remove(appName);
|
||||
if (CollectionUtils.isEmpty(oldRules)) {
|
||||
return;
|
||||
}
|
||||
Set<String> newCluster = getAllCluster();
|
||||
changeClusterSubscribe(oldCluster, newCluster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onEndPointChange(String cluster, Set<Endpoint> endpoints) {
|
||||
// find and update subset
|
||||
DestinationSubset<T> subset = destinationSubsetMap.get(cluster);
|
||||
if (subset == null) {
|
||||
return;
|
||||
}
|
||||
subset.setEndpoints(endpoints);
|
||||
computeSubset(subset, currentInvokeList.clone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
for (String app : subscribeApplications) {
|
||||
rdsRouteRuleManager.unSubscribeRds(app, this);
|
||||
}
|
||||
for (String cluster : getAllCluster()) {
|
||||
edsEndpointManager.unSubscribeEds(cluster, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
Set<String> getSubscribeApplications() {
|
||||
return subscribeApplications;
|
||||
}
|
||||
|
||||
/**
|
||||
* for ut only
|
||||
*/
|
||||
@Deprecated
|
||||
BitList<Invoker<T>> getInvokerList() {
|
||||
return currentInvokeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* for ut only
|
||||
*/
|
||||
@Deprecated
|
||||
ConcurrentHashMap<String, List<XdsRouteRule>> getXdsRouteRuleMap() {
|
||||
return xdsRouteRuleMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* for ut only
|
||||
*/
|
||||
@Deprecated
|
||||
ConcurrentHashMap<String, DestinationSubset<T>> getDestinationSubsetMap() {
|
||||
return destinationSubsetMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
|
||||
import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory;
|
||||
|
||||
@Activate(order = 100)
|
||||
public class XdsRouterFactory implements StateRouterFactory {
|
||||
|
||||
@Override
|
||||
public <T> StateRouter<T> getRouter(Class<T> interfaceClass, URL url) {
|
||||
return new XdsRouter<>(url);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
public class ClusterWeight {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final int weight;
|
||||
|
||||
public ClusterWeight(String name, int weight) {
|
||||
this.name = name;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
import org.apache.dubbo.registry.xds.util.protocol.message.Endpoint;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.cluster.router.state.BitList;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class DestinationSubset<T> {
|
||||
|
||||
public DestinationSubset(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
private final String clusterName;
|
||||
|
||||
private Set<Endpoint> endpoints = new HashSet<>();
|
||||
|
||||
private BitList<Invoker<T>> invokers;
|
||||
|
||||
public String getClusterName() {
|
||||
return clusterName;
|
||||
}
|
||||
|
||||
public Set<Endpoint> getEndpoints() {
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
public void setEndpoints(Set<Endpoint> endpoints) {
|
||||
this.endpoints = endpoints;
|
||||
}
|
||||
|
||||
public BitList<Invoker<T>> getInvokers() {
|
||||
return invokers;
|
||||
}
|
||||
|
||||
public void setInvokers(BitList<Invoker<T>> invokers) {
|
||||
this.invokers = invokers;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HTTPRouteDestination {
|
||||
|
||||
private String cluster;
|
||||
|
||||
private List<ClusterWeight> weightedClusters;
|
||||
|
||||
public String getCluster() {
|
||||
return cluster;
|
||||
}
|
||||
|
||||
public void setCluster(String cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
public List<ClusterWeight> getWeightedClusters() {
|
||||
return weightedClusters;
|
||||
}
|
||||
|
||||
public void setWeightedClusters(List<ClusterWeight> weightedClusters) {
|
||||
this.weightedClusters = weightedClusters;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
public class HeaderMatcher {
|
||||
|
||||
public String name;
|
||||
|
||||
public String exactValue;
|
||||
|
||||
private String regex;
|
||||
|
||||
public LongRangeMatch range;
|
||||
|
||||
public Boolean present;
|
||||
|
||||
public String prefix;
|
||||
|
||||
public String suffix;
|
||||
|
||||
public boolean inverted;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getExactValue() {
|
||||
return exactValue;
|
||||
}
|
||||
|
||||
public void setExactValue(String exactValue) {
|
||||
this.exactValue = exactValue;
|
||||
}
|
||||
|
||||
public String getRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
public void setRegex(String regex) {
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public LongRangeMatch getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
public void setRange(LongRangeMatch range) {
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public Boolean getPresent() {
|
||||
return present;
|
||||
}
|
||||
|
||||
public void setPresent(Boolean present) {
|
||||
this.present = present;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getSuffix() {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public void setSuffix(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public boolean isInverted() {
|
||||
return inverted;
|
||||
}
|
||||
|
||||
public void setInverted(boolean inverted) {
|
||||
this.inverted = inverted;
|
||||
}
|
||||
|
||||
public boolean match(String input) {
|
||||
if (getPresent() != null) {
|
||||
return (input == null) == getPresent().equals(isInverted());
|
||||
}
|
||||
if (input == null) {
|
||||
return false;
|
||||
}
|
||||
if (getExactValue() != null) {
|
||||
return getExactValue().equals(input) != isInverted();
|
||||
} else if (getRegex() != null) {
|
||||
return input.matches(getRegex()) != isInverted();
|
||||
} else if (getRange() != null) {
|
||||
return getRange().isMatch(input) != isInverted();
|
||||
} else if (getPrefix() != null) {
|
||||
return input.startsWith(getPrefix()) != isInverted();
|
||||
} else if (getSuffix() != null) {
|
||||
return input.endsWith(getSuffix()) != isInverted();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HttpRequestMatch {
|
||||
|
||||
private final PathMatcher pathMatcher;
|
||||
|
||||
private final List<HeaderMatcher> headerMatcherList;
|
||||
|
||||
public HttpRequestMatch(PathMatcher pathMatcher, List<HeaderMatcher> headerMatcherList) {
|
||||
this.pathMatcher = pathMatcher;
|
||||
this.headerMatcherList = headerMatcherList;
|
||||
}
|
||||
|
||||
public PathMatcher getPathMatcher() {
|
||||
return pathMatcher;
|
||||
}
|
||||
|
||||
public List<HeaderMatcher> getHeaderMatcherList() {
|
||||
return headerMatcherList;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
public class LongRangeMatch {
|
||||
private long start;
|
||||
private long end;
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(long start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public long getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(long end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public boolean isMatch(String input) {
|
||||
try {
|
||||
long num = Long.parseLong(input);
|
||||
return num >= getStart() && num <= getEnd();
|
||||
} catch (NumberFormatException ignore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
public class PathMatcher {
|
||||
|
||||
private String path;
|
||||
|
||||
private String prefix;
|
||||
|
||||
private String regex;
|
||||
|
||||
private boolean caseSensitive;
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
public void setRegex(String regex) {
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public boolean isCaseSensitive() {
|
||||
return caseSensitive;
|
||||
}
|
||||
|
||||
public void setCaseSensitive(boolean caseSensitive) {
|
||||
this.caseSensitive = caseSensitive;
|
||||
}
|
||||
|
||||
public boolean isMatch(String input) {
|
||||
if (getPath() != null) {
|
||||
return isCaseSensitive() ? getPath().equals(input) : getPath().equalsIgnoreCase(input);
|
||||
} else if (getPrefix() != null) {
|
||||
return isCaseSensitive()
|
||||
? input.startsWith(getPrefix())
|
||||
: input.toLowerCase().startsWith(getPrefix());
|
||||
}
|
||||
return input.matches(getRegex());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.cluster.router.xds.rule;
|
||||
|
||||
public class XdsRouteRule {
|
||||
|
||||
private final HttpRequestMatch match;
|
||||
|
||||
private final HTTPRouteDestination route;
|
||||
|
||||
public XdsRouteRule(HttpRequestMatch match, HTTPRouteDestination route) {
|
||||
this.match = match;
|
||||
this.route = route;
|
||||
}
|
||||
|
||||
public HttpRequestMatch getMatch() {
|
||||
return match;
|
||||
}
|
||||
|
||||
public HTTPRouteDestination getRoute() {
|
||||
return route;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright Istio Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The canonical version of this proto can be found at
|
||||
// https://github.com/istio/api/blob/9abf4c87205f6ad04311fa021ce60803d8b95f78/security/v1alpha1/ca.proto
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
import "google/protobuf/struct.proto";
|
||||
|
||||
// Keep this package for backward compatibility.
|
||||
package istio.v1.auth;
|
||||
|
||||
option go_package = "istio.io/api/security/v1alpha1";
|
||||
option java_generic_services = true;
|
||||
option java_multiple_files = true;
|
||||
|
||||
// Certificate request message. The authentication should be based on:
|
||||
// 1. Bearer tokens carried in the side channel;
|
||||
// 2. Client-side certificate via Mutual TLS handshake.
|
||||
// Note: the service implementation is REQUIRED to verify the authenticated caller is authorize to
|
||||
// all SANs in the CSR. The server side may overwrite any requested certificate field based on its
|
||||
// policies.
|
||||
message IstioCertificateRequest {
|
||||
// PEM-encoded certificate request.
|
||||
// The public key in the CSR is used to generate the certificate,
|
||||
// and other fields in the generated certificate may be overwritten by the CA.
|
||||
string csr = 1;
|
||||
// Optional: requested certificate validity period, in seconds.
|
||||
int64 validity_duration = 3;
|
||||
|
||||
// $hide_from_docs
|
||||
// Optional: Opaque metadata provided by the XDS node to Istio.
|
||||
// Supported metadata: WorkloadName, WorkloadIP, ClusterID
|
||||
google.protobuf.Struct metadata = 4;
|
||||
}
|
||||
|
||||
// Certificate response message.
|
||||
message IstioCertificateResponse {
|
||||
// PEM-encoded certificate chain.
|
||||
// The leaf cert is the first element, and the root cert is the last element.
|
||||
repeated string cert_chain = 1;
|
||||
}
|
||||
|
||||
// Service for managing certificates issued by the CA.
|
||||
service IstioCertificateService {
|
||||
// Using provided CSR, returns a signed certificate.
|
||||
rpc CreateCertificate(IstioCertificateRequest)
|
||||
returns (IstioCertificateResponse) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
xds=org.apache.dubbo.registry.xds.XdsRegistryFactory
|
||||
|
|
@ -1 +0,0 @@
|
|||
xds=org.apache.dubbo.registry.xds.XdsServiceDiscoveryFactory
|
||||
|
|
@ -1 +0,0 @@
|
|||
istio=org.apache.dubbo.registry.xds.istio.IstioCitadelCertificateSigner
|
||||
|
|
@ -1 +0,0 @@
|
|||
xds=org.apache.dubbo.rpc.cluster.router.xds.XdsRouterFactory
|
||||
|
|
@ -1 +0,0 @@
|
|||
xds-route=org.apache.dubbo.rpc.cluster.router.xds.XdsScopeModelInitializer
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue