一、Spring Cloud Gateway
我們都知道Spring Cloud Gateway是一個基於Spring Boot、Spring WebFlux、Project Reactor構建的高性能網關,旨在提供簡單、高效的API路由。Spring Cloud Gateway基於Netty運行,因此在傳統Servlet容器中或者打成war包是不能正常運行的。
二、Spring Cloud Gateway兩種負載均衡器
2.1 官網説明兩種負載均衡器
Gateway有兩種客户端負載均衡器,LoadBalancerClientFilter和ReactiveLoadBalancerClientFilter。LoadBalancerClientFilter使用一個Ribbon的阻塞式LoadBalancerClient,Gateway建議使用ReactiveLoadBalancerClientFilter。可以通過設置spring.cloud.loadbalancer.ribbon.enabled=false,切換到ReactiveLoadBalancerClientFilter。無論使用Ribbon還是LoadBalancer,在Route中配置的lb是一樣的
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
- id: message-service
uri: lb://message-service
predicates:
- Path=/message/**
nacos:
discovery:
server-addr: localhost:8848
如果URI以lb開頭,比如如上配置中的lb://user-service,Spring Cloud Gateway會用ReactiveLoadBalancerClientFilter 解析服務名為user-service的實例對應的實際host和端口,並做集羣負載均衡。
2.2 跳坑
官網説用lb://lakerservice形式即可,但是配置完成後,並未生效。這個官網沒有詳細説明,查資料也沒有,最後發現必須加入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2.3 簡易流程説明
Client ----> gateway ----> Ribbion負載均衡 取一個服務A ---->轉發到服務A
2.4 Ribbon説明
Spring Cloud Ribbon 在高版本移除了
三、用RouteRecordGlobalFilter記錄路由後的實際代理地址
@Slf4j
@Component
public class RouteRecordGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// RouteToRequestUrlFilter會把實際路由的URL通過該屬性保存
URI proxyRequestUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
long start = System.currentTimeMillis();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long end = System.currentTimeMillis();
log.info("實際調用地址為:{},調用耗時為:{}ms", proxyRequestUri, (end - start));
}));
}
@Override
public int getOrder() {
// 優先級設為最低,先讓RouteToRequestUrlFilter先調用
return Ordered.LOWEST_PRECEDENCE;
}
}
RouteRecordGlobalFilter 這個全局過濾器我們主要用來記錄路由後的實際代理地址,以及調用耗時。我們看下RouteToRequestUrlFilter的描述會發現實際路由地址會通過ServerWebExchange中名為ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的屬性保存。
四、源碼
4.1 基於Ribbon的LoadBalancerClientFilter
gateway中的自動配置類GatewayLoadBalancerClientAutoConfiguration。
package org.springframework.cloud.gateway.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledGlobalFilter;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.DispatcherHandler;
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureAfter({RibbonAutoConfiguration.class})
@EnableConfigurationProperties({LoadBalancerProperties.class})
public class GatewayLoadBalancerClientAutoConfiguration {
public GatewayLoadBalancerClientAutoConfiguration() {
}
@Bean
@ConditionalOnBean({LoadBalancerClient.class})
@ConditionalOnMissingBean({LoadBalancerClientFilter.class, ReactiveLoadBalancerClientFilter.class})
@ConditionalOnEnabledGlobalFilter
public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) {
return new LoadBalancerClientFilter(client, properties);
}
}
2
該自動配置類需要在RibbonAutoConfiguration自動配置類之後執行,該類是spring-cloud-netflix-ribbon的自動配置類,因此需要引入下面的jar包依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
使用默認的ribbon,則ribbon的配置如下
#=======================ribbon配置(使用netflix的Ribbon負載均衡)=======================
#關閉nacos集成ribbon,否則ribbon客户端會從nacos註冊中心獲取服務列表
ribbon.nacos.enabled=false
#配置serviceId為providerService的服務List
providerService.ribbon.listOfServers=http://192.168.10.1:8080,http://192.168.10.2:8080
#配置serviceId為providerService的服務負載均衡
#providerService.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule
providerService.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.AvailabilityFilteringRule
4.2 Spring Cloud Loadbalancer
官網Spring Cloud LoadBalancer
Spring Cloud Load Balancer並不是一個獨立的項目,而是spring-cloud-commons其中的一個模塊,因此很多配置和類可以在spring-cloud-common中找到。gateway中的自動配置類GatewayReactiveLoadBalancerClientAutoConfiguration
Spring Cloud提供了自己的客户端負載均衡器抽象和實現。對於負載平衡機制,ReactiveLoadBalancer已添加了接口,併為其提供了基於Round-Robin和Random的實現。為了獲得實例以從反應式中進行選擇ServiceInstanceListSupplier 。當前,我們支持基於服務發現的實現,ServiceInstanceListSupplier 該實現使用類路徑中可用的發現客户端從服務發現中檢索可用實例。
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({LoadBalancerClient.class, ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureBefore({GatewayLoadBalancerClientAutoConfiguration.class})
@AutoConfigureAfter({LoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({LoadBalancerProperties.class})
public class GatewayReactiveLoadBalancerClientAutoConfiguration {
public GatewayReactiveLoadBalancerClientAutoConfiguration() {
}
@Bean
@ConditionalOnBean({LoadBalancerClientFactory.class})
@ConditionalOnMissingBean({ReactiveLoadBalancerClientFilter.class})
@Conditional({GatewayReactiveLoadBalancerClientAutoConfiguration.OnNoRibbonDefaultCondition.class})
@ConditionalOnEnabledGlobalFilter
public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
return new ReactiveLoadBalancerClientFilter(clientFactory, properties);
}
private static final class OnNoRibbonDefaultCondition extends AnyNestedCondition {
private OnNoRibbonDefaultCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnMissingClass({"org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient"})
static class RibbonLoadBalancerNotPresent {
RibbonLoadBalancerNotPresent() {
}
}
@ConditionalOnProperty(
value = {"spring.cloud.loadbalancer.ribbon.enabled"},
havingValue = "false"
)
static class RibbonNotEnabled {
RibbonNotEnabled() {
}
}
}
}