1.背景
今天我们学习SpringCloud的客户端负载均衡Ribbon
我们今天继续使用之前eureka-server作为服务注册中心
使用Springboot和springcloud的版本如下
- springboot版本:2.3.5-release
- springcloud版本:Hoxton.SR9
2.源码解析
@LoadBalanced
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
RestTemplate
@RequestMapping("/api/comsumer/user")
@RestController
public class UserController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/{id}")
public String selectUser(@PathVariable("id") String id){
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://ms-ribbon-producer/api/user/{id}",String.class,id);
return responseEntity.getBody();
}
}
通过消费者,我们看到我们使用RestTemplate,这个如何使用ribbon的呢?
2.1分析RestTemplate
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
ClientHttpResponse response = null;
try {
//特别重要。创建请求
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
//特别重要。执行请求
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
finally {
if (response != null) {
response.close();
}
}
}
org.springframework.http.client.AbstractClientHttpRequest#execute
@Override
public final ClientHttpResponse execute() throws IOException {
assertNotExecuted();
////特别重要。执行请求
ClientHttpResponse result = executeInternal(this.headers);
this.executed = true;
return result;
}
org.springframework.http.client.AbstractBufferingClientHttpRequest#executeInternal(org.springframework.http.HttpHeaders
@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
byte[] bytes = this.bufferedOutput.toByteArray();
if (headers.getContentLength() < 0) {
headers.setContentLength(bytes.length);
}
////特别重要。执行请求
ClientHttpResponse result = executeInternal(headers, bytes);
this.bufferedOutput = new ByteArrayOutputStream(0);
return result;
}
org.springframework.http.client.InterceptingClientHttpRequest#executeInternal
@Override
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
////特别重要。执行请求
return requestExecution.execute(this, bufferedOutput);
}
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
////特别重要。执行拦截器
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
}
org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
//this.loadBalancer =RibbonLoadBalancerClient
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
//获取负载均衡器
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
//获取服务
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
//执行真正的服务调用
return execute(serviceId, ribbonServer, request);
}
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#getServer 获取对应的服务器
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
// Use 'default' on a null hint, or just pass it on?
//特别重要:通过负载均衡器来寻找对应的服务
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
com.netflix.loadbalancer.ZoneAwareLoadBalancer#chooseServer 选择对应的服务器
@Override
public Server chooseServer(Object key) {
//特别重要:基本都会走这个方法,寻找父类的选择服务的方法
if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
logger.debug("Zone aware logic disabled or there is only one zone");
return super.chooseServer(key);
}
}
com.netflix.loadbalancer.BaseLoadBalancer#chooseServer
选择对应的服务器的真正算法
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
if (rule == null) {
return null;
} else {
try {
//特别重要:通过负载均衡算法来选择服务
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonServer) {
server = ((RibbonServer) serviceInstance).getServer();
}
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
//特别重要:发起http请求
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
}
return null;
}
3.源码总结:
- RestTemPlate Spring自带的远程调用客户端
- @LoadBalanced 告知Spring使用负载均衡算法
- RestTemplateCustomizer RestTemPlate的定制器
- LoadBalancerInterceptor 负载均衡拦截器
- LoadBalancerClient 负载均衡客户端,实现类:RibbonLoadBalancerClient
- ILoadBalancer负载均衡器实现类:ZoneAwareLoadBalancer
- 负载均衡算法IRule 实现类:ZoneAvoidanceRule