什么是负载均衡
负载均衡(Load Balance,简称 LB),是⾼并发,⾼可⽤系统必不可少的关键组件.
当服务流量增⼤时,通常会采⽤增加机器的⽅式进⾏扩容,负载均衡就是⽤来在多个机器或者其他资源中,按照⼀定的规则合理分配负载.
负载均衡的⼀些实现
服务多机部署时,开发⼈员都需要考虑负载均衡的实现,所以也出现了⼀些负载均衡器,来帮助我们实现负载均衡.
负载均衡分为服务端负载均衡和客户端负载均衡.
服务端负载均衡
在服务端进⾏负载均衡的算法分配.⽐较有名的服务端负载均衡器是 Nginx.请求先到达 Nginx负载均衡器,然后通过负载均衡算法,在多个服 务器之间选择⼀个进⾏访问.
个人理解:服务端的负载均衡是在服务器前有一个负载均衡器获取所有的请求,并将请求按照负载均衡算法分发给各个服务器。
客户端负载均衡
在客户端进⾏负载均衡的算法分配.
把负载均衡的功能以库的⽅式集成到客户端,⽽不再是由⼀台指定的负载均衡设备集中提供.⽐如 Spring Cloud 的 Ribbon,客户端从注册中⼼(⽐如 Eureka )获取服务列表,在发送请求前通过负载均衡算法选择⼀个服务器,然后进⾏访问.Ribbon 是 Spring Cloud 早期的默认实现,由于不维护了, 所以最新版本的 Spring Cloud 负载均衡集成的是 Spring Cloud LoadBalancer(Spring Cloud 官⽅维护)
个人理解:相当于在请求从客户端发出前就通过负载均衡决定好了要将请求发送给哪个服务器
Spring Cloud LoadBalancer
SpringCloud 从 2020.0.1 版本开始,移除了 Ribbon 组件,使⽤Spring Cloud LoadBalancer 组件来代替 Ribbon 实现客户端负载均衡.
使⽤ Spring Cloud LoadBalancer 实现负载均衡
1.添加 @LoadBalanced 注解
给 RestTemplate 这个Bean添加 @LoadBalanced 注解就可以在发送 HTTP 请求时,获取服务列表,再根据负载均衡算法,将请求发送给选中的服务器
@Configuration
public class BeanConfig {@Bean//加上 @LoadBalanced 注解,RestTemplate 对象在发送请求给服务提供者时,如果服务提供者有多个,会进行负载均衡@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}
}
2.修改 IP 端⼝号为服务名称
加上 @LoadBalanced 注解后 RestTemplate 会根据服务名称去注册中心获取服务列表,并通过负载均衡算法选出其中的一个服务器,并将该服务器的 IP 地址和端口号替换掉服务名称
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public OrderInfo selectOrderById(Integer orderId){OrderInfo orderInfo = orderMapper.selectOrderById(orderId);String url="http://product-service/product/info/"+orderInfo.getProductId();//通过 restTemplate 发送 HTTP 请求到指定的接口,并将响应数据封装成对象ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}
负载均衡策略
负载均衡策略是⼀种思想,⽆论是哪种负载均衡器,它们的负载均衡策略都是相似的.Spring Cloud LoadBalancer 仅⽀持两种负载均衡策略:轮询策略和随机策略
1. 轮询(Round Robin):轮询策略是指服务器轮流处理⽤户的请求.这是⼀种实现最简单,也最常⽤的策略.
2. 随机选择(Random):随机选择策略是指随机选择⼀个后端服务器来处理新的请求.
Spring Cloud LoadBalancer默认负载均衡策略是轮询策略,实现是 RoundRobinLoadBalancer
可以通过如下代码不使用 LoadBalanced 简单实现负载均衡-轮询策略
@Service
@Slf4j
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;//注入 DiscoveryClient 对象,来从注册中心拉取服务列表@Autowiredprivate DiscoveryClient discoveryClient;实例化一个原子类的 Integer 对象记录请求次数private AtomicInteger atomicInteger=new AtomicInteger(1);public OrderInfo selectOrderById(Integer orderId){OrderInfo orderInfo = orderMapper.selectOrderById(orderId);从 eureka 注册中心获取服务列表List<ServiceInstance> instances = discoveryClient.getInstances("product-service");int index=atomicInteger.getAndIncrement()%instances.size();String uri = instances.get(0).getUri().toString();log.info(uri);String url=uri+"/product/info/"+orderInfo.getProductId();//通过 restTemplate 发送 HTTP 请求到指定的接口,并将响应数据封装成对象ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}
自定义负载均衡策略
修改负载均衡策略为随机策略
1. 定义随机算法对象,通过 @Bean 将其加载到 Spring 容器中
此处使⽤ Spring Cloud LoadBalancer 提供的 RandomLoadBalancer
public class LoadBalancerConfig{@BeanReactorLoadBalancer<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. 在组件扫描范围内
2.使⽤ @LoadBalancerClient 或者 @LoadBalancerClients 注解
在 RestTemplate 配置类上⽅,使⽤ @LoadBalancerClient 或 @LoadBalancerClients 注解,可以对不同的服务提供⽅配置不同的客户端负载均衡算法策略.
@LoadBalancerClient(name = "product-service", configuration =
LoadBalancerConfig.class)
@Configuration
public class BeanConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}
}
@LoadBalancerClient 注解说明
1. name:该负载均衡策略对哪个服务⽣效(服务提供⽅)
2. configuration:该负载均衡策略⽤哪个负载均衡策略实现.
服务部署(Linux)
服务构建打包
采⽤ Maven 打包,需要对3个服务分别打包: eureka-server,order-service,product-service
1. 打包⽅式和 SpringBoot 项⽬⼀致,依次对三个项⽬打包即可.
启动服务
1. 上传Jar包到云服务器
第⼀次上传需要安装 lrzsz
apt install lrzsz
直接拖动⽂件到xshell窗⼝,上传成功.
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 &
再多启动两台 product-service 实例
#启动实例, 指定端⼝号为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 &