Java开发高性能网站需要关注的事
?
轉(zhuǎn)自:http://www.javabloger.com/java-development-concern-those-things/
?
近期各家IT媒體舉辦的業(yè)內(nèi)技術(shù)大會(huì)讓很多網(wǎng)站都在披露自己的技術(shù)內(nèi)幕與同行們分享,大到facebook,百度,小到剛起步的網(wǎng)站。facebook,百度之類的大型網(wǎng)站采用的技術(shù)和超凡的處理能力的確給人耳目一新的感覺,但并不是每個(gè)網(wǎng)站都是像facebook,百度 有上億的用戶訪問流量,有海量的數(shù)據(jù)需要存儲,需要使用到mapreduce/并行計(jì)算,HBase/列存儲這些技術(shù)不可。技術(shù)手段始終是運(yùn)營的支撐,對于當(dāng)前的運(yùn)營環(huán)境適用就好,沒有必要非要趕個(gè)時(shí)髦,一定要和某項(xiàng)流行的技術(shù)產(chǎn)生點(diǎn)關(guān)系才善罷甘休。
在最近的技術(shù)大會(huì)中我們更多的目光都聚焦在這些大型網(wǎng)站,其實(shí)中小型門戶網(wǎng)站的技術(shù)體系也是值得去探討和關(guān)注。全天下的攻城師們并不是都在為這些大型門戶網(wǎng)站服務(wù),更多的攻城師們正在默默無聞的為一些剛剛起步的中小型網(wǎng)站服務(wù),而且占據(jù)了攻城師隊(duì)伍中的60%以上的人群。在關(guān)注大型門戶網(wǎng)站的時(shí)候,中小型門戶網(wǎng)站的技術(shù)發(fā)展和實(shí)戰(zhàn)經(jīng)驗(yàn)更值得去分享。
無論大型門戶網(wǎng)站還是中小型垂直類型網(wǎng)站都會(huì)對穩(wěn)定性、性能和可伸縮性有所追求。大型網(wǎng)站的技術(shù)經(jīng)驗(yàn)分享值得我們?nèi)W(xué)習(xí)和借用,但落實(shí)到更具體的實(shí)踐上并不是對所有網(wǎng)站可以適用,其他語言開發(fā)的網(wǎng)站我還不敢多說,但Java開發(fā)的系統(tǒng),我還是能您給插上幾句話:
JVM
JEE容器中運(yùn)行的JVM參數(shù)配置參數(shù)的正確使用直接關(guān)系到整個(gè)系統(tǒng)的性能和處理能力,JVM的調(diào)優(yōu)主要是對內(nèi)存管理方面的調(diào)優(yōu),優(yōu)化的方向分為以下4點(diǎn):
1.HeapSize???????????? 堆的大小,也可以說Java虛擬機(jī)使用內(nèi)存的策略,這點(diǎn)是非常關(guān)鍵的。
2.GarbageCollector? 通過配置相關(guān)的參數(shù)進(jìn)行Java中的垃圾收集器的4個(gè)算法(策略)進(jìn)行使用。
3.StackSize???????????? 棧是JVM的內(nèi)存指令區(qū),每個(gè)線程都有他自己的Stack,Stack的大小限制著線程的數(shù)量。
4.DeBug/Log?????????? 在JVM中還可以設(shè)置對JVM運(yùn)行時(shí)的日志和JVM掛掉后的日志輸出,這點(diǎn)非常的關(guān)鍵,根據(jù)各類JVM的日志輸出才能配置合適的參數(shù)。
網(wǎng)上隨處可見JVM的配置技巧,但是我還是推薦閱讀Sun官方的2篇文章,可以對配置參數(shù)的其所依然有一個(gè)了解
1.Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
2.Troubleshooting Guide for Java SE 6 with HotSpot VMhttp://www.oracle.com/technetwork/java/javase/index-137495.html
另外,我相信不是每個(gè)人攻城師都是天天對著這些JVM參數(shù)的,如果你忘記了那些關(guān)鍵的參數(shù)你可以輸入Java -X(大寫X)進(jìn)行提示。
JDBC
針對MySQL的JDBC的參數(shù)在之前的文章中也有介紹過,在單臺機(jī)器或者集群的環(huán)境下合理的使用JDBC中的配置參數(shù)對操作數(shù)據(jù)庫也有很大的影響。
一些所謂高性能的 Java ORM開源框架也就是打開了很多JDBC中的默認(rèn)參數(shù):
1.例如:autoReconnect、prepStmtCacheSize、cachePrepStmts、useNewIO、blobSendChunkSize 等,
2.例如集群環(huán)境下:roundRobinLoadBalance、failOverReadOnly、autoReconnectForPools、secondsBeforeRetryMaster。
具體內(nèi)容可以參閱MySQL的JDBC官方使用手冊:
http://dev.mysql.com/doc/refman/5.1/zh/connectors.html#cj-jdbc-reference
數(shù)據(jù)庫連接池(DataSource)
應(yīng)用程序與數(shù)據(jù)庫連接頻繁的交互會(huì)給系統(tǒng)帶來瓶頸和大量的開銷會(huì)影響到系統(tǒng)的性能,JDBC連接池負(fù)責(zé)分配、管理和釋放數(shù)據(jù)庫連接,它允許應(yīng)用程序重復(fù)使用一個(gè)現(xiàn)有的數(shù)據(jù)庫連接,而再不是重新建立一個(gè)連接,因此應(yīng)用程序不需要頻繁的與數(shù)據(jù)庫開關(guān)連接,并且可以釋放空閑時(shí)間超過最大空閑時(shí)間的數(shù)據(jù)庫連接來避免因?yàn)闆]有釋放數(shù)據(jù)庫連接而引起的數(shù)據(jù)庫連接遺漏。這項(xiàng)技術(shù)能明顯提高對數(shù)據(jù)庫操作的性能。
在此我認(rèn)為有一點(diǎn)需要說明:
連接池的使用也是需要關(guān)閉,因?yàn)樵跀?shù)據(jù)庫連接池啟動(dòng)的時(shí)候就預(yù)先和數(shù)據(jù)庫獲得了相應(yīng)的連接,之后不再需要應(yīng)用程序直接的和數(shù)據(jù)庫打交道,因?yàn)閼?yīng)用程序使用數(shù)據(jù)庫連接池是一個(gè)“借”的概念,應(yīng)用程序從數(shù)據(jù)庫連接池中獲得資源是“借出”,還需要還回去,就好比有20個(gè)水桶放在這里,需要拿水的人都可以使用這些木桶從水池里面拿水,如果20個(gè)人都拿完水,不將水桶還回原地,那么后面來的人再需要拿水,只能在旁邊等待有人將木桶還回去,之前的人用完后需要放回去,不然后面的人就會(huì)一直等待,造成資源堵塞,同理,應(yīng)用程序獲取數(shù)據(jù)庫連接的時(shí)候Connection連接對象的時(shí)候是從“池”中分配一個(gè)數(shù)據(jù)庫連接出去,在使用完畢后,歸還這個(gè)數(shù)據(jù)庫連接,這樣才能保持?jǐn)?shù)據(jù)庫的連接“有借有還”準(zhǔn)則。
參考資料:
http://dev.mysql.com/doc/refman/5.1/zh/connectors.html#cj-connection-pooling
數(shù)據(jù)存取
數(shù)據(jù)庫服務(wù)器的優(yōu)化和數(shù)據(jù)的存取,什么類型的數(shù)據(jù)放在什么地方更好是值得去思考的問題,將來的存儲很可能是混用的,Cache,NOSQL,DFS,DataBase 在一個(gè)系統(tǒng)中都會(huì)有,生活的餐具和平日里穿衣服需要擺放在家里,但是不會(huì)用同一種類型的家具存放,貌似沒有那個(gè)人家把餐具和衣服放在同一個(gè)柜子里面的。這就像是系統(tǒng)中不同類型的數(shù)據(jù)一樣,對不同類型的數(shù)據(jù)需要使用合適的存儲環(huán)境。文件和圖片的存儲,首先按照訪問的熱度分類,或者按照文件的大小。強(qiáng)關(guān)系類型并且需要事務(wù)支持的采用傳統(tǒng)的數(shù)據(jù)庫,弱關(guān)系型不需要事務(wù)支持的可以考慮NOSQL,海量文件存儲可以考慮一下支持網(wǎng)絡(luò)存儲的DFS,至于緩存要看你單個(gè)數(shù)據(jù)存儲的大小和讀寫的比例。
還有一點(diǎn)值得注意就是數(shù)據(jù)讀寫分離,無論在DataBase還是NOSQL的環(huán)境中大部分都是讀大于寫,因此在設(shè)計(jì)時(shí)還需考慮 不僅僅需要讓數(shù)據(jù)的讀分散在多臺機(jī)器上,還需要考慮多臺機(jī)器之間的數(shù)據(jù)一致性,MySQL的一主多從,在加上MySQL-Proxy或者借用JDBC中的一些參數(shù)(roundRobinLoadBalance、failOverReadOnly、autoReconnectForPools、secondsBeforeRetryMaster)對后續(xù)應(yīng)用程序開發(fā),可以將讀和寫分離,將大量讀的壓力分散在多臺機(jī)器上,并且還保證了數(shù)據(jù)的一致性。
緩存
在宏觀上看緩存一般分為2種:本地緩存和分布式緩存
1.本地緩存,對于Java的本地緩存而言就是講數(shù)據(jù)放入靜態(tài)(static)的數(shù)據(jù)結(jié)合中,然后需要用的時(shí)候就從靜態(tài)數(shù)據(jù)結(jié)合中拿出來,對于高并發(fā)的環(huán)境建議使用 ConcurrentHashMap或者CopyOnWriteArrayList作為本地緩存。緩存的使用更具體點(diǎn)說就是對系統(tǒng)內(nèi)存的使用,使用多少內(nèi)存的資源需要有一個(gè)適當(dāng)比例,如果超過適當(dāng)?shù)氖褂么鎯υL問,將會(huì)適得其反,導(dǎo)致整個(gè)系統(tǒng)的運(yùn)行效率低下。
2. 分布式緩存,一般用于分布式的環(huán)境,將每臺機(jī)器上的緩存進(jìn)行集中化的存儲,并且不僅僅用于緩存的使用范疇,還可以作為分布式系統(tǒng)數(shù)據(jù)同步/傳輸?shù)囊环N手段,一般被使用最多的就是Memcached和Redis。
數(shù)據(jù)存儲在不同的介質(zhì)上讀/寫得到的效率是不同的,在系統(tǒng)中如何善用緩存,讓你的數(shù)據(jù)更靠近c(diǎn)pu,下面有一張圖你需要永遠(yuǎn)牢記在心里,來自Google的技術(shù)大牛Jeff Dean(Ref)的杰作,如圖所示:
并發(fā)/多線程
在高并發(fā)環(huán)境下建議開發(fā)者使用JDK中自帶的并發(fā)包(java.util.concurrent),在JDK1.5以后使用java.util.concurrent下的工具類可以簡化多線程開發(fā),在java.util.concurrent的工具中主要分為以下幾個(gè)主要部分:
1.線程池,線程池的接口(Executor、ExecutorService)與實(shí)現(xiàn)類(ThreadPoolExecutor、 ScheduledThreadPoolExecutor),利用jdk自帶的線程池框架可以管理任務(wù)的排隊(duì)和安排,并允許受控制的關(guān)閉。因?yàn)檫\(yùn)行一個(gè)線程需要消耗系統(tǒng)CPU資源,而創(chuàng)建、結(jié)束一個(gè)線程也對系統(tǒng)CPU資源有開銷,使用線程池不僅僅可以有效的管理多線程的使用,還是可以提高線程的運(yùn)行效率。
2.本地隊(duì)列,提供了高效的、可伸縮的、線程安全的非阻塞 FIFO 隊(duì)列。java.util.concurrent 中的五個(gè)實(shí)現(xiàn)都支持?jǐn)U展的 BlockingQueue 接口,該接口定義了 put 和 take 的阻塞版本:LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue、PriorityBlockingQueue 和 DelayQueue。這些不同的類覆蓋了生產(chǎn)者-使用者、消息傳遞、并行任務(wù)執(zhí)行和相關(guān)并發(fā)設(shè)計(jì)的大多數(shù)常見使用的上下文。
3.同步器,四個(gè)類可協(xié)助實(shí)現(xiàn)常見的專用同步語句。Semaphore 是一個(gè)經(jīng)典的并發(fā)工具。CountDownLatch 是一個(gè)極其簡單但又極其常用的實(shí)用工具,用于在保持給定數(shù)目的信號、事件或條件前阻塞執(zhí)行。CyclicBarrier 是一個(gè)可重置的多路同步點(diǎn),在某些并行編程風(fēng)格中很有用。Exchanger 允許兩個(gè)線程在 collection 點(diǎn)交換對象,它在多流水線設(shè)計(jì)中是有用的。
4.并發(fā)包 Collection,此包還提供了設(shè)計(jì)用于多線程上下文中的 Collection 實(shí)現(xiàn):ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和 CopyOnWriteArraySet。當(dāng)期望許多線程訪問一個(gè)給定 collection 時(shí),ConcurrentHashMap 通常優(yōu)于同步的 HashMap,ConcurrentSkipListMap 通常優(yōu)于同步的 TreeMap。當(dāng)期望的讀數(shù)和遍歷遠(yuǎn)遠(yuǎn)大于列表的更新數(shù)時(shí),CopyOnWriteArrayList 優(yōu)于同步的 ArrayList。
隊(duì)列
關(guān)于隊(duì)列可以分為:本地的隊(duì)列 和 分布式隊(duì)列 2類
本地隊(duì)列:一般常見的用于非及時(shí)性的數(shù)據(jù)批量寫入,可以將獲取的數(shù)據(jù)緩存在一個(gè)數(shù)組中等達(dá)到一定數(shù)量的時(shí)候在進(jìn)行批量的一次寫入,可以使用BlockingQueue或者List/Map來實(shí)現(xiàn)。
相關(guān)資料:Sun Java API.
分布式隊(duì)列:一般作為消息中間件,構(gòu)建分布式環(huán)境下子系統(tǒng)與子系統(tǒng)之間通信的橋梁,JEE環(huán)境中使用最多的就是Apache的AvtiveMQ和Sun公司的OpenMQ。
輕量級的MQ中間件之前也向大家介紹過一些例如:Kestrel和Redis(Ref http://www.javabloger.com/article/mq-kestrel-redis-for-java.html),最近又聽說LinkedIn的搜索技術(shù)團(tuán)隊(duì)推出了一個(gè)MQ產(chǎn)品-kaukaf(Ref http://sna-projects.com/kafka ),對此保持關(guān)注。
相關(guān)資料:
1.ActiveMQ?http://activemq.apache.org/getting-started.html
2.OpenMQ??http://mq.java.net/about.html
3.Kafka???????http://sna-projects.com/kafka??????
4.JMS文章??http://www.javabloger.com/article/category/jms
NIO
NIO是在JDK1.4后的版本中出現(xiàn)的,在Java 1.4之前,Jdk提供的都是面向流的I/O系統(tǒng),例如讀/寫文件則是一次一個(gè)字節(jié)地處理數(shù)據(jù),一個(gè)輸入流產(chǎn)生一個(gè)字節(jié)的數(shù)據(jù),一個(gè)輸出流消費(fèi)一個(gè)字節(jié)的數(shù)據(jù), 面向流的I/O速度非常慢,并且一個(gè)數(shù)據(jù)包要么整個(gè)數(shù)據(jù)報(bào)已經(jīng)收到,要么還沒有。Java NIO非堵塞技術(shù)實(shí)際是采取Reactor模式,有內(nèi)容進(jìn)來會(huì)自動(dòng)通知,不必死等、死循環(huán),大大的提升了系統(tǒng)性能。在現(xiàn)實(shí)場景中NIO技術(shù)多數(shù)運(yùn)用兩個(gè)方面,1是文件的讀寫操作,2是網(wǎng)絡(luò)上數(shù)據(jù)流的操作。在NIO中有幾個(gè)核心對象需要掌握:1選擇器(Selector)、2通道(Channel)、3緩沖區(qū)(Buffer)。
我的廢話:
1.在Java NIO的技術(shù)范疇中內(nèi)存映射文件是一種高效的做法,可以用于緩存中存儲的冷/熱數(shù)據(jù)分離,將緩存中的一部分冷數(shù)據(jù)進(jìn)行這樣的處理,這種做法上比常規(guī)的基于流或者基于通道的I/O快的多,通過使文件中的數(shù)據(jù)出現(xiàn)為內(nèi)存數(shù)組的內(nèi)容來完成的,將文件中實(shí)際讀取或者寫入的部分才會(huì)映射到內(nèi)存中,并不是將整個(gè)文件讀到內(nèi)存中。
2.在Mysql的jdbc驅(qū)動(dòng)中也可以使用NIO技術(shù)對數(shù)據(jù)庫進(jìn)行操作來提升系統(tǒng)的性能。
長連接/Servlet3.0
這里說的長連接就是長輪詢,以前瀏覽器(客戶端)需要關(guān)注服務(wù)器端發(fā)生的數(shù)據(jù)變化需要不斷的訪問服務(wù)器,這樣客戶端的數(shù)量一多必然會(huì)給服務(wù)器端造成很大的壓力,例如:論壇中的站內(nèi)消息。現(xiàn)在Servlet3.0規(guī)范中提供了一個(gè)新的特性:異步IO通信;該特性會(huì)保持一個(gè)長連接。利用Servlet3異步請求的這項(xiàng)技術(shù)可以大大的緩解服務(wù)器端的壓力。
Servlet3.0的原理就是將request的請求開啟一個(gè)線程掛起,中間設(shè)置等待超時(shí)的時(shí)間,如果后臺事件觸發(fā)request請求,得到的結(jié)果返回給客戶端的request請求,如果在設(shè)置等待超時(shí)的時(shí)間內(nèi)沒有任何事件發(fā)生也將請求返回給客戶端,客戶端將再次發(fā)起request請求,客戶端與服務(wù)器端的交互可以與此往復(fù)。
就好比,你先過來跟我說如果有人找你,我就立馬通知你你來見他,原先你需要不斷的問我有沒有要找你,而不管有沒有人找你,你都需要不斷的問我有沒有人找你,這樣的話不論問的人還是被問的人都會(huì)累死。
日志
Log4J是通常被人們使用的工具,系統(tǒng)在剛剛上線的時(shí)候日志一般都設(shè)置在INFO的級,真正上線后一般設(shè)置在ERROR級,但無論在任何時(shí)候,日志的輸入內(nèi)容都是需要關(guān)注的,開發(fā)人員一般可以依靠輸出的日志查找出現(xiàn)的問題或者依靠輸出的日志對系統(tǒng)的性能進(jìn)行優(yōu)化,日志也是系統(tǒng)運(yùn)行狀態(tài)的報(bào)告和排錯(cuò)的依據(jù)。
簡單來說日志按照定義的不同策略和等級輸出到不同的環(huán)境,那樣便于我們分析和管理。相反你沒有策略的輸出,那么機(jī)器一多,時(shí)間一長,會(huì)有一大推亂糟糟的日志,會(huì)讓你排錯(cuò)的時(shí)候無從下手,所以日志的輸出策略是使用日志的關(guān)鍵點(diǎn)。
參考資料:http://logging.apache.org/log4j/1.2/manual.html
打包/部署
在代碼設(shè)計(jì)的時(shí)候最好能將不同類型的功能模塊在IDE環(huán)境中粗粒度的分為不同的工程,便于打成不同jar包部署在不同的環(huán)境中。有這樣的一個(gè)應(yīng)用場景:需要每天定時(shí)遠(yuǎn)程從SP那邊獲得當(dāng)天100條新聞和部分城市的天氣預(yù)報(bào),雖然每天的數(shù)據(jù)量不多,但是前端訪問的并發(fā)量很大,顯然需要在系統(tǒng)架構(gòu)上做到讀寫分離。
如果把web工程和定時(shí)抓取的功能模塊完全集中在一個(gè)工程里打包,將導(dǎo)致需要擴(kuò)展的時(shí)候每臺機(jī)器上既有web應(yīng)用也有定時(shí)器,因?yàn)楣δ苣K沒有分開,每臺機(jī)器上都有定時(shí)器工作將會(huì)造成數(shù)據(jù)庫里面的數(shù)據(jù)重復(fù)。
如果開發(fā)的時(shí)候就將web和定時(shí)器分為2個(gè)工程,打包的時(shí)候就可以分開部署,10臺web對應(yīng)一臺定期器,分解了前端請求的壓力,數(shù)據(jù)的寫入也不會(huì)重復(fù)。
這樣做的另一個(gè)好處就是可以共用,在上述的場景中web和定時(shí)器都需要對數(shù)據(jù)庫進(jìn)行讀取,那么web和定時(shí)器的工程里都有操作數(shù)據(jù)庫的代碼,在代碼的邏輯上還是感覺亂亂的。如果再抽出一個(gè)DAL層的jar,web和定時(shí)器的應(yīng)用模塊開發(fā)者只需要引用DAL層的jar,開發(fā)相關(guān)的業(yè)務(wù)邏輯,面向接口編程,無需考慮具體的數(shù)據(jù)庫操作,具體的對數(shù)據(jù)庫操作由其他開發(fā)者完成,可以在開發(fā)任務(wù)分工上很明確,并且互不干涉。
框架
所謂流行的SSH(Struts/Spring/Hiberanet)輕量級框架,對于很多中小型項(xiàng)目而言一點(diǎn)都不輕量級,開發(fā)者不僅需要維護(hù)代碼,還需要維護(hù)繁瑣的xml配置文件,而且說不定某個(gè)配置文件寫的不對就讓整個(gè)都工程無法運(yùn)行。無配置文件可以取代SSH(struts/Spring/Hiberanet)框架的產(chǎn)品真的太多了,我之前就向大家介紹過一些個(gè)產(chǎn)品(Ref)。
這個(gè)我并不是一味的反對使用SSH(Struts/Spring/Hiberanet)框架,在我眼里SSH框架真的作用是做到了規(guī)范開發(fā),而并不使用了SSH(Struts/Spring/Hiberanet)框架能提高多少性能。
SSH框架只是對于非常大的項(xiàng)目人數(shù)上百人的團(tuán)隊(duì),還需要、繼續(xù)增加團(tuán)隊(duì)規(guī)模的公司而言,是需要選擇一些市面上大家都認(rèn)可,并且熟悉的技術(shù),SSH(Struts/Spring/Hiberanet)框架比較成熟所以是首先產(chǎn)品。
但是對于一些小團(tuán)隊(duì)中間有個(gè)把技術(shù)高人的團(tuán)隊(duì)而言完全可以選擇更加簡潔的框架,真正的做到提速你的開發(fā)效率,早日拋棄SSH框架選擇更簡潔的技術(shù)在小團(tuán)隊(duì)開發(fā)中是一種比較明知的選擇。
轉(zhuǎn)載于:https://www.cnblogs.com/HigginCui/p/6610384.html
總結(jié)
以上是生活随笔為你收集整理的Java开发高性能网站需要关注的事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Java技术》第二次作业计科1501赵
- 下一篇: Java 异常处理 try catch