com.github.houbbbbb.sso.nt.handler.HeartbeatServerHandler Maven / Gradle / Ivy
The newest version!
package com.github.houbbbbb.sso.nt.handler;
import com.github.houbbbbb.sso.config.SSOPFilterConfig;
import com.github.houbbbbb.sso.nt.constants.CacheConstants;
import com.github.houbbbbb.sso.nt.constants.CommonConstants;
import com.github.houbbbbb.sso.nt.entity.AppDTO;
import com.github.houbbbbb.sso.nt.opt.CacheOpt;
import com.github.houbbbbb.sso.nt.util.DebugUtil;
import com.github.houbbbbb.sso.nt.util.HandlerUtils;
import com.github.houbbbbb.sso.util.GsonUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import lombok.Getter;
import lombok.ToString;
import java.net.InetSocketAddress;
import java.util.*;
/**
* @todo:
* @author: hbw
* @date: 2020/7/8
**/
@ChannelHandler.Sharable
public class HeartbeatServerHandler extends ServerChannelHandler {
private static Integer timesLimit;
private static Long timeout;
public HeartbeatServerHandler (SSOPFilterConfig ssopFilterConfig) {
timesLimit = ssopFilterConfig.getTickTimedOutTimes();
timeout = ssopFilterConfig.getTickTimedOut();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Handler tickHandler = HandlerFactory.getHandler(HandlerType.TICK_HANDLER);
ReadInfo readInfo = tickHandler.getReadInfo(ctx, msg);
tickHandler.handle(readInfo);
tickHandler.resp(ctx, readInfo);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
DebugUtil.debug("exception: " + cause.getMessage(), address.getAddress().getHostAddress() + " : " + address.getPort());
DebugUtil.warn("exception: " + cause.getMessage(), address.getAddress().getHostAddress() + " : " + address.getPort());
ctx.close();
}
@Getter
@ToString
static class HostInfo {
private String hostName;
private String ip;
private Integer port;
private Long time;
public HostInfo setHostName(String hostName) {
this.hostName = hostName;
return this;
}
public HostInfo setIp (String ip) {
this.ip = ip;
return this;
}
public HostInfo setPort (Integer port) {
this.port = port;
return this;
}
public HostInfo setTime(Long time) {
this.time = time;
return this;
}
public static HostInfo create () {
return new HostInfo();
}
private HostInfo () {}
}
@ToString
@Getter
static class ReadInfo {
private HostInfo hostInfo;
private String rd;
public ReadInfo setHostInfo(HostInfo hostInfo) {
this.hostInfo = hostInfo;
return this;
}
public ReadInfo setRd(String rd) {
this.rd = rd;
return this;
}
public static ReadInfo create () {
return new ReadInfo();
}
private ReadInfo () {}
}
public static class TickHandler implements Handler {
public static final String UNDERLINE = "_";
@Override
public void handle (ReadInfo readInfo) {
record(readInfo);
calculate(readInfo);
}
@Override
public ReadInfo getReadInfo(ChannelHandlerContext ctx, Object msg) {
String rd = HandlerUtils.rd(msg);
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
// hostName不在这里获取
//String hostName = address.getHostName();
AppDTO appDTO = GsonUtil.toObj(rd, AppDTO.class);
String ip = address.getAddress().getHostAddress();
HostInfo hostInfo = HostInfo.create().setHostName(appDTO.getHostName()).setIp(ip).setTime(System.currentTimeMillis());
return ReadInfo.create().setHostInfo(hostInfo).setRd(rd);
}
@Override
public void resp(ChannelHandlerContext ctx, ReadInfo readInfo) {
int size = CacheConstants.LINK_CACHE.size();
String key = getKey(readInfo.getHostInfo());
String wt = CommonConstants.APP_INFO;
if (!CacheConstants.LINK_COUNT.containsKey(key)) {
CacheConstants.LINK_COUNT.put(key, 0);
}
if (CacheConstants.LINK_COUNT.get(key) != size) {
CacheConstants.LINK_COUNT.put(key, size);
List appDTOS = new ArrayList<>(16);
CacheConstants.LINK_CACHE.forEach((a, b) -> {
AppDTO appDTO = CacheConstants.INFO_CACHE.get(a);
if (null != appDTO) {
appDTOS.add(appDTO);
}
});
wt = GsonUtil.toJson(appDTOS);
}
setUpdate(readInfo);
String re = getClearCache(readInfo, wt);
if(null != re){
wt = re;
}
HandlerUtils.wt(ctx, wt);
}
/**
* 返回要清除的本地缓存的session的列表
* @return
*/
private String getClearCache(ReadInfo readInfo, String wt){
if(!CommonConstants.APP_INFO.equals(wt)){
return null;
}
String currentKey = getKey(readInfo.getHostInfo());
Map> map = new HashMap<>(CacheConstants.UPDATE_CACHE);
Set sessionList = new HashSet<>(16);
if(map.size() > 0){
List removeList = new ArrayList<>(10);
map.forEach((key, value) -> {
if(value.contains(currentKey)){
value.remove(currentKey);
sessionList.add(key);
if(value.size() == 0){
removeList.add(key);
}
}
});
removeList.forEach(CacheConstants.UPDATE_CACHE::remove);
}
if(sessionList.size() > 0) {
return CacheOpt.setPrefix(GsonUtil.toJson(sessionList));
}
return null;
}
/**
* 将登出信息加入到UPDATE_CACHE中
* @param readInfo
*/
private void setUpdate(ReadInfo readInfo){
String rd = readInfo.getRd();
AppDTO appDTO = GsonUtil.toObj(rd, AppDTO.class);
String update = appDTO.getUdpate();
if(null == update){
return;
}
String session = CacheOpt.getSession(update);
CacheConstants.UPDATE_CACHE.put(session, CacheConstants.INFO_CACHE.keySet());
CacheConstants.UPDATE_CACHE.get(session).remove(getKey(readInfo.getHostInfo()));
if(CacheConstants.UPDATE_CACHE.get(session).size() == 0){
CacheConstants.UPDATE_CACHE.remove(session);
}
}
private void record (ReadInfo readInfo) {
String rd = readInfo.getRd();
HostInfo hostInfo = readInfo.getHostInfo();
AppDTO appDTO = GsonUtil.toObj(rd, AppDTO.class);
appDTO = appDTO.setHostName(hostInfo.getHostName()).setIp(hostInfo.getIp());
hostInfo.setPort(appDTO.getPort());
CacheConstants.INFO_CACHE.put(getKey(hostInfo), appDTO);
}
private String getKey (HostInfo hostInfo) {
StringBuilder sb = new StringBuilder();
sb.append(hostInfo.getIp()).append(UNDERLINE).append(hostInfo.getPort());
String key = sb.toString();
return key;
}
private String getValue (Integer times, HostInfo hostInfo) {
StringBuilder sb = new StringBuilder();
sb.append(hostInfo.getTime()).append(UNDERLINE).append(times);
String value = sb.toString();
return value;
}
private void calculate (ReadInfo readInfo) {
HostInfo hostInfo = readInfo.getHostInfo();
String key = getKey(hostInfo);
if (CacheConstants.LINK_CACHE.containsKey(key)) {
String value = CacheConstants.LINK_CACHE.get(key);
CacheConstants.LINK_CACHE.put(key, getValue(getTimes(value), hostInfo));
} else {
CacheConstants.LINK_CACHE.put(key, getValue(0, hostInfo));
}
}
private Long getTime (String value) {
return Long.valueOf(value.split(UNDERLINE)[0]);
}
private Integer getTimes (String value) {
return Integer.valueOf(value.split(UNDERLINE)[1]);
}
private Boolean hasTimeouted (String value, HostInfo hostInfo) {
long now = hostInfo.getTime();
long last = getTime(value);
DebugUtil.debug("compare", (now - last));
if (now - last > timeout) {
return true;
}
return false;
}
public void checkTimeout () {
DebugUtil.debug("time", "tick");
Map map = new HashMap<>(CacheConstants.LINK_CACHE);
List removeKeys = new ArrayList<>();
List timeoutKeys = new ArrayList<>();
map.forEach((key, value) -> {
if (hasTimeouted(value, HostInfo.create().setTime(System.currentTimeMillis()))) {
int times = getTimes(value);
if (times > timesLimit) {
removeKeys.add(key);
} else {
timeoutKeys.add(key);
}
}
});
removeKeys.forEach(key -> {
CacheConstants.LINK_CACHE.remove(key);
CacheConstants.INFO_CACHE.remove(key);
CacheConstants.LINK_COUNT.remove(key);
});
timeoutKeys.forEach(key -> {
String value = CacheConstants.LINK_CACHE.get(key);
CacheConstants.LINK_CACHE.put(key, addTimes(value));
});
}
private String addTimes(String value) {
long time = getTime(value);
int times = getTimes(value);
value = getValue(++times, HostInfo.create().setTime(time));
return value;
}
}
static class HandlerFactory {
private static final Map HANDLER_POOL = new HashMap<>(16);
public static Handler getHandler (HandlerType handlerType) {
if (!HANDLER_POOL.containsKey(handlerType)) {
Handler handler = null;
switch (handlerType) {
case TICK_HANDLER: handler = new TickHandler(); break;
default: break;
}
HANDLER_POOL.put(handlerType, handler);
}
return HANDLER_POOL.get(handlerType);
}
}
interface Handler {
/**
* 处理
* @param readInfo
*/
void handle(ReadInfo readInfo);
/**
* 响应
* @param ctx
*/
void resp(ChannelHandlerContext ctx, ReadInfo readInfo);
/**
* 获取请求信息
* @param ctx
* @param msg
* @return
*/
ReadInfo getReadInfo(ChannelHandlerContext ctx, Object msg);
}
enum HandlerType {
/**
* TickHandler
*/
TICK_HANDLER;
}
}