1)nginx
client_max_body_size 10M; 设置文件最大上传大小
keepalive_timeout 1800; 设置最大响应时间
sendfile on; 是否启用高效上传模式
2)springboot
spring:
http:
multipart:
max-file-size: 500Mb
max-request-size: 500Mb
或者
spring:
servlet:
multipart:
# 是否启用分段上传支持,默认:true
enabled: true # 启用上传处理,默认是true
# 文件写入磁盘的阈值,当上传文件达到 1M 事开始写入磁盘。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。默认:0
file-size-threshold: 1MB # 当上传文件达到1MB的时候进行磁盘写入
# 最大单个文件大小。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。默认:1MB
max-file-size: 500MB
# 最大请求大小。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。默认:10MB
max-request-size: 500MB
location: / # 上传文件的临时位置
Spring Boot 1.4.x 版之前:
multipart:
enabled: true
max-file-size: 100MB
Spring Boot 1.5.x - 2.x 版之间:
spring:
http:
multipart:
enabled: true
max-file-size: 100MB
Spring Boot 2.x 版之后
spring:
servlet:
multipart:
enabled: true
max-file-size: 20MB
3)hystrix 超时
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 50000
zuul:
host:
socket-timeout-millis: 12000
connect-timeout-millis: 12000
- ribbon
修改熔断器的连接时间和读取超时时间
在Zuul中默认是使用Ribbon进行看来进行服务调度的,Ribbon会根据实际情况来做请求分发,所以对于文件上传来说,我们需要适当的设置超时时间,这样可以避免Zuul返回错误的消息。
ribbon:
ReadTimeout: 300000 #主要该属性
ConnectTimeout: 300000
MaxAutoRetries: 1 #同一个Server重试的次数(除去首次)
MaxAutoRetriesNextServer: 1 #切换到不同Server的次数
okToRetryOnAllOperations: true #对所有方法进行重试
eureka:
enabled: true
zuul默认的是信号量方式,也就是控制进来的请求总量,可以全局配置,也可以配置单个服务的最大信号量,还有一种方式是线程隔离,每种路由方式会创建单独的线程池,这样的好处是一个服务的影响不会扩散到其他的服务,但是在资源有限,且服务有限的情况下,还是信号量合适,这样我们可以充分利用tomcat创建的线程池,而不用创建过多的hystrix线程池
如果你使用@enablezuulproxy,你可以使用代理路径来上传文件,只要文件很小,它就可以工作。对于大文件,有另一种路径,可以绕过Spring DispatcherServlet(以避免多部分处理)在“/zuul/”中。换句话说,如果你有zuul.route.customers=/customers/*,然后您可以将大文件发布到/zuul/customers/*。servlet路径通过zuul.servletPath外部化。如果代理路由带您经历一个带负载均衡器,那么超大的文件也需要更高的超时设置
使用Zuul也可以进行文件上传处理,但是在文档中明确说明,Zuul在做文件上传的
时候只支持小文件的上传,大文件上传会报错。但是Zuul给出了备选的方案,Zuul
实质是一个Servlet,它会默认集成SpringMVC,当你上传小文件的时候,Zuul会将
请求交给SpringMVC代理处理,但是如果你不想交给SpringMVC,此时你就需要使
用Zuul提供Servlet路径绕过SpringMVC,这个Servlet的默认路径为:/zuul/*。当你
提供的zuul.routes.customers=/customers/**,那么你访问"/zuul/customers/*"会直
接访问对应微服务的接口。
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
@EnableFeignClients
@EnableEurekaClient
@EnableRetry
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
/**
* 解决上传文件重置问题
* @return
*/
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory(){
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
if(connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?>){
( (AbstractHttp11Protocol<?>)connector.getProtocolHandler()).setMaxSwallowSize(-1);
}
});
return factory;
}
}
修改配置,让网关只做简单处理后就直接交给服务的上传接口处理
对于大文件,可以绕过Spring DispatcherServlet(以避免多部分处理)在“/zuul/”
中。换句话说,如果你有zuul.route.customers=/customers/*,然后您可以将大文
件发布到/zuul/customers/*。servlet路径通过zuul.servletPath外部化。
或者在配置文件加 zuul.servlet-path=/
spring:
application:
name: zuul-service
##启用retry机制
cloud:
loadbalancer:
retry:
enabled: true
http:
encoding:
charset: UTF-8
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
@RestController
@RequestMapping("/api")
public class FileUploadController {
@PostMapping("/upload")
public @ResponseBody String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
//1.将文件转换为字节流
byte[] bytes=file.getBytes();
// 根据文件名来创建一个名字,上传到指定路径
File newFile=new File("upload\\"+file.getOriginalFilename());
FileCopyUtils.copy(bytes,newFile);
return newFile.getAbsolutePath();
//使用命令,测试文件上全 curl -F "file=@文件全名" localhost:8050/upload
}
}
通过在ZuulFilter过滤器的run()方法中使用下面方法:
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getParameter("token");
发现无法获取到token,token为空。
解决
通过下面代码就可以获取到multipart/form-data类型的参数。
HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper) request;
token = httpServletRequestWrapper.getRequest().getParameter("token");
解决过程
通在继续DEBUG我发现context.getRequest()获取到的是Servlet30RequestWrapper
直接通过该包装类,无法获取到multipart/form-data类型的参数。
Servlet30RequestWrapper 继承自HttpServletRequestWrapper
HttpServletRequestWrapper中有个一个方法
public HttpServletRequest getRequest(){...}
通过该方法可以获取一个HttpServletRequest,这个HttpServletRequest实际上也就是StandardMultipartHttpServletRequest。
于是便可以通过StandardMultipartHttpServletRequest获取到请求中的表单参数。
#actuator 启用所有的监控端点 “*”号代表启用所有的监控端点,可以单独启用,例如,health,info,metrics
# spring boot 升为 2.0 后,为了安全,默认 Actuator 只暴露了2个端点,heath 和 info
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
@EnableCircuitBreaker //开启断路器
总结
目前的学习和测试结果来看:
单纯的 Ribbon + Hystrix 搭配使用时,配置是最灵活的,二者没有相互干涉,能够自由定义 commandKey 来实现超时时间的配置
Feign + Hystrix 搭配时,因为 Feign 封装了 Hystrix 所需的 commandKey,咱们不能自定义,因此同一个 FeignClient 下的服务接口不能方便的统一配置,若是有相应的业务需求,或许只能对每一个特殊的接口方法作独立的超时配置(找到新方法的话再回来更新)
Zuul + Hystrix 搭配时,和上述的状况相反,能对不一样服务实例作不一样的超时配置,但不能再细化到服务下的具体接口方法