eureka監聽各服務狀態,下線、重連等,並做相應的處理
阿新 • • 發佈:2019-01-10
在一些場景下,我們需要監聽eureka服務中心的一些狀態,譬如某個微服務掛掉了,我們希望能監聽到,並給管理員傳送郵件通知。
Eureka的server端會發出5個事件通知,分別是:
EurekaInstanceCanceledEvent 服務下線事件
EurekaInstanceRegisteredEvent 服務註冊事件
EurekaInstanceRenewedEvent 服務續約事件
EurekaRegistryAvailableEvent Eureka註冊中心啟動事件
EurekaServerStartedEvent Eureka Server啟動事件
我們可以從原始碼中很方便地看到
eureka的server端原始碼一共也沒幾個類,Controller是給介面用的,還有一些配置類,我們簡單來看幾個。
@Configuration @CommonsLog public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered { ... @Override public void start() { new Thread(new Runnable() { @Override public void run() { try { //TODO: is this class even needed now? // 初始化eureka server eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext); log.info("Started Eureka Server"); // 傳遞eureka註冊事件,找來找去沒看到有listen去處理這件事件 // spring包中沒有去處理,如果我們有需要可以自己處理 // https://github.com/spring-cloud/spring-cloud-netflix/issues/1726 publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig())); // 設定在spring容器關閉的時候執行stop方法 EurekaServerInitializerConfiguration.this.running = true; publish(new EurekaServerStartedEvent(getEurekaServerConfig())); } catch (Exception ex) { // Help! log.error("Could not initialize Eureka servlet context", ex); } } }).start(); } @Bean public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry, EurekaServerContext serverContext) { return new EurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry, serverContext); } ... }
@CommonsLog 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.."); //如果在雲環境中執行,需要傳遞java命令列屬性-Deureka.datacenter = cloud,以便Eureka客戶端/伺服器知道初始化AWS雲特定的資訊 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); } //eureka 執行環境,java命令列屬性-Deureka.environment=eureka-client-{test,prod} 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); } } // eureka使用XStream來對json、xml格式的轉換 protected void initEurekaServerContext() throws Exception { // 向後相容 //註冊狀態轉換器 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"); // 從相鄰的eureka節點複製登錄檔 int registryCount = this.registry.syncUp(); // 預設每30秒傳送心跳,1分鐘就是2次 // 修改eureka狀態為up this.registry.openForTraffic(this.applicationInfoManager, registryCount); // 註冊所有監控 EurekaMonitors.registerAllStats(); } ... }
public class InstanceRegistry extends PeerAwareInstanceRegistryImpl implements ApplicationContextAware {
private static final Log log = LogFactory.getLog(InstanceRegistry.class);
private ApplicationContext ctxt;
private int defaultOpenForTrafficCount;
public InstanceRegistry(EurekaServerConfig serverConfig, EurekaClientConfig clientConfig, ServerCodecs serverCodecs, EurekaClient eurekaClient, int expectedNumberOfRenewsPerMin, int defaultOpenForTrafficCount) {
super(serverConfig, clientConfig, serverCodecs, eurekaClient);
this.expectedNumberOfRenewsPerMin = expectedNumberOfRenewsPerMin;
this.defaultOpenForTrafficCount = defaultOpenForTrafficCount;
}
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.ctxt = context;
}
public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
super.openForTraffic(applicationInfoManager, count == 0 ? this.defaultOpenForTrafficCount : count);
}
public void register(InstanceInfo info, int leaseDuration, boolean isReplication) {
this.handleRegistration(info, leaseDuration, isReplication);
super.register(info, leaseDuration, isReplication);
}
public void register(InstanceInfo info, boolean isReplication) {
this.handleRegistration(info, this.resolveInstanceLeaseDuration(info), isReplication);
super.register(info, isReplication);
}
public boolean cancel(String appName, String serverId, boolean isReplication) {
this.handleCancelation(appName, serverId, isReplication);
return super.cancel(appName, serverId, isReplication);
}
public boolean renew(String appName, String serverId, boolean isReplication) {
this.log("renew " + appName + " serverId " + serverId + ", isReplication {}" + isReplication);
List<Application> applications = this.getSortedApplications();
Iterator var5 = applications.iterator();
while(var5.hasNext()) {
Application input = (Application)var5.next();
if (input.getName().equals(appName)) {
InstanceInfo instance = null;
Iterator var8 = input.getInstances().iterator();
while(var8.hasNext()) {
InstanceInfo info = (InstanceInfo)var8.next();
if (info.getId().equals(serverId)) {
instance = info;
break;
}
}
//這裡釋出重連事件
this.publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId, instance, isReplication));
break;
}
}
return super.renew(appName, serverId, isReplication);
}
protected boolean internalCancel(String appName, String id, boolean isReplication) {
this.handleCancelation(appName, id, isReplication);
return super.internalCancel(appName, id, isReplication);
}
private void handleCancelation(String appName, String id, boolean isReplication) {
this.log("cancel " + appName + ", serverId " + id + ", isReplication " + isReplication);
this.publishEvent(new EurekaInstanceCanceledEvent(this, appName, id, isReplication));
}
private void handleRegistration(InstanceInfo info, int leaseDuration, boolean isReplication) {
this.log("register " + info.getAppName() + ", vip " + info.getVIPAddress() + ", leaseDuration " + leaseDuration + ", isReplication " + isReplication);
this.publishEvent(new EurekaInstanceRegisteredEvent(this, info, leaseDuration, isReplication));
}
private void log(String message) {
if (log.isDebugEnabled()) {
log.debug(message);
}
}
private void publishEvent(ApplicationEvent applicationEvent) {
this.ctxt.publishEvent(applicationEvent);
}
private int resolveInstanceLeaseDuration(InstanceInfo info) {
int leaseDuration = 90;
if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
leaseDuration = info.getLeaseInfo().getDurationInSecs();
}
return leaseDuration;
}
}
差不多能用上的就這幾個類了,這5個server端事件在這幾個類中都能找到。知道了事件名,要監聽就很簡單了
package com.tianyalei.server;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRenewedEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaRegistryAvailableEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaServerStartedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* Created by wuweifeng on 2017/10/10.
*/
@Component
public class EurekaStateChangeListener {
@EventListener
public void listen(EurekaInstanceCanceledEvent eurekaInstanceCanceledEvent) {
//服務斷線事件
String appName = eurekaInstanceCanceledEvent.getAppName();
String serverId = eurekaInstanceCanceledEvent.getServerId();
System.out.println(appName);
System.out.println(serverId);
}
@EventListener
public void listen(EurekaInstanceRegisteredEvent event) {
InstanceInfo instanceInfo = event.getInstanceInfo();
System.out.println(instanceInfo);
}
@EventListener
public void listen(EurekaInstanceRenewedEvent event) {
event.getAppName();
event.getServerId();
}
@EventListener
public void listen(EurekaRegistryAvailableEvent event) {
}
@EventListener
public void listen(EurekaServerStartedEvent event) {
//Server啟動
}
}
在server專案里加上這個監聽類,然後在各個事件被觸發時就能在這裡監聽並處理了。譬如我啟動一個client
在這裡就能看到新註冊的client的各項詳細資訊。
然後我再關掉client
可以看到掛掉的client的appName,serverId等資訊。那麼我們就可以在這裡做一些處理,譬如郵件通知管理員,某某服務掛掉了。