|
1 | | -PageSpeed Insights |
2 | | --------- |
3 | | -With Google Cloud Platform / Cloud Functions. |
| 1 | + |
4 | 2 |
|
5 | | -Setup |
6 | | -> pip3 install -r requirements.txt |
| 3 | +使用 Google Cloud Scheduler, Pub/Sub, Functions , Storage 等云服务,搭建 PageSpeed Insights 前端网站网页的质量和性能 benchmark 定时审查系统。与 CI/CD 流程结合,定时大批量审查网站技术性能指标。 |
7 | 4 |
|
8 | | -or |
9 | | -> pip3 install --upgrade requests |
10 | | -> pip3 install --upgrade google-api-python-client |
| 5 | +### 1. PageSpeed Insights |
| 6 | + |
| 7 | +#### 1.1 简介 |
| 8 | + |
| 9 | +PageSpeed Insights 是 Google 提供的一款网页性能检测优化工具,能够针对移动设备和桌面设备生成网页的实际性能报告,并提供关于如何改进相应网页的建议。它采用 Google Lighthouse 提供的各种最佳实践作为测试基准,使用 Blink 渲染工具(即 Google Chrome 的渲染引擎),模拟移动设备和桌面设备,抓取目标网站网页,进行优化分析。 |
| 10 | +以下简称PSI。 |
| 11 | + |
| 12 | +#### 1.2 版本历史 |
| 13 | + |
| 14 | +版本 | 发布时间 | 功能更新 |
| 15 | +--|--|-- |
| 16 | +V5 | 2018年Q4 | 当前最新版本。2019.05.08更新使用 Lighthouse 5.0 作为其分析引擎。 |
| 17 | +V4 | 2018年1月 | 2019年Q3之前停用 |
| 18 | +V2 | 2015年1月| 已停用 |
| 19 | +V1 | 更早期 | 已停用 |
| 20 | + |
| 21 | +#### 1.3 分析报告组成 |
| 22 | + |
| 23 | +#### 1.3.1 综合速度得分 |
| 24 | + |
| 25 | +评分和等级: |
| 26 | + |
| 27 | ++ 快 90分以上 |
| 28 | ++ 中等 50-90分 |
| 29 | ++ 慢 50分以下 |
| 30 | + |
| 31 | +V5版本使用 Lighthouse 计算多项性能指标的综合加权得分。 |
| 32 | +V4及之前版本结合 Chrome 用户体验报告数据库中的真实用户测速数据,计算评分和等级。主要参考以下两项指标。 |
| 33 | + |
| 34 | ++ FCP (First Contentful Paint)首次内容绘制,用于衡量用户何时看到来自相应网页的可见响应。所用时间越短,留住用户的可能性就越大。 |
| 35 | ++ DCL 文档内容加载,用于衡量何时完成 HTML 文档的加载和解析。所用时间越短,跳出率越低。 |
| 36 | + |
| 37 | +#### 1.3.2 实测数据 |
| 38 | + |
| 39 | +结合 Chrome 用户体验报告中的其他网页过去30天内的实测数据相比的得分。 |
| 40 | + |
| 41 | +#### 1.3.3 实验室数据 |
| 42 | + |
| 43 | +给出以下几项指标的耗时绝对值数据: |
| 44 | + |
| 45 | ++ First Contentful Paint 首次内容绘制时间 |
| 46 | ++ First Meaningful Paint 首次有效绘制时间 |
| 47 | ++ Speed Index 速度指数 |
| 48 | ++ First CPU Idle 首次 CPU 闲置时间 |
| 49 | ++ Time to Interactive 可交互前的耗时 |
| 50 | ++ Estimated Input Latency 最长的潜在FID |
| 51 | + |
| 52 | +#### 1.3.4 关于如何加快网页加载速度的优化建议 |
| 53 | + |
| 54 | +#### 1.3.5 关于Web开发最佳实践的详细诊断建议。 |
| 55 | + |
| 56 | +#### 1.3.6 已通过的符合最佳实践的审查项 |
| 57 | + |
| 58 | +#### 1.4 实际案例 |
| 59 | + |
| 60 | +以携程机票H5航班动态首页的某线上版本为例,直观的查看分析报告: |
| 61 | +https://m.ctrip.com/webapp/flight/schedule/detail.html |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +#### 1.5 使用方法 |
| 76 | + |
| 77 | +PSI API是Google RESTful APIs之一, 仅需一次 HTTP 请求 ,应答返回一个 JSON Ojbect。使用极其简便。 |
| 78 | + |
| 79 | +#### HTTP Request |
| 80 | + |
| 81 | +> GET https://www.googleapis.com/pagespeedonline/v5/runPagespeed |
| 82 | +
|
| 83 | +必选参数1个: |
| 84 | + |
| 85 | ++ `url`: 目标分析网页的链接 |
| 86 | + |
| 87 | +可选参数6个: |
| 88 | + |
| 89 | ++ category:`accessibility`,`best-practices`,`performance`,`pwa`,`seo`。默认是`performance`。 |
| 90 | ++ locale:返回结果文本的本地化语言版本。目前支持40种。默认英语`en`。 |
| 91 | ++ strategy:`desktop` 针对桌面浏览器进行优化分析,`mobile` 针对移动设备浏览器进行优化分析。 |
| 92 | ++ utm_campaign:广告系列名称 |
| 93 | ++ utm_source:广告系列来源 |
| 94 | ++ fields: 定制 Response 内容字段。 |
| 95 | + |
| 96 | +#### HTTP Response |
| 97 | + |
| 98 | +返回一个 JSON Object ,字段内容较多,此处省略,详见官网文档。 |
| 99 | + |
| 100 | +#### 最简单命令行调用 |
| 101 | + |
| 102 | +> curl https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://m.ctrip.com |
| 103 | +
|
| 104 | +### 2. Google Cloude Platform (GCP) |
| 105 | + |
| 106 | +#### 2.1 系统流程图 |
| 107 | + |
| 108 | +```mermaid |
| 109 | +graph TB |
| 110 | + Job(定时作业任务) |
| 111 | + CPS_Job_P(Job Publisher) |
| 112 | + CPS_Job_S(Job Subscriber) |
| 113 | + CPS_PSI_P(PSI Publisher) |
| 114 | + CPS_PSI_S(PSI Subscriber) |
| 115 | + CF_Job(Job Function Service) |
| 116 | + CF_PSI(PSI Function Service) |
| 117 | + GAPI_PSI(PageSpeed Insights API) |
| 118 | +
|
| 119 | + Job -->|Push| CPS_Job_P |
| 120 | + CPS_Job_S -->|Push| CF_Job |
| 121 | + CF_Job -->| 并发 HTTP | CPS_PSI_P |
| 122 | + CPS_PSI_S -->| 并发 Push | CF_PSI |
| 123 | + CF_PSI -->| 并发 HTTP Request | GAPI_PSI |
| 124 | +
|
| 125 | + subgraph Cloud Scheduler |
| 126 | + Job |
| 127 | + end |
| 128 | +
|
| 129 | + subgraph Cloud Pub/Sub |
| 130 | + subgraph Job Pub/Sub |
| 131 | + CPS_Job_P --> CPS_Job_S |
| 132 | + end |
| 133 | +
|
| 134 | + subgraph PSI Pub/Sub |
| 135 | + CPS_PSI_P --> CPS_PSI_S |
| 136 | + end |
| 137 | + end |
| 138 | +
|
| 139 | + subgraph Cloud Functions |
| 140 | + CF_Job |
| 141 | + CF_PSI |
| 142 | + end |
| 143 | +
|
| 144 | + subgraph Google APIs |
| 145 | + GAPI_PSI |
| 146 | + end |
| 147 | +``` |
| 148 | + |
| 149 | +#### 2.2 Cloud Scheduler |
| 150 | + |
| 151 | +Cloud Scheduler 是 GCP 的一项全托管式企业级 cron 作业调度服务。支持 App Engine、Cloud Pub/Sub 和任意 HTTP 端点,允许作业触发 Compute Engine、Google Kubernetes Engine 和本地资源。 |
| 152 | +使用 Google Cloud Console 创建Job。目标有3种:HTTP,Pub/Sub,App Engine HTTP。这里选择 Pub/Sub 。设置每天22:00自动触发。 |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | +创建成功后查看部署状态,部署成功后可以直接“立即运行”,查看日志,确认运行正常。 |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | +#### 2.3 Cloud Pub/Sub |
| 161 | + |
| 162 | +Cloud Pub/Sub 是 GCP 的一项简单、可靠、可伸缩,可以用作数据流分析和事件驱动型计算系统的基础。 |
| 163 | +这里创建两个主题,`psi-job` 用于 Cloude Scheduler Job 的事件数据中转,`psi-single` 用于 Cloud Functions 的并发 HTTP 请求的事件数据中转。 |
| 164 | + |
| 165 | + |
| 166 | +### 2.4 Cloud Functions |
| 167 | + |
| 168 | +实现并发大量网页的 PageSpeed Insights 检查,有多种方式。可以使用 Google App engine, Google Compute Engine。鉴于 PSI API 是上下文无关的简单 HTTP RESTful API,Cloud Functions Serverless 是最佳最简实现。 |
| 169 | +Cloud Functions 是 GCP 的一项事件驱动型无服务器计算平台。通过构建多个分别专注于做好一件事的小型独立功能单元,再将这些功能单元组合成一个系统,实现快速开发和部署。支持在单个函数(而不是整个应用、容器或虚拟机)级构建和部署服务。 |
| 170 | + |
| 171 | +#### 2.4.1 编写 Function |
| 172 | + |
| 173 | +目前支持以下几种方案: |
| 174 | + |
| 175 | +语言 | JavaScript |
| 176 | +--|-- |
| 177 | +运行时 | Node.js 6(已弃用)、8、10(测试版) |
| 178 | +HTTP 框架 | Express |
| 179 | +HTTP 函数 | Express Request & Response Context |
| 180 | +后台函数 | (data, context, callback) |
| 181 | +依赖项管理 | npm/yarn + package.json |
| 182 | + |
| 183 | +语言 | Python |
| 184 | +--|-- |
| 185 | +运行时 | 3.7.1 |
| 186 | +HTTP 框架 | Flask |
| 187 | +HTTP 函数 | 入参:Flask Request Object。返回值:符合 Flask.make_response() 的任意对象。 |
| 188 | +后台函数 | (data, context) |
| 189 | +依赖项管理 | pip + requirements.txt |
| 190 | + |
| 191 | +语言 | Go |
| 192 | +--|-- |
| 193 | +运行时 | Go 1.11 |
| 194 | +HTTP 框架 | http.HandlerFunc 标准接口 |
| 195 | +HTTP 函数 | request: *http.Request. response: http.ResponseWriter. |
| 196 | +后台函数 | (ctx, Event) |
| 197 | +依赖项管理 | go.mod/vendor |
| 198 | + |
| 199 | +#### 2.4.2 部署 Function |
| 200 | + |
| 201 | +目前支持以下几种方式: |
| 202 | + |
| 203 | ++ 从本地机器部署。`使用 gcloud 命令行工具。` |
| 204 | ++ 通过源代码控制系统部署。`使用 Google Cloud Source Repositories ,通过 OAuth 关联源代码仓库(如 GitHub 或 Bitbucket)。` |
| 205 | ++ 通过 GCP Console 部署。 |
| 206 | + + 网页内嵌编辑器.`直接在线编写函数代码。` |
| 207 | + + 上传本地ZIP文件。`文件夹目录结构与上述依赖性管理的源码工程结构一致。` |
| 208 | + + 导入 Cloud Storage 中的 ZIP 文件。`同上。` |
| 209 | + + 引用 Google Cloud Source Repositories的源代码工程。 |
| 210 | ++ 通过CI/CD部署。`使用 Cloud Build 搭建持续集成和部署系统。` |
| 211 | + |
| 212 | +#### 2.4.3 监控 Function |
| 213 | + |
| 214 | +Google Stackdriver 提供了服务监控工具,包括 `Debugger,Monitoring,Trace,Logging, Error Reporting,Profiler`。 |
| 215 | + |
| 216 | +### 3. PSI Functions 实现 |
| 217 | + |
| 218 | +创建好一个 Scheduler Job 和两个 Pub/Sub 主题后,接下来实现两个对应的 Functions 。 |
| 219 | + |
| 220 | +#### 3.1 psi-single function |
| 221 | + |
| 222 | +psi-single() 负责针对具体单一 URL ,调用 PSI API 获取 JSON 结果的功能。 |
| 223 | +Google APIs 支持多种调用方式。 |
| 224 | + |
| 225 | +**3.1.1 使用 `google api client` 。** |
| 226 | +通过 `Discovery API` ,获得已经封装好的 `Service` ,再调用具体接口。 |
| 227 | + |
| 228 | +```Python |
| 229 | +from googleapiclient.discovery import build |
| 230 | + |
| 231 | +def run(url): |
| 232 | + pagespeedonline = build( |
| 233 | + serviceName = 'pagespeedonline', |
| 234 | + version = 'v5', |
| 235 | + developerKey = API_KEY |
| 236 | + ) |
| 237 | + response = pagespeedonline.pagespeedapi().runpagespeed(url = url).execute() |
| 238 | + print(response) |
| 239 | + return 'OK' |
| 240 | +``` |
| 241 | + |
| 242 | +**3.1.2 针对简单接口,直接调用 `HTTP RESTful API` 。** |
| 243 | + |
| 244 | +```Python |
| 245 | +import requests |
| 246 | +GAPI_PSI = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed" |
| 247 | + |
| 248 | +def run(url): |
| 249 | + try: |
| 250 | + payload = {"url": url, |
| 251 | + "key": API_KEY |
| 252 | + } |
| 253 | + with requests.Session() as session: |
| 254 | + response = session.get(url=GAPI_PSI, params=payload) |
| 255 | + print(response.status_code) |
| 256 | + print(response.json()) |
| 257 | + except requests.RequestException as _e: |
| 258 | + print(_e) |
| 259 | + return 'OK' |
| 260 | +``` |
| 261 | + |
| 262 | +**3.1.3 实现 Pub/Sub 主题的订阅** |
| 263 | +订阅消息 `event` 的格式详见官网文档,其中 data 属性是一段 `base64` 编码的 `ByteArray` ,承载了实际的数据内容。 |
| 264 | + |
| 265 | +```Python |
| 266 | +import base64 |
| 267 | + |
| 268 | +def run_pubsub(event, context): |
| 269 | + pubsub_message = base64.urlsafe_b64decode(event['data']).decode('utf-8') |
| 270 | + return run(pubsub_message) |
| 271 | +``` |
| 272 | + |
| 273 | +#### 3.2 psi-job function |
| 274 | + |
| 275 | +psi-job() 由 Scheduler Job 触发,将所有需审查的 URL 以 Pub/Sub 事件形式,并行分发给 psi-single() 。 |
| 276 | + |
| 277 | +```Python |
| 278 | +from google.cloud import pubsub_v1 |
| 279 | + |
| 280 | +def run(event, context): |
| 281 | + publisher = pubsub_v1.PublisherClient() |
| 282 | + topic = publisher.topic_path(PROJECT_ID, TOPIC_NAME) |
| 283 | + for url in URL_DICT: |
| 284 | + data = url.encode('utf-8') |
| 285 | + publisher.publish(topic, data) |
| 286 | + return 'OK' |
| 287 | +``` |
| 288 | + |
| 289 | +#### 3.3 环境变量和依赖项 |
| 290 | + |
| 291 | +为了避免安全敏感信息泄漏,可以将关键信息写入 Functions 环境变量和本地环境变量(本地开发调试使用)。 |
| 292 | +上述代码中 `API_KEY, PROJECT_ID` 等数据通过 `os.getenv()` 获取。 |
| 293 | +Cloude Functions 已内置常用依赖库,详见官网文档。如需增加依赖项,配置各语言对应的工程文件。上述代码引用了两个依赖库。 |
| 294 | + |
| 295 | +```Python |
| 296 | +# requirements.txt |
| 297 | +# Function dependencies |
| 298 | +requests==2.21.0 |
| 299 | +google-cloud-pubsub==0.40.0 |
| 300 | +``` |
| 301 | + |
| 302 | +### 4. Storage |
| 303 | + |
| 304 | +上述代码中的 `print()` 会写入 StackDriver 日志库,供后续过滤分析。鉴于每一个 URL 的审查结果是一个 JSON Object 字符串,可以进一步写入 BigTable , 使用 BigQuery 进行查询分析,再进一步导入 Google Data Studio , 进行可视化报表展示。 |
| 305 | +这里使用 Cloud Storage 存储 JSON 字符串为单一文件。 |
| 306 | + |
| 307 | +```Python |
| 308 | +from urllib import parse |
| 309 | +from google.cloud import storage |
| 310 | +from google.cloud.storage import Blob |
| 311 | + |
| 312 | +def save(url, report): |
| 313 | + '''Save to https://console.cloud.google.com/storage/browser/[bucket-id]/''' |
| 314 | + client = storage.Client() |
| 315 | + bucket = client.get_bucket("psi-report") |
| 316 | + blob = Blob(f"${parse.quote_plus(url)}.json", bucket) |
| 317 | + blob.upload_from_string(report, "application/json") |
| 318 | +``` |
| 319 | + |
| 320 | +添加依赖项。 |
| 321 | + |
| 322 | +```Python |
| 323 | +# requirements.txt |
| 324 | +# Function dependencies |
| 325 | +google-cloud-storage==1.15.0 |
| 326 | +``` |
| 327 | + |
| 328 | +### 5. 源代码 |
| 329 | + |
| 330 | +https://github.com/9468305/python-script/tree/master/PageSpeedInsights |
| 331 | + |
| 332 | +### 6. 文档链接 |
| 333 | + |
| 334 | +1. PageSpeed Insights |
| 335 | +https://developers.google.com/speed/pagespeed/insights |
| 336 | +2. Google Lighthouse |
| 337 | +https://developers.google.com/web/tools/lighthouse/ |
| 338 | +3. Google Cloud Scheduler |
| 339 | +https://cloud.google.com/scheduler/ |
| 340 | +4. Google Cloud Pub/Sub |
| 341 | +https://cloud.google.com/pubsub/ |
| 342 | +5. Google Cloud Functions |
| 343 | +https://cloud.google.com/functions/ |
| 344 | +6. Google Cloud Storage |
| 345 | +https://cloud.google.com/storage/ |
| 346 | +7. Google Cloud Build |
| 347 | +https://cloud.google.com/cloud-build/ |
| 348 | +8. Google Stackdriver |
| 349 | +https://cloud.google.com/stackdriver/ |
0 commit comments