2008年1月2日
http://www.tkk7.com/zhuanggl/archive/2010/07/14/326119.html
http://blog.csdn.net/cleanfield/article/details/6339428
http://tech.idv2.com/2008/07/10/memcached-001/
axis可以根據(jù)wsdl文件生成對(duì)應(yīng)的客戶端、服務(wù)端java代碼,非常方便,具體使用方式為: java org.apache.axis.wsdl.WSDL2Java-t -s D:\test\webservice\TestCaseAndWsdl\TestService\TaskService.wsdl 上述classpath沒(méi)有設(shè)置,需要自行設(shè)定引用jar文件。
當(dāng)關(guān)系數(shù)據(jù)庫(kù)試圖在一個(gè)單一表中存儲(chǔ)數(shù) TB 的數(shù)據(jù)時(shí),總性能經(jīng)常會(huì)降低。顯然,對(duì)所有數(shù)據(jù)編索引不僅對(duì)于讀而且對(duì)于寫都很耗時(shí)。因?yàn)?NoSQL 數(shù)據(jù)商店尤其適合存儲(chǔ)大型數(shù)據(jù)(如 Google 的 Bigtable),顯然 NoSQL 是一種非關(guān)系數(shù)據(jù)庫(kù)方法。對(duì)于傾向于使用 ACID-ity 和實(shí)體結(jié)構(gòu)關(guān)系數(shù)據(jù)庫(kù)的開(kāi)發(fā)人員及需要這種結(jié)構(gòu)的項(xiàng)目來(lái)說(shuō),切分是一個(gè)令人振奮的可選方法。 切分 是數(shù)據(jù)庫(kù)分區(qū)的一個(gè)分支,但是它不是本地?cái)?shù)據(jù)庫(kù)技術(shù) — 切分發(fā)生在應(yīng)用程序級(jí)別。在各種切分實(shí)現(xiàn)中,Hibernate Shards 是 Java™ 技術(shù)世界中最受歡迎的一個(gè)。這個(gè)靈活絕妙的項(xiàng)目可以讓您使用映射至邏輯數(shù)據(jù)庫(kù)的 POJO 對(duì)切分?jǐn)?shù)據(jù)集進(jìn)行幾乎無(wú)縫操作(我將在下文簡(jiǎn)要介紹 “幾乎” 的原因)。使用 Hibernate Shards 時(shí),您無(wú)須將您的 POJO 特別映射至切分 — 您可以像使用 Hibernate 方法對(duì)任何常見(jiàn)關(guān)系數(shù)據(jù)庫(kù)進(jìn)行映射時(shí)一樣對(duì)其進(jìn)行映射。Hibernate Shards 可以為您管理低級(jí)別的切分任務(wù)。 事實(shí)上,Hibernate Shards 的編碼工作比較簡(jiǎn)單。其中關(guān)鍵的部分在于判斷 如何進(jìn)行切分以及對(duì)什么進(jìn)行切分。 切分簡(jiǎn)介 數(shù)據(jù)庫(kù)切分 是一個(gè)固有的關(guān)系流程,可以通過(guò)一些邏輯數(shù)據(jù)塊將一個(gè)表的行分為不同的小組。例如,如果您正在根據(jù)時(shí)間戳對(duì)一個(gè)名為 foo 的超大型表進(jìn)行分區(qū),2010 年 8 月之前的所有數(shù)據(jù)都將進(jìn)入分區(qū) A,而之后的數(shù)據(jù)則全部進(jìn)入分區(qū) B。分區(qū)可以加快讀寫速度,因?yàn)樗鼈兊哪繕?biāo)是單獨(dú)分區(qū)中的較小型數(shù)據(jù)集。 分區(qū)功能并不總是可用的(MySQL 直到 5.1 版本后才支持),而且其需要的商業(yè)系統(tǒng)的成本也讓人望而卻步。更重要的是,大部分分區(qū)實(shí)現(xiàn)在同一個(gè)物理機(jī)上存儲(chǔ)數(shù)據(jù),所以受到硬件基礎(chǔ)的影響。除此之外, 分區(qū)也不能鑒別硬件的可靠性或者說(shuō)缺乏可靠性。因此,很多智慧的人們開(kāi)始尋找進(jìn)行伸縮的新方法。 切分 實(shí)質(zhì)上是數(shù)據(jù)庫(kù)級(jí)別的分區(qū):它不是通過(guò)數(shù)據(jù)塊分割數(shù)據(jù)表的行,而是通過(guò)一些邏輯數(shù)據(jù)元素對(duì)數(shù)據(jù)庫(kù)本身進(jìn)行分割(通常跨不同的計(jì)算機(jī))。也就是說(shuō),切分不是將數(shù)據(jù)表 分割成小塊,而是將整個(gè)數(shù)據(jù)庫(kù) 分割成小塊。 切分的一個(gè)典型示例是基于根據(jù)區(qū)域?qū)σ粋€(gè)存儲(chǔ)世界范圍客戶數(shù)據(jù)的大型數(shù)據(jù)庫(kù)進(jìn)行分割:切分 A 用于存儲(chǔ)美國(guó)的客戶信息,切分 B 用戶存儲(chǔ)亞洲的客戶信息,切分 C 歐洲,等。這些切分分別處于不同的計(jì)算機(jī)上,且每個(gè)切分將存儲(chǔ)所有相關(guān)數(shù)據(jù),如客戶喜好或訂購(gòu)歷史。 切分的好處(如分區(qū)一樣)在于它可以壓縮大型數(shù)據(jù):?jiǎn)为?dú)的數(shù)據(jù)表在每個(gè)切分中相對(duì)較小,這樣就可以支持更快速的讀寫速度,從而提高性能。切分 還可以改善可靠性,因?yàn)榧幢阋粋€(gè)切分意外失效,其他切分仍然可以服務(wù)數(shù)據(jù)。而且因?yàn)榍蟹质窃趹?yīng)用程序?qū)用孢M(jìn)行的,您可以對(duì)不支持常規(guī)分區(qū)的數(shù)據(jù)庫(kù)進(jìn)行切分 處理。資金成本較低同樣也是一個(gè)潛在優(yōu)勢(shì)。 切分和策略 像很多其他技術(shù)一樣,進(jìn)行切分時(shí)也需要作出部分妥協(xié)。因?yàn)榍蟹植皇且豁?xiàng)本地?cái)?shù)據(jù)庫(kù)技術(shù) — 也就是說(shuō),必須在應(yīng)用程序中實(shí)現(xiàn) —在開(kāi)始切分之前需要制定出您的切分策略。進(jìn)行切分時(shí)主鍵和跨切分查詢都扮演重要角色,主要通過(guò)定義您不可以做什么實(shí)現(xiàn)。 主鍵 切分利用多個(gè)數(shù)據(jù)庫(kù),其中所有數(shù)據(jù)庫(kù)都獨(dú)立起作用,不干涉其他切分。因此,如果您依賴于數(shù)據(jù)庫(kù)序列(如自動(dòng)主鍵生成),很有可能在一個(gè)數(shù)據(jù)庫(kù)集中將出現(xiàn)同 一個(gè)主鍵。可以跨分布式數(shù)據(jù)庫(kù)協(xié)調(diào)序列,但是這樣會(huì)增加系統(tǒng)的復(fù)雜程度。避免相同主鍵最安全的方法就是讓應(yīng)用程序(應(yīng)用程序?qū)⒐芾砬蟹窒到y(tǒng))生成主鍵。 跨切分查詢 大部分切分實(shí)現(xiàn)(包括 Hibernate Shards)不支持跨切分查詢,這就意味著,如果您想利用不同切分的兩個(gè)數(shù)據(jù)集,就必須處理額外的長(zhǎng)度。(有趣的是,Amazon 的 SimpleDB 也禁止跨域查詢)例如,如果將美國(guó)客戶信息存儲(chǔ)在切分 1 中,還需要將所有相關(guān)數(shù)據(jù)存儲(chǔ)在此。如果您嘗試將那些數(shù)據(jù)存儲(chǔ)在切分 2 中,情況就會(huì)變得復(fù)雜,系統(tǒng)性能也可能受影響。這種情況還與之前提到的一點(diǎn)有關(guān) — 如果您因?yàn)槟撤N原因需要進(jìn)行跨切分連接,最好采用一種可以消除重復(fù)的方式管理鍵! 很明顯,在建立數(shù)據(jù)庫(kù)前必須全面考慮切分策略。一旦選擇了一個(gè)特定的方向之后,您差不多就被它綁定了 — 進(jìn)行切分后很難隨便移動(dòng)數(shù)據(jù)了。 一個(gè)策略示例 因?yàn)榍蟹謱⒛壎ㄔ谝粋€(gè)線型數(shù)據(jù)模型中(也就是說(shuō),您無(wú)法輕松連接不同切分中的數(shù)據(jù)),您必須對(duì)如何在每個(gè)切分中對(duì)數(shù)據(jù)進(jìn)行邏輯組織有一個(gè)清 晰的概念。這可以通過(guò)聚焦域中的主要節(jié)點(diǎn)實(shí)現(xiàn)。如在一個(gè)電子商務(wù)系統(tǒng)中,主要節(jié)點(diǎn)可以是一個(gè)訂單或者一個(gè)客戶。因此,如果您選擇 “客戶” 作為切分策略的節(jié)點(diǎn),那么與客戶有關(guān)的所有數(shù)據(jù)將移動(dòng)至各自的切分中,但是您仍然必須選擇將這些數(shù)據(jù)移動(dòng)至哪個(gè)切分。 對(duì)于客戶來(lái)說(shuō),您可以根據(jù)所在地(歐洲、亞洲、非洲等)切分,或者您也可以根據(jù)其他元素進(jìn)行切分。這由您決定。但是,您的切分策略應(yīng)該包含將 數(shù)據(jù)均勻分布至所有切分的方法。切分的總體概念是將大型數(shù)據(jù)集分割為小型數(shù)據(jù)集;因此,如果一個(gè)特定的電子商務(wù)域包含一個(gè)大型的歐洲客戶集以及一個(gè)相對(duì)小 的美國(guó)客戶集,那么基于客戶所在地的切分可能沒(méi)有什么意義。 回到比賽 — 使用切分! 現(xiàn)在讓我們回到我經(jīng)常提到的賽跑應(yīng)用程序示例中,我可以根據(jù)比賽或參賽者進(jìn)行切分。在本示例中,我將根據(jù)比賽進(jìn)行切分,因?yàn)槲铱吹接蚴歉鶕?jù)參 加不同比賽的參賽者進(jìn)行組織的。因此,比賽是域的根。我也將根據(jù)比賽距離進(jìn)行切分,因?yàn)楸荣悜?yīng)用程序包含不同長(zhǎng)度和不同參賽者的多項(xiàng)比賽。 請(qǐng)注意:在進(jìn)行上述決定時(shí),我已經(jīng)接受了一個(gè)妥協(xié):如果一個(gè)參賽者參加了不止一項(xiàng)比賽,他們分屬不同的切分,那該怎么辦 呢?Hibernate Shards (像大多數(shù)切分實(shí)現(xiàn)一樣)不支持跨切分連接。我必須忍受這些輕微不便,允許參賽者被包含在多個(gè)切分中 — 也就是說(shuō),我將在參賽者參加的多個(gè)比賽切分中重建該參賽者。 為了簡(jiǎn)便起見(jiàn),我將創(chuàng)建兩個(gè)切分:一個(gè)用于 10 英里以下的比賽;另一個(gè)用于 10 英里以上的比賽。 實(shí)現(xiàn) Hibernate Shards Hibernate Shards 幾乎可以與現(xiàn)有 Hibernate 項(xiàng)目無(wú)縫結(jié)合使用。唯一問(wèn)題是 Hibernate Shards 需要一些特定信息和行為。比如,需要一個(gè)切分訪問(wèn)策略、一個(gè)切分選擇策略和一個(gè)切分處理策略。這些是您必須實(shí)現(xiàn)的接口,雖然部分情況下,您可以使用默認(rèn)策 略。我們將在后面的部分逐個(gè)了解各個(gè)接口。 ShardAccessStrategy 執(zhí)行查詢時(shí),Hibernate Shards 需要一個(gè)決定首個(gè)切分、第二個(gè)切分及后續(xù)切分的機(jī)制。Hibernate Shards 無(wú)需確定查詢什么(這是 Hibernate Core 和基礎(chǔ)數(shù)據(jù)庫(kù)需要做的),但是它確實(shí)意識(shí)到,在獲得答案之前可能需要對(duì)多個(gè)切分進(jìn)行查詢。因此,Hibernate Shards 提供了兩種極具創(chuàng)意的邏輯實(shí)現(xiàn)方法:一種方法是根據(jù)序列機(jī)制(一次一個(gè))對(duì)切分進(jìn)行查詢,直到獲得答案為止;另一種方法是并行訪問(wèn)策略,這種方法使用一個(gè) 線程模型一次對(duì)所有切分進(jìn)行查詢。 為了使問(wèn)題簡(jiǎn)單,我將使用序列策略,名稱為 SequentialShardAccessStrategy 。我們將稍后對(duì)其進(jìn)行配置。 ShardSelectionStrategy 當(dāng)創(chuàng)建一個(gè)新對(duì)象時(shí)(例如,當(dāng)通過(guò) Hibernate 創(chuàng)建一個(gè)新 Race 或 Runner 時(shí)),Hibernate Shards 需要知道需將對(duì)應(yīng)的數(shù)據(jù)寫入至哪些切分。因此,您必須實(shí)現(xiàn)該接口并對(duì)切分邏輯進(jìn)行編碼。如果您想進(jìn)行默認(rèn)實(shí)現(xiàn),有一個(gè)名為 RoundRobinShardSelectionStrategy 的策略,它使用一個(gè)循環(huán)策略將數(shù)據(jù)輸入切分中。 對(duì)于賽跑應(yīng)用程序,我需要提供根據(jù)比賽距離進(jìn)行切分的行為。因此,我們需要實(shí)現(xiàn) ShardSelectionStrategy 接口并提供依據(jù) Race 對(duì)象的 distance 采用 selectShardIdForNewObject 方法進(jìn)行切分的簡(jiǎn)易邏輯。(我將稍候在 Race 對(duì)象中展示。) 運(yùn)行時(shí),當(dāng)在我的域?qū)ο笊险{(diào)用某一類似 save 的方法時(shí),該接口的行為將被深層用于 Hibernate 的核心。 清單 1. 一個(gè)簡(jiǎn)單的切分選擇策略 import org.hibernate.shards.ShardId; import org.hibernate.shards.strategy.selection.ShardSelectionStrategy;
public class RacerShardSelectionStrategy implements ShardSelectionStrategy {
public ShardId selectShardIdForNewObject(Object obj) { if (obj instanceof Race) { Race rce = (Race) obj; return this.determineShardId(rce.getDistance()); } else if (obj instanceof Runner) { Runner runnr = (Runner) obj; if (runnr.getRaces().isEmpty()) { throw new IllegalArgumentException("runners must have at least one race"); } else { double dist = 0.0; for (Race rce : runnr.getRaces()) { dist = rce.getDistance(); break; } return this.determineShardId(dist); } } else { throw new IllegalArgumentException("a non-shardable object is being created"); } }
private ShardId determineShardId(double distance){ if (distance > 10.0) { return new ShardId(1); } else { return new ShardId(0); } } }
| 如您在 清單 1 中所看到的,如果持久化對(duì)象是一場(chǎng) Race ,那么其距離被確定,而且(因此)選擇了一個(gè)切分。在這種情況下,有兩個(gè)切分:0 和 1,其中切分 1 中包含 10 英里以上的比賽,切分 0 中包含所有其他比賽。 如果持久化一個(gè) Runner 或其他對(duì)象,情況會(huì)稍微復(fù)雜一些。我已經(jīng)編碼了一個(gè)邏輯規(guī)則,其中有三個(gè)原則: - 一名
Runner 在沒(méi)有對(duì)應(yīng)的 Race 時(shí)無(wú)法存在。 - 如果
Runner 被創(chuàng)建時(shí)參加了多場(chǎng) Race s,這名 Runner 將被持久化到尋找到的首場(chǎng) Race 所屬的切分中。(順便說(shuō)一句,該原則對(duì)未來(lái)有負(fù)面影響。) - 如果還保存了其他域?qū)ο螅F(xiàn)在將引發(fā)一個(gè)異常。
然后,您就可以擦掉額頭的熱汗了,因?yàn)榇蟛糠制D難的工作已經(jīng)搞定了。隨著比賽應(yīng)用程序的增長(zhǎng),我所使用的邏輯可能會(huì)顯得不夠靈活,但是它完全可以順利地完成這次演示! ShardResolutionStrategy 當(dāng)通過(guò)鍵搜索一個(gè)對(duì)象時(shí),Hibernate Shards 需要一種可以決定首個(gè)切分的方法。將需要使用 SharedResolutionStrategy 接口對(duì)其進(jìn)行指引。 如我之前提到的那樣,切分迫使您重視主鍵,因?yàn)槟鷮⑿枰H自管理這些主鍵。幸運(yùn)的是,Hibernate 在提供鍵或 UUID 生成方面表現(xiàn)良好。因此 Hibernate Shards 創(chuàng)造性地提供一個(gè) ID 生成器,名為 ShardedUUIDGenerator ,它可以靈活地將切分 ID 信息嵌入到 UUID 中。 如果您最后使用 ShardedUUIDGenerator 進(jìn)行鍵生成(我在本文中也將采取這種方法),那么您也可以使用 Hibernate Shards 提供的創(chuàng)新 ShardResolutionStrategy 實(shí)現(xiàn),名為 AllShardsShardResolutionStrategy ,這可以決定依據(jù)一個(gè)特定對(duì)象的 ID 搜索什么切分。 配置好 Hibernate Shards 工作所需的三個(gè)接口后,我們就可以對(duì)切分示例應(yīng)用程序的第二步進(jìn)行實(shí)現(xiàn)了。現(xiàn)在應(yīng)該啟動(dòng) Hibernate 的 SessionFactory 了。 配置 Hibernate Shards Hibernate 的其中一個(gè)核心接口對(duì)象是它的 SessionFactory 。Hibernate 的所有神奇都是在其配置 Hibernate 應(yīng)用程序過(guò)程中通過(guò)這個(gè)小對(duì)象實(shí)現(xiàn)的,例如,通過(guò)加載映射文件和配置。如果您使用了注釋或 Hibernate 珍貴的 .hbm 文件,那么您還需要一個(gè) SessionFactory 來(lái)讓 Hibernate 知道哪些對(duì)象是可以持久化的,以及將它們持久化到 哪里。 因此,使用 Hibernate Shards 時(shí),您必須使用一個(gè)增強(qiáng)的 SessionFactory 類型來(lái)配置多個(gè)數(shù)據(jù)庫(kù)。它可以被命名為 ShardedSessionFactory ,而且它當(dāng)然是 SessionFactory 類型的。當(dāng)創(chuàng)建一個(gè) ShardedSessionFactory 時(shí),您必須提供之前配置好的三個(gè)切分實(shí)現(xiàn)類型(ShardAccessStrategy 、ShardSelectionStrategy 和 ShardResolutionStrategy )。您還需提供 POJO 所需的所有映射文件。(如果您使用一個(gè)基于備注的 Hibernate POJO 配置,情況可能會(huì)有所不同。)最后,一個(gè) ShardedSessionFactory 示例需要每個(gè)切分都對(duì)應(yīng)多個(gè) Hibernate 配置文件。 創(chuàng)建一個(gè) Hibernate 配置 我已經(jīng)創(chuàng)建了一個(gè) ShardedSessionFactoryBuilder 類型,它有一個(gè)主要方法 createSessionFactory ,可以創(chuàng)建一個(gè)配置合理的 SessionFactory 。之后,我將將所有的一切都與 Spring 連接在一起(現(xiàn)在誰(shuí)不使用一個(gè) IOC 容器?)。現(xiàn)在,清單 2 顯示了 ShardedSessionFactoryBuilder 的主要作用:創(chuàng)建一個(gè) Hibernate 配置 : 清單 2. 創(chuàng)建一個(gè) Hibernate 配置 private Configuration getPrototypeConfig(String hibernateFile, List<String> resourceFiles) { Configuration config = new Configuration().configure(hibernateFile); for (String res : resourceFiles) { configs.addResource(res); } return config; }
| 如您在 清單 2 中所看到的,可以從 Hibernate 配置文件中創(chuàng)建了一個(gè)簡(jiǎn)單的 Configuration 。 該文件包含如下信息,如使用的是什么類型的數(shù)據(jù)庫(kù)、用戶名和密碼等,以及所有必須的資源文件,如 POJO 所用的 .hbm 文件。在進(jìn)行切分的情況下,您通常需要使用多個(gè)數(shù)據(jù)庫(kù)配置,但是 Hibernate Shards 支持您僅使用一個(gè) hibernate.cfg.xml 文件,從而簡(jiǎn)化了整個(gè)過(guò)程(但是,如您在 清單 4 中所看到的,您將需要對(duì)使用的每一個(gè)切分準(zhǔn)備一個(gè) hibernate.cfg.xml 文件)。 下一步,在清單 3 中,我將所有的切分配置都收集到了一個(gè) List 中: 清單 3. 切分配置列表 List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>(); for (String hibconfig : this.hibernateConfigurations) { shardConfigs.add(buildShardConfig(hibconfig)); }
| Spring 配置 在 清單 3 中,對(duì) hibernateConfigurations 的引用指向了 String s List ,其中每個(gè) String 都包含了 Hibernate 配置文件的名字。該 List 通過(guò) Spring 自動(dòng)連接。清單 4 是我的 Spring 配置文件中的一段摘錄: 清單 4. Spring 配置文件中的一部分 <bean id="shardedSessionFactoryBuilder" class="org.disco.racer.shardsupport.ShardedSessionFactoryBuilder"> <property name="resourceConfigurations"> <list> <value>racer.hbm.xml</value> </list> </property> <property name="hibernateConfigurations"> <list> <value>shard0.hibernate.cfg.xml</value> <value>shard1.hibernate.cfg.xml</value> </list> </property> </bean>
| 如您在 清單 4 中所看到的,ShardedSessionFactoryBuilder 正在與一個(gè) POJO 映射文件和兩個(gè)切分配置文件連接。清單 5 中是 POJO 文件的一段摘錄: 清單 5. 比賽 POJO 映射 <class name="org.disco.racer.domain.Race" table="race"dynamic-update="true" dynamic-insert="true">
<id name="id" column="RACE_ID" unsaved-value="-1"> <generator class="org.hibernate.shards.id.ShardedUUIDGenerator"/> </id>
<set name="participants" cascade="save-update" inverse="false" table="race_participants" lazy="false"> <key column="race_id"/> <many-to-many column="runner_id" class="org.disco.racer.domain.Runner"/> </set>
<set name="results" inverse="true" table="race_results" lazy="false"> <key column="race_id"/> <one-to-many class="org.disco.racer.domain.Result"/> </set>
<property name="name" column="NAME" type="string"/> <property name="distance" column="DISTANCE" type="double"/> <property name="date" column="DATE" type="date"/> <property name="description" column="DESCRIPTION" type="string"/> </class>
| 請(qǐng)注意,清單 5 中的 POJO 映射的唯一獨(dú)特方面是 ID 的生成器類 — 這就是 ShardedUUIDGenerator ,它(如您想象的一樣)將切分 ID 信息嵌入到 UUID 中。這就是我的 POJO 映射中切分的唯一獨(dú)特方面。 切分配置文件 下一步,如清單 6 中所示,我已經(jīng)配置了一個(gè)切分 — 在本示例中,除切分 ID 和連接信息外,切分 0 和切分 1 的文件是一樣的。 清單 6. Hibernate Shards 配置文件 <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory name="HibernateSessionFactory0"> <property name="dialect">org.hibernate.dialect.HSQLDialect</property> <property name="connection.driver_class">org.hsqldb.jdbcDriver</property> <property name="connection.url"> jdbc:hsqldb:file:/.../db01/db01 </property> <property name="connection.username">SA</property> <property name="connection.password"></property> <property name="hibernate.connection.shard_id">0</property> <property name="hibernate.shard.enable_cross_shard_relationship_checks">true </property> </session-factory> </hibernate-configuration>
| 如其名字所示,enable_cross_shard_relationship_checks 屬性對(duì)跨切分關(guān)系進(jìn)行了檢查。根據(jù) Hibernate Shards 文檔記錄,該屬性非常耗時(shí),在生成環(huán)境中應(yīng)該關(guān)閉。 最后,ShardedSessionFactoryBuilder 通過(guò)創(chuàng)建 ShardStrategyFactory ,然后添加三個(gè)類型(包括 清單 1 中的 RacerShardSelectionStrategy ),將一切都整合到了一起,如清單 7 中所示: 清單 7. 創(chuàng)建 ShardStrategyFactory private ShardStrategyFactory buildShardStrategyFactory() { ShardStrategyFactory shardStrategyFactory = new ShardStrategyFactory() { public ShardStrategy newShardStrategy(List<ShardId> shardIds) { ShardSelectionStrategy pss = new RacerShardSelectionStrategy(); ShardResolutionStrategy prs = new AllShardsShardResolutionStrategy(shardIds); ShardAccessStrategy pas = new SequentialShardAccessStrategy(); return new ShardStrategyImpl(pss, prs, pas); } }; return shardStrategyFactory; }
| 最后,我執(zhí)行了那個(gè)名為 createSessionFactory 的絕妙方法,在本示例中創(chuàng)建了一個(gè) ShardedSessionFactory ,如清單 8 所示: 清單 8. 創(chuàng)建 ShardedSessionFactory public SessionFactory createSessionFactory() { Configuration prototypeConfig = this.getPrototypeConfig (this.hibernateConfigurations.get(0), this.resourceConfigurations);
List<ShardConfiguration> shardConfigs = new ArrayList<ShardConfiguration>(); for (String hibconfig : this.hibernateConfigurations) { shardConfigs.add(buildShardConfig(hibconfig)); }
ShardStrategyFactory shardStrategyFactory = buildShardStrategyFactory(); ShardedConfiguration shardedConfig = new ShardedConfiguration( prototypeConfig, shardConfigs,shardStrategyFactory); return shardedConfig.buildShardedSessionFactory(); }
| 使用 Spring 連接域?qū)ο?/a> 現(xiàn)在可以深呼吸一下了,因?yàn)槲覀凂R上就成功了。到目前為止,我已經(jīng)創(chuàng)建一個(gè)可以合理配置 ShardedSessionFactory 的生成器類,其實(shí)就是實(shí)現(xiàn) Hibernate 無(wú)處不在的 SessionFactory 類型。ShardedSessionFactory 完成了切分中所有的神奇。它利用我在 清單 1 中所部署的切分選擇策略,并從我配置的兩個(gè)切分中讀寫數(shù)據(jù)。(清單 6 顯示了切分 0 和切分 1 的配置幾乎相同。) 現(xiàn)在我需要做的就是連接我的域?qū)ο螅诒臼纠校驗(yàn)樗鼈円蕾囉?Hibernate,需要一個(gè) SessionFactory 類型進(jìn)行工作。我將僅使用我的 ShardedSessionFactoryBuilder 提供一種 SessionFactory 類型,如清單 9 中所示: 清單 9. 在 Spring 中連接 POJO <bean id="mySessionFactory" factory-bean="shardedSessionFactoryBuilder" factory-method="createSessionFactory"> </bean>
<bean id="race_dao" class="org.disco.racer.domain.RaceDAOImpl"> <property name="sessionFactory"> <ref bean="mySessionFactory"/> </property> </bean>
| 如您在 清單 9 中所看到的,我首先在 Spring 中創(chuàng)建了一個(gè)類似工廠的 bean;也就是說(shuō),我的 RaceDAOImpl 類型有一個(gè)名為 sessionFactory 的屬性,是 SessionFactory 類型。之后,mySessionFactory 引用通過(guò)在 ShardedSessionFactoryBuilder 上調(diào)用 createSessionFactory 方法創(chuàng)建了一個(gè) SessionFactory 示例,如 清單 4 中所示。 當(dāng)我為我的 Race 對(duì)象示例使用 Spring(我主要將其作為一個(gè)巨型工廠使用,以返回預(yù)配置的對(duì)象)時(shí),一切事情就都搞定了。雖然沒(méi)有展示,RaceDAOImpl 類型是一個(gè)利用 Hibernate 模板進(jìn)行數(shù)據(jù)存儲(chǔ)和檢索的對(duì)象。我的 Race 類型包含一個(gè) RaceDAOImpl 示例,它將所有與數(shù)據(jù)商店相關(guān)的活動(dòng)都推遲至此。很默契,不是嗎? 請(qǐng)注意,我的 DAO 與 Hibernate Shards 在代碼方面并沒(méi)有綁定,而是通過(guò)配置進(jìn)行了綁定。配置(如 清單 5 中的)將它們綁定在一個(gè)特定切分 UUID 生成方案中,也就是說(shuō)了我可以在需要切分時(shí)從已有 Hibernate 實(shí)現(xiàn)中重新使用域?qū)ο蟆?/p> 切分:使用 easyb 的測(cè)試驅(qū)動(dòng) 接下來(lái),我需要驗(yàn)證我的切分實(shí)現(xiàn)可以工作。我有兩個(gè)數(shù)據(jù)庫(kù)并通過(guò)距離進(jìn)行切分,所以當(dāng)我創(chuàng)建一場(chǎng)馬拉松時(shí)(10 英里以上的比賽),該 Race 示例應(yīng)在切分 1 中找到。一個(gè)小型的比賽,如 5 公里的比賽(3.1 英里),將在切分 0 中找到。創(chuàng)建一場(chǎng) Race 后,我可以檢查單個(gè)數(shù)據(jù)庫(kù)的記錄。 在清單 10 中,我已經(jīng)創(chuàng)建了一場(chǎng)馬拉松,然后繼續(xù)驗(yàn)證記錄確實(shí)是在切分 1 中而非切分 0 中。使事情更加有趣(和簡(jiǎn)單)的是,我使用了 easyb,這是一個(gè)基于 Groovy 的行為驅(qū)動(dòng)開(kāi)發(fā)架構(gòu),利用自然語(yǔ)言驗(yàn)證。easyb 也可以輕松處理 Java 代碼。即便不了解 Groovy 或 easyb,您也可以通過(guò)查看清單 10 中的代碼,看到一切如期進(jìn)行。(請(qǐng)注意,我?guī)椭鷦?chuàng)建了 easyb,并且在 developerWorks 中對(duì)這個(gè)話題發(fā)表過(guò)文章。) 清單 10. 一個(gè)驗(yàn)證切分正確性的 easyb 故事中一段摘錄 scenario "races greater than 10.0 miles should be in shard 1 or db02", { given "a newly created race that is over 10.0 miles", { new Race("Leesburg Marathon", new Date(), 26.2, "Race the beautiful streets of Leesburg!").create() } then "everything should work fine w/respect to Hibernate", { rce = Race.findByName("Leesburg Marathon") rce.distance.shouldBe 26.2 } and "the race should be stored in shard 1 or db02", { sql = Sql.newInstance(db02url, name, psswrd, driver) sql.eachRow("select race_id, distance, name from race where name=?", ["Leesburg Marathon"]) { row -> row.distance.shouldBe 26.2 } sql.close() } and "the race should NOT be stored in shard 0 or db01", { sql = Sql.newInstance(db01url, name, psswrd, driver) sql.eachRow("select race_id, distance, name from race where name=?", ["Leesburg Marathon"]) { row -> fail "shard 0 contains a marathon!" } sql.close() } }
| 當(dāng)然,我的工作還沒(méi)有完 — 我還需要?jiǎng)?chuàng)建一個(gè)短程比賽,并驗(yàn)證其位于切分 0 中而非切分 1 中。您可以在本文提供的 代碼下載 中看到該驗(yàn)證操作! 切分的利弊 切分可以增加應(yīng)用程序的讀寫速度,尤其是如果您的應(yīng)用程序包含大量數(shù)據(jù) — 如數(shù) TB — 或者您的域處于無(wú)限制發(fā)展中,如 Google 或 Facebook。 在進(jìn)行切分之前,一定要確定應(yīng)用程序的規(guī)模和增長(zhǎng)對(duì)其有利。切分的成本(或者說(shuō)缺點(diǎn))包括對(duì)如何存儲(chǔ)和檢索數(shù)據(jù)的特定應(yīng)用程序邏輯進(jìn)行編碼的成本。進(jìn)行切分后,您多多少少都被鎖定在您的切分模型中,因?yàn)橹匦虑蟹植⒎且资隆?/p> 如果能夠正確實(shí)現(xiàn),切分可以用于解決傳統(tǒng) RDBMS 規(guī)模和速度問(wèn)題。切分對(duì)于綁定于關(guān)系基礎(chǔ)架構(gòu)、無(wú)法繼續(xù)升級(jí)硬件以滿足大量可伸縮數(shù)據(jù)存儲(chǔ)要求的組織來(lái)說(shuō)是一個(gè)非常成本高效的決策。
最后更新時(shí)間 2009-04-10 更新人 dormando@rydia.net 這里收集了經(jīng)常被問(wèn)到的關(guān)于memcached的問(wèn)題 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">集群架構(gòu)方面的問(wèn)題 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是怎么工作的? Memcached的神奇來(lái)自兩階段哈希(two-stage hash)。Memcached就像一個(gè)巨大的、存儲(chǔ)了很多<key,value>對(duì)的哈希表。通過(guò)key,可以存儲(chǔ)或查詢?nèi)我獾臄?shù)據(jù)。 客戶端可以把數(shù)據(jù)存儲(chǔ)在多臺(tái)memcached上。當(dāng)查詢數(shù)據(jù)時(shí),客戶端首先參考節(jié)點(diǎn)列表計(jì)算出key的哈希值(階段一哈希),進(jìn)而選中一個(gè)節(jié)點(diǎn); 客戶端將請(qǐng)求發(fā)送給選中的節(jié)點(diǎn),然后memcached節(jié)點(diǎn)通過(guò)一個(gè)內(nèi)部的哈希算法(階段二哈希),查找真正的數(shù)據(jù)(item)。 舉個(gè)列子,假設(shè)有3個(gè)客戶端1, 2, 3,3臺(tái)memcached A, B, C: Client 1想把數(shù)據(jù)"barbaz"以key "foo"存儲(chǔ)。Client 1首先參考節(jié)點(diǎn)列表(A, B, C),計(jì)算key "foo"的哈希值,假設(shè)memcached B被選中。接著,Client 1直接connect到memcached B,通過(guò)key "foo"把數(shù)據(jù)"barbaz"存儲(chǔ)進(jìn)去。Client 2使用與Client 1相同的客戶端庫(kù)(意味著階段一的哈希算法相同),也擁有同樣的memcached列表(A, B, C)。 于是,經(jīng)過(guò)相同的哈希計(jì)算(階段一),Client 2計(jì)算出key "foo"在memcached B上,然后它直接請(qǐng)求memcached B,得到數(shù)據(jù)"barbaz"。 各種客戶端在memcached中數(shù)據(jù)的存儲(chǔ)形式是不同的(perl Storable, php serialize, java hibernate, JSON等)。一些客戶端實(shí)現(xiàn)的哈希算法也不一樣。但是,memcached服務(wù)器端的行為總是一致的。 最后,從實(shí)現(xiàn)的角度看,memcached是一個(gè)非阻塞的、基于事件的服務(wù)器程序。這種架構(gòu)可以很好地解決C10K problem ,并具有極佳的可擴(kuò)展性。 可以參考A Story of Caching ,這篇文章簡(jiǎn)單解釋了客戶端與memcached是如何交互的。 memcached最大的優(yōu)勢(shì)是什么? 請(qǐng)仔細(xì)閱讀上面的問(wèn)題(即memcached是如何工作的)。Memcached最大的好處就是它帶來(lái)了極佳的水平可擴(kuò)展性,特別是在一個(gè)巨大的系 統(tǒng)中。由于客戶端自己做了一次哈希,那么我們很容易增加大量memcached到集群中。memcached之間沒(méi)有相互通信,因此不會(huì)增加 memcached的負(fù)載;沒(méi)有多播協(xié)議,不會(huì)網(wǎng)絡(luò)通信量爆炸(implode)。memcached的集群很好用。內(nèi)存不夠了?增加幾臺(tái) memcached吧;CPU不夠用了?再增加幾臺(tái)吧;有多余的內(nèi)存?在增加幾臺(tái)吧,不要浪費(fèi)了。 基于memcached的基本原則,可以相當(dāng)輕松地構(gòu)建出不同類型的緩存架構(gòu)。除了這篇FAQ,在其他地方很容易找到詳細(xì)資料的。 看看下面的幾個(gè)問(wèn)題吧,它們?cè)趍emcached、服務(wù)器的local cache和MySQL的query cache之間做了比較。這幾個(gè)問(wèn)題會(huì)讓您有更全面的認(rèn)識(shí)。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached和MySQL的query cache相比,有什么優(yōu)缺點(diǎn)? 把memcached引入應(yīng)用中,還是需要不少工作量的。MySQL有個(gè)使用方便的query cache,可以自動(dòng)地緩存SQL查詢的結(jié)果,被緩存的SQL查詢可以被反復(fù)地快速執(zhí)行。Memcached與之相比,怎么樣呢?MySQL的query cache是集中式的,連接到該query cache的MySQL服務(wù)器都會(huì)受益。 - 當(dāng)您修改表時(shí),MySQL的query cache會(huì)立刻被刷新(flush)。存儲(chǔ)一個(gè)memcached item只需要很少的時(shí)間,但是當(dāng)寫操作很頻繁時(shí),MySQL的query cache會(huì)經(jīng)常讓所有緩存數(shù)據(jù)都失效。
- 在多核CPU上,MySQL的query cache會(huì)遇到擴(kuò)展問(wèn)題(scalability issues)。在多核CPU上,query cache會(huì)增加一個(gè)全局鎖(global lock), 由于需要刷新更多的緩存數(shù)據(jù),速度會(huì)變得更慢。
- 在MySQL的query cache中,我們是不能存儲(chǔ)任意的數(shù)據(jù)的(只能是SQL查詢結(jié)果)。而利用memcached,我們可以搭建出各種高效的緩存。比如,可以執(zhí)行多個(gè)獨(dú)立 的查詢,構(gòu)建出一個(gè)用戶對(duì)象(user object),然后將用戶對(duì)象緩存到memcached中。而query cache是SQL語(yǔ)句級(jí)別的,不可能做到這一點(diǎn)。在小的網(wǎng)站中,query cache會(huì)有所幫助,但隨著網(wǎng)站規(guī)模的增加,query cache的弊將大于利。
- query cache能夠利用的內(nèi)存容量受到MySQL服務(wù)器空閑內(nèi)存空間的限制。給數(shù)據(jù)庫(kù)服務(wù)器增加更多的內(nèi)存來(lái)緩存數(shù)據(jù),固然是很好的。但是,有了memcached,只要您有空閑的內(nèi)存,都可以用來(lái)增加memcached集群的規(guī)模,然后您就可以緩存更多的數(shù)據(jù)。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached和服務(wù)器的local cache(比如PHP的APC、mmap文件等)相比,有什么優(yōu)缺點(diǎn)? 首先,local cache有許多與上面(query cache)相同的問(wèn)題。local cache能夠利用的內(nèi)存容量受到(單臺(tái))服務(wù)器空閑內(nèi)存空間的限制。不過(guò),local cache有一點(diǎn)比memcached和query cache都要好,那就是它不但可以存儲(chǔ)任意的數(shù)據(jù),而且沒(méi)有網(wǎng)絡(luò)存取的延遲。 - local cache的數(shù)據(jù)查詢更快。考慮把highly common的數(shù)據(jù)放在local cache中吧。如果每個(gè)頁(yè)面都需要加載一些數(shù)量較少的數(shù)據(jù),考慮把它們放在local cached吧。
- local cache缺少集體失效(group invalidation)的特性。在memcached集群中,刪除或更新一個(gè)key會(huì)讓所有的觀察者覺(jué)察到。但是在local cache中, 我們只能通知所有的服務(wù)器刷新cache(很慢,不具擴(kuò)展性),或者僅僅依賴緩存超時(shí)失效機(jī)制。
- local cache面臨著嚴(yán)重的內(nèi)存限制,這一點(diǎn)上面已經(jīng)提到。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的cache機(jī)制是怎樣的? Memcached主要的cache機(jī)制是LRU(最近最少用)算法+超時(shí)失效。當(dāng)您存數(shù)據(jù)到memcached中,可以指定該數(shù)據(jù)在緩存中可以呆 多久Which is forever, or some time in the future。如果memcached的內(nèi)存不夠用了,過(guò)期的slabs會(huì)優(yōu)先被替換,接著就輪到最老的未被使用的slabs。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached如何實(shí)現(xiàn)冗余機(jī)制? 不 實(shí)現(xiàn)!我們對(duì)這個(gè)問(wèn)題感到很驚訝。Memcached應(yīng)該是應(yīng)用的緩存層。它的設(shè)計(jì)本身就不帶有任何冗余機(jī)制。如果一個(gè)memcached節(jié)點(diǎn)失去了所有 數(shù)據(jù),您應(yīng)該可以從數(shù)據(jù)源(比如數(shù)據(jù)庫(kù))再次獲取到數(shù)據(jù)。您應(yīng)該特別注意,您的應(yīng)用應(yīng)該可以容忍節(jié)點(diǎn)的失效。不要寫一些糟糕的查詢代碼,寄希望于 memcached來(lái)保證一切!如果您擔(dān)心節(jié)點(diǎn)失效會(huì)大大加重?cái)?shù)據(jù)庫(kù)的負(fù)擔(dān),那么您可以采取一些辦法。比如您可以增加更多的節(jié)點(diǎn)(來(lái)減少丟失一個(gè)節(jié)點(diǎn)的影 響),熱備節(jié)點(diǎn)(在其他節(jié)點(diǎn)down了的時(shí)候接管IP),等等。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached如何處理容錯(cuò)的? 不處理!:) 在memcached節(jié)點(diǎn)失效的情況下,集群沒(méi)有必要做任何容錯(cuò)處理。如果發(fā)生了節(jié)點(diǎn)失效,應(yīng)對(duì)的措施完全取決于用戶。節(jié)點(diǎn)失效時(shí),下面列出幾種方案供您選擇: - 忽略它! 在失效節(jié)點(diǎn)被恢復(fù)或替換之前,還有很多其他節(jié)點(diǎn)可以應(yīng)對(duì)節(jié)點(diǎn)失效帶來(lái)的影響。
- 把失效的節(jié)點(diǎn)從節(jié)點(diǎn)列表中移除。做這個(gè)操作千萬(wàn)要小心!在默認(rèn)情況下(余數(shù)式哈希算法),客戶端添加或移除節(jié)點(diǎn),會(huì)導(dǎo)致所有的緩存數(shù)據(jù)不可用!因?yàn)楣⒄盏墓?jié)點(diǎn)列表變化了,大部分key會(huì)因?yàn)楣V档母淖兌挥成涞剑ㄅc原來(lái))不同的節(jié)點(diǎn)上。
- 啟動(dòng)熱備節(jié)點(diǎn),接管失效節(jié)點(diǎn)所占用的IP。這樣可以防止哈希紊亂(hashing chaos)。
- 如果希望添加和移除節(jié)點(diǎn),而不影響原先的哈希結(jié)果,可以使用一致性哈希算法(consistent hashing)。您可以百度一下一致性哈希算法。支持一致性哈希的客戶端已經(jīng)很成熟,而且被廣泛使用。去嘗試一下吧!
- 兩次哈希(reshing)。當(dāng)客戶端存取數(shù)據(jù)時(shí),如果發(fā)現(xiàn)一個(gè)節(jié)點(diǎn)down了,就再做一次哈希(哈希算法與前一次不同),重新選擇另一個(gè)節(jié)點(diǎn) (需要注意的時(shí),客戶端并沒(méi)有把down的節(jié)點(diǎn)從節(jié)點(diǎn)列表中移除,下次還是有可能先哈希到它)。如果某個(gè)節(jié)點(diǎn)時(shí)好時(shí)壞,兩次哈希的方法就有風(fēng)險(xiǎn)了,好的節(jié) 點(diǎn)和壞的節(jié)點(diǎn)上都可能存在臟數(shù)據(jù)(stale data)。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">如何將memcached中item批量導(dǎo)入導(dǎo)出? 您不應(yīng)該這樣做!Memcached是一個(gè)非阻塞的服務(wù)器。任何可能導(dǎo)致memcached暫停或瞬時(shí)拒絕服務(wù)的操作都應(yīng)該值得深思熟慮。向 memcached中批量導(dǎo)入數(shù)據(jù)往往不是您真正想要的!想象看,如果緩存數(shù)據(jù)在導(dǎo)出導(dǎo)入之間發(fā)生了變化,您就需要處理臟數(shù)據(jù)了;如果緩存數(shù)據(jù)在導(dǎo)出導(dǎo)入 之間過(guò)期了,您又怎么處理這些數(shù)據(jù)呢? 因此,批量導(dǎo)出導(dǎo)入數(shù)據(jù)并不像您想象中的那么有用。不過(guò)在一個(gè)場(chǎng)景倒是很有用。如果您有大量的從不變化 的數(shù)據(jù),并且希望緩存很快熱(warm)起來(lái),批量導(dǎo)入緩存數(shù)據(jù)是很有幫助的。雖然這個(gè)場(chǎng)景并不典型,但卻經(jīng)常發(fā)生,因此我們會(huì)考慮在將來(lái)實(shí)現(xiàn)批量導(dǎo)出導(dǎo)入的功能。 Steven Grimm,一如既往地,,在郵件列表中給出了另一個(gè)很好的例子:http://lists.danga.com/pipermail/memcached/2007-July/004802.html 。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">但是我確實(shí)需要把memcached中的item批量導(dǎo)出導(dǎo)入,怎么辦?? 好吧好吧。如果您需要批量導(dǎo)出導(dǎo)入,最可能的原因一般是重新生成緩存數(shù)據(jù)需要消耗很長(zhǎng)的時(shí)間,或者數(shù)據(jù)庫(kù)壞了讓您飽受痛苦。 如果一個(gè)memcached節(jié)點(diǎn)down了讓您很痛苦,那么您還會(huì)陷入其他很多麻煩。您的系統(tǒng)太脆弱了。您需要做一些優(yōu)化工作。比如處理"驚群"問(wèn) 題(比如 memcached節(jié)點(diǎn)都失效了,反復(fù)的查詢讓您的數(shù)據(jù)庫(kù)不堪重負(fù)...這個(gè)問(wèn)題在FAQ的其他提到過(guò)),或者優(yōu)化不好的查詢。記住,Memcached 并不是您逃避優(yōu)化查詢的借口。 如果您的麻煩僅僅是重新生成緩存數(shù)據(jù)需要消耗很長(zhǎng)時(shí)間(15秒到超過(guò)5分鐘),您可以考慮重新使用數(shù)據(jù)庫(kù)。這里給出一些提示: - 使用MogileFS(或者CouchDB等類似的軟件)在存儲(chǔ)item。把item計(jì)算出來(lái)并dump到磁盤上。MogileFS可以很方便地 覆寫item,并提供快速地訪問(wèn)。.您甚至可以把MogileFS中的item緩存在memcached中,這樣可以加快讀取速度。 MogileFS+Memcached的組合可以加快緩存不命中時(shí)的響應(yīng)速度,提高網(wǎng)站的可用性。
- 重新使用MySQL。MySQL的 InnoDB主鍵查詢的速度非常快。如果大部分緩存數(shù)據(jù)都可以放到VARCHAR字段中,那么主鍵查詢的性能將更好。從memcached中按key查詢 幾乎等價(jià)于MySQL的主鍵查詢:將key 哈希到64-bit的整數(shù),然后將數(shù)據(jù)存儲(chǔ)到MySQL中。您可以把原始(不做哈希)的key存儲(chǔ)都普通的字段中,然后建立二級(jí)索引來(lái)加快查 詢...key被動(dòng)地失效,批量刪除失效的key,等等。
上面的方法都可以引入memcached,在重啟memcached的時(shí)候仍然提供很好的性能。由于您不需要當(dāng)心"hot"的item被 memcached LRU算法突然淘汰,用戶再也不用花幾分鐘來(lái)等待重新生成緩存數(shù)據(jù)(當(dāng)緩存數(shù)據(jù)突然從內(nèi)存中消失時(shí)),因此上面的方法可以全面提高性能。 關(guān)于這些方法的細(xì)節(jié),詳見(jiàn)博客:http://dormando.livejournal.com/495593.html 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是如何做身份驗(yàn)證的? 沒(méi)有身份認(rèn)證機(jī)制!memcached是運(yùn)行在應(yīng)用下層的軟件(身份驗(yàn)證應(yīng)該是應(yīng)用上層的職責(zé))。memcached的客戶端和服務(wù)器端之所以是輕量級(jí)的,部分原因就是完全沒(méi)有實(shí)現(xiàn)身份驗(yàn)證機(jī)制。這樣,memcached可以很快地創(chuàng)建新連接,服務(wù)器端也無(wú)需任何配置。
如果您希望限制訪問(wèn),您可以使用防火墻,或者讓memcached監(jiān)聽(tīng)unix domain socket。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的多線程是什么?如何使用它們? 線 程就是定律(threads rule)!在Steven Grimm和Facebook的努力下,memcached 1.2及更高版本擁有了多線程模式。多線程模式允許memcached能夠充分利用多個(gè)CPU,并在CPU之間共享所有的緩存數(shù)據(jù)。memcached使 用一種簡(jiǎn)單的鎖機(jī)制來(lái)保證數(shù)據(jù)更新操作的互斥。相比在同一個(gè)物理機(jī)器上運(yùn)行多個(gè)memcached實(shí)例,這種方式能夠更有效地處理multi gets。
如果您的系統(tǒng)負(fù)載并不重,也許您不需要啟用多線程工作模式。如果您在運(yùn)行一個(gè)擁有大規(guī)模硬件的、龐大的網(wǎng)站,您將會(huì)看到多線程的好處。
更多信息請(qǐng)參見(jiàn):http://code.sixapart.com/svn/memcached/trunk/server/doc/threads.txt 。
簡(jiǎn) 單地總結(jié)一下:命令解析(memcached在這里花了大部分時(shí)間)可以運(yùn)行在多線程模式下。memcached內(nèi)部對(duì)數(shù)據(jù)的操作是基于很多全局鎖的(因 此這部分工作不是多線程的)。未來(lái)對(duì)多線程模式的改進(jìn),將移除大量的全局鎖,提高memcached在負(fù)載極高的場(chǎng)景下的性能。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached能接受的key的最大長(zhǎng)度是多少? key 的最大長(zhǎng)度是250個(gè)字符。需要注意的是,250是memcached服務(wù)器端內(nèi)部的限制,如果您使用的客戶端支持"key的前綴"或類似特性,那么 key(前綴+原始key)的最大長(zhǎng)度是可以超過(guò)250個(gè)字符的。我們推薦使用使用較短的key,因?yàn)榭梢怨?jié)省內(nèi)存和帶寬。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached對(duì)item的過(guò)期時(shí)間有什么限制? 過(guò)期時(shí)間最大可以達(dá)到30天。memcached把傳入的過(guò)期時(shí)間(時(shí)間段)解釋成時(shí)間點(diǎn)后,一旦到了這個(gè)時(shí)間點(diǎn),memcached就把item置為失效狀態(tài)。這是一個(gè)簡(jiǎn)單但obscure的機(jī)制。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached最大能存儲(chǔ)多大的單個(gè)item? 1MB。如果你的數(shù)據(jù)大于1MB,可以考慮在客戶端壓縮或拆分到多個(gè)key中。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">為什么單個(gè)item的大小被限制在1M byte之內(nèi)? 啊...這是一個(gè)大家經(jīng)常問(wèn)的問(wèn)題!
簡(jiǎn)單的回答:因?yàn)閮?nèi)存分配器的算法就是這樣的。
詳 細(xì)的回答:Memcached的內(nèi)存存儲(chǔ)引擎(引擎將來(lái)可插拔...),使用slabs來(lái)管理內(nèi)存。內(nèi)存被分成大小不等的slabs chunks(先分成大小相等的slabs,然后每個(gè)slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小 依次從一個(gè)最小數(shù)開(kāi)始,按某個(gè)因子增長(zhǎng),直到達(dá)到最大的可能值。
如果最小值為400B,最大值是1MB,因子是1.20,各個(gè)slab的chunk的大小依次是:slab1 - 400B slab2 - 480B slab3 - 576B ...
slab中chunk越大,它和前面的slab之間的間隙就越大。因此,最大值越大,內(nèi)存利用率越低。Memcached必須為每個(gè)slab預(yù)先分配內(nèi)存,因此如果設(shè)置了較小的因子和較大的最大值,會(huì)需要更多的內(nèi)存。
還有其他原因使得您不要這樣向memcached中存取很大的數(shù)據(jù)...不要嘗試把巨大的網(wǎng)頁(yè)放到mencached中。把這樣大的數(shù)據(jù)結(jié)構(gòu)load和unpack到內(nèi)存中需要花費(fèi)很長(zhǎng)的時(shí)間,從而導(dǎo)致您的網(wǎng)站性能反而不好。
如果您確實(shí)需要存儲(chǔ)大于1MB的數(shù)據(jù),你可以修改slabs.c:POWER_BLOCK的值,然后重新編譯memcached;或者使用低效的malloc/free。其他的建議包括數(shù)據(jù)庫(kù)、MogileFS等。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">我可以在不同的memcached節(jié)點(diǎn)上使用大小不等的緩存空間嗎?這么做之后,memcached能夠更有效地使用內(nèi)存嗎? Memcache 客戶端僅根據(jù)哈希算法來(lái)決定將某個(gè)key存儲(chǔ)在哪個(gè)節(jié)點(diǎn)上,而不考慮節(jié)點(diǎn)的內(nèi)存大小。因此,您可以在不同的節(jié)點(diǎn)上使用大小不等的緩存。但是一般都是這樣做 的:擁有較多內(nèi)存的節(jié)點(diǎn)上可以運(yùn)行多個(gè)memcached實(shí)例,每個(gè)實(shí)例使用的內(nèi)存跟其他節(jié)點(diǎn)上的實(shí)例相同。
" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">什么是二進(jìn)制協(xié)議,我該關(guān)注嗎? 關(guān)于二進(jìn)制最好的信息當(dāng)然是二進(jìn)制協(xié)議規(guī)范:http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol 。
二進(jìn)制協(xié)議嘗試為端提供一個(gè)更有效的、可靠的協(xié)議,減少客戶端/服務(wù)器端因處理協(xié)議而產(chǎn)生的CPU時(shí)間。 根據(jù)Facebook的測(cè)試,解析ASCII協(xié)議是memcached中消耗CPU時(shí)間最多的環(huán)節(jié)。所以,我們?yōu)槭裁床桓倪M(jìn)ASCII協(xié)議呢? 在這個(gè)郵件列表的thread中可以找到一些舊的信息:http://lists.danga.com/pipermail/memcached/2007-July/004636.html 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached的內(nèi)存分配器是如何工作的?為什么不適用malloc/free!?為何要使用slabs? 實(shí) 際上,這是一個(gè)編譯時(shí)選項(xiàng)。默認(rèn)會(huì)使用內(nèi)部的slab分配器。您確實(shí)確實(shí)應(yīng)該使用內(nèi)建的slab分配器。最早的時(shí)候,memcached只使用 malloc/free來(lái)管理內(nèi)存。然而,這種方式不能與OS的內(nèi)存管理以前很好地工作。反復(fù)地malloc/free造成了內(nèi)存碎片,OS最終花費(fèi)大量 的時(shí)間去查找連續(xù)的內(nèi)存塊來(lái)滿足malloc的請(qǐng)求,而不是運(yùn)行memcached進(jìn)程。如果您不同意,當(dāng)然可以使用malloc!只是不要在郵件列表中 抱怨啊:)
slab分配器就是為了解決這個(gè)問(wèn)題而生的。內(nèi)存被分配并劃分成chunks,一直被重復(fù)使用。因?yàn)閮?nèi)存被劃分成大小不等的 slabs,如果item的大小與被選擇存放它的slab不是很合適的話,就會(huì)浪費(fèi)一些內(nèi)存。Steven Grimm正在這方面已經(jīng)做出了有效的改進(jìn)。
郵件列表中有一些關(guān)于slab的改進(jìn)(power of n 還是 power of 2)和權(quán)衡方案:http://lists.danga.com/pipermail/memcached/2006-May/002163.html http://lists.danga.com/pipermail/memcached/2007-March/003753.html 。
如果您想使用malloc/free,看看它們工作地怎么樣,您可以在構(gòu)建過(guò)程中定義USE_SYSTEM_MALLOC。這個(gè)特性沒(méi)有經(jīng)過(guò)很好的測(cè)試,所以太不可能得到開(kāi)發(fā)者的支持。 更多信息:http://code.sixapart.com/svn/memcached/trunk/server/doc/memory_management.txt 。 " src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">memcached是原子的嗎? 當(dāng)然!好吧,讓我們來(lái)明確一下: 所有的被發(fā)送到memcached的單個(gè)命令是完全原子的。如果您針對(duì)同一份數(shù)據(jù)同時(shí)發(fā)送了一個(gè)set命令和一個(gè)get命令,它們不會(huì)影響對(duì)方。它們將被串行化、先后執(zhí)行。即使在多線程模式,所有的命令都是原子的,除非程序有bug:) 命令序列不是原子的。如果您通過(guò)get命令獲取了一個(gè)item,修改了它,然后想把它set回memcached,我們不保證這個(gè)item沒(méi)有被其他進(jìn)程(process,未必是操作系統(tǒng)中的進(jìn)程)操作過(guò)。在并發(fā)的情況下,您也可能覆寫了一個(gè)被其他進(jìn)程set的item。
memcached 1.2.5以及更高版本,提供了gets和cas命令,它們可以解決上面的問(wèn)題。如果您使用gets命令查詢某個(gè)key的item,memcached會(huì) 給您返回該item當(dāng)前值的唯一標(biāo)識(shí)。如果您覆寫了這個(gè)item并想把它寫回到memcached中,您可以通過(guò)cas命令把那個(gè)唯一標(biāo)識(shí)一起發(fā)送給 memcached。如果該item存放在memcached中的唯一標(biāo)識(shí)與您提供的一致,您的寫操作將會(huì)成功。如果另一個(gè)進(jìn)程在這期間也修改了這個(gè) item,那么該item存放在memcached中的唯一標(biāo)識(shí)將會(huì)改變,您的寫操作就會(huì)失敗。
通常,基于memcached中item的值來(lái)修改item,是一件棘手的事情。除非您很清楚自己在做什么,否則請(qǐng)不要做這樣的事情。
SQL SERVER的排序規(guī)則平時(shí)使用不是很多,也許不少初學(xué)者還比較陌生,但有一個(gè)錯(cuò)誤大家應(yīng)是經(jīng)常碰到: SQL SERVER數(shù)據(jù)庫(kù),在跨庫(kù)多表連接查詢時(shí),若兩數(shù)據(jù)庫(kù)默認(rèn)字符集不同,系統(tǒng)就會(huì)返回這樣的錯(cuò)誤: “無(wú)法解決 equal to 操作的排序規(guī)則沖突。” 一.錯(cuò)誤分析: 這個(gè)錯(cuò)誤是因?yàn)榕判蛞?guī)則不一致造成的,我們做個(gè)測(cè)試,比如: create table #t1( name varchar(20) collate Albanian_CI_AI_WS, value int)
create table #t2( name varchar(20) collate Chinese_PRC_CI_AI_WS, value int )
表建好后,執(zhí)行連接查詢:
select * from #t1 A inner join #t2 B on A.name=B.name
這樣,錯(cuò)誤就出現(xiàn)了:
服務(wù)器: 消息 446,級(jí)別 16,狀態(tài) 9,行 1 無(wú)法解決 equal to 操作的排序規(guī)則沖突。 要排除這個(gè)錯(cuò)誤,最簡(jiǎn)單方法是,表連接時(shí)指定它的排序規(guī)則,這樣錯(cuò)誤就不再出現(xiàn)了。語(yǔ)句這樣寫:
select * from #t1 A inner join #t2 B on A.name=B.name collate Chinese_PRC_CI_AI_WS
二.排序規(guī)則簡(jiǎn)介:
什么叫排序規(guī)則呢?MS是這樣描述的:"在 Microsoft SQL Server 2000 中,字符串的物理存儲(chǔ)由排序規(guī)則控制。排序規(guī)則指定表示每個(gè)字符的位模式以及存儲(chǔ)和比較字符所使用的規(guī)則。 在查詢分析器內(nèi)執(zhí)行下面語(yǔ)句,可以得到SQL SERVER支持的所有排序規(guī)則。
select * from ::fn_helpcollations()
排序規(guī)則名稱由兩部份構(gòu)成,前半部份是指本排序規(guī)則所支持的字符集。 如: Chinese_PRC_CS_AI_WS 前半部份:指UNICODE字符集,Chinese_PRC_指針對(duì)大陸簡(jiǎn)體字UNICODE的排序規(guī)則。 排序規(guī)則的后半部份即后綴 含義: _BIN 二進(jìn)制排序 _CI(CS) 是否區(qū)分大小寫,CI不區(qū)分,CS區(qū)分 _AI(AS) 是否區(qū)分重音,AI不區(qū)分,AS區(qū)分 _KI(KS) 是否區(qū)分假名類型,KI不區(qū)分,KS區(qū)分 _WI(WS) 是否區(qū)分寬度 WI不區(qū)分,WS區(qū)分
區(qū)分大小寫:如果想讓比較將大寫字母和小寫字母視為不等,請(qǐng)選擇該選項(xiàng)。 區(qū)分重音:如果想讓比較將重音和非重音字母視為不等,請(qǐng)選擇該選項(xiàng)。如果選擇該選項(xiàng),比較還將重音不同的字母視為不等。 區(qū)分假名:如果想讓比較將片假名和平假名日語(yǔ)音節(jié)視為不等,請(qǐng)選擇該選項(xiàng)。 區(qū)分寬度:如果想讓比較將半角字符和全角字符視為不等,請(qǐng)選擇該選項(xiàng) 三.排序規(guī)則的應(yīng)用: SQL SERVER提供了大量的WINDOWS和SQLSERVER專用的排序規(guī)則,但它的應(yīng)用往往被開(kāi)發(fā)人員所忽略。其實(shí)它在實(shí)踐中大有用處。
例1:讓表NAME列的內(nèi)容按拼音排序:
create table #t(id int,name varchar(20)) insert #t select 1,'中' union all select 2,'國(guó)' union all select 3,'人' union all select 4,'阿'
select * from #t order by name collate Chinese_PRC_CS_AS_KS_WS drop table #t /*結(jié)果: id name ----------- -------------------- 4 阿 2 國(guó) 3 人 1 中 */
例2:讓表NAME列的內(nèi)容按姓氏筆劃排序:
create table #t(id int,name varchar(20))
insert #t select 1,'三' union all select 2,'乙' union all select 3,'二' union all select 4,'一' union all select 5,'十' select * from #t order by name collate Chinese_PRC_Stroke_CS_AS_KS_WS drop table #t /*結(jié)果: id name ----------- -------------------- 4 一 2 乙 3 二 5 十 1 三 */
四.在實(shí)踐中排序規(guī)則應(yīng)用的擴(kuò)展 SQL SERVER漢字排序規(guī)則可以按拼音、筆劃等排序,那么我們?nèi)绾卫眠@種功能來(lái)處理漢字的一些難題呢?我現(xiàn)在舉個(gè)例子:
用排序規(guī)則的特性計(jì)算漢字筆劃
要計(jì)算漢字筆劃,我們得先做準(zhǔn)備工作,我們知道,WINDOWS多國(guó)漢字,UNICODE目前 收錄漢字共20902個(gè)。簡(jiǎn)體GBK碼漢字UNICODE值從19968開(kāi)始。 首先,我們先用SQLSERVER方法得到所有漢字,不用字典,我們簡(jiǎn)單利用SQL語(yǔ)句就可以得到:
select top 20902 code=identity(int,19968,1) into #t from syscolumns a,syscolumns b
再用以下語(yǔ)句,我們就得到所有漢字,它是按UNICODE值排序的:
select code,nchar(code) as CNWord from #t
然后,我們用SELECT語(yǔ)句,讓它按筆劃排序。
select code,nchar(code) as CNWord from #t order by nchar(code) collate Chinese_PRC_Stroke_CS_AS_KS_WS,code
結(jié)果: code CNWord ----------- ------ 19968 一 20008 丨 20022 丶 20031 丿 20032 乀 20033 乁 20057 乙 20058 乚 20059 乛 20101 亅 19969 丁 ..........
從上面的結(jié)果,我們可以清楚的看到,一筆的漢字,code是從19968到20101,從小到大排,但到了二筆漢字的第一個(gè)字“丁”,CODE為 19969,就不按順序而重新開(kāi)始了。有了這結(jié)果,我們就可以輕松的用SQL語(yǔ)句得到每種筆劃漢字歸類的第一個(gè)或最后一個(gè)漢字。 下面用語(yǔ)句得到最后一個(gè)漢字:
create table #t1(id int identity,code int,cnword nvarchar(2))
insert #t1(code,cnword) select code,nchar(code) as CNWord from #t order by nchar(code) collate Chinese_PRC_Stroke_CS_AS_KS_WS,code
select A.cnword from #t1 A left join #t1 B on A.id=B.id-1 and A.code<B.code where B.code is null order by A.id
得到36個(gè)漢字,每個(gè)漢字都是每種筆劃數(shù)按Chinese_PRC_Stroke_CS_AS_KS_WS排序規(guī)則排序后的 最后一個(gè)漢字:
亅阝馬風(fēng)龍齊龜齒鴆齔龕龂齠齦齪龍龠龎龐龑龡龢龝齹龣龥齈龞麷鸞麣龖龗齾齉龘
上面可以看出:“亅”是所有一筆漢字排序后的最后一個(gè)字,“阝”是所有二筆漢字排序后的最后一個(gè)字......等等。 但同時(shí)也發(fā)現(xiàn),從第33個(gè)漢字“龗(33筆)”后面的筆劃有些亂,不正確。但沒(méi)關(guān)系,比“龗”筆劃多的只有四個(gè)漢字,我們手工加上:齾35筆,齉36筆,靐39筆,龘64筆 建漢字筆劃表(TAB_HZBH): create table tab_hzbh(id int identity,cnword nchar(1)) --先插入前33個(gè)漢字 insert tab_hzbh select top 33 A.cnword from #t1 A left join #t1 B on A.id=B.id-1 and A.code<B.code where B.code is null order by A.id --再加最后四個(gè)漢字 set identity_insert tab_hzbh on go insert tab_hzbh(id,cnword) select 35,N'齾' union all select 36,N'齉' union all select 39,N'靐' union all select 64,N'龘' go set identity_insert tab_hzbh off go
到此為止,我們可以得到結(jié)果了,比如我們想得到漢字“國(guó)”的筆劃:
declare @a nchar(1) set @a='國(guó)' select top 1 id from tab_hzbh where cnword>=@a collate Chinese_PRC_Stroke_CS_AS_KS_WS order by id
id ----------- 8 (結(jié)果:漢字“國(guó)”筆劃數(shù)為8)
上面所有準(zhǔn)備過(guò)程,只是為了寫下面這個(gè)函數(shù),這個(gè)函數(shù)撇開(kāi)上面建的所有臨時(shí)表和固定表,為了通用和代碼轉(zhuǎn)移方便,把表tab_hzbh的內(nèi)容寫在語(yǔ)句內(nèi),然后計(jì)算用戶輸入一串漢字的總筆劃:
create function fun_getbh(@str nvarchar(4000)) returns int as begin declare @word nchar(1),@n int set @n=0 while len(@str)>0 begin set @word=left(@str,1) --如果非漢字,筆劃當(dāng)0計(jì) set @n=@n+(case when unicode(@word) between 19968 and 19968+20901 then (select top 1 id from ( select 1 as id,N'亅' as word union all select 2,N'阝' union all select 3,N'馬' union all select 4,N'風(fēng)' union all select 5,N'龍' union all select 6,N'齊' union all select 7,N'龜' union all select 8,N'齒' union all select 9,N'鴆' union all select 10,N'齔' union all select 11,N'龕' union all select 12,N'龂' union all select 13,N'齠' union all select 14,N'齦' union all select 15,N'齪' union all select 16,N'龍' union all select 17,N'龠' union all select 18,N'龎' union all select 19,N'龐' union all select 20,N'龑' union all select 21,N'龡' union all select 22,N'龢' union all select 23,N'龝' union all select 24,N'齹' union all select 25,N'龣' union all select 26,N'龥' union all select 27,N'齈' union all select 28,N'龞' union all select 29,N'麷' union all select 30,N'鸞' union all select 31,N'麣' union all select 32,N'龖' union all select 33,N'龗' union all select 35,N'齾' union all select 36,N'齉' union all select 39,N'靐' union all select 64,N'龘' ) T where word>=@word collate Chinese_PRC_Stroke_CS_AS_KS_WS order by id ASC) else 0 end) set @str=right(@str,len(@str)-1) end return @n end
--函數(shù)調(diào)用實(shí)例: select dbo.fun_getbh('中華人民共和國(guó)'),dbo.fun_getbh('中華人民共和國(guó)') 執(zhí)行結(jié)果:筆劃總數(shù)分別為39和46,簡(jiǎn)繁體都行。
當(dāng)然,你也可以把上面“UNION ALL”內(nèi)的漢字和筆劃改存在固定表內(nèi),在漢字列建CLUSTERED INDEX,列排序規(guī)則設(shè)定為: Chinese_PRC_Stroke_CS_AS_KS_WS 這樣速度更快。如果你用的是BIG5碼的操作系統(tǒng),你得另外生成漢字,方法一樣。但有一點(diǎn)要記住:這些漢字是通過(guò)SQL語(yǔ)句SELECT出來(lái)的,不是手工輸入的,更不是查字典得來(lái)的,因?yàn)樾氯A字典畢竟不同于UNICODE字符集,查字典的結(jié)果會(huì)不正確。
用排序規(guī)則的特性得到漢字拼音首字母
用得到筆劃總數(shù)相同的方法,我們也可以寫出求漢字拼音首字母的函數(shù)。如下:
create function fun_getPY(@str nvarchar(4000)) returns nvarchar(4000) as begin declare @word nchar(1),@PY nvarchar(4000) set @PY='' while len(@str)>0 begin set @word=left(@str,1) --如果非漢字字符,返回原字符 set @PY=@PY+(case when unicode(@word) between 19968 and 19968+20901 then (select top 1 PY from ( select 'A' as PY,N'驁' as word union all select 'B',N'簿' union all select 'C',N'錯(cuò)' union all select 'D',N'鵽' union all select 'E',N'樲' union all select 'F',N'鰒' union all select 'G',N'腂' union all select 'H',N'夻' union all select 'J',N'攈' union all select 'K',N'穒' union all select 'L',N'鱳' union all select 'M',N'旀' union all select 'N',N'桛' union all select 'O',N'漚' union all select 'P',N'曝' union all select 'Q',N'囕' union all select 'R',N'鶸' union all select 'S',N'蜶' union all select 'T',N'籜' union all select 'W',N'鶩' union all select 'X',N'鑂' union all select 'Y',N'韻' union all select 'Z',N'咗' ) T where word>=@word collate Chinese_PRC_CS_AS_KS_WS order by PY ASC) else @word end) set @str=right(@str,len(@str)-1) end return @PY end
--函數(shù)調(diào)用實(shí)例: select dbo.fun_getPY('中華人民共和國(guó)'),dbo.fun_getPY('中華人民共和國(guó)') 結(jié)果都為:ZHRMGHG
你若有興趣,也可用相同的方法,擴(kuò)展為得到漢字全拼的函數(shù),甚至還可以得到全拼的讀音聲調(diào),不過(guò)全拼分類大多了。得到全拼最好是用對(duì)照表,兩萬(wàn)多漢字搜索速度很快,用對(duì)照表還可以充分利用表的索引。 排序規(guī)則還有很多其它的巧妙用法。歡迎大家共同探討。
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html The JavaTM Virtual Machine Specification Second Edition Tim Lindholm Frank Yellin
- Preface
- 1 Introduction
- 1.1 A Bit of History
- 1.2 The Java Virtual Machine
- 1.3 Summary of Chapters
- 1.4 Notation
- 2 Java Programming Language Concepts
- 2.1 Unicode
- 2.2 Identifiers
- 2.3 Literals
- 2.4 Types and Values
- 2.4.1 Primitive Types and Values 2.4.2 Operators on Integral Values
- 2.4.3 Floating-Point Types, Value Sets, and Values
- 2.4.4 Operators on Floating-Point Values
- 2.4.5 Operators on
boolean Values
- 2.4.6 Reference Types, Objects, and Reference Values
- 2.4.7 The Class
Object
- 2.4.8 The Class
String
- 2.4.9 Operators on Objects
- 2.5 Variables
- 2.5.1 Initial Values of Variables
- 2.5.2 Variables Have Types, Objects Have Classes
- 2.6 Conversions and Promotions
- 2.6.1 Identity Conversions
- 2.6.2 Widening Primitive Conversions
- 2.6.3 Narrowing Primitive Conversions
- 2.6.4 Widening Reference Conversions
- 2.6.5 Narrowing Reference Conversions
- 2.6.6 Value Set Conversion
- 2.6.7 Assignment Conversion
- 2.6.8 Method Invocation Conversion
- 2.6.9 Casting Conversion
- 2.6.10 Numeric Promotion
- 2.7 Names and Packages
- 2.7.1 Simple Names and Qualified Names
- 2.7.2 Packages
- 2.7.3 Members
- 2.7.4 Qualified Names and Access Control
- 2.7.5 Fully Qualified Names
- 2.8 Classes
- 2.8.1 Class Names
- 2.8.2 Class Modifiers
- 2.8.3 Superclasses and Subclasses
- 2.8.4 The Class Members
- 2.9 Fields
- 2.9.1 Field Modifiers
- 2.9.2 Initialization of Fields
- 2.10 Methods
- 2.10.1 Formal Parameters
- 2.10.2 Method Signature
- 2.10.3 Method Modifiers
- 2.11 Static Initializers
- 2.12 Constructors
- 2.12.1 Constructor Modifiers
2.13 Interfaces
- 2.13.1 Interface Modifiers
- 2.13.2 Superinterfaces
- 2.13.3 Interface Members
- 2.13.4 Overriding, Inheritance, and Overloading in Interfaces
- 2.14 Nested Classes and Interfaces
- 2.15 Arrays
- 2.15.1 Array Types
- 2.15.2 Array Variables
- 2.15.3 Array Creation
- 2.15.4 Array Access
- 2.16 Exceptions
- 2.16.1 The Causes of Exceptions
- 2.16.2 Handling an Exception
- 2.16.3 The Exception Hierarchy
- 2.16.4 The Classes Exception and RuntimeException
- 2.17 Execution
- 2.17.1 Virtual Machine Start-up
- 2.17.2 Loading
- 2.17.3 Linking: Verification, Preparation, and Resolution
- 2.17.4 Initialization
- 2.17.5 Detailed Initialization Procedure
- 2.17.6 Creation of New Class Instances
- 2.17.7 Finalization of Class Instances
- 2.17.8 Unloading of Classes and Interfaces
- 2.17.9 Virtual Machine Exit
- 2.18 FP-strict Expressions
- 2.19 Threads
- 3 The Structure of the Java Virtual Machine
- 3.1 The
class File Format
- 3.2 Data Types
- 3.3 Primitive Types and Values
- 3.3.1 Integral Types and Values
- 3.3.2 Floating-Point Types, Value Sets, and Values
- 3.3.3 The
returnAddress Type and Values
- 3.3.4 The
boolean Type 3.4 Reference Types and Values
- 3.5 Runtime Data Areas
- 3.5.1 The
pc Register
- 3.5.2 Java Virtual Machine Stacks
- 3.5.3 Heap
- 3.5.4 Method Area
- 3.5.5 Runtime Constant Pool
- 3.5.6 Native Method Stacks
3.6 Frames
- 3.6.1 Local Variables
- 3.6.2 Operand Stacks
- 3.6.3 Dynamic Linking
- 3.6.4 Normal Method Invocation Completion
- 3.6.5 Abrupt Method Invocation Completion
- 3.6.6 Additional Information
3.7 Representation of Objects
- 3.8 Floating-Point Arithmetic
- 3.8.1 Java Virtual Machine Floating-Point Arithmetic and IEEE 754
- 3.8.2 Floating-Point Modes
- 3.8.3 Value Set Conversion
3.9 Specially Named Initialization Methods
- 3.10 Exceptions
- 3.11 Instruction Set Summary
- 3.11.1 Types and the Java Virtual Machine
- 3.11.2 Load and Store Instructions
- 3.11.3 Arithmetic Instructions
- 3.11.4 Type Conversion Instructions
- 3.11.5 Object Creation and Manipulation
- 3.11.6 Operand Stack Management Instructions
- 3.11.7 Control Transfer Instructions
- 3.11.8 Method Invocation and Return Instructions
- 3.11.9 Throwing Exceptions
- 3.11.10 Implementing finally
- 3.11.11 Synchronization
3.12 Class Libraries
- 3.13 Public Design, Private Implementation
- 4 The
class File Format
- 4.1 The
ClassFile Structure
- 4.2 The Internal Form of Fully Qualified Class and Interface Names
- 4.3 Descriptors
- 4.3.1 Grammar Notation
- 4.3.2 Field Descriptors
- 4.3.3 Method Descriptors
4.4 The Constant Pool
- 4.4.1 The
CONSTANT_Class_info Structure
- 4.4.2 The
CONSTANT_Fieldref_info , CONSTANT_Methodref_info , and CONSTANT_InterfaceMethodref_info Structures
- 4.4.3 The
CONSTANT_String_info Structure
- 4.4.4 The
CONSTANT_Integer_info and CONSTANT_Float_info Structures
- 4.4.5 The
CONSTANT_Long_info and CONSTANT_Double_info Structures
- 4.4.6 The
CONSTANT_NameAndType_info Structure
- 4.4.7 The
CONSTANT_Utf8_info Structure 4.5 Fields
- 4.6 Methods
- 4.7 Attributes
- 4.7.1 Defining and Naming New Attributes
- 4.7.2 The
ConstantValue Attribute
- 4.7.3 The
Code Attribute
- 4.7.4 The
Exceptions Attribute
- 4.7.5 The
InnerClasses Attribute
- 4.7.6 The
Synthetic Attribute
- 4.7.7 The
SourceFile Attribute
- 4.7.8 The
LineNumberTable Attribute
- 4.7.9 The
LocalVariableTable Attribute
- 4.7.10 The
Deprecated Attribute 4.8 Constraints on Java Virtual Machine Code
- 4.8.1 Static Constraints
- 4.8.2 Structural Constraints
4.9 Verification of class Files
- 4.9.1 The Verification Process
- 4.9.2 The Bytecode Verifier
- 4.9.3 Values of Types
long and double
- 4.9.4 Instance Initialization Methods and Newly Created Objects
- 4.9.5 Exception Handlers
- 4.9.6 Exceptions and finally
4.10 Limitations of the Java Virtual Machine
- 5 Loading, Linking, and Initializing
- 5.1 The Runtime Constant Pool
- 5.2 Virtual Machine Start-up
- 5.3 Creation and Loading
- 5.3.1 Loading Using the Bootstrap Class Loader
- 5.3.2 Loading Using a User-defined Class Loader
- 5.3.3 Creating Array Classes
- 5.3.4 Loading Constraints
- 5.3.5 Deriving a Class from a
class File Representation 5.4 Linking
- 5.4.1 Verification
- 5.4.2 Preparation
- 5.4.3 Resolution
- 5.4.4 Access Control
5.5 Initialization
- 5.6 Binding Native Method Implementations
- 6 The Java Virtual Machine Instruction Set
- 6.1 Assumptions: The Meaning of "Must"
- 6.2 Reserved Opcodes
- 6.3 Virtual Machine Errors
- 6.4 Format of Instruction Descriptions
- 7 Compiling for the Java Virtual Machine
- 7.1 Format of Examples
- 7.2 Use of Constants, Local Variables, and Control Constructs
- 7.3 Arithmetic
- 7.4 Accessing the Runtime Constant Pool
- 7.5 More Control Examples
- 7.6 Receiving Arguments
- 7.7 Invoking Methods
- 7.8 Working with Class Instances
- 7.9 Arrays
- 7.10 Compiling Switches
- 7.11 Operations on the Operand Stack
- 7.12 Throwing and Handling Exceptions
- 7.13 Compiling
finally
- 7.14 Synchronization
- 7.15 Compiling Nested Classes and Interfaces
- 8 Threads and Locks
- 8.1 Terminology and Framework
- 8.2 Execution Order and Consistency
- 8.3 Rules About Variables
- 8.4 Nonatomic Treatment of
double and long Variables
- 8.5 Rules About Locks
- 8.6 Rules About the Interaction of Locks and Variables
- 8.7 Rules for
volatile Variables
- 8.8 Prescient Store Operations
- 8.9 Discussion
- 8.10 Example: Possible Swap
- 8.11 Example: Out-of-Order Writes
- 8.12 Threads
- 8.13 Locks and Synchronization
- 8.14 Wait Sets and Notification
- 9 Opcode Mnemonics by Opcode
- Appendix: Summary of Clarifications and Amendments
- Index
本文來(lái)自和你在一起的博客,原文標(biāo)題為《JVM調(diào)優(yōu)總結(jié)(一)-- 一些概念》。 數(shù)據(jù)類型 Java虛擬機(jī)中,數(shù)據(jù)類型可以分為兩類:基本類型和引用類型。基本類型的變量保存原始值,即:他代表的值就是數(shù)值本身;而引用類型的變量保存引用值。“引用值”代表了某個(gè)對(duì)象的引用,而不是對(duì)象本身,對(duì)象本身存放在這個(gè)引用值所表示的地址的位置。 基本類型包括:byte, short, int, long, char, float, double, Boolean, returnAddress 引用類型包括:類類型,接口類型和數(shù)組。 堆與棧 堆和棧是程序運(yùn)行的關(guān)鍵,很有必要把他們的關(guān)系說(shuō)清楚。 棧是運(yùn)行時(shí)的單位,而堆是存儲(chǔ)的單位。 棧解決程序的運(yùn)行問(wèn)題,即程序如何執(zhí)行,或者說(shuō)如何處理數(shù)據(jù);堆解決的是數(shù)據(jù)存儲(chǔ)的問(wèn)題,即數(shù)據(jù)怎么放、放在哪兒。 在Java中一個(gè)線程就會(huì)相應(yīng)有一個(gè)線程棧與之對(duì)應(yīng),這點(diǎn)很容易理解,因?yàn)椴煌木€程執(zhí)行邏輯有所不同,因此需要一個(gè)獨(dú)立的線程棧。而堆則是所有線 程共享的。棧因?yàn)槭沁\(yùn)行單位,因此里面存儲(chǔ)的信息都是跟當(dāng)前線程(或程序)相關(guān)信息的。包括局部變量、程序運(yùn)行狀態(tài)、方法返回值等等;而堆只負(fù)責(zé)存儲(chǔ)對(duì)象 信息。 為什么要把堆和棧區(qū)分出來(lái)呢?棧中不是也可以存儲(chǔ)數(shù)據(jù)嗎? 第一,從軟件設(shè)計(jì)的角度看,棧代表了處理邏輯,而堆代表了數(shù)據(jù)。這樣分開(kāi),使得處理邏輯更為清晰。分而治之的思想。這種隔離、模塊化的思想在軟件設(shè)計(jì)的方方面面都有體現(xiàn)。 第二,堆與棧的分離,使得堆中的內(nèi)容可以被多個(gè)棧共享(也可以理解為多個(gè)線程訪問(wèn)同一個(gè)對(duì)象)。這種共享的收益是很多的。一方面這種共享提供了一種有效的數(shù)據(jù)交互方式(如:共享內(nèi)存),另一方面,堆中的共享常量和緩存可以被所有棧訪問(wèn),節(jié)省了空間。 第三,棧因?yàn)檫\(yùn)行時(shí)的需要,比如保存系統(tǒng)運(yùn)行的上下文,需要進(jìn)行地址段的劃分。由于棧只能向上增長(zhǎng),因此就會(huì)限制住棧存儲(chǔ)內(nèi)容的能力。而堆不同,堆中的對(duì)象是可以根據(jù)需要?jiǎng)討B(tài)增長(zhǎng)的,因此棧和堆的拆分,使得動(dòng)態(tài)增長(zhǎng)成為可能,相應(yīng)棧中只需記錄堆中的一個(gè)地址即可。 第四,面向?qū)ο缶褪嵌押蜅5耐昝澜Y(jié)合。其實(shí),面向?qū)ο蠓绞降某绦蚺c以前結(jié)構(gòu)化的程序在執(zhí)行上沒(méi)有任何區(qū)別。但是,面向?qū)ο蟮囊耄沟脤?duì)待問(wèn)題的思 考方式發(fā)生了改變,而更接近于自然方式的思考。當(dāng)我們把對(duì)象拆開(kāi),你會(huì)發(fā)現(xiàn),對(duì)象的屬性其實(shí)就是數(shù)據(jù),存放在堆中;而對(duì)象的行為(方法),就是運(yùn)行邏輯, 放在棧中。我們?cè)诰帉憣?duì)象的時(shí)候,其實(shí)即編寫了數(shù)據(jù)結(jié)構(gòu),也編寫的處理數(shù)據(jù)的邏輯。不得不承認(rèn),面向?qū)ο蟮脑O(shè)計(jì),確實(shí)很美。 在Java中,Main函數(shù)就是棧的起始點(diǎn),也是程序的起始點(diǎn)。 程序要運(yùn)行總是有一個(gè)起點(diǎn)的。同C語(yǔ)言一樣,java中的Main就是那個(gè)起點(diǎn)。無(wú)論什么java程序,找到main就找到了程序執(zhí)行的入口:) 堆中存什么?棧中存什么? 堆中存的是對(duì)象。棧中存的是基本數(shù)據(jù)類型和堆中對(duì)象的引用。一個(gè)對(duì)象的大小是不可估計(jì)的,或者說(shuō)是可以動(dòng)態(tài)變化的,但是在棧中,一個(gè)對(duì)象只對(duì)應(yīng)了一個(gè)4btye的引用(堆棧分離的好處:))。 為什么不把基本類型放堆中呢?因?yàn)槠湔加玫目臻g一般是1~8個(gè)字節(jié)——需要空間比較少,而且因?yàn)槭腔绢愋停圆粫?huì)出現(xiàn)動(dòng)態(tài)增長(zhǎng)的情況——長(zhǎng)度固 定,因此棧中存儲(chǔ)就夠了,如果把他存在堆中是沒(méi)有什么意義的(還會(huì)浪費(fèi)空間,后面說(shuō)明)。可以這么說(shuō),基本類型和對(duì)象的引用都是存放在棧中,而且都是幾個(gè) 字節(jié)的一個(gè)數(shù),因此在程序運(yùn)行時(shí),他們的處理方式是統(tǒng)一的。但是基本類型、對(duì)象引用和對(duì)象本身就有所區(qū)別了,因?yàn)橐粋€(gè)是棧中的數(shù)據(jù)一個(gè)是堆中的數(shù)據(jù)。最常 見(jiàn)的一個(gè)問(wèn)題就是,Java中參數(shù)傳遞時(shí)的問(wèn)題。 Java中的參數(shù)傳遞時(shí)傳值呢?還是傳引用? 要說(shuō)明這個(gè)問(wèn)題,先要明確兩點(diǎn): 1. 不要試圖與C進(jìn)行類比,Java中沒(méi)有指針的概念 2. 程序運(yùn)行永遠(yuǎn)都是在棧中進(jìn)行的,因而參數(shù)傳遞時(shí),只存在傳遞基本類型和對(duì)象引用的問(wèn)題。不會(huì)直接傳對(duì)象本身。 明確以上兩點(diǎn)后。Java在方法調(diào)用傳遞參數(shù)時(shí),因?yàn)闆](méi)有指針,所以它都是進(jìn)行傳值調(diào)用(這點(diǎn)可以參考C的傳值調(diào)用)。因此,很多書里面都說(shuō)Java是進(jìn)行傳值調(diào)用,這點(diǎn)沒(méi)有問(wèn)題,而且也簡(jiǎn)化的C中復(fù)雜性。 但是傳引用的錯(cuò)覺(jué)是如何造成的呢?在運(yùn)行棧中,基本類型和引用的處理是一樣的,都是傳值,所以,如果是傳引用的方法調(diào)用,也同時(shí)可以理解為“傳引用 值”的傳值調(diào)用,即引用的處理跟基本類型是完全一樣的。但是當(dāng)進(jìn)入被調(diào)用方法時(shí),被傳遞的這個(gè)引用的值,被程序解釋(或者查找)到堆中的對(duì)象,這個(gè)時(shí)候才 對(duì)應(yīng)到真正的對(duì)象。如果此時(shí)進(jìn)行修改,修改的是引用對(duì)應(yīng)的對(duì)象,而不是引用本身,即:修改的是堆中的數(shù)據(jù)。所以這個(gè)修改是可以保持的了。 對(duì)象,從某種意義上說(shuō),是由基本類型組成的。可以把一個(gè)對(duì)象看作為一棵樹(shù),對(duì)象的屬性如果還是對(duì)象,則還是一顆樹(shù)(即非葉子節(jié)點(diǎn)),基本類型則為樹(shù) 的葉子節(jié)點(diǎn)。程序參數(shù)傳遞時(shí),被傳遞的值本身都是不能進(jìn)行修改的,但是,如果這個(gè)值是一個(gè)非葉子節(jié)點(diǎn)(即一個(gè)對(duì)象引用),則可以修改這個(gè)節(jié)點(diǎn)下面的所有內(nèi) 容。
堆和棧中,棧是程序運(yùn)行最根本的東西。程序運(yùn)行可以沒(méi)有堆,但是不能沒(méi)有棧。而堆是為棧進(jìn)行數(shù)據(jù)存儲(chǔ)服務(wù),說(shuō)白了堆就是一塊共享的內(nèi)存。不過(guò),正是因?yàn)槎押蜅5姆蛛x的思想,才使得Java的垃圾回收成為可能。 Java中,棧的大小通過(guò)-Xss來(lái)設(shè)置,當(dāng)棧中存儲(chǔ)數(shù)據(jù)比較多時(shí),需要適當(dāng)調(diào)大這個(gè)值,否則會(huì)出現(xiàn)java.lang.StackOverflowError異常。常見(jiàn)的出現(xiàn)這個(gè)異常的是無(wú)法返回的遞歸,因?yàn)榇藭r(shí)棧中保存的信息都是方法返回的記錄點(diǎn)。
二 JAVA垃圾收集器 2.1 垃圾收集簡(jiǎn)史 垃圾收集提供了內(nèi)存管理的機(jī)制,使得應(yīng)用程序不需要在關(guān)注內(nèi)存如何釋放,內(nèi)存用完后,垃圾收集會(huì)進(jìn)行收集,這樣就減輕了因?yàn)槿藶榈墓芾韮?nèi)存而造成的 錯(cuò)誤,比如在C++語(yǔ)言里,出現(xiàn)內(nèi)存泄露時(shí)很常見(jiàn)的。Java語(yǔ)言是目前使用最多的依賴于垃圾收集器的語(yǔ)言,但是垃圾收集器策略從20世紀(jì)60年代就已經(jīng) 流行起來(lái)了,比如Smalltalk,Eiffel等編程語(yǔ)言也集成了垃圾收集器的機(jī)制。 2.2 常見(jiàn)的垃圾收集策略  所有的垃圾收集算法都面臨同一個(gè)問(wèn)題,那就是找出應(yīng)用程序不可到達(dá)的內(nèi)存塊,將其釋放,這里面得不可到達(dá)主要是指應(yīng)用程序已經(jīng)沒(méi)有內(nèi)存塊的引用了, 而在JAVA中,某個(gè)對(duì)象對(duì)應(yīng)用程序是可到達(dá)的是指:這個(gè)對(duì)象被根(根主要是指類的靜態(tài)變量,或者活躍在所有線程棧的對(duì)象的引用)引用或者對(duì)象被另一個(gè)可 到達(dá)的對(duì)象引用。 2.2.1 Reference Counting(引用計(jì)數(shù)) 引用計(jì)數(shù)是最簡(jiǎn)單直接的一種方式,這種方式在每一個(gè)對(duì)象中增加一個(gè)引用的計(jì)數(shù),這個(gè)計(jì)數(shù)代表當(dāng)前程序有多少個(gè)引用引用了此對(duì)象,如果此對(duì)象的引用計(jì)數(shù)變?yōu)?,那么此對(duì)象就可以作為垃圾收集器的目標(biāo)對(duì)象來(lái)收集。 優(yōu)點(diǎn): 簡(jiǎn)單,直接,不需要暫停整個(gè)應(yīng)用 缺點(diǎn): 1.需要編譯器的配合,編譯器要生成特殊的指令來(lái)進(jìn)行引用計(jì)數(shù)的操作,比如每次將對(duì)象賦值給新的引用,或者者對(duì)象的引用超出了作用域等。 2.不能處理循環(huán)引用的問(wèn)題 2.2.2 跟蹤收集器 跟蹤收集器首先要暫停整個(gè)應(yīng)用程序,然后開(kāi)始從根對(duì)象掃描整個(gè)堆,判斷掃描的對(duì)象是否有對(duì)象引用,這里面有三個(gè)問(wèn)題需要搞清楚:  1.如果每次掃描整個(gè)堆,那么勢(shì)必讓GC的時(shí)間變長(zhǎng),從而影響了應(yīng)用本身的執(zhí)行。因此在JVM里面采用了分代收集,在新生代收集的時(shí)候minor gc只需要掃描新生代,而不需要掃描老生代。 2.JVM采用了分代收集以后,minor gc只掃描新生代,但是minor gc怎么判斷是否有老生代的對(duì)象引用了新生代的對(duì)象,JVM采用了卡片標(biāo)記的策略,卡片標(biāo)記將老生代分成了一塊一塊的,劃分以后的每一個(gè)塊就叫做一個(gè)卡 片,JVM采用卡表維護(hù)了每一個(gè)塊的狀態(tài),當(dāng)JAVA程序運(yùn)行的時(shí)候,如果發(fā)現(xiàn)老生代對(duì)象引用或者釋放了新生代對(duì)象的引用,那么就JVM就將卡表的狀態(tài)設(shè) 置為臟狀態(tài),這樣每次minor gc的時(shí)候就會(huì)只掃描被標(biāo)記為臟狀態(tài)的卡片,而不需要掃描整個(gè)堆。具體如下圖: 3.GC在收集一個(gè)對(duì)象的時(shí)候會(huì)判斷是否有引用指向?qū)ο螅贘AVA中的引用主要有四種:Strong reference,Soft reference,Weak reference,Phantom reference. ◆ Strong Reference 強(qiáng)引用是JAVA中默認(rèn)采用的一種方式,我們平時(shí)創(chuàng)建的引用都屬于強(qiáng)引用。如果一個(gè)對(duì)象沒(méi)有強(qiáng)引用,那么對(duì)象就會(huì)被回收。 - public void testStrongReference(){
- Object referent = new Object();
- Object strongReference = referent;
- referent = null;
- System.gc();
- assertNotNull(strongReference);
- }
◆ Soft Reference 軟引用的對(duì)象在GC的時(shí)候不會(huì)被回收,只有當(dāng)內(nèi)存不夠用的時(shí)候才會(huì)真正的回收,因此軟引用適合緩存的場(chǎng)合,這樣使得緩存中的對(duì)象可以盡量的再內(nèi)存中待長(zhǎng)久一點(diǎn)。 - Public void testSoftReference(){
- String str = "test";
- SoftReference<String> softreference = new SoftReference<String>(str);
- str=null;
- System.gc();
- assertNotNull(softreference.get());
- }
◆ Weak reference 弱引用有利于對(duì)象更快的被回收,假如一個(gè)對(duì)象沒(méi)有強(qiáng)引用只有弱引用,那么在GC后,這個(gè)對(duì)象肯定會(huì)被回收。 - Public void testWeakReference(){
- String str = "test";
- WeakReference<String> weakReference = new WeakReference<String>(str);
- str=null;
- System.gc();
- assertNull(weakReference.get());
- }
◆ Phantom reference 2.2.2.1 Mark-Sweep Collector(標(biāo)記-清除收集器) 標(biāo)記清除收集器最早由Lisp的發(fā)明人于1960年提出,標(biāo)記清除收集器停止所有的工作,從根掃描每個(gè)活躍的對(duì)象,然后標(biāo)記掃描過(guò)的對(duì)象,標(biāo)記完成以后,清除那些沒(méi)有被標(biāo)記的對(duì)象。 優(yōu)點(diǎn): 1 解決循環(huán)引用的問(wèn)題 2 不需要編譯器的配合,從而就不執(zhí)行額外的指令 缺點(diǎn): 1.每個(gè)活躍的對(duì)象都要進(jìn)行掃描,收集暫停的時(shí)間比較長(zhǎng)。 2.2.2.2 Copying Collector(復(fù)制收集器)復(fù)制收集器將內(nèi)存分為兩塊一樣大小空間,某一個(gè)時(shí)刻,只有一個(gè)空間處于活躍的狀態(tài),當(dāng)活躍的空間滿的時(shí)候,GC就會(huì)將活 躍的對(duì)象復(fù)制到未使用的空間中去,原來(lái)不活躍的空間就變?yōu)榱嘶钴S的空間。復(fù)制收集器具體過(guò)程可以參考下圖:  優(yōu)點(diǎn): 1 只掃描可以到達(dá)的對(duì)象,不需要掃描所有的對(duì)象,從而減少了應(yīng)用暫停的時(shí)間 缺點(diǎn): 1.需要額外的空間消耗,某一個(gè)時(shí)刻,總是有一塊內(nèi)存處于未使用狀態(tài) 2.復(fù)制對(duì)象需要一定的開(kāi)銷 2.2.2.3 Mark-Compact Collector(標(biāo)記-整理收集器)標(biāo)記整理收集器汲取了標(biāo)記清除和復(fù)制收集器的優(yōu)點(diǎn),它分兩個(gè)階段執(zhí)行,在第一個(gè)階段,首先掃描所有活躍的對(duì)象,并 標(biāo)記所有活躍的對(duì)象,第二個(gè)階段首先清除未標(biāo)記的對(duì)象,然后將活躍的的對(duì)象復(fù)制到堆得底部。標(biāo)記整理收集器的過(guò)程示意圖請(qǐng)參考下圖:Mark- compact策略極大的減少了內(nèi)存碎片,并且不需要像Copy Collector一樣需要兩倍的空間。
垃圾收集器策略從20世紀(jì)60年代就已經(jīng)流行起來(lái)了,相比于其他編程語(yǔ)言,Java語(yǔ)言是目前使用最多的依賴于垃圾收集器的語(yǔ)言。 AD: JVM內(nèi)存模型是Java的核心技術(shù)之一,之前51CTO曾為大家介紹過(guò)JVM分代垃圾回收策略的基礎(chǔ)概念,現(xiàn)在很多編程語(yǔ)言都引入了類似Java JVM的內(nèi)存模型和垃圾收集器的機(jī)制,下面我們將主要針對(duì)Java中的JVM內(nèi)存模型及垃圾收集的具體策略進(jìn)行綜合的分析。 一 JVM內(nèi)存模型 1.1 Java棧 Java棧是與每一個(gè)線程關(guān)聯(lián)的,JVM在創(chuàng)建每一個(gè)線程的時(shí)候,會(huì)分配一定的棧空間給線程。它主要用來(lái)存儲(chǔ)線程執(zhí)行過(guò)程中的局部變量,方法的返回 值,以及方法調(diào)用上下文。棧空間隨著線程的終止而釋放。StackOverflowError:如果在線程執(zhí)行的過(guò)程中,棧空間不夠用,那么JVM就會(huì)拋 出此異常,這種情況一般是死遞歸造成的。 1.2 堆 Java中堆是由所有的線程共享的一塊內(nèi)存區(qū)域,堆用來(lái)保存各種JAVA對(duì)象,比如數(shù)組,線程對(duì)象等。 1.2.1 Generation JVM堆一般又可以分為以下三部分:  ◆ Perm Perm代主要保存class,method,filed對(duì)象,這部門的空間一般不會(huì)溢出,除非一次性加載了很多的類,不過(guò)在涉及到熱部署的應(yīng)用服 務(wù)器的時(shí)候,有時(shí)候會(huì)遇到j(luò)ava.lang.OutOfMemoryError : PermGen space 的錯(cuò)誤,造成這個(gè)錯(cuò)誤的很大原因就有可能是每次都重新部署,但是重新部署后,類的class沒(méi)有被卸載掉,這樣就造成了大量的class對(duì)象保存在了 perm中,這種情況下,一般重新啟動(dòng)應(yīng)用服務(wù)器可以解決問(wèn)題。 ◆ Tenured Tenured區(qū)主要保存生命周期長(zhǎng)的對(duì)象,一般是一些老的對(duì)象,當(dāng)一些對(duì)象在Young復(fù)制轉(zhuǎn)移一定的次數(shù)以后,對(duì)象就會(huì)被轉(zhuǎn)移到Tenured區(qū),一般如果系統(tǒng)中用了application級(jí)別的緩存,緩存中的對(duì)象往往會(huì)被轉(zhuǎn)移到這一區(qū)間。 ◆ Young Young區(qū)被劃分為三部分,Eden區(qū)和兩個(gè)大小嚴(yán)格相同的Survivor區(qū),其中Survivor區(qū)間中,某一時(shí)刻只有其中一個(gè)是被使用的, 另外一個(gè)留做垃圾收集時(shí)復(fù)制對(duì)象用,在Young區(qū)間變滿的時(shí)候,minor GC就會(huì)將存活的對(duì)象移到空閑的Survivor區(qū)間中,根據(jù)JVM的策略,在經(jīng)過(guò)幾次垃圾收集后,任然存活于Survivor的對(duì)象將被移動(dòng)到 Tenured區(qū)間。 1.2.2 Sizing the Generations JVM提供了相應(yīng)的參數(shù)來(lái)對(duì)內(nèi)存大小進(jìn)行配置。正如上面描述,JVM中堆被分為了3個(gè)大的區(qū)間,同時(shí)JVM也提供了一些選項(xiàng)對(duì)Young,Tenured的大小進(jìn)行控制。  ◆ Total Heap -Xms :指定了JVM初始啟動(dòng)以后初始化內(nèi)存 -Xmx:指定JVM堆得最大內(nèi)存,在JVM啟動(dòng)以后,會(huì)分配-Xmx參數(shù)指定大小的內(nèi)存給JVM,但是不一定全部使用,JVM會(huì)根據(jù)-Xms參數(shù)來(lái)調(diào)節(jié)真正用于JVM的內(nèi)存 -Xmx -Xms之差就是三個(gè)Virtual空間的大小 ◆ Young Generation -XX:NewRatio=8意味著tenured 和 young的比值8:1,這樣eden+2*survivor=1/9 堆內(nèi)存 -XX:SurvivorRatio=32意味著eden和一個(gè)survivor的比值是32:1,這樣一個(gè)Survivor就占Young區(qū)的1/34. -Xmn 參數(shù)設(shè)置了年輕代的大小 ◆ Perm Generation -XX:PermSize=16M -XX:MaxPermSize=64M Thread Stack -XX:Xss=128K 1.3 堆棧分離的好處 呵呵,其它的先不說(shuō)了,就來(lái)說(shuō)說(shuō)面向?qū)ο蟮脑O(shè)計(jì)吧,當(dāng)然除了面向?qū)ο蟮脑O(shè)計(jì)帶來(lái)的維護(hù)性,復(fù)用性和擴(kuò)展性方面的好處外,我們看看面向?qū)ο笕绾吻擅畹?利用了堆棧分離。如果從JAVA內(nèi)存模型的角度去理解面向?qū)ο蟮脑O(shè)計(jì),我們就會(huì)發(fā)現(xiàn)對(duì)象它完美的表示了堆和棧,對(duì)象的數(shù)據(jù)放在堆中,而我們編寫的那些方法 一般都是運(yùn)行在棧中,因此面向?qū)ο蟮脑O(shè)計(jì)是一種非常完美的設(shè)計(jì)方式,它完美的統(tǒng)一了數(shù)據(jù)存儲(chǔ)和運(yùn)行。
轉(zhuǎn)自:http://hi.baidu.com/fecasmoy123/blog/item/7f91fd8da34ac918b31bbabf.html
servlet 2.3 新增功能:
2000年10月份出來(lái)
Servlet API 2.3中最重大的改變是增加了filters
servlet 2.4 新增功能:
2003年11月份出來(lái)
1、web.xml DTD改用了XML Schema;
Servlet 2.3之前的版本使用DTD作為部署描述文件的定義,其web.xml的格式為如下所示:
<?xml version="1.0" encoding="IS0-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//sunMicrosystems,Inc.//DTD WebApplication 2.3f//EN"
"
<web-app>
.......
</web-app>
Servlet 2.4版首次使用XML Schema定義作為部署描述文件,這樣Web容器更容易校驗(yàn)web.xml語(yǔ)法。同時(shí)XML Schema提供了更好的擴(kuò)充性,其web.xml中的格式如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="
xmlns:workflow="
xmins:xsi="
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
.........
</web-app>
注意: 改為Schema后主要加強(qiáng)了兩項(xiàng)功能:
(1) 元素不依照順序設(shè)定
(2) 更強(qiáng)大的驗(yàn)證機(jī)制
主要體現(xiàn)在:
a.檢查元素的值是否為合法的值
b.檢查元素的值是否為合法的文字字符或者數(shù)字字符
c.檢查Servlet,Filter,EJB-ref等等元素的名稱是否唯一
2.新增Filter四種設(shè)定:REQUEST、FORWARD、INCLUDE和ERROR。
3.新增Request Listener、Event和Request Attribute Listener、Enent。
4.取消SingleThreadModel接口。當(dāng)Servlet實(shí)現(xiàn)SingleThreadModel接口時(shí),它能確保同時(shí)間內(nèi),只能有一個(gè)thread執(zhí)行此Servlet。
5.<welcome-file-list>可以為Servlet。
6.ServletRequest接口新增一些方法。
public String getLocalName()
public String getLocalAddr()
public int getLocalPort()
public int getRemotePort()
Servlet 2.5的新特征
2005年9月發(fā)布Servlet 2.5
Servlet2.5一些變化的介紹:
1) 基于最新的J2SE 5.0開(kāi)發(fā)的。
2) 支持annotations 。
3) web.xml中的幾處配置更加方便。
4) 去除了少數(shù)的限制。
5) 優(yōu)化了一些實(shí)例
servlet的各個(gè)版本對(duì)監(jiān)聽(tīng)器的變化有:
(1)servlet2.2和jsp1.1
新增Listener:HttpSessionBindingListener
新增Event: HttpSessionBindingEvent
(2)servlet2.3和jsp1.2
新增Listener:ServletContextListener,ServletContextAttributeListener
,HttpSessionListener,HttpSessionActivationListener,HttpSessionAttributeListener
新增Event: ServletContextEvent,ServletContextAttributeEvent,HttpSessionEvent
(3)servlet2.4和jsp2.0
新增Listener:ServletRequestListener,ServletRequestAttribureListener
新增Event: ServletRequestEvent,ServletRequestAttributeEvent
摘要: PostgreSQL與MySQL數(shù)據(jù)庫(kù)的比較 閱讀全文
Two Simple Rules for HTTP Caching
In practice, you only need two settings to optimize caching:
- Don’t cache HTML
- Cache everything else forever
“Wooah…hang on!”, we hear you say. “Cache all my scripts and images forever?“
Yes, that’s right. You don’t need anything else in between. Caching
indefinitely is fine as long as you don’t allow your HTML to be cached.
“But what about if I need to issue code patches to my JavaScript? I can’t allowbrowsers to hold on to all my images either. I often need to update those as well.”
Simple – just change the URL of the item in your HTML and it will bypass the existing entry in the cache.
In practice, caching ‘forever’ typically means setting an Expires header value of Sun, 17-Jan-2038 19:14:07 GMT
since that’s the maximum value supported by the 32 bit Unix time/date
format. If you’re using IIS6 you’ll find that the UI won’t allow
anything beyond 31-Dec-2035. The advantage of setting long expiry dates
is that the content can be read from the local browser cache whenever
the user revisits the web page or goes to another page that uses the
same images, script or CSS files.
You’ll see long expiry dates like this if you look at a Google web
page with HttpWatch. For example, here are the response headers used for
the main Google logo on the home page:

If Google needs to change the logo for a special occasion like
Halloween they just change the name of the file in the page’s HTML to
something like halloween2007.gif.
The diagram below shows how a JavaScript file is loaded into the browser cache on the first visit to a web page:

On any subsequent visits the browser only has to fetch the page’s HTML:

The JavaScript file can be read directly from the browser cache on
the user’s hard disk. This avoids a network round trip and is typically
100 to 1000 times faster than downloading the file over a broadband
connection.
The key to this caching scheme is to keep tight control over your
HTML as it holds the references to everything else on your web site. One
way to do this is to ensure that your pages have a Cache-Control: no-cache header. This will prevent any caching of the HTML and will ensure the browser requests the page’s HTML every time.
If you do this, you can update any content on the page just by
changing the URL that refers to it in the HTML. The old version will
still be in the browser’s cache, but the updated version will be
downloaded because of the modified URL.
For instance, if you had a file called topMenu.js and you fixed some
bugs in it, you might rename the file topMenu-v2.js to force it to be
downloaded:

Now this is all very well, but whenever there’s a discussion of
longer expiration times, the marketing people get very twitchy and
concerned that they won’t be able to re-brand a site if stylesheets and
images are cached for long periods of time.
In fact, choosing an expiration time of anything other than zero or
infinite is inherently uncertain. The only way to know exactly when you
can release a new version to all users simultaneously is to choose a
specific time of day for your cache expiry; say midnight. It’s better to
set indefinite caching on all your page-linked items so that you get
the maximum amount of caching, and then force updates as required.
Now, by this point, you might have the marketing types on board but
you’ll be losing the developers. The developers by now are seeing all
the extra work involved in changing the filenames of all their CSS,
javascript and images both in their source controlled projects and in
their deployment scripts.
So here’s the icing on the cake; you don’t actually need to
change the filename, just the URL. A simple way to do this is to append a
query string parameter onto the end of the existing URL when the
resource has changed.
Here’s the previous example that updated a JavaScript file. The
difference this time is that it uses a query string parameter ‘v2′ to
bypass the existing cache entry:

The web server will simply ignore the query string parameter unless you have chosen to do anything with it programmatically.
There’s one final optimization you can make. The Cache-Control: no-cache
response header works well for dynamic pages as it ensures that pages
will always be refreshed from the server; even when pressing the Back
button. However, for HTML that changes less frequently it is better to
use the Last-Modified header instead. This will avoid a
complete download of the page’s HTML, if it has not changed since it was
last cached by the browser.
The Last-Modified header is added automatically by IIS
for static HTML files and can be added programmatically in dynamic pages
(e.g. ASPX and PHP). When this header is present, the browser will
revalidate the local, cached copy of an HTML page in each new browser
session. If the page is unchanged the web server returns a 304 Not Modified response indicating the browser can use the cached version of the page.
So to summarize:
- Don’t cache HTML
- Use
Cache-Control: no-cache for dynamic HTML pages
- Use the
Last-Modified header with the current file time for static HTML
- Cache everything else forever
- For all other file types set an
Expires header to the maximum future date your web server will allow
- Modify URLs by appending a query string in your HTML to any page element you wish to ‘expire’ immediately.
當(dāng)你建立好一個(gè)WEB服務(wù)后,通常有兩個(gè)類型的緩存需要配置:
- 設(shè)置網(wǎng)站有更新的時(shí)候html資源馬上過(guò)期,以便正在瀏覽的用戶可以很快地得到更新.
- 設(shè)置所有其它資源(例如圖片,CSS,javascript腳本)在一定時(shí)間后過(guò)期.
這個(gè)緩存方案涵蓋Two Simple Rules for HTTP Caching文章中提到關(guān)于如何處理更新的一些思想.
現(xiàn)在HttpWatch 6.0支持Firefox了,我們想探討一下Firefox在處理緩存上與IE有些什么不同.設(shè)置較長(zhǎng)過(guò)期時(shí)間的使用方式(上面第二條)仍可以直接用于Firefox,但配置1在兩者之間還是存在細(xì)微差別的.
在之前的文章 中,我們把第一條劃分為:
- 某些時(shí)候動(dòng)態(tài)HTML頁(yè)面需要即時(shí)從服務(wù)器更新以備隨時(shí)顯示-甚至是使用后退按鈕的時(shí)候.例如,顯示銀行帳號(hào)的狀態(tài)或在線訂單.
- 靜態(tài)HTML頁(yè)面,比如聯(lián)系,FAQs或者站點(diǎn)地圖等頁(yè)面,如果它們?cè)O(shè)置了
Last-Modified響應(yīng)頭 ,允許瀏覽器在需要的時(shí)候重新校驗(yàn),就可以利用到緩存.
一:火狐機(jī)制
本文剩下部分探討了Firefox中影響HTML頁(yè)面緩存的兩個(gè)重要不同點(diǎn).
1. 使用no-cache防止Firefox緩存無(wú)效
你可以簡(jiǎn)單地設(shè)置如下的響應(yīng)頭預(yù)防IE緩存任何東西:
Cache-Control: no-cache
使用了這個(gè)響應(yīng)頭的頁(yè)面不會(huì)保存在緩存里,IE總會(huì)重新從服務(wù)器加載;即使你使用后退按鈕.下面這個(gè)例子使用HttpWatch監(jiān)聽(tīng)一個(gè)網(wǎng)上商店,當(dāng)我們?cè)谔峤挥唵伪韱魏簏c(diǎn)擊后退按鈕,結(jié)果如下圖:

然而,這個(gè)響應(yīng)頭卻不能防止Firefox的緩存.這意味著,Firefox在正常訪問(wèn)的情況下,將一直使用緩存的頁(yè)面,直到它發(fā)送GET請(qǐng)求重新檢驗(yàn).并且,如果是通過(guò)后退按鈕訪問(wèn)頁(yè)面,Firefox不會(huì)再次訪問(wèn)服務(wù)器,而是簡(jiǎn)單直接地從緩存加載.
那怎樣才能關(guān)掉Firefox中的緩存呢? 答案很簡(jiǎn)單,關(guān)不了. 因?yàn)镕irefox依靠緩存中的副本為"文件->另存為","查看源代碼"這樣的操作服務(wù).但是,你可以控制頁(yè)面緩存到哪里及那些緩存條目可以用于顯示.
下面響應(yīng)頭在Firefox中可以防止持久化的緩存,強(qiáng)制頁(yè)面被緩存到內(nèi)存中:
Cache-Control:no-store
這個(gè)頭也可以防止使用后退按鈕時(shí)訪問(wèn)了緩存頁(yè)面,它將觸發(fā)一個(gè)HTTP GET請(qǐng)求.
這兩個(gè)響應(yīng)頭的值組合使用可以在IE與Firefox得到期待的結(jié)果:
Cache-Control: no-cache, no-store
如下HttpWatch響應(yīng)頭標(biāo)簽所示:

2. 如果沒(méi)有設(shè)置過(guò)期時(shí)間Firefox會(huì)為你設(shè)置一個(gè)
當(dāng)IE遇到?jīng)]有Expires頭的http響應(yīng)時(shí),它就認(rèn)為永遠(yuǎn)不能自動(dòng)使用緩存條目,直到它重新從服務(wù)校驗(yàn).由于IE的臨時(shí)文件的一個(gè)設(shè)置項(xiàng)"檢查所在網(wǎng)頁(yè)的較新版本"默認(rèn)為"自動(dòng)",所以通常都是一個(gè)會(huì)話做一次.
這就為控制靜態(tài)的html內(nèi)容的緩存提供了一個(gè)合理的方式.用戶新打開(kāi)的IE會(huì)得到html的最新版本,而緩存的版本就在關(guān)閉IE前會(huì)一直被使用.
Firefox處理缺失Expires頭的方式不同.如果影響中有Last-Modified頭它就會(huì)使用HTTP 1.1規(guī)范RFC2616中指定的一個(gè)嘗試性的過(guò)期值:
(引用規(guī)范:)
并且,如果響應(yīng)中有Last-Modified時(shí)間值,嘗試性的過(guò)期值不能超過(guò)這個(gè)值到現(xiàn)在時(shí)間間隔的一個(gè)比率,一般設(shè)置這個(gè)比率為10%.
計(jì)算方式如下:(默認(rèn)過(guò)期時(shí)間)
過(guò)期時(shí)間 = 現(xiàn)在時(shí)間 + 0.1 * (Last-Modified到現(xiàn)在的時(shí)間差)
例如,如果你的靜態(tài)HTML文件上次修改時(shí)間是100天前,那過(guò)期時(shí)間就是10天之后.下面的示例是一個(gè)沒(méi)有Expires頭頁(yè)面的HttpWatch緩存標(biāo)簽:

Firefox自動(dòng)設(shè)置了過(guò)期時(shí)間為8天后,因?yàn)檫@個(gè)頁(yè)面大概80天沒(méi)有被修改過(guò)了.
這意味著,為了保持控制好你的HTML頁(yè)面,正如我們?cè)?Two Simple Rules for HTTP Caching 文章中討論過(guò)的,你最好為你的靜態(tài)資源如HTML,圖片,CSS文件等,在你的WEB服務(wù)器設(shè)置一個(gè)合適的Expires值.
結(jié)論
為了確保IE與Firefox的緩存行為一致,你應(yīng)該:
- 總是指定一個(gè)Expires頭. 一般設(shè)置-1使用html頁(yè)面能即時(shí)刷新或者對(duì)其它如圖片,CSS,javascript腳本資源設(shè)置一個(gè)特定的過(guò)期時(shí)間
- 如果你要強(qiáng)制頁(yè)面刷新,甚至是點(diǎn)擊后臺(tái)按鈕的時(shí)候,那就設(shè)置 Cache-Control: no-cache, no-store
----------------------------------
二:IE機(jī)制

1:每次都會(huì)到服務(wù)器檢查最后一次修改時(shí)間是否變化 無(wú)變化返回304
2:IE啟動(dòng)時(shí)檢查
3:自動(dòng)檢查
3.1:根據(jù)IE的默認(rèn)在瀏覽器不關(guān)閉時(shí),再次訪問(wèn)同一鏈接都讀本地cache
3.2:IE重啟時(shí)候會(huì)根據(jù)服務(wù)器設(shè)置的expire值去判斷是否去服務(wù)器請(qǐng)求判斷,無(wú)變化返回304
4:第一次下載后從不檢查
最好的機(jī)制是 3 。根據(jù)服務(wù)器的設(shè)定機(jī)制走,會(huì)提高訪問(wèn)速度。
提高訪問(wèn)速度,最好的辦法就是減少TCP通信。
-------------------------
三:服務(wù)器的設(shè)置
參考: http://hi.baidu.com/%C8%FD%BE%D6%CE%AA%B6%FE/blog/item/30dae1325363ed92a8018e5c.html
-------------------------
四:服務(wù)端設(shè)置和客戶端的關(guān)聯(lián)
服務(wù)器的設(shè)置告知瀏覽器怎么做緩存,緩存時(shí)間是多長(zhǎng)。客戶端同時(shí)也可以是CDN。
-------------------------
五:工具類分析
IE httpwatch ,FF httpfox YSlow firebug
在Java5.0中對(duì)于RMI的使用發(fā)生了一些改變, 使得RMI的使用更加容易方便.
一. 定義RMI接口, 這一步最簡(jiǎn)單, 與以前相比, 也沒(méi)有變化.
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Payment extends Remote {
public double calculatePayment(double principal, double annualRate, int terms)
throws RemoteException;
}
注意點(diǎn):
1. 必須繼承java.rmi.Remote
2. 接口中的方法必須拋出java.rmi.RemoteException異常
二. 定義遠(yuǎn)程類實(shí)現(xiàn)接口
與以前相比, 也沒(méi)有發(fā)生變化
import java.rmi.RemoteException;
public class PaymentImpl implements Payment {
public double calculatePayment(double principal, double annRate, int years)
throws RemoteException {
double monthlyInt = annRate / 12;
double monthlyPayment = (principal * monthlyInt)
/ (1 - Math.pow(1/ (1 + monthlyInt), years * 12));
return format(monthlyPayment, 2);
}
public double format(double amount, int places) {
double temp = amount;
temp = temp * Math.pow(10, places);
temp = Math.round(temp);
temp = temp/Math.pow(10, places);
return temp;
}
}
注意點(diǎn):
1. 遠(yuǎn)程對(duì)象必須實(shí)現(xiàn)第一步定義的接口, 在這里是Payment接口.
2. 遠(yuǎn)程對(duì)象中可以定義接口中不包括的方法, 比如: format, 但是這些方法不能遠(yuǎn)程使用.
3. 實(shí)現(xiàn)遠(yuǎn)程方法時(shí), 可以不拋出java.rmi.RemoteException異常
三. 定義遠(yuǎn)程服務(wù)
此處與以前相比, 發(fā)生了一些變化, 需要注意.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server {
public Server() {}
public static void main(String args[]) {
try {
PaymentImpl robj = new PaymentImpl();
Payment stub = (Payment) UnicastRemoteObject.exportObject(robj, 0);
Registry registry = LocateRegistry.getRegistry();
registry.bind("Mortgage", stub);
System.out.println("Mortgage Server is ready to listen");
} catch (Exception e) {
System.err.println("Server exception thrown: " + e.toString());
e.printStackTrace();
}
}
}
而傳統(tǒng)的做法是這樣的:
public class Server extends java.rmi.server.UnicastRemoteObject implements aRemoteInterface{
public Server(int port) {
super(port);
}
....
Naming.bind(uniqueName, this);
....
}
請(qǐng)自行進(jìn)行比較.
四. 定義客戶端類
相應(yīng)地, 這部分也有了變化.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private static Payment stub = null;
private Client() {}
public static void main(String[] args) {
double payment, principal = 80000;
double annualInterest = .065;
int years = 15;
try {
Registry reg = LocateRegistry.getRegistry("localhost");
stub = (Payment) reg.lookup("Mortgage");
} catch (Exception e) {
System.err.println("Client exception thrown: " + e.toString());
e.printStackTrace();
}
if (args.length == 3) {
try {
principal = Double.parseDouble(args[0]);
annualInterest = Double.parseDouble(args[1]);
years = Integer.parseInt(args[2]);
}
catch (Exception e) {
System.out.println("Wrong input " + e.getMessage() );
System.exit(0);
}
print(principal, annualInterest, years);
} else {
System.out.println("Usage: java Client principal annualInterest years ");
System.out.println("\nFor example: java Client 80000 .065 15 ");
System.out.println("\nYou will get the output like the following: \n");
print(principal, annualInterest, years);
System.exit(0);
}
}
public static void print(double pr, double annRate, int years){
double mpayment = 0;
try {
mpayment = stub.calculatePayment(pr, annRate, years);
}catch(Exception e) {
System.out.println("Remote method exception thrown: " + e.getMessage());
}
System.out.println("The principal is $" + (int)pr);
System.out.println("The annual interest rate is " + annRate*100 +"%");
System.out.println("The term is " + years + " years");
System.out.println("Your monthly payment is $" + mpayment);
}
}
注意點(diǎn): lookup服務(wù)的方法與以前是不同的
五. 測(cè)試使用
現(xiàn)在我們有了4個(gè)類
Payment.java-- a remote interface
PaymentImpl.java-- a remote object class
Server.java-- an RMI server
Client.java-- an RMI client
1. 我們把它們都放到一個(gè)目錄下, 比如: 以 c:\myrmi
2. 使用javac編譯這4個(gè)類. javac -d . Payment.java PaymentImpl.java Server.java Client.java
3. 注意: Java5.0的新特性, 現(xiàn)在在不需要在啟動(dòng)服務(wù)之前生成Stub了, stub現(xiàn)在自動(dòng)生成.
而在Java5.0以前是需要用rmic手工生成的.
4. start rmiregistry, 出現(xiàn)一個(gè)窗口, 不要關(guān)閉.
5. start java Server, 出現(xiàn)一個(gè)窗口, 顯示 Mortgage server is ready to listen...
6. 啟動(dòng)客戶端java Client, 如果出現(xiàn)
The principal is $150000
The annual interest rate is 6.0%
The term is 15 years
Your monthly payment is $1265.79
那就大功告成了.
注意點(diǎn): 安全策略機(jī)制進(jìn)行了弱化, 不再需要指定策略文件 授這個(gè)權(quán),那個(gè)權(quán)的了.
我們都知道,IP是由四段數(shù)字組成,在此,我們先來(lái)了解一下3類常用的IP
A類IP段 0.0.0.0 到127.255.255.255
B類IP段 128.0.0.0 到191.255.255.255
C類IP段 192.0.0.0 到223.255.255.255
XP默認(rèn)分配的子網(wǎng)掩碼每段只有255或0
A類的默認(rèn)子網(wǎng)掩碼 255.0.0.0 一個(gè)子網(wǎng)最多可以容納1677萬(wàn)多臺(tái)電腦
B類的默認(rèn)子網(wǎng)掩碼 255.255.0.0 一個(gè)子網(wǎng)最多可以容納6萬(wàn)臺(tái)電腦
C類的默認(rèn)子網(wǎng)掩碼 255.255.255.0 一個(gè)子網(wǎng)最多可以容納254臺(tái)電腦
我以前認(rèn)為,要想把一些電腦搞在同一網(wǎng)段,只要IP的前三段一樣就可以了,今天,我才知道我錯(cuò)了。如果照我這說(shuō)的話,一個(gè)子網(wǎng)就只能容納254臺(tái)電腦?真是有點(diǎn)笑話。我們來(lái)說(shuō)詳細(xì)看看吧。
要想在同一網(wǎng)段,只要網(wǎng)絡(luò)標(biāo)識(shí)相同就可以了,那要怎么看網(wǎng)絡(luò)標(biāo)識(shí)呢?首先要做的是把每段的IP轉(zhuǎn)換為二進(jìn)制。(有人說(shuō),我不會(huì)轉(zhuǎn)換耶,沒(méi)關(guān)系,我們用
Windows自帶計(jì)算器就行。打開(kāi)計(jì)算器,點(diǎn)查看>科學(xué)型,輸入十進(jìn)制的數(shù)字,再點(diǎn)一下“二進(jìn)制”這個(gè)單選點(diǎn),就可以切換至二進(jìn)制了。)
把子網(wǎng)掩碼切換至二進(jìn)制,我們會(huì)發(fā)現(xiàn),所有的子網(wǎng)掩碼是由一串[red]連續(xù)[/red]的1和一串[red]連續(xù)[/red]的0組成的(一共4段,每段8位,一共32位數(shù))。
255.0.0.0 11111111.00000000.00000000.00000000
255.255.0.0 11111111.11111111.00000000.00000000
255.255.255.0 11111111.11111111.11111111.00000000
這是A/B/C三類默認(rèn)子網(wǎng)掩碼的二進(jìn)制形式,其實(shí),還有好多種子網(wǎng)掩碼,只要是一串連續(xù)的1和一串連續(xù)的0就可以了(每段都是8位)。如
11111111.11111111.11111000.00000000,這也是一段合法的子網(wǎng)掩碼。子網(wǎng)掩碼決定的是一個(gè)子網(wǎng)的計(jì)算機(jī)數(shù)目,計(jì)算機(jī)公
式是2的m次方,其中,我們可以把m看到是后面的多少顆0。如255.255.255.0轉(zhuǎn)換成二進(jìn)制,那就是
11111111.11111111.11111111.00000000,后面有8顆0,那m就是8,255.255.255.0這個(gè)子網(wǎng)掩碼可以容納
2的8次方(臺(tái))電腦,也就是256臺(tái),但是有兩個(gè)IP是不能用的,那就是最后一段不能為0和255,減去這兩臺(tái),就是254臺(tái)。我們?cè)賮?lái)做一個(gè)。
255.255.248.0這個(gè)子網(wǎng)掩碼可以最多容納多少臺(tái)電腦?
計(jì)算方法:
把將其轉(zhuǎn)換為二進(jìn)制的四段數(shù)字(每段要是8位,如果是0,可以寫成8個(gè)0,也就是00000000)
11111111.1111111.11111000.00000000
然后,數(shù)數(shù)后面有幾顆0,一共是有11顆,那就是2的11次方,等于2048,這個(gè)子網(wǎng)掩碼最多可以容納2048臺(tái)電腦。
一個(gè)子網(wǎng)最多可以容納多少臺(tái)電腦你會(huì)算了吧,下面我們來(lái)個(gè)逆向算法的題。
一個(gè)公司有530臺(tái)電腦,組成一個(gè)對(duì)等局域網(wǎng),子網(wǎng)掩碼設(shè)多少最合適?
首先,無(wú)疑,530臺(tái)電腦用B類IP最合適(A類不用說(shuō)了,太多,C類又不夠,肯定是B類),但是B類默認(rèn)的子網(wǎng)掩碼是255.255.0.0,可以容納6萬(wàn)臺(tái)電腦,顯然不太合適,那子網(wǎng)掩碼設(shè)多少合適呢?我們先來(lái)列個(gè)公式。
2的m次方=560
首先,我們確定2一定是大于8次方的,因?yàn)槲覀冎?的8次方是256,也就是C類IP的最大容納電腦的數(shù)目,我們從9次方一個(gè)一個(gè)試2的9次方是
512,不到560,2的10次方是1024,看來(lái)2的10次方最合適了。子網(wǎng)掩碼一共由32位組成,已確定后面10位是0了,那前面的22位就是1,最
合適的子網(wǎng)掩碼就是:11111111.11111111.11111100.00000000,轉(zhuǎn)換成10進(jìn)制,那就是255.255.252.0。
分配和計(jì)算子網(wǎng)掩碼你會(huì)了吧,下面,我們來(lái)看看IP地址的網(wǎng)段。
相信好多人都和偶一樣,認(rèn)為IP只要前三段相同,就是在同一網(wǎng)段了,其實(shí),不是這樣的,同樣,我樣把IP的每一段轉(zhuǎn)換為一個(gè)二進(jìn)制數(shù),這里就拿IP:192.168.0.1,子網(wǎng)掩碼:255.255.255.0做實(shí)驗(yàn)吧。
192.168.0.1
11000000.10101000.00000000.00000001
(這里說(shuō)明一下,和子網(wǎng)掩碼一樣,每段8位,不足8位的,前面加0補(bǔ)齊。)
IP 11000000.10101000.00000000.00000001
子網(wǎng)掩碼 11111111.11111111.11111111.00000000
在這里,向大家說(shuō)一下到底怎么樣才算同一網(wǎng)段。
要想在同一網(wǎng)段,必需做到網(wǎng)絡(luò)標(biāo)識(shí)相同,那網(wǎng)絡(luò)標(biāo)識(shí)怎么算呢?各類IP的網(wǎng)絡(luò)標(biāo)識(shí)算法都是不一樣的。A類的,只算第一段。B類,只算第一、二段。C類,算第一、二、三段。
算法只要把IP和子網(wǎng)掩碼的每位數(shù)AND就可以了。
AND方法:0和1=0 0和0=0 1和1=1
如:And 192.168.0.1,255.255.255.0,先轉(zhuǎn)換為二進(jìn)制,然后AND每一位
IP 11000000.10101000.00000000.00000001
子網(wǎng)掩碼 11111111.11111111.11111111.00000000
得出AND結(jié)果 11000000.10101000.00000000.00000000
轉(zhuǎn)換為十進(jìn)制192.168.0.0,這就是網(wǎng)絡(luò)標(biāo)識(shí),
再將子網(wǎng)掩碼反取,也就是00000000.00000000.00000000.11111111,與IP AND
得出結(jié)果00000000.00000000.00000000.00000001,轉(zhuǎn)換為10進(jìn)制,即0.0.0.1,
這0.0.0.1就是主機(jī)標(biāo)識(shí)。要想在同一網(wǎng)段,必需做到網(wǎng)絡(luò)標(biāo)識(shí)一樣。
我們?cè)賮?lái)看看這個(gè)改為默認(rèn)子網(wǎng)掩碼的B類IP
如IP:188.188.0.111,188.188.5.222,子網(wǎng)掩碼都設(shè)為255.255.254.0,在同一網(wǎng)段嗎?
先將這些轉(zhuǎn)換成二進(jìn)制
188.188.0.111 10111100.10111100.00000000.01101111
188.188.5.222 10111100.10111100.00000101.11011010
255.255.254.0 11111111.11111111.11111110.00000000
分別AND,得
10111100.10111100.00000000.00000000
10111100.10111100.00000100.00000000
網(wǎng)絡(luò)標(biāo)識(shí)不一樣,即不在同一網(wǎng)段。
判斷是不是在同一網(wǎng)段,你會(huì)了吧,下面,我們來(lái)點(diǎn)實(shí)際的。
一個(gè)公司有530臺(tái)電腦,組成一個(gè)對(duì)等局域網(wǎng),子網(wǎng)掩碼和IP設(shè)多少最合適?
子網(wǎng)掩碼不說(shuō)了,前面算出結(jié)果來(lái)了11111111.11111111.11111100.00000000,也就是255.255.252.0
我們現(xiàn)在要確定的是IP如何分配,首先,選一個(gè)B類IP段,這里就選188.188.x.x吧
這樣,IP的前兩段確定的,關(guān)鍵是要確定第三段,只要網(wǎng)絡(luò)標(biāo)識(shí)相同就可以了。我們先來(lái)確定網(wǎng)絡(luò)號(hào)。(我們把子網(wǎng)掩碼中的1和IP中的?對(duì)就起來(lái),0和*對(duì)應(yīng)起來(lái),如下:)
255.255.252.0 11111111.11111111.11111100.00000000
188.188.x.x 10111100.10111100.??????**.********
網(wǎng)絡(luò)標(biāo)識(shí) 10111100.10111100.??????00.00000000
由此可知,?處隨便填(只能用0和1填,不一定全是0和1),我們就用全填0吧,*處隨便,這樣呢,我們的IP就是
10111100.10111100.000000**.********,一共有530臺(tái)電腦,IP的最后一段1~254可以分給254臺(tái)計(jì)算
機(jī),530/254=2.086,采用進(jìn)1法,得整數(shù)3,這樣,我們確定了IP的第三段要分成三個(gè)不同的數(shù)字,也就是說(shuō),把000000**中的**填三
次數(shù)字,只能填1和0,而且每次的數(shù)字都不一樣,至于填什么,就隨我們便了,如00000001,00000010,00000011,轉(zhuǎn)換成二進(jìn)制,分
別是1,2,3,這樣,第三段也確定了,這樣,就可以把IP分成188.188.1.y,188.188.2.y,188.188.3.y,y處隨便填,
只要在1~254范圍之內(nèi),并且這530臺(tái)電腦每臺(tái)和每臺(tái)的IP不一樣,就可以了。
有人也許會(huì)說(shuō),既然算法這么麻煩,干脆用A類IP和A類默認(rèn)子網(wǎng)掩碼得了,偶要告訴你的是,由于A類IP和A類默認(rèn)子網(wǎng)掩碼的主機(jī)數(shù)目過(guò)大,這樣做無(wú)疑是大海撈針,如果同時(shí)局域網(wǎng)訪問(wèn)量過(guò)頻繁、過(guò)大,會(huì)影響效率的,所以,最好設(shè)置符合自己的IP和子網(wǎng)掩碼^_^
摘要: Eclipse 遠(yuǎn)程DEBUG 方法 閱讀全文
部分內(nèi)容參考:http://www.linuxdiyf.com/viewarticle.php?id=78024
在/etc/init.d下面新建一個(gè)文件dbora,內(nèi)容:
#!/bin/sh
su - $ORA_OWNER -c "lsnrctl start"
su - $ORA_OWNER -c "sqlplus /nolog @$ORACLE_HOME/dbstart.sql"
$ORACLE_HOME/dbstart.sql的內(nèi)容:
conn / as sysdba
startup
exit
將dbora改成可執(zhí)行
chmod 777 dbora
將dbstart.sql改成oracle所有
chown oracle /oracle/product/9.2.0/dbstart.sql
然后作一個(gè)連接
ln -s /etc/init.d/dbora /etc/rc2.d/S99dbora
ln -s /etc/init.d/dbora /etc/rc0.d/K10dbora
需要注意的地方是oracle用戶需要正確設(shè)置環(huán)境變量上面的自動(dòng)啟動(dòng)才會(huì)生效,具體如下
su - oracle
vi .profile (設(shè)置oracle的環(huán)境變量文件,這個(gè)文件在oracle用戶的home目錄下)
加入下面變量設(shè)置
ORACLE_OWNER=oracle;export ORACLE_OWNER
ORACLE_HOME=/opt/oracle/product/10.2.0/Db_1;export ORACLE_HOME
ORACLE_SID=orcl;export ORACLE_SID
PATH=$PATH:$ORACLE_HOME/bin
另外如果環(huán)境變量設(shè)置了NLS_LAN變量注意不要設(shè)錯(cuò),否則oracle會(huì)報(bào):ora-12705錯(cuò)誤。
用xmanager遠(yuǎn)程在solaris上安裝oracle10g
1.在oracle下載合適的oracle版本
2.用root登陸
3.設(shè)置oracle賬戶信息
1)groupadd dba
2)useradd -d dba -d /opt/oracle -m -s /bin/sh oracle
3)修改/etc/group文件最后一行:dba::100:oracle
4)設(shè)置/opt/oracle目錄權(quán)限 chmod -R 777 oracle
5)vi /etc/system 在最后一行加入(設(shè)置用戶變量)
set shmsys:shminfo_shmmax=4294967295
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=100
set shmsys:shminfo_shmseg=10
set semsys:seminfo_semmns=2000
set semsys:seminfo_semmsl=1000
set semsys:seminfo_semmni=100
set semsys:seminfo_semopm=100
set semsys:seminfo_semvmx=32767
set unlimit=3000000
6)passwd oracle 設(shè)置oracle用戶密碼
必需要先重啟才能安裝oracle(sytem設(shè)置才能生效)
注意安裝oracle時(shí)需要用oracle用戶安裝
3.設(shè)置display環(huán)境變量
csh
setenv DISPLAY xx.xx.xx.xx:1.0
xx.xx.xx.xx是xmanager所在的ip地址
4.su - oracle 運(yùn)行oracle的./runInstaller開(kāi)始安裝
5.手工設(shè)置oracle環(huán)境變量(我裝的時(shí)候自動(dòng)設(shè)置沒(méi)有生效)
設(shè)置oracle環(huán)境變量,最好可以設(shè)置到.profile中,不用每次重啟麻煩
csh
setenv ORACLE_OWNER oracle
setenv ORACLE_HOME /opt/oracle/product/10.2.0/Db_1
setenv ORACLE_SID orcl
6.手工啟動(dòng)oracle
1)su - oracle
2)sqlplus /nolog
3)/>connect sys/system as sysdba
4)/>startup
5)/>quit
6)./lsnrctl start &
看一下oracle的進(jìn)程
ps -ef | grep oracle
7.solaris 命令部分
設(shè)置環(huán)境變量在/.profile中
#vi .profile
LANG=zh_CN.GBK;export LANG
(必須向上面這樣設(shè)置才能生效)
重啟solais
init 6
查看內(nèi)存
/usr/sbin/swap -s
看進(jìn)程的全路徑
/usr/ucb/ps -auxww | more | grep java
websphere4 配置過(guò)程中遇到的問(wèn)題
1.admin.config 中配置oracle driver 的classpath
-Dws.ext.dirs=
連接不上oracle數(shù)據(jù)庫(kù),報(bào)java.sql.SQLException: ORA-01453: SET TRANSACTION 必須是事務(wù)處理的第一個(gè)語(yǔ)句錯(cuò)誤
[08.06.06 15:50:48:622 CST] 7a4489 EJBEngine X WSVR0062E:未能啟動(dòng) EJB,admin#repository.jar#Node:org.omg.CORBA.portable.UnknownException: minor code: 0 completed: Maybe
[08.06.06 15:50:50:554 CST] 7a4489 EJBEngine I WSVR0037I:正在啟動(dòng) EJB jar:Tasks
[08.06.06 15:50:51:329 CST] 7a4489 Helpers W NMSV0610I: NamingException 從javax.naming.Context 實(shí)現(xiàn)中拋出。詳細(xì)信息請(qǐng)參照:
上下文實(shí)現(xiàn):com.ibm.ejs.ns.jndi.CNContextImpl
上下文方法:lookup
上下文名:domainRoots/UnspecifiedDomainName/legacyRoot
目標(biāo)名:ejsadmin/homes/NodeHome
其它數(shù)據(jù):
異常堆棧跟蹤:javax.naming.NamingException: Error during resolve. Root exception is org.omg.CORBA.portable.UnknownException: minor code: 0 completed: Maybe
使用合適的oracle driver,比如4.0需要用oracle8的class12.jar才行
2.websphere能夠啟動(dòng)后,通過(guò)/bin/adminclient.sh配置應(yīng)用和datasource
1)資源配置jdbc 供應(yīng)商
2)企業(yè)應(yīng)用程序-》右鍵安裝企業(yè)應(yīng)用=》選擇一個(gè)空的ear文件
webspherejvm設(shè)置
adminclient->節(jié)點(diǎn)->avgg1->應(yīng)用服務(wù)器->Default Server
-Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Dclient.encoding.override=UTF-8 -Duser.language=zh -Duser.region=CN
3.解析get/post參數(shù)時(shí)報(bào)錯(cuò):
[08.06.11 14:32:38:143 CST] 24ed99 SRTServletRes W Failed to create a writer with encoding: GB2312. The default encoding will be used.
[08.06.11 14:32:44:328 CST] 24ed99 WebGroup X Servlet Error: : java.lang.IllegalArgumentException
at com.ibm.servlet.engine.webapp.RequestUtils.parseQueryString(RequestUtils.java:256)
通過(guò)反編譯websphere源代碼找到問(wèn)題原因,代碼在WS_HOME/lib/webcontainer.jar中
com.ibm.servlet.engine.srt.STRRequestUtils 裝在converter.properties文件
負(fù)責(zé)獲取編碼
public static String getJvmConverter(String s)
{
String s1 = _jvmProps.getProperty(s);
if(s1 != null)
return s1;
else
return s;
}
STRServletRequest#
public String getReaderEncoding()
{
String s = getCharacterEncoding();
if(s == null)
s = SRTRequestUtils.getEncodingFromLocale(getLocale());
if(s == null)
s = System.getProperty("default.client.encoding");
if(s == null)
s = "ISO-8859-1";
return SRTRequestUtils.getJvmConverter(s);
}
需要配置/properties/converter.properties
GB2312=Cp1386 改為GB2312=Gb2312
通過(guò)Axis webservice客戶端進(jìn)行NTLM認(rèn)證的方法:
axis是通過(guò)httpclient進(jìn)行認(rèn)證的,所以需要先下載httpclient程序,還需要解碼程序commons-codec-1.3.jar
這兩個(gè)接口google一下就行了,Axis的客戶端代碼如下:
1 try
2 {
3 String endpoint = "http://localhost:7001/Test/WebServices.asmx?wsdl";
4 Service service = new Service();
5 Call call = (Call)service.createCall();
6
7 call.setTargetEndpointAddress(new URL(endpoint));
8 call.getMessageContext().setUsername("domain\\user");
9 call.getMessageContext().setPassword("password");
10 call.setClientHandlers(new CommonsHTTPSender(),null);
11
12 call.setOperationName(new QName("Method"));
13 call.addParameter("ParamName1", XMLType.XSD_STRING, ParameterMode.IN );
14 call.setReturnClass(String.class);
15 String ret = (String)call.invoke(new Object[]{new String("aaaa")});
16
17 System.out.println(ret);
18
19 }
20 catch(Exception ex)
21 {
22 ex.printStackTrace();
23 }
通過(guò)監(jiān)聽(tīng)客戶端發(fā)出的http頭信息,可以發(fā)現(xiàn)發(fā)出的認(rèn)證信息如下:
Authorization: NTLM LKJSDLKFJLSDKJFLKSDAAAAAAAAAAAAAAAAAAAAAAA=
服務(wù)器如果采用一般的http認(rèn)證,可以HTTPSender代替CommonHttpSender,通過(guò)他的代碼可以發(fā)現(xiàn):
1 if (userID != null) {
2 StringBuffer tmpBuf = new StringBuffer();
3
4 tmpBuf.append(userID).append(":").append((passwd == null)
5 ? ""
6 : passwd);
7 otherHeaders.append(HTTPConstants.HEADER_AUTHORIZATION)
8 .append(": Basic ")
9 .append(Base64.encode(tmpBuf.toString().getBytes()))
10 .append("\r\n");
11 }
HttpSender直接生成http的Authorization頭信息進(jìn)行設(shè)置,發(fā)出的http header代碼例子:
Authorization: Basic SDKFJLSDKJLlsdlfksjdflksjw232lj
如果需要進(jìn)行webservice的自定義認(rèn)證可以自己實(shí)現(xiàn)axis的handler。
在用dom4j解析xml時(shí)如果報(bào)下面這個(gè)錯(cuò)誤:
org.dom4j.DocumentException: Error on line 1 of document
file:///d:/xxx.xml :
缺少文件根組件。 Nested exception: 缺少文件根組件。
這是因?yàn)閤ml解析器的問(wèn)題,以前一直在web環(huán)境下跑程序,用的是xerces解析器,就不會(huì)報(bào)上面的錯(cuò)誤。
但是獨(dú)立運(yùn)行的程序如果用jdk1.4.2和dom4j1.6.1時(shí)就回有上面的錯(cuò)誤,因?yàn)橛玫氖莏dk自帶的解析器
crimson。
解決辦法就是
用xerces解析器,可以從tomcat中找到xercesImpl.jar和xml-api.jar放到工程中就可以了。
另外在jdk1.5下面也沒(méi)有這個(gè)問(wèn)題,應(yīng)該是jdk1.5自帶的解析器解決了這個(gè)問(wèn)題了。
根本原因是xml含有bom頭導(dǎo)致,就是EF BB BF。
如果報(bào)這個(gè)錯(cuò)誤:Content is not allowed in prolog
使用dom4j1.6.1就ok了。
生活的態(tài)度
好的生活態(tài)度可以使人安定、平靜、充滿信心,反之則使人急躁、郁悶、自卑。
人啊,怎么活都是一輩子,快樂(lè)、幸福、積極的活著才有意義。
努力工作、技術(shù)專家、努力學(xué)習(xí)、珍惜自己的幸福。
websphere升級(jí)jdk的策略是前3個(gè)版本號(hào)必須一致,比如1.3.0這3位必須相同,或者升級(jí)websphere才能升級(jí)更高版本的jdk。
經(jīng)過(guò)這兩天查找的資料發(fā)現(xiàn)從Websphere4升級(jí)到sunjdk1.4.2這條路是行不通的,Websphere4和5似乎ibm已經(jīng)不繼續(xù)支持了,推薦
升級(jí)到更高的版本。
在網(wǎng)上找到一篇:如何升級(jí) WebSphere Application Server v4.01 for Solaris 的 JDK 到 SUN JDK v1.3.1
按照這個(gè)配置嘗試了一下升級(jí),結(jié)果報(bào)錯(cuò)corba讀取數(shù)據(jù)會(huì)有問(wèn)題,可能是序列化的問(wèn)題,錯(cuò)誤如下:
java.rmi.MarshalException: CORBA MARSHAL 0 No; nested exception is:
org.omg.CORBA.MARSHAL: Unable to read value from underlying bridge : null vmcid: 0x0 minor code: 0 completed: No
websphere升級(jí)的策略:http://www-1.ibm.com/support/docview.wss?uid=swg21138332
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|
常用鏈接
留言簿(12)
隨筆分類
隨筆檔案
文章分類
文章檔案
相冊(cè)
收藏夾
好友的blog
技術(shù)網(wǎng)站
搜索
積分與排名
最新評(píng)論

閱讀排行榜
評(píng)論排行榜
|
|