Atom

思考、故事和创意

Google

Android 使用体验

去年 11 月份,我在 Google 商店订了 Google Pixel,过了半个多月到手,至今已经用了差不多半年,可以说一说用 Android 是什么样的体验。

在国内使用原生 Android 似乎有不小的障碍,好在我几年前就已经给家里的路由器做好了科学上网的配置。实际上我最开始的想法是:没太多新鲜感。

主屏

再讲更多东西之前,有这样几件事情必须要说明:

  1. 关于 Android 的体验,仅限于原生 Android
  2. 与 iOS 的对比,也只仅限于 iPhone 5S 上的 iOS 9
  3. 完全不涉及 root 或者越狱

简单来说,这是我个人比较狭隘的, 还算客观的感受。

在这之前我用了好几年 iPhone,对于 Android,我期待有完全不同的体验。拿到手用了一天后,新鲜感并没有我预期的那么强烈。毕竟大多数时候是在用同样的一些 App,例如微信、QQ、淘宝、部落冲突等等。

差异还是有的,很快我就遇到些恼人的事情,有些 App 在 Google Play Store 里竟然没有:支付宝、滴滴打车、网易云音乐…… 有些是因为不同地区帐号的限制,有些是因为太流氓被下架了。Android 系统可以安装未知来源的 apk 文件,所以要用这些 App 的办法很多,各厂官网一般都会提供 apk 文件的下载,或者发布到国内各种 App 市场。不过我也信不过国内各厂,尽量都从 Google Play Store 安装应用。

Android 的 App 生态比 iOS 的生态差得多,由于系统的开放性,各种流氓应用大行其道,这种开放性对于用户来说,可以说是好坏参半。最初我产生换 Android 系统的想法,原因之一就是想用 Rescuetime 这个 App 来记录手机各个 App 的使用时间;另外有一点非常值得一提,目前我在用 Enpass 作为密码管理软件,可以在全系统范围内使用它的自动填充功能,非常方便。在 iOS 上,以我以往的经验,也就是说 iOS 9 以及之前版本,并不能做到这些,由于我并没有用过比 iPhone 5S 更新的 iPhone,不知道现在是否可以做到上面提到的功能。

回到 App 生态的话题,Android 的劣势还体现在另一方面,几乎各厂都是先发布 iOS 版的 App,不知道多久之后才有 Android 版本,然后或许还要更久才发布到 Google Play Store 上。最近火热的微信“搜一搜”和“看一看”功能,作为 Android 用户,我还没真正见识过呢。

要找一个在 Android 上有,而在 iOS 上没有的优秀 App,或者 Android 版本比 iOS 版本质量更好的 App,还真是不太容易。不过再怎么样,Android App 也不是一无是处,有不少 App 可以说至少不比 iOS 版差。

使用这部手机过程中,最早让我非常惊喜的东西是 Google Photos,几乎所以的照片管理应用都会被它碾压。功能强大和易用,甚至让你有点害怕,产生 "You are being watched" 的感觉,它似乎比你更清楚你去了哪里拍照,在什么场合拍的照,拍了哪些人,拍了哪些东西。Google Photos 也有 iOS 版,所以这个惊喜并不是 Android 独占。在知乎可以看下其他人对 Google Photos 的详细评价。如何评价 Google I/O 2015 上发布的 Google Photos?

另外一个惊喜是 Google 智能助手,只有在手机语言设置为英语时才有这个功能。我问过它能不能说中文,它很干脆地告诉我不能。我用得最多的是在家里的时候用它解锁手机、定时、设定提醒闹钟、问天气。还有一个很有用的功能,当你在刷手机时,可能是在刷微博时看到别人发的图,你想知道出自哪里,或者可能是浏览网页时看到别人提到的一部电影,你想了解下这部电影,这时候长按 Home 键(手机屏幕底部中间那个键,是叫 Home 键吧?)或者说 "Ok, Google",把它给呼唤出来,就可以让它查当前手机屏幕上出现的内容是什么了。

智能助手

想比 Siri,Google 智能助手的上下文关联做得更好。Google 智能助手更喜欢把内容说出来,而 Siri 展示在屏幕上的更多。可以备个梯子,去 Youtube 看这个对比视频 Google Assistant vs Siri!

总之,对于 Android 的体验,就是这样几点:

  • 在日常使用中,与 iOS 并没有天差地别
  • 开放性对用户有利有弊
  • App 整体质量不如 iOS App
  • 有些小小的亮点

希望 Android 上的各个 App 都老实点……

对 Google TCP BBR 的浅薄认识

关于 Google 发布的新的 TCP 拥塞控制算法 BBR (Bottleneck Bandwidth and RTT) 的一些记录,仍然在学习中,难免有误解。之后会持续修正和完善。

网络链路上的包比较少时,道路很通畅,这个阶段,对于一个 TCP 连接来说,它的速度由这个连接两端之间的距离决定,也可以说是由 RTT 决定。当发包速率变大,把道路基本上填满了之后,这个阶段,带宽的大小决定了这个连接的速度,这时两端之间可能就会有包要排队,延迟时间除了 RTT 还有排队时间。

BBR 目的是要尽量跑满带宽,并且尽量不要有排队的情况。

cwnd 是普通的拥塞控制算法里最终要求得的一个值,用来控制发包速率。BBR 也要求到这个值,但是它不是最主要的控制发包速率的变量,主要的变量是 pacing_rate

这两个变量都由探测到的带宽值和 RTT 值得到,整个过程都围绕着这两个值。在 BBR 算法中,有四种状态,几种状态可能会有如下的转换:

            |
            V
   +---> STARTUP  ----+
   |        |         |
   |        V         |
   |      DRAIN   ----+
   |        |         |
   |        V         |
   +---> PROBE_BW ----+
   |      ^    |      |
   |      |    |      |
   |      +----+      |
   |                  |
   +---- PROBE_RTT <--+

另外还有一个重要变量,是在不同状态,不同阶段取的不同增益系数,比如有时需要多发包探测到最大瓶颈带宽,有时需要把发包量降下来探测较为准确的 RTT。

STARTUP 类似与普通拥塞控制里的慢启动,增益系数是 2ln2,每一个来回都以这个系数增大发包速率,估测到带宽满了就进入 DRAIN 状态 —— 连续三个来回,测得的最大瓶颈带宽没有比上一轮增大 25% 以上,就算做带宽满了。

进入 DRAIN 状态,增益系数小于 1,也就降速了。一个包来回,把 STARTUP 状态中产生的队列“抽干”,怎么样测算到队列空了?发出去还没有 ACK 的包量 inflight,与 BDP (带宽延迟积)进行比较,inflight < BDP 说明空了,道路不那么满了,如果 inflght > BDP 说明还不能到下一个状态,继续 DRAIN。

PROBEBW 是稳定状态,这时已经测出来一个最大瓶颈带宽,而且尽量不会产生排队现象。之后的每个来回,在 PROBEBW 状态循环(除非要进入下面提到的 PROBERTT 状态),轮询下面这些增益系数,5/4, 3/4, 1, 1, 1, 1, 1, 1,如此,最大瓶颈带宽就会在其停止增长的地方上下徘徊。大部分时间都应该处于 PROBEBW 状态。

前面三种状态,都可能进入 PROBERTT 状态。超过十秒没有估测到更小的 RTT 值,这时进入 PROBERTT 状态,把发包量降低,空出道路来比较准确得测一个 RTT 值,至少 200ms 或一个包的来回之后退出这个状态。检查带宽是否是满的,进入不同的状态:如果不满,进入 STARTUP 状态,如果满,进入 PROBEBW 状态。

给后台人员的前端优化

作为一名后台开发,听到前端优化(不涉及后端 Web Server、数据库等),我大概了解的词有这些:主要是减小体积、减少请求、合理布置页面原素等,再具体些就是,开启 Gzip 压缩、合并 css 文件、合并 js 文件、长链接、减少 DNS 查询、使用 cookie-free 域名、js 放页面最下面、指定缓存时间、ETag、延迟加载、异步加载……

作为一个没什么访问量的小网站,费心思搞这些东西干什么呢?一方面服务器本来就没什么压力,另一方面访问者能感受到的速度差异能有多少?结果我还是搞了一下,因为有个方便的工具,Google 出品的 PageSpeed,不用我自己操那么多心。

这货不是被 Google 关了吗?如果是说 Google 提供的带 PageSpeed 功能的 CDN 服务,对,是关了。但是实现这个功能的模块 PageSpeed Module,还是可以自己拿来用的。它是作为 Web Server 的模块来使用的,Web Server 有了它,就能自动优化网站了。目前有提供 Apache 和 Nginx 的模块,对于 Apache,模块名是 mod_pagespeed 还提供各个平台编译完打好包的二进制文件,对于 Nginx,模块名是 ngx_pagespeed, 需要自己去编译。

因为我只用过 Nginx,所以自然是向 ngx_pagespeed 下手了。

编译安装

ngx_pagespeed 这个模块编译进 Nginx,官方有个简单明了的编译步骤 Build ngx_pagespeed From Source,很清晰。

1. 先装依赖的库

RedHat, Centos, or Fedora

sudo yum install gcc-c++ pcre-devel zlib-devel make unzip

Ubuntu or Debian

sudo apt-get install build-essential zlib1g-dev libpcre3 libpcre3-dev unzip

2. 然后下载 ngx_pagespeed

cd
NPS_VERSION=1.9.32.10
wget https://github.com/pagespeed/ngx_pagespeed/archive/release-${NPS_VERSION}-beta.zip
unzip release-${NPS_VERSION}-beta.zip
cd ngx_pagespeed-release-${NPS_VERSION}-beta/
wget https://dl.google.com/dl/page-speed/psol/${NPS_VERSION}.tar.gz
tar -xzvf ${NPS_VERSION}.tar.gz  # extracts to psol/

这一步里后面下载的 psol 的那个包比较大。

3. 最后下载 nginx 进行编译

cd
# check http://nginx.org/en/download.html for the latest version
NGINX_VERSION=1.8.0
wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar -xvzf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}/
./configure --add-module=$HOME/ngx_pagespeed-release-${NPS_VERSION}-beta
make
sudo make install

通常在编译 nginx 之前,我会执行 nginx -V 看一下当前的 nginx 的编译参数,把这些编译参数加到 ./configure 这一条命令的参数里(又加了别的参数,就可能又需要安装别的依赖,最笨的方法,看缺库的报错信息见招拆招就行)。

配置使用

在每个要启动 PageSpeed 的 server 块中加入如下代码:

pagespeed on;

# Needs to exist and be writable by nginx.  Use tmpfs for best performance.
pagespeed FileCachePath /var/ngx_pagespeed_cache;

# Ensure requests for pagespeed optimized resources go to the pagespeed handler
# and no extraneous headers get set.
location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
  add_header "" "";
}
location ~ "^/pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }

这一行配置 pagespeed FileCachePath /var/ngx_pagespeed_cache; 的注释里说了,设置的这个目录要存在,而且 nginx 进程对它有写权限。

配置 filter

这才是进行优化的重头戏。

把 filter 翻译成“过滤器”容易让人迷惑,实际上这些 filter 的作用是改变原本内容,原本内容从 filter 过一遍,变成另外的样子。nginx 的很多模块都属于 filter。

为了实现开头说的各种优化,PageSpeed 提供了多种多样的 filter,通过 pagespeed EnableFilters xxx,xxx,xxx 来启用,EnableFilters 后面可以跟一个或多个以逗号分隔的 filter 名字,与之对应的 DisableFilters 用来停用 filter。为了方便人民群众,PageSpeed 给了三种预设的模式,通过启用各种不同的 filter 进行不同目的的优化。

  • PassThrough
  • CoreFilters
  • OptimizeForBandwidth

pagespeed RewriteLevel PassThrough 指定 PassThrough 模式,顾名思义,这样配置就相当于什么也不做。Corefilters 是默认的模式,开启了不少 filter,对网站内容一般来说是安全的。OptimizeForBandWidth 更保守些,更安全。这里的安全是指对原始内容的改变是否会影响页面的展示、效果、功能等等。

Enabling, Disabling, And Forbidding Specific Filters 这里有 CoreFiltersOptimizeForBandWidth 所启用 filter 的详细列表,以及对各个 filter 的简介,也可以从列表里进到每个 filter 的详情页查看。

RewriteLevelEnableFilters 可以组合起来用。比如

pagespeed RewriteLevel PassThrough;
pagespeed EnableFilters combine_css,extend_cache,rewrite_images;
pagespeed EnableFilters rewrite_css,rewrite_javascript;

这样就只开启了后面两行指定的 5 个 filter。

或者这样用

pagespeed RewriteLevel CoreFilters
pagespeed DisableFilters rewrite_css,rewrite_javascript;

开启 CoreFilters 模式,但是停掉 rewrite_cssrewrite_javascript

这是我的配置,仅供参考。每项内容在官网都有详细解释。

pagespeed on;

pagespeed FileCachePath /var/ngx_pagespeed_cache;

location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
	add_header "" "";
}
location ~ "^/pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }

pagespeed XHeaderValue "Powered By ngx_pagespeed";

pagespeed RewriteLevel CoreFilters;
pagespeed EnableFilters collapse_whitespace,remove_comments;
pagespeed EnableFilters local_storage_cache;
pagespeed EnableFilters in_place_optimize_for_browser;
pagespeed EnableFilters inline_javascript,inline_css,inline_images;
pagespeed EnableFilters dedup_inlined_images;

pagespeed AvoidRenamingIntrospectiveJavascript off;
pagespeed CriticalImagesBeaconEnabled false;