rabbitmq中默认unack超时时间_RabbitMQ 与 Kafka 的技术差异以及使用注意点

f4311b37ec6bfdfdcce9c37e4ff04db4.png

导言

ea058e23fcc55c68805ca47b0e0b38c2.png

作为一个有丰富经验的微服务系统架构师,经常有人问我,“应该选择RabbitMQ还是Kafka?”。基于某些原因, 许多开发者会把这两种技术当做等价的来看待。的确,在一些案例场景下选择RabbitMQ还是Kafka没什么差别,但是这两种技术在底层实现方面是有许多差异的。不同的场景需要不同的解决方案,选错一个方案能够严重的影响你对软件的设计,开发和维护的能力。第一篇文章介绍了RabbitMQ和Apache Kafka内部实现的相关概念。本篇文章会从两个方面探讨这两种技术之间的差异,一个是这两种技术之间的显著差异,另一个是对于软件架构师和开发者需要注意的差异。那么,我们先来说说架构模式,也就是我们尝试着利用这两种技术来实现的架构模式,并且评估什么时候该使用哪一个。注意 1如果你对RabbitMQ和Kafka的内部结构还不熟悉,我强烈推荐你阅读我之前的第一篇文章。如果你不确定,那么可以简要的看一下里面的标题和图表,至少对这些差异有个大概的了解。注意 2上一篇文章发表之后,有些读者问我对于Apache Pulsar的看法。Pulsar是另一种类型的消息系统,它旨在提供RabbitMQ和Kafka都有的一些优点。作为一个现代的消息系统,它看上去很有前途;但是像其他平台系统一样,都有各自的优缺点。这边文章主要是比较RabbitMQ和Kafka,之后我会尝试针对Apache Pulsar做一个比较。RabbitMQ和Kafka的显著差异

ea058e23fcc55c68805ca47b0e0b38c2.png

RabbitMQ是一个消息代理,但是Apache Kafka是一个分布式流式系统。好像从语义上就可以看出差异,但是它们内部的一些特性会影响到我们是否能够很好的设计各种用例。例如,Kafka最适用于数据的流式处理,但是RabbitMQ对流式中的消息就很难保持它们的顺序。另一方面,RabbitMQ内置重试逻辑和死信(dead-letter)交换器,但是Kafka只是把这些实现逻辑交给用户来处理。这部分主要强调在不同系统之间它们的主要差异。消息顺序对于发送到队列或者交换器上的消息,RabbitMQ不保证它们的顺序。尽管消费者按照顺序处理生产者发来的消息看上去很符合逻辑,但是这有很大误导性。RabbitMQ文档中有关于消息顺序保证的说明:“发布到一个通道(channel)上的消息,用一个交换器和一个队列以及一个出口通道来传递,那么最终会按照它们发送的顺序接收到。” ——RabbitMQ代理语义(Broker Semantics)换话句话说,只要我们是单个消费者,那么接收到的消息就是有序的。然而,一旦有多个消费者从同一个队列中读取消息,那么消息的处理顺序就没法保证了。由于消费者读取消息之后可能会把消息放回(或者重传)到队列中(例如,处理失败的情况),这样就会导致消息的顺序无法保证。一旦一个消息被重新放回队列,另一个消费者可以继续处理它,即使这个消费者已经处理到了放回消息之后的消息。因此,消费者组处理消息是无序的,如下表所示。

5674e7836740d25ae1e813784fdd1342.png

使用RabbitMQ丢失消息顺序的例子当然,我们可以通过限制消费者的并发数等于1来保证RabbitMQ中的消息有序性。更准确点说,限制单个消费者中的线程数为1,因为任何的并行消息处理都会导致无序问题。不过,随着系统规模增长,单线程消费者模式会严重影响消息处理能力。所以,我们不要轻易的选择这种方案。另一方面,对于Kafka来说,它在消息处理方面提供了可靠的顺序保证。Kafka能够保证发送到相同主题分区的所有消息都能够按照顺序处理。回顾第一篇文章介绍,默认情况下,Kafka会使用循环分区器(round-robin partitioner)把消息放到相应的分区上。不过,生产者可以给每个消息设置分区键(key)来创建数据逻辑流(比如来自同一个设备的消息,或者属于同一租户的消息)。所有来自相同流的消息都会被放到相同的分区中,这样消费者组就可以按照顺序处理它们。但是,我们也应该注意到,在同一个消费者组中,每个分区都是由一个消费者的一个线程来处理。结果就是我们没法伸缩(scale)单个分区的处理能力。不过,在Kafka中,我们可以伸缩一个主题中的分区数量,这样可以让每个分区分担更少的消息,然后增加更多的消费者来处理额外的分区。获胜者(Winner):显而易见,Kafka是获胜者,因为它可以保证按顺序处理消息。RabbitMQ在这块就相对比较弱。消息路由RabbitMQ可以基于定义的订阅者路由规则路由消息给一个消息交换器上的订阅者。一个主题交换器可以通过一个叫做routing_key的特定头来路由消息。或者,一个头部(headers)交换器可以基于任意的消息头来路由消息。这两种交换器都能够有效地让消费者设置他们感兴趣的消息类型,因此可以给解决方案架构师提供很好的灵活性。另一方面,Kafka在处理消息之前是不允许消费者过滤一个主题中的消息。一个订阅的消费者在没有异常情况下会接受一个分区中的所有消息。作为一个开发者,你可能使用Kafka流式作业(job),它会从主题中读取消息,然后过滤,最后再把过滤的消息推送到另一个消费者可以订阅的主题。但是,这需要更多的工作量和维护,并且还涉及到更多的移动操作。获胜者:在消息路由和过滤方面,RabbitMQ提供了更好的支持。消息时序(timing)在测定发送到一个队列的消息时间方面,RabbitMQ提供了多种能力:消息存活时间(TTL)发送到RabbitMQ的每条消息都可以关联一个TTL属性。发布者可以直接设置TTL或者根据队列的策略来设置。系统可以根据设置的TTL来限制消息的有效期。如果消费者在预期时间内没有处理该消息,那么这条消息会自动的从队列上被移除(并且会被移到死信交换器上,同时在这之后的消息都会这样处理)。TTL对于那些有时效性的命令特别有用,因为一段时间内没有处理的话,这些命令就没有什么意义了。延迟/预定的消息RabbitMQ可以通过插件的方式来支持延迟或者预定的消息。当这个插件在消息交换器上启用的时候,生产者可以发送消息到RabbitMQ上,然后这个生产者可以延迟RabbitMQ路由这个消息到消费者队列的时间。这个功能允许开发者调度将来(future)的命令,也就是在那之前不应该被处理的命令。例如,当生产者遇到限流规则时,我们可能会把这些特定的命令延迟到之后的一个时间执行。Kafka没有提供这些功能。它在消息到达的时候就把它们写入分区中,这样消费者就可以立即获取到消息去处理。Kafka也没用为消息提供TTL的机制,不过我们可以在应用层实现。不过,我们必须要记住的一点是Kafka分区是一种追加模式的事务日志。所以,它是不能处理消息时间(或者分区中的位置)。获胜者:毫无疑问,RabbitMQ是获胜者,因为这种实现天然的就限制Kafka。消息留存(retention)当消费者成功消费消息之后,RabbitMQ就会把对应的消息从存储中删除。这种行为没法修改。它几乎是所有消息代理设计的必备部分。相反,Kafka会给每个主题配置超时时间,只要没有达到超时时间的消息都会保留下来。在消息留存方面,Kafka仅仅把它当做消息日志来看待,并不关心消费者的消费状态。消费者可以不限次数的消费每条消息,并且他们可以操作分区偏移来“及时”往返的处理这些消息。Kafka会周期的检查分区中消息的留存时间,一旦消息超过设定保留的时长,就会被删除。Kafka的性能不依赖于存储大小。所以,理论上,它存储消息几乎不会影响性能(只要你的节点有足够多的空间保存这些分区)。获胜者:Kafka设计之初就是保存消息的,但是RabbitMQ并不是。所以这块没有可比性,Kafka是获胜者。容错处理当处理消息,队列和事件时,开发者常常认为消息处理总是成功的。毕竟,生产者把每条消息放入队列或者主题后,即使消费者处理消息失败了,它仅仅需要做的就是重新尝试,直到成功为止。尽管表面上看这种方法是没错的,但是我们应该对这种处理方式多思考一下。首先我们应该承认,在某些场景下,消息处理会失败。所以,即使在解决方案部分需要人为干预的情况下,我们也要妥善地处理这些情况。消息处理存在两种可能的故障:
  • 瞬时故障——故障产生是由于临时问题导致,比如网络连接,CPU负载,或者服务崩溃。我们可以通过一遍又一遍的尝试来减轻这种故障。

  • 持久故障——故障产生是由于永久的问题导致的,并且这种问题不能通过额外的重试来解决。比如常见的原因有软件bug或者无效的消息格式(例如,损坏(poison)的消息)

作为架构师和开发者,我们应该问问自己:“对于消息处理故障,我们应该重试多少次?每一次重试之间我们应该等多久?我们怎样区分瞬时和持久故障?”最重要的是:“所有重试都失败后或者遇到一个持久的故障,我们要做什么?”当然,不同业务领域有不同的回答,消息系统一般会给我们提供工具让我们自己实现解决方案。RabbitMQ会给我们提供诸如交付重试和死信交换器(DLX)来处理消息处理故障。DLX的主要思路是根据合适的配置信息自动地把路由失败的消息发送到DLX,并且在交换器上根据规则来进一步的处理,比如异常重试,重试计数以及发送到“人为干预”的队列。查看这篇文章[1],它在RabbitMQ处理重试上提供了额外的可能模式视角。在RabbitMQ中我们需要记住最重要的事情是当一个消费者正在处理或者重试某个消息时(即使是在把它返回队列之前),其他消费者都可以并发的处理这个消息之后的其他消息。当某个消费者在重试处理某条消息时,作为一个整体的消息处理逻辑不会被阻塞。所以,一个消费者可以同步地去重试处理一条消息,不管花费多长时间都不会影响整个系统的运行。

4e74e56bb0acf297051ddc87c4bca21d.png

消费者1持续的在重试处理消息1,同时其他消费者可以继续处理其他消息和RabbitMQ相反,Kafka没有提供这种开箱即用的机制。在Kafka中,需要我们自己在应用层提供和实现消息重试机制。另外,我们需要注意的是当一个消费者正在同步地处理一个特定的消息时,那么同在这个分区上的其他消息是没法被处理的。由于消费者不能改变消息的顺序,所以我们不能够拒绝和重试一个特定的消息以及提交一个在这个消息之后的消息。你只要记住,分区仅仅是一个追加模式的日志。一个应用层解决方案可以把失败的消息提交到一个“重试主题”,并且从那个主题中处理重试;但是这样的话我们就会丢失消息的顺序。我们可以在Uber.com上找到Uber工程师实现的一个例子。如果消息处理的时延不是关注点,那么对错误有足够监控的Kafka方案可能就足够了。如果消费者阻塞在重试一个消息上,那么底部分区的消息就不会被处理获胜者:RabbitMQ是获胜者,因为它提供了一个解决这个问题的开箱即用的机制。伸缩有多个基准测试,用于检查RabbitMQ和Kafka的性能。尽管通用的基准测试对一些特定的情况会有限制,但是Kafka通常被认为比RabbitMQ有更优越的性能。Kafka使用顺序磁盘I / O来提高性能。从Kafka使用分区的架构上看,它在横向扩展上会优于RabbitMQ,当然RabbitMQ在纵向扩展上会有更多的优势。Kafka的大规模部署通常每秒可以处理数十万条消息,甚至每秒百万级别的消息。过去,Pivotal记录了一个Kafka集群每秒处理一百万条消息[2]的例子;但是,它是在一个有着30个节点集群上做的,并且这些消息负载被优化分散到多个队列和交换器上。典型的RabbitMQ部署包含3到7个节点的集群,并且这些集群也不需要把负载分散到不同的队列上。这些典型的集群通常可以预期每秒处理几万条消息。获胜者:尽管这两个消息平台都可以处理大规模负载,但是Kafka在伸缩方面更优并且能够获得比RabbitMQ更高的吞吐量,因此这局Kafka获胜。但是,值得注意的是大部分系统都还没有达到这些极限!所以,除非你正在构建下一个非常受欢迎的百万级用户软件系统,否则你不需要太关心伸缩性问题,毕竟这两个消息平台都可以工作的很好。消费者复杂度RabbitMQ使用的是智能代理和傻瓜式消费者模式。消费者注册到消费者队列,然后RabbitMQ把传进来的消息推送给消费者。RabbitMQ也有拉取(pull)API;不过,一般很少被使用。RabbitMQ管理消息的分发以及队列上消息的移除(也可能转移到DLX)。消费者不需要考虑这块。根据RabbitMQ结构的设计,当负载增加的时候,一个队列上的消费者组可以有效的从仅仅一个消费者扩展到多个消费者,并且不需要对系统做任何的改变。

965e1b2b11e26a3910209d7d307cb4dd.png

RabbitMQ高效的伸缩相反,Kafka使用的是傻瓜式代理和智能消费者模式。消费者组中的消费者需要协调他们之间的主题分区租约(以便一个具体的分区只由消费者组中一个消费者监听)。消费者也需要去管理和存储他们分区偏移索引。幸运的是Kafka SDK已经为我们封装了,所以我们不需要自己管理。另外,当我们有一个低负载时,单个消费者需要处理并且并行的管理多个分区,这在消费者端会消耗更多的资源。当然,随着负载增加,我们只需要伸缩消费者组使其消费者的数量等于主题中分区的数量。这就需要我们配置Kafka增加额外的分区。但是,随着负载再次降低,我们不能移除我们之前增加的分区,这需要给消费者增加更多的工作量。尽管这样,但是正如我们上面提到过,Kafka SDK已经帮我们做了这个额外的工作。

b7df38d8e8b66e202e33b83ee2f75905.png

Kafka分区没法移除,向下伸缩后消费者会做更多的工作获胜者:根据设计,RabbitMQ就是为了傻瓜式消费者而构建的。所以这轮RabbitMQ获胜。如何选择?

ea058e23fcc55c68805ca47b0e0b38c2.png

现在我们就如面对百万美元问题一样:“什么时候使用RabbitMQ以及什么时候使用Kafka?”概括上面的差异,我们不难得出以下结论:优先选择RabbitMQ的条件:
  1. 高级灵活的路由规则

  2. 消息时序控制(控制消息过期或者消息延迟)

  3. 高级的容错处理能力,在消费者更有可能处理消息不成功的情景中(瞬时或者持久)

  4. 更简单的消费者实现

优先选择Kafka的条件:
  1. 严格的消息顺序

  2. 延长消息留存时间,包括过去消息重放的可能

  3. 传统解决方案无法满足的高伸缩能力

大部分情况下这两个消息平台都可以满足我们的要求。但是,它取决于我们的架构师,他们会选择最合适的工具。当做决策的时候,我们需要考虑上面着重强调的功能性差异和非功能性限制。这些限制如下:
  1. 当前开发者对这两个消息平台的了解

  2. 托管云解决方案的可用性(如果适用)

  3. 每种解决方案的运营成本

  4. 适用于我们目标栈的SDK的可用性

当开发复杂的软件系统时,我们可能被诱导使用同一个消息平台去实现所有必须的消息用例。但是,从我的经验看,通常同时使用这两个消息平台能够带来更多的好处。例如,在一个事件驱动的架构系统中,我们可以使用RabbitMQ在服务之间发送命令,并且使用Kafka实现业务事件通知。原因是事件通知常常用于事件溯源,批量操作(ETL风格),或者审计目的,因此Kafka的消息留存能力就显得很有价值。相反,命令一般需要在消费者端做额外处理,并且处理可以失败,所以需要高级的容错处理能力。这里,RabbitMQ在功能上有很多闪光点。以后我可能会写一篇详细的文章来介绍,但是你必须记住--你的里程(mileage)可能会变化,因为适合性取决于你的特定需求。总结思想

ea058e23fcc55c68805ca47b0e0b38c2.png

写这两篇文章是由于我观察到许多开发者把这RabbitMQ和Kafka作为等价来看待。我希望通过这两篇文章的帮助能够让你获得对这两种技术实现的深刻理解以及它们之间的技术差异。反过来通过它们之间的差异来影响这两个平台去给用例提供更好的服务。这两个消息平台都很棒,并且都能够给多个用例提供很好的服务。但是,作为解决方案架构师,取决于我们对每一个用例需求的理解,以及优化,然后选择最合适的解决方案。相关链接:
  1. https://engineering.nanit.com/rabbitmq-retries-the-full-story-ca4cc6c5b493

  2. https://content.pivotal.io/blog/rabbitmq-hits-one-million-messages-per-second-on-google-compute-engine

原文链接:https://medium.com/better-programming/rabbitmq-vs-kafka-1779b5b70c41文章来源:分布式实验室,点击查看原文。▼往期精彩回顾▼Delayed Message 插件实现 RabbitMQ 延迟队列利用 RabbitMQ 死信队列和 TTL 实现定时任务高并发场景下 RabbitMQ 消费端服务限流实践图文实践 RabbitMQ 不同类型交换机消息投递机制一次 RabbitMQ 生产故障引发的服务重连限流思考RabbitMQ 和 Kafka 的比较

67de3b728a751334f747778f7a3788d0.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410215.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

五桌面工具来创建优秀的Windows环境

很多工具并不实用,都说能解压文件什么的,今天我给你们几个牛逼的 文件修复 文件修复软件是一个功能强大的工具来修复损坏的文件。它可以扫描损坏的文件,并提取到一个新的可用文件的最大数据。您可以修复Word文档,Excel电子表格&am…

假设以邻接矩阵作为图的存储结构_图的存储

因为图的结构特点,使得其在存储、遍历也相对复杂一些。邻接矩阵存储图最简单的方式就是将图的顶点用一维数组存储进来,然后将边信息存储在二维矩阵中,这两个数组合称为图的邻接矩阵(Adjacency Matrix)。无向图的邻接矩…

lombok依赖_为什么有些公司不让用 Lombok ?不要问,自己看吧

来源:toutiao.com/i6820517160964588044如果您正在阅读此文,想必您对Project Lombok已经有了一段时间的了解。您是否正准备拥抱Lombok?还是正准备将如此酷炫的项目推荐给你的团队?如果您准备那么做,不妨听听我在使用Lombok一年后…

ocx控件 postmessage消息会消失_实战经验:如何检测CMFCTabCtrl控件标签页切换事件...

MFC库中经常会使用到的一个控件是Tab标签页控件,这个控件在展现多个平级数据集非常合适。与控件对应的,是MFC库中的两个类:CMFCTabCtrl和CMFCBaseTabCtrl,其中CMFCTabCtrl继承自CMFCBaseTabCtrl,而CMFCBaseTabCtrl又继…

c++ 用eclipse建立一个类,并实例化并运行

新建项目file->new->c/c project 项目结构cpc.cpp// // Name : cpc.cpp // Author : cpc // Version : // Copyright : Your copyright notice // Description : Hello World in C, Ansi-style //#include <iostream> #include "mycoach.h&…

ps制作20种特效文字_用PS制作裂、火、电特效——电

本例是关于人物的电特效。包含的技术点比较多&#xff0c;其中最重要的技术是质感合成技术与变形技术。案例步骤&#xff08;1&#xff09;按CtrlN组合键新建一个19491417像素的文档。然后新建一个图层&#xff0c;接着打开渐变编辑器&#xff0c;设置第一个颜色的色标为&#…

JQuery放大镜效果

在网上找了些实现放大镜效果的方法&#xff0c;感觉这个方法符合我的要求就摘过来了&#xff0c;非原创 在一些电子商务或商品展示网上&#xff0c;为了更好的显示商品&#xff0c;一般都会添加放大镜的效果。 如图&#xff1a;&#xff08;当把鼠标放到小图片上&#xff0c;右…

echarts轨迹图,各个城市线路图轨迹如何取值

ehcarts官网网站给的是北京交通线路图https://www.echartsjs.com/examples/editor.html?clines-bmap-effect&#xff0c;这里其实有误区。 在开发项目的时候&#xff0c;我一直以为将某一路公交车的每个站点的坐标&#xff08;经纬度&#xff09;&#xff0c;放到series中的da…

如何查看电脑显卡配置_组装电脑:电脑配置显卡推荐

重点提醒&#xff1a;最新3000显卡上市&#xff0c;以下内容在发布之后会更新。现在不要着急购买2060系列的显卡。 今年显卡价格在大量矿卡的冲击下&#xff0c;价格普遍下滑&#xff0c;老黄家的1000系列也升级到1600系列&#xff0c;而AMD的扛把子蓝宝石RX580 8G价格更是一路…

智能优化算法应用:基于蛇优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蛇优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蛇优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蛇优化算法4.实验参数设定5.算法结果6.参考文…

J2EE技术(三)——JMS

一、JMS基本思想 JMS&#xff08;Java Message Service&#xff09;即java消息服务&#xff0c;提供了一组标准的java API&#xff0c;用于企业级的消息处理。通过JMS可以在java程序之间发送和接受消息以达到交换数据的目的。 一个JMS应用由以下几部分组成&#xff1a; 1、 …

ssh-keygen -t rsa执行后提示没有权限

ssh-keygen -t rsa生成密钥文件和私钥文件 产生原因&#xff1a;ssh-keygen命令不是被识别的内部或者外部命令&#xff0c;没有权限 解决方案&#xff1a;把这个命令加入到全局变量内&#xff0c;再执行&#xff0c;就OK了 1.找到git安装目录下ssh-keygen地址&#xff1a; 2.把…

windows安全警报怎么关闭_鄂州人注意!本周五上午,全市将进行防空警报鸣放...

根据《人民防空法》规定&#xff0c;经鄂州市政府批准&#xff0c;于2020年10月23日上午9时10分进行防空警报鸣放&#xff0c;届时请市民不要惊慌&#xff0c;保持正常的工作和生活秩序。■延伸若是战时防空警报响起&#xff0c;市民该怎么做&#xff1f;如今是和平年代&#x…

Visual Studio 添加 自定义 路径宏

在编辑VS工程包含路径和库路径时&#xff0c;有时需要添加第三方包的路径&#xff0c;比如c boost库&#xff0c; 为了协作的方便&#xff0c;不合适直接把本地绝对路径添加入工程设置&#xff0c;此时可以添加自定义路径宏&#xff0c; 然后参与协作的每个开发人员&#xff0c…

openwrt 19 overlay 空间不足_重视 | 山西一矿井瓦斯爆炸,有限空间作业切记注意安全...

新闻速递20日2时许&#xff0c;山西潞安集团左权阜生煤业井下发生一起瓦斯爆炸事故&#xff0c;造成4人遇难、1人受伤。目前事故救援基本结束&#xff0c;事故原因正在调查中。近年来有限空间作业事故频发2010年6月21日凌晨1时40分左右&#xff0c;平顶山市卫东区兴东二矿发生井…

扩展极小值—lhMorpEMin

扩展极小值&#xff08;5*5正方形 h100 显示结果二值化&#xff09; 函数&#xff1a;lhMorpEMin 说明&#xff1a;形态学扩展极小值 参数&#xff1a; src 输入图像 dst 输出图像 h 深度或动态范围阈值 se 结构元素 源码&#xff1a; void lhMorpEMin(const IplImage* src…

c++win32项目 如何显示后再删除一个绘图_CAD快速绘图,不可不知的50个经典技巧,看完不迷茫!...

1、文字镜像如何设置转动与不转动&#xff1f;A、在镜像前&#xff0c;输入MIRRTEXT指令B、输入新值0 代表不转动 &#xff1b;输入新值1代表转动C、MIRRTEXT指令完成后&#xff0c;输入MI镜像指令就OK了2、CAD的版本转换&#xff1f;A、CAD高版本可以打开所有低版本的图纸B、C…

PHP添加php-java-brideg模块(ubuntu环境)

1、下载php-java-bridge 下载地址&#xff1a;https://sourceforge.net/projects/php-java-bridge/files/Binary%20package/php-java-bridge_7.2.1/exploded/JavaBridge.jar/download 2、将JavaBridge.jar上传到linux服务器中。例如/usr/local/src/ 3、修改php.ini文件。用find…

YSlow简介与使用(转)

原文&#xff1a;http://www.huluboke.com/firebug-yslow/ 除了Firebug自带的网络监控功能以外&#xff0c;还有很多Firebug监控插件提供更为详细的监控信息报告&#xff0c;在此介绍其中的佼佼者&#xff0c;YSlow。YSlow的下载地址为&#xff1a;http://developer.yahoo.com/…

安卓手机阅读器_乐应用|安卓手机本地阅读的不二之选

现在属于个人的时间越来越碎片化&#xff0c;因此为了方便&#xff0c;很多人选择在手机上阅读书籍。不同的人&#xff0c;有不同的阅读喜好&#xff0c;所以合适的阅读软件没有最好&#xff0c;只有更好。比如有人喜欢追网络小说&#xff0c;应用中集成在线书库就比较重要&…