Ribbon的应用
版本信息:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xiecongcong</groupId>
<artifactId>spring-cloud-ribbon-user-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-ribbon-user-service</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
</dependencies>
</project>
-
服务一:spring-cloud-ribbon-user-service
server.port=8080 spring-cloud-ribbon-order-service.ribbon.listOfServers=\ localhost:8081,localhost:8082
@RestController public class UserController { @Autowired RestTemplate restTemplate; @Autowired LoadBalancerClient loadBalancerClient; @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @RequestMapping("/user") @GetMapping public String getUser(){ ServiceInstance serviceInstance = loadBalancerClient.choose("spring-cloud-ribbon-order-service"); String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort()+"/order"); ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class); return "获得了User"+"\n"+forEntity.getBody(); } }
-
服务二:spring-cloud-ribbon-order-service
spring.application.name=spring-cloud-ribbon-order-service server.port=8081
@RestController public class OrderController { @Value("${server.port}") private Integer port; @RequestMapping("order") @GetMapping public String getOrder(){ System.out.println(this.port); return "获得Order"; } }
-
启动两个服务二,模拟集群
-
结果(默认负载均衡是轮询)
Ribbon的应用——注解方式
@RestController
public class UserController {
@Autowired
RestTemplate restTemplate;
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@RequestMapping("/user")
@GetMapping
public String getUser(){
return restTemplate.getForObject("http://spring-cloud-ribbon-order-service/order",String.class);
}
}
其他同上,结果一样
Ribbon都做了什么?
1.解析配置中的服务列表
2.基于负载均衡的算法实现请求的分发
Ribbon原理
-
初始化,自动装配拦截器。
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(RestTemplate.class) @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) //@LoadBalanced的实质是@Qualifier,这里会注入所有添加了@LoadBalanced注解的bean private List<RestTemplate> restTemplates = Collections.emptyList(); //给每一个RestTemplate包上一层RestTemplateCustomizer,这样才能为每一个RestTemplate去设置拦截器 @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } }); } @Configuration(proxyBeanMethods = false) @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class LoadBalancerInterceptorConfig { //初始化拦截器 @Bean public LoadBalancerInterceptor loadBalancerInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } //初始化RestTemplateCustomizer //作用是对加了@LoadBalanced注解的RestTemplate实例添加LoadBalancerInterceptor拦截器 @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } } }
-
拦截器拦截请求
1)发送请求,一定会进入拦截器中的intercept方法public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { //初始化的时候在LoadBalancerAutoConfiguration 这里注入 private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) { this.loadBalancer = loadBalancer; this.requestFactory = requestFactory; } public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) { // for backwards compatibility this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer)); } @Override //拦截请求 public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); //调用RibbonLoadBalancerClient的execute方法 return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); } }
2)调用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); }
3)获取负载均衡器
4)根据负载均衡算法获得具体应该访问的服务实例
5)url的重构,调用RibbonLoadBalancerClient的execute方法@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 { //request=ServiceRequestWrapper通过匿名内部类来构建 T returnVal = request.apply(serviceInstance); statsRecorder.recordStats(returnVal); return returnVal; } // catch IOException and rethrow so RestTemplate behaves correctly catch (IOException ex) { statsRecorder.recordStats(ex); throw ex; } catch (Exception ex) { statsRecorder.recordStats(ex); ReflectionUtils.rethrowRuntimeException(ex); } return null; }
T returnVal = request.apply(serviceInstance);实际调用的是LoadBalancerRequestFactory.createRequest中的匿名方法
public LoadBalancerRequest<ClientHttpResponse> createRequest( final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) { //实际调用的是这个方法 return instance -> { HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer); if (this.transformers != null) { for (LoadBalancerRequestTransformer transformer : this.transformers) { serviceRequest = transformer.transformRequest(serviceRequest, instance); } } return execution.execute(serviceRequest, body); }; }
然后execution.execute(serviceRequest, body)会进入到InterceptingClientHttpRequest的内部类InterceptingRequestExecution的execute方法中
class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest { private class InterceptingRequestExecution implements ClientHttpRequestExecution { @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); } else { HttpMethod method = request.getMethod(); Assert.state(method != null, "No standard HTTP method"); //ServiceRequestWrapper中的getURI()重构URI ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method); request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value)); if (body.length > 0) { if (delegate instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate; streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream)); } else { StreamUtils.copy(body, delegate.getBody()); } } return delegate.execute(); } } } }
OpenFeign的应用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
@FeignClient("spring-cloud-ribbon-order-service")
public interface OrderServiceFeignClient {
@GetMapping("/order")
String getOrder();
}
@RestController
public class OpenFeignController {
@Autowired
OrderServiceFeignClient orderServiceFeignClient;
@GetMapping("/user")
public String getUser(){
return orderServiceFeignClient.getOrder();
}
}
@EnableFeignClients
@SpringBootApplication
public class SpringCloudRibbonUserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudRibbonUserServiceApplication.class, args);
}
}
其他同上,结果同上。