1.如何防止表单重复提交
- 前端。每次点击后都要等X秒才能点击。
- 数据库添加唯一索引
- 服务器返回表单页面时,会先生成一个subToken保存于session或redis,当表单提交时候携带token,如果token一致,则执行后续,并将服务器中的token删除。如果token不存在,则说明是重复提交。
2.如何设计一个秒杀系统
-
前端。
在秒杀之前,按钮置灰,并且不给前端真正的请求地址。前端定时请求后端接口,如果到了秒杀时间,则返回给前端真正的地址,前端放开按钮,每次点击后都要等XX秒才能点击。 -
服务器:服务器用nginx做集群、redis也做集群
-
限流:在秒杀之前,将秒杀数量的令牌存入到redis中,可以用list,每次来请求都去redis中取出令牌,如果获取到说明秒杀成功,然后去访问数据库下单,如果没有获取到,则说明商品卖完了。
-
消息中间件。如果秒杀数量比较多,例如上万十万,则秒杀成功之后,将成功的请求放入到mq或者kafka中间件中,再从消息队列中获取请求。
-
服务降级。为了以防万一,还是要做服务熔断降级。
3.分布式系统的接口幂等性设计
https://blog.csdn.net/sinat_41780498/article/details/121923329
哪些需要保证幂等性:
- 前端按钮点击,比如下单按钮,由于网络原因导致没有响应会重复点击(表单重复提交,或者请求重复发送)
- 消息重复消费
- rpc接口超时重试
分布式系统的接口幂等性设计:
- 唯一id。每次操作,都根据操作和内容生成唯一的id,在执行之前先判断id是否存在,如果不存在则执行后续操作,并且保存到数据库或者redis等。
- 服务端提供发送token的接口,业务调用接口前先获取token,然后调用业务接口请求时,把token携带过去,服务器判断token是否存在redis中,存在表示第一次请求,可以继续执行业务,执行业务完成后,最后需要把redis中的token删除。
- 建去重表。将业务中有唯一标识的字段保存到去重表,如果表中存在,则表示已经处理过了。
- 版本控制。增加版本号,当版本号符合时,才能更新数据。乐观锁(更新操作)。
- 状态控制。例如订单有状态:已支付、未支付、支付中、支付失败,当处于未支付的时候才允许修改为支付中等。
- 分布式锁。使用分布式锁结合状态判断或者唯一id或者去重表。
4.管理分布式session的四种方式(单点登录)
-
session复制
应用服务器开启Web容器的Session复制功能,在集群中的几台服务器之间同步Session对象。方案简单,且从本机读取session也相当快捷,但有非常明显的缺陷:只能使用在集群规模比较小的情况下(企业应用系统,使用人数少,相对比较常见这种模式),当集群规模比较大的时候,集群服务器之间需要大量的通信进行Session的复制,占用服务器和网络的大量资源,系统负担较大。而且由于用户的session信息在每台服务器上都有备份,在大量用户访问下,可能会出现服务器内存都还不够session使用的情况。
-
session会话保持
会话保持是利用负载均衡的原地址Hash算法实现,负载均衡服务器总是将来源于同一IP的请求分发到同一台服务器上。这种方案虽然保证了每个用户都能准确的拿到自己的session,而且大量用户访问也不怕,但是这种会话保持不符合系统高可用的需求。这种方案有着致命的缺陷:一旦某台服务器发生宕机,则该服务器上的所有session信息就会不存在,用户请求就会切换到其他服务器,而其他服务器因为没有其对应的session信息导致无法完成相关业务。所以这种方法基本上不会被采纳。
-
cookie记录
将session或者token记录在客户端,每次请求服务器的时候将session或者token放在请求中发送给服务器,服务器进行校验即可判断是否登录。利用cookie记录session是存在很多缺点:比如cookie的大小存在限制能记录的信息不能超过限制;比如每次请求都要传输cookie影响性能;比如cookie可被修改或者存在破解的可能,导致cookie不能存重要信息,安全系数不够。但是由于cookie简单易用,支持服务器的线性伸缩,而且大部分的session信息相对较小,所以其实很多网站或多或少的都会使用cookie来记录部分不重要的session信息。
-
session共享
使用redis保存系统的所有session,redis作为一个存储中心,分布式系统的多台机器可以共享使用,当请求时,去redis中查找是否存在session,即可判断用户是否登录。此方式对redis的可用性较高,需要搭建redis集群。
5. 如何保障请求执行顺序
一般来说,从业务逻辑上最好设计系统不需要这种顺序的保证,因为一旦引入顺序性保障,会导致系统复杂度的上升,效率会降低,对于热点数据会压力过大等问题。
首先使用一致性hash负载均衡策略,将同一个id的请求都分发到同一个机器上面去处理,比如订单可以根据订单id。如果处理的机器上面是多线程处理的,可以引入内存队列去处理,将相同id的请求通过hash到同一个队列当中,一个队列只对应一个处理线程。
最好能将多个操作合并成一个操作。
6.跨域的常见5种解决方式
https://zhuanlan.zhihu.com/p/81809258
- jsonp,仅可使用get请求,jsonp的原理就是利用
<script>
标签没有跨域限制,通过<script>
标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据 - script标签解决跨域
- 前端代理解决
- nginx代理解决
- 后端设置响应头(Access-Control-Allow-Origin)
7. 如何保证接口的安全性?
- 接口使用https协议
- 对接口数据进行加密,对称加密或非对称加密
- 对接口请求数据作签名验证,确保请求数据不会被篡改
- 对接口作黑名单,白名单ip限制
- 对接口作限流
- 接口不要暴露真实ip,避免服务器被攻击
- 避免xss(跨站脚本攻击)、sql、XSRF(跨站请求伪造)注入等攻击
- 接口作一些dto、vo之间的转换,避免更新操作,将无用的数据更新到数据库中
8. 生产环境cpu使用率飙高产生的原因?
- 代码中写一些cas循环,不断重试
- 代码中存在bug,导致死循环存在
- 服务器中使用redis,redis被攻击,拿去挖矿
- 服务器被攻击,进行ddos攻击,导致服务器被压垮
9. 生产环境cpu使用率飙高,如何排查?
- 先找到哪个进程cpu使用率比较高,使用top指令
- 再分析进程中哪个线程的cpu使用率较高
- 再通过线程名称去搜索java代码,分析到底是什么原因导致cpu的使用率飙高,所以在使用多线程或线程池时,一定要指定线程的名称,目的是为了在后续方便我们排查cpu飙高产生的原因,通过线程名称搜索java代码。
10、如何排查线程死锁,查看线程状态
引用:https://blog.csdn.net/fengsheng5210/article/details/123576559
- 使用jstack打印线程堆栈信息分析找到
Found one Java-level deadlock
等字样并且线程状态为BLOCKED
状态,然后找到对应行数的代码分析,查看是否存在死锁的四个必要条件(互斥条件、请求与保持、不可剥夺、循环等待),有则打破即可。 - 使用jconsole图形化界面分析工具,进行死锁检测
- 使用jvisualvm图形化工具分析工具,进行死锁检测
11、如何排查数据库事务死锁、锁表情况
引用:https://blog.csdn.net/qq_43842093/article/details/130477353
Mysql 查询是否存在锁表有多种方式,这里只介绍一种最常用的。
1、查看正在进行中的事务
SELECT * FROM information_schema.INNODB_TRX
2、查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
3、查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
4、查询是否锁表
-- 显示当前被锁定的表的列表
SHOW OPEN TABLES where In_use > 0;
在发生死锁时,这几种方式都可以查询到和当前死锁相关的信息。
5、查看最近死锁的日志
show engine innodb status
解除死锁
如果需要解除死锁,有一种最简单粗暴的方式,那就是找到进程id之后,直接干掉。
查看当前正在进行中的进程
-- 查看mysql进程列表
show processlist
// 也可以使用
SELECT * FROM information_schema.INNODB_TRX;
这两个命令找出来的进程id 是同一个。
杀掉进程对应的进程 id
kill id
验证(kill后再看是否还有锁)
12、如何排查内存泄漏
内存泄漏的最终导向就是内存溢出,一般通过公司的服务监控工具会告警服务内存居高。可以手动导出堆转储文件或发生OOM时自动生成堆转储文件,通过一些内存分析工具,如MAT、VisualVM等查看内存快照,分析对象引用,分析对象的创建个数在GC之后是否变少。见下边OOM介绍。
13、如何排查内存溢出OOM?
- java应用执行时一般都会增加两个参数:
-XX:+HeapDumpOnOutOfMemoryError
和-XX:HeapDumpPath=/tmp/heapdump.hprof
,当程序发生OOM时,会自动生成dump堆内存信息到指定目录 - 使用jstat查看监控JVM的内存和GC情况,先观察问题大概出在在JVM的哪个区域
- 使用MAT工具载入dump文件,分析大对象的占用情况,比如HashMap做缓存未清理,时间长了就会内存溢出,可以把它改为弱引用
- 分析的结果:
- 程序中的对象一直增多,并且属于正常情况,说明堆空间分配小了,则增加堆空间大小
- 程序执行,GC之后内存空间还一直在增加,没有呈现升高下降升高下降的趋势,说明有可能存在内存泄露,此时需要进一步排查分析dump文件中对象,来判断是哪里内存泄露了
14、如何进行jvm调优,有哪些命令和工具
JVM调优的目的:
让程序更稳定、吞吐量更高、性能更高、响应速度更快
JVM调优的手段:
-
调整堆内存大小:通过调整-Xms和-Xmx参数来设置堆内存的初始大小和最大大小,合理配置堆内存大小可以减少垃圾回收的频率和停顿时间,提高应用程序的性能。
-
选择合适的垃圾回收器:JVM提供了多种垃圾回收器,如Serial、Parallel、CMS、G1等,不同的垃圾回收器适用于不同的场景,根据应用程序的特点选择合适的垃圾回收器可以提高垃圾回收的效率和响应速度。
-
调整垃圾回收策略:通过调整垃圾回收的相关参数,如-XX:NewRatio、-XX:SurvivorRatio、-XX:MaxTenuringThreshold等,可以优化垃圾回收的行为,减少垃圾回收的停顿时间。
-
调整线程池大小:通过调整JVM的线程相关参数,如-XX:ParallelThreads、-XX:ConcGCThreads等,可以优化线程池的大小,提高并发处理能力。
-
使用性能分析工具:使用性能分析工具来识别性能瓶颈和优化机会,如Java Mission Control、YourKit Java Profiler、JProfiler等,可以帮助定位性能问题并提供优化建议。
-
进行代码优化:通过优化代码来改进应用程序的性能,包括减少内存使用、优化算法、避免不必要的IO操作等。
-
使用合适的JVM版本:不同版本的JVM可能会有不同的性能特点和优化策略,选择合适的JVM版本可以提升应用程序的性能。
JVM调优的命令和工具:
-
监控和分析JVM性能问题:使用以下命令和工具来监控和分析JVM性能问题:
- jstat:用于监控JVM的各种统计数据,如堆内存使用情况、垃圾回收情况等。例如:jstat -gc <进程ID> <时间间隔> <次数>。
- jstack:用于生成Java线程的堆栈跟踪信息,用于分析死锁和线程等待情况。例如:jstack <进程ID>。
- jmap:用于生成Java堆内存的转储快照,用于分析内存泄漏和对象分布情况。例如:jmap -dump:format=b,file=<文件名> <进程ID>。
- VisualVM:一款功能强大的Java虚拟机监视和分析工具,可以进行实时监控、线程分析、堆内存分析等。它可以通过插件支持更多功能。
-
调整JVM参数:根据应用程序的需求和硬件环境,调整JVM参数以优化性能,常见的参数包括:
- 堆内存相关参数:如-Xmx、-Xms、-XX:NewSize、-XX:MaxNewSize等,用于调整堆内存的大小。
- 垃圾回收相关参数:如-XX:+UseG1GC、-XX:+UseConcMarkSweepGC等,用于选择垃圾回收器和调整垃圾回收策略。
- 线程相关参数:如-XX:ParallelThreads、-XX:ConcGCThreads等,用于调整线程池的大小。
- JIT编译器相关参数:如-XX:+TieredCompilation、-XX:CompileThreshold等,用于调整JIT编译器的行为。
-
使用性能分析工具:使用性能分析工具来识别性能瓶颈和优化机会,常见的工具包括:
- Java Mission Control(JMC):一款Java性能分析工具,可以进行实时监控、分析和故障诊断。
- YourKit Java Profiler:一款商业性能分析工具,提供实时的CPU和内存分析,可以帮助定位性能问题。
- JProfiler:一款商业性能分析工具,提供实时的CPU、内存和线程分析,支持多种应用服务器和框架。
-
进行代码优化:通过代码优化来改进应用程序的性能,包括减少内存使用、优化算法、避免不必要的IO操作等。
部分来源:https://blog.csdn.net/zgsxhdzxl/article/details/104414475