1. <nobr id="easjo"><address id="easjo"></address></nobr>

      <track id="easjo"><source id="easjo"></source></track>
      1. 
        

      2. <bdo id="easjo"><optgroup id="easjo"></optgroup></bdo>
      3. <track id="easjo"><source id="easjo"><em id="easjo"></em></source></track><option id="easjo"><span id="easjo"><em id="easjo"></em></span></option>
          貴州做網站公司
          貴州做網站公司~專業!靠譜!
          10年網站模板開發經驗,熟悉國內外開源網站程序,包括DEDECMS,WordPress,ZBlog,Discuz! 等網站程序,可為您提供網站建設,網站克隆,仿站,網頁設計,網站制作,網站推廣優化等服務。我們專注高端營銷型網站,企業官網,集團官網,自適應網站,手機網站,網絡營銷,網站優化,網站服務器環境搭建以及托管運維等。為客戶提供一站式網站解決方案?。?!

          基于Selenium和ChromeDriver的自動化頁面性能測試

          來源:互聯網轉載 時間:2024-01-29 08:00:07

          由于最近工作一直很緊張,拖了很久才在五一假期將Selenium實現自動化頁面性能測試的代碼實現部分補上,希望今后自己能更勤勉,多一些知識產出。 Selenium WebDriver(以下簡稱SW)提供了一套用于Web應用程序的自動化測試工具。SW按其應用場景不同可以分為(1)基于HtmlUnit的無界面實現,并非驅動真實瀏覽器進行測試;(2)模擬真實輸入,對多瀏覽器的支持和測試,包括FirefoxDriver、InternetExplorerDriver、OperaDriver和ChromeDriver;(3)對移動應用的測試,包括AndroidDriver和iPhoneDriver。 針對SW進行功能性測試的文章和書已經很多了,比如如何操作獲取頁面元素內容。而本文所要寫的是如何基于Selenium和ChromeDriver做頁面性能測試,比如獲取頁面請求的加載時間、獲取頁面的DOM元素加載完成時間等等。類似于一些成熟的撥測產品的實現原型(這也是筆者正在做的項目)。我想這是非常有意義的一次探索。

          • 1. Maven依賴
          • 2、ChromeDriver使用詳解
            • 2.1、DesiredCapabilities & ChromeOptions
            • 2.2、Performance Log
            • 2.3、Chrome DevTools Protocol View
              • 2.3.1、Network
                • Network.requestWillBeSent
                • Network.responseReceived
                • Network.loadingFailed
                • Network.loadingFinished
              • 2.3.2、Page
                • Page.domContentEventFired
                • Page.loadEventFired
          • 3、持久化ChromeDriverService的使用
          • 4、一個應用實例的實現

          1. Maven依賴

          首先,項目需要引入依賴的相關selenium包:selenium-api和selenium-java,要考慮不同版本和JDK版本的兼容性,筆者是JDK 1.8。

          <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-api --><dependency>    <groupId>org.seleniumhq.selenium</groupId>    <artifactId>selenium-api</artifactId>    <version>3.5.3</version></dependency>
          <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --><dependency>    <groupId>org.seleniumhq.selenium</groupId>    <artifactId>selenium-java</artifactId>    <version>3.5.3</version></dependency>

          2、ChromeDriver使用詳解

          本節內容參考https://sites.google.com/a/chromium.org/chromedriver/home,另外ChromeDriver的安裝,筆者在《CentOS 7.x環境下搭建: Headless chrome + Selenium + ChromeDriver 實現自動化測試》中有詳述。

          2.1、DesiredCapabilities & ChromeOptions

          Capabilities屬性可以定義和配置你的ChromeDriver會話,以滿足對應功能和需求。 在Java實現中,類ChromeOptions和類DesiredCapabilities都可以用于具體定義Capabilities。 比如以下代碼,通過ChromeOptions來定義Chrome的window-size屬性:

          // 設置chromedriver路徑System.setProperty("webdriver.chrome.driver","/opt/drivers/chromedriver");ChromeOptions options = new ChromeOptions();// 設置chrome啟動時size大小options.addArguments("--window-size=1980,1000");// 根據ChromeOptions實例化ChromeDriverWebDriver driver = new ChromeDriver(options);try {    // 打開蘇寧易購    driver.get("https://www.suning.com");    } catch (Exception e) {    e.printStackTrace();    } finally {    // 關閉瀏覽器    driver.quit();}

          當然,以上例子也可以改寫為通過DesiredCapabilities來實現:

          // 設置chromedriver路徑System.setProperty("webdriver.chrome.driver","/opt/drivers/chromedriver");ChromeOptions options = new ChromeOptions();// 設置chrome啟動時size大小options.addArguments("--window-size=1980,1000");DesiredCapabilities cap = DesiredCapabilities.chrome();cap.setCapability(ChromeOptions.CAPABILITY, options);// 根據DesiredCapabilities實例化ChromeDriverWebDriver driver = new ChromeDriver(cap);try {    // 打開蘇寧易購    driver.get("https://www.suning.com");    } catch (Exception e) {    e.printStackTrace();    } finally {    // 關閉瀏覽器    driver.quit();}

          2.2、Performance Log

          ChromeDriver支持性能日志(Performance Log)數據的采集。想想看Chrome的F12控制臺,我們能夠采集到”Network”、Page”等,而這些是實現頁面性能測試的基礎。 Performance Log并非是默認開啟的屬性,所以我們可以通過上節說的DesiredCapabilities在創建新會話的時候開啟Performance Log。 而采集到的日志,我們可以通過LogEntry對象輸出到Console。具體代碼實現如下:

          package com.suning.webdrivertest.chromedemo;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.logging.LogEntry;import org.openqa.selenium.logging.LogType;import org.openqa.selenium.logging.LoggingPreferences;import org.openqa.selenium.remote.CapabilityType;import org.openqa.selenium.remote.DesiredCapabilities;import java.util.logging.Level;/** * * Created by zhuyiquan90 on 2018/1/3. */public class ChromeDriverDemo1 {        public static void main(String[] args) {        // 設置chromedriver路徑        System.setProperty("webdriver.chrome.driver", "/opt/drivers/chromedriver");        DesiredCapabilities cap = DesiredCapabilities.chrome();        LoggingPreferences logPrefs = new LoggingPreferences();        // 啟用Performance Log日志采集        logPrefs.enable(LogType.PERFORMANCE, Level.ALL);        cap.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);        // 根據DesiredCapabilities實例化ChromeDriver        WebDriver driver = new ChromeDriver(cap);        try {            // 打開蘇寧易購            driver.get("https://www.suning.com");            for (LogEntry entry : driver.manage().logs().get(LogType.PERFORMANCE)) {                // 輸出采集到的性能日志                System.out.println(Thread.currentThread().getName() + entry.toString());            }        } catch (Exception e) {            e.printStackTrace();        } finally {            // 關閉瀏覽器            driver.quit();        }    }}

          其輸出結果如下:

          Starting ChromeDriver 2.34.522932 (4140ab217e1ca1bec0c4b4d1b148f3361eb3a03e) on port 29777Only local connections are allowed.四月 30, 2018 3:06:27 下午 org.openqa.selenium.remote.ProtocolHandshake createSession信息: Detected dialect: OSSmain[2018-04-30T15:06:27+0800] [INFO] {    "message":{    "method":"Page.frameAttached","params":{    "frameId":"49C70573CE1145CEB5B38A270213A48","parentFrameId":"28DAFE9FE90E9292F1B8EDB3315608EC","stack":{    "callFrames":[{    "columnNumber":240,"functionName":"","lineNumber":0,"scriptId":"21","url":""}]}}},"webview":"28DAFE9FE90E9292F1B8EDB3315608EC"}main[2018-04-30T15:06:27+0800] [INFO] {    "message":{    "method":"Page.frameStartedLoading","params":{    "frameId":"49C70573CE1145CEB5B38A270213A48"}},"webview":"28DAFE9FE90E9292F1B8EDB3315608EC"}main[2018-04-30T15:06:27+0800] [INFO] {    "message":{    "method":"Page.frameNavigated","params":{    "frame":{    "id":"49C70573CE1145CEB5B38A270213A48","loaderId":"EE699DC52C8ACA226069D24DC92E16","mimeType":"text/html","name":"chromedriver dummy frame","parentId":"28DAFE9FE90E9292F1B8EDB3315608EC","securityOrigin":"://","url":"about:blank"}}},"webview":"28DAFE9FE90E9292F1B8EDB3315608EC"}

          2.3、Chrome DevTools Protocol View

          這一節,我們來講講Network和Page包含的內容,即針對上一節輸出的內容,我們如何有效利用,通過它們來計算頁面性能(參考Chrome DevTools Protocol)。

          2.3.1、Network

          Network中我們用到的事件主要是requestWillBeSent、responseReceived、loadingFailed和loadingFinished四種:

          Network.requestWillBeSent

          當頁面即將發送HTTP請求時觸發,其Json格式為:

          {    "message": { "method": "Network.requestWillBeSent", "params": { "documentURL": "about:blank", "frameId": "C80F96297F4216E35079CFD86251AB8B", "initiator": { "lineNumber": 0, "type": "parser", "url": "https://www.suning.com/" }, "loaderId": "58DDB2CF16600EAE484A541DF9440089", "redirectResponse": { "connectionId": 639, "connectionReused": false, "encodedDataLength": 497, "fromDiskCache": false, "fromServiceWorker": false, "headers": { "Cache-Control": "no-cache", "Connection": "keep-alive", "Content-Length": "0", "Date": "Mon, 30 Apr 2018 07:06:42 GMT", "Expires": "Thu, 01 Jan 1970 00:00:00 GMT", "Location": "https://cm.g.doubleclick.net/pixel?google_nid=ipy&google_cm", "P3P": "CP="NON DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONa HISa TELa OTPa OUR UNRa IND UNI COM NAV INT DEM CNT PRE LOC"", "Pragma": "no-cache", "Server": "nginx/1.10.2", "Set-Cookie": "CMBMP=IWl; Domain=.ipinyou.com; Expires=Thu, 10-May-2018 07:06:42 GMT; Path=/" }, "headersText": "HTTP/1.1 302 FoundrnServer: nginx/1.10.2rnDate: Mon, 30 Apr 2018 07:06:42 GMTrnContent-Length: 0rnConnection: keep-alivernCache-Control: no-cachernPragma: no-cachernExpires: Thu, 01 Jan 1970 00:00:00 GMTrnP3P: CP="NON DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONa HISa TELa OTPa OUR UNRa IND UNI COM NAV INT DEM CNT PRE LOC"rnSet-Cookie: CMBMP=IWl; Domain=.ipinyou.com; Expires=Thu, 10-May-2018 07:06:42 GMT; Path=/rnLocation: https://cm.g.doubleclick.net/pixel?google_nid=ipy&google_cmrnrn", "mimeType": "", "protocol": "http/1.1", "remoteIPAddress": "127.0.0.1", "remotePort": 1086, "requestHeaders": { "Accept": "image/webp,image/apng,image/*,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "keep-alive", "Cookie": "sessionId=I4UF6b1WcgGMC; PYID=I4UF6b1Wcg99; CMTMS=p7Ik3Ve; CMSTMS=p7Ik3Ve; CMPUB=ADV-DefaultAdv; CMBMP=IW2", "Host": "cm.ipinyou.com", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" }, "requestHeadersText": "GET /baidu/cms.gif?baidu_error=1&timestamp=1525072001 HTTP/1.1rnHost: cm.ipinyou.comrnConnection: keep-alivernUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36rnAccept: image/webp,image/apng,image/*,*/*;q=0.8rnAccept-Encoding: gzip, deflate, brrnAccept-Language: zh-CN,zh;q=0.9rnCookie: sessionId=I4UF6b1WcgGMC; PYID=I4UF6b1Wcg99; CMTMS=p7Ik3Ve; CMSTMS=p7Ik3Ve; CMPUB=ADV-DefaultAdv; CMBMP=IW2rn", "securityDetails": { "certificateId": 0, "cipher": "AES_256_GCM", "issuer": "RapidSSL SHA256 CA", "keyExchange": "ECDHE_RSA", "keyExchangeGroup": "P-256", "protocol": "TLS 1.2", "sanList": ["*.ipinyou.com", "ipinyou.com"], "signedCertificateTimestampList": [{ "hashAlgorithm": "SHA-256", "logDescription": "Symantec log", "logId": "DDEB1D2B7A0D4FA6208B81AD8168707E2E8E9D01D55C888D3D11C4CDB6ECBECC", "origin": "Embedded in certificate", "signatureAlgorithm": "ECDSA", "signatureData": "3045022024364934CBC90A8529E327E6EF853E3EF5E48B7F1598414E0F10059DC92685FC022100A74F93A8CF23D6572D7597C072368D69EC43AFB6A9EDAA4B01B43921AADEFDC2", "status": "Verified", "timestamp": 1511173770857.0 }, { "hashAlgorithm": "SHA-256", "logDescription": "Google 'Pilot' log", "logId": "A4B90990B418581487BB13A2CC67700A3C359804F91BDFB8E377CD0EC80DDC10", "origin": "Embedded in certificate", "signatureAlgorithm": "ECDSA", "signatureData": "3046022100F319D0F56F27C82228E2B01934A1C7F46915A1509F094EE91508F08C3B5AE2B2022100B0D94DD6FD00CB435EC33B916B52EC76FE5FFCC5D5BD8CB559248243AEDFE3CE", "status": "Verified", "timestamp": 1511173770923.0 }], "subjectName": "*.ipinyou.com", "validFrom": 1511136000, "validTo": 1547942399 }, "securityState": "secure", "status": 302, "statusText": "Found", "timing": { "connectEnd": 772.852999994939, "connectStart": 0.566999995498918, "dnsEnd": -1, "dnsStart": -1, "proxyEnd": -1, "proxyStart": -1, "pushEnd": 0, "pushStart": 0, "receiveHeadersEnd": 1226.29800000141, "requestTime": 42129.997749, "sendEnd": 773.012999998173, "sendStart": 772.960999995121, "sslEnd": 772.844999999506, "sslStart": 1.62599999748636, "workerReady": -1, "workerStart": -1 }, "url": "https://cm.ipinyou.com/baidu/cms.gif?baidu_error=1&timestamp=1525072001" }, "request": { "headers": { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" }, "initialPriority": "Low", "method": "GET", "mixedContentType": "none", "referrerPolicy": "no-referrer-when-downgrade", "url": "https://cm.g.doubleclick.net/pixel?google_nid=ipy&google_cm" }, "requestId": "20524.247", "timestamp": 42131.225431, "type": "Image", "wallTime": 1525072000.35906 } },    "webview": "28DAFE9FE90E9292F1B8EDB3315608EC" }

          參數說明:

          參數

          類型

          說明

          requestId

          String

          唯一請求ID

          loaderId

          String

          加載ID

          documentURL

          String

          頁面文檔URL

          request

          Request

          請求數據對象

          timestamp

          float

          以過去某個任意時間點為基點,從打開頁面開始,以秒為單位單調遞增的時間戳

          wallTime

          float

          UTC時間

          initiator

          Initiator

          請求初始化對象

          redirectResponse

          Response

          重定向響應對象

          type

          String

          資源類型

          frameId

          String

          FrameID

          hasUserGesture

          boolean

          Whether the request is initiated by a user gesture. Defaults to false.

          其中, Request對象:

          參數

          類型

          說明

          url

          String

          請求url

          method

          String

          HTTP請求類型

          headers

          Object

          請求頭信息

          postData

          String

          Post請求數據

          hasPostData

          boolean

          如果是Post請求,則為true

          mixedContentType

          String

          是否存在混淆內容問題:blockable, optionally-blockable, none.

          initialPriority

          String

          資源加載優先級:VeryLow, Low, Medium, High, VeryHigh.

          referrerPolicy

          String

          跨域策略:no-referrer-when-downgrade, no-referrer, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin

          isLinkPreload

          boolean

          是否通過預加載方式加載

          Response對象:

          參數

          類型

          說明

          url

          String

          請求url

          status

          int

          響應狀態碼

          statusText

          String

          狀態碼內容

          headers

          Object

          響應頭部,json格式

          headersText

          String

          響應頭部,文本格式

          mimeType

          String

          Resource mimeType

          requestHeaders

          Obeject

          請求頭部,json格式

          requestHeadersText

          String

          請求頭部,文本格式

          connectionReused

          boolean

          連接是否被復用

          connectionId

          long

          物理連接ID

          remoteIPAddress

          String

          Remote IP address

          remotePort

          int

          Remote port

          fromDiskCache

          boolean

          是否直接從瀏覽器緩存獲取資源

          fromServiceWorker

          boolean

          Specifies that the request was served from the ServiceWorker

          encodedDataLength

          long

          響應字節數

          timing

          ResourceTiming

          ResourceTiming對象

          protocol

          String

          協議

          securityState

          String

          Security state of the request resource:unknown, neutral, insecure, secure, info

          securityDetails

          SecurityDetails

          Security details for the request

          ResourceTiming對象:

          參數

          類型

          說明

          requestTime

          float

          時間基線

          proxyStart

          float

          Started resolving proxy.

          proxyEnd

          float

          Finished resolving proxy.

          dnsStart

          float

          Started DNS address resolve.

          dnsEnd

          float

          Finished DNS address resolve.

          connectStart

          float

          Started connecting to the remote host.

          connectEnd

          float

          Connected to the remote host.

          sslStart

          float

          Started SSL handshake.

          sslEnd

          float

          Finished SSL handshake.

          workerStart

          float

          Started running ServiceWorker.

          workerReady

          float

          Finished Starting ServiceWorker.

          sendStart

          float

          Started sending request.

          sendEnd

          float

          Finished sending request.

          pushStart

          float

          Time the server started pushing request.

          pushEnd

          float

          Time the server finished pushing request.

          receiveHeadersEnd

          float

          Finished receiving response headers.

          Network.responseReceived

          當HTTP響應可用時觸發,其Json格式為:

          {    "message": { "method": "Network.responseReceived", "params": { "frameId": "28DAFE9FE90E9292F1B8EDB3315608EC", "loaderId": "44DBCD0BEBFCEE5AED6388366BCB719B", "requestId": "20524.277", "response": { "connectionId": 468, "connectionReused": true, "encodedDataLength": 439, "fromDiskCache": false, "fromServiceWorker": false, "headers": { "Cache-Control": "no-cache, max-age=0, must-revalidate", "Connection": "keep-alive", "Content-Length": "43", "Content-Type": "image/gif", "Date": "Mon, 30 Apr 2018 07:06:42 GMT", "Expires": "Fri, 01 Jan 1980 00:00:00 GMT", "Last-Modified": "Mon, 28 Sep 1970 06:00:00 GMT", "Pragma": "no-cache", "Server": "nginx/1.6.3", "X-Dscp-Value": "0", "X-Via": "1.1 dxun38:1 (Cdn Cache Server V2.0), 1.1 shb115:4 (Cdn Cache Server V2.0), 1.1 ls10:0 (Cdn Cache Server V2.0)" }, "headersText": "HTTP/1.1 200 OKrnDate: Mon, 30 Apr 2018 07:06:42 GMTrnServer: nginx/1.6.3rnContent-Type: image/gifrnContent-Length: 43rnLast-Modified: Mon, 28 Sep 1970 06:00:00 GMTrnExpires: Fri, 01 Jan 1980 00:00:00 GMTrnPragma: no-cachernCache-Control: no-cache, max-age=0, must-revalidaternX-Dscp-Value: 0rnX-Via: 1.1 dxun38:1 (Cdn Cache Server V2.0), 1.1 shb115:4 (Cdn Cache Server V2.0), 1.1 ls10:0 (Cdn Cache Server V2.0)rnConnection: keep-alivernrn", "mimeType": "image/gif", "protocol": "http/1.1", "remoteIPAddress": "127.0.0.1", "remotePort": 1086, "requestHeaders": { "Accept": "image/webp,image/apng,image/*,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "keep-alive", "Cookie": "_snstyxuid=ADFD3F4299718846; _snvd=152507199416958111", "Host": "sa.suning.cn", "Referer": "https://www.suning.com/", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" }, "requestHeadersText": "GET /ajaxSiteExpro.gif?oId=152507199969277498&pvId=152507199147454663&expoInfo=index3_homepage1_32618013033_word03,index3_homepage1_32618013033_word04,index3_homepage1_newUser_tankuang&expoType=1&pageUrl=https://www.suning.com/&visitorId=&loginUserName=&memberID=-&sessionId=&pageType=web&hidUrlPattern=&iId=log_1525071999692 HTTP/1.1rnHost: sa.suning.cnrnConnection: keep-alivernUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36rnAccept: image/webp,image/apng,image/*,*/*;q=0.8rnReferer: https://www.suning.com/rnAccept-Encoding: gzip, deflate, brrnAccept-Language: zh-CN,zh;q=0.9rnCookie: _snstyxuid=ADFD3F4299718846; _snvd=152507199416958111rn", "securityDetails": { "certificateId": 0, "cipher": "AES_256_GCM", "issuer": "WoSign OV SSL CA", "keyExchange": "ECDHE_RSA", "keyExchangeGroup": "P-256", "protocol": "TLS 1.2", "sanList": ["*.suning.cn", "suning.cn"], "signedCertificateTimestampList": [], "subjectName": "*.suning.cn", "validFrom": 1479721356, "validTo": 1574329356 }, "securityState": "secure", "status": 200, "statusText": "OK", "timing": { "connectEnd": -1, "connectStart": -1, "dnsEnd": -1, "dnsStart": -1, "proxyEnd": -1, "proxyStart": -1, "pushEnd": 0, "pushStart": 0, "receiveHeadersEnd": 656.157999997959, "requestTime": 42130.56839, "sendEnd": 1.03800000215415, "sendStart": 0.979999997070991, "sslEnd": -1, "sslStart": -1, "workerReady": -1, "workerStart": -1 }, "url": "https://sa.suning.cn/ajaxSiteExpro.gif?oId=152507199969277498&pvId=152507199147454663&expoInfo=index3_homepage1_32618013033_word03,index3_homepage1_32618013033_word04,index3_homepage1_newUser_tankuang&expoType=1&pageUrl=https://www.suning.com/&visitorId=&loginUserName=&memberID=-&sessionId=&pageType=web&hidUrlPattern=&iId=log_1525071999692" }, "timestamp": 42131.22618, "type": "Image" } },    "webview": "28DAFE9FE90E9292F1B8EDB3315608EC" }

          參數說明:

          參數

          類型

          說明

          requestId

          String

          唯一請求ID

          timestamp

          float

          以過去某個任意時間點為基點,從打開頁面開始,以秒為單位單調遞增的時間戳

          type

          String

          資源類型

          response

          Response

          響應對象

          frameId

          String

          FrameID

          應用場景:根據Response可以快速識別請求的各種異常狀態碼(5XX、4XX),以及時間的分布。

          Network.loadingFailed

          當HTTP請求無法加載時觸發,其Json格式為:

          {    "message": { "method": "Network.loadingFailed", "params": { "canceled": true, "errorText": "net::ERR_ABORTED", "requestId": "20524.271", "timestamp": 42130.877864, "type": "Image" } },    "webview": "28DAFE9FE90E9292F1B8EDB3315608EC" }

          參數說明:

          參數

          類型

          說明

          requestId

          String

          唯一請求ID

          loaderId

          String

          加載ID

          timestamp

          float

          以過去某個任意時間點為基點,從打開頁面開始,以秒為單位單調遞增的時間戳

          type

          String

          資源類型

          errorText

          String

          錯誤原因提示

          canceled

          boolean

          如果請求加載被取消,則為true

          blockedReason

          String

          請求被阻塞的原因

          應用場景:我們可以通過loadingFailed和requestWillBeSent確定哪些請求加載失敗。

          Network.loadingFinished

          當HTTP請求完成加載時觸發,其Json格式為:

          {    "message": { "method": "Network.loadingFinished", "params": { "blockedCrossSiteDocument": false, "encodedDataLength": 327, "requestId": "20524.262", "timestamp": 42130.87542 } },    "webview": "28DAFE9FE90E9292F1B8EDB3315608EC" }

          參數說明:

          參數

          類型

          說明

          requestId

          String

          唯一請求ID

          timestamp

          float

          以過去某個任意時間點為基點,從打開頁面開始,以秒為單位單調遞增的時間戳

          encodedDataLength

          long

          響應字節數

          blockedCrossSiteDocument

          boolean

          如果由于跨域阻塞了響應,則為true

          應用場景:根據encodedDataLength,計算響應的最終大小。

          針對requestWillBeSent、responseReceived、loadingFailed和loadingFinished四種對象,Java構建Model如下所示:

          2.3.2、Page

          Page中我們用到的事件主要是domContentEventFired和loadEventFired兩種:

          Page.domContentEventFired

          頁面Dom內容加載完成時間。

          {    "message": { "method": "Page.domContentEventFired", "params": { "timestamp": 42124.003701 } },    "webview": "28DAFE9FE90E9292F1B8EDB3315608EC" }

          Page.loadEventFired

          頁面加載完成時間。

          {    "message": { "method": "Page.loadEventFired", "params": { "timestamp": 42133.108263 } },    "webview": "28DAFE9FE90E9292F1B8EDB3315608EC" }

          以上,我們可以根據ChromeDriver來完成對頁面加載性能分析的自動化測試了。

          3、持久化ChromeDriverService的使用

          本節介紹ChromeDriverService,這完全是出于提高測試性能的考慮。我們知道每次創建一個ChromeDriver,完成測試以后再釋放掉這個對象,等下次來了一個新的測試,仍要再新建一個對象,如此反復。這相當于每次都打開瀏覽器,再關閉瀏覽器,再打開瀏覽器。這種實現方式并不利于高并發的測試場景。 我們希望如Java的池化設計思想一樣,初始化生成多個持久化的瀏覽器對象,后面每次測試都用這些瀏覽器對象進行,這樣會極大提升測試性能(想想看,避免了往復創建和關閉進程的過程啊!)。因此引入ChromeDriverService,ChromeDriverService是一個管理ChromeDriver server的的持久化實例:

          The purpose of ChromeDriverService is to manage a persistent instance of the ChromeDriver server. Standard practice is to use the ChromeDriver class or the Selenium standalone server to obtain Chrome driver instances, but this practice sacrifices performance for convenience. In this scenario, each driver instance is associated with its own instance of the ChromeDriver server, which gets launched when the driver is requested and terminated when the driver exits. This per-instance server management adds overhead to test execution, both in terms of run-time and resource utilization. Using ChromeDriverService, this overhead can be reduced to a minimum by enabling your test framework to launch a server instance at the start of the test suite and shut it down when the suite finishes. An example of this approach can be found on the ChromeDriver Getting started page under the heading Controlling ChromeDriver’s lifetime.

          其使用可以參考:Java Code Examples for org.openqa.selenium.chrome.ChromeDriverService。 下面是我實現的一個Demo,我生成了3個線程分別持有一個ChromeDrvierService對象,相當于每個線程管理一個瀏覽器進程。采用阻塞隊列BQ來實現生產者-消費者模式,當隊列中有任務時,會分配給一個線程去進行測試。當隊列中無任務時,也不會銷毀ChromeDrvierService。阻塞隊列的深度和線程池的大小可以根據服務器性能動態調整。

          package com.suning.webdrivertest.chromedemo;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.chrome.ChromeDriverService;import org.openqa.selenium.logging.LogEntry;import org.openqa.selenium.logging.LogType;import org.openqa.selenium.logging.LoggingPreferences;import org.openqa.selenium.remote.CapabilityType;import org.openqa.selenium.remote.DesiredCapabilities;import java.io.File;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.CountDownLatch;import java.util.logging.Level;/** * Created by zhuyiquan90 on 2018/1/9. */public class ChromeDriverDemo2 {        // 阻塞隊列長度    private static final int BLOCK_QUEUE_SIZE = 100;    // 瀏覽器driver線程數    private static final int THREAD_SIZE = 3;    // chromedriver地址    private static final String chromedriverPath = "opt/drivers/chromedriver";    private static final BlockingQueue<String> reqQuene = new ArrayBlockingQueue<String>(BLOCK_QUEUE_SIZE);    static class DriverRunnable implements Runnable {        private WebDriver driver;        CountDownLatch latch;        public DriverRunnable(CountDownLatch latch) {            ChromeDriverService chromeDriverService = new ChromeDriverService.Builder()                    .usingDriverExecutable(new File(chromedriverPath))                    .usingAnyFreePort()                    .build();            DesiredCapabilities cap = DesiredCapabilities.chrome();            LoggingPreferences logPrefs = new LoggingPreferences();            logPrefs.enable(LogType.PERFORMANCE, Level.ALL);            cap.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);            driver = new ChromeDriver(chromeDriverService, cap);            this.latch = latch;        }        public void run() {            while (true) {                try {                    driver.get(reqQuene.take());                    for (LogEntry entry : driver.manage().logs().get(LogType.PERFORMANCE)) {                        System.out.println(Thread.currentThread().getName() + entry.toString());                    }                    latch.countDown();                } catch (Exception e) {                    e.printStackTrace();                }            }        }    }    public static void main(String[] args) throws InterruptedException {        CountDownLatch latch = new CountDownLatch(10);        for (int i = 0; i < THREAD_SIZE; i++) {            Thread driverThread = new Thread(new DriverRunnable(latch), "driverThread" + i);            driverThread.start();        }        for (int i = 0; i < 10; i++) {            reqQuene.put("https://www.suning.com");        }        // 使用latch.await()的目的僅僅是為Demo的輸出順序直觀,沒有其他作用        // 可以去掉latch        latch.await();    }}

          4、一個應用實例的實現

          下面是本文的最后一部分,我想通過一個相對完整的應用實例來收官。這個實例來自于真實的應用場景,需求是采集每個頁面的如下數據: 首屏性能,包括:

          • 首屏請求數
          • 首屏大小
          • 首屏DOM總數
          • 首屏DOM加載完成時間
          • 首屏完全加載完成時間
          • 首屏異常響應
          • 首屏失敗響應
          • 首屏慢響應

          以及全頁面性能,即打開頁面后完成對整個頁面的瀏覽,包括:

          • 全頁面請求數
          • 全頁面大小
          • 全頁面DOM總數
          • 全頁面DOM加載完成時間
          • 全頁面完全加載完成時間
          • 全頁面異常響應
          • 全頁面失敗響應
          • 全頁面慢響應

          最終實現如下:

          package com.suning.webdrivertest.chrome;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.suning.webdrivertest.networkdto.NetworkLoadingFailedDTO;import com.suning.webdrivertest.networkdto.NetworkLoadingFinishedDTO;import com.suning.webdrivertest.networkdto.NetworkRequestWillBeSentDTO;import com.suning.webdrivertest.networkdto.NetworkResponseReceivedDTO;import com.suning.webdrivertest.performancedto.*;import org.openqa.selenium.JavascriptExecutor;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.chrome.ChromeDriverService;import org.openqa.selenium.chrome.ChromeOptions;import org.openqa.selenium.logging.LogEntry;import org.openqa.selenium.logging.LogType;import org.openqa.selenium.logging.LoggingPreferences;import org.openqa.selenium.remote.CapabilityType;import org.openqa.selenium.remote.DesiredCapabilities;import java.text.MessageFormat;import java.util.ArrayList;import java.util.List;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.logging.Level;/** * Created by zhuyiquan90 on 2018/1/10. */public class ChromeTest {        // 阻塞隊列長度    private static final int BLOCK_QUEUE_SIZE = 100;    // 瀏覽器driver線程數    private static final int THREAD_SIZE = 1;    // Fired when page is about to send HTTP request.    public static final String NETWORK_REQUEST_WILL_BE_SENT = "Network.requestWillBeSent";    // Fired when HTTP response is available.    public static final String NETWORK_RESPONSE_RECEIVED = "Network.responseReceived";    // Fired when HTTP request has finished loading.    public static final String NETWORK_LOADING_FAILED = "Network.loadingFailed";    // Fired when HTTP request has failed to load.    public static final String NETWORK_LOADING_FINISHED = "Network.loadingFinished";    // DOM Length JS    public static final String JS_DOM_LENGTH = "return document.getElementsByTagName('*').length";    // ScrollingTop JS    public static final String JS_SCROLLINGTOP = "return $(window).scrollTop( {0} * 1000)";    // Scrolling Y JS    public static final String JS_SCROLLINGY = "return window.scrollY";    // Performance Timing JS    public static final String JS_PERFORMANCE_TIMING = "return performance.timing";    private static final BlockingQueue<String> reqQuene = new ArrayBlockingQueue<String>(BLOCK_QUEUE_SIZE);    static class DriverRunnable implements Runnable {        private WebDriver driver;        public DriverRunnable() {            System.setProperty(ChromeDriverService.CHROME_DRIVER_LOG_PROPERTY,                    System.getProperty("user.dir") + "/target/chromedriver.log");            System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY,                    System.getProperty("user.dir") + "/drivers/chromedriver");            ChromeDriverService chromeDriverService = new ChromeDriverService.Builder()                    .withVerbose(true)                    .usingAnyFreePort()                    .build();            ChromeOptions options = new ChromeOptions();            // options.addArguments("--headless");            options.addArguments("--window-size=1980,1000");            options.addArguments("--disable-web-security");            // options.addArguments("--start-fullscreen");            // options.addArguments("--screenshot");            // options.addArguments("--golden-screenshots-dir=" + chromedriverPath);            DesiredCapabilities cap = DesiredCapabilities.chrome();            LoggingPreferences logPrefs = new LoggingPreferences();            logPrefs.enable(LogType.PERFORMANCE, Level.ALL);            cap.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);            cap.setCapability(ChromeOptions.CAPABILITY, options);            driver = new ChromeDriver(chromeDriverService, cap);        }        public void run() {            while (true) {                try {                    String url = reqQuene.take();                    TotalPerformanceDTO totalPerformance = new TotalPerformanceDTO();                    totalPerformance.setFirstScreenPerformance(                            detectFirstScreenPerformance(url, driver));                    totalPerformance.setFullPagePerformance(                            detectFullPagePerformance(url, driver));                    System.out.println(totalPerformance.toString());                    // 滑動頁面,直到頁面底部// long scrollStart = 0, scrollEnd = 1;// int i = 1;// while (scrollStart != scrollEnd) {    // scrollStart = (Long) js.executeScript("return window.scrollY");// String scrollTo = "return $(window).scrollTop(" + i++ + " * 1000)";// js.executeScript(scrollTo);// Thread.sleep(200);// scrollEnd = (Long) js.executeScript("return window.scrollY");// System.out.println(scrollTo + ":" + scrollEnd);// }// System.out.println(Thread.currentThread().getName() + ": " +// js.executeScript("return document.getElementsByTagName('*').length"));// for (LogEntry entry : driver.manage().logs().get(LogType.PERFORMANCE)) {    // System.out.println(Thread.currentThread().getName() + entry.getMessage());// }                } catch (Exception e) {                    e.printStackTrace();                }            }        }    }    /** * 首屏數據統計 * * @param url * @param driver * @return */    private static FirstScreenPerformanceDTO detectFirstScreenPerformance(String url, WebDriver driver) {        driver.get(url);        // js操作對象        JavascriptExecutor js = (JavascriptExecutor) driver;        FirstScreenPerformanceDTO firstScreenPerformance = new FirstScreenPerformanceDTO();        List<NetworkRequestWillBeSentDTO> firstscreenRequestList =                new ArrayList<NetworkRequestWillBeSentDTO>();        List<NetworkResponseReceivedDTO> firstscreenResponseList =                new ArrayList<NetworkResponseReceivedDTO>();        List<NetworkLoadingFailedDTO> firstscreenFailList =                new ArrayList<NetworkLoadingFailedDTO>();        List<NetworkLoadingFinishedDTO> firstscreenFinishedList =                new ArrayList<NetworkLoadingFinishedDTO>();        int pageRequestNum = 0;        double pageSize = 0.0;        for (LogEntry entry : driver.manage().logs().get(LogType.PERFORMANCE)) {            JSONObject jsonObj = JSON.parseobject(entry.getMessage()).getJSONObject("message");            String method = jsonObj.getString("method");            String params = jsonObj.getString("params");            if (method.equals(NETWORK_REQUEST_WILL_BE_SENT)) {                NetworkRequestWillBeSentDTO request = JSON.parseObject(                        params, NetworkRequestWillBeSentDTO.class);                pageRequestNum++;// System.out.println(method + ":" + request.toString());                firstscreenRequestList.add(request);            } else if (method.equals(NETWORK_RESPONSE_RECEIVED)) {                NetworkResponseReceivedDTO response = JSON.parseObject(                        params, NetworkResponseReceivedDTO.class);// System.out.println(method + ":" + response.getResponse().getUrl());                firstscreenResponseList.add(response);            } else if (method.equals(NETWORK_LOADING_FINISHED)) {                NetworkLoadingFinishedDTO finished = JSON.parseObject(                        params, NetworkLoadingFinishedDTO.class);                pageSize += finished.getEncodedDataLength();// System.out.println(method + ":" + finished.toString());                firstscreenFinishedList.add(finished);            } else if (method.equals(NETWORK_LOADING_FAILED)) {                NetworkLoadingFailedDTO failed = JSON.parseObject(                        params, NetworkLoadingFailedDTO.class);// System.out.println(method + ":" + failed.toString());                firstscreenFailList.add(failed);            }        }        // 獲取首屏DOM數        firstScreenPerformance.setPageDomNum(executeDomLengthJS(js));        // 獲取首屏DOM加載完成時間 和 首屏完全加載完成時間        PerformanceTimingDTO performanceTiming = executePerformanceTimingJS(js);        firstScreenPerformance.setDomContentLoadedCost(                performanceTiming.getDomContentLoadedEventEnd() - performanceTiming.getConnectStart());        firstScreenPerformance.setLoadEventCost(                performanceTiming.getLoadEventEnd() - performanceTiming.getConnectStart());        // 獲取首屏大小        firstScreenPerformance.setPageSize(pageSize / (1000 * 1000));        // 獲取首屏請求數        firstScreenPerformance.setPageRequestNum(pageRequestNum);        System.out.println("頁面" + url + ":首屏請求數:"                + firstScreenPerformance.getPageRequestNum() + ", 首屏大小:"                + firstScreenPerformance.getPageSize() + "MB, 首屏DOM總數:"                + firstScreenPerformance.getPageDomNum() + ", 首屏DOM加載完成時間:"                + firstScreenPerformance.getDomContentLoadedCost() + "ms, 首屏完全加載完成時間:"                + firstScreenPerformance.getLoadEventCost() + "ms");        // 分析異常響應        PageErrorsDTO pageErrorsDTO = new PageErrorsDTO();        pageErrorsDTO.setCodeErrorResponseList(                analysisCodeErrorResponse(firstscreenResponseList));        if (!pageErrorsDTO.getCodeErrorResponseList().isempty()) {            System.out.println("首屏異常響應:");            for (int i = 0; i < pageErrorsDTO.getCodeErrorResponseList().size(); i++) {                System.out.println(pageErrorsDTO.                        getCodeErrorResponseList().get(i).getUrl() + ": " + pageErrorsDTO.                        getCodeErrorResponseList().get(i).getStatus());            }        }        // 分析失敗響應        pageErrorsDTO.setFailResponseList(                analysisFailResponse(firstscreenFailList, firstscreenRequestList));        if (!pageErrorsDTO.getFailResponseList().isEmpty()) {            System.out.println("首屏失敗響應:");            for (int i = 0; i < pageErrorsDTO.getFailResponseList().size(); i++) {                System.out.println(pageErrorsDTO.                        getFailResponseList().get(i).getUrl() + ": " + pageErrorsDTO.                        getFailResponseList().get(i).getErrorText() + " " + pageErrorsDTO.                        getFailResponseList().get(i).getBlockedReason());            }        }        // 分析慢響應        pageErrorsDTO.setSlowReponseList(                analysisSlowResponse(firstscreenRequestList, firstscreenFinishedList, 3.0));        if (!pageErrorsDTO.getSlowReponseList().isEmpty()) {            System.out.println("首屏慢響應:");            for (int i = 0; i < pageErrorsDTO.getSlowReponseList().size(); i++) {                System.out.println(pageErrorsDTO.                        getSlowReponseList().get(i).getUrl() + ": " + pageErrorsDTO.                        getSlowReponseList().get(i).getCost());            }        }        firstScreenPerformance.setPageErrorsDTO(pageErrorsDTO);        return firstScreenPerformance;    }    /** * 全頁面數據統計 * * @param url * @param driver * @return */    private static FullPagePerformanceDTO detectFullPagePerformance(String url, WebDriver driver) {        driver.get(url);        // js操作對象        JavascriptExecutor js = (JavascriptExecutor) driver;        FullPagePerformanceDTO fullPagePerformance = new FullPagePerformanceDTO();        // 滾動到頁面底部        scrollToBottom(js);        List<NetworkRequestWillBeSentDTO> fullPageRequestList =                new ArrayList<NetworkRequestWillBeSentDTO>();        List<NetworkResponseReceivedDTO> fullPageResponseList =                new ArrayList<NetworkResponseReceivedDTO>();        List<NetworkLoadingFailedDTO> fullPageFailList =                new ArrayList<NetworkLoadingFailedDTO>();        List<NetworkLoadingFinishedDTO> fullPageFinishedList =                new ArrayList<NetworkLoadingFinishedDTO>();        int pageRequestNum = 0;        double pageSize = 0.0;        for (LogEntry entry : driver.manage().logs().get(LogType.PERFORMANCE)) {            JSONObject jsonObj = JSON.parseObject(entry.getMessage()).getJSONObject("message");            String method = jsonObj.getString("method");            String params = jsonObj.getString("params");            if (method.equals(NETWORK_REQUEST_WILL_BE_SENT)) {                NetworkRequestWillBeSentDTO request = JSON.parseObject(                        params, NetworkRequestWillBeSentDTO.class);                pageRequestNum++;// System.out.println(method + ":" + request.toString());                fullPageRequestList.add(request);            } else if (method.equals(NETWORK_RESPONSE_RECEIVED)) {                NetworkResponseReceivedDTO response = JSON.parseObject(                        params, NetworkResponseReceivedDTO.class);// System.out.println(method + ":" + response.getResponse().getUrl());                fullPageResponseList.add(response);            } else if (method.equals(NETWORK_LOADING_FINISHED)) {                NetworkLoadingFinishedDTO finished = JSON.parseObject(                        params, NetworkLoadingFinishedDTO.class);                pageSize += finished.getEncodedDataLength();// System.out.println(method + ":" + finished.toString());                fullPageFinishedList.add(finished);            } else if (method.equals(NETWORK_LOADING_FAILED)) {                NetworkLoadingFailedDTO failed = JSON.parseObject(                        params, NetworkLoadingFailedDTO.class);// System.out.println(method + ":" + failed.toString());                fullPageFailList.add(failed);            }        }        // 獲取全頁面DOM數        fullPagePerformance.setPageDomNum(executeDomLengthJS(js));        // 獲取全頁面DOM加載完成時間 和 首屏完全加載完成時間        PerformanceTimingDTO performanceTiming = executePerformanceTimingJS(js);        fullPagePerformance.setDomContentLoadedCost(                performanceTiming.getDomContentLoadedEventEnd() - performanceTiming.getConnectStart());        fullPagePerformance.setLoadEventCost(                performanceTiming.getLoadEventEnd() - performanceTiming.getConnectStart());        // 獲取全頁面大小        fullPagePerformance.setPageSize(pageSize / (1000 * 1000));        // 獲取全頁面請求數        fullPagePerformance.setPageRequestNum(pageRequestNum);        System.out.println("頁面" + url + ":全頁面請求數:"                + fullPagePerformance.getPageRequestNum() + ", 全頁面大小:"                + fullPagePerformance.getPageSize() + "MB, 全頁面DOM總數:"                + fullPagePerformance.getPageDomNum() + ", 全頁面DOM加載完成時間:"                + fullPagePerformance.getDomContentLoadedCost() + "ms, 全頁面完全加載完成時間:"                + fullPagePerformance.getLoadEventCost() + "ms");        // 分析異常響應        PageErrorsDTO pageErrorsDTO = new PageErrorsDTO();        pageErrorsDTO.setCodeErrorResponseList(                analysisCodeErrorResponse(fullPageResponseList));        if (!pageErrorsDTO.getCodeErrorResponseList().isEmpty()) {            System.out.println("全頁面異常響應:");            for (int i = 0; i < pageErrorsDTO.getCodeErrorResponseList().size(); i++) {                System.out.println(pageErrorsDTO.                        getCodeErrorResponseList().get(i).getUrl() + ": " + pageErrorsDTO.                        getCodeErrorResponseList().get(i).getStatus());            }        }        // 分析失敗響應        pageErrorsDTO.setFailResponseList(                analysisFailResponse(fullPageFailList, fullPageRequestList));        if (!pageErrorsDTO.getFailResponseList().isEmpty()) {            System.out.println("全頁面失敗響應:");            for (int i = 0; i < pageErrorsDTO.getFailResponseList().size(); i++) {                System.out.println(pageErrorsDTO.                        getFailResponseList().get(i).getUrl() + ": " + pageErrorsDTO.                        getFailResponseList().get(i).getErrorText() + " " + pageErrorsDTO.                        getFailResponseList().get(i).getBlockedReason());            }        }        // 分析慢響應        pageErrorsDTO.setSlowReponseList(                analysisSlowResponse(fullPageRequestList, fullPageFinishedList, 3.0));        if (!pageErrorsDTO.getSlowReponseList().isEmpty()) {            System.out.println("全頁面慢響應:");            for (int i = 0; i < pageErrorsDTO.getSlowReponseList().size(); i++) {                System.out.println(pageErrorsDTO.                        getSlowReponseList().get(i).getUrl() + ": " + pageErrorsDTO.                        getSlowReponseList().get(i).getCost());            }        }        fullPagePerformance.setPageErrorsDTO(pageErrorsDTO);        return fullPagePerformance;    }    /** * 滾動到頁面底部 * * @param js * @return */    private static long scrollToBottom(JavascriptExecutor js) {        long scrollStart = 0, scrollEnd = 1;        int i = 1;        while (scrollStart != scrollEnd) {            scrollStart = (Long) js.executeScript(JS_SCROLLINGY);            String scrollTo = MessageFormat.format(JS_SCROLLINGTOP, i++);            System.out.println(scrollTo);            js.executeScript(scrollTo);            try {                Thread.sleep(200);            } catch (InterruptedException e) {                e.printStackTrace();            }            scrollEnd = (Long) js.executeScript(JS_SCROLLINGY);            // System.out.println(scrollTo + ":" + scrollEnd);        }        return scrollEnd;    }    /** * 執行js,獲取頁面DOM數 * * @param js * @return */    private static long executeDomLengthJS(JavascriptExecutor js) {        return (Long) js.executeScript(JS_DOM_LENGTH);    }    /** * 執行js,獲取Performance Timing * * @param js * @return */    private static PerformanceTimingDTO executePerformanceTimingJS(JavascriptExecutor js) {        String performance = js.executeScript(JS_PERFORMANCE_TIMING).toString();        performance = performance.replace("unloadEventEnd=", ""unloadEventEnd":")                .replace("responseEnd=", ""responseEnd":")                .replace("responseStart=", ""responseStart":")                .replace("domInteractive=", ""domInteractive":")                .replace("domainLookupEnd=", ""domainLookupEnd":")                .replace("unloadEventStart=", ""unloadEventStart":")                .replace("domComplete=", ""domComplete":")                .replace("domContentLoadedEventStart=", ""domContentLoadedEventStart":")                .replace("domainLookupStart=", ""domainLookupStart":")                .replace("redirectEnd=", ""redirectEnd":")                .replace("redirectStart=", ""redirectStart":")                .replace("connectEnd=", ""connectEnd":")                .replace("toJSON={},", "")                .replace("connectStart=", ""connectStart":")                .replace("loadEventStart=", ""loadEventStart":")                .replace("navigationStart=", ""navigationStart":")                .replace("requestStart=", ""requestStart":")                .replace("secureConnectionStart=", ""secureConnectionStart":")                .replace("fetchStart=", ""fetchStart":")                .replace("domContentLoadedEventEnd=", ""domContentLoadedEventEnd":")                .replace("domLoading=", ""domLoading":")                .replace("loadEventEnd=", ""loadEventEnd":");// System.out.println(performance);        return JSON.parseObject(                performance, PerformanceTimingDTO.class);    }    /** * 分析異常狀態碼的響應 * * @param networkResponseReceivedList * @return */    private static List<CodeErrorResponseDTO> analysisCodeErrorResponse    (List<NetworkResponseReceivedDTO> networkResponseReceivedList) {        List<CodeErrorResponseDTO> codeErrorResponseList = new ArrayList<CodeErrorResponseDTO>();        for (NetworkResponseReceivedDTO r : networkResponseReceivedList) {            if (r.getResponse().getStatus() >= 400                    && r.getResponse().getStatus() <= 599) {                CodeErrorResponseDTO codeErrorResponseDTO = new CodeErrorResponseDTO();                codeErrorResponseDTO.setUrl(r.getResponse().getUrl());                codeErrorResponseDTO.setStatus(r.getResponse().getStatus());                System.out.println(r.toString());                codeErrorResponseList.add(codeErrorResponseDTO);            }        }        return codeErrorResponseList;    }    /** * 分析失敗的響應 * * @param networkLoadingFailedList * @param networkRequestWillBeSentList * @return */    private static List<FailResponseDTO> analysisFailResponse(            List<NetworkLoadingFailedDTO> networkLoadingFailedList,            List<NetworkRequestWillBeSentDTO> networkRequestWillBeSentList) {        List<FailResponseDTO> failResponseList = new ArrayList<FailResponseDTO>();        for (NetworkLoadingFailedDTO f : networkLoadingFailedList) {            for (NetworkRequestWillBeSentDTO r : networkRequestWillBeSentList) {                if (f.getRequestId().equals(r.getRequestId())) {                    FailResponseDTO failResponseDTO = new FailResponseDTO();                    failResponseDTO.setUrl(r.getRequest().getUrl());                    failResponseDTO.setErrorText(f.getErrorText());                    failResponseDTO.setBlockedReason(f.getBlockedReason());                    failResponseList.add(failResponseDTO);                }            }        }        return failResponseList;    }    /** * 分析慢響應,單位s * * @param networkRequestWillBeSentList * @param networkLoadingFinishedList * @param slowThreshold * @return */    private static List<SlowReponseDTO> analysisSlowResponse(            List<NetworkRequestWillBeSentDTO> networkRequestWillBeSentList,            List<NetworkLoadingFinishedDTO> networkLoadingFinishedList,            double slowThreshold) {        List<SlowReponseDTO> slowReponseList = new ArrayList<SlowReponseDTO>();        for (NetworkRequestWillBeSentDTO r : networkRequestWillBeSentList) {            for (NetworkLoadingFinishedDTO f : networkLoadingFinishedList) {                if (r.getRequestId().equals(f.getRequestId())) {                    double cost = f.getTimestamp() - r.getTimestamp();                    if (cost >= slowThreshold) {                        SlowReponseDTO slowReponseDTO = new SlowReponseDTO();                        slowReponseDTO.setUrl(r.getRequest().getUrl());                        slowReponseDTO.setCost(cost);                        slowReponseList.add(slowReponseDTO);                    }                }            }        }        return slowReponseList;    }    public static void main(String[] args) throws InterruptedException {        for (int i = 0; i < THREAD_SIZE; i++) {            Thread driverThread = new Thread(new DriverRunnable(), "driverThread" + i);            driverThread.start();        }        for (int i = 0; i < 1; i++) {            reqQuene.put("https://www.suning.com");        }    }}

          158899.html

          網絡推廣與網站優化公司(網絡優化與推廣專家)作為數字營銷領域的核心服務提供方,其價值在于通過技術手段與策略規劃幫助企業提升線上曝光度、用戶轉化率及品牌影響力。這...

          在當今數字化時代,公司網站已成為企業展示形象、傳遞信息和開展業務的重要平臺。然而,對于許多公司來說,網站建設的價格是一個關鍵考量因素。本文將圍繞“公司網站建設價...

          在當今的數字化時代,企業網站已成為企業展示形象、吸引客戶和開展業務的重要平臺。然而,對于許多中小企業來說,高昂的網站建設費用可能會成為其發展的瓶頸。幸運的是,隨...

          mdf文件是什么格式文件?打開方法?MDF文件是Microsoft SQL server使用的主要數據庫文件格式。企業數據庫程序用于安裝SQL server和相關附加組件的數據庫文件。用戶還可以創建自定義MDF文件。所以您可以使用Microsoft SQL Server軟件打開MDF文件。以下是具體的演示步驟:1。打開Microsoft SQL Server軟件后,右鍵單擊Microsoft SQ...

          北京到成都動車最快幾小時?北京到成都有最快的高鐵,G309到成都東,8:23到2:09。運行14小時46分鐘,沒有火車。Z91小時11分,11336028是8:39,價格是高鐵的一半。北京復興號動車到成都途經哪些站?答:目前北京到成都的高鐵有5趟,??空军c有北京西、石家莊站、保定站、涿州東站、邢臺站、邯鄲東站、安陽東站、鶴壁東站、新鄉東站、澠池南站、鄭州東站、洛陽龍門站、靈寶西站、洋縣西站、平頂山...

          如何統計excel表格每人每月出勤天數?在win7中,以excel 2007為例,可以參考以下步驟在excel表格中統計每人每月的出勤天數:1.首先,點擊Excel軟件,打開如圖所示的Exc考勤表中代表出勤半天,那么如何讓在表格最后邊,自動生成出勤天數?用COUNTIF函數統計的個數就可以了。例:COUNTIF(A1:A30,)。怎么用excel計算考勤?如:遲到時間、早退時間、加班時間?在E2中...

          TOP
          国产初高中生视频在线观看|亚洲一区中文|久久亚洲欧美国产精品|黄色网站入口免费进人
          1. <nobr id="easjo"><address id="easjo"></address></nobr>

              <track id="easjo"><source id="easjo"></source></track>
              1. 
                

              2. <bdo id="easjo"><optgroup id="easjo"></optgroup></bdo>
              3. <track id="easjo"><source id="easjo"><em id="easjo"></em></source></track><option id="easjo"><span id="easjo"><em id="easjo"></em></span></option>