图片URL:http://www.google.co.nz/logos/2012/d4g_nz12-hp.jpg
这是2011年新西兰Doodle 4 Google获胜作品,名叫“我的新西兰愿望”,作者是庞森比小学的Liam Platt,他说他对新西兰的愿望是清洁无污染的环境和保护我们大自然的生灵。
图片URL:http://www.google.co.nz/logos/2012/d4g_nz12-hp.jpg
这是2011年新西兰Doodle 4 Google获胜作品,名叫“我的新西兰愿望”,作者是庞森比小学的Liam Platt,他说他对新西兰的愿望是清洁无污染的环境和保护我们大自然的生灵。
本文是 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。
在软件包层面,我们声明了一些数据结构,描述火鸡的各个元素、对应图像所在文件夹,以及各图像应绘制在背景图上的位置。
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 幅默认图)加载到全局变量中。
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(defaultImageFile)
backgroundImage = loadPNG(backgroundImageFile)
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))
}
}
}
请求按下述顺序处理:
layoutMap 判断应绘制的位置。)如果出错,则将 defaultImage 返回给用户,并在 App Engine 控制台记下日志,供日后分析之用。
下面是含说明注释的请求接管器代码:
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
图片URL:http://www.google.co.jp/logos/2011/doodle4google_japan11-hp.jpg
这幅题目叫“母亲”的doodle作品来自动静文京区笼町一年级的床井海心小朋友,他的作品在今天登上了Google日本主页,并获得了一台笔记本电脑(希望不是Chromebook……),其所在学校则获得价值2百万日元的电脑资助。
图片URL:http://www.google.co.in/logos/2011/d4g11-india-hp.jpg
在印度,儿童节的日子就是开国总理贾瓦哈拉尔·尼赫鲁的生日,也就是每年的11月14日。
Via 维基百科
图片URL:http://www.google.it/logos/2011/d4g_italy11-hp.jpg
这是2011年意大利Doodle 4 Google涂鸦大赛的获胜者,主题为“150年的意大利”,作者为小学生Borgo Rosselli,作品详情见此(意大利语)。
原始图片 url:http://www.google.pl/logos/2011/poland_d4g-hp.jpg
2011年波兰地区的 Doodle 4 Google 设计大赛结果公布,Martyna Króliszewskiej Murzasichle 设计的 Doodle 最终获得大奖并在7月1日波兰上任欧盟轮值主席的当天登上了 Google.pl 的首页。
原始图片 url:http://www.google.com.tr/logos/2011/maritimefestival11-hp.jpg
每年7月1日是土耳其伊斯坦布尔的海事&沿海运输节,我没有找到更多关于这个节日的说明,欢迎大家在留言中补充。
原始图片 url:http://www.google.com.gh/logos/2011/ghanad4g11_12-14-hp.jpg
今天 Google 加纳首页放上的是 2011 年 加纳 Doodle 4 Google 设计比赛 12-14 岁组的冠军作品,来自 Nana Abena Asabea Ametepe。昨天的 Nil Carreras Del Peso 是 9-11 岁组的获胜者。
Google宣布了2011年度美国区的Doodle 4 Google涂鸦大赛获奖作品(上图),是来自南旧金山Matteo Lopez小盆友的Space Life。
Matteo Lopez小盆友用该作品回答了这次比赛的主题“未来我会做啥”:“成为一名宇航员探索星空里的生命”,他的作品表现了未来人们宇宙旅行、在月球上行走甚至是跟外星人成为好朋友的场景。他获得了15,000刀的奖学金,一个上网本,他所在的Monte Verde Elementary School学校也可赢得25,000刀的奖金。他的作品将在美国时间2011年5月20日登上美国Google.com的首页。
再看看其它三个优秀奖:
威尔明顿4年级的Joseph Eugene Miller小盆友的作品“我的银河系”:

莫肯纳7年级的Justas Varpucanskis小盆友的作品“壮阔的大海”:

费耶特维尔11年级的Hannah Newsom小盆友的作品“幻想”:

Update:该作品已经登上Google.com美国区的首页,地址是:http://www.google.com/logos/2011/d4g11-matteolopez-HP.png
Via SEL

今天,美国地区的Doodle 4 Google投票开始,有来自40个地区的doodle作品从超过107,000幅作品中突出重围进入最后的决赛圈。
投票已经开始,直到太平洋时间5月13日夜里11:59截止,在5月19日将公布美国区的最终获奖作品并将在纽约进行颁奖仪式。获奖小朋友将获得15,000美元奖学金,其所在学校将获得25,000美元的奖金用于建设计算机教室,获奖的doodle除了将在5月20日登上Google.com首页展示以外,还将收录于纽约的Whitney Museum of American Art和旧金山的SFMOMA。
Via Google Blog