自己做的记录,推荐看大神的Ribbon的负载均衡策略及原理
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
return (restTemplate) -> {
List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
//添加一个 LoadBalancerInterceptor 请求拦截器
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
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);
//调用 RibbonLoadBalancerClient 中的 execute() 方法
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
//1.首先获取一个 ILoadBalancer
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
//2.通过获取到的 ILoadBalancer 获取 Server
Server server = this.getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
//获取 Server 方法
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
//通过 loadBalancer 调用 chooseServer()
return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}
public Server chooseServer(Object key) {
if (this.counter == null) {
this.counter = this.createCounter();
}
this.counter.increment();
if (this.rule == null) {
return null;
} else {
try {
//通过
return this.rule.choose(key);
} catch (Exception var3) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});
return null;
}
}
}
以负载调用指定服务为例,Ribbon实现了 IRule 接口,该接口中提供了根据 key,操作在注册中心获取到的服务数据的方法,对于这些方法,Ribbon 根据算法不同,提供了具体实现,支持 轮询(默认),随机,一致性哈希,哈希,加权
注意点: 在修改负载策略时,需要创建配置类,配置类不可以放在 @ComponentScan 扫描自动注入的当前包或子包路径下(@SpringBootApplication注解底层也是使用的 @ComponentScan),否则所有的 Ribbon 都会使用当前配置的这个负载策略
假设服务消费方A,调用服务提供方"CLOUD-PAYMENT-SERVICE" (注册到注册中心的名字),将默认的轮询算法修改为随机算法
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySelfRule {
// RandomRule 随机负载,是IRule接口的实现类
@Bean
public IRule myRule(){
return new RandomRule();
}
}
import myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@SpringBootApplication
@EnableEurekaClient
//@EnableDiscoveryClient
//指定服务提供方"CLOUD-PAYMENT-SERVICE",使用 MySelfRule 配置类中配置的负载策略
@RibbonClient(name="CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}