负载均衡的两种方式
- 服务器端负载均衡
- 客户端侧负载均衡
实现一个简单的客户端侧负载均衡
- 使用DiscoveryClient的getInstances()方法,传入需要调用的服务名称,获取服务列表
- 根据服务列表获取url列表
- 随机一个url,通过RestTemplate调用服务
List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
List<String> urlList = instances.stream().map(serviceInstance ->
serviceInstance.getUri().toString() + "/xxx/xxx").collect(Collectors.toList());
int i = ThreadLocalRandom.current().nextInt(urlList.size());
使用Ribbon实现负载均衡
- 不需要添加依赖,spring-cloud-starter-alibaba-nacos-discovery已经包含依赖
- 添加注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
- 不需要添加配置
Ribbon组成
Ribbon内置负载均衡规则
Ribbon配置方式
细粒度配置
#服务名称
user-center:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
NFLoadBalancerPingClassName: com.netflix.loadbalancer.PingUrl
配置饿加载
ribbon:
eager-load:
enabled: true
clients: user-center,xxx,xxx
扩展Ribbon-支持Nacos权重
- 创建NacosWeightedRule类
package com.istimeless.contentcenter.configuration;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
/**
* @author lijiayin
*/
@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
//读取配置文件,并初始化
}
@Override
public Server choose(Object o) {
try {
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
//想要请求的微服务名称
String name = loadBalancer.getName();
//实现负载均衡算法
//拿到服务发现相关API
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
//nacos client自动通过基于权重的负载均衡算法,给我们选择一个实例。
Instance instance = namingService.selectOneHealthyInstance(name);
log.info("选择的实例是:port = {}, instance = {}", instance.getPort(), instance);
return new NacosServer(instance);
} catch (NacosException e) {
log.error("选择实例异常:{}", e.getMessage(), e);
return null;
}
}
}
- 创建RibbonConfiguration类
package com.istimeless.ribbonconfiguration;
import com.istimeless.contentcenter.configuration.NacosWeightedRule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author lijiayin
*/
@Configuration
public class RibbonConfiguration {
@Bean
public IRule ribbonRule() {
return new NacosWeightedRule();
}
}
特别注意:RibbonConfiguration要建在启动类扫描不到的地方,如图所示:
- 创建UserCenterRibbonConfiguration类,实现全局配置
package com.istimeless.contentcenter.configuration;
import com.istimeless.ribbonconfiguration.RibbonConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;
/**
* @author lijiayin
*/
@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {
}