iOS 點贊功能高併發的思考

Hsusue發表於2018-08-02

前言

image.png
微博上熱點的點贊數數W。伺服器遇到這麼高併發請求壓力肯定很大。資料庫要怎麼寫入這些點贊就不探究了(真是個大工程,如果每接收一條就寫入一次),只探究iOS手機端怎麼處理點贊功能。

本文將參考微信和微博,猜測其實現,並探究如何處理點贊功能。

微信朋友圈的點贊數相對來說少。微博的點贊數能達五位數。其應該採用了不同的策略。


先從巨集觀上思考這個問題。

分為兩類 什麼時候獲取伺服器最新點贊數 點贊按鈕與網路的互動

  • 手機端不會對每一條訊息實時跟蹤點贊數,在某些情景下才會請求資料(進入詳情頁等)。
  • 如果每次點選 點贊/取消點贊 按鈕,就傳送一次請求,使用者期望是最後一次操作的效果。但是若遇到網路問題,最終寫入資料庫的值就無法控制了。那麼按鈕事件應該如何處理呢?

什麼時候獲取伺服器最新點贊

  • 先研究微博

    微博獲取點贊數
    對於別人的微博。 可以看到一開始是採用本地儲存的贊數,點進去詳情頁後才會向網路獲取一次點贊數,並且更新本地儲存的。(僅發起一次)

    針對自己發的微博就不研究了,萬年不發一條微博,印象中是收到點贊會收到推送的。

  • 再研究微信

    1. 開啟朋友圈,先用本地的資料,但是當滑動到某一條別人發的朋友圈時。如果有新的贊,會顯示出來(有延遲,應該是網路請求滯後)。

    2. 再來看自己發的朋友圈,收到點贊紅點後,滑動下去發現這個贊就躺在那了,無延遲。如果檢視鎖定在自己發的朋友圈內(如下圖),點贊也會更新,因為有收到紅點動態。即收到紅點動態的時候,也會獲得點贊資訊,有必要的話重新整理UI。 別人對自己發的朋友圈首次點贊?

      首次點贊

    3. 對第二點再深入點。自己發的朋友圈,如果某個人點了贊,檢視完紅點後,這個人取消->再次點贊。微信不會顯示紅點。換言之,不屬於新動態。

    4. 點了別人的贊後,該條朋友圈有新動態也會重新整理點贊資訊。

    5. 點進詳情頁必定會重新整理。

    所以大膽猜測,對自己跟蹤的朋友圈,會有個特殊的API獲知哪一條朋友圈的新動態。該API可能採用定時訪問,偶爾就訪問一次;聽杜優秀說可能是socket或者protobuf這種高速的http請求。

  • 總結

    1. 滑到cell處對該cell進行重新整理(一次)。
    2. 採用一個特殊的API,獲取跟蹤的新資訊。
    3. 進詳情頁必定獲取一次最新點贊資訊。

當然具體的情形遠比這複雜,以上也就是個人瞎猜的。

點贊按鈕與網路的互動

由於網路的不穩定性 以及 使用者可能隨時關閉App導致未成功發出請求,使用者期望的點贊狀態與資料庫的資料無論如何也不能保證一定一致。

但是如何儘可能地實現一致呢?針對這個問題提出個人的愚見。

還是先從微信和微博入手思考。

  • 無網狀態下

    微博UI不變狀態,當然也不發出請求。

    iOS 點贊功能高併發的思考

    微信UI有點贊效果

    一開始是沒有點讚的,最終UI設為點贊。

    順便說說結果,連上網後詳情頁發現確實設了點贊。

    但是如果關閉微信,再開啟,會發現點贊不見了。

iOS 點贊功能高併發的思考

  • 很差的網路狀態下

    2G網路實測,三條微博從無點贊狀態 按了好幾次點贊又取消,退出App連4G後開啟看伺服器的狀態,發現有些點了贊,有些沒有。即難以確定。

    2G網路實測,微信點贊後取消點贊,UI突然變成點贊狀態了。應該是請求成功後重新整理本地資料和UI。

iOS 點贊功能高併發的思考

  • 如何實現比較合理

以下純屬個人愚見,如果有更好的實現方法,歡迎大佬提出來。

首先是 UI如何顯示

先說個簡單萬能的方法。

有時候,使用者有意識發起的網路請求,我們都會顯示“載入中”甚至不讓使用者點選。然後在請求成功處理資料完成後,再讓使用者點選。例如登入介面,點選登入後,我們會顯示"登入中",並且禁止使用者進行其他操作。類比著,點贊後,不讓使用者進行其他操作,顯示“發起請求中”。成功就更換按鈕狀態,失敗提示"點贊失敗/取消點贊失敗"。這樣雖然能完全避免高併發以及期望值不一樣但使用者不知道的情況。

然而,這種方法使用者體驗並不好。但是如果選擇了使用者體驗,帶來的問題是使用者或許無聊會一直點按鈕,高併發請求,對手機記憶體和伺服器都造成了巨大壓力。也有可能最後寫入伺服器的資料並不是使用者期望值。但主流App都會為了使用者體驗,承擔這些後果。

所以除了在伺服器那加進處理外,手機端也有必要減少高併發的壓力。

在這我更傾向於微信的做法,讓按鈕二級view顯示,這就減少了使用者誤點贊後取消點讚的可能。而且也給了時間完成網路請求。

還有一種想法是,先讓UI更改,請求失敗就改回去,並且提示“請求失敗”。 微信裡會小紅點提示“點贊未傳送”。應該是類似的。

然後是 什麼時候要發起網路請求

微博那個我並沒有看懂,有網情況下,每次點選都會發起請求,然後隨緣看最後一次的結果嗎?

微信的點贊請求應該是馬上發出的,但取消點贊很難說。因為2G時,點贊後馬上取消點贊,最終結果卻是點贊。

每次點選都傳送是最容易造成高併發期望值偏差 的操作。 所以不建議每次點選都傳送這種隨緣法。

  • 方法一

猜測微信裡應該是更改第一次請求,請求期間點選按鈕都只是更改個樣子看看,並且請求成功後重新整理本地資料和UI。然後才允許下一次請求。

那麼就會有一個問題,微博上剛剛傳送點贊請求,然後又點進了詳情頁。所以在點跳轉之前判斷,沒有請求完成就在詳情控制器中註冊了一個通知。當請求成功後,傳送通知,通知詳情控制器重新整理資料。

猜測程式碼如下

NSMutableArray *isRequestingArray;//儲存每一個cell 是否在發起網路請求的BOOL

- (void)btnClick:(UIButton *)btn atIndexPath:(NSIndexPath *)indexPath {
    // 更改btn的樣子
    btn.selected = !btn.selected; 
    // 還要修改本地的資料 點選進詳情頁要看到狀態變了
    // 最後根據判斷髮起網路請求
    if (![_isRequestingArray[indexPath.section][indexPath.row] boolValue]) { // 無其他請求
        //設定正在請求
        _isRequestingArray[indexPath.section][indexPath.row] = @"1"; 
        __weak typeof(self) wself = self;
        [PraiseAPI startWithSuccessBlock:^(__kindof BaseRequest *request){
            __strong typeof(wself) sself = wself;
            sself->isRequestingArray[indexPath.section][indexPath.row] = @"0";
            // 更改btn的樣子
            // 修改本地的資料
            // 傳送通知
        } failureBlock:^(__kindof BaseRequest *request, NSError *error) {
            __strong typeof(wself) sself = wself;
            sself->isRequestingArray[indexPath.section][indexPath.row] = @"0";
            // 恢復btn的樣子
        }];
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NextViewcontroller *nextVC = [[NextViewcontroller alloc] init];
    if ([_isRequestingArray[indexPath.section][indexPath.row] boolValue]) {
        //正在請求  nextVC新增通知
    }
    [self.navigationController pushViewController:nextVC animated:YES];
}
複製程式碼
  • 方法二 但是微博那些可能會手誤點了贊,於是馬上就取消點贊。這時候採用第一種方法就不太好了。這時候我們就要採用延遲傳送請求的策略。

若點選後2s內無再次點選,並且和原狀態不一樣,才發起網路請求。在檢視退出介面的時候也馬上發起網路請求。

  • 方法三 聽某位朋友說,介面裡加個當前時間進去。伺服器按最後一次時間來寫入。問題是並沒有解決高併發問題。

想得還不是很完善,希望能拋磚引玉。

相關文章