Zuul

一、认识Zuul

1.什么是Zuul

不同的微服务一般会有不同的网络地址,而外部APP可能需要调用多个服务的接口才能完成一个业务需求。

让客户端和各个微服务通信肯定不行。

微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。这样的好处是易于监控、认证,也减少了客户端与各个微服务之间的交互次数

2. Zuul的作用

Zuul可以和Eureka、Ribbon、Hystrix等组件配合使用。核心是一系列过滤器,这些过滤器可以完成以下功能:

        ----身份认证与安全

        ----审查与监控

        ----动态路由

        ----压力测试

        ----负载分配

        ----静态相应处理

        ----多区域弹性

3.编写Zuul微服务网关(添加zuul依赖,并将Zuul注册到Eureka)

3.1 添加依赖

<dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    	<version>2.1.2.RELEASE</version>
    </dependency>
     <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    	<version>2.1.1.RELEASE</version>
    </dependency>

注:其中,spring-cloud-starter-netflix-zuul包含了spring-boot-starter-actoator

3.2 配置启动类

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
	public static void main(String[] args) {
		SpringApplication.run(ZuulApplication.class, args);
	}
}

3.3 修改配置文件

server.port=8040
spring.application.name=gateway-zuul
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

四、管理端点

@EnableZuulProxySpring Boot Actuator配合使用时,Zuul会暴露两个端点:/routes和/filters

4.1 route端点

4.1.1 使用GET方法访问该端点,即可返回Zuul当前映射的路由列表

4.1.2 使用POST方法访问该端点就会强制刷新Zuul当前映射的路由列表。尽管路由会自动刷新,Spring Cloud依然提供了强制立刻刷新的方式

4.1.3 SpringCloudEdgware/routes端点进行了进一步的增强,我们可以使用/routes?format=details查看更多与路由相关的详细设置

4.2 filters端点:访问该端点即可返回Zuul中当前所有过滤器的详情,并按照类型分类

4.2.1 一共有四种类型过滤器:error、post、pre、route

五、路由配置详解

5.1 自定义指定微服务的访问路径

zuul.routes.<serviceId>=/user/**	#指定路径

5.2 忽略指定微服务

zuul.ignored-services=provider-user,consumer-user

5.3 忽略所有微服务,只路由指定微服务

zuul.ignored-services='*'
zuul.routes.<serviceId>=/user/**	#指定路径

5.4 同时指定微服务的serviceId和对应路径

zuul.routes.<路由名称>.service-id=user
zuul.routes.<路由名称>.path=/user/**

5.5 同时指定path和URL

zuul.routes.<路由名称>.url=http://localhost:8000/
zuul.routes.<路由名称>.path=/user/**

注:使用这种方式配置的路由不会作为HystrixCommand执行,同时也不能使用Ribbon来负载均衡多个URL。5.6可以解决这些问题

5.6 同时指定path和URL,并且不破坏Zuul的Hystrix、ribbon特性

zuul.routes.<路由名称>.path=/user/**
zuul.routes.<路由名称>.service-id=user
ribbon.eureka.enabled=false
user.ribbon.listOfServers=localhost:8000,localhost:8001

5.7 使用正则表达式指定Zuul的路由匹配规则

@Bean
	public PatternServiceRouteMapper serviceRouteMapper(){
		return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)", "${version}/${name}");
	}

5.8 路由前缀

zuul.prefix=/api
zuul.strip-prefix=false
zuul.routes.<路由名称>=/user/**
zuul.routes.<路由名称>.path=/user/**
zuul.routes.<路由名称>.strip-prefix=false

5.9 更细粒度忽略某些路径

zuul.ignoredPatterns=/**/admin/**
zuul.routes.<路由名称>=/user/**

5.10 本地转发

zuul.routes.route-name.path=/path-a/**
zuul.routes.route-name.url=forward:/path-b

六、Zuul的安全与Header

6.1 敏感Header的设置

6.1.1 一般来说,同一个系统中的服务共享Header,但是对于一些敏感的Header,可以这样设置

zuul.routes.<路由名称>.path=/user/**
zuul.routes.<路由名称>.sensitive-headers=Cookie,Set-Cookie,Authorization
zuul.routes.<路由名称>.url=https://downstream

6.1.2 设置全局指定敏感Header

zuul.sensitive-headers=Cookie,Set-Cookie,Authorization

6.2 忽略Header

zuul.ignored-headers=Header1,Header2

七、使用Zuul上传文件

7.1 对于1M以内的文件上传,无须任何处理

7.2 对于10M以上的需要为上传路径添加/zuul前缀。也可以使用zuul.servlet-path自定义前缀

7.3 上传实例

7.3.1 添加依赖

<dependency>
     	<groupId>org.springframework.boot</groupId>
     	<artifactId>spring-boot-starter-web</artifactId>
     	<version>2.1.3.RELEASE</version>
     </dependency>
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    	<version>2.1.1.RELEASE</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-actuator</artifactId>
    	<version>2.1.3.RELEASE</version>
    </dependency>

7.3.2 启动类上添加@SpringBootApplication,@EnableEurekaClient

7.3.3 编写Controller

@Controller
public class FileUploadController {

	@RequestMapping(value="/upload",method=RequestMethod.POST)
	public @ResponseBody String handleFileUpload(@RequestParam(value="file",required=true) MultipartFile file) throws IOException{
		byte[] bytes = file.getBytes();
		File fileToSave = new File(file.getOriginalFilename());
		FileCopyUtils.copy(bytes, fileToSave);
		return fileToSave.getAbsolutePath();
	}
}

7.3.4 配置文件

server.port=8050
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.prefer-ip-address=true
spring.application.name=file-upload
spring.http.multipart.max-file-size=2000Mb		#默认1M
spring.http.multipart.max-request-size=2500Mb	#默认10M

7.3.5 测试文件上传

curl -F "file=@文件全名" localhost:8050/upload

八、Zuul的过滤器

8.1 过滤器类型和请求生命周期

标准过滤器类型:PRE、ROUTE、POST、ERROR

定制过滤器:STATIC

8.2 内置过滤器详解

RequestContext其用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse等信息。RequestContext扩展了ConcurrentHashMap,所以理论上任何数据都可以存储在RequestContext中。

8.3 @EnableZuulServer所启用的过滤器

8.3.1 pre类型过滤器

8.3.1.1 ServletDetectionFilter:该过滤器用于检查请求是否通过Spring Dispatcher。检查后,通过FilterConstants.IS_DISPATCHER_SERVLET_KEY设置布尔值。

8.3.1.2 FormBodyWrapperFilter解析表单数据,并为请求重新编码

8.3.1.3 DebugFilter调试用的过滤器。当设置zuul.include-debug-header=true或者zuul.debug.request=true,并在请求时加上debug=true的参数,就会开启该过滤器。该过滤器会把RequestContext.setDebugRouting()以及RequestContext.setDebugRequest()设为true

8.3.2 route类型过滤器

8.3.2.1 SendForwardFilter:该过滤器使用Servlet RequestDispatcher转发请求,转发位置存储在RequestContext的属性FilterContants.FORWARD_TO_KEY中。

zuul.routes.<路由名称>.path=/path-a/**
zuul.routes.<路由名称>.url=forward:/path-b

8.3.3 post类型过滤器

8.3.3.1 SendResponseFilter:将代理请求的响应写入当前响应

8.3.4 error类型过滤器

8.3.4.1 SendErrorFilter:若RequestContext.getThrowable()不为null,则默认转发到/error,也可设置error.path属性来修改默认的转发路径

8.4 @EnableZuulProxy所启用的过滤器

如果使用@EnableZuulProxy,那么除上述过滤器外,Spring Cloud还会安装以下过滤器:

8.4.1 pre类型过滤器

PreDecorationFilter:该过滤器根据提供的RouteLocator确定路由到的地址,以及怎么去路由。同时,该过滤器还为下游请求设置各种代理相关的header

8.4.2 route类型过滤器

8.4.2.1 RibbonRoutingFilter该过滤器使用Ribbon、Hystrix和可插拔的HTTP客户端发送请求serviceIdRequestContext的属性FilterContants.SERVICE_ID_KEY中。该过滤器可使用如下这些不同的HTTP客户端

        * Apache HttpClient:默认的HTTP客户端

        * Squareup OkHttpClient v3:若需使用该客户端,需保证com.squareup.okhttp3的依赖在classpath中,并设置ribbon.okhttp.enabled=true

        * Netflix Ribbon HTTP Client:设置ribbon.restclient.enabled=true即可启用该HTTP客户端。该客户端有一定限制,例如不支持PATCH方法。另外,它还有内置的重试机制

8.4.2.2 SimpleHostRoutingFilter:该过滤器通过Apache HttpClient向指定的URL发送请求。URL在RequestContext.getRouteHost()

九、编写Zuul过滤器

public class PreRequestLogFilter extends ZuulFilter {
	private static final Logger LOGGER = LoggerFactory.getLogger(PreRequestLogFilter.class);
	
	/**
	 * 返回过滤器的类型,一共有四种:pre,route,post,error
	 */
	@Override
	public String filterType() {
		//pre类型的过滤器
		return FilterConstants.PRE_TYPE;
	}
	
	/**
	 * 返回一个int来指定过滤器的执行顺序
	 */
	@Override
	public int filterOrder() {
		//在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter之前执行
		return FilterConstants.PRE_DECORATION_FILTER_ORDER -1;
	}
	
	/**
	 * 返回一个boolean判断该过滤器是否要执行
	 */
	@Override
	public boolean shouldFilter() {
		return true;
	}
	
	/**
	 * 过滤器的具体逻辑
	 */
	@Override
	public Object run() throws ZuulException {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		PreRequestLogFilter.LOGGER.info(String.format("send %s request to %s", request.getMethod(),request.getRequestURL().toString()));
		return null;
	}
}

十、禁用过滤器

zuul.<SimpleClassName>.<filterType>.disable=true

十一、Zuul的容错和回退

想要为Zuul添加回退,需要实现ZuulFallbackProvider接口。在实现类中,指定为哪个微服务提供回退,并提供一个ClientHttpResponse作为回退响应。

11.1 编写zuul的回退类

@Component
public class MyFallbackProvider implements FallbackProvider {

	@Override
	public String getRoute() {
		// 表明为哪个微服务提供回退,*表示为所以微服务提供回退
		return "*";
	}
	
	@Override
	public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
		if(cause instanceof HystrixTimeoutException){
			return response(org.springframework.http.HttpStatus.GATEWAY_TIMEOUT);
		}else{
			return this.fallbackResponse(null, null);
		}
	}

	private ClientHttpResponse response(final org.springframework.http.HttpStatus status) {
		return new ClientHttpResponse() {
			
			@Override
			public HttpHeaders getHeaders() {
				HttpHeaders headers = new HttpHeaders();
				MediaType mType = new MediaType("application","json",Charset.forName("UTF-8"));
				headers.setContentType(mType);
				return headers;
			}
			
			@Override
			public InputStream getBody() throws IOException {
				// TODO Auto-generated method stub
				return new ByteArrayInputStream("服务不可用,请稍后再试".getBytes());
			}
			
			@Override
			public String getStatusText() throws IOException {
				// TODO Auto-generated method stub
				return status.getReasonPhrase();
			}
			
			@Override
			public org.springframework.http.HttpStatus getStatusCode()
					throws IOException {
				// TODO Auto-generated method stub
				return status;
			}
			
			@Override
			public int getRawStatusCode() throws IOException {
				// TODO Auto-generated method stub
				return status.value();
			}
			
			@Override
			public void close() {
				// TODO Auto-generated method stub
				
			}
		};
	}
}

十二、饥饿加载

因为zuul整合了ribbon实现负载均衡,而ribbon默认是懒加载的,可能会导致首次请求较慢。可以使用以下配置进行饥饿加载

zuul.ribbon.eager-load.enabled=true

十三、QueryString编码

当处理请求时,query param会被解码,因此,可在Zuul过滤器中进行一些适当的修改。

这些参数在route过滤器中构建请求时,将被重新编码。如果query param使用js的encodeURIComponent()方法进行编码,那么重新编码后的结果可能与原始值不同(一般情况下不会有问题,但是某些web服务器可能会对复杂的query string进行编码)

如果强制让query stringHttpServletRequest.getQueryString()保持一致,可使用如下配置:

zuul.forceOriginalQueryStringEncoding=true

十四、Hystrix隔离策略与线程池

14.1 在Hystrix那一节说过,Hystrix有两种隔离策略:THREAD、SEMAPHORE

默认情况下,Zuul的Hystrix隔离策略是SEMAPHORE,可使用如下方法将隔离策略变成THREAD

zuul.ribbon-isolation-strategy=thread

14.2

14.2.1 当Hystrix的线程隔离级别是THREAD时,将作用于所有路由HystrixThreadPoolKey默认是RibbonCommand。这意味着所有的路由HystrixCommand都会在相同的Hystrix线程池中执行。

14.2.2 可使用如下配置,让每个路由使用独立的线程池:

zuul.threadPool.useSeparateThreadPools=true

14.2.3 如果想为HystrixThreadPoolKey添加前缀,使用如下配置:

zuul.threadPool.useSeparateThreadPools=true
zuul.threadPool.threadPoolKeyPrefix=prefix-

十五、Zuul的高可用

Zuul的高可用很关键,因为外部请求到后端微服务的流量都会经过Zuul。所以需要避免单点故障

15.1 将Zuul客户端也注册到了Eureka Server上

只需将Zuul节点注册到Eureka Server上

15.2 Zuul客户端未注册到Eureka Server上

借助额外的负载均衡器来实现Zuul的高可用,比如Nginx。Zuul客户端将请求发送到负载均衡器,负载均衡器将请求转发到其代理的其中一个Zuul节点

十六、使用Zuul聚合微服务

场景:外部请求需要查询Zuul后端的多个微服务,我们想让手机APP只发送一个请求给Zuul,由Zuul请求其他多个微服务,并将数据组织好返回APP。别让手机APP自己去访问多个微服务。

使用RxJava

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鹏哥哥啊Aaaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值