1.StringBoot
启动流程:
1.创建一个StopWatch并执行start方法,这个类主要记录任务的执行时间
2.配置Headless属性,Headless模式是在缺少显示屏、键盘或者鼠标时候的系统配置
3.在文件META-INF\spring.factories中获取SpringApplicationRunListener接口的实现类EventPublishingRunListener,主要发布SpringApplicationEvent
4.把输入参数转成DefaultApplicationArguments类
5.创建Environment并设置比如环境信息,系统熟悉,输入参数和profile信息
6.打印Banner信息
7.创建Application的上下文,根据WebApplicationTyp来创建Context类,如果非web项目则创建AnnotationConfigApplicationContext,在构造方法中初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
8.在文件META-INF\spring.factories中获取SpringBootExceptionReporter接口的实现类FailureAnalyzers
9.准备application的上下文
10.初始化ApplicationContextInitializer
11.执行Initializer的contextPrepared方法,发布ApplicationContextInitializedEvent事件
12.如果延迟加载,在上下文添加处理器LazyInitializationBeanFactoryPostProcessor
13.执行加载方法,BeanDefinitionLoader.load方法,主要初始化了AnnotatedGenericBeanDefinition
14.执行Initializer的contextLoaded方法,发布ApplicationContextInitializedEvent事件
15.刷新上下文(后文会单独分析refresh方法),在这里真正加载bean到容器中。如果是web容器,会在onRefresh方法中创建一个Server并启动。
Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:@SpringBootApplication(exclude{DataSourceAutoConfiguration.class})
@ComponentScan:Spring组件扫描。
2.SpringCloud
springcloud五大组件:
1、服务发现Netflix Eureka;
2、客服端负载均衡Netflix Ribbon;
3、断路器Netflix Hystrix;
4、服务网关Netflix Zuul;
5、分布式配置。
1.服务注册中心
Eureka:
Eureka客户端会向Eureka注册中心注册为服务,并通过心跳来更新它的服务租约。同时也可以从服务端查询当前注册的服务信息并把他们缓存到本地并周期性的刷新服务状态。若服务集群出现分区故障时,Eureka会转入自动保护模式,允许分区故障的节点继续提供服务;若分区故障恢复,集群中其他分区会把他们的状态再次同步回来。
Zookeeper:
Zookeeper是大数据Hadoop中的一个分布式调度组件,强调数据一致性和扩展性,可用于服务的注册和发现。
Consul:
Consul是一个高可用的分布式服务注册中心,由HashiCorp公司推出,Golang实现的开源共享的服务工具。Consul在分布式服务注册与发现方面有自己的特色,解决方案更加“一站式”,不再需要依赖其他工具。
1、通过HTTP接口和DNS协议调用API存储键值对,使服务注册和服务发现更容易;
2、支持健康检查,可以快速的告警在集群中的操作
3、支持key/value存储动态配置
4、支持任意数量的区域
ETCD:
ETCD是一个高可用的分布式键值数据库,可用于共享配置、服务的注册和发现。ETCD采用Raft一致性算法,基于Go语言实现。ETCD作为后起之秀,又非常大的优势。
1、基于HTTP+JSON的API,使用简单;
2、可选SSL客户认证机制,更安全;
3、单个实例支持每秒千次写操作,快速。
4、采用Raft一致性算法保证分布式。
Nacos:
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
2.客服端负载均衡
Ribbon客户端:ribbon 是一个客户端负载均衡器,可以简单的理解成类似于 nginx的负载均衡模块的功能。
Feign客户端:是一个声明式的Web Service客户端
负载均衡算法:
1:简单轮询负载均衡(RoundRobin)
2:随机负载均衡 (Random)
3:加权响应时间负载均衡 (WeightedResponseTime)
4:区域感知轮询负载均衡(ZoneAvoidanceRule)
3.Spring Cloud Alibaba
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
4.Redis
1.redis的5种数据类型:
string 字符串(可以为整形、浮点型和字符串,统称为元素)
list 列表(实现队列,元素不唯一,先入先出原则)
set 集合(各不相同的元素)
hash hash散列值(hash的key必须是唯一的)
sort set 有序集合
2.string类型的常用命令:
自加:incr
自减:decr
加: incrby
减: decrby
3.list类型支持的常用命令:
lpush:从左边推入
lpop:从右边弹出
rpush:从右变推入
rpop:从右边弹出
llen:查看某个list数据类型的长度
4.set类型支持的常用命令:
sadd:添加数据
scard:查看set数据中存在的元素个数
sismember:判断set数据中是否存在某个元素
srem:删除某个set数据中的元素
5.hash数据类型支持的常用命令:
hset:添加hash数据
hget:获取hash数据
hmget:获取多个hash数据
6.sort set和hash很相似,也是映射形式的存储:
zadd:添加
zcard:查询
zrange:数据排序
内存淘汰机制
noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错;
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中移除最近最少使用的 key;
allkeys-random:当内存不足以容纳新写入数据时,在键空间中随机移除某个 key;
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key;
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key;
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除;
5.Mysql
1.为什么用自增列作为主键
1、如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择主键作为聚集索引。
如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引。
如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。
2、数据记录本身被存于主索引(一颗B+Tree)的叶子节点上,这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放
因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)
3、如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页
4、如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置
此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销
同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。
2.为什么使用数据索引能提高效率
数据索引的存储是有序的
在有序的情况下,通过索引查询一个数据是无需遍历索引记录的
极端情况下,数据索引的查询效率为二分法查询效率,趋近于 log2(N)
3.Mysql锁
mysql悲观锁:在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,依靠数据库提供的锁机制,每次会申请锁并加锁和解锁操作for update 的悲观锁语法锁住记录
mysql乐观锁:乐观锁认为一般情况下数据不会造成冲突,所以在数据进行提交更新时才会对数据的冲突与否进行检测。如果没有冲突那就OK;如果出现冲突了,则返回错误信息并让用户决定如何去做。
总结&对比
悲观锁 乐观锁
概念 查询时直接锁住记录使得其它事务不能查询更不能更新 提交更新时检查版本或者时间戳是否符合
语法 select ... for update 使用 version 或者 timestamp 进行比较
实现者 数据库本身 开发者
适用场景 并发量大 并发量小
类比Java Synchronized关键字 CAS 算法
悲观锁
优点:写多读少的并发环境中使用
缺点:加锁会增加系统开销,虽然能保证数据的安全,但数据处理吞吐量低,不适合在读书写少的场合下使用
乐观锁
优点:在读多写少的并发场景下,可以避免数据库加锁的开销
缺点:在写多读少的并发场景下,即在写操作竞争激烈的情况下,会导致CAS多次重试,冲突频率过高,导致开销比悲观锁更高
4.Mysql 如何设置隔离级别,以及事物隔离有几种级别。
1)read uncommitted : 读取尚未提交的数据 :就是脏读
2)read committed:读取已经提交的数据 :可以解决脏读
3)repeatable read:重读读取:可以解决脏读 和 不可重复读 ---mysql默认的
4)serializable:串行化:可以解决 脏读 不可重复读 和 虚读---相当于锁表
4.Zookeeper
Zookeeper的角色:
领导者(leader),负责进行投票的发起和决议,更新系统状态
学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票
Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度
客户端(client),请求发起方
5.消息中间件
RabbitMQ
(1)生产者丢数据
从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
transaction机制就是说,发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。
(2)消息队列丢数据
处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。
如何保证消息的顺序性?
针对这个问题,通过某种算法,将需要保持先后顺序的消息放到同一个消息队列中(kafka中就是partition,rabbitMq中就是queue)。然后只用一个消费者去消费该队列。
我的观点是保证入队有序就行,出队以后的顺序交给消费者自己去保证,没有固定套路。
5.分布式事务
一、两阶段提交(2PC)
两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。
1.1 准备阶段
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
1.2 提交阶段
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
2. 存在的问题
2.1 同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
2.2 单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
2.3 数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
2.4 太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
二、补偿事务(TCC)
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:
Try 阶段主要是对业务系统做检测及资源预留
Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
举个例子,假入 Bob 要向 Smith 转账,思路大概是: 我们有一个本地方法,里面依次调用
首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。
优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。
三、MQ 事务消息
6.Netty
并发高,传输快,封装好
Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高
Netty的传输快其实也是依赖了NIO的一个特性——零拷贝。我们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。
Netty针对这种情况,使用了NIO中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。
netty如何解决粘包和半包
消息定长,每发送一次消息,在接收消息的同时截取固定长度的字节。
以某种分隔符进行分割。
把消息封装成消息头和消息体。
7.TCP/IP
1.DNS域名解析;
2.建立TCP连接;
3.发送HTTP请求;
4.服务器处理请求;
5.返回响应结果;
6.关闭TCP连接;
7.浏览器解析HTML;
8.浏览器布局渲染;
1、 TCP面向连接 (如打电话要先拨号建立连接); UDP是无连接 的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
校验和
序列号
确认应答
超时重传
连接管理
流量控制
拥塞控制
TCP协议的三次握手连接和四次挥手断开
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收机能正常。
第一次握手:Client什么都不能确认;Server确认了对方发送正常
第二次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自己接收正常,对方发送正常
第三次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自己发送、接收正常,对方发送接收正常
所以三次握手就能确认双发收发功能都正常,缺一不可。
1.第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
2.第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
3.第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
4.第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1, Server进入CLOSED状态,完成四次挥手。