Blogs

什么是消息队列(Kafka)

模拟场景

假如现在我需要维护两个服务A和B,B服务每秒能处理100个消息,但A服务每秒能发两百个消息

结果我们也能想到,B服务非常容器就爆炸了,聪明的我一定能想到,我在B里面加一个队列来存放A发来的消息,用offset偏移量来记录消息的位置,B服务看能力来处理消息,不断更新offset值

但这又产生了一个新问题,来不及处理的消息放在内存里,如果在服务B里面的消息没有处理完的情况下B服务关机或者重启了,里面的消息就全部丢失了,但聪明的我肯定还会想到,那我们把队列拉出来单独开一个进程不就行了嘛

这就是消息队列的来源,像A服务一样发数据到队列里就是生产者,B服务这样处理数据的就是消费者

优化

  • 高性能 简单来说就是软件不行加硬件,消费者太慢了就加消费者,生产者慢就加生产者,但这听着感觉还是不对,如果多个生产者和消费者同时争抢同一个消息队列,抢不到就等怎么办

    解决方法: 对消息进行分类,每一类是一个topic,按照topic数量,生产者将消息分发给不同的消息队列里面,一个消费者需要订阅不同的队列来获取消息,但这样还是会出现一个topic里消息还是很多,我们还可以把单个topic拆成多个partition分区,每个消费者负责一个partition分区

  • 高扩展

    随着partition变多,如果partition都在同一台机器上可能会导致单机CPU内存负载过高,影响系统整体效率

    解决方法: 将partition分散部署到多台机器上,每一台机器就是一个broker,我们可以通过增加broker来缓解机器CPU负载过高带来的性能问题

  • 高可用

    如果其中一个broker挂了,那里面所有partition的消息也会跟着消失

    解决方法: 给partition多加几个副本,统称为replicas,将他们分为leader和follower,leader负责应对生产者和消费者的读写请求,follower负责同步leader的消息,将leader和follower分散到不同的broker里,这样就算leader所在的broker挂了也不会影响到follower所在的broker,并且还能重新选举一个leader partition顶上

  • 持久化和过期策略

    假设全部broker都挂了,那所以的partition的消息不都丢失了吗

    解决方法: 所以我们不能光将数据放在内存里,还需要持久化到磁盘上,但问题又来了,磁盘是有限的,一直往里面写数据迟早得炸,所以还需要给数据加上保留策略retention policy,比如磁盘数据超过一定大小或者数据放置超过一定时间就会被清理掉

redis主从复制的理解

主从复制本质上就是从一台服务器master上的数据拷贝到另一台服务器slave上,数据的复制是单向的,只能由主节点到从节点,redis里提供了全量复制和增量复制两种方法:

  • 全量复制:一般用于slave新构建的时候,slave会向master发送全量复制请求,然后master会拷贝当前数据快照给slave,slave丢弃旧的数据来加载新的数据,但需要注意redis并没有采用强一致性,所以会出现数据同步延迟导致数据不一致问题

  • 增量复制:当master节点收到数据改动,master会把变更的数据同步给所有的slave节点,主要原理是master和slave会共同维护一个偏移量offset,用来表示master向slave传递的字节数量,每一次进行增量数据的传递,offset都会对应增加数量

主从连接后master 接收命令,判断runid是否匹配,判定offset是否在复制缓冲区中,runid和offset有一个不满足,执行全量复制

心跳机制:进入命令传播阶段候,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线

  • master会去ping slave,默认每十秒一次,来获取slave最后一次连接时间间隔,一般在0或1为正常
  • slave会用REPLCONF ACK {offset},每一秒一次,汇报slave自己的复制偏移量,获取最新的数据变更指令以及判断master是否在线

ArrayList 和 LinkedList 的区别

  • ArrayList基于动态数组实现的非线程安全的集合;LinkedList基于链表实现的非线程安全的集合。

  • 对于随机index访问的get和set方法,一般ArrayList的速度要优于LinkedList。因为ArrayList直接通过数组下标直接找到元素;LinkedList要移动指针遍历每个元素直到找到为止。

  • 新增和删除元素,一般LinkedList的速度要优于ArrayList。因为ArrayList在新增和删除元素时,可能扩容和复制数组;LinkedList实例化对象需要时间外,只需要修改指针即可。

  • LinkedList集合不支持 高效的随机随机访问(RandomAccess)

  • ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间

Kafka如何避免重复消费问题

首先,kafka的块上会储存offset标记,kafka消费者通过offset标记来维护已经消费的数据,消费者每消费完一批数据时会更新offset值,来避免重复消费问题。

默认情况,消费完以后会自动提交offset值避免重复消费,Kafka消费端自动提交的逻辑中默认了5秒的间隔,所以在consumer的消费过程中,如果5秒内被强行kill了或者宕机导致offset没有提交,会导致重复消费问题。

在Kafka里有一种叫Partition Balance机制,就是把多个消费区都负载均衡给consumer消费者,如果消费者在默认的五分钟内没有处理完里面的消费,就会触发ReBalance机制导致offset提交失败,在重启ReBalance后,消费端还是会从之前没有提交offset的位置开始去消费,从而导致重复消费问题,如何去解决也有很多方法:

  1. 提高消费端的处理性能,避免触发Balance,例如:
    1. 比如采用异步的方法来处理消息,缩短单个信息消费的时长
    2. 调整消费处理的超时时间
    3. 减少一次性从区中获取数据的条数
  2. 针对信息生产md5然后保存在MySQL或者redis里,在处理消息前先去MySQL或者redis里判断是否消费过

什么是SpringMVC

SpringMVC 是属于Spring Framework生态里面的一个模块,是在servlet的基础上构建并且使用了MVC模式涉及的web框架,目的是为了去简化传统的servlet+JSP模式下的web开发方式。 其次Spring MVC的架构设计是对Javaweb里面的mvc框架模式做了一些增强和扩展,主要体现在几个方面 :

  1. 把传统MVC框架里面的Controller控制器做了拆分,分为了前端控制器DispatcherServlet和后端控制器Controller
  2. 把model模型拆分成业务层service和数据访问层Repository
  3. 在视图层,可以支持不同的视图,比如Freemark、velocity、JSP等

所以,SpringMVC就是为了MVC模式设计的,因此在开发MVC应用时会更加方便灵活

SpringMVC整体工作流程:

  1. 浏览器请求首先经过核心控制器DispatherServlet,把请求分发到对应的Controller里面
  2. 然后等Controller调用业务逻辑进行处理完后返回Model And View
  3. DispatcherServlet去寻找一个或多个ViewResolver视图解析器,找到Model And View指定的视图并且把数据展示到客户端