通过Django Channels设计聊天机器人WEB框架

这两个月都在忙着设计针对银联客服业务的智能聊天机器人,上一周已经交完设计报告,这一周还和部门同事一起分享了系统设计及运行效果。因为时间的关系,系统原型我使用了Flask+jQuery的组合,感觉用以原型可以,上线使用存在性能拓展瓶颈。最近技术调研发现Django框架中自带了实时通信的工具包Channels,网上评价不错,因此测试使用并记录。

在本文中,我们将通过Django Channels打造一个聊天机器人的WEB框架,主要实现前后端的信息交互。
参考文档

Django Channels介绍

首先要理解Django现有的请求响应策略是这样的:浏览器发出请求,Django服务器接受请求后通过路由匹配该请求到某个视图,视图将会返回一个响应并由服务器发送回浏览器。类似的请求响应在Flask实现也是如此。对于一般性的网页浏览(比如新闻阅读),这样的响应机制是没有问题的,但对于需要一个保持不断会话的请求来说,这是行不通的,因为Django的声明周期只能存在一个请求中,它不能让服务器在没有请求的情况下不断地发送数据岛浏览器客服端。这样的场景目前正在不断地涌现,例如在线聊天室,会话机器人,以及最近很流行的微服务应用。
Channels改变了Django的工作方式,让它实现了一种包括通道、消费者和worker的worker监听的模式,所有消费者都会分配有单独的通道,worker监听通道的消息,确保消息到来时能进行处理。为了确保上述机制运行,Channels需要有三个工作层:

  1. 接口服务器,Django和用户(浏览器)之间通信的桥梁,包括一个实现WSGI协议的适配器和一个独立的websocket服务器。
  2. 通道后端, 在接口服务器和worker之间传递消息,由插拔式的python代码和存储组成,存储可以是内存、数据库或者redis,推荐使用redis,兼具其余两者的优点。
  3. worker,监听所有channel,当有新消息到来时候唤醒功能函数。

Channels可以让Django的框架变得更为可靠和可拓展,整个通信的服务器数可以按需拓展,至少保证一台协议服务器和一台工作服务器即可。使用Channels后,你不再需要组织code去为异步调用,Channls已经将一切都已经帮你准备好。
关于Channels的介绍,我推荐大家看一下第二篇参考文档,虽然全英文的,但是看起来不是很吃力,作者很是体谅我等词汇量不够的读者啊。

实验教程

本篇教程的开发环境如下,默认大家已经准备好。关于Windows redis的安装可以参见我之前的博文

  • Windows7
  • Python2.7.13
  • pyCharm2016.2
  • redis 3.0.53 Windows x64
    本实验的目的是搭建一个用于聊天机器人的WEB交互框架,可以直接拉到最下方看实现效果。
    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 **参数

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来对管道中的消息进行监控

修改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
        })
    })

三个函数可以认为是对三个管道的工作函数,这三个管道在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添加相应的网页前端代码,确保用户能发送请求到服务器:

<!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;">发送</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="//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

运行效果如下:


本文原创,转载前请注明出处
相关的代码上传到github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容