【目錄】
目錄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...