一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情
Ribbon 原理初探
决心是成功的开始 Ribbon 相关文章
图解+源码讲解 Ribbon 如何获取注册中心的实例
图解+源码讲解 Ribbon 原理初探
图解+源码讲解 Ribbon 服务列表更新
图解+源码讲解 Ribbon 服务选择原理
Ribbon 原理初探
eureka 相关文章
eureka-server 项目结构分析
图解+源码讲解 Eureka Server 启动流程分析
图解+源码讲解 Eureka Client 启动流程分析
图解+源码讲解 Eureka Server 注册表缓存逻辑
图解+源码讲解 Eureka Client 拉取注册表流程
图解+源码讲解 Eureka Client 服务注册流程
图解+源码讲解 Eureka Client 心跳机制流程
图解+源码讲解 Eureka Client 下线流程分析
图解+源码讲解 Eureka Server 服务剔除逻辑
图解+源码讲解 Eureka Server 集群注册表同步机制
Ribbon 使用方法
Ribbon 的使用方法是在 RestTemplate 的方法上面加上一个 **@LoadBalanced **注解,之后这个方法进行请求的时候就可以进行负载均衡请求了
@Configuration
public class RestConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
负载均衡原理图解
RestTemplate 方法使用
如果不用 Ribbon 的话,在进行 http 请求的时候,直接 new 一个 RestTemplate 对象,通过相应的方法比如 getForEntity() 进行调用就可以了,这种情况直接请求就可以了,不会进行负载均衡的
@Override
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType,
Object... uriVariables)
throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor =
responseEntityExtractor(responseType);
// 执行请求
return nonNull(execute(url, HttpMethod.GET, requestCallback,
responseExtractor, uriVariables));
}
@LoadBalanced 注解源码
/**
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
看注释就是显示这个注解使用在 RestTemplate 类上面去配置 LoadBalancerClient ,我们去找找 LoadBalancerClient 这个接口看看在哪个包里面 package org.springframework.cloud.client.loadbalancer;
public interface LoadBalancerClient extends ServiceInstanceChooser {
// 执行负载均衡器指定的服务请求
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
// 执行负载均衡器指定的服务请求
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
// 创建一个具有真实主机和端口的适当URI,供系统使用。
// 一些系统使用带有逻辑服务名称的URI作为主机,
// 例如 http://myservice/path/to/service
// 这将用ServiceInstan中的主机:端口替换服务名称
URI reconstructURI(ServiceInstance instance, URI original);
}
/**
* 通过负载均衡器选择一个服务实例
*/
public interface ServiceInstanceChooser {
ServiceInstance choose(String serviceId);
}
Ribbon客户端配置类RibbonClientConfiguration
public class RibbonClientConfiguration {
// 客户端链接超时
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
// 客户端读取超时
public static final int DEFAULT_READ_TIMEOUT = 1000;
@RibbonClientName
private String name = "client";
@Autowired
private PropertiesFactory propertiesFactory;
......
}
1. RibbonClientConfig 客户端配置类
// Ribbon 客户端配置类
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
// 设置超时事件
config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
// 设置读取时间
config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
return config;
}
2. RibbonRule 规则
默认是轮询规则
// Ribbon 规则类
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, name)) {
return this.propertiesFactory.get(IRule.class, config, name);
}
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
// 初始化规则,默认是轮询规则
rule.initWithNiwsConfig(config);
return rule;
}
3. RibbonPing Ping方式
ping 服务节点是否好用
// Ribbon Ping类
@Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
if (this.propertiesFactory.isSet(IPing.class, name)) {
return this.propertiesFactory.get(IPing.class, config, name);
}
return new DummyPing();
}
4. Ribbon ribbonServerList 服务列表
// Ribbon 服务列表
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList<Server> ribbonServerList(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerList.class, name)) {
return this.propertiesFactory.get(ServerList.class, config, name);
}
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
5. RibbonServerListUpdater Ribbon 服务列表更新器
PollingServerListUpdater 服务列表主要操作类
// Ribbon 服务列表更新器,默认选择的是 PollingServerListUpdater 定时拉取
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
// 服务列表主要操作类
return new PollingServerListUpdater(config);
}
6. RibbonLoadBalancer 服务的负载均衡器
通过配置、服务列表、规则、ping机制以及服务列表更新器进行负载均衡创建
// Ribbon 服务的负载均衡器
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
7. RibbonServerListFilter 服务列表筛选器
// 服务列表筛选器
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
return this.propertiesFactory.get(ServerListFilter.class, config, name);
}
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.initWithNiwsConfig(config);
return filter;
}
负载均衡器初始化核心配置类
Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
// 负载均衡 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);
}
}
});
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
// 负载均衡拦截器配置
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
......
}
负载均衡拦截器 LoadBalancerInterceptor
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
// 解析服务地址
final URI originalUri = request.getURI();
// 请求服务名称
String serviceName = originalUri.getHost();
// 执行的请求流程
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
负载均衡器执行请求 loadBalancer.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);
}
// 创建 Ribbon 服务
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
// 真正的执行请求
return execute(serviceId, ribbonServer, request);
}
真正的执行请求
通过 LoadBalancerRequest 请求进行执行
@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 {
// 真正的请求方法
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
}
return null;
}
ServiceRequestWrapper 封装请求进行服务请求
@Override
public ListenableFuture<ClientHttpResponse> intercept(final HttpRequest request,
final byte[] body, final AsyncClientHttpRequestExecution execution)
throws IOException {
// 获取请求的路径URI
final URI originalUri = request.getURI();
// 获取服务的地址
String serviceName = originalUri.getHost();
return this.loadBalancer.execute(serviceName,
new LoadBalancerRequest<ListenableFuture<ClientHttpResponse>>() {
// 执行的方法
@Override
public ListenableFuture<ClientHttpResponse> apply(
final ServiceInstance instance) throws Exception {
// 封装请求
HttpRequest serviceRequest = new ServiceRequestWrapper(request,
instance, AsyncLoadBalancerInterceptor.this.loadBalancer);
// 进行服务调用执行访问
return execution.executeAsync(serviceRequest, body);
}
});
}
小结
其实就是通过拦截器将 RestTemplate 请求进行了封装,再将请求地址进行解析成对应可用的服务地址,再进行请求,不过这个请求的服务实例是通过负载均衡器进行选择出来的