1 學成在線是個什么樣的項目?
1.1 項目背景
學成在線借鑒了MOOC(大型開放式網絡課程,即MOOC(massive open online courses))的設計思想,是一
個提供IT職業課程在線學習的平台,它為即將和已經加入IT領域的技術人才提供在線學習服務,用戶通過在線學
習、在線練習、在線考試等學習內容,最終掌握所學的IT技能,並能在工作中熟練應用。
當前市場的在線教育模式多種多樣,包括:B2C、C2C、B2B2C等業務模式,學成在線采用B2B2C業務模式,即向
企業或個人提供在線教育平台和學生完成教學活動,市場上類似的平台有:網易雲課堂、騰訊課堂等,學成在線的
特點是IT職業課程在線教學。
1.2 項目的功能模塊
學成在線是一個在線教育平台,提供IT職業課程在線學習,平台包括:門戶、學習中心、教學管理中心、系統管理
中心、社交系統等子系統。
項目的功能架構如下圖:
門戶是整個平台的入口,功能包括:門戶首頁、注冊/登錄、課程搜索、職業規划,客服等。
學習中心為用戶提供在線學習服務,包括:我的課程、視頻點播、視頻直播、在線考試、在線答疑、學習統計等功
能;
教學管理中心為教育機構或個人講師提供教學管理功能,包括:課程管理、媒資管理、考試管理、問答管理等功
能;
系統管理中心提供系統參數配置、CMS、數據字典、分類管理等功能。
1.3 項目的技術架構
項目采用前后端分離的技術架構,前端采用vue.js技術棧,服務端采用SpringBoot、SpringCloud等Spring全家桶
技術棧。
具體見問題2.
2 項目采用什么技術架構?
項目采用前后端分離的技術架構,前端采用vue.js構建,服務端采用Spring Cloud微服務架構,系統分為用戶層、
CDN、負載均衡、前端UI、微服務層、數據層、接口層及DevOps等部分組成,下圖是完整的技術架構圖:
業務流程舉例:
1 、用戶可以通過pc、手機等客戶端訪問系統進行在線學習。
2、 系統應用CDN技術,對一些圖片、CSS、視頻等資源從CDN調度訪問。
3、所有的請求全部經過負載均衡器。
4、對於PC、H5等客戶端請求,首先請求UI層,渲染用戶界面。
5、客戶端UI請求服務層獲取進行具體的業務操作。
6、服務層將數據持久化到數據庫。
下圖是技術架構簡圖:
1、用戶層
用戶層描述了本系統所支持的客戶端用戶有哪些,本項目目前為各用戶提供服務,包括H5、PC、Android和IOS
等。
2 CDN全稱Content Delivery Network,即內容分發網絡,本系統所有靜態資源全部通過CDN加速來提高訪問速
度。系統靜態資源包括:html頁面、js文件、css文件、image圖片、pdf和ppt及doc教學文檔、video視頻等。
3 負載均衡 系統的CDN層、UI層、服務層及數據層均設置了負載均衡服務,系統采用LVS+Nginx實現負載均衡均
衡。
4 UI層 UI層描述了系統向pc用戶、app用戶、h5用戶提供的產品界面。本項目在PC和H5端采用vue.js+elementUI
實現。
5 微服務層將系統服務分類三類:前端服務、后端服務及系統服務。 前端服務:主要為學習用戶提供學習服務。
后端服務:主要為管理用戶提供教學管理服務。 系統服務:公共服務,為系統的所有微服務提供公共服務功能。
6 外部系統接口 包括如下接口:
1)第三方登錄接口,如QQ、微博、微信等。 2)支付寶、微信支付接口 3)短信接口 (阿里大於)4)郵件接
口,通過smpt郵件服務器對外發送電子郵件。 5)微信公眾號 6)點播、直播。 7)OSS存儲 8)CDN,使用最里
雲CDN服務加速視頻訪問速度。
7 DevOps提供了本系統開發、運營、維護支撐的系統,包括如下內容:
Eureka服務治理中心:提供服務治理服務,包括:服務注冊、服務獲取等。
Docker容器化部署服務:將本系統所有服務采用容器化部署方式。
Maven項目管理工具:提供管理項目所有的Java包依賴、項目工程打包服務。
Git/GitLab代碼管理服務:提供git代碼管理服務。
Spring Boot Admin服務健康監控:監控微服務的健康狀態、會話數量、並發數等。
2.1 微服務技術棧
所有微服務基於Spring Boot、Spring Cloud構建
1)控制層:
Spring MVC、Spring Security Oauth2 、Swagger
2)業務層:
事務控制:Spring
任務處理:Spring Task
數據緩存:Spring Data Redis
消息隊列:Spring RabbitTemplate
搜索: Elasticsearch
3) 持久層:
操作MySQL:MyBatis、com.alibaba.druid(采用druid-spring-boot-starter)Spring Data Jpa
操作MongoDB:Spring Data Mongodb
4) 數據層,采用MySQL和MongoDb存儲數據,MySQL存儲用戶、課程等系統核心信息,MongoDb存儲
cms、配置信息等。
2.2 接口定義規范
項目架構設立接口層,接口層使用swagger注解描述接口的內容,接口定義規范如下:
1、請求
get 請求時,前端請求key/value串,SpringMVC采用基本數據類型(String、Integer等)或自定義類型接收。
Post請求時,前端請Form表單數據(application/x-www-form-urlencoded)和Json數據(Content-
Type=application/json)、多部件類型數據(multipart/form-data),對於Json數據SpringMVC使用
@RequestBody注解解析請求的json數據。
2、響應
響應結果統一信息為:是否成功、操作代碼、提示信息及自定義數據。
響應結果統一格式為json。
2.3 注冊中心
兩台Eureka Server互相注冊,組成高可用。
微服務向Eureka Server注冊自己,並在遠程調用時從微服務發現目標服務地址。
微服務遠程調用采用客戶端負載均衡技術,使用Feign Client。
2.4 網關
網關的作用是負載均衡、路由轉發、請求過慮等。
項目中網關與Nginx配合使用。
2.5 項目使用Spring了嗎?用了它的哪些東西?
項目是基於Spring進行構建的:
1 、所有的微服務開發采用Spring Boot開發
2、數據層使用Spring Data JPA、Spring Data MongoDB、Spring Data redis。
3、業務層使用Spring來控制本地事務,還使用了Spring Task任務調度框架、Spring AMQP組件等。
4、控制層使用SpringMVC、Sprnig Security Oauth2。
5、微服務管理使用Spring Cloud的Eureka注冊中心,微服務之間調用使用Ribbon和Feign Client完成。
6、使用Zuul網關完成微服務安全驗證
2.6 Spring Cloud 是怎么使用的?
1、每個微服務使用Spring Boot開發,每個微服務工程包括了web、service、dao三層,這和開發一般的項目沒有
區別:
a、web層使用Spring MVC實現,對外暴露API接口給前端調用。
b、service層就是根據業務邏輯編寫JavaBean,並使用Spring的聲明式事務控制方式來控制事務。
c、dao層就是數據訪問接口,來訪問MySQL和Mongodb,訪問MySQL使用Spring Data JPA和Mybatis,訪問
mongodb使用Spring data mongodb。
2、微服務開發完成要向Eureka注冊中心注冊,以便被其它微服務查找和訪問。
3、微服務與微服務之間使用feign來調用,feign Client具有負載均衡的作用。只需要在接口上聲明@FeignClient注
解,Spring底層會產生動態代理對象,使用ribbon客戶端完成調用。
4、前端訪問微服務需要通過網關,網關使用Nginx和Zuul來實現,Nginx是最前邊的負載均衡,通過Nginx之后便
到達了Zuul,項目中Zuul的功能是過慮用戶請求,判斷用戶身份,對於一些對外公開的微服務則需要經過Zuul,直
接通過Nginx負載均衡即可訪問。
2.7 Spring Data JPA 和 MyBatis為什么兩個都用?具體怎么用的?
項目中使用Spring Data JPA和MyBatis都是用來訪問MySQL,但是它們的分工不同:
Spring Data JPA是Spring 提供的一套JPA接口,使用Spring Data JPA主要完成一些簡單的增、刪、改、查功能。
對於復雜的查詢功能會使用MyBatis編寫SQL語言來實現,因為使用Spring Data JPA來做一些復雜的查詢是沒有
MyBatis方便的,Spring Data JPA是面向的對象,而MyBatis直接面向SQL語句。
2.8 什么雪崩?如何解決?
容錯保護是指微服務在執行過程中出現錯誤並從錯誤中恢復的能力。微服務容錯性不好很容易導致雪崩效應,什么
是雪崩效應?
微服務的雪崩效應表現在服務與服務之間調用,當其中一個服務無法提供服務可能導致其它服務也死掉,比如:單
點登錄服務調用用戶信息服務查詢用戶信息,由於用戶信息服務無法提供服務導致單點登錄服務一直等待,從而導
致用戶登錄、用戶退出功能無法使用,像這樣由一個服務所引起的一連串的多個服務無法提供服務即是微服務的雪
崩效應。
Spring Cloud Hystrix 是基於Netflix的開源框架Hystrix的整合,它實現了斷路保護、線程隔離、信號隔離等容錯
功能。
斷路保護:
斷路保護就類似家庭電路中的保險絲,當電路過載時保險絲會自動切斷,保護整個電路的安全。微服務的斷路保護
的工作原理是當請求微服務失敗的數量達到一定比例時會切換為開路狀態,當請求微服務時就直接返回結果不再請
求微服務,當保持開路狀態一段時間后判斷微服務是否可以正常請求,如果正常則切換到半開路狀態,最后切換到
哪閉路狀態。
具體的操作方法可以采用 Fallback,會每個FeignClient方法調用Fallback,當出現開路則調用Fallback方法返回錯
誤結果。
線程隔離:
調用微服務使用不同的線程池,線程池之間互不影響,即使某個服務不可用也不影響其它服務的調用,比如:對商
品服務的調用使用一個線程池,對用戶服務的調用使用另一個線程池,即使用戶服務不可用也不影響商品服務的調
用。
2.9 視圖層用什么技術實現?
1、視圖層在前端和服務端都存在。
2、前端視圖采用vue.js+elementUI產品界面。
3、服務端都是暴露的rest接口,統一用json展示數據。
2.10 接口是怎么定義的?采用什么數據格式?如何實現?
本問題考察前后端分離開發中接口定義技能。
1、接口定義
使用 SpringMVC編寫Controller方法,對外暴露Http接口,在Controller方法上使用RequestMapping、
PostMapping、GetMapping等注解定義Http接口。
2、采用什么數據格式?
分別說明請求和響應:
請求:
get 請求時,前端請求key/value串,SpringMVC采用基本數據類型(String、Integer等)或自定義類型接收。
Post請求時,前端請Form表單數據(application/x-www-form-urlencoded)和Json數據(Content-
Type=application/json)、多部件類型數據(multipart/form-data),對於Json數據SpringMVC使用
@RequestBody注解解析請求的json數據。
響應:
統一響應json格式。
3、如何實現的?
json格式數據SpringMVC采用FastJson解析為對象。
非json格式數據SpringMVC提供參數綁定的方法,將key/value或Form-Data數據轉換為對象或基本數據類型的變
量。
3 前后端開發時具體流程是什么?
1、前端與后端開發人員討論確定接口。
接口討論通過,形成接口文檔 。
本項目專門設立一個api工程,在此工程定義接口,Spring Boot 集成Swagger,生成Swagger接口,前后端 開發
人員通過html查看接口文檔的內容。
2、前端與后端開發人員按照接口文檔進行開發。
開發過程中各自進行單元測試。
3、雙方功能開發完成進行前后端聯調。
3.1 前端采用什么技術棧?
前端工程大多為單頁面應用(SPA),采用vue.js框架開發,搜索功能前端采用nuxt.js服務端渲染(SSR)框架開
發。
技術棧包括:
1、node.js
2、vue.js
3、npm/cnpm
4、webpack
5、axios
6、nuxt.js
4 CMS 頁面管理
CMS 即內容管理系統,本項目對CMS系統的定位是對各各網站(子站點)頁
面的管理,本項目的CMS系統不去管理每個子網站的全部資源,比如:圖片、CSS、html頁面等,主要管理由於運
營需要而經常變動的頁面,從而滿足根據運營需要快速開發、上線的需求。
功能包括:
1、站點管理,站點就是本項目各各子網站,站點信息包括:站點名稱、站點域名、端口、服務器物理路徑等。
2、模板管理,由於要對頁面進行靜態化,使用freemarker引擎技術,所以需要定義模板。
3、頁面管理,包括:頁面添加、頁面修改、頁面刪除等操作。
4、頁面預覽,對頁面靜態化,在瀏覽器預覽頁面靜態化內容。
5、頁面發布,將頁面靜態化后發布到所屬站點服務器。
4.1 GirdFS 是什么?工作原理是什么?如何使用?
GridFS是MongoDB提供的用於持久化存儲文件的模塊,它可以作為分布式文件系統使用,CMS子系統將頁面文
件、模板文件存儲到GridFS中,由於本項目使用MongoDB,選用GridFS可以快速集成開發。
它的工作原理是:
在GridFS存儲文件是將文件分塊存儲,文件會按照256KB的大小分割成多個塊進行存儲,GridFS使用兩個集合
(collection)存儲文件,一個集合是chunks, 用於存儲文件的二進制數據;一個集合是files,用於存儲文件的元數
據信息(文件名稱、塊大小、上傳時間等信息)。
從GridFS中讀取文件要對文件的各各塊進行組裝、合並。
使用方法是:
使用Spring data mongodb包下提供的GridFsTemplate訪問GirdFS。
gridFsTemplate.findone() 查詢文件 gridFsTemplate.delete() 刪除文件 gridFsTemplate.store()存儲文件
4.2 MQ 是怎么使用的?
1、平台包括多個站點,頁面歸屬不同的站點,需求是發布一個頁面應將該頁面發布到所屬站點的服務器上。
2、每個站點服務部署CMS Client程序,並與交換機綁定,綁定時指定站點Id為routingKey。
指定站點id為routingKey就可以實現cms client只能接收到所屬站點的頁面發布消息。
3、頁面發布程序向MQ發布消息時指定頁面所屬站點Id為routingKey,根據routingKey將消息發給指定的
CMS Client。
4.3 頁面發布的結果如何收集?
每次發布會在數據庫記錄發布日志,每個CMS Client完成頁面發布會上報發布結果。
1、在站點管理中配置了每個站點的服務器信息
2、在每次發布頁面時會記錄發布日志(服務器ID、頁面ID、頁面名稱、發布結果)
3、CMS Client完成頁面發布后會向數據上報發布結果。
4、用戶通過查詢發布日志表的信息就可以知道每一次的發布結果(哪些服務器頁面發布成功,哪些發布失敗)。
5 課程管理
5.1 為什么用多張表存儲課程信息?
1、課程信息比較復雜,為了方便教學機構按步驟管理課程信息,並且也可以划分權限管理課程信息,將課程信息
管理功能分為課程基本信息管理、課程圖片管理、課程營銷信息管、課程計划管理等模塊。
2、將課程信息分開也是為了系統擴展需要,如果將課程所有信息存儲在一張表中將不利於系統擴展。
5.2 課程圖片是如何管理的?
見問題“圖片服務器”。
6 媒資管理
每個教學機構都可以在媒資系統管理自己的教學資源,包括:視頻、教案等文件。
媒資管理的主要管理對象是課程錄播視頻,包括:媒資文件的查詢、視頻上傳、視頻刪除、視頻處理等。
媒資查詢:教學機構查詢自己所擁有的媒體文件。
視頻上傳:將用戶線下錄制的教學視頻上傳到媒資系統。
視頻處理:視頻上傳成功,系統自動對視頻進行編碼處理。
視頻刪除 :如果該視頻已不再使用,可以從媒資系統刪除。
6.1 如何上傳大文件?
前端使用WebUploader將文件分塊,調用服務端分塊上傳接口上傳分塊文件,分塊上傳完畢前端請求服務端進行
合並,當上傳過程中斷再次進行上傳時服務端判斷分塊是否已經上傳,已經上傳的分塊不再重新上傳。
6.2 如何進行視頻處理?
上圖所示,Java程序調用ffmpeg及流媒體程序員提供的視頻處理類庫(C程序)完成avi、mp4視頻轉成m3u8格式
的視頻。
Java程序使用Jdk提供的Process Builder調用ffmpeg及C程序進行視頻處理。
Process Builder可以調用第三方程序,在java程序運行時啟動第三方程序進程。
視頻處理完成,Java程序捕獲第三方程序的輸出日志,解析出視頻處理完成標記,更新視頻處理狀態為已完成。
6.3 CDN 內容分發是什么?
視頻處理完成會在中心媒體服務器保存一份,另外通過CDN程序將視頻發布到邊緣媒體服務器,用戶點播視頻通
過CDN請求邊緣媒體服務器中的視頻,提高了視頻播放速度。
具體使用的是第三方公司的CDN服務。
7 搜索
項目中課程搜索采用ElasticSearch來完成。
實現方法是:
1、使用 Logstash(logstash是ES下的一款開源軟件,它能夠同時 從多個來源采集數據、轉換數據)將MySQL中
的課程信息讀取到ES中創建索引,使用IK分詞器進行分詞。
2、使用 Java High Level REST Client完成搜索。
3、生產環境使用ES部署為集群。
8 圖片服務器
本項目采用fastDFS分布式系統作為圖片服務器。
FastDFS是用c語言編寫的一款開源的分布式文件系統,適合小文件的存儲。
FastDFS包括 Tracker server和Storageserver。客戶端請求Tracker server進行文件上傳、下載,通過Tracker
server調度向Storage server完成文件上傳和下載。
使用FastDSF官方提供的Java API實現。
圖片服務使用Nginx作為代理服務器,對Storage上部署的Nginx完成負載均衡請求。
8.1 使用FastDFS的好處是什么?
FastDFS相比其它的分布式文件系統它適用小文件存儲,它不對文件進行分塊存儲,也不用對文件進行合並處理,
所以性能比GFS、HDFS等通用文件系統的性能要高。
8.2 圖片上傳流程?
執行流程如下:
1 、管理員進入教學管理前端,點擊上傳圖片
2、圖片上傳至文件系統服務,文件系統請求fastDFS上傳文件
3、文件系統將文件入庫,存儲到文件系統服務數據庫中。
4、文件系統服務向前端返回文件上傳結果,如果成功則包括文件的Url路徑。
5、課程管理前端請求課程管理進行保存課程圖片信息到課程數據庫。
6、課程管理服務將課程圖片保存在課程數據庫。
8.3 FastDFS 支持斷點續傳嗎?
FastDFS支付斷點續傳,在Api中有append_file1方法就是用來實現斷點續傳的,本項目沒有使用FastDFS的斷點續
傳功能。
9 認證授權是如何實現的?
本項目采用 Spring security + Oauth2完成用戶認證及用戶授權。認證授權流程如下:
1、用戶請求認證服務完成身份認證。
2、認證服務下發用戶身份令牌和JWT令牌,擁有身份令牌表示身份合法,Jwt令牌用於完成授權。
3、用戶攜帶jwt令牌請求資源服務。
4、網關校驗用戶身份令牌的合法,不合法表示用戶沒有登錄,如果合法則放行繼續訪問。
5、資源服務獲取jwt令牌,根據jwt令牌完成授權。
10 事務是怎么控制的?用到分布式事務控制了嗎?
1、在微服務中使用Spring 聲明式事務控制方式進行控制,在Service方法上添加@Transctional注解即可實現事務
控制,它控制的是MySQL的本地事務。
2、項目中大量存在分布式事務控制,比如下單支付、課程發布等地址都用到了分布式事務。
本項目實現分布式事務控制實現最終數據一致性,做法是:
a、將分布式事務拆分為多個本地事務。
b、提交事務前每個參與者要通過數據校驗,和資源預留。
c、由消息隊列去通知多個事務參與者完成本地事務的提交。
d、提交失敗的本地事務會重試。
11 一個接口出現Bug你是怎么調試的?
1、接口的開發需要前端和服務端共同調試,要仔細閱讀測試人員反映的bug信息,判斷這個bug是服務端的bug還
是前端的bug。通常服務接口開發完成會使用postman工具進行測試,測試沒有問題再提交到Git或SVN。
2、找到bug的出錯點就可以根據bug信息進行修改。
3、修改完成需要前后端再次連調測試,按照測試人員提交的測試流程重新進行測試,測試通過將此bug置為已解
決。
12 做過支付接口嗎?你是如何做的?遇到什么問題?
如果做過支付接口則要回答實現過程:
1、系統中收費的課程需要用戶在線支付,支付接口采用微信的掃碼支付。
2 、拿到需求后,確定使用微信支付,首先去閱讀微信的接口文檔,這里重點閱讀統一下單、支付結果通知、支付
結果查詢三個接口。
3、下載官方提供的sdk編寫單元測試用例測試每個接口。測試時沒有使用微信的沙箱測試,直接使用正式接口,我
們將金額改的小一些進行測試。
4、單元測試通過后開發整個支付功能,最終集成測試通過。
根據自己的實際情況回答開發中遇到的問題:
例子:
接口參數的簽名問題,當時是因為自己沒有仔細看接口文檔導致少寫一個必填參數一直報簽名失敗,隨后將所有必
填參數填寫完成,最終解決問題。
13 系統的異常是怎么處理的?
系統對異常的處理使用統一的異常處理流程。
1、自定義異常類型。
2、自定義錯誤代碼及錯誤信息。
3、對於可預知的異常由程序員在代碼中主動拋出自定義異常類型的異常,拋出異常時需要指定錯誤代碼。
4、對於不可預知的異常(運行時異常)由SpringMVC統一捕獲Exception類型的異常,由統一的異常捕獲類來解析
處理,並轉換為與自定義異常類型一致的信息格式(錯誤代碼+錯誤信息)。
5、可預知的異常及不可預知的運行時異常最終會采用統一的信息格式(錯誤代碼+錯誤信息)來表示,最終也會隨
請求響應給客戶端。
14 如何使用消息隊列的?有哪些應用場景?
項目使用RabbitMQ消息隊列。
RabbitMQ提供很多的工作模式,如下:
1、Work queues
2、Publish/Subscribe
3、Routing
4、Topics
5、Header
6、RPC
項目主要使用了Routing模式。
Routing模式即路由模式,使用方法是:
1、每個消費者監聽自己的隊列,並且設置routingkey。
2、生產者將消息發給交換機,由交換機根據routingkey來轉發消息到指定的隊列。
有哪些應用場景?
1 、任務異步處理。
將不需要同步處理的並且耗時長的操作由消息隊列通知消息接收方進行異步處理。提高了應用程序的響應時間。
2、應用程序解耦合
MQ相當於一個中介,生產方通過MQ與消費方交互,它將應用程序進行解耦合。
15 視頻點播功能是怎么實現的?
本項目采用 HLS 技術實現視頻點播。
1、使用FFmpeg對視頻進行編碼處理,生成m3u8文件及ts文件。
2、使用Nginx作為媒體服務器。
3、客戶端使用video.js播放視頻。
16 你在開發中遇到什么問題?是怎么解決的?
例子:
在處理訂單時要用到定時任務,當時采用的是Spring Task來完成,由於一個訂單服務會部署多個,多個訂單服務
同時去處理任務會造成任務被重復處理的情況,如何解決任務的重復處理。
解決:
采用樂觀鎖解決,在任務表中設置一個version字段記錄版本號,取出任務記錄同時拿到任務的版本號,執行前對
任務進行鎖定,具體的做法是執行update根據當前版本號將版本號加1,update成功表示鎖定任務成功,即可開始
執行任務。
展望
大概花了一個多月來做這個項目,感觸很深,了解了微服務在企業的開發流程,從需求分析丶環境搭建丶數據庫設計丶服務端開發丶前端丶測試丶部署等流程。雖然這個項目是2018年的,但很多技術都不算落后,用來做為微服務項目經驗足以。做這個項目也遇到很多bug,明明按照視頻文檔一步一步的做,有時得不到自己想要的結果,然后就不停的調試,卸載重裝。一個問題可能做一下午也解決不了,有時會崩潰,這時候就做其他事情分散注意力,比如洗個澡清醒一下再戰。不要灰心喪氣,急躁,當你解決了的就會有成就感,即使解決不了也盡力了。這就是我們程序員的代碼精神吧。
資料
后台代碼:
https://github.com/Anmaking/xcEduService01.git
配套視頻課件:
鏈接:https://pan.baidu.com/s/1B4xXEYVgJh0A4j2IM2BAig
提取碼:48nn