PWA系列 - Web Push 技术


基本概念

(1)webapp 

网络应用,是指使用web技术实现的应用程序,在web浏览器或其它web运行环境的上下文中执行。

(2)application server

网络应用的服务器,是指webapp的服务器端。

(3)push message

推送消息,是指application server发送给webapp的消息。application server在推送消息给推送服务(例如,GCM)时,会带上push subscription,推送服务会找到与push subscription相关联的active worker ,然后把消息推送给它,并触发它的onpush。

(4)push subscription

推送订阅,是代表webapp在用户代理(例如,浏览器)和推送服务(例如,GCM)之间建立的消息传递上下文。每个push subscription都与一个service worker registration相关联,而一个service worker registration最多只有一个push subscription。每个push subscription都有一个push endpoint和一个公钥,公钥是用于给application server加密要推送的消息数据。

(5)push endpoint

push endpoint,在ServiceWorker向push service注册时,由push service生成。在Chrome 45版本之前,push endpoint是指推送服务的地址,例如,GCM服务器的地址。在Chrome 45及之后版本,push endpoint = push server address + subscriptionId,其中subscriptionId可以唯一标识push subscription。在浏览器客户端,通过subscriptionId可以找到相应的app_id和sender_id。app_id包含作用域和workerId,例如,push#http://example.com/test/#1。

(6)sender_id

sender_id,是创建Google API项目时生成的项目ID。ServiceWorker通过浏览器向GCM注册订阅消息时,需要上传sender_id,才能订阅成功。GCM会使用sender_id,通过Instance ID API到Instance ID Service去注册生成Instance ID,此步骤最终生成的注册信息,也就是前面提到的 subscriptionId。注:Chrome52之前版本对应的GCM/FCM还不支持标准的Web Push Protocol,所以才需要通过gcm_sender_id去找到相应的浏览器客户端,Chrome52版本及其对应的GCM/FCM已支持标准的Web Push Protocol,不再需要sender_id了,即支持标准的Web Push Protocol的push服务器都是不需要sender_id的。

(7)Subscription Refreshes

浏览器或推送服务,可以在任何时间更新push subscription。更新成功后,必须触发与push subscription相关联的service worker registrationpushsubscriptionchange事件,如果更新失败,浏览器应定期触发更新,直至成功。

(8)Subscription Deactivation

push subscription停止使用时,浏览器和推送服务必须删除已保存的副本,相关消息不再推送。service worker registration在注销或清除注册信息时,相关联的push subscription会被停用。

(9)Push service

推送服务,是指一个推送服务系统,允许application server通过它推送消息给webapp。

Push流程

9dd4034cd21c3b4a4e8590e91440a56aa09e0b09

(1)页面向浏览器注册一个ServiceWorker,浏览器生成对应的ServiceWorkerRegistration对象。

(2)页面通过ServiceWorker的PushManager.subscribe方法向push service(GCM)订阅消息,GCM 返回push subscription (endpoint+subscriptionId)给浏览器,浏览器找到对应的ServiceWorker,并把push subscription传递给ServiceWorker。

(3)页面通过PushManager.getSubscription获取到push subscription,并将它发送给页面服务器。注,可以使用ajax请求发送数据给页面服务器。

(4)页面服务器需要推送消息时,将push message和push subscription发送给push service(例如,GCM)。GCM根据push subscription找到对应的浏览器客户端并把消息推送给它,浏览器通过app_id找到相应的ServiceWorker,激活和把消息传递给它并触发它的onpush。

(5)页面JS根据实际需要去处理onpush事件,比如,弹出消息通知,存储数据到本地,提前到服务器去fetch资源,等等。

(6)页面在不再需要订阅消息时,可以发送ajax请求通知页面服务器去清除push subscription。然后使用PushSubscription.unsubscribe向push service去取消订阅。

页面实现消息推送的详细流程,可以参考官方文档 Your First Web Push Notification 和 Push Notifications on the Open Web

基本流程的示例代码:

// https://example.com/serviceworker.js
this.onpush = function(event) {
  console.log(event.data);
  // From here we can write the data to IndexedDB, send it to any open
  // windows, display a notification, etc.
}
 
// https://example.com/webapp.js
navigator.serviceWorker.register('serviceworker.js').then(
  function(serviceWorkerRegistration) {
    serviceWorkerRegistration.pushManager.subscribe().then(
      function(pushSubscription) {
        console.log(pushSubscription.endpoint);
        console.log(pushSubscription.getKey('p256dh'));
        console.log(pushSubscription.getKey('auth'));
        // The push subscription details needed by the application
        // server are now available, and can be sent to it using,
        // for example, an XMLHttpRequest.
      }, function(error) {
        // During development it often helps to log errors to the
        // console. In a production environment it might make sense to
        // also report information about errors back to the
        // application server.
        console.log(error);
      }
    );
  });

Push服务

(1)GCM/FCM

a0e09dbd36960b2abe4cebf42dfb627e256be289

注1:页面服务器(app server)使用HTTP/XMPP协议发送消息给Google GCM Connection Servers,GCM Connection Server 推送消息给相应的浏览器客户端。

注2:页面服务器(app server)发送消息的格式如下,

curl --header "Authorization: key=<YOUR_API_KEY>" --header "Content-Type: application/json" https://android.googleapis.com/gcm/send -d "{\"registration_ids\":[\"<YOUR_REGISTRATION_ID>\"]}"

其中,API_KEY 是创建Google API项目时生成的Server Key,GCM可通过它找到对应的sender_id。YOUR_REGISTRATION_ID 是订阅消息时生成的subscriptionId。

注3:Google Chrome是第一个支持Push API的浏览器,最先支持的版本为Chrome 42,这个时候Web Push Protocol还未完稿,所以其push service使用的GCM/FCM服务器并不支持标准Web Push ProtocolChrome 52版本实现了对标准Web Push Protocol的支持,此时,GCM/FCM也已支持标准Web Push Protocol

(2)Mozilla Push Service

Mozilla推送服务使用autopush作为它的push服务器。autopush是支持标准Web Push Protocol,它使用websockets与FireFox浏览器进行交互。

Mozilla推送服务的架构如下,

88646f58cce6f45802f9a2f34c8d2a571453b4a8

(3)第三方Push服务

Push API标准是允许替换endpoint的,即任何实现了Web Push Protocol的服务器都可作为endpoint,并且对使用者是透明的。

那么需要做哪些工作来实现我们浏览器自己的push服务呢?

我们先看看Web Push Protocol定义了那些内容:

 

    +-------+           +--------------+       +-------------+
    |  UA   |           | Push Service |       | Application |
    +-------+           +--------------+       |   Server    |
        |                      |               +-------------+
        |      Subscribe       |                      |
        |--------------------->|                      |
        |       Monitor        |                      |
        |<====================>|                      |
        |                      |                      |
        |          Distribute Push Resource           |
        |-------------------------------------------->|
        |                      |                      |
        :                      :                      :
        |                      |     Push Message     |
        |    Push Message      |<---------------------|
        |<---------------------|                      |
        |                      |                      |

                      Figure 1: WebPush Architecture

 

  1. 浏览器客户端连接push service
    需要使用https。
  2. 浏览器客户端订阅push message
    浏览器需要发送POST请求,到push service去订阅消息。比如,
        POST /subscribe HTTP/1.1
        Host: push.example.net
    订阅成功后push service的响应如下,
        HTTP/1.1 201 Created
        Date: Thu, 11 Dec 2014 23:56:52 GMT
        Link: </push/JzLQ3raZJfFBR0aqvOMsLrt54w4rJUsV>;
                    rel="urn:ietf:params:push"
        Link: </subscription-set/4UXwi2Rd7jGS7gp5cuutF8ZldnEuvbOy>;
                    rel="urn:ietf:params:push:set"
        Location: https://push.example.net/subscription/LBhhw0OohO-Wl4Oi971UG
    这个步骤中,push service需要能够根据不同情况返回不同的响应码,能够识别出同一客户端的请求。
  3. 页面服务器发送推送消息
    application server发送POST请求给push service,请求它将消息推送给浏览器客户端。请求如下,
        POST /push/JzLQ3raZJfFBR0aqvOMsLrt54w4rJUsV HTTP/1.1
        Host: push.example.net
        TTL: 15
        Content-Type: text/plain;charset=utf8
        Content-Length: 36

        iChYuI3jMzt3ir20P8r_jgRR-dSuN182x7iB

    push service的响应如下,
        HTTP/1.1 201 Created
        Date: Thu, 11 Dec 2014 23:56:55 GMT
        Location: https://push.example.net/message/qDIYHNcfAIPP_5ITvURr-d6BGt
    这个步骤中,push service需要能够根据不同情况返回不同的响应码,能够根据TTL保存推送消息一段时间并且含有Topic
    头部的新消息能替换旧消息,能够根据Urgency头部决定推送的优先级。
  4. 浏览器客户端接收订阅的推送消息
    浏览器客户端发GET请求请求接收推送消息,请求信息如下,
        HEADERS [stream 7] +END_STREAM +END_HEADERS
        :method = GET
        :path = /subscription/LBhhw0OohO-Wl4Oi971UG
        :authority = push.example.net
    push service允许保持请求,在application server发送推送消息之后,它将消息通过HTTP/2 server push将消息推送给浏览器客户端,响应信息如下,
        PUSH_PROMISE [stream 7; promised stream 4] +END_HEADERS
        :method = GET
        :path = /message/qDIYHNcfAIPP_5ITvURr-d6BGt
        :authority = push.example.net
        HEADERS [stream 4] +END_HEADERS
        :status = 200
        date = Thu, 11 Dec 2014 23:56:56 GMT
        last-modified = Thu, 11 Dec 2014 23:56:55 GMT
        cache-control = private
        link = </push/JzLQ3raZJfFBR0aqvOMsLrt54w4rJUsV>;
                     rel="urn:ietf:params:push"
        content-type = text/plain;charset=utf8
        content-length = 36
        DATA [stream 4] +END_STREAM
        iChYuI3jMzt3ir20P8r_jgRR-dSuN182x7iB
        HEADERS [stream 7] +END_STREAM +END_HEADERS
        :status = 200
    浏览器客户端接收到消息后,必须回应一个HTTP DELETE给push service,
        DELETE /message/qDIYHNcfAIPP_5ITvURr-d6BGt HTTP/1.1
        Host: push.example.net
    push service在收到浏览器客户端的回应后,必须返回一个204的响应给application server。如果没有收到回应,则会认为消息还未派发到浏览器客户端,
    那么push service应该继续重试,直至消息过期。
  5. 安全隐私方面的考虑
    push服务需要考虑安全相关的一些问题,比如推送的消息需要进行加密,用户设备信息不应包含在消息推送中,过多异常请求时能够拒绝服务,
    服务器日志不应暴露用户信息,等等。

从上面可以看看,push service至少需要具备的一些能力,比如,支持https,支持http/2 server push,支持POST,GET,DELETE请求,能够根据请求生成订阅信息,能够根据请求头部控制响应的优先级,能够存储消息一段时间。即服务器端有一定的实现成本。

Chrome 52已支持标准Web Push Protocol,第三方浏览器可参考其实现,即浏览器端的实现成本会变低。

浏览器支持

在国外,Google Chrome,Firefox,以及一些厂商的系统浏览器,比如三星,都已支持Push API。预计后面会越来越多的浏览器厂商支持。

在国内,还未有浏览器厂商支持Push API,估计这种情况会持续很长一段时间,门槛在于push service的缺失,风险在于内容监管。

从上面我们可以看到,Push API提供了一种页面服务器与页面交互的方法,是web page能成长为web app所需的关键能力。同时,我们也可看到,国内是不能使用GCM/FCM作为push service的,因为需要翻墙。如果我们自己搭建push service,服务器的软硬件都需要不小的成本,估计还未有浏览器厂商愿意承担这个成本。这对一些现有的推送服务商倒是一个十分难得的机遇,支持标准的Web Push Protocol,提供给所有浏览器厂商使用,做成国内最有影响力的推送服务,类似Google的GCM/FCM

如果Web Push一直得不到支持,对于浏览器SDK来说,可以提供一套非标准的API,允许客户端(比如,手淘)推送消息给浏览器的ServiceWorker,push service则由内置浏览器SDK的客户端自行去实现。

参考文档

Push API - W3C

Web Push Protocol - W3C

Your First Web Push Notification

Push Notifications on the Open Web

Web 推送技术

Web Push Interoperability Wins

Mozilla Push Service

Generic Event Delivery Using HTTP Push

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值