译者:原始文章有点性能测试工具软文的感觉,毕竟文章来源于某工具官方博客。高手请略过。
对于我这种新手,此文还是给我带来一些惊喜,从上到下地,从表象到根源地,定位他们遇到性能问题-响应时间过长-的根本原因,有具体的步骤,思考和判断依据,这就是一个比较不错性能测试分析实例。可以更清楚看到性能测试如何分析定位,可以学习其思路。故分享之。
原文连接:http://apmblog.compuware.com/2013/06/04/how-to-accurately-identify-impact-of-system-issues-on-end-user-response-time/
以下为正文
我们希望检测下我们社区网站的负载能力,所以我们开发团队进行了一个任务,验证生产环境的系统是否能在现有的硬件基础上处理10倍于目前的负载。为了将网站在高负载下可能的崩溃影响降到最低,我们决定在周日下午进行第一轮测试。在运行测试之前,我们给运维团队提了一个醒:他们可能在这次两小时的期间观察到明显的负载变化,从而可能影响到运行在同一环境下的其他应用程序。
在测试过程中,运维团队和开发团队一起监控实时性能数据,当达到一定的负载水平后,我们看到最终用户的响应时间和耗尽资源。在本次测试中非常有趣的是,开发团队和运维团队都看着相同的数据,但是却从不同的角度去审视这些结果。然而,他们都是依赖于最近才公布的Compuware的PureStack技术,这是——整合dynaTrace和PurePath——的第一个解决方案,显示出在高负载下生产环境的硬件是如何影响到关键业务应用程序的性能。
上下文为运维团队和开发团队的数据之间架起桥梁:这张图片显示"横向"事务以及"纵向"层面的热点区域(BridgingtheGapbetweenOpsandAppsDatabyaddingContext:OnepicturethatshowstheHotspotsof"Horizontal"Transactionaswellasthe"Vertical"Stack.)。
在我们的场景中表现不佳的根本原因-一个运行着Web和应用程序服务的主服务器的CPU被耗尽-从而导致达不到我们的负载目标。事实证明,这个问题是跟硬件设备和应用程序都有关系(ThisturnedouttobebothanITprovisioningandanapplicationproblem)。让我解释一下团队的步骤以及他们是如何得出他们的行动项列表,以便改善目前的系统性能,以便在第二轮测试中得到更好的结果。
第1步:监控和识别硬件健康状况
运维团队希望能够看着他们的服务器列表,而所有关键指标(CPU,内存,网络,磁盘等)都能很快地呈现出绿色状态(OperationsTeamslikehavingtheabilitytolookattheirlistofserversandquicklyseethatallcriticalindicators(CPU,Memory,Network,Disk,etc)aregreen)。但是,当我们的负载测试达到了顶峰时,他们看向服务器的状态时,显示出来却是,他们有2台机器正出现了异常:
我们的社区网站核心服务器出现CPU相关的问题,并影响到另一运行在这台服务器上的应用程序。
步骤2:哪些运行中的应用程序被真正影响到了?
单击受影响的程序选项卡,它会显示受影响的机器上所有运行的应用程序,以及目前受影响的应用程序:
增加的负载不仅影响到社区网站,而且也影响到我们支持网站
这次负载测试已经让我们明白:如果我们希望未来的社区网站能够承担更高的负载,那我们可能需要移动支持网站到其他的机器,以避免不必要的影响。
当两个团队孤立检查,运维导向的监测是不会有这个的结论(Whenexaminedindependently,operations-orientedmonitoringwouldnotbethattelling.)。但是,当它被放到具体的上下文中,并涉及到关联的数据(最终用户响应时间,用户体验,...),这对开发团队来说是非常重要的,两个团队将获得更多的灵感和视角。这是一个良好的开端,但仍然还有很多需要了解的。
步骤3:哪些关键事务被真正影响到了?
点击社区应用程序的链接,它会显示实际受硬件状态影响的事务和页面,但仍然存在着两个关键的却又悬而未决的问题:
这些事务,是否是我们成功运行的关键?
这些事务和个人用户受性能问题影响后,有多严重的后果?
自动基线告诉我们,社区网站主要页面的响应时间受到明显的的性能影响。也包括我们的首页,这是我们最有价值的一个页面。
步骤4:可视化受硬件问题影响的事务流
事务流图表是一个令人满意的方式,能使得运维团队和开发团队达到一个基本的共识,并根据其完整的上下文查看关键的数据。它能显示涉及到的应用层,正在运行的物理机器和虚拟机器,以及哪里是热点区域。
运维团队和开发团队有相同的概要图表,告诉他们无论是在"横向"事务和"纵向"层面的热点区域。
我们知道,我们的网页内容非常"丰富"(图像,JavaScript和CSS),高达80%的事务时间花费在浏览器上。看到热点区域这样的表现,现在整体页面加载时间下降到50%,我们马上就知道更多的事务时间已经转移到新的热点区域:服务器端。好消息是,数据库是没有问题的(只用了1%的响应时间),整个性能热点区域似乎转到Web和应用程序服务器,它们都运行在同一台机器上-即那台存在CPU问题的机器。
第5步:精确定位存在问题的机器的健康问题
点击主机健康图表(HostHealthDashboard),它会显示了那个特定的服务器出了什么问题:
运维团队立即看到了CPU的消耗主要来自于一个Java应用服务器。网络,磁盘和页面错误在一些某些特定的时间也都存在不寻常的波动。
第6步:进程健康图表显示应用程序服务器上响应缓慢
我们可以看到,这台机器上的两个主要进程是IIS(Web服务器)和Tomcat(应用程序服务器)。再进一步看看,随着时间的推移,他们具体的表现情况:
我们并不是没有足够的工作线程。传输速率是相当平缓。这就说明,Web服务器正在等待应用服务器的响应。
它表明应用程序服务器的CPU被耗尽。负载测试工具发送的持续请求在排队等待,因为服务器无法及时处理掉这些请求。实际上已处理的事务数量在下降。
第7步:精确定位CPU的大量消耗
现在我们开发团队非常有兴趣搞清楚到底是什么在消耗着CPU,以及是否我们可以在应用程序代码里面修复,或者是否只是我们需要更多的CPU:
热点区域显示应用程序的两层都消耗CPU比较多。让我们进一步深入。
我们有些相当复杂的页面(包含大量Confluencemacros)导致大量的CPU占用。
缺少资源和身份验证问题导致了这些异常。
运维和开发团队现在可以轻松地划分处理硬件和应用程序问题的优先级
所以如前所述,上下文是关键。但这些数据不是轻而易举就能获得的-上下文依赖于智能关联的能力,使所有相关的数据组成一个连贯的故事。当"横向"的事务数据(最终用户响应时间的分析)关联到"纵向"的硬件层面信息,这就很容易让两个团队达到一个共识,并规划影响最小的修复的优先级。
这次实验使我们能够确定以下几个行动项:
当应用程序对其他程序造成负面影响时,部署我们的关键应用程序到其他的机器上。
优化我们的页面生成方式,以便降低CPU使用率。
为虚拟机分配更多的CPU,以便能够处理更多的负载。
版权声明:本文出自 在劫录 的51Testing软件测试博客: http://www.51testing.com/?166582
原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。
性能测试
1. 如何理解TPS?
2. 如何理解线程调用?
3. 如何理解响应时间?
4. 如何理解性能建模?(可分类回答)
5. 如何理解响应时间、TPS曲线和用户之间的关系?
6. 在LoadRunner中为什么要设置思考时间和pacing?
应用服务器
1. 如何理解J2EE的系统架构?
2. 如何理解J2EE应用服务器的容器?
3. 如何理解内存泄露?如何定位JAVA类的应用的内存泄露?如何定位C语言编写的应用的内存泄露?
4. 如果用纯JAVA的应用调用J2EE应用服务器的容器资源会出现什么结果?需要如何维护容器资源?(说明原理即可)
5. 如何定位JAVA的方法调用消耗的时间?(不通过在源代码中加时间戳的方式)?
6. 如何定位C语言中的函数调用消耗的时间?
7. 如何监控J2EE应用服务器?(可以用一个具体的应用服务器做例子)
数据库
1. 如何理解数据库架构?(可以用一个数据库做例子)
2. SQL语句在数据库中的执行分成几步,每一步都做什么?(可以用一个数据库做例子)
3. 如何跟踪SQL的执行时间和内存的消耗?(可以用一个数据库做例子)
4. 如何监控数据库?监控能得到什么数据?(可以用一个数据库做例子)
5. 如何定位死锁问题?如何定位热块问题?如何监控日志切换?(可以用一个数据库做例子)
6. 有几种手段可以改变执行计划?(可以用一个数据库做例子)
操作系统
1. 如何判断CPU、内存、磁盘的瓶颈?
2. 如何理解CPU、内存、磁盘之间的关系?
3. 如何理解paging in/paging out?
4. 如何监控操作系统的资源?(可以用一个操作系统做例子)
5. 如何理解内存管理和线程调度?(可以用一个操作系统做例子)
6. 如何理解CSwitch?(可以用一个操作系统做例子)
7. 如何理解磁盘IO?(可以用一个操作系统做例子)
网络
1. 如何定位数据包的传输在网络上消耗的时间?
2. 如何理解纯路由和NAT的区别?
性能测试工具
1. 解释LoadRunner的工作原理。
2. 如何理解LoadRunner里的关联?
3. 如何理解性能压力工具?
4. 如何理解虚拟用户?(可以用一个工具做例子)
5. 如果理解业务到脚本的转化?(可以用一个工具做例子)
6. 如何做到业务统计数据到场景的转化?(可以用一个工具做例子)
之前看乙醇视频中提到,selenium 的ruby 实现有一个小后门,在代码中加上$DEBUG=1 ,再运行脚本的过程中,就可以看到客户端请求的信息与服务器端返回的数据;觉得这个功能很强大,可以帮助理解webdriver的运行原理。
后来查了半天,python并没有提供这样一个方便的后门,不过我们可以通过代理的方式获得这些交互信息;
一、需要安装java 虚拟机与selenium-server-standalone ,参考 《selenium + python自动化测试环境搭建》第7、8操作:
二、通过下面命令启动服务:
C:\selenium>java -jar selenium-server-standalone-2.33.0.jar
在命令结尾加 >d:\log.txt 可以将命令信息存入文件,但信息很少。
然后运行下面的自动化脚本:
#coding = utf-8 import time from selenium import webdriver from selenium.webdriver.common.desired_capabilities import DesiredCapabilities driver = webdriver.Remote(desired_capabilities=DesiredCapabilities.CHROME) driver.get(http://www.youdao.com) driver.find_element_by_name("q").send_keys("hello") driver.find_element_by_name("q").send_keys("key.ENTER") driver.close() |
webdriver原理:
1. WebDriver 启动目标浏览器,并绑定到指定端口。该启动的浏览器实例,做为web driver的remote server。
2. Client 端通过CommandExcuter 发送HTTPRequest 给remote server 的侦听端口(通信协议: the webriver wire protocol)
3. Remote server 需要依赖原生的浏览器组件(如:IEDriver.dll,chromedriver.exe),来转化转化浏览器的native调用。
查看命令提示符下的运行日志:
咋一看很乱,慢慢分析一下就发现很有意思!结合上面的脚本分析
--------------------------------------------------------------------------------------- 启动代理进入监听状态 C:\selenium>java -jar selenium-server-standalone-2.33.0.jar 八月 22, 2013 10:19:48 上午 org.openqa.grid.selenium.GridLauncher main INFO: Launching a standalone server 10:19:48.734 INFO - Java: Oracle Corporation 23.21-b01 10:19:48.734 INFO - OS: Windows XP 5.1 x86 10:19:48.734 INFO - v2.33.0, with Core v2.33.0. Built from revision 4e90c97 10:19:48.843 INFO - RemoteWebDriver instances should connect to: http://127.0.0. 1:4444/wd/hub 10:19:48.843 INFO - Version Jetty/5.1.x 10:19:48.843 INFO - Started HttpContext[/selenium-server/driver,/selenium-server /driver] 10:19:48.843 INFO - Started HttpContext[/selenium-server,/selenium-server] 10:19:48.843 INFO - Started HttpContext[/,/] 10:19:48.890 INFO - Started org.openqa.jetty.jetty.servlet.ServletHandler@176343 e 10:19:48.890 INFO - Started HttpContext[/wd,/wd] 10:19:48.906 INFO - Started SocketListener on 0.0.0.0:4444 10:19:48.906 INFO - Started org.openqa.jetty.jetty.Server@388c74 -------------------------------------------------------------------------------------- 创建新session 10:20:38.593 INFO - Executing: [new session: {platform=ANY, javascriptEnabled=tr ue, browserName=chrome, version=}] at URL: /session) 10:20:38.593 INFO - Creating a new session for Capabilities [{platform=ANY, java scriptEnabled=true, browserName=chrome, version=}] webdrivr通过GET方式发送请求 [0.921][INFO]: received Webriver request: GET /status 向webdrver返回响应,返回码200表示成功 [0.921][INFO]: sending Webriver response: 200 { "sessionId": "", "status": 0, "value": { "build": { "version": "alpha" }, "os": { "arch": "x86", "name": "Windows NT", "version": "5.1 SP3" } } } webdriver 再次以POST方式发送请求,并启动浏览器相关信息 [0.984][INFO]: received Webriver request: POST /session { "desiredCapabilities": { "browserName": "chrome", "javascriptEnabled": true, "platform": "ANY", "version": "" } } [0.984][INFO]: Launching chrome: "C:\ocuments and Settings\Administrator\Local S ettings\Application ata\Google\Chrome\Application\chrome.exe" --remote-debugging -port=4223 --no-first-run --enable-logging --logging-level=1 --user-data-dir="C: \OCUME~1\AMINI~1\LOCALS~1\Temp\scoped_dir1808_7550" --load-extension="C:\OCUME~1 \AMINI~1\LOCALS~1\Temp\scoped_dir1808_26821\internal" --ignore-certificate-error s data:text/html;charset=utf-8, [1.773][INFO]: sending Webriver response: 303 webdriver再次以GET方法请求,这附加上了session的信息 [1.778][INFO]: received Webriver request: GET /session/32b33aa585ccbbf7ba7853588 2852af3 服务器先对sesssionID进行解析,确认是selenium调用的以及要访问的网址, [1.779][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": { "acceptSslCerts": true, "applicationCacheEnabled": false, "browserConnectionEnabled": false, "browserName": "chrome", "chrome": { "chromedriverVersion": "2.0" }, "cssSelectorsEnabled": true, "databaseEnabled": true, "handlesAlerts": true, "javascriptEnabled": true, "locationContextEnabled": true, "nativeEvents": true, "platform": "Windows NT", "rotatable": false, "takesScreenshot": true, "version": "27.0.1453.116", "webStorageEnabled": true } } 10:20:40.640 INFO - Done: /session 10:20:40.640 INFO - Executing: org.openqa.selenium.remote.server.handler.GetSess ionCapabilities@14cf7a1 at URL: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc) 10:20:40.640 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc 10:20:40.656 INFO - Executing: [get: http://www.youdao.com] at URL: /session/ac5 b2c71-5b1a-469e-814c-fdd09a2061fc/url) webdriver正试向服务器请求youdao网站 [1.820][INFO]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/url { "url": "http://www.youdao.com" } [1.822][INFO]: waiting for pending navigations... [1.829][INFO]: done waiting for pending navigations [2.073][INFO]: waiting for pending navigations... [2.900][INFO]: done waiting for pending navigations 获得服务器数据的应答 [2.900][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": null } 10:20:41.734 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/url -------------------------------------------------------------------------------------- 下面接着发送定位输入框的信息 10:20:41.734 INFO - Executing: [find element: By.name: q] at URL: /session/ac5b2 c71-5b1a-469e-814c-fdd09a2061fc/element) [2.905][INFO]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/element { "using": "name", "value": "q" } [2.905][INFO]: waiting for pending navigations... [2.905][INFO]: done waiting for pending navigations [2.922][INFO]: waiting for pending navigations... [2.922][INFO]: done waiting for pending navigations 得到服务器应答 [2.922][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": { "ELEMENT": "0.19427558477036655:1" } } 10:20:41.765 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element 10:20:41.765 INFO - Executing: [send keys: 0 org.openqa.selenium.support.events. EventFiringWebDriver$EventFiringWebElement@a8215ba9, [h, e, l, l, o]] at URL: /s ession/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/0/value) 向定位到的输入框写入hello [2.936][INFO]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/element/0.19427558477036655:1/value { "id": "0.19427558477036655:1", "value": [ "h", "e", "l", "l", "o" ] } [2.936][INFO]: waiting for pending navigations... [2.936][INFO]: done waiting for pending navigations [3.002][INFO]: waiting for pending navigations... [3.002][INFO]: done waiting for pending navigations [3.002][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": null } 10:20:41.843 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/ 0/value 再次发送定位输入框的请求 10:20:41.843 INFO - Executing: [find element: By.name: q] at URL: /session/ac5b2 c71-5b1a-469e-814c-fdd09a2061fc/element) [3.006][INFO]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/element { "using": "name", "value": "q" } [3.006][INFO]: waiting for pending navigations... [3.006][INFO]: done waiting for pending navigations [3.016][INFO]: waiting for pending navigations... [3.016][INFO]: done waiting for pending navigations [3.016][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": { "ELEMENT": "0.19427558477036655:1" } } 10:20:41.859 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element 10:20:41.859 INFO - Executing: [send keys: 0 org.openqa.selenium.support.events. EventFiringWebDriver$EventFiringWebElement@a8215ba9, [k, e, y, ., E, N, T, E, R] ] at URL: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/0/value) 对定位的到的输入框发送回车(ENTER)事件请求 [3.021][INFO]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/element/0.19427558477036655:1/value { "id": "0.19427558477036655:1", "value": [ "k", "e", "y", ".", "E", "N", "T", "E", "R" ] } [3.021][INFO]: waiting for pending navigations... [3.021][INFO]: done waiting for pending navigations [3.064][INFO]: waiting for pending navigations... [3.064][INFO]: done waiting for pending navigations [3.064][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": null } 10:20:41.906 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/ 0/value 10:20:41.906 INFO - Executing: [close window] at URL: /session/ac5b2c71-5b1a-469 e-814c-fdd09a2061fc/window) [3.068][INFO]: received Webriver request: ELETE /session/32b33aa585ccbbf7ba78535 882852af3/window [WARNING:chrome_desktop_impl.cc(88)] chrome detaches, user should take care of d irectory:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\scoped_dir1808_7550 and C:\DOCUME~1\ ADMINI~1\LOCALS~1\Temp\scoped_dir1808_26821 [5.318][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": null } 10:20:44.156 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/window |
request 请求 / response 应答
一次请求会对应一次应答
POST/GET 是请求(request)两种类型;关于两种请求方式的类别参考其它资料
200 、203 是 HTTP请求返回的状态码,200表示成功;
sessionid :每一个访问服务器的客户端,都要先得到服务器端分配的一个sessionid ,就像通行证一样,只有得到sessionid的客户端才能向服务器请求想要的数据。
其它还包括操作系统版本,浏览器类型、URL、字符类型等非常详细的记录。熟悉HTTP ,了解TCP 的三次握手四次挥手,相信你对浏览器的交互与webdriver原理会有更深入的认识。
相关文章:
selenium-webdriver(python) (十三) -- cookie处理
Selenium 1.x时代已经远去,它理应躺在历史的角落里,靠着壁炉烤着火,抽着旱烟,在袅袅的升起的青烟中回忆那曾经属于自己的美好时代。
不过事实却并非如此,现今原本早应退役的selenium 1.x却还是多数人坚定的选择,究其原因不过是1.x时代遗留下了大量的文档,代码,教程让人们误以为1.x还是这个年代的主流,还应该光鲜亮丽在前台演出属于它的美好。长江后浪推前浪,最为前浪的selenium 1.x的宿命应该是死在沙滩上。
好了,直入主题,由于开源社区不再维护selenium 1.x再加之更为先进的selenium 2.0确实有不少优势之初可以完全取代1.x,在这里笔者会花一些笔墨,若干篇幅,争取深入浅出的讲解selenium 2.0的一些基本知识,常用方法和高级扩展,但由于笔者水平和时间精力等确实有限,文中应该避免不了谬误和臆断之处,还望众位读者多多海涵。
本文中所以代码和示例均由Ruby编写,本文介绍的webdriver api也主要是ruby binding。所以首先请确保ruby语言在开发环境上正确安装。
教程的第一节从selenium 2.0和webdriver关系说起。
Selenium 2.0 和webdriver之间有什么关系,有什么不可告人的秘密?说来话长,但也简单。Selenium 2.0其实就是webdriver。就像张飞就是张翼德,关羽就是关云长一样,叫法不同但内容却是一样的。
安装selenium webdriver
(1)安装ruby1.8.7或1.9.2。注意selenium-webdriver只支持1.8.7以上的ruby版本;
(2)使用gem安装selenium-webdriver;打开命令行,输入下列代码完成安装。注意,如果你的开发环境需要http proxy的话,请注意在gem命令中加入--http_proxy参数;
gem install selenium-webdriver [--http_proxy]
(3)在命令行中输入gem list,如果selenium-webdriver正确安装,则其应该出现在结果列表里。在文本写作时,selenium webdriver的最新版本应该是2.2.0;
gem list selenium-webdriver
(4)安装firefox。本文使用firefox作为测试浏览器进行讲解,所以请确保开发环境上正确安装了firefox。由于firefox版本更新较快,我们只需要选择1个稳定版本安装既可,本文中笔者使用的版本是FF 5.0;
简单的google test
下面我们写几行代码在初次感受一下webdriver的魅力,好吧,说老实话原生的selenium webdriver并没有什么独特的魅力,相反到是watir-webdriver更加的平易近人老少咸宜。这个不是文本讨论的范畴,暂且打住。
require 'rubygems' require 'selenium-webdriver' driver = Selenium::WebDriver.for :firefox driver.navigate.to http://google.com sleep 3 element = driver.find_element(:name, 'q') element.send_keys "Hello WebDriver!" element.submit puts driver.title driver.quit |
一、Mysqlslap介绍
mysqlslap是MySQL5.1之后自带的benchmark基准测试工具,类似Apache Bench负载产生工具,生成schema,装载数据,执行benckmark和查询数据,语法简单,灵活,容易使用。该工具可以模拟多个客户端同时并发的向服务器发出查询更新,给出了性能测试数据而且提供了多种引擎的性能比较。mysqlslap为mysql性能优化前后提供了直观的验证依据,笔者建议系统运维人员应该掌握一些常见的压力测试工具,这样才能较为准确的掌握线上系统能够支撑的用户流量上限及其抗压性等问题。
二、使用方法介绍
可以使用mysqlslap --help来显示使用方法:
1) --concurrency代表并发数量,多个可以用逗号隔开,例如:concurrency=10,50,100, 并发连接线程数分别是10、50、100个并发。
2) --engines代表要测试的引擎,可以有多个,用分隔符隔开。
3) --iterations代表要运行这些测试多少次。
4) --auto-generate-sql 代表用系统自己生成的SQL脚本来测试。
5)--auto-generate-sql-load-type 代表要测试的是读还是写还是两者混合的(read,write,update,mixed)
6) --number-of-queries 代表总共要运行多少次查询。每个客户端运行的查询数量可以用查询总数/并发数来计算。
7) --debug-info 代表要额外输出CPU以及内存的相关信息。
8) --number-int-cols :创建测试表的 int 型字段数量
9) --auto-generate-sql-add-autoincrement : 代表对生成的表自动添加auto_increment列,从5.1.18版本开始
10) --number-char-cols 创建测试表的 char 型字段数量。
11) --create-schema 测试的schema,MySQL中schema也就是database。
12) --query 使用自定义脚本执行测试,例如可以调用自定义的一个存储过程或者sql语句来执行测试。
13) --only-print 如果只想打印看看SQL语句是什么,可以用这个选项。
三、Demo实例
下面我们使用几个demo实例来进行测试
1、Demo1:
[root@localhost ~]# mysqlslap -uroot -p123abc --concurrency=100 --iterations=1 --auto-generate-sql --auto-generate-sql-load-type=mixed --auto-generate-sql-add-autoincrement --engine=myisam --number-of-queries=10 --debug-info
#备注本次测试以100个并发线程、测试1次,自动生成SQL测试脚本、读、写、更新混合测试、自增长字段、测试引擎为myisam、共运行10次查询,输出cpu资源信息
返回信息如下所示:
rement --engine=myisam --number-of-queries=10 --debug-info Benchmark Running for engine myisam Average number of seconds to run all queries: 0.129 seconds Minimum number of seconds to run all queries: 0.107 seconds Maximum number of seconds to run all queries: 0.264 seconds Number of clients running queries: 100 Average number of queries per client: 0 User time 0.16, System time 0.25 Maximum resident set size 4624, Integral resident set size 0 Non-physical pagefaults 7346, Physical pagefaults 0, Swaps 0 Blocks in 0 out 0, Messages in 0 out 0, Signals 0 Voluntary context switches 27221, Involuntary context switches 4241 |
2、Demo2:指定数据库和sql语句
mysqlslap -h192.168.202.84 -P3309 --concurrency=100 --iterations=1 --create-schema='mms_sdmtv' --query='select * from role;' --number-of-queries=10 --debug-info -uroot -p123abc
#备注使用mysqlslap指定sql语句进行测试
3、Demo3:测试用例
[root@localhost /]# mysqlslap --concurrency=50,100,200 --iterations=20 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=read --engine=myisam,innodb --number-of-queries=200 --verbose --socket=/var/lib/mysql/mysql.sock -uroot -p123abc #系统脚本测试,增加int型 4列char 型35列,测试2种引擎myisam,innodb读的性能,分别用50,100,200个客户端对服务器进行测试总共200个查询语句 执行20次查询 Benchmark Running for engine myisam Average number of seconds to run all queries: 0.666 seconds Minimum number of seconds to run all queries: 0.223 seconds Maximum number of seconds to run all queries: 4.889 seconds Number of clients running queries: 50 Average number of queries per client: 4 Benchmark Running for engine myisam Average number of seconds to run all queries: 0.620 seconds Minimum number of seconds to run all queries: 0.231 seconds Maximum number of seconds to run all queries: 4.898 seconds Number of clients running queries: 100 Average number of queries per client: 2 Benchmark Running for engine myisam Average number of seconds to run all queries: 0.503 seconds Minimum number of seconds to run all queries: 0.257 seconds Maximum number of seconds to run all queries: 4.269 seconds Number of clients running queries: 200 Average number of queries per client: 1 Benchmark Running for engine innodb Average number of seconds to run all queries: 1.049 seconds Minimum number of seconds to run all queries: 0.244 seconds Maximum number of seconds to run all queries: 5.292 seconds Number of clients running queries: 50 Average number of queries per client: 4 Benchmark Running for engine innodb Average number of seconds to run all queries: 0.712 seconds Minimum number of seconds to run all queries: 0.246 seconds Maximum number of seconds to run all queries: 6.585 seconds Number of clients running queries: 100 Average number of queries per client: 2 Benchmark Running for engine innodb Average number of seconds to run all queries: 0.269 seconds Minimum number of seconds to run all queries: 0.175 seconds Maximum number of seconds to run all queries: 0.328 seconds Number of clients running queries: 200 Average number of queries per client: 1 |
4、自建SQL测试用例
mysqlslap --create=/yourpath/Test1.sql --query=/yourpath/Test2.sql --concurrency=50,100,200 --iterations=20 --engine=myisam,innodb -u root -p123abc
#在设定的yourpath目录下创建你的测试sql文Test1及Test2并进行50、100及200的模拟并发测试
TPC(Tracsaction Processing Performance Council) 事务处理性能协会是一个评价大型数据库系统软硬件性能的非盈利的组织,TPC-C是TPC协会制定的,用来测试典型的复杂OLTP系统的性能。Tpcc-mysql是percona基于tpcc衍生出来的产品,专用于mysql基准测试,其源码放在bazaar上,因此需要先安装bazaar客户端。
一 下载工具
安装rpm包 ,根据系统的不同选择合适的rpm
rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
安装bzr 客户端
yum install bzr
下载tpcc-mysql
[root@rac1 markbench]# bzr branch lp:~percona-dev/perconatools/tpcc-mysql You have not informed bzr of your Launchpad ID, and you must do this to write to Launchpad or access privatedata. See "bzr help launchpad-login". Branched 48 revision(s). bzr: warning: some compiled extensions could not be loaded; see <https://answers.launchpad.net/bzr/+faq/703> [root@rac1 markbench]# |
遇到的问题
[root@rac1 markbench]# bzr branch lp:~percona-dev/perconatools/tpcc-mysql bzr: ERROR: Couldn't import bzrlib and dependencies. Please check the directory containing bzrlib is on yourPYTHONPATH. Traceback (most recent call last): File "/usr/bin/bzr", line 102, in <module> import bzrlib ImportError: No module named bzrlib |
python安装环境,建议使用python2.6的版本,提示找不到 bzrlib 模块
解决方法:
[root@rac1 markbench]# find / -name bzrlib -print /usr/lib64/python2.4/site-packages/bzrlib [root@rac1 markbench]# exportPYTHONPATH=/usr/lib64/python2.4/site-packages |
再次下载ok.
二 编译安装
进入源码目录
cdtpcc-mysql/src !!!!make之前一定要修改src下面makefile的mysql_config make [root@rac1src]#make cc-w-O2-g-I.`mysql_config--include`-cload.c cc-w-O2-g-I.`mysql_config--include`-csupport.c ccload.osupport.o`mysql_config--libs_r`-lrt-o../tpcc_load cc-w-O2-g-I.`mysql_config--include`-cmain.c cc-w-O2-g-I.`mysql_config--include`-cspt_proc.c cc-w-O2-g-I.`mysql_config--include`-cdriver.c cc-w-O2-g-I.`mysql_config--include`-csequence.c cc-w-O2-g-I.`mysql_config--include`-crthist.c cc-w-O2-g-I.`mysql_config--include`-cneword.c cc-w-O2-g-I.`mysql_config--include`-cpayment.c cc-w-O2-g-I.`mysql_config--include`-cordstat.c cc-w-O2-g-I.`mysql_config--include`-cdelivery.c cc-w-O2-g-I.`mysql_config--include`-cslev.c ccmain.ospt_proc.odriver.osupport.osequence.orthist.oneword.opayment.oordstat.odelivery.oslev.o`mysql_config--libs_r`-lrt-o../tpcc_start |
三 初始化测试库环境
make命令会在tpcc-mysql目录下生成 tpcc 命令行工具 tpcc_load ,tpcc_start
tpcc_load 提供初始化数据的功能
tpcc_start 进行压力测试
用法:
# ./tpcc_load –help tpcc_load [server] [DB] [user] [pass] [warehouse] server : 服务器名 DB : 数据库名 user : 用户名 pass : 密码 warehouse: 仓库的数量 # ./tpcc_start –help tpcc_start -h server_host -P port -d database_name -u mysql_user -p mysql_password -w warehouses -c connections -r warmup_time -l running_time -i report_interval -f report_file |
介绍一下各个参数的用法
-h server_host: 服务器名 -P port : 端口号,默认为3306 -d database_name: 数据库名 -u mysql_user : 用户名 -p mysql_password : 密码 -w warehouses: 仓库的数量 -c connections : 线程数,默认为1 -r warmup_time : 热身时间,单位:s,默认为10s ,热身是为了将数据加载到内存。 -l running_time: 测试时间,单位:s,默认为20s -i report_interval 指定生成报告间隔时长 -f report_file : 测试结果输出文件 |
注意
tpcc 默认会读取/var/lib/mysql/mysql.sock 这个socket位置,如果你的测试环境的mysql socket不在相应路径的话,就需要做个软连接,或者通过TCP/IP的方式连接测试服务器。
准备工作:
mysqladmin -uroot -h127.0.0.1 create tpcc # 创建测试用的数据库
mysql -uroot -h127.0.0.1 tpcc < create_table.sql # 创建测试用的表
mysql -uroot -h127.0.0.1 tpcc < add_fkey_idx.sql # 创建FK和索引
1 创建五个数据仓库
./tpcc_load 127.0.0.1 tpcc root "" 5 ************************************* *** ###easy### TPC-C Data Loader *** ************************************* <Parameters> [server]: 127.0.0.1 [port]: 3306 [DBname]: tpcc [user]: root [pass]: [warehouse]: 5 TPCC Data Load Started... Loading Item .................................................. 5000 .................................................. 10000 |
忽略部分输出结果
四、进行测试
#使用tpcc_start 进行5个线程的测试,热身时间为120秒, 测试时间为1小时 !
[root@rac1 tpcc-mysql]# tpcc_start -hlocalhost -d tpcc -u root -p '' -w 5 -c 5 -r 120 -l 300 -f tpcc_mysql_20130331.xls *************************************** *** ###easy### TPC-C Load Generator *** *************************************** option h with value 'localhost' option d with value 'tpcc' option u with value 'root' option p with value '' option w with value '5' option c with value '5' option r with value '120' option l with value '300' option f with value 'tpcc_mysql_20130331.xls' <Parameters> [server]: localhost [port]: 3306 [DBname]: tpcc [user]: root [pass]: [warehouse]: 5 [connection]: 5 [rampup]: 120 (sec.) [measure]: 300 (sec.) RAMP-UP TIME.(120 sec.) MEASURING START. ---每隔10s 输出一次结果 10, 812(1):1.509|5.419, 814(0):0.447|1.575, 81(0):0.241|0.539, 82(0):2.270|2.824, 81(0):4.571|6.648 ...... 290, 851(0):1.582|2.949, 849(0):0.441|0.630, 85(0):0.199|0.235, 86(0):1.937|2.000, 85(0):4.178|4.226 300, 820(0):1.451|2.321, 822(0):0.443|0.718, 82(0):0.201|0.284, 81(0):1.922|2.972, 82(0):4.409|5.245 STOPPING THREADS..... <Raw Results> [0] sc:25485 lt:1 rt:0 fl:0 [1] sc:25487 lt:0 rt:0 fl:0 [2] sc:2548 lt:0 rt:0 fl:0 [3] sc:2549 lt:0 rt:0 fl:0 [4] sc:2549 lt:0 rt:0 fl:0 in 300 sec. <Raw Results2(sum ver.)> [0] sc:25485 lt:1 rt:0 fl:0 [1] sc:25487 lt:0 rt:0 fl:0 [2] sc:2548 lt:0 rt:0 fl:0 [3] sc:2549 lt:0 rt:0 fl:0 [4] sc:2549 lt:0 rt:0 fl:0 <Constraint Check> (all must be [OK]) [transaction percentage] Payment: 43.48% (>=43.0%) [OK] Order-Status: 4.35% (>= 4.0%) [OK] Delivery: 4.35% (>= 4.0%) [OK] Stock-Level: 4.35% (>= 4.0%) [OK] [response time (at least 90% passed)] New-Order: 100.00% [OK] Payment: 100.00% [OK] Order-Status: 100.00% [OK] Delivery: 100.00% [OK] Stock-Level: 100.00% [OK] <TpmC> 5097.200 TpmC |
业务流程:
1.打开此网页 http://nanjing.xiaomishu.com/shop/search/sp2048_745
2.向下拖动滚动条,右下角自动会出现【投诉与建议】(此网页已经修改不拖动也出现了,以前是没有的,)
3.点击【投诉与建议】
4.在打开的div 层中输入姓名,邮件,内容 并点击确定
5.验证页面上的提示文字
package com.example.tests; import org.junit.*; import org.openqa.selenium.*; import org.openqa.selenium.ie.InternetExplorerDriver; public class SeleniumWebDriver { public static WebDriver driver; @Test public void testUnit() { driver = new InternetExplorerDriver(); driver.get(http://nanjing.xiaomishu.com/shop/search/sp2048_745); maxBrowser(driver); setScroll(driver,500);
driver.findElement(By.linkText("投诉与建议")).click(); driver.findElement(By.xpath("//input[@id='repName']")).sendKeys("1"); driver.findElement(By.xpath("//input[@id='repMail']")).sendKeys("1"); driver.findElement(By.xpath("//textarea[@id='repContent']")).sendKeys("hello"); driver.findElement(By.xpath("//a[@id='repBtn']")).click(); Assert.assertEquals("您输入的邮箱格式不正确", driver.findElement(By.xpath("//div[@id='floatBox_remind']/span")).getText()); } //将IE最大化 public static void maxBrowser(WebDriver driver){ try { String maxBroswer = "if (window.screen) {window.moveTo(0, 0);" + "window.resizeTo(window.screen.availWidth,window.screen.availHeight);}"; JavascriptExecutor jse=(JavascriptExecutor) driver; jse.executeScript(maxBroswer); } catch (Exception e) { System.out.println("Fail to Maximization browser"); } } //将滚动条滚到适合的位置 public static void setScroll(WebDriver driver,int height){ try { String setscroll = "document.documentElement.scrollTop=" + height; JavascriptExecutor jse=(JavascriptExecutor) driver; jse.executeScript(setscroll); } catch (Exception e) { System.out.println("Fail to set the scroll."); } } }
|
对于这种顶级div层,一开始用id来定位,在firefox中可以正常跑
在IE中会报stack overflow的错误,一直以来是以为没有加等待时间而没找到
后来试了很多方法,最后发现用xpath就解决了,真是耽误了很久的时间
给大家借鉴,如果一个定位方法不能用时,多换换其他的
这个例子中我们学习了如何用JS控制滚动条,如何最大化IE页面。
本节重点:
- driver.get_cookies() 获得cookie信息
- add_cookie(cookie_dict) 向cookie添加会话信息
- delete_cookie(name) 删除特定(部分)的cookie
- delete_all_cookies() 删除所有cookie
通过webdriver 操作cookie 是一件非常有意思的事儿,有时候我们需要了解浏览器中是否存在了某个cookie 信息,webdriver 可以帮助我们读取、添加,删除cookie信息。
打印cookie信息
#coding=utf-8 from selenium import webdriver import time driver = webdriver.Chrome() driver.get("http://www.youdao.com") # 获得cookie信息 cookie= driver.get_cookies() #将获得cookie的信息打印 print cookie driver.quit()
运行打印信息:
[{u'domain': u'.youdao.com', u'secure': False, u'value': u'aGFzbG9nZ2VkPXRydWU=', u'expiry': 1408430390.991375, u'path': u'/', u'name': u'_PREF_ANONYUSER__MYTH'}, {u'domain': u'.youdao.com', u'secure': False, u'value': u'1777851312@218.17.158.115', u'expiry': 2322974390.991376, u'path': u'/', u'name': u'OUTFOX_SEARCH_USER_ID'}, {u'path': u'/', u'domain': u'www.youdao.com', u'name': u'JSESSIONID', u'value': u'abcUX9zdw0minadIhtvcu', u'secure': False}]
对cookie的操作
上面的方式打印了所有cookie信息表,太多太乱,我们只想有真对性的打印自己想要的信息,看下面的例子
#coding=utf-8 from selenium import webdriver import time driver = webdriver.Firefox() driver.get("http://www.youdao.com") #向cookie的name 和value添加会话信息。 driver.add_cookie({'name':'key-aaaaaaa', 'value':'value-bbbb'}) #遍历cookies中的name 和value信息打印,当然还有上面添加的信息 for cookie in driver.get_cookies(): print "%s -> %s" % (cookie['name'], cookie['value']) # 下面可以通过两种方式删除cookie # 删除一个特定的cookie driver.delete_cookie("CookieName") # 删除所有cookie driver.delete_all_cookies() time.sleep(2) driver.close()
运行打印信息:
YOUDAO_MOBILE_ACCESS_TYPE -> 1 _PREF_ANONYUSER__MYTH -> aGFzbG9nZ2VkPXRydWU= OUTFOX_SEARCH_USER_ID -> -1046383847@218.17.158.115 JSESSIONID -> abc7qSE_SBGsVgnVLBvcu key-aaaaaaa -> value-bbbb # 这一条是我们自己添加的
通过博客园登陆来分析cookie
#coding=utf-8 from selenium import webdriver import time driver = webdriver.Firefox() driver.get("http://passport.cnblogs.com/login.aspx?ReturnUrl=http://www.cnblogs.com/fnng/admin/EditPosts.aspx") time.sleep(3) driver.maximize_window() # 浏览器全屏显示 #通过用户名密码登陆 driver.find_element_by_id("tbUserName").send_keys("fnngj") driver.find_element_by_id("tbPassword").send_keys("123456") #勾选保存密码 driver.find_element_by_id("chkRemember").click() time.sleep(3) #点击登陆按钮 driver.find_element_by_id("btnLogin").click() #获取cookie信息并打印 cookie= driver.get_cookies() print cookie time.sleep(2) driver.close()
运行打印信息:
#第一次执行信息 >>> [{u'domain': u'.cnblogs.com', u'name': u'.DottextCookie', u'value': u'C709F15A8BC0B3E8D9AD1F68B371053849F7FEE31F73F1292A150932FF09A7B0D4A1B449A32A6B24AD986CDB05B9998471A37F39C3B637E85E481AA986D3F8C187D7708028F9D4ED3B326B46DC43B416C47B84D706099ED1D78B6A0FC72DCF948DB9D5CBF99D7848FDB78324', u'expiry': None, u'path': u'/', u'secure': False}] >>> ========================= RESTART ================================ #第二次执行信息 >>> [{u'domain': u'.cnblogs.com', u'name': u'.DottextCookie', u'value': u'5BB735CAD62E99F8CCB9331C32724E2975A0150D199F4243AD19357B3F99A416A93B2E803F4D5C9D065429713BE8B5DB4ED760EDCBAF492EABE2158B3A6FBBEA2B95C4DA3D2EFEADACC3247040906F1462731F652199E2A8BEFD8A9B6AAE87CF3059A3CAEB9AB0D8B1B7AD2A', u'expiry': 1379502502, u'path': u'/', u'secure': False}] >>>
第一次注释掉勾选保存密码的操作,第二次通过勾选保存密码获得cookie信息 ;来看两次运行结果的cookie的何不同:
u'expiry': None
u'expiry': 1379502502
通过对比发现,不勾选保存密码时expiry 的值为none ; 那么就可以初步判断勾选保存密码的操作在cookie 中起到了作用。至于是否准确可以再做进一步的分析。
简单工厂模式 SimpleFactory,适用于业务相对较简单地情况,少扯闲话,先上code:
~~ ICar.java ~~ 抽象产品角色接口
/** * @author VerpHen * @date 2013年9月10日 上午9:36:19 */ package org.verphen.simpleFactory; /*抽象产品角色接口:汽车Car*/ public interface ICar { /* 汽车启动 */ public void run(); /* 汽车停止 */ public void stop(); } |
~~ Benz.java ~~ 具体产品角色类
/** * @author VerpHen * @date 2013年9月10日 上午9:38:08 */ package org.verphen.simpleFactory; /*具体产品角色类:汽车-奔驰Benz*/ public class Benz implements ICar { @Override public void run() { System.out.println("奔驰启动"); } @Override public void stop() { System.out.println("奔驰停止"); } } |
~~ Ferrari.java ~~ 具体产品角色类
/** * @author VerpHen * @date 2013年9月10日 上午9:40:18 */ package org.verphen.simpleFactory; /*具体产品角色类:汽车-法拉利Ferrari*/ public class Ferrari implements ICar { @Override public void run() { System.out.println("法拉利启动"); } @Override public void stop() { System.out.println("法拉利停止"); } } |
~~ Jaguar.java ~~ 具体产品角色类
/** * @author VerpHen * @date 2013年9月10日 上午9:42:21 */ package org.verphen.simpleFactory; /*具体产品角色类:汽车-捷豹-Jaguar*/ public class Jaguar implements ICar { @Override public void run() { System.out.println("捷豹启动"); } @Override public void stop() { System.out.println("捷豹停止"); } } |
~~ SimpleFactory.java ~~ 工厂角色类
/** * @author VerpHen * @date 2013年9月10日 上午9:29:02 */ package org.verphen.simpleFactory; /*工厂角色类*/ public class SimpleFactory { /* 工厂方法,返回类型为抽象的产品角色 */ public static ICar driveCar(String carType) { /* equalsIgnoreCase() 忽略carType大小写 */ if ("Benz".equalsIgnoreCase(carType)) { return new Benz(); } else if ("Ferrari".equalsIgnoreCase(carType)) { return new Ferrari(); } else if ("Jaguar".equalsIgnoreCase(carType)) { return new Jaguar(); } return null; } } |
~~ SimpleFactoryTest.java ~~ 测试类
/** * @author VerpHen * @date 2013年9月10日 上午9:48:32 */ package org.verphen.simpleFactory; /*测试简单工厂方法类*/ public class SimpleFactoryTest { public static void main(String[] args) { // ICar car = SimpleFactory.driveCar("Benz"); // ICar car = SimpleFactory.driveCar("Ferrari"); ICar car = SimpleFactory.driveCar("Jaguar"); car.run(); car.stop(); } } |
这里演示的是如何将上传图片直接保存至SQL Server数据库中。
在数据库中,图片使用varbinary(MAX)存储:
这是个基于MVC3架构的例子。废话不多说,直接上代码:
View:
@{ ViewBag.Title = "UpLoadImg"; } @using (Html.BeginForm("Create", "UpLoadImg", FormMethod.Post, new { enctype = "multipart/form-data" })) { <h2> UpLoadImg</h2> <div id="mainform"> <div> <input type="file" id="UpLoadFile" name="UpLoadFile" /> <input id="btnUpLoad" type="submit" value="上传" /> </div> <div> </div> </div> } |
Controller后台Action:
public ActionResult Create() { string filename = string.Empty; string filetype=string.Empty; byte[] filecontext=null; HttpPostedFileBase filebase = Request.Files["UpLoadFile"]; if (filebase.ContentLength > 0) { Stream stream = filebase.InputStream; byte[] by = new byte[filebase.ContentLength]; int i = stream.Read(by,0,filebase.ContentLength); stream.Close(); string[] arrs = filebase.FileName.Split('\\'); if (arrs.Length > 0) { filename = arrs[arrs.Length - 1]; } else { filename = filebase.FileName; } filetype=filebase.ContentType; filecontext=by; }//淡雅一抹繁华,几多思念许他,他不知花开不易,他不懂人心需要珍惜。 int count = 0; #region 插入数据 try { string ImageStore = System.Configuration.ConfigurationManager.AppSettings["ConnectionStrImageStore"].ToString().Trim(); string sqlStr = string.Empty; sqlStr = @"INSERT INTO [Images] ([filename],[filetype],[filecontext],[uploadtime]) VALUES(@filename,@filetype,@filecontext,@uploadtime)"; SqlConnection connection = new SqlConnection(ImageStore); SqlCommand command = new SqlCommand(sqlStr, connection); command.Parameters.AddWithValue("@filename",filename); command.Parameters.AddWithValue("@filetype",filetype); command.Parameters.AddWithValue("@filecontext",filecontext); command.Parameters.AddWithValue("@uploadtime",DateTime.Now); command.Connection.Open(); count=command.ExecuteNonQuery(); command.Connection.Close(); } catch { } #endregion if (count > 0) { return RedirectToAction("UpLoadImg"); } else { return RedirectToAction("Index"); } } |