Java 面试之--开放性问题
服务器宕机,如何排查问题?
服务器宕机可以从以下几个方面考虑
服务器性能
:服务器性能原因是普遍的故障因素,当数据处理能力更不上的时候,是很容易发生宕机故障。资源耗尽
:当服务器的资源发生耗尽情况的时候,也会出现宕机的故障。例如受到了网络攻击,访客的暴增,高负载等情况就是主要的原因。- 服务器内部系统错误:服务器内部系统错误也是发生宕机的主要因素,例如链接中断,无法链接的情况。
排查问题
控制台使用Jconsole 连接到本地服务器或者远程服务器,查看内存、线程的使用情况;
使用jps 或者 ps -ef | grep java 查看java线程PID;
然后使用jstack 查看并分析 Thread dump 的详细信息。
PS:开始发现问题了
- 如果发现有大量的线程都处在 Wait on condition,说明发生了网络瓶颈,大量的http连接没有被释放或者存在大量的CLOSE_WAIT状态,所以需要定时释放空闲的连接。
- 如果发现JVM 垃圾回收线程进行过多的GC,则需要进行JVM参数调优,增加年轻代的大小。
- 如果发现内存泄漏,就可能就是代码的问题。比如 ThreadLocal 在finnal模块里面没有被手动的remove,ThreadLocalMap的key被回收了,value没有被回收。
- 也有可能会发现死锁。
- 也有可能是多个线程访问共享资源、或者进行频繁的系统调用,导致进行频繁的线程上下文切换,因为上下文切换,会导致用户态到内核态的拷贝,很耗费系统的性能。
- 也有可能是数据库连接达到上限,增加JDBC 最大连接池即可。
CPU100% 如何排查问题?
- 使用top查看系统性能数据, 可以找出CPU使用率较高的进程。(Top命令类似于Windows的资源管理器,它可以将进程的实时状态展示出来,包括CPU信息)。
top
,然后查看command 是java的应用对应的PID。 - 使用 top -H -p pid 将这个进程的线程信息显示出来。可以看见CPU使用率
- CPU 利用率100%,可能是线程出现死循环,也有可能是GC出现问题,使用
jstat 线程ID
查看GC情况 - CPU利用率不停的变化,说明不是某一个线程导致的CPU偏高
- CPU 利用率100%,可能是线程出现死循环,也有可能是GC出现问题,使用
- 使用
jstack pid > pid.log
将线程栈 Dump 到日志文件中。 - 通过栈中的日志查看问题所在。
- 注意dump出来的线程ID 是16进制的,而使用top命令看到的线程ID是十进制,所以需要使用printf命令转化一下进制,然后使用16进制的ID去dump里找到对应的线程。
jstack查看某个Java进程内的线程堆栈信息
内存泄漏如何解决? OOM?
内存泄漏就是OOM,可能出现在是堆、栈、永久代里面。需要使用Java VisualVM对Java Heap进行内存监视。出现的原因有可能是无用对象一直占用内存,而得不到释放。(ThreadLocal)
- 使用top 查看java进程的pid,和cpu使用情况
- 使用jstat -gcutil pid 查看GC使用情况,包括年轻代、老年代的使用情况
- 使用jmap -dump:format=b,file=mydump.bin pid 命令dump出JVM内存快照
- 使用内存泄漏工具(jdk自带的JVisualVM)检查这个dump文件
- 找到占用内存最多的大对象。然后分析对应的代码问题。
jmap 可以查看堆内存使用状况
网页请求很慢,如何排查问题?
打开一个网页并显示给用户所耗费的时间=网络传输的时间+后台数据处理的时间+浏览器DOM渲染的时间
网络传输
- 首先排除是否是自己网络的原因,本地网络差,访问速度自然比较慢。
- 排除是否是访问的服务器的原因,用 ping 命令查看是否连通。
- 可以通过阿里云监控平台,查看是否是服务器的性能原因,如果性能原因可以选择对服务器进行硬件、软件升级。
- 浏览器通过F12-> netWork 可以查看加载的资源信息,包括成功的、失败的资源、加载资源耗费的时间。
- 对资源耗费的时间进行降序排序,查看耗费时间比较多的资源,可能原因是请求的文件比较大,比如高清的图片。
网络传输部分可以从:HTTPS安全传输、TCP的三次握手、四次挥手、DNS域名解析、ARP地址解析、PING连通性检测、区域覆盖的问题(国外的服务器,在国内进行访问) 进行讲述。
后台数据处理
服务器接收到客户端的请求,如果涉及到数据库,会进行数据库的查询,如果使用了select * 、多表关联查询、一条慢查询的sql语句、查询结结果返回大量的数据,会导致后台数据处理缓慢,需要进行慢查询优化 或者 只返回必要的数据和字段。
也有可能是算法时间复杂度比较高,比如排序算法,如果选择插入算法,时间复杂度高达到$O(N^2)$。
DOM渲染
浏览器收到返回的http响应数据,需要进行DOM渲染,然后再展示给用户。
- 浏览器解析html源码,然后创建一个DOM树。在DOM树中,每一个HTML标签都有一个对应的节点。DOM树的根节点就是documentElement,对应的是html标签。
- 加载外部的js脚本文件和css样式文件,
会进行HTTP请求
。 - 创建并执行js 脚本代码。
- 浏览器解析CSS代码,计算出最终的样式数据。
- 创建完DOM树并得到最终的样式数据之后,构建一个渲染树。
- 当渲染树创建完成之后,浏览器就可以根据渲染树直接把页面绘制到屏幕上。
- 加载图片等外部文件。(大文件或者高清图片会耗费较多时间)
- 页面加载完毕
渲染树和DOM树有点像,但是有区别。DOM树完全和html标签一一对应,而渲染树会忽略不需要渲染的元素(head、display:none的元素)。渲染树中每一个节点都存储着对应的CSS属性。
如何实现大文件上传?
基本术语
秒传
对上传的文件做MD5校验,如果发现服务器上面有这个文件,则自己返回这个文件的链接地址,而不是真正的上传文件。
可以使用redis来记录文件上传的状态,其中Key为MD5校验码,value是表示是否上传成功的标志。
分片上传
分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。
断点续传
上传或下载文件时如果碰到网络故障,可以从已经上传或下载的部分开始继续上传或者下载未完成的部分,而没有必要从头开始上传或者下载。
实现步骤:
将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
计算每块文件的MD5值,用于实现秒传。
按照一定的策略(串行或并行)发送各个分片数据块;(上传之前先发送MD5检验码进行秒传,和获取文件上传到那部分了)
所有分快文件上传完毕后,发送合并文件请求。
服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件。
由于File继承自Blob,客户端可使用Blob.slice对大文件进行分割;服务端对分片文件存储,提供合并接口按切割顺序进行合并
扫码登陆的原理?
- 客户端向服务器请求生成二维码,二维码里面包含唯一的ID(UUID), 服务器保存UUID信息并设置过期时间到redis服务器里面。
- 浏览器展示包含UUID信息的二维码。
- 手机端扫码二维码,点击登陆,把扫码获取到的UUID 和 自身登陆的token信息发送到服务器端。
- 服务器端验证token信息和UUID信息,并绑定保存到redis里面。如果UUID失效,则登陆失败返回验证码失效。
- 客户端轮询查看redis里面UUID跟token相对应,获取到用户登陆信息。
UUID要设置过期时间,避免一直轮询,而且UUID登陆成功后就要失效。
手机端和浏览器端采用不同的redis服务器,所以不能直接共享token。
安全性
强制使用 HTTPS 协议、对数据进行签名和加密。
轮询与长轮询
polling即轮询,是指浏览器通过周期性轮询
,查看服务器是否有更新的信息;
long polling指的是长轮询,浏览器与服务器建立连接之后,服务器将此连接进行挂起,但有更新信息时,再将信息发送给浏览器端
。浏览器端重新建立连接,如此循环反复,这是一种长连接的方式。
微信扫码登陆
- 用户点击网站微信登陆图标
- 网站带上AppID和AppSecret和
回调域名参数
请求微信OAuth2.0授权登陆。 - 通过后返回二维码,浏览器展示二维码
- 用户扫码确定授权。
5、微信带上临时code参数,回调java后端相关接口。
6、java后端获取到code后,在带上带上AppID和AppSecret和code再去调微信接口。
7、获取access_token后就可以解析用户的一些基本信息,比如:微信用户头像、用户名、性别、城市等一些基本参数。
github 登陆类似