Java进阶 - 易错知识点整理(待更新)

Java进阶 - 易错知识点整理(待更新)

Note:这里根据 CSDN Java进阶技能树 整理的Java易错题(不带),并摘录了博主"哪吒"Java面试题整理(带)、牛客网的Java面试题库(带)。

Java基础知识点 参考 Java基础 - 知识点整理(待更新)
Java进阶2 - 中间件章节 参考 Java进阶2 - 易错知识点整理(待更新)

1、JavaEE

  • 【问】J2EE和Java Web的区别,参考J2EE与javaweb的区别
    Note

    • J2EEJava的企业应用开发,涵盖了B/S和C/S(Server服务器),注重的是结构和框架,我们所熟知的struts2、hibernate和spring即ssh就是j2ee的一些基本框架
    • JavaWeb是指Java在B/S方面的开发,做的是网络应用;
    • j2EEJavaWeb说的是两个层面:
      • javaee是指Java的企业级应用,可以说是一个规范,包含servlet,jsp,jndi,jms,mvc框架,对象持久化等等组件
      • javaweb是指用Java技术来解决相关web互联网领域的技术总和
    • 可以说 J2EE 是一个更大的范畴,包含了 Java Web 开发所需的技术,而 Java Web 则是 J2EE 中的一个子领域,专注于 Web 应用开发
  • 【问】tomcat的监听器是什么?(使用监听器可以在Tomcat容器生命周期内监听和响应事件,实现一些自定义的业务逻辑,例如在Web应用程序启动时加载一些配置信息,或者在HTTP会话创建时记录日志),参考ChatGPT

    Note

    • Tomcat的监听器(Listener)是一种Web应用程序组件,可以用来监听和响应Tomcat容器的事件。Tomcat提供了多种类型的监听器,可以通过在Web应用程序的部署描述符(web.xml)文件中配置来使用它们。

      以下是Tomcat中常见的监听器类型:

      1. ServletContextListener:用于监听Web应用程序的启动和停止事件。

      2. HttpSessionListener:用于监听HTTP会话的创建和销毁事件。

      3. ServletRequestListener:用于监听HTTP请求的创建和销毁事件。

      4. ServletContextAttributeListener:用于监听ServletContext范围内属性的添加、删除和更改事件。

      5. HttpSessionAttributeListener:用于监听HttpSession范围内属性的添加、删除和更改事件。

      6. ServletRequestAttributeListener:用于监听HttpServletRequest范围内属性的添加、删除和更改事件。

      7. HttpSessionBindingListener:用于监听对象是否被绑定到或从HttpSession解绑。

      8. ServletRequestBindingListener:用于监听对象是否被绑定到或从HttpServletRequest解绑。

    使用监听器可以在Tomcat容器生命周期内监听和响应事件,实现一些自定义的业务逻辑,例如在Web应用程序启动时加载一些配置信息,或者在HTTP会话创建时记录日志。

  • 【问】Servlet的作用是什么?(Servlet是 Java Web 开发中的重要组件之一,它主要用于接收 HTTP 请求并作出相应的响应:接收请求,处理请求,生成响应,维护会话,实现过滤器),参考ChatGPT

    Note

    • Servlet是 Java Web 开发中的重要组件之一,它主要用于接收 HTTP 请求并作出相应的响应。Servlet 的作用可以概括为以下几点:

      1. 接收请求:Servlet 通过实现 HttpServlet 类中的 doGet()doPost()等方法来处理浏览器发送的请求。

      2. 处理请求Servlet可以对请求进行处理,例如读取请求参数、验证用户身份、调用业务逻辑处理等。

      3. 生成响应Servlet可以生成 HTML、XML、JSON 等格式的响应内容,也可以将请求转发或重定向给其他 Servlet、JSP 等组件来生成响应。

      4. 维护会话Servlet可以通过 HttpSession 对象来维护用户会话状态,以提供个性化的服务。

      5. 实现过滤器Servlet还可以实现 Filter 接口来拦截请求和响应,进行预处理和后处理,如登录验证、编码转换、日志记录等。

      总之,Servlet是 Java Web 应用程序中的核心组件之一,通过 Servlet 可以处理用户请求并生成响应,实现了 Web 应用程序的基本功能。

  • 【问】jsp 和 servlet 有什么区别?,参考JSP简单介绍
    Note

    • servlet是服务器端的java程序,是客户端和服务器端的中间层;
    • JSP(Java server pages) 是一种动态网页开发技术,它使JSP标签在HTML网页中插入Java代码,其本质是一个java servlet
    • JSP需要通过web容器(比如tomcatJSP代码编译成JVM能够识别的Java类(Servlet
    • JSP有内置对象,而Servlet没有内置对象。
    • Springboot开发中,也可以使用Thymeleaf模板引擎来显示页面,和JSP不同的是,Thymeleaf是一个纯的html页面,能够像静态的HTML那样以原始的方式编写和预览,并且能够在运行时渲染动态模型数据,而JSP只能在web容器中渲染后运行。参考JSP和Thymeleaf
      JSPThymeleaf的设计是基于模型-视图-控制器(MVC)模式,它们的缺点是前后端耦合度太高,不利于前后端应用的分离。
  • 【问】说一下 jsp 的 4 种作用域?,参考jsp九大内置对象、四种作用域、跳转方式
    Note:jsp有四种作用域:

    • page -> 页面级别,显然只有在一个jsp页面内可用。
    • request -> 请求级别,服务器跳转,一次请求之后消失
    • session -> 会话级别,客户端跳转(服务器跳转),与浏览器有关,ie是在重新打开ie时才会不同(刷新浏览器session域的数据会消失)。
    • application = 应用级别,当重启服务器时才会消失
  • 【问】jsp 有哪些内置对象?作用分别是什么?,参考jsp九大内置对象、四种作用域、跳转方式
    Note

    内置对象内容类型作用域
    request请求对象(封装请求内容)类型 javax.servlet.ServletRequest作用域 Request
    response响应对象类型 javax.servlet.ServletResponse作用域 page
    pageContext页面上下文对象类型 javax.servlet.jsp.PageContext作用域 page
    session会话对象类型 javax.servlet.http.HttpSession作用域 session
    application应用程序对象类型 avax.servlet.ServletContext作用域 application
    out输出对象类型 javax.servlet.jsp.JspWriter作用域 page
    config配置对象类型 javax.servlet.ServletConfig作用域 page
    page页面对象类型 javax.lang.Object作用域 page
    exception异常对象类型 javax.lang.Throwable作用域 page
    • 通过pageContext可以获取JSP页面的out,request,response,session和application对象;
    • session可以通过ThreadLocal来管理;
    • application实现了用户间数据的共享,可存放全局变量,它开始于服务器启动,直到服务器关闭;
    • page即JSP本身,config可取得服务器的配置信息。
  • 【问】说一下 session 的工作原理?(客户端登录会在服务器端生成session,服务端返回数据时将sessionid通过cookies返回给浏览器,用户下次登录时取出cookiessessionid,到服务器端去匹配,如果不存在则跳转回登录页面)

  • 【问】session 和 cookie 有什么区别?(位置/容量/类型/有效期/安全)跨域&Cookie&Session
    Note

    • 1)存储位置不同:cookie在客户端浏览器;session在服务器;
    • 2)存储容量不同:cookie<=4K,一个站点最多保留20个cookie;session没有上线,出于对服务器的保护,session内不可存过多东西,并且要设置session删除机制
    • 3)存储方式不同:cookie只能保存ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据;session中能存储任何类型的数据,包括并不局限于Stringintegerlistmap等;
    • 4)隐私策略不同:cookie对客户端是可见的,不安全;session存储在服务器上,安全;
    • 5)有效期不同:开发可以通过设置cookie的属性,达到使cookie长期有效的效果;
      session依赖于名为JESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session达不到长期有效的效果;
    • 6)跨域支持上不同:cookie支持跨域;session不支持跨域
  • 【问】如果客户端禁止 cookie,此时session 还能用吗?(虽然无法将sessionid通过cookie传给浏览器,但可以通过url重写的方式,将sessionId追加到url尾)

  • 【问】cookie、session、token的区别?,参考Cookie、Session和Token的区别与联系加密,签名,token解释及场景分析摘要算法和加密算法区别
    Note

    • cookiesession混合使用的,session的实现常常依赖于cookie机制,其中cookiesession的区别见上两问解析。

    • session在集群中存在的问题:在分布式系统中,客户端通过cookies中的sessionid,到服务器端去匹配是否有这个人sessionid,由于无法有效统一管理session(不同服务器之间复制所有用户的session开销会很大,单点存储session又不高可用),因此有人提出没必要把所有的sessionId放在一个服务器中,而是通过加密解密的方式来进行身份验证,解密的密钥只有服务器端有,就不存在安全问题,因此就有了token。参考cookie、session与token的真正区别

    • CA数字签名是在服务器端(某网站)和服务器端(CA机构)来发送的,某网站发送公钥给CA机构CA机构使用私钥对其公钥进行加密,并将消息发送给用户,用户可以使用CA提供的公钥来校验网站CA签名的合法性。

    • 摘要算法和加密算法是不同的

      • 摘要算法:用于生成签名,而签名主要用于身份验证。签名的生成方式通过密钥 + 时间 +消息内容 + 自定义算法(比如抽取消息内容中截取一些字符)来生成。
        摘要算法只能用于对数据的单项运算无法还原被摘要源数据,其特点为定长输出、雪崩效应(少量消息位的变化会引起信息摘要的许多位变化)。摘要算法有三个特性,一是不可逆,即无法从摘要算法的输出推出输入;二是唯一,即在同一种摘要算法下,不同的输入一定会产生不同的输出;三是输出结果长度固定。基于以上特性,摘要算法通常用来判断某个消息在传输过程中是否被改变,这里的改变包括恶意篡改和噪声。
        简单理解,配合密钥的摘要算法(参考token验证过程)将明文处理成密文,密文作为签名用于比较验证,无法处理成明文
      • 加密算法:加密是对数据进行机密性保护,过程是发送者对明文进行加密,接收者对密文进行解密的过程。
    • Token也是基于签名的,是在服务器端和客户端进行发送的,比如说服务器用HMAC-SHA256算法(信息摘要算法),加上一个密钥(加密算法), 对用户名的数据(通常对userId)做一个签名,并将“数据 + 签名”作为token保存在客户端的cookies中。
      在验证的时候,客户端通过cookies向服务端发送token,服务端会
      使用私钥对数据部分进行解密得到新的签名
      ,并与原token中的签名进行比较,如果相等则验证通过,如果不等说明数据已被篡改。参考加密,签名,token解释及场景分析cookie、session与token的真正区别

  • 【问】如何避免 sql 注入?,参考万能密码:‘or 1=1-- 实战SQL注入,秒破后台MyBatis 框架下 SQL 注入攻击的 3 种方式,真是防不胜防!
    Note

    • 一般没有进行SQL语句参数化的登录语句是这样的:
      	Select * From 用户表 Where UserName=xxx and Password=xxx
      
      如果对于上面的sql,Password如果输入xxx or 1=1就能够查询到数据
      	Select * From 用户表 Where UserName=xxx and Password=xxx or 1=1
      
    • mybatis通过#{}预编译可以防止sql注入,${}只是对sql进行拼接

2、网络基础

  • 【问】http 响应码 301 和 302 代表的是什么?有什么区别?(301302状态码都表示重定向,301表示永久性重定向(页面域名会发生修改),302表示临时性重定向(页面域名不修改,但内容会修改)),参考永久性重定向&临时性重定向

  • 【问】forward 和 redirect 的区别?,参考页面跳转的两种方式(转发和重定向)区别及应用场景分析重定向和转发的区别
    Note

    • 相同点:两者都会实现页面跳转;
    • 不同点
      • url是否改变redirect改变地址栏的url内容;而forward 不会改变url 内容;
      • 行为不同forward是服务器内部行为,而redirect 是客户端行为
      • 请求次数不同forward一次请求过程,redirect 二次请求过程,因此在效率上转发高于重定向
      • 语法不同:转发是request.getRequestDispatcher("xxx.jsp或者servlet").forward(request,response);,重定向是response.sendRedirect("xxx.jsp或者servlet")
      • 安全性不同forward是在服务器内部实现跳转,客户端不知道跳转路径,相对来说比较安全;而在redirect 中,客户端参与到跳转流程,给攻击者带来了攻击入口,受威胁的可能性较大。
    • 适合场景
      • forward转发
        • 要保持request域的数据时使用转发;
        • 携带较多参数则选择服务器内转发;
      • redirect 重定向
        • 对于竭力避免表单重复提交的场景下,即对数据进行修改、删除、添加操作的时候选择重定向方式;
        • 访问外站资源的时候用重定向。
  • 【问】简述 tcp 和 udp的区别?(TCP可靠有连接,保证数据正确性、顺序性;UDP不可靠无连接,可能丢数据),参考【计算机网络】传输层知识点总结

  • 【问】tcp 在建立连接时为什么要三次握手,两次不行吗?为什么?(3次握手才能完成ack同步,才能确定服务端已经准备开启连接),参考【计算机网络】传输层知识点总结
    Note

    • 主要原因是通过三次握手避免打开历史连接请求。如果客户端发送的连接请求在网络中滞留太久,客户端等待一个超时重传时间后,就会重新请求连接。有三次握手,服务器虽然收到了两个连接请求,并回发了两个请求确认,但客户端不会回应前一个请求确认,就不会打开这个失效的链接请求【具体就是通过服务端返回的 ack 来验证的,如果客户端发现不是自己期望的 ack 号,就会发起 RST 报文中止这个失效链接】。
    • 通过三次握手同步双方的初始序列号
  • 【问】说一下 tcp 粘包是怎么产生的?(原因是一条消息的边界不确定),可参考什么是TCP粘包?TCP粘包现象分析及处理方式
    Note

    • TCP面向流的的传输协议,存在粘包问题,发送端可以一次发送不定长度的数据,而接收端也可以一次提取不定长度的数据。即这种传输方式是无保护消息边界的。
      UDP面向数据报的传输协议,不存在粘包问题发送的UDP报文都被接收端视为一条消息,若消息太长被分片,UDP协议也会完成组合后才呈现在内核缓冲区;且UDP报文有消息头,对于接收端来说,易于区分处理。即这种传输方式是有保护消息边界的。
    • TCP粘包的原因
      • 应用层传到 TCP 协议的数据,不是以消息报为单位向目的主机发送,而是以字节流的方式发送到下游,这些数据可能被切割和组装成各种数据包,接收端收到这些数据包后没有正确还原原来的消息,因此出现粘包现象。
      • 粘包出现的根本原因不确定消息的边界。接收端在面对"无边无际"的二进制流的时候,根本不知道收了多少 01 才算一个消息
    • 粘包的处理方法:只要在发送端每次发送消息的时候给消息带上识别消息边界的信息,接收端就可以根据这些信息识别出消息的边界,从而区分出每个消息。常见的方法有:
      • 加入特殊标志:头标志和尾标志
      • 加入消息长度信息
  • 【问】OSI 的七层模型都有哪些?(应用层,表示层,会话层,传输层,网络层,链路层,物理层)

  • 【问】get 和 post 请求有哪些区别?(参数位置/长度/安全性/请求次数/编码)
    Note

    • get请求参数是连接在url后面的;而post请求参数是存放在requestbody内的;
    • get请求因为浏览器url长度有限制,所以参数个数有限制;而post请求参数个数没有限制
    • 因为get请求参数暴露在url上;所以安全方面postget更加安全;
    • get请求只能进行url编码post请求可以支持多种编码方式
    • get请求参数会保存在浏览器历史记录内;post请求并不会;
    • get请求浏览器会主动cachepost并不会,除非主动设置;
    • get请求产生1个tcp数据包post请求产生2个tcp数据包
    • 浏览器在发送get请求时会将headerdata一起发送给服务器,服务器返回200状态码;而在发送post请求时,会先将header发送给服务器,服务器返回100,之后再将data发送给服务器,服务器返回200 状态码;
  • 【问】域名解析全过程?(HOST表或缓存 -> 本地域名服务器 -> 迭代查询根域名服务器(先返回顶级域名服务器,接着返回权限域名服务器,接着返回IP地址))
    Note:域名解析过程:(假设域名为www.baidu.com

    • 1)先在HOSTS表或者本地主存中的DNS缓存中查询
    • 2)如果没有,再通过递归查询查找本地DNS服务器
    • 3)如果还没找到,本地DNS服务器通过迭代查询查找根域名服务器,根域名服务器返回.com域的顶级域名服务器
    • 4)接着本地域名服务器向.com顶级域名服务器发出请求,返回baidu.com权限域名服务器
    • 5)本地域名服务器向baidu.com权限域名服务器发送请求,得到了www.baidu.com的IP地址。
  • 【问】Cookie设置域名domain与跨域的问题说明?(协议、域名和端口号都相同才不存在跨域;预检请求方式判断是否同源),参考面试被问跨域问题,怎么破?
    Note

    • 一个标准的URL格式如下:协议://主机名.域名.域名后缀或IP地址(:端口号)/目录/文件名
      比如http://www.dailynews.com.cn/channel/welcome.htm中,www为主机名,dailynews为本地域名,com为组织域名,cn为最高层域名(表示中国);
      其中dailynews.com.cn父域名包括com.cncn子域名包括www.dailynews.com.cn
    • 什么是跨域问题
      • 跨域是指一个域(网站)下的文档或脚本试图去请求另一个域(网站)下的资源。
      • 跨域问题是浏览器为了防御CSRF攻击(Cross-site request forgery 跨站请求伪造)发生,避免恶意攻击而带来的风险而采取的 同源策略限制。当一个页面中使用XMLHTTPRequestXHR请求,也既AJAX请求)对象发送HTTP请求时,必须保证当前页面和请求的对象是同源的,即协议、域名和端口号要完全一致,否则浏览器就会阻止此跨域请求返回的数据。
        比如:
        • http://www.a.comhttps://www.a.com 是不同源的,它们协议不同(跨域)
        • http://www.a.comhttp://www.b.com 是不同源的,它们域名不同(跨域)
        • http://www.a.com/test1.jshttp://www.a.com/test2.js 是同源的(不跨域
      • 浏览器如何判断当前网页的域名与要访问的对象是否是同源的?主要通过预检请求方式询问服务器
        • 发生跨域条件时候,浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
    • Cookie无法设置除当前域名或者其父域名(向右表示上级)之外的其他domain,这是因为浏览器出于对cookie的保护造成的(同源策略),也就是cookie无法跨域设置。
      cookiesdomain设置规则如下:当前域名只能设置当前域名以及当前域名的父(上级)域名,不能设置当前域名的子(下级)域名。
      假设现在有三个域名如下3个域名,一个顶级域名zydya.com、一个二级域名blog.zyday.com和一个三级域名one.blog.zyday.com
      • zyday.com域名下设置cookie,其他域名下是否会取到cookie;
      • blog.zyday.com域名下设置cookie,其他域名下是否会取到cookie;
      • one.blog.zyday.com域名下设置cookie,其他域名下是否会取到cookie;
  • 【问】如何实现跨域?(CORS(前后端设置)/Nginx反向代理/WebSocket),参考什么是跨域?跨域解决方法面试被问跨域问题,怎么破?
    Note

    • 跨域主要涉及4个响应头
      • Access-Control-Allow-Origin 用于设置允许跨域请求源地址 (预检请求和正式请求在跨域时候都会验证)
      • Access-Control-Allow-Headers 跨域允许携带的特殊头信息字段 (只在预检请求验证)
      • Access-Control-Allow-Methods 跨域允许的请求方法或者说HTTP动词 (只在预检请求验证)
      • Access-Control-Allow-Credentials 是否允许跨域使用cookies,如果要跨域使用cookies,可以添加上此请求响应头,值设为true;
    • 方案1CORS跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法
      • 1)普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
      • 2)cookie跨域请求:前后端都需要进行设置
        • 前端设置:根据xhr.withCredentials字段判断是否带有cookie,如果是vue则设置为:Vue.http.options.credentials = true;如果是axios则设置为:axios.defaults.withCredentials = true
        • 服务器端设置:服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
          java后台实现:
          // 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
          response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 
           
          // 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
          response.setHeader("Access-Control-Allow-Credentials", "true"); 
           
          // 提示OPTIONS预检时,后端需要设置的两个常用自定义头
          response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
          
    • 方案2Nginx反向代理
      • 使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session不需要修改任何代码,并且不会影响服务器性能。
    • 方案3WebSocket持久化协议
      • WebsocketHTML5 的一个持久化的协议,它实现了浏览器 与 服务器的全双工通信,同时也是跨域的一种解决方案。WebSocketHTTP 都是应用层协议,都基于 TCP 协议。
      • Websocket只需要要做一个握手的动作,在建立连接之后,双方可以在任意时刻,相互推送信息。
    • 方案4JSONP(JSON协议)
      • JSONP服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求
      • 核心思想:网页通过添加一个<script>元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。
        <script src="http://test.com/data.php?callback=dosomething"></script>
        // 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
        
        // 处理服务器返回回调函数的数据
        <script type="text/javascript">
            function dosomething(res){
                // 处理获得的数据
                console.log(res.data)
            }
        </script>
        
  • 【问】websocket应用的是哪个协议?(HTML5中的持久化协议,属于应用层协议,见上一问)

  • 【问】说一下 JSONP 实现原理?(见上2问)

  • 【问】什么是 XSS 攻击,如何避免?(和sql注入类似),参考XSS攻击
    Note

    • XSS(Cross Site Scripting,避免与CSS冲突缩写为XSS)跨站脚本攻击:利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,在用户浏览器上,在渲染DOM树的时候,执行了不可预期的JS脚本,从而发生了安全问题比如使用户加载并执行攻击者恶意制造的网页程序;
    • 基于HTML的XSS避免方法不用innerHtml,用innerText对用户的输入进行过滤,如对& < > " ' /等进行转义;
  • 【问】什么是 CSRF 攻击,如何避免?(用户在银行页面上时,CSRF主要是通过伪造成广告,当用户点击之后,该url会自动携带用户的cookies去访问银行页面;避免方式是:验证 HTTP Referer 字段 或 在请求地址中添加 token 并验证),参考CSRF攻击与防御(写得非常好)

3、Mysql

更多内容参考【软考】软件设计师知识点整理中《数据库系统》章节Mysql初阶易错知识点整理(待更新)MySQL进阶 - 易错知识点整理(待更新)

  • 【问】数据库的三范式是什么?,参考数据库的规范理论
    Note

    • 1NF:表中属性不可分,但表中非主属性存在部分函数依赖;不满足第一范式的数据库模式不能称为关系数据库
    • 2NF:满足1NF,非主属性 完全函数依赖 于候选码;表中非主属性存在传递依赖;
    • 3NF:符合2NF,并且消除了非主属性对于候选码的 传递函数依赖。
    • 还有BCNF(在3NF基础上消除主属性对码的部分和传递函数依赖),4NF(在BCNF基础上消除非平凡且非函数依赖的多值依赖
    • 目的:尽量消除插入、删除异常,修改复杂,数据冗余;
      基本思想:逐步消除数据依赖中不合适的部分
  • 【问】一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
    Note

    • 表类型是InnoDB
      • 如果不重启,InnoDB会在缓存中通过保存最大的ID(7),插入新数据时ID为8;
      • 如果重启,InnoDB保存最大的ID(7)丢失,插入新数据时ID为6
    • 表类型是MyIsAM:重启之后,最大ID也不会丢失,ID是8;主要原因是两类型的存储引擎不同
    • InnoDB 必须有主键(建议使用自增主键,不用UUID,自增主键索引查询效率高)、支持行级锁,事务和外键,有更好的并发能力;MyISAM不支持事务和外键,系统崩溃时很难恢复数据,因此建议使用InnoDB作为表类型。
  • 【问】如何获取当前数据库版本(mysql则select version();mysql -v

  • 【问】说一下 ACID 是什么?(原子性/一致性(实时/强一致)/隔离性(不可见)/持久性),参考Mysql四大属性 & 四个隔离级别是如何实现的

  • 【问】char 和 varchar 的区别是什么?(char固定长度,占用空间大但查找效率高;varchar变长),参考MySQL 中 varchar 和 char 区别

  • 【问】FLOAT和DOUBLE的区别是什么?(FLOAT最多存储8位十进制数,共4字节;DOUBLE最多存储18位十进制数,共8字节,参考老马资源)

  • 【问】mysql 的内连接、左连接、右连接有什么区别?,参考mysql 内连接、自然连接、外连接的区别
    Note

    • 自然连接(natural join)是一种特殊的等值连接,在无需设置条件下,两个表的相同属性列建立连接;
    • 内连接(inner join,inner可省略) 基本与自然连接相同,不同之处在于通过设置条件,内连接可以让两个表的不相同的属性列建立连接;
    • 左外连接(left outer join,outer可省略),通过设置条件建立两表连接,左表要舍弃的保留在结果集中,右表对应的列上填null;右外连接则相反。
  • 【问】mysql 索引是怎么实现的?(InnoDB存储引擎中,B+树是从二叉查找树,平衡二叉树,B树发展来的),参考MySQL的索引(普通索引、唯一索引,主键索引、组合索引、全文索引、空间索引)相关操作B+树真的不难图解 B+树的插入与删除操作
    Note

    • B+树基本概念:
      • B+树是平衡树(B树),如果每页可存放4条记录,扇出(fan out)为5,可以理解成5阶多叉树
      • 对于数据结构B+树来说,在插入关键字时,需要注意以下几点:
        • 插入的操作全部都在叶子结点上进行,且不能破坏关键字自小而大的顺序;
        • 由于 B+树中各结点中存储的关键字的个数有明确的范围,做插入操作可能会出现结点中关键字个数超过阶数的情况,此时需要将该结点进行 “分裂”;过程即为依次向上分裂
      • 对于mysqlB+非叶子节点上是不存储数据的,仅存储键值,而 B 树(平衡树)节点中不仅存储键值,也会存储数据。
        之所以这么做是因为在数据库中页的大小是固定的,InnoDB页的默认大小是 16KB。如果不存储数据,那么就会存储更多的键值,如果我们的 B+ 树一个节点可以存储 1000 个键值,那么 3 层 B+ 树可以存储 1000×1000×1000=10 亿个数据
        相较于BB+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。
      • InnoDBB+ 树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的。那么 B+ 树使得范围查找,排序查找,分组查找以及去重查找变得异常简单;InnoDBB+树索引是聚集索引,即数据和索引在一起;
      • MyISAM 中的 B+ 树索引实现与 InnoDB 中的略有不同。在 MyISAM 中,B+ 树索引的叶子节点并不存储数据,而是存储数据的文件地址,指向了数据行MyISAMB+树索引是非聚集索引,即数据和索引分离,在查询时先到内存中找到该索引,接着到磁盘中找到相应数据,较为耗时。
    • Hash索引和B+树索引的比较
      • Hash索引
        • 在查询速度上,如果是等值查询,Hash索引比B+树索引效率高,时间复杂度O(1)MysqlMemory存储引擎支持Hash索引;
        • 如果键值不是唯一(或存在Hash冲突),就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据,这时候复杂度会变成O(n)降低了Hash索引的查找效率
          Hash 索引通常不会用到重复值多的列上,比如列为性别、年龄的情况等;当然B+ tree索引也不适合这种离散型低的字段上。
      • B+树索引
        • Hash索引是无序的,如果是范围查询检索,这时候 Hash 索引就无法起到作用;
        • 1)Hash 索引只支持等值比较查询、无法支持范围查询检索B+tree索引的叶子节点形成有序链表,便于范围查询。
        • 2)Hash 索引无法做 like ‘xxx%’ 这样的部分模糊查询,因为需要对完整 key 做 Hash 计算,定位bucket。而 B+ tree 索引具有最左前缀匹配,可以进行部分模糊查询
        • 3)Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算B+tree索引的叶子节点形成有序链表,可用于排序。
        • 4)Hash 索引不支持多列联合索引,对于联合索引来说,Hash 索引在计算 Hash 值的时候是将索引键合并后再一起计算 Hash 值,不会针对每个索引单独计算 Hash 值。因此如果用到联合索引的一个或者几个索引时,联合索引无法被利用
        • 5)因为存在哈希碰撞问题,在有大量重复键值情况下,哈希索引的效率极低B+tree 所有查询都要找到叶子节点,性能稳定
    • 应用场景
      • 1)大多数场景下,都会有组合查询,范围查询、排序、分组、模糊查询等查询特征,Hash 索引无法满足要求,建议数据库使用B+树索引。
      • 2)在离散型高,数据基数大等值查询时候,Hash索引有优势
  • 【问】什么是聚簇索引和非聚簇索引?(数据和索引是否分离;聚簇索引包括整条数据,而非聚集索引包含的是数据行地址,搜索效率较低;参考上一问和下一问)

  • 【问】B+树在满足聚簇索引时,是否需要回表查询数据(聚簇索引包含整行数据,不需要回表查询,参考老马资源)
    Note

    • B+树的索引中,叶子节点可能存储了当前的key值,也可能存储了当前的key值以及整行的数据,这就是聚簇索引和非聚簇索引。
    • InnoDB中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则隐式的生成一个键来建立聚簇索引
    • 当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询
  • 【问】非聚簇索引一定会回表查询吗?(不一定,看要查询字段是否包含在索引中,比如行select age from employee where age < 20就不用回表,参考老马资源)

  • 【问】数据库为什么不用红黑树而用B+树?(B+树多叉,红黑树二叉)
    Note

    • 红黑树是二叉树,树高最高不会超过 2 ∗ l o g ( n ) 2*log(n) 2log(n),因此查找的时间复杂度为 O ( l o g ( n ) ) O(log(n)) O(log(n)),但是在数据量非常大时,需要访问节点数还是会比较多,而B+树是多叉的,可以有效减少磁盘IO次数;
  • 【问】说一下 mysql 的行锁和表锁?(行锁和表锁都是悲观锁,粒度不同),参考MySQL之行锁与表锁
    Note

    • 按照锁的性质可以把锁分为共享锁(S读锁)和排它锁(X写锁)
      • 若事务T给表/行加了S锁,则其他事务可以获取该表/行的S锁,但不能加X锁
      • 若事务T给表/行加了X锁,则其他事务对该表/行不能加S锁或X锁
    • innodb表锁:
      • 在执行insert、select、delete、update语句时,并不会对该表添加表级S锁或X锁
      • Innodb的表级锁一般不会用到,只会在特殊情况下,例如系统崩溃恢复时使用,在autocommit = 0innodbc_table_locks = 1的情况下,可通过 下面的语句手动获取表级别的S锁和X锁:
        lock tables t read:对表t加S锁
        lock tables t write:对表t加X锁
    • innodb行锁:
      • Record lock:Record Lock也称行锁,官方称之为 LOCK_REC_NOT_GAP,仅仅锁一条记录;行锁也有S锁和X锁之分,一条记录的S锁如果被获取,那么其他事务依然可以继续获取该记录的S锁,但是不可以获取X锁
      • Gap lock间隙锁,锁定一个范围,不包括记录本身;可以解决幻读问题。
        例如事务B想插入一个ID为4的记录,先定位到4的下一条记录的位置,此时定位到8,发现8上面有一个Gap Lock,那么事务B就会阻塞至8的Gap Lock释放之后,才可以将新记录插入进去。
      • Next-key lock:record+gap 锁定一个范围,包含记录本身
  • 【问】说一下乐观锁和悲观锁?(乐观锁即不加锁,通过带版本号的CAS算法 - 旧值A、修改值B、当前内存值V来实现;悲观锁通过行锁和表锁实现),可参考乐观锁之CAS算法MySQL之行锁与表锁

  • 【问】说一下数据库的事务隔离?(读提交/读未提交/可重复读/串行化),参考Mysql的四个隔离级别是如何实现的
    Note

    • 读未提交(RU:三个问题都不可以解决

      • 所有的读不加锁,读到的数据都是最新的数据,性能最好。
      • 所有的写加行级锁,写完释放。
    • 读已提交(RC使用MVVC技术实现):可以解决脏读;

      • 写操作:加行级锁。事务开始后,会在UNDO日志中写入修改记录,数据行中的隐藏列DATA_POLL_PTR存储指向该行的UNDO记录的指针。
      • 读操作:不加锁。在读取时,如果该行被其它事务锁定,则顺着隐藏列DATA_POLL_PTR指针,找到上一个有效的历史记录(有效的记录:该记录对当前事务可见,且DELETE_BIT=0)。
    • 可重复读(RR:可以解决脏读和不可重复读;

      • 写操作:加行级锁;读操作:不加锁;
      • RR读写操作和加锁逻辑和RC相同,都是使用MVVC(多版本并发控制) 技术实现
      • 不同点在于:行记录对于当前事务的可见性(可见性:即哪个版本的行记录对这个事务是可见的)。RC级别对数据的可见性是该数据的最新记录,RR级别对数据的可见性是事务开始时,该数据的记录。
    • 串行化:可以解决幻读(设置读锁(共享锁)和写锁);

    • 不可重复读侧重的是数据的修改(事务B多次对数据进行更新和提交,事务A每次读的数据都不一样),幻读侧重的是数据的新增或删除(表的数据发生增加或删除,影响事务A的操作结果)。解决不可重复读,只有锁住行,而解决幻读,需要锁住整张表

  • 【问】说一下 mysql 常用的引擎?(InnoDBMyIsAM,见第二问)

  • 【问】在mysql子查询中,inexists有什么区别(exists不一定比in快,但not exists都比not in快,参考老马资源),可参考MySQL使用IN、EXISTS、ANY、ALL关键字的子查询
    Note

    • mysql中的in语句是把外表和内表(子查询子表) 作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询
    • 只有子查询返回的结果列包含一个值时,比较运算符才适用。假如一个子查询返回的结果集是值的列表,这时比较运算符就必须用IN运算符代替。
      // 查询出研发部(dept为'dev')和人力资源部(dept为'hr')的员工列表
      select id, dept, name, post 
      from employee
      where dept in ('dev', 'hr');
      
    • EXISTS关键字时,内层查询语句不返回查询的记录。而是返回一个真假值。如果内层查询语句查询到满足条件的记录,就返回true,否则false
      //找出所有其所在部门没有助理(post 为 assistant)的员工信息,使用exists实现
      select id, name, dept
      from employee as o
      where not exists(select * from employee as i where o.dept = i.dept and post='assistant'); 
      
    • 1)如果查询的两个表大小相当,那么用inexists差别不大。
      2)如果两个表中一个较小,一个是大表,则子查询表大的用exists子查询表小的用in
      3)not innot exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
  • 【问】如何定位及优化SQL语句的性能问题(Explain + select语句显示了mysql如何使用索引来处理select语句以及连接表;可以在慢查询日志中查看执行时间超过long_query_time秒的sql并对其优化,参考老马资源),可参考MYSQL explain详解MySQL 数据库管理之 — 日志查询
    Note

    • 执行计划包含的信息 id 有一组数字组成。表示一个查询中各个子查询的执行顺序;

      • id相同执行顺序由上至下;id不同,id值越大优先级越高,越先被执行
      • select_type为每个子查询的查询类型,一些常见的查询类型如下:
    • type(非常重要,可以看到有没有走索引) 访问类型:

      • ALL 扫描全表数据
      • index 遍历索引 (索引文件全扫描)
      • range 索引范围查找
      • index_subquery 在子查询中使用 ref
      • unique_subquery 在子查询中使用 eq_ref
      • ref_or_null 对Null进行索引的优化的
      • ref fulltext 使用全文索引
      • ref 使用非唯一索引(普通索引)查找数据
    • extra 的信息非常丰富,常见的有:

      • Using index 使用覆盖索引
      • Using where 使用了用where子句来过滤结果集
      • Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。
      • Using temporary 使用了临时表
    • SQL性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts 则更好。 说明:

      • 1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
      • 2)ref 指的是使用普通的索引(normal index,非唯一索引)。
      • 3) range 对索引进行范围检索。 如果explain表的结果type=index表示遍历索引,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描相差无几
  • 【问】如何做 mysql 的性能优化?(用具体字段替代*;where中避免用oror不走索引,可用in代替;where中使用默认值代替null,因为null不走索引;在联合索引中,全值匹配会自动用到mysql优化器,最左连续匹配),可参考MySQL性能优化的9种方法SQL优化 21 连击 + 思维导图Mysql最左匹配原则

  • 【问】讲一讲你的SQL优化经历(先建立单列索引,如果单列索引粒度较粗,过滤后的数据行还很多,可以使用多列联合索引),可参考一次非常有意思的 SQL 优化经历:从 30248.271s 到 0.001s
    Note:sql调优总结(参考上面链接做的实验进行理解)

    • 列类型尽量定义成数值类型,且长度尽可能短,如主键和外键,类型字段等等
    • 建立单列索引,根据需要建立多列联合索引
      • 当单个列过滤之后还有很多数据,那么索引的效率将会比较低,即列的区分度较低,那么如果在多个列上建立索引,那么多个列的区分度就大多了,将会有显著的效率提高。
    • 根据业务场景建立覆盖索引只查询业务需要的字段,如果这些字段被索引覆盖,将极大的提高查询效率。
    • 多表连接的字段上需要建立索引,这样可以极大的提高表连接的效率
    • where条件字段上需要建立索引
    • 排序字段上需要建立索引,可以提高排序效率
    • 分组字段上需要建立索引
    • Where条件上不要使用运算函数,以免索引失效
  • 【问】MySQL 上亿大表如何优化?(可以使用pt-query-digest工具分析最近一周的mysql-slow.log,查看存在哪些慢查询(其中不乏使用了索引的),进而对索引进行优化),可参考MySQL 上亿大表如何优化?

  • 【问】mysql中select......for update是锁表还是锁行?(select不会加锁,但对于带更新操作的select......for update会加悲观锁(写锁))
    Note

    • 如果使用主键id = 1为条件去select ... for update,然后开启另一个事务也去update id=1的数据,会发现第二个事务更新被阻塞
      如果开启另一个事务也去update 主键id=2的数据,会发现事务顺利执行;
      因此select ... for update查询id=1时使用了行锁
    • 如果使用普通字段code去操作,一个事务select * from user where code = 001 for update,另一个事务update user set age = 10 where code = 003,发现第二个事务更新被阻塞
      因此select ... for update查询code时对整张表上了锁(表锁);
    • 因此select......for update没用索引/主键的话就是表锁,用索引或主键则是行锁
  • 【问】什么是分库分表?(如果上面的sql优化后效果不是很好,水平分/垂直分库/表),参考不用找了,大厂在用的分库分表方案,都在这了
    Note

    • 数据库瓶颈:不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值,可用数据库连接少甚至无连接可用,并发量、吞吐量低,系统崩溃。
      • IO瓶颈
        • 第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表
        • 第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库
      • CPU瓶颈
        • 第一种:SQL问题导致的查询效率低,可进行SQL语句优化;
        • 第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 -> 水平分表
    • 水平分库分表:横向切,按行抽离出数据。
      • 水平分库:库多了,iocpu的压力自然可以成倍缓解(数据库连接池)。
        • 1)以字段为依据,按照一定策略hashrange等),将一个中的数据拆分到多个库中。
        • 2)每个库的结构都一样,但不同库中的数据没有交集,所有库的并集是全量数据;
      • 水平分表:表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担。
        • 概念和分表结果和水平分库相同(上面的“库”改为“表”即可)
    • 垂直分库分表:纵向切,按业务抽离出表或字段。
      • 垂直分库:随着的业务的拓展,之前库中的配置表,字典表可以抽象到更高一层进行服务化。
        • 1)以为依据,按照业务归属不同,将不同的拆分到不同的中。
        • 2)每个库的结构都不一样,不同库中的数据没有交集,所有库的并集是全量数据;
        • 3)使用场景
          • 随着业务的发展一些公用的配置表、字典表等越来越多,这时可以将这些表拆到单独的库中,甚至可以服务化
          • 再有,随着业务的发展孵化出了一套业务模式,这时可以将相关的表拆到单独的库中,甚至可以服务化。
      • 垂直分表:随着的需求的细化,比如用户经常看到列表页而非详情页,所以可将两者抽离出来,符合数据库规范理论
        • 1)以字段为依据,按照字段的活跃性,将表中字段拆到不同的表 (主表和扩展表)中。
        • 2)每个表的结构都不一样;每个表的数据也不一样,一般来说,每个表的字段至少有一列交集,一般是主键,用于关联数据;所有表的并集是全量数据;
        • 3)使用场景
          • 可以用列表页详情页来帮助理解。垂直分表的拆分原则是将热点数据(可能在一起查询时经常存在冗余的数据)放在一起作为主表非热点数据放在一起作为扩展表
          • 这样更多的热点数据就能被缓存下来,进而减少了随机读IO。拆了之后,要想获得全部数据就需要关联两个表来取数据。关联数据,应该在业务Service层做文章(不要使用join,或者说没必要),分别获取主表和扩展表数据然后用关联字段关联得到全部数据。
    • 分库分表工具
      • sharding-sphere:jar,前身是sharding-jdbc;
      • TDDL:jar,Taobao Distribute Data Layer;
      • Mycat:中间件。
    • 分库分表步骤
      根据容量(当前容量和增长量)评估分库或分表个数 -> 选key(均匀)-> 分表规则(hashrange等)-> 执行(一般双写)-> 扩容问题(尽量减少数据的移动)。
    • 分库分表总结
      • 1)分库分表,首先得知道瓶颈在哪里,然后才能合理地拆分(分库还是分表?水平还是垂直?分几个?)。且不可为了分库分表而拆分
      • 2)选key很重要,既要考虑到拆分均匀(常用hash),也要考虑到非partition key的查询。
      • 3)只要能满足需求,拆分规则越简单越好
  • 【问】分库分表之后,id 主键如何处理?(snowflake 分布式id算法,提供 64 位的long 型的id41bit表示的是时间戳,10bit记录工作机器 id,12bit用来记录同一个毫秒内产生的不同idmongoDB id也用到类似思想),参考分库分表之后,id 主键如何处理?分布式 ID(雪花算法,mongoDB id)

  • 【问】分库分表具体如何操作?

  • 【问】什么是数据一致性?,参考ChatGPT

    Note

    • 数据一致性Data Consistency)是指数据库中的数据在任何给定时间点都保持正确、有效和可靠的状态。当多个用户或应用程序同时访问和修改数据库时,确保数据的一致性非常重要。

      在数据库系统中,数据一致性通常涉及以下几个方面:

      1. 事务一致性:事务是一系列操作的逻辑单元,要么全部执行成功,要么全部回滚,保证数据的一致性。在事务中,数据的修改必须符合一定的规则和约束,以确保数据库的状态从一个一致的状态转移到另一个一致的状态。

      2. 数据完整性:数据完整性确保数据的准确性和完整性。数据库中定义的约束条件(例如主键、外键、唯一性约束等)用于确保数据的完整性。如果数据操作违反了这些约束条件,数据库系统将拒绝操作,从而维护数据的一致性。

      3. 并发控制:并发访问是指多个用户或应用程序同时对数据库进行读取和写入操作。并发控制机制用于管理并发访问,以避免数据访问冲突和数据不一致。常见的并发控制技术包括锁定机制、事务隔离级别和多版本并发控制(MVCC)等。

      4. 复制和同步:在分布式系统中,数据复制和同步是保持数据一致性的关键。当数据被复制到多个节点或副本时,确保这些副本之间的数据保持一致非常重要。这通常涉及到数据复制策略、同步机制和冲突解决方案等。

      综上所述,数据一致性是确保数据库中的数据始终保持正确、有效和可靠的属性。通过事务管理、数据完整性、并发控制和数据复制等机制,数据库系统可以维护和保证数据的一致性。

  • 【问】分库分表后,数据库数据一致性问题如何解决?(分布式事务),参考 分库分表后,数据库数据一致性问题如何解决?
    Note

    • 通过对数据的垂直拆分水平拆分后,我们解决了数据库容量、性能等问题,但是将会面临数据迁移和数据一致性的问题。
    • 在数据迁移方面,需要考虑如何快速迁移、平滑迁移、不停机的迁移等。待数据迁移完毕后,还需要校验数据的完整性。可以采用的方法:binlog + 全量(停机) + 增量
    • 数据一致性方面,要根据的业务来判断是否要必要引入分布式事务,如果需要引入分布式事务,需要斟酌是采用XA,还是基于BASE的柔性事务
  • 【问】什么是mysql的预编译(在批处理上可以提高效率),参考预编译语句(Prepared Statements)介绍,以MySQL为例
    Note

    • 编译sql语句:通过 PREPARE stmt_name FROM preparable_stm的语法来预编译一条sql语句

      mysql> prepare ins from 'insert into t select ?,?';
      Query OK, 0 rows affected (0.00 sec)
      Statement prepared
      
    • 执行sql语句:通过EXECUTE stmt_name [USING @var_name [, @var_name] ...]的语法来执行预编译语句

      mysql> set @a=999,@b='hello';
      Query OK, 0 rows affected (0.00 sec)
      
      mysql> execute ins using @a,@b;
      Query OK, 1 row affected (0.01 sec)
      Records: 1  Duplicates: 0  Warnings: 0
      
      mysql> select * from t;
      +------+-------+
      | a    | b     |
      +------+-------+
      |  999 | hello |
      +------+-------+
      1 row in set (0.00 sec)
      
    • 预编译语句不使用可以释放掉:要释放一条预编译语句,则可以使用{DEALLOCATE | DROP} PREPARE stmt_name的语法进行操作:

      mysql> deallocate prepare ins;
      Query OK, 0 rows affected (0.00 sec)
      
  • 【问】如果有多条数据要进行插入/更新,逐条插入/更新效率低,有什么办法?(多个update放在一个事务里再commit可以复用缓存的预编译语句 / replace into/insert into … on duplicate key update,参考【MySQL】 update 大量数据批量更新在MySQL对于批量更新操作的一种优化方式

    Note

    • 不建议使用下面这种for循环,来完成多条数据的批量update一条记录update一次commit一次,这样性能很差,也很容易造成阻塞

      foreach ($display_order as $id => $ordinal) { 
          $sql = "UPDATE categories SET display_order = $ordinal WHERE id = $id"; 
          mysql_query($sql); 
      }
      

      所以优化方法是

      • 减少commit操作对性能的影响,尽量将多个语句放到一个事务内,保证只提交一次事务。
      • 多条语句合并成一条来提高执行效率。

      解决方法:

      #!/bin/bash
      echo "begin;" > update.sql
      for i in {1..1000}
      do
         echo "update t1 set c2=$((RANDOM%1000+1)) where c1=$i;" >> update.sql
      done
      echo "commit;" >> update.sql
      
    • 三种批量插入的方法:

      • 方法1replace into tbl_name (col_name, …) values(…)

        replace into test_tbl (id,dr) values (1,'2'),(2,'3'),...(x,'y');
        
      • 方法2insert into … on duplicate key update

        insert into test_tbl (id,dr) values  (1,'2'),(2,'3'),...(x,'y') on duplicate key update dr=values(dr);
        
      • 方法3:创建临时表,先更新临时表,然后从临时表中update

        create temporary table tmp(id int(4) primary key,dr varchar(50));
        insert into tmp values  (0,'gone'), (1,'xx'),...(m,'yy');
        update test_tbl, tmp set test_tbl.dr=tmp.dr where test_tbl.id=tmp.id; 
        注意:这种方法需要用户有temporary 表的create 权限。
        
    • 下面是上述方法update 100000条数据的性能测试结果:

      逐条update
      real    0m15.557s
      user    0m1.684s
      sys    0m1.372s
      
      replace into
      real    0m1.394s
      user    0m0.060s
      sys    0m0.012s
      
      insert into on duplicate key update
      real    0m1.474s
      user    0m0.052s
      sys    0m0.008s
      
      create temporary table and update:
      real    0m0.643s
      user    0m0.064s
      sys    0m0.004s
      
    • 就测试结果来看,逐条update性能最差(主要是每次update会删除掉原来预编译的语句,而疲劳update则可以重用这些语句,减少更新耗时),使用replace into性能较好(其他也不错)。replace intoinsert into on duplicate key update的不同在于:

      • replaceinsert的增强版,本质是delete + insertreplace into首先尝试插入数据到表中,如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据(binlog中记录update操作),否则,直接插入新数据(binlog中记录insert操作)。参考replace into详解
      • insert into … on duplicate key update 则是只update重复记录,不会改变其它字段。
      • replace适合的场景:该表必须有主键或者唯一索引约束;主表和从表不存在外键约束(因为replace本质是delete + insert,会存在级联删除错误)

4、Spring/SpringMVC(IoC装配、AOP增强、常用注解、SpringMVC流程和拦截器)

参考Spring常见面试题总结(超详细回答)SpringMVC常见面试题总结(超详细回答)Spring 中文文档 3.1
在学习这一章节前,为了更好地理解IoCDI强烈建议先熟悉一下创建型设计模式(单例,静态工厂,抽象工厂,生成器和原型),参考创建型设计模式

  • 【问】什么是 Spring 框架?Spring 框架有哪些主要模块?(Context/CORE/AOP/Web/MVC/ORM/DAO)
    Note

    • Spring是一个轻量级的IoCAOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。主要包括以下七个模块
      • Spring ContextSpring上下文提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
      • Spring CoreSpring核心类库,所有功能都依赖于该类库,提供IOCDI服务;
      • Spring AOPAOP允许将一些通用任务,如安全、事务、日志等进行集中式处理,从而提高了程序的复用性
      • Spring Web:提供了基本的面向Web的综合特性,提供对常见框架如Struts2的支持Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器
      • Spring MVC:提供面向Web应用的Model-View-Controller,即MVC实现。
      • Spring ORM:对现有的ORM框架(hibernatemybatis)的支持;
      • Spring DAO:对JDBC的抽象封装,简化了数据访问异常的处理,并能统一管理JDBC事务
  • 【问】如何使用XML将bean对象注册到Spring Ioc容器中,参考Spring Ioc 使用XML配置

    • 配置applicationContext.xml(假设有两个实体类UserAddress):
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
      
              <bean id="address" class="com.ared.entity.Address">
                  <property name="homeAddress" value="青山湖"/>
                  <property name="officeAddress" value="高新"/>
              </bean>
          
              <bean id="student" class="com.ared.entity.Student">
                  <property name="id" value="10001"/>
                  <property name="name" value="码小农"/>
                  <!-- 添加一个list参数 -->
                  <property name="phone">
                      <list>
                          <value>13711112222</value>
                          <value>13711112223</value>
                      </list>
                  </property>
                  <!-- 添加一个set参数 -->
                  <property name="oldName">
                      <set>
                          <value>小明</value>
                          <value>小红</value>
                      </set>
                  </property>
                  <!--添加一个Properties参数-->
                  <property name="info">
                      <props>
                          <prop key="username">admin</prop>
                          <prop key="password">123456</prop>
                      </props>
                  </property>
                  <!--如果属性有对象的话可以先创建bean 然后使用ref 应用id 得到值-->
                  <property name="address" ref="address"/>
              </bean>
      </beans>
      
  • 【问】Spring常用注解?(SpringMVC的见下一问),参考Spring常用注解(绝对经典)精讲Spring—Spring常用注解【经典总结】java注解原理:反射 & 动态代理
    Note

    • 1)组件类注解

      • @Component泛指各种组件(相当于xml文件中的
        <bean id="" class=""/>),用于标注一个普通的spring Bean类,@Component可以代替@Repository@Service@Controller@Configration等,因为这三个注解是被@Component标注的。但使用具体的注解会携带更多语义,并且便于开发和维护
      • @Controller@Service@Repository都可以称为@Component
        • @Controller:标注一个控制器组件类。
        • @Service:标注一个业务逻辑组件类。
        • @Repository:标注一个DAO组件类。
      • 被注解的java类当做Bean实例(默认单例),Bean实例的名称默认是Bean类的首字母小写,其他部分不变。
      • 指定了某些类可作为Spring Bean类使用后,最好还需要让spring搜索指定路径,在Spring配置文件加入如下配置:
      	<!-- 自动扫描指定包及其子包下的所有Bean类 -->
      	<context:component-scan base-package="org.springframework.*"/>
      
    • 2)装配(注入)Spring bean的注解

      • @Autowired:属于Springorg.springframework.beans.factory.annotation包下,可用于为类的属性、构造器、方法进行注值;有个属性为required,可以配置为false;只按照Type 注入
        @Resource不属于spring的注解,而是来自于JSR-250位于java.annotation包下;@Resource默认Name自动注入,也提供按照Type注入
        @PostConstruct@PreDestroy方法 实现初始化和销毁bean之前进行的操作。
      • @Autowired@Resource的区别:
        • @Autowired注解默认按照类型装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合@Qualifier注解进行限定
        • @Resource注解的使用性更为灵活,可指定名称,也可以指定类型
    • 3) JSON序列化@JsonIgnorejson序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响;一般标记在属性或者方法上,返回的json数据即不包含该属性。在和fastjson一起使用时可能会存在注解失效的问题。

    • 4)Java配置类相关注解(JavaConfig)

      • @Bean注解主要用于方法上,有点类似于工厂方法,当使用了@Bean注解,我们可以连续使用多种定义bean时用到的注解:譬如用@Qualifier注解定义工厂方法的名称,用@Scope注解定义该bean作用域范围(譬如是singleton还是prototype等)。
      • 使用@Configuration来注解类 表示 类可以被 SpringIoC 容器所使用,作为 bean 定义的资源。
        @Target({ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Component  //看这里!!!
        public @interface Configuration {
            String value() default "";
        }
        
      • 在配置@Configuration时,并不能用@Component替代,原因如下:
        //配置方法1
        @Configuration
        public static class Config {
        
            @Bean
            public SimpleBean simpleBean() {
                return new SimpleBean();
            }
        
            @Bean
            public SimpleBeanConsumer simpleBeanConsumer() {
                return new SimpleBeanConsumer(simpleBean());
            }
        }
        
        //配置方法2
        @Component
        public static class Config {
        
            @Bean
            public SimpleBean simpleBean() {
                return new SimpleBean();
            }
        
            @Bean
            public SimpleBeanConsumer simpleBeanConsumer() {
                return new SimpleBeanConsumer(simpleBean());
            }
        }
        
      • 第一个代码正常工作,正如预期的那样,SimpleBeanConsumer将会得到一个单例SimpleBean的链接。
      • 第二个配置是完全错误的(不能用@Component,不能被Spring IoC容器使用),因为Spring会创建一个SimpleBean单例bean,但是SimpleBeanConsumer将获得另一个SimpleBean实例(也就是相当于直接调用new SimpleBean() 这个bean是不归Spring管理的),既new SimpleBean() 实例是Spring上下文控件之外的
    • 5)切面(AOP)相关注解:参考@Pointcut 注解的使用
      Spring支持AspectJ的注解式切面编程:

      • Advice(通知、切面)某个连接点JointPoint所采用的处理逻辑,也就是向连接点注入的代码, AOP在特定的切入点上执行的增强处理。
        1)@Before: 标识一个前置增强方法,相当于BeforeAdvice的功能.
        2) @After: final增强,不管是抛出异常或者正常退出都会执行.
        3) @AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行
        4) @AfterThrowing: 异常抛出增强,相当于ThrowsAdvice.
        5) @Around: 环绕增强,相当于MethodInterceptor
      • JointPoint(连接点):程序运行中的某个阶段点,比如方法的调用、异常的抛出等。
      • Pointcut(切入点)JoinPoint的集合,是程序中需要注入Advice的位置的集合指明Advice要在什么样的条件下才能被触发,在程序中主要体现为:表示式(expression)和签名(signature)。
        //Pointcut表示式
        @Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
        //Point签名
        private void log(){}
        
      • Advisor(增强): 是PointCutAdvice的综合体,完整描述了一个advice将会在pointcut所定义的位置被触发
      • @Aspect(切面): 通常是一个类的注解,里面可以定义切入点Pointcut和通知Advice

      java配置类中使用@EnableAspectJAutoProxy注解开启SpringAspectJ代理的支持。

    • 6)异步相关

      • @EnableAsync:配置类中通过此注解开启对异步任务的支持
      • @Async:在实际执行的bean方法上使用该注解来声明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)
    • 7)Enable***注解说明:主要是用来开启对xxx的支持

      • @EnableAspectAutoProxy:开启对AspectJ自动代理的支持;
      • @EnableAsync:开启异步方法的支持;
      • @EnableScheduling:开启计划任务的支持;
      • @EnableWebMvc:开启web MVC的配置支持;
        @EnableConfigurationProperties:开启对@ConfigurationProperties注解配置Bean的支持;
        @EnableJpaRepositories:开启对SpringData JPA Repository的支持;
        @EnableTransactionManagement:开启注解式事务的支持;
        @EnableCaching:开启注解式的缓存支持
  • 【问】SpringMVC常用注解?,参考Spring常用注解(绝对经典)精讲Spring—Spring常用注解【经典总结】SpringMVC常见面试题总结(超详细回答)
    Note

    • 1)web模块常用到的注解
      • @Controller :表明该类会作为与前端作交互的控制层组件,通过服务接口定义的提供访问应用程序的一种行为,解释用户的输入,将其转换成一个模型然后将视图呈献给用户。
      • @Controller定义的控制器,还允许自动检测定义在类路径下的组件(配置文件中配置扫描路径)并自动注册
        • @RequestMapping : 这个注解用于url映射到整个处理类或者特定的处理请求的方法
        • @RequestParam :将请求的参数绑定到方法中的参数上,有required参数,默认情况下,required=true,也就是该参数必须要传。如果该参数可以传可不传,可以配置required=false
        • @PathVariable : 该注解用于修饰方法参数,会将修饰的方法参数变为可供使用的uri变量(可用于动态绑定)。
        • @RequestBody@RequestBody是指方法参数应该被绑定到HTTP请求Body
        • @ResponseBody@ResponseBody用于修饰方法返回值,其作用是将返回类型直接输入到HTTP response body中。@ResponseBody在输出JSON格式的数据时,会经常用到;如果不用则返回字符串数据。
      • @RestController :与@Controller类似,控制器实现了REST的API,只为服务于JSON,XML或其它自定义的类型内容@RestController用来创建REST类型的控制器,可以避免你重复的写@Controller@ResponseBody
        //@Controller
        //@ResponseBody
        @RestController
        @RequestMapping("/hello")
        public class HelloController {
        	@RequestMapping(value = "/happy", method =RequestMethod.POST)
        	String helloWorld() {    
        		return "Hello World";//返回String类型
        	}
        	@RequestMapping(value="/happy/{dayid}",method=RequestMethod.GET)
        	public String findPet(@PathVariable String dayid, Model mode) {
        		//使用@PathVariable注解绑定 {dayid} 到String dayid
        	}
        	@RequestMapping(value = "/something", method = RequestMethod.PUT)
        	public void handle(@RequestBody String body,@RequestBody User user){
        	   	//可以绑定自定义的对象类型
        	}
        }
        
    • 2)Spring事务模块注解
      • 在处理dao层或service层的事务操作时,譬如删除失败时的回滚操作。使用@Transactional作为注解,但是需要在配置文件激活
        	<!-- 开启注解方式声明事务 -->
        <tx:annotation-driven transaction-manager="transactionManager" />
        
        代码如下:
        @Service
        public class CompanyServiceImpl implements CompanyService {
          @Autowired
          private CompanyDAO companyDAO;
        
          @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
          public int deleteByName(String name) {
        
            int result = companyDAO.deleteByName(name);
            return result;
          }
          ...
        }
        
      • 其中事务的传播机制隔离机制比较重要!
        • 事务传播行为类型包括:PROPAGATION_REQUIREDPROPAGATION_SUPPORTS
        • 事务的隔离机制包括:DEFAULTREAD_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE 串行化。
  • 【问】什么是 JavaConfig?(Spring4.0提出的新特性,通过@Configuration使得JavaConfig可以替代xml,将bean注册到Spring容器(面向注解编程),底层使用CGLIB静态代理;而@Component底层不使用CGLIB静态代理,需要配合@ComponentScan指明bean的扫描路径),参考JavaConfig实现配置@Component和@Configuration作为配置类的差别

    • JavaConfig的使用

      //User.java
      
      //@Component
      public class User {
      
          public String name;
          public String getName() {
              return name;
          }
          @Value("测试名字")
          public void setName(String name) {
              this.name = name;
          }
      
      } 
      

      Note:写@Component(把普通pojo实例化到spring容器中,相当于xml文件中的
      <bean id="" class=""/>)的目的是将该类的属性注入但是这里并没有发挥作用,因为JavaConfig.java中的Bean已经创建了对象,实现了注入,所以这里不写@Component

      //JavaConfig.java
      
      @Configuration 
      public class JavaConfig {
      
          @Bean
          public User getUser() {
              return new User();
          } 
      }
      

      Note

      • @Configuration标注的类会被注册到spring容器中

      • @Bean相当于注册一个bean标签,其中方法名字 “user” 相当于 bean 标签的 “id”,方法返回值"User" 相当于 bean 标签的 “class”;Bean已经实现了注入,不需要在User.java文件中 @compoment

      • 使用@Compoment需要@CompomentScan配合指明bean扫描路径

       @Test
          public void test01(){
              ApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
              User user = ctx.getBean("getUser",User.class);
              System.out.println(user.name);
          } 
      
       ---
      测试名字
      
    • 使用JavaConfig 的优点在于:

      • 1)面向对象的配置:由于配置被定义为JavaConfig中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean方法等。

      • 2)减少或消除 XML 配置:基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。

      • 3)类型安全和重构友好:JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型(type)而不是按名称(name)检索 bean,不需要任何强制转换或基于字符串的查找。

  • 【问】@Component和@Configuration作为配置类的差别(在作为配置类时,@Component并不会为注解的类生成CGLIB代理类),参考@Component和@Configuration作为配置类的差别

    • 从参考链接给的代码可以发现,使用@Configuration注解配置类时在driver和spring容器之中的是同一个对象,而使用@Component时是不同的对象。
      造成不同结果的原因在ConfigurationClassPostProcessor类之中,通过调用enhanceConfigurationClasses方法,为被注解@Configuration的类进行CGLIB代理。

    • 虽然@Component注解也会当做配置类,但是并不会为其生成CGLIB代理Class,所以在生成Driver对象时和生成Car对象时调用car()方法执行了两次new操作,所以是不同的对象。当使用@Configuration注解时,生成当前对象的子类Class,并对方法拦截,第二次调用car()方法时直接从BeanFactory之中获取对象,所以得到的是同一个对象。

    • 使用@Component注解的配置类,其所创建的bean是不归Spring容器管理的,配置类必须用@Configuration

  • 【问】使用 Spring 框架能带来哪些好处?(7大模块),参考Spring常见面试题总结(超详细回答)
    Note

    • 1)Spring属于低侵入式设计,代码的污染极低(即Spring代码基本符合设计模式中的6大基本原则);
    • 2)spring通过IoC容器中的DI机制将对象之间的依赖关系交由框架处理,降低组件的耦合性;
    • 3)提供对AOP的支持,它允许将一些通用任务,如安全、事务、日志等进行集中式处理,从而提高了程序的复用性
    • 4)Spring4提供Junit4的支持,可以通过注解方便测试spring程序;
    • 5)方便集成各种优秀框架,如Struts、hibernate、mybstis;
    • 6)支持声明式事务处理@Transactional,只需通过配置就可以完成对事务的管理。
  • 【问】什么是控制反转(IoC)?什么是依赖注入(DI)?(Spring IoC容器控制对象的生命周期(并非对象托管),反射实现DI,参考老马资源),参考Spring常见面试题总结(超详细回答)阿里云面试:Spring 中 Bean 的生命周期是怎样的?
    Note

    • IOCInversion of Control,控制反转),指将对象的控制权转移给Spring框架。以前是由自己控制它所引用对象的生命周期,而在IoCSpring容器来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。

    • IoC 的一个重点就是在程序运行时,动态的向某个对象提供它所需要的其他对象,这一点是由DIDependency Injection,依赖注入)来实现的,即应用程序在运行时依赖 IoC容器来动态注入对象所需要的外部依赖依赖是指对象间的关联关系,比如聚合/组合/继承)。而 SpringDI 具体就是通过反射实现注入的。

    • IoC容器提供哪些Bean管理功能:Spring 通过一个配置文件描述 BeanBean 之间的依赖关系,利用 Java 语言的反射功能实例化 Bean 并建立Bean之间的依赖关系SpringIoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载等高级服务。

    • IoC容器如何获取Bean之间的依赖关系:Spring启动时读取应用程序提供的 Bean 配置信息,并Spring 容器中生成一份相应的 Bean 配置注册表BeanDefinitionRegistry,然后根据这张注册表实例化 Bean装配好 Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。其中 Bean 缓存池由 HashMap 实现。

  • 【问】请解释下 Spring 框架中的 IoC?(IoC 容器实现 参考老马资源)
    Note

    • ApplicationContext的继承体系图如下:参考ApplicationContext继承关系解析
    • BeanDefinitionRegistry注册表:Spring配置文件中每一个节点元素在 Spring容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册 BeanDefinition对象的方法
    • BeanFactory顶层接口:位于类结构树的顶端 ,它最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的 BeanBeanFactory的功能通过其他的接口得到不断扩展
    • ListableBeanFactory:该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 Bean 的个数、获取某一类型 Bean配置名、查看容器中是否包括某一 Bean 等方法;
    • HierarchicalBeanFactory父子级联:父子级联 IoC容器的接口,子容器可以通过接口方法访问父容器; 通过HierarchicalBeanFactory接口, SpringIoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean
      Spring使用父子容器实现了很多功能,比如在Spring MVC 中,表现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,表现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到表现层的 Bean
    • ConfigurableBeanFactory是一个重要的接口,增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法;
    • AutowireCapableBeanFactory自动装配:定义了将容器中的 Bean 按某种规则(如按名字name匹配、按类型type匹配等,使用到了@Autowired@Resource)进行自动装配的方法;
    • SingletonBeanRegistry运行期间注册单例Bean:定义了允许在运行期间向容器注册单实例 Bean 的方法;对于单实例(singleton)的 Bean 来说,BeanFactory 会缓存 Bean 实例,所以第二次使用 getBean() 获取 Bean 时将直接从 IoC 容器的缓存中获取 Bean 实例。Spring 在 DefaultSingletonBeanRegistry 类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用 HashMap 实现的缓存器,单实例的 BeanbeanName 为键保存在这个 HashMap
    • 依赖日志框:在初始化 BeanFactory 时,必须为其提供一种日志框架,比如使用 Log4J, 即在类路径下提供Log4J配置文件,这样启动 Spring 容器才不会报错。
    • ApplicationContext 面向开发应用:ApplicationContextBeanFactory 派 生而来 , 提供了更多面向实际应用的功能 。
      ApplicationContext 继承了 HierarchicalBeanFactoryListableBeanFactory 接口,在此基础上,还通过多个其他的接口扩展了 BeanFactory 的功能。
  • 【问】BeanFactory 和 ApplicationContext 有什么区别?(开发时用ApplicationContext作为IoC容器),参考Spring常见面试题总结(超详细回答)
    Note

    • BeanFactoryApplicationContextSpring的两大核心接口都可以当做Spring的容器
      • 1)BeanFactorySpring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean定义、加载、实例化,依赖注入和生命周期管理
      • 2)ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
        • 继承MessageSource,因此支持国际化
        • 资源文件访问,如URL和文件(ResourceLoader)。
        • 载入多个(有继承关系)上下文(即同时加载多个配置文件) ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
        • 提供在监听器中注册bean的事件
    • BeanFactroyApplicationContext在实例化Bean时存在不同:
      • BeanFactroy采用的是延迟加载(懒加载)形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能提前发现一些存在的Spring的配置问题
      • ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入
      • ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactoryApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。
    • BeanFactoryApplicationContext都支持BeanPostProcessorBeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册
    • BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader
  • 【问】请解释 Spring Bean 的生命周期?(实例化(东西在但不全)在初始化(东西可用)之前),参考Spring常见面试题总结(超详细回答)阿里云面试:Spring 中 Bean 的生命周期是怎样的?,更具体的Bean加载过程参考Spring的Bean加载流程
    NoteSpring Bean生命周期只有四个阶段实例化 (Instantiation) --> 属性赋值(Populate) --> 初始化(Initialization) --> 销毁(Destruction),但具体来说,Spring Bean的生命周期包含下图的流程(7 + 2):

    • 1)实例化Bean

      • 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean()进行实例化
      • 对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息(配置注册表),实例化所有的bean
    • 2)设置对象属性(依赖注入)

      • 实例化后的对象被封装在BeanWrapper对象中;
      • 紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口,完成属性设置与依赖注入
    • 3)处理Aware接口Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源(设置注入):

      • ①如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,传入Bean的名字;
      • ②如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
      • ②如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
      • ③如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
    • 4)BeanPostProcessor前置处理:如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

    • 5)InitializingBean接口是否实现:如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。

    • 6)init-method:如果BeanSpring配置文件中配置了init-method属性,则会自动调用其配置的初始化方法

    • 7)BeanPostProcessor后置处理:如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

      以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

      • 第5、6 步是真正的初始化,
      • 第 3、4 步为在初始化前执行,
      • 第 7 步在初始化后执行,初始化完成之后,Bean 就可以被使用了
    • 8)Bean在使用前注册了销毁的相关调用接口

    • 9)DisposableBean接口是否实现:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

    • 10)destroy-method:最后,如果这个BeanSpring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

  • 【问】Spring Bean 的作用域(scope)之间有什么区别?,参考Spring常见面试题总结(超详细回答)
    Note

    • 1)singleton:默认作用域,单例bean,每个容器中只有一个bean的实例。
    • 2)prototype:为每一个bean请求创建一个实例
    • 3)request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收
    • 4)session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例(ThreadLocal管理session)。
    • 5)global-session全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中。
  • 【问】什么是 Spring inner beans?(bean中的成员属性也是一个bean,比如Customer中聚集了一个person对象)

  • 【问】Spring 框架中的单例 Beans 是线程安全的么?(Spring框架并没有对单例bean进行任何多线程的封装处理,需开发者自行去解决(synchronized,lock等)),参考Spring常见面试题总结(超详细回答)
    Note

    • Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体情况还是要结合Bean的作用域来讨论。
      • 1)对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题
      • 2)对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。但是如果单例Bean是一个无状态Bean(该Bean的成员属性只执行查询操作),那么这个单例Bean线程安全的。比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身。

        有状态Bean(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的
        无状态Bean(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的

    • 有状态单例Bean的线程安全解决方法:
      • 对于有状态的bean(比如ModelView),就需要自行保证线程安全,最浅显的解决办法就是将有状态的bean的作用域由“singleton”改为“prototype”
      • 也可以采用ThreadLocal解决线程安全问题(空间换时间),为每个线程提供一个独立的变量副本,不同线程只操作自己线程的副本变量。还可以使用synchronized,lock(时间换空间)对单例Bean加锁。
  • 【问】请解释 Spring Bean 的自动装配?如何开启基于注解的自动装配?(通过@Autowired自动装配)
    Note:要使用 @Autowired,需要注册AutowiredAnnotationBeanPostProcessor

    • 方法1:引入配置文件中的<beans>下引入 <context:annotation-config>
      <beans>
          <context:annotation-config />
      </beans>
      
    • 方法2:在bean配置文件中直接引入AutowiredAnnotationBeanPostProcessor
      <beans>
          <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
      </beans>
      
  • 【问】请解释自动装配模式的区别?或者说自动装配的规则?,可参考面试题 请解释自动装配模式的区别?
    Note

    • Spring 装配包括手动装配和自动装配,手动装配是基于 xml 配置文件的、而自动装配则通过constructorsetter方法实现(用到@Autowired注解)。
      • no默认的方式是不进行自动装配,通过显式设置 ref属性来进行装配。
      • byName:通过参数名 自动装配,Spring容器在配置文件中发现bean的autowire(@Autowired + @Qualifier )属性被设置成byname,之后容器试图匹配、装配和该 bean属性具有相同名字bean
      • byType:通过参数类型自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean如果有多个 bean 符合条件,则抛出错误
      • constructor:这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
      • autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。
  • 【问】请举例解释@Autowired 注解?( @Resource默认name注入,也提供type注入;@Autowired仅支持type依赖注入,如果要想用name注入,则需要配合@Qualifier消歧)

  • 【问】请举例说明@Qualifier注解?,参考SpringBoot @Autowired的两种注入方式
    Note

    • 如果在xml中定义了一种类型(type,比如Student类)的多个bean,同时在java注解中又想把其中一个bean对象作为属性,那么此时可以使用@Qualifier@Autowired来达到这一目的。
    • 若不在student2上加@Qualifier(value="student2")这个注解,在运行时会出现“No qualifying bean of type [com.tutorialspoint.Student] is defined: expected single matching bean but found 2: student1,student2”这个异常。
  • 【问】Spring如何解决循环依赖问题?,参考Spring如何解决循环依赖问题
    Note
    循环依赖问题在Spring中主要有三种情况:

    • 1)通过构造方法进行依赖注入时产生的循环依赖问题。
    • 2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
    • 3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

    Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。这是因为:

    • 第一种构造方法注入的情况下,new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
    • 第二种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去最终就会导致OOM问题的出现。

    Spring单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。

  • 【问】构造方法注入和设值注入有什么区别?(设值setter注入支持大部分依赖注入,单例模式下的setter可解决循环依赖的问题)
    Note

    • 两者存在以下明显的区别:
      • 1)设值注入(setter)支持大部分依赖注入,如果我们仅需要注入intstringlong型的变量,不要用设值方法注入。对于基本类型,如果没有注入,可以为基本类型设置默认值。
        构造方法(constructor)注入不支持大部分依赖注入,因为在调用构造方法时必须传入正确的构造参数,否则会报错。
      • 2)设值注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入和设值注入,那么构造方法将不能覆盖设值注入的值。很明显,因为构造方法只在对象被创建时被调用(设值setter注入在前,构造方法constructor注入在后)。
      • 3)在使用设值注入时还不能保证某种依赖是否已经被注入,也就是说,这时对象的依赖关系有可能是不完整的。而在另一种情况下,构造器(constructor)注入则不允许生成依赖关系不完整的对象
      • 4)在设值注入时如果对象A和对象B互相依赖,在创建对象A时Spring会抛出ObjectCurrentlyInCreationException异常,因为在对象B被创建之前对象A是不能被创建的,反之亦然。Spring用设值(setter)注入解决了循环依赖问题,因为对象的设值方法(setter)是在对象被创建之前被调用的
  • 【问】请举例说明如何在 Spring 中注入一个 Java Collection?(手动装配)
    Note

    • Spring提供了以下四种集合类的配置元素:
      • <list> : 该标签用来装配可重复的list值。
      • <set> : 该标签用来装配没有重复的set值。
      • <map>: 该标签可用来注入键和值可以为任何类型的键值对。
      • <props> : 该标签支持注入键和值都是字符串类型的键值对。
    • 例子如下:
      <beans>
      <!-- Definition for javaCollection -->
      <bean id="javaCollection" class="com.howtodoinjava.JavaCollection">
      	<!-- java.util.List -->
      	<property name="customList">
      	<list>
      	<value>INDIA</value>
      	<value>Pakistan</value>
      	<value>USA</value>
      	<value>UK</value>
      	</list>
      	</property>
      </bean>
      </beans>
      
  • 【问】如何向 Spring Bean 中注入一个 Java.util.Properties?(手动装配)
    Note:

    • 方法1:使用<value>标签配置多值
      <bean id="propertiesInjectBean1" class="com.doctor.spring.context.inject.PropertiesInjectBean1">
      	<property name="properties">
      		<value>
      			name=doctor
      			sex=man
      			address=alien
      		</value>
      	</property>
      </bean>
      
    • 方法2:使用<property>标签配置单值(设值注入)
      <bean id="propertiesInjectBean2" class="com.doctor.spring.context.inject.PropertiesInjectBean2">
      	<property name="properties">
      		<props>
      			<prop key="name">doctor</prop>
      			<prop key="address">alien</prop>
      		</props>
      	</property>
      </bean>
      
  • 【问】Spring基于xml注入bean的几种方式?(前两种最常用),参考Spring中bean的注入方式
    Note:

    • set()方法注入(<property name="springDao" ref="springDao"></property>)
    • 构造器注入:①通过index属性设置参数的位置;②通过ref属性设置参数类型;(<constructor-arg index="0" ref="springDao"></constructor-arg>)
    • 静态工厂注入;
    • 实例工厂;
  • 【问】将Bean放入Spring容器中的五种方式?

  • 【问】Spring AOP是什么?与AspectJ有什么区别?(Spring AOP常用注解,AOP术语 即 类图 见上面解析),参考关于 Spring AOP (AspectJ) 你该知晓的一切@Pointcut 注解的使用
    Note

    • OOP面向对象,允许开发者定义纵向的关系(继承),但并不适用于定义横向的关系,会导致大量代码的重复,而不利于各个模块的重用。

    • AOP,一般称为面向切面,作为面向对象的一种补充(横向扩展),用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理

    • AOP实现的关键在于 代理模式,AOP代理主要分为静态代理动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

      • 1)AspectJ静态代理,也称为编译时增强,AOP框架会在编译阶段(ajc编译器)生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
      • 2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法
    • Spring AOP中的动态代理主要有两种方式JDK动态代理和CGLIB动态代理(Spring AOP 的实现是遵循AOP规范的,特别是以AspectJ(与java无缝整合)为参考,因此在AOP的术语概念上与前面分析的AspectJAOP术语是一样的)

      • JDK动态代理只提供接口的代理(代理类需要实现该接口),不支持类的代理,要求被代理类实现接口。
      • 如果被代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
    • 静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

    • 5)AOP术语:参考@Pointcut 注解的使用

      • Advice(通知、切面)某个连接点JointPoint所采用的处理逻辑,也就是向连接点注入的代码, AOP在特定的切入点上执行的增强处理。
        1)@Before: 标识一个前置增强方法,相当于BeforeAdvice的功能.
        2) @After: final增强,不管是抛出异常或者正常退出都会执行.
        3) @AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行
        4) @AfterThrowing: 异常抛出增强,相当于ThrowsAdvice.
        5) @Around: 环绕增强,相当于MethodInterceptor
      • JointPoint(连接点):程序运行中的某个阶段点,比如方法的调用、异常的抛出等。
      • Pointcut(切入点)JoinPoint的集合,是程序中需要注入Advice的位置的集合指明Advice要在什么样的条件下才能被触发,在程序中主要体现为:表示式(expression)和签名(signature)。
        //Pointcut表示式
        @Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
        //Point签名
        private void log(){}
        
      • Advisor(增强): 是PointCutAdvice的综合体,完整描述了一个advice将会在pointcut所定义的位置被触发
      • @Aspect(切面): 通常是一个类的注解,里面可以定义切入点Pointcut和通知Advice

      java配置类中使用@EnableAspectJAutoProxy注解开启SpringAspectJ代理的支持。

  • 【问】请举例解释@Required 注解?(@Required 注解主要用在 setter 方法上,它表示该 setter 方法的属性必须要在配置时注入值。否则就会报 BeanInitializationException 异常),参考Spring之@Required注解

  • 【问】SpringMVC的流程?,参考SpringMVC常见面试题总结(超详细回答)
    Note

    具体步骤:

    • 1)用户向前端控制器DispatcherServlet发送请求;
    • 2)DispatcherServletHandlerMapping(请求到处理器映射)请求handler
    • 3)HandlerMapping返回handler对象DispatcherServlet
    • 4)DispatcherServlet拿着handler对象HandlerAdapter进行适配;
    • 5)HandlerAdapterDispatcherServlet返回ModelAndView对象
    • 6)DispatcherServlet拿着ModelAndViewViewResolver(视图解析器)去解析,得到View对象
    • 7)DispatcherServlet模型数据填充到视图中(渲染);
    • 8)DispatcherServlet响应用户;
  • 【问】Springmvc的优点?,参考SpringMVC常见面试题总结(超详细回答)
    Note

    • 1)可以支持各种视图技术,而不仅仅局限于JSP;
    • 2)与Spring框架集成(如IoC容器、AOP等);
    • 3)清晰的角色分配:前端控制器(dispatcherServlet) ,请求到处理器映射(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)。
    • 4) 支持各种请求资源的映射策略。
  • 【问】SpringMVC怎么样设定重定向和转发的?,参考SpringMVC常见面试题总结(超详细回答)
    Note

    • 1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"
    • 2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"
  • 【问】SpringMVC拦截器和J2EE过滤器有什么区别,参考最详细的讲解过滤器,拦截器,AOP的区别SpringMVC拦截器&拦截器案例谈谈过滤器和拦截器的区别?

    • Filter(过滤器)是J2EE的规范

      • Servlet2.3开始引入/实现的是责任链模式。多个过滤器形成一个过滤器链,过滤器链中不同过滤器的先后顺序由部署文件web.xml中过滤器映射的顺序决定。过滤器位于客户端和web应用程序之间,用于检查和修改两者之间流过的请求和响应。
      • 请求到达Servlet/JSP之前,过滤器截获请求
      • 过滤器的主要作用:
        • 用户访问权限处理
        • 设置字符集乱码处理
        • 过滤敏感词汇、压缩响应信息
        • 通过设置跨域涉及的4个响应头,处理跨域问题
    • 拦截器的概念(基于SpringMVC)

      • 基于 java 的反射机制,代理模式(AOP)实现,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理
      • 实现HandlerInterceptor接口有三个方法,分别为在业务处理器处理请求之前被调用(preHandle()),在业务处理器处理完请求后(postHandle()),完全处理完请求后被调用(afterCompletion())。
      • java里的拦截器动态拦截Action调用的对象,采用责任链模式实现。
      • 拦截器作用(对主业务进行功能增强):
        • 只能拦截请求,可以访问上下文等对象,功能强大,一个请求可多次拦截。
        • 用户访问权限处理
        • 登记日志
    • 过滤器与拦截器的区别:

      • 实现原理:拦截器是基于java的反射机制的,而过滤器是基于函数回调
      • 处理时机:过滤器依赖于servlet容器,在进入servlet之前被调用;而拦截器不依赖于servlet容器。在action的生命周期中,拦截器可以多次被调用过滤器时在容器初始化的时候初始化一次,请求一次调用一次
      • 拦截范围:拦截器只能action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  • 【问】Spring 框架中有哪些不同类型的事件?(更新/开始/停止/关闭/请求事件;Spring事件驱动模型用到了观察者模式,事件为观察者)
    NoteSpring 提供了以下5种标准的事件:

    • 1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发
    • 2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContextStart()方法开始/重新开始容器时触发该事件
    • 3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContextStop()方法停止容器时触发该事件
    • 4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件容器被关闭时,其管理的所有单例Bean都被销毁
    • 5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

    如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

  • 【问】Spring 框架中都用到了哪些设计模式?
    Note

    • Spring设计模式的详细使用案例可以阅读这篇文章:Spring中所使用的设计模式
      • 1)工厂模式:Spring使用工厂模式,通过BeanFactoryApplicationContext来创建对象
      • 2)单例模式:Bean默认为单例模式
      • 3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略;
      • 4)代理模式Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
      • 5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate
      • 6)适配器模式Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
      • 7)观察者模式Spring事件驱动模型就是观察者模式的一个经典应用。
      • 8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库

5、Spring Boot/Spring Cloud

参考Spring Boot面试题(2020最新版)【金三银四】Spring Boot面试题(2021最新版)Spring Cloud面试题(2020最新版)【金三银四】Spring Cloud面试题(2021最新版)SpringBoot面试题及答案140道(2021年最新)

1)SpringBoot部分(父项目与依赖,自动配置(javaConfig),微服务监控,可视化接口文档)

2)SpringCloud整体理解

  • 【问】为什么需要学习Spring Cloud?(单体结构不适合业务扩展,如果扩展业务,系统复杂度高)

    • 由于单体结构的应用随着系统复杂度的增高,会暴露出各种各样的问题。近些年来,微服务架构逐渐取代了单体架构,且这种趋势将会越来越流行。Spring Cloud是目前最常用的微服务开发框架,已经在企业级开发中大量的应用。
  • 【问】什么是Spring Cloud?(一系列成熟框架的有序集合,或者说是“SpringBoot微服务应用 + 分布式基础设施”,划分的功能很多很细,支持一键启动和部署,为中小型公司提供关于分布式系统基础设施的一站式解决方案,便于开发者更精准地制定优化服务方案,提高系统的可维护性)

    • Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署
    • Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
  • 【问】Spring Cloud有哪五大组件?(Eureka微服务注册与发现,Ribbon客户端负载均衡,Feign远程调用,Zuul路由/认证/限流/服务端负载均衡,Hystrix隔离熔断降级,这五个组件都来自Netflix;还有Spring Cloud Config也是个重要组件,可实现分布式统一配置管理),参考面试请不要再问我Spring Cloud底层原理

    • Spring Cloud Eureka是Spring Cloud Netflix微服务套件的一部分,基于Netflix Eureka做了二次封装,主要负责完成微服务实例的自动注册与发现,这也是微服务架构中的核心和基础功能。Eureka服务冶理体系中的三个核心角色:服务注册中心、服务提供者以及服务消费者,其中服务提供者以及服务消费者都属于Eureka Client。更多Eureka相关的内容,可以参考 Spring Cloud 之 Eureka

    • Ribbon就是一个客户端的负载均衡开源组件,是Netflix发布的开源项目。它不像服务注册中心Eureka Server、配置中心Spring Cloud Config那样独立部署,而是作为基础设施模块,几乎存在于每个Spring Cloud微服务提供者中。Feign组件自身不具备负载均衡能力,Spring Cloud Feign是通过集成Ribbon组件实现客户端的负载均衡微服务间的RPC调用以及API网关的代理请求的RPC转发调用,实际上都需要通过Ribbon来实现负载均衡。Ribbon在客户端以轮询、随机、权重等多种方式实现负载均衡。更多Ribbon相关的内容,可以参考 Spring Cloud 之 Ribbon

    • 在 Spring Cloud 中使用 Feign,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问 HTTP 请求。Feign 整合了 Ribbon 和 Hystrix,具备负载均衡、隔离、熔断与降级功能;更多Feign的相关内容,可以参考 Spring Cloud 之 Feign 简介及简单使用

    • Zuul的功能大致有:

      • 路由:将不同REST请求转发至不同的微服务提供者,其作用类似于Nginx的反向代理。同时,也起到了统一端口的作用,将很多微服务提供者的不同端口统一到了Zuul的服务端口。
      • 认证:网关直接暴露在公网上时,终端要调用某个服务,通常会把登录后的token(令牌)传过来,网关层对token进行有效性验证。如果token无效(或没有token),就不允许访问REST服务。可以结合Spring Security中的认证机制完成Zuul网关的安全认证。
      • 限流:高并发场景下瞬时流量不可预估,为了保证服务对外的稳定性,限流成为每个应用必备的一道安全防火墙。如果没有这道安全防火墙,那么请求的流量超过服务的负载能力时很容易造成整个服务的瘫痪。
      • 负载均衡:在多个微服务提供者之间按照多种策略实现负载均衡。

      更多Zuul的相关内容,可以参考 Spring Cloud 之 Zuul

    • Hystrix是Netflix公司的一款组件,其功能是:

      • 隔离:通过Hystrix的线程池去访问服务,不同的服务通过不同的线程池,实现了不同的服务调度隔离;
      • 熔断:分布式架构中的熔断器(断路器)主要用于RPC接口上,为接口安装上“保险丝”,以防止RPC接口出现拥塞时导致系统压力过大而引起的系统瘫痪,当RPC接口流量过大或者目标Provider出现异常时,熔断器及时切断故障可以起到自我保护的作用。
      • 降级:当服务不可用(服务正在等待、链接超时、网络延迟、服务器响应慢等),客户端一直等待时,调用fallback方法给客户端返回一个错误提示,不让客户端继续等待。

      更多Hystrix的相关内容,可以参考 Spring Cloud 之 Hystrix

  • 【问】关于分布式基础设施,SpringCloud如何实现?(Config,Netflix,Bus,Consul,Security,Sleuth,Stream,Task,Zookeeper,Gateway,OpenFeign)

    Spring Cloud的子项目,大致可分成两类,一类是对现有成熟框架"Spring Boot化"的封装和抽象,也是数量最多的项目;第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色。

    • Spring Cloud Config
      集中配置管理工具,分布式系统中统一的外部配置管理,默认使用Git来存储配置,可以支持客户端配置的刷新及加密、解密操作。

    • Spring Cloud Netflix(SpringCloud 5大组件):
      Netflix OSS 开源组件集成,包括EurekaHystrixRibbonFeignZuul等核心组件。

    • Spring Cloud Bus
      用于传播集群状态变化的消息总线,使用轻量级消息代理链接分布式系统中的节点,可以用来动态刷新集群中的服务配置。

    • Spring Cloud Consul
      基于Hashicorp Consul服务治理组件

    • Spring Cloud Security
      安全工具包,对Zuul代理中的负载均衡OAuth2客户端及登录认证进行支持。

    • Spring Cloud Sleuth
      Spring Cloud应用程序的分布式请求链路跟踪,支持使用ZipkinHTrace和基于日志(例如ELK)的跟踪。

    • Spring Cloud Stream
      轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。

    • Spring Cloud Task
      用于快速构建短暂、有限数据处理任务的微服务框架,用于向应用中添加功能性和非功能性的特性。

    • Spring Cloud Zookeeper
      基于Apache Zookeeper的服务治理组件

    • Spring Cloud Gateway
      API网关组件,对请求提供路由及过滤功能。

    • Spring Cloud OpenFeign

      基于Ribbon和Hystrix的声明式服务调用组件,可以动态创建基于Spring MVC注解的接口实现用于服务调用,在Spring Cloud 2.0中已经取代Feign成为了一等公民

  • 【问】SpringBoot和SpringCloud的区别?(Springboot可以快速开发单个微服务,SpringCloud是对每个微服务进行管理,全局的服务治理框架)

    • SpringBoot专注于快速方便的开发单个个体微服务
    • SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,
      为各个微服务之间提供配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务
    • SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系
    • SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。
  • 【问】Spring Cloud和SpringBoot版本对应关系?Spring Cloud和各子项目版本对应关系?

    Spring Cloud VersionSpringBoot Version
    Hoxton2.2.x
    Greenwich2.1.x
    Finchley2.0.x
    Edgware1.5.x
    Dalston1.5.x
    ComponentEdgware.SR6Greenwich.SR2
    spring-cloud-bus1.3.4.RELEASE2.1.2.RELEASE
    spring-cloud-commons1.3.6.RELEASE2.1.2.RELEASE
    spring-cloud-config1.4.7.RELEASE2.1.3.RELEASE
    spring-cloud-netflix1.4.7.RELEASE2.1.2.RELEASE
    spring-cloud-security1.2.4.RELEASE2.1.3.RELEASE
  • 【问】Spring Cloud 和dubbo区别?

    • 1)服务调用方式:dubbo是RPC(XML-RPCJSON-RPC,不同点在于数据传输的格式);springcloud 是 Rest Api(HTTP协议定义的通用动词方法GET、PUT、DELETE、POST),参考RPC、REST API深入理解
    • 2)注册中心:dubbo是zookeeper;springcloud是Eureka,也可以是zookeeper
    • 3)服务网关:dubbo本身没有实现,只能通过其他第三方技术整合;springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持熔断器(Hystrix),与git完美集成配置文件支持版本控制(Config),事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。
  • 【问】微服务的框架那么多比如:dubbo、Kubernetes,为什么就要使用Spring Cloud的呢?(Spring亲儿子,社区活跃,有更新保证,对Java开发者友好;服务拆分粒度细, 耦合度低, 提供关于分布式系统基础设施的一站式解决方案;支持跨平台并不是它的优势)

    • 优点

      • 产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善

      • 组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等;

      • Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案

      • 服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率

      • 可以更精准的制定优化服务方案,提高系统的可维护性

      • 减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发

      • 微服务可以是跨平台的,可以用任何一种语言开发

      • 适于互联网时代,产品迭代周期更短

    • 缺点

      • 微服务过多,治理成本高,不利于维护系统
      • 分布式系统开发的成本高(容错,分布式事务等)对团队挑战大

    Spring Cloud对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用Spring Cloud一站式解决方案能在从容应对业务发展的同时大大减少开发成本。

  • 【问】Kubernetes和Spring Cloud哪个部署微服务更好?(在微服务实现上,Kubernetes是基于容器编排实现的;而Spring Cloud则是通过集成各种成熟框架实现的,本身没有强调容器技术,因此将两者结合是目前最好的解决方案:spring-cloud-kubernetes),参考Kubernetes和Springcloud比较

    Note

    • kubernetes是一个容器集群管理系统,为容器化的应用程序提供部署运行,**维护,扩展,资源调度,服务发现(ETCD)**等功能。
    • SpringCloud是一个构建微服务的框架,而Kubernete是通过对运行的容器编排来实现微服务的。
    • 相同点:两者从构建微服务的角度和实现方式有很大的不同,但他们提供了构建微服务所需的全部功能
    • 区别:kubernetes:支持多语言,除了有微服务功能,还有环境生命周期,更像是一个平台,而springcloud是一个框架, 但是学习成本高。

    结合了kubernetesSpringCloud的优点,springcloud官方推出的Spring Cloud Kubernetes开源项目,链接如下: https://github.com/spring-cloud/spring-cloud-kubernetes#why-do-you-need-spring-cloud-kubernetes

    参考文档如下:你好spring-cloud-kubernetes

3、SpringCloud五大组件

a)Eureka(服务注册与发现)
  • 【问】什么是服务注册与发现?(注册中心和服务提供者双方共同维护一张服务注册表;注册中心客户端组件负责和注册中心通信(包括服务注册和发现), 远程客户端组件负责和服务提供者通信(包括RPC远程调用))
    Note
    从宏观角度,微服务架构下的系统角色可以简单分为注册中心服务提供者两个角色,而服务提供者中又包括注册中心客户端组件和 远程客户端组件

    • 服务注册是指服务提供者将自己的服务信息(如服务名、IP地址等)告知服务注册中心。
    • 服务发现注册中心客户端组件从注册中心查询所有服务提供者信息,当其他服务下线后,注册中心能够告知注册中心客户端组件这种变化。
    • 远程客户端组件服务提供者之间一般使用某种RPC通信机制来进行服务消费(不同服务提供者之间的PRC通信),常见的RPC通信方式为REST API,底层为HTTP传输协议。服务提供者通常以Web服务的方式提供REST API接口;远程客户端组件则通常以模块组件的方式完成RESTAPI的远程调用
    • 注册中心的主要功能如下:
      • 服务注册表维护:此功能是注册中心的核心,用来记录各个服务提供者实例的状态信息。注册中心提供Provider实例清单的查询和管理API,用于查询可用的Provider实例列表,管理Provider实例的上线和下线。
      • 服务健康检查:注册中心使用一定机制定时检测已注册的Provider实例,如发现某实例长时间无法访问,就会从服务注册表中移除该实例
    • 服务提供者的主要功能如下:
      • 服务注册:是指Provider微服务实例在启动时(或者定期)将自己的信息注册到注册中心的过程。
      • 心跳续约Provider实例会定时向注册中心提供“心跳”,以表明自己还处于可用的状态。当一个Provider实例停止心跳一段时间后,注册中心会认为该服务实例不可用了,就会将该服务实例从服务注册表中剔除。如果被剔除掉的Provider实例过了一段时间后又继续向注册中心提供心跳,那么注册中心会把该Provider实例重新加入服务注册表中。
      • 健康状况查询Provider实例能提供健康状况查看的API,注册中心或者其他的微服务Provider能够获取其健康状况。
    • 服务提供者的服务注册和心跳续约一般都会通过注册中心客户端组件来完成。注册中心客户端组件还有如下功能
      • 服务发现:从注册中心查询可用Provider实例清单。
      • 实例缓存:将从注册中心查询的Provider实例清单缓存到本地,不需要在每次使用时都去注册中心临时获取。

    Spring Cloud生态体系中存在多种注册中心框架,例如EurekaNacosConsulZooKeeper等。参考微服务:注册中心ZooKeeper、Eureka、Consul 、Nacos对比

    服务注册中心本质上是为了解耦服务提供者服务消费者。对于任何一个微服务,原则上都应存在或者支持多个提供者,这是由微服务的分布式属性决定的。更进一步,为了支持弹性扩缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也是无法预先确定的。因此,原本在单体应用阶段常用的静态LB机制就不再适用了,需要引入额外的组件来管理微服务提供者的注册与发现,而这个组件就是服务注册中心

  • 【问】什么是Eureka(服务通过Eureka客户端注册到EurekaService上,并保持心跳,EurekaService可对这些服务进行监控),参考 Spring Cloud 之 Eureka

  • 【问】Eureka怎么实现高可用(注册多台Eureka服务器)

  • 【问】什么是Eureka的自我保护模式,参考 Spring Cloud 之 Eureka

    • 在检测某个服务的心跳时,如果在15min内失败比例低于85%阈值,则该服务为不健康的服务,自我保护机制并不会将该服务从注册表中马上删除
    • 建议在延迟高,网络质量差的环境关闭自我保护机制
  • 【问】DiscoveryClient类的作用,参考spring cloud:eureka服务发现

    • SpringCloud中可以通过@EnableEurekaClient来将服务注册到Eureka,使得Eureka和其他服务提供者可以发现该服务实例。
    • 当一个客户端(服务提供者)注册到eureka,它会提供关于它自己的端口、地址、健康监控url和home页面等等的元数据,erueka会从每个实例接受心跳信息。如果心跳在配置的时间内失败,实例通常会从注册表中移除。
    • DiscoveryClientNetflix提供的com.netflix.discovery.DiscoveryClient,也有SpringCloud提供的org.springframework.cloud.client.discovery.DiscoveryClient,后者不只用于Eureka的服务发现和注册。
  • 【问】Eureka和ZooKeeper都可以提供服务注册与发现的功能,请说说两个的区别?(ZooKeeper有主从,选举期间注册服务挂;Eureka无主从,一个挂了部分注册服务仍然可用)

    • 1)ZooKeeper中的节点服务挂了就要选举,在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的, 选举就是改微服务做了集群,必须有一台主其他的都是从
    • 2)Eureka各个节点是平等关系(无主从),服务器挂了没关系,只要有一台Eureka就可以保证服务可用,数据都是最新的。 如果查询到的数据并不是最新的,就是因为Eureka的自我保护模式导致的。
    • 3)Eureka本质上是一个工程,而ZooKeeper只是一个进程
    • 4)Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper一样使得整个注册系统瘫痪
    • 5)ZooKeeper保证的是CP,Eureka保证的是AP;
b)Ribbon(不能单独使用)
c)Feign(配合Ribbon使用)

NoteFeign在Spring Cloud2.0升级为OpenFeign

  • 【问】什么是Feign?(Feign帮我们声明一组与Provider的REST接口相对应的本地API接口方法,本质是利用动态代理抽象成FeignClient(Java API)客户端,完成与服务提供方(Provider)REST接口的绑定;Feign + ribbon实现“微服务注册与发现”中的远程调用组件),参考Spring Cloud 之 Feign 简介及简单使用

    • Feign是在RestTemplate基础上封装的,使用注解的方式声明一组与服务提供者(Provider)Rest接口所对应的本地Java API接口方法
    • Feign将远程Rest接口抽象成一个声明式的FeignClient(Java API)客户端(个人理解应该是抽象成一个RestController,通过方法上@RequestMapping来代理远程的Provider;在API接口的方法内部通过对远程provider的服务地址进行解析,进而代理完成对远程provider发出的REST请求的处理),并且负责完成FeignClient客户端和服务提供方(Provider)的Rest接口绑定
    • Feign使用了动态代理,使用@FeignClient调用接口的本质就是调用Feign创建的动态代理,然后根据接口上的@RequestMapping等注解,来动态构造出要请求的服务的地址并对这个地址发起请求、解析响应(动态代理模拟客户端(浏览器,App)发送REST请求的过程)。
    • Feign具备可插拔的注解支持,包括Feign注解和JAX-RS注解。同时,对于Feign自身的一些主要组件,比如编码器和解码器等,也以可插拔的方式提供,在有需求时方便扩张和替换它们。
    • 在 Spring Cloud 中使用Feign,可以做到使用HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问 HTTP 请求。接下来介绍一下 Feign 的特性,具体如下:
      • 可插拔的注解支持,包括 Feign 注解和AX-RS注解。
      • 支持可插拔的 HTTP 编码器和解码器
      • 支持 Hystrix 和它的 Fallback
      • 支持 Ribbon 的负载均衡。
      • 支持 HTTP 请求和响应的压缩。
    • Feign是通过集成Ribbon组件实现客户端的负载均衡微服务间的RPC调用以及API网关的代理请求的RPC转发调用,实际上都需要通过Ribbon来实现负载均衡
    • 它整合了 RibbonHystrix,从而不需要开发者针对 Feign 对其进行整合。Feign 还提供了 HTTP 请求的模板,通过编写简单的接口和注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。Feign 会完全代理 HTTP 的请求,在使用过程中我们只需要依赖注入 Bean,然后调用对应的方法传递参数即可。
  • 【问】SpringCloud有几种调用接口方式(Feign,RestTemplate)

  • 【问】Ribbon和Feign调用服务的区别(请求调用方式不同,Ribbon需要自己构建Http请求,Feign则直接通过API接口方法帮我们处理了)

    • Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐;

    • Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

  • 【问】OpenFeign服务远程调用过程,参考Spring Cloud 之 Feign 简介及简单使用

    • 1)Provider通过注册中心客户端组件,利用DiscoveryClient从注册中心获取服务注册表
    • 2)在发起RPC远程调用时,远程调用组件中的Ribbon,通过轮询或随机方式,选择要请求的服务接口。
    • 3)远程调用组件中的Feign通过动态代理的方式实现FeignClient客户端(实例化);
    • 4)接着完成FeignClient中的API接口方法与服务提供方(Provider)提供的REST接口进行绑定(初始化);
    • 5)根据接口上的@RequestMapping等注解,来动态构造出要请求的服务的地址并对这个地址发起请求、解析响应(动态代理模拟客户端(浏览器,App)发送REST请求的过程)。
d)Hystrix(对RPC调用接口进行过载保护)
  • 【问】什么是断路器(熔断器作用在RPC接口,可以在RPC接口出现阻塞时通过熔断进行过载保护,避免资源耗尽问题;处理机制为统计RPC调用失败的比例,如果大于阈值会决定开启断路器),参考Spring Cloud 之 Hystrix

    • 分布式架构中的熔断器主要用于RPC接口上,为接口安装上“保险丝”,以防止RPC接口出现拥塞时导致系统压力过大而引起的系统瘫痪,当RPC接口流量过大或者目标Provider出现异常时,熔断器及时切断故障可以起到自我保护的作用。

    • 为什么说熔断器非常重要呢?如果没有过载保护,在分布式系统中,当被调用的远程服务无法使用时,就会导致请求的资源阻塞在远程服务器上而耗尽。很多时候刚开始可能只是出现了局部小规模的故障,然而由于种种原因,故障影响范围越来越大,最终导致全局性的后果。

    • 熔断器具体的工作机制为:统计最近RPC调用发生错误的次数,然后根据统计值中的失败比例等信息来决定是否允许后面的RPC调用继续或者快速地失败回退
      熔断器的3种状态如下:

      • 关闭(closed):熔断器关闭状态,这也是熔断器的初始状态,此状态下RPC调用正常放行
      • 开启(open):失败比例到一定的阈值之后,熔断器进入开启状态,此状态下RPC将会快速失败,然后执行失败回退逻辑
      • 半开启(half-open):在打开一定时间之后(睡眠窗口结束),熔断器进入半开启状态,小流量尝试进行RPC调用放行。如果尝试成功,熔断器就变为关闭状态RPC调用正常;如果尝试失败,熔断器就变为开启状态,RPC调用快速失败。(有点类似TCP拥塞控制中的慢开始
    • 熔断器状态之间的相互转换关系如图所示。

      在半开启状态下,允许进行一次RPC调用的尝试,如果实际调用成功,熔断器就会复位到关闭状态,回归正常的模式;但是如果这次RPC调用的尝试失败,熔断器就会返回到开启状态,一直等待到下次半开启状态。

  • 【问】什么是 Hystrix?(延迟容错的组件,包括熔断,隔离,降级和监控的功能,可防止服务雪崩)

    • Hystrix翻译过来是豪猪,由于豪猪身上长满了刺,因此能保护自己不受天敌的伤害,代表了一种防御机制。Hystrix开源框架是Netflix开源的一个延迟和容错的组件,主要用于在远程Provider服务异常时对消费端的RPC进行保护。
    • 在分布式系统,我们一定会依赖各种服务,那么这些个服务一定会出现失败的情况,就会导致雪崩Hystrix就是这样的一个工具,防雪崩利器,它具有服务降级,服务熔断,服务隔离,监控等。
      一些防止雪崩的技术。
      Hystrix有四种防雪崩方式:
      • 服务降级:接口调用失败就调用本地的方法返回一个空(或者是一个友好的提示信息)
      • 服务熔断:接口调用失败就会进入调用接口提前定义好的一个熔断的方法,返回错误信息
      • 服务隔离:隔离服务之间相互影响,通过Hystrix为隔离的服务开启一个独立的线程池来实现
      • 服务监控:在服务发生调用时,会将每秒请求数、成功请求数等运行指标记录下来(统计RPC接口调用的失败比例)。

    有关Hystrix的详细资料,可参考其官方网站:https://github.com/Netflix/Hystrix

  • 【问】谈谈服务雪崩效应(某个服务的宕机现象会蔓延到其他服务(同一台机子,不同机子不知道会不会),导致雪崩;服务就是资源,有点类似死锁)

    • 雪崩效应是在大型互联网项目中,当某个服务发生宕机时,调用这个服务的其他服务也会发生宕机,大型项目的微服务之间的调用是互通的,这样就会将服务的不可用逐步扩大到各个其他服务中,从而使整个项目的服务容机崩溃发生雪崩效应的原因有以下几点
      • 1)单个服务的代码存在bug
      • 2)请求访问量激增导致服务发生崩溃(如大型商城的枪红包,秒杀功能);
      • 3)服务器的硬件故障也会导致部分服务不可用;
  • 【问】在微服务中,如何保护服务?(服务隔离(独立线程池管理资源),服务降级(类似死锁的解决方法:及时释放资源))

    • 一般使用使用Hystrix框架,实现服务隔离来避免出现服务的雪崩效应,从而达到保护服务的效果。当微服务中,高并发的数据库访问量导致服务线程阻塞,使单个服务启机,服务的不可用会蔓延到其他服务,引起整体服务灾难性后果,使用服务降级能有效为不同的服务分配资源,一旦服务不可用则返回友好提示,不占用其他服务资源,从而避免单个服务崩溃引发整体服务的不可用。
  • 【问】服务雪崩效应产生的原因(Tomcat默认使用一个线程池处理所有请求)

    因为 Tomcat 默认情况下只有一个线程池来维护客户端发送的所有的请求,这时候某一接口在某一时刻被大量访问就会占据tomcat线程池中的所有线程,其他请求处于等待状态,无法连接到服务接口。

  • 【问】谈谈服务降级、熔断、服务隔离

    • 服务降级:当客户端请求服务器端的时候,防止客户端一直等待,不会处理业务逻辑代码,直接返回一个友好的提示给客户端。
    • 服务熔断:在服务降级的基础上更直接的一种保护方式,当在一个统计时间范围内的请求失败数量达到设定值(requestVolumeThreshold)或当前的请求错误率达到设定的错误率阀值(errorThresholdPercentage)时开启断路,之后的请求直接走fallback方法,在设定时间(sleepWindowlnMilliseconds)后尝试恢复。
    • 服务隔离Hystrix为隔离的服务开启一个独立的线程池,这样在高并发的情况下不会影响其他服务。服务隔离有线程池和信号量两种实现方式,一般使用线程池方式。
  • 【问】服务降级底层是如何实现的?(重写getFallback()实现失败回退)

    Hystrix实现服务降级的功能是通过重写HystrixCommand中的getFallback()方法,当Hystrixrun方法或construct执行发生错误时转而执行getFallback()方法。

e)Zuul(微服务网关)
  • 【问】什么是微服务网关?有什么作用?(作为网络服务架构的入口,用于统一解决Provider路由、均衡负载、权限控制(用户身份认证);微服务网关(Nginx,Zuul,Spring Cloud Gateway)可以解决跨域问题),参考Spring Cloud 之 Zuul

    • 网关相当于一个网络服务架构的入口,所有网络请求必须通过网关转发到具体的服务。作用是统一管理微服务请求,权限控制、负载均衡、路由转发、监控、安全控制黑名单和白名单等。

    • 在微服务分布式架构下,客户端(如浏览器)直接访问Provider服务提供者会存在以下问题:

      • 客户端需要进行负载均衡,从多个Provider中挑选最合适的微服务提供者。
      • 存在跨域请求时,服务端需要进行额外处理。
      • 每个服务需要进行独立的用户认证
    • 解决以上问题的手段就是使用微服务网关。微服务网关是微服务架构中不可或缺的部分,它统一解决Provider路由、均衡负载、权限控制等功能
      微服务网关的功能如图所示:

  • 【问】什么是Spring Cloud Zuul(服务网关),参考Spring Cloud 之 Zuul

    • Zuul是Netflix公司的开源网关产品,可以和EurekaRibbonHystrix等组件配合使用。

    • Zuul规则引擎和过滤器基本上可以用任何JVM语言编写,内置支持JavaGroovy

      在Spring Cloud框架中,Zuul的角色是网关,负责接收所有的REST请求(如网页端、App端等),然后进行内部转发,是微服务提供者集群的流量入口。Zuul称为内部网关,以便和Nginx外部网关相区分。

    • Zuul作为网关层,自身也是一个微服务,跟其他服务提供者一样都注册在Eureka Server,可以相互发现。

      Zuul感知到哪些Provider实例在线,同时通过配置路由规则可以将REST请求自动转发到指定的后端微服务提供者(Provider)。

    • Zuul是对SpringCloud提供的成熟的路由方案,他会根据请求的路径不同,网关会定位到指定的微服务,并代理请求到不同的微服务接口,他对外隐蔽了微服务的真正接口地址

      三个重要概念:动态路由表,路由定位,反向代理

      • 动态路由表:Zuul支持Eureka路由,手动配置路由,这俩种都支持自动更新
      • 路由定位:根据请求路径,Zuul有自己的一套定位服务规则以及路由表达式匹配
      • 反向代理:客户端请求到路由网关,网关受理之后,在对目标发送请求,拿到响应之后在给客户端;
    • Zuul功能大致有:

      • 路由转发:将不同REST请求转发至不同的微服务提供者,其作用类似于Nginx的反向代理。同时,也起到了统一端口的作用,将很多微服务提供者的不同端口统一到了Zuul的服务端口。
      • 用户认证:网关直接暴露在公网上时,终端要调用某个服务,通常会把登录后的token(令牌)传过来,网关层对token进行有效性验证。如果token无效(或没有token),就不允许访问REST服务。可以结合Spring Security中的认证机制完成Zuul网关的安全认证
      • 限流:高并发场景下瞬时流量不可预估,为了保证服务对外的稳定性,限流成为每个应用必备的一道安全防火墙。如果没有这道安全防火墙,那么请求的流量超过服务的负载能力时很容易造成整个服务的瘫痪。
      • 负载均衡:在多个微服务提供者之间按照多种策略实现负载均衡ribbon实现了provider之间的负载均衡,是客户端负载均衡;而Zuul是对来自浏览器或App的REST请求进行负载均衡,是服务器端的负载均衡)??。
    • Zuul的应用场景:

      • 对外暴露,权限校验,服务聚合,日志审计
  • 【问】网关与过滤器有什么区别(网关是对所有服务的请求进行分析过滤,过滤器是对单个服务而言)

  • 【问】常用网关框架有那些?(Nginx 、 Zuul 、Spring Cloud Gateway)

  • 【问】Zuul与Nginx有什么区别?(Zuul是内部网关,Nginx是外部网关;Zuul用Java实现,对网关操作更灵活)

    • Zuuljava 语言实现的,主要java 服务提供网关服务,尤其在微服务架构中可以更加灵活的对网关进行操作
    • Nginx 是使用 C 语言实现,性能高于Zuul ,但是实现自定义操作需要熟悉 lua 语言,对程序员要求较高,可以使用NginxZuul 集群。
  • 【问】如何设计一套API接口(内部API接口供内部服务器使用,外部API接口供外部合作单位使用)

    • 考虑到 API 接口的分类可以将 API 接口分为开发 API 接口内网 API 接口内网 API 接口用于局域网,为内部服务器提供服务。开放API 接口用于对外部合作单位提供接口调用,需要遵循 Oauth2.0 权限认证协议。同时还需要考虑安全性、幂等性等问题。
  • 【问】ZuulFilter常用有那些方法

    • Run():过滤器的具体业务逻辑
    • shouldFilter():判断过滤器是否有效
    • fifilterOrder():过滤器执行顺序
    • fifilterType():过滤器拦截位置
  • 【问】如何实现动态Zuul网关路由转发

    • 通过 path 配置拦截请求,通过 ServiceId 到配置中心获取转发的服务列表, Zuul 内部使用 Ribbon 实现本地负载均衡和转发。
  • 【问】Zuul网关如何搭建集群(使用 Nginxupstream 设置Zuul 服务集群)

    • 使用 Nginxupstream 设置Zuul 服务集群,通过 location 拦截请求并转发到 upstream ,默认使用轮询机制对Zuul集群发送请求
f)五大组件流程图

参考面试请不要再问我Spring Cloud底层原理,结合参考博客理解业务

Eureka

Ribbon

Feign

Hystrix

Zuul

g)其他
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值