中间件 订阅
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。目前,它并没有很严格的定义,但是普遍接受IDC的定义:中间件是一种独立的系统软件服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。从这个意义上可以用一个等式来表示中间件:中间件=平台+通信,这也就限定了只有用于分布式系统中才能叫中间件,同时也把它与支撑软件和实用软件区分开来。 [1] 展开全文
中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。目前,它并没有很严格的定义,但是普遍接受IDC的定义:中间件是一种独立的系统软件服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。从这个意义上可以用一个等式来表示中间件:中间件=平台+通信,这也就限定了只有用于分布式系统中才能叫中间件,同时也把它与支撑软件和实用软件区分开来。 [1]
信息
性    质
计算机用语
外文名
middleware
相关概念
企业服务总线
中文名
中间件
实    质
独立的系统软件或服务程序
中间件简介
中间件是一类连接软件组件和应用的计算机软件,它包括一组服务。以便于运行在一台或多台机器上的多个软件通过网络进行交互。该技术所提供的互操作性,推动了一致分布式体系架构的演进,该架构通常用于支持并简化那些复杂的分布式应用程序,它包括web服务器、事务监控器和消息队列软件。 [2]  中间件(middleware)是基础软件的一大类,属于可复用软件的范畴。顾名思义,中间件处于操作系统软件与用户的应用软件的中间。 [2]  中间件在操作系统、网络和数据库之上,应用软件的下层,总的作用是为处于自己上层的应用软件提供运行与开发的环境,帮助用户灵活、高效地开发和集成复杂的应用软件。在众多关于中间件的定义中,比较普遍被接受的是IDC表述的:中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。 [2]  近年来,人类生活中越来越多的领域已经变得离不开计算机、网络技术以及通信技术。并且随着计算机技术的快速发展,更多的应用软件被要求在许多不同的网络协议、不同的硬件生产厂商以及不一样的网络平台和环境上运营。这导致了软件开发者需要面临数据离散、操作困难、系统匹配程度低,以及需要开发多种应用程序来达到运营的目的。所以,中间件技术的产生,在极大程度上减轻了开发者的负担,使得网络的运行更有效率。 [2] 
收起全文
精华内容
参与话题
问答
  • 消息中间件MQ与RabbitMQ面试题(2020最新版)

    万次阅读 多人点赞 2020-03-01 11:11:21
    你们公司生产环境用的是什么消息中间件?Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?MQ 有哪些常见问题?如何解决这些问题?什么是RabbitMQ?rabbitmq 的使用场景RabbitMQ基本概念RabbitMQ的工作模式如何...

    Java面试总结汇总,整理了包括Java基础知识,集合容器,并发编程,JVM,常用开源框架Spring,MyBatis,数据库,中间件等,包含了作为一个Java工程师在面试中需要用到或者可能用到的绝大部分知识。欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章持续更新中…

    序号 内容 链接地址
    1 Java基础知识面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390612
    2 Java集合容器面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588551
    3 Java异常面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390689
    4 并发编程面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104863992
    5 JVM面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390752
    6 Spring面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397516
    7 Spring MVC面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397427
    8 Spring Boot面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397299
    9 Spring Cloud面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397367
    10 MyBatis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/101292950
    11 Redis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/103522351
    12 MySQL数据库面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104778621
    13 消息中间件MQ与RabbitMQ面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588612
    14 Dubbo面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390006
    15 Linux面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588679
    16 Tomcat面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397665
    17 ZooKeeper面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397719
    18 Netty面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104391081
    19 架构设计&分布式&数据结构与算法面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/105870730

    为什么使用MQ?MQ的优点

    简答

    • 异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
    • 应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
    • 流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
    • 日志处理 - 解决大量日志传输。
    • 消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。

    详答

    主要是:解耦、异步、削峰。

    解耦:A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃…A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。

    就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦。

    异步:A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求。如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms。

    削峰:减少高峰时期对服务器压力。

    消息队列有什么优缺点?RabbitMQ有什么优缺点?

    优点上面已经说了,就是在特殊场景下有其对应的好处解耦异步削峰

    缺点有以下几个:

    系统可用性降低

    本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低;

    系统复杂度提高

    加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。

    一致性问题

    A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。

    所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了 10 倍。但是关键时刻,用,还是得用的。

    你们公司生产环境用的是什么消息中间件?

    这个首先你可以说下你们公司选用的是什么消息中间件,比如用的是RabbitMQ,然后可以初步给一些你对不同MQ中间件技术的选型分析。

    举个例子:比如说ActiveMQ是老牌的消息中间件,国内很多公司过去运用的还是非常广泛的,功能很强大。

    但是问题在于没法确认ActiveMQ可以支撑互联网公司的高并发、高负载以及高吞吐的复杂场景,在国内互联网公司落地较少。而且使用较多的是一些传统企业,用ActiveMQ做异步调用和系统解耦。

    然后你可以说说RabbitMQ,他的好处在于可以支撑高并发、高吞吐、性能很高,同时有非常完善便捷的后台管理界面可以使用。

    另外,他还支持集群化、高可用部署架构、消息高可靠支持,功能较为完善。

    而且经过调研,国内各大互联网公司落地大规模RabbitMQ集群支撑自身业务的case较多,国内各种中小型互联网公司使用RabbitMQ的实践也比较多。

    除此之外,RabbitMQ的开源社区很活跃,较高频率的迭代版本,来修复发现的bug以及进行各种优化,因此综合考虑过后,公司采取了RabbitMQ。

    但是RabbitMQ也有一点缺陷,就是他自身是基于erlang语言开发的,所以导致较为难以分析里面的源码,也较难进行深层次的源码定制和改造,毕竟需要较为扎实的erlang语言功底才可以。

    然后可以聊聊RocketMQ,是阿里开源的,经过阿里的生产环境的超高并发、高吞吐的考验,性能卓越,同时还支持分布式事务等特殊场景。

    而且RocketMQ是基于Java语言开发的,适合深入阅读源码,有需要可以站在源码层面解决线上生产问题,包括源码的二次开发和改造。

    另外就是Kafka。Kafka提供的消息中间件的功能明显较少一些,相对上述几款MQ中间件要少很多。

    但是Kafka的优势在于专为超高吞吐量的实时日志采集、实时数据同步、实时数据计算等场景来设计。

    因此Kafka在大数据领域中配合实时计算技术(比如Spark Streaming、Storm、Flink)使用的较多。但是在传统的MQ中间件使用场景中较少采用。

    Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?

    ActiveMQ RabbitMQ RocketMQ Kafka ZeroMQ
    单机吞吐量 比RabbitMQ低 2.6w/s(消息做持久化) 11.6w/s 17.3w/s 29w/s
    开发语言 Java Erlang Java Scala/Java C
    主要维护者 Apache Mozilla/Spring Alibaba Apache iMatix,创始人已去世
    成熟度 成熟 成熟 开源版本不够成熟 比较成熟 只有C、PHP等版本成熟
    订阅形式 点对点(p2p)、广播(发布-订阅) 提供了4种:direct, topic ,Headers和fanout。fanout就是广播模式 基于topic/messageTag以及按照消息类型、属性进行正则匹配的发布订阅模式 基于topic以及按照topic进行正则匹配的发布订阅模式 点对点(p2p)
    持久化 支持少量堆积 支持少量堆积 支持大量堆积 支持大量堆积 不支持
    顺序消息 不支持 不支持 支持 支持 不支持
    性能稳定性 一般 较差 很好
    集群方式 支持简单集群模式,比如’主-备’,对高级集群模式支持不好。 支持简单集群,'复制’模式,对高级集群模式支持不好。 常用 多对’Master-Slave’ 模式,开源版本需手动切换Slave变成Master 天然的‘Leader-Slave’无状态集群,每台服务器既是Master也是Slave 不支持
    管理界面 一般 较好 一般

    综上,各种对比之后,有如下建议:

    一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了;

    后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;

    不过现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 Apache,但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,人家有活跃的开源社区,绝对不会黄。

    所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。

    如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。

    MQ 有哪些常见问题?如何解决这些问题?

    MQ 的常见问题有:

    1. 消息的顺序问题
    2. 消息的重复问题

    消息的顺序问题

    消息有序指的是可以按照消息的发送顺序来消费。

    假如生产者产生了 2 条消息:M1、M2,假定 M1 发送到 S1,M2 发送到 S2,如果要保证 M1 先于 M2 被消费,怎么做?

    img

    解决方案:

    (1)保证生产者 - MQServer - 消费者是一对一对一的关系

    img

    缺陷:

    • 并行度就会成为消息系统的瓶颈(吞吐量不够)
    • 更多的异常处理,比如:只要消费端出现问题,就会导致整个处理流程阻塞,我们不得不花费更多的精力来解决阻塞的问题。 (2)通过合理的设计或者将问题分解来规避。
    • 不关注乱序的应用实际大量存在
    • 队列无序并不意味着消息无序 所以从业务层面来保证消息的顺序而不仅仅是依赖于消息系统,是一种更合理的方式。

    消息的重复问题

    造成消息重复的根本原因是:网络不可达。

    所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

    消费端处理消息的业务逻辑保持幂等性。只要保持幂等性,不管来多少条重复消息,最后处理的结果都一样。保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现。利用一张日志表来记录已经处理成功的消息的 ID,如果新到的消息 ID 已经在日志表中,那么就不再处理这条消息。

    什么是RabbitMQ?

    RabbitMQ是一款开源的,Erlang编写的,基于AMQP协议的消息中间件

    rabbitmq 的使用场景

    (1)服务间异步通信

    (2)顺序消费

    (3)定时任务

    (4)请求削峰

    RabbitMQ基本概念

    • Broker: 简单来说就是消息队列服务器实体
    • Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列
    • Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
    • Binding: 绑定,它的作用就是把exchange和queue按照路由规则绑定起来
    • Routing Key: 路由关键字,exchange根据这个关键字进行消息投递
    • VHost: vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。
    • Producer: 消息生产者,就是投递消息的程序
    • Consumer: 消息消费者,就是接受消息的程序
    • Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务

    由Exchange、Queue、RoutingKey三个才能决定一个从Exchange到Queue的唯一的线路。

    RabbitMQ的工作模式

    一.simple模式(即最简单的收发模式)

    img

    1.消息产生消息,将消息放入队列

    2.消息的消费者(consumer) 监听 消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除(隐患 消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失,这里可以设置成手动的ack,但如果设置成手动ack,处理完后要及时发送ack消息给队列,否则会造成内存溢出)。

    二.work工作模式(资源的竞争)

    img

    1.消息产生者将消息放入队列消费者可以有多个,消费者1,消费者2同时监听同一个队列,消息被消费。C1 C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息(隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize) 保证一条消息只能被一个消费者使用)。

    三.publish/subscribe发布订阅(共享资源)

    img

    1、每个消费者监听自己的队列;

    2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。

    四.routing路由模式

    img

    1.消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) 当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息;

    2.根据业务功能定义路由字符串

    3.从系统的代码逻辑中获取对应的功能字符串,将消息任务扔到对应的队列中。

    4.业务场景:error 通知;EXCEPTION;错误通知的功能;传统意义的错误通知;客户通知;利用key路由,可以将程序中的错误封装成消息传入到消息队列中,开发者可以自定义消费者,实时接收错误;

    五.topic 主题模式(路由模式的一种)

    img

    1.星号井号代表通配符

    2.星号代表多个单词,井号代表一个单词

    3.路由功能添加模糊匹配

    4.消息产生者产生消息,把消息交给交换机

    5.交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费

    (在我的理解看来就是routing查询的一种模糊匹配,就类似sql的模糊查询方式)

    如何保证RabbitMQ消息的顺序性?

    拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。

    消息如何分发?

    若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。通过路由可实现多消费的功能

    消息怎么路由?

    消息提供方->路由->一至多个队列消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。通过队列路由键,可以把队列绑定到交换器上。消息到达交换器后,RabbitMQ 会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则);

    常用的交换器主要分为一下三种:

    fanout:如果交换器收到消息,将会广播到所有绑定的队列上

    direct:如果路由键完全匹配,消息就被投递到相应的队列

    topic:可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符

    消息基于什么传输?

    由于 TCP 连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制。

    如何保证消息不被重复消费?或者说,如何保证消息消费时的幂等性?

    先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除;

    但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。

    针对以上问题,一个解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性;

    比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过;

    假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。

    如何确保消息正确地发送至 RabbitMQ? 如何确保消息接收方消费了消息?

    发送方确认模式

    将信道设置成 confirm 模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的 ID。

    一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。

    如果 RabbitMQ 发生内部错误从而导致消息丢失,会发送一条 nack(notacknowledged,未确认)消息。

    发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。

    接收方确认机制

    消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。

    这里并没有用到超时机制,RabbitMQ 仅通过 Consumer 的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ 给了 Consumer 足够长的时间来处理消息。保证数据的最终一致性;

    下面罗列几种特殊情况

    • 如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ 会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
    • 如果消费者接收到消息却没有确认消息,连接也未断开,则 RabbitMQ 认为该消费者繁忙,将不会给该消费者分发更多的消息。

    如何保证RabbitMQ消息的可靠传输?

    消息不可靠的情况可能是消息丢失,劫持等原因;

    丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息;

    生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息;

    transaction机制就是说:发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降;

    confirm模式用的居多:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后;

    rabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了;

    如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

    消息队列丢数据:消息持久化。

    处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。

    这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。

    这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

    那么如何持久化呢?

    这里顺便说一下吧,其实也很容易,就下面两步

    1. 将queue的持久化标识durable设置为true,则代表是一个持久的队列
    2. 发送消息的时候将deliveryMode=2

    这样设置以后,即使rabbitMQ挂了,重启后也能恢复数据

    消费者丢失消息:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可!

    消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息;

    如果这时处理消息失败,就会丢失该消息;

    解决方案:处理消息成功后,手动回复确认消息。

    为什么不应该对所有的 message 都使用持久化机制?

    首先,必然导致性能的下降,因为写磁盘比写 RAM 慢的多,message 的吞吐量可能有 10 倍的差距。

    其次,message 的持久化机制用在 RabbitMQ 的内置 cluster 方案时会出现“坑爹”问题。矛盾点在于,若 message 设置了 persistent 属性,但 queue 未设置 durable 属性,那么当该 queue 的 owner node 出现异常后,在未重建该 queue 前,发往该 queue 的 message 将被 blackholed ;若 message 设置了 persistent 属性,同时 queue 也设置了 durable 属性,那么当 queue 的 owner node 异常且无法重启的情况下,则该 queue 无法在其他 node 上重建,只能等待其 owner node 重启后,才能恢复该 queue 的使用,而在这段时间内发送给该 queue 的 message 将被 blackholed 。

    所以,是否要对 message 进行持久化,需要综合考虑性能需要,以及可能遇到的问题。若想达到 100,000 条/秒以上的消息吞吐量(单 RabbitMQ 服务器),则要么使用其他的方式来确保 message 的可靠 delivery ,要么使用非常快速的存储系统以支持全持久化(例如使用 SSD)。另外一种处理原则是:仅对关键消息作持久化处理(根据业务重要程度),且应该保证关键消息的量不会导致性能瓶颈。

    如何保证高可用的?RabbitMQ 的集群

    RabbitMQ 是比较有代表性的,因为是基于主从(非分布式)做高可用性的,我们就以 RabbitMQ 为例子讲解第一种 MQ 的高可用性怎么实现。RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。

    单机模式,就是 Demo 级别的,一般就是你本地启动了玩玩儿的?,没人生产用单机模式

    普通集群模式,意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。你创建的 queue,只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据(元数据可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来。这方案主要是提高吞吐量的,就是说让集群中多个节点来服务某个 queue 的读写操作。

    镜像集群模式:这种模式,才是所谓的 RabbitMQ 的高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,你创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,就是说,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。RabbitMQ 有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。这样的话,好处在于,你任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。坏处在于,第一,这个性能开销也太大了吧,消息需要同步到所有机器上,导致网络带宽压力和消耗很重!RabbitMQ 一个 queue 的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个 queue 的完整数据。

    如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?

    消息积压处理办法:临时紧急扩容:

    先修复 consumer 的问题,确保其恢复消费速度,然后将现有 cnosumer 都停掉。
    新建一个 topic,partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量。
    然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue。
    接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据。
    等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的 consumer 机器来消费消息。
    MQ中消息失效:假设你用的是 RabbitMQ,RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是大量的数据会直接搞丢。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来。也只能是这样了。假设 1 万个订单积压在 mq 里面,没有处理,其中 1000 个订单都丢了,你只能手动写程序把那 1000 个订单给查出来,手动发到 mq 里去再补一次。

    mq消息队列块满了:如果消息积压在 mq 里,你很长时间都没有处理掉,此时导致 mq 都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案,到了晚上再补数据吧。

    设计MQ思路

    比如说这个消息队列系统,我们从以下几个角度来考虑一下:

    首先这个 mq 得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下 kafka 的设计理念,broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给 topic 增加 partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?

    其次你得考虑一下这个 mq 的数据要不要落地磁盘吧?那肯定要了,落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路。

    其次你考虑一下你的 mq 的可用性啊?这个事儿,具体参考之前可用性那个环节讲解的 kafka 的高可用保障机制。多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务。

    能不能支持数据 0 丢失啊?可以的,参考我们之前说的那个 kafka 数据零丢失方案。

    展开全文
  • 中间件

    2019-03-16 13:03:12
    (django封装了7个中间件,settings里有middleware源码,一个中间件就是一个类,执行中间件,)执行每个中间件的process request 路由分发给试图函数 调用数据库models和模板层templates,取出数据返回消息 ...

    django声明请求周期

    • 浏览器发送请求

    • wsgiref收到请求信息解析封装成request

    • (django封装了7个中间件,settings里有middleware源码,一个中间件就是一个类,执行中间件,)执行每个中间件的process request

    • 路由分发给试图函数

    • 调用数据库models和模板层templates,取出数据返回消息

    • 执行中间件process response

    • wsgiref 封装数据按照协议

    • 发送给浏览器,浏览器解析页面

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    中间件中一共有四个方法,按顺序执行:
    
    process_request  	def process_request(self,request):
    process_view 		def process_view(self, request, callback, callback_args, callback_kwargs):
    process_exception	def process_exception(self, request, exception):
    process_response	def process_response(self,request,response):
    

    中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

    自定义中间件

    • 需要导入from django.utils.deprecation import MiddlewareMixin
    • 写一个类,但是必须继承MiddlewareMixin
    • 路径加入settings
        "app01.my_middlewares.CustomerMiddleware",
        "app01.my_middlewares.CustomerMiddleware2"
    
    • 定义方法,注意参数!!!

    示例:注意执行顺序

    in views:

    
    def index(request):
    
        print("view函数...")
        return HttpResponse("OK")
    

    in Mymiddlewares.py

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    
    class Md1(MiddlewareMixin):
    
       def process_request(self,request):
           print("Md1请求")
    
       def process_response(self,request,response):
           print("Md1返回")
           return response
    
    class Md2(MiddlewareMixin):
    
       def process_request(self,request):
           print("Md2请求")
           #return HttpResponse("Md2中断")
       def process_response(self,request,response):
           print("Md2返回")
           return response
    结果:
    
    Md1请求
    Md2请求
    view函数...
    Md2返回
    Md1返回
    注意:如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下:
    
    返回Md2中断的页面,后台打印如下:
    
    Md1请求  
    Md2请求  
    Md2返回  
    Md1返回
    
    class Md1(MiddlewareMixin):
    
        def process_request(self,request):
            print("Md1请求")
            #return HttpResponse("Md1中断")
        def process_response(self,request,response):
            print("Md1返回")
            return response
        def process_view(self, request, callback, callback_args, callback_kwargs):
            # return HttpResponse("hello")
            # response=callback(request,*callback_args,**callback_kwargs)
            # return response
            print("md1 process_view...")
        def process_exception(self):
            print("md1 process_exception...")
    class Md2(MiddlewareMixin):
        def process_request(self,request):
            print("Md2请求")
            # return HttpResponse("Md2中断")
        def process_response(self,request,response):
            print("Md2返回")
            return response
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("md2 process_view...")
        def process_exception(self):
            print("md1 process_exception...")
    
    Md1请求
    Md2请求
    md1 process_view...
    md2 process_view...
    view函数...
    
    Md2返回
    Md1返回
    
    

    当views出现错误时:

    Md1请求
    Md2请求
    md1 process_view...
    md2 process_view...
    view函数...
    md2 process_exception...
    Md2返回
    Md1返回
    

    总结:

    1. request没有返回值,response必须有返回值,返回值是前面方法的执行结果
    2. 依次执行各中间件的相同方法
    3. process_exception 遇到函数执行异常时调用
    4. 遇到每一步,如果有return即跳转到执行response方法

    应用:

    • 1、做IP访问频率限制

    某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

    • 2、URL访问过滤

    如果用户访问的是login视图(放过)
    如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!

    from demo import settings
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse,redirect
    
    class AuthMiddleware(MiddlewareMixin):
        def process_request(self,request):
    
            if request.path in settings.white_list:
                if not request.user.is_authenticated:
                    return redirect('/login/')
    
    展开全文
  • 中间件相关问题整理

    万次阅读 2020-04-07 11:50:15
    备注:针对基本问题做一些基本的总结,不是详细解答! 1.用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗? 2.MQ系统的数据如何保证不丢失? ...

    目录

    1.用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗?

    一、常见的消息中间件

    1.Kafka主要特性、优缺点

    2.RabbitMQ主要特性、优缺点

    3.RocketMQ主要特性、优缺点

    4.ActiveMQ主要特性、优缺点

    二、消息中间件相关知识

    1.消息中间件的组成

    2.消息中间件模式分类

    点对点

    3.消息中间件的普遍优势

    4.消息中间件常用协议

    三、消息队列应用场景

    场景一:异步处理

    场景二:应用解耦

    场景三:流量削锋(一般在秒杀或团抢活动中使用广泛)

    场景四:日志处理

    场景五:消息通讯

    2.MQ系统的数据如何保证不丢失?

    一、MQ原则

    二、丢失数据场景分析

    (一)rabbitmq丢失数据场景分析

    1.生产者弄丢了数据

    2.rabbitmq自己丢了数据

    3.消费端弄丢了数据

    (二)kafka丢失数据场景分析

    三、如何防止消息丢失

    (一)rabbitmq防止消息丢失

    1.生产者丢失消息

    2.rabbitmq自己弄丢了数据

    3.消费者弄丢了数据

    (二)kafka防止消息丢失

    1.消费端弄丢了数据

    2.kafka弄丢了数据

    3.生产者弄丢了数据

    补充问题:

    关于MQ的几件小事(五)如何保证消息按顺序执行

    关于MQ的几件小事(三)如何保证消息不重复消费

    关于MQ的几件小事(二)如何保证消息队列的高可用

    Kafka的文件存储机制

    Kafka 如何保证可靠性

    Kafka消息是采用Pull模式,还是Push模式

    Kafka是如何实现高吞吐率的

    Kafka判断一个节点还活着的两个条件

    参考书籍、文献和资料


    备注:针对基本问题做一些基本的总结,不是详细解答!

    1.用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗?

    一、常见的消息中间件

    消息队列中间件是分布式系统中重要的组件,主要解决应用耦合、异步消息、流量削锋等问题,实现高性能、高可用、可伸缩和最终一致性架构。

    当前使用较多的消息队列有RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMQ等,而部分数据库如Redis、MySQL以及phxsql也可实现消息队列的功能

    1.Kafka主要特性、优缺点

    Apache Kafka是一个分布式消息发布订阅系统。Kafka性能高效、可扩展良好并且可持久化。它的分区特性、可复制和可容错都是其不错的特性。

    主要特性

    • 快速持久化:可以在O(1)的系统开销下进行消息持久化
    • 高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
    • 完全的分布式系统:Broker、Producer和Consumer都原生自动支持分布式,自动实现负载均衡
    • 支持同步和异步复制两种高可用机制;
    • 支持数据批量发送和拉取
    • 零拷贝技术(zero-copy):减少IO操作步骤,提高系统吞吐量
    • 数据迁移、扩容对用户透明;
    • 无需停机即可扩展机器;
    • 其他特性:丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制

    优点

    • 客户端语言丰富:支持Java、.Net、PHP、Ruby、Python、Go等多种语言
    • 高性能:单机写入TPS约在100万条/秒,消息大小10个字节;
    • 提供完全分布式架构,并有replica机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积
    • 支持批量操作
    • 消费者采用Pull方式获取消息。消息有序,通过控制能够保证所有消息被消费且仅被消费一次;
    • 有优秀的第三方KafkaWeb管理界面Kafka-Manager
    • 在日志领域比较成熟,被多家公司和多个开源项目使用

    缺点

    • Kafka单机超过64个队列/分区时,Load时会发生明显的飙高现象。队列越多,负载越高,发送消息响应时间变长;
    • 使用短轮询方式,实时性取决于轮询间隔时间;
    • 消费失败不支持重试
    • 支持消息顺序,但是一台代理宕机后,就会产生消息乱序
    • 社区更新较慢。

    2.RabbitMQ主要特性、优缺点

    RabbitMQ于2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。

    主要特性

    • 可靠性:提供了多种技术可以让你在性能和可靠性之间进行权衡。这些技术包括持久性机制、投递确认、发布者证实和高可用性机制;
    • 灵活的路由:消息在到达队列前是通过交换机进行路由的。RabbitMQ为典型的路由逻辑提供了多种内置交换机类型。如果你有更复杂的路由需求,可以将这些交换机组合起来使用,你甚至可以实现自己的交换机类型,并且当做RabbitMQ的插件来使用;
    • 消息集群:在相同局域网中的多个RabbitMQ服务器可以聚合在一起,作为一个独立的逻辑代理来使用;
    • 队列高可用:队列可以在集群中的机器上进行镜像,以确保在硬件问题下还保证消息安全;
    • 支持多种协议:支持多种消息队列协议;
    • 支持多种语言:用Erlang语言编写,支持只要是你能想到的所有编程语言;
    • 管理界面:RabbitMQ有一个易用的用户界面,使得用户可以监控和管理消息Broker的许多方面;
    • 跟踪机制:如果消息异常,RabbitMQ 提供消息跟踪机制,使用者可以找出发生了什么;
    • 插件机制:提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。

    优点

    • 由于Erlang语言的特性,消息队列性能较好,支持高并发;
    • 健壮、稳定、易用、跨平台、支持多种语言、文档齐全;
    • 有消息确认机制和持久化机制,可靠性高;
    • 高度可定制的路由;
    • 管理界面较丰富,在互联网公司也有较大规模的应用,社区活跃度高。

    缺点

    • 尽管结合 Erlang 语言本身的并发优势,性能较好,但是不利于做二次开发和维护
    • 实现了代理架构,意味着消息在发送到客户端之前可以在中央节点上排队。此特性使得RabbitMQ易于使用和部署,但是使得其运行速度较慢,因为中央节点增加了延迟,消息封装后也比较大;需要学习比较复杂的接口和协议,学习和维护成本较高。

    3.RocketMQ主要特性、优缺点

    RocketMQ出自阿里的开源产品,用Java语言实现,在设计时参考了Kafka,并做出了自己的一些改进,消息可靠性上比Kafka更好。RocketMQ在阿里内部被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。

    主要特性

    • 基于队列模型:具有高性能、高可靠、高实时、分布式等特点
    • Producer、Consumer、队列都支持分布式
    • Producer向一些队列轮流发送消息,队列集合称为TopicConsumer如果做广播消费,则一个Consumer实例消费这个Topic对应的所有队列如果做集群消费,则多个Consumer 实例平均消费这个Topic对应的队列集合
    • 能够保证严格的消息顺序
    • 提供丰富的消息拉取模式
    • 高效的订阅者水平扩展能力
    • 实时的消息订阅机制
    • 亿级消息堆积能力
    • 较少的外部依赖。

    优点

    • 单机支持1万以上持久化队列
    • RocketMQ的所有消息都是持久化的,先写入系统PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据,而访问时,直接从内存读取;
    • 模型简单,接口易用(JMS的接口很多场合并不太实用);
    • 性能非常好,可以允许大量堆积消息在Broker中
    • 支持多种消费模式,包括集群消费、广播消费等
    • 各个环节分布式扩展设计,支持主从和高可用
    • 开发度较活跃,版本更新很快

    缺点

    • 支持的客户端语言不多,目前是Java及C++,其中C++还不成熟;
    • RocketMQ社区关注度及成熟度也不及前两者
    • 没有Web管理界面,提供了一个 CLI (命令行界面) 管理工具带来查询、管理和诊断各种问题
    • 没有在MQ核心里实现JMS等接口

    4.ActiveMQ主要特性、优缺点

    ActiveMQ是由Apache出品,ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。

    主要特性

    • 服从JMS规范:JMS 规范提供了良好的标准和保证,包括:同步 或 异步 的消息分发,一次和仅一次的消息分发,消息接收和订阅等等。遵从JMS规范的好处在于,不论使用什么JMS实现提供者,这些基础特性都是可用的;
    • 连接灵活性:ActiveMQ提供了广泛的连接协议,支持的协议有:HTTP/S,IP多播,SSL,TCP,UDP等等。对众多协议的支持让ActiveMQ拥有了很好的灵活性;
    • 支持的协议种类多:OpenWire、STOMP、REST、XMPP、AMQP;
    • 持久化插件和安全插件:ActiveMQ提供了多种持久化选择。而且,ActiveMQ的安全性也可以完全依据用户需求进行自定义鉴权和授权;
    • 支持的客户端语言种类多:除了Java之外,还有:C/C++,.NET,Perl,PHP,Python,Ruby;
    • 代理集群:多个ActiveMQ代理可以组成一个集群来提供服务;
    • 异常简单的管理:ActiveMQ是以开发者思维被设计的。所以,它并不需要专门的管理员,因为它提供了简单又使用的管理特性。有很多中方法可以监控ActiveMQ不同层面的数据,包括使用在JConsole或者在ActiveMQ的WebConsole中使用JMX。通过处理JMX的告警消息,通过使用命令行脚本,甚至可以通过监控各种类型的日志。

    优点

    • 跨平台(JAVA编写与平台无关,ActiveMQ几乎可以运行在任何的JVM上)
    • 可以用JDBC可以将数据持久化到数据库。虽然使用JDBC会降低ActiveMQ的性能,但是数据库一直都是开发人员最熟悉的存储介质;
    • 支持JMS规范:支持JMS规范提供的统一接口;
    • 支持自动重连和错误重试机制
    • 有安全机制:支持基于shiro,jaas等多种安全配置机制,可以对Queue/Topic进行认证和授权;
    • 监控完善:拥有完善的监控,包括WebConsole,JMX,Shell命令行,Jolokia的RESTful API;
    • 界面友善:提供的WebConsole可以满足大部分情况,还有很多第三方的组件可以使用,比如hawtio

    缺点

    • 社区活跃度不及RabbitMQ高
    • 根据其他用户反馈,会出莫名其妙的问题,会丢失消息
    • 目前重心放到activemq6.0产品Apollo,对5.x的维护较少;
    • 不适合用于上千个队列的应用场景

    二、消息中间件相关知识

    1.消息中间件的组成

    • Broker:消息服务器,作为server提供消息核心服务
    • Producer:消息生产者,业务的发起方,负责生产消息传输给broker
    • Consumer:消息消费者,业务的处理方,负责从broker获取消息并进行业务逻辑处理
    • Topic:主题,发布订阅模式下的消息统一汇集地,不同生产者向topic发送消息,由MQ服务器分发到不同的订阅者,实现消息的广播
    • Queue:队列,PTP模式下,特定生产者向特定queue发送消息,消费者订阅特定的queue完成指定消息的接收
    • Message:消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输

    2.消息中间件模式分类

    • 点对点

    PTP点对点:使用queue作为通信载体 

    消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。 
    消息被消费以后,queue中不再存储,所以消息消费者不可能消费到已经被消费的消息。 Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费

    • 发布/订阅

    Pub/Sub发布订阅(广播):使用topic作为通信载体 

    消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。

    queue实现了负载均衡,将producer生产的消息发送到消息队列中,由多个消费者消费。但一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有一个可用的消费者。 
    topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到一个消息的拷贝

    3.消息中间件的普遍优势

    • 系统解耦

    交互系统之间没有直接的调用关系,只是通过消息传输,故系统侵入性不强,耦合度低

    • 提高系统响应时间

    例如原来的一套逻辑,完成支付可能涉及先修改订单状态、计算会员积分、通知物流配送几个逻辑才能完成通过MQ架构设计,就可将紧急重要(需要立刻响应)的业务放到该调用方法中,响应要求不高的使用消息队列,放到MQ队列中,供消费者处理

    • 为大数据处理架构提供服务

    通过消息作为整合,大数据的背景下,消息队列还与实时处理架构整合,为数据处理提供性能支持

    • Java消息服务——JMS

    Java消息服务(Java Message Service,JMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。 
    JMS中的P2P和Pub/Sub消息模式:点对点(point to point, queue)与发布订阅(publish/subscribe,topic)最初是由JMS定义的。这两种模式主要区别或解决的问题就是发送到队列的消息能否重复消费(多订阅)。

    4.消息中间件常用协议

    • AMQP协议

    AMQP即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。 
    优点:可靠、通用

    • MQTT协议

    MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。 
    优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统

    • STOMP协议

    STOMP(Streaming Text Orientated Message Protocol)是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。 
    优点:命令模式(非topic\queue模式)

    • XMPP协议

    XMPP(可扩展消息处理现场协议,Extensible Messaging and Presence Protocol)是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。 
    优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大

    三、消息队列应用场景

    在实际应用中常用的使用场景:异步处理、应用解耦、流量削锋和消息通讯四个场景

    场景一:异步处理

    场景说明:用户注册后,需要发注册邮件和注册短信传统的做法有两种 1.串行的方式;2.并行方式

    • 串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。
    • 并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。

    假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

    因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150),并行方式处理的请求量是10次(1000/100)

    小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

    引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:

    按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。

    场景二:应用解耦

    场景说明:用户下单后,订单系统需要通知库存系统传统的做法是,订单系统调用库存系统的接口。假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,缺点在于订单系统与库存系统耦合

    如何解决以上问题呢?引入应用消息队列后的方案,如下图:

    • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
    • 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作

    假如:在下单时库存系统不能正常使用,也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦

    场景三:流量削锋(一般在秒杀或团抢活动中使用广泛)

    应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列:可以控制活动的人数、可以缓解短时间内高流量压垮应用。用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面秒杀业务根据消息队列中的请求信息,再做后续处理。

    场景四:日志处理

    日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下,

    • 日志采集客户端:负责日志数据采集,定时写受写入Kafka队列
    • Kafka消息队列:负责日志数据的接收,存储和转发
    • 日志处理应用:订阅并消费kafka队列中的日志数据

    查看新浪kafka日志处理应用案例:转自(http://cloud.51cto.com/art/201507/484338.htm)

    • (1)Kafka:接收用户日志的消息队列
    • (2)Logstash:做日志解析,统一成JSON输出给Elasticsearch
    • (3)Elasticsearch:实时日志分析服务的核心技术,一个schemaless,实时的数据存储服务,通过index组织数据,兼具强大的搜索和统计功能
    • (4)Kibana:基于Elasticsearch的数据可视化组件,超强的数据可视化能力是众多公司选择ELK stack的重要原因

    场景五:消息通讯

    消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等

    • 点对点通讯:客户端A和客户端B使用同一队列,进行消息通讯。

    • 聊天室通讯:客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。 

    以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。

    2.MQ系统的数据如何保证不丢失?

    一、MQ原则

    数据不能多,也不能少,不能多是说消息不能重复消费。不能少,就是说不能丢失数据。如果mq传递的是非常核心的消息,支撑核心的业务,那么这种场景是一定不能丢失数据的。

    二、丢失数据场景分析

    丢数据一般分为两种,一种是mq把消息丢了,一种就是消费时将消息丢了。

    下面从rabbitmq和kafka分别说一下,丢失数据的场景:

    (一)rabbitmq丢失数据场景分析

    1.生产者弄丢了数据

    生产者将数据发送到rabbitmq的时候,可能在传输过程中因为网络等问题而将数据弄丢了

    2.rabbitmq自己丢了数据

    如果没有开启rabbitmq的持久化,那么rabbitmq一旦重启,那么数据就丢了。所以必须开启持久化将消息持久化到磁盘,这样就算rabbitmq挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢失。除非极其罕见的情况,rabbitmq还没来得及持久化自己就挂了,这样可能导致一部分数据丢失。

    3.消费端弄丢了数据

    主要是因为消费者消费时,刚消费到,还没有处理,结果消费者就挂了,这样你重启之后,rabbitmq就认为你已经消费过了,然后就丢了数据。

    (二)kafka丢失数据场景分析

    1.生产者弄丢了数据

    生产者没有设置相应的策略,发送过程中丢失数据

    2.kafka弄丢了数据

    比较常见的一个场景,就是kafka的某个broker宕机了,然后重新选举partition的leader时。如果此时follower还没来得及同步数据,leader就挂了,然后某个follower成为了leader,他就少了一部分数据。

    3.消费者弄丢了数据

    消费者消费到了这个数据,然后消费之自动提交了offset,让kafka知道你已经消费了这个消息,当你准备处理这个消息时,自己挂掉了,那么这条消息就丢了

    三、如何防止消息丢失

    (一)rabbitmq防止消息丢失

    1.生产者丢失消息

    • 可以选择使用rabbitmq提供是事物功能

    就是生产者在发送数据之前开启事物,然后发送消息,如果消息没有成功被rabbitmq接收到,那么生产者会受到异常报错,这时就可以回滚事物,然后尝试重新发送;如果收到了消息,那么就可以提交事物。

      channel.txSelect();//开启事物
      try{
          //发送消息
      }catch(Exection e){
          channel.txRollback();//回滚事物
          //重新提交
      }

    缺点:rabbitmq事物已开启,就会变为同步阻塞操作,生产者会阻塞等待是否发送成功,太耗性能会造成吞吐量的下降

    • 可以开启confirm模式。

    在生产者哪里设置开启了confirm模式之后,每次写的消息都会分配一个唯一的id,然后如何写入了rabbitmq之中,rabbitmq会给你回传一个ack消息,告诉你这个消息发送OK了;如果rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息失败了,你可以进行重试。而且你可以结合这个机制知道自己在内存里维护每个消息的id,如果超过一定时间还没接收到这个消息的回调,那么你可以进行重发。

        //开启confirm
        channel.confirm();
        //发送成功回调
        public void ack(String messageId){
          
        }
    
        // 发送失败回调
        public void nack(String messageId){
            //重发该消息
        }
    • 二者不同

    事务机制是同步的,你提交了一个事物之后会阻塞住,但是confirm机制是异步的,发送消息之后可以接着发送下一个消息,然后rabbitmq会回调告知成功与否。
    一般在生产者这块避免丢失,都是用confirm机制。

    2.rabbitmq自己弄丢了数据

    设置消息持久化到磁盘。设置持久化有两个步骤:

    • ①创建queue的时候将其设置为持久化的,这样就可以保证rabbitmq持久化queue的元数据,但是不会持久化queue里面的数据。
    • ②发送消息的时候讲消息的deliveryMode设置为2,这样消息就会被设为持久化方式,此时rabbitmq就会将消息持久化到磁盘上。
    • 必须要同时开启这两个才可以。

    而且持久化可以跟生产的confirm机制配合起来,只有消息持久化到了磁盘之后,才会通知生产者ack,这样就算是在持久化之前rabbitmq挂了,数据丢了,生产者收不到ack回调也会进行消息重发。

    3.消费者弄丢了数据

    使用rabbitmq提供的ack机制,首先关闭rabbitmq的自动ack,然后每次在确保处理完这个消息之后,在代码里手动调用ack。这样就可以避免消息还没有处理完就ack。

    (二)kafka防止消息丢失

    1.消费端弄丢了数据

    关闭自动提交offset,在自己处理完毕之后手动提交offset,这样就不会丢失数据

    2.kafka弄丢了数据

    一般要求设置4个参数来保证消息不丢失

    • 给topic设置 replication.factor参数:这个值必须大于1,表示要求每个partition必须至少有2个副本。
    • 在kafka服务端设置min.isync.replicas参数:这个值必须大于1,表示 要求一个leader至少感知到有至少一个follower在跟自己保持联系正常同步数据,这样才能保证leader挂了之后还有一个follower。
    • 在生产者端设置acks=all:表示 要求每条每条数据,必须是写入所有replica副本之后,才能认为是写入成功了
    • 在生产者端设置retries=MAX(很大的一个值,表示无限重试):表示 这个是要求一旦写入事变,就无限重试

    3.生产者弄丢了数据

    如果按照上面设置了ack=all,则一定不会丢失数据,要求是,你的leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了如果没满足这个条件,生产者会自动不断的重试,重试无限次

    补充问题:

    关于MQ的几件小事(五)如何保证消息按顺序执行

    https://www.jianshu.com/p/02fdcb9e8784

    关于MQ的几件小事(三)如何保证消息不重复消费

    https://www.jianshu.com/p/172295e2e978

    关于MQ的几件小事(二)如何保证消息队列的高可用

    https://www.jianshu.com/p/ab64681beb17

    Kafka的文件存储机制

    Kafka中消息是以topic进行分类的,生产者通过topic向Kafka broker发送消息,消费者通过topic读取数据。

    然而topic在物理层面又能以partition为分组,一个topic可以分成若干个partition。partition还可以细分为segment,一个partition物理上由多个segment组成,segment文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为segment索引文件和数据文件。这两个文件的命令规则为:partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值

    Kafka 如何保证可靠性

    如果我们要往 Kafka 对应的主题发送消息,我们需要通过 Producer 完成。前面我们讲过 Kafka 主题对应了多个分区,每个分区下面又对应了多个副本;为了让用户设置数据可靠性, Kafka 在 Producer 里面提供了消息确认机制。也就是说我们可以通过配置来决定消息发送到对应分区的几个副本才算消息发送成功。可以在定义 Producer 时通过 acks 参数指定。这个参数支持以下三种值:

    • acks = 0:意味着如果生产者能够通过网络把消息发送出去,那么就认为消息已成功写入 Kafka 。在这种情况下还是有可能发生错误,比如发送的对象无能被序列化或者网卡发生故障,但如果是分区离线或整个集群长时间不可用,那就不会收到任何错误。在 acks=0 模式下的运行速度是非常快的(这就是为什么很多基准测试都是基于这个模式),你可以得到惊人的吞吐量和带宽利用率,不过如果选择了这种模式, 一定会丢失一些消息。
    • acks = 1:意味若 Leader 在收到消息并把它写入到分区数据文件(不一定同步到磁盘上)时会返回确认或错误响应。在这个模式下,如果发生正常的 Leader 选举,生产者会在选举时收到一个 LeaderNotAvailableException 异常,如果生产者能恰当地处理这个错误,它会重试发送悄息,最终消息会安全到达新的 Leader 那里。不过在这个模式下仍然有可能丢失数据,比如消息已经成功写入 Leader,但在消息被复制到 follower 副本之前 Leader发生崩溃。
    • acks = all(这个和 request.required.acks = -1 含义一样):意味着 Leader 在返回确认或错误响应之前,会等待所有同步副本都收到悄息。如果和min.insync.replicas 参数结合起来,就可以决定在返回确认前至少有多少个副本能够收到悄息,生产者会一直重试直到消息被成功提交。不过这也是最慢的做法,因为生产者在继续发送其他消息之前需要等待所有副本都收到当前的消息。

    Kafka消息是采用Pull模式,还是Push模式

    Kafka最初考虑的问题是,customer应该从brokes拉取消息还是brokers将消息推送到consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉取消息。

    push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。

    最终Kafka还是选取了传统的pull模式。Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达。

    Kafka是如何实现高吞吐率的

    1. 顺序读写:kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能
    2. 零拷贝跳过“用户缓冲区”的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到“用户态缓冲区”
    3. 文件分段:kafka的队列topic被分为了多个区partition,每个partition又分为多个段segment,所以一个队列中的消息实际上是保存在N多个片段文件中
    4. 批量发送:Kafka允许进行批量发送消息,先将消息缓存在内存中,然后一次请求批量发送出去
    5. 数据压缩:Kafka还支持对消息集合进行压缩,Producer可以通过GZIP或Snappy格式对消息集合进行压缩

    Kafka判断一个节点还活着的两个条件

    1. 节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接
    2. 如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久

     

    参考书籍、文献和资料

    1.https://www.cnblogs.com/Vito-Yan/p/10319826.html

    2.https://blog.csdn.net/wqc19920906/article/details/82193316

    3.https://www.jianshu.com/p/70c5b8d51c75

    4.http://cloud.51cto.com/art/201507/484338.htm

    5.https://www.jianshu.com/p/8ed16edc73e4

     

     

    展开全文

空空如也

1 2 3 4 5 ... 20
收藏数 72,639
精华内容 29,055
关键字:

中间件