今天,我們的目標是使用Django,Redis,和Socket.IO建立一個即時的聊天室。雖然幾乎所有的Web應用程式都可以建立一個聊天室的。這篇文章將以較高的水平告訴你如何將基於REST的應用程式轉換成一個即時的Web應用程式的。我會使用Django建立REST的部分,實際上自由地使用任何你舒服的語言/架構均可。接下來,讓我們跳進代碼,先列舉我們所需要的部分。
組成:
- Django 1.4+
- Redis 2.6.x (版本可選,但是建議使用)
- Redis-py 2.7.x (僅當你使用Redis時需要)
- Node.js v0.8.x
- Socket.IO v0.9.x
- Cookie v0.0.5
- 資料庫、sqlite、其他你覺得類似資料庫形式的 均可
你的使用的版本可能與我不同,我暫時未測試其他版本,全部使用當前最新穩定版本。如果你無法通過下面方法安裝,我已經編譯好Ubuntu的軟體包。你可以從評論中得到其他動作系統版本情況。
#https://docs.djangoproject.com/en/dev/topics/install/sudo apt-get install python-pipsudo pip install django #http://redis.io/downloadsudo apt-get install redis-server #https://github.com/andymccurdy/redis-pysudo pip install redis #https://github.com/joyent/node/wiki/Installing-Node.js-via-package-managersudo apt-get install python-software-propertiessudo add-apt-repository ppa:chris-lea/node.jssudo apt-get updatesudo apt-get install nodejs #https://github.com/LearnBoost/socket.ionpm install socket.io #https://github.com/shtylman/node-cookienpm install cookie
讓我們從Django Project開始
django-admin.py startproject realtime_tutorial && cd realtime_tutorialpython manage.py startapp coremkdir nodejs
執行完以上的代碼,django project就配置好了,接下來要做的是在settings檔案中設定資料庫。先建立一個空白資料庫。(這是一個settings file的例子。在我的app中添加了一個“core”然後配置templates和urls的路徑。你可以隨意更改settings中的配置資訊,但是要與你的app相對應。
Model
models很簡單,我們將要建一個包含user和text的表。如果你想讓他更複雜一些,可以添加chatroom等資訊。(為了簡單起見,這裡唯寫了兩個)
from django.db import modelsfrom django.contrib.auth.models import User class Comments(models.Model): user = models.ForeignKey(User) text = models.CharField(max_length=255)
這就是我們將要使用的model,接下來執行下面的syncdb代碼(第一行代碼),建立資料庫。然後建立幾個user來測試。(第二行代碼)
python manage.py syncdbpython manage.py createsuperuser Node Server With Socket.IO
這一部分將要介紹即時資訊的發送和擷取。使用Node.js建立一個依賴Socket.IO的app server,使用Redis 來做這項苦差事。在nodejs字典中,建立一個叫做“chat.js”的檔案,然後把它放在這裡:
var http = require('http');var server = http.createServer().listen(4000);var io = require('socket.io').listen(server);var cookie_reader = require('cookie');var querystring = require('querystring'); var redis = require('socket.io/node_modules/redis');var sub = redis.createClient(); //訂閱chat channelsub.subscribe('chat'); //配置socket.io來儲存Django設定的cookieio.configure(function(){ io.set('authorization', function(data, accept){ if(data.headers.cookie){ data.cookie = cookie_reader.parse(data.headers.cookie); return accept(null, true); } return accept('error', false); }); io.set('log level', 1);}); io.sockets.on('connection', function (socket) { //把資訊從Redis發送到用戶端 sub.on('message', function(channel, message){ socket.send(message); }); //用戶端通過socket.io發送訊息 socket.on('send_message', function (message) { values = querystring.stringify({ comment: message, sessionid: socket.handshake.cookie['sessionid'], }); var options = { host: 'localhost', port: 3000, path: '/node_api', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': values.length } }; //使用Django server發訊息 var req = http.get(options, function(res){ res.setEncoding('utf8'); //輸出錯誤資訊 res.on('data', function(message){ if(message != 'Everything worked :)'){ console.log('Message: ' + message); } }); }); req.write(values); req.end(); });});
首先,我們匯入並建立http server來監聽localhost 4000連接埠。然後訂閱Redis的 "chat" chanel。最後,只要我們在Django view中調用就可以了。
上次我們設定了Socket.IO能在本地領域使用cookie的那個Django設定,這能讓我們通過socket.handshake.cookie去訪問cookie資料。能讓我們怎樣得到使用者的session會話。
我們設定Socket.IO的cookies之後我們才能持有很多事件,第一個事件是Redis 發布通道,當我們的使用者注意到一個新的訊息已經被通知它將發送訊息給所有網站的用戶端。
另一個事件是當用戶端通過Socket.IO發送一個資訊,我們使用字串查詢(queryString)模組去建立一個query查詢才能被發送到我們的Django服務。我們的Django服務在本地連接埠3000將會運行但你能改變了那個需求。路徑設定成/node_api那個URL我們將不久建立在Django旁邊。一旦我們發送queryString我們等待的Django就會儲存相關組件並給我們返回"Everything worked(都在工作)"。如果我們沒有得到返回給我們的輸出錯誤就關閉節點控制台
一個關於不使用Redis的節點
你真的完全沒必要為這項目使用Redis,我發現它將是一個好的學習體驗,如果你想分流Redis你可以建立一個通道,使用運算式或一些其它類庫,在這上面的代碼會從Django裡接收一個訊息當一個注釋被儲存時,然後你能通過Socket.IO添加註釋給所有的用戶端
模板
這就是我們所有HTML和javascript被放置的地方,它允許我們顯示注釋和互動我們的Node服務
Realtime Django
{% for comment in comments %}
- {{comment.user}}: {{comment.text}}
{% endfor %}
在上面我們用socket.IO在本地連接埠4000串連我們的節點服務。當從伺服器得到了一個資訊我們就在目錄和添加它到我們注釋列表裡做了些轉義,當我們想要發送一個資訊我們就對輸入盒子裡做了相應的13(按下一個鍵)的按鍵檢查。一旦那被按下後我們就發出資訊給伺服器使其被持有。一旦它被Django儲存到我們的資料庫我們就得到一個"message"事件將其添加到我們的會話列表裡
我們的Django顯示我們在下一步將載入一個"comments"變數,因此我們那樣設定並遍曆下面所有的迴圈。這部分僅僅是當頁面初始載入時使用了,我們的javascript將添加資料給這個目錄作為一個新的資料來自我們的Node服務
View
開啟realtime_tutorial/core/views.py,然後像我一樣編輯:
from core.models import Comments, User from django.shortcuts import renderfrom django.http import HttpResponse, HttpResponseServerErrorfrom django.views.decorators.csrf import csrf_exemptfrom django.contrib.sessions.models import Sessionfrom django.contrib.auth.decorators import login_required import redis @login_requireddef home(request): comments = Comments.objects.select_related().all()[0:100] return render(request, 'index.html', locals()) @csrf_exemptdef node_api(request): try: #通過sessionid獲得 user session = Session.objects.get(session_key=request.POST.get('sessionid')) user_id = session.get_decoded().get('_auth_user_id') user = User.objects.get(id=user_id) #建立Comment Comments.objects.create(user=user, text=request.POST.get('comment')) #建立後就把它發送到聊天室 r = redis.StrictRedis(host='localhost', port=6379, db=0) r.publish('chat', user.username + ': ' + request.POST.get('comment')) return HttpResponse("Everything worked :)") except Exception, e: return HttpResponseServerError(str(e))
讓我們看看這裡發生了什麼。home是一個標準的view檔案。使用select_related來獲得每一個comment的username,而不是在頁面第一次載入的時候,就返回一個comment的query集合。
第二個就是我們Node app發送資訊的view。我們從POST中擷取sessionid,然後通過解碼獲得userid。確定user存在後,就可以建立comment了。現在吧username 和 comment 發送到 Redis server。最後,把資料發送到這裡叫做"chat"的頻道。
URLs
這裡比較簡單,因為我們將要使用Django內建的views和template。
from django.conf.urls import patterns, include, url urlpatterns = patterns('', url(r'^$', 'core.views.home', name='home'), url(r'^node_api$', 'core.views.node_api', name='node_api'), url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'admin/login.html'}, name='login'), url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'),)
Start It Up!
開啟servers。
python manage.py runserver localhost:3000 #In a new terminal tab cd into the nodejs directory we created earliernode chat.js
我把代碼放到github。如果你想把它做得更好,就允許user建立、加入聊天室。你也可以使用PHP或者Rails開發。
如果你有什麼問題,請在評論處寫下或聯絡我。