谷歌开发者大会2017心得:Firebase 和 Serverless
Takeaways from Google Developer Days 2017: Firebase and Serverless
今天是谷歌开发者大会(GDG, Google Developer Days)在上海举办的第一天。由于GDG是在工作日举办,因此还不得不向公司告了假才能来参加。这届的GDG有非常多让人兴奋的议题。我打算写几篇文章介绍我在 GDG 2017 的收获(如果我偷懒了,可能也只有这一篇)。
这一篇文章的主题是 Firebase 和 Serverless。
在此之前,我对 Firebase 稍有了解,我们的 Quesbook 项目中有一个在线课堂,课堂上的实时聊天内容是使用 Firebase 存储的。这让我对 Firebase 有一个误解, 我以为它就单纯是一个实时数据库。但是,这次在 GDG 上,Firebase 让我大为惊叹 —— 它是一个全套的Serverless构解决方案,不仅有 Real-time Database,还有 Storage, Cloud Functions 和 Authentication ,让人可以不需要自己部署代码到服务器,就可以从0到1实现自己的应用。
GDG 上的例子
在下午的会议上,有两位演示者给我们介绍了用 Firebase 实现一个应用。这个应用可以将说话的人的语音录下来,然后再翻译成其他语言的文字展示给别人。这里面使用了 Firebase 的 Storage (语音文件和转录的文件)和Cloud Functions(Sound to Text 和翻译),只用了几十行代码,就实现了所有的必要功能,让人非常惊讶。
上面这幅图介绍了这个应用的架构,前端App通过SDK调用部署在 Firebase 定义的functions,比如上传语音文件,将其存储到 Storage。同时 Firebase 会监听文件的存储,当有语音文件写入后,就会调用 Cloud Functions 将其转化成文字再存储,而这又触发了另一个 function,它会将文字翻译成目标语言。
在这个架构中,我们可以看到,里面已经没有了server的角色——这便是serverless的由来。
整个代码也非常简单,以下代码都是在 functions/index.js
中:
首先,是引用需要的package:
const functions = require('firebase-functions');
const Speech = require('@google-cloud/speech');
const speech = Speech({keyFilename: "service-account-credentials.json"});
const Translate = require('@google-cloud/translate');
const translate = Translate({keyFilename: "service-account-credentials.json"});
const Encoding = Speech.v1.types.RecognitionConfig.AudioEncoding;
其次,是当语音文件写入时,它会触发下面这个 onUpload
事件,将文件读取出来,然后调用 Cloud Function: Speech 将其转化成 transcript 并写回 Storage:
exports.onUpload = functions.database
.ref("/uploads/{uploadId}")
.onWrite((event) => {
let data = event.data.val();
let language = data.language ? data.language : "en";
let sampleRate = data.sampleRate ? data.sampleRate : 16000;
let encoding = data.encoding == "FLAC" ? Encoding.FLAC : Encoding.AMR;
let request = {
config: {
languageCode : language,
sampleRateHertz : sampleRate,
encoding : encoding
},
audio: { uri : `gs://mimming-babelfire.appspot.com/${data.fullPath}` }
};
return speech.recognize(request).then((response) => {
let transcript = response[0].results[0].alternatives[0].transcript;
return event.data.adminRef.root
.child("transcripts").child(event.params.uploadId)
.set({text: transcript, language: language});
});
});
最后,是 onTranscript
函数,当它发现有 transcripts 写入(onWrite事件)时,则去调用 Cloud Function: Translate 将其翻译成目标语言:
exports.onTranscript = functions.database
.ref("/transcripts/{transcriptId}")
.onWrite((event) => {
let value = event.data.val();
let transcriptId = event.params.transcriptId;
let text = value.text ? value.text : value;
let languages = ["en", "es", "pt", "de", "ja", "hi", "nl", "fr", "pl"];
// All supported languages: https://cloud.google.com/translate/docs/languages
let from = value.language ? getLanguageWithoutLocale(value.language) : "en";
let promises = languages.map(to => {
console.log(`translating from '${from}' to '${to}', text '${text}'`);
// Call the Google Cloud Platform Translate API
if (from == to) {
return event.data.adminRef.root
.child("translations").child(transcriptId).child(to)
.set({text: text, language: from});
} else {
return translate.translate(text, {
from: from,
to: to
}).then(result => {
// Write the translation to the database
let translation = result[0];
return event.data.adminRef.root
.child("translations").child(transcriptId).child(to)
.set({text: translation, language: to});
});
}
});
return Promise.all(promises);
});
总共只有71行代码,而且这不是伪代码,而是完整地实现这个应用所需的全部后端功能。
这个项目完整的Github repo 在这里 Zero to App: Building a universal translator。不仅有上述提到的 functions 部分的代码,还有 Android/iPhone/Web 客户端的代码。
我们自己来试一试
其实 Firebase 官方的 Get Started 就已经非常全面了,里面包含了:
如何创建项目
如何启用 Firebase,以及创建 Storage
如何将文件写入Storage并触发函数调用
如何在本地安转和初始化
如何在本地测试和部署
我尝试跟着里面一步一步进行,几乎是没有遇到什么问题的。除了在本地测试这一步,它是利用了 Local Emulator Suite,它是依赖 Node >= 8 和 Java >= 11的,这一点没有在 Get Started 部分提到。如果自己尝试的话,需要先准备好环境。
其他趣闻
在 GDG 上午的 opening 中,我们见到了李飞飞。她回到中国,宣布了Google AI中国中心的成立。
此外,她还倡导:“AI没有国界,AI的福祉亦无边界”。