-
实现负载均衡的两种方式
Load Balancing,即负载均衡, 是一种计算机技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。
实现负载均衡的两大方式:
一、服务端负载均衡:Nginx实现负载均衡
负载均衡是Nginx常用的一个功能,是在服务端通过的负载均衡算法实现的,Nginx也要具有很多不同的负载均衡策略。负载均衡的意思是将请求分摊到不同的服务器上执行,例如:web服务器、企业内部服务器等等,这样可以提高系统的吞吐量和请求的响应速度。
常见的负载均衡算法有三策略(这里只挑选三种来说明):
- 轮询(默认)
#每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除
upstream test {
server localhost:8080;
server localhost:8081;
}
server {
listen 8081;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
这里模拟了两台tomcat服务器,采用不同的端口8080、8081,当两台服务器之间有一个服务器处于不能访问的状态(服务器挂了),请求就不会打到该服务器,所有避免了一台服务器挂了而直接影响到整个系统业务不能正常使用的情况,由于Nginx默认是采用RR策略,所有不需要替他更多的配置。
2. 权重
#指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况upstream test {
server localhost:8080 weight=8;
server localhost:8081 weight=2;
}
- ip_hash
upstream test {
ip_hash;
server localhost:8080;
server localhost:8081;
}
二、客户端负载均衡:手写负载均衡算法实现负载均衡
假设现在有用户中心服务 user-center 和内容中心 content-center 服务,内容中心需要调用用户中心的端口服务,由于在并发较高的环境下,这是要在内容中心(客户端)实现一个负载均衡算法来实现调用user-center(服务端)服务,如何手写一个负载均衡算法来实现呢?
ShareController
@RestController
@RequestMapping("/shares")
public class ShareController {
@Autowired
private ShareService shareService;
@GetMapping("/{id}")
public ShareDTO findById(@PathVariable Integer id) {
return this.shareService.findById(id);
}
}
ShareServiceImpl
@Slf4j
@Service
public class ShareServiceImpl implements ShareService {
@Autowired
private ShareMapper shareMapper;
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Override
public ShareDTO findById(Integer id) {
//1.获取分享详情
Share share = shareMapper.selectByPrimaryKey(id);
//2.发布人的id
Integer userId = share.getUserId();
//拿到用户中心所有实例的信息
List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
//获取所有用户中心实例请求地址
List<String> targetUrls = instances.stream()
.map(instance -> instance.getUri().toString() + "/users/1")
.collect(Collectors.toList());
//设计随机算法,随机生成i
int i = ThreadLocalRandom.current().nextInt(targetUrls.size());
String targetUrl = targetUrls.get(i);
log.info("请求目标地址:{}", targetUrl);
//调用用户微服务的/users/{userId}?使用RestTemplate,RestTemplate会帮我们自动转换成UserDTO
UserDTO userDTO = this.restTemplate.getForObject(
"http://user-center/users/1",
UserDTO.class);
//3.消息的装配
ShareDTO shareDTO = new ShareDTO();
BeanUtils.copyProperties(share, shareDTO);
shareDTO.setWxNickname(userDTO.getWxNickname());
return shareDTO;
}
}
我们通过log打印出请求目标地址如下,我们可以清楚的看到分别打到8080、8081端口,实现了客户端的负载均衡:
三、使用Spring Cloud Ribbon实现负载均衡
Ribbon组成
Ribbon是比较灵活的,它对所有的组件值都提供了接口,如果你对这些值不满意,你可以基于Ribbon的接口自定义来实现。
接口 | 作用 | 默认值 |
IClientConfig | 服务配置 | DefaultClientConfigImpl |
IRule | 负载均衡规则,选择实例 | ZoneAvoidanceRule |
IPing | 筛选掉ping不通的实例 |
DumyPing (该类什么不干,认为每个实例都可用,都能ping通) |
ServerList | 交给Ribbon的实例列表 |
Ribbon:ConfigurationBasedServerList Spring Cloud Alibaba: NacosServerList |
ServerListFilter | 过滤掉不符合条件的实例 | ZonePreferenceServerListFilter |
ILoadBalancer | Ribbon负载均衡的入口 | ZoneAwareLoadBalancer |
ServerListUpdater | 更新交给Ribbon的List的策略 | PollingServerListUpdater |
Ribbon八大负载均衡规则介绍
规则名称 | 特点 |
---|---|
AvailabilityFilteringRule |
过滤掉一直连接失败的被标记为circuit tripped(电路跳闸)的后端Service,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤Server的逻辑,其实就是检查status的记录的各个Server的运行状态 |
BestAvailableRule |
选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过 |
RandomRule |
随机选择一个Server |
ResponseTimeWeightedRule |
已废弃,作用同WeightedResponseTimeRule |
RetryRule |
对选定的负责均衡策略机上充值机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的Server |
RoundRobinRule |
轮询选择,轮询index,选择index对应位置Server |
WeightedResponseTimeRule |
根据相应时间加权,相应时间越长,权重越小,被选中的可能性越低 |
ZoneAvoidanceRule |
(默认是这个)负责判断Server所Zone的性能和Server的可用性选择Server,在没有Zone的环境下,类似于轮询(RoundRobinRule ) |
引入Ribbon
配置在RestTemplate添加@LoadBalanced负载均衡注解,这时候会为RestTemplate整合Ribbon:
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
使用Ribbon我们不需要关注负载均衡算法的实现,因为Ribbon已经涵盖了丰富的负载均衡策略,我们只需要给Ribbon注册一个实例列表,Ribbon会自动帮我们选择具体的某个实例,引进Ribbon代码演进如下:
@Override
public ShareDTO findById(Integer id) {
//1.获取分享详情
Share share = shareMapper.selectByPrimaryKey(id);
//2.发布人的id
Integer userId = share.getUserId();
//调用用户微服务的/users/{userId}?使用RestTemplate,RestTemplate会帮我们自动转换成UserDTO
UserDTO userDTO = this.restTemplate.getForObject(
"http://user-center/users/1",
UserDTO.class);
//3.消息的装配
ShareDTO shareDTO = new ShareDTO();
BeanUtils.copyProperties(share, shareDTO);
shareDTO.setWxNickname(userDTO.getWxNickname());
return shareDTO;
}
引入Ribbon之后我们的代码简化了很多,这是后访问 http://localhost:8888/shares/1 也能得到结果,表示我们使用Ribbon实现实现了负载均衡:
出处:https://www.cnblogs.com/lhboke/p/15292135.html