隨筆 - 41  文章 - 7  trackbacks - 0
          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          原文:http://www.rabbitmq.com/memory.html
          RabbitMQ服務(wù)器在啟動(dòng)時(shí)以及abbitmqctl set_vm_memory_high_watermark fraction 執(zhí)行時(shí),會(huì)檢查計(jì)算機(jī)的RAM總大小. 默認(rèn)情況下下, 當(dāng) RabbitMQ server 的使用量超過RAM的40% ,它就會(huì)發(fā)出內(nèi)存警報(bào),并阻塞所有連接. 一旦內(nèi)存警報(bào)清除 (如,服務(wù)器將消息轉(zhuǎn)存于磁盤,或者將消息投遞給clients),服務(wù)又地恢復(fù).
          默認(rèn)內(nèi)存閥值設(shè)置為已安裝RAM的40%. 注意這并不會(huì)阻止RabbitMQ server使用內(nèi)存量超過40%, 它只是為了壓制發(fā)布者. Erlang的垃圾回收器最壞情況下,可使用配置內(nèi)存的2倍(默認(rèn)情況下t, RAMr的80%). 因此強(qiáng)制建議開啟OS swap或page files .
          32位架構(gòu)傾向于每一個(gè)進(jìn)程有2GB的內(nèi)存限制. 64位架構(gòu)的一般實(shí)現(xiàn)(i.e. AMD64 和 Intel EM64T) 只允許每個(gè)進(jìn)程為256TB. 64-位 Windows 限制為8TB. 但是,請(qǐng)注意,即使是64位操作系統(tǒng)下,一個(gè)32位的過程往往只有一個(gè)2GB的最大地址空間。
          配置內(nèi)存閥值
          內(nèi)存閥值可通過編輯configuration file來配置.下面的例子將閥值設(shè)為默認(rèn)值0.4:
          [{rabbit, [{vm_memory_high_watermark, 0.4}]}].
          默認(rèn)值0.4 代表的是已安裝RAM的 40% , 有時(shí)候還更小.如:在 32位平臺(tái)中,如果你安裝有4GB RAM , 4GB 的40% 是 1.6GB, 但是 32-位 Windows 正常情況下限制進(jìn)程為2GB,因此實(shí)際閥值是2GB的40% (即820MB).
          另外, 內(nèi)存閥值也可以設(shè)置為絕對(duì)值. 下面的例子將閥值設(shè)為了1073741824 字節(jié) (1024 MB):
          [{rabbit, [{vm_memory_high_watermark, {absolute, 1073741824}}]}].
          同例, 也可使用內(nèi)存單位:
          [{rabbit, [{vm_memory_high_watermark, {absolute, "1024MiB"}}]}].
          如果絕對(duì)上限大于了安裝的RAM可用的虛擬地址空間, 閥值上限會(huì)略小.
          當(dāng)RabbitMQ服務(wù)器啟動(dòng)時(shí),內(nèi)存限制將追加到RABBITMQ_NODENAME.log 文件中:
          =INFO REPORT==== 29-Oct-2009::15:43:27 === Memory limit set to 2048MB.
          內(nèi)存限制也可以使用rabbitmqctl status命令查詢
          其閥值也可以在broker運(yùn)行時(shí),通過rabbitmqctl set_vm_memory_high_watermark fraction 命令或 rabbitmqctl set_vm_memory_high_watermark absolute memory_limit 命令修改. 內(nèi)存單位也可以在命令中使用. 此命令會(huì)在broker重啟后生效. 當(dāng)執(zhí)行此命令時(shí),內(nèi)存限制可能會(huì)改變熱插拔RAM,而不會(huì)發(fā)生報(bào)警,這是因?yàn)樾枰繑?shù)量的系統(tǒng)RAM.
          禁止所有發(fā)布
          其值為0時(shí),會(huì)立即觸發(fā)報(bào)警并禁用所有發(fā)布 (當(dāng)需要禁用全局發(fā)布時(shí),這可能是有用的); use rabbitmqctl set_vm_memory_high_watermark 0.
          限制的地址空間
          當(dāng)在64位操作系統(tǒng)中運(yùn)行32位 Erlang VM時(shí),(or a 32 bit OS with PAE), 可用地址內(nèi)存是受限制的. 服務(wù)器探測(cè)到后會(huì)記錄像下邊的日志消息:
          =WARNING REPORT==== 19-Dec-2013::11:27:13 === Only 2048MB of 12037MB memory usable due to limited address space. Crashes due to memory exhaustion are possible - see http://www.rabbitmq.com/memory.html#address-space
          內(nèi)存報(bào)警系統(tǒng)是不完美的.雖然停止發(fā)布通常會(huì)防止任何進(jìn)一步的內(nèi)存使用,但可能有其他東西繼續(xù)增加內(nèi)存使用。通常情況下,當(dāng)這種情況發(fā)生時(shí),物理內(nèi)存耗盡,操作系統(tǒng)將開始交換。但是當(dāng)運(yùn)行一個(gè)有限的地址空間,超過限制的運(yùn)行會(huì)導(dǎo)致虛擬機(jī)崩潰。
          因此強(qiáng)制建議在在64位操作系統(tǒng)上運(yùn)行64位的Erlang VM.
          配置分頁閾值
          在broker達(dá)到最高水位阻塞發(fā)布者之前,它會(huì)嘗試將隊(duì)列內(nèi)容分頁輸出到磁盤上來釋放內(nèi)存. 持久化和瞬時(shí)消息都會(huì)分頁輸出 (已經(jīng)在磁盤上的持久化消息會(huì)被趕出內(nèi)存).
          默認(rèn)情況下,在達(dá)最高水位的50%時(shí),就會(huì)發(fā)生這種情況. (即,默認(rèn)最高水位為0.4, 這會(huì)在內(nèi)存使用達(dá)到20%時(shí)就會(huì)發(fā)生). 要修改此值,可修改vm_memory_high_watermark_paging_ratio 配置的0.5默認(rèn)值. 例如:
          [{rabbit, [{vm_memory_high_watermark_paging_ratio, 0.75}, {vm_memory_high_watermark, 0.4}]}].
          上面的配置表示在內(nèi)存使用達(dá)到30%時(shí),就會(huì)啟動(dòng),40%的時(shí)候會(huì)阻塞發(fā)布者.
          也可以將vm_memory_high_watermark_paging_ratio 值設(shè)為大于1.0的值.在這種情況下,隊(duì)列不會(huì)把它的內(nèi)容分頁到磁盤上.如果這引起了內(nèi)存報(bào)警關(guān)閉,那么生產(chǎn)者會(huì)如上面預(yù)期的一樣被阻塞.
          未確認(rèn)的平臺(tái)
          如果RabbitMQ服務(wù)器不能識(shí)別你的系統(tǒng),它將在RABBITMQ_NODENAME.log 文件中追加警告.
          然后它會(huì)假設(shè)安裝了超過了1GB的RAM:
          =WARNING REPORT==== 29-Oct-2009::17:23:44 === Unknown total memory size for your OS {unix,magic_homebrew_os}. Assuming memory size is 1024MB.
          在這種情況下,vm_memory_high_watermark 配置值假設(shè)為1GB RAM. 在 vm_memory_high_watermark 默認(rèn)設(shè)為 0.4的情況下, RabbitMQ的內(nèi)存閥值設(shè)為了410MB, 因此當(dāng)RabbitMQ使用了多于410M內(nèi)存時(shí),它會(huì)阻塞生產(chǎn)者.因此當(dāng)RabbitMQ不能識(shí)別你的平臺(tái)時(shí),如果你實(shí)際有8GB RAM,并且你想讓RabbitMQ內(nèi)存使用量超過3GB阻塞生產(chǎn)者,你可以設(shè)置vm_memory_high_watermark為3.
          推薦RAM 水位設(shè)置,可參考Production Checklist.

          posted @ 2016-07-30 15:05 胡小軍 閱讀(7972) | 評(píng)論 (0)編輯 收藏
          鳥欲高飛先振翅,人求上進(jìn)先讀書。本文是原書的第9章 線程的監(jiān)控及其日常工作中如何分析里的9.3.3節(jié)常見的內(nèi)存溢出的三種情況。
          3. 常見的內(nèi)存溢出的三種情況:
          1)JVM Heap(堆)溢出:java.lang.OutOfMemoryError: Java heap space
          JVM在啟動(dòng)的時(shí)候會(huì)自動(dòng)設(shè)置JVM Heap的值, 可以利用JVM提供的-Xmn -Xms -Xmx等選項(xiàng)可進(jìn)行設(shè)置。Heap的大小是Young Generation 和Tenured Generaion 之和。在JVM中如果98%的時(shí)間是用于GC,且可用的Heap size 不足2%的時(shí)候?qū)伋龃水惓P畔ⅰ?/span>
          解決方法:手動(dòng)設(shè)置JVM Heap(堆)的大小。
          2)PermGen space溢出: java.lang.OutOfMemoryError: PermGen space
          PermGen space的全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域。為什么會(huì)內(nèi)存溢出,這是由于這塊內(nèi)存主要是被JVM存放Class和Meta信息的,Class在被Load的時(shí)候被放入PermGen space區(qū)域,它和存放Instance的Heap區(qū)域不同,sun的 GC不會(huì)在主程序運(yùn)行期對(duì)PermGen space進(jìn)行清理,所以如果你的APP會(huì)載入很多CLASS的話,就很可能出現(xiàn)PermGen space溢出。一般發(fā)生在程序的啟動(dòng)階段。
          解決方法: 通過-XX:PermSize和-XX:MaxPermSize設(shè)置永久代大小即可。
          3)棧溢出: java.lang.StackOverflowError : Thread Stack space
          棧溢出了,JVM依然是采用棧式的虛擬機(jī),這個(gè)和C和Pascal都是一樣的。函數(shù)的調(diào)用過程都體現(xiàn)在堆棧和退棧上了。調(diào)用構(gòu)造函數(shù)的 “層”太多了,以致于把棧區(qū)溢出了。 通常來講,一般棧區(qū)遠(yuǎn)遠(yuǎn)小于堆區(qū)的,因?yàn)楹瘮?shù)調(diào)用過程往往不會(huì)多于上千層,而即便每個(gè)函數(shù)調(diào)用需要 1K的空間(這個(gè)大約相當(dāng)于在一個(gè)C函數(shù)內(nèi)聲明了256個(gè)int類型的變量),那么棧區(qū)也不過是需要1MB的空間。通常棧的大小是1-2MB的。通俗一點(diǎn)講就是單線程的程序需要的內(nèi)存太大了。 通常遞歸也不要遞歸的層次過多,很容易溢出。
          解決方法:1:修改程序。2:通過 -Xss: 來設(shè)置每個(gè)線程的Stack大小即可。
          4. 所以Server容器啟動(dòng)的時(shí)候我們經(jīng)常關(guān)心和設(shè)置JVM的幾個(gè)參數(shù)如下(詳細(xì)的JVM參數(shù)請(qǐng)參看附錄三):
          -Xms:java Heap初始大小, 默認(rèn)是物理內(nèi)存的1/64。
          -Xmx:ava Heap最大值,不可超過物理內(nèi)存。
          -Xmn:young generation的heap大小,一般設(shè)置為Xmx的3、4分之一 。增大年輕代后,將會(huì)減小年老代大小,可以根據(jù)監(jiān)控合理設(shè)置。
          -Xss:每個(gè)線程的Stack大小,而最佳值應(yīng)該是128K,默認(rèn)值好像是512k。
          -XX:PermSize:設(shè)定內(nèi)存的永久保存區(qū)初始大小,缺省值為64M。
          -XX:MaxPermSize:設(shè)定內(nèi)存的永久保存區(qū)最大大小,缺省值為64M。
          -XX:SurvivorRatio:Eden區(qū)與Survivor區(qū)的大小比值,設(shè)置為8,則兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:8,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/10
          -XX:+UseParallelGC:F年輕代使用并發(fā)收集,而年老代仍舊使用串行收集.
          -XX:+UseParNewGC:設(shè)置年輕代為并行收集,JDK5.0以上,JVM會(huì)根據(jù)系統(tǒng)配置自行設(shè)置,所無需再設(shè)置此值。
          -XX:ParallelGCThreads:并行收集器的線程數(shù),值最好配置與處理器數(shù)目相等 同樣適用于CMS。
          -XX:+UseParallelOldGC:年老代垃圾收集方式為并行收集(Parallel Compacting)。
          -XX:MaxGCPauseMillis:每次年輕代垃圾回收的最長(zhǎng)時(shí)間(最大暫停時(shí)間),如果無法滿足此時(shí)間,JVM會(huì)自動(dòng)調(diào)整年輕代大小,以滿足此值。
          -XX:+ScavengeBeforeFullGC:Full GC前調(diào)用YGC,默認(rèn)是true。
          實(shí)例如:JAVA_OPTS=”-Xms4g -Xmx4g -Xmn1024m -XX:PermSize=320M -XX:MaxPermSize=320m -XX:SurvivorRatio=6″
          原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明: 轉(zhuǎn)載自并發(fā)編程網(wǎng) – ifeve.com本文鏈接地址: 《 Java并發(fā)編程從入門到精通》 常見的內(nèi)存溢出的三種情況
          posted @ 2016-07-26 23:05 胡小軍 閱讀(345) | 評(píng)論 (0)編輯 收藏

          原文:http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html

          學(xué)習(xí)如何在你的應(yīng)用程序中集成WebSockets.

          Published April 2013

          對(duì)于許多基于客戶端-服務(wù)器程序來說,老的HTTP 請(qǐng)求-響應(yīng)模型已經(jīng)有它的局限性. 信息必須通過多次請(qǐng)求才能將其從服務(wù)端傳送到客戶端.

          過去許多的黑客使用某些技術(shù)來繞過這個(gè)問題,例如:長(zhǎng)輪詢(long polling)、基于 HTTP 長(zhǎng)連接的服務(wù)器推技術(shù)(Comet)

          然而,基于標(biāo)準(zhǔn)的、雙向的、客戶端和服務(wù)器之間全雙工的信道需求再不斷增加。

          在2011年, IETF發(fā)布了標(biāo)準(zhǔn)WebSocket協(xié)議-RFC 6455. 從那時(shí)起,大多數(shù)Web瀏覽器都實(shí)現(xiàn)了支持WebSocket協(xié)議的客戶端APIs.同時(shí),許多Java 包也開始實(shí)現(xiàn)了WebSocket協(xié)議.

          WebSocket協(xié)議利用HTTP升級(jí)技術(shù)來將HTTP連接升級(jí)到WebSocket. 一旦升級(jí)后,連接就有了在兩個(gè)方向上相互獨(dú)立(全雙式)發(fā)送消息(數(shù)據(jù)楨)的能力. 

          不需要headers 或cookies,這大大降低了所需的帶寬通常,WebSockets來周期性地發(fā)送小消息 (例如,幾個(gè)字節(jié)). 

          額外的headers常常會(huì)使開銷大于有效負(fù)載(payload)。

          JSR 356

          JSR 356, WebSocket的Java API, 明確規(guī)定了API,當(dāng)Java開發(fā)者需要在應(yīng)用程序中集成WebSocket時(shí),就可以使用此API—服務(wù)端和客戶端均可. 每個(gè)聲明兼容JSR 356的WebSocket協(xié)議,都必須實(shí)現(xiàn)這個(gè)API. 

          因此,開發(fā)人員可以自己編寫?yīng)毩⒂诘讓覹ebSocket實(shí)現(xiàn)的WebSocket應(yīng)用。這是一個(gè)巨大的好處,因?yàn)樗梢苑乐构?yīng)商鎖定,并允許更多的選擇、自由的庫、應(yīng)用程序服務(wù)器。

          JSR 356是即將到來的java EE 7標(biāo)準(zhǔn)的一部分,因此,所有與Java EE 7兼容的應(yīng)用服務(wù)器都有JSR 365標(biāo)準(zhǔn)WebSocket的實(shí)現(xiàn).一旦建立,WebSocket客戶端和服務(wù)器節(jié)點(diǎn)已經(jīng)是對(duì)稱的了。客戶端API與服務(wù)器端API的區(qū)別是很小的,JSR 356定義的Java client API只是Java EE7完整API的子集.

          客戶段-服務(wù)器端程序使用WebSockets,通常會(huì)包含一個(gè)服務(wù)器組件和多個(gè)客戶端組件, 如圖1所示:

          Figure 1

          圖1

          在這個(gè)例子中,server application 是通過Java編寫的,WebSocket 協(xié)議細(xì)節(jié)是由包含在Java EE 7容器中JSR 356 實(shí)現(xiàn)來處理的.

          JavaFX 客戶端可依賴任何與JSR 356兼容的客戶端實(shí)現(xiàn)來處理WebSocket協(xié)議問題. 

          其它客戶端(如,iOS 客戶端和HTML5客戶端)可使用其它 (非Java)與RFC6455兼容的實(shí)現(xiàn)來與server application通信.

          編程模型

          JSR 356定義的專家小組,希望支持Java EE開發(fā)人員常用的模式和技術(shù)。因此,JSR 356使用了注釋和注入。

          一般來說,支持兩種編程模型:

          • 注解驅(qū)動(dòng)(annotation-driven). 通過使用注解POJOs, 開發(fā)者可與WebSocket生命周期事件交互.
          • 接口驅(qū)動(dòng)(interface-driven). 開發(fā)者可實(shí)現(xiàn)Endpoint接口和與生命周期交互的方法.

          生命周期事件

          典型的WebSocket 交互生命周期如下:

          • 一端 (客戶端) 通過發(fā)送HTTP握手請(qǐng)求來初始化連接.
          • 其它端(服務(wù)端) 回復(fù)握手響應(yīng).
          • 建立連接.從現(xiàn)在開始,連接是完全對(duì)稱的.
          • 兩端都可發(fā)送和接收消息.
          • 其中一端關(guān)閉連接.

          大部分WebSocket生命周期事件都與Java方法對(duì)應(yīng),不管是 annotation-driven 還是interface-driven.

          Annotation-Driven 方式

          接受WebSocket請(qǐng)求的端點(diǎn)可以是以 @ServerEndpoint 注解的POJO. 

          此注解告知容器,此類應(yīng)該被認(rèn)為是WebSocket端點(diǎn). 

          必須的value 元素指定了WebSocket端點(diǎn)的路徑.

          考慮下面的代碼片斷:

          @ServerEndpoint("/hello")  public class MyEndpoint { } 

          此代碼將會(huì)以相對(duì)路徑hello來發(fā)布一個(gè)端點(diǎn).在后續(xù)方法調(diào)用中,此路徑可攜帶路徑參數(shù),如: /hello/{userid}是一個(gè)有效路徑,在這里{userid} 的值,可在生命周期方法使用@PathParam 注解獲取.

          在GlassFish中,如果你的應(yīng)用程序是用上下文mycontextroot 部署的,且在localhost的8080端口上監(jiān)聽, WebSocket可通過使用ws://localhost:8080/mycontextroot/hello來訪問.

          初始化WebSocket連接的端點(diǎn)可以是以 @ClientEndpoint 注解的POJO.@ClientEndpoint 和 @ServerEndpoint的主要區(qū)別是ClientEndpoint 不接受路徑路值元素,因?yàn)樗O(jiān)聽進(jìn)來的請(qǐng)求。

          @ClientEndpoint  public class MyClientEndpoint {} 

          Java中使用注解驅(qū)動(dòng)POJO方式來初始化WebSocket連接,可通過如下代碼來完成:

          javax.websocket.WebSocketContainer container = javax.websocket.ContainerProvider.getWebSocketContainer();  container.conntectToServer(MyClientEndpoint.class, new URI("ws://localhost:8080/tictactoeserver/endpoint")); 

          此后,以 @ServerEndpoint 或@ClientEndpoint 注解的類都稱為注解端點(diǎn).

          一旦建立了WebSocket連接 ,就會(huì)創(chuàng)建 Session,并且會(huì)調(diào)用注解端點(diǎn)中以@OnOpen注解的方法. 

          此方法包含了幾個(gè)參數(shù):

          • javax.websocket.Session 參數(shù), 代表創(chuàng)建的Session
          • EndpointConfig 實(shí)例包含了關(guān)于端點(diǎn)配置的信息
          • 0個(gè)或多個(gè)以 @PathParam注解的字符串參數(shù),指的是端點(diǎn)路徑的path參數(shù)

          下面的方法實(shí)現(xiàn)了當(dāng)打開WebSocket時(shí),將會(huì)打印session的標(biāo)識(shí)符:

          @OnOpen public void myOnOpen (Session session) {    System.out.println ("WebSocket opened: "+session.getId()); } 

          Session實(shí)例只要WebSocket未關(guān)閉就會(huì)一直有效Session類中包含了許多有意思的方法,以允許開發(fā)者獲取更多關(guān)于的信息

          同時(shí),Session 也包含了應(yīng)用程序特有的數(shù)據(jù)鉤子,即通過getUserProperties() 方法來返回 Map<String, Object>

          這允許開發(fā)者可以使用session-和需要在多個(gè)方法調(diào)用間共享的應(yīng)用程序特定信息來填充Session實(shí)例.

          i當(dāng)WebSocket端收到消息時(shí),將會(huì)調(diào)用以@OnMessage注解的方法.以@OnMessage 注解的方法可包含下面的參數(shù):

          • javax.websocket.Session 參數(shù).
          • 0個(gè)或多個(gè)以 @PathParam注解的字符串參數(shù),指的是端點(diǎn)路徑的path參數(shù)
          • 消息本身. 下面有可能消息類型描述.

          當(dāng)其它端發(fā)送了文本消息時(shí),下面的代碼片斷會(huì)打印消息內(nèi)容:

          @OnMessage public void myOnMessage (String txt) {    System.out.println ("WebSocket received message: "+txt); }  

          如果以@OnMessage i注解的方法返回值不是void, WebSocket實(shí)現(xiàn)會(huì)將返回值發(fā)送給其它端點(diǎn).下面的代碼片斷會(huì)將收到的文本消息以首字母大寫的形式發(fā)回給發(fā)送者:

          @OnMessage public String myOnMessage (String txt) {    return txt.toUpperCase(); }  

          另一種通過WebSocket連接來發(fā)送消息的代碼如下:

          RemoteEndpoint.Basic other = session.getBasicRemote(); other.sendText ("Hello, world"); 

          在這種方式中,我們從Session 對(duì)象開始,它可以從生命周期回調(diào)方法中獲取(例如,以 @OnOpen注解的方法).session實(shí)例上getBasicRemote() 方法返回的是WebSocket其它部分的代表RemoteEndpointRemoteEndpoint 實(shí)例可用于發(fā)送文本或其它類型的消息,后面有描述.

          當(dāng)關(guān)閉WebSocket連接時(shí),將會(huì)調(diào)用@OnClose 注解的方法。此方法接受下面的參數(shù):

          • javax.websocket.Session 參數(shù). 注意,一旦WebSocket真正關(guān)閉了,此參數(shù)就不能被使用了,這通常發(fā)生在@OnClose 注解方法返回之后.
          • javax.websocket.CloseReason 參數(shù),用于描述關(guān)閉WebSocket的原因,如:正常關(guān)閉,協(xié)議錯(cuò)誤,服務(wù)過載等等.
          • 0個(gè)或多個(gè)以 @PathParam注解的字符串參數(shù),指的是端點(diǎn)路徑的path參數(shù)

          下面的代碼片段打印了WebSocket關(guān)閉的原因:

          @OnClose public void myOnClose (CloseReason reason) {    System.out.prinlnt ("Closing a WebSocket due to "+reason.getReasonPhrase()); } 

          完整情況下,這里還有一個(gè)生命周期注解:如果收到了錯(cuò)誤,將會(huì)調(diào)用 @OnError 注解的方法。

          Interface-Driven 方式

          annotation-driven 方式允許我們注解一個(gè)Java類,以及使用生命周期注解來注解方法. 

          使用interface-driven方式,開發(fā)者可繼承javax.websocket.Endpoint 并覆蓋其中的onOpenonClose, 以及onError 方法:

          public class myOwnEndpoint extends javax.websocket.Endpoint {    public void onOpen(Session session, EndpointConfig config) {...}    public void onClose(Session session, CloseReason closeReason) {...}    public void onError (Session session, Throwable throwable) {...} } 

          為了攔截消息,需要在onOpen實(shí)現(xiàn)中注冊(cè)一個(gè)javax.websocket.MessageHandler:

          public void onOpen (Session session, EndpointConfig config) {    session.addMessageHandler (new MessageHandler() {...}); } 

          MessageHandler 接口有兩個(gè)子接口: MessageHandler.Partial和 MessageHandler.Whole

          MessageHandler.Partial 接口應(yīng)該用于當(dāng)開發(fā)者想要收到部分消息通知的時(shí)候,MessageHandler.Whole的實(shí)現(xiàn)應(yīng)該用于整個(gè)消息到達(dá)通知

          下面的代碼片斷會(huì)監(jiān)聽進(jìn)來的文件消息,并將文本信息轉(zhuǎn)換為大小版本后發(fā)回給其它端點(diǎn):

          public void onOpen (Session session, EndpointConfig config) {    final RemoteEndpoint.Basic remote = session.getBasicRemote();    session.addMessageHandler (new MessageHandler.Whole<String>() {       public void onMessage(String text) {                  try {                      remote.sendString(text.toUpperCase());                  } catch (IOException ioe) {                      // handle send failure here                  }              }     }); } 

          消息類型,編碼器,解碼器

          WebSocket的JavaAPI非常強(qiáng)大,因?yàn)樗试S發(fā)送任或接收任何對(duì)象作為WebSocket消息.

          基本上,有三種不同類型的消息:

          • 基于文本的消息
          • 二進(jìn)制消息
          • Pong 消息,它是WebSocket連接自身

          當(dāng)使用interface-driven模式,每個(gè)session最多只能為這三個(gè)不同類型的消息注冊(cè)一個(gè)MessageHandler.

          當(dāng)使用annotation-driven模式,針對(duì)不同類型的消息,只允許出現(xiàn)一個(gè)@onMessage 注解方法. 在注解方法中,消息內(nèi)容中允許的參數(shù)依賴于消息類型。

          Javadoc for the @OnMessage annotation 明確指定了消息類型上允許出現(xiàn)的消息參數(shù):

          • "如果方法用于處理文本消息: 

            • String 用于接收整個(gè)消息
            • Java 原型或等價(jià)的類用于接收整個(gè)消息并將其轉(zhuǎn)換為此類型
            • String 和 boolean 對(duì)用于部分接收消息
            • Reader 用于以阻塞流的方式接收整個(gè)消息
            • 端點(diǎn)的任何對(duì)象參數(shù)存在文本解碼器 (Decoder.Text 或 Decoder.TextStream).
          • 如果方法用于處理二進(jìn)制消息: 

            • byte[] 或 ByteBuffer 用于接收整個(gè)消息
            • byte[] 和 boolean 對(duì), 或者 ByteBuffer 和boolean對(duì)用于部分接收消息
            • InputStream 用于按阻塞流的方式接收整個(gè)消息
            • 端點(diǎn)的任何對(duì)象參數(shù)存在二進(jìn)制解碼器(Decoder.Binary or Decoder.BinaryStream).
          • 如果方法是用于處理pong消息: 

          任何Java對(duì)象使用編碼器都可以編碼為基于文本或二進(jìn)制的消息.這種基于文本或二進(jìn)制的消息將轉(zhuǎn)輸?shù)狡渌它c(diǎn),在其它端點(diǎn),它可以解碼成Java對(duì)象-或者被另外的WebSocket 包解釋. 

          通常情況下,XML或JSON用于來傳送WebSocket消息, 編碼/解碼然后會(huì)將Java對(duì)象編組成XML或JSON并在另一端解碼為Java對(duì)象.

          encoder是以javax.websocket.Encoder 接口的實(shí)現(xiàn)來定義,decoder是以javax.websocket.Decoder 接口的實(shí)現(xiàn)來定義的. 

          有時(shí),端點(diǎn)實(shí)例必須知道encoders和decoders是什么.使用annotation-driven方式, 可向@ClientEndpoint 和 @ServerEndpoint l注解中的encode和decoder元素傳遞 encoders和decoders的列表。

          Listing 1 中的代碼展示了如何注冊(cè)一個(gè) MessageEncoder 類(它定義了MyJavaObject實(shí)例到文本消息的轉(zhuǎn)換). MessageDecoder 是以相反的轉(zhuǎn)換來注冊(cè)的.

          @ServerEndpoint(value="/endpoint", encoders = MessageEncoder.class, decoders= MessageDecoder.class) public class MyEndpoint { ... }  class MessageEncoder implements Encoder.Text<MyJavaObject> {    @override    public String encode(MyJavaObject obj) throws EncodingException {       ...    } }  class MessageDecoder implements Decoder.Text<MyJavaObject> {    @override     public MyJavaObject decode (String src) throws DecodeException {       ...    }     @override     public boolean willDecode (String src) {       // return true if we want to decode this String into a MyJavaObject instance    } } 

          Listing 1

          Encoder 接口有多個(gè)子接口:

          • Encoder.Text 用于將Java對(duì)象轉(zhuǎn)成文本消息
          • Encoder.TextStream 用于將Java對(duì)象添加到字符流中
          • Encoder.Binary 用于將Java對(duì)象轉(zhuǎn)換成二進(jìn)制消息
          • Encoder.BinaryStream 用于將Java對(duì)象添加到二進(jìn)制流中

          類似地,Decoder 接口有四個(gè)子接口:

          • Decoder.Text 用于將文本消息轉(zhuǎn)換成Java對(duì)象
          • Decoder.TextStream 用于從字符流中讀取Java對(duì)象
          • Decoder.Binary 用于將二進(jìn)制消息轉(zhuǎn)換成Java對(duì)象
          • Decoder.BinaryStream 用于從二進(jìn)制流中讀取Java對(duì)象

          結(jié)論

          WebSocket Java API為Java開發(fā)者提供了標(biāo)準(zhǔn)API來集成IETF WebSocket標(biāo)準(zhǔn).通過這樣做,Web 客戶端或本地客戶端可使用任何WebSocket實(shí)現(xiàn)來輕易地與Java后端通信。

          Java Api是高度可配置的,靈活的,它允許java開發(fā)者使用他們喜歡的模式。

          也可參考

          posted @ 2016-07-24 01:35 胡小軍 閱讀(2789) | 評(píng)論 (0)編輯 收藏
               摘要: Servlets定義為JSR 340,可以下載完整規(guī)范.servlet是托管于servlet容器中的web組件,并可生成動(dòng)態(tài)內(nèi)容.web clients可使用請(qǐng)求/響應(yīng)模式同servlet交互. servlet容器負(fù)責(zé)處理servlet的生命周期事件,接收請(qǐng)求和發(fā)送響應(yīng),以及執(zhí)行其它必要的編碼/解碼部分.WebServlet它是在POJO上使用@WebServlet注...  閱讀全文
          posted @ 2016-07-24 01:32 胡小軍 閱讀(871) | 評(píng)論 (0)編輯 收藏
          在本章中,我們將涵蓋下面的主題:
          1. 監(jiān)控RabbitMQ的行為
          2. 使用RabbitMQ進(jìn)行故障診斷
          3. 跟蹤RabbitMQ當(dāng)前活動(dòng)
          4. 調(diào)試RabbitMQ的消息
          5. 當(dāng)RabbitMQ重啟失敗時(shí)該做什么
          6. 使用Wireshark來調(diào)試
          介紹
          每當(dāng)我們開發(fā)一個(gè)應(yīng)用程序的時(shí)候,一種常見的做法是開發(fā)一個(gè)診斷基礎(chǔ)設(shè)施. 這可以基于日志文件,SNMP 轉(zhuǎn)移以及其它手段.
          RabbitMQ提供了標(biāo)準(zhǔn)日志文件和內(nèi)建消息故障診斷解決方案.
          在前面三個(gè)食譜中,我們將看到如何來使用這三種特性.
          某些時(shí)候,會(huì)存在一些阻止RabbitMQ啟動(dòng)的問題.在這種情況下,需要強(qiáng)制解決服務(wù)器上的問題并重啟服務(wù)器。這一點(diǎn),我們將在RabbitMQ重啟失敗時(shí)該做什么食譜中講解。

          然而,調(diào)試消息也是應(yīng)用程序開發(fā)的一部分.在這種情況下,我們需要知道RabbitMQ和客戶之間交換的準(zhǔn)確信息.可以使用一個(gè)內(nèi)建代理工具(Java client API的一部分) (查看調(diào)試RabbitMQ的消息食譜)或者使用高級(jí)網(wǎng)絡(luò)監(jiān)視工具來檢查網(wǎng)絡(luò)狀況,正如我們將在使用Wireshark來調(diào)試食譜中看到的一樣。
          監(jiān)控RabbitMQ行為
          為了檢查RabbitMQ的正常行為,有一個(gè)監(jiān)控工具是很有用的,特別是在處理集群的時(shí)候.
          有許多不同的工具,商業(yè)的或免費(fèi)的, 
          這有助于讓它們受控于分布式系統(tǒng)如Nagios、Zabbix之下.
          在本食譜中,我們將展示如何配置Ganglia  RabbitMQ 插件(http://sourceforge.net/apps/trac/ganglia/wiki/ganglia_quick_start).
          準(zhǔn)備
          為了運(yùn)行此食譜,你需要啟用管理插件來配置RabbitMQ。同時(shí),你也需要安裝和配置Ganglia. 在本食譜中,我們使用的版本是3.6.0.
          如何做
          為了使用Ganglia監(jiān)控圖形來查看RabbitMQ統(tǒng)計(jì)數(shù)據(jù),你需要執(zhí)行下面的步驟:
          1. 在你的Linux發(fā)行版上,使用yum或apt-get安裝和配置Ganglia.你需要下面的包:
          ganglia-gmetad
          ganglia-gmond
          ganglia-gmond-python
          ganglia-web
          ganglia
          2. 從https://github.com/ganglia/gmond_python_modules/blob/master/rabbit/python_modules/rabbitmq.py拷貝Python Ganglia監(jiān)控插件到/usr/lib64/ganglia/python_modules.
          3. 從https://github.com/ganglia/gmond_python_modules/blob/master/rabbit/conf.d/rabbitmq.pyconf拷貝Python Ganglia 配置文件到/etc/ganglia/conf.d 

          4. 檢查配置文件中的參數(shù)正確性.實(shí)際上, 你可能需要通過只留下默認(rèn)的虛擬主機(jī)來解決以下條目:
          paramvhost {
          value = "/"
          }
          5.如果它已經(jīng)運(yùn)行了,使用下面的命令來重啟gmond:
          service gmond restart

          如何工作
          通過這個(gè)食譜, 你可以從Ganglia環(huán)境中來監(jiān)控RabbitMQ.
          TIP
          對(duì)于基本的故障診斷,你可以通過日志文件來進(jìn)行,默認(rèn)情況下,日志文件存儲(chǔ)于/var/log/rabbitmq.

          一旦它運(yùn)行起來,你就可以從同一個(gè)web界面中,看到系統(tǒng)信息和RabbitMQ節(jié)點(diǎn)、隊(duì)列的相關(guān)信息,如下面的截圖所示:


          更多
          Ganglia是集群監(jiān)測(cè)一種廣泛的解決方案,但不是唯一的一個(gè)。其它的解決方案還包括Nagios (www.nagios.org), Zabbix (www.zabbix.com), 以及Puppet (puppetlabs.com).

          通過RabbitMQ排除故障
          正如前面食譜提到的, 我們可以通過一種便利的方式,即以日志文件的方式來監(jiān)控RabbitMQ行為.
          也可以使用RabbitMQ自身來訪問同種信息, 通過向AMQP client通知broker的活動(dòng).


          準(zhǔn)備
          要運(yùn)行本食譜,我們需要運(yùn)行RabbitMQ以及Java client library.
          如何做
          為了消費(fèi)日志消息,你可在Consumer.java中執(zhí)行主方法.你可在Chapter12/Recipe02/Java/src/rmqexample中找到源友. 下面,我們將高亮主要步驟:
          1. 創(chuàng)建一個(gè)臨時(shí)匿名隊(duì)列,并將其綁定到AMQP log交換器:
          String tmpQueue = channel.queueDeclare().getQueue();
          channel.queueBind(tmpQueue,"amq.rabbitmq.log","#");
          2. 在消費(fèi)者回調(diào)(ActualConsumer.java)中,可以檢索消息和每個(gè)消息的路由鍵,并將它們打印出來:
          String routingKey = envelope.getRoutingKey();
          String message = new String(body);
          System.out.println(routingKey + ": " + message);
          3. 此時(shí),你可以在broker上執(zhí)行任何RabbitMQ操作,并且你會(huì)看到日志輸出到標(biāo)準(zhǔn)輸出上。


          如何工作
          RabbitMQ log交換器amq.rabbitmq.log是一個(gè)topic交換器,RabbitMQ自身用來發(fā)布其日志消息.
          在我們的示例代碼中,我們使用#通配符來消費(fèi)所有topics的消息.
          例如,通過運(yùn)行另一份代碼,我們可運(yùn)行同一個(gè)broker的兩個(gè)連接,然后中斷它,我們將下面的輸出:
          info: accepting AMQP connection <0.2737.0> (127.0.0.1:54698 ->127.0.0.1:5672)

          info: accepting AMQP connection <0.2753.0> (127.0.0.1:54699 ->127.0.0.1:5672)
          warning: closing AMQP connection <0.2737.0> (127.0.0.1:54698 ->127.0.0.1:5672):
          connection_closed_abruptly
          warning: closing AMQP connection <0.2753.0> (127.0.0.1:54699 ->127.0.0.1:5672):
          connection_closed_abruptly
          值得注意的是,在這里報(bào)告的信息,信息和警告不是自己的一部分,但是我們?cè)诿總€(gè)開始打印的路由鍵消息(前一步驟的步驟2)。

          TIP
          如果我們只想收到警告和錯(cuò)誤消息,我們可以訂閱相應(yīng)的主題.

          更多
          默認(rèn)情況下,日志交換器-amq.rabbitmq.log被創(chuàng)建在虛擬主機(jī)/中。通過在RabbitMQ配置文件中定義default_vhost從而設(shè)置其位置是可能的.

          追蹤RabbitMQ當(dāng)前活動(dòng)
          有時(shí),為了分析和調(diào)試未知的應(yīng)用程序行為,我們需要追蹤RabbitMQ接收和分發(fā)的所有消息.
          RabbitMQ提供追蹤這些消息的所謂的流水追蹤工具。
          追蹤活動(dòng)可以在運(yùn)行時(shí)啟用和禁用,并且它應(yīng)該只用于調(diào)試,因?yàn)樗?guī)定了broker活動(dòng)的開銷.

          準(zhǔn)備
          要運(yùn)行此食譜,我們需要運(yùn)行此食譜,我們需要運(yùn)行RabbitMQ和Java client library.
          如何做
          RabbitMQ使用與日志消息中同樣的機(jī)制來發(fā)送追蹤消息;因此,示例代碼與前一個(gè)食譜中的非常相似.

          為了消費(fèi)追蹤消息,你可以執(zhí)行Consumer.java中的主方法,你可在Chapter12/Recipe02/Java/src/rmqexample目錄中找到源碼.這里,我們高亮了主要步驟:
          1. 創(chuàng)建一個(gè)臨時(shí)隊(duì)列,并將其綁定到AMQP log交換器上:
          String tmpQueue = channel.queueDeclare().getQueue();
          channel.queueBind(tmpQueue,"amq.rabbitmq.trace","#");
          2.在消費(fèi)者回調(diào)(ActualConsumer.java)中, 可以獲取每個(gè)消息,并使用下面的代碼打印出來:
          String routingKey = envelope.getRoutingKey();
          String message = new String(body);
          Map<String,Object> headers = properties.getHeaders();
          LongStringexchange_name = (LongString)
          headers.get("exchange_name");
          LongString node = (LongString) headers.get("node");
          ...
          3. 可從root用戶(Linux)或在RabbitMQ命令控制臺(tái)(Windows)來激活firehose,其激活命令如下:
          rabbimqctl trace_on
          4. 此時(shí),你可以向broker啟動(dòng)生產(chǎn)和發(fā)送消息,然后你們會(huì)在標(biāo)準(zhǔn)輸出中看到追蹤信息.
          5. 可調(diào)用下面的命令來禁用firehose:
          rabbimqctl trace_off


          如何工作 
          默認(rèn)情況下,amq.rabbit.trace topic交換器不會(huì)接收任何消息,但一旦激活firehose后(前面步驟的步驟3),所有流經(jīng)broker的消息將被按下面的規(guī)則拷貝:
          1.進(jìn)入broker的消息,它們是通過路由鍵publish.exchange-name發(fā)布的, 這里的exchange-name是消息最初發(fā)布的交換器名稱.
          2.離開broker的消息,它們是通過路由鍵deliver.queuename發(fā)布的,這里的queue-name是消息最初被消費(fèi)的隊(duì)列名稱.
          3.消息的body是從原始消息中拷貝過來的.

          4.原始消息的元數(shù)據(jù)會(huì)插入到拷貝消息的header屬性中. 在步驟2中,我們已經(jīng)看到了,如何獲取最初分發(fā)消息的交換器名稱,但獲取所有原始信息也是可以的,即,找到所有可有字段,并將它們插到消息屬性中.firehose的官方文檔鏈接位于:http://www.rabbitmq.com/firehose.html.

          調(diào)試RabbitMQ消息
          有時(shí),通過在標(biāo)準(zhǔn)輸出中記錄通過broker的消息是有用的.通過RabbitMQ Java客戶端提供的簡(jiǎn)單應(yīng)用程序來追蹤消息,也是可行的.
          準(zhǔn)備
          要運(yùn)行此食譜,你需要運(yùn)行的RabbitMQ(運(yùn)行標(biāo)準(zhǔn)端口 5672),以及RabbitMQ Java client library
          如何做
          RabbitMQ的Java client library中包含了一個(gè)追蹤工具,你可以按下面的步驟來進(jìn)行實(shí)際使用.
          1. 從http://www.rabbitmq.com/java-client.html頁面下載最新版本的RabbitMQ Java client library.
          2. 將其解壓,并進(jìn)入其目錄
          .
          3. 通過下面的命令來運(yùn)行Java tracer:
          ./runjava.sh com.rabbitmq.tools.Tracer
          4. 運(yùn)行用于調(diào)試Java client,并將其連接到5673端口.對(duì)于本食譜,我們可以使用包含在Java client包中的另一個(gè)Java工具,其調(diào)用如下:
          ./runjava.sh com.rabbitmq.examples.PerfTest -h amqp://localhost:5673 -C 1 -D 1

          如何工作
          追蹤工具是一個(gè)簡(jiǎn)單的AMQP代理;默認(rèn)情況下,它監(jiān)聽5673端口,并會(huì)把所有的請(qǐng)求轉(zhuǎn)發(fā)到默認(rèn)監(jiān)聽5672端口的RabbitMQ broker.
          所有生產(chǎn)或消費(fèi)的消息,如同AMQP操作一樣,都會(huì)記錄到標(biāo)準(zhǔn)輸出中.
          運(yùn)行前面步驟的步驟4,我們使用了包含在Java client包中另一個(gè)用于作RabbitMQ壓力測(cè)試的工具.
          在這里,我們只是限制了生產(chǎn)一個(gè)消息(-C 1) 并消費(fèi)它(-D 1).
          TIP
          追蹤工具只在Java client API中可用.

          更多
          可以使用下面的代碼來為Java追蹤程序傳遞更多的參數(shù):
          ./runjava.sh com.rabbitmq.tools.Tracer listenPort connectHost connectPort
          listenPort指的是追蹤器監(jiān)聽的端口(默認(rèn)為5673), connectHost/connectPort (默認(rèn)為localhost/5672) 是用于連接并轉(zhuǎn)發(fā)收到請(qǐng)求的主機(jī)和端口。
          使用下面的命令,你可以找到所有PerfTest可用選項(xiàng):
          ./runjava.sh com.rabbitmq.examples.PerfTest --help


          也可參考
          在http://www.rabbitmq.com/java-tools.html中,你可以找到Java追蹤工具以及PerfTest的文檔.

          當(dāng)RabbitMQ重啟失敗時(shí),需要做什么
          偶爾情況下, RabbitMQ 可能會(huì)重啟失敗。如果broker包含持久化數(shù)據(jù)時(shí),這是一個(gè)嚴(yán)重的問題,否則,有足夠的能力重設(shè)其持久化狀態(tài)。
          準(zhǔn)備
          要運(yùn)行此食譜,你只需要一個(gè)測(cè)試RabbitMQ broker.
          TIP
          我們將銷毀所有之前定義的數(shù)據(jù)—以避免使用生產(chǎn)實(shí)例.

          如何做
          要清空RabbitMQ, 可執(zhí)行下面的簡(jiǎn)單步驟:
          1. 如果RabbitMQ運(yùn)行的話,停止它.
          2. 定位到Mnesia 數(shù)據(jù)庫目錄.默認(rèn)是/var/lib/rabbitmq/mnesia (Linux) or %APPDATA%\RabbitMQ\db (Windows).
          3. 遞歸刪除其目錄和文件.
          4. 重啟RabbitMQ.

          如何工作
          Mnesia 數(shù)據(jù)庫包含了所有運(yùn)行時(shí)的RabbitMQ定義信息: 隊(duì)列,交換器,用戶等等.
          通過刪除Mnesia數(shù)據(jù)庫 (或者通過重命名,這樣可以在需要的時(shí)候恢復(fù)某些數(shù)據(jù)), RabbitMQ會(huì)重置到出廠默認(rèn)狀態(tài),當(dāng)RabbitMQ啟動(dòng)時(shí),它會(huì)創(chuàng)建一個(gè)新的Mnesia數(shù)據(jù)庫,并使用默認(rèn)值進(jìn)行初始化.


          更多
          如果broker無法在第一時(shí)間啟動(dòng),有可能是某個(gè)系統(tǒng)目錄存在權(quán)限問題:即要么是Mnesia數(shù)據(jù)庫目錄,要么是日志目錄,要么是某些在配置文件中指定的臨時(shí)的或自定義的目錄.
          你可以在RabbitMQ故障排除頁頁找到詳盡的列表(http://www.rabbitmq.com/troubleshooting.html).

          也可參考
          在Mnesia API 文檔頁面(http://www.erlang.org/doc/man/mnesia.html),,你可以找到更多關(guān)于如何破解Mnesia數(shù)據(jù)庫的信息.

          使用Wireshark調(diào)試
          在調(diào)試RabbitMQ消息食譜中,我們已經(jīng)了解過了如何來追蹤RabbitMQ的消息.

          然而,以下情況并不總是可能的或可取的,即停止正在運(yùn)行的客戶端(或RabbitMQ服務(wù)器),修改它的連接端口,指向一個(gè)不同的broker;我們只想監(jiān)控正在實(shí)時(shí)傳遞的消息,影響系統(tǒng)的活動(dòng)應(yīng)該盡可能的少。

          TIP
          但是,正如在前面食譜(追蹤RabbitMQ當(dāng)前活動(dòng))中看到的一樣,激活firehose是可行的 .


          Wireshark是一個(gè)免費(fèi)的有能力解碼AMQP消息的網(wǎng)絡(luò)分析工具.
          此工具既可用在客戶段,也可以用在服務(wù)端,從而
          無縫監(jiān)控AMQP交通狀況.

          準(zhǔn)備
          要練習(xí)這個(gè)食譜,你需要運(yùn)行的RabbitMQ以及RabbitMQ Java client library.

          如何做
          在下面的步驟中,我們將看到如何使用Wireshark來追蹤AMQP消息:
          1. 如果Wireshark在你的系統(tǒng)中尚不可用,那么需要從http://www.wireshark.org/下載和安裝Wireshark . 如果可能的話,你也可以從你的發(fā)行版中進(jìn)行安裝,如:
          yum install wireshark-gnome
          2.在Linux系統(tǒng)中,以root用戶來啟動(dòng)Wireshark.
          3.啟動(dòng)從環(huán)回接口中捕獲消息.
          4.切換到Java client library路徑下,在終端中運(yùn)行下面的命令:
          ./runjava.sh com.rabbitmq.examples.PerfTest -C 1 -D 1
          5.
          停止Wireshark GUI的采集,并分析抓到的AMQP交通狀況.
          如何工作
          使用Wireshark,可用于檢測(cè)AMQP交通的退出或進(jìn)行一個(gè)持有RabbitMQ服務(wù)器或客戶端的服務(wù)器.
          在我們的例子中,我們捕獲了運(yùn)行在同一臺(tái)機(jī)器上服務(wù)端和客戶端網(wǎng)絡(luò)交通狀況,因此連接是localhost中發(fā)生的.這也是為什么我們從環(huán)回接口中捕獲交通狀況的原因(前面步驟的步驟3).
          反之,我們應(yīng)該從網(wǎng)絡(luò)接口中進(jìn)行捕獲,這種網(wǎng)絡(luò)接口通常是eth0或其它相似的網(wǎng)絡(luò)接口.

          TIP
          在Linux上,可以直接捕獲localhost;但同樣的操作不能應(yīng)用到Windows上.在這種情況下,客戶端和服務(wù)端必須位于不同的兩臺(tái)機(jī)器上, 并且在網(wǎng)絡(luò)接口上(要么是物理的,要么是虛擬的)必須激活捕獲, 這樣它們之間才可連接.


          所以,為了運(yùn)行Wireshark的圖形用戶界面,如果RabbitMQ客戶端和服務(wù)器運(yùn)行在同一個(gè)節(jié)點(diǎn),你需要選擇環(huán)回接口,如圖下面的截圖所示:



          TIP
          在Linux上,當(dāng)你安裝Wireshark軟件包,你通常只會(huì)有命令行界面,tshark。要安裝Wireshark的GUI,你必須安裝相應(yīng)的軟件包. 例如,在Fedora中,你需要安裝wireshark-gnome包.

          一旦AMQP交通已穿過環(huán)回接口,它已經(jīng)被Wireshark捕獲。
          運(yùn)行在步驟4的實(shí)驗(yàn)實(shí)際上在兩個(gè)獨(dú)立的連接中開啟了一個(gè)生產(chǎn)者和一個(gè)消費(fèi)者.
          為了高亮顯示,找到一個(gè)描述為Basic.PublishContent-Header的包,點(diǎn)擊它,并選擇Follow TCP stream.然后,您可以關(guān)閉顯示客戶端和服務(wù)器之間有效負(fù)載對(duì)話的窗口。在主窗口中,您現(xiàn)在可以看到在客戶端和服務(wù)器之間交換的網(wǎng)絡(luò)數(shù)據(jù)包,如下面的截圖所示:



          用同樣的方式,你可以選擇RabbitMQ server,如下面的截圖所示:

          在前面的兩個(gè)截圖中,我們已經(jīng)強(qiáng)調(diào)了消息AMQP的有效載荷,但由于Wireshark中包括了一個(gè)非常完整的AMQP解剖器,你會(huì)發(fā)現(xiàn)在AMQP交通的很多細(xì)節(jié)。
          更多
          如果RabbitMQ配置為使用SSL,并且你想要分析加密流量,在一定條件下,通過在Wireshark配置中合理配置SSL公共/私有密鑰也是可以的
          可在http://wiki.wireshark.org/SSL找到更多信息.
          也可參考
          http://wiki.wireshark.org/AMQP,你可以找到一些關(guān)于Wireshark AMQP 解剖器的一些指南.




          posted @ 2016-07-20 11:39 胡小軍 閱讀(3982) | 評(píng)論 (1)編輯 收藏
               摘要: 在本章節(jié)中,我們將展現(xiàn)一些RabbitMQ中的可用插件.然后,我們將展示如何使用現(xiàn)實(shí)世界中的例子來開發(fā)新插件.啟用和配置STOMP插件管理RabbitMQ集群監(jiān)控Shovel狀態(tài)開發(fā)新插件– 使用ODBC連接關(guān)系數(shù)據(jù)庫介紹多虧了插件設(shè)施,使得RabbitMQ成為了一個(gè)可擴(kuò)展平臺(tái).它提供了許多通用插件,其中一些已經(jīng)在前面的章節(jié)中解釋過了.例如, Federation和...  閱讀全文
          posted @ 2016-07-20 11:30 胡小軍 閱讀(2605) | 評(píng)論 (1)編輯 收藏
               摘要: 在這一章中,我們將涵蓋:多線程和隊(duì)列系統(tǒng)調(diào)整改善帶寬使用不同分發(fā)工具介紹這里沒有標(biāo)準(zhǔn)的RabbitMQ調(diào)優(yōu)指南,因?yàn)椴煌瑧?yīng)用程序會(huì)采用不同方式優(yōu)化.通常情況下,應(yīng)用程序需要在客戶端進(jìn)行優(yōu)化:處理器密集型應(yīng)用程序可以通過為每個(gè)處理器內(nèi)核運(yùn)行一個(gè)線程來進(jìn)行優(yōu)化I/O密集型應(yīng)用程序可以通過在單核上運(yùn)行多個(gè)線程來隱藏隱式延遲在兩種情況下,消息傳遞是完美的結(jié)合.為了優(yōu)化網(wǎng)絡(luò)傳輸速率,AMQP標(biāo)準(zhǔn)規(guī)定消息按束...  閱讀全文
          posted @ 2016-07-15 14:53 胡小軍 閱讀(9375) | 評(píng)論 (0)編輯 收藏
               摘要: 在本章中我們將覆蓋涉及:鏡像隊(duì)列同步隊(duì)列優(yōu)化鏡像策略在幾個(gè)broker之間分發(fā)消息創(chuàng)建一個(gè)地理位置集群復(fù)制過濾和轉(zhuǎn)發(fā)消息將高可用技術(shù)結(jié)合在一起客戶端高可用性介紹RabbitMQ通過數(shù)據(jù)復(fù)制來達(dá)到高可用,當(dāng)數(shù)據(jù)完整性、服務(wù)連續(xù)性是最重要的時(shí)候, 這一點(diǎn)與存儲(chǔ)(如:RAID解決方案),數(shù)據(jù)庫,以及所有IT基礎(chǔ)設(shè)施解決方案是相同的。事實(shí)上,這些解決方案不僅可以避免數(shù)據(jù)丟失,也可以避免計(jì)劃維護(hù)和...  閱讀全文
          posted @ 2016-07-02 19:11 胡小軍 閱讀(1924) | 評(píng)論 (0)編輯 收藏

          名稱

          rabbitmq-service.bat — 管理RabbitMQ AMQP service

          語法

          rabbitmq-service.bat [command]

          描述

          RabbitMQ是AMQP的實(shí)現(xiàn), 后者是高性能企業(yè)消息通信的新興標(biāo)準(zhǔn). RabbitMQ server是AMQP 中間件的健壯,可擴(kuò)展實(shí)現(xiàn).

          運(yùn)行rabbitmq-service,可允許RabbitMQ broker在NT/2000/2003/XP/Vista®環(huán)境上以服務(wù)來運(yùn)行,這樣就可以通過Windows® services applet來啟動(dòng)和停止服務(wù).

          默認(rèn)情況下,服務(wù)會(huì)以本地系統(tǒng)帳戶中認(rèn)證上下文來運(yùn)行。因此,有必要將Erlang cookies 和本地系統(tǒng)帳戶進(jìn)行同步(典型地,C:\WINDOWS\.erlang.cookie和帳戶將用來運(yùn)行 rabbitmqctl).

          命令

          help

          顯示使用信息.

          install

          安裝service,安裝后,它不會(huì)啟動(dòng)。如果環(huán)境變量修改了的話,隨后的調(diào)用將更新服務(wù)參數(shù).

          remove

          刪除service.如果刪除時(shí),service正在運(yùn)行,則將會(huì)自動(dòng)停止。 它不會(huì)刪除任何文件,后續(xù)可通過rabbitmq-server 繼續(xù)操作。

          start

          啟動(dòng)service. 在此之前,service必須被正確安裝

          stop

          停止service. 

          disable

          禁用service. 這等價(jià)于在服務(wù)控制面板中,將啟動(dòng)類型設(shè)置為禁用.

          enable

          啟用service. 這等價(jià)于在服務(wù)控制面板中,將啟動(dòng)類型設(shè)置為自動(dòng).

          環(huán)境變量

          RABBITMQ_SERVICENAME

          默認(rèn)為RabbitMQ.

          RABBITMQ_BASE

          默認(rèn)是當(dāng)前用戶的應(yīng)用程序數(shù)據(jù)目錄. 這是日志和數(shù)據(jù)目錄的位置(C:\Users\Administrator\AppData\Roaming\RabbitMQ).

          RABBITMQ_NODENAME

          默認(rèn)是rabbit. 當(dāng)你想在一臺(tái)機(jī)器上運(yùn)行多個(gè)節(jié)點(diǎn)時(shí),此配置是相當(dāng)有用的, RABBITMQ_NODENAME在每個(gè)erlang-node和機(jī)器的組合中應(yīng)該唯一。

          參考clustering on a single machine guide 來更多細(xì)節(jié).

          RABBITMQ_NODE_IP_ADDRESS

          默認(rèn)情況下,RabbitMQ會(huì)綁定到所有網(wǎng)絡(luò)接口上,如果只想綁定某個(gè)網(wǎng)絡(luò)接口,可修改此設(shè)置。

          RABBITMQ_NODE_PORT

          默認(rèn)為5672.

          ERLANG_SERVICE_MANAGER_PATH

          默認(rèn)為C:\Program Files\erl5.5.5\erts-5.5.5\bin (或64位環(huán)境 中為C:\Program Files (x86)\erl5.5.5\erts-5.5.5\bin). 這是Erlang service manager的安裝位置.

          RABBITMQ_CONSOLE_LOG

          將此變量設(shè)置為new或reuse,以將服務(wù)器控制臺(tái)的輸出重定向到名為SERVICENAME.debug文件中(位于安裝服務(wù)的用戶應(yīng)用程序數(shù)據(jù)目錄).在Vista下,其位置在C:\Users\AppData\username\SERVICENAME. 在Windows的前期版本中,位置在C:\Documents and Settings\username\Application Data\SERVICENAME. 

          如果RABBITMQ_CONSOLE_LOG設(shè)置為new,那么每次服務(wù)啟動(dòng)時(shí)都會(huì)創(chuàng)建一個(gè)新文件。

          如果RABBITMQ_CONSOLE_LOG設(shè)置為reuse,那么每次服務(wù)啟動(dòng)時(shí),文件都會(huì)被覆蓋.

          當(dāng)RABBITMQ_CONSOLE_LOG 沒有設(shè)置或設(shè)置的值不是new或reuse時(shí),默認(rèn)的行為是丟棄服務(wù)器輸出。


          posted @ 2016-06-24 23:58 胡小軍 閱讀(1549) | 評(píng)論 (0)編輯 收藏

          要求

          要運(yùn)行ftp4j library,你需要Java 運(yùn)行時(shí)環(huán)境v. 1.4+.

          安裝

          將ftp4j JAR文件添加到你應(yīng)用程序的classpath中, 然后你就可以自動(dòng)啟用ftp4j類的使用了.

          Javadocs

          可參考ftp4j javadocs.

          快速入門

          包中的主類是FTPClient (it.sauronsoftware.ftp4j.FTPClient).

          創(chuàng)建一個(gè)FTPClient 實(shí)例:

          FTPClient client = new FTPClient();

          連接遠(yuǎn)程FTP服務(wù):

          client.connect("ftp.host.com");

          如果服務(wù)端口不是標(biāo)準(zhǔn)的21端口 (或 FTPS的990端口),需要使用port參數(shù)進(jìn)行指定:

          client.connect("ftp.host.com", port);

          如:

          client.connect("ftp.host.com", 8021);

          然后進(jìn)行登錄流程:

          client.login("carlo", "mypassword");

          如果沒有拋出任何異常的話,那么你就通過遠(yuǎn)程服務(wù)器的認(rèn)證了.否則,如果驗(yàn)證失敗,你將會(huì)收到it.sauronsoftware.ftp4j.FTPException異常.

          匿名認(rèn)證,如果被連接服務(wù)認(rèn)可的話, 可通過發(fā)送用戶名"anonymous" 和任意密碼來完成(注意,有些服務(wù)器需要e-mail地址來代替密碼):

          client.login("anonymous", "ftp4j");

          使用遠(yuǎn)程FTP服務(wù)來做任何事情,然后再斷開連接:

          client.disconnect(true);

          這會(huì)向遠(yuǎn)程器發(fā)送FTP QUIT命令, 以進(jìn)行一個(gè)合法斷開流程.如果你只是想中斷連接而不想向服務(wù)器發(fā)送任何通知,那么可以使用:

          client.disconnect(false);

          使用代理進(jìn)行連接

          客戶端通過連接器(一個(gè)繼承自it.sauronsoftware.ftp4j.FTPConnector的對(duì)象)來連接服務(wù)器, 它將返回一個(gè)已經(jīng)打開的連接(一個(gè)實(shí)現(xiàn)了it.sauronsoftware.ftp4j.FTPConnection 接口的對(duì)象).這也是為什么ftp4j 可以支持大量代理的原因.

          在連接遠(yuǎn)程服務(wù)器前,客戶端實(shí)例可以使用setConnector() 方法來設(shè)置連接器:

          client.setConnector(anyConnectorYouWant);

          如果沒有設(shè)置連接器的話,會(huì)使用默認(rèn)的連接器DirectConnector (it.sauronsoftware.ftp4j.connectors.DirectConnector), 它實(shí)現(xiàn)了對(duì)遠(yuǎn)程服務(wù)器的直接連接,且不會(huì)使用代理。

          如果你只能通過代理來連接遠(yuǎn)程服務(wù)器, ftp4j包可以讓你在下面的連接器中進(jìn)行選擇:

          • HTTPTunnelConnector (it.sauronsoftware.ftp4j.connectors.HTTPTunnelConnector)
            它可以通過HTTP代理來進(jìn)行連接,并支持CONNECT方法.
          • FTPProxyConnector (it.sauronsoftware.ftp4j.connectors.FTPProxyConnector)
            它可以通過FTP代理進(jìn)行連接,支持SITE和OPEN命令風(fēng)格的苛刻遠(yuǎn)程主機(jī)連接.其它類型的FTP代理,需要username@remotehost 認(rèn)證,且可以不使用連接器,因?yàn)樗鼈儗?duì)于客戶端來說是透明的。
          • SOCKS4Connector (it.sauronsoftware.ftp4j.connectors.SOCKS4Connector)
            它可以通過SOCKS 4/4a代理進(jìn)行連接.
          • SOCKS5Connector (it.sauronsoftware.ftp4j.connectors.SOCKS5Connector)
            它可以通過SOCKS 5代理進(jìn)行連接.

          因?yàn)閒tp4j的連接器架構(gòu)設(shè)計(jì)為可插拔的,因此你可以繼承FTPConnector 抽象類來構(gòu)建自己的連接器.

          FTPS/FTPES 安全連接

          ftp4j包支持FTPS (隱式基于 TLS/SSL的FTP) 和FTPES (顯示基于TLS/SSL的FTP).

          setSecurity() 方法可用來打開這種特性:

          client.setSecurity(FTPClient.SECURITY_FTPS); // 啟用 FTPS
          client.setSecurity(FTPClient.SECURITY_FTPES); // 啟用 FTPES

          兩個(gè)方法都需要在連接遠(yuǎn)程服務(wù)器前調(diào)用.

          如果安全協(xié)議設(shè)置成了SECURITY_FTPS, 則connect() 方法默認(rèn)使用的端口為990.

          默認(rèn)情況下,客戶端對(duì)象商討SSL連接會(huì)使用javax.net.ssl.SSLSocketFactory.getDefault()作為其套接字工廠.可通過調(diào)用client.setSSLSocketFactory()方法來改變默認(rèn)套接字工廠. 另外一種SSLSocketFactory, 可用來信任遠(yuǎn)程服務(wù)器頒發(fā)的證書(謹(jǐn)慎使用):

          import it.sauronsoftware.ftp4j.FTPClient;
          import java.security.KeyManagementException;
          import java.security.NoSuchAlgorithmException;
          import java.security.SecureRandom;
          import java.security.cert.X509Certificate;
          import javax.net.ssl.SSLContext;
          import javax.net.ssl.SSLSocketFactory;
          import javax.net.ssl.TrustManager;
          import javax.net.ssl.X509TrustManager;
          // ... TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {
          public X509Certificate[] getAcceptedIssuers() { return null;
          }
          public void checkClientTrusted(X509Certificate[] certs, String authType) { }
          public void checkServerTrusted(X509Certificate[] certs, String authType) { } } };
          SSLContext sslContext = null;
          try { sslContext = SSLContext.getInstance("SSL");
          sslContext.init(null, trustManager, new SecureRandom());
          } catch (NoSuchAlgorithmException e) {
          e.printStackTrace();
          } catch (KeyManagementException e) {
          e.printStackTrace();
          }
          SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
          FTPClient client = new FTPClient();
          client.setSSLSocketFactory(sslSocketFactory);
          client.setSecurity(FTPClient.SECURITY_FTPS);
          // or client.setSecurity(FTPClient.SECURITY_FTPES); // ...

          瀏覽遠(yuǎn)程站點(diǎn)

          獲取當(dāng)前目錄的的絕對(duì)路徑(此目錄是FTP服務(wù)器的home目錄):

          String dir = client.currentDirectory();

          改變目錄:

          client.changeDirectory(newPath);

          你可以使用絕對(duì)路徑和相對(duì)路徑:

          client.changeDirectory("/an/absolute/one"); 
          client.changeDirectory("relative");

          回到父目錄:

          client.changeDirectoryUp();

          重命名文件和目錄

          要重命名遠(yuǎn)程文件或目錄:

          client.rename("oldname", "newname");

          移動(dòng)文件和文件家

          rename() 方法也可以用來從當(dāng)前位置移動(dòng)文件或目錄到其它位置.

          在這個(gè)例子子,假設(shè)在當(dāng)前工作目錄中,你有一個(gè)名為"myfile.txt"的文件,然后你想將其移動(dòng)到子目錄"myfolder"中:

          client.rename("myfile.txt", "myfolder/myfile.txt");

          刪除文件

          要?jiǎng)h除遠(yuǎn)程文件,需要調(diào)用:

          client.deleteFile(relativeOrAbsolutePath);

          在這個(gè)例子中:

          client.deleteFile("useless.txt");

          創(chuàng)建、刪除目錄

          如果遠(yuǎn)程服務(wù)給你機(jī)會(huì)的話,你可以在遠(yuǎn)程站點(diǎn)上創(chuàng)建新目錄:

          client.createDirectory("newfolder");

          你也可以已存在的目錄:

          client.deleteDirectory(absoluteOrRelativePath);

          在這個(gè)例子中:

          client.deleteDirectory("oldfolder");

          請(qǐng)注意,通常情況下,F(xiàn)TP 服務(wù)器只允許刪除空目錄.

          列出文件、目錄、連接

          FTP 協(xié)議并不會(huì)提供大量支持方法來獲取工作目錄的完整信息.通常LIST命令會(huì)給你想知道的東西,但不辛的是,每個(gè)服務(wù)器會(huì)使用不同樣式的響應(yīng). 這意味著某些服務(wù)器會(huì)返回UNIX樣式的目錄,有些服務(wù)器會(huì)返回DOS樣式的目錄,其它的服務(wù)器又會(huì)使用別的樣式.

          ftp4j 包可以處理許多的LIST響應(yīng)格式, 并將它們構(gòu)建成統(tǒng)一目錄內(nèi)容的結(jié)構(gòu)對(duì)象表示.當(dāng)前ftp4j可以處理:

          • UNIX 樣式及其變種(如MAC樣式)
          • DOS 樣式
          • NetWare 樣式
          • EPLF
          • MLSD

          這可以通過使用可插拔的parsers來完成.包it.sauronsoftware.ftp4j.listparsers包含了用于處理上述樣式的對(duì)象.大多數(shù)時(shí)間,這些已經(jīng)夠用了。

          要列出當(dāng)前工作目錄下的文件或文件夾,可調(diào)用:

          FTPFile[] list = client.list();

          如果你收到了FTPListParseException (it.sauronsoftware.ftp4j.FTPListParseException) 異常,這就意味著服務(wù)器對(duì)LIST命令返回了不可理解的樣式,即它不是上述列出的樣式.因此,你可以嘗試使用listNames() 方法, 但它并不如list()方法有優(yōu)勢(shì).。為了彌補(bǔ)這種缺陷,你可以構(gòu)建你自己的LIST響應(yīng)解析器,以支持你遇到的樣式.你可以實(shí)現(xiàn)FTPListParser (it.sauronsoftware.ftp4j.FTPListParser) 接口,然后你可以在client的addListParser()方法使用此實(shí)現(xiàn).

          FTPFile (it.sauronsoftware.ftp4j.FTPFile) 對(duì)象提供了目錄內(nèi)容的表示,包括文件,子目錄和連接. 根據(jù)服務(wù)器的響應(yīng),F(xiàn)TPFile對(duì)象的某些字段可以是null 的或者是無意義的.請(qǐng)檢查javadocs來了解細(xì)節(jié).

          list() 方法中你也可以使用文件過濾參數(shù),如:

          FTPFile[] list = client.list("*.jpg");

          如果連接服務(wù)器明確支持MLSD命令, ftp4j會(huì)用其來代替基本的LIST命令。MLSD的響應(yīng)事實(shí)更為標(biāo)準(zhǔn),準(zhǔn)確,更易解析.不幸的是,不是所有服務(wù)器都支持這個(gè)命令,并且有些服務(wù)器支持得非常糟糕.基于這些理由,開發(fā)者可以控制ftp4j是否應(yīng)該使用MLSD命令,即通過調(diào)用FTPClient對(duì)象的setMLSDPolicy()方法. 合法的值:

          • FTPClient.MLSD_IF_SUPPORTED
            client只在服務(wù)器明確支持MLSD命令時(shí),才使用MLSD命令. 這是ftp4j默認(rèn)的行為.

          • FTPClient.MLSD_ALWAYS
            client總是會(huì)使用MLSD命令, 即便服務(wù)器沒有明確表明支持MLSD命令.

          • FTPClient.MLSD_NEVER
            client絕不使用MLSD命令,即便服務(wù)器明確表明支持MLSD命令.

          例如:

          client.setMLSDPolicy(FTPClient.MLSD_NEVER);

          獲取文件、目錄的最后修改時(shí)間

          通常情況下FTPFile對(duì)象會(huì)告訴你條目的最后修改時(shí)間, 但正如上面描述的,這依賴于服務(wù)器發(fā)回的響應(yīng).如果你需要最后的修改時(shí)間,但你又不能通過list()方法得到,那么可以嘗試這樣做:

          java.util.Date md = client.modifiedDate("filename.ext");

          下載、上傳文件

          下載遠(yuǎn)程文件最簡(jiǎn)單的方式是調(diào)用download(String, File) 方法:

          client.download("remoteFile.ext", new java.io.File("localFile.ext"));

          要上傳:

          client.upload(new java.io.File("localFile.ext"));

          要在已有文件中上傳追加內(nèi)容:

          client.append(new java.io.File("localFile.ext"));

          這些是阻塞式調(diào)用:它們會(huì)在傳輸完成后(或failed, 或 aborted時(shí))才返回. 此外同步鎖是否由客戶端來實(shí)施的,因?yàn)樵诿總€(gè)時(shí)間段內(nèi)只允許有一個(gè)常規(guī)的FTP通信.在每個(gè)時(shí)間段內(nèi),你可以處理多個(gè)傳輸器,即使用多個(gè)FTPClient 對(duì)象,每個(gè)都與服務(wù)器建立一個(gè)私有連接.

          你可以使用FTPDataTransferListener (it.sauronsoftware.ftp4j.FTPDataTransferListener)對(duì)象來監(jiān)控傳輸.你可以自己實(shí)現(xiàn)一個(gè):

          import it.sauronsoftware.ftp4j.FTPDataTransferListener;  
          public class MyTransferListener implements FTPDataTransferListener {
          public void started() {
          // Transfer started
          }
          public void transferred(int length) {
          // Yet other length bytes has been transferred since the last time this
          // method was called
          }
          public void completed() {
          // Transfer completed
          }
          public void aborted() {
          // Transfer aborted
          }
          public void failed() {
          // Transfer failed
          }
          }

          現(xiàn)在像下面這樣來下載或上傳:

          client.download("remoteFile.ext", new java.io.File("localFile.ext"), new MyTransferListener());
          client.upload(new java.io.File("localFile.ext"), new MyTransferListener());
          client.append(new java.io.File("localFile.ext"), new MyTransferListener());

          當(dāng)client處理下載或上傳時(shí),傳輸器可以被同一個(gè)FTPClient對(duì)象的不同線程通過調(diào)用 abortCurrentDataTransfer() 方法aborted. 此方法還需要一個(gè)boolean參數(shù):true表示執(zhí)行合法的abort過程(即向服務(wù)器發(fā)送ABOR命令), false表示實(shí)然關(guān)閉傳輸器,而不向服務(wù)器發(fā)送通知:

          client.abortCurrentDataTransfer(true); // Sends ABOR
          client.abortCurrentDataTransfer(false); // Breaks abruptly

          需要注意的是,list()和listNames() 方法暗中包含了數(shù)據(jù)傳輸器,因abortCurrentDataTransfer() 方法也可以用來中斷其list過程.

          當(dāng)數(shù)據(jù)傳輸器在download()upload()append()list() and listNames() 方法中中斷時(shí),將會(huì)拋出FTPAbortedException (it.sauronsoftware.ftp4j.FTPAbortedException).

          下載和上傳操作可通過restartAt 參數(shù)來重新恢復(fù):

          client.download("remoteFile.ext", new java.io.File("localFile.ext"), 1056);

          此操作會(huì)文件的第1056個(gè)字節(jié)處繼續(xù)執(zhí)行下載操作。第一個(gè)傳輸?shù)淖止?jié)將是第1057個(gè).

          其它 download()upload() 和append()方法的變種可以讓你使用流來替代java.io.File對(duì)象.因此你可以在數(shù)據(jù)庫,網(wǎng)絡(luò)連接或其它流上來傳輸數(shù)據(jù)。

          Active 、 passive 數(shù)據(jù)傳輸模式

          客戶端和服務(wù)器之間的數(shù)據(jù)傳輸通道是通過單獨(dú)的網(wǎng)絡(luò)連接來建立的. 在傳輸通道建立期間,服務(wù)器可以是active或passive的. 服務(wù)器激活數(shù)據(jù)傳輸時(shí),工作如下:

          1. client向服務(wù)器發(fā)送其IP地址和端口號(hào).
          2. client向服務(wù)器發(fā)送數(shù)據(jù)傳輸請(qǐng)求,并在之前發(fā)送的端口上啟動(dòng)監(jiān)聽.
          3. 服務(wù)器使用客戶端提供的地址和端口來連接客房端.
          4. 數(shù)據(jù)傳輸將在新建立的通道中進(jìn)行.

          active模式需要你的client能夠收到來自服務(wù)器的連接.如果你的client處于防火墻, 代理或這兩者混合之后,那么大部分時(shí)間都會(huì)出現(xiàn)問題,因?yàn)樗荒苁盏絹硗饨绲倪B接. 下面是passive數(shù)據(jù)傳輸模式

          1. client要求服務(wù)器準(zhǔn)備好一個(gè)passive數(shù)據(jù)傳輸.
          2. 服務(wù)器使用其IP地址和端口號(hào)進(jìn)行響應(yīng).
          3. client請(qǐng)求傳輸和連接.
          4. 數(shù)據(jù)傳輸將在新建立的通道中進(jìn)行.

          在passive模式中,客戶端連接不要求能收到服務(wù)器的連接請(qǐng)求.

          在ftp4j中,你可以使用下面的調(diào)用來切換active、passive模式:

          client.setPassive(false); // Active mode
          client.setPassive(true); // Passive mode

          ftp4j client passive 標(biāo)志的默認(rèn)值為true: 如果你沒有調(diào)用setPassive(false) ,你的客戶端在每次傳輸前,都會(huì)向服務(wù)器請(qǐng)求passive模式.

          當(dāng)使用 passive文件傳輸時(shí),服務(wù)器會(huì)提供一個(gè) IP地址和一個(gè)端口號(hào).作為FTP規(guī)范的client,需要使用給定的主機(jī)號(hào)和端口進(jìn)行連接.在商業(yè)環(huán)境中,這種行為可能會(huì)經(jīng)常帶來問題,因?yàn)镹AT配置可能會(huì)阻止對(duì)IP地址的連接.這就是為什么FTP clients通常會(huì)忽略服務(wù)器返回的任何IP地址,進(jìn)而在通信線路中使用同樣的主機(jī)來連接服務(wù)器.ftp4j的行為依賴于服務(wù)器因素:

          • 每個(gè)FTPConnector 都有其默認(rèn)行為.大部分連接器都會(huì)忽略服務(wù)器返回的IP地址。目前,默認(rèn)使用返回地址的連接器是FTPProxyConnector.
          • 連接器的行為可通過定義名為ftp4j.passiveDataTransfer.useSuggestedAddress的系統(tǒng)屬性來覆蓋。如果設(shè)置為"true", "yes" 或"1",所有連接器都會(huì)使用服務(wù)器返回的地址,反之,如果將其設(shè)置為"false", "no" or "0", 所有服務(wù)器都不會(huì)使用返回的地址.
          • 最后,連接器的默認(rèn)行為和全局設(shè)置都可以在任何連接器實(shí)例中進(jìn)行覆蓋。你可通過獲取客房端連接器,并調(diào)用其setUseSuggestedAddressForDataConnections() 方法來達(dá)到目的.

          在active傳輸模式中,可以設(shè)置下面的系統(tǒng)屬性:

          • ftp4j.activeDataTransfer.hostAddress
            主機(jī)地址.當(dāng)服務(wù)器請(qǐng)求執(zhí)行與客戶端連接時(shí),client會(huì)跳轉(zhuǎn)到給定地址的服務(wù)器. 此值應(yīng)該是一個(gè)有效的IPv4地址,如:178.12.34.167. 如果沒有提供此值,客戶端會(huì)自動(dòng)解析系統(tǒng)地址.但如果client運(yùn)行于LAN中,為了激活數(shù)據(jù)傳輸,將會(huì)使用帶端口轉(zhuǎn)發(fā)的路由器來連接外部服務(wù)器,那么自動(dòng)探測(cè)到的地址可能不是正確的. 當(dāng)系統(tǒng)有多個(gè)網(wǎng)絡(luò)接口時(shí),也可能發(fā)生這種情況.通常使用系統(tǒng)屬性,可以解決這種問題

          • ftp4j.activeDataTransfer.portRange
            連接端口范圍. client會(huì)在其中挑選一個(gè)來進(jìn)行數(shù)據(jù)傳輸.此值 必須是start-stop 形式 ,如6000-7000 表示client只會(huì)在給定范圍內(nèi)挑選一個(gè)端口來連接服務(wù)器.默認(rèn)情況下沒有指定端口范圍:這表示client會(huì)挑選任何一個(gè)可用的端口.

          • ftp4j.activeDataTransfer.acceptTimeout
            以毫秒為單位的連接超時(shí)時(shí)間. 如果服務(wù)器不能在給定超時(shí)時(shí)間內(nèi)連接client,傳輸會(huì)因FTPDataTransferException異常而中斷.0值表示永不超時(shí)。默認(rèn)值30000 (即30秒).

          要設(shè)置系統(tǒng)屬性,你可以:

          • 用一個(gè)或多個(gè) -Dproperty=value參數(shù)來啟動(dòng)JVM.如:

            java -Dftp4j.activeDataTransfer.hostAddress=178.12.34.167      -Dftp4j.activeDataTransfer.portRange=6000-7000      -Dftp4j.activeDataTransfer.acceptTimeout=5000 MyClass
          • 直接在代碼中設(shè)置系統(tǒng)屬性,如:

            System.setProperty("ftp4j.activeDataTransfer.hostAddress", "178.12.34.167"); 
            System.setProperty("ftp4j.activeDataTransfer.portRange", "6000-7000");
            System.setProperty("ftp4j.activeDataTransfer.acceptTimeout", "5000");

          二進(jìn)制和文本數(shù)據(jù)傳輸類型

          數(shù)據(jù)傳輸?shù)牧硪粋€(gè)核心概念是binary 和textual 類型.當(dāng)傳傳輸?shù)奈募嵌M(jìn)制文件時(shí),它將視為二進(jìn)制流,服務(wù)器會(huì)按原樣存儲(chǔ)。而文本數(shù)據(jù)傳輸會(huì)將傳輸?shù)奈募暈樽址?會(huì)進(jìn)行字符集轉(zhuǎn)換. 假設(shè)你的client正運(yùn)行Windows平臺(tái)上,而服務(wù)器運(yùn)行UNIX上,它們的默認(rèn)字符集通常是不同的. client以文本類型來發(fā)送文件時(shí),client會(huì)假設(shè)文件是按機(jī)器標(biāo)準(zhǔn)字符集來編碼的,因此在發(fā)送前,它會(huì)解碼每個(gè)字符并將其編碼為 中間字符集. 服務(wù)器收到流后,在存儲(chǔ)前,會(huì)解碼中間字符集,并將其編碼為機(jī)器默認(rèn)的字符集.字節(jié)雖然被改變了,但內(nèi)容是相同的。

          你可以調(diào)用下面的方法選擇你傳輸?shù)念愋?

          client.setType(FTPClient.TYPE_TEXTUAL);
          client.setType(FTPClient.TYPE_BINARY);
          client.setType(FTPClient.TYPE_AUTO);

          默認(rèn)的TYPE_AUTO常量 ,會(huì)讓client自動(dòng)來挑選類型:如果文件的擴(kuò)展名是client能被識(shí)別的文本類型標(biāo)記,那么它會(huì)選擇文本傳輸器來執(zhí)行. 文件擴(kuò)展名是通過FTPTextualExtensionRecognizer (it.sauronsoftware.ftp4j.FTPTextualExtensionRecognizer) 實(shí)例來識(shí)別的. 默認(rèn)擴(kuò)展識(shí)別器是it.sauronsoftware.ftp4j.recognizers.DefaultTextualExtensionRecognizer, 會(huì)將下面的視為文本類型:

          abc acgi aip asm asp c c cc cc com conf cpp
          csh css cxx def el etx f f f77 f90 f90 flx
          for for g h h hh hh hlb htc htm html htmls
          htt htx idc jav jav java java js ksh list
          log lsp lst lsx m m mar mcf p pas php pl pl
          pm py rexx rt rt rtf rtx s scm scm sdml sgm
          sgm sgml sgml sh shtml shtml spc ssi talk
          tcl tcsh text tsv txt uil uni unis uri uris
          uu uue vcs wml wmls wsc xml zsh

          你可通過實(shí)現(xiàn)FTPTextualExtensionRecognizer 接口來實(shí)現(xiàn)你自己的識(shí)別器,但你可以更喜歡使用 class ParametricTextualExtensionRecognizer(it.sauronsoftware.ftp4j.recognizers.ParametricTextualExtensionRecognizer)便利類.
          無論如何,都不要忘記將你的識(shí)別器設(shè)置在client中:

          client.setTextualExtensionRecognizer(myRecognizer);

          數(shù)據(jù)傳輸壓縮

          有些服務(wù)器支持?jǐn)?shù)據(jù)傳輸壓縮特性-MODE Z. 在傳輸大文件時(shí),此特性可以節(jié)省帶寬.一旦client連上服務(wù)器并通過認(rèn)證,就可通過調(diào)用下面的方法來檢查是否支持壓縮:

          boolean compressionSupported = client.isCompressionSupported();

          如果服務(wù)器端支持壓縮,就可通過下面的調(diào)用來啟用壓縮:

          client.setCompressionEnabled(true);

          在此調(diào)用之后,后續(xù)的數(shù)據(jù)傳輸(下載,上傳,列舉操作)都被將壓縮以節(jié)省帶寬.

          數(shù)據(jù)傳輸壓縮可通過下面的調(diào)用來禁用:

          client.setCompressionEnabled(false);

          也可以檢查標(biāo)記值:

          boolean compressionEnabled = client.isCompressionEnabled();

          請(qǐng)注意:壓縮數(shù)據(jù)傳輸只當(dāng)壓縮支持且啟用了的情況下才會(huì)發(fā)生.

          默認(rèn)情況下,壓縮是禁用的,即便是服務(wù)器支持壓縮. 如果有需要,可以顯示地打開.

          不做任何事(NOOPing the server)

          假設(shè)你的client什么事情都不做,因?yàn)樵诘却脩糨斎? 通常情況下, FTP服務(wù)器會(huì)自動(dòng)斷開非活躍客戶端. 為了避免超時(shí),你可以發(fā)送NOOP命令.
          此命令不會(huì)做任何事情,但它會(huì)向服務(wù)器說明:客戶端仍然還活著,請(qǐng)重圍超時(shí)計(jì)數(shù)器.調(diào)用如下:

          client.noop();

          當(dāng)非活躍超時(shí)發(fā)生時(shí),客戶端也可以自動(dòng)發(fā)送NOOPs. 默認(rèn)情況下,此特性是禁用的。它可以在 setAutoNoopTimeout() 方法中設(shè)置超時(shí)時(shí)間時(shí)啟用,并提供一個(gè)毫秒為單位的值.如:

          client.setAutoNoopTimeout(30000);

          使用此值,client會(huì)在30秒后發(fā)送一個(gè)NOOP命令.

          NOOP超時(shí)可通過設(shè)置小于等于0的值來禁用:

          client.setAutoNoopTimeout(0);

          網(wǎng)站特殊的自定義命令

          你可以像下面這樣來發(fā)送站點(diǎn)特殊命令:

          FTPReply reply = client.sendSiteCommand("YOUR COMMAND");

          你也可以發(fā)送自定義命令:

          FTPReply reply = client.sendCustomCommand("YOUR COMMAND");

          sendSiteCommand() 和 sendCustomCommand() 都會(huì)返回一個(gè)FTPReply (it.sauronsoftware.ftp4j.FTPReply)對(duì)象.使用此對(duì)象,你可以檢查服務(wù)器的響應(yīng)代碼和消息. 
          FTPCodes (it.sauronsoftware.ftp4j.FTPCodes) 接口報(bào)告了一些通用的FTP響應(yīng)代碼,因此你可以使用這些包中的某個(gè)來進(jìn)行匹配.

          異常處理

          ftp4j 包定義了五種類型的異常:

          • FTPException (it.sauronsoftware.ftp4.FTPException
            依賴于方法,這會(huì)報(bào)告拋出了一個(gè)FTP故障.你可以檢查報(bào)告的錯(cuò)誤碼,使用FTPCodes 常量來獲取故障原因的詳細(xì)信息.
          • FTPIllegalReplyException (it.sauronsoftware.ftp4.FTPIllegalReplyException)
            這表示遠(yuǎn)程服務(wù)器使用非法方式進(jìn)行了應(yīng)答, 這與FTP是不兼容的. 這應(yīng)該是非常罕見的.
          • FTPListParseException (it.sauronsoftware.ftp4.FTPListParseException)
            這通常發(fā)生在list()方法,如果服務(wù)器發(fā)回的響應(yīng)不能被客戶端包中現(xiàn)有解析器進(jìn)行的話,就會(huì)拋出此種異常
          • FTPDataTransferException (it.sauronsoftware.ftp4.FTPDataTransferException)
            當(dāng)數(shù)據(jù)傳輸 (downloadupload, but also list and listNames) 因網(wǎng)絡(luò)連接錯(cuò)誤失敗時(shí),就會(huì)拋出此種異常.
          • FTPAbortedException (it.sauronsoftware.ftp4.FTPAbortedException)
            當(dāng)數(shù)據(jù)傳輸 (download, upload, but also list and listNames) 因客戶端發(fā)出中斷請(qǐng)求失敗時(shí),拋出的異常.
          posted @ 2016-06-21 22:34 胡小軍 閱讀(6013) | 評(píng)論 (0)編輯 收藏
          僅列出標(biāo)題
          共5頁: 上一頁 1 2 3 4 5 下一頁 
          主站蜘蛛池模板: 宜春市| 长兴县| 鲜城| 白水县| 卓尼县| 株洲市| 葫芦岛市| 曲阜市| 伽师县| 大石桥市| 都昌县| 大余县| 长泰县| 稻城县| 广宁县| 章丘市| 丰城市| 朔州市| 磴口县| 石屏县| 长乐市| 绍兴县| 永宁县| 小金县| 辰溪县| 凌源市| 工布江达县| 策勒县| 义马市| 新闻| 淅川县| 久治县| 余江县| 吴川市| 花莲市| 买车| 台东县| 洮南市| 白城市| 土默特左旗| 瑞昌市|