全棧開發——動手打造屬於自己的直播間(Vue+SpringBoot+Nginx)

開源中國OSC-jack-hoo2017-06-28 10:36:45


前言


大學的學習時光臨近尾聲,感歎時光匆匆,三年一晃而過。同學們都忙着找工作,我也在這裏拋一份簡歷吧,歡迎各位老闆和獵手誠邀。我們進入正題。行業是當前火熱的行業,誰都想從中分得一杯羹,直播養活了一大批人,一個平台主播粗略估計就有幾千號人,但是實時在線觀看量有的居然到了驚人的百萬級別,特別是遊戲主播,可想而知,直播間是一個磁鐵式的廣告傳播媒介,也難怪這麼多巨頭公司都搶着做直播。我不太清楚直播行業技術有多深,畢竟自己沒做過,但是咱們可以自己實現一個滿足幾百號人同時觀看的直播間呀。


最終成果


  • 演示地址(電腦端與移動端效果不同哦)

  • 項目地址

  • 項目地址

手機端效果

這個場景很熟悉吧~~ 通過obs推流軟件來推流。

户外直播,通過yasea手機端推流軟件,使用手機攝像頭推流。 

電腦端效果

播放香港衞視

直播畫面


技術總覽


項目分為三個部分:

  1. 客户端
    直播間視頻拉流、播放和聊天室,炫酷的彈幕以及直播間信息

  2. 服務端
    處理直播間、用户的數據業務,聊天室的處理

  3. 服務器部署
    視頻服務器和web服務器


技術棧


移動客户端

  • VUE全家桶

  • UI層vonic

  • axios

  • 視頻播放器: vue-video-player + videojs-contrib-hls

  • websocket客户端: vue-stomp

  • 彈幕插件: vue-barrage

  • 打包工具:webpack

電腦端客户端

  • 項目架構: Jquery + BootStrap

  • 視頻播放器: video.js

  • websocket客户端: stomp.js + sockjs.js

  • 彈幕插件: Jquery.danmu.js

  • 模版引擎: thymeleaf

服務端

  • IDE: IntelliJ IDEA

  • 項目架構: SpringBoot1.5.4 +Maven3.0

  • 主數據庫: Mysql5.7

  • 輔數據庫: redis3.2

  • 數據庫訪問層: spring-boot-starter-data-jpa + spring-boot-starter-data-redis

  • websocket: spring-boot-starter-websocket

  • 消息中間件: RabbitMQ/3.6.10

服務器部署

  • 視頻直播模塊: nginx-rtmp-module

  • web應用服務器: tomcat8.0

  • 服務器: 騰訊雲centos6.5

技術點講解


直播間主要涉及到兩個主要功能:第一是視頻直播、第二是聊天室。這兩個都是非常講究實時性。

視頻直播

説到直播我們先了解下幾個常用的直播流協議,看了挺多的流媒體協議文章博客,但都是非常粗略,這裏有個比較詳細的 流媒體協議介紹,如果想詳細瞭解協議內容估計去要看看專業書籍了。這裏我們用到的只是rtmp和hls,實踐後發現:rtmp只能夠在電腦端播放,hls只能夠在手機端播放。而且rtmp是相當快的儘管沒有rtsp那麼快,延遲只有幾秒,我測試的就差不多2-5秒,但是hls大概有10幾秒。所以如果你體驗過demo,就會發現手機延遲比較多。

直播的流程: 直播分為推流和拉流兩個過程,那麼流推向哪裏,拉流又從哪裏拉取呢?那當然需要視頻服務器啦,千萬不要以為視頻直播服務器很複雜,其實在nginx服務器中一切都變得簡單。後面我會講解如何部署Nginx服務器並配置視頻模塊(nginx-rtmp-module).

首先主播通過推流軟件,比如OBS Studio推流軟件,這個是比較專業級別的,很多直播平台的推薦主播使用這個軟件來推送視頻流,這裏我也推薦一個開源的安卓端推流工具Yasea,下載地址,文件很小,但是很強大。 直播內容推送到服務器後,就可以在服務器端使用視頻編碼工具進行轉碼了,可以轉換成各種高清,標清,超清的分辨率視頻,也就是為什麼我們在各個視頻網站都可以選擇視頻清晰度。這裏我們沒有轉碼,只是通過前端視頻播放器(video.js)來拉取視頻.這樣整個視頻推流拉流過程就完成了。

聊天室

直播間裏面的聊天室跟我們的羣聊天差不多,只不過它變成了web端,web端的即時通信方案有很多,這裏我們選擇websocket協議來與服務端通信,websocket是基於http之上的傳輸協議,客户端向服務端發送http請求,並攜帶Upgrade:websocket升級頭信息表示轉換websocket協議,通過與服務端握手成功後就可以建立tcp通道,由此來傳遞消息,它與http最大的差別就是,服務端可以主動向客户端發送消息。

既然建立了消息通道,那我們就需要往通道里發消息,但是總得需要一個東西來管控消息該發給誰吧,要不然全亂套了,所以我們選擇了消息中間件RabbitMQ.使用它來負責消息的路由去向。

理論知識都講完啦,實操時間到!


移動客户端實操


源碼請點擊“閲讀原文”獲取

工程結構

功能模塊

  • 拉取服務器的直播視頻流(hls)並播放直播畫面

  • 與服務端創建websocket連接,收發聊天室消息

  • 通過websocket獲取消息併發送到彈幕

  • 通過websocket實時更新在線用户

  • 結合服務端獲取訪問歷史記錄

  • 問題反饋模塊

效果圖

源碼請點擊“閲讀原文”查看


服務端實操


由於個人比較喜歡接觸新的東西,所以後端選擇了springboot,前端選擇了Vue.js年輕人嘛總得跟上潮流。SpringBoot實踐過後發現真的太省心了,不用再理會各種配置文件,全自動化裝配。 這裏貼一下pom.xml

application.properties文件

websocket配置

配置類繼承了消息代理配置類,意味着我們將使用消息代理rabbitmq.使用registerStompEndpoints方法註冊一個websocket終端連接。這裏我們需要了解兩個東西,第一個是stomp和sockjs,sockjs是啥呢,其實它是對於websocket的封裝,因為如果單純使用websocket的話效率會非常低,我們需要的編碼量也會增多,而且如果瀏覽器不支持websocket,sockjs會自動降級為輪詢策略,並模擬websocket,保證客户端和服務端可以通信。 stomp有是什麼看這裏

stomp是一種簡單(流)文本定向消息協議,它提供了一個可互操作的連接格式,允許STOMP客户端與任意STOMP消息代理(Broker)進行交互,也就是我們上面的RabbbitMQ,它就是一個消息代理。 我們可以通過configureMessageBroker來配置消息代理,需要注意的是我們將要部署的服務器也應該要有RabbitMQ,因為它是一箇中間件,安裝非常容易,這裏就不説明了。這裏我們配置了“/topic,/queue”兩個代理轉播策略,就是説客户端訂閲了前綴為“/topic,/queue”頻道都會通過消息代理(RabbitMQ)來轉發。跟spring沒啥關係啦,完全解耦。

websocke如何保證安全

一開始接觸 stomp的時候一直有個問題困擾我,客户端只要與服務端通過websocket建立了連接,那麼他就可以訂閲任何內容,意味着可以接受任何消息,這樣豈不是亂了套啦,於是我翻閲了大量博客文章,很多都是官方的例子並沒有解決實際問題。經過琢磨,其實websocket是要考慮安全性的。具體在以下幾個方面

  1. 跨域websocket連接

  2. 協議升級前握手攔截器

  3. 消息信道攔截器

對於跨域問題,我們可以通過setAllowedOrigins方法來設置可連接的域名,防止跨站連接。

對於站內用户是否允許連接我們可以如下配置

HttpSessionHandshakeInterceptor 這個攔截器用來管理握手和握手後的事情,我們可以通過請求信息,比如token、或者session判用户是否可以連接,這樣就能夠防範非法用户。

那如何限制用户只能訂閲指定內容呢?我們接着往下看

在stomp裏面,Channel信道就是消息傳送的通道,客户端與服務端建立了連接就相當於建立了通道,以後的信息就是通過這個通道來傳輸。所有的消息都有消息頭,被封裝在了spring 的messag接口中,比如建立連接時候消息頭就含有CONNECT,當然還有一些其他的信息。客户端訂閲的時候也有訂閲頭信息SUBSCRIBE,那麼我是不是可以在這個攔截器ChannelInterceptorAdapter 中攔截每個人的訂閲信息,然後與數據庫的信息作比對,最後決定這個用户是否可以訂閲這個頻道的信息呢,對的,這是我的想法,按照這樣的思路,做單聊不是迎刃而解了嗎。 那客户端通過websocket發送的消息如何到達訂閲者手中呢,按照rabbitmq的規則,訂閲者屬於消費者,發送消息的一方屬於生產者,生產者通過websocket把消息發送到服務端,服務端通過轉發給消息代理(rabbitmq),消息代理負責存儲消息,管理髮送規則,推送消息給訂閲者,看下面的代碼

@MessageMapping看起來跟springmvc方法特別像,它即可以用在類級別上也可以用在方法級別上 當發送者往‘/chat’發送消息後,服務端接受到消息,再發送給“/topic/group”的訂閲者,@SendTo就是發送給誰,這裏需要注意的有,如果我們沒有配置消息代理,只使用了enableSimpleBroker("/topic","/queue")簡單消息代理,那麼就是直接發送到消息訂閲者,如果配置了消息代理,那還要通過消息代理,由它來轉發。

如果我們想在服務端隨時發送消息,而不是在客户端發送(這樣的場景很常見,比如發送全局通知),可以使用SimpMessagingTemplate類,通過注入該bean,在合適的業務場景中發送消息。

Redis統計數據

直播間經常需要統計數據,比如實時在線人數,訪問量,貢獻排行榜,訂閲量。我選擇的方案是使用redis來計數,儘管這個demo可能不會太多人訪問,但是我的目的是學習如何使用redis 先看springboot中redis的配置

redis數據統計Dao的實現

Dao層非常簡單,因為我們只需要統計在線人數和訪客。但是在線人數是實時更新的,既然我們使用了websocket實時數據更新就非常容易了,前面我們講過,通過信道攔截器可以攔截連接,訂閲,斷開連接等等事件信息,所以我們就可以當用户連接時存儲在線用户,通過websocket返回在線用户信息。

由於這個項目有移動端和電腦端,所以需要根據請求代理UserAgent來判斷客户端屬於哪一種類型。這個工具類在源碼上有。我就不貼了。


服務器部署


説了這麼多即時通信,卻沒發現視頻直播。不要着急我們馬上進入視頻環節。文章開頭就説明了幾種媒體流協議,這裏不講解詳細的協議流程,只需要知道,我們是通過推流軟件採集視頻信息,如何採集也不是我們關注的。採集到信息後通過軟件來推送到指定的服務器,如下圖

obs推流設置

 

yasea手機端推流設置 

紅色部分是服務器開放的獲取流接口。

Nginx-rtmp-module配置

視頻服務器有很多,也支持很多媒體流協議。這裏我們選擇nginx-rtmp-module來做視頻服務,接下來我們需要在linux下安裝nginx,並安裝rtmp模塊。本人也是linux初學者,一步步摸索着把服務器搭建好,聽説tomcat和nginx很配哦,所以作為免費開源的當然首選這兩個。 接下來需要在linux安裝一下軟件和服務。

  1. Nginx以及Nginx-rtmp-module

  2. Tomcat

  3. Mysql

  4. Redis

  5. RabbitMQ

安裝步驟我就不説了,大家搜索一下啦,這裏貼一下nginx.conf文件配置

上面代碼是配置rtmp模塊, play /yjdata/www/www/video 指的是配置點播模塊,可以直接播放/yjdata/www/www/video路徑下的視頻。hls_path制定hls分塊存放路徑,因為hls是通過獲取到推送的視頻流信息,分塊存儲在服務器。所以它的延時比rtmp要更高。

上面配置了location 指向/hls,別名是/yjdata/www/www/live/hls/,所以可以在前端直接通過域名+/hls/+文件名.m3u8獲取直播視頻。 關於nginx的配置還有很多,我也在學習當中。總而言之nginx非常強大。


總結


通過從前端=>後台=>服務器,整個流程走下來還是需要花很多心思。但是收穫也是很多。本人將從大學出來,初出茅廬,文章錯誤之處,盡請指正。



推薦閲讀

Debian 9 Stretch 經過 26 個月的開發後終於正式發佈

關於 ASP.NET 內存緩存你需要知道的 10 點

14 個開源 REST 與 SOAP 服務 API 測試工具

從滿腔熱血到想刪庫跑路,神級程序員分享開源苦與樂

點擊“閲讀原文”查看更多精彩內容

閲讀原文

TAGS: