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中,压 ...
Java性能 -- 序列化
序列化方案
Java RMI采用的是Java序列化
Spring Cloud采用的是_JSON序列化_
Dubbo虽然兼容Java序列化,但默认使用的是_Hessian序列化_
Java序列化原理
Serializable
JDK提供了输入流对象ObjectInputStream和输出流对象ObjectOutputStream
它们只能对实现了Serializable接口的类的对象进行序列化和反序列化
12345// 只能对实现了Serializable接口的类的对象进行序列化// java.io.NotSerializableException: java.lang.ObjectObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_PATH));oos.writeObject(new Object());oos.close();
transient
ObjectOutputStream的默认序列化方式,仅对对象的非transient的实例变量进行序列化
不会序列化对象的transient的实例变量,也不 ...
Kafka -- 生产者消息分区机制
分区概念
主题是承载真实数据的逻辑容器,主题之下分为若干个分区,Kafka的消息组织方式为三级结构:主题、分区、消息
主题下的每条消息只会保存在某个分区中,而不会在多个分区中被保存多份
分区的作用是提供负载均衡的能力,实现系统的高伸缩性
不同的分区能够被放置在不同的机器节点上,而数据读写操作的粒度也是分区
每个机器节点都能独立地执行各自分区的读写请求处理,还可以通过添加新的机器节点来增加整体系统的吞吐量
分区在不同的分布式系统有不同的叫法,但分区的思想都是类似的
Kafka – Partition
MongoDB、Elasticsearch – Shard
HBase – Region
分区策略
分区策略:决定生产者将消息发送到哪个分区的算法,Kafka提供了默认的分区策略,也支持自定义的分区策略
自定义的分区策略,需要显式地配置生产者端的参数partitioner.class
实现接口:org.apache.kafka.clients.producer.Partitioner
消息数据:topic、key、keyBytes、value、valueBytes
集群数据:cluster
...
Java性能 -- IO模型
什么是IO
IO是机器获取和交换信息的主要渠道,而流是完成IO操作的主要方式
在计算机中,流是一种信息的转换
流是有序的
把机器或者应用程序接收外界的信息称为输入流(InputStream)
从机器或者应用程序向外输出的信息称为输出流(OutputStream)
流可以被看作一种数据的载体,通过它可以实现数据的交换和传输
Java IO
Java IO主要在java.io下,有四个基本类:InputStream、OutputStream、Reader、Writer,分别用于处理字节流和字符流
字符到字节必须经过转码,该过程非常耗时,如果不知道编码类型就很容易出现乱码问题
因此IO流提供了直接操作字符的接口,方便对字符进行流操作
字节流
字节流的抽象类:InputStream/OutputStream
文件的读写操作:FileInputStream/FileOutputStream
数组的读写操作:ByteArrayInputStream/ByteArrayOutputStream
普通字符串的读写操作:BufferedInputStream/Buff ...
Kafka -- 集群参数
Broker参数存储
log.dir:表示单个路径
log.dirs:表示多个路径,推荐使用
/home/kafka1,/home/kafka2,/home/kafka3
线上生产环境中一定要为log.dirs配置多个路径,格式为CSV,逗号分隔
建议把不同的路径挂载到不同的物理磁盘上
提升读写性能,比起单块硬盘,多块物理磁盘同时读写数据有更高的吞吐量
能够实现故障转移(Failover),从Kafka 1.1引入,坏掉的磁盘上的数据会自动地转移到其他正常的磁盘上
Zookeeper
Zookeeper是一个分布式协调框架,负责协调管理并保存Kafka集群的所有元数据信息
集群有哪些Broker在运行,创建了哪些Topic,每个Topic有多少分区、分区的Leader副本在哪些机器上
zookeeper.connect,CSV格式,zk1:2181,zk2:2181,zk3:2181
Zookeeper地chroot,只需写一次,zk1:2181,zk2:2181,zk3:2181/kafka1和zk1:2181,zk2:2181,zk3:2181/kafka2
连接
listeners
...