Appearance
文件传输网关的流量控制
背景
- 缺乏流量管控机制,过往有几次错误的使用造成了服务器内存、CPU以及其他中间件的告警
- XX年XX月XX日XX部门:使用脚本/代码的方式创建了许多个SFTP连接,每一个SFTP连接又创建了许多个Channel,每一个Channel在apache mina sshd sftp服务端组件中都会交给一个独立的线程维护,用户在完成Channel的文件传输任务后,没有及时关闭Channel,导致服务端创建了众多空闲无用的线程,最终导致了OOM,服务不可用
- XX年XX月XX日XX部门:使用脚本/代码的方式创建了许多个SFTP连接,并重复下载了文件(几十万次重复下载),造成了服务器CPU告警
为何要限流
- 保护文件传输网关服务,避免流量过载导致服务挂掉,影响到正在进行的文件传输
- 基于一定的流量指标实现弹性伸缩
主流限流模式对比
- 目前主流的资料的流控工具有Guava,Sentinel等,这些流控基于QPS或者线程数来进行流量控制的,适合于CPU密集型的短耗时请求
- 基于QPS限流,假想我们限制QPS为1,也即每秒一次请求,如果是大文件传输,一分钟就会建立60个连接,这一下子就会将我们的带宽占满,在这种文件传输场景中,QPS限流是不科学的
- 基于线程来限流,可能更加接近于我们的业务场景,一个文件传输会对应一个线程,看似合理,实际上在apache-mina-sshd-sftp服务端组件中,由于采用了NIO非阻塞网络模型,导致一个线程管控了多个文件传输TCP连接,而每一个文件传输的TCP连接中,每一次的文件传输都会分散成多个数据包多次发送,这种多连接共享单线程以及单次文件传输基于多次请求响应的传输模式,让基于线程来限流的实现会有非常大的困难和挑战。假想一种情况,我们设置并发至为50,在某次文件传输中,并发线程数达到了49,这时候新来了3次文件传输请求,3次请求都要求传输1G文件,第一次请求建立成功后这时候恰好达到了流量阈值(50),传输了100M文件,还剩下900MB+剩余量待传输,这时候线程将该TCP连接挂起(49),接管了第2次连接请求,第2次连接建立后(50),服务中某一个文件恰好传输完成,释放了线程资源(49),然后第3个连接也建立成功(50),这个时候如果第一个请求要求继续传输文件,那么将直接报错(不排队的情况下),这种情况严重浪费了系统资源(之前我们传输了100M,但是后面都失败了),也影响到了用户的体验。
实现方式
- 总流量控制:限制单日传输文件数量和单日传输文件总流量
- 外部SFTP服务侧限制并发文件传输数量:
- 内部SFTP服务侧限制并发文件传输数量:基于mina组件提供的EventListner机制,对文件传输的开始-文件传输进行-文件传输结束的事件进行代码植入