app的push流程分析

捋一捋关于APP 常用促活的一个手段:push。先来看一张图,撇开平台相关,基本流程大体和下图一致:
上图

  • 手机是如何显示“Push Notification”的?
  • 服务器怎么下发“Push Notification”到用户手机上?

之前我也对这俩问题蛮好奇的,后来对接了一个相关的需求,就摸索了下相关知识,现整理如下。

手机 APP(这里指商用APP,单机版 APP 不作考虑) 一般来说,都会添加一些手机服务商的 SDK(尤其是安卓手机,OV 华米,小米推送等,iOS 自己有另外的 APNS 服务),其目的就是让手机和服务商做成**“长链接”**,方便一些业务场景的实现。

下面一起看下 APP 如何与业务服务器配合,实现用户push notification 的。

  1. APP 向操作系统注册 push 通知
  2. 操作系统向服务商申请 device token
  3. APP 将拿到的 device token 与用户相关信息(如 userid)发送给业务服务器,由业务服务器进行记录
  4. 业务服务器将 userid 对应的 device token 拿到,配合业务文案,发给服务商
  5. 服务商通过长链接,将信息推给 APP,来实现“push notification”

整个流程还算比较清晰,但是在一个公司内部,如何把这套系统做的更简洁高效,还是需要花点时间的。

  1. 第三步中,用户将device token 以及自己的信息传给业务服务器,服务器需要进行记录,这个 device tokenn 通常来说每个 APP 独一份。业务服务器对每次 push 业务,都需要拿到每个人的 device token,然后加上业务文案,告诉服务商:“把这个文案推给device token”是 xxx 的设备上。
  2. 第四步中业务服务器向服务商请求的过程一般来说都不会很快,所以 qps 肯定高不到哪里去。这就导致运营的某次 push 通知需求可能持续很久很久。所以一般会借助消息队列来达到“异步”、“削峰”处理。
  3. 和公司双端的客户端确认了下,这个 device token 上报的时机是不一样的,安卓是会记录上报状态,遍历所有推送渠道如 OV 华米,小米等渠道是否上报过或者本地账号切换都会触发重新上传;iOS 是每次冷启动,用户登录登出会进行解绑,然后重新上报。
  4. 发送量(业务服务器管理)、到达率(对接服务商)、点击率(用户上报到业务服务器)等十个很冗杂的活儿,一般来说 push 本身是一个较强的促活手段,对很多 APP 来说是标配组件,还是值得花时间来完整做一套出来的。

业务服务器向服务商请求 push, 以OPPO 的 push 为例:

public function sendToOne($title, $desc, $data, $userToken, $expireTime=3600, $retry=true) {
        $ret = 1;
        if($this->accessToken == "" || $this->expire < time()) {
            $this->getAccessToken(true);
        }
        if($this->isenabled == 1) {
            $messages = array(
                'target_type' => 2,
                'registration_id' => $userToken,
                'target_value' => $userToken,
                'notification' => array(
                    //'app_message_id' => intval($data['id']),// 这个id不能重复,不然就不发了
                    'title' => $title,
                    'subtitle' => '',
                    'content' => $desc,
                    'click_action_type' => 1,
                    'click_action_activity' => 'com.xiaochang.easylive.OppoPushMessage',
                    'action_parameters' => array(
                        'uri' => json_encode(array(
                            'id' => intval($data['id']),
                            'title' => strval($title),
                            'url' => strval($data['url']),
                            'iscb' => strval($data['iscb']),)),
                            'source' => 'oppo',
                    ),
                    //'call_back_url' => '',
                    //'call_back_parameter' => strval($data['id']),
                )
            );

            if(!empty($expireTime)) {
                $message['off_line_ttl'] = $expireTime;
            }
            $postData = array('auth_token' => $this->accessToken, 'message' => json_encode($messages),);
            $pushUrl = self::$pushUrl.'/server/v1/message/notification/unicast';
            $curl['url'] = $pushUrl;
            $curl['post'] = http_build_query($postData);

            list($code, $response) = $this->curlContent($curl);
            LogError("response code for {$userToken} : $code, response ".json_encode($response), "/tmp/oppopush.log");
            if($code == 0) {
                $response = json_decode($response, true);
                if($response["code"] == 29) {
                    $this->getAccessToken(true);
                    if($retry) {//取token然后再尝试发一次。
                        $ret = $this->sendToOne($title, $desc, $data, $userToken, $expireTime, false);
                    }
                } else if($response["code"] == 0) {
                    $ret = 0;
                } else if($response["code"] == 33) {//超限了,今天不走oppo推送了
                    global $user_redis;
                    $num = isset($response["data"]["permits"]) ? intval($response["data"]["permits"]) : 1;
                    $user_redis->init()->hset(self::IS_OPPO_OUT_OF_LIMIT, date("YmdH"), $num);
                    $this->isenabled = 0;
                }
            }
        }
        return $ret;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章