多机部署,负载均衡-LoadBalancer

负载均衡介绍

负载均衡(LoadBalancer,简称LB),是高并发,高可用系统中必不可少的关键组件

当服务流量增大的时候,通常会采用增加机器的方式进行扩容,负载均衡就是用来在多个机器或者其他资源中,按照一定的规则合理分配负载

服务多机部署的时候,开发人员都需要考虑负载均衡的实现,所以也出现了一些负载均衡器,来帮助我们实现负载均衡

负载均衡分为服务端负载均衡和客户端负载均衡

服务端负载均衡

在服务端进行负载均衡的算法分配

比较有名的服务端负载均衡器是Nginx,请求先到达Nginx负载均衡器,然后通过负载均衡算法,在多个服务器之间选择一个进行访问

客户端负载均衡

在客户端进行负载均衡的算法分配

把负载均衡的功能以库的方式集成到客户端,而不是由一台指定的负载均衡设备集中提供

比如Spring Cloud中的Ribbon,请求发送到客户端,客户端从注册中心(比如Eureka)获取服务列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问

Ribbon是Spring Cloud 早期的默认实现,由于不维护了,所以最新版本的SpringCloud负载均衡集成的是SpringCloudBalancer(SpringCloud官方维护)

客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置

Spring Cloud LoadBalancer

快速上手

用法:1.添加注解 给RestTemplate这个Bean添加@LoadBalanced注解就可

2.修改远程调用代码,把IP和端口号修改为应用名

负载均衡策略

负载均衡策略是一种思想,无论是哪一种负载均衡器,他们的负载均衡策略都是相似的.SpringCloudLoadBalancer仅支持两种负载均衡策略:轮询策略和随机策略

1.轮询(Round Ribbon) :轮询策略是指服务器轮流处理用户的请求,这是一种最简单,也最常用的策略.(生活中也有类似的场景,比如学校轮流值日)

2.随机选择(Random):随机选择策略是指随机选择一个后端服务器来处理新的请求

自定义负载均衡策略

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

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

此处SpringCloudLoadBalancer提供的RandomLoadBalancer

2.使用LoadBalancerClient或者@LoadBalancerClients注解

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

@LoadBalancerClient注解说明:

1.name:该负载均衡策略对哪个服务生效(服务提供方)

2.configuration:该负载均衡策略,用哪个负载均衡策略实现

LoadBalancer原理

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

我们来看看源码实现:

复制代码
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

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

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

1.request.getURI() 从请求中获取uri,也就是http://product-

2.originalUri.getHost() 从uri中获取路径的主机名,也就是服务id,product-service

3.loadBalancer.execute 根据服务id,进行负载均衡,并处理请求

java 复制代码
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);
        }
    
java 复制代码
	//根据serviceId和负载均衡策略,选择处理的服务
	public <T> ServiceInstance choose(String serviceId, Request<T> request) {
		ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
		if (loadBalancer == null) {
			return null;
		}
		Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
		if (loadBalancerResponse == null) {
			return null;
		}
		return loadBalancerResponse.getServer();
	}
相关推荐
用户0328472220703 小时前
如何搭建本地yum源(上)
运维
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工3 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智3 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_3 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
慧一居士3 天前
Feign的GET请求如何传递对象参数?
java·spring cloud
施努卡机器视觉3 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造
AC赳赳老秦3 天前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw