下载功能的设计随笔

总结一个月前的记录:

我所在组是负责做中间件的(就是那个响应 前端应用层请求,然后处理一些必要的业务处理操作后再以2进制协议向低层发送业务请求的,再将数据传回应用层, So,不要把中间件这3个字想太大了)

新公司刚入2周,前1周无非就是熟悉现在系统的框架,通信协议,规范,以及本行业的一些基础知识等等。这周上峰安排任务下来了,文件打包下载速度太慢,要优化改造一下。好吧,我是新人,也就先别急着想要造新东西了,优化优化原来的老东西,不也挺那个嘛,Jobs不也管这叫作微创新来着,咱也是科技创新工作者,自我阿Q一把先。。。

接下来就是摸透这个功能的流程来龙去脉:
原有的流程:应用层向中间层请求,中间层再向低层请求批量下载,低层将所有文件打包成一个文件再通过Socket传送到中间层,中间层再用一个大数组接受然后封装在一个Bean对象里,再传回Servlet,调用一个统一的WriteFile方法向Response流输出给应用层。
好,为什么会有这么stupid形式呢?是原来中间层框架就是设计做成一个通用入口和出口的问题导致,所有请求都有一个对应的Operation类,实现各种业务操作并将从低层返回的数据都封装成一个对应的Bean,再送往出口方法操作输出。当然不是说这个设计就是不好,我感觉此处可以针对在大数据量处理的时候,变通一下,把批量下载改为在出口方法操作时再调用。还不用伤筋动骨,所以感觉这个框架设计还是Ok的。

针对上面的流程,有如下2个明显的缺陷:
1、压力大、数据量大时中间层可能会出现OOM。
2、响应速度太慢, 因为要等低层打完包了才能传给中间层。

针对上面2个问题,我在内存控制和速度控制两个方法的设计上一阵猛抠,想办法如何尽可能少的用内存,同时能可能快的传输数据,却忽略了异常及数据流的闭环!!!!
后来。。。我第一份设计解决方案通过了,而且上线了,可悲的是被因为该功能引起其他问题被用户投诉了。。。他娘滴。
原因是我在设计时,为了速度解放低层的打包工作,将打包工作移到中间层来做,中间层直接请求原始数据再打包,然后就是想到了Producer与Consumer模式, Socket的输入作为Producer,Response的输出作为Consumer,有块缓冲区进行写/读,剩下的你懂的。可是Response可能因前端取消请求或者他娘滴浏览器挂了但却没有notify通知Producer该醒醒退出了,我滴Socket娃还在那傻傻的wait,这就导致Socket一直没释放回池里,而2货的连接池一直紧紧地等待着这个可怜滴娃被释放回到他这个2货温暖滴家,所以就悲催了,随着越来越多这样恶意的请求,直到有天有其他的请求进来再要给个连接时, 2货的连接池告诉他说:俺滴娃都在外面紧张滴参加着伊拉克战争呢,一个都没回家,没有娃再让你征用了(应该给你个英雄Mother奖)。所以用户投诉就来了,因为他的其他业务因此受到影响了。

在这里我为什么会没考虑到这个问题呢,可能会问为什么不直接将Socket的InputStream往Respone.outputStream写,这是因为原始数据是受到保护,经过加密和编码的,所以在写出之前需要进行解码。

在这里我才发现了连接池这个2货有好几个弊端,为我日后优化之目标

第2个方案: 依然是Producer与Consumer,但是无限量缓冲区,因为Producer不受缓冲区满的限制,可以无限地拉出数据,而Consumer在遇到缓冲区数据不足以解码时会wait,等待Producer的呼唤。
1、增加了文件I/O的消耗,原来的内容缓冲区改变成用一个临时文件作为缓冲区,增加一个内存缓冲区提升RandomFileAccess性能,控制好读写的指针就OK。
2、增加控制下载速度因子: 下载速度快,Socket就慢释放(边写边下); Socket写得快,前端显示的下载的速度就慢点,但后劲十足
3、如果Response关闭,则立即通知Socket停止写,并直接关闭,因为后续数据未读完,不应该保留该Socket,防止Dirty。

总结:用文件作缓冲区,主要是可以控制Socket的写的速度不受缓冲区满的影响导致停顿,可以尽快的释放回连接池。

现在这个方案已经上线解决了问题,下一步就是优化连接池这个2货了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值