一、核心概念
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
*/
@EnableZuulProxy
@SpringCloudApplication
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=always
1、配置 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
- user-service-client
(二)将 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=always
3、激活服务发现
因为我们在 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
关键代码:
@Override
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
中的实现:
@Bean
public ZuulController zuulController() {
return new ZuulController();
}
@Bean
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