纸上得来终觉浅,绝知此事要躬行。–陆游

本文是新系列“实战高并发”的开篇作。这个系列作为“我说分布式”的子系列,将着重挑选若干典型的分布式实战场景,尽量对当下高并发领域较为热门的架构及业务场景做一次较为深入的实践与总结。

该系列既是对笔者接触过的业务的整理,也希望系列中分享的套路能够对读者朋友解决实际业务中面临的问题有所帮助。

言归正传,本文我将主要从业务场景及技术架构等方面出发,对”电商高并发秒杀”这一业务场景做一次较为全面的阐述,同时作为后续实操的开发设计依据。

何为“秒杀”及其特点

“秒杀”这一业务场景在如今已经不是什么新鲜名词,它本质上属于短时突发性高并发访问问题,业务特点如下:

  1. 定时触发,流量在瞬间突增
  2. 秒杀请求中常常只有部分能够成功
  3. 秒杀商品数量往往有限,不能超卖,但能接受少卖
  4. 不要求立即返回真实下单结果

Read More

最近在忙RocketMQ社区的线下开发者训练营的事情,需要准备一个实战课题的demo。

考虑到现场的环境复杂,比如:网络信号不一定很好,每个学员的机器环境不完全一致。因此我决定尽量减少外部组件的依赖,能用内置的就用内置:

缓存不使用Redis,而是用ConcurrentHashMap;数据库不用MySQL了,改用H2嵌入式数据库。

本文就重点整理一下H2嵌入式数据库的整合过程。

Read More

本文是“研磨Kafka”系列较为重要的一篇,我将在本文中重点讲解Kafka是通过何种手段达到消息不丢失的目的。

Kafka通过ISR机制保证了消息发送的可靠性。

首先介绍下什么是ISR。

ISR

ISR,即:in-sync replica,中文含义为:与leader 副本保持同步的副本集合。

我们在之前的文章中已经介绍过Kafka的副本机制,如:单个分区可配置多个副本。

在Kafka中,分区下维护了一个replica集合,集合中所有副本保存的日志都和leader 副本保持着同步状态。

只有该集合中的replica才能被选举为leader,当该集合中所有的副本都收到了同一条消息,Kafka才会将该消息状态设置为“已提交”状态,对客户端而言就是消息发送成功了。

Kafka保证,在分区下的replica集合中,只要存在至少一个replica,则状态为 “已提交” 的消息就不会丢失。

这里我们要记住两个要点:

  1. ISR 中存在至少一个可用的replica
  2. 消息状态为– 已提交

Read More

本文中主要涉及到Kafka的消息主题(Topic)、分区(Partition)、消息位移(Offset)。

主题(Topic)与分区(Partition)

首先说说Topic。

直观的说,Topic是一个逻辑上的概念,代表了一类消息。在业务上,一个Topic代表了一类型的具体的业务场景,如:下单场景使用OrderChargeTopic,支付场景则使用PayOrderTopic。

一个Topic通常会被多个消费者所订阅,从性能方面考量,Kafka没有采用topic->message的两级结构,而是采用了topic->Partition->message三级结构进行负载的分散。也就是说,对于一个topic而言,它包含了多个Partition,对于Partition,我们在 《研磨消息中间件kafka之消息持久化及副本》 这篇文章中也有所涉及。

如图,是Kafka官网对topic与Partition关系的直观表述

topic与Partition

Read More

之前的文章中我们了解了Kafka的消息持久化及副本相关的策略,本文我们学习一下Kafka在高可用方面实现的技术方案。

Kafka在高可用方面主要包含如下的机制:

  1. 负载均衡与故障转移
  2. 伸缩性

我们对这两方面做详细的讲解。

负载均衡与故障转移

首先谈谈Kafka负载均衡的实现机理。

Kafka集群在默认情况相爱,每台服务器都有均等的机会为客户端提供服务,能够将负载分散在集群中的所有机器上,避免了某个节点资源被耗尽的情况出现。

Kafka实现负载均衡本质上是通过分区leader选举实现的,Kafka实现了一套智能的leader选举算法,可以保证集群内所有的机器都能以均等的机会分散各个分区(Partition)的leader,在整体上实现了负载均衡。

接着讨论一下负载均衡的实现。

故障转移,顾名思义,就是在服务器出现故障的时候,集群能够高效的检测到该failure,并立刻将该服务器上的服务自动转移到其他正常的服务节点上。

一般情况下,故障转移是通过“心跳”或者“会话”机制来实现的,也就是当主服务器与备份服务器之间的心跳无法维持或者主服务器注册到服务中心的会话超时过期了,我们就认为主服务器此时已经down掉,集群需要自动的启动某个备份服务器替代down机的主服务器提供服务。

对Kafka而言,故障转移采用的机制是会话机制。

每一个Kafka服务器启动后都会用会话将自己注册到zookeeper上,当它出现故障,与zk之间的会话维持不了导致超时,则Kafka集群会再选取另外一台服务器代替该服务器继续提供服务。

伸缩性

如果服务需要具备伸缩性,那很大程度上表明该服务本身是无状态的,而Kafka服务器正是利用了无状态的思想,将状态的保存与管理交给分布式协调服务zookeeper来做,服务器本身便不需要做复杂的状态保持,降低了维护的复杂度。

因为服务本身无状态,因此可以通过增减节点达到集群的扩缩容。

当然Kafka本身并不是什么状态都不保持,它也保留了一部分必要的内部状态。

本文主要介绍Kafka的消息持久化策略及副本机制。

Kafka消息持久化策略

首先简要说明一下Kafka持久化消息的优点:

  1. Kafka通过消息持久化解耦了消息的生产者和消费者,这也是采用队列的优势,使得生产者和消费者均只需要关心自己的逻辑,而不需要直接感知到彼此的存在。
  2. Kafka支持对消费过的消息进行“消息重演(Message Replay)”,而重演的基础就是实现消息持久化。

Kafka为了保证消息落盘的实时,确保消息不丢失,没有采用直接写内存,等写满后一次性刷盘的策略,而是将数据立即写入文件系统的日志中,写入成功后才将结果返回给客户端告知客户端–消息已经成功写入。这么做的目的是,一方面实时地保存了数据,另一方面又减少了对内存的消耗,将内存空间尽量留给页缓存使用,从而提升了整体的性能。

Read More

提升Kafka对消息处理的吞吐量及低延时主要通过磁盘顺序写、零拷贝(zero copy)以及利用页缓存(Page Cache)实现。下面我们进行具体说明。

消息顺序写

Kafka的消息存储在每次写入时,只是将数据写入到操作系统的页缓存(PageCache)中,最终是由操作系统决定何时将页缓存中的数据落盘的。这样做的好处如下:

  1. 由于页缓存是OS在内存中分配的,因此消息写入速度很快;
  2. 由于Kafka将消息写入页缓存中,因此避免了直接与底层文件系统打交道时候的繁琐流程,所有的I/O操作均交给了操作系统进行处理;
  3. Kafka写操作采用了append方式(即:追加写入),这种顺序写盘的方式速度很快,避免了因随机写而导致的写入效率低下。

Read More

本文是新系列“研磨”系列的开篇,该系列主要立足源码分析、核心技术点分析、常见问题整理等方面。一篇文章围绕一个中间件,立足于讲清、讲透、讲明白。

系列的第一部分主题为–“研磨Kafka”,本篇中,我们先从宏观的视角窥探Kafka的核心角色,并对后文中要讲到的主题做一个宏观的概述。

Kafka的设计原理及结构概述

首先对Kafka重要的角色进行总结。

要点 解释
Kafka概述 Kafka是一款分布式消息中间件,但它不符合JMS规范。
消费特点 Kafka消费消息成功,不会马上删除消息。消息存储一段时间后,会被批量删除。
Broker Broker是Kafka的服务端,主要作用为接收消息并对消息进行持久化存储。
Broker可以有多个,每一个Broker节点可存储多个Topic。但是Broker本身并不会存储消费的Offset(即消费者消费消息的位置),Offset数据由Consumer(消费者)保存,保存位置为Zookeeper。
Topic Topic为消息主题,它是生产者与消费者之间进行消息传递的依据。单个Topic下包括多个Partition,它的所有元数据存储在zookeeper中。
Partition Partition即分区,Kafka为提高性能及扩展性,将一个Topic分为多个分区,及Partition,每个Partition均可独立存放在一个Broker上,这保证了Kafka的可用性及稳定性。
Producer Producer为消息生产者,它一般是集成了Kafka客户端的业务应用。
Producer负责发送消息到Broker。Producer直连Broker,具有往Topic下发布消息的能力。
它会与Topic下的所有PartitionLeader保持Socket长连接。Producer具有同步、异步两种消息发送能力,且Producer支持消息的批量发送,即将多条消息缓存在客户端,在达到指定的时间延迟或者消息数量后,批量地提交给Broker。
Consumer Consumer为消息消费者,它一般是集成了Kafka客户端的业务应用。消费者向Broker订阅Topic,并从Topic中接收消息。
每一个消费者均属于某一个消费者组,且同消费者组中的消费者订阅同一个Topic,同一个Topic下的不同消费者分别订阅该Topic下不同Partition的数据。一个消费者可以订阅多个Partition,但同一个Partition只能被一个消费者订阅,该策略的目的是一定程度上避免消息被重复消费。
可以通过增加Partition的方式对消费能力进行横向扩展。
当出现某个消费者down机,消费过程会进行 Rebalance

研磨Kafka主题

该系列后续会涉及到的要点,在此处进行整理

要点
如何提升Kafka的吞吐量及低延时
Kafka的消息持久化方式
Kafka如何实现负载均衡及故障转移
如何理解Kafka的“伸缩性”特点
概览主题(Topic)与分区(Partition)
解释什么是消息位移offset
解释下Kafka的副本机制如何实现
Kafka如何利用“ISR机制”保证消息不丢失?
kafka是否存在消息丢失的情况?

Kafka使用场景

Kafka主要应用的场景如下

场景 说明
消息传递 Kafka的高吞吐、高可靠特性使得它很适合作为消息总线或者消息代理的替代品,常用来实现超大规模消息引擎集群。
用户行为分析 Kafka在大数据领域常常与大数据组件相结合,协助进行用户行为数据的搜集。
运维数据收集 在运维领域,Kafka常被用作异步数据上报,监控数据收集等场景。
日志收集 这是Kafka使用的经典场景,通过Kafka对分散在不同机器上的机器进行收集,并集中存储在分布式存储中,Kafka的高吞吐、低延时的特性让它能够成为主流的日志收集解决方案。
流式处理 Kafka社区推出的Kafka Streams标志着它正式进入流式处理的大家庭。

首先了解下何为SPI,这里引用Dubbo官方对SPI的解说,比较全面。SPI简介

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。

Dubbo本身实现了对JDK的SPI的扩展,为了能够更好的理解Dubbo的SPI机制,我们需要理解JDK的原生SPI原理。

从概念可以看出,SPI机制是一种服务发现机制,能够在运行时动态的增加、获取服务实现。个人感觉很像设计原则中的里氏替换原则。

我将通过一个实例来讲解如何对JDK的SPI进行运用。

Read More

Fork me on GitHub