简单整理下,对自己的基础查漏补缺设计模式
-
单例 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
懒汉、饿汉式、双重锁
-
多线程 多线程一般用饿汉式保证线程安全,懒汉式需要加双重锁+volatile(防止指令重排)
-
数据库连接池
-
网站计数器
-
-
工厂 定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类
-
适配器 将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
-
动态代理
-
日志采集
-
权限控制
-
AOP
-
全局异常
-
-
外观模式
统一渠道平台就是使用了外观模式,封装不同的接口,做统一管理
精确报价时需要提前准备车型信息,需要车型查询、车型校验等,我们统一处理
HashMap,
-
数据结构,1.7和1.8的区别
-
为什么用红黑树呢,
-
用二叉树不行吗?(极端情况下,二叉树会退化为左子树、右子树,也就是链表形式了)
ConcurrentHashMap
多线程线程池
为什么阿里禁用Executors?有哪些参数?具体作用?
任务进来的流程是怎么流转的?
拒绝策略有几种呢?还可以自定义拒绝策略(重要的线程,直接丢弃不好,可以记录日志,后面重新捞出来做事后补偿)
讲下AQS,用了什么设计模式?
模版模式,像ReentrantLock里Sync就是继承了AQS重写方法
维护了一个volatile修饰的state,和一个双向队列(用来存储需要等待获取锁的线程,把它封装成node从尾部添加进去)
CAS--自旋,减少线程的切换
-
有什么问题
-
LongAdder是什么?
Synchronized,锁优化?适应性自旋?默认10次
volaitle
ReadLock/WriteLock? state-->高16位读,低16位写
ReentrantLock 可重入锁,结合AQS将过程
项目中什么场景用到多线程了:充分利用CPU,提高执行效率;当一个业务逻辑执行效率特别低,耗时长,可以考虑多线程
蚂蚁、微保名单推送,我们异步送创盈、电销-->电销线下坐席联系,然后报价....
批处理任务,批量同步数据,同步东风日产、滴滴数据
接口调用失败重试
退保业务,异步处理
mq批处理获取支付宝小程序处理结果信息
hystrix里面使用多线程-->currentHashMap-->资源隔离-->设置超时时间,防止线程卡死
JVM组成
运行时数据区
-
堆
-
方法区
-
本地方法栈
-
虚拟机栈
-
程序计数器
-
PC寄存器
类加载
-
类加载器
-
双亲委派
执行引擎
本地库接口
垃圾回收算法有几种?
-
标记清除 mark-sweep
-
标记复制 copying
-
标记整理 mark-compact
怎么识别垃圾?
-
引用计数
-
可达性分析算法
垃圾回收器有哪些?
-
Serial
-
CMS
初始标记 标记GCRoots直接关联的对象以及年轻代指向老年代的对象,发生STW,但由于只标记一层,速度影响小
并发标记 GCRoots向下标记所有可达的对象
重新标记 由于并发标记过程中产生新的垃圾,重新标记,STW
并发清除 用户线程和GC线程同时执行,会产生浮动垃圾,下次GC处理
弊端:产生内存碎片、需要预留空间
-
G1 (不追求一次把所有垃圾清除,注重STW时间)
初始标记
并发标记
最终标记
清理
优势:逻辑分代,划分多个region,有Humongus区域存储大对象
FGC触发条件?
-
手动调用system.gc
-
老年代空间不足
-
方法区空间不足
如何让系统快一点的去回收垃圾?
存在大对象,或者内存泄漏,内存溢出,怎么找出原因?
配置参数,生成dump文件;通过jvisualvm来分析
性能优化
1.生产环境性能监控
2.测试环境性能分析
3.性能调优
常用命令参数
jps --查看正在运行的java进程 jstat --查看jvm统计信息(本地或远程虚拟机中的类装载、内存、垃圾收集、JIT编译等) jstat -class pid --查看类加载信息 jstat -gc pid --查看gc信息 jstat -gc -t pid 1000 20 --查看20次,每隔1000ms记录(根据GCT/间隔时间判断OOM) jinfo --查看JVM配置信息 jinfo -flags pid jmap --导出内存印象文件和内存使用情况 jmap -dump 堆转储快照文件(自动:-XX:+HeapDumpOnOutOfMemoryError,通常写dump文件前会触发异常FGC) --显示堆内存相关信息 jmap -heap pid jmap -histo pid ....
借助于工具
Visual VM
Arthas(不需要远程连接,也不需要配置监控参数 localhost:3658)
java -jar arthas-boot.jar PID(启动pid对应的arthas客户端) --jvm相关指令 dashboard --实时数据面板 thread --线程堆栈信息 heapdumpSpring
-
@Autowired和@Resource的区别
autowired根据类型匹配,resource根据名称,不行在根据类型
-
IOC和DI
-
AOP
-
Spring常用的设计模式
-
单例
实现线程安全有几种方式?
1.double check
为什么加volatile(避免指令重排,创建对象的步骤1.分配内存空间 2.初始化对象 3.将内存空间的地址赋值给对象的引用)
2.枚举
3.静态内部类
-
工厂 BeanFacotry
-
代理 SpringAOP动态代理
-
模版 解决代码重复的问题,如jdbcTemplate/restTemplate/jpaTemplate
-
观察者模式 当一个对象状态发生变化时,所有依赖于它的对象都得到通知并自动更新 listener
-
-
SpringMVC流程
-
用户发送请求到前端控制器DispatcherServlet
-
DispatcherServlet收到请求后调用HandlerMapping处理映射器,请求获取handler
-
DispatcherServlet调用处理适配器HandlerAdapter执行handler
-
handler执行业务逻辑,返回modelandview
-
modelandview-->HandlerAdaptar-->DispatcherServlet
-
DispatcherServlet将modelandview传给视图解析器ViewResolver解析
-
ViewResolver返回给具体视图view
-
DispatcherServlet对view做渲染响应用户
-
-
bean生命周期
-
实例化
-
填充属性
-
初始化 3-7
-
销毁
0.getBean,获取BeanName,尝试缓存获取(如果是FactoryBean,需要获取它的产品,调用getObject,获取bean),合并bean(对父类递归合并),记录正在创建的beanName,使用InstantiationAware BeanPostProcessor实例化前置处理
1.实例化bean
-
使用工厂方法、构造函数创建
-
组装包装类BeanWrapper
-
如果是单例,添加到单例工厂,用于提前暴露实例解决循环依赖
2.设置对象属性
-
通过名称、类型查找依赖
-
使用InstantiationAware BeanPostProcessor进行实例化后处理
-
检索依赖(getBean递归调用),注入依赖
3.检查Aware的相关接口并设置依赖
4.BeanPostProcessor前置处理
5.是否实现IntializingBean接口
6.是否配置自定义init-method
7.BeanPostProcessor后置处理
8.删除正在创建的Bean
9.如果是单例缓存生成的实例
10.如果是factoryBean,获取它的产品,调用getObject
-
-
-
怎么解决循环依赖
A对象引用B对象,B对象引用A对象
三级缓存:
-
singletonObjects(完整对象)
-
earlySingletonObjects(实例化未初始化,由三级缓存放进来)
-
singltonFactories(value是一个对象工厂)
1.先对A做实例化,属性注入前,先把A放到三级缓存中,用于提前暴露实例解决循环依赖的问题
2.属性注入B,先对B做实例化,发现依赖A,从三级缓存中获取用FactoryBean.getObject获取A的实例
3.把三级缓存中的A删除,放到二级缓存(初始化完成会删除,放到一级缓存,我们最终getBean实际是拿的一级缓存)
4.这时候A已经实例化了,最终B创建成功
5.B对象返回A属性注入的方法上,A也创建完成
-
-
SpringCloud
-
Euraka 服务注册与发现
-
自我保护机制
-
-
Zuul 服务网关
-
简单了解
-
-
Ribbon 客户端负载均衡
-
Feign 声明式服务客户端
-
Hystrix 断路器
服务隔离实现?
熔断怎么做的?有几种状态?
降级怎么做的?降级后会触发报警机制吗,怎么及时了解报警信息的?
实现原理?基于滑动窗口
-
Config 分布式统一配置
-
工作原理
-
读取Mybatis配置文件,mybatis-config.xml
-
加载SQL映射文件
-
构建会话工厂 sqlSessionFactory
-
创建会话对象SqlSession(完成数据库交互,增删改查功能)
-
Executor执行器(负责SQL语句生成和查询缓存的维护)
-
MappedStatement对象(维护节点封装 select update....)
-
输入参数映射
-
输出参数映射
动态sql
动态的做各种判断处理,空判断、sql拼接、循环处理
常用标签 if choose when otherwise foreach
xml和dao的映射:nampaces+id唯一
Zookeeper分布式锁怎么实现的
watch机制:一个临时节点删除会通知顺序节点的下一个,而不是通知所有节点(羊群现象)
选举机制:
集群角色:
-
Leader 读写服务,负责投票发起和决议
-
Follower 读服务,参与投票
-
Observer 读服务,不参与投票(提升集群读性能)
http
https
TCP
UDP
NIO
数据包的粘包和拆包
Netty MQkafka
-
怎么实现高可用的?
顺序写
预读
零拷贝
-
丢失数据怎么处理?
生产者丢失
MQ丢失
消费者丢失
为什么用MQ,为什么用RabbitMQ?
-
异步
-
解耦
-
削峰
各个系统直接耦合度太高,增加或者减少一个服务,代码层面可能都需要做处理(是否通知成功,异常重试等),用MQ,不用去关心结果(是否调用成功、超时等等);实际生产过程中老是要些通知不成功的单子,我们本地会异步跑,需要线下确认原因的,但是我们本身是不关心这些数据的,最终落地到MQ,让消费方自行确认
怎么保证高可用?
镜像集群--数据同步到其他节点(防止单点故障,3个节点)
如何保证消息不被重复消费-->幂等性
数据库唯一索引(多个库就设置一样的步长)
redis
雪花算法
如何保证信息不丢失
-
生产者:1) 发送消息前开启事务,channel.txselect(),但是阻塞的,效率低;2)/confirm/i机制,异步,MQ持久化后才返回ack
-
MQ:1)创建queue持久化 durable 2)消息的持久化 deliverymode设置为2;配合生产者/confirm/i机制,MQ挂了可以让生产者重发
-
消费者:1)关闭自动ack,改为手动代码里手动回写-->需要做幂等性判断,防止重复消费
怎么保证顺序性
拆分多个queue,一个queue对应一个consumer【业务层面保证有序】
消费端出现问题,消息堆积过多怎么办
1.大量消息堆积几个小时
临时征用机器,队列和消费端扩大N倍,等积压数据处理完了再恢复原样
2.mq中的消息过期了,数据丢了
批量重导,丢弃数据,等高峰期过了,再写临时程序,灌数据到mq
MQ有哪些应用场景
和多线程区别,多线程的性能消耗仍然在这台机器上;MQ转移到消费者上
接口的异步逻辑(支付结果回调、出单回调、配送、发短信、邮件等)
redis缓存穿透
缓存击穿
热点key可以失效,导致直接访问数据库
缓存雪崩
-
事前:配置主从+哨兵,redis cluster,避免全盘崩溃
-
事中:本地ehcache缓存+hystrix限流&降级,避免mysql被打死
-
事后:redis持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据
数据结构
-
String
redis里的字符串和java字符串有什么区别?
-
list 有序可重复集合,FIFO原则,底层双向链表,正向、反向查找(获取最新回复功能)
-
set 无序去重集合 (交集、并集)
-
zset set的排序版,支持优先级排序,维护一个score参数来实现(排行榜、带权重的消息队列)
-
hash 存储关系人信息(存储对象)
-
bitmaps 位图(判断产品有没有、用户存不存在等)
-
hyperloglog 基数统计(统计网页UV)
-
geo 存储地理位置(定位、附近的人等)
生产环境redis怎么部署的
cluster,去中心化,3主3从
为什么用redis
基于内存,高效;数据结构丰富,I/O多路复用
持久化
RDB: redis database
优点:dump.rdb,fork子进程来做写操作,效率高;冷备份
缺点:间隔一段时间做持久化的,会丢失数据
AOF:append of file
优点:记录每次写命令到日志文件中,数据较RDB全面
缺点:文件比RDB大,同步速度慢;rewrite重写
过期策略:处理过期的缓存数据
定时过期
定期过期
惰性过期:访问key时才判断是否过期;如果key一直不被访问,占用内存
内存淘汰策略:处理内存不足时需要申请额外空间的数据
全局的键空间选择性移除
noevication:默认写入报错
allkeys-lru:最常用,移除最近最少使用的key
allkeys-random
设置过期的键选择性移除
volatile-lru
volatile-random
volatile-ttl
内存优化:key-value尽量用紧凑型,节约空间;如产品信息,不要为单独的产品名称、险种、险别分别设置key
redis线程模型redis6.0以前,网络模型使用单线程,异步任务还是多线程的
基于Reactor模式文件事件处理器
-
多个socket
-
IO多路复用程序
-
文件事件分派器
-
事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
IO多路复用监听多个socket,将产生事件的socket放到队列中排队,事件分派器每次从队列取一个socket,根据事件类型交给对应的事件处理器处理
redis6.0为何引入多线程
redis的网络I/O瓶颈越来越明显,提升性能:
-
优化网络I/O模型
-
零拷贝技术或者DPDK技术
-
利用多核优势(性价比高)
-
-
提高机器内存读写速度
-
依赖硬件发展
-
redis高可用
-
redis cluster
hash slot--hash槽:16384,每个master持有部分slot,对每个key计算CRC16值,取模
-
主从复制怎么做的?
MySQLredLock
永远只有一个client能拿到锁
慢查询优化流程
-
开启慢查询日志配置
-
top10条,拿到具体sql语句
-
对具体sql做分析
-
通过Explain执行
说几个重要的字段
-
possible key
-
type
-
key
-
key_length
-
extra
using index
using
using filesort
-
MySQL底层数据结构
B+树-->叶子节点存储索引+数据,非叶子节点只存储索引可以存储更多(b树节点都是存储的索引+数据),且叶子节点有链表指向
InnoDB和MyISAM有什么区别
聚簇索引和非聚簇索引
聚簇索引(主键索引,如果没有主键,会默认创建内置id)
非聚簇索引(其他辅助索引)
ACID
-
A-原子性
-
C-一致性
-
I-隔离性
-
D-持久性
undo log 是事务原子性的保证
实现原子性的关键是当事务回滚时能撤销已经执行成功的sql语句,依靠undo log ; InnoDB会根据undo log的内容做与之相反的动作。举例如果dml是做的insert,则undo log 记录的就是delete
redo log 是事务持久性的保证
这边先引入一个概念Buffer pool;为了避免每次读写操作磁盘IO效率低下的问题,InnoDB提供了缓存(buffer pool),当我们从数据库查数据时,会先从buffer pool查找,有返回,没有再查找磁盘;当向数据库中写入数据时,先写入buffer pool, buffer pool 中的数据会定期flush到磁盘中。
有个问题,当数据库宕机,如果修改的数据还没有从buffer pool中刷入磁盘,就会导致数据的丢失,怎么办?
redo log的作用发挥了。当我们修改数据时,先写入redo log,再更新到buffer pool,这样就不会因为数据库宕机而导致事务持久性无法保证。
隔离性的保证
从读写操作来讲,主要有两方面:
-
锁机制
-
MVCC
-
锁
分库分表
中间件:Sharding-jdbc、Mycat
Sharding-jdbc基于client,有点在于不用部署,运维成本低
Mycat属于Proxy层方案,缺点是需要部署,运维团队维护
怎么从未分库到分库分表
-
停机迁移【停机维护、通过数据库中间件做分库分表】
-
双写迁移【不用停机,同时写两个库,新库去同步老库历史数据(根据时间,当然可能会有出入,再从老库写,循环,直到一样)】
MySQL读写分离
MySQL主从复制
基于binlog
大表优化方案
-
优化MySQL数据库
-
数据库设计和表创建的时候考虑性能
-
表字段避免null值出现,可以用0值代替
-
TIMESTAMP代替DATETIME
-
单表字段建议20以内
-
整型枚举代替字符型
-
索引
-
-
sql编写优化
-
limit限制查询数据量
-
避免select *
-
开启慢查询查询sql
-
不做列运算
-
大SQL拆分
-
避免!= <>等
-
避免类型转换
-
-
分区
-
range 根据月份拆分
-
hash
-
-
分库分表(需要修改代码,成本高)
-
-
增加缓存中间件,redis
-
涉及数据库类型,换个兼容MySQL的
-
开源数据库tiDB、Cubrid 会有额外的运维成本
-
阿里云、腾讯云
-
-
大数据 Hadboo 或者阿里云产品
分库分表后如何查询?
其他安全框架Shiro、Spring Security
Shiro
-
核心组件
-
Subject:当前操作用户。外部程序通过Subject进行认证授权,Subject通过SecurityManager进行认证授权
-
SecurityManager:安全管理器。所有与安全有关的操作都会和SecurityManager交互,如通过Authenticator进行认证、通过Authorizer进行授权,通过SessionManager进行会话管理等
-
Realm:SecurityManager通过Realm获取数据判断用户身份、操作权限等
-
-
认证流程
-
调用Subject.login(token)进行认证,委托给SecurityManager,调用之前必须先配置安全管理器securityManager
-
SecurityManager负责身份验证,委托给Authenticator
-
....
-
Authenticator会将响应的token传入Realm,从Realm获取身份验证信息
-
-
授权流程(先从Realm中获取用户所拥有的权限角色信息,再匹配当前的角色权限是否包含,从而判断用户是否有权限)
-
调用Subject.isPermitted接口,委托给SecurityManager
-
SecurityManager会委托给Authorizer
-
授权之前,调用对应Realm获取Subject的角色/权限用于匹配传入的角色/权限
-
Authorizer进行判断
-
Spring Securtiy
-
核心组件
-
SecurityContext
-
SecurityContextHolder
-
Authentication
-
Userdetails
-
AuthenticationManager
-
-
工作原理
Spring security 是基于filter的,filter按照顺序去执行,根据filterChain去调用下一个filter,Spring Security 在 Filter 中创建 Authentication 对象,并调用 AuthenticationManager 进行校验。Spring Security有一个filterChain去管理filter,根据需要的功能在配置中配置。
JWT
jwt只是一个生成token的机制,而Spring Security、Shiro这两个框架是用来后台做权限认证,管理,筛选的框架
-
组成(3者用 . 相连)
-
Header 由token的类型和算法名称组成json,然后在对这个json进行base64编码
{ "alg":"RSA", "type":"JWT } -
Payload 它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明
{ "admin":"true", "name":"zzz" } -
Signature 为了得到签名,必须要具备前两个条件(base64 编码过后的header、payload),还需要一个密钥,签名算法使用的header中的指定的算法,然后对他们签名
RSA(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
-
-
流程
-
用户携带用户名和密码请求访问
-
服务器校验用户凭据
-
应用提供一个token给客户端
-
客户端存储token,并且在随后的每一次请求中都带着它
-
服务器校验token并返回数据
-
-
优点
-
token存放在客户端,减轻服务器端的负担,可以让负载均衡器将用户传递到任意服务器
-
每次请求都需要带上token,没有会话,安全性大
-
开放题: 设计用户权限系统
1.模块设计
用户-->角色-->权限
用户基数加大,比如一个部门的用户角色一致,增加用户组的概念
增加组织<-->角色
2.表结构设计
PD建模,用户、角色、权限、用户角色、角色权限、用户组织、组织角色等
3.权限框架
Spring Security(和Spring Boot很好的集成)/Shrio(简单易用,好上手)
jwt-token
怎么设计微服务用户端:PC端、APP、小程序
负载均衡
网关(黑白名单的设置、限流)
业务层
中台(具体的模块)
存储层
整体包括:日志记录、权限、部署
服务治理、配置中心、链路追踪 秒杀系统设计 高并发思路app、客户端、小程序访问并放量上升,数据库几千并发已经是极限,需要做其他层面的处理
-
缓存(高性能、高并发)
大部分高并发场景都是读多写少,redis+ehcache双缓存机制抗并发
-
事前:配置主从+哨兵,redis cluster,避免全盘崩溃
-
事中:本地ehcache缓存+hystrix限流&降级,避免mysql被打死
-
-
系统拆分
微服务,模块拆分,每个服务有自己的数据库
-
考虑服务间的通信
-
-
MQ
像一些高并发写,接口耗时长的,丢到MQ里,来了大量请求,队列排队,慢慢消费
-
考虑MQ的高可用
-
-
分库分表
还是不够,只能分库分表
-
考虑哪些需要分,哪些不分,单库的表怎么和分库的表去做join,哪些数据放到缓存里等
-
-
读写分离
主从架构,主读从写,读流量多的的时候加从机
-
大数据
ES天然分布式,支持高并发,因为可以扩容机器来抗更高的并发



