[TOC]

在学习完前面的知识后,微服务架构已经初具雏形。但还有一些问题:不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。如下图:

请求多个服务
因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:
1、易于监控
2、易于认证
3、减少了客户端与各个微服务之间的交互次数
如下图:
微服务网关架构图

在Spring Cloud中 Zuul是Netflix的基于JVM的路由器和服务器端负载均衡器。Zuul可以和Eureka、Ribbon、Hystrix、等组件配合使用。

构建微服务网关

新建一个spring Boot项目microservice-gateway-zuul 。
Spring Boot :1.5.9.RELEASE
Spring Cloud:Edgware.RELEASE

引入依赖

1
2
3
4
5
6
7
8
<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>

修改启动类

上面依赖中也加入了Eureka ,一般我们都会在启动类中添加上
@EnableDiscoveryClient 注解,但在ZUUL 中我们只需要添加一个
@EnableZuulProxy注解即可

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

添加配置

这里先使用最简单的配置,即配置应用名、端口、注册Eureka即可。后面再慢慢优化配置。

1
2
3
4
5
6
7
8
9
spring:
application:
name: microservice-gateway-zuul
server:
port: 8040
eureka:
client:
service-url:
defaultZone: http://user:admin@localhost:8761/eureka

测试

完成配置后则可以分别启动Eureka、用户微服务、Config Server(如果使用了)、microservice-gateway-zuul 项目。这样一个简单的微服务网关就完成了。我们可以通过 microservice-gateway-zuul 的路径加 微服务应用名 来访问微服务。
如访问用户微服务:http://localhost:8040/microservice-provider-user/1
微服务网关测试
这样所有的微服务都可以通过 Zuul 进行访问。
前面提到Zuul 可以配合Ribbon使用,我们不用加任何配置和依赖,即可以实现负载均衡,可以再启动一个用户微服务进行测试。

优化配置

上一节使用 Zuul 实现了最简单的微服务网关,在实际环境中需要对Zuul 进行优化配置。

路由

1、转发
在Zuul的配置中可以为各微服务添加路由映射,如添加配置:

1
2
3
zuul:
routes:
microservice-provider-user: /user/**

表示HTTP调用将 /user 转发到microservice-provider-user服务,于是我们访问用户微服务的地址可以简化为:
http://localhost:8040/user/1

/user/* 表示匹配其下的一个级别的路径,/user/** 表示匹配其下多个级别的路径。

另外 还有一种方式也可以实现:

1
2
3
4
5
zuul:
routes:
user:
path: /user/**
serviceId: microservice-provider-user

这种写法与上面的效果相同。zuul.routes.user 这里的 user 是只该路由名称,可以自己随意命名。
2、正则表达式指定路由规则
Zuul中可以写正则来指定路由规则,如微服务名命名规则为:微服务名+版本。通过添加以下正则规则 ,我们可以通过 版本 + 微服务名 来访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
@EnableZuulProxy
public class MicroserviceGatewayZuulApplication {
//正则表达式指定路由规则
@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
return new PatternServiceRouteMapper(
"(?<name>^.+)-(?<version>v.+$)",
"${version}/${name}");
}
public static void main(String[] args) {
SpringApplication.run(MicroserviceGatewayZuulApplication.class, args);
}
}

添加PatternServiceRouteMapper 传入两个参数,第一个为微服务的命名规则正则表达式,第二个是需要转化成什么形式的正则表达式。
(?<name>^.+)-(?<version>v.+$)表示我们的微服务命名 为服务名+版本;
${version}/${name} 表示访问路径转化成版本+服务名
测试
为了测试方便,把用户微服务的spring.application.name 更改为 microservice-provider-user-v1 ,如果使用了Config Server 把git上的配置也记得更改。
我们重启下Zuul,再次访问用户微服务会我发现原来的访问路径不行了。需要使用以下方式:http://localhost:8040/v1/microservice-provider-user/1
正则匹配

3、前缀
添加如下配置:

1
2
zuul:
prefix: /api

访问Zuul 的/api/microservice-provider-user/1会被映射到 /microservice-provider-user/1 即添加了前缀。
添加配置:

1
2
3
zuul:
prefix: /api
strip-prefix: false

配置 strip-prefix后,访问Zuul 的api/microservice-provider-user/1会被转发到 microservice-provider-user/api/1
strip-prefix需与prefix配合使用。

4、忽略微服务或路径
在有些情况下我们不想微服务网关去代理某个微服务,或者想保护某个微服务下了敏感路径可以使用以下配置:

1
2
zuul:
ignored-services: microservice-config-server

zuul.ignored-services 表示忽略指定的微服务,则通过Zuul不能访问到该微服务。

1
2
zuul:
ignored-patterns: /**/getProfile/**

zuul.ignored-patterns 表示忽略所以包含 /getProfile/的路径,上一篇笔记中我们为用户微服务添加了 /getProfile/ 现在可以测试已无法访问了,但其它路径则正常,这种方式常可以用来屏蔽 /admin/等比较敏感的路径。

Zuul 过滤器

Zuul 过滤器是Zuul的核心组件,Zuul中已经实现了一些过滤器,同时我们也可以自己定义过滤器。在Zuul中定义过滤器很简单,只需要继承 ZuulFilter 类。

自定义过滤器

在上面构建的 microservice-gateway-zuul中新建类 并继承 ZuulFilter 如下:

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
public class MyFilter extends ZuulFilter {
/**
* 过滤器类型
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的优先级,数字越大顺序越后
*/
@Override
public int filterOrder() {
return 1;
}
/**
* 是否使用该过滤器
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 具体实现
*/
@Override
public Object run() {
System.out.println("这里是通过ZuulFilter中打印出来的!");
return null;
}
}

继承 ZuulFilter后实现它的四个方法,作用可看上面的注释。
过滤器类型有如下几种:

PRE:这种过滤器在请求被路由之前调用,可以利用这种过滤器实现身份认证,记录调用信息等。
ROUTING:这种过滤器将请求路由到微服务。
POST:这种过滤器在路由到微服务后执行。
ERROR:在其他阶段发生错误时执行该过滤器。

在启动类中添加Bean

1
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication
@EnableZuulProxy
public class MicroserviceGatewayZuulApplication {
@Bean
public MyFilter myFilter(){
return new MyFilter();
}
public static void main(String[] args) {
SpringApplication.run(MicroserviceGatewayZuulApplication.class, args);
}
}

重新启动Zuul,随意访问一个微服务,可以在Zuul控制台看到打印了过滤器run方法中输出的内容:
过滤器输出内容

禁用过滤器

Zuul默认启用了一些过滤器,这些过滤器存放在spring-cloud-netflix-core包中的com.netflix.zuul.filters中,在发某些场景下,我们想禁用一些过滤器可以直接在配置文件中设置:
zuul.<SimpleClassName>.<filterType>.disable=true

SimpleClassName:是指过滤器类名
filterType:是过滤器类型
如禁用我们上面自定义过滤器可以这样写:

1
2
3
4
zuul:
MyFilter:
pre:
disable: true

重启Zuul再次通过Zuul访问微服务,会发现Zuul的控制台不会再打印run方法中的内容,说明已经被禁用了。