這兩個月都在忙著設計針對銀聯(lián)客服業(yè)務的智能聊天機器人,上一周已經交完設計報告,這一周還和部門同事一起分享了系統(tǒng)設計及運行效果。因為時間的關系,系統(tǒng)原型我使用了Flask+jQuery的組合,感覺用以原型可以,上線使用存在性能拓展瓶頸。最近技術調研發(fā)現(xiàn)Django框架中自帶了實時通信的工具包Channels,網上評價不錯,因此測試使用并記錄。
在本文中,我們將通過Django Channels打造一個聊天機器人的WEB框架,主要實現(xiàn)前后端的信息交互。
參考文檔
Django Channels介紹
首先要理解Django現(xiàn)有的請求響應策略是這樣的:瀏覽器發(fā)出請求,Django服務器接受請求后通過路由匹配該請求到某個視圖,視圖將會返回一個響應并由服務器發(fā)送回瀏覽器。類似的請求響應在Flask實現(xiàn)也是如此。對于一般性的網頁瀏覽(比如新聞閱讀),這樣的響應機制是沒有問題的,但對于需要一個保持不斷會話的請求來說,這是行不通的,因為Django的聲明周期只能存在一個請求中,它不能讓服務器在沒有請求的情況下不斷地發(fā)送數(shù)據(jù)島瀏覽器客服端。這樣的場景目前正在不斷地涌現(xiàn),例如在線聊天室,會話機器人,以及最近很流行的微服務應用。
Channels改變了Django的工作方式,讓它實現(xiàn)了一種包括通道、消費者和worker的worker監(jiān)聽的模式,所有消費者都會分配有單獨的通道,worker監(jiān)聽通道的消息,確保消息到來時能進行處理。為了確保上述機制運行,Channels需要有三個工作層:
- 接口服務器,Django和用戶(瀏覽器)之間通信的橋梁,包括一個實現(xiàn)WSGI協(xié)議的適配器和一個獨立的websocket服務器。
- 通道后端, 在接口服務器和worker之間傳遞消息,由插拔式的python代碼和存儲組成,存儲可以是內存、數(shù)據(jù)庫或者redis,推薦使用redis,兼具其余兩者的優(yōu)點。
- worker,監(jiān)聽所有channel,當有新消息到來時候喚醒功能函數(shù)。
Channels可以讓Django的框架變得更為可靠和可拓展,整個通信的服務器數(shù)可以按需拓展,至少保證一臺協(xié)議服務器和一臺工作服務器即可。使用Channels后,你不再需要組織code去為異步調用,Channls已經將一切都已經幫你準備好。
關于Channels的介紹,我推薦大家看一下第二篇參考文檔,雖然全英文的,但是看起來不是很吃力,作者很是體諒我等詞匯量不夠的讀者啊。
實驗教程
本篇教程的開發(fā)環(huán)境如下,默認大家已經準備好。關于Windows redis的安裝可以參見我之前的博文。
- Windows7
- Python2.7.13
- pyCharm2016.2
- redis 3.0.53 Windows x64
本實驗的目的是搭建一個用于聊天機器人的WEB交互框架,可以直接拉到最下方看實現(xiàn)效果。
note: 下面的代碼運行需要redis服務開啟了6379端口正常運行。
Step1:安裝第三方工具包,建議通過pip install -r的方式安裝。
asgi-redis==1.0.0
asgiref==1.0.0
attrs==16.3.0
autobahn==0.17.1
Automat==0.5.0
channels==1.0.3
constantly==15.1.0
daphne==1.0.3
Django==1.10.5
incremental==16.10.1
msgpack-python==0.4.8
redis==2.10.5
six==1.10.0
Twisted==17.1.0
txaio==2.6.1
zope.interface==4.3.3
txredisapi==1.4.4
Step2:新建django項目“example_channels”,建議用pycharm新建很方便
Step3: 分別在以下目錄新建文件:
example_channels/example 新增consumers.py,urls.py,新增templates/example子目錄,并在該子目錄下新增chat.html。
example_channels/example_channels中新增routing.py。完整的目錄結構如下:
接下來的內容是如何修改上述文件使得所設計的框架能正常運行。
Step4:修改配置文件,增加channels的配置,你可以認為這是配置Channels的管道
首先修改example_channels/example_channels/setting.py的**INSTALLED_APPS **參數(shù)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels',
'example',
]
并增加配置CHANNEL_LAYERS :
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'asgi_redis.RedisChannelLayer',
'CONFIG': {
'hosts': [('localhost', 6379)],
},
'ROUTING': 'example_channels.routing.channel_routing',
}
}
Step5:增加worker來對管道中的消息進行監(jiān)控
修改example_channels/example/consumers.py文件,增加下面的代碼:
from channels import Group
import json
def ws_connect(message):
Group('users').add(message.reply_channel)
message.reply_channel.send({
'text': json.dumps({
'msg': u"你好,很高興為你服務。",
'talk': False
})
})
def ws_disconnect(message):
Group('users').discard(message.reply_channel)
def ws_receive(message):
data = json.loads(message['text'])
message.reply_channel.send({
'text': json.dumps({
'msg': u"我正在思考你的問題{%s}" % data["text"],
'talk': True
})
})
三個函數(shù)可以認為是對三個管道的工作函數(shù),這三個管道在Step6中進行了定義,分別是connect、disconnect、receive,這三個管道代替view起作用。
Step6:在example_channels/example_channels/routing.py中增加管道的定義
from channels.routing import route
from example.consumers import ws_connect, ws_disconnect,ws_receive
channel_routing = [
route('websocket.connect', ws_connect),
route('websocket.receive', ws_receive),
route('websocket.disconnect', ws_disconnect),
]
到Step6為止,我們完成了Channels的配置。我們將在Step7中增加一個頁面來測試上述的配置。
Step8:聊天機器人前端框架設計
首先,我們在example_channels/example/templates/example/chat.html添加相應的網頁前端代碼,確保用戶能發(fā)送請求到服務器:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test Django Channels</title>
</head>
<body>
<div style="text-align: center;margin-top: 50px">
<input id="message" type="text" style="width: 300px" placeholder="輸入消息">
<button id="send-message" style="width:80px;margin-left:20px;">發(fā)送</button>
</div>
<table id="show-message" style="width: 410px;margin: 0 auto;margin-top: 10px">
<tr>
<td style="text-align: center; border-bottom:1px dashed #000;"><strong>聊天記錄</strong></td>
</tr>
</table>
</body>
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
var socket = new WebSocket('ws://' + window.location.host + '/users/');
if (socket.readyState == WebSocket.OPEN) {
socket.onopen();
}
socket.onmessage = function (message) {
var data = JSON.parse(message.data);
updateLog("機器人", data["msg"]);
$("#message").val("");
$("#message").focus();
};
$("#send-message").click(function () {
var inputText = $("#message").val();
if (typeof(inputText) == "undefined" || inputText.length < 1) {
alert("沒有輸入信息");
}
else {
var msg = {"text": inputText};
socket.send(JSON.stringify(msg));
updateLog("你", inputText);
}
});
function updateLog(name, message) {
var chat = $("#show-message");
var ele = "<tr><td>" + name + ": " + message + "</td></tr>"
chat.append(ele);
}
</script>
</html>
然后需要在服務器上配置該網頁的路由,在example_channels/example_channels/urls.py中增加下面的代碼:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('example.urls', namespace='example')),
]
并在example_channels/example/urls.py中增加代碼:
from django.conf.urls import url
from example.views import chat
urlpatterns = [
url(r'^$', chat, name='chat'),
]
至此,基于Django Channels的聊天機器人設計完成。搞懂上述的配置尤其是Step8需要具備基本的Django知識,這里就不再描述。
測試代碼運行效果,在Terminal中輸入
D:\PWorkspace\example_channels>python manage.py runserver
運行效果如下:
本文原創(chuàng),轉載前請注明出處
相關的代碼上傳到github。