扩展注册中心,实现事件发布监控服务上下线

本文介绍了一个用于监听和处理Eureka服务注册中心实例状态变化的抽象观察者类。该类能够缓存事件,支持手动重试,并通过RxJava进行事件消费与自动重试。同时,文章展示了如何通过修改Application类的方法实现对实例添加和移除的监听。
摘要由CSDN通过智能技术生成
package io.choerodon.eureka.event;

import com.netflix.appinfo.InstanceInfo;
import io.choerodon.eureka.event.endpoint.EurekaEventService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import rx.schedulers.Schedulers;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 监听eureka实例启动事件的观察者类
 */
public abstract class AbstractEurekaEventObserver implements Observer, EurekaEventService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractEurekaEventObserver.class);

    private final AntPathMatcher matcher = new AntPathMatcher();

    private final LinkedList<EurekaEventPayload> eventCache = new SynchronizedLinkedList<>(new LinkedList<>());

    @Autowired
    private EurekaEventProperties properties;

    public AbstractEurekaEventObserver() {
        EurekaEventHandler.getInstance().getObservable().addObserver(this);
    }

    public void setProperties(EurekaEventProperties properties) {
        this.properties = properties;
    }

    public List<EurekaEventPayload> getEventCache() {
        return eventCache;
    }

    @Override
    public List<EurekaEventPayload> unfinishedEvents(String service) {
        if (StringUtils.isEmpty(service)) {
            return eventCache;
        }
        List<EurekaEventPayload> payloads = new ArrayList<>(4);
        for (EurekaEventPayload payload : eventCache) {
            if (service.equalsIgnoreCase(payload.getAppName())) {
                payloads.add(payload);
            }
        }
        return payloads;
    }

    @Override
    public List<EurekaEventPayload> retryEvents(String id, String service) {
        if (!StringUtils.isEmpty(id)) {
            return Collections.singletonList(manualRetryEventById(id));
        }
        if (!StringUtils.isEmpty(service)) {
            return manualRetryEventsByService(service);
        }
        return manualRetryAllEvents();
    }

    private EurekaEventPayload manualRetryEventById(String id) {
        List<EurekaEventPayload> payloads = new ArrayList<>(eventCache);
        for (EurekaEventPayload payload : payloads) {
            if (id.equals(payload.getId())) {
                consumerEvent(payload);
                return payload;
            }
        }
        return null;
    }

    private List<EurekaEventPayload> manualRetryEventsByService(String service) {
        List<EurekaEventPayload> returnPayloads = new ArrayList<>();
        List<EurekaEventPayload> payloads = new ArrayList<>(eventCache);
        for (EurekaEventPayload payload : payloads) {
            if (service.equalsIgnoreCase(payload.getAppName())) {
                consumerEvent(payload);
                returnPayloads.add(payload);
            }
        }
        return returnPayloads;
    }

    private List<EurekaEventPayload> manualRetryAllEvents() {
        List<EurekaEventPayload> returnPayloads = new ArrayList<>();
        List<EurekaEventPayload> payloads = new ArrayList<>(eventCache);
        for (EurekaEventPayload payload : payloads) {
            consumerEvent(payload);
            returnPayloads.add(payload);
        }
        return returnPayloads;
    }

    @Override
    public void update(Observable o, Object arg) {
        if (arg instanceof EurekaEventPayload) {
            EurekaEventPayload payload = (EurekaEventPayload) arg;
            if (Arrays.stream(properties.getSkipServices()).anyMatch(t -> matcher.match(t, payload.getAppName()))) {
                LOGGER.info("Skip event that is skipServices, {}", payload);
                return;
            }
            if (InstanceInfo.InstanceStatus.UP.name().equals(payload.getStatus())) {
                LOGGER.info("Receive UP event, payload: {}", payload);
            } else {
                LOGGER.info("Receive DOWN event, payload: {}", payload);
            }
            putPayloadInCache(payload);
            consumerEventWithAutoRetry(payload);
        }
    }


    public void putPayloadInCache(final EurekaEventPayload payload) {
        if (eventCache.size() >= properties.getMaxCacheSize()) {
            LOGGER.warn("Remove first payload because of maxCacheSize limit, remove payload: {}", eventCache.getFirst());
            eventCache.removeFirst();
        }
        eventCache.add(payload);
    }

    private void consumerEvent(final EurekaEventPayload payload) {
        if (InstanceInfo.InstanceStatus.UP.name().equals(payload.getStatus())) {
            LOGGER.debug("Consumer UP event, payload: {}", payload);
            receiveUpEvent(payload);
        } else {
            LOGGER.debug("Consumer DOWN event, payload: {}", payload);
            receiveDownEvent(payload);
        }
        eventCache.remove(payload);
    }

    private void consumerEventWithAutoRetry(final EurekaEventPayload payload) {
        rx.Observable.just(payload)
                .map(t -> {
                    consumerEvent(payload);
                    return payload;
                }).retryWhen(x -> x.zipWith(rx.Observable.range(1, properties.getRetryTime()),
                (t, retryCount) -> {
                    if (retryCount >= properties.getRetryTime()) {
                        if (t instanceof RemoteAccessException || t instanceof RestClientException) {
                            LOGGER.warn("error.eurekaEventObserver.fetchError, payload {}", payload, t);
                        } else {
                            LOGGER.warn("error.eurekaEventObserver.consumerError, payload {}", payload, t);
                        }
                    }
                    return retryCount;
                }).flatMap(y -> rx.Observable.timer(properties.getRetryInterval(), TimeUnit.SECONDS)))
                .subscribeOn(Schedulers.io())
                .subscribe((EurekaEventPayload payload1) -> {
                });
    }

    public abstract void receiveUpEvent(EurekaEventPayload payload);

    public abstract void receiveDownEvent(EurekaEventPayload payload);

}

 ClassPool classPool = new ClassPool(true);

修改源代码执行

package io.choerodon.eureka.event;

import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.Applications;
import javassist.*;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.netflix.discovery.shared.Application;
public class EurekaEventHandler {

    private final Set<String> serviceInstanceIds = Collections.synchronizedSet(new HashSet<>());

    private static EurekaEventHandler instance = new EurekaEventHandler();

    private EurekaEventObservable observable = new EurekaEventObservable();

    private static final String APPLICATION_PATH = "com.netflix.discovery.shared.Application";

    private EurekaEventHandler() {
    }

    public Set<String> getServiceInstanceIds() {
        return serviceInstanceIds;
    }

    public static EurekaEventHandler getInstance() {
        return instance;
    }

    public void eurekaAddInstance(final InstanceInfo instanceInfo) {
        if (instanceInfo.getStatus() == InstanceInfo.InstanceStatus.UP  && !serviceInstanceIds.contains(instanceInfo.getId())) {
            serviceInstanceIds.add(instanceInfo.getId());
            observable.sendEvent(new EurekaEventPayload(instanceInfo));
        }
    }

    public void eurekaRemoveInstance(final InstanceInfo instanceInfo) {
        if (instanceInfo.getStatus() == InstanceInfo.InstanceStatus.DOWN
                && serviceInstanceIds.contains(instanceInfo.getId())) {
            serviceInstanceIds.remove(instanceInfo.getId());
            observable.sendEvent(new EurekaEventPayload(instanceInfo));
        }
    }

    public EurekaEventObservable getObservable() {
        return observable;
    }

    public void init() {
        try {
            ClassPool classPool = new ClassPool(true);
            //添加com.netflix.discovery包的扫描路径
            ClassClassPath classPath = new ClassClassPath(Applications.class);
            classPool.insertClassPath(classPath);
            //获取要修改Application类
            CtClass ctClass = classPool.get(APPLICATION_PATH);
            //获取addInstance方法
            CtMethod addInstanceMethod = ctClass.getDeclaredMethod("addInstance");
            //修改addInstance方法
            addInstanceMethod.setBody("{instancesMap.put($1.getId(), $1);"
                    + "synchronized (instances) {io.choerodon.eureka.event.EurekaEventHandler.getInstance().eurekaAddInstance($1);" +
                    "instances.remove($1);instances.add($1);isDirty = true;}}");
            //获取removeInstance方法
            CtMethod removeInstanceMethod = ctClass.getDeclaredMethod("removeInstance");
            //修改removeInstance方法
            removeInstanceMethod.setBody("{io.choerodon.eureka.event.EurekaEventHandler.getInstance().eurekaRemoveInstance($1);this.removeInstance($1, true);}");
            //覆盖原有的Application类
            ctClass.toClass();
            //使用类加载器重新加载Application类
            classPool.getClassLoader().loadClass(APPLICATION_PATH);
            Class.forName(APPLICATION_PATH);
        } catch (Exception e) {
            throw new EurekaEventException(e);
        }

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值