ASP.NET服务器端执行耗时操作的工作记录

公司之前有这样一个业务需求:

  一名同事做出文件a0和b0,然后将a0加密为a1、b0加密为b1;再将文件a0、a1、b0和b1上传至服务器M;同时要将服务器N上的数据表添加一条记录,该记录的ID就是前面四个文件的文件名(扩展名各不相同)。另外说一句,公司网站也在服务器N上,一会儿会用到这点。


公司原来的实现模式是这样的:

  程序员小x用VB写了一个软件,能将文本文件生成文件a0,并将文件a0加密为a1,还生成一个access数据库文件,通过软件界面收集到的信息,生成一条记录为稍后向服务器N添加记录提供字段值;

  程序员小y用C++的MFC写了一个软件,能将做好的b0文件加密为b1(加密过程调用公司同事小z的一个dll库),再根据access数据库文件的信息将a0、a1、b0、b1上传至服务器M,最后将服务器N上的数据表添加记录。


  公司现在想要用B/S模式实现文件上传的功能,而且目前程序员小x和小y都已经离职,只剩C++程序员小z。我是C#程序员,不太会C++和VB,对现有的状况和公司的要求仔细考虑了一下,做出如下方案:

  因为程序员小x的VB软件涉及到a0文件的生成和加密,以及该记录各字段信息的收集功能,公司里也没有VB程序员,改动程序比较困难,重新用C++或C#写一个软件工作量又大(老板要求尽快完成,且不要弄得太复杂、太麻烦),所以小x的VB软件不动;再看程序员小y的MFC程序软件,它实现了b0文件的加密(调用小z的库),实现4个文件的上传和向服务器N中的数据库写记录(根据access文件)的功能,所以小y的软件不要了;

  而让小z做一个简单的MFC程序软件,实现如下几点功能:1、让用户选择一个access文件(用户用小x软件生成的access文件),程序读取数据表信息;2、根据数据表中各记录的ID值把指定文件夹中b0文件加密成为b1文件;3、将数据表及表中各记录ID值对应的a0、a1、b0、b1文件添加到一个压缩包文件。最后让用户访问我写好的一个页面,有一个上传flash插件和一个保存按钮,具体功能实现如下。

  上传flash插件选择压缩包文件,上传flash插件指向后台ashx文件,将这个压缩包文件保存至服务器N站点(公司网站)下的一个文件夹中;这个上传文件的功能做了前后台的验证,前台用js做文件类型、长度等信息验证,而后台也对HttpPostFiles对象里的相关属性验证;服务器保存完毕后,用户在页面点击保存按钮,按钮的点击事件执行如下操作:1、进行操作合法性验证;2、解压缩文件包;3、找到access数据库文件,并读取其中数据信息;4、循环遍历每一行,执行FTP上传至服务器M,每上传成功一条记录对应的所有文件,便将这条记录添加到服务器N中的数据库中。
  操作合法性验证:当前登录用户是否为预期的用户,指定的压缩包文件是否存在。

  解压缩文件包:用了一个解压缩库,将压缩包内的文件解压缩到指定的文件夹中。

  读取access数据库文件:若不存在则返回,否则将数据表中数据记录读取到DataSet中。

  循环遍历数据表,并执行FTP上传:循环DataTable,每次获取DataRow后,将记录ID对应的4个文件上传至服务器M上,确保所有文件都上传成功后,向数据库(公司网站服务器就是服务器N)写入记录,在上传之前判断该记录ID是否已经存在在服务器数据库中,若存在则跳过,FTP上传也用了一个库。额外做了一个数据表,记录上传状态,同时在FTP上传功能代码段中加入了写日志文件功能,以便确定上传失败的原因。

  最后对实现这个功能过程中遇到的一些问题及解决思路:
  一、用小文件测试上传时,上传功能正常,可是换了真正的大文件时,上传功能出错。
    一开始没意识到是asp.net配置的问题,因为是上传flash插件无法将请求提交到ashx文件,而且用VS调试运行也不行,用IIS运行则报500或404错误。还以为是项目或flash插件出了问题,可是调试都不进断点,所以找不到原因,后来突然换回之前的小文件,功能正常。于是乎知道了答案:asp.net有个默认的请求长度限制,这个要在web.config中进行设置。<httpRuntime maxRequestLength="820000" executionTimeout="14400"/>maxRequestLength属性就是,其默认值为4096(4MB),注意除非做上传功能,否则这个值不要设置过大,因为这个值的意义在于防止拒绝服务攻击;还有executionTimeout属性,这个意思是在被 ASP.NET 自动关闭前,允许执行请求的最大秒数,默认值为110秒(.NET1.0和.NET1.1默认值为90秒);还有一个是Session超时<sessionState timeout="240"/>默认值为20分钟。
  二、上传一条记录(也就是4个文件)时没有问题,可上传10条记录时过一段时间就无响应了,经过多次观察发现这个时间是20分钟左右(根据日志文件最后一次写入时间判断)。
    一开始以为是网络连接断了,可是也应该在我的日志文件中抛出异常,可是没有。经常是某文件开始上传,之后就没下文了,就不再写日志文件了。经过多次试验,觉得可以认为是程序运行中断,但是是什么原因呢?后来知道IIS中有一项设置,IIS-网站站点所用的应用程序池-右键高级设置-闲置超时属性,系统默认值为20分钟,这下可以确定了,就是20分钟没人请求网站,IIS认为闲置超时,所以将进程停止,导致程序运行中断。因为本地测试是在我电脑上的IIS,所以只有我一个人访问,而我主要测试上传,所以上传后就一直在等待,也没有访问其他页面。而真正的网站可能会被很多人访问,所以这个情况不大容易出现。
  三、上传文件后,进行保存操作,服务器端需要进行读数据库、解压文件、FTP上传、写数据库,10个文件时大约用1分钟,因此我想将这些耗时操作放到异步线程中执行,如何实现?
    一开始以为开个新线程就能实现,多线程就代表异步执行吗?百度也没有查到new Thread(某个方法),然后.Start(),是不是异步执行,网上只说Invoke是同步执行,而BeginInvoke是异步执行。后来终于查到用委托的BeginInvoke就是异步执行,同时博问中有个回答者给出了委托的BeginInvoke是否新开线程的测试代码,经过测试委托的方法是在新线程中执行的,这样就坚定了我的信心。而我也没有自己写委托,网上介绍.NET3.5新增了系统委托Action和Func(可能还有其他),刚好我的站点用.NET4.0,所以就用它,同时还用到匿名委托,这样参数和返回值都不需要有,于是果断用Action(不带任何泛型,带一个泛型则说明有一个参数,最多可能有16个,而Func至少有一个泛型,最后一个泛型为返回值,必须有返回值,其余泛型为参数,可以没有参数,最多可能有16个),并在Action对象.BeginInvoke()方法后响应输出给客户端提示信息,而异步线程在服务器端自行运行。

  四、测试本地项目打开页面时,javascript控制台显示一直有脚本在不断执行请求。(2014-3-11补充)

    一开始以为复制粘贴已有代码时,把不相关的JS脚本代码一并贴过来了,检查并没有发现这样的脚本。这是怎么回事呢?难道电脑中病毒了,系统或浏览器被劫持,被恶意注入JS自动执行脚本了吗?换了几个浏览器都有同样的现象发生,用本机架设的IIS服务器访问IP地址页面和用VS访问localhost页面,仍然有这个现象发生。回头再找开发者工具中的HTML功能,发现里面确实生成了一段我没写过的JS脚本代码,将其动态删除后,不断执行请求的JS脚本停止运行了。而且发送请求的地址是以localhost地址发送的,并且端口号也跟VS调试页面所用端口不一样。后来在开发者工具的网络界面中发现这个请求中含有一个关键词“Browser Link”,于是乎用这个关键词百度了一下,居然有结果,而且就是我要寻找的原因。原因是VS2013(我之前一直使用VS2010,最近在升级使用VS2013版本的)为调试Web项目新增一个功能,多浏览器动态实时刷新功能(我个人理解的意思,如果大家想详细了解,请百度之),这个是默认启用的,也可以关闭这个功能,有了这个功能,在VS调试页面时可以同时开多个浏览器调试同一个页面,而且修改这个页面上前端代码(也可能包含服务器端代码,没具体使用这个功能)的同时,浏览器实时显示新修改好的效果。


  代码就不贴了,自己做个总结,以后遇到类似问题可供参考。

原文地址:https://www.cnblogs.com/shouhuqingtian/p/3584465.html