服務(wù)端開(kāi)發(fā)小感
從事服務(wù)端開(kāi)發(fā)已經(jīng)有一些日子了,靜下來(lái)可以想想和記錄些服務(wù)端開(kāi)發(fā)的想法了。
服務(wù)端開(kāi)發(fā),特別是Web開(kāi)發(fā),基本上全是處理HTTP請(qǐng)求的處理。根據(jù)具體用途分為兩種:Web頁(yè)面開(kāi)發(fā)和API接口開(kāi)發(fā)。Web頁(yè)面開(kāi)發(fā)也完全可以看成是API接口開(kāi)發(fā),只是它的兩個(gè)主要部分,頁(yè)面和ajax請(qǐng)求,一個(gè)是返回html,另外一個(gè)可以返回html,也可以返回其他格式的而已。API接口開(kāi)發(fā)是針對(duì)有客戶(hù)端產(chǎn)品而言的。可能是移動(dòng)設(shè)備,可能是PC應(yīng)用等。
應(yīng)用框架
應(yīng)用框架一般使用的是LNMP或者LAMP,基本的框架就是前端N臺(tái)Web服務(wù)機(jī) + cgi訪(fǎng)問(wèn)PHP + php訪(fǎng)問(wèn)mysql。
PHP可以看成是C寫(xiě)的一個(gè)大型的Web框架,它的優(yōu)勢(shì)在于解釋型,即時(shí)修改即時(shí)更新。所以線(xiàn)上代碼更新維護(hù)成本極低,加之其為web開(kāi)發(fā)幾乎是專(zhuān)門(mén)定制的一些函數(shù),所以適合用于web開(kāi)發(fā)。相較于java開(kāi)發(fā)web服務(wù),動(dòng)不動(dòng)就需要重新編譯的痛苦就很知足了。
web服務(wù)器現(xiàn)在nginx是越來(lái)越多使用,nginx比較apache的優(yōu)勢(shì)就在于輕便和靜態(tài)頁(yè)面的高并發(fā)性能。一般拿到設(shè)備先需要考慮下單機(jī)可承受的qps大概多少,方法大致就是先只考慮內(nèi)存,計(jì)算同時(shí)能開(kāi)啟多少個(gè)php-cgi,比如一個(gè)4G內(nèi)存的機(jī)器,每個(gè)php-fpm大概占用20M內(nèi)存,所以差不多能開(kāi)啟200個(gè)php-cgi進(jìn)程(一般會(huì)留些空余的),每個(gè)進(jìn)程同一個(gè)時(shí)間只能跑一個(gè)php程序,所以假設(shè)每個(gè)php程序跑0.1s,1s就能處理10個(gè)請(qǐng)求,所以單機(jī)qps大概會(huì)是2000。當(dāng)然,一般不會(huì)開(kāi)啟到這么極致的程度,有幾個(gè)原因:
1 需要考慮到其他進(jìn)程使用內(nèi)存的情況
2 考慮到如果一旦全部?jī)?nèi)存都使用完了,是否啟用swap,如果沒(méi)有的話(huà),那機(jī)器是否就立即當(dāng)機(jī)
3 還需要考慮到CPU和帶寬的使用情況。CPU對(duì)一些比如加解密,視頻轉(zhuǎn)碼等操作比較耗時(shí),這個(gè)時(shí)候如果沒(méi)有使用隊(duì)列,那么每個(gè)請(qǐng)求的時(shí)間就會(huì)加長(zhǎng)。
文件服務(wù)器
一般會(huì)要求文件服務(wù)器和web服務(wù)器分開(kāi),分開(kāi)的意思就是使用不同的域名進(jìn)行拆分。當(dāng)然web服務(wù)器也是可以當(dāng)做文件服務(wù)器的,但是由于文件服務(wù)器需要上傳文件,而上傳文件是一個(gè)非常耗時(shí)的工作,即php的一個(gè)程序需要停留的時(shí)間很長(zhǎng),所以需要將它們分開(kāi)。一則可以為以后擴(kuò)展文件服務(wù)器提供便利,二則不會(huì)導(dǎo)致文件服務(wù)影響了正常的web服務(wù)。
從文件服務(wù)器拆分的理由上看,在運(yùn)營(yíng)過(guò)程中一些比較占用資源或者特別頻繁調(diào)用的接口是可以或者應(yīng)該考慮拆分到不同機(jī)器上的。
Web前端機(jī)始終要訪(fǎng)問(wèn)持久化的數(shù)據(jù)的,mysql的使用是最為頻繁的。其實(shí)所有的web服務(wù)說(shuō)到底都是對(duì)數(shù)據(jù)庫(kù)進(jìn)行增刪改查的操作。說(shuō)到性能,數(shù)據(jù)庫(kù)的增刪改查操作的性能其實(shí)就決定了一切。所以對(duì)數(shù)據(jù)庫(kù)的建表,索引的使用對(duì)一個(gè)網(wǎng)站來(lái)說(shuō)尤為重要。覺(jué)得最有用的幾個(gè)mysql的技巧有:
1 覆蓋索引。就是想辦法讓查詢(xún)操作只查索引而不去查表的索引建立方法。建立合適的索引和能只在索引就能找到數(shù)據(jù)的查詢(xún)能提高效率。
2 InnoDB表最好能使用自增鍵,提高插入操作的效率。
3 string類(lèi)型的變量的存儲(chǔ)格式,是使用varchar還是char比較好,曾經(jīng)有個(gè)項(xiàng)目表設(shè)計(jì)從char到varchar之后的數(shù)據(jù)庫(kù)大小差別達(dá)到70G和20G的大小…
4 建表的時(shí)候需要考慮下以后的分庫(kù)分表,如果是使用分表,什么是分表鍵?是否需要反向查詢(xún)表?
5 甚至當(dāng)考慮到數(shù)據(jù)庫(kù)和Web機(jī)器的機(jī)房分布,這個(gè)設(shè)計(jì)就更麻煩了...
mysql的建表環(huán)節(jié)和需求有很大關(guān)系。沒(méi)有明確的需求,表設(shè)計(jì)一定是不正確的。
數(shù)據(jù)庫(kù)支持有可能還是不足夠的,那么首先想到的可能就是緩存了。緩存是使用全局緩存?放在web前端機(jī)?需要用什么hash算法?用什么緩存?memcache?redis?mysql也有自帶緩存,如何查詢(xún)才能更好命中這個(gè)緩存?當(dāng)數(shù)據(jù)更新的時(shí)候,緩存中的數(shù)據(jù)是否是臟數(shù)據(jù)?如何更新數(shù)據(jù)?
Web頁(yè)面開(kāi)發(fā)
當(dāng)需要做一個(gè)網(wǎng)站的時(shí)候,首先要考慮的是用戶(hù)量有多少?做一個(gè)SNS網(wǎng)站和做一個(gè)運(yùn)營(yíng)后臺(tái)網(wǎng)站完全是兩個(gè)不同的概念。
首先是在頁(yè)面壓力上,SNS網(wǎng)站的qps可能幾千上萬(wàn),而運(yùn)營(yíng)后臺(tái)壓力幾乎完全可以不用計(jì)算。這個(gè)就意味著后端的數(shù)據(jù)庫(kù)支持不同了。SNS網(wǎng)站可能最常調(diào)用的會(huì)是好友關(guān)系和個(gè)人信息的接口,這樣的接口是不是需要獨(dú)立出來(lái)處理?這樣的請(qǐng)求會(huì)很多是重復(fù)的,是不是考慮使用中間件或者緩存來(lái)減輕對(duì)數(shù)據(jù)庫(kù)的直接壓力呢?運(yùn)營(yíng)數(shù)據(jù)一般使用單表就可以解決的。個(gè)人覺(jué)得運(yùn)營(yíng)中統(tǒng)計(jì)的需求是最難做的。首先統(tǒng)計(jì)并不是任意的統(tǒng)計(jì)要求都可以滿(mǎn)足,這個(gè)需要和產(chǎn)品討論需求。其次,統(tǒng)計(jì)一般需要使用些訪(fǎng)問(wèn)日志之類(lèi)的,可能涉及到許多shell腳本。
API開(kāi)發(fā)
其實(shí)相對(duì)于Web開(kāi)發(fā),API開(kāi)發(fā)是屬于被動(dòng)的。意思就是,由于客戶(hù)端可能是手機(jī)產(chǎn)品,可能是PC產(chǎn)品。往往都是有發(fā)布和版本的。這個(gè)意味著API接口沒(méi)法像Web那樣為所欲為隨時(shí)更新代碼。它更多需要考慮到各個(gè)版本之間的兼容問(wèn)題。兼容問(wèn)題在很大程度上會(huì)變?yōu)榧臃ǎ肋h(yuǎn)不會(huì)是減法。個(gè)人感覺(jué),如果毫無(wú)節(jié)制地滿(mǎn)足需求,隨著版本越來(lái)越多,你的代碼中會(huì)越來(lái)越多if else,到最后,你的代碼就根本無(wú)法維護(hù)了。然后就會(huì)是別人來(lái)接手你的工作,踩坑,邊罵邊重構(gòu)….API開(kāi)發(fā)是最需要依賴(lài)測(cè)試的。往往只有測(cè)試人員才對(duì)各個(gè)版本的小改動(dòng),小特性如數(shù)家珍。
再考慮到非功能配套:
你可能需要對(duì)API調(diào)用時(shí)間進(jìn)行統(tǒng)計(jì),這樣你才明白你的接口表現(xiàn)如何。
你的代碼可能還會(huì)用到其他機(jī)器上的服務(wù),比如curl一個(gè)其他服務(wù),這樣的情況,最好考慮下錯(cuò)誤處理和日志記錄。
對(duì)于有金錢(qián)交易的接口服務(wù),日志處理更是必不可少。
對(duì)于一些內(nèi)部錯(cuò)誤,最好不需要直接拋出顯示給用戶(hù),所以需要使用的最好是白名單錯(cuò)誤機(jī)制。
接口的加密方式,一般最少是需要有個(gè)簽名機(jī)制的,考慮到加密方法,大致有幾種:對(duì)稱(chēng)加密和非對(duì)稱(chēng)加密。加密的時(shí)候就需要考慮到一些情況了,比如手機(jī)客戶(hù)端的用電量等。
如果是給手機(jī)開(kāi)發(fā)接口,需要考慮流量問(wèn)題,圖片的規(guī)格問(wèn)題。
框架永遠(yuǎn)是會(huì)變的,不說(shuō)需求的變化,單就用戶(hù)量的變化,20w用戶(hù)和1000w用戶(hù)的框架一定是不一樣的。剛開(kāi)始的時(shí)候你不可能根據(jù)1000w的用戶(hù)量來(lái)設(shè)計(jì)框架來(lái)給20w人用。所以一個(gè)好的服務(wù)端框架一定是隨著用戶(hù)量變化會(huì)進(jìn)行幾次大的變化的。
…
后記
這篇是想到哪寫(xiě)到哪,寫(xiě)到這里發(fā)現(xiàn)寫(xiě)不下去了…總之,web服務(wù)開(kāi)發(fā)的技巧和小東西還是很多的。有的坑是需要自己踩過(guò)才知道痛的。可愛(ài)的是,我還在繼續(xù)踩坑中…
補(bǔ)充下,接口重構(gòu)幾乎是每個(gè)服務(wù)端開(kāi)發(fā)人員必須經(jīng)歷過(guò)的。相較于開(kāi)發(fā)一個(gè)新系統(tǒng),接口重構(gòu)的難度可以說(shuō)是翻翻,當(dāng)然這里的難度也可以理解為難受程度…也會(huì)是很鍛煉人的一個(gè)活。對(duì)于重構(gòu)來(lái)說(shuō),測(cè)試尤為重要,如何有個(gè)很好的測(cè)試集來(lái)保證你的重構(gòu)的正確性是個(gè)難度。
原文鏈接:http://www.cnblogs.com/yjf512/archive/2013/03/22/2974842.html