Kafka -- 位移主题
ZooKeeper 老版本Consumer的位移管理依托于Apache ZooKeeper,自动或手动地将位移数据提交到ZK中保存 当Consumer重启后,能自动从ZK中读取位移数据,从而在上次消费截止的地方继续消费 这种设计使得Kafka Broker不需要保存位移数据,减少了Broker端需要持有的状态空间,有利于实现高伸缩性 但ZK并不适用于高频的写操作 位移主题 将Consumer的位移数据作为普通的Kafka消息,提交到__consumer_offsets(保存Consumer的位移信息) 提交过程需要实现高持久性,并需要支持高频的写操作 位移主题是普通的Kafka主题,同时也是一个内部主题,交由Kafka管理即可 位移主题的消息格式由Kafka定义,用户不能修改 因此不能随意向位移主题写消息,一旦写入的消息不能满足格式,那Kafka内部无法成功解析,会造成Broker崩溃 Kafka Consumer有API来提交位移(即向位移主题写消息) 消息格式 常用格式:Key-Value Key为消息键值,Value为消息体,在Kafka中都是字节数组 Key <Group ...
Java性能 -- 线程上下文切换
线程数量 在并发程序中,并不是启动更多的线程就能让程序最大限度地并发执行 线程数量设置太小,会导致程序不能充分地利用系统资源 线程数量设置太大,可能带来资源的过度竞争,导致上下文切换,带来的额外的系统开销 上下文切换 在单处理器时期,操作系统就能处理多线程并发任务,处理器给每个线程分配CPU时间片,线程在CPU时间片内执行任务 CPU时间片是CPU分配给每个线程执行的时间段,一般为几十毫秒 时间片决定了一个线程可以连续占用处理器运行的时长 当一个线程的时间片用完,或者因自身原因被迫暂停运行,此时另一个线程会被操作系统选中来占用处理器 上下文切换(Context Switch):一个线程被暂停剥夺使用权,另一个线程被选中开始或者继续运行的过程 切出:一个线程被剥夺处理器的使用权而被暂停运行 切入:一个线程被选中占用处理器开始运行或者继续运行 切出切入的过程中,操作系统需要保存和恢复相应的进度信息,这个进度信息就是_上下文_ 上下文的内容 寄存器的存储内容:CPU寄存器负责存储已经、正在和将要执行的任务 程序计数器存储的指令内容:程序计数器负责存储CPU正在执行的指令位置、即将执行的下一条...
Kafka -- 消费者组
消费者组 消费者组(Consumer Group)是Kafka提供的可扩展且具有容错性的消费者机制 一个消费者组内可以有多个消费者或消费者实例(进程/线程),它们共享一个Group ID(字符串) 组内的所有消费者协调在一起来消费订阅主题的所有分区 每个分区只能由同一个消费者组内的一个Consumer实例来消费,Consumer实例对分区有所有权 消息引擎模型 两种模型:点对点模型(消息队列)、发布订阅模型 点对点模型(传统的消息队列模型) 缺陷/特性:消息一旦被消费、就会从队列中被删除,而且只能被下游的一个Consumer消费 伸缩性很差,下游的多个Consumer需要抢占共享消息队列中的消息 发布订阅模型 缺陷:伸缩性不高,每个订阅者都必须订阅主题的所有分区(全量订阅) Consumer Group 当Consumer Group订阅了多个主题之后 组内的每个Consumer实例不要求一定要订阅主题的所有分区,只会消费部分分区的消息 Consumer Group之间彼此独立,互不影响,它们能够订阅相同主题而互不干涉 Kafka使用Consumer Group...
Java性能 -- CAS乐观锁
synchronized / Lock / CAS synchronized和Lock实现的同步锁机制,都属于悲观锁,而CAS属于_乐观锁_ 悲观锁在高并发的场景下,激烈的锁竞争会造成线程阻塞,而大量阻塞线程会导致系统的上下文切换,增加系统的性能开销 乐观锁 乐观锁:在操作共享资源时,总是抱着乐观的态度进行,认为自己能够完成操作 但实际上,当多个线程同时操作一个共享资源时,只有一个线程会成功,失败的线程不会被挂起,仅仅只是返回 乐观锁相比于悲观锁来说,不会带来死锁、饥饿等活性故障问题,线程间的相互影响也远远比悲观锁要小 乐观锁没有因竞争而造成的系统上下文切换,所以在性能上更胜一筹 实现原理 CAS是实现乐观锁的核心算法,包含3个参数:V(需要更新的变量),E(预期值)、N(最新值) 只有V等于E时,V才会被设置为N 如果V不等于E了,说明其它线程已经更新了V,此时该线程不做操作,返回V的真实值 CAS实现原子操作AtomicInteger是基于CAS实现的一个线程安全的整型类,Unsafe调用CPU底层指令实现原子操作 12345678// java.util.conc...
Kafka -- 幂等性生产者 + 事务生产者
消息交付可靠性保障 消息交付可靠性保障:Kafka对Producer和Consumer要处理的消息所提供的承诺 常见的承诺 最多一次(at most once):消息可能会丢失,但绝不会被重复发送 至少一次(at least once):消息不会丢失,但有可能被重复发送 精确一次(exactly once):消息不会丢失,也不会被重复发送 Kafka默认提供的交付可靠性保障:_至少一次_ 只有Broker成功提交消息且Producer接到Broker的应答才会认为该消息成功发送 如果Broker成功提交消息,但Broker的应答没有成功送回Producer端,Producer只能选择重试 最多一次 Kafka也可以提供最多一次交付可靠性保证,只需要让Producer禁止重试即可,但大部分场景下并不希望出现消息丢失 精确一次 消息不会丢失,也不会被重复处理,即使Producer端重复发送了相同的消息,Broker端也能自动去重 两种机制:幂等性、事务 幂等性 幂等原是数学中的概念:某些操作或者函数能够被执行多次,但每次得到的结果都是不变的 幂等操作:乘1,取整函数;非幂等操作:加1 ...
Java性能 -- Lock优化
Lock / synchronizedLock锁的基本操作是通过乐观锁实现的,由于Lock锁也会在阻塞时被挂起,依然属于悲观锁 synchronized Lock 实现方式 JVM层实现 Java底层代码实现 锁的获取 JVM隐式获取 lock() / tryLock() / tryLock(timeout, unit) / lockInterruptibly() 锁的释放 JVM隐式释放 unlock() 锁的类型 非公平锁、可重入 非公平锁/公平锁、可重入 锁的状态 不可中断 可中断 锁的性能 高并发下会升级为重量级锁 更稳定 实现原理 Lock锁是基于Java实现的锁,Lock是一个接口 常见的实现类:ReentrantLock、ReentrantReadWriteLock,都是依赖AbstractQueuedSynchronizer(AQS)实现 AQS中包含了一个基于链表实现的等待队列(即CLH队列),用于存储所有阻塞的线程 AQS中有一个state变量,该变量对ReentrantLock来说表示加...
Kafka -- 生产者管理TCP连接
建立TCP连接创建KafkaProducer实例123456789101112Properties properties = new Properties();properties.put("bootstrap.servers", "localhost:9092");properties.put("key.serializer", StringSerializer.class.getName());properties.put("value.serializer", StringSerializer.class.getName());// try-with-resources// 创建KafkaProducer实例时,会在后台创建并启动Sender线程,Sender线程开始运行时首先会创建与Broker的TCP连接try (Producer<String, String> producer = new KafkaProducer<>(properties)) { ProducerRec...
Java性能 -- synchronized锁升级优化
synchronized / Lock JDK 1.5之前,Java通过synchronized关键字来实现锁功能 synchronized是JVM实现的内置锁,锁的获取和释放都是由JVM隐式实现的 JDK 1.5,并发包中新增了Lock接口来实现锁功能 提供了与synchronized类似的同步功能,但需要显式获取和释放锁 Lock同步锁是基于Java实现的,而synchronized是基于底层操作系统的Mutex Lock实现的 每次获取和释放锁都会带来用户态和内核态的切换,从而增加系统的性能开销 在锁竞争激烈的情况下,synchronized同步锁的性能很糟糕 在JDK 1.5,在单线程重复申请锁的情况下,synchronized锁性能要比Lock的性能差很多 JDK 1.6,Java对synchronized同步锁做了充分的优化,甚至在某些场景下,它的性能已经超越了Lock同步锁 实现原理12345678910public class SyncTest { public synchronized void method1() { ...
Kafka -- 拦截器
设计思路 基本思想:允许应用程序在不修改逻辑的情况下,动态地实现一组可插拔的事件处理逻辑链 拦截器能够在主业务操作的前后多个时间点上插入对应的拦截逻辑 以配置拦截器类的方式动态插入到应用程序中,可以快速地切换不同的拦截器,而不影响主程序逻辑 Kafka拦截器 Kafka拦截器自0.10.0.0版本被引入后并未得到太多的实际应用 Kafka拦截器分为生产者拦截器和消费者拦截器 生产者拦截器:允许在发送消息前以及消息提交成功后植入拦截逻辑 消费者拦截器:允许在消费消息前以及提交位移后植入拦截逻辑 Kafka拦截器支持链式调用,Kafka会按照添加顺序依次执行拦截器逻辑 Kafka拦截器通过参数interceptor.classes来配置(生产者和消费者一致) 指定拦截器类时需要使用全限定名 123456789Properties props = new Properties();props.put("bootstrap.servers", "localhost:9092");props.put("key.serializer", S...
Java性能 -- NIO
BIO / NIO 在Tomcat 8.5之前,默认使用BIO线程模型,在高并发的场景下,可以设置为NIO线程模型,来提供系统的网络通信性能 页面请求用于模拟多IO读写操作的请求,Tomcat在IO读写请求比较多的情况下,使用NIO线程模型有明显的优势 网络IO模型优化网络通信中,最底层的是操作系统内核中的网络IO模型,分别为阻塞式IO、非阻塞式IO、IO复用、信号驱动式IO、异步IO TCP工作流程 首先,应用程序通过系统调用socket,创建一个套接字,它是系统分配给应用程序的一个文件描述符 其次,应用程序通过系统调用bind,绑定地址和的端口号,给套接字命名一个名称 然后,系统调用listen,创建一个队列用于存放客户端进来的连接 最后,应用程序通过系统调用accept来监听客户端的连接请求 当有一个客户端连接到服务端后,服务端会通过系统调用fork,创建一个子进程 通过系统调用read监听客户端发来的消息,通过系统调用write向客户端返回消息 阻塞式IO每一个连接创建时,都需要一个用户线程来处理,并且在IO操作没有就绪或者结束时,线程会被挂起,进入阻塞等待状态...














