天猫双 11 前端分享系列(七):如何精确识别终端

2,051 阅读4分钟
原文链接: github.com

首先,要先说声抱歉,因为,其实目前我们还没有做到精确地做到识别99%的终端设别,其中原因,一部分是因为终端类型和UA实在难以覆盖,另外一部分原因也是因为使用了一些错误的识别策略。

注1:后面会大量出现detector,其实就是我们给内部终端识别工具起的一个名字。
注2:天猫页面一直在实施一个url对应多份不同终端的页面,所以终端识别非常重要。

在哪一层进行识别

初期方案

由于当时处于业务mobile页面发展的初期,且大部分页面还在基于php进行开发,所以诞生了detector的第一个版本,php版本。识别逻辑也比较简单,纯正则匹配UA。

后来,node业务渐渐的增加,我们又重写了一份node版本,其中为了保持终端识别能力的一致,用于匹配UA的正则统一放到了一份json文件里,php和node都统一来读这一份文件。

正则主要还是收集了github上各种比较成熟的识别方案综合出来的。

改造期

在2014年底,为了保证pad用户的访问质量,我们对终端识别的工具进行了非常大的改造。其中,一直在坚持的一点就是将识别能力放到服务端进行。

当时面临的一个难题是,安卓pad和安卓phone之间的UA并没有差异,特别是4.2之前的版本,无法通过UA进行识别,但是又希望能够让用户在安卓pad上看到更合适的PC版本,我们设计并产出了MED的终端硬件信息获取方案。

MED

MED的运行逻辑其实很简单:用户第一次访问的时候,在nginx端插入一个脚本,计算设备宽高、像素宽高、是否支持触摸等信息,然后记录到cookie中,第二次访问的时候,nginx就可以拿到用户的终端信息了。

于是,我们就可以知道用户的物理宽度了。可惜这里埋了一些坑。

  1. 物理宽度的计算用到screen.width/screen.height,但是不同厂商的安卓设备,在不同的浏览器或者webview下给了各种不同的值,而并不是屏幕的分辨率。
  2. 安卓手机的屏幕越做越大,和PAD之间的分界线越来越模糊。

nginx-detector

由于nginx端包含了拿到终端硬件信息的能力,那么这里就有两个选择

  1. 将拿到的信息写到http头里,转发给应用的php/node detector
  2. 直接在nginx层进行识别,将识别结果转发给下游的应用

其实,这里并没有太多的纠结,服务端语言太多,针对各种不同的语言维护一份实在不太现实。于是,我们选择在nginx层做这些事情,这里用到了开源的tengine模块http_user_agent

具体的识别规则也是从正则切换到了nginx配置文件。整个流程就优化为

  1. nginx对UA进行解析
  2. 解析完成后,nginx再结合硬件信息,如果物理设备宽度较大,则识别为pad

这个方案逐渐部署到了各个应用上,支持了包括频道、活动、搜索等应用的终端识别,也顺利经过了双11的考验。

惊喜

nginx层做解析带来一个惊喜,就是原本只有一个url一份缓存的方案,由于天猫一个url对应的是多个端不同的内容,无法进行缓存。

在nginx层面能够识别用户终端后,我们可以让一个url针对多份缓存副本,从而实现在cdn上可以直接经过nginx转发请求到用户终端对应的副本。

策略变更

双11结束之后,我这边对已有方案进行了梳理,nginx方案已经暴露了一些问题,更新nginx配置文件成本相对发布前端文件或者后端文件都略高,且很多安卓phone都反馈了访问时看到了pc版本的页面。

前者的问题在于维护成本,后者的原因是来自前面提到的浏览器提供数值问题。

目前识别策略还是遵守安卓UA规范,包含android + mobile则判断是Phone,android不带phone就是pad,也算是面对未来的解决方案了。

最后,如果有更好的识别方案或者建议,欢迎找我沟通。