spring的循环依赖
Spring 的循环依赖的理论依据基于 Java 的引用传递,当获得对象的引用时,对象的属性是可以延 后设置的,但是构造器必须是在获取引用之前
Spring通过setXxx或者@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFactory对 象来完成的,简单来说ClassA在调用构造器完成对象初始化之后, 在调用ClassA的setClassB方法 之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。
- Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。
- ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB,此时ClassB不存在Spring 容器中。
- Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中 ClassB调用setClassA方法,Spring从容器中获取ClassA ,因为第一步中已经提前暴露了
- ClassA,因此可以获取到ClassA实例 ClassA通过spring容器获取到ClassB,完成了对象初始化操作。 这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。
Spring 实现AOP思想
Spring 实现AOP思想使用的是动态代理技术 默认情况下,Spring会根据被代理对象是否实现接口来选择使用JDK还是CGLIB。当被代理对象没有实现 任何接口时,Spring会选择CGLIB。 当被代理对象实现了接口,Spring会选择JDK官方的代理技术,不过 我们可以通过配置的方式,让Spring强制使用CGLIB。
spring事务的四大特性:
- 原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都 不发生。 从操作的⻆度来描述,事务中的各个操作要么都成功要么都失败
- 一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。 例如转账前A有1000,B有1000。转账后A+B也得是2000。 一致性是从数据的⻆度来说的,(1000,1000) (900,1100),不应该出现(900,1000)
- 隔离性(Isolation) 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务, 每个事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。 比如:事务1给员工涨工资2000,但是事务1尚未被提交,员工发起事务2查询工资,发现工资涨了2000 块钱,读到了事务1尚未提交的数据(脏读)
- 持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障 也不应该对其有任何影响。
spring事务的隔离级别;
不考虑隔离级别,会出现以下情况:(以下情况全是错误的),也即为隔离级别在解决事务并发问题
- 脏读:一个线程中的事务读到了另外一个线程中未提交的数据。
- 不可重复读:一个线程中的事务读到了另外一个线程中已经提交的update的数据(前后内容不一样)
场景: 员工A发起事务1,查询工资,工资为1w,此时事务1尚未关闭 财务人员发起了事务2,给员工A张了2000块钱,并且提交了事务 员工A通过事务1再次发起查询请求,发现工资为1.2w,原来读出来1w读不到了,叫做不可重复读
- 虚读(幻读):一个线程中的事务读到了另外一个线程中已经提交的insert或者delete的数据(前后条 数不一样)
场景: 事务1查询所有工资为1w的员工的总数,查询出来了10个人,此时事务尚未关闭 事务2财务人员发起,新来员工,工资1w,向表中插入了2条数据,并且提交了事务 事务1再次查询工资为1w的员工个数,发现有12个人,⻅了⻤了
数据库共定义了四种隔离级别:
- Serializable(串行化):可避免脏读、不可重复读、虚读情况的发生。(串行化) 最高
- Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。(幻读有可能发生) 第二 该机制下会对要update的行进行加锁
- Read committed(读已提交):可避免脏读情况发生。不可重复读和幻读一定会发生。 第三
- Read uncommitted(读未提交):最低级别,以上情况均无法保证。(读未提交) 最低 注意:级别依次升高,效率依次降低
- MySQL的默认隔离级别是:REPEATABLE READ 查询当前使用的隔离级别: select @@tx_isolation; 设置MySQL事务的隔离级别: set session transaction isolation level xxx; (设置的是当前 mysql连接会话的,并不是永久改变的)
spring事务的7种传播行为
| 传播行为 | 说明 |
|---|---|
| PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常⻅的选择 |
| PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
| PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
| PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
| PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
| PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
| PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则 执行与PROPAGATION_REQUIRED类似的操作。 |
springmvc工作流程
- 第一步:用户发送请求至前端控制器DispatcherServlet
- 第二步:DispatcherServlet收到请求调用HandlerMapping处理器映射器
- 第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),生成处理器对象及处理器拦截 器(如果 有则生成)一并返回DispatcherServlet
- 第四步:DispatcherServlet调用HandlerAdapter处理器适配器去调用Handler
- 第五步:处理器适配器执行Handler
- 第六步:Handler执行完成给处理器适配器返回ModelAndView
- 第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的一个 底层对 象,包括 Model 和 View
- 第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名来解析真正的视图。
- 第九步:视图解析器向前端控制器返回View
- 第十步:前端控制器进行视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
- 第十一步:前端控制器向用户响应结果
spring mvc 九大组件
- HandlerMapping(处理器映射器) HandlerMapping 是用来查找 Handler 的,也就是处理器,具体的表现形式可以是类,也可以是 方法。比如,标注了@RequestMapping的每个方法都可以看成是一个Handler。Handler负责具 体实际的请求处理,在请求到达后,HandlerMapping 的作用便是找到请求相应的处理器 Handler 和 Interceptor.
- HandlerAdapter(处理器适配器) HandlerAdapter 是一个适配器。因为 Spring MVC 中 Handler 可以是任意形式的,只要能处理请 求即可。但是把请求交给 Servlet 的时候,由于 Servlet 的方法结构都是 doService(HttpServletRequest req,HttpServletResponse resp)形式的,要让固定的 Servlet 处理 方法调用 Handler 来进行处理,便是 HandlerAdapter 的职责。
- HandlerExceptionResolver
- HandlerExceptionResolver 用于处理 Handler 产生的异常情况。它的作用是根据异常设置 ModelAndView,之后交给渲染方法进行渲染,渲染方法会将 ModelAndView 渲染成⻚面。 ViewResolver
- ViewResolver即视图解析器, 用于将String类型的视图名和Locale解析为View类型的视图,只有一 个resolveViewName()方法。从方法的定义可以看出,Controller层返回的String类型视图名 viewName 最终会在这里被解析成为View。View是用来渲染⻚面的,也就是说,它会将程序返回 的参数和数据填入模板中,生成html文件。 ViewResolver 在这个过程主要完成两件事情: ViewResolver 找到渲染所用的模板(第一件大事)和所用的技术(第二件大事,其实也就是找到 视图的类型,如JSP)并填入参数。默认情况下,Spring MVC会自动为我们配置一个 InternalResourceViewResolver,是针对 JSP 类型视图的。
- RequestToViewNameTranslator RequestToViewNameTranslator 组件的作用是从请求中获取 ViewName.因为 ViewResolver 根据 ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置 ViewName, 便要通过这个组件从请求中查找 ViewName。
- LocaleResolver ViewResolver 组件的 resolveViewName 方法需要两个参数,一个是视图名,一个是 Locale。 LocaleResolver 用于从请求中解析出 Locale,比如中国 Locale 是 zh-CN,用来表示一个区域。这 个组件也是 i18n 的基础。
- ThemeResolver ThemeResolver 组件是用来解析主题的。主题是样式、图片及它们所形成的显示效果的集合。 Spring MVC 中一套主题对应一个 properties文件,里面存放着与当前主题相关的所有资源,如图 片、CSS样式等。创建主题非常简单,只需准备好资源,然后新建一个“主题名.properties”并将资 源设置进去,放在classpath下,之后便可以在⻚面中使用了。 SpringMVC中与主题相关的类有 ThemeResolver、ThemeSource和Theme。ThemeResolver负责从请求中解析出主题名, ThemeSource根据主题名找到具体的主题,其抽象也就是Theme,可以通过Theme来获取主题和 具体的资源。
- MultipartResolver MultipartResolver 用于上传请求,通过将普通的请求包装成 MultipartHttpServletRequest 来实 现。MultipartHttpServletRequest 可以通过 getFile() 方法 直接获得文件。如果上传多个文件,还 可以调用 getFileMap()方法得到Map<FileName,File>这样的结构,MultipartResolver 的作用就 是封装普通的请求,使其拥有文件上传的功能。
- FlashMapManager FlashMap 用于重定向时的参数传递,比如在处理用户订单时候,为了避免重复提交,可以处理完 post请求之后重定向到一个get请求,这个get请求可以用来显示订单详情之类的信息。这样做虽然 可以规避用户重新提交订单的问题,但是在这个⻚面上要显示订单的信息,这些数据从哪里来获得 呢?因为重定向时么有传递参数这一功能的,如果不想把参数写进URL(不推荐),那么就可以通 过FlashMap来传递。只需要在重定向之前将要传递的数据写入请求(可以通过 ServletRequestAttributes.getRequest()方法获得)的属性OUTPUT_FLASH_MAP_ATTRIBUTE 中,这样在重定向之后的Handler中Spring就会自动将其设置到Model中,在显示订单信息的⻚面 上就可以直接从Model中获取数据。FlashMapManager 就是用来管理 FalshMap 的。
springboot
约定大于配置
Spring Boot 是所有基于 Spring 开发的项目的起点。Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件。
- 起步依赖: 起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
- 自动配置 简单、快速、方便地搭建项目;对主流开发框架的无配置集成;极大提高了开发、部署效率
- 自定义starter机制
类加载机制
- 双亲委派机制
tomcat配置调优:
调整tomcat线程池:Connector连接器使用线程池
调整tomcat的连接器:
- 调整tomcat/conf/server.xml 中关于链接器的配置可以提升应用服务器的性能。
- maxConnections:最大连接数, 当到达该值后,服务器接收但不会处理更多的请求, 额外的请 求将会阻塞直到连接数低于maxConnections 。 可通过ulimit -a 查看服务器 限制。对于CPU要求更高(计算密集型)时,建议不要配置过大 ; 对于CPU要求 不是特别高时,建议配置在2000左右(受服务器性能影响)。 当然这个需要服务器硬件的支持
- maxThreads:最大线程数,需要根据服务器的硬件情况,进行一个合理的设置
- acceptCount:最大排队等待数 当服务器接收的请求数量到达maxConnections ,此时 Tomcat会将后面的请求,存放在任务队列中进行排序, acceptCount指的 就是任务队列中排队等待的请求数 。 一台Tomcat的最大的请求处理数量, 是maxConnections+acceptCount
禁用AJP连接器
调整IO模式
Tomcat8之前的版本默认使用BIO(阻塞式IO),对于每一个请求都要创建一个线程来处理,不适 合高并发;Tomcat8以后的版本默认使用NIO模式(非阻塞式IO) 当Tomcat并发性能有较高要求或者出现瓶颈时,我们可以尝试使用APR模式,APR(Apache Portable Runtime)是从操作系统级别解决异步IO问题, 使用时需要在操作系统上安装APR和Native(因为APR 原理是使用使用JNI技术调用操作系统底层的IO接口)
动静分离
可以使用Nginx+Tomcat相结合的部署方案,Nginx负责静态资源访问,Tomcat负责Jsp等动态资 源访问处理(因为Tomcat不擅⻓处理静态资源)。
Nginx
Nginx是一个高性能的http和反向代理web服务器,同时提供IMAP/POP3/SMTP服务,核心特点占有内存少,并发能力强
Nginx应用场景
- Http服务器(web服务器) 性能非常高,非常注重效率,能够经受高负载的考验。 支持50000个并发连接数,不仅如此,CPU和内存的占用也非常的低,10000个没有活动的连 接才占用2.5M的内存
- 反向代理服务器
- 正向代理 在浏览器中配置代理服务器的相关信息,通过代理服务器访问目标网站,代理服务器收 到目标网站的响应之后,会把响应信息返回给我们自己的浏览器客户端
- 反向代理 浏览器客户端发送请求到反向代理服务器(比如Nginx),由反向代理服务器选择原始 服务器提供服务获取结果响应,最终再返回给客户端浏览器
- 负载均衡服务器 负载均衡,当一个请求到来的时候(结合上图),Nginx反向代理服务器根据请求去找到一个 原始服务器来处理当前请求,那么这叫做反向代理。 那么,如果目标服务器有多台(比如上 图中的tomcat1,tomcat2,tomcat3...),找哪一个目标服务器来处理当前请求呢, 这样一 个寻找确定的过程就叫做负载均衡。 负载均衡就是为了解决高负载的问题。
- 动静分离 静态资源放在nginx上
Nginx的特点
- 跨平台:Nginx可以在大多数类unix操作系统上编译运行,而且也有windows版本
- Nginx的上手非常容易,配置也比较简单
- 高并发,性能好
- 稳定性也特别好,宕机概率很低
Nginx核心配置文件
Nginx的核心配置文件conf/nginx.conf包含三块内容:全局块、events块、http块
- 全局块:
从配置文件开始到events块之间的内容,此处的配置影响nginx服务器整体的运行, 比如worker进 程的数量、错误日志的位置等
分布式集群架构场景化解决方案
- 分布式一定是集群,但是集群不一定是分布式 集群是多个实例(复制)一起工作,分布式是将一个系统拆分后的多个实例
一致性hash算法
hash算法在安全加密领域MD5、SHA等加密算法,在数据存储和查找方面有hash表等
hash表的查询效率高不高取决于hash算法,hash算法能够让数据平均分布,既能够节省空间又能够提高查询效率
hash表的查找方法
- 顺序查找:循环来完成,原始,效率低
- 二分查找:排序后折半查找,相对顺序提高一些效率,不特别好
- 直接寻址法:直接把数据和数组的下标绑定到一起,查找的时候,直接array[n]就取出了数据,速度快,一次查找得到结果,缺点:浪费空间
- 开放寻址法: hash算法 除留余数法
- 拉链法:数据长度定义好,算好hash值,在数组元素存储位置放置链表
hash算法应用场景
- 请求的负载均衡(nginx的ip_hash策略)
- 分布式存储:(redis集群部署 redis的hash(key)%3 = index,根据余数锁定存储的服务器节点)
一致性hash算法思路
hash环:但是会有数据(请求)倾斜问题,,引入了虚拟节点机制:对每一个服务节点计算多个hash,每个计算结果位置都放置一个此服务节点,称为虚拟节点 比如ip加编号来实现
集群时钟同步问题
思路
- 分布式集群中各个服务器节点都可以链接互联网
#使用 ntpdate 网络时间同步命令
ntpdate -u ntp.api.bz #从一个时间服务器同步时间
windows有计划任务 Linux也有定时任务,crond,可以使用linux的定时任务,每隔10分钟执行一次ntpdate命令
- 分布式集群中某一服务器节点可以访问互联网或所有节点都不能够访问互联网 设置集群中一个服务节点a为时间服务器,整个集群时间从这台服务器同步
- 先设置好a服务的时间
- 把a配置为时间服务器(修改/etc/ntp.conf文件)
# 1、如果有 restrict default ignore,注释掉它
# 2、添加如下几行内容
# 放开局域网同步功能,172.17.0.0是你的局域网网段
restrict 172.17.0.0 mask 255.255.255.0 nomodify notrap
server 127.127.1.0 # local clock
fudge 127.127.1.0 stratum 10
# 3、重启生效并配置ntpd服务开机自启动
service ntpd restart
chkconfig ntpd on
- 集群中其他节点就可以从A服务器同步时间了
分布式id解决方案
分布式集群环境下的全局唯一ID
- UUID
UUID 是指Universally Unique Identifier,翻译为中文是通用唯一识别码 产生重复 UUID 并造成错误的情况非常低,是故大可不必考虑此问题。 Java中得到一个UUID,可以使用java.util包提供的方法
- 独立数据库的自增ID
- SnowFlake雪花算法(可以用,推荐)
- 借助Redis的Incr命令获取全局唯一ID(推荐)
分布式调度问题
分布式调度-》 在分布式集群环境下的定时任务 Elastic-job
定时任务的场景
定时任务形式:每隔一定时间/特定某一时刻执行 例如:
- 订单审核、出库
- 订单超时自动取消、支付退款
- 礼券同步、生成、发放作业
- 物流信息推送、抓取作业、退换货处理作业
- 数据积压监控、日志监控、服务可用性探测作业
- 定时备份数据
- 金融系统每天的定时结算
- 数据归档、清理作业
- 报表、离线数据分析作业
什么是分布式任务调度?有两层含义
- 1)运行在分布式集群环境下的调度任务(同一个定时任务程序部署多份,只应该有一个定时任务在执 行)
- 2)分布式调度—>定时任务的分布式—>定时任务的拆分(即为把一个大的作业任务拆分为多个小的作 业任务,同时执行)
定时任务和消息队列的区别
共同点
- 异步处理 比如注册、下单事件
- 应用解耦
不管定时任务作业还是MQ都可以作为两个应用之间的⻮轮实现应用解耦,这个⻮轮可以中转 数据,当然单体服务不需要考虑这些,服务拆分的时候往往都会考虑
- 流量削峰
双十一的时候,任务作业和MQ都可以用来扛流量,后端系统根据服务能力定时处理订单或者 从MQ抓取订单抓取到一个订单到来事件的话触发处理,对于前端用户来说看到的结果是已经 下单成功了,下单是不受任何影响的 本质不同
- 定时任务作业是时间驱动,而MQ是事件驱动;
- 时间驱动是不可代替的,比如金融系统每日的利息结算,不是说利息来一条(利息到来事件)就算 一下,而往往是通过定时任务批量计算;
- 所以,定时任务作业更倾向于批处理,MQ倾向于逐条处理;
分布式调度框架Elastic-Job
Elastic-Job是当当网开源的一个分布式调度解决方案,基于Quartz二次开发的,由两个相互独立的子项 目Elastic-Job-Lite和Elastic-Job-Cloud组成。我们要学习的是 Elastic-Job-Lite,它定位为轻量级无中心 化解决方案,使用Jar包的形式提供分布式任务的协调服务,而Elastic-Job-Cloud子项目需要结合Mesos 以及Docker在云环境下使用。
Elastic-Job的github地址:github.com/elasticjob
主要功能介绍
- 分布式调度协调 在分布式环境中,任务能够按指定的调度策略执行,并且能够避免同一任务多实例重复执行
- 丰富的调度策略 基于成熟的定时任务作业框架Quartz cron表达式执行定时任务
- 弹性扩容缩容 当集群中增加某一个实例,它应当也能够被选举并执行任务;当集群减少一个实例 时,它所执行的任务能被转移到别的实例来执行。
- 失效转移 某实例在任务执行失败后,会被转移到其他实例执行
- 错过执行作业重触发 若因某种原因导致作业错过执行,自动记录错过执行的作业,并在上次作业 完成后自动触发。
- 支持并行调度 支持任务分片,任务分片是指将一个任务分为多个小任务项在多个实例同时执行。
- 作业分片一致性 当任务被分片后,保证同一分片在分布式环境中仅一个执行实例。
Elastic-Job依赖于Zookeeper进行分布式协调,所以需要安装Zookeeper软件(3.4.6版本以上)
Session共享问题
Session问题原因分析
出现这个问题的原因,从根本上来说是因为Http协议是无状态的协议。客户端和服务端在某次会话中产 生的数据不会被保留下来, 所以第二次请求服务端无法认识到你曾经来过, Http为什么要设计为无状态 协议?早期都是静态⻚面无所谓有无状态,后来有动态的内容更丰富, 就需要有状态,出现了两种用于 保持Http状态的技术,那就是Cookie和Session。而出现上述不停让登录的问题,分析如下图:
场景:nginx默认轮询策略
解决Session一致性的方案
- Nginx的 IP_Hash 策略(可以使用) 同一个客户端IP的请求都会被路由到同一个目标服务器,也叫做会话粘滞
优点: - 配置简单,不入侵应用,不需要额外修改代码 缺点: - 服务器重启Session丢失 - 存在单点负载高的⻛险 - 单点故障问题
-
Session复制(不推荐) 也即,多个tomcat之间通过修改配置文件,达到Session之间的复制
优点:- 不入侵应用
- 便于服务器水平扩展 能适应各种负载均衡策略 服务器重启或者宕机不会造成Session丢失 缺点:
- 性能低
- 内存消耗
- 不能存储太多数据,否则数据越多越影响性能
- 延迟性
-
Session共享,Session集中存储redis(推荐)
优点:-
能适应各种负载均衡策略 服务器重启或者宕机不会造成Session丢失 扩展能力强
-
适合大集群数量使用 缺点:
-
对应用有入侵,引入了和Redis的交互代码
-