使用Django Signal 實現對模組的解耦

2022-09-06 17:51:10 字數 4532 閱讀 6317

【目錄】

目錄3. 訊號解耦、非同步任務

最近負責開發乙個自動發現主機資訊的應用。應用中資料流向複雜,處理邏輯冗餘堆積。專案技術棧選擇的是django + vuejs。前端使用 webpack打包,模組化管理,主要是展示資料。後端涉及的模組多、處理規則多、資料表多,每次涉及之前功能修改時,都消耗大量時間進行code review。讓我才意識到,在複雜應用中解耦模組非常重要。下面是一些調研和實踐。

在實踐中,我主要使用的是 django signal,實現對模組的解耦。django signal 是 django 對觀察者模式的實現和應用。因此,有必要先了解一下觀察者模式。

發布訂閱模式與觀察者模式區別:

發布訂閱模式的通訊依賴於訊息佇列(rabbitmq、rocketmq、activemq、kafka、zeromq、metamq等)屬於非同步,觀察者模式通常是同步的。

發布訂閱模式鬆散耦合,發布者和訂閱者甚至所屬不同應用;觀察者模式所屬乙個應用。在實現上,觀察者模式,需要維護乙個訂閱列表。當狀態發生改變時,自動通知列表中的全部物件。

signal 是 django 框架中提供的乙個訊號分發器。傳送器傳送訊號,通知一系列的接收器,從而觸發接收器執行一些操作。

需要注意的是,django 訊號是同步的。如果濫用,會影響到 django 的處理效率。

下面我會以 django1.8.3為例,從乙個使用案例出發,再到原始碼,介紹 django 中 signal 的實現方式。

這裡有乙個小需求:在model表執行s**e後,觸發一些執行邏輯。

# -*- coding: utf-8 -*-

# -*- coding: utf-8 -*-

def ready(self):

繫結訊號處理函式

# -*- coding: utf-8 -*-

from django.dispatch import receiver

from django.db.models.signals import post_s**e

@receiver(post_s**e, sender=mymodel, dispatch_uid="mymodel_post_s**e")

def my_model_handler(sender, **kwargs):

# 這裡寫 mymodel 執行 s**e 後的邏輯

pass

上面的例子,使用了極少量的**,就享受到了 django 提供的訊號處理機制所帶來的便利。但是,如果僅僅停留在使用,你可能無法對 django signal有更深入的了解。下面,從原始碼來看看 django signal 的處理邏輯。

宣告訊號

django 內建了大量 model 相關的訊號,可以直接使用。上面例子使用的訊號 post_s**e ,就是 modelsignal 類的乙個例項,而 modelsignal 又繼承自 signal 類。

django/db/models/signal.py

from django.dispatch import signal

class modelsignal(signal):

def connect(self, receiver, sender=none, weak=true, dispatch_uid=none):

super(modelsignal, self).connect(

receiver, sender=sender, weak=weak, dispatch_uid=dispatch_uid

)post_s**e = modelsignal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=true)

註冊訊號處理函式django 提供的 receiver 函式是乙個裝飾器,被修飾的函式作為引數註冊到接收器物件列表。

django/dispatch/__init__.py

from django.dispatch.dispatcher import signal, receiver

django/dispatch/dispatcher.py

def receiver(signal, **kwargs):

def _decorator(func):

if isinstance(signal, (list, tuple)):

for s in signal:

s.connect(func, **kwargs)

else:

signal.connect(func, **kwargs)

return func

return _decorator

django/dispatch/dispatcher.py

class signal(object):

def __init__(self, providing_args=none, use_caching=false):

self.receivers =

def connect(self, receiver, sender=none, weak=true, dispatch_uid=none):

傳送訊號

在 s**e 完成之後,django 會主動發出 post_s**e 訊號;如果是自定義訊號,那麼需要自行觸發。。

django/db/models/base.py

class model(six.with_metaclass(modelbase)):

# 觸發 model 相關的訊號

def s**e_base(self, raw=false, force_insert=false,

force_update=false, using=none, update_fields=none):

# signal that the s**e is complete

signals.post_s**e.send(sender=origin, instance=self, created=(not updated),

update_fields=update_fields, raw=raw, using=using)

處理訊號,實際上就是依次呼叫接受器列表中的函式。

django/dispatch/dispatcher.py

class signal(object):

def send(self, sender, **named):

responses =

for receiver in self._live_receivers(sender):

response = receiver(signal=self, sender=sender, **named)

return responses

在學習了觀察者模式,了解 django signal 之後,就基本掌握了 django 模組解耦的基礎知識。接著,需要進一步明確模組之間的耦合機制,制定專案約定,就可以利落地實踐了。

梳理一下請求的處理鏈路:

請求經過接入層、中介軟體處理之後,由 url 分發器匹配到合適的處理模組,最終某個模組負責返回響應。各個模組連線資料庫、訊息佇列、物件儲存儲存狀態。

每個模組包含四部分:

signal,模組內建的訊號

signalhandle,模組關注的訊號處理控制代碼

celerytasks,模組的非同步任務

模組與模組之前完全通過訊號耦合:

由於 django signal 是同步處理機制,為了支援非同步處理,可以結合 celery 和 rabbitmq 進行實踐。

下面是乙個訊號處理非同步邏輯的例子:

# -*- coding: utf-8 -*-

from celery import task

@task(ignore_result=true)

def my_task(instance):

pass

# -*- coding: utf-8 -*-

from django.dispatch import receiver

from django.db.models.signals import post_s**e

@receiver(post_s**e, sender=mymodel, dispatch_uid="mymodel_post_s**e")

def my_model_handler(sender, **kwargs):

instance = kwargs['instance']

# 非同步

# 同步

pass

Response AddHeader使用例項收集

response.addheader refresh 60 url newpath newpage.asp 這等同於客戶機端元素 頁面轉向 response.status 302 object moved response.addheader location newpath newpage.asp...

使用ServletFileUpload實現上傳

1.首先我們應該為上傳的檔案建乙個存放的位置,一般位置分為臨時和真是資料夾,那我們就需要獲取這倆個資料夾的絕對路徑,在servlet中我們可以這樣做 然後建立檔案工廠即倉庫乙個引數表示存放多大後flush,fileitemfactory factory new diskfileitemfactory...

使用ServletFileUpload實現上傳

1.首先我們應該為上傳的檔案建乙個存放的位置,一般位置分為暫時和真是目錄,那我們就須要獲取這倆個目錄的絕對路徑,在servlet中我們能夠這樣做 然後建立檔案工廠即倉庫乙個引數表示存放多大後flush,fileitemfactory factory new diskfileitemfactory c...