接上篇《26.Zuul的各种姿势》 Spring Cloud版本为Finchley.SR2版
上一篇我们简单介绍Zuul的一些其它特性,如Head的过滤、有关路由的一些管理服务(Routes、Filters),以及本地转发。本篇我们来探讨一下使用Zuul上传文件,以及有关Zuul的Filter过滤器禁用的相关知识。
本部分官方文档:https://cloud.spring.io/spring-cloud-static/Finchley.SR4/single/spring-cloud.html#_zuul_http_client 注:好像Finchley.SR2的文档已经挂了,最新的是Finchley.SR4的文档。
目录
一、Zuul上传文件的端倪
二、试一试就知道
三、Zuul的大文件上传
四、总结
一、Zuul上传文件的端倪
正常情况下,我们使用普通的响应层(如SpringMVC)框架,是可以上传任意大小的文件的。一般我们对文件大小的控制,例如SpringMVC,是通过其“multipartResolver”这个Bean的设置,指定其“MaxUploadSize”参数的具体数字,来控制上传文件的具体大小的。
我们使用Zuul作为服务网关的情况下,也是可以上传文件的。但是使用Zuul上传文件的时候,根据Spring Cloud官方文档的介绍,Zuul在做文件上传的时候,只支持小文件的上传(1M以内),大文件(10M以上)上传会则报错,这是Zuul对文件上传的默认的设置。
我们下面来编写一个上传文件的实例,来测试一下Zuul对文件上传有何种限制,是不是像上面说的那样,然后对Zuul为什么会有这种限制,其原理是什么进行一个深入探讨。
二、试一试就知道
首先我们创建一个Maven工程,名为“microserver-file-upload”:
然后在POM文件中引入Spring Cloud的父工程和“eureka”的依赖,因为需要Web页面来上传文件,所以也引入了Web的Strater依赖:
注意,这里为了便于版本统一管理,该工程的parent父工程和我们User、Movie工程一样,均依赖于microserver-spring-cloud工程(此父工程统一引入了spring-cloud-dependencies的Finchley.SR2版,以及spring-boot-starter-parent-2.0.6.RELEASE的parent,这个在前面的章节已经讲过)。
然后编写启动类MicroserverFileUploadApplication.java:
这里注意:Finchley.RELEASE版本的SpringCloud不需要@EnableEurekaClient注解就可以启用注册功能。在SpringBoot的自动配置类里启用了注册功能,只要引了eureka-client的依赖就会进行注册。然后咱们编写一个简单的上传文件的服务(代码参考自教学视频):
注:上面是一段已经被各大博客参考烂了的上传代码,这里再厚脸皮的用一用o(╯□╰)o。
这里仅仅从POST请求体中获取Multi类型的多媒体流(这里即文件流),然后获取其比特流,然后将数据流通过Fiile工具类,默认写入当前工程的根目录下(文件名同上传文件)。
然后我们在src/main/resource下创建一个静态页面index.html:
该文件是上传文件的前置页面:
下面我们src/main/resource下新建application.yml主配置文件,添加以下配置:
这里我们首先配置了该应用的端口为8050,然后将该应用注册到了erueka,添加了eureka的serviceUrl;最后指定了该应用的服务名,以及上传文件的最大和最小限制(Spring Boot2.0以上的配置方式)。这里上传文件的大小是以Servlet的multipart设置为主,我们在pom文件上使用Ctrl+鼠标左键,可以进入源码:
这里我们可以看到max-file-size的默认值为1M,而max-request-size的默认值为10M。那么我们在配置文件中设置了最大文件请求大小为500Mb,在使用Zuul的情况下,能上传成功吗?我们接下来试试。
这里我们别忘记在Zuul中为microservice-file-upload工程设置网关的路由(因为我们原来配置了ignored-services: '*',会忽略file服务的网关):
这里我们设置microservice-file-upload服务的网关路由为“/file-upload/**”。
我们分别启动microservice-file-upload工程、之前写好的Zuul服务microserver-getaway-zuul和microserver-discovery-eureka注册中心:
然后在eureka Server主页上看到我们的文件上传服务、Zuul服务已经启动起来:
我们访问“http://localhost:8050/index.html”路径,可以进行文件上传的测试:
首先我们选择一个文件大小有248KB左右的文件,发现可以上传成功,并返回了上传成功的路径:
然后我们换一个252MB大小的文件,发现上传也没有问题:
到工程的根目录下查看,文件也成功上传了:
但是当我们通过zuul的网关去上传文件,路径应为“http://localhost:8040/file-upload/upload”,我们这里把原来html中的服务名更改为这个路径:
然后重新访问上传文件的路径,删除之前上传到工程根目录下的两个文件,重新上传。这时我们会发现上传248KB左右的文件依然成功,但是上传252MB大小的文件报错了:
这里报的什么错呢?很简单,“the request was rejected”就是请求被拒绝了,Zuul也给出了原因,就是“because its size (264656928) exceeds the configured maximum (10485760)”,即请求上传内容的大小(252MB)超出了最大上传大小(10M)。对于我们上传文件的微服务,我们尚且为Servlet设置了max-file-size以及max-request-size,而在我们使用的Zuul上,人家也是需要设置类似max-file-size以及max-request-size的,所以这里我们给Zuul也设置一下max-file-size以及max-request-size(Zuul工程的application.yml):
然后重启Zuul,重新做刚才的操作,虽然文件上传成功了,但是发现没有返回路径,并且控制台还报错了:
我们都已经设置了max-file-size以及max-request-size,为什么还会报错?这是因为Zuul的上传机制问题,我们下面就来仔细探讨一下为什么会报这个错,以及怎么给Zuul进行设置,才能使我们能正常的上传大文件。
三、Zuul的大文件上传
通过上面的报错“microservice-file-upload timed-out and no fallback available.”我们可以了解到,主要原因还是因为请求处理超时,并且没有响应。那么为什么为超时和没有响应呢?
而该超时的异常为com.netflix.hystrix.exception.HystrixRuntimeException,可以注意到异常是hystrix抛出的,说明hystrix对于情求的反馈时间是有要求的,而我们在上传大文件时,会因为上传时间过长,而导致hystrix认为该请求无响应,最终给客户端反馈了超时并没有响应的异常,我们只需在Zuul的配置文件中,提升hystrix的超时时间即可:
我们重启Zuul,删除上传好的文件,再试一遍,发现不再报错:
有同学问了,我在Zuul上还需要再配置max-file-size以及max-request-size,岂不是很麻烦,为什么不能直接使用上传服务的设置呢?这里我们要认真探讨一下:
对于Zuul而言,其实它的本质就是一个Servlet,默认集成了SpringMVC,当我们使用Zuul上传文件时,实际上的上传还是经过了SpringMVC的DispatcherServlet,而使用SpringMVC的DispatcherServlet上传文件,会默认进行“multipart processing”,该处理是用来上传时检查文件大小之类的,我们即使在真正的上传微服务中配置了大小限制,也没有用,因为过不了这第一关,所以这就是为什么我们在Zuul上又设置了max-file-size以及max-request-size才成功的。
如果你不想将上传交给SpringMVC处理,需要真实的调用自己的上传服务处理,此时你就需要使用Zuul提供Servlet路径绕过SpringMVC,这个Servlet的默认路径为:/zuul/*。当你提供的zuul.routes.customers=/customers/**,那么你访问"/zuul/customers/*"会直接访问对应微服务的接口。
这样就不需要在Zuul客户端再设置一次max-file-size以及max-request-size了。(经过测试确实是这样,大家将Html中action改为http://localhost:8040/zuul/file-upload/upload,然后把max-file-size以及max-request-size去掉试一下就知道了)
四、总结
对于小文件(1M以内上传),无须任何处理,即可正常上传。
对于大文件(10M以上)上传,需要为上传路径添加/zuul前缀。也可使用zuul.servlet-path自定义前缀。
假设zuul.routes.microservice-file-upload=/microservice-file-upload/**
如果http://{HOST}:{PORT}/upload是微服务microservice-file-upload的上传路径,则可使用Zuul的/zuul/microservice-file-upload/upload路径上传大文件。
如果Zuul使用了Ribbon做负载均衡,那么对于超大的文件(例如500M),需要提高超时设置,例如:
参考:《51CTO学院Spring Cloud高级视频》