読者です 読者をやめる 読者になる 読者になる

UUUM攻殻機動隊

UUUMのエンジニアによる技術ブログです

CSRF(クロスサイトリクエストフォージェリ)

エンジニアのspin13です. しばらくの間,地球そのものが敵となって花粉に苦しめられていました.

今回は クロスサイトリクエストフォージェリ(以後CSRF)について書きます.

続きを読む

GoとNode.jsで作った簡易サーバーのパフォーマンスを比較してみた

エンジニアのタナカです。
UUUMではiOSアプリを開発をしています。

アプリを開発するにあたって、バックエンドに何を使えばいいか悩んだりしませんか? バックエンドを決めるには、様々な側面から考える必要があると思いますが、今回はパフォーマンスだけに着目して検討してみました。

ということでGoとNode.jsで簡易WEBサーバーを作って比較してみたいと思います。おまけでRubyも比較してみました。
パフォーマンスで考えた場合にRubyを選ぶことはあまりないとは思いますが、参考までに。

検証環境

  • Go go1.6.2 darwin/amd64
  • Node.js v5.10.1
  • Ruby ruby 2.2.4p230

※GOMAXPROCSは設定していません(マルチコアを使用できるGoが有利な環境)

検証コード

UNIX TIMEを返すだけの簡単なサーバーを用意しました。

Go

標準パッケージのみで作成

package main

import (
    "fmt"
    "net/http"
    "time"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "%d", time.Now().Unix())
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Node.js

Expressは使わず標準パッケージで

var http = require('http');

var server = http.createServer(function (request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end(String(Math.floor( (new Date()).getTime() / 1000 )));
});

server.listen(8080);

Ruby

こちらもwebrick等は使わずに

require 'socket'
require 'time'

server = TCPServer.new 8080
loop do
  client = server.accept
  client.gets
  client.puts "HTTP/1.0 200 OK"
  client.puts "Content-Type: text/plain"
  client.puts
  client.puts Time.now.to_i
  client.close
end

比較1

Apache付属のabコマンドを使用しました。 今回は、同時100接続のベンチマークを取りたいと思います。そのため以下のオプションを指定しました。

ab -n 100 -c 100 http://127.0.0.1:8080/

結果1

ローカルで動かしたので正確な値ではありませんが、以下の結果になりました。

Go

Concurrency Level:      100
Time taken for tests:   0.008 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      12700 bytes
HTML transferred:       1000 bytes
Requests per second:    12584.95 [#/sec] (mean)
Time per request:       7.946 [ms] (mean)
Time per request:       0.079 [ms] (mean, across all concurrent requests)
Transfer rate:          1560.83 [Kbytes/sec] received

Node.js

Concurrency Level:      100
Time taken for tests:   0.046 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      11100 bytes
HTML transferred:       1000 bytes
Requests per second:    2161.69 [#/sec] (mean)
Time per request:       46.260 [ms] (mean)
Time per request:       0.463 [ms] (mean, across all concurrent requests)
Transfer rate:          234.32 [Kbytes/sec] received

Ruby

Concurrency Level:      100
Time taken for tests:   0.007 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      5300 bytes
HTML transferred:       1100 bytes
Requests per second:    14341.03 [#/sec] (mean)
Time per request:       6.973 [ms] (mean)
Time per request:       0.070 [ms] (mean, across all concurrent requests)
Transfer rate:          742.26 [Kbytes/sec] received

「Requests per second」が1秒間に処理されたリクエスト数なので、この数値が大きい方がパフォーマンスがよいことになります。
意外にも一番よいスコアーだったのはRubyでした。 僅差でGo、Node.jsは1桁低いスコアーでした。

今回用意した検証コードがI/OよりCPUリソースが必要だったのか、シングルスレッドのNode.jsにとっては不利なものだったのかもしれません。

同時100接続ではRubyのスコアーがよかったですが、同時2000接続では、Rubyは応答できませんでした。
また、Goのスコアー7329.66に対してNode.jsのスコアーは3914.49と差が小さくなっていました。
Node.jsはアクセスが多い場合に力を発揮するようです。

比較2

比較1で使用したabコマンドでの同時接続数を100づつ増やして上限を確認してみました。

結果2

Go

同時2300までOK、2400でエラー

Node.js

同時2100までOK、2200でエラー

Ruby

同時120までOK、130でエラー

同時接続数はGoが一番よい結果が出ました。
Node.jsもGoに続くよい結果でした。 Rubyが少なすぎる気がしたのでwebrickでも試してみましたが、同じような結果でした。

まとめ

応答速度、同時アクセス数とも、Goのパフォーマンスが優れています。 通信回数の多いゲーム等を開発する場合は、バックエンドにGoを使うとよいかもしれません。

あと、処理内容にもよると思いますが、同時100アクセスくらいならRubyで十分かもしれません。

Ansibleことはじめ

こんにちは、エンジニアのナカハシです。

UUUMのインフラ構築はAnsibleで構築することが恒例になっており、絶賛開発中のRailsアプリも当然Ansibleでインフラを記述しています。

つまり、AnsibleのPlayBookを読めないと既存のインフラもさっぱり理解できなくなるわけで、Webアプリの実装ばかりやっている私としてもさすがにヤバいのではないかと思い、改めてAnsibleに入門してみることにしました。

続きを読む

SVGの基本的な仕組みについて

どうも、えんじにあのやまぐちです。

先日UUUMで社内運動会が行われ、攻殻機動隊も数名参加しました。 未だに筋肉痛がとれず日常生活に支障がでていますが、普段動かない仕事なだけに久しぶりにスポーツをして楽しかったです。 ちなみに我らのCTOは女性参加型のドッジボールで全力投球をかましていました。

さて、今回の記事ですが、SVGについて書かせていただこうと思います。
3年前ぐらいからよく目にしていたのですが、閲覧環境が整っていない感があったので取り敢えずスルーしていました。しかし、弊社のWebサイトでもSVGが使用されていたのを発見したので、この機会にどういう仕組なのか少し調べてみました。

SVGとは

SVGとはScalable Vector Graphicsの略です。一般的な画像はJPGやPNGといったラスタ形式(ビットマップ)ですが、SVGはベクタ形式で表現します。
ベクタ形式の特徴はアンカーと呼ばれる座標を複数指定し、そのアンカーを線で繋いだり、囲まれた範囲内を塗ることで描画します。

ベクタ形式とラスタ形式

ベクタ形式とラスタ形式の最大の特徴はリサイズによる画像の劣化があげられます。 ラスタ形式はドットが集合した画像なのでリサイズ時に画像が劣化する場合がありますが、ベクタ形式では先ほど説明したように座標を複数の点で指定して描画するため、リサイズ時に劣化がほとんどありません。

形式 利点
ベクタ 直線や曲線などで構成された図形
ラスタ 写真や風景など複雑なグラフィック

SVGはXMLベースの文書形式で作成されているため、テキストエディタで編集することが可能です。画像と違いテキスト形式なのでバージョン管理システムとの相性が良いとも言われています。 しかし、手軽に編集ができるため容易に改ざんすることも可能です。そのためセキュリティには気をつけなければなりません。

ブラウザのサポート状況

f:id:tky_ymgc:20161018195144p:plain

引用元: http://caniuse.com/#feat=svg-html5

モダンブラウザにはほぼ対応済みです。しかしIE8など一部のブラウザで対応できていないのでフォールバックする必要はあるかと思います。 例えば、ModernizrといったJSライブラリを用いてブラウザのSVGサポート状況を判別し、サポート外であった場合にPNG画像などで代替するといったことが必要となります。

Modernizr

HTML5やCSS3などの新しいWebの標準がWebブラウザに実装されているか、汎用的なインタフェースを通じて確認することができるJavaScriptライブラリ

SVGとCanvas

CanvasはHTNL5でグラフィックを表現するための要素です。特徴としてはラスタ形式(ビットマップ)で描画され、画像を別形式で保存しやすいことです。ここがSVGとの最大の違いです。
SVGとCanvasはそれぞれ適しているケースで使い分けると良いです。

SVGが適しているケース

  • 円や四角形などの図形の組み合わせで構成されている
  • ベクタ形式なので拡大、縮小で図形の劣化が少ない方が良い
  • 外部ファイルとして受け渡しを行い、別のHTML上でも表示させたい

Canvasの方が適しているケース

  • ビットマップ形式なのでピクセル単位での細かい描画を行いたい
  • 描画した画像を別形式の画像(PNGなど)で保存したい

SVGの埋め込み方法

SVGにはいくつかの埋め込み方法があります。 通常の画像ファイルのようにHTMLのimgタグやCSSのbackground-imageで読み込む方法や、SVG要素として埋め込む方法、外部メディアとして読み込む方法があります。

画像としてHTMLに埋め込む

<img src="assets/svg/image.svg" alt="" width="100" height="100">

画像としてCSSに埋め込む

.svg {
    background-image: url(assets/svg/image.svg);
}

外部メディアとして埋め込む

<object data="assets/svg/image.svg" width="300" height="300" codetype=“image/svg+xml"></object>

要素として埋め込む

<svg width="300" height="300">
    <line x1="0" y1="0" x2="300" y2="300" stroke="black" stroke-width="1" />
</svg>

基本的な図形

テキスト

<svg width="300px" height="300px">
    <text x="100" y="100">Hello World</text>
</svg>

テキストは<text>タグを使用します。左上が起点となり、X座標とY座標の指定した位置からテキストが描画されます。

直線

<svg width="300px" height="300px">
    <line x1="0" y1="0" x2="100" y2="100" stroke="black" stroke-width="1" />
</svg>

直線は<line>タグを使用します。X座標1、Y座標1からX座標2,Y座標2までを直線で結びます。属性にstrokeを記述することで線の色や太さを指定することができます。

四角形

<svg width="300px" height="300px">
    <rect x="10" y="10" width="100" height="100" stroke="black" stroke-width="1" fill="red" />
</svg>

四角形は<rect>タグを使用します。左上が起点となり、X座標とY座標の指定した位置から四角形が描画されます。widthとheightを使用して大きさを指定します。

円形

<svg width="300px" height="300px">
    <circle cx="100" cy="100" r="50" stroke="black" stroke-width="1" fill="blue" />
</svg>

円形は<circle>タグを使用します。CX座標とCY座標で円の中心地点を指定し、rで半径の大きさを指定します。

パス図形

<svg width="300px" height="300px">
  <path stroke="black" stroke-width="1" d="M20,70 h60l-30,60z"/>
</svg>

パス図形は<path>タグを使用します。任意の図形を描画することができるので自由度は高いですが、上記の図形よりも少し複雑になります。 パス図形ではd属性(パスデータ)を指定することで曲線を描画することができます。d属性にはパスコマンドをカンマやスペース区切りで複数記述し、前から順に実行していき、曲線で囲まれた領域がパス図形となります。

以下は基本的なパスコマンドの一覧です。

基本的なパスコマンド

コマンド 操作
M, m 初期位置
L, l 直線を引く
H, h 水平線を引く
V, v 垂直線を引く
C, c \ S, s \ Q, q \ T, t 曲線を引く
A, a 円弧を引く
Z, z 線を閉じる

※大文字は絶対位置を指定、小文字は相対位置を指定する

【Rails】Rubyテンプレートで axlsx を出力する方法

ハローこんにちは、新人エンジニアのハトネコエです。

2016年10月22日発売の 『WEB+DB PRESS Vol.95』
UUUM のエンジニア達が書いた特集『試して学ぶHTTP』が載ります。よろしくお願いします。

WEB+DB PRESS Vol.95

WEB+DB PRESS Vol.95

  • 作者: 小出淳子,黒澤剛志,牧大輔,横江亮佑,山口貴也,尾藤正人,佐藤琢哉,中橋研太郎,田中慎司,小西裕介,伊藤直也,稲富駿,前島真一,長野雅広,山際康貴,のざきひろふみ,うらがみ,岡林大,遠藤雅伸,ひげぽん,海野弘成,はまちや2,竹原,大場寧子,大場光一郎,野々下裕子,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2016/10/22
  • メディア: 大型本
  • この商品を含むブログを見る

Rubyテンプレート

Rails4になってcsvが出力しやすくなった - Qiita

ここの記事にあるように、view テンプレートとして .ruby ファイルが使えるようになっています。
長くなりがちな CSV 出力の処理を controller 内でなく別ファイルに書けるので controller がすっきりしますね。

Excel 出力もこんなふうにしたい

Excel で表データを扱うことを前提にすると、CSV では、文字コードとか先頭に0を詰める出力がつらい。
CSV でなく Excel 用の .xlsx 形式で書き出したいよねーというわけで、
お世話になっているのが Axlsx という gem。

これで上記のような方法で controller と出力処理を分離しつつエクスポートする方法を探りました。

続きを読む

JSON型にindexも!MySQL 5.7のGenerated Columnsの可能性について探る

nazoです。

今回はMySQL 5.7の新機能であるGenerated Columnsについて紹介したいと思います。

Generated Columnsとは?

簡単に言うと、トリガーで特定のカラムにデータを入れるのを簡単に定義する方法みたいなもので、カラム定義時にAS 計算式と書くことで、そのカラムの値が該当の計算結果になります。

この機能はMySQL 5.7.6から追加されました。日本語では「生成列」と呼ぶこともあるようです。

続きを読む

BabelでES6で書いて、webpackでビルドして、mochaでテスト書いて、power-assertでassertの出力を見やすくして、karmaで複数ブラウザのテストを自動化して、カバレッジを出力するようにした

おはこんばんちは!! 尾藤 a.k.a. BTOです。

UUUMでは業務の効率改善に kintone を導入しようとしているのですが、kintone の機能を拡張するには Javascript でプログラムを書く必要があります。 そこで、Javascript の開発環境を構築したので、何をやったのかをまとめてみました。

具体的には、 BabelでES6で書いて、webpackでビルドして、mochaでテスト書いて、power-assertでassertの出力を見やすくして、karmaで複数ブラウザのテストを自動化して、カバレッジを出力するようにしました。 お前は何を言っているんだと思うかもしれませんが、僕も何を言っているのかわかりません。

Babel

言わずと知れた ES6 用のトランスパイルツールです。 ご存知の方も多いと思うので、詳細は割愛しますね。

Babel の導入は npm でインストールするだけ

% npm install babel babel-preset-es2015 --save-dev

babel-preset-es2015 は ES6(別名ES2015)用のプリセットです。 これでES6で記述できるようになります。

Babelは .babelrc というファイルがあると、その設定を読み込んでくれます。 ここに es2015 のプリセットを使用するように設定します。

{
  "preset": ["2015"]
}

Polyfill

Babel でトランスパイルすることで、古いブラウザでも動作する Javascript を出力できるようになったのですが、古いブラウザの標準オブジェクトの中には ES6 に対応していないものがあります。 そのために標準オブジェクトを拡張する Polyfill を導入します。

% npm install babel-polyfill --save-dev

わざわざ Polyfill が別ライブラリに分かれているのには、ちゃんと意味があります。 標準オブジェクトを拡張することは、既存の環境と互換性を失うことになります。 互換性を失うのは危険を伴うので、別ライブラリに分けているのです。 Polyfill を導入するときは、標準オブジェクトを拡張していることをちゃんと認識したうえで使う必要があります。

babel-runtime

上記のように Polyfill には標準オブジェクトを拡張するという問題があるので、それをしないようにする実装もあります。 それが babel-runtime です。 babel-runtime は標準オブジェクトの対応していない機能を、その場で別のコードにトランスパイルして解決します。

Polyfill を使うのか babel-runtime を使うのかは、決めの問題だと思うので、どちらかを導入すれば良いと思います。 Polyfill を導入しているケースが多いように思われますので、個人的には Polyfill でいいんじゃないかと思っています。

webpack

ブラウザで実行するには、複数に分けられたjsファイルを1つにして読み込まないといけません。 複数ファイルを読み込むことももちろんできますが、依存関係の解決とか大変ですよね。 そのためにビルドツールを使うわけですが、今回は webpack を採用しました。

webpack だと browserify と違って複数ファイルを生成するのが簡単にできるそうです。 (browserify使ったことないので、詳細はわかりません)

% npm install webpack babel-loader --save-dev

インストールすれば、すぐに webpack が使えます。 簡単ですね。 webpack は様々形式のデータを扱うのに、プラグイン形式で対応しており、babel を使う場合は babel-loader を使います。

これだけだと webpack がどのようにファイルをビルドするかがわからないので、設定ファイルを書きます。

webpack.config.js

webpack は設定ファイルを webpack.config.js で書くのですが、どうせなら設定ファイルも ES6 で書きたいですよね。 これをやるのはとても簡単で、bable-register をインストールすれば、 webpack.config.babel.js というファイルで設定を ES6 で書くことができます。

% npm install babel-register --save-dev

interpret

babel-register をインストールするだけで、なぜ ES6 で設定ファイルを書くことができるようになるのでしょうか。 そこには interpret というライブラリの存在があります。

interpret の仕組みはとても簡単で、設定された拡張子に対して動的な前処理を適用するだけです。 interpret の方で、 .babel.js という拡張子に対しては、 babel-register で変換して処理するという風に定義されていますので、 webpack.conf.babel.js でも問題なく処理できるというわけです。

つまり、interpret に対応しているツールに関しては、babel-register をインストールすれば、もれなく ES6 にも対応できるようになります。

JSのように逐次解釈して実行する言語をインタプリタ言語と言いますが、 interpret はインタプリタと同じ意味(品詞は違う)の単語で、とてもわかりやすい名前ですね。

webpack.config.babel.js

webpack.config.babel.js には次のような設定を書きました。

export default {
  entry: {
    main: './src/main.js',
    kintone: './src/kintone.js',
  },
  output: {
    filename: '[name].js',
    path: `${__dirname}/web`,
  },
  module: {
    loaders: [{
      test: /\.js$/,
      include: [
        `${__dirname}/src`,
        `${__dirname}/test`,
      ],
      loader: 'babel',
    }],
  },
};

entry には、どのファイルをビルドするのかを指定します。 ここでは、2つ(main, kintone)を指定しています。 このように、複数のファイルを指定できるのが、webpackの良いところですね。

output には、どのように出力するかを定義します。 ここでは web ディレクトリにビルド結果を出力するようにしています。

module.loader でローダの指定をします。 ここでは、 .js のファイルに対して、 babel-loader を使用して babel でトランスパイルするように指定しています。 include ではローダの対象となるファイルを指定しています。

相対パス地獄を解決する

相対パス地獄は ES6 だけでなく他の言語でも見られますが、みなさんも

require('../../../../foo.js');

のような相対パスでのモジュールの読み込みで苦い思いをした経験があるのではないでしょうか。 これは webpack の機能を使うことで解決できます。

webpack の設定の resolve.root でディレクトリを指定すれば、そのパスを起点としたモジュールが読み込めるようになります。 webpack.config.babel.js に記述を追加します。

  resolve: {
    root: [
      `${__dirname}/src`,
    ],
  },

この設定だと、例えば src/uuum/foo.js は、

import from 'uuum/foo';

のような記述で読み込みができるようになります。

ここで注意しないといけないのが、この機能は webpackが提供している機能 だというところです。 なので、 babel 単体ではうまく処理することができませんので、 webpack を通さない処理だと当然使用することができません。 例えば、 webpack.config.babel.js は、 webpack を通さずに直接 babel でトランスパイルするので、もし src/ 以下のファイルを読み込んでいた場合は、import に失敗します。(NODE_PATHを設定すれば大丈夫)

ソースマップ

Babel で ES6 で記述できるようになったのは良いのですが、ブラウザ上で実際に実行されるのは、トランスパイルされた Javascript なので、ブラウザ上でエラーが出た場合に元のソースコードのどこでエラーが発生したのかがわかりません。 それがわかるようにするためにソースマップを生成しておくと、オリジナルのソースコードのどこに対応するのかがわかるようになるので、デバッグが楽になります。

ソースマップを追加するのは devtoolinline-source-map を設定するだけです。

  devtool: 'inline-source-map',

名前の通り、この設定を追加すると生成されるファイルの中にソースマップが設定されます。 他にもソースマップの生成方法はいろいろありますが、詳しくはマニュアルをご参照ください。

ここでは1つ課題があって、本番環境ではソースマップは必要ないんですが、現状だと本番用でもソースマップが追加されてしまっています。 なので、本番環境と開発環境でわけないといけないのですが、 webpack.config.prod.babel.js の用に環境ごとに設定ファイルを分けるのが一般的なようですね。 これはおいおい解決していこうと思ってます。

Mocha

テストツールには Mocha を採用しました。 Mocha はシンプルな TDD/BDD 対応のテストツールです。 テストに必要な基本的な部分のみ実装していて、 assertion ですら外部のライブラリを使用します。 そのため時流に乗って適切なライブラリと組み合わせることができ、その点が気に入っています。

Mocha はインストールするだけで、すぐに使えるようになります。

% npm install mocha --save-dev

テストディレクトリは標準の test/ にします。

test/mocha.opts に Mocha のコマンドラインのオプションを書いておくと、自動的に適用されるようになります。 こうしておけば、 mocha コマンドを叩くだけでテストが実行できるようになります。

mocha.opts には次のような設定を書きました。

--compilers js:babel-register
--recursive
--require babel-polyfill
--require src/kintone

1行目の --compilers js:babel-register を指定しておくと、テストファイルを読み込む時に、 babel-register で前処理してくれるようになります。 つまり、テストコードも ES6 で記述できるようになります。

--recursive はその名の通り。

あとは Polyfill が必要なので読み込んでいるので、このプロジェクトは kintone ようなので、それ用のライブラリを読み込んでいます。

mocha コマンドは次のように実行します。

NODE_ENV=test NODE_PATH=$NODE_PATH:$PWD/src mocha

テストなので、 NODE_ENV に test をセットします。

NODE_PATH は先程の webpack のところでやった、相対パス地獄を回避するためにライブラリの読み込みパスを指定しないといけません。

power-assert

Mocha は assertion のライブラリを持っていないと書きましたが、 assertion には power-assert を採用しました。 power-assert を使うと、 assert で失敗した時の出力が超絶にわかりやすくなります。

どのような出力になるかは本家をご参照ください。(power-assert)

power-assert をインストールします。

% npm install power-assert babel-plugin-espower --save-dev

power-assert はそのままでは babel に対応していないので、 babel-plugin-espower も導入します。

.babelrc に次の設定を追加します。

  "env": {
    "test": {
      "plugins": [
        "espower",
      ]
    }
  }

power-assert はテスト環境だけで、動作すれば十分なので、テスト環境のみ espower を追加するようにしています。

Karma

ごめん、書くの疲れた。 また今度書きます。

感想

相変わらず JS 界はツール類が多くて大変というのが正直な感想です。 ブラウザの違い、バージョンの違い、サーバ・クライアントの違いと複雑な条件が絡み合っているので仕方がないとは思うのですが。。。

しかし、このあたりの仕組をちゃんと把握したうえで使いこなすのは、本当に大変だと思うし、今の環境も1年後には古くなっているのを考えると少し憂鬱な気持ちになってしまいます。

とはいえ、現環境ではまずまずの状態を作れたのではないかと思うので、しばらくはこの環境で開発を進めていこうと思っています。

まとめ

UUUMでは富士登山(任意参加!!)ができます。

f:id:masatobito:20160915160031j:plain

エンジニアで参加したのは僕だけでしたがw