03-面试讲解
# TTS性能优化面试讲解
# 知识点图谱
整个知识点主要分为这么几个部分:
文字分段请求
情绪标记
并发控制
- 请求取消
设置缓存
# 难点叙述
模拟问题:我看到你的项目亮点里面写的是“优化 TTS 服务性能问题”,你能详细说一下这是啥么?然后讲一下有什么样的性能问题么?并且你这边具体是如何进行优化的?
问题分析
- 先解释一下什么是 TTS
- 简单介绍一下项目的背景
- 描述遇到的问题
- 你拿到这个问题你是如何思考的
- 你思考出来的落地方案
- 落地方案的一个效果
参考答案
TTS 是英语“Text-to-Speech”的缩写,中文就是“文本转语音”。(简单介绍什么是TTS)当时我们这个项目是一个知识分享平台的项目,有点像极客时间那种类型的,一些老师会分享一些知识的的文档,然后文档支持在线语音播放功能。(项目的背景)
由于 WebAPI 的语音转换太单板了,所以我们当时选择的是科大讯飞的 TTS 服务,不过会遇到这么一些问题:
- 文本过大导致转换时间过长
- 转换出来的音频数据过大,传输也比较耗时
- 需要根据文本内容给语音添加情绪
(描述遇到的问题)
我主要就是针对这些问题来进行优化。那么首先第一个问题和第二个问题其实是同一个问题,文本过大,自然传输到 TTS 服务所花费的时间就会越久,而且自然生成的音频数据也就越大,而且这一块儿用的是第三方服务平台,我是没法下手的。所以我就想到了从文本方面下手,将文本分段之后在发送请求,这样就大大降低了转换时间,并且转换出来的音频数据也不会太大。
另外因为文本分段了,所以大小虽然变小了,但是数量变多了,所以这一块儿我考虑使用并发的方式来发送,这样速度会更快一些。不过这里面都还有一些细节在里面。(钩子🪝)
至于添加情绪标记,这个前端不太好处理,涉及到自然语言的处理,而 python 是这一块儿的强项,所以当时和后端的同事商量,看他能不能搞定这一块儿。最终他还是搞定了,返回给我的是音频流数据。
我拿到音频流数据后,因为我们这个项目是有 BFF 层的,所以我在前端服务器对拿到了 bytes 数据做了一层转换,转换成 base64 数据,这样更方便页面上的 audio 元素使用。
另外我还做了一层缓存优化,在前端服务器上添加了一个数据库,将转换过的音频数据存储在了 mongodb 里面,这样能够减少重复的转换。(简单阐述了我是如何思考的,以及我根据思考落地下来的方案)
整体的优化方案落地后,响应式时间从之前的接近 1min 减少到几秒以内。(方案落地后的一个效果)你看面试老师您需要我把里面的细节展开来讲一下不?(钩子🪝)
# 知识点叙述
# 1. 文本切割
模拟问题:可以展开讲一讲。你先说一下文本切割这一块儿呢?
问题分析
- 简单介绍一下切割的不同方案
- 你选择的方案,为什么选择这种方案
- 自然引到并发,特别是并发控制这个点
参考答案
文本切割主要就是把一个大文本切割成多个小文本,这样能够加速 TTS 服务的处理数据。切割的时候需要考虑切割的粒度,有两种切割方案:
- 按照标点符号断句,然后切割
- 固定长度字符进行切割
这个项目里面我选择的是第一种切割方案,因为这样能保证句子完整,不会切断语义,而且也方便后面做情绪标记。(阐述我为什么选择第一种方案)
不过文本切割后会带来文本数量变多的问题,这里我采用的并发的方式来进行处理的,并且并发的时候还做了一些控制,防止服务器过载。这其实也是一个蛮重要的优化点,需要把这个点说一下么?
# 2. 并发控制与请求取消
模拟问题:可以,你说一下你是如何对并发进行控制的
问题分析
在回答的时候将并发控制有哪些关键的点讲出来,然后引出另外一个优化方针:缓存。
参考答案
并发控制这边我主要做了这么几件事情:
- 限制并发数量,这样能控制到达服务器的请求数量
- 使用队列管理和调度并发请求,确保新的请求在当前并发请求完成后才发出的
- 根据服务器和负载情况和网络状态,动态的调整最大并发请求数
查看网络状态这一块儿我之前是没有做的,不过当时因为做这个项目,我刚好也就去看了一些和网络相关的知识、博客、视频,发现这也是一个非常不错的优化点,就加上了。(凸显你自己的一个做事儿风格,并且还能增加真实性)
整套方案下来,就达到了并发发送多个文本的目的,同时又能对并发量进行一个很好的控制。
另外在“发送”这一块儿,我还做了一层优化,就是请求的取消。因为我考虑到这么一种情况,就是用户鼠标先 hover 一段文本,本来要对这段文本进行转换的,但是用户又 hover 到了其他文本,那么第一次的请求就不需要了,这里可以将其取消掉。
这就是整个“发送”这一块儿所做的优化。至于“响应”回来的数据,我则是通过设置缓存来进行优化的。所以整体的优化方案概括起来,就是一去一回,去的时候能做什么样的优化,数据回来后又能做什么优化。
# 3. 请求取消
模拟问题:你能说一下请求取消这一块儿,具体是如何做的么?
问题分析
- 简单介绍 AbortController
- 结合你的项目是怎么使用的
- 暗示面试官“请求”这一块儿的优化就这么多了
参考答案
请求的取消可以使用 AbortController,这是浏览器提供的一个 API,专门用于中止一个或多个 Web 请求。(简单介绍AbortController)
当时在项目里面,我给每一个切割的文本创建了一个 AbortController 实例,然后发请求的时候会将实例的 signal 一起放到请求里面,一旦涉及到需要取消请求,直接调用实例对象的 abort 方法做取消操作就可以了。(介绍项目里面具体是如何使用的)
关于请求这一块儿的优化差不多就是这两个地方,一个并发控制,一个请求取消,这两块儿搞定后,我觉得请求方面的就优化得差不多了,后面就是数据回来后也需要做一些优化。
# 4. 情绪标记
模拟问题:等一下,我对情绪标记这个点比较好奇,你说后端搞定了,他那边是如何搞定的呢?你有问过他么?
问题分析
在回答的时候大致介绍一下即可。
- 介绍一下这个技术涉及 XXX 领域
- 你们项目是如何处理的
参考答案
这个情绪标记涉及到了对自然语言文字的分析和处理,属于 python 的强项,python 生态中有很多的和自然语言处理相关的库。所以当时我和后端同事聊了一下,他刚好也会一些 python,所以他那边负责搞定了这个问题。
后来他告诉我好像是在服务器中设置了 python 服务,用到了一个 NLTK 的库专门来处理这个情绪标记的问题,具体细节我倒是不太清楚,因为我主要是负责前端这一块儿的 TTS 性能优化。(再次把话题拉回来)
# 5. 缓存
模拟问题:那你最后说一下缓存这一块儿的注意点有哪些呢?
问题分析
遇到这种情况,面试官询问的是注意点,你直接贴着你的项目来讲即可。讲一下项目里面缓存是如何落地的,以及你自己的一个心路历程。
参考答案
当时我在考虑缓存的时候,考虑了两种方案,一种是缓存在客户端,一种是用一个数据库,缓存在服务器端。
缓存在客户端的好处在于获取缓存的速度很快,可以采用 localstorage 来存,值存对应的 base64 数据,键则是对应的文本内容。不过这里有个细节的点要考虑,就是键如果直接用文本的话,可能一句话过长,导致键超出 localstorage 的长度限制,所以需要做一层 MD5 处理。本来都打算这么做了,后来我想了一下,客户端的缓存只能存当前用户转换过的文本,但是一般来讲,用户看完某篇知识文档后,转而就会去看下一篇,不太会一篇文档翻来复去听个十来遍,除非是一篇超高质量、需要反复玩味的文档。(整个这一段是在介绍你是如何思考的,整体的一个心路历程)
所以我改变了策略,决定缓存在前端服务器端,这样只要转换过某个文本,都能缓存起来,方便下一次使用。数据库方面我选择了我熟悉的 MongoDB 数据库。
整套优化方案落地下来,以前的一个长文本平均需要 30s 的转换时间,优化后分段转换,总时间降到几秒以内,而如果是存在缓存的情况下,基本上 1s 上下。(最后对整体的落地方案以及效果进行一个总结)
不过现在回想起这个项目,我认为缓存这一块儿仍然有优化的地方,例如可以设置双层缓存,客户端用 localstorage 缓存一次,客户端找不到,就去前端数据库去找,如果还是找不到,最后再请求 TTS 服务,就有点类似于 DNS 的解析流程一样,不知道我想这种方案面试老师您怎么看?
(完美收官)
-EOF-