1、简单的秒杀过程
当前端服务器进入到秒杀倒计时,我们就通过网关进入到秒杀活动服务,1、查询库存,2、获取秒杀令牌 ,3、请求秒杀接口,创建订单,接着进入到订单服务进行数据库操作,1、扣减库存,2、生成订单表
1.1、问题解决
扣减库存的时候,由于mysql 性能的限制,所以把库存存入到redis中,此时会产生一个redis中库存和数据库库存一致性的问题,我们这里采用rocketMQ事务型消息来保证数据一致性

2、那么RocketMQ是如何实现分布式事务?
本地事务-MQ发送方-MQ server -MQ订阅方
2.1、首先事务发送方发送prepare消息到MQ
2.2、消息发送成功后就开始执行本地事务
2.3、根据执行本地事务结果返回的rollback还是commit来执行下一步操作
2.4、如果是rollback,那么MQ就会把prepare消息删除不进行下发,如果是commit消息,MQ就会把prepare消息发送到consumer端
2.5、如果在执行本地事务的时候,执行端挂掉,或者超时,那么MQ将会不停的在其同组寻找其他的producer来获取状态
2.6、所以说consumer端消费成功的机制有MQ来保证

一: 如果秒杀服务过程中MQ服务挂掉了怎么办?
1、首先从设计角度,保证mq的高可用性,做集群和主从备份
2、需要给MQ配置消息持久化,防止挂掉之后消息丢失
3、做好监控,防止服务挂了之后没有检测到,并且及时把服务重启
4、从业务角度来保证MQ挂了之后用户的体验

二:MQ消费者如果消费不成功怎么办
1、MQ如何消费不成功,RocketMQ就会把消息丢入到重试队列中
2、MQ重试队列默认有重试机制,会反复去投递成功或者是在16次之后假如还是消费失败的话,会把该消息投递到MQ死信消息队列里面
3、我们可以通过后台管理系统从死信队列里面把没消费成功的信息取出来,然后手动的去库存扣减

3、分布式事务有哪几种?
分布式事务--刚性事务-二阶段提交
-三阶段提交
--柔性事务-补偿事务-TCC(基于二阶段提交)
-消息型事务-本地消息表
MQ事务型消息

4、说一下二阶段提交
首先二阶段提交把事务分为两个阶段,并且引进了事务管理器
一阶段:1、提交事务请求:协调者向各个参与者节点发送MQ事务内容,并询问是否可以提交事务,并开始等待响应
2、执行事务:接着各个参与者节点开始执行事务的操作,并把Undo(undo即undo段,是指数据库为了保持读一致性,存储历史数据在一个位置)和Redo(redo即redo日志,记录数据库变化的日志)信息记录事务日志中
3、反馈:这时候各个参与者就开始把执行的信息反馈回去,如果反馈的是yes响应,表示事务执行成功,那么就说明事务可以执行。如果是no响应,那么说明事务执行失败,表示事务不能执行。
二阶段:1、执行事务提交:这时候如果事务管理器收到所有事务参与者响应都为yes,那么就会执行事务提交,协调者就会向参与者发送commit请求。如果收到的响应为no的话,或者等待操作,那么就会进行事务中断,然后协调者发送的是rollback请求,请求资源回滚,释放资源。
2、当参与者收到了commit请求,就开始执行事务提交操作,并且在完成所以的事务提交操作后,就会开始释放整个事务期间占有的资源,并向协调者发送ack消息
3、当协调者收到ack消息后,就说明事务完成

5、说一说Redis的持久化策略?
其中持久化方式有两种,持久化策略有四种
RDB(数据快照模式):定期存储,保存的是数据本身,存储文件是紧凑的
AOF(追加模式):每次修改数据,同步到硬盘中(写操作日志),保存的是数据变更的记录
如果只想保存数据在内存中,那么两种策略都可以关闭
也可以两种策略都开启,因为redis重启的时候,AOF会重建原始数据

6、Redis的数据类型有哪几种?知道setnx和setex是干嘛的吗?
Redis支持五种数据类型:string,hash,list,set,zset
1、setnx:设置key对应的值为string类型的value,如果已经存在,就返回0,如果不存在就返回value和1,nx,也是no exist的意思,可用于分布式锁。例如setnx key value。
2、setex:设置key对应的值为string类型的value,并指定此键值对对应的有效期。例如:setex key second value。

7、消息队列有哪些?rocketMQ对比其他消息队列有什么优势
ActiveMQ:基于java语言开发,单机吞吐量万级,高可用性(主从框架)。是成熟的产品,有较多的文档,各种协议支持较好
RabbitMQ:基于erlang语言开发,单机吞吐量万级别,时效性是微妙级别,高可用性(主从框架),并发能力很强,性能很好,延时低,管理界面丰富
RocketMQ:基于java语言开发,单机吞吐量为10万级别,非常高可用性(分布式框架),功能比较完备,扩展性佳
kafka:基于Scala语言开发,单机吞吐量为10万级别,非常高的可用性(分布式架构),支持主要的MQ功能,像一些消息查询,消息回溯等功能没有提供,毕竟是为大数据准备的,在大数据领域应用广泛

消息队列消息类型有哪些?
1、普通消息:也叫无序消息,因为不需要保证消息的顺序,所以消息可以大规模的并发和消费,吞吐量很高,适合大部分场景
2、有序消息:就是按照一定的先后顺序的消息型类型,进一步还可以分为:全局有序消息、局部有序消息
3、延时消息:简单时候就是当producer将消息发送过去,会延时一定时间才投递给consumer进行消费
4、事务消息:本地事务和事务型消息的投递保持一致性

8、说一下分布式锁?
单机环境下,我们用Synchronized关键字来修饰方法或者是修饰代码块,以此来保证其在同一时间只能被一个线程执行。但是到了分布式环境中,synchronized就不好使了,因为他是jvm提供的关键字,只能在一台JVM中保证同一时间只有一个线程执行,那么假如有多台JVM呢(项目有多个节点了),那么就需要分布式锁来保证同一时间,在多个节点内,只有一个线程执行该代码。
三种实现方式:
1、基于数据库:
数据库自带的通过唯一性索引或者主键索引,通过它的唯一性,当某个节点使用某个方法时就在数据库中添加固定的值,这样别的节点来使用这个方法就会因为无法在添加这个唯一字段而失败,这样就实现了锁。一旦这个节点执行完毕后,就删除这一行数据,别的节点就又能访问这个方法了,这就是释放锁。(容易死锁,数据库本身性能 比较低,不推荐使用)
2、Redis分布式锁
setnx关键字:设置参数如果存在就返回0,不存在就返回value和1
expire关键字:为key设置时间,解决死锁
delete关键字:删除key释放锁
实现思想:
1、当我们获取锁的时候,用setnx来加锁,并用expire来设置超时时间,如果超出这个时间就自动释放锁,锁的value值通过生成一个UUID,在释放锁的时候进行判断
2、获取锁的时候,我们也设置一个获取超时的时间,如果超过这个时间就会放弃获取锁
3、释放锁的时候,通过判断UUID是不是该锁,如果是,就会执行delete进行锁的释放
3、zookeeper实现分布式锁
zookeeper是一个为分布式应用提供一致***的开源组件,他的内部是一个分层次的文件系统目录树结构,并且规定同一个目录下只能有一个唯一文件名。
步骤如下:
1、创建一个目录mylock
2、线程A想获取锁,就需要在mylock目录下创建一个临时顺序节点
3、获取mylock目录下所有子节点,然后获取比自己小 的兄弟节点,如果不存在,就说明自己是最小的顺序号,然后就获锁
4、线程B获取所有的节点,判断自己是不是最小节点,然后设置监听比自己次小的节点
5、当线程A处理完,删除自己的节点,线程B监听到变更事件,再判断自己是不是最小的节点,是的话,就获取锁
速度没有redis快,但是能解决死锁问题,可用性高于redis

9、项目使用线程池,简单说一下
newCachedThreadPool:
当有新任务到来,则插入到SynchronousQueue中(同步队列),接着它就会在池中可用线程来执行,若是没有可用线程则创建一个线程来执行该任务;若线程池空闲时间超过指定大小,则该线程会被销毁,适用于执行很多短期异步的小程序或者负载比较轻的服务器
流程:1、当线程池小于corePoolSize,新提交的任务就会创建一个新的线程来执行任务,即使此时线程池存在空闲线程
2、当线程池达到corePoolSize时,新提交任务将被放入到workQueue中,等待线程池的任务调度执行
3、当workQueue已满,且maxximumPoolSize时,新提交任务会创建新线程任务
4、当提交任务数超过maximumPoolSize时,新提交任务有RejectExecutionHandler处理
5、当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6、设置allowCoreThreadOut(true)时,线程池corePoolSize线程空闲时间达到keepAliveTime也将关闭

10、说一下zookeeper的作用?zookeeper在某一个瞬间挂了一个dubbo服务器还能调用吗?如何实现zookeeper的高可用?
1、作用:zookeeper作为注册中心为分布式环境下各个微服务之间的调用提供协调工作
2、还能调用,我们启动dubbo时,消费者会从zookeeper拉取生产者的地址接口等数据,缓存在本地。每次调用的时候,只需要按照本地存储地址进行调用
3、实现高可用:搭建zookeeper集群实现高可用

11、zookeeper集群最少需要配置几台机器?为什么?
最少3台,zookeeper关于leader的选举机制,主要提供了三种方式:LeaderElection,AuthFastLeaderElection,FastLeaderElection
默认算法是FastLeaderElection,在选择的过程中,为了保证选举过程最后能选出leader,就不能出现两台机器票数相同的僵局,所以数量要为奇数,也就是2n+1,并且如果集群出现问题,其中存活的机器必须大于n+1台,否则leader无法获得更多数server的支持,系统就会自动挂掉。所以一般是大于等于3

12、数据库有多少条记录?考虑过分库分表吗?
mysql单表性能在500W条左右(具体也与表结构和机器配置有关)
1、分库:使用mycat或者是Sharing-JDBC等中间件来作为数据库的代理
2、表:单表业务量过大,可以采用业务表或者是按时间分表手段来切分
3、中间件:是提供系统软件和应用软件之间的连接软件,以便于软件各部件之间的沟通,如数据库、Apache的Tomcat,IBM公司的WebSphere,BEA公司的WebLogic应用服务器,东方通公司的Tong系列中间件。
4、一般本地开发的话建议使用tomcat。

    linux系统建议使用jetty或apache hpptd

    大型的项目就用JBOSS或webloigc

    小项目,或者是个人开发tomcat 大项目或者商业项目一般采用:weblgoic/webshere
    其他的还有jboss、glasshfish等
    一些示例项目或者小项目常采用jetty  
 5、分库分表,结合实际需求操作,一般在项目初期是不会考虑分库分表的,随着业务量和数据量的不断提升,才会有一些性能上的要求,这个时候假如数据库有了瓶颈就可以考虑使用分库分表来提高系统性能

13、Redis是如何配置的。说一下集群、哨兵模式?
1、port:6379指定访问redis服务的端口
bind:0.0.0.0指定redis绑定的主机地址,同时外网也可以访问
timeout:指定客户端连接redis服务器时,当闲置的时间为多少时可以关闭
dir:指本地数据文件存放目录
requirepass:指定redis的访问密码
maxmemory:指定redis的最大内存
2、redis的集群模式
2.1、主从复制模式
Slave启动并连接到Master之后,会主动发送一个SYNC命令。Master收到命令后,并收集所有用于修改数据集的命令,在后台执行完毕后,就将整个数据库文件传送到Slave,来完成一次完全同步。
Slave收到数据文件之后就会存在存盘中并加载到内存中。此后Master收集所有命令传送给Slave,Slave执行这些数据修改命令,从而达到最终的数据同步。
2.2、哨兵模式
哨兵是一个分布式系统,你可以在一个架构中运行多个哨兵进程,这些进程使用流言协议来接收关于Master是否下线的信息,并使用投票协议来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master
每个哨兵会向其他哨兵,master,slave定时发送消息,确认对方是否还活着,如果在指定的时间内未响应,说明对方已经挂了,“主观认为宕机sdown”
不过,只有“哨兵群”中的多数sentinel,都报告master没有响应,系统才会认为master“彻底死亡”,通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置
虽然哨兵释放出一个可单独的可执行文件,但实际上它只是一个运行在特殊模式下的redis服务器,你可以启动一个普通redis服务器时通过给定--sentinel选项来启动哨兵
2.3、Cluster集群模式
14、如何限流
1、应用级别
A、限制瞬时并发数,如nginx的limit_conn模块,用来限制瞬时并发连接数、nginx的limit_req模块,限制每秒的平均速率
B、限流总并发/连接/请求数,如配置Tomcat的线程池的参数:
1、acceptCount:如果Tomcat的线程忙于响应,新来的连接会进入队列排队,如果超出排队大小,则拒绝连接
2、maxConnections:瞬时最大连接数,超出就会排队等待
3、maxThread:Tomcat能启动用来处理请求最大线程数,如果请求处理量一直远远大于最大线程数则可能会僵死
2、服务级别:
A、限制总资源数
使用线程池、连接池等,比如分配给每个应用的数据库连接是100个,那么该应用在同时最多能使用100个资源,超出了就抛出异常
B、限流某个接口
a、限制某个接口总并发量
b、平滑限流某个接口的请求数

15、JWT的原理?
JWT结构
JWT由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
Header
Payload
Signature
1、前端通过表单将用户名和密码发送到后端接口
2、后端核对用户名和密码后吗,将用户的id及其他非敏感信息作为JWT Payload,将其与头部分分别进行base64编码后签名,生成JWT
3、后端将JWT字符串作为登入成功的结果返回前端,前端可以将JWT保存到localStorage(局部存储器)或者sessionStorage中,退出登入的时候,前端就会删除保存的JWT信息
4、前端每次在请求的时候,将JWT放到header中的Authorization
5、后端验证JWT的有效性
6、验证通过后,进行其他的逻辑操作

JWT如何做到安全?
1、生产JWT的加密算法和秘钥不要泄***r> 2、使用HTTPS协议传输,防止数据被拦截

16、Nginx了解吗?会部署吗?如何做负载均衡?
1、Nginx的负载分发策略
Nginx的upstream目前支持的分配算法
1.1、轮询---1:1轮流处理请求(默认),每份请求按照时间顺序分配到不同的应用服务器,如果应用服务器down掉,自动剔除,剩下的继续轮询
1.2、权重:通过配置权重,指定轮询几率,权重和访问比率成正比,用于服务器性能不均的情况
1.3、ip_哈希算法:每个请求按照访问ip的hash结果分配,这样每个访客固定访问一个应用服务器,可以解决session共享的问题

17、说一下redis的缓存雪崩,缓存穿透?如何保证redis缓存里面都是热点数据
1、缓存雪崩:是指在一段时间内,缓存集中失效,假如大量缓存在某一时间同时失效,这个时候就会导致大量是请求去请求数据库,可能会引起数据库的瘫痪
1.1、如何避免:设置热点缓存的时候过期时间分散设置
2、缓存穿透:是指查询数据库一定不存在的数据。正常的使用缓存流程大概是,数据查询先进行缓存查询,如果key不在或者key已经过期了,在对数据库进行查询,并把数据库查询到的对象,放入到缓存。如果数据库查询的对象为空,则不放进缓存。那么如果查询的这个数据一直在缓存中找不到,那么就会一直到数据库中查询,当我们查询量很大的时候,就有可能击穿我们的数据库。
2.1、如何避免:当查询为空的时候,可以在缓存中打上一个标记,这样下次去查询的时候就可以得知了
3、如何保证redis里面都是热点数据
当redis使用的内存超过了设置的最大内存时,会触发redis的key淘汰机制,在redis3.0中有6中淘汰机制
3.1、noeviction:不删除策略。当达到最大内存限制时,如果需要使用更多内存,则直接返回错误信息。(redis默认的淘汰策略)
3.2、allkeys-lru:在所有key中优先删除最近最少使用的key
3.3、allkeys-random:在所有key中随机删除一部分key
3.4、volatie-lru:在设置超时时间的key中,优先删除最近最少使用的key
3.5、volatile-random:在设置超市时间的key中随机删除一部分key
3.6、volatile-ttl:在设置超时时间的key中优先删除剩余时间短的key