每周 ARTS第12 期

一. Algorithm

本周做的是 「92.Reverse Linked List II」。 是上周做的 「206.Reverse Linked List」反转单列表的改进。题目要求给定 m 和 n 两个索引,反转索引之内的列表。

基本思路是:

找到索引 m 处 node 的前一个 preNode

反转 m 到 n 区间的链表

处理边界,原来的 preNode 的 next 为原来索引为 n 处的 node; 而原 m Node 的 next 变为 原来 n 处 Node 的 next

难点在于搞清楚变换过程中节点指针之间的变换,很容易被绕晕。 代码如下:

classSolution{

publicListNodereverseBetween(ListNodehead,intm,intn) {

if(head==null||head.next==null) {

returnhead;

}

ListNoderesult=head;

intpos=1;

ListNodepreNode=null;

ListNodetmpNode=head;

while(pos

preNode=tmpNode;

tmpNode=tmpNode.next;

pos++;

}

ListNodemHeadNode=tmpNode;

ListNodemTailNode=null;

ListNodepreTmpNode=tmpNode;

while(pos

pos++;

// 记录 n 节点

mTailNode=tmpNode;

ListNodenext=tmpNode.next;

tmpNode.next=preTmpNode;

preTmpNode=tmpNode;

tmpNode=next;

}

mHeadNode.next=tmpNode;

if(preNode==null) {

returnmTailNode;

}else{

preNode.next=mTailNode;

returnresult;

}

}

}

时间复杂度为 O(n), 空间复杂度为 O(n)。

二. Review

本周读了耗子叔在练级攻略中推荐的一篇文章 「Understanding When to use RabbitMQ or Apache Kafka」。

作者在针对消息队列应用对 Kafka 和 RabbitMQ 作了一系列的对比。主要对比了如下几个方面:

1. 起源

RabbitMQ 是最早实现 AMQP 协议的消息队列,在此之前消息队列应用都是依赖于语言平台的,比如 JMS 是依赖于 Java 平台,其他语言平台无法使用。通过实现 AMQP 协议使得消息队列应用实现了跨语言应用,极大提高了其灵活性。

Kafka 最初起源于 LinkedIn, 用 Scala 语言编写。起初是为了解决领英采用分布式架构后不同系统之间的交互问题,尤其是数据集成和实时流的处理。现在已并入 Apache 基金会,Kafka 非常适合用在事件驱动的系统中。

2. 架构设计

下面是 RabbitMQ 的架构图。

RabbitMQ 的设计目标是成为一个通用的消息中间件,采用点对点、请求响应、pub/sub 等多种方式实现消息的传递。从图中也可以看出,其消息是从 producer 传递到了 exchange,然后经由 queue 传递到 consumer。实现了 queue 与 producer 的解耦,这样避免了 producer 发送时需要硬编码指定队列,只需要将消息交给对应的 exchange 交换器,然后在根据需要发送不到不同的队列由不同的 consumer 获取即可,极大的提高了消息分发的灵活性。另外 RabbitMQ 也支持集群部署,提高了服务的可用性。

下面是 Apache Kafka 的架构图。

前面起源中提到 Apache Kafka 注重解决数据集成和实时流的处理,此时必须要保证数据处理的性能以及数据的安全性,这从开始注定了 Kafka 其高效、持久化和可扩展性的特性。Kafka 本质上是在一个集群中提供了类似 log 的信息持久化存储功能,并按 topic 进行分类。

Kafka 中每条 message 有 key、value 和 一个 timestamp 时间戳组成。和 RabbitMQ 有所不同的是,RabbitMQ 的中间件消息发布十分灵活,consumer 只管按需接收消息即可,不需要做过多处理,即 smart broker/dumb consumer。而 Kafka 的理念是 dumb broker/smart consumer。Kafka 并不关心某条信息是否被每一个 consumer 接收,其只维护存储未被消费过的 message,根据配置将数据保留一段时间,同时会有多个备份保证数据可用性。而 consumer 自己负责来确保其追踪到了每条 message 的接收。因为不需要做额外的追踪处理,而专注于 message 的存储,Kafka 可以在不消耗非常高的资源下支持大量的 consumer 和高容量的数据存储。

另外需要注意的一点是 RabbitMQ 是不依赖外部服务的,而 Kafka 需要依赖于 zookeeper 服务,zookeeper 是一个高性能分布式应用协调服务器,具体功能和细节就不在这里赘述了。现在自己也只是会最简单的配置启动,后续继续学习时在认真钻研。

3. 使用场景

基于起源定位与架构的不同,RabbitMQ 更多的定位于通用的消息中间件,使我们可以解耦服务,而 Kafka 的强项在于大数据量下的实时流处理,其最近新增的 Kafka Stream 定位于类似 Apache Spark, Apache Flink, Apache Beam/Google Cloud Data Flow and Spring Cloud Data Flow 等技术的流媒体平台方案。

这是其官网使用场景文档,总结来说 Kafka有如下几个使用场景:

服务A 到 B 的高吞吐量的流处理,保证其按分区顺序至少发送一次

应用需要访问数据流历史记录时,Kafka 可以满足需求。因为 Kafka 是一个持久化的消息存储机制,因此其可以根据客户端的需要重播事件,这一点和传统消息队列发送成功即删除消息的机制有所不同

日志聚合、指标监控

流处理与事件驱动架构

RabbitMQ 作为标准的 AMQP 实现,其提供了众多优秀的特性,比如可靠的分发机制,灵活的路由,高可用的集群以及便捷的管理工具,其主要使用场景如下:

新的服务需要与已有的基于 AMQP 协议的组件交互时考虑使用 RabbitMQ

针对每个 message 进行细粒度的控制管理与发送保证。(这一点 Kafka 现在做的更好 = = )

系统需要复杂的消息分发、pub/sub 服务

另外关于微服务中两者的使用比较,作者给出了另一篇博客 「Messaging Patterns for Event-Driven Microservices」,还没读,暂时作为下周的 ARTS 部分在读吧。

4. 开发体验

RabbitMQ 基本提供了主流语言和开发框架的支持,Java、Go、Python、PHP 以及 Spring、.NET 都对其有良好的支持,并且提供了良好的 API 与 文档。

对于 Kafka 官方提供了Java 的 API,但是开源社区提供了大量第三方库供开发人员使用,基本可以满足要求。地址在这

总体而言两者作为热门的、使用广泛的技术,都对开发提供了良好的 API 与文档,开发起来还是比较顺畅的。

5. 安全与运维

这一点比较上 RabbitMQ 具有压倒性的优势,其提供了丰富的监控 UI 与管理 CLI,还有第三方服务 CollectD, Datadog, or New Relic 等提供了优秀的监控运维功能。在安全方面除了支持 TSL 外还提供了 RBAC 访问控制,除了用户名密码的形式还支持 x509 certificate 证书校验。

Kafka 这方面做得暂时并不好,不过文章中拿的是 0.9 版本进行比较,现在 Kafka 已经更新到了 2.0,应该很多特性已经完善了许多,后续深入学习的时候在了解这块吧。

6. 性能

这里就是 Kafka 的强项了,其支持 100k/sec 的高吞吐的性能是人们选择 Kafka 的原因。

单个 RabbitMQ 节点可以支持 20K/sec 的消息量,其有单个在本机 OS 线程池中的一个 Erlang 轻量级线程进行调度,因此其被 CPU 调度的的时间很容易成为瓶颈。

当消息量逐渐增加时,可以通过 RabbitMQ 集群保证性能,一般来说一个 3 ~ 7 节点的集群可以提供足够优秀的性能。当达到 30 个节点时也可以达到 100k/sec 的性能。

消息队列的性能也会受到环境、硬件、负载、任务性质的影响,在衡量性能时都要综合考虑。

7. 总结

作者是基于一篇论文中的表格得到灵感,因此做了这篇调研文章,同时提到当做技术决策时也要尽可能多的了解业务部门的需求和行业动态,做出最合适的决定。

以上就是针对本周文章的 Review,作者从起源、架构、使用场景、开发体验、安全、运维管理、性能几个方面为我们比较了 Kafka 和 RabbitMQ 之间的差异,使我们能在具体细节之外站在一定高度之上理解这两门看起来差不多的技术的异同。同时这篇也提供了一个比较开源技术的范本,通过了解起源可以知道该技术解决的最基本问题是什么,通过了解架构可以从全局把握技术的原理与性质,也有助于理解其最合适的使用场景。理解其使用场景可以让我们做出更合适的技术决策,同时开发体验、安全、运维、性能也在技术层面为我们做技术决策提供了全面的参考依据,使我们最初更加适合当前业务、当前团队的技术决策。

三. Tip

在使用云服务器的时候,有时候 ifconfig 不会展示其公网 IP,要看的话只能去云服务厂商的控制台查看,不太方便。这里分享一个查看本机公网 IP 的小技巧。

curl ifconfig.me

此时会打印出本机的公网 IP。

在浏览器中输入网址会出现如下界面:

第一部分是本机网络信息,第二部分是 ifconfig.me 的一些其他命令,通过这些命令可以在命令行查询本机的网络信息。

四. Share

今天和同事吃饭时聊到了学到的东西很久不用就会忘的问题,同事之前看了设计模式相关的内容,但是现在貌似也只记得那些模式的名字了,具体的写法和应用也记不太清了。

这个是自己非常有体会的问题,自己也一直存在类似的问题,有时候也会因此而感觉到焦虑。简单写两点自己最近的思考和改进吧:

尽可能的找到应用场景

学以致用是最有效的学习手段之一,举一个自己的例子,之前也看过设计模式但也只是停留在知道名称阶段。后来有一次在做项目时有一个添加多种相似数据的实现,流程基本一致但是不同种类的数据具体操作不同。开始一个一个的类去做了单独处理,后来 Review 时突然意识到可以将过程抽象出来,然后具体细节交由各个子类实现,典型的模板方法模式。虽然之前写 Android 时知道其 Activity 生命周期的代码模式就是模板方法模式,但对其一直没有深刻的理解。经过这里项目中的实际应用,过了很长一段时间仍然记忆犹新,因此多去尝试,不断练习增加应用经验是保证知识扎实掌握的有效方法。不仅仅在工作中,也要在平时的学习中主动去练习。

重原理,轻应用

之前在学习一门知识尤其是 IT 类知识的的时候。很容易沉溺于细节,尤其是各种 API 、命令无法自拔。不仅耗时,而且还容易忘,时间久了很容易有挫败感。自己之前学 ES 的时候有段时间就过度关注与其 SearchAPI 与 聚合分析 API 的应用,但因为并没有经常使用又导致忘记。后来改变策略,平常不去注重这些应用层的东西,转过头专注于其集群管理、性能调优、查询机制等原理性的知识,然后在通过项目应用去边做边查各种 API 的使用,这样因为之前相关的原理都了解了,应用时对何种 API 具体发生了什么事,有何影响,性能怎样都比较清楚,反过来也加深了 API 的掌握和使用。

关注高效学习的方法介绍已经很多了,耗子叔专栏里面也提有讲到,方法都懂了最后能不能真正以合适的方式去应用就是需要自己不断尝试的了,欢迎小伙伴们分享自己更高效的学习方法。

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20181024G23XDT00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券