一、核心概念
1、网关
网关是程序或者系统之间的连接节点,扮演者程序或系统之间的门户,允许他们之间通过通讯协议交换信息,他们可能是同构和异构的系统。
例如:
- REST API 网关
- WebServices 网关
2、我对网关的理解
因为微服务架构会将一个大系统分成一个个独立的子系统,每个子系统都会有独立的 域名 或者 IP,若子系统非常多的话,就会导致使用者需要记住很多 URL,这样显然不是用户友好的。
而在引入了 Zuul(网关)之后,我们只要记住网关的 IP,然后将各个子系统映射的路径加上即可。比如:
zuul.routes.user-service-provider=/user-service/**就是将 user-service-provider 子系统映射为 /user-service/** 路径。原来我们访问 user-service-provider 的某个接口,比如:http;//localhost:9090/user/find/all,现在通过网关,我们只需要通过:http;//localhost:5050/user-service/user/find/all (5050是网关的端口),就可达到同样的效果。网关的作用就是在访问 http;//localhost:5050/user-service/user/find/all 时,将请求转换成 http;//localhost:9090/user/find/all。
二、整合 Zuul

创建 Zuul 代理应用:zuul-proxy
(一)添加 Zuul 依赖
<!-- 依赖 Spring Cloud Netflix Zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>(二)激活 Zuul
/**
* 注解 @EnableZuulProxy:激活 Zuul
* 注解 @SpringCloudApplication:激活 @SpringBootApplication、@EnableDiscoveryClient和EnableCircuitBreaker
*/
public class ZuulProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulProxyApplication.class, args);
}
}(三)配置 Zuul
# Zuul 代理应用
spring.application.name=zuul-proxy
#服务端口
server.port=5050
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always1、配置 Zuul 路由规则
# /* 当前层级匹配 /** 递归层级匹配
url.pattern=/user-service/**
#指定 user-service-provider(服务映射:zuul.routes.${serviceId}=${url.pattern})
zuul.routes.user-service-provider=${url.pattern}
#配置 Ribbon(这样是白名单方式,一旦引入 Eureka,这项配置需要删除)
user-service-provider.ribbon.listOfServers=http://localhost:9090/ 在如此配置之后,访问 http://localhost:5050/user-service/user/find/all ,会被 Zuul 转发至 http://localhost:9090/user/find/all。
2、配置 HTTP 客户端
注意:实际配置 Ribbon 底层 HTTP 调用客户端,并非 Zuul 独享此功能。
(1)默认客户端:HttpClient 装配类:HttpClientRibbonConfiguration
(2)可配置客户端:OkHttpClient 装配类:OkHttpRibbonConfiguration 激活配置:ribbon.okhttp.enabled=true
(三)Zuul 端点
实现:RoutesEndpoint 路径:/actuator/routes(即可发现所有的网关中的映射)
过滤器:
三、Spring Cloud 再整合
(一)服务依赖关系
- eureka-server
- user-service-provider
- config-server
- user-service-client
- zuul-proxy
(二)将 zuul-proxy 配置为 config-client
在 config-server 中新建 configs/zuul-config.properties:
#zuul proxy 配置内容
# /* 当前层级匹配 /** 递归层级匹配
url.pattern.provider=/user-service/**
url.pattern.client=/user-client/**
#指定 user-service-provider(服务映射:zuul.routes.${serviceId}=${url.pattern})
zuul.routes.user-service-provider=${url.pattern.provider}
#指定 user-service-client
zuul.routes.user-service-client=${url.pattern.client}(三)zuul-proxy 整合作为 配置客户端 和 服务发现客户端
1、增加依赖
<!-- 依赖 Config Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 增加 Eureka Client 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>2、配置 config-client 和 eureka-client
(1)bootstrap.properties(新建)
# Zuul 代理应用
spring.application.name=zuul-proxy
#配置客户端应用关联的应用(这里用配置文件 zuul-config.properties 文件名)
spring.cloud.config.name=zuul-config
#关联 profile
spring.cloud.config.profile=default
#关联 label
spring.cloud.config.label=master
#激活 config server 服务发现
spring.cloud.config.discovery.enabled=true
#config server 应用服务器的名称
spring.cloud.config.discovery.service-id=config-server
#eureka 客户端注册到 eureka 服务器
eureka.client.service-url.defaultZone=http://localhost:7070/eureka(2)application.properties(改造)
#服务端口
server.port=5050
#配置 Ribbon(这样是白名单方式,一旦引入 Eureka,这项配置需要删除)
#user-service-provider.ribbon.listOfServers=http://localhost:9090/
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always3、激活服务发现
因为我们在 ZuulProxyApplication 引导类上加了 @SpringCloudApplication 注解,该注解继承了 @EnableDiscoveryClient,所以无需在单独加注解激活。
三、过滤器 ZuulFilter
ZuulFilter 调用链:
-
ZuulServlet /ZuulServletFilter
-
ZuulRunner#preRoute() -
ZuulRunner#route() -
ZuulRunner#postRoute()
-
FilterProcessor#preRoute() -
FilterProcessor#route() -
FilterProcessor#postRoute()
-
FilterProcessor#runFilters()
-
ZuulFilter#runFilter()
-
ZuulFilter#run()
ZuulServlet 关键代码:
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
// Marks this request as having passed through the "Zuul engine", as opposed to servlets
// explicitly bound in web.xml, for which requests will not have the same data attached
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
preRoute();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}四、Zuul 自动装配
ZuulServletFilter 使用范围更大,可以拦截所有 Servlet ,包括 ZuulServlet 。
ZuulServlet 会有URL匹配模式,url-pattern。
Zuul 两种激活模式:
1、@EnableZuulProxy
导入 ZuulProxyMarkerConfiguration,随后生成一个 ZuulProxyMarkerConfiguration.Marker() Bean,这个 Bean 作为 ZuulProxyAutoConfiguration 装配的前置条件。
注意:ZuulProxyAutoConfiguration 扩展了 ZuulServerAutoConfiguration,所以 ZuulController 和 ZuulServlet 会被自动装配。
ZuulController 由 DispatcherServlet 来控制,它的映射地址是:/*。DispatcherServlet 中注册了一个 ZuulHandlerMapping ,它控制映射到 ZuulController,具体实现可以参考 ZuulServerAutoConfiguration 中的实现:
public ZuulController zuulController() {
return new ZuulController();
}
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
mapping.setErrorController(this.errorController);
return mapping;
} 通过源码分析,ZuulController 将请求委派给 ZuulServlet ,所以所有的 ZuulFilter 实例都会被执行。
因此,访问 http://localhost:5050/user-service-client/user/find/all ,实际上是将请求递交给 DispatcherServlet 发送请求"/user-service-client/user/find/all"
调用链:
-
DispatcherServlet
-
ZuulHandlerMapping
-
ZuulController
-
ZuulServlet
-
RibbonRoutingFilter
2、@EnableZuulServer
导入 ZuulServerMarkerConfiguration ,随后生成一个 ZuulServerMarkerConfiguration.Marker() Bean,主要用作装配 ZuulServerAutoConfiguration。
ZuulProxyAutoConfiguration 与 父类 ZuulServerAutoConfiguration 区别:ZuulProxyAutoConfiguration 提供了 RibbonRoutingFilter。
调用链:
-
DispatcherServlet
-
ZuulHandlerMapping
-
ZuulController
-
ZuulServlet
-
ZuulFilter









