目录
1.负载均衡介绍
1.1问题描述
1.2什么是负载均衡
1.3负载均衡的一些实现
服务端负载均衡
客户端负载均衡
2.Spring Cloud LoadBalancer
2.1快速上手实现负载均衡
2.2负载均衡策略
自定义负载均衡策略
3.服务部署(Linux)
3.1服务构建打包
3.2启动服务
承接上文服务注册/服务发现-Eureka
1.负载均衡介绍
1.1问题描述
观察上个章节远程调用的代码
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
//服务可能有多个, 获取第⼀个
EurekaServiceInstance instance = (EurekaServiceInstance) instances.get(0);
1.根据应用名称获取了服务实例列表
2.从列表中选择了一个服务实例
思考:如果一个服务对应多个实例呢?流量是否可以合理的分配到多个实例
现象观察:
我们再启动2个product-service实例
选中要启动的服务,右键选择Copy Configuration
在弹出的框中选择 Configuration -> VM options
添加VM options: -Dserver.port=9091
9091为服务启动的端口号,根据自己的情况进行修改
11:46:05.684+08:00 INFO 23128 --- [nio-8080-exec-1]
com.bite.order.service.OrderService : LUCF:product-service:9090
11:46:06.435+08:00 INFO 23128 --- [nio-8080-exec-2]
com.bite.order.service.OrderService : LUCF:product-service:9090
11:46:07.081+08:00 INFO 23128 --- [nio-8080-exec-3]
com.bite.order.service.OrderService : LUCF:product-service:9090
先启动Eureka后启动所有实例
观察Eureka,可以看到product-service下有三个实例
访问:http://127.0.0.1:8080/order/1
访问结果:
11:46:05.684+08:00 INFO 23128 --- [nio-8080-exec-1]
com.bite.order.service.OrderService : LUCF:product-service:9090
11:46:06.435+08:00 INFO 23128 --- [nio-8080-exec-2]
com.bite.order.service.OrderService : LUCF:product-service:9090
11:46:07.081+08:00 INFO 23128 --- [nio-8080-exec-3]
com.bite.order.service.OrderService : LUCF:product-service:9090
通过日志可以观察到,请求多次访问,都是同一台机器。
这肯定不是我们想要的结果,启动多个实例,是希望可以分担其他机器的负荷,那么如何实现呢?
解决方案:
我们可以对上述代码进行简单修改:
import com.example.orderservice.mapper.OrderMapper;
import com.example.orderservice.model.OrderInfo;
import com.example.orderservice.model.ProductInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
private static AtomicInteger atomicInteger = new AtomicInteger(1);
public OrderInfo selectOrderById(Integer orderId){
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
// String url = "http://127.0.0.1:9090/product/"+orderInfo.getProductId();
//从Eureka中获取服务列表
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
String uri = instances.get(atomicInteger.getAndIncrement() % instances.size()).getUri().toString();
String url = uri+"/product/"+orderInfo.getProductId();
log.info("远程调用url:{}", url);
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
观察日志:
12:02:13.245+08:00 INFO 1800 --- [nio-8080-exec-1]
com.bite.order.service.OrderService : LUCF:product-service:9091
12:02:15.723+08:00 INFO 1800 --- [nio-8080-exec-2]
com.bite.order.service.OrderService : LUCF:product-service:9090
12:02:16.534+08:00 INFO 1800 --- [nio-8080-exec-3]
com.bite.order.service.OrderService : LUCF:product-service:9092
12:02:16.864+08:00 INFO 1800 --- [nio-8080-exec-4]
com.bite.order.service.OrderService : LUCF:product-service:9091
12:02:17.078+08:00 INFO 1800 --- [nio-8080-exec-5]
com.bite.order.service.OrderService : LUCF:product-service:9090
12:02:17.260+08:00 INFO 1800 --- [nio-8080-exec-6]
com.bite.order.service.OrderService : LUCF:product-service:9092
12:02:17.431+08:00 INFO 1800 --- [nio-8080-exec-7]
com.bite.order.service.OrderService : LUCF:product-service:9091
通过日志可以看到,请求被均衡的分配在不同的实例上,这就是负载均衡
1.2什么是负载均衡
负载均衡(Load Balance,简称LB),是高并发,高可用系统必不可少的关键组件.
当服务流量增大时,通常会采用增加机器的方式进行扩容,负载均衡就是用来在多个机器或者其他资源中,按照一定的规则合理分配负载.
一个团队最开始只有一个人,后来随着工作量的增加,公司又招聘了几个人.负载均衡就是:如何把工作量均衡的分配到这几个人身上,以提高整个团队的效率
1.3负载均衡的一些实现
上面的例子中,我们只是简单的对实例进行了轮询,但真实的业务场景会更加复杂.比如根据机器的配置进行负载分配,配置高的分配的流量高,配置低的分配流量低等. 类似企业员工:能力强的员工可以多承担一些工作。
服务多机部署时,开发人员都需要考虑负载均衡的实现,所以也出现了一些负载均衡器,来帮助我们实现负载均衡.
负载均衡分为服务端负载均衡和客户端负载均衡.
服务端负载均衡
在服务端进行负载均衡的算法分配
比较有名的服务端负载均衡器是Nginx.
请求先到达Nginx负载均衡器,然后通过负载均衡算法,在多个服务器之间选择一个进行访问.
客户端负载均衡
在客户端进行负载均衡的算法分配.
把负载均衡的功能以库的方式集成到客户端,而不再是由一台指定的负载均衡设备集中提供.
2.Spring Cloud LoadBalancer
2.1快速上手实现负载均衡
1.给RestTemplate这个Bean添加@LoadBalanced注解
@Configuration
public class BeanConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.修改IP端口号为服务名称
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;
}
3.启动多个product-service实例
2.2负载均衡策略
负载均衡策略是一种思想,无论是哪种负载均衡器,它们的负载均衡策略都是相似的.
Spring Cloud LoadBalancer仅支持两种负载均衡策略:轮询策略和随机策略
- 轮询(Round Robin):轮询策略是指服务器轮流处理用户的请求.这是一种实现最简单,也最常用的 策略.生活中也有类似的场景,比如学校轮流值日,或者轮流打扫卫生.
- 随机选择(Random):随机选择策略是指随机选择一个后端服务器来处理新的请求.
自定义负载均衡策略
Spring Cloud LoadBalancer默认负载均衡策略是轮询策略,实现是RoundRobinLoadBalancer,如果服务的消费者如果想采用随机的负载均衡策略,也非常简单.
参考官网:Spring Cloud LoadBalancer :: Spring Cloud Commons
此处使⽤Spring Cloud LoadBalancer提供的 RandomLoadBalancer
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
注意:该类需要满足
- 不用@Configuration注释
- 在组件扫描范围内
@LoadBalancerClient(name = "product-service",configuration = CustomLoadBalancerConfiguration.class)
@Configuration
public class BeanConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@LoadBalancerClient注解说明:
- name:该负载均衡策略对哪个服务生效(服务提供方)
- configuration:该负载均衡策略用哪个负载均衡策略实现.
3.服务部署(Linux)
3.1服务构建打包
采用Maven打包,需要对3个服务分别打包:
eureka-server,order-service,product-service
1.打包方式和SpringBoot项目一致,依次对三个项目打包即可.
3.2启动服务
#后台启动eureka-server, 并设置输出⽇志到logs/eureka.log
nohup java -jar eureka-server.jar >logs/eureka.log &
#后台启动order-service, 并设置输出⽇志到logs/order.log
nohup java -jar order-service.jar >logs/order.log &
#后台启动product-service, 并设置输出⽇志到logs/order.log
nohup java -jar product-service.jar >logs/product-9090.log &
#启动实例, 指定端⼝号为9091
nohup java -jar product-service.jar --server.port=9091 >logs/product-9091.log &
#启动实例, 指定端⼝号为9092
nohup java -jar product-service.jar --server.port=9092 >logs/product-9092.log &