Spring Cloud Netflix Zuul 路由网关及服务过滤

Posted by 彭超 on 2019-11-16
Estimated Reading Time 5 Minutes
Words 1k In Total
Viewed Times

Zuul 简介

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

引入依赖

在统一依赖管理项目中继承 Spring Boot 2.0.2.RELEASE 父项目,并声明 Spring Cloud Finchley.RC1 依赖版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

在当前项目 pom.xml 中主要添加依赖 spring-cloud-starter-netflix-eureka-serverspring-cloud-starter-netflix-zuul

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

相关配置

application.yml 中主要添加 Zuul 路由配置

1
2
3
4
5
6
7
8
zuul:
routes:
api-a:
path: /api/ribbon/**
serviceId: hello-spring-cloud-web-admin-ribbon
api-b:
path: /api/feign/**
serviceId: hello-spring-cloud-web-admin-feign

路由说明:

  • /api/ribbon 开头的请求都转发给 spring-cloud-web-admin-ribbon 服务
  • /api/feign 开头的请求都转发给 spring-cloud-web-admin-feign 服务

Application 入口类中添加 @EnableZuulProxy 注解开启 zuul 功能

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}

配置网关路由失败时的回调

创建 WebAdminFeignFallbackProvider 回调类

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* 路由 hello-spring-cloud-web-admin-feign 失败时的回调
*/
@Component
public class WebAdminFeignFallbackProvider implements FallbackProvider {

@Override
public String getRoute() {
// ServiceId,如果需要所有调用都支持回退,则 return "*" 或 return null
return "hello-spring-cloud-web-admin-feign";
}

/**
* 如果请求服务失败,则返回指定的信息给调用者
* @param route
* @param cause
* @return
*/
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
/**
* 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的,
* 不应该把 api 的 404,500 等问题抛给客户端
* 网关和 api 服务集群对于客户端来说是黑盒
* @return
* @throws IOException
*/
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}

@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}

@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}

@Override
public void close() {

}

@Override
public InputStream getBody() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("message", "无法连接");
return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8"));
}

@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
// 和 getBody 中的内容编码一致
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}
};
}
}

测试路由访问

依次运行 EurekaApplication > ServiceAdminApplication > WebAdminRibbonApplication > WebAdminFeignApplication > ZuulApplication 各服务

使用 Zuul 的服务过滤功能

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
53
54
55
56
57
58
59
60
61
62
63
/**
* Zuul 的服务过滤演示
*/
@Component
public class LoginFilter extends ZuulFilter {

private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);

/**
* 配置过滤类型,有四种不同生命周期的过滤器类型
* 1. pre:路由之前
* 2. routing:路由之时
* 3. post:路由之后
* 4. error:发送错误调用
* @return
*/
@Override
public String filterType() {
return "pre";
}

/**
* 配置过滤的顺序
* @return
*/
@Override
public int filterOrder() {
return 0;
}

/**
* 配置是否需要过滤:true/需要,false/不需要
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}

/**
* 过滤器的具体业务代码
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getParameter("token");
if (token == null) {
logger.warn("Token is empty");
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
try {
context.getResponse().getWriter().write("Token is empty");
} catch (IOException e) {
}
} else {
logger.info("OK");
}
return null;
}
}

测试过滤器


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !