Spring Cloud学习笔记【十一】微服务网关Zuul的过滤和限流
zuul的工作原理
zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。这些过滤器帮助我们执行以下功能:
- 身份验证和安全性——识别每个资源的身份验证需求并拒绝不满足这些需求的请求。
- 洞察和监控——在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。
- 动态路由——根据需要动态地将请求路由到不同的后端集群。
- 压力测试——逐步增加集群的流量,以评估性能。
- 减少负载——为每种类型的请求分配容量,并删除超过限制的请求。
- 静态响应处理——直接在边缘构建一些响应,而不是将它们转发到内部集群。
- 多区域弹性——跨AWS区域路由请求,以使我们的ELB使用多样化,并使我们的优势更接近我们的成员。
a.依赖引入
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
b.@EnableZuulProxy注解添加
@EnableEurekaClient
@EnableZuulProxy
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
d.配置文件配置
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: zull
server:
port: 9000
c.编写测试demo
@RestController
@RequestMapping("/order")
public class OrderInfoController {
@Autowired
private OrderCodeConfig orderCodeConfig;
@Autowired
private OrderInfoService orderInfoService;
/**
* @todo 查询订单列表
* @return
* @throws Exception
*/
@RequestMapping(value = "/list",method = RequestMethod.GET)
public String list() throws Exception {
List<OrderInfo> list = orderInfoService.list();
return JSON.toJSONString(list);
}
/**
* @todo 查询订单配置
* @return
* @throws Exception
*/
@RequestMapping(value = "/config",method = RequestMethod.GET)
public String config() throws Exception {
return JSON.toJSONString(orderCodeConfig);
}
}
e.自定义过滤器
@Component
public class TokenFilter extends ZuulFilter {
private Log LOGGER = LogFactory.getLog(TokenFilter.class);
/**
* 过滤器类型:有四种类型(分别为:ERROR_TYPE、POST_TYPE、PRE_TYPE、ROUTE_TYPE)
*
* @return
*/
@Override
public String filterType() {
return PRE_TYPE;
}
/**
* 过滤的顺序:越小越靠前,FilterConstants.PRE_DECORATION_FILTER_ORDER已经定义了的大小顺序,可以直接使用
*
* @return
*/
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
/**
* 过滤是否生效:代表这个过滤器是否生效(true 、 false)
*
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 处理业务代码的逻辑
*
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
LOGGER.info("进入token的过滤器");
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String token = request.getParameter("token");//获取请求的参数
if (StringUtils.isNotBlank(token)) {
ctx.setSendZuulResponse(true); //对该请求进行路由
ctx.setResponseStatusCode(HttpStatus.OK.value());
ctx.set("isSuccess", true); // 设值,让下一个Filter看到上一个Filter的状态
} else {
ctx.setSendZuulResponse(false); //过滤该请求,不对其进行路由
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());// 返回错误码
ctx.setResponseBody("this is not auth"); //返回错误信息
ctx.set("isSuccess", false);
}
ctx.getResponse().setContentType("text/html;charset=UTF-8");
return null;
}
}
启动测试:http://localhost:9000/myOrder/order/list,不进行token携带
http://localhost:9000/myOrder/order/list?token=abc,进行token携带
直接查看这个类,定义了过滤器的一些常用的信息:
org.springframework.cloud.netflix.zuul.filters.support.FilterConstants
1、filterType过滤器类型(一共定义了四种)
PRE:该类型的filters在Request routing到源web-service之前执行。用来实现Authentication、选择源服务地址等
ROUTING:该类型的filters用于把Request routing到源web-service,源web-service是实现业务逻辑的服务。这里使用HttpClient请求web-service。
POST:该类型的filters在ROUTING返回Response后执行。用来实现对Response结果进行修改,收集统计数据以及把Response传输会客户端。
ERROR:上面三个过程中任何一个出现错误都交由ERROR类型的filters进行处理。
主要关注 pre、post和error。分别代表前置过滤,后置过滤和异常过滤。
请求先进入pre的filter类,你可以进行一些权限认证,日志记录,或者额外给Request增加一些属性供后续的filter使用。pre会优先按照order从小到大执行,然后再去执行请求转发到业务服务。
post,那么就会执行完被路由的业务服务后,再进入post的filter,在post的filter里,一般做一些日志记录,或者额外增加response属性什么的。
error,如果在上面的任何一个地方出现了异常,就会进入到type为error的filter中,进行异常的统一处理。
2、filterOrder过滤器顺序(数字越大,优先级越低)
3、shouldFilter过滤器是否生效(true/false)
4、Run方法(处理逻辑的地方)
下图是filter的执行顺序
过滤器间的协调
过滤器没有直接的方式来访问对方。 它们可以使用RequestContext共享状态,这是一个类似Map的结构,具有一些显式访问器方法用于被认为是Zuul的原语,内部是使用ThreadLocal实现的。
禁用Zuul过滤器
设置zuul.<SimpleClassName>.<filterType>.disable=true
,即可禁用SimpleClassName所对应的过滤器。比如:自定义的过滤器com.example.demo.filter.TokenFilter进行禁用,可以这样设置:
zuul:
TokenFilter:
pre:
disable: true
限流
在Zuul
上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法。限流算法有漏桶算法以及令牌桶算法,可参考 https://www.cnblogs.com/LBSer/p/4083131.html。Google Guava
为我们提供了限流工具类RateLimiter。
@Component
public class RateLimitZuulFilter extends ZuulFilter {
private final RateLimiter rateLimiter = RateLimiter.create(1000.0);
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public boolean shouldFilter() {
// 这里可以考虑弄个限流开启的开关,开启限流返回true,关闭限流返回false,你懂的。
return true;
}
@Override
public Object run() {
try {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletResponse response = currentContext.getResponse();
if (!rateLimiter.tryAcquire()) {
HttpStatus httpStatus = HttpStatus.TOO_MANY_REQUESTS;
response.setContentType(MediaType.TEXT_PLAIN_VALUE);
response.setStatus(httpStatus.value());
response.getWriter().append(httpStatus.getReasonPhrase());
currentContext.setSendZuulResponse(false);
throw new ZuulException(
httpStatus.getReasonPhrase(),
httpStatus.value(),
httpStatus.getReasonPhrase()
);
}
} catch (Exception e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return null;
}
}
这个是单点的限流措施,对于分布式和微服务可以参考:http://www.itmuch.com/spring-cloud-sum/spring-cloud-ratelimit/
跨域
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}