﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-j2ee绿洲-文章分类-消息队列专题</title><link>http://www.blogjava.net/livery/category/38585.html</link><description>找到属于自己的一片天空</description><language>zh-cn</language><lastBuildDate>Wed, 12 Dec 2012 12:29:02 GMT</lastBuildDate><pubDate>Wed, 12 Dec 2012 12:29:02 GMT</pubDate><ttl>60</ttl><item><title>metamorphosis-11-监控命令</title><link>http://www.blogjava.net/livery/articles/392845.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Wed, 12 Dec 2012 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/392845.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/392845.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/392845.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/392845.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/392845.html</trackback:ping><description><![CDATA[&nbsp;metamorphosis 是通过jmx原理实现的监控,重载配置等服务.所以需要对metaq监控就有两个条件必须满足:<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 &nbsp;使用<span style="font-size: 12px;">metamorphosis-server-wrapper<br />
</span>&nbsp; &nbsp; &nbsp; &nbsp; 2 &nbsp;metaq的java启动参数中必须开启jmx的相关参数(<span style="font-size: 12px;">-Dcom.sun.management.jmxremote )</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;">才能使用metaServer.sh命令监控<br />
stats命令<br />不加任何参数,展示metaq的整个程序的相关参数.<br />
</span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->./metaServer.sh&nbsp;stats</div>
结果<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; "><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->STATS<br />pid&nbsp;16585<br />broker_id&nbsp;1002<br />port&nbsp;8123<br />uptime&nbsp;691558<br />version&nbsp;1.4.3<br />slave&nbsp;false<br />curr_connections&nbsp;3<br />threads&nbsp;134<br />cmd_put&nbsp;1142<br />cmd_get&nbsp;437188<br />cmd_offset&nbsp;0<br />tx_begin&nbsp;0<br />tx_xa_begin&nbsp;0<br />tx_commit&nbsp;0<br />tx_rollback&nbsp;0<br />get_miss&nbsp;437123<br />put_failed&nbsp;0<br />total_messages&nbsp;3192<br />topics&nbsp;14<br />config_checksum&nbsp;3605506033<br />END</div><br />-item参数 &nbsp;brokers<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->./metaServer.sh&nbsp;stats&nbsp;-item&nbsp;brokers</div>结果<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->STATS<br />brokers<br />resultCode&nbsp;1<br />partitions&nbsp;0<br />message_count&nbsp;0<br />bytes&nbsp;0<br />topic_realtime_put&nbsp;Invalid&nbsp;keyTwo:brokers<br />topic_realtime_get&nbsp;Invalid&nbsp;keyTwo:brokers<br />topic_realtime_offset&nbsp;null<br />topic_realtime_get_miss&nbsp;Invalid&nbsp;keyTwo:brokers<br />topic_realtime_put_failed&nbsp;null<br />topic_realtime_message_size&nbsp;Invalid&nbsp;keyTwo:brokers<br />END</div>&nbsp;topics<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->./metaServer.sh&nbsp;stats&nbsp;-item</div>执行结果<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->STATS<br />category&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;28&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />metatest2&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;1000&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />test&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;31&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />metatest&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;1058&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />user_fav&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;3&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />product&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;3&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />listen_queue&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;15&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />listen_queue_confirm&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;1048&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />user_remind&nbsp;partitions&nbsp;1&nbsp;message_count&nbsp;7&nbsp;accept_publish&nbsp;true&nbsp;accept_subscribe&nbsp;true<br />cart&nbsp;*Empty*<br />listen_queue_success&nbsp;*Empty*<br />order&nbsp;*Empty*<br />user_changePass&nbsp;*Empty*<br />user_reg&nbsp;*Empty*<br />END</div>topic名称(比如:test)<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->./metaServer.sh&nbsp;stats&nbsp;-item&nbsp;test</div>执行结果<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->STATS<br />test<br />resultCode&nbsp;0<br />partitions&nbsp;1<br />message_count&nbsp;31<br />bytes&nbsp;3855<br />topic_realtime_put&nbsp;Count=0,Value=0,Value/Count=invalid,Count/Duration=0,Duration=1251<br />topic_realtime_get&nbsp;Count=0,Value=0,Value/Count=invalid,Count/Duration=0,Duration=1251<br />topic_realtime_offset&nbsp;null<br />topic_realtime_get_miss&nbsp;Count=0,Value=0,Value/Count=invalid,Count/Duration=0,Duration=1251<br />topic_realtime_put_failed&nbsp;null<br />topic_realtime_message_size&nbsp;Count=0,Value=0,Value/Count=invalid,Count/Duration=0,Duration=1251<br />END</div><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><img src ="http://www.blogjava.net/livery/aggbug/392845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-12-12 10:17 <a href="http://www.blogjava.net/livery/articles/392845.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-10-源码分析</title><link>http://www.blogjava.net/livery/articles/391809.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Sat, 24 Nov 2012 01:48:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391809.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391809.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391809.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391809.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391809.html</trackback:ping><description><![CDATA[<h1>Meta的client实现分析</h1><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">由于meta不像activeMQ等产品，它们的broker端承载了非常多的功能，而像meta这样追求性能为目的的消息中间件，则是把broker端的功能弱化，同时加强了client端的某些功能，如当前client的消息offset的存储、从broker中pull消息等。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">下面我们从消息pull这样一个client端最重要的功能作为分析的主线来了解meta中client的实现。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">下面是client端执行pull消息的处理流程示意图:</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/a4c7ceb02be2441d4456f4924e7b186c52cde1de/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f34316134386232623934383634666565386264643264623037346433336335332e706e67" alt="meta1" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">1.通过ZKLoadRebalanceListener的rebalance方法，根据该client所订阅topic的分区数量来初始化对应数量的FetchRequest实例，并把它们放到FetchRequestQueue中(先进先出)。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">2.消息抓取管理器初始化fetchRunners线程池，并启动所有线程对FetchRequestQueue进行读请求的操作。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">3.当FetchRequestQueue中有请求时，则执行FetchRequestRunner线程中的processRequest方法，进行后续的操作。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">4.通过SimpleMessageConsumer(消息消费者基类)的fetch方法从broker端获取某topic的某分区的消息数据byte组。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">5.对broker端返回的消息数据byte组进行操作，解析出一条条消息，并对这些消息进行消费，具体代码实现在FetchRequestRunner类的notifyListener方法，它是消息消费的核心方法，后面我们会重点介绍。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">6.FetchRequestRunner处理完一次从broker获取消息并消费的过程后，会把FetchRequest实例重新放回FetchRequestQueue中，重复进行下一轮操作。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">上面是client端pull消息的主过程，由于meta的client涉及的功能也较多，为了更进一步了解client端的实现细节，我们从下面几个方面做更进一步的分析</p><h2>pull模型的轮训时间</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">很多用户在一看到消息的获取是通过client端主动pull的方式，就感觉和activeMQ等其他消息中间件所采用的broker主动推送消息到client的方式相比较，实时性有所降低。但通过对meta源码的分析，发现它的实时性还是可以保证的，具体实现方法分析如下：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">轮训时间的控制在FetchRequestQueue类的take方法中的如下几行代码：</p><pre style="margin-top: 15px; margin-bottom: 15px; padding: 6px 10px; border: 1px solid #cccccc; font-size: 13px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; line-height: 19px; overflow: auto; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; color: #333333; "><code style="margin: 0px; padding: 0px; border: none; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: transparent; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; ">final long delay = first.getDelay(TimeUnit.NANOSECONDS); if (delay &gt; 0) {       final long tl = this.available.awaitNanos(delay); } </code></pre><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">delay值的设置是按照下面规则进行的：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/b23a44d668eedd907808d8b82c6691d681401ddb/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f37383262653863626238666434316635623164346266326539303735613033352e706e67" alt="meta2" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">初始化为0，所以第一次该请求会立即和broker端通讯，以获得消息组。</li><li style="margin: 0px; padding: 0px; border: 0px; ">大部分情况下pull的轮训间隔为0，所以它的实时性还是可以保证的。</li></ul><h1>Meta使用zookeeper的细节</h1><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">meta对zookeeper非常依赖，而且重要的信息同步都是通过它完成的，官方文档对这一块的说明也不多，所以，我们对meta的分析就从这开始，当我们清晰meta中zookeeper的使用细节后，meta的内部实现原理也就基本清晰了。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">meta里使用zookeeper最典型的就是类ZKLoadRebalanceListener的 rebalance()方法，该方法使用到了很多zookeeper的znode，下面我们对这个方面做一个详细介绍：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">rebalance方法是用来计算某消息消费者具体应该消费哪个meta服务器节点上的哪些分区中的消息(也就是&#8220;消费者的负载平衡&#8221;)。该方面在下面两种情况下被触发：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">监视同一个消费者分组的consumer列表是否有变化；</li><li style="margin: 0px; padding: 0px; border: 0px; ">监视订阅的topic下的broker节点是否有新增或删除；</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">当上面两种情况发生时，zookeeper的机制会自动触发到rebalance方法，它的具体算法如下：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">1.从zookeeper的'/meta/consumers/某消费者组/ids/某消费者id'节点上获取该消费者的所有订阅了的主题;</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">2.从zookeeper的'/meta/brokers/ids'节点上获取所有的broker列表;</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">3.从zookeeper的'/meta/consumers/某消费者组/ids/'、'/meta/consumers/某消费者组/ids/某消费者id'这两个节点中获得每个topic下有哪些消费者;</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">4.从zookeeper的'/meta/brokers/topics/某topic'节点获得对应topic在broker(包括:master和slaves)里有哪些partition;</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">5.从上面获得的所有这些信息，调用类ZKLoadRebalanceListener中getRelevantTopicMap方法，判断最新的partition列表或consumer列表和当前在用的是否有变化，如果没有变化则再补充做一个动作(因为虽然partition和consumer都没有新增或删除，但可能cluster的结构发生变化了)：对集群做一个比较，看是否有机器down或新增；如果没有变化则继续进行后续rebalance操作；</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">6.如果经过上面的操作确认相关partition列表或consumer列表有变化，则根据'负载均衡策略'获取某个consumer对应的partition列表，然后根据之前老的和最新的partition列表做相关操作，如新分区在zookeeper上的挂载、释放等操作；</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">上面是client端一个比较重要的功能，也是zookeeper在meta里一个用的较多的地方。</p><h1>Meta的HA</h1><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">HA是任何进入生产环境的软件都需要考虑的一个重要因素，meta提供两种HA的方式：同步和异步(推荐使用异步方式)。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">官方提供的文档里已经很清晰说明了HA的场景，这里我们再补充一些官方文档中未详细说明的部分：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">meta采用冷备方式来实现HA，任何主、备的切换都得重启相关broker。</li><li style="margin: 0px; padding: 0px; border: 0px; ">任何时候只有master可以执行写操作；实现代码在BrokerConnectionListener类的syncedUpdateBrokersInfo方法,该方法从zookeeper上同步最新的master列表供producer使用。</li><li style="margin: 0px; padding: 0px; border: 0px; ">client可以连接cluster上的任何一台机器包括master和slave,作为它的消息来源，类似数据库的读写分离。</li><li style="margin: 0px; padding: 0px; border: 0px; ">如果master因为某种原因当机，则必须手动停止某台slave并对它进行相关配置操作，并启动它使它成为新的master。当那台坏了的master修复后，它将作为一台新的slave加入集群。</li><li style="margin: 0px; padding: 0px; border: 0px; ">在master上不停机的情况下新增一个topic，这个新增的topic不能自动的同步到slave上，必须通过某种方式把master上的server.ini同步到所有slave上，然后通过人工重启或通过jmx重启slave。</li></ul><h2>同步HA的切换过程：</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">生产环境中有一台master和一个同步slave,slave是不注册到zookeeper上的，当master当机，则所有连接到该broker的生产者和消费者都停止正常工作。然后人工停止slave,并新增samsa_master.properties配置文件，修改其中recoverOffset属性为true。并且修改server.ini中的brokerId为故障master的id，这些修改做完后，重启slave。这样它就成为新的master对外提供服务了。</p><h1>Meta的transaction实现</h1><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">对事务的支持是meta的一个重要特点，目前它支持XA和本地事务，下面我们详细对XA事务进行分析，由于本地事务相对简单，可以参考XA的实现。</p><h2>分布式事务（XA）</h2><h3>分布式事务介绍</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">分布式事务在分布式应用中是非常重要的，目前分布式事务的实现标准是XA，而在java体系中就是JTA标准。下面是XA的一个示意图：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/63b6f82ddf1a6a142dce420b7ad69b5c8e803c4a/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f37353163303236643839373934613131613461643061306430663336376630652e706e67" alt="meta3" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">这里我们重点说一下两阶段提交协议(2PC),它是XA的核心思想，具体示意图如下：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/edf924de019f2c6d38aed1ec22010784ca3e0e8a/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f36613132613635373932356334336233623032646163643739396461323831612e706e67" alt="meta2" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">具体更多关于XA的细节请参考XA的接口规范。</p><h3>meta的实现</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">meta里事务的实现主要是如下几个类：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">TransactionalCommandProcessor：事务命令处理器。它主要作用是接收client端发过来的各种请求，如beginTransaction、prepare、commit、rollback、新增消息等。</li><li style="margin: 0px; padding: 0px; border: 0px; ">JournalTransactionStore：基于文件方式的事务存储引擎。</li><li style="margin: 0px; padding: 0px; border: 0px; ">JournalStore：具体存储事务的文件存储类。</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">它们3者之间的关系是：TransactionalCommandProcessor接收client端的各种事务请求，然后调用JournalTransactionStore进行事务存储，JournalTransactionStore根据不同的client请求调用JournalStore具体保存事务信息到磁盘文件。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">下面通过一个示意图来进一步进行说明：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/b38bcfe5e5d390a36486b1a222937aec6545e96c/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f32623765646536653939376234386335383039346461326566623163623461392e706e67" alt="meta10" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">client通过调用beginTransaction来新开始一个事务(在meta里就是一个Tx实例),并把它放在JournalTransactionStore类的inflightTransactions队列里，然后client就可以在这个Tx中新增消息，但这些新增的消息是放在JournalStore文件里，并且完整的保存在内存中(由于meta目前没有专门的内存管理机制，当事务数量特别大的时候，这个地方有可能会出现内存溢出)。当client进行2PC中的prepare时，事务从inflightTransactions队列移到preparedTransactions队列，并保存相关信息到JournalStore。当执行commit时，该Tx的所有消息才真正放到MessageStore里供消息消费者读取。当client端发起rollback请求后，Tx被从preparedTransactions队列中删除，并保存相关信息到JournalStore。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">下面我们对meta事务实现的几个重要方面做一个详细介绍：</p><h4>beginTransaction</h4><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">当client端的TransactionContext(XAResource的实现)调用start方法，broker接收到请求后，启动一个新事务。</p><h4>新增消息</h4><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">处理序列图如下：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/ef4186426ae9a3ca05abaf83a7585095cf4bbcf4/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f36353539383132643838666634366133613030623333363466623462306564302e706e67" alt="image" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">当事务begin后，client端向broker发送多条消息存储的请求，broker收到请求后会调用JournalTransactionStore的addMessage方法。该方法把请求存储在事务日志文件中(JournalStore)，同时新建或找到对应的Tx实例，把这些消息存储请求保存在内存中。这里注意一点，在事务没有提交之前，这些消息存储是不会被放到对应topic消息存储文件中去的。</p><h4>prepare的处理过程</h4><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">处理序列图如下：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/8b212ee757b85412f0b0c1cd1dd3d3be023a724e/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f31636639393835666366313834383830393735346334663164333232636563342e706e67" alt="img" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">prepare的处理过程相对简单些，它只是把Tx实例从JournalTransactionStore类的inflightTransactions中移除到preparedTransactions中，同时在事务日志文件存储相关信息。</p><h4>commit的处理过程</h4><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">处理序列图如下：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/78216800f49ccc6ab6b80eaaf7e3ac0eb533c34e/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f63333531623130663131613034346332396538323964383264393931353662322e706e67" alt="img" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">commit过程相当复杂点。broker收到client端的commit请求，调用JournalTransactionStore的commit方法，从preparedTransactions里找到对应的Tx,把该Tx里的所有请求命令(PutCommand)，按照topic和分区分别保存到真正的topic消息存储文件中去，当全部保存完时，就会通过回调类AppendCallback的appendComplete方法记录commit日志到事务日志文件。</p><h4>recover的处理过程</h4><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">处理序列图如下：</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><img src="https://a248.e.akamai.net/camo.github.com/c87f50782c1408e092f3d11dd55a6ef48ee97d7e/687474703a2f2f696d672e68616f6b616e62752e636f6d2f696d672f626c6f672f33383065616465363963393034303932396431346361366464643532353337612e706e67" alt="img" style="margin: 0px; padding: 0px; border: 0px; max-width: 100%; box-sizing: border-box; " /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">recover操作发生在系统重启的时候，主要是为了还原系统上一次停止时候的事务场景，如还原处在prepare阶段的事务，rollback所有本地事务和没有prepare的XA事务。recover的处理细节包括两部分：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">在JournalTransactionStore的构造函数中进行JournalStore的recover操作</li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">JournalStore的recover主要是完成从事务日志文件中按照最近的checkpoint从日志中读取所有的日志记录，并按照记录的类型APPEND_MSG和TX_OP分别进行还原操作：</p><h5>APPEND_MSG类型</h5><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">这种类型的日志记录就调用JournalTransactionStore的addMessage方法，但是不会往日志文件中重复记录该消息了。</p><h5>TX_OP类型</h5><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">这种类型的处理相当复杂点。它根据日志记录的类型又细分为下面几种</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">XA_PREPARE:根据TransactionId把对应的Tx实例从JournalTransactionStore类的inflightTransactions中移到preparedTransactions中。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">XA_COMMIT和LOCAL_COMMIT:根据TransactionId从JournalTransactionStore类的inflightTransactions或preparedTransactions中找到对应的Tx实例。把该Tx内的所有消息请求对比相应topic消息存储文件中消息，如果topic消息存储文件中不存在这些消息则新增，如果存在则通过crc32校验码进行比对。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">LOCAL_ROLLBACK和XA_ROLLBACK:根据TransactionId把对应的Tx实例从JournalTransactionStore类的inflightTransactions或preparedTransactions中删除。</p></li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在TransactionalCommandProcessor的init方法中调用JournalTransactionStore类的recover操作</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">经过上面的recover操作后，它已经把meta重启前的事务现场在JournalTransactionStore和JournalStore中进行了还原。接下来就是TransactionalCommandProcessor类的事务现场还原，这个过程是把JournalTransactionStore类的preparedTransactions中的所有Tx在TransactionalCommandProcessor中进行还原，该过程相对简单，可参考源码实现。</p><p style="margin-top: 15px; margin-right: 0px; margin-bottom: 0px !important; margin-left: 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">经过上面这些recover步骤后，meta作为XAResource就可以继续加入XA事务了。</p><img src ="http://www.blogjava.net/livery/aggbug/391809.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-24 09:48 <a href="http://www.blogjava.net/livery/articles/391809.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-8-高可用</title><link>http://www.blogjava.net/livery/articles/391806.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 23 Nov 2012 02:02:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391806.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391806.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391806.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391806.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391806.html</trackback:ping><description><![CDATA[<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta相比于kafka的一个重要特性就是消息高可用方案的实现，我们称之为HA方案。消息在发送到broker之后立即写入磁盘才返回客户端告诉消息生产者消息发送成功，通过<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">unflushThreshold</code>和<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">unflushInterval</code>两个参数的控制，可以保证单机消息数据的安全性，只要机器的磁盘没有永久损坏，消息总可以在重启后恢复并正常投递给消费者们。但是，如果遇到了磁盘永久损坏或者数据文件永久损坏的情况，那么该broker上的消息数据将可能永久丢失。为了防止这种情况的发生，一个可行的方案就是将消息数据复制到多台机器，类似mysql的主从复制功能。</p>
<h1>同步复制和异步复制</h1>
<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">meta提供类似mysql主从复制的异步复制和同步功能，分别对应不同的可靠级别。理论上说同步复制能带来更高的可靠级别，异步复制因为延迟的存在，可能会丢失极少量的消息数据，相应地，同步复制会带来性能的损失，因为要同步写入两台甚至更多的broker机器上才算写入成功。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在实际实践中，**我更推荐采用异步复制的架构**，因为异步复制的架构相对简单，并且易于维护和恢复，对性能也没有影响。而同步复制对运维要求相对很高，机制复杂容易出错，故障恢复也比较麻烦。**异步复制加上磁盘做磁盘阵列**，足以应对非常苛刻的数据可靠性要求。</p>
<h1>异步复制配置</h1>
<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">假设你已经根据<a href="https://github.com/killme2008/Metamorphosis/wiki/%E5%A6%82%E4%BD%95%E5%BC%80%E5%A7%8B" style="margin: 0px; padding: 0px; border: 0px; color: #4183c4; text-decoration: none; ">如何开始</a>这份文档配置了一台broker服务器，并且配置了一个topic为<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">test</code>，现在你希望test能复制到另一台slave broker上来保证消息数据的高可用。你可以这样做：</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">1.首先，你需要部署一个新的broker，具体仍然参照<a href="https://github.com/killme2008/Metamorphosis/wiki/%E5%A6%82%E4%BD%95%E5%BC%80%E5%A7%8B" style="margin: 0px; padding: 0px; border: 0px; color: #4183c4; text-decoration: none; ">如何开始</a>这份文档，配置server.ini从master broker拷贝一份。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">2.其次，配置slave文件。编辑conf/async_slave.properties：</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">#</span><span style="color: #008000; ">slave编号,大于等于0表示作为slave启动,同一个master下的slave编号应该设不同值.</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">slaveId</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0</span><span style="color: #000000; "><br /><br /></span><span style="color: #008000; ">#</span><span style="color: #008000; ">作为slave启动时向master订阅消息的group,如果没配置则默认为meta-slave-group</span><span style="color: #008000; "><br />#</span><span style="color: #008000; ">不同的slaveId请使用不同的group</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">slaveGroup</span><span style="color: #000000; ">=</span><span style="color: #000000; ">meta</span><span style="color: #000000; ">-</span><span style="color: #000000; ">slave</span><span style="color: #000000; ">-</span><span style="color: #000000; ">group<br /><br /></span><span style="color: #008000; ">#</span><span style="color: #008000; ">slave数据同步的最大延时,单位毫秒&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">slaveMaxDelayInMills</span><span style="color: #000000; ">=</span><span style="color: #000000; ">500</span><span style="color: #000000; "><br /><br /></span><span style="color: #008000; ">#</span><span style="color: #008000; ">是否自动从master同步server.ini,&nbsp;1.4.2新增选项</span><span style="color: #008000; "><br />#</span><span style="color: #008000; ">第一次仍然需要自己拷贝server.ini，后续可以通过设置此选项为true来自动同步</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">autoSyncMasterConfig</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">true</span></div>
配置参数的含义请自己看注释。可见，一个master可以复制到多个slave。<p>&nbsp;</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">3.执行下列命令启动slave:<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">bin/metaServer.sh&nbsp;start&nbsp;slave&nbsp;</span></div><p>&nbsp;</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">4.第一次复制因为需要跟master完全同步需要耗费一定时间，你可以在数据文件的目录观察复制情况。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">5.**请注意，异步复制的slave将参与消费者的消费活动，消息消费者可以从slave中获取消息并消费，消费者会随机从master和slaves中挑选一台作为消费broker。**</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">6.**请注意，从1.4.2开始，可以通过autoSyncMasterConfig选项配置是否自动同步master的server.ini到异步复制的slave上，当master的server.ini文件变更并通过<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">bin/metaServer.sh reload</code>之后，slave将监控到这一变更并自动同步。**</p>
<h2>异步复制的局限</h2>
<ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
     <li style="margin: 0px; padding: 0px; border: 0px; ">异步复制有延迟，虽然可以通过设定<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">slaveMaxDelayInMills</code>来控制延迟。</li>
</ul>
<h2>异步复制的故障处理</h2>
<ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
     <li style="margin: 0px; padding: 0px; border: 0px; ">Master永久故障： 将slave作为master启动，去除启动参数中的slave即可，也就是<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">metaServer.sh restart</code></li>
     <li style="margin: 0px; padding: 0px; border: 0px; ">Slave永久故障： 启动新的broker并配置作为master新的slave启动。</li>
</ul>
<h1>同步复制配置</h1><img src ="http://www.blogjava.net/livery/aggbug/391806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-23 10:02 <a href="http://www.blogjava.net/livery/articles/391806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-7-集群与负载均衡</title><link>http://www.blogjava.net/livery/articles/391802.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 23 Nov 2012 01:58:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391802.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391802.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391802.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391802.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391802.html</trackback:ping><description><![CDATA[<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta假定producer、broker和consumer都是分布式的集群系统。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Producer可以是一个集群，多台机器上的producer可以往同一个topic发送消息。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta的服务器broker一般也是一个集群，多台broker组成一个集群提供一些topic服务，生产者按照一定的路由规则往集群里某台broker发送消息，消费者按照一定的路由规则拉取某台broker上的消息。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Consumer也可以组织成一个集群来消费同一个topic，发往这个topic的消息按照一定的路由规则发送到consumer集群里的某一台机器。Consumer集群每个consumer必须拥有相同的分组名称。</p><h2>Broker集群配置</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Broker集群配置非常容易，假设你已经按照<a href="https://github.com/killme2008/Metamorphosis/wiki/%E5%A6%82%E4%BD%95%E5%BC%80%E5%A7%8B" style="margin: 0px; padding: 0px; border: 0px; color: #4183c4; text-decoration: none; ">如何开始</a>和<a href="https://github.com/killme2008/Metamorphosis/wiki/%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86" style="margin: 0px; padding: 0px; border: 0px; color: #4183c4; text-decoration: none; ">服务器配置管理</a>配置好并启用了你第一台broker,某一天你发现这个单台broker无法支撑更大的消息量，那么你可能就需要引入更多的broker作为集群来提供服务，你要做的事情很简单：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">拷贝broker1的配置文件<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">conf/server.ini</code>到新的broker,假设为broker2。</li><li style="margin: 0px; padding: 0px; border: 0px; ">修改broker2的server.ini,只要修改brokerId为另一个不同于broker1的值即可</li><li style="margin: 0px; padding: 0px; border: 0px; ">启动broker2，这样一来broker2将和broker1组成一个服务器集群</li><li style="margin: 0px; padding: 0px; border: 0px; ">在这个过程中你不需要重启任何现有的服务，包括生产者、消费者和broker1，他们都将自动感知到新的broker2</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">可见，配置一个集群唯一要做的就是使用同一份配置文件并定义不同的<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">brokerId</code>即可。</p><h1>负载均衡</h1><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">负载均衡和failover分不开，我们将分别讨论下生产者和消费者的负载均衡策略。我们先假定broker是一个集群，这样每个topic必定有多个分区。</p><h2>生产者的负载均衡和failover</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">每个broker都可以配置一个topic可以有多少个分区，但是在生产者看来，一个topic在所有broker上的的所有分区组成一个分区列表来使用。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在创建producer的时候，客户端会从zookeeper上获取publish的topic对应的broker和分区列表，生产者在发送消息的时候必须选择一台broker上的一个分区来发送消息，默认的策略是一个轮询的路由规则，一张图来表示<br /><img src="http://www.blogjava.net/images/blogjava_net/livery/metaq1.jpg" width="633" height="388" alt="" /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">生产者在通过zk获取分区列表之后，会按照brokerId和partition的顺序排列组织成一个有序的分区列表，发送的时候按照从头到尾循环往复的方式选择一个分区来发送消息。考虑到我们的broker服务器软硬件配置基本一致，默认的轮询策略已然足够。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">如果你想实现自己的负载均衡策略，可以实现上文提到过的PartitionSelector接口，并在创建producer的时候传入即可。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在broker因为重启或者故障等因素无法服务的时候，producer通过zookeeper会感知到这个变化，将失效的分区从列表中移除做到fail over。因为从故障到感知变化有一个延迟，可能在那一瞬间会有部分的消息发送失败。</p><h2>消费者的负载均衡</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消费者的负载均衡会相对复杂一些。我们这里讨论的是单个分组内的消费者集群的负载均衡，不同分组的负载均衡互不干扰，没有讨论的必要。 消费者的负载均衡跟topic的分区数目紧密相关，要考察几个场景。 首先是，单个分组内的消费者数目如果比总的分区数目多的话，则多出来的消费者不参与消费，如图<br /><img src="http://www.blogjava.net/images/blogjava_net/livery/metaq2.jpg" width="665" height="354" alt="" /><br /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">其次，如果分组内的消费者数目比分区数目小，则有部分消费者要额外承担消息的消费任务，具体见示例图如下<br /><img src="http://www.blogjava.net/images/blogjava_net/livery/metaq3.jpg" width="680" height="407" alt="" /><br /></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">综上所述，单个分组内的消费者集群的负载均衡策略如下</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">每个分区针对同一个group只挂载一个消费者</li><li style="margin: 0px; padding: 0px; border: 0px; ">如果同一个group的消费者数目大于分区数目，则多出来的消费者将不参与消费</li><li style="margin: 0px; padding: 0px; border: 0px; ">如果同一个group的消费者数目小于分区数目，则有部分消费者需要额外承担消费任务</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta的客户端会自动帮处理消费者的负载均衡，它会将消费者列表和分区列表分别排序，然后按照上述规则做合理的挂载。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">从上述内容来看，合理地设置分区数目至关重要。如果分区数目太小，则有部分消费者可能闲置，如果分区数目太大，则对服务器的性能有影响。</p><p style="margin-top: 15px; margin-right: 0px; margin-bottom: 0px !important; margin-left: 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在某个消费者故障或者重启等情况下，其他消费者会感知到这一变化（通过 zookeeper watch消费者列表），然后重新进行负载均衡，保证所有的分区都有消费者进行消费。</p><img src ="http://www.blogjava.net/livery/aggbug/391802.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-23 09:58 <a href="http://www.blogjava.net/livery/articles/391802.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-6-服务器配置</title><link>http://www.blogjava.net/livery/articles/391800.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 23 Nov 2012 01:49:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391800.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391800.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391800.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391800.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391800.html</trackback:ping><description><![CDATA[<h2>示例配置</h2><a href="https://github.com/killme2008/Metamorphosis/blob/master/metamorphosis-server-wrapper/conf/sample.server.ini" style="background-color: #ffffff; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; margin: 0px; padding: 0px; border: 0px; color: #4183c4; text-decoration: none; "><font color="#4183c4" face="Helvetica, arial, freesans, clean, sans-serif"><span style="background-color: #ffffff; line-height: 22px;">&nbsp; &nbsp;</span></font><span style="background-color: #ffffff; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; margin: 0px; padding: 0px; border: 0px; color: #000000; text-decoration: none; "> &nbsp; &nbsp;</span><span style="background-color: #ffffff; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; margin: 0px; padding: 0px; border: 0px; color: #000000; text-decoration: none; ">&nbsp;示例文件存放位置：&nbsp;</span></a><span style="background-color: #eeeeee; font-size: 13px; ">metaq文件目录/conf/sample.server.ini</span><h2>服务端配置</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta服务端配置主要在服务器conf目录下的server.ini文件，整体配置分为三部分：系统参数、zookeeper参数以及topic配置。系统参数在system section，zookeeper参数配置在zookeeper section，而topic的配置是在topic=xxxx section。具体说明如下：</p><h3>系统参数部分</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">系统参数配置都放在<span style="font-size: 13px; color: #800000; font-weight: bold; ">[</span><span style="font-size: 13px; color: #800000; ">system</span><span style="font-size: 13px; color: #800000; font-weight: bold; ">]</span>下面：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">brokerId: 服务器集群中唯一的id，必须为整型0-1024之间。对服务器集群的定义是使用同一个zookeeper并且在zookeeper上的root path相同，具体参见zookeeper配置。</li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">hostName: 服务器hostname，默认取本机IP地址，如果你是多网卡机器，可能需要明确指定。服务器会将此hostname加上端口写入到zookeeper提供给客户端发现。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">serverPort:服务器端口，默认8123。PS. 选择8123是因为这蕴含着作者儿子的生日。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">numPartitions:系统默认情况下每个topic的分区数目，默认为1，可被topic配置覆盖。单个服务器的总分区数目不建议超过1000，太多将导致频繁的磁盘寻道严重影响IO性能。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">dataPath: 服务器数据文件路径，默认在~home/meta下,每个topic可以覆盖此配置，对于多块磁盘的机器，可设置不同topic到不同磁盘来提升IO效率。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">dataLogPath：数据日志文件路径，主要存放事务日志，默认跟dataPath一致，最好单独设置到不同的磁盘或者目录上。如果为空，使用指定的dataPath</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">getProcessThreadCount: 处理get请求的并发线程数，默认为CPUS*10。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">putProcessThreadCount: 处理put请求的并发线程数，默认为CPUS*10。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">maxSegmentSize: 单个数据文件的大小，默认为1G。默认无需修改此选项。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">maxTransferSize: 传输给消费者的最大数据大小，默认为1M，请根据你的最大消息大小酌情设置，如果太小，每次无法传输一个完整的消息给消费者，导致消费者消费停滞。可设置成一个大数来取消限制。</p></li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">1.4.3引入的新参数：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">acceptPublish: 是否接收消息，默认为true；如果为false，则不会注册发送信息到zookeeper上，客户端当然无法发送消息到该broker。本参数可以被后续的topic配置覆盖。</p></li><li style="margin: 0px; padding: 0px; border: 0px; "><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; ">acceptSubscribe: 与acceptPublish类似，默认也为true；如果为false，则不会注册消费信息到zookeeper上，消费者无法发现该broker，当然无法从该broker消费消息。本参数可以被后续的topic配置覆盖。</p></li></ul><h4>数据可靠性参数</h4><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta保证消息可靠性是建立在磁盘可靠性的基础上，发送的每一条消息都保证是在&#8220;写入磁盘&#8221;的情况下才返回给客户端应答。这里有两个关键参数可以控制：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">unflushThreshold: 每隔多少条消息做一次磁盘sync，强制将更改的数据刷入磁盘。默认为1000。也就是说在掉电情况下，最多允许丢失1000条消息。可设置为0，强制每次写入都sync。在设置为0的情况下，服务器会自动启用group commit技术，将多个消息合并成一次sync来提升IO性能。经过测试，group commit情况下消息发送者的TPS没有受到太大影响，但是服务端的负载会上升很多。</li><li style="margin: 0px; padding: 0px; border: 0px; ">unflushInterval: 间隔多少毫秒定期做一次磁盘sync,默认是10秒。也就是说在服务器掉电情况下，最多丢失10秒内发送过来的消息。不可设置为小于或者等于0。</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">请注意，上述两个参数都可以被topic单独配置说覆盖，也就是说每个topic可以配置不同的数据可靠级别。</p><h4>数据删除策略配置</h4><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">默认情况下，meta是会保存不断添加的消息，然后定期对&#8220;过期&#8221;的数据进行删除或者归档处理，这都是通过下列参数控制的：</p><span style="background-color: #ffffff; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; ">deleteWhen: 何时执行删除策略的cron表达式，默认是</span><span style="background-color: #eeeeee; font-size: 13px; ">&nbsp;</span><span style="background-color: #eeeeee; font-size: 13px; ">0</span><span style="background-color: #eeeeee; font-size: 13px; ">&nbsp;</span><span style="background-color: #eeeeee; font-size: 13px; ">0</span><span style="background-color: #eeeeee; font-size: 13px; ">&nbsp;</span><span style="background-color: #eeeeee; font-size: 13px; ">6</span><span style="background-color: #eeeeee; font-size: 13px; ">,</span><span style="background-color: #eeeeee; font-size: 13px; ">18</span><span style="background-color: #eeeeee; font-size: 13px; ">&nbsp;*&nbsp;*&nbsp;?&nbsp;</span><span style="background-color: #ffffff; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; ">，也就是每天的早晚6点执行处理策略。</span><br /><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">deletePolicy: 数据删除策略，默认超过7天即删除,这里的168是小时，10s表示10秒，10m表示10分钟，10h表示10小时，不明确指定单位默认为小时。<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">delete</code>是指删除，超过指定时间的数据文件将被彻底从磁盘删除。也可以选择<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">archive</code>策略，即不对过期的数据文件做删除而是归档，当使用archive策略的时候可以选择是否压缩数据文件，如<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">167,archive,true</code>即选择将更改时间超过7天的数据文件归档并压缩为zip文件，如果不选择压缩，则重命名为扩展名为arc的文件。</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">上述两个参数都可以被topic单独配置所覆盖，也就是每个topic可以指定自己独特的删除策略。通常来说，对于不重要的topic可以将更早地将他们删除来节省磁盘空间。</p><h5>事务相关配置</h5><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">maxCheckpoints: 最大保存事务checkpoint数目，默认为3，服务器在启动的时候会从最近一次checkpoint回访事务日志文件，恢复重启前的事务状态。不建议修改此参数。</li><li style="margin: 0px; padding: 0px; border: 0px; ">checkpointInterval：事务checkpoint时间间隔，单位毫秒，默认1小时。间隔时间太长，会导致启动的时候replay事务日志占用了太多时间，太短则可能影响到性能。</li><li style="margin: 0px; padding: 0px; border: 0px; ">maxTxTimeoutTimerCapacity：最大事务超时timer的数量。服务端会为每个事务启动一个定时器监控事务是否超时，定时器的数目上限通过本参数限制。限制了本参数，也变相地控制了最大可运行的事务数。默认为30000个。</li><li style="margin: 0px; padding: 0px; border: 0px; ">maxTxTimeoutInSeconds：最大事务超时时间，单位为秒，默认为60秒。客户端设置的事务超时时间不能超过此设定，超过将被强制限制为此设定。</li><li style="margin: 0px; padding: 0px; border: 0px; ">flushTxLogAtCommit：服务端对事务日志的sync策略，0表示让操作系统决定，1表示每次commit都刷盘，2表示每隔1秒刷盘一次。此参数严重影响事务性能，可根据你需要的性能和可靠性之间权衡做出一个合理的选择。通常建议设置为2，表示每隔1秒刷盘一次，也就是最多丢失一秒内的运行时事务。这样的可靠级别对大多数服务是足够的。最安全的当然是设置为1，但是将严重影响事务性能。而0的安全级别最低。安全级别上<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">1&gt;=2&gt;0</code>，而性能则是<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">0 &gt;= 2 &gt; 1</code>。</li></ul><h3>zookeeper配置</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">meta服务端会将自身id,topic信息和socket地址发送到zookeeper上，让客户端可以发现并连接服务器。Zookeeper相关的配置放在<span style="font-size: 13px; color: #800000; font-weight: bold; ">[</span><span style="font-size: 13px; color: #800000; ">zookeeper</span><span style="font-size: 13px; color: #800000; font-weight: bold; ">]</span>模块下面:</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">zk.zkEnable: 是否启用zookeeper，也就是是否将信息注册到zookeeper上。默认为true。对于同步复制的slave来说，本参数会被强制设置为false。</li><li style="margin: 0px; padding: 0px; border: 0px; ">zk.zkConnect: zookeeper服务器列表，例如<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">localhost:1281</code>这样的字符串。默认也是<code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; ">localhost:2181</code>。请设置你的zk集群地址列表。</li><li style="margin: 0px; padding: 0px; border: 0px; ">zk.zkSessionTimeoutMs: zookeeper的session timeout，默认为30秒。单位毫秒。</li><li style="margin: 0px; padding: 0px; border: 0px; ">zk.zkConnectionTimeoutMs： zookeeper的连接超时时间，默认同样为30秒，单位毫秒。</li><li style="margin: 0px; padding: 0px; border: 0px; ">zk.zkSyncTimeMs: 预期的zk集群间数据同步延迟，默认为5秒，这个参数对服务器无意义。</li></ul><h3>Topic配置</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">服务器将提供哪些topic服务都是通过topic配置来实现的，topic配置都是在<span style="font-size: 13px; color: #800000; font-weight: bold; ">[</span><span style="font-size: 13px; color: #800000; ">topic=xxx</span><span style="font-size: 13px; color: #800000; font-weight: bold; ">]</span>的模块下面，其中<span style="background-color: #eeeeee; font-size: 13px; ">xxx</span>就是topic名称，一个示范配置如下：<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">topic=meta-test</span><span style="color: #800000; font-weight: bold; ">]</span><span style="color: #000000; ">&nbsp;<br />stat</span><span style="color: #000000; ">=</span><span style="color: #000000; ">true&nbsp;<br />numPartitions</span><span style="color: #000000; ">=</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;</span></div><p>&nbsp;</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">这里配置了一个名为<span style="background-color: #eeeeee; font-size: 13px; ">meta-test</span>的topic，并针对该topic启用实时统计，并将topic的在本服务器的分区数目设置为1。可见，topic配置可覆盖服务器的部分配置，包括：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">stat:是否启用实时统计，启用则会在服务端对该topic的请求做实时统计，可以通过<span style="color: #000000; background-color: #eeeeee; font-size: 13px; ">stats&nbsp;topic-name</span>协议观察到该topic运行状况，可选。</li><li style="margin: 0px; padding: 0px; border: 0px; ">numPartitions: 该topic在本服务器的分区总数，覆盖系统配置，可选。</li><li style="margin: 0px; padding: 0px; border: 0px; ">unflushInterval：每隔多少条消息做一次磁盘sync，覆盖系统配置，可选。</li><li style="margin: 0px; padding: 0px; border: 0px; ">unflushThreshold：每隔多少秒做一次磁盘sync，覆盖系统配置，可选。</li><li style="margin: 0px; padding: 0px; border: 0px; ">deletePolicy：topic的删除策略，覆盖系统配置，可选。</li><li style="margin: 0px; padding: 0px; border: 0px; ">deleteWhen：删除策略的执行时间，覆盖系统配置，可选。</li><li style="margin: 0px; padding: 0px; border: 0px; ">dataPath：设置数据文件路径，覆盖系统配置，可选。</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">1.4.3新增参数：</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">acceptPublish: 是否接收该topic的消息，覆盖系统配置，可选。</li><li style="margin: 0px; padding: 0px; border: 0px; ">acceptSubscribe: 是否接受消费者的订阅，覆盖系统配置，可选。</li></ul><h3>新增Topic热部署</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在新增或者删除topic并保存server.ini之后，可以通过下列命令热加载新的配置文件并生效：<span style="background-color: #eeeeee; font-size: 13px; ">&nbsp;bin/metaServer.sh&nbsp;reload</span></p><img src ="http://www.blogjava.net/livery/aggbug/391800.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-23 09:49 <a href="http://www.blogjava.net/livery/articles/391800.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-3-metaQ初步</title><link>http://www.blogjava.net/livery/articles/391796.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Fri, 23 Nov 2012 01:20:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391796.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391796.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391796.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391796.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391796.html</trackback:ping><description><![CDATA[<h1>Java客户端例子</h1>
<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">使用maven，引用metaq的java client非常简单：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">dependency</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">groupId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="color: #000000; font-size: 10pt; font-family: Arial; ">com.taobao.metamorphosis</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">groupId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">artifactId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="color: #000000; font-size: 10pt; font-family: Arial; ">metamorphosis-client-extension</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">artifactId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">version</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="color: #000000; font-size: 10pt; font-family: Arial; ">1.4.3</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">version</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">dependency</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span></div></p>
<p>&nbsp;也可以引用 client-extend<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">dependency</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><br />&nbsp;&nbsp;<span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">groupId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="font-size: 10pt; font-family: Arial; ">com.taobao.metamorphosis</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">groupId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><br />&nbsp;&nbsp;<span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">artifactId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="font-size: 10pt; font-family: Arial; ">metamorphosis-client-extension</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">artifactId</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><br />&nbsp;&nbsp;<span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">version</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><span style="font-size: 10pt; font-family: Arial; ">1.4.3</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">version</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span><br /><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&lt;/</span><span style="color: #800000; font-size: 10pt; font-family: Arial; ">dependency</span><span style="color: #0000ff; font-size: 10pt; font-family: Arial; ">&gt;</span></div><br /></p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">直接用git 将<a href="https://github.com/killme2008/metamorphosis-example" style="margin: 0px; padding: 0px; border: 0px; color: #4183c4; text-decoration: none; ">metamorphosis-example</a>&nbsp;clone &nbsp;下载页面：<a href="https://github.com/killme2008/metamorphosis-example">https://github.com/killme2008/metamorphosis-example</a>&nbsp;。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><strong style="margin: 0px; padding: 0px; border: 0px; ">请注意，1.4.3及以上版本的java客户端只能连接1.4.3及以上版本的MetaQ服务器，而1.4.3之前的老客户端则没有限制。主要是因为1.4.3引入了发布和订阅topic的分离，1.4.3的新客户端只能查找到新版本的broker</strong></p>
<h2>消息会话工厂类</h2>
<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在使用消息生产者和消费者之前，需要用到消息会话工厂类&#8212;&#8212;MessageSessionFactory，由这个工厂帮你创建生产者或者消费者。除了这些，MessageSessionFactory还默默无闻地在后面帮你做很多事情，包括：</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">1.服务的查找和发现，通过diamond和zookeeper帮你查找日常的meta服务器地址列表，diamond可以忽略。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">2.连接的创建和销毁，自动创建和销毁到meta服务器的连接，并做连接复用，也就是到同一台meta的服务器在一个工厂内只维持一个连接。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">3.消费者的消息存储和恢复。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">4.协调和管理各种资源，包括创建的生产者和消费者的。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">因此，我们首先需要创建一个会话工厂类，MessageSessionFactory仅是一个接口，它的实现类常用的是MetaMessageSessionFactory：</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">MessageSessionFactory&nbsp;sessionFactory&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MetaMessageSessionFactory(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MetaClientConfig());&nbsp;</span></div>请注意,<strong style="color: #800000; ">MessageSessionFactory应当尽量复用，也就是作为应用中的单例来使用，简单的做法是交给spring之类的容器帮你托管</strong>。</p>
<h2>消息生产者 &nbsp;<br /><br />
</h2>
<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.example;<br />
<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.BufferedReader;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.InputStreamReader;<br />
<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.Message;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.MessageSessionFactory;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.MetaClientConfig;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.MetaMessageSessionFactory;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.producer.MessageProducer;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.producer.SendResult;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.utils.ZkUtils.ZKConfig;<br />
<br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Producer&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;MetaClientConfig&nbsp;metaClientConfig&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MetaClientConfig();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;ZKConfig&nbsp;zkConfig&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ZKConfig();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">设置zookeeper地址</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;zkConfig.zkConnect&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">127.0.0.1:2181</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;metaClientConfig.setZkConfig(zkConfig);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;New&nbsp;session&nbsp;factory,强烈建议使用单例</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageSessionFactory&nbsp;sessionFactory&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MetaMessageSessionFactory(metaClientConfig);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;create&nbsp;producer,强烈建议使用单例</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageProducer&nbsp;producer&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;sessionFactory.createProducer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;publish&nbsp;topic</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;String&nbsp;topic&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"mata</span><span style="color: #000000; ">test</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;producer.publish(topic);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BufferedReader&nbsp;reader&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;BufferedReader(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;InputStreamReader(System.in));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;line&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">while</span><span style="color: #000000; ">&nbsp;((line&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;reader.readLine())&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;send&nbsp;message</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SendResult&nbsp;sendResult&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;producer.sendMessage(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Message(topic,&nbsp;line.getBytes()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;check&nbsp;result</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">sendResult.isSuccess())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Send&nbsp;message&nbsp;failed,error&nbsp;message:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;sendResult.getErrorMessage());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Send&nbsp;message&nbsp;successfully,sent&nbsp;to&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;sendResult.getPartition());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}</span></div>
消息生产者的接口是MessageProducer，你可以通过它来发送消息。创建生产者很简单，通过MessageSessionFactory的createProducer方法即可以创建一个生产者。在Meta里，每个消息对象都是Message类的实例，Message表示一个消息对象，它包含这么几个属性：
<ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
     <li style="margin: 0px; padding: 0px; border: 0px; ">id: Long型的消息id,消息的唯一id，系统自动产生，用户无法设置，在发送成功后由服务器返回，发送失败则为0。</li>
     <li style="margin: 0px; padding: 0px; border: 0px; ">topic: 消息的主题，订阅者订阅该主题即可接收发送到该主题下的消息，生产者通过指定发布的topic查找到需要连接的服务器地址，必须。</li>
     <li style="margin: 0px; padding: 0px; border: 0px; ">data: 消息的有效载荷，二进制数据，也就是消息内容，meta永远不会修改消息内容。消息内容通常限制在1M以内，作者的建议是最好不要发送超过上百K的消息，必须。数据是否压缩也完全取决于使用方。</li>
     <li style="margin: 0px; padding: 0px; border: 0px; ">attribute: 消息属性，一个字符串，可选。生产者可通过设置消息属性来让消费者简单过滤。</li>
</ul>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在sendMessage之前必须调用 MessageProducer的publish(topic)方法<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">producer.publish(topic);&nbsp;</span></div></p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">这一步在发送消息前是必要的，必须先发布将要发送消息的topic（另外，metaq的broker必须要先通过配置文件设置好topic，启动broker后，客户端才可能发布消息成功，不然就会报出异常），这是为了让会话工厂帮你去查找接收这些topic的meta服务器地址并初始化连接。这个步骤针对每个topic只需要做一次，多次调用无影响。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">总结下这个例子，从标准输入读入你输入的数据，并将数据封装成一个Message对象，发送到[localhost:8123]服务的&nbsp;topic为metatestbroker&nbsp;上。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">请注意，<span style="color: #800000; "><strong>MessageProducer是线程安全的，完全可重复使用，因此最好在应用中作为单例来使用，一次创建，到处使用，配置为spring里的singleton bean。MessageProducer创建的代价昂贵，每次都需要通过zk查找服务器并创建tcp长连接。</strong></span></p>
<h2>消息消费者</h2>
<p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">发送消息后，消费者可以接收消息了，下面的代码创建异步消费者并订阅test这个主题，等待消息送达并打印消息内容</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.example;<br />
<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.util.concurrent.Executor;<br />
<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.Message;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.MessageSessionFactory;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.MetaClientConfig;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.MetaMessageSessionFactory;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.consumer.ConsumerConfig;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.consumer.MessageConsumer;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.client.consumer.MessageListener;<br />
</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.taobao.metamorphosis.utils.ZkUtils.ZKConfig;<br />
<br /><br />
</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;AsyncConsumer&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;MetaClientConfig&nbsp;metaClientConfig&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MetaClientConfig();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;ZKConfig&nbsp;zkConfig&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ZKConfig();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">设置zookeeper地址</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;zkConfig.zkConnect&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">127.0.0.1:2181</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;metaClientConfig.setZkConfig(zkConfig);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;New&nbsp;session&nbsp;factory,强烈建议使用单例</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageSessionFactory&nbsp;sessionFactory&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MetaMessageSessionFactory(metaClientConfig);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;subscribed&nbsp;topic</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;String&nbsp;topic&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"meta</span><span style="color: #000000; ">test</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;consumer&nbsp;group</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;String&nbsp;group&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">meta-example</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;create&nbsp;consumer,强烈建议使用单例</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageConsumer&nbsp;consumer&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;sessionFactory.createConsumer(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ConsumerConfig(group));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;subscribe&nbsp;topic</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;consumer.subscribe(topic,&nbsp;</span><span style="color: #000000; ">1024</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1024</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MessageListener()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;recieveMessages(Message&nbsp;message)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Receive&nbsp;message&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;String(message.getData()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Executor&nbsp;getExecutor()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Thread&nbsp;pool&nbsp;to&nbsp;process&nbsp;messages,maybe&nbsp;null.</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;complete&nbsp;subscribe</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;consumer.completeSubscribe();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
通过createConsumer方法来创建MessageConsumer，请注意传入一个ConsumerConfig参数，这是消费者的配置对象。每个消息者都必须有一个ConsumerConfig配置对象，这里只设置了group属性，这是消费者的分组名称。Meta的Producer、Consumer和Broker都可以为集群。消费者可以组成一个集群共同消费同一个topic，发往这个topic的消息将按照一定的负载均衡规则发送给集群里的一台机器。同一个消费者集群必须拥有同一个分组名称，也就是同一个group。我们这里将分组名称设置为meta-example。
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">订阅消息通过subscribe方法，这个方法接受三个参数</p>
<ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
     <li style="margin: 0px; padding: 0px; border: 0px; ">topic，订阅的主题</li>
     <li style="margin: 0px; padding: 0px; border: 0px; ">maxSize，因为meta是一个消费者主动拉取的模型，这个参数规定每次拉取的最大数据量，单位为字节，这里设置为1M，默认最大为1M。</li>
     <li style="margin: 0px; padding: 0px; border: 0px; ">MessageListener，消息监听器，负责消息消息。</li>
</ul>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">MessageListener的接口方法如下:</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;MessageListener&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;接收到消息列表，只有message不为空并且不为null的情况下会触发此方法<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;message<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;recieveMessages(Message&nbsp;message);<br /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;处理消息的线程池<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Executor&nbsp;getExecutor();<br />}</span></div>消息的消费过程可以是一个并发处理的过程，getExecutor返回你想设置的线程池，每次消费都会在这个线程池里进行。recieveMessage方法用于实际的消息消费处理，message参数即为消费者收到的消息，它必不为null。
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">这里简单地打印收到的消息内容就完成消费。如果在消费过程中抛出任何异常，该条消息将会在一定间隔后重新尝试提交给MessageListener消费。在多次消费失败的情况下，该消息将会存储到消费者应用的本次磁盘，并在后台自动恢复重试消费。</p>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">在调用subscribe之后，我们还调用了completeSubscribe方法来完成订阅过程。请注意，subscribe仅是将订阅信息保存在本地，并没有实际跟meta服务器交互，要使得订阅关系生效必须调用一次completeSubscribe，completeSubscribe仅能被调用一次，多次调用将抛出异常。 为什么需要completeSubscribe方法呢，原因有二：</p>
<ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">
     <li style="margin: 0px; padding: 0px; border: 0px; ">首先，subscribe方法可以被调用多次，也就是一个消费者可以消费多种topic</li>
     <li style="margin: 0px; padding: 0px; border: 0px; ">其次，如果每次调用subscribe都跟zk和meta服务器交互一次，代价太高</li>
</ul>
<p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">因此completeSubscribe一次性将所有订阅的topic生效，并处理跟zk和meta服务器交互的所有过程。</p>
<p style="margin-top: 15px; margin-right: 0px; margin-bottom: 0px !important; margin-left: 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">同样<span style="color: #800000; "><strong>,MessageConsumer也是线程安全的，创建的代价不低，因此也应该尽量复用。</strong></span></p><img src ="http://www.blogjava.net/livery/aggbug/391796.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-23 09:20 <a href="http://www.blogjava.net/livery/articles/391796.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-2-部署服务</title><link>http://www.blogjava.net/livery/articles/391778.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Thu, 22 Nov 2012 09:56:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391778.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391778.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391778.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391778.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391778.html</trackback:ping><description><![CDATA[1、首先下载最新版本<a href="https://github.com/killme2008/Metamorphosis/downloads">https://github.com/killme2008/Metamorphosis/downloads<br />
</a>2、下载后解压缩<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->tar&nbsp;-zxvf&nbsp;metamorphosis-server-wrapper-x.x.x.x.tar.gz</div>
3、解压缩后，文件结构如下：<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">启动脚本放在bin目录，主要的脚本是</span><code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; font-size: 12px; font-family: Consolas, 'Liberation Mono', Courier, monospace; background-color: #f8f8f8; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; white-space: nowrap; color: #333333; ">metaServer.sh</code><span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">，而配置文件主要是conf目录下server.ini，lib存放所有的依赖jar包。</span>&nbsp;<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; ">taobao &nbsp; &nbsp;<br />
&nbsp; &nbsp;--metamorphosis-server-wrapper<br />
&nbsp; &nbsp; &nbsp; &nbsp; --bin<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env.bat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env.sh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log4j.properties&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;metaServer.bat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;metaServer.sh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tools_log4j.properties<br />
&nbsp; &nbsp; &nbsp; &nbsp; --conf<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;async_slave.properties<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gregor_slave.properties<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jettyBroker.properties<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notifyadaper<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notifySlave.properties<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sample.server.ini<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;samsa_master.properties<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.ini<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timetunnel.properties<br />
&nbsp; &nbsp; &nbsp; &nbsp; --lib<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><img src="http://www.blogjava.net/Images/dot.gif" alt="" /></div>
4、zookeeper依赖<br />
&nbsp; &nbsp; metamorphosis&nbsp;必须要依赖zookeeper，也就是说要想启动meta，不能没有zookeeper，使用zookeeper发布和订阅服务，并默认使用zookeeper存储消费者offset&nbsp;。虽然conf/server.ini 中有这样的说明
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #008000; ">;</span><span style="color: #008000; ">zk配置</span>&nbsp;<br />
<span style="color: #800000; font-weight: bold; ">[</span><span style="color: #800000; ">zookeeper</span><span style="color: #800000; font-weight: bold; ">]</span><br />
<span style="color: #008000; ">;</span><span style="color: #008000; ">是否注册到zk，默认为true</span><span style="color: #008000; "><br />
;</span><span style="color: #008000; ">zk.zkEnable=true</span></div>
但是笔者尝试将zkEnable属性设置为false，metamorphosis可以启动。但是在client端程序也没找到相应地不依赖于zookeeper的meta的调用方式（可能需要自己实现客户端）。于是乎，还是要下载zookeeper， 如何配置和启动zookeeper，详见地址
<div>&nbsp;&nbsp;&nbsp;&nbsp;假设你在本机安装并启动了一个zookeeper，端口在默认的2181，请修改conf/server.ini文件，保证zookeeper地址正确</div>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;<span style="color: #008000; ">;</span><span style="color: #008000; ">zk的服务器列表&nbsp;</span><span style="color: #008000; "><br />
</span>&nbsp;zk.zkConnect=localhost:2181&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;<span style="color: #008000; ">;</span><span style="color: #008000; ">zk心跳超时，单位毫秒，默认30秒&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; "><br />
</span>&nbsp;zk.zkSessionTimeoutMs=30000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;<span style="color: #008000; ">;</span><span style="color: #008000; ">zk连接超时时间，单位毫秒，默认30秒&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; "><br />
</span>&nbsp;zk.zkConnectionTimeoutMs=30000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;<span style="color: #008000; ">;</span><span style="color: #008000; ">zk数据同步时间，单位毫秒，默认5秒&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; "><br />
</span>&nbsp;zk.zkSyncTimeMs=5000&nbsp; &nbsp;</div>其它重要参数，详情见服务器配置&nbsp;<span style="background-color: #eeeeee; font-size: 13px; ">http://www.blogjava.net/livery/articles/</span><span style="background-color: #eeeeee; font-size: 13px; ">391800</span><span style="background-color: #eeeeee; font-size: 13px; ">.html</span><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; "><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->brokerId&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;服务器唯一标志，必须要有，不能重复<br />dataPath&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;指定数据文件路径，默认user.home/meta<br />dataLogPath&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;日志文件路径，默认跟dataPath一样<br />numPartitions&nbsp;&nbsp;&nbsp;默认topic的分区数目<br />hostName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;服务器hostname，可以为空，默认将取本机IP<br />serverPort&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;服务器端口&nbsp;&nbsp;&nbsp;默认8123<br />其它具体参数信息请看conf/sample_server.ini示范文件。</div>5、启动、重启、关闭、检查运行状态<br />&nbsp; &nbsp;&nbsp;<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">确保你的机器上安装了JDK并正确设置JAVA_HOME和PATH变量。</span>
<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">修改JAVA_HOME,JMX等变量，请修改</span>&nbsp;bin/env.sh&nbsp;。<br /><span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">启动服务：</span>&nbsp;<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->bin/metaServer.sh&nbsp;start</div>重启服务：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->bin/metaServer.sh&nbsp;restart</div>关闭服务：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; "><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->bin/metaServer.sh&nbsp;stop</div>查询服务状态 &nbsp;<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->bin/metaServer.sh&nbsp;stats</div>&nbsp;&nbsp;&nbsp;&nbsp;查询结果 &nbsp;<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">也可以telnet到默认的8123端口执行</span><span style="color: #333333; background-color: #ffffff; font-size: 12px; white-space: nowrap;"><font face="Consolas, Liberation Mono, Courier, monospace">stats</font></span><span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">命令查看。</span>&nbsp;<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />STATS<br />pid&nbsp;16637<br />broker_id&nbsp;1001<br />port&nbsp;8123<br />uptime&nbsp;11604<br />version&nbsp;1.4.3<br />slave&nbsp;false<br />curr_connections&nbsp;1<br />threads&nbsp;125<br />cmd_put&nbsp;2050<br />cmd_get&nbsp;946<br />cmd_offset&nbsp;0<br />tx_begin&nbsp;0<br />tx_xa_begin&nbsp;0<br />tx_commit&nbsp;0<br />tx_rollback&nbsp;0<br />get_miss&nbsp;944<br />put_failed&nbsp;0<br />total_messages&nbsp;2050<br />topics&nbsp;3<br />config_checksum&nbsp;2176933603<br />END<br /><img src="http://www.blogjava.net/Images/dot.gif" alt="" /></div>&nbsp;<img src ="http://www.blogjava.net/livery/aggbug/391778.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-22 17:56 <a href="http://www.blogjava.net/livery/articles/391778.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-1-对比其它消息队列</title><link>http://www.blogjava.net/livery/articles/391595.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Mon, 19 Nov 2012 08:48:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391595.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391595.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391595.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391595.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391595.html</trackback:ping><description><![CDATA[&nbsp; 准备替换掉现实服务中使用的activemq，又开始调研消息队列。初步选中metaQ和ZeroMQ，因为这两款听人介绍得很多，耳朵都快出茧子了。<br />简单研究后，发现这两个消息队列的不同之处太多，不太适合用来对比。ZeroMQ和metaQ都是支持多语言客户端，性能都很好；相对于activemq这种企业级使用的消息队列，提高了消息的处理性能&nbsp;，尽量弱化broker 的功能，而ZeroMQ比较极致，甚至去掉了单独broker，采用类库依赖，客户端实现逻辑。<br />metaQ&nbsp;的基础逻辑是<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消息生产者负责创建消息并发送到</span>metaQ&nbsp;<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">服务器，meta服务器会将消息持久化到磁盘，消息消费者从</span>metaQ<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">服务器拉取消息并提交给应用消费</span>&nbsp;。与activemq相比弱化了broker的功能，将消息的消费保存、出错、重试等机制都尽可能放在了客户端上；使broker的处理逻辑尽量简单，来提高数据传输和处理的速度。metaQ还支持XA事务；当然XA事务还不是很成熟，另外事务还是对性能有一点影响的。<br />ZeroMQ并不像是一个传统意义上的消息队列服务器，事实上，它也根本不是一个服务器，它更像是一个底层的网络通讯库，在Socket API之上做了一层封装，将网络通讯、进程通讯和线程通讯抽象为统一的API接口。。所以它的性能也很好，相比metaQ来说，需要自己来实现大量的容错机制的代码。<br />硬件配置<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->metaq服务器（activemq也在其中）<br />型号&nbsp;DELL&nbsp;510<br />CPU&nbsp;&nbsp;Intel(R)Xeon(R)&nbsp;CPU&nbsp;E5606&nbsp;&nbsp;@&nbsp;2.13GHz&nbsp;*4<br />Mem&nbsp;&nbsp;12G<br />网卡&nbsp;100M<br /><br />客户端服务器<br />型号&nbsp;DELL&nbsp;710<br />CPU&nbsp;&nbsp;Intel(R)Xeon(R)&nbsp;CPU&nbsp;E5606&nbsp;&nbsp;@&nbsp;2.13GHz&nbsp;*8<br />Mem&nbsp;&nbsp;8G<br />网卡&nbsp;100M</div><img src="http://www.blogjava.net/images/blogjava_net/livery/未命名.jpg" width="992" height="341" alt="" /><img src ="http://www.blogjava.net/livery/aggbug/391595.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-19 16:48 <a href="http://www.blogjava.net/livery/articles/391595.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-5-客户端常用API函数介绍</title><link>http://www.blogjava.net/livery/articles/391589.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Mon, 19 Nov 2012 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391589.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391589.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391589.html</trackback:ping><description><![CDATA[例子和相关源码稍后再补充 &nbsp; &nbsp;<br />MessageSessionFactory接口<br />&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 常用实现类 MetaMessageSessionFactory<br />&nbsp; &nbsp; MessageProducer接口<br />&nbsp; &nbsp; &nbsp; &nbsp; 常用实现类SimpleMessageProducer<br />&nbsp; &nbsp; PartitionSelector接口<br />&nbsp; &nbsp; &nbsp; &nbsp; 分区选择器，用于从分区列表中选中将要发送消息的分区。<br />&nbsp; &nbsp; &nbsp; &nbsp; 主要实现类，轮询分区选择器 RoundRobinPartitionSelector<br />&nbsp; &nbsp; &nbsp; &nbsp; 客户端可自定义分区选择器，并在创建生产者的时候注入<br />&nbsp; &nbsp; SendResult类<br />&nbsp; &nbsp; &nbsp; &nbsp; 发送结果消息类<br />&nbsp;&nbsp;&nbsp;&nbsp;MessageConsumer接口<br />&nbsp; &nbsp; &nbsp; &nbsp; 主要实现类SimpleMessageConsumer<br />&nbsp; &nbsp; MessageListener接口<br />&nbsp; &nbsp; &nbsp; &nbsp; 消息监听器，处理消费消息<br />&nbsp; &nbsp; OffsetStorage接口<br />&nbsp; &nbsp; &nbsp; &nbsp; offset存储器 &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; 默认提供下列三种存储器<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1、ZkOffsetStorage &nbsp; 存储在zookeeper<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 2、MysqlOffsetStorage &nbsp; 存储在mysql数据库<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 3、LocalOffsetStorage &nbsp; &nbsp;存储在本地文件，适合消费者分组只有一个消费者的情况，无需共享offset信息。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用户可以自定义实现自己存储器。<img src ="http://www.blogjava.net/livery/aggbug/391589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-19 16:16 <a href="http://www.blogjava.net/livery/articles/391589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-9-最佳实践</title><link>http://www.blogjava.net/livery/articles/391588.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Mon, 19 Nov 2012 07:53:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391588.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391588.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391588.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391588.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391588.html</trackback:ping><description><![CDATA[<span style="font-size: 18pt; "><strong>客户端最佳实践</strong></span><br />&nbsp; &nbsp; 复用MessageSessionFactory，最好作为全局单例来使用。<br /><span style="font-size: 18pt; "><strong>生产者最佳实践</strong></span><br /><ul><li>&nbsp; &nbsp; 尽量复用MessageProducer,最好也是使用单例。可以单个MessageProducer发送多个topic，或者多个MessageProducer每个发送一种topic，尽量减少重复创建producer。</li><li>&nbsp; &nbsp; 消息data的序列化方式锦衣不要使用特定语言的序列化方式（如java序列化），可以考虑 自定义协议、json、protocolbuffer、hessian等序列化协议，以便跨语言消费。</li><li>&nbsp; &nbsp; 实现发送顺序所需要的PartitionSelector，推荐使用业务id。如：交易订单id来取模，分区列表选择固定分区发送。 &nbsp; &nbsp;</li><li>&nbsp; &nbsp; 单条消息大小最好限制在100k以下。</li><li>&nbsp; &nbsp; 如无顺序等特殊需求，不要实现自己的PartitionSelector，默认的轮询策略足够。</li></ul><span style="font-size: 18pt; "><strong>消费者最佳实践</strong></span><br /><ul><li>&nbsp; &nbsp; 尽量复用MessageConsumer，最好也是使用单例。可以单个MessageConsumer订阅多种topic，或者多个MessageConsumer每个订阅一种topic，尽量减少重复创建consumer。</li><li>&nbsp; &nbsp; 单次拉取的数据不宜过大。如果对消息实时性要求较高的应用，应将单次拉取的数据缩小，但至少要大于单条消息的大小。如果对吞吐量要求较高，可以将该值设置大一些。</li><li>&nbsp; &nbsp; 假设消费过程非常轻量级（如：只是打印log），可以不设置MessageListener线程池，减少资源耗费。</li><li>&nbsp; &nbsp; 假设消息发送量巨大，消费能力不高，可适当提高拉取消息线程数 fetchRunnerCount和MessageListener的线程池大小。</li><li>&nbsp; &nbsp; 尽量在消息的消费过程中，扑捉所有异常，减少消息在本地的堆积和恢复，前提是不要遗漏消息。如确实无法处理，请主动抛出异常，以便重试。</li></ul><img src ="http://www.blogjava.net/livery/aggbug/391588.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-19 15:53 <a href="http://www.blogjava.net/livery/articles/391588.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>metamorphosis-4-基本概念</title><link>http://www.blogjava.net/livery/articles/391197.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Mon, 12 Nov 2012 05:44:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/391197.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/391197.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/391197.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/391197.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/391197.html</trackback:ping><description><![CDATA[<h2><span style="font-family: Arial; font-size: 12pt; font-weight: normal;">最近开始调研metamorphosis，官方文档很是详尽。于是边学习边做笔记。</span></h2><p align="left"><strong><span style="font-size: 18pt; font-family: 宋体; ">基本概念<br /></span><span style="font-size:12.0pt;font-family:宋体;"><br />&nbsp;消息生产者 </span></strong></p>  <p align="left"><span style="font-size:12.0pt; font-family:宋体;">也称为Message Producer，一般简称为producer，负责产生消息并发送消息到meta服务器。</span></p>  <p align="left"><strong><span style="font-size:12.0pt;font-family:宋体;">&nbsp;</span></strong><strong><span style="font-size:12.0pt;font-family:宋体;">消息消费者 </span></strong></p>  <p align="left"><span style="font-size:12.0pt; font-family:宋体;">也称为Message Consumer，一般简称为consumer，负责消息的消费，meta采用pull模型，由消费者主动从meta服务器拉取数据并解析成消息并消费。</span></p>  <p align="left"><strong><span style="font-size:12.0pt;font-family:宋体;">&nbsp;Topic </span></strong></p>  <p align="left"><span style="font-size:12.0pt; font-family:宋体;">消息的主题，由用户定义并在服务端配置。producer发送消息到某个topic下，consumer从某个topic下消费消息。</span></p>  <p align="left"><strong><span style="font-size:12.0pt;font-family:宋体;">&nbsp;</span></strong><strong><span style="font-size:12.0pt;font-family:宋体;">分区(partition) </span></strong></p>  <p align="left"><span style="font-size:12.0pt; font-family:宋体;">同一个topic下面还分为多个分区，如meta-test这个topic我们可以分为10个分区，分别有两台服务器提供，那么可能每台服务器提供5个分区，假设服务器分别为0和1，则所有分区为0-0、0-1、0-2、0-3、0-4、1-0、1-1、1-2、1-3、1-4。</span><span style="font-family: 宋体; font-size: 12pt; ">分区跟消费者的负载均衡机制相关。</span></p>  <p align="left"><strong><span style="font-size:12.0pt;font-family:宋体;">&nbsp;Message </span></strong></p>  <p align="left"><span style="font-size:12.0pt; font-family:宋体;">消息，负载用户数据并在生产者、服务端和消费者之间传输。</span></p>  <p align="left"><strong><span style="font-size:12.0pt;font-family:宋体;">&nbsp;Broker </span></strong></p>  <p align="left"><span style="font-size:12.0pt; font-family:宋体;">就是meta的服务端或者说服务器，在消息中间件中也通常称为broker。</span></p>  <p align="left"><strong><span style="font-size:12.0pt;font-family:宋体;">&nbsp;</span></strong><strong><span style="font-size:12.0pt;font-family:宋体;">消费者分组(Group) </span></strong></p>  <p align="left"><span style="font-size:12.0pt; font-family:宋体;">消费者可以是多个消费者共同消费一个topic下的消息，每个消费者消费部分消息。这些消费者就组成一个分组，拥有同一个分组名称,通常也称为消费者集群</span></p>  <p align="left"><strong><span style="font-size:12.0pt;font-family:宋体;">&nbsp;Offset </span></strong></p>  <span style="font-size: 12pt; font-family: 宋体; ">消息在broker上的每个分区都是组织成一个文件列表，消费者拉取数据需要知道数据在文件中的偏移量，这个偏移量就是所谓offset。Offset是绝对偏移量，服务器会将offset转化为具体文件的相对偏移量。<br /><br /></span><h2 style="margin: 0px 0px 10px; padding: 0px; border-width: 0px 0px 1px; border-bottom-style: solid; border-bottom-color: #cccccc; -webkit-font-smoothing: antialiased; cursor: text; position: relative; font-size: 24px; font-family: Helvetica, arial, freesans, clean, sans-serif; background-color: #ffffff; ">可靠性</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Metamorphosis的可靠性保证贯穿客户端和服务器。</p><h3 style="margin: 20px 0px 10px; padding: 0px; border: 0px; font-size: 18px; -webkit-font-smoothing: antialiased; cursor: text; position: relative; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; background-color: #ffffff; ">生产者的可靠性保证</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消息生产者发送消息后返回SendResult，如果isSuccess返回为true,则表示消息已经确认发送到服务器并被服务器接收存储。整个发送过程是一个同步的过程。保证消息送达服务器并返回结果。</p><h3 style="margin: 20px 0px 10px; padding: 0px; border: 0px; font-size: 18px; -webkit-font-smoothing: antialiased; cursor: text; position: relative; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; background-color: #ffffff; ">服务器的可靠性保证</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消息生产者发送的消息，meta服务器收到后在做必要的校验和检查之后的第一件事就是写入磁盘，写入成功之后返回应答给生产者。因此，可以确认每条发送结果为成功的消息服务器都是写入磁盘的。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">写入磁盘，不意味着数据落到磁盘设备上，毕竟我们还隔着一层os，os对写有缓冲。Meta有两个特性来保证数据落到磁盘上</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">每1000条（可配置），即强制调用一次force来写入磁盘设备。</li><li style="margin: 0px; padding: 0px; border: 0px; ">每隔10秒（可配置），强制调用一次force来写入磁盘设备。</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">因此,Meta通过配置可保证在异常情况下（如磁盘掉电）10秒内最多丢失1000条消息。当然通过参数调整你甚至可以在掉电情况下不丢失任何消息。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">服务器通常组织为一个集群，一条从生产者过来的消息可能按照路由规则存储到集群中的某台机器。Meta已经实现高可用的HA方案，类似mysql的同步和异步复制，将一台meta服务器的数据完整复制到另一台slave服务器，并且slave服务器还提供消费功能（同步复制不提供消费）。</p><h3 style="margin: 20px 0px 10px; padding: 0px; border: 0px; font-size: 18px; -webkit-font-smoothing: antialiased; cursor: text; position: relative; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; background-color: #ffffff; ">消费者的可靠性保证</h3><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消息的消费者是一条接着一条地消费消息，只有在成功消费一条消息后才会接着消费下一条。如果在消费某条消息失败（如异常），则会尝试重试消费这条消息（默认最大5次），超过最大次数后仍然无法消费，则将消息存储在消费者的本地磁盘，由后台线程继续做重试。而主线程继续往后走，消费后续的消息。因此，只有在MessageListener确认成功消费一条消息后，meta的消费者才会继续消费另一条消息。由此来保证消息的可靠消费。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消费者的另一个可靠性的关键点是offset的存储，也就是拉取数据的偏移量。我们目前提供了以下几种存储方案</p><ul style="margin: 15px 0px; padding: 0px 0px 0px 30px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; "><li style="margin: 0px; padding: 0px; border: 0px; ">zookeeper，默认存储在zoopkeeper上，zookeeper通过集群来保证数据的安全性。</li><li style="margin: 0px; padding: 0px; border: 0px; ">mysql，可以连接到您使用的mysql数据库，只要建立一张特定的表来存储。完全由数据库来保证数据的可靠性。</li><li style="margin: 0px; padding: 0px; border: 0px; ">file，文件存储，将offset信息存储在消费者的本地文件中，适合消费者分组只有一个消费者的情况，无需共享offset信息。</li></ul><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Offset会定期保存，并且在每次重新负载均衡前都会强制保存一次。</p><h2 style="margin: 20px 0px 10px; padding: 0px; border-width: 0px 0px 1px; border-bottom-style: solid; border-bottom-color: #cccccc; -webkit-font-smoothing: antialiased; cursor: text; position: relative; font-size: 24px; font-family: Helvetica, arial, freesans, clean, sans-serif; background-color: #ffffff; ">顺序</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">很多人关心的消息顺序，希望消费者消费消息的顺序跟消息的发送顺序是一致的。比如，我发送消息的顺序是A、B、C，那么消费者消费的顺序也应该是A、B、C。乱序对某些应用可能是无法接受的。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Metamorphosis对消息顺序性的保证是有限制的，默认情况下，消息的顺序以谁先达到服务器并写入磁盘，则谁就在先的原则处理。并且，发往同一个分区的消息保证按照写入磁盘的顺序让消费者消费，这是因为消费者针对每个分区都是按照从前到后递增offset的顺序拉取消息。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta可以保证，在单线程内使用该producer发送的消息按照发送的顺序达到服务器并存储，并按照相同顺序被消费者消费，前提是这些消息发往同一台服务器的同一个分区。为了实现这一点，你还需要实现自己的PartitionSelector用于固定选择分区<br /><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">interface</span><span style="color: #000000; ">&nbsp;PartitionSelector&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Partition&nbsp;getPartition(String&nbsp;topic,&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Partition</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;partitions,&nbsp;Message&nbsp;message)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;MetaClientException;<br />}</span></div></p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">选择分区可以按照一定的业务逻辑来选择，如根据业务id来取模。或者如果是传输文件，可以固定选择第n个分区使用。当然，如果传输文件，通常我们会建议你只配置一个分区，那也就无需选择了。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消息的顺序发送可以使用这个OrderedMessageProducer，自定义管理分区信息，并提供故障情况下的本地存储功能。</p><h2 style="margin: 20px 0px 10px; padding: 0px; border-width: 0px 0px 1px; border-bottom-style: solid; border-bottom-color: #cccccc; -webkit-font-smoothing: antialiased; cursor: text; position: relative; font-size: 24px; font-family: Helvetica, arial, freesans, clean, sans-serif; background-color: #ffffff; ">消息重复</h2><p style="margin: 0px 0px 15px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">消息的重复包含两个方面，生产者重复发送消息以及消费者重复消费消息。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">针对生产者来说，有可能发生这种情况，生产者发送消息，等待服务器应答，这个时候发生网络故障，服务器实际已经将消息写入成功，但是由于网络故障没有返回应答。那么生产者会认为发送失败，则再次发送同一条消息，如果发送成功，则服务器实际存储两条相同的消息。这种由故障引起的重复，meta是无法避免的，因为meta不判断消息的data是否一致，因为它并不理解data的语义，而仅仅是作为载荷来传输。</p><p style="margin: 15px 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">针对消费者来说也有这个问题，消费者成功消费一条消息，但是此时断电，没有及时将前进后的offset存储起来，则下次启动的时候或者其他同个分组的消费者owner到这个分区的时候，会重复消费该条消息。这种情况meta也无法完全避免。</p><p style="margin-top: 15px; margin-right: 0px; margin-bottom: 0px !important; margin-left: 0px; padding: 0px; border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; line-height: 22px; background-color: #ffffff; ">Meta对消息重复的保证只能说在正常情况下保证不重复，异常情况无法保证，这些限制是由远程调用的语义引起的，要做到完全不重复的代价很高，meta暂时不会考虑。</p><span style="font-size: 12pt; font-family: 宋体; "><br /></span><img src ="http://www.blogjava.net/livery/aggbug/391197.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2012-11-12 13:44 <a href="http://www.blogjava.net/livery/articles/391197.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>activemq 注意问题</title><link>http://www.blogjava.net/livery/articles/316269.html</link><dc:creator>心情经纬</dc:creator><author>心情经纬</author><pubDate>Tue, 23 Mar 2010 03:33:00 GMT</pubDate><guid>http://www.blogjava.net/livery/articles/316269.html</guid><wfw:comment>http://www.blogjava.net/livery/comments/316269.html</wfw:comment><comments>http://www.blogjava.net/livery/articles/316269.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/livery/comments/commentRss/316269.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/livery/services/trackbacks/316269.html</trackback:ping><description><![CDATA[<h2>1 使用jms需要注意的问题</h2>
<p>一下所述的问题，不仅是对ActiveMQ，对于其他的JMS也一样有效。</p>
<h3>1.1 不要频繁的建立和关闭连接</h3>
<p>JMS使用长连接方式，一个程序，只要和JMS服务器保持一个连接就可以了，不要频繁的建立和关闭连接。频繁的建立和关闭连接，对程序的性能影响还是很大的。这一点和jdbc还是不太一样的。</p>
<h3>1.2 Connection的start()和stop()方法代价很高</h3>
<p>JMS的Connection的start()和stop()方法代价很高，不能经常调用。我们试用的时候，写了个jms的connection
pool，每次将connection取出pool时调用start()方法，归还时调用stop()方法，然而后来用jprofiler发现，一般的
cpu时间都耗在了这两个方法上。</p>
<h3>1.3 start()后才能收消息</h3>
<p>Connection的start()方法调用后，才能收到jms消息。如果不调用这个方法，能发出消息，但是一直收不到消息。不知道其它的jms服务器也是这样。</p>
<h3>1.4 显示关闭Session</h3>
<p>如果忘记了最后关闭Connection或Session对象，都会导致内存泄漏。这个在我测试的时候也发现了。本来以为关闭了
Connection，由这个Connection生成的Session也会被自动关闭，结果并非如此，Session并没有关闭，导致内存泄漏。所以一
定要显示的关闭Connection和Session。</p>
<h3>1.5 对Session做对象池</h3>
<p>对Session做对象池，而不是Connection。Session也是昂贵的对象，每次使用都新建和关闭，代价也非常高。而且后来我们发现，
原来Connection是线程安全的，而Session不是，所以后来改成了对Session做对象池，而只保留一个Connection。</p>
<h2>2 集群</h2>
<p>ActiveMQ有强大而灵活的集群功能，但是使用起来还是会有很多陷阱。</p>
<h3>2.1 broker cluster和 master-slave</h3>
<p>ActiveMQ可以做broker的集群，也可以做master-slave方式的集群。前者能在多个broker之前fail-over和
load-balance，但是在某个节点出故障时，可能导致消息丢失；而后者能实时备份消息，和fail-over，但是不能load-
balance。broker
cluser的方式，在一个broker上发送的消息可以在其它的broker上收到。当一个broker失效时，客户端可以自动的转到别的broker
上运行，多个broker可以同时提供服务，但是消息只存储在一个broker上，如果那个broker失效了，那么客户端直到它重新启动后才能收到该
broker上的消息，假如很不幸，那个broker的存储介质坏了，那么消息就丢失掉了。<br />
Master-slave方式中，只有master提供服务，slave只是实时的备份master的数据，所以消息不会丢失。当master失效
时，slave会自动升为master，客户端会自动转到slave上工作，所以能fail-over。由于只有master提供服务，所以不能将负载分
到多个broker上。<br />
其实单个broker的性能已经是相当的惊人了，在我们公司的机器上能达到每秒收发4000个消息，没个消息4K字节这样的速度，足够公司目前的需要了，而公司并不希望丢失任何数据，所以我们选择使用master-slave模式。</p>
<h3>2.2 多种master-slave模式</h3>
<p>master-slave也有多种实现方式。它们的不同只是在共享数据和锁机制上。</p>
<h4>2.2.1 Pure master-slave</h4>
<p>Pure
master-slave，显示的在配置文件中指定一个broker做为另一个broker的slave。运行时，slave同过网络自动从master
出复制数据，同时在和master失去连接时自动升级为master。当master失效，slave成为master后，如果要让原先的master重
新投入运行，需要停掉运行中的slave(现在升级为master了)，手动复制slave中的数据到master中。再重新启动master和
slave。这种方式最简单，效率也不错，但是只能有两台做集群，只能fail-over一次，而且需要停机回复master-slave结构。</p>
<h4>2.2.2 JDBC master-slave</h4>
<p>这种方式不需要特殊的配置，只要让所有的节点都把数据存储到同一个数据库中。先拿到数据库表的锁的节点成为master，一旦它失效了，其它的节点
获得锁，就可以成为master。因为数据通过数据库共享，放在一个地方，不需要停机恢复master-slave。这种方式，需要额外的数据库服务器，
如果数据库失效了，那么就全失效了，而且速度不是很快。我们在用mysql测试时，并没有成功，master失效后，其他的节点始终没有升级成
slave，可能是数据库配置的问题。</p>
<h4>2.2.3 Share file master-slave</h4>
<p>这种方式类似于前者，也不需要特别的配置，只是通过共享文件系统来共享数据，靠文件锁实现只有一台成为master。共享文件系统的方式有很多，我们测试了nfs v4 (v3有bug，不行)， 最终在稳定性，效率等方面不是很满意，可能是通过网络太慢了。</p>
<p>测试过众多master-slave模式后发现，pure方式管理起来麻烦，jdbc方式成本高，效率低，share
file方式需要高性能的共享文件，都有缺点。鉴于单台activeMQ很可靠，而我们的基础平台组愿意用硬件备份，最终还是决定不用master-
slave了，也不用broker cluster，就用单台，通过硬件冗余保证数据不会丢失，并找另外一台刀片机做冷备，在主服务器失效时顶替。</p>
<img src ="http://www.blogjava.net/livery/aggbug/316269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/livery/" target="_blank">心情经纬</a> 2010-03-23 11:33 <a href="http://www.blogjava.net/livery/articles/316269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>