Skip to content

Commit

Permalink
[SCB-1979] load filter chains
Browse files Browse the repository at this point in the history
  • Loading branch information
wujimin authored and liubao68 committed Jun 3, 2020
1 parent 47f43ff commit 9798ca8
Show file tree
Hide file tree
Showing 7 changed files with 579 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* 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.servicecomb.core.filter;

import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine;
import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.deleteLast;
import static org.apache.servicecomb.swagger.invocation.InvocationType.CONSUMER;
import static org.apache.servicecomb.swagger.invocation.InvocationType.PRODUCER;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;

import org.apache.servicecomb.core.SCBEngine;
import org.apache.servicecomb.core.filter.config.FilterChainsConfig;
import org.apache.servicecomb.core.filter.config.TransportFiltersConfig;
import org.apache.servicecomb.swagger.invocation.InvocationType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class FilterChainsManager {
private TransportFiltersConfig transportFiltersConfig;

private FilterManager filterManager;

private FilterChainsConfig consumerChainsConfig;

private FilterChainsConfig producerChainsConfig;

private boolean enabled;

@Autowired
public FilterChainsManager setTransportFiltersConfig(TransportFiltersConfig transportFiltersConfig) {
this.transportFiltersConfig = transportFiltersConfig;
return this;
}

@Value("${servicecomb.filter-chains.enabled:false}")
public FilterChainsManager setEnabled(boolean enabled) {
this.enabled = enabled;
return this;
}

public FilterManager getFilterManager() {
return filterManager;
}

@Autowired
public FilterChainsManager setFilterManager(FilterManager filterManager) {
this.filterManager = filterManager;
return this;
}

public FilterChainsManager init(SCBEngine engine) {
transportFiltersConfig.load();
filterManager.init(engine);

consumerChainsConfig = new FilterChainsConfig(transportFiltersConfig, CONSUMER);
producerChainsConfig = new FilterChainsConfig(transportFiltersConfig, PRODUCER);

return this;
}

public boolean isEnabled() {
return enabled;
}

public FilterChainsManager addProviders(FilterProvider... providers) {
return addProviders(Arrays.asList(providers));
}

public FilterChainsManager addProviders(Collection<FilterProvider> providers) {
filterManager.addProviders(providers);
return this;
}

public FilterNode createConsumerFilterChain(String microservice) {
return createFilterNode(consumerChainsConfig, microservice);
}

public FilterNode createProducerFilterChain(String microservice) {
return createFilterNode(producerChainsConfig, microservice);
}

public List<Filter> createConsumerFilters(String microservice) {
return createFilters(consumerChainsConfig, microservice);
}

public List<Filter> createProducerFilters(String microservice) {
return createFilters(producerChainsConfig, microservice);
}

public String collectResolvedChains() {
// currently not implement consumer filter chain, so not print consumer information now.

StringBuilder sb = new StringBuilder();
// appendLine(sb, "consumer filters: %s", filterManager.getConsumerFilters());
appendLine(sb, "producer filters: %s", filterManager.getProducerFilters());
// collectChainsByInvocationType(sb, consumerChainsConfig, CONSUMER);
collectChainsByInvocationType(sb, producerChainsConfig, PRODUCER);

return deleteLast(sb, 1).toString();
}

private void collectChainsByInvocationType(StringBuilder sb, FilterChainsConfig chainsConfig,
InvocationType invocationType) {
appendLine(sb, "%s chains:", invocationType.name().toLowerCase(Locale.US));
appendLine(sb, " default: %s", chainsConfig.getDefaultChain());
for (Entry<String, List<Object>> entry : chainsConfig.getMicroserviceChains().entrySet()) {
appendLine(sb, " %s: %s", entry.getKey(), entry.getValue());
}
}

private FilterNode createFilterNode(FilterChainsConfig chainsConfig, String microservice) {
List<Filter> filters = createFilters(chainsConfig, microservice);
return FilterNode.buildChain(filters);
}

private List<Filter> createFilters(FilterChainsConfig chainsConfig, String microservice) {
if (!enabled) {
return Collections.emptyList();
}

List<Object> chain = chainsConfig.findChain(microservice);
return filterManager.createFilters(chain);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* 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.servicecomb.core.filter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.apache.servicecomb.core.SCBEngine;
import org.apache.servicecomb.core.filter.config.TransportFilterConfig;
import org.apache.servicecomb.core.filter.impl.TransportFilters;
import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
import org.apache.servicecomb.swagger.invocation.InvocationType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class FilterManager {
interface Factory {
Filter create();
}

private SCBEngine engine;

private final List<FilterProvider> providers = new ArrayList<>(
SPIServiceUtils.getOrLoadSortedService(FilterProvider.class));

private final Map<String, Factory> factoryMap = new HashMap<>();

private final List<String> consumerFilters = new ArrayList<>();

private final List<String> producerFilters = new ArrayList<>();

@Autowired(required = false)
public void addProviders(Collection<FilterProvider> providers) {
this.providers.addAll(providers);
}

public List<String> getConsumerFilters() {
return consumerFilters;
}

public List<String> getProducerFilters() {
return producerFilters;
}

public void init(SCBEngine engine) {
this.engine = engine;
List<Class<? extends Filter>> filterClasses = providers.stream()
.flatMap(provider -> provider.getFilters().stream())
.collect(Collectors.toList());

for (Class<? extends Filter> filterClass : filterClasses) {
FilterMeta meta = filterClass.getAnnotation(FilterMeta.class);
Factory factory = buildFactory(filterClass, meta);

if (factoryMap.put(meta.name(), factory) != null) {
throw new IllegalStateException(
String.format("duplicated filter, name=%s, class=%s", meta.name(), filterClass.getName()));
}

if (Arrays.binarySearch(meta.invocationType(), InvocationType.CONSUMER) >= 0) {
consumerFilters.add(meta.name());
}
if (Arrays.binarySearch(meta.invocationType(), InvocationType.PRODUCER) >= 0) {
producerFilters.add(meta.name());
}
}
}

public List<Filter> createFilters(List<Object> chain) {
return chain.stream()
.map(filterConfig -> {
Filter filter = createFilter(filterConfig);
filter.init(engine);
return filter;
})
.collect(Collectors.toList());
}

private Filter createFilter(Object filterConfig) {
if (filterConfig instanceof String) {
return createFilterByName((String) filterConfig);
}

if (filterConfig instanceof TransportFilterConfig) {
return createTransportFilter((TransportFilterConfig) filterConfig);
}

throw new IllegalStateException("not support create filter by " + filterConfig);
}

private Filter createTransportFilter(TransportFilterConfig config) {
TransportFilters transportFilters = new TransportFilters();
for (Entry<String, List<Object>> entry : config.getFiltersByTransport().entrySet()) {
List<Filter> filters = createFilters(entry.getValue());
transportFilters.getChainByTransport().put(entry.getKey(), FilterNode.buildChain(filters));
}
return transportFilters;
}

private Filter createFilterByName(String filterName) {
Factory factory = factoryMap.get(filterName);
if (factory != null) {
return factory.create();
}

throw new IllegalStateException("filter not exist, name=" + filterName);
}

private Factory buildFactory(Class<? extends Filter> filterClass, FilterMeta meta) {
if (meta.shareable()) {
Filter filter = createFilter(filterClass);
return () -> filter;
}

return () -> createFilter(filterClass);
}

private Filter createFilter(Class<? extends Filter> filterClass) {
try {
Filter filter = filterClass.newInstance();
filter.init(engine);
return filter;
} catch (Exception e) {
throw new IllegalStateException("failed to create filter.", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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.servicecomb.core.filter.config;

import static org.apache.servicecomb.core.filter.config.TransportFiltersConfig.FILTER_CHAINS_PREFIX;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.configuration.Configuration;
import org.apache.servicecomb.config.ConfigUtil;
import org.apache.servicecomb.swagger.invocation.InvocationType;

import com.netflix.config.DynamicPropertyFactory;

public class FilterChainsConfig {
private final List<Object> defaultChain;

private final Map<String, List<Object>> microserviceChains = new HashMap<>();

private final TransportFiltersConfig transportFiltersConfig;

public FilterChainsConfig(TransportFiltersConfig transportFiltersConfig, InvocationType type) {
this.transportFiltersConfig = transportFiltersConfig;

Configuration config = (Configuration) DynamicPropertyFactory.getBackingConfigurationSource();
String root = FILTER_CHAINS_PREFIX + type.name().toLowerCase(Locale.US);
defaultChain = resolve(ConfigUtil.getStringList(config, root + ".default"));
loadMicroserviceChains(config, root + ".policies");
}

public List<Object> getDefaultChain() {
return defaultChain;
}

public Map<String, List<Object>> getMicroserviceChains() {
return microserviceChains;
}

public List<Object> findChain(String microservice) {
return microserviceChains.getOrDefault(microservice, defaultChain);
}

private void loadMicroserviceChains(Configuration config, String policiesRoot) {
config.getKeys(policiesRoot).forEachRemaining(qualifiedKey -> {
String microserviceName = qualifiedKey.substring(policiesRoot.length() + 1);
List<String> chain = ConfigUtil.getStringList(config, qualifiedKey);

microserviceChains.put(microserviceName, resolve(chain));
});
}

private List<Object> resolve(List<String> rawChain) {
return rawChain.stream()
.map(value -> {
TransportFilterConfig config = transportFiltersConfig.getConfig(value);
return config == null ? value : config;
})
.collect(Collectors.toList());
}
}

0 comments on commit 9798ca8

Please sign in to comment.