Django’da Websocket Kullanarak Gerçek Zamanlı Uygulama Geliştirme

WebSocket, tek bir TCP bağlantısı üzerinden tam çift yönlü iletişim kanalı sağlayan bir bilgisayar iletişim protokolüdür. Tüm internet tarayıcıları 2011′ den beri Websockets’ leri destekliyor.

Websocket ile uygulama geliştirme popüler bir konu. Fakat uygulama geliştirirken gerçekten ihtiyacınız var mı bunun analizini yapmış olmanız gerekiyor.

Websocket ile Gerçekleştirilecek Uygulama Alanları

  1. Chat Uygulamaları
  2. web uygulamanızda ziyaretçilerinize gerçek zamanlı bildirimler yollama
  3. Gerçek zamanlı grafik sunma
  4. IOT cihazlarına ile bağlantı kurularak(rasberrypie gibi…) gerçek zamanlı iletişim uygulamaları(GPS takibi, sensörlerden gelen anlık veriyi okuma ve aksiyon alma)
  5. Chat botları

Django channels: Django projelerine sadece Http bağlantının dışında, WebSockets, MQTT, chatbots, amateur radio gibi uygulamalar için uzun süreli bağlantı imkanı sunan bir pakettir. Django ile gelen asenkron view lere ekstra özellikler kazandırır.

Amaç

Bu yazıda

  1. Django projemize Django channels kullanarak Web sockets desteği sağlamak
  2. Jupyter notebook’ ta bir publisher oluşturarak websockets ile Django uygulamamıza veri gönderme
  3. Frontend’ de websocket bağlantısı yaparak veriyi anlık olarak güncelleme işlemlerini gerçekleştireceğiz.

Yüklenecek hütüphaneler

Gerçek zamanlı uygulamamız için aşağıdaki paketleri bilgisayarımıza yüklememiz gerekiyor.

pip install Django
pip install -U channels
pip install websocket-client

settings.py

settings.py dosyamızda yüklü uygulamalarımıza channels’ ı aşağıdaki gibi tanımlamamız gerekiyor. Projemizde liveapp isminde uygulamayı başlattık.

INSTALLED_APPS = [
    'channels', # for async app  it must be top place
  
  	'liveapp'

Uygulamamız asenkron çalışacağı için aşağıdaki şekilde tanımlamamız gerekiyor. learndj projemizde url‘ leri tanımlayacağımız routing.py adında dosya oluşturduk.

WSGI_APPLICATION = 'learndj.wsgi.application'
ASGI_APPLICATION = 'learndj.routing.application'

CHANNEL_LAYERS‘ ı eğer Django ile birlikte geleni kullanacaksak aşağıdaki kodları ekleriz. Eğer gerçek zamanlı uygulamada Redis kullanılacaksa aşağıdaki kodlarda yorum satırına alınmış kısmı kullanabilirsiniz.

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels.layers.InMemoryChannelLayer"
    },
}

# CHANNEL_LAYERS = {
#     "default": {
#         "BACKEND": "channels_redis.core.RedisChannelLayer",
#         "CONFIG": {
#             "hosts": [("localhost", 6379)],
#         },
#     },
# }

learndj projemizin dosya yapısı aşağıdaki gibidir.

routing.py

routing.py dosyasını oluşturduktan sonra aşağıdaki kodları kullanacağız. websocket url‘ i tanımlarken başına diğer urllerden ayırmak için ws/ ekliyoruz. Bu url’ e istek yapıldığında çalışacak consumer class’ ı yeni oluşturduğumuz consumer.py dosyasındaki DashConsumer classıdır.

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from django.urls import path
from liveapp import consumer

websocket_urlPattern = [
    #In routing.py, "as_asgi()" is required for versions over python 3.6.
    path('ws/pollData', consumer.DashConsumer.as_asgi()), # add ws for prefix.
]

application = ProtocolTypeRouter({
    'websocket':AuthMiddlewareStack(URLRouter(websocket_urlPattern ))
})

consumer.py

Consumer’ lar Django’ daki view fonksiyonlarının karşılığıdır. liveapp uygulamamızda consumer.py isminde dosyamızı oluşturduk.

Aşağıdaki kod bloğu ile çalışacak consumer fonksiyonlarını tanımladık. Uygulamamızın websocket url’ ine bağlanan bir kullanıcı dashboard grubuna eklenecek.

from channels.generic.websocket import AsyncJsonWebsocketConsumer
import json

class DashConsumer(AsyncJsonWebsocketConsumer):
    print('==================')
    async def connect(self):
        self.groupname='dashboard'
        await self.channel_layer.group_add(
            self.groupname,
            self.channel_name,
        )
        await self.accept()

    async def disconnect(self,close_code):
        await self.channel_layer.group_discard(
            self.groupname,
            self.channel_name
        )   

    async def receive(self, text_data):
        datapoint = json.loads(text_data)
        val =datapoint['value']

        await self.channel_layer.group_send(
            self.groupname,
            {
                'type':'deprocessing', #function name to run
                'value':val #value to send function
            }
        )
        print ('>>>>',text_data)

    async def deprocessing(self,event):
        valOther=event['value']
        valOther = f'IP VALUE: {valOther}'
        await self.send(text_data=json.dumps({'value2':valOther}))# send for frontend

Uygulamamıza bağlanacak ve json formatında data gönderecek python scripti aşağıdaki gibidir. Jupyter notebookta anlık olarak üretilen veriler templette render edilecek.

Jupyter notebook’u çalıştırdığımızda, consumer.py ‘deki receive fonksiyonun içinde, gelen verileri print ederiz . Print edilen json verisini djangoyu çalıştırdığımız komut satırında aşağıdaki resimdeki gibi görebiliriz.

live_app.html isminde template oluşturup aşağıdaki kod blokunu ekliyoruz.

<h2>Live APP</h2>
<h3 id='poll_data'>Data</h3>
<script>
  	const myDiv = document.getElementById('poll_data')
    
    let socket = new WebSocket('ws://localhost:8000/ws/pollData');
    socket.onopen = function(e){
        alert('connection established');
    }

    socket.onmessage = function(e){
        console.log(e.data);
        result = JSON.parse(e.data);
      	myDiv.textContent = result.value2;
    }

    socket.onclose = function(e){
        alert('connection closed');
    }
</script>

Bu template’i render edecek herhangi url ve view‘ i tanımladığımızda aşağıdaki resimdeki gibi verilerin anlık olarak güncellendiğini görebiliriz.

Başarılar …