天天看點

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

本文首發于簡書——何時夕,搬運轉載請注明出處,否則将追究版權責任。

本項目的 github 位址:MyTikTok

這兩周實在是太忙了,第一個需求即将上線加了一周的班,然後第二周又團建了三天導緻這次的文章滞後了兩周,估計大家都以為我要棄坑了。但是其實我在團建的時候都在趕文章,讓大家久等了。本周的文章将會讨論下面幾個問題,大家可以按需跳章檢視以節省寶貴的時間,本文預計閱讀時間10分鐘。
  • 1.讨論——總結前兩周評論中有意義的讨論并給予我的解答
  • 2.日志和埋點——讨論一下日志和埋點如何設計以及實作方案
  • 3.後端架構初步設想——讨論一下未來的 app 的後端需要怎麼架構以及如何實作
  • 4.ubuntu環境初始化——将雲上的環境初始化成我所熟悉的 mac 環境(讀者如果是 windows 也可以了解一下,到後期的話文章會涉及到比較多的 linux 下的操作)

一、讨論

讨論1:項目會不會使用 kotlin?

  • 1.目前我的計劃是在基礎子產品上面使用 java ,在業務子產品中看情況選擇幾個子產品使用 kotlin。

讨論2:本系列文章是标題黨,蹭抖音的熱度

  • 1.首先明确一點為什麼我要以抖音為例子,原因就是我的公司就是開發短視訊的,技術上有類似的地方,而本公司的産品是不可能作為例子開發的,是以我就以抖音為例希望能過一遍大公司的項目開發流程和架構,不僅僅是給讀者帶來好處,對我來說也是一個很好的提升。
  • 2.當然不可否認的是抖音這個 title 給我帶來了一定的流量,也吸引了一部分人的眼球,但是我問心無愧。因為每一篇文章的内容都是我花費兩周以上的業餘時間撰寫的,内容的品質上我敢說比一般的文章要好上不少。
  • 3.有句話說得好:人紅是非多,放在文章上也是一樣。我不希望打無謂的口水仗是以:以後如果文章中有與技術和文章無關的攻擊或者诋毀的評論我會直接删除,并且不做回複。

二、日志和埋點

日志在一個項目中起着非常重要的輔助作用,它可以讓開發人員友善的定位 bug。它可以在系統上線之後讓背景監控 app 的性能以及穩定性。他還可以收集使用者的行為資料以友善對使用者的需求進行分析。在這一節中我會分析5種不同的日志,并講解其中幾種日志的實作方式。

首先我先列舉一下五種不同的 log 吧。

  • 1.debug 日志:用于開發人員本地 debug
  • 2.aop debug 日志:用于開發人員本地 debug, 使用了 aop 可以通過簡單的注釋,對方法和類進行切片打日志。用于打一些需要統一執行的日志。
  • 3.網絡請求 日志:用于開發人員在本地對網絡請求 debug
  • 4.本地檔案 日志:用于記錄在 app 上線之後出現的bug,将日志打到檔案中,可以通過一個入口讓使用者手動點選上傳日志。
  • 5.埋點 日志:用于記錄使用者使用 app 的資料、app 性能等等的埋點日志,資料結構由前後端協商定義,最後會存入後端的資料庫以便進行一些資料分析。埋點的方式可以是手動的,可以是自動的。

1、debug日志

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖1:debug日志.png

  • 1.debug 日志比較簡單,如圖一就是将 android 自身提供的 Log 類進行一些封裝,添加一些自己需要的特性和擴充,這裡就不多贅述了具體實作可以看項目中的代碼。。

2、aop日志

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖2:aop日志.png

  • 1.很多人在寫一些重複性的日志的時候就會想到 aop,這種技術可以在注解的方法前後注入需要的模闆代碼。我在上一篇文章中講到了這個技術,有興趣的同學可以去看看,這裡我就簡單說一下。
  • 2.首先我們得先定義一個注解類,其可以用于注解類或者方法。注解類中可以被填入一些資訊,比如是否需要列印方法的初入參等等。
  • 3.在注解類使用了之後,我們需要用到 gradle transform。這種技術可以讓我們在編譯期間掃描所有的類,進而找被注解類所注解的方法和類。
  • 4.最後我們可以用上javassist來給找到的方法前後注入我們需要的代碼。注意這裡的日志可以是本地的 debug日志,也可以是本地檔案日志,還可以是埋點日志。可以說 aop 日志隻是一種對另外幾種日志的自動化封裝。

3、網絡請求日志

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖3:網絡請求日志.png

  • 1.我們在調試網絡請求的時候,除了抓包還會列印出網絡請求。這個時候就如果有一種統一的形式來列印日志的話就會友善許多。
  • 2.現在絕大部分的廠商使用的網絡請求庫都是 okhttp ,是以我就直接在其上面進行日志的定制就行了。因為項目的 http 子產品還沒有進行開發,是以還沒有實作代碼,這裡就講一講大緻方案。之後在開發 http 子產品的時候會順便講解具體實作。
  • 3.在講解方案之前我們需要知道,okhttp 的工作方式。如圖3中所示,在一個 okhttp 請求的過程中會經過一個個攔截器,從本地向網絡請求的時候會經過一次,網絡請求回來的時候又會經過一次。
  • 4.是以我們就可以添加一個日志攔截器在兩次經過攔截器的時候列印請求的 head 和按需列印 請求的 body。注意,這裡列印可以是向 debug 日志、本地檔案日志、埋點日志這三個地方列印。分别用于本地 debug、線上 debug和網絡性能監控。

4、本地檔案日志

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖4:本地檔案日志.png

  • 1.當我們線上上遇見 bug 的時候咋辦呢?有些 crash 的日志可以通過 bugly 這種平台來進行回撈。但是有些奇葩的 bug 隻在某些機型甚至某些使用者的手機上發生。這個時候本地檔案日志就派上用場了。
  • 2.我們可以在開發的時候在一些關鍵的功能上手動添加上本地檔案日志。當某個使用者報了 bug 之後我們就可以讓其通過一個入口将檔案日志發送到背景,最後由開發人員進行日志分析找到問題。
  • 3.接下來我就來通過代碼結合上面的圖4來講解本地檔案日志的實作方式。
  • 4.我們先來看看圖4:
    • 1.LocalFileLogger負責提供本子產品對外的 api,主要功能有兩個:
      • 1.初始化和綁定LocalFileLoggerService(這是一個 service,可以通過 binder 來與外部互動)
      • 2.通過 binder 将外部的添加日志的請求交給LocalFileLoggerService
    • 2.LocalFileLoggerService中會初始化一個 HandlerThread,本 Service 會通過 Handler 向其不斷的抛入經過高性能拼接的日志的添加請求。
    • 3.FileLogger是負責将日志寫入本地的類,其也初始化了一個 HandlerThread,并且自定義了一個 LoggerHandler。這個 Handler 會将 LocalFileLoggerService 抛過來的一條條日志進行累積,當積累到了一定量的時候。發出寫入日志的請求交給 HandlerThread執行。
從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖5:LocalFileLogger代碼1.png

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖6:LocalFileLoggerService代碼1.png

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖7:FileLogger代碼1.png

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖8:LoggerHandler代碼1.png

  • 5.再來看看代碼,我們跟着代碼走一遍流程:
    • 1.首先在圖5中我們可以看見在 addLog 中經過一系列的調用,最終交給了 sLogInterface.log 這個對象是一個 Binder 對象,用于操作 LocalFileLoggerService 。
    • 2.進入到圖6,可以看見 Service 初始化了一個 HandlerThread 然後定義了一個 Handler 用于向其中抛送請求。然後在看 mBinder 的實作就是通過 Handler 向 HandlerThread 中抛送 FileLogger.addLog 的執行請求。
    • 3.進入到圖7,可以看見在 FileLogger 初始化的時候也初始化了一個 HandlerThread ,然後定義了一個 LoggerHandler 來向其中抛日志寫入請求。FileLogger.addLog 方法中是直接發送一個請求。
    • 4.再看圖8,LoggerHandler.add 中并不會立即向本地寫入日志,而是會有一 LOG_CACHE_COUNT 門檻值,隻有超過了這個門檻值才會向檔案系統中寫入日志。

5、埋點日志

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖9:埋點日志.png

  • 1.埋點日志其實和檔案日志類似,我這裡就結合圖9簡單說一下,具體的代碼大家可以去翻看項目
  • 2.首先還是有一個 UploadLogManager 用于給外部提供 api 以及初始化 LocalFileLoggerService。這裡比檔案系統複雜的地方就在于多了一個 UploadLogConfiguration 用于裝配一些設定。
  • 3.有了 LocalFileLoggerService 之後這裡分兩個不同的埋點日志添加方式。
    • 1.實時埋點日志添加:外部需要立即将目前的埋點日志上報,此時就直接将請求發送給 UploadLogHandler 然後交給 HandlerThread 執行,最終 通過 LogSender執行網絡上報。
    • 2.非實時埋點日志添加:這種方式是每隔一定的時間,LocalFileLoggerService 會從 UploadLogStorage 中取出一定量的日志,合并之後再像1中一樣上報埋點。
  • 4.目前因為 Http 子產品和 資料庫子產品都沒有開始寫,是以 UploadLogStorage 和 LogSender 都還隻是接口,但是并不影響代碼邏輯。

三、後端架構的初步設想

雖然本項目的着重點是仿抖音 android 端 app 的開發,但是背景方面也會有所涉及。接下來筆者會介紹一下本項目在後端方面的目标和預期達到的效果。

1、RPC

可能會有用戶端的同學對 RPC(遠端過程調用) 這個詞不怎麼了解,我這裡就先簡單介紹一下。

拿 Java 來說:比如我們有兩個服務 A、B 在兩個伺服器上,此時我們要在 A 上調用 B 的服務擷取其上的資料 Foo。那麼在 A 中可以寫成 Foo f = b.XXXService();。在這裡 Foo 是 A、B 兩個服務所定義的資料傳輸結構,b 是 B 服務所抽象出來的對象,其内部實作可以是各種網絡資料互動協定,比如說 http 協定。簡單來說:RPC就是要像調用本地的函數一樣去調遠端函數。

現存的 RPC 架構有很多,各個大廠也都開源了自己架構,我這裡就介紹和比較一下幾個架構,最後結合本項目的需求選擇适合的架構。

  • 1.Dubbo:這個是阿裡開源的一個架構,後來阿裡因為種種原因把他廢棄了,最後被當當網維護擴充出了一個 Dubbox。這裡就講一講他的優劣勢吧:
    • 1.優勢:
      • 1.Dubbo 是用 java 寫的,對于 android 用戶端的開發者來說比較友好。
      • 2.Dubbo 的生态目前來說還是比較好的,筆者去年在有贊實習 java 開發的時候,用過半年的 Dubbo,感覺各種坑都有人踩過,各種庫也都比較完善。
      • 3.對于服務治理支援的比較到位。
    • 2.劣勢:
      • 1.跨平台能力差,原生的 Dubbo 基本上沒有跨平台能力,後面的話內建了 thrift 作為擴充的話就有了,不過我總感覺內建之後用起來不友善。
      • 2.以 java 作為主開發語言的話,不能快速疊代。我們項目的時間主要是要向 android 用戶端傾斜,是以需要一個能快速疊代的語言。
      • 3.序列化和反序列化的速度與其他 RPC 架構相比都不是很拔尖。
      • 4.性能較其他幾個架構差。
  • 2.Thrift:這個是 FaceBook 開源的一個架構,2007年由facebook貢獻到apache基金,是apache下的頂級項目。
    • 1.優勢:
      • 1.跨平台能力強,支援幾乎所有的主流語言。
      • 2.性能比較好
    • 2.劣勢:
      • 1.跨平台的語言協定寫起來比較麻煩。
      • 2.不支援服務治理
  • 3.Grpc:由 Google 開源的架構,我司目前後端也在使用這個架構
    • 1.優勢:
      • 1.跨平台能力強、支援大部分主流開發語言
      • 2.跨平台語言協定用的是 ProtoBuf,與我們用戶端的技術棧一緻。
      • 3.性能比較好
      • 4.有我司的技術支援,當然不是官方的,不過我可以了解我司在這方面的技術,最後反哺到我們的項目中。
    • 2.劣勢:
      • 1.不支援服務治理

看了上面的比較我想大家心裡已經有了答案,沒錯我決定使用 grpc 做為本項目後端的 rpc 架構。然後開發的語言是 python 為主,java 為輔助,後面如果有時間的話可能會用 go 實作一個小的服務也說不定。使用這些語言的原因有下面幾點:

  • 1.首先 python 目前背景的生态也比較成熟,用起來也比較友善快速。
  • 2.其次我們到了後面會使用 tensorflow 來訓練各種深度學習的模型,這樣的話熟練使用 python 是必須的。
  • 3.有人會問你為什麼要用幾種不同的語言來實作後端的服務呢?這不是多此一舉嗎。的确,從正常開發的角度來講是挺多餘的,但是多語言的環境在大一些的廠來講是再正常不過的事情,我的一部分目的也是為了模拟這種場景。除此之外,這種多語言的環境在我看來還是比較有意思的,想試試玩玩看。

2.微服務與服務治理

其實本來在這裡我有很多東西想說的,但是發現自己現在的能力并不能完全說好這兩個東西,怕最後會誤導大家,是以我這裡就列一下最後本項目需要完成的與這兩個目标相關的東西。
  • 1.在未來筆者預期的是會有10台服務機器,兩台為一組提供一類服務,一共會有五個大類的服務。
  • 2.是以第一個要實作的功能就是:服務發現注冊功能。這個功能主要是和注冊中心進行互動。
    • 1.服務提供者啟動,向注冊中心注冊自己提供的服務
    • 2.消費者啟動,向注冊中心訂閱自己需要的服務
    • 3.注冊中心傳回服務提供者的清單給消費者
    • 4.消費者從服務提供者清單中,按照軟負載均衡算法,選擇一台發起請求
  • 3.為了了解和監控各個服務的情況,第二個要實作的功能就是:服務監控,即累計計算随着時間推移各個服務被調用的次數。
  • 4.為了區分内外網,以及統一鑒權。需要實作的第三個功能就是:服務網關,所有外部請求都會經過這個網關,網關會将請求分發給内部的機器,内部機器調用完成之後會将結果通過網關傳回給外部。

四、ubuntu環境初始化

不知道在我的讀者中有多少個人用的是 mac。因為我本人就是 mac 和 win 的雙系統使用者是以我深知。mac 在開發方面的好處。這一節就輕松一點,我示範一下如何将本地 mac 指令行環境初始化到雲上的 ubuntu 中。

1、oh my zsh

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖10:oh my zsh.png

  • 1.首先需要在XX雲中買一個機器。我買的是阿裡雲,最開始的系統模闆選擇 ubuntu16,然後什麼都不要裝。然後在本地用 ssh 登入雲主機。
  • 2.在本地電腦上 clone 一下我的這個庫,接下來要用到裡面的兩個腳本檔案。ubuntu初始化
  • 3.用 scp 指令将2中的兩個檔案上傳到伺服器上分别是:ubuntu_init.sh 和 ubuntu_init_oh-my-zsh.sh。例如:scp a.jpg [email protected]:/root/a.jpg,将本地本目錄的 a.jpg 檔案上傳為雲伺服器上的/root/a.jpg檔案。
  • 4.運作ubuntu_init.sh,中間會讓你輸入密碼,最後會重新開機伺服器。
  • 5.等4中重新開機伺服器之後,登入伺服器然後運作ubuntu_init_oh-my-zsh.sh。如此就大功告成了。最終效果如圖10,這個終端比 ubuntu 原生的好用多了,而且還支援各種定制的插件。
  • 6.忘了說了這個指令行是一個開源項目:oh my zsh,英語比較好的同學可以看原項目,來拓展自己的配置。

2、vim 配置

從零開始仿寫一個抖音App——日志和埋點以及後端初步架構本項目的 github 位址:MyTikTok本項目的 github 位址:MyTikTok

圖11:vim.png

  • 1.接下來就是 vim 的配置,其實我到現在也沒完全成功的把配置完全成功的把配置完成成功的轉移到 ubuntu 上面,是以大家看看就好。
  • 2.ubuntu初始化,這個倉庫裡 .vimrc 是 vim 的配置檔案。vim 插件管理,這個倉庫裡是 vim 插件庫。
  • 3.這裡其實就是為了 show 一下我的成果,對于初學者來說能學習的方面不多,對于老鳥來說也看不上我的配置。

3、docker配置

這兩周我也抽空學習了一下 docker,我的了解上 docker 就是一個友善打包重用超輕量虛拟機。是以我們後端也會用上這個技術以友善運維。我也是剛學這東西,是以我就貼幾個我學習的網址吧!
  • 1.docker初始學習
  • 2.docker python 學習

五、尾巴

本篇文章是從零開始仿寫一個抖音App系列文章的第四篇,篇幅比較長能看到這裡的同學非常感謝你們對我的認可。從決定寫這個系列的文章開始到現在已經兩個多月了,我發現這兩個月我的成長是非常迅速的,是以接下來我還會堅持這樣寫下去。

連載文章

  • 1.從零開始仿寫一個抖音app——開始
  • 2.從零開始仿寫一個抖音App——基本架構與MVPs
  • 3.從零開始仿寫一個抖音App——Apt代碼生成技術、gradle插件開發與protocol協定