谷粒商城面试总结分析 第1篇
1. 实现HandlerInterceptor接口。
2. 重写preHandle方法,该方法将在执行业务方法之前执行。return true则放行,否则拦截。
3. 可以重写postHandle方法,该方法将在执行业务方法之后执行。
4. 启用拦截器。
1) 方法一:使用Java配置类。
@Configuration public class MyWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { (new MyInterceptor()).addPathPatterns(_/**_); } }
2) 方法二:在xml文件中配置bean。
谷粒商城面试总结分析 第2篇
线程池的七个参数一般怎么设置?
● int corePoolSize,核心线程池大小。
根据经验,假如服务器的CPU个数为N,
对于CPU密集型的任务,将线程数设为N+1。
对于IO密集型的任务,将线程数设为2N。
对于计算和IO操作都比较多的任务,应考虑使用两个线程池,分别处理计算和IO操作。
对于计算和IO操作都比较多且不可拆分的任务,采用算式 num=N×(任务总耗时/计算耗时) 来计算。
● int maximumPoolSize,最大线程池大小。
与核心线程池大小保持一致,减少任务处理过程中创建和销毁线程的开销。
● long keepAliveTime,活跃时间。
因为核心线程池和最大线程池大小保持一致,所以设多少都可以。
● TimeUnit unit,时间单位。
因为核心线程池和最大线程池大小保持一致,所以设多少都可以。
● BlockingQueue
必须是有界队列。
● ThreadFactory threadFactory,线程工厂。
使用默认的()就可以。
● RejectedExecutionHandler handler,拒绝执行处理程序。
一般使用默认的AbortPolicy就可以。
对于不容许任务失败的场景,使用CallerRunsPolicy。
对于无关紧要的任务,处理异常的收益很低,可以使用DiscardPolicy。
对于时效性比较强的任务,比如发布消息,可以使用DiscardOldestPolicy。
虽然建议使用自定义线程池,但在写一些测试代码的时候,使用Java内置的线程池还是很方便的。
工具类Executors中提供了四种内置的线程池:
● SingleThreadPool:单线程线程池。
● FixedThreadPool:定长线程池。
● CachedThreadPool:可缓存线程池。
● ScheduledThreadPool:定时调度线程池。
创建内置线程池示例:
ExecutorService executorService = (10);
异步编排的关键点有两个:
1) 开启一个新线程执行异步任务,可以使用线程池。
2) 可以设置收束点,等待所有的异步任务完成后再往后执行。
消息队列通常有三个使用场景:异步、解耦、流量控制(削峰)。
● 异步:对于页面请求,只需要将消息发送给消息队列,就可以立即返回。
● 解耦:消息的发送方和接收方可以是两个毫不相干的系统。
● 流量控制:消息接收方可以自定义接收消息的规则,在大流量下保证最后落到数据库的请求数不超出数据库的处理能力。
交换器共有四种类型:direct、fanout、topic、headers。
● direct:交换器将消息发送给绑定的、队列名与路由键(Routing Key)完全一致的单个队列。
● fanout:交换器将消息发送给绑定的每个队列。
● topic:交换器将消息发送给绑定的、队列名与路由键匹配的队列。队列名由若干个单词组成,单词之间使用_._隔开。路由键可以使用两个通配符:_#_和_*_,_#_匹配0个或多个单词,_*_匹配一个单词。
● headers:在匹配规则上与direct相似,但是性能差很多,现在基本上不用了。
● 消息丢失。
场景一:消息发出后,由于网络问题没有抵达服务器。
场景二:经纪人收到消息后宕机。
场景三:自动ack情况下,消费者收到消息后宕机。
解决方法:
1. 在数据库中记录生产者发送的每条消息,每条消息有唯一的ID进行标识。
2. 开启确认回调,经纪人收到消息后向生产者发送一条确认ack,生产者收到确认ack后,变更消息状态为发送成功。定期扫描数据库,重发发送不成功的消息。
3. 同样的,消费者消费消息后,向经纪人发送一条确认ack,经纪人收到确认ack后,再将消息从队列中删除。
● 消息积压。
场景一:消费者宕机。
场景二:发送者发送流量太大。
解决方法:
1. 上线更多消费者。
2. 先将消息记录到数据库中,以后再慢慢处理。
● 消息重复。
场景一:消息消费成功,但生产者没有收到确认ack,消息被重新发送。
场景二:消费者消费消息成功,但经纪人没有收到确认ack,没有将消息从队列中删除,消息被重复消费。
解决方法:
1. 使用防重表,每条消息都有唯一的ID进行标识,消费者消费消息之前先查防重表,如果存在该消息的消费记录,就不再处理。
2. 标识消息是否是第1次发送。
1. RabbitMQ可以对队列和消息分别设置消息的超时时间(TTL),超时未被消费的消息称为死信。
2. 我们可以创建一个没有任何消费者的队列,每条路由到该队列的消息,过一段时间后都会过期,成为死信。
3. 我们可以控制将死信发送给一个交换器,该交换器被称为死信路由,死信路由将死信发送给某个指定的交换器,然后死信被路由到某个队列,再被消费。
谷粒商城面试总结分析 第3篇
在一个分布式系统中,一致性、可用性、分区容错性三个要素最多只能同时实现两个,不可能三者兼顾。
● 一致性(Consistency):在分布式系统中一条数据的所有备份,在同一时刻的值都是一样的。
● 可用性(Availability):集群可以响应客户端的读写请求,即使是在集群的一部分节点发生故障后。
● 分区容错性(Partition tolerance):大多数分布式系统分布在多个子网络,每个子网络叫作一个区。分区容错意味着:即使区间通信失败也不影响集群的使用。
一般来说,区间通信失败是无法避免的,因此必须保证分区容错性。因此我们必须在一致性或可用性上有所让步。
BASE理论是对CAP定理的延伸,思想是:如果无法做到高可用,做到基本可用也是可以的,如果无法做到强一致,做到最终一致也是可以的。
BASE具体指的是:
● 基本可用(Basically Available):当分布式系统出现故障的时候,允许损失部分可用性。比如响应时间增加、功能降级等。
● 软状态(Soft State):允许系统存在中间状态,该中间状态不会影响系统整体的可用性。
● 最终一致性(Eventual Consistency):系统中的所有数据副本经过一定的时间后,最终能够达到一致的状态。
一个分布式集群只能有一个leader服务器,这个leader服务器是由所有服务器投票选举出来的。
领导选举过程:
1. 每台服务器都持有一张选票,选票信息包括myid(服务器的唯一ID)、支持的服务器的myid、事务日志中记录的最新一条事务的zxid(zxid是事务的唯一ID,zxid越大表示对数据进行修改的时间越靠后)等。
2. 一开始,每台服务器支持的服务器都是自己。
3. 服务器之间会进行通信,并进行选票pk,参与选票pk的服务器会比较持有选票中的最新一条事务的zxid,zxid比较大的一方赢下pk(没有zxid或zxid相同时比较myid),输的一方改票跟投(将持有选票中支持的服务器的myid、最新一条事务的zxid改成与赢的一方一致)。
4. 选票pk不断进行,直到超过半数的选票支持某一台服务器时,该服务器成为leader,其它参与选举的服务器成为它的follower。
谷粒商城面试总结分析 第4篇
分布式事务包含三个核心组件:
● Transaction Coordinator(TC):事务协调器。维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
● Transaction Manager(TM):事务管理器。控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
● Resource Manager(RM):资源管理器。控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支事务(即本地事务)的提交和回滚。
一个典型的分布式事务过程包括:
1. 事务管理器向事务协调器申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID。
2. XID在微服务调用链路的上下文中传播。
3. 资源管理器向事务协调器注册分支事务,将其纳入XID对应的全局事务的管辖。
4. 事务管理器向事务协调器发起针对XID的全局提交或回滚决议。
5. 事务协调器调度XID下管辖的全部分支事务完成提交或回滚请求。
Zookeeper通过ZAB协议(Zookeeper Atomic Broadcast,Zookeeper原子广播)来保证分布式事务的最终一致性。
ZAB协议的内容是:
1. 所有的事务性请求必须由一台全局唯一的服务器来协调处理,这台服务器被称为leader服务器,其他服务器称为它的follower服务器。
2. leader服务器负责将客户端发送过来的事务请求转换成事务,并分发给集群中所有的follower服务器。
3. 每台follower服务器收到事务后会给leader服务器返回一个ack请求,当leader收到超过半数的follower的ack请求后,leader会再次向所有的follower服务器发送commit消息,要求提交事务。
谷粒商城面试总结分析 第5篇
ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。 而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。
1. 创建ThreadLocal对象。
private static ThreadLocal
2. 对外提供get和set方法。
public static String getInfo(){ return (); }
public static void setInfo(String info){ (info); }
谷粒商城面试总结分析 第6篇
1. 从客户端请求中获取用户输入的username和password。
2. 根据username查询用户信息,如果查到的用户信息为null,说明用户未注册。
3. 校验password。
4. 校验通过后,使用session记录用户的登录状态,返回用户信息。
记录用户的登录状态有两个明显的好处:
1) 用户再次进入系统时,不用再次登录。
2) 处理用户的每一个请求时,都可以使用session中的身份信息进行鉴权。
谷粒商城面试总结分析 第7篇
客户端向服务端请求的资源分为静态资源和动态资源,其中静态资源包括js文件、css文件、图片等,无论请求多少次,返回结果都是一样的。
引入Nginx可以实现资源的动静分离:由Nginx服务(服务器或集群,下同)来处理对静态资源的请求,让业务服务专注于处理对动态资源的请求,这么做可以有效地提升系统的吞吐量。
1. 客户端向服务端发送请求。
2. Nginx服务(通过监听端口)首先收到来自客户端的请求,将静态资源返回给客户端。
3. Nginx服务做反向代理,将动态请求转发给网关服务。
4. 网关服务再根据路由规则,将请求转发给业务服务。
5. 请求被处理后,给客户端返回响应。
谷粒商城面试总结分析 第8篇
在分布式架构中,一台业务服务器对数据库中的数据进行修改后,无法对其他服务器上的缓存做同步更新,这样,其他服务器再处理请求时,很可能会返回修改前的数据。
分布式缓存的思想是:创建一块公共的缓存区域,为所有的业务服务器提供缓存服务。
加分布式锁可能会出现哪些问题?怎么解决?
● 业务逻辑执行异常,导致锁无法被释放。
将加锁操作放在try块中,解锁操作放在finally块中。
即使解锁操作放在finally块中,也有可能因为服务器宕机而无法解锁,所以要给锁设置一个超时时间。这个超时时间一般设为业务平均执行时间的3~5倍。
加锁和给锁设置超时时间两个行为必须放在一个原子操作当中,防止加锁成功后给锁设置超时时间失败。
● 业务逻辑执行期间,其他线程释放了锁。
加锁的时候给锁附上一段验证信息,一般采用服务器ID+线程ID或者UUID。解锁时,必须校验当前线程,验证成功才能释放锁。
校验和释放锁两个行为必须放在一个原子操作当中,防止校验通过后,锁正好到期,别的线程拿到锁,又被当前线程释放。
● 到了设置的超时时间业务还没有执行完。
起一个守护线程,每隔一段时间检查一次业务逻辑有没有执行结束,如果没有,就重置锁的超时时间。这种机制被称为锁续命。
如何判断业务逻辑有没有执行结束?
看占有分布式锁的线程有没有加锁,如果有,说明业务逻辑还没有执行结束。请注意,这里的“加锁”是真正的加锁,在对象的markword中记录线程指针,区别于分布式锁,分布式锁本质上只是一套控制流。
● 分布式锁应该是可重入的。
在加锁时维护当前的锁重入次数,第1次加锁时记为1,每次加锁时锁重入次数加1。解锁时,锁重入次数减1,当且仅当减1后的结果为0时,释放锁。
在加锁时还要维护锁的超时时间,当发生锁重入时,比较重入的超时时间和维护的超时时间,取其中较大值作为新的超时时间,这样可以防止锁重入导致超时时间减小而提前过期。
谷粒商城面试总结分析 第9篇
Elasticsearch是一个搜索与数据分析引擎,使用Elasticsearch可以实现对关键字的快速检索。
为什么快呢?是因为Elasticsearch使用了倒排索引技术。
每一条存入Elasticsearch的记录都被称为文档。用一个很长的二进制数表示一个关键字是否出现在了每个文档中,比如关键字“新能源”对应的二进制数可能是0100100011000001……,表示第2、第5、第9、第10、第16个文档中包含这个关键字。同样,假定“汽车”对应的二进制数是0010100110000001……,那么要找到同时包含“新能源”和“汽车”的文档,只需要将这两个二进制数进行布尔运算AND,结果为0000100000000001……,表示第5、第16个文档满足要求。
1. 用户将数据提交给Elasticsearch。
2. Elasticsearch使用分词器对用户提交的文档进行分词,将分词结果和权重一并保存。
3. 用户使用Elasticsearch进行搜索时,根据权重对结果进行打分、排名,再将结果返回给用户。
一切信息安全问题的核心都在于“权限”。最简单直接的保证信息安全的方法是对用户发送的每一个请求都做鉴权,只为有权限的用户服务。
用户获得权限的方式——注册。
用户身份认证的方式——登录。
谷粒商城面试总结分析 第10篇
将一些经常使用数据(从数据库中查出后)保存在内存中,当再次想要获得这些数据时,就可以直接从内存中返回。
● 读模式:当服务端收到一个查询请求时,会先去查缓存,如果缓存命中,就直接从缓存中返回查询结果;如果缓存不命中,就去查数据库,然后将查询结果写入缓存。
● 写模式:当服务端收到一个修改请求时,在修改数据库中的数据后,需要对缓存中的数据做同步修改。
我们在内存中创建缓存记录时,如果不对其设置超时时间,除非手动删除,这条记录将永远存在于内存中,很容易造成内存泄漏。
实现缓存过期的方式有两种:被动方式和主动方式。两种方式需要结合起来使用。
● 被动方式:当服务端收到查询请求去查缓存时,如果查到的是一条已过期的缓存记录,就删掉这条记录。
● 主动方式:每隔一段时间,随机选取一些缓存记录进行查验,删除其中已过期的记录。
● 缓存穿透:当客户端请求一些数据库中不存在的数据时,如果不把查询的空结果写入缓存,那么对这些数据的请求每次都会打到数据库,缓存就失去了意义。
解决方法:将查询数据库得到的空结果写入缓存。
● 缓存雪崩:在高并发场景下,某一时刻,大量缓存记录同时失效,对这些缓存数据的请求会在一瞬间全部被打到数据库,数据库很可能会因瞬时压力过大而崩溃。
解决方法:设置缓存记录的超时时间时,在设定时间的基础上增加一个随机时长。
● 缓存击穿:一些_热点_数据,在某个时刻,可能会被许多客户端同时请求,如果此时缓存记录正好失效,大量的查询请求会全部被打到数据库,可能导致数据库崩溃。
解决方法:加锁。同一时间只允许一个线程进行查询,其他线程需要等待。
缓存一致性问题指的是:数据库中的数据更新后,缓存中的数据要和数据库中保持一致。
为什么这会是一个问题?
数据库中的数据更新、缓存数据同步更新,两个步骤不能通过一个原子操作完成,这样在高并发场景就可能出现各种问题。
从设计的角度来讲,我们放入缓存的数据不应该是对实时性、一致性要求很高的数据,所以我们只要做到下面两点就可以满足大部分业务场景了:
1) 设置缓存超时时间。
2) 数据库中的数据更新后删除缓存。
如果一定要解决缓存一致性问题,业内通常有两种解决方案:
1) 延时双删。
删除缓存记录 => 更新数据库数据 => 等待1~5s => 再次删除缓存记录
● 为什么要删除缓存记录而不是直接更新?
多个线程先后更新数据库数据后,它们不一定会按顺序更新缓存数据,而删除操作不会受到顺序影响。
● 更新数据库数据前为什么要删除缓存数据?
防止数据库数据更新成功后,某些原因导致缓存数据删除失败,缓存数据失真。
● 更新数据库数据后为什么要删除缓存数据?
更新数据库数据前,可能有其他线程查到了旧数据,在更新数据库数据后,又将旧数据写入了缓存。
● 为什么要等待1~5s?
等待查到旧数据的线程向缓存写数据完成、等待数据库主从复制完成。
2) 订阅数据库变更日志。
当数据库中的数据被修改时,数据库生成一条变更日志(Binlog),我们可以订阅变更日志,拿到具体的变更操作,据此删除对应的缓存记录。
目前比较成熟的订阅数据库变更日志的开源中间件:Canal。
注意,无论是延时双删策略还是订阅数据库变更日志策略,都只能保证缓存的最终一致性,不能保证强一致性。对实时性要求较高的数据,就应该直接去查数据库。
我们希望能够组合多个命令,让这些命令能够在一个原子操作内执行。
Redis给出的解决方案是:支持执行Lua脚本。
谷粒商城面试总结分析 第11篇
将域名范围扩大为父域名
通过过滤器来拦截请求,将request和response包装成wrappedRequest和wrappedResponse,做了功能增强,这样就是从SessionRepository获取得到的,做到从redis获取session。
添加商品到购物车,要判断用户有没有登录,没有登录就操作临时购物车,登录了就操作登录购物车并且还要合并临时购物车
然后通过拦截器来拦截判断用户是否登录,然后将用户的信息封装起来,放到threadlocal中
1.双写模式
解决方案:1)给缓存数据设置过期时间,数据最终一致性(看用户是否可以忍受)
2)加读写锁
失效模式
解决方案:加读写锁
3.延时双删
来监听mysql的binlog日志,然后更新redis
5.分布式的读写锁
谷粒商城面试总结分析 第12篇
1) 尽量避免在循环中操作数据库。
可以在准备数据时一次查询所有可能用到的数据,再在服务层对数据进行过滤。
2) 尽量不采用联表查询,而是采用单表查询+代码层组装的方式。
● 单表查询SQL更容易被复用,缓存利用率也较高。
● 联表查询时,因表结构变动导致SQL需要做同步修改的可能性较高。
● 两个大表做联查的效率可能很低。
谷粒商城面试总结分析 第13篇
推荐使用LocalDateTime。
1) 获取今天的0:00:00。
LocalDate today = (); // 获取今天的日期 LocalTime min = ; // 获取一天的最小时间 LocalDateTime start = (today, min); // 组装日期和时间 String formatStart = ((_yyyy-MM-dd HH:mm:ss_)); // 获取格式化的时间
2) 获取后天的23:59:59。
LocalDate today = (); // 获取今天的日期 LocalDate plus = (2); // 将今天的日期向后推两天 LocalTime max = ; // 获取一天的最大时间 LocalDateTime end = (plus, min); // 组装日期和时间 String formatEnd = ((_yyyy-MM-dd HH:mm:ss_)); // 获取格式化的时间
3) Date数据转换成LocalDateTime类型。
LocalDateTime localDateTime = ().atZone(()).toLocalDateTime();
4) LocalDateTime数据转换成Date类型。
Date date = ((()).toInstant());
注:本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即后台留言通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意
发表评论