轉載自:https://blog.csdn.net/A_BlackMoon/article/details/80094814
請描述一下這個系統?
【回答技巧】
從3個方面來回答這個問題:
|--系統背景及系統概述
|--系統包括的業務模塊及主業務流程
|--責任模塊
【回答示例】
第一個方面:系統背景及系統概述
優購時尚商城是香港上市公司百麗國際公司爲拓寬旗下運動品牌服飾市場而開發的一個專業銷售購物網站戶外運動裝備的網站。
第二個方面:系統包括的業務模塊及主業務流程
改項目分爲前臺和後天2大模塊:
前臺又包含:全部商品分類、運動館、戶外館、鞋靴館、女裝館、男裝館、母嬰館、生活館、會員中心、秒殺、閃團、INNET運動鞋、潮流館。每一個分類都是對該範圍類商品的一些具體分類以及明星商品的展示、新品展示、品牌連接等等。前臺還包含用戶的登錄註冊、我的優購、我的訂單、公告等模塊,主要用於客戶下單。結算、收貨等一系列購物操作。以及個人中心的個人信息、訂單信息的查看和維護。
後臺後臺包含:商品管理、訂單管理、代客下單、支付管理、廣告管理、合作伙伴管理、會員管理、權限管理、系統配置、報表管理等模塊。
第三方面:責任模塊
這個系統我主要負責的是商品管理模塊的CRUD以及商品的屬性管理、商品的上下架、品牌管理、訂單管理
前臺主要負責商品的主頁面展示、商品的篩選、商品詳情展示。在商品詳情頁面採用freemarker頁面靜態化技術。降低服務器壓力減少對服務器的I/O。商品詳情頁實現了添加購物車和結算功能。購物車根據與項目經理協商採用cookie技術實現。【此處可以加入幾種保存方案:1.保存數據庫{問題是造成數據庫壓力倍增} 2.使用cookie{用戶更換電腦或瀏覽器添加購物車商品丟失,但是此網站不考慮} 3.最好解決方案保存到redis數據庫。前兩個問題全部解決】。提交訂單功能可能出現併發問題。原因在於大量用戶都可能購買。庫存出現不足問題。這裏可以插入自己對數據庫的理解:鎖機制【悲觀所:線程等待。效率太低。樂觀鎖:解決了悲觀所的效率問題。可選。但是update語句本身就帶鎖。所以不用加鎖就能解決這個併發修改庫存問題】,前臺我還負責了個人中心的模塊。包括:我的訂單、退換貨訂單、我的收藏、個人資料、收貨地址等功能模塊。
說說系統的架構?
本系統使用maven進行構建,
將系統分成了技術基礎架構模塊、前臺工程模塊、後臺工程模塊、主工程模塊、文件系統工程模塊。
擴展問題:
Spring在系統中如何使用?
Spring對控制層、業務層、持久層的bean進行統一管理。
對控制層的action,通過@controller註解,自動組件掃描方式將action在spring容器註冊。
對業務層的service,在spring配置文件進行配置,好處是方便系統開發與維護。Spring對業務層進行事務控制。
對持久層的mapper,通過spring與mybatis整合包的mapper掃描器自動掃描編寫的mapper。
本系統如何用maven開發?
本系統採用maven進行模塊劃分,將系統分成了核心基礎架構模塊、前臺工程模塊、後臺工程模塊、主工程模塊、文件系統工程模塊
核心基礎架構模塊:
此模塊包括了對spring、mybatis的配置信息
pom.xml中配置jar包的依賴,方便統一管理本項目的jar包依賴。
前臺和後臺所有業務和數據訪問層的編碼實現。方便子模塊重複調用。
前臺工程模塊:
此模塊包括系統前臺所用到所有controller。負責調用核心基礎模塊中的業務方法進行前臺數據展示
後臺工程模塊:
此模塊包括系統後臺所用到所有controller。負責調用核心基礎模塊中的業務方法進行商家的後臺管理
主工程模塊:
此模塊是將各各子模塊進行聚合,最終生成一個war包。
文件系統工程模塊:
次模塊用於保存系統的所有文件。包括商品圖片、報表模板、企業公告等文件。用於分散服務器I/O提高項目訪問效率。
擴展問題:
maven倉庫是怎麼構建?
實際開發在局域網中公司創建了一個maven私服,私服上存放系統所用到的jar包。
本系統實現國際化了嗎?是怎麼做?
本系統沒有實現國際化,本系統是具有中國特色的電商項目,不需要實現國際化
這個系統mybatis是怎麼用的?或這個系統持久層如何實現的?
1、mybatis框架
使用mybatis官方出的mybatis與spring整合包將mybatis和spring整合。
針對單表的增、刪、改、查使用mybatis官方提供的逆向工程,根據數據庫表的結構生成mybatis當中的mapper.xml、mapper.java、po及相關的類。在service中直接使用自動生成的mapper接口。
針對自動生成的mapper無法滿足業務需求時,自定義mapper,一般情況下多表查詢需要自定義mapper。
Mapper開發完成通過mybatis與spring整合包中的mapper掃描器將mapper在spring容器中進行註冊。
擴展問題:(回答問題切莫問什麼答什麼。自己要發散思維。擴展的回答面試問題)
1、 mapper的掃描器是如何使用的
在spring配置文件配置mapper掃描器,配置項指定掃描的包的路徑。
使用掃描器需要遵循一些mapper編寫規則:
Mapper.xml和mapper.java在同一個目錄,且文件名相同。
Mapper.xml中的statement 的id和mapper.java中的方法名一致。
Mapper.xml中的statement的parameterType和mapper.java中的方法輸入參數一致。
Mapper.xml中的statement的resulttype和mapper.java中的方法輸出結果類型一致。
這個系統springmvc是怎麼用的?
使用springmvc註解開發。
1、 對於頁面展示類的方法,控制器方法返回string ,string就是邏輯視圖名。
2、 對於提交類的方法,控制器方法返回json數據,使用@Responsebody註解將action方法的返回值轉爲json輸出。
Responsebody註解內部使用jackson將java對象轉爲json
本系統ajax+json具體是怎麼做的?action的方法返回的json是如何實現的?
ajax+json:頁面採用ajax提交,服務端返回是json,
頁面提交統一採用ajax Form提交方式,使用了jquery提供form組件,在開發時和原始form的post 提交方法配合使用,使用jquery Form組件更能簡化開發提高用戶體驗和開發效率。
擴展問題:
系統哪些地方使用到了json?
1、 圖片上傳返回的相對和絕對路徑。方便圖片回顯和URL保存。
2、 購物車中的最小銷售單元的數據保存
3、 權限列表使用json數據表示。
4、 個人中心的省市縣聯動數據使用json返回
使用json目的:使用json方便客戶端頁面解析數據。
這個系統的用戶認證是怎麼實現的?
使用用戶名密碼認證方式。
1、 用戶認證
2、 用戶身份校驗
用戶身份校驗使用springmvc提供攔截器完成。
流程:
公開權限:用戶不需要登錄就訪問地址。在單獨的配置文件進行配置。比如系統登錄頁面。
電商項目介紹
電商行業的發展
從截圖我們可以發現,市場需求推動了電商的飛速發展,而且我們應該堅信電商會一直火下去,爲了“錢途”我們必須學好這個項目!
電商行業技術特點
①技術新:(NoSql推廣首在社區網站和電商項目),發展快,需求推動技術的革新。
②技術範圍廣:除了java,像淘寶前端還使用了PHP,數據庫MySQL或者oracle,nosql,服務器端使用Linux,服務器安全、系統安全
③分佈式:以前是在一臺機器上做運算,現在是分散到很多機器上,最後彙總起來。(集中式向分佈式進行考慮)由需求來推動
④高併發、集羣、負載均衡、高可用:由併發問題採用集羣進行處理,其中,集羣會涉及服務器的主從以及分佈問題,使用負載均衡。(權重高低)高可用是對用戶而言,用戶的服務不中斷(系統升級,服務不中斷,淘寶每週更新2次)。
⑤海量數據:雙11,570億的背後,訂單有多少?瀏覽次數有多少?商品會有多少?活動相關數據?
⑥業務複雜:不要簡單的認爲是:商品展示出來後,加入購物車後購買就完成了。後臺特別複雜,比如優惠(包郵、滿減)
⑦系統安全:系統上線必須通過系統安全部門審覈通過。前年CSDN數據泄露。快捷酒店數據泄露(通過身份證就可以查看你的開房記錄)。近幾年,安全意識逐步在提高。
電商行業的一些概念
B2C:商家對客戶,京東、噹噹、發展爲B2C平臺,天貓(B2C平臺淘寶商城由馬雲提出,率先發展爲平臺),1號店也是(在上海)
B2B:商家對商家,阿里巴巴(不零售,只批發,淘寶很多商家都會去阿里巴巴進貨);
C2C:個人對個人,淘寶市場,淘寶,QQ商城;
系統功能
本商城系統是一個綜合性的B2C平臺,類似京東商城、天貓商城。
會員可以在商城瀏覽商品、下訂單,以及參加各種活動。
商家可以在入住淘淘商城,在該平臺上開店出售自己的商品,並且得到淘淘商城提供的可靠的服務。
管理員、運營可以在平臺後臺管理系統中管理商品、訂單、會員等。
客服可以在後臺管理系統中處理用戶的詢問以及投訴。
思維導圖
說明:
上圖中,數字標號爲功能模塊開發優先級,鑑於本系統搜索模塊使用的技術點Lucene,由2級別提升爲1級別。期初的電商項目是沒有購物車功能的(不同商家發貨,付款方式也多樣化,這樣過於複雜)
上述功能不要強行記憶,以商品爲中心進行發散理解記憶!如下圖
首先我們要有商品,管理員可以在系統中管理商品,用戶可以查看商品。
商品多了之後要有類目模塊,管理員可以管理類目信息,用戶可以根據類目檢索商品。
有了商品之後,要有人(會員)去買東西,普通用戶註冊爲會員,會員可以登錄到系統管理自己的信息(密碼等)
買了之後會生成訂單,會員可以購買商品並且可以下單,管理員可以管理訂單。
有了訂單之後需要支付(在線支付/貨到付款)
這樣,我們就可以把整個電商項目的功能記清楚了!
分佈式系統架構
分佈式系統架構
傳統架構
各個系統說明:
後臺管理系統:管理商品、訂單、類目、商品規格屬性、用戶管理以及內容發佈等功能。
前臺系統:用戶可以在前臺系統中進行註冊、登錄、瀏覽商品、首頁、下單等操作。
會員系統:用戶可以在該系統中查詢已下的訂單、收藏的商品、我的優惠券、團購等信息。
訂單系統:提供下單、查詢訂單、修改訂單狀態、定時處理訂單。
搜索系統:提供商品的搜索功能。
單點登錄系統:爲多個系統之間提供用戶登錄憑證以及查詢登錄用戶的信息。
談到分佈式架構,我們必須對比傳統架構才能彰顯其優勢。
最爲明顯的一點,在傳統的架構中,如果某個功能需要進行維護,那麼我們必須停掉整個服務,這對於公司的運營會造成損失。分佈式系統在覈心功能模塊使用單獨服務器,維護部分模塊不影響用戶的其他操作。
在海量數據處理方面,傳統架構顯得比較乏力;分佈式系統架構採用服務器集羣,使用負載均衡,海量數據處理遊刃有餘!
在性能(檢索)以及維護方面,分佈式系統架構也有較爲明顯的優勢。
本系統人員配置情況
產品經理:3人,確定需求以及給出產品原型圖。
項目經理:1人,項目管理。
前端團隊:3人,根據產品經理給出的原型製作靜態頁面。
後端團隊:20人,實現產品功能。
測試團隊:3人,測試所有的功能。
運維團隊:2人,項目的發佈以及維護。
開發流程
後臺開發環境
需要注意,在幾個環境中,預發佈環境和生產環境幾乎一樣,開發環境和測試環境是獨立存在的,每一個階段的活是由對應的工作人員來負責的。
此外,我們還需要熟悉 SVN主幹、分支、標籤開發過程流程:
整個開發沿着主線進行,在一個版本定型後,前一個版本出現bug,那麼此時會對bug進行修復,投入使用的依舊是之前定型的那個版本,待前一個版本的bug修復好了之後,會定義一個新的版本,主線不會改變,如果改動不大,那麼只需修訂問題,繼續沿用此版本(1.0.x),只有出現較大改動時,纔會升級一個新的版本號(1.1.x)。所有動作,交替進行,沿主線向前推進!
涉及技術
Spring、SpringMVC、Mybatis
JSP、JSTL、jQuery、jQuery plugin、EasyUI、KindEditor(富文本編輯器)、CSS+DIV
Redis(緩存服務器)
Lucene、Solr(搜索)
httpclient(調用系統服務)
Mysql
Nginx(web服務器)
Quartz(定時任務)
RabbitMQ(消息隊列)
開發工具和環境
Eclipse 4.4.1
Maven 3.2.3
Tomcat 7.0.47(Maven Tomcat Plugin)
JDK 1.7
Mysql 5.6
Nginx 1.5.1
Redis 2.8.9
Win7 操作系統
SVN(版本管理)
需要注意所有開發工具的版本號要對應(一致),否則會出現衝突問題,面試大忌!(常識:電商項目火起來的時間不是很久,在開發的時候,很多開發工具和環境不像之前那些傳統系統那麼老舊,這點一定要引起注意!)
電商面試題
2.1 說說你最近做的這個項目的背景,簡單的介紹一下你這個項目?
背景
電商項目的背景一般是由市場推動的,比如行業競爭或者經營方式的改變(營銷理念)。競爭的形態也發生了巨大的變化,從以產品、價格爲主的競爭轉向以服務爲主的競爭,服務成爲主導競爭格局的重要因素。渠道作爲企業完成客戶溝通、產品/服務交換過程以及實現價值、產生效益的重要載體,發揮了採集、傳達客戶和競爭對手等市場信息,爲買賣雙方提供便利,協調供需矛盾,爲客戶提供合適的產品與服務,向客戶傳遞產品/服務信息,實現營銷/服務目標等重要的功能。
XXX商城之前主要以實體店爲主,進行批發與零售。業務也相對比較傳統,爲了提升業務績效,增強客戶滿意度和粘性,另一方面,也爲基於互聯網的商務模式創新奠定基礎。針對上述行業環境變化和業務戰略目標,xxx商城網上終端預約銷售基礎上,即將啓動網上商城建設項目,用於建立網上終端、營銷案在線銷售及相關輔助功能,包含商品管理、訂單管理、類目管理、客戶管理、合作商管理、客服管理、購物平臺、內容管理等,很大程度上分擔了人工的壓力,對提高客戶服務效率和客戶滿意度能夠起到較好的作用。基於此,XXX公司提出建設網上商城建設項目工程。
項目介紹
xxx商城項目打造的是“社區+電商”的模式,用戶不只是在社區中有自己的圈子,還可以將電商加入到社區中,整個電商網站實現的功能非常之多,採用分佈式的架構設計,包括後臺管理、前臺系統、訂單系統、單點登錄系統、搜索系統、會員系統等。
①該項目是自己公司的產品,我們公司負責整個網站的運營,屬於B2C平臺;
②系統的用途,主要是提供B2C的平臺,其中自營商品也有商家入住,類似天貓。
③系統架構,採用分佈式的系統架構,其中前臺系統和單點登錄系統採用了集羣的方式部署,在後臺管理系統中採用了Maven的多模塊化的管理,其中採用了水平切分的方式,將pojo、dao、service、web分層開發,這樣做的好處就是可以重用性更高。
系統內部接口調用採用Httpclient,並且使用Httpclient的連接池技術,接口提供端採用RESTful方式的接口定義;
系統之間的通知機制採用MQ的方式,使用RabbitMQ的實現,使用了RabbitMQ的消息訂閱模式的消息機制;
系統的接口還對JS的跨域做了支持,採用了jsonp的解決方法,在後臺接口中擴展了spirng提供的jackson數據轉化器實現;
部署方面,採用了Nginx+tomcat的模式,其中nginx的作用一方面是做反向代理、負載均衡、另一方面是做圖片等靜態資源的服務器。
2.2 整個項目的架構如何?
一般情況這個問題要從兩個方面去回答,從技術架構和功能架構去談。
技術架構:本項目使用市場上較爲主流的框架spring+springmvc+mybatis進行開發,採用分佈式的系統架構,前臺系統和單點登錄系統採用了集羣的方式部署,後臺管理系統中採用了Maven的多模塊化的管理,其中採用了水平切分的方式,將pojo、dao、service、web分層開發,這樣做的好處就是可以重用性更高。系統內部接口調用採用Dubbo,在後臺接口中擴展了spirng提供的jackson數據轉化器實現;搜索系統使用了solr實現,在保證系統高性能的前提下,儘可能爲公司節約成本,我們使用MySQL數據庫進行集羣(oracle收費)。在部署方面,採用了Nginx+tomcat的模式,其中nginx的作用一方面是做反向代理、負載均衡、另一方面是做圖片等靜態資源的服務器。
功能架構:分佈式系統架構
各個系統說明:
後臺管理系統:管理商品、訂單、類目、商品規格屬性、用戶管理以及內容發佈等功能。
前臺系統:用戶可以在前臺系統中進行註冊、登錄、瀏覽商品、首頁、下單等操作。
會員系統:用戶可以在該系統中查詢已下的訂單、收藏的商品、我的優惠券、團購等信息。
訂單系統:提供下單、查詢訂單、修改訂單狀態、定時處理訂單。
搜索系統:提供商品的搜索功能。
單點登錄系統:爲多個系統之間提供用戶登錄憑證以及查詢登錄用戶的信息。
談到分佈式架構,我們必須對比傳統架構才能彰顯其優勢。
①最爲明顯的一點,在傳統的架構中,如果某個功能需要進行維護,那麼我們必須停掉整個服務,這對於公司的運營會造成損失。分佈式系統在覈心功能模塊使用單獨服務器,維護部分模塊不影響用戶的其他操作。
②在海量數據處理方面,傳統架構顯得比較乏力;分佈式系統架構採用服務器集羣,使用負載均衡,海量數據處理遊刃有餘!
③在性能(檢索)以及維護方面,分佈式系統架構也有較爲明顯的優勢。
傳統架構:
這個項目爲用戶提供了哪些服務?包括哪些功能?
商品管理模塊:其中包括品牌管理,屬性管理商品錄入/上下架管理,商品添加審覈,靜態頁面發佈
訂單模塊:其中包括使用activiti工作流訂單的查詢和訂單的流轉,定時作廢
商品前臺首頁:其中主要負責首頁商品列表篩選,首頁上動態展示篩選條件,點擊每一個篩選條件下面的商品列表要做聯動
單品頁面:採用freemarker來實現頁面靜態化,展示商品詳情信息和商品購買,該頁面採用靜態化以減輕系統壓力,使用了cxf框架發佈服務
提交訂單頁面:提交用戶的訂單信息, 處理併發問題。
個人中心:包括用戶的登錄,個人信息的管理,收貨地址的管理,用戶所下的訂單的管理
購物車:把購物車的信息存在cookie裏面管理
你承擔這個項目的哪些核心模塊?
在項目中主要負責相關係統的開發,主要有:
後臺管理系統:主要實現商品管理、商品規格參數管理、訂單管理、會員管理等、CMS(內容管理系統)等,並且提供了跨域支持;
前臺系統:主要是面向用戶訪問,使用Httpclient和後臺系統接口做交互,並且該系統在部署上採用集羣的方式;
單點登錄系統:主要是提供集中用戶登錄憑證的集中解決方案,提供和用戶信息相關的接口:比如說用戶註冊、查詢等接口。
訂單系統:主要是提供和訂單相關的業務接口,在訂單系統了做了嚴格的數據校驗以及高併發寫的支持(這裏可以說使用隊列實現),並且使用了Quartz定時任務實現對訂單的定時掃描,比如說關閉超時未付款的訂單;
搜索系統:主要是提供商品的搜索,採用開源企業級系統Solr實現,採用了MQ機制保證了商品數據可以及時同步到solr中;
會員系統:主要是維護用戶的信息,已購買訂單、優惠券、系統消息、修改密碼、綁定手機等功能;
緩存:主要是用Redis實現,並且對Redis做了集羣來保證Redis服務的高可用。
支付系統:,主要是負責訂單的支付、對賬等功能,主要是對接了支付寶的接口;
這些模塊的實現思路說一下?
①商品管理模塊
品牌管理:
主要掌握的核心是品牌的添加
l 圖片服務器的搭建,
l 上傳圖片到圖片服務器
l 表單的驗證,離開焦點的驗證,點擊完成時的驗證,後臺服務器的ajax驗證,表單規範
l 防止表單的二次提交
l 編輯時不需要修改品牌名,區別readOnly和disabled
l 刪除時的二次確認
注意:
自定義屬性必須掌握:html中自定義的屬性可以幫助索引元素
圖片服務器的搭建
1. 創建一個maven的web工程,在工程中創建一個存放資源的目錄
2. 把tomcat的web.xml中DefaultServlet的只讀屬性改成false
3. 編寫上傳到圖片服務器的代碼
由於應用服務器與圖片服務器出於不同的兩臺機器之中,所以可以提高系統的性能,能起到負載均衡的作用。上傳圖片時使用Jersey 客戶端 API 調用 REST 風格的 Web 服務, Jersey 1 是一個開源的、可以用於生產環境的 JAX-RS(RESTful Web Services 的 Java API 規範,JSR-311)實現。通過 Jersey 可以很方便的使用 Java 來創建一個 RESTful Web Services。
byte[] fileByte = commFile.getBytes();
//創建客服端,基於webservice
Client client = Client.create();
//指定資源路徑
WebResource webResource = client.resource(Constants.picPath+fileName);
//使用put的請求方式把資源文件放到資源服務器上
webResource.put(String.class, fileByte);
前臺使用ajax提交表單,需要使用jquery的jquery.form.js插件
$("#form").ajaxSubmit({
url:url,
type:"post",
dataType:"text",
data:{
...
},
//beforeSubmit:validate,
success:function(responseText){
var obj = $.parseJSON(responseText);
},
error:function(){
}
});
4. 圖片服務器中Upload文件夾中隨便建立一個文件,防止空文件夾的情況下發布後upload消失
表單驗證
1. 提交時做驗證
做好約定,每個文本中設置reg屬性和tip自定義的屬性,reg存放正則表達式,tip中存放不合法時的提示信息,還有品牌名稱重複的驗證。
reg2,tip屬於自定義的屬性,這種定義方式方便使用jquery的屬性選擇器
<p>
<label><samp>*</samp>品牌名稱:</label>
<input type="text" id="brandName" name="brandName" class="text state" reg2="^[a-zA-Z0-9\u4e00-\u9fa5]{1,20}$" tip="必須是中英文或數字字符,長度1-20"/>
<span></span>
</p>
2. 在表單提交時做驗證使用$(“form”).submit(function(){ return false });,必填字段和非必填的字段需要區別對待
$("#form111").submit(function(){
var isSubmit = true;
$(this).find("[reg2]").each(function(){
var regStr = $(this).attr("reg2");
//剪掉值中的兩側的字符串
var value = $.trim($(this).val());
var tip = $(this).attr("tip");
//創建正則表達式的對象
var reg = new RegExp(regStr);
if(!reg.test(value)){
$(this).next("span").html(tip);
isSubmit = false;
//跳出循環,在jquery的each語句之中跳出循環使用return false; 如果在原生js裏面可使用break;, return;:代表終止執行程序
return false;
}
});
$(this).find("[reg1]").each(function(){
var regStr = $(this).attr("reg1");
var value = $.trim($(this).val());
var tip = $(this).attr("tip");
var reg = new RegExp(regStr);
if(value != null && value != ""){
if(!reg.test(value)){
$(this).next("span").html(tip);
isSubmit = false;
return false;
}
}
});
return isSubmit;
});
3. 使用離焦事件做友好的提示
$("input[reg2]").blur(function(){
var regStr = $(this).attr("reg2");
var value = $.trim($(this).val());
var tip = $(this).attr("tip");
var reg = new RegExp(regStr);
if(!reg.test(value)){
$(this).next("span").html(tip);
}else{
$(this).next("span").html("");
}
});
4. 表單的二次提交處理
l 鎖屏
l 鎖按鈕
②商品的查詢
商品查詢需要組合條件加分頁查詢
l 組合條件:品牌,審覈狀態,商品名稱,需要動態sql
<select id="queryItemByCondtion" resultMap="BaseResultMap" parameterType="map">
select *
from (select a.*, rownum rm
from (
select *
from eb_item ei
<where>
<if test="brandId != null">
ei.brand_id = #{brandId}
</if>
<if test="auditStatus != null">
and ei.audit_status = #{auditStatus}
</if>
<if test="showStatus != null">
and ei.show_status = #{showStatus}
</if>
<if test="itemName != null">
and ei.item_name like '%${itemName}%'
</if>
</where>
order by ei.item_id desc) a
<![CDATA[
where rownum < #{endNum}) b
where b.rm > #{startNum}
]]>
</select>
--查詢大於當前頁首行號,主要解決oracle的rownum不支持大於號的問題
select *
from (
--查詢小於當前最大行號的數據
select rownum rm,a.*
from (
--第一個select查詢所有的業務數據
select * from eb_item
) a
where rownum < 21) b
where b.rm > 10
l 分頁查詢
1. 查詢結果集的sql,傳入開始行數和結束的行數
select *
from (select a.*, rownum rm
from (
...
內部sql
...
) a where rownum < #{endNum}) b
where b.rm > #{startNum}
2. 使用內部sql查詢結果集的總條數
3. 使用分頁工具類,創建page對象更換每次的數據總條數和當前頁數和每頁的條數,查詢出結果集後把結果集註入到page對象之中
public class Page {
int totalCount = 0;
int pageSize = 10;
int currentPageNo = 1;
int startNum = 0;
int endNum = 11;
public Page(int totalCount, int pageSize, int currentPageNo) {
super();
this.totalCount = totalCount;
this.pageSize = pageSize;
this.currentPageNo = currentPageNo;
}
public int getStartNum(){
return (currentPageNo - 1) * pageSize;
}
public int getEndNum(){
return currentPageNo * pageSize + 1;
}
public int getTotalPage(){
int totalPage = totalCount/pageSize;
if(totalPage == 0 || totalCount%pageSize != 0){
totalPage ++;
}
return totalPage;
}
public int getNextPage(){
if(currentPageNo >= getTotalPage()){
return currentPageNo;
}else{
return currentPageNo + 1;
}
}
public int getPrePage(){
if(currentPageNo <= 1){
return currentPageNo;
}else{
return currentPageNo - 1;
}
}
List<?> list;
public List<?> getList() {
return list;
}
public void setList(List<?> list) {
this.list = list;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getCurrentPageNo() {
return currentPageNo;
}
public void setCurrentPageNo(int currentPageNo) {
this.currentPageNo = currentPageNo;
}
public void setStartNum(int startNum) {
this.startNum = startNum;
}
public void setEndNum(int endNum) {
this.endNum = endNum;
}
}
4. 製作前端樣式
var currentPageNo = parseInt($("#currentPageNo").val());
var totalCount = parseInt($("#totalCount").val());
var totalPage = parseInt($("#totalPage").val());
$("#pagePiece").html(totalCount);
$("#pageTotal").html(currentPageNo+"/"+totalPage);
if(currentPageNo <= 1){
$("#previous").hide();
}else{
$("#previous").show();
}
if(currentPageNo >= totalPage){
$("#next").hide();
}else{
$("#next").show();
}
$("#next").click(function(){
$("#pageNo").val(parseInt(currentPageNo)+1);
$("#form1").submit();
});
$("#previous").click(function(){
$("#pageNo").val(parseInt(currentPageNo)-1);
$("#form1").submit();
});
Oracle 分頁sql的描述:
1.最內層的寫查詢當前表的全量即可
2.對於oracle數據庫分頁需要依賴於rownum,但是rownum不支持大於號,但是支持小於號,可以rownum小於結束行號查詢出來一個結果集(在全量的外層套一個select,它的結果集需要把rownum作爲結果返回)
3.在第二步的結果集基礎上再做一次查詢,查詢條件以第二步查詢出來的rownum的值作爲條件大於開始行號即可
③商品發佈
Console和portal是分開部署在兩臺服務器上,發佈需要在console端去控制,但是生成的靜態化的文件要發佈到portal的工程之中,所以發佈的服務要在portal上,但是要在console中來調用,異構之間的調用要使用webservice。
1. 採用cxf的webservice框架來整合spring
2. 在web.xml中來配置cxf的核心servlet
<servlet>
<servlet-name>cxfServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cxfServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
3. 創建服務的接口和接口的實現類,注意接口上加上@WebService註解
4. 創建cxf的核心配置文件cxf-servlet.xml,配置帶有接口的webservice服務使用<jaxws:server>標籤
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
<!-- 引入CXF Bean定義如下,早期的版本中使用 -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:server id="publish" address="/publish" serviceClass="cn.itcast.ecps.ws.service.EbItemWSService">
<jaxws:serviceBean>
<bean class="cn.itcast.ecps.ws.service.impl.EbItemWSServiceImpl"></bean>
</jaxws:serviceBean>
<!-- 輸入輸出的攔截器 -->
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
</jaxws:outInterceptors>
</jaxws:server>
</beans>
5. 修改cxf-servlet.xml的位置,在spring的listener中加載
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:beans.xml,classpath*:cxf-servlet.xml
</param-value>
</context-param>
6. 啓動服務器發佈webservice的服務,使用wsdl2java生成客戶端的代碼
Wsdl2java –d . –p cn.itcast.ecps.ws.stub http://.........wsdl?
7. 在客戶端調用。
④訂單管理模塊
1) 流程設計
2) 訂單整合activiti工作流
1.activiti流程和spring整合
創建activiti-context.xml文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
<!-- 配置事務管理器,統一事務 -->
<property name="transactionManager" ref="txManager" />
<!-- 設置建表策略 -->
<property name="databaseSchemaUpdate" value="true" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
</beans>
1. 畫流程圖
2. 創建流程服務類
package cn.itcast.service.impl;
import java.io.File;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.DeploymentBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.itcast.service.IWorkFlowService;
//@Service
public class WorkflowServiceImpl implements IWorkFlowService {
@Autowired
RepositoryService repositoryService;
/*public RepositoryService getRepositoryService() {
return repositoryService;
}
public void setRepositoryService(RepositoryService repositoryService) {
this.repositoryService = repositoryService;
}*/
public void deployFlow() {
//創建發佈流程配置對象
DeploymentBuilder builder = repositoryService.createDeployment();
//指定流程資源路徑
builder.addClasspathResource("activit-orderflow.bpmn").addClasspathResource("activit-orderflow.png");
builder.deploy();
}
}
1. 部署流程
2. 查詢業務任務
3. 辦理任務
項目中哪些功能模塊涉及了大數據量訪問?你是如何解決的?
系統前臺是互聯網上的用戶訪問的,會有大量用戶來訪問。
假定有1w個人打開你的網站來訂商品,問你如何解決併發問題(可擴展到任何高併發網站要考慮的併發讀寫問題)
問題,1w個人來訪問,商品沒出去前要保證大家都能看到有商品,不可能一個人在看到商品的時候別人就不能看了。到底誰能搶到,那得看這個人的“運氣”(網絡快慢等)
其次考慮的問題,併發,1w個人同時點擊購買,到底誰能成交?總共只有一張商品。
Update eb_sku t sett.stock = t.stock – 1 where t.sku_id = #{skuId} and t.stock > 0
Update eb_sku t set t.sale = t.sale +1 where t.sku_id = #{skuId}
首先我們容易想到和併發相關的幾個方案 : 鎖同步
同步更多指的是應用程序的層面,多個線程進來,只能一個一個的訪問,java中指的是syncrinized關鍵字。鎖也有2個層面,一個是java中談到的對象鎖,用於線程同步;另外一個層面是數據庫的鎖;如果是分佈式的系統,顯然只能利用數據庫端的鎖來實現。
假定我們採用了同步機制或者數據庫物理鎖機制,如何保證1w個人還能同時看到有商品,顯然會犧牲性能,在高併發網站中是不可取的。使用hibernate後我們提出了另外一個概念:樂觀鎖(一定要用)、悲觀鎖(即傳統的物理鎖);採用樂觀鎖即可解決此問題。樂觀鎖意思是不鎖定表的情況下,利用業務的控制來解決併發問題,這樣即保證數據的併發可讀性又保證保存數據的排他性,保證性能的同時解決了併發帶來的髒數據問題。
hibernate中如何實現樂觀鎖:
前提:在現有表當中增加一個冗餘字段,version版本號, long類型
原理:
1)只有當前版本號》=數據庫表版本號,才能提交
2)提交成功後,版本號version ++
實現很簡單:在ormapping增加一屬性-lock="version"即可,以下是樣例片段optimistic
<hibernate-mapping>
<class name="com.insigma.stock.ABC" optimistic-lock="version" table="T_Stock" schema="STOCK">
</hibernate-mapping>
更新的時候給版本號字段加上 1,然後 UPDATE 會返回一個更新結果的行數,通過這個行數去判斷。
Sku_id | sale | Version(樂觀鎖的標誌字段) |
1 | 100 | 1 |
UPDATE 必須這樣寫:
Sale = 100
UPDATE EB_SKU u
SET u.SALES = #SALES#,
u.version = u.version + 1
WHERE u.SKU_ID = #SKUID#
AND u.version = #version#
Update user t set t.address = #{address} where t.user_id = #{userId}
如果更新執行返回的數量是 0 表示產生併發修改了,需要重新獲得最新的數據後再進行更新操作。
Hibernate、JPA 等 ORM 框架或者實現,是使用版本號,再判斷 UPDATE 後返回的數值,如果這個值小於 1 時則拋出樂觀鎖併發修改異常。
解決大量用戶訪問量問題方案是集羣部署,防止宕機,負載均衡
1.我們採用4臺portal服務器來集羣,使用2臺nginx代理服務器,
1)反向代理,把4臺portal的服務(host)集中起來,訪問動態鏈接代理地址(代理IP:192.168.1.100)時候會把請求轉發到4太portal上,如果訪問靜態頁面直接訪問nginx上的資源文件就可以了,靜態html中有ajax的請求由nginx的反向代理功能來轉發。
2)部署靜態資源(html和圖片)
2.Rsync用作資源同步
當console上傳的圖片,由於rsync的部署,會指定一個具體的同步目錄(上傳圖片的目錄),一旦發現目錄中有文件就立刻同步到nginx上
3.Redis負責管理session和緩存搜索的數據
管理session的原因:用於多臺服務器之間需要有相同的session,同享策略耗費資源,所以採用redis來存儲session。緩存頻繁被搜索的數據。
在做這個項目的時候你碰到了哪些問題?你是怎麼解決的?
①.開發webservice接口出現客戶端和服務端不同步,導致接口無法測試,產生的原因溝通不暢。
②.訂單提交時由於本地bug或者意外故障導致用戶錢支付了但是訂單不成功,採用對賬方式來解決。
③.上線的時候一定要把支付的假接口換成真接口。
④.項目中用到了曾經沒有用過的技術,解決方式:用自己的私人時間主動學習
⑤.在開發過程中與測試人員產生一些問題,本地環境ok但是測試環境有問題,環境的問題產生的,瀏覽器環境差異,服務器之間的差異
⑥.系統運行環境問題,有些問題是在開發環境下OK,但是到了測試環境就問題,比如說系統文件路徑問題、導出報表中的中文問題(報表採用highcharts),需要在系統jdk中添加相應的中文字體才能解決;
你做完這個項目後有什麼收穫?
首先,在數據庫方面,我現在是真正地體會到數據庫的設計真的是一個程序或軟件設計的重要和根基。因爲數據庫怎麼設計,直接影響到一個程序或軟件的功能的實現方法、性能和維護。由於我做的模塊是要對數據庫的數據進行計算和操作的,所以我對數據庫的設計對程序的影響是深有體會,就是因爲我們的數據庫設計得不好,搞得我在對數據庫中的數據進行獲取和計算利潤、總金時,非常困難,而且運行效率低,時間和空間的複雜也高,而且維護起來很困難,過了不久,即使自己有註釋,但是也要認真地看自己的代碼才能明白自己當初的想法和做法。加上師兄的解說,讓我對數據庫的重要的認識更深一層,數據庫的設計真的是重中之重。
其次,就是分工的問題。雖然這次的項目我們沒有在四人選出一個組長,但是,由於我跟其他人都比較熟,也有他們的號碼,然後我就像一個小組長一樣,也是我對他們進行了分工。俗話也說,分工合作,分好了工,才能合作。但是這次項目,我們的分工卻非常糟糕,我們在分工之前分好了模塊,每個模塊實現什麼功能,每個人負責哪些模塊。本以爲我們的分工是明確的,後來才發現,我們的分工是那麼的一踏糊塗,一些功能上緊密相連的模塊分給了兩個人來完成,使兩個人都感到迷惘,不知道自己要做什麼,因爲兩個人做的東西差不多。我做的,他也在做,那我是否要繼續做下去?總是有這樣的疑問。從而導致了重複工作,浪費時間和精力,並打擊了隊員的激情,因爲自己辛辛苦苦寫的代碼,最後可能沒有派上用場。我也知道,沒有一點經驗的我犯這樣的錯是在所難免,我也不過多地怪責自己,吸取這次的教訓就好。分工也是一門學問。
再者,就是命名規範的問題。可能我們以前都是自己一個人在寫代碼,寫的代碼都是給自己看的,所以我們都沒有注意到這個問題。就像師兄說的那樣,我們的代碼看上去很上難看很不舒服,也不知道我們的變量是什麼類型的,也不知道是要來做什麼的。但是我覺得我們這一組人的代碼都寫得比較好看,每個人的代碼都有註釋和分隔,就是沒有一個統一的規範,每個人都人自己的一個命名規則和習慣,也不能見名知義。還有就是沒有定義好一些公共的部分,使每個人都有一個自己的“公共部分”,從而在拼起來時,第一件事,就是改名字。而這些都應該是在項目一開始,還沒開始寫代碼時應該做的。
然後,我自己在計算時,竟然太大意算錯了利潤,這不能只一句我不小心就敷衍過去,也是我的責任,而且這也是我們的項目的核心部分,以後在做完一個模塊後,一定要測試多次,不能過於隨便地用一個數據測試一下,能成功就算了,要用可能出現的所有情況去測試程序,讓所有的代碼都有運行過一次,確認無誤。
最後,也是我比較喜歡的東西,就是大家一起爲了一個問題去討論和去交流。因爲我覺得,無論是誰,他能想的東西都是有限的,別人總會想到一些自己想不到的地方。跟他人討論和交流能知道別人的想法、瞭解別人是怎樣想一個問題的,對於同樣的問題自己又是怎樣想的,是別人的想法好,還是自己的想法好,好在什麼地方。因爲我發現問題的能力比較欠缺,所以我也總是喜歡別人問我問題,也喜歡跟別人去討論一個問題,因爲他們幫我發現了我自己沒有發現的問題。在這次項目中,我跟植榮的討論就最多了,很多時候都是不可開交的那種,不過我覺得他總是能夠想到很多我想不到的東西,他想的東西也比我深入很多,雖然很多時候我們好像鬧得很僵,但是我們還是很要好的! 嘻嘻!而且在以後的學習和做項目的過程中,我們遇到的問題可能會多很多,複雜很多,我們一個人也不能解決,或者是沒有想法,但是懂得與他人討論與交流就不怕這個問題,總有人的想法會給我們帶來一片新天地。相信我能做得更好。
還有就是做項目時要抓準客戶的要求,不要自以爲是,自己覺得這樣好,那樣好就把客戶的需求改變,項目就是項目,就要根據客戶的要求來完成。
你這個項目中使用什麼構建的?多模塊開發是如何劃分的呢?爲什麼要這麼做?
我們這個項目使用Maven進行構建,並使用了水平劃分,這樣劃分層次清晰,代碼重用性高,易於獨立維護。
①垂直劃分
②水平劃分
優缺點:
垂直:功能模塊明確,層次不夠清晰,代碼重用性差。
水平:層次清晰,代碼重用性高,獨立維護。
淘淘商城後臺管理系統採取水平劃分。
你覺得在文件上傳功能上面需要注意什麼?
對上傳的文件做校驗!
在你這個項目中,是如何設計商品規格的?
實現思路很重要!
在這個項目中你是如何實現跨系統調用的?(DUbbo)
有兩種調用方式:
1、 Ajax,走前臺js,通過jsonp來跨域,關於jsonp請參考:http://www.cnblogs.com/yuzhongwusan/archive/2012/12/11/2812849.html
a) 效率
b) 帶寬
document.domain="taotao.com"
jsonp,部署子域名的情況
需要在SpringMVC中擴展MappingJackson2HttpMessageConverter,支持jsonp。跨域問題,因爲我們是子域名訪問子系統接口的,採用jsonp解決;
2、 後臺轉發請求,走後臺,通過httpclient來調。
a) 可以加邏輯(加緩存只能這條路走)
b) 安全,接口不在公網公開
重點學習httpclient中的示例
掌握spring和Httpclient的集成
我們這個項目2種方式都使用到了。
你這個項目中CMS系統是如何設計的,簡單的說一下其設計思想?
隱藏在內容管理系統(CMS)之後的基本思想是分離內容的管理和設計。頁面設計存儲在模板裏,而內容存儲在數據庫或獨立的文件中。當一個用戶請求頁面時,各部分聯合生成一個標準的HTML(標準通用標記語言下的一個應用)頁面。
內容管理系統被分離成以下幾個層面:各個層面優先考慮的需求不同
1,後臺業務子系統管理(管理優先:內容管理):新聞錄入系統,BBS論壇子系統,全文檢索子系統等,針對不同系統的方便管理者的內容錄入:所見即所得的編輯管理界面等,清晰的業務邏輯:各種子系統的權限控制機制等;
2,Portal系統(表現優先:模板管理):大部分最終的輸出頁面:網站首頁,子頻道/專題頁,新聞詳情頁一般就是各種後臺子系統模塊的各種組合,這種發佈組合邏輯是非常豐富的,Portal系統就是負責以上這些後臺子系統的組合表現管理;
3,前臺發佈(效率優先:發佈管理):面向最終用戶的緩存發佈,和搜索引擎spider的URL設計等……
內容管理和表現的分離:很多成套的CMS系統沒有把後臺各種子系統和Portal分離開設計,以至於在Portal層的模板表現管理和新聞子系統的內容管理邏輯混合在一起,甚至和BBS等子系統的管理都耦合的非常高,整個系統會顯得非常龐雜。而且這樣的系統各個子系統捆綁的比較死,如果後臺的模塊很難改變。但是如果把後臺各種子系統內容管理邏輯和前臺的表現/發佈分離後,Portal和後臺各個子系統之間只是數據傳遞的關係:Portal只決定後臺各個子系統數據的取捨和表現,而後臺的各個子系統也都非常容易插拔。
內容管理和數據分發的分離:需要要Portal系統設計的時候注意可緩存性(Cache Friendly)性設計:CMS後臺管理和發佈機制,本身不要過多考慮"效率"問題,只要最終頁面輸出設計的比較Cacheable,效率問題可通過更前端專門的緩存服務器解決。
此外,就是除了面向最終瀏覽器用戶外,還要注意面向搜索引擎友好(Search engine Friendly)的URL設計:通過 URL REWRITE轉向或基於PATH_INFO的參數解析使得動態網頁在鏈接(URI)形式上更像靜態的目錄結構,方便網站內容被搜索引擎收錄;
在這個項目中,你們主要使用什麼樣的數據格式來進行數據的傳輸的?你對JSON瞭解麼?能說說JSON對象如何轉換成Java對象的?
單點系統的設計思想你瞭解嗎?他在系統架構中的作用是什麼?位置如何?
單點登錄SSO(Single Sign On)說得簡單點就是在一個多系統共存的環境下,用戶在一處登錄後,就不用在其他系統中登錄,也就是用戶的一次登錄能得到其他所有系統的信任。單點登錄在大型網站裏使用得非常頻繁,例如像阿里巴巴這樣的網站,在網站的背後是成百上千的子系統,用戶一次操作或交易可能涉及到幾十個子系統的協作,如果每個子系統都需要用戶認證,不僅用戶會瘋掉,各子系統也會爲這種重複認證授權的邏輯搞瘋掉。實現單點登錄說到底就是要解決如何產生和存儲那個信任,再就是其他系統如何驗證這個信任的有效性,因此要點也就以下幾個:
存儲信任
驗證信任
只要解決了以上的問題,達到了開頭講得效果就可以說是SSO。最簡單實現SSO的方法就是用Cookie,實現流程如下所示:
不難發現以上的方案是把信任存儲在客戶端的Cookie裏,這種方法雖然實現方便但立馬會讓人質疑兩個問題:
Cookie不安全
不能跨域免登
對於第一個問題一般都是通過加密Cookie來處理,第二個問題是硬傷,其實這種方案的思路的就是要把這個信任關係存儲在客戶端,要實現這個也不一定只能用Cookie,用flash也能解決,flash的Shared Object API就提供了存儲能力。
一般說來,大型系統會採取在服務端存儲信任關係的做法,實現流程如下所示:
以上方案就是要把信任關係存儲在單獨的SSO系統(暫且這麼稱呼它)裏,說起來只是簡單地從客戶端移到了服務端,但其中幾個問題需要重點解決:
如何高效存儲大量臨時性的信任數據
如何防止信息傳遞過程被篡改
如何讓SSO系統信任登錄系統和免登系統
對於第一個問題,一般可以採用類似與memcached的分佈式緩存的方案,既能提供可擴展數據量的機制,也能提供高效訪問。對於第二個問題,一般採取數字簽名的方法,要麼通過數字證書籤名,要麼通過像md5的方式,這就需要SSO系統返回免登URL的時候對需驗證的參數進行md5加密,並帶上token一起返回,最後需免登的系統進行驗證信任關係的時候,需把這個token傳給SSO系統,SSO系統通過對token的驗證就可以辨別信息是否被改過。對於最後一個問題,可以通過白名單來處理,說簡單點只有在白名單上的系統才能請求生產信任關係,同理只有在白名單上的系統才能被免登錄。
你們這個項目中訂單ID是怎麼生成的?我們公司最近打算做一個電商項目,如果讓你設計這塊,你會考慮哪些問題?
生成訂單ID的目的是爲了使訂單不重複,本系統訂單ID生成規則:
用戶ID+當前系統的時間戳
String orderId = order.getUserId() + "" + System.currentTimeMillis();
設計的時候我會考慮:
訂單ID不能重複
訂單ID儘可能的短(佔用存儲空間少,實際使用方便,客服相關)
訂單ID要求是全數字(客服)
各個服務器的時間不統一怎麼辦?
在各個服務器上做時間的統一;(運維)
在問題17的基礎上,可能存在毫秒級的偏差情況,怎麼辦?
修改訂單生成規則:
用戶ID+當前系統的時間戳+隨機數(3~4位) 問題:太長? 把時間戳中的2014中的20拿掉;
你們線上部署時什麼樣的,能畫一下嗎?
多臺tomcat之間的session是怎麼同步的?
不用session,我們使用單點登陸,使用redis,存在redis,生成,A同步到B,B同步到C。
如何解決併發問題的?
集羣,負載均衡,nginx(主備,一般主在工作,備閒置;資源浪費),lvs(在2個Nginx前做一個攔截,接收後進行分工)。有問題,如果nginx掛掉,整個系統就掛了。可以主備解決,可以前面搭一個lvs。這塊不是你做的,但是你知道怎麼解決(非常複雜,但是必須瞭解。針對具體的情況去具體對待,CPU,內存,不要一刀切。)
你們生產環境的服務器有多少臺?
面試前要數好,一般是十幾到二十臺。(用在哪裏?這是重點)
Nginx至少2臺
Tomcat至少3臺以上
數據庫至少2臺
Redis至少一臺
可參考17問圖
備份是怎麼做的?有沒有做讀寫分離?
主從(一主多從,主要是備份主),每天備份,備份的文件不要放到數據庫服務器上,可以FTP。要檢查有效否。讀寫分離自己查一下,分庫分表做過。
你們服務器不止一臺吧,那麼你們的session是怎麼同步的?
此問題與18相同,如果購物車使用session做的話,此問題極易被問到。
Session放到redis裏面,使用單點登錄系統。購物車設計思路:未登錄(先寫到cookie中,登錄後寫到數據庫表中);已登錄(直接寫到數據庫,而不會寫到cookie)實際項目是不使用session的,使用redis集中處理處理數據,取代session的作用,應用在單點登錄、購物車等。
你們使用什麼做支付的?如果使用易寶做支付,請求超時了怎麼處理?
①重試,一般三次,每次重試都要停頓一會,比如,以第一次停頓1秒,第二次停頓2秒,第三次停頓3秒;
②給訂單標識付款異常狀態,並且發出警告(郵件、短信)給相關人員。
③寫個定時任務,定時處理異常狀態的訂單。
你剛纔不是說付款成功後易寶會有數據返回嗎?如果付款後易寶沒有返回,或者返回超時了,但是錢又已經扣了,你怎麼辦?
①我們請求了易寶,但是沒有接受到響應,我們就認爲該訂單沒有支付成功,並且將訂單標識爲異常狀態;
②使用定時任務處理;
③做一個對賬的任務,實時處理異常狀態的訂單。
你們怎麼做退款功能的,要多長時間才能把錢退回給用戶?
用戶申請退款後,經過客服審覈通過會將退款請求提交到易寶,具體到賬時間要看易寶的處理。
你前臺portal採用4臺服務器集羣部署 那你數據庫有幾臺服務器?如果前臺高併發訪問性能提上去了,那數據庫會不會造成一個瓶頸,這一塊你是怎麼處理的?
你購物車存cookie裏邊可以實現不登錄就可以使用購物車,那麼我現在沒有登錄把商品存購物車了,然後登錄了,然後我換臺電腦並且登錄了還能不能看見我購物車的信息?如果看不到怎麼做到cookie同步,就是在另外一臺電腦上可以看到購物車信息
更換電腦,必須登錄才能看到之前購物車的商品。
跨域cookie同步方案:
場景:有時一個公司可能有多個不同域名的網站,比如sina.com和weibo.cn,比如taobao.com和tmall.com。
這些網站背後很多是同一套會員體系。由於http協議規定cookie是跟着域名走的,這時就需要在不同的域名下同步登陸狀態,避免出現用戶體驗上出現需要二次登陸驗證的情況。
假設下面這樣一個場景:
用戶在 bbb.com上已經登陸,現在要去aaa.com上玩,在aaa.com域名下暫未登錄。需要訪問的aaa.com/resource.html資源需要登錄才能訪問。兩個網站是同一套會員體系,同一個公司的。這是要讓用戶體驗上做到用戶在aaa.com上玩也能識別出登錄狀態。
以上面場景爲例,下面畫了個實現跨域同步簡單流程圖:
解釋如下:
第一步:用戶向aaa.com發起get請求,獲取resource.html資源,aaa.com發現用戶未登錄,返回302狀態和外部重定向url:
Java代碼 收藏代碼
j.bbb.com?target=www.aaa.com/resource.html
注意j.bbb.com子域名上部署的應用可以認爲是專門用了跨域同步。
第二步:用戶根據重定向url,訪問j.bbb.com?target=www.aaa.com/resource.html,由於在bbb.com上已經登錄,所以bbb.com上能拿到從client端傳遞過來cookie信息。子域j.bbb.com上的應用負責將cookie讀取出來,並作爲參數再次重定向到
Java代碼 收藏代碼
p.aaa.com?tartet=www.aaa.com/resource.html&sessionid=xxx&loginId=xxx&……
第三步:用戶根據第二步重定向url,訪問p.aaa.com。p.aaa.com子域名上的應用專門負責根據請求參數裏的參數對,往aaa.com域寫入cookie,並重定向到用戶第一步請求的url。
第四步:經過前三步,已經完成了再aaa.com域名下同步bbb.com的登錄狀態,用戶再次請求aaa.com/resource.html,這是就能成功訪問了。
點一個鏈接訪問到一個頁面,這個頁面上既有靜態數據,又有動態數據(需要查數據庫的),打開這個頁面的時候就是很慢但是也能打開。怎麼解決這個問題,怎麼優化?
如果要靜態頁面的話那就得用freemarker或者通過ajax異步,通過js操作異步刷新表單,通過js對返回結果組裝成html。
緩存、動態頁面靜態化:
所謂緩存, 是指將那些經常重複的操作結果暫時存放起來, 在以後的執行過程中, 只要使用前面的暫存結果即可.
那麼在我們開發Web網站的過程中, 到底有多少工作可以採用用緩存呢? 或者說, 我們可以在哪些地方使用緩存呢? 見下圖: 下圖是客戶端瀏覽器和Web服務器之間的一次完整的通信過程, 紅色圓圈標示了可以採用緩存的地方.
首先, 最好的情況是客戶端不發送任何請求直接就能獲得數據, 這種情況下, 用於緩存的數據保存在客戶端瀏覽器的緩存中.
其次, 在具有代理服務器的網絡環境中, 代理服務器可以針對那些經常訪問的網頁製作緩存, 當局域網中第一臺主機請求了某個網頁並返回結果後, 局域網中的第二臺主機再請求同一個網頁, 這時代理服務器會直接返回上一次緩存的結果, 並不會向網絡中的IIS服務器發送請求, 例如: 現在的連接電信和網通線路的加速器等. 但是代理服務器通常有自己專門的管理軟件和管理系統, 作爲網站開發人員對代理服務器的控制能力有限.
再次, 前面也說過, 當用戶將請求地址發送到IIS服務器時, IIS服務器會根據請求地址選擇不同的行爲, 如: 對於*.aspx頁面會走應用程序管道, 而對*.html、*.jpg等資源會直接返回資源, 那麼我們可以把那些頻繁訪問的頁面做成*.html文件, 這樣用戶請求*.html, 將不用再走應用程序管道, 因此會提升效率. 例如: 網站首頁、或某些突發新聞或突發事件等, 可以考慮做成靜態網頁. 就拿”天氣預報的發佈頁面”打比方: 天氣預報的發佈頁面的訪問用戶非常多, 我們可以考慮將發佈頁做成靜態的*.html網頁, 之後在整個網站程序啓動時, 在Gboabl.asax的Application_Start事件處理器中, 創建子線程以實現每3個小時重新獲取數據生成新的天氣發佈頁面內容.
之後的asp.net的處理流程, 作爲程序員我們是無法干涉的. 直到啓動HttpApplication管道後, 我們纔可以通過Global.asax或IHttpModule來控制請求處理過程, 在應用程序管道中適合做整頁或用戶控件的緩存. 如: 緩存熱門頁面, 我們可以自動緩存整個網站中訪問量超過一定數值(閥值)的頁面, 其中爲了減小IO操作, 將緩存的頁面放在內容中.
如果用戶一直向購物車添加商品怎麼辦?並且他添加一次你查詢一次數據庫?互聯網上用戶那麼多,這樣會對數據庫造成很大壓力你怎麼辦?
在回答這個問題前,請想好自己的項目是否真的需要使用購物車?(SKU數少,商品結構單一等就不需要使用購物車了)
購物車的設計方案:
購物車的實現不存在哪種方式更好,完全是根據公司和項目架構相關的,類似蘇寧使用的是數據庫存儲,但是國美使用的就是Session,不同的軟件架構和不同的業務需求對應的購物車存儲也是不一樣的
用數據庫存你得給數據庫造成多大的負擔啊, 而且對於購物車, 這種需要實時操作的東西, 數據庫的訪問量一大了, 就容易出現併發錯誤, 或者直接崩潰.
用Session確實效率很高, 而且會話是針對各個連接的, 所以便於管理, 但是用Session也不是完美的, 因爲Session是有有效期的, 根據服務器的設置不同而不一樣長, 如果你在購物的過程中Session超時了, 那麼購物車中的東西就會全沒了.不知道你看過當當網的購物車沒有, 當你下線之後, 再次上線, 購物車中的東西還是存在的, 這對於用戶來說非常方便.所以如果你的服務器夠強的話, 你完全可以用一個靜態變量來保存所有用戶的購物車, 比如用一個靜態的Map, 以IP作爲Key,區分不同用戶的購物車, 這樣就可以使用戶在下線的情況下也可以保存購物車中的內容.這種方法實現過, 只是沒有用大量的併發訪問測試其穩定性, 但是一定是可行的。
採用存儲過程將購物車存儲於數據庫相應表的方式,優點:數據穩定,不易丟失。缺點:效率低,增加數據庫服務器負擔。變量 + Datatable保存於客戶端,優點:效率高,減輕數據庫服務器負擔。缺點:Session保存的變量容易丟失,但是一般情況下不會造成影響。變量 + 購物車對象保存於客戶端,這種方式以面向對象爲指導思想,邏輯上具有一定的複雜性。優點:效率高,減輕數據庫服務器負擔,使用便捷。缺點:Session保存的變量容易丟失,但是一般情況下不會造成影響
購物車的核心功能
購物車數據存數據庫好處有很多,可以分析購買行爲,可以爲客戶保存購買信息(不會因爲瀏覽器關閉而丟失)等,我的這個項目的購物車使用的就是將購物車數據存數據庫中,未登錄時可以加20個商品,登錄後可以加50個。
做促銷時,商品詳情頁面的靜態頁面如何處理價格問題。
京東商品詳情頁雖然僅是單個頁面,但是其數據聚合源是非常多的,除了一些實時性要求比較高的如價格、庫存、服務支持等通過AJAX異步加載加載之外,其他的數據都是在後端做數據聚合然後拼裝網頁模板的。整個京東有數億商品,如果每次動態獲取如上內容進行模板拼裝,數據來源之多足以造成性能無法滿足要求;最初的解決方案是生成靜態頁,但是靜態頁的最大的問題:
1、無法迅速響應頁面需求變更;
2、很難做多版本線上對比測試。如上兩個因素足以制約商品頁的多樣化發展,因此靜態化技術不是很好的方案。
數據主要分爲四種:商品頁基本信息、商品介紹(異步加載)、其他信息(分類、品牌、店鋪等)、其他需要實時展示的數據(價格、庫存等)。而其他信息如分類、品牌、店鋪是非常少的,完全可以放到一個佔用內存很小的Redis中存儲;而商品基本信息我們可以借鑑靜態化技術將數據做聚合存儲,這樣的好處是數據是原子的,而模板是隨時可變的,吸收了靜態頁聚合的優點,彌補了靜態頁的多版本缺點;另外一個非常嚴重的問題就是嚴重依賴這些相關係統,如果它們掛了或響應慢則商品頁就掛了或響應慢;商品介紹我們也通過AJAX技術惰性加載(因爲是第二屏,只有當用戶滾動鼠標到該屏時才顯示);而實時展示數據通過AJAX技術做異步加載
1、接收商品變更消息,做商品基本信息的聚合,即從多個數據源獲取商品相關信息如圖片列表、顏色尺碼、規格參數、擴展屬性等等,聚合爲一個大的JSON數據做成數據閉環,以key-value存儲;因爲是閉環,即使依賴的系統掛了我們商品頁還是能繼續服務的,對商品頁不會造成任何影響;
2、接收商品介紹變更消息,存儲商品介紹信息;
3、介紹其他信息變更消息,存儲其他信息
技術選型
MQ可以使用如Apache ActiveMQ;
Worker/動態服務可以通過如Java技術實現;
RPC可以選擇如alibaba Dubbo;
KV持久化存儲可以選擇SSDB(如果使用SSD盤則可以選擇SSDB+RocksDB引擎)或者ARDB(LMDB引擎版);
緩存使用Redis;
SSDB/Redis分片使用如Twemproxy,這樣不管使用Java還是Nginx+Lua,它們都不關心分片邏輯;
前端模板拼裝使用Nginx+Lua;
數據集羣數據存儲的機器可以採用RAID技術或者主從模式防止單點故障;
因爲數據變更不頻繁,可以考慮SSD替代機械硬盤。
核心流程
1、首先我們監聽商品數據變更消息;
2、接收到消息後,數據聚合Worker通過RPC調用相關係統獲取所有要展示的數據,此處獲取數據的來源可能非常多而且響應速度完全受制於這些系統,可能耗時幾百毫秒甚至上秒的時間;
3、將數據聚合爲JSON串存儲到相關數據集羣;
4、前端Nginx通過Lua獲取相關集羣的數據進行展示;商品頁需要獲取基本信息+其他信息進行模板拼裝,即拼裝模板僅需要兩次調用(另外因爲其他信息數據量少且對一致性要求不高,因此我們完全可以緩存到Nginx本地全局內存,這樣可以減少遠程調用提高性能);當頁面滾動到商品介紹頁面時異步調用商品介紹服務獲取數據;
5、如果從聚合的SSDB集羣/Redis中獲取不到相關數據;則回源到動態服務通過RPC調用相關係統獲取所有要展示的數據返回(此處可以做限流處理,因爲如果大量請求過來的話可能導致服務雪崩,需要採取保護措施),此處的邏輯和數據聚合Worker完全一樣;然後發送MQ通知數據變更,這樣下次訪問時就可以從聚合的SSDB集羣/Redis中獲取數據了。
基本流程如上所述,主要分爲Worker、動態服務、數據存儲和前端展示;因爲系統非常複雜,只介紹動態服務和前端展示、數據存儲架構;Worker部分不做實現。
商品搜索框的搜索聯想如何實現?比如輸入“羽絨” ,然後輸入框下會列出很多關於羽絨服的搜索條件 “羽絨服男正品折扣 ”等等。
一個電商項目,在tomcat裏面部署要打幾個war包?
你說你用了redis緩存,你redis存的是什麼格式的數據,是怎麼存的?
購物車知識補充(在設計購物車時需要注意哪些細節)
爲什麼購物車的設計很重要?
①購物車是消費的最後一環
購物車在用戶整體消費過程中一般是在最後一環,用戶完整的消費體驗應該是:打開APP或網站->瀏覽商品->加入購物車->確認訂單並支付,在這個過程中,購物車和支付環節可以合併成一環,基本上用戶點開購物車並開始填寫地址的時候,就有很大的機率要完成購買,做好商品展現以及推送的環節,如果在最後的購物一環沒有好的用戶體驗,豈不嗚呼哀哉。
②購物車隱含的對比收藏功能
與現實購物車不同的是,網絡消費者也比較喜歡把看中但不計劃買的商品先放入購物車,或者把商品統一放到購物車直接進行比較,以備日後購買,因此從購物車保存的信息,就能夠知道用戶的大致偏好。
③購物車的重交易屬性
用戶在瀏覽商品涉及的只是前端展示,但購物車這一環涉及到最終的交易,對於用戶來說,需要了解本次交易的基本物品信息、價格信息;而對於商戶來說,確認收款、訂單生成、物流環節都需要在這裏獲取到信息,才能完成本次的交易。
購物車設計需要展示的基本信息
購物車主要作用就是告訴用戶買了什麼,價格多少,不同類型的物品可能會有不同展示方式,但最基本的包括商品名稱、價格、數量(若是服務,可能是次數)、其他附屬信息。
哪些細節要讓用戶買得舒服?
親,記得前面說的用戶是如何看待購物車的功能嗎?還記得你的用戶會多次使用購物車,如果你只是完整做好信息展示不做好其他事情真的好嗎?
①登錄環節不要放在加入購物車前
請讓用戶先加入購物車,並在進行結算的時候在提醒用戶需要登錄。爲什麼?過早提醒用戶需要登錄才能購買,會打斷用戶瀏覽的流程(用戶可能還要購買其他物品好嗎?)這樣的設置會讓部分用戶避而遠之。
這裏涉及到的一個點是在APP端需要記憶用戶加入購物車的信息,與登錄後的購物車信息合併(如果一開始沒有這樣考慮好,技術那可能會有難度)
②自動勾選用戶本次挑選的商品
用戶使用購物車有一個大的作用就是收藏,所以你要知道很多用戶在購物車中積累了很多物品,當每次挑選加入購物車的商品,用戶每次來到購物車要重新把本次的購買商品選上是很不好的體驗。
所以這裏一般是自動勾選本次挑選的商品,同樣這裏也要儲存用戶的勾選信息。
③陳列展示,注意沉底商品
讓用戶看見當前想買的商品就好了,把一些時間久遠的,已經賣完的沉底顯示。這樣做的好處是能讓用戶看見之前的選擇但沒購買的商品,提醒一下說不定就又勾上買了哦!
④歸類展示,可能增加購買
考慮如何進行歸類展示,C2C可以按照商家分類,B2C可以按照品牌分類。
⑤價格和優惠的提醒
消費用戶會關係自己每一次的消費價格,爲避免商品列表過長隱藏價格信息,APP端一般會把總價固定底部提示。同時在合計信息中,展示優惠價格,能夠促進消費者購買。
哪些細節要推動用戶繼續購買?
①還差一點就可以有優惠啦!
湊單,常用的手段包括運費見面或是滿減促銷,一般在網站底部會展示一些適合湊單的商品;在