Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >从零到 Go:Google感恩节火鸡涂鸦开发纪实

从零到 Go:Google感恩节火鸡涂鸦开发纪实

作者头像
李海彬
发布于 2018-03-20 06:10:42
发布于 2018-03-20 06:10:42
1.2K00
代码可运行
举报
文章被收录于专栏:Golang语言社区Golang语言社区
运行总次数:0
代码可运行

文是 Google 搜索团队软件工程师 Reinaldo Aguiar 发表在 Go 语言博客的客座文章,他分享了在一天之内完成首款 Go 程序的开发并发布给数百万受众的经历。

我最近有幸参与了一项虽小却曝光率极高的“20% 项目”——2011 年感恩节的 Google Doodle。这幅 doodle 中的火鸡由不同样式的头、翅膀、羽毛与爪子随机组合而成。用户可以通过点击火鸡的不同部位自定义组合。这种互动通过 JavaScript、CSS 实现,由浏览器实时渲染出各种火鸡。

用户制作出的个性化火鸡可以分享到 Google+ 上。点击“分享”按钮(图中未给出)即可在用户的 Google+ 流中生成一篇含有火鸡图片的帖子。要满足这种需求,图片必须是单独一张,且与用户所制作的火鸡完全相同。

由于火鸡的八个部位(头、双爪、几片羽毛等)各有 13 种样式,用户可能设计出八亿多种火鸡。预先制作好八亿多张图片显然行不通。因此,必须在服务端实时生成图片。出于即时扩展性与高度可用性的共同需求,合适的平台非常明显:Google App Engine!

接下来要决定的就是选用哪款 App Engine runtime 了。图像处理任务极度依赖 CPU,所以这种情况下性能是决定性因素。

为确保可靠,我们首先进行了测试。我们为新版 Python 2.7 runtime(该版本提供基于 C 的图像处理库 PIL) 与 Go runtime 准备了一些等效的演示应用。各应用 分别合成几张小图片生成图像文件,编码为 JPEG,并将 JPEG 数据作为 HTTP 响应发回客户端。Python 2.7 应用处理请求的中位响应时间为 65 毫秒,而 Go 应用的中位延时仅为 32 毫秒。

因此这成为了试用 Go runtime 的大好机会。

此前我对 Go 语言毫无经验,而时间又很紧:两天内达到生产需求。虽然紧张,我还是将它视作从另一常被忽略的方面——开发速度——测试 Go 的机会。完全没有 Go 语言开发经验的人能在多快的时间内掌握并开发出高性能高扩展性的应用?

设计

基本步骤是在 URL 中编码火鸡各态、实时绘制并编码图像。

各 doodle 的基础是背景图画:

有效的请求 URL 形如:http://google-turkey.appspot.com/thumb/20332620

/thumb/ 后面跟着的数字字串(十六进制)代表各外观元素要绘制的形状,如下图所示:

程序的请求接管器解析 URL 决定各组件所选定的元素,在背景上绘制对应图像,并返回 JPEG 成品。

如果出错则返回默认图像。不必返回错误页面,因为用户不可能看到——浏览器肯定是在加载 image 标记中的 URL。

实现

在软件包层面,我们声明了一些数据结构,描述火鸡的各个元素、对应图像所在文件夹,以及各图像应绘制在背景图上的位置。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var (
 // 各外观元素存储位置的文件夹映射。
 dirs = map[string]string{
 "h": "img/heads",
 "b": "img/eyes_beak",
 "i": "img/index_feathers",
 "m": "img/middle_feathers",
 "r": "img/ring_feathers",
 "p": "img/pinky_feathers",
 "f": "img/feet",
 "w": "img/wing",
 }
// urlMap 映射各 URL 字符与所对应的外观元素。
 urlMap = [...]string{"b", "h", "i", "m", "r", "p", "f", "w"}
// layoutMap 映射各外观元素与在背景图像上的位置。
 layoutMap = map[string]image.Rectangle{
 "h": {image.Pt(109, 50), image.Pt(166, 152)},
 "i": {image.Pt(136, 21), image.Pt(180, 131)},
 "m": {image.Pt(159, 7), image.Pt(201, 126)},
 "r": {image.Pt(188, 20), image.Pt(230, 125)},
 "p": {image.Pt(216, 48), image.Pt(258, 134)},
 "f": {image.Pt(155, 176), image.Pt(243, 213)},
 "w": {image.Pt(169, 118), image.Pt(250, 197)},
 "b": {image.Pt(105, 104), image.Pt(145, 148)},
 }
 )

上述各点的几何位置是通过图像中各元素的实际位置而得到的。

每次请求都从磁盘加载图像是很浪费的重复行为,因此我们在收到首个请求时就将全部 106 幅图像(13×8 个元素 + 1 幅背景 + 1 幅默认图)加载到全局变量中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var (
 // elements 映射各外观元素及其图像。
 elements = make(map[string][]*image.RGBA)
 // backgroundImage 含背景图像数据。
 backgroundImage *image.RGBA
 // defaultImage 是出错时返回的图像。
 defaultImage *image.RGBA
 // loadOnce 用于仅在首次请求时调用 load 函数。
 loadOnce sync.Once )
 // load 函数从磁盘读取各 PNG 图像,并存储到对应的全局变量中。
 func load() {
 defaultImage = loadPNG(默认图像文件)
 backgroundImage = loadPNG(背景图像文件)
 for dirKey, dir := range dirs {
 paths, err := filepath.Glob(dir + "/*.png")
 if err != nil {
 panic(err)
 }
 for _, p := range paths {
 elements[dirKey] = append(elements[dirKey], loadPNG(p))
 }
 }
 }

请求按下述顺序处理: 1、解析请求 URL,按顺序解码出各字符的十进制值。

2、为背景图像创建副本,作为最终图像的基础。

3、在背景图像上绘制各图像元素(使用 layoutMap 判断应绘制的位置。)

4、将图像编码为 JPEG

5、将 JPEG 直接写入 HTTP 响应写入器中,将图像返回给用户。

如果出错,则将 defaultImage 返回给用户,并在 App Engine 控制台记下日志,供日后分析之用。

下面是含说明注释的请求接管器代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func handler(w http.ResponseWriter, r *http.Request) {
 // Defer 函数可以从错乱中恢复。
 // 恢复时将错误情况记录到 App Engine 控制台并给用户发送默认图像。
defer func() {
 if err := recover(); err != nil {
 c := appengine.NewContext(r)
 c.Errorf("%s", err)
 c.Errorf("%s", "Traceback: %s", r.RawURL)
 if defaultImage != nil {
 w.Header().Set("Content-type", "image/jpeg")
 jpeg.Encode(w, defaultImage, &imageQuality)
 }
 }
 }()
 // 在首次请求时从磁盘加载图像。
 loadOnce.Do(load)
 // 创建背景副本,作为绘制基础。
 bgRect := backgroundImage.Bounds()
 m := image.NewRGBA(bgRect.Dx(), bgRect.Dy())
 draw.Draw(m, m.Bounds(), backgroundImage, image.ZP, draw.Over)
 // 处理请求字串中的各个字符。
 code := strings.ToLower(r.URL.Path[len(prefix):])
 for i, p := range code {
 // 解码遇到的十六进制字符 p。
 if p < 'a' {
 // 是数字
 p = p - '0'
 } else {
 // 是字母
 p = p - 'a' + 10
 }
 t := urlMap[i] // 按索引查找元素类型
 em := elements[t] // 按类型查找元素图像
 if p >= len(em) {
 panic(fmt.Sprintf("元素索引越界 %s: "+
 "%d >= %d", t, p, len(em)))
 }
 // 将元素绘制到 m 上
 // 使用 layoutMap 指定其位置。
 draw.Draw(m, layoutMap[t], em[p], image.ZP, draw.Over)
 }
 // 编码为 JPEG 图像并写为响应。
 w.Header().Set("Content-type", "image/jpeg")
 w.Header().Set("Cache-control", "public, max-age=259200")
 jpeg.Encode(w, m, &imageQuality)
 }

为简洁起见,这些代码段中我省略了一些辅助函数。完整代码请参阅源码

性能

该图表从 App Engine 控制台截取,展示了发布后的平均请求时间。显然,即使在高负载情况下也没有超过 60 ms,中位延迟时间为 32 ms。考虑请求接管器在处理图像并实时编码,这已经相当快了。

结论

我觉得 Go 语言的语法直观、简单且洁净。我过去常与解析型语言打交道,尽管 Go 是静态录入编译型语言,编写这款应用的感觉却更像是在用动态解析型语言。

开发服务器提供了可以在程序有变动后迅速重新编译的 SDK,所以开发部署与解析型语言一样快。而且非常简单——我只花了不到一分钟就配置好了开发环境。

Go 语言优秀的文档也帮助了我迅速完成开发。文档是从源代码生成的,各函数的文档与相关联的源码直接链接。这不仅可以让开发者迅速理解特定函数的作用,还鼓励开发者深入挖掘软件包的实现,简化了对良好编程风格与规则的掌握。

编写这款应用的过程中,我只参考了三份资源:App Engine 的 Hello World Go 示例Go 软件包文档以及一篇演示 Draw 软件包的博文。感谢开发服务器的迅速部署,以及该语言自身的优异特性,我得以在 24 小时内掌握该语言,并开发出超快、满足生产需求的 doodle 生成器。

应用的完整源码(包括图像文件)可以在 Google Code 项目中下载到。

向设计该 doodle 的 Guillermo Real 与 Ryan Germick 致以特别的谢意。

原文:From zero to Go: launching on the Google homepage in 24 hours

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
2011年12月13日 Go生态洞察:从零到Go,在谷歌首页上的24小时飞跃
🔍 搜索词条:Go语言, Google Doodle, 开发速度, App Engine, 图像处理
猫头虎
2024/04/09
1060
2011年12月13日 Go生态洞察:从零到Go,在谷歌首页上的24小时飞跃
Golang-实现图片缩放
package main import ( "fmt" "graphics" "image" "image/png" "log" "net/http" "os" "strconv" "strings" ) func main() { http.HandleFunc("/", doImageHandler) http.ListenAndServe("127.0.0.1:6789", nil) } func doImag
李海彬
2018/03/20
3.8K0
Go每日一库之120:image(标准库图形处理)
在开发中,有时会遇到对图片的处理需求,在 Python中, PIL/Pillow 库非常强大和易用。 而 Golang 语言中,处理图片的标准库 image也可以实现一些基本操作。 image 库支持常见的 PNG、JPEG、GIF 等格式的图片处理, 可以对图片进行读取、裁剪、绘制、生成等操作。
luckpunk
2023/09/30
2.5K0
2种方式!带你快速实现前端截图
导语 | 本文将介绍在前端开发中页面截图的两种方式,包括对其实现原理和使用方式进行详细阐述,希望能为更多前端开发者提供一些经验和帮助。 一、 背景 页面截图功能在前端开发中,特别是营销场景相关的需求中, 是比较常见的。比如截屏分享,相对于普通的链接分享,截屏分享具有更丰富的展示、更多的信息承载等优势。最近在需求开发中遇到了相关的功能,所以调研了相关的实现和原理。 二、相关技术 前端要实现页面截图的功能,现在比较常见的方式是使用开源的截图npm库,一般使用比较多的npm库有以下两个: dom-to-image
腾讯云开发者
2022/03/03
4.2K1
谈谈水印实现的几种方式
日常工作中,经常会遇到很多敏感的数据,为防止数据的泄露,我们要在数据上做一些”包装“。目的就是让那些有心泄露数据的”不法分子“迫于严重的”舆论压力“而放弃不法行为,使之”犯罪未遂“,达到不战而屈人之兵的效果。而在安全部门工作的我们,数据安全的观念早已深入骨髓,每个文字,每张图片,都要留心是否有泄露的风险,怎么防止数据泄露,是我们一直思考的问题。比如图片的水印,就是我们工作过程中经常涉及到的问题。因为本身工作内容就是审核平台的开发,经常有一些风险图片会在审核平台出现,考虑到审核人员的安全意识参差不齐,所以为防止不安全的事情发生,图片增加水印的工作是必须要做的。
@超人
2021/10/12
1K0
创建一个具有背景轮播和3D卡片翻转效果的个人名片网页
在本篇博客中,我们将学习如何创建一个具有多个功能的个人名片网页。这个项目包括背景轮播效果和3D卡片翻转效果,适合前端开发初学者。
命运之光
2024/03/20
2110
创建一个具有背景轮播和3D卡片翻转效果的个人名片网页
3 分钟带你通过 Go 生成宣传海报!
对于公网应用来说,海报功能是非常非常重要的,它不仅能扩大应用的知名度,还能起到营销的作用。
小锟哥哥
2022/12/05
1.5K0
3 分钟带你通过 Go 生成宣传海报!
Rust vs Go:常用语法对比(九)
题图来自 Golang vs Rust - The Race to Better and Ultimate Programming Language
fliter
2023/09/05
2550
Rust vs Go:常用语法对比(九)
Go 每日一库之 plot
本文介绍 Go 语言的一个非常强大、好用的绘图库——plot。plot内置了很多常用的组件,基本满足日常需求。同时,它也提供了定制化的接口,可以实现我们的个性化需求。plot主要用于将数据可视化,便于我们观察、比较。
用户7731323
2020/09/08
1.4K0
9.27【前端开发】图片文件格式:常见的图片格式对比有何优劣以及如何使用Google的webp格式?
总结一下,对于色彩与图像内容比较丰富,变化比较多端的,适合使用jpg,例如大型背景、头像、人物照片等。对于颜色单一,有大色块的图像,例如图标等,适合用png,压缩效率高,并且有透明。小动画可以使用gif,便不适合使用复杂的视频。
LIYI
2020/10/09
2.4K0
9.27【前端开发】图片文件格式:常见的图片格式对比有何优劣以及如何使用Google的webp格式?
图片处理不用愁,给你十个小帮手
本文阿宝哥会为小伙伴们隆重介绍用于图片处理的十个 “小帮手”,他们各个身怀绝技,拥有模糊、压缩、裁剪、旋转、合成、比对等技能。相信认识他们之后,你将能够轻松应对大多数的图片处理场景。
阿宝哥
2020/06/23
5.2K0
【Python从入门到精通】(二十六)用Python的PIL库(Pillow)处理图像真的得心应手
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦。本篇重点介绍Python处理图像的标准库PIL库,处理图像真的的很方便。 干货满满,建议收藏,需要用到时常看看。小伙伴们如有问题及需要,欢迎踊跃留言哦~ ~ ~。
码农飞哥
2021/10/08
3.7K0
Python OpenCV3 计算机视觉秘籍:6~9
变量之间的线性相关性是所有可能选项中最简单的。 从近似和几何任务到数据压缩,相机校准和机器学习,它可以在许多应用中找到。 但是,尽管它很简单,但是当现实世界的影响发挥作用时,事情就会变得复杂。 从传感器收集的所有数据都包含一部分噪声,这可能导致线性方程组具有不稳定的解。 计算机视觉问题通常需要求解线性方程组。 即使在许多 OpenCV 函数中,这些线性方程也是隐藏的。 可以肯定的是,您将在计算机视觉应用中面对它们。 本章中的秘籍将使您熟悉线性代数的方法,这些方法可能有用并且实际上已在计算机视觉中使用。
ApacheCN_飞龙
2023/04/27
2.5K0
Python OpenCV3 计算机视觉秘籍:6~9
使用计算机视觉实战项目精通 OpenCV:1~5
本章将向您展示如何为 Android 智能手机和平板电脑编写一些图像处理过滤器,该过滤器首先针对台式机(使用 C/C++)编写,然后移植到 Android(使用相同的 C/C++ 代码,但使用 Java GUI), 这是为移动设备开发时的推荐方案。 本章将涵盖:
ApacheCN_飞龙
2023/04/27
2.3K0
对html中的图片进行深度实践,一个简单到爆的知识点,到底要不要看?
我们继续,深入剖析一下图像的大小、宽度和高度这两个知识点,在上面的例子中,直接使用了width和height属性指定了宽度和高度。那么,其实还可以使用style属性指定图像的宽度和高度,下面我们一起来看看小栗子:
不背锅运维
2022/07/04
7170
对html中的图片进行深度实践,一个简单到爆的知识点,到底要不要看?
精通 Python OpenCV4:第一部分
在本书的第一部分中,将向您介绍 OpenCV 库。 您将学习如何安装开始使用 Python 和 OpenCV 进行编程所需的一切。 另外,您还将熟悉通用的术语和概念,以根据您所学的内容进行语境化,并为掌握本书的主要概念奠定基础。 此外,您将开始编写第一个脚本以掌握 OpenCV 库,并且还将学习如何处理文件和图像,这是构建计算机视觉应用所必需的。 最后,您将看到如何使用 OpenCV 库绘制基本和高级形状。
ApacheCN_飞龙
2023/04/27
3.1K0
【基础系列】Canvas专题
        为了在canvas上绘制,你必须先得到一个画布上下文对象的引用,用本方法即可完成这一操作,格式如下:
江中散人_Jun
2023/10/16
4000
【基础系列】Canvas专题
现代图片性能优化及体验优化指南
图片资源,在我们的业务中可谓是占据了非常大头的一环,尤其是其对带宽的消耗是十分巨大的。
Sb_Coco
2023/04/01
1.6K0
现代图片性能优化及体验优化指南
全栈之前端 | 9.CSS3基础知识之图像元素样式学习
描述: 好久没更新此《#前端学习之路》专栏文章了,不知道学习前端看友们想不想我,所以这一段时间又开始整理更新了。通过前面几章的学习,相信大家已经对CSS有了一个简单的了解吧,现在我们又回到使用频率较高的 文本 text、图像 images、媒体 media、表单 table 等元素CSS样式的设置,此章节主要讲解针对图像的相关CSS属性以其使用的示例演示。
全栈工程师修炼指南
2023/12/18
3190
全栈之前端 | 9.CSS3基础知识之图像元素样式学习
重新认识下网页水印
单独使用 css 实现,使用 backgroundImage,backgroundRepeat 将背景图片平铺到需要加水印的容器中即可。如果希望实现旋转效果,可以借助伪元素,将背景样式放到伪元素中,旋转伪元素实现:
前端小书童
2023/09/02
2710
重新认识下网页水印
推荐阅读
相关推荐
2011年12月13日 Go生态洞察:从零到Go,在谷歌首页上的24小时飞跃
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验