面试:第四章:项目介绍

哪些情况用到activeMq?

商品上架后更新ES索引库、更新静态页、发送短信

提交订单后清除购物车中的数据

支付未完成时支付完成后修改订单状态
秒杀的时候,只有最后一件物品,该怎么去抢或者分配?

秒杀商品的库存都会放到redis缓存中,在客户下单时就减库存,我们设置库存库存闸值,用于某些商品数量非单件不可分割,减完库存会判断库存是否为大于库存闸值,如果小于,表示库存不足,刚才减去的数量再恢复,整个过程使用redis的watch锁 。
你项目对于订单是怎么处理的,假如一个客户在下订单的时候没有购买怎么办?

订单表中设置了一个过期时间,每天会有定时任务来扫描订单表数据,如果到达预订的过期时间没有付款就会取消此订单交易。
对于顾客在购买商品的时候你们怎么处理你们的库存?

普通商品只有在发货时才去更新库存,如果库存不足商家会马上补货

秒杀的商品会在客户下单时就减库存,如果在规定时间(半个小时)没有付款,会取消此订单把库存还原。
秒杀系统中如何防止超售?如何避免脚本进行恶意刷单?

防止超售解决方案:将存库从MySQL前移到Redis中,所有的写操作放到内存中,由于Redis中不存在锁故不会出现互相等待,并且由于Redis的写性能和读性能都远高于MySQL,这就解决了高并发下的性能问题。然后通过队列等异步手段,将变化的数据异步写入到DB中。当达到库存阀值的时候就不在消费队列,并关闭购买功能。避免脚本恶意刷单:采用IP级别的限流,即针对某一个IP,限制单位时间内发起请求数量。
单点登录你们是自己编写的还是使用通用的CAS?

项目使用通用的CAS框架。
什么是CAS?

中央认证服务,企业级单点登录解决方案。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。
如果一个用户的token被其他用户劫持了,怎样解决这个安全问题。

a、在存储的时候把token进行对称加密存储,用时解开。
b、将请求URL、时间戳、token三者进行合并加盐签名,服务端校验有效性。

c.HTTPS对URL进行加密
对系统运行造成很大压力,随着项目上线时间增长,压力会越来越大,我们怎么减轻系统访问压力

流量分为三种,一种是商家流量,另一种是用户流量,第三种运营商流量。

解决方案:

      这三种流量对系统运行造成很大压力,随着项目上线时间增长,压力会越来越大,因此我们要减轻系统访问压力 ,就需要做一系列优化措施。

 具体优化如下:
 数据层面的优化:

从数据库层面做优化,比如:索引,缓存集群双缓存,把查询独立出来读写分离,配置数据库集群主从复制,使用Mycat分表,分库。

从数据库设计层面的优化:比如减少表关联,加入冗余字段

从缓存方面优化:比如redis实现数据缓存,减轻数据库压力

从搜索上进行优化:比如查找索引库,使用es或solr全文搜索
项目层面的优化:

采用面向服务的分布式架构:分担服务器压力 ,提高项目并发量。比如dubbox+zookeeper的SOA分布式架构

采用分布式文件系统实现海量文件存储:如采用fastdfs实现海量图片存储,提高文件的访问速度。

采用mq使用服务进一步解藕:同步索引库,同步静态资源,短信发送
服务器层面的优化

集群思想的使用:tomcat,zookeeper,redis,mysql等

Tomcat异步通信的使用,tomcat连接池配置
秒杀和团购业务实现思路

将商品数量查询出存入到redis中,所有用户下单后,减掉redis中的数量

如果并发量很大时,还要考虑高并发问题,所以可以加入mq消息中间件处理抢单问题,再结合redis实现库存减少操作。高并发方面还可以考虑CDN,Nginx负载均衡等
你们项目中使用的安全框架是什么?

第一种:使用springSecurity

 

第二种:使用shiro(配置一下再用一下标签就行了),校验用户登录和用户权限!
项目中使用到的应用服务器是什么?

Tomcat+nginx
项目中遇到什么问题?
ES高亮不能显示的问题

前台使用angularJS加载搜索结果,但是发现高亮不能展示。

问题原因:angularJS底层使用ajax,异步加载高亮信息返回给页面后,页面没有刷新,就直接显示返回的数据。此时会把所有的数据作为普通的文本数据进行加载。因此就没有高亮的效果。

解决方案:使用angularJS过滤器过滤文本数据,此时angularJS过滤器把html文本数据解析为浏览器能识别的html标签。高亮就能展示了。
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 框架可以解决等等。
订单中的事物是如何保证一致性的。

使用分布式事务来进行控制,保证数据最终结果的一致性。
讲讲angularJS四大特征?
MVC 模式

Model:数据,其实就是angular变量($scope.XX);

View: 数据的呈现,Html+Directive(指令);

Controller:操作数据,就是function,数据的增删改查;
双向绑定

首先我们要理解数据绑定。我们看到的网站页面中,是由数据和设计两部分组合而成。将设计转换成浏览器能理解的语言,便是html和css主要做的工作。而将数据显示在页面上,并且有一定的交互效果(比如点击等用户操作及对应的页面反应)则是js主要完成的工作。很多时候我们不可能每次更新数据便刷新页面(get请求),而是通过向后端请求相关数据,并通过无刷新加载的方式进行更新页面(post请求)。那么数据进行更新后,页面上相应的位置也能自动做出对应的修改,便是数据绑定。

在以前的开发模式中,这一步一般通过jq操作DOM结构,从而进行更新页面。但这样带来的是大量的代码和大量的操作。如果能在开始的时候,便已经确定好从后端获取的数据到页面上需要进行的操作,当数据发生改变,页面的相关内容也自动发生变化,这样便能极大地方便前端工程师的开发。在新的框架中(angualr,react,vue等),通过对数据的监视,发现变化便根据已经写好的规则进行修改页面,便实现了数据绑定。可以看出,数据绑定是M(model,数据)通过VM(model-view,数据与页面之间的变换规则)向V(view)的一个修改。

而双向绑定则是增加了一条反向的路。在用户操作页面(比如在Input中输入值)的时候,数据能及时发生变化,并且根据数据的变化,页面的另一处也做出对应的修改。有一个常见的例子就是淘宝中的购物车,在商品数量发生变化的时候,商品价格也能及时变化。这样便实现了V——M——VM——V的一个双向绑定。

这里是区别于Jquery的,jq操作的是dom对象,angularJS操作的是变量
依赖注入

对象在创建时,其依赖的对象由框架来自动创建并注入进来。控制器就是通过依赖注入的方式实现对服务的调用。
模块化设计

高内聚低耦合法则

高内聚:每个模块的具体功能具体实现

低耦合:模块之间尽可能的少用关联和依赖

1)官方提供的模块  ng(最核心)、ngRoute(路由)、ngAnimate(动画)

2)用户自定义的模块     angular.module('模块名',[ ])
 
当商品库存数量不足时,如何保证不会超卖。

场景一: 如果系统并发要求不是很高

那么此时库存就可以存储在数据库中,数据库中加锁串行化减库存,控制库存的超卖现象。

场景二:系统的并发量很大

如果系统并发量很大,那么就不能再使用数据库来进行减库存操作了,因为数据库加锁操作本身是以损失数据库的性能来进行控制数据库数据的一致性的。但是当并发量很大的时候,将会导致数据库排队,发生阻塞。

因此必须使用一个高效的nosql数据库服务器来进行减库存,此时可以使用redis服务器来存储库存,redis是一个内存版的数据库,查询效率相当的高,可以使用watch来监控减库存的操作,一旦发现库存被减为0,立马停止售卖操作。
商城系统中有以下活动:

1)      秒杀活动

a)    后台设置秒杀商品

b)    设置秒杀开启时间,定时任务,开启秒杀

c)    秒杀减库存(秒杀时间结束,库存卖完,活动结束)

2)      促销活动

3)      团购活动

4)      今日推荐
涉及到积分积累和兑换商品等业务是怎么设计的

积分累计有2大块:

积分累计:

根据用户购买的商品的价格不同,购买一定价格的商品,获取一定的积分。

积分商城:

积分商城是用户可以使用积分商品换取商品的区域。
项目的亮点是:

1) 项目采用面向服务分布式架构(使用dubbo,zookeeper)

a)    解耦

b)    提高项目并发能力

c)    分担服务器压力

2) 项目中使用activeMQ对项目进一步解耦

a)    提高项目并发能力

b)    提高任务处理速度

3)  使用支付宝支付

4)  使用前后端分离

5)  使用第三方分布式文件系统存储海量文件

6)  Nginx部署静态页面实现动静分离
购物车流程:

 

秒杀商品流程:

(1)商家提交秒杀商品申请,录入秒杀商品数据,主要包括:商品标题、原价、秒杀价、商品图片、介绍等信息

(2)运营商审核秒杀申请

(3)秒杀频道首页列出秒杀商品(进行中的)点击秒杀商品图片跳转到秒杀商品详细页。将秒杀的商品放入缓存减少数据库瞬间的访问压力!

(4)商品详细页显示秒杀商品信息,点击立即抢购实现秒杀下单,下单时扣减库存。当库存为0或不在活动期范围内时无法秒杀。读取商品详细信息时运用缓存,当用户点击抢购时减少redis中的库存数量,当库存数为0时或活动期结束时,同步到数据库。

(5)秒杀下单成功,直接跳转到支付页面(微信扫码),支付成功,跳转到成功页,填写收货地址、电话、收件人等信息,完成订单。

(6)当用户秒杀下单5分钟内未支付,取消预订单,调用微信支付的关闭订单接口,恢复库存。产生的秒杀预订单也不会立刻写到数据库中,而是先写到缓存,当用户付款成功后再写入数据库。
单点登录怎么做的,知道原理吗?

在分布式项目中实现session共享,完成分布式系统单点登录

Cookie中共享ticket

 Redis存储session

分布式系统共享用户身份信息session,必须先获取ticket票据,然后再根据票据信息获取redis中用户身份信息。

实现以上2点即可实现session共享。目前项目中使用的Shiro+ cas 来实现的单点登录,cas自动产生ticket票据信息,每次获取用户信息,cas将会携带ticket信息获取用户身份信息。

 
介绍一下自己的项目?

我最近的一个项目是一个电商项目,我主要负责的是后台管理和商品详情的模块,然后也会参与到购物车和订单模块。这个项目是以SpringBoot和mybatis为框架,应为springBoot相对于SSM来说 配置方面,还有操作方面简单很多。然后是采用zookeeper加dubbo分布式架构和RPC远程调用,因为他Dubbo实现了软负载均衡,其特点是成本低,但也会有缺点,就是负载能力会受服务器本身影响,然后为了解决软负载均衡的缺点,我们使用了Nginx进行负载均衡的轮询算法,但Nginx主要在我们项目还是实现反向代理,就是可以防止外网对内网服务器的恶性攻击、缓存以减少服务器的压力和访问安全控制。基础模块就有后台管理,商品详情,订单,支付,物流情况,库存服务。然后SpringBoot整合Thymeleaf模块技术开发项目商品详情模块,easyUI开发后台管理项目。至于我负责的两个模块呢,就是后台管理和商品详情,其中呢使用了sku和spu的数据表结构进行增删改查,spu就好比我们要买一台Mate20,但是我们没有选择它是什么配置,那么关于详细的配置就是sku了,就是我要买一台Mate20,黑色,内存是128G的。商品详情和商品列表模块使用Nginx实现集群,使用Redis解决应用服务器的cpu和内存压力,减少io的读操作,减轻io的压力,使用分布式锁防止Redis缓存击穿。其中Redis的作用我是觉得挺大的,因为他可以防止过多的用户去直接访问我们的数据库,当然,Redis也会在高并发的时候宕机,在使用Redis做缓存的时候,我们使用Redis持久化功能,防止Redis宕机后数据丢失,如果Redis宕机了,用户就会大量的去访问数据库,从而我们数据库也会崩溃吧。这个时候我们就用了一个分布式锁,用户需要获得一个锁才能访问我们的数据库,当然啦,并不只是只有一个锁,而是锁的数量是有限的,当一位用户查完了数据之后,锁就会释放,给下位用户,这也就是服务降降级。没有获得锁的用户,页面就一直刷新直到自己拿到锁为止。redis提供了持久化功能——RDB和AOF。通俗的讲就是将内存中的数据写入硬盘中。在实际应用中,用户如果要查询商品的话呢,首先回到Redis缓存里面找的,如果找不到,就会到数据库里面找,然后缓存到Redis中,那么下一次或者下一个用户需要查找这个数据就不必到数据库中查找了!然后我还参与了购物车和订单模块的开发。购物车模块里面呢,我先和您讲下他的业务逻辑吧。就像你逛网页淘宝一样,在没有登录的时候,把东西放入购物车,它是不会和你的账号里的商品合并的,这个时候,商品就会以cookie的形式,放到你的浏览器里面。这个时候如果你想购买这些商品的时候,你就要登录,这个时候就会使用到单点登录这一个技术。用户跳转到订单页面的时候,我们会用拦截器去进行判断用户是否已经登录。我们是用cookie中是否有token,如果没有token的话就跳转到登录页面,然后生成token,至于token的生成呢,我们是用本地的IP,用户的id,保存在map中,还有一个常量,这个我们通常会以项目名称来命名的。至于为什么要token呢,其实是因为cookie是不太安全的,它很容易被伪造,所以我们就需要token,然后有了token之后,我们用JWT这个盐值生成最后的token。并把它保存到cookie当中。下一次支付的时候我们也还会用到这个token,用一个加密算法再去运算验证一下就可以了!然后就是合并购物车了。这个的话我所知道的就是将客户端的cookie复印一份到缓存中进行修改然后送回客户端进行覆盖,再接着就是数据库的修改了。那这个如果登陆了的就直接从数据库中取得数据跳到订单系统了。然后订单模块里面,简单来说就是从购物车中勾选的商品迁移到订单里面。但是呢订单模块其实是会联系到另外两个模块的,就是库存和支付。如果你点击了提交订单,商品就会在购物车里移除。然后我们提交订单避免他反复的提交同一个订单,就会通过交易码防止订单重复提交。我们会吧tradecode放在缓存里面,以用户id为key商品的交易为value在Redis里面保存这个交易码。到最后选好收货地址,留言之后,提交订单了,就会用自己的tradecode和在Redis里面通过用户的id去获取tradecode进行对比,如果能跳转到支付页面,那么缓存中的交易码就会删除掉。到最后就是支付功能,这一步的话我是不太清楚其中的技术点了,只知道这个模块调用了支付宝的接口和用了消息队列,异步通知。