JAVA WEB项目中开启流量控制Filter
Flow Control:控流的概念
- 主要是用来限定server所能承载的最大(高并发)流量峰值,以免在峰值是Server过载而宕机,对于WEB系统而言
- 通常是分布式部署,如果请求并发量很大,会导致整个集群崩溃,也就是通常所说的“雪崩效应”。
- 所以,我们不仅在网络代理层面(比如nginx)设置流量控制以抵抗、拒止溢出流量,
- 我们还应该在application server层面有一定的自我保护策略,确保当前JVM的负载应该在可控范围之内,对于JVM承载能力之外的请求,应该被合理管理。
本文开发了一个分布式流量控制Filter,来限定application的并发量:
1)对于过量的请求,首先将请求buffer在队列中。
2)当buffer队列满时,多余的请求将会被直接拒绝。(过载请求量)
3)那些buffer中被阻塞的请求,等待一定时间后任然无法被执行,则直接返回错误URL。(溢出请求量)
4)我们设定一个允许的并发量,通过java中Semaphore控制。只有获取“锁”的请求,才能继续执行。
web.xml配置 <filter>
<filter-name>flowControlFilter</filter-name>
<filter-class>com.demo.security.FlowControlFilter</filter-class>
<init-param>
<param-name>permits</param-name>
<param-value>128</param-value>
</init-param>
<init-param>
<param-name>timeout</param-name>
<param-value>15000</param-value>
</init-param>
<init-param>
<param-name>bufferSize</param-name>
<param-value>500</param-value>
</init-param>
<init-param>
<param-name>errorUrl</param-name>
<param-value>/error.html</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>flowControlFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Java代码:
package com.src.java.filter; import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse; /**
*
* @ClassName: FlowControlFilter
* @Description: 分布式系统流量控制
* @author chinasoft_liuhanlin
* @date 2017年6月1日 下午3:57:08
*/
public class FlowControlFilter implements Filter { /**
* 最大并发量 默认为500
*/
private int permits = Runtime.getRuntime().availableProcessors() + 1; /**
* 当并发量达到permits后,新的请求将会被buffer,buffer最大尺寸 如果buffer已满,则直接拒绝
*/
private int bufferSize = 500;
/**
* buffer中的请求被阻塞,此值用于控制最大阻塞时间 默认阻塞时间
*/
private long timeout = 30000;
/**
* 跳转的错误页面
*/
private String errorUrl; private BlockingQueue<Node> waitingQueue; private Thread selectorThread;
private Semaphore semaphore; private Object lock = new Object(); @Override
public void destroy() { } /**
* <p>
* Title: doFilter
* </p>
* <p>
* Description:
* </p>
*
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
checkSelector();
Thread t = Thread.currentThread();
HttpServletResponse httpServletResponse = (HttpServletResponse) response; Node node = new Node(t, false);
boolean buffered = waitingQueue.offer(node);
// 如果buffer已满
if (!buffered) {
if (errorUrl != null) {
httpServletResponse.sendRedirect(errorUrl);
}
return;
}
long deadline = System.currentTimeMillis() + timeout;
// 进入等待队列后,当前线程阻塞
LockSupport.parkNanos(this, TimeUnit.MICROSECONDS.toNanos(timeout));
if (t.isInterrupted()) {
// 如果线程是中断返回
t.interrupted();// clear status }
// 如果等待过期,则直接返回
if (deadline >= System.currentTimeMillis()) {
if (errorUrl != null) {
httpServletResponse.sendRedirect(errorUrl);
}
// 对信号量进行补充
synchronized (lock) {
if (node.dequeued) {
semaphore.release();
} else {
node.dequeued = true;
}
}
return;
}
// 继续执行
try {
chain.doFilter(request, response);
} finally {
semaphore.release();
checkSelector();
}
} /**
* <p>
* Title: init
* </p>
* <p>
* Description:
* </p>
*
* @param filterConfig
* @throws ServletException
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String p = filterConfig.getInitParameter("permits");
if (p != null) {
permits = Integer.parseInt(p);
if (permits < 0) {
throw new IllegalArgumentException("FlowControlFilter,permits parameter should be greater than 0 !");
}
} String t = filterConfig.getInitParameter("timeout");
if (t != null) {
timeout = Long.parseLong(t);
if (timeout < 1) {
throw new IllegalArgumentException("FlowControlFilter,timeout parameter should be greater than 0 !");
}
} String b = filterConfig.getInitParameter("bufferSize");
if (b != null) {
bufferSize = Integer.parseInt(b);
if (bufferSize < 0) {
throw new IllegalArgumentException("FlowControlFilter,bufferSize parameter should be greater than 0 !");
}
} errorUrl = filterConfig.getInitParameter("errorUrl"); waitingQueue = new LinkedBlockingQueue<>(bufferSize);
semaphore = new Semaphore(permits); selectorThread = new Thread(new SelectorRunner());
selectorThread.setDaemon(true);
selectorThread.start(); } /**
* @Title: checkSelector
* @Description: TODO
* @param:
* @return: void
* @throws
*/
private void checkSelector() {
if (selectorThread != null && selectorThread.isAlive()) {
return;
}
synchronized (lock) {
if (selectorThread != null && selectorThread.isAlive()) {
return;
}
selectorThread = new Thread(new SelectorRunner());
selectorThread.setDaemon(true);
selectorThread.start();
}
} /**
*
* @ClassName: SelectorRunner
* @Description: TODO
* @author chinasoft_liuhanlin
* @date 2017年6月1日 下午3:59:11
*/
private class SelectorRunner implements Runnable { @Override
public void run() {
try {
while (true) {
Node node = waitingQueue.take();
// 如果t,阻塞逃逸,只能在pack超时后退出
synchronized (lock) {
if (node.dequeued) {
// 如果此线程已经park过期而退出了,则直接忽略
continue;
} else {
node.dequeued = true;
} }
semaphore.acquire();
LockSupport.unpark(node.currentThread);
}
} catch (Exception e) {
//
} finally {
// 全部释放阻塞
Queue<Node> queue = new LinkedList<>();
waitingQueue.drainTo(queue);
for (Node n : queue) {
if (!n.dequeued) {
LockSupport.unpark(n.currentThread);
}
}
}
}
} private class Node {
Thread currentThread;
boolean dequeued;// 是否已经出队 public Node(Thread t, boolean dequeued) {
this.currentThread = t;
this.dequeued = dequeued;
}
}
}
JAVA WEB项目中开启流量控制Filter的更多相关文章
- 在Java web项目中防止用户注销后使用浏览器中的“后退”按钮返回注销前页面
一背景 公司安全整改, 要求:系统中对于关键业务操作应确保使用浏览器"后退"功能无法回到上一步操作界面. 提供:凭证提供所有被检查系统关键业务操作后回退视频,视频显示关键业务操作后 ...
- JAVA WEB项目中各种路径的获取
JAVA WEB项目中各种路径的获取 标签: java webpath文件路径 2014-02-14 15:04 1746人阅读 评论(0) 收藏 举报 分类: JAVA开发(41) 1.可以在s ...
- linux 下用renameTo方法修改java web项目中文件夹名称问题
经测试,在Linux环境中安装tomcat,然后启动其中的项目,在项目中使用java.io.File.renameTo(File dest)方法可行. 之前在本地运行代码可以修改,然后传到Linux服 ...
- 对Java Web项目中路径的理解
第一个:文件分隔符 坑比Window.window分隔符 用\;unix采用/.于是用File.separator来跨平台 请注意:这是文件路径.在File f = new File(“c:\\hah ...
- Java Web项目中缺少Java EE 6 Libraries怎么添加
Java Web项目中缺少Java EE 6 Libraries怎么添加 具体步骤如下: 1.项目名称上点击鼠标右键,选择"Build Path-->Configure Build P ...
- java web 项目中 简单定时器实现 Timer
java web 项目中 简单定时器实现 Timer 标签: Java定时器 2016-01-14 17:28 7070人阅读 评论(0) 收藏 举报 分类: JAVA(24) 版权声明:本文为博 ...
- Java Web项目中连接Access数据库的配置方法
本文是对前几天的"JDBC连接Access数据库的几种方式"这篇的升级.因为在做一些小项目的时候遇到的问题,因此才决定写这篇博客的.昨天已经将博客公布了.可是后来经过一些验证有点问 ...
- java web项目中打开资源文件中文乱码
1 java web项目中经常使用多模块管理.在某一个模块中添加了一些资源文件.但不是启动项目.有时候需要在程序中读取资源文件内容,打包后放到容器中就不能正常运行了.需要将所有资源文件放到启动项目的 ...
- 在java web项目中编写自己的代码生成器
在java web项目中编写自己的代码生成器
随机推荐
- 推荐一个免费的在线IDE和终端
墙裂推荐!支持众多语言,方便学习,测试,地址如下 https://www.tutorialspoint.com/codingground.htm
- python 11
# 一.闭包 # # 判断:函数名.__closure__ # 若返回cell,则是闭包,返回None则不是闭包. # # 闭包:内层函数对外层函数非全局变量的引用就叫闭包. def func1(x) ...
- BZOJ-3105: 新Nim游戏 (nim博弈&线性基)
pro: 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴.可以只拿一根,也可以拿走整堆火柴,但不能同时从 ...
- ubuntu18.04 下利用conda安装opencv3
ubuntu18.04 下利用conda安装opencv3 安装opencv3 conda install -c https://conda.anaconda.org/menpo opencv3 出现 ...
- MATLAB 进行五种边缘检测
自定义函数: function []=edge_detect(image_name) a=imread(image_name); I=rgb2gray(a); BW1=edge(I,'Roberts' ...
- JavaEE基本框架(Struts2+Spring+MyBatis三层,Struts MVC)之间的关系
郭晨 软件151 1531610114 [整理]JavaEE基本框架(Struts2+Spring+MyBatis三层,Struts MVC)之间的关系 visio文件下载 概述 一个JavaEE的项 ...
- 网络请求 selenium
网络请求 selenium 部分流程: 第一:爬虫引擎生成requests请求,送往scheduler调度模块,进入等待队列,等待调度.第二:scheduler模块开始调度这些requests,出队, ...
- zabbix监测图形界面显示方框乱码解决方法
思路如下:用Windows下中文字体进行替换,修改配置文件即可 详细步骤如下: 1.在Windows的控制面板里的字体中,选择一种中文字体,将该字体文件复制到桌面.例如,我选择了宋体 常规字体,复制到 ...
- nginx ngx_http_image_filter_module 简单试用
nginx包含了一个ngx_http_image_filter_module 模块,我们可以方便的进行图片的缩略图,平时一些简单的功能 已经够用了 环境准备 为了简单使用docker-compose ...
- WinForm中使用BackgroundWorker异步加载数据并使用进度条
在WinForm程序中,有时会因为加载大量数据导致UI界面假死,这种情况对于用户来说是非常不友好的.因此,在加载大量数据的情况下,首先应该将数据加载放在另一线程中进行,这样保证了UI界面的响应:其次可 ...