第一轮:
类加载
-
子父类加载顺序
Fu 静态代码块 static{}
Zi 静态代码块
FU 构造代码块 {}
Fu 构造器…
zi 构造代码块…
Zi 构造器…静态代码块实现单例模式:
public class StaticInnerSingleton { private StaticInnerSingleton() { if (SingletonHolder.instance != null) { throw new IllegalStateException(); } } private static class SingletonHolder { private static StaticInnerSingleton instance = new StaticInnerSingleton(); } public static StaticInnerSingleton getInstance() { return SingletonHolder.instance; } }
优势:
-
兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。
多线程:
-
启动一个线种的几种方式:
- 继承Thread 重写run()
- 声明实现Runnable接口的类 重写run()
- 实现Callable接口,重写call()方法有执行完返回值
-
synchronized修饰静态方法时,它表示锁定class对象;修饰普通方法时,表示锁定当前对象(this)。
- 释放该锁只有两种情况
- 一是,获取锁的线程执行完了该代码块,然后线程释放对锁的占有。
- 二是,线程执行发生异常,此时JVM会让线程自动释放锁。
-
Lock接口和和synchronized的区别:
- Lock不是java语言内置的,synchronized关键字是java语言内置特性,通过Lock类可以实现同步访问
- Lock需要代码手动释放锁,而synchronized会在代码块执行完之后,自动释放锁。
-
ReentrantLock是Lock的实现类 ReentrantLock的意思是可重入锁 ReentrantLock默认是一种公平锁
- lock() 拿到锁
- unlock() 释放锁
- tryLock() 试着拿锁 true拿到了,false 未拿到
- interruptibly() 可中断的锁(如果是在等待拿锁的过程中,可以被其他线程终止拿锁)Thread的interup()方法,对锁进行中断。
-
ReadWriteLock 接口下的类ReetrantReadWriteLock
- writeLock().lock() 写锁
- readLock().lock() 读锁
-
悲观锁与乐观锁:
- synchronized就是悲观锁,是一种独占锁
- 偏向锁,轻量锁,自旋锁,锁消除,锁粗化
-
死锁:
-
synchronized死锁
public SyncThread(Object o1, Object o2){ this.obj1=o1; this.obj2=o2; } @Override public void run() throws Exception{ //两个线程同时调此方法,t1(o1,02) t2(o2,o1) synchronized (obj1) { Thread.sleep(1000); synchronized (obj2) { Thread.sleep(1000); } } }
-
-
wait 和sleep 方法的区别:
wait()方法是Object提供,需要在synchronized同步代码块中才能使用,调用wait ()后线程将会释放锁,而等待其他线程调用 notify()方法随机唤醒,唤醒后,等待拿锁。
sleep()方法是线程对象Thread的静态方法,调用后,并不会释放锁资源 。等待时间自动解锁。
-
线程池:Executors
-
阿里开发手册:【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 -
创建几种线程池的方式
-
SpringBoot 整合线程池:
@Configuration @EnableAsync public class TaskPoolConfig { @Bean("taskExecutor") public Executor taskExecutror(){ ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(50); taskExecutor.setMaxPoolSize(100); taskExecutor.setQueueCapacity(10000); taskExecutor.setKeepAliveSeconds(60); taskExecutor.setThreadNamePrefix("basicIaskExecutor--"); taskExecutor.setWaitForTasksToCompleteOnShutdown(true); taskExecutor.setAwaitTerminationSeconds(60); return taskExecutor; } }
-
-
CompletableFuture 实现异步编排。
创建异步任务:
// 无返回值 public static CompletableFuture runAsync(Runnable runnable); public static CompletableFuture runAsync(Runnable runnable, Executor executor); //有返回值 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier); public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);
计算完成时对结果的处理
//成功的回调:执行完的结果作为参数。 是否异步,是否使用线程池 //结果为T Throwable 为异常 public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action); public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action); public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor);
//异常的回调 T为有异常的返回值 public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn); //执行完之后(无论成功失败)后续处理 public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn); public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn); public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor); //串行化 //对前一个线程的返回值进行处理 public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn); public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn); public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor); //需要前一个线程的返回值 public CompletableStage<void> thenAccept(Consumer<? super T> action); public CompletableStage<void> thenAcceptAsync(Consumer<? super T> action); public CompletableStage<void> thenAcceptAsync(Consumer<? super T> action,Executor executor); //不需要前一个线程的返回值 public CompletableFuture<void> thenRun(Runnable action); public CompletableFuture<void> thenRunAsync(Runnable action); public CompletableFuture<void> thenRunAsync(Runnable action,Executor executor);
//编排 thenCombine(...) 组合两个future,获取两个future的返回值,执行第当前future然后返回 thenAcceptBoth(... ) 组合两个future,获取两个future的返回值,执行第当前future runAfterBoth(... ) 组合两个future,并执行当前future无返回值
//全部执行完,或者某一个执行完 public static completableFuture<void> allOf(CompletableFuture<void> ... csf); public static completableFuture<void> anyOf(CompletableFuture<void> ... csf);
-
消息队列
-
BlockingQueue接口也是java.util.concurrent下的主要用来控制线程同步的工具
-
boolean add() 加入队列 能容纳 true,不能,抛异常
-
boolean offer() 加入队列 能容纳 true,不能容纳 false
-
put() 加入队列能存则存,不能存等有空间了再存
-
pull(time)取走里排在首位的对象,若不能立即取出则等time指定的时间,取不到返回null
-
take()阻塞方法,有则取出 ,无则等
-
int remainingCapacity()返回队列剩余容量
-
boolean remove()从队列中移除某个元素
-
实现类:ArrayBlockingQueue:一个由数组支持的有界阻塞队列
-
实现类:LinkedBlockingQueue:大小不一定的BlockingQueue可以指定也可以不指定大小
-
JVM的回收机制
https://www.cnblogs.com/laoqing/p/8 583583.html
- 按代的垃圾回收机制
新生代(Young generation):绝大多数最新被创建的对象都会被分配到这里,由于大部分在创建后很快变得不可达,很多对象被创建在新生代,然后“消失”。对象从这个区域“消失”的过程我们称之为:Minor GC 。
老年代(Old generation):对象没有变得不可达,并且从新生代周期中存活了下来,会被拷贝到这里。其区域分配的空间要比新生代多。也正由于其相对大的空间,发生在老年代的GC次数要比新生代少得多。对象从老年代中消失的过程,称之为:Major GC 或者 Full GC。
持久代(Permanent generation)也称之为 方法区(Method area):用于保存类常量以及字符串常量。注意,这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC事件也被算为 Major GC 。 - 默认的新生代(Young generation)、老年代(Old generation)所占空间比例为 1 : 2 。
- 新生代空间的构成与逻辑
为了更好的理解GC,我们来学习新生代的构成,它用来保存那些第一次被创建的对象,它被分成三个空间:
一个伊甸园空间(Eden)
两个幸存者空间(Fron Survivor、To Survivor)
默认新生代空间的分配:Eden : Fron : To = 8 : 1 : 1
每个空间的执行顺序如下:
1、绝大多数刚刚被创建的对象会存放在伊甸园空间(Eden)。
2、在伊甸园空间执行第一次GC(Minor GC)之后,存活的对象被移动到其中一个幸存者空间(Survivor)。
3、此后,每次伊甸园空间执行GC后,存活的对象会被堆积在同一个幸存者空间。
4、当一个幸存者空间饱和,还在存活的对象会被移动到另一个幸存者空间。然后会清空已经饱和的哪个幸存者空间。
5、在以上步骤中重复N次(N = MaxTenuringThreshold(年龄阀值设定,默认15))依然存活的对象,就会被移动到老年代。
从上面的步骤可以发现,两个幸存者空间,必须有一个是保持空的。如果两个两个幸存者空间都有数据,或两个空间都是空的,那一定是你的系统出现了某种错误。
我们需要重点记住的是,对象在刚刚被创建之后,是保存在伊甸园空间的(Eden)。那些长期存活的对象会经由幸存者空间(Survivor)转存到老年代空间(Old generation)。
也有例外出现,对于一些比较大的对象(需要分配一块比较大的连续内存空间)则直接进入到老年代。一般在Survivor 空间不足的情况下发生。 - 老年代空间的构成与逻辑
老年代空间的构成其实很简单,它不像新生代空间那样划分为几个区域,它只有一个区域,里面存储的对象并不像新生代空间绝大部分都是朝闻道,夕死矣。这里的对象几乎都是从Survivor 空间中熬过来的,它们绝不会轻易的狗带。因此,Full GC(Major GC)发生的次数不会有Minor GC 那么频繁,并且做一次Major GC 的时间比Minor GC 要更长(约10倍)。
简述堆栈
- 都是java在内存中存放数据的地方,程序员不能直接地设置堆或者栈
堆栈的不同点 | 堆 | 栈 |
---|---|---|
速度 | 慢 | 快 |
生存周期与数据大小 | 动态分配 | 确定 |
存放内容 | 包装类型: Integer, String, Double | 基本数据类型,部分String |
是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量. | 所有基本类型和引用类型的引用都在栈中存储.栈中数据的生存空间一般在当前scopes内(就是由{…}括起来的区域). |
设计模式:动态代理
算法:快速排序 的实现
集合
两种区别 | HashMap | ConcurrentHashMap |
---|---|---|
底层 | Entry数组+链表 | Segment数组+链表 |
默认大小 | 16 | (16)*2 |
加载因子 | 0.75 | 0.75 |
扩容方式 | Entry数组数组扩容 | Segment内部扩容(数组默认16,只能初始化定义不支持扩展) |
线程安全 | 否 | Segment本身是线程安全的继承 ReentrantLock |
读写数度 | 快 | 慢 |
Spring ioc 、事务
- 什么是DI机制:依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者
- IOC容器:就是具有依赖注入功能的容器,是可以创建对象的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做。。在Spring中BeanFactory是IOC容器的实际代表者。
- 给IOC容器中注册组件:
- 1, 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[适合自己写的类]
- 2,@Configuration@Bean[导入第三方包里的组件]
- 3,@Configuration@Import[快速给窗口中注册一个组件]
- @Import[快速给窗口中导入一个组件]
- ImportSelector:返回需要导入的组件的全类名数组
- ImportBeanDefinitionRegistrar
- 使用Spring 提供的FactoryBean(工厂Bean)
- 注入方式:构造器,set方法
- AOP原理:
- 面向切面编程(aop)是对面向对象编程(oop)的补充, 面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。 AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象, 是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
- AOP 面向切面编程
- 将业务逻辑组件和切面类都加入到容器中,告诉Spring 哪个是切面 类(@Aspect)
- 在切面 类上的每一个通知方法上标注通知注解,告诉Spring何时 何地运行(切入点表达式)。
- 配置文件开启aop模式,或者配置类@EnableAspectJAutoProxy
- 声明式事物:
- @Configuration类 @EableTransactionManagerment
- 注册事务管理器在容器中 (引用数据源)
SpringMVC
-
(1)客户端通过url发送请求
(2-3)核心控制器Dispatcher Servlet接收到请求,通过系统或自定义的映射器配置找到对应的handler,并将url映射的控制器controller返回给核心控制器。
(4)通过核心控制器找到系统或默认的适配器
(5-7)由找到的适配器,调用实现对应接口的处理器,并将结果返回给适配器,结果中包含数据模型和视图对象,再由适配器返回给核心控制器(8-9)核心控制器将获取的数据和视图结合的对象传递给视图解析器,获取解析得到的结果,并由视图解析器响应给核心控制器
(10)核心控制器将结果返回给客户端
Dubbo 原理
SpingBoot
- 1、首先 SpringBoot可以快速一键快速搭建Spring框架,简化初始配置 ,可与主流框架集成
2、内置Servlet容器,无需在打War包
3、使用了Starter(启动器)管理依赖并版本控制
4、大量的自动配置,简化开发,方便集成第三方
5、提供准生产环境运行时的监控,如指标,健康,外部配置等
6、无需配置,推荐使用yml.
SpringCloud:
- Eureka 服务发现与注册
- Ribbon 客户端负载均衡
- Hystrix 断路器 熔断器
- Zuul 服务网关
- Config 分布式配置文件管理
索引:
索引的数据结构:
-
使用 B+tree 、Hash 的数据结构 。
-
其它数据结构的特点:
-
二叉树:如果是有序新增,会有单边增长的风险。
-
红黑树:有节点太长的风险。
-
HASH:hash索引 无法满足范围查找和LIKE查询。
-
Btree:
- B-TREE 每个节点都是一个二元数组: [key, data],所有节点都可以存储数据。key为索引key,data为除key之外的数据。
- 检索原理:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或未找到节点返回null指针。
- 缺点:1.插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质。造成IO操作频繁。2.区间查找可能需要返回上层节点重复遍历,IO操作繁琐。
-
B+Tree: B-Tree的变种
- 与B-Tree相比,B+Tree有以下不同点:非叶子节点不存储data,只存储索引key;只有叶子节点才存储data
- Mysql中B+Tree:在经典B+Tree的基础上进行了优化,增加了顺序访问指针。在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。这样就提高了区间访问性能
-
-
Btree 存储的索引失效问题。
- 全值匹配
- 最佳左前缀法则
- 不在索引列上做任何操作
- 存储引擎不能使用索引中范围右边的列
- 尽量使用覆盖索引
- mysql在使用不等于 != 、<>的时候无法匹配索引
- is null、is not null也无法使用索引
- like通配符开关%abc索引会失效
- 字符串不加单引号
- 少用or
MYSQL 主从复制 分表分库
-
分区:
- rang 分区根据某个字段的范围来分区
- list 分区基于列值匹配一个离散值集合中的某个值来进行选择。
-
分库分表:
- 按照业务拆分,把不同的业务放在不同的数据库
- 主从复制
- 分表,按照业务ID进行分表,垂直拆分与水平拆分
- 垂直拆分,表字段过长,分为多个表
- 数量量太大,按照业务分为多个表,理论上单表数据到500W
-
主从复制:
- 原理:slave会从master读取binlog日志来进行数据同步
- 配置:my.cnf 配置文件 /etc/my.cnf,开启二进制日志,设置server-id=1
- 创建用户并授权:CREATE USER ‘repl’@‘123.57.44.85’ IDENTIFIED BY ‘slavepass’
- GRANT REPLICATION SLAVE ON . TO ‘repl’@‘123.57.44.85’;#分配权限
- flush privileges; #刷新权限
- 查看master状态 SHOW MASTER STATUS;记住file和position的值
- 从机:配置my.cnf 修改server-id=2 重启
- 从机:
mysql> CHANGE MASTER TO
-> MASTER_HOST=‘182.92.172.80’,
-> MASTER_USER=‘rep1’,
-> MASTER_PASSWORD=‘slavepass’,
-> MASTER_LOG_FILE=‘mysql-bin.000003’,
-> MASTER_LOG_POS=73; - 查看从机状态:show slave status\G; 当Slave_IO_Running和Slave_SQL_Running都为YES的时候就表示主从同步设置成功了。
- 停止服务复制功能:stop slave;
MYSQL的引擎
数据库引擎及功能 ISAM MYISAM InnoDB Memory(HEAP)
读速度 快快 较快 相对较慢 最快
外键 是 是 是
支持索引 否 否 是
支持事务 否 否 是
支持容错 否 否 是
锁 表锁 行锁
一般来说,MyISAM适合:
(1)做很多count 的计算;
(2)插入不频繁,查询非常频繁;
(3)没有事务。
InnoDB适合:
(1)可靠性要求比较高,或者要求事务;
(2)支持聚集索引:索引中的数据物理存放地址和索引的顺序是一致的
(3)表更新和查询都相当的频繁,并且表锁定的机会比较大的情况指定数据引擎的创建,让所有的灵活性成为可能的开关是提供给ANSI SQL的MySQL扩展——TYPE参数。MySQL能够让你在表格这一层指定数据库引擎,所以它们有时候也指的是table formats。下面的示例代码表明了如何创建分别使用MyISAM、ISAM和HEAP引擎的表格。要注意,创建每个表格的代码是相同的,除了最后的 TYPE参数,这一参数用来指定数据引擎。
- 表锁与行锁
- 表锁:MYISAM引擎支持:查看
- show status like ‘table%’:
- table_locks_immediate 产生表锁的次数
- table_locks_waited 出现表级锁定争用发生等待的次数
- 行锁:InnoDB :读已之所写。索引失效可能导致表锁
- show status like ‘innodb_row_lock%’ :
- innodb_row_lock_avg 平均等待时长
- innodb_row_lock_waits 等待发生的次数
- innodb_row_lock_time 等待总时长
- 表锁:MYISAM引擎支持:查看
- InnoDB行锁升级表锁:1,类型转换 where id = ‘123’ 2,索引失效
事物的特性,与隔离级别
-
spring对事务的控制有:传播行为、隔离级别、是否只读、有回滚机制、事务超时
-
事物的特性:ACID
- 原子性:Atomicity 一件事做完,要不做,要么做完
- 一致性:Consistency 事务的运行不改变数据库原本的一致性
- 隔离性:Isolation 事务未提交,数据不会改变
- 持久性:Durability 事务一旦提交后,即使宕机,也不会丢
-
读数据的概念:
- 脏读(Dirty Reads):脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
- 不可重复读(Non-Repeatable Reads):不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
- 幻读(Phantom Reads):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
-
隔离级别:
- 隔离级别 一致性 脏读 不可重复读 幻读
读未提交 最低级别不读取物理上损坏数据 是 是 是
读已提交 语句级 否 是 是
可重复读 事物级 否 否 是
串行化 最高级别、事务级 否 否 否 - 读未提交(Read Uncommitted):SELECT语句以非锁定方式被执行,所以有可能读到脏数据,隔离级别最低。
- 读已提交(Read Committed):只能读取到已经提交的数据。即解决了脏读,但未解决不可重复读。
- 可重复读(Repeated Read):在同一个事务内的查询都是事务开始时刻一致的,InnoDB的默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻读。
- 串行话(Serializable):完全的串行化读,所有SELECT语句都被隐式的转换成SELECT … LOCK IN SHARE MODE,即读取使用表级共享锁,读写相互都会阻塞。隔离级别最高。
- Mysql 四种都支持 默认可重复读
- 隔离级别 一致性 脏读 不可重复读 幻读
-
传播行为:
- PROPAGATION_REQUIRED 默认的,假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
- PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
- PROPAGATION_REQUIRES_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
Redis
-
Redis的优点:
性能极高 – Redis能支持超过 100K+ 每秒的读写频率。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
-
持久化:
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
REDIS持久化的区别 | RDB | AOF |
---|---|---|
对性能的影响 | 小 | 大 |
一致性 | 弱 | 强 |
恢复速度 | 快 | 慢 |
redis宕机以及开发建议:https://blog.fundebug.com/2018/09/21/redis_incident/
redis主从复制
- 功能 命令
查看主从状态 info replication
认定一个主机,自己做从机 slaveof 192.168.152.128 6379 临时生效
反客为主 slaveof no one - 原理以及步骤:
- 保存主节点信息
- 主从建立socket连接
- 发送ping命令
- 权限验证
- 同步数据集
- 命令持续复制
- 主从复制方式:
- 一主二从:一台主机两台从机
- 薪火相传:一台主机下一台从机,从机下面又有一台从机
- 反客为主:当主机宕机时,从机使用slaveof no one 命令,自己当作主机
- 哨兵模式:
- 自定义:/myredis目录下 sentinel.conf文件
- sentinel auth-pass mymaster 12345678 //sentinel连主节点的密码
- 启动哨兵:redis-sentinel /myredis/sentinel .conf 看日志
reids雪崩与击穿
-
redis雪崩:
- 数据同时过期:可以针对过期时间设定不同的值,固定值+随机值
- redis挂了:主从复制、本地缓存如:ehcache
-
redis缓存击穿:请求数据大量不命中
- 参数不合法,业务上过滤掉
- 第一次请求走数据库,然后再入缓存,第二次就可以走缓存了。
-
双写不一致:
- 先更新数据库,再更新缓存
- 先删除缓存,再更新数据库
- 先更新数据库,再删除缓存 (推荐)
RabbitMQ
概念:
- Server:接收客户端的连接,实现AMQP实体服务。
- Connection:连接,应用程序与Server的网络连接,TCP连接。
- Channel:信道,消息读写等操作在信道中进行。客户端可以建立多个信道,每个信道代表一个会话任务。
- Message:消息,应用程序和服务器之间传送的数据,消息可以非常简单,也可以很复杂。有Properties和Body组成。Properties为外包装,可以对消息进行修饰,比如消息的优先级、延迟等高级特性;Body就是消息体内容。
- Virtual Host:虚拟主机,用于逻辑隔离。一个虚拟主机里面可以有若干个Exchange和Queue,同一个虚拟主机里面不能有相同名称的Exchange或Queue。
- Exchange:交换器,接收消息,按照路由规则将消息路由到一个或者多个队列。如果路由不到,或者返回给生产者,或者直接丢弃。RabbitMQ常用的交换器常用类型有direct、topic、fanout、headers四种,后面详细介绍。
- Binding:绑定,交换器和消息队列之间的虚拟连接,绑定中可以包含一个或者多个RoutingKey。
- RoutingKey:路由键,生产者将消息发送给交换器的时候,会发送一个RoutingKey,用来指定路由规则,这样交换器就知道把消息发送到哪个队列。路由键通常为一个“.”分割的字符串,例如“com.rabbitmq”。
- Queue:消息队列,用来保存消息,供消费者消费。
交换机类型:
-
Fanout Exchange
扇出:会把所有发送到交换器的消息路由到所有绑定的队列中。优点是转发消息最快,性能最好。
-
Topic Exchange
主题:该类型的交换器将所有发送到Topic Exchange的消息被转发到所有RoutingKey中指定的Topic的队列上面。
-
Direct Exchange
Direct :该类型的交换器将所有发送到该交换器的消息被转发到RoutingKey指定的队列中,全值匹配。
-
Headers Exchange
该类型的交换器不依赖路由规则来路由消息,而是根据消息内容中的headers属性进行匹配。headers类型交换器性能差,在实际中并不常用。
第二轮:
秒杀
- 服务单一职责 、独立部署
秒杀服务即使自己扛不住压力,也不影响其他功能正常运行。 - 秒杀链接加密。
防止恶意攻击,防止链接暴露,提前秒杀商品。 - 库存预热、快速扣减。
秒杀读多少写少,无需每次实时验证库存,我们库存预热放在redis中,信号量控制进来的请求。 - 动静分离
nginx做好动静分离,保证秒杀和商品详情页的动态请求才进入到后端的服务器的集群,使用CDN网络,分担本集群的压力。 - 恶意请求拦截
识别非法攻击请求,并进行拦截。 - 流量错峰。
使用各种手段,将流量分担到更大宽度的时间点,比如验证码、加入购物车等。 - 限流 alibaba-sentinel、熔断、降级。
前端限流加后端限流
限制请求次数,限制总量,快速失败降级运行,熔断隔离防止雪崩。 - 队列削峰
一万个商品,每个1000件秒杀,双十一所有秒杀成功的请求,进入队列,慢慢创建订单,扣减库存。 - 一句话总结 :缓存、异步、队排好
项目中有哪些比较难解决的问题,最后都是怎么解决的
分布式事务
SEATA:
-
概念:阿里巴巴开源的分布式事务框架。Seata目前的事务模式有AT,TCC,Saga三种模式,默认即是AT模式,AT本质上是2pc协议的一种实现,三种模式的不同后面文章再详细介绍。
-
TC:事务协调者
-
TM:事务管理者
-
RM:事务执行者
AT模式:2PC