Zuul 网关
- 路由在微服务体系结构中的一个组成部分;Zuul是Netflix的基于JVM的路由和服务器端负载均衡器。
- 可以使用Zuul进行一下操作:
- Authentication(认证)
- 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
- Insights(审查与监控)
- 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
- Stress Testing(压力测试)
- 逐渐增加指向集群的负载流量,从而计算性能水平。
- Canary Testing(金丝雀测试)
- Dynamic Routing(动态路由)
- 以动态方式根据需要将请求路由至不同后端集群处。
- Service Migration(服务迁移)
- Load Shedding(负载均衡)
- Security(安全)
- Static Response handling(静态响应处理)
- 在边缘位置直接建立部分响应,从而避免其流入内部集群。
- Automatic/Active traffic management(自动/手动流量管理)
- Authentication(认证)
- Zuul的规则引擎允许基本上写任何JVM语言编写规则和过滤器,内置Java和Groovy。
- 注意:
- 配置属性zuul.max.host.connections已被两个新属性zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections替换,分别默认为200和20。
- 所有路由的默认Hystrix隔离模式(ExecutionIsolationStrategy)为SEMAPHORE。如果首选隔离模式,则zuul.ribbonIsolationStrategy可以更改为THREAD。
Zuul网关配置
-
配置跨域
- 默认情况下,Zuul将所有跨源请求路由到服务。如果你想让Zuul来处理这些请求,可以提供定制的WebMvcConfigurer bean:
-
@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/path-1/**") .allowedOrigins("http://allowed-origin.com") .allowedMethods("GET", "POST"); } }; }
- allowedOrigins,allowedMethods,allowedHeaders,exposedHeaders,allowCredentials and maxAge via this configuration.
-
pom.xml
-
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
-
-
application.yml
-
spring: application: name: hyq-zuul-server profiles: active: instance1 # setting up spring eureka service discovery eureka: instance: prefer-ip-address: true instance-id: ${spring.application.name}:${server.port} # 路由配置:实际开发过程中放到config配置中心 zuul: routes: # 别名 eureka-client: # 通配符说明: # ?: 匹配任意单字符 eg:/client/? 匹配 /client/a /client/b # *: 匹配任意数量字符 eg: /client/* 匹配 /client/user # **: 匹配任意数量字符,支持多久目录 eg: /client/** 匹配 /client/user/info path: /client/** serviceId: HYQ-EUREKA-CLIENT #如果是/ribbon/**路径下的请求,则跳转到service-ribbon eureka-server: path: /server/** serviceId: HYQ-HSYTRIX-CLIENT #如果是/feign/**路径下的请求,则跳转到service-feign # 使用zuul.ignored-services配置需要忽略的服务,多个用逗号分隔 ignored-services: '*' #只代理配置的微服务 ignored-patterns: /client/** # 忽略所有包含client/** 的路径 # 暴露 routes 接口:默认暴露health,info # 查看以及管理Zuul的路由:http://localhost:4001/actuator/routes management: endpoints: web: exposure: include: routes --- spring: profiles: instance1 server.port: 4001 eureka: client: serviceUrl: # defaultZone: http://localhost:8081/eureka/,http://localhost:8082/eureka/,http://localhost:8083/eureka/ defaultZone: http://admin:admin@localhost:8081/eureka/ # 配默认熔断超时策略 # zuul配置了熔断fallback的话,熔断超时也要配置, # 不然如果你配置的ribbon超时时间大于熔断的超时,那么会先走熔断,相当于你配的ribbon超时就不生效了。 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 60000 # ribbon超时时间设置 # zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis这两个配置, # 这两个和上面的ribbon都是配超时的。区别在于,如果路由方式是serviceId的方式,那么ribbon的生效, # 如果是url的方式,则zuul.host开头的生效。(此处重要!使用serviceId路由和url路由是不一样的超时策略) ribbon: ReadTimeout: 60000 ConnectTimeout: 60000 # 如果要指定具体某个服务配置超时时间: 就将default改为对应的 server_id即可 eg: # hystrix.command.user_server.execution.isolation.thread.timeoutInMilliseconds: 6000 # user_server.ribbon.ReadTimeout: 6000
-
-
zuul.routes.eureka-client.url: http://localhost:6002/eureka-client,http://localhost:6003/feign-client
- 这些简单的URL路由不会被执行为HystrixCommand,也不能使用Ribbon对多个URL进行负载平衡。为此,请指定service-route并为serviceId配置Ribbon客户端
- 方法一:指定服务路由并为serviceId配置一个Ribbon客户端
-
zuul: routes: users: path: /myusers/** serviceId: users # 在Ribbon中禁用Eureka使用 ribbon: eureka: enabled: false users: ribbon: listOfServers: example.com,google.com
- 方法二:使用静态服务器列表指定serviceId
-
zuul: routes: echo: path: /myusers/** serviceId: myusers-service stripPrefix: true hystrix: command: myusers-service: execution: isolation: thread: timeoutInMilliseconds: ... myusers-service: ribbon: NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList listOfServers: http://example1.com,http://example2.com ConnectTimeout: 1000 ReadTimeout: 3000 MaxTotalHttpConnections: 500 MaxConnectionsPerHost: 100
-
Zuul Http客户端
- zuul使用的默认HTTP客户端现在由Apache HTTP Client支持,而不是不推荐使用的Ribbon RestClient。
- 要分别使用RestClient或使用okhttp3.OkHttpClient集合ribbon.restclient.enabled=true或ribbon.okhttp.enabled=true。
-
Cookie和敏感标题
- application.yml
-
zuul: routes: users: path: /myusers/** sensitiveHeaders: Cookie,Set-Cookie,Authorization url: https://downstream
- sensitiveHeaders是一个黑名单,默认值不为空,所以要使Zuul发送所有标题(“被忽略”除外),您必须将其显式设置为空列表。如果您要将Cookie或授权标头传递到后端,这是必要的。
- 除了每个路由的敏感标头,您还可以为与下游服务交互期间应该丢弃的值(请求和响应)设置全局值为zuul.ignoredHeaders。默认情况下,如果Spring安全性不在类路径上,则它们是空的,否则它们被初始化为由Spring Security指定的一组众所周知的“安全性”头(例如涉及缓存)。在这种情况下的假设是下游服务可能也添加这些头,我们希望代理的值。为了不丢弃这些众所周知的安全标头,只要Spring安全性在类路径上,您可以将zuul.ignoreSecurityHeaders设置为false。如果您禁用Spring安全性中的HTTP安全性响应头,并希望由下游服务提供的值,这可能很有用
-
通过Zuul上传文件
- @EnableZuulProxy您可以使用代理路径上传文件,只要文件很小,它就应该工作。
- 对于大文件,有一个替代路径绕过“/ zuul / *”中的Spring DispatcherServlet(以避免多部分处理)。也就是说,如果zuul.routes.customers=/customers/**则可以将大文件发送到“/ zuul / customers / *”。servlet路径通过zuul.servletPath进行外部化。
- application.yml
-
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000 ribbon: ConnectTimeout: 3000 ReadTimeout: 60000
-
查询字符串编码
- 要强制查询字符串的原始编码,可以将特殊标志传递给ZuulProperties,以便查询字符串与HttpServletRequest::getQueryString方法相同:
- application.yml
-
zuul: forceOriginalQueryStringEncoding: true
- 此特殊标志仅适用于SimpleHostRoutingFilter,您可以使用RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)轻松覆盖查询参数,因为查询字符串现在直接在原始的HttpServletRequest上获取。
Zuul提供Hystrix回退
- 当Zuul中给定路由的电路跳闸时,您可以通过创建类型为ZuulFallbackProvider的bean来提供回退响应。在这个bean中,您需要指定回退的路由ID,并提供返回的ClientHttpResponse作为后备。
-
package com.hyq.zuulserver.config; import com.netflix.zuul.context.RequestContext; import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * Hystrix熔断 */ @Component public class ZuulFallBackProvider implements FallbackProvider { /** * 需要熔断的 路由: 如果是全部路由即:return "*" 或者 return null; * @return */ @Override public String getRoute() { // return "HYQ-HSYTRIX-CLIENT"; return "*"; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { return new ClientHttpResponse() { /** * 响应 Header * @return Header */ @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } /** * 响应体:Body * @return Body * @throws IOException 异常 */ @Override public InputStream getBody() throws IOException { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String resp = request.getRequestURI()+"=="+request.getMethod()+"=="+request.getRequestedSessionId(); return new ByteArrayInputStream(resp.getBytes()); } /** * 状态码 * @return 状态码 * @throws IOException 异常 */ @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } /** * 状态码 * @return 状态码 * @throws IOException 异常 */ @Override public int getRawStatusCode() throws IOException { return 200; } /** * 状态码 * @return 状态码 * @throws IOException 异常 */ @Override public String getStatusText() throws IOException { return "OK"; } @Override public void close() { } }; } }
Filter 过滤器
-
代码示例1:
-
package com.hyq.zuulserver.config; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_ERROR_FILTER_ORDER; /** * 过滤器: */ @Component public class ApiGatewayFilter extends ZuulFilter { /** * 过滤器的类型:它决定过滤器在请求的哪个生命周期中执行 * 按类型对筛选器进行分类。Zuul中的标准类型是“pre”,用于预路由过滤, * *“route”用于路由到一个原点,“post”用于路由后过滤器,“error”用于错误处理。 * *我们还支持静态响应的“static”类型,参见StaticResponseFilter。 * *通过调用FilterProcessor.runFilters(type)来创建或添加和运行任何filterType * * @return 枚举值:pre, route, post, error,static */ @Override public String filterType() { return PRE_TYPE; } /** * 过滤器执行的顺序:当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行 * * @return 优先级, 0是最高优先级即最先执行 */ @Override public int filterOrder() { return SEND_ERROR_FILTER_ORDER; } /** * 判断该过滤器是否需要被执行 * * @return 写逻辑,是否需要执行过滤。true会执行run函数,false不执行run函数 */ @Override public boolean shouldFilter() { return true; } /** * 过滤器的具体逻辑。 * * @return 结果 * @throws ZuulException 异常 */ @Override public Object run() throws ZuulException { System.out.println("Zuul Filter Is Working..."); RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); System.out.println("Zuul LOG: " + request.getMethod() + "====" + request.getRequestURI()); return null; } }
-
-
代码示例2:
-
package com.hyq.zuulserver.config; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; import java.util.UUID; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER; /** * 后置过滤器(post 过滤器):过滤器添加了一个随机UUID作为X-Sample头文件: */ @Component public class MyResponseHeaderFilter extends ZuulFilter { @Override public String filterType() { return POST_TYPE; } @Override public int filterOrder() { return SEND_RESPONSE_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext context = RequestContext.getCurrentContext(); HttpServletResponse servletResponse = context.getResponse(); servletResponse.addHeader("X-Sample", UUID.randomUUID().toString()); return null; } }
-