Spring Cloud 項目總結

      網友投稿 798 2025-03-31

      Spring Cloud + Vue 前后端分離 開發企業級在線視頻課程系統


      代碼:https://gitee.com/hiszm/online-course

      文章目錄

      總體架構

      技術選型

      代碼部分

      system

      gateway

      server

      generator

      mybatis

      file

      加密視頻

      授權播放

      總體架構

      技術選型

      代碼部分

      admin -- web后臺管理頁面 business -- 核心模塊 doc/db -- 數據庫文件 eureka -- 注冊中心 file -- 核心模塊 gateway -- 網關 generator -- 代碼生成器 server -- 公共模塊 system -- 核心模塊 web -- web首頁

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      權限管理

      角色管理

      SpringCloud的網關組件可以用gateway或zuul;最早使用的是zuul,后面spring自己出了gateway

      網關主要功能:

      限流(流量控制);

      重試(請求失敗時重試,慎用);

      跨域(前后端不在同一個域);

      路由(轉發請求);

      鑒權(登錄校驗,簽名校驗)等…

      單點登錄

      IP-HASH

      A:request.getSession()可以訪問這片空間

      B:節點中,沒有經歷過之前的登錄,Session中沒有保存登錄用戶信息,于是B節點會認為未登錄過,會攔截掉業務請求

      缺點,當其中一個節點宕機,則該節點下的用戶需要重新登錄;另個缺點是對SLB不能靈活的配置流量,比如A這臺機器性能好一 點,可以分配流量高一-些

      服務器負載均衡(Server Load Balancing)

      種類

      基于DNS負載均衡

      其原理就是當用戶訪問域名的時候,會先向 DNS 服務器去解析域名對應的 IP 地址,這個時候我們可以讓 DNS 服務器根據不同地理位置的用戶返回不同的 IP。比如南方的用戶就返回我們在廣州業務服務器的 IP,北方的用戶來訪問的話,我就返回北京業務服務器所在的 IP。

      用戶就相當于實現了按照「就近原則」將請求分流了

      優點

      使用 DNS 做負載均衡的方案,天然的優勢就是配置簡單,實現成本非常低,無需額外的開發和維護工作。

      缺點

      但是也有一個明顯的缺點是:當配置修改后,生效不及時。這個是由于 DNS 的特性導致的,DNS 一般會有多級緩存,所以當我們修改了 DNS 配置之后,由于緩存的原因,會導致 IP 變更不及時,從而影響負載均衡的效果。

      另外,使用 DNS 做負載均衡的話,大多是基于地域或者干脆直接做 IP 輪詢,沒有更高級的路由策略,所以這也是 DNS 方案的局限所在。

      基于硬件負載均衡

      比如大名鼎鼎的 F5 Network Big-IP,也就是我們常說的 F5,它是一個網絡設備,你可以簡單的理解成類似于網絡交換機的東西,完全通過硬件來抗壓力,性能是非常的好,每秒能處理的請求數達到百萬級,即 幾百萬 / 秒 的負載,

      當然價格也就非常非常貴了,十幾萬到上百萬人民幣都有。

      基于軟件負載均衡

      軟件負載均衡是指使用軟件的方式來分發和均衡流量。

      軟件負載均衡,分為 7 層協議 和 4 層協議。

      網絡協議有七層,

      基于第四層傳輸層來做流量分發的方案稱為 4 層負載均衡,例如 LVS,

      基于第七層應用層來做流量分發的稱為 7 層負載均衡,例如 Nginx。

      這兩種在性能和靈活性上是有些區別的

      基于 4 層的負載均衡性能要高一些,一般能達到 幾十萬 / 秒 的處理量,

      基于 7 層的負載均衡處理量一般只在 幾萬 / 秒 。

      基于軟件的負載均衡的特點也很明顯,便宜。

      在正常的服務器上部署即可,無需額外采購,就是投入一點技術去優化優化即可,因此這種方式是互聯網公司中用得最多的一種方式。

      均衡算法

      NO.1—— Random 隨機

      這是最簡單的一種,使用隨機數來決定轉發到哪臺機器上。

      優點:簡單使用,不需要額外的配置和算法。

      缺點:隨機數的特點是在數據量大到一定量時才能保證均衡,所以如果請求量有限的話,可能會達不到均衡負載的要求。

      NO.2—— Round Robin 輪詢

      這個也很簡單,請求到達后,依次轉發,不偏不向。每個服務器的請求數量很平均。

      缺點:當集群中服務器硬件配置不同、性能差別大時,無法區別對待。引出下面的算法。

      NO.3—— Weighted Round Robin 加權輪詢

      這種算法的出現就是為了解決簡單輪詢策略中的不足。在實際項目中,經常會遇到這樣的情況。

      比如有 5 臺機器,兩臺新買入的性能等各方面都特別好,剩下三臺老古董。這時候我們設置一個權重,讓新機器接收更多的請求。物盡其用、能者多勞嘛!

      這種情況下,“均衡 “就比較相對了,也沒必要做到百分百的平均。

      NO.4—— Least Connections 最少連接

      這是最符合負載均衡算法的一個。需要記錄每個應用服務器正在處理的連接數,然后將新來的請求轉發到最少的那臺上。

      NO.5—— Source Hashing 源地址散列

      根據請求的來源 ip 進行 hash 計算,然后對應到一個服務器上。之后所有來自這個 ip 的請求都由同一臺服務器處理。

      共享session

      單點登錄(Single SignOn ) ,

      簡稱為SSO,核心功能: session共享

      不管是在哪一-臺做的登錄,登錄完成后,會把登錄信息保存到redis中。

      當業務請求進來時,再到redis中獲取登錄信息,能獲取到就表示已登錄;未獲取到就表示未登錄,攔截掉請求

      需要解決Session共享的場景:

      1.同個應用多節點共享登錄信息;

      2.多個項目間共享登錄信息。一般我們通常說的單點登錄系統,是用來解決場景2的。

      system business file 三個是本項目的核心部分,依賴于server,本身是不會啟動,是我們的公共模塊

      server 所有的service層,持久層代碼,都放到server里面

      mybatis-generator

      持久層:負責數據持久化。即將數據存儲到數據

      庫或硬盤等,斷電也不會丟失數據。

      ORM : 對象關系映射

      Hibernate是全自動ORM

      Mybatis : 是半自動ORM , Mybatis可以操作的花樣更多,是首選的持久層框架,

      文件上傳

      // 上傳控件 // 調用函數 uploadImage () { let _this = this; let formData = new window.FormData(); // key:"file"必須和后端controller參數名一致 formData.append('file', document.querySelector('#file-upload-input').files[0]); Loading.show(); _this.$ajax.post(process.env.VUE_APP_SERVER + '/file/admin/upload', formData).then((response)=>{ Loading.hide(); let resp = response.data; }); // 后臺代碼 public class UploadController { private static final Logger LOG = LoggerFactory.getLogger(UploadController.class); public static final String BUSINESS_NAME = "文件上傳"; @RequestMapping("/upload") public ResponseDto upload(@RequestParam MultipartFile file) throws IOException { LOG.info("上傳文件開始:{}", file); LOG.info(file.getOriginalFilename()); LOG.info(String.valueOf(file.getSize())); // 保存文件到本地 String fileName = file.getOriginalFilename(); String key = UuidUtil.getShortUuid(); String fullPath = "D:/file/imooc/teacher/" + key + "-" + fileName; File dest = new File(fullPath); file.transferTo(dest); LOG.info(dest.getAbsolutePath()); ResponseDto responseDto = new ResponseDto(); return responseDto; } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      斷點續傳

      作為一個視頻網站, -一個文件小則幾十M ,大則上G ,上傳 一個大文

      件受網絡影響很大,文件越大, 上傳失敗率越高

      我們要完善文件上傳功能,支持斷點續傳,當文件上傳到一半,網絡

      斷了,下次再上傳時,只上傳剩下的部分,這就是斷點續傳。

      // 文件分片 let shardSize = 20 * 1024 * 1024; //以20MB為一個分片 let shardIndex = 1; //分片索引 let start = shardIndex * shardSize; //當前分片起始位置 let end = Math.min(file.size, start + shardSize); //當前分片結束位置 let fileShard = file.slice(start, end); //從文件中截取當前的分片數據 // key:"file"必須和后端controller參數名一致 formData.append('file', fileShard); formData.append('use', _this.use); Loading.show(); _this.$ajax.post(process.env.VUE_APP_SERVER + '/file/admin/upload', formData).then((response)=>{ Loading.hide(); let resp = response.data; console.log("上傳文件成功:", resp); _this.afterUpload(resp); $("#" + _this.inputId + "-input").val(""); }); },

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      @GetMapping("/merge") public ResponseDto merge() throws Exception { File newFile = new File(FILE_PATH + "/course/test123.mp4"); FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加寫入 FileInputStream fileInputStream = null;//分片文件 byte[] byt = new byte[10 * 1024 * 1024]; int len; try { // 讀取第一個分片 fileInputStream = new FileInputStream(new File(FILE_PATH + "/course/Bc0SXtFn.blob")); while ((len = fileInputStream.read(byt)) != -1) { outputStream.write(byt, 0, len); } // 讀取第二個分片 fileInputStream = new FileInputStream(new File(FILE_PATH + "/course/roQbPm2x.blob")); while ((len = fileInputStream.read(byt)) != -1) { outputStream.write(byt, 0, len); } } catch (IOException e) { LOG.error("分片合并異常", e); } finally { try { if (fileInputStream != null) { fileInputStream.close(); } outputStream.close(); LOG.info("IO流關閉"); } catch (Exception e) { LOG.error("IO流關閉", e); } } ResponseDto responseDto = new ResponseDto(); return responseDto; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      升級

      將分片的記錄存到數據庫里面

      +alter table `file` add column (`shard_index` int comment '已上傳分片'); +alter table `file` add column (`shard_size` int comment '分片大小|B'); +alter table `file` add column (`shard_total` int comment '分片總數'); +alter table `file` add column (`key` varchar(32) comment '文件標識'); +alter table `file` add unique key key_unique (`key`);

      1

      2

      3

      4

      5

      使用文件生成md5簽名,作為文件標識

      /** * 10進制轉62進制 * @param number * @returns {string} * @private */ _10to62: function (number) { let chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'; let radix = chars.length; let arr = []; do { let mod = number % radix; number = (number - mod) / radix; arr.unshift(chars[mod]); } while (number); return arr.join(''); } //獲取文件的屬性 console.log(file); /* name: "test.mp4" lastModified: 1901173357457 lastModifiedDate: Tue May 27 2099 14:49:17 GMT+0800 (中國標準時間) {} webkitRelativePath: "" size: 37415970 type: "video/mp4" */ // 生成文件標識,標識多次上傳的是不是同一個文件 let key = hex_md5(file); let key10 = parseInt(key, 16); let key62 = Tool._10to62(key10); console.log(key, key10, key62); /* d41d8cd98f00b204e9800998ecf8427e 2.8194976848941264e+38 6sfSqfOwzmik4A4icMYuUe */

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      重構合并

      public void merge(FileDto fileDto) throws Exception { LOG.info("合并分片開始"); String path = fileDto.getPath(); //http://127.0.0.1:9000/file/f/coursesfSqfOwzmik4A4icMYuUe.mp4 path = path.replace(FILE_DOMAIN, ""); //coursesfSqfOwzmik4A4icMYuUe.mp4 Integer shardTotal = fileDto.getShardTotal(); File newFile = new File(FILE_PATH + path); FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加寫入 FileInputStream fileInputStream = null;//分片文件 byte[] byt = new byte[10 * 1024 * 1024]; int len; try { for (int i = 0; i < shardTotal; i++) { // 讀取第i個分片 fileInputStream = new FileInputStream(new File(FILE_PATH + path + "." + (i + 1))); // coursesfSqfOwzmik4A4icMYuUe.mp4.1 while ((len = fileInputStream.read(byt)) != -1) { outputStream.write(byt, 0, len); } } } catch (IOException e) { LOG.error("分片合并異常", e); } finally { try { if (fileInputStream != null) { fileInputStream.close(); } outputStream.close(); LOG.info("IO流關閉"); } catch (Exception e) { LOG.error("IO流關閉", e); } } LOG.info("合并分片結束"); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      上傳完畢后,合并文件后;刪除分片

      System.gc(); LOG.info("刪除分片開始"); for (int i = 0; i < shardTotal; i++) { String filePath = FILE_PATH + path + "." + (i + 1); File file = new File(filePath); boolean result = file.delete(); LOG.info("刪除{},{}", filePath, result ? "成功" : "失敗"); } LOG.info("刪除分片結束");

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      斷點檢查分片是否繼續上傳?

      /** * 檢查文件狀態,是否已上傳過?傳到第幾個分片? */ check (param) { let _this = this; _this.$ajax.get(process.env.VUE_APP_SERVER + '/file/admin/check/' + param.key).then((response)=>{ let resp = response.data; if (resp.success) { let obj = resp.content; if (!obj) { param.shardIndex = 1; console.log("沒有找到文件記錄,從分片1開始上傳"); _this.upload(param); } else { param.shardIndex = obj.shardIndex + 1; console.log("找到文件記錄,從分片" + param.shardIndex + "開始上傳"); _this.upload(param); } } else { Toast.warning("文件上傳失敗"); $("#" + _this.inputId + "-input").val(""); } }) },

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      public FileDto findByKey(String key) { return CopyUtil.copy(selectByKey(key), FileDto.class); }

      1

      2

      3

      極速秒傳

      極速秒傳,聽起來

      很高深,原理其實很簡單,就是開始.上傳前,先檢查一下文件是否上傳過了,如果已上傳過,直接彈出提示,極速秒傳成功。

      /** * 檢查文件狀態,是否已上傳過?傳到第幾個分片? */ check (param) { let _this = this; _this.$ajax.get(process.env.VUE_APP_SERVER + '/file/admin/check/' + param.key).then((response)=>{ let resp = response.data; if (resp.success) { let obj = resp.content; if (!obj) { param.shardIndex = 1; console.log("沒有找到文件記錄,從分片1開始上傳"); _this.upload(param); } else if (obj.shardIndex === obj.shardTotal) { // 已上傳分片 = 分片總數,說明已全部上傳完,不需要再上傳 Toast.success("文件極速秒傳成功!"); _this.afterUpload(resp); $("#" + _this.inputId + "-input").val(""); } else { param.shardIndex = obj.shardIndex + 1; console.log("找到文件記錄,從分片" + param.shardIndex + "開始上傳"); _this.upload(param); } } else { Toast.warning("文件上傳失敗"); $("#" + _this.inputId + "-input").val(""); } }) },

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      復制文件地址

      if (fileDto != null) { fileDto.setPath(FILE_DOMAIN + fileDto.getPath()); }

      1

      2

      3

      HTTP Live Streaming(縮寫是 HLS)是一個由蘋果公司提出的基于 HTTP 的流媒體網絡傳輸協議。是蘋果公司 QuickTime X 和 iPhone 軟件系統的一部分。它的工作原理是把整個流分成一個個小的基于 HTTP 的文件來下載,每次只下載一些。當媒體流正在播放時,客戶端可以選擇從許多不同的備用源中以不同的速率下載同樣的資源,允許流媒體會話適應不同的數據速率。在開始一個流媒體會話時,客戶端會下載一個包含元數據的 extended M3U (m3u8)playlist 文件,用于尋找可用的媒體流。 \ HLS 只請求基本的 HTTP 報文,與實時傳輸協議(RTP)不同,HLS 可以穿過任何允許 HTTP 數據通過的防火墻或者代理服務器。它也很容易使用內容分發網絡來傳輸媒體流。

      Spring Cloud 項目總結

      HLS 協議規定:

      視頻的封裝格式是 TS。

      視頻的編碼格式為 H264,音頻編碼格式為 MP3、AAC 或者 AC-3。

      除了 TS 視頻文件本身,還定義了用來控制播放的 m3u8 文件(文本文件)。

      #EXTM3U #EXT-X-VERSION:3 #EXT-X-KEY:METHOD=AES-128,URI="https://ipc-camera.fast-cn.wgine.com/api/cloud/key?devId=6c4db6784fffcc3892nxmp&magic=qKr6mxwDmUbJ8EYnHCdmAnD3488CsMaj",IV=0x7b84a718bbac5e2053d64b3295ca2dce #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-TARGETDURATION:10 #EXT-X-PROGRAM-DATE-TIME:2020-01-08T20:01:16.000+00:00 #EXTINF:10, bobf339vmg9aa815lus0zTCWOElTj5jd_0.ts?token=5160f1deec8742b4c5ecd9891f5bcf934290b7d4ea3f29284a1ebce9bab2efab #EXT-X-PROGRAM-DATE-TIME:2020-01-08T20:01:26.000+00:00 #EXTINF:10, bobf339vmg9aa815lus0zTCWOElTj5jd_1.ts?token=9588f1926541d09bbc06db2f9ffd944fa9b2f2062e9e4186938ed64e7869b5f3 #EXT-X-PROGRAM-DATE-TIME:2020-01-08T20:01:36.000+00:00 #EXTINF:10, #EXT-X-ENDLIST

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      2.1 EXTM3U 每個 M3U 文件第一行必須是這個 tag,起標示作用。 2.2 EXT-X-VERSION 用以標示協議版本。 2.3 EXT-X-KEY 這個標示了當前 M3U8 的解密方式。 2.4 EXT-X-MEDIA-SEQUENCE 每一個 media URI 在 PlayList 中只有唯一的序號,相鄰之間序號+1, 一個 media URI 并不是必須要包含的,如果沒有,默認為 0。(因為存在多個 m3u8 的情況,視頻太大時減少 m3u8 大小) 2.5 EXT-X-TARGETDURATION 每一份媒體文件的時間, 以秒為單位, 這里是 10 秒一份 2.6 EXTINF 每一份媒體文件的具體數據,包括文件 url,持續時間等 2.7 EXT-X-PROGRAM-DATE-TIME 播放的絕對時間。(這里我們用來更新進度條)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      Spring Spring Cloud 彈性負載均衡 ELB

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:獲取Excel單元格中字符串的最后一個單詞
      下一篇:WPS演示制作動態幻燈片的方法(wps幻燈片制作視頻教學)
      相關文章
      亚洲a∨无码男人的天堂| 久久精品国产精品亚洲艾草网| 亚洲AV人人澡人人爽人人夜夜| ZZIJZZIJ亚洲日本少妇JIZJIZ| 国产产在线精品亚洲AAVV| 亚洲精品无码一区二区| 亚洲色少妇熟女11p| 亚洲中文无码mv| 亚洲高清毛片一区二区| 亚洲精品乱码久久久久久V| 亚洲人成色77777在线观看| 亚洲日本中文字幕天天更新| 亚洲精品无码少妇30P| 亚洲sm另类一区二区三区| 最新亚洲人成网站在线观看| 国产精品亚洲综合网站| 四虎亚洲国产成人久久精品 | 国产在亚洲线视频观看| 亚洲AV无码一区二区大桥未久| 亚洲经典千人经典日产| 含羞草国产亚洲精品岁国产精品 | 亚洲成a人片在线观看天堂无码| 亚洲日产乱码一二三区别| MM1313亚洲国产精品| 亚洲日本韩国在线| 亚洲精品无码久久久久sm| 亚洲人成在线电影| 亚洲日韩在线视频| 亚洲国产综合精品中文第一| 亚洲国产欧美日韩精品一区二区三区| 亚洲国产精品无码第一区二区三区| 国产精品亚洲精品久久精品| 亚洲Av无码乱码在线观看性色| 久久国产成人精品国产成人亚洲| 亚洲成a人片在线观看无码| 精品日韩亚洲AV无码| 亚洲综合校园春色| 色偷偷噜噜噜亚洲男人| 亚洲无码高清在线观看| 亚洲国产精品高清久久久| 亚洲网站视频在线观看|