Spring Cloud Ribbon 分析(一)之RibbonAutoConfiguration入口配置

隨著微服務(wù)的項目越來越多标沪,對Ribbon的使用也逐漸增多榄攀,之前小編在項目中也只是簡單的了解Ribbon的作用,對于Ribbon是如何進(jìn)行負(fù)載的原理也不全面金句,所以在此進(jìn)行分析和總結(jié)檩赢,在后續(xù)也會分析和總結(jié)Spring Boot2.4.x版本之后默認(rèn)的負(fù)載均衡Spring Cloud LoadBalance!


RibbonAutoConfiguration配置文件

作為Ribbon的入口配置文件违寞,通過spring.factories進(jìn)行加載

@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {

    @Autowired(required = false)
    private List<RibbonClientSpecification> configurations = new ArrayList<>();

    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }

    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }
    ......
}

以上為配置文件比較重要的幾個點贞瞒,下文會逐一分析具體作用


@RibbonClients注解

@Configuration
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClients {

    RibbonClient[] value() default {};

    Class<?>[] defaultConfiguration() default {};

}

@Import(RibbonClientConfigurationRegistrar.class),字面意思不難理解趁曼,就是將@RibbonClients注解中配置的value與defaultConfiguration配置類注冊到Spring容器中(實例RibbonClientSpecification),但是為什么要通過這樣動態(tài)方式去注冊配置類呢军浆?仔細(xì)想想也不難理解,因為需要根據(jù)用戶的配置(@RibbonClients挡闰、@RibbonClient)進(jìn)行注冊實例


RibbonClientConfigurationRegistrar

public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        ......
        if (attrs != null && attrs.containsKey("defaultConfiguration")) {
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            } else {
                name = "default." + metadata.getClassName();
            }
            registerClientConfiguration(registry, name,
                    attrs.get("defaultConfiguration"));
        }
        ......
    }
    ......
    private void registerClientConfiguration(BeanDefinitionRegistry registry,
            Object name, Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(RibbonClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(name + ".RibbonClientSpecification",
                builder.getBeanDefinition());
    }
}

通過代碼片段得知@RibbonClients注解配置會生成RibbonClientSpecification實例


SpringClientFactory

/**
* 非常重要的一個工廠類乒融,Ribbon的很多獲取實例Bean都是通過該工廠
* 創(chuàng)建客戶端、負(fù)載平衡器和客戶端配置實例的工廠
* 為每個客戶端名稱創(chuàng)建一個Spring ApplicationContext,可以從中獲取需要的bean
*/
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

    static final String NAMESPACE = "ribbon";

    public SpringClientFactory() {
        super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
    }
    ......

    public ILoadBalancer getLoadBalancer(String name) {
        return getInstance(name, ILoadBalancer.class);
    }

    public IClientConfig getClientConfig(String name) {
        return getInstance(name, IClientConfig.class);
    }
    ......

這樣的解釋感覺有點牽強(qiáng)摄悯,我們繼續(xù)分析下赞季,SpringClientFactory繼承NamedContextFactory,這個NamedContextFactory我們下文會繼續(xù)講解奢驯,我們把焦點關(guān)注到構(gòu)造函數(shù)申钩,3個參數(shù),分別是配置類瘪阁、命名空間撒遣、屬性字段名,通過構(gòu)造函數(shù)管跺,我們就將RibbonClientConfiguration配置類引入進(jìn)來了义黎,簡單理解就是RibbonClientConfiguration這個配置類被Spring掃描到了,該配置類下面的@Bean配置在后續(xù)就會通過懶加載方式進(jìn)行訪問伙菜,最后一個\color{#000000}{(ribbon.client.name)}這個屬性名會在后續(xù)的分析中提到


NamedContextFactory

上文講到SpringClientFactory繼承NamedContextFactory轩缤,這是一個用命名空間劃分的一個上下文工廠類,那這個類具體有什么作用呢贩绕,我們接著講

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
        implements DisposableBean, ApplicationContextAware {

    public interface Specification {
        String getName();

        Class<?>[] getConfiguration();
    }
    private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

    protected AnnotationConfigApplicationContext getContext(String name) {
        if (!this.contexts.containsKey(name)) {
            synchronized (this.contexts) {
                if (!this.contexts.containsKey(name)) {
                    this.contexts.put(name, createContext(name));
                }
            }
        }
        return this.contexts.get(name);
    }

    protected AnnotationConfigApplicationContext createContext(String name) {
        ......
        context.register(PropertyPlaceholderAutoConfiguration.class,
                this.defaultConfigType);
        context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
                this.propertySourceName,
                Collections.<String, Object> singletonMap(this.propertyName, name)));
        if (this.parent != null) {
            // Uses Environment from parent as well as beans
            context.setParent(this.parent);
        }
        context.setDisplayName(generateDisplayName(name));
        context.refresh();
        return context;
    }
    public <T> T getInstance(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
                type).length > 0) {
            return context.getBean(type);
        }
        return null;
    }
    ......
}

SpringClientFactory.getInstance獲取對應(yīng)的實例時候火的,我們可以看到為每一個客戶端名稱(name屬性)都會創(chuàng)建一個Spring ApplicationContext,然后這個客戶端上下文就可以獲取Spring容器中的Bean實例,比如當(dāng)前業(yè)務(wù)中我的聚合服務(wù)A訪問領(lǐng)域服務(wù)B淑倾,通過@FeignClient方式進(jìn)行訪問馏鹤,那么此時getInstance中的name就是@FeignClient注解中的name或者value值,this.propertyName(\color{#000000}{(ribbon.client.name)})這個參數(shù)對應(yīng)的值就為@FeignClient注解中的name或者value值


LoadBalancerClient

Spring Cloud 對外提供的負(fù)載均衡客戶端接口娇哆,默認(rèn)實現(xiàn)類也能看出使用的是RibbonLoadBalancerClient,那這個類是怎么用起來的呢湃累?通過什么方式才能被用起來勃救?我們放到Spring Cloud Ribbon 分析(二)之LoadBalancerAutoConfiguration進(jìn)行總結(jié)!


入口配置類比較重要的幾個分析總結(jié)告一段落!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末治力,一起剝皮案震驚了整個濱河市蒙秒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宵统,老刑警劉巖晕讲,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異马澈,居然都是意外死亡瓢省,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門痊班,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勤婚,“玉大人,你說我怎么就攤上這事涤伐÷ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵废亭,是天一觀的道長国章。 經(jīng)常有香客問我,道長豆村,這世上最難降的妖魔是什么液兽? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮掌动,結(jié)果婚禮上四啰,老公的妹妹穿的比我還像新娘。我一直安慰自己粗恢,他們只是感情好柑晒,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眷射,像睡著了一般匙赞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妖碉,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天涌庭,我揣著相機(jī)與錄音,去河邊找鬼欧宜。 笑死坐榆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冗茸。 我是一名探鬼主播席镀,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼匹中,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了豪诲?” 一聲冷哼從身側(cè)響起顶捷,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎跛溉,沒想到半個月后焊切,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扮授,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡芳室,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了刹勃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堪侯。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖荔仁,靈堂內(nèi)的尸體忽然破棺而出伍宦,到底是詐尸還是另有隱情,我是刑警寧澤乏梁,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布次洼,位于F島的核電站,受9級特大地震影響遇骑,放射性物質(zhì)發(fā)生泄漏卖毁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一落萎、第九天 我趴在偏房一處隱蔽的房頂上張望亥啦。 院中可真熱鬧,春花似錦练链、人聲如沸翔脱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽届吁。三九已至,卻和暖如春绿鸣,著一層夾襖步出監(jiān)牢的瞬間疚沐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工枚驻, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留濒旦,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓再登,卻偏偏與公主長得像尔邓,于是被迫代替她去往敵國和親晾剖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容