zuul常用作网关服务,本例使用zuul作为网关限流服务
一.pom.xml依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>1.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
二.注册zuul过滤器
package zuul;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.filters.FilterRegistry;
/**
* Created by Jack on 2016/11/22.
*/
@EnableZuulProxy
@SpringBootApplication
@Import({
XdiamondApplication.class
})
public class AppGateway {
private static final Logger logger = LoggerFactory.getLogger(AppGateway.class);
private static final String OAUTH_SERVER = "/oauthserver";
public static void main(String[] args) {
SpringApplication.run(AppGateway.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() { // 允许跨域
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("origin, content-type, accept, x-requested-with")
.allowedOrigins("*")
.allowedMethods("POST", "DELETE", "PUT", "GET", "OPTIONS");
}
};
}
@Component
public static class MyCommandLineRunner implements CommandLineRunner {
@Autowired
private UrlLimit urlLimit;
public void run(String... args) throws Exception {
initJavaFilters();
}
private void initJavaFilters() {
final FilterRegistry r = FilterRegistry.instance();
r.put("javaPreFilter", new ZuulFilter() {
@Override
public int filterOrder() {
return 50000;
}
@Override
public String filterType() {
return "pre";
}
public boolean shouldFilter() {
return true;
}
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String authType = request.getAuthType();
String contextPath = request.getContextPath();
Enumeration<String> headerNames = request.getHeaderNames();
String method = request.getMethod();
String pathInfo = request.getPathInfo();
String queryString = request.getQueryString();
String sessionId = request.getRequestedSessionId();
String remoteUser = request.getRemoteUser();
String uri = request.getRequestURI();
String url = request.getRequestURL().toString();
String servletPath = request.getServletPath();
String forwardedFor = RequestContext.getCurrentContext().getZuulRequestHeaders().get("x-forwarded-for");
String forwardedHost = RequestContext.getCurrentContext().getZuulRequestHeaders().get("x-forwarded-host");
String os = request.getParameter("os");
logger.info("running javaPreFilter");
logger.info("authType:" + authType +
" contextPath:" + contextPath +
" method:" + method + " pathInfo:" + pathInfo +
" queryString:" + queryString + " sessionId:" + sessionId + " remoteUser:" + remoteUser+
" uri:" + uri + " servletPath:" + servletPath +" url:" + url +
" headerNames:"+headerNames +
" forwardedFor:"+forwardedFor+" forwardedHost:"+forwardedHost + " os:" + os);
logger.info(RequestContext.getCurrentContext().getZuulRequestHeaders().toString());
if("OPTIONS".equals(method)) { //web端加head之前的探测请求
logger.info("web options request allow pass.");
return null;
}
//通过计算次数来判断请求是否需要限制
if(urlLimit.needLimit(ctx)){
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
logger.info("AuthorizePreFilter : the request had been limited");
}else {
/**处理token的逻辑, 需要在url中加入oauthserver
* 如:https://api.998jk.com/analyzer/brs/df/detail?type=1&userId=1657110
* 最终变为https://api.998jk.com/oauthserver/analyzer/brs/df/detail?type=1&userId=1657110
*/
String newUrl = request.getScheme() + "://" + forwardedHost + OAUTH_SERVER + request.getRequestURI();
newUrl = "https://api.998jk.com/oauthserver/analyzer/brs/df/detail?type=1&userId=1657110";
logger.info("new url is :" + newUrl);
int status = 0;
try {
HttpHead httpHead = new HttpHead(newUrl);
httpHead.setHeader("Authorization",request.getHeader("Authorization"));
httpHead.setHeader("Content-Type", "application/json");
HttpResponse returnResp = HttpClients.createDefault().execute(httpHead);
status = returnResp.getStatusLine().getStatusCode();
} catch (IOException e) {
logger.error("get oauthserver response error :" + e);
}
if(status == HttpServletResponse.SC_UNAUTHORIZED) { //token没有验证通过
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
logger.info("AuthorizePreFilter : the request has no auth");
}else {
logger.info("AuthorizePreFilter : the request is passed. and the status is :" + status);
}
}
}
catch(Exception e){
logger.info("error:", e);
e.printStackTrace();
}
return null;
}
});
}
}
}
用redis记录访问次数,进行限流
package zuul;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import com.netflix.zuul.context.RequestContext;
import io.github.xdiamond.client.XDiamondConfig;
import io.github.xdiamond.client.event.AllKeyListener;
import io.github.xdiamond.client.event.ConfigEvent;
/**
* @author mhl
* @version 创建时间:2017年3月8日
*
*/
@Component
public class UrlLimit {
private static final Logger logger = LoggerFactory.getLogger(UrlLimit.class);
@SuppressWarnings("rawtypes")
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private XDiamondConfig xDiamondConfig;
private static final String BLOCKED_IP = "blockedip";
private static final String LIMIT_COUNT = "limitCount";
private static final String REQUEST_TIME = "requestTime";
private static final String BLACK_LIST_TIME = "blackListTime";
private int limitCount = 5; //请求次数限制
private int requestTime = 1; //请求次数时限(秒)
private int blackListTime = 7; //黑名单时长(天)
/**
* 通过请求次数来做限制
* mhl 2017年3月8日
*/
public boolean needLimit(RequestContext context) {
boolean result = false;
HttpServletRequest request = context.getRequest();
String url = request.getRequestURI(); //访问url
String forwardedFor = context.getZuulRequestHeaders().get("x-forwarded-for"); //IP
if(forwardedFor == null) {
return false;
}
if(redisTemplate.hasKey(forwardedFor)) {
//黑名单需要限制
result = true;
logger.info("[UrlLimit:calculate] the ip is in blackList");
}else { //检查访问频次
String key = forwardedFor + "_" + url;
Long count = redisTemplate.opsForValue().increment(key, 1);
if (count == 1) {
requestTime = getConfigInt(REQUEST_TIME, 1);
redisTemplate.expire(key, requestTime, TimeUnit.SECONDS);
}
limitCount = getConfigInt(LIMIT_COUNT, limitCount);
if(count > limitCount) {
result = true;
logger.info("[UrlLimit:calculate] the ip called too many times: " + key);
}
}
return result;
}
/**
* 从配置文件读取ip 批量加入黑名单
* mhl 2017年3月9日
*/
@PostConstruct
public void batchAddBlacklist() {
String blockedIps = getConfig(BLOCKED_IP);
if(blockedIps == null || blockedIps.length() == 0) {
logger.info("UrlLimit | blockedIps is null or has no data");
return;
}
String[] array = blockedIps.split(",");
if(array.length > 0) {
for (String string : array) {
addBlacklist(string);
}
}
logger.info("UrlLimit | blockedIps successed");
}
public String getConfig(String key) {
if(xDiamondConfig.getProperties() == null || xDiamondConfig.getProperties().isEmpty()) {
init();
}
return xDiamondConfig.getProperty(key);
}
public int getConfigInt(String key, int defaultInt) {
int returnInt = defaultInt;
if(xDiamondConfig.getProperties() == null || xDiamondConfig.getProperties().isEmpty()) {
init();
}
String keyStr = xDiamondConfig.getProperty(key);
if (StringUtils.isNotBlank(keyStr)){
returnInt = Integer.parseInt(keyStr);
}
return returnInt;
}
/**
* 初始化xDiamondConfig 并且增加监听事件
* mhl 2017年3月14日
*/
private void init() {
xDiamondConfig.init();
xDiamondConfig.addAllKeyListener(new AllKeyListener() {
public void onConfigEvent(ConfigEvent configevent) {
String key = configevent.getKey();
if(key.equals(BLOCKED_IP)) {
String newBlackIpStr = configevent.getValue();
if(newBlackIpStr != null) {
String[] array = newBlackIpStr.split(",");
if(array.length > 0) {
for (String string : array) {
addBlacklist(string);
}
}
}
logger.info("UrlLimit | blackListListener successed");
}else if(key.equals(LIMIT_COUNT)) {
limitCount = Integer.valueOf(configevent.getValue());
logger.info("UrlLimit | limitCount value changed " + limitCount);
}else if(key.equals(REQUEST_TIME)) {
requestTime = Integer.valueOf(configevent.getValue());
logger.info("UrlLimit | requestTime value changed " + requestTime);
}else if(key.equals(BLACK_LIST_TIME)) {
blackListTime = Integer.valueOf(configevent.getValue());
logger.info("UrlLimit | blackListTime value changed " + blackListTime);
}
}
});
}
/**
* 手动添加黑名单
* mhl 2017年3月9日
* @param ip
* @return
*/
public boolean addBlacklist(String key) {
redisTemplate.opsForSet().add(key, key);
String blackListTimeStr = getConfig(BLACK_LIST_TIME);
if(blackListTimeStr != null) {
blackListTime = Integer.valueOf(blackListTimeStr);
}
redisTemplate.expire(key, blackListTime, TimeUnit.DAYS);
return true;
}
/**
* 手动移除黑名单
* mhl 2017年3月9日
* @param ip
* @return
*/
public boolean removeBlacklist(String key) {
redisTemplate.delete(key);
return true;
}
/**
* 判断key是否在redis中
* mhl 2017年3月9日
* @param key
* @return
*/
public boolean inRadis(String key) {
return redisTemplate.hasKey(key);
}
}
--------------
spring mvc+tomcat源码分析视频(链接复制在浏览器打开)
https://study.163.com/course/courseMain.htm?share=2&shareId=480000001919582&courseId=1209399899&_trace_c_p_k2_=6d81bc445e9c462ab8d6345e40f6b0bf