資料型別的有關資訊及位元組與其他度量單位的換算關係
阿新 • • 發佈:2020-11-14
編者按
本文強調了應用程式定製指標的重要性,用程式碼例項演示瞭如何設計指標並整合Prometheus到Django專案中,為使用Django構建應用的開發者提供了參考。
為什麼自定義指標很重要?
儘管有大量關於這一主題的討論,但應用程式的自定義指標的重要性怎麼強調都不為過。和為Django應用收集的核心服務指標(應用和web伺服器統計資料、關鍵資料庫和快取操作指標)不同,自定義指標是業務特有的資料點,其邊界和閾值只有你自己知道,這其實是很有趣的事情。
什麼樣的指標才是有用的?考慮下面幾點:
- 執行一個電子商務網站並追蹤平均訂單數量。突然間訂單的數量不那麼平均了。有了可靠的應用指標和監控,你就可以在
- 你正在寫一個爬蟲,它每小時從一個新聞網站抓取最新的文章。突然最近的文章並不新了。可靠的指標和監控可以更早地揭示問題所在。
- 我認為你已經理解了重點。
設定Django應用程式
除了明顯的依賴(pip install Django
)之外,我們還需要為寵物專案(譯者注:demo)新增一些額外的包。繼續並安裝pip install django-prometheus-client
。這將為我們提供一個Python的Prometheus客戶端,以及一些有用的Django hook,包括中介軟體和一個優雅的DB包裝器。接下來,我們將執行Django管理命令來啟動專案,更新我們的設定來使用Prometheus客戶端,並將Prometheus的URL新增到URL配置中。
啟動一個新的專案和應用程式
為了這篇文章,並且切合代理的品牌,我們建立了一個遛狗服務。請注意,它實際上不會做什麼事,但足以作為一個教學示例。執行如下命令:
django-admin.py startproject demo
python manage.py startapp walker
#settings.py
INSTALLED_APPS = [
...
'walker',
...
]
現在,我們來新增一些基本的模型和檢視。簡單起見,我只實現將要驗證的部分。如果想要完整地示例,可以從這個demo應用獲取原始碼。
# walker/models.py
from django.db import models
from django_prometheus.models import ExportModelOperationsMixin
class Walker(ExportModelOperationsMixin('walker'), models.Model):
name = models.CharField(max_length=127)
email = models.CharField(max_length=127)
def __str__(self):
return f'{self.name} // {self.email} ({self.id})'
class Dog(ExportModelOperationsMixin('dog'), models.Model):
SIZE_XS = 'xs'
SIZE_SM = 'sm'
SIZE_MD = 'md'
SIZE_LG = 'lg'
SIZE_XL = 'xl'
DOG_SIZES = (
(SIZE_XS, 'xsmall'),
(SIZE_SM, 'small'),
(SIZE_MD, 'medium'),
(SIZE_LG, 'large'),
(SIZE_XL, 'xlarge'),
)
size = models.CharField(max_length=31, choices=DOG_SIZES, default=SIZE_MD)
name = models.CharField(max_length=127)
age = models.IntegerField()
def __str__(self):
return f'{self.name} // {self.age}y ({self.size})'
class Walk(ExportModelOperationsMixin('walk'), models.Model):
dog = models.ForeignKey(Dog, related_name='walks', on_delete=models.CASCADE)
walker = models.ForeignKey(Walker, related_name='walks', on_delete=models.CASCADE)
distance = models.IntegerField(default=0, help_text='walk distance (in meters)')
start_time = models.DateTimeField(null=True, blank=True, default=None)
end_time = models.DateTimeField(null=True, blank=True, default=None)
@property
def is_complete(self):
return self.end_time is not None
@classmethod
def in_progress(cls):
""" get the list of `Walk`s currently in progress """
return cls.objects.filter(start_time__isnull=False, end_time__isnull=True)
def __str__(self):
return f'{self.walker.name} // {self.dog.name} @ {self.start_time} ({self.id})'
# walker/views.py
from django.shortcuts import render, redirect
from django.views import View
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponseNotFound, JsonResponse, HttpResponseBadRequest, Http404
from django.urls import reverse
from django.utils.timezone import now
from walker import models, forms
class WalkDetailsView(View):
def get_walk(self, walk_id=None):
try:
return models.Walk.objects.get(id=walk_id)
except ObjectDoesNotExist:
raise Http404(f'no walk with ID {walk_id} in progress')
class CheckWalkStatusView(WalkDetailsView):
def get(self, request, walk_id=None, **kwargs):
walk = self.get_walk(walk_id=walk_id)
return JsonResponse({'complete': walk.is_complete})
class CompleteWalkView(WalkDetailsView):
def get(self, request, walk_id=None, **kwargs):
walk = self.get_walk(walk_id=walk_id)
return render(request, 'index.html', context={'form': forms.CompleteWalkForm(instance=walk)})
def post(self, request, walk_id=None, **kwargs):
try:
walk = models.Walk.objects.get(id=walk_id)
except ObjectDoesNotExist:
return HttpResponseNotFound(content=f'no walk with ID {walk_id} found')
if walk.is_complete:
return HttpResponseBadRequest(content=f'walk {walk.id} is already complete')
form = forms.CompleteWalkForm(data=request.POST, instance=walk)
if form.is_valid():
updated_walk = form.save(commit=False)
updated_walk.end_time = now()
updated_walk.save()
return redirect(f'{reverse("walk_start")}?walk={walk.id}')
return HttpResponseBadRequest(content=f'form validation failed with errors {form.errors}')
class StartWalkView(View):
def get(self, request):
return render(request, 'index.html', context={'form': forms.StartWalkForm()})
def post(self, request):
form = forms.StartWalkForm(data=request.POST)
if form.is_valid():
walk = form.save(commit=False)
walk.start_time = now()
walk.save()
return redirect(f'{reverse("walk_start")}?walk={walk.id}'