面試:第四章:項目介紹

哪些情況用到activeMq?

商品上架后更新ES索引庫、更新靜態(tài)頁、發(fā)送短信

提交訂單后清除購物車中的數據

支付未完成時支付完成后修改訂單狀態(tài)
秒殺的時候,只有最后一件物品,該怎么去搶或者分配?

秒殺商品的庫存都會放到redis緩存中,在客戶下單時就減庫存,我們設置庫存庫存閘值,用于某些商品數量非單件不可分割,減完庫存會判斷庫存是否為大于庫存閘值,如果小于,表示庫存不足,剛才減去的數量再恢復,整個過程使用redis的watch鎖 。
你項目對于訂單是怎么處理的,假如一個客戶在下訂單的時候沒有購買怎么辦?

訂單表中設置了一個過期時間,每天會有定時任務來掃描訂單表數據,如果到達預訂的過期時間沒有付款就會取消此訂單交易。
對于顧客在購買商品的時候你們怎么處理你們的庫存?

普通商品只有在發(fā)貨時才去更新庫存,如果庫存不足商家會馬上補貨

秒殺的商品會在客戶下單時就減庫存,如果在規(guī)定時間(半個小時)沒有付款,會取消此訂單把庫存還原。
秒殺系統(tǒng)中如何防止超售?如何避免腳本進行惡意刷單?

防止超售解決方案:將存庫從MySQL前移到Redis中,所有的寫操作放到內存中,由于Redis中不存在鎖故不會出現互相等待,并且由于Redis的寫性能和讀性能都遠高于MySQL,這就解決了高并發(fā)下的性能問題。然后通過隊列等異步手段,將變化的數據異步寫入到DB中。當達到庫存閥值的時候就不在消費隊列,并關閉購買功能。避免腳本惡意刷單:采用IP級別的限流,即針對某一個IP,限制單位時間內發(fā)起請求數量。
單點登錄你們是自己編寫的還是使用通用的CAS?

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

中央認證服務,企業(yè)級單點登錄解決方案。CAS Server 需要獨立部署,主要負責對用戶的認證工作;CAS Client 負責處理對客戶端受保護資源的訪問請求,需要登錄時,重定向到 CAS Server。
如果一個用戶的token被其他用戶劫持了,怎樣解決這個安全問題。

a、在存儲的時候把token進行對稱加密存儲,用時解開。
b、將請求URL、時間戳、token三者進行合并加鹽簽名,服務端校驗有效性。

c.HTTPS對URL進行加密
對系統(tǒng)運行造成很大壓力,隨著項目上線時間增長,壓力會越來越大,我們怎么減輕系統(tǒng)訪問壓力

流量分為三種,一種是商家流量,另一種是用戶流量,第三種運營商流量。

解決方案:

      這三種流量對系統(tǒng)運行造成很大壓力,隨著項目上線時間增長,壓力會越來越大,因此我們要減輕系統(tǒng)訪問壓力 ,就需要做一系列優(yōu)化措施。

 具體優(yōu)化如下:
 數據層面的優(yōu)化:

從數據庫層面做優(yōu)化,比如:索引,緩存集群雙緩存,把查詢獨立出來讀寫分離,配置數據庫集群主從復制,使用Mycat分表,分庫。

從數據庫設計層面的優(yōu)化:比如減少表關聯,加入冗余字段

從緩存方面優(yōu)化:比如redis實現數據緩存,減輕數據庫壓力

從搜索上進行優(yōu)化:比如查找索引庫,使用es或solr全文搜索
項目層面的優(yōu)化:

采用面向服務的分布式架構:分擔服務器壓力 ,提高項目并發(fā)量。比如dubbox+zookeeper的SOA分布式架構

采用分布式文件系統(tǒng)實現海量文件存儲:如采用fastdfs實現海量圖片存儲,提高文件的訪問速度。

采用mq使用服務進一步解藕:同步索引庫,同步靜態(tài)資源,短信發(fā)送
服務器層面的優(yōu)化

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

Tomcat異步通信的使用,tomcat連接池配置
秒殺和團購業(yè)務實現思路

將商品數量查詢出存入到redis中,所有用戶下單后,減掉redis中的數量

如果并發(fā)量很大時,還要考慮高并發(fā)問題,所以可以加入mq消息中間件處理搶單問題,再結合redis實現庫存減少操作。高并發(fā)方面還可以考慮CDN,Nginx負載均衡等
你們項目中使用的安全框架是什么?

第一種:使用springSecurity

 

第二種:使用shiro(配置一下再用一下標簽就行了),校驗用戶登錄和用戶權限!
項目中使用到的應用服務器是什么?

Tomcat+nginx
項目中遇到什么問題?
ES高亮不能顯示的問題

前臺使用angularJS加載搜索結果,但是發(fā)現高亮不能展示。

問題原因: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發(fā)現,一般的cpu時間都耗在了這兩個方法上。

3)start()后才能收消息:Connection的start()方法調用后,才能收到jms消息。如果不調用這個方法,能發(fā)出消息,但是一直收不到消息。不知道其它的jms服務器也是這樣。

4)顯式關閉Session:如果忘記了最后關閉Connection或Session對象,都會導致內存泄漏。這個在我測試的時候也發(fā)現了。本來以為關閉了Connection,由這個Connection生成的Session也會被自動關閉,結果并非如此,Session并沒有關閉,導致內存泄漏。所以一定要顯式的關閉Connection和Session。

5)對Session做對象池:對Session做對象池,而不是Connection。Session也是昂貴的對象,每次使用都新建和關閉,代價也非常高。而且后來我們發(fā)現,原來Connection是線程安全的,而Session不是,所以后來改成了對Session做對象池,而只保留一個Connection。

6) 集群:ActiveMQ有強大而靈活的集群功能,但是使用起來還是會有很多陷阱
activeMQ存在發(fā)出消息太大,造成消息接受不成功。多個線程從activeMQ中取消息,隨著業(yè)務的擴大,該機器占用的網絡帶寬越來越高。

仔細分析發(fā)現,mq入隊時并沒有異常高的網絡流量,僅僅在出隊時會產生很高的網絡流量。

最終發(fā)現是spring的jmsTemplate與activemq的prefetch機制配合導致的問題。

研究源碼發(fā)現jmsTemplate實現機制是:每次調用receive()時都會創(chuàng)建一個新的consumer對象,用完即銷毀。

正常情況下僅僅會浪費重復創(chuàng)建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()也不會有任何問題。但是會有資源浪費,因為要反復創(chuàng)建consumer并頻繁與服務器進 行數據通信,但在性能要求不高的應用中也不算什么問題。

2、不使用jmsTemplate,手工創(chuàng)建一個consumer,并單線程反復使用它來receive(),此時可以充分利用prefetch機制。配合多線程的方式每個線程擁有自己的一個consumer,此時能夠充分發(fā)揮MQ在大吞吐量時的速度優(yōu)勢。

切記避免多線程使用一個consumer造成的消息混亂。大吞吐量的應用推薦使用方案2,能夠充分利用prefetch機制提高系MQ的吞吐性能。
商品的價格變化后,如何同步redis中數以百萬計的購物車數據。

解決方案:購物車只存儲商品id,到購物車結算頁面將會從新查詢購物車數據,因此就不會涉及購物車商品價格同步的問題。
系統(tǒng)中的錢是如何保證安全的。

在當前互聯網系統(tǒng)中錢的安全是頭等大事,如何保證錢的安全可以從以下2個方面來思考:

1)錢計算方面

在系統(tǒng)中必須是浮點數計算類型存儲錢的額度,否則計算機在計算時可能會損失精度。

2)事務處理方面

在當前環(huán)境下,高并發(fā)訪問,多線程,多核心處理下,很容易出現數據一致性問題,此時必須使用事務進行控制,訪問交易出現安全性的問題,那么在分布式系統(tǒng)中,存在分布式事務問題,可以有很多解決方案:

使用 jpa可以解決

使用 tcc 框架可以解決等等。
訂單中的事物是如何保證一致性的。

使用分布式事務來進行控制,保證數據最終結果的一致性。
講講angularJS四大特征?
MVC 模式

Model:數據,其實就是angular變量($scope.XX);

View: 數據的呈現,Html+Directive(指令);

Controller:操作數據,就是function,數據的增刪改查;
雙向綁定

首先我們要理解數據綁定。我們看到的網站頁面中,是由數據和設計兩部分組合而成。將設計轉換成瀏覽器能理解的語言,便是html和css主要做的工作。而將數據顯示在頁面上,并且有一定的交互效果(比如點擊等用戶操作及對應的頁面反應)則是js主要完成的工作。很多時候我們不可能每次更新數據便刷新頁面(get請求),而是通過向后端請求相關數據,并通過無刷新加載的方式進行更新頁面(post請求)。那么數據進行更新后,頁面上相應的位置也能自動做出對應的修改,便是數據綁定。

在以前的開發(fā)模式中,這一步一般通過jq操作DOM結構,從而進行更新頁面。但這樣帶來的是大量的代碼和大量的操作。如果能在開始的時候,便已經確定好從后端獲取的數據到頁面上需要進行的操作,當數據發(fā)生改變,頁面的相關內容也自動發(fā)生變化,這樣便能極大地方便前端工程師的開發(fā)。在新的框架中(angualr,react,vue等),通過對數據的監(jiān)視,發(fā)現變化便根據已經寫好的規(guī)則進行修改頁面,便實現了數據綁定??梢钥闯觯瑪祿壎ㄊ荕(model,數據)通過VM(model-view,數據與頁面之間的變換規(guī)則)向V(view)的一個修改。

而雙向綁定則是增加了一條反向的路。在用戶操作頁面(比如在Input中輸入值)的時候,數據能及時發(fā)生變化,并且根據數據的變化,頁面的另一處也做出對應的修改。有一個常見的例子就是淘寶中的購物車,在商品數量發(fā)生變化的時候,商品價格也能及時變化。這樣便實現了V——M——VM——V的一個雙向綁定。

這里是區(qū)別于Jquery的,jq操作的是dom對象,angularJS操作的是變量
依賴注入

對象在創(chuàng)建時,其依賴的對象由框架來自動創(chuàng)建并注入進來??刂破骶褪峭ㄟ^依賴注入的方式實現對服務的調用。
模塊化設計

高內聚低耦合法則

高內聚:每個模塊的具體功能具體實現

低耦合:模塊之間盡可能的少用關聯和依賴

1)官方提供的模塊  ng(最核心)、ngRoute(路由)、ngAnimate(動畫)

2)用戶自定義的模塊     angular.module('模塊名',[ ])
 
當商品庫存數量不足時,如何保證不會超賣。

場景一: 如果系統(tǒng)并發(fā)要求不是很高

那么此時庫存就可以存儲在數據庫中,數據庫中加鎖串行化減庫存,控制庫存的超賣現象。

場景二:系統(tǒng)的并發(fā)量很大

如果系統(tǒng)并發(fā)量很大,那么就不能再使用數據庫來進行減庫存操作了,因為數據庫加鎖操作本身是以損失數據庫的性能來進行控制數據庫數據的一致性的。但是當并發(fā)量很大的時候,將會導致數據庫排隊,發(fā)生阻塞。

因此必須使用一個高效的nosql數據庫服務器來進行減庫存,此時可以使用redis服務器來存儲庫存,redis是一個內存版的數據庫,查詢效率相當的高,可以使用watch來監(jiān)控減庫存的操作,一旦發(fā)現庫存被減為0,立馬停止售賣操作。
商城系統(tǒng)中有以下活動:

1)      秒殺活動

a)    后臺設置秒殺商品

b)    設置秒殺開啟時間,定時任務,開啟秒殺

c)    秒殺減庫存(秒殺時間結束,庫存賣完,活動結束)

2)      促銷活動

3)      團購活動

4)      今日推薦
涉及到積分積累和兌換商品等業(yè)務是怎么設計的

積分累計有2大塊:

積分累計:

根據用戶購買的商品的價格不同,購買一定價格的商品,獲取一定的積分。

積分商城:

積分商城是用戶可以使用積分商品換取商品的區(qū)域。
項目的亮點是:

1) 項目采用面向服務分布式架構(使用dubbo,zookeeper)

a)    解耦

b)    提高項目并發(fā)能力

c)    分擔服務器壓力

2) 項目中使用activeMQ對項目進一步解耦

a)    提高項目并發(fā)能力

b)    提高任務處理速度

3)  使用支付寶支付

4)  使用前后端分離

5)  使用第三方分布式文件系統(tǒng)存儲海量文件

6)  Nginx部署靜態(tài)頁面實現動靜分離
購物車流程:

 

秒殺商品流程:

(1)商家提交秒殺商品申請,錄入秒殺商品數據,主要包括:商品標題、原價、秒殺價、商品圖片、介紹等信息

(2)運營商審核秒殺申請

(3)秒殺頻道首頁列出秒殺商品(進行中的)點擊秒殺商品圖片跳轉到秒殺商品詳細頁。將秒殺的商品放入緩存減少數據庫瞬間的訪問壓力!

(4)商品詳細頁顯示秒殺商品信息,點擊立即搶購實現秒殺下單,下單時扣減庫存。當庫存為0或不在活動期范圍內時無法秒殺。讀取商品詳細信息時運用緩存,當用戶點擊搶購時減少redis中的庫存數量,當庫存數為0時或活動期結束時,同步到數據庫。

(5)秒殺下單成功,直接跳轉到支付頁面(微信掃碼),支付成功,跳轉到成功頁,填寫收貨地址、電話、收件人等信息,完成訂單。

(6)當用戶秒殺下單5分鐘內未支付,取消預訂單,調用微信支付的關閉訂單接口,恢復庫存。產生的秒殺預訂單也不會立刻寫到數據庫中,而是先寫到緩存,當用戶付款成功后再寫入數據庫。
單點登錄怎么做的,知道原理嗎?

在分布式項目中實現session共享,完成分布式系統(tǒng)單點登錄

Cookie中共享ticket

 Redis存儲session

分布式系統(tǒng)共享用戶身份信息session,必須先獲取ticket票據,然后再根據票據信息獲取redis中用戶身份信息。

實現以上2點即可實現session共享。目前項目中使用的Shiro+ cas 來實現的單點登錄,cas自動產生ticket票據信息,每次獲取用戶信息,cas將會攜帶ticket信息獲取用戶身份信息。

 
介紹一下自己的項目?

我最近的一個項目是一個電商項目,我主要負責的是后臺管理和商品詳情的模塊,然后也會參與到購物車和訂單模塊。這個項目是以SpringBoot和mybatis為框架,應為springBoot相對于SSM來說 配置方面,還有操作方面簡單很多。然后是采用zookeeper加dubbo分布式架構和RPC遠程調用,因為他Dubbo實現了軟負載均衡,其特點是成本低,但也會有缺點,就是負載能力會受服務器本身影響,然后為了解決軟負載均衡的缺點,我們使用了Nginx進行負載均衡的輪詢算法,但Nginx主要在我們項目還是實現反向代理,就是可以防止外網對內網服務器的惡性攻擊、緩存以減少服務器的壓力和訪問安全控制?;A模塊就有后臺管理,商品詳情,訂單,支付,物流情況,庫存服務。然后SpringBoot整合Thymeleaf模塊技術開發(fā)項目商品詳情模塊,easyUI開發(fā)后臺管理項目。至于我負責的兩個模塊呢,就是后臺管理和商品詳情,其中呢使用了sku和spu的數據表結構進行增刪改查,spu就好比我們要買一臺Mate20,但是我們沒有選擇它是什么配置,那么關于詳細的配置就是sku了,就是我要買一臺Mate20,黑色,內存是128G的。商品詳情和商品列表模塊使用Nginx實現集群,使用Redis解決應用服務器的cpu和內存壓力,減少io的讀操作,減輕io的壓力,使用分布式鎖防止Redis緩存擊穿。其中Redis的作用我是覺得挺大的,因為他可以防止過多的用戶去直接訪問我們的數據庫,當然,Redis也會在高并發(fā)的時候宕機,在使用Redis做緩存的時候,我們使用Redis持久化功能,防止Redis宕機后數據丟失,如果Redis宕機了,用戶就會大量的去訪問數據庫,從而我們數據庫也會崩潰吧。這個時候我們就用了一個分布式鎖,用戶需要獲得一個鎖才能訪問我們的數據庫,當然啦,并不只是只有一個鎖,而是鎖的數量是有限的,當一位用戶查完了數據之后,鎖就會釋放,給下位用戶,這也就是服務降降級。沒有獲得鎖的用戶,頁面就一直刷新直到自己拿到鎖為止。redis提供了持久化功能——RDB和AOF。通俗的講就是將內存中的數據寫入硬盤中。在實際應用中,用戶如果要查詢商品的話呢,首先回到Redis緩存里面找的,如果找不到,就會到數據庫里面找,然后緩存到Redis中,那么下一次或者下一個用戶需要查找這個數據就不必到數據庫中查找了!然后我還參與了購物車和訂單模塊的開發(fā)。購物車模塊里面呢,我先和您講下他的業(yè)務邏輯吧。就像你逛網頁淘寶一樣,在沒有登錄的時候,把東西放入購物車,它是不會和你的賬號里的商品合并的,這個時候,商品就會以cookie的形式,放到你的瀏覽器里面。這個時候如果你想購買這些商品的時候,你就要登錄,這個時候就會使用到單點登錄這一個技術。用戶跳轉到訂單頁面的時候,我們會用攔截器去進行判斷用戶是否已經登錄。我們是用cookie中是否有token,如果沒有token的話就跳轉到登錄頁面,然后生成token,至于token的生成呢,我們是用本地的IP,用戶的id,保存在map中,還有一個常量,這個我們通常會以項目名稱來命名的。至于為什么要token呢,其實是因為cookie是不太安全的,它很容易被偽造,所以我們就需要token,然后有了token之后,我們用JWT這個鹽值生成最后的token。并把它保存到cookie當中。下一次支付的時候我們也還會用到這個token,用一個加密算法再去運算驗證一下就可以了!然后就是合并購物車了。這個的話我所知道的就是將客戶端的cookie復印一份到緩存中進行修改然后送回客戶端進行覆蓋,再接著就是數據庫的修改了。那這個如果登陸了的就直接從數據庫中取得數據跳到訂單系統(tǒng)了。然后訂單模塊里面,簡單來說就是從購物車中勾選的商品遷移到訂單里面。但是呢訂單模塊其實是會聯系到另外兩個模塊的,就是庫存和支付。如果你點擊了提交訂單,商品就會在購物車里移除。然后我們提交訂單避免他反復的提交同一個訂單,就會通過交易碼防止訂單重復提交。我們會吧tradecode放在緩存里面,以用戶id為key商品的交易為value在Redis里面保存這個交易碼。到最后選好收貨地址,留言之后,提交訂單了,就會用自己的tradecode和在Redis里面通過用戶的id去獲取tradecode進行對比,如果能跳轉到支付頁面,那么緩存中的交易碼就會刪除掉。到最后就是支付功能,這一步的話我是不太清楚其中的技術點了,只知道這個模塊調用了支付寶的接口和用了消息隊列,異步通知。