Spring Cloud教程五:Zuul | Java提升营

Spring Cloud教程五:Zuul

前一篇讲了Spring Cloud断路器组件,本篇文章基于上一篇文章的项目开展,没看过上篇的先去上一篇文章了解一下。这篇文章我们讲一下智能路由Zuul的使用。

Zuul简介

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

Netflix Zuul有以下功能:

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

准备工作

在之前工程的基础上, 启动eureka-server,端口为9090;启动eureka-client, 端口为8040;启动ribbon-service, 端口为8050;启动feign-service, 端口为8080;

温馨提示:这篇文章要启动5个项目,如果个人电脑内存有限,可以设置一下项目启动参数,加入-Xms256m -Xmx256m

创建zuul-service项目

使用Spring Initializr新建一个Moudle,取名为zuul-service, 在Spring Cloud Discovery中勾选Eureka Discovery Client,在Spring Cloud Routing中勾选Zuul,在Web中勾选Spring Web:

其pom.xml文件如下:

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.noodles.mars</groupId>
<artifactId>zuul-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zuul-service</name>
<description>Zuul Service</description>

<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

zuul-service配置注册服务中心地址应用名端口路由信息,配置文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9090/eureka/
server:
port: 8090
spring:
application:
name: zuul-service
zuul:
routes:
feign-api:
path: /feign-api/**
serviceId: feign-service
ribbon-api:
path: /ribbon-api/**
serviceId: ribbon-service

其他的配置前面文章都讲过,这里重点讲一下zuul的配置,以/feign-api/ 开头的请求都转发给feign-service服务;以/ribbon-api/开头的请求都转发给ribbon-service服务。
在项目启动类上注解@EnableZuulProxy, 开启Zuul的功能;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.noodles.mars.zuulservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ZuulServiceApplication {

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

}

启动Zuul项目,浏览器访问:http://localhost:8090/feign-api/hello?name=Mars

1
Hello, My name is Mars, I'm from port: 8040

再打开一个窗口访问:http://localhost:8090/ribbon-api/hello?name=Mars

这说明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
package com.noodles.mars.zuulservice;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;


@Component
public class ValidateTokenFilter extends ZuulFilter {

private final static String VALIDATE_TOKEN_FILTER_TYPE = "pre";

private final static String TOKEN_PARAM_NAME = "token";

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

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

@Override
public int filterOrder() {
return 0;
}

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

@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();

HttpServletRequest request = ctx.getRequest();
logger.info("Request info: method {{}} url {{}}", request.getMethod(), request.getRequestURL().toString());

String token = request.getParameter(TOKEN_PARAM_NAME);

if(ObjectUtils.isEmpty(token)) {
logger.warn("Token in request is empty.");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);

try {
ctx.getResponse().getWriter().write("Token can not be empty!");
} catch (IOException e) {
return null;
}
}
logger.info("Validate via...");
return null;
}
}
  • filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
    • pre:路由之前
    • routing:路由之时
    • post: 路由之后
    • error:发送错误调用
  • filterOrder:过滤的顺序
  • shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
  • run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。

重启项目,继续访问:http://localhost:8090/feign-api/hello?name=Mars

1
Token can not be empty!

访问:http://localhost:8090/feign-api/hello?name=Mars&token=validate

1
Hello, My name is Mars, I'm from port: 8040

说明过滤功能工作了。

点击进入源码仓库

给老奴加个鸡腿吧 🍨.