JAVA 面试准备

1.客户端发出请求,请求发送至WEB容器。

2.Web容器将JSP转译成Servlet源代码。

3.Web容器将产生的源代码进行编译。

4.Web容器加载编译后的代码并执行。

5.把执行结果响应至客户端。

tomcat WEB服务器:

Tomcat8.0起已经默认nio模式,不需要做修改,BIO模式也已经抛弃了,今天主要介绍下tomcat的三种运行模式:BIO、NIO、ARP。

TOMCAT BIO、NIO、AIO适用场景分析:

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO方式适用于**【连接数目多且连接比较短】**(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO方式使用于**【连接数目多且连接比较长】(**重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

二.JDBC连接数据库的流程

JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API。

2.1 确定连接数据库的类型,然后选用对应数据库类型jar包;

2.2 Class.forName(“com.mysql.jdbc.Driver”)

2.3 DriverManager类中getConnection方法获取到对应数据库连接对象

​ Connection connectMySQL = DriverManager.geiConnection(“jdbc:mysql://localhost:3306/myuser","root" ,"root" );

2.4 通过连接对象,创建一个preparedStatement

2.5 执行SQL语句,最常用的是:executeQuery() 、executeUpdate()

2.6 如果是查询,遍历结果集;

2.7 处理异常,关闭JDBC对象资源
操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声
明顺序相反:

三.SpringMVC执行的流程

1、什么是Spring MVC ?简单介绍下你对springMVC的理解?

​ Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

2、SpringMVC的执行流程?

image-20200518175137398

1.用户向服务器发送请求,请求被前端控制DispatcherServlet捕获; 2.DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回; 3.DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。 4.提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中 5.Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象; 6.根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver) 返回给 DispatcherServlet ; 7.ViewResolver 结合Model和View,来渲染视图 8.将渲染结果返回给客户端。

面试可以如下回答:

1、用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获(捕获) 2、DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;(查找handler) 3、DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller), Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象(执行handler) 4、DispatcherServlet 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver) (选择ViewResolver) 5、通过ViewResolver 结合Model和View,来渲染视图,DispatcherServlet 将渲染结果返回给客户端。(渲染返回) ————————————————快速记忆技巧:

核心控制器捕获请求、查找Handler、执行Handler、选择ViewResolver,通过ViewResolver渲染视图并返回

四.Redis

4.1 概念

redis是一个开源的、高性能的、基于内存的key-value NoSQL数据库,数据类型以及适用场景都很丰富

4.2 使用redis的好处

数据存储在内存中,读写速度快,且支持持久化
丰富的数据类型和特性支持,可支持缓存、消息、分布式锁等多种业务场景
支持事务,操作都是原子性的,要么全部执行,要么不执行,虽然是伪事务,失败无法回滚

4.3 redis的数据类型

redis的常用数据类型有:string、list、set、Sorted set、hash五种
string:最大容量512M
list:通过双端链表或压缩列表实现
hash:通过压缩列表或哈希表实现
	1.当使用ziplist,也就是压缩列表作为底层实现时,新增的键值对是保存到压缩列表的表尾。key和value依次作为列表元素存放
	2.hashtable 编码的哈希表对象底层使用字典数据结构,哈希对象中的每个键值对都使用一个字典键值对。
	3.在前面介绍压缩列表时,我们介绍过压缩列表是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,相对于字典数据结构,压缩列表用于元素个数少、元素长度小的场景。其优势在于集中存储,节省空间。
set:intset或hashtable
	1.intset 编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合中。
	2.hashtable 编码的集合对象使用 字典作为底层实现,字典的每个键都是一个字符串对象,这里的每个字符串对象就是一个集合中的元素,而字典的值则全部设置为 null。这里可以类比Java集合中HashSet 集合的实现,HashSet 集合是由 HashMap 来实现的,集合中的元素就是 HashMap 的key,而 HashMap 的值都设为 null
zset:
	1.ziplist 编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值。并且压缩列表内的集合元素按分值从小到大的顺序进行排列,小的放置在靠近表头的位置,大的放置在靠近表尾的位置。
	2.skiplist 编码的有序集合对象使用 zet 结构作为底层实现,一个 zset 结构同时包含一个字典和一个跳跃表

4.4 为什么redis的单线程性能仍然很好

redis是单进程的,但并不是单线程的,只是redis的事件分派器是单线程模型,所以真正的数据处理是单线程的。因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了
1.纯内存操作
2.避免了不必要的线程上下文切换和锁开销
3.基于非阻塞的io多路复用

五.JVM的内存结构

5.1 JVM管理的内存结构是怎样的?

Java虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域。

image-20200518181830343

5.1.1 PC寄存器

​ 是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器,指向下一个将要执行的指令代码,由执行引擎来读取下一条指令。更确切的说,一个线程的执行,是通过字节码解释器改变当前线程的计数器的值,来获取下一条需要执行的字节码指令,从而确保线程的正确执行。

为了确保线程切换后(上下文切换)能恢复到正确的执行位置,每个线程都有一个独立的程序计数器,各个线程的计数器互不影响,独立存储。也就是说程序计数器是线程私有的内存。

如果线程执行 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是 Native 方法,计数器值为Undefined。

程序计数器不会发生内存溢出(OutOfMemoryError即OOM)问题。

5.1.2 JVM 中的栈

包括 Java 虚拟机栈和本地方法栈,两者的区别就是,Java 虚拟机栈为 JVM 执行 Java 方法服务,本地方法栈则为 JVM 使用到的 Native 方法服务。两者作用是极其相似的,本文主要介绍 Java 虚拟机栈,以下简称栈。

JDK 中有很多方法是使用 Native 修饰的。Native 方法不是以 Java 语言实现的,而是以本地语言实现的(比如 C 或 C++)。个人理解Native 方法是与操作系统直接交互的。比如通知垃圾收集器进行垃圾回收的代码 System.gc(),就是使用 native 修饰的。说白了就是为底层交互留的后门。

public final class System {
    public static void gc() {
        Runtime.getRuntime().gc();
    }
}
 
public class Runtime {
    //使用native修饰
     public native void gc();

限定仅在表头进行插入和删除操作的线性表。即压栈(入栈)和弹栈(出栈)都是对栈顶元素进行操作的。所以栈是后进先出的。

栈是线程私有的,他的生命周期与线程相同。每个线程都会分配一个栈的空间,即每个线程拥有独立的栈空间

可能出现的异常有:StackOverflowError:栈溢出错误 和OutOfMemoryError:内存不足

5.1.3 堆

堆是Java虚拟机所管理的内存中最大的一块存储区域。堆内存被所有线程共享。主要存放使用new关键字创建的对象。所有对象实例以及数组都要在堆上分配。垃圾收集器就是根据GC算法,收集堆上对象所占用的内存空间(收集的是对象占用的空间而不是对象本身)。

Java堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为伊甸园(Eden)和幸存区(Survivor区);幸存区又分为From Survivor空间和 To Survivor空间。

年轻代存储“新生对象”,我们新创建的对象存储在年轻代中。当年轻内存占满后,会触发Minor GC,清理年轻代内存空间。

老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发Full GC。

注:Full GC是清理整个堆空间,包括年轻代和老年代。如果Full GC之后,堆中仍然无法存储对象,就会抛出OutOfMemoryError异常。

5.1.4 方法区

​ 方法区同 Java 堆一样是被所有线程共享的区间,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。更具体的说,静态变量+常量+类信息(版本、方法、字段等)+运行时常量池存在方法区中。常量池是方法区的一部分

JDK1.8 使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。元空间两个参数:

  1. MetaSpaceSize:初始化元空间大小,控制发生GC阈值
  2. MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存

六.Ajax

什么情况造成跨域?

同源策略限制 不同源会造成跨域。以下任意一种情况不同,都是不同源。

http://www.baidu.com/8080/index.html

http:// 协议不同
www 子域名不同
baidu.com 主域名不同
8080 端口号不同
www.baidu.com ip地址和网址不同
1.jsonp 只能解决get跨域(问的最多)
  • 原理:动态创建一个script标签。利用script标签的src属性不受同源策略限制。因为所有的src属性和href属性都不受同源策略限制。可以请求第三方服务器数据内容。
  • 步骤:
  1. 去创建一个script标签
  2. script的src属性设置接口地址
  3. 接口参数,必须要带一个自定义函数名 要不然后台无法返回数据。
  4. 通过定义函数名去接收后台返回数据
//去创建一个script标签
var  script = document.createElement("script");
//script的src属性设置接口地址 并带一个callback回调函数名称
script.src = "http://127.0.0.1:8888/index.php?callback=jsonpCallback";
//插入到页面
document.head.appendChild(script);
//通过定义函数名去接收后台返回数据
function jsonpCallback(data){
    //注意  jsonp返回的数据是json对象可以直接使用
    //ajax  取得数据是json字符串需要转换成json对象才可以使用。
}
2:CORS:跨域资源共享
  • 原理:服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求
  • 限制:浏览器需要支持HTML5,可以支持POST,PUT等方法兼容ie9以上
需要后台设置
Access-Control-Allow-Origin: *              //允许所有域名访问,或者
Access-Control-Allow-Origin: http://a.com   //只允许所有域名访问
3:设置 document.domain
  • 原理:相同主域名不同子域名下的页面,可以设置document.domain让它们同域
  • 限制:同域document提供的是页面间的互操作,需要载入iframe页面
// URL http://a.com/foo
var ifr = document.createElement('iframe');
ifr.src = 'http://b.a.com/bar'; 
ifr.onload = function(){
    var ifrdoc = ifr.contentDocument || ifr.contentWindow.document;
    ifrdoc.getElementsById("foo").innerHTML);
};

ifr.style.display = 'none';
document.body.appendChild(ifr);
4:用Apache做转发(逆向代理),让跨域变成同域

七.SpringMVC面试题

1、什么是Spring MVC ?简单介绍下你对springMVC的理解?

Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

2.2、SpringMVC的流程?

1570167624269.png

(1)用户发送请求至前端控制器DispatcherServlet; (2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle; (3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet; (4)DispatcherServlet 调用 HandlerAdapter处理器适配器; (5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器); (6)Handler执行完成返回ModelAndView; (7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet; (8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析; (9)ViewResolver解析后返回具体View; (10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中) (11)DispatcherServlet响应用户。

详细案例细化步骤:

1.DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

我们假设请求的url为 : http://localhost:8080/SpringMVC/hello
如上url拆分成三部分:
http://localhost:8080服务器域名
SpringMVC部署在服务器上的web站点
hello表示控制器
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

2.HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。 3.HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。 HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。 4.HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。Handler让具体的Controller执行。 5.Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。 6.HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。 7.DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。 视图解析器将解析的逻辑视图名传给DispatcherServlet。 8.DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。最终视图呈现给用户。

3、Springmvc的优点:

(1)可以支持各种视图技术,而不仅仅局限于JSP;

(2)与Spring框架集成(如IoC容器、AOP等);

(3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。

(4) 支持各种请求资源的映射策略。

  1. 轻量级,简单易学
  2. 高效 , 基于请求响应的MVC框架
  3. 与Spring兼容性好,无缝结合
  4. 约定优于配置
  5. 功能强大:RESTful、数据验证、格式化、本地化、主题等
  6. 简洁灵活

4、 SpringMVC常用的注解有哪些?

@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。

@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户

5、SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代?

答:一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外,一般不用别的注解代替。

6、如果在拦截请求中,我想拦截get方式提交的方法,怎么配置?

答:可以在@RequestMapping注解里面加上method=RequestMethod.GET。 ————————————————

八.Shiro

Shiro框架介绍 是一个轻量级的安全框架,主要提供了 授权、认证、加密、会话管理这几个功能。

如何获得Subject对象

shiro安全数据源有哪些: 1.数据库 2.静态ini文件 3.session

Shiro运行流程 比如一个登陆流程: 1、Subject对象理解为一个用户,首先调用Subject.login(token)进行登录,他会委托给SecurityManager 2、SecurityManager负责真正的身份验证逻辑,它会委托给Authenticator进行身份验证; 3、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有就返回认证失败,有的话就继续执行操作。

Shiro 的优点 简单的身份认证, 支持多种数据源 非常简单的加密 API 对角色的简单的授权, 支持细粒度的授权(方法级) 支持一级缓存,以提升应用程序的性能; 内置的基于 POJO 企业会话管理, 适用于 Web 以及非 Web 的环境 不跟任何的框架或者容器捆绑, 可以独立运行

比较 SpringSecurity 和 Shiro 相比 Spring Security, Shiro 在保持强大功能的同时, 使用简单性和灵活性 SpringSecurity: 即使是一个一个简单的请求,最少得经过它的 8 个Filter SpringSecurity 必须在 Spring 的环境下使用 初学 Spring Security, 曲线还是较大, 需要深入学习其源码和框架, 配置起来也较费力.

简述 Shiro 的3个核心组件 1.Subject 正与系统进行交互的人, 或某一个第三方服务. 所有 Subject 实例都被绑定到一个SecurityManager 上。

2.SecurityManager Shiro 架构的心脏, 用来协调内部各安全组件, 管理内部组件实例, 并通过它来提供安全管理的各种服务. 当 Shiro 与一个 Subject 进行交互时, 实质上是幕后的SecurityManager 处理所有繁重的 Subject 安全操作。

3.Realms 本质上是一个特定安全的 DAO. 当配置 Shiro 时, 必须指定至少一个 Realm 用来进行身份验证和授权. Shiro 提供了多种可用的 Realms 来获取安全相关的数据. 例如关系数据库(JDBC), INI 及属性文件等. 可以定义自己 Realm 实现来代表自定义的数据源。

Shiro认证过程 ①. 应用程序代码调用 Subject.login 方法,传递创建好的包含终端用户的 Principals(身份)和 Credentials(凭证)的 AuthenticationToken 实例

②. Subject 实例委托应用程序的 SecurityManager 通过调用securityManager.login(token) 开始真正的验证。 Subject 实例(通常为 DelegatingSubject或它的子类)

③. SubjectManager 接收 token,调用内部的 Authenticator 实例调用 authenticator.authenticate(token).Authenticator 通常是一个 ModularRealmAuthenticator 实例, 支持在身份验证中协调一个或多个Realm 实例

④. 如果应用程序中配置了一个以上的 Realm, ModularRealmAuthenticator 实例将利用配置好的AuthenticationStrategy 来启动 Multi-Realm 认证尝试。在Realms 被身份验证调用之前、调用期间、调用之后,AuthenticationStrategy 被调用使其能够对每个Realm 的结果作出反应。(AuthenticationStrategy都会被调用,对每个Realm 的结果作出反应)

⑤. 每个配置的 Realm 用来帮助看它是否支持提交的 AuthenticationToken. 如果支持, 那么支持 Realm 的 getAuthenticationInfo 方法将会伴随着提交的 token 被调用. getAuthenticationInfo 方法有效地代表一个特定 Realm 的单一的身份验证尝试。

Shiro授权过程 ①. 应用程序或框架代码调用任何 Subject 的hasRole*, checkRole*, isPermitted*,或者checkPermission*方法的变体, 传递任何所需的权限

②. Subject 的实例 调用securityManager 的对应的方法. Subject 实例(通常为 DelegatingSubject或它的子类)

③. SecurityManager 调用 org.apache.shiro.authz.Authorizer 接口的对应方法.默认情况下,authorizer 实例是一个 ModularRealmAuthorizer 实例, 它支持协调任何授权操作过程中的一个或多个Realm 实例

④. 每个配置好的 Realm 被检查是否实现了相同的 Authorizer 接口. 如果是, Realm 各自的 hasRole*, checkRole*,isPermitted*,或 checkPermission* 方法将被调用。

Shiro 如何自实现认证 Shiro 的认证过程由 Realm 执行,SecurityManager 会调用 org.apache.shiro.realm.Realm 的 getAuthenticationInfo(AuthenticationToken token) 方法。 实际开发中, 通常提供 org.apache.shiro.realm.AuthenticatingRealm 的实现类, 并在该实现类中提供 doGetAuthenticationInfo(AuthenticationToken token)方法的具体实现

如何实现自实现授权 实际开发中, 通常提供 org.apache.shiro.realm.AuthorizingRealm 的实现类,并提供 doGetAuthorizationInfo(PrincipalCollection principals) 方法的具体实现

如何配置在 Spring 中配置使用 Shiro 1、在 web.xml 中配置 Shiro 的 Filter 2、在 Spring 的配置文件中配置 Shiro 3、配置自定义 Realm:实现自定义认证和授权 4、配置 Shiro 实体类使用的缓存策略 5、配置 SecurityManager 6、配置保证 Shiro 内部 Bean 声明周期都得到执行的 Lifecycle Bean 后置处理器 7、配置AOP 式方法级权限检查 8、配置 Shiro Filter

九.Spring事务

	Spring中注解的方式@Transactional标注事务方法。为了将方法定义为支持事务处理,可以在方法上添加@Transactional注解。根据Spring AOP基于代理机制,只能标注公有方法。如果在类上标注@Transactional注解,那么这个类中所有公有方法都会被定义为支持事务。

编程式事务管理

 将事务管理代码嵌到业务方法中来控制事务的提交和回滚

缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码 声明式事务管理

一般情况下比编程式事务好用。将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

十.高并发相关

如何设计一个高并发系统?

详细比较

可以从经历参与的房地产开发商的云售楼软件说下:

PHP的优点

  1. 简单, 开发效率高,是Java两倍以上,能快速出产品,并快速迭代更新.
  2. 优化简单,多数企业很容易获取较好的性能

PHP的缺点

  1. 由于PHP-FPM IO 阻塞的特点,一个进程同时只能处理一个请求,像京东,小米这样的抢购情况下,大并发处理能力较差。
  2. 现在大学生学php的人少,不好招人。

Java的优点

  1. 由于Tomcat非阻塞的特点,一个线程可同时处理多个请求,大并发性能较好
  2. 大学生学Java的人多,好招人

Java的缺点

  1. 配置复杂,开发效率较PHP低

  2. 很占内存,性能优化复杂,如果优化不好,性能反而不及PHP

其实许多初创公司的访问量,远远没有达到PHP或是JAVA撑不住的地步.

这里假设公司面临像小米或是12306这样的大并发访问。

大并发情况下,IO阻塞与非阻塞,性能差距是很大的. 查看一下Nginx, tomcat, php-fpm 的源码,我们发现,Nginx, tomcat使用的是非阻塞多路复用机制(对于linux, 底层就是epoll),一个线程可同时处理多个请求,而php-fpm是阻塞机制,一个进程同时只能处理一个请求。 (php-fpm 有个配置可以使用epoll,只适用于master管理进程,对应worker进程还是阻塞的) 处理大并发的能力排名nginx第一,tomcat第二,php-fpm第三。

对于像12306这样的大并发情况,无论tomcat还是php-fpm都是瓶颈。 只有在nginx上做文章. 顺便说下对于底层的IO多路复用,FreeBSD 的kqueue 性能要优于Linux 的epoll。

一般可以分为6点:

该怎么回答这个问题:

可以分为以下 6 点:

系统拆分

缓存

MQ

分库分表

读写分离

ElasticSearch

img

1.系统拆分

将一个系统拆分为多个子系统,用 dubbo/SpringCloud 来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,不也可以扛高并发么。

2.缓存

缓存,必须得用缓存。大部分的高并发场景,都是读多写少,那你完全可以在数据库和缓存里都写一份,然后读的时候大量走缓存不就得了。毕竟人家 redis 轻轻松松单机几万的并发。所以你可以考虑考虑你的项目里,那些承载主要请求的读场景,怎么用缓存来抗高并发。页面静态化,也是缓存策略的一种。

3.MQ

MQ,必须得用 MQ。可能你还是会出现高并发写的场景,比如说一个业务操作里要频繁搞数据库几十次,增删改增删改,疯了。那高并发绝对搞挂你的系统,你要是用 redis 来承载写那肯定不行,人家是缓存,数据随时就被 LRU 了,数据格式还无比简单,没有事务支持。所以该用 mysql 还得用 mysql 啊。那你咋办?用 MQ 吧,大量的写请求灌入 MQ 里,排队慢慢玩儿,后边系统消费后慢慢写,控制在 mysql 承载范围之内。所以你得考虑考虑你的项目里,那些承载复杂写业务逻辑的场景里,如何用 MQ 来异步写,提升并发性。MQ 单机抗几万并发也是 ok 的,这个之前还特意说过。

4.分库分表

分库分表,数据库集群,可能到了最后数据库层面还是免不了抗高并发的要求,好吧,那么就将一个数据库拆分为多个库,多个库来扛更高的并发;然后将一个表拆分为多个表,每个表的数据量保持少一点,提高 sql 跑的性能。

5.读写分离

读写分离,这个就是说大部分时候数据库可能也是读多写少,没必要所有请求都集中在一个库上吧,可以搞个主从架构,主库写入,从库读取,搞一个读写分离。读流量太多的时候,还可以加更多的从库

6.Nginx

负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。

7.ElasticSearch

Elasticsearch,简称 es。es 是分布式的,可以随便扩容,分布式天然就可以支撑高并发,因为动不动就可以扩容加机器来扛更高的并发。那么一些比较简单的查询、统计类的操作,可以考虑用 es 来承载,还有一些全文搜索类的操作,也可以考虑用 es 来承载。

上面的 6 点,基本就是高并发系统肯定要干的一些事儿,大家可以仔细结合之前讲过的知识考虑一下,到时候你可以系统的把这块阐述一下,然后每个部分要注意哪些问题,之前都讲过了,你都可以阐述阐述,表明你对这块是有点积累的。

说句实话,毕竟你真正厉害的一点,不是在于弄明白一些技术,或者大概知道一个高并发系统应该长什么样?其实实际上在真正的复杂的业务系统里,做高并发要远远比上面提到的点要复杂几十倍到上百倍。你需要考虑:哪些需要分库分表,哪些不需要分库分表,单库单表跟分库分表如何 join,哪些数据要放到缓存里去,放哪些数据再可以扛住高并发的请求,你需要完成对一个复杂业务系统的分析之后,然后逐步逐步的加入高并发的系统架构的改造,这个过程是无比复杂的,一旦做过一次,并且做好了,你在这个市场上就会非常的吃香。

其实大部分公司,真正看重的,不是说你掌握高并发相关的一些基本的架构知识,架构中的一些技术,RocketMQ、Kafka、Redis、Elasticsearch,高并发这一块,你了解了,也只能是次一等的人才。对一个有几十万行代码的复杂的分布式系统,一步一步架构、设计以及实践过高并发架构的人,这个经验是难能可贵的。

十一.数据库连接池

什么情况下使用连接池?

对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。

使用连接池的好处

  1. 连接复用。通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
  2. 对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。

连接池的实现

数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。

外部使用者可通过 getConnection 方法获取连接,使用完毕后再通过 close 方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。

Java 中有一个 DataSource 接口, 数据库连接池就是 DataSource 的一个实现

一、JDBC数据库连接池的必要性

  在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤:

  ①在主程序(如servlet、beans)中建立数据库连接。   ②进行sql操作   ③断开数据库连接。

  这种模式开发,存在的问题:

  ①普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用.若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。  ②对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。  ③这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。

二、数据库连接池(connection pool)

数据库连接池简单介绍

  为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。

  数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。

  数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

  数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

​ 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。如下图所示:

image-20200602094248738

数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:

  1. 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
  2. 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
  3. 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.

数据库连接池技术的优点

资源重用:

  ①由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。

  更快的系统反应速度:  数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间

  新的资源分配手段:  对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源

  统一的连接管理,避免数据库连接泄露:  在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露

十二:HashMap和HashSet的区别

1、为什么用HashMap?

  • HashMap是一个散列桶(数组和链表),它存储的内容是键值对(key-value)映射
  • HashMap采用了数组和链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改
  • HashMap是非synchronized,所以HashMap很快
  • HashMap可以接受null键和值,而Hashtable则不能(原因就是equlas()方法需要对象,因为HashMap是后出的API经过处理才可以)

2、HashMap的工作原理是什么?

  • HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,计算并返回的hashCode是用于找到Map数组的bucket位置来储存Node 对象。这里关键点在于指出,HashMap是在bucket中储存键对象和值对象,作为Map.Node 。

image-20200604084312748

十三.bootstrap常见面试题

1.为什么使用bootstrap?

Bootstrap具有移动设备优先、浏览器支持良好、容易上手、响应式设计等优点,所以Bootstrap被广泛应用。

2.head 中添加 viewport meta 标签

为了让 Bootstrap 开发的网站对移动设备友好,确保适当的绘制和触屏缩放,需要在网页的 head 之中添加 viewport meta 标签,

width属性控制设备的宽度。假设您的网站将被带有不同屏幕分辨率的设备浏览,那么将它设置为device-width;可以确保它能正确呈现在不同设备上。

initial-scale=1.0;确保网页加载时,以 1:1 的比例呈现,不会有任何的缩放。

在移动设备浏览器上,通过为viewport meta标签添加user-scalable=no可以禁用其缩放(zooming)功能。

通常情况下,maximum-scale=1.0 与 user-scalable=no 一起使用。这样禁用缩放功能后,用户只能滚动屏幕,就能让您的网站看上去更像原生应用的感觉,所以要加上以下代码:

1

<metaname="viewport" content="width=device-width,initial-scale=1">

3.对于各类尺寸的设备,Bootstrap设置的class前缀分别是什么?

超小设备手机(<768px):.col-xs-

小型设备平板电脑(>=768px):.col-sm-

中型设备台式电脑(>=992px):.col-md-

大型设备台式电脑(>=1200px):.col-lg-

4.Bootstrap如何设置响应式表格?

增加class="table-responsive"

5、使用Bootstrap创建垂直表单的基本步骤?

(1)向父<form>元素添加role="form";

(2)把标签和控件放在一个带有class="form-group"的<p>中,这是获取最佳间距所必需的;

(3)向所有的文本元素<input>、<textarea>、<select>添加class="form-control"。

6、使用Bootstrap创建水平表单的基本步骤?

(1)向父<form>元素添加class="form-horizontal";

(2)把标签和控件放在一个带有class="form-group"的<p>中;

(3)向标签添加class="control-label"。

7、使用Bootstrap如何创建表单控件的帮助文本?

增加class="help-block"的span标签或p标签。

8、使用Bootstrap激活或禁用按钮要如何操作?

激活按钮:给按钮增加.active的class

禁用按钮:给按钮增加disabled="disabled"的属性

9、Bootstrap有哪些关于<img>的class?

(1).img-rounded 为图片添加圆角

(2).img-circle 将图片变为圆形

(3).img-thumbnail 缩略图功能

(4).img-responsive 图片响应式 (将很好地扩展到父元素)

10、Bootstrap中有关元素浮动及清除浮动的class?

(1)class="pull-left" 元素浮动到左边

(2)class="pull-right" 元素浮动到右边

(3)class="clearfix" 清除浮动

11、除了屏幕阅读器外,其他设备上隐藏元素的class?

class="sr-only"

12、Bootstrap如何制作下拉菜单?

(1)将下拉菜单包裹在class="dropdown"的<p>中;

(2)在触发下拉菜单的按钮中添加:class="btn dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown"

(3)在包裹下拉菜单的ul中添加:class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1"

(4)在下拉菜单的列表项中添加:role="presentation"。其中,下拉菜单的标题要添加class="dropdown-header",选项部分要添加tabindex="-1"。

13、Bootstrap如何制作按钮组?以及水平按钮组和垂直按钮组的优先级?

(1)用class="btn-group"的<p>去包裹按钮组;class="btn-group-vertical"可设置垂直按钮组。

(2)btn-group的优先级高于btn-group-vertical的优先级。

14、Bootstrap如何设置按钮的下拉菜单?

在一个 .btn-group 中放置按钮和下拉菜单即可。

15、Bootstrap中的输入框组如何制作?

(1)把前缀或者后缀元素放在一个带有class="input-group"中的<p>中;

(2)在该<p>内,在class="input-group-addon"的<span>里面放置额外的内容;

(3)把<span>放在<input>元素的前面或后面。

16、Bootstrap中的导航都有哪些?

(1)导航元素:有class="nav nav-tabs"的标签页导航,还有class="nav nav-pills"的胶囊式标签页导航;

(2)导航栏:class="navbar navbar-default" role="navigation";

(3)面包屑导航:class="breadcrumb"

17、Bootstrap中设置分页的class?

默认的分页:class="pagination"

默认的翻页:class="pager"

18、Bootstrap中显示标签的class?

class="label"

19、Bootstrap中如何制作徽章?

<span class="badge">26</span>

20、Bootstrap中超大屏幕的作用是什么?

设置class="jumbotron"可以制作超大屏幕,该组件可以增加标题的大小并增加更多的外边距。

十四.jQuery面试题

jQuery的美元符号$有什么作用

  美元符号$是“jQuery”的别名,它是jQuery的选择器,如下:

$(document).ready(function(){ //其中可以用jQuery代替$

});

1
2
3

  在jQuery中,可以通过这个美元符号实现各种灵活的DOM元素选择,例如$("#main")既“选中id为main的元素”。

body中的onload()函数和jQuery中的document.ready()有什么区别?

  document.ready()函数在页面DOM元素加载完以后就会被调用,而onload()函数则要在所有的关联资源(包括图像、音频)加载完毕之后才会被调用。   我们可以在页面中使用多个document.ready(),但只能使用一次onload()。

使用jQuery将页面上的所有元素边框设置为2px宽的红色的虚线

<script language="javascript" type="text/javascript"> $("*").css("border","2px dotted red"); </script>

1
2
3

jQuery中$.get()提交和$.post()提交有区别吗?

  $.get()方法使用get方法来进行异步请求的,$.post()方法使用post方法来进行异步请求的;
  get请求会将参数跟在URL后进行传递,而post请求则是作为HTTP消息的实体内容发送给Web服务器的;
  get方式传输的数据大小不能超过2KB,而post要大得多;
  get方式的请求的数据会被浏览器缓存起来,因此有安全问题。

jQuery中如何去操作样式?

  addClass()追加样式;   removeClass()删除样式;   toggleClass()切换样式。 jQuery 中的方法链是什么?使用方法链有什么好处?

方法链是对一个方法返回的结果调用另一个方法,这使得代码简洁明了,同时由于只对 DOM 进行了一轮查找,性能方面更加出色。