月度归档:2014年01月

wireshark抓包了解tcp建立过程

1. wireshark界面

wireshark是捕获机器上的某一块网卡的网络包,当你的机器上有多块网卡的时候,

2. 选择一个网卡。

点击Caputre->Interfaces.. 出现下面对话框,选择正确的网卡。然后点击"Start"按钮, 开始抓包

3. Wireshark 窗口介绍

WireShark 主要分为这几个界面

1. Display Filter(显示过滤器), 用于过滤

2. Packet List Pane(封包列表), 显示捕获到的封包, 有源地址和目标地址,端口号。 颜色不同,代表

3. Packet Details Pane(封包详细信息), 显示封包中的字段

4. Dissector Pane(16进制数据)

5. Miscellanous(地址栏,杂项)

4. Wireshark 显示过滤

使用过滤是非常重要的, 初学者使用wireshark时,将会得到大量的冗余信息,在几千甚至几万条记录中,以至于很难找到自己需要的部分。搞得晕头转向。

过滤器会帮助我们在大量的数据中迅速找到我们需要的信息。

过滤器有两种,

一种是显示过滤器,就是主界面上那个,用来在捕获的记录中找到所需要的记录

一种是捕获过滤器,用来过滤捕获的封包,以免捕获太多的记录。 在Capture -> Capture Filters 中设置

保存过滤

在Filter栏上,填好Filter的表达式后,点击Save按钮, 取个名字。比如"Filter 102",

Filter栏上就多了个"Filter 102" 的按钮。

过滤表达式的规则

表达式规则

1. 协议过滤

比如TCP,只显示TCP协议。

2. IP 过滤

比如 ip.src ==192.168.1.102 显示源地址为192.168.1.102,

ip.dst==192.168.1.102, 目标地址为192.168.1.102

3. 端口过滤

tcp.port ==80, 端口为80的

tcp.srcport == 80, 只显示TCP协议的愿端口为80的。

4. Http模式过滤

http.request.method=="GET", 只显示HTTP GET方法的。

5. 逻辑运算符为 AND/ OR

常用的过滤表达式

过滤表达式

用途

http

只查看HTTP协议的记录

ip.src ==192.168.1.102 or ip.dst==192.168.1.102

源地址或者目标地址是192.168.1.102

封包列表(Packet List Pane)

封包列表的面板中显示,编号,时间戳,源地址,目标地址,协议,长度,以及封包信息。 你可以看到不同的协议用了不同的颜色显示。

你也可以修改这些显示颜色的规则, View ->Coloring Rules.

封包详细信息 (Packet Details Pane)

这个面板是我们最重要的,用来查看协议中的每一个字段。

各行信息分别为

Frame: 物理层的数据帧概况

Ethernet II: 数据链路层以太网帧头部信息

Internet Protocol Version 4: 互联网层IP包头部信息

Transmission Control Protocol: 传输层T的数据段头部信息,此处是TCP

Hypertext Transfer Protocol: 应用层的信息,此处是HTTP协议

5. wireshark与对应的OSI七层模型

6. TCP包的具体内容

从下图可以看到wireshark捕获到的TCP包中的每个字段。

三次握手过程为

这图我都看过很多遍了, 这次我们用wireshark实际分析下三次握手的过程。

打开wireshark, 打开浏览器输入 http://www.cr173.com

在wireshark中输入http过滤, 然后选中GET /tankxiao HTTP/1.1的那条记录,右键然后点击"Follow TCP Stream",

这样做的目的是为了得到与浏览器打开网站相关的数据包,将得到如下图

图中可以看到wireshark截获到了三次握手的三个数据包。第四个包才是HTTP的, 这说明HTTP的确是使用TCP建立连接的。

第一次握手数据包

客户端发送一个TCP,标志位为SYN,序列号为0, 代表客户端请求建立连接。 如下图

第二次握手的数据包

服务器发回确认包, 标志位为 SYN,ACK. 将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即0+1=1, 如下图

第三次握手的数据包

客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1, 如下图:

就这样通过了TCP三次握手,建立了连接

tcptraceroute 、Traceroute和tracert

1 Traceroute

Traceroute最简单的基本用法是:traceroute hostname

Traceroute 程序的设计是利用ICMP及IP header的TTL(Time To Live)栏位(field)。首先,traceroute送出一个TTL是1 的IP datagram(其实,每次送出的为3个40字节的包,包括源地址,目的地址和包发出的时间标签)到目的地,当路径上的第一个路由器 (router)收到这个datagram时,它将TTL减1。此时,TTL变为0了,所以该路由器会将此datagram丢掉,并送回一个 「ICMP time exceeded」消息(包括发IP包的源地址,IP包的所有内容及路由器的IP地址),traceroute 收到这个消息后, 便知道这个路由器存在于这个路径上,接着traceroute 再送出另一个TTL是2 的datagram,发现第2 个路由 器...... traceroute 每次将送出的datagram的TTL 加1来发现另一个路由器,这个重复的动作一直持续到某个 datagram 抵达目的地。当datagram到达目的地后,该主机并不会送回ICMP time exceeded消息,因为它已是目的地了,那么 traceroute如何得知目的地到达了呢?

Traceroute 在送出UDP datagrams到目的地时,它所选择送达的port number 是一个一般应用程序都不会用的号码(30000 以上),所以当此 UDP datagram 到达目的地后该主机会送回一个「ICMP port unreachable」的消息,而当traceroute 收到这个消 息时,便知道目的地已经到达了。所以traceroute 在Server端也是没有所谓的Daemon 程式。

Traceroute提取发 ICMP TTL到期消息设备的IP地址并作域名解析。每次 ,Traceroute都打印出一系列数据,包括所经过的路由设备的域名及 IP地址,三个包每次来回所花时间。

完整命令选项:

traceroute [-46dFITUnreAV] [-f first_ttl] [-g gate,...]

[-i device] [-m max_ttl] [-p port] [-s src_addr]

[-q nqueries] [-N squeries] [-t tos]

[-l flow_label] [-w waittime] [-z sendwait]

[-UL] [-P proto] [--sport=port] [-M method] [-O mod_options]

[--mtu] [--back]

host [packet_len]

traceroute6 [options]

tcptraceroute [options]

lft [options]

2. tcptraceroute

traceroute6 等价于 traceroute -6。

tcptraceroute 等价于 traceroute -T。

lft:第四层traceroute,执行tcptraceroute,像 traceroute -T,但尝试提供兼容原始的这类实现。

常用选项

-4, -6:显示强制使用IPv4或IPv6。

-I:使用ICMP ECHO进行探测。

-T:使用TCP SYN进行探测。

-F:不要分帧探测包。

-f first_ttl:指定TTL的起始值,默认是1。

-i interface:从指定网络接口来发送探测包。

-m max_ttl:指定TTL的最大值,默认是30。

-N squeries:指定同时发送的探测包数量,默认是16。

-n:不要进行域名解析,以数字形式显示地址。

-w waittime:设置等待探测响应的时间,单位秒,默认是5s。

-q nqueries:设置每个hop(路由跳)的探测包数量,默认是3。

-s source_addr:设置探测包的来源IP地址。

-r:跳过普通的路由表,直接将数据包发送到远程主机。

--mtu:发现跟踪路径上的最大传输单元大小(MTU)

--back:打印返回路径的hop(路由跳),如果看起来跟去的方向的不一样。

3. tracert

tracert 是windows下的traceroute。

用法: tracert [-d] [-h maximum_hops] [-j host-list] [-w timeout] target_name

选项说明

-d:以数字形式显示地址,不进行域名解析。

-h maximum_hops:指定经过的最大路由跳数量。

-j host-list:指定 Tracert 实用程序数据包所采用路径中的路由器接口列表。

-w timeout:等待探测包响应的毫秒数。

target_name:目标主机的名称或 IP 地址。

举例

跟踪我的VPS的路由情况。

<

pre> C:\Documents and Settings\Administrator>tracert coderbee.net

Tracing route to coderbee.net [198.56.238.193] over a maximum of 30 hops:

1 <1 ms <1 ms <1 ms 192.168.203.1 2 5 ms * 5 ms 120.197.59.129 3 2 ms * 3 ms 120.196.2.125 4 2 ms * 51 ms 120.196.0.233 5 6 ms 2 ms 5 ms 221.176.22.201 6 17 ms * 3 ms 221.176.18.114 7 * 4 ms 5 ms 221.176.24.150 8 9 ms * 9 ms 211.136.1.97 9 178 ms 185 ms 187 ms 223.118.2.34 10 185 ms * * he.net.coresite.com [206.223.143.122] 11 184 ms 190 ms 279 ms 64.71.153.34 12 183 ms * * dc03r01bg02.scalabledns.com [199.48.68.42] 13 187 ms 183 ms 192 ms dc03r01bg02.scalabledns.com [199.48.68.42] 14 * 185 ms 189 ms 198.56.238.193

Android App应用之发布各广告平台版本

来源:http://www.cnblogs.com/qianxudetianxia/archive/2012/12/25/2830343.html

Android的广告平台是很多的,各市场对各平台的接受程度是不一样的,Android的开发者如果想集成广告基本要考虑下面两个问题:
(1)集成什么广告,会赚钱?
(2)集成什么广告,不会被市场拒绝?
最终的结果往往是折中的。
第一个问题是广告平台的判断问题,我没有发言权去评论,本文主要是针对第二个问题展开。
解决方案就是打包应用的不同广告平台版本,本文接下来逐一展开相关话题。

1. 基础
本文其实是针对《Android学习系列(6)--App模块化及工程扩展》和《Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包》的一个扩展和应用场景。
所以当然也需要这两篇文章的基础:
(1). Android类库的应用
(2). 因为要结合自动编译各市场版本,所以要更新编译打包的脚本,当然这一步是可选的,如果你愿意手动的话。

2. 广告平台的选择
我以三个平台为例子吧:万普,有米,桔子。
这里我强调的是,我不做广告,我不是要推荐这三个平台,只是以三个平台为例子。同时,我还要骂这些广告平台,拖款,扣量等等。
为什么选择这三个平台?
(1). 万普是单价比较高,应用比较多,收入比较好,很多开发者选择这个平台,但是因为之前推送的用户体验和积分墙问题,被很多市场封杀,很典型。
(2). 有米,宣传做的好,但是普遍反映收入低,banner广告和积分墙是分开的,所以banner部分可以单独拿出来,发布到一些要求严格的市场。
(3). 桔子,暂时也不大了解,有积分墙,貌似打款还算比较积极,很多新平台开始都很积极(随便挑了个新平台陪衬陪衬吧)
有人说,使用聚合呀,能够使用不同的广告平台,我觉得这和本文讨论是完全不同的两个问题,此处省略五千字。

3. 集成架构
在讨论集成架构之前,我们必须讨论一下应用和广告的关联方式:
(1). 多对多的关系,一个广告平台肯定会被嵌入到很多应用中,一个应用往往也需要发布多个广告平台的版本。
(2). 一对多的关系,这一条是和第一条矛盾的,但是,广告平台的sdk本身就是解决了嵌入到各个应用的问题,而且真要考虑多对多的方案,会很麻烦而且无太大价值,所以我们的重点是,一个应用发布多个广告平台的版本。
(3). 先广告,后应用,再后广告平台。意思是,一开始就考虑广告的集成,但是仅仅是空白占位,什么地方显示banner,要不要在哪里显示个应用推荐,留个空方法会接口;然后开

发应用;应用完成之后,在考虑如何集成不同的广告平台,根据不同的平台重写不同的广告占位,或者是以不同的广告形式展示广告。
在这几个考虑的前提下,我画出如下的集成架构图:

image_thumb[1]

这个架构图是系列应用的一个例子,不仅每个应用能发布多个广告版本,而且一个基库能封装定制出多个应用,这应该是一个比较典型的例子。

4. 举例


我已经把这个思路应用在world项目中了,参考地址:https://github.com/openproject/world2
而且经过验证,效果还不错。
当然,具体的广告占位和覆写,我就在此略去,你也可以参考上面地址的代码。

5. 编译的问题
针对不同的广告平台,我们需要对自动编译打包脚本做一些修改。
(1). 三层工程使用ANT编译的问题:R.java文件的生成和资源打包的问题。
强调这个问题,主要是提醒注意一下别搞错了。
R.java的生成,三层工程则要生成3次(build.xml):

        <echo>generating R.java for project to dir gen (using aapt) ... </echo>
        <exec executable="aapt">
            <arg value="package" />
            <arg value="-m" />
            <arg value="-J" />
            <arg value="gen" />
            <arg value="-M" />
            <arg value="AndroidManifest.xml" />
            <arg value="-S" />
            <arg value="res" />
            <arg value="-S" />
            <arg value="../baseworld2.waps/res" />
            <arg value="-S" />
            <arg value="../baseworld2/res" />
            <arg value="-I" />
            <arg value="${android-jar}" />
            <arg value="--auto-add-overlay" />
        </exec>

        <echo>generating R.java for wap library project to dir gen (using aapt) ... </echo>
        <exec executable="aapt">
            <arg value="package" />
            <arg value="-m" />
            <arg value="--non-constant-id" />
            <arg value="--auto-add-overlay" />
            <arg value="-J" />
            <arg value="gen" />
            <arg value="-M" />
            <arg value="../baseworld2.waps/AndroidManifest.xml" />
            <arg value="-S" />
            <arg value="res" />
            <arg value="-S" />
            <arg value="../baseworld2.waps/res" />
            <arg value="-S" />
            <arg value="../baseworld2/res" />
            <arg value="-I" />
            <arg value="${android-jar}" />
        </exec>

        <echo>generating R.java for library to dir gen (using aapt) ... </echo>
        <exec executable="aapt">
            <arg value="package" />
            <arg value="-m" />
            <arg value="--non-constant-id" />
            <arg value="--auto-add-overlay" />
            <arg value="-J" />
            <arg value="gen" />
            <arg value="-M" />
            <arg value="../baseworld2/AndroidManifest.xml" />
            <arg value="-S" />
            <arg value="res" />
            <arg value="-S" />
            <arg value="../baseworld2.waps/res" />
            <arg value="-S" />
            <arg value="../baseworld2/res" />
            <arg value="-I" />
            <arg value="${android-jar}" />
        </exec>

资源打包也是如此,要特别注意资源文件夹的顺序:

        <echo>packaging resource (include res, assets, AndroidManifest.xml, etc.) to res.zip ... </echo>
        <exec executable="aapt">
            <arg value="package" />
            <arg value="-f" />
            <arg value="-M" />
            <arg value="AndroidManifest.xml" />
            <arg value="-S" />
            <arg value="res" />
            <arg value="-S" />
            <arg value="../baseworld2.waps/res" />
            <arg value="-S" />
            <arg value="../baseworld2/res" />
            <arg value="-A" />
            <arg value="assets" />
            <arg value="-I" />
            <arg value="${android-jar}" />
            <arg value="-F" />
            <arg value="bin/res.zip" />
            <arg value="--auto-add-overlay" />
        </exec>

(2). 多渠道打包编译脚本(build.sh)

#!/bin/bash

# get the file parent dir
basedir=$(cd "$(dirname "$0")";pwd)

# get project name by dir name
project=$(echo $basedir | awk -F "/" '{print $NF}')
project=${project/\.*/}

# enter the right parent dir
cd $basedir

#markets="waps google official appchina gfan qq nduo feiliu 3g 360 zhuamob baidu sohu 163 samsung coolmart meizu moto liantong iandroid imobile xiaomi nearme dev"
markets="mumayi eo dev"
for market in $markets
do
    echo packaging $project.v2.1_$market.apk ...
    sed -i "s/\(android:value=\)\"\(.*\)\"\( android:name=\"UMENG_CHANNEL\"\)/\1\"$market\"\3/g" AndroidManifest.xml
    sed -i "s/\(android:value=\)\"\(.*\)\"\( android:name=\"YOUMI_CHANNEL\"\)/\1\"$market\"\3/g" AndroidManifest.xml
    sed -i "s/\(android:value=\)\"\(.*\)\"\( android:name=\"WAPS_PID\"\)/\1\"$market\"\3/g" AndroidManifest.xml
    ant -Dapk-name=$project -Dapk-version=v2.1 -Dapk-market=$market
done

这两个脚本是放在app.waps下执行,为了通用方便,可以放在某个地方,然后使用ln命令建立一个软链接也可以。

6. 小结

本文可以说是炒剩饭,新瓶装旧酒,所以,很多地方都是一言带过,如果觉得思维有些跳跃的话,呵呵,请阅读前面相关的文章。

本文就是用之前的知识解决把应用打包多个广告平台版本的问题,分享了一些注意的地方,希望有所帮助。

activemq的消息存储机制

一.KahaDB简介

ActiveMQ 5.3以后,出现了KahaDB。她是一个基于文件支持事务的消息存储器,是一个可靠,高性能,可扩展的消息存储器。
她的设计初衷就是使用简单并尽可能的快。KahaDB的索引使用一个transaction log,并且所有的destination只使用一个index,有人测试表明:如果用于生产环境,支持1万个active connection,每个connection有一个独立的queue。该表现已经足矣应付大部分的需求。
KahaDB内部分为:data logs, 按照Message ID高度优化的索引,memory message cache。

data logs扮演一个message journal,存储消息和命令。当大小超过了规定,将会新建一个data log
.所有在data log里的消息是引用计数的,所以当一个log里的消息不在需要了,这里在添加一句, 经过测试是这个文件中全部消息都可以不再需要了, 才进行后续操作, 假若有一条消息还需要保留, 则整个文件都是被保留的, 因此若是, 您万一多个或者全部日志文件里面都有少量消息需要保存, 这样会造成日志的无法删除,在最坏的情况下, 若是配置了相关的kahadb的相关流控(其实也是磁盘容量,内存容量等的限制),若是日志文件长期无法删除, 会造成生产者无法再发消息(由于磁盘空间占用等达到限额, 并且日志又不能删除,没有多余磁盘空间,因此在配置正确情况下, 客户端会接受到相关的异常信息, 配置不好的, 用户会被挂起,并且没有异常消息的。),可以被删除或者放入archived文件夹。每次消息的写入都是在log的末尾增加记录,所以存储速度很快。
缓存则是临时持有那些有对应消费者在线的消息。如果消费者反馈消息已经成功接收,那么这些消息就不用写入磁盘。
BTree索引,保存消息的引用,并按照message ID排序。Redo log是用来保证MQ broker未干净关闭情况下,用于Btree index的重建。
KahaDB的目录会在你启动MQ后自动创建(使用了KAhaDB作为存储器),

db log files:以db-递增数字.log命名。
archive directory: 当配置支持archiving(默认不支持)并且存在,该文件夹才会创建。用于存储不再需要的data logs。
db.data:存储btree索引
db.redo:用于hard-stop broker后,btree索引的重建

二.KahaDB的异常处理机制

KahaDB 支持多种机制在系统异常关闭后重启并恢复。包括检测数据文件丢失和还原损坏的metadata。这些特性并不能完全保证系统异常关闭不造成消息丢失。如果需要保证系统的高可靠性,建议部署到容灾系统上。例如RAID磁盘阵列中。

当broker正常关闭时, KahaDB message store会将所有的缓存数据刷到文件系统中。尤其是这些数据:
1、所有未处理的日志数据
2、所有缓存的metadata
最后meta store中的信息与journal数据文件中的数据保持一致性。

正常情况下,在系统恢复时优先读取journal中的数据。因为metacache中的索引信息是周期性的更新到meta store中的。当系统异常关闭时,可能journal中有的数据meta store中并没有不存在索引。但是KahaDB在恢复时会先读取meta store中的数据,然后再读取journal有但是meta store不存在的数据(因为KahaDB根据meta store中的索引信息快速定位到metastore没有但是journal文件中包含的数据,然后根据这些数据重新在meta store中建立索引信息)

KahaDB会在更新metadata store之前,保存更新操作的概要信息到重做日志(db.redo)中。减少系统异常关闭时的风险。因为重做日志非常小,所以在系统异常关闭时能快速写入。当系统恢复时会判断重做日志中的信息是否需要更新到metadata中。

如果 metadata store 已被不可挽回的损坏了,可以删除metadata store文件(db.data)来强制恢复;只不过这个时候,broker会读取所有的journal文件来重建metadata store,需要一段比较长的时间。

KahaDB可以检测是否有journal文件丢失,如果有丢失,默认将会抛出一个异常然后关闭。便于管理员调查丢失的journal文件,并手 动还原。可以通过设置ignoreMissingJournalfiles为true,让broker在启动时忽略这些丢失的journal文件。

KahaDB同样可以检测journal文件的完整性。不过这些特性需要明确的配置来启用。

<persistenceAdapter>

<kahaDB directory="activemq-data"

journalMaxFileLength="32mb"

checksumJournalFiles="true"

checkForCorruptJournalFiles="true"

/>

</persistenceAdapter>

三.AMQ Message Store 是什么?

具体参考:http://activemq.apache.org/kahadb.html

默认的activemq消息存储是通过一个所谓的AMQ Message Store来完成。
AMQ Message Store是一个高效的可嵌入支持事务的消息存储解决方案。
在此方案下消息(Message)本身以日志的形式实现持久化,存放在Data Log里。并且还对日志里的消息做了引用索引,方便快速取回Message。

一般情况下消息索引存放于内存(Cache)中,MQ Server定期将索引内容持久化,存放到Reference Store。
Message Data Log文件是有容量限制的,默认是32MB,可自行配置容量。当该Data Log文件里所有消息都被消费完的时候,Data Log文件就会被加上一个标记,通知下一次消息清理时可以被处理掉(处理方式可以是delete或是转移到Achieve目录)。

AMQ Message Store方案中 Cache 、Data Log、Reference Store 协作图如下:

AMQ Message Store的属性是可以配置的,
你可以在conf/activemq.xml配置文件里添加上如下配置:

Xml代码

<persistenceAdapter>

<amqPersistenceAdapter directory="activemq-data" maxFileLength="32mb"/>

</persistenceAdapter>

四.kahadb属性说明

property name

default value

Comments

directory

activemq-data

存储消息文件和日志的目录

useNIO

true

使用 NIO 特性

syncOnWrite

false

同步写文件到磁盘

maxFileLength

32mb

Message Data日志文件的最大 Size

persistentIndex

true

持久化日志索引,如果设为 false ,则在内存中保存

maxCheckpointMessageAddSize

4kb

在自动提交前在事务中能保持的最大消息数

cleanupInterval

30000

每隔多少时间清理不再使用的消息日志(毫秒)

indexBinSize

1024

这个值是用来提升索引的性能的,值越大,索引相对性能越好

indexKeySize

96

index key的size,index key基于message id

indexPageSize

16kb

索引页的size

directoryArchive

archive

消费完的Data Log存放的目录

archiveDataLogs

false

设置为true的话,消费完的Data Log就放到Archive目录,而不是删除。

AMQ Message Store体系中 目录结构参照下图 :

顶层目录broker name 用broker name命名,默认目录名是localhost,broker name在activemq的配置文件里指定,以下是它的子目录:

archive 丢弃的Data Log就放到这里,当archiveDataLogs 属性配置为true时才会存在

journal message data log的所在

kr-store reference store 目录

data 引用索引所在目录

state 记录store的状态

tmp-storage 用来存储一些事物性的消息以减轻内存的负担例如等待正常但是速度很慢的消费端来消费非持久化的Topic.

其他持久化方式 activemq同样支持JDBC持久化Message,我们只需要把配置从

<persistenceAdapter>

<amqPersistenceAdapter directory="activemq-data" maxFileLength="32mb"/>

</persistenceAdapter>

改成AMQ Message Store and JDBC(推荐,同时使用两者可以同时保证效率和可靠性):

<persistenceAdapter>

<journaledJDBC dataDirectory="${activemq.base}/data" dataSource="#oracle-ds"/>

</persistenceAdapter>

<persistenceAdapter>

<jdbcPersistenceAdapter dataSource="#oracle-ds"/>

</persistenceAdapter>

ActiveMQ 中消息并行存储转发

英文文章地址:http://fusesource.com/docs/broker/5.4/persistence/KahaDB-Concurrent.html

Concurrent store and dispatch is a strategy that facilitates high rates of message throughput, provided the consumers are able to keep up with the flow of messages from the broker. By allowing the storing of messages to proceed concurrently with the dispatch of those messages to consumers, it can happen that the consumers return acknowledgments before the messages are ever written to disk. In this case, the message writes can be optimized away, because the dispatch has already completed.

并行存储转发是一种高吞吐量策略,可以让消息消费者跟上消息流的速度。在并行存储和转发的过程中,可能会在消息未持久化到硬盘时,接收到了消息消费的确认 信息,所以在这种情况下就不需要再持久化到硬盘了。值得注意得是,如果使用了jms事务,就不能使用并行储存转发。因为并行存储和转发是不保证消息的一致 性的。

Queue默认是使用并行存储转发的。当然也是可配置的。主要可配置项如下:
concurrentStoreAndDispatchQueues
concurrentStoreAndDispatchTopics
concurrentStoreAndDispatchTransactions

下面解析快慢两种消费者的情况下,并行存储转发的过程;

慢消费者:

1、producer发送一个消息M到destination
2、broker发送消息M到持久层。持久层中是一些负责写入消息到日志中的线程。
3、同时存储和转发。消息可能被转发给一个或者多个消费者。因为消费者较慢,在收到消息消费的确认信息之前,消息将被持久化到日志文件中。
4、收到消息消费的确认信息。
5、broker通知持久层删除日志文件中的消息。
(KahaDB的可回滚日志中,所以消息不会被直接删除,而是会记录一条信息到日子,表示这条消息已消费完成。等到一个日志文件中所有的消息都被消费完成后,才会删除或者归档这个日志文件)

快消费者:

1、producer发送一个消息M到destination
2、broker发送消息M到持久层。持久层中是一些负责写入消息到日志中的线程。
3、同时进行存储和转发。
4、消费者快速返回了消息消费完成的确认信息。
5、但受到所有消费者返回的确认信息后,便会通知持久层删除该消息。此时前面准备去持久化的消息还被阻塞着,这个时候便不再需要写入到持久层中。直接从内存中删除。

如果你要使KahaDB串行存储和转发,必须明确禁用并行存储转发。禁用Queue、topic、Transactions

Xml代码

<broker brokerName="broker" persistent="true" useShutdownHook="false">

...

<persistenceAdapter>

<kahaDB directory="activemq-data"

journalMaxFileLength="32mb"

concurrentStoreAndDispatchQueues="false"

concurrentStoreAndDispatchTopics="false"

concurrentStoreAndDispatchTransactions="false"

/>

</persistenceAdapter>

</broker>

下图展示串行存储和转发:

1、producer发送一个消息M到broker上的某个destination
2、broker发送消息到持久层。因为并行存储转发已关闭,消息将会被立即写入到日志文件中。
3、消息被分发到一个或多个消费者
4、消费者发送消息消费确认信息到broker
5、当broker收到所有消费者的确认信息后,broker通知持久层删除该消息。

为了避免丢失消息,JMS规范要求broker在发送给producer确认接收信息前把消息持久化。如果使用Jms事务中时,会将事务的相关信息也持久化。默认情况下,KahaDB并未如此配置。如果应用需要避免丢失信息,就需要对KahaDB的配置进行修改:
1、配置并行存储转发为false,使用串行存储转发
2、持久化时使用同步写入。配置enableJournalDiskSyncs为true。

另外,使用事务可以提升持久层的效率。因为事务是批量处理消息的。在事务提交之前,所有的消息都不会写入到message store中的。这样就减少了IO,每次事务提交进行一次IO,而不是每条消息进行一次IO。