解耦、异步、削峰
传统模式:

传统模式缺点:
系统间耦合性太强,如上图所示,系统A在代码中直接调用系统B和系统C,如果将来系统D要接入,系统A还要修改代码,过于麻烦!

中间件模式优点:
将消息写入消息队列,要消息的系统自己从消息队列中订阅,从而系统A不再需要做任何修改
传统模式:

一些非必要的业务逻辑以同步的方式运行,太耗费时间。
中间件模式:

中间件模式的优点:
将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度
传统模式:

传统模式缺点:
并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常
中间件模式:

中间件模式的优点:
系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的。
1、ActiveMQ:更新比较慢、java开发的、万级吞吐量
2、RabbitMQ:相对ActiveMQ来说更新较快、erlang语言开发(erlang语言天生具有高并发的特效,但是熟悉erlang的人相对较少,好在社区比较活跃)
3、RocketMQ:支持分布式架构、Java语言开发可以定制化开发
4、Kafka:支持分布式架构、吞吐量十万级

使用场景:
在我们的电商中,在发送短信和商品上下架的时候使用了rebbitMQ
分析:在第二点说过了,引入消息队列后,系统可用性下降,在生产中,没人使用单机模式的消息队列,因为作为一个合格的程序员,应该对消息队列的高可用有很深刻的了解。
如果面试时问如何保证高可用?你的回答只是表明自己只会订阅和发布消息,面试官就会怀疑你是不是只是自己搭着玩,没有生产使用过。
以RCOKETMQ为例,它的集群就有多master模式、多master多slave异步复制模式、多master多slave同步双写模式。多master多slave模式部署架构图:

其实博主第一眼看到这个图,就觉得和kafka好像,只是NameServer集群,在kafka中用zookeeper代替,都是用来保存和发现master和slave用的,通信过程如下:
Producer与NameServer集群中的其中一个节点(随机)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Broker Master和Slave建立长连接,即可以从Broker Master订阅消息,也可以从Broker Slave订阅消息。
那么kafka呢,为了对比说明直接上kafka拓补结构:

如上图所示,一个典型的Kafka集群中包含若干Producer(可以是web前端产生的page view,或者是服务器日志,系统cpu,memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过broker,Consumer使用pull模式从broker订阅并消费消息。
至于rabbitMQ也有普通集群和镜像集群模式,自行去了解,比较简单。
1、RabbitMQ会发生一个ACK确认消息
2、RocketMQ会返回一个CONSUME_SUCCESS成功标志
3、Kafka每条消息会有一个offset,在消费后需要提交offset,让消息队列知道已经消费
分析:在使用消息队列过程中,应该做到消息不能多消费,也不能少消费。如果无法做到可靠性传输,可能给公司带来千万级别的财产损失。同样的,如果可靠性传输在使用过程中,没有考虑到,这不是给公司挖坑么。还是那句话,认真对待每一个项目,不要给公司挖坑。
三个角度:生产者弄丢数据、消息队列弄丢数据、消费者弄丢数据
RabbitMQ提供transaction和confirm来保证生产者不丢消息
1.1 transaction机制就是说在发送消息的时候开启事务<channel.txSelect()>,然后再发送消息,若发送过程中发生异常则会滚<channel.txRollback()>,成功则提交<channel.txCommit()>,缺点就是吞吐量下降
1.2 confirm模式生产上用的居多,一旦channel进入confirm模式,所有在该信道上的消息都会被指派一个唯一ID(从1开始),一旦消息投递到所匹配的队列都,RabbitMQ会发送一个Ack(包含消息的唯一ID)给生产者,这就知道已经消费处理了,如果RabbitMQ没有处理这条消息,则会发送一个Nack消息给你,这就知道消费失败,然后就可以重试了。
RabbitMQ消息队列丢数据
2.1 处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。
2.11 那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步
2.11 1. 将queue的持久化标识durable设置为true,则代表是一个持久的队列
2.11 2. 发送消息的时候将deliveryMode=2
这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据
RabbitMQ消费者丢数据
3.1 消费者丢数据一般是因为采用了自动确认消息模式(采用手动提交即可)
这里先引用一张kafka replication的效果图

Producer在发布消息到某个Partition时,先通过zookeeper找到该partition的leader,然后无论该topic的replication factor为多少(也即该partition有多少个replication),Producer只将该消息发送到该Partition的leader,leader会将该消息写入本地log,每个Follower都从Leader中pull数据。
Kafka生产丢失数据
4.1 在kafka生产中,基本都有一个leader和多个follwer。follwer会去同步leader的信息。因此,为了避免生产者丢数据,做如下两点配置
第一个配置要在producer端设置acks=all。这个配置保证了,follwer同步完成后,才认为消息发送成功。
在producer端设置retries=MAX,一旦写入失败,这无限重试
Kafka消息队列丢数据
5.1 针对消息队列丢数据的情况,无外乎就是,数据还没同步,leader就挂了,这时zookpeer会将其他的follwer切换为leader,那数据就丢失了。针对这种情况,应该做两个配置。
replication.factor参数,这个值必须大于1,即要求每个partition必须有至少2个副本
min.insync.replicas参数,这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系。这两个配置加上上面生产者的配置联合起来用,基本可确保kafka不丢数据
Kafka消费者丢数据
6.1 这种情况一般是自动提交了offset,然后你处理程序过程中挂了。kafka以为你处理好了。解决方案也很简单,改成手动提交即可。
6.2 offset介绍:
6.21 offset:指的是kafka的topic中的每个消费组消费的下标。简单的来说就是一条消息对应一个offset下标,每次消费数据的时候如果提交offset,那么下次消费就会从提交的offset加一那里开始消费。
比如一个topic中有100条数据,我消费了50条并且提交了,那么此时的kafka服务端记录提交的offset就是49(offset从0开始),那么下次消费的时候offset就从50开始消费。
请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是
我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行
我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是
转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev
打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性
Ruby中的Fixnum方法.next和.succ有什么区别?看起来它的工作原理是一样的:1.next=>21.succ=>2如果有什么不同,为什么有两种方法做同样的事情? 最佳答案 它们是等价的。Fixnum#succ只是Fixnum#next的同义词。他们甚至在thereferencemanual中共享同一block. 关于ruby-Ruby中.next和.succ的区别,我们在StackOverflow上找到一个类似的问题: https://stacko
我明白了defa(&block)block.call(self)end和defa()yieldselfend导致相同的结果,如果我假设有这样一个blocka{}。我的问题是-因为我偶然发现了一些这样的代码,它是否有任何区别或者是否有任何优势(如果我不使用变量/引用block):defa(&block)yieldselfend这是一个我不理解&block用法的具体案例:defrule(code,name,&block)@rules=[]if@rules.nil?@rules 最佳答案 我能想到的唯一优点就是自省(introspecti
由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A
在Ruby中,我试图理解to_enum和enum_for方法。在我提出问题之前,我提供了一些示例代码和两个示例来帮助理解上下文。示例代码:#replicatesgroup_bymethodonArrayclassclassArraydefgroup_by2(&input_block)returnself.enum_for(:group_by2)unlessblock_given?hash=Hash.new{|h,k|h[k]=[]}self.each{|e|hash[input_block.call(e)]示例#1:irb(main)>puts[1,2,3].group_by2.ins
关于SSHkit-Github它说:Allbackendssupporttheexecute(*args),test(*args)&capture(*args)来自SSHkit-Rubydoc,我明白execute实际上是test的别名?test之间有什么区别?,execute,capture在Capistrano/SSHKit中我应该什么时候使用? 最佳答案 执行只是执行命令。使用非0退出引发错误。测试方法的行为与execute完全相同,但是它返回bool值(true如果命令以0退出,而false否则)。它通常用于控制任务中的流程