服務端是如何主動推送信息到客戶端的?

像天氣類,新聞類 app,服務端是怎麼把信息發送給每一個客戶端的?
在我的認識里,打開網頁或app去查詢或者刷新時,客戶端向伺服器發出請求然後返回數據。 而服務端主動推送到客戶端是怎麼一個過程呢?
(特別不明白應用伺服器如何確定每一個應用所在的設備,服務端把消息推到哪?客戶端又不像伺服器有一個固定的地址)

各位大神的意思是:現在的app,仍然是以客戶端主動向服務端請求為主? 類似中國移動給全網信號內所有手機發消息這種不存在?

我這問題很多餘么


@Hugo 的答案更是只是傳統的拉取消息模式(Pull),不能算是推送(Push)。

總結下題主的問題:

  • 服務端主動推送到客戶端是怎麼一個過程呢
    • 應用伺服器如何確定每一個應用所在的設備
    • 服務端把消息推到哪,客戶端又不像伺服器有一個固定的地址
  • 現在的APP是如何使用消息推送的。

服務端主動推送到客戶端是怎麼一個過程


目前服務端給客戶端推送,普遍做法是客戶端與服務端維持一個長連接,客戶端定時向服務端發送心跳以維持這個長連接。當有新消息過來的時候,服務端查出該消息對應的TCP Channel的ID並找到對應的通道進行消息下發。


這只是最基本的通訊模型,在此之上,有衍生出針對消息的發布/訂閱模型,客戶端可以訂閱某一個Topic,服務端根據Topic找到對應的Channel進行批量的消息下發。所有的客戶端隱式的訂閱的all這個opic,所以『類似中國移動給全網信號內所有手機發消息的模式』亦可以理解『廣播消息』,即給all這個Topic發消息。

在此基礎上,又要幾個開源的協議來幫你定義這個事情,比較有名的如MQTT協議(剛好這幾天我看到MQTT協議的中文翻譯,分享給大家),Github上搜索MQTT可以找到對應的開源的協議實現項目,有興趣可以自行搜索。


現在的APP是如何使用消息推送的


實際上,主流的移動平台都已經有系統級的推送產品,Android上有GCM,iOS上有APNS,WinPhone有MPNS。但因為某些你懂的原因,GCM在國內處於不可用狀態,所以國內的移動應用採用另外一種做法---在後台運行一個Service,維持應用於服務端的TCP長連接,以達到實時消息送達的效果。


但是在移動端如何穩定的維持長連接是一件非常複雜的事情,前面說了,客戶端通過定時發送心跳信號(Heartbeat)以維持與服務端的長連接,但是,如果心跳的頻率太頻繁,移動設備耗電增加,心跳間隔太久又可能使得連接被斷開。並且普遍認為移動設備處於一個多變的網路環境中,WIFI,2G,4G切換,基站切換都會引起網路變動,在不同網路環境下的心跳頻率,與網路變動的重連動作,都需要大量的數據統計分析總結出來。


這僅僅是客戶端的難題,在如今移動應用動輒成百上千的用戶量的情況下,如何維護如此多的長連接,如果應對大規模的消息下發以及後續針對下發消息的各種統計動作都是技術難點。


再者,現在應用一般都是全平台的,發送一條消息,應該同時發送給Android,iOS, WinPhone,Android端走自建的TCP長連接通道,iOS與WinPhone走自家的系統推送通道。那麼意味著你服務端要維護這三套推送系統。


顯然對於小團隊,要獨自建立一套消息推送系統的難度非常大,所以市場上湧現出很多優秀的推送產品,幫開發者聚合這些推送方式,並提供統一的推送介面。國外如 Urban Airship, Parse等, 國內有JPush,百度雲推送,信鴿,LeanCloud等。(比較遺憾的是,非常優秀的Parse已經被Facebook宣布停止開發,並將於1年後關閉)


現在除了體量非常大的公司自建推送系統外,一般普通公司都是使用第三方推送服務,以上所有的第三方推送服務,基礎功能都是免費的,如果有條件的話,建議可以集成多家服務,A/B測試對比下推送效果,本人從事與以上某推送公司,在此就不評價各家產品好壞了,關於推送相關的問題可以私信問我 :)


Other

如果感(dian)興(zan)趣的人多的話,我可以分享下推送方面的相關案例。


1.輪詢,定時請求
2.長連接


現在手機主流的幾個平台都有自家提供Push的功能,讓應用開發者能夠很方便地把Push能力集成到應用中。

Android 上有 GCM (Google Cloud Messaging)
iOS 上有 APNs(Apple Push Notification service)
Windows Phone 上有 MPNs(Microsoft Push Notification service)。

但是由於Windows Phone的市場佔比不高,所以一般也就沒有人會專門做wp系統的推送。至於Android的GCM在國內基本上是不可用的。原因主要有以下兩點:
一、國內大部分Android手機都不帶Google服務,也就用不了GCM,這是主要的問題。
二、在國內Google的服務一般都不太穩定,原因你懂的。
所以現在在做消息推送的時候Android平台採用伺服器與設備直接拉一條長連接的方式實現功能,而IOS平台則採用蘋果自己的APNs服務。在分別說這兩個平台推送原理的時候,先回答一下題主關於伺服器如何先找到設備、再找到app的問題。每一個設備都有一個自己的設備號,而設備中的app又都有一個唯一的包名。所以伺服器只需要找到設備號與包名就可以定位到某個設備的某個應用,而這設備號與包名會一起構成一個標識符,叫做device_token,因此問題就簡化為把device_token與消息內容等信息交給伺服器,伺服器把內容發到唯一的device_token上。這就好像你在上海要通過順豐寄送一個快件兒給某某小區的某某房間,那麼快件兒首先會郵遞到順豐公司在北京的總站點,之後再根據小區的地址投遞/路由到某某房間,這樣一個寄件過程就算完成了。在這裡,你要寄送的快件兒就是你要發的「消息」,送達房間相當於最終「接收消息的App」,順豐公司在北京的總站點相當於這裡提到的「設備」,送達房間的房間號就相當於這個環節裡面提到的「包名」。

接下來分別簡單說一下這兩個平台的推送實現原理。
首先是IOS平台,IOS的推送是通過蘋果自己的APNs服務進行的,用戶需要將device_token以及消息內容等推送信息交給APNs伺服器,剩下的均由蘋果自己來完成。詳細方法可以看我在知乎的另一個回答iOS 的 push 推送是如何實現的? - 李琰的回答,但是如果提供的device_token是失效的(app被卸載、系統版本升級導致device_token變化等情況)那麼推送過程就會被中斷,頻繁的斷線重連甚至會被APNs認為是一直DoS攻擊。詳情可以參考為什麼蘋果的推送,兩次推送之間間隔比較久的話,第二次推送會很慢? - 沙漠的回答
接下來是Android平台,Android平台在不使用GCM的情況下就需要將自己的伺服器或是第三方推送服務提供商的伺服器與設備建立一條長連接,通過長連接進行推送。但是不建議自己設置伺服器實現推送功能,一是因為成本太高(開發成本、維護成本),自己搭建的伺服器無論是穩定性還是速度上都比不了第三方推送服務提供商的效果。另一個是因為自己的數據量較小,使用第三方推送服務提供商可以用他們的維度進行推送,實現精準推送。友盟推送就是做的比較好的,可以根據用戶分群、地區、語言等多維度進行推送,最大程度減少對於用戶的干擾,僅把消息推送給相關用戶。友盟推送的優勢是什麼? - 李琰的回答
下圖是Android平台消息推送的簡單示意圖。

開發者通過第三方推送服務提供商將信息直接下發給需要的設備,第三方推送服務提供商與設備建立一條長連接通道,並且將消息路由到APP中(圖中的設備1與設備2),對於像設備3這種無網路連接或是沒有成功建立長連接通道的設備,會在設備3連網且推送消息沒有過期的情況下自動收到由第三方推送服務提供商推送過來的消息,保證消息不會丟失。


一般的伺服器Push技術包括:

  1. 基於 AJAX 的長輪詢(long-polling)方式,伺服器Hold一段時間後再返回信息;
  2. HTTP Streaming,通過iframe和&標籤完成數據的傳輸;
  3. TCP 長連接
  4. HTML5新引入的WebSocket,可以實現伺服器主動發送數據至網頁端,它和HTTP一樣,是一個基於HTTP的應用層協議,跑的是TCP,所以本質上還是個長連接,雙向通信,意味著伺服器端和客戶端可以同時發送並響應請求,而不再像HTTP的請求和響應

上述的1和2統稱為comet技術,這裡有個簡單的介紹:Comet:基於 HTTP 長連接的「伺服器推」技術

前些日子給項目網站加了後台通知的實時推送到前端顯示,用的是nodejs的http://socket.io,它是websocket的一個開源實現,對不支持websocket的瀏覽器降級成comet / ajax 輪詢,http://socket.io的良好封裝使代碼編寫非常容易。


其實基本上伺服器推送一般都是 客戶端請求的

不過在web2。0炒得火熱的時期,流行了一段時間的 真正意義上的伺服器短連接 推技術

說來簡單,在伺服器response 流關閉的時候,這個http連接就算over了

伺服器幹啥呢,就是寫個死循環,讓這個流一直不結束,也就是說永遠不告訴客戶端,大爺我給你發完了。

然後伺服器就可以有事沒事調戲客戶端了。當然瀏覽器上還是需要加些代碼設置

以前的網路聊天室據說就是這麼乾的

從成本來說,客戶端輪詢請求 伺服器壓力最小,但是不太敏感

tcp長連接呢,伺服器壓力大些,但是消息及時

這種老式的方案,屬於折中方案,但是挺扯淡的,需求不是特別符合還是別用了。


沒有Push,只有Pull。客戶端自己去取消息。國內安卓應用,如果沒有使用GCM,絕大多數沒有使用,那就起後台Service定時喚醒系統查詢,一個是為了保持心跳,一個是為了查詢消息。
這種機制非常浪費運營商的資源,也非常費電。
所以,建議把所有的應用設置都看清楚,盡量選擇非推送。尤其是天氣預報,手工查一下就行了,否則,那個所謂的墨跡天氣,5分鐘查一次,頻率比得上微信了。完全沒有必要,太費電了。要是趕上小區擁堵的情況,頻繁的PDP激活,建立RRC連接,再釋放,電池消耗特別大。而且還會發熱,用戶體驗會很糟糕。


天氣可以一個小時向伺服器問一次


題主限定了「服務端主動」, 那隻能長連接了。


2016年8月8日更新:
最近寫了一篇關於Android推送的文章,把推送的一些問題做了一下總結:

Android端外推送到底有多煩?

================================

對於iOS App來說,一般情況下(有例外情況),App不允許後台長期運行。所以當App被系統掛起或殺掉後,推送只能通過APNS送達。APNS通知到達手機後,由用戶喚起App,App運行起來後,就能夠啟動自己的長連接(如XMPP),完成進一步的推送。當然,APNS的推送速度肯定比不上自己的長連接,所以一般iOS上還一般採用偽推送的方式。App在退到後台後能短暫執行幾分鐘,長連接也會持續一段時間。如果在這段時間內推送可以通過自己的長連接到達,那麼App在客戶端產生一個本地推送(類似於系統產生的遠程推送)。

而在Android上,跟APNS對應的服務GCM,在國內無法使用。而各個手機廠商(如小米)自己的推送服務又無法覆蓋所有手機終端。所以,在Android上實現推送只能自己啟動Service維持一個長連接。正是因為安卓上很多應用都有後台長連接掛著,所以安卓手機更費流量,也更費電。

==================================
用手機打了上面兩段,實在太累了。找到台電腦,更新一下。

題主真正想問的可能是原理性的東西,而非工程性的。下面先正面回答一下題主的兩個問題:

應用伺服器如何確定每一個應用所在的設備,服務端把消息推到哪?客戶端又不像伺服器有一個固定的地址
--客戶端確實不一定有固定IP地址,但如果客戶端有長連接(TCP)連接到伺服器,那麼伺服器就知道當前處於連接狀態的所有TCP連接的客戶端地址。從編程的角度來講,伺服器維護了所有連接著的客戶端socket,只要往客戶端socket里寫數據,就能到達客戶端;從TCP原理來講,伺服器維護了每個TCP連接的發送窗口,這裡的關鍵是,伺服器知道發給每個客戶端的TCP包應該是什麼序號(seq),而處於連接狀態的客戶端也時刻準備著在自己的接收窗口裡面接收正確序號的TCP包。

現在的app,仍然是以客戶端主動向服務端請求為主?
--是的,主要以客戶端主動向服務端請求為主。但涉及到IM、實時對戰遊戲之類的應用場景,就一定需要長連接推送。像天氣類,新聞類 app,對推送實時性要求並不太高,採取定期拉取策略也是可以的(實際上實現難度要降低幾個數量級)。

另外,關於實現長連接推送需要考慮的一些工程性的問題,把想到的列出如下,有機會再深入探討。
(1)採用什麼協議?XMPP還是MQTT還是自定義二進位協議?是否像微信一樣,需要推送二進位數據(比如短語音和縮略圖數據)?
(2)如何保證後台長連接不死?像大公司的各個App,普通會採用互拉的手段保持後台運行。而最牛的還是微信,在各個手機系統上基本都算是「開掛」了。
(3)如何做才能真正不丟數據?涉及到系統的方方面面,比如消息的確認,客戶端和伺服器的數據同步,客戶端的數據存儲的事務保證,後台消息隊列如何設計保證不丟數據。如果是IM,離線數據如何處理?
(4)長連接的Keep Alive和連接狀態的檢測。比如XMPP相當於一個永遠解析不完的XML流,使用一個空格作為Keep Alive消息。
(5)在iOS上進行偽推送。前面已經提到過。還有如何利用iOS特性,用特殊APNS推送喚起App預先下載消息。
(6)長連接的安全性。驗證以及加密。
(7)是用各種雲推送還是自己實現?用哪家雲推送更好?


在Java中應該就是基於JMS的ActiveMQ了吧,我把消息發過去了你愛接不接!你愛在線不在線!
ps:今天下班買的肉夾饃是豬腳餡兒的,原來這世上還有那麼難吃的肉夾饃


Push的原理:

Push 的工作機制可以簡單的概括為下圖

圖中,Provider是指某個iPhone軟體的Push伺服器,這篇文章我將使用.net作為Provider。

APNS 是Apple Push Notification Service(Apple Push伺服器)的縮寫,是蘋果的伺服器。

上圖可以分為三個階段。

第一階段:.net應用程序把要發送的消息、目的iPhone的標識打包,發給APNS。

第二階段:APNS在自身的已註冊Push服務的iPhone列表中,查找有相應標識的iPhone,並把消息發到iPhone。

第三階段:iPhone把發來的消息傳遞給相應的應用程序, 並且按照設定彈出Push通知。

原文:http://blog.csdn.net/zhuqilin0/article/details/6527113


我最近發現一個專業做web實時推送的,叫Goeasy,我試了一下,中英文都很齊全,伺服器穩定,代碼也簡潔易懂,幾分鐘自己可以寫一個demo,大家可以試一下,網站是GoEasy | Free web message push service


建立tcp連接,定期發keep alive防止socket超時斷開。發消息直接發tcp數據包。

或者直接用第三方的push notification服務。原理類似,只不過平台統一keep alive省電。需要註冊在服務提供方註冊。


Android推送從伺服器到客戶端一體化解決方案(1)-socket長連接實現 - 寫更優雅的程序,像寫一手漂亮的字一樣。 - 知乎專欄


連接都是由客戶端發起的!(因為客戶端通常在子網下,沒有公網IP,根本沒辦法接受連接)

所謂『推送』的實現方式無外乎兩種:
一是基於長連接,客戶端發起連接,雙方維護這個連接,伺服器端有變動隨時拿這個連接通知客戶端。這就是正常意義上的『推送』。
二是基於短連接,客戶端輪詢發起短連接,詢問伺服器端變化。這實際上是『拉取』,並不是『推送』。

燃鵝,維護連接的成本太大了,除了即時性要求苛刻的場景之外,大家普遍採用了第二種方法。

PS: 中國移動那種真正意義的推送,是因為手機號唯一,服務端有能力向客戶端建立連接。以後IPV6上了,每台設備IP固定了,技術上是可行的,到時候就需要關心互聯網上的辣雞簡訊了~


網頁app是單純的app,天氣類app有服務,在後台會定期向伺服器訪問。這和windows更新的機制一樣。


可以看看最近寫的一篇關於推送架構和關鍵技術實現的文章,聊聊推送的架構及關鍵技術實現(上) - 知乎專欄


客戶端與服務端使用長連接,由服務端向客戶端主動push消息。這個TCP連接就是負責消息的傳輸,比較…


tcp長連接


幾個場景:

1. 顧客到窗口問:菜好了沒?
2. 窗口小二叫道:25桌,你要的包子的炒肝!
3. 服務員輕輕地站在你的身邊:先生,你要的法式麵包卷牛肉丸。
4. DU~,DIDAR,喂,請問XXX在嗎?好的,你的快遞!

嗯。暫時這麼多。


現在也在做推送這塊功能,要將一些圖片,文字,視頻等富媒體信息推送到android端顯示,不知道有什麼好的推薦方案不?看了百度雲推送和個推等,更多的還是推送消息通知


推薦閱讀:

用 thrift 或 gRPC 之類的框架做 app 和伺服器的通信合適嗎?
目前主流的伺服器有哪些?軟體和硬體?
公司在機房有幾台伺服器,進機房前應該做哪些防護措施呢?輻射會不會很大?
查看一個網站伺服器操作系統的最準確方法是什麼?

TAG:程序員 | 伺服器 | 客戶端 | 應用程序Application | 推送Push |