
net.gdface.facelog.hb.DeviceHeartbeat Maven / Gradle / Ivy
package net.gdface.facelog.hb;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Observable;
import java.util.Observer;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import static com.google.common.base.Preconditions.*;
import gu.simplemq.Channel;
import gu.simplemq.IPublisher;
import gu.simplemq.MQRuntimeException;
import gu.simplemq.MessageQueueFactorys;
import net.gdface.facelog.ChannelConstant;
import net.gdface.facelog.mq.BaseServiceHeartbeatListener;
import net.gdface.facelog.mq.DeviceHeartdbeatPackage;
import net.gdface.facelog.mq.ServiceHeartbeatPackage;
import static gu.dtalk.engine.DeviceUtils.DEVINFO_PROVIDER;
/**
* 设备心跳包redis实现
* 以{@link #intervalMills}指定的周期向redis表({@link ChannelConstant#TABLE_HEARTBEAT})写入当前设备序列号及报道时间.
* 如果指定了心跳实时监控通道({@link #setMonitorChannelSupplier(Supplier)}),还会向该通道(频道)发布订阅消息
* 调用{@link #start()}心跳开始
* 应用程序结束时心跳包线程自动停止
* @author guyadong
*
*/
public class DeviceHeartbeat extends BaseServiceHeartbeatListener implements ChannelConstant{
public static final Logger logger = LoggerFactory.getLogger(DeviceHeartbeat.class);
/** 单实例 */
private static DeviceHeartbeat heartbeat;
private boolean stoped = false;
/** 心跳周期(毫秒) */
private long intervalMills = TimeUnit.MILLISECONDS.convert(DEFAULT_HEARTBEAT_PERIOD,TimeUnit.SECONDS);
/**
* 提供设备心跳实时监控通道名,如果指定了通道名,
* 每次心跳都不仅会向{@link ChannelConstant#TABLE_HEARTBEAT} 写入心跳报告,还会向该频道发布订阅消息
*/
private final MonitorChannelSupplier monitorChannelSupplier = new MonitorChannelSupplier();
private final DeviceHeartdbeatPackage heartBeatPackage;
private final IPublisher publisher;
/** 执行定时任务的线程池对象 */
private final ScheduledThreadPoolExecutor scheduledExecutor;
/** {@link #scheduledExecutor}的自动退出封装 */
private final ScheduledExecutorService timerExecutor;
private ScheduledFuture> future;
/** 附加任务表,执行定时任务发送心跳包时执行附加任务表中的任务对象 */
private final LinkedHashMap additionalTasks = Maps.newLinkedHashMap();
private final Observable mqErrorObservable = new Observable(){
@Override
public void notifyObservers(Object arg) {
setChanged();
try {
super.notifyObservers(arg);
} finally {
clearChanged();
}
}
};
/** 定时任务 */
private final Runnable timerTask = new Runnable(){
@Override
public void run() {
try {
if(!stoped){
heartBeatPackage.setHostAddress(DEVINFO_PROVIDER.getIpAsString());
Channel monitorChannel = monitorChannelSupplier.get();
if(null != monitorChannel){
try {
publisher.publish(monitorChannel, heartBeatPackage);
} catch(MQRuntimeException e){
// 消息发送失败通知侦听器
try {
mqErrorObservable.notifyObservers(e);
} catch (Throwable e2) {
logger.error("raise exception :{} while send heartbeat package,but FAIL to notify observers caused by: {}",e.getClass().getSimpleName(), e2.getMessage());
}
}
}
synchronized (additionalTasks) {
for(Iterator> itor = additionalTasks.entrySet().iterator(); itor.hasNext(); ){
Entry entry = itor.next();
try {
entry.getValue().run();
} catch (Exception e) {
itor.remove();
logger.info("additionalTask [{}] removed:caused by {}",entry.getKey(),e.getMessage());
}
}
}
}
} catch (Throwable e) {
logger.info(e.getMessage());
}
}};
/**
* 构造方法
* @param deviceID 当前设备ID
* @throws NullPointerException {@code poolLazy}为{@code null}
*/
private DeviceHeartbeat(int deviceID) {
this.heartBeatPackage = new DeviceHeartdbeatPackage().setDeviceId(deviceID);
this.publisher = MessageQueueFactorys.getDefaultFactory().getPublisher();
this.scheduledExecutor =new ScheduledThreadPoolExecutor(1,
new ThreadFactoryBuilder().setNameFormat("heartbeat-pool-%d").build());
this.timerExecutor = MoreExecutors.getExitingScheduledExecutorService( scheduledExecutor);
}
/**
* 创建{@link DeviceHeartbeat}实例
* {@link DeviceHeartbeat}为单实例,该方法只能调用一次。
* @param deviceID 设备ID
* @return {@link DeviceHeartbeat}实例
* @throws IllegalStateException 实例已经创建
*/
public static synchronized final DeviceHeartbeat makeHeartbeat(int deviceID){
checkState(null == heartbeat,"singleton instance created");
heartbeat = new DeviceHeartbeat(deviceID);
return heartbeat;
}
/**
* 返回已经创建的{@link DeviceHeartbeat}实例,如果实例还没有创建则抛出异常
* @throws IllegalStateException 实例还没有创建
*/
public static final DeviceHeartbeat getInstance(){
checkState(null !=heartbeat,"singleton instance be not yet created,call makeHeartbeat method firstly");
return heartbeat;
}
/**
* 设置设备心跳包发送周期
* 设置后须调用{@link #start()}才能生效
* @param period 心跳周期(大于0有效)
* @param unit
* @return 当前对象
*/
public DeviceHeartbeat setInterval(long period,TimeUnit unit){
if(period > 0 ){
this.intervalMills = TimeUnit.MILLISECONDS.convert(period, checkNotNull(unit));
}
return this;
}
/**
* 设置设备当前工作状态
* @param status
* @return 当前对象
* @see net.gdface.facelog.mq.DeviceHeartdbeatPackage#setStatus(int)
*/
public DeviceHeartbeat setStatus(int status) {
heartBeatPackage.setStatus(status);
return this;
}
/**
* @return 返回设备当前工作状态
* @see net.gdface.facelog.mq.DeviceHeartdbeatPackage#getStatus()
*/
public int getStatus() {
return heartBeatPackage.getStatus();
}
/**
* @see net.gdface.facelog.mq.DeviceHeartdbeatPackage#getFeature()
*/
public int getFeature() {
return heartBeatPackage.getFeature();
}
/**
* 设置当前设备的工作特性
* @param feature
* @see net.gdface.facelog.mq.DeviceHeartdbeatPackage#setFeature(int)
*/
public void setFeature(int feature) {
heartBeatPackage.setFeature(feature);
}
private class MonitorChannelSupplier implements Supplier>{
Channel channel;
Supplier channelSupplier;
boolean reload = true;
String hbChannel = null;
@Override
public Channel get() {
if(null == channel || reload){
if(null != channelSupplier){
String n = channelSupplier.get();
if(!Strings.isNullOrEmpty(n)){
// 通道名变化了则输出日志
if(!Objects.equal(hbChannel, n)){
logger.info("Device Heartbeat channel changed:{}->{}",hbChannel,n);
hbChannel = n;
channel = new Channel(hbChannel){};
reload = false;
}
}
}
}
return channel;
}
}
/**
* 设置提供设备心跳实时监控通道名的{@link Supplier}实例
* @param channelSupplier
* @return 当前 {@link DeviceHeartbeat}实例
*/
public DeviceHeartbeat setMonitorChannelSupplier(Supplier channelSupplier){
this.monitorChannelSupplier.channelSupplier = checkNotNull(channelSupplier,"channelSupplier is null");
return this;
}
/**
* 用指定的心跳周期参数({@link #intervalMills})启动心跳包报告定时任务
* 如果定时任务已经启动则先取消当前的定时任务再启动一个新的定时任务,确保只有一个定时任务在执行
*/
public synchronized void start(){
if(null != future){
this.scheduledExecutor.remove((Runnable) future);
}
/** 返回 RunnableScheduledFuture>实例 */
future = this.timerExecutor.scheduleAtFixedRate(timerTask, intervalMills, intervalMills, TimeUnit.MILLISECONDS);
}
public synchronized void stop(){
if(null != future){
this.scheduledExecutor.remove((Runnable) future);
future = null;
stoped = true;
}
}
@Override
protected boolean doServiceOnline(ServiceHeartbeatPackage heartbeatPackage) {
monitorChannelSupplier.reload = true;
return true;
}
/**
* 添加附加任务
* 附加任务在执行时抛出异常则自动被移除不再执行
* @param name 任务名,不可为{@code null},如果同名任务已经存在则覆盖
* @param task 任务对象,不可为{@code null}
* @return 返回{@code name}之前关联的任务对象,如果{@code name}为新加入任务名则返回{@code null}
*/
public Runnable addAdditionalTask(String name,Runnable task) {
checkArgument(null != name,"name is null");
checkArgument(null != task,"task is null");
synchronized (additionalTasks) {
return additionalTasks.put(name,task);
}
}
/**
* 删除附加任务
* @param task
*/
public void removeAdditionalTask(Runnable task) {
synchronized (additionalTasks) {
if(null != task){
for(Iterator> itor = additionalTasks.entrySet().iterator();itor.hasNext();){
Entry entry = itor.next();
if(task == entry.getValue()){
itor.remove();
}
}
}
}
}
/**
* 删除{@code name}指定附加任务
* @param name 任务名
*/
public void removeAdditionalTask(String name) {
if(null != name){
synchronized (additionalTasks) {
additionalTasks.remove(name);
}
}
}
/**
* 清除所有附加任务
*/
public void clear() {
synchronized (additionalTasks) {
additionalTasks.clear();
}
}
/**
* 添加消息系统异常侦听器
* {@link Observer#update(Observable, Object)}传入的第二个参数类型为{@link gu.simplemq.MQRuntimeException}
* @param o
* @return 当前对象
*/
public DeviceHeartbeat addMQErrorObserver(Observer o) {
mqErrorObservable.addObserver(o);
return this;
}
/**
* 删除消息系统异常侦听器
* @param o
* @return 当前对象
*/
public DeviceHeartbeat deleteMQErrorObserver(Observer o) {
mqErrorObservable.deleteObserver(o);
return this;
}
}