版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
企業(yè)級應(yīng)用系統(tǒng)體系架構(gòu)(十一)
企業(yè)應(yīng)用的處理ChenHaopengSunday,October8,2023References:Ted
Neward:EffectiveEnterpriseJava1企業(yè)級應(yīng)用系統(tǒng)體系架構(gòu)(十一)
企業(yè)應(yīng)用處理你必須放下兩方的所有偏見,既不相信也不排斥任何事情,因為其他任何人或者這些人的描述,都已經(jīng)排斥或相信它了。你自己的理由是上天給你的唯一神諭,對此你負(fù)有責(zé)任,不是為了抉擇的公正,而是為了抉擇的合理。
——ThomasJefferson不要去找精靈商議,因為他們既說是又說否?!狦andalf對Frodo說,指環(huán)王處理你必須放下兩方的所有偏見,既不相信也不排斥任何事情,因為處理在處理是在企業(yè)系統(tǒng)中中間件不能為我們解決的那一部分,是我們應(yīng)用的“血肉”。它有過:業(yè)務(wù)規(guī)則、業(yè)務(wù)邏輯、領(lǐng)域邏輯、應(yīng)用代碼,等等無數(shù)多的稱呼。正如它的本質(zhì)所言,處理是業(yè)務(wù)首先要關(guān)注的東西:對沒有在其它任何地方處理過的數(shù)據(jù)進(jìn)行處理。企業(yè)系統(tǒng)中相當(dāng)大一部分都是在處理并發(fā)——考慮到最大的吞吐量,我們需要假定兩件事情可以在同一時間發(fā)生。在企業(yè)系統(tǒng)中,并發(fā)同時涉及到兩個主題:編程語言自己的基于監(jiān)視器的對象同步系統(tǒng)和構(gòu)建于大多數(shù)企業(yè)資源管理器內(nèi)部的基于事務(wù)的同步機(jī)制。。處理在處理是在企業(yè)系統(tǒng)中中間件不能為我們解決的那一部分,是我處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔保持簡潔KISS(KeepItSimple,Stupid,保持簡潔易用)。要做那些能夠讓系統(tǒng)運轉(zhuǎn)的最簡潔的事情,這就是簡潔性規(guī)則。復(fù)雜性的某些部分需要被應(yīng)用到領(lǐng)域問題本身,因此如果你將全部復(fù)雜性都花費到了復(fù)雜的技術(shù)上,以至于超越費用底線,那么該項目將會崩潰。保持簡潔KISS(KeepItSimple,Stupi保持簡潔如果你不能解釋一個給定的由一條單一語句,或者至多兩條語句構(gòu)成的處理代碼片段做了些什么,那么它就可能過于復(fù)雜了。復(fù)雜解決方案存在的問題數(shù)也數(shù)不清,但是基本上都可以歸咎于下面幾個基本直接原因:復(fù)雜解決方案很難被模塊化,因此難以被重用。復(fù)雜解決方案難于調(diào)試。復(fù)雜解決方案難于被優(yōu)化。復(fù)雜解決方案更難于維護(hù)。通常,企業(yè)級項目傾向于復(fù)雜而不是簡單:復(fù)雜系統(tǒng)比簡單系統(tǒng)更具“男子氣概”這種想法是不對的。無論出于什么原因,我們都應(yīng)該不斷地重復(fù)強調(diào):保持簡潔,讓工具去完成盡可能多的工作。
保持簡潔如果你不能解釋一個給定的由一條單一語句,或者至多兩條處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行請考慮一下,如果為你桌子上的計算機(jī)定購DVD,那么我們需要確保他們得到的不是KorSplatt5900顯卡,因為這種顯卡不能和此DVD模型一起工作,當(dāng)然除非想更換為CD-RW/DVD。噢,KorSplatt5900不能在一臺少于512MBRAM的機(jī)器上運轉(zhuǎn),除非它是SuperReallyFastRAM……。由于所有可能的“公司希望在基本不兼容的限制之上仍然能夠運行”的促銷策略的因素,再加上所有這些東西一個月(如果不是一個星期的話?。┚蜁儎右淮?,以及突然冒出來的要試圖為在線PC生產(chǎn)商創(chuàng)建“計算機(jī)配置器”的想法,都使得大多數(shù)堅毅的IT行家望而卻步。優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行請考慮一下,如果優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行這里的問題是這種復(fù)雜性評估很難用Java這種命令式語言去執(zhí)行,這種語言關(guān)注的是CPU一步一步的實現(xiàn)。從本質(zhì)上說,我們告訴機(jī)器的是怎樣做這項工作,因此這意味著我們必須對各種需要被評估的條件以及這些條件需要被考慮的順序非常明了。這會導(dǎo)致產(chǎn)生像下面這樣復(fù)雜和難以維護(hù)的代碼:if(currentPC.drives().contains("DVD")){if(currentPC.videoCard().equals("KorSplatt5900")&&!(currentPC.drives().get("DVD").equals("CD-RW/DVD"))){warn("DVDincompatiblewithKorSplatt5900");}}elseif(currentPC.videoCard().equals("KorSplatt5900")&¤tPC.memory()<512){//...}
優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行這里的問題是這種優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行這種類型的處理是一種特殊的編程模式,稱作基于規(guī)則的程序設(shè)計,并且幸運的是我們能夠得到這些規(guī)則引擎,這種軟件可以檢驗數(shù)據(jù)集,查看在引擎中聲明的規(guī)則列表,然后推斷應(yīng)該引發(fā)那條規(guī)則以響應(yīng)數(shù)據(jù)的當(dāng)前狀態(tài)。更重要的是,規(guī)則引擎接著可以在必要時重新應(yīng)用這些規(guī)則到發(fā)生了變更的數(shù)據(jù)上,直到數(shù)據(jù)到達(dá)一種穩(wěn)定狀態(tài)不再觸發(fā)更多的規(guī)則為止。優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行這種類型的處理是優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行規(guī)則引擎通常服務(wù)于兩個目的:(1)以最好的方式捕獲業(yè)務(wù)規(guī)則(2)允許修改這些規(guī)則而不需要重新編碼Java代碼本身。第一個目的通常是最大的收獲,因為在一大堆的if/then/else語句中去盡力追蹤業(yè)務(wù)規(guī)則不僅難于實現(xiàn)而且易于發(fā)生錯誤。然而,如果你的用戶足夠老練,那么教會他們規(guī)則引擎能夠理解的“規(guī)則語言”還會有一個附加的好處,即給予了他們修改應(yīng)用的業(yè)務(wù)邏輯的核心部分的能力,從而有效地將程序員從業(yè)務(wù)邏輯改變的循環(huán)中跳出,因為業(yè)務(wù)邏輯的改變可能需要另外一個完整的開發(fā)周期(開發(fā)、測試、QA、發(fā)布、配置等等)。優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行規(guī)則引擎通常服務(wù)優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行RuleServiceProviderprovider=//...//EitheruseaJNDIlookup,orelseuse//RuleServiceProviderManager,similarinconcept//totheJDBCDriverManagerRuleAdministratoradmin=provider.getRuleAdministrator();RuleExecutionSetruleSet=null;//Loadtherulesfromalocalfileusing//LocalRuleExecutionSetProviderFileReaderreader=newFileReader("rules.xml");try{HashMapprops=newHashMap();props.put("name","ConfigurationRules");props.put("description","RulebaseforcompanyPCconfiguration");LocalRuleExecutionSetProviderlresp=admin.getLocalRuleExecutionSetProvider(props);ruleSet=lresp.createRuleExecutionSet(reader,props);}Finally{reader.close();}admin.registerRuleExecutionSet("rules",ruleSet,props);
優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行RuleServ優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行rules.xml文件正如它的名字所暗示的那樣,包含一些用面向XML的格式表示的規(guī)則。一旦引擎的初始化完成,在運行時使用它就顯得相當(dāng)簡單直接了。JSR-94定義了兩種RuleSession類型,有狀態(tài)的和無狀態(tài)的,大致與EJB的會話Bean的有狀態(tài)和無狀態(tài)的差異相對應(yīng):有狀態(tài)的RuleSession在多個調(diào)用之間保留有它自己的工作內(nèi)存,而無狀態(tài)的RuleSession則沒有。這意味著在無狀態(tài)的RuleSession中,那些(為了評估規(guī)則)被添加到工作內(nèi)存的對象,在規(guī)則被評估之后,即消失了。優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行rules.xm優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行//CreatetheRuleSessioninstanceRuleRuntimeruntime=rep.getRuleRuntime();StatefulRuleSessionsrs=(StatefulRuleSession)runtime.createRuleSession("rules",props,RuleRuntime.STATEFUL_SESSION_TYPE);//PopulatetheRuleSession'sworkingmemorywiththedata//toevaluateagainstsrs.addObject(newInteger(12));srs.addObject("Hello,world");srs.addObject(newCustomDataObject());//Executetherulessrs.executeRules();//Examinetheentirecontentsoftheworking//memory—objectsmayhavebeenmodifiedby//executingrulesListresults=srs.getObjects();//ReleasetheRuleSessionsrs.release();
優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行//Create優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行與SQL和XSLT很相似,大多數(shù)規(guī)則語言本質(zhì)上是命令式的,意思是你不必像指定什么時間去做和應(yīng)該做什么那樣指出怎樣去做——也就是,你指定某種謂詞條件,去說明觸發(fā)的規(guī)則和在規(guī)則觸發(fā)時應(yīng)采取的動作。例如,在構(gòu)成完備的類似XML的規(guī)則語言中,一條用于PC配置器的規(guī)則可能像這里展示的代碼那樣,在這里,條件元素是某種類似XPath的查詢語法,行為元素像某種腳本語言。<rule><condition>(drives[@type="DVD"and@type!="CD-RW/DVD"]>1)and(video[@mfr="KorSplatt"]andvideo[@version="5900"])</condition><action>put("KorSplatt5900isincompatiblewithregularDVD")</action></rule>
優(yōu)先采用規(guī)則引擎去
處理復(fù)雜狀態(tài)的評估和執(zhí)行與SQL和XS處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔優(yōu)先為隱含的非原子性
錯誤場景采用事務(wù)性處理作為一個程序員,我們習(xí)慣于那種使用隱含地是原子性的語言去實現(xiàn)各種操作的模式:也就是說,被調(diào)用的函數(shù)要么返回正確的值,要么返回某種錯誤代碼或異常。遺憾的是,這種原子性操作完全是一種假象。在我們編程的高層幾乎沒有任何事物是真正原子性的。例如,當(dāng)我們調(diào)用Math.sqrt方法時,我們所用的執(zhí)行線程依次開始執(zhí)行在Math.sqrt定義內(nèi)的代碼,將這些代碼再依次分解成單獨的CPU指令,在CPU上依次連續(xù)地執(zhí)行這些指令,而CPU肯定周期性地請求總線從RAM獲取某些額外的數(shù)據(jù),等等。很多工作是“在遮蓋下”完成的以呈現(xiàn)出這種視覺上的原子性——實際上,就是這語言和/或編譯器的大部分工作,極大地掩蓋這種復(fù)雜性,因此就不會“妨礙”到我們真正想做的,在這里就是計算出平方根。優(yōu)先為隱含的非原子性
錯誤場景采用事務(wù)性處理作為一個程序員,優(yōu)先為隱含的非原子性
錯誤場景采用事務(wù)性處理從編程人員的角度來看,事務(wù)的基本模型很簡單:你依據(jù)資源管理器創(chuàng)建一個事務(wù),其中資源管理器就是你想進(jìn)行處理的事物。事務(wù)性資源管理器當(dāng)然就是關(guān)系型數(shù)據(jù)庫,但是其它資源管理器當(dāng)然也是有可能的,包括但不僅限于JMS消息代理、遺留的大型機(jī)系統(tǒng)或其它用Connector訪問的系統(tǒng),甚至可能是底層的文件系統(tǒng),如果文件系統(tǒng)支持的話。優(yōu)先為隱含的非原子性
錯誤場景采用事務(wù)性處理從編程人員的角度處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)一個用戶正在使用在線銀行系統(tǒng)。她只是想進(jìn)行簡單的余額過戶,從她的現(xiàn)金賬號過戶到她的支票賬戶(可能包括她今天稍早時候簽過的大額支票)。她選擇菜單中余額過戶(BalanceTransfer)選項,從顯示的賬戶列表中選擇她的支票賬戶,輸入她想過戶的錢數(shù),從顯示的第二個賬戶列表中選擇她的現(xiàn)金賬戶,然后點擊執(zhí)行(Go)。這里到底用到了多少個事務(wù)呢?問題并不像看起來的那樣簡單。對于用戶以及任何一個會計學(xué)的學(xué)生來說,這似乎好像是只有一個事務(wù),但是對于系統(tǒng)來說,可能是有兩個數(shù)據(jù)庫的事務(wù),尤其是如果我們正在和多個數(shù)據(jù)庫或其它資源通信區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)一個用戶正在使用在線銀行系統(tǒng)。她只是想處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔最小化鎖窗口“作為一條規(guī)則,在被同步的區(qū)域內(nèi),你應(yīng)該執(zhí)行盡可能少的操作。”對于企業(yè)級系統(tǒng),這個規(guī)則也有效,在企業(yè)級系統(tǒng)中,共享資源中的鎖將引發(fā)競爭,從而限制了系統(tǒng)最終的可擴(kuò)展性??紤]這個繁瑣的例子,其中一個線程調(diào)用一個非同步方法三千五百萬次,在這之后調(diào)用另一個等同的同步方法三千五百萬次,然后比較作為結(jié)果的計時時間。我們打算為這個有點瑣碎的代碼寫出三個不同的版本一個完全不使用任何同步一個在方法調(diào)用本身上使用一個被同步的修改器另一個使用粒度更粗的被同步的方法。執(zhí)行分成兩部分第一部分將從一個單一線程中執(zhí)行一個方法三千五百萬次然后我們新增加一個沒有任何其它影響的由命令行傳入的線程數(shù),并讓這些線程每一個都執(zhí)行三千五百萬次。
最小化鎖窗口“作為一條規(guī)則,在被同步的區(qū)域內(nèi),你應(yīng)該執(zhí)行盡可最小化鎖窗口首先,我們可以很清楚地看到非同步版本是最快的。第二,我們還可以很清楚地看到粗粒度的同步版本比細(xì)粒度的同步版本花費時間要少很多,不過對于細(xì)粒度同步版本,有一件奇怪的事情:執(zhí)行花費的時間好像與線程結(jié)束的時間成線性比例。實際上,這正是很精確地對應(yīng)于所發(fā)生的事情——第一個調(diào)度線程搶占鎖,并在整個三千五百次迭代循環(huán)中持有該鎖,期間從沒有放棄過該鎖,因此其它的線程必須等待直到釋放鎖,才能去為得到它而競爭。到最后一個線程獲得鎖的時候,這個線程已經(jīng)等待了一段時間。第三,在細(xì)粒度的機(jī)制中,在每次調(diào)用上都會獲得和釋放監(jiān)視器,因此產(chǎn)生了最平均的數(shù)字,不過也最差。簡而言之,競爭是可擴(kuò)展性的敵人。系統(tǒng)中存在越多的競爭,我們就需要等待越長的時間,反過來又會加重系統(tǒng)的整體的響應(yīng)延遲。我們不能擴(kuò)展一個存在競爭問題的系統(tǒng)。最小化鎖窗口首先,我們可以很清楚地看到非同步版本是最快的。最小化鎖窗口緊記兩點。首先,當(dāng)你有別的辦法時,就請避免使用鎖——即使沒有任何其他人最終和你競爭鎖,但它仍要把花費寶貴的時間花在只是獲得監(jiān)視器和將它釋放上。第二,盡可能地減少花費在同步區(qū)域內(nèi)的時間最小化鎖窗口緊記兩點。首先,當(dāng)你有別的辦法時,就請避免使用鎖處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔優(yōu)先使用本地事務(wù)
而不是分布式事務(wù)如果我們有兩個需要作為某個單一處理的一部分而被操作的資源,那么會發(fā)生什么呢?大量的數(shù)據(jù)庫開發(fā)商聚集到一起定義了分布式事務(wù)協(xié)議,稱作兩階段提交協(xié)議(TPC,two-phasecommitprotocol)。在TPC中,我們正式地定義了作為每個事務(wù)的一部分的三個參與方:客戶端;資源管理器(RM,ResourceManager),它提供了我們試圖共享訪問的共享資源;和事務(wù)管理器(TM,TransactionManager),它被用來創(chuàng)建分布式事務(wù)并處理客戶和RM之間的交互。不過,沒有免費的午餐,TPC也有它自身的開銷。優(yōu)先使用本地事務(wù)
而不是分布式事務(wù)如果我們有兩個需要作為某優(yōu)先使用本地事務(wù)
而不是分布式事務(wù)除非你絕對必須使用分布式事務(wù),否則你就應(yīng)該避免使用它,因為分布式事務(wù)意味著你必須在多個資源上(數(shù)據(jù)庫、JMS提供者、JCA的Connector提供者等等)具有ACID屬性。如果你想使你的事務(wù)窗口盡可能的短,由于TPC協(xié)議增加的通信需求,你將會在所有可能的地方使用本地事務(wù)。優(yōu)先使用本地事務(wù)
而不是分布式事務(wù)除非你絕對必須使用分布式處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制樂觀的并發(fā)模型,也稱作樂觀離線鎖(OptimisticOfflineLock)或者樂觀鎖(OptimisticLock),提供了一種方法,可以兼顧兩方,并雙收其利——給你增加了一小部分的額外工作,同時你還可以將實際取出的鎖的數(shù)量保持到最小。實際上樂觀并發(fā)可以按照不同的形式實現(xiàn);版本號(VersionNumber)就是這樣的一種方法在這種方法中,每個表包含一個增添的版本號列;其它方法喜歡使用最新修改的時間戳。使用版本號會給你一個提示,以告訴你自從你上次查看這個數(shù)據(jù)開始,它已經(jīng)被修改了多少次(如果你持有的是版本1,而當(dāng)前版本是20,說明這個數(shù)據(jù)已經(jīng)被修改過多次,那么你可能會選擇將它丟棄然后重新開始),它不是以簡單的時間戳方式來呈現(xiàn)的。另一方面,時間戳方法可以讓你在方法執(zhí)行過程中獲得數(shù)據(jù)庫的臨時要素,這對于審計來說極為有用。為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制樂觀的并發(fā)模型,也為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制在表中存在的問題是應(yīng)該怎樣處理數(shù)據(jù),這些數(shù)據(jù)現(xiàn)在已經(jīng)不能夠和在數(shù)據(jù)庫中存儲的數(shù)據(jù)保持同步了。放棄:直接將修改丟棄,用最近被修改過的數(shù)據(jù)再重新開始。在某些情形下,這是最好的方法,尤其是對于修改幅度很大,或者如果使用者不是真的有能力去決定哪些值應(yīng)該被合并的情況更是如此。它不要求你必須要告訴用戶你做了什么,不過——放棄而不告知用戶是相當(dāng)不友好的做法。覆蓋:有時最好的方法就是讓最后的更新勝出。不過如果是這種情形,那么,首先就真的沒有進(jìn)行樂觀加鎖的必要了——盲目的UPDATE也能很好地運轉(zhuǎn)。合并:獲得該用戶的數(shù)據(jù),與原來的數(shù)據(jù)進(jìn)行比較,然后合并所做的修改。不過,這是一種充滿技巧的方法,因為你要艱難地決定應(yīng)該怎樣進(jìn)行合并。為了知道你的用戶修改了哪些列,你怎么才能知道源數(shù)據(jù)集中哪些數(shù)據(jù)被修改過,萬一用戶數(shù)據(jù)和當(dāng)前數(shù)據(jù)與源數(shù)據(jù)都不一致,那么哪一個該獲勝?在某些情形中,最好的方法就是向用戶詢問部分或所有的合并。詢問:這是與用戶交談的確定對話,“數(shù)據(jù)已經(jīng)被改變了,你想放棄、覆蓋還是合并?”正如已經(jīng)描述過的那樣,合并最多只能算是一種艱難的指望,因此通?!霸儐枴狈椒ㄖ苯訉で笥卯?dāng)前的新數(shù)據(jù)重載該數(shù)據(jù),并讓用戶再次去編輯那些需要改變的數(shù)據(jù),這是一種混雜的放棄/詢問方法。為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制在表中存在的問題是為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制樂觀并發(fā)提供了一系列好處,首要好處是:在修改先前讀取的數(shù)據(jù)期間,不需要取出任何本地數(shù)據(jù)庫上的鎖。換句話說,客戶端讀取數(shù)據(jù)的代碼不必“預(yù)先”決定是否要取出鎖。樂觀并發(fā)方法帶來的另外一個好處,就是提高了診斷能力。萬一其他用戶修改了數(shù)據(jù),你的代碼就有機(jī)會告知你的用戶發(fā)生了什么,以及應(yīng)該怎樣處理。為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制樂觀并發(fā)提供了一系為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制遺憾的是,樂觀并發(fā)模型并不完美。尤其是,你依賴于應(yīng)用代碼來保持并發(fā)模型井井有條。通常,樂觀并發(fā)模型純粹被用作為一種可擴(kuò)展性的措施,通過減少獲取的本地數(shù)據(jù)庫鎖的實際數(shù)量以及它們持有的時間長度,來最小化鎖窗口。通常,對大多數(shù)企業(yè)級Java系統(tǒng)來說,樂觀并發(fā)模型可能應(yīng)該是缺省的并發(fā)模型——依賴于底層數(shù)據(jù)庫來提供在短期內(nèi)讓所有的并發(fā)都能夠運行的能力,但是最終當(dāng)你擴(kuò)展系統(tǒng)的時候,仍將會引發(fā)競爭。為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制遺憾的是,樂觀并發(fā)處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔為了顯式的并發(fā)控制而考慮
使用悲觀的并發(fā)機(jī)制樂觀鎖定假設(shè):在更新可以被接受的時候要接收到通告。不過,在其它情形中,在一個更新將要引發(fā)一個問題之前,樂觀并發(fā)模型不會發(fā)送通告??紤]一個Web應(yīng)用,要求用戶通過復(fù)雜的12屏的處理才能更新一個訂單。如果你由于一個錯誤消息“對不起,別人已經(jīng)更新過這個訂單”而返回,并且強制用戶從第一個頁面重新開始,那么你就要小心,因為很快你就要開始找尋另一份新的工作了。為了顯式的并發(fā)控制而考慮
使用悲觀的并發(fā)機(jī)制樂觀鎖定假設(shè):在為了顯式的并發(fā)控制而考慮
使用悲觀的并發(fā)機(jī)制悲觀并發(fā)模型(PessimisticConcurrencyModel)(也稱作悲觀脫機(jī)鎖(PessimisticOfflineLock),在有些地方稱作悲觀鎖(PessimisticLock)。盡管它也有若干缺點,但是它提供給你的所有好處就是:讓你,編程人員能夠?qū)σ獔?zhí)行的事物進(jìn)行更多的控制。在系統(tǒng)的并發(fā)能力方面,悲觀并發(fā)方案提供給你巨大的力量;不過,記著,正如漫畫書中的英雄所領(lǐng)悟到的艱辛之路:“強大的力量伴隨著強大的責(zé)任?!北^并發(fā)給予你極大的靈活性,但是這取決于你去確保你所選擇的并發(fā)決策是可靠的;否則,數(shù)據(jù)污染就是最終結(jié)果。為了顯式的并發(fā)控制而考慮
使用悲觀的并發(fā)機(jī)制悲觀并發(fā)模型(P處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量
大多數(shù)據(jù)庫系統(tǒng)支持降低事務(wù)隔離級別這種思想,要么通過一個直接的SQL調(diào)用,要么通過在Java中被稱為JDBC的調(diào)用級接口來實現(xiàn)。下面標(biāo)準(zhǔn)的隔離級別以及作為結(jié)果所產(chǎn)生的影響都可以在標(biāo)準(zhǔn)的SQL中獲得:SERIALIZABLE:不會產(chǎn)生上面講到的任何影響。REPEATABLEREAD:此隔離級別確保對用戶維護(hù)相同的數(shù)據(jù)庫視圖——在那里的數(shù)據(jù)仍然在那里。它防止臟讀和不可重復(fù)讀發(fā)生,但是允許幻影讀發(fā)生。READCOMMITTED:這個級別允許客戶端事務(wù)查看其它已提交事務(wù)的動作,因此可以防止臟讀,但是允許不可重復(fù)讀和幻影讀。READUNCOMMITTED:這個級別能夠防止混亂,它允許用戶查看其它還未提交(或可能無法提交)的事務(wù)的動作。它允許臟讀、不可重復(fù)讀以及幻讀;簡言之,除了直接污染數(shù)據(jù),它允許很多事情。注意,事務(wù)的其它三個屬性仍舊是完全有效的:隔離級別為READUNCOMMITTED的事務(wù)仍舊是原子性的,仍舊是一致性的,仍舊是持久性的——它要么全部成功,要么全部失敗,它總是在數(shù)據(jù)庫上產(chǎn)生相同的效果,并且當(dāng)事務(wù)完成的時候,它的動作就會被存儲起來。這里唯一被改變的事情是事務(wù)對系統(tǒng)其它部分的可見性,反之亦然。考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量大多數(shù)據(jù)庫系統(tǒng)處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔面臨回滾時使用保存點
來保留部分工作遺憾的是,事務(wù)的原子性并不總是一件好事情。尤其是,原子性的事務(wù)引起的問題之一就是:當(dāng)在事務(wù)的工作區(qū)內(nèi)某事物出現(xiàn)問題時,那么每件事情就都有問題。一旦數(shù)據(jù)庫語句在事務(wù)內(nèi)失敗,那么作為該事務(wù)一部分的每一點工作行為現(xiàn)在都被視為是無效的,因此必須放棄。通常情況下,我們認(rèn)為這種行為是積極的。畢竟,我們需要原子性事務(wù)的原因在于:萬一發(fā)生錯誤時,我們不必編寫回滾或者補償事務(wù)的代碼來撤銷我們已經(jīng)完成的工作。盡管這種行為是我們在大多數(shù)時間內(nèi)都想擁有的行為,但是有時它也會不符合我們的想法。面臨回滾時使用保存點
來保留部分工作遺憾的是,事務(wù)的原子性并面臨回滾時使用保存點
來保留部分工作當(dāng)出現(xiàn)錯誤時,保存點的好處就顯現(xiàn)出來了。如果事務(wù)的一部分突然失敗,我們可以回滾事務(wù)到最近的保存點,因此僅僅放棄由最近的保存點作為邊界的已完成工作,然后重新開始;保存點提供了一個機(jī)會來保存已經(jīng)在一個事務(wù)中完成的工作,因此,我們可以選擇事務(wù)工作是原子性的還是非原子性的。這是好消息;壞消息是保存點模式是JDBC3.0規(guī)范中的新成員,并非所有的數(shù)據(jù)庫驅(qū)動都真正支持它。如果你想了解自己現(xiàn)在正在使用JDBC驅(qū)動,那就要確保去查實它是否支持保存點。面臨回滾時使用保存點
來保留部分工作當(dāng)出現(xiàn)錯誤時,保存點的好處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔當(dāng)有可能避免鎖定區(qū)域時
就復(fù)制數(shù)據(jù)源想象你現(xiàn)在是一個幼兒園老師。20個孩子,差不多都是5歲的樣子,似乎永遠(yuǎn)都在你的照料之下,而實際上只是每天4個小時。當(dāng)休息時,你帶他們到操場上活動筋骨,釋放能量。小Johnny抓住繩子開始跳。很自然地,小孩子有他們的天性,另外19個孩子也會一起大叫到“不公平!”他們會在同一時間都想玩這唯一的一條繩子。你首先會告訴他們每個人輪流玩。畢竟,這是你作為一名教師應(yīng)該做的:教導(dǎo)孩子們成為世界上行為端正的公民,開始教導(dǎo)怎樣去分享。任何提出這種建議的人,永遠(yuǎn)都不可能讓這19個5歲的孩子站成一排去等待超過2分鐘。問題是:假定每個孩子可以拿繩子跳一分鐘,之后必須將繩子交給隊列中的下一個孩子,然后回到隊列后面。這意味著,每個孩子在19分鐘內(nèi)完全不做任何事情只是等待(假定,每個孩子在他或她的輪次結(jié)束的時候,很樂意將繩子交出,完全不像個5歲孩子)。也就是,每20分鐘才能活動一分鐘,那么也就沒有怎么釋放能量。當(dāng)有可能避免鎖定區(qū)域時
就復(fù)制數(shù)據(jù)源想象你現(xiàn)在是一個幼兒園老當(dāng)有可能避免鎖定區(qū)域時
就復(fù)制數(shù)據(jù)源解決方案是什么?一種方法是讓每個孩子跳得更快,每個輪次是30秒,但是這樣做加速了隊列的前進(jìn)速度——不過,它的比率實際上仍是每20分鐘活動一分鐘。相反,你可能會通過跳躍的次數(shù)來計算孩子們的輪次,但是這樣會懲罰那些跳得比較快的孩子,速度慢的孩子會因此而更長時間地獲得繩子,甚至?xí)屘每斓奶K者活動得更少。如果詢問其他幼兒園老師應(yīng)該怎樣解決這個問題,他們都會告訴你:買更多的跳繩。單一資源的關(guān)鍵問題是:這些資源通常會創(chuàng)造出競爭點,以至于必須使用同步結(jié)構(gòu)來管理。一個單一競爭點會引起系統(tǒng)無法擴(kuò)展,因為添加的硬件只會引入更多的客戶去競爭這個單一競爭點;換句話說,當(dāng)我們試圖將幼兒園教室從20個學(xué)生擴(kuò)展到200或者2000,這種只有一條跳繩的情況會變得更糟。當(dāng)有可能避免鎖定區(qū)域時
就復(fù)制數(shù)據(jù)源解決方案是什么?一種方法當(dāng)有可能避免鎖定區(qū)域時
就復(fù)制數(shù)據(jù)源在某些情形中,你可以將本地數(shù)據(jù)庫和樂觀并發(fā)結(jié)合起來,以完全在本地數(shù)據(jù)庫上運行你的應(yīng)用,只是在眾所周知的同步點上從數(shù)據(jù)庫中壓入和取出數(shù)據(jù)(每晚、每小時,或者每當(dāng)網(wǎng)絡(luò)被探測到的時候,等等)。復(fù)制不能解決所有的可擴(kuò)展性問題,不過當(dāng)我們謹(jǐn)慎使用時,它可以為單一資源緩解相當(dāng)大的一部分壓力。要想讓復(fù)制成功的關(guān)鍵在于,要最終確保你復(fù)制的資源是沒有標(biāo)識的,因為一旦復(fù)制的是獨一無二的數(shù)據(jù),我們就會陷入一致性的問題,對于這個問題,仍沒有任何很好的通用解決方案。當(dāng)有可能避免鎖定區(qū)域時
就復(fù)制數(shù)據(jù)源在某些情形中,你可以將本處理第1項:保持簡潔第2項:優(yōu)先采用規(guī)則引擎去處理復(fù)雜狀態(tài)的評估和執(zhí)行第3項:優(yōu)先為隱含的非原子性錯誤場景采用事務(wù)性處理第4項:區(qū)分用戶事務(wù)和系統(tǒng)事務(wù)第5項:最小化鎖窗口第6項:優(yōu)先使用本地事務(wù)而不是分布式事務(wù)第7項:為了更好的可擴(kuò)展性而考慮使用樂觀的并發(fā)機(jī)制第8項:為了顯式的并發(fā)控制而考慮使用悲觀的并發(fā)機(jī)制第9項:考慮使用較低的隔離級別以獲得更大的事務(wù)吞吐量第10項:面臨回滾時使用保存點來保留部分工作第11項:當(dāng)有可能避免鎖定區(qū)域時就復(fù)制數(shù)據(jù)源第12項:偏愛不可變的,因為它不需要任何鎖
處理第1項:保持簡潔偏愛不可變的
因為它不需要任何鎖有一種方法可以將你從不得不擔(dān)心的同步問題中解救出來,那就是通過創(chuàng)建狀態(tài)永遠(yuǎn)不會改變的對象,來完全避免它——利用一個不可變對象是不會改變其狀態(tài)這個事實,同步就完全沒有必要了。這樣就避免了對synchronized語句塊和鎖的需要,從而也就避免了競爭沖突。使用這種方法也帶來了幾條限制。首先,不可變對象并不是很容易就能夠建立的——要想構(gòu)建一個對象,它沒有給可獲取其數(shù)據(jù)的用戶以任何后門,著實是看起來容易做起來難。第二,我們通常需要不可變對象去改變其狀態(tài),也就是說,我們想得到一個狀態(tài)稍微不同的新的不可變對象。這需要構(gòu)建一個全新的對象,而不是僅僅在現(xiàn)有對象中去梳理一些域,以獲得我們所感興趣的狀態(tài)。偏愛不可變的
因為它不需要任何鎖有一種方法可以將你從不得不擔(dān)偏愛不可變的
因為它不需要任何鎖構(gòu)建不可變對象比初看起來的時候要難一點;請參閱EffectiveJava去查看涉及到的更多細(xì)節(jié)。額外補充一個例子,看看下面這個標(biāo)準(zhǔn)類,用來表示地球上以碳為基礎(chǔ)的生命形式:publicclassPerson{publicPerson(StringfirstName,StringlastName,intage,AddresshomeAddress,Personspouse){
//Dowhatyou'dexpecthere—copyparameterstofields}publicStringgetFirstName(){returnthis.firstName;}publicStringgetLastName(){returnthis.lastName;}publicintgetAge(){returnage;}publicAddressgetHomeAddress(){returnthis.homeAddress;}publicPersongetSpouse(){returnspouse;}}偏愛不可變的
因為它不需要任何鎖構(gòu)建不可變對象比初看起來的時偏愛不可變的
因為它不需要任何鎖不仔細(xì)看,Person類似乎是完全不可變的——在這個類的API中哪里都沒有提供任何設(shè)置方法,因此不可能修改類成員,是這樣嗎?快速提問:如果我們這樣做,會發(fā)生什么?Personp=newPerson("Michael","Neward",10,newAddress(...),null);p.getAddress().setStreet("100WhiteHouseWay");可能不會很快就察覺到,Person類“遺漏”了一個被Person類中的homeAddress域所引用Address對象的句柄。這就給用戶提供了“曲線迂回到”引用對象(在此情形下就是Person),并直接修改它內(nèi)部的對象的能力。這就違反了Person的不可變性,會讓它遭受到Address對象中的同步問題所造成的影響。偏愛不可變的
因為它不需要任何鎖不仔細(xì)看,Person類似乎偏愛不可變的
因為它不需要任何鎖但是,當(dāng)我們確實想修改Person狀態(tài)的時候,我們必須創(chuàng)建一個全新的Person實例來實現(xiàn)。最簡單的方法就是重用上面定義的構(gòu)造器:Personp=newPerson("Michael","Neward",10,newAddress(...),null);//Michaeljusthadabirthday:incrementhisagep=newPerson(p.getFirstName(),p.getLastName(),p.getAge()+1,p.getAddress(),p.getSpouse());這種方法有一個問題,當(dāng)進(jìn)行不相干的同步保持時,就會強迫分配眾多的對象,給垃圾收集器帶來了更大的工作量,至少從原理上講是這樣。幸運的是,很多垃圾收集器在面臨眾多的短期存活的臨時對象時,能夠很好的運轉(zhuǎn),因此,這不是一個多大的問題。偏愛不可變的
因為它不需要任何鎖但是,當(dāng)我們確實想修改Per企業(yè)級應(yīng)用系統(tǒng)體系架構(gòu)(十一)
企業(yè)應(yīng)用的表示ChenHaopengSunday,October8,2023References:Ted
Neward:EffectiveEnterpriseJava50企業(yè)級應(yīng)用系統(tǒng)體系架構(gòu)(十一)
企業(yè)應(yīng)表示一切不過是對話框和數(shù)據(jù)而已。
——MikeCohn表示:看起來真是太簡單了。只要隨便準(zhǔn)備一些對話框和按鈕,再加上幾個列表框和滾動條,用戶就能夠很容易地明白這是什么意思了,對嗎?只要獲取用戶的輸入,進(jìn)行一些處理,再把它發(fā)送回服務(wù)器,我們就算完成任務(wù)了。表示一切不過是對話框和數(shù)據(jù)而已。表示對許多開發(fā)者來說,構(gòu)建用戶界面很可能算是工作中最有趣的部分。其中部分原因在于:與系統(tǒng)中的其它部分不同,用戶界面是看得見,摸得著的,它是衡量項目進(jìn)度的指示器。遺憾的是,對于盲目樂觀的UI程序員,這里潛藏著許多危險。許多開發(fā)者發(fā)現(xiàn)(通常是項目即將發(fā)布之前才會注意到),對于系統(tǒng)應(yīng)該做什么,用戶有一個嚴(yán)格的概念模型,一旦用戶界面不能反映這個概念模型,那么用戶的贊譽馬上就會變成指責(zé)。因為開發(fā)者常常會在最后關(guān)頭做出突發(fā)奇想式的修改,以安撫用戶,所以用戶界面常常是應(yīng)用中修改量最大的部分。表示對許多開發(fā)者來說,構(gòu)建用戶界面很可能算是工作中最有趣的部表示而且,許多用戶逐漸認(rèn)識到,程序員并非總是進(jìn)行用戶界面設(shè)計的最佳人選。VisualBasic之父AlanCooper,他也是用戶界面書籍《TheInmatesAreRunningtheAsylum》和《AboutFace2.0》的作者,一生都在從事有關(guān)面向客戶的用戶界面設(shè)計方面的咨詢工作。在給出的某些蹩腳的用戶界面設(shè)計的例子中,他從一些以用戶為中心的應(yīng)用中(主要來自太平洋西北部的一家大型軟件公司),引用了用戶界面方面的研究成果。作為開發(fā)者,在我們看來顯而易見的內(nèi)容,但是對于那些不以編寫代碼謀生的人來說,卻非?;逎蜕衩?。表示而且,許多用戶逐漸認(rèn)識到,程序員并非總是進(jìn)行用戶界面設(shè)計表示第1項:考慮富客戶端UI技術(shù)第2項:使HTML短小精悍第3項:表示與處理相分離第4項:內(nèi)容與樣式相分離第5項:預(yù)生成內(nèi)容以最小化處理過程第6項:盡早驗證,盡量驗證
表示第1項:考慮富客戶端UI技術(shù)考慮富客戶端UI技術(shù)這一條是指與瘦客戶端技術(shù)相比(基于HTML瀏覽器),能夠呈現(xiàn)更為豐富、更加強大的用戶界面的表示形式。與傳統(tǒng)上兩層的、胖客戶端的客戶/服務(wù)器應(yīng)用相比,基于HTML瀏覽器的瘦客戶端應(yīng)用具有明顯的優(yōu)勢:較低的甚至壓根就沒有的部署成本。因為HTML文件位于服務(wù)器,所以部署新版本的應(yīng)用只需在客戶端進(jìn)行零安裝。只要客戶端有Web瀏覽器,用戶就可以使用該系統(tǒng)。準(zhǔn)確地說,因為現(xiàn)在每個操作系統(tǒng)都把瀏覽器作為基本安裝的一部分,所以只要把應(yīng)用安裝到服務(wù)器端,客戶馬上就可以開始使用該應(yīng)用。更重要的是,因為不需要在客戶端機(jī)器上進(jìn)行更新,所以對應(yīng)用的更新可以按實際需要頻繁地進(jìn)行。這種“零部署場景”十分有用,尤其是對于企業(yè)級應(yīng)用系統(tǒng)。這是因為,企業(yè)級系統(tǒng)往往比那些放在貨架上銷售的軟件更容易發(fā)生變化。用戶甚至沒有必要知道這種版本的變化,無論如何,他們總是通過訪問服務(wù)器來使用系統(tǒng)的??紤]富客戶端UI技術(shù)這一條是指與瘦客戶端技術(shù)相比(基于HTM考慮富客戶端UI技術(shù)不過,HTML也存在著不利的方面:可移植性:HTML表單上的控件完全是按照主機(jī)(瀏覽器)的意愿進(jìn)行繪制的,所以在不同的平臺上,表單會具有不同的行為。狀態(tài)管理:因為Web瀏覽器不能持有客戶端狀態(tài)(cookie除外,不過它的作用非常有限),所以基于HTML的瘦客戶應(yīng)用需要在服務(wù)器上持有所有的用戶狀態(tài)??刂疲篐TML幾乎沒有提供多少辦法來對如何呈現(xiàn)頁面上的元素進(jìn)行控制。層疊樣式表(CSS)提供了某種控制能力,但盡管如此,也并非所有的瀏覽器都完全支持CSS,CSS也不能對所有的表示元素都提供完全的控制能力??缙脚_的差異性:盡管萬維網(wǎng)聯(lián)盟做出了巨大努力,但HTML仍舊是互聯(lián)網(wǎng)上最少被遵守的標(biāo)準(zhǔn)之一。兩種主流的瀏覽器,微軟公司的InternetExplorer和網(wǎng)景公司的NetscapeNavigator(Mozilla),都提供了顯著的擴(kuò)展功能。當(dāng)考慮到客戶端腳本語言的時候,問題就更嚴(yán)重了:InternetExplorer同時支持VBScript和Jscript,而Navigator則使用JavaScript??紤]富客戶端UI技術(shù)不過,HTML也存在著不利的方面:考慮富客戶端UI技術(shù)缺乏表示元素:HTML表單極其簡單,只提供了六種可用的控件:按鈕、單選按鈕、復(fù)選框、編輯控件(單行、多行和密碼輸入)和下拉列表框。盡管你對此可能有異議,但是對任何應(yīng)用,有這些“核心”控件就足夠了,并且HTML還可以通過可點擊的圖像來提供額外功能。但事實是,與瀏覽器所在的操作系統(tǒng)能夠提供的UI元素相比,HTML的表現(xiàn)能力非常有限。雖然能夠使用動態(tài)HTML(DHTML)或客戶端腳本語言來模擬菜單條和工具條,但是我們又會遇到跨平臺支持的問題。之所以還有如此多的應(yīng)用開發(fā)者愿意在開發(fā)過程中去忍受這些問題,只不過是看上“零部署”的好處罷了。實際上,許多應(yīng)用僅僅只支持HTML4.0,因此,應(yīng)用的用戶界面看起來就像是回到了1980年代的早期。考慮富客戶端UI技術(shù)缺乏表示元素:HTML表單極其簡單,只提考慮富客戶端UI技術(shù)這里的問題在于,開發(fā)者已經(jīng)忘記了自己采用基于HTML的界面的初衷:在部署新版本代碼的時候,很少的甚至是為零的部署成本。對于某些應(yīng)用,HTML當(dāng)然已經(jīng)足夠,不過考察一下其它UI技術(shù),看看是否既能夠得到零部署成本的好處,又能夠解決這些問題,也很不錯。實際上,確實有幾種富客戶技術(shù),既能夠提供零部署成本的好處,又擁有更豐富的客戶端功能。富客戶技術(shù)的一個主要好處在于狀態(tài)管理考慮富客戶端UI技術(shù)這里的問題在于,開發(fā)者已經(jīng)忘記了自己采用表示第1項:考慮富客戶端UI技術(shù)第2項:使HTML短小精悍第3項:表示與處理相分離第4項:內(nèi)容與樣式相分離第5項:預(yù)生成內(nèi)容以最小化處理過程第6項:盡早驗證,盡量驗證
表示第1項:考慮富客戶端UI技術(shù)使HTML短小精悍與標(biāo)準(zhǔn)的GUI應(yīng)用相比,Web應(yīng)用在很多方面都與之不同,最明顯的事實是,所有東西都要從服務(wù)器傳輸?shù)娇蛻舳恕_@不僅包括要顯示的數(shù)據(jù),還包括顯示數(shù)據(jù)所需要的格式代碼。這與舊式大型機(jī)上基于終端的應(yīng)用很相似;從HTTP服務(wù)器返回的響應(yīng)中,不僅包括了與用戶請求在邏輯上匹配的數(shù)據(jù),還包括了用于表示數(shù)據(jù)的HTML元素,以及用來在瀏覽器內(nèi)提供特定功能的某種客戶端腳本。然而,Web的實際情況是,頁面下載速度非常慢。請記住,對UI技術(shù)的研究表明,用戶在放棄等待,然后訪問其它站點以前,平均會等待5秒鐘。使HTML短小精悍與標(biāo)準(zhǔn)的GUI應(yīng)用相比,Web應(yīng)用在很多方使HTML短小精悍那么,究竟是什么原因,使得精心設(shè)計,功能正確的應(yīng)用慢得像蝸牛爬呢?很大一部分原因是由于返回的HTML頁面。所有這些內(nèi)容都要從服務(wù)器移動到客戶端,這就意味著二者之間的管道被用來傳輸這些不必要的內(nèi)容。這就導(dǎo)致了其它客戶可用帶寬的降低,也就降低了應(yīng)用整體上的可擴(kuò)展性。遺憾的是,這也屬于不能“通過添加更多的服務(wù)器硬件來解決問題”的領(lǐng)域之一,進(jìn)入建筑(或數(shù)據(jù)中心)的管道是瓶頸所在,要進(jìn)行擴(kuò)展將非常昂貴。使HTML短小精悍那么,究竟是什么原因,使得精心設(shè)計,功能正使HTML短小精悍為了防止不佳的HTML破壞你的應(yīng)用,請牢記以下技巧。盡量少用“重量級”標(biāo)記。APPLET、OBJECT和IMG標(biāo)記都不能提供瀏覽器所需的所有內(nèi)容,還需要和服務(wù)器進(jìn)行另一次HTTP傳輸,這就意味著這段時間內(nèi)用戶界面將保持“停頓”,從這種角度看,它們都屬于“重量級”標(biāo)記。許多瀏覽器能夠繼續(xù)解析和處理頁面的后續(xù)內(nèi)容,但是會留下一大塊丑陋的空白區(qū)域,用來放置圖片、applet,或其它內(nèi)容。這就是令用戶“覺得”慢的部分原因。使用框架(frame)來劃分頁面的不同部分。如果Web應(yīng)用有一個在上方顯示的主菜單,可以把它放到一個單獨的(無邊)框架中,這樣菜單就只需要獲取一次。在頁面下方的版權(quán)標(biāo)志,也可以這樣處理。盡可能重用頁面上的圖像。因為瀏覽器(包括代理服務(wù)器和防火墻這樣的中繼處理節(jié)點)傾向于對網(wǎng)站上下載的數(shù)據(jù)進(jìn)行緩存,通過在不同頁面中重用相同的圖像,就提高了頁面的載入性能。這使得瀏覽器能夠使用已緩存的圖像,而不是從Web站點重新下載。不過,對圖像的引用必須完全匹配,所以,為了在不同頁面中統(tǒng)一圖像的URL,你要確保所有圖像都來自于服務(wù)器上的某個常見目錄中。使HTML短小精悍為了防止不佳的HTML破壞你的應(yīng)用,請牢記使HTML短小精悍使用HTML的特性,而不是圖像。HTML標(biāo)記提供了豐富的功能,要顯示它們也無需額外的下載。例如,與其把超鏈接放進(jìn)圖像,來實現(xiàn)圖像“按鈕”的功能,還不如在標(biāo)準(zhǔn)文本上用不同的背景顏色,前景顏色,字體進(jìn)行修飾,也能得到類似的效果。這樣就節(jié)省了到服務(wù)器上取得圖像的時間。遵循高雅、實用的用戶界面設(shè)計原則,避免過多的頁面導(dǎo)航(或者換句話說,避免網(wǎng)絡(luò)傳輸)。如果在基于Web的應(yīng)用中采用向?qū)эL(fēng)格的界面,那么假如向?qū)е械拿恳徊蕉奸g隔了10秒種,以等待服務(wù)器對下一步進(jìn)行響應(yīng),這種方案很快就會崩潰??梢栽囍褞讉€步驟置于一個頁面中,可能的話,就完全拋棄向?qū)эL(fēng)格的界面。使HTML短小精悍使用HTML的特性,而不是圖像。HTML標(biāo)表示第1項:考慮富客戶端UI技術(shù)第2項:使HTML短小精悍第3項:表示與處理相分離第4項:內(nèi)容與樣式相分離第5項:預(yù)生成內(nèi)容以最小化處理過程第6項:盡早驗證,盡量驗證
表示第1項:考慮富客戶端UI技術(shù)表示與處理相分離考慮以下(簡化過的)JSP頁面:<%@pagelanguage="Java"%><%@pageimport="java.sql.*,javax.sql.*"%><html><head><title>ProductSearchResults</title></head><%Connectionconn=application.getAttribute("dbconn");
//AssumeJDBCConnectionwasstoredinServletContextStringSQL="SELECTname,sku,stockFROMproducts"+"WHEREname=?ANDstock>0";PreparedStatementstmt=conn.prepareStatement(SQL);stmt.setString(1,request.getParameter("name"));ResultSetrs=stmt.executeQuery();ArrayListresults=newArrayList();while(rs.next()){HashMaphm=newHashMap();hm.add("name",rs.getString(1);
hm.add("sku",rs.getString(2);表示與處理相分離考慮以下(簡化過的)JSP頁面:表示與處理相分離hm.add("stock",rs.getString(3);results.add(hm);}stmt.close();//Aggressivelyreleaseresources;%><body><h1>ProductSearchResults</h1><%if(results.size()==0){%><b>Noitemsfoundthatmatchyourquery;tryagain?</b><%}else{%><table><tr>表示與處理相分離hm.add("stock",rs.g表示與處理相分離<th>ProductName</th><th>ProductSKU</th><th>StockCount</th></tr><%for(Iteratoriter=results.iterator();iter.hasNext();){Mapcurrent=(Map)iter.next();%><tr><td><%=current.get("name")%></td>
<td><%=current.get("sku")%></td>
<td><%=current.get("stock")%></td></tr><%}%></table><%表示與處理相分離<th>ProductName</th表示與處理相分離}%></body></html>看出問題了嗎?沒有的話,也別擔(dān)心。許多JSP頁面就是這么編寫的,并且這種編程風(fēng)格似乎很流行,尤其是在許多有關(guān)JSP的文章和書籍中。在某些情況下,這樣確實可以簡化范例。不過問題在于,當(dāng)項目的交付日期很緊的時候,讀者未必能認(rèn)識到(或者關(guān)心)這個問題,這就導(dǎo)致了此種風(fēng)格的代碼混進(jìn)產(chǎn)品代碼中。這里的危險之處很隱蔽,業(yè)務(wù)規(guī)則(此處是指,搜索請求只能針對庫存里存在的產(chǎn)品)混進(jìn)了JSP頁面之中。這樣一來,業(yè)務(wù)規(guī)則就會與“用于顯示結(jié)果的代碼”出現(xiàn)在同一層次之中,這種混和很危險,通常應(yīng)該進(jìn)行隔離。表示與處理相分離}表示與處理相分離把表示層代碼和處理邏輯相分離帶來的額外好處,在于為并行開發(fā)開來了機(jī)會。編寫一個隔離的層次,表示層設(shè)計者就不用關(guān)心查詢的真正細(xì)節(jié),這樣他就能夠使用可以傳回模擬數(shù)據(jù)的模擬對象,對表示層進(jìn)行測試。這樣就能夠幫助減輕項目進(jìn)度的壓力,用戶可以盡可能快地看到表示層,他們就可以明確將得到的產(chǎn)品。表示與處理相分離把表示層代碼和處理邏輯相分離帶來的額外好處,表示第1項:考慮富客戶端UI技術(shù)第2項:使HTML短小精悍第3項:表示與處理相分離第4項:內(nèi)容與樣式相分離第5項:預(yù)生成內(nèi)容以最小化處理過程第6項:盡早驗證,盡量驗證
表示第1項:考慮富客戶端UI技術(shù)內(nèi)容與樣式相分離乍一看,這一條似乎不過是前一項的重復(fù)。盡管它們在思路上有些共同點,但內(nèi)容還是有些不同。在前一項中,我們考慮把表示層邏輯和處理邏輯相隔離,這里我們則試圖把表示的樣式與內(nèi)容區(qū)分開來。不過,在我們深入討論之前,我們需要先定義二者的不同。內(nèi)容是指,從某個請求返回的實際數(shù)據(jù)。這些數(shù)據(jù)沒有任何顯示機(jī)制或相關(guān)標(biāo)記。例如,當(dāng)考慮在Web網(wǎng)站上進(jìn)行搜索的時候,內(nèi)容就是搜索所返回的各種數(shù)據(jù)項(或者就是“未找到項目”這樣的文字)。另一方面,樣式則關(guān)系到一些審美元素,我們使用樣式來修飾內(nèi)容,以在視覺上吸引用戶。字體尺寸、背景/前景顏色、布局等都屬于樣式。一條基本經(jīng)驗是,我們從頁面中移除樣式,而不會影響頁面的可用性;但是,我們無法在不影響可用性的前提下移除內(nèi)容。搜索結(jié)果即使不加任何修飾,僅以逗號風(fēng)格,仍舊是有用的;但是即使格式再漂亮的頁面,沒有內(nèi)容(甚至連“未找到項目”也沒有)的話,也是無用的。內(nèi)容與樣式相分離乍一看,這一條似乎不過是前一項的重復(fù)。盡管它內(nèi)容與樣式相分離至少有兩種技術(shù)可以解決這個問題。第一種是層疊樣式表(CascadingStyleSheet),更為人知的是其縮寫,CSS。它提供了對HTML頁面中元素的樣式和布局進(jìn)行控制的能力,通常在一個單獨文件中集中定義整個站點的樣式。顏色、字體、尺寸、甚至是確切位置和布局都可以通過CSS元素進(jìn)行控制。CSS還支持樣式級別。比如,它既可以為頁面上的所有段落元素(P)定義樣式,也可以對“通過帶名稱的樣式屬性來定義的”段落元素提供不同的樣式。作為萬維網(wǎng)聯(lián)盟的標(biāo)準(zhǔn),CSS也獲益匪淺,絕大多數(shù)HTML瀏覽器都對其提供廣泛支持。然而,不同的瀏覽器實現(xiàn)(甚至不同的版本),對CSS的支持也參差不齊;一個主要依賴CSS來定義樣式的站點,可能在某個瀏覽器版本下工作正常,但是在別的或者較老的瀏覽器中,就可能完全不同。內(nèi)容與樣式相分離至少有兩種技術(shù)可以解決這個問題。內(nèi)容與樣式相分離第二種方式是使用XSLT的方式。首先,由于不同瀏覽器所支持的HTML花樣繁多,要想編寫出在所有的瀏覽器上都能夠正常顯示的HTML,其實非常困難。所以還不如在所有JSP頁面之前放置一個過濾器,用它來檢查HTTP請求報頭中的User-Agent,以判斷發(fā)出請求的瀏覽器類型,然后使用相應(yīng)的XSLT頁面,把JSP輸出轉(zhuǎn)換成特定于用戶瀏覽器版本的HTML文件。對于避免直接在JSP頁面中對這些差異進(jìn)行編碼,這么做會很有幫助。而且,被呈現(xiàn)的HTML的樣式可以在XSLT文件中被直接捕獲,這樣就把所有有關(guān)樣式的決定都放在了一個邏輯位置上。無論你選擇何種方式,目的都一樣:把所有有關(guān)樣式的決定局限于單一位置,也就是遵守“一次且僅有一次”規(guī)則。這樣,當(dāng)市場部的副總裁來到你面前,告訴你要在整個企業(yè)范圍內(nèi)改變站點的外觀時,這種對Web應(yīng)用進(jìn)行更新以反映新變化的過程,只需以天或小時計算,而不是以周或月來計算。實際上,如果站點本身使用了CSS,你直接重用新的CSS文件就可以了。內(nèi)容與樣式相分離第二種方式是使用XSLT的方式。首先,由于不表示第1項:考慮富客戶端UI技術(shù)第2項:使HTML短小精悍第3
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025版高考?xì)v史一輪復(fù)習(xí)課后限時集訓(xùn)4近代西方資本主義政體的建立含解析岳麓版
- 生態(tài)修復(fù)模板施工勞務(wù)合同
- 大學(xué)校園電力系統(tǒng)施工合同
- 環(huán)保設(shè)備庫房施工合同
- 農(nóng)田灌溉水泵施工合同
- 模具銷售渠道合同范本
- 耐高低溫涂料施工協(xié)議
- 物業(yè)服務(wù)委托協(xié)議
- 市政工程項目并購協(xié)議
- 林業(yè)機(jī)械維修施工合同
- KPI考核表-品質(zhì)部
- Access數(shù)據(jù)庫課程標(biāo)準(zhǔn)
- 幼兒園中班語言:《兩只蚊子吹牛皮》 課件
- 臨時用電漏電保護(hù)器運行檢測記錄表
- 頭痛的國際分類(第三版)中文
- 音樂ppt課件《小小的船》
- 幼兒園教學(xué)課件語言教育《雪地里的小畫家》
- 結(jié)構(gòu)化面試經(jīng)典100題及答案
- ESG引領(lǐng)下的西部城市再出發(fā)-新型城市競爭力策略研究白皮書
- 小學(xué)生班干部競選自我介紹PPT模板公開課一等獎市賽課獲獎?wù)n件
- 萬科物業(yè)崗位說明書2
評論
0/150
提交評論