目录
Zuul
简介
功能
工作流程
搭建
1.引入依赖
2.添加注解
3.路由转发
4.测试
实现原理
@EnableZuulProxy注解
ZuulServlet
FilterProcessor
Zuul内置过滤器
常用配置
Zuul
简介
zuul是SpringCloud子项目的核心组件之一,可以作为微服务架构中的API网关使用,支持动态路由和过滤功能;
功能
1.统一入口:为全部服务提供一个唯一的入口,网关起到了外部和内部的隔离作用
2.鉴权校验:识别每一个请求的权限,拒绝不符合要求的请求
3.动态路由:动态的将请求路由到不同的后端集群中
4.减少客户端与服务器端的耦合:服务可以独立发展,通过网关来进行映射
工作流程
Zuul本质是一个Servlet来对请求进行控制,核心是创建了一系列的过滤器。Zuul包括以下四种过滤器:
1、PRE过滤器:请求路由到具体的服务之前执行,可以用作安全校验、身份校验、参数校验等前置工作;
2、ROUTING过滤器:用于将请求路由到具体的服务实例,默认使用Http Client进行网络请求;
3、POST过滤器:在请求已经被路由到微服务后执行,通常用于收集统计信息、指标并将响应返回给客户端;
4、ERROR过滤器:在其他过滤器出现异常时执行;
各个类型的过滤器执行顺序依次为PRE过滤器->ROUTING过滤器->POST过滤器,如果出现异常就执行ERROR过滤器
搭建
1.引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- zuul 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2.添加注解
启动类添加注解 @EnableZuulProxy
@SpringBootApplication
@EnableZuulProxy //开启Zuul
@EnableEurekaClient
public class ZuulMain9401 {
public static void main(String[] args) {
SpringApplication.run(ZuulMain9401.class, args);
}
}
因为不涉及服务消费等,只是做api的处理,所以主启动类还是比较简单的
3.路由转发
1.在原有的application.yml配置文件上添加:
zuul:
routes:
user-a:
path: /api-a/**
serviceId: eureka-provide
user-a:随便定义
path:外部访问路径
serviceId:微服务配置文件的spring.application.name
的值
2.设置url
zuul:
routes:
# user-a:
# path: /api-a/**
# serviceId: eureka-provide
user-b:
path: /api-b/**
url: http://localhost:7101/
url:需要转发到哪个服务
3.设置非注册Eureka的服务id
通过Ribbon设置访问一些没有注册进Eureka的服务,同样在API网关也能通过配置文件设置Ribbon来达到一样的效果
zuul:
routes:
# user-a:
# path: /api-a/**
# serviceId: eureka-provide
# user-b:
# path: /api-b/**
# url: http://localhost:7101/
user-c:
path: /api-c/**
serviceId: provide-without-eureka
#一定需要这个才行
ribbon:
eureka:
enabled: false
provide-without-eureka:
ribbon:
ServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
listOfServers: localhost:7201, localhost:7202
ConnectTimeout: 1000
ReadTimeout: 3000
MaxTotalHttpConnections: 500
MaxConnectionsPerHost: 100
serviceId同样是微服务的名称,然后对这个微服务设置,所以是设置微服务名[provid-without-eureka].ribbon
4.测试
接着访问http://localhost:8080/api-a/eureka/provide,按照分析,应该会被转发到eureka-provide
服务里的eureka/provide路径。
实现原理
@EnableZuulProxy注解
zuul的使用比较简单,只需要在启动类上添加@EnableZuulProxy注解即可,zuul所有的功能都是围绕改注解进行,@EnableZuulProxy的作用是加载ZuulProxyMarkerConfiguration的实例:
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}
ZuulProxyMarkerConfiguration的作用是注入一个ZuulProxy的标记实例
@Configuration
public class ZuulProxyMarkerConfiguration {
@Bean
public Marker zuulProxyMarkerBean() {
return new Marker();
}
class Marker {
}
}
内部类Marker唯一的作用就是用于标记,所以需要看哪里需要用到该标记,通过引用搜索发现引用的地方是ZuulProxyAutoConfiguration,父类是ZuulServerAutoConfiguration
@Configuration
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
// Make sure to get the ServerProperties from the same place as a normal web app would
// FIXME @Import(ServerPropertiesAutoConfiguration.class)
public class ZuulServerAutoConfiguration {
父类ZuulServerAutoConfiguration中会向Spring容器中注入ServletRegistrationBean实例,该实例中创建了一个ZuulServlet。而Zuul的核心功能就在于这个ZuulServlet,Zuul接收到的所有请求最终都会由于ZuulServlet的service方法来完成
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(new ZuulServlet(),
this.zuulProperties.getServletPattern());
// The whole point of exposing this servlet is to provide a route that doesn't
// buffer requests.
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}
ZuulServlet
ZuulServlet继承之HttpServlet,初始化init方法创建了ZuulRunner对象,而核心功能在于service方法,所有请求都会先执行到service方法
@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
/** 1.初始化ZuulRunner 对象*/
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
/** 2.获取HttpRequest请求上下文*/
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
/** 3.执行pre过滤器*/
preRoute();
} catch (ZuulException e) {
/** 异常时执行error过滤器和post过滤器*/
error(e);
postRoute();
return;
}
try {
/** 4.执行route过滤器*/
route();
} catch (ZuulException e) {
/** 异常时执行error过滤器和post过滤器*/
error(e);
postRoute();
return;
}
try {
/** 5.执行post过滤器*/
postRoute();
} catch (ZuulException e) {
/** 异常时执行error过滤器*/
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
/** 清理本地缓存RequestContext*/
RequestContext.getCurrentContext().unset();
}
}
FilterProcessor
FilterProcessor是过滤器的具体执行者,是一个单例对象。ZuulServlet的各种过滤器最终分别执行了FilterProcessor的preRoute()、route()、postRoute()、error()方法,而这四个方法最终都是执行了FilterProcessor的内部方法runFilters(String filterType)方法。
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
/** 1.获取指定类型的过滤器列表*/
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
/** 2.遍历执行过滤器*/
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
Zuul内置过滤器
Zuul过滤器可以通过自定义ZuulFilter实现类来添加,同时Zuul还提供了内置的众多过滤器,分别如下图示,比较核心的就是RibbonRoutingFilter,这个过滤器负载路由转发并且集成了Ribbon的负载均衡功能:
Zuul本质上就是一个Servlet,并且通过Spring容器管理了一系列的过滤器ZuulFilter实例,各个ZuulFilter有一个类型,包括preRoute、route、postRoute、error四种类型,不同的类型执行的顺序和时机不同。当请求进入Zuul时,由ZuulServlet接收并通过service方法处理。service方法逻辑就是从Spring容器中找到各种类型的ZuulFilter过滤器实例,然后遍历按顺序和时机来执行过滤器的处理逻辑。使用时可以自定义ZuulFilter来对请求的不同时间短进行功能扩展。
常用配置
zuul:
#给服务配置路由
routes:
user-service:
path: /userService/**
feign-service:
path: /feignService/**
#关闭默认路由配置
ignored-services: user-service,feign-service
#给网关路由添加前缀
prefix: /proxy
#配置过滤敏感的请求头信息,设置为空就不会过滤
sensitive-headers: Cookie,Set-Cookie,Authorization
#设置为true重定向是会添加host请求头
add-host-header: true
# 关闭重试机制
retryable: true
PreLogFilter:
pre:
#控制是否启用过滤器
disable: false