一、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);
}
}
}

此类的核心方法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信息

006-spring cloud gateway-GatewayAutoConfiguration核心配置-GatewayProperties初始化加载、Route初始化加载的更多相关文章

  1. spring cloud gateway的stripPrefix配置

    序 本文主要研究下spring cloud gateway的stripPrefix配置 使用zuul的配置 zuul: routes: demo: sensitiveHeaders: Access-C ...

  2. Spring Cloud Gateway实战之二:更多路由配置方式

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. Spring Cloud Gateway(三):网关处理器

    1.Spring Cloud Gateway 源码解析概述 API网关作为后端服务的统一入口,可提供请求路由.协议转换.安全认证.服务鉴权.流量控制.日志监控等服务.那么当请求到达网关时,网关都做了哪 ...

  4. spring Cloud网关之Spring Cloud Gateway

    Spring Cloud Gateway是什么?(官网地址:https://cloud.spring.io/spring-cloud-gateway/reference/html/) Spring C ...

  5. Spring Cloud Gateway应用篇(十三)

    一.概述 在微服务架构中,每个服务都是一个可以独立开发和运行的组件,而一个完整的微服务架构由一系列独立运行的微服务组成.其中每个服务都只会完成特定领域的功能,比如订单服务提供与订单业务场景有关的功能. ...

  6. Spring Cloud Gateway 全局通用异常处理

    为什么需要全局异常处理 在传统 Spring Boot 应用中, 我们 @ControllerAdvice 来处理全局的异常,进行统一包装返回 // 摘至 spring cloud alibaba c ...

  7. Spring Cloud Alibaba学习笔记(16) - Spring Cloud Gateway 内置的路由谓词工厂

    Spring Cloud Gateway路由配置的两种形式 Spring Cloud Gateway的路由配置有两种形式,分别是路由到指定的URL以及路由到指定的微服务,在上文博客的示例中我们就已经使 ...

  8. API网关spring cloud gateway和负载均衡框架ribbon实战

    通常我们如果有一个服务,会部署到多台服务器上,这些微服务如果都暴露给客户,是非常难以管理的,我们系统需要有一个唯一的出口,API网关是一个服务,是系统的唯一出口.API网关封装了系统内部的微服务,为客 ...

  9. Spring Cloud Alibaba(5)---Nacos(配置中心)

    Nacos(配置中心) 有关Spring Cloud Alibaba之前写过四篇文章,这篇也是在上面项目的基础上进行开发. Spring Cloud Alibaba(1)---入门篇 Spring C ...

随机推荐

  1. eclipse cdt运行c程序报错“launch failed,binary not found”

    1. 安装了eclipsecdt版 2. 采用mingw 编译第一个c程序,报错“launch failed,binary not found”.检查是mingw下的bin目录在环境变量里设置错了. ...

  2. Foxmail邮箱最新应用指南二

    Foxmail邮箱最新应用指南二 1.打开Foxmail主界面—工具—账号管理,或者鼠标右击任何已有账号—属性,弹出账号管理窗口,点击左下角的“新建”按钮: 2.输入邮箱地址,下一步→选择邮箱类型(I ...

  3. 【linux】nginx options 跨域问题 请求HTTP错误405 用于访问该页的HTTP动作未被许可 Method Not Allowed

    JavaScript JS 跨域问题 HTTP 错误 405 - 用于访问该页的 HTTP 动作未被许可HTTP 错误 405.0 - Method Not Allowed Nginx 处理跨域问题. ...

  4. cxGrid数据录入

    一.数据录入 1 在TcxGridDBTableView中,设定属性 NewItemRow.Visible = True 2 在cxgrid中输入数据怎样回车换行   在TcxGridDBTableV ...

  5. JS方法 - 字符串处理函数封装汇总 (更新中...)

    一.计算一段字符串的字节长度 字符串的charCodeAt()方法, 可返回字符串固定位置的字符的Unicode编码,这个返回值是0-65535之间的整数,如果值<=255时为英文,反之为中文. ...

  6. 【HIbernate异常】could not initialize proxy - no Session (已解决)

    异常信息: org.hibernate.LazyInitializationException: could not initialize proxy - no Session 解决方法: 用 get ...

  7. React组件设计(转)

    React组件设计 组件分类 展示组件和容器组件 展示组件 容器组件 关注事物的展示 关注事物如何工作 可能包含展示和容器组件,并且一般会有DOM标签和css样式 可能包含展示和容器组件,并且不会有D ...

  8. CSS3 渐变效果

    CSS3 渐变效果 background-image: -moz-linear-gradient(top, #8fa1ff, #3757fa); /* Firefox */ background-im ...

  9. mysqlslap 压测工具使用说明

    PS:今天一同事问我有木有比较靠谱的mysql压力测试工具可用.其实mysql自带就有一个叫mysqlslap的压力测试工具,还是模拟的不错的.下面举例说说.mysqlslap是从5.1.4版开始的一 ...

  10. nginx作为下载文件服务器

    1.前言 当我们希望分享自己的文件时,有多种方式,局域网可以采用共享,rtx传输,qq传输,发送到邮箱,直接u盘拷贝等等.但最简单的就是开启本地服务器,其他电脑通过网页的方式直接下载,这里介绍使用ng ...