• Spring Cloud(三):Spring Cloud Alibaba Ribbon


    负载均衡介绍

    目前主流的负载均衡方案分为以下两种:

    • 集中式负载均衡:在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如 F5),也有软件的(比如 Nginx)

    • 客户端根据自己的请求情况做负载均衡:Ribbon 就属于客户端自己做负载均衡

    服务端的负载均衡

    在这里插入图片描述

    客户端的负载均衡

    在这里插入图片描述

    常见负载均衡算法

    • 随机:通过随机选择服务进行执行,一般这种方式使用较少
    • 轮训:负载均衡默认实现方式,请求来之后排队处理
    • 加权轮训:通过对服务器性能的分析,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力
    • 地址hash:通过客户端请求的IP地址的hash值取模映射进行服务器调度
    • 最小连接数:即使请求均衡了,压力不一定会均衡,最小连接数法就是根据服务器的情况,比如请求积压数等参数,将请求分配到当前压力最小的服务器上,使用比较多

    Ribbon

    Spring Cloud Ribbon是基于Netflix Ribbon 实现的一套客户端的负载均衡工具

    Ribbon客户端组件提供一系列的完善的配置,如超时,重试等

    通过Load Balancer获取到服务提供的所有机器实例,Ribbon会自动基于某种规则(轮询,随机)去调用这些服务。Ribbon也可以实现我们自己的负载均衡算法

    注册中心已经引入Ribbon

     <dependency>
       <groupId>org.springframework.cloudgroupId>
       <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
     dependency>
    
    • 1
    • 2
    • 3
    • 4

    RestConfig

    @Configuration
    public class RestConfig {
    
        @Bean
        public RestTemplate noLoadBalancedrestTemplate() {
            return new RestTemplate();
        }
    
        @Bean
        @LoadBalanced //open-api-service-product => ip:port
        public RestTemplate hasLoadBalancedrestTemplate() {
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    RestTemplateController

    @Slf4j
    @RestController
    @RequestMapping("/demo")
    public class RestTemplateController {
    
        @Autowired
        private RestTemplate noLoadBalancedrestTemplate;
    
    
        @Autowired
        private RestTemplate hasLoadBalancedrestTemplate;
    
        @GetMapping("/ip")
        public String ip() {
            log.info("demo product start");
            String url = "http://localhost:8001/check";
            String result = noLoadBalancedrestTemplate.getForObject(url, String.class);
            log.info("demo product result = " + result);
            return result;
        }
    
        @GetMapping("/ribbon")
        public String ribbon() {
            log.info("demo product start");
            String url = "http://open-api-service-product/check";
            String result = hasLoadBalancedrestTemplate.getForObject(url, String.class);
            log.info("demo product result = " + result);
            return result;
        }
    
        //没有配置@LoadBalanced 不识别项目名 open-api-service-product
        @GetMapping("/error_not_is_load_balanced")
        public String no_load_balanced_error() {
            log.info("demo product start");
            String url = "http://open-api-service-product/check";
            String result = noLoadBalancedrestTemplate.getForObject(url, String.class);
            log.info("demo product result = " + result);
            return result;
        }
    
        //@LoadBalanced 不识别IP地址 localhost:8001
        @GetMapping("/error_not_is_serveice_name")
        public String error_not_is_serveice_name() {
            log.info("demo product start");
            String url = "http://localhost:8001/check";
            String result = hasLoadBalancedrestTemplate.getForObject(url, String.class);
            log.info("demo product result = " + result);
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • http://localhost:8000/demo/error_not_is_load_balanced
      在这里插入图片描述
    • http://localhost:8000/demo/error_not_is_serveice_name
      在这里插入图片描述

    Ribbon内核原理

    在这里插入图片描述

    Ribbon 源码分析

    在这里插入图片描述

    @LoadBalanced 注解原理 Qualifier 之 LoadBalancerInterceptor

    @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Qualifier //注入优先使用
    public @interface LoadBalanced {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    LoadBalancerAutoConfiguration 默认自动装配的

    @Configuration
    @ConditionalOnClass(RestTemplate.class)
    @ConditionalOnBean(LoadBalancerClient.class)
    @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
    public class LoadBalancerAutoConfiguration {
    
    	...
    	@LoadBalanced //对添加有@LoadBalanced的Bean定制添加一个拦截器
    	@Autowired(required = false)
    	private List<RestTemplate> restTemplates = Collections.emptyList();
    
    	...
    	@Configuration
    	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    	static class LoadBalancerInterceptorConfig {
    		@Bean
    		public LoadBalancerInterceptor ribbonInterceptor(
    				LoadBalancerClient loadBalancerClient,
    				LoadBalancerRequestFactory requestFactory) {
    			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
    		}
    
    		@Bean
    		@ConditionalOnMissingBean
    		public RestTemplateCustomizer restTemplateCustomizer( //添加定制
    				final LoadBalancerInterceptor loadBalancerInterceptor) {
    			return restTemplate -> {
                    List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                            restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor); //添加LoadBalancerInterceptor拦截器
                    restTemplate.setInterceptors(list);
                };
    		}
    	}
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    @Configuration
    public class RestConfig {
    
        @Bean
        public RestTemplate noLoadBalancedrestTemplate() {
            return new RestTemplate();
        }
    
        @Bean
        @LoadBalanced //open-api-service-product => ip:port
        public RestTemplate hasLoadBalancedrestTemplate() {
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    @LoadBalanced使用了@Qualifier,spring中@Qualifier用于筛选限定注入Bean。

    @LoadBalanced利用@Qualifier作为restTemplates注入的筛选条件,筛选出具有负载均衡标识的RestTemplate。

    被@LoadBalanced注解的restTemplate会被定制,添加LoadBalancerInterceptor拦截器。

    注意: SmartInitializingSingleton是在所有的bean都实例化完成之后才会调用的,所以在bean的实例化期间使用@LoadBalanced修饰的restTemplate是不具备负载均衡作用的。

    如果不使用@LoadBalanced注解,也可以通过添加LoadBalancerInterceptor拦截器让restTemplate起到负载均衡器的作用。

    @Bean
    public RestTemplate restTemplate(LoadBalancerInterceptor loadBalancerInterceptor) {
        RestTemplate restTemplate = new RestTemplate();
        //注入loadBalancerInterceptor拦截器
        restTemplate.setInterceptors(Arrays.asList(loadBalancerInterceptor));
        return restTemplate;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    默认轮询算法

    AbstractServerPredicate#incrementAndGetModulo

     private int incrementAndGetModulo(int modulo) {
         for (;;) {
             int current = nextIndex.get();
             int next = (current + 1) % modulo;
             if (nextIndex.compareAndSet(current, next) && current < modulo)
                 return current;
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Ribbon 自定义负载均衡

    • 需要定制restTemplate (InitializingBean 在 SmartInitializingSingleton 之前调用引起的问题)

    注意: SmartInitializingSingleton是在所有的bean都实例化完成之后才会调用的,所以在bean的实例化期间使用@LoadBalanced修饰的restTemplate是不具备负载均衡作用的。

    RibbonConfig

    @Configuration
    public class RibbonConfig {
    
        /**
         * 方法实现说明:原生的RestTemplate +@LB不行 因为在
         * InitializingBean方法执行前我们的RestTemplate还没有被增强
         * 需要自己改写RestTemplate
         */
    //    @Bean
    //    public DemoRestTemplate restTemplate(DiscoveryClient discoveryClient) {
    //        return new DemoRestTemplate(discoveryClient);
    //    }
    
        /**
         *
         * 手动注入loadBalancerInterceptor拦截器,实现负载均衡功能
         * @param loadBalancerInterceptor
         * @return
         *
         */
        @Bean
        public RestTemplate restTemplate(LoadBalancerInterceptor loadBalancerInterceptor){
            RestTemplate restTemplate = new RestTemplate();
            List<ClientHttpRequestInterceptor> list = new ArrayList();
            list.add(loadBalancerInterceptor);
            restTemplate.setInterceptors(list);
            return restTemplate;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    DemoRestTemplate

    /**
     * 根据RestTemplate特性自己改造
     */
    @Slf4j
    public class DemoRestTemplate extends RestTemplate {
    
        private DiscoveryClient discoveryClient;
    
        public DemoRestTemplate(DiscoveryClient discoveryClient) {
            this.discoveryClient = discoveryClient;
        }
    
        protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
                                  @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    
            Assert.notNull(url, "URI is required");
            Assert.notNull(method, "HttpMethod is required");
            ClientHttpResponse response = null;
            try {
                //判断url的拦截路径,然后去redis(作为注册中心)获取地址随机选取一个
                log.info("请求的url路径为:{}",url);
                url = replaceUrl(url);
                log.info("替换后的路径:{}",url);
                ClientHttpRequest request = createRequest(url, method);
                if (requestCallback != null) {
                    requestCallback.doWithRequest(request);
                }
                response = request.execute();
                handleResponse(url, method, response);
                return (responseExtractor != null ? responseExtractor.extractData(response) : null);
            }
            catch (IOException ex) {
                String resource = url.toString();
                String query = url.getRawQuery();
                resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
                throw new ResourceAccessException("I/O error on " + method.name() +
                        " request for \"" + resource + "\": " + ex.getMessage(), ex);
            } finally {
                if (response != null) {
                    response.close();
                }
            }
        }
    
    
        /**
         * 把服务实例名称替换为ip:端口
         */
        private URI replaceUrl(URI url){
            //解析我们的微服务的名称
            String sourceUrl = url.toString();
            String [] httpUrl = sourceUrl.split("//");
            int index = httpUrl[1].replaceFirst("/","@").indexOf("@");
            String serviceName = httpUrl[1].substring(0,index);
    
            //通过微服务的名称去nacos服务端获取 对应的实例列表
            List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
            if(serviceInstanceList.isEmpty()) {
                throw new RuntimeException("没有可用的微服务实例列表:"+serviceName);
            }
    
            //采取随机的获取一个
            Random random = new Random();
            Integer randomIndex = random.nextInt(serviceInstanceList.size());
            log.info("随机下标:{}",randomIndex);
            String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
            log.info("随机选举的服务IP:{}",serviceIp);
            String targetSource = httpUrl[1].replace(serviceName,serviceIp);
            try {
                return new URI(targetSource);
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
            return url;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    Ribbon扩展功能

    Ribbon相关接口

    参考: org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration

    • IClientConfig:Ribbon的客户端配置,默认采用DefaultClientConfigImpl实现
    • IRule:Ribbon的负载均衡策略,默认采用ZoneAvoidanceRule实现,该策略能够在多区域环境下选出最佳区域的实例进行访问
    • IPing:Ribbon的实例检查策略,默认采用DummyPing实现,该检查策略是一个特殊的实现,实际上它并不会检查实例是否可用,而是始终返回true,默认认为所有服务实例都是可用的
    • ServerList:服务实例清单的维护机制,默认采用ConfigurationBasedServerList实现
    • ServerListFilter:服务实例清单过滤机制,默认采ZonePreferenceServerListFilter,该策略能够优先过滤出与请求方处于同区域的服务实例
    • ILoadBalancer:负载均衡器,默认采用ZoneAwareLoadBalancer实现,它具备了区域感知的能力

    Ribbon负载均衡策略

    在这里插入图片描述

    • RandomRule: 随机选择一个Server
    • RetryRule: 对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的server
    • RoundRobinRule: 轮询选择, 轮询index,选择index对应位置的Server
    • AvailabilityFilteringRule: 过滤掉一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就是检查status里记录的各个Server的运行状态
    • BestAvailableRule: 选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过
    • WeightedResponseTimeRule: 根据响应时间加权,响应时间越长,权重越小,被选中的可能性越低
    • ZoneAvoidanceRule: 默认的负载均衡策略,即复合判断Server所在区域的性能和Server的可用性选择Server,在没有区域的环境下,类似于轮询
    • NacosRule: 优先调用同一集群的实例,基于随机权重

    修改默认负载均衡策略

    public class RibbonClientConfiguration {
    	...
        @Bean
        @ConditionalOnMissingBean
        public IRule ribbonRule(IClientConfig config) {
            if (this.propertiesFactory.isSet(IRule.class, this.name)) {
                return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
            } else {
                ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
                rule.initWithNiwsConfig(config);
                return rule;
            }
        }
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    • 全局配置:调用的微服务,一律使用指定的负载均衡策略
    @Configuration
    public class RibbonConfig {
    
        /**
         * 全局配置
         * 指定负载均衡策略
         * @return
         */
        @Bean
        public IRule ribbonRule() {
            // 指定使用Nacos提供的负载均衡策略(优先调用同一集群的实例,基于随机权重)
            return new NacosRule();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 局部配置:调用指定微服务时,使用对应的负载均衡策略

    修改application.yml

    # 被调用的微服务名
    open-api-service-product:
      ribbon:
        # 指定使用Nacos提供的负载均衡策略(优先调用同一集群的实例,基于随机&权重)
        NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
    
    • 1
    • 2
    • 3
    • 4
    • 5

    配置权重
    在这里插入图片描述
    配置集群

    spring:
      application:
        name: open-api-global-rule
      cloud:
        nacos:
          discovery:
            server-addr: nacos.localhost.com:8848
            namespace: 2cd251e2-5fb4-491a-955e-67c43be601f4
            group: open-api
            cluster-name: ShangHai #配置上海集群
            metadata: #配置附加信息,自定义负载均衡时获取过滤
              version: 1.0
              flag: grey
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    NacosRule 负载均衡策略(优先调用同一集群的实例,基于随机权重)

    public class NacosRule extends AbstractLoadBalancerRule {
    
    	private static final Logger LOGGER = LoggerFactory.getLogger(NacosRule.class);
    
    	@Autowired
    	private NacosDiscoveryProperties nacosDiscoveryProperties;
    
    	@Override
    	public Server choose(Object key) {
    		try {
    			String clusterName = this.nacosDiscoveryProperties.getClusterName();
    			DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
    			String name = loadBalancer.getName();
    
    			NamingService namingService = nacosDiscoveryProperties
    					.namingServiceInstance();
    			List<Instance> instances = namingService.selectInstances(name, true);
    			if (CollectionUtils.isEmpty(instances)) {
    				LOGGER.warn("no instance in service {}", name);
    				return null;
    			}
    
    			List<Instance> instancesToChoose = instances;
    			if (StringUtils.isNotBlank(clusterName)) {
    				List<Instance> sameClusterInstances = instances.stream()
    						.filter(instance -> Objects.equals(clusterName,
    								instance.getClusterName()))
    						.collect(Collectors.toList());
    				if (!CollectionUtils.isEmpty(sameClusterInstances)) {
    					instancesToChoose = sameClusterInstances;
    				}
    				else {
    					LOGGER.warn(
    							"A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}",
    							name, clusterName, instances);
    				}
    			}
    
    			Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
    
    			return new NacosServer(instance);
    		}
    		catch (Exception e) {
    			LOGGER.warn("NacosRule error", e);
    			return null;
    		}
    	}
    
    	@Override
    	public void initWithNiwsConfig(IClientConfig iClientConfig) {
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    自定义负载均衡策略

    通过实现 IRule 接口可以自定义负载策略,主要的选择服务逻辑在 choose 方法中

    参考 NacosRule

    饥饿加载

    在进行服务调用的时候,如果网络情况不好,第一次调用会超时。Ribbon默认懒加载,意味着只有在发起调用的时候才会创建客户端。

    ribbon:
      eager-load:
        enabled: true
        clients: open-api-service-product
    
    • 1
    • 2
    • 3
    • 4

    参数说明:

    • ribbon.eager-load.enabled:开启ribbon的饥饿加载模式
    • ribbon.eager-load.clients:指定需要饥饿加载的服务名,也就是你需要调用的服务,如果有多个服务,则用逗号隔开

    不能在Spring初始化中使用LoadBalance远程服务如预热、cache、token

    什么是LoadBalancer

    Spring Cloud LoadBalancer是Spring Cloud官方自己提供的客户端负载均衡器, 用来替代Ribbon。

    Spring官方提供了两种客户端都可以使用loadbalancer:

    • RestTemplate
      RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。
    • WebClient
      WebClient是从Spring WebFlux 5.0版本开始提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具。它的响应式编程的基于Reactor的。WebClient中提供了标准Http请求方式对应的get、post、put、delete等方法,可以用来发起相应的请求。

    注意: nacos-discovery中引入了ribbon,需要移除ribbon的包
    如果不移除,也可以在yml中配置不使用ribbon spring.cloud.loadbalancer.ribbon.enabled=false

    默认情况下,如果同时拥有RibbonLoadBalancerClient和BlockingLoadBalancerClient,为了保持向后兼容性,将使用RibbonLoadBalancerClient。

    官网地址: https://spring.io/guides/gs/spring-cloud-loadbalancer/

    在这里插入图片描述

    RestTemplate整合LoadBalancer
    1. 依赖
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-loadbalancerartifactId>
    dependency>
    
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
            exclusion>
        exclusions>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    或者

    spring:
      application:
        name: open-api-service-product
      cloud:
        nacos:
          discovery:
            server-addr: nacos.localhost.com:8848
        # 不使用ribbon,使用loadbalancer
        loadbalancer:
          ribbon:
            enabled: false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 配置
      使用@LoadBalanced注解修饰RestTemplate,开启客户端负载均衡功能
    @Configuration
    public class RestConfig {
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    WebClient整合LoadBalancer
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-loadbalancerartifactId>
    dependency>
    
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webfluxartifactId>
    dependency>
    
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
            exclusion>
        exclusions>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. 配置
    @Configuration
    public class WebClientConfig {
    
        @LoadBalanced
        @Bean
        WebClient.Builder webClientBuilder() {
            return WebClient.builder();
        }
        
        @Bean
        WebClient webClient() {
          return webClientBuilder().build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 使用
      基于WebClient
    @Autowired
    private WebClient webClient;
    
    @RequestMapping(value = "/test")
    public Mono<R> test() {
    
        String url = "http://open-api-service-product/check";
        //基于WebClient
        Mono<R> result = webClient.get().uri(url).retrieve().bodyToMono(R.class);
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    基于WebClient+webFlux

    @Autowired
    private ReactorLoadBalancerExchangeFilterFunction lbFunction;
    
    @RequestMapping(value = "/check")
    public Mono<R> check() {
    
        String url = "http://open-api-service-product/check";
        Mono<R> result = WebClient.builder()
                .filter(lbFunction)
                .build()
                .get()
                .uri(url)
                .retrieve()
                .bodyToMono(R.class);
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    Linkers of Addition
    k8s部署手册-v06
    微信小程序的双向数据绑定和vue的哪里不一样?下拉刷新的方式代码示例
    漏洞复现-.Net-ueditor上传
    【matplotlib】降维 可视化
    光环:元宇宙概念及生态发展现状与研判——张子良
    基于SpringBoot的冬奥会科普平台
    @Autowired和@Resource的区别
    Dijkstra&floyed
    系统设计综述——个人思考总结
  • 原文地址:https://blog.csdn.net/menxu_work/article/details/126750012