[SpringCloud][8]Spring Cloud LoadBanlancer快速上手以及LoadBalancer原理

文章目录

Spring Cloud LoadBanlancer

快速上手

Spring Cloud2020.0.1 版本开始,移除了 Ribbon 组件,使用 Spring Cloud LoadBalancer 组件来代替 Ribbon 实现客户端负载均衡

实现负载均衡---@LoadBalanced

  1. RestTemplate 这个 Bean 添加 @LoadBalanced 注解即可
java 复制代码
package org.example.order.config;  
  
import org.springframework.cloud.client.loadbalancer.LoadBalanced;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.client.RestTemplate;  
  
@Configuration  
public class BeanConfig {  
    @Bean  
    @LoadBalanced    
    public RestTemplate restTemplate() {  
        return new RestTemplate();  
    }  
}
  1. 修改 IP 端口号为服务名称
    order-service/service/OrderService
java 复制代码
public OrderInfo selectOrderById(Integer orderId) {  
    OrderInfo orderInfo = orderMapper.selectOrderById(orderId);  
    //String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();  
    String url = "http://product-service/product/" + orderInfo.getProductId();  
    ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);  
    orderInfo.setProductInfo(productInfo);  
    return orderInfo;  
}

启动多个 product-service 实例

按照上一篇文章的方法,启动多个实例

测试负载均衡

连续多次发起请求: http://127.0.0.1:8080/order/1

观察 product-service 日志,会发现请求被分配到这三个实例上了

负载均衡策略

负载均衡策略是一种思想,无论是哪种负载均衡器,它们的负载均衡策略都是相似的,Spring Cloud LoadBalancer 仅支持两种负载均衡策略:

  1. 轮询Round Robin):轮询策略是指服务器轮流处理用户的请求。这是一种实现最简单,也最常用的策略。生活中也有类似的场景,比如学校轮流值日,或者轮流打扫卫生
  2. 随机选择Random):随机选择策略是指随机选择一个后端服务器来处理新的请求

自定义负载均衡策略

Spring Cloud LoadBalancer 默认如在均衡策略是轮询策略 ,实现是 RoundRobinLoadBalancer,如果服务的消费者如果想采用随机的负载均衡策略,也非常简单

定义随机算法对象
  1. 定义随机算法对象,通过 @Bean 将其加载到 Spring 容器中

此处使用 Spring Cloud LoadBalancer 提供的 RandomLoadBalancer

java 复制代码
package org.example.order.config;  
  
  
import org.springframework.cloud.client.ServiceInstance;  
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;  
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;  
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;  
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;  
import org.springframework.context.annotation.Bean;  
import org.springframework.core.env.Environment;  
  
public class LoadBalancerConfig {  
    @Bean  
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,  
        LoadBalancerClientFactory loadBalancerClientFactory) {  
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);  
        return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,  
                ServiceInstanceListSupplier.class), name);  
    }  
}
  • 注意:该类需要满足:
    1. 不用 @Configuration 注解
    2. 在组建扫描范围内
  1. 使用 @LoadBanlancerClient 或者 @LoadBalancerClients 注解

RestTemplate 配置类上方,使用 @LoadBalancerClientLoadBalancerClients 注解,可以对不同的服务提供方配置不同的客户端负载均衡算法策略

由于我们项目中只有一个服务提供者,所以使用 @LoadBalancerClient

java 复制代码
package org.example.order.config;  
  
import org.springframework.cloud.client.loadbalancer.LoadBalanced;  
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.client.RestTemplate;  
  
@LoadBalancerClient(name = "product-service", configuration = LoadBalancerConfig.class)  
@Configuration  
public class BeanConfig {  
    @Bean  
    @LoadBalanced    public RestTemplate restTemplate() {  
        return new RestTemplate();  
    }  
}
  • @LoadBalancerClient 注解说明
    1. name:该负载均衡策略对哪个服务生效(服务提供方)
    2. configuration:该负载均衡策略,用哪个负载均衡策略实现

LoadBalancer 原理

LoadBalancer 的实现,主要是 LoadBalancerInterceptor,这个类会对 RestTemplate 的请求进行拦截,然后从 Eureka 根据服务 ID 获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务 ID

我们来看看源码实现:

java 复制代码
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    //...
    public ClientHttpResponse intercept(final HttpRequest request, final
            byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a
                valid hostname: " + originalUri);
        return (ClientHttpResponse) this.loadBalancer.execute(serviceName,
                this.requestFactory.createRequest(request, body, execution));
    }
}

可以看到这里的 intercept 方法,拦截了用户的 HttpRequest 请求,然后做了几件事:

  1. request.getURI():从请求中获取 uri,也就是 http://product-service/product/1001
  2. originalUri.getHost():从 uri 中获取路径的主机名,也就是服务 idproduct-service
  3. loadBalancer.execute:根据服务 id,进行负载均衡,并处理请求

点进去继续跟踪:

java 复制代码
public class BlockingLoadBalancerClient implements LoadBalancerClient {

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
            throws IOException {
        String hint = this.getHint(serviceId);
        LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new 
            LoadBalancerRequestAdapter<>(request, this.buildRequestContext(request, hint));
        Set<LoadBalancerLifecycle> supportedLifecycleProcessors = 
            this.getSupportedLifecycleProcessors(serviceId);
        supportedLifecycleProcessors.forEach((lifecycle) -> {
            lifecycle.onStart(lbRequest);
        });

        // 根据serviceId, 和负载均衡策略,选择处理的服务
        ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);

        if (serviceInstance == null) {
            supportedLifecycleProcessors.forEach((lifecycle) -> {
                lifecycle.onComplete(new CompletionContext(Status.DISCARD, 
                    lbRequest, new EmptyResponse()));
            });
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            return this.execute(serviceId, serviceInstance, lbRequest);
        }
    }

    /**
     * 根据serviceId, 和负载均衡策略,选择处理的服务
     */
    public <T> ServiceInstance choose(String serviceId, Request<T> request) {
        // 获取负载均衡器
        ReactiveLoadBalancer<ServiceInstance> loadBalancer = 
            this.loadBalancerClientFactory.getInstance(serviceId);
        if (loadBalancer == null) {
            return null;
        } else {
            // 根据负载均衡算法,在列表中选择一个服务实例
            Response<ServiceInstance> loadBalancerResponse = 
                (Response)Mono.from(loadBalancer.choose(request)).block();
            return loadBalancerResponse == null ? null : 
                (ServiceInstance)loadBalancerResponse.getServer();
        }
    }
}
相关推荐
不才不才不不才9 分钟前
Spring AI 实战:聊天、提示词、记忆三件套
java·人工智能·spring·ai
IT_陈寒2 小时前
Vite热更新失效?可能你在用Windows
前端·人工智能·后端
椰椰椰耶3 小时前
[SpringCloud][14]OpenFeign参数传递方法
后端·spring·spring cloud
onething3653 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 3 —— 消息表设计 + 级联删除 + 事务管理
人工智能·后端
荣江3 小时前
Hermes Agent 代码仓库打包工具使用指南(repomix-rs 高性能版)
后端
王某某人3 小时前
LangChain4j 入门:Java 程序员的第一个 AI 对话程序
人工智能·后端
码农刚子3 小时前
从零开始:在 Windows 服务器上部署 Node.js 项目(小白实战教程)
后端·node.js
Cache技术分享3 小时前
435. Java 日期时间 API - Clock 灵活获取当前时间
前端·后端
浩子coding3 小时前
通过 Spring AI Alibaba 源码,看如何玩转 ReAct 智能体范式
人工智能·后端
星浩AI4 小时前
合规项目大模型如何部署?硬件选型 + vLLM/LMDeploy 实战
pytorch·后端·llm