网关 Spring Cloud Zuul 常见功能介绍(翻译)

zuul.stripPrefix仅适用于中设置的前缀zuul.prefix。它对给定路由中定义的前缀没有任何影响path

在前面的例子中,请求到/myusers/101转发到/myusers/101users服务。

这些zuul.routes条目实际上绑定到类型为的对象ZuulProperties。如果查看该对象的属性,则可以看到它也有一个retryable标志。设置该标志以true使功能区客户端自动重试失败的请求。您还可以将该标志设置为何true时需要修改使用功能区客户端配置的重试操作的参数。

默认情况下,X-Forwarded-Host标头被添加到转发的请求中。要关闭它,请设置zuul.addProxyHeaders = false。默认情况下,前缀路径被剥离,并且到后端的请求选择一个X-Forwarded-Prefix标头(/myusers在前面显示的示例中)。

如果设置默认路由(/),则具有的应用程序@EnableZuulProxy可以充当独立服务器。例如,zuul.route.home: /将所有流量(“ / **”)路由到“家庭”服务。

如果需要更细粒度的忽略,则可以指定要忽略的特定模式。这些模式在路线定位过程开始时进行评估,这意味着模式中应包含前缀以保证匹配。被忽略的模式跨越所有服务,并取代任何其他路由规范。以下示例显示了如何创建忽略的模式:

application.yml

zuul:

ignoredPatterns: //admin/

routes:

users: /myusers/**

前面的示例意味着所有呼叫(例如/myusers/101)都被转发到/101users服务上。但是,包括在内的呼叫/admin/无法解决。

如果您需要保留路线的顺序,则需要使用YAML文件,因为使用属性文件时顺序会丢失。以下示例显示了这样的YAML文件:

application.yml

zuul:

routes:

users:

path: /myusers/**

legacy:

path: /**

如果要使用属性文件,则该legacy路径可能会终止于该users 路径的前面,从而导致该users路径不可访问。

Http 客户端选择


Zuul的默认HTTP客户端是Apache HTTP客户端而不是已经过时的Ribbon的RestClient。如果要使用RestClient或者okhttp3.OkHttpClient,可以设置ribbon.restclient.enabled=true 或者 ribbon.okhttp.enabled=true。

如果你想自定义Apache HTTP client 或者 OK HTTP client,那么需要提供一个ClosableHttpClient 或者 OkHttpClient类型的bean。

Cookies and Sensitive Headers(cookies和敏感头部)


您可以在同一系统中的服务之间共享标头,但您可能不希望敏感标头泄漏到下游到外部服务器中。您可以在路由配置中指定忽略的标头列表。Cookies发挥着特殊的作用,因为它们在浏览器中具有定义明确的语义,并且始终将它们视为敏感内容。如果代理的使用者是浏览器,那么下游服务的cookie也会给用户带来麻烦,因为它们都混杂在一起(所有下游服务看起来都来自同一位置)。

如果您对服务的设计很谨慎(例如,如果只有一个下游服务设置cookie),则可以让它们从后端一直流到调用者。另外,如果您的代理设置cookie,并且所有后端服务都属于同一系统,则自然可以简单地共享它们(例如,使用Spring Session将它们链接到某些共享状态)。除此之外,由下游服务设置的任何cookie可能对调用者都没有用,因此建议您(至少)将Set-CookieCookie放入不属于您域的路由的敏感标头中。即使对于属于您网域的路由,在让Cookie在它们和代理之间流动之前,也应仔细考虑其含义。

可以将敏感头配置为每个路由的逗号分隔列表,如以下示例所示

zuul:

routes:

users:

path: /myusers/**

sensitiveHeaders: Cookie,Set-Cookie,Authorization

url: https://downstream

注意:上面的例子是sensitiveHeaders的默认值,这是在Spring Cloud Netflix 1.1版本新增的功能。(在1.0中,不能设置头部并且所有cookie双向流动)。

sensitiveHeaders是一个黑名单,默认不为空。因此要让Zuul发送所有头部的话,需要明确指定sensitiveHeaders为空。如下:

zuul:

routes:

users:

path: /myusers/**

sensitiveHeaders:

url: https://downstream

您还可以通过设置设置敏感的标题zuul.sensitiveHeaders。如果sensitiveHeaders在路径上设置了,则它将覆盖全局sensitiveHeaders设置。

忽略头部


除了路由敏感头部以外,你还可以设置zuul.ignoredHeaders成那些在与下游服务交互时应该剔除的值。默认,Spring Security不在classpath上时,这些值是空的。否则他们被Spring Security初始化为一些常见的“安全”头部。

在这种情况下,假设下游服务也可以添加这些标头,但是我们需要来自代理的值。要在Spring Security位于类路径上时不丢弃这些众所周知的安全标头,可以将设置zuul.ignoreSecurityHeadersfalse。如果您在Spring Security中禁用了HTTP Security响应标头并需要下游服务提供的值,则这样做很有用。

管理端点


如果你同时使用@EnableZuulProxy 和Spring Boot Actuator,那么将会开启两个额外的端点:

  • Routes

  • Filters

Routes Endpoint(路由端点)

到路由端点的GET/routes返回已映射路由的列表:

GET /routes

{

/stores/**: “http://localhost:8081”

}

可以通过将?format=details查询字符串添加到来请求其他路线详细信息/routes。这样做会产生以下输出:

GET /routes/details

{

“/stores/**”: {

“id”: “stores”,

“fullPath”: “/stores/**”,

“location”: “http://localhost:8081”,

“path”: “/**”,

“prefix”: “/stores”,

“retryable”: false,

“customSensitiveHeaders”: false,

“prefixStripped”: true

}

}

/routes的POST方法将会强制刷新路由信息。

可以通过endpoints.routes.enabled=false来禁用这个端点。

注意:路由会自动根据服务目录的改动来更新,但是POST请求/routes是一种立即强制更新的方法。

Filters Endpoint(过滤器端点)

/filters 的GET请求将会返回过滤器类型列表。

压缩模式和本地转发


当迁移一个老的应用或者API时,需要慢慢把它的访问端点替换成新的实现。Zuul会是一个很有用的代理,因为你可以使用它处理所有来自客户端老的端点的流量并且重定向一些请求到新的实现上。如下:

zuul:

routes:

first:

path: /first/**

url: https://first.example.com

second:

path: /second/**

url: forward:/second

third:

path: /third/**

url: forward:/3rd

legacy:

path: /**

url: https://legacy.example.com

其中,forward:开头的url将会转发到本地。

通过Zuul上传文件


如果你使用@EnableZuulProxy,那么可以通过代理路径来上传一些小文件,对于大文件有一个可以绕过Spring DispatcherServlet的路径“/zuul/*”,换句话说,如果zuul.routes.customers=/customers/*,那么可以

通过发送POST请求到/zuul/customers/*。servlet路径是通过zuul.servletPath外部化的。如果代理路由使用Ribbon,尤其大文件需要提高超时时间。如下

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000

ribbon:

ConnectTimeout: 3000

ReadTimeout: 60000

请注意,要使流传输处理大文件,您需要在请求中使用分块编码(某些浏览器默认不这样做),如以下示例所示:

$ curl -v -H “Transfer-Encoding: chunked” \

-F “file=@mylarge.iso” localhost:9999/zuul/simple/file

查询字段编码


当处理请求时,查询字段将会被解码,这样就可以在Zuul过滤器中修改他们。然后在过滤器中再重新编码后发送请求给后端。如果使用Javascrip的encodeURIComponent()方法,那么结果可能会和原始输入不同。这在大多数情况下不会有问题,但是一些web服务器对于复杂查询字段的编码要求还是很挑剔的。

为了强制查询字符串的原始编码,可以向ZuulProperties传递一个特殊的标志,以便将查询字符串作为HttpServletRequest::getQueryString方法使用。

zuul:

forceOriginalQueryStringEncoding: true

注意:这个特殊的标志只对SimpleHostRoutingFilter有效,另外可以通过RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)来覆盖查询字符串。

请求URI编码


在处理传入请求时,在将请求URI与路由匹配之前,先对请求URI进行解码。然后在路由过滤器中重建后端请求时,将对请求URI进行重新编码。如果您的URI包含编码的“ /”字符,则可能导致某些意外行为。

要使用原始请求URI,可以向’ZuulProperties’传递一个特殊标志,以便该URI与该HttpServletRequest::getRequestURI方法一样被使用,如以下示例所示:

zuul:

decodeUrl: false

如果使用requestURIRequestContext属性覆盖请求URI,并且此标志设置为false,则将不对在请求上下文中设置的URL进行编码。确保URL已被编码是您的责任。

禁用Zuul过滤器


在代理和服务器模式,spring cloud Zuul都会默认注册一些ZuulFilter。可以通过zuul…disable=true来禁用指定的过滤器。

按照惯例,包名中filters的后面就是filterType。例如,如果要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter,可以设置zuul.SendResponseFilter.post.disable=true。

为路由提供Hystrix降级服务


当Zuul的路由回路出现问题时,可以通过一个FallbackProvider类型的bean来提供降级服务。在这个bean中,需要指定路由的ID,并且提供一个ClientHttpResponse。下面的例子提供了一个相对简单的FallbackProvider的实现。

class MyFallbackProvider implements FallbackProvider {

@Override

public String getRoute() {

return “customers”;

}

@Override

public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {

if (cause instanceof HystrixTimeoutException) {

return response(HttpStatus.GATEWAY_TIMEOUT);

} else {

return response(HttpStatus.INTERNAL_SERVER_ERROR);

}

}

private ClientHttpResponse response(final 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(“fallback”.getBytes());

}

@Override

public HttpHeaders getHeaders() {

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.APPLICATION_JSON);

return headers;

}

};

}

}

下面的例子是对应上面例子的路由配置:

zuul:

routes:

customers: /customers/**

如果要对所有路由提供一个默认降级服务,可以创建一个FallbackProvider类型的bean,然后在getRoute方法中返回“*”或者null。如下:

class MyFallbackProvider implements FallbackProvider {

@Override

public String getRoute() {

return “*”;

}

@Override

public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {

return new ClientHttpResponse() {

@Override

public HttpStatus getStatusCode() throws IOException {

return HttpStatus.OK;

}

@Override

public int getRawStatusCode() throws IOException {

return 200;

}

@Override

public String getStatusText() throws IOException {

return “OK”;

}

@Override

public void close() {

}

@Override

public InputStream getBody() throws IOException {

return new ByteArrayInputStream(“fallback”.getBytes());

}

@Override

public HttpHeaders getHeaders() {

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.APPLICATION_JSON);

return headers;

}

};

}

}

Zuul的超时时间


如果想要为通过Zuul代理的请求设置socket超时时间和读取超时时间,你有两个选项,基于配置:

1)如果Zuul使用服务发现,则配置ribbon.ReadTimeout和ribbon.SocketTimeout;

2)如果路由是通过URL指定的,那么需要配置zuul.host.connect-timeout-millis和zuul.host.socket-timeout-millis

重写头部Location字段


如果Zuul在一个web应用前面,那么你需要重写Location头部当你的web应用通过HTTP状态码3XX重定向。否则,浏览器会重定向到web应用的URL而不是Zuul的URL。

可以通过配置一个LocationRewriteFilter类型的Zuul过滤器来重写Location头部到Zuul的URL。它还恢复了删除的全局前缀和特定于路由的前缀。如下

@Configuration

@EnableZuulProxy

public class ZuulConfig {

@Bean

public LocationRewriteFilter locationRewriteFilter() {

return new LocationRewriteFilter();

}

}

注意:要非常小心使用这个过滤器,因为它会作用于所有响应码为3XX的Location头部,这可能在某些场合不适合。比如要重定向到一个外部地址。

跨域请求


默认情况下,Zuul将所有跨源请求(CORS)路由到服务。如果您想让Zuul处理这些请求,可以通过提供自定义WebMvcConfigurerbean来完成

@Bean

public WebMvcConfigurer corsConfigurer() {

return new WebMvcConfigurer() {

public void addCorsMappings(CorsRegistry registry) {

registry.addMapping(“/path-1/**”)

.allowedOrigins(“https://allowed-origin.com”)

.allowedMethods(“GET”, “POST”);

}

};

}

在上面的示例中,我们允许GETPOST方法从allowed-origin.com将跨域请求发送到以开头的端点path-1。您可以使用/**映射将CORS配置应用于特定的路径模式,也可以将其整体应用于整个应用程序。您可以自定义属性:allowedOriginsallowedMethodsallowedHeadersexposedHeadersallowCredentialsmaxAge通过此配置。

度量指标


Zuul在Actuator metrics端点下提供metrics,当路由请求出现失败时。可以通过/actuator/metrics端点查看。metrics名称的格式为ZUUL::EXCEPTION:errorCause:statusCode。

限流


https://www.baeldung.com/spring-cloud-zuul-rate-limit

com.marcosbarbero.cloud

spring-cloud-zuul-ratelimit

2.2.0.RELEASE

zuul:

routes:

serviceSimple:

path: /greeting/simple

url: forward:/

serviceAdvanced:

path: /greeting/advanced

url: forward:/

ratelimit:

enabled: true

repository: JPA

policy-list:

serviceSimple:

  • limit: 5

refresh-interval: 60

type:

  • origin

serviceAdvanced:

  • limit: 1

refresh-interval: 2

type:

  • origin

strip-prefix: true

为_serviceSimple_ 端点增加了每60秒5个请求的速率限制。相反, _serviceAdvanced_的速率限制为每2秒1个请求

The type configuration specifies which rate limit approach we want to follow. Here are the possible values:

  • origin – rate limit based on the user origin request

  • url – rate limit based on the request path of the downstream service

  • user – rate limit based on the authenticated username or ‘anonymous’

  • No value – acts as a global configuration per service. To use this approach just don’t set param ‘type’

在ZuulFilter中修改请求和相应


在ZuulFilter中修改请求Resquest

public class CustomZuulFilter extends ZuulFilter {

@Override

public Object run() {

RequestContext ctx = RequestContext.getCurrentContext();

ctx.addZuulRequestHeader(“Test”, “TestSample”);

return null;

}

@Override

public boolean shouldFilter() {

return true;

}

// …

}

下游服务取值

@RestController

public class FooController {

@GetMapping(“/foos/{id}”)

public Foo findById(

@PathVariable long id, HttpServletRequest req, HttpServletResponse res) {

if (req.getHeader(“Test”) != null) {

res.addHeader(“Test”, req.getHeader(“Test”));

}

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

读者福利

秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer

更多笔记分享

秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
") != null) {

res.addHeader(“Test”, req.getHeader(“Test”));

}

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-kjmzbCGE-1712985988064)]

[外链图片转存中…(img-CXRmyXss-1712985988065)]

[外链图片转存中…(img-DizGtbC3-1712985988065)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

读者福利

[外链图片转存中…(img-1MbZ5Kkj-1712985988065)]

更多笔记分享

[外链图片转存中…(img-XPqZ6Ogh-1712985988066)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值