
Python實現(xiàn)動圖生成:輕松創(chuàng)建自定義表情包
當(dāng)我構(gòu)建 Avalara AvaTax REST API 時,我必須允許用戶發(fā)送地址。由于他們的數(shù)據(jù)很亂,有時他們會發(fā)送 ISO 國家代碼,或者可能是國家名稱,也可能是別名。我可以處理這個問題,因為 GitHub 上有很多具有寬松許可證的 國家數(shù)據(jù)源 ,但最終我選擇付費(fèi)購買 官方 ISO 3166 國家代碼列表 。
下一步是讓我的 API 服務(wù)器在啟動時加載這些數(shù)據(jù)。代碼不必太復(fù)雜——這里有一些類似 C# 的偽代碼,大致顯示了如何使其工作:
private static Task<List<Country>>? _cachedQuery = null ;
private Task<List<Country>> GetCachedCountries()
{
// 將承諾保存到靜態(tài)變量
if (_cache == null ) {
_cache = Database.Countries.ToListAsync();
}
// 所有調(diào)用者加入同一個承諾
return _cache;
}
為什么要這樣做?幸運(yùn)的是,新國家/地區(qū)并不經(jīng)常創(chuàng)建。如果國家/地區(qū)列表要更改,我們會在每月的應(yīng)用程序部署期間發(fā)送 SQL 腳本來添加新記錄。
我的 C# API 服務(wù)器不是查詢數(shù)據(jù)庫中的表,而是將這些數(shù)據(jù)保存在單例中。它會在輸入或輸出時查找正確的名稱。數(shù)據(jù)只占用幾千字節(jié),為了方便起見,我有多個散列的不區(qū)分大小寫的字典。
您可能有幾十個這樣的靜態(tài)數(shù)據(jù)集。查找數(shù)據(jù)集、原因代碼、配置標(biāo)志 — 將它們存儲在靜態(tài)單例中!如果您忘記了,您可能會發(fā)現(xiàn)您的系統(tǒng)每秒對永遠(yuǎn)不會改變的數(shù)據(jù)進(jìn)行數(shù)千次不必要的查詢。
您的 API 服務(wù)器需要一個健康檢查系統(tǒng)。它可以是一個頁面或一個 API,但它應(yīng)該執(zhí)行一系列基本功能測試,以確保機(jī)器能夠正常工作。典型的測試包括:
這些類型的狀態(tài)檢查對于啟動作為自動擴(kuò)展組一部分的服務(wù)器或使用容器化啟動模板是必不可少的。在部署服務(wù)器之前,徹底測試所有內(nèi)容非常重要——啟動缺少數(shù)據(jù)庫連接字符串的機(jī)器會很糟糕。
這些狀態(tài)檢查的一個副作用是,它們通常也用于監(jiān)控部署后的服務(wù)器整體健康狀況。一些云服務(wù)會每分鐘多次調(diào)用此狀態(tài)頁面,如果服務(wù)器無法響應(yīng),則會從負(fù)載平衡器中移除該服務(wù)器。如果您的狀態(tài)頁面在此測試中執(zhí)行查詢,這可能會迅速消耗您的數(shù)據(jù)庫。
可以想象,在啟動時測試數(shù)據(jù)庫連接至關(guān)重要。但是,一旦服務(wù)器成功部署,有效的數(shù)據(jù)庫連接以后突然變?yōu)闊o效的可能性就很小。我發(fā)現(xiàn)最好將成功的結(jié)果緩存一小段時間,比如 30 秒。這意味著我的健康檢查仍然可以將有問題的服務(wù)器排除在輪換之外,但不會使數(shù)據(jù)庫過載:
public static DateTime LastCheckTime = DateTime.MinValue;
public const int SECONDS_FOR_RETEST = 30 ;
public static bool Status ()
{
var now = DateTime.UtcNow;
var timeSinceLastCheck = now - LastCheckTime;
if (timeSinceLastCheck.TotalSeconds > SECONDS_FOR_RETEST) {
...在這里做一些數(shù)據(jù)庫健康檢查 ...
LastCheckTime = now;
}
return true ;
}
大多數(shù)重度 API 用戶會迅速發(fā)出大量請求。對于每個請求,服務(wù)器需要檢查用戶是否經(jīng)過身份驗證,以及他們是否有權(quán)執(zhí)行他們請求的工作。許多這些檢查都需要從數(shù)據(jù)庫中提取數(shù)據(jù):
對每個請求都這樣做似乎很自然,但這些信息可能會浪費(fèi)大量時間。幸運(yùn)的是,有一種 方法可以解決緩慢的身份驗證數(shù)據(jù)庫查詢問題 :如果調(diào)用者發(fā)出請求,您可以在短時間內(nèi)緩存他們的憑據(jù)。
緩存授權(quán)可能看起來很可怕,因為更改不是即時的,但在實踐中,“即時”很難定義。如果在撤銷訪問權(quán)限之前 API 調(diào)用正在進(jìn)行中,則用戶可能會或可能不會根據(jù)隨機(jī)運(yùn)氣發(fā)出請求 — 無論 API 調(diào)用是否在撤銷之前到達(dá)。
如果我們更新文檔,說“更改用戶權(quán)限后,請等待 5 分鐘,所有服務(wù)器才會更新新權(quán)限”——那么您就可以規(guī)劃性能了!這里的技巧是對 API 調(diào)用的承載令牌及其 IP 地址進(jìn)行哈希處理,然后在緩存中查找所有身份驗證和授權(quán)數(shù)據(jù):
Entity Framework 等現(xiàn)代技術(shù)使得訪問數(shù)據(jù)庫變得極其簡單。事實上,這非常容易,以至于我們經(jīng)常可以編寫一個方法來執(zhí)行數(shù)據(jù)庫調(diào)用 — 然后發(fā)現(xiàn)人們在使用這個方法時并沒有意識到它接觸了數(shù)據(jù)庫。
一個簡單的例子可能是這樣的:
公共 異步任務(wù) < int > GetNumberOfUsers ( int id ) {
var count = 0 ;
var items = await _database.GetRecords(id);
foreach ( var item in records) {
count += CountUsersPerItem(item);
}
返回count;
}
這段代碼可能看起來微不足道,但如果該方法 CountUsersPerItem
聯(lián)系數(shù)據(jù)庫,可能是為了獲取一個標(biāo)志或查詢一個子表,您可能會發(fā)現(xiàn)看似一個查詢變成了數(shù)百或數(shù)千個查詢。
更糟糕的是,此功能的性能在開發(fā)人員的桌面上可能看起來不錯,但當(dāng)現(xiàn)實世界的客戶面臨同樣的情況時,可能會突然下降。
我發(fā)現(xiàn)了一些有助于追蹤此問題的技巧:
Query
名稱中都必須包含該單詞,例如, CheckStatusQuery()
接觸數(shù)據(jù)庫的方法會 CheckStatus()
執(zhí)行相同的操作但沒有查詢。這個問題非常隱蔽。現(xiàn)代數(shù)據(jù)庫技術(shù)非常強(qiáng)大,簡單的數(shù)據(jù)庫查詢通常可以與查詢 REDIS 一樣快甚至更快。在本地工作的開發(fā)人員通常會看到非常好的性能,因為他們的應(yīng)用程序和數(shù)據(jù)庫服務(wù)器之間沒有延遲,兩者都在筆記本電腦上的容器中運(yùn)行。
即使您的 SQL Server 或 Postgres 實例可以在一毫秒內(nèi)做出響應(yīng),這些毫秒也會累積起來。如果您的 API 請求發(fā)出十個一毫秒的查詢,則可能會使您的 API 延遲增加十毫秒 – 當(dāng)平均預(yù)期時間少于一百毫秒時,這是一個不可忽略的量。
這里的關(guān)鍵經(jīng)驗是,在應(yīng)用程序接口設(shè)計中,每個數(shù)據(jù)庫查詢都很重要。關(guān)注它們,你的應(yīng)用程序接口就會變得快速而實用。