1. 程式人生 > >【一起學原始碼-微服務】Feign 原始碼二:Feign動態代理構造過程

【一起學原始碼-微服務】Feign 原始碼二:Feign動態代理構造過程

前言

前情回顧

上一講主要看了@EnableFeignClients中的registerBeanDefinitions()方法,這裡面主要是
將EnableFeignClients註解對應的配置屬性注入,將FeignClient註解對應的屬性注入。

最後是生成FeignClient對應的bean,注入到Spring 的IOC容器。

本講目錄

目錄如下:

  1. registerFeignClient()回顧
  2. FeignClientFactoryBean.getObject()解析
  3. Feign.builder()及client()構建邏輯
  4. 建立Feign動態代理實現細節

說明

原創不易,如若轉載 請標明來源!

部落格地址:一枝花算不算浪漫
微信公眾號:壹枝花算不算浪漫

原始碼分析

registerFeignClient()回顧

回顧下之前的程式碼:

private void registerFeignClient(BeanDefinitionRegistry registry,
        AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
            .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    definition.addPropertyValue("type", className);
    definition.addPropertyValue("decode404", attributes.get("decode404"));
    definition.addPropertyValue("fallback", attributes.get("fallback"));
    definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    String alias = name + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

    boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null

    beanDefinition.setPrimary(primary);

    String qualifier = getQualifier(attributes);
    if (StringUtils.hasText(qualifier)) {
        alias = qualifier;
    }

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
            new String[] { alias });
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

registerFeignClient()方法中構造了一個BeanDefinitionBuilder物件,BeanDefinitionBuilder的主要作用就是構建一個AbstractBeanDefinition,AbstractBeanDefinition類最終被構建成一個BeanDefinitionHolder 然後註冊到Spring中。

beanDefinition類為FeignClientFactoryBean,故在Spring獲取類的時候實際返回的是FeignClientFactoryBean類。

FeignClientFactoryBean作為一個實現了FactoryBean的工廠類,那麼每次在Spring Context 建立實體類的時候會呼叫它的getObject()

方法。

FeignClientFactoryBean.getObject()解析

這裡直接分析FeignClientFactoryBean.getObject()方法,這裡包含著Feign動態代理的原理。

先看下程式碼:

@Override
public Object getObject() throws Exception {
    // 可以類比於ribbon中的SpringClientFactory,每個服務都對應一個獨立的spring容器
    FeignContext context = applicationContext.getBean(FeignContext.class);
    // builder中包含contract、logLevel、encoder、decoder、options等資訊
    Feign.Builder builder = feign(context);

    // 如果@FeignClient註解上沒有指定url,說明是要用ribbon的負載均衡
    if (!StringUtils.hasText(this.url)) {
        String url;
        if (!this.name.startsWith("http")) {
            url = "http://" + this.name;
        }
        else {
            url = this.name;
        }
        // 這裡構建的url類似於:http://serviceA
        url += cleanPath();
        return loadBalance(builder, context, new HardCodedTarget<>(this.type,
                this.name, url));
    }
    if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
        this.url = "http://" + this.url;
    }
    String url = this.url + cleanPath();
    Client client = getOptional(context, Client.class);
    if (client != null) {
        if (client instanceof LoadBalancerFeignClient) {
            // not lod balancing because we have a url,
            // but ribbon is on the classpath, so unwrap
            client = ((LoadBalancerFeignClient)client).getDelegate();
        }
        builder.client(client);
    }
    Targeter targeter = get(context, Targeter.class);
    return targeter.target(this, builder, context, new HardCodedTarget<>(
            this.type, this.name, url));
}

public <T> T getInstance(String name, Class<T> type) {
    // getContext是從SpringClientContext中獲取,之前講ribbon原始碼時講過
    // 一個serviceName都會有自己的一個SpringClientContext上下文資訊
    AnnotationConfigApplicationContext context = getContext(name);
    if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
            type).length > 0) {
        // 這裡是獲取到LoadBalancerFeignClient
        return context.getBean(type);
    }
    return null;
}

首先是FeignContext ,我們可以類比下ribbon中的SpringClientFactory, 每個服務的呼叫,都有一個獨立的ILoadBalancer、IRule、IPing等等,每個服務都對應一個獨立的spring容器,從那個獨立的容器中,可以取出這個服務關聯的屬於自己的LoadBalancer之類的東西。

如果我們呼叫一個服務的話,比如ServiceA,那麼這個服務就會關聯一個spring容器,FeignContext就代表一個獨立的容器,關聯著自己獨立的一些元件,例如Logger元件、Decoder元件、Encoder元件等等。

我們可以看下FeignAutoConfiguration中:

@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        // configurations是一個Map結構
        context.setConfigurations(this.configurations);
        return context;
    }
}

public class FeignContext extends NamedContextFactory<FeignClientSpecification> {

    public FeignContext() {
        // FeignClientsConfiguration中會載入Encoder、Decoder、Logger等元件
        super(FeignClientsConfiguration.class, "feign", "feign.client.name");
    }
}

這裡可以知道FeignContext的結構,裡面其實就是封裝了一個服務例項(ServiceA)對應的各種元件,其中FeignClientsConfiguration是載入預設的元件資訊配置類。

接下來還是回到FeignClientFactoryBean.getObject()中,接著看feign()方法:

protected Feign.Builder feign(FeignContext context) {
    // 從context中獲取到預設Logger元件:Slf4jLogger
    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(this.type);

    // 從context中找type:Feign.Builder.class 對應的元件資訊
    // 然後往builder中放入各種元件資訊
    Feign.Builder builder = get(context, Feign.Builder.class)
            // required values
            .logger(logger)
            .encoder(get(context, Encoder.class))
            .decoder(get(context, Decoder.class))
            .contract(get(context, Contract.class));
    // @formatter:on

    configureFeign(context, builder);

    return builder;
}

protected <T> T get(FeignContext context, Class<T> type) {
    // context中轉載的有Logger元件資訊,這裡預設的是Slf4jLogger
    T instance = context.getInstance(this.name, type);
    if (instance == null) {
        throw new IllegalStateException("No bean found of type " + type + " for "
                + this.name);
    }
    return instance;
}

這裡是構造一個Feign.builder()物件,裡面還是封裝了各種元件資訊。其中Feign.builder在FeignClientsConfiguration被初始化,一般使用的是HystrixFeign.builder()

@Configuration
public class FeignClientsConfiguration {
    // 一般環境都會配置feign.hystrix.enabled = true,這裡直接看HystrixFeign.builder();
    @Configuration
    @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
    protected static class HystrixFeignConfiguration {
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = false)
        public Feign.Builder feignHystrixBuilder() {
            return HystrixFeign.builder();
        }
    }
}

接著看configureFeign() 方法,這個方法是讀取application.properties中的配置資訊。這裡有個很有趣的配置:

configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(this.name), builder);

如果我們配置feign,先指定一個全域性配置,在指定針對於某個服務的配置,那麼某個服務配置的優先順序會覆蓋全域性的配置。

一張圖總結下Feign.builder()構建的過程:

Feign.builder()及client()構建邏輯

還是接著上面getObject() 方法去分析,上面分析完了Feign.builder()的構建,下面接著看看剩下的程式碼。

loadBalance(builder, context, new HardCodedTarget<>(this.type,this.name, url));

這裡形式構造了一個HardCodeTarget物件,這個物件包含了介面型別(com.barrywang.service.feign.ServiceAFeignClient)、服務名稱(ServiceA)、url地址(http://ServiceA),跟Feign.Builder、FeignContext,一起,傳入了loadBalance()方法裡去。

接著檢視loadBalance() 方法:

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> target) {
    // 這裡還是從context中獲取feignClient資料
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        Targeter targeter = get(context, Targeter.class);
        return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
            "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

protected <T> T getOptional(FeignContext context, Class<T> type) {
    return context.getInstance(this.name, type);
}

這裡還是從context中獲取Client.class對應的資料,我們繼續檢視FeignAutoConfiguration 類,但是並沒有發現Feign.client相關的資料,檢視FeignAutoConfiguration的依賴,可以找到FeignRibbonClientAutoConfiguration ,程式碼如下:

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
// 這裡會import三個FeignLoadBalance配置
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
        OkHttpFeignLoadBalancedConfiguration.class,
        DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {

    @Bean
    @Primary
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    public CachingSpringLoadBalancerFactory cachingLBClientFactory(
            SpringClientFactory factory) {
        return new CachingSpringLoadBalancerFactory(factory);
    }

    @Bean
    @Primary
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
        SpringClientFactory factory,
        LoadBalancedRetryPolicyFactory retryPolicyFactory,
        LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory,
        LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory) {
        return new CachingSpringLoadBalancerFactory(factory, retryPolicyFactory, loadBalancedBackOffPolicyFactory, loadBalancedRetryListenerFactory);
    }

    // Options是超時相關的配置
    @Bean
    @ConditionalOnMissingBean
    public Request.Options feignRequestOptions() {
        return LoadBalancerFeignClient.DEFAULT_OPTIONS;
    }
}

@Configuration
class DefaultFeignLoadBalancedConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Client.Default(null, null),
                cachingFactory, clientFactory);
    }
}

到了這裡就知道了,這裡Feign.client預設應該就是LoadBalancerFeignClient了。

到這繼續用一張圖總結下:

建立Feign動態代理實現細節

接著上面程式碼,預設Feign.client()為LoadBalancerFeignClient, 然後將client加入到builder中。接著繼續跟進targer相關:

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
        HardCodedTarget<T> target) {
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        // 這裡又是通過Targer然後再context中獲取預設配置
        Targeter targeter = get(context, Targeter.class);
        return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
            "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

protected <T> T get(FeignContext context, Class<T> type) {
    T instance = context.getInstance(this.name, type);
    if (instance == null) {
        throw new IllegalStateException("No bean found of type " + type + " for "
                + this.name);
    }
    return instance;
}

可以看到,這裡又是通過Targeter.class從context中獲取對應預設Targter。我們繼續通過FeignAutoConfiguration中進行查詢:

@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {

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

    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }

    // 如果配置了feign.hystrix.HystrixFeign 則建立HystrixTargeter
    @Configuration
    @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
    protected static class HystrixFeignTargeterConfiguration {
        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new HystrixTargeter();
        }
    }

    // 如果沒有配置feign.hystrix.HystrixFeign 則建立DefaultTargeter
    @Configuration
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    protected static class DefaultFeignTargeterConfiguration {
        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new DefaultTargeter();
        }
    }
}

在預設情況下,feign是和hystrix整合的,feign.hystrix.HystrixFeign會有配置,所以這裡預設Targeter使用的是HystrixTargeter, 在loadBalance()方法中執行的targeter.target()方法就是執行HystrixTargeter.target()方法:

class HystrixTargeter implements Targeter {
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                        Target.HardCodedTarget<T> target) {
    // 判斷Feign.builder()型別
    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
        return feign.target(target);
    }
    feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
    SetterFactory setterFactory = getOptional(factory.getName(), context,
        SetterFactory.class);
    if (setterFactory != null) {
        builder.setterFactory(setterFactory);
    }
    Class<?> fallback = factory.getFallback();
    if (fallback != void.class) {
        return targetWithFallback(factory.getName(), context, target, builder, fallback);
    }
    Class<?> fallbackFactory = factory.getFallbackFactory();
    if (fallbackFactory != void.class) {
        return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
    }

    // 最終都會執行feign.target()方法
    return feign.target(target);
}


public abstract class Feign {

  public static Builder builder() {
    return new Builder();
  }

  /**
   * Returns a new instance of an HTTP API, defined by annotations in the {@link Feign Contract},
   * for the specified {@code target}. You should cache this result.
   */
  public abstract <T> T newInstance(Target<T> target);

  public static class Builder {

    // 省略部分程式碼


    public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }

    public Feign build() {
      // 構建一個SynchronousMethodHandler工廠
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                               logLevel, decode404);

      // 構建
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder,
                                  errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
    }
  }
}

這裡主要是build方法,構造了一個ReflectieFein物件,接著看它裡面的newInstance()方法:

@Override
public <T> T newInstance(Target<T> target) {
    // nameToHandler是@FeignClient中的方法名對應的MethodHandler物件
    Map<String, InvocationHandlerFactory.MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = new LinkedHashMap<Method, InvocationHandlerFactory.MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
        if (method.getDeclaringClass() == Object.class) {
            continue;
        } else if (Util.isDefault(method)) {
            DefaultMethodHandler handler = new DefaultMethodHandler(method);
            defaultMethodHandlers.add(handler);
            methodToHandler.put(method, handler);
        } else {
            // 將具體的method作為map的key值
            methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
        }
    }

    // JDK動態代理 返回類似於:ReflectiveFeign$FeignInvocationHandler@7642
    // methodToHandler中包含Feign.builder()、Feign.client()等資訊
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
        defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}

這裡就是使用了JDK動態代理,實際上返回的Feign動態代理的物件類似於:ReflectiveFeign$FeignInvocationHandler@7642

這也和我們第一講中的debug截圖一致了,到了這裡feign動態代理物件的生成原理都已經很清楚了。

最後debug一下,看下最終生成的動態代理物件:

總結

最後用一張圖總結Feign動態代理生成的規則:

  1. 生成Feign.builder(),裡面包含Encoder、Decoder、Logger等元件,還有application.properties中相關的feign client配置資訊
  2. 生成Feign.client(),預設為LoadBalancerFeignClient
  3. 生成預設Targter物件:HystrixTargter
  4. builder、client、targter 通過JDK動態代理生成feign動態代理物件

一張圖總結:

申明

本文章首發自本人部落格:https://www.cnblogs.com/wang-meng 和公眾號:壹枝花算不算浪漫,如若轉載請標明來源!

感興趣的小夥伴可關注個人公眾號:壹枝花算不算浪漫

相關推薦

一起原始碼-服務Ribbon 原始碼Ribbon概念理解及Demo除錯

前言 前情回顧 前面文章已經梳理清楚了Eureka相關的概念及原始碼,接下來開始研究下Ribbon的實現原理。 我們都知道Ribbon在spring cloud中擔當負載均衡的角色, 當兩個Eureka Client互相呼叫的時候,Ribbon能夠做到呼叫時的負載,保證多節點的客戶端均勻接收請求。(這個有點類

一起原始碼-服務Ribbon 原始碼通過Debug找出Ribbon初始化流程及ILoadBalancer原理分析

前言 前情回顧 上一講講了Ribbon的基礎知識,通過一個簡單的demo看了下Ribbon的負載均衡,我們在RestTemplate上加了@LoadBalanced註解後,就能夠自動的負載均衡了。 本講目錄 這一講主要是繼續深入RibbonLoadBalancerClient和Ribbon+Eureka整合的

一起原始碼-服務Ribbon 原始碼Ribbon與Eureka整合原理分析

前言 前情回顧 上一篇講了Ribbon的初始化過程,從LoadBalancerAutoConfiguration 到RibbonAutoConfiguration 再到RibbonClientConfiguration,我們找到了ILoadBalancer預設初始化的物件等。 本講目錄 這一講我們會進一步往下

一起原始碼-服務Ribbon 原始碼進一步探究Ribbon的IRule和IPing

前言 前情回顧 上一講深入的講解了Ribbon的初始化過程及Ribbon與Eureka的整合程式碼,與Eureka整合的類就是DiscoveryEnableNIWSServerList,同時在DynamicServerListLoadBalancer中會呼叫PollingServerListUpdater 進

一起原始碼-服務Ribbon原始碼Ribbon原始碼解讀彙總篇~

前言 想說的話 【一起學原始碼-微服務-Ribbon】專欄到這裡就已經全部結束了,共更新四篇文章。 Ribbon比較小巧,這裡是直接 讀的spring cloud 內嵌封裝的版本,裡面的各種configuration確實有點繞,不過看看第三講Ribbon初始化的過程總結圖就會清晰很多。 緊接著會繼續整理學習F

一起原始碼-服務Hystrix 原始碼Hystrix基礎原理與Demo搭建

說明 原創不易,如若轉載 請標明來源! 歡迎關注本人微信公眾號:壹枝花算不算浪漫 更多內容也可檢視本人部落格:一枝花算不算浪漫 前言 前情回顧 上一個系列文章講解了Feign的原始碼,主要是Feign動態代理實現的原理,及配合Ribbon實現負載均衡的機制。 這裡我們講解一個新的元件Hystrix,也是和Fe

一起原始碼-服務Hystrix 原始碼Hystrix核心流程Hystix非降級邏輯流程梳理

說明 原創不易,如若轉載 請標明來源! 歡迎關注本人微信公眾號:壹枝花算不算浪漫 更多內容也可檢視本人部落格:一枝花算不算浪漫 前言 前情回顧 上一講我們講了配置了feign.hystrix.enabled=true之後,預設的Targeter就會構建成HystrixTargter, 然後通過對應的Hystr

一起原始碼-服務Hystrix 原始碼Hystrix核心流程Hystix降級、熔斷等原理剖析

說明 原創不易,如若轉載 請標明來源! 歡迎關注本人微信公眾號:壹枝花算不算浪漫 更多內容也可檢視本人部落格:一枝花算不算浪漫 前言 前情回顧 上一講我們講解了Hystrix在配合feign的過程中,一個正常的請求邏輯該怎樣處理,這裡涉及到執行緒池的建立、HystrixCommand的執行等邏輯。 如圖所示:

一起原始碼-服務Feign 原始碼原始碼初探,通過Demo Debug Feign原始碼

前言 前情回顧 上一講深入的講解了Ribbon的初始化過程及Ribbon與Eureka的整合程式碼,與Eureka整合的類就是DiscoveryEnableNIWSServerList,同時在DynamicServerListLoadBalancer中會呼叫PollingServerListUpdater 進

一起原始碼-服務Feign 原始碼Feign動態代理構造過程

前言 前情回顧 上一講主要看了@EnableFeignClients中的registerBeanDefinitions()方法,這裡面主要是 將EnableFeignClients註解對應的配置屬性注入,將FeignClient註解對應的屬性注入。 最後是生成FeignClient對應的bean,注入到Spr

一起原始碼-服務Feign 原始碼Feign結合Ribbon實現負載均衡的原理分析

前言 前情回顧 上一講我們已經知道了Feign的工作原理其實是在專案啟動的時候,通過JDK動態代理為每個FeignClinent生成一個動態代理。 動態代理的資料結構是:ReflectiveFeign.FeignInvocationHandler。其中包含target(裡面是serviceName等資訊)和d

一起原始碼-服務Nexflix Eureka 原始碼EurekaServer啟動之配置檔案載入以及面向介面的配置項讀取

前言 上篇文章已經介紹了 為何要讀netflix eureka原始碼了,這裡就不再概述,下面開始正式原始碼解讀的內容。 如若轉載 請標明來源:一枝花算不算浪漫 程式碼總覽 還記得上文中,我們通過web.xml找到了eureka server入口的類EurekaBootStrap,這裡我們就先來簡單地看下: /

一起原始碼-服務Nexflix Eureka 原始碼EurekaServer啟動之EurekaServer上下文EurekaClient建立

前言 上篇文章已經介紹了 Eureka Server 環境和上下文初始化的一些程式碼,其中重點講解了environment初始化使用的單例模式,以及EurekaServerConfigure基於介面對外暴露配置方法的設計方式。這一講就是講解Eureka Server上下文初始化剩下的內容:Eureka Cli

一起原始碼-服務Nexflix Eureka 原始碼在眼花繚亂的程式碼中,EurekaClient是如何註冊的?

前言 上一講已經講解了EurekaClient的啟動流程,到了這裡已經有6篇Eureka原始碼分析的文章了,看了下之前的文章,感覺程式碼成分太多,會影響閱讀,後面會只擷取主要的程式碼,加上註釋講解。 這一講看的是EurekaClient註冊的流程,當然也是一塊核心,標題為什麼會寫上眼花繚亂呢?關於Eureka

一起原始碼-服務Nexflix Eureka 原始碼通過單元測試來Debug Eureka註冊過程

前言 上一講eureka client是如何註冊的,一直跟到原始碼傳送http請求為止,當時看eureka client註冊時如此費盡,光是找一個regiter的地方就找了半天,那麼client端傳送了http請求給server端,server端是如何處理的呢? 帶著這麼一個疑問 就開始今天原始碼的解讀了。

一起原始碼-服務Nexflix Eureka 原始碼EurekaClient登錄檔抓取 精妙設計分析!

前言 前情回顧 上一講 我們通過單元測試 來梳理了EurekaClient是如何註冊到server端,以及server端接收到請求是如何處理的,這裡最重要的關注點是登錄檔的一個數據結構:ConcurrentHashMap<String, Map<String, Lease<InstanceI

一起原始碼-服務Nexflix Eureka 原始碼服務續約原始碼分析

前言 前情回顧 上一講 我們講解了服務發現的相關邏輯,所謂服務發現 其實就是登錄檔抓取,服務例項預設每隔30s去註冊中心抓取一下注冊表增量資料,然後合併本地登錄檔資料,最後有個hash對比的操作。 本講目錄 今天主要是看下服務續約的邏輯,服務續約就是client端給server端傳送心跳檢測,告訴對方我還活著

一起原始碼-服務Nexflix Eureka 原始碼服務下線及例項摘除,一個client下線到底多久才會被其他例項感知?

前言 前情回顧 上一講我們講了 client端向server端傳送心跳檢查,也是預設每30鍾傳送一次,server端接收後會更新登錄檔的一個時間戳屬性,然後一次心跳(續約)也就完成了。 本講目錄 這一篇有兩個知識點及一個疑問,這個疑問是在工作中真真實實遇到過的。 例如我有服務A、服務B,A、B都註冊在同一個註

一起原始碼-服務Nexflix Eureka 原始碼十一EurekaServer自我保護機制竟然有這麼多Bug?

前言 前情回顧 上一講主要講了服務下線,已經註冊中心自動感知宕機的服務。 其實上一講已經包含了很多EurekaServer自我保護的程式碼,其中還發現了1.7.x(1.9.x)包含的一些bug,但這些問題在master分支都已修復了。 服務下線會將服務例項從登錄檔中刪除,然後放入到recentQueue中,下

一起原始碼-服務Nexflix Eureka 原始碼EurekaServer叢集模式原始碼分析

前言 前情回顧 上一講看了Eureka 註冊中心的自我保護機制,以及裡面提到的bug問題。 哈哈 轉眼間都2020年了,這個系列的文章從12.17 一直寫到現在,也是不容易哈,每天持續不斷學習,輸出部落格,這一段時間確實收穫很多。 今天在公司給組內成員分享了Eureka原始碼剖析,反響效果還可以,也算是感覺收