一、 前言
前面阅读了命名容器工厂和定制化服务配置的Ribbon源码。到这里进入整个ribbon请求的流程解读,现在可以认为Spring子容器里只要我们想用什么组件,什么组件就已经存在了,并且是可配置的,比如ILoadBalancer、IRule(都是基于Ribbon自己的实现,和注册中心[Eureka、Connsul]无关)。 接下来我们需要关注的是这几点:
1、重点关注Ribbon是如何将serviceName经过负载均衡之后转换为ip:port
2、netflix-ribbon提供了ribbon的相关负载均衡实现,spring-cloud-common提供了负载均衡在SpringCloud中的抽象,这个抽象是如何让外部接入的,而spring-cloud-netflix-ribbon又是如何衔接两者的
接下来会从RestTemplate讲起,最后自己写一个类似的流程梳理一下。
二、RestTemplate
案例
- 注意RestTemplate必须被@LoadBalanced注解修饰才能进行负载均衡
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {Application.class, RestTemplateTest.RestTemplateTestConfiguration.class},
value = {"stock-service.ribbon.listOfServers=127.0.0.1:20002"})
@Slf4j
public class RestTemplateTest {
@Autowired
private RestTemplate restTemplate;
private static final String STOCK_URL = "http://stock-service/getStockByMpId";
@Test
public void testRestTemplate() throws JsonProcessingException {
ApiResponse response = restTemplate.getForObject(STOCK_URL + "/" + 1, ApiResponse.class);
log.info(new ObjectMapper().writeValueAsString(response));
}
static class RestTemplateTestConfiguration {
@Bean
@LoadBalanced
public RestTemplate testRestTemplate() {
return new RestTemplate();
}
}
}
最外层:org.springframework.web.client.RestTemplate#doExecute
restTemplate.getForObject最终走到RestTemplate的doExecute方法,这是RestTemplate的核心流程分为五步
// 1. RestTemplate抽象父类HttpAccessor.createRequest创建请求对象(重要)
ClientHttpRequest request = createRequest(url, method);
// 2. 对于get请求在http请求头中放入Accept=application/json(忽略)
requestCallback.doWithRequest(request);
// 3. 执行请求(重要)
response = request.execute();
// 4. 取RestTemplate#errorHandler解析返回结果是否发生异常
// 如果异常则构造不同异常抛出,默认实现类是DefaultResponseErrorHandler(忽略)
handleResponse(url, method, response);
// 5. 循环所有RestTemplate#messageConverters,判断是否能解析返回的数据,能解析则解析(忽略)
return responseExtractor != null ? responseExtractor.extractData(response) : null;
三、创建请求对象ClientHttpRequest[InterceptingClientHttpRequest]
RestTemplate抽象父类HttpAccessor.createRequest,最终会创建一个InterceptingClientHttpRequest,在RestTemplate主流程第三步执行execute方法
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
// 1. 创建RequestFactory
ClientHttpRequestFactory factory = getRequestFactory();
// 2. 创建ClientHttpRequest
ClientHttpRequest request = factory.createRequest(url, method);
// 3. 调用所有ClientHttpRequestInitializer
initialize(request);
return request;
}
1、创建RequestFactory[InterceptingClientHttpRequestFactory]
RestTemplate抽象父类InterceptingHttpAccessor#getRequestFactory获取请求工厂,关键点是interceptors包含了LoadBalancerInterceptor,而这个拦截器是在ioc容器构造RestTemplate的时候传入的,具体逻辑将在后续自己写的Ribbon请求流程中体现,这里只需要知道RestTemplate里有一个重要的拦截器。
public ClientHttpRequestFactory getRequestFactory() {
// 1. 获取RestTemplate里的拦截器列表,不为空创建InterceptingClientHttpRequestFactory
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
return factory;
}
else {
// 2. 如果拦截器为空,直接走SimpleClientHttpRequestFactory(如果配置了RestTemplate的requestFactory那就是自己配置的,默认是基于JDK的URLConnection的SimpleClientHttpRequestFactory),变成一个普通的http调用(忽略这段逻辑)
return super.getRequestFactory();
}
}
2、InterceptingClientHttpRequestFactory创建ClientHttpRequest[InterceptingClientHttpRequest]
InterceptingClientHttpRequestFactory.createRequest 把uri(原始的)、method、拦截器(LoadBalancerInterceptor)、请求工厂(SimpleClientHttpRequestFactory)包装在一起为一个InterceptingClientHttpRequest
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
3、初始化请求initialize(request)
循环调用ClientHttpRequestInitializer的initialize方法
private void initialize(ClientHttpRequest request) {
this.clientHttpRequestInitializers.forEach(initializer -> initializer.initialize(request));
}
ClientHttpRequestInitializer可以在restTemplate构造的时候传入,如
@Bean
@Primary
public RestTemplate testRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getClientHttpRequestInitializers().add(request -> {
log.info(request.getURI().toString());
});
return restTemplate;
}
四、request.execute()[InterceptingClientHttpRequest.execute]
一波调用链下来进到InterceptingClientHttpRequest的executeInternal方法,实际执行的是他的内部类InterceptingRequestExecution的execute方法。
这里最想知道的是:如何获取到需要调用的服务实例,将InterceptingClientHttpRequest转换为ServiceRequestWrapper再次进入这个execute方法
@Override
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) {
InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
return requestExecution.execute(this, bufferedOutput);
}
private class InterceptingRequestExecution implements ClientHttpRequestExecution {
private final Iterator<ClientHttpRequestInterceptor> iterator;
public InterceptingRequestExecution() {
this.iterator = interceptors.iterator();
}
// request = InterceptingClientHttpRequest
// 由刚才InterceptingClientHttpRequestFactory创建出来的ClientHttpRequest
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
// 第一次进来先走LoadBalancerInterceptor#intercept
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
// 第二次进来request变成了ServiceRequestWrapper
HttpMethod method = request.getMethod();
// 调用RestTemplate里的requestFactory(默认SimpleClientHttpRequestFactory)创建真正的http请求
// request.getURI()这一步真正替换了URI
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
// 把request的头部拷贝到真正的http请求里 省略
// 把request的body拷贝到真正的http请求里 省略
return delegate.execute();
}
}
}
1、一次进入InterceptingRequestExecution.execute
首次进入InterceptingRequestExecution.execute,走拦截器LoadBalancerInterceptor#intercept
request=InterceptingClientHttpRequest
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
// 1. LoadBalancerRequestFactory创建LoadBalancerRequest,这个request的apply方法是回到execute方法的入口
LoadBalancerRequest<ClientHttpResponse> lbRequest = requestFactory.createRequest(request, body, execution);
// 2. 执行RibbonLoadBalancer的execute方法,主要是使用负载均衡策略从服务列表中选出ServiceInstance
return this.loadBalancer.execute(serviceName,lbRequest);
}
1-1、LoadBalancerRequestFactory
LoadBalancerRequestFactory仅仅是为了创建LoadBalancerRequest,这里先当做直接new了个对象,apply方法也不会在这里调用
public LoadBalancerRequest<ClientHttpResponse> createRequest(
final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) {
return new LoadBalancerRequest<ClientHttpResponse>() {
@Override
public ClientHttpResponse apply(ServiceInstance instance) throws Exception {
// ... 先忽略
}
};
}
1-2、Ribbon接入层:RibbonLoadBalancerClient
RibbonLoadBalancerClient作为LoadBalancerClient的实现类,可以看做netflix-ribbon接入SpringCloud的入口。这里不会再跟进去看通过ILoadBalancer.chooseServer了,因为就是调用IRule获取服务,这些负载均衡组件都是由SpringClientFactory加载的,没啥可说的。
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) {
// 从SpringClientFactory获取ILoadBalancer
// 一般如果没有设置提前初始化ribbon(ribbon.eager-load.enabled),这里才会加载子容器
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
// 调用BaseLoadBalancer的chooseServer,使用IRule获取server
Server server = this.getServer(loadBalancer, hint);
// 构建ServiceInstance实例,一个普通的有getter/setter方法的对象
// 值得关注的是,它里面的Server用的是真实的通过负载均衡策略获取到的真实Server(包含真正的host:port)
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
// 带着转换后的ServiceInstance和request,继续execute
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
// ... 省略无关代码
// 执行LoadBalancerRequest<ClientHttpResponse>的apply方法
T returnVal = request.apply(serviceInstance);
return returnVal;
}
1-3、LoadBalancerRequest的apply方法
这个LoadBalancerRequest就是LoadBalancerRequestFactory的createRequest方法创建的。
public LoadBalancerRequest<ClientHttpResponse> createRequest(
final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) {
return new LoadBalancerRequest<ClientHttpResponse>() {
@Override
public ClientHttpResponse apply(ServiceInstance instance) throws Exception {
// ... 先忽略
}
};
}
- 通过代理模式,封装原始请求到ServiceRequestWrapper里,之后可以通过execution透传
- ServiceRequestWrapper主要作用就是getURI方法重写了,是通过loadBalancerClient(RibbonLoadBalancerClient)的reconstructURI方法重写了URI返回
- 调用所有LoadBalancerRequestTransformer,这是个扩展点
- 通过execution回到了InterceptingRequestExecution#execute
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);
2、二次进入InterceptingRequestExecution.execute
最后再回到execute(HttpRequest request, byte[] body),这个时候进来已经没有拦截器了,并且request被改为了ServiceRequestWrapper透传进来
// 第二次进来request变成了ServiceRequestWrapper
HttpMethod method = request.getMethod();
// 调用RestTemplate里的requestFactory(默认SimpleClientHttpRequestFactory)创建真正的http请求
// request.getURI()这一步真正替换了URI
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
// 把request的头部拷贝到真正的http请求里 省略
// 把request的body拷贝到真正的http请求里 省略
return delegate.execute();
这里request因为已经变成了ServiceRequestWrapper,而ServiceRequestWrapper里有LoadBalancerClient,可以重构URI,也有真正的服务实例ServiceInstance,可以获取host:port。
@Override
public URI getURI() {
// loadBalancer = RibbonLoadBalancerClient
return this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
}
// instance:RibbonLoadBalancerClient的execute方法中创建的
public URI reconstructURI(ServiceInstance instance, URI original) {
String serviceId = instance.getServiceId();
// 从SpringClientFactory获取RibbonLoadBalancerContext
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
URI uri;
Server server;
if (instance instanceof RibbonLoadBalancerClient.RibbonServer) {
RibbonLoadBalancerClient.RibbonServer ribbonServer = (RibbonLoadBalancerClient.RibbonServer)instance;
// 这里获取到的Server对象有真正的ip:port
server = ribbonServer.getServer();
uri = RibbonUtils.updateToSecureConnectionIfNeeded(original, ribbonServer);
}
// 用server里的host:port重构uri
return context.reconstructURIWithServer(server, uri);
}
五、自己实现
这里会利用前两章自己实现的命名容器工厂和自定义配置。自己实现的主要目的是屏蔽很多源码流程中的异常处理和空指针处理,这样更清晰。并且梳理一下哪些类是spring-cloud-common的抽象层,哪些是spring-cloud-netflix-ribbon的接入层。
1、工程结构
- Application:启动类,配置RestTemplate,展示一下两个扩展点
- 使用okhttp代替原来的SimpleClientHttpRequestFactory(基于JDK的URLConnection)
- ClientHttpRequestInitializer在构造ClientHttpRequest结束后,初始化ClientHttpRequest
@Bean
@MyLoadBalanced
@Primary
public RestTemplate myRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
// 请求工厂
restTemplate.setRequestFactory(new OkHttp3ClientHttpRequestFactory());
// 初始化
restTemplate.getClientHttpRequestInitializers().add(request -> {
request.getHeaders().add("FromClientHttpRequestInitializer", UUID.randomUUID().toString());
});
return restTemplate;
}
-
lb.mybbon:参照spring-cloud-netflix-ribbon,属于spring-cloud-common接入ribbon的一层
- contextFactory:源码阅读第一章实现的命名容器工厂
- specification:源码阅读第二章实现的客户端特殊配置
- MyLoadBalancerClient:参考RibbonLoadBalancerClient,接入spring-cloud-common的最外层
-
lb:spring-cloud-common中org.springframework.cloud.client.loadbalancer包下的一些实现,都是抽象逻辑层,让ribbon等外部组件可以接入
-
application.properties:
stock-service.myRibbon.listOfServers=127.0.0.1:20002
2、MyLoadBalancerAutoConfiguration和它的成员
MyLoadBalancerAutoConfiguration参照spring-cloud-common的spring.factories里自动配置的LoadBalancerAutoConfiguration,注入了org.springframework.cloud.client.loadbalancer包下的一些实现。只不过这里没放到spring.factories里,效果一样的。
@Configuration
public class MyLoadBalancerAutoConfiguration {
// 注入所有被@MyLoadBalanced注解修饰的RestTemplate
@MyLoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = new ArrayList<>();
// LB请求创建工厂
@Bean
public MyLBRequestFactory myLBRequestFactory(LoadBalancerClient loadBalancerClient) {
return new MyLBRequestFactory(loadBalancerClient);
}
// 拦截器
@Bean
public ClientHttpRequestInterceptor myLoadBalancerInterceptor(LoadBalancerClient loadBalancerClient,
MyLBRequestFactory myLBRequestFactory) {
return new MyLoadBalancerInterceptor(loadBalancerClient, myLBRequestFactory);
}
// RestTemplateCustomizer 定制化restTemplate
// 这里就是加了个lb拦截器
@Bean
public RestTemplateCustomizer myRestTemplateCustomizer(ClientHttpRequestInterceptor myLoadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(myLoadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
// Callback interface triggered at the end of the singleton pre-instantiation phase
// SpringBean生命周期相关,在单例bean提前初始化阶段被执行
@Bean
public SmartInitializingSingleton mySmartInitializingSingleton(RestTemplateCustomizer myRestTemplateCustomizer) {
return new SmartInitializingSingleton(){
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate : restTemplates) {
myRestTemplateCustomizer.customize(restTemplate);
}
}
};
}
}
2-1、MyLoadBalancerInterceptor
public class MyLoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private final LoadBalancerClient loadBalancerClient;
private final MyLBRequestFactory myLBRequestFactory;
public MyLoadBalancerInterceptor(LoadBalancerClient loadBalancerClient, MyLBRequestFactory myLBRequestFactory) {
this.loadBalancerClient = loadBalancerClient;
this.myLBRequestFactory = myLBRequestFactory;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
// 1. 获取服务名称
String serviceName = request.getURI().getHost();
// 2. 创建LBRequest
LoadBalancerRequest<ClientHttpResponse> lbRequest = myLBRequestFactory.createRequest(request, body, execution);
// 3. 执行loadBalancerClient.execute
return loadBalancerClient.execute(serviceName, lbRequest);
}
}
2-2、MyLBRequestFactory创建LoadBalancerRequest
public class MyLBRequestFactory {
private final LoadBalancerClient loadBalancerClient;
public MyLBRequestFactory(LoadBalancerClient loadBalancerClient) {
this.loadBalancerClient = loadBalancerClient;
}
public LoadBalancerRequest<ClientHttpResponse> createRequest(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) {
return new LoadBalancerRequest<ClientHttpResponse>() {
@Override
public ClientHttpResponse apply(ServiceInstance instance) throws Exception {
// 1. 制作一个代理对象,包装instance和原始request
MyRequestWrapper proxy = new MyRequestWrapper(request, loadBalancerClient, instance);
// 2. 回到org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution.execute
return execution.execute(proxy, body);
}
};
}
}
2-3、MyRequestWrapper代理原始请求
主要目的是getURI方法被重写,通过loadBalancerClient重构URI
public class MyRequestWrapper implements HttpRequest {
private HttpRequest request;
private LoadBalancerClient loadBalancerClient;
private ServiceInstance serviceInstance;
public MyRequestWrapper(HttpRequest request, LoadBalancerClient loadBalancerClient, ServiceInstance serviceInstance) {
this.request = request;
this.loadBalancerClient = loadBalancerClient;
this.serviceInstance = serviceInstance;
}
@Override
public String getMethodValue() {
return request.getMethodValue();
}
@Override
public URI getURI() {
return loadBalancerClient.reconstructURI(serviceInstance, request.getURI());
}
@Override
public HttpHeaders getHeaders() {
return request.getHeaders();
}
}
3、MyRibbonAutoConfiguration和它的成员
因为这里MyContextFactory和MyRibbonClientSpecification在前两章讲过了,这里不再展示了。
@Configuration
public class MyRibbonAutoConfiguration {
@Autowired(required = false)
private List<MyRibbonClientSpecification> configurations = new ArrayList<>();
@Bean
@ConditionalOnMissingBean
public MyContextFactory myContextFactory() {
MyContextFactory factory = new MyContextFactory();
factory.setConfigurations(configurations); // 对于每个服务的定制化配置
return factory;
}
@Bean
public MyLoadBalancerClient myLoadBalancerClient(MyContextFactory myContextFactory) {
return new MyLoadBalancerClient(myContextFactory);
}
}
3-1、MyLoadBalancerClient接入spring-cloud-common
public class MyLoadBalancerClient implements LoadBalancerClient {
private MyContextFactory myContextFactory;
public MyLoadBalancerClient(MyContextFactory myContextFactory) {
this.myContextFactory = myContextFactory;
}
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
ILoadBalancer loadBalancer = myContextFactory.getInstance(serviceId, ILoadBalancer.class);
Server server = loadBalancer.chooseServer(null);
ServiceInstance serviceInstance = new MyRibbonServer(serviceId, server);
return this.execute(serviceId, serviceInstance, request);
}
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
try {
return request.apply(serviceInstance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
String serviceId = instance.getServiceId();
RibbonLoadBalancerContext context = myContextFactory.getInstance(serviceId, RibbonLoadBalancerContext.class);
MyRibbonServer myRibbonServer = (MyRibbonServer) instance;
Server server = myRibbonServer.getServer();
return context.reconstructURIWithServer(server, original);
}
@Override
public ServiceInstance choose(String serviceId) {
ILoadBalancer loadBalancer = myContextFactory.getInstance(serviceId, ILoadBalancer.class);
Server server = loadBalancer.chooseServer(null);
return server == null ? null : new MyRibbonServer(serviceId, server);
}
}
六、总结
1、Ribbon是如何将serviceName经过负载均衡之后转换为ip:port
配置阶段:
LoadBalancerAutoConfiguration#loadBalancedRestTemplateInitializerDeprecated在Bean装配阶段给RestTemplate注入拦截器LoadBalancerInterceptor
运行阶段:
InterceptingClientHttpRequestFactory创建InterceptingClientHttpRequest->
InterceptingClientHttpRequest的executeInternal方法->
一次进入InterceptingRequestExecution.execute->
Ribbon接入层RibbonLoadBalancerClient的execute方法->
ILoadBalancer的chooseServer->
IRule获取服务实例->
封装ServiceInstance和LoadBalancerClient和原始request到ServiceRequestWrapper->
二次进入InterceptingRequestExecution.execute,这里request已经被换成ServiceRequestWrapper->
ServiceRequestWrapper.getURI通过LoadBalancerClient和ServiceInstance重构URI
2、netflix-ribbon、spring-cloud-common、spring-cloud-netflix-ribbon的关系
- netflix-ribbon:提供一些抽象概念和实现,比如:Server、ILoadBalancer、IRule等。最重要的是ILoadBalancer和他的实现(BaseLoadBalancer、DynamicServerListLoadBalancer)。
- spring-cloud-common:提供NamedContextFactory命名容器工厂,提供RestTemplate、LoadBalancerClient、LoadBalancerInterceptor方便外部负载均衡组件接入
- spring-cloud-netflix-ribbon:实现自己的NamedContextFactory(SpringCliengFactory)实现服务配置隔离。实现LoadBalancerClient(RibbonLoadBalancerClient)接入spring-cloud-common负载均衡流程
3、为什么说Feign内部使用了Ribbon,它到底用了什么,我大致看了一下
- 用了com.netflix.ribbon的jar包里的Ribbon的抽象和实现,比如ILoadBalancer抽象和它的实现BaseLoadBalancer
- 用了spring-cloud-netflix-ribbon的一些实现,比如SpringClientFactory命名工厂
- 一些实现都是和spring-cloud-netflix-ribbon类似的,比如FeignAutoConfiguration可以看到和spring-cloud-netflix-ribbon一样的服务定制化配置
@Autowired(required = false)
private List<FeignClientSpecification> configurations;
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}