SpringCloud父子容器源码浅析

15.springcloud_openfeign之FeignClientFactoryBean 在上一篇中我们介绍了通过注解扫描并注册每个标识的接口对应的到spring容器中, 本节我们来了解一下这个类的具体内容。本节主要介绍了springcloud_openfeign如何根据注解和以及父子容器来构建Feign.Builder对象, 并处理Target需要的url, 最后通过Feign.Builder#target构建feign接口的代理对象。本文没有具体介绍其中父子容器的相关细节, 以及配置的一些具体内容, 这些将会在后面的文章中介绍到通过@Import注解给spring容器注入了。 阅读详情

一. 前言

1. 相关文章

2. 说明

  • 本文SpringCloud源码解读使用的版本为2.2.7.RELEASE. 引入的依赖为 spring-cloud-context:2.2.7.RELEASE
  • 本文涉及的SpringBoot源码版本为2.3.9.RELEASE. 引入依赖 spring-boot-starter-web:2.3.9.RELEASE
  • 附上SpringCloud官方文档中文版地址: https://www.springcloud.cc/spring-reference.html
  • 附上SpringCloud官方文档地址: https://spring.p2hp.com/cloud.html

二. 源码浅析

1. 启动流程

在这里插入图片描述

在这里插入图片描述
简易流程
在这里插入图片描述

2. 进入源码

2.1 父容器创建入口

SpringApplication#prepareEnvironment, 准备环境完成后, 发布ApplicationEnvironmentPreparedEvent事件

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		//...
		// 准备环境完成, 发布ApplicationEnvironmentPreparedEvent事件
		listeners.environmentPrepared(environment);
		//...
	}

2.2 BootstrapApplicationListener监听事件处理

BootstrapApplicationListener这个监听器是在spring-cloud-context的META-INF.spring.factories中添加的. 在SpringApplication实例化时该bean就会添加到应用监听器列表中去

在这里插入图片描述
进入BootstrapApplicationListener#onApplicationEvent

    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        // 是否开启bootstrap父容器创建, 默认都是开启的
        if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) {
            return;
        }
        // 判断enviroment中是否有"bootstrap"配置源. 有该配置源则表示正在创建父容器, 直接结束避免死循环
        if (environment.getPropertySources().contains("bootstrap")) {
            return;
        }
        ConfigurableApplicationContext context = null;
        // 通过ParentContextApplicationContextInitializer获取父容器.
        String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
        for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) {
            if (initializer instanceof ParentContextApplicationContextInitializer) {
            	// 获取ParentContextApplicationContextInitializer中存储的父容器
                // 注: 正常启动时, debug发现这里永远不会执行, 因为ParentContextApplicationContextInitializer是手动new出来没有添加到容器中.
                //     源码位置BootstrapApplicationListener.AncestorInitializer#initialize
                context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer, configName);
            }
        }

        if (context == null) {
            // 创建父容器, 添加祖先监听器, 为Environment添加属性源
            context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
            // 为子SpringApplication添加一个应用监听器CloseContextOnFailureApplicationListener, 监听启动失败事件
            event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
        }

        // 为子SpringApplication添加主加载源和添加应用上下文监听器
        apply(context, event.getSpringApplication(), environment);
    }

2.3 创建父容器(核心代码)

核心的代码在BootstrapApplicationListener#bootstrapServiceContext中. 下面展开看下它做了什么

    private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment,
            final SpringApplication application, String configName) {
        // 创建父容器的Environment(实现类为StandardEnvironment), 清空所有属性源PropertySource
        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
        MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
        for (PropertySource<?> source : bootstrapProperties) {
            bootstrapProperties.remove(source.getName());
        }

        // 构建一个名为bootstrap的属性源, 添加到父容器的Environment
        String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
        String configAdditionalLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
        Map<String, Object> bootstrapMap = new HashMap<>();
        bootstrapMap.put("spring.config.name", configName);
        bootstrapMap.put("spring.main.web-application-type", "none");
        if (StringUtils.hasText(configLocation)) {
            bootstrapMap.put("spring.config.location", configLocation);
        }
        if (StringUtils.hasText(configAdditionalLocation)) {
            bootstrapMap.put("spring.config.additional-location", configAdditionalLocation);
        }
        bootstrapProperties.addFirst(new MapPropertySource("bootstrap", bootstrapMap));

        // 父Environment添加 子Environment中非StubPropertySource的属性源
        for (PropertySource<?> source : environment.getPropertySources()) {
            if (source instanceof StubPropertySource) {
                continue;
            }
            bootstrapProperties.addLast(source);
        }

        // 构建父SpringApplication
        SpringApplicationBuilder builder = new SpringApplicationBuilder()
                .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
                .environment(bootstrapEnvironment)
                .registerShutdownHook(false).logStartupInfo(false)
                .web(WebApplicationType.NONE);
        final SpringApplication builderApplication = builder.application();

        // 正常启动不执行代码块. main方法启动类是SpringApplication创建时可以从堆栈中获取到的
        if (builderApplication.getMainApplicationClass() == null) {
            builder.main(application.getMainApplicationClass());
        }
        // 正常启动不执行代码块. 若子environment含有refreshArgs属性源则过滤掉日志相关的监听器
        if (environment.getPropertySources().contains("refreshArgs")) {
            builderApplication.setListeners(filterListeners(builderApplication.getListeners()));
        }

        // 添加加载源, 在build()时会将该加载源作为父SpringApplication唯一的primarySources
        builder.sources(BootstrapImportSelectorConfiguration.class);
        // 执行父SpringApplication的run方法, 获取到父容器ConfigurableApplicationContext
        final ConfigurableApplicationContext context = builder.run();

        // 父容器命名为bootstrap
        context.setId("bootstrap");
        // 给子SpringApplication添加一个祖先初始化器AncestorInitializer.
        // AncestorInitializer作用: 暂存了父容器对象, 待后续创建出子容器后进行父子容器绑定
        addAncestorInitializer(application, context);

        // 移除父容器environment中的bootstrap的属性源
        bootstrapProperties.remove("bootstrap");
        // 为父子environment都添加同一个名为springCloudDefaultProperties空的属性源
        mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
        return context;
    }

这里实际上就做了以下几件事

  • 创建父Environment和父SpringApplication, 执行run方法后创建出父容器
  • 为子SpringApplication添加祖先监听器, 以便后续创建出子容器后 完成父子容器绑定
  • 为父子SpringApplication添加一个公共的名为springCloudDefaultProperties空的属性源

这里提到了一个祖先监听器AncestorInitializer, 我们简单看下它的代码

    private static class AncestorInitializer implements
            ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
        
        private ConfigurableApplicationContext parent;
        AncestorInitializer(ConfigurableApplicationContext parent) {
            this.parent = parent;
        }

        //...

        public void initialize(ConfigurableApplicationContext context) {
            while (context.getParent() != null && context.getParent() != context) {
                context = (ConfigurableApplicationContext) context.getParent();
            }
            // 重排子Environment中springCloudDefaultProperties属性源顺序
            reorderSources(context.getEnvironment());
            // 绑定父子容器关系. 等价于context.setParent(this.parent)
            new ParentContextApplicationContextInitializer(this.parent)
                    .initialize(context);
        }
        //...
    }

	// ParentContextApplicationContextInitializer#initialize代码如下
	private final ApplicationContext parent;
	public void initialize(ConfigurableApplicationContext applicationContext) {
		if (applicationContext != this.parent) {
			// 绑定父子容器
			applicationContext.setParent(this.parent);
			// 为子容器添加EventPublisher监听器
			applicationContext.addApplicationListener(EventPublisher.INSTANCE);
		}
	}

AncestorInitializer监听器是加到子SpringApplication中, 当子容器创建完成调用SpringApplication#prepareContext方法时, 就会执行到该祖先初始化器的initialize方法, 最终执行到ParentContextApplicationContextInitializer#initialize, 完成父子容器的绑定

注: 这里的ParentContextApplicationContextInitializer仅仅是为了调用初始化方法, 并没有将该初始化器的bean放入容器中

2.4 执行BootstrapApplicationListener#apply

为子SpringApplication添加主加载源和初始化器

    private void apply(ConfigurableApplicationContext context,
                       SpringApplication application, ConfigurableEnvironment environment) {
        if (application.getAllSources().contains(BootstrapMarkerConfiguration.class)) {
            return;
        }
        // 为子SpringApplication添加一个主加载源BootstrapMarkerConfiguration
        application.addPrimarySources(Arrays.asList(BootstrapMarkerConfiguration.class));

        // 为子SpringApplication添加所有类型为ApplicationContextInitializer的初始化器
        Set target = new LinkedHashSet<>(application.getInitializers());
        target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
        application.setInitializers(target);
        
        // 为子SpringApplication添加初始化器DelegatingEnvironmentDecryptApplicationInitializer
        addBootstrapDecryptInitializer(application);
    }


    private void addBootstrapDecryptInitializer(SpringApplication application) {
        DelegatingEnvironmentDecryptApplicationInitializer decrypter = null;
        Set<ApplicationContextInitializer<?>> initializers = new LinkedHashSet<>();
        for (ApplicationContextInitializer<?> ini : application.getInitializers()) {
            if (ini instanceof EnvironmentDecryptApplicationInitializer) {
                ApplicationContextInitializer del = ini;
                decrypter = new DelegatingEnvironmentDecryptApplicationInitializer(del);
                initializers.add(ini);
                // 若子SpringApplication含有EnvironmentDecryptApplicationInitializer初始化器,
                // 则再添加一个对应的DelegatingEnvironmentDecryptApplicationInitializer
                initializers.add(decrypter);
            } else if (ini instanceof DelegatingEnvironmentDecryptApplicationInitializer) {
                // do nothing
            } else {
                initializers.add(ini);
            }
        }
        ArrayList<ApplicationContextInitializer<?>> target = new ArrayList<ApplicationContextInitializer<?>>(
                initializers);
        application.setInitializers(target);
    }

三. 拓展

暂无

四. 思考

1. 为什么SpringCloud使用父子容器这种模式? 父子容器的作用是什么?

以下仅为个人理解

  • SpringBoot创建的容器(IOC容器)不适合SpringCloud框架代码的执行.
  • SpringCloud容器和SpringBoot容器相互隔离, 以免父容器启动时修改到子容器的关键属性. 例如两者所需的Environment和PropertySource是不一样的, SpringCloud加载配置文件和SpringBoot也不完全相同
  • SpringCloud和SpringBoot启动所需条件是不一样的. 例如SpringCloud以非servlet和非reactive的应用类型启动.

2. 使用nacos配置中心时, 若启动完成后, 去更改配置文件, 会重新触发Spring启动, 那此时父子容器创建和启动时有什么不同呢?

五. 参考文档

  • 聊聊SpringCloud环境下父子容器: https://blog.youkuaiyun.com/jackcheng1117/article/details/105844597
STM32F407 CanOpen主机实战:对象字典配置与多电机CIA402协议驱动详解 本文详细介绍了基于STM32F407的CanOpen主机开发实战,重点解析了对象字典的配置流程与多电机CIA402协议驱动方法。通过使用objdictedit工具配置SDO和PDO参数,并整合CanFestival协议栈,实现了对多个伺服电机的稳定同步控制。文章提供了从硬件连接到代码调试的完整指南,帮助开发者快速掌握工业现场的多轴运动控制技术。 阅读详情

相关推荐

双目视觉三维重构(一)————简介

优快云中关于立三维重构的介绍层出不穷,CNKI中也有各类综述对三维重构进行总结,撰写这篇博客仅作为本人对该类博客、论文的总结学习,加深自身学习的印象、作为学习的笔记。如有错误的地方,欢迎指正。   1、三维重构 1.1、三维重构到底是什么?   首先要了解立体匹配算法,首先要知道立体匹配算法主要具体应用于什么方向。 三维重构又被称为三维重建,百度百科中对它的定义为:指对三维物体建立适合...

qq_39634612的博客 1万+

SpringCloud 中的父子容器

中,提到了 SpringBoot 在启动时,会触发相关一系列监听器,监听器各司其职,做一些初始化预处理操作。SpringBoot创建的 Spring容器是最核心的容器,也是使用最多的Spring容器。创建的对象会有3种类型,Servlet,Reactive,和默认。如果引入了 FeignClient,则会实例化一个容器工厂,以服务名称为key,value为Feign 和 Ribbon配置类容器,配置隔离,父容器则都为。对象,再次执行run方法,所以启动流程会执行两遍,只是读取的配置文件和配置类不同。

meser88的博客 588

历年北京理工大学复试专业综合题

。

A place to record sth. 2万+

聊聊 SpringCloud 中的父子容器

来源公号:吉姆餐厅ak 概述 在引入 SpringCloud 的项目中会多次创建 Spring 容器,本篇从源码角度深入分析具体哪些点会创建 Spring 容器,以及这些容器之间的区别与联系。 本文介绍的Spring相关容器是基于 SpringCloud Finchley.RELEASE 版本。 容器大致分为三层,分别对应上面的三类: BootStrap...

方志朋的专栏 1万+

Python全栈项目--手机语音远程控制智能家居系统(含嵌入式控制)

摘要:本项目实现了一个基于Python全栈的智能家居语音控制系统,支持手机端语音指令控制家居设备。系统采用FastAPI后端+Vue3前端的架构,集成Web Speech API实现语音识别,通过嵌入式设备(如ESP32)执行硬件控制。功能包括用户认证、设备管理、场景联动、指令解析和日志记录,支持HTTP轮询和直推两种命令下发模式。项目涵盖物联网全链路开发,适用于课程设计或毕业设计原型开发。

exlink2012的专栏 877

手把手教你学Simulink--基于飞轮储能系统的场景实例:飞轮储能与锂电池混合储能系统的协同控制

本文详细介绍了基于Simulink的飞轮储能与锂电池混合储能系统协同控制建模方法。通过高通/低通滤波器实现功率分配,使飞轮处理高频波动(±100kW),锂电池承担削峰填谷(50kW)。仿真结果表明,该系统效率达88%,锂电池循环次数减少90%,投资成本比纯电池方案节省200万美元。文章包含完整的建模步骤、控制策略设计和多场景仿真分析,为构建高效、长寿、经济的智能储能系统提供了实用解决方案。

MHD0815的博客 341

spring源码系列:SpringCloud中的父子容器&容器启动流程源码解析(一)

作者寄语 今天我们来聊一下Spring的启动流程 以及SpringCloud的父子容器的初始化过程。这个在平时工作中面试中也是出现频率比较高的知识点。特别是SpringCloud的父子容器的概念,如果你的项目中引入了Feign和Ribbon组件 却不知道SpringCloud底层是怎么管理的 在实际开发中排查问题是非常困难的。 我们分4篇文章来一探究竟。 容器概述 在微服务的环境中 我们说的IOC容器实际上有三层意思:bootstrap容器,Spring容器,微服务组件容器,如下图所示 bootst

xiaomujiang_dafangzi的博客 1025

超声波雷达探测车位及信号处理方法

在APA泊车中用到了超声波雷达,第一次接触,总结研究之。

周陽讀書 1229

Spring Cloud之BootstrapApplicationListener巧妙构建双容器

spring cloud 双容器启动机制分析

万物皆字节 1460

抑制电磁干扰(EMI)的实用电路技术

抑制电磁干扰(EMI)的实用电路技术 qtFK_EMC_EMI 【导读】本系列文章的第 1 部分至第 4 部分详细介绍了开关电源稳压器引起的传导发射和辐射发射,包括噪声产生机制、测量要求、频率范围、适用的测试限值、传播模式和寄生效应。在第 5 部分中,我将基于这一理论基础介绍抑制电磁干扰 (EMI) 的实用电路技术。 一般来说,电路原理图和印刷电路板 (PCB) 对于实现出色的 EMI 性能至关重要。第 3 部分重点强调通过谨慎的元器件选型和 PCB 布局尽量减小“功率回路”寄生电感的重要性。电源转换

学海无涯的专栏 1187

滑块逆向及流程分析

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

pyzzd的博客 8005

Spring Cloud父子容器及容器的启动源码

接触过Spring Cloud都知道,服务启动的时候会先启动Spring Cloud容器加载bootstrap.yml的配置,然后再启动我们常说的Spring容器,那么为什么需要父子容器,父容器又是在什么地方进行创建的呢?@Override// ...省略前面前面的监听执行的时候会有一句apply方法执行。return;// 增加BootstrapMarkerConfiguration到资源中,我看这个是没有任何代码的类,具体不太清楚目的是何// 拿到Spring容器中的初始化器。

endwas 451

PyQt5自动化测试,让测试更加便利

PyQt5是一个Python的GUI编程工具包,帮助开发者可以更加简单、快捷地创建图形界面和处理用户输入。在PyQt5中有两种常见的测试方式:手动测试和自动化测试。手动测试需要通过点击、输入等交互操作进行测试,自动化测试则是通过程序模拟鼠标、键盘等操作进行测试。这段代码创建了一个简单的窗口,其中包含一个按钮。这段代码打开一个本地HTML文件,点击页面上的按钮并检查输出是否为“Button clicked”。以上就是在PyQt5中进行自动化测试的简单示例。使用PyQt5可以大大简化测试过程,提高测试效率。

PixelCoder的博客 710

SpringCloud | 深入解析SpringCloud中的父子容器

概述 在引入 SpringCloud 的项目中会多次创建 Spring 容器,本篇从源码角度深入分析具体哪些点会创建 Spring 容器,以及这些容器之间的区别与联系。 本文介绍的Spring相关容器是基于SpringCloud项目。 容器大致分为三层: BootStrap Spring 容器:由SpringCloud 监听器创建,用来初始化 SpringCloud 上下文 Spring...

程序员阿康 3175

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

聊聊SpringCloud环境下父子容器

聊聊SpringCloud环境下父子容器 注: 本文基于 SpringBoot-2.0.4-RELEASE S pringCloud-Finchley.SR1 版本. 起因 ​ Debug Spring web环境时了解到在启动期间创建了两个容器: Spring IOC容器和Web容器,其中Web容器是Spring IOC 的子容器。Debug SpringBoot源码时了解到 SpringBo...

jackcheng1117的博客 1137

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

boot spring 没有父子容器_spring源码系列:SpringCloud中的父子容器&容器启动流程源码解析(一)...

概述今天我们来聊一下Spring的启动流程 以及SpringCloud的父子容器的初始化过程。这个在平时工作中面试中也是出现频率比较高的知识点。特别是SpringCloud的父子容器的概念,如果你的项目中引入了Feign和Ribbon组件 却不知道SpringCloud底层是怎么管理的 在实际开发中排查问题是非常困难的。容器在微服务的环境中 我们说的IOC容器实际上有三层意思:boots...

weixin_35945980的博客 130

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

boot spring 没有父子容器_SpringCloud系列:聊聊SpringCloud 中的父子容器

概述在引入SpringCloud的项目中会多次创建Spring容器,本篇从源码角度深入分析具体哪些点会创建Spring容器,以及这些容器之间的区别与联系。本文介绍的Spring相关容器是基于SpringCloud Finchley.RELEASE版本。容器大致分为三层,分别对应上面的三类:·BootStrap上下文:由SpringCloud监听器创建,用来初始化SpringCloud上下文,也是祖...

weixin_28996083的博客 629

bootstrap项目源码_spring源码系列:SpringCloud中的父子容器&容器启动流程源码解析(一)...

概述今天我们来聊一下Spring的启动流程 以及SpringCloud的父子容器的初始化过程。这个在平时工作中面试中也是出现频率比较高的知识点。特别是SpringCloud的父子容器的概念,如果你的项目中引入了Feign和Ribbon组件 却不知道SpringCloud底层是怎么管理的 在实际开发中排查问题是非常困难的。容器在微服务的环境中 我们说的IOC容器实际上有三层意思:boots...

weixin_39525007的博客 96

SpringBoot启动源码浅析

SpringBoot启动源码浅析(貌似写得非常详细了)

Java_dragon95的博客 1249

SpringCloud源码阅读0-SpringCloud必备知识

SpringCloud 是一系列微服务工具项目的**集合**。这个集合包含如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等工具。

2017年 504

Spring Cloud项目是如何读取bootstrap.properties文件的?

org.springframework.cloud.bootstrap.BootstrapApplicationListener 监听事件 读取配置 可以通过设置spring.cloud.bootstrap.enabled为false,不读取bootstrap配置,但是此配置参数必须在当前监听器之前。(默认情况下此配置为true) 已经解析过(已经存在名称为bootstrap的MutablePropertySources对象)或者当前正在解析,不需要再进行解析 @Override public voi

m0_37607945的博客 1万+

spring cloud,sping boot,微服务,tomcat容器关系及创建执行顺序详解

1、spring cloud 父子容器整体关系 由于在spring cloud启动源码加载过程中,没太弄清楚tomcat容器,spring 容器,spring cloud容器整体的加载顺序以及关系,所以想分析下这个流程,先不多说,上图 容器大致分为三层: BootStrap Spring 容器:由SpringCloud 监听器创建,用来初始化 SpringCloud 上下文 SpringBoot Spring 容器:由SpringBoot创建,也是项目中常用的Spring容器。 微服务 Spring相关容

qq_22162093的博客 2322

爆肝spring源码笔记

还有ListableBeanFactory 我们判断bean工厂是否包含某beandefinition 通过什么bean name 或者bean type去获取某个bean 就是通过它。图1.3可以看出我们的工厂是列出bean容器根接口BeanFactory下的一些bean容器接口与类。中指出了springmvc中父子工厂的事务冲突问题 这个在springboot中由于引入了内置的。然后提到的xmlbeanfactory 通过解析xml生成beandefinition存到jvm。

JavaCoder_juejue的博客 598

孙帅suns的Spring第一集总结

1、时至今日我们认为Spring可能就是 解决方案、技术栈、全家桶2、首当其冲的单纯就是Spring框架,做web开发又衍生出来了SpringMVC,后续为了能够更加快速的开发引入了SpringBoot,有了SpringBoot,我们可能基于SpringBoot要开发微服务,于是有了SpringCloud,3、针对于Spring还有一些附属的框架比如说我想做安全验证,SpringSecurity比如说我想做批处理,SpringBatch持久化框架,SpringData衍生出来了很多内容如果我们想做社交登录,

weixin_43996338的博客 1267

花一个周末,掌握 SpringCloud Ribbon 核心原理

前言 继 SpringCloud Feign 之后的第二篇分布式框架文章,同样秉承单周末一个 SpringCloud 组件的大目标为原则 如果想看 Feign 的小伙伴,猛戳这里,Feign 核心原理与你不期而遇 在平常使用 SpringCloud 中,一般会使用 Feign,因为 Feign 内部集成了 Ribbon 但是 Ribbon 又是一个不可忽视的知识点,并且比 Feign 要难很多。列举文章大纲主题 如何获取注册中心服务实例 非健康服务实例如何下线 Ribbon 底层原理实现

m0_50180963的博客 387
上一篇: Nacos注册中心使用
下一篇: SpringBoot启动源码浅析
陈*^_^*
博客等级 码龄6年 48粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值