GVKun编程网logo

基于 Docker 搭建 Kong Gateway 体验环境(docker安装kong)

30

本文的目的是介绍基于Docker搭建KongGateway体验环境的详细情况,特别关注docker安装kong的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解基于Do

本文的目的是介绍基于 Docker 搭建 Kong Gateway 体验环境的详细情况,特别关注docker安装kong的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解基于 Docker 搭建 Kong Gateway 体验环境的机会,同时也不会遗漏关于006-spring cloud gateway-GatewayAutoConfiguration核心配置-GatewayProperties初始化加载、Route初始化加载、009-spring cloud gateway-过滤器GatewayFilter、GlobalFilter、GatewayFilterChain、作用、生命周期、GatewayFilterFa...、4.1 Docker网络之搭建实验环境、apigateway-kong (七) 配置说明的知识。

本文目录一览:

基于 Docker 搭建 Kong Gateway 体验环境(docker安装kong)

基于 Docker 搭建 Kong Gateway 体验环境(docker安装kong)

在此记录一下 Kong Gateway 体验环境的搭建

1,创建 kong-net 网络

docker network create kong-net

2,  创建 kong 数据库,这里使用 postgresql

docker run -d --name kong-database \
               --network=kong-net \
               -p 5432:5432 \
               -e "POSTGRES_USER=kong" \
               -e "POSTGRES_DB=kong" \
               -e "POSTGRES_PASSWORD=kong" \
               postgres:9.6

3, 初始化数据

docker run --rm \
     --network=kong-net \
     -e "KONG_DATABASE=postgres" \
     -e "KONG_PG_HOST=kong-database" \
     -e "KONG_PG_PASSWORD=kong" \
     kong:latest kong migrations bootstrap

4, 启动 kong 组件 (非局域网注意端口映射,会有安全风险)

docker run -d --name kong \
     --network=kong-net \
     -e "KONG_DATABASE=postgres" \
     -e "KONG_PG_HOST=kong-database" \
     -e "KONG_PG_PASSWORD=kong" \
     -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
     -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
     -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
     -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
     -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
     -p 8000:8000 \
     -p 8443:8443 \
     -p 8001:8001 \
     -p 8444:8444 \
     kong:latest

5, 测试  http://ip:8001  http://ip:8001/status 是否正常。 如果正常则安装成功,否则请跟踪上述的步骤

6,安装 KongaUI  

创建Konga数据库
 docker run -d --name konga-database --network=kong-net -p 5433:5432 -v  konga-postgresql:/var/lib/postgresql/data -e "POSTGRES_USER=konga" -e "POSTGRES_DB=konga" -e "POSTGRES_PASSWORD=konga" postgres:9.6

初始化Konga数据
docker run --rm  --network=kong-net pantsel/konga:latest  -c prepare -a postgres -u postgres://konga:konga@konga-database:5432/konga

启动kongaUI
docker run -d --name konga --network=kong-net -e "DB_ADAPTER=postgres" -e "DB_URI=postgres://konga:konga@konga-database:5432/konga" -e "NODE_ENV=production" -e "DB_PASSWORD=konga" -p 1337:1337  pantsel/konga

启动完成后  http://ip:1337 访问即可,会要求创建管理员帐号密码,要求填入 kong admin url 时,填入第 5 步中的  http://ip:8001 即可。

 

006-spring cloud gateway-GatewayAutoConfiguration核心配置-GatewayProperties初始化加载、Route初始化加载

006-spring cloud gateway-GatewayAutoConfiguration核心配置-GatewayProperties初始化加载、Route初始化加载

一、GatewayProperties

1.1、在GatewayAutoConfiguration中加载

  在Spring-Cloud-Gateway初始化时,同时GatewayAutoConfiguration核心配置类会被初始化加载如下 :

NettyConfiguration 底层通信netty配置
GlobalFilter (AdaptCachedBodyGlobalFilter,RouteToRequestUrlFilter,ForwardRoutingFilter,ForwardPathFilter,WebsocketRoutingFilter,WeightCalculatorWebFilter等)
FilteringWebHandler
GatewayProperties
PrefixPathGatewayFilterFactory
RoutePredicateFactory
RouteDefinitionLocator
RouteLocator
RoutePredicateHandlerMapping 查找匹配到 Route并进行处理
GatewayWebfluxEndpoint 管理网关的 HTTP API

  其中在GatewayAutoConfiguration配置加载中含初始化加载GatewayProperties实例的配置:

查看GatewayAutoConfiguration源码:

    @Bean
    public GatewayProperties gatewayProperties() {
        return new GatewayProperties();
    }

1.2、再次查看GatewayProperties源码:

@ConfigurationProperties("spring.cloud.gateway")
@Validated
public class GatewayProperties {
    @NotNull
    @Valid
    private List<RouteDefinition> routes = new ArrayList();
    private List<FilterDefinition> defaultFilters = new ArrayList();
    private List<MediaType> streamingMediaTypes;

    public GatewayProperties() {
        this.streamingMediaTypes = Arrays.asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
    }

    public List<RouteDefinition> getRoutes() {
        return this.routes;
    }

    public void setRoutes(List<RouteDefinition> routes) {
        this.routes = routes;
    }

    public List<FilterDefinition> getDefaultFilters() {
        return this.defaultFilters;
    }

    public void setDefaultFilters(List<FilterDefinition> defaultFilters) {
        this.defaultFilters = defaultFilters;
    }

    public List<MediaType> getStreamingMediaTypes() {
        return this.streamingMediaTypes;
    }

    public void setStreamingMediaTypes(List<MediaType> streamingMediaTypes) {
        this.streamingMediaTypes = streamingMediaTypes;
    }

    public String toString() {
        return "GatewayProperties{routes=" + this.routes + ", defaultFilters=" + this.defaultFilters + ", streamingMediaTypes=" + this.streamingMediaTypes + ''}'';
    }
}

以上会被默认加载并且读取配置信息,如下配置信息:

  • spring.cloud.gateway.routes:网关路由定义配置,列表形式
  • spring.cloud.gateway.default-filters: 网关默认过滤器定义配置,列表形式
  • spring.cloud.gateway.streamingMediaTypes:网关网络媒体类型,列表形式
其中routes是RouteDefinition集合,defaultFilters是FilterDefinition集合,参看具体的 配置字段。实际配置文件可如下:
spring:
  cloud:
    gateway:
      default-filters:
      - PrefixPath=/httpbin
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
      routes:
      - id: websocket_test
        uri: ws://localhost:9000
        order: 9000
        predicates:
        - Path=/echo
      - id: default_path_to_httpbin
        uri: ${test.uri}
        order: 10000
        predicates:
        - Path=/**

注意:default-filters的配置PrefixPath=/httpbin字符串,可以查看FilterDefinition的构造函数,它其中构造函数包含接收一个text字符串解析字符传并创建实例信息。predicates的配置也是如此。

字符传格式:name=param1,param2,param3

public FilterDefinition(String text) {
        int eqIdx = text.indexOf("=");
        if (eqIdx <= 0) {
            this.setName(text);
        } else {
            this.setName(text.substring(0, eqIdx));
            String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");

            for(int i = 0; i < args.length; ++i) {
                this.args.put(NameUtils.generateName(i), args[i]);
            }

        }
    }

二、Route初始化加载

2.1、GatewayAutoConfiguration加载RouteLocator

  Spring-Cloud-Gateway路由信息是通过路由定位器RouteLocator加载以及初始化。
  在Spring-Cloud-Gateway初始化时,同时GatewayAutoConfiguration核心配置类会被初始化加载如下 :
/**
     * 创建一个根据RouteDefinition转换的路由定位器
     */
    @Bean
    public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
                                                   List<GatewayFilterFactory> GatewayFilters,
                                                   List<RoutePredicateFactory> predicates,
                                                   RouteDefinitionLocator routeDefinitionLocator) {
        return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties);
    }

    /**
     * 创建一个缓存路由的路由定位器
     * @param routeLocators
     * @return
     */
    @Bean
    @Primary//在相同的bean中,优先使用用@Primary注解的bean.
    public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
        
        //1.创建组合路由定位器,根据(容器)已有的路由定位器集合
        //2.创建缓存功能的路由定位器
        return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
    }

路由定位器的创建流程:

1、RouteDefinitionRouteLocator
2、CompositeRouteLocator
3、CachingRouteLocator
其中 RouteDefinitionRouteLocator 是获取路由的主要地方,CompositeRouteLocator,CachingRouteLocator对路由定位器做了附加功能的包装,最终使用的是CachingRouteLocator对外提供服务

2.2、查看RouteLocator源码:

/**
 * 路由定位器,服务获取路由信息
 * 可以通过 RouteDefinitionRouteLocator 获取 RouteDefinition ,并转换成 Route
 */
public interface RouteLocator {

    /**
     * 获取路由
     */
    Flux<Route> getRoutes();
}

查看RouteLocator实现类

缓存功能实现→CachingRouteLocator
组合功能实现→CompositeRouteLocator
通过路由定义转换路由实现→RouteDefinitionRouteLocator

2.3、缓存功能实现→CachingRouteLocator

// 路由定位器的包装类,实现了路由的本地缓存功能
public class CachingRouteLocator implements RouteLocator {
    //目标路由定位器
    private final RouteLocator delegate;

    /**
     * 路由信息
     * Flux 相当于一个 RxJava Observable,
     * 能够发出 0~N 个数据项,然后(可选地)completing 或 erroring。处理多个数据项作为stream
     */
    private final Flux<Route> routes;

    // 本地缓存,用于缓存路由定位器获取的路由集合
    private final Map<String, List> cache = new HashMap<>();
    public CachingRouteLocator(RouteLocator delegate) {
        this.delegate = delegate;
        routes = CacheFlux.lookup(cache, "routes", Route.class)
                .onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));
    }
    @Override
    public Flux<Route> getRoutes() {
        return this.routes;
    }

    // 刷新缓存
    public Flux<Route> refresh() {
        this.cache.clear();
        return this.routes;
    }

    @EventListener(RefreshRoutesEvent.class)
    void handleRefresh() {
        refresh();
    }
}

1、路由信息的本地缓存,通过Map<String, List> cache 缓存路由到内存中;
2、此类通过@EventListener(RefreshRoutesEvent.class)监听RefreshRoutesEvent事件实现了对缓存的动态刷新;

注:路由动态刷新,使用GatewayControllerEndpoint发布刷新事件

@RestControllerEndpoint(id = "gateway")
public class GatewayControllerEndpoint implements ApplicationEventPublisherAware{
    // 调用url= /gateway/refresh 刷新缓存中的路由信息
        @PostMapping("/refresh")
    public Mono<Void> refresh() {
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return Mono.empty();
    }
}

2.4、组合功能实现→CompositeRouteLocator

//组合多个 RRouteLocator 的实现,为Route提供统一获取入口
public class CompositeRouteLocator implements RouteLocator {

    /**
     * 能够发出 0~N 个数据项(RouteLocator),然后(可选地)completing 或 erroring。处理多个数据项作为stream
     */
    private final Flux<RouteLocator> delegates;

    public CompositeRouteLocator(Flux<RouteLocator> delegates) {
        this.delegates = delegates;
    }

    @Override
    public Flux<Route> getRoutes() {
        //this.delegates.flatMap((routeLocator)-> routeLocator.getRoutes());
        return this.delegates.flatMap(RouteLocator::getRoutes);
    }
}

此类将遍历传入的目录路由定位器集合,组合每个路由定位器获取到的路由信息

2.5、通过路由定义转换路由实现→RouteDefinitionRouteLocator

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.gateway.route;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.event.FilterArgsEvent;
import org.springframework.cloud.gateway.event.PredicateArgsEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.handler.AsyncPredicate;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.Route.AsyncBuilder;
import org.springframework.cloud.gateway.support.ConfigurationUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.Validator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;

public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private final RouteDefinitionLocator routeDefinitionLocator;
    private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();
    private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap();
    private final GatewayProperties gatewayProperties;
    private final SpelExpressionParser parser = new SpelExpressionParser();
    private BeanFactory beanFactory;
    private ApplicationEventPublisher publisher;
    @Autowired
    private Validator validator;

    public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<RoutePredicateFactory> predicates, List<GatewayFilterFactory> gatewayFilterFactories, GatewayProperties gatewayProperties) {
        this.routeDefinitionLocator = routeDefinitionLocator;
        this.initFactories(predicates);
        gatewayFilterFactories.forEach((factory) -> {
            GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);
        });
        this.gatewayProperties = gatewayProperties;
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    private void initFactories(List<RoutePredicateFactory> predicates) {
        predicates.forEach((factory) -> {
            String key = factory.name();
            if (this.predicates.containsKey(key)) {
                this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");
            }

            this.predicates.put(key, factory);
            if (this.logger.isInfoEnabled()) {
                this.logger.info("Loaded RoutePredicateFactory [" + key + "]");
            }

        });
    }

    public Flux<Route> getRoutes() {
        return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute).map((route) -> {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("RouteDefinition matched: " + route.getId());
            }

            return route;
        });
    }

    private Route convertToRoute(RouteDefinition routeDefinition) {
        AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
        List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
        return ((AsyncBuilder)Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters)).build();
    }

    private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
        List<GatewayFilter> filters = (List)filterDefinitions.stream().map((definition) -> {
            GatewayFilterFactory factory = (GatewayFilterFactory)this.gatewayFilterFactories.get(definition.getName());
            if (factory == null) {
                throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
            } else {
                Map<String, String> args = definition.getArgs();
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
                }

                Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
                Object configuration = factory.newConfig();
                ConfigurationUtils.bind(configuration, properties, factory.shortcutFieldPrefix(), definition.getName(), this.validator);
                GatewayFilter gatewayFilter = factory.apply(configuration);
                if (this.publisher != null) {
                    this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
                }

                return gatewayFilter;
            }
        }).collect(Collectors.toList());
        ArrayList<GatewayFilter> ordered = new ArrayList(filters.size());

        for(int i = 0; i < filters.size(); ++i) {
            GatewayFilter gatewayFilter = (GatewayFilter)filters.get(i);
            if (gatewayFilter instanceof Ordered) {
                ordered.add(gatewayFilter);
            } else {
                ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
            }
        }

        return ordered;
    }

    private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
        List<GatewayFilter> filters = new ArrayList();
        if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
            filters.addAll(this.loadGatewayFilters("defaultFilters", this.gatewayProperties.getDefaultFilters()));
        }

        if (!routeDefinition.getFilters().isEmpty()) {
            filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
        }

        AnnotationAwareOrderComparator.sort(filters);
        return filters;
    }

    private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
        List<PredicateDefinition> predicates = routeDefinition.getPredicates();
        AsyncPredicate<ServerWebExchange> predicate = this.lookup(routeDefinition, (PredicateDefinition)predicates.get(0));

        AsyncPredicate found;
        for(Iterator var4 = predicates.subList(1, predicates.size()).iterator(); var4.hasNext(); predicate = predicate.and(found)) {
            PredicateDefinition andPredicate = (PredicateDefinition)var4.next();
            found = this.lookup(routeDefinition, andPredicate);
        }

        return predicate;
    }

    private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
        RoutePredicateFactory<Object> factory = (RoutePredicateFactory)this.predicates.get(predicate.getName());
        if (factory == null) {
            throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
        } else {
            Map<String, String> args = predicate.getArgs();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("RouteDefinition " + route.getId() + " applying " + args + " to " + predicate.getName());
            }

            Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
            Object config = factory.newConfig();
            ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(), predicate.getName(), this.validator);
            if (this.publisher != null) {
                this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties));
            }

            return factory.applyAsync(config);
        }
    }
}
View Code

此类的核心方法getRoutes通过传入的routeDefinitionLocator获取路由定位,并循环遍历路由定位依次转换成路由返回,
代码中可以看到getRoutes通过convertToRoute方法将路由定位转换成路由的

2.5.1、RouteDefinition转换:convertToRoute

// RouteDefinition 转换为对应的Route
    private Route convertToRoute(RouteDefinition routeDefinition) {
        //获取routeDefinition中的Predicate信息
        Predicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
        //获取routeDefinition中的GatewayFilter信息
        List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
        //构建路由信息
        return Route.builder(routeDefinition)
                .predicate(predicate)
                .replaceFilters(gatewayFilters)
                .build();
    }

convertToRoute方法功能作用
  获取routeDefinition中的Predicate信息 (通过combinePredicates方法)
  获取routeDefinition中的GatewayFilter信息(通过gatewayFilters方法)
  构建路由信息

1、convertToRoute中combinePredicates获取routeDefinition中的Predicate信息如下:

// 返回组合的谓词
    private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
        //获取RouteDefinition中的PredicateDefinition集合
        List<PredicateDefinition> predicates = routeDefinition.getPredicates();

        Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));

        for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
            Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
             //流程4
            //返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND
            predicate = predicate.and(found);
        }

        return predicate;
    }

    /**
     * 获取一个谓语定义(PredicateDefinition)转换的谓语
     * @param route
     * @param predicate
     * @return
     */
    @SuppressWarnings("unchecked")
    private Predicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
        //流程1
        //流程1==获取谓语创建工厂
        RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
        if (factory == null) {
            throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
        }
        //流程2
        //获取参数
        Map<String, String> args = predicate.getArgs();
        if (logger.isDebugEnabled()) {
            logger.debug("RouteDefinition " + route.getId() + " applying "
                    + args + " to " + predicate.getName());
        }

        //组装参数
        Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
        //构建创建谓语的配置信息
        Object config = factory.newConfig();
        ConfigurationUtils.bind(config, properties,
                factory.shortcutFieldPrefix(), predicate.getName(), validator);
        if (this.publisher != null) {
            this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties));
        }
        //流程3
        //通过谓语工厂构建谓语
        return factory.apply(config);
    }

获取Predicate流程:

  • 根据PredicateDefinition name 获取 RoutePredicateFactory
  • 根据PredicateDefinition args 组装 config信息
  • 通过RoutePredicateFactory 根据config信息创建Predicate信息
  • 多个Predicate 以短路逻辑AND组合
2、convertToRoute中 getFilters获取routeDefinition中的GatewayFilter信息
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
        List<GatewayFilter> filters = new ArrayList<>();

        //校验gatewayProperties是否含义默认的过滤器集合
        if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
            //加载全局配置的默认过滤器集合
            filters.addAll(loadGatewayFilters("defaultFilters",
                    this.gatewayProperties.getDefaultFilters()));
        }

        if (!routeDefinition.getFilters().isEmpty()) {
            //加载路由定义中的过滤器集合
            filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
        }

        //排序
        AnnotationAwareOrderComparator.sort(filters);
        return filters;
    }
     /**
     * 加载过滤器,根据过滤器的定义加载
     * @param id
     * @param filterDefinitions
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
        //遍历过滤器定义,将过滤器定义转换成对应的过滤器
        List<GatewayFilter> filters = filterDefinitions.stream()
                .map(definition -> {
                
                   //流程1    //通过过滤器定义名称获取过滤器创建工厂
                    GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
                    if (factory == null) {
                        throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
                    }
                    //流程2
                    //获取参数
                    Map<String, String> args = definition.getArgs();
                    if (logger.isDebugEnabled()) {
                        logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
                    }

                    //根据args组装配置信息
                    Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
                    //构建过滤器创建配置信息
                    Object configuration = factory.newConfig();
                    ConfigurationUtils.bind(configuration, properties,
                            factory.shortcutFieldPrefix(), definition.getName(), validator);

//流程3
                    //通过过滤器工厂创建GatewayFilter
                    GatewayFilter gatewayFilter = factory.apply(configuration);
                    if (this.publisher != null) {
                        //发布事件
                        this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
                    }
                    return gatewayFilter;
                })
                .collect(Collectors.toList());

        ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
        //包装过滤器使其所有过滤器继承Ordered属性,可进行排序
        for (int i = 0; i < filters.size(); i++) {
            GatewayFilter gatewayFilter = filters.get(i);
            if (gatewayFilter instanceof Ordered) {
                ordered.add(gatewayFilter);
            }
            else {
                ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
            }
        }

        return ordered;
    }
  • getFilters 方法 同时加载 全局配置 gatewayProperties与routeDefinition配置下的所有过滤器定义filterDefinitions
  • loadGatewayFilters 负责将filterDefinition转化成对应的GatewayFilter
    转化流程如下
  1. 根据filterDefinition name 获取 GatewayFilterFactory
  2. 根据filterDefinition args 组装 config信息
  3. 通过GatewayFilterFactory 根据config信息创建PGatewayFilter信息

009-spring cloud gateway-过滤器GatewayFilter、GlobalFilter、GatewayFilterChain、作用、生命周期、GatewayFilterFa...

009-spring cloud gateway-过滤器GatewayFilter、GlobalFilter、GatewayFilterChain、作用、生命周期、GatewayFilterFa...

一、概述

  在Spring-Cloud-Gateway之请求处理流程中最终网关是将请求交给过滤器链表进行处理。

  核心接口:GatewayFilter,GlobalFilter,GatewayFilterChain。

查看整体类图

  

二、网关过滤器作用

  

  当使用微服务构建整个 API 服务时,一般有许多不同的应用在运行,如上图所示的mst-user-servicemst-good-servicemst-order-service,这些服务都需要对客户端的请求的进行 Authentication。最简单粗暴的方法就是像上图一样,为每个微服务应用都实现一套用于校验的过滤器或拦截器。

  通过前置的网关服务来完成这些非业务性质的校验。

  

三、Filter 的生命周期

  Spring Cloud Gateway 的 Filter 的生命周期有两个:“pre” 和 “post”。

  

  “pre”和 “post” 分别会在请求被执行前调用和被执行后调用,和 Zuul Filter 或 Spring Interceptor 中相关生命周期类似,但在形式上有些不一样。

  Zuul 的 Filter 是通过filterType()方法来指定,一个 Filter 只能对应一种类型,要么是 “pre” 要么是“post”。Spring Interceptor 是通过重写HandlerInterceptor中的三个方法来实现的。而 Spring Cloud Gateway 基于 Project Reactor 和 WebFlux,采用响应式编程风格,打开它的 Filter 的接口GatewayFilter你会发现它只有一个方法filter

四、核心接口解读

4.1、GatewayFilterChain--网关过滤链表

/**
 * 网关过滤链表接口
 * 用于过滤器的链式调用
 */
public interface GatewayFilterChain {

    /**
     *  链表启动调用入口方法*/
    Mono<Void> filter(ServerWebExchange exchange);

}

默认实现

  /**
     * 网关过滤的链表,用于过滤器的链式调用
     * 过滤器链表接口的默认实现,
     * 包含2个构建函数:
     *  1.集合参数构建用于初始化吧构建链表
     *  2. index,parent参数用于构建当前执行过滤对应的下次执行的链表 
     */
    private static class DefaultGatewayFilterChain implements GatewayFilterChain {

        /**
         * 当前过滤执行过滤器在集合中索引
         */
        private final int index;
        /**
         * 过滤器集合
         */
        private final List<GatewayFilter> filters;

        public DefaultGatewayFilterChain(List<GatewayFilter> filters) {
            this.filters = filters;
            this.index = 0;
        }

        /**
         * 构建
         * @param parent 上一个执行过滤器对应的FilterChain
         * @param index  当前要执行过滤器的索引
         */
        private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
            this.filters = parent.getFilters();
            this.index = index;
        }

        public List<GatewayFilter> getFilters() {
            return filters;
        }

        /**
         * @param exchange the current server exchange
         * @return
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange) {
            return Mono.defer(() -> {
                if (this.index < filters.size()) {
                    //获取当前索引的过滤器
                    GatewayFilter filter = filters.get(this.index);
                    //构建当前索引的下一个过滤器的FilterChain
                    DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
                    //调用过滤器的filter方法执行过滤器
                    return filter.filter(exchange, chain);
                } else {
                    //当前索引大于等于过滤集合大小,标识所有链表都已执行完毕,返回空
                    return Mono.empty(); // complete
                }
            });
        }
    }

过滤器的GatewayFilterChain 执行顺序

  1. 通过GatewayFilter集合构建顶层的GatewayFilterChain
  2. 调用顶层GatewayFilterChain,获取第一个Filter,并创建下一个Filter索引对应的GatewayFilterChain
  3. 调用filter的filter方法执行当前filter,并将下次要执行的filter对应GatewayFilterChain传入。

4.2、GatewayFilter--网关路由过滤器

/**
 * 网关路由过滤器,
 * Contract for interception-style, chained processing of Web requests that may
 * be used to implement cross-cutting, application-agnostic requirements such
 * as security, timeouts, and others. Specific to a Gateway
 *
 */
public interface GatewayFilter extends ShortcutConfigurable {

    String NAME_KEY = "name";
    String VALUE_KEY = "value";

    /**
     *  过滤器执行方法
     * Process the Web request and (optionally) delegate to the next
     * {@code WebFilter} through the given {@link GatewayFilterChain}.
     * @param exchange the current server exchange
     * @param chain provides a way to delegate to the next filter
     * @return {@code Mono<Void>} to indicate when request processing is complete
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

网关过滤器接口,有且只有一个方法filter,执行当前过滤器,并在此方法中决定过滤器链表是否继续往下执行。

1️⃣、OrderedGatewayFilter--排序

/**
 * 排序的网关路由过滤器,用于包装真实的网关过滤器,已达到过滤器可排序
 */
public class OrderedGatewayFilter implements GatewayFilter, Ordered {

    //目标过滤器
    private final GatewayFilter delegate;
    //排序字段
    private final int order;

    public OrderedGatewayFilter(GatewayFilter delegate, int order) {
        this.delegate = delegate;
        this.order = order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return this.delegate.filter(exchange, chain);
    }
}

OrderedGatewayFilter实现类主要目的是为了将目标过滤器包装成可排序的对象类型。是目标过滤器的包装类

2️⃣、GatewayFilterAdapter

  /**
     * 全局过滤器的包装类,将全局路由包装成统一的网关过滤器
     */
    private static class GatewayFilterAdapter implements GatewayFilter {

        /**
         * 全局过滤器
         */
        private final GlobalFilter delegate;

        public GatewayFilterAdapter(GlobalFilter delegate) {
            this.delegate = delegate;
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return this.delegate.filter(exchange, chain);
        }
    }

GatewayFilterAdapter实现类主要目的是为了将GlobalFilter过滤器包装成GatewayFilter类型的对应。是GlobalFilter过滤器的包装类

4.3、GlobalFilter

  

  GlobalFilter 为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,模式系统初始化时加载,并作用在每个路由上

1️⃣、初始化加载,通过GatewayAutoConfiguration自动创建

//GatewayAutoConfiguration 类
        /**
         * 全局过滤器,用户通过HttpClient转发请求
         */
        @Bean
        public NettyRoutingFilter routingFilter(HttpClient httpClient,
                                                ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
            return new NettyRoutingFilter(httpClient, headersFilters);
        }

        /**
         * 全局的过滤器,用户将HttpClient客户端转发请求的响应写入到原始的请求响应中
         */
        @Bean
        public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) {
            return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
        }
        
//GatewayLoadBalancerClientAutoConfiguration 类
    /**
     * 全局过滤器,用于在通过负载均衡客户端选择服务实例信息
     */
    @Bean
    @ConditionalOnBean(LoadBalancerClient.class)
    public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) {
        return new LoadBalancerClientFilter(client);
    }

2️⃣、GlobalFilter转换成GatewayFilter,并作用于每个路由上,在FilteringWebHandler实现

//FilteringWebHandler类
    /**
     * 包装加载全局的过滤器,将全局过滤器包装成GatewayFilter
     */
    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
        return filters.stream()
                .map(filter -> {
                    //将所有的全局过滤器包装成网关过滤器
                    GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
                    //判断全局过滤器是否实现了可排序接口
                    if (filter instanceof Ordered) {
                        int order = ((Ordered) filter).getOrder();
                        //包装成可排序的网关过滤器
                        return new OrderedGatewayFilter(gatewayFilter, order);
                    }
                    return gatewayFilter;
                }).collect(Collectors.toList());
    }
    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        //获取请求上下文设置的路由实例
        Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
        //获取路由定义下的网关过滤器集合
        List<GatewayFilter> gatewayFilters = route.getFilters();

        //组合全局的过滤器与路由配置的过滤器
        List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
        //添加路由配置过滤器到集合尾部
        combined.addAll(gatewayFilters);
        //对过滤器进行排序
        //TODO: needed or cached?
        AnnotationAwareOrderComparator.sort(combined);

        logger.debug("Sorted gatewayFilterFactories: "+ combined);
        //创建过滤器链表对其进行链式调用
        return new DefaultGatewayFilterChain(combined).filter(exchange);
    }
  • loadFilters方法是将全局路由使用GatewayFilterAdapter包装成GatewayFilter
  • handle方法
    • 获取当前请求使用的路由Route
    • 获取路由配置的过滤器集合route.getFilters()
    • 合并全过滤器与路由配置过滤器combined
    • 对过滤器排序AnnotationAwareOrderComparator.sort
    • 通过过滤器集合构建顶级链表DefaultGatewayFilterChain,并对其当前请求调用链表的filter方法。
小结

Spring-Cloud-Gateway的过滤器接口分为两种:

  1. GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器
  2. GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上

五、GatewayFilterFactory 配置路由过滤器

  路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路径过滤器的范围限定为特定路径。 

1️⃣、加载GatewayFilter

  在路由定位器中以及看到了通过路由定义转换路由方法,其中包含了通过过滤器定义(FilterDefinition)转换过滤器(GatewayFilter)的部分,在RouteDefinitionRouteLocator类中源码如下:

/**
     * 加载过滤器,根据过滤器的定义加载
     */
    @SuppressWarnings("unchecked")
    private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
        //遍历过滤器定义,将过滤器定义转换成对应的过滤器
        List<GatewayFilter> filters = filterDefinitions.stream()
                .map(definition -> {
                    //通过过滤器定义名称获取过滤器创建工厂
                    GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
                    if (factory == null) {
                        throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
                    }
                    //获取参数
                    Map<String, String> args = definition.getArgs();
                    if (logger.isDebugEnabled()) {
                        logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
                    }

                    //根据args组装配置信息
                    Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
                    //构建过滤器创建配置信息
                    Object configuration = factory.newConfig();
                    ConfigurationUtils.bind(configuration, properties,
                            factory.shortcutFieldPrefix(), definition.getName(), validator);

                    //通过过滤器工厂创建GatewayFilter
                    GatewayFilter gatewayFilter = factory.apply(configuration);
                    if (this.publisher != null) {
                        //发布事件
                        this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
                    }
                    return gatewayFilter;
                })
                .collect(Collectors.toList());

        ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
        //包装过滤器使其所有过滤器继承Ordered属性,可进行排序
        for (int i = 0; i < filters.size(); i++) {
            GatewayFilter gatewayFilter = filters.get(i);
            if (gatewayFilter instanceof Ordered) {
                ordered.add(gatewayFilter);
            }
            else {
                ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
            }
        }

        return ordered;
    }

    /**
     * 获取RouteDefinition中的过滤器集合
     */
    private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
        List<GatewayFilter> filters = new ArrayList<>();

        //校验gatewayProperties是否含义默认的过滤器集合
        //TODO: support option to apply defaults after route specific filters?
        if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
            //加载全局配置的默认过滤器集合
            filters.addAll(loadGatewayFilters("defaultFilters",
                    this.gatewayProperties.getDefaultFilters()));
        }

        if (!routeDefinition.getFilters().isEmpty()) {
            //加载路由定义中的过滤器集合
            filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
        }

        //排序
        AnnotationAwareOrderComparator.sort(filters);
        return filters;
    }
  • getFilters方法 合并配置中的全局过滤器与路由自身配置的过滤器,并对其排序(全局配置过滤器信息通过gatewayProperties.getDefaultFilters()获取)
  • loadGatewayFilters 依次遍历路由定义下的FilterDefinition并将其通过对应的GatewayFilterFactory转换为GatewayFilter对象。
2️⃣、GatewayFilterFactory配置过滤器创建工厂创建GatewayFilter对象
  默认内置很多GatewayFilterFactory实现类,用于创建作用不同的网关过滤器。
类图
  

子类及其划分

3️⃣、AddResponseHeaderGatewayFilterFactory 创建解析

/**
 *
 * 响应header添加数据过滤器
 * 用户在response header中添加配置数据
 */
public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            //获取Response并将配置的数据添加到header中
            exchange.getResponse().getHeaders().add(config.getName(), config.getValue());

            return chain.filter(exchange);
        };
    }
}。

配置示例:

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
  • AddResponseHeader=X-Response-Default-Foo, Default-Bar 会被解析成FilterDefinition对象 (name =AddResponseHeader ,args= [X-Response-Default-Foo,Default-Bar])
  • 通FilterDefinition的Name找到AddResponseHeaderGatewayFilterFactory工厂
  • 通过FilterDefinition 的args 创建Config对象(name=X-Response-Default-Foo,value=Default-Bar)
  • 通过 AddResponseHeaderGatewayFilterFactory工厂的apply方法传入config创建GatewayFilter对象。

4️⃣、全部配置

5.1、请求头

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

名称和值,这将为所有匹配请求的下游请求标头添加X-Request-Foo:Bar标头。

移除请求头

        filters:
        - RemoveRequestHeader=X-Request-Foo

5.2、请求参数

        filters:
        - AddRequestParameter=foo, bar

这会将foo = bar添加到下游请求的所有匹配请求的查询字符串中。

5.3、添加响应头

        filters:
        - AddResponseHeader=X-Response-Foo, Bar

这会将X-Response-Foo:Bar标头添加到所有匹配请求的下游响应标头中。

移除响应头

        filters:
        - RemoveResponseHeader=X-Response-Foo

设置响应头

        filters:
        - SetResponseHeader=X-Response-Foo, Bar

此GatewayFilter将替换具有给定名称的所有标头,而不是添加。

5.4、路径前缀

        filters:
        - PrefixPath=/mypath

这将使/ mypath前缀为所有匹配请求的路径。所以对/ hello的请求会被发送到/ mypath / hello。

5.5、原始主机头

没有参数,此过滤器设置路由过滤器将检查的请求属性,以确定是否应发送原始主机头,而不是http客户端确定的主机头。

        filters:
        - PreserveHostHeader

5.6、重定向

        filters:
        - RedirectTo=302, http://acme.org

这将发送带有Location:http://acme.org标头的状态302以执行重定向。

5.7、重写路径

        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/(?<segment>.*), /$\{segment}

对于/ foo / bar的请求路径,这将在发出下游请求之前将路径设置为/ bar。注意由于YAML规范,$ \替换为$。

5.8、保存Session

        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

5.9、路径模板

SetPath GatewayFilter Factory采用路径模板参数。它提供了一种通过允许模板化路径段来操作请求路径的简单方法。

        predicates:
        - Path=/foo/{segment}
        filters:
        - SetPath=/{segment}

对于/ foo / bar的请求路径,这将在发出下游请求之前将路径设置为/ bar。

5.10、设置响应状态

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: http://example.org
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route uri: http://example.org  filters: - SetStatus=401

5.11、请求参数剥离

parts参数指示在将请求发送到下游之前从请求中剥离的路径中的部分数。

        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

当通过网关向/ name / bar / foo发出请求时,对nameservice的请求将类似于http:// nameservice / foo。

5.12、重试

retries:重试:应该尝试的重试次数

statuses:状态:应该重试的HTTP状态代码,使用org.springframework.http.HttpStatus表示

methods:方法:应该重试的HTTP方法,使用org.springframework.http.HttpMethod表示

series:系列:要重试的状态代码系列,使用org.springframework.http.HttpStatus.Series表示

      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry args: retries: 3 statuses: BAD_GATEWAY

5.13、Hystrix GatewayFilter Factory

https://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#_hystrix_gatewayfilter_factory

5.14、请求限速 

  RequestRateLimiter GatewayFilter Factory

5.15、安全头

  SecureHeaders GatewayFilter Factory

  

4.1 Docker网络之搭建实验环境

4.1 Docker网络之搭建实验环境

搭建两台虚拟机,这两台虚拟机可以互相ping通,也能够与宿主机互相ping通。Vagrantfile的配置如下:

# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.require_version ">= 1.6.0"
boxes = [
    {
        :name => "docker-node1",
        :eth1 => "192.168.205.10",
        :mem => "1024",
        :cpu => "1"
    },
    {
        :name => "docker-node2",
        :eth1 => "192.168.205.11",
        :mem => "1024",
        :cpu => "1"
    }
]
Vagrant.configure(2) do |config|
  config.vm.box = "centos/7"
  boxes.each do |opts|
      config.vm.define opts[:name] do |config|
      config.vm.hostname = opts[:name]
      config.vm.provider "virtualbox" do |v|
        v.customize ["modifyvm", :id, "--memory", opts[:mem]]
        v.customize ["modifyvm", :id, "--cpus", opts[:cpu]]
      end
      config.vm.network :private_network, ip: opts[:eth1]
    end
  end
  config.vm.synced_folder "./labs", "/home/vagrant/labs", :nfs => true
  config.vm.provision "shell", privileged: true, path: "./setup.sh"
end

其中,setup.sh的内容如下

#!/usr/bin/env bash
#/bin/sh
# install some tools
sudo yum install -y git vim gcc glibc-static telnet bridge-utils
# install docker
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh
# start docker service
sudo groupadd docker
sudo usermod -aG docker vagrant
sudo systemctl start docker
rm -rf get-docker.sh

apigateway-kong (七) 配置说明

apigateway-kong (七) 配置说明

  这一部分应该在最开始介绍,但是我觉得在对 kong 有一定了解后再回头看下配置,会理解的更深刻。接下来对这个配置文件里的参数做个详细的解释便于更好的使用或优化 kong 网关。

目录

    • 一。配置加载
    • 二。验证配置
    • 三。环境变量
    • 四。自定义 Nginx 配置 & 嵌入式的 Kong 配置
      • 1. 自定义 nginx 配置
      • 2. 在 OpenResty 中嵌入 Kong
      • 3. 为 Kong 提供 WEB 和 API
    • 五。属性配置
      • 1. 通用部分
      • 2.nginx 部分
      • 3. 数据存储部分
      • 4. 数据缓存部分
      • 5.DNS 解析部分
      • 6. 开发 & 杂项

配置加载

  Kong 提供了一个默认的配置文件,如果通过其中一个官方软件包安装了 Kong,那么它可以在 /etc/kong/kong.conf.default 找到。要开始配置 Kong,你可以复制这个文件:

$ cp /etc/kong/kong.conf.default /etc/kong/kong.conf

 如果您的配置中的所有值都被注释掉,Kong 将以默认设置运行。开始时,Kong 查找可能包含配置文件的几个默认位置:

/etc/kong/kong.conf
/etc/kong.conf

可以通过在命令行中使用 - c /--conf 参数指定配置文件的自定义路径来覆盖此行为:

$ kong start --conf /path/to/kong.conf

配置格式非常简单:只需取消注释任何属性(注释由#字符定义)并将其修改为自己需要的值。为方便起见,布尔值可以指定为 on/off 或 true/false

验证配置

  可以使用 check 命令验证设置的完整性:

$ kong check <path/to/kong.conf>
configuration at <path/to/kong.conf> is valid

该命令将考虑您当前设置的环境变量,并且在您的设置无效的情况下会出错。此外,还可以在 debug 模式下使用 CLI 来更深入地了解 Kong 正在启动的属性:

$ kong start -c <kong.conf> --vv
2016/08/11 14:53:36 [verbose] no config file found at /etc/kong.conf
2016/08/11 14:53:36 [verbose] no config file found at /etc/kong/kong.conf
2016/08/11 14:53:36 [debug] admin_listen = "0.0.0.0:8001"
2016/08/11 14:53:36 [debug] database = "postgres"
2016/08/11 14:53:36 [debug] log_level = "notice"
[...]

环境变量

  当从配置文件中加载属性时,Kong 也会查找同名的环境变量,这允许你通过环境变量完全配置 Kong,例如,这对于基于容器的基础架构非常方便。

所有以 KONG_为前缀的环境变量,大写并且与设置同名,将覆盖原配置。

例如: log_level = debug # in kong.conf
可以重写为: $ export KONG_LOG_LEVEL=error

自定义 Nginx 配置 & 嵌入 Kong 配置

  调整 Nginx 配置是设置 Kong 实例的重要组成部分,它允许您优化其基础架构的性能,或者将 Kong 嵌入到已经运行的 OpenResty 实例中。

1. 自定义 Nginx 配置

  Kong 可以使用 --nginx-conf 参数执行启动、重新加载、重新启动的操作,该参数必须指定 Nginx 配置模板。此模板使用 Penlight 引擎,它使用指定的 Kong 配置进行编译,并在启动 Nginx 之前,将其转储到您的 Kong 前缀目录中。

  默认的模板文件为:https://github.com/Mashape/kong/tree/master/kong/templates。它分为两个 Nginx 配置文件:nginx.lua nginx_kong.lua。nginx_kong.lua 包含了 KONG 启动时的所有配置,nginx.lua 则包含了 nginx_kong.lua 在内的所有配置。在启动 Nginx 之前,请将这两个文件复制到 kong 的根目录下,类似与这样:

/usr/local/kong
├── nginx-kong.conf
├── nginx.conf

如果你希望在 Nginx 的配置中包含其他的服务器模块,或者你必须调整 Kong 未公开的全局设置,可参考以下代码:

# ---------------------
# custom_nginx.template
# ---------------------

worker_processes ${{NGINX_WORKER_PROCESSES}}; # can be set by kong.conf
daemon ${{NGINX_DAEMON}};                     # can be set by kong.conf

pid pids/nginx.pid;                      # this setting is mandatory
error_log logs/error.log ${{LOG_LEVEL}}; # can be set by kong.conf

events {
    use epoll; # custom setting
    multi_accept on;
}

http {
    # include default Kong Nginx config
    include ''nginx-kong.conf'';

    # custom server
    server {
        listen 8888;
        server_name custom_server;

        location / {
          ... # etc
        }
    }
}

然后可以使用下面的命令来启动 Kong:

$ kong start -c kong.conf --nginx-conf custom_nginx.template

如果你希望自定义 Kong Nginx 子配置文件,最终添加其他 Lua 处理程序或自定义 lua_ * 指令,则可以在此 custom_nginx.template 示例文件中内嵌 nginx_kong.lua 配置:

# ---------------------
# custom_nginx.template
# ---------------------

worker_processes ${{NGINX_WORKER_PROCESSES}}; # can be set by kong.conf
daemon ${{NGINX_DAEMON}};                     # can be set by kong.conf

pid pids/nginx.pid;                      # this setting is mandatory
error_log logs/error.log ${{LOG_LEVEL}}; # can be set by kong.conf

events {}

http {
  resolver ${{DNS_RESOLVER}} ipv6=off;
  charset UTF-8;
  error_log logs/error.log ${{LOG_LEVEL}};
  access_log logs/access.log;

  ... # etc
}

2. 在 OpenResty 中嵌入 Kong

  如果你运行自己的 OpenResty 服务器,还可以通过使用 include 指令(类似上一节例子)添加 Kong Nginx 的子配置文件来轻松嵌入 Kong。如果你有一个有效的、只包含 Kong 特定配置的、top-level 的 NGINX 配置:

# my_nginx.conf

http {
    include ''nginx-kong.conf'';
}

你可以如下启动你的实例:

$ nginx -p /usr/local/openresty -c my_nginx.conf

Kong 将在该实例中运行(如在 nginx-kong.conf 中配置一样)。

3. 为 Kong 提供 WEB 和 API

  API 提供者的一个常见用例是让 Kong 通过代理端口 - 服务器端口 80 或 443 服务于网站和 API。例如,https://my-api.com(网站)和 https://my-api.com/api/v1(API)。

为了实现这一点,我们不能简单地声明一个新的虚拟服务器模块,就像我们在前一节中所做的那样。一个好的解决方案是使用一个自定义的 Nginx 配置模板,该模板内联 nginx_kong.lua,并添加一个新的位置块,用于服务该网站以及 Kong 代理位置块:

# ---------------------
# custom_nginx.template
# ---------------------

worker_processes ${{NGINX_WORKER_PROCESSES}}; # can be set by kong.conf
daemon ${{NGINX_DAEMON}};                     # can be set by kong.conf

pid pids/nginx.pid;                      # this setting is mandatory
error_log logs/error.log ${{LOG_LEVEL}}; # can be set by kong.conf
events {}

http {
  # here, we inline the contents of nginx_kong.lua
  charset UTF-8;

  # any contents until Kong''s Proxy server block
  ...

  # Kong''s Proxy server block
  server {
    server_name kong;

    # any contents until the location / block
    ...

    # here, we declare our custom location serving our website
    # (or API portal) which we can optimize for serving static assets
    location / {
      root /var/www/my-api.com;
      index index.htm index.html;
      ...
    }

    # Kong''s Proxy location / has been changed to /api/v1
    location /api/v1 {
      set $upstream_host nil;
      set $upstream_scheme nil;
      set $upstream_uri nil;

      # Any remaining configuration for the Proxy location
      ...
    }
  }

  # Kong''s Admin server block goes below
}
View Code

属性配置

1. 通用部分

prefix (默认 /usr/local/kong)

工作目录。相当于 Nginx 的前缀路径,包含临时文件和日志。每个 Kong 流程都必须有一个单独的工作目录。

log_level (默认 notice)

Nginx 服务器的日志级别。日志可以在 <前缀> /logs/error.log 找到
有关可接受值的列表,请参阅 http://nginx.org/en/docs/ngx_core_module.html#error_log。

proxy_access_log (默认 logs/access.log)

代理端口请求访问日志的路径。将此值设置为 off 以禁用日志记录代理请求。如果这个值是一个相对路径,它将被放置在 prefix 位置下。

proxy_error_log (默认 logs/error.log)

代理端口请求错误日志的路径。这些日志的粒度由 log_level 指令进行调整

admin_access_log (默认 logs/admin_access.log)

管理 API 请求访问日志的路径。将此值设置为 off 以禁用记录管理 API 请求。如果这个值是一个相对路径,它将被放置在 prefix 位置下。

admin_error_log (默认 logs/error.log)

管理 API 请求错误日志的路径。这些日志的粒度由 log_level 指令进行调整。

custom_plugins (默认 None,举例:my-plugin,hello-world,custom-rate-limiting)

应加载此节点的逗号分隔的其他插件列表。使用此属性加载未与 Kong 捆绑的自定义插件。插件将从 kong.plugins.{name}.* 命名空间中加载

anonymous_reports (默认 on)

发送匿名使用数据(如错误堆栈跟踪)以帮助改进 Kong。

2.nginx 部分

proxy_listen (默认 0.0.0.0:8000, 0.0.0.0:8443 ssl  示例:0.0.0.0:80, 0.0.0.0:81 http2, 0.0.0.0:443 ssl, 0.0.0.0:444 http2 ssl)

  代理服务器应该侦听的地​​址和端口的逗号分隔列表。代理服务器是 Kong 的公共入口点,它将消费者的流量代理到后端服务。该值接受 IPv4,IPv6 和主机名。

可以为每一对指定一些后缀:

  • ssl 将要求通过特定 address/port 进行的所有连接都是在启用 TLS 的情况下进行的。
  • http2 将允许客户端打开到 Kong 代理服务器的 HTTP/2 连接。
  • 最后,proxy_protocol 将启用对给定 address/port 使用 PROXY 协议。

  该节点的代理端口,启用可以配置连接到同一数据库的节点集群的 “control-plane” 模式(无流量代理功能)。
请参阅 http://nginx.org/en/docs/http/ngx_http_core_module.html#listen 以获取有关此和其他 * _listen 值的可接受格式的说明。

admin_listen (默认 127.0.0.1:8001, 127.0.0.1:8444 ssl  示例  127.0.0.1:8444 http2 ssl)

管理界面应该监听的地址和端口的逗号分隔列表。 Admin 界面是允许您配置和管理 Kong 的 API。只有 Kong 管理员才能访问此界面。该值接受 IPv4,IPv6 和主机名。可以为每一对指定一些后缀:

  • ssl 将要求通过特定地址 / 端口进行的所有连接都是在启用 TLS 的情况下进行的。
  • http2 将允许客户端打开到 Kong 代理服务器的 HTTP / 2 连接。
  • 最后,proxy_protocol 将启用对给定地址 / 端口使用 PROXY 协议。

可以将此值设置为 off,从而禁用此节点的 Admin 界面,启用 “data-plane” 模式(无配置功能)从数据库中提取其配置更改。

nginx_user (默认 nobody  nobody  示例 nginx www)

定义工作进程使用的用户和组凭据。如果省略组,则使用名称等于用户名的组。

nginx_worker_processes (默认 auto)

确定 Nginx 产生的工作进程的数量。请参阅 http://nginx.org/en/docs/ngx_core_module.html#worker_processes 了解该指令的详细用法以及可接受值的说明。

nginx_daemon (默认 on)

确定 Nginx 是作为后台进程运行还是作为前台进程运行。主要用于开发或在 Docker 环境中运行 Kong 时使用。请参阅 http://nginx.org/en/docs/ngx_core_module.html#daemon。

mem_cache_size (默认 128M)

数据库实体的内存缓存大小。接受的单位是 k 和 m,最小推荐值为几 MB。

ssl_cipher_suite (默认 modern)

定义 Nginx 提供的 TLS 密码。接受的值是 modern, intermediate, old 或 custom。有关每个密码套件的详细说明,请参阅 https://wiki.mozilla.org/Security/Server_Side_TLS。

ssl_ciphers (默认 None)

定义由 Nginx 提供的 TLS 密码的自定义列表。该列表必须符合由 openssl ciphers 定义的模式。如果 ssl_cipher_suite 不是 custom 的,则该值将被忽略。

ssl_cert (默认 None)

启用了 SSL 的 proxy_listen 值的 SSL 证书 (certificate) 的绝对路径。

ssl_cert_key (默认 None)

启用了 SSL 的 proxy_listen 值的 SSL 密钥 (key) 的绝对路径。

client_ssl (默认 off)

确定 Nginx 在代理请求时是否应发送客户端 SSL 证书。

client_ssl_cert (默认 None)

如果启用 client_ssl,则为 proxy_ssl_certificate 指令的客户端 SSL 证书的绝对路径。请注意,此值静态定义在节点上,并且当前不能基于每个 API 进行配置。

client_ssl_cert_key (默认 None)

如果启用 client_ssl,则为 proxy_ssl_certificate_key 地址的客户端 SSL 密钥的绝对路径。请注意,此值在节点上静态定义,并且当前无法在每个 API 基础上配置。

admin_ssl_cert (默认  None)

启用了 SSL 的 admin_listen 值的 SSL 证书的绝对路径。

admin_ssl_cert_key (默认 None)

启用了 SSL 的 admin_listen 值的 SSL 密钥的绝对路径。

upstream_keepalive (默认 60)

设置保留在每个辅助进程缓存中的上游服务器的最大空闲保活连接数。当这个数字被超过时,最近最少使用的连接被关闭。

server_tokens (默认 on)

启用或禁用在错误页面和 Server 或 Via(如果请求被代理)响应头 (response header) 字段中发出 Kong 版本。

latency_tokens (默认 on)

启用或禁用在 X-Kong-Proxy-Latency 和 X-Kong-Upstream-Latency 响应头域中发布 Kong 延迟信息。

trusted_ips (默认 None)

定义已知可发送正确的 X-Forwarded-* 标头的可信 IP 地址块。来自可信 IP 的请求使得 Kong 向上游转发他们的 X-Forwarded- * 报头。不可信的请求使得 Kong 插入它自己的 X-Forwarded- * 标头。

该属性还可以在 Nginx 配置中设置 set_real_ip_from 指令。它接受相同类型的值(CIDR 块),但是作为逗号分隔的列表。

要信任所有 /\IP,请将此值设置为 0.0.0.0/0,::/0。
如果指定了特殊值 unix:,则所有 UNIX 域套接字都将被信任。

有关 set_real_ip_from 指令的更多详细信息,请参阅 Nginx 文档。

real_ip_header (默认 X-Real-IP)

定义其值将用于替换客户端地址的请求标头字段。该值在 Nginx 配置中设置相同名称的 ngx_http_realip_module 指令。
如果此值接收 proxy_protocol,则 proxy_protocol 参数将被附加到 Nginx 模板的 listen 指令。

有关此指令的说明,请参阅 Nginx 文档。

real_ip_recursive (默认 off)

该值在 Nginx 配置中设置相同名称的 ngx_http_realip_module 指令。

有关此指令的说明,请参阅 Nginx 文档。

client_max_body_size (默认 0)

定义由 Kong 代理的请求允许的最大请求主体大小,在 Content-Length 请求头中指定。如果请求超过这个限制,Kong 将回复 413(请求实体太大)。将此值设置为 0 将禁用检查请求主体大小。

注意:请参阅 Nginx 文档以获取有关此参数的进一步说明。数字值可以用 k 或 m 作为后缀,以千字节或兆字节为单位表示限制。

client_body_buffer_size (默认 8k)

定义读取请求主体的缓冲区大小。如果客户端请求主体大于此值,则主体将被缓冲到磁盘。请注意,当主体缓存到磁盘时访问或操作请求主体的 Kong 插件可能无法工作,因此建议将此值设置为尽可能高(例如,将其设置为与 client_max_body_size 一样高以强制请求主体保留在内存中)。请注意,高并发环境将需要大量的内存分配来处理许多并发的大型请求体。

注意:请参阅 Nginx 文档以获取有关此参数的进一步说明。数字值可以用 k 或 m 作为后缀,以千字节或兆字节为单位表示限制。

error_default_type (默认 text/plain)

在请求 Accept 头部缺失并且 Nginx 正在为请求返回错误时使用的默认的 MIME 类型。接受的值是 text /plain,text /html,application /json 和 application /xml。

3. 数据存储部分

Kong 将把所有的数据(如 API,消费者和插件)存储在 Cassandra 或 PostgreSQL 中。
属于同一集群的所有 Kong 节点必须将自己连接到同一个数据库。

Kong v0.12.0开始:

PostgreSQL 9.4支持应被视为弃用。建议升级到9.5+
Cassandra 2.1支持应该被视为弃用。建议升级到2.2+

确定 Kong 节点将使用哪个 PostgreSQL 或 Cassandra 作为其数据存储。可接受的值是 postgres 和 cassandra。

默认:postgres

Postgres 设置

名称 描述
pg_host Postgres 的服务器的主机地址
pg_port Postgres 的服务器的端口
pg_user Postgres 用户名
pg_password Postgres 的用户密码
pg_database 要连接的数据库实例名,必须存在
pg_ssl 是否启用与服务器的 SSL 连接
pg_ssl_verify 如果启用了 pg_ssl,则切换服务器证书验证。请参阅 lua_ssl_trusted_certificate 设置

 

 

 

 

 

 

 

 

Cassandra 设置

名称 描述
cassandra_contact_points 集群名称列表,以逗号分隔
cassandra_port 节点正在监听的端口
cassandra_keyspace 在群集中使用的密钥空间,如果不存在将被自动创建
cassandra_consistency 设置读写操作的一致性
cassandra_timeout 读写操作的超时设定,单位为毫秒 ms
cassandra_ssl 配置启用 SSL 连接
cassandra_ssl_verify 如果启用 cassandra_ssl,则切换服务器证书验证。请参阅 lua_ssl_trusted_certificate 设置
cassandra_username 使用 PasswordAuthenticator 方案时的用户名
cassandra_password 使用 PasswordAuthenticator 方案时的用户密码
cassandra_consistency 读 / 写 Cassandra 群集时使用的一致性设置
cassandra_lb_policy 在 Cassandra 群集中分发查询时使用的负载均衡策略。接受的值是 RoundRobin 和 DCAwareRoundRobin。当且仅当您使用多数据中心集群时方可配置,此时,请同时配置 cassandra_local_datacenter 选项
cassandra_local_datacenter 当使用 DCAwareRoundRobin 策略时,必须在 KONG 节点中指定本地集群的名称
cassandra_repl_strategy 如果是首次创建密钥空间,请指定复制策略
cassandra_repl_factor 指定 SimpleStrategy 的复制条件
cassandra_data_centers 指定 NetworkTopologyStrategy 的数据中心
cassandra_schema_consensus_timeout 定义 Cassandra 节点之间每个模式共识等待期的超时(以毫秒为单位)。该值仅在迁移过程中使用。

 

4. 数据缓存部分

为了避免与数据存储区进行不必要的通信,Kong 将实体(例如 API,消费者,证书等)缓存一段可配置的时间。如果实体更新,它也处理失效。

本节允许配置 Kong 关于缓存这些配置实体的行为。

db_update_frequency (默认 5s)

用数据存储检查更新实体的频率(以秒为单位)。当节点通过管理 API 创建,更新或删除实体时,其他节点需要等待下一轮(由该值配置)最终清除旧缓存实体并开始使用新实例。

db_update_propagation (默认 0s)

用于将数据存储中的实体传播到其他数据中心的副本节点 (replica nodes) 的时间(以秒为单位)。在分布式环境(如多数据中心 Cassandra 群集)中,此值应为 Cassandra 将行传播 (propagate) 到其他数据中心所花费的最大秒数。设置后,该属性将增加 Kong 传播实体更改所花费的时间。单数据中心设置或 PostgreSQL 服务器应该不会出现这种延迟,并且该值可以安全地设置为 0。

db_cache_ttl (默认 3600s, 即 1h)

当由此节点缓存时,实体从数据存储区生存的时间(以秒为单位)。根据此设置,数据库未命中(无实体)也被缓存。如果设置为 0,则此类缓存的实体 / 未命中将永不过期。

5.DNS 解析部分

  Kong 会将主机名解析为 SRV 或 A 记录(按此顺序,CNAME 记录将在过程中解除引用)。如果一个名称被解析为 SRV 记录,它也将通过从 DNS 服务器接收的 port 字段内容覆盖任何给定的端口号。

DNS 选项 SEARCH 和 NDOTS(来自 /etc/resolv.conf 文件)将用于将短名称扩展为完全合格的名称。所以它会首先尝试整个 SEARCH 列表中的 SRV 类型,如果失败,它会尝试 SEARCH A 记录列表等。

在 ttl 的持续时间内,内部 DNS 解析器将负载均衡其通过 DNS 记录中的条目获得的每个请求。对于 SRV 记录,权重 (weight) 字段将被遵守,但它只会使用记录中最低优先级 (lowest priority) 的字段条目。

dns_resolver (默认 None)

由逗号分隔的服务器名称列表,每个条目的格式为 ipv4 [:port]。如果未指定,将使用本地 resolv.conf 文件中的服务器名称。如果省略,则默认端口号为 53。

dns_hostsfile (默认 /etc/hosts)

hosts 文件。该文件在启动时被读取一次,其在内存中是静态的。如果要修改此文件,必须重新加载服务。

dns_order (默认 LAST,SRV,A,CNAME)

解析不同记录类型的顺序。 LAST 类型表示上次成功查找的类型(用于指定的名称)。格式是一个(不区分大小写)逗号分隔的列表。

dns_stale_ttl (默认 4s)

以秒为单位定义记录在超过 TTL 时将保留在缓存中的时间。这个值将在后台获取新的 DNS 记录时使用。陈旧的数据将在记录到期时使用,直到刷新查询完成或经过了 dns_stale_ttl 秒数。

dns_not_found_ttl (默认 30s)

TTL 在几秒钟内为空 DNS 响应和 “(3)name error” 响应。

dns_error_ttl (默认 1s)

错误响应的 TTL。

dns_no_sync (默认 off)

如果启用,则在缓存未命中时,每个请求都会触发其自己的 dns 查询。禁用时,对同一 name/type 的多个请求将同步到单个查询。

6. 开发 & 杂项

从 lua-nginx 模块继承的其他设置允许更多的灵活性和高级用法。

有关更多信息,请参阅 lua-nginx-module 文档:https://github.com/openresty/lua-nginx-module。

lua_ssl_trusted_certificate (默认 None)

PEM 格式的 Lua cosockets 的证书颁发机构文件的绝对路径。当启用 pg_ssl_verify 或 cassandra_ssl_verify 时,此证书将用于验证 Kong 的数据库连接。参见文档
lua_ssl_verify_depth (默认 1)

设置由 lua_ssl_trusted_certificate 设置的 Lua 套接字使用的服务器证书链中的验证深度。
这包括为 Kong 的数据库连接配置的证书。 参见文档

lua_package_path (默认 None)

设置 Lua 模块搜索路径(LUA_PATH)。在开发或使用未存储在默认搜索路径中的自定义插件时非常有用。参见 文档

lua_package_cpath (默认 None)

设置 Lua C 模块搜索路径(LUA_CPATH)参见文档

lua_socket_pool_size (默认 30)

指定与每个远程服务器关联的每个 Cosocket 连接池的大小限制。参见文档

 

累死宝宝了,终于结束了~

 

今天关于基于 Docker 搭建 Kong Gateway 体验环境docker安装kong的介绍到此结束,谢谢您的阅读,有关006-spring cloud gateway-GatewayAutoConfiguration核心配置-GatewayProperties初始化加载、Route初始化加载、009-spring cloud gateway-过滤器GatewayFilter、GlobalFilter、GatewayFilterChain、作用、生命周期、GatewayFilterFa...、4.1 Docker网络之搭建实验环境、apigateway-kong (七) 配置说明等更多相关知识的信息可以在本站进行查询。

本文标签: