电商项目案例介绍
電商行業技術特點
①技術新:(NoSql推廣首在社區網站和電商項目),發展快,需求推動技術的革新。
②技術范圍廣:除了java,像淘寶前端還使用了PHP,數據庫MySQL或者oracle,nosql,服務器端使用Linux,服務器安全、系統安全
③分布式:以前是在一臺機器上做運算,現在是分散到很多機器上,最后匯總起來。(集中式向分布式進行考慮)由需求來推動
④高并發、集群、負載均衡、高可用:由并發問題采用集群進行處理,其中,集群會涉及服務器的主從以及分布問題,使用負載均衡。(權重高低)高可用是對用戶而言,用戶的服務不中斷(系統升級,服務不中斷,淘寶每周更新2次)。
⑤海量數據:雙11,570億的背后,訂單有多少?瀏覽次數有多少?商品會有多少?活動相關數據?
⑥業務復雜:不要簡單的認為是:商品展示出來后,加入購物車后購買就完成了。后臺特別復雜,比如優惠(包郵、滿減)
⑦系統安全:系統上線必須通過系統安全部門審核通過。前年CSDN數據泄露??旖菥频陻祿孤?通過身份證就可以查看你的開房記錄)。近幾年,安全意識逐步在提高。
電商行業的一些概念
? ??1、?B2C:商家對客戶,京東、當當、發展為B2C平臺,天貓(B2C平臺?淘寶商城由馬云提出,率先發展為平臺),1號店也是(在上海)
? ??? ????2、?B2B:商家對商家,阿里巴巴(不零售,只批發,淘寶很多商家都會去阿里巴巴進貨);
? ??? ????3、?C2C:個人對個人,淘寶市場,淘寶,QQ商城;
系統功能
?? 本商城系統是一個綜合性的B2C平臺,類似京東商城、天貓商城。
會員可以在商城瀏覽商品、下訂單,以及參加各種活動。
商家可以在入住淘淘商城,在該平臺上開店出售自己的商品,并且得到淘淘商城提供的可靠的服務。
管理員、運營可以在平臺后臺管理系統中管理商品、訂單、會員等。
客服可以在后臺管理系統中處理用戶的詢問以及投訴。
??
?
? ??首先我們要有商品,管理員可以在系統中管理商品,用戶可以查看商品。
? ??商品多了之后要有類目模塊,管理員可以管理類目信息,用戶可以根據類目檢索商品。
? ??有了商品之后,要有人(會員)去買東西,普通用戶注冊為會員,會員可以登錄到系統管理自己的信息(密碼等)
? ??買了之后會生成訂單,會員可以購買商品并且可以下單,管理員可以管理訂單。
? ??有了訂單之后需要支付(在線支付/貨到付款)
? ??……
這樣,我們就可以把整個電商項目的功能記清楚了!
分布式系統架構
左圖為分布式系統架構,右圖為傳統架構!
? ?
各個系統說明:
- 后臺管理系統:管理商品、訂單、類目、商品規格屬性、用戶管理以及內容發布等功能。
- 前臺系統:用戶可以在前臺系統中進行注冊、登錄、瀏覽商品、首頁、下單等操作。
- 會員系統:用戶可以在該系統中查詢已下的訂單、收藏的商品、我的優惠券、團購等信息。
- 訂單系統:提供下單、查詢訂單、修改訂單狀態、定時處理訂單。
- 搜索系統:提供商品的搜索功能。
- 單點登錄系統:為多個系統之間提供用戶登錄憑證以及查詢登錄用戶的信息。
談到分布式架構,我們必須對比傳統架構才能彰顯其優勢。
? ??①最為明顯的一點,在傳統的架構中,如果某個功能需要進行維護,那么我們必須停掉整個服務,這對于公司的運營會造成損失。分布式系統在核心功能模塊使用單獨服務器,維護部分模塊不影響用戶的其他操作。
? ??②在海量數據處理方面,傳統架構顯得比較乏力;分布式系統架構采用服務器集群,使用負載均衡,海量數據處理游刃有余!
? ??③在性能(檢索)以及維護方面,分布式系統架構也有較為明顯的優勢。
6.本系統人員配置情況
l?產品經理:3人,確定需求以及給出產品原型圖。
l?項目經理:1人,項目管理。
l?前端團隊:3人,根據產品經理給出的原型制作靜態頁面。
l?后端團隊:20人,實現產品功能。
l?測試團隊:3人,測試所有的功能。
l?運維團隊:2人,項目的發布以及維護。
?
7.開發流程
? ??
?
8.后臺開發環境
? ??
需要注意,在幾個環境中,預發布環境和生產環境幾乎一樣,開發環境和測試環境是獨立存在的,每一個階段的活是由對應的工作人員來負責的。
此外,我們還需要熟悉?SVN主干、分支、標簽開發過程流程:
? ??
? ??整個開發沿著主線進行,在一個版本定型后,前一個版本出現bug,那么此時會對bug進行修復,投入使用的依舊是之前定型的那個版本,待前一個版本的bug修復好了之后,會定義一個新的版本,主線不會改變,如果改動不大,那么只需修訂問題,繼續沿用此版本(1.0.x),只有出現較大改動時,才會升級一個新的版本號(1.1.x)。所有動作,交替進行,沿主線向前推進!
9.涉及技術
l?Spring、SpringMVC、Mybatis
l?JSP、JSTL、jQuery、jQuery?plugin、EasyUI、KindEditor(富文本編輯器)、CSS+DIV
l?Redis(緩存服務器)
l?Lucene、Solr(搜索)
l?httpclient(調用系統服務)
l?Mysql
l?Nginx(web服務器)
l?Quartz(定時任務)
l?RabbitMQ(消息隊列)
?
技術詳解見其它章節,未接觸技術點(標背景色技術點)將在本項目后續內容中進行介紹。
10.開發工具和環境
l?Eclipse?4.4.1?
l?Maven?3.2.3
l?Tomcat?7.0.47(Maven?Tomcat?Plugin)
l?JDK?1.7
l?Mysql?5.6
l?Nginx?1.5.1
l?Redis?2.8.9
l?Win7?操作系統
l?SVN(版本管理)?
?
此章節,需要注意所有開發工具的版本號要對應(一致),否則會出現沖突問題,面試大忌!(常識:電商項目火起來的時間不是很久,在開發的時候,很多開發工具和環境不像之前那些傳統系統那么老舊,這點一定要引起注意!)
?
?
?
電商項目面試問題
?
項目介紹
①屬于B2C平臺----商家對客戶
②系統的用途,主要是提供B2C的平臺,其中自營商品也有商家入住,類似天貓。
③系統架構,采用分布式的系統架構,其中前臺系統和單點登錄系統采用了集群的方式部署,在后臺管理系統中采用了Maven的多模塊化的管理,其中采用了水平切分的方式,將pojo、dao、service、web分層開發,這樣做的好處就是可以重用性更高。
系統內部接口調用采用Httpclient,并且使用Httpclient的連接池技術,接口提供端采用RESTful方式的接口定義;
系統之間的通知機制采用MQ的方式,使用RabbitMQ的實現,使用了RabbitMQ的消息訂閱模式的消息機制;
系統的接口還對JS的跨域做了支持,采用了jsonp的解決方法,在后臺接口中擴展了spirng提供的jackson數據轉化器實現;
? ??④部署方面,采用了Nginx+tomcat的模式,其中nginx的作用一方面是做反向代理、負載均衡、另一方面是做圖片等靜態資源的服務器。
項目介紹應該從這個項目的模式、功能、架構、解決了什么問題、部署以及投入使用情況來說。
2.整個項目的架構如何?
一般情況這個問題要從兩個方面去回答,從技術架構和功能架構去談。
技術架構:本項目使用主流框架spring+springmvc+mybatis進行開發,采用分布式的系統架構,前臺系統和單點登錄系統采用了集群的方式部署,后臺管理系統中采用了Maven的多模塊化的管理,其中采用了水平切分的方式,將pojo、dao、service、web分層開發,這樣做的好處就是可以重用性更高。系統內部接口調用采用Httpclient,并且使用Httpclient的連接池技術,接口提供端采用RESTful方式的接口定義;系統之間的通知機制采用MQ的方式,使用RabbitMQ的實現,使用了RabbitMQ的消息訂閱模式的消息機制;系統的接口還對JS的跨域做了支持,采用了jsonp的解決方法,在后臺接口中擴展了spirng提供的jackson數據轉化器實現;搜索系統使用了solr實現,在保證系統高性能的前提下,盡可能為公司節約成本,我們使用MySQL數據庫進行集群(oracle收費)。在部署方面,采用了Nginx+tomcat的模式,其中nginx的作用一方面是做反向代理、負載均衡、另一方面是做圖片等靜態資源的服務器。
功能架構:分布式系統架構
?
?
各個系統說明:
- 后臺管理系統:管理商品、訂單、類目、商品規格屬性、用戶管理以及內容發布等功能。
- 前臺系統:用戶可以在前臺系統中進行注冊、登錄、瀏覽商品、首頁、下單等操作。
- 會員系統:用戶可以在該系統中查詢已下的訂單、收藏的商品、我的優惠券、團購等信息。
- 訂單系統:提供下單、查詢訂單、修改訂單狀態、定時處理訂單。
- 搜索系統:提供商品的搜索功能。
- 單點登錄系統:為多個系統之間提供用戶登錄憑證以及查詢登錄用戶的信息。
談到分布式架構,我們必須對比傳統架構才能彰顯其優勢。
? ??①最為明顯的一點,在傳統的架構中,如果某個功能需要進行維護,那么我們必須停掉整個服務,這對于公司的運營會造成損失。分布式系統在核心功能模塊使用單獨服務器,維護部分模塊不影響用戶的其他操作。
? ??②在海量數據處理方面,傳統架構顯得比較乏力;分布式系統架構采用服務器集群,使用負載均衡,海量數據處理游刃有余!
? ??③在性能(檢索)以及維護方面,分布式系統架構也有較為明顯的優勢。
傳統架構:
?
?
?
?
3.這個項目為用戶提供了哪些服務?包括哪些功能?
u?商品管理模塊:其中包括品牌管理,屬性管理商品錄入/上下架管理,商品添加審核,靜態頁面發布
u?訂單模塊:其中包括使用activiti工作流訂單的查詢和訂單的流轉,定時作廢
u?商品前臺首頁:其中主要負責首頁商品列表篩選,首頁上動態展示篩選條件,點擊每一個篩選條件下面的商品列表要做聯動
u?單品頁面:采用freemarker來實現頁面靜態化,展示商品詳情信息和商品購買,該頁面采用靜態化以減輕系統壓力,使用了cxf框架發布服務
u?提交訂單頁面:提交用戶的訂單信息,?處理并發問題。
u?個人中心,包括用戶的登錄,個人信息的管理,收貨地址的管理,用戶所下的訂單的管理
u?購物車:把購物車的信息存在cookie里面管理
4.你承擔這個項目的哪些核心模塊?
在項目中主要負責相關系統的開發,主要有:
? ??1)?后臺管理系統,主要實現商品管理、商品規格參數管理、訂單管理、會員管理等、CMS(內容管理系統)等,并且提供了跨域支持;
? ??2)?前臺系統,主要是面向用戶訪問,使用Httpclient和后臺系統接口做交互,并且該系統在部署上采用集群的方式;
? ??3)?單點登錄系統,主要是提供集中用戶登錄憑證的集中解決方案,提供和用戶信息相關的接口,比如說用戶注冊、查詢等接口。
? ??4)?訂單系統,主要是提供和訂單相關的業務接口,在訂單系統了做了嚴格的數據校驗以及高并發寫的支持(這里可以說使用隊列實現),并且使用了Quartz定時任務實現對訂單的定時掃描,比如說關閉超時未付款的訂單;
? ??5)?搜索系統,主要是提供商品的搜索,采用開源企業級系統Solr實現,采用了MQ機制保證了商品數據可以及時同步到solr中;
? ??6)?會員系統,主要是維護用戶的信息,已購買訂單、優惠券、系統消息、修改密碼、綁定手機等功能;
? ??7)?緩存,主要是用Redis實現,并且對Redis做了集群來保證Redis服務的高可用。8)?支付系統,主要是負責訂單的支付、對賬等功能,主要是對接了支付寶的接口;
5.這些模塊的實現思路說一下?
①商品管理模塊
品牌管理:
主要掌握的核心是品牌的添加
l?圖片服務器的搭建,
l?上傳圖片到圖片服務器
l?表單的驗證,離開焦點的驗證,點擊完成時的驗證,后臺服務器的ajax驗證,表單規范
l?防止表單的二次提交
l?編輯時不需要修改品牌名,區別readOnly和disabled
l?刪除時的二次確認
注意:
自定義屬性必須掌握:html中自定義的屬性可以幫助索引元素
圖片服務器的搭建
1.?創建一個maven的web工程,在工程中創建一個存放資源的目錄
2.?把tomcat的web.xml中DefaultServlet的只讀屬性改成false
?
3.?編寫上傳到圖片服務器的代碼
由于應用服務器與圖片服務器出于不同的兩臺機器之中,所以可以提高系統的性能,能起到負載均衡的作用。上傳圖片時使用Jersey客戶端API調用REST風格的Web服務,Jersey1是一個開源的、可以用于生產環境的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.?辦理任務
6.項目中哪些功能模塊涉及了大數據量訪問?你是如何解決的?
系統前臺是互聯網上的用戶訪問的,會有大量用戶來訪問。
假定有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。緩存頻繁被搜索的數據。
7.在做這個項目的時候你碰到了哪些問題?你是怎么解決的?
①.開發webservice接口出現客戶端和服務端不同步,導致接口無法測試,產生的原因溝通不暢。
②.訂單提交時由于本地bug或者意外故障導致用戶錢支付了但是訂單不成功,采用對賬方式來解決。
③.上線的時候一定要把支付的假接口換成真接口。
④.項目中用到了曾經沒有用過的技術,解決方式:用自己的私人時間主動學習
⑤.在開發過程中與測試人員產生一些問題,本地環境ok但是測試環境有問題,環境的問題產生的,瀏覽器環境差異,服務器之間的差異
⑥.系統運行環境問題,有些問題是在開發環境下OK,但是到了測試環境就問題,比如說系統文件路徑問題、導出報表中的中文問題(報表采用highcharts),需要在系統jdk中添加相應的中文字體才能解決;
8.你做完這個項目后有什么收獲?
? ??首先,在數據庫方面,我現在是真正地體會到數據庫的設計真的是一個程序或軟件設計的重要和根基。因為數據庫怎么設計,直接影響到一個程序或軟件的功能的實現方法、性能和維護。由于我做的模塊是要對數據庫的數據進行計算和操作的,所以我對數據庫的設計對程序的影響是深有體會,就是因為我們的數據庫設計得不好,搞得我在對數據庫中的數據進行獲取和計算利潤、總金時,非常困難,而且運行效率低,時間和空間的復雜也高,而且維護起來很困難,過了不久,即使自己有注釋,但是也要認真地看自己的代碼才能明白自己當初的想法和做法。加上師兄的解說,讓我對數據庫的重要的認識更深一層,數據庫的設計真的是重中之重。
?
? ??其次,就是分工的問題。雖然這次的項目我們沒有在四人選出一個組長,但是,由于我跟其他人都比較熟,也有他們的號碼,然后我就像一個小組長一樣,也是我對他們進行了分工。俗話也說,分工合作,分好了工,才能合作。但是這次項目,我們的分工卻非常糟糕,我們在分工之前分好了模塊,每個模塊實現什么功能,每個人負責哪些模塊。本以為我們的分工是明確的,后來才發現,我們的分工是那么的一踏糊涂,一些功能上緊密相連的模塊分給了兩個人來完成,使兩個人都感到迷惘,不知道自己要做什么,因為兩個人做的東西差不多。我做的,他也在做,那我是否要繼續做下去?總是有這樣的疑問。從而導致了重復工作,浪費時間和精力,并打擊了隊員的激情,因為自己辛辛苦苦寫的代碼,最后可能沒有派上用場。我也知道,沒有一點經驗的我犯這樣的錯是在所難免,我也不過多地怪責自己,吸取這次的教訓就好。分工也是一門學問。
?
? ??再者,就是命名規范的問題??赡芪覀円郧岸际亲约阂粋€人在寫代碼,寫的代碼都是給自己看的,所以我們都沒有注意到這個問題。就像師兄說的那樣,我們的代碼看上去很上難看很不舒服,也不知道我們的變量是什么類型的,也不知道是要來做什么的。但是我覺得我們這一組人的代碼都寫得比較好看,每個人的代碼都有注釋和分隔,就是沒有一個統一的規范,每個人都人自己的一個命名規則和習慣,也不能見名知義。還有就是沒有定義好一些公共的部分,使每個人都有一個自己的“公共部分”,從而在拼起來時,第一件事,就是改名字。而這些都應該是在項目一開始,還沒開始寫代碼時應該做的。
?
? ??然后,我自己在計算時,竟然太大意算錯了利潤,這不能只一句我不小心就敷衍過去,也是我的責任,而且這也是我們的項目的核心部分,以后在做完一個模塊后,一定要測試多次,不能過于隨便地用一個數據測試一下,能成功就算了,要用可能出現的所有情況去測試程序,讓所有的代碼都有運行過一次,確認無誤。
?
? ??最后,也是我比較喜歡的東西,就是大家一起為了一個問題去討論和去交流。因為我覺得,無論是誰,他能想的東西都是有限的,別人總會想到一些自己想不到的地方。跟他人討論和交流能知道別人的想法、了解別人是怎樣想一個問題的,對于同樣的問題自己又是怎樣想的,是別人的想法好,還是自己的想法好,好在什么地方。因為我發現問題的能力比較欠缺,所以我也總是喜歡別人問我問題,也喜歡跟別人去討論一個問題,因為他們幫我發現了我自己沒有發現的問題。在這次項目中,我跟植榮的討論就最多了,很多時候都是不可開交的那種,不過我覺得他總是能夠想到很多我想不到的東西,他想的東西也比我深入很多,雖然很多時候我們好像鬧得很僵,但是我們還是很要好的! 嘻嘻!而且在以后的學習和做項目的過程中,我們遇到的問題可能會多很多,復雜很多,我們一個人也不能解決,或者是沒有想法,但是懂得與他人討論與交流就不怕這個問題,總有人的想法會給我們帶來一片新天地。相信我能做得更好。
?
? ??還有就是做項目時要抓準客戶的要求,不要自以為是,自己覺得這樣好,那樣好就把客戶的需求改變,項目就是項目,就要根據客戶的要求來完成。
9.你這個項目中使用什么構建的?多模塊開發是如何劃分的呢?為什么要這么做?
我們這個項目使用Maven進行構建,并使用了水平劃分,這樣劃分層次清晰,代碼重用性高,易于獨立維護。
①垂直劃分
?
②水平劃分
?
優缺點:
? ? 垂直:功能模塊明確,層次不夠清晰,代碼重用性差。
? ? 水平:層次清晰,代碼重用性高,獨立維護。
?
淘淘商城后臺管理系統采取水平劃分。
10.你覺得在文件上傳功能上面需要注意什么?
對上傳的文件做校驗!
11.在你這個項目中,是如何設計商品規格的?
實現思路很重要!
?
12.在這個項目中你是如何實現跨系統調用的?
?
有兩種調用方式:
1、?Ajax,走前臺js,通過jsonp來跨域,關于jsonp請參考:jsonp詳解 - yuzhongwusan - 博客園
a)?效率
b)?帶寬
document.domain="taotao.com"
jsonp,部署子域名的情況?
需要在SpringMVC中擴展MappingJackson2HttpMessageConverter,支持jsonp??缬騿栴},因為我們是子域名訪問子系統接口的,采用jsonp解決;
2、?后臺轉發請求,走后臺,通過httpclient來調。
a)?可以加邏輯(加緩存只能這條路走)
b)?安全,接口不在公網公開
重點學習httpclient中的示例
掌握spring和Httpclient的集成
我們這個項目2種方式都使用到了。
13.你這個項目中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)形式上更像靜態的目錄結構,方便網站內容被搜索引擎收錄;
14.在這個項目中,你們主要使用什么樣的數據格式來進行數據的傳輸的?你對JSON了解么?能說說JSON對象如何轉換成Java對象的?
?
?
?
?
15.單點系統的設計思想你了解嗎?他在系統架構中的作用是什么?位置如何?
單點登錄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的驗證就可以辨別信息是否被改過。對于最后一個問題,可以通過白名單來處理,說簡單點只有在白名單上的系統才能請求生產信任關系,同理只有在白名單上的系統才能被免登錄。
16.你們這個項目中訂單ID是怎么生成的?我們公司最近打算做一個電商項目,如果讓你設計這塊,你會考慮哪些問題?
生成訂單ID的目的是為了使訂單不重復,本系統訂單ID生成規則:
? ??用戶ID+當前系統的時間戳
- String?orderId?=?order.getUserId()?+?""?+?System.currentTimeMillis();
設計的時候我會考慮:
訂單ID不能重復
訂單ID盡可能的短(占用存儲空間少,實際使用方便,客服相關)
訂單ID要求是全數字(客服)
17.各個服務器的時間不統一怎么辦?
在各個服務器上做時間的統一;(運維)
18.在問題17的基礎上,可能存在毫秒級的偏差情況,怎么辦?
修改訂單生成規則:
? ??用戶ID+當前系統的時間戳+隨機數(3~4位)?問題:太長??把時間戳中的2014中的20拿掉;
19.你們線上部署時什么樣的,能畫一下嗎?
?
20.多臺tomcat之間的session是怎么同步的?
不用session,我們使用單點登陸,使用redis,存在redis,生成,A同步到B,B同步到C。
21.如何解決并發問題的?
集群,負載均衡,nginx(主備,一般主在工作,備閑置;資源浪費),lvs(在2個Nginx前做一個攔截,接收后進行分工)。有問題,如果nginx掛掉,整個系統就掛了??梢灾鱾浣鉀Q,可以前面搭一個lvs。這塊不是你做的,但是你知道怎么解決(非常復雜,但是必須了解。針對具體的情況去具體對待,CPU,內存,不要一刀切。)
22.你們生產環境的服務器有多少臺?
面試前要數好,一般是十幾到二十臺。(用在哪里?這是重點)
Nginx至少2臺
Tomcat至少3臺以上
數據庫至少2臺
Redis至少一臺
可參考17問圖
23.數據備份是怎么做的?有沒有做讀寫分離?
主從(一主多從,主要是備份主),每天備份,備份的文件不要放到數據庫服務器上,可以FTP。要檢查有效否。讀寫分離自己查一下,分庫分表做過。
24.你們服務器不止一臺吧,那么你們的session是怎么同步的?
此問題與18相同,如果購物車使用session做的話,此問題極易被問到。
Session放到redis里面,使用單點登錄系統。購物車設計思路:未登錄(先寫到cookie中,登錄后寫到數據庫表中);已登錄(直接寫到數據庫,而不會寫到cookie)實際項目是不使用session的,使用redis集中處理處理數據,取代session的作用,應用在單點登錄、購物車等。
25.你們使用什么做支付的?如果使用易寶做支付,請求超時了怎么處理?
①重試,一般三次,每次重試都要停頓一會,比如,以第一次停頓1秒,第二次停頓2秒,第三次停頓3秒;
②給訂單標識付款異常狀態,并且發出警告(郵件、短信)給相關人員。
③寫個定時任務,定時處理異常狀態的訂單。
26.你剛才不是說付款成功后易寶會有數據返回嗎?如果付款后易寶沒有返回,或者返回超時了,但是錢又已經扣了,你怎么辦?
①我們請求了易寶,但是沒有接受到響應,我們就認為該訂單沒有支付成功,并且將訂單標識為異常狀態;
②使用定時任務處理;
③做一個對賬的任務,實時處理異常狀態的訂單。
27.你們怎么做退款功能的,要多長時間才能把錢退回給用戶?
用戶申請退款后,經過客服審核通過會將退款請求提交到易寶,具體到賬時間要看易寶的處理。
28.你前臺portal采用4臺服務器集群部署?那你數據庫有幾臺服務器?如果前臺高并發訪問性能提上去了,那數據庫會不會造成一個瓶頸,這一塊你是怎么處理的?
?
29.你購物車存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,這是就能成功訪問了。
30.點一個鏈接訪問到一個頁面,這個頁面上既有靜態數據,又有動態數據(需要查數據庫的),打開這個頁面的時候就是很慢但是也能打開。怎么解決這個問題,怎么優化?
如果要靜態頁面的話 那就得用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操作, 將緩存的頁面放在內容中.
31.如果用戶一直向購物車添加商品怎么辦?并且他添加一次你查詢一次數據庫?互聯網上用戶那么多,這樣會對數據庫造成很大壓力你怎么辦?
在回答這個問題前,請想好自己的項目是否真的需要使用購物車?(SKU數少,商品結構單一等就不需要使用購物車了)
購物車的設計方案:
購物車的實現不存在哪種方式更好,完全是根據公司和項目架構相關的,類似蘇寧使用的是數據庫存儲,但是國美使用的就是Session,不同的軟件架構和不同的業務需求對應的購物車存儲也是不一樣的
用數據庫存你得給數據庫造成多大的負擔啊, 而且對于購物車, 這種需要實時操作的東西, 數據庫的訪問量一大了, 就容易出現并發錯誤, 或者直接崩潰.
用Session確實效率很高, 而且會話是針對各個連接的, 所以便于管理, 但是用Session也不是完美的, 因為Session是有有效期的, 根據服務器的設置不同而不一樣長, 如果你在購物的過程中Session超時了, 那么購物車中的東西就會全沒了.不知道你看過當當網的購物車沒有, 當你下線之后, 再次上線, 購物車中的東西還是存在的, 這對于用戶來說非常方便.所以如果你的服務器夠強的話, 你完全可以用一個靜態變量來保存所有用戶的購物車, 比如用一個靜態的Map, 以IP作為Key,區分不同用戶的購物車, 這樣就可以使用戶在下線的情況下也可以保存購物車中的內容.這種方法實現過, 只是沒有用大量的并發訪問測試其穩定性, 但是一定是可行的。?
采用存儲過程將購物車存儲于數據庫相應表的方式,優點:數據穩定,不易丟失。缺點:效率低,增加數據庫服務器負擔。變量 + Datatable保存于客戶端,優點:效率高,減輕數據庫服務器負擔。缺點:Session保存的變量容易丟失,但是一般情況下不會造成影響。變量 + 購物車對象保存于客戶端,這種方式以面向對象為指導思想,邏輯上具有一定的復雜性。優點:效率高,減輕數據庫服務器負擔,使用便捷。缺點:Session保存的變量容易丟失,但是一般情況下不會造成影響
購物車的核心功能
?
購物車數據存數據庫好處有很多,可以分析購買行為,可以為客戶保存購買信息(不會因為瀏覽器關閉而丟失)等,我的這個項目的購物車使用的就是將購物車數據存數據庫中,未登錄時可以加20個商品,登錄后可以加50個。
32.做促銷時,商品詳情頁面的靜態頁面如何處理價格問題。
京東商品詳情頁雖然僅是單個頁面,但是其數據聚合源是非常多的,除了一些實時性要求比較高的如價格、庫存、服務支持等通過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部分不做實現。
33.商品搜索框的搜索聯想如何實現?比如輸入“羽絨” ,然后輸入框下會列出很多關于羽絨服的搜索條件?“羽絨服男正品折扣?”等等。
?
?
?
34.一個電商項目,在tomcat里面部署要打幾個war包?
?
?
?
35.你說你用了redis緩存,你redis存的是什么格式的數據,是怎么存的?
?36.購物車知識補充(在設計購物車時需要注意哪些細節)
為什么購物車的設計很重要?
①購物車是消費的最后一環
購物車在用戶整體消費過程中一般是在最后一環,用戶完整的消費體驗應該是:打開APP或網站->瀏覽商品->加入購物車->確認訂單并支付,在這個過程中,購物車和支付環節可以合并成一環,基本上用戶點開購物車并開始填寫地址的時候,就有很大的幾率要完成購買,做好商品展現以及推送的環節,如果在最后的購物一環沒有好的用戶體驗,豈不嗚呼哀哉。
②購物車隱含的對比收藏功能
與現實購物車不同的是,網絡消費者也比較喜歡把看中但不計劃買的商品先放入購物車,或者把商品統一放到購物車直接進行比較,以備日后購買,因此從購物車保存的信息,就能夠知道用戶的大致偏好。
③購物車的重交易屬性
用戶在瀏覽商品涉及的只是前端展示,但購物車這一環涉及到最終的交易,對于用戶來說,需要了解本次交易的基本物品信息、價格信息;而對于商戶來說,確認收款、訂單生成、物流環節都需要在這里獲取到信息,才能完成本次的交易。
購物車設計需要展示的基本信息
購物車主要作用就是告訴用戶買了什么,價格多少,不同類型的物品可能會有不同展示方式,但最基本的包括商品名稱、價格、數量(若是服務,可能是次數)、其他附屬信息。
哪些細節要讓用戶買得舒服?
親,記得前面說的用戶是如何看待購物車的功能嗎?還記得你的用戶會多次使用購物車,如果你只是完整做好信息展示不做好其他事情真的好嗎?
①登錄環節不要放在加入購物車前
請讓用戶先加入購物車,并在進行結算的時候在提醒用戶需要登錄。為什么?過早提醒用戶需要登錄才能購買,會打斷用戶瀏覽的流程(用戶可能還要購買其他物品好嗎?)這樣的設置會讓部分用戶避而遠之。
這里涉及到的一個點是在APP端需要記憶用戶加入購物車的信息,與登錄后的購物車信息合并(如果一開始沒有這樣考慮好,技術那可能會有難度)
②自動勾選用戶本次挑選的商品
用戶使用購物車有一個大的作用就是收藏,所以你要知道很多用戶在購物車中積累了很多物品,當每次挑選加入購物車的商品,用戶每次來到購物車要重新把本次的購買商品選上是很不好的體驗。
所以這里一般是自動勾選本次挑選的商品,同樣這里也要儲存用戶的勾選信息。
③陳列展示,注意沉底商品
讓用戶看見當前想買的商品就好了,把一些時間久遠的,已經賣完的沉底顯示。這樣做的好處是能讓用戶看見之前的選擇但沒購買的商品,提醒一下說不定就又勾上買了哦!
④歸類展示,可能增加購買
考慮如何進行歸類展示,C2C可以按照商家分類,B2C可以按照品牌分類。
⑤價格和優惠的提醒
消費用戶會關系自己每一次的消費價格,為避免商品列表過長隱藏價格信息,APP端一般會把總價固定底部提示。同時在合計信息中,展示優惠價格,能夠促進消費者購買。
?
哪些細節要推動用戶繼續購買?
①還差一點就可以有優惠啦!
湊單,常用的手段包括運費見面或是滿減促銷,一般在網站底部會展示一些適合湊單的商品;在APP端可以給鏈接(不過需要權衡用戶跳轉會不會再跳回來哦!)
②提醒用戶有些商品你真的可以買了
有關調查顯示,加入購物車而沒有購買的,在4小時以內提醒用戶,會有27%的喚醒率哦!
所以需要提醒的幾個點有:
- 生成訂單但是還沒支付的
- 商品有優惠信息
- 商品庫存不足的
這些信息可以促進消費者購買,注意提醒的時間段,早上9點至晚上8點為宜,其他時間段就可能打擾用戶咯(當然也要視產品類型而定啦,只不過大半夜提醒用戶買東西確實不好,不是?)
?
?
?
?
?
?
?
?
?
?
?
?
?
總結
- 上一篇: Lync-用户-电话号码-更新
- 下一篇: squid 服务器的应用