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;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章