eureka监听各服务状态,下线、重连等,并做相应的处理

来源:互联网 发布:快手直播人气软件 编辑:程序博客网 时间:2024/05/19 17:02

在一些场景下,我们需要监听eureka服务中心的一些状态,譬如某个微服务挂掉了,我们希望能监听到,并给管理员发送邮件通知。

Eureka的server端会发出5个事件通知,分别是:

EurekaInstanceCanceledEvent 服务下线事件
EurekaInstanceRegisteredEvent 服务注册事件
EurekaInstanceRenewedEvent 服务续约事件
EurekaRegistryAvailableEvent Eureka注册中心启动事件
EurekaServerStartedEvent Eureka Server启动事件

我们可以从源码中很方便地看到

eureka的server端源码一共也没几个类,Controller是给界面用的,还有一些配置类,我们简单来看几个。


@Configuration@CommonsLogpublic 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);    }    ...}

@CommonsLogpublic 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. */@Componentpublic 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等信息。那么我们就可以在这里做一些处理,譬如邮件通知管理员,某某服务挂掉了。



阅读全文
0 0