异步长轮询配置信息检测demo

231 阅读2分钟

1.前言

采用长轮询的处理可以减少不必要的通信请求次数。

2.基本流程

image.png

3.实现demo

工具类
public class LongPollUtil {
	private static Logger logger = LoggerFactory.getLogger(LongPollUtil.class);

	// 配置变量
	public static final Map<String, String> propMap = new ConcurrentHashMap<String, String>();
	//异步链接
	public static final Multimap<String, LongPollUtil> longPollMap = Multimaps
			.synchronizedSetMultimap(HashMultimap.create());

	// 超时延时处理
	private static final ThreadFactory threadFactory = new ThreadFactoryBuilder()
			.setNameFormat("longPol-timeout-%d").setDaemon(true).build();
	private ScheduledExecutorService timeoutChecker = new ScheduledThreadPoolExecutor(1, threadFactory);

	private AsyncContext asyncContext;
	private String key;
	private String value;
	private long timeOut;// 超时时间
	private boolean isOver;// 已经处理了

	// 初始测试k-v
	static {
		propMap.put("test1", "1");
		propMap.put("test2", "2");
	}

	/**
	 * 初始化参数
	 * 
	 * @param asyncContext
	 * @param timeOut
	 * @throws IOException
	 */
	public LongPollUtil(AsyncContext asyncContext, long timeOut) throws IOException {
		this.asyncContext = asyncContext;
		this.key = asyncContext.getRequest().getParameter("key");
		this.value = asyncContext.getRequest().getParameter("value");
		this.timeOut = timeOut;
        this.isOver=false;
        
		if (isWait()) {
			longPollMap.put(key, this);
			timeOutBack();
		} else {
			LongPollUtil.print(asyncContext,HttpServletResponse.SC_OK,"[新数据]key:"+key+",value:"+propMap.get(key));
		}
	}

	/**
	 * 初始判断是否变更
	 * @return
	 * @throws IOException
	 */
	public boolean isWait() {
		if (propMap.containsKey(key) && value.equals(propMap.get(key))) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 超时等待
	 */
	public void timeOutBack() {
		logger.info("超时启动...");
		// 启动定时器
		timeoutChecker.schedule(() -> {
			// 触发定时后,判断任务是否被执行
			if (!this.isOver) {
				// 清除缓存中的任务
				logger.info("超时时间到了...");
				longPollMap.remove(key, this);
				//HttpServletResponse.SC_NOT_MODIFIED;//304 没变更 200为了显示数据
				LongPollUtil.print(asyncContext,HttpServletResponse.SC_OK,"[数据没改变]key:"+key+",value:"+propMap.get(key));
			}
		}, Integer.parseInt(String.valueOf(timeOut)), TimeUnit.SECONDS);
	}

	/**
	 * 打印数据方法
	 * @param asyncContext
	 * @param status
	 * @param str
	 */
	public static void print(AsyncContext asyncContext,int status,String str) {
		try {
			HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
			response.setStatus(status);
			response.setContentType("text/html;charset=UTF-8");
			response.getWriter().print(str);
			response.flushBuffer();
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}finally {
			asyncContext.complete();
		}
	}
	
	/**
	 * 更新配置
	 * @param key
	 * @param value
	 */
	public static void updateProperties(String key, String value) {
		if (StringUtils.hasLength(key) && StringUtils.hasLength(value)) {
			String valueOld = propMap.get(key);
			if (!value.equals(valueOld)) {
				propMap.put(key, value);
				changBack(key);
			}
		}
	}

	/**
	 * 触发返回更新数据
	 * @param key
	 */
	public static void changBack(String key) {
		logger.info("变更触发...");
		if (StringUtils.hasLength(key) && null != longPollMap && longPollMap.size() > 0) {
			Collection<LongPollUtil> longPollTask = longPollMap.removeAll(key);
			if (null != longPollTask) {
				for (LongPollUtil lpu : longPollTask) {
					lpu.isOver = true;
					lpu.timeoutChecker.shutdown();
					LongPollUtil.print(lpu.asyncContext,HttpServletResponse.SC_OK,"key:"+key+",value:"+propMap.get(key));
				}
			}
		}
	}
}
http测试demo
/**
 * @author 肥皂
 * @date 2022年12月15日
 */
@Controller
@RequestMapping("/longPoll")
public class TestLongPoll {
      
	private Logger logger=LoggerFactory.getLogger(TestLongPoll.class);
	
	@RequestMapping("/check")
	public void longPoll(HttpServletRequest request,HttpServletResponse response) throws IOException {
		String key=request.getParameter("key");
		String value=request.getParameter("value");
        if(!StringUtils.hasLength(key) && !StringUtils.hasLength(value)) {
        	response.setContentType("text/html;charset=UTF-8");
        	response.getWriter().print("[存在数据为空]");
        	response.flushBuffer();
    		return;
		}
        
		AsyncContext asyncContext=request.startAsync(request, response);
		try {
			//时间不准自己控制
			asyncContext.setTimeout(0L);
			//超时测试时间为10S
			new LongPollUtil(asyncContext, 10);
		}catch (Exception e) {
			logger.error(e.getMessage(), e);
			asyncContext.complete();
		}
	}

	@RequestMapping("/update")
	@ResponseBody
	public String update(String key,String value) {
		LongPollUtil.updateProperties(key, value);
		return "[修改成功]key:"+key+",value:"+value;
	}
}