Eureka 原始碼分析之 Eureka Server
文章首發於公眾號《程式設計師果果》
地址 : https://mp.weixin.qq.com/s/FfJrAGQuHyVrsedtbr0Ihw
簡介
上一篇文章《Eureka 原始碼分析之 Eureka Client》 通過原始碼知道 ,eureka Client 是通過 http rest來 與 eureka server 互動,實現 註冊服務,續約服務,服務下線 等。本篇探究下eureka server。
原始碼分析
從 @EnableEurekaServer 註解為入口分析,通過原始碼可以看出他是一個標記註解:
/** * Annotation to activate Eureka Server related configuration {@link */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(EurekaServerMarkerConfiguration.class) public @interface EnableEurekaServer { }
從註釋可以知道,用來啟用 eureka server 的 配置類 EurekaServerAutoConfiguration 中相關配置,EurekaServerAutoConfiguration 的關鍵程式碼如下:
@Configuration @Import(EurekaServerInitializerConfiguration.class) @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) @EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class }) @PropertySource("classpath:/eureka/server.properties") public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter { /** * List of packages containing Jersey resources required by the Eureka server */ private static final String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery", "com.netflix.eureka" }; @Autowired private ApplicationInfoManager applicationInfoManager; @Autowired private EurekaServerConfig eurekaServerConfig; @Autowired private EurekaClientConfig eurekaClientConfig; @Autowired private EurekaClient eurekaClient; @Autowired private InstanceRegistryProperties instanceRegistryProperties; public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson(); @Bean public HasFeatures eurekaServerFeature() { return HasFeatures.namedFeature("Eureka Server", EurekaServerAutoConfiguration.class); } @Configuration protected static class EurekaServerConfigBeanConfiguration { // 建立並載入EurekaServerConfig的實現類,主要是Eureka-server的配置資訊 @Bean @ConditionalOnMissingBean public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) { EurekaServerConfigBean server = new EurekaServerConfigBean(); if (clientConfig.shouldRegisterWithEureka()) { // Set a sensible default if we are supposed to replicate server.setRegistrySyncRetries(5); } return server; } } //載入EurekaController,SpringCloud 提供了一些額外的介面,用來獲取eurekaServer的資訊 @Bean @ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true) public EurekaController eurekaController() { return new EurekaController(this.applicationInfoManager); } //省略 ... // 接收客戶端的註冊等請求就是通過InstanceRegistry來處理的,是真正處理業務的類,接下來會詳細分析 @Bean public PeerAwareInstanceRegistry peerAwareInstanceRegistry( ServerCodecs serverCodecs) { this.eurekaClient.getApplications(); // force initialization return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount()); } //配置服務節點資訊,這裡的作用主要是為了配置Eureka的peer節點,也就是說當有收到有節點註冊上來的時候,需要通知給哪些節點 @Bean @ConditionalOnMissingBean public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs) { return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.applicationInfoManager); } //省略 ... //EurekaServer的上下文 @Bean public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) { return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager); } // 初始化Eureka-server,會同步其他註冊中心的資料到當前註冊中心 @Bean public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry, EurekaServerContext serverContext) { return new EurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry, serverContext); } // 配置攔截器,ServletContainer裡面實現了jersey框架,通過他來實現eurekaServer對外的restFull介面 @Bean public FilterRegistrationBean jerseyFilterRegistration( javax.ws.rs.core.Application eurekaJerseyApp) { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new ServletContainer(eurekaJerseyApp)); bean.setOrder(Ordered.LOWEST_PRECEDENCE); bean.setUrlPatterns( Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*")); return bean; } //省略 ... }
從EurekaServerAutoConfiguration 類上的註解@Import(EurekaServerInitializerConfiguration.class) 可以到,例項化類EurekaServerAutoConfiguration之前,已經例項化了EurekaServerInitializerConfiguration類,程式碼如下:
@Configuration @CommonsLog public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered { // 此處省略部分程式碼 @Override public void start() { // 啟動一個執行緒 new Thread(new Runnable() { @Override public void run() { try { //初始化EurekaServer,同時啟動Eureka Server ,後面著重講這裡 eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext); log.info("Started Eureka Server"); // 釋出EurekaServer的註冊事件 publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig())); // 設定啟動的狀態為true EurekaServerInitializerConfiguration.this.running = true; // 傳送Eureka Start 事件 , 其他還有各種事件,我們可以監聽這種時間,然後做一些特定的業務需求,後面會講到。 publish(new EurekaServerStartedEvent(getEurekaServerConfig())); } catch (Exception ex) { // Help! log.error("Could not initialize Eureka servlet context", ex); } } }).start(); } // 此處省略部分程式碼 }
這個start方法中開啟了一個新的執行緒,然後進行一些Eureka Server的初始化工作,比如呼叫eurekaServerBootstrap的contextInitialized方法,EurekaServerBootstrap程式碼如下:
public class EurekaServerBootstrap {
// 此處省略部分程式碼
public void contextInitialized(ServletContext context) {
try {
// 初始化Eureka的環境變數
initEurekaEnvironment();
// 初始化Eureka的上下文
initEurekaServerContext();
context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
}
catch (Throwable e) {
log.error("Cannot bootstrap eureka server :", e);
throw new RuntimeException("Cannot bootstrap eureka server :", e);
}
}
protected void initEurekaEnvironment() throws Exception {
log.info("Setting the eureka configuration..");
String dataCenter = ConfigurationManager.getConfigInstance()
.getString(EUREKA_DATACENTER);
if (dataCenter == null) {
log.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);
}
String environment = ConfigurationManager.getConfigInstance()
.getString(EUREKA_ENVIRONMENT);
if (environment == null) {
ConfigurationManager.getConfigInstance()
.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
log.info(
"Eureka environment value eureka.environment is not set, defaulting to test");
}
else {
ConfigurationManager.getConfigInstance()
.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
}
}
protected void initEurekaServerContext() throws Exception {
// For backward compatibility
JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
XStream.PRIORITY_VERY_HIGH);
XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
XStream.PRIORITY_VERY_HIGH);
if (isAws(this.applicationInfoManager.getInfo())) {
this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
this.eurekaClientConfig, this.registry, this.applicationInfoManager);
this.awsBinder.start();
}
//初始化eureka server上下文
EurekaServerContextHolder.initialize(this.serverContext);
log.info("Initialized server context");
// Copy registry from neighboring eureka node
// 從相鄰的eureka節點複製登錄檔
int registryCount = this.registry.syncUp();
// 預設每30秒傳送心跳,1分鐘就是2次
// 修改eureka狀態為up
// 同時,這裡面會開啟一個定時任務,用於清理 60秒沒有心跳的客戶端。自動下線
this.registry.openForTraffic(this.applicationInfoManager, registryCount);
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
}
public void contextDestroyed(ServletContext context) {
try {
log.info("Shutting down Eureka Server..");
context.removeAttribute(EurekaServerContext.class.getName());
destroyEurekaServerContext();
destroyEurekaEnvironment();
}
catch (Throwable e) {
log.error("Error shutting down eureka", e);
}
log.info("Eureka Service is now shutdown...");
}
}
在初始化Eureka Server上下文環境後,就會繼續執行openForTraffic方法,這個方法主要是設定了期望每分鐘接收到的心跳次數,並將服務例項的狀態設定為UP,最後又通過方法postInit來開啟一個定時任務,用於每隔一段時間(預設60秒)將沒有續約的服務例項(預設90秒沒有續約)清理掉。openForTraffic的方法程式碼如下:
@Override
public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
// Renewals happen every 30 seconds and for a minute it should be a factor of 2.
// 計算每分鐘最大續約數
this.expectedNumberOfRenewsPerMin = count * 2;
// 計算每分鐘最小續約數
this.numberOfRenewsPerMinThreshold =
(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
logger.info("Got {} instances from neighboring DS node", count);
logger.info("Renew threshold is: {}", numberOfRenewsPerMinThreshold);
this.startupTime = System.currentTimeMillis();
if (count > 0) {
this.peerInstancesTransferEmptyOnStartup = false;
}
DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();
boolean isAws = Name.Amazon == selfName;
if (isAws && serverConfig.shouldPrimeAwsReplicaConnections()) {
logger.info("Priming AWS connections for all replicas..");
primeAwsReplicas(applicationInfoManager);
}
logger.info("Changing status to UP");
// 修改服務例項的狀態為UP
applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
// 開啟定時任務,每隔一段時間(預設60秒)將沒有續約的服務例項(預設90秒沒有續約)清理掉
super.postInit();
}
postInit方法開啟了一個新的定時任務,程式碼如下:
protected void postInit() {
renewsLastMin.start();
if (evictionTaskRef.get() != null) {
evictionTaskRef.get().cancel();
}
evictionTaskRef.set(new EvictionTask());
evictionTimer.schedule(evictionTaskRef.get(),
serverConfig.getEvictionIntervalTimerInMs(),
serverConfig.getEvictionIntervalTimerInMs());
}
這裡的時間間隔都來自於EurekaServerConfigBean類,可以在配置檔案中以eureka.server開頭的配置來進行設定。
參考
https://www.e-learn.cn/content/qita/775244/
https://nobodyiam.com/2016/06/25/dive-into-eureka/
https://blog.csdn.net/Lammonpeter/article/details/84330900
歡迎掃碼或微信搜尋公眾號《程式設計師果果》關注我,關注有驚喜~
相關推薦
Eureka 原始碼分析之 Eureka Server
文章首發於公眾號《程式設計師果果》 地址 : https://mp.weixin.qq.com/s/FfJrAGQuHyVrsedtbr0Ihw 簡介 上一篇文章《Eureka 原始碼分析之 Eureka Client》 通過原始碼知道 ,eureka Client 是通過 http rest來 與 eu
Eureka原始碼分析之Eureka Client獲取例項資訊流程
下方是Eureka Client從Eureka Server獲取例項資訊的總體流程圖,後面會詳細介紹每個步驟。 Eureka Client在剛啟動的時候會從Eureka Server全量獲取一次註冊資訊,同時初始化Eureka Client本地例項資訊快取定時更新任務,預設30s一次
Eureka原始碼分析之eureka-client
很多人對Eureka進行了原始碼分析,但是主要著重於Eureka server端的原始碼分析,本篇博文著重分析eureka-client的分析,先上圖看類結構(首先宣告以下內容為本人淺顯見解,如有不妥請指正批評)eureka客戶端核心jar包為以上截圖,其中核心的類包
Eureka 原始碼分析之 Eureka Client
文章首發於微信公眾號《程式設計師果果》 地址:https://mp.weixin.qq.com/s/47TUd96NMz67_PCDyvyInQ 簡介 Eureka是一種基於REST(Representational State Transfer)的服務,主要用於AWS雲,用於定位服務,以實現中間層伺服器
Spring Cloud原始碼分析之Eureka篇第六章:服務註冊
在文章《Spring Cloud原始碼分析之Eureka篇第四章:服務註冊是如何發起的 》的分析中,我們知道了作為Eureka Client的應用啟動時,在com.netflix.discovery.DiscoveryClient類的initScheduledT
Spring Cloud原始碼分析之Eureka篇第八章:服務註冊名稱的來歷
關於服務註冊名稱 服務註冊名稱,是指Eureka client註冊到Eureka server時,用於標記自己身份的標誌,舉例說明,以下是個簡單的Eureka client配置: server: port: 8082 spring: applicatio
Spring Cloud原始碼分析之Eureka篇第五章:更新服務列表
在上一章《Spring Cloud原始碼分析之Eureka篇第四章:服務註冊是如何發起的 》,我們知道了作為Eureka Client的應用啟動時,在com.netflix.discovery.DiscoveryClient類的initScheduledTask
Eureka原始碼分析:Eureka不會進行二次Replication的原因
Eureka不會進行二次同步註冊資訊 Eureka會將本例項中的註冊資訊同步到它的peer節點上,這是我們都知道的特性。然而,當peer節點收到同步資料後,並不會將這些資訊再同步到它自己的peer節點上。如果有A, B, C三個例項,A配B, B配C, C配A
Netflix Eureka原始碼分析(13)——eureka server的登錄檔多級快取過期機制:主動過期+定時過期+被動過期
(1)主動過期 readWriteCacheMap,讀寫快取 有新的服務例項發生註冊、下線、故障的時候,就會去重新整理readWriteCacheMap 比如說現在有一個服務A,ServiceA,有一個新的服務例項,Instance010來註冊了,註冊完了之後,其實必須
Netflix Eureka原始碼分析(18)——eureka server網路故障時的的自我保護機制原始碼剖析
假如說,20個服務例項,結果在1分鐘之內,只有8個服務例項保持了心跳 --> eureka server是應該將剩餘的12個沒有心跳的服務例項都摘除嗎? 這個時候很可能說的是,eureka server自己網路故障了,那些服務沒問題的。只不過eureka server
Netflix Eureka原始碼分析(19)——eureka server叢集機制原始碼剖析:登錄檔同步以及高可用
(1)eureka core的BootStrap裡面,有一塊程式碼,是PeerEurekaNodes的程式碼,其實是在處理eureka server叢集資訊的初始化,會執行PeerEurekaNodes.start()方法 public class EurekaBootSt
eureka原始碼解讀之服務端
剖析eureka服務端啟動流程 服務端啟動類-入口處 @EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(Strin
Netflix Eureka原始碼分析(3)——listener(EurekaBootStrap監聽類)分析
web.xml中的listener: <listener> <listener-class>com.netflix.eureka.EurekaBootStrap</listener-class> </listener>
Netflix Eureka原始碼分析(7)——eureka client啟動環境初始化流程
eureka-examples,有一個類,ExampleEurekaClient,就是一個自帶的例子,如果是一個eureka服務,一定會有一個eureka client,服務例項啟動的時候,一定會啟動eureka client,eureka client去向eureka se
Eureka原始碼分析-環境構建篇
承接上一篇文章《什麼是微服務》,我們已經對微服務有一定了解,並且以一個實現了註冊中心、服務提供者及消費者的例子作為文章的結尾,而本篇文章,主要介紹Eureka原始碼的環境構建及示例除錯。
Eureka高可用之Eureka Server複製機制:PeerAwareInstanceRegistryImpl
還是先提出幾個疑問,看本篇文章前最好看過Eureka高可用之Client重試機制:RetryableEurekaHttpClient,要知道Eureka Client只會向一個Server節點進行註冊(心跳、狀態改變等類似),註冊失敗時才會嘗試下一個server節點。當然正是由於這種機制,才會有
【SpringCloud Eureka原始碼】從Eureka Client發起註冊請求到Eureka Server處理的整個服務註冊過程(上)
目錄 Eureka Client啟動並呼叫Eureka Server的註冊介面 Spring Cloud Eureka的自動配置 @EnableDiscoveryClient EurekaDiscoveryClientConfiguration
UiAutomator系列——Appium Server 原始碼分析之啟動執行Express http伺服器(010)
通過上一個系列Appium Android Bootstrap原始碼分析我們瞭解到了appium在安卓目標機器上是如何通過bootstrap這個服務來接收appium從pc端傳送過來的命令,並最終使用uiautomator框架進行處理的。大家還沒有這方面的背景知識的話建議先
深入理解Spring cloud原始碼篇之Eureka原始碼
1.eureka功能分析 首先,eureka在springcloud中充當服務註冊功能,相當於dubbo+zk裡面得zk,但是比zk要簡單得多,zk可以做得東西太多了,包括分散式鎖,分散式佇列都是基於zk裡面得四種節點加watch機制通過長連線來
SpringCloud Eureka 原始碼分析
目錄 SpringCloud-Eureka 整合專案 spring-cloud-netflix-eureka-server spring-cloud-netflix-eureka-client Eureka架構圖