周末记一次笔记
ZuulFilter 的生命周期
过滤器类型 filterType
Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。
而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。
1)pre
可以在请求被路由之前调用。适用于身份认证的场景,认证通过后再继续执行下面的流程。
2)route
在路由请求时被调用。适用于灰度发布场景,在将要路由的时候可以做一些自定义的逻辑。
3)post
在 route 和 error 过滤器之后被调用。这种过滤器将请求路由到达具体的服务之后执行。适用于需要添加响应头,记录响应日志等应用场景。
4)error
处理请求时发生错误时被调用。在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息。
package com.cloudyoung.zuul.filter;
import com.cloudyoung.zuul.service.RedisService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
@Configuration
public class PreFilter extends ZuulFilter {
private final String Shiro_Session="shiro:session:";
@Resource
private RedisService redisService;
private static Logger log = LoggerFactory.getLogger(PreFilter.class);
/**
* 请求前处理
* @return
*/
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
/**
* 是否需要过滤 true 默认false
* @return
*/
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String requestUrl = request.getRequestURL().toString();
// 请求URL内不包含login或join则需要经过该过滤器,即执行run()
return !requestUrl.contains("login") && !requestUrl.contains("logout");
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s 方式请求 %s", request.getMethod(), request.getRequestURL().toString()));
//获取传来的参数accessToken
// Object accessToken = request.getParameter("Authorization");
Object accessToken = request.getHeader("Authorization");
if(accessToken == null) {
log.warn("Authorization token is empty");
//过滤该请求,不往下级服务去转发请求,到此结束
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("{\"result\":\"Authorization is empty!\"}");
return null;
}else if(accessToken!=null && !accessToken.equals("")){
String key = String.valueOf(accessToken);
String result = redisService.get(Shiro_Session+key);
boolean notBlank = StringUtils.isNotBlank(result);
if(notBlank){
ctx.setSendZuulResponse(true);
ctx.setResponseStatusCode(200);
//如果有token,则进行路由转发
log.info("Authorization token ok");
}else{
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("{\"result\":\"Authorization is unefftive!\"}");
}
}
return null;
}
}
使用RequestContext 传递参数
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
/过滤该请求,不往下级服务去转发请求,到此结束
ctx.setSendZuulResponse(false);
该过滤器只对进入路由后的请求路径进行过滤。(、api/serach/ 或 api/portrait/ 后的请求才进行过滤 )
yml配置
#添加路由前缀 api
zuul:
prefix: /api
routes:
ai-serach:
path: /serach/**
serviceId: ai-serach
ai-portrait:
path: /portrait/**
serviceId: ai-portrait
add-host-header: true #添加host头信息
sensitive-headers: #敏感头信息
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 12000 #熔断 超时时长 5000ms
#ribbon的默认超时时长:(read + connect) * 2, 必须小于hystrix的时长
ribbon:
ConnectionTimeOut: 5000 #ribbon 链接超时时长
ReadTimeout: 5000 #ribbon 读取超时时长
MaxAutoRetries: 0 #ribbon 当前服务重试次数
MaxAutoRetriesNextServer: 0 #ribbon 切换服务重试次数