Perlで替え歌エンジンをつくる

※当エントリはPerl入学式 Advent Calendar 2016の第23日目のエントリです。

どーも、わいとんです。

皆さん、明日はクリスマスイブですね。ところで皆さんはカラオケは好きですか?そうでもない?では替え歌ならどうでしょうか?愉快な替え歌なら、ワクワクすると思います。

今日はそんな替え歌を、自動で作ってくれるスクリプトをPerlでつくってみました。

すでに上の画像の時点でネタばれなのですが、クリスマスソングとしておなじみの「赤鼻のトナカイ」の歌いだしの部分を、いい感じに悲壮感漂う風味に仕上がるようにちょっとだけ頑張りました。

と言っても、そんなに難しいことはしておらず、Perl入学式の卒業生であれば、比較的かんたんにつくれる程度のものです。

ソースコードをgistに公開していますので、何をやっているのか見てみると、参考になるかもしれません。

さすがに何の解説もないと理解に苦しむ個所もあろうかと思いますので、少しだけ解説。

動作概説

やってることをかいつまんでしまうと、以下のような手順になります。

  1. 替え歌のもとになる歌詞を形態素解析APIに投げ込み、結果をうけとる。
  2. 受け取った結果データをJSONからハッシュリファレンスに変換する。
  3. 2のデータから一般名詞だけを一定確率で別の単語にランダムで置き換える。
  4. 3のデータをぜんぶjoinしてprintfで出力する。

なお1のAPIは、わいとんお手製のAPIとなっています。いつまでも残しておく保証はないですが、ちょっと遊ぶくらいには使っていただいて構いません。

課題

Perl入学式の受講生ならびに卒業生の皆さんには、思い思いの改造を施して、あなたのオリジナルの替え歌エンジンを作ってみてください。

File::Spec/File::Basenameを使ったライブラリパス指定

※このエントリは Perl Advent Calendar 2016 の15日目のエントリです。

どーも、わいとんです。

早速ですが、みなさんはPerlのライブラリパスをどんな風に指定していますか?

そもそもスクリプト内で指定してない?絶対パスで直接指定?それともFindBinを使ってる?

今回はわいとんがよくやっているFile::SpecFile::Basenameを使ったライブラリパス指定の方法を紹介します。

以下のようなプロジェクトがあったとします。

./
|
+---cpanfile
|
+---bin/
|    |
|    +---run.pl
|
+---lib/
     |
     +---MyProj.pm
     |
     +---MyProj/
          |
          +---Client.pm

cpanfileは以下のような内容です。

requires 'perl', '5.008001';
requires 'LWP::UserAgent';
requires 'LWP::Protocol::https';
on 'test' => sub {
    requires 'Test::More', '0.98';
};

これをcpm installなんてやった場合、local/lib/perl5配下に依存ライブラリがどんどん入ってきます。(cpmについてはskajiさんのエントリを参照してください。)

で、bin/run.plからMyProj::Client->new(...);なんてやりたいなぁって思った時に、以下のように書くと思います。

use strict;
use warnings;
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

でもって、実行時に

perl -Ilib -Ilocal/lib/perl5 ./bin/run.pl

なぁんて書くと思います。別に悪くないけど、面倒ですよね。

ちょっと賢くFindBinを使う

で、use libFindBinを組み合わせて、以下のようにしたりしますよね。(しますよね?したことありません?)

use strict;
use warnings;
use FindBin;
use lib (
    "$FindBin::Bin/../lib",
    "$FindBin::Bin/../local/lib/perl5",
);
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

実行時にはこんな感じで。

perl ./bin/run.pl

楽になりましたね!でもでもでもでもチョッッット待ってwww それってカレントディレクトリがプロジェクトフォルダの直下にないと動かなくないっすか?(動かないですよね?)あとそれWindowsじゃ動かなくないですか?(Windowsなんか使わない?あ、すみませんww)

File::SpecとFile::BasenameとFILEを使う

まあそこで出てくるのがFile::SpecFile::Basenameなんですね。

超雑に解説すると、File::SpecってのはOS間で異なるパス表現(¥とか/とかそういうやつ)を吸収してくれるライブラリで、File::Basenameてのは、食わせた文字列をパス表現文字列とみなしてざっくりパーズし、フォルダまでのパスとかファイル名とかをよしなに引っ張ってきてくれるライブラリです。

あと__FILE__っていうのは実は組みこみ関数でして、__FILE__が記述されているファイルのパス(つまり実行されるスクリプトのパスそのもの)を返してくれます。

んで、こいつらを使ってさっきのbin/run.plを書き換えると、こんな感じになります。

use strict;
use warnings;
use File::Spec;
use File::Basename 'dirname';
use lib (
    File::Spec->catdir(dirname(__FILE__), qw/.. lib/),
    File::Spec->catdir(dirname(__FILE__), qw/.. local lib perl5/),
);
use MyProj::Client;
my $client = MyProj::Client->new(...);
...

若干冗長ですが、実行時は常にスクリプトを正しく指定するだけでOK。

perl ./bin/run.pl

例えばlib/MyProj/からでも

perl ../../bin/run.pl

と実行できます。

少し補足。 File::Spec->catdir(...)は、引数にリストを食わせることで、実行環境に応じたパス文字列を仕立て上げてくれます。dirname(...)は与えられたパス文字列のディレクトリパスを返してくれます。

これらを組み合わせて、どの環境でどのパスから実行されても、読み取りたいライブラリパスがずれずに利用可能となる、と。こういうわけです。

何より、File::SpecFile::Basenameもコアモジュールなので、Perl5が入っていればまず使えないってことはないんじゃないでしょうか。→ツッコミ参照ください

まとめ

  • ライブラリパスをがっちりつかんで離さないスクリプトを書くための方法を紹介しました。
  • File::SpecFile::Basenameを使うぞって言いながら__FILE__も使いました。
  • きっとWindowsの人もニッコリ。
  • cpmかわいいよcpm

ツッコミもらいました

だそうです。。。skajiさん、ありがとうございますm(_ _)m

benchmark.pl 2016 Winter – ベンチマークから考える効率的なロジック

※このエントリはPerl5 Advent Calendar 2016 第13日目のエントリです。

どーも、わいとんです。

タイトルはイベント名っぽくすることで、このエントリを毎年恒例にしていくぞ、という心構えを形にしたもので、特に大きな意味はありません。

効率的なロジック。プログラムを書くときに、突き詰めていくと気になってきますよね。

そんな時、自分の書いたコードに対してベンチマークを取ったりすることかと思います。

ベンチマークを取る?

早い話が「処理速度を測定する」ってことです。こう書いてしまえば身も蓋もないですねw

Perlでベンチマークを取るには

CPANにはBenchmarkというそのものズバリなネーミングのモジュールが存在します。

Benchmarkは、perl-5.004の頃からコアモジュールに含まれており(つまりperlが使える環境ならBenchmarkも使える)、時代の流れとともに進化してきた経緯があります。

基礎的なケースのベンチマーク

どんなプログラムも、基礎の組み合わせで成り立っています。今回は基礎的なケースをいくつか例に実際にベンチマークをとって、より効率的なロジックの模索をしていきたいと思います。

なお、ベンチマークは以下の環境(MacOSX + plenv + perl-5.24.0)で実施しました。基礎的なケースばかりですので環境ごとの揺らぎはほぼないと思いますが、念のため参考としてください。

$ perl -v

This is perl 5, version 24, subversion 0 (v5.24.0) built for darwin-2level
(with 1 registered patch, see perl -V for more detail)

Copyright 1987-2016, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

配列への一括処理

ベンチマークのために書いたスクリプトは以下の通りです。

use strict;
use warnings;
use Benchmark ':all';
use Test::More;

## 処理対象となる配列
my @list = (0 .. 10000);


## 各パターンの定義

#- 配列でforを回してpushするタイプ
my $for_array_push = sub {
    my @result;
    for my $row (@list) {
        push @result, "num=".$row;
    }
    return @result;
};

#- 配列インデックスでforを回して直接インデックス指定して代入するタイプ。
my $for_array_index = sub {
    my @result;
    for my $i (0 .. $#list) {
        $result[$i] = "num=".$list[$i];
    }
    return @result;
};

#- Cスタイルのforで直接インデックス指定して代入するタイプ。
my $for_cstyle_index = sub {
    my @result;
    for (my $i=0; $i<=$#list; $i++) {
        $result[$i] = "num=".$list[$i];
    }
    return @result;
};

#- forを使わないでmapで写像を得るタイプ
my $mapping = sub {
    return map { "num=".$_ } @list;
};

## for_array_push を基準に、各パターンが同じ出力になることを確認する
is_deeply [$for_array_push->()], [$for_array_index->()], 'for_array_push eq for_array_index';
is_deeply [$for_array_push->()], [$for_cstyle_index->()], 'for_array_push eq for_cstyle_index';
is_deeply [$for_array_push->()], [$mapping->()], 'for_array_push eq mapping';

## 処理時間の測定
my $result = timethese(10000, {
    'for_array_push'   => $for_array_push,
    'for_array_index'  => $for_array_index,
    'for_cstyle_index' => $for_cstyle_index,
    'mapping'          => $mapping,
});

## 測定結果の比較表示
cmpthese($result);

## テスト完了
done_testing();

for_array_push for_array_index for_cstyle_index mapping の4つのパターンを用意し、それぞれ同じ結果となることを is_deeply で確認してから timethese で速度測定。最後に cmpthese で測定結果の比較を表示しています。

なおこのスクリプトはperlで実行可能ですので、手元でもお試しいただけます。

結果はmapping圧勝

以下、実行結果です。

$ perl ./loop.pl
ok 1 - for_array_push eq for_array_index
ok 2 - for_array_push eq for_cstyle_index
ok 3 - for_array_push eq mapping
Benchmark: timing 10000 iterations of for_array_index, for_array_push, for_cstyle_index, mapping...
for_array_index: 25 wallclock secs (24.37 usr +  0.01 sys = 24.38 CPU) @ 410.17/s (n=10000)
for_array_push: 23 wallclock secs (22.86 usr +  0.02 sys = 22.88 CPU) @ 437.06/s (n=10000)
for_cstyle_index: 31 wallclock secs (31.14 usr +  0.05 sys = 31.19 CPU) @ 320.62/s (n=10000)
mapping:  6 wallclock secs ( 6.09 usr +  0.03 sys =  6.12 CPU) @ 1633.99/s (n=10000)
                Rate for_cstyle_index for_array_index for_array_push  mapping
for_cstyle_index  321/s               --            -22%           -27%     -80%
for_array_index   410/s              28%              --            -6%     -75%
for_array_push    437/s              36%              7%             --     -73%
mapping          1634/s             410%            298%           274%       --
1..3

Cスタイルでの書き方に比べて、mapを使った書き方はなんと4.1倍も速いのがわかりますね。

このことから、配列を全件処理して別の配列を作る場合はmapを使うと速くて短く書ける、という結論が得られました。

ただ注意して欲しいのは、配列を全件処理するだけ(つまり別の配列を作らない)場合にmapを使うことは、可読性という面では好ましくないと考えます(ループ処理だけしたいのか、加工された配列が欲しいのか判断がつかない)。そういうケースでは for my $row (@list) {...} のやり方のほうが意図が読み取りやすいですね。

条件分岐

こちらのベンチマークコードは以下の通りです。

use strict;
use warnings;
use Benchmark ':all';
use Test::More;

## 処理対象の配列
my @list = map {[int(rand(6)+1), int(rand(6)+1)]} 1..10000;

## 処理パターン定義

#- if/elsif/elseを使った条件分岐
my $if_else = sub {
    map {
        if    ($_->[0] == $_->[1])      {1}
        elsif ($_->[0] + $_->[1] >= 10) {2}
        else {0}
    } @list;
};

#- 三項演算子を使った条件分岐
my $ternary = sub {
    map {
        $_->[0] == $_->[1]       ? 1 :
        $_->[0] +  $_->[1] >= 10 ? 2 :
        0;
    } @list;
};

#- ハッシュを使った分岐
my $map = {
    '1:1' => 1, '2:2' => 1, '3:3' => 1,
    '4:4' => 1, '5:5' => 1, '6:6' => 1,
    '4:6' => 2, '5:6' => 2,
    '6:4' => 2, '6:5' => 2,
};
my $hashmap = sub {
    map { $map->{join(':', $_->[0], $_->[1])} || 0 } @list;
};

## if_elseパターンを基準に同一性の担保をする
is_deeply [$if_else->()], [$ternary->()], 'if_else eq ternary';
is_deeply [$if_else->()], [$hashmap->()], 'if_else eq hashmap';

## 速度計測
my $result = timethese(10000, {
    if_else => $if_else,
    ternary => $ternary,
    hashmap => $hashmap,
});

## 結果比較
cmpthese($result);

## テスト完了
done_testing();

if_else ternary hashmap の3つのパターンを用意し、それぞれ同じ結果となることを is_deeply で確認してから timethese で速度測定。最後に cmpthese で測定結果の比較を表示しています。

なおこちらもコピペで実験できます。

三項演算子は速い

こちらが実行結果です。

$ perl ifelse.pl
ok 1 - if_else eq ternary
ok 2 - if_else eq hashmap
Benchmark: timing 10000 iterations of hashmap, if_else, ternary...
hashmap: 18 wallclock secs (18.45 usr +  0.01 sys = 18.46 CPU) @ 541.71/s (n=10000)
if_else: 20 wallclock secs (19.80 usr +  0.05 sys = 19.85 CPU) @ 503.78/s (n=10000)
ternary: 13 wallclock secs (12.22 usr +  0.03 sys = 12.25 CPU) @ 816.33/s (n=10000)
        Rate if_else hashmap ternary
if_else 504/s      --     -7%    -38%
hashmap 542/s      8%      --    -34%
ternary 816/s     62%     51%      --
1..2

目に見えて三項演算子が速いですね。可読性はよろしくないので、使いどころは単純な二分岐にとどめておいたほうが良いと思いますが、変数代入の判断基準のための二分岐にはもってこいと言えそうです。

まとめ

今回は基本的なケースでのベンチマークを試しました。

時代が進んでperlのインターナルな実装に手が入った時、どういうロジックが速くなるのか、ということは都度ベンチマークをする必要があります。そのため、「現時点ではこのくらいの感じなんだなぁ」という程度に参考にしてもらえると幸いです。

YAPC::Hokkaido 2016 SAPPORO が楽しかったという話

※このエントリはPerl5 Advent Calendar 2016の11日目のエントリを兼ねております。

どーも、わいとんです。

昨日のエントリで少しだけ触れましたが、YAPC::Hokkaido 2016 SAPPOROに参加しました。

なお、最強のYAPCレポーターと名高いhirataraさんによるエントリが大変参考になりますので、是非ご参照されることをお勧めします。

YAPCとは?

若い方の中にはYAPCってなんぞ?という方もいらっしゃることかと思います。

簡単に解説しておきますと、YAPCというのはYet Another Perl Conferenceの略称です。大変端折った書き方をするならば、「Perlのカンファレンス」です。そのままですね。

Yet Anotherという接頭辞については、元々Perl Conferenceが別にあるらしいから、という事ですが、詳しくはwww.perl.orgに記載がありますので、興味のある方は是非見てみてください。

初の国内地方都市開催

国内では東京以外で開催されたのは初めてのことでして、軽い観光を兼ねたスケジュールで臨むことにしました。

参加人数は265名(実質185名、後述のことが影響か)で、札幌のポテンシャルと試されっぷりに驚愕しております。

試されっぷり?

実は今回のYAPCは冬の札幌での開催ということもあり、まさにエクストリームYAPCと言える幕開けでした。

  • 新千歳空港が大雪で最終的に閉鎖
  • 閉鎖前に到着した便も2時間ほど遅延したり「途中で引き返す可能性」があってスリル満点
  • 当然空の便は結構欠航
  • 「札幌に着いても安心するなぁ、路面凍結で足元を掬ってやるからなぁ」(CV:大○洋)

とまあ、ホテルや会場に到着するまでエクストリームな心構えを忘れさせない北海道さんの本気を見せつけられた次第です。

あるお方は欠航被害に遭遇したので、機転を利かせてわざわざ成田から北海道新幹線と函館本線を乗り継ぐという、エクストリームの極みと言える旅程を体験したようです。冬の北海道の洗礼はそれだけに止まらなかったようですが・・・

全国各地からPerlつながりで人が集まった

北は北海道(というか札幌開催なので地元の方や道内各地の皆さん)から、南は沖縄まで(しかも学生)、全国津々浦々からPerlというトピックのためにたくさんの人が集まりました。

濃度高めのセッション

タイムテーブルhiratara氏のエントリを見てもわかるように、大変内容の濃いセッションが多かったと思います。

ザクッとまとめてもhttp/2やPerlの詳細なリリース情報、クラウド事業者4社によるパネルディスカッションなど、PerlとWebテクノロジーに興味のある方には垂涎ものの内容でした。

個人的には下記の発表がなかなか刺さる内容だったな、と感じています。

モジュールメンテナンスを通して感じる最近のPerl

モジュールの継続的なメンテナンスにかかるコストに着眼して、後継者へ引き継ぎしやすくする、諦める、などの考えがありますよ、という感じの内容でした。

僕個人としてもモジュールをリリースしている立場として、一言で言えば「身につまされる」セッションでした。Net::Azure::EventHubsのco-maintainerも探さないとな・・・w

PHPエンジニアが初めてPerlでwebアプリを作った話

近年Perlに関する書籍が少ない事や、そもそもドキュメンテーションがあまりないという感想を聞いた時、ドキュメンテーションの薄いモジュールなどを見た時の辛さを思い出してしまいました。

あるモジュールなどに関して、個人ブログにおけるいわゆる「使ってみた」エントリなどはそれなりにあるのですが、一箇所に情報が集約されていないのは確かにしんどいかも・・・

あと、発表者が「PerlのWAFはAmon2Mojolicious使ってみましたが、何か皆さんお勧めのWAFありますか?」という質問をしていましたが、個人的な意見としては中規模以上のWebAppを書くならMojoliciousかAmon2、APIなら自作WAF、小規模なら素のPlackを使うと、この場を持って回答したいと思います。

まとめ

このエントリはチェックアウト直前のホテルの中で慌てて書きました。さて、帰りもエクストリームとなりそうな予感ではありますが、YAPCは楽しいですので、まだ未体験のPerl/Web技術大好きな方は是非次回3月のYAPC::Kansaiなどを検討してみてはいかがでしょうか。

最後に

JPAの皆さん、スタッフの皆さん、そして発表者のみなさんのおかげで大変楽しい思い出ができました。ありがとうございました!

あと今度のYAPC::Amsterdamも行きたいと思います!

2016-12-12 追記

クロージングでJPAのnekokak氏はこう仰っておられました。

nekokak氏のありがたいスライド

さて実は、北海道は札幌でこの追記をしております。

言霊の恐ろしさを感じずにはいられないのです。。。。

Azure + Perl でサーバレスなジョブシステムを作る

Azure + Perl でサーバレスなジョブシステムを作る

※このエントリは Microsoft Azure Advent Calendar 2016 及び Perl Advent Calendar 2016 の10日目のエントリです。

どーも、わいとんです。

今日はタイトルの通り、Microsoft AzureとPerlでサーバレスなジョブシステムを作るまでの流れを紹介していきます。

Azure 及び Perl について

その前に、このエントリにはAzure及びPerl双方の大まかな知識があることと、手元の端末がMacOSXであることが前提になります。

念のため、それぞれざっくりした説明をしておきます(多分に私見が混じっておりますことをあらかじめご了承ください)。

Azure とは

Microsoft Azure(以下Azure)はMicrosoft社がサービス提供をしているクラウドサービスです。

仮想マシンやネットワークなどをはじめとしたIaaS、Web Appsを軸にしたPaaS、そしてMobile AppsやNotification HubsなどのSaaSが提供されており、近年Amazon Web ServicesやGoogle Cloud Platformと並んで、クラウド御三家 などと例えられたりします(主に僕が例えてます)。

Microsoftのサービスらしく、基盤技術としてはWindowsや.NET技術が中心となっていますが、同社は最近OSSへのコミットメントが大変著しく、AzureでもLinuxのサポートが行なわれているサービスが結構存在します。

なお当エントリは、既にMicrosoftアカウントをお持ちで、Azure Portalでのサービス操作が可能なことが前提となっています。また、各種費用については事前に料金計算ツールなどでご確認の上、ご自身の責任においてお試しください。

Perl とは

1987年にLarry Wall(ラリー・ウォール)によって公開されたスクリプト言語です。

今日LL(Lightweight Language)と呼ばれるプログラミング言語群の先駆けであり、過去にはCGIというWebアプリケーション提供手法と組み合わせたパラダイムで一世を風靡しましたが、ここ最近はPHPやRubyなどの登場により、シェアは漸減しているようです。

しかしながら、CPANを軸にしたエコシステムの存在によって拡張性が大変強力であり、かつ徹底した後方互換性の維持方針によって、古いソースコードでも安定稼働が期待できます。

近年はテストドリブンな開発手法や継続テストなどによるモダンかつ安定性を重視した開発手法が採用されることもあり、当エントリも「モダンな手法としてのサーバレスアーキテクチャ」をPerlで実現する、というところを重視したつもりです。

Azure上に環境を作る

前置きが長くなりましたが、ここからはスナップショット画像を交えて、Azure上への環境構築の様子を解説していきます。

リソースグループを作る

Azure Portal上で新しくリソースグループを作ります。

※リソースグループとは: Azureにある様々なサービスを、用途ごとにまとめておく枠組みです。詳しくはこちらのエントリを参照してください。

画面左側にある水色の立方体アイコンがあるので、これをクリックすると、リソースグループ一覧が表示されます。そこに「追加」というボタンがあるので押すと、リソースグループ名の入力とロケーションの選択を促されます。

これらの入力を終えて作成ボタンを押して少し待つと、以下のようにリソースグループの概要が示されます。

リソースグループができた様子

これでリソースグループの完成です。

Event Hubsをデプロイする

リソースグループの概要画面で「追加」ボタンを押すと、デプロイしたいサービスを選んだり検索できる画面になります。ここでは「event hubs」と検索窓に入力してあげると、以下のようにEvent Hubsが登場しますので、これを選択します。

event hubsを検索した様子

すると、以下のように概要説明が出てきますので、作成ボタンを押します。

event hubsを検索した様子

作成ボタンを押すと、以下のように料金体系の選択や名称の設定など、5項目について入力を求められます。

event hubsをデプロイする様子

なお、今回僕は以下のように設定しました(なんかでかいかも)。

event hubsの設定例

デプロイには数分かかることもあるので、コーヒーでも淹れながらのんびり待っていると良いかと思います。そのうち以下のような通知がベルマークのところに出てくるので、待ちましょう。

デプロイ完了の通知

デプロイが完了してからリソースグループの更新を行うと、以下のようにEvent Hubsがデプロイされていることが確認できます。

event hubsがデプロイされた様子

Event HubsにHubを追加する

続いて、実際にペイロードの受容部となるHubを追加します。

先ほど作成したEvent Hubsを選択すると、以下のような画面になりますので、「+Event Hub」ボタンを押します。

event hubsにHubを追加する様子

すると、以下のように設定項目が登場しますので、NameとPartition Count(今回は8にしました)を適宜設定し、Createボタンを押します。

Hubの設定

少し待つと、Event Hub作成完了通知が以下のように出てきます。

Hubの作成完了通知

これでHubの追加が完了しました。

追加したHubにSAS鍵を追加する

実際にHubに対してペイロードを受容してもらうためには、あらかじめ定められた鍵を以ってペイロードの送信を行う必要があります。この鍵を使ったやりとりのことをSAS(Shared Access Signature)と呼びます。

鍵を作るには、先ほど追加したHubの概要を開きます。下記の例では 「Event Hubs」をメニューから選択し、一番下の方にちらっと見えている「advent」という名のHubを選択する感じになります。

Event HubsのHub一覧

すると以下のような画面が出てきますので、メニューから「Shared Access Policy」(鍵マークのやつ)を選択します。

Hub概要

最初は見ての通りShared Access Policyが何も存在しません。ポリシーを新しく作る必要があるので、「Add」ボタンを押します。

SASポリシーの確認

新規作成するポリシーの名称及び権限設定(Claim)を要求されます。

SASポリシーの設定

名前は任意の文字列、権限は「Manage」を設定して「Create」ボタンを押すと、ポリシーが作成されます。

SASポリシーの確認、再度。

中身を見てみると、以下のように権限設定の確認、それと鍵と接続文字列がそれぞれPrimary, Secondaryと並んでいます。

とりあえず後で使うので、接続文字列を(Primary/Secondaryどちらでもいいので)コピーして、どこかにメモしておいてください。

Function Appをデプロイする

Event Hubsの準備ができたところで、次にFunction Appsの準備を進めます。

改めてリソースグループの概要に戻り、「追加」ボタンを押して「function app」と検索窓に入力します。すると、Function App が検索結果の一番上に出てくるので、これを選びます。

function app の検索結果

以下のように解説が出てきますが、とりあえず「作成」ボタンを押します。

function app の説明

設定を要求するブレードが登場しますので、以下の例を参考に各々埋めて「作成」ボタンを押してください。

function appの設定

しばらく待っているとデプロイが完了しますので、リソースグループの概要で「更新」ボタンを押すと、以下のように稲妻アイコンのApp Serviceが登場しています。これがFunction Appです。また、ストレージアカウントも一緒に作られます。

Function app 作った後のリソースグループ

Function Appに関数を追加する

早速、出来上がったFunction Appを見てみると、以下のようにデカデカと関数への早道と書かれた仰々しい画面が登場しますが、これはどうでもいいです。

Function Appの概要

とりあえずデカイやつはどうでもよくて、左側のメニューにある「新しい関数」を選択します。すると以下のように「テンプレートの選択」が登場します。

ここでいろんなテンプレートがあって目移りするかもしれませんが、僕としては男は黙ってBashという信条がございます故、言語にBashを選択します。

言語選択

すると・・・

おお、神よ!Bashを救いたまえ!

悲しいかな、選べるものが1つしかなく、しかも「Azure Queue Storage」をトリガーとした関数テンプレートしかないのです。

しかし!ここで諦めてはいけない! まず、そのままこのテンプレートを選択します。

すると、選択したテンプレートの下に以下のようなフォーム群が登場します。とりあえず関数名を簡単なものにして、そのまま「作成」ボタンを押してください。

設定が登場する

これでとりあえず関数がデプロイされました。ただ、このままではEvent Hubsとの連携ができていませんので、この後直していきます。

追加した関数の入力元にEvent Hubsを指定する

さて、出来上がった関数は以下のようになっています。

出来上がった関数(bash)

なるほど。シンプルですね。

次に、左側のメニューから「統合」を選択します。

関数の統合設定

先ほどのテンプレート設定みたいな画面が出てきましたが、右上にある「詳細エディター」を選択してください。

詳細エディター

詳細エディターというのは、トリガーや入出力をJSON形式で自力で設定するためのツールなのですが、ここで以下のように修正し、保存します。

編集後

変更した箇所は "type" の設定値が "queueTrigger" だったのを "eventHubTrigger" に変えただけです。

この状態で「標準エディタ」を選択すると・・・

標準エディタで見てみた図

トリガーの部分が「Azure Queue Storage」から「Azure イベント ハブ」に変わっていますね!

Event Hub接続をFunctions Appに設定する

今度は「イベント ハブ接続」の設定を行うため、上気した画面の右下にある「新規」リンクを選択します。すると下記のような画面になります。

標準エディタで見てみた図

最初は「結果はありません」と書かれていますね。その上にある「接続文字列の追加」を押してください。

Service Bus 接続の図

接続名は任意の文字列隣ます。、接続文字列ですが、ここで先ほどコピーしておいたEvent Hubの接続文字列を貼り付けます。

ここまでやったら「OK」ボタンを押します。

イベントハブ接続が確立した図

ご覧の通り、イベントハブ接続の設定ができました。

クライアント側の実装を作る

さて、ここまででようやくAzure側の設定が完了しました。あとはクライアント側のコード実装を行い、ペイロードを投げ込むだけです。

ところで皆さん、PerlでEventHubsにペイロードを信するための方法をご存知でしょうか?

おそらくほとんどの方はご存知ないと思いますが、Net::Azure::EventHubsというモジュールがCPANにリリースされていて、これを使うと簡単にEventHubsにペイロードを送信することができます。なお、Net::Azure::EventHubsを作ったのは です。

Net::Azure::EventHubsを導入する

それでは cpanm あるいは cpm を使ってNet::Azure::EventHubsをインストールしてみましょう。

  • cpanmの場合

    $ sudo cpanm Net::Azure::EventHubs

  • cpmの場合

    $ sudo cpm install -g Net::Azure::EventHubs

これでインストールに成功すると思います。

スクリプトを書く

では実際にペイロードを送信するプログラムを書いてみます。

use strict;
use warnings;
use Net::Azure::EventHubs;

my $hub = Net::Azure::EventHubs->new(
    connection_string => 'Endpoint=sb://...',
);

my $req = $hub->message({
    name => 'ytnobody',
    age  => '36',
    fav  => [qw/Gyoza Curry Lucky-Pierrot/],
});
$req->do;

connection_string はご自身の環境に合わせて変更してください。

実際にペイロードを送信してみる

これをbashで実行すると、下記のgifのようになります。

実際にペイロードを送ってみる

ウル技(ウルテク)

さらにもう一歩Perlを推し進めたウル技を披露します。

Function AppでPerlを使ってペイロードを処理する

Azure側の関数のコードを以下のようにすることで、関数記述言語として、なんとPerl(v5.22.0)が利用可能です!!!!

まず run.sh は以下のように。

直接受ける側のbashコード

そして新しく worker.pl を作り、以下のようなコードにします。

worker.pl

非常に簡単なコードですね。これは run.sh で受けたペイロードをそのまま worker.pl に横流しして、そのままデバッグログに出力するだけの仕組みです。

これを動かすと、こうなります・・・!

worker.plをFunction Apps上で動かす

Azure Function AppはPerlが動くぞ!すごい!!

まとめ

  • AzureとPerlは協調できる!
  • Net::Azure::EventHubs を作ったぞ!
  • Azure Function App はPerlも使えるぞ!

最後に

このエントリは YAPC::Hokkaido 2016 SAPPORO参加しながら執筆しました。次回のYAPC::Japanは関西での開催らしいです。

Kichijojipm #9 でVSCodeの回し者をしてきた

すっかりブログを書くのが遅くなってしまいましたが、Kichijojipm #9でVSCode for Perlというタイトルで発表してきました。

スライドの元になったMarkdownはこちら

基本的にはVSCodeの素晴らしさを説く内容なのですが、Perlの集まりということもあるので、VSCodeを使うとどんな風に生産性を向上させることが可能か、という観点での発表となりました。

個人的には VSCodeVim/Vim とか vscode-reveal は本当便利だから入れといて損はないよ!と思います。

割といいなぁと思ったセッション、Exporter周りの話とCarton-Mirrorの話なんかは参考になる感じしました。

Net::Azure::CognitiveServices::Face – A wrapper class for Face API of Azure Cognitive Services – metacpan.org

Net::Azure::CognitiveServices::Face – A wrapper class for Face API of Azure Cognitive Services – metacpan.org

Net::Azure::NotificationHubs – A Client Class for Azure Notification Hubs – metacpan.org

Net::Azure::NotificationHubs – A Client Class for Azure Notification Hubs – metacpan.org

Net::Azure::EventHubs – A Client Class for Azure Event Hubs – metacpan.org

Net::Azure::EventHubs – A Client Class for Azure Event Hubs – metacpan.org