Fork me on GitHub

2011/05/11


このエントリーをはてなブックマークに追加
僕にとってデカいニュースが飛び込んできた。
Goの開発プロジェクトに関わってて良かった。今日はそう思える日になりそうだ。
まだGoogle App Engine Blogにもエントリされてないが、Google App EngineにGo言語が仲間入りした。
Downloads - Google App Engine - Google Code

Google App Engine SDK for Go

http://code.google.com/intl/en/appengine/downloads.html#Google_App_Engine_SDK_for_Go
Go言語は元々Google社員が開発している言語で、コンパイル型言語とは言えDuckTypingを前面に押し出した、スクリプト言語に近い仕様になっています。またWebに関するAPIは豊富で短いコードで簡単にWebアプリケーションを書く事が出来る様になっています。
例えば、先日書いたコマンドライン向けTwitter Clientであれば、OAuthライブラリこそ他に頼っているがメインのコードは350行だ。

さて今回公開されたGoogle App Engine for Goだが仕組みはGoが元々持っているWebサーバ機能を使っている。ソースはコンパイルせずに配置しapp.yamlファイルは以下の様に書く。
application: helloworld
vesion: 1
runtime: go
api_version: 1

handlers:
url: .*
  script: _go_app
この _go_app は固定。実行すると内部でコンパイルされ、applicationで指定したパッケージのinit関数が呼び出される。アプリケーションはあくまでパッケージとして提供し、main関数は書かない。なのでinitでハンドラを登録する。
極端に簡単なアプリケーションを書くとすると、以下の様になる。
package helloworld

import "http"

func init() {
    http.HandleFunc("/"func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type""text/plain")
        w.Write([]byte("Hello, World"))
    })
}
まぁ簡単。もちろんGoogle App Engine上で動く言語 Python や Java と同様に appspot.com でホストする場合はローカルディスクにはアクセス出来ない。ただし既に
  • blobstore
  • datastore
  • mail
  • memcache
  • taskque
  • urlfetch
  • user
という他の言語と同じだけのAPIは既に用意されている。Goにはポインタやアドレスという概念がある為にメモリに直接アクセスする様な事も出来るが、Goはその全てをランタイムを介して行っているのでそれらを例外として扱う事が出来る。
なお、appengine向けのGoランタイム上で動作する為、appspot.com でホストする場合にはコンパイル済みのモジュールを配置する事が出来ない。こちらは python で pyd が置けないのと同じですね。

まさかPerlよりも先にGoogle App Engine向け言語として採用されるとは思ってませんでした。ようやく日の目を浴びる事になりますね。

Go言語はじまったな

Posted at 10:13 in ソフトウェア::lang::go
Tagged as: appengine, golang, googleappengine
Bookmarks: add to hatena add to hatena | add to delicious.com | add to livedoor.clip add to livedoor.clip

2008/04/09


このエントリーをはてなブックマークに追加
アプリ第2号です。といってもflickr画像検索同様に有用な物ではありません。
pythonで動作するWebService::Simple「webSimple」を使ってネタバイザーのRSSから最新ネタを取得し、LingrのチャットルームにこれまたwebSimpleで発言するアプリです。
まずテンプレート

lingr.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>ネタバイザーのネタをlingrに転送</title>
<style tyle="text/css"><!--
body {
    font-family: 'メイリオ', 'Osaka'
}
#content {
    margin-left: 50px;
}
--></style>
</head>
    <body>
        <h1>ネタバイザーのネタをlingrに転送</h1>
        <div id="content">
        <p><a href="http://netaviser.woresukebe.com/" class="external" target="_blank">ネタバイザー</a>の最新発言を受信し、<a href="http://www.lingr.com/" class="external" target="_blank">Lingr</a>のチャットルーム「<a href="http://www.lingr.com/room/hO4SmQWTdJ4">LingrAPI Test</a>」に転送します。</p>
        <p>「ネタ転送」ボタンを押下して下さい。</p>
        <form method="post">
            <input type="submit" value="ネタ転送" />
        </form>
        <div>
            {% if neta %}
            ネタ「{{ neta|escape }}」を転送しました。
            {% endif %}
        </div>
        </div>
        <hr />
        <p style="text-align: center">provided by <a href="http://mattn.kaoriya.net">mattn</a>, hosted on google app server.</p>
    </body>
</html>

そしてハンドラ

lingr.py
#!-*- coding:utf-8 -*-
import os
import cgi
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch
from webSimple import Simple
import elementtree.SimpleXMLTreeBuilder as xmlbuilder
import xmllib

class MainPage(webapp.RequestHandler):
  def get_neta(self):
      neta = urlfetch.fetch('http://netaviser.woresukebe.com/index.xml').content
      parser = xmlbuilder.TreeBuilder()
      xmllib.XMLParser.__init__(parser, accept_utf8=1)
      parser.feed(neta)
      xml = parser.close()
      return xml.find('channel/item/title').text

  def post(self):
    api = Simple({
        'base_url' : 'http://www.lingr.com',
        'param' : {
            'api_key' : 'your-api-key',
            'format'  : 'xml'
        },
    })

    neta = self.get_neta()

    session = api.get({}, {
        'path' : '/api/session/create',
    }).parse_xml().find('session').text;

    ticket = api.get({
        'session'  : session,
        'id'       : 'hO4SmQWTdJ4',
        'nickname' : 'ネタバイザー転送サーバ',
    }, {
        'path' : '/api/room/enter',
    }).parse_xml().find('ticket').text;

    status = api.get({
        'session'  : session,
        'ticket'   : ticket,
        'message'  : neta,
    }, {
        'path' : '/api/room/say',
    }).parse_xml().find('status').text;

    api.get({
        'session'  : session,
    }, {
        'path' : '/api/session/destroy',
    })

    path = os.path.join(os.path.dirname(__file__), 'lingr.html')
    template_values = {
        'session' : session,
        'ticket'  : ticket,
        'neta'    : neta,
        'status'  : status,
    }
    self.response.out.write(template.render(path, template_values))

  def get(self):
    path = os.path.join(os.path.dirname(__file__), 'lingr.html')
    self.response.out.write(template.render(path, {}))

def main():
  application = webapp.WSGIApplication([('/lingr/', MainPage)], debug=True)
  wsgiref.handlers.CGIHandler().run(application)

で、動いている物がこちら
ネタバイザーのネタをlingrに転送
ネタバイザーに負荷が掛かりますので、あまりにリクエストが多い場合には停止させて頂く所存です。またネタバイザーの方が苦情があれば、これまた停止させて頂く所存です。

なお、先ほど修正したのですがPyWrapperのTreeBuilderはlibxml.XMLParserを初期化する際にutf-8を許可するかどうかのフラグ、「accept_utf8」を0のまま渡してしまっています。よってutf-8なXMLが通りませんでした。
初期化が冗長ですが、作ったTreeBuilderをlibxml.XMLParser.__init__で再初期化する様修正しています。