Fork me on GitHub

SpringCloud服务网关Zuul

1. 简介

在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服务,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在git仓库,方便开发人员随时改配置。

一、Zuul简介
Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。

zuul有以下功能:

  • Authentication
  • Insights
  • Stress Testing
  • Canary Testing
  • Dynamic Routing
  • Service Migration
  • Load Shedding
  • Security
  • Static Response handling
  • Active/Active traffic management

spring cloud zuul的架构图如下:

1在Zuul中一次http请求的生命周期如下:

2

2. Zuul的路由

创建项目:

3

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
application:
name: api-gateway
cloud:
config:
discovery:
enabled: true
service-id: CONFIG
profile: test
eureka:
client:
service-url:
defaultZone: http://eureka1:8761/eureka/

路由转发功能:

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {

public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}

}

然后假如你想访问的微服务的id是product,然后访问的页面是微服务下的product/list页面,则启动应用之后你只需要访问localhost:8080/product/product/list既可以了。只要是你注册在服务中心的应用都可以路由访问到。

自定义路由:

添加配置

1
2
3
4
5
zuul:
routes:
myProduct:
path: /myProduct/**
serviceId: product

myProduct:是你自己定义的名称(可以随便定义),path是你自己定义的路径, serviceId: 是你要访问的服务的ID,访问localhost:8080/myProduct/product/list就可以访问了。

禁止掉某一些路由:ignored-patterns:下面配置你想要禁止访问的路由即可,下面配置的是set类型的数据

1
2
3
4
5
6
7
8
zuul:
routes:
myProduct:
path: /myProduct/**
serviceId: product
ignored-patterns:
- /product/product/listForOrder
- /myProduct/product/listForOrder

还能写成

1
2
ignored-patterns:
- /**/product/listForOrder

然后在配置里面添加sensitiveHeaders:并置为空,这样子就可以从前端获取到cokkie了。

1
2
3
4
5
6
zuul:
routes:
myProduct:
path: /myProduct/**
serviceId: product
sensitiveHeaders:

3. Zuul的过滤功能

要想过滤的话继承ZuulFilter类并实现其中的方法即可,这里实现的是过滤掉URL后没有token后缀的url地址。pre过滤器。

pre 过滤器的话默认放在PRE_DECORATION_FILTER_ORDER 前面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Component
public class TokenFilter extends ZuulFilter {


@Override
public String filterType() {
return PRE_TYPE;
}

@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//从url参数获取,也可以从cookie, header里获取
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED); //没有认证权限,报错401
}
return null;
}
}

post过滤器:类型POST_TYPE,优先级默认在SEND_RESPONSE_FILTER_ORDER前面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component
public class addResponseHeaderFilter extends ZuulFilter {
@Override
public String filterType() {
return POST_TYPE;
}

@Override
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER - 1 ;
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletResponse response = requestContext.getResponse();
response.setHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}

4. Zuul限流

限流放在pre过滤器中使用,在请求被转发之前调用。并且限流是最高的,因为限流的话是要要放在最前面实现的。

令牌桶限流。

4

具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Component
public class RateFilter extends ZuulFilter {

private static final RateLimiter RATE_LIMITER = RateLimiter.create(100); //google开源的令牌桶算法


@Override
public String filterType() {
return PRE_TYPE; //在pre之前做的,所以过滤器是pre类型的
}

@Override
public int filterOrder() {
return SERVLET_DETECTION_FILTER_ORDER -1; //SERVLET_DETECTION_FILTER_ORDER优先级最小,优先级越小越早执行
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() throws ZuulException {
if (!RATE_LIMITER.tryAcquire()){ //就是没有拿到令牌的话
throw new RateLimmitException();
}
return null;
}
}

5. Zuul鉴权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Component
public class AuthFilter extends ZuulFilter {

@Autowired
StringRedisTemplate stringRedisTemplate;
@Override
public String filterType() {
return PRE_TYPE;
}

@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//从url参数获取,也可以从cookie, header里获取
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)){
/**
* /order/create 只能买家访问(cookie中有openid)
* /order/finish 只能卖家访问(cookie中有token,并且对应Redis中的值)
* /product/list 都能访问
*/
if ("/order/order/create".equals(request.getRequestURI())){
Cookie cookie = CookieUtil.get(request, "openid");
if (cookie == null || StringUtils.isEmpty(cookie.getValue())){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
}
}

if ("/order/order/create".equals(request.getRequestURI())){
Cookie cookie = CookieUtil.get(request, "token");
if (cookie == null || StringUtils.isEmpty(cookie.getValue()) ||
StringUtils.isEmpty(stringRedisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_TEMPLATE, cookie.getValue())))){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
}
}
}
return null;
}
}

5. Zuul跨域访问

首先在springboot中要想对某一个接口跨域访问的话,直接在该方法或者是类上添加

@CrossOrigin的注解,表明可以支持跨域访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class CorsConfig {

@Bean
public CorsFilter corsFilter(){
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

final CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("*")); //哪些原始域的cookie可以跨域访问
config.setAllowedHeaders(Arrays.asList("*")); //头
config.setAllowedMethods(Arrays.asList("*")); //方法
config.setMaxAge(300l); //缓存时间,在设置的时间段内对于相同的跨域请求不再检查
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);

}
}

本文标题:SpringCloud服务网关Zuul

文章作者:WilsonSong

发布时间:2019年01月21日 - 21:01

最后更新:2019年03月10日 - 22:03

原始链接:https://songwell1024.github.io/2019/01/21/SpringCloudZuul/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------
分 享