package com.dianping.pigeon.remoting.provider.listener; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Set; import com.dianping.pigeon.config.ConfigChangeListener; import com.dianping.pigeon.config.ConfigManager; import com.dianping.pigeon.config.ConfigManagerLoader; import com.dianping.pigeon.log.Logger; import com.dianping.pigeon.log.LoggerLoader; import com.dianping.pigeon.monitor.Monitor; import com.dianping.pigeon.monitor.MonitorLoader; import com.dianping.pigeon.registry.RegistryManager; import com.dianping.pigeon.remoting.common.util.Constants; import com.dianping.pigeon.remoting.provider.config.ProviderConfig; /** * Created by chenchongze on 15/12/4. */ public class HeartBeatListener extends Thread { private static final Logger logger = LoggerLoader.getLogger(HeartBeatListener.class); private static final ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private static final RegistryManager registryManager = RegistryManager.getInstance(); private static final Monitor monitor = MonitorLoader.getMonitor(); private static final Set<String> serviceHeartBeatCache = Collections.synchronizedSet(new HashSet<String>()); private static volatile int REFRESH_INTERVAL = configManager.getIntValue(Constants.KEY_PROVIDER_HEARTBEAT_INTERNAL, Constants.DEFAULT_PROVIDER_HEARTBEAT_INTERNAL); private static volatile HeartBeatListener heartBeatListener = null; static { registerConfigChangeListener(); } private boolean isSendHeartBeat; private final String serviceAddress; private HeartBeatListener(String threadName, Thread.UncaughtExceptionHandler uncaughtExceptionHandler, boolean isDaemon, String serviceAddress){ this.setName(threadName); this.setUncaughtExceptionHandler(uncaughtExceptionHandler); this.setDaemon(isDaemon); this.serviceAddress = serviceAddress; } public static void registerHeartBeat(ProviderConfig<?> providerConfig) { try { String serviceName = providerConfig.getUrl(); serviceHeartBeatCache.add(serviceName); if(heartBeatListener == null) { initHeartBeat(configManager.getLocalIp() + ":" + providerConfig.getServerConfig().getActualPort()); } } catch (Throwable t) { logger.error("Error while register heartbeat of service.", t); } } public static void unregisterHeartBeat(ProviderConfig<?> providerConfig) { try { String serviceName = providerConfig.getUrl(); serviceHeartBeatCache.remove(serviceName); if(serviceHeartBeatCache.size() == 0 && heartBeatListener != null) { stopHeartBeat(heartBeatListener.serviceAddress); } } catch (Throwable t) { logger.error("Error while unregister heartbeat of service.", t); } } private static synchronized void initHeartBeat(String serviceAddress) { if(heartBeatListener == null) { heartBeatListener = new HeartBeatListener("Pigeon-Provider-HeartBeat",new HeartBeatReboot(), true, serviceAddress); heartBeatListener.isSendHeartBeat = true; heartBeatListener.start(); //registryManager.registerAppHostList(serviceAddress, configManager.getAppName(), ProviderBootStrap.getHttpServer().getPort()); monitor.logEvent("PigeonService.heartbeat", "ON", new Date()+""); } } private static synchronized void stopHeartBeat(String serviceAddress) { if(serviceHeartBeatCache.size() == 0 && heartBeatListener != null) { heartBeatListener.isSendHeartBeat = false; heartBeatListener = null; registryManager.deleteHeartBeat(serviceAddress); //registryManager.unregisterAppHostList(serviceAddress, configManager.getAppName()); monitor.logEvent("PigeonService.heartbeat", "OFF", new Date()+""); } } @Override public void run() { try { while (this.equals(heartBeatListener) && isSendHeartBeat) { Long heartbeat = System.currentTimeMillis(); // 写心跳 if(serviceHeartBeatCache.size() > 0) { registryManager.updateHeartBeat(serviceAddress, heartbeat); } Long internal = REFRESH_INTERVAL - System.currentTimeMillis() + heartbeat; if(internal > 0) { Thread.sleep(internal); } } } catch (Throwable e) { tryRestartThread(this, e); } finally { // release resources if needed } } private static void tryRestartThread(Thread t, Throwable thrown) { logger.error("heartbeat thread terminated with exception: " + t.getName(), thrown); logger.info("Thread status: " + t.getState()); logger.info("trying to start a new thread."); // 等待之后重启心跳线程 try { Thread.sleep(REFRESH_INTERVAL); initHeartBeat(heartBeatListener.serviceAddress); } catch (Exception e) { logger.fatal("HeartBeat restart failed! Please check!", e); heartBeatListener = null; } } private static class HeartBeatReboot implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { tryRestartThread(t, e); } } private static void registerConfigChangeListener(){ configManager.registerConfigChangeListener(new ConfigChangeListener() { @Override public void onKeyUpdated(String key, String value) { if (Constants.KEY_PROVIDER_HEARTBEAT_INTERNAL.equals(key)) { try { REFRESH_INTERVAL = Integer.parseInt(value); } catch (NumberFormatException e) { logger.info("failed to change heartbeat refresh internal, please check value: " + value); } } } @Override public void onKeyAdded(String key, String value) { } @Override public void onKeyRemoved(String key) { } }); } }