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.concurr ...
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)) { ProducerRecord ...
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", Stri ...
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操作没有就绪或者结束时,线程会被挂起,进入阻塞等待状态
co ...
Kafka -- 无消息丢失
持久化保证
Kafka只对已提交的消息做有限度的持久化保证
已提交的消息
当Kafka的若干个Broker成功地接收到一条消息并写入到日志文件后,会告诉生产者这条消息已经成功提交
有限度的持久化保证
Kafka不保证在任何情况下都能做到不丢失消息,例如机房着火等极端情况
消息丢失生产者丢失
目前Kafka Producer是异步发送消息的,Producer.send(record)立即返回,但不能认为消息已经发送成功
丢失场景:网络抖动,导致消息没有到达Broker;消息太大,超过Broker的承受能力,Broker拒收
解决方案:Producer永远要使用带有回调通知的发送API,即**Producer.send(record, callback)**
callback能够准确地告知Producer消息是不是真的提交成功,一旦出现消息提交失败,可以进行针对性的处理
消费者丢失
Consumer端丢失数据主要体现在Consumer端要消费的消息不见了
Consumer程序有位移的概念,表示该Consumer当前消费到Topic分区的位置
丢失原因:Consumer接收一批消息后,在未处理完 ...
Java性能 -- 优化RPC网络通信
服务框架的核心
大型服务框架的核心:RPC通信
微服务的核心是远程通信和服务治理
远程通信提供了服务之间通信的桥梁,服务治理提供了服务的后勤保障
服务的拆分增加了通信的成本,因此远程通信很容易成为系统瓶颈
在满足一定的服务治理需求的前提下,对远程通信的性能需求是技术选型的主要影响因素
很多微服务框架中的服务通信是基于RPC通信实现的
在没有进行组件扩展的前提下,Spring Cloud是基于Feign组件实现RPC通信(基于HTTP+JSON序列化)
Dubbo是基于SPI扩展了很多RPC通信框架,包括RMI、Dubbo、Hessian等(默认为Dubbo+Hessian序列化)
性能测试基于Dubbo:2.6.4,单一TCP长连接+Protobuf(响应时间和吞吐量更优),短连接的HTTP+JSON序列化
RPC通信架构演化无论是微服务、SOA、还是RPC架构,都是分布式服务架构,都需要实现服务之间的互相通信,通常把这种通信统称为RPC通信
概念
RPC:Remote Process Call,远程服务调用,通过网络请求远程计算机程序服务的通信技术
RPC框架封装了底层网络通信和序列化等技 ...
Kafka -- 压缩
压缩的目的时间换空间,用CPU时间去换磁盘空间或网络IO传输量
消息层次
消息集合(Message Set)和消息
一个消息集合中包含若干条日志项(Record Item),而日志项用于封装消息
Kafka底层的消息日志由一系列消息集合日志项组成
Kafka不会直接操作具体的消息,而是在消息集合这个层面上进行写入操作
消息格式
目前Kafka共有两大类消息格式,社区分别称之为V1版本和V2版本(在0.11.0.0引入)
V2版本主要针对V1版本的一些弊端进行了优化
优化1:把消息的公共部分抽取到外层消息集合里面
在V1版本中,每条消息都需要执行CRC校验,但在某些情况下,消息的CRC值会发生变化
Broker端可能对消息的时间戳字段进行更新,重新计算后的CRC值也会相应更新
Broker端在执行消息格式转换时(兼容老版本客户端),也会带来CRC值的变化
因此没必要对每条消息都执行CRC校验,浪费空间和时间
在V2版本中,消息的CRC校验被移到了消息集合这一层
优化2:对整个消息集合进行压缩
在V1版本中,对多条消息进行压缩,然后保存到外层消息的消息体字段中
压缩的时机在Kafka中,压 ...