【转】Zuul高级配置(zuul--3)

转自:https://blog.csdn.net/pengjunlee/article/details/87285673

为路由提供HystrixFallback

当Zuul中某一个路由的断路器被断开时,你可以通过创建一个FallbackProvider类型的Bean来为它提供一个Fallback响应。在这个Bean中你需要指定Fallback响应所对应的路由的ID并提供一个ClientHttpResponse作为返回的Fallback响应,下面是一个FallbackProvider实现的实例。

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

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 com.netflix.hystrix.exception.HystrixTimeoutException;

@Component
public class MessageFallbackProvider implements FallbackProvider {

	@Override
	public String getRoute() {
		return "zuul-msg";
	}

	@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("消息服务暂时不可用,请稍后重试!".getBytes());
			}

			@Override
			public HttpHeaders getHeaders() {
				HttpHeaders headers = new HttpHeaders();
				headers.setContentType(MediaType.APPLICATION_JSON);
				return headers;
			}
		};
	}
}

路由的配置信息如下:

zuul:
  routes:
    message-service: /zuul-msg/**

当message-service服务调用失败时,返回结果见下图。

如果你想为所有的路由提供一个默认的Fallback,可以创建一个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;
            }
        };
    }
}

重写Location头信息

如果Zuul面向的是一个Web应用,那么你可能会碰到需要重写Location请求头的情况,你只需要创建一个LocationRewriteFilter类型Bean即可。

import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.post.LocationRewriteFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableZuulProxy
public class ZuulConfig {
	@Bean
	public LocationRewriteFilter locationRewriteFilter() {
		return new LocationRewriteFilter();
	}
}

注意:此过滤器会作用于所有响应状态码为 3XX 的Location头信息,这并不一定适用于所有的场景,比如重定向到外部URL。

启用跨域请求

默认情况下,Zuul会把所有的Cross Origin requests (CORS,跨域请求)路由给相应的服务,如果你想代替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");
			}
		};
	}

在这个例子中,我们允许从 http://allowed-origin.com 向Zuul中以 path-1 开头的终端发送 GET和POST类型的跨域请求。你既可以将 CORS 配置作用于指定的路径,也可以使用 /** 让它作用于整个应用。你可以在其中配置以下属性:allowedOrigins、allowedMethods、allowedHeaders、exposedHeaders、allowCredentials、maxAge。

创建前置过滤器

前置过滤器对RequestContext中的数据进行设置,并提供给下游的过滤器使用,它的最主要的用途就是对路由过滤器所需的信息进行设置。

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

public class QueryParamPreFilter extends ZuulFilter {
	@Override
	public int filterOrder() {
		return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; // run before PreDecoration
	}

	@Override
	public String filterType() {
		return FilterConstants.PRE_TYPE;
	}

	@Override
	public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		return !ctx.containsKey(FilterConstants.FORWARD_TO_KEY) // a filter has already forwarded
				&& !ctx.containsKey(FilterConstants.SERVICE_ID_KEY); // a filter has already determined serviceId
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (request.getParameter("sample") != null) {
			// put the serviceId in `RequestContext`
			ctx.put(FilterConstants.SERVICE_ID_KEY, request.getParameter("foo"));
		}
		return null;
	}
}

创建路由过滤器

路由过滤器在前置过滤器之后运行,并负责构造需要发送给其他服务的请求。它在这的主要工作是:把请求数据传送至客户端所需要的 model 中,并把客户端 model 中的内容转换成响应数据返回。

import java.io.InputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.StreamUtils;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.internal.http.HttpMethod;

public class OkHttpRoutingFilter extends ZuulFilter {
	@Autowired
	private ProxyRequestHelper helper;

	@Override
	public String filterType() {
		return FilterConstants.ROUTE_TYPE;
	}

	@Override
	public int filterOrder() {
		return FilterConstants.SIMPLE_HOST_ROUTING_FILTER_ORDER - 1;
	}

	@Override
	public boolean shouldFilter() {
		return RequestContext.getCurrentContext().getRouteHost() != null
				&& RequestContext.getCurrentContext().sendZuulResponse();
	}

	@Override
	public Object run() {
		OkHttpClient httpClient = new OkHttpClient.Builder().build(); // customize

		RequestContext context = RequestContext.getCurrentContext();
		HttpServletRequest request = context.getRequest();

		String method = request.getMethod();

		String uri = this.helper.buildZuulRequestURI(request);

		Headers.Builder headers = new Headers.Builder();
		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String name = headerNames.nextElement();
			Enumeration<String> values = request.getHeaders(name);

			while (values.hasMoreElements()) {
				String value = values.nextElement();
				headers.add(name, value);
			}
		}
		try {
			InputStream inputStream = request.getInputStream();

			RequestBody requestBody = null;
			if (inputStream != null && HttpMethod.permitsRequestBody(method)) {
				MediaType mediaType = null;
				if (headers.get("Content-Type") != null) {
					mediaType = MediaType.parse(headers.get("Content-Type"));
				}
				requestBody = RequestBody.create(mediaType, StreamUtils.copyToByteArray(inputStream));
			}

			Request.Builder builder = new Request.Builder().headers(headers.build()).url(uri).method(method,
					requestBody);

			Response response = httpClient.newCall(builder.build()).execute();

			LinkedMultiValueMap<String, String> responseHeaders = new LinkedMultiValueMap<>();

			for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
				responseHeaders.put(entry.getKey(), entry.getValue());
			}

			this.helper.setResponse(response.code(), response.body().byteStream(), responseHeaders);
			context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

创建后置过滤器

后置过滤器通常是用来对响应进行处理,例如下面这个过滤器添加了一个UUID作为 X-Sample 的头内容。

import java.util.UUID;

import javax.servlet.http.HttpServletResponse;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

public class AddResponseHeaderFilter extends ZuulFilter {
	@Override
	public String filterType() {
		return FilterConstants.POST_TYPE;
	}

	@Override
	public int filterOrder() {
		return FilterConstants.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;
	}
}

Zuul错误处理

在Zuul过滤器生命周期的任一阶段一旦出现异常,错误过滤器就会执行。SendErrorFilter 只会在RequestContext.getThrowable() 不是 null 的时候运行。它会在请求中设置一些特定的 javax.servlet.error.* 属性并将请求转向Springboot的错误页面。

应用上下文饿加载

Zuul 在内部使用的是Ribbon 对远程url进行调用。默认情况下,Ribbon客户端会在Spring Cloud第一次调用时进行懒加载。如果需要在应用启动时就进行加载,可以做如下配置。

zuul:
  ribbon:
    eager-load:
      enabled: true

请求失败重试

Spring Cloud Netflix 提供了很多种方法用来创建 HTTP 请求,你可以使用负载均衡的RestTemplate、Ribbon 或者 Feign ,但无论你选择了哪一种,都有可能会出现请求失败的情况。当一个请求失败时,你可能会希望它自动地进行重试,要实现这一点的话,你需要把 Spring Retry 添加到你的classpath ,然后,负载均衡的RestTemplate、Ribbon 、 Feign 以及 Zuul 就会对所有失败的请求自动进行重试(前提是你的配置允许它这样做的话)。

补偿策略
默认情况下,重试请求是没有补偿策略的,如果你想要配置一个补偿策略,你需要为指定的服务创建一个 LoadBalancedRetryFactory 类型的Bean,并重写它的 createBackOffPolicy 方法。

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryFactory retryFactory() {
        return new LoadBalancedRetryFactory() {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
                return new ExponentialBackOffPolicy();
            }
        };
    }
}

相关配置

在使用Ribbon进行重试时,你可以使用下面前三个个Ribbon配置项对重试的功能进行控制。

# Max number of retries on the same server (excluding the first try)
sample-client.ribbon.MaxAutoRetries=1

# Max number of next servers to retry (excluding the first server)
sample-client.ribbon.MaxAutoRetriesNextServer=1

# Whether all operations can be retried for this client
sample-client.ribbon.OkToRetryOnAllOperations=true

# Interval to refresh the server list from the source
sample-client.ribbon.ServerListRefreshInterval=2000

# Connect timeout used by Apache HttpClient
sample-client.ribbon.ConnectTimeout=3000

# Read timeout used by Apache HttpClient
sample-client.ribbon.ReadTimeout=3000

# Initial list of servers, can be changed via Archaius dynamic property at runtime
sample-client.ribbon.listOfServers=www.microsoft.com:80,www.yahoo.com:80,www.google.com:80

另外,如果你想当返回的响应的状态码为某些值的时候才进行重试,你可以使用 clientName.ribbon.retryableStatusCodes 把那些需要Ribbon客户端进行重试的状态码列举出来。

clientName:
  ribbon:
    retryableStatusCodes: 404,502

或者创建一个 LoadBalancedRetryPolicy 类型的Bean ,并实现它的retryableStatusCode方法。

禁用重试

# 全部禁用
zuul.retryable=false
# 禁用指定路由
zuul.routes.routename.retryable=false

参考文章
https://github.com/spring-projects/spring-retry

https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#netflix-zuul-starter

原文地址:https://www.cnblogs.com/wjqhuaxia/p/11828747.html

时间: 2024-11-08 03:04:59

【转】Zuul高级配置(zuul--3)的相关文章

【转】Zuul高级配置(zuul--2)

转自:https://blog.csdn.net/pengjunlee/article/details/87162192 自定义路由规则 在<API Gateway 的路由和过滤(Zuul)>一章中,我们并未对Zuul的路由规则进行设置,默认会使用服务的 ID 对服务进行路由,即:在源服务的URI之前增加 /service-id 前缀. # Zuul 默认路由地址 http://<zuul-host>:<zuul-port>/service-id/[service-UR

服务网关zuul之七:zuul中的动态刷新路由配置

Spring cloud使用/refresh端点手动刷新配置 一 介绍很多场景下,需要在运行期间动态调整配置.如果配置发生了修改,微服务也应该实现配置的刷新.下面实现配置的手动刷新.二 新建项目microservice-config-client-refresh三 为项目添加spring-boot-starter-actuator依赖,该依赖包含了/refresh端点,用于配置的刷新. <dependencies> <dependency> <groupId>org.s

证书解析(二):windows 2012 r2证书服务安装与高级配置

本着应用隔离的原则,建议把证书服务部署在一台独立的windows server 2012 r2虚拟机之中.证书服务器可以不用考虑高可用,因证书服务宕掉后,除了不能继续颁发证书和不能访问证书吊销信息,并不影响证书的其他验证. 证书服务的安装 证书服务的安装很简单,运行服务器管理器,添加角色与功能,选择"Active Directory证书服务", 在角色服务中选择"证书颁发机构"与"证书颁发机构WEB注册" (不是证书注册web服务)两项,证书颁发

过滤器的高级配置

1 2 <filter-mapping> 3 <filter-name>FilterDemo3</filter-name> 4 <url-pattern>/*</url-pattern> 5 <dispatcher>REQUEST</dispatcher> 默认情况.如果写了另外一种情况,默认的就没有了 6 <dispatcher>FORWARD</dispatcher> 7 <dispatc

OSPF高级配置

OSPF高级配置 n 路由重分发 路由重分发通常在那些负责从一个自治系统学习路由,然后向另一个自治系统广播的路由器上进行配置.例如:一台路由器既运行OSPF又运行RIP,如果OSPF进程被配置为通告由RIP学到的路由到OSPF自治系统中,那么这种做法就可以叫"重分发RIP". 一个单一的IP路由协议是管理网络中IP路由的首选方案.无论是从配置管理的角度还是容错管理的角度,每台路由器都被期望运行单种路由协议而不是多种路由协议.然而,显示网络的情况是存在多种路由协议的,并且这些网络要互相连

Linux运维实战之Apache服务器的高级配置(虚拟主机、status)

HTTP协议基础知识参考博文:http://sweetpotato.blog.51cto.com/533893/1656137 Apache2.2的基本配置参考博文:http://sweetpotato.blog.51cto.com/533893/1657668 Apache服务的配置分为三段: 1.全局配置(the 'global environment'):对主服务器或虚拟机都有效,且有些功能是服务器自身工作属性: 2.主服务器(main server):主站属性: 3.虚拟主机(Virtu

(转载)HanLP的高级配置

原文地址:http://www.hankcs.com/nlp/segment/full-text-retrieval-solr-integrated-hanlp-chinese-word-segmentation.html 高级配置 目前本插件支持如下基于schema.xml的配置: 配置项名称 功能 默认值 enableIndexMode 设为索引模式 true enableCustomDictionary 是否启用用户词典 true customDictionaryPath 用户词典路径(绝

Cisco ASA 高级配置

Cisco ASA 高级配置 一.防范IP分片攻击 1.Ip分片的原理: 2.Ip分片的安全问题: 3.防范Ip分片. 这三个问题在之前已经详细介绍过了,在此就不多介绍了.详细介绍请查看上一篇文章:IP分片原理及分析. 二.URL过滤 利用ASA防火墙IOS的特性URL过滤可以对访问的网站域名进行控制,从而达到某种管理目的. 实施URL过滤一般分为以下三个步骤: (1) 创建class-map (类映射),识别传输流量. (2) 创建policy-map (策略映射),关联class-map.

存储学习之--StarWind高级配置(添加新的存储和扩展SIZE)

存储学习之--StarWind高级配置(添加新的存储和扩展SIZE)     在配置完StarWind的存储后,可以按照需求来增加或扩展共享存储设备的容量,以下是具体的操作步骤.  一.添加新的存储(New Target) 选择添加新的target 命名新的target 选择存储设备类型 选择虚拟的方式 以镜像方式添加 建立镜像文件和指定Size 设置共享存储属性,支持Cluster 设置Cache大小 完成创建 创建成功 查看新的存储属性 二.扩展存储的尺寸 选择扩展Size 指定扩展后的Si