基於Server-Sent Event的簡單在線聊天室

Web即時通訊javascript

所謂Web即時通訊,就是說咱們能夠經過一種機制在網頁上當即通知用戶一件事情的發生,是不須要用戶刷新網頁的。Web即時通訊的用途有不少,好比實時聊天,即時推送等。如當咱們在登錄瀏覽 知乎時若是有人回答了咱們的問題,知乎就會即時提醒咱們,再好比如今電子商務的在線客服功能。這些能大大提升用戶體驗的功能都是基於Web即時通訊實現的。java

  • 普通HTTP流程
    1. 客戶端從服務器端請求網頁
    2. 服務器做出相應的反應
    3. 服務器返回相應到客戶端

而因爲HTTP請求是無狀態的,也就是說每次請求完成後,HTTP連接就斷開了,服務器和瀏覽器互相之間是徹底不可知的,只有下一次再發起一次請求 才能更新相應的信息。談到這裏咱們就不難想到,咱們能夠簡單的讓瀏覽器每隔一個週期就發起一次請求,這樣就能在必定程度上模擬實時效果了,這也就是輪訓,術語叫作Polling。redis

  • Polling流程
    1. 客戶端使用普通的http方式向服務器端請求網頁
    2. 客戶端執行網頁中的JavaScript輪詢腳本,按期循環的向服務器發送請求(例如每5秒發送一次請求),獲取信息
    3. 服務器對每次請求做出響應,並返回相應信息,就像正常的http請求同樣

經過輪訓的方式咱們就能夠相對即時的獲取信息。可是因爲輪訓的原理是使瀏覽器頻繁的向服務器發起請求,這在必定程度上會形成性能效率問題。爲了優化 這些性能問題,人們又想到了一種方法。那就是在服務器接收到請求的時候不理解返回,而是隻有當有數據變化(或者超時)的時候才返回。這樣一來,咱們就能夠 利用一次請求最大可能的保持鏈接的有效性,大大的減小了Polling中的請求次數。這個方法叫作長輪訓,也叫作Long-Polling。數據庫

以上方法是實現Web實時通訊的經常使用方法。固然在HTML5出來以後,咱們就有更好的選擇啦。在HTML5中,咱們可使用SSE或者是WebSocket。SSE的全稱是Server Send Event,聽名字就很好理解啦。也就是由服務器來推送數據。看到這裏是否是興奮呢?其實不少狀況下,咱們只須要這種簡單的功能:由服務器推送數據到瀏覽器。好比推送比賽信息、股價的變化等等。flask

若是SSE還不能知足咱們的需求的話,咱們徹底就可使用WebSocket啦。當使用WebSocket時,瀏覽器和服務器之間就創建了一個全雙工通道,互相均可以發送消息,徹底的作到了及時,就像使用tcp socket同樣。瀏覽器

  • SSE和WebSocket的簡單對比:
    • WebSocket是全雙工通道,能夠雙向通訊,功能更強;SSE是單向通道,只能服務器向瀏覽器端發送。
    • WebSocket是一個新的協議,須要服務器端支持;SSE則是部署在HTTP協議之上的,現有服務器軟件都支持。
    • SSE是一個輕量級協議,相對簡單;WebSocket是一種較重的協議,相對複雜。

到這裏咱們就基本瞭解了一些事先Web實時通訊的機制,下一節中,咱們將使用SSE實現一個簡單的在線聊天室。緩存

基於SSE的在線聊天室的實現

在線聊天室推送消息有不少種方式,在這門課程中咱們使用SSE來實現。爲了方便接收消息,咱們藉助Redispub/sub功能來接收和發送消息。Web服務器端咱們將使用Flask實現。若是對Flask不是很熟悉,也能夠在實驗樓學習相關的Flask課程。(http://www.shiyanlou.com/)服務器

1. SSE 的工做方式

在上面的課程中,咱們瞭解到SSE是基於HTTP實現的,那麼瀏覽器怎麼樣知道這是一個服務器事件流呢?其實很簡單啦,就是將HTTP的頭部Content-Type設置成text/event-stream就能夠了。其實SSE,就是瀏覽器向服務器發送一個HTTP請求,而後服務器不斷單向地向瀏覽器推送"信息",這些信息的格式也很是簡單,就是前綴data:加上發送的數據內容,而後以\n\n結尾。session

2. Redis中的訂閱功能

Redis是很流行的一個內存數據庫,能夠用於實現緩存,隊列等服務。在這門項目課程中咱們將使用的Redis的發佈/訂閱功 能。簡單來講,咱們所謂訂閱功能就是咱們能夠訂閱一些頻道,而後當這些頻道有新的消息的時候咱們就能夠自動接收這些信息。當服務器接收到瀏覽器POST過 來的消息的時候,會將這些信息發佈到特定的頻道中。接着咱們以前訂閱了這些頻道的客戶端就回自動收到這些消息,最後咱們就將這些消息經過SSE推送到客戶端。app

3. 實現

通過上面的分析,整個聊天室的流程已經很清晰啦。下面經過源代碼註釋的方式進行分析吧。在/home/shiyanlou目錄下,建立目錄code,而後在該目錄下建立源文件app.py

# 消息生成器
def event_stream():
    pubsub = r.pubsub()
    # 訂閱'chat'頻道
    pubsub.subscribe('chat')
    # 開始監聽消息,若是有消息產生在返回消息
    for message in pubsub.listen():
        print message
        # Server-Send Event的數據格式以'data:'開始
        yield 'data: %s\n\n' % message['data']


# 登錄函數,首次訪問須要登錄
@app.route('/login', methods=['GET', 'POST'])
def login():
    if flask.request.method == 'POST':
        # 將用戶信息記錄到session中
        flask.session['user'] = flask.request.form['user']
        return flask.redirect('/')
    return '<form action="" method="post">user: <input name="user">'


# 接收javascript post過來的消息
@app.route('/post', methods=['POST'])
def post():
    message = flask.request.form['message']
    user = flask.session.get('user', 'anonymous')
    now = datetime.datetime.now().replace(microsecond=0).time()
    # 將消息發佈到'chat'頻道中
    r.publish('chat', u'[%s] %s: %s' % (now.isoformat(), user, message))
return flask.Response(status=204)

 

 

詳細代碼請登陸實驗樓http://www.shiyanlou.com/courses/?course_type=project&tag=all

4. 運行

因爲使用了Redis,因此須要安裝redis服務器,經過如下步驟就能夠將redis服務器,以及相關的依賴包安裝好。

$ sudo apt-get update
$ sudo apt-get install redis-server
$ sudo service redis start
$ sudo pip install redis
$ sudo pip install flask

 

安裝完成之後,運行,而後經過瀏覽器訪問http://127.0.0.1:8989就看到效果啦.

另有其餘項目課的詳細講解和內容歡迎登錄實驗樓http://www.shiyanlou.com/courses/?course_type=project&tag=all

官方網站:實驗樓 http://www.shiyanlou.com

相關文章
相關標籤/搜索