上篇文章已經介紹了 爲什麼要讀netflix eureka源碼了,這裏就再也不概述,下面開始正式源碼解讀的內容。java
如若轉載 請標明來源:一枝花算不算浪漫node
還記得上文中,咱們經過web.xml找到了eureka server入口的類EurekaBootStrap
,這裏咱們就先來簡單地看下:web
/** * The class that kick starts the eureka server. 負責啓動Eureka server的類 * * <p> * 這裏要注意兩個關鍵點: * eureka server對應的配置類爲:EurekaServerConfig * eureka client對應的配置類爲:EurekaInstanceConfig * * The eureka server is configured by using the configuration * {@link EurekaServerConfig} specified by <em>eureka.server.props</em> in the * classpath. The eureka client component is also initialized by using the * configuration {@link EurekaInstanceConfig} specified by * <em>eureka.client.props</em>. If the server runs in the AWS cloud, the eureka * server binds it to the elastic ip as specified. * </p> * * @author Karthik Ranganathan, Greg Kim, David Liu * * 負責EurekaServer初始化的類 */ public class EurekaBootStrap implements ServletContextListener { /** * Initializes Eureka, including syncing up with other Eureka peers and publishing the registry. * * @see * javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) */ @Override public void contextInitialized(ServletContextEvent event) { try { initEurekaEnvironment(); initEurekaServerContext(); ServletContext sc = event.getServletContext(); sc.setAttribute(EurekaServerContext.class.getName(), serverContext); } catch (Throwable e) { logger.error("Cannot bootstrap eureka server :", e); throw new RuntimeException("Cannot bootstrap eureka server :", e); } } }
看下注釋 咱們能夠了解到幾個關鍵點:bootstrap
接着近一步往下跟,這裏能夠先看 initEurekaEnvironment()
安全
代碼以下:app
protected void initEurekaEnvironment() throws Exception { logger.info("Setting the eureka configuration.."); // 獲取dataCenter數據中心 這裏重點看ConfigurationManager // ConfigurationManager:配置管理器,管理eureka本身全部的配置, // 重點:getConfigInstance裏面使用的是volatile+synchronized+double check模式的單例模式 /** * ConfigurationManager 建立過程:(繼續日後跟讀代碼) * 一、建立一個ConcurrentCompositeConfiguration實例,這個類表明了所謂的配置,包括eureka須要的全部配置。 * 二、往ConcurrentCompositeConfiguration加入一堆config,而後返回ConfigurationManager實例 * 三、初始化數據中心的配置,若是沒有配置的話就是default data center * 四、初始化eureka 運行的環境,若是沒有配置的話,默認就是test環境 */ String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER); // 初始化數據中心,沒有配置的話 使用DEFAULT data center if (dataCenter == null) { logger.info("Eureka data center value eureka.datacenter is not set, defaulting to default"); ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT); } else { ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter); } // 獲取eureka server運行環境,沒有配置的話默認使用test環境 // 後面讀取配置文件會根據運行環境讀取,好比eureka-server-test.properties String environment = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT); if (environment == null) { ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST); logger.info("Eureka environment value eureka.environment is not set, defaulting to test"); } }
這裏註釋寫的比較詳細,且這裏有兩個重點:ide
這裏一個關鍵點 是使用了很經典的double check 單例模式。微服務
這種單例是一種線程安全的方式,裏面使用了volatile+synchronized+double check,具體秒在何處 我這裏就不展開講解了,搜索double check單例模式就會有不少解析文章,這裏直接看代碼。編碼
static volatile AbstractConfiguration instance = null; /** * Get the current system wide configuration. If there has not been set, it will return a default * {@link ConcurrentCompositeConfiguration} which contains a SystemConfiguration from Apache Commons * Configuration and a {@link DynamicURLConfiguration}. */ public static AbstractConfiguration getConfigInstance() { if (instance == null) { synchronized (ConfigurationManager.class) { if (instance == null) { instance = getConfigInstance(Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG)); } } } return instance; }
這裏instance用volatile修飾來保證線程之間的可見性和有序性(禁止指令重排序),通常的對象建立過程都是非原子性的,內部會發生指令重排序的狀況,因此加上volatile能夠防止指令重排。用synchronized來保證線程串行化,double check來保證不被單例化。spa
接着咱們就繼續往下跟,看看ConfigurationManager的建立過程。
private static AbstractConfiguration getConfigInstance(boolean defaultConfigDisabled) { if (instance == null && !defaultConfigDisabled) { instance = createDefaultConfigInstance(); registerConfigBean(); } return instance; } private static AbstractConfiguration createDefaultConfigInstance() { ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration(); try { DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration(); config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME); } catch (Throwable e) { logger.warn("Failed to create default dynamic configuration", e); } if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) { SystemConfiguration sysConfig = new SystemConfiguration(); config.addConfiguration(sysConfig, SYS_CONFIG_NAME); } if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) { EnvironmentConfiguration envConfig = new EnvironmentConfiguration(); config.addConfiguration(envConfig, ENV_CONFIG_NAME); } ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration(); config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES); config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig)); return config; } public ConcurrentCompositeConfiguration() { clear(); } public final void clear() { fireEvent(EVENT_CLEAR, null, null, true); configList.clear(); namedConfigurations.clear(); // recreate the in memory configuration containerConfiguration = new ConcurrentMapConfiguration(); containerConfiguration.setThrowExceptionOnMissing(isThrowExceptionOnMissing()); containerConfiguration.setListDelimiter(getListDelimiter()); containerConfiguration.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); containerConfiguration.addConfigurationListener(eventPropagater); configList.add(containerConfiguration); overrideProperties = new ConcurrentMapConfiguration(); overrideProperties.setThrowExceptionOnMissing(isThrowExceptionOnMissing()); overrideProperties.setListDelimiter(getListDelimiter()); overrideProperties.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); overrideProperties.addConfigurationListener(eventPropagater); fireEvent(EVENT_CLEAR, null, null, false); containerConfigurationChanged = false; invalidate(); }
上面代碼是比較多,若是一行行去摳細節 真的就沒有必要了,這裏咱們只是看一些重點的流程,咱們上面註釋也寫到過ConfigurationManager的建立過程:
一、建立一個ConcurrentCompositeConfiguration實例,這個類表明了所謂的配置,包括eureka須要的全部配置。
二、往ConcurrentCompositeConfiguration加入一堆config,而後返回ConfigurationManager實例
這裏我是不建議太過於扣細節的,由於每每這些細枝末節的東西會將咱們繞進去。
關於ConfigurationManager具體的細節這裏也有兩篇比較好的文章推薦:
先看代碼:
protected void initEurekaServerContext() throws Exception { // 一、加載eureka-server properties文件中和配置 EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig(); // For backward compatibility JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH); XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH); logger.info("Initializing the eureka client..."); logger.info(eurekaServerConfig.getJsonCodecName()); ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig); // 二、初始化一個ApplicationInfoManager,和第3步建立eureka client相關,後續會講解 ApplicationInfoManager applicationInfoManager = null; // 三、初始化eureka-server內部的一個eureka-client(用來跟其餘的eureka-server節點作註冊和通訊) // 類的開頭已經說明了:EurekaInstanceConfig其實就是eureka client相關的配置類 if (eurekaClient == null) { EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext()) ? new CloudInstanceConfig() : new MyDataCenterInstanceConfig(); applicationInfoManager = new ApplicationInfoManager( instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get()); EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig(); eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig); } else { applicationInfoManager = eurekaClient.getApplicationInfoManager(); } // 三、處理註冊相關的事情 PeerAwareInstanceRegistry registry; if (isAws(applicationInfoManager.getInfo())) { registry = new AwsInstanceRegistry( eurekaServerConfig, eurekaClient.getEurekaClientConfig(), serverCodecs, eurekaClient ); awsBinder = new AwsBinderDelegate(eurekaServerConfig, eurekaClient.getEurekaClientConfig(), registry, applicationInfoManager); awsBinder.start(); } else { registry = new PeerAwareInstanceRegistryImpl( eurekaServerConfig, eurekaClient.getEurekaClientConfig(), serverCodecs, eurekaClient ); } // 四、處理peer節點相關的事情 PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes( registry, eurekaServerConfig, eurekaClient.getEurekaClientConfig(), serverCodecs, applicationInfoManager ); // 五、完成eureka-server上下文(context)的構建及初始化 serverContext = new DefaultEurekaServerContext( eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, applicationInfoManager ); EurekaServerContextHolder.initialize(serverContext); serverContext.initialize(); logger.info("Initialized server context"); // Copy registry from neighboring eureka node // 六、處理一些善後的事情,從相鄰的eureka節點拷貝註冊信息 int registryCount = registry.syncUp(); registry.openForTraffic(applicationInfoManager, registryCount); // Register all monitoring statistics. // 七、註冊全部的監控統計項 EurekaMonitors.registerAllStats(); }
代碼有點長,加載context信息分爲了上面註釋的好幾步,代碼註釋都有寫
加載eureka-server properties文件中和配置
EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
private static final DynamicStringProperty EUREKA_PROPS_FILE = DynamicPropertyFactory.getInstance().getStringProperty("eureka.server.props","eureka-server"); public DefaultEurekaServerConfig() { init(); } private void init() { String env = ConfigurationManager.getConfigInstance().getString( EUREKA_ENVIRONMENT, TEST); ConfigurationManager.getConfigInstance().setProperty( ARCHAIUS_DEPLOYMENT_ENVIRONMENT, env); String eurekaPropsFile = EUREKA_PROPS_FILE.get(); try { // ConfigurationManager // .loadPropertiesFromResources(eurekaPropsFile); ConfigurationManager .loadCascadedPropertiesFromResources(eurekaPropsFile); } catch (IOException e) { logger.warn( "Cannot find the properties specified : {}. This may be okay if there are other environment " + "specific properties or the configuration is installed with a different mechanism.", eurekaPropsFile); } } public static void loadCascadedPropertiesFromResources(String configName) throws IOException { Properties props = loadCascadedProperties(configName); if (instance instanceof AggregatedConfiguration) { ConcurrentMapConfiguration config = new ConcurrentMapConfiguration(); config.loadProperties(props); ((AggregatedConfiguration) instance).addConfiguration(config, configName); } else { ConfigurationUtils.loadProperties(props, instance); } }
首先咱們看下EurekaServerConfig
:
裏面包含好多getxxx方法,看一下具體實現:
其中configInstance是DynamicPropertyFactory
對象。EurekaServerConfig,這是個接口,這裏面有一堆getXXX()的方法,包含了eureka server須要使用的全部的配置,均可以經過這個接口來獲取。
想象一下,eureka-sever.properties文件裏,都是一個一個的key=value的不少的配置項,確定是將這些key-value格式的配置項加載到內存的Properties對象去存放,Map。通常來講,若是讓咱們本身來設計這個讀取properties文件的配置的代碼,也許咱們就是作到將配置加載到Properties對象中就結束了。
EurekaServerConfig,表明了eureka-server須要的全部的配置項,經過接口定義了大量的方法,讓你能夠從這裏獲取全部你須要的配置
DefaultEurekaServerConfig
就是上面EurekaServerConfig
的實現類,建立實例的時候,會執行一個init()方法,在這個方法中,就會完成eureka-server.properties文件中的配置項的加載。EUREKA_PROPS_FILE,對應着要加載的eureka的配置文件的名字。
將加載出來的Properties中的配置項都放到ConfigurationManager中去,由這個ConfigurationManager來管理
好比說eureka-server那個工程裏,就有一個src/main/resources/eureka-server.properties文件,只不過裏面是空的,所有都用了默認的配置
DefaultEurekaServerConfig.init()方法中,會將eureka-server.properties文件中的配置加載出來,都放到ConfdigurationManager中去,而後在DefaultEurekaServerConfig的各類獲取配置項的方法中,配置項的名字是在各個方硬編碼的,是從一個DynamicPropertyFactory裏面去獲取的,你能夠認爲DynamicPropertyFactory是從ConfigurationManager那兒來的,由於ConfigurationManager中都包含了加載出來的配置了,因此DynamicPropertyFactory裏,也能夠獲取到全部的配置項
在從DynamicPropertyFactory中獲取配置項的時候,若是你沒配置,那麼就用默認值,所有都給你弄好了各個配置項的默認值,至關於全部的配置項的默認值,在DefaultEurekaServerConfig的各個方法中,均可以看到,若是你沒配置,那麼就用這裏的默認值就能夠了
加載eureka-server.properties的過程:
(1)建立了一個DefaultEurekaServerConfig對象
(2)建立DefaultEurekaServerConfig對象的時候,在裏面會有一個init方法
(3)先是將eureka-server.properties中的配置加載到了一個Properties對象中,而後將Properties對象中的配置放到ConfigurationManager中去,此時ConfigurationManager中去就有了全部的配置了
(4)而後DefaultEurekaServerConfig提供的獲取配置項的各個方法,都是經過硬編碼的配置項名稱,從DynamicPropertyFactory中獲取配置項的值,DynamicPropertyFactory是從ConfigurationManager那兒來的,因此也包含了全部配置項的值
(5)在獲取配置項的時候,若是沒有配置,那麼就會有默認的值,所有屬性都是有默認值的
本文章首發自本人博客:https://www.cnblogs.com/wang-meng 和公衆號:壹枝花算不算浪漫,如若轉載請標明來源!
感興趣的小夥伴可關注我的公衆號:壹枝花算不算浪漫