Django Channels 實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)實(shí)時(shí)聊天和消息推送功能
簡(jiǎn)介在很多實(shí)際的項(xiàng)目開(kāi)發(fā)中,我們需要實(shí)現(xiàn)很多實(shí)時(shí)功能;而在這篇文章中,我們就利用django channels簡(jiǎn)單地實(shí)現(xiàn)了點(diǎn)對(duì)點(diǎn)聊天和消息推送功能。
手邊有一個(gè)項(xiàng)目需要用到后臺(tái)消息推送和用戶之間一對(duì)一在線聊天的功能。例如用戶A評(píng)論了用戶B的帖子,這時(shí)候用戶B就應(yīng)該收到一條通知,顯示自己的帖子被評(píng)論了。這個(gè)功能可以由最基本的刷新頁(yè)面后訪問(wèn)數(shù)據(jù)庫(kù)來(lái)完成,但是這樣會(huì)增加對(duì)后臺(tái)服務(wù)器的壓力,同時(shí)如果是手機(jī)客戶端的話,也會(huì)造成流量的損失。于是,我們考慮使用websocket建立一個(gè)連接來(lái)完成這個(gè)功能。
但是django并不支持websocket,因此在一番尋找之后發(fā)現(xiàn)了django-channels這個(gè)項(xiàng)目,它允許Django項(xiàng)目不僅可以處理HTTP,還可以處理需要長(zhǎng)時(shí)間連接的協(xié)議 - WebSockets,MQTT,chatbots,業(yè)余無(wú)線電等等。
作者本人也接觸channels沒(méi)多久,為了搞這兩個(gè)功能看channels文檔看到自閉,最終簡(jiǎn)單實(shí)現(xiàn)了這兩個(gè)功能,特地記錄一下
一:安裝channels
如果使用的是django 1.9 及以上,在pip安裝channels時(shí)可以不加-U參數(shù)
pip install channels
安裝結(jié)束后,我們把channels作為一個(gè)app添加進(jìn)入我們的django項(xiàng)目,在settings.py中添加
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'Your-app', 'channels', ]
在這里,我們使用redis做為channels的通道后端,以便支持更多的功能,具體涉及到的一些功能在后文中會(huì)提及。于是我們還需要安裝一些依賴包以支持其正常工作
pip install channels_redis
然后在settings.py文件中添加
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [('127.0.0.1', 6379)],
},
# 配置路由的路徑
# "ROUTING": "exmchannels.routing.channel_routing",
},
}
ASGI_APPLICATION = 'exmchannels.routing.application'
二:點(diǎn)對(duì)點(diǎn)聊天
在項(xiàng)目目錄下新建一個(gè)文件,用來(lái)存放我們的channels代碼,為channel。在channel中新建一個(gè)comsumers.py文件,在其中新建一個(gè)ChatComsumer類用來(lái)處理我們聊天時(shí)的websocket請(qǐng)求。相對(duì)于建立一個(gè)聊天室,在這里不同的是我們?cè)贑hatComsumer中添加了一個(gè)chats來(lái)記錄每一個(gè)group中的連接數(shù)。以此根據(jù)這個(gè)連接數(shù)來(lái)判斷,聊天雙方是否都已連接進(jìn)入該個(gè)聊天group。
同時(shí),我們?cè)O(shè)定聊天組的命名形式為user_a的id加上下劃線_加上user_b的id,其中id值從小到大放置,例如:195752_748418
class ChatConsumer(AsyncJsonWebsocketConsumer):
chats = dict()
async def connect(self):
self.group_name = self.scope['url_route']['kwargs']['group_name']
await self.channel_layer.group_add(self.group_name, self.channel_name)
# 將用戶添加至聊天組信息chats中
try:
ChatConsumer.chats[self.group_name].add(self)
except:
ChatConsumer.chats[self.group_name] = set([self])
#print(ChatConsumer.chats)
# 創(chuàng)建連接時(shí)調(diào)用
await self.accept()
async def disconnect(self, close_code):
# 連接關(guān)閉時(shí)調(diào)用
# 將關(guān)閉的連接從群組中移除
await self.channel_layer.group_discard(self.group_name, self.channel_name)
# 將該客戶端移除聊天組連接信息
ChatConsumer.chats[self.group_name].remove(self)
await self.close()
ChatComsumer中的chats是一個(gè)字典,用來(lái)記錄每一個(gè)group中的連接數(shù)目。每當(dāng)一個(gè)客戶端訪問(wèn)正確的websocket url之后,都會(huì)調(diào)用connect()函數(shù),將該客戶端添加入其url中指向的一個(gè)group,同時(shí)向chats中添加該客戶端的信息。當(dāng)該客戶端斷開(kāi)連接時(shí),會(huì)調(diào)用disconnect()函數(shù),將該客戶端從group中移除,同時(shí)刪除它在chats中的記錄。
完成了連接和斷開(kāi)連接的處理之后,我們來(lái)進(jìn)行接收信息的處理
async def receive_json(self, message, **kwargs):
# 收到信息時(shí)調(diào)用
to_user = message.get('to_user')
# 信息發(fā)送
length = len(ChatConsumer.chats[self.group_name])
if length == 2:
await self.channel_layer.group_send(
self.group_name,
{
"type": "chat.message",
"message": message.get('message'),
},
)
else:
await self.channel_layer.group_send(
to_user,
{
"type": "push.message",
"event": {'message': message.get('message'), 'group': self.group_name}
},
)
async def chat_message(self, event):
# Handles the "chat.message" event when it's sent to us.
await self.send_json({
"message": event["message"],
})
在上述函數(shù)中,我們可以看到,當(dāng)接收到來(lái)自客戶端的websocket信息之后,我們首先判斷一下,這個(gè)聊天組中客戶端連接個(gè)數(shù)是一個(gè)還是兩個(gè)。如果連接個(gè)數(shù)為2,說(shuō)明聊天雙方都已經(jīng)連接到了該聊天組,因此可以直接向該group發(fā)送信息,這樣對(duì)方就可以直接收到信息;如果連接個(gè)數(shù)為1,說(shuō)明信息接受者還未進(jìn)入聊天組,我們便向其推送一條信息,包含group_name和信息內(nèi)容。
就這樣,我們完成了一個(gè)點(diǎn)對(duì)點(diǎn)的聊天系統(tǒng)。
三:消息推送
消息推送工作原理大致上和聊天的原理一致,即每一個(gè)用戶都有屬于自己的一個(gè)websocket連接,這里我們可以使用其username作為group_name,當(dāng)其他用戶的某些行為觸發(fā)了推送條件時(shí),后臺(tái)便向該用戶所在的group發(fā)送一條信息,這樣就完成了消息推送服務(wù)。
再次,特地說(shuō)明一下,channels同樣提供了單通道發(fā)送,即每一個(gè)客戶端連接時(shí)都會(huì)生成一個(gè)專門(mén)的通道名稱。但是我們?cè)谶@里使用的全部都是group發(fā)送,一個(gè)原因是我個(gè)人比較懶,使用group便可以完成相應(yīng)的功能,只要在客戶端連接時(shí)添加用戶認(rèn)證,便能保證每個(gè)用戶只能連接上自己的那個(gè)group。當(dāng)然,在這里只是展示簡(jiǎn)單的消息推送如何實(shí)現(xiàn),并不展示其他代碼。
# 推送consumer
class PushConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.group_name = self.scope['url_route']['kwargs']['username']
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
# print(PushConsumer.chats)
async def push_message(self, event):
print(event)
await self.send(text_data=json.dumps({
"event": event['event']
}))
消息推送是后臺(tái)向客戶端推送信息,因此不涉及處理接受來(lái)自客戶端的信息的操作,因此我們只要改寫(xiě)connect()、disconnect()函數(shù),然后添加一個(gè)對(duì)發(fā)送信息的處理函數(shù)push_message()
然后我們?cè)賹?xiě)一個(gè)push()函數(shù),用來(lái)在項(xiàng)目的其他地方調(diào)用,這就是為什么我們?cè)诘谝徊嚼锩嬉褂胷edis做為channels的通道后端。
from channels.layers import get_channel_layer
def push(username, event):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
username,
{
"type": "push.message",
"event": event
}
)
這個(gè)函數(shù)寫(xiě)在PushComsumer之外,因?yàn)槲覀冊(cè)陧?xiàng)目的其他地方調(diào)用時(shí),不會(huì)使用self.self.channel_layer來(lái)獲取通道層,因此單獨(dú)寫(xiě)做一個(gè)函數(shù),然后使用get_channel_layer來(lái)檢索它。
因此,在我們需要使用消息推送的地方,只要直接調(diào)用push()函數(shù),傳入被推送用戶的用戶名和推送的信息就OK了。
四:routing配置和其他配置
同樣,在channel文件夾下新建一個(gè)routing.py文件,然后在其中添加以下內(nèi)容,其工作原理和django的urls.py一致,是websocket的連接路徑。
from . import consumers websocket_urlpatterns = [ url(r'^ws/chat/(?P<group_name>[^/]+)/$', consumers.ChatConsumer), url(r'^push/(?P<username>[0-9a-z]+)/$', consumers.PushConsumer), ]
然后在settings.py同目錄新建一個(gè)routing.py文件,在其中添加以下代碼
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import example.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
example.routing.websocket_urlpatterns
)
),
})
這樣,客戶端便可以成功連接到websocket了,功能簡(jiǎn)單實(shí)現(xiàn)。
總結(jié)
以上所述是小編給大家介紹的Django Channels 實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)實(shí)時(shí)聊天和消息推送功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
- Django實(shí)現(xiàn)WebSocket在線聊天室功能(channels庫(kù))
- Django中如何使用Channels功能
- Django使用channels + websocket打造在線聊天室
- django使用channels實(shí)現(xiàn)通信的示例
- 淺談django channels 路由誤導(dǎo)
- 詳解Django-channels 實(shí)現(xiàn)WebSocket實(shí)例
- Django使用Channels實(shí)現(xiàn)WebSocket的方法
- django使用channels2.x實(shí)現(xiàn)實(shí)時(shí)通訊
- django channels使用和配置及實(shí)現(xiàn)群聊
相關(guān)文章
PyCharm最新激活碼(2020/10/27全網(wǎng)最新)
Pycharm最新激活碼全網(wǎng)最新(2020/10/27更新),適用Intellij idea 2020.2.x,WebStorm 2020.2.x,Pycharm 2020.2.x2020-10-10
Python操作Sql Server 2008數(shù)據(jù)庫(kù)的方法詳解
這篇文章主要介紹了Python操作Sql Server 2008數(shù)據(jù)庫(kù)的方法,結(jié)合實(shí)例形式分析了Python使用pyodbc庫(kù)操作Sql Server 2008數(shù)據(jù)庫(kù)的連接、執(zhí)行sql語(yǔ)句、關(guān)閉連接等相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-05-05
Python subprocess模塊常見(jiàn)用法分析
這篇文章主要介紹了Python subprocess模塊常見(jiàn)用法,結(jié)合實(shí)例形式分析了subprocess模塊進(jìn)程操作相關(guān)使用技巧與注意事項(xiàng),需要的朋友可以參考下2018-06-06
Django models.py應(yīng)用實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了Django models.py應(yīng)用實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
Python數(shù)據(jù)分析中Groupby用法之通過(guò)字典或Series進(jìn)行分組的實(shí)例
下面小編就為大家分享一篇Python數(shù)據(jù)分析中Groupby用法之通過(guò)字典或Series進(jìn)行分組的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
詳解Django-channels 實(shí)現(xiàn)WebSocket實(shí)例
這篇文章主要介紹了詳解Django-channels實(shí)現(xiàn)WebSocket實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Python PyQt5實(shí)戰(zhàn)項(xiàng)目之文件拷貝器的具體實(shí)現(xiàn)詳解
PyQt5以一套Python模塊的形式來(lái)實(shí)現(xiàn)功能。它包含了超過(guò)620個(gè)類,600個(gè)方法和函數(shù)。本篇文章手把手帶你用PyQt5實(shí)現(xiàn)一個(gè)簡(jiǎn)單的文件拷貝器,大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-11-11

