分析抖音的互联网架构 | 青训营

123 阅读5分钟

我们先从前端来看

前端主要包括

  1. 视频内容 推荐/关注/同城/经验/发视频、剪辑
  2. 评论、点赞、收藏、转发 个人主页等一些社交属性的按钮
  3. 商城主要是b/s架构的一个网站

对应前端的功能我们能看出抖音的核心技术,那么它们是如何实现的呢

  • 视频录制和编辑:抖音的核心功能是视频录制和编辑。抖音使用了一种名为“实时美颜”的技术,以在录制过程中对视频进行处理和优化。此外,抖音还使用了一些算法来自动剪辑视频,以确保它们具有最佳的长度和内容。

  • 视频压缩和传输:由于短视频的特殊性质,即时的传输和高效的压缩对于抖音至关重要。抖音使用了一种名为“分块压缩”的技术,以将视频分成多个小块并对其进行压缩。这使得视频可以更快地上传和下载,同时还可以减少数据使用量。

  • AI推荐算法:抖音使用了一种名为“内容推荐”的算法,以根据用户的兴趣和行为推荐相关的视频。这种算法使用了机器学习和人工智能技术,以分析用户的历史行为和喜好,并根据这些信息推荐新的内容。

  • 网络优化和负载均衡:抖音需要处理大量的视频上传和下载请求,因此需要进行网络优化和负载均衡,以确保应用程序的稳定性和性能。抖音使用了一种名为“分布式存储”的技术,以将数据存储在多个服务器上,并使用负载均衡算法将请求分配到最适合的服务器上。

那么抖音上海量的视频是如何存储的呢? 抖音的存储方案是基于云存储的,这意味着视频数据不是存储在本地设备上,而是存储在云端的服务器上。这种存储方案具有以下优点:

  1. 可扩展性强:云存储方案可以轻松地扩展存储容量,以满足不断增长的视频数据量。
  2. 高可靠性:云存储方案可以提供高可靠性的数据备份和灾难恢复功能,以确保视频数据的安全性和可靠性。
  3. 快速访问:云存储方案可以通过多个节点实现数据的快速访问,以提高视频数据的访问速度和用户体验。
  4. 分布式存储技术:将视频数据分散存储在多个节点上,以提高存储效率和可靠性。
  5. 压缩技术:采用高效的视频压缩技术,以减少视频数据的存储空间和传输带宽。
  6. CDN加速技术:采用CDN加速技术,将视频数据缓存到离用户最近的节点上,以提高视频的访问速度和用户体验。

如此多的视频和用户,抖音的数据库也要做到高可用,分库分表和缓存必不可少。

分库分表可以使用一些开源的框架,比如 ShardingSphere、MyCat等,也可能使用的是自研技术

后端如何控制视频流? 在上一次青训营中,我们组大项目feed限流使用了计数器原理 计数器是一种最简单限流算法,其原理就是:在一段时间间隔内,对请求进行计数,与阀值进行比较判断是否需要限流,一旦到了时间临界点,将计数器清零。 程序执行逻辑:

  • 可以在程序中设置一个变量 count,当过来一个请求我就将这个数 +1,同时记录请求时间。
  • 当下一个请求来的时候判断 count 的计数值是否超过设定的频次,以及当前请求的时间和第一次请求时间是否在 1 分钟内。
  • 如果在 1 分钟内并且超过设定的频次则证明请求过多,后面的请求就拒绝掉。
  • 如果该请求与第一个请求的间隔时间大于计数周期,且 count 值还在限流范围内,就重置 count。

相关代码如下

package main
import (
"log"
"sync"
"time"
)
type Counter struct {
	rate  int           //计数周期内最多允许的请求数
	begin time.Time     //计数开始时间
	cycle time.Duration //计数周期
	count int           //计数周期内累计收到的请求数
	lock  sync.Mutex
}
func (l *Counter) Allow() bool {
	l.lock.Lock()
	defer l.lock.Unlock()
	if l.count == l.rate-1 {
		now := time.Now()
		if now.Sub(l.begin) >= l.cycle {
			//速度允许范围内, 重置计数器
			l.Reset(now)
			return true
		} else {
			return false
		}
	} else {
		//没有达到速率限制,计数加1
		l.count++
		return true
	}
}
func (l *Counter) Set(r int, cycle time.Duration) {
	l.rate = r
	l.begin = time.Now()
	l.cycle = cycle
	l.count = 0
}
func (l *Counter) Reset(t time.Time) {
	l.begin = t
	l.count = 0
}
func main() {
	var wg sync.WaitGroup
	var lr Counter
	lr.Set(3, time.Second) // 1s内最多请求3次
	for i := 0; i < 10; i++ {
		wg.Add(1)
		log.Println("创建请求:", i)
		go func(i int) {
			if lr.Allow() {
				log.Println("响应请求:", i)
			}
			wg.Done()
		}(i)
		time.Sleep(200 * time.Millisecond)
	}
	wg.Wait()
}

这段代码是一个使用Go语言编写的速率限制器示例。它定义了一个名为Counter的结构体,用于跟踪请求的速率和计数周期。

结构体Counter具有以下成员变量:

  • rate: 表示在计数周期内最多允许的请求数。
  • begin: 表示计数开始的时间点。
  • cycle: 表示计数周期的持续时间。
  • count: 表示计数周期内累计收到的请求数。
  • lock: 用于保护共享资源的互斥锁。

结构体中还定义了以下函数:

  • Allow(): 检查当前是否允许发送请求。如果请求数已经达到最大值或计时超过了计数周期,则返回false;否则返回true。
  • Set(r int, cycle time.Duration): 设置速率限制器的参数,包括最大请求数、计时周期等。
  • Reset(t time.Time): 重置计数器的起始时间和计数。

main()函数中,我们创建了一个Counter对象lr,并通过调用Set()函数将其配置为每秒最多允许3次请求。然后,我们使用一个循环模拟10个并发请求的情况。在循环中,我们创建一个新的goroutine来处理每个请求,并在发送请求之前调用Allow()函数进行速率限制检查。如果允许发送请求,则打印响应信息,并通过调用wg.Done()通知WaitGroup完成该goroutine的执行。最后,我们在循环结束后调用wg.Wait()等待所有goroutine执行完毕。