• Spring Cloud Gateway负载均衡


    一、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
    我们都知道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是一样的

    1. spring:
    2. cloud:
    3. gateway:
    4. routes:
    5. - id: user-service
    6. uri: lb://user-service
    7. predicates:
    8. - Path=/user/**
    9. - id: message-service
    10. uri: lb://message-service
    11. predicates:
    12. - Path=/message/**
    13. nacos:
    14. discovery:
    15. server-addr: localhost:8848

    如果URI以lb开头,比如如上配置中的lb://user-service,Spring Cloud Gateway会用ReactiveLoadBalancerClientFilter 解析服务名为user-service的实例对应的实际host和端口,并做集群负载均衡。

    2.2 跳坑
    官网说用lb://lakerservice形式即可,但是配置完成后,并未生效。这个官网没有详细说明,查资料也没有,最后发现必须加入依赖:

    1. org.springframework.cloud
    2. spring-cloud-starter-loadbalancer

    2.3 简易流程说明

    Client ----> gateway ----> Ribbion负载均衡 取一个服务A ---->转发到服务A

    2.4 Ribbon说明
    Spring Cloud Ribbon 在高版本移除了

    三、用RouteRecordGlobalFilter记录路由后的实际代理地址

    1. @Slf4j
    2. @Component
    3. public class RouteRecordGlobalFilter implements GlobalFilter, Ordered {
    4. @Override
    5. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    6. // RouteToRequestUrlFilter会把实际路由的URL通过该属性保存
    7. URI proxyRequestUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
    8. long start = System.currentTimeMillis();
    9. return chain.filter(exchange).then(Mono.fromRunnable(() -> {
    10. long end = System.currentTimeMillis();
    11. log.info("实际调用地址为:{},调用耗时为:{}ms", proxyRequestUri, (end - start));
    12. }));
    13. }
    14. @Override
    15. public int getOrder() {
    16. // 优先级设为最低,先让RouteToRequestUrlFilter先调用
    17. return Ordered.LOWEST_PRECEDENCE;
    18. }
    19. }

    RouteRecordGlobalFilter 这个全局过滤器我们主要用来记录路由后的实际代理地址,以及调用耗时。我们看下RouteToRequestUrlFilter的描述会发现实际路由地址会通过ServerWebExchange中名为ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的属性保存。

    四、源码
    4.1 基于Ribbon的LoadBalancerClientFilter

    gateway中的自动配置类GatewayLoadBalancerClientAutoConfiguration。

    1. package org.springframework.cloud.gateway.config;
    2. import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    3. import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    4. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    5. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    6. import org.springframework.boot.context.properties.EnableConfigurationProperties;
    7. import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    8. import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledGlobalFilter;
    9. import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
    10. import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
    11. import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
    12. import org.springframework.context.annotation.Bean;
    13. import org.springframework.context.annotation.Configuration;
    14. import org.springframework.web.reactive.DispatcherHandler;
    15. @Configuration(
    16. proxyBeanMethods = false
    17. )
    18. @ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})
    19. @AutoConfigureAfter({RibbonAutoConfiguration.class})
    20. @EnableConfigurationProperties({LoadBalancerProperties.class})
    21. public class GatewayLoadBalancerClientAutoConfiguration {
    22. public GatewayLoadBalancerClientAutoConfiguration() {
    23. }
    24. @Bean
    25. @ConditionalOnBean({LoadBalancerClient.class})
    26. @ConditionalOnMissingBean({LoadBalancerClientFilter.class, ReactiveLoadBalancerClientFilter.class})
    27. @ConditionalOnEnabledGlobalFilter
    28. public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) {
    29. return new LoadBalancerClientFilter(client, properties);
    30. }
    31. }

    该自动配置类需要在RibbonAutoConfiguration自动配置类之后执行,该类是spring-cloud-netflix-ribbon的自动配置类,因此需要引入下面的jar包依赖

    1. org.springframework.cloud
    2. spring-cloud-starter-netflix-ribbon

    使用默认的ribbon,则ribbon的配置如下

    1. #=======================ribbon配置(使用netflix的Ribbon负载均衡)=======================
    2. #关闭nacos集成ribbon,否则ribbon客户端会从nacos注册中心获取服务列表
    3. ribbon.nacos.enabled=false
    4. #配置serviceId为providerService的服务List
    5. providerService.ribbon.listOfServers=http://192.168.10.1:8080,http://192.168.10.2:8080
    6. #配置serviceId为providerService的服务负载均衡
    7. #providerService.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule
    8. providerService.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.AvailabilityFilteringRule

    4.2 Spring Cloud Loadbalancer

    Spring Cloud Load Balancer并不是一个独立的项目,而是spring-cloud-commons其中的一个模块,因此很多配置和类可以在spring-cloud-common中找到。gateway中的自动配置类GatewayReactiveLoadBalancerClientAutoConfiguration

    Spring Cloud提供了自己的客户端负载均衡器抽象和实现。对于负载平衡机制,ReactiveLoadBalancer已添加了接口,并为其提供了基于Round-Robin和Random的实现。为了获得实例以从反应式中进行选择ServiceInstanceListSupplier 。当前,我们支持基于服务发现的实现,ServiceInstanceListSupplier 该实现使用类路径中可用的发现客户端从服务发现中检索可用实例。

    1. @Configuration(
    2. proxyBeanMethods = false
    3. )
    4. @ConditionalOnClass({LoadBalancerClient.class, ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class})
    5. @AutoConfigureBefore({GatewayLoadBalancerClientAutoConfiguration.class})
    6. @AutoConfigureAfter({LoadBalancerAutoConfiguration.class})
    7. @EnableConfigurationProperties({LoadBalancerProperties.class})
    8. public class GatewayReactiveLoadBalancerClientAutoConfiguration {
    9. public GatewayReactiveLoadBalancerClientAutoConfiguration() {
    10. }
    11. @Bean
    12. @ConditionalOnBean({LoadBalancerClientFactory.class})
    13. @ConditionalOnMissingBean({ReactiveLoadBalancerClientFilter.class})
    14. @Conditional({GatewayReactiveLoadBalancerClientAutoConfiguration.OnNoRibbonDefaultCondition.class})
    15. @ConditionalOnEnabledGlobalFilter
    16. public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
    17. return new ReactiveLoadBalancerClientFilter(clientFactory, properties);
    18. }
    19. private static final class OnNoRibbonDefaultCondition extends AnyNestedCondition {
    20. private OnNoRibbonDefaultCondition() {
    21. super(ConfigurationPhase.REGISTER_BEAN);
    22. }
    23. @ConditionalOnMissingClass({"org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient"})
    24. static class RibbonLoadBalancerNotPresent {
    25. RibbonLoadBalancerNotPresent() {
    26. }
    27. }
    28. @ConditionalOnProperty(
    29. value = {"spring.cloud.loadbalancer.ribbon.enabled"},
    30. havingValue = "false"
    31. )
    32. static class RibbonNotEnabled {
    33. RibbonNotEnabled() {
    34. }
    35. }
    36. }
    37. }

    该自动配置类在LoadBalancerAutoConfiguration自动配置类之后执行,该配置类在spring-cloud-commons中。
    该自动配置类在GatewayLoadBalancerClientAutoConfiguration自动配置类之前执行,也就是前面的基于Ribbon的自动装配类,由此可见,gateway是优先使用ReactiveLoadBalancer的,只有没有开启ReactiveLoadBalancer时才使用使用Ribbon。

    引入依赖:

    1. org.springframework.cloud
    2. spring-cloud-starter-loadbalancer

    配置如下:
    从配置文件中读取服务,而不是从服务注册中心自动发现服务
    注意:如果在项目的类路径下存在Spring Cloud Ribbon相关的类,需要通过配置关闭Ribbon功能,因为Spring Cloud默认优先使用Ribbon,因此spring.cloud.loadbalancer.ribbon.enabled禁用调Ribbon,这也是上面刚提到过的。

    1. #=======================SpringCloudLoadBalancer配置服务列表=======================
    2. #使用ReactiveLoadBalancerClient时通过该参数禁用调ribbon
    3. spring.cloud.loadbalancer.ribbon.enabled=false
    4. #配置providerService的instances,不是从注册中心自动发现服务实例
    5. spring.cloud.discovery.client.simple.instances.providerService[0].uri=http://192.168.10.1:8080
    6. spring.cloud.discovery.client.simple.instances.providerService[1].uri=http://192.168.10.2:8080
    7. #指定健康检查请求路径,默认健康检查路径是/actuator/health
    8. spring.cloud.loadbalancer.health-check.path.providerService=/custom/customHealthCheckPath
    9. #运行状况检查计划程序的初始延迟值
    10. spring.cloud.loadbalancer.health-check.initial-delay=0
    11. #重新运行运行状况检查计划程序的时间间隔
    12. spring.cloud.loadbalancer.health-check.interval=5s
    13. #启用预定义的负载平衡器配置
    14. spring.cloud.loadbalancer.configurations=health-check

    4.3 SimpleDiscoveryClient

    SimpleDiscoveryClient可以结合注册中心使用,也可以静态配置。如果在类路径中没有支持从注册中心发现服务的DiscoveryClient实例,则将使用SimpleDiscoveryClient实例,该实例使用SimpleDiscoveryProperties来获取有关服务和实例的信息。参考上面的配置文件中的配置。

    1. public class SimpleDiscoveryClient implements DiscoveryClient {
    2. private SimpleDiscoveryProperties simpleDiscoveryProperties;
    3. public SimpleDiscoveryClient(SimpleDiscoveryProperties simpleDiscoveryProperties) {
    4. this.simpleDiscoveryProperties = simpleDiscoveryProperties;
    5. }
    6. public String description() {
    7. return "Simple Discovery Client";
    8. }
    9. public List getInstances(String serviceId) {
    10. List serviceInstances = new ArrayList();
    11. List serviceInstanceForService = (List)this.simpleDiscoveryProperties.getInstances().get(serviceId);
    12. if (serviceInstanceForService != null) {
    13. serviceInstances.addAll(serviceInstanceForService);
    14. }
    15. return serviceInstances;
    16. }
    17. public List getServices() {
    18. return new ArrayList(this.simpleDiscoveryProperties.getInstances().keySet());
    19. }
    20. public int getOrder() {
    21. return this.simpleDiscoveryProperties.getOrder();
    22. }
    23. }

    SimpleDiscoveryProperties 服务实例的属性配置

    1. @ConfigurationProperties(
    2. prefix = "spring.cloud.discovery.client.simple"
    3. )
    4. public class SimpleDiscoveryProperties {
    5. //在配置文件中配置的实例属性
    6. private Map> instances = new HashMap();
    7. private DefaultServiceInstance local = new DefaultServiceInstance((String)null, (String)null, (String)null, 0, false);
    8. private int order = 0;
    9. }

    DefaultServiceInstance默认的服务实例定义

    1. public class DefaultServiceInstance implements ServiceInstance {
    2. private String instanceId;
    3. private String serviceId;
    4. private String host;
    5. private int port;
    6. private boolean secure;
    7. private Map metadata;
    8. private URI uri;
    9. ...省略...
    10. }

    4.3.1 在负载均衡算法之间切换
    ReactiveLoadBalancer默认情况下使用的实现是RoundRobinLoadBalancer。要针对选定的服务或所有服务切换到不同的实现,可以使用自定义LoadBalancer配置机制。例如,可以通过@LoadBalancerClient注释传递以下配置以切换为使用RandomLoadBalancer:

    1. public class CustomLoadBalancerConfiguration {
    2. @Bean
    3. ReactorLoadBalancer randomLoadBalancer(Environment environment,
    4. LoadBalancerClientFactory loadBalancerClientFactory) {
    5. String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
    6. return new RandomLoadBalancer(loadBalancerClientFactory
    7. .getLazyProvider(name, ServiceInstanceListSupplier.class),
    8. name);
    9. }
    10. }

    4.3.2 实例健康检查

    可以为LoadBalancer启用计划健康检查。为此提供了HealthCheckServiceInstanceListSupplier。它定期验证委托ServiceInstanceListSupplier提供的实例是否仍然存在,并且只返回健康的实例,除非没有实例—然后返回所有检索到的实例。

    在使用SimpleDiscoveryClient时,这种机制特别有用。对于由实际服务注册中心支持的客户端,不需要使用它,因为我们在查询外部ServiceDiscovery之后已经获得了健康的实例。

    对于每个服务只有少量实例的设置,也建议使用此供应商,以避免重试调用失败的实例。

  • 相关阅读:
    逆向分析 工具、加壳、安全防护篇
    PCL学习之滤波Filtering
    数列分块入门
    【图解HTTP】确保WEB安全的HTTPS
    公共建筑节能大数据应用进展
    laravel+elementui el-upload上传文件
    微擎模块 名片小程序 1.7.9 小程序前端+后端有加密,优化代码,新增付费发布幻灯片
    Linux常用命令(精简版)
    《深入浅出OCR》第一章:OCR技术导论
    SpringBoot数据库管理 - 用flyway对数据库管理和迁移
  • 原文地址:https://blog.csdn.net/shun35/article/details/136174129