SSM框架面试问题
讲下springmvc框架的工作流程
1、用户向服务器发送请求,请求被SpringMVC的前端控制器DispatcherServlet截获。
2、DispatcherServlet对请求的URL(统一资源定位符)进行解析,得到URI(请求资源标识符),然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象,包括Handler对象以及Handler对象对应的拦截器,这些对象都会被封装到一个HandlerExecutionChain对象当中返回。
3、DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter。HandlerAdapter的设计符合面向对象中的单一职责原则,代码结构清晰,便于维护,最为重要的是,代码的可复制性高。HandlerAdapter会被用于处理多种Handler,调用Handler实际处理请求的方法。
4、提取请求中的模型数据,开始执行Handler(Controller)。在填充Handler的入参过程中,根据配置,spring将帮助做一些额外的工作
消息转换:将请求的消息,如json、xml等数据转换成一个对象,将对象转换为指定的响应信息。
数据转换:对请求消息进行数据转换,如String转换成Integer、Double等。
数据格式化:对请求的消息进行数据格式化,如将字符串转换为格式化数字或格式化日期等。
数据验证:验证数据的有效性如长度、格式等,验证结果存储到BindingResult或Error中。
5、Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象,ModelAndView对象中应该包含视图名或视图模型。
6、根据返回的ModelAndView对象,选择一个合适的ViewResolver(视图解析器)返回给DispatcherServlet。
7、ViewResolver结合Model和View来渲染视图。
8、将视图渲染结果返回给客户端。
以上8个步骤,DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等对象协同工作,完成SpringMVC请求—>响应的整个工作流程,这些对象完成的工作对于开发者来说都是不可见的,开发者并不需要关心这些对象是如何工作的,开发者,只需要在Handler(Controller)当中完成对请求的业务处理。
图片怎么上传
前端实现异步上传,后端使用springmvc的MultipartFile类型来接收,放到分布式图片服务器中,服务器返回图片路径把路径返回页面回显图片,开发或者测试环境可以使用FastDFS
微服务和SOA有什么区别?
如果一句话来谈SOA和微服务的区别,即微服务不再强调传统SOA架构里面比较重的ESB企业服务总线,同时SOA的思想进入到单个业务系统内部实现真正的组件化。说的更直白一点就是微服务被拆分的粒度更小
spring框架AOP执行原理简单说下?还有就是AOP在事务管理方面是怎么实现的?
Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
AOP在事务管理方面,Spring使用AOP来完成声明式的事务管理有annotation和xml两种形式。开发中,方便代码编写,很多时候都是在spring配置文件中配置事务管理器并开启事务控制注解。在业务类或业务类方法中添加@Transactional实现事务控制。
Spring 分布式事务如何处理的?
第一种方案:可靠消息最终一致性,需要业务系统结合MQ消息中间件实现,在实现过程中需要保证消息的成功发送及成功消费。即需要通过业务系统控制MQ的消息状态
第二种方案:TCC补偿性,分为三个阶段TRYING-CONFIRMING-CANCELING。每个阶段做不同的处理。
TRYING阶段主要是对业务系统进行检测及资源预留
CONFIRMING阶段是做业务提交,通过TRYING阶段执行成功后,再执行该阶段。默认如果TRYING阶段执行成功,CONFIRMING就一定能成功。
CANCELING阶段是回对业务做回滚,在TRYING阶段中,如果存在分支事务TRYING失败,则需要调用CANCELING将已预留的资源进行释放。
Springboot用过没,跟我说说,他的特点?
Springboot是从无数企业实战开发中总结出来的一个更加精炼的框架,使得开发更加简单,能使用寥寥数行代码,完成一系列任务。
1) Springboot解决那些问题
a) 编码更简单
i. Spring框架由于超重量级的XML,annotation配置,使得系统变得很笨重,难以维护
ii. Springboot采用约点大于配置的方法,直接引入依赖,即可实现代码的开发
b) 配置更简单
Xml文件使用javaConfig代替,XML中bean的创建,使用@bean代替后可以直接注入。
配置文件变少很多,就是application.yml
c) 部署更简单
d) 监控更简单
Spring-boot-start-actuator:
可以查看属性配置
线程工作状态
环境变量
JVM性能监控
支付接口是怎么做的?
微信支付
调用微信的支付接口,参考微信提供的api
使用了微信的统一下单接口和查询支付状态接口
每个接口需要的参数放入到map中使用微信提供的sdk转成XML字符串,httpClient远程提交参数和接收结果。
支付宝支付
SpringBoot相关面试题
什么是 Spring Boot?
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。
为什么要用 Spring Boot?
Spring Boot 优点非常多,如:
独立运行
简化配置
自动配置
无代码生成和XML配置
应用监控
上手容易
Spring Boot 的核心配置文件有哪几个?它们的区别是什么?
Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。
application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。
bootstrap 配置文件有以下几个应用场景。
- 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
- 一些固定的不能被覆盖的属性;
- 一些加密/解密的场景
Spring Boot 的配置文件有哪几种格式?它们有什么区别?
.properties 和 .yml,它们的区别主要是书写格式不同。
Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
开启 Spring Boot 特性有哪几种方式?
1)继承spring-boot-starter-parent项目
2)导入spring-boot-dependencies项目依赖
Spring Boot 需要独立的容器运行吗?
可以不需要,内置了 Tomcat/ Jetty 等容器.
运行 Spring Boot 有哪几种方式?
1)打包用命令或者放到容器中运行
2)用 Maven/ Gradle 插件运行
3)直接执行 main 方法运行
Spring Boot 自动配置原理是什么?
注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。
你如何理解 Spring Boot 中的 Starters?
Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。
Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖.
如何在 Spring Boot 启动的时候运行一些特定的代码?
可以实现接口 ApplicationRunner 或者 CommandLineRunner,这两个接口实现方式一样,它们都只提供了一个 run 方法.
Spring Boot 有哪几种读取配置的方式?
Spring Boot 可以通过 @PropertySource,@Value,@Environment, @ConfigurationProperties 来绑定变量
电商项目业务面试问题
哪些情况用到activeMq?
商品上架后更新ES索引库、更新静态页、发送短信
秒杀的时候,只有最后一件物品,该怎么去抢或者分配?
秒杀商品的库存都会放到redis中,在客户下单时就减库存,减完库存会判断库存是否为大于 0,如果小于0,表示库存不足,刚才减去的数量再恢复,整个过程使用redis的watch锁。
你项目对于订单是怎么处理的,假如一个客户在下订单的时候没有购买怎么办,对于顾客在购买商品的时候你们怎么处理你们的库存?
订单表中设置了一个过期时间,每天会有定时任务来扫描订单表数据,如果到达预订的过期时间没有付款就会取消此订单交易。
关于库存的设计是这样的:
普通商品在发货时才去更新库存,如果库存不足商家会马上补货
秒杀的商品会在客户下单时就减库存,如果在规定时间(半个小时)没有付款,会取消此订单把库存还原
redis存储格式的选择
redis支持的数据结构总共有5种:hash、value、list、set、zset,其中项目中用到最多是hash
商品表中的数据是哪里来的
商品表的数据是在商家管理后台中由商家录入的。数据分别录入到商品表、商品描述表和商品项表
当初设计项目时预计的访问量计划是多少
访问量计划是3000至5000
简单介绍一下你的这个项目以及项目中涉及到的技术框架以及使用场景以及你主要负责项目中的哪一块?
项目介绍时,先整体介绍是什么项目,项目主要是做啥的,为什么会做这个项目(市场需求)?例如:XXX电商项目,是一个B2B2C综合电商平台。由三个系统组成,包含:运营商管理后台、商家管理后台、网站前台。运营商平台主要负责基础数据维护、商家审核、商品审核等。商家管理后台主要负责商家入驻、商品录入/修改、商品上下架等。网站前台主要负责商品销售。包含:网站首页、商品搜索、商品详情展示、购物车、订单、支付、用户中心等模块。
再介绍自己在项目中做的功能模块。例如:运营商管理后台的品牌、规格数据录入,已经商品管理后台商品录入功能。同时,实现了网站前台系统中的商品搜索、购物车等功能模块。
然后介绍里面使用的技术:例如:dubbo分布式框架、ssm、es、redis、activeMQ、支付宝支付等等。最好是结合技术讲解项目功能点如何实现。
秒杀系统中如何防止超售?如何避免脚本进行恶意刷单?
防止超售解决方案:将存库从MySQL前移到Redis中,所有的写操作放到内存中,由于Redis中不存在锁故不会出现互相等待,并且由于Redis的写性能和读性能都远高于MySQL,这就解决了高并发下的性能问题。然后通过队列等异步手段,将变化的数据异步写入到DB中。当达到库存阀值的时候就不在消费队列,并关闭购买功能。
避免脚本恶意刷单:采用IP级别的限流,即针对某一个IP,限制单位时间内发起请求数量。
单点登录你们是自己编写的还是使用通用的CAS?
项目使用通用的CAS框架。
如果一个用户的token被其他用户劫持了,怎样解决这个安全问题。
a、在存储的时候把token进行对称加密存储,用时解开。
b、将请求URL、时间戳、token三者进行合并加盐签名,服务端校验有效性。
c、HTTPS对URL进行加密
项目部署上线后,运营商管理,商品审核等后台流量问题?
先询问流量是指哪方面?流量分为三种,一种是商家流量,另一种是用户流量,第三种运营商流量。
解决方案:
这三种流量对系统运行造成很大压力,随着项目上线时间增长,压力会越来越大,因此我们要减轻系统访问压力 ,就需要做一系列优化措施。
具体优化如下:
数据层面的优化:
从数据库层面做优化,比如:索引,缓存,集群,读写分离,主从复制,分表,分库。
从数据库设计层面的优化:比如减少表关联,加入冗余字段
从缓存方面优化:比如redis实现数据缓存,减轻数据库压力
从搜索上进行优化:比如查找索引库
项目层面的优化:
采用面向服务的分布式架构:分担服务器压力 ,提高项目并发量。 比如dubbox+zookeeper分布式架构
采用分布式文件系统实现海量文件存储:如采用fastdfs实现海量图片存储,提高文件的访问速度。
采用mq使用服务进一步解藕:同步索引库,同步静态资源,短信发送
服务器层面的优化:
集群思想的使用:tomcat,zookeeper,redis,mysql等
Tomcat异步通信的使用,tomcat连接池配置
秒杀和团购业务实现思路
回答:
将商品数量查询出存入到redis中,所有用户下单后,减掉redis中的数量
如果并发量很大时,还要考虑高并发问题,所以可以加入mq消息中间件处理抢单问题,再结合redis实现库存减少操作。高并发方面还可以考虑CDN,Nginx负载均衡等
你们项目中使用的安全框架是什么?
使用springSecurity 或者shiro,校验用户登录和用户权限!
项目中使用到的应用服务器是什么?
Tomcat+nginx
讲一下每台服务器的集群数量:
项目中一共15台项目服务,那么为了每一台高可用一主一备,但首页项目高并发设为四台服务器,则一共32台项目服务器,再加redis集群用了3台,为了每一台高可用一主一备一共6台,fastdfs一个trackerServer一个storageServer搭建集群一共6台,solr集群7台服务器,nginx为了高可用一主一备一共2台,mysql数据库集群3台!activemq消息中间件高可用2台;
共计:58台服务器!
你在项目开发中碰到过哪些重大且棘手的问题
场景一:需求不明确困境
在项目开发中,项目采用迭代开发,开发需求不是很明确,对于项目开发初期来说非常困难,进度非常慢,有时开发的出的产品结果往往不能令老板满意,或者是甲方满意,项目还需要不停的迭代,修改。
比如说:
在开发商城项目的时候,客户定位是一个综合性的商务平台,可以实现在线第三方商家对接,实现商品的销售
但是并没有明确的需求,因此开发全凭借电商的项目经验来实现里面的相关的业务,后期慢慢迭代。
场景二: ES高亮不能显示的问题
前台使用angularJS加载搜索结果,但是发现高亮不能展示。
问题原因:
angularJS底层使用ajax,异步加载高亮信息返回给页面后,页面没有刷新,就直接显示返回的数据。此时会把所有的数据作为普通的文本数据进行加载。因此就没有高亮的效果。
解决方案:
使用angularJS过滤器过滤文本数据,此时angularJS过滤器把html文本数据解析为浏览器能识别的html标签。高亮就能展示了。
场景三:Nginx静态页面服务跳转到购物车跨域问题
在Nginx中部署了静态页面,添加购物车时必须从静态页面跳转到购物车系统,实现购物车添加操作。
由于在静态页面中使用angularJS实现的跳转,发现跳转到购物车系统完全没有问题,但是并不能跳转回到购物车系统页面。
问题分析:
从静态详情系统跳转到购物车系统,会存在跨域问题,因此不能进行回调函数的数据传递。所以在回调函数中的页面跳转就不能实现。
解决方案:
使用angularJS跨域调用及springmvc跨域配置,解决问题。
场景四:activeMQ存在运行时间长了以后,收不到消息的现象
时间长了就会出现,卡死,新的数据不能从队列接听到。只能重启程序。
解决方案:
1)不要频繁的建立和关闭连接
JMS使用长连接方式,一个程序,只要和JMS服务器保持一个连接就可以了,不要频繁的建立和关闭连接。频繁的建立和关闭连接,对程序的性能影响还是很大的。这一点和jdbc还是不太一样的。
2)Connection的start()和stop()方法代价很高
JMS的Connection的start()和stop()方法代价很高,不能经常调用。我们试用的时候,写了个jms的connection pool,每次将connection取出pool时调用start()方法,归还时调用stop()方法,然而后来用jprofiler发现,一般的cpu时间都耗在了这两个方法上。
3)start()后才能收消息
Connection的start()方法调用后,才能收到jms消息。如果不调用这个方法,能发出消息,但是一直收不到消息。不知道其它的jms服务器也是这样。
4)显式关闭Session
如果忘记了最后关闭Connection或Session对象,都会导致内存泄漏。这个在我测试的时候也发现了。本来以为关闭了Connection,由这个Connection生成的Session也会被自动关闭,结果并非如此,Session并没有关闭,导致内存泄漏。所以一定要显式的关闭Connection和Session。
5)对Session做对象池
对Session做对象池,而不是Connection。Session也是昂贵的对象,每次使用都新建和关闭,代价也非常高。而且后来我们发现,原来Connection是线程安全的,而Session不是,所以后来改成了对Session做对象池,而只保留一个Connection。
6) 集群
ActiveMQ有强大而灵活的集群功能,但是使用起来还是会有很多陷阱
场景五:activeMQ存在发出消息太大,造成消息接受不成功
多个线程从activeMQ中取消息,随着业务的扩大,该机器占用的网络带宽越来越高。
仔细分析发现,mq入队时并没有异常高的网络流量,仅仅在出队时会产生很高的网络流量。
最终发现是spring的jmsTemplate与activemq的prefetch机制配合导致的问题。
研究源码发现jmsTemplate实现机制是:每次调用receive()时都会创建一个新的consumer对象,用完即销毁。
正常情况下仅仅会浪费重复创建consumer的资源代价,并不至于产生正常情况十倍百倍的网络流量。
但是activeMQ有一个提高性能的机制prefetch,此时就会有严重的问题。
prefetch机制:
每次consumer连接至MQ时,MQ预先存放许多message到消费者(前提是MQ中存在大量消息),预先存 放message的数量取决于prefetchSize(默认为1000)。此机制的目的很显然,是想让客户端代码用一个consumer反复进行 receive操作,这样能够大量提高出队性能。
此机制与jmsTemplate配合时就会产生严重的问题,每次jmsTemplate.receive(),都会产生1000个消息的网络流量, 但是因为jmsTemplae并不会重用consumer,导致后面999个消息都被废弃。反复jmsTemplate.receive()时,表面上看 不出任何问题,其实网络带宽会造成大量的浪费。
解决方案:
1、若坚持使用jmsTemplate,需要设置prefetch值为1,相当于禁用了activeMQ的prefetch机制,此时感觉最健壮, 就算多线程,反复调用jmsTemplate.receive()也不会有任何问题。但是会有资源浪费,因为要反复创建consumer并频繁与服务器进 行数据通信,但在性能要求不高的应用中也不算什么问题。
2、不使用jmsTemplate,手工创建一个consumer,并单线程反复使用它来receive(),此时可以充分利用prefetch机制。配合多线程的方式每个线程拥有自己的一个consumer,此时能够充分发挥MQ在大吞吐量时的速度优势。
切记避免多线程使用一个consumer造成的消息混乱。大吞吐量的应用推荐使用方案2,能够充分利用prefetch机制提高系MQ的吞吐性能。
商品的价格变化后,如何同步redis中数以百万计的购物车数据。
购物车只存储商品id,到购物车结算页面将会从新查询购物车数据,因此就不会涉及购物车商品价格同步的问题。
系统中的钱是如何保证安全的。
在当前互联网系统中钱的安全是头等大事,如何保证钱的安全可以从以下2个方面来思考:
1)钱计算方面
在系统中必须是浮点数计算类型存储钱的额度,否则计算机在计算时可能会损失精度。
2)事务处理方面
在当前环境下,高并发访问,多线程,多核心处理下,很容易出现数据一致性问题,此时必须使用事务进行控制,访问交易出现安全性的问题,那么在分布式系统中,存在分布式事务问题,可以有很多解决方案:
使用 jpa可以解决
使用 tcc 框架可以解决等等。
做交易或是金融系统安全性需要从哪些方面考虑?没有用什么第三方可以框架
ip黑白名单,访问日志明细记录,防止重复提交,访问频率控制,分布式锁,数据前后端校验,自动对账任务处理,互联网金融项目一般情况下,不建议自动重试,最好结合对账系统,人工进行处理,写好人工处理的接口就好。其他就是控制好数据的一致性了,这个最重要,其次还要保证接口的幂等性,不要重复处理订单。这些是最基本的安全控制了。像这类网站用户的输入数据一般都不会太多,一般敏感词过滤,广告之类的可以忽略,如果有的话还要控制这些。安全框架选shiro 了,在系统中分配好角色就好了,控制好用户的资源访问。其他的用springmvc 就够了
订单中的事物是如何保证一致性的。
使用分布式事务来进行控制,保证数据最终结果的一致性。
当商品库存数量不足时,如何保证不会超卖。
当库存数量不足时,必须保证库存不能被减为负数,如果不加以控制,库存被减为小于等于0的数,那么这就叫做超卖。
那么如何防止超卖的现象发生呢?
场景一: 如果系统并发要求不是很高
那么此时库存就可以存储在数据库中,数据库中加锁控制库存的超卖现象。
场景二:系统的并发量很大
如果系统并发量很大,那么就不能再使用数据库来进行减库存操作了,因为数据库加锁操作本身是以损失数据库的性能来进行控制数据库数据的一致性的。
但是当并发量很大的时候,将会导致数据库排队,发生阻塞。因此必须使用一个高效的nosql数据库服务器来进行减库存。
此时可以使用redis服务器来存储库存,redis是一个内存版的数据库,查询效率相当的高,可以使用watch来监控减库存的操作,一旦发现库存被减为0,立马停止售卖操作。
你们系统的商品模块和订单模块的数据库是怎么设计的
商品模块设计:
商品模块一共8张表,整个核心就是模板表。采用模板表为核心的设计方法,来构造商品数据。
订单设计:
订单涉及的表有:
1) 收货人地址
2) 订单信息
3) 订单明细
系统中商家活动策划以及上报相关业务流程。
商城系统中有以下活动:
1) 秒杀活动
a) 后台设置秒杀商品
b) 设置秒杀开启时间,定时任务,开启秒杀
c) 秒杀减库存(秒杀时间结束,库存卖完,活动结束)
2) 促销活动
3) 团购活动
4) 今日推荐
以上活动销售记录,统计,使用图形化报表进行统计,可以查看销售情况。
涉及到积分积累和兑换商品等业务是怎么设计的
积分累计有2大块:
积分累计:
根据用户购买的商品的价格不同,没有购买一定价格的商品,获取一定的积分。
积分商城:
积分商城是用户可以使用积分商品换取商品的区域。
介绍下电商项目,你觉得那些是亮点?
这个项目是为xxx开发的b2b2c类型综合购物平台,主要以销售xxx,电子产品为主要的电子商城网站。
项目的亮点是:
1)项目采用面向服务分布式架构(使用dubbo,zookeeper)
a) 解耦
b) 提高项目并发能力
c) 分担服务器压力
2)项目中使用activeMQ对项目进一步解耦
a) 提高项目并发能力
b) 提高任务处理速度
3) 使用微信支付,支付宝支付(自己总结)
4) 使用阿里大鱼发生短信
5) 使用第三方分布式文件系统存储海量文件
6) Nginx部署静态页面实现动静分离
购物车功能做了吗,实现原理说一下?
- 加入购物车
加入购物车插入到库中一条购物记录,同时插入到缓存中,缓存的key是记录的id
未登录状态
用户未登录时点击加入购物车,将productId ,skuId,buyNum 转换成json存到cookie中(同一件商品不同的skuId视为两个商品,相同的skuId和productId视为相同商品数量累加),用户登录成功的时候接收用户的消息将cookie中的商品信息保存到数据库中,然后清空cookie数据(京东)不然会出现登录成功后删除购物车商品然后退出,购物车中显示问题
登录状态
点击加入购物车将long userId,long productId,long skuId,int count 存到库中,相同的productId和skuId 数量累加,不同的skuId新增一条
addToCart(long userId,long skuId,int count); //加入sku到购物车商品 - 修改商品数量
未登录状态
用户未登录时,点击加减数量,根据productId和skuId从cooike中将商品数量进行加减,注意校验cooike中的数量不能小于0,不能大于库存数量
登录状态
用户登录状态时,点击加减数量productId和skuId,userId将用户购物车中某个sku的数量增加或减去differ值,注意校验库存数量
updateAmount(long userId,skuId,int differ,List selectedSkuIds); //将用户购物车中某个sku的数量增加或减去differ值。此方法更新商品后,会根据selectedSkuIds重新计算一遍购物车价格,返回满足条件的优惠券 - 删除购物车记录
未登录状态
用户未登录时,根据productId和skuId删除cookie中的记录
deleteCart(long userId,long skuId, List selectedSkuIds); //将某个sku从用户购物车移除。此接品,在清除后台会重复计算selectedSkuIds价格,并会返回选中的sku列表与未选中的sku列表集合。及相应优惠券。
登录状态
登录状态下,直接根据productId和skuId以及userId删除库中数据
4购物车列表展示
未登录状态
从cookie中取出productId以及skuId列表展示商品信息
登录状态
登录状态下根据用户id查询库中的记录数
getCart(long userId,list selectedSkuIds); //查询用户购物车。此接口会重新计算selectedSkuIds,并返回选中与未选中sku列表集合,返回相应的满足条件的优惠券信息。
5.订单提交成功后更新购物车数量以及修改购物车状态
订单提交成功后接收订单成功消息,更新购物车状态和数量删除缓存记录
6.商品下架后,更新库存状态,显示失效
商品下架后接收消息修改购物车里的商品状态为失效
你们的项目上线了吗?这么大的项目怎么没上线?
项目上线问题回答:
1) 项目没有上线
如果你没有做过电商的项目,可以说项目没有上线之前,你离职了,这个一个创业型的公司,或者此项目是给甲方做的项目,你没有参与上线。以此来回避这个问题.
2) 项目上线
项目已经上线了
上线环境:
a) Centos7
b) Mysql
c) Jdk8
d) Tomcat8
关于上线,那么面试官一定会问您,上线遇到什么问题没有?
因此必须把项目中遇到的问题准备2个,以下可以作为参考
问题一:(用户非正常流程导致的错误)
用户注册一半就退出来,导致再次注册不成功或者用证件号登陆触发空指针异常。
解决办法:一旦输入证件号时,检查数据库的表是否有相应的证件号记录,有则把相关记录全部删掉,从而让他成功注册。空指针异常的解决办法,做非空验证的判断。
问题二:(并发插入,流水号不一致)
出现大量的主键唯一约束错误,后来想到是产生的预报名号不同步,导致有可能大并发量时产生多个相同的流水号,插入就会出现主键唯一约束错误。
解决办法:在数据库里写一个insert的触发器。自动替换掉要插入的主键为 max(key)+1.
问题三:(并发删除,索引失效)
出现某些表的索引失效,后来发现是插入相同主键多次之后导致表失效。
解决办法:设定oracle任务,让数据库每隔12个小时自动重建所有索引。
问题四:(js代码的不细致)
发现报考志愿显示的专业比原来的少一个。
解决办法:发现时jsp页面的js少循环一个=号导致的。。。。
问题五:(页面和后台代码太不通用)
用户需求一旦更改或者程序逻辑有错误后,导致要修改很多页面和后台代码,十分不通用,要从业务逻辑上设计的通用点。改一个,就能等同于改全部。用一些设计模式去解决。
订单怎么实现的,你们这个功能怎么这么简单?
订单实现:
从购物车系统跳转到订单页面,选择默认收货地址
选择支付方式
购物清单展示
提交订单
订单业务处理:
一个商家一个订单,不同的仓库发送的货品也是属于不同的订单。因此会产出不同的订单号。
订单处理:根据支付的状态进行不同的处理
1) 在线支付
a) 支付未成功—从新发起支付
b) 支付超时---订单关闭
2) 货到付款
你们这个项目有秒杀吗,怎么实现的?
所谓“秒杀”,就是网络卖家发布一些超低价格的商品,
所有买家在同一时间网上抢购的一种销售方式。通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动。由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟。
秒杀商品通常有两种限制:库存限制、时间限制。
需求:
(1)商家提交秒杀商品申请,录入秒杀商品数据,主要包括:商品标题、原价、秒杀价、商品图片、介绍等信息
(2)运营商审核秒杀申请
(3)秒杀频道首页列出秒杀商品(进行中的)点击秒杀商品图片跳转到秒杀商品详细页。
(4)商品详细页显示秒杀商品信息,点击立即抢购实现秒杀下单,下单时扣减库存。当库存为0或不在活动期范围内时无法秒杀。
(5)秒杀下单成功,直接跳转到支付页面(微信扫码),支付成功,跳转到成功页,填写收货地址、电话、收件人等信息,完成订单。
(6)当用户秒杀下单5分钟内未支付,取消预订单,调用微信支付的关闭订单接口,恢复库存。
数据库表分析
Tb_seckill_goods 秒杀商品表
Tb_seckill_order 秒杀订单表
秒杀实现思路
秒杀技术实现核心思想是运用缓存减少数据库瞬间的访问压力!读取商品详细信息时运用缓存,当用户点击抢购时减少redis中的库存数量,当库存数为0时或活动期结束时,同步到数据库。 产生的秒杀预订单也不会立刻写到数据库中,而是先写到缓存,当用户付款成功后再写入数据库。
你们这个项目用的什么数据库,数据库有多少张表?
项目使用mysql数据库,总共有103张表,其中商品表共计有8张。
项目部署做过吗,能不能部署?
做过,可以部署。
项目服务器:集群部署
数据库服务器:集群部署
Nginx集群:负载均衡
单点登录怎么做的,用别人知道原理吗?
在分布式项目中实现session共享,完成分布式系统单点登录
3) Cookie中共享ticket
4) Redis存储session
分布式系统共享用户身份信息session,必须先获取ticket票据,然后再根据票据信息获取redis中用户身份信息。
实现以上2点即可实现session共享。
目前项目中使用的springsecurity + cas 来实现的单点登录,cas自动产生ticket票据信息,每次获取用户信息,cas将会携带ticket信息获取用户身份信息。
支付做了吗,支付宝还是微信,实现说下?
微信支付:
1) 调用微信支付下单接口
2) 返回支付地址,生成二维码
3) 扫描二维码即可完成支付
问题: 微信支付二维码是我们自己生成的,因此必须时刻监控微信支付二维码的状态,确保支付成功。
支付宝支付可以参考www.alipay.com
缓存及优化方面的面试问题
怎么提高redis缓存利用率?
1、从业务场景分析,预计会高频率用到的数据预先存放到redis中,
2、可以定时扫描命中率低的数据,可以直接从redis中清除。
怎么实现数据量大、 并发量高的搜索
创建solr索引库,数据量特别大时采用solr分布式集群
MySQL索引使用限制
不要在列上进行运算。
select * from users where YEAR(adddate)<2007; 将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成select * from users where adddate<‘2007-01-01’;
like语句操作
如果使用like。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
select * from users where name like '%aaa%'不会使用索引
select * from users where name like 'aaa%'可以使用索引
使用短索引
例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
索引不会包含NULL列
复合索引中如果有一列含有NULL值那么这个组合索引都将失效,一般需要给默认值0或者 ' '字符串
最左匹配
不按索引最左列开始查询(多列索引) 例如index(‘c1’, ‘c2’, ‘c3’) ,where ‘c2’ = ‘aaa’ 不使用索引,where ‘c2’ = ‘aaa’ and ‘c3’ = ‘sss’ 不能使用索引。where ‘c1’ = ‘aaa’ and ‘c2’ = ‘bbb’ 可以使用索引
多列范围查询
查询中某个列有范围查询,则其右边的所有列都无法使用查询(多列查询)。where c1= ‘xxx’ and c2 like = ‘aa%’ and c3=’sss’ 该查询只会使用索引中的前两列,c3将不能使用到索引,因为like是范围查询。
检索排序
一个查询语句中,既有检索又有排序并且是不同的字段,且这两个列上都有单列索引(独立索引),那么只有其中一个列用到索引,因为查询优化器在做检索和排序中不能同时使用两个不同的索引
索引散列度
通过索引扫描的记录超过了表总行数的30%(估计值),则查询优化器认为全表扫描的效率更高,所以会变成全表扫描查询
隐式转换
隐式转换导致的索引失效。比如,表的字段tu_mdn定义为varchar(20),但在查询时把该字段作为number类型当做where条件,这样会导致索引失效. 错误的例子:select * from test where tu_mdn=13333333333; 正确的例子:select * from test where tu_mdn='13333333333’;
怎么分词
使用第三方的分词器IKAnalyzer,会按照中国人用此习惯自动分词。
seo怎么优化
使用restful,或静态页这样能更好的被搜索引擎收录。
怎么加快访问速度
硬件上加大网络带宽、和服务器内存
代码的处理:静态页面、缓存、优化sql、创建索引等方案
讲到redis缓存的时候说不清楚
- redis中项目中的应用。1.主要应用在门户网站首页广告信息的缓存。因为门户网站访问量较大,将广告缓存到redis中,可以降低数据库访问压力,提高查询性能。2.应用在用户注册验证码缓存。利用redis设置过期时间,当超过指定时间后,redis清理验证码,使过期的验证码无效。3.用在购物车模块,用户登陆系统后,添加的购物车数据需要保存到redis缓存中。
- 技术角度分析:
内存如果满了,采用LRU算法进行淘汰。
Redis如何实现负载的?采用Hash槽来运算存储值,使用CRC16算法取模运算,来保证负载问题。
Redis缓存穿透问题?将数据查询出来如果没有强制设置空值,并且设置过期时间,减少频繁查询数据库。
能讲下redis的具体使用场景吗?使用redis存储长期不改变的数据完全可以使用也看静态化,那么你们当时是为什么会使用redis?
redis在项目中应用:1.主要应用在门户网站首页广告信息的缓存。因为门户网站访问量较大,将广告缓存到redis中,可以降低数据库访问压力,提高查询性能。2.应用在用户注册验证码缓存。利用redis设置过期时间,当超过指定时间后,redis清理验证码,使过期的验证码无效。3.用在购物车模块,用户登陆系统后,添加的购物车数据需要保存到redis缓存中。
使用redis主要是减少系统数据库访问压力。从缓存中查询数据,也提高了查询性能,挺高用户体验度。
redis中对一个key进行自增或者自减操作,它是原子性的吗?
是原子性的。对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。Redis的操作之所以是原子性的,是因为Redis是单线程的。对Redis来说,执行get、set以及eval等API,都是一个一个的任务,这些任务都会由Redis的线程去负责执行,任务要么执行成功,要么执行失败,这就是Redis的命令是原子性的原因。Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
你们项目中使用到的数据库是什么?你有涉及到关于数据库到建库建表操作吗?数据库创建表的时候会有哪些考虑呢?
项目中使用的是MySQL数据库,
数据库创建表时要考虑
a、大数据字段最好剥离出单独的表,以便影响性能
b、使用varchar,代替char,这是因为varchar会动态分配长度,char指定为20,即时你存储字符“1”,它依然是20的长度
c、给表建立主键,看到好多表没主键,这在查询和索引定义上将有一定的影响
d、避免表字段运行为null,如果不知道添加什么值,建议设置默认值,特别int类型,比如默认值为0,在索引查询上,效率立显。
e、建立索引,聚集索引则意味着数据的物理存储顺序,最好在唯一的,非空的字段上建立,其它索引也不是越多越好,索引在查询上优势显著,在频繁更新数据的字段上建立聚集索引,后果很严重,插入更新相当忙。
f、组合索引和单索引的建立,要考虑查询实际和具体模式
mysql中哪些情况下可以使用索引,哪些情况不能使用索引?mysql索引失效的情形有哪些?
使用索引:
a、 为了快速查找匹配WHERE条件中涉及到列。
b、 如果表有一个multiple-column索引,任何一个索引的最左前缀可以通过使用优化器来查找行
c、 当运行joins时,为了从其他表检索行。MySql可以更有效的使用索引在多列上如果他们声明的类型和大小是一样的话。在这个环境下,VARCHAR和CHAR是一样的如果他们声明的大小是一样的
d、 为了找到 MIN() or MAX()的值对于一个指定索引的列key_col.
总之,就是经常用到的列就最好创建索引。
不能使用引用:
a) 数据唯一性差(一个字段的取值只有几种时)的字段不要使用索引
比如性别,只有两种可能数据。意味着索引的二叉树级别少,多是平级。这样的二叉树查找无异于全表扫描
b) 频繁更新的字段不要使用索引
比如logincount登录次数,频繁变化导致索引也频繁变化,增大数据库工作量,降低效率
c) 字段不在where语句出现时不要添加索引,如果where后含IS NULL /IS NOT NULL/ like ‘%输入符%’等条件,不建议使用索引只有在where语句出现,mysql才会去使用索引
d) where 子句里对索引列使用不等于(<>),使用索引效果一般
索引失效:
a.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)
注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
b.对于多列索引,不是使用的第一部分,则不会使用索引
c.like查询是以%开头
d.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
e.如果mysql估计使用全表扫描要比使用索引快,则不使用索引
8,java中的多线程在你们的这个项目当中有哪些体现?
a,后台任务:如定时向大量(100W以上)的用户发送邮件;定期更新配置文件、任务调度(如quartz),一些监控用于定期信息采集
b, 自动作业处理:比如定期备份日志、定期备份数据库
c, 异步处理:如发微博、记录日志
Redis分布式锁理解
实现思想
获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
Redis怎么设置过期的?项目过程中,使用了哪一种持久化方式
设置过期:
this.redisTemplate.expire("max",tempTime,TimeUnit.SECONDS);
持久化方式:Redis默认的RDB方式
项目添加Redis缓存后,持久化具体怎么实现的。
RDB:保存存储文件到磁盘;同步时间为15分钟,5分钟,1分钟一次,可能存在数据丢失问题。
AOF:保存命令文件到磁盘;安全性高,修改后立即同步或每秒同步一次。
上述两种方式在我们的项目中都有使用到,在广告轮播的功能中使用了redis缓存,先从redis中获取数据,无数据后从数据库中查询后保存到redis中
采用默认的RDB方式,在广告轮播的功能中使用了redis缓存,先从redis中获取数据,无数据就从数据库中查询后再保存到redis中
项目中有用到过redis,访问redis是通过什么访问的?redis能够存储的数据类型有哪几种?
Redis通过SpringDataRedis访问的.
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)
怎样进行程序性能调优
系统性能就是两个事:
Throughput ,吞吐量。也就是每秒钟可以处理的请求数,任务数。
Latency, 系统延迟。也就是系统在处理一个请求或一个任务时的延迟。
那么Latency越好,能支持的Throughput就会越高。因为Latency短说明处理速度快,于是就可以处理更多的请求。
提高吞吐量:
分布式集群,模块解藕,设计模式
系统延迟:
异步通信
数据库设计的面试问题
你有了解mysql的隔离级别吗?mysql默认的隔离级别是什么?
数据库事务的隔离级别有四种,隔离级别高的数据库的可靠性高,但并发量低,而隔离级别低的数据库可靠性低,但并发量高,系统开销小。
- READ UNCIMMITTED(未提交读)
- READ COMMITTED(提交读)
- REPEATABLE READ(可重复读)
- SERIALIZABLE(可串行化)
mysql默认的事务处理级别是'REPEATABLE-READ',也就是可重复读。
sql语句中关于查询语句的优化你们是怎么做的?
1、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
2、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
3、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
4、尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
5、in 和 not in 也要慎用,否则会导致全表扫描
6、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
7、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描
8、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
9、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。
10、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
11、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
12、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
mysql索引失效的场景有哪些?like做模糊查询的时候会失效吗?
1.WHERE字句的查询条件里有不等于号(WHERE column!=…),MYSQL将无法使用索引
2.类似地,如果WHERE字句的查询条件里使用了函数(如:WHERE DAY(column)=…),MYSQL将无法使用索引
3.在JOIN操作中(需要从多个数据表提取数据时),MYSQL只有在主键和外键的数据类型相同时才能使用索引,否则即使建立了索引也不会使用
4.如果WHERE子句的查询条件里使用了比较操作符LIKE和REGEXP,MYSQL只有在搜索模板的第一个字符不是通配符的情况下才能使用索引。比如说,如果查询条件是LIKE 'abc%',MYSQL将使用索引;如果条件是LIKE '%abc',MYSQL将不使用索引。
5.在ORDER BY操作中,MYSQL只有在排序条件不是一个查询条件表达式的情况下才使用索引。尽管如此,在涉及多个数据表的查询里,即使有索引可用,那些索引在加快ORDER BY操作方面也没什么作用。
6.如果某个数据列里包含着许多重复的值,就算为它建立了索引也不会有很好的效果。比如说,如果某个数据列里包含了净是些诸如“0/1”或“Y/N”等值,就没有必要为它创建一个索引。
7.索引有用的情况下就太多了。基本只要建立了索引,除了上面提到的索引不会使用的情况下之外,其他情况只要是使用在WHERE条件里,ORDER BY 字段,联表字段,一般都是有效的。 建立索引要的就是有效果。 不然还用它干吗? 如果不能确定在某个字段上建立的索引是否有效果,只要实际进行测试下比较下执行时间就知道。
8.如果条件中有or(并且其中有or的条件是不带索引的),即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)。注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
9.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
10.如果mysql估计使用全表扫描要比使用索引快,则不使用索引
问题二:Like模糊查询,建立索引会失效
项目中关于表结构拆分,你们是业务层面的拆分还是表结构层面的拆分?
表结构层面的拆分。通过mycat数据库中间件完成数据库分表操作。
业务层面也有拆分,比如商品模块拆分成8张表来实现存储
有了解过大数据层面的分库分表吗?以及mysql的执行计划吗?
分库:通过Mycat结点来管理不同服务器上的数据库,每个表最多存500万条记录
分表:重直切割,水平切割
MySql提供了EXPLAIN语法用来进行查询分析,在SQL语句前加一个"EXPLAIN"即可。mysql中的explain语法可以帮助我们改写查询,优化表的结构和索引的设置,从而最大地提高查询效率。
有了解过数据库中的表级锁和行级锁吗?乐观锁和悲观锁你有哪些了解?
MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);InnoDB存储引擎既支持行级锁( row-level locking),也支持表级锁,但默认情况下是采用行级锁。
MySQL主要的两种锁的特性可大致归纳如下:
表级锁: 开销小,加锁快;不会出现死锁(因为MyISAM会一次性获得SQL所需的全部锁);锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁: 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
乐观锁:通过version版本字段来实现
悲观锁:通过for update来实现
Mysql优化有没有工具
三个MySQL性能测试工具:The MySQL Benchmark Suite、MySQL super-smack、MyBench。除了第一个为MySQL性能测试工具,其他两个都为压力测试工具。
你们项目中使用到的数据库是什么?你有涉及到关于数据库到建库建表操作吗?数据库创建表的时候会有哪些考虑呢?
项目中使用的是MySQL数据库,
数据库创建表时要考虑
a、大数据字段最好剥离出单独的表,以便影响性能
b、使用varchar,代替char,这是因为varchar会动态分配长度,char指定为20,即时你存储字符“1”,它依然是20的长度
c、给表建立主键,看到好多表没主键,这在查询和索引定义上将有一定的影响
d、避免表字段运行为null,如果不知道添加什么值,建议设置默认值,特别int类型,比如默认值为0,在索引查询上,效率立显。
e、建立索引,聚集索引则意味着数据的物理存储顺序,最好在唯一的,非空的字段上建立,其它索引也不是越多越好,索引在查询上优势显著,在频繁更新数据的字段上建立聚集索引,后果很严重,插入更新相当忙。
f、组合索引和单索引的建立,要考虑查询实际和具体模式
怎样进行数据库优化?
a,选取最适用的字段
在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOTNULL,
b,使用连接(JOIN)来代替子查询(Sub-Queries)
c,使用联合(UNION)来代替手动创建的临时表
d,事物:
a)要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
b) 是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。
e,锁定表
f,使用外键
锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。
g,使用索引
h,优化的查询语句
怎样进行数据库性能调优
一应用程序优化
(1)把数据库当作奢侈的资源看待,在确保功能的同时,尽可能少地动用数据库资源。
(2)不要直接执行完整的SQL 语法,尽量通过存储过程实现数据库操作。
(3)客户与服务器连接时,建立连接池,让连接尽量得以重用,以避免时间与资源的损耗。
(4)非到不得已,不要使用游标结构,确实使用时,注意各种游标的特性。
二基本表设计优化
(1)表设计遵循第三范式。在基于表驱动的信息管理系统中,基本表的设计规范是第三范式。
(2)分割表。分割表可分为水平分割表和垂直分割表两种:水平分割是按照行将一个表分割为多个表。
(3)引入中间表。
三 数据库索引优化
索引是建立在表上的一种数据组织,它能提高访问表中一条或多条记录的特定查询效率。
聚集索引
一种索引,该索引中键值的逻辑顺序决定了表中相应行的物理顺序。
聚集索引确定表中数据的物理顺序。
非聚集索引
一种索引,该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同.
分布式开发面试问题
分布式架构session共享问题,如何在集群里边实现共享。
用了CAS,所有应用项目中如果需要登录时在web.xml中配置过滤器做请求转发到cas端工作原理是在cas登录后会给浏览器发送一个票据(ticket),浏览器cookie中会缓存这个ticket,在登录其他项目时会拿着浏览器的ticket转发到cas,到cas后根据票据判断是否登录
项目中如何配置集群?
配置了redis集群,使用redis3.0版本官方推荐的配置方式
solr集群使用了solrCloud,使用zookeeper关联solrCloud的配置文件
zookeeper也配置了集群
应用层使用Nginx负载均衡
对分布式,dubbo,zookeeper说的不太清楚
分布式是从项目业务角度考虑划分项目整个架构。可以将项目基于功能模块划分再分别部署。Dubbo是实现分布式项目部署框架。在zookeeper是dubbo分布式框架的注册中心,管理服务的注册和调用。
从前端到后台的实现的过程描述的也不清楚
项目前端采用angularjs框架在controller控制器中完成数据组装和数据展示,在服务层(service)代码完成中后台请求操作。后端基于前端的接口调用,完成数据的增删改查操作。前后端数据交互通过json格式字符串完成。
Dubbo为什么选择Zookeeper,而不选择Redis
引入了ZooKeeper作为存储媒介,也就把ZooKeeper的特性引进来。
首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;
资源同步,单单有负载均衡还不够,节点之间的数据和资源需要同步,ZooKeeper集群就天然具备有这样的功能;
命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。 其他特性还有Mast选举,分布式锁等。
项目中Zookeeper服务器挂了,服务调用可以进行吗
可以的,消费者在启动时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。
每次调用时,按照本地存储的地址进行调用
如何保证dubbo高可用?
1. zookeeper宕机与dubbo直连
在实际生产中,假如zookeeper注册中心宕掉,一段时间内服务消费方还是能够调用提供方的服务的,实际上它使用的本地缓存进行通讯,这只是dubbo健壮性的一种。
dubbo的健壮性表现:
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
注册中心的作用在于保存服务提供者的位置信息,我们可以完全可以绕过注册中心——采用dubbo直连,即在服务消费方配置服务提供方的位置信息。
点对点直连方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。
xml配置方式
<dubbo:reference id="userService" interface="com.zang.gmall.service.UserService" url="dubbo://localhost:20880" />
注解上直接添加
@Reference(url = "127.0.0.1:20880")
UserService userService;
2. 集群下dubbo负载均衡配置
在集群负载均衡时,Dubbo提供了4种均衡策略,如:Random LoadBalance(随机均衡算法)、RoundRobin LoadBalance(权重轮循均衡算法)、LeastAction LoadBalance(最少活跃调用数均衡算法)、ConsistentHash LoadBalance(一致性Hash均衡算法)。缺省时为Random随机调用。
@Reference(loadbalance = "roundrobin")
UserService userService;
服务方方法级别配置(基于xml配置的权重轮询均衡算法)
<dubbo:service interface="com.zang.gmall.service.UserService"
<dubbo:method name="getUserAddressList" loadbalance="roundrobin"></dubbo:method>
</dubbo:service>
3. 权重设置
当不设置负载均衡策略,即采用默认的Random LoadBalance(随机均衡算法)时,默认每个服务的权重相同,我们可以通过设置权重来分配访问的随机性。
权重默认为100,可以在暴露服务的同时设置
4. 服务降级
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略(不调用服务即返回为空 or 调用失败返回为空)。
向注册中心写入动态配置覆盖规则:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
其中:
- mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
- 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
通过操作管理控制台也可以方便的进行服务降级
5. 集群容错
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
集群容错模式主要有以下几种:
Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
消费方服务级注解添加(不能到方法级)
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
集群模式配置方法
在服务提供方和消费方配置集群模式
6. 整合hystrix
Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。
spring boot官方提供了对hystrix的集成,直接在pom.xml里加入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>
然后在Application类上增加@EnableHystrix
来启用hystrix starter:
配置服务提供方:
在Dubbo的Provider上增加@HystrixCommand 配置,这样子调用就会经过Hystrix代理。
配置服务消费方:
对于Consumer端,则可以增加一层method调用,并在method上配置@HystrixCommand 。当调用出错时,会走到 fallbackMethod = "reliable" 的调用里。
@HystrixCommand注解设置的 reliable 调用方法的里的参数要和 method 的参数保持一致。
ActiveMq消息被重复消费,丢失,或者不消费怎么办
重复消费:Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
丢消息:用持久化消息,或者非持久化消息及时处理不要堆积,或者启动事务,启动事务后,commit()方法会负责任的等待服务器的返回,也就不会关闭连接导致消息丢失了。
不消费:去ActiveMQ.DLQ里找找
怎样解决activeMQ的消息持久化问题?
A:持久化为文件
这个你装ActiveMQ时默认就是这种,只要你设置消息为持久化就可以了。涉及到的配置和代码有
<persistenceAdapter>
<kahaDB directory="${activemq.base}/data/kahadb"/>
</persistenceAdapter>
producer.Send(request, MsgDeliveryMode.Persistent, level, TimeSpan.MinValue);
B:持久化为MySql
加载驱动jar,为数据中创建三个数据库表,存储activemq的消息信息
如果activeMQ的消息没有发送成功,怎样确保再次发送成功。
重新传递消息的情况
ActiveMQ在接收消息的Client有以下几种操作的时候,需要重新传递消息:
1:Client用了transactions(事务),且在session中调用了rollback()
2:Client用了transactions,且在调用commit()之前关闭
3:Client在CLIENT_ACKNOWLEDGE的传递模式下,在session中调用了recover()
确保客户端有几种状态,检测状态,只要提交了那就说明客户端成功!
Zookeeper怎样进行服务治理。
接受提供者的接口信息和提供者ip地址进行存储,然后管理消费者和提供者之间调用关系!
如果activeMQ的服务挂了,怎么办?
1、在通常的情况下,非持久化消息是存储在内存中的,持久化消息是存储在文件中的,它们的最大限制在配置文件的<systemUsage>节点中配置。但是,在非持久化消息堆积到一定程度,内存告急的时候,ActiveMQ会将内存中的非持久化消息写入临时文件中,以腾出内存。虽然都保存到了文件里,但它和持久化消息的区别是,重启后持久化消息会从文件中恢复,非持久化的临时文件会直接删除。
2、考虑高可用,实现activemq集群。
如果zookeeper服务挂了怎么办?
注册中心对等集群,任意一台宕掉后,会自动切换到另一台
注册中心全部宕掉,服务提供者和消费者仍可以通过本地缓存通讯
服务提供者无状态,任一台宕机后,不影响使用
服务提供者全部宕机,服务消费者会无法使用,并无限次重连等待服务者恢复
Dubbo有3次重试,假如新消息被重复消费怎么处理
1、去掉超时重试机制
2、服务端增加幂等校验,服务器加入校验机制,如果这个消息已被 消费就不再重复消费
MQ消费者接收不到消息怎么办。
Mq消费者接受不到消息存在2中情况:
1. 处理失败 指的是MessageListener的onMessage方法里抛出RuntimeException。
2. Message头里有两个相关字段:Redelivered默认为false,redeliveryCounter默认为0。
3. 消息先由broker发送给consumer,consumer调用listener,如果处理失败,本地redeliveryCounter++,给broker一个特定应答,broker端的message里redeliveryCounter++,延迟一点时间继续调用,默认1s。超过6次,则给broker另一个特定应答,broker就直接发送消息到DLQ。
4. 如果失败2次,consumer重启,则broker再推过来的消息里,redeliveryCounter=2,本地只能再重试4次即会进入DLQ。
5. 重试的特定应答发送到broker,broker即会在内存将消息的redelivered设置为true,redeliveryCounter++,但是这两个字段都没有持久化,即没有修改存储中的消息记录。所以broker重启时这两个字段会被重置为默认值。
系统的高并发问题是怎么解决的。
并发问题高,这个问题的解决方案是一个系统性的,系统的每一层面都需要做优化:
1) 数据层
a) 集群
b) 分表分库
c) 开启索引
d) 开启缓存
e) 表设计优化
f) Sql语句优化
g) 缓存服务器(提高查询效率,减轻数据库压力)
h) 搜索服务器(提高查询效率,减轻数据库压力)
2) 项目层
a) 采用面向服务分布式架构(分担服务器压力,提高并发能力)
b) 采用并发访问较高的详情系统采用静态页面
c) 使用页面缓存
d) 用ActiveMQ使得业务进一步进行解耦,提高业务处理能力
e) 使用分布式文件系统存储海量文件
3) 应用层
a) Nginx服务器来做负载均衡
b) Lvs做二层负载
并发数多少,项目中怎么解决并发问题?
面试中项目的并发数不宜说的过大,安装目前谷粒商城项目拆分规模,这个项目的并发是在10000+,但是学生面试不能说的这么高。
可以有以下2方面的回答:
1) 项目并发并不清楚(只是底层程序员)
2) 参与核心业务设计,知道并发是多少(测试峰值,上线并发)
3000---5000吧
面对项目高并发,项目必须做各种优化措施了:
4) 数据层
a) 集群
b) 分表分库
c) 开启索引
d) 开启缓存
e) 表设计优化
f) Sql语句优化
g) 缓存服务器(提高查询效率,减轻数据库压力)
h) 搜索服务器(提高查询效率,减轻数据库压力)
5) 项目层
a) 采用面向服务分布式架构(分担服务器压力,提高并发能力)
b) 采用并发访问较高的详情系统采用静态页面
c) 使用页面缓存
d) 用ActiveMQ使得业务进一步进行解耦,提高业务处理能力
e) 使用分布式文件系统存储海量文件
6) 应用层
a) Nginx服务器来做负载均衡
b) Lvs做二层负载
消息发送失败怎么处理,发送数据,数据库已经保存了数据,但是redis中没有同步,怎么办。或者说如何做到消息同步。
消息发送失败,可以进行消息的重新发送,可以配置消息的重发次数。
如果消息重发完毕后,消息还没有接受成功,重启服务。
Dubbo的通信原理?
Dubbo底层使用hessain2进行二进制序列化进行远程调用
Dubbo底层使用netty框架进行异步通信。NIO
其他技术面试问题
单点登录的访问或者跨域问题
首先要理解什么是单点登录。单点登录是相互信任的系统模块登录一个模块后,其他模块不需要重复登录即认证通过。项目采用的是CAS单点登录框架完成的。首先CAS有两大部分。客户端和服务端。服务端就是一个web工程部署在tomcat中。在服务端完成用户认证操作。每次访问系统模块时,需要去CAS完成获取ticket。当验证通过后,访问继续操作。对于CAS服务端来说,我们访问的应用模块就是CAS客户端。
跨域问题,首先明白什么是跨域。什么时候涉及跨域问题。当涉及前端异步请求的时候才涉及跨域。那什么是跨域呢?当异步请求时,访问的请求地址的协议、ip地址、端口号任意一个与当前站点不同时,就会涉及跨域访问。解决方案:1、jQuery提供了jsonp实现2、W3C标准提供了CORS(跨域资源共享)解决方案。
shiro安全认证时如何做的
要明白shiro执行流程以及shiro的核心组件
认证过程:
在application Code应用程序中调用subject的login方法。将页面收集的用户名和密码传给安全管理器securityManager,将用户名传给realm对象。Realm对象可以理解为是安全数据桥,realm中认证方法基于用户名从数据库中查询用户信息。如果用户存在,将数据库查询密码返回给安全管理器securityManager,然后安全管理器判断密码是否正确。
ES的用途
ES在系统中主要完成商品搜索功能,提高搜索性能。
分布式锁的问题
针对分布式锁的实现,目前比较常用的有以下几种方案:
1.基于数据库实现分布式锁
2.基于缓存(redis,memcached,tair)实现分布式锁
3.基于zookeeper实现分布式锁
ES索引中使用了IK分词器,你们项目中使用到了分词器的哪种工作模式?
IK分词器,基本可分为两种模式,一种为smart模式,一种为非smart模式。
例如:张三说的确实在理
smart模式的下分词结果为:
张三 | 说的 | 确实 | 在理
而非smart模式下的分词结果为:
张三 | 三 | 说的 | 的确 | 的 | 确实 | 实在 | 在理
可见非smart模式所做的就是将能够分出来的词全部输出;smart模式下,IK分词器则会根据内在方法输出一个认为最合理的分词结果,这就涉及到了歧义判断。
项目中采用的是smart模块分词的。
java中关于多线程的了解你有多少?线程池有涉及吗?
同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。线程分为五个阶段:创建、就绪、运行、阻塞、终止。
Java线程有五种基本状态
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Java中线程的创建常见有如三种基本形式
1.继承Thread类,重写该类的run()方法。
2.实现Runnable接口,并重写该接口的run()方法,
该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。
3.使用Callable和Future接口创建线程。
具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
线程池:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
如何实现线程的同步?
为何要使用同步?
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
线程同步(5种同步方式)
1.同步方法 2.同步代码块 3.使用特殊域变量(volatile)实现线程同步 4.使用重入锁实现线程同步 5.使用局部变量实现线程同步
遍历hashmap有几种方式?
Map的四种遍历方式
(1) for each map.entrySet()
(2) 显示调用map.entrySet()的集合迭代器
(3) for each map.keySet(),再调用get获取
(4) for each map.entrySet(),用临时变量保存map.entrySet()
简单介绍一下Es全文检索在整个系统中的应用,在更新索引库的同时会产生索引碎片,这个碎片是如何处理的?
根据商品的名称,分类,品牌等属性来创建索引进行商品搜索。
更新索引库时会先删除索引,然后再重建。而对于删除聚集索引,则会导致对应的非聚集索引重建两次(删除时重建,建立时再重建).直接删除碎片。
java并发包下有哪些并发组件?
分为两层组成
外层框架主要有Lock(ReentrantLock、ReadWriteLock等)、同步器(semaphores等)、阻塞队列(BlockingQueue等)、Executor(线程池)、并发容器(ConcurrentHashMap等)、还有Fork/Join框架;
内层有AQS(AbstractQueuedSynchronizer类,锁功能都由他实现)、非阻塞数据结构、原子变量类(AtomicInteger等无锁线程安全类)三种。
讲一下jvm调优。
a,堆大小设置
b,回收器选择
c,辅助信息
JVM提供了大量命令行参数,打印信息,供调试使用;
讲一下jvm的组成。
JVM 由类加载器子系统、运行时数据区、执行引擎以及本地方法接口组成
讲一下ThreadLocal类。
ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量;
ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。
但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大;
怎么确保session共享?
在分布式项目中实现session共享必须做以下准备工作:
1) Cookie中共享ticket
2) Redis存储session
分布式系统共享用户身份信息session,必须先获取ticket票据,然后再根据票据信息获取redis中用户身份信息。
实现以上2点即可实现session共享。
目前项目中使用的springsecurity + cas 来实现的单点登录,cas自动产生ticket票据信息,每次获取用户信息,cas将会携带ticket信息获取用户身份信息。
项目中哪块涉及了线程问题,怎么处理的?
项目的高并发访问就是一个多线程问题。
项目中普通的业务开发基本没有涉及多线程问题,不过你可以谈谈你使用的框架中使用的多线程技术:
因为我们项目使用的框架进行开发的,因此多线程处理多让框架非我们处理结束了。
1) 高并发就是多线程,这里的多线程让servlet服务器给处理了谈谈Tomcat多线程配置;
a) 配置线程池,扩大并发能力
b) 开启NIO能力等等
2) 框架多线程:mybatis 框架底层使用的连接池