当前位置:网站首页>Ribbon原理与Nacos的服务发现原理分析
Ribbon原理与Nacos的服务发现原理分析
2022-04-21 19:00:00 【小柒7】
Ribbon原理分析
Ribbon是使用拦截器来实现服务的远程调用的,源码如下:
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
@Bean
// 循环全部增强类去给restTemplate增强,也是在这里给它添加拦截器的
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(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
// 注册负载均衡拦截器,LoadBalancerClient 是执行正在服务在均衡的功能类
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
// 注册RestTemplate的增强类
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
// 添加拦截器
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
// 略......
进入LoadBalancerInterceptor的intercept可以看到,它是使用LoadBalancerClient去执行真正的流程的,LoadBalancerClient是负责调用流程的,具体的执行则是由ILoadBalancer负载均衡器来执行的
@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);
// 执行负载均衡
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
接着进入RibbonLoadBalancerClient的execute方法查看。
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
// 1、从容器中获取负载均衡器
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 2、使用ILoadBalancer获取一个服务
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));
// 3、调用这个服务
return execute(serviceId, ribbonServer, request);
}
服务的发现与负载均衡都发生在这3步中,我们着重分析这3步都干了什么
1、getLoadBalancer
从容器中获取负载均衡器,在RibbonClientConfiguration给容器注册了ZoneAwareLoadBalancer,所以实际获取的就是它。

接着看一个ZoneAwareLoadBalancer的构建过程做了什么,点进去会来到它的父类的DynamicServerListLoadBalancer构造方法,源码如下
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
// 调用上级构造,主要初始化一些配置信息
super(clientConfig, rule, ping);
// serverList服务列表,用于发现服务的
this.serverListImpl = serverList;
// 服务的过滤器
this.filter = filter;
// 服务列表的更新器
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
}
// 初始化,拉取服务和开启服务更新
restOfInit(clientConfig);
}
restOfInit方法源码
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
// 开启服务列表的监听
enableAndInitLearnNewServersFeature();
// 拉取服务
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
enableAndInitLearnNewServersFeature是开启一个定时任务拉取服务,调用的就是updateListOfServers,主要看updateListOfServers是如何拉取服务的,方法源码如下:
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
// 调用服务列表服务拉取并更新服务
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
if (filter != null) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
}
}
// 更新内存的服务列表信息
updateAllServerList(servers);
}
serverListImpl是负载均衡的规范接口,用于获取服务的,Nacos也是实现的这个接口给Ribbon获取的服务列表的
public interface ServerList<T extends Server> {
public List<T> getInitialListOfServers();
/** * Return updated list of servers. This is called say every 30 secs * (configurable) by the Loadbalancer's Ping cycle * */
public List<T> getUpdatedListOfServers();
}
NacosServerList的部分源码:
@Override
public List<NacosServer> getUpdatedListOfServers() {
return getServers();
}
private List<NacosServer> getServers() {
try {
String group = discoveryProperties.getGroup();
// 使用Nacos的NameingService发起Api调用获取服务的实例列表
List<Instance> instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, group, true);
return instancesToServerList(instances);
}
catch (Exception e) {
throw new IllegalStateException(
"Can not get service instances from nacos, serviceId=" + serviceId,
e);
}
}
到这也就知道服务的发现是怎样的了,剩下的就是根据负载策略选择一个服务去调用了。接着看下一步getServer
2、getServer
重新回到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");
}
因为默认的话是不会使用zone的所以会直接进入到BaseLoadBalancer的chooseServer方法,源码如下:
public Server chooseServer(Object key) {
// 创建计数器或者+1操作
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;
}
}
}
这一步使用负载策略选择一个服务服务了,至于Rule策略,我会在下一步文章分析,接着看下一步
3、execute
这一步就是调用服务了
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;
}
// 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;
}
Ribbon的负载与Nacos的服务发现流程到这就基本分析完了!!
版权声明
本文为[小柒7]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_38650898/article/details/117284669
边栏推荐
- APM 行业认知系列 - 三
- 解析创客教育的实践进程
- Tiandan released the cloud native intelligent operation and maintenance product line and launched the world's first full stack observability technical standard
- Apply El tooltip (bubble text prompt box) in El tabs
- 真无线耳机什么牌子好?高颜值旗舰蓝牙耳机
- ssh-keygen 设置免密登录
- Crystal Chem小鼠葡萄糖检测试剂盒说明书
- 编程中的Context(上下文)
- JVM class loading mechanism
- Channel Allocation 别用四色定理
猜你喜欢

leetcode:423. Reconstructing numbers from English

解析机器人智能推理规划

Semaphore and exchange of concurrent tools

el-tabs中套用el-tooltip(气泡文字提示框)

LeetCode1765. Highest point in the map (BFS)
![[04] [02] [02] SPI mechanism](/img/62/c9852086e7893d392ef407c294dd1e.png)
[04] [02] [02] SPI mechanism

Use the replay function of chrome to publish a blog quickly

体育场馆系统可以连接其他智能设备吗

Abbexa 山羊 F(ab‘)2 IgG 同种型对照

【王道考研3】OSI七层参考模型,TCP/IP参考模型和5层参考模型
随机推荐
What kind of Bluetooth headset is inexpensive and practical? Wireless Bluetooth headset for student party
[untitled]
【持续更新中】C#常见问题及其解决(VS2019)
How much can I get a month with a PMP certificate
预处理问题
Analytic robot intelligent reasoning planning
初等数学建模问题
Wide application of medical robot in AI field
Use the replay function of chrome to publish a blog quickly
SVG系列——1,入门级操作
2022.04.21(LC_452_用最少数量的箭引爆气球)
编程中的Context(上下文)
flutter xcode打包发布失败 Error.90165
APM 行业认知系列 - 三
On the significance of sustainable development of maker Education
2022.04.21(LC_435_无重叠区间)
Apache Doris创建动态分区
LeetCode1765. Highest point in the map (BFS)
Depth first search DFS (adjacency matrix and adjacency table version)
[Wangdao postgraduate entrance examination 3] OSI seven layer reference model, TCP / IP reference model and five layer reference model