从零开始仿写一个抖音App——日志和埋点以及后端初步架构

本文首发于微信公众号——世界上有意思的事,搬运转载请注明出处,不然将追究版权责任。微信号:a1018998632,交流qq群:859640274html

连载文章

本项目的 github 地址:MyTikTok

这两周实在是太忙了,第一个需求即将上线加了一周的班,而后第二周又团建了三天致使此次的文章滞后了两周,估计你们都觉得我要弃坑了。可是其实我在团建的时候都在赶文章,让你们久等了。本周的文章将会讨论下面几个问题,你们能够按需跳章查看以节省宝贵的时间,本文预计阅读时间10分钟java

  • 1.讨论——总结前两周评论中有意义的讨论并给予个人解答
  • 2.日志和埋点——讨论一下日志和埋点如何设计以及实现方案
  • 3.后端架构初步设想——讨论一下将来的 app 的后端须要怎么架构以及如何实现
  • 4.ubuntu环境初始化——将云上的环境初始化成我所熟悉的 mac 环境(读者若是是 windows 也能够了解一下,到后期的话文章会涉及到比较多的 linux 下的操做)

1、讨论

讨论1:项目会不会使用 kotlin?python

  • 1.目前个人计划是在基础模块上面使用 java ,在业务模块中看状况选择几个模块使用 kotlin。

讨论2:本系列文章是标题党,蹭抖音的热度linux

  • 1.首先明确一点为何我要以抖音为例子,缘由就是个人公司就是开发短视频的,技术上有相似的地方,而本公司的产品是不可能做为例子开发的,因此我就以抖音为例但愿能过一遍大公司的项目开发流程和架构,不只仅是给读者带来好处,对我来讲也是一个很好的提高。
  • 2.固然不能否认的是抖音这个 title 给我带来了必定的流量,也吸引了一部分人的眼球,可是我心安理得。由于每一篇文章的内容都是我花费两周以上的业余时间撰写的,内容的质量上我敢说比通常的文章要好上很多。
  • 3.有句话说得好:人红是非多,放在文章上也是同样。我不但愿打无谓的口水仗因此:之后若是文章中有与技术和文章无关的攻击或者诋毁的评论我会直接删除,而且不作回复。

2、日志和埋点

日志在一个项目中起着很是重要的辅助做用,它可让开发人员方便的定位 bug。它能够在系统上线以后让后台监控 app 的性能以及稳定性。他还能够收集用户的行为数据以方便对用户的需求进行分析。在这一节中我会分析5种不一样的日志,并讲解其中几种日志的实现方式。android

首先我先列举一下五种不一样的 log 吧。git

  • 1.debug 日志:用于开发人员本地 debug
  • 2.aop debug 日志:用于开发人员本地 debug, 使用了 aop 能够经过简单的注释,对方法和类进行切片打日志。用于打一些须要统一执行的日志。
  • 3.网络请求 日志:用于开发人员在本地对网络请求 debug
  • 4.本地文件 日志:用于记录在 app 上线以后出现的bug,将日志打到文件中,能够经过一个入口让用户手动点击上传日志。
  • 5.埋点 日志:用于记录用户使用 app 的数据、app 性能等等的埋点日志,数据结构由先后端协商定义,最后会存入后端的数据库以便进行一些数据分析。埋点的方式能够是手动的,能够是自动的。

一、debug日志

图1:debug日志.png

  • 1.debug 日志比较简单,如图一就是将 android 自身提供的 Log 类进行一些封装,添加一些本身须要的特性和扩展,这里就很少赘述了具体实现能够看项目中的代码。

二、aop日志

图2:aop日志.png

  • 1.不少人在写一些重复性的日志的时候就会想到 aop,这种技术能够在注解的方法先后注入须要的模板代码。我在上一篇文章中讲到了这个技术,有兴趣的同窗能够去看看,这里我就简单说一下。
  • 2.首先咱们得先定义一个注解类,其能够用于注解类或者方法。注解类中能够被填入一些信息,好比是否须要打印方法的初入参等等。
  • 3.在注解类使用了以后,咱们须要用到 gradle transform。这种技术可让咱们在编译期间扫描全部的类,从而找被注解类所注解的方法和类。
  • 4.最后咱们能够用上javassist来给找到的方法先后注入咱们须要的代码。注意这里的日志能够是本地的 debug日志,也能够是本地文件日志,还能够是埋点日志。能够说 aop 日志只是一种对另外几种日志的自动化封装。

三、网络请求日志

图3:网络请求日志.png

  • 1.咱们在调试网络请求的时候,除了抓包还会打印出网络请求。这个时候就若是有一种统一的形式来打印日志的话就会方便许多。
  • 2.如今绝大部分的厂商使用的网络请求库都是 okhttp ,因此我就直接在其上面进行日志的定制就好了。由于项目的 http 模块尚未进行开发,因此尚未实现代码,这里就讲一讲大体方案。以后在开发 http 模块的时候会顺便讲解具体实现。
  • 3.在讲解方案以前咱们须要知道,okhttp 的工做方式。如图3中所示,在一个 okhttp 请求的过程当中会通过一个个拦截器,从本地向网络请求的时候会通过一次,网络请求回来的时候又会通过一次。
  • 4.因此咱们就能够添加一个日志拦截器在两次通过拦截器的时候打印请求的 head 和按需打印 请求的 body。注意,这里打印能够是向 debug 日志、本地文件日志、埋点日志这三个地方打印。分别用于本地 debug、线上 debug和网络性能监控。

四、本地文件日志

图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执行。

图5:LocalFileLogger代码1.png
图6:LocalFileLoggerService代码1.png
图7:FileLogger代码1.png
图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 阈值,只有超过了这个阈值才会向文件系统中写入日志。

五、埋点日志

图9:埋点日志.png

  • 1.埋点日志其实和文件日志相似,我这里就结合图9简单说一下,具体的代码你们能够去翻看项目
  • 2.首先仍是有一个 UploadLogManager 用于给外部提供 api 以及初始化 LocalFileLoggerService。这里比文件系统复杂的地方就在于多了一个 UploadLogConfiguration 用于装配一些设置。
  • 3.有了 LocalFileLoggerService 以后这里分两个不一样的埋点日志添加方式。
    • 1.实时埋点日志添加:外部须要当即将当前的埋点日志上报,此时就直接将请求发送给 UploadLogHandler 而后交给 HandlerThread 执行,最终 经过 LogSender执行网络上报。
    • 2.非实时埋点日志添加:这种方式是每隔必定的时间,LocalFileLoggerService 会从 UploadLogStorage 中取出必定量的日志,合并以后再像1中同样上报埋点。
  • 4.目前由于 Http 模块和 数据库模块都没有开始写,因此 UploadLogStorage 和 LogSender 都还只是接口,可是并不影响代码逻辑。

3、后端架构的初步设想

虽然本项目的着重点是仿抖音 android 端 app 的开发,可是后台方面也会有所涉及。接下来笔者会介绍一下本项目在后端方面的目标和预期达到的效果。程序员

一、RPC

可能会有客户端的同窗对 RPC(远程过程调用) 这个词不怎么了解,我这里就先简单介绍一下。github

拿 Java 来讲:好比咱们有两个服务 A、B 在两个服务器上,此时咱们要在 A 上调用 B 的服务获取其上的数据 Foo。那么在 A 中能够写成 Foo f = b.XXXService();。在这里 Foo 是 A、B 两个服务所定义的数据传输结构,b 是 B 服务所抽象出来的对象,其内部实现能够是各类网络数据交互协议,好比说 http 协议。简单来讲:RPC就是要像调用本地的函数同样去调远程函数。算法

现存的 RPC 框架有不少,各个大厂也都开源了本身框架,我这里就介绍和比较一下几个框架,最后结合本项目的需求选择适合的框架。docker

  • 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.为了区份内外网,以及统一鉴权。须要实现的第三个功能就是:服务网关,全部外部请求都会通过这个网关,网关会将请求分发给内部的机器,内部机器调用完成以后会将结果经过网关返回给外部。

4、ubuntu环境初始化

不知道在个人读者中有多少我的用的是 mac。由于我本人就是 mac 和 win 的双系统用户因此我深知。mac 在开发方面的好处。这一节就轻松一点,我演示一下如何将本地 mac 命令行环境初始化到云上的 ubuntu 中。

一、oh my zsh

图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 root@47.106.145.211:/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,英语比较好的同窗能够看原项目,来拓展本身的配置。

二、vim 配置

图11:vim.png

  • 1.接下来就是 vim 的配置,其实我到如今也没彻底成功的把配置彻底成功的把配置完成成功的转移到 ubuntu 上面,因此你们看看就好。
  • 2.ubuntu初始化,这个仓库里 .vimrc 是 vim 的配置文件。vim 插件管理,这个仓库里是 vim 插件库。
  • 3.这里其实就是为了 show 一下个人成果,对于初学者来讲能学习的方面很少,对于老鸟来讲也看不上个人配置。

三、docker配置

这两周我也抽空学习了一下 docker,个人理解上 docker 就是一个方便打包重用超轻量虚拟机。因此咱们后端也会用上这个技术以方便运维。我也是刚学这东西,因此我就贴几个我学习的网址吧!

5、尾巴

本篇文章是从零开始仿写一个抖音App系列文章的第四篇,篇幅比较长能看到这里的同窗很是感谢大家对个人承认。从决定写这个系列的文章开始到如今已经两个多月了,我发现这两个月个人成长是很是迅速的,因此接下来我还会坚持这样写下去。

不贩卖焦虑,也不标题党。分享一些这个世界上有意思的事情。题材包括且不限于:科幻、科学、科技、互联网、程序员、计算机编程。下面是个人微信公众号:世界上有意思的事,干货多多等你来看。

世界上有意思的事