当前位置: 首页 > news >正文

多机部署,负载均衡-LoadBalance

文章目录

  • 多机部署,负载均衡-LoadBalance
    • 1. 开启多个服务
    • 2. 什么是负载均衡
      • 负载均衡的实现
      • 客户端负载均衡
    • 3. Spring Cloud LoadBalance
      • 快速上手
        • 使用Spring Cloud LoadBalance实现负载均衡
        • 修改IP,端口号为服务名称
        • 启动多个服务
      • 负载均衡策略
        • 自定义负载均衡策略
      • LoadBalance原理

多机部署,负载均衡-LoadBalance

1. 开启多个服务

多复制两个服务,将服务全部启动起来

此时多次访问原来的接口,发现

调用的还是同一个

我们对代码进行修改:

@Service
public class OrderService {private static final Logger log = LoggerFactory.getLogger(OrderService.class);@Autowiredprivate OrderMapper orderMapper;@Resourceprivate DiscoveryClient discoveryClient;@Autowiredprivate RestTemplate restTemplate;private static AtomicInteger atomicInteger = new AtomicInteger(1);private static List<ServiceInstance> instances;@PostConstructpublic void init() {instances = discoveryClient.getInstances("product-service");}public  OrderInfo getOrderList(int orderId){OrderInfo orderInfo = orderMapper.getOrderList(orderId);int index = atomicInteger.getAndIncrement() % instances.size();EurekaServiceInstance instance = (EurekaServiceInstance) instances.get(index);String url = instance.getUri() + "/product/getProduct?productId=" + orderInfo.getProductId();log.info(url);ProductInfo productInfo = restTemplate.getForObject(url,ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}

通过轮询的方式去将请求均衡的分配到不同的实例上,这就是负载均衡

2. 什么是负载均衡

负载均衡是高并发,高可用系统必不可少的关键组件

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

负载均衡的实现

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

服务端负载均衡

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

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

客户端负载均衡

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

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

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

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

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

3. Spring Cloud LoadBalance

快速上手

使用Spring Cloud LoadBalance实现负载均衡

给RestTemplate这个Bean 添加 @LoadBalance注解

@Configuration
public class BeanConfig {@LoadBalanced@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
修改IP,端口号为服务名称
@Service
public class OrderService {private static final Logger log = LoggerFactory.getLogger(OrderService.class);@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public  OrderInfo getOrderList(int orderId){OrderInfo orderInfo = orderMapper.getOrderList(orderId);String url = "http://product-service/product/getProduct?productId=" + orderInfo.getProductId();log.info(url);ProductInfo productInfo = restTemplate.getForObject(url,ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}
启动多个服务

进行6次访问

为了方便观察,我们在product-service里面加上日志

此时进行访问

可以看到请求被均匀的分布在3个实例上

负载均衡策略

负载均衡策略是一种思想,无论是哪种负载均衡,他们的负载均衡策略都是相似的.Spring Cloud LoadBalance仅支持两种负载均衡策略:轮询策略以及随机策略

  1. 轮询:轮询策略指的是服务器轮流处理用户的请求.这是一种实现最简单,也最常用的策略
  2. 随机选择:随机选择策略是指随机选择一个后端服务器来处理新的请求
自定义负载均衡策略

Spring Cloud LoadBalance默认的负载均衡策略是轮询策略.实现是RoundRobinLoadBalance

如果想使用随机的负载均衡策略:

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

这里我们使用Spring Cloud LoadBalancer提供的RandomLoadBalance

public class LoadBalanceConfig {@BeanReactorLoadBalancer<ServiceInstance> randomLoadBalance(Environment env, LoadBalancerClientFactory clientFactory) {String name = env.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RandomLoadBalancer(clientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);}
}

注意:不用@Configuration注释,要求这个类在组件扫描范围内

  1. 使用@LoadBalancerClieent或者@LoadBalancerClients注解在RestTemplate配置类上方,就可以实现对不同的服务提供方配置不同的客户端负载均衡算法策略
@LoadBalancerClient(name = "product-service",configuration = LoadBalanceConfig.class)
@Configuration
public class BeanConfig {@LoadBalanced@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}

其中,name表示该负载均衡策略对哪个服务生效(服务提供方),Configuration表示负载均衡策略

LoadBalance原理

LoadBalance的实现,主要是LoadBalanceInterceptor,这个类会对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));}
}

主要就是看intercept方法,拦截了用户的请求后,做了以下几件事:

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

我们继续看execute方法

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);
}
}
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();}
}
http://www.lryc.cn/news/446440.html

相关文章:

  • Hadoop安装与配置
  • 一个自制的比较low的刷题软件
  • 【Java 集合】List接口 —— ArrayList 与 LinkedList 详解
  • 通信工程学习:什么是PNF物理网络功能
  • Unity的Text组件中实现输入内容的渐变色效果
  • network-scripts目录下没有ens33文件的问题
  • OpenHarmony(鸿蒙南向)——平台驱动指南【DAC】
  • 10.Lab Nine —— file system-下
  • 低代码中实现数据映射的必要性与方案
  • SpringBoot集成阿里easyexcel(一)基础导入导出
  • 四元组问题
  • 如何用Prometheus监控禁用了Actuator的SpringBoot?
  • 使用TensorFlow实现一个简单的神经网络:从入门到精通
  • 应用DFX能力介绍
  • 第三篇 第20章工程计价数字化与智能化
  • 成语700词(46~65组)
  • linux如何配置静态IP
  • Dependency Check:一款针对应用程序依赖组件的安全检测工具
  • Python 从入门到实战28(文件的读操作)
  • [leetcode刷题]面试经典150题之7同构字符串(简单)
  • 【Keil5教程及技巧】耗时一周精心整理万字全网最全Keil5(MDK-ARM)功能详细介绍【建议收藏-细细品尝】
  • 【rust】 基于rust编写wasm,实现markdown转换为html文本
  • Java中的反向代理与负载均衡:Nginx与Java服务的集成
  • 高级java每日一道面试题-2024年9月26日-运维篇[分布式篇]-如何保证每个服务器的时间都是同步的?
  • 探索MemGPT:AI界的新宠儿
  • 处理RabbitMQ连接和认证问题
  • FFmpeg中结构释放小函数
  • C语言中的一些小知识(三)
  • 编译win2k3中tools目录下i386mk.inc文件的作用
  • IPSec隧道协议学习(一)