亚洲乱色熟女一区二区三区丝袜,天堂√中文最新版在线,亚洲精品乱码久久久久久蜜桃图片,香蕉久久久久久av成人,欧美丰满熟妇bbb久久久

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

也談如何構(gòu)建高性能服務(wù)端程序

admin
2014年12月2日 23:51 本文熱度 6839

  引子


  我接觸過很多編程語言,接觸過各種各樣的服務(wù)器端開發(fā),Java,Go,Ruby,Javascript等語言,Spring,Node.js,Rails等等常見服務(wù)器端框架和編程模型都有接觸。這里談一下我個人對高性能服務(wù)器端程序的一些看法,希望給各位讀者一些認(rèn)識。這片文章提到的內(nèi)容也是 Coding(https://coding.net) 代碼托管乃至整站都在使用的一些概念和技術(shù)。


  此外,閱讀這篇文章,有如下幾個前提:不談硬件,不評論編程語言以及框架的好壞,不談高級算法,可拍磚,拒絕噴子


  三個關(guān)鍵詞


  Cache,Asynchronous,Concurrent
  我們一個一個來講。


  Cache


  Cache 翻譯成中文就是緩存,臺灣的叫法叫做快取,其本質(zhì)是將獲取緩慢或者計算緩慢的數(shù)據(jù)結(jié)果暫時存儲起來,以便以后再次獲取或者計算同樣的數(shù)據(jù)可以直接從存儲中取得結(jié)果,從而可能提升性能的一種手段。Cache 最早是應(yīng)用在計算機(jī)的 CPU 中,這篇文章不談硬件,所以有需要了解 CPU 的緩存的同學(xué)可自行搜索。


  可以想象,如果讓一個人一遍一遍的從 1+2+3+4+…+99+100=? 這樣去算,他加到最后發(fā)現(xiàn)等于5050,而這個過程耗費了他大量的時間,耗費了大量的腦力,在此期間,他可能把所有精力都放在這個計算上面而無暇顧及其他事情。等到他累得滿頭大汗,加完了結(jié)果,他告訴你是 5050。沒過多久,你又讓他做同樣的事情,我相信這家伙會不加思索的再次告訴你 5050。為什么?你會笑我說,人又不是傻子,這為同學(xué)肯定記得這個結(jié)果是5050啊。


  可是,計算機(jī)不一樣,計算機(jī)就是你上面要嘲笑的那個傻子,他傻到,完全不會記得剛在做了什么事情,他會傻乎乎的再重新算一遍告訴你結(jié)果。沒錯如果你問他一萬遍,這頭沒有腦子的機(jī)器會算一萬遍的。雖然上面這個從1加到100這個例子對于一款現(xiàn)代化的計算機(jī)來講簡直是小菜一碟,但是計算機(jī)往往面臨的計算難題是我們?nèi)祟愃鶡o法企及的。


  Cache 就是為了來解決這個事情的,因為事情往往是這樣的:你會發(fā)現(xiàn)一些非常復(fù)雜的過程的計算結(jié)果是可重用的,而且把這個結(jié)果暫時存儲在某些地方,查找起來也是極為方便的。


  所以,現(xiàn)在你理解了緩存,那可以來思考一些緩存的設(shè)計策略了。這里做一點說明,不同的緩存策略跟具體的業(yè)務(wù)系統(tǒng)關(guān)系非常大,制定緩存策略需要根據(jù)具體的情況來分析。常用的策略:



  • 最終結(jié)果型緩存。這種緩存往往提升性能效果最為明顯,但是命中率卻低,也就是可重用性不高。

  • 中間結(jié)果型緩存。還拿上面的例子來說,1加到100,你可以構(gòu)建出是個緩存分別是1加到10,10加到20,20加到30 … 一直到 90加到100 這9個緩存。好處是你如果被請求到 1加到60 的時候,仍然可以使用這些緩存結(jié)果??蓧奶幰埠苊黠@,你取到幾個緩存的結(jié)果后不得不再進(jìn)行一次運(yùn)算。所以實際情況,往往是在最終結(jié)果和中間結(jié)果之間找到平衡點,或者是兩者配合使用。

  不知不覺中,你有沒有發(fā)現(xiàn),1+2+3+4+…+99+100=5050 是個永遠(yuǎn)都成立的事實,這也就意味著,它永遠(yuǎn)不用被清除??墒聦嵤峭牵彺媸怯杏行诘?,例如需要緩存今天的天氣情況,今天是 2014年11月16日,到了明天就是 11月17日,天氣就不一樣了。再例如需要緩存 Coding 的最新冒泡列表,當(dāng)有人發(fā)布了新的冒泡,那么這個列表就得被更新。從這個角度來看,緩存的策略又有如下常見的幾種:



  • 永久式緩存:結(jié)果在任何情況下都不發(fā)生改變,無需清除或者更新

  • 有有效期的緩存:在特定時間點或者時間段后失效

  • 觸發(fā)式失效緩存:當(dāng)某一事件產(chǎn)生時,緩存失效,當(dāng)然有有效期式緩存也可以理解成時間點和時間段到期為觸發(fā)條件的觸發(fā)式失效緩存

  嗯,既然提到了緩存的更新或者清除,那么就牽扯到緩存的更新策略。例子永遠(yuǎn)好過大段的理論:假如我們要緩存 Coding 的冒泡列表。有這么一種策略:當(dāng)用戶請求時我們檢查下是否已存在這樣的緩存,如果有直接返回緩存數(shù)據(jù),否則我們生成這個列表(計算機(jī)的計算過程),返回給用戶并且把冒泡列表(計算結(jié)果)存儲起來,以便以后的用戶訪問時直接獲取。當(dāng)用戶發(fā)布了一個新的冒泡的時候,我們清除這個緩存,再有用戶請求時將重復(fù)以上過程。這是其中一種完整的緩存清除策略。另外一種是,每當(dāng)我們收到一個用戶發(fā)布的冒泡時,都重新構(gòu)建這個緩存,用戶每次查看冒泡列表都是取的緩存數(shù)據(jù)。這兩種緩存分別稱之為:



  • 被動式緩存:需要用到時才構(gòu)建

  • 主動式緩存:預(yù)先構(gòu)建

  關(guān)于 Cache 還有很多很多需要注意和設(shè)計上的思路和策略,這里不再一一贅述。這些緩存在不同的維度有不同的策略,我們需要根據(jù)具體的業(yè)務(wù)情況來選擇合適的策略。Coding 的很多業(yè)務(wù)中使用了上述很多種策略,例如我們常見的分支列表和標(biāo)簽列表就是使用觸發(fā)式失效緩存,我們的廣場項目列表就是使用主動式緩存構(gòu)建。


  Asynchronous


  Asynchronous 的意思是異步。什么是異步呢?就是不在第一時間告知調(diào)用者結(jié)果,告訴他我已經(jīng)收到這個任務(wù)了,我會處理,處理完畢后通知你結(jié)果,如果你不是等不到結(jié)果就無法進(jìn)行下去的話,你完全可以先干別的事情。
  嗯,好像我描述的比較拉雜。還是例子:你去咖啡廳點一杯咖啡,服務(wù)員告訴你現(xiàn)磨咖啡需要15分鐘才可做好,那么在咖啡做好之前,你不可能盯著服務(wù)員或者咖啡師15分鐘,你肯定會干點別的,比如說玩手機(jī)上一下網(wǎng),或者跟你女朋友商量下去看電影什么的,總之你不會傻乎乎等著的。等到咖啡做好了,服務(wù)員會記得給你端過來的。這就是異步過程,你的大腦不必為一個漫長的過程卡住,可以繼續(xù)其他的事情。


  服務(wù)端程序設(shè)計往往也是這樣,在你等待一個很緩慢的過程的時候,如果你不是必須要得到這個過程的結(jié)果才能繼續(xù)下去,你完全可以先進(jìn)行別的過程,等到那個緩慢的過程執(zhí)行完畢后,它會通知你結(jié)果的。


  異步已經(jīng)在現(xiàn)在的各種編程領(lǐng)域有了很廣泛的應(yīng)用,例如 Ajax 技術(shù),就是一種異步的手段,在瀏覽器和服務(wù)器交互的時候,完全不影響你在網(wǎng)頁上的其他操作。


  異步在各種編程語言和框架中都有相應(yīng)的支持,這里簡單介紹一下 Javascript 的異步支持。熟悉它的人的人請無視這段。它使用回調(diào)的方式支持異步,大致意思是,A 交代給 B 一個任務(wù),并且告知 B 任務(wù)完成后繼續(xù)執(zhí)行哪段程序(往往包裝成一個匿名function),B執(zhí)行完任務(wù)后,執(zhí)行這個匿名的 function,這樣來完成異步過程。在 Javascript 中大量的使用這種回調(diào)的異步方案,已經(jīng)不再局限于對一個緩慢的過程了,可以對幾乎所有的過程都采用異步處理。


  在服務(wù)端程序中,除了使用線程,協(xié)程,回調(diào)之外,另外一種常見的異步的支持方式就是消息隊列。其原理是,生產(chǎn)者發(fā)送消息到消息隊列中,消費者從中取出消息,做出相應(yīng)處理,并把結(jié)果存儲起來或者通過某種方式告知生產(chǎn)者。


  異步在很多時候可以運(yùn)用現(xiàn)代化計算機(jī) CPU 的多核特性和分布式計算特性,能顯著的提升應(yīng)用的性能,但是一個前提就是,異步的任務(wù)的結(jié)果必須是主進(jìn)程進(jìn)行下一步操作所不依賴的,否則主進(jìn)程必須等待,直到這個任務(wù)執(zhí)行結(jié)束,拿到結(jié)果再進(jìn)行下一步,這時就變成了傳統(tǒng)的同步計算了。


  異步操作在 Coding 中也有非常廣泛的應(yīng)用。例如當(dāng)用戶執(zhí)行完一次 Push,Coding 需要生成一條 Push 的動態(tài),需要清理掉相應(yīng)的緩存,需要觸發(fā)相關(guān)的 WebHook 等等,這些操作都是通過消息隊列來異步完成的。因為這些操作非常的耗時,而且完全不需要即時完成,所以用戶在 Push 的時候等待著這些操作完成是很不合理的。異步操作在這里即展示出了其應(yīng)用多核和多臺服務(wù)器的優(yōu)勢,在某種程度上還能提升用戶體驗。


  Golang 是 Google 2009 年發(fā)布的一門現(xiàn)代化語言,其語言特性對異步提供了良好的支持。這里舉個例子體現(xiàn)一下異步的魅力:


//一個結(jié)構(gòu)體
type project struct {
//參數(shù)Channel
name chan string
result chan
string
}
//addProject
func addProject(u user, p project) {
//檢查用戶權(quán)限
checkPermission(u)
//啟動協(xié)程
go func() {
//獲取輸入
name := <-p.name
//訪問數(shù)據(jù)庫,輸出結(jié)果通道
q.result <- "add project :" + name
}()
}
//主進(jìn)程
func main() {
//初始化project
p := project{ make(chan string, 1), make(chan string, 1) }
//某位用戶
u := user{}
//執(zhí)行addProject,注意執(zhí)行的時候還不需要告知要創(chuàng)建的項目名字
addProject(u,p)
//準(zhǔn)備參數(shù)
p.name <- "an-asynchronous-project"
//獲取結(jié)果
fmt.Println(<-p.result)
}

  這一段程序涉及到了 Golang 的 goroutine 和 channel,不了解的可以去查一下相關(guān)資料。
  這段程序?qū)崿F(xiàn)了在還為準(zhǔn)備好參數(shù)時就已經(jīng)調(diào)用一個 function 。當(dāng)我們調(diào)用 addProject 的時候還不知道項目的名字,但是這完全不影響我們?nèi)z查用戶權(quán)限。程序完全可以一邊去檢查權(quán)限,一邊去獲取項目名字,當(dāng)程序執(zhí)行到不得不拿到項目的名字才能繼續(xù)的時候,它將阻塞,直到我們告訴他項目名字。


  Concurrent


  Concurrent 的意思是并行?,F(xiàn)代化的 CPU 往往具有多個核心,而且有些 CPU 也具有超線程能力。如果我們可以將單個過程拆分成小的任務(wù),交給 CPU 的多個核心,或者是分布式計算系統(tǒng)的多個計算節(jié)點,就可以充分利用并行計算來提升性能。前提是這些任務(wù)相互之間不要有相互依賴的關(guān)系。依然是例子:需要計算網(wǎng)站上某一批用戶的活躍度積分,傳統(tǒng)的,我們會查出這一批用戶,然后寫一個循環(huán),然后輪流計算他們的積分,最后得到結(jié)果。其實每個用戶的積分的計算都是獨立的,相互不依賴,那么我們就可以利用這一點來并行化這個計算。


  下面給出一段 Coding 代碼托管中的程序,這段程序是指定條件獲取一個提交列表,使用了并行計算的一種 并發(fā)循環(huán)


public List<Commit> getCommits(String objectId, String path, int offset, int maxCount) {
List
<String> shas = getCommitsSha(this, objectId, path, offset, maxCount);
List
<Commit> commits = new ArrayList<>();
if (shas != null) {
List
<GetCommit> getCommits = new ArrayList<>();
for (String sha : shas) {
getCommits.add(
new GetCommit(this, sha));
}
//聲明一個自適應(yīng)的線程池
ExecutorService executor = Executors.newFixedThreadPool(8);
List
<Future<Commit>> futureList = null;
//并發(fā)的調(diào)用getCommit
futureList = executor.invokeAll(getCommits);
executor.shutdown();
for (Future<Commit> future : futureList) {
Commit commit
= future.get();
commits.add(commit);
}
}
return commits;
}
//Java 是一個啰嗦的語言,還要聲明一個類來包裝一下這個過程。
class GetCommit implements Callable<Commit> {
private Repo repo;
private String sha;
public GetCommit(Repo repo, String sha) {
this.repo = repo;
this.sha = sha;
}
@Override
public Commit call() throws Exception {
return repo.getCommit(sha);
}
}

  這段程序是一個并發(fā)循環(huán)的例子,例子中需要根據(jù)一些參數(shù)查詢到 Commit 的列表,而 repo.getCommit 這個過程完全不需要一個一個輪流查詢,因為他們是完全獨立的,所以可以使用 Java 的 Cocurrent 包來做并發(fā)循環(huán),充分利用多核來盡快得到執(zhí)行結(jié)果。


  總結(jié)


  關(guān)于高性能服務(wù)器程序需要關(guān)注的點還有很多,這里只是簡單的介紹了下三個利器(Cache,Asynchronous,Concurrent)。而即便是這三個利器,我的介紹也只是冰山一角,但是請相信你看懂了我介紹的這些東西,重新去思考服務(wù)端編程會獲得不少收獲的。
  這三者也是相輔相成的關(guān)系,很多時候都是配合著使用才能起到很好的效果。異步和并行在某種程度上是有重疊的,而我們經(jīng)常使用異步的方式去主動構(gòu)建緩存。


  最后再給一些小提示:



  • 不要讓 CPU 閑著(CPU 正常情況下壓力大的時候自然不會閑著,這里指的是CPU負(fù)載低谷時,可以讓他主動的構(gòu)建緩存,或者做一些準(zhǔn)備工作等等。)

  • 提升 CPU 效率,即不要總讓 CPU 做重復(fù)的勞動,用空間換時間的理念去減輕 CPU 的壓力

  • 不要讓無關(guān)緊要的附屬的任務(wù)卡住主進(jìn)程,讓他們在后臺慢慢做

  • 可以提前做好準(zhǔn)備工作,這個比較抽象,但是舉例子就很明白,連接池,主動緩存,以及我舉得那個 Golang 的例子都是很好的例子

該文章在 2014/12/2 23:51:37 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場、車隊、財務(wù)費用、相關(guān)報表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務(wù)都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved