NIO编程
经过前面对 NIO 的介绍,我们理解 NIO 不会在数据没有准备好的时候一直阻塞线程,线程可以继续做其他事情。正是因为这个特点,NIO 可以做到通过一个线程来处理多个操作。 使用场景分析 BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序简单易理解。 NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,弹幕系统,服务器间通讯等。编程比较复杂,JDK1.4 开始支持。 AIO 方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持。 NIO 的三大核心组件 每一个 channel 都会对应一个 Buffer; Selector 对应一个线程,一个线程对应多个 channel 连接; Selector 会根据不同的事件在各个通道上切换,因为程序切换到哪个 channel 是由事件决定的; Buffer 是一个内存块,底层是一个数组; 和 BIO 的输入输出流不同, NIO 的 Buffer...
RPC-动态代理
使用 JDK 动态代理的核心是InvocationHandler接口和Proxy类。 InvocationHandler 接口实现该接口来处理自定义逻辑,当动态代理对象调用一个方法时,这个方法的调用就会被转发到实现了 InvocationHandler 接口类的invoke方法来调用。12345public interface InvocationHandler { /** * 当你使用代理对象调用方法的时候实际会调用到这个方法 */ public Object invoke(Object proxy, Method method, Object[] args) Proxy 类使用最多的是 Proxy 类的newProxyInstance()方法,用来生成代理对象。123public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, ...
ZooKeeper
ZooKeeper集群为了保证高可用性,最好以集群方式部署 ZooKeeper,官方架构图就是一个 ZooKeeper 集群整体对外提供服务。 每一个 Server 代表一个安装 ZooKeeper 服务的服务器,组成 ZooKeeper 服务的服务器会在内存中维护一个状态,并且每台 Server 之间都互相保持着通信。集群之间通过 ZAB 协议保持数据的「一致性」。 ZooKeeper集群角色在 ZooKeeper 中没有选择传统的 Master/Slave 概念,而是通过 Leader、Follower、Observer 三种角色: 角色 说明 Leader 为客户端提供读、写服务,负责投票的发起和决议,更新状态 Follower 为客户端提供读服务,如果是写则转发给Leader;参与选举中的投票 Observer 为客户端提供读服务;不参与选举投票,不参与“过半写成功”策略 Observer 观察者在不影响写性能的情况下(不参与“过半写成功”策略)提升集群的读性能。 Leader选举当 Leader...
Java NIO
NIO(Non-blocking I/O 或者 New I/O),是一种同步非阻塞的 I/O 模型,也是 I/O 多路复用的基础,逐渐成为解决高并发与大量连接问题的有效方式。 传统的 BIO 模型传统服务器同步阻塞 I/O 处理(Blocking I/O)的经典模型是:BIO 同步阻塞模型通常使用多线程,这是因为socket.accept(), socket.read(),socket.write()三个主要 I/O 方法都是同步阻塞的,如果是单线程的话就只能挂死在那;开启多线程可以充分利用 CPU 去处理其他的事情。 多线程的本质:①利用多核特性;②当 I/O 阻塞系统,但 CPU 空闲的时候可以利用多线程使用 CPU 资源。 现在的多线程编程通常都基于线程池,可以降低线程频繁创建和销毁的成本开销,在活动连接数不是很多(单机小于1000)情况下是合理的,可以让每个连接专注自己的 I/O。 BIO 模型最本质的问题在于「严重依赖线程资源」,像 Java 的线程栈至少要分配将近 1M 的空间,如果系统的线程数过千整个 JVM...
设计模式
我理解的设计模式实际上是在某些编码场景下,针对某类问题的一种通用的设计方法和解决方案。设计模式有 23 种共分为三大类:创建型模式、结构型模式、行为型模式,这篇文章总结我用过的设计模式。 创建型模式单例模式单例模式保证一个类只有一个实例——如果你创建了一个对象,同时过一会儿后你决定再创建一个新对象,此时你会获得之前已创建的对象,而不是一个新对象;为该实例提供全局访问节点,允许在程序的任何地方访问特定对象。通过「双重检查锁」支持多线程创建单例对象: 1234567891011121314151617181920final class SingletonDemo { private static volatile SingletonDemo instance = null; // 禁用构造方法 private SingletonDemo() { } public static singletonDemogetInstance() { // 先判断实例是否存在 if (instance ==...
ThreadLocal机制
ThreadLocal为什么要有 ThreadLocal为了解决多线程存在「线程安全」的问题——线程并发对同一临界区的共享资源进行访问而导致的内存数据安全问题,才引入的 ThreadLocal——每个线程拥有自己线程隔离的「本地变量」。 ThreadLocal 中弱引用问题要说清楚 ThreadLocal 中的弱引用机制,还要从 ThreadLocal 的设计初衷讲起。为了解决多线程线程安全问题,每一个 Thread 中都有一个 ThreadLocalMap,其中 key 为 ThreadLocal 本地变量,value 为想要存放的具体值。为了实现线程安全,不允许用户直接接触 ThreadLocalMap 向里面 put Entry,而是通过 ThreadLocal 的 API:get、set、remove 来间接操作。具体的实现是,ThreadLocalMap 作为 ThreadLocal 中的静态内部类,ThreadLocal 为 Map 提供了外层的用户封装,而将可能出现的线程冲突交由内部实现来解决。 这就造成了当使用者没有正确的通过 remove 方法将...
消息队列选型
消息队列这是一篇对消息队列的详细总结(可能对于高可用和架构方面的总结还不够,未来会随着对知识理解的加深继续完善)。在项目中使用了 RabbitMQ,所以也记录了 RabbitMQ 的使用集成。 消息队列可以看成一个存放消息的容器,由于 Queue 是一种先进先出的数据结构,所以消息队列消费数据也是「按序」的。 消息队列基础消息队列有什么作用 这部分来自 JavaGuide 异步处理 将服务中一些「非核心的耗时业务」以异步方式执行,将消息加入消息队列中之后就立即返回结果,减少响应时间。 由于用户请求在将数据写入消息队列后就返回给用户,但是下游任务可能会因为各种情况而失败。所以在使用消息队列异步处理下游业务时还要进行一些流程上的修改——比如订机票、酒店时用户提交订单,后台将订单写入 MQ,不能立即返回订单提交成功,需要等待 MQ 中的订单消费者将订单出库后,再通过短信的方式通知用户。 为了保证消息队列中的消息被消费,会将消息缓存在生产者服务器的 buffer 中,直到收到消费者服务器的成功消费通知。 削峰 / 限流...
手写SpringBoot Starter
SpringBoot 自动装配用我的一句话总结自动装配就是:SpringBoot 通过起步依赖和自动装配机制来简化项目的构建和开发管理。 手动实现SpringBoot Starter步骤1 创建Maven项目创建新 Maven 项目,手动添加必要的 pom 依赖。 12345678910111213141516<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.7</version></parent><dependencies> <dependency> <groupId>org.springframework.boot</groupId> ...
MySQL是如何存储数据的
执行一条 select 语句,在 MySQL 中发生了什么? MySQL 数据文件存放在哪 InnoDB 数据组织方式InnoDB 的行记录作为 MySQL 用来存储数据的默认存储引擎 InnoDB,在内部到底是如何存储的呢?InnoDB 总共有四种数据记录的行格式,其中只需要知道 Compact 和 Dynamic,Compact 是一种紧凑的行格式,目的是能在数据页中尽可能多的存储更多记录,Dynamic 作为 5.7 版本之后默认的行格式是由 Compact 发展来的。 记录的额外信息 变长字段长度列表:先找出所有变长字段,对变长字段列逆序存放 NULL 值列表:先找出所有允许 null 的字段,用比特 0 表示不为 NULL,用比特 1 表示为 NULL,仍然对这些字段列逆序存放 为什么要对变长字段长度列表和 NULL 列表进行「逆序排列」?因为连接不同行记录的指针指向行记录的位置是记录头信息和真实数据之间的位置,这样的好处是向左可以读到记录的额外信息,向右可以直接读到真实数据。而逆序存放的目的是使位置靠前的记录的真实数据和数据对应的字段长度可以同时在一个...
垃圾收集器
JVM垃圾收集器JVM的垃圾回收机制一共分为两步:第一步先识别出不再使用的内存空间,第二步回收这些内存空间。 判断对象死亡的方式:1、引用计数法 → 无法解决对象之间循环引用2、可达性分析法 回收内存空间的方法:1、标记-清除算法 → 产生内存碎片2、复制算法(为了解决内存碎片问题:可用内存减少一半) → 对于存活时间久的对象需要频繁的复制,所以更适合存活时间短的新生代对象3、标记-整理算法(整理开销大) → 更适合不需要频繁GC的老年代4、分代收集算法:对堆内存分为新生代和老年代,应用不同的垃圾回收策略,提高回收效率。 介绍完这么多,终于可以引出今天的主题,也就是垃圾收集器。如果说垃圾回收算法是方法论,收集器就是依据方法论的具体实现。JVM垃圾收集器分为分代收集(CMS)和分区收集(G1、ZGC),下面就来一一介绍。 CMSCMS(Concurrent Mark...