这是SpringCloud实战系列中第4篇文章,了解前面第两篇文章更有助于更好理解本文内容:
①SpringCloud 实战:引入Eureka组件,完善服务治理
②SpringCloud 实战:引入Feign组件,发起服务间调用
③SpringCloud 实战:使用 Ribbon 客户端负载均衡
④SpringCloud 实战:引入Hystrix组件,分布式系统容错java
Zuul 也是 Netflix OSS 中的一员,是一个基于 JVM 路由和服务端的负载均衡器,支持动态路由、监控、弹性和安全等特性。Spring Cloud 会建立一个嵌入式 Zuul 代理来简化一个常见用例的开发,好比用户程序可能会对一个或多个后端服务进行调用,引入 Zuul 网关能有效避免为全部后端独立管理CORS和身份验证问题的需求web
Zuul的使用了一系列的过滤器,这些过滤器能够完成如下功能:spring
新建一个新的项目jlw-zuul
后端
引入zuul依赖api
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
启动类上加入注解@EnableZuulProxy
跨域
引入Eureka注册中心,并注册上去安全
如今不须要额外配置就能够启动了,启动以后你会看到默认的服务映射相关日志:cookie
Mapped URL path [/eureka-provider-temp/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/eureka-server/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/eureka-provider/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/ribbon-client/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] Mapped URL path [/eureka-client/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
而后就能够经过zuul网关来访问后端服务了并发
Zuul 默认依赖了 actuator,而且会暴露/actuator/routes
和/actuator/filters
两个端点,访问这两个断点,能够很直观的查看到路由信息,在查看以前须要添加如下配置:app
# 应该包含的端点ID,所有:* management.endpoints.web.exposure.include: *
访问http://127.0.0.1:8000/actuator能够查看全部端点信息,访问http://127.0.0.1:8000/actuator/routes 可查看到路由信息:
为网关添加前缀
# 访问网关的时候必需要加的路径前缀 zuul.prefix = /api
添加以上配置后,访问网关时路径必须是/api/**,而后才会正确的路由到后端对应的服务
若是在转发请求到服务的时候要去掉这个前缀,能够设置strip-prefix= false来忽略
# 请求转发前是否要删除 zuul.prefix 设置的前缀 ,true:转发前要带上前缀(默认值),fasle:不带上前缀 zuul.routes.ecs.strip-prefix = true
配置路由
# 忽略注册中心 eureka-server,*:会忽略全部的服务 zuul.ignored-services = eureka-server,eureka-client # eureka-client 服务映射规则,http://127.0.0.1:8000/ec/sayHello zuul.routes.eureka-client = /ec/**
上面的配置会忽略eureka-server和eureka-client,访问http://127.0.0.1:8000/api/ec/**
的请求的都会被路由到eureka-client,若是没有忽略eureka-client,则访问/eureka-client/**
和/ec/**
都会路由到eureka-client服务。
注意/ec/*
只会匹配一个层级,/ec/**
会匹配多个层级。
指定服务id和path
# 指定service-id和path zuul.routes.rcs.service-id = ribbon-client zuul.routes.rcs.path = /rc/**
而后访问http://127.0.0.1:8000/api/rc/queryPort接口就会被路由到ribbon-client
服务
路由配置顺序
若是想按照配置的顺序进行路由规则控制,则须要使用YAML,若是是使用propeties文件,则会丢失顺序。例如:
zuul: routes: users: path: /myusers/** legacy: path: /**
使用propeties文件,旧的路径可能出如今用户路径的前面,从而致使用户路径没法访问。
关闭重试
能够经过将zuul.retryable设置为false来关闭Zuul的重试功能,默认值也是false。
zuul.retryable=false
还能够经过将zuul.routes.routename.retryable设置为false来禁用逐个路由的重试功能
# 关闭指定路由的重试 zuul.routes.ecs.retryable = false
添加如下配置会忽略指定的服务,很明显注册中心通常是不须要经过网关来访问的,因此须要忽略它
# 忽略注册中心 eureka-server,*:会忽略全部的服务 zuul.ignored-services = eureka-server
也能够经过zuul.ignoredPatterns
来配置你不想暴露出去的API
# 改成信号量隔离 zuul.ribbon-isolation-strategy=semaphore # Hystrix的最大总信号量 zuul.semaphore.max-semaphores=1000 # 单个路由可使用的最大链接数 zuul.host.max-per-route-connections=500
最大总信号量默认是100,单个路由最大的链接数默认是20,有时候并发量上不去可能就是使用的默认配置。
Zuul 中默认采用信号量隔离机制,若是想要换成线程,须要配置 zuul.ribbon-isolation-strategy=THREAD
,配置后全部的路由对应的 Command 都在一个线程池中执行,这样其实达不到隔离的效果,因此咱们须要增长一个 zuul.thread-pool.use-separate-thread-pools 的配置,让每一个路由都使用独立的线程池,zuul.thread-pool.thread-pool-key-prefix 能够为线程池配置对应的前缀,方便调试。
## 线程隔离 #zuul.ribbon-isolation-strategy=THREAD ## 每一个路由使用独立的线程池 #zuul.thread-pool.use-separate-thread-pools=true ## 线程池前缀 #zuul.thread-pool.thread-pool-key-prefix=zuul-pool-
Zuul默认使用的是 Apache HTTP Client,须要更换的话只须要设置对应的属性便可
# Ribbon RestClient ribbon.restclient.enabled=true # or okhttp ribbon.okhttp.enabled=true
Zuul 提供了一个敏感头属性配置,设置了该属性后,Zuul 就不会把相关的请求头转发到下游的服务,好比:
# 请求头里面的字段不会带到eureka-client服务 zuul.routes.ecs.sensitive-headers = jinglingwang
sensitiveHeaders 的默认值是Cookie、Set-Cookie、Authorization,若是把该值配置成空值,则会把全部的头都传递到下游服务。
还能够经过设置zuul.sensitiveHeaders来设置全局的敏感标头。 若是在路由上设置了sensitiveHeaders,它将覆盖全局的sensitiveHeaders设置
除了对路由敏感的标头单独设置以外,还能够设置一个名为zuul.ignoredHeaders的全局值,好比:
# 该配置的Header也不会转发到下游服务 zuul.ignored-headers=jinglingwang
在默认状况下是没有这个配置的,若是项目中引入了Spring Security,那么Spring Security会自动加上这个配置,默认值为: Pragma,Cache-Control,X-Frame-Options,X-Content-Type-Options,X-XSS-Protection,Expries。
下游服务须要使用Spring Security的Header时,能够增长zuul.ignoreSecurityHeaders=false
的配置
经过Zuul网关上传文件时,只要文件不大,均可以正常的上传,对于大文件,Zuul有一个替代路径(/zuul/*
)能够绕过Spring DispatcherServlet,好比你的文件服务(file-service)路由配置是zuul.routes.file-service=/file/**
,而后你post提交文件到/zuul/file/**
便可。
还有一种办法就是直接修改可上传文件大小的配置:
# 文件最大值。值可使用后缀“ MB”或“ KB”分别表示兆字节或千字节 spring.servlet.multipart.max-file-size=10MB # 最大请求大小 spring.servlet.multipart.max-request-size=30MB
两种办法都须要在文件服务里面添加以上的配置
在上传大文件时也须要设置合理的超时时间:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000 ribbon: ConnectTimeout: 3000 ReadTimeout: 60000
过滤器是 Zuul 中的核心内容,不少高级的扩展都须要自定义过滤器来实现,在 Zuul 中自定义一个过滤器只须要继承 ZuulFilter,而后重写 ZuulFilter 的四个方法便可:
@Component public class LogFilter extends ZuulFilter{ /** * 返回过滤器的类型,可选值有 pre、route、post、error 四种类型 * @return */ @Override public String filterType(){ return "pre"; } /** * 指定过滤器的执行顺序,数字越小,优先级越高 * 默认的filter的顺序能够在FilterConstants类中查看。 * @return */ @Override public int filterOrder(){ // pre filter return PRE_DECORATION_FILTER_ORDER - 1 ; // ROUTE filter //return SIMPLE_HOST_ROUTING_FILTER_ORDER - 1 ; // POST filter //return SEND_RESPONSE_FILTER_ORDER - 1 ; } /** * 决定了是否执行该过滤器,true 为执行,false 为不执行 * @return */ @Override public boolean shouldFilter(){ return true; } /** * 若是shouldFilter()为true,则将调用此方法。该方法是ZuulFilter的核心方法 * @return 返回值会被忽略 * @throws ZuulException */ @Override public Object run() throws ZuulException{ HttpServletRequest req = (HttpServletRequest) RequestContext.getCurrentContext().getRequest(); System.out.println("ZUUL REQUEST:: " + req.getScheme() + " " + req.getRemoteAddr() + ":" + req.getRemotePort() + " uri::"+ req.getRequestURI()) ; return null; } }
Zuul 默认提供了不少过滤器(ZuulFilter),有关可启用的过滤器列表,能够参考Zuul 过滤器的包(netflix.zuul.filters)。若是要禁用一个过滤器,能够按照zuul.<SimpleClassName>.<filterType>.disable=true
格式来进行设置,好比:
zuul.SendResponseFilter.post.disable=true
若是是外部网页应用须要调用网关的 API,不在同一个域名下则会存在跨域的问题,想让Zuul处理这些跨域的请求,能够经过提供自定义WebMvcConfigurer bean来完成:
@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { /** * 配置跨源请求处理 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/path/**") .allowedOrigins("https://jinglingwang.cn") .allowedMethods("GET", "POST"); } }; }
上面的示例中,容许jinglingwang.cn
的GET
和POST
方法将跨域请求发送到 /path/**开头的端点
有两种状况:
若是Zuul使用服务发现,则须要配置Ribbon的属性配置超时
ribbon.ReadTimeout ribbon.SocketTimeout
若是经过指定URL配置了Zuul路由
# 套接字超时(以毫秒为单位)。默认为10000 zuul.host.socket-timeout-millis=15000 # 链接超时(以毫秒为单位)。默认为2000 zuul.host.connect-timeout-millis=3000
Spring Cloud 中,Zuul 默认整合了 Hystrix,当Zuul中给定路由的电路跳闸时,能够经过建立FallbackProvider类型的bean提供回退响应。配置示例代码以下:
@Component public class EurekaClientFallbackProvider implements FallbackProvider{ @Override public String getRoute(){ // 路由的server-id,* or null:为全部的路由都配置回退 return "eureka-client"; } @Override public ClientHttpResponse fallbackResponse(String route,Throwable cause){ if (cause instanceof HystrixTimeoutException) { return response(HttpStatus.GATEWAY_TIMEOUT); } else { return response(HttpStatus.INTERNAL_SERVER_ERROR); } } private ClientHttpResponse response(HttpStatus status){ return new ClientHttpResponse(){ @Override public HttpStatus getStatusCode() throws IOException{ return status; } @Override public int getRawStatusCode() throws IOException{ return status.value(); } @Override public String getStatusText() throws IOException{ return status.getReasonPhrase(); } @Override public void close(){ } @Override public InputStream getBody() throws IOException{ return new ByteArrayInputStream("eureka-client 服务暂不可用,jinglingwang请你稍后重试!".getBytes()); } @Override public HttpHeaders getHeaders(){ HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8); return headers; } }; } }
重启后,运行效果以下:
若是要为全部路由提供默认回退,getRoute方法返回*或null便可。
Zuul内部使用Ribbon来调用远程URL。 默认状况下,Ribbon 客户端在第一次调用时由Spring Cloud进行延迟加载。能够经过如下配置来开启启动时当即加载:
zuul.ribbon.eager-load.enabled=true
Spring Cloud Netflix安装了不少过滤器,具体取决于用于启用Zuul的注解。 @EnableZuulProxy是@EnableZuulServer的超集。换句话说,@ EnableZuulProxy包含@EnableZuulServer安装的全部过滤器。 “proxy”中的其余过滤器启用路由功能。 若是须要一个“空白”的 Zuul,则应使用@EnableZuulServer。